aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h13
-rw-r--r--src/video_core/macro.cpp17
-rw-r--r--src/video_core/macro_memo.h30
-rw-r--r--src/video_core/nx2_profile.h19
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;