mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-15 02:56:50 +00:00
Improve DXIL bytecode editor
* Add helpers for creating DX op instructions with less boilerplate. * On encode, strip any unused functions or globals to comply with strict DXIL validation requirements. * Create attribute sets on demand to match functions. * Add some extra helpers for creating constants, blocks, and patching runtime chunk.
This commit is contained in:
@@ -210,7 +210,12 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
|
||||
ProgramEditor editor(dxbc, editedBlob);
|
||||
|
||||
const Type *handleType = editor.CreateNamedStructType("dx.types.Handle", {});
|
||||
const Type *i32 = editor.GetInt32Type();
|
||||
const Type *i8 = editor.GetInt8Type();
|
||||
const Type *i1 = editor.GetBoolType();
|
||||
|
||||
const Type *handleType = editor.CreateNamedStructType(
|
||||
"dx.types.Handle", {editor.CreatePointerType(i8, Type::PointerAddrSpace::Default)});
|
||||
const Function *createHandle = editor.GetFunctionByName("dx.op.createHandle");
|
||||
const Function *createHandleFromBinding =
|
||||
editor.GetFunctionByName("dx.op.createHandleFromBinding");
|
||||
@@ -225,118 +230,21 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
(isShaderModel6_6OrAbove && !createHandleFromHeap && !createHandleFromBinding))
|
||||
return false;
|
||||
|
||||
const Type *i32 = editor.GetInt32Type();
|
||||
const Type *i8 = editor.GetInt8Type();
|
||||
const Type *i1 = editor.GetBoolType();
|
||||
|
||||
// Create createHandleFromBinding we'll need to create the feedback UAV
|
||||
if(!createHandleFromBinding && isShaderModel6_6OrAbove)
|
||||
{
|
||||
const Type *resBindType = editor.CreateNamedStructType("dx.types.ResBind", {i32, i32, i32, i8});
|
||||
const Type *funcType = editor.CreateFunctionType(handleType, {i32, resBindType, i32, i1});
|
||||
|
||||
Function createHandleBaseFunction;
|
||||
createHandleBaseFunction.name = "dx.op.createHandleFromBinding";
|
||||
createHandleBaseFunction.type = funcType;
|
||||
createHandleBaseFunction.external = true;
|
||||
|
||||
for(const AttributeSet &attrs : editor.GetAttributeSets())
|
||||
{
|
||||
if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind)
|
||||
{
|
||||
createHandleBaseFunction.attrs = &attrs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!createHandleBaseFunction.attrs)
|
||||
RDCWARN("Couldn't find existing nounwind attr set");
|
||||
createHandleFromBinding = editor.DeclareFunction(createHandleBaseFunction);
|
||||
createHandleFromBinding = editor.DeclareFunction("dx.op.createHandleFromBinding", handleType,
|
||||
{i32, resBindType, i32, i1},
|
||||
Attribute::NoUnwind | Attribute::ReadNone);
|
||||
}
|
||||
|
||||
// get the functions we'll need
|
||||
Function *atomicBinOp = editor.GetFunctionByName("dx.op.atomicBinOp.i32");
|
||||
if(!atomicBinOp)
|
||||
{
|
||||
const Type *funcType = editor.CreateFunctionType(i32, {i32, handleType, i32, i32, i32, i32, i32});
|
||||
|
||||
Function atomicFunc;
|
||||
atomicFunc.name = "dx.op.atomicBinOp.i32";
|
||||
atomicFunc.type = funcType;
|
||||
atomicFunc.external = true;
|
||||
|
||||
for(const AttributeSet &attrs : editor.GetAttributeSets())
|
||||
{
|
||||
if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind)
|
||||
{
|
||||
atomicFunc.attrs = &attrs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!atomicFunc.attrs)
|
||||
RDCWARN("Couldn't find existing nounwind attr set");
|
||||
// should we add a set here? or assume the attrs don't matter because this is a builtin function
|
||||
// anyway?
|
||||
|
||||
atomicBinOp = editor.DeclareFunction(atomicFunc);
|
||||
}
|
||||
|
||||
const Function *binOp = editor.GetFunctionByName("dx.op.binary.i32");
|
||||
if(!binOp)
|
||||
{
|
||||
const Type *funcType = editor.CreateFunctionType(i32, {i32, i32, i32});
|
||||
|
||||
Function binopFunc;
|
||||
binopFunc.name = "dx.op.binary.i32";
|
||||
binopFunc.type = funcType;
|
||||
binopFunc.external = true;
|
||||
|
||||
for(const AttributeSet &attrs : editor.GetAttributeSets())
|
||||
{
|
||||
if(attrs.functionSlot &&
|
||||
attrs.functionSlot->params == (Attribute::NoUnwind | Attribute::ReadNone))
|
||||
{
|
||||
binopFunc.attrs = &attrs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we haven't implemented adding attribute sets since their encoding is obtuse, so if we can't
|
||||
// get the 'real' binop attributes try to get the next most conservative one
|
||||
if(!binopFunc.attrs)
|
||||
{
|
||||
for(const AttributeSet &attrs : editor.GetAttributeSets())
|
||||
{
|
||||
if(attrs.functionSlot &&
|
||||
attrs.functionSlot->params == (Attribute::NoUnwind | Attribute::ReadOnly))
|
||||
{
|
||||
binopFunc.attrs = &attrs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!binopFunc.attrs)
|
||||
{
|
||||
for(const AttributeSet &attrs : editor.GetAttributeSets())
|
||||
{
|
||||
if(attrs.functionSlot && attrs.functionSlot->params == Attribute::NoUnwind)
|
||||
{
|
||||
binopFunc.attrs = &attrs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!binopFunc.attrs)
|
||||
RDCWARN("Couldn't find existing nounwind readnone attr set");
|
||||
|
||||
// should we add a set here? or assume the attrs don't matter because this is a builtin function
|
||||
// anyway?
|
||||
|
||||
binOp = editor.DeclareFunction(binopFunc);
|
||||
}
|
||||
const Function *atomicBinOp = editor.DeclareFunction("dx.op.atomicBinOp.i32", i32,
|
||||
{i32, handleType, i32, i32, i32, i32, i32},
|
||||
Attribute::NoUnwind | Attribute::ReadNone);
|
||||
const Function *binOp = editor.DeclareFunction("dx.op.binary.i32", i32, {i32, i32, i32},
|
||||
Attribute::NoUnwind | Attribute::ReadNone);
|
||||
|
||||
// while we're iterating through the metadata to add our UAV, we'll also note the shader-local
|
||||
// register IDs of each SRV/UAV with slots, and record the base slot in this array for easy access
|
||||
@@ -344,11 +252,6 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
// the index dxc provides is register-relative
|
||||
rdcarray<rdcpair<uint32_t, int32_t>> srvBaseSlots, uavBaseSlots;
|
||||
|
||||
// when we add metadata we do it in reverse order since it's unclear if we're supposed to have
|
||||
// forward references (LLVM seems to handle it, but not emit it, so it's very much a grey area)
|
||||
// we do this by recreating any nodes that we modify (or their parents recursively up to the named
|
||||
// metadata)
|
||||
|
||||
// declare the resource, this happens purely in metadata but we need to store the slot
|
||||
uint32_t regSlot = 0;
|
||||
Metadata *reslist = NULL;
|
||||
@@ -603,22 +506,19 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
if(createHandle)
|
||||
{
|
||||
RDCASSERT(!isShaderModel6_6OrAbove);
|
||||
handle = editor.CreateInstruction(createHandle);
|
||||
handle->type = handleType;
|
||||
handle->args = {
|
||||
// dx.op.createHandle opcode
|
||||
editor.CreateConstant(57U),
|
||||
// kind = UAV
|
||||
editor.CreateConstant((uint8_t)HandleKind::UAV),
|
||||
// ID/slot
|
||||
editor.CreateConstant(regSlot),
|
||||
// array index
|
||||
editor.CreateConstant(0U),
|
||||
// non-uniform
|
||||
editor.CreateConstant(false),
|
||||
};
|
||||
|
||||
f->instructions.insert(startInst++, handle);
|
||||
handle = editor.InsertInstruction(
|
||||
f, startInst++,
|
||||
editor.CreateInstruction(createHandle, DXOp::createHandle,
|
||||
{
|
||||
// kind = UAV
|
||||
editor.CreateConstant((uint8_t)HandleKind::UAV),
|
||||
// ID/slot
|
||||
editor.CreateConstant(regSlot),
|
||||
// array index
|
||||
editor.CreateConstant(0U),
|
||||
// non-uniform
|
||||
editor.CreateConstant(false),
|
||||
}));
|
||||
}
|
||||
else if(createHandleFromBinding)
|
||||
{
|
||||
@@ -636,74 +536,56 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
editor.CreateConstant((uint8_t)HandleKind::UAV),
|
||||
});
|
||||
|
||||
Instruction *handleCreate = editor.CreateInstruction(createHandleFromBinding);
|
||||
handleCreate->type = handleType;
|
||||
handleCreate->args = {
|
||||
// dx.op.createHandleFromBinding opcode
|
||||
editor.CreateConstant(217U),
|
||||
// resBind
|
||||
resBindConstant,
|
||||
// ID/slot
|
||||
editor.CreateConstant(0U),
|
||||
// non-uniform
|
||||
editor.CreateConstant(false),
|
||||
};
|
||||
Instruction *handleCreate = editor.InsertInstruction(
|
||||
f, startInst++,
|
||||
editor.CreateInstruction(createHandleFromBinding, DXOp::createHandleFromBinding,
|
||||
{
|
||||
// resBind
|
||||
resBindConstant,
|
||||
// ID/slot
|
||||
editor.CreateConstant(0U),
|
||||
// non-uniform
|
||||
editor.CreateConstant(false),
|
||||
}));
|
||||
|
||||
f->instructions.insert(startInst++, handleCreate);
|
||||
|
||||
// Annotate handle
|
||||
handle = editor.CreateInstruction(editor.GetFunctionByName("dx.op.annotateHandle"));
|
||||
handle->type = handleType;
|
||||
handle->args = {
|
||||
// dx.op.annotateHandle opcode
|
||||
editor.CreateConstant(216U),
|
||||
// Resource handle
|
||||
handleCreate,
|
||||
// Resource properties
|
||||
editor.CreateConstant(
|
||||
editor.CreateNamedStructType("dx.types.ResourceProperties", {}),
|
||||
handle = editor.InsertInstruction(
|
||||
f, startInst++,
|
||||
editor.CreateInstruction(
|
||||
annotateHandle, DXOp::annotateHandle,
|
||||
{
|
||||
// IsUav : (1 << 12)
|
||||
editor.CreateConstant(uint32_t((1 << 12) | (uint32_t)ResourceKind::RawBuffer)),
|
||||
//
|
||||
editor.CreateConstant(0U),
|
||||
}),
|
||||
};
|
||||
|
||||
f->instructions.insert(startInst++, handle);
|
||||
// Resource handle
|
||||
handleCreate,
|
||||
// Resource properties
|
||||
editor.CreateConstant(
|
||||
editor.CreateNamedStructType("dx.types.ResourceProperties", {}),
|
||||
{
|
||||
// IsUav : (1 << 12)
|
||||
editor.CreateConstant(uint32_t((1 << 12) | (uint32_t)ResourceKind::RawBuffer)),
|
||||
//
|
||||
editor.CreateConstant(0U),
|
||||
}),
|
||||
}));
|
||||
}
|
||||
|
||||
Constant *undefi32;
|
||||
{
|
||||
Constant c;
|
||||
c.type = i32;
|
||||
c.setUndef(true);
|
||||
undefi32 = editor.CreateConstant(c);
|
||||
}
|
||||
Constant *undefi32 = editor.CreateUndef(i32);
|
||||
|
||||
// insert an OR to offset 0, just to indicate validity
|
||||
{
|
||||
Instruction *inst = editor.CreateInstruction(atomicBinOp);
|
||||
inst->type = i32;
|
||||
inst->args = {
|
||||
// dx.op.atomicBinOp.i32 opcode
|
||||
editor.CreateConstant(78U),
|
||||
// feedback UAV handle
|
||||
handle,
|
||||
// operation OR
|
||||
editor.CreateConstant(2U),
|
||||
// offset
|
||||
editor.CreateConstant(0U),
|
||||
// offset 2
|
||||
undefi32,
|
||||
// offset 3
|
||||
undefi32,
|
||||
// value
|
||||
editor.CreateConstant(magicFeedbackValue),
|
||||
};
|
||||
|
||||
f->instructions.insert(startInst++, inst);
|
||||
}
|
||||
editor.InsertInstruction(f, startInst++,
|
||||
editor.CreateInstruction(atomicBinOp, DXOp::atomicBinOp,
|
||||
{
|
||||
// feedback UAV handle
|
||||
handle,
|
||||
// operation OR
|
||||
editor.CreateConstant(2U),
|
||||
// offset
|
||||
editor.CreateConstant(0U),
|
||||
// offset 2
|
||||
undefi32,
|
||||
// offset 3
|
||||
undefi32,
|
||||
// value
|
||||
editor.CreateConstant(magicFeedbackValue),
|
||||
}));
|
||||
|
||||
for(size_t i = startInst; i < f->instructions.size(); i++)
|
||||
{
|
||||
@@ -800,72 +682,64 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
if(slotInfo.first == 0)
|
||||
continue;
|
||||
|
||||
Instruction op;
|
||||
op.op = Operation::Sub;
|
||||
|
||||
// idx0Based = idx - baseReg
|
||||
Instruction *idx0Based = editor.CreateInstruction(Operation::Sub);
|
||||
f->instructions.insert(i++, idx0Based);
|
||||
idx0Based->type = i32;
|
||||
idx0Based->args = {
|
||||
// idx to the createHandle op
|
||||
idxArg,
|
||||
// register that this is relative to
|
||||
editor.CreateConstant((uint32_t)slotInfo.second),
|
||||
};
|
||||
Instruction *idx0Based = editor.InsertInstruction(
|
||||
f, i++,
|
||||
editor.CreateInstruction(Operation::Sub, i32,
|
||||
{
|
||||
// idx to the createHandle op
|
||||
idxArg,
|
||||
// register that this is relative to
|
||||
editor.CreateConstant((uint32_t)slotInfo.second),
|
||||
}));
|
||||
|
||||
// slotPlusBase = idx0Based + slot
|
||||
Instruction *slotPlusBase = editor.CreateInstruction(Operation::Add);
|
||||
f->instructions.insert(i++, slotPlusBase);
|
||||
slotPlusBase->type = i32;
|
||||
slotPlusBase->args = {
|
||||
// idx to the createHandle op
|
||||
idx0Based,
|
||||
// base slot
|
||||
editor.CreateConstant(slotInfo.first),
|
||||
};
|
||||
Instruction *slotPlusBase = editor.InsertInstruction(
|
||||
f, i++,
|
||||
editor.CreateInstruction(Operation::Add, i32,
|
||||
{
|
||||
// idx to the createHandle op
|
||||
idx0Based,
|
||||
// base slot
|
||||
editor.CreateConstant(slotInfo.first),
|
||||
}));
|
||||
|
||||
// slotPlusBaseClamped = min(slotPlusBase, maxSlot)
|
||||
Instruction *slotPlusBaseClamped = editor.CreateInstruction(binOp);
|
||||
f->instructions.insert(i++, slotPlusBaseClamped);
|
||||
slotPlusBaseClamped->type = i32;
|
||||
slotPlusBaseClamped->args = {
|
||||
// dx.op.binOp.i32 UMin opcode
|
||||
editor.CreateConstant(40U),
|
||||
// slotPlusBase
|
||||
slotPlusBase,
|
||||
// max slot
|
||||
editor.CreateConstant(maxSlot),
|
||||
};
|
||||
Instruction *slotPlusBaseClamped =
|
||||
editor.InsertInstruction(f, i++,
|
||||
editor.CreateInstruction(binOp, DXOp::UMin,
|
||||
{
|
||||
// slotPlusBase
|
||||
slotPlusBase,
|
||||
// max slot
|
||||
editor.CreateConstant(maxSlot),
|
||||
}));
|
||||
|
||||
// byteOffset = slotPlusBaseClamped << 2
|
||||
Instruction *byteOffset = editor.CreateInstruction(Operation::ShiftLeft);
|
||||
f->instructions.insert(i++, byteOffset);
|
||||
byteOffset->type = i32;
|
||||
byteOffset->args = {
|
||||
slotPlusBaseClamped,
|
||||
editor.CreateConstant(2U),
|
||||
};
|
||||
Instruction *byteOffset =
|
||||
editor.InsertInstruction(f, i++,
|
||||
editor.CreateInstruction(Operation::ShiftLeft, i32,
|
||||
{
|
||||
slotPlusBaseClamped,
|
||||
editor.CreateConstant(2U),
|
||||
}));
|
||||
|
||||
Instruction *atomicOr = editor.CreateInstruction(atomicBinOp);
|
||||
f->instructions.insert(i++, atomicOr);
|
||||
atomicOr->args = {
|
||||
// dx.op.atomicBinOp.i32 opcode
|
||||
editor.CreateConstant(78U),
|
||||
// feedback UAV handle
|
||||
handle,
|
||||
// operation OR
|
||||
editor.CreateConstant(2U),
|
||||
// offset
|
||||
byteOffset,
|
||||
// offset 2
|
||||
undefi32,
|
||||
// offset 3
|
||||
undefi32,
|
||||
// value
|
||||
editor.CreateConstant(magicFeedbackValue),
|
||||
};
|
||||
atomicOr->type = i32;
|
||||
editor.InsertInstruction(f, i++,
|
||||
editor.CreateInstruction(atomicBinOp, DXOp::atomicBinOp,
|
||||
{
|
||||
// feedback UAV handle
|
||||
handle,
|
||||
// operation OR
|
||||
editor.CreateConstant(2U),
|
||||
// offset
|
||||
byteOffset,
|
||||
// offset 2
|
||||
undefi32,
|
||||
// offset 3
|
||||
undefi32,
|
||||
// value
|
||||
editor.CreateConstant(magicFeedbackValue),
|
||||
}));
|
||||
}
|
||||
else if(inst.op == Operation::Call && createHandleFromHeap &&
|
||||
inst.getFuncCall()->name == createHandleFromHeap->name)
|
||||
@@ -963,45 +837,53 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space,
|
||||
Instruction op;
|
||||
|
||||
// slotPlusBase = idx0Based + slot
|
||||
Instruction *slotPlusBase = editor.CreateInstruction(Operation::Add);
|
||||
f->instructions.insert(i++, slotPlusBase);
|
||||
slotPlusBase->type = i32;
|
||||
slotPlusBase->args = {
|
||||
// idx to the createHandleFromHeap op
|
||||
idxArg,
|
||||
// base slot
|
||||
editor.CreateConstant(it->second.Slot()),
|
||||
};
|
||||
Instruction *slotPlusBase = editor.InsertInstruction(
|
||||
f, i++,
|
||||
editor.CreateInstruction(Operation::Add, i32,
|
||||
{
|
||||
// idx to the createHandleFromHeap op
|
||||
idxArg,
|
||||
// base slot
|
||||
editor.CreateConstant(it->second.Slot()),
|
||||
}));
|
||||
|
||||
// slotPlusBaseClamped = min(slotPlusBase, maxSlot)
|
||||
Instruction *slotPlusBaseClamped =
|
||||
editor.InsertInstruction(f, i++,
|
||||
editor.CreateInstruction(binOp, DXOp::UMin,
|
||||
{
|
||||
// slotPlusBase
|
||||
slotPlusBase,
|
||||
// max slot
|
||||
editor.CreateConstant(maxSlot),
|
||||
}));
|
||||
|
||||
// byteOffset = slotPlusBase << 2
|
||||
Instruction *byteOffset = editor.CreateInstruction(Operation::ShiftLeft);
|
||||
f->instructions.insert(i++, byteOffset);
|
||||
byteOffset->type = i32;
|
||||
byteOffset->args = {
|
||||
slotPlusBase,
|
||||
editor.CreateConstant(2U),
|
||||
};
|
||||
Instruction *byteOffset =
|
||||
editor.InsertInstruction(f, i++,
|
||||
editor.CreateInstruction(Operation::ShiftLeft, i32,
|
||||
{
|
||||
slotPlusBaseClamped,
|
||||
editor.CreateConstant(2U),
|
||||
}));
|
||||
|
||||
uint32_t feedbackValue = magicFeedbackValue | (1 << (uint32_t)handleKind);
|
||||
Instruction *atomicOr = editor.CreateInstruction(atomicBinOp);
|
||||
f->instructions.insert(i++, atomicOr);
|
||||
atomicOr->args = {
|
||||
// dx.op.atomicBinOp.i32 opcode
|
||||
editor.CreateConstant(78U),
|
||||
// feedback UAV handle
|
||||
handle,
|
||||
// operation OR
|
||||
editor.CreateConstant(2U),
|
||||
// offset
|
||||
byteOffset,
|
||||
// offset 2
|
||||
undefi32,
|
||||
// offset 3
|
||||
undefi32,
|
||||
// value
|
||||
editor.CreateConstant(feedbackValue),
|
||||
};
|
||||
atomicOr->type = i32;
|
||||
editor.InsertInstruction(f, i++,
|
||||
editor.CreateInstruction(atomicBinOp, DXOp::atomicBinOp,
|
||||
{
|
||||
// feedback UAV handle
|
||||
handle,
|
||||
// operation OR
|
||||
editor.CreateConstant(2U),
|
||||
// offset
|
||||
byteOffset,
|
||||
// offset 2
|
||||
undefi32,
|
||||
// offset 3
|
||||
undefi32,
|
||||
// value
|
||||
editor.CreateConstant(feedbackValue),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1120,14 +1002,12 @@ static bool AddArraySlots(WrappedID3D12PipelineState::ShaderEntry *shad, uint32_
|
||||
{
|
||||
if(AnnotateDXILShader(shad->GetDXBC(), space, slots, numSlots, editedBlob))
|
||||
{
|
||||
// strip ILDB because it's valid code (with debug info) and who knows what might use it
|
||||
DXBC::DXBCContainer::StripChunk(editedBlob, DXBC::FOURCC_ILDB);
|
||||
|
||||
if(!D3D12_Debug_FeedbackDumpDirPath().empty())
|
||||
{
|
||||
bytebuf orig = shad->GetDXBC()->GetShaderBlob();
|
||||
|
||||
DXBC::DXBCContainer::StripChunk(orig, DXBC::FOURCC_ILDB);
|
||||
DXBC::DXBCContainer::StripChunk(orig, DXBC::FOURCC_STAT);
|
||||
|
||||
FileIO::WriteAll(D3D12_Debug_FeedbackDumpDirPath() + "/before_dxil_" +
|
||||
ToStr(shad->GetDetails().stage).c_str() + ".dxbc",
|
||||
|
||||
@@ -490,7 +490,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
|
||||
RDCASSERTMSG("Linkage is non-default and not internal", rootchild.ops[3] == 0,
|
||||
rootchild.ops[3]);
|
||||
if(rootchild.ops[4] > 0 && rootchild.ops[4] - 1 < m_AttributeSets.size())
|
||||
f->attrs = &m_AttributeSets[(size_t)rootchild.ops[4] - 1];
|
||||
f->attrs = m_AttributeSets[(size_t)rootchild.ops[4] - 1];
|
||||
|
||||
f->align = rootchild.ops[5];
|
||||
|
||||
@@ -565,10 +565,10 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
|
||||
continue;
|
||||
}
|
||||
|
||||
AttributeGroup group;
|
||||
AttributeGroup *group = alloc.alloc<AttributeGroup>();
|
||||
|
||||
size_t id = (size_t)attrgroup.ops[0];
|
||||
group.slotIndex = (uint32_t)attrgroup.ops[1];
|
||||
group->slotIndex = (uint32_t)attrgroup.ops[1];
|
||||
|
||||
for(size_t i = 2; i < attrgroup.ops.size(); i++)
|
||||
{
|
||||
@@ -576,7 +576,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
group.params |= Attribute(1ULL << (attrgroup.ops[i + 1]));
|
||||
group->params |= Attribute(1ULL << (attrgroup.ops[i + 1]));
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
@@ -584,13 +584,13 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
|
||||
{
|
||||
uint64_t param = attrgroup.ops[i + 2];
|
||||
Attribute attr = Attribute(1ULL << attrgroup.ops[i + 1]);
|
||||
group.params |= attr;
|
||||
group->params |= attr;
|
||||
switch(attr)
|
||||
{
|
||||
case Attribute::Alignment: group.align = param; break;
|
||||
case Attribute::StackAlignment: group.stackAlign = param; break;
|
||||
case Attribute::Dereferenceable: group.derefBytes = param; break;
|
||||
case Attribute::DereferenceableOrNull: group.derefOrNullBytes = param; break;
|
||||
case Attribute::Alignment: group->align = param; break;
|
||||
case Attribute::StackAlignment: group->stackAlign = param; break;
|
||||
case Attribute::Dereferenceable: group->derefBytes = param; break;
|
||||
case Attribute::DereferenceableOrNull: group->derefOrNullBytes = param; break;
|
||||
default: RDCERR("Unexpected attribute %llu with parameter", attr);
|
||||
}
|
||||
i += 2;
|
||||
@@ -614,7 +614,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
|
||||
i += a.size() + 1;
|
||||
}
|
||||
|
||||
group.strs.push_back({a, b});
|
||||
group->strs.push_back({a, b});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -640,24 +640,24 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
|
||||
continue;
|
||||
}
|
||||
|
||||
AttributeSet attrs;
|
||||
AttributeSet *attrs = alloc.alloc<AttributeSet>();
|
||||
|
||||
attrs.orderedGroups = paramattr.ops;
|
||||
attrs->orderedGroups = paramattr.ops;
|
||||
|
||||
for(uint64_t g : paramattr.ops)
|
||||
{
|
||||
if(g < m_AttributeGroups.size())
|
||||
{
|
||||
const AttributeGroup &group = m_AttributeGroups[(size_t)g];
|
||||
if(group.slotIndex == AttributeGroup::FunctionSlot)
|
||||
const AttributeGroup *group = m_AttributeGroups[(size_t)g];
|
||||
if(group->slotIndex == AttributeGroup::FunctionSlot)
|
||||
{
|
||||
RDCASSERT(attrs.functionSlot == NULL);
|
||||
attrs.functionSlot = &group;
|
||||
RDCASSERT(attrs->functionSlot == NULL);
|
||||
attrs->functionSlot = group;
|
||||
}
|
||||
else
|
||||
{
|
||||
attrs.groupSlots.resize_for_index(group.slotIndex);
|
||||
attrs.groupSlots[group.slotIndex] = &m_AttributeGroups[(size_t)g];
|
||||
attrs->groupSlots.resize_for_index(group->slotIndex);
|
||||
attrs->groupSlots[group->slotIndex] = group;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1249,7 +1249,7 @@ Program::Program(const byte *bytes, size_t length) : alloc(32 * 1024)
|
||||
inst->type = funcCall->type->inner;
|
||||
inst->opFlags() = flags;
|
||||
if(paramAttrs > 0)
|
||||
inst->extra(alloc).paramAttrs = &m_AttributeSets[paramAttrs - 1];
|
||||
inst->extra(alloc).paramAttrs = m_AttributeSets[paramAttrs - 1];
|
||||
|
||||
if(funcCallType)
|
||||
{
|
||||
@@ -2298,28 +2298,26 @@ Metadata::~Metadata()
|
||||
SAFE_DELETE(debugLoc);
|
||||
}
|
||||
|
||||
static const uint32_t unvisitedValueId = Value::NoID - 0x1;
|
||||
static const uint32_t visitedValueId = Value::NoID - 0x2;
|
||||
static const uint16_t unvisitedTypeId = 0xffff;
|
||||
|
||||
void LLVMOrderAccumulator::reset(GlobalVar *g)
|
||||
{
|
||||
g->id = unvisitedValueId;
|
||||
g->id = Value::UnvisitedID;
|
||||
reset((Constant *)g->initialiser);
|
||||
}
|
||||
|
||||
void LLVMOrderAccumulator::reset(Alias *a)
|
||||
{
|
||||
a->id = unvisitedValueId;
|
||||
a->id = Value::UnvisitedID;
|
||||
reset(a->val);
|
||||
}
|
||||
|
||||
void LLVMOrderAccumulator::reset(Constant *c)
|
||||
{
|
||||
if(!c || c->id == unvisitedValueId)
|
||||
if(!c || c->id == Value::UnvisitedID)
|
||||
return;
|
||||
|
||||
c->id = unvisitedValueId;
|
||||
c->id = Value::UnvisitedID;
|
||||
c->refCount = 0;
|
||||
if(c->isCast())
|
||||
{
|
||||
@@ -2334,14 +2332,14 @@ void LLVMOrderAccumulator::reset(Constant *c)
|
||||
|
||||
void LLVMOrderAccumulator::reset(Block *b)
|
||||
{
|
||||
b->id = unvisitedValueId;
|
||||
b->id = Value::UnvisitedID;
|
||||
}
|
||||
|
||||
void LLVMOrderAccumulator::reset(Metadata *m)
|
||||
{
|
||||
if(!m || m->id == unvisitedValueId)
|
||||
if(!m || m->id == Value::UnvisitedID)
|
||||
return;
|
||||
m->id = unvisitedValueId;
|
||||
m->id = Value::UnvisitedID;
|
||||
|
||||
reset(m->value);
|
||||
|
||||
@@ -2351,10 +2349,10 @@ void LLVMOrderAccumulator::reset(Metadata *m)
|
||||
|
||||
void LLVMOrderAccumulator::reset(Instruction *i)
|
||||
{
|
||||
if(!i || i->id == unvisitedValueId)
|
||||
if(!i || i->id == Value::UnvisitedID)
|
||||
return;
|
||||
|
||||
i->id = unvisitedValueId;
|
||||
i->id = Value::UnvisitedID;
|
||||
|
||||
for(Value *a : i->args)
|
||||
reset(a);
|
||||
@@ -2365,7 +2363,7 @@ void LLVMOrderAccumulator::reset(Instruction *i)
|
||||
|
||||
void LLVMOrderAccumulator::reset(Function *f)
|
||||
{
|
||||
f->id = unvisitedValueId;
|
||||
f->id = Value::UnvisitedID;
|
||||
for(Instruction *i : f->args)
|
||||
reset(i);
|
||||
for(Instruction *i : f->instructions)
|
||||
@@ -2394,7 +2392,7 @@ void LLVMOrderAccumulator::reset(Value *v)
|
||||
reset(a);
|
||||
}
|
||||
|
||||
void LLVMOrderAccumulator::processGlobals(Program *prog)
|
||||
void LLVMOrderAccumulator::processGlobals(Program *prog, bool doLiveChecking)
|
||||
{
|
||||
// reset all IDs, so we know if we're encountering a new value/metadata or not when walking
|
||||
for(Type *t : prog->m_Types)
|
||||
@@ -2408,6 +2406,8 @@ void LLVMOrderAccumulator::processGlobals(Program *prog)
|
||||
for(Function *f : prog->m_Functions)
|
||||
reset(f);
|
||||
|
||||
liveChecking = doLiveChecking;
|
||||
|
||||
// just for extra fun, the search order for types for printing, and types enumerated while getting
|
||||
// values is slightly different! yay yay yay!
|
||||
for(const GlobalVar *g : prog->m_GlobalVars)
|
||||
@@ -2454,29 +2454,35 @@ void LLVMOrderAccumulator::processGlobals(Program *prog)
|
||||
accumulateTypePrintOrder(visited, meta);
|
||||
}
|
||||
|
||||
for(const GlobalVar *g : prog->m_GlobalVars)
|
||||
accumulate(g);
|
||||
|
||||
for(const Function *f : prog->m_Functions)
|
||||
if(!liveChecking)
|
||||
{
|
||||
accumulate(f);
|
||||
assignTypeId(prog->GetPointerType(f->type, Type::PointerAddrSpace::Default));
|
||||
}
|
||||
for(const GlobalVar *g : prog->m_GlobalVars)
|
||||
accumulate(g);
|
||||
|
||||
for(const Alias *a : prog->m_Aliases)
|
||||
accumulate(a);
|
||||
for(const Function *f : prog->m_Functions)
|
||||
{
|
||||
accumulate(f);
|
||||
assignTypeId(prog->GetPointerType(f->type, Type::PointerAddrSpace::Default));
|
||||
}
|
||||
|
||||
for(const Alias *a : prog->m_Aliases)
|
||||
accumulate(a);
|
||||
}
|
||||
|
||||
firstConst = values.size();
|
||||
|
||||
for(const GlobalVar *g : prog->m_GlobalVars)
|
||||
if(g->initialiser)
|
||||
accumulate(g->initialiser);
|
||||
if(!liveChecking)
|
||||
{
|
||||
for(const GlobalVar *g : prog->m_GlobalVars)
|
||||
if(g->initialiser)
|
||||
accumulate(g->initialiser);
|
||||
|
||||
for(const Alias *a : prog->m_Aliases)
|
||||
accumulate(a->val);
|
||||
for(const Alias *a : prog->m_Aliases)
|
||||
accumulate(a->val);
|
||||
|
||||
for(const Value *v : prog->m_ValueSymtabOrder)
|
||||
accumulate(v);
|
||||
for(const Value *v : prog->m_ValueSymtabOrder)
|
||||
accumulate(v);
|
||||
}
|
||||
|
||||
assignTypeId(prog->m_MetaType);
|
||||
|
||||
@@ -2513,7 +2519,10 @@ void LLVMOrderAccumulator::processGlobals(Program *prog)
|
||||
}
|
||||
|
||||
numConsts = values.size() - firstConst;
|
||||
sortConsts = !prog->m_Uselists;
|
||||
// don't skip constants when doing live checking, because then constants won't be contiguous as
|
||||
// globals referenced later will be pulled into values later. When skipping globals we only care
|
||||
// if they are seen at all (and given a value id)
|
||||
sortConsts = !prog->m_Uselists && !liveChecking;
|
||||
|
||||
if(sortConsts)
|
||||
{
|
||||
@@ -2557,6 +2566,7 @@ void LLVMOrderAccumulator::processFunction(Function *f)
|
||||
{
|
||||
for(size_t a = 0; a < inst->args.size(); a++)
|
||||
accumulate(cast<Constant>(inst->args[a]));
|
||||
accumulate(inst->getFuncCall());
|
||||
}
|
||||
|
||||
numFuncConsts = values.size() - firstFuncConst;
|
||||
@@ -2608,7 +2618,7 @@ void LLVMOrderAccumulator::processFunction(Function *f)
|
||||
accumulate(inst->getAttachedMeta()[m].second);
|
||||
|
||||
for(size_t a = 0; a < inst->args.size(); a++)
|
||||
if(inst->args[a]->kind() == ValueKind::Constant)
|
||||
if(inst->args[a]->kind() == ValueKind::Constant || liveChecking)
|
||||
accumulate(inst->args[a]);
|
||||
|
||||
if(inst->type->isVoid())
|
||||
@@ -2724,7 +2734,7 @@ void LLVMOrderAccumulator::assignTypeId(const Constant *c)
|
||||
void LLVMOrderAccumulator::accumulate(const Value *v)
|
||||
{
|
||||
Value *value = (Value *)v;
|
||||
if(!v || v->id != unvisitedValueId)
|
||||
if(!v || v->id != Value::UnvisitedID)
|
||||
{
|
||||
Constant *c = cast<Constant>(value);
|
||||
if(c)
|
||||
@@ -2736,7 +2746,7 @@ void LLVMOrderAccumulator::accumulate(const Value *v)
|
||||
|
||||
assignTypeId(value->type);
|
||||
|
||||
value->id = visitedValueId;
|
||||
value->id = Value::VisitedID;
|
||||
|
||||
if(Constant *c = cast<Constant>(value))
|
||||
{
|
||||
@@ -2759,11 +2769,11 @@ void LLVMOrderAccumulator::accumulate(const Value *v)
|
||||
|
||||
void LLVMOrderAccumulator::accumulate(const Metadata *m)
|
||||
{
|
||||
if(!m || m->id != unvisitedValueId)
|
||||
if(!m || m->id != Value::UnvisitedID)
|
||||
return;
|
||||
|
||||
Metadata *meta = (Metadata *)m;
|
||||
meta->id = visitedValueId;
|
||||
meta->id = Value::VisitedID;
|
||||
|
||||
for(const Metadata *c : m->children)
|
||||
if(c)
|
||||
|
||||
@@ -307,6 +307,28 @@ enum class Operation : uint8_t
|
||||
AtomicUMin,
|
||||
};
|
||||
|
||||
// added as needed, since names in docs/LLVM don't match neatly so there's no pre-made list
|
||||
enum class DXOp : uint32_t
|
||||
{
|
||||
UMin = 40,
|
||||
createHandle = 57,
|
||||
atomicBinOp = 78,
|
||||
barrier = 80,
|
||||
groupId = 94,
|
||||
threadIdInGroup = 95,
|
||||
flattenedThreadIdInGroup = 96,
|
||||
rawBufferLoad = 139,
|
||||
rawBufferStore = 140,
|
||||
setMeshOutputCounts = 168,
|
||||
emitIndices = 169,
|
||||
getMeshPayload = 170,
|
||||
storeVertexOutput = 171,
|
||||
storePrimitiveOutput = 172,
|
||||
dispatchMesh = 173,
|
||||
annotateHandle = 216,
|
||||
createHandleFromBinding = 217,
|
||||
};
|
||||
|
||||
inline Operation DecodeBinOp(const Type *type, uint64_t opcode)
|
||||
{
|
||||
bool isFloatOp = (type->scalarType == Type::Float);
|
||||
@@ -431,6 +453,9 @@ struct Value
|
||||
// this ID is very close but different to the number displayed in disassembly. This ID is only
|
||||
// used internally for encoding
|
||||
static constexpr uint32_t NoID = 0x00ffffff;
|
||||
// these IDs are used during enumeration to count values which we have or haven't seen before
|
||||
static constexpr uint32_t UnvisitedID = 0x00fffffe;
|
||||
static constexpr uint32_t VisitedID = 0x00fffffd;
|
||||
uint32_t id : 24;
|
||||
|
||||
rdcstr toString(bool withType = false) const;
|
||||
@@ -1167,8 +1192,8 @@ protected:
|
||||
const Type *m_MetaType = NULL;
|
||||
const Type *m_LabelType = NULL;
|
||||
|
||||
rdcarray<AttributeGroup> m_AttributeGroups;
|
||||
rdcarray<AttributeSet> m_AttributeSets;
|
||||
rdcarray<AttributeGroup *> m_AttributeGroups;
|
||||
rdcarray<AttributeSet *> m_AttributeSets;
|
||||
|
||||
rdcarray<NamedMetadata *> m_NamedMeta;
|
||||
|
||||
@@ -1203,7 +1228,7 @@ public:
|
||||
size_t firstConst;
|
||||
size_t numConsts;
|
||||
|
||||
void processGlobals(Program *p);
|
||||
void processGlobals(Program *p, bool doLiveChecking);
|
||||
|
||||
size_t firstFuncConst;
|
||||
size_t numFuncConsts;
|
||||
@@ -1214,6 +1239,7 @@ public:
|
||||
private:
|
||||
size_t functionWaterMark;
|
||||
bool sortConsts = true;
|
||||
bool liveChecking = false;
|
||||
|
||||
void reset(GlobalVar *g);
|
||||
void reset(Alias *a);
|
||||
|
||||
@@ -84,7 +84,7 @@ ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outB
|
||||
// potential cycles that llvm puts in :(
|
||||
|
||||
LLVMOrderAccumulator accum;
|
||||
accum.processGlobals(this);
|
||||
accum.processGlobals(this, false);
|
||||
|
||||
for(size_t idx = accum.firstConst; idx < accum.firstConst + accum.numConsts; idx++)
|
||||
m_Constants.push_back((Constant *)cast<const Constant>(accum.values[idx]));
|
||||
@@ -102,9 +102,45 @@ ProgramEditor::ProgramEditor(const DXBC::DXBCContainer *container, bytebuf &outB
|
||||
|
||||
ProgramEditor::~ProgramEditor()
|
||||
{
|
||||
LLVMOrderAccumulator accum;
|
||||
accum.processGlobals(this, true);
|
||||
|
||||
// delete any functions that aren't referenced by call instructions
|
||||
rdcarray<const Function *> keep;
|
||||
for(Function *f : m_Functions)
|
||||
{
|
||||
accum.processFunction(f);
|
||||
accum.exitFunction();
|
||||
}
|
||||
|
||||
RDCCOMPILE_ASSERT(Value::VisitedID < Value::UnvisitedID && Value::UnvisitedID < Value::NoID,
|
||||
"ID constants should be ordered");
|
||||
|
||||
m_Functions.removeIf(
|
||||
[&keep](Function *f) { return f->instructions.empty() && f->id >= Value::UnvisitedID; });
|
||||
|
||||
// delete any globals that aren't referenced
|
||||
m_GlobalVars.removeIf([&accum](GlobalVar *var) { return var->id >= Value::UnvisitedID; });
|
||||
|
||||
m_ValueSymtabOrder.removeIf([this](Value *v) {
|
||||
if(v->kind() == ValueKind::Function && !m_Functions.contains(cast<Function>(v)))
|
||||
return true;
|
||||
|
||||
if(v->kind() == ValueKind::GlobalVar && !m_GlobalVars.contains(cast<GlobalVar>(v)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// replace the DXIL bytecode in the container with
|
||||
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_DXIL, EncodeProgram());
|
||||
|
||||
// strip ILDB because it's valid code (with debug info) and who knows what might use it
|
||||
DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_ILDB);
|
||||
|
||||
// also strip STAT because it might have stale reflection info
|
||||
DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_STAT);
|
||||
|
||||
#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
|
||||
@@ -205,6 +241,36 @@ Type *ProgramEditor::CreateNewType()
|
||||
return m_Types.back();
|
||||
}
|
||||
|
||||
const AttributeSet *ProgramEditor::GetAttributeSet(Attribute desiredAttrs)
|
||||
{
|
||||
for(const AttributeSet *attrs : m_AttributeSets)
|
||||
if(attrs && attrs->functionSlot && attrs->functionSlot->params == desiredAttrs)
|
||||
return attrs;
|
||||
|
||||
m_AttributeGroups.push_back(alloc.alloc<AttributeGroup>());
|
||||
m_AttributeGroups.back()->slotIndex = AttributeGroup::FunctionSlot;
|
||||
m_AttributeGroups.back()->params = desiredAttrs;
|
||||
|
||||
m_AttributeSets.push_back(alloc.alloc<AttributeSet>());
|
||||
m_AttributeSets.back()->functionSlot = m_AttributeGroups.back();
|
||||
m_AttributeSets.back()->orderedGroups = {m_AttributeGroups.size() - 1};
|
||||
|
||||
return m_AttributeSets.back();
|
||||
}
|
||||
|
||||
Type *ProgramEditor::CreateScalarType(Type::ScalarKind scalarType, uint32_t bitWidth)
|
||||
{
|
||||
for(size_t i = 0; i < m_Types.size(); i++)
|
||||
if(m_Types[i]->scalarType == scalarType && m_Types[i]->bitWidth == bitWidth)
|
||||
return m_Types[i];
|
||||
|
||||
Type *t = CreateNewType();
|
||||
t->type = Type::Scalar;
|
||||
t->scalarType = scalarType;
|
||||
t->bitWidth = bitWidth;
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *ProgramEditor::CreateNamedStructType(const rdcstr &name, rdcarray<const Type *> members)
|
||||
{
|
||||
for(size_t i = 0; i < m_Types.size(); i++)
|
||||
@@ -221,15 +287,15 @@ Type *ProgramEditor::CreateNamedStructType(const rdcstr &name, rdcarray<const Ty
|
||||
return structType;
|
||||
}
|
||||
|
||||
DXIL::Type *ProgramEditor::CreateFunctionType(const Type *ret, rdcarray<const Type *> params)
|
||||
DXIL::Type *ProgramEditor::CreateFunctionType(const Type *retType, rdcarray<const Type *> params)
|
||||
{
|
||||
for(Type *type : m_Types)
|
||||
if(type->type == Type::Function && type->inner == ret && type->members == params)
|
||||
if(type->type == Type::Function && type->inner == retType && type->members == params)
|
||||
return type;
|
||||
|
||||
Type *funcType = CreateNewType();
|
||||
funcType->type = Type::Function;
|
||||
funcType->inner = ret;
|
||||
funcType->inner = retType;
|
||||
funcType->members = params;
|
||||
return funcType;
|
||||
}
|
||||
@@ -256,6 +322,47 @@ Function *ProgramEditor::GetFunctionByName(const rdcstr &name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Function *ProgramEditor::GetFunctionByPrefix(const rdcstr &name)
|
||||
{
|
||||
for(size_t i = 0; i < m_Functions.size(); i++)
|
||||
if(m_Functions[i]->name.beginsWith(name))
|
||||
return m_Functions[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Function *ProgramEditor::DeclareFunction(const rdcstr &name, const Type *retType,
|
||||
rdcarray<const Type *> params, Attribute desiredAttrs)
|
||||
{
|
||||
Function *ret = GetFunctionByName(name);
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
const Type *funcType = CreateFunctionType(retType, params);
|
||||
|
||||
Function functionDef;
|
||||
functionDef.name = name;
|
||||
functionDef.type = funcType;
|
||||
functionDef.external = true;
|
||||
functionDef.attrs = GetAttributeSet(desiredAttrs);
|
||||
|
||||
ret = DeclareFunction(functionDef);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Block *ProgramEditor::CreateBlock()
|
||||
{
|
||||
if(m_LabelType == NULL)
|
||||
{
|
||||
Type *label = CreateNewType();
|
||||
label->type = Type::Label;
|
||||
m_LabelType = label;
|
||||
}
|
||||
return new(alloc) Block(m_LabelType);
|
||||
}
|
||||
|
||||
Metadata *ProgramEditor::GetMetadataByName(const rdcstr &name)
|
||||
{
|
||||
for(size_t i = 0; i < m_NamedMeta.size(); i++)
|
||||
@@ -360,6 +467,11 @@ NamedMetadata *ProgramEditor::CreateNamedMetadata(const rdcstr &name)
|
||||
return m_NamedMeta.back();
|
||||
}
|
||||
|
||||
Literal *ProgramEditor::CreateLiteral(uint64_t val)
|
||||
{
|
||||
return new(alloc) Literal(val);
|
||||
}
|
||||
|
||||
Constant *ProgramEditor::CreateConstant(const Constant &c)
|
||||
{
|
||||
// for scalars, check for an existing constant
|
||||
@@ -391,6 +503,32 @@ Constant *ProgramEditor::CreateConstant(const Type *type, const rdcarray<Value *
|
||||
return ret;
|
||||
}
|
||||
|
||||
Constant *ProgramEditor::CreateConstantGEP(const Type *resultType,
|
||||
const rdcarray<Value *> &pointerAndIdxs)
|
||||
{
|
||||
Constant *ret = new(alloc) Constant;
|
||||
ret->op = Operation::GetElementPtr;
|
||||
ret->type = resultType;
|
||||
ret->setCompound(alloc, pointerAndIdxs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Constant *ProgramEditor::CreateUndef(const Type *t)
|
||||
{
|
||||
Constant c;
|
||||
c.type = t;
|
||||
c.setUndef(true);
|
||||
return CreateConstant(c);
|
||||
}
|
||||
|
||||
Constant *ProgramEditor::CreateNULL(const Type *t)
|
||||
{
|
||||
Constant c;
|
||||
c.type = t;
|
||||
c.setNULL(true);
|
||||
return CreateConstant(c);
|
||||
}
|
||||
|
||||
Instruction *ProgramEditor::CreateInstruction(Operation op)
|
||||
{
|
||||
Instruction *ret = new(alloc) Instruction;
|
||||
@@ -405,7 +543,27 @@ Instruction *ProgramEditor::CreateInstruction(const Function *f)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define getAttribID(a) uint64_t(a - m_AttributeSets.begin())
|
||||
Instruction *ProgramEditor::CreateInstruction(Operation op, const Type *retType,
|
||||
const rdcarray<Value *> &args)
|
||||
{
|
||||
Instruction *ret = new(alloc) Instruction;
|
||||
ret->op = op;
|
||||
ret->type = retType;
|
||||
ret->args = args;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Instruction *ProgramEditor::CreateInstruction(const Function *f, DXOp op,
|
||||
const rdcarray<Value *> &args)
|
||||
{
|
||||
Instruction *ret = CreateInstruction(f);
|
||||
ret->type = f->type->inner;
|
||||
ret->args = args;
|
||||
ret->args.insert(0, CreateConstant((uint32_t)op));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define getAttribID(a) uint64_t(m_AttributeSets.indexOf((AttributeSet *)a))
|
||||
#define getTypeID(t) uint64_t(t->id)
|
||||
#define getMetaID(m) uint64_t(m->id)
|
||||
#define getValueID(v) uint64_t(v->id)
|
||||
@@ -420,7 +578,7 @@ bytebuf ProgramEditor::EncodeProgram()
|
||||
LLVMBC::BitcodeWriter::Config cfg = {};
|
||||
|
||||
LLVMOrderAccumulator accum;
|
||||
accum.processGlobals(this);
|
||||
accum.processGlobals(this, false);
|
||||
|
||||
const rdcarray<const Value *> &values = accum.values;
|
||||
const rdcarray<const Metadata *> &metadata = accum.metadata;
|
||||
@@ -478,18 +636,18 @@ bytebuf ProgramEditor::EncodeProgram()
|
||||
|
||||
for(size_t i = 0; i < m_AttributeGroups.size(); i++)
|
||||
{
|
||||
if(m_AttributeGroups[i].slotIndex != AttributeGroup::InvalidSlot)
|
||||
if(m_AttributeGroups[i] && m_AttributeGroups[i]->slotIndex != AttributeGroup::InvalidSlot)
|
||||
{
|
||||
const AttributeGroup &group = m_AttributeGroups[i];
|
||||
const AttributeGroup *group = m_AttributeGroups[i];
|
||||
|
||||
vals.clear();
|
||||
vals.push_back(i);
|
||||
vals.push_back(group.slotIndex);
|
||||
vals.push_back(group->slotIndex);
|
||||
|
||||
// decompose params bitfield into bits
|
||||
if(group.params != Attribute::None)
|
||||
if(group->params != Attribute::None)
|
||||
{
|
||||
uint64_t params = (uint64_t)group.params;
|
||||
uint64_t params = (uint64_t)group->params;
|
||||
for(uint64_t p = 0; p < 64; p++)
|
||||
{
|
||||
if((params & (1ULL << p)) != 0)
|
||||
@@ -500,28 +658,28 @@ bytebuf ProgramEditor::EncodeProgram()
|
||||
{
|
||||
vals.push_back(1);
|
||||
vals.push_back(p);
|
||||
vals.push_back(group.align);
|
||||
vals.push_back(group->align);
|
||||
break;
|
||||
}
|
||||
case Attribute::StackAlignment:
|
||||
{
|
||||
vals.push_back(1);
|
||||
vals.push_back(p);
|
||||
vals.push_back(group.stackAlign);
|
||||
vals.push_back(group->stackAlign);
|
||||
break;
|
||||
}
|
||||
case Attribute::Dereferenceable:
|
||||
{
|
||||
vals.push_back(1);
|
||||
vals.push_back(p);
|
||||
vals.push_back(group.derefBytes);
|
||||
vals.push_back(group->derefBytes);
|
||||
break;
|
||||
}
|
||||
case Attribute::DereferenceableOrNull:
|
||||
{
|
||||
vals.push_back(1);
|
||||
vals.push_back(p);
|
||||
vals.push_back(group.derefOrNullBytes);
|
||||
vals.push_back(group->derefOrNullBytes);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -535,9 +693,9 @@ bytebuf ProgramEditor::EncodeProgram()
|
||||
}
|
||||
}
|
||||
|
||||
if(!group.strs.empty())
|
||||
if(!group->strs.empty())
|
||||
{
|
||||
for(const rdcpair<rdcstr, rdcstr> &strAttr : group.strs)
|
||||
for(const rdcpair<rdcstr, rdcstr> &strAttr : group->strs)
|
||||
{
|
||||
if(strAttr.second.empty())
|
||||
vals.push_back(3);
|
||||
@@ -565,7 +723,7 @@ bytebuf ProgramEditor::EncodeProgram()
|
||||
writer.BeginBlock(LLVMBC::KnownBlock::PARAMATTR_BLOCK);
|
||||
|
||||
for(size_t i = 0; i < m_AttributeSets.size(); i++)
|
||||
writer.Record(LLVMBC::ParamAttrRecord::ENTRY, m_AttributeSets[i].orderedGroups);
|
||||
writer.Record(LLVMBC::ParamAttrRecord::ENTRY, m_AttributeSets[i]->orderedGroups);
|
||||
|
||||
writer.EndBlock();
|
||||
}
|
||||
@@ -1620,21 +1778,114 @@ void ProgramEditor::RegisterUAV(DXILResourceType type, uint32_t space, uint32_t
|
||||
// patch SFI0 here for non-CS non-PS shaders
|
||||
if(m_Type != DXBC::ShaderType::Compute && m_Type != DXBC::ShaderType::Pixel)
|
||||
{
|
||||
// cheekily cast away const since this returns the blob in-place
|
||||
DXBC::GlobalShaderFlags *flags =
|
||||
(DXBC::GlobalShaderFlags *)DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_SFI0, sz);
|
||||
|
||||
// this *should* always be present, so we can just add our flag
|
||||
if(flags)
|
||||
(*flags) |= DXBC::GlobalShaderFlags::UAVsEveryStage;
|
||||
else
|
||||
RDCWARN("Feature flags chunk not present");
|
||||
PatchGlobalShaderFlags(
|
||||
[](DXBC::GlobalShaderFlags &flags) { flags |= DXBC::GlobalShaderFlags::UAVsEveryStage; });
|
||||
}
|
||||
|
||||
// strip the root signature, we shouldn't need it and it may no longer match and fail validation
|
||||
DXBC::DXBCContainer::StripChunk(m_OutBlob, DXBC::FOURCC_RTS0);
|
||||
}
|
||||
|
||||
void ProgramEditor::SetNumThreads(uint32_t dim[3])
|
||||
{
|
||||
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;
|
||||
|
||||
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 = sizeof(uint16_t) + 10 * sizeof(uint8_t);
|
||||
const uint32_t headerSizeVer2 = 3 * sizeof(uint32_t);
|
||||
|
||||
if(*headerSize >= headerSizeVer2)
|
||||
{
|
||||
cur += headerSizeVer0;
|
||||
cur += headerSizeVer1;
|
||||
memcpy(cur, dim, sizeof(uint32_t) * 3);
|
||||
}
|
||||
|
||||
DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void ProgramEditor::PatchGlobalShaderFlags(std::function<void(DXBC::GlobalShaderFlags &)> patcher)
|
||||
{
|
||||
size_t sz = 0;
|
||||
// cheekily cast away const since this returns the blob in-place
|
||||
DXBC::GlobalShaderFlags *flags =
|
||||
(DXBC::GlobalShaderFlags *)DXBC::DXBCContainer::FindChunk(m_OutBlob, DXBC::FOURCC_SFI0, sz);
|
||||
|
||||
// this *should* always be present, so we can just add our flag
|
||||
if(flags)
|
||||
patcher(*flags);
|
||||
else
|
||||
RDCWARN("Feature flags chunk not present");
|
||||
}
|
||||
|
||||
void ProgramEditor::EncodeConstants(LLVMBC::BitcodeWriter &writer,
|
||||
const rdcarray<const Value *> &values, size_t firstIdx,
|
||||
size_t count) const
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace DXBC
|
||||
{
|
||||
class DXBCContainer;
|
||||
enum class GlobalShaderFlags : int64_t;
|
||||
};
|
||||
|
||||
namespace LLVMBC
|
||||
@@ -74,15 +75,22 @@ public:
|
||||
using Program::GetInt8Type;
|
||||
using Program::GetPointerType;
|
||||
|
||||
const rdcarray<AttributeSet> &GetAttributeSets() { return m_AttributeSets; }
|
||||
int32_t GetKind(const rdcstr &kind) { return m_Kinds.indexOf(kind); }
|
||||
|
||||
const rdcarray<Type *> &GetTypes() const { return m_Types; }
|
||||
const AttributeSet *GetAttributeSet(Attribute desiredAttrs);
|
||||
Type *CreateScalarType(Type::ScalarKind scalarType, uint32_t bitWidth);
|
||||
Type *CreateNamedStructType(const rdcstr &name, rdcarray<const Type *> members);
|
||||
Type *CreateFunctionType(const Type *ret, rdcarray<const Type *> params);
|
||||
Type *CreateFunctionType(const Type *retType, rdcarray<const Type *> params);
|
||||
Type *CreatePointerType(const Type *inner, Type::PointerAddrSpace addrSpace);
|
||||
Function *GetFunctionByName(const rdcstr &name);
|
||||
Function *GetFunctionByPrefix(const rdcstr &name);
|
||||
Metadata *GetMetadataByName(const rdcstr &name);
|
||||
|
||||
Function *DeclareFunction(const rdcstr &name, const Type *retType, rdcarray<const Type *> params,
|
||||
Attribute desiredAttrs);
|
||||
Function *DeclareFunction(const Function &f);
|
||||
Block *CreateBlock();
|
||||
Metadata *CreateMetadata();
|
||||
Metadata *CreateConstantMetadata(Constant *val);
|
||||
Metadata *CreateConstantMetadata(uint32_t val);
|
||||
@@ -92,20 +100,46 @@ public:
|
||||
Metadata *CreateConstantMetadata(const rdcstr &str);
|
||||
NamedMetadata *CreateNamedMetadata(const rdcstr &name);
|
||||
|
||||
Literal *CreateLiteral(uint64_t val);
|
||||
|
||||
// I think constants have to be unique, so this will return an existing constant (for simple cases
|
||||
// like integers or NULL) if it exists
|
||||
Constant *CreateConstant(const Constant &c);
|
||||
Constant *CreateConstant(const Type *t, const rdcarray<Value *> &members);
|
||||
Constant *CreateConstantGEP(const Type *resultType, const rdcarray<Value *> &pointerAndIdxs);
|
||||
Constant *CreateUndef(const Type *t);
|
||||
Constant *CreateNULL(const Type *t);
|
||||
|
||||
Constant *CreateConstant(uint32_t u) { return CreateConstant(Constant(m_Int32Type, u)); }
|
||||
Constant *CreateConstant(uint8_t u) { return CreateConstant(Constant(m_Int8Type, u)); }
|
||||
Constant *CreateConstant(bool b) { return CreateConstant(Constant(m_BoolType, b)); }
|
||||
Instruction *CreateInstruction(Operation op);
|
||||
Instruction *CreateInstruction(Operation op, const Type *retType, const rdcarray<Value *> &args);
|
||||
Instruction *CreateInstruction(const Function *f);
|
||||
Instruction *CreateInstruction(const Function *f, DXOp op, const rdcarray<Value *> &args);
|
||||
Instruction::ExtraInstructionInfo &GetInstructionExtras(Instruction *inst)
|
||||
{
|
||||
return inst->extra(alloc);
|
||||
}
|
||||
|
||||
Instruction *AddInstruction(Function *f, Instruction *i)
|
||||
{
|
||||
f->instructions.push_back(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
Instruction *InsertInstruction(Function *f, size_t idx, Instruction *i)
|
||||
{
|
||||
f->instructions.insert(idx, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
void RegisterUAV(DXILResourceType type, uint32_t space, uint32_t regBase, uint32_t regEnd,
|
||||
ResourceKind kind);
|
||||
|
||||
void SetNumThreads(uint32_t dim[3]);
|
||||
void SetASPayloadSize(uint32_t payloadSize);
|
||||
void SetMSPayloadSize(uint32_t payloadSize);
|
||||
void PatchGlobalShaderFlags(std::function<void(DXBC::GlobalShaderFlags &)> patcher);
|
||||
private:
|
||||
bytebuf &m_OutBlob;
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ void Program::MakeDisassemblyString()
|
||||
}
|
||||
|
||||
LLVMOrderAccumulator accum;
|
||||
accum.processGlobals(this);
|
||||
accum.processGlobals(this, false);
|
||||
|
||||
bool printedTypes = false;
|
||||
|
||||
@@ -463,13 +463,16 @@ void Program::MakeDisassemblyString()
|
||||
rdcarray<const AttributeGroup *> funcAttrGroups;
|
||||
for(size_t i = 0; i < m_AttributeGroups.size(); i++)
|
||||
{
|
||||
if(m_AttributeGroups[i].slotIndex != AttributeGroup::FunctionSlot)
|
||||
if(!m_AttributeGroups[i])
|
||||
continue;
|
||||
|
||||
if(funcAttrGroups.contains(&m_AttributeGroups[i]))
|
||||
if(m_AttributeGroups[i]->slotIndex != AttributeGroup::FunctionSlot)
|
||||
continue;
|
||||
|
||||
funcAttrGroups.push_back(&m_AttributeGroups[i]);
|
||||
if(funcAttrGroups.contains(m_AttributeGroups[i]))
|
||||
continue;
|
||||
|
||||
funcAttrGroups.push_back(m_AttributeGroups[i]);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < m_Functions.size(); i++)
|
||||
|
||||
+64
-52
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="rdcinflexiblestr">
|
||||
<DisplayString>{(char*)pointer,s}</DisplayString>
|
||||
@@ -66,58 +66,70 @@
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="rdcpair<*>">
|
||||
<DisplayString>{{ {first}, {second} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="first" ExcludeView="simple">first</Item>
|
||||
<Item Name="second" ExcludeView="simple">second</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="SDType">
|
||||
<DisplayString>{name}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="name">name</Item>
|
||||
<Item Name="basetype">basetype</Item>
|
||||
<Item Name="byteSize">byteSize</Item>
|
||||
<Item Name="flags">flags</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="SDObject">
|
||||
<DisplayString Condition="type.flags & SDTypeFlags::HasCustomString">{name} = {data.str}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::String">{name} = {data.str}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::UnsignedInteger">{name} = {data.basic.u}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::SignedInteger">{name} = {data.basic.i}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Float">{name} = {data.basic.d}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Boolean">{name} = {data.basic.b}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Character">{name} = {data.basic.c}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Resource">{name} = {data.basic.id}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Array">{name} = {type.name}[]</DisplayString>
|
||||
<DisplayString>SDObject: {type.name} {name}</DisplayString>
|
||||
<Expand>
|
||||
<Item Condition="type.basetype == SDBasic::Array" Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
|
||||
<ArrayItems Condition="type.basetype == SDBasic::Array || type.basetype == SDBasic::Struct">
|
||||
<Size>data.children.usedCount</Size>
|
||||
<ValuePointer>data.children.elems</ValuePointer>
|
||||
</ArrayItems>
|
||||
<Item Condition="type.basetype != SDBasic::Array && type.basetype != SDBasic::Struct" Name="type">type</Item>
|
||||
<Item Condition="type.basetype != SDBasic::Array && type.basetype != SDBasic::Struct" Name="name">name</Item>
|
||||
<Item Condition="type.basetype != SDBasic::Array && type.basetype != SDBasic::Struct" Name="data">data</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="SDChunk">
|
||||
<DisplayString>SDChunk: {name} ({metadata.chunkID})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="metadata" ExcludeView="simple">metadata</Item>
|
||||
<Item Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
|
||||
<ArrayItems>
|
||||
<Size>data.children.usedCount</Size>
|
||||
<ValuePointer>data.children.elems</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="D3D12ResourceLayout">
|
||||
<Type Name="rdcpair<*>">
|
||||
<DisplayString>{{ {first}, {second} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="first" ExcludeView="simple">first</Item>
|
||||
<Item Name="second" ExcludeView="simple">second</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="SDType">
|
||||
<DisplayString>{name}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="name">name</Item>
|
||||
<Item Name="basetype">basetype</Item>
|
||||
<Item Name="byteSize">byteSize</Item>
|
||||
<Item Name="flags">flags</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="SDObject">
|
||||
<DisplayString Condition="type.flags & SDTypeFlags::HasCustomString">{name} = {data.str}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::String">{name} = {data.str}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::UnsignedInteger">{name} = {data.basic.u}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::SignedInteger">{name} = {data.basic.i}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Float">{name} = {data.basic.d}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Boolean">{name} = {data.basic.b}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Character">{name} = {data.basic.c}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Resource">{name} = {data.basic.id}</DisplayString>
|
||||
<DisplayString Condition="type.basetype == SDBasic::Array">{name} = {type.name}[]</DisplayString>
|
||||
<DisplayString>SDObject: {type.name} {name}</DisplayString>
|
||||
<Expand>
|
||||
<Item Condition="type.basetype == SDBasic::Array" Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
|
||||
<ArrayItems Condition="type.basetype == SDBasic::Array || type.basetype == SDBasic::Struct">
|
||||
<Size>data.children.usedCount</Size>
|
||||
<ValuePointer>data.children.elems</ValuePointer>
|
||||
</ArrayItems>
|
||||
<Item Condition="type.basetype != SDBasic::Array && type.basetype != SDBasic::Struct" Name="type">type</Item>
|
||||
<Item Condition="type.basetype != SDBasic::Array && type.basetype != SDBasic::Struct" Name="name">name</Item>
|
||||
<Item Condition="type.basetype != SDBasic::Array && type.basetype != SDBasic::Struct" Name="data">data</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="SDChunk">
|
||||
<DisplayString>SDChunk: {name} ({metadata.chunkID})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="metadata" ExcludeView="simple">metadata</Item>
|
||||
<Item Name="[size]" ExcludeView="simple">data.children.usedCount</Item>
|
||||
<ArrayItems>
|
||||
<Size>data.children.usedCount</Size>
|
||||
<ValuePointer>data.children.elems</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="D3D12ResourceLayout">
|
||||
<DisplayString Condition="value == D3D12_BARRIER_LAYOUT_UNDEFINED">{D3D12_BARRIER_LAYOUT_UNDEFINED}</DisplayString>
|
||||
<DisplayString Condition="value & 0x80000000U">{D3D12_BARRIER_LAYOUT(value & 0x7fffffffU)}</DisplayString>
|
||||
<DisplayString>{D3D12_RESOURCE_STATES(value)}</DisplayString>
|
||||
</Type>
|
||||
</Type>
|
||||
<Type Name="DXIL::Value" Inheritable="false">
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valKind==1">*(DXIL::Literal *)this</ExpandedItem>
|
||||
<ExpandedItem Condition="valKind==2">*(DXIL::Alias *)this</ExpandedItem>
|
||||
<ExpandedItem Condition="valKind==3">*(DXIL::Constant *)this</ExpandedItem>
|
||||
<ExpandedItem Condition="valKind==4">*(DXIL::GlobalVar *)this</ExpandedItem>
|
||||
<ExpandedItem Condition="valKind==5">*(DXIL::Metadata *)this</ExpandedItem>
|
||||
<ExpandedItem Condition="valKind==6">*(DXIL::Instruction *)this</ExpandedItem>
|
||||
<ExpandedItem Condition="valKind==7">*(DXIL::Function *)this</ExpandedItem>
|
||||
<ExpandedItem Condition="valKind==8">*(DXIL::Block *)this</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
Reference in New Issue
Block a user