/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2016 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_VULKAN_PIPELINE_CACHE_H_ #define XENIA_GPU_VULKAN_PIPELINE_CACHE_H_ #include #include "xenia/base/string_buffer.h" #include "xenia/base/xxhash.h" #include "xenia/gpu/register_file.h" #include "xenia/gpu/spirv_shader_translator.h" #include "xenia/gpu/vulkan/render_cache.h" #include "xenia/gpu/vulkan/vulkan_shader.h" #include "xenia/gpu/xenos.h" #include "xenia/ui/spirv/spirv_disassembler.h" #include "xenia/ui/vulkan/vulkan.h" #include "xenia/ui/vulkan/vulkan_device.h" namespace xe { namespace gpu { namespace vulkan { // Configures and caches pipelines based on render state. // This is responsible for properly setting all state required for a draw // including shaders, various blend/etc options, and input configuration. class PipelineCache { public: enum class UpdateStatus { kCompatible, kMismatch, kError, }; PipelineCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device); ~PipelineCache(); VkResult Initialize(VkDescriptorSetLayout uniform_descriptor_set_layout, VkDescriptorSetLayout texture_descriptor_set_layout, VkDescriptorSetLayout vertex_descriptor_set_layout); void Shutdown(); // Loads a shader from the cache, possibly translating it. VulkanShader* LoadShader(xenos::ShaderType shader_type, uint32_t guest_address, const uint32_t* host_address, uint32_t dword_count); // Configures a pipeline using the current render state and the given render // pass. If a previously available pipeline is available it will be used, // otherwise a new one may be created. Any state that can be set dynamically // in the command buffer is issued at this time. // Returns whether the pipeline could be successfully created. UpdateStatus ConfigurePipeline(VkCommandBuffer command_buffer, const RenderState* render_state, VulkanShader* vertex_shader, VulkanShader* pixel_shader, xenos::PrimitiveType primitive_type, VkPipeline* pipeline_out); // Sets required dynamic state on the command buffer. // Only state that has changed since the last call will be set unless // full_update is true. bool SetDynamicState(VkCommandBuffer command_buffer, bool full_update); // Pipeline layout shared by all pipelines. VkPipelineLayout pipeline_layout() const { return pipeline_layout_; } // Clears all cached content. void ClearCache(); private: // Creates or retrieves an existing pipeline for the currently configured // state. VkPipeline GetPipeline(const RenderState* render_state, uint64_t hash_key); bool TranslateShader(VulkanShader::VulkanTranslation& translation); void DumpShaderDisasmAMD(VkPipeline pipeline); void DumpShaderDisasmNV(const VkGraphicsPipelineCreateInfo& info); // Gets a geometry shader used to emulate the given primitive type. // Returns nullptr if the primitive doesn't need to be emulated. VkShaderModule GetGeometryShader(xenos::PrimitiveType primitive_type, bool is_line_mode); RegisterFile* register_file_ = nullptr; ui::vulkan::VulkanDevice* device_ = nullptr; // Temporary storage for AnalyzeUcode calls. StringBuffer ucode_disasm_buffer_; // Reusable shader translator. std::unique_ptr shader_translator_ = nullptr; // Disassembler used to get the SPIRV disasm. Only used in debug. xe::ui::spirv::SpirvDisassembler disassembler_; // All loaded shaders mapped by their guest hash key. std::unordered_map shader_map_; // Vulkan pipeline cache, which in theory helps us out. // This can be serialized to disk and reused, if we want. VkPipelineCache pipeline_cache_ = nullptr; // Layout used for all pipelines describing our uniforms, textures, and push // constants. VkPipelineLayout pipeline_layout_ = nullptr; // Shared geometry shaders. struct { VkShaderModule line_quad_list; VkShaderModule point_list; VkShaderModule quad_list; VkShaderModule rect_list; } geometry_shaders_; // Shared dummy pixel shader. VkShaderModule dummy_pixel_shader_; // Hash state used to incrementally produce pipeline hashes during update. // By the time the full update pass has run the hash will represent the // current state in a way that can uniquely identify the produced VkPipeline. XXH3_state_t hash_state_; // All previously generated pipelines mapped by hash. std::unordered_map cached_pipelines_; // Previously used pipeline. This matches our current state settings // and allows us to quickly(ish) reuse the pipeline if no registers have // changed. VkPipeline current_pipeline_ = nullptr; private: UpdateStatus UpdateState(VulkanShader* vertex_shader, VulkanShader* pixel_shader, xenos::PrimitiveType primitive_type); UpdateStatus UpdateRenderTargetState(); UpdateStatus UpdateShaderStages(VulkanShader* vertex_shader, VulkanShader* pixel_shader, xenos::PrimitiveType primitive_type); UpdateStatus UpdateVertexInputState(VulkanShader* vertex_shader); UpdateStatus UpdateInputAssemblyState(xenos::PrimitiveType primitive_type); UpdateStatus UpdateViewportState(); UpdateStatus UpdateRasterizationState(xenos::PrimitiveType primitive_type); UpdateStatus UpdateMultisampleState(); UpdateStatus UpdateDepthStencilState(); UpdateStatus UpdateColorBlendState(); bool SetShadowRegister(uint32_t* dest, uint32_t register_name); bool SetShadowRegister(float* dest, uint32_t register_name); bool SetShadowRegisterArray(uint32_t* dest, uint32_t num, uint32_t register_name); struct UpdateRenderTargetsRegisters { uint32_t rb_modecontrol; reg::RB_SURFACE_INFO rb_surface_info; reg::RB_COLOR_INFO rb_color_info; reg::RB_DEPTH_INFO rb_depth_info; reg::RB_COLOR_INFO rb_color1_info; reg::RB_COLOR_INFO rb_color2_info; reg::RB_COLOR_INFO rb_color3_info; uint32_t rb_color_mask; uint32_t rb_depthcontrol; uint32_t rb_stencilrefmask; UpdateRenderTargetsRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_render_targets_regs_; struct UpdateShaderStagesRegisters { xenos::PrimitiveType primitive_type; uint32_t pa_su_sc_mode_cntl; reg::SQ_PROGRAM_CNTL sq_program_cntl; VulkanShader* vertex_shader; VulkanShader* pixel_shader; UpdateShaderStagesRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_shader_stages_regs_; VkPipelineShaderStageCreateInfo update_shader_stages_info_[3]; uint32_t update_shader_stages_stage_count_ = 0; struct UpdateVertexInputStateRegisters { VulkanShader* vertex_shader; UpdateVertexInputStateRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_vertex_input_state_regs_; VkPipelineVertexInputStateCreateInfo update_vertex_input_state_info_; VkVertexInputBindingDescription update_vertex_input_state_binding_descrs_[32]; VkVertexInputAttributeDescription update_vertex_input_state_attrib_descrs_[96]; struct UpdateInputAssemblyStateRegisters { xenos::PrimitiveType primitive_type; uint32_t pa_su_sc_mode_cntl; uint32_t multi_prim_ib_reset_index; UpdateInputAssemblyStateRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_input_assembly_state_regs_; VkPipelineInputAssemblyStateCreateInfo update_input_assembly_state_info_; struct UpdateViewportStateRegisters { // uint32_t pa_cl_clip_cntl; uint32_t rb_surface_info; uint32_t pa_cl_vte_cntl; uint32_t pa_su_sc_mode_cntl; uint32_t pa_sc_window_offset; uint32_t pa_sc_window_scissor_tl; uint32_t pa_sc_window_scissor_br; float pa_cl_vport_xoffset; float pa_cl_vport_yoffset; float pa_cl_vport_zoffset; float pa_cl_vport_xscale; float pa_cl_vport_yscale; float pa_cl_vport_zscale; UpdateViewportStateRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_viewport_state_regs_; VkPipelineViewportStateCreateInfo update_viewport_state_info_; struct UpdateRasterizationStateRegisters { xenos::PrimitiveType primitive_type; uint32_t pa_cl_clip_cntl; uint32_t pa_su_sc_mode_cntl; uint32_t pa_sc_screen_scissor_tl; uint32_t pa_sc_screen_scissor_br; uint32_t pa_sc_viz_query; uint32_t pa_su_poly_offset_enable; uint32_t multi_prim_ib_reset_index; UpdateRasterizationStateRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_rasterization_state_regs_; VkPipelineRasterizationStateCreateInfo update_rasterization_state_info_; struct UpdateMultisampleStateeRegisters { uint32_t pa_sc_aa_config; uint32_t pa_su_sc_mode_cntl; uint32_t rb_surface_info; UpdateMultisampleStateeRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_multisample_state_regs_; VkPipelineMultisampleStateCreateInfo update_multisample_state_info_; struct UpdateDepthStencilStateRegisters { uint32_t rb_depthcontrol; uint32_t rb_stencilrefmask; UpdateDepthStencilStateRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_depth_stencil_state_regs_; VkPipelineDepthStencilStateCreateInfo update_depth_stencil_state_info_; struct UpdateColorBlendStateRegisters { uint32_t rb_color_mask; uint32_t rb_blendcontrol[4]; uint32_t rb_modecontrol; UpdateColorBlendStateRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } update_color_blend_state_regs_; VkPipelineColorBlendStateCreateInfo update_color_blend_state_info_; VkPipelineColorBlendAttachmentState update_color_blend_attachment_states_[4]; struct SetDynamicStateRegisters { uint32_t pa_sc_window_offset; uint32_t pa_su_sc_mode_cntl; uint32_t pa_sc_window_scissor_tl; uint32_t pa_sc_window_scissor_br; uint32_t rb_surface_info; uint32_t pa_su_sc_vtx_cntl; // Bias is in Vulkan units because depth format may potentially effect it. float pa_su_poly_offset_scale; float pa_su_poly_offset_offset; uint32_t pa_cl_vte_cntl; float pa_cl_vport_xoffset; float pa_cl_vport_yoffset; float pa_cl_vport_zoffset; float pa_cl_vport_xscale; float pa_cl_vport_yscale; float pa_cl_vport_zscale; float rb_blend_rgba[4]; uint32_t rb_stencilrefmask; reg::SQ_PROGRAM_CNTL sq_program_cntl; uint32_t sq_context_misc; uint32_t rb_colorcontrol; reg::RB_COLOR_INFO rb_color_info; reg::RB_COLOR_INFO rb_color1_info; reg::RB_COLOR_INFO rb_color2_info; reg::RB_COLOR_INFO rb_color3_info; float rb_alpha_ref; uint32_t pa_su_point_size; SetDynamicStateRegisters() { Reset(); } void Reset() { std::memset(this, 0, sizeof(*this)); } } set_dynamic_state_registers_; }; } // namespace vulkan } // namespace gpu } // namespace xe #endif // XENIA_GPU_VULKAN_PIPELINE_CACHE_H_