Skip to content

Commit a4dac01

Browse files
committed
Vulkan: wrap external buffers as regions
1 parent d58798a commit a4dac01

7 files changed

Lines changed: 320 additions & 51 deletions

File tree

src/runtime/HalideRuntimeVulkan.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,21 @@ extern int halide_vulkan_release_context(void *user_context,
105105
VkDevice device,
106106
VkQueue queue,
107107
VkDebugUtilsMessengerEXT messenger);
108+
109+
/** Wrap an externally-owned VkBuffer with a byte offset. */
110+
extern int halide_vulkan_wrap_vk_buffer(void *user_context,
111+
struct halide_buffer_t *buf,
112+
uint64_t vk_buffer,
113+
uint64_t offset);
114+
115+
/** Detach a wrapped VkBuffer without destroying it. */
116+
extern int halide_vulkan_detach_vk_buffer(void *user_context, halide_buffer_t *buf);
117+
118+
/** Return the underlying VkBuffer for a halide_buffer_t. */
119+
extern uintptr_t halide_vulkan_get_vk_buffer(void *user_context, halide_buffer_t *buf);
120+
121+
/** Return the total byte offset for a wrapped or cropped Vulkan buffer. */
122+
extern uint64_t halide_vulkan_get_vk_crop_offset(void *user_context, halide_buffer_t *buf);
108123
// --
109124

110125
// Override the default allocation callbacks (default uses Vulkan runtime implementation)

src/runtime/internal/memory_resources.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,21 @@ struct RegionIndexing {
7878
int32_t offset = 0; //< indexing offset from start of region (used to adjust indices in compute shader to avoid alignment constraints for arbitrary crops)
7979
};
8080

81+
enum class MemoryOwnership {
82+
Owned, //< region metadata managed by allocator; handle points to native resource
83+
CropAlias, //< alias metadata for a cropped view; owner points to root region
84+
Wrapped //< metadata supplied by caller for an external native resource
85+
};
86+
8187
// Client-facing struct for exchanging memory region allocation requests
8288
struct MemoryRegion {
8389
void *handle = nullptr; //< client data storing native handle (managed by alloc_block_region/free_block_region) or a pointer to region owning allocation
8490
RegionAllocation allocation; //< allocation in parent block for region
8591
RegionIndexing indexing; //< indexing adjustments for controlling access
8692
bool dedicated = false; //< flag indicating whether allocation is one dedicated resource (or split/shared into other resources)
8793
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 allocation.
94+
MemoryOwnership ownership = MemoryOwnership::Owned;
95+
MemoryRegion *owner = nullptr;
8896
MemoryProperties properties; //< properties for the allocated region
8997
};
9098

src/runtime/runtime_api.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,14 @@ extern "C" __attribute__((used)) void *halide_runtime_api_functions[] = {
213213
(void *)&halide_d3d12compute_release_context,
214214
(void *)&halide_d3d12compute_run,
215215
(void *)&halide_vulkan_acquire_context,
216+
(void *)&halide_vulkan_detach_vk_buffer,
216217
(void *)&halide_vulkan_device_interface,
218+
(void *)&halide_vulkan_get_vk_buffer,
219+
(void *)&halide_vulkan_get_vk_crop_offset,
217220
(void *)&halide_vulkan_initialize_kernels,
218221
(void *)&halide_vulkan_release_context,
219222
(void *)&halide_vulkan_run,
223+
(void *)&halide_vulkan_wrap_vk_buffer,
220224
(void *)&halide_webgpu_device_interface,
221225
(void *)&halide_webgpu_initialize_kernels,
222226
(void *)&halide_webgpu_finalize_kernels,

src/runtime/vulkan.cpp

Lines changed: 171 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,86 @@
1010
#include "vulkan_resources.h"
1111

1212
using namespace Halide::Runtime::Internal::Vulkan;
13+
using Halide::Runtime::Internal::MemoryOwnership;
14+
15+
// --------------------------------------------------------------------------
16+
17+
namespace Halide {
18+
namespace Runtime {
19+
namespace Internal {
20+
namespace Vulkan {
21+
22+
ALWAYS_INLINE uint64_t vk_external_buffer_offset_bytes(void *user_context, VulkanMemoryAllocator *allocator, MemoryRegion *region) {
23+
halide_debug_assert(user_context, region != nullptr);
24+
MemoryRegion *owner = allocator->owner_of(user_context, region);
25+
return (owner != nullptr && owner->ownership == MemoryOwnership::Wrapped) ? owner->allocation.offset : 0;
26+
}
27+
28+
ALWAYS_INLINE uint64_t vk_total_buffer_offset_bytes(void *user_context, VulkanMemoryAllocator *allocator, MemoryRegion *region, halide_type_t type) {
29+
return vk_external_buffer_offset_bytes(user_context, allocator, region) + (region->indexing.offset * type.bytes());
30+
}
31+
32+
ALWAYS_INLINE void vk_destroy_wrapped_region(void *user_context,
33+
VulkanMemoryAllocator *allocator,
34+
MemoryRegion *region) {
35+
if (region == nullptr) {
36+
return;
37+
}
38+
39+
MemoryRegion *owner = allocator->owner_of(user_context, region);
40+
if (owner == nullptr) {
41+
return;
42+
}
43+
44+
if (region != owner && region->ownership == MemoryOwnership::CropAlias) {
45+
vk_host_free(user_context, region, allocator->callbacks());
46+
return;
47+
}
48+
49+
if (owner->ownership != MemoryOwnership::Wrapped) {
50+
return;
51+
}
52+
53+
vk_host_free(user_context, owner->handle, allocator->callbacks());
54+
vk_host_free(user_context, owner, allocator->callbacks());
55+
}
56+
57+
ALWAYS_INLINE MemoryRegion *vk_create_wrapped_buffer_region(void *user_context,
58+
halide_buffer_t *buf,
59+
VkBuffer vk_buffer,
60+
uint64_t offset,
61+
const VkAllocationCallbacks *callbacks) {
62+
constexpr VkSystemAllocationScope alloc_scope = VK_SYSTEM_ALLOCATION_SCOPE_OBJECT;
63+
VkBuffer *native_handle = reinterpret_cast<VkBuffer *>(
64+
vk_host_malloc(user_context, sizeof(VkBuffer), 0, alloc_scope, callbacks));
65+
if (native_handle == nullptr) {
66+
error(user_context) << "Vulkan: Failed to allocate wrapped buffer handle metadata.\n";
67+
return nullptr;
68+
}
69+
70+
MemoryRegion *region = reinterpret_cast<MemoryRegion *>(
71+
vk_host_malloc(user_context, sizeof(MemoryRegion), 0, alloc_scope, callbacks));
72+
if (region == nullptr) {
73+
vk_host_free(user_context, native_handle, callbacks);
74+
error(user_context) << "Vulkan: Failed to allocate wrapped buffer region metadata.\n";
75+
return nullptr;
76+
}
77+
78+
*native_handle = vk_buffer;
79+
memset(region, 0, sizeof(MemoryRegion));
80+
region->handle = native_handle;
81+
region->allocation.offset = offset;
82+
region->allocation.size = buf->size_in_bytes();
83+
region->is_owner = true;
84+
region->ownership = MemoryOwnership::Wrapped;
85+
region->owner = nullptr;
86+
return region;
87+
}
88+
89+
} // namespace Vulkan
90+
} // namespace Internal
91+
} // namespace Runtime
92+
} // namespace Halide
1393

1494
// --------------------------------------------------------------------------
1595

@@ -110,12 +190,20 @@ WEAK int halide_vulkan_device_free(void *user_context, halide_buffer_t *halide_b
110190

111191
// get the allocated region for the device
112192
MemoryRegion *device_region = reinterpret_cast<MemoryRegion *>(halide_buffer->device);
193+
#ifdef DEBUG_RUNTIME
194+
const uint64_t device_region_size = device_region->allocation.size;
195+
#endif
113196
MemoryRegion *memory_region = ctx.allocator->owner_of(user_context, device_region);
114197
if (ctx.allocator && memory_region && memory_region->handle) {
115-
if (halide_can_reuse_device_allocations(user_context)) {
116-
ctx.allocator->release(user_context, memory_region);
198+
if (memory_region->ownership == MemoryOwnership::Wrapped) {
199+
debug(user_context) << "Vulkan: Releasing wrapped external buffer metadata only.\n";
200+
vk_destroy_wrapped_region(user_context, ctx.allocator, device_region);
117201
} else {
118-
ctx.allocator->reclaim(user_context, memory_region);
202+
if (halide_can_reuse_device_allocations(user_context)) {
203+
ctx.allocator->release(user_context, memory_region);
204+
} else {
205+
ctx.allocator->reclaim(user_context, memory_region);
206+
}
119207
}
120208
}
121209
halide_buffer->device = 0;
@@ -126,7 +214,7 @@ WEAK int halide_vulkan_device_free(void *user_context, halide_buffer_t *halide_b
126214
debug(user_context) << "Vulkan: Released memory for device region ("
127215
<< "user_context: " << user_context << ", "
128216
<< "buffer: " << halide_buffer << ", "
129-
<< "size_in_bytes: " << (uint64_t)device_region->allocation.size << ")\n";
217+
<< "size_in_bytes: " << device_region_size << ")\n";
130218

131219
uint64_t t_after = halide_current_time_ns(user_context);
132220
debug(user_context) << " Time: " << (t_after - t_before) / 1.0e6 << " ms\n";
@@ -272,15 +360,24 @@ WEAK int halide_vulkan_device_malloc(void *user_context, halide_buffer_t *buf) {
272360
size_t size = buf->size_in_bytes();
273361
if (buf->device) {
274362
MemoryRegion *device_region = (MemoryRegion *)(buf->device);
275-
if (device_region->allocation.size >= size) {
363+
MemoryRegion *memory_region = ctx.allocator->owner_of(user_context, device_region);
364+
if (memory_region != nullptr && memory_region->allocation.size >= size) {
276365
debug(user_context) << "Vulkan: Requested allocation for existing device memory ... using existing buffer!\n";
277366
return halide_error_code_success;
278367
} else {
368+
if (memory_region == nullptr) {
369+
error(user_context) << "Vulkan: Failed to retrieve memory region for existing device buffer!\n";
370+
return halide_error_code_internal_error;
371+
}
372+
if (memory_region->ownership == MemoryOwnership::Wrapped) {
373+
error(user_context) << "Vulkan: Wrapped external buffer is too small for requested allocation!\n";
374+
return halide_error_code_device_malloc_failed;
375+
}
279376
debug(user_context) << "Vulkan: Requested allocation of different size ... reallocating buffer!\n";
280377
if (halide_can_reuse_device_allocations(user_context)) {
281-
ctx.allocator->release(user_context, device_region);
378+
ctx.allocator->release(user_context, memory_region);
282379
} else {
283-
ctx.allocator->reclaim(user_context, device_region);
380+
ctx.allocator->reclaim(user_context, memory_region);
284381
}
285382
buf->device = 0;
286383
}
@@ -487,7 +584,7 @@ WEAK int halide_vulkan_copy_to_device(void *user_context, halide_buffer_t *halid
487584
bool to_host = false;
488585

489586
uint64_t src_offset = copy_helper.src_begin;
490-
uint64_t dst_offset = copy_helper.dst_begin + (device_region->indexing.offset * halide_buffer->type.bytes());
587+
uint64_t dst_offset = copy_helper.dst_begin + vk_total_buffer_offset_bytes(user_context, ctx.allocator, device_region, halide_buffer->type);
491588

492589
copy_helper.src = (uint64_t)(staging_buffer);
493590
copy_helper.dst = (uint64_t)(device_buffer);
@@ -656,7 +753,7 @@ WEAK int halide_vulkan_copy_to_host(void *user_context, halide_buffer_t *halide_
656753
bool from_host = false;
657754
bool to_host = true;
658755
uint64_t copy_dst = copy_helper.dst;
659-
uint64_t src_offset = copy_helper.src_begin + (device_region->indexing.offset * halide_buffer->type.bytes());
756+
uint64_t src_offset = copy_helper.src_begin + vk_total_buffer_offset_bytes(user_context, ctx.allocator, device_region, halide_buffer->type);
660757
uint64_t dst_offset = copy_helper.dst_begin;
661758

662759
copy_helper.src = (uint64_t)(device_buffer);
@@ -937,8 +1034,8 @@ WEAK int halide_vulkan_buffer_copy(void *user_context, struct halide_buffer_t *s
9371034

9381035
// define the src and dst config
9391036
uint64_t copy_dst = copy_helper.dst;
940-
uint64_t src_offset = copy_helper.src_begin + (src_buffer_region->indexing.offset * src->type.bytes());
941-
uint64_t dst_offset = copy_helper.dst_begin + (dst_buffer_region->indexing.offset * dst->type.bytes());
1037+
uint64_t src_offset = copy_helper.src_begin + vk_total_buffer_offset_bytes(user_context, ctx.allocator, src_buffer_region, src->type);
1038+
uint64_t dst_offset = copy_helper.dst_begin + vk_total_buffer_offset_bytes(user_context, ctx.allocator, dst_buffer_region, dst->type);
9421039

9431040
copy_helper.src = (uint64_t)(src_device_buffer);
9441041
copy_helper.dst = (uint64_t)(dst_device_buffer);
@@ -1345,13 +1442,33 @@ WEAK int halide_vulkan_device_and_host_free(void *user_context, struct halide_bu
13451442
return halide_default_device_and_host_free(user_context, buf, &vulkan_device_interface);
13461443
}
13471444

1348-
WEAK int halide_vulkan_wrap_vk_buffer(void *user_context, struct halide_buffer_t *buf, uint64_t vk_buffer) {
1445+
WEAK int halide_vulkan_wrap_vk_buffer(void *user_context,
1446+
struct halide_buffer_t *buf,
1447+
uint64_t vk_buffer,
1448+
uint64_t offset) {
13491449
halide_debug_assert(user_context, buf->device == 0);
13501450
if (buf->device != 0) {
13511451
error(user_context) << "Vulkan: Unable to wrap buffer ... invalid device pointer!\n";
13521452
return halide_error_code_device_wrap_native_failed;
13531453
}
1354-
buf->device = vk_buffer;
1454+
if (vk_buffer == 0) {
1455+
error(user_context) << "Vulkan: Unable to wrap buffer ... invalid VkBuffer handle!\n";
1456+
return halide_error_code_device_wrap_native_failed;
1457+
}
1458+
1459+
VulkanContext ctx(user_context);
1460+
if (ctx.error != halide_error_code_success) {
1461+
error(user_context) << "Vulkan: Failed to acquire context!\n";
1462+
return ctx.error;
1463+
}
1464+
1465+
MemoryRegion *region = vk_create_wrapped_buffer_region(
1466+
user_context, buf, reinterpret_cast<VkBuffer>(vk_buffer), offset, ctx.allocator->callbacks());
1467+
if (region == nullptr) {
1468+
return halide_error_code_out_of_memory;
1469+
}
1470+
1471+
buf->device = reinterpret_cast<uint64_t>(region);
13551472
buf->device_interface = &vulkan_device_interface;
13561473
buf->device_interface->impl->use_module();
13571474
return halide_error_code_success;
@@ -1365,6 +1482,19 @@ WEAK int halide_vulkan_detach_vk_buffer(void *user_context, halide_buffer_t *buf
13651482
error(user_context) << "Vulkan: Unable to detach buffer ... invalid device interface!\n";
13661483
return halide_error_code_incompatible_device_interface;
13671484
}
1485+
MemoryRegion *device_region = reinterpret_cast<MemoryRegion *>(buf->device);
1486+
VulkanContext ctx(user_context);
1487+
if (ctx.error != halide_error_code_success) {
1488+
error(user_context) << "Vulkan: Failed to acquire context!\n";
1489+
return ctx.error;
1490+
}
1491+
1492+
MemoryRegion *owner = ctx.allocator->owner_of(user_context, device_region);
1493+
if (owner == nullptr || owner->ownership != MemoryOwnership::Wrapped) {
1494+
error(user_context) << "Vulkan: Unable to detach buffer ... buffer is not externally wrapped!\n";
1495+
return halide_error_code_device_detach_native_failed;
1496+
}
1497+
vk_destroy_wrapped_region(user_context, ctx.allocator, device_region);
13681498
buf->device = 0;
13691499
buf->device_interface->impl->release_module();
13701500
buf->device_interface = nullptr;
@@ -1376,7 +1506,29 @@ WEAK uintptr_t halide_vulkan_get_vk_buffer(void *user_context, halide_buffer_t *
13761506
return 0;
13771507
}
13781508
halide_debug_assert(user_context, buf->device_interface == &vulkan_device_interface);
1379-
return (uintptr_t)buf->device;
1509+
MemoryRegion *device_region = reinterpret_cast<MemoryRegion *>(buf->device);
1510+
VulkanContext ctx(user_context);
1511+
if (ctx.error != halide_error_code_success) {
1512+
return 0;
1513+
}
1514+
MemoryRegion *owner = ctx.allocator->owner_of(user_context, device_region);
1515+
if (owner == nullptr || owner->handle == nullptr) {
1516+
return 0;
1517+
}
1518+
return (uintptr_t)(*reinterpret_cast<VkBuffer *>(owner->handle));
1519+
}
1520+
1521+
WEAK uint64_t halide_vulkan_get_vk_crop_offset(void *user_context, halide_buffer_t *buf) {
1522+
if (buf->device == 0) {
1523+
return 0;
1524+
}
1525+
halide_debug_assert(user_context, buf->device_interface == &vulkan_device_interface);
1526+
MemoryRegion *device_region = reinterpret_cast<MemoryRegion *>(buf->device);
1527+
VulkanContext ctx(user_context);
1528+
if (ctx.error != halide_error_code_success) {
1529+
return 0;
1530+
}
1531+
return vk_total_buffer_offset_bytes(user_context, ctx.allocator, device_region, buf->type);
13801532
}
13811533

13821534
WEAK const struct halide_device_interface_t *halide_vulkan_device_interface() {
@@ -1445,6 +1597,10 @@ namespace Vulkan {
14451597

14461598
// --------------------------------------------------------------------------
14471599

1600+
int vk_wrap_native_vk_buffer(void *user_context, struct halide_buffer_t *buf, uint64_t vk_buffer) {
1601+
return halide_vulkan_wrap_vk_buffer(user_context, buf, vk_buffer, 0);
1602+
}
1603+
14481604
WEAK halide_device_interface_impl_t vulkan_device_interface_impl = {
14491605
halide_use_jit_module,
14501606
halide_release_jit_module,
@@ -1460,7 +1616,7 @@ WEAK halide_device_interface_impl_t vulkan_device_interface_impl = {
14601616
halide_vulkan_device_crop,
14611617
halide_vulkan_device_slice,
14621618
halide_vulkan_device_release_crop,
1463-
halide_vulkan_wrap_vk_buffer,
1619+
vk_wrap_native_vk_buffer,
14641620
halide_vulkan_detach_vk_buffer,
14651621
};
14661622

0 commit comments

Comments
 (0)