d3d12: Use template class for caching

This commit is contained in:
vlj 2015-05-15 21:48:43 +02:00 committed by Vincent Lejeune
parent a58974eac8
commit ba66992ee3
6 changed files with 172 additions and 527 deletions

View File

@ -471,7 +471,7 @@ bool D3D12GSRender::LoadProgram()
return false; return false;
} }
PipelineProperties prop = {}; D3D12PipelineProperties prop = {};
/* /*
#define GL_POINTS 0x0000 #define GL_POINTS 0x0000
#define GL_LINES 0x0001 #define GL_LINES 0x0001
@ -504,8 +504,8 @@ bool D3D12GSRender::LoadProgram()
prop.Topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; prop.Topology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
break; break;
} }
prop.IASet = m_IASet;
m_PSO = m_cachePSO.getGraphicPipelineState(m_device, m_rootSignature, m_cur_vertex_prog, m_cur_fragment_prog, prop, m_IASet); m_PSO = m_cachePSO.getGraphicPipelineState(m_device, m_rootSignature, m_cur_vertex_prog, m_cur_fragment_prog, prop, std::make_pair(m_device, m_rootSignature));
return m_PSO != nullptr; return m_PSO != nullptr;
} }

View File

@ -2,279 +2,10 @@
#if defined (DX12_SUPPORT) #if defined (DX12_SUPPORT)
#include "D3D12PipelineState.h" #include "D3D12PipelineState.h"
#include "Emu/Memory/vm.h"
#include "Utilities/Log.h"
#include <wrl/client.h>
#include <d3dcompiler.h> #include <d3dcompiler.h>
#include <unordered_map>
#include "VertexProgramDecompiler.h"
#include "FragmentProgramDecompiler.h"
#include "Utilities/File.h"
#include <stdlib.h>
#pragma comment (lib, "d3dcompiler.lib") #pragma comment (lib, "d3dcompiler.lib")
namespace ProgramHashUtil
{
size_t getFPBinarySize(void *ptr)
{
const qword *instBuffer = (const qword*)ptr;
size_t instIndex = 0;
while (true)
{
const qword& inst = instBuffer[instIndex];
bool isSRC0Constant = ((inst.word[1] >> 8) & 0x3) == 2;
bool isSRC1Constant = ((inst.word[2] >> 8) & 0x3) == 2;
bool isSRC2Constant = ((inst.word[3] >> 8) & 0x3) == 2;
bool end = (inst.word[0] >> 8) & 0x1;
if (isSRC0Constant || isSRC1Constant || isSRC2Constant)
{
instIndex += 2;
if (end)
return instIndex * 4 * 4;
continue;
}
instIndex++;
if (end)
return (instIndex) * 4 * 4;
}
}
};
PipelineStateObjectCache::PipelineStateObjectCache() : m_currentShaderId(0)
{}
PipelineStateObjectCache::~PipelineStateObjectCache()
{
for (auto pair : m_cachePSO)
pair.second->Release();
}
bool PipelineStateObjectCache::SearchFp(const RSXFragmentProgram& rsx_fp, Shader& shader)
{
binary2FS::const_iterator It = m_cacheFS.find(vm::get_ptr<void>(rsx_fp.addr));
if (It != m_cacheFS.end())
{
shader = It->second;
return true;
}
return false;
}
bool PipelineStateObjectCache::SearchVp(const RSXVertexProgram& rsx_vp, Shader& shader)
{
binary2VS::const_iterator It = m_cacheVS.find((void*)rsx_vp.data.data());
if (It != m_cacheVS.end())
{
shader = It->second;
return true;
}
return false;
}
ID3D12PipelineState *PipelineStateObjectCache::GetProg(const PSOKey &key) const
{
std::unordered_map<PSOKey, ID3D12PipelineState *, PSOKeyHash, PSOKeyCompare>::const_iterator It = m_cachePSO.find(key);
if (It == m_cachePSO.end())
return nullptr;
return It->second;
}
void PipelineStateObjectCache::AddVertexProgram(Shader& vp, RSXVertexProgram& rsx_vp)
{
size_t actualVPSize = rsx_vp.data.size() * 4;
void *fpShadowCopy = malloc(actualVPSize);
memcpy(fpShadowCopy, rsx_vp.data.data(), actualVPSize);
vp.Id = (u32)m_currentShaderId++;
m_cacheVS.insert(std::make_pair(fpShadowCopy, vp));
}
void PipelineStateObjectCache::AddFragmentProgram(Shader& fp, RSXFragmentProgram& rsx_fp)
{
size_t actualFPSize = ProgramHashUtil::getFPBinarySize(vm::get_ptr<u8>(rsx_fp.addr));
void *fpShadowCopy = malloc(actualFPSize);
memcpy(fpShadowCopy, vm::get_ptr<u8>(rsx_fp.addr), actualFPSize);
fp.Id = (u32)m_currentShaderId++;
m_cacheFS.insert(std::make_pair(fpShadowCopy, fp));
}
void PipelineStateObjectCache::Add(ID3D12PipelineState *prog, const PSOKey& PSOKey)
{
m_cachePSO.insert(std::make_pair(PSOKey, prog));
}
ID3D12PipelineState *PipelineStateObjectCache::getGraphicPipelineState(
ID3D12Device *device,
ID3D12RootSignature *rootSignature,
RSXVertexProgram *vertexShader,
RSXFragmentProgram *fragmentShader,
const PipelineProperties &pipelineProperties,
const std::vector<D3D12_INPUT_ELEMENT_DESC> &IASet)
{
ID3D12PipelineState *result = nullptr;
Shader m_vertex_prog, m_fragment_prog;
bool m_fp_buf_num = SearchFp(*fragmentShader, m_fragment_prog);
bool m_vp_buf_num = SearchVp(*vertexShader, m_vertex_prog);
if (!m_fp_buf_num)
{
LOG_WARNING(RSX, "FP not found in buffer!");
FragmentDecompiler FS(fragmentShader->addr, fragmentShader->size, fragmentShader->offset);
const std::string &shader = FS.Decompile();
m_fragment_prog.Compile(shader, SHADER_TYPE::SHADER_TYPE_FRAGMENT);
AddFragmentProgram(m_fragment_prog, *fragmentShader);
// TODO: This shouldn't use current dir
fs::file("./FragmentProgram.hlsl", o_write | o_create | o_trunc).write(shader.c_str(), shader.size());
}
if (!m_vp_buf_num)
{
LOG_WARNING(RSX, "VP not found in buffer!");
VertexDecompiler VS(vertexShader->data);
std::string shaderCode = VS.Decompile();
m_vertex_prog.Compile(shaderCode, SHADER_TYPE::SHADER_TYPE_VERTEX);
AddVertexProgram(m_vertex_prog, *vertexShader);
// TODO: This shouldn't use current dir
fs::file("./VertexProgram.hlsl", o_write | o_create | o_trunc).write(shaderCode.c_str(), shaderCode.size());
}
if (m_fp_buf_num && m_vp_buf_num)
{
result = GetProg({ m_vertex_prog.Id, m_fragment_prog.Id, pipelineProperties });
}
if (result != nullptr)
{
return result;
/* // RSX Debugger: Check if this program was modified and update it
if (Ini.GSLogPrograms.GetValue())
{
for (auto& program : m_debug_programs)
{
if (program.id == m_program.id && program.modified)
{
// TODO: This isn't working perfectly. Is there any better/shorter way to update the program
m_vertex_prog.shader = program.vp_shader;
m_fragment_prog.shader = program.fp_shader;
m_vertex_prog.Wait();
m_vertex_prog.Compile();
checkForGlError("m_vertex_prog.Compile");
m_fragment_prog.Wait();
m_fragment_prog.Compile();
checkForGlError("m_fragment_prog.Compile");
glAttachShader(m_program.id, m_vertex_prog.id);
glAttachShader(m_program.id, m_fragment_prog.id);
glLinkProgram(m_program.id);
checkForGlError("glLinkProgram");
glDetachShader(m_program.id, m_vertex_prog.id);
glDetachShader(m_program.id, m_fragment_prog.id);
program.vp_id = m_vertex_prog.id;
program.fp_id = m_fragment_prog.id;
program.modified = false;
}
}
}
m_program.Use();*/
}
else
{
// LOG_WARNING(RSX, "Add program :");
// LOG_WARNING(RSX, "*** vp id = %d", m_vertex_prog.Id);
// LOG_WARNING(RSX, "*** fp id = %d", m_fragment_prog.Id);
D3D12_GRAPHICS_PIPELINE_STATE_DESC graphicPipelineStateDesc = {};
if (m_vertex_prog.bytecode != nullptr)
{
graphicPipelineStateDesc.VS.BytecodeLength = m_vertex_prog.bytecode->GetBufferSize();
graphicPipelineStateDesc.VS.pShaderBytecode = m_vertex_prog.bytecode->GetBufferPointer();
}
if (m_fragment_prog.bytecode != nullptr)
{
graphicPipelineStateDesc.PS.BytecodeLength = m_fragment_prog.bytecode->GetBufferSize();
graphicPipelineStateDesc.PS.pShaderBytecode = m_fragment_prog.bytecode->GetBufferPointer();
}
graphicPipelineStateDesc.pRootSignature = rootSignature;
// Sensible default value
static D3D12_RASTERIZER_DESC CD3D12_RASTERIZER_DESC =
{
D3D12_FILL_MODE_SOLID,
D3D12_CULL_MODE_NONE,
FALSE,
D3D12_DEFAULT_DEPTH_BIAS,
D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
TRUE,
FALSE,
FALSE,
0,
D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF,
};
static D3D12_DEPTH_STENCIL_DESC CD3D12_DEPTH_STENCIL_DESC =
{
TRUE,
D3D12_DEPTH_WRITE_MASK_ALL,
D3D12_COMPARISON_FUNC_LESS_EQUAL,
FALSE,
D3D12_DEFAULT_STENCIL_READ_MASK,
D3D12_DEFAULT_STENCIL_WRITE_MASK,
};
static D3D12_BLEND_DESC CD3D12_BLEND_DESC =
{
FALSE,
FALSE,
{
FALSE,FALSE,
D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
}
};
graphicPipelineStateDesc.BlendState = CD3D12_BLEND_DESC;
graphicPipelineStateDesc.DepthStencilState = CD3D12_DEPTH_STENCIL_DESC;
graphicPipelineStateDesc.RasterizerState = CD3D12_RASTERIZER_DESC;
graphicPipelineStateDesc.PrimitiveTopologyType = pipelineProperties.Topology;
graphicPipelineStateDesc.NumRenderTargets = 1;
graphicPipelineStateDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
graphicPipelineStateDesc.DSVFormat = DXGI_FORMAT_D16_UNORM;
graphicPipelineStateDesc.InputLayout.pInputElementDescs = IASet.data();
graphicPipelineStateDesc.InputLayout.NumElements = (UINT)IASet.size();
graphicPipelineStateDesc.SampleDesc.Count = 1;
graphicPipelineStateDesc.SampleMask = UINT_MAX;
graphicPipelineStateDesc.NodeMask = 1;
device->CreateGraphicsPipelineState(&graphicPipelineStateDesc, IID_PPV_ARGS(&result));
Add(result, {m_vertex_prog.Id, m_fragment_prog.Id, pipelineProperties });
// RSX Debugger
/*if (Ini.GSLogPrograms.GetValue())
{
RSXDebuggerProgram program;
program.id = m_program.id;
program.vp_id = m_vertex_prog.id;
program.fp_id = m_fragment_prog.id;
program.vp_shader = m_vertex_prog.shader;
program.fp_shader = m_fragment_prog.shader;
m_debug_programs.push_back(program);
}*/
}
return result;
}
#define TO_STRING(x) #x #define TO_STRING(x) #x
void Shader::Compile(const std::string &code, SHADER_TYPE st) void Shader::Compile(const std::string &code, SHADER_TYPE st)

