d3d12: Avoid recompiling fragment shader if constants change

This commit is contained in:
vlj 2015-05-15 17:38:56 +02:00 committed by Vincent Lejeune
parent 789ed767e2
commit 5cb0fe63b8
2 changed files with 143 additions and 99 deletions

View File

@ -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++;

View File

@ -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
{ {