Expand bindless bounds checking to runtime-sized descriptor arrays (#2316)

This commit is contained in:
greg-lunarg 2019-02-07 12:00:36 -07:00 committed by Steven Perron
parent 9b6ba4d1c5
commit cf21146137
8 changed files with 519 additions and 325 deletions

View File

@ -30,17 +30,17 @@ namespace spvtools {
// Stream Output Buffer Offsets
//
// The following values provide 32-bit word offsets into the output buffer
// The following values provide offsets into the output buffer struct
// generated by InstrumentPass::GenDebugStreamWrite. This method is utilized
// by InstBindlessCheckPass.
//
// The first word of the debug output buffer contains the next available word
// The first member of the debug output buffer contains the next available word
// in the data stream to be written. Shaders will atomically read and update
// this value so as not to overwrite each others records. This value must be
// initialized to zero
static const int kDebugOutputSizeOffset = 0;
// The second word of the output buffer is the start of the stream of records
// The second member of the output buffer is the start of the stream of records
// written by the instrumented shaders. Each record represents a validation
// error. The format of the records is documented below.
static const int kDebugOutputDataOffset = 1;
@ -122,14 +122,45 @@ static const int kInstMaxOutCnt = kInstStageOutCnt + 3;
// These are the possible validation error codes.
static const int kInstErrorBindlessBounds = 0;
// Direct Input Buffer Offsets
//
// The following values provide member offsets into the input buffers
// consumed by InstrumentPass::GenDebugDirectRead(). This method is utilized
// by InstBindlessCheckPass.
//
// The only object in an input buffer is a runtime array of unsigned
// integers. Each validation will have its own formatting of this array.
static const int kDebugInputDataOffset = 0;
// Debug Buffer Bindings
//
// These are the bindings for the different buffers which are
// read or written by the instrumentation passes.
//
// This is the output buffer written by InstBindlessCheckPass.
// This is the output buffer written by InstBindlessCheckPass
// and possibly other future validations.
static const int kDebugOutputBindingStream = 0;
// The binding for the input buffer for InstBindlessCheckPass. The input
// buffer needs only be created if the shaders being validated contain a
// descriptor array of runtime size, and validation of runtime size descriptor
// arrays have been enabled at the time of the bindless validation pass
// creation.
static const int kDebugInputBindingBindless = 1;
// Bindless Validation Input Buffer Format
//
// An input buffer for bindless validation consists of a single array of
// unsigned integers we will call Data[]. This array is formatted as follows.
//
// At the beginning of the array is a single uint reserved for future use.
static const int kDebugInputBindlessOffsetReserved = 0;
// Following the reserved uint is some number of uints such that the following
// is true: the number of descriptors at (set=s, binding=b) is:
// Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ]
static const int kDebugInputBindlessOffsetLengths = 1;
} // namespace spvtools
#endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_

View File

@ -711,10 +711,12 @@ Optimizer::PassToken CreateCombineAccessChainsPass();
// The instrumentation will read and write buffers in debug
// descriptor set |desc_set|. It will write |shader_id| in each output record
// to identify the shader module which generated the record.
// |runtime_array_enable| controls instrumentation of runtime arrays which
// require input buffer support.
//
// TODO(greg-lunarg): Add support for vk_ext_descriptor_indexing.
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
uint32_t shader_id);
Optimizer::PassToken CreateInstBindlessCheckPass(
uint32_t desc_set, uint32_t shader_id, bool runtime_array_enable = false);
// Create a pass to upgrade to the VulkanKHR memory model.
// This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.

View File

