mirror of https://github.com/RPCS3/rpcs3.git
d3d12: Avoid recompiling fragment shader if constants change
This commit is contained in:
parent
789ed767e2
commit
5cb0fe63b8
|
@ -13,20 +13,23 @@
|
||||||
|
|
||||||
#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;
|
size_t getFPBinarySize(void *ptr)
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
const qword& inst = instBuffer[instIndex];
|
const qword *instBuffer = (const qword*)ptr;
|
||||||
bool end = (inst.word[0] >> 8) & 0x1;
|
size_t instIndex = 0;
|
||||||
if (end)
|
while (true)
|
||||||
return (instIndex + 1) * 4;
|
{
|
||||||
instIndex++;
|
const qword& inst = instBuffer[instIndex];
|
||||||
|
bool end = (inst.word[0] >> 8) & 0x1;
|
||||||
|
if (end)
|
||||||
|
return (instIndex + 1) * 4 * 4;
|
||||||
|
instIndex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
PipelineStateObjectCache::PipelineStateObjectCache() : m_currentShaderId(0)
|
PipelineStateObjectCache::PipelineStateObjectCache() : m_currentShaderId(0)
|
||||||
|
@ -79,7 +82,7 @@ void PipelineStateObjectCache::AddVertexProgram(Shader& vp, RSXVertexProgram& rs
|
||||||
|
|
||||||
void PipelineStateObjectCache::AddFragmentProgram(Shader& fp, RSXFragmentProgram& rsx_fp)
|
void PipelineStateObjectCache::AddFragmentProgram(Shader& fp, RSXFragmentProgram& rsx_fp)
|
||||||
{
|
{
|
||||||
size_t actualFPSize = getFPBinarySize(vm::get_ptr<u8>(rsx_fp.addr));
|
size_t actualFPSize = ProgramHashUtil::getFPBinarySize(vm::get_ptr<u8>(rsx_fp.addr));
|
||||||
void *fpShadowCopy = malloc(actualFPSize);
|
void *fpShadowCopy = malloc(actualFPSize);
|
||||||
memcpy(fpShadowCopy, vm::get_ptr<u8>(rsx_fp.addr), actualFPSize);
|
memcpy(fpShadowCopy, vm::get_ptr<u8>(rsx_fp.addr), actualFPSize);
|
||||||
fp.Id = (u32)m_currentShaderId++;
|
fp.Id = (u32)m_currentShaderId++;
|
||||||
|
|
|
@ -40,109 +40,150 @@ public:
|
||||||
void Compile(const std::string &code, SHADER_TYPE st);
|
void Compile(const std::string &code, SHADER_TYPE st);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Based on
|
|
||||||
// https://github.com/AlexAltea/nucleus/blob/master/nucleus/gpu/rsx_pgraph.cpp
|
|
||||||
union qword
|
|
||||||
{
|
|
||||||
u64 dword[2];
|
|
||||||
u32 word[4];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HashVertexProgram
|
|
||||||
|
namespace ProgramHashUtil
|
||||||
{
|
{
|
||||||
size_t operator()(const void *program) const
|
// Based on
|
||||||
|
// https://github.com/AlexAltea/nucleus/blob/master/nucleus/gpu/rsx_pgraph.cpp
|
||||||
|
union qword
|
||||||
{
|
{
|
||||||
// 64-bit Fowler/Noll/Vo FNV-1a hash code
|
u64 dword[2];
|
||||||
size_t hash = 0xCBF29CE484222325ULL;
|
u32 word[4];
|
||||||
const qword *instbuffer = (const qword*)program;
|
};
|
||||||
size_t instIndex = 0;
|
|
||||||
bool end = false;
|
|
||||||
return 0;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
const qword inst = instbuffer[instIndex];
|
|
||||||
bool end = inst.word[0] >> 31;
|
|
||||||
if (end)
|
|
||||||
return hash;
|
|
||||||
hash ^= inst.dword[0];
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HashFragmentProgram
|
struct HashVertexProgram
|
||||||
{
|
|
||||||
size_t operator()(const void *program) const
|
|
||||||
{
|
{
|
||||||
// 64-bit Fowler/Noll/Vo FNV-1a hash code
|
size_t operator()(const void *program) const
|
||||||
size_t hash = 0xCBF29CE484222325ULL;
|
|
||||||
const qword *instbuffer = (const qword*)program;
|
|
||||||
size_t instIndex = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
const qword& inst = instbuffer[instIndex];
|
// 64-bit Fowler/Noll/Vo FNV-1a hash code
|
||||||
bool end = (inst.word[0] >> 8) & 0x1;
|
size_t hash = 0xCBF29CE484222325ULL;
|
||||||
if (end)
|
const qword *instbuffer = (const qword*)program;
|
||||||
return hash;
|
size_t instIndex = 0;
|
||||||
hash ^= inst.dword[0];
|
bool end = false;
|
||||||
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
|
return 0;
|
||||||
hash ^= inst.dword[1];
|
while (true)
|
||||||
hash += (hash << 1) + (hash << 4) + (hash << 5) + (hash << 7) + (hash << 8) + (hash << 40);
|
{
|
||||||
instIndex++;
|
const qword inst = instbuffer[instIndex];
|
||||||
|
bool end = inst.word[0] >> 31;
|
||||||
|
if (end)
|
||||||
|
return hash;
|
||||||
|
hash ^= inst.dword[0];
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
return 0;
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexProgramCompare
|
|
||||||
{
|
struct VertexProgramCompare
|
||||||
bool operator()(const void *binary1, const void *binary2) const
|
|
||||||
{
|
{
|
||||||
const qword *instBuffer1 = (const qword*)binary1;
|
bool operator()(const void *binary1, const void *binary2) const
|
||||||
const qword *instBuffer2 = (const qword*)binary2;
|
|
||||||
size_t instIndex = 0;
|
|
||||||
return true;
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
const qword& inst1 = instBuffer1[instIndex];
|
const qword *instBuffer1 = (const qword*)binary1;
|
||||||
const qword& inst2 = instBuffer2[instIndex];
|
const qword *instBuffer2 = (const qword*)binary2;
|
||||||
bool end = (inst1.word[0] >> 31) && (inst2.word[0] >> 31);
|
size_t instIndex = 0;
|
||||||
if (end)
|
return true;
|
||||||
return true;
|
while (true)
|
||||||
if (inst1.dword[0] != inst2.dword[0] || inst1.dword[1] != inst2.dword[1])
|
{
|
||||||
return false;
|
const qword& inst1 = instBuffer1[instIndex];
|
||||||
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 FragmentProgramCompare
|
struct FragmentHashUtil
|
||||||
{
|
|
||||||
bool operator()(const void *binary1, const void *binary2) const
|
|
||||||
{
|
{
|
||||||
const qword *instBuffer1 = (const qword*)binary1;
|
/**
|
||||||
const qword *instBuffer2 = (const qword*)binary2;
|
* RSX fragment program constants are inlined inside shader code.
|
||||||
size_t instIndex = 0;
|
* This function takes an instruction from a fragment program and
|
||||||
while (true)
|
* 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)
|
||||||
{
|
{
|
||||||
const qword& inst1 = instBuffer1[instIndex];
|
qword result = initialQword;
|
||||||
const qword& inst2 = instBuffer2[instIndex];
|
u64 dword0Mask = 0, dword1Mask = 0;;
|
||||||
bool end = ((inst1.word[0] >> 8) & 0x1) && ((inst2.word[0] >> 8) & 0x1);
|
// Check if there is a constant and mask word if there is
|
||||||
if (end)
|
SRC0 s0 = { initialQword.word[1] };
|
||||||
return true;
|
SRC1 s1 = { initialQword.word[2] };
|
||||||
if (inst1.dword[0] != inst2.dword[0] || inst1.dword[1] != inst2.dword[1])
|
SRC2 s2 = { initialQword.word[3] };
|
||||||
return false;
|
if (s0.reg_type == 2)
|
||||||
instIndex++;
|
result.word[1] = 0;
|
||||||
|
if (s1.reg_type == 2)
|
||||||
|
result.word[2] = 0;
|
||||||
|
if (s2.reg_type == 2)
|
||||||
|
result.word[3] = 0;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::unordered_map<void *, Shader, HashVertexProgram, VertexProgramCompare> binary2VS;
|
struct HashFragmentProgram
|
||||||
typedef std::unordered_map<void *, Shader, HashFragmentProgram, FragmentProgramCompare> binary2FS;
|
{
|
||||||
|
size_t operator()(const void *program) const
|
||||||
|
{
|
||||||
|
// 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 = FragmentHashUtil::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++;
|
||||||
|
}
|
||||||
|
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 = FragmentHashUtil::fragmentMaskConstant(inst1);
|
||||||
|
const qword& maskedInst2 = FragmentHashUtil::fragmentMaskConstant(inst2);
|
||||||
|
|
||||||
|
if (maskedInst1.dword[0] != maskedInst2.dword[0] || maskedInst1.dword[1] != maskedInst2.dword[1])
|
||||||
|
return false;
|
||||||
|
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
|
struct PSOKey
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue