mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-24 16:51:06 +00:00
Expand bindless bounds checking to runtime-sized descriptor arrays (#2316)
This commit is contained in:
parent
9b6ba4d1c5
commit
cf21146137
@ -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_
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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 :)
|
||||
|
Loading…
Reference in New Issue
Block a user