Skip to content

Commit 2ea4003

Browse files
authored
opt: split composite from array flattening (KhronosGroup#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 microsoft/DirectXShaderCompiler#6745 Signed-off-by: Nathan Gauër <[email protected]> * add commandline options Signed-off-by: Nathan Gauër <[email protected]> * clang-format Signed-off-by: Nathan Gauër <[email protected]> --------- Signed-off-by: Nathan Gauër <[email protected]>
1 parent 4c7e1fa commit 2ea4003

File tree

8 files changed

+307
-44
lines changed

8 files changed

+307
-44
lines changed

include/spirv-tools/optimizer.hpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -827,14 +827,19 @@ Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass();
827827

828828
// Create descriptor scalar replacement pass.
829829
// This pass replaces every array variable |desc| that has a DescriptorSet and
830-
// Binding decorations with a new variable for each element of the array.
831-
// Suppose |desc| was bound at binding |b|. Then the variable corresponding to
832-
// |desc[i]| will have binding |b+i|. The descriptor set will be the same. It
833-
// is assumed that no other variable already has a binding that will used by one
834-
// of the new variables. If not, the pass will generate invalid Spir-V. All
835-
// accesses to |desc| must be OpAccessChain instructions with a literal index
836-
// for the first index.
830+
// Binding decorations with a new variable for each element of the
831+
// array/composite. Suppose |desc| was bound at binding |b|. Then the variable
832+
// corresponding to |desc[i]| will have binding |b+i|. The descriptor set will
833+
// be the same. It is assumed that no other variable already has a binding that
834+
// will used by one of the new variables. If not, the pass will generate
835+
// invalid Spir-V. All accesses to |desc| must be OpAccessChain instructions
836+
// with a literal index for the first index. This variant flattens both
837+
// composites and arrays.
837838
Optimizer::PassToken CreateDescriptorScalarReplacementPass();
839+
// This variant flattens only composites.
840+
Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass();
841+
// This variant flattens only arrays.
842+
Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass();
838843

839844
// Create a pass to replace each OpKill instruction with a function call to a
840845
// function that has a single OpKill. Also replace each OpTerminateInvocation

source/opt/desc_sroa.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ bool IsDecorationBinding(Instruction* inst) {
3131

3232
Pass::Status DescriptorScalarReplacement::Process() {
3333
bool modified = false;
34-
3534
std::vector<Instruction*> vars_to_kill;
3635

3736
for (Instruction& var : context()->types_values()) {
38-
if (descsroautil::IsDescriptorArray(context(), &var)) {
37+
bool is_candidate =
38+
flatten_arrays_ && descsroautil::IsDescriptorArray(context(), &var);
39+
is_candidate |= flatten_composites_ &&
40+
descsroautil::IsDescriptorStruct(context(), &var);
41+
if (is_candidate) {
3942
modified = true;
4043
if (!ReplaceCandidate(&var)) {
4144
return Status::Failure;

source/opt/desc_sroa.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,16 @@ namespace opt {
3232
// Documented in optimizer.hpp
3333
class DescriptorScalarReplacement : public Pass {
3434
public:
35-
DescriptorScalarReplacement() {}
36-
37-
const char* name() const override { return "descriptor-scalar-replacement"; }
35+
DescriptorScalarReplacement(bool flatten_composites, bool flatten_arrays)
36+
: flatten_composites_(flatten_composites),
37+
flatten_arrays_(flatten_arrays) {}
38+
39+
const char* name() const override {
40+
if (flatten_composites_ && flatten_arrays_)
41+
return "descriptor-scalar-replacement";
42+
if (flatten_composites_) return "descriptor-compososite-scalar-replacement";
43+
return "descriptor-array-scalar-replacement";
44+
}
3845

3946
Status Process() override;
4047

@@ -141,6 +148,9 @@ class DescriptorScalarReplacement : public Pass {
141148
// array |var|. If the entry is |0|, then the variable has not been
142149
// created yet.
143150
std::map<Instruction*, std::vector<uint32_t>> replacement_variables_;
151+
152+
bool flatten_composites_;
153+
bool flatten_arrays_;
144154
};
145155

146156
} // namespace opt

source/opt/desc_sroa_util.cpp

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,41 +29,58 @@ uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) {
2929
return length_const->GetU32();
3030
}
3131

32-
} // namespace
33-
34-
namespace descsroautil {
32+
bool HasDescriptorDecorations(IRContext* context, Instruction* var) {
33+
const auto& decoration_mgr = context->get_decoration_mgr();
34+
return decoration_mgr->HasDecoration(
35+
var->result_id(), uint32_t(spv::Decoration::DescriptorSet)) &&
36+
decoration_mgr->HasDecoration(var->result_id(),
37+
uint32_t(spv::Decoration::Binding));
38+
}
3539

36-
bool IsDescriptorArray(IRContext* context, Instruction* var) {
40+
Instruction* GetVariableType(IRContext* context, Instruction* var) {
3741
if (var->opcode() != spv::Op::OpVariable) {
38-
return false;
42+
return nullptr;
3943
}
4044

4145
uint32_t ptr_type_id = var->type_id();
4246
Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
4347
if (ptr_type_inst->opcode() != spv::Op::OpTypePointer) {
44-
return false;
48+
return nullptr;
4549
}
4650

4751
uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
48-
Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id);
49-
if (var_type_inst->opcode() != spv::Op::OpTypeArray &&
50-
var_type_inst->opcode() != spv::Op::OpTypeStruct) {
51-
return false;
52+
return context->get_def_use_mgr()->GetDef(var_type_id);
53+
}
54+
55+
} // namespace
56+
57+
namespace descsroautil {
58+
59+
bool IsDescriptorArray(IRContext* context, Instruction* var) {
60+
Instruction* var_type_inst = GetVariableType(context, var);
61+
if (var_type_inst == nullptr) return false;
62+
return var_type_inst->opcode() == spv::Op::OpTypeArray &&
63+
HasDescriptorDecorations(context, var);
64+
}
65+
66+
bool IsDescriptorStruct(IRContext* context, Instruction* var) {
67+
Instruction* var_type_inst = GetVariableType(context, var);
68+
if (var_type_inst == nullptr) return false;
69+
70+
while (var_type_inst->opcode() == spv::Op::OpTypeArray) {
71+
var_type_inst = context->get_def_use_mgr()->GetDef(
72+
var_type_inst->GetInOperand(0).AsId());
5273
}
5374

75+
if (var_type_inst->opcode() != spv::Op::OpTypeStruct) return false;
76+
5477
// All structures with descriptor assignments must be replaced by variables,
5578
// one for each of their members - with the exceptions of buffers.
5679
if (IsTypeOfStructuredBuffer(context, var_type_inst)) {
5780
return false;
5881
}
5982

60-
if (!context->get_decoration_mgr()->HasDecoration(
61-
var->result_id(), uint32_t(spv::Decoration::DescriptorSet))) {
62-
return false;
63-
}
64-
65-
return context->get_decoration_mgr()->HasDecoration(
66-
var->result_id(), uint32_t(spv::Decoration::Binding));
83+
return HasDescriptorDecorations(context, var);
6784
}
6885

6986
bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) {

source/opt/desc_sroa_util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ namespace descsroautil {
2727
// descriptor array.
2828
bool IsDescriptorArray(IRContext* context, Instruction* var);
2929

30+
// Returns true if |var| is an OpVariable instruction that represents a
31+
// struct containing descriptors.
32+
bool IsDescriptorStruct(IRContext* context, Instruction* var);
33+
3034
// Returns true if |type| is a type that could be used for a structured buffer
3135
// as opposed to a type that would be used for a structure of resource
3236
// descriptors.

source/opt/optimizer.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag,
364364
RegisterPass(CreateSpreadVolatileSemanticsPass());
365365
} else if (pass_name == "descriptor-scalar-replacement") {
366366
RegisterPass(CreateDescriptorScalarReplacementPass());
367+
} else if (pass_name == "descriptor-composite-scalar-replacement") {
368+
RegisterPass(CreateDescriptorCompositeScalarReplacementPass());
369+
} else if (pass_name == "descriptor-array-scalar-replacement") {
370+
RegisterPass(CreateDescriptorArrayScalarReplacementPass());
367371
} else if (pass_name == "eliminate-dead-code-aggressive") {
368372
RegisterPass(CreateAggressiveDCEPass(preserve_interface));
369373
} else if (pass_name == "eliminate-insert-extract") {
@@ -1059,7 +1063,20 @@ Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
10591063

10601064
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
10611065
return MakeUnique<Optimizer::PassToken::Impl>(
1062-
MakeUnique<opt::DescriptorScalarReplacement>());
1066+
MakeUnique<opt::DescriptorScalarReplacement>(
1067+
/* flatten_composites= */ true, /* flatten_arrays= */ true));
1068+
}
1069+
1070+
Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass() {
1071+
return MakeUnique<Optimizer::PassToken::Impl>(
1072+
MakeUnique<opt::DescriptorScalarReplacement>(
1073+
/* flatten_composites= */ true, /* flatten_arrays= */ false));
1074+
}
1075+
1076+
Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass() {
1077+
return MakeUnique<Optimizer::PassToken::Impl>(
1078+
MakeUnique<opt::DescriptorScalarReplacement>(
1079+
/* flatten_composites= */ false, /* flatten_arrays= */ true));
10631080
}
10641081

10651082
Optimizer::PassToken CreateWrapOpKillPass() {

0 commit comments

Comments
 (0)