1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "VkSemaphore.hpp"
16
17 #include "VkConfig.hpp"
18 #include "VkStringify.hpp"
19 #include "VkTimelineSemaphore.hpp"
20
21 #include "marl/blockingcall.h"
22 #include "marl/conditionvariable.h"
23
24 #include <functional>
25 #include <memory>
26 #include <utility>
27
28 namespace vk {
29
30 // This is a base abstract class for all external semaphore implementations
31 // used in this source file.
32 class BinarySemaphore::External
33 {
34 public:
35 virtual ~External() = default;
36
37 // Initialize new instance with a given initial state.
38 virtual VkResult init(bool initialState) = 0;
39
40 virtual bool tryWait() = 0;
41 virtual void wait() = 0;
42 virtual void signal() = 0;
43
44 // For VK_KHR_external_semaphore_fd
importOpaqueFd(int fd)45 virtual VkResult importOpaqueFd(int fd)
46 {
47 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
48 }
49
exportOpaqueFd(int * pFd)50 virtual VkResult exportOpaqueFd(int *pFd)
51 {
52 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
53 }
54
55 #if VK_USE_PLATFORM_FUCHSIA
56 // For VK_FUCHSIA_external_semaphore
importHandle(zx_handle_t handle)57 virtual VkResult importHandle(zx_handle_t handle)
58 {
59 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
60 }
exportHandle(zx_handle_t * pHandle)61 virtual VkResult exportHandle(zx_handle_t *pHandle)
62 {
63 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
64 }
65 #endif
66 // Pointer to previous temporary external instanc,e used for |tempExternal| only.
67 External *previous = nullptr;
68 };
69
70 } // namespace vk
71
72 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
73 # if defined(__linux__) || defined(__ANDROID__)
74 # include "VkSemaphoreExternalLinux.hpp"
75 # else
76 # error "Missing VK_KHR_external_semaphore_fd implementation for this platform!"
77 # endif
78 #elif VK_USE_PLATFORM_FUCHSIA
79 # include "VkSemaphoreExternalFuchsia.hpp"
80 #endif
81
82 namespace vk {
83
84 // The bitmask of all external semaphore handle types supported by this source file.
85 static const VkExternalSemaphoreHandleTypeFlags kSupportedTypes =
86 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
87 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT |
88 #endif
89 #if VK_USE_PLATFORM_FUCHSIA
90 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA |
91 #endif
92 0;
93
94 // Create a new instance. The external instance will be allocated only
95 // the pCreateInfo->pNext chain indicates it needs to be exported.
SemaphoreCreateInfo(const VkSemaphoreCreateInfo * pCreateInfo)96 SemaphoreCreateInfo::SemaphoreCreateInfo(const VkSemaphoreCreateInfo *pCreateInfo)
97 {
98 for(const auto *nextInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
99 nextInfo != nullptr; nextInfo = nextInfo->pNext)
100 {
101 switch(nextInfo->sType)
102 {
103 case VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO:
104 {
105 const auto *exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo);
106 exportSemaphore = true;
107 exportHandleTypes = exportInfo->handleTypes;
108 if((exportHandleTypes & ~kSupportedTypes) != 0)
109 {
110 UNSUPPORTED("exportInfo->handleTypes 0x%X (supports 0x%X)",
111 int(exportHandleTypes),
112 int(kSupportedTypes));
113 }
114 }
115 break;
116 case VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO:
117 {
118 const auto *tlsInfo = reinterpret_cast<const VkSemaphoreTypeCreateInfo *>(nextInfo);
119 semaphoreType = tlsInfo->semaphoreType;
120 initialPayload = tlsInfo->initialValue;
121 }
122 break;
123 default:
124 WARN("nextInfo->sType = %s", vk::Stringify(nextInfo->sType).c_str());
125 break;
126 }
127 }
128 }
129
Semaphore(VkSemaphoreType type)130 Semaphore::Semaphore(VkSemaphoreType type)
131 : type(type)
132 {
133 }
134
getSemaphoreType() const135 VkSemaphoreType Semaphore::getSemaphoreType() const
136 {
137 return type;
138 }
139
wait()140 void BinarySemaphore::wait()
141 {
142 marl::lock lock(mutex);
143 External *ext = tempExternal ? tempExternal : external;
144 if(ext)
145 {
146 if(!ext->tryWait())
147 {
148 // Dispatch the ext wait to a background thread.
149 // Even if this creates a new thread on each
150 // call, it is assumed that this is negligible
151 // compared with the actual semaphore wait()
152 // operation.
153 lock.unlock_no_tsa();
154 marl::blocking_call([ext]() {
155 ext->wait();
156 });
157 lock.lock_no_tsa();
158 }
159
160 // If the import was temporary, reset the semaphore to its previous state.
161 // See "6.4.5. Importing BinarySemaphore Payloads" in Vulkan 1.1 spec.
162 if(ext == tempExternal)
163 {
164 tempExternal = ext->previous;
165 deallocateExternal(ext);
166 }
167 }
168 else
169 {
170 internal.wait();
171 }
172 }
173
signal()174 void BinarySemaphore::signal()
175 {
176 ASSERT(type == VK_SEMAPHORE_TYPE_BINARY);
177 marl::lock lock(mutex);
178 External *ext = tempExternal ? tempExternal : external;
179 if(ext)
180 {
181 // Assumes that signalling an external semaphore is non-blocking,
182 // so it can be performed directly either from a fiber or thread.
183 ext->signal();
184 }
185 else
186 {
187 internal.signal();
188 }
189 }
190
BinarySemaphore(const VkSemaphoreCreateInfo * pCreateInfo,void * mem,const VkAllocationCallbacks * pAllocator)191 BinarySemaphore::BinarySemaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const VkAllocationCallbacks *pAllocator)
192 : Semaphore(VK_SEMAPHORE_TYPE_BINARY)
193 , allocator(pAllocator)
194 {
195 SemaphoreCreateInfo info(pCreateInfo);
196 exportableHandleTypes = info.exportHandleTypes;
197 ASSERT(info.semaphoreType == VK_SEMAPHORE_TYPE_BINARY);
198 type = info.semaphoreType;
199 }
200
destroy(const VkAllocationCallbacks * pAllocator)201 void BinarySemaphore::destroy(const VkAllocationCallbacks *pAllocator)
202 {
203 marl::lock lock(mutex);
204 while(tempExternal)
205 {
206 External *ext = tempExternal;
207 tempExternal = ext->previous;
208 deallocateExternal(ext);
209 }
210 if(external)
211 {
212 deallocateExternal(external);
213 external = nullptr;
214 }
215 }
216
ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo * pCreateInfo)217 size_t BinarySemaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo)
218 {
219 // Semaphore::External instance is created and destroyed on demand so return 0 here.
220 return 0;
221 }
222
223 template<class EXTERNAL>
allocateExternal()224 BinarySemaphore::External *BinarySemaphore::allocateExternal()
225 {
226 auto *ext = reinterpret_cast<BinarySemaphore::External *>(
227 vk::allocate(sizeof(EXTERNAL), alignof(EXTERNAL), allocator));
228 new(ext) EXTERNAL();
229 return ext;
230 }
231
deallocateExternal(BinarySemaphore::External * ext)232 void BinarySemaphore::deallocateExternal(BinarySemaphore::External *ext)
233 {
234 ext->~External();
235 vk::deallocate(ext, allocator);
236 }
237
238 template<typename ALLOC_FUNC, typename IMPORT_FUNC>
importPayload(bool temporaryImport,ALLOC_FUNC alloc_func,IMPORT_FUNC import_func)239 VkResult BinarySemaphore::importPayload(bool temporaryImport,
240 ALLOC_FUNC alloc_func,
241 IMPORT_FUNC import_func)
242 {
243 marl::lock lock(mutex);
244
245 // Create new External instance if needed.
246 External *ext = external;
247 if(temporaryImport || !ext)
248 {
249 ext = alloc_func();
250 }
251 VkResult result = import_func(ext);
252 if(result != VK_SUCCESS)
253 {
254 if(temporaryImport || !external)
255 {
256 deallocateExternal(ext);
257 }
258 return result;
259 }
260
261 if(temporaryImport)
262 {
263 ext->previous = tempExternal;
264 tempExternal = ext;
265 }
266 else if(!external)
267 {
268 external = ext;
269 }
270 return VK_SUCCESS;
271 }
272
273 template<typename ALLOC_FUNC, typename EXPORT_FUNC>
exportPayload(ALLOC_FUNC alloc_func,EXPORT_FUNC export_func)274 VkResult BinarySemaphore::exportPayload(ALLOC_FUNC alloc_func, EXPORT_FUNC export_func)
275 {
276 marl::lock lock(mutex);
277 // Sanity check, do not try to export a semaphore that has a temporary import.
278 if(tempExternal != nullptr)
279 {
280 TRACE("Cannot export semaphore with a temporary import!");
281 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
282 }
283 // Allocate |external| if it doesn't exist yet.
284 if(!external)
285 {
286 External *ext = alloc_func();
287 VkResult result = ext->init(internal.isSignalled());
288 if(result != VK_SUCCESS)
289 {
290 deallocateExternal(ext);
291 return result;
292 }
293 external = ext;
294 }
295 return export_func(external);
296 }
297
298 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
importFd(int fd,bool temporaryImport)299 VkResult BinarySemaphore::importFd(int fd, bool temporaryImport)
300 {
301 return importPayload(
302 temporaryImport,
303 [this]() {
304 return allocateExternal<OpaqueFdExternalSemaphore>();
305 },
306 [fd](External *ext) {
307 return ext->importOpaqueFd(fd);
308 });
309 }
310
exportFd(int * pFd)311 VkResult BinarySemaphore::exportFd(int *pFd)
312 {
313 if((exportableHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) == 0)
314 {
315 TRACE("Cannot export semaphore as opaque FD (exportableHandleType = 0x%X, want 0x%X)",
316 exportableHandleTypes,
317 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);
318
319 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
320 }
321
322 return exportPayload([this]() { return allocateExternal<OpaqueFdExternalSemaphore>(); },
323 [pFd](External *ext) {
324 return ext->exportOpaqueFd(pFd);
325 });
326 }
327 #endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
328
329 #if VK_USE_PLATFORM_FUCHSIA
importHandle(zx_handle_t handle,bool temporaryImport)330 VkResult BinarySemaphore::importHandle(zx_handle_t handle, bool temporaryImport)
331 {
332 return importPayload(
333 temporaryImport,
334 [this]() {
335 return allocateExternal<ZirconEventExternalSemaphore>();
336 },
337 [handle](External *ext) {
338 return ext->importHandle(handle);
339 });
340 }
341
exportHandle(zx_handle_t * pHandle)342 VkResult BinarySemaphore::exportHandle(zx_handle_t *pHandle)
343 {
344 if((exportableHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA) == 0)
345 {
346 TRACE("Cannot export semaphore as Zircon handle (exportableHandleType = 0x%X, want 0x%X)",
347 exportableHandleTypes,
348 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA);
349
350 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
351 }
352
353 return exportPayload([this]() { return allocateExternal<ZirconEventExternalSemaphore>(); },
354 [pHandle](External *ext) {
355 return ext->exportHandle(pHandle);
356 });
357 }
358 #endif // VK_USE_PLATFORM_FUCHSIA
359
360 } // namespace vk
361