Files
Xenia-Canary/src/xenia/gpu/d3d12/pipeline_cache.h

244 lines
8.4 KiB
C++

/**
******************************************************************************
* 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 <deque>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#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<const Pipeline*>(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<DxbcShaderTranslator> shader_translator_ = nullptr;
// All loaded shaders mapped by their guest hash key.
std::unordered_map<uint64_t, D3D12Shader*> shader_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 Pipeline {
// nullptr if creation has failed.
ID3D12PipelineState* state;
PipelineDescription description;
};
// All previously generated pipelines identified by hash and the description.
std::unordered_multimap<uint64_t, Pipeline*> 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<Pipeline*> 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<xe::threading::Event> 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<std::unique_ptr<xe::threading::Thread>> creation_threads_;
};
} // namespace d3d12
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_D3D12_PIPELINE_CACHE_H_