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