text: /**
   ******************************************************************************
   * 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 <condition_variable>
  #include <cstdio>
  #include <deque>
  #include <memory>
  #include <mutex>
  #include <string>
  #include <thread>
  #include <unordered_map>
  #include <utility>
  #include <vector>
  
  #include "xenia/base/hash.h"
  #include "xenia/base/platform.h"
  #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:
    static constexpr size_t kLayoutUIDEmpty = 0;
  
    PipelineCache(D3D12CommandProcessor& command_processor,
                  const RegisterFile& register_file, bool bindless_resources_used,
                  bool edram_rov_used, uint32_t resolution_scale);
    ~PipelineCache();
  
    bool Initialize();
    void Shutdown();
    void ClearCache(bool shutting_down = false);
  
    void InitializeShaderStorage(const std::filesystem::path& storage_root,
                                 uint32_t title_id, bool blocking);
    void ShutdownShaderStorage();
  
    void EndSubmission();
    bool IsCreatingPipelineStates();
  
    D3D12Shader* LoadShader(xenos::ShaderType shader_type, uint32_t guest_address,
                            const uint32_t* host_address, uint32_t dword_count);
  
    // Returns the host vertex shader type for the current draw if it's valid and
    // supported, or Shader::HostVertexShaderType(-1) if not.
    Shader::HostVertexShaderType GetHostVertexShaderTypeIfValid() const;
  
    // Translates shaders if needed, also making shader info up to date.
    bool EnsureShadersTranslated(
        D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
        Shader::HostVertexShaderType host_vertex_shader_type);
  
    bool ConfigurePipeline(
        D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
        xenos::PrimitiveType primitive_type, xenos::IndexFormat index_format,
        bool early_z,
        const RenderTargetCache::PipelineRenderTarget render_targets[5],
        void** pipeline_state_handle_out,
        ID3D12RootSignature** root_signature_out);
  
    // Returns a pipeline state object with deferred creation by its handle. May
    // return nullptr if failed to create the pipeline state object.
    inline ID3D12PipelineState* GetD3D12PipelineStateByHandle(
        void* handle) const {
      return reinterpret_cast<const PipelineState*>(handle)->state;
    }
  
   private:
    XEPACKEDSTRUCT(ShaderStoredHeader, {
      uint64_t ucode_data_hash;
  
      uint32_t ucode_dword_count : 16;
      xenos::ShaderType type : 1;
      Shader::HostVertexShaderType host_vertex_shader_type : 3;
  
      reg::SQ_PROGRAM_CNTL sq_program_cntl;
  
      static constexpr uint32_t kVersion = 0x20200405;
    });
  
    // Update PipelineDescription::kVersion if any of the Pipeline* enums are
    // changed!
  
    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,
    };
  
    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,
    };
  
    // Update PipelineDescription::kVersion if anything is changed!
    XEPACKEDSTRUCT(PipelineRenderTarget, {
      uint32_t used : 1;                          // 1
      xenos::ColorRenderTargetFormat format : 4;  // 5
      PipelineBlendFactor src_blend : 4;          // 9
      PipelineBlendFactor dest_blend : 4;         // 13
      xenos::BlendOp blend_op : 3;                // 16
      PipelineBlendFactor src_blend_alpha : 4;    // 20
      PipelineBlendFactor dest_blend_alpha : 4;   // 24
      xenos::BlendOp blend_op_alpha : 3;          // 27
      uint32_t write_mask : 4;                    // 31
    });
  
    XEPACKEDSTRUCT(PipelineDescription, {
      uint64_t vertex_shader_hash;
      // 0 if drawing without a pixel shader.
      uint64_t pixel_shader_hash;
  
      int32_t depth_bias;
      float depth_bias_slope_scaled;
  
      PipelineStripCutIndex strip_cut_index : 2;                 // 2
      Shader::HostVertexShaderType host_vertex_shader_type : 3;  // 5
      // PipelinePrimitiveTopologyType for a vertex shader.
      // xenos::TessellationMode for a domain shader.
      uint32_t primitive_topology_type_or_tessellation_mode : 2;  // 7
      // Zero for non-kVertex host_vertex_shader_type.
      PipelineGeometryShader geometry_shader : 2;       // 9
      uint32_t fill_mode_wireframe : 1;                 // 10
      PipelineCullMode cull_mode : 2;                   // 12
      uint32_t front_counter_clockwise : 1;             // 13
      uint32_t depth_clip : 1;                          // 14
      uint32_t rov_msaa : 1;                            // 15
      xenos::DepthRenderTargetFormat depth_format : 1;  // 16
      xenos::CompareFunction depth_func : 3;            // 19
      uint32_t depth_write : 1;                         // 20
      uint32_t stencil_enable : 1;                      // 21
      uint32_t stencil_read_mask : 8;                   // 29
      uint32_t force_early_z : 1;                       // 30
  
      uint32_t stencil_write_mask : 8;                   // 8
      xenos::StencilOp stencil_front_fail_op : 3;        // 11
      xenos::StencilOp stencil_front_depth_fail_op : 3;  // 14
      xenos::StencilOp stencil_front_pass_op : 3;        // 17
      xenos::CompareFunction stencil_front_func : 3;     // 20
      xenos::StencilOp stencil_back_fail_op : 3;         // 23
      xenos::StencilOp stencil_back_depth_fail_op : 3;   // 26
      xenos::StencilOp stencil_back_pass_op : 3;         // 29
      xenos::CompareFunction stencil_back_func : 3;      // 32
  
      PipelineRenderTarget render_targets[4];
  
      static constexpr uint32_t kVersion = 0x20200405;
    });
  
    XEPACKEDSTRUCT(PipelineStoredDescription, {
      uint64_t description_hash;
      PipelineDescription description;
    });
  
    struct PipelineRuntimeDescription {
      ID3D12RootSignature* root_signature;
      D3D12Shader* vertex_shader;
      D3D12Shader* pixel_shader;
      PipelineDescription description;
    };
  
    // Can be called from multiple threads.
    bool TranslateShader(DxbcShaderTranslator& translator, D3D12Shader& shader,
                         reg::SQ_PROGRAM_CNTL cntl,
                         IDxbcConverter* dxbc_converter = nullptr,
                         IDxcUtils* dxc_utils = nullptr,
                         IDxcCompiler* dxc_compiler = nullptr,
                         Shader::HostVertexShaderType host_vertex_shader_type =
                             Shader::HostVertexShaderType::kVertex);
  
    bool GetCurrentStateDescription(
        D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
        xenos::PrimitiveType primitive_type, xenos::IndexFormat index_format,
        bool early_z,
        const RenderTargetCache::PipelineRenderTarget render_targets[5],
        PipelineRuntimeDescription& runtime_description_out);
  
    ID3D12PipelineState* CreateD3D12PipelineState(
        const PipelineRuntimeDescription& runtime_description);
  
    D3D12CommandProcessor& command_processor_;
    const RegisterFile& register_file_;
    bool bindless_resources_used_;
    bool edram_rov_used_;
    uint32_t resolution_scale_;
  
    // Reusable shader translator.
    std::unique_ptr<DxbcShaderTranslator> shader_translator_;
  
    // Command processor thread DXIL conversion/disassembly interfaces, if DXIL
    // disassembly is enabled.
    IDxbcConverter* dxbc_converter_ = nullptr;
    IDxcUtils* dxc_utils_ = nullptr;
    IDxcCompiler* dxc_compiler_ = nullptr;
  
    // All loaded shaders mapped by their guest hash key.
    std::unordered_map<uint64_t, D3D12Shader*, xe::hash::IdentityHasher<uint64_t>>
        shader_map_;
  
    struct LayoutUID {
      size_t uid;
      size_t vector_span_offset;
      size_t vector_span_length;
    };
    std::mutex layouts_mutex_;
    // Texture binding layouts of different shaders, for obtaining layout UIDs.
    std::vector<D3D12Shader::TextureBinding> texture_binding_layouts_;
    // Map of texture binding layouts used by shaders, for obtaining UIDs. Keys
    // are XXH64 hashes of layouts, values need manual collision resolution using
    // layout_vector_offset:layout_length of texture_binding_layouts_.
    std::unordered_multimap<uint64_t, LayoutUID,
                            xe::hash::IdentityHasher<uint64_t>>
        texture_binding_layout_map_;
    // Bindless sampler indices of different shaders, for obtaining layout UIDs.
    // For bindful, sampler count is used as the UID instead.
    std::vector<uint32_t> bindless_sampler_layouts_;
    // Keys are XXH64 hashes of used bindless sampler indices.
    std::unordered_multimap<uint64_t, LayoutUID,
                            xe::hash::IdentityHasher<uint64_t>>
        bindless_sampler_layout_map_;
  
    // Empty depth-only pixel shader for writing to depth buffer via ROV when no
    // Xenos pixel shader provided.
    std::vector<uint8_t> depth_only_pixel_shader_;
  
    struct PipelineState {
      // nullptr if creation has failed.
      ID3D12PipelineState* state;
      PipelineRuntimeDescription description;
    };
    // All previously generated pipeline state objects identified by hash and the
    // description.
    std::unordered_multimap<uint64_t, PipelineState*,
                            xe::hash::IdentityHasher<uint64_t>>
        pipeline_states_;
  
    // Previously used pipeline state object. This matches our current state
    // settings and allows us to quickly(ish) reuse the pipeline state if no
    // registers have changed.
    PipelineState* current_pipeline_state_ = nullptr;
  
    // Currently open shader storage path.
    std::filesystem::path shader_storage_root_;
    uint32_t shader_storage_title_id_ = 0;
  
    // Shader storage output stream, for preload in the next emulator runs.
    FILE* shader_storage_file_ = nullptr;
    bool shader_storage_file_flush_needed_ = false;
  
    // Pipeline state storage output stream, for preload in the next emulator
    // runs.
    FILE* pipeline_state_storage_file_ = nullptr;
    bool pipeline_state_storage_file_flush_needed_ = false;
  
    // Thread for asynchronous writing to the storage streams.
    void StorageWriteThread();
    std::mutex storage_write_request_lock_;
    std::condition_variable storage_write_request_cond_;
    // Storage thread input is protected with storage_write_request_lock_, and the
    // thread is notified about its change via storage_write_request_cond_.
    std::deque<std::pair<const Shader*, reg::SQ_PROGRAM_CNTL>>
        storage_write_shader_queue_;
    std::deque<PipelineStoredDescription> storage_write_pipeline_state_queue_;
    bool storage_write_flush_shaders_ = false;
    bool storage_write_flush_pipeline_states_ = false;
    bool storage_write_thread_shutdown_ = false;
    std::unique_ptr<xe::threading::Thread> storage_write_thread_;
  
    // Pipeline state object creation threads.
    void CreationThread(size_t thread_index);
    void CreateQueuedPipelineStatesOnProcessorThread();
    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<PipelineState*> creation_queue_;
    // Number of threads that are currently creating a pipeline state object -
    // incremented when a pipeline state object is dequeued (the completion event
    // can't be triggered before this is zero). Protected with
    // creation_request_lock_.
    size_t creation_threads_busy_ = 0;
    // Manual-reset event set when the last queued pipeline state object is
    // created and there are no more pipeline state objects to create. This is
    // triggered by the thread creating the last pipeline state object.
    std::unique_ptr<xe::threading::Event> creation_completion_event_;
    // 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;
    // Creation threads with this index or above need to be shut down as soon as
    // possible. Protected with creation_request_lock_, notify_all
    // creation_request_cond_ when set.
    size_t creation_threads_shutdown_from_ = SIZE_MAX;
    std::vector<std::unique_ptr<xe::threading::Thread>> creation_threads_;
  };
  
  }  // namespace d3d12
  }  // namespace gpu
  }  // namespace xe
  
  #endif  // XENIA_GPU_D3D12_PIPELINE_CACHE_H_
  