aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h16
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp57
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp1
4 files changed, 82 insertions, 1 deletions
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index b1ac5a3fcc..dcb9f48abe 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -119,6 +119,21 @@ public:
: nullptr;
}
+ /// The frame the pipeline cache last handed this pipeline out (evicts long unused ones).
+ void MarkUsed(u64 frame) noexcept {
+ last_use_frame = frame;
+ }
+
+ [[nodiscard]] u64 LastUse() const noexcept {
+ return last_use_frame;
+ }
+
+ /// Forgets the transitions, which may point to pipelines the cache evicted.
+ void ClearTransitions() noexcept {
+ transition_keys.clear();
+ transitions.clear();
+ }
+
[[nodiscard]] bool IsBuilt() const noexcept {
return is_built.load(std::memory_order::relaxed);
}
@@ -159,6 +174,7 @@ private:
std::vector<GraphicsPipelineCacheKey> transition_keys;
std::vector<GraphicsPipeline*> transitions;
+ u64 last_use_frame{};
std::array<vk::ShaderModule, NUM_STAGES> spv_modules;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index f877b9d341..2eee5dbf45 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -613,7 +613,8 @@ void PipelineCache::SaveUsedPipelines() {
used_pipelines_saved = std::chrono::steady_clock::now();
// A session too short to say what the game uses (a boot to the menu) keeps the old list.
constexpr size_t MinimumUsed = 64;
- if (used_pipelines_filename.empty() || used_this_session.size() < MinimumUsed) {
+ if (used_pipelines_filename.empty() ||
+ used_this_session.size() + used_and_evicted.size() < MinimumUsed) {
return;
}
std::unordered_map<u64, u8> next;
@@ -623,6 +624,9 @@ void PipelineCache::SaveUsedPipelines() {
next.emplace(key.Hash(), u8{0});
}
}
+ for (const u64 hash : used_and_evicted) {
+ next.emplace(hash, u8{0});
+ }
for (const auto& [hash, age] : used_pipelines) {
if (age + 1 < UsedPipelinesMaxAge) {
next.try_emplace(hash, static_cast<u8>(age + 1));
@@ -650,6 +654,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
if (next) {
current_pipeline = next;
+ current_pipeline->MarkUsed(frame_count);
return BuiltPipeline(current_pipeline);
}
}
@@ -929,9 +934,58 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
current_pipeline->AddTransition(pipeline.get());
}
current_pipeline = pipeline.get();
+ current_pipeline->MarkUsed(frame_count);
return BuiltPipeline(current_pipeline);
}
+void PipelineCache::TickFrame() {
+ ++frame_count;
+ while (!evicted_pipelines.empty() && scheduler.IsFree(evicted_pipelines.front().first)) {
+ evicted_pipelines.pop_front();
+ }
+ constexpr u64 EvictCheckFrames = 600;
+ if (frame_count % EvictCheckFrames == 0) {
+ EvictUnusedPipelines();
+ }
+}
+
+void PipelineCache::EvictUnusedPipelines() {
+ // Each built pipeline holds ~0.45 MB of driver memory and none was ever destroyed, so it grew
+ // with every place a session went (title 1: 1,500 on its title screen alone). One unused for
+ // this long is destroyed; it takes 2-3 ms to build again from the driver's pipeline cache.
+ constexpr u64 EvictAfterFrames = 9000;
+ if (frame_count <= EvictAfterFrames) {
+ return;
+ }
+ const u64 cutoff = frame_count - EvictAfterFrames;
+ const u64 tick = scheduler.CurrentTick();
+ size_t evicted = 0;
+ for (auto it = graphics_cache.begin(); it != graphics_cache.end();) {
+ GraphicsPipeline* const pipeline = it->second.get();
+ if (pipeline == nullptr || pipeline == current_pipeline || !pipeline->IsBuilt() ||
+ pipeline->LastUse() >= cutoff) {
+ ++it;
+ continue;
+ }
+ if (used_this_session.erase(pipeline) != 0) {
+ used_and_evicted.insert(it->first.Hash());
+ }
+ evicted_pipelines.emplace_back(tick, std::move(it->second));
+ it = graphics_cache.erase(it);
+ ++evicted;
+ }
+ if (evicted == 0) {
+ return;
+ }
+ for (auto& [key, pipeline] : graphics_cache) {
+ if (pipeline) {
+ pipeline->ClearTransitions();
+ }
+ }
+ LOG_INFO(Render_Vulkan, "Evicted {} graphics pipelines unused for {} frames, {} left", evicted,
+ EvictAfterFrames, graphics_cache.size());
+}
+
GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const noexcept {
if (pipeline->IsBuilt()) {
return pipeline;
@@ -1126,6 +1180,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
QueueVulkanPipelineCacheFlush();
return pipeline;
}
+ disk_pipelines.insert(graphics_key.Hash());
serialization_thread.QueueWork([this, key = graphics_key, envs = std::move(environments.envs)] {
boost::container::static_vector<const GenericEnvironment*, Maxwell::MaxShaderProgram>
env_ptrs;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 4d1abf83d5..66fa615ea5 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -10,6 +10,7 @@
#include <atomic>
#include <chrono>
#include <cstddef>
+#include <deque>
#include <filesystem>
#include <unordered_map>
#include <unordered_set>
@@ -116,6 +117,8 @@ public:
[[nodiscard]] GraphicsPipeline* CurrentGraphicsPipeline();
+ void TickFrame();
+
[[nodiscard]] ComputePipeline* CurrentComputePipeline();
void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
@@ -165,6 +168,12 @@ private:
GraphicsPipelineCacheKey graphics_key{};
GraphicsPipeline* current_pipeline{};
+ /// Destroys graphics pipelines unused for a while, once the GPU is done with them.
+ void EvictUnusedPipelines();
+ u64 frame_count{};
+ std::deque<std::pair<u64, std::unique_ptr<GraphicsPipeline>>> evicted_pipelines; ///< (tick, pipeline)
+ std::unordered_set<u64> used_and_evicted; ///< Key hashes of pipelines used this session, evicted since
+
::Common::unordered_map<ComputePipelineCacheKey, std::unique_ptr<ComputePipeline>> compute_cache;
::Common::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 0826820714..d659e44fe2 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1193,6 +1193,7 @@ void RasterizerVulkan::FlushCommands() {
void RasterizerVulkan::TickFrame() {
draw_counter = 0;
+ pipeline_cache.TickFrame();
guest_descriptor_queue.TickFrame();
compute_pass_descriptor_queue.TickFrame();
descriptor_buffer_ring.TickFrame();