GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
87 / 98
88.8%
Branches:
32 / 46
69.6%
Functions:
11 / 12
91.7%

device/buffer.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file buffer.cpp
3 /// @brief Allocating buffers through VMA by their use, and sharing them.
4 #include "ernie/device/buffer.h"
5
6 #include <atomic>
7 #include <utility>
8
9 namespace ernie {
10 namespace {
11
12 /// Every buffer's usage: on devices with ray queries, any may hold or feed an acceleration
13 /// structure, so geometry in columns builds one in place.
14 343 VkBufferCreateInfo BufferInfo(const DeviceContext& context, std::uint64_t size,
15 VkBufferUsageFlags usage) {
16 343 const VkBufferUsageFlags acceleration =
17 343 context.Capabilities().rayQuery
18
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 343 times.
343 ? VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
19 VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR
20 343 : 0;
21 343 return {.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
22 .size = size,
23 343 .usage = usage | kBufferUsage | acceleration,
24 343 .sharingMode = VK_SHARING_MODE_EXCLUSIVE};
25 }
26
27 2 void RequireExternal(const DeviceContext& context) {
28
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!context.Capabilities().externalMemory) {
29 ✗ throw DeviceError(DeviceErrorKind::Unsupported,
30
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ context.Info().name + " cannot share memory with other APIs");
31 }
32 2 }
33
34 } // namespace
35
36 342 Buffer::Buffer(DeviceContext& context, std::uint64_t size, MemoryUse use, std::string_view name,
37
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 341 times.
342 VkBufferUsageFlags usage)
38
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 341 times.
342 : allocator_(context.Allocator()), device_(context.Device()), shared_(use == MemoryUse::Shared),
39
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 341 times.
342 size_(size) {
40 342 const VkBufferCreateInfo info = BufferInfo(context, size, usage);
41 342 VmaAllocationInfo allocated{};
42
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 341 times.
342 if (shared_) {
43 1 RequireExternal(context);
44 1 CreateExportableBuffer(allocator_, info, buffer_, allocation_, allocated);
45 1 Finish(context, allocated, name);
46 1 return;
47 }
48 341 VmaAllocationCreateInfo allocation{.usage = VMA_MEMORY_USAGE_AUTO};
49
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 220 times.
341 if (use == MemoryUse::Upload) {
50 // Cached host memory: staging is written a few rows at a time, which write-combined or
51 // device-local mappings turn into a bus transaction each.
52 121 allocation.flags =
53 VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
54
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 134 times.
220 } else if (use == MemoryUse::Readback) {
55 86 allocation.flags =
56 VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
57 } else {
58 134 allocation.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
59 }
60
2/4
✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
682 CheckVk(vmaCreateBuffer(allocator_, &info, &allocation, &buffer_, &allocation_, &allocated),
61
3/6
✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 341 times.
✗ Branch 9 not taken.
1023 "Allocating " + std::to_string(size) + " bytes for " + std::string(name));
62 341 Finish(context, allocated, name);
63 }
64
65 1 Buffer::Buffer(DeviceContext& context, ExternalHandle handle, std::uint64_t size,
66 1 std::string_view name, SharedFrom from)
67 1 : allocator_(context.Allocator()), device_(context.Device()), shared_(true), size_(size) {
68 1 RequireExternal(context);
69 1 VmaAllocationInfo allocated{};
70
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (from == SharedFrom::Vulkan) {
71 1 CreateImportedBuffer(allocator_, BufferInfo(context, size, 0), handle, buffer_, allocation_,
72 allocated);
73 } else {
74 ✗ CreateImportedBuffer(device_, context.Physical(), BufferInfo(context, size, 0), handle,
75 ✗ buffer_, memory_);
76 ✗ allocated.size = size;
77 }
78 1 Finish(context, allocated, name);
79 1 }
80
81 549 void Buffer::Destroy() {
82
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 549 times.
549 if (memory_ != VK_NULL_HANDLE) {
83 ✗ vkDestroyBuffer(device_, buffer_, nullptr);
84 ✗ vkFreeMemory(device_, memory_, nullptr);
85
2/2
✓ Branch 0 taken 343 times.
✓ Branch 1 taken 206 times.
549 } else if (buffer_ != VK_NULL_HANDLE) {
86 343 vmaDestroyBuffer(allocator_, buffer_, allocation_);
87 }
88 549 }
89
90 343 void Buffer::Finish(DeviceContext& context, const VmaAllocationInfo& allocated,
91 std::string_view name) {
92 343 const VkBufferDeviceAddressInfo addressInfo{
93 343 .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = buffer_};
94 343 address_ = vkGetBufferDeviceAddress(device_, &addressInfo);
95 343 memorySize_ = allocated.size;
96 343 static std::atomic<std::uint64_t> ids = 0;
97
2/2
✓ Branch 0 taken 207 times.
✓ Branch 1 taken 136 times.
343 id_ = ++ids;
98
2/2
✓ Branch 0 taken 207 times.
✓ Branch 1 taken 136 times.
343 if (allocated.pMappedData != nullptr) {
99 207 mapped_ = {static_cast<std::byte*>(allocated.pMappedData), static_cast<std::size_t>(size_)};
100 }
101 343 context.Name(VK_OBJECT_TYPE_BUFFER, reinterpret_cast<std::uint64_t>(buffer_), name);
102 343 }
103
104 446 Buffer::~Buffer() {
105 446 Destroy();
106 446 }
107
108 ✗ Buffer::Buffer(Buffer&& other) noexcept {
109 ✗ *this = std::move(other);
110 ✗ }
111
112 103 Buffer& Buffer::operator=(Buffer&& other) noexcept {
113
1/2
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
103 if (this != &other) {
114 103 Destroy();
115 103 allocator_ = std::exchange(other.allocator_, VK_NULL_HANDLE);
116 103 device_ = std::exchange(other.device_, VK_NULL_HANDLE);
117 103 shared_ = std::exchange(other.shared_, false);
118 103 buffer_ = std::exchange(other.buffer_, VK_NULL_HANDLE);
119 103 allocation_ = std::exchange(other.allocation_, VK_NULL_HANDLE);
120 103 memory_ = std::exchange(other.memory_, VK_NULL_HANDLE);
121 103 size_ = std::exchange(other.size_, 0);
122 103 memorySize_ = std::exchange(other.memorySize_, 0);
123 103 id_ = std::exchange(other.id_, 0);
124 103 address_ = std::exchange(other.address_, 0);
125 103 mapped_ = std::exchange(other.mapped_, {});
126 }
127 103 return *this;
128 }
129
130 121 void Buffer::Flush(std::uint64_t offset, std::uint64_t size) const {
131 121 CheckVk(vmaFlushAllocation(allocator_, allocation_, offset, size), "Flushing a buffer");
132 121 }
133
134 86 void Buffer::Invalidate(std::uint64_t offset, std::uint64_t size) const {
135 86 CheckVk(vmaInvalidateAllocation(allocator_, allocation_, offset, size),
136 "Invalidating a buffer");
137 86 }
138
139 2 ExternalHandle Buffer::Export() const {
140
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (!shared_) {
141
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
2 throw DeviceError(DeviceErrorKind::Unsupported, "Only shared buffers can be exported");
142 }
143
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (memory_ != VK_NULL_HANDLE) {
144 ✗ return ExportMemory(device_, memory_, "a shared buffer");
145 }
146 1 VmaAllocationInfo allocated{};
147 1 vmaGetAllocationInfo(allocator_, allocation_, &allocated);
148 // Shared buffers have dedicated memory: the buffer starts it.
149 1 return ExportMemory(device_, allocated.deviceMemory, "a shared buffer");
150 }
151
152 } // namespace ernie
153