1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // SyncVk.cpp:
7 // Implements the class methods for SyncVk.
8 //
9
10 #include "libANGLE/renderer/vulkan/SyncVk.h"
11
12 #include "common/debug.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/Display.h"
15 #include "libANGLE/renderer/vulkan/ContextVk.h"
16 #include "libANGLE/renderer/vulkan/DisplayVk.h"
17
18 #if !defined(ANGLE_PLATFORM_WINDOWS)
19 # include <poll.h>
20 # include <unistd.h>
21 #else
22 # include <io.h>
23 #endif
24
25 namespace
26 {
27 // Wait for file descriptor to be signaled
SyncWaitFd(int fd,uint64_t timeoutNs)28 VkResult SyncWaitFd(int fd, uint64_t timeoutNs)
29 {
30 #if !defined(ANGLE_PLATFORM_WINDOWS)
31 struct pollfd fds;
32 int ret;
33
34 // Convert nanoseconds to milliseconds
35 int timeoutMs = static_cast<int>(timeoutNs / 1000000);
36 // If timeoutNs was non-zero but less than one millisecond, make it a millisecond.
37 if (timeoutNs > 0 && timeoutNs < 1000000)
38 {
39 timeoutMs = 1;
40 }
41
42 ASSERT(fd >= 0);
43
44 fds.fd = fd;
45 fds.events = POLLIN;
46
47 do
48 {
49 ret = poll(&fds, 1, timeoutMs);
50 if (ret > 0)
51 {
52 if (fds.revents & (POLLERR | POLLNVAL))
53 {
54 return VK_ERROR_UNKNOWN;
55 }
56 return VK_SUCCESS;
57 }
58 else if (ret == 0)
59 {
60 return VK_TIMEOUT;
61 }
62 } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
63
64 return VK_ERROR_UNKNOWN;
65 #else
66 UNREACHABLE();
67 return VK_ERROR_UNKNOWN;
68 #endif
69 }
70
71 } // anonymous namespace
72
73 namespace rx
74 {
75 namespace vk
76 {
SyncHelper()77 SyncHelper::SyncHelper() {}
78
~SyncHelper()79 SyncHelper::~SyncHelper() {}
80
releaseToRenderer(RendererVk * renderer)81 void SyncHelper::releaseToRenderer(RendererVk *renderer) {}
82
initialize(ContextVk * contextVk,bool isEglSyncObject)83 angle::Result SyncHelper::initialize(ContextVk *contextVk, bool isEglSyncObject)
84 {
85 ASSERT(!mUse.getSerial().valid());
86
87 // Submit the commands:
88 //
89 // - This breaks the current render pass to ensure the proper ordering of the sync object in the
90 // commands,
91 // - The sync object has a valid serial when it's waited on later,
92 // - After waiting on the sync object, every resource that's used so far (and is being synced)
93 // will also be aware that it's finished (based on the serial) and won't incur a further wait
94 // (for example when a buffer is mapped).
95 //
96 retain(&contextVk->getResourceUseList());
97 return contextVk->flushImpl(nullptr, RenderPassClosureReason::SyncObjectInit);
98 }
99
clientWait(Context * context,ContextVk * contextVk,bool flushCommands,uint64_t timeout,VkResult * outResult)100 angle::Result SyncHelper::clientWait(Context *context,
101 ContextVk *contextVk,
102 bool flushCommands,
103 uint64_t timeout,
104 VkResult *outResult)
105 {
106 RendererVk *renderer = context->getRenderer();
107
108 // If the event is already set, don't wait
109 bool alreadySignaled = false;
110 ANGLE_TRY(getStatus(context, &alreadySignaled));
111 if (alreadySignaled)
112 {
113 *outResult = VK_EVENT_SET;
114 return angle::Result::Continue;
115 }
116
117 // If timeout is zero, there's no need to wait, so return timeout already.
118 if (timeout == 0)
119 {
120 *outResult = VK_TIMEOUT;
121 return angle::Result::Continue;
122 }
123
124 // We always flush when a sync object is created, so they should always have a valid Serial
125 // when being waited on.
126 ASSERT(mUse.getSerial().valid() && !usedInRecordedCommands());
127
128 VkResult status = VK_SUCCESS;
129 ANGLE_TRY(renderer->waitForSerialWithUserTimeout(context, mUse.getSerial(), timeout, &status));
130
131 // Check for errors, but don't consider timeout as such.
132 if (status != VK_TIMEOUT)
133 {
134 ANGLE_VK_TRY(context, status);
135 }
136
137 *outResult = status;
138 return angle::Result::Continue;
139 }
140
serverWait(ContextVk * contextVk)141 angle::Result SyncHelper::serverWait(ContextVk *contextVk)
142 {
143 // Every resource already tracks its usage and issues the appropriate barriers, so there's
144 // really nothing to do here. An execution barrier is issued to strictly satisfy what the
145 // application asked for.
146 vk::OutsideRenderPassCommandBuffer *commandBuffer;
147 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
148 commandBuffer->pipelineBarrier(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
149 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 0,
150 nullptr);
151 return angle::Result::Continue;
152 }
153
getStatus(Context * context,bool * signaled) const154 angle::Result SyncHelper::getStatus(Context *context, bool *signaled) const
155 {
156 ASSERT(mUse.getSerial().valid() && !usedInRecordedCommands());
157
158 ANGLE_TRY(context->getRenderer()->checkCompletedCommands(context));
159 *signaled = !isCurrentlyInUse(context->getRenderer()->getLastCompletedQueueSerial());
160 return angle::Result::Continue;
161 }
162
SyncHelperNativeFence()163 SyncHelperNativeFence::SyncHelperNativeFence() : mNativeFenceFd(kInvalidFenceFd) {}
164
~SyncHelperNativeFence()165 SyncHelperNativeFence::~SyncHelperNativeFence()
166 {
167 if (mNativeFenceFd != kInvalidFenceFd)
168 {
169 close(mNativeFenceFd);
170 }
171 }
172
releaseToRenderer(RendererVk * renderer)173 void SyncHelperNativeFence::releaseToRenderer(RendererVk *renderer)
174 {
175 renderer->collectGarbageAndReinit(&mUse, &mFenceWithFd);
176 }
177
178 // Note: We have mFenceWithFd hold the FD, so that ownership is with ICD. Meanwhile we store a dup
179 // of FD in SyncHelperNativeFence for further reference, i.e. dup of FD. Any call to clientWait
180 // or serverWait will ensure the FD or dup of FD goes to application or ICD. At release, above
181 // it's Garbage collected/destroyed. Otherwise we can't time when to close(fd);
initializeWithFd(ContextVk * contextVk,int inFd)182 angle::Result SyncHelperNativeFence::initializeWithFd(ContextVk *contextVk, int inFd)
183 {
184 ASSERT(inFd >= kInvalidFenceFd);
185
186 // If valid FD provided by application - import it to fence.
187 if (inFd > kInvalidFenceFd)
188 {
189 // File descriptor ownership: EGL_ANDROID_native_fence_sync
190 // Whenever a file descriptor is passed into or returned from an
191 // EGL call in this extension, ownership of that file descriptor is
192 // transferred. The recipient of the file descriptor must close it when it is
193 // no longer needed, and the provider of the file descriptor must dup it
194 // before providing it if they require continued use of the native fence.
195 mNativeFenceFd = inFd;
196 return angle::Result::Continue;
197 }
198
199 RendererVk *renderer = contextVk->getRenderer();
200 VkDevice device = renderer->getDevice();
201
202 DeviceScoped<vk::Fence> fence(device);
203
204 VkExportFenceCreateInfo exportCreateInfo = {};
205 exportCreateInfo.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO;
206 exportCreateInfo.pNext = nullptr;
207 exportCreateInfo.handleTypes = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
208
209 // Create fenceInfo base.
210 VkFenceCreateInfo fenceCreateInfo = {};
211 fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
212 fenceCreateInfo.flags = 0;
213 fenceCreateInfo.pNext = &exportCreateInfo;
214
215 // Initialize/create a VkFence handle
216 ANGLE_VK_TRY(contextVk, fence.get().init(device, fenceCreateInfo));
217
218 // invalid FD provided by application - create one with fence.
219 /*
220 Spec: "When a fence sync object is created or when an EGL native fence sync
221 object is created with the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute set to
222 EGL_NO_NATIVE_FENCE_FD_ANDROID, eglCreateSyncKHR also inserts a fence command
223 into the command stream of the bound client API's current context and associates it
224 with the newly created sync object.
225 */
226 // Flush first because the fence comes after current pending set of commands.
227 ANGLE_TRY(contextVk->flushImpl(nullptr, RenderPassClosureReason::SyncObjectWithFdInit));
228
229 retain(&contextVk->getResourceUseList());
230
231 Serial serialOut;
232 // exportFd is exporting VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR type handle which
233 // obeys copy semantics. This means that the fence must already be signaled or the work to
234 // signal it is in the graphics pipeline at the time we export the fd. Thus we need to
235 // EnsureSubmitted here.
236 ANGLE_TRY(renderer->queueSubmitOneOff(contextVk, vk::PrimaryCommandBuffer(),
237 contextVk->hasProtectedContent(),
238 contextVk->getPriority(), nullptr, 0, &fence.get(),
239 vk::SubmitPolicy::EnsureSubmitted, &serialOut));
240
241 VkFenceGetFdInfoKHR fenceGetFdInfo = {};
242 fenceGetFdInfo.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
243 fenceGetFdInfo.fence = fence.get().getHandle();
244 fenceGetFdInfo.handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
245 ANGLE_VK_TRY(contextVk, fence.get().exportFd(device, fenceGetFdInfo, &mNativeFenceFd));
246
247 mFenceWithFd = fence.release();
248
249 return angle::Result::Continue;
250 }
251
clientWait(Context * context,ContextVk * contextVk,bool flushCommands,uint64_t timeout,VkResult * outResult)252 angle::Result SyncHelperNativeFence::clientWait(Context *context,
253 ContextVk *contextVk,
254 bool flushCommands,
255 uint64_t timeout,
256 VkResult *outResult)
257 {
258 RendererVk *renderer = context->getRenderer();
259
260 // If already signaled, don't wait
261 bool alreadySignaled = false;
262 ANGLE_TRY(getStatus(context, &alreadySignaled));
263 if (alreadySignaled)
264 {
265 *outResult = VK_SUCCESS;
266 return angle::Result::Continue;
267 }
268
269 // If timeout is zero, there's no need to wait, so return timeout already.
270 if (timeout == 0)
271 {
272 *outResult = VK_TIMEOUT;
273 return angle::Result::Continue;
274 }
275
276 if (flushCommands && contextVk)
277 {
278 ANGLE_TRY(contextVk->flushImpl(nullptr, RenderPassClosureReason::SyncObjectClientWait));
279 }
280
281 VkResult status = VK_SUCCESS;
282 if (mUse.getSerial().valid())
283 {
284 // We have a valid serial to wait on
285 ANGLE_TRY(
286 renderer->waitForSerialWithUserTimeout(context, mUse.getSerial(), timeout, &status));
287 }
288 else
289 {
290 // We need to wait on the file descriptor
291
292 status = SyncWaitFd(mNativeFenceFd, timeout);
293 if (status != VK_TIMEOUT)
294 {
295 ANGLE_VK_TRY(contextVk, status);
296 }
297 }
298
299 *outResult = status;
300 return angle::Result::Continue;
301 }
302
serverWait(ContextVk * contextVk)303 angle::Result SyncHelperNativeFence::serverWait(ContextVk *contextVk)
304 {
305 RendererVk *renderer = contextVk->getRenderer();
306 VkDevice device = renderer->getDevice();
307
308 DeviceScoped<Semaphore> waitSemaphore(device);
309 // Wait semaphore for next vkQueueSubmit().
310 // Create a Semaphore with imported fenceFd.
311 ANGLE_VK_TRY(contextVk, waitSemaphore.get().init(device));
312
313 VkImportSemaphoreFdInfoKHR importFdInfo = {};
314 importFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
315 importFdInfo.semaphore = waitSemaphore.get().getHandle();
316 importFdInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR;
317 importFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
318 importFdInfo.fd = dup(mNativeFenceFd);
319 ANGLE_VK_TRY(contextVk, waitSemaphore.get().importFd(device, importFdInfo));
320
321 // Add semaphore to next submit job.
322 contextVk->addWaitSemaphore(waitSemaphore.get().getHandle(),
323 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
324 contextVk->addGarbage(&waitSemaphore.get()); // This releases the handle.
325 return angle::Result::Continue;
326 }
327
getStatus(Context * context,bool * signaled) const328 angle::Result SyncHelperNativeFence::getStatus(Context *context, bool *signaled) const
329 {
330 // We've got a serial, check if the serial is still in use
331 if (mUse.getSerial().valid())
332 {
333 *signaled = !isCurrentlyInUse(context->getRenderer()->getLastCompletedQueueSerial());
334 return angle::Result::Continue;
335 }
336
337 // We don't have a serial, check status of the file descriptor
338 VkResult result = SyncWaitFd(mNativeFenceFd, 0);
339 if (result != VK_TIMEOUT)
340 {
341 ANGLE_VK_TRY(context, result);
342 }
343 *signaled = (result == VK_SUCCESS);
344 return angle::Result::Continue;
345 }
346
dupNativeFenceFD(Context * context,int * fdOut) const347 angle::Result SyncHelperNativeFence::dupNativeFenceFD(Context *context, int *fdOut) const
348 {
349 if (!mFenceWithFd.valid() || mNativeFenceFd == kInvalidFenceFd)
350 {
351 return angle::Result::Stop;
352 }
353
354 *fdOut = dup(mNativeFenceFd);
355
356 return angle::Result::Continue;
357 }
358
359 } // namespace vk
360
SyncVk()361 SyncVk::SyncVk() : SyncImpl() {}
362
~SyncVk()363 SyncVk::~SyncVk() {}
364
onDestroy(const gl::Context * context)365 void SyncVk::onDestroy(const gl::Context *context)
366 {
367 mSyncHelper.releaseToRenderer(vk::GetImpl(context)->getRenderer());
368 }
369
set(const gl::Context * context,GLenum condition,GLbitfield flags)370 angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
371 {
372 ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
373 ASSERT(flags == 0);
374
375 return mSyncHelper.initialize(vk::GetImpl(context), false);
376 }
377
clientWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout,GLenum * outResult)378 angle::Result SyncVk::clientWait(const gl::Context *context,
379 GLbitfield flags,
380 GLuint64 timeout,
381 GLenum *outResult)
382 {
383 ContextVk *contextVk = vk::GetImpl(context);
384
385 ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
386
387 bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
388 VkResult result;
389
390 ANGLE_TRY(mSyncHelper.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout),
391 &result));
392
393 switch (result)
394 {
395 case VK_EVENT_SET:
396 *outResult = GL_ALREADY_SIGNALED;
397 return angle::Result::Continue;
398
399 case VK_SUCCESS:
400 *outResult = GL_CONDITION_SATISFIED;
401 return angle::Result::Continue;
402
403 case VK_TIMEOUT:
404 *outResult = GL_TIMEOUT_EXPIRED;
405 return angle::Result::Incomplete;
406
407 default:
408 UNREACHABLE();
409 *outResult = GL_WAIT_FAILED;
410 return angle::Result::Stop;
411 }
412 }
413
serverWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout)414 angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
415 {
416 ASSERT(flags == 0);
417 ASSERT(timeout == GL_TIMEOUT_IGNORED);
418
419 ContextVk *contextVk = vk::GetImpl(context);
420 return mSyncHelper.serverWait(contextVk);
421 }
422
getStatus(const gl::Context * context,GLint * outResult)423 angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
424 {
425 ContextVk *contextVk = vk::GetImpl(context);
426 bool signaled = false;
427 ANGLE_TRY(mSyncHelper.getStatus(contextVk, &signaled));
428
429 *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
430 return angle::Result::Continue;
431 }
432
EGLSyncVk(const egl::AttributeMap & attribs)433 EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs)
434 : EGLSyncImpl(), mSyncHelper(nullptr), mAttribs(attribs)
435 {}
436
~EGLSyncVk()437 EGLSyncVk::~EGLSyncVk()
438 {
439 SafeDelete<vk::SyncHelper>(mSyncHelper);
440 }
441
onDestroy(const egl::Display * display)442 void EGLSyncVk::onDestroy(const egl::Display *display)
443 {
444 mSyncHelper->releaseToRenderer(vk::GetImpl(display)->getRenderer());
445 }
446
initialize(const egl::Display * display,const gl::Context * context,EGLenum type)447 egl::Error EGLSyncVk::initialize(const egl::Display *display,
448 const gl::Context *context,
449 EGLenum type)
450 {
451 ASSERT(context != nullptr);
452 mType = type;
453
454 switch (type)
455 {
456 case EGL_SYNC_FENCE_KHR:
457 ASSERT(mAttribs.isEmpty());
458 mSyncHelper = new vk::SyncHelper();
459 if (mSyncHelper->initialize(vk::GetImpl(context), true) == angle::Result::Stop)
460 {
461 return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
462 }
463 return egl::NoError();
464 case EGL_SYNC_NATIVE_FENCE_ANDROID:
465 {
466 vk::SyncHelperNativeFence *syncHelper = new vk::SyncHelperNativeFence();
467 mSyncHelper = syncHelper;
468 int nativeFd = static_cast<EGLint>(mAttribs.getAsInt(EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
469 EGL_NO_NATIVE_FENCE_FD_ANDROID));
470 return angle::ToEGL(syncHelper->initializeWithFd(vk::GetImpl(context), nativeFd),
471 vk::GetImpl(display), EGL_BAD_ALLOC);
472 }
473 default:
474 UNREACHABLE();
475 return egl::Error(EGL_BAD_ALLOC);
476 }
477 }
478
clientWait(const egl::Display * display,const gl::Context * context,EGLint flags,EGLTime timeout,EGLint * outResult)479 egl::Error EGLSyncVk::clientWait(const egl::Display *display,
480 const gl::Context *context,
481 EGLint flags,
482 EGLTime timeout,
483 EGLint *outResult)
484 {
485 ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
486
487 bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
488 VkResult result;
489
490 ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
491 if (mSyncHelper->clientWait(vk::GetImpl(display), contextVk, flush,
492 static_cast<uint64_t>(timeout), &result) == angle::Result::Stop)
493 {
494 return egl::Error(EGL_BAD_ALLOC);
495 }
496
497 switch (result)
498 {
499 case VK_EVENT_SET:
500 // fall through. EGL doesn't differentiate between event being already set, or set
501 // before timeout.
502 case VK_SUCCESS:
503 *outResult = EGL_CONDITION_SATISFIED_KHR;
504 return egl::NoError();
505
506 case VK_TIMEOUT:
507 *outResult = EGL_TIMEOUT_EXPIRED_KHR;
508 return egl::NoError();
509
510 default:
511 UNREACHABLE();
512 *outResult = EGL_FALSE;
513 return egl::Error(EGL_BAD_ALLOC);
514 }
515 }
516
serverWait(const egl::Display * display,const gl::Context * context,EGLint flags)517 egl::Error EGLSyncVk::serverWait(const egl::Display *display,
518 const gl::Context *context,
519 EGLint flags)
520 {
521 // Server wait requires a valid bound context.
522 ASSERT(context);
523
524 // No flags are currently implemented.
525 ASSERT(flags == 0);
526
527 DisplayVk *displayVk = vk::GetImpl(display);
528 ContextVk *contextVk = vk::GetImpl(context);
529
530 return angle::ToEGL(mSyncHelper->serverWait(contextVk), displayVk, EGL_BAD_ALLOC);
531 }
532
getStatus(const egl::Display * display,EGLint * outStatus)533 egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
534 {
535 bool signaled = false;
536 if (mSyncHelper->getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
537 {
538 return egl::Error(EGL_BAD_ALLOC);
539 }
540
541 *outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
542 return egl::NoError();
543 }
544
dupNativeFenceFD(const egl::Display * display,EGLint * fdOut) const545 egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const
546 {
547 switch (mType)
548 {
549 case EGL_SYNC_NATIVE_FENCE_ANDROID:
550 return angle::ToEGL(mSyncHelper->dupNativeFenceFD(vk::GetImpl(display), fdOut),
551 vk::GetImpl(display), EGL_BAD_PARAMETER);
552 default:
553 return egl::EglBadDisplay();
554 }
555 }
556
557 } // namespace rx
558