• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::CommandBuffer *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.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     // Flush current work, block after current pending commands.
322     ANGLE_TRY(contextVk->flushImpl(nullptr, RenderPassClosureReason::SyncObjectServerWait));
323 
324     // Add semaphore to next submit job.
325     contextVk->addWaitSemaphore(waitSemaphore.get().getHandle(),
326                                 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
327     contextVk->addGarbage(&waitSemaphore.get());  // This releases the handle.
328     return angle::Result::Continue;
329 }
330 
getStatus(Context * context,bool * signaled) const331 angle::Result SyncHelperNativeFence::getStatus(Context *context, bool *signaled) const
332 {
333     // We've got a serial, check if the serial is still in use
334     if (mUse.valid())
335     {
336         *signaled = !isCurrentlyInUse(context->getRenderer()->getLastCompletedQueueSerial());
337         return angle::Result::Continue;
338     }
339 
340     // We don't have a serial, check status of the file descriptor
341     VkResult result = SyncWaitFd(mNativeFenceFd, 0);
342     if (result != VK_TIMEOUT)
343     {
344         ANGLE_VK_TRY(context, result);
345     }
346     *signaled = (result == VK_SUCCESS);
347     return angle::Result::Continue;
348 }
349 
dupNativeFenceFD(Context * context,int * fdOut) const350 angle::Result SyncHelperNativeFence::dupNativeFenceFD(Context *context, int *fdOut) const
351 {
352     if (!mFenceWithFd.valid() || mNativeFenceFd == kInvalidFenceFd)
353     {
354         return angle::Result::Stop;
355     }
356 
357     *fdOut = dup(mNativeFenceFd);
358 
359     return angle::Result::Continue;
360 }
361 
362 }  // namespace vk
363 
SyncVk()364 SyncVk::SyncVk() : SyncImpl() {}
365 
~SyncVk()366 SyncVk::~SyncVk() {}
367 
onDestroy(const gl::Context * context)368 void SyncVk::onDestroy(const gl::Context *context)
369 {
370     mSyncHelper.releaseToRenderer(vk::GetImpl(context)->getRenderer());
371 }
372 
set(const gl::Context * context,GLenum condition,GLbitfield flags)373 angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
374 {
375     ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
376     ASSERT(flags == 0);
377 
378     return mSyncHelper.initialize(vk::GetImpl(context), false);
379 }
380 
clientWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout,GLenum * outResult)381 angle::Result SyncVk::clientWait(const gl::Context *context,
382                                  GLbitfield flags,
383                                  GLuint64 timeout,
384                                  GLenum *outResult)
385 {
386     ContextVk *contextVk = vk::GetImpl(context);
387 
388     ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
389 
390     bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
391     VkResult result;
392 
393     ANGLE_TRY(mSyncHelper.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout),
394                                      &result));
395 
396     switch (result)
397     {
398         case VK_EVENT_SET:
399             *outResult = GL_ALREADY_SIGNALED;
400             return angle::Result::Continue;
401 
402         case VK_SUCCESS:
403             *outResult = GL_CONDITION_SATISFIED;
404             return angle::Result::Continue;
405 
406         case VK_TIMEOUT:
407             *outResult = GL_TIMEOUT_EXPIRED;
408             return angle::Result::Incomplete;
409 
410         default:
411             UNREACHABLE();
412             *outResult = GL_WAIT_FAILED;
413             return angle::Result::Stop;
414     }
415 }
416 
serverWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout)417 angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
418 {
419     ASSERT(flags == 0);
420     ASSERT(timeout == GL_TIMEOUT_IGNORED);
421 
422     ContextVk *contextVk = vk::GetImpl(context);
423     return mSyncHelper.serverWait(contextVk);
424 }
425 
getStatus(const gl::Context * context,GLint * outResult)426 angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
427 {
428     ContextVk *contextVk = vk::GetImpl(context);
429     bool signaled        = false;
430     ANGLE_TRY(mSyncHelper.getStatus(contextVk, &signaled));
431 
432     *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
433     return angle::Result::Continue;
434 }
435 
EGLSyncVk(const egl::AttributeMap & attribs)436 EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs)
437     : EGLSyncImpl(), mSyncHelper(nullptr), mAttribs(attribs)
438 {}
439 
~EGLSyncVk()440 EGLSyncVk::~EGLSyncVk()
441 {
442     SafeDelete<vk::SyncHelper>(mSyncHelper);
443 }
444 
onDestroy(const egl::Display * display)445 void EGLSyncVk::onDestroy(const egl::Display *display)
446 {
447     mSyncHelper->releaseToRenderer(vk::GetImpl(display)->getRenderer());
448 }
449 
initialize(const egl::Display * display,const gl::Context * context,EGLenum type)450 egl::Error EGLSyncVk::initialize(const egl::Display *display,
451                                  const gl::Context *context,
452                                  EGLenum type)
453 {
454     ASSERT(context != nullptr);
455     mType = type;
456 
457     switch (type)
458     {
459         case EGL_SYNC_FENCE_KHR:
460             ASSERT(mAttribs.isEmpty());
461             mSyncHelper = new vk::SyncHelper();
462             if (mSyncHelper->initialize(vk::GetImpl(context), true) == angle::Result::Stop)
463             {
464                 return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
465             }
466             return egl::NoError();
467         case EGL_SYNC_NATIVE_FENCE_ANDROID:
468         {
469             vk::SyncHelperNativeFence *syncHelper = new vk::SyncHelperNativeFence();
470             mSyncHelper                           = syncHelper;
471             int nativeFd = static_cast<EGLint>(mAttribs.getAsInt(EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
472                                                                  EGL_NO_NATIVE_FENCE_FD_ANDROID));
473             return angle::ToEGL(syncHelper->initializeWithFd(vk::GetImpl(context), nativeFd),
474                                 vk::GetImpl(display), EGL_BAD_ALLOC);
475         }
476         default:
477             UNREACHABLE();
478             return egl::Error(EGL_BAD_ALLOC);
479     }
480 }
481 
clientWait(const egl::Display * display,const gl::Context * context,EGLint flags,EGLTime timeout,EGLint * outResult)482 egl::Error EGLSyncVk::clientWait(const egl::Display *display,
483                                  const gl::Context *context,
484                                  EGLint flags,
485                                  EGLTime timeout,
486                                  EGLint *outResult)
487 {
488     ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
489 
490     bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
491     VkResult result;
492 
493     ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
494     if (mSyncHelper->clientWait(vk::GetImpl(display), contextVk, flush,
495                                 static_cast<uint64_t>(timeout), &result) == angle::Result::Stop)
496     {
497         return egl::Error(EGL_BAD_ALLOC);
498     }
499 
500     switch (result)
501     {
502         case VK_EVENT_SET:
503             // fall through.  EGL doesn't differentiate between event being already set, or set
504             // before timeout.
505         case VK_SUCCESS:
506             *outResult = EGL_CONDITION_SATISFIED_KHR;
507             return egl::NoError();
508 
509         case VK_TIMEOUT:
510             *outResult = EGL_TIMEOUT_EXPIRED_KHR;
511             return egl::NoError();
512 
513         default:
514             UNREACHABLE();
515             *outResult = EGL_FALSE;
516             return egl::Error(EGL_BAD_ALLOC);
517     }
518 }
519 
serverWait(const egl::Display * display,const gl::Context * context,EGLint flags)520 egl::Error EGLSyncVk::serverWait(const egl::Display *display,
521                                  const gl::Context *context,
522                                  EGLint flags)
523 {
524     // Server wait requires a valid bound context.
525     ASSERT(context);
526 
527     // No flags are currently implemented.
528     ASSERT(flags == 0);
529 
530     DisplayVk *displayVk = vk::GetImpl(display);
531     ContextVk *contextVk = vk::GetImpl(context);
532 
533     return angle::ToEGL(mSyncHelper->serverWait(contextVk), displayVk, EGL_BAD_ALLOC);
534 }
535 
getStatus(const egl::Display * display,EGLint * outStatus)536 egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
537 {
538     bool signaled = false;
539     if (mSyncHelper->getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
540     {
541         return egl::Error(EGL_BAD_ALLOC);
542     }
543 
544     *outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
545     return egl::NoError();
546 }
547 
dupNativeFenceFD(const egl::Display * display,EGLint * fdOut) const548 egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const
549 {
550     switch (mType)
551     {
552         case EGL_SYNC_NATIVE_FENCE_ANDROID:
553             return angle::ToEGL(mSyncHelper->dupNativeFenceFD(vk::GetImpl(display), fdOut),
554                                 vk::GetImpl(display), EGL_BAD_PARAMETER);
555         default:
556             return egl::EglBadDisplay();
557     }
558 }
559 
560 }  // namespace rx
561