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