// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include #include #include #include #include #include "common/container/unordered_map.h" #include #include "common/common_types.h" #include "common/thread_worker.h" #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/value.h" #include "shader_recompiler/frontend/maxwell/control_flow.h" #include "shader_recompiler/host_translate_info.h" #include "shader_recompiler/object_pool.h" #include "shader_recompiler/profile.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/renderer_vulkan/fixed_pipeline_state.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" #include "video_core/renderer_vulkan/vk_compute_pipeline.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/shader_cache.h" namespace Core { class System; } namespace Shader::IR { struct Program; } namespace VideoCore { class ShaderNotify; } namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct ComputePipelineCacheKey { u64 unique_hash; u32 shared_memory_size; std::array workgroup_size; size_t Hash() const noexcept; bool operator==(const ComputePipelineCacheKey& rhs) const noexcept; bool operator!=(const ComputePipelineCacheKey& rhs) const noexcept { return !operator==(rhs); } }; static_assert(std::has_unique_object_representations_v); static_assert(std::is_trivially_copyable_v); static_assert(std::is_trivially_constructible_v); } // namespace Vulkan namespace std { template <> struct hash { size_t operator()(const Vulkan::ComputePipelineCacheKey& k) const noexcept { return k.Hash(); } }; } // namespace std namespace Vulkan { class ComputePipeline; class DescriptorPool; class Device; class PipelineStatistics; class RenderPassCache; class Scheduler; using VideoCommon::ShaderInfo; struct ShaderPools { void ReleaseContents() { flow_block.ReleaseContents(); block.ReleaseContents(); inst.ReleaseContents(); } Shader::ObjectPool inst{8192}; Shader::ObjectPool block{32}; Shader::ObjectPool flow_block{32}; }; class PipelineCache : public VideoCommon::ShaderCache { public: explicit PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device, Scheduler& scheduler, DescriptorPool& descriptor_pool, GuestDescriptorQueue& guest_descriptor_queue, DescriptorBufferRing& descriptor_buffer_ring, RenderPassCache& render_pass_cache, BufferCache& buffer_cache, TextureCache& texture_cache, VideoCore::ShaderNotify& shader_notify_); ~PipelineCache(); [[nodiscard]] GraphicsPipeline* CurrentGraphicsPipeline(); void TickFrame(); [[nodiscard]] ComputePipeline* CurrentComputePipeline(); void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback); private: [[nodiscard]] GraphicsPipeline* CurrentGraphicsPipelineSlowPath(); [[nodiscard]] GraphicsPipeline* BuiltPipeline(GraphicsPipeline* pipeline) const noexcept; std::unique_ptr CreateGraphicsPipeline(); std::unique_ptr CreateGraphicsPipeline( ShaderPools& pools, const GraphicsPipelineCacheKey& key, std::span envs, PipelineStatistics* statistics, bool build_in_parallel); std::unique_ptr CreateComputePipeline(const ComputePipelineCacheKey& key, const ShaderInfo* shader); std::unique_ptr CreateComputePipeline(ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, PipelineStatistics* statistics, bool build_in_parallel); void SerializeVulkanPipelineCache(const std::filesystem::path& filename, const vk::PipelineCache& pipeline_cache, u32 cache_version); vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename, u32 expected_cache_version); void QueueVulkanPipelineCacheFlush(); const Device& device; Scheduler& scheduler; DescriptorPool& descriptor_pool; GuestDescriptorQueue& guest_descriptor_queue; DescriptorBufferRing& descriptor_buffer_ring; RenderPassCache& render_pass_cache; BufferCache& buffer_cache; TextureCache& texture_cache; VideoCore::ShaderNotify& shader_notify; bool use_asynchronous_shaders{}; bool use_vulkan_pipeline_cache{}; 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>> evicted_pipelines; ///< (tick, pipeline) std::unordered_set used_and_evicted; ///< Key hashes of pipelines used this session, evicted since ::Common::unordered_map> compute_cache; ::Common::unordered_map> graphics_cache; ShaderPools main_pools; Shader::Profile profile; Shader::HostTranslateInfo host_info; std::filesystem::path pipeline_cache_filename; /// Key hashes of the graphics pipelines in the disk cache. Only those a recent session used /// (used_pipelines) are built at boot; the rest are built when first needed, and not stored /// again. Each built pipeline holds driver memory (title 1: 6.5k cached, a quarter used). std::unordered_set disk_pipelines; std::unordered_map used_pipelines; ///< Key hash -> sessions since it was last used std::unordered_set used_this_session; std::filesystem::path used_pipelines_filename; std::chrono::steady_clock::time_point used_pipelines_saved{}; void SaveUsedPipelines(); std::filesystem::path vulkan_pipeline_cache_filename; vk::PipelineCache vulkan_pipeline_cache; size_t pipelines_since_flush{}; std::chrono::steady_clock::time_point last_flush{}; std::atomic last_cache_size{}; std::atomic_bool flush_in_flight{}; Common::ThreadWorker workers; Common::ThreadWorker serialization_thread; DynamicFeatures dynamic_features; }; } // namespace Vulkan