diff options
Diffstat (limited to 'src')
25 files changed, 416 insertions, 126 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index b99d0fc91..45b2eef52 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp | |||
| @@ -42,6 +42,15 @@ void ApplyMix(std::span<s32> output, std::span<const s32> input, s32 gain, s32 s | |||
| 42 | 42 | ||
| 43 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta, | 43 | s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta, |
| 44 | s32 sample_count) { | 44 | s32 sample_count) { |
| 45 | // XC2 passes in NaN mix volumes, causing further issues as we handle everything as s32 rather | ||
| 46 | // than float, so the NaN propogation is lost. As the samples get further modified for | ||
| 47 | // volume etc, they can get out of NaN range, so a later heuristic for catching this is | ||
| 48 | // more difficult. Handle it here by setting these samples to silence. | ||
| 49 | if (std::isnan(gain)) { | ||
| 50 | gain = 0.0f; | ||
| 51 | delta = 0.0f; | ||
| 52 | } | ||
| 53 | |||
| 45 | s32 x = 0; | 54 | s32 x = 0; |
| 46 | for (s32 i = 0; i < sample_count; i++) { | 55 | for (s32 i = 0; i < sample_count; i++) { |
| 47 | x = static_cast<s32>(static_cast<float>(input[i]) * gain); | 56 | x = static_cast<s32>(static_cast<float>(input[i]) * gain); |
diff --git a/src/common/settings.h b/src/common/settings.h index d2e91a2c9..71d0f864f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -330,7 +330,7 @@ struct Values { | |||
| 330 | Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; | 330 | Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; |
| 331 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; | 331 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; |
| 332 | Setting<bool> use_vsync{true, "use_vsync"}; | 332 | Setting<bool> use_vsync{true, "use_vsync"}; |
| 333 | Setting<bool> disable_fps_limit{false, "disable_fps_limit"}; | 333 | BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; |
| 334 | Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"}; | 334 | Setting<bool> use_assembly_shaders{false, "use_assembly_shaders"}; |
| 335 | Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; | 335 | Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; |
| 336 | Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; | 336 | Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 24eff210f..7019a7a68 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <optional> | 7 | #include <optional> |
| 8 | #include <ranges> | ||
| 9 | #include <utility> | 8 | #include <utility> |
| 10 | 9 | ||
| 11 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index dc15cf58b..7ca7f2aac 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -7,6 +7,9 @@ | |||
| 7 | #ifdef __GNUC__ | 7 | #ifdef __GNUC__ |
| 8 | #pragma GCC diagnostic push | 8 | #pragma GCC diagnostic push |
| 9 | #pragma GCC diagnostic ignored "-Wshadow" | 9 | #pragma GCC diagnostic ignored "-Wshadow" |
| 10 | #ifndef __clang__ | ||
| 11 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | ||
| 12 | #endif | ||
| 10 | #endif | 13 | #endif |
| 11 | #include <httplib.h> | 14 | #include <httplib.h> |
| 12 | #include <mbedtls/sha256.h> | 15 | #include <mbedtls/sha256.h> |
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 7399c3648..8de3d4520 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -294,8 +294,8 @@ void InputSubsystem::ReloadInputDevices() { | |||
| 294 | impl->udp->ReloadSockets(); | 294 | impl->udp->ReloadSockets(); |
| 295 | } | 295 | } |
| 296 | 296 | ||
| 297 | std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( | 297 | std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([ |
| 298 | Polling::DeviceType type) const { | 298 | [maybe_unused]] Polling::DeviceType type) const { |
| 299 | #ifdef HAVE_SDL2 | 299 | #ifdef HAVE_SDL2 |
| 300 | return impl->sdl->GetPollers(type); | 300 | return impl->sdl->GetPollers(type); |
| 301 | #else | 301 | #else |
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp index edced69bb..9f5a54de4 100644 --- a/src/tests/video_core/buffer_base.cpp +++ b/src/tests/video_core/buffer_base.cpp | |||
| @@ -536,7 +536,7 @@ TEST_CASE("BufferBase: Cached write downloads") { | |||
| 536 | REQUIRE(rasterizer.Count() == 63); | 536 | REQUIRE(rasterizer.Count() == 63); |
| 537 | buffer.MarkRegionAsGpuModified(c + PAGE, PAGE); | 537 | buffer.MarkRegionAsGpuModified(c + PAGE, PAGE); |
| 538 | int num = 0; | 538 | int num = 0; |
| 539 | buffer.ForEachDownloadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); | 539 | buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); |
| 540 | buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); | 540 | buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); |
| 541 | REQUIRE(num == 0); | 541 | REQUIRE(num == 0); |
| 542 | REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); | 542 | REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); |
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index b121d36a3..c3318095c 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h | |||
| @@ -226,19 +226,24 @@ public: | |||
| 226 | /// Call 'func' for each CPU modified range and unmark those pages as CPU modified | 226 | /// Call 'func' for each CPU modified range and unmark those pages as CPU modified |
| 227 | template <typename Func> | 227 | template <typename Func> |
| 228 | void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) { | 228 | void ForEachUploadRange(VAddr query_cpu_range, u64 size, Func&& func) { |
| 229 | ForEachModifiedRange<Type::CPU>(query_cpu_range, size, func); | 229 | ForEachModifiedRange<Type::CPU>(query_cpu_range, size, true, func); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | /// Call 'func' for each GPU modified range and unmark those pages as GPU modified | 232 | /// Call 'func' for each GPU modified range and unmark those pages as GPU modified |
| 233 | template <typename Func> | 233 | template <typename Func> |
| 234 | void ForEachDownloadRange(VAddr query_cpu_range, u64 size, Func&& func) { | 234 | void ForEachDownloadRange(VAddr query_cpu_range, u64 size, bool clear, Func&& func) { |
| 235 | ForEachModifiedRange<Type::GPU>(query_cpu_range, size, func); | 235 | ForEachModifiedRange<Type::GPU>(query_cpu_range, size, clear, func); |
| 236 | } | ||
| 237 | |||
| 238 | template <typename Func> | ||
| 239 | void ForEachDownloadRangeAndClear(VAddr query_cpu_range, u64 size, Func&& func) { | ||
| 240 | ForEachModifiedRange<Type::GPU>(query_cpu_range, size, true, func); | ||
| 236 | } | 241 | } |
| 237 | 242 | ||
| 238 | /// Call 'func' for each GPU modified range and unmark those pages as GPU modified | 243 | /// Call 'func' for each GPU modified range and unmark those pages as GPU modified |
| 239 | template <typename Func> | 244 | template <typename Func> |
| 240 | void ForEachDownloadRange(Func&& func) { | 245 | void ForEachDownloadRange(Func&& func) { |
| 241 | ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), func); | 246 | ForEachModifiedRange<Type::GPU>(cpu_addr, SizeBytes(), true, func); |
| 242 | } | 247 | } |
| 243 | 248 | ||
| 244 | /// Mark buffer as picked | 249 | /// Mark buffer as picked |
| @@ -415,7 +420,7 @@ private: | |||
| 415 | * @param func Function to call for each turned off region | 420 | * @param func Function to call for each turned off region |
| 416 | */ | 421 | */ |
| 417 | template <Type type, typename Func> | 422 | template <Type type, typename Func> |
| 418 | void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) { | 423 | void ForEachModifiedRange(VAddr query_cpu_range, s64 size, bool clear, Func&& func) { |
| 419 | static_assert(type != Type::Untracked); | 424 | static_assert(type != Type::Untracked); |
| 420 | 425 | ||
| 421 | const s64 difference = query_cpu_range - cpu_addr; | 426 | const s64 difference = query_cpu_range - cpu_addr; |
| @@ -467,7 +472,9 @@ private: | |||
| 467 | bits = (bits << left_offset) >> left_offset; | 472 | bits = (bits << left_offset) >> left_offset; |
| 468 | 473 | ||
| 469 | const u64 current_word = state_words[word_index] & bits; | 474 | const u64 current_word = state_words[word_index] & bits; |
| 470 | state_words[word_index] &= ~bits; | 475 | if (clear) { |
| 476 | state_words[word_index] &= ~bits; | ||
| 477 | } | ||
| 471 | 478 | ||
| 472 | if constexpr (type == Type::CPU) { | 479 | if constexpr (type == Type::CPU) { |
| 473 | const u64 current_bits = untracked_words[word_index] & bits; | 480 | const u64 current_bits = untracked_words[word_index] & bits; |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index cad7f902d..2871682f6 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <vector> | 15 | #include <vector> |
| 16 | 16 | ||
| 17 | #include <boost/container/small_vector.hpp> | 17 | #include <boost/container/small_vector.hpp> |
| 18 | #include <boost/icl/interval_set.hpp> | ||
| 18 | 19 | ||
| 19 | #include "common/common_types.h" | 20 | #include "common/common_types.h" |
| 20 | #include "common/div_ceil.h" | 21 | #include "common/div_ceil.h" |
| @@ -77,6 +78,9 @@ class BufferCache { | |||
| 77 | using Runtime = typename P::Runtime; | 78 | using Runtime = typename P::Runtime; |
| 78 | using Buffer = typename P::Buffer; | 79 | using Buffer = typename P::Buffer; |
| 79 | 80 | ||
| 81 | using IntervalSet = boost::icl::interval_set<VAddr>; | ||
| 82 | using IntervalType = typename IntervalSet::interval_type; | ||
| 83 | |||
| 80 | struct Empty {}; | 84 | struct Empty {}; |
| 81 | 85 | ||
| 82 | struct OverlapResult { | 86 | struct OverlapResult { |
| @@ -148,18 +152,26 @@ public: | |||
| 148 | /// Return true when there are uncommitted buffers to be downloaded | 152 | /// Return true when there are uncommitted buffers to be downloaded |
| 149 | [[nodiscard]] bool HasUncommittedFlushes() const noexcept; | 153 | [[nodiscard]] bool HasUncommittedFlushes() const noexcept; |
| 150 | 154 | ||
| 155 | void AccumulateFlushes(); | ||
| 156 | |||
| 151 | /// Return true when the caller should wait for async downloads | 157 | /// Return true when the caller should wait for async downloads |
| 152 | [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept; | 158 | [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept; |
| 153 | 159 | ||
| 154 | /// Commit asynchronous downloads | 160 | /// Commit asynchronous downloads |
| 155 | void CommitAsyncFlushes(); | 161 | void CommitAsyncFlushes(); |
| 162 | void CommitAsyncFlushesHigh(); | ||
| 156 | 163 | ||
| 157 | /// Pop asynchronous downloads | 164 | /// Pop asynchronous downloads |
| 158 | void PopAsyncFlushes(); | 165 | void PopAsyncFlushes(); |
| 159 | 166 | ||
| 167 | [[nodiscard]] bool DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount); | ||
| 168 | |||
| 160 | /// Return true when a CPU region is modified from the GPU | 169 | /// Return true when a CPU region is modified from the GPU |
| 161 | [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); | 170 | [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); |
| 162 | 171 | ||
| 172 | /// Return true when a CPU region is modified from the CPU | ||
| 173 | [[nodiscard]] bool IsRegionCpuModified(VAddr addr, size_t size); | ||
| 174 | |||
| 163 | std::mutex mutex; | 175 | std::mutex mutex; |
| 164 | 176 | ||
| 165 | private: | 177 | private: |
| @@ -190,6 +202,36 @@ private: | |||
| 190 | } | 202 | } |
| 191 | } | 203 | } |
| 192 | 204 | ||
| 205 | template <typename Func> | ||
| 206 | void ForEachWrittenRange(VAddr cpu_addr, u64 size, Func&& func) { | ||
| 207 | const VAddr start_address = cpu_addr; | ||
| 208 | const VAddr end_address = start_address + size; | ||
| 209 | const VAddr search_base = | ||
| 210 | static_cast<VAddr>(std::min<s64>(0LL, static_cast<s64>(start_address - size))); | ||
| 211 | const IntervalType search_interval{search_base, search_base + 1}; | ||
| 212 | auto it = common_ranges.lower_bound(search_interval); | ||
| 213 | if (it == common_ranges.end()) { | ||
| 214 | it = common_ranges.begin(); | ||
| 215 | } | ||
| 216 | for (; it != common_ranges.end(); it++) { | ||
| 217 | VAddr inter_addr_end = it->upper(); | ||
| 218 | VAddr inter_addr = it->lower(); | ||
| 219 | if (inter_addr >= end_address) { | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | if (inter_addr_end <= start_address) { | ||
| 223 | continue; | ||
| 224 | } | ||
| 225 | if (inter_addr_end > end_address) { | ||
| 226 | inter_addr_end = end_address; | ||
| 227 | } | ||
| 228 | if (inter_addr < start_address) { | ||
| 229 | inter_addr = start_address; | ||
| 230 | } | ||
| 231 | func(inter_addr, inter_addr_end); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 193 | static bool IsRangeGranular(VAddr cpu_addr, size_t size) { | 235 | static bool IsRangeGranular(VAddr cpu_addr, size_t size) { |
| 194 | return (cpu_addr & ~Core::Memory::PAGE_MASK) == | 236 | return (cpu_addr & ~Core::Memory::PAGE_MASK) == |
| 195 | ((cpu_addr + size) & ~Core::Memory::PAGE_MASK); | 237 | ((cpu_addr + size) & ~Core::Memory::PAGE_MASK); |
| @@ -272,8 +314,6 @@ private: | |||
| 272 | 314 | ||
| 273 | void DeleteBuffer(BufferId buffer_id); | 315 | void DeleteBuffer(BufferId buffer_id); |
| 274 | 316 | ||
| 275 | void ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id); | ||
| 276 | |||
| 277 | void NotifyBufferDeletion(); | 317 | void NotifyBufferDeletion(); |
| 278 | 318 | ||
| 279 | [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; | 319 | [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr) const; |
| @@ -327,9 +367,9 @@ private: | |||
| 327 | 367 | ||
| 328 | std::vector<BufferId> cached_write_buffer_ids; | 368 | std::vector<BufferId> cached_write_buffer_ids; |
| 329 | 369 | ||
| 330 | // TODO: This data structure is not optimal and it should be reworked | 370 | IntervalSet uncommitted_ranges; |
| 331 | std::vector<BufferId> uncommitted_downloads; | 371 | IntervalSet common_ranges; |
| 332 | std::deque<std::vector<BufferId>> committed_downloads; | 372 | std::deque<IntervalSet> committed_ranges; |
| 333 | 373 | ||
| 334 | size_t immediate_buffer_capacity = 0; | 374 | size_t immediate_buffer_capacity = 0; |
| 335 | std::unique_ptr<u8[]> immediate_buffer_alloc; | 375 | std::unique_ptr<u8[]> immediate_buffer_alloc; |
| @@ -352,6 +392,7 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_, | |||
| 352 | // Ensure the first slot is used for the null buffer | 392 | // Ensure the first slot is used for the null buffer |
| 353 | void(slot_buffers.insert(runtime, NullBufferParams{})); | 393 | void(slot_buffers.insert(runtime, NullBufferParams{})); |
| 354 | deletion_iterator = slot_buffers.end(); | 394 | deletion_iterator = slot_buffers.end(); |
| 395 | common_ranges.clear(); | ||
| 355 | } | 396 | } |
| 356 | 397 | ||
| 357 | template <class P> | 398 | template <class P> |
| @@ -422,6 +463,68 @@ void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) { | |||
| 422 | } | 463 | } |
| 423 | 464 | ||
| 424 | template <class P> | 465 | template <class P> |
| 466 | bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { | ||
| 467 | const std::optional<VAddr> cpu_src_address = gpu_memory.GpuToCpuAddress(src_address); | ||
| 468 | const std::optional<VAddr> cpu_dest_address = gpu_memory.GpuToCpuAddress(dest_address); | ||
| 469 | if (!cpu_src_address || !cpu_dest_address) { | ||
| 470 | return false; | ||
| 471 | } | ||
| 472 | const bool source_dirty = IsRegionGpuModified(*cpu_src_address, amount); | ||
| 473 | const bool dest_dirty = IsRegionGpuModified(*cpu_dest_address, amount); | ||
| 474 | if (!source_dirty && !dest_dirty) { | ||
| 475 | return false; | ||
| 476 | } | ||
| 477 | |||
| 478 | const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount}; | ||
| 479 | uncommitted_ranges.subtract(subtract_interval); | ||
| 480 | for (auto& interval_set : committed_ranges) { | ||
| 481 | interval_set.subtract(subtract_interval); | ||
| 482 | } | ||
| 483 | |||
| 484 | BufferId buffer_a; | ||
| 485 | BufferId buffer_b; | ||
| 486 | do { | ||
| 487 | has_deleted_buffers = false; | ||
| 488 | buffer_a = FindBuffer(*cpu_src_address, static_cast<u32>(amount)); | ||
| 489 | buffer_b = FindBuffer(*cpu_dest_address, static_cast<u32>(amount)); | ||
| 490 | } while (has_deleted_buffers); | ||
| 491 | auto& src_buffer = slot_buffers[buffer_a]; | ||
| 492 | auto& dest_buffer = slot_buffers[buffer_b]; | ||
| 493 | SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount)); | ||
| 494 | SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount)); | ||
| 495 | std::array copies{BufferCopy{ | ||
| 496 | .src_offset = src_buffer.Offset(*cpu_src_address), | ||
| 497 | .dst_offset = dest_buffer.Offset(*cpu_dest_address), | ||
| 498 | .size = amount, | ||
| 499 | }}; | ||
| 500 | |||
| 501 | boost::container::small_vector<IntervalType, 4> tmp_intervals; | ||
| 502 | auto mirror = [&](VAddr base_address, VAddr base_address_end) { | ||
| 503 | const u64 size = base_address_end - base_address; | ||
| 504 | const VAddr diff = base_address - *cpu_src_address; | ||
| 505 | const VAddr new_base_address = *cpu_dest_address + diff; | ||
| 506 | const IntervalType add_interval{new_base_address, new_base_address + size}; | ||
| 507 | uncommitted_ranges.add(add_interval); | ||
| 508 | tmp_intervals.push_back(add_interval); | ||
| 509 | }; | ||
| 510 | ForEachWrittenRange(*cpu_src_address, amount, mirror); | ||
| 511 | // This subtraction in this order is important for overlapping copies. | ||
| 512 | common_ranges.subtract(subtract_interval); | ||
| 513 | for (const IntervalType add_interval : tmp_intervals) { | ||
| 514 | common_ranges.add(add_interval); | ||
| 515 | } | ||
| 516 | |||
| 517 | runtime.CopyBuffer(dest_buffer, src_buffer, copies); | ||
| 518 | if (source_dirty) { | ||
| 519 | dest_buffer.MarkRegionAsGpuModified(*cpu_dest_address, amount); | ||
| 520 | } | ||
| 521 | std::vector<u8> tmp_buffer(amount); | ||
| 522 | cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); | ||
| 523 | cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); | ||
| 524 | return true; | ||
| 525 | } | ||
| 526 | |||
| 527 | template <class P> | ||
| 425 | void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, | 528 | void BufferCache<P>::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, |
| 426 | u32 size) { | 529 | u32 size) { |
| 427 | const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); | 530 | const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); |
| @@ -547,29 +650,30 @@ void BufferCache<P>::FlushCachedWrites() { | |||
| 547 | 650 | ||
| 548 | template <class P> | 651 | template <class P> |
| 549 | bool BufferCache<P>::HasUncommittedFlushes() const noexcept { | 652 | bool BufferCache<P>::HasUncommittedFlushes() const noexcept { |
| 550 | return !uncommitted_downloads.empty(); | 653 | return !uncommitted_ranges.empty() || !committed_ranges.empty(); |
| 551 | } | 654 | } |
| 552 | 655 | ||
| 553 | template <class P> | 656 | template <class P> |
| 554 | bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { | 657 | void BufferCache<P>::AccumulateFlushes() { |
| 555 | return !committed_downloads.empty() && !committed_downloads.front().empty(); | 658 | if (Settings::values.gpu_accuracy.GetValue() != Settings::GPUAccuracy::High) { |
| 659 | uncommitted_ranges.clear(); | ||
| 660 | return; | ||
| 661 | } | ||
| 662 | if (uncommitted_ranges.empty()) { | ||
| 663 | return; | ||
| 664 | } | ||
| 665 | committed_ranges.emplace_back(std::move(uncommitted_ranges)); | ||
| 556 | } | 666 | } |
| 557 | 667 | ||
| 558 | template <class P> | 668 | template <class P> |
| 559 | void BufferCache<P>::CommitAsyncFlushes() { | 669 | bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { |
| 560 | // This is intentionally passing the value by copy | 670 | return false; |
| 561 | committed_downloads.push_front(uncommitted_downloads); | ||
| 562 | uncommitted_downloads.clear(); | ||
| 563 | } | 671 | } |
| 564 | 672 | ||
| 565 | template <class P> | 673 | template <class P> |
| 566 | void BufferCache<P>::PopAsyncFlushes() { | 674 | void BufferCache<P>::CommitAsyncFlushesHigh() { |
| 567 | if (committed_downloads.empty()) { | 675 | AccumulateFlushes(); |
| 568 | return; | 676 | if (committed_ranges.empty()) { |
| 569 | } | ||
| 570 | auto scope_exit_pop_download = detail::ScopeExit([this] { committed_downloads.pop_back(); }); | ||
| 571 | const std::span<const BufferId> download_ids = committed_downloads.back(); | ||
| 572 | if (download_ids.empty()) { | ||
| 573 | return; | 677 | return; |
| 574 | } | 678 | } |
| 575 | MICROPROFILE_SCOPE(GPU_DownloadMemory); | 679 | MICROPROFILE_SCOPE(GPU_DownloadMemory); |
| @@ -577,20 +681,43 @@ void BufferCache<P>::PopAsyncFlushes() { | |||
| 577 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; | 681 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; |
| 578 | u64 total_size_bytes = 0; | 682 | u64 total_size_bytes = 0; |
| 579 | u64 largest_copy = 0; | 683 | u64 largest_copy = 0; |
| 580 | for (const BufferId buffer_id : download_ids) { | 684 | for (const IntervalSet& intervals : committed_ranges) { |
| 581 | slot_buffers[buffer_id].ForEachDownloadRange([&](u64 range_offset, u64 range_size) { | 685 | for (auto& interval : intervals) { |
| 582 | downloads.push_back({ | 686 | const std::size_t size = interval.upper() - interval.lower(); |
| 583 | BufferCopy{ | 687 | const VAddr cpu_addr = interval.lower(); |
| 584 | .src_offset = range_offset, | 688 | ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) { |
| 585 | .dst_offset = total_size_bytes, | 689 | boost::container::small_vector<BufferCopy, 1> copies; |
| 586 | .size = range_size, | 690 | buffer.ForEachDownloadRangeAndClear( |
| 587 | }, | 691 | cpu_addr, size, [&](u64 range_offset, u64 range_size) { |
| 588 | buffer_id, | 692 | const VAddr buffer_addr = buffer.CpuAddr(); |
| 693 | const auto add_download = [&](VAddr start, VAddr end) { | ||
| 694 | const u64 new_offset = start - buffer_addr; | ||
| 695 | const u64 new_size = end - start; | ||
| 696 | downloads.push_back({ | ||
| 697 | BufferCopy{ | ||
| 698 | .src_offset = new_offset, | ||
| 699 | .dst_offset = total_size_bytes, | ||
| 700 | .size = new_size, | ||
| 701 | }, | ||
| 702 | buffer_id, | ||
| 703 | }); | ||
| 704 | // Align up to avoid cache conflicts | ||
| 705 | constexpr u64 align = 256ULL; | ||
| 706 | constexpr u64 mask = ~(align - 1ULL); | ||
| 707 | total_size_bytes += (new_size + align - 1) & mask; | ||
| 708 | largest_copy = std::max(largest_copy, new_size); | ||
| 709 | }; | ||
| 710 | |||
| 711 | const VAddr start_address = buffer_addr + range_offset; | ||
| 712 | const VAddr end_address = start_address + range_size; | ||
| 713 | ForEachWrittenRange(start_address, range_size, add_download); | ||
| 714 | const IntervalType subtract_interval{start_address, end_address}; | ||
| 715 | common_ranges.subtract(subtract_interval); | ||
| 716 | }); | ||
| 589 | }); | 717 | }); |
| 590 | total_size_bytes += range_size; | 718 | } |
| 591 | largest_copy = std::max(largest_copy, range_size); | ||
| 592 | }); | ||
| 593 | } | 719 | } |
| 720 | committed_ranges.clear(); | ||
| 594 | if (downloads.empty()) { | 721 | if (downloads.empty()) { |
| 595 | return; | 722 | return; |
| 596 | } | 723 | } |
| @@ -623,6 +750,19 @@ void BufferCache<P>::PopAsyncFlushes() { | |||
| 623 | } | 750 | } |
| 624 | 751 | ||
| 625 | template <class P> | 752 | template <class P> |
| 753 | void BufferCache<P>::CommitAsyncFlushes() { | ||
| 754 | if (Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High) { | ||
| 755 | CommitAsyncFlushesHigh(); | ||
| 756 | } else { | ||
| 757 | uncommitted_ranges.clear(); | ||
| 758 | committed_ranges.clear(); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | |||
| 762 | template <class P> | ||
| 763 | void BufferCache<P>::PopAsyncFlushes() {} | ||
| 764 | |||
| 765 | template <class P> | ||
| 626 | bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { | 766 | bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { |
| 627 | const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); | 767 | const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); |
| 628 | for (u64 page = addr >> PAGE_BITS; page < page_end;) { | 768 | for (u64 page = addr >> PAGE_BITS; page < page_end;) { |
| @@ -642,6 +782,25 @@ bool BufferCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { | |||
| 642 | } | 782 | } |
| 643 | 783 | ||
| 644 | template <class P> | 784 | template <class P> |
| 785 | bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) { | ||
| 786 | const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE); | ||
| 787 | for (u64 page = addr >> PAGE_BITS; page < page_end;) { | ||
| 788 | const BufferId image_id = page_table[page]; | ||
| 789 | if (!image_id) { | ||
| 790 | ++page; | ||
| 791 | continue; | ||
| 792 | } | ||
| 793 | Buffer& buffer = slot_buffers[image_id]; | ||
| 794 | if (buffer.IsRegionCpuModified(addr, size)) { | ||
| 795 | return true; | ||
| 796 | } | ||
| 797 | const VAddr end_addr = buffer.CpuAddr() + buffer.SizeBytes(); | ||
| 798 | page = Common::DivCeil(end_addr, PAGE_SIZE); | ||
| 799 | } | ||
| 800 | return false; | ||
| 801 | } | ||
| 802 | |||
| 803 | template <class P> | ||
| 645 | void BufferCache<P>::BindHostIndexBuffer() { | 804 | void BufferCache<P>::BindHostIndexBuffer() { |
| 646 | Buffer& buffer = slot_buffers[index_buffer.buffer_id]; | 805 | Buffer& buffer = slot_buffers[index_buffer.buffer_id]; |
| 647 | TouchBuffer(buffer); | 806 | TouchBuffer(buffer); |
| @@ -649,7 +808,9 @@ void BufferCache<P>::BindHostIndexBuffer() { | |||
| 649 | const u32 size = index_buffer.size; | 808 | const u32 size = index_buffer.size; |
| 650 | SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); | 809 | SynchronizeBuffer(buffer, index_buffer.cpu_addr, size); |
| 651 | if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { | 810 | if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) { |
| 652 | runtime.BindIndexBuffer(buffer, offset, size); | 811 | const u32 new_offset = offset + maxwell3d.regs.index_array.first * |
| 812 | maxwell3d.regs.index_array.FormatSizeInBytes(); | ||
| 813 | runtime.BindIndexBuffer(buffer, new_offset, size); | ||
| 653 | } else { | 814 | } else { |
| 654 | runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format, | 815 | runtime.BindIndexBuffer(maxwell3d.regs.draw.topology, maxwell3d.regs.index_array.format, |
| 655 | maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count, | 816 | maxwell3d.regs.index_array.first, maxwell3d.regs.index_array.count, |
| @@ -863,7 +1024,7 @@ void BufferCache<P>::UpdateIndexBuffer() { | |||
| 863 | const GPUVAddr gpu_addr_end = index_array.EndAddress(); | 1024 | const GPUVAddr gpu_addr_end = index_array.EndAddress(); |
| 864 | const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); | 1025 | const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); |
| 865 | const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); | 1026 | const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); |
| 866 | const u32 draw_size = index_array.count * index_array.FormatSizeInBytes(); | 1027 | const u32 draw_size = (index_array.count + index_array.first) * index_array.FormatSizeInBytes(); |
| 867 | const u32 size = std::min(address_size, draw_size); | 1028 | const u32 size = std::min(address_size, draw_size); |
| 868 | if (size == 0 || !cpu_addr) { | 1029 | if (size == 0 || !cpu_addr) { |
| 869 | index_buffer = NULL_BINDING; | 1030 | index_buffer = NULL_BINDING; |
| @@ -1010,16 +1171,16 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s | |||
| 1010 | Buffer& buffer = slot_buffers[buffer_id]; | 1171 | Buffer& buffer = slot_buffers[buffer_id]; |
| 1011 | buffer.MarkRegionAsGpuModified(cpu_addr, size); | 1172 | buffer.MarkRegionAsGpuModified(cpu_addr, size); |
| 1012 | 1173 | ||
| 1013 | const bool is_accuracy_high = Settings::IsGPULevelHigh(); | 1174 | const IntervalType base_interval{cpu_addr, cpu_addr + size}; |
| 1175 | common_ranges.add(base_interval); | ||
| 1176 | |||
| 1177 | const bool is_accuracy_high = | ||
| 1178 | Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High; | ||
| 1014 | const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); | 1179 | const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
| 1015 | if (!is_accuracy_high || !is_async) { | 1180 | if (!is_async && !is_accuracy_high) { |
| 1016 | return; | ||
| 1017 | } | ||
| 1018 | if (std::ranges::find(uncommitted_downloads, buffer_id) != uncommitted_downloads.end()) { | ||
| 1019 | // Already inserted | ||
| 1020 | return; | 1181 | return; |
| 1021 | } | 1182 | } |
| 1022 | uncommitted_downloads.push_back(buffer_id); | 1183 | uncommitted_ranges.add(base_interval); |
| 1023 | } | 1184 | } |
| 1024 | 1185 | ||
| 1025 | template <class P> | 1186 | template <class P> |
| @@ -1103,7 +1264,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, | |||
| 1103 | if (!copies.empty()) { | 1264 | if (!copies.empty()) { |
| 1104 | runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies); | 1265 | runtime.CopyBuffer(slot_buffers[new_buffer_id], overlap, copies); |
| 1105 | } | 1266 | } |
| 1106 | ReplaceBufferDownloads(overlap_id, new_buffer_id); | ||
| 1107 | DeleteBuffer(overlap_id); | 1267 | DeleteBuffer(overlap_id); |
| 1108 | } | 1268 | } |
| 1109 | 1269 | ||
| @@ -1244,14 +1404,28 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si | |||
| 1244 | boost::container::small_vector<BufferCopy, 1> copies; | 1404 | boost::container::small_vector<BufferCopy, 1> copies; |
| 1245 | u64 total_size_bytes = 0; | 1405 | u64 total_size_bytes = 0; |
| 1246 | u64 largest_copy = 0; | 1406 | u64 largest_copy = 0; |
| 1247 | buffer.ForEachDownloadRange(cpu_addr, size, [&](u64 range_offset, u64 range_size) { | 1407 | buffer.ForEachDownloadRangeAndClear(cpu_addr, size, [&](u64 range_offset, u64 range_size) { |
| 1248 | copies.push_back(BufferCopy{ | 1408 | const VAddr buffer_addr = buffer.CpuAddr(); |
| 1249 | .src_offset = range_offset, | 1409 | const auto add_download = [&](VAddr start, VAddr end) { |
| 1250 | .dst_offset = total_size_bytes, | 1410 | const u64 new_offset = start - buffer_addr; |
| 1251 | .size = range_size, | 1411 | const u64 new_size = end - start; |
| 1252 | }); | 1412 | copies.push_back(BufferCopy{ |
| 1253 | total_size_bytes += range_size; | 1413 | .src_offset = new_offset, |
| 1254 | largest_copy = std::max(largest_copy, range_size); | 1414 | .dst_offset = total_size_bytes, |
| 1415 | .size = new_size, | ||
| 1416 | }); | ||
| 1417 | // Align up to avoid cache conflicts | ||
| 1418 | constexpr u64 align = 256ULL; | ||
| 1419 | constexpr u64 mask = ~(align - 1ULL); | ||
| 1420 | total_size_bytes += (new_size + align - 1) & mask; | ||
| 1421 | largest_copy = std::max(largest_copy, new_size); | ||
| 1422 | }; | ||
| 1423 | |||
| 1424 | const VAddr start_address = buffer_addr + range_offset; | ||
| 1425 | const VAddr end_address = start_address + range_size; | ||
| 1426 | ForEachWrittenRange(start_address, range_size, add_download); | ||
| 1427 | const IntervalType subtract_interval{start_address, end_address}; | ||
| 1428 | common_ranges.subtract(subtract_interval); | ||
| 1255 | }); | 1429 | }); |
| 1256 | if (total_size_bytes == 0) { | 1430 | if (total_size_bytes == 0) { |
| 1257 | return; | 1431 | return; |
| @@ -1316,18 +1490,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id) { | |||
| 1316 | } | 1490 | } |
| 1317 | 1491 | ||
| 1318 | template <class P> | 1492 | template <class P> |
| 1319 | void BufferCache<P>::ReplaceBufferDownloads(BufferId old_buffer_id, BufferId new_buffer_id) { | ||
| 1320 | const auto replace = [old_buffer_id, new_buffer_id](std::vector<BufferId>& buffers) { | ||
| 1321 | std::ranges::replace(buffers, old_buffer_id, new_buffer_id); | ||
| 1322 | if (auto it = std::ranges::find(buffers, new_buffer_id); it != buffers.end()) { | ||
| 1323 | buffers.erase(std::remove(it + 1, buffers.end(), new_buffer_id), buffers.end()); | ||
| 1324 | } | ||
| 1325 | }; | ||
| 1326 | replace(uncommitted_downloads); | ||
| 1327 | std::ranges::for_each(committed_downloads, replace); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | template <class P> | ||
| 1331 | void BufferCache<P>::NotifyBufferDeletion() { | 1493 | void BufferCache<P>::NotifyBufferDeletion() { |
| 1332 | if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { | 1494 | if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { |
| 1333 | dirty_uniform_buffers.fill(~u32{0}); | 1495 | dirty_uniform_buffers.fill(~u32{0}); |
| @@ -1349,15 +1511,9 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s | |||
| 1349 | if (!cpu_addr || size == 0) { | 1511 | if (!cpu_addr || size == 0) { |
| 1350 | return NULL_BINDING; | 1512 | return NULL_BINDING; |
| 1351 | } | 1513 | } |
| 1352 | // HACK(Rodrigo): This is the number of bytes bound in host beyond the guest API's range. | ||
| 1353 | // It exists due to some games like Astral Chain operate out of bounds. | ||
| 1354 | // Binding the whole map range would be technically correct, but games have large maps that make | ||
| 1355 | // this approach unaffordable for now. | ||
| 1356 | static constexpr u32 arbitrary_extra_bytes = 0xc000; | ||
| 1357 | const u32 bytes_to_map_end = static_cast<u32>(gpu_memory.BytesToMapEnd(gpu_addr)); | ||
| 1358 | const Binding binding{ | 1514 | const Binding binding{ |
| 1359 | .cpu_addr = *cpu_addr, | 1515 | .cpu_addr = *cpu_addr, |
| 1360 | .size = std::min(size + arbitrary_extra_bytes, bytes_to_map_end), | 1516 | .size = size, |
| 1361 | .buffer_id = BufferId{}, | 1517 | .buffer_id = BufferId{}, |
| 1362 | }; | 1518 | }; |
| 1363 | return binding; | 1519 | return binding; |
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 8b33c04ab..8d28bd884 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/cityhash.h" | 5 | #include "common/cityhash.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "common/settings.h" | ||
| 7 | #include "core/core.h" | 8 | #include "core/core.h" |
| 8 | #include "core/memory.h" | 9 | #include "core/memory.h" |
| 9 | #include "video_core/dma_pusher.h" | 10 | #include "video_core/dma_pusher.h" |
| @@ -76,8 +77,13 @@ bool DmaPusher::Step() { | |||
| 76 | 77 | ||
| 77 | // Push buffer non-empty, read a word | 78 | // Push buffer non-empty, read a word |
| 78 | command_headers.resize(command_list_header.size); | 79 | command_headers.resize(command_list_header.size); |
| 79 | gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), | 80 | if (Settings::IsGPULevelHigh()) { |
| 80 | command_list_header.size * sizeof(u32)); | 81 | gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), |
| 82 | command_list_header.size * sizeof(u32)); | ||
| 83 | } else { | ||
| 84 | gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), | ||
| 85 | command_list_header.size * sizeof(u32)); | ||
| 86 | } | ||
| 81 | } | 87 | } |
| 82 | for (std::size_t index = 0; index < command_headers.size();) { | 88 | for (std::size_t index = 0; index < command_headers.size();) { |
| 83 | const CommandHeader& command_header = command_headers[index]; | 89 | const CommandHeader& command_header = command_headers[index]; |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 2ee980bab..24481952b 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -21,6 +21,10 @@ MaxwellDMA::MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_) | |||
| 21 | 21 | ||
| 22 | MaxwellDMA::~MaxwellDMA() = default; | 22 | MaxwellDMA::~MaxwellDMA() = default; |
| 23 | 23 | ||
| 24 | void MaxwellDMA::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { | ||
| 25 | rasterizer = rasterizer_; | ||
| 26 | } | ||
| 27 | |||
| 24 | void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { | 28 | void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) { |
| 25 | ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); | 29 | ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register"); |
| 26 | 30 | ||
| @@ -44,7 +48,6 @@ void MaxwellDMA::Launch() { | |||
| 44 | 48 | ||
| 45 | // TODO(Subv): Perform more research and implement all features of this engine. | 49 | // TODO(Subv): Perform more research and implement all features of this engine. |
| 46 | const LaunchDMA& launch = regs.launch_dma; | 50 | const LaunchDMA& launch = regs.launch_dma; |
| 47 | ASSERT(launch.remap_enable == 0); | ||
| 48 | ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE); | 51 | ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE); |
| 49 | ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); | 52 | ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); |
| 50 | ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); | 53 | ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); |
| @@ -77,11 +80,29 @@ void MaxwellDMA::CopyPitchToPitch() { | |||
| 77 | // When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D | 80 | // When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D |
| 78 | // buffer of length `line_length_in`. | 81 | // buffer of length `line_length_in`. |
| 79 | // Otherwise we copy a 2D image of dimensions (line_length_in, line_count). | 82 | // Otherwise we copy a 2D image of dimensions (line_length_in, line_count). |
| 83 | auto& accelerate = rasterizer->AccessAccelerateDMA(); | ||
| 80 | if (!regs.launch_dma.multi_line_enable) { | 84 | if (!regs.launch_dma.multi_line_enable) { |
| 81 | memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in); | 85 | const bool is_buffer_clear = regs.launch_dma.remap_enable != 0 && |
| 86 | regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A; | ||
| 87 | // TODO: allow multisized components. | ||
| 88 | if (is_buffer_clear) { | ||
| 89 | ASSERT(regs.remap_const.component_size_minus_one == 3); | ||
| 90 | std::vector<u32> tmp_buffer(regs.line_length_in, regs.remap_consta_value); | ||
| 91 | memory_manager.WriteBlock(regs.offset_out, reinterpret_cast<u8*>(tmp_buffer.data()), | ||
| 92 | regs.line_length_in * sizeof(u32)); | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); | ||
| 96 | if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { | ||
| 97 | std::vector<u8> tmp_buffer(regs.line_length_in); | ||
| 98 | memory_manager.ReadBlockUnsafe(regs.offset_in, tmp_buffer.data(), regs.line_length_in); | ||
| 99 | memory_manager.WriteBlock(regs.offset_out, tmp_buffer.data(), regs.line_length_in); | ||
| 100 | } | ||
| 82 | return; | 101 | return; |
| 83 | } | 102 | } |
| 84 | 103 | ||
| 104 | UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); | ||
| 105 | |||
| 85 | // Perform a line-by-line copy. | 106 | // Perform a line-by-line copy. |
| 86 | // We're going to take a subrect of size (line_length_in, line_count) from the source rectangle. | 107 | // We're going to take a subrect of size (line_length_in, line_count) from the source rectangle. |
| 87 | // There is no need to manually flush/invalidate the regions because CopyBlock does that for us. | 108 | // There is no need to manually flush/invalidate the regions because CopyBlock does that for us. |
| @@ -105,6 +126,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { | |||
| 105 | } | 126 | } |
| 106 | 127 | ||
| 107 | // Deswizzle the input and copy it over. | 128 | // Deswizzle the input and copy it over. |
| 129 | UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); | ||
| 108 | const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in; | 130 | const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in; |
| 109 | const Parameters& src_params = regs.src_params; | 131 | const Parameters& src_params = regs.src_params; |
| 110 | const u32 width = src_params.width; | 132 | const u32 width = src_params.width; |
| @@ -134,6 +156,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() { | |||
| 134 | 156 | ||
| 135 | void MaxwellDMA::CopyPitchToBlockLinear() { | 157 | void MaxwellDMA::CopyPitchToBlockLinear() { |
| 136 | UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); | 158 | UNIMPLEMENTED_IF_MSG(regs.dst_params.block_size.width != 0, "Block width is not one"); |
| 159 | UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); | ||
| 137 | 160 | ||
| 138 | const auto& dst_params = regs.dst_params; | 161 | const auto& dst_params = regs.dst_params; |
| 139 | const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in; | 162 | const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in; |
| @@ -156,13 +179,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() { | |||
| 156 | write_buffer.resize(dst_size); | 179 | write_buffer.resize(dst_size); |
| 157 | } | 180 | } |
| 158 | 181 | ||
| 159 | if (Settings::IsGPULevelExtreme()) { | 182 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); |
| 160 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); | 183 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); |
| 161 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); | ||
| 162 | } else { | ||
| 163 | memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size); | ||
| 164 | memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); | ||
| 165 | } | ||
| 166 | 184 | ||
| 167 | // If the input is linear and the output is tiled, swizzle the input and copy it over. | 185 | // If the input is linear and the output is tiled, swizzle the input and copy it over. |
| 168 | if (regs.dst_params.block_size.depth > 0) { | 186 | if (regs.dst_params.block_size.depth > 0) { |
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index c77f02a22..4ed0d0996 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -21,8 +21,18 @@ namespace Tegra { | |||
| 21 | class MemoryManager; | 21 | class MemoryManager; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | namespace VideoCore { | ||
| 25 | class RasterizerInterface; | ||
| 26 | } | ||
| 27 | |||
| 24 | namespace Tegra::Engines { | 28 | namespace Tegra::Engines { |
| 25 | 29 | ||
| 30 | class AccelerateDMAInterface { | ||
| 31 | public: | ||
| 32 | /// Write the value to the register identified by method. | ||
| 33 | virtual bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) = 0; | ||
| 34 | }; | ||
| 35 | |||
| 26 | /** | 36 | /** |
| 27 | * This engine is known as gk104_copy. Documentation can be found in: | 37 | * This engine is known as gk104_copy. Documentation can be found in: |
| 28 | * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h | 38 | * https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h |
| @@ -187,6 +197,8 @@ public: | |||
| 187 | }; | 197 | }; |
| 188 | static_assert(sizeof(RemapConst) == 12); | 198 | static_assert(sizeof(RemapConst) == 12); |
| 189 | 199 | ||
| 200 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); | ||
| 201 | |||
| 190 | explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); | 202 | explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); |
| 191 | ~MaxwellDMA() override; | 203 | ~MaxwellDMA() override; |
| 192 | 204 | ||
| @@ -213,6 +225,7 @@ private: | |||
| 213 | Core::System& system; | 225 | Core::System& system; |
| 214 | 226 | ||
| 215 | MemoryManager& memory_manager; | 227 | MemoryManager& memory_manager; |
| 228 | VideoCore::RasterizerInterface* rasterizer; | ||
| 216 | 229 | ||
| 217 | std::vector<u8> read_buffer; | 230 | std::vector<u8> read_buffer; |
| 218 | std::vector<u8> write_buffer; | 231 | std::vector<u8> write_buffer; |
| @@ -240,7 +253,9 @@ private: | |||
| 240 | u32 pitch_out; | 253 | u32 pitch_out; |
| 241 | u32 line_length_in; | 254 | u32 line_length_in; |
| 242 | u32 line_count; | 255 | u32 line_count; |
| 243 | u32 reserved06[0xb8]; | 256 | u32 reserved06[0xb6]; |
| 257 | u32 remap_consta_value; | ||
| 258 | u32 remap_constb_value; | ||
| 244 | RemapConst remap_const; | 259 | RemapConst remap_const; |
| 245 | Parameters dst_params; | 260 | Parameters dst_params; |
| 246 | u32 reserved07[0x1]; | 261 | u32 reserved07[0x1]; |
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index f055b61e9..34dc6c596 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <queue> | 8 | #include <queue> |
| 9 | 9 | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/settings.h" | ||
| 11 | #include "core/core.h" | 12 | #include "core/core.h" |
| 12 | #include "video_core/delayed_destruction_ring.h" | 13 | #include "video_core/delayed_destruction_ring.h" |
| 13 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| @@ -53,6 +54,12 @@ public: | |||
| 53 | delayed_destruction_ring.Tick(); | 54 | delayed_destruction_ring.Tick(); |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 57 | // Unlike other fences, this one doesn't | ||
| 58 | void SignalOrdering() { | ||
| 59 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 60 | buffer_cache.AccumulateFlushes(); | ||
| 61 | } | ||
| 62 | |||
| 56 | void SignalSemaphore(GPUVAddr addr, u32 value) { | 63 | void SignalSemaphore(GPUVAddr addr, u32 value) { |
| 57 | TryReleasePendingFences(); | 64 | TryReleasePendingFences(); |
| 58 | const bool should_flush = ShouldFlush(); | 65 | const bool should_flush = ShouldFlush(); |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 35cc561be..ff024f530 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -50,6 +50,7 @@ void GPU::BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) { | |||
| 50 | maxwell_3d->BindRasterizer(rasterizer); | 50 | maxwell_3d->BindRasterizer(rasterizer); |
| 51 | fermi_2d->BindRasterizer(rasterizer); | 51 | fermi_2d->BindRasterizer(rasterizer); |
| 52 | kepler_compute->BindRasterizer(rasterizer); | 52 | kepler_compute->BindRasterizer(rasterizer); |
| 53 | maxwell_dma->BindRasterizer(rasterizer); | ||
| 53 | } | 54 | } |
| 54 | 55 | ||
| 55 | Engines::Maxwell3D& GPU::Maxwell3D() { | 56 | Engines::Maxwell3D& GPU::Maxwell3D() { |
| @@ -268,11 +269,13 @@ void GPU::CallPullerMethod(const MethodCall& method_call) { | |||
| 268 | case BufferMethods::SemaphoreAddressHigh: | 269 | case BufferMethods::SemaphoreAddressHigh: |
| 269 | case BufferMethods::SemaphoreAddressLow: | 270 | case BufferMethods::SemaphoreAddressLow: |
| 270 | case BufferMethods::SemaphoreSequence: | 271 | case BufferMethods::SemaphoreSequence: |
| 271 | case BufferMethods::RefCnt: | ||
| 272 | case BufferMethods::UnkCacheFlush: | 272 | case BufferMethods::UnkCacheFlush: |
| 273 | case BufferMethods::WrcacheFlush: | 273 | case BufferMethods::WrcacheFlush: |
| 274 | case BufferMethods::FenceValue: | 274 | case BufferMethods::FenceValue: |
| 275 | break; | 275 | break; |
| 276 | case BufferMethods::RefCnt: | ||
| 277 | rasterizer->SignalReference(); | ||
| 278 | break; | ||
| 276 | case BufferMethods::FenceAction: | 279 | case BufferMethods::FenceAction: |
| 277 | ProcessFenceActionMethod(); | 280 | ProcessFenceActionMethod(); |
| 278 | break; | 281 | break; |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 0cec4225b..58014c1c3 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -15,7 +15,10 @@ | |||
| 15 | 15 | ||
| 16 | namespace Tegra { | 16 | namespace Tegra { |
| 17 | class MemoryManager; | 17 | class MemoryManager; |
| 18 | namespace Engines { | ||
| 19 | class AccelerateDMAInterface; | ||
| 18 | } | 20 | } |
| 21 | } // namespace Tegra | ||
| 19 | 22 | ||
| 20 | namespace VideoCore { | 23 | namespace VideoCore { |
| 21 | 24 | ||
| @@ -63,6 +66,9 @@ public: | |||
| 63 | /// Signal a GPU based syncpoint as a fence | 66 | /// Signal a GPU based syncpoint as a fence |
| 64 | virtual void SignalSyncPoint(u32 value) = 0; | 67 | virtual void SignalSyncPoint(u32 value) = 0; |
| 65 | 68 | ||
| 69 | /// Signal a GPU based reference as point | ||
| 70 | virtual void SignalReference() = 0; | ||
| 71 | |||
| 66 | /// Release all pending fences. | 72 | /// Release all pending fences. |
| 67 | virtual void ReleaseFences() = 0; | 73 | virtual void ReleaseFences() = 0; |
| 68 | 74 | ||
| @@ -116,6 +122,8 @@ public: | |||
| 116 | return false; | 122 | return false; |
| 117 | } | 123 | } |
| 118 | 124 | ||
| 125 | [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0; | ||
| 126 | |||
| 119 | /// Attempt to use a faster method to display the framebuffer to screen | 127 | /// Attempt to use a faster method to display the framebuffer to screen |
| 120 | [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, | 128 | [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, |
| 121 | VAddr framebuffer_addr, u32 pixel_stride) { | 129 | VAddr framebuffer_addr, u32 pixel_stride) { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 07ad0e205..82c84127a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -171,7 +171,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra | |||
| 171 | buffer_cache_runtime(device), | 171 | buffer_cache_runtime(device), |
| 172 | buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), | 172 | buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), |
| 173 | shader_cache(*this, emu_window_, gpu, maxwell3d, kepler_compute, gpu_memory, device), | 173 | shader_cache(*this, emu_window_, gpu, maxwell3d, kepler_compute, gpu_memory, device), |
| 174 | query_cache(*this, maxwell3d, gpu_memory), | 174 | query_cache(*this, maxwell3d, gpu_memory), accelerate_dma(buffer_cache), |
| 175 | fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), | 175 | fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), |
| 176 | async_shaders(emu_window_) { | 176 | async_shaders(emu_window_) { |
| 177 | if (device.UseAsynchronousShaders()) { | 177 | if (device.UseAsynchronousShaders()) { |
| @@ -634,6 +634,13 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) { | |||
| 634 | fence_manager.SignalSyncPoint(value); | 634 | fence_manager.SignalSyncPoint(value); |
| 635 | } | 635 | } |
| 636 | 636 | ||
| 637 | void RasterizerOpenGL::SignalReference() { | ||
| 638 | if (!gpu.IsAsync()) { | ||
| 639 | return; | ||
| 640 | } | ||
| 641 | fence_manager.SignalOrdering(); | ||
| 642 | } | ||
| 643 | |||
| 637 | void RasterizerOpenGL::ReleaseFences() { | 644 | void RasterizerOpenGL::ReleaseFences() { |
| 638 | if (!gpu.IsAsync()) { | 645 | if (!gpu.IsAsync()) { |
| 639 | return; | 646 | return; |
| @@ -650,6 +657,7 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { | |||
| 650 | 657 | ||
| 651 | void RasterizerOpenGL::WaitForIdle() { | 658 | void RasterizerOpenGL::WaitForIdle() { |
| 652 | glMemoryBarrier(GL_ALL_BARRIER_BITS); | 659 | glMemoryBarrier(GL_ALL_BARRIER_BITS); |
| 660 | SignalReference(); | ||
| 653 | } | 661 | } |
| 654 | 662 | ||
| 655 | void RasterizerOpenGL::FragmentBarrier() { | 663 | void RasterizerOpenGL::FragmentBarrier() { |
| @@ -693,6 +701,10 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf | |||
| 693 | return true; | 701 | return true; |
| 694 | } | 702 | } |
| 695 | 703 | ||
| 704 | Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() { | ||
| 705 | return accelerate_dma; | ||
| 706 | } | ||
| 707 | |||
| 696 | bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, | 708 | bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, |
| 697 | VAddr framebuffer_addr, u32 pixel_stride) { | 709 | VAddr framebuffer_addr, u32 pixel_stride) { |
| 698 | if (framebuffer_addr == 0) { | 710 | if (framebuffer_addr == 0) { |
| @@ -1388,4 +1400,11 @@ void RasterizerOpenGL::EndTransformFeedback() { | |||
| 1388 | glEndTransformFeedback(); | 1400 | glEndTransformFeedback(); |
| 1389 | } | 1401 | } |
| 1390 | 1402 | ||
| 1403 | AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} | ||
| 1404 | |||
| 1405 | bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { | ||
| 1406 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 1407 | return buffer_cache.DMACopy(src_address, dest_address, amount); | ||
| 1408 | } | ||
| 1409 | |||
| 1391 | } // namespace OpenGL | 1410 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 482efed7a..ccee9ba33 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "common/common_types.h" | 19 | #include "common/common_types.h" |
| 20 | #include "video_core/engines/const_buffer_info.h" | 20 | #include "video_core/engines/const_buffer_info.h" |
| 21 | #include "video_core/engines/maxwell_3d.h" | 21 | #include "video_core/engines/maxwell_3d.h" |
| 22 | #include "video_core/engines/maxwell_dma.h" | ||
| 22 | #include "video_core/rasterizer_accelerated.h" | 23 | #include "video_core/rasterizer_accelerated.h" |
| 23 | #include "video_core/rasterizer_interface.h" | 24 | #include "video_core/rasterizer_interface.h" |
| 24 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 25 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| @@ -58,6 +59,16 @@ struct BindlessSSBO { | |||
| 58 | }; | 59 | }; |
| 59 | static_assert(sizeof(BindlessSSBO) * CHAR_BIT == 128); | 60 | static_assert(sizeof(BindlessSSBO) * CHAR_BIT == 128); |
| 60 | 61 | ||
| 62 | class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface { | ||
| 63 | public: | ||
| 64 | explicit AccelerateDMA(BufferCache& buffer_cache); | ||
| 65 | |||
| 66 | bool BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) override; | ||
| 67 | |||
| 68 | private: | ||
| 69 | BufferCache& buffer_cache; | ||
| 70 | }; | ||
| 71 | |||
| 61 | class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { | 72 | class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { |
| 62 | public: | 73 | public: |
| 63 | explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 74 | explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| @@ -83,6 +94,7 @@ public: | |||
| 83 | void ModifyGPUMemory(GPUVAddr addr, u64 size) override; | 94 | void ModifyGPUMemory(GPUVAddr addr, u64 size) override; |
| 84 | void SignalSemaphore(GPUVAddr addr, u32 value) override; | 95 | void SignalSemaphore(GPUVAddr addr, u32 value) override; |
| 85 | void SignalSyncPoint(u32 value) override; | 96 | void SignalSyncPoint(u32 value) override; |
| 97 | void SignalReference() override; | ||
| 86 | void ReleaseFences() override; | 98 | void ReleaseFences() override; |
| 87 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | 99 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 88 | void WaitForIdle() override; | 100 | void WaitForIdle() override; |
| @@ -93,6 +105,7 @@ public: | |||
| 93 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, | 105 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, |
| 94 | const Tegra::Engines::Fermi2D::Surface& dst, | 106 | const Tegra::Engines::Fermi2D::Surface& dst, |
| 95 | const Tegra::Engines::Fermi2D::Config& copy_config) override; | 107 | const Tegra::Engines::Fermi2D::Config& copy_config) override; |
| 108 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; | ||
| 96 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, | 109 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, |
| 97 | u32 pixel_stride) override; | 110 | u32 pixel_stride) override; |
| 98 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 111 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, |
| @@ -233,6 +246,7 @@ private: | |||
| 233 | BufferCache buffer_cache; | 246 | BufferCache buffer_cache; |
| 234 | ShaderCacheOpenGL shader_cache; | 247 | ShaderCacheOpenGL shader_cache; |
| 235 | QueryCache query_cache; | 248 | QueryCache query_cache; |
| 249 | AccelerateDMA accelerate_dma; | ||
| 236 | FenceManagerOpenGL fence_manager; | 250 | FenceManagerOpenGL fence_manager; |
| 237 | 251 | ||
| 238 | VideoCommon::Shader::AsyncShaders async_shaders; | 252 | VideoCommon::Shader::AsyncShaders async_shaders; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index bd4d649cc..e378a5679 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -251,7 +251,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra | |||
| 251 | buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), | 251 | buffer_cache(*this, maxwell3d, kepler_compute, gpu_memory, cpu_memory_, buffer_cache_runtime), |
| 252 | pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, | 252 | pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, |
| 253 | descriptor_pool, update_descriptor_queue), | 253 | descriptor_pool, update_descriptor_queue), |
| 254 | query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, | 254 | query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, accelerate_dma{buffer_cache}, |
| 255 | fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), | 255 | fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler), |
| 256 | wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { | 256 | wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { |
| 257 | scheduler.SetQueryCache(query_cache); | 257 | scheduler.SetQueryCache(query_cache); |
| @@ -580,6 +580,13 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) { | |||
| 580 | fence_manager.SignalSyncPoint(value); | 580 | fence_manager.SignalSyncPoint(value); |
| 581 | } | 581 | } |
| 582 | 582 | ||
| 583 | void RasterizerVulkan::SignalReference() { | ||
| 584 | if (!gpu.IsAsync()) { | ||
| 585 | return; | ||
| 586 | } | ||
| 587 | fence_manager.SignalOrdering(); | ||
| 588 | } | ||
| 589 | |||
| 583 | void RasterizerVulkan::ReleaseFences() { | 590 | void RasterizerVulkan::ReleaseFences() { |
| 584 | if (!gpu.IsAsync()) { | 591 | if (!gpu.IsAsync()) { |
| 585 | return; | 592 | return; |
| @@ -612,6 +619,7 @@ void RasterizerVulkan::WaitForIdle() { | |||
| 612 | cmdbuf.SetEvent(event, flags); | 619 | cmdbuf.SetEvent(event, flags); |
| 613 | cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {}); | 620 | cmdbuf.WaitEvents(event, flags, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, {}, {}, {}); |
| 614 | }); | 621 | }); |
| 622 | SignalReference(); | ||
| 615 | } | 623 | } |
| 616 | 624 | ||
| 617 | void RasterizerVulkan::FragmentBarrier() { | 625 | void RasterizerVulkan::FragmentBarrier() { |
| @@ -652,6 +660,10 @@ bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surf | |||
| 652 | return true; | 660 | return true; |
| 653 | } | 661 | } |
| 654 | 662 | ||
| 663 | Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() { | ||
| 664 | return accelerate_dma; | ||
| 665 | } | ||
| 666 | |||
| 655 | bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, | 667 | bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, |
| 656 | VAddr framebuffer_addr, u32 pixel_stride) { | 668 | VAddr framebuffer_addr, u32 pixel_stride) { |
| 657 | if (!framebuffer_addr) { | 669 | if (!framebuffer_addr) { |
| @@ -690,6 +702,13 @@ void RasterizerVulkan::FlushWork() { | |||
| 690 | draw_counter = 0; | 702 | draw_counter = 0; |
| 691 | } | 703 | } |
| 692 | 704 | ||
| 705 | AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} | ||
| 706 | |||
| 707 | bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { | ||
| 708 | std::scoped_lock lock{buffer_cache.mutex}; | ||
| 709 | return buffer_cache.DMACopy(src_address, dest_address, amount); | ||
| 710 | } | ||
| 711 | |||
| 693 | void RasterizerVulkan::SetupShaderDescriptors( | 712 | void RasterizerVulkan::SetupShaderDescriptors( |
| 694 | const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders, bool is_indexed) { | 713 | const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders, bool is_indexed) { |
| 695 | image_view_indices.clear(); | 714 | image_view_indices.clear(); |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 41459c5c5..3a78de258 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <boost/container/static_vector.hpp> | 13 | #include <boost/container/static_vector.hpp> |
| 14 | 14 | ||
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "video_core/engines/maxwell_dma.h" | ||
| 16 | #include "video_core/rasterizer_accelerated.h" | 17 | #include "video_core/rasterizer_accelerated.h" |
| 17 | #include "video_core/rasterizer_interface.h" | 18 | #include "video_core/rasterizer_interface.h" |
| 18 | #include "video_core/renderer_vulkan/blit_image.h" | 19 | #include "video_core/renderer_vulkan/blit_image.h" |
| @@ -49,6 +50,16 @@ struct VKScreenInfo; | |||
| 49 | 50 | ||
| 50 | class StateTracker; | 51 | class StateTracker; |
| 51 | 52 | ||
| 53 | class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface { | ||
| 54 | public: | ||
| 55 | explicit AccelerateDMA(BufferCache& buffer_cache); | ||
| 56 | |||
| 57 | bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override; | ||
| 58 | |||
| 59 | private: | ||
| 60 | BufferCache& buffer_cache; | ||
| 61 | }; | ||
| 62 | |||
| 52 | class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { | 63 | class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { |
| 53 | public: | 64 | public: |
| 54 | explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 65 | explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| @@ -75,6 +86,7 @@ public: | |||
| 75 | void ModifyGPUMemory(GPUVAddr addr, u64 size) override; | 86 | void ModifyGPUMemory(GPUVAddr addr, u64 size) override; |
| 76 | void SignalSemaphore(GPUVAddr addr, u32 value) override; | 87 | void SignalSemaphore(GPUVAddr addr, u32 value) override; |
| 77 | void SignalSyncPoint(u32 value) override; | 88 | void SignalSyncPoint(u32 value) override; |
| 89 | void SignalReference() override; | ||
| 78 | void ReleaseFences() override; | 90 | void ReleaseFences() override; |
| 79 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | 91 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 80 | void WaitForIdle() override; | 92 | void WaitForIdle() override; |
| @@ -85,6 +97,7 @@ public: | |||
| 85 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, | 97 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, |
| 86 | const Tegra::Engines::Fermi2D::Surface& dst, | 98 | const Tegra::Engines::Fermi2D::Surface& dst, |
| 87 | const Tegra::Engines::Fermi2D::Config& copy_config) override; | 99 | const Tegra::Engines::Fermi2D::Config& copy_config) override; |
| 100 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; | ||
| 88 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, | 101 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, |
| 89 | u32 pixel_stride) override; | 102 | u32 pixel_stride) override; |
| 90 | 103 | ||
| @@ -185,6 +198,7 @@ private: | |||
| 185 | BufferCache buffer_cache; | 198 | BufferCache buffer_cache; |
| 186 | VKPipelineCache pipeline_cache; | 199 | VKPipelineCache pipeline_cache; |
| 187 | VKQueryCache query_cache; | 200 | VKQueryCache query_cache; |
| 201 | AccelerateDMA accelerate_dma; | ||
| 188 | VKFenceManager fence_manager; | 202 | VKFenceManager fence_manager; |
| 189 | 203 | ||
| 190 | vk::Event wfi_event; | 204 | vk::Event wfi_event; |
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h index 9fbdc1ac6..47a11cb2f 100644 --- a/src/video_core/texture_cache/types.h +++ b/src/video_core/texture_cache/types.h | |||
| @@ -133,8 +133,8 @@ struct BufferImageCopy { | |||
| 133 | }; | 133 | }; |
| 134 | 134 | ||
| 135 | struct BufferCopy { | 135 | struct BufferCopy { |
| 136 | size_t src_offset; | 136 | u64 src_offset; |
| 137 | size_t dst_offset; | 137 | u64 dst_offset; |
| 138 | size_t size; | 138 | size_t size; |
| 139 | }; | 139 | }; |
| 140 | 140 | ||
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index e04f7dfc6..b1e02c57a 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp | |||
| @@ -8,7 +8,17 @@ | |||
| 8 | #include <string> | 8 | #include <string> |
| 9 | 9 | ||
| 10 | #include <fmt/format.h> | 10 | #include <fmt/format.h> |
| 11 | |||
| 12 | #ifdef __GNUC__ | ||
| 13 | #pragma GCC diagnostic push | ||
| 14 | #ifndef __clang__ | ||
| 15 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | ||
| 16 | #endif | ||
| 17 | #endif | ||
| 11 | #include <httplib.h> | 18 | #include <httplib.h> |
| 19 | #ifdef __GNUC__ | ||
| 20 | #pragma GCC diagnostic pop | ||
| 21 | #endif | ||
| 12 | 22 | ||
| 13 | #include "common/logging/log.h" | 23 | #include "common/logging/log.h" |
| 14 | #include "web_service/web_backend.h" | 24 | #include "web_service/web_backend.h" |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 87cb9dc93..8c71ad5c1 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -839,7 +839,6 @@ void Config::ReadRendererValues() { | |||
| 839 | ReadGlobalSetting(Settings::values.use_nvdec_emulation); | 839 | ReadGlobalSetting(Settings::values.use_nvdec_emulation); |
| 840 | ReadGlobalSetting(Settings::values.accelerate_astc); | 840 | ReadGlobalSetting(Settings::values.accelerate_astc); |
| 841 | ReadGlobalSetting(Settings::values.use_vsync); | 841 | ReadGlobalSetting(Settings::values.use_vsync); |
| 842 | ReadGlobalSetting(Settings::values.disable_fps_limit); | ||
| 843 | ReadGlobalSetting(Settings::values.use_assembly_shaders); | 842 | ReadGlobalSetting(Settings::values.use_assembly_shaders); |
| 844 | ReadGlobalSetting(Settings::values.use_asynchronous_shaders); | 843 | ReadGlobalSetting(Settings::values.use_asynchronous_shaders); |
| 845 | ReadGlobalSetting(Settings::values.use_fast_gpu_time); | 844 | ReadGlobalSetting(Settings::values.use_fast_gpu_time); |
| @@ -1369,7 +1368,6 @@ void Config::SaveRendererValues() { | |||
| 1369 | WriteGlobalSetting(Settings::values.use_nvdec_emulation); | 1368 | WriteGlobalSetting(Settings::values.use_nvdec_emulation); |
| 1370 | WriteGlobalSetting(Settings::values.accelerate_astc); | 1369 | WriteGlobalSetting(Settings::values.accelerate_astc); |
| 1371 | WriteGlobalSetting(Settings::values.use_vsync); | 1370 | WriteGlobalSetting(Settings::values.use_vsync); |
| 1372 | WriteGlobalSetting(Settings::values.disable_fps_limit); | ||
| 1373 | WriteGlobalSetting(Settings::values.use_assembly_shaders); | 1371 | WriteGlobalSetting(Settings::values.use_assembly_shaders); |
| 1374 | WriteGlobalSetting(Settings::values.use_asynchronous_shaders); | 1372 | WriteGlobalSetting(Settings::values.use_asynchronous_shaders); |
| 1375 | WriteGlobalSetting(Settings::values.use_fast_gpu_time); | 1373 | WriteGlobalSetting(Settings::values.use_fast_gpu_time); |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 8d13c9857..a9e611125 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp | |||
| @@ -28,7 +28,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { | |||
| 28 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); | 28 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); |
| 29 | 29 | ||
| 30 | ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); | 30 | ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); |
| 31 | ui->disable_fps_limit->setChecked(Settings::values.disable_fps_limit.GetValue()); | ||
| 32 | ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); | 31 | ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); |
| 33 | ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); | 32 | ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); |
| 34 | ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue()); | 33 | ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue()); |
| @@ -59,8 +58,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { | |||
| 59 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, | 58 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, |
| 60 | ui->anisotropic_filtering_combobox); | 59 | ui->anisotropic_filtering_combobox); |
| 61 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); | 60 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); |
| 62 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.disable_fps_limit, | ||
| 63 | ui->disable_fps_limit, disable_fps_limit); | ||
| 64 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, | 61 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, |
| 65 | ui->use_assembly_shaders, use_assembly_shaders); | 62 | ui->use_assembly_shaders, use_assembly_shaders); |
| 66 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, | 63 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, |
| @@ -103,7 +100,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 103 | if (Settings::IsConfiguringGlobal()) { | 100 | if (Settings::IsConfiguringGlobal()) { |
| 104 | ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); | 101 | ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); |
| 105 | ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); | 102 | ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); |
| 106 | ui->disable_fps_limit->setEnabled(Settings::values.disable_fps_limit.UsingGlobal()); | ||
| 107 | ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); | 103 | ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); |
| 108 | ui->use_asynchronous_shaders->setEnabled( | 104 | ui->use_asynchronous_shaders->setEnabled( |
| 109 | Settings::values.use_asynchronous_shaders.UsingGlobal()); | 105 | Settings::values.use_asynchronous_shaders.UsingGlobal()); |
| @@ -116,8 +112,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 116 | } | 112 | } |
| 117 | 113 | ||
| 118 | ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); | 114 | ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); |
| 119 | ConfigurationShared::SetColoredTristate(ui->disable_fps_limit, | ||
| 120 | Settings::values.disable_fps_limit, disable_fps_limit); | ||
| 121 | ConfigurationShared::SetColoredTristate( | 115 | ConfigurationShared::SetColoredTristate( |
| 122 | ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders); | 116 | ui->use_assembly_shaders, Settings::values.use_assembly_shaders, use_assembly_shaders); |
| 123 | ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, | 117 | ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 6ac5f20ec..9148aacf2 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h | |||
| @@ -35,7 +35,6 @@ private: | |||
| 35 | std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; | 35 | std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; |
| 36 | 36 | ||
| 37 | ConfigurationShared::CheckState use_vsync; | 37 | ConfigurationShared::CheckState use_vsync; |
| 38 | ConfigurationShared::CheckState disable_fps_limit; | ||
| 39 | ConfigurationShared::CheckState use_assembly_shaders; | 38 | ConfigurationShared::CheckState use_assembly_shaders; |
| 40 | ConfigurationShared::CheckState use_asynchronous_shaders; | 39 | ConfigurationShared::CheckState use_asynchronous_shaders; |
| 41 | ConfigurationShared::CheckState use_fast_gpu_time; | 40 | ConfigurationShared::CheckState use_fast_gpu_time; |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 18c43629e..ad0840355 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui | |||
| @@ -77,24 +77,6 @@ | |||
| 77 | </widget> | 77 | </widget> |
| 78 | </item> | 78 | </item> |
| 79 | <item> | 79 | <item> |
| 80 | <widget class="QCheckBox" name="disable_fps_limit"> | ||
| 81 | <property name="enabled"> | ||
| 82 | <bool>true</bool> | ||
| 83 | </property> | ||
| 84 | <property name="toolTip"> | ||
| 85 | <string> | ||
| 86 | <html><head/><body> | ||
| 87 | <p>Presents guest frames as they become available, disabling the FPS limit in most titles.</p> | ||
| 88 | <p>NOTE: Will cause instabilities.</p> | ||
| 89 | </body></html> | ||
| 90 | </string> | ||
| 91 | </property> | ||
| 92 | <property name="text"> | ||
| 93 | <string>Disable framerate limit (experimental)</string> | ||
| 94 | </property> | ||
| 95 | </widget> | ||
| 96 | </item> | ||
| 97 | <item> | ||
| 98 | <widget class="QCheckBox" name="use_assembly_shaders"> | 80 | <widget class="QCheckBox" name="use_assembly_shaders"> |
| 99 | <property name="toolTip"> | 81 | <property name="toolTip"> |
| 100 | <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string> | 82 | <string>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</string> |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 5ed3b90b8..fbd5001e9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -1355,6 +1355,9 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S | |||
| 1355 | 1355 | ||
| 1356 | ConfigureVibration::SetAllVibrationDevices(); | 1356 | ConfigureVibration::SetAllVibrationDevices(); |
| 1357 | 1357 | ||
| 1358 | // Disable fps limit toggle when booting a new title | ||
| 1359 | Settings::values.disable_fps_limit.SetValue(false); | ||
| 1360 | |||
| 1358 | // Save configurations | 1361 | // Save configurations |
| 1359 | UpdateUISettings(); | 1362 | UpdateUISettings(); |
| 1360 | game_list->SaveInterfaceLayout(); | 1363 | game_list->SaveInterfaceLayout(); |
| @@ -1428,8 +1431,10 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S | |||
| 1428 | std::filesystem::path{filename.toStdU16String()}.filename()); | 1431 | std::filesystem::path{filename.toStdU16String()}.filename()); |
| 1429 | } | 1432 | } |
| 1430 | const bool is_64bit = system.Kernel().CurrentProcess()->Is64BitProcess(); | 1433 | const bool is_64bit = system.Kernel().CurrentProcess()->Is64BitProcess(); |
| 1431 | const auto instruction_set_suffix = is_64bit ? " (64-bit)" : " (32-bit)"; | 1434 | const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)"); |
| 1432 | title_name += instruction_set_suffix; | 1435 | title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit") |
| 1436 | .arg(QString::fromStdString(title_name), instruction_set_suffix) | ||
| 1437 | .toStdString(); | ||
| 1433 | LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); | 1438 | LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); |
| 1434 | const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor(); | 1439 | const auto gpu_vendor = system.GPU().Renderer().GetDeviceVendor(); |
| 1435 | UpdateWindowTitle(title_name, title_version, gpu_vendor); | 1440 | UpdateWindowTitle(title_name, title_version, gpu_vendor); |
| @@ -2913,7 +2918,12 @@ void GMainWindow::UpdateStatusBar() { | |||
| 2913 | } else { | 2918 | } else { |
| 2914 | emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); | 2919 | emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); |
| 2915 | } | 2920 | } |
| 2916 | game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); | 2921 | if (Settings::values.disable_fps_limit) { |
| 2922 | game_fps_label->setText( | ||
| 2923 | tr("Game: %1 FPS (Limit off)").arg(results.average_game_fps, 0, 'f', 0)); | ||
| 2924 | } else { | ||
| 2925 | game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); | ||
| 2926 | } | ||
| 2917 | emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); | 2927 | emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); |
| 2918 | 2928 | ||
| 2919 | emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); | 2929 | emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); |