1 // Copyright 2018 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 #ifndef VK_SEMAPHORE_HPP_
16 #define VK_SEMAPHORE_HPP_
17 
18 #include "VkConfig.hpp"
19 #include "VkObject.hpp"
20 
21 #include "marl/event.h"
22 #include "marl/mutex.h"
23 #include "marl/tsa.h"
24 
25 #include "System/Synchronization.hpp"
26 
27 #if VK_USE_PLATFORM_FUCHSIA
28 #	include <zircon/types.h>
29 #endif
30 
31 namespace vk {
32 
33 class BinarySemaphore;
34 class TimelineSemaphore;
35 
36 class Semaphore
37 {
38 public:
39 	Semaphore(VkSemaphoreType type);
40 
41 	virtual ~Semaphore() = default;
42 
Cast(VkSemaphore semaphore)43 	static inline Semaphore *Cast(VkSemaphore semaphore)
44 	{
45 		return static_cast<Semaphore *>(static_cast<void *>(semaphore));
46 	}
47 
destroy(const VkAllocationCallbacks * pAllocator)48 	virtual void destroy(const VkAllocationCallbacks *pAllocator)
49 	{
50 	}
51 
52 	VkSemaphoreType getSemaphoreType() const;
53 	//static size_t ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo);
54 
55 protected:
56 	VkSemaphoreType type;
57 	marl::mutex mutex;
58 };
59 
60 class BinarySemaphore : public Semaphore, public Object<BinarySemaphore, VkSemaphore>
61 {
62 public:
63 	BinarySemaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const VkAllocationCallbacks *pAllocator);
64 	void destroy(const VkAllocationCallbacks *pAllocator);
65 
66 	static size_t ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo);
67 
68 	void wait();
69 
wait(const VkPipelineStageFlags & flag)70 	void wait(const VkPipelineStageFlags &flag)
71 	{
72 		// NOTE: not sure what else to do here?
73 		wait();
74 	}
75 
76 	void signal();
77 
78 #if SWIFTSHADER_EXTERNAL_SEMAPHORE_OPAQUE_FD
79 	VkResult importFd(int fd, bool temporaryImport);
80 	VkResult exportFd(int *pFd);
81 #endif
82 
83 #if VK_USE_PLATFORM_FUCHSIA
84 	VkResult importHandle(zx_handle_t handle, bool temporaryImport);
85 	VkResult exportHandle(zx_handle_t *pHandle);
86 #endif
87 
88 	class External;
89 
90 private:
91 	// Small technical note on how semaphores are imported/exported with Vulkan:
92 	//
93 	// - A Vulkan Semaphore objects has a "payload", corresponding to a
94 	//   simple atomic boolean flag.
95 	//
96 	// - A Vulkan Semaphore object can be "exported": this creates a
97 	//   platform-specific handle / descriptor (which can be passed to other
98 	//   processes), and is linked in some way to the original semaphore's
99 	//   payload.
100 	//
101 	// - Similarly, said handle / descriptor can be "imported" into a Vulkan
102 	//   Semaphore object. By default, that semaphore loses its payload, and
103 	//   instead uses the one referenced / shared through the descriptor.
104 	//
105 	//   Hence if semaphore A exports its payload through a descriptor that
106 	//   is later imported into semaphore B, then both A and B will use/share
107 	//   the same payload (i.e. signal flag), making cross-process
108 	//   synchronization possible.
109 	//
110 	// - There are also "temporary imports", where the target semaphore's
111 	//   payload is not lost, but is simply hidden/stashed. But the next wait()
112 	//   operation on the same semaphore should remove the temporary import,
113 	//   and restore the previous payload.
114 	//
115 	// - There are many handle / descriptor types, which are listed through
116 	//   the VkExternalSemaphoreHandleTypeFlagBits. A given Vulkan
117 	//   implementation might support onle one or several at the same time
118 	//   (e.g. on Linux or Android, it could support both OPAQUE_FD_BIT and
119 	//   SYNC_FD_BIT, while on Windows, it would be OPAQUE_WIN32_BIT +
120 	//   OPAQUE_WIN32_KMT_BIT + D3D12_FENCE_BIT).
121 	//
122 	// - To be able to export a semaphore, VkCreateSemaphore() must be called
123 	//   with a VkSemaphoreCreateInfo that lists the types of all possible
124 	//   platform-specific handles the semaphore could be exported to
125 	//   (e.g. on Linux, it is possible to specify that a semaphore might be
126 	//   exported as an opaque FD, or as a Linux Sync FD).
127 	//
128 	//   However, which exact type is however only determined later by the
129 	//   export operation itself (e.g. vkGetSemaphoreFdKHR() could be called to export
130 	//   either a VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT or a
131 	//   VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT).
132 	//
133 	//   Once a semaphore has been exported as one type, it is not possible
134 	//   to export the same payload with a different type (though the spec
135 	//   doesn't seem to be explicit about this, it's simply impossible in
136 	//   general).
137 	//
138 	// This leads to the following design:
139 	//
140 	//   - |internal| is a simple marl::Event that represents the semaphore's
141 	//     payload when it is not exported, or imported non-temporarily.
142 	//
143 	//   - |external| points to an external semaphore payload. It is created
144 	//     on demand if the semaphore is exported or imported non-temporarily.
145 	//     Note that once |external| is created, |internal| is ignored.
146 	//
147 	//   - |tempExternal| points to a linked-list of temporary external
148 	//     semaphore payloads. The list head corresponds to the most recent
149 	//     temporary import.
150 	//
151 
152 	// Internal template to allocate a new External implementation.
153 	template<class EXTERNAL>
154 	External *allocateExternal();
155 
156 	void deallocateExternal(External *ext);
157 
158 	// Used internally to import an external payload.
159 	// |temporaryImport| is true iff the import is temporary.
160 	// |alloc_func| is callable that allocates a new External instance of the
161 	// appropriate type.
162 	// |import_func| is callable that takes a single parameter, which
163 	// corresponds to the external handle/descriptor, and returns a VkResult
164 	// values.
165 	template<typename ALLOC_FUNC, typename IMPORT_FUNC>
166 	VkResult importPayload(bool temporaryImport,
167 	                       ALLOC_FUNC alloc_func,
168 	                       IMPORT_FUNC import_func);
169 
170 	// Used internally to export a given payload.
171 	// |alloc_func| is a callable that allocates a new External instance of
172 	// the appropriate type.
173 	// |export_func| is a callable that takes a pointer to an External instance,
174 	// and a pointer to a handle/descriptor, and returns a VkResult.
175 	template<typename ALLOC_FUNC, typename EXPORT_FUNC>
176 	VkResult exportPayload(ALLOC_FUNC alloc_func, EXPORT_FUNC export_func);
177 
178 	const VkAllocationCallbacks *allocator = nullptr;
179 	VkExternalSemaphoreHandleTypeFlags exportableHandleTypes = (VkExternalSemaphoreHandleTypeFlags)0;
180 	marl::Event internal;
181 	External *external GUARDED_BY(mutex) = nullptr;
182 	External *tempExternal GUARDED_BY(mutex) = nullptr;
183 };
184 
Cast(VkSemaphore object)185 static inline Semaphore *Cast(VkSemaphore object)
186 {
187 	return Semaphore::Cast(object);
188 }
189 
190 template<typename T>
DynamicCast(VkSemaphore object)191 static inline T *DynamicCast(VkSemaphore object)
192 {
193 	Semaphore *semaphore = vk::Cast(object);
194 	if(semaphore == nullptr)
195 	{
196 		return nullptr;
197 	}
198 
199 	static_assert(std::is_same_v<T, BinarySemaphore> || std::is_same_v<T, TimelineSemaphore>);
200 	if constexpr(std::is_same_v<T, BinarySemaphore>)
201 	{
202 		if(semaphore->getSemaphoreType() != VK_SEMAPHORE_TYPE_BINARY)
203 		{
204 			return nullptr;
205 		}
206 	}
207 	else
208 	{
209 		if(semaphore->getSemaphoreType() != VK_SEMAPHORE_TYPE_TIMELINE)
210 		{
211 			return nullptr;
212 		}
213 	}
214 	return static_cast<T *>(semaphore);
215 }
216 
217 // This struct helps parse VkSemaphoreCreateInfo. It also looks at the pNext
218 // structures and stores their data flatly in a single struct. The default
219 // values of each data member are what the absence of a pNext struct implies
220 // for those values.
221 struct SemaphoreCreateInfo
222 {
223 	bool exportSemaphore = false;
224 	VkExternalSemaphoreHandleTypeFlags exportHandleTypes = 0;
225 
226 	VkSemaphoreType semaphoreType = VK_SEMAPHORE_TYPE_BINARY;
227 	uint64_t initialPayload = 0;
228 
229 	SemaphoreCreateInfo(const VkSemaphoreCreateInfo *pCreateInfo);
230 };
231 
232 }  // namespace vk
233 
234 #endif  // VK_SEMAPHORE_HPP_
235