Add initial part of injecting code to SPIR-V to dump outputs to buffer

* At the moment it adds the necessary types and decorations etc to
  declare the buffer, but doesn't add any code to write the outputs yet.
This commit is contained in:
baldurk
2016-02-07 18:47:05 +01:00
parent 4370fda970
commit 344a4a4d2d
5 changed files with 543 additions and 7 deletions
@@ -58,6 +58,8 @@ struct SPVModule
SPVModule();
~SPVModule();
vector<uint32_t> spirv;
uint32_t moduleVersion;
uint32_t generator;
@@ -2627,6 +2627,24 @@ void SPVModule::MakeReflection(ShaderReflection *reflection, ShaderBindpointMapp
RDCWARN("Unexpected storage class for global: %s", ToStr::Get(inst->var->storage).c_str());
}
}
// sort system value semantics to the start of the list
struct sig_param_sort
{
bool operator() (const SigParameter &a, const SigParameter &b)
{
if(a.systemValue == b.systemValue) return a.regIndex < b.regIndex;
if(a.systemValue == eAttr_None)
return false;
if(b.systemValue == eAttr_None)
return true;
return a.systemValue < b.systemValue;
}
};
std::sort(inputs.begin(), inputs.end(), sig_param_sort());
std::sort(outputs.begin(), outputs.end(), sig_param_sort());
reflection->InputSig = inputs;
reflection->OutputSig = outputs;
@@ -2681,6 +2699,8 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
return;
}
module.spirv.assign(spirv, spirv+spirvLength);
module.generator = spirv[2];
module.ids.resize(spirv[3]);
@@ -2694,12 +2714,12 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
size_t it = 5;
while(it < spirvLength)
{
uint16_t WordCount = spirv[it]>>16;
uint16_t WordCount = spirv[it]>>spv::WordCountShift;
module.operations.push_back(new SPVInstruction());
SPVInstruction &op = *module.operations.back();
op.opcode = spv::Op(spirv[it]&0xffff);
op.opcode = spv::Op(spirv[it]&spv::OpCodeMask);
bool mathop = false;
@@ -3555,8 +3575,8 @@ void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module)
it = 5;
while(it < spirvLength)
{
uint16_t WordCount = spirv[it]>>16;
spv::Op op = spv::Op(spirv[it]&0xffff);
uint16_t WordCount = spirv[it]>>spv::WordCountShift;
spv::Op op = spv::Op(spirv[it]&spv::OpCodeMask);
switch(op)
{
+514 -2
View File
@@ -1945,8 +1945,8 @@ void VulkanDebugManager::PatchFixedColShader(VkShaderModule &mod, VkShader &shad
size_t it = 5;
while(it < spirvLength)
{
uint16_t WordCount = spirv[it]>>16;
spv::Op opcode = spv::Op(spirv[it]&0xffff);
uint16_t WordCount = spirv[it]>>spv::WordCountShift;
spv::Op opcode = spv::Op(spirv[it]&spv::OpCodeMask);
if(opcode == spv::OpConstant)
{
@@ -3059,3 +3059,515 @@ MeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(const MeshFor
return cache;
}
inline uint32_t MakeSPIRVOp(spv::Op op, uint32_t WordCount)
{
return (uint32_t(op) & spv::OpCodeMask) | (WordCount << spv::WordCountShift);
}
void AddOutputDumping(ShaderReflection refl, vector<uint32_t> &modSpirv)
{
uint32_t *spirv = &modSpirv[0];
size_t spirvLength = modSpirv.size();
int numOutputs = refl.OutputSig.count;
// save the id bound. We use this whenever we need to allocate ourselves
// a new ID
uint32_t idBound = spirv[3];
// we do multiple passes through the SPIR-V to simplify logic, rather than
// trying to do as few passes as possible.
// first try to find a few IDs of things we know we'll probably need:
// * gl_VertexID (identified by a DecorationBuiltIn
// * Int32 type, signed and unsigned
// * Float types, half, float and double
// * Input Pointer to Int32 (for declaring gl_VertexID)
// * UInt32 constants from 0 up to however many outputs we have
//
// At the same time we find the highest descriptor set used and add a
// new descriptor set binding on the end for our output buffer. This is
// much easier than trying to add a new bind to an existing descriptor
// set (which would cascade into a new descriptor set layout, new pipeline
// layout, etc etc!). However, this might push us over the limit on number
// of descriptor sets.
//
// we also note the index where decorations end, and the index where
// functions start, for if we need to add new decorations or new
// types/constants/global variables
uint32_t vertidxID = 0;
uint32_t sint32ID = 0;
uint32_t sint32PtrInID = 0;
uint32_t uint32ID = 0;
uint32_t halfID = 0;
uint32_t floatID = 0;
uint32_t doubleID = 0;
struct outputIDs
{
uint32_t constID; // constant ID for the index of this output
uint32_t basetypeID; // the type ID for this output. Must be present already by definition!
uint32_t uniformPtrID; // Uniform Pointer ID for this output. Used to write the output data
};
outputIDs outs[100] = {0};
RDCASSERT(numOutputs < 100);
uint32_t maxDescSetBind = 0;
size_t decorateOffset = 0;
size_t typeVarOffset = 0;
size_t it = 5;
while(it < spirvLength)
{
uint16_t WordCount = spirv[it]>>spv::WordCountShift;
spv::Op opcode = spv::Op(spirv[it]&spv::OpCodeMask);
if(opcode == spv::OpDecorate && spirv[it+2] == spv::DecorationBuiltIn && spirv[it+3] == spv::BuiltInVertexId)
{
if(vertidxID != 0)
RDCWARN("found multiple decorated gl_VertexIDs %u %u!", spirv[it+1], vertidxID); // not sure if this is valid or not
vertidxID = spirv[it+1];
}
if(opcode == spv::OpTypeInt && spirv[it+2] == 32 && spirv[it+3] == 1)
{
if(sint32ID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], sint32ID); // not sure if this is valid or not
sint32ID = spirv[it+1];
}
if(opcode == spv::OpTypeInt && spirv[it+2] == 32 && spirv[it+3] == 0)
{
if(uint32ID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], uint32ID); // not sure if this is valid or not
uint32ID = spirv[it+1];
}
if(opcode == spv::OpTypeFloat && spirv[it+2] == 16)
{
if(halfID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], halfID); // not sure if this is valid or not
halfID = spirv[it+1];
}
if(opcode == spv::OpTypeFloat && spirv[it+2] == 32)
{
if(floatID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], floatID); // not sure if this is valid or not
floatID = spirv[it+1];
}
if(opcode == spv::OpTypeFloat && spirv[it+2] == 64)
{
if(doubleID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], doubleID); // not sure if this is valid or not
doubleID = spirv[it+1];
}
if(opcode == spv::OpTypePointer && spirv[it+2] == spv::StorageClassInput && spirv[it+3] == sint32ID)
{
if(sint32PtrInID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], sint32PtrInID); // not sure if this is valid or not
sint32PtrInID = spirv[it+1];
}
for(int i=0; i < numOutputs; i++)
{
if(opcode == spv::OpConstant && spirv[it+1] == uint32ID && spirv[it+3] == (uint32_t)i)
{
if(outs[i].constID != 0)
RDCWARN("identical constant declared with two different IDs %u %u!", spirv[it+2], outs[i].constID); // not sure if this is valid or not
outs[i].constID = spirv[it+2];
}
if(refl.OutputSig[i].compCount > 1 && opcode == spv::OpTypeVector)
{
uint32_t baseID = 0;
if(refl.OutputSig[i].compType == eCompType_UInt)
baseID = uint32ID;
else if(refl.OutputSig[i].compType == eCompType_SInt)
baseID = sint32ID;
else if(refl.OutputSig[i].compType == eCompType_Float)
baseID = floatID;
else if(refl.OutputSig[i].compType == eCompType_Double)
baseID = doubleID;
else
RDCERR("Unexpected component type for output signature element");
// if we have the base type, see if this is the right sized vector of that type
if(baseID != 0 && spirv[it+2] == baseID && spirv[it+3] == refl.OutputSig[i].compCount)
{
if(outs[i].basetypeID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], outs[i].basetypeID); // not sure if this is valid or not
outs[i].basetypeID = spirv[it+1];
}
}
// if we've found the base type, try and identify uniform pointers to that type
if(outs[i].basetypeID != 0 && opcode == spv::OpTypePointer && spirv[it+2] == spv::StorageClassUniform && spirv[it+3] == outs[i].basetypeID)
{
if(outs[i].uniformPtrID != 0)
RDCWARN("identical type declared with two different IDs %u %u!", spirv[it+1], outs[i].uniformPtrID); // not sure if this is valid or not
outs[i].uniformPtrID = spirv[it+1];
}
}
if(opcode == spv::OpDecorate && spirv[it+2] == spv::DecorationDescriptorSet)
maxDescSetBind = RDCMAX(maxDescSetBind, spirv[it+3]);
// when we reach the types, decorations are over
if(decorateOffset == 0 && opcode >= spv::OpTypeVoid && opcode <= spv::OpTypeForwardPointer)
decorateOffset = it;
// stop when we reach the functions, types are over
if(opcode == spv::OpFunction)
{
typeVarOffset = it;
break;
}
it += WordCount;
}
for(int i=0; i < numOutputs; i++)
{
// handle non-vectors once here
if(refl.OutputSig[i].compCount == 1)
{
if(refl.OutputSig[i].compType == eCompType_UInt)
outs[i].basetypeID = uint32ID;
else if(refl.OutputSig[i].compType == eCompType_SInt)
outs[i].basetypeID = sint32ID;
else if(refl.OutputSig[i].compType == eCompType_Float)
outs[i].basetypeID = floatID;
else if(refl.OutputSig[i].compType == eCompType_Double)
outs[i].basetypeID = doubleID;
else
RDCERR("Unexpected component type for output signature element");
}
// must have at least found the base type, or something has gone seriously wrong
RDCASSERT(outs[i].basetypeID != 0);
}
if(vertidxID == 0)
{
// need to declare our own "in int gl_VertexID;"
// if needed add new ID for sint32 type
if(sint32ID == 0)
{
sint32ID = idBound++;
uint32_t typeOp[] = {
MakeSPIRVOp(spv::OpTypeInt, 4),
sint32ID,
32U, // 32-bit
1U, // signed
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, typeOp, typeOp+ARRAY_COUNT(typeOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(typeOp);
}
// if needed, new ID for input ptr type
if(sint32PtrInID == 0)
{
sint32PtrInID = idBound;
idBound++;
uint32_t typeOp[] = {
MakeSPIRVOp(spv::OpTypePointer, 4),
sint32PtrInID,
spv::StorageClassInput,
sint32ID,
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, typeOp, typeOp+ARRAY_COUNT(typeOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(typeOp);
}
// new ID for vertex index
vertidxID = idBound;
idBound++;
uint32_t varOp[] = {
MakeSPIRVOp(spv::OpVariable, 4),
sint32PtrInID, // type
vertidxID, // variable id
spv::StorageClassInput,
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, varOp, varOp+ARRAY_COUNT(varOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(varOp);
uint32_t decorateOp[] = {
MakeSPIRVOp(spv::OpDecorate, 4),
vertidxID,
spv::DecorationBuiltIn,
spv::BuiltInVertexId,
};
// insert at the end of the decorations before the types
modSpirv.insert(modSpirv.begin()+decorateOffset, decorateOp, decorateOp+ARRAY_COUNT(decorateOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(decorateOp);
decorateOffset += ARRAY_COUNT(decorateOp);
}
// if needed add new ID for uint32 type
if(uint32ID == 0)
{
uint32ID = idBound++;
uint32_t typeOp[] = {
MakeSPIRVOp(spv::OpTypeInt, 4),
uint32ID,
32U, // 32-bit
0U, // unsigned
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, typeOp, typeOp+ARRAY_COUNT(typeOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(typeOp);
}
// add any constants we're missing
for(int i=0; i < numOutputs; i++)
{
if(outs[i].constID == 0)
{
outs[i].constID = idBound++;
uint32_t constantOp[] = {
MakeSPIRVOp(spv::OpConstant, 4),
uint32ID,
outs[i].constID,
(uint32_t)i,
};
// insert at the end of the types/variables/constants section
modSpirv.insert(modSpirv.begin()+typeVarOffset, constantOp, constantOp+ARRAY_COUNT(constantOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(constantOp);
}
}
// add any uniform pointer types we're missing. Note that it's quite likely
// output types will overlap (think - 5 outputs, 3 of which are float4/vec4)
// so any time we create a new uniform pointer type, we update all subsequent
// outputs to refer to it.
for(int i=0; i < numOutputs; i++)
{
if(outs[i].uniformPtrID == 0)
{
outs[i].uniformPtrID = idBound++;
uint32_t typeOp[] = {
MakeSPIRVOp(spv::OpTypePointer, 4),
outs[i].uniformPtrID,
spv::StorageClassUniform,
outs[i].basetypeID,
};
// insert at the end of the types/variables/constants section
modSpirv.insert(modSpirv.begin()+typeVarOffset, typeOp, typeOp+ARRAY_COUNT(typeOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(typeOp);
// update subsequent outputs of identical type
for(int j=i+1; j < numOutputs; j++)
{
if(outs[i].basetypeID == outs[j].basetypeID)
{
RDCASSERT(outs[j].uniformPtrID == 0);
outs[j].uniformPtrID = outs[i].uniformPtrID;
}
}
}
}
uint32_t outBufferVarID = 0;
// now add the structure type etc for our output buffer
{
uint32_t vertStructID = idBound++;
uint32_t vertStructOp[2+100] = {
MakeSPIRVOp(spv::OpTypeStruct, 2+numOutputs),
vertStructID,
};
for(int i=0; i < numOutputs; i++)
vertStructOp[2+i] = outs[i].basetypeID;
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, vertStructOp, vertStructOp+2+numOutputs);
// update offsets to account for inserted op
typeVarOffset += 2+numOutputs;
uint32_t runtimeArrayID = idBound++;
uint32_t runtimeArrayOp[] = {
MakeSPIRVOp(spv::OpTypeRuntimeArray, 3),
runtimeArrayID,
vertStructID,
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, runtimeArrayOp, runtimeArrayOp+ARRAY_COUNT(runtimeArrayOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(runtimeArrayOp);
uint32_t outputStructID = idBound++;
uint32_t outputStructOp[] = {
MakeSPIRVOp(spv::OpTypeStruct, 3),
outputStructID,
runtimeArrayID,
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, outputStructOp, outputStructOp+ARRAY_COUNT(outputStructOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(outputStructOp);
uint32_t outputStructPtrID = idBound++;
uint32_t outputStructPtrOp[] = {
MakeSPIRVOp(spv::OpTypePointer, 4),
outputStructPtrID,
spv::StorageClassUniform,
outputStructID,
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, outputStructPtrOp, outputStructPtrOp+ARRAY_COUNT(outputStructPtrOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(outputStructPtrOp);
outBufferVarID = idBound++;
uint32_t outputVarOp[] = {
MakeSPIRVOp(spv::OpVariable, 4),
outputStructPtrID,
outBufferVarID,
spv::StorageClassUniform,
};
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+typeVarOffset, outputVarOp, outputVarOp+ARRAY_COUNT(outputVarOp));
// update offsets to account for inserted op
typeVarOffset += ARRAY_COUNT(outputVarOp);
// need to add decorations as appropriate
vector<uint32_t> decorations;
// reserve room for 1 member decorate per output, plus
// other fixed decorations
decorations.reserve(5*numOutputs + 20);
uint32_t memberOffset = 0;
for(int i=0; i < numOutputs; i++)
{
decorations.push_back(MakeSPIRVOp(spv::OpMemberDecorate, 5));
decorations.push_back(vertStructID);
decorations.push_back((uint32_t)i);
decorations.push_back(spv::DecorationOffset);
decorations.push_back(memberOffset);
uint32_t elemSize = 0;
if(refl.OutputSig[i].compType == eCompType_Double)
elemSize = 8;
else if(refl.OutputSig[i].compType == eCompType_SInt ||
refl.OutputSig[i].compType == eCompType_UInt ||
refl.OutputSig[i].compType == eCompType_Float)
elemSize = 4;
else
RDCERR("Unexpected component type for output signature element");
memberOffset += elemSize*refl.OutputSig[i].compCount;
}
// the array is the only element in the output struct, so
// it's at offset 0
decorations.push_back(MakeSPIRVOp(spv::OpMemberDecorate, 5));
decorations.push_back(outputStructID);
decorations.push_back(0);
decorations.push_back(spv::DecorationOffset);
decorations.push_back(0);
// set array stride
decorations.push_back(MakeSPIRVOp(spv::OpDecorate, 4));
decorations.push_back(runtimeArrayID);
decorations.push_back(spv::DecorationArrayStride);
decorations.push_back(memberOffset);
// set object type
decorations.push_back(MakeSPIRVOp(spv::OpDecorate, 3));
decorations.push_back(outputStructID);
decorations.push_back(spv::DecorationBufferBlock);
// set binding
decorations.push_back(MakeSPIRVOp(spv::OpDecorate, 4));
decorations.push_back(outBufferVarID);
decorations.push_back(spv::DecorationDescriptorSet);
decorations.push_back(maxDescSetBind+1);
decorations.push_back(MakeSPIRVOp(spv::OpDecorate, 4));
decorations.push_back(outBufferVarID);
decorations.push_back(spv::DecorationBinding);
decorations.push_back(0);
// insert at the end of the types/variables section
modSpirv.insert(modSpirv.begin()+decorateOffset, decorations.begin(), decorations.end());
// update offsets to account for inserted op
typeVarOffset += decorations.size();
decorateOffset += decorations.size();
}
// update these values, since vector may have resized and/or reallocated above
spirv = &modSpirv[0];
spirvLength = modSpirv.size();
// patch up the new id bound
spirv[3] = idBound;
}
void VulkanDebugManager::InitPostVSBuffers(uint32_t frameID, uint32_t eventID)
{
const WrappedVulkan::PartialReplayData::StateVector &state = m_pDriver->m_PartialReplayData.state;
VulkanCreationInfo &c = m_pDriver->m_CreationInfo;
const VulkanCreationInfo::Pipeline &p = c.m_Pipeline[state.graphics.pipeline];
const VulkanCreationInfo::Shader &s = c.m_Shader[p.shaders[VK_SHADER_STAGE_VERTEX]];
const VulkanCreationInfo::ShaderModule &m = c.m_ShaderModule[s.module];
vector<uint32_t> modSpirv = m.spirv.spirv;
AddOutputDumping(s.refl, modSpirv);
RDCBREAK();
}
+2
View File
@@ -68,6 +68,8 @@ class VulkanDebugManager
ResourceId RenderOverlay(ResourceId texid, TextureDisplayOverlay overlay, uint32_t frameID, uint32_t eventID, const vector<uint32_t> &passEvents);
void InitPostVSBuffers(uint32_t frameID, uint32_t eventID);
struct GPUBuffer
{
enum CreateFlags
+1 -1
View File
@@ -3710,7 +3710,7 @@ bool VulkanReplay::GetHistogram(ResourceId texid, uint32_t sliceFace, uint32_t m
void VulkanReplay::InitPostVSBuffers(uint32_t frameID, uint32_t eventID)
{
VULKANNOTIMP("VulkanReplay::InitPostVSBuffers");
GetDebugManager()->InitPostVSBuffers(frameID, eventID);
}
vector<EventUsage> VulkanReplay::GetUsage(ResourceId id)