Split EliminateDeadInputComponents into safe and unsafe versions. (#4984)
Safe version will only optimize vertex shaders. All other shaders will succeed without change. Change --eliminate-dead-input-components to use new safe version. Unsafe version (allowing non-vertex shaders) currently only available through API. Should only be used in combination with other optimizations to keep interfaces consistent. See optimizer.hpp for more details.
This commit is contained in:
Родитель
a8647f59c0
Коммит
8ea3ae6be2
|
@ -913,6 +913,14 @@ Optimizer::PassToken CreateEliminateDeadInputComponentsPass();
|
|||
// shader, then apply EliminateDeadOutputStores to this shader.
|
||||
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass();
|
||||
|
||||
// Removes unused components from composite input variables. This safe
|
||||
// version will not cause interface incompatibilities since it only changes
|
||||
// vertex shaders. The current implementation just removes trailing unused
|
||||
// components from input structs and input arrays. The pass performs best
|
||||
// after maximizing dead code removal. A subsequent dead code elimination
|
||||
// pass would be beneficial in removing newly unused component types.
|
||||
Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass();
|
||||
|
||||
// Analyzes shader and populates |live_locs| and |live_builtins|. Best results
|
||||
// will be obtained if shader has all dead code eliminated first. |live_locs|
|
||||
// and |live_builtins| are subsequently used when calling
|
||||
|
|
|
@ -35,7 +35,11 @@ namespace spvtools {
|
|||
namespace opt {
|
||||
|
||||
Pass::Status EliminateDeadInputComponentsPass::Process() {
|
||||
// Current functionality assumes shader capability
|
||||
// Process non-vertex only if explicitly allowed.
|
||||
auto stage = context()->GetStage();
|
||||
if (stage != spv::ExecutionModel::Vertex && vertex_shader_only_)
|
||||
return Status::SuccessWithoutChange;
|
||||
// Current functionality assumes shader capability.
|
||||
if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
|
||||
return Status::SuccessWithoutChange;
|
||||
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
||||
|
@ -62,13 +66,21 @@ Pass::Status EliminateDeadInputComponentsPass::Process() {
|
|||
}
|
||||
const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray();
|
||||
if (arr_type != nullptr) {
|
||||
// Only process array if input of vertex shader, or output of
|
||||
// fragment shader. Otherwise, if one shader has a runtime index and the
|
||||
// other does not, interface incompatibility can occur.
|
||||
if (!((ptr_type->storage_class() == spv::StorageClass::Input &&
|
||||
stage == spv::ExecutionModel::Vertex) ||
|
||||
(ptr_type->storage_class() == spv::StorageClass::Output &&
|
||||
stage == spv::ExecutionModel::Fragment)))
|
||||
continue;
|
||||
unsigned arr_len_id = arr_type->LengthId();
|
||||
Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id);
|
||||
if (arr_len_inst->opcode() != spv::Op::OpConstant) {
|
||||
continue;
|
||||
}
|
||||
// SPIR-V requires array size is >= 1, so this works for signed or
|
||||
// unsigned size
|
||||
// unsigned size.
|
||||
unsigned original_max =
|
||||
arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1;
|
||||
unsigned max_idx = FindMaxIndex(var, original_max);
|
||||
|
@ -92,7 +104,7 @@ Pass::Status EliminateDeadInputComponentsPass::Process() {
|
|||
}
|
||||
|
||||
// Move changed vars after their new type instruction to preserve backward
|
||||
// referencing
|
||||
// referencing.
|
||||
for (auto var : vars_to_move) {
|
||||
auto type_id = var->type_id();
|
||||
auto type_inst = def_use_mgr->GetDef(type_id);
|
||||
|
|
|
@ -28,13 +28,14 @@ namespace opt {
|
|||
// See optimizer.hpp for documentation.
|
||||
class EliminateDeadInputComponentsPass : public Pass {
|
||||
public:
|
||||
explicit EliminateDeadInputComponentsPass(bool output_instead = false)
|
||||
: output_instead_(output_instead) {}
|
||||
explicit EliminateDeadInputComponentsPass(bool output_instead = false,
|
||||
bool vertex_shader_only = true)
|
||||
: output_instead_(output_instead),
|
||||
vertex_shader_only_(vertex_shader_only) {}
|
||||
|
||||
const char* name() const override {
|
||||
return "eliminate-dead-input-components";
|
||||
}
|
||||
|
||||
Status Process() override;
|
||||
|
||||
// Return the mask of preserved Analyses.
|
||||
|
@ -61,6 +62,9 @@ class EliminateDeadInputComponentsPass : public Pass {
|
|||
|
||||
// Process output variables instead
|
||||
bool output_instead_;
|
||||
|
||||
// Only process vertex shaders
|
||||
bool vertex_shader_only_;
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
|
|
@ -26,7 +26,6 @@ static const int kInstCommonParamInstIdx = 0;
|
|||
static const int kInstCommonParamCnt = 1;
|
||||
|
||||
// Indices of operands in SPIR-V instructions
|
||||
static const int kEntryPointExecutionModelInIdx = 0;
|
||||
static const int kEntryPointFunctionIdInIdx = 1;
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -1056,22 +1055,7 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
|
|||
// one model per module. In such cases we will need
|
||||
// to clone any functions which are in the call trees of entrypoints
|
||||
// with differing execution models.
|
||||
uint32_t ecnt = 0;
|
||||
auto stage = spv::ExecutionModel::Max;
|
||||
for (auto& e : get_module()->entry_points()) {
|
||||
if (ecnt == 0)
|
||||
stage = spv::ExecutionModel(
|
||||
e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx));
|
||||
else if (spv::ExecutionModel(e.GetSingleWordInOperand(
|
||||
kEntryPointExecutionModelInIdx)) != stage) {
|
||||
if (consumer()) {
|
||||
std::string message = "Mixed stage shader module not supported";
|
||||
consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
++ecnt;
|
||||
}
|
||||
spv::ExecutionModel stage = context()->GetStage();
|
||||
// Check for supported stages
|
||||
if (stage != spv::ExecutionModel::Vertex &&
|
||||
stage != spv::ExecutionModel::Fragment &&
|
||||
|
|
|
@ -525,7 +525,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
|||
} else if (pass_name == "remove-dont-inline") {
|
||||
RegisterPass(CreateRemoveDontInlinePass());
|
||||
} else if (pass_name == "eliminate-dead-input-components") {
|
||||
RegisterPass(CreateEliminateDeadInputComponentsPass());
|
||||
RegisterPass(CreateEliminateDeadInputComponentsSafePass());
|
||||
} else if (pass_name == "fix-func-call-param") {
|
||||
RegisterPass(CreateFixFuncCallArgumentsPass());
|
||||
} else if (pass_name == "convert-to-sampled-image") {
|
||||
|
@ -1016,6 +1016,18 @@ Optimizer::PassToken CreateInterpolateFixupPass() {
|
|||
}
|
||||
|
||||
Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::EliminateDeadInputComponentsPass>(
|
||||
/* output_instead */ false, /* vertex_shader_only */ false));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::EliminateDeadInputComponentsPass>(
|
||||
/* output_instead */ true, /* vertex_shader_only */ false));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::EliminateDeadInputComponentsPass>());
|
||||
}
|
||||
|
@ -1034,12 +1046,6 @@ Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
|
|||
MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::EliminateDeadInputComponentsPass>(
|
||||
/* output_instead */ true));
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateConvertToSampledImagePass(
|
||||
const std::vector<opt::DescriptorSetAndBinding>&
|
||||
descriptor_set_binding_pairs) {
|
||||
|
|
|
@ -85,7 +85,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
|
||||
|
@ -135,7 +136,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
|
||||
|
@ -202,7 +204,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
|
||||
|
@ -268,7 +271,8 @@ TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
|
||||
|
@ -350,7 +354,8 @@ TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
|
||||
|
@ -396,7 +401,8 @@ TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, ElimStructMember) {
|
||||
|
@ -460,7 +466,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimStructMember) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, ElimOutputStructMember) {
|
||||
|
@ -558,7 +565,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOutputStructMember) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, ElimOutputArrayMembers) {
|
||||
|
@ -577,7 +585,8 @@ TEST_F(ElimDeadInputComponentsTest, ElimOutputArrayMembers) {
|
|||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Vertex %main "main" %uv
|
||||
OpEntryPoint Fragment %main "main" %uv
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 450
|
||||
OpName %main "main"
|
||||
OpName %uv "uv"
|
||||
|
@ -609,7 +618,72 @@ TEST_F(ElimDeadInputComponentsTest, ElimOutputArrayMembers) {
|
|||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, true,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ElimDeadInputComponentsTest, VertexOnly) {
|
||||
// Should NOT eliminate uv
|
||||
//
|
||||
// #version 450
|
||||
//
|
||||
// in Vertex {
|
||||
// vec4 Cd;
|
||||
// vec2 uv;
|
||||
// } iVert;
|
||||
//
|
||||
// out vec4 fragColor;
|
||||
//
|
||||
// void main()
|
||||
// {
|
||||
// vec4 color = vec4(iVert.Cd);
|
||||
// fragColor = color;
|
||||
// }
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %iVert %fragColor
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource GLSL 450
|
||||
OpName %main "main"
|
||||
OpName %Vertex "Vertex"
|
||||
OpMemberName %Vertex 0 "Cd"
|
||||
OpMemberName %Vertex 1 "uv"
|
||||
OpName %iVert "iVert"
|
||||
OpName %fragColor "fragColor"
|
||||
OpDecorate %Vertex Block
|
||||
OpDecorate %iVert Location 0
|
||||
OpDecorate %fragColor Location 0
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v4float = OpTypeVector %float 4
|
||||
%v2float = OpTypeVector %float 2
|
||||
%Vertex = OpTypeStruct %v4float %v2float
|
||||
; CHECK: %Vertex = OpTypeStruct %v4float %v2float
|
||||
%_ptr_Input_Vertex = OpTypePointer Input %Vertex
|
||||
; CHECK: %_ptr_Input_Vertex = OpTypePointer Input %Vertex
|
||||
%iVert = OpVariable %_ptr_Input_Vertex Input
|
||||
; CHECK: %iVert = OpVariable %_ptr_Input_Vertex Input
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
||||
%fragColor = OpVariable %_ptr_Output_v4float Output
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%17 = OpAccessChain %_ptr_Input_v4float %iVert %int_0
|
||||
%18 = OpLoad %v4float %17
|
||||
OpStore %fragColor %18
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetTargetEnv(SPV_ENV_VULKAN_1_3);
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true, false,
|
||||
true);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
Загрузка…
Ссылка в новой задаче