@ -35,6 +35,18 @@ static const int kSpvConstantValueInIdx = 0;
namespace spvtools {
namespace opt {
uint32_t InstBindlessCheckPass::GenDebugReadLength(
uint32_t image_id, InstructionBuilder* builder) {
uint32_t desc_set_idx =
var2desc_set_[image_id] + kDebugInputBindlessOffsetLengths;
uint32_t desc_set_idx_id = builder->GetUintConstantId(desc_set_idx);
uint32_t desc_set_offset_id = GenDebugDirectRead(desc_set_idx_id, builder);
Instruction* binding_idx_inst =
builder->AddBinaryOp(GetUintId(), SpvOpIAdd, desc_set_offset_id,
builder->GetUintConstantId(var2binding_[image_id]));
return GenDebugDirectRead(binding_idx_inst->result_id(), builder);
}
void InstBindlessCheckPass::GenBindlessCheckCode(
BasicBlock::iterator ref_inst_itr,
UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t instruction_idx,
@ -119,20 +131,23 @@ void InstBindlessCheckPass::GenBindlessCheckCode(
uint32_t ptr_type_id =
var_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx);
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
// TODO(greg-lunarg): Handle RuntimeArray. Will need to pull length
// out of debug input buffer.
if (ptr_type_inst->opcode() != SpvOpTypeArray) return;
// If index and bound both compile-time constants and index < bound,
// return without changing
uint32_t length_id =
ptr_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx);
Instruction* index_inst = get_def_use_mgr()->GetDef(index_id);
Instruction* length_inst = get_def_use_mgr()->GetDef(length_id);
if (index_inst->opcode() == SpvOpConstant &&
length_inst->opcode() == SpvOpConstant &&
index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) <
length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx))
uint32_t length_id = 0;
if (ptr_type_inst->opcode() == SpvOpTypeArray) {
length_id =
ptr_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx);
Instruction* index_inst = get_def_use_mgr()->GetDef(index_id);
Instruction* length_inst = get_def_use_mgr()->GetDef(length_id);
if (index_inst->opcode() == SpvOpConstant &&
length_inst->opcode() == SpvOpConstant &&
index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) <
length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx))
return;
} else if (!runtime_array_enabled_ ||
ptr_type_inst->opcode() != SpvOpTypeRuntimeArray) {
return;
}
// Generate full runtime bounds test code with true branch
// being full reference and false branch being debug output and zero
// for the referenced value.
@ -141,6 +156,13 @@ void InstBindlessCheckPass::GenBindlessCheckCode(
context(), &*new_blk_ptr,
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds);
// If length id not yet set, descriptor array is runtime size so
// generate load of length from stage's debug input buffer.
if (length_id == 0) {
assert(ptr_type_inst->opcode() == SpvOpTypeRuntimeArray &&
"unexpected bindless type");
length_id = GenDebugReadLength(image_id, &builder);
}
Instruction* ult_inst =
builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, index_id, length_id);
uint32_t merge_blk_id = TakeNextId();
@ -236,6 +258,18 @@ void InstBindlessCheckPass::InitializeInstBindlessCheck() {
break;
}
}
// If descriptor indexing extension and runtime array support enabled,
// create variable to descriptor set mapping.
if (ext_descriptor_indexing_defined_ && runtime_array_enabled_)
for (auto& anno : get_module()->annotations())
if (anno.opcode() == SpvOpDecorate) {
if (anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet)
var2desc_set_[anno.GetSingleWordInOperand(0u)] =
anno.GetSingleWordInOperand(2u);
else if (anno.GetSingleWordInOperand(1u) == SpvDecorationBinding)
var2binding_[anno.GetSingleWordInOperand(0u)] =
anno.GetSingleWordInOperand(2u);
}
}
Pass::Status InstBindlessCheckPass::ProcessImpl() {

View File

@ -29,10 +29,14 @@ namespace opt {
class InstBindlessCheckPass : public InstrumentPass {
public:
// For test harness only
InstBindlessCheckPass() : InstrumentPass(7, 23, kInstValidationIdBindless) {}
InstBindlessCheckPass()
: InstrumentPass(7, 23, kInstValidationIdBindless),
runtime_array_enabled_(true) {}
// For all other interfaces
InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBindless) {}
InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id,
bool runtime_array_enable)
: InstrumentPass(desc_set, shader_id, kInstValidationIdBindless),
runtime_array_enabled_(runtime_array_enable) {}
~InstBindlessCheckPass() override = default;
@ -42,6 +46,10 @@ class InstBindlessCheckPass : public InstrumentPass {
const char* name() const override { return "inst-bindless-check-pass"; }
private:
// Generate instructions into |builder| to read length of runtime descriptor
// array |image_id| from debug input buffer and return id of value.
uint32_t GenDebugReadLength(uint32_t image_id, InstructionBuilder* builder);
// Initialize state for instrumenting bindless checking
void InitializeInstBindlessCheck();
@ -85,6 +93,15 @@ class InstBindlessCheckPass : public InstrumentPass {
// True if VK_EXT_descriptor_indexing is defined
bool ext_descriptor_indexing_defined_;
// Enable instrumentation of runtime arrays
bool runtime_array_enabled_;
// Mapping from variable to descriptor set
std::unordered_map<uint32_t, uint32_t> var2desc_set_;
// Mapping from variable to binding
std::unordered_map<uint32_t, uint32_t> var2binding_;
};
} // namespace opt

View File

@ -107,7 +107,7 @@ void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id,
builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id,
builder->GetUintConstantId(field_offset));
uint32_t buf_id = GetOutputBufferId();
uint32_t buf_uint_ptr_id = GetOutputBufferUintPtrId();
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
Instruction* achain_inst =
builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
builder->GetUintConstantId(kDebugOutputDataOffset),
@ -222,6 +222,18 @@ void InstrumentPass::GenDebugStreamWrite(
(void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args);
}
uint32_t InstrumentPass::GenDebugDirectRead(uint32_t idx_id,
InstructionBuilder* builder) {
uint32_t input_buf_id = GetInputBufferId();
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
Instruction* ibuf_ac_inst = builder->AddTernaryOp(
buf_uint_ptr_id, SpvOpAccessChain, input_buf_id,
builder->GetUintConstantId(kDebugInputDataOffset), idx_id);
Instruction* load_inst =
builder->AddUnaryOp(GetUintId(), SpvOpLoad, ibuf_ac_inst->result_id());
return load_inst->result_id();
}
bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const {
return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage;
}
@ -280,12 +292,12 @@ void InstrumentPass::UpdateSucceedingPhis(
}
// Return id for output buffer uint ptr type
uint32_t InstrumentPass::GetOutputBufferUintPtrId() {
if (output_buffer_uint_ptr_id_ == 0) {
output_buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
uint32_t InstrumentPass::GetBufferUintPtrId() {
if (buffer_uint_ptr_id_ == 0) {
buffer_uint_ptr_id_ = context()->get_type_mgr()->FindPointerToType(
GetUintId(), SpvStorageClassStorageBuffer);
}
return output_buffer_uint_ptr_id_;
return buffer_uint_ptr_id_;
}
uint32_t InstrumentPass::GetOutputBufferBinding() {
@ -298,22 +310,74 @@ uint32_t InstrumentPass::GetOutputBufferBinding() {
return 0;
}
uint32_t InstrumentPass::GetInputBufferBinding() {
switch (validation_id_) {
case kInstValidationIdBindless:
return kDebugInputBindingBindless;
default:
assert(false && "unexpected validation id");
}
return 0;
}
analysis::Type* InstrumentPass::GetUintRuntimeArrayType(
analysis::DecorationManager* deco_mgr, analysis::TypeManager* type_mgr) {
if (uint_rarr_ty_ == nullptr) {
analysis::Integer uint_ty(32, false);
analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty);
uint_rarr_ty_ = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp);
uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(uint_rarr_ty_);
// By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of
// a block, and will therefore be decorated with an ArrayStride. Therefore
// the undecorated type returned here will not be pre-existing and can
// safely be decorated. Since this type is now decorated, it is out of
// sync with the TypeManager and therefore the TypeManager must be
// invalidated after this pass.
assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 &&
"used RuntimeArray type returned");
deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
}
return uint_rarr_ty_;
}
void InstrumentPass::AddStorageBufferExt() {
if (storage_buffer_ext_defined_) return;
if (!get_feature_mgr()->HasExtension(kSPV_KHR_storage_buffer_storage_class)) {
const std::string ext_name("SPV_KHR_storage_buffer_storage_class");
const auto num_chars = ext_name.size();
// Compute num words, accommodate the terminating null character.
const auto num_words = (num_chars + 1 + 3) / 4;
std::vector<uint32_t> ext_words(num_words, 0u);
std::memcpy(ext_words.data(), ext_name.data(), num_chars);
context()->AddExtension(std::unique_ptr<Instruction>(
new Instruction(context(), SpvOpExtension, 0u, 0u,
{{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
}
storage_buffer_ext_defined_ = true;
}
// Return id for output buffer
uint32_t InstrumentPass::GetOutputBufferId() {
if (output_buffer_id_ == 0) {
// If not created yet, create one
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Type* reg_uint_rarr_ty =
GetUintRuntimeArrayType(deco_mgr, type_mgr);
analysis::Integer uint_ty(32, false);
analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
analysis::RuntimeArray uint_rarr_ty(reg_uint_ty);
analysis::Type* reg_uint_rarr_ty =
type_mgr->GetRegisteredType(&uint_rarr_ty);
uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(reg_uint_rarr_ty);
deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, 4u);
analysis::Struct obuf_ty({reg_uint_ty, reg_uint_rarr_ty});
analysis::Type* reg_obuf_ty = type_mgr->GetRegisteredType(&obuf_ty);
uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_obuf_ty);
analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty});
analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
// By the Vulkan spec, a pre-existing struct containing a RuntimeArray
// must be a block, and will therefore be decorated with Block. Therefore
// the undecorated type returned here will not be pre-existing and can
// safely be decorated. Since this type is now decorated, it is out of
// sync with the TypeManager and therefore the TypeManager must be
// invalidated after this pass.
assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 &&
"used struct type returned");
deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock);
deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset,
SpvDecorationOffset, 0);
@ -331,23 +395,48 @@ uint32_t InstrumentPass::GetOutputBufferId() {
desc_set_);
deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding,
GetOutputBufferBinding());
// Look for storage buffer extension. If none, create one.
if (!get_feature_mgr()->HasExtension(
kSPV_KHR_storage_buffer_storage_class)) {
const std::string ext_name("SPV_KHR_storage_buffer_storage_class");
const auto num_chars = ext_name.size();
// Compute num words, accommodate the terminating null character.
const auto num_words = (num_chars + 1 + 3) / 4;
std::vector<uint32_t> ext_words(num_words, 0u);
std::memcpy(ext_words.data(), ext_name.data(), num_chars);
context()->AddExtension(std::unique_ptr<Instruction>(
new Instruction(context(), SpvOpExtension, 0u, 0u,
{{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}})));
}
AddStorageBufferExt();
}
return output_buffer_id_;
}
uint32_t InstrumentPass::GetInputBufferId() {
if (input_buffer_id_ == 0) {
// If not created yet, create one
analysis::DecorationManager* deco_mgr = get_decoration_mgr();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Type* reg_uint_rarr_ty =
GetUintRuntimeArrayType(deco_mgr, type_mgr);
analysis::Struct buf_ty({reg_uint_rarr_ty});
analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty);
uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty);
// By the Vulkan spec, a pre-existing struct containing a RuntimeArray
// must be a block, and will therefore be decorated with Block. Therefore
// the undecorated type returned here will not be pre-existing and can
// safely be decorated. Since this type is now decorated, it is out of
// sync with the TypeManager and therefore the TypeManager must be
// invalidated after this pass.
assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 &&
"used struct type returned");
deco_mgr->AddDecoration(ibufTyId, SpvDecorationBlock);
deco_mgr->AddMemberDecoration(ibufTyId, 0, SpvDecorationOffset, 0);
uint32_t ibufTyPtrId_ =
type_mgr->FindPointerToType(ibufTyId, SpvStorageClassStorageBuffer);
input_buffer_id_ = TakeNextId();
std::unique_ptr<Instruction> newVarOp(new Instruction(
context(), SpvOpVariable, ibufTyPtrId_, input_buffer_id_,
{{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
{SpvStorageClassStorageBuffer}}}));
context()->AddGlobalValue(std::move(newVarOp));
deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet,
desc_set_);
deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding,
GetInputBufferBinding());
AddStorageBufferExt();
}
return input_buffer_id_;
}
uint32_t InstrumentPass::GetVec4FloatId() {
if (v4float_id_ == 0) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
@ -447,7 +536,7 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
// Gen test if debug output buffer size will not be exceeded.
uint32_t obuf_record_sz = kInstStageOutCnt + val_spec_param_cnt;
uint32_t buf_id = GetOutputBufferId();
uint32_t buf_uint_ptr_id = GetOutputBufferUintPtrId();
uint32_t buf_uint_ptr_id = GetBufferUintPtrId();
Instruction* obuf_curr_sz_ac_inst =
builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id,
builder.GetUintConstantId(kDebugOutputSizeOffset));
@ -620,14 +709,17 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
void InstrumentPass::InitializeInstrument() {
output_buffer_id_ = 0;
output_buffer_uint_ptr_id_ = 0;
buffer_uint_ptr_id_ = 0;
output_func_id_ = 0;
output_func_param_cnt_ = 0;
input_buffer_id_ = 0;
v4float_id_ = 0;
uint_id_ = 0;
v4uint_id_ = 0;
bool_id_ = 0;
void_id_ = 0;
storage_buffer_ext_defined_ = false;
uint_rarr_ty_ = nullptr;
// clear collections
id2function_.clear();

View File

@ -76,7 +76,7 @@ class InstrumentPass : public Pass {
IRContext::kAnalysisInstrToBlockMapping |
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId |
IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
IRContext::kAnalysisConstants;
}
protected:
@ -195,6 +195,13 @@ class InstrumentPass : public Pass {
const std::vector<uint32_t>& validation_ids,
InstructionBuilder* builder);
// Generate in |builder| instructions to read the unsigned integer from the
// input buffer at offset |idx_id|. Return the result id.
//
// The binding and the format of the input buffer is determined by each
// specific validation, which is specified at the creation of the pass.
uint32_t GenDebugDirectRead(uint32_t idx_id, InstructionBuilder* builder);
// Generate code to cast |value_id| to unsigned, if needed. Return
// an id to the unsigned equivalent.
uint32_t GenUintCastCode(uint32_t value_id, InstructionBuilder* builder);
@ -211,15 +218,28 @@ class InstrumentPass : public Pass {
// Return id for void type
uint32_t GetVoidId();
// Return id for output buffer uint type
uint32_t GetOutputBufferUintPtrId();
// Return pointer to type for runtime array of uint
analysis::Type* GetUintRuntimeArrayType(analysis::DecorationManager* deco_mgr,
analysis::TypeManager* type_mgr);
// Return id for buffer uint type
uint32_t GetBufferUintPtrId();
// Return binding for output buffer for current validation.
uint32_t GetOutputBufferBinding();
// Return binding for input buffer for current validation.
uint32_t GetInputBufferBinding();
// Add storage buffer extension if needed
void AddStorageBufferExt();
// Return id for debug output buffer
uint32_t GetOutputBufferId();
// Return id for debug input buffer
uint32_t GetInputBufferId();
// Return id for v4float type
uint32_t GetVec4FloatId();
@ -321,7 +341,7 @@ class InstrumentPass : public Pass {
uint32_t output_buffer_id_;
// type id for output buffer element
uint32_t output_buffer_uint_ptr_id_;
uint32_t buffer_uint_ptr_id_;
// id for debug output function
uint32_t output_func_id_;
@ -329,6 +349,9 @@ class InstrumentPass : public Pass {
// param count for output function
uint32_t output_func_param_cnt_;
// id for input buffer variable
uint32_t input_buffer_id_;
// id for v4float type
uint32_t v4float_id_;
@ -344,6 +367,12 @@ class InstrumentPass : public Pass {
// id for void type
uint32_t void_id_;
// boolean to remember storage buffer extension
bool storage_buffer_ext_defined_;
// runtime array of uint type
analysis::Type* uint_rarr_ty_;
// Pre-instrumentation same-block insts
std::unordered_map<uint32_t, Instruction*> same_block_pre_;

View File

@ -57,8 +57,8 @@ Optimizer::PassToken::~PassToken() {}
struct Optimizer::Impl {
explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
spv_target_env target_env; // Target environment.
opt::PassManager pass_manager; // Internal implementation pass manager.
spv_target_env target_env; // Target environment.
opt::PassManager pass_manager; // Internal implementation pass manager.
};
Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {}
@ -380,7 +380,7 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
} else if (pass_name == "replace-invalid-opcode") {
RegisterPass(CreateReplaceInvalidOpcodePass());
} else if (pass_name == "inst-bindless-check") {
RegisterPass(CreateInstBindlessCheckPass(7, 23));
RegisterPass(CreateInstBindlessCheckPass(7, 23, true));
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
@ -789,9 +789,11 @@ Optimizer::PassToken CreateUpgradeMemoryModelPass() {
}
Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
uint32_t shader_id) {
uint32_t shader_id,
bool runtime_array_enable) {
return MakeUnique<Optimizer::PassToken::Impl>(
MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id));
MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id,
runtime_array_enable));
}
Optimizer::PassToken CreateCodeSinkingPass() {

View File

@ -630,274 +630,6 @@ OpFunctionEnd
true);
}
TEST_F(InstBindlessTest, ReuseConstsTypesBuiltins) {
// This test verifies that the pass resuses existing constants, types
// and builtin variables. This test was created by editing the SPIR-V
// from the Simple test.
const std::string defs_before =
R"(OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
OpName %g_tColor "g_tColor"
OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
OpName %_ ""
OpName %g_sAniso "g_sAniso"
OpName %i_vTextureCoords "i.vTextureCoords"
OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
OpDecorate %g_tColor DescriptorSet 3
OpDecorate %g_tColor Binding 0
OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %85 DescriptorSet 7
OpDecorate %85 Binding 0
OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%v4float = OpTypeVector %float 4
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%20 = OpTypeImage %float 2D 0 0 0 1 Unknown
%uint = OpTypeInt 32 0
%uint_128 = OpConstant %uint 128
%_arr_20_uint_128 = OpTypeArray %20 %uint_128
%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128
%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_uint_128 UniformConstant
%PerViewConstantBuffer_t = OpTypeStruct %uint
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
%35 = OpTypeSampler
%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35
%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant
%39 = OpTypeSampledImage %20
%_ptr_Input_v2float = OpTypePointer Input %v2float
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
%uint_0 = OpConstant %uint 0
%bool = OpTypeBool
%_runtimearr_uint = OpTypeRuntimeArray %uint
%_struct_83 = OpTypeStruct %uint %_runtimearr_uint
%_ptr_StorageBuffer__struct_83 = OpTypePointer StorageBuffer %_struct_83
%85 = OpVariable %_ptr_StorageBuffer__struct_83 StorageBuffer
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%uint_10 = OpConstant %uint 10
%uint_4 = OpConstant %uint 4
%uint_1 = OpConstant %uint 1
%uint_23 = OpConstant %uint 23
%uint_2 = OpConstant %uint 2
%uint_9 = OpConstant %uint 9
%uint_3 = OpConstant %uint 3
%_ptr_Input_v4float = OpTypePointer Input %v4float
%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
%v4uint = OpTypeVector %uint 4
%uint_5 = OpConstant %uint 5
%uint_6 = OpConstant %uint 6
%uint_7 = OpConstant %uint 7
%uint_8 = OpConstant %uint 8
%131 = OpConstantNull %v4float
)";
const std::string defs_after =
R"(OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
OpName %g_tColor "g_tColor"
OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
OpName %_ ""
OpName %g_sAniso "g_sAniso"
OpName %i_vTextureCoords "i.vTextureCoords"
OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
OpDecorate %g_tColor DescriptorSet 3
OpDecorate %g_tColor Binding 0
OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %10 DescriptorSet 7
OpDecorate %10 Binding 0
OpDecorate %gl_FragCoord BuiltIn FragCoord
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_34 Block
OpMemberDecorate %_struct_34 0 Offset 0
OpMemberDecorate %_struct_34 1 Offset 4
OpDecorate %74 DescriptorSet 7
OpDecorate %74 Binding 0
%void = OpTypeVoid
%12 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%v4float = OpTypeVector %float 4
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%18 = OpTypeImage %float 2D 0 0 0 1 Unknown
%uint = OpTypeInt 32 0
%uint_128 = OpConstant %uint 128
%_arr_18_uint_128 = OpTypeArray %18 %uint_128
%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128
%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant
%PerViewConstantBuffer_t = OpTypeStruct %uint
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18
%26 = OpTypeSampler
%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26
%g_sAniso = OpVariable %_ptr_UniformConstant_26 UniformConstant
%28 = OpTypeSampledImage %18
%_ptr_Input_v2float = OpTypePointer Input %v2float
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
%uint_0 = OpConstant %uint 0
%bool = OpTypeBool
%_runtimearr_uint = OpTypeRuntimeArray %uint
%_struct_34 = OpTypeStruct %uint %_runtimearr_uint
%_ptr_StorageBuffer__struct_34 = OpTypePointer StorageBuffer %_struct_34
%10 = OpVariable %_ptr_StorageBuffer__struct_34 StorageBuffer
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%uint_10 = OpConstant %uint 10
%uint_4 = OpConstant %uint 4
%uint_1 = OpConstant %uint 1
%uint_23 = OpConstant %uint 23
%uint_2 = OpConstant %uint 2
%uint_9 = OpConstant %uint 9
%uint_3 = OpConstant %uint 3
%_ptr_Input_v4float = OpTypePointer Input %v4float
%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
%v4uint = OpTypeVector %uint 4
%uint_5 = OpConstant %uint 5
%uint_6 = OpConstant %uint 6
%uint_7 = OpConstant %uint 7
%uint_8 = OpConstant %uint 8
%50 = OpConstantNull %v4float
%68 = OpTypeFunction %void %uint %uint %uint %uint
%74 = OpVariable %_ptr_StorageBuffer__struct_34 StorageBuffer
%uint_82 = OpConstant %uint 82
)";
const std::string func_before =
R"(%MainPs = OpFunction %void None %3
%5 = OpLabel
%53 = OpLoad %v2float %i_vTextureCoords
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
%64 = OpLoad %uint %63
%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64
%67 = OpLoad %35 %g_sAniso
%78 = OpLoad %20 %65
%79 = OpSampledImage %39 %78 %67
%71 = OpImageSampleImplicitLod %v4float %79 %53
OpStore %_entryPointOutput_vColor %71
OpReturn
OpFunctionEnd
)";
const std::string func_after =
R"(%MainPs = OpFunction %void None %12
%51 = OpLabel
%52 = OpLoad %v2float %i_vTextureCoords
%53 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
%54 = OpLoad %uint %53
%55 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %54
%56 = OpLoad %26 %g_sAniso
%57 = OpLoad %18 %55
%58 = OpSampledImage %28 %57 %56
%60 = OpULessThan %bool %54 %uint_128
OpSelectionMerge %61 None
OpBranchConditional %60 %62 %63
%62 = OpLabel
%64 = OpLoad %18 %55
%65 = OpSampledImage %28 %64 %56
%66 = OpImageSampleImplicitLod %v4float %65 %52
OpBranch %61
%63 = OpLabel
%105 = OpFunctionCall %void %67 %uint_82 %uint_0 %54 %uint_128
OpBranch %61
%61 = OpLabel
%106 = OpPhi %v4float %66 %62 %50 %63
OpStore %_entryPointOutput_vColor %106
OpReturn
OpFunctionEnd
)";
const std::string output_func =
R"(%67 = OpFunction %void None %68
%69 = OpFunctionParameter %uint
%70 = OpFunctionParameter %uint
%71 = OpFunctionParameter %uint
%72 = OpFunctionParameter %uint
%73 = OpLabel
%75 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_0
%76 = OpAtomicIAdd %uint %75 %uint_4 %uint_0 %uint_9
%77 = OpIAdd %uint %76 %uint_9
%78 = OpArrayLength %uint %74 1
%79 = OpULessThanEqual %bool %77 %78
OpSelectionMerge %80 None
OpBranchConditional %79 %81 %80
%81 = OpLabel
%82 = OpIAdd %uint %76 %uint_0
%83 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %82
OpStore %83 %uint_9
%84 = OpIAdd %uint %76 %uint_1
%85 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %84
OpStore %85 %uint_23
%86 = OpIAdd %uint %76 %uint_2
%87 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %86
OpStore %87 %69
%88 = OpIAdd %uint %76 %uint_3
%89 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %88
OpStore %89 %uint_4
%90 = OpLoad %v4float %gl_FragCoord
%91 = OpBitcast %v4uint %90
%92 = OpCompositeExtract %uint %91 0
%93 = OpIAdd %uint %76 %uint_4
%94 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %93
OpStore %94 %92
%95 = OpCompositeExtract %uint %91 1
%96 = OpIAdd %uint %76 %uint_5
%97 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %96
OpStore %97 %95
%98 = OpIAdd %uint %76 %uint_6
%99 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %98
OpStore %99 %70
%100 = OpIAdd %uint %76 %uint_7
%101 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %100
OpStore %101 %71
%102 = OpIAdd %uint %76 %uint_8
%103 = OpAccessChain %_ptr_StorageBuffer_uint %74 %uint_1 %102
OpStore %103 %72
OpBranch %80
%80 = OpLabel
OpReturn
OpFunctionEnd
)";
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
true);
}
TEST_F(InstBindlessTest, InstrumentOpImage) {
// This test verifies that the pass will correctly instrument shader
// using OpImage. This test was created by editing the SPIR-V
@ -1848,6 +1580,261 @@ OpFunctionEnd
true);
}
TEST_F(InstBindlessTest, RuntimeArray) {
// This test verifies that the pass will correctly instrument shader
// with runtime descriptor array. This test was created by editing the
// SPIR-V from the Simple test.
const std::string defs_before =
R"(OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_EXT_descriptor_indexing"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
OpName %g_tColor "g_tColor"
OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
OpName %_ ""
OpName %g_sAniso "g_sAniso"
OpName %i_vTextureCoords "i.vTextureCoords"
OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
OpDecorate %g_tColor DescriptorSet 0
OpDecorate %g_tColor Binding 0
OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %g_sAniso Binding 1
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%v4float = OpTypeVector %float 4
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%20 = OpTypeImage %float 2D 0 0 0 1 Unknown
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%_rarr_20 = OpTypeRuntimeArray %20
%_ptr_UniformConstant__arr_20 = OpTypePointer UniformConstant %_rarr_20
%g_tColor = OpVariable %_ptr_UniformConstant__arr_20 UniformConstant
%PerViewConstantBuffer_t = OpTypeStruct %uint
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20
%35 = OpTypeSampler
%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35
%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant
%39 = OpTypeSampledImage %20
%_ptr_Input_v2float = OpTypePointer Input %v2float
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
)";
const std::string defs_after =
R"(OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord
OpExecutionMode %MainPs OriginUpperLeft
OpSource HLSL 500
OpName %MainPs "MainPs"
OpName %g_tColor "g_tColor"
OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t"
OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx"
OpName %_ ""
OpName %g_sAniso "g_sAniso"
OpName %i_vTextureCoords "i.vTextureCoords"
OpName %_entryPointOutput_vColor "@entryPointOutput.vColor"
OpDecorate %g_tColor DescriptorSet 0
OpDecorate %g_tColor Binding 0
OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0
OpDecorate %PerViewConstantBuffer_t Block
OpDecorate %g_sAniso DescriptorSet 0
OpDecorate %g_sAniso Binding 1
OpDecorate %i_vTextureCoords Location 0
OpDecorate %_entryPointOutput_vColor Location 0
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_40 Block
OpMemberDecorate %_struct_40 0 Offset 0
OpDecorate %42 DescriptorSet 7
OpDecorate %42 Binding 1
OpDecorate %_struct_64 Block
OpMemberDecorate %_struct_64 0 Offset 0
OpMemberDecorate %_struct_64 1 Offset 4
OpDecorate %66 DescriptorSet 7
OpDecorate %66 Binding 0
OpDecorate %gl_FragCoord BuiltIn FragCoord
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%v4float = OpTypeVector %float 4
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%16 = OpTypeImage %float 2D 0 0 0 1 Unknown
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%_runtimearr_16 = OpTypeRuntimeArray %16
%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16
%g_tColor = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant
%PerViewConstantBuffer_t = OpTypeStruct %uint
%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t
%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant
%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
%24 = OpTypeSampler
%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24
%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant
%26 = OpTypeSampledImage %16
%_ptr_Input_v2float = OpTypePointer Input %v2float
%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output
%uint_0 = OpConstant %uint 0
%_runtimearr_uint = OpTypeRuntimeArray %uint
%_struct_40 = OpTypeStruct %_runtimearr_uint
%_ptr_StorageBuffer__struct_40 = OpTypePointer StorageBuffer %_struct_40
%42 = OpVariable %_ptr_StorageBuffer__struct_40 StorageBuffer
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%bool = OpTypeBool
%58 = OpTypeFunction %void %uint %uint %uint %uint
%_struct_64 = OpTypeStruct %uint %_runtimearr_uint
%_ptr_StorageBuffer__struct_64 = OpTypePointer StorageBuffer %_struct_64
%66 = OpVariable %_ptr_StorageBuffer__struct_64 StorageBuffer
%uint_9 = OpConstant %uint 9
%uint_4 = OpConstant %uint 4
%uint_23 = OpConstant %uint 23
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%_ptr_Input_v4float = OpTypePointer Input %v4float
%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
%v4uint = OpTypeVector %uint 4
%uint_5 = OpConstant %uint 5
%uint_6 = OpConstant %uint 6
%uint_7 = OpConstant %uint 7
%uint_8 = OpConstant %uint 8
%uint_59 = OpConstant %uint 59
%110 = OpConstantNull %v4float
)";
const std::string func_before =
R"(%MainPs = OpFunction %void None %3
%5 = OpLabel
%53 = OpLoad %v2float %i_vTextureCoords
%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
%64 = OpLoad %uint %63
%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64
%66 = OpLoad %20 %65
%67 = OpLoad %35 %g_sAniso
%68 = OpSampledImage %39 %66 %67
%71 = OpImageSampleImplicitLod %v4float %68 %53
OpStore %_entryPointOutput_vColor %71
OpReturn
OpFunctionEnd
)";
const std::string func_after =
R"(%MainPs = OpFunction %void None %10
%29 = OpLabel
%30 = OpLoad %v2float %i_vTextureCoords
%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
%32 = OpLoad %uint %31
%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32
%34 = OpLoad %16 %33
%35 = OpLoad %24 %g_sAniso
%36 = OpSampledImage %26 %34 %35
%44 = OpAccessChain %_ptr_StorageBuffer_uint %42 %uint_0 %uint_1
%45 = OpLoad %uint %44
%46 = OpIAdd %uint %45 %uint_0
%47 = OpAccessChain %_ptr_StorageBuffer_uint %42 %uint_0 %46
%48 = OpLoad %uint %47
%50 = OpULessThan %bool %32 %48
OpSelectionMerge %51 None
OpBranchConditional %50 %52 %53
%52 = OpLabel
%54 = OpLoad %16 %33
%55 = OpSampledImage %26 %54 %35
%56 = OpImageSampleImplicitLod %v4float %55 %30
OpBranch %51
%53 = OpLabel
%109 = OpFunctionCall %void %57 %uint_59 %uint_0 %32 %48
OpBranch %51
%51 = OpLabel
%111 = OpPhi %v4float %56 %52 %110 %53
OpStore %_entryPointOutput_vColor %111
OpReturn
OpFunctionEnd
)";
const std::string output_func =
R"(%57 = OpFunction %void None %58
%59 = OpFunctionParameter %uint
%60 = OpFunctionParameter %uint
%61 = OpFunctionParameter %uint
%62 = OpFunctionParameter %uint
%63 = OpLabel
%67 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_0
%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_9
%71 = OpIAdd %uint %70 %uint_9
%72 = OpArrayLength %uint %66 1
%73 = OpULessThanEqual %bool %71 %72
OpSelectionMerge %74 None
OpBranchConditional %73 %75 %74
%75 = OpLabel
%76 = OpIAdd %uint %70 %uint_0
%77 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %76
OpStore %77 %uint_9
%79 = OpIAdd %uint %70 %uint_1
%80 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %79
OpStore %80 %uint_23
%82 = OpIAdd %uint %70 %uint_2
%83 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %82
OpStore %83 %59
%85 = OpIAdd %uint %70 %uint_3
%86 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %85
OpStore %86 %uint_4
%89 = OpLoad %v4float %gl_FragCoord
%91 = OpBitcast %v4uint %89
%92 = OpCompositeExtract %uint %91 0
%93 = OpIAdd %uint %70 %uint_4
%94 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %93
OpStore %94 %92
%95 = OpCompositeExtract %uint %91 1
%97 = OpIAdd %uint %70 %uint_5
%98 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %97
OpStore %98 %95
%100 = OpIAdd %uint %70 %uint_6
%101 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %100
OpStore %101 %60
%103 = OpIAdd %uint %70 %uint_7
%104 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %103
OpStore %104 %61
%106 = OpIAdd %uint %70 %uint_8
%107 = OpAccessChain %_ptr_StorageBuffer_uint %66 %uint_1 %106
OpStore %107 %62
OpBranch %74
%74 = OpLabel
OpReturn
OpFunctionEnd
)";
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + output_func, true,
true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// TODO(greg-lunarg): Come up with cases to put here :)