diff options
| -rw-r--r-- | src/video_core/buffer_cache/buffer_cache.h | 13 | ||||
| -rw-r--r-- | src/video_core/macro.cpp | 17 | ||||
| -rw-r--r-- | src/video_core/macro_memo.h | 30 | ||||
| -rw-r--r-- | src/video_core/nx2_profile.h | 19 |
4 files changed, 69 insertions, 10 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e2de0eede1..b909971e42 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -679,6 +679,19 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { Buffer& buffer = slot_buffers[buffer_id]; second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset; const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset); + if (Nx2Profile::Enabled()) { + u64 on_dma_pages = 0; + const auto& pages = Nx2Profile::DmaReadPages(); + for (DAddr page = orig_device_addr >> 12; page <= (orig_device_addr + copy.size - 1) >> 12; + ++page) { + if (pages.contains(page)) { + const DAddr begin = (std::max)(orig_device_addr, page << 12); + const DAddr end = (std::min)(orig_device_addr + copy.size, (page + 1) << 12); + on_dma_pages += end - begin; + } + } + Nx2Profile::Add(Nx2Profile::Counter::DownloadBytesOnDmaPages, on_dma_pages); + } async_downloads.Add(orig_device_addr, copy.size); buffer.MarkUsage(copy.src_offset, copy.size); runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); diff --git a/src/video_core/macro.cpp b/src/video_core/macro.cpp index 3422cb16fa..50e32ce999 100644 --- a/src/video_core/macro.cpp +++ b/src/video_core/macro.cpp @@ -1420,6 +1420,14 @@ void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u // from a culling dispatch) and still be only in host buffers: flush them first, // or the macro draws with an older frame's arguments (objects pop in and out). const size_t read_bytes = size_t{std::min<u32>(value, 0x1000)} * 4; + if (VideoCommon::Nx2Profile::Enabled()) { + VideoCommon::Nx2Profile::Add(VideoCommon::Nx2Profile::Counter::DmaReadBytes, read_bytes); + for (GPUVAddr page = source & ~GPUVAddr{0xfff}; page < source + read_bytes; page += 0x1000) { + if (const auto device = memory.GpuToCpuAddress(page)) { + VideoCommon::Nx2Profile::DmaReadPages().insert(*device >> 12); + } + } + } if (memory.IsMemoryDirty(source, read_bytes, VideoCommon::CacheType::BufferCache)) { memory.FlushRegion(source, read_bytes, VideoCommon::CacheType::BufferCache); static u64 flushes{}; @@ -1462,10 +1470,11 @@ void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u memo_stats && (++native_calls & 0xffffff) == 0) { const auto& stats = native_memo.GetStatistics(); LOG_INFO(HW_GPU, - "Macro memo: {} hits, {} misses, {} guard failures, {} stores ({} in " - "place), {} clears; {} entries, {} KiB", - stats.hits, stats.misses, stats.guard_failures, stats.stores, - stats.overwrites, stats.clears, native_memo.Size(), + "Macro memo: {} hits ({} on the last call's slot), {} misses, {} guard " + "failures, {} stores ({} in place), {} compactions, {} clears; {} entries, " + "{} KiB", + stats.hits, stats.slot_hits, stats.misses, stats.guard_failures, stats.stores, + stats.overwrites, stats.compactions, stats.clears, native_memo.Size(), native_memo.ArenaWords() * 4 / 1024); } native_memo.Synchronize(native_code_generation); diff --git a/src/video_core/macro_memo.h b/src/video_core/macro_memo.h index 0e994fc78a..7fe914b4f2 100644 --- a/src/video_core/macro_memo.h +++ b/src/video_core/macro_memo.h @@ -41,7 +41,8 @@ public: }; struct Statistics { - std::uint64_t hits{}, misses{}, guard_failures{}, stores{}, overwrites{}, clears{}; + std::uint64_t hits{}, slot_hits{}, misses{}, guard_failures{}, stores{}, overwrites{}, + compactions{}, clears{}; }; BasicMacroMemo() { @@ -57,6 +58,7 @@ public: if (last.first == start && last.second != NoRecord && Match(last.second, start, parameters, data_ram, effects)) { ++statistics.hits; + ++statistics.slot_hits; return true; } const auto it = entries.find(Hash(start, parameters)); @@ -127,8 +129,7 @@ public: } } if (arena.size() >= MaxArenaWords) { - Clear(); - ++statistics.clears; + Compact(); } const auto offset = static_cast<std::uint32_t>(arena.size()); arena.push_back(start); @@ -186,8 +187,31 @@ private: last_by_start.fill({0, NoRecord}); } + /// Copies the records still in the map to a new arena. A call stored again with another + /// shape leaves its old record behind: title 1 filled the arena with them every ~2 s, and + /// clearing it made the next ~20,000 calls interpret again. Clears only when the live + /// records alone would fill most of it. + void Compact() { + compacted.clear(); + for (auto& entry : entries) { + const std::uint32_t* record = arena.data() + entry.second; + const std::size_t words = + HeaderWords + record[1] + std::size_t{2} * (record[2] + record[3] + record[4]); + entry.second = static_cast<std::uint32_t>(compacted.size()); + compacted.insert(compacted.end(), record, record + words); + } + arena.swap(compacted); + last_by_start.fill({0, NoRecord}); + ++statistics.compactions; + if (arena.size() >= MaxArenaWords / 4 * 3) { + Clear(); + ++statistics.clears; + } + } + Map entries; std::vector<std::uint32_t> arena; + std::vector<std::uint32_t> compacted; ///< Compact's new arena (kept for its capacity) /// The last record found or stored per macro start (direct mapped). std::array<std::pair<std::uint32_t, std::uint32_t>, 256> last_by_start{}; std::uint64_t generation{}; diff --git a/src/video_core/nx2_profile.h b/src/video_core/nx2_profile.h index 088bfc5fe7..e4609cf77d 100644 --- a/src/video_core/nx2_profile.h +++ b/src/video_core/nx2_profile.h @@ -7,6 +7,7 @@ #include <chrono> #include <cstdlib> #include <string> +#include <unordered_set> #include "common/common_types.h" #include "common/logging.h" @@ -74,7 +75,7 @@ inline u64 Now() { struct ThreadData { std::array<u64, static_cast<size_t>(Zone::Count)> exclusive_ns{}; std::array<u64, static_cast<size_t>(Zone::Count)> count{}; - std::array<u64, 2> counters{}; ///< See Counter + std::array<u64, 4> counters{}; ///< See Counter u64 child_ns{}; ///< Time of the zones nested in the current one u64 window_start{}; }; @@ -118,8 +119,17 @@ private: enum class Counter : u32 { DownloadBytes, ///< Buffer data copied back from the host GPU at fences DownloadCopies, ///< Buffer ranges copied back + DmaReadBytes, ///< Guest memory the native MME read with DMA + DownloadBytesOnDmaPages, ///< Of DownloadBytes: on pages the MME has DMA-read }; +/// Device pages (4 KiB) the native MME has DMA-read since start (GPU thread only; filled only +/// while profiling): which buffer downloads anything on the GPU thread reads back. +inline std::unordered_set<u64>& DmaReadPages() { + static std::unordered_set<u64> pages; + return pages; +} + inline void Add(Counter counter, u64 amount) { if (Enabled()) { t_data.counters[static_cast<size_t>(counter)] += amount; @@ -158,9 +168,12 @@ inline void Report() { const double calls = static_cast<double>(data.count[i]) * 1e9 / window; line += fmt::format(" | {} {:.1f} ms ({:.0f}/s)", zone_names[i], ms, calls); } - line += fmt::format(" | buffer downloads {:.0f} KiB in {:.0f} copies", + line += fmt::format(" | buffer downloads {:.0f} KiB in {:.0f} copies ({:.0f} KiB on pages " + "MME DMA reads) | MME DMA reads {:.0f} KiB", static_cast<double>(data.counters[0]) * 1e9 / 1024.0 / window, - static_cast<double>(data.counters[1]) * 1e9 / window); + static_cast<double>(data.counters[1]) * 1e9 / window, + static_cast<double>(data.counters[3]) * 1e9 / 1024.0 / window, + static_cast<double>(data.counters[2]) * 1e9 / 1024.0 / window); LOG_INFO(HW_GPU, "GPU thread per second:{}", line); data = {}; data.window_start = now; |
