/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2018 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_D3D12_PIPELINE_CACHE_H_ #define XENIA_GPU_D3D12_PIPELINE_CACHE_H_ #include #include #include #include #include #include #include #include "xenia/base/threading.h" #include "xenia/gpu/d3d12/d3d12_shader.h" #include "xenia/gpu/d3d12/render_target_cache.h" #include "xenia/gpu/dxbc_shader_translator.h" #include "xenia/gpu/register_file.h" #include "xenia/gpu/xenos.h" namespace xe { namespace gpu { namespace d3d12 { class D3D12CommandProcessor; class PipelineCache { public: PipelineCache(D3D12CommandProcessor* command_processor, RegisterFile* register_file, bool edram_rov_used, uint32_t resolution_scale); ~PipelineCache(); bool Initialize(); void Shutdown(); void ClearCache(); void EndFrame(); D3D12Shader* LoadShader(ShaderType shader_type, uint32_t guest_address, const uint32_t* host_address, uint32_t dword_count); // Translates shaders if needed, also making shader info up to date. bool EnsureShadersTranslated(D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated, PrimitiveType primitive_type); bool ConfigurePipeline( D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated, PrimitiveType primitive_type, IndexFormat index_format, bool early_z, const RenderTargetCache::PipelineRenderTarget render_targets[5], void** pipeline_handle_out, ID3D12RootSignature** root_signature_out); // Returns a pipeline with deferred creation by its handle. May return nullptr // if failed to create the pipeline. inline ID3D12PipelineState* GetPipelineStateByHandle(void* handle) const { return reinterpret_cast(handle)->state; } private: enum class PipelineStripCutIndex : uint32_t { kNone, kFFFF, kFFFFFFFF, }; enum class PipelineTessellationMode : uint32_t { kNone, kDiscrete, kContinuous, kAdaptive, }; enum class PipelinePatchType : uint32_t { kNone, kLine, kTriangle, kQuad, }; enum class PipelinePrimitiveTopologyType : uint32_t { kPoint, kLine, kTriangle, kPatch, }; enum class PipelineGeometryShader : uint32_t { kNone, kPointList, kRectangleList, kQuadList, }; enum class PipelineCullMode : uint32_t { kNone, kFront, kBack, }; enum class PipelineBlendFactor : uint32_t { kZero, kOne, kSrcColor, kInvSrcColor, kSrcAlpha, kInvSrcAlpha, kDestColor, kInvDestColor, kDestAlpha, kInvDestAlpha, kBlendFactor, kInvBlendFactor, kSrcAlphaSat, }; struct PipelineRenderTarget { uint32_t used : 1; // 1 ColorRenderTargetFormat format : 4; // 5 PipelineBlendFactor src_blend : 4; // 9 PipelineBlendFactor dest_blend : 4; // 13 BlendOp blend_op : 3; // 16 PipelineBlendFactor src_blend_alpha : 4; // 20 PipelineBlendFactor dest_blend_alpha : 4; // 24 BlendOp blend_op_alpha : 3; // 27 uint32_t write_mask : 4; // 31 }; struct PipelineDescription { ID3D12RootSignature* root_signature; D3D12Shader* vertex_shader; D3D12Shader* pixel_shader; int32_t depth_bias; float depth_bias_slope_scaled; PipelineStripCutIndex strip_cut_index : 2; // 2 PipelineTessellationMode tessellation_mode : 2; // 4 PipelinePrimitiveTopologyType primitive_topology_type : 2; // 6 PipelinePatchType patch_type : 2; // 8 PipelineGeometryShader geometry_shader : 2; // 10 uint32_t fill_mode_wireframe : 1; // 11 PipelineCullMode cull_mode : 2; // 13 uint32_t front_counter_clockwise : 1; // 14 uint32_t depth_clip : 1; // 15 uint32_t rov_msaa : 1; // 16 DepthRenderTargetFormat depth_format : 1; // 17 CompareFunction depth_func : 3; // 20 uint32_t depth_write : 1; // 21 uint32_t stencil_enable : 1; // 22 uint32_t stencil_read_mask : 8; // 30 uint32_t force_early_z : 1; // 31 uint32_t stencil_write_mask : 8; // 8 StencilOp stencil_front_fail_op : 3; // 11 StencilOp stencil_front_depth_fail_op : 3; // 14 StencilOp stencil_front_pass_op : 3; // 17 CompareFunction stencil_front_func : 3; // 20 StencilOp stencil_back_fail_op : 3; // 23 StencilOp stencil_back_depth_fail_op : 3; // 26 StencilOp stencil_back_pass_op : 3; // 29 CompareFunction stencil_back_func : 3; // 32 PipelineRenderTarget render_targets[4]; }; bool TranslateShader(D3D12Shader* shader, reg::SQ_PROGRAM_CNTL cntl, bool tessellated, PrimitiveType primitive_type); bool GetCurrentStateDescription( D3D12Shader* vertex_shader, D3D12Shader* pixel_shader, bool tessellated, PrimitiveType primitive_type, IndexFormat index_format, bool early_z, const RenderTargetCache::PipelineRenderTarget render_targets[5], PipelineDescription& description_out); ID3D12PipelineState* CreatePipelineState( const PipelineDescription& description); D3D12CommandProcessor* command_processor_; RegisterFile* register_file_; // Whether the output merger is emulated in pixel shaders. bool edram_rov_used_; uint32_t resolution_scale_; // Reusable shader translator. std::unique_ptr shader_translator_ = nullptr; // All loaded shaders mapped by their guest hash key. std::unordered_map shader_map_; // Empty depth-only pixel shader for writing to depth buffer via ROV when no // Xenos pixel shader provided. std::vector depth_only_pixel_shader_; struct Pipeline { // nullptr if creation has failed. ID3D12PipelineState* state; PipelineDescription description; }; // All previously generated pipelines identified by hash and the description. std::unordered_multimap pipelines_; // Previously used pipeline. This matches our current state settings // and allows us to quickly(ish) reuse the pipeline if no registers have // changed. Pipeline* current_pipeline_ = nullptr; // Pipeline creation threads. void CreationThread(); std::mutex creation_request_lock_; std::condition_variable creation_request_cond_; // Protected with creation_request_lock_, notify_one creation_request_cond_ // when set. std::deque creation_queue_; // Number of threads that are currently creating a pipeline - incremented when // a pipeline is dequeued (the completion event can't be triggered before this // is zero). Protected with creation_request_lock_. uint32_t creation_threads_busy_ = 0; // Manual-reset event set when the last queued pipeline is created and there // are no more pipelines to create. This is triggered by the thread creating // the last pipeline. std::unique_ptr creation_completion_event_ = nullptr; // Whether setting the event on completion is queued. Protected with // creation_request_lock_, notify_one creation_request_cond_ when set. bool creation_completion_set_event_ = false; // Whether to shut down the creation threads as soon as possible. Protected // with creation_request_lock_, notify_all creation_request_cond_ when set. bool creation_threads_shutdown_ = false; std::vector> creation_threads_; }; } // namespace d3d12 } // namespace gpu } // namespace xe #endif // XENIA_GPU_D3D12_PIPELINE_CACHE_H_