Skip to content

Commit d5a30fb

Browse files
[vulkan] Add a buffer index offset during codegen to allow arbitrary crop offsets (#8954)
* Add an index offset during codegen to allow arbitrary offsets when accessing buffers to avoid restrictive alignment constraints. CodeGen now adds one int32 buffer offset param for each buffer after all other scalar args The runtime packs these params into the uniform buffer for each storage buffer Crop device now computes an index offset (instead of a byte offset Copy to/from device recomputes a byte offset from this index offset * Refactor cleanup to remove unused MemoryRegion.range. Adjusted relative offsets to use indexing for buffer copies. Added RegionAllocation and RegionIndexing to clarify mapping. Updated all affected interfaces. * Clang formatting pass
1 parent 0d5335c commit d5a30fb

File tree

7 files changed

+267
-135
lines changed

7 files changed

+267
-135
lines changed

src/CodeGen_Vulkan_Dev.cpp

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ class CodeGen_Vulkan_Dev : public CodeGen_GPU_Dev {
147147
void store_at_scalar_index(const Store *op, SpvId index_id, SpvId variable_id, Type value_type, Type storage_type, SpvStorageClass storage_class, SpvId value_id);
148148
void store_at_vector_index(const Store *op, SpvId variable_id, Type value_type, Type storage_type, SpvStorageClass storage_class, SpvId value_id);
149149

150+
SpvId apply_storage_buffer_offset(SpvId variable_id, SpvId index_id);
151+
150152
SpvFactory::Components split_vector(Type type, SpvId value_id);
151153
SpvId join_vector(Type type, const SpvFactory::Components &value_components);
152154
SpvId fill_vector(Type type, SpvId value_id);
@@ -237,6 +239,9 @@ class CodeGen_Vulkan_Dev : public CodeGen_GPU_Dev {
237239
using StorageAccessMap = std::unordered_map<SpvId, StorageAccess>;
238240
StorageAccessMap storage_access_map;
239241

242+
using StorageBufferOffsetMap = std::unordered_map<SpvId, SpvId>;
243+
StorageBufferOffsetMap storage_buffer_offset_map;
244+
240245
// Defines the binding information for a specialization constant
241246
// that is exported by the module and can be overriden at runtime
242247
struct SpecializationBinding {
@@ -1373,6 +1378,27 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::visit(const Select *op) {
13731378
builder.update_id(result_id);
13741379
}
13751380

1381+
SpvId CodeGen_Vulkan_Dev::SPIRV_Emitter::apply_storage_buffer_offset(SpvId variable_id, SpvId index_id) {
1382+
auto offset_map_it = storage_buffer_offset_map.find(variable_id);
1383+
if (offset_map_it == storage_buffer_offset_map.end()) {
1384+
return index_id;
1385+
}
1386+
1387+
SpvId offset_id = offset_map_it->second;
1388+
SpvId index_type_id = builder.declare_type(Int(32));
1389+
SpvId adjusted_index_id = builder.reserve_id(SpvResultId);
1390+
1391+
debug(2) << "CodeGen_Vulkan_Dev::SPIRV_Emitter::apply_storage_buffer_offset(): "
1392+
<< "variable_id=" << variable_id << " "
1393+
<< "index_type_id=" << index_type_id << " "
1394+
<< "index_id=" << index_id << " "
1395+
<< "offset_id=" << offset_id << " "
1396+
<< "adjusted_index_id=" << adjusted_index_id << "\n";
1397+
1398+
builder.append(SpvFactory::integer_add(index_type_id, adjusted_index_id, index_id, offset_id));
1399+
return adjusted_index_id;
1400+
}
1401+
13761402
void CodeGen_Vulkan_Dev::SPIRV_Emitter::load_from_scalar_index(const Load *op, SpvId index_id, SpvId variable_id, Type value_type, Type storage_type, SpvStorageClass storage_class) {
13771403
debug(2) << "CodeGen_Vulkan_Dev::SPIRV_Emitter::load_from_scalar_index(): "
13781404
<< "index_id=" << index_id << " "
@@ -1392,7 +1418,7 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::load_from_scalar_index(const Load *op, S
13921418

13931419
uint32_t zero = 0;
13941420
SpvId src_id = SpvInvalidId;
1395-
SpvId src_index_id = index_id;
1421+
SpvId src_index_id = apply_storage_buffer_offset(variable_id, index_id);
13961422
if (storage_class == SpvStorageClassUniform) {
13971423
if (builder.is_struct_type(base_type_id)) {
13981424
SpvId zero_id = builder.declare_constant(UInt(32), &zero);
@@ -1490,7 +1516,7 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::store_at_scalar_index(const Store *op, S
14901516

14911517
uint32_t zero = 0;
14921518
SpvId dst_id = SpvInvalidId;
1493-
SpvId dst_index_id = index_id;
1519+
SpvId dst_index_id = apply_storage_buffer_offset(variable_id, index_id);
14941520

14951521
SpvId ptr_type_id = builder.declare_pointer_type(storage_type, storage_class);
14961522
if (storage_class == SpvStorageClassUniform) {
@@ -2259,6 +2285,7 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::reset() {
22592285
SymbolScope empty;
22602286
symbol_table.swap(empty);
22612287
storage_access_map.clear();
2288+
storage_buffer_offset_map.clear();
22622289
descriptor_set_table.clear();
22632290
reset_workgroup_size();
22642291
}
@@ -2598,6 +2625,14 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_entry_point(const Stmt &s, SpvId
25982625
builder.add_entry_point(kernel_func_id, SpvExecutionModelGLCompute, entry_point_variables);
25992626
}
26002627

2628+
namespace {
2629+
2630+
uint32_t align_offset(uint32_t offset, uint32_t alignment) {
2631+
return (offset + (alignment - 1)) & ~(alignment - 1);
2632+
}
2633+
2634+
} // namespace
2635+
26012636
void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint32_t entry_point_index,
26022637
const std::string &entry_point_name,
26032638
const std::vector<DeviceArgument> &args) {
@@ -2621,7 +2656,9 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint3
26212656

26222657
// GLSL-style: each input buffer is a runtime array in a buffer struct
26232658
// All other params get passed in as a single uniform block
2624-
// First, need to count scalar parameters to construct the uniform struct
2659+
// First, need to count scalar parameters and buffer parameters to construct the uniform struct
2660+
uint32_t scalar_arg_count = 0;
2661+
uint32_t buffer_arg_count = 0;
26252662
SpvBuilder::StructMemberTypes param_struct_members;
26262663
for (const auto &arg : args) {
26272664
if (!arg.is_buffer) {
@@ -2634,11 +2671,21 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint3
26342671

26352672
SpvId arg_type_id = builder.declare_type(arg.type);
26362673
param_struct_members.push_back(arg_type_id);
2674+
scalar_arg_count++;
2675+
} else {
2676+
buffer_arg_count++;
26372677
}
26382678
}
26392679

2680+
// Add a buffer offset parameter for each buffer (one Int32 per buffer)
2681+
// to support crops at arbitrary index offsets.
2682+
Type offset_type = Int(32);
2683+
SpvId offset_type_id = builder.declare_type(offset_type);
2684+
param_struct_members.insert(param_struct_members.end(), size_t(buffer_arg_count), offset_type_id);
2685+
26402686
// Add a binding for a uniform buffer packed with all scalar args
26412687
uint32_t binding_counter = 0;
2688+
SpvId param_pack_var_id = SpvInvalidId;
26422689
if (!param_struct_members.empty()) {
26432690

26442691
const std::string struct_name = std::string("k") + std::to_string(kernel_index) + std::string("_args_struct");
@@ -2647,6 +2694,8 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint3
26472694
// Add a decoration describing the offset for each parameter struct member
26482695
uint32_t param_member_index = 0;
26492696
uint32_t param_member_offset = 0;
2697+
2698+
// First, add decorations for each scalar arg
26502699
for (const auto &arg : args) {
26512700
if (!arg.is_buffer) {
26522701
SpvBuilder::Literals param_offset_literals = {param_member_offset};
@@ -2656,13 +2705,24 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint3
26562705
}
26572706
}
26582707

2708+
// Force alignment for the parameter offset (e.g. all Int32 members in Uniform blocks must be 4-byte aligned)
2709+
param_member_offset = align_offset(param_member_offset, offset_type.bytes());
2710+
2711+
// Next, add a decoration for the storage buffer offsets
2712+
for (uint32_t b = 0; b < buffer_arg_count; b++) {
2713+
SpvBuilder::Literals param_offset_literals = {param_member_offset};
2714+
builder.add_struct_annotation(param_struct_type_id, param_member_index, SpvDecorationOffset, param_offset_literals);
2715+
param_member_offset += offset_type.bytes();
2716+
param_member_index++;
2717+
}
2718+
26592719
// Add a Block decoration for the parameter pack itself
26602720
builder.add_annotation(param_struct_type_id, SpvDecorationBlock);
26612721

26622722
// Add a variable for the parameter pack
26632723
const std::string param_pack_var_name = std::string("k") + std::to_string(kernel_index) + std::string("_args_var");
26642724
SpvId param_pack_ptr_type_id = builder.declare_pointer_type(param_struct_type_id, SpvStorageClassUniform);
2665-
SpvId param_pack_var_id = builder.declare_global_variable(param_pack_var_name, param_pack_ptr_type_id, SpvStorageClassUniform);
2725+
param_pack_var_id = builder.declare_global_variable(param_pack_var_name, param_pack_ptr_type_id, SpvStorageClassUniform);
26662726

26672727
// We always pass in the parameter pack as the first binding
26682728
SpvBuilder::Literals binding_index = {0};
@@ -2672,7 +2732,7 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint3
26722732
descriptor_set.uniform_buffer_count++;
26732733
binding_counter++;
26742734

2675-
// Declare all the args with appropriate offsets into the parameter struct
2735+
// Declare all the scalar args with appropriate offsets into the parameter struct
26762736
uint32_t scalar_index = 0;
26772737
for (const auto &arg : args) {
26782738
if (!arg.is_buffer) {
@@ -2692,6 +2752,8 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint3
26922752
}
26932753

26942754
// Add bindings for all device buffers declared as GLSL-style buffer blocks in uniform storage
2755+
// and adjust the indices with the appropriate storage buffer offsets (to support arbitrary crops)
2756+
uint32_t buffer_index = 0;
26952757
for (const auto &arg : args) {
26962758
if (arg.is_buffer) {
26972759

@@ -2741,6 +2803,24 @@ void CodeGen_Vulkan_Dev::SPIRV_Emitter::declare_device_args(const Stmt &s, uint3
27412803
access.storage_class = storage_class;
27422804
storage_access_map[buffer_block_var_id] = access;
27432805
descriptor_set.storage_buffer_count++;
2806+
2807+
// Load the storage buffer offset for this buffer from the uniform struct
2808+
// These offsets are stored *after* all scalar args in the uniform struct
2809+
if (param_pack_var_id != SpvInvalidId) {
2810+
uint32_t buffer_offset_index_param = scalar_arg_count + buffer_index;
2811+
SpvId buffer_offset_index_param_id = builder.declare_constant(UInt(32), &buffer_offset_index_param);
2812+
SpvId index_ptr_type_id = builder.declare_pointer_type(offset_type_id, SpvStorageClassUniform);
2813+
SpvFactory::Indices buffer_offset_index_access_indices = {buffer_offset_index_param_id};
2814+
SpvId buffer_offset_index_access_chain = builder.declare_access_chain(index_ptr_type_id, param_pack_var_id, buffer_offset_index_access_indices);
2815+
2816+
SpvId buffer_offset_index_id = builder.reserve_id(SpvResultId);
2817+
builder.append(SpvFactory::load(offset_type_id, buffer_offset_index_id, buffer_offset_index_access_chain));
2818+
2819+
// Store the mapping from the parameter defining the buffer index offset to the variable it should be applied to
2820+
storage_buffer_offset_map[buffer_block_var_id] = buffer_offset_index_id;
2821+
}
2822+
2823+
buffer_index++;
27442824
}
27452825
}
27462826

src/runtime/internal/memory_resources.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,18 +69,20 @@ struct MemoryBlock {
6969
MemoryProperties properties; //< properties for the allocated block
7070
};
7171

72-
// Client-facing struct for specifying a range of a memory region (eg for crops)
73-
struct MemoryRange {
74-
size_t head_offset = 0; //< byte offset from start of region
75-
size_t tail_offset = 0; //< byte offset from end of region
72+
struct RegionAllocation {
73+
size_t offset = 0; //< offset from base address in block (in bytes)
74+
size_t size = 0; //< allocated size in block (in bytes)
75+
};
76+
77+
struct RegionIndexing {
78+
int32_t offset = 0; //< indexing offset from start of region (used to adjust indices in compute shader to avoid alignment constraints for arbitrary crops)
7679
};
7780

7881
// Client-facing struct for exchanging memory region allocation requests
7982
struct MemoryRegion {
8083
void *handle = nullptr; //< client data storing native handle (managed by alloc_block_region/free_block_region) or a pointer to region owning allocation
81-
size_t offset = 0; //< offset from base address in block (in bytes)
82-
size_t size = 0; //< allocated size (in bytes)
83-
MemoryRange range; //< optional range (e.g. for handling crops, etc)
84+
RegionAllocation allocation; //< allocation in parent block for region
85+
RegionIndexing indexing; //< indexing adjustments for controlling access
8486
bool dedicated = false; //< flag indicating whether allocation is one dedicated resource (or split/shared into other resources)
8587
bool is_owner = true; //< flag indicating whether allocation is owned by this region, in which case handle is a native handle. Otherwise handle points to owning region of alloction.
8688
MemoryProperties properties; //< properties for the allocated region

0 commit comments

Comments
 (0)