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.h"
18
19#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
20#include "VkSemaphoreExternalLinux.hpp"
21#elif SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
22#include "VkSemaphoreExternalFuchsia.hpp"
23#else
24#include "VkSemaphoreExternalNone.hpp"
25#endif
26
27#include "marl/blockingcall.h"
28#include "marl/conditionvariable.h"
29
30#include <functional>
31#include <memory>
32#include <mutex>
33#include <utility>
34
35namespace vk
36{
37
38// An implementation of VkSemaphore based on Marl primitives.
39class Semaphore::Impl
40{
41public:
42 // Create a new instance. The external instance will be allocated only
43 // the pCreateInfo->pNext chain indicates it needs to be exported.
44 Impl(const VkSemaphoreCreateInfo* pCreateInfo) {
45 bool exportSemaphore = false;
46 for (const auto* nextInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
47 nextInfo != nullptr; nextInfo = nextInfo->pNext)
48 {
49 if (nextInfo->sType == VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO)
50 {
51 const auto* exportInfo = reinterpret_cast<const VkExportSemaphoreCreateInfo *>(nextInfo);
52 if (exportInfo->handleTypes != External::kExternalSemaphoreHandleType)
53 {
54 UNIMPLEMENTED("exportInfo->handleTypes");
55 }
56 exportSemaphore = true;
57 break;
58 }
59 }
60
61 if (exportSemaphore)
62 {
63 allocateExternalNoInit();
64 external->init();
65 }
66 }
67
68 ~Impl() {
69 deallocateExternal();
70 }
71
72 // Deallocate the External semaphore if any.
73 void deallocateExternal()
74 {
75 if (external)
76 {
77 external->~External();
78 external = nullptr;
79 }
80 }
81
82 // Allocate the external semaphore.
83 // Note that this does not allocate the internal resource, which must be
84 // performed by calling external->init(), or importing one using
85 // a platform-specific external->importXXX(...) method.
86 void allocateExternalNoInit()
87 {
88 external = new (externalStorage) External();
89 }
90
91 void wait()
92 {
93 if (external)
94 {
95 if (!external->tryWait())
96 {
97 // Dispatch the external wait to a background thread.
98 // Even if this creates a new thread on each
99 // call, it is assumed that this is negligible
100 // compared with the actual semaphore wait()
101 // operation.
102 marl::blocking_call([this](){
103 external->wait();
104 });
105 }
106
107 // If the import was temporary, reset the semaphore to its
108 // permanent state by getting rid of |external|.
109 // See "6.4.5. Importing Semaphore Payloads" in Vulkan 1.1 spec.
110 if (temporaryImport)
111 {
112 deallocateExternal();
113 temporaryImport = false;
114 }
115 }
116 else
117 {
118 waitInternal();
119 }
120 }
121
122 void signal()
123 {
124 if (external)
125 {
126 // Assumes that signalling an external semaphore is non-blocking,
127 // so it can be performed directly either from a fiber or thread.
128 external->signal();
129 }
130 else
131 {
132 signalInternal();
133 }
134 }
135
136private:
137 // Necessary to make ::importXXX() and ::exportXXX() simpler.
138 friend Semaphore;
139
140 void waitInternal()
141 {
142 // Wait on the marl condition variable only.
143 std::unique_lock<std::mutex> lock(mutex);
144 condition.wait(lock, [this]{ return this->signaled; });
145 signaled = false; // Vulkan requires resetting after waiting.
146 }
147
148 void signalInternal()
149 {
150 // Signal the marl condition variable only.
151 std::unique_lock<std::mutex> lock(mutex);
152 if (!signaled)
153 {
154 signaled = true;
155 condition.notify_one();
156 }
157 }
158
159 // Implementation of a non-external semaphore based on Marl.
160 std::mutex mutex;
161 marl::ConditionVariable condition;
162 bool signaled = false;
163
164 // Optional external semaphore data might be referenced and stored here.
165 External* external = nullptr;
166
167 // Set to true if |external| comes from a temporary import.
168 bool temporaryImport = false;
169
170 alignas(External) char externalStorage[sizeof(External)];
171};
172
173Semaphore::Semaphore(const VkSemaphoreCreateInfo* pCreateInfo, void* mem)
174{
175 impl = new (mem) Impl(pCreateInfo);
176}
177
178void Semaphore::destroy(const VkAllocationCallbacks* pAllocator)
179{
180 impl->~Impl();
181 vk::deallocate(impl, pAllocator);
182}
183
184size_t Semaphore::ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo* pCreateInfo)
185{
186 return sizeof(Semaphore::Impl);
187}
188
189void Semaphore::wait()
190{
191 impl->wait();
192}
193
194void Semaphore::signal()
195{
196 impl->signal();
197}
198
199#if SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
200VkResult Semaphore::importFd(int fd, bool temporaryImport)
201{
202 std::unique_lock<std::mutex> lock(impl->mutex);
203 if (!impl->external)
204 {
205 impl->allocateExternalNoInit();
206 }
207 VkResult result = impl->external->importFd(fd);
208 if (result != VK_SUCCESS)
209 {
210 impl->deallocateExternal();
211 }
212 else
213 {
214 impl->temporaryImport = temporaryImport;
215 }
216 return result;
217}
218
219VkResult Semaphore::exportFd(int* pFd) const
220{
221 std::unique_lock<std::mutex> lock(impl->mutex);
222 if (!impl->external)
223 {
224 TRACE("Cannot export non-external semaphore");
225 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
226 }
227 return impl->external->exportFd(pFd);
228}
229#endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_LINUX_MEMFD
230
231#if SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
232VkResult Semaphore::importHandle(zx_handle_t handle, bool temporaryImport)
233{
234 std::unique_lock<std::mutex> lock(impl->mutex);
235 if (!impl->external)
236 {
237 impl->allocateExternalNoInit();
238 }
239 // NOTE: Imports are just moving a handle so cannot fail.
240 impl->external->importHandle(handle);
241 impl->temporaryImport = temporaryImport;
242 return VK_SUCCESS;
243}
244
245VkResult Semaphore::exportHandle(zx_handle_t *pHandle) const
246{
247 std::unique_lock<std::mutex> lock(impl->mutex);
248 if (!impl->external)
249 {
250 TRACE("Cannot export non-external semaphore");
251 return VK_ERROR_INVALID_EXTERNAL_HANDLE;
252 }
253 return impl->external->exportHandle(pHandle);
254}
255#endif // SWIFTSHADER_EXTERNAL_SEMAPHORE_ZIRCON_EVENT
256
257} // namespace vk
258