/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_VULKAN_VULKAN_PIPELINE_STATE_CACHE_H_ #define XENIA_GPU_VULKAN_VULKAN_PIPELINE_STATE_CACHE_H_ #include #include #include #include #include #include "xenia/base/hash.h" #include "xenia/base/platform.h" #include "xenia/base/xxhash.h" #include "xenia/gpu/primitive_processor.h" #include "xenia/gpu/register_file.h" #include "xenia/gpu/registers.h" #include "xenia/gpu/spirv_shader_translator.h" #include "xenia/gpu/vulkan/vulkan_render_target_cache.h" #include "xenia/gpu/vulkan/vulkan_shader.h" #include "xenia/gpu/xenos.h" #include "xenia/ui/vulkan/vulkan_provider.h" namespace xe { namespace gpu { namespace vulkan { class VulkanCommandProcessor; // TODO(Triang3l): Create a common base for both the Vulkan and the Direct3D // implementations. class VulkanPipelineCache { public: class PipelineLayoutProvider { public: virtual ~PipelineLayoutProvider() {} virtual VkPipelineLayout GetPipelineLayout() const = 0; protected: PipelineLayoutProvider() = default; }; VulkanPipelineCache(VulkanCommandProcessor& command_processor, const RegisterFile& register_file, VulkanRenderTargetCache& render_target_cache); ~VulkanPipelineCache(); bool Initialize(); void Shutdown(); void ClearCache(); VulkanShader* LoadShader(xenos::ShaderType shader_type, const uint32_t* host_address, uint32_t dword_count); // Analyze shader microcode on the translator thread. void AnalyzeShaderUcode(Shader& shader) { shader.AnalyzeUcode(ucode_disasm_buffer_); } // Retrieves the shader modification for the current state. The shader must // have microcode analyzed. SpirvShaderTranslator::Modification GetCurrentVertexShaderModification( const Shader& shader, Shader::HostVertexShaderType host_vertex_shader_type) const; SpirvShaderTranslator::Modification GetCurrentPixelShaderModification( const Shader& shader, uint32_t normalized_color_mask) const; // TODO(Triang3l): Return a deferred creation handle. bool ConfigurePipeline( VulkanShader::VulkanTranslation* vertex_shader, VulkanShader::VulkanTranslation* pixel_shader, const PrimitiveProcessor::ProcessingResult& primitive_processing_result, reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t normalized_color_mask, VulkanRenderTargetCache::RenderPassKey render_pass_key, VkPipeline& pipeline_out, const PipelineLayoutProvider*& pipeline_layout_out); private: enum class PipelineGeometryShader : uint32_t { kNone, kRectangleList, }; enum class PipelinePrimitiveTopology : uint32_t { kPointList, kLineList, kLineStrip, kTriangleList, kTriangleStrip, kTriangleFan, kLineListWithAdjacency, kPatchList, }; enum class PipelinePolygonMode : uint32_t { kFill, kLine, kPoint, }; enum class PipelineBlendFactor : uint32_t { kZero, kOne, kSrcColor, kOneMinusSrcColor, kDstColor, kOneMinusDstColor, kSrcAlpha, kOneMinusSrcAlpha, kDstAlpha, kOneMinusDstAlpha, kConstantColor, kOneMinusConstantColor, kConstantAlpha, kOneMinusConstantAlpha, kSrcAlphaSaturate, }; // Update PipelineDescription::kVersion if anything is changed! XEPACKEDSTRUCT(PipelineRenderTarget, { PipelineBlendFactor src_color_blend_factor : 4; // 4 PipelineBlendFactor dst_color_blend_factor : 4; // 8 xenos::BlendOp color_blend_op : 3; // 11 PipelineBlendFactor src_alpha_blend_factor : 4; // 15 PipelineBlendFactor dst_alpha_blend_factor : 4; // 19 xenos::BlendOp alpha_blend_op : 3; // 22 uint32_t color_write_mask : 4; // 26 }); XEPACKEDSTRUCT(PipelineDescription, { uint64_t vertex_shader_hash; uint64_t vertex_shader_modification; // 0 if no pixel shader. uint64_t pixel_shader_hash; uint64_t pixel_shader_modification; VulkanRenderTargetCache::RenderPassKey render_pass_key; // Shader stages. PipelineGeometryShader geometry_shader : 2; // 2 // Input assembly. PipelinePrimitiveTopology primitive_topology : 3; // 5 uint32_t primitive_restart : 1; // 6 // Rasterization. uint32_t depth_clamp_enable : 1; // 7 PipelinePolygonMode polygon_mode : 2; // 9 uint32_t cull_front : 1; // 10 uint32_t cull_back : 1; // 11 uint32_t front_face_clockwise : 1; // 12 // Depth / stencil. uint32_t depth_write_enable : 1; // 13 xenos::CompareFunction depth_compare_op : 3; // 15 uint32_t stencil_test_enable : 1; // 17 xenos::StencilOp stencil_front_fail_op : 3; // 20 xenos::StencilOp stencil_front_pass_op : 3; // 23 xenos::StencilOp stencil_front_depth_fail_op : 3; // 26 xenos::CompareFunction stencil_front_compare_op : 3; // 29 xenos::StencilOp stencil_back_fail_op : 3; // 32 xenos::StencilOp stencil_back_pass_op : 3; // 3 xenos::StencilOp stencil_back_depth_fail_op : 3; // 6 xenos::CompareFunction stencil_back_compare_op : 3; // 9 // Filled only for the attachments present in the render pass object. PipelineRenderTarget render_targets[xenos::kMaxColorRenderTargets]; // Including all the padding, for a stable hash. PipelineDescription() { Reset(); } PipelineDescription(const PipelineDescription& description) { std::memcpy(this, &description, sizeof(*this)); } PipelineDescription& operator=(const PipelineDescription& description) { std::memcpy(this, &description, sizeof(*this)); return *this; } bool operator==(const PipelineDescription& description) const { return std::memcmp(this, &description, sizeof(*this)) == 0; } void Reset() { std::memset(this, 0, sizeof(*this)); } uint64_t GetHash() const { return XXH3_64bits(this, sizeof(*this)); } struct Hasher { size_t operator()(const PipelineDescription& description) const { return size_t(description.GetHash()); } }; }); struct Pipeline { VkPipeline pipeline = VK_NULL_HANDLE; // Owned by VulkanCommandProcessor, valid until ClearCache. const PipelineLayoutProvider* pipeline_layout; Pipeline(const PipelineLayoutProvider* pipeline_layout_provider) : pipeline_layout(pipeline_layout_provider) {} }; // Description that can be passed from the command processor thread to the // creation threads, with everything needed from caches pre-looked-up. struct PipelineCreationArguments { std::pair* pipeline; const VulkanShader::VulkanTranslation* vertex_shader; const VulkanShader::VulkanTranslation* pixel_shader; VkRenderPass render_pass; }; // Can be called from multiple threads. bool TranslateAnalyzedShader(SpirvShaderTranslator& translator, VulkanShader::VulkanTranslation& translation); void WritePipelineRenderTargetDescription( reg::RB_BLENDCONTROL blend_control, uint32_t write_mask, PipelineRenderTarget& render_target_out) const; bool GetCurrentStateDescription( const VulkanShader::VulkanTranslation* vertex_shader, const VulkanShader::VulkanTranslation* pixel_shader, const PrimitiveProcessor::ProcessingResult& primitive_processing_result, reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t normalized_color_mask, VulkanRenderTargetCache::RenderPassKey render_pass_key, PipelineDescription& description_out) const; // Whether the pipeline for the given description is supported by the device. bool ArePipelineRequirementsMet(const PipelineDescription& description) const; // Can be called from creation threads - all needed data must be fully set up // at the point of the call: shaders must be translated, pipeline layout and // render pass objects must be available. bool EnsurePipelineCreated( const PipelineCreationArguments& creation_arguments); VulkanCommandProcessor& command_processor_; const RegisterFile& register_file_; VulkanRenderTargetCache& render_target_cache_; VkShaderModule gs_rectangle_list_ = VK_NULL_HANDLE; // Temporary storage for AnalyzeUcode calls on the processor thread. StringBuffer ucode_disasm_buffer_; // Reusable shader translator on the command processor thread. std::unique_ptr shader_translator_; // Ucode hash -> shader. std::unordered_map> shaders_; std::unordered_map pipelines_; // Previously used pipeline, to avoid lookups if the state wasn't changed. const std::pair* last_pipeline_ = nullptr; }; } // namespace vulkan } // namespace gpu } // namespace xe #endif // XENIA_GPU_VULKAN_VULKAN_PIPELINE_STATE_CACHE_H_