mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-21 19:20:07 +00:00
opt: split composite from array flattening (#5733)
* opt: split composite from array flattening DXC has an option to flatten resource arrays. But when this option is not used, the resource arrays should be kept as-is. On the other hand, when a struct contains resources, we MUST flatten is to be compliant with the Vulkan spec. Because this pass flattens both types of resources, using a struct of resources automatically implied flattening arrays. By adding those 2 new settings, we decide if the pass flattens only one type of resources, or both. Note: the flatten_arrays flag only impacts resource arrays. Arrays of composites containing resources are still flattened. Since the API is considered stable, I added 2 new functions to create passes with one flag or the other, and kept the original behavior as-is. Related to https://github.com/microsoft/DirectXShaderCompiler/issues/6745 Signed-off-by: Nathan Gauër <brioche@google.com> * add commandline options Signed-off-by: Nathan Gauër <brioche@google.com> * clang-format Signed-off-by: Nathan Gauër <brioche@google.com> --------- Signed-off-by: Nathan Gauër <brioche@google.com>
This commit is contained in:
parent
4c7e1fa5c3
commit
2ea4003633
@ -827,14 +827,19 @@ Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass();
|
||||
|
||||
// Create descriptor scalar replacement pass.
|
||||
// This pass replaces every array variable |desc| that has a DescriptorSet and
|
||||
// Binding decorations with a new variable for each element of the array.
|
||||
// Suppose |desc| was bound at binding |b|. Then the variable corresponding to
|
||||
// |desc[i]| will have binding |b+i|. The descriptor set will be the same. It
|
||||
// is assumed that no other variable already has a binding that will used by one
|
||||
// of the new variables. If not, the pass will generate invalid Spir-V. All
|
||||
// accesses to |desc| must be OpAccessChain instructions with a literal index
|
||||
// for the first index.
|
||||
// Binding decorations with a new variable for each element of the
|
||||
// array/composite. Suppose |desc| was bound at binding |b|. Then the variable
|
||||
// corresponding to |desc[i]| will have binding |b+i|. The descriptor set will
|
||||
// be the same. It is assumed that no other variable already has a binding that
|
||||
// will used by one of the new variables. If not, the pass will generate
|
||||
// invalid Spir-V. All accesses to |desc| must be OpAccessChain instructions
|
||||
// with a literal index for the first index. This variant flattens both
|
||||
// composites and arrays.
|
||||
Optimizer::PassToken CreateDescriptorScalarReplacementPass();
|
||||
// This variant flattens only composites.
|
||||
Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass();
|
||||
// This variant flattens only arrays.
|
||||
Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass();
|
||||
|
||||
// Create a pass to replace each OpKill instruction with a function call to a
|
||||
// function that has a single OpKill. Also replace each OpTerminateInvocation
|
||||
|
@ -31,11 +31,14 @@ bool IsDecorationBinding(Instruction* inst) {
|
||||
|
||||
Pass::Status DescriptorScalarReplacement::Process() {
|
||||
bool modified = false;
|
||||
|
||||
std::vector<Instruction*> vars_to_kill;
|
||||
|
||||
for (Instruction& var : context()->types_values()) {
|
||||
if (descsroautil::IsDescriptorArray(context(), &var)) {
|
||||
bool is_candidate =
|
||||
flatten_arrays_ && descsroautil::IsDescriptorArray(context(), &var);
|
||||
is_candidate |= flatten_composites_ &&
|
||||
descsroautil::IsDescriptorStruct(context(), &var);
|
||||
if (is_candidate) {
|
||||
modified = true;
|
||||
if (!ReplaceCandidate(&var)) {
|
||||
return Status::Failure;
|
||||
|
@ -32,9 +32,16 @@ namespace opt {
|
||||
// Documented in optimizer.hpp
|
||||
class DescriptorScalarReplacement : public Pass {
|
||||
public:
|
||||
DescriptorScalarReplacement() {}
|
||||
DescriptorScalarReplacement(bool flatten_composites, bool flatten_arrays)
|
||||
: flatten_composites_(flatten_composites),
|
||||
flatten_arrays_(flatten_arrays) {}
|
||||
|
||||
const char* name() const override { return "descriptor-scalar-replacement"; }
|
||||
const char* name() const override {
|
||||
if (flatten_composites_ && flatten_arrays_)
|
||||
return "descriptor-scalar-replacement";
|
||||
if (flatten_composites_) return "descriptor-compososite-scalar-replacement";
|
||||
return "descriptor-array-scalar-replacement";
|
||||
}
|
||||
|
||||
Status Process() override;
|
||||
|
||||
@ -141,6 +148,9 @@ class DescriptorScalarReplacement : public Pass {
|
||||
// array |var|. If the entry is |0|, then the variable has not been
|
||||
// created yet.
|
||||
std::map<Instruction*, std::vector<uint32_t>> replacement_variables_;
|
||||
|
||||
bool flatten_composites_;
|
||||
bool flatten_arrays_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -29,41 +29,58 @@ uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) {
|
||||
return length_const->GetU32();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
bool HasDescriptorDecorations(IRContext* context, Instruction* var) {
|
||||
const auto& decoration_mgr = context->get_decoration_mgr();
|
||||
return decoration_mgr->HasDecoration(
|
||||
var->result_id(), uint32_t(spv::Decoration::DescriptorSet)) &&
|
||||
decoration_mgr->HasDecoration(var->result_id(),
|
||||
uint32_t(spv::Decoration::Binding));
|
||||
}
|
||||
|
||||
namespace descsroautil {
|
||||
|
||||
bool IsDescriptorArray(IRContext* context, Instruction* var) {
|
||||
Instruction* GetVariableType(IRContext* context, Instruction* var) {
|
||||
if (var->opcode() != spv::Op::OpVariable) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t ptr_type_id = var->type_id();
|
||||
Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
|
||||
if (ptr_type_inst->opcode() != spv::Op::OpTypePointer) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id);
|
||||
if (var_type_inst->opcode() != spv::Op::OpTypeArray &&
|
||||
var_type_inst->opcode() != spv::Op::OpTypeStruct) {
|
||||
return false;
|
||||
return context->get_def_use_mgr()->GetDef(var_type_id);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace descsroautil {
|
||||
|
||||
bool IsDescriptorArray(IRContext* context, Instruction* var) {
|
||||
Instruction* var_type_inst = GetVariableType(context, var);
|
||||
if (var_type_inst == nullptr) return false;
|
||||
return var_type_inst->opcode() == spv::Op::OpTypeArray &&
|
||||
HasDescriptorDecorations(context, var);
|
||||
}
|
||||
|
||||
bool IsDescriptorStruct(IRContext* context, Instruction* var) {
|
||||
Instruction* var_type_inst = GetVariableType(context, var);
|
||||
if (var_type_inst == nullptr) return false;
|
||||
|
||||
while (var_type_inst->opcode() == spv::Op::OpTypeArray) {
|
||||
var_type_inst = context->get_def_use_mgr()->GetDef(
|
||||
var_type_inst->GetInOperand(0).AsId());
|
||||
}
|
||||
|
||||
if (var_type_inst->opcode() != spv::Op::OpTypeStruct) return false;
|
||||
|
||||
// All structures with descriptor assignments must be replaced by variables,
|
||||
// one for each of their members - with the exceptions of buffers.
|
||||
if (IsTypeOfStructuredBuffer(context, var_type_inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!context->get_decoration_mgr()->HasDecoration(
|
||||
var->result_id(), uint32_t(spv::Decoration::DescriptorSet))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return context->get_decoration_mgr()->HasDecoration(
|
||||
var->result_id(), uint32_t(spv::Decoration::Binding));
|
||||
return HasDescriptorDecorations(context, var);
|
||||
}
|
||||
|
||||
bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) {
|
||||
|
@ -27,6 +27,10 @@ namespace descsroautil {
|
||||
// descriptor array.
|
||||
bool IsDescriptorArray(IRContext* context, Instruction* var);
|
||||
|
||||
// Returns true if |var| is an OpVariable instruction that represents a
|
||||
// struct containing descriptors.
|
||||
bool IsDescriptorStruct(IRContext* context, Instruction* var);
|
||||
|
||||
// Returns true if |type| is a type that could be used for a structured buffer
|
||||
// as opposed to a type that would be used for a structure of resource
|
||||
// descriptors.
|
||||
|
@ -364,6 +364,10 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag,
|
||||
RegisterPass(CreateSpreadVolatileSemanticsPass());
|
||||
} else if (pass_name == "descriptor-scalar-replacement") {
|
||||
RegisterPass(CreateDescriptorScalarReplacementPass());
|
||||
} else if (pass_name == "descriptor-composite-scalar-replacement") {
|
||||
RegisterPass(CreateDescriptorCompositeScalarReplacementPass());
|
||||
} else if (pass_name == "descriptor-array-scalar-replacement") {
|
||||
RegisterPass(CreateDescriptorArrayScalarReplacementPass());
|
||||
} else if (pass_name == "eliminate-dead-code-aggressive") {
|
||||
RegisterPass(CreateAggressiveDCEPass(preserve_interface));
|
||||
} else if (pass_name == "eliminate-insert-extract") {
|
||||
@ -1059,7 +1063,20 @@ Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
|
||||
|
||||
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::DescriptorScalarReplacement>());
|
||||
MakeUnique<opt::DescriptorScalarReplacement>(
|
||||
/* flatten_composites= */ true, /* flatten_arrays= */ true));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::DescriptorScalarReplacement>(
|
||||
/* flatten_composites= */ true, /* flatten_arrays= */ false));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::DescriptorScalarReplacement>(
|
||||
/* flatten_composites= */ false, /* flatten_arrays= */ true));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateWrapOpKillPass() {
|
||||
|
@ -198,7 +198,8 @@ TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfTextures) {
|
||||
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSamplers) {
|
||||
@ -249,7 +250,8 @@ TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSamplers) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSSBOs) {
|
||||
@ -308,7 +310,8 @@ TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSSBOs) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, NameNewVariables) {
|
||||
@ -370,7 +373,8 @@ TEST_F(DescriptorScalarReplacementTest, NameNewVariables) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, DontExpandCBuffers) {
|
||||
@ -430,7 +434,8 @@ TEST_F(DescriptorScalarReplacementTest, DontExpandCBuffers) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, DontExpandStructuredBuffers) {
|
||||
@ -497,7 +502,8 @@ TEST_F(DescriptorScalarReplacementTest, DontExpandStructuredBuffers) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, StructureArrayNames) {
|
||||
@ -511,7 +517,39 @@ TEST_F(DescriptorScalarReplacementTest, StructureArrayNames) {
|
||||
)";
|
||||
|
||||
const std::string text = checks + GetStructureArrayTestSpirv();
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest,
|
||||
FlattensArraysOfStructsButNoResourceArrays) {
|
||||
// Check that only the composite array is flattenned, but internal resource
|
||||
// arrays are left as-is.
|
||||
const std::string checks = R"(
|
||||
; CHECK: OpName %globalS_0__0__t "globalS[0][0].t"
|
||||
; CHECK: OpName %globalS_0__0__s "globalS[0][0].s"
|
||||
; CHECK: OpName %globalS_1__1__t "globalS[1][1].t"
|
||||
; CHECK: OpName %globalS_1__1__s "globalS[1][1].s"
|
||||
; CHECK-NOT: OpName %globalS_1__1__t_0_
|
||||
; CHECK-NOT: OpName %globalS_1__1__s_0_
|
||||
)";
|
||||
|
||||
const std::string text = checks + GetStructureArrayTestSpirv();
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/false);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, FlattenNothingIfAskedTo) {
|
||||
// Not useful, but checks what happens if both are set to false.
|
||||
// In such case, nothing happens.
|
||||
const std::string checks = R"(
|
||||
; CHECK: OpName %globalS
|
||||
; CHECK-NOT: OpName %globalS_
|
||||
)";
|
||||
|
||||
const std::string text = checks + GetStructureArrayTestSpirv();
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/false, /* flatten_arrays=*/false);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, StructureArrayBindings) {
|
||||
@ -525,7 +563,8 @@ TEST_F(DescriptorScalarReplacementTest, StructureArrayBindings) {
|
||||
)";
|
||||
|
||||
const std::string text = checks + GetStructureArrayTestSpirv();
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, StructureArrayReplacements) {
|
||||
@ -540,7 +579,8 @@ TEST_F(DescriptorScalarReplacementTest, StructureArrayReplacements) {
|
||||
)";
|
||||
|
||||
const std::string text = checks + GetStructureArrayTestSpirv();
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, ResourceStructAsFunctionParam) {
|
||||
@ -724,7 +764,9 @@ TEST_F(DescriptorScalarReplacementTest, ResourceStructAsFunctionParam) {
|
||||
; CHECK: OpFAdd %v4float [[sample_3]] [[sample_4]]
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(checks + shader, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
checks + shader, true, /* flatten_composites=*/true,
|
||||
/* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) {
|
||||
@ -765,7 +807,8 @@ TEST_F(DescriptorScalarReplacementTest, BindingForResourceArrayOfStructs) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
shader, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) {
|
||||
@ -828,7 +871,8 @@ TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
shader, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, DecorateStringForReflect) {
|
||||
@ -915,7 +959,8 @@ TEST_F(DescriptorScalarReplacementTest, DecorateStringForReflect) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
shader, true, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, ExpandArrayInOpEntryPoint) {
|
||||
@ -983,7 +1028,161 @@ TEST_F(DescriptorScalarReplacementTest, ExpandArrayInOpEntryPoint) {
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(text, false);
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, false, /* flatten_composites=*/true, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest,
|
||||
ExpandArrayWhenCompositeExpensionIsOff) {
|
||||
const std::string text = R"(; SPIR-V
|
||||
; Version: 1.6
|
||||
; Bound: 31
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
|
||||
; CHECK: OpEntryPoint GLCompute %main "main" %output_0_ %output_1_
|
||||
|
||||
OpEntryPoint GLCompute %main "main" %output
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpSource HLSL 670
|
||||
OpName %type_RWByteAddressBuffer "type.RWByteAddressBuffer"
|
||||
OpName %output "output"
|
||||
OpName %main "main"
|
||||
OpName %src_main "src.main"
|
||||
OpName %bb_entry "bb.entry"
|
||||
|
||||
; CHECK: OpDecorate %output_1_ DescriptorSet 0
|
||||
; CHECK: OpDecorate %output_1_ Binding 1
|
||||
; CHECK: OpDecorate %output_0_ DescriptorSet 0
|
||||
; CHECK: OpDecorate %output_0_ Binding 0
|
||||
|
||||
OpDecorate %output DescriptorSet 0
|
||||
OpDecorate %output Binding 0
|
||||
|
||||
OpDecorate %_runtimearr_uint ArrayStride 4
|
||||
OpMemberDecorate %type_RWByteAddressBuffer 0 Offset 0
|
||||
OpDecorate %type_RWByteAddressBuffer Block
|
||||
%int = OpTypeInt 32 1
|
||||
%int_1 = OpConstant %int 1
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%uint_32 = OpConstant %uint 32
|
||||
%_runtimearr_uint = OpTypeRuntimeArray %uint
|
||||
%type_RWByteAddressBuffer = OpTypeStruct %_runtimearr_uint
|
||||
%_arr_type_RWByteAddressBuffer_uint_2 = OpTypeArray %type_RWByteAddressBuffer %uint_2
|
||||
%_ptr_StorageBuffer__arr_type_RWByteAddressBuffer_uint_2 = OpTypePointer StorageBuffer %_arr_type_RWByteAddressBuffer_uint_2
|
||||
%void = OpTypeVoid
|
||||
%23 = OpTypeFunction %void
|
||||
%_ptr_StorageBuffer_type_RWByteAddressBuffer = OpTypePointer StorageBuffer %type_RWByteAddressBuffer
|
||||
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
|
||||
|
||||
; CHECK: %output_1_ = OpVariable %_ptr_StorageBuffer_type_RWByteAddressBuffer StorageBuffer
|
||||
; CHECK: %output_0_ = OpVariable %_ptr_StorageBuffer_type_RWByteAddressBuffer StorageBuffer
|
||||
|
||||
%output = OpVariable %_ptr_StorageBuffer__arr_type_RWByteAddressBuffer_uint_2 StorageBuffer
|
||||
|
||||
%main = OpFunction %void None %23
|
||||
%26 = OpLabel
|
||||
%27 = OpFunctionCall %void %src_main
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%src_main = OpFunction %void None %23
|
||||
%bb_entry = OpLabel
|
||||
%28 = OpAccessChain %_ptr_StorageBuffer_type_RWByteAddressBuffer %output %int_1
|
||||
%29 = OpShiftRightLogical %uint %uint_0 %uint_2
|
||||
%30 = OpAccessChain %_ptr_StorageBuffer_uint %28 %uint_0 %29
|
||||
OpStore %30 %uint_32
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, false, /* flatten_composites=*/false, /* flatten_arrays=*/true);
|
||||
}
|
||||
|
||||
TEST_F(DescriptorScalarReplacementTest, ExpandStructButNotArray) {
|
||||
const std::string text = R"(; SPIR-V
|
||||
; Version: 1.6
|
||||
; Generator: Khronos SPIR-V Tools Assembler; 0
|
||||
; Bound: 41
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %out_var_SV_Target
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource HLSL 660
|
||||
OpName %type_2d_image "type.2d.image"
|
||||
OpName %Textures "Textures"
|
||||
OpName %type_sampler "type.sampler"
|
||||
OpName %out_var_SV_Target "out.var.SV_Target"
|
||||
OpName %main "main"
|
||||
OpName %type_sampled_image "type.sampled.image"
|
||||
OpName %TheStruct "TheStruct"
|
||||
OpMemberName %StructOfResources 0 "Texture"
|
||||
OpMemberName %StructOfResources 1 "Sampler"
|
||||
; CHECK: OpName %TheStruct_Sampler "TheStruct.Sampler"
|
||||
; CHECK: OpName %TheStruct_Texture "TheStruct.Texture"
|
||||
OpDecorate %out_var_SV_Target Location 0
|
||||
OpDecorate %Textures DescriptorSet 0
|
||||
OpDecorate %Textures Binding 0
|
||||
OpDecorate %TheStruct DescriptorSet 0
|
||||
OpDecorate %TheStruct Binding 10
|
||||
; CHECK: OpDecorate %TheStruct_Sampler DescriptorSet 0
|
||||
; CHECK: OpDecorate %TheStruct_Sampler Binding 11
|
||||
; CHECK: OpDecorate %TheStruct_Texture DescriptorSet 0
|
||||
; CHECK: OpDecorate %TheStruct_Texture Binding 10
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%float = OpTypeFloat 32
|
||||
%float_0 = OpConstant %float 0
|
||||
%v2float = OpTypeVector %float 2
|
||||
%13 = OpConstantComposite %v2float %float_0 %float_0
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_10 = OpConstant %uint 10
|
||||
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
|
||||
%_arr_type_2d_image_uint_10 = OpTypeArray %type_2d_image %uint_10
|
||||
%_ptr_UniformConstant__arr_type_2d_image_uint_10 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_10
|
||||
%type_sampler = OpTypeSampler
|
||||
%StructOfResources = OpTypeStruct %type_2d_image %type_sampler
|
||||
%_ptr_UniformConstant__struct_18 = OpTypePointer UniformConstant %StructOfResources
|
||||
%v4float = OpTypeVector %float 4
|
||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
||||
%void = OpTypeVoid
|
||||
%23 = OpTypeFunction %void
|
||||
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
|
||||
%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
|
||||
%type_sampled_image = OpTypeSampledImage %type_2d_image
|
||||
%Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_10 UniformConstant
|
||||
%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
|
||||
%TheStruct = OpVariable %_ptr_UniformConstant__struct_18 UniformConstant
|
||||
%main = OpFunction %void None %23
|
||||
%26 = OpLabel
|
||||
%27 = OpAccessChain %_ptr_UniformConstant_type_2d_image %Textures %int_0
|
||||
%28 = OpLoad %type_2d_image %27
|
||||
%29 = OpAccessChain %_ptr_UniformConstant_type_sampler %TheStruct %int_1
|
||||
%31 = OpLoad %type_sampler %29
|
||||
; CHECK: %31 = OpLoad %type_sampler %TheStruct_Sampler
|
||||
%32 = OpSampledImage %type_sampled_image %28 %31
|
||||
%33 = OpImageSampleImplicitLod %v4float %32 %13 None
|
||||
%34 = OpAccessChain %_ptr_UniformConstant_type_2d_image %TheStruct %int_0
|
||||
%35 = OpLoad %type_2d_image %34
|
||||
; CHECK: %35 = OpLoad %type_2d_image %TheStruct_Texture
|
||||
%36 = OpAccessChain %_ptr_UniformConstant_type_sampler %TheStruct %int_1
|
||||
%37 = OpLoad %type_sampler %36
|
||||
; CHECK: %37 = OpLoad %type_sampler %TheStruct_Sampler
|
||||
%38 = OpSampledImage %type_sampled_image %35 %37
|
||||
%39 = OpImageSampleImplicitLod %v4float %38 %13 None
|
||||
%40 = OpFAdd %v4float %33 %39
|
||||
OpStore %out_var_SV_Target %40
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DescriptorScalarReplacement>(
|
||||
text, false, /* flatten_composites=*/true, /* flatten_arrays=*/false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -181,6 +181,14 @@ Options (in lexicographical order):)",
|
||||
must be in OpAccessChain instructions with a literal index for
|
||||
the first index.)");
|
||||
printf(R"(
|
||||
--descriptor-composite-scalar-replacement
|
||||
Same as descriptor-scalar-replacement, but only impacts composite/structs.
|
||||
For details, see --descriptor-scalar-replacement help.)");
|
||||
printf(R"(
|
||||
--descriptor-array-scalar-replacement
|
||||
Same as descriptor-scalar-replacement, but only impacts arrays.
|
||||
For details, see --descriptor-scalar-replacement help.)");
|
||||
printf(R"(
|
||||
--eliminate-dead-branches
|
||||
Convert conditional branches with constant condition to the
|
||||
indicated unconditional branch. Delete all resulting dead
|
||||
|
Loading…
Reference in New Issue
Block a user