in renderdoc/driver/vulkan/vk_shaderdebug.cpp [2223:2937]
void GenerateSamplingShaderModule(rdcarray<uint32_t> &spirv, bool depthTex, bool uintTex,
bool sintTex)
{
// this could be done as a glsl shader, but glslang has some bugs compiling the specialisation
// constants, so we generate it by hand - which isn't too hard
rdcspv::Editor editor(spirv);
// create as SPIR-V 1.0 for best compatibility
editor.CreateEmpty(1, 0);
editor.AddCapability(rdcspv::Capability::Shader);
editor.AddCapability(rdcspv::Capability::ImageQuery);
editor.AddCapability(rdcspv::Capability::Sampled1D);
editor.AddCapability(rdcspv::Capability::SampledBuffer);
if(m_pDriver->GetDeviceEnabledFeatures().shaderResourceMinLod)
editor.AddCapability(rdcspv::Capability::MinLod);
if(m_pDriver->GetDeviceEnabledFeatures().shaderImageGatherExtended)
editor.AddCapability(rdcspv::Capability::ImageGatherExtended);
const bool cubeArray = (m_pDriver->GetDeviceEnabledFeatures().imageCubeArray != VK_FALSE);
rdcspv::Id entryId = editor.MakeId();
editor.AddOperation(
editor.Begin(rdcspv::Section::MemoryModel),
rdcspv::OpMemoryModel(rdcspv::AddressingModel::Logical, rdcspv::MemoryModel::GLSL450));
rdcspv::Id u32 = editor.DeclareType(rdcspv::scalar<uint32_t>());
rdcspv::Id i32 = editor.DeclareType(rdcspv::scalar<int32_t>());
rdcspv::Id f32 = editor.DeclareType(rdcspv::scalar<float>());
rdcspv::Id v2i32 = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<int32_t>(), 2));
rdcspv::Id v3i32 = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<int32_t>(), 3));
rdcspv::Id v2f32 = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<float>(), 2));
rdcspv::Id v3f32 = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<float>(), 3));
rdcspv::Id v4f32 = editor.DeclareType(rdcspv::Vector(rdcspv::scalar<float>(), 4));
// int2[4]
rdcspv::Id a4v2i32 = editor.AddType(
rdcspv::OpTypeArray(editor.MakeId(), v2i32, editor.AddConstantImmediate<uint32_t>(4)));
rdcspv::Scalar base = rdcspv::scalar<float>();
if(uintTex)
base = rdcspv::scalar<uint32_t>();
else if(sintTex)
base = rdcspv::scalar<int32_t>();
rdcspv::Id resultType = editor.DeclareType(rdcspv::Vector(base, 4));
rdcspv::Id scalarResultType = editor.DeclareType(base);
// add specialisation constants for all the parameters
#define MEMBER_IDX(struct, name) uint32_t(offsetof(struct, name) / sizeof(uint32_t))
#define DECL_SPECID(type, name, value) \
rdcspv::Id name = \
editor.AddSpecConstantImmediate<type>(0U, MEMBER_IDX(ShaderConstParameters, value)); \
editor.SetName(name, "spec_" #name);
DECL_SPECID(uint32_t, operation, operation);
DECL_SPECID(bool, useGradOrGatherOffsets, useGradOrGatherOffsets);
DECL_SPECID(uint32_t, dim, dim);
DECL_SPECID(int32_t, gatherChannel, gatherChannel);
DECL_SPECID(int32_t, gather_u0, gatherOffsets.u0);
DECL_SPECID(int32_t, gather_v0, gatherOffsets.v0);
DECL_SPECID(int32_t, gather_u1, gatherOffsets.u1);
DECL_SPECID(int32_t, gather_v1, gatherOffsets.v1);
DECL_SPECID(int32_t, gather_u2, gatherOffsets.u2);
DECL_SPECID(int32_t, gather_v2, gatherOffsets.v2);
DECL_SPECID(int32_t, gather_u3, gatherOffsets.u3);
DECL_SPECID(int32_t, gather_v3, gatherOffsets.v3);
struct StructMember
{
rdcspv::Id loadedType;
rdcspv::Id ptrType;
rdcspv::Id loadedId;
const char *name;
uint32_t memberIndex;
};
rdcarray<rdcspv::Id> memberIds;
rdcarray<StructMember> cbufferMembers;
rdcspv::Id type_int32_t = editor.DeclareType(rdcspv::scalar<int32_t>());
rdcspv::Id type_float = editor.DeclareType(rdcspv::scalar<float>());
rdcspv::Id uniformptr_int32_t =
editor.DeclareType(rdcspv::Pointer(type_int32_t, rdcspv::StorageClass::Uniform));
rdcspv::Id uniformptr_float =
editor.DeclareType(rdcspv::Pointer(type_float, rdcspv::StorageClass::Uniform));
#define DECL_UNIFORM(type, name, value) \
rdcspv::Id name = editor.MakeId(); \
editor.SetName(name, "uniform_" #name); \
cbufferMembers.push_back({CONCAT(type_, type), CONCAT(uniformptr_, type), name, #name, \
MEMBER_IDX(ShaderUniformParameters, value)}); \
memberIds.push_back(CONCAT(type_, type));
DECL_UNIFORM(int32_t, texel_u, texel_uvw.x);
DECL_UNIFORM(int32_t, texel_v, texel_uvw.y);
DECL_UNIFORM(int32_t, texel_w, texel_uvw.z);
DECL_UNIFORM(int32_t, texel_lod, texel_lod);
DECL_UNIFORM(float, u, uvwa[0]);
DECL_UNIFORM(float, v, uvwa[1]);
DECL_UNIFORM(float, w, uvwa[2]);
DECL_UNIFORM(float, cube_a, uvwa[3]);
DECL_UNIFORM(float, dudx, ddx[0]);
DECL_UNIFORM(float, dvdx, ddx[1]);
DECL_UNIFORM(float, dwdx, ddx[2]);
DECL_UNIFORM(float, dudy, ddy[0]);
DECL_UNIFORM(float, dvdy, ddy[1]);
DECL_UNIFORM(float, dwdy, ddy[2]);
DECL_UNIFORM(int32_t, dynoffset_u, offset.x);
DECL_UNIFORM(int32_t, dynoffset_v, offset.y);
DECL_UNIFORM(int32_t, dynoffset_w, offset.z);
DECL_UNIFORM(int32_t, sampleIdx, sampleIdx);
DECL_UNIFORM(float, compare, compare);
DECL_UNIFORM(float, lod, lod);
DECL_UNIFORM(float, minlod, minlod);
rdcspv::Id cbufferStructID = editor.AddType(rdcspv::OpTypeStruct(editor.MakeId(), memberIds));
editor.AddDecoration(rdcspv::OpDecorate(cbufferStructID, rdcspv::Decoration::Block));
for(const StructMember &m : cbufferMembers)
{
editor.AddDecoration(rdcspv::OpMemberDecorate(
cbufferStructID, m.memberIndex,
rdcspv::DecorationParam<rdcspv::Decoration::Offset>(m.memberIndex * sizeof(uint32_t))));
editor.SetMemberName(cbufferStructID, m.memberIndex, m.name);
}
rdcspv::Id constoffset_u = gather_u0;
rdcspv::Id constoffset_uv = editor.AddConstant(
rdcspv::OpSpecConstantComposite(v2i32, editor.MakeId(), {gather_u0, gather_v0}));
rdcspv::Id constoffset_uvw = editor.AddConstant(
rdcspv::OpSpecConstantComposite(v3i32, editor.MakeId(), {gather_u0, gather_v0, gather_u1}));
editor.SetName(constoffset_u, "constoffset_u");
editor.SetName(constoffset_uv, "constoffset_uv");
editor.SetName(constoffset_uvw, "constoffset_uvw");
rdcspv::Id gather_0 = editor.AddConstant(
rdcspv::OpSpecConstantComposite(v2i32, editor.MakeId(), {gather_u0, gather_v0}));
rdcspv::Id gather_1 = editor.AddConstant(
rdcspv::OpSpecConstantComposite(v2i32, editor.MakeId(), {gather_u1, gather_v1}));
rdcspv::Id gather_2 = editor.AddConstant(
rdcspv::OpSpecConstantComposite(v2i32, editor.MakeId(), {gather_u2, gather_v2}));
rdcspv::Id gather_3 = editor.AddConstant(
rdcspv::OpSpecConstantComposite(v2i32, editor.MakeId(), {gather_u3, gather_v3}));
rdcspv::Id gatherOffsets = editor.AddConstant(rdcspv::OpSpecConstantComposite(
a4v2i32, editor.MakeId(), {gather_0, gather_1, gather_2, gather_3}));
editor.SetName(gatherOffsets, "gatherOffsets");
// create the output. It's always a 4-wide vector
rdcspv::Id outPtrType =
editor.DeclareType(rdcspv::Pointer(resultType, rdcspv::StorageClass::Output));
rdcspv::Id outVar = editor.AddVariable(
rdcspv::OpVariable(outPtrType, editor.MakeId(), rdcspv::StorageClass::Output));
editor.AddDecoration(
rdcspv::OpDecorate(outVar, rdcspv::DecorationParam<rdcspv::Decoration::Location>(0)));
editor.SetName(outVar, "output");
rdcspv::ImageFormat unk = rdcspv::ImageFormat::Unknown;
// create the five textures and sampler
rdcspv::Id texSampTypes[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
editor.DeclareType(rdcspv::Image(base, rdcspv::Dim::_1D, 0, 1, 0, 1, unk)),
editor.DeclareType(rdcspv::Image(base, rdcspv::Dim::_2D, 0, 1, 0, 1, unk)),
editor.DeclareType(rdcspv::Image(base, rdcspv::Dim::_3D, 0, 0, 0, 1, unk)),
editor.DeclareType(rdcspv::Image(base, rdcspv::Dim::_2D, 0, 1, 1, 1, unk)),
editor.DeclareType(rdcspv::Image(base, rdcspv::Dim::Cube, 0, cubeArray ? 1 : 0, 0, 1, unk)),
editor.DeclareType(rdcspv::Image(base, rdcspv::Dim::Buffer, 0, 0, 0, 1, unk)),
editor.DeclareType(rdcspv::Sampler()),
cbufferStructID,
};
rdcspv::Id bindVars[(uint32_t)ShaderDebugBind::Count];
rdcspv::Id texSampCombinedTypes[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
editor.DeclareType(rdcspv::SampledImage(texSampTypes[1])),
editor.DeclareType(rdcspv::SampledImage(texSampTypes[2])),
editor.DeclareType(rdcspv::SampledImage(texSampTypes[3])),
editor.DeclareType(rdcspv::SampledImage(texSampTypes[4])),
editor.DeclareType(rdcspv::SampledImage(texSampTypes[5])),
editor.DeclareType(rdcspv::SampledImage(texSampTypes[6])),
rdcspv::Id(),
rdcspv::Id(),
};
for(size_t i = (size_t)ShaderDebugBind::First; i < (size_t)ShaderDebugBind::Count; i++)
{
rdcspv::StorageClass storageClass = rdcspv::StorageClass::UniformConstant;
if(depthTex)
{
if(i == (size_t)ShaderDebugBind::Tex3D && !m_pDriver->GetReplay()->Depth3DSupported())
continue;
else if(i == (size_t)ShaderDebugBind::TexCube && !m_pDriver->GetReplay()->DepthCubeSupported())
continue;
}
if(i == (size_t)ShaderDebugBind::Constants)
storageClass = rdcspv::StorageClass::Uniform;
rdcspv::Id ptrType = editor.DeclareType(rdcspv::Pointer(texSampTypes[i], storageClass));
bindVars[i] = editor.AddVariable(rdcspv::OpVariable(ptrType, editor.MakeId(), storageClass));
editor.AddDecoration(rdcspv::OpDecorate(
bindVars[i], rdcspv::DecorationParam<rdcspv::Decoration::DescriptorSet>(0U)));
editor.AddDecoration(rdcspv::OpDecorate(
bindVars[i], rdcspv::DecorationParam<rdcspv::Decoration::Binding>((uint32_t)i)));
}
editor.SetName(bindVars[(size_t)ShaderDebugBind::Tex1D], "Tex1D");
editor.SetName(bindVars[(size_t)ShaderDebugBind::Tex2D], "Tex2D");
if(bindVars[(size_t)ShaderDebugBind::Tex3D] != rdcspv::Id())
editor.SetName(bindVars[(size_t)ShaderDebugBind::Tex3D], "Tex3D");
editor.SetName(bindVars[(size_t)ShaderDebugBind::Tex2DMS], "Tex2DMS");
if(bindVars[(size_t)ShaderDebugBind::TexCube] != rdcspv::Id())
editor.SetName(bindVars[(size_t)ShaderDebugBind::TexCube], "TexCube");
editor.SetName(bindVars[(size_t)ShaderDebugBind::Buffer], "Buffer");
editor.SetName(bindVars[(size_t)ShaderDebugBind::Sampler], "Sampler");
editor.SetName(bindVars[(size_t)ShaderDebugBind::Constants], "CBuffer");
rdcspv::Id uvwa_ptr = editor.DeclareType(rdcspv::Pointer(v4f32, rdcspv::StorageClass::Input));
rdcspv::Id input_uvwa_var = editor.AddVariable(
rdcspv::OpVariable(uvwa_ptr, editor.MakeId(), rdcspv::StorageClass::Input));
editor.AddDecoration(rdcspv::OpDecorate(
input_uvwa_var, rdcspv::DecorationParam<rdcspv::Decoration::Location>(0)));
editor.SetName(input_uvwa_var, "input_uvwa");
// register the entry point
editor.AddOperation(editor.Begin(rdcspv::Section::EntryPoints),
rdcspv::OpEntryPoint(rdcspv::ExecutionModel::Fragment, entryId, "main",
{input_uvwa_var, outVar}));
editor.AddExecutionMode(rdcspv::OpExecutionMode(entryId, rdcspv::ExecutionMode::OriginUpperLeft));
rdcspv::Id voidType = editor.DeclareType(rdcspv::scalar<void>());
rdcspv::Id funcType = editor.DeclareType(rdcspv::FunctionType(voidType, {}));
rdcspv::OperationList func;
func.add(rdcspv::OpFunction(voidType, entryId, rdcspv::FunctionControl::None, funcType));
func.add(rdcspv::OpLabel(editor.MakeId()));
// access chain and load all the cbuffer variables
for(const StructMember &m : cbufferMembers)
{
rdcspv::Id ptr = func.add(rdcspv::OpAccessChain(
m.ptrType, editor.MakeId(), bindVars[(size_t)ShaderDebugBind::Constants],
{editor.AddConstantImmediate<uint32_t>(m.memberIndex)}));
func.add(rdcspv::OpLoad(m.loadedType, m.loadedId, ptr));
}
// declare cbuffer composites
rdcspv::Id texel_uv =
func.add(rdcspv::OpCompositeConstruct(v2i32, editor.MakeId(), {texel_u, texel_v}));
rdcspv::Id texel_uvw =
func.add(rdcspv::OpCompositeConstruct(v3i32, editor.MakeId(), {texel_u, texel_v, texel_w}));
editor.SetName(texel_uv, "texel_uv");
editor.SetName(texel_uvw, "texel_uvw");
rdcspv::Id uv = func.add(rdcspv::OpCompositeConstruct(v2f32, editor.MakeId(), {u, v}));
rdcspv::Id uvw = func.add(rdcspv::OpCompositeConstruct(v3f32, editor.MakeId(), {u, v, w}));
rdcspv::Id uvwa =
func.add(rdcspv::OpCompositeConstruct(v4f32, editor.MakeId(), {u, v, w, cube_a}));
editor.SetName(uv, "uv");
editor.SetName(uvw, "uvw");
editor.SetName(uvwa, "uvwa");
rdcspv::Id ddx_uv = func.add(rdcspv::OpCompositeConstruct(v2f32, editor.MakeId(), {dudx, dvdx}));
rdcspv::Id ddx_uvw =
func.add(rdcspv::OpCompositeConstruct(v3f32, editor.MakeId(), {dudx, dvdx, dwdx}));
editor.SetName(ddx_uv, "ddx_uv");
editor.SetName(ddx_uvw, "ddx_uvw");
rdcspv::Id ddy_uv = func.add(rdcspv::OpCompositeConstruct(v2f32, editor.MakeId(), {dudy, dvdy}));
rdcspv::Id ddy_uvw =
func.add(rdcspv::OpCompositeConstruct(v3f32, editor.MakeId(), {dudy, dvdy, dwdy}));
editor.SetName(ddy_uv, "ddy_uv");
editor.SetName(ddy_uvw, "ddy_uvw");
rdcspv::Id dynoffset_uv =
func.add(rdcspv::OpCompositeConstruct(v2i32, editor.MakeId(), {dynoffset_u, dynoffset_v}));
rdcspv::Id dynoffset_uvw = func.add(rdcspv::OpCompositeConstruct(
v3i32, editor.MakeId(), {dynoffset_u, dynoffset_v, dynoffset_w}));
editor.SetName(dynoffset_uv, "dynoffset_uv");
editor.SetName(dynoffset_uvw, "dynoffset_uvw");
rdcspv::Id input_uvwa = func.add(rdcspv::OpLoad(v4f32, editor.MakeId(), input_uvwa_var));
rdcspv::Id input_uvw =
func.add(rdcspv::OpVectorShuffle(v3f32, editor.MakeId(), input_uvwa, input_uvwa, {0, 1, 2}));
rdcspv::Id input_uv =
func.add(rdcspv::OpVectorShuffle(v2f32, editor.MakeId(), input_uvw, input_uvw, {0, 1}));
rdcspv::Id input_u = func.add(rdcspv::OpCompositeExtract(f32, editor.MakeId(), input_uvw, {0}));
// first store NULL data in, so the output is always initialised
rdcspv::Id breakLabel = editor.MakeId();
rdcspv::Id defaultLabel = editor.MakeId();
// combine the operation with the image type:
// operation * 10 + dim
RDCCOMPILE_ASSERT(size_t(ShaderDebugBind::Count) < 10, "Combining value ranges will overlap!");
rdcspv::Id switchVal = func.add(rdcspv::OpIMul(u32, editor.MakeId(), operation,
editor.AddConstantImmediate<uint32_t>(10U)));
switchVal = func.add(rdcspv::OpIAdd(u32, editor.MakeId(), switchVal, dim));
// switch on the combined operation and image type value
rdcarray<rdcspv::SwitchPairU32LiteralId> targets;
rdcspv::OperationList cases;
rdcspv::Id texel_coord[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
texel_uv, // 1D - u and array
texel_uvw, // 2D - u,v and array
texel_uvw, // 3D - u,v,w
texel_uvw, // 2DMS - u,v and array
rdcspv::Id(), // Cube
texel_u, // Buffer - u
};
// only used for QueryLod, so we can ignore MSAA/Buffer
rdcspv::Id input_coord[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
input_u, // 1D - u
input_uv, // 2D - u,v
input_uvw, // 3D - u,v,w
rdcspv::Id(), // 2DMS
input_uvw, // Cube - u,v,w
rdcspv::Id(), // Buffer
};
rdcspv::Id coord[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
uv, // 1D - u and array
uvw, // 2D - u,v and array
uvw, // 3D - u,v,w
uvw, // 2DMS - u,v and array
cubeArray ? uvwa : uvw, // Cube - u,v,w and array (if supported)
u, // Buffer - u
};
rdcspv::Id constoffset[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
constoffset_u, // 1D - u
constoffset_uv, // 2D - u,v
constoffset_uvw, // 3D - u,v,w
constoffset_uv, // 2DMS - u,v
rdcspv::Id(), // Cube - not valid
constoffset_u, // Buffer - u
};
rdcspv::Id dynoffset[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
dynoffset_u, // 1D - u
dynoffset_uv, // 2D - u,v
dynoffset_uvw, // 3D - u,v,w
dynoffset_uv, // 2DMS - u,v
rdcspv::Id(), // Cube - not valid
dynoffset_u, // Buffer - u
};
rdcspv::Id ddxs[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
dudx, // 1D - u
ddx_uv, // 2D - u,v
ddx_uvw, // 3D - u,v,w
ddx_uv, // 2DMS - u,v
ddx_uvw, // Cube - u,v,w
dudx, // Buffer - u
};
rdcspv::Id ddys[(uint32_t)ShaderDebugBind::Count] = {
rdcspv::Id(),
dudy, // 1D - u
ddy_uv, // 2D - u,v
ddy_uvw, // 3D - u,v,w
ddy_uv, // 2DMS - u,v
ddy_uvw, // Cube - u,v,w
dudy, // Buffer - u
};
uint32_t sampIdx = (uint32_t)ShaderDebugBind::Sampler;
rdcspv::Id zerof = editor.AddConstantImmediate<float>(0.0f);
for(uint32_t i = (uint32_t)ShaderDebugBind::First; i < (uint32_t)ShaderDebugBind::Count; i++)
{
if(i == sampIdx || i == (uint32_t)ShaderDebugBind::Constants)
continue;
if(bindVars[i] == rdcspv::Id())
continue;
rdcspv::ImageOperandsAndParamDatas imageOperandsWithOffsets;
// most operations support offsets, set the operands commonly here.
// with the shaderImageGatherExtended feature, gather opcodes will always get their operands
// via uniforms to cut down on pipeline specialisations a little, but all other cases the
// offsets must be constant.
if(constoffset[i] != rdcspv::Id())
imageOperandsWithOffsets.setConstOffset(constoffset[i]);
// can't fetch from cubemaps
if(i != (uint32_t)ShaderDebugBind::TexCube)
{
rdcspv::Op op = rdcspv::Op::ImageFetch;
rdcspv::Id label = editor.MakeId();
targets.push_back({(uint32_t)op * 10 + i, label});
rdcspv::ImageOperandsAndParamDatas operands = imageOperandsWithOffsets;
if(i != (uint32_t)ShaderDebugBind::Buffer && i != (uint32_t)ShaderDebugBind::Tex2DMS)
operands.setLod(texel_lod);
if(i == (uint32_t)ShaderDebugBind::Tex2DMS)
operands.setSample(sampleIdx);
cases.add(rdcspv::OpLabel(label));
rdcspv::Id loaded = cases.add(rdcspv::OpLoad(texSampTypes[i], editor.MakeId(), bindVars[i]));
rdcspv::Id sampleResult = cases.add(
rdcspv::OpImageFetch(resultType, editor.MakeId(), loaded, texel_coord[i], operands));
cases.add(rdcspv::OpStore(outVar, sampleResult));
cases.add(rdcspv::OpBranch(breakLabel));
}
// buffers and multisampled images don't support sampling, so skip the other operations at
// this point
if(i == (uint32_t)ShaderDebugBind::Buffer || i == (uint32_t)ShaderDebugBind::Tex2DMS)
continue;
{
rdcspv::Op op = rdcspv::Op::ImageQueryLod;
rdcspv::Id label = editor.MakeId();
targets.push_back({(uint32_t)op * 10 + i, label});
cases.add(rdcspv::OpLabel(label));
rdcspv::Id loadedImage =
cases.add(rdcspv::OpLoad(texSampTypes[i], editor.MakeId(), bindVars[i]));
rdcspv::Id loadedSampler =
cases.add(rdcspv::OpLoad(texSampTypes[sampIdx], editor.MakeId(), bindVars[sampIdx]));
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
rdcspv::Id sampleResult =
cases.add(rdcspv::OpImageQueryLod(v2f32, editor.MakeId(), combined, input_coord[i]));
sampleResult = cases.add(rdcspv::OpVectorShuffle(v4f32, editor.MakeId(), sampleResult,
sampleResult, {0, 1, 0, 1}));
// if we're sampling from an integer texture the output variable will be the same type.
// Just bitcast the float bits into it, which will come out the other side the right type.
if(uintTex || sintTex)
sampleResult = cases.add(rdcspv::OpBitcast(resultType, editor.MakeId(), sampleResult));
cases.add(rdcspv::OpStore(outVar, sampleResult));
cases.add(rdcspv::OpBranch(breakLabel));
}
for(rdcspv::Op op : {rdcspv::Op::ImageSampleExplicitLod, rdcspv::Op::ImageSampleImplicitLod})
{
rdcspv::Id label = editor.MakeId();
targets.push_back({(uint32_t)op * 10 + i, label});
cases.add(rdcspv::OpLabel(label));
rdcspv::Id loadedImage =
cases.add(rdcspv::OpLoad(texSampTypes[i], editor.MakeId(), bindVars[i]));
rdcspv::Id loadedSampler =
cases.add(rdcspv::OpLoad(texSampTypes[sampIdx], editor.MakeId(), bindVars[sampIdx]));
rdcspv::Id mergeLabel = editor.MakeId();
rdcspv::Id gradCase = editor.MakeId();
rdcspv::Id lodCase = editor.MakeId();
cases.add(rdcspv::OpSelectionMerge(mergeLabel, rdcspv::SelectionControl::None));
cases.add(rdcspv::OpBranchConditional(useGradOrGatherOffsets, gradCase, lodCase));
rdcspv::Id lodResult;
{
cases.add(rdcspv::OpLabel(lodCase));
rdcspv::ImageOperandsAndParamDatas operands = imageOperandsWithOffsets;
operands.setLod(lod);
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
lodResult = cases.add(rdcspv::OpImageSampleExplicitLod(resultType, editor.MakeId(),
combined, coord[i], operands));
cases.add(rdcspv::OpBranch(mergeLabel));
}
rdcspv::Id gradResult;
{
cases.add(rdcspv::OpLabel(gradCase));
rdcspv::ImageOperandsAndParamDatas operands = imageOperandsWithOffsets;
operands.setGrad(ddxs[i], ddys[i]);
if(m_pDriver->GetDeviceEnabledFeatures().shaderResourceMinLod)
operands.setMinLod(minlod);
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
gradResult = cases.add(rdcspv::OpImageSampleExplicitLod(resultType, editor.MakeId(),
combined, coord[i], operands));
cases.add(rdcspv::OpBranch(mergeLabel));
}
cases.add(rdcspv::OpLabel(mergeLabel));
rdcspv::Id sampleResult = cases.add(rdcspv::OpPhi(
resultType, editor.MakeId(), {{lodResult, lodCase}, {gradResult, gradCase}}));
cases.add(rdcspv::OpStore(outVar, sampleResult));
cases.add(rdcspv::OpBranch(breakLabel));
}
// on Qualcomm we only emit Dref instructions against 2D textures, otherwise the compiler may
// crash.
if(m_pDriver->GetDriverInfo().QualcommDrefNon2DCompileCrash())
depthTex &= (i == (uint32_t)ShaderDebugBind::Tex2D);
// VUID-StandaloneSpirv-OpImage-04777
// OpImage*Dref must not consume an image whose Dim is 3D
if(i == (uint32_t)ShaderDebugBind::Tex3D)
depthTex = false;
// don't emit dref's for uint/sint textures
if(uintTex || sintTex)
depthTex = false;
if(depthTex)
{
for(rdcspv::Op op :
{rdcspv::Op::ImageSampleDrefExplicitLod, rdcspv::Op::ImageSampleDrefImplicitLod})
{
rdcspv::Id label = editor.MakeId();
targets.push_back({(uint32_t)op * 10 + i, label});
cases.add(rdcspv::OpLabel(label));
rdcspv::Id loadedImage =
cases.add(rdcspv::OpLoad(texSampTypes[i], editor.MakeId(), bindVars[i]));
rdcspv::Id loadedSampler =
cases.add(rdcspv::OpLoad(texSampTypes[sampIdx], editor.MakeId(), bindVars[sampIdx]));
rdcspv::Id mergeLabel = editor.MakeId();
rdcspv::Id gradCase = editor.MakeId();
rdcspv::Id lodCase = editor.MakeId();
cases.add(rdcspv::OpSelectionMerge(mergeLabel, rdcspv::SelectionControl::None));
cases.add(rdcspv::OpBranchConditional(useGradOrGatherOffsets, gradCase, lodCase));
rdcspv::Id lodResult;
{
cases.add(rdcspv::OpLabel(lodCase));
rdcspv::ImageOperandsAndParamDatas operands = imageOperandsWithOffsets;
operands.setLod(lod);
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
lodResult = cases.add(rdcspv::OpImageSampleDrefExplicitLod(
scalarResultType, editor.MakeId(), combined, coord[i], compare, operands));
cases.add(rdcspv::OpBranch(mergeLabel));
}
rdcspv::Id gradResult;
{
cases.add(rdcspv::OpLabel(gradCase));
rdcspv::ImageOperandsAndParamDatas operands = imageOperandsWithOffsets;
operands.setGrad(ddxs[i], ddys[i]);
if(m_pDriver->GetDeviceEnabledFeatures().shaderResourceMinLod)
operands.setMinLod(minlod);
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
gradResult = cases.add(rdcspv::OpImageSampleDrefExplicitLod(
scalarResultType, editor.MakeId(), combined, coord[i], compare, operands));
cases.add(rdcspv::OpBranch(mergeLabel));
}
cases.add(rdcspv::OpLabel(mergeLabel));
rdcspv::Id scalarSampleResult = cases.add(rdcspv::OpPhi(
scalarResultType, editor.MakeId(), {{lodResult, lodCase}, {gradResult, gradCase}}));
rdcspv::Id sampleResult = cases.add(rdcspv::OpCompositeConstruct(
resultType, editor.MakeId(), {scalarSampleResult, zerof, zerof, zerof}));
cases.add(rdcspv::OpStore(outVar, sampleResult));
cases.add(rdcspv::OpBranch(breakLabel));
}
}
// can only gather with 2D/Cube textures
if(i == (uint32_t)ShaderDebugBind::Tex1D || i == (uint32_t)ShaderDebugBind::Tex3D)
continue;
for(rdcspv::Op op : {rdcspv::Op::ImageGather, rdcspv::Op::ImageDrefGather})
{
if(op == rdcspv::Op::ImageDrefGather && !depthTex)
continue;
rdcspv::Id label = editor.MakeId();
targets.push_back({(uint32_t)op * 10 + i, label});
cases.add(rdcspv::OpLabel(label));
rdcspv::Id loadedImage =
cases.add(rdcspv::OpLoad(texSampTypes[i], editor.MakeId(), bindVars[i]));
rdcspv::Id loadedSampler =
cases.add(rdcspv::OpLoad(texSampTypes[sampIdx], editor.MakeId(), bindVars[sampIdx]));
rdcspv::Id sampleResult;
if(m_pDriver->GetDeviceEnabledFeatures().shaderImageGatherExtended)
{
rdcspv::Id mergeLabel = editor.MakeId();
rdcspv::Id constsCase = editor.MakeId();
rdcspv::Id baseCase = editor.MakeId();
cases.add(rdcspv::OpSelectionMerge(mergeLabel, rdcspv::SelectionControl::None));
cases.add(rdcspv::OpBranchConditional(useGradOrGatherOffsets, constsCase, baseCase));
rdcspv::Id baseResult;
{
cases.add(rdcspv::OpLabel(baseCase));
rdcspv::ImageOperandsAndParamDatas operands;
if(dynoffset[i] != rdcspv::Id())
operands.setOffset(dynoffset[i]);
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
if(op == rdcspv::Op::ImageGather)
baseResult = cases.add(rdcspv::OpImageGather(resultType, editor.MakeId(), combined,
coord[i], gatherChannel, operands));
else
baseResult = cases.add(rdcspv::OpImageDrefGather(
resultType, editor.MakeId(), combined, coord[i], compare, operands));
cases.add(rdcspv::OpBranch(mergeLabel));
}
rdcspv::Id constsResult;
{
cases.add(rdcspv::OpLabel(constsCase));
rdcspv::ImageOperandsAndParamDatas operands; // don't use the offsets above
// if this feature isn't available, this path will never be exercised (since we only
// come in here when the actual shader used const offsets) so it's fine to drop it in
// that case to ensure the module is still legal.
if(m_pDriver->GetDeviceEnabledFeatures().shaderImageGatherExtended &&
i != (uint32_t)ShaderDebugBind::TexCube)
operands.setConstOffsets(gatherOffsets);
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
if(op == rdcspv::Op::ImageGather)
constsResult = cases.add(rdcspv::OpImageGather(resultType, editor.MakeId(), combined,
coord[i], gatherChannel, operands));
else
constsResult = cases.add(rdcspv::OpImageDrefGather(
resultType, editor.MakeId(), combined, coord[i], compare, operands));
cases.add(rdcspv::OpBranch(mergeLabel));
}
cases.add(rdcspv::OpLabel(mergeLabel));
sampleResult = cases.add(rdcspv::OpPhi(
resultType, editor.MakeId(), {{baseResult, baseCase}, {constsResult, constsCase}}));
}
else
{
rdcspv::ImageOperandsAndParamDatas operands = imageOperandsWithOffsets;
rdcspv::Id combined = cases.add(rdcspv::OpSampledImage(
texSampCombinedTypes[i], editor.MakeId(), loadedImage, loadedSampler));
if(op == rdcspv::Op::ImageGather)
sampleResult = cases.add(rdcspv::OpImageGather(resultType, editor.MakeId(), combined,
coord[i], gatherChannel, operands));
else
sampleResult = cases.add(rdcspv::OpImageDrefGather(
resultType, editor.MakeId(), combined, coord[i], compare, operands));
}
cases.add(rdcspv::OpStore(outVar, sampleResult));
cases.add(rdcspv::OpBranch(breakLabel));
}
}
func.add(rdcspv::OpSelectionMerge(breakLabel, rdcspv::SelectionControl::None));
func.add(rdcspv::OpSwitch32(switchVal, defaultLabel, targets));
func.append(cases);
// default: store NULL data
func.add(rdcspv::OpLabel(defaultLabel));
func.add(rdcspv::OpStore(
outVar, editor.AddConstant(rdcspv::OpConstantNull(resultType, editor.MakeId()))));
func.add(rdcspv::OpBranch(breakLabel));
func.add(rdcspv::OpLabel(breakLabel));
func.add(rdcspv::OpReturn());
func.add(rdcspv::OpFunctionEnd());
editor.AddFunction(func);
}