View File

@ -2,28 +2,37 @@
#if defined (DX12_SUPPORT) #if defined (DX12_SUPPORT)
#include <d3d12.h> #include <d3d12.h>
#include "Emu/RSX/RSXFragmentProgram.h"
#include "Emu/RSX/RSXVertexProgram.h"
#include <wrl/client.h> #include <wrl/client.h>
#include "../Common/ProgramStateCache.h"
#include "VertexProgramDecompiler.h"
#include "FragmentProgramDecompiler.h"
#include "Utilities/File.h"
enum class SHADER_TYPE
{
SHADER_TYPE_VERTEX,
SHADER_TYPE_FRAGMENT
};
struct PipelineProperties struct D3D12PipelineProperties
{ {
D3D12_PRIMITIVE_TOPOLOGY_TYPE Topology; D3D12_PRIMITIVE_TOPOLOGY_TYPE Topology;
std::vector<D3D12_INPUT_ELEMENT_DESC> IASet;
bool operator==(const D3D12PipelineProperties &in) const
{
return Topology == in.Topology;
}
}; };
/** Storage for a shader /** Storage for a shader
* Embeds the D3DBlob corresponding to * Embeds the D3DBlob
*/ */
class Shader struct Shader
{ {
public: public:
enum class SHADER_TYPE
{
SHADER_TYPE_VERTEX,
SHADER_TYPE_FRAGMENT
};
Shader() : bytecode(nullptr) {} Shader() : bytecode(nullptr) {}
~Shader() {} ~Shader() {}
@ -37,222 +46,127 @@ public:
// void Decompile(RSXFragmentProgram& prog) // void Decompile(RSXFragmentProgram& prog)
/** Compile the decompiled fragment shader into a format we can use with OpenGL. */ /** Compile the decompiled fragment shader into a format we can use with OpenGL. */
void Compile(const std::string &code, SHADER_TYPE st); void Compile(const std::string &code, enum class SHADER_TYPE st);
}; };
struct D3D12Traits
namespace ProgramHashUtil
{ {
// Based on typedef Shader VertexProgramData;
// https://github.com/AlexAltea/nucleus/blob/master/nucleus/gpu/rsx_pgraph.cpp typedef Shader FragmentProgramData;
union qword typedef ID3D12PipelineState PipelineData;
typedef D3D12PipelineProperties PipelineProperties;
typedef std::pair<ID3D12Device *, ID3D12RootSignature *> ExtraData;
static
void RecompileFragmentProgram(RSXFragmentProgram *RSXFP, FragmentProgramData& fragmentProgramData, size_t ID)
{ {
u64 dword[2]; FragmentDecompiler FS(RSXFP->addr, RSXFP->size, RSXFP->offset);
u32 word[4]; const std::string &shader = FS.Decompile();
fragmentProgramData.Compile(shader, Shader::SHADER_TYPE::SHADER_TYPE_FRAGMENT);
// TODO: This shouldn't use current dir
fs::file("./FragmentProgram.hlsl", o_write | o_create | o_trunc).write(shader.c_str(), shader.size());
fragmentProgramData.Id = (u32)ID;
}
static
void RecompileVertexProgram(RSXVertexProgram *RSXVP, VertexProgramData& vertexProgramData, size_t ID)
{
VertexDecompiler VS(RSXVP->data);
std::string shaderCode = VS.Decompile();
vertexProgramData.Compile(shaderCode, Shader::SHADER_TYPE::SHADER_TYPE_VERTEX);
// TODO: This shouldn't use current dir
fs::file("./VertexProgram.hlsl", o_write | o_create | o_trunc).write(shaderCode.c_str(), shaderCode.size());
vertexProgramData.Id = (u32)ID;
}
static
PipelineData *BuildProgram(VertexProgramData &vertexProgramData, FragmentProgramData &fragmentProgramData, const PipelineProperties &pipelineProperties, const ExtraData& extraData)
{
ID3D12PipelineState *result;
D3D12_GRAPHICS_PIPELINE_STATE_DESC graphicPipelineStateDesc = {};
if (vertexProgramData.bytecode != nullptr)
{
graphicPipelineStateDesc.VS.BytecodeLength = vertexProgramData.bytecode->GetBufferSize();
graphicPipelineStateDesc.VS.pShaderBytecode = vertexProgramData.bytecode->GetBufferPointer();
}
if (fragmentProgramData.bytecode != nullptr)
{
graphicPipelineStateDesc.PS.BytecodeLength = fragmentProgramData.bytecode->GetBufferSize();
graphicPipelineStateDesc.PS.pShaderBytecode = fragmentProgramData.bytecode->GetBufferPointer();
}
graphicPipelineStateDesc.pRootSignature = extraData.second;
// Sensible default value
static D3D12_RASTERIZER_DESC CD3D12_RASTERIZER_DESC =
{
D3D12_FILL_MODE_SOLID,
D3D12_CULL_MODE_NONE,
FALSE,
D3D12_DEFAULT_DEPTH_BIAS,
D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
TRUE,
FALSE,
FALSE,
0,
D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF,
}; };
struct HashVertexProgram static D3D12_DEPTH_STENCIL_DESC CD3D12_DEPTH_STENCIL_DESC =
{ {
size_t operator()(const void *program) const TRUE,
D3D12_DEPTH_WRITE_MASK_ALL,
D3D12_COMPARISON_FUNC_LESS_EQUAL,
FALSE,
D3D12_DEFAULT_STENCIL_READ_MASK,
D3D12_DEFAULT_STENCIL_WRITE_MASK,
};
static D3D12_BLEND_DESC CD3D12_BLEND_DESC =
{ {
// 64-bit Fowler/Noll/Vo FNV-1a hash code FALSE,
size_t hash = 0xCBF29CE484222325ULL; FALSE,
const qword *instbuffer = (const qword*)program;
size_t instIndex = 0;
bool end = false;
return 0;
while (true)
{ {
const qword inst = instbuffer[instIndex]; FALSE,FALSE,
bool end = inst.word[0] >> 31; D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
if (end) D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
return hash; D3D12_LOGIC_OP_NOOP,
hash ^= inst.dword[0]; D3D12_COLOR_WRITE_ENABLE_ALL,
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
hash ^= inst.dword[1];
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
instIndex++;
}
return 0;
} }
}; };
graphicPipelineStateDesc.BlendState = CD3D12_BLEND_DESC;
graphicPipelineStateDesc.DepthStencilState = CD3D12_DEPTH_STENCIL_DESC;
graphicPipelineStateDesc.RasterizerState = CD3D12_RASTERIZER_DESC;
graphicPipelineStateDesc.PrimitiveTopologyType = pipelineProperties.Topology;
struct VertexProgramCompare graphicPipelineStateDesc.NumRenderTargets = 1;
{ graphicPipelineStateDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
bool operator()(const void *binary1, const void *binary2) const graphicPipelineStateDesc.DSVFormat = DXGI_FORMAT_D16_UNORM;
{
const qword *instBuffer1 = (const qword*)binary1;
const qword *instBuffer2 = (const qword*)binary2;
size_t instIndex = 0;
return true;
while (true)
{
const qword& inst1 = instBuffer1[instIndex];
const qword& inst2 = instBuffer2[instIndex];
bool end = (inst1.word[0] >> 31) && (inst2.word[0] >> 31);
if (end)
return true;
if (inst1.dword[0] != inst2.dword[0] || inst1.dword[1] != inst2.dword[1])
return false;
instIndex++;
}
}
};
struct FragmentProgramUtil graphicPipelineStateDesc.InputLayout.pInputElementDescs = pipelineProperties.IASet.data();
{ graphicPipelineStateDesc.InputLayout.NumElements = (UINT)pipelineProperties.IASet.size();
/** graphicPipelineStateDesc.SampleDesc.Count = 1;
* returns true if the given source Operand is a constant graphicPipelineStateDesc.SampleMask = UINT_MAX;
*/ graphicPipelineStateDesc.NodeMask = 1;
static bool isConstant(u32 sourceOperand)
{
return ((sourceOperand >> 8) & 0x3) == 2;
}
/** extraData.first->CreateGraphicsPipelineState(&graphicPipelineStateDesc, IID_PPV_ARGS(&result));
* RSX fragment program constants are inlined inside shader code.
* This function takes an instruction from a fragment program and
* returns an equivalent instruction where inlined constants
* are masked.
* This allows to hash/compare fragment programs even if their
* inlined constants are modified inbetween
*/
static qword fragmentMaskConstant(const qword &initialQword)
{
qword result = initialQword;
if (isConstant(initialQword.word[1]))
result.word[1] = 0;
if (isConstant(initialQword.word[2]))
result.word[2] = 0;
if (isConstant(initialQword.word[3]))
result.word[3] = 0;
return result; return result;
} }
};
struct HashFragmentProgram static
void DeleteProgram(PipelineData *ptr)
{ {
size_t operator()(const void *program) const ptr->Release();
{
// 64-bit Fowler/Noll/Vo FNV-1a hash code
size_t hash = 0xCBF29CE484222325ULL;
const qword *instbuffer = (const qword*)program;
size_t instIndex = 0;
while (true)
{
const qword& inst = instbuffer[instIndex];
bool end = (inst.word[0] >> 8) & 0x1;
if (end)
return hash;
const qword& maskedInst = FragmentProgramUtil::fragmentMaskConstant(inst);
hash ^= maskedInst.dword[0];
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
hash ^= maskedInst.dword[1];
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
instIndex++;
// Skip constants
if (FragmentProgramUtil::isConstant(inst.word[1]) ||
FragmentProgramUtil::isConstant(inst.word[2]) ||
FragmentProgramUtil::isConstant(inst.word[3]))
instIndex++;
}
return 0;
}
};
struct FragmentProgramCompare
{
bool operator()(const void *binary1, const void *binary2) const
{
const qword *instBuffer1 = (const qword*)binary1;
const qword *instBuffer2 = (const qword*)binary2;
size_t instIndex = 0;
while (true)
{
const qword& inst1 = instBuffer1[instIndex];
const qword& inst2 = instBuffer2[instIndex];
bool end = ((inst1.word[0] >> 8) & 0x1) && ((inst2.word[0] >> 8) & 0x1);
if (end)
return true;
const qword& maskedInst1 = FragmentProgramUtil::fragmentMaskConstant(inst1);
const qword& maskedInst2 = FragmentProgramUtil::fragmentMaskConstant(inst2);
if (maskedInst1.dword[0] != maskedInst2.dword[0] || maskedInst1.dword[1] != maskedInst2.dword[1])
return false;
instIndex++;
// Skip constants
if (FragmentProgramUtil::isConstant(inst1.word[1]) ||
FragmentProgramUtil::isConstant(inst1.word[2]) ||
FragmentProgramUtil::isConstant(inst1.word[3]))
instIndex++;
}
}
};
}
typedef std::unordered_map<void *, Shader, ProgramHashUtil::HashVertexProgram, ProgramHashUtil::VertexProgramCompare> binary2VS;
typedef std::unordered_map<void *, Shader, ProgramHashUtil::HashFragmentProgram, ProgramHashUtil::FragmentProgramCompare> binary2FS;
struct PSOKey
{
u32 vpIdx;
u32 fpIdx;
PipelineProperties properties;
};
struct PSOKeyHash
{
size_t operator()(const PSOKey &key) const
{
size_t hashValue = 0;
hashValue ^= std::hash<unsigned>()(key.vpIdx);
return hashValue;
} }
}; };
struct PSOKeyCompare class PipelineStateObjectCache : public ProgramStateCache<D3D12Traits>
{ {
size_t operator()(const PSOKey &key1, const PSOKey &key2) const
{
return (key1.vpIdx == key2.vpIdx) && (key1.fpIdx == key2.fpIdx) && (key1.properties.Topology == key2.properties.Topology);
}
};
/**
* Cache for shader blobs and Pipeline state object
* The class is responsible for creating the object so the state only has to call getGraphicPipelineState
*/
class PipelineStateObjectCache
{
private:
size_t m_currentShaderId;
binary2VS m_cacheVS;
binary2FS m_cacheFS;
std::unordered_map<PSOKey, ID3D12PipelineState *, PSOKeyHash, PSOKeyCompare> m_cachePSO;
bool SearchFp(const RSXFragmentProgram& rsx_fp, Shader& shader);
bool SearchVp(const RSXVertexProgram& rsx_vp, Shader& shader);
ID3D12PipelineState *GetProg(const PSOKey &psoKey) const;
void AddVertexProgram(Shader& vp, RSXVertexProgram& rsx_vp);
void AddFragmentProgram(Shader& fp, RSXFragmentProgram& rsx_fp);
void Add(ID3D12PipelineState *prog, const PSOKey& PSOKey);
public:
PipelineStateObjectCache();
~PipelineStateObjectCache();
// Note: the last param is not taken into account if the PSO is not regenerated
ID3D12PipelineState *getGraphicPipelineState(
ID3D12Device *device,
ID3D12RootSignature *rootSignature,
RSXVertexProgram *vertexShader,
RSXFragmentProgram *fragmentShader,
const PipelineProperties &pipelineProperties,
const std::vector<D3D12_INPUT_ELEMENT_DESC> &IASet
);
}; };

View File

@ -69,7 +69,7 @@ void FragmentDecompiler::SetDst(std::string code, bool append_mask)
{ {
if (dst.set_cond) if (dst.set_cond)
{ {
AddCode("$ifcond " + m_parr.AddParam(PARAM_NONE, typeName[3], "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + code + ";"); AddCode("$ifcond " + m_parr.AddParam(PF_PARAM_NONE, typeName[3], "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + code + ";");
} }
else else
{ {
@ -86,7 +86,7 @@ void FragmentDecompiler::SetDst(std::string code, bool append_mask)
if (dst.set_cond) if (dst.set_cond)
{ {
AddCode(m_parr.AddParam(PARAM_NONE, typeName[3], "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + dest + ";"); AddCode(m_parr.AddParam(PF_PARAM_NONE, typeName[3], "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + dest + ";");
} }
} }
@ -114,24 +114,24 @@ std::string FragmentDecompiler::GetMask()
std::string FragmentDecompiler::AddReg(u32 index, int fp16) std::string FragmentDecompiler::AddReg(u32 index, int fp16)
{ {
return m_parr.AddParam(PARAM_NONE, typeName[3], std::string(fp16 ? "h" : "r") + std::to_string(index), typeName[3] + "(0.0)"); return m_parr.AddParam(PF_PARAM_NONE, typeName[3], std::string(fp16 ? "h" : "r") + std::to_string(index), typeName[3] + "(0.0)");
} }
bool FragmentDecompiler::HasReg(u32 index, int fp16) bool FragmentDecompiler::HasReg(u32 index, int fp16)
{ {
return m_parr.HasParam(PARAM_NONE, typeName[3], return m_parr.HasParam(PF_PARAM_NONE, typeName[3],
std::string(fp16 ? "h" : "r") + std::to_string(index)); std::string(fp16 ? "h" : "r") + std::to_string(index));
} }
std::string FragmentDecompiler::AddCond() std::string FragmentDecompiler::AddCond()
{ {
return m_parr.AddParam(PARAM_NONE, typeName[3], "cc" + std::to_string(src0.cond_reg_index)); return m_parr.AddParam(PF_PARAM_NONE, typeName[3], "cc" + std::to_string(src0.cond_reg_index));
} }
std::string FragmentDecompiler::AddConst() std::string FragmentDecompiler::AddConst()
{ {
std::string name = std::string("fc") + std::to_string(m_size + 4 * 4); std::string name = std::string("fc") + std::to_string(m_size + 4 * 4);
if (m_parr.HasParam(PARAM_UNIFORM, typeName[3], name)) if (m_parr.HasParam(PF_PARAM_UNIFORM, typeName[3], name))
{ {
return name; return name;
} }
@ -143,14 +143,14 @@ std::string FragmentDecompiler::AddConst()
u32 y = GetData(data[1]); u32 y = GetData(data[1]);
u32 z = GetData(data[2]); u32 z = GetData(data[2]);
u32 w = GetData(data[3]); u32 w = GetData(data[3]);
return m_parr.AddParam(PARAM_UNIFORM, typeName[3], name, return m_parr.AddParam(PF_PARAM_UNIFORM, typeName[3], name,
std::string(typeName[3] + "(") + std::to_string((float&)x) + ", " + std::to_string((float&)y) std::string(typeName[3] + "(") + std::to_string((float&)x) + ", " + std::to_string((float&)y)
+ ", " + std::to_string((float&)z) + ", " + std::to_string((float&)w) + ")"); + ", " + std::to_string((float&)z) + ", " + std::to_string((float&)w) + ")");
} }
std::string FragmentDecompiler::AddTex() std::string FragmentDecompiler::AddTex()
{ {
return m_parr.AddParam(PARAM_UNIFORM, "sampler2D", std::string("tex") + std::to_string(dst.tex_num)); return m_parr.AddParam(PF_PARAM_UNIFORM, "sampler2D", std::string("tex") + std::to_string(dst.tex_num));
} }
std::string FragmentDecompiler::Format(const std::string& code) std::string FragmentDecompiler::Format(const std::string& code)
@ -275,7 +275,7 @@ void FragmentDecompiler::AddCodeCond(const std::string& dst, const std::string&
cond = "(" + AddCond() + swizzle + " " + cond + " " + typeName[3] + "(0., 0., 0., 0.))"; cond = "(" + AddCond() + swizzle + " " + cond + " " + typeName[3] + "(0., 0., 0., 0.))";
ShaderVar dst_var(dst); ShaderVariable dst_var(dst);
dst_var.symplify(); dst_var.symplify();
//const char *c_mask = f; //const char *c_mask = f;
@ -320,12 +320,12 @@ template<typename T> std::string FragmentDecompiler::GetSRC(T src)
default: default:
if (dst.src_attr_reg_num < sizeof(reg_table) / sizeof(reg_table[0])) if (dst.src_attr_reg_num < sizeof(reg_table) / sizeof(reg_table[0]))
{ {
ret += m_parr.AddParam(PARAM_IN, typeName[3], reg_table[dst.src_attr_reg_num]); ret += m_parr.AddParam(PF_PARAM_IN, typeName[3], reg_table[dst.src_attr_reg_num]);
} }
else else
{ {
LOG_ERROR(RSX, "Bad src reg num: %d", fmt::by_value(dst.src_attr_reg_num)); LOG_ERROR(RSX, "Bad src reg num: %d", fmt::by_value(dst.src_attr_reg_num));
ret += m_parr.AddParam(PARAM_IN, typeName[3], "unk"); ret += m_parr.AddParam(PF_PARAM_IN, typeName[3], "unk");
Emu.Pause(); Emu.Pause();
} }
break; break;
@ -425,7 +425,7 @@ void FragmentDecompiler::insertOutputs(std::stringstream & OS)
for (int i = 0; i < sizeof(table) / sizeof(*table); ++i) for (int i = 0; i < sizeof(table) / sizeof(*table); ++i)
{ {
if (m_parr.HasParam(PARAM_NONE, typeName[3], table[i].second)) if (m_parr.HasParam(PF_PARAM_NONE, typeName[3], table[i].second))
OS << " " << typeName[3] << " " << table[i].first << " : SV_TARGET" << i << ";" << std::endl; OS << " " << typeName[3] << " " << table[i].first << " : SV_TARGET" << i << ";" << std::endl;
} }
OS << "};" << std::endl; OS << "};" << std::endl;
@ -443,7 +443,7 @@ void FragmentDecompiler::insertConstants(std::stringstream & OS)
} }
OS << "};" << std::endl;*/ OS << "};" << std::endl;*/
for (ParamType PT : m_parr.params[PARAM_UNIFORM]) for (ParamType PT : m_parr.params[PF_PARAM_UNIFORM])
{ {
for (ParamItem PI : PT.items) for (ParamItem PI : PT.items)
OS << PT.type << " " << PI.name << " = " << PI.value << ";" << std::endl; OS << PT.type << " " << PI.name << " = " << PI.value << ";" << std::endl;
@ -454,13 +454,13 @@ void FragmentDecompiler::insertMainStart(std::stringstream & OS)
{ {
OS << "PixelOutput main(PixelInput In)" << std::endl; OS << "PixelOutput main(PixelInput In)" << std::endl;
OS << "{" << std::endl; OS << "{" << std::endl;
for (ParamType PT : m_parr.params[PARAM_IN]) for (ParamType PT : m_parr.params[PF_PARAM_IN])
{ {
for (ParamItem PI : PT.items) for (ParamItem PI : PT.items)
OS << " " << PT.type << " " << PI.name << " = In." << PI.name << ";" << std::endl; OS << " " << PT.type << " " << PI.name << " = In." << PI.name << ";" << std::endl;
} }
// Declare output // Declare output
for (ParamType PT : m_parr.params[PARAM_NONE]) for (ParamType PT : m_parr.params[PF_PARAM_NONE])
{ {
for (ParamItem PI : PT.items) for (ParamItem PI : PT.items)
OS << " " << PT.type << " " << PI.name << " = float4(0., 0., 0., 0.);" << std::endl; OS << " " << PT.type << " " << PI.name << " = float4(0., 0., 0., 0.);" << std::endl;
@ -480,7 +480,7 @@ void FragmentDecompiler::insertMainEnd(std::stringstream & OS)
OS << " PixelOutput Out;" << std::endl; OS << " PixelOutput Out;" << std::endl;
for (int i = 0; i < sizeof(table) / sizeof(*table); ++i) for (int i = 0; i < sizeof(table) / sizeof(*table); ++i)
{ {
if (m_parr.HasParam(PARAM_NONE, typeName[3], table[i].second)) if (m_parr.HasParam(PF_PARAM_NONE, typeName[3], table[i].second))
OS << " Out." << table[i].first << " = " << table[i].second << ";" << std::endl; OS << " Out." << table[i].first << " = " << table[i].second << ";" << std::endl;
} }
OS << " return Out;" << std::endl; OS << " return Out;" << std::endl;

View File

@ -5,12 +5,12 @@
enum ParamFlag enum ParamFlag
{ {
PARAM_IN, PF_PARAM_IN,
PARAM_OUT, PF_PARAM_OUT,
PARAM_UNIFORM, PF_PARAM_UNIFORM,
PARAM_CONST, PF_PARAM_CONST,
PARAM_NONE, PF_PARAM_NONE,
PARAM_COUNT, PF_PARAM_COUNT,
}; };
struct ParamItem struct ParamItem
@ -51,7 +51,7 @@ struct ParamType
struct ParamArray struct ParamArray
{ {
std::vector<ParamType> params[PARAM_COUNT]; std::vector<ParamType> params[PF_PARAM_COUNT];
ParamType* SearchParam(const ParamFlag &flag, const std::string& type) ParamType* SearchParam(const ParamFlag &flag, const std::string& type)
{ {
@ -105,14 +105,14 @@ struct ParamArray
} }
}; };
class ShaderVar class ShaderVariable
{ {
public: public:
std::string name; std::string name;
std::vector<std::string> swizzles; std::vector<std::string> swizzles;
ShaderVar() = default; ShaderVariable() = default;
ShaderVar(const std::string& var) ShaderVariable(const std::string& var)
{ {
auto var_blocks = fmt::split(var, { "." }); auto var_blocks = fmt::split(var, { "." });
@ -138,7 +138,7 @@ public:
return swizzles[swizzles.size() - 1].length(); return swizzles[swizzles.size() - 1].length();
} }
ShaderVar& symplify() ShaderVariable& symplify()
{ {
std::unordered_map<char, char> swizzle; std::unordered_map<char, char> swizzle;

View File

@ -52,13 +52,13 @@ std::string VertexDecompiler::GetDST(bool isSca)
switch (isSca ? 0x1f : d3.dst) switch (isSca ? 0x1f : d3.dst)
{ {
case 0x1f: case 0x1f:
ret += m_parr.AddParam(PARAM_NONE, typeName[3], std::string("tmp") + std::to_string(isSca ? d3.sca_dst_tmp : d0.dst_tmp)); ret += m_parr.AddParam(PF_PARAM_NONE, typeName[3], std::string("tmp") + std::to_string(isSca ? d3.sca_dst_tmp : d0.dst_tmp));
break; break;
default: default:
if (d3.dst > 15) if (d3.dst > 15)
LOG_ERROR(RSX, fmt::Format("dst index out of range: %u", d3.dst)); LOG_ERROR(RSX, fmt::Format("dst index out of range: %u", d3.dst));
ret += m_parr.AddParam(PARAM_NONE, typeName[3], std::string("dst_reg") + std::to_string(d3.dst), d3.dst == 0 ? typeName[3] + "(0.0f, 0.0f, 0.0f, 1.0f)" : typeName[3] + "(0.0, 0.0, 0.0, 0.0)"); ret += m_parr.AddParam(PF_PARAM_NONE, typeName[3], std::string("dst_reg") + std::to_string(d3.dst), d3.dst == 0 ? typeName[3] + "(0.0f, 0.0f, 0.0f, 1.0f)" : typeName[3] + "(0.0, 0.0, 0.0, 0.0)");
break; break;
} }
@ -82,21 +82,21 @@ std::string VertexDecompiler::GetSRC(const u32 n)
switch (src[n].reg_type) switch (src[n].reg_type)
{ {
case 1: //temp case 1: //temp
ret += m_parr.AddParam(PARAM_NONE, typeName[3], "tmp" + std::to_string(src[n].tmp_src)); ret += m_parr.AddParam(PF_PARAM_NONE, typeName[3], "tmp" + std::to_string(src[n].tmp_src));
break; break;
case 2: //input case 2: //input
if (d1.input_src < (sizeof(reg_table) / sizeof(reg_table[0]))) if (d1.input_src < (sizeof(reg_table) / sizeof(reg_table[0])))
{ {
ret += m_parr.AddParam(PARAM_IN, typeName[3], reg_table[d1.input_src], d1.input_src); ret += m_parr.AddParam(PF_PARAM_IN, typeName[3], reg_table[d1.input_src], d1.input_src);
} }
else else
{ {
LOG_ERROR(RSX, "Bad input src num: %d", fmt::by_value(d1.input_src)); LOG_ERROR(RSX, "Bad input src num: %d", fmt::by_value(d1.input_src));
ret += m_parr.AddParam(PARAM_IN, typeName[3], "in_unk", d1.input_src); ret += m_parr.AddParam(PF_PARAM_IN, typeName[3], "in_unk", d1.input_src);
} }
break; break;
case 3: //const case 3: //const
m_parr.AddParam(PARAM_UNIFORM, typeName[3], std::string("vc[468]")); m_parr.AddParam(PF_PARAM_UNIFORM, typeName[3], std::string("vc[468]"));
ret += std::string("vc[") + std::to_string(d1.const_src) + (d3.index_const ? " + " + AddAddrReg() : "") + "]"; ret += std::string("vc[") + std::to_string(d1.const_src) + (d3.index_const ? " + " + AddAddrReg() : "") + "]";
break; break;
@ -161,7 +161,7 @@ void VertexDecompiler::SetDST(bool is_sca, std::string value)
if (d0.cond_update_enable_0 && d0.cond_update_enable_1) if (d0.cond_update_enable_0 && d0.cond_update_enable_1)
{ {
dest = m_parr.AddParam(PARAM_NONE, typeName[3], "cc" + std::to_string(d0.cond_reg_sel_1), typeName[3] + "(0.0)") + mask; dest = m_parr.AddParam(PF_PARAM_NONE, typeName[3], "cc" + std::to_string(d0.cond_reg_sel_1), typeName[3] + "(0.0)") + mask;
} }
else if (d3.dst != 0x1f || (is_sca ? d3.sca_dst_tmp != 0x3f : d0.dst_tmp != 0x3f)) else if (d3.dst != 0x1f || (is_sca ? d3.sca_dst_tmp != 0x3f : d0.dst_tmp != 0x3f))
{ {
@ -197,7 +197,7 @@ std::string VertexDecompiler::GetFunc()
std::string VertexDecompiler::GetTex() std::string VertexDecompiler::GetTex()
{ {
return m_parr.AddParam(PARAM_UNIFORM, "sampler2D", std::string("vtex") + std::to_string(/*?.tex_num*/0)); return m_parr.AddParam(PF_PARAM_UNIFORM, "sampler2D", std::string("vtex") + std::to_string(/*?.tex_num*/0));
} }
std::string VertexDecompiler::Format(const std::string& code) std::string VertexDecompiler::Format(const std::string& code)
@ -312,7 +312,7 @@ void VertexDecompiler::AddCodeCond(const std::string& dst, const std::string& sr
std::string cond = fmt::Format("%s(cc%d%s, vec4(0.0))", cond_string_table[d0.cond], d0.cond_reg_sel_1, swizzle.c_str()); std::string cond = fmt::Format("%s(cc%d%s, vec4(0.0))", cond_string_table[d0.cond], d0.cond_reg_sel_1, swizzle.c_str());
ShaderVar dst_var(dst); ShaderVariable dst_var(dst);
dst_var.symplify(); dst_var.symplify();
//const char *c_mask = f; //const char *c_mask = f;
@ -340,7 +340,7 @@ std::string VertexDecompiler::AddAddrMask()
std::string VertexDecompiler::AddAddrReg() std::string VertexDecompiler::AddAddrReg()
{ {
static const char f[] = { 'x', 'y', 'z', 'w' }; static const char f[] = { 'x', 'y', 'z', 'w' };
return m_parr.AddParam(PARAM_NONE, "ivec4", "a" + std::to_string(d0.addr_reg_sel_1), "ivec4(0)") + AddAddrMask(); return m_parr.AddParam(PF_PARAM_NONE, "ivec4", "a" + std::to_string(d0.addr_reg_sel_1), "ivec4(0)") + AddAddrMask();
} }
u32 VertexDecompiler::GetAddr() u32 VertexDecompiler::GetAddr()
@ -428,11 +428,11 @@ std::string VertexDecompiler::BuildCode()
std::stringstream OS; std::stringstream OS;
insertHeader(OS); insertHeader(OS);
insertInputs(OS, m_parr.params[PARAM_IN]); insertInputs(OS, m_parr.params[PF_PARAM_IN]);
OS << std::endl; OS << std::endl;
insertOutputs(OS, m_parr.params[PARAM_NONE]); insertOutputs(OS, m_parr.params[PF_PARAM_NONE]);
OS << std::endl; OS << std::endl;
insertConstants(OS, m_parr.params[PARAM_UNIFORM]); insertConstants(OS, m_parr.params[PF_PARAM_UNIFORM]);
OS << std::endl; OS << std::endl;
insertMainStart(OS); insertMainStart(OS);
@ -541,11 +541,11 @@ void VertexDecompiler::insertMainStart(std::stringstream & OS)
// Declare inside main function // Declare inside main function
for (auto &i : reg_table) for (auto &i : reg_table)
{ {
if (m_parr.HasParam(PARAM_NONE, typeName[3], i.src_reg)) if (m_parr.HasParam(PF_PARAM_NONE, typeName[3], i.src_reg))
OS << " float4 " << i.src_reg << ";" << std::endl; OS << " float4 " << i.src_reg << ";" << std::endl;
} }
for (const ParamType PT : m_parr.params[PARAM_IN]) for (const ParamType PT : m_parr.params[PF_PARAM_IN])
{ {
for (const ParamItem &PI : PT.items) for (const ParamItem &PI : PT.items)
OS << " " << PT.type << " " << PI.name << " = In." << PI.name << ";" << std::endl; OS << " " << PT.type << " " << PI.name << " = In." << PI.name << ";" << std::endl;
@ -559,7 +559,7 @@ void VertexDecompiler::insertMainEnd(std::stringstream & OS)
// Declare inside main function // Declare inside main function
for (auto &i : reg_table) for (auto &i : reg_table)
{ {
if (m_parr.HasParam(PARAM_NONE, typeName[3], i.src_reg)) if (m_parr.HasParam(PF_PARAM_NONE, typeName[3], i.src_reg))
OS << " Out." << i.src_reg << " = " << i.src_reg << ";" << std::endl; OS << " Out." << i.src_reg << " = " << i.src_reg << ";" << std::endl;
} }
// TODO: Find why I need to do this // TODO: Find why I need to do this
@ -583,7 +583,7 @@ VertexDecompiler::VertexDecompiler(std::vector<u32>& data) :
std::string VertexDecompiler::Decompile() std::string VertexDecompiler::Decompile()
{ {
for (unsigned i = 0; i < PARAM_COUNT; i++) for (unsigned i = 0; i < PF_PARAM_COUNT; i++)
m_parr.params[i].clear(); m_parr.params[i].clear();
m_instr_count = 0; m_instr_count = 0;