#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_PIPELINECACHE_H_ #define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_PIPELINECACHE_H_ #include "common/abi.h" #include "common/assert.h" #include "common/common.h" #include "graphics/host_gpu/renderer/colorRenderTarget.h" #include "graphics/host_gpu/renderer/depthRenderTarget.h" #include "graphics/host_gpu/renderer/renderTarget.h" #include "graphics/host_gpu/vulkanCommon.h" #include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h" #include "graphics/shader/rectListShader.h" #include "graphics/shader/shader.h" #include #include #include #include #include #include #include #include namespace Libs::Graphics { struct GraphicContext; struct ShaderParams; class CommandBuffer; namespace ShaderRecompiler { struct CompileOptions; struct ShaderSource; struct TranslateResult; } // namespace ShaderRecompiler namespace ShaderRecompiler::IR { class ImageHeapSource; } // namespace ShaderRecompiler::IR #pragma pack(push, 1) struct PipelineStaticParameters { bool negative_one_to_one = false; bool depth_clip_enable = true; vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; // 0 without tessellation, 3 for a rectangle list, else the input control points. uint8_t patch_control_points = 0; bool primitive_restart_enable = false; uint32_t samples = 1; bool sample_shading_enable = false; bool alpha_to_coverage_enable = false; uint32_t color_mask[RENDER_COLOR_ATTACHMENTS_MAX] = {}; bool cull_front = false; bool cull_back = false; bool face = false; bool provoking_vtx_last = false; vk::PolygonMode polygon_mode = vk::PolygonMode::eFill; uint8_t color_srcblend[RENDER_COLOR_ATTACHMENTS_MAX] = {}; uint8_t color_comb_fcn[RENDER_COLOR_ATTACHMENTS_MAX] = {}; uint8_t color_destblend[RENDER_COLOR_ATTACHMENTS_MAX] = {}; uint8_t alpha_srcblend[RENDER_COLOR_ATTACHMENTS_MAX] = {}; uint8_t alpha_comb_fcn[RENDER_COLOR_ATTACHMENTS_MAX] = {}; uint8_t alpha_destblend[RENDER_COLOR_ATTACHMENTS_MAX] = {}; bool separate_alpha_blend[RENDER_COLOR_ATTACHMENTS_MAX] = {}; bool blend_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {}; }; struct PipelineRenderingState { std::array color_formats {}; vk::Format depth_format = vk::Format::eUndefined; vk::Format stencil_format = vk::Format::eUndefined; uint32_t color_count = 0; }; struct PipelineVertexInputState { struct Binding { uint32_t stride = 0; uint8_t instance = 0; }; struct Attribute { uint32_t offset = 0; uint8_t binding = 0; }; std::array bindings {}; std::array attributes {}; uint8_t binding_count = 0; uint8_t attribute_count = 0; }; // Everything a graphics pipeline is made of besides the stage modules its ids name. Aligned so // that rendering.color_formats can be passed to Vulkan. struct alignas(4) GraphicsPipelineKey { // VS, LS or mesh; HS; TES; PS. 0 marks an absent stage. std::array stage_ids {}; PipelineRenderingState rendering; PipelineVertexInputState vertex_input; PipelineStaticParameters static_params; bool operator==(const GraphicsPipelineKey& other) const; }; #pragma pack(pop) // Keys are compared and hashed as bytes. static_assert(std::has_unique_object_representations_v); // A compiled stage with what pipeline creation needs from it besides the key. struct ShaderModule { uint64_t id = 0; vk::ShaderModule module = nullptr; ShaderRecompiler::IR::CompiledShaderInfo info; ShaderRecompiler::IR::ResourceSpecialization specialization; std::vector function_code; // VS or LS: the host format of each vertex input attribute. std::vector vertex_formats; // Rectangle-list PS: the parameters it reads. std::vector rect_parameters; }; // A draw's topology, shader stages (from PipelineCache::GetGraphicsPrograms) and targets. struct DrawRenderState { RenderDepthInfo depth_info; RenderColorInfo color_info[RENDER_COLOR_ATTACHMENTS_MAX] = {}; uint32_t color_count = 0; // Color targets the pixel program writes. uint32_t mrt_mask = 0; vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; bool primitive_restart_enable = false; bool ps_active = false; std::array vertex_info; ShaderPixelInputInfo ps_input_info; [[nodiscard]] std::span VertexStages() const { return {vertex_info.data(), vertex_info[1].stage ? 3u : 1u}; } }; // The owning renderer serializes access, including saves while the GPU is running. class PipelineCache { public: PipelineCache(GraphicContext& graphics, ShaderRecompiler::IR::ImageHeapSource* image_heap); ~PipelineCache(); KYTY_CLASS_NO_COPY(PipelineCache); void Save(); struct Pipeline { vk::PipelineLayout pipeline_layout = nullptr; vk::Pipeline pipeline = nullptr; vk::DescriptorSetLayout descriptor_set_layout = nullptr; bool uses_push_descriptors = false; bool uses_image_heap = false; }; // Reads the shaders and registers bound in `command` into the state's shader stages. void GetGraphicsPrograms(const CommandBuffer& command, DrawRenderState& state); // Null skips the dispatch. const ShaderModule* GetComputeProgram(const HW::ComputeShaderInfo& regs, ShaderComputeInputInfo& input_info); Pipeline& GetGraphicsPipeline(const CommandBuffer& command, const DrawRenderState& state); Pipeline& GetComputePipeline(const ShaderComputeInputInfo& input_info); private: friend struct PipelineCacheTestAccess; struct Program { std::unique_ptr call_source; ShaderRecompiler::IR::ResourcePlan resource_plan; ShaderRecompiler::IR::ResourceSnapshot resources; ShaderRecompiler::IR::ResourceSpecialization specialization; std::vector modules; bool skip_dispatch = false; [[nodiscard]] std::span FunctionCode() const; bool RefreshCallSource(const ShaderRecompiler::IR::SrtRuntime& runtime); template ShaderModule* FindModule(const ShaderRecompiler::IR::SrtRuntime& runtime, uint32_t push_data_cursor); }; struct ProgramKey { ShaderType stage = ShaderType::Unknown; uint64_t hash = 0; uint32_t user_data_count = 0; std::vector static_state; bool operator==(const ProgramKey&) const = default; }; // Variants of one source share a bucket; ProgramKey equality compares the state. struct ProgramKeyHash { std::size_t operator()(const ProgramKey& key) const { return key.hash; } }; struct GraphicsPipelineKeyHash { std::size_t operator()(const GraphicsPipelineKey& key) const; }; GraphicContext& m_graphics; ShaderRecompiler::IR::ImageHeapSource* m_image_heap; std::unordered_map m_program_cache; ProgramKey m_lookup_key; vk::PipelineCache m_driver_cache = nullptr; std::filesystem::path m_driver_cache_path; std::chrono::steady_clock::time_point m_next_driver_cache_save; bool m_driver_cache_dirty = false; std::unordered_map m_graphics_pipelines; std::unordered_map m_compute_pipelines; template const ShaderModule* GetProgram(const ShaderParams& params, InputInfo& input_info, uint32_t& push_data_cursor); ShaderModule& CompileModule(Program& program, const ProgramKey& key, ShaderRecompiler::CompileOptions options, ShaderRecompiler::TranslateResult translated, uint32_t push_data_cursor); void InitializeDriverCache(); void CheckpointDriverCache(); }; // Stages are indexed like GraphicsPipelineKey::stage_ids. void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& pipeline, const GraphicsPipelineKey& key, const std::array& stages, vk::PipelineCache driver_cache); void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::Pipeline& pipeline, const ShaderModule& module, vk::PipelineCache driver_cache); } // namespace Libs::Graphics #endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_PIPELINECACHE_H_