mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-22 22:46:41 +00:00
Replace manual patching of PSV with decode/encode
* The DXIL program editor will now automatically decode and encode the PSV and RDAT chunks (if found) as part of its roundtrip
This commit is contained in:
@@ -600,189 +600,54 @@ void D3D12Replay::PatchQuadWritePS(D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC &pi
|
||||
}
|
||||
}
|
||||
|
||||
DXIL::PSVData rastFeedingPSV;
|
||||
DXIL::PSVData patchedPSV;
|
||||
|
||||
if(rastFeedingDXBC.GetPipelineValidation(rastFeedingPSV) &&
|
||||
quadOverdrawDXBC.GetPipelineValidation(patchedPSV))
|
||||
{
|
||||
// do a horrible franken-patch to merge the PSV0 chunks. We use the header from the existing
|
||||
// PS,
|
||||
// change the number of declared input elements, then copy the signature elements from the
|
||||
// last
|
||||
// shader's chunk. We can't copy the whole string table because that will likely include other
|
||||
// strings and then the damned thing won't match according to the runtime's validation.
|
||||
size_t rastPsv0Size = 0;
|
||||
const byte *rastPsv0Bytes =
|
||||
DXBC::DXBCContainer::FindChunk(rastFeedingBytes, DXBC::FOURCC_PSV0, rastPsv0Size);
|
||||
StreamReader rastPsv0(rastPsv0Bytes, rastPsv0Size);
|
||||
DXIL::PSVData mergedPSV = patchedPSV;
|
||||
|
||||
size_t psPsv0Size = 0;
|
||||
const byte *psPsv0Bytes =
|
||||
DXBC::DXBCContainer::FindChunk(patchedDXBC, DXBC::FOURCC_PSV0, psPsv0Size);
|
||||
StreamReader psPsv0(psPsv0Bytes, psPsv0Size);
|
||||
|
||||
StreamWriter mergedPsv0(1024);
|
||||
|
||||
uint32_t rastHeaderSize = 0;
|
||||
if(!rastPsv0.Read<uint32_t>(rastHeaderSize))
|
||||
return;
|
||||
|
||||
uint32_t psHeaderSize = 0;
|
||||
if(!psPsv0.Read<uint32_t>(psHeaderSize))
|
||||
return;
|
||||
|
||||
struct PSVHeader0
|
||||
// if the previous shader only uses version0, use version0 ourselves as I don't know if it's
|
||||
// safe to mix and match versions in a pipeline. Version 0 doesn't have signatures so we can
|
||||
// basically stop here, the resources are already all set up as we based mergedPSV on patchedPSV
|
||||
if(rastFeedingPSV.version == DXIL::PSVData::Version::Version0)
|
||||
{
|
||||
uint32_t unused[6];
|
||||
};
|
||||
|
||||
struct PSVHeader1 : public PSVHeader0
|
||||
{
|
||||
// other data
|
||||
uint32_t unused1;
|
||||
|
||||
// signature element counts
|
||||
uint8_t inputEls;
|
||||
uint8_t outputEls;
|
||||
uint8_t patchConstEls;
|
||||
|
||||
// signature vector counts
|
||||
uint8_t inputVecs;
|
||||
uint8_t outputVecs[4];
|
||||
};
|
||||
|
||||
struct PSVHeader2 : public PSVHeader1
|
||||
{
|
||||
uint32_t NumThreadsX;
|
||||
uint32_t NumThreadsY;
|
||||
uint32_t NumThreadsZ;
|
||||
};
|
||||
|
||||
bytebuf copyBuf;
|
||||
PSVHeader2 rastHeader = {}, psHeader = {};
|
||||
|
||||
if(rastHeaderSize < sizeof(PSVHeader1))
|
||||
{
|
||||
// only copy the header0 part out of the ps one since we won't have signature data to copy,
|
||||
// hope this is OK
|
||||
|
||||
// read the whole ps header
|
||||
psPsv0.Read(&psHeader, psHeaderSize);
|
||||
|
||||
// write only the old sized header
|
||||
mergedPsv0.Write(rastHeaderSize);
|
||||
mergedPsv0.Write(&psHeader, rastHeaderSize);
|
||||
mergedPSV.version = DXIL::PSVData::Version::Version0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rastPsv0.Read(&rastHeader, rastHeaderSize);
|
||||
psPsv0.Read(&psHeader, psHeaderSize);
|
||||
// newer versions, we need to patch the signature elements in our new patched PS reading
|
||||
// from the right input signature element to match the previous signature's outputs
|
||||
mergedPSV.inputSigElems = rastFeedingPSV.outputSigElems;
|
||||
mergedPSV.inputSigVectors = rastFeedingPSV.outputSigVectors[0];
|
||||
mergedPSV.inputSig = rastFeedingPSV.outputSig;
|
||||
|
||||
// copy the previous output signature into the ps input
|
||||
psHeader.inputEls = rastHeader.outputEls;
|
||||
psHeader.inputVecs = rastHeader.outputVecs[0];
|
||||
|
||||
// the ps header should have no other elements for us to worry about
|
||||
RDCASSERT(psHeader.outputEls == 0);
|
||||
RDCASSERT(psHeader.patchConstEls == 0);
|
||||
RDCASSERT(psHeader.outputVecs[0] == 0);
|
||||
RDCASSERT(psHeader.outputVecs[1] == 0);
|
||||
RDCASSERT(psHeader.outputVecs[2] == 0);
|
||||
RDCASSERT(psHeader.outputVecs[3] == 0);
|
||||
|
||||
// we should have a table offset for each output entry
|
||||
RDCASSERT(rastHeader.outputEls == stringTableOffsets.size());
|
||||
RDCASSERT(rastHeader.outputEls == semanticIndexTableOffsets.size());
|
||||
|
||||
mergedPsv0.Write(psHeaderSize);
|
||||
mergedPsv0.Write(&psHeader, psHeaderSize);
|
||||
}
|
||||
|
||||
// skip resource counts in raster side shader
|
||||
uint32_t rastResCount = 0;
|
||||
if(!rastPsv0.Read<uint32_t>(rastResCount))
|
||||
return;
|
||||
|
||||
if(rastResCount > 0)
|
||||
{
|
||||
uint32_t resSize = 0;
|
||||
if(!rastPsv0.Read<uint32_t>(resSize))
|
||||
return;
|
||||
rastPsv0.SkipBytes(rastResCount * resSize);
|
||||
}
|
||||
|
||||
uint32_t psResCount = 0;
|
||||
if(!psPsv0.Read<uint32_t>(psResCount))
|
||||
return;
|
||||
mergedPsv0.Write(psResCount);
|
||||
|
||||
// copy any resources in the pixel psv0
|
||||
if(psResCount > 0)
|
||||
{
|
||||
uint32_t resSize = 0;
|
||||
if(!psPsv0.Read<uint32_t>(resSize))
|
||||
return;
|
||||
mergedPsv0.Write(resSize);
|
||||
copyBuf.resize(psResCount * resSize);
|
||||
psPsv0.Read(copyBuf.data(), copyBuf.size());
|
||||
mergedPsv0.Write(copyBuf.data(), copyBuf.size());
|
||||
}
|
||||
|
||||
// if we have a new header with signature elements (what we expect)
|
||||
if(rastHeaderSize >= sizeof(PSVHeader1))
|
||||
{
|
||||
// we're effectively done with the rest of the ps chunk here, we're just going to copy the
|
||||
// old
|
||||
// chunk except skipping the input signature. There might be data we don't need in the
|
||||
// string/indices tables but that's fine.
|
||||
|
||||
// align string table to multiple of 4 size
|
||||
stringTable.resize(AlignUp4(stringTable.size()));
|
||||
|
||||
// skip the old string table and semantic index table
|
||||
uint32_t stringTableSize = 0;
|
||||
if(!rastPsv0.Read<uint32_t>(stringTableSize))
|
||||
return;
|
||||
rastPsv0.SkipBytes(stringTableSize);
|
||||
|
||||
uint32_t indexTableSize = 0;
|
||||
if(!rastPsv0.Read<uint32_t>(indexTableSize))
|
||||
return;
|
||||
rastPsv0.SkipBytes(indexTableSize * sizeof(uint32_t));
|
||||
|
||||
mergedPsv0.Write((uint32_t)stringTable.size());
|
||||
mergedPsv0.Write(stringTable.data(), stringTable.size());
|
||||
|
||||
mergedPsv0.Write((uint32_t)semanticIndexTable.size());
|
||||
mergedPsv0.Write(semanticIndexTable.data(), semanticIndexTable.byteSize());
|
||||
|
||||
uint32_t sigElSize = 0;
|
||||
if(!rastPsv0.Read<uint32_t>(sigElSize))
|
||||
return;
|
||||
mergedPsv0.Write(sigElSize);
|
||||
|
||||
// skip any inputs from the previous stage, we don't want to copy that
|
||||
rastPsv0.SkipBytes(rastHeader.inputEls * sigElSize);
|
||||
|
||||
struct PSVSigElement
|
||||
// the PS header should have no other elements for us to worry about
|
||||
RDCASSERT(mergedPSV.outputSigElems == 0);
|
||||
RDCASSERT(mergedPSV.patchConstPrimSigElems == 0);
|
||||
for(uint32_t i = 0; i < 4; i++)
|
||||
{
|
||||
uint32_t stringTableOffset;
|
||||
uint32_t semanticTableOffset;
|
||||
};
|
||||
|
||||
// copy the output elements, this will become the input elements. We need to modify the
|
||||
// table
|
||||
// offsets to match the one we generated
|
||||
for(uint8_t el = 0; el < rastHeader.outputEls; el++)
|
||||
{
|
||||
copyBuf.resize(sigElSize);
|
||||
rastPsv0.Read(copyBuf.data(), copyBuf.size());
|
||||
PSVSigElement *sigEl = (PSVSigElement *)copyBuf.data();
|
||||
sigEl->stringTableOffset = stringTableOffsets[el];
|
||||
sigEl->semanticTableOffset = semanticIndexTableOffsets[el];
|
||||
|
||||
mergedPsv0.Write(copyBuf.data(), copyBuf.size());
|
||||
RDCASSERT(mergedPSV.outputSigVectors[i] == 0);
|
||||
}
|
||||
|
||||
// similarly we should have no I/O dependency tables
|
||||
for(uint32_t i = 0; i < 4; i++)
|
||||
{
|
||||
RDCASSERT(mergedPSV.viewIDAffects.outputMask[i].bitmask[0] == 0);
|
||||
RDCASSERT(mergedPSV.viewIDAffects.outputMask[i].bitmask[1] == 0);
|
||||
RDCASSERT(mergedPSV.IODependencies[i].dependentOutputsForInput.empty());
|
||||
}
|
||||
RDCASSERT(mergedPSV.viewIDAffects.patchConstOrPrimMask.bitmask[0] == 0);
|
||||
RDCASSERT(mergedPSV.viewIDAffects.patchConstOrPrimMask.bitmask[1] == 0);
|
||||
RDCASSERT(mergedPSV.PCIODependencies.dependentPCOutputsForInput.empty());
|
||||
}
|
||||
|
||||
DXBC::DXBCContainer::ReplaceChunk(patchedDXBC, DXBC::FOURCC_PSV0, mergedPsv0.GetData(),
|
||||
(size_t)mergedPsv0.GetOffset());
|
||||
DXBC::DXBCContainer::SetPipelineValidation(patchedDXBC, mergedPSV);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Couldn't get PSV for raster feeding or quad write shader to patch");
|
||||
}
|
||||
}
|
||||
else // dxbc bytecode not dxil
|
||||
|
||||
@@ -98,6 +98,9 @@ ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outB
|
||||
}
|
||||
|
||||
m_Constants.removeIf([](Constant *a) { return a == NULL; });
|
||||
|
||||
m_PSVPresent = container->GetPipelineValidation(m_PSV);
|
||||
m_RDATPresent = container->GetRuntimeData(m_RDAT);
|
||||
}
|
||||
|
||||
ProgramEditor::~ProgramEditor()
|
||||
@@ -141,6 +144,16 @@ ProgramEditor::~ProgramEditor()
|
||||
// also strip STAT because it might have stale reflection info
|
||||
DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_STAT);
|
||||
|
||||
if(m_PSVPresent)
|
||||
{
|
||||
DXBC::DXBCContainer::SetPipelineValidation(m_OutBlob, m_PSV);
|
||||
}
|
||||
|
||||
if(m_RDATPresent)
|
||||
{
|
||||
DXBC::DXBCContainer::SetRuntimeData(m_OutBlob, m_RDAT);
|
||||
}
|
||||
|
||||
#if ENABLED(RDOC_DEVEL) && 1
|
||||
// on debug builds, run through dxil for "validation" if it's available.
|
||||
// we need BOTH of htese because dxil.dll's interface is incomplete, it lacks the library
|
||||
@@ -1702,84 +1715,15 @@ struct ResourceBind1 : ResourceBind0
|
||||
void ProgramEditor::RegisterUAV(DXILResourceType type, uint32_t space, uint32_t regBase,
|
||||
uint32_t regEnd, ResourceKind kind)
|
||||
{
|
||||
size_t sz = 0;
|
||||
const byte *psv0 = DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_PSV0, sz);
|
||||
|
||||
ResourceBind1 bind = {};
|
||||
DXIL::PSVResource bind = {};
|
||||
bind.type = type;
|
||||
bind.space = space;
|
||||
bind.regBase = regBase;
|
||||
bind.regStart = regBase;
|
||||
bind.regEnd = regEnd;
|
||||
bind.kind = kind;
|
||||
|
||||
if(psv0)
|
||||
{
|
||||
bytebuf psv0blob(psv0, sz);
|
||||
|
||||
byte *begin = psv0blob.data();
|
||||
byte *end = begin + sz;
|
||||
|
||||
byte *cur = begin;
|
||||
|
||||
uint32_t *headerSize = (uint32_t *)cur;
|
||||
cur += sizeof(uint32_t);
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
// don't need to patch the header
|
||||
cur += *headerSize;
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
uint32_t *numResources = (uint32_t *)cur;
|
||||
cur += sizeof(uint32_t);
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
if(*numResources > 0)
|
||||
{
|
||||
uint32_t *resourceBindSize = (uint32_t *)cur;
|
||||
cur += sizeof(uint32_t);
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
// fortunately UAVs are the last entry so we don't need to walk the list to insert in the
|
||||
// right place, we can just add it at the end
|
||||
cur += (*resourceBindSize) * (*numResources);
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
// add an extra resource
|
||||
(*numResources)++;
|
||||
|
||||
if(*resourceBindSize == sizeof(ResourceBind1) || *resourceBindSize == sizeof(ResourceBind0))
|
||||
{
|
||||
psv0blob.insert(cur - begin, (byte *)&bind, *resourceBindSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Unexpected resource bind size %u", *resourceBindSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// from definitions in dxc
|
||||
const uint32_t headerSizeVer0 = 6 * sizeof(uint32_t);
|
||||
const uint32_t headerSizeVer1 = headerSizeVer0 + sizeof(uint16_t) + 10 * sizeof(uint8_t);
|
||||
const uint32_t headerSizeVer2 = headerSizeVer1 + 3 * sizeof(uint32_t);
|
||||
|
||||
// If there is no resource in the chunk we also need to insert the size of a resource bind
|
||||
*numResources = 1;
|
||||
size_t insertOffset = cur - begin;
|
||||
uint32_t resourceBindSize =
|
||||
*headerSize == headerSizeVer2 ? sizeof(ResourceBind1) : sizeof(ResourceBind0);
|
||||
psv0blob.insert(insertOffset, (byte *)&resourceBindSize, sizeof(resourceBindSize));
|
||||
psv0blob.insert(insertOffset + sizeof(resourceBindSize), (byte *)&bind, resourceBindSize);
|
||||
}
|
||||
|
||||
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
|
||||
}
|
||||
RDCASSERT(m_PSVPresent);
|
||||
m_PSV.resources.push_back(bind);
|
||||
|
||||
// patch SFI0 here for non-CS non-PS shaders
|
||||
if(m_Type != DXBC::ShaderType::Compute && m_Type != DXBC::ShaderType::Pixel)
|
||||
@@ -1794,87 +1738,22 @@ void ProgramEditor::RegisterUAV(DXILResourceType type, uint32_t space, uint32_t
|
||||
|
||||
void ProgramEditor::SetNumThreads(uint32_t dim[3])
|
||||
{
|
||||
size_t sz = 0;
|
||||
const byte *psv0 = DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_PSV0, sz);
|
||||
RDCASSERT(m_PSVPresent);
|
||||
RDCASSERT(m_PSV.version >= PSVData::Version::Version2);
|
||||
|
||||
if(psv0)
|
||||
{
|
||||
bytebuf psv0blob(psv0, sz);
|
||||
|
||||
byte *begin = psv0blob.data();
|
||||
byte *end = begin + sz;
|
||||
|
||||
byte *cur = begin;
|
||||
|
||||
uint32_t *headerSize = (uint32_t *)cur;
|
||||
cur += sizeof(uint32_t);
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
// from definitions in dxc
|
||||
const uint32_t headerSizeVer0 = 6 * sizeof(uint32_t);
|
||||
const uint32_t headerSizeVer1 = headerSizeVer0 + sizeof(uint16_t) + 10 * sizeof(uint8_t);
|
||||
const uint32_t headerSizeVer2 = headerSizeVer1 + 3 * sizeof(uint32_t);
|
||||
|
||||
if(*headerSize >= headerSizeVer2)
|
||||
{
|
||||
cur += headerSizeVer1;
|
||||
memcpy(cur, dim, sizeof(uint32_t) * 3);
|
||||
}
|
||||
|
||||
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
|
||||
}
|
||||
memcpy(m_PSV.threadCount, dim, sizeof(uint32_t) * 3);
|
||||
}
|
||||
|
||||
void ProgramEditor::SetASPayloadSize(uint32_t payloadSize)
|
||||
{
|
||||
size_t sz = 0;
|
||||
const byte *psv0 = DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_PSV0, sz);
|
||||
|
||||
if(psv0)
|
||||
{
|
||||
bytebuf psv0blob(psv0, sz);
|
||||
|
||||
byte *begin = psv0blob.data();
|
||||
byte *end = begin + sz;
|
||||
|
||||
byte *cur = begin;
|
||||
|
||||
cur += sizeof(uint32_t);
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
// the AS info with the payload size is immediately at the start of the header
|
||||
memcpy(cur, &payloadSize, sizeof(uint32_t));
|
||||
|
||||
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
|
||||
}
|
||||
RDCASSERT(m_PSVPresent);
|
||||
m_PSV.as.payloadBytes = payloadSize;
|
||||
}
|
||||
|
||||
void ProgramEditor::SetMSPayloadSize(uint32_t payloadSize)
|
||||
{
|
||||
size_t sz = 0;
|
||||
const byte *psv0 = DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_PSV0, sz);
|
||||
|
||||
if(psv0)
|
||||
{
|
||||
bytebuf psv0blob(psv0, sz);
|
||||
|
||||
byte *begin = psv0blob.data();
|
||||
byte *end = begin + sz;
|
||||
|
||||
byte *cur = begin;
|
||||
|
||||
cur += sizeof(uint32_t);
|
||||
if(cur >= end)
|
||||
return;
|
||||
|
||||
// the MS info is immediately at the start of the header
|
||||
// the first two uint32s are groupshared related, then comes the payload size
|
||||
memcpy(cur + sizeof(uint32_t) * 2, &payloadSize, sizeof(uint32_t));
|
||||
|
||||
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
|
||||
}
|
||||
RDCASSERT(m_PSVPresent);
|
||||
m_PSV.ms.payloadBytes = payloadSize;
|
||||
}
|
||||
|
||||
void ProgramEditor::PatchGlobalShaderFlags(std::function<void(DXBC::GlobalShaderFlags &)> patcher)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "dxil_bytecode.h"
|
||||
#include "dxil_common.h"
|
||||
#include "dxil_metadata.h"
|
||||
|
||||
namespace DXBC
|
||||
{
|
||||
@@ -129,6 +130,11 @@ public:
|
||||
private:
|
||||
bytebuf &m_OutBlob;
|
||||
|
||||
DXIL::RDATData m_RDAT;
|
||||
DXIL::PSVData m_PSV;
|
||||
bool m_RDATPresent = false;
|
||||
bool m_PSVPresent = false;
|
||||
|
||||
rdcarray<Constant *> m_Constants;
|
||||
|
||||
Type *CreateNewType();
|
||||
|
||||
Reference in New Issue
Block a user