in renderdoc/driver/vulkan/vk_shaderdebug.cpp [2971:3915]
static void CreatePSInputFetcher(rdcarray<uint32_t> &fragspv, uint32_t &structStride,
VulkanCreationInfo::ShaderModuleReflection &shadRefl,
const uint32_t paramAlign, StorageMode storageMode,
bool usePrimitiveID, bool useSampleID, bool useViewIndex)
{
rdcspv::Editor editor(fragspv);
editor.Prepare();
rdcspv::Id entryID;
// first delete all functions. We will recreate the entry point with just what we need
{
rdcarray<rdcspv::Id> removedIds;
rdcspv::Iter it = editor.Begin(rdcspv::Section::Functions);
rdcspv::Iter end = editor.End(rdcspv::Section::Functions);
while(it < end)
{
removedIds.push_back(rdcspv::OpDecoder(it).result);
editor.Remove(it);
it++;
}
it = editor.Begin(rdcspv::Section::EntryPoints);
end = editor.End(rdcspv::Section::EntryPoints);
while(it < end)
{
rdcspv::OpEntryPoint e(it);
if(e.name == shadRefl.entryPoint && e.executionModel == rdcspv::ExecutionModel::Fragment)
{
// remember the Id of our entry point
entryID = e.entryPoint;
}
else
{
// remove all other entry points
removedIds.push_back(e.entryPoint);
editor.Remove(it);
}
it++;
}
it = editor.Begin(rdcspv::Section::ExecutionMode);
end = editor.End(rdcspv::Section::ExecutionMode);
while(it < end)
{
// this can also handle ExecutionModeId and we don't care about the difference
rdcspv::OpExecutionMode execMode(it);
// remove any execution modes not for our entry
if(execMode.entryPoint != entryID)
editor.Remove(it);
it++;
}
// remove any OpName that refers to deleted IDs - functions or results
it = editor.Begin(rdcspv::Section::DebugNames);
end = editor.End(rdcspv::Section::DebugNames);
while(it < end)
{
if(it.opcode() == rdcspv::Op::Name)
{
rdcspv::OpName name(it);
if(removedIds.contains(name.target))
editor.Remove(it);
}
it++;
}
// same for decorations
it = editor.Begin(rdcspv::Section::Annotations);
end = editor.End(rdcspv::Section::Annotations);
while(it < end)
{
if(it.opcode() == rdcspv::Op::Decorate)
{
rdcspv::OpDecorate dec(it);
if(removedIds.contains(dec.target))
editor.Remove(it);
}
else if(it.opcode() == rdcspv::Op::DecorateId)
{
rdcspv::OpDecorateId dec(it);
if(removedIds.contains(dec.target))
editor.Remove(it);
}
it++;
}
}
rdcspv::MemoryAccessAndParamDatas alignedAccess;
alignedAccess.setAligned(sizeof(uint32_t));
rdcspv::Id uint32Type = editor.DeclareType(rdcspv::scalar<uint32_t>());
rdcspv::Id floatType = editor.DeclareType(rdcspv::scalar<float>());
rdcspv::Id boolType = editor.DeclareType(rdcspv::scalar<bool>());
rdcarray<rdcspv::Id> uintConsts;
auto getUIntConst = [&uintConsts, &editor](uint32_t c) {
for(uint32_t i = (uint32_t)uintConsts.size(); i <= c; i++)
uintConsts.push_back(editor.AddConstantImmediate<uint32_t>(uint32_t(i)));
return uintConsts[c];
};
rdcspv::StorageClass bufferClass;
if(storageMode == Binding)
bufferClass = editor.StorageBufferClass();
else
bufferClass = rdcspv::StorageClass::PhysicalStorageBuffer;
rdcarray<rdcspv::Id> addedInputs;
// builtin inputs we need
struct BuiltinAccess
{
rdcspv::Id base;
rdcspv::Id type;
uint32_t member = ~0U;
} fragCoord, primitiveID, sampleIndex, viewIndex;
// look to see which ones are already provided
for(size_t i = 0; i < shadRefl.refl->inputSignature.size(); i++)
{
const SigParameter ¶m = shadRefl.refl->inputSignature[i];
BuiltinAccess *access = NULL;
if(param.systemValue == ShaderBuiltin::Position)
{
access = &fragCoord;
}
else if(param.systemValue == ShaderBuiltin::PrimitiveIndex)
{
access = &primitiveID;
access->type = VarTypeCompType(param.varType) == CompType::SInt
? editor.DeclareType(rdcspv::scalar<int32_t>())
: editor.DeclareType(rdcspv::scalar<uint32_t>());
}
else if(param.systemValue == ShaderBuiltin::MSAASampleIndex)
{
access = &sampleIndex;
access->type = VarTypeCompType(param.varType) == CompType::SInt
? editor.DeclareType(rdcspv::scalar<int32_t>())
: editor.DeclareType(rdcspv::scalar<uint32_t>());
}
else if(param.systemValue == ShaderBuiltin::MultiViewIndex)
{
access = &viewIndex;
access->type = VarTypeCompType(param.varType) == CompType::SInt
? editor.DeclareType(rdcspv::scalar<int32_t>())
: editor.DeclareType(rdcspv::scalar<uint32_t>());
}
if(access)
{
SPIRVInterfaceAccess &patch = shadRefl.patchData.inputs[i];
access->base = patch.ID;
// should only be one deep at most, built-in interface block isn't allowed to be nested
RDCASSERT(patch.accessChain.size() <= 1);
if(!patch.accessChain.empty())
access->member = patch.accessChain[0];
}
}
// now declare any variables we didn't already have
if(fragCoord.base == rdcspv::Id())
{
rdcspv::Id type = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<float>(), 4));
rdcspv::Id ptrType = editor.DeclareType(rdcspv::Pointer(type, rdcspv::StorageClass::Input));
fragCoord.base =
editor.AddVariable(rdcspv::OpVariable(ptrType, editor.MakeId(), rdcspv::StorageClass::Input));
fragCoord.type = type;
editor.AddDecoration(rdcspv::OpDecorate(
fragCoord.base,
rdcspv::DecorationParam<rdcspv::Decoration::BuiltIn>(rdcspv::BuiltIn::FragCoord)));
addedInputs.push_back(fragCoord.base);
}
if(primitiveID.base == rdcspv::Id() && usePrimitiveID)
{
rdcspv::Id type = editor.DeclareType(rdcspv::scalar<uint32_t>());
rdcspv::Id ptrType = editor.DeclareType(rdcspv::Pointer(type, rdcspv::StorageClass::Input));
primitiveID.base =
editor.AddVariable(rdcspv::OpVariable(ptrType, editor.MakeId(), rdcspv::StorageClass::Input));
primitiveID.type = type;
editor.AddDecoration(rdcspv::OpDecorate(
primitiveID.base,
rdcspv::DecorationParam<rdcspv::Decoration::BuiltIn>(rdcspv::BuiltIn::PrimitiveId)));
editor.AddDecoration(rdcspv::OpDecorate(primitiveID.base, rdcspv::Decoration::Flat));
addedInputs.push_back(primitiveID.base);
editor.AddCapability(rdcspv::Capability::Geometry);
}
if(sampleIndex.base == rdcspv::Id() && useSampleID)
{
rdcspv::Id type = editor.DeclareType(rdcspv::scalar<uint32_t>());
rdcspv::Id ptrType = editor.DeclareType(rdcspv::Pointer(type, rdcspv::StorageClass::Input));
sampleIndex.base =
editor.AddVariable(rdcspv::OpVariable(ptrType, editor.MakeId(), rdcspv::StorageClass::Input));
sampleIndex.type = type;
editor.AddDecoration(rdcspv::OpDecorate(
sampleIndex.base,
rdcspv::DecorationParam<rdcspv::Decoration::BuiltIn>(rdcspv::BuiltIn::SampleId)));
editor.AddDecoration(rdcspv::OpDecorate(sampleIndex.base, rdcspv::Decoration::Flat));
addedInputs.push_back(sampleIndex.base);
editor.AddCapability(rdcspv::Capability::SampleRateShading);
}
if(viewIndex.base == rdcspv::Id() && useViewIndex)
{
rdcspv::Id type = editor.DeclareType(rdcspv::scalar<uint32_t>());
rdcspv::Id ptrType = editor.DeclareType(rdcspv::Pointer(type, rdcspv::StorageClass::Input));
viewIndex.base =
editor.AddVariable(rdcspv::OpVariable(ptrType, editor.MakeId(), rdcspv::StorageClass::Input));
viewIndex.type = type;
editor.AddDecoration(rdcspv::OpDecorate(
viewIndex.base,
rdcspv::DecorationParam<rdcspv::Decoration::BuiltIn>(rdcspv::BuiltIn::ViewIndex)));
editor.AddDecoration(rdcspv::OpDecorate(viewIndex.base, rdcspv::Decoration::Flat));
addedInputs.push_back(viewIndex.base);
editor.AddCapability(rdcspv::Capability::MultiView);
}
rdcspv::Id PSInput;
enum Variant
{
Variant_Base,
Variant_ddxcoarse,
Variant_ddycoarse,
Variant_ddxfine,
Variant_ddyfine,
Variant_Count,
};
struct valueAndDerivs
{
rdcspv::Id valueType;
rdcspv::Id data[Variant_Count];
uint32_t structIndex;
rdcspv::OperationList storeOps;
};
rdcarray<valueAndDerivs> values;
values.resize(shadRefl.refl->inputSignature.size());
{
rdcarray<rdcspv::Id> ids;
rdcarray<uint32_t> offsets;
rdcarray<uint32_t> indices;
for(size_t i = 0; i < shadRefl.refl->inputSignature.size(); i++)
{
const SigParameter ¶m = shadRefl.refl->inputSignature[i];
rdcspv::Scalar base = rdcspv::scalar(param.varType);
values[i].structIndex = (uint32_t)offsets.size();
uint32_t width = (base.width / 8);
// treat bools as uints
if(base.type == rdcspv::Op::TypeBool)
width = 4;
offsets.push_back(structStride);
structStride += param.compCount * width;
if(param.compCount == 1)
values[i].valueType = editor.DeclareType(base);
else
values[i].valueType = editor.DeclareType(rdcspv::Vector(base, param.compCount));
if(values[i].valueType == boolType)
ids.push_back(uint32Type);
else
ids.push_back(values[i].valueType);
// align offset conservatively, to 16-byte aligned. We do this with explicit uints so we can
// preview with spirv-cross (and because it doesn't cost anything particularly)
uint32_t paddingWords = ((16 - (structStride % 16)) / 4) % 4;
for(uint32_t p = 0; p < paddingWords; p++)
{
ids.push_back(uint32Type);
offsets.push_back(structStride);
structStride += 4;
}
}
PSInput = editor.DeclareStructType(ids);
for(size_t i = 0; i < offsets.size(); i++)
{
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSInput, uint32_t(i), rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offsets[i])));
}
for(size_t i = 0; i < values.size(); i++)
editor.SetMemberName(PSInput, values[i].structIndex, shadRefl.refl->inputSignature[i].varName);
editor.SetName(PSInput, "__rd_PSInput");
}
rdcspv::Id float4Type = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<float>(), 4));
rdcspv::Id float2Type = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<float>(), 2));
rdcspv::Id arrayLength =
editor.AddSpecConstantImmediate<uint32_t>(1U, (uint32_t)InputSpecConstant::ArrayLength);
editor.SetName(arrayLength, "arrayLength");
rdcspv::Id destX = editor.AddSpecConstantImmediate<float>(0.0f, (uint32_t)InputSpecConstant::DestX);
rdcspv::Id destY = editor.AddSpecConstantImmediate<float>(0.0f, (uint32_t)InputSpecConstant::DestY);
editor.SetName(destX, "destX");
editor.SetName(destY, "destY");
rdcspv::Id destXY = editor.AddConstant(
rdcspv::OpSpecConstantComposite(float2Type, editor.MakeId(), {destX, destY}));
editor.SetName(destXY, "destXY");
rdcspv::Id PSHit = editor.DeclareStructType({
// float4 pos;
float4Type,
// uint prim;
uint32Type,
// uint sample;
uint32Type,
// uint view;
uint32Type,
// uint valid;
uint32Type,
// float ddxDerivCheck;
floatType,
// <uint3 padding>
// IN
PSInput,
// INddxcoarse
PSInput,
// INddycoarse
PSInput,
// INddxfine
PSInput,
// INddxfine
PSInput,
});
{
editor.SetName(PSHit, "__rd_PSHit");
uint32_t offs = 0, member = 0;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "pos");
offs += sizeof(Vec4f);
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "prim");
offs += sizeof(uint32_t);
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "sample");
offs += sizeof(uint32_t);
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "view");
offs += sizeof(uint32_t);
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "valid");
offs += sizeof(uint32_t);
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "ddxDerivCheck");
offs += sizeof(uint32_t);
member++;
// <uint3 padding>
offs += sizeof(uint32_t) * 3;
RDCASSERT((offs % sizeof(Vec4f)) == 0);
RDCASSERT((structStride % sizeof(Vec4f)) == 0);
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "IN");
offs += structStride;
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "INddxcoarse");
offs += structStride;
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "INddycoarse");
offs += structStride;
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "INddxfine");
offs += structStride;
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "INddyfine");
offs += structStride;
member++;
}
rdcspv::Id PSHitRTArray = editor.AddType(rdcspv::OpTypeRuntimeArray(editor.MakeId(), PSHit));
editor.AddDecoration(rdcspv::OpDecorate(
PSHitRTArray, rdcspv::DecorationParam<rdcspv::Decoration::ArrayStride>(structStride * 5 +
sizeof(Vec4f) * 3)));
rdcspv::Id bufBase = editor.DeclareStructType({
// uint hit_count;
uint32Type,
// uint total_count;
uint32Type,
// <uint2 padding>
// PSHit hits[];
PSHitRTArray,
});
rdcspv::Id ssboVar;
{
editor.SetName(bufBase, "__rd_HitStorage");
editor.AddDecoration(rdcspv::OpMemberDecorate(
bufBase, 0, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(0)));
editor.SetMemberName(bufBase, 0, "hit_count");
editor.AddDecoration(rdcspv::OpMemberDecorate(
bufBase, 1, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(sizeof(uint32_t))));
editor.SetMemberName(bufBase, 1, "total_count");
editor.AddDecoration(rdcspv::OpMemberDecorate(
bufBase, 2, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(sizeof(Vec4f))));
editor.SetMemberName(bufBase, 2, "hits");
}
rdcspv::Id bufptrtype;
rdcspv::Id addressConstant;
if(storageMode == Binding)
{
// the pointers are SSBO pointers
bufptrtype = editor.DeclareType(rdcspv::Pointer(bufBase, bufferClass));
// patch all bindings up by 1
for(rdcspv::Iter it = editor.Begin(rdcspv::Section::Annotations),
end = editor.End(rdcspv::Section::Annotations);
it < end; ++it)
{
// we will use descriptor set 0 for our own purposes if we don't have a buffer address.
//
// Since bindings are arbitrary, we just increase all user bindings to make room, and we'll
// redeclare the descriptor set layouts and pipeline layout. This is inevitable in the case
// where all descriptor sets are already used. In theory we only have to do this with set 0,
// but that requires knowing which variables are in set 0 and it's simpler to increase all
// bindings.
if(it.opcode() == rdcspv::Op::Decorate)
{
rdcspv::OpDecorate dec(it);
if(dec.decoration == rdcspv::Decoration::Binding)
{
RDCASSERT(dec.decoration.binding != 0xffffffff);
dec.decoration.binding += 1;
it = dec;
}
}
}
// add our SSBO variable, at set 0 binding 0
ssboVar = editor.MakeId();
editor.AddVariable(rdcspv::OpVariable(bufptrtype, ssboVar, bufferClass));
editor.AddDecoration(
rdcspv::OpDecorate(ssboVar, rdcspv::DecorationParam<rdcspv::Decoration::DescriptorSet>(0)));
editor.AddDecoration(
rdcspv::OpDecorate(ssboVar, rdcspv::DecorationParam<rdcspv::Decoration::Binding>(0)));
editor.SetName(ssboVar, "__rd_HitBuffer");
editor.DecorateStorageBufferStruct(bufBase);
}
else
{
bufptrtype = editor.DeclareType(rdcspv::Pointer(bufBase, bufferClass));
// add the extension
editor.AddExtension(storageMode == KHR_bda ? "SPV_KHR_physical_storage_buffer"
: "SPV_EXT_physical_storage_buffer");
if(useViewIndex)
editor.AddExtension("SPV_KHR_multiview");
// change the memory model to physical storage buffer 64
rdcspv::Iter it = editor.Begin(rdcspv::Section::MemoryModel);
rdcspv::OpMemoryModel model(it);
model.addressingModel = rdcspv::AddressingModel::PhysicalStorageBuffer64;
it = model;
// add capabilities
editor.AddCapability(rdcspv::Capability::PhysicalStorageBufferAddresses);
// declare the address constant which we will specialise later. There is a chicken-and-egg where
// this function determines how big the buffer needs to be so instead of hardcoding the address
// here we let it be allocated later and specialised in.
if(storageMode == KHR_bda)
{
rdcspv::Id addressConstantLSB =
editor.AddSpecConstantImmediate<uint32_t>(0U, (uint32_t)InputSpecConstant::Address);
rdcspv::Id addressConstantMSB =
editor.AddSpecConstantImmediate<uint32_t>(0U, (uint32_t)InputSpecConstant::AddressMSB);
rdcspv::Id uint2 = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<uint32_t>(), 2));
addressConstant = editor.AddConstant(rdcspv::OpSpecConstantComposite(
uint2, editor.MakeId(), {addressConstantLSB, addressConstantMSB}));
}
else
{
editor.AddCapability(rdcspv::Capability::Int64);
addressConstant =
editor.AddSpecConstantImmediate<uint64_t>(0ULL, (uint32_t)InputSpecConstant::Address);
}
editor.SetName(addressConstant, "__rd_bufAddress");
// struct is block decorated
editor.AddDecoration(rdcspv::OpDecorate(bufBase, rdcspv::Decoration::Block));
}
if(editor.EntryPointAllGlobals() && ssboVar != rdcspv::Id())
addedInputs.push_back(ssboVar);
// add our inputs to the entry point's ID list. Since we're expanding the list we have to copy,
// erase, and insert. Modifying in-place doesn't support expanding
if(!addedInputs.empty())
{
rdcspv::Iter it = editor.GetEntry(entryID);
// this copies into the helper struct
rdcspv::OpEntryPoint entry(it);
// add our IDs
entry.iface.append(addedInputs);
// erase the old one
editor.Remove(it);
editor.AddOperation(it, entry);
}
rdcspv::Id float4InPtr =
editor.DeclareType(rdcspv::Pointer(float4Type, rdcspv::StorageClass::Input));
rdcspv::Id float4BufPtr = editor.DeclareType(rdcspv::Pointer(float4Type, bufferClass));
rdcspv::Id uint32InPtr =
editor.DeclareType(rdcspv::Pointer(uint32Type, rdcspv::StorageClass::Input));
rdcspv::Id uint32BufPtr = editor.DeclareType(rdcspv::Pointer(uint32Type, bufferClass));
rdcspv::Id floatBufPtr = editor.DeclareType(rdcspv::Pointer(floatType, bufferClass));
rdcspv::Id glsl450 = editor.ImportExtInst("GLSL.std.450");
editor.AddCapability(rdcspv::Capability::DerivativeControl);
{
rdcspv::OperationList ops;
rdcspv::Id voidType = editor.DeclareType(rdcspv::scalar<void>());
ops.add(rdcspv::OpFunction(voidType, entryID, rdcspv::FunctionControl::None,
editor.DeclareType(rdcspv::FunctionType(voidType, {}))));
ops.add(rdcspv::OpLabel(editor.MakeId()));
{
// grab all the values here and get any derivatives we need now before we branch non-uniformly
for(size_t i = 0; i < values.size(); i++)
{
const SPIRVInterfaceAccess &access = shadRefl.patchData.inputs[i];
const SigParameter ¶m = shadRefl.refl->inputSignature[i];
rdcarray<rdcspv::Id> accessIndices;
for(uint32_t idx : access.accessChain)
accessIndices.push_back(getUIntConst(idx));
rdcspv::Id valueType = values[i].valueType;
rdcspv::Id ptrType =
editor.DeclareType(rdcspv::Pointer(valueType, rdcspv::StorageClass::Input));
// if we have no access chain it's a global pointer of the type we want, so just load
// straight out of it
rdcspv::Id ptr;
if(accessIndices.empty())
ptr = access.ID;
else
ptr = ops.add(rdcspv::OpAccessChain(ptrType, editor.MakeId(), access.ID, accessIndices));
rdcspv::Id base = ops.add(rdcspv::OpLoad(valueType, editor.MakeId(), ptr));
if(valueType == boolType)
{
valueType = uint32Type;
// can't store bools directly, need to convert to uint
base = ops.add(
rdcspv::OpSelect(valueType, editor.MakeId(), base, getUIntConst(1), getUIntConst(0)));
}
values[i].data[Variant_Base] = base;
editor.SetName(base, StringFormat::Fmt("__rd_base_%zu_%s", i, param.varName.c_str()));
// only float values have derivatives
if(VarTypeCompType(param.varType) == CompType::Float)
{
values[i].data[Variant_ddxcoarse] =
ops.add(rdcspv::OpDPdxCoarse(valueType, editor.MakeId(), base));
values[i].data[Variant_ddycoarse] =
ops.add(rdcspv::OpDPdyCoarse(valueType, editor.MakeId(), base));
values[i].data[Variant_ddxfine] =
ops.add(rdcspv::OpDPdxFine(valueType, editor.MakeId(), base));
values[i].data[Variant_ddyfine] =
ops.add(rdcspv::OpDPdyFine(valueType, editor.MakeId(), base));
editor.SetName(values[i].data[Variant_ddxcoarse],
StringFormat::Fmt("__rd_ddxcoarse_%zu_%s", i, param.varName.c_str()));
editor.SetName(values[i].data[Variant_ddycoarse],
StringFormat::Fmt("__rd_ddycoarse_%zu_%s", i, param.varName.c_str()));
editor.SetName(values[i].data[Variant_ddxfine],
StringFormat::Fmt("__rd_ddxfine_%zu_%s", i, param.varName.c_str()));
editor.SetName(values[i].data[Variant_ddyfine],
StringFormat::Fmt("__rd_ddyfine_%zu_%s", i, param.varName.c_str()));
}
else
{
values[i].data[Variant_ddxcoarse] = values[i].data[Variant_ddycoarse] =
values[i].data[Variant_ddxfine] = values[i].data[Variant_ddyfine] =
editor.AddConstant(rdcspv::OpConstantNull(valueType, editor.MakeId()));
editor.SetName(values[i].data[Variant_ddxcoarse],
StringFormat::Fmt("__rd_noderiv_%zu_%s", i, param.varName.c_str()));
}
}
rdcspv::Id structPtr = ssboVar;
if(structPtr == rdcspv::Id())
{
// if we don't have the struct as a bind, we need to cast it from the pointer. In
// KHR_buffer_device_address we bitcast since we store it as a uint2
if(storageMode == KHR_bda)
structPtr = ops.add(rdcspv::OpBitcast(bufptrtype, editor.MakeId(), addressConstant));
else
structPtr = ops.add(rdcspv::OpConvertUToPtr(bufptrtype, editor.MakeId(), addressConstant));
editor.SetName(structPtr, "HitBuffer");
}
rdcspv::Id uintPtr = editor.DeclareType(rdcspv::Pointer(uint32Type, bufferClass));
// get a pointer to buffer.hit_count
rdcspv::Id hit_count =
ops.add(rdcspv::OpAccessChain(uintPtr, editor.MakeId(), structPtr, {getUIntConst(0)}));
// get a pointer to buffer.total_count
rdcspv::Id total_count =
ops.add(rdcspv::OpAccessChain(uintPtr, editor.MakeId(), structPtr, {getUIntConst(1)}));
rdcspv::Id scope = editor.AddConstantImmediate<uint32_t>((uint32_t)rdcspv::Scope::Device);
rdcspv::Id semantics =
editor.AddConstantImmediate<uint32_t>((uint32_t)rdcspv::MemorySemantics::AcquireRelease);
// increment total_count
ops.add(rdcspv::OpAtomicIAdd(uint32Type, editor.MakeId(), total_count, scope, semantics,
getUIntConst(1)));
// look up the fragcoord
rdcspv::Id fragCoordLoaded = editor.MakeId();
if(fragCoord.member == ~0U)
{
ops.add(rdcspv::OpLoad(float4Type, fragCoordLoaded, fragCoord.base));
}
else
{
rdcspv::Id posptr =
ops.add(rdcspv::OpAccessChain(float4InPtr, editor.MakeId(), fragCoord.base,
{editor.AddConstantImmediate(fragCoord.member)}));
ops.add(rdcspv::OpLoad(float4Type, fragCoordLoaded, posptr));
}
rdcspv::Id fragCoord_ddx =
ops.add(rdcspv::OpDPdx(float4Type, editor.MakeId(), fragCoordLoaded));
rdcspv::Id bool2Type = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<bool>(), 2));
// grab x and y
rdcspv::Id fragXY = ops.add(rdcspv::OpVectorShuffle(
float2Type, editor.MakeId(), fragCoordLoaded, fragCoordLoaded, {0, 1}));
// subtract from the destination co-ord
rdcspv::Id fragXYRelative =
ops.add(rdcspv::OpFSub(float2Type, editor.MakeId(), fragXY, destXY));
// abs()
rdcspv::Id fragXYAbs = ops.add(rdcspv::OpGLSL450(float2Type, editor.MakeId(), glsl450,
rdcspv::GLSLstd450::FAbs, {fragXYRelative}));
rdcspv::Id half = editor.AddConstantImmediate<float>(0.5f);
rdcspv::Id threshold =
editor.AddConstant(rdcspv::OpConstantComposite(float2Type, editor.MakeId(), {half, half}));
// less than 0.5
rdcspv::Id inPixelXY =
ops.add(rdcspv::OpFOrdLessThan(bool2Type, editor.MakeId(), fragXYAbs, threshold));
// both less than 0.5
rdcspv::Id inPixel = ops.add(rdcspv::OpAll(boolType, editor.MakeId(), inPixelXY));
// bool inPixel = all(abs(gl_FragCoord.xy - dest.xy) < 0.5f);
rdcspv::Id killLabel = editor.MakeId();
rdcspv::Id continueLabel = editor.MakeId();
ops.add(rdcspv::OpSelectionMerge(killLabel, rdcspv::SelectionControl::None));
ops.add(rdcspv::OpBranchConditional(inPixel, continueLabel, killLabel));
ops.add(rdcspv::OpLabel(continueLabel));
// allocate a slot with atomic add
rdcspv::Id slot = ops.add(rdcspv::OpAtomicIAdd(uint32Type, editor.MakeId(), hit_count, scope,
semantics, getUIntConst(1)));
editor.SetName(slot, "slot");
rdcspv::Id inRange = ops.add(rdcspv::OpULessThan(boolType, editor.MakeId(), slot, arrayLength));
rdcspv::Id killLabel2 = editor.MakeId();
continueLabel = editor.MakeId();
ops.add(rdcspv::OpSelectionMerge(killLabel2, rdcspv::SelectionControl::None));
ops.add(rdcspv::OpBranchConditional(inRange, continueLabel, killLabel2));
ops.add(rdcspv::OpLabel(continueLabel));
rdcspv::Id hitptr = editor.DeclareType(rdcspv::Pointer(PSHit, bufferClass));
// get a pointer to the hit for our slot
rdcspv::Id hit =
ops.add(rdcspv::OpAccessChain(hitptr, editor.MakeId(), structPtr, {getUIntConst(2), slot}));
// store fixed properties
rdcspv::Id storePtr =
ops.add(rdcspv::OpAccessChain(float4BufPtr, editor.MakeId(), hit, {getUIntConst(0)}));
ops.add(rdcspv::OpStore(storePtr, fragCoordLoaded, alignedAccess));
rdcspv::Id loaded;
if(primitiveID.base != rdcspv::Id())
{
if(primitiveID.member == ~0U)
{
loaded = ops.add(rdcspv::OpLoad(primitiveID.type, editor.MakeId(), primitiveID.base));
}
else
{
rdcspv::Id inPtrType =
editor.DeclareType(rdcspv::Pointer(primitiveID.type, rdcspv::StorageClass::Input));
rdcspv::Id posptr =
ops.add(rdcspv::OpAccessChain(inPtrType, editor.MakeId(), primitiveID.base,
{editor.AddConstantImmediate(primitiveID.member)}));
loaded = ops.add(rdcspv::OpLoad(primitiveID.type, editor.MakeId(), posptr));
}
// if it was loaded as signed int by the shader and not as unsigned by us, bitcast to
// unsigned.
if(primitiveID.type != uint32Type)
loaded = ops.add(rdcspv::OpBitcast(uint32Type, editor.MakeId(), loaded));
}
else
{
// explicitly store 0
loaded = getUIntConst(0);
}
storePtr =
ops.add(rdcspv::OpAccessChain(uint32BufPtr, editor.MakeId(), hit, {getUIntConst(1)}));
ops.add(rdcspv::OpStore(storePtr, loaded, alignedAccess));
if(sampleIndex.base != rdcspv::Id())
{
if(sampleIndex.member == ~0U)
{
loaded = ops.add(rdcspv::OpLoad(sampleIndex.type, editor.MakeId(), sampleIndex.base));
}
else
{
rdcspv::Id inPtrType =
editor.DeclareType(rdcspv::Pointer(sampleIndex.type, rdcspv::StorageClass::Input));
rdcspv::Id posptr =
ops.add(rdcspv::OpAccessChain(inPtrType, editor.MakeId(), sampleIndex.base,
{editor.AddConstantImmediate(sampleIndex.member)}));
loaded = ops.add(rdcspv::OpLoad(sampleIndex.type, editor.MakeId(), posptr));
}
// if it was loaded as signed int by the shader and not as unsigned by us, bitcast to
// unsigned.
if(sampleIndex.type != uint32Type)
loaded = ops.add(rdcspv::OpBitcast(uint32Type, editor.MakeId(), loaded));
}
else
{
// explicitly store 0
loaded = getUIntConst(0);
}
storePtr =
ops.add(rdcspv::OpAccessChain(uint32BufPtr, editor.MakeId(), hit, {getUIntConst(2)}));
ops.add(rdcspv::OpStore(storePtr, loaded, alignedAccess));
if(viewIndex.base != rdcspv::Id())
{
if(viewIndex.member == ~0U)
{
loaded = ops.add(rdcspv::OpLoad(viewIndex.type, editor.MakeId(), viewIndex.base));
}
else
{
rdcspv::Id inPtrType =
editor.DeclareType(rdcspv::Pointer(viewIndex.type, rdcspv::StorageClass::Input));
rdcspv::Id viewidxptr =
ops.add(rdcspv::OpAccessChain(inPtrType, editor.MakeId(), viewIndex.base,
{editor.AddConstantImmediate(viewIndex.member)}));
loaded = ops.add(rdcspv::OpLoad(viewIndex.type, editor.MakeId(), viewidxptr));
}
// if it was loaded as signed int by the shader and not as unsigned by us, bitcast to
// unsigned.
if(viewIndex.type != uint32Type)
loaded = ops.add(rdcspv::OpBitcast(uint32Type, editor.MakeId(), loaded));
}
else
{
// explicitly store 0
loaded = getUIntConst(0);
}
storePtr =
ops.add(rdcspv::OpAccessChain(uint32BufPtr, editor.MakeId(), hit, {getUIntConst(3)}));
ops.add(rdcspv::OpStore(storePtr, loaded, alignedAccess));
storePtr =
ops.add(rdcspv::OpAccessChain(uint32BufPtr, editor.MakeId(), hit, {getUIntConst(4)}));
ops.add(rdcspv::OpStore(storePtr, editor.AddConstantImmediate(validMagicNumber), alignedAccess));
// store ddx(gl_FragCoord.x) to check that derivatives are working
storePtr = ops.add(rdcspv::OpAccessChain(floatBufPtr, editor.MakeId(), hit, {getUIntConst(5)}));
rdcspv::Id fragCoord_ddx_x =
ops.add(rdcspv::OpCompositeExtract(floatType, editor.MakeId(), fragCoord_ddx, {0}));
ops.add(rdcspv::OpStore(storePtr, fragCoord_ddx_x, alignedAccess));
{
rdcspv::Id inputPtrType = editor.DeclareType(rdcspv::Pointer(PSInput, bufferClass));
rdcspv::Id outputPtrs[Variant_Count] = {
ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(6)})),
ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(7)})),
ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(8)})),
ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(9)})),
ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(10)})),
};
for(size_t i = 0; i < values.size(); i++)
{
rdcspv::Id valueType = values[i].valueType;
if(valueType == boolType)
valueType = uint32Type;
rdcspv::Id ptrType = editor.DeclareType(rdcspv::Pointer(valueType, bufferClass));
for(size_t j = 0; j < Variant_Count; j++)
{
rdcspv::Id ptr = ops.add(rdcspv::OpAccessChain(ptrType, editor.MakeId(), outputPtrs[j],
{getUIntConst(values[i].structIndex)}));
ops.add(rdcspv::OpStore(ptr, values[i].data[j], alignedAccess));
}
}
}
// join up with the early-outs we did
ops.add(rdcspv::OpBranch(killLabel2));
ops.add(rdcspv::OpLabel(killLabel2));
ops.add(rdcspv::OpBranch(killLabel));
ops.add(rdcspv::OpLabel(killLabel));
}
// don't return, kill. This makes it well-defined that we don't write anything to our outputs
ops.add(rdcspv::OpKill());
ops.add(rdcspv::OpFunctionEnd());
editor.AddFunction(ops);
}
}