GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
40 / 97
41.2%
Branches:
8 / 66
12.1%
Functions:
7 / 14
50.0%

device/external.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file external.cpp
3 /// @brief Exporting and importing memory and semaphores through file descriptors or NT handles.
4 #if defined(_WIN32)
5 #define VK_USE_PLATFORM_WIN32_KHR
6 #include <windows.h>
7 #else
8 #include <unistd.h>
9 #endif
10
11 #include "ernie/device/external.h"
12 #include "ernie/util/on_failure.h"
13
14 #include <string>
15 #include <utility>
16
17 namespace ernie {
18 namespace {
19
20 #if defined(_WIN32)
21 using MemoryImport = VkImportMemoryWin32HandleInfoKHR;
22 #else
23 using MemoryImport = VkImportMemoryFdInfoKHR;
24 #endif
25
26 /// The allocation info importing the memory behind `handle`.
27 1 MemoryImport ImportInfo(const ExternalHandle& handle) {
28 #if defined(_WIN32)
29 return {.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
30 .handleType = kExternalMemoryType,
31 .handle = reinterpret_cast<HANDLE>(handle.Get())};
32 #else
33 1 return {.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
34 .handleType = kExternalMemoryType,
35 1 .fd = static_cast<int>(handle.Get())};
36 #endif
37 }
38
39 2 void CreateDedicatedBuffer(VmaAllocator allocator, const VkBufferCreateInfo& info,
40 void* allocateNext, VkBuffer& buffer, VmaAllocation& allocation,
41 VmaAllocationInfo& allocated, const std::string& what) {
42 2 VkExternalMemoryBufferCreateInfo external{
43 .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
44 2 .pNext = info.pNext,
45 2 .handleTypes = static_cast<VkExternalMemoryHandleTypeFlags>(kExternalMemoryType)};
46 2 VkBufferCreateInfo shared = info;
47 2 shared.pNext = &external;
48 2 const VmaAllocationCreateInfo create{.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE};
49 2 CheckVk(vmaCreateDedicatedBuffer(allocator, &shared, &create, allocateNext, &buffer,
50 &allocation, &allocated),
51 what);
52 2 }
53
54 } // namespace
55
56 1 ExternalHandle::~ExternalHandle() {
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 if (value_ != kNone) {
58 #if defined(_WIN32)
59 CloseHandle(reinterpret_cast<HANDLE>(value_));
60 #else
61 ✗ close(static_cast<int>(value_));
62 #endif
63 }
64 1 }
65
66 ✗ ExternalHandle& ExternalHandle::operator=(ExternalHandle&& other) noexcept {
67
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (this != &other) {
68 ✗ ExternalHandle old(std::exchange(value_, other.Release()));
69 ✗ }
70 ✗ return *this;
71 }
72
73 82 bool SharesMemory(VkPhysicalDevice physical) {
74 82 const VkPhysicalDeviceExternalBufferInfo buffer{
75 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO,
76 .usage = kBufferUsage,
77 .handleType = kExternalMemoryType};
78 82 VkExternalBufferProperties properties{.sType = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES};
79 82 vkGetPhysicalDeviceExternalBufferProperties(physical, &buffer, &properties);
80 82 const VkExternalMemoryFeatureFlags both =
81 VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
82 82 return (properties.externalMemoryProperties.externalMemoryFeatures & both) == both;
83 }
84
85 ✗ bool SharesTimelines(VkPhysicalDevice physical) {
86 ✗ const VkSemaphoreTypeCreateInfo timeline{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
87 .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE};
88 ✗ const VkPhysicalDeviceExternalSemaphoreInfo semaphore{
89 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
90 .pNext = &timeline,
91 ✗ .handleType = kExternalSemaphoreType};
92 ✗ VkExternalSemaphoreProperties properties{.sType =
93 VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES};
94 ✗ vkGetPhysicalDeviceExternalSemaphoreProperties(physical, &semaphore, &properties);
95 ✗ const VkExternalSemaphoreFeatureFlags both =
96 VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
97 ✗ return (properties.externalSemaphoreFeatures & both) == both;
98 }
99
100 1 void CreateExportableBuffer(VmaAllocator allocator, const VkBufferCreateInfo& info,
101 VkBuffer& buffer, VmaAllocation& allocation,
102 VmaAllocationInfo& allocated) {
103 1 VkExportMemoryAllocateInfo exported{
104 .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
105 .handleTypes = static_cast<VkExternalMemoryHandleTypeFlags>(kExternalMemoryType)};
106 2 CreateDedicatedBuffer(allocator, info, &exported, buffer, allocation, allocated,
107
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
3 "Allocating " + std::to_string(info.size) + " shareable bytes");
108 1 }
109
110 1 void CreateImportedBuffer(VmaAllocator allocator, const VkBufferCreateInfo& info,
111 ExternalHandle& handle, VkBuffer& buffer, VmaAllocation& allocation,
112 VmaAllocationInfo& allocated) {
113 1 MemoryImport imported = ImportInfo(handle);
114 2 CreateDedicatedBuffer(allocator, info, &imported, buffer, allocation, allocated,
115
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
3 "Importing " + std::to_string(info.size) + " bytes");
116 1 handle.Imported();
117 1 }
118
119 ✗ void CreateImportedBuffer(VkDevice device, VkPhysicalDevice physical,
120 const VkBufferCreateInfo& info, ExternalHandle& handle, VkBuffer& buffer,
121 VkDeviceMemory& memory) {
122
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ const std::string what = "Importing " + std::to_string(info.size) + " bytes";
123 ✗ const VkExternalMemoryBufferCreateInfo external{
124 .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
125 .handleTypes = static_cast<VkExternalMemoryHandleTypeFlags>(kExternalMemoryType)};
126 ✗ VkBufferCreateInfo shared = info;
127 ✗ shared.pNext = &external;
128
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ CheckVk(vkCreateBuffer(device, &shared, nullptr, &buffer), what);
129
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ const OnFailure freeBuffer([&] { vkDestroyBuffer(device, buffer, nullptr); });
130 ✗ VkMemoryRequirements requirements{};
131
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ vkGetBufferMemoryRequirements(device, buffer, &requirements);
132 ✗ VkPhysicalDeviceMemoryProperties types{};
133
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ vkGetPhysicalDeviceMemoryProperties(physical, &types);
134 std::uint32_t type = 0;
135
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ while (type < types.memoryTypeCount &&
136
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ ((requirements.memoryTypeBits & (1u << type)) == 0 ||
137
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ (types.memoryTypes[type].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0)) {
138 ✗ ++type;
139 }
140
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (type == types.memoryTypeCount) {
141
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw DeviceError(DeviceErrorKind::Unsupported, what + ": no device-local memory fits");
142 }
143 ✗ const MemoryImport imported = ImportInfo(handle);
144 ✗ const VkMemoryAllocateFlagsInfo flags{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
145 .pNext = &imported,
146 ✗ .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT};
147 ✗ const VkMemoryAllocateInfo allocate{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
148 .pNext = &flags,
149 ✗ .allocationSize = info.size,
150 ✗ .memoryTypeIndex = type};
151
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ CheckVk(vkAllocateMemory(device, &allocate, nullptr, &memory), what);
152
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ handle.Imported();
153
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ const OnFailure freeMemory([&] { vkFreeMemory(device, memory, nullptr); });
154
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ CheckVk(vkBindBufferMemory(device, buffer, memory, 0), what);
155 ✗ }
156
157 1 ExternalHandle ExportMemory(VkDevice device, VkDeviceMemory memory, std::string_view name) {
158
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 const std::string what = "Exporting " + std::string(name);
159 #if defined(_WIN32)
160 const VkMemoryGetWin32HandleInfoKHR info{.sType =
161 VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR,
162 .memory = memory,
163 .handleType = kExternalMemoryType};
164 HANDLE handle = nullptr;
165 CheckVk(vkGetMemoryWin32HandleKHR(device, &info, &handle), what);
166 return ExternalHandle(reinterpret_cast<std::intptr_t>(handle));
167 #else
168 1 const VkMemoryGetFdInfoKHR info{.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
169 .memory = memory,
170 1 .handleType = kExternalMemoryType};
171 1 int fd = -1;
172
2/4
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
1 CheckVk(vkGetMemoryFdKHR(device, &info, &fd), what);
173 1 return ExternalHandle(fd);
174 #endif
175 1 }
176
177 ✗ ExternalHandle ExportSemaphore(VkDevice device, VkSemaphore semaphore, std::string_view name) {
178
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ const std::string what = "Exporting " + std::string(name);
179 #if defined(_WIN32)
180 const VkSemaphoreGetWin32HandleInfoKHR info{
181 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR,
182 .semaphore = semaphore,
183 .handleType = kExternalSemaphoreType};
184 HANDLE handle = nullptr;
185 CheckVk(vkGetSemaphoreWin32HandleKHR(device, &info, &handle), what);
186 return ExternalHandle(reinterpret_cast<std::intptr_t>(handle));
187 #else
188 ✗ const VkSemaphoreGetFdInfoKHR info{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
189 .semaphore = semaphore,
190 ✗ .handleType = kExternalSemaphoreType};
191 ✗ int fd = -1;
192
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ CheckVk(vkGetSemaphoreFdKHR(device, &info, &fd), what);
193 ✗ return ExternalHandle(fd);
194 #endif
195 ✗ }
196
197 ✗ void ImportSemaphore(VkDevice device, VkSemaphore semaphore, ExternalHandle& handle,
198 std::string_view name) {
199
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ const std::string what = "Importing " + std::string(name);
200 #if defined(_WIN32)
201 const VkImportSemaphoreWin32HandleInfoKHR info{
202 .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR,
203 .semaphore = semaphore,
204 .handleType = kExternalSemaphoreType,
205 .handle = reinterpret_cast<HANDLE>(handle.Get())};
206 CheckVk(vkImportSemaphoreWin32HandleKHR(device, &info), what);
207 #else
208 ✗ const VkImportSemaphoreFdInfoKHR info{.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
209 .semaphore = semaphore,
210 .handleType = kExternalSemaphoreType,
211
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ .fd = static_cast<int>(handle.Get())};
212
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ CheckVk(vkImportSemaphoreFdKHR(device, &info), what);
213 ✗ handle.Release();
214 #endif
215 ✗ }
216
217 } // namespace ernie
218