• 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 namespace rx
19 {
20 namespace vk
21 {
SyncHelper()22 SyncHelper::SyncHelper() {}
23 
~SyncHelper()24 SyncHelper::~SyncHelper() {}
25 
releaseToRenderer(RendererVk * renderer)26 void SyncHelper::releaseToRenderer(RendererVk *renderer)
27 {
28     renderer->collectGarbageAndReinit(&mUse, &mEvent);
29     mFence.reset(renderer->getDevice());
30 }
31 
initialize(ContextVk * contextVk)32 angle::Result SyncHelper::initialize(ContextVk *contextVk)
33 {
34     ASSERT(!mEvent.valid());
35 
36     RendererVk *renderer = contextVk->getRenderer();
37     VkDevice device      = renderer->getDevice();
38 
39     VkEventCreateInfo eventCreateInfo = {};
40     eventCreateInfo.sType             = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
41     eventCreateInfo.flags             = 0;
42 
43     DeviceScoped<Event> event(device);
44     ANGLE_VK_TRY(contextVk, event.get().init(device, eventCreateInfo));
45     ANGLE_TRY(contextVk->getNextSubmitFence(&mFence));
46 
47     mEvent = event.release();
48 
49     vk::CommandBuffer *outsideRenderPassCommandBuffer;
50     ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&outsideRenderPassCommandBuffer));
51     outsideRenderPassCommandBuffer->setEvent(mEvent.getHandle(),
52                                              VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
53     retain(&contextVk->getResourceUseList());
54 
55     return angle::Result::Continue;
56 }
57 
clientWait(Context * context,ContextVk * contextVk,bool flushCommands,uint64_t timeout,VkResult * outResult)58 angle::Result SyncHelper::clientWait(Context *context,
59                                      ContextVk *contextVk,
60                                      bool flushCommands,
61                                      uint64_t timeout,
62                                      VkResult *outResult)
63 {
64     RendererVk *renderer = context->getRenderer();
65 
66     // If the event is already set, don't wait
67     bool alreadySignaled = false;
68     ANGLE_TRY(getStatus(context, &alreadySignaled));
69     if (alreadySignaled)
70     {
71         *outResult = VK_EVENT_SET;
72         return angle::Result::Continue;
73     }
74 
75     // If timeout is zero, there's no need to wait, so return timeout already.
76     if (timeout == 0)
77     {
78         *outResult = VK_TIMEOUT;
79         return angle::Result::Continue;
80     }
81 
82     if (flushCommands && contextVk)
83     {
84         ANGLE_TRY(contextVk->flushImpl(nullptr));
85     }
86 
87     // Wait on the fence that's expected to be signaled on the first vkQueueSubmit after
88     // `initialize` was called. The first fence is the fence created to signal this sync.
89     ASSERT(mFence.get().valid());
90     VkResult status = mFence.get().wait(renderer->getDevice(), timeout);
91 
92     // Check for errors, but don't consider timeout as such.
93     if (status != VK_TIMEOUT)
94     {
95         ANGLE_VK_TRY(context, status);
96     }
97 
98     *outResult = status;
99     return angle::Result::Continue;
100 }
101 
serverWait(ContextVk * contextVk)102 angle::Result SyncHelper::serverWait(ContextVk *contextVk)
103 {
104     vk::CommandBuffer *outsideRenderPassCommandBuffer;
105     ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&outsideRenderPassCommandBuffer));
106     outsideRenderPassCommandBuffer->waitEvents(
107         1, mEvent.ptr(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
108         0, nullptr, 0, nullptr, 0, nullptr);
109     retain(&contextVk->getResourceUseList());
110     return angle::Result::Continue;
111 }
112 
getStatus(Context * context,bool * signaled) const113 angle::Result SyncHelper::getStatus(Context *context, bool *signaled) const
114 {
115     VkResult result = mEvent.getStatus(context->getDevice());
116     if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
117     {
118         ANGLE_VK_TRY(context, result);
119     }
120     *signaled = (result == VK_EVENT_SET);
121     return angle::Result::Continue;
122 }
123 
releaseToRenderer(RendererVk * renderer)124 void SyncHelperNativeFence::releaseToRenderer(RendererVk *renderer)
125 {
126     renderer->collectGarbageAndReinit(&mUse, &mFenceWithFd);
127 }
128 
129 // Note: Having mFenceWithFd hold the FD, so that ownership is with ICD. Any call to clientWait
130 // or serverWait will ensure the FD or dup of FD goes to application or ICD. At release, above
131 // it's Garbage collected/destroyed. Otherwise can't time when to close(fd);
initializeWithFd(ContextVk * contextVk,int inFd)132 angle::Result SyncHelperNativeFence::initializeWithFd(ContextVk *contextVk, int inFd)
133 {
134     RendererVk *renderer = contextVk->getRenderer();
135     VkDevice device      = renderer->getDevice();
136 
137     // Create fenceInfo base.
138     VkFenceCreateInfo fenceCreateInfo = {};
139     fenceCreateInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
140     fenceCreateInfo.flags             = 0;
141 
142     // If valid FD provided by application - import it to fence.
143     if (inFd > kInvalidFenceFd)
144     {
145         // Initialize/create a VkFence handle
146         ANGLE_VK_TRY(contextVk, mFenceWithFd.init(device, fenceCreateInfo));
147 
148         // Import FD - after creating fence.
149         VkImportFenceFdInfoKHR importFenceFdInfo = {};
150         importFenceFdInfo.sType                  = VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR;
151         importFenceFdInfo.pNext                  = nullptr;
152         importFenceFdInfo.fence                  = mFenceWithFd.getHandle();
153         importFenceFdInfo.flags                  = VK_FENCE_IMPORT_TEMPORARY_BIT_KHR;
154         importFenceFdInfo.handleType             = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
155         importFenceFdInfo.fd                     = inFd;
156 
157         VkResult result = mFenceWithFd.importFd(device, importFenceFdInfo);
158         if (result != VK_SUCCESS)
159         {
160             mFenceWithFd.destroy(device);
161             ANGLE_VK_TRY(contextVk, result);
162         }
163         retain(&contextVk->getResourceUseList());
164         return angle::Result::Continue;
165     }
166 
167     // If invalid FD provided by application - create one with fence.
168     if (inFd == kInvalidFenceFd)
169     {
170         // Attach export FD-handleType, pNext struct, to indicate we may want to export FD.
171         VkExportFenceCreateInfo exportCreateInfo = {};
172         exportCreateInfo.sType                   = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO;
173         exportCreateInfo.handleTypes             = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
174         fenceCreateInfo.pNext                    = &exportCreateInfo;
175 
176         // Initialize/create a VkFence handle
177         ANGLE_VK_TRY(contextVk, mFenceWithFd.init(device, fenceCreateInfo));
178 
179         /*
180           Spec: "When a fence sync object is created or when an EGL native fence sync
181           object is created with the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute set to
182           EGL_NO_NATIVE_FENCE_FD_ANDROID, eglCreateSyncKHR also inserts a fence command
183           into the command stream of the bound client API's current context and associates it
184           with the newly created sync object.
185         */
186         // Flush first because the fence comes after current pending set of commands.
187         ANGLE_TRY(contextVk->flushImpl(nullptr));
188 
189         retain(&contextVk->getResourceUseList());
190 
191         Serial serialOut;
192         VkSubmitInfo submitInfo = {};
193         submitInfo.sType        = VK_STRUCTURE_TYPE_SUBMIT_INFO;
194         if (renderer->queueSubmit(contextVk, contextVk->getPriority(), submitInfo, &mFenceWithFd,
195                                   &serialOut) != angle::Result::Continue)
196         {
197             mFenceWithFd.destroy(device);
198             return angle::Result::Stop;
199         }
200         return angle::Result::Continue;
201     }
202     // Should not get here
203     return angle::Result::Stop;
204 }
205 
clientWait(Context * context,ContextVk * contextVk,bool flushCommands,uint64_t timeout,VkResult * outResult)206 angle::Result SyncHelperNativeFence::clientWait(Context *context,
207                                                 ContextVk *contextVk,
208                                                 bool flushCommands,
209                                                 uint64_t timeout,
210                                                 VkResult *outResult)
211 {
212     RendererVk *renderer = context->getRenderer();
213 
214     // If already signaled, don't wait
215     bool alreadySignaled = false;
216     ANGLE_TRY(getStatus(context, &alreadySignaled));
217     if (alreadySignaled)
218     {
219         *outResult = VK_SUCCESS;
220         return angle::Result::Continue;
221     }
222 
223     // If timeout is zero, there's no need to wait, so return timeout already.
224     if (timeout == 0)
225     {
226         *outResult = VK_TIMEOUT;
227         return angle::Result::Continue;
228     }
229 
230     if (flushCommands && contextVk)
231     {
232         ANGLE_TRY(contextVk->flushImpl(nullptr));
233     }
234     // Wait for mFenceWithFd to be signaled.
235     VkResult status = mFenceWithFd.wait(renderer->getDevice(), timeout);
236 
237     // Check for errors, but don't consider timeout as such.
238     if (status != VK_TIMEOUT)
239     {
240         ANGLE_VK_TRY(context, status);
241     }
242 
243     *outResult = status;
244     return angle::Result::Continue;
245 }
246 
serverWait(ContextVk * contextVk)247 angle::Result SyncHelperNativeFence::serverWait(ContextVk *contextVk)
248 {
249     if (!mFenceWithFd.valid())
250     {
251         return angle::Result::Stop;
252     }
253 
254     RendererVk *renderer = contextVk->getRenderer();
255     VkDevice device      = renderer->getDevice();
256 
257     // Export an FD from mFenceWithFd
258     int fenceFd                   = kInvalidFenceFd;
259     VkFenceGetFdInfoKHR getFdInfo = {};
260     getFdInfo.sType               = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
261     getFdInfo.fence               = mFenceWithFd.getHandle();
262     getFdInfo.handleType          = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
263     ANGLE_VK_TRY(contextVk, mFenceWithFd.exportFd(device, getFdInfo, &fenceFd));
264 
265     DeviceScoped<Semaphore> waitSemaphore(device);
266     // Wait semaphore for next vkQueueSubmit().
267     // Create a Semaphore with imported fenceFd.
268     ANGLE_VK_TRY(contextVk, waitSemaphore.get().init(device));
269 
270     VkImportSemaphoreFdInfoKHR importFdInfo = {};
271     importFdInfo.sType                      = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
272     importFdInfo.semaphore                  = waitSemaphore.get().getHandle();
273     importFdInfo.flags                      = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR;
274     importFdInfo.handleType                 = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
275     importFdInfo.fd                         = fenceFd;
276     ANGLE_VK_TRY(contextVk, waitSemaphore.get().importFd(device, importFdInfo));
277 
278     // Flush current work, block after current pending commands.
279     ANGLE_TRY(contextVk->flushImpl(nullptr));
280 
281     // Add semaphore to next submit job.
282     contextVk->addWaitSemaphore(waitSemaphore.get().getHandle(),
283                                 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
284     contextVk->addGarbage(&waitSemaphore.get());  // This releases the handle.
285     return angle::Result::Continue;
286 }
287 
getStatus(Context * context,bool * signaled) const288 angle::Result SyncHelperNativeFence::getStatus(Context *context, bool *signaled) const
289 {
290     VkResult result = mFenceWithFd.getStatus(context->getDevice());
291     if (result != VK_SUCCESS && result != VK_NOT_READY)
292     {
293         ANGLE_VK_TRY(context, result);
294     }
295     *signaled = (result == VK_SUCCESS);
296     return angle::Result::Continue;
297 }
298 
dupNativeFenceFD(Context * context,int * fdOut) const299 angle::Result SyncHelperNativeFence::dupNativeFenceFD(Context *context, int *fdOut) const
300 {
301     if (!mFenceWithFd.valid())
302     {
303         return angle::Result::Stop;
304     }
305 
306     VkFenceGetFdInfoKHR fenceGetFdInfo = {};
307     fenceGetFdInfo.sType               = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
308     fenceGetFdInfo.fence               = mFenceWithFd.getHandle();
309     fenceGetFdInfo.handleType          = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
310     ANGLE_VK_TRY(context, mFenceWithFd.exportFd(context->getDevice(), fenceGetFdInfo, fdOut));
311     return angle::Result::Continue;
312 }
313 
314 }  // namespace vk
315 
SyncVk()316 SyncVk::SyncVk() : SyncImpl() {}
317 
~SyncVk()318 SyncVk::~SyncVk() {}
319 
onDestroy(const gl::Context * context)320 void SyncVk::onDestroy(const gl::Context *context)
321 {
322     mSyncHelper.releaseToRenderer(vk::GetImpl(context)->getRenderer());
323 }
324 
set(const gl::Context * context,GLenum condition,GLbitfield flags)325 angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
326 {
327     ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
328     ASSERT(flags == 0);
329 
330     return mSyncHelper.initialize(vk::GetImpl(context));
331 }
332 
clientWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout,GLenum * outResult)333 angle::Result SyncVk::clientWait(const gl::Context *context,
334                                  GLbitfield flags,
335                                  GLuint64 timeout,
336                                  GLenum *outResult)
337 {
338     ContextVk *contextVk = vk::GetImpl(context);
339 
340     ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
341 
342     bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
343     VkResult result;
344 
345     ANGLE_TRY(mSyncHelper.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout),
346                                      &result));
347 
348     switch (result)
349     {
350         case VK_EVENT_SET:
351             *outResult = GL_ALREADY_SIGNALED;
352             return angle::Result::Continue;
353 
354         case VK_SUCCESS:
355             *outResult = GL_CONDITION_SATISFIED;
356             return angle::Result::Continue;
357 
358         case VK_TIMEOUT:
359             *outResult = GL_TIMEOUT_EXPIRED;
360             return angle::Result::Incomplete;
361 
362         default:
363             UNREACHABLE();
364             *outResult = GL_WAIT_FAILED;
365             return angle::Result::Stop;
366     }
367 }
368 
serverWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout)369 angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
370 {
371     ASSERT(flags == 0);
372     ASSERT(timeout == GL_TIMEOUT_IGNORED);
373 
374     ContextVk *contextVk = vk::GetImpl(context);
375     return mSyncHelper.serverWait(contextVk);
376 }
377 
getStatus(const gl::Context * context,GLint * outResult)378 angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
379 {
380     bool signaled = false;
381     ANGLE_TRY(mSyncHelper.getStatus(vk::GetImpl(context), &signaled));
382 
383     *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
384     return angle::Result::Continue;
385 }
386 
EGLSyncVk(const egl::AttributeMap & attribs)387 EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs)
388     : EGLSyncImpl(), mSyncHelper(nullptr), mAttribs(attribs)
389 {}
390 
~EGLSyncVk()391 EGLSyncVk::~EGLSyncVk()
392 {
393     SafeDelete<vk::SyncHelper>(mSyncHelper);
394 }
395 
onDestroy(const egl::Display * display)396 void EGLSyncVk::onDestroy(const egl::Display *display)
397 {
398     mSyncHelper->releaseToRenderer(vk::GetImpl(display)->getRenderer());
399 }
400 
initialize(const egl::Display * display,const gl::Context * context,EGLenum type)401 egl::Error EGLSyncVk::initialize(const egl::Display *display,
402                                  const gl::Context *context,
403                                  EGLenum type)
404 {
405     ASSERT(context != nullptr);
406     mType = type;
407 
408     switch (type)
409     {
410         case EGL_SYNC_FENCE_KHR:
411             ASSERT(mAttribs.isEmpty());
412             mSyncHelper = new vk::SyncHelper();
413             if (mSyncHelper->initialize(vk::GetImpl(context)) == angle::Result::Stop)
414             {
415                 return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
416             }
417             return egl::NoError();
418         case EGL_SYNC_NATIVE_FENCE_ANDROID:
419         {
420             vk::SyncHelperNativeFence *syncHelper = new vk::SyncHelperNativeFence();
421             mSyncHelper                           = syncHelper;
422             int nativeFd = static_cast<EGLint>(mAttribs.getAsInt(EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
423                                                                  EGL_NO_NATIVE_FENCE_FD_ANDROID));
424             return angle::ToEGL(syncHelper->initializeWithFd(vk::GetImpl(context), nativeFd),
425                                 vk::GetImpl(display), EGL_BAD_ALLOC);
426         }
427         default:
428             UNREACHABLE();
429             return egl::Error(EGL_BAD_ALLOC);
430     }
431 }
432 
clientWait(const egl::Display * display,const gl::Context * context,EGLint flags,EGLTime timeout,EGLint * outResult)433 egl::Error EGLSyncVk::clientWait(const egl::Display *display,
434                                  const gl::Context *context,
435                                  EGLint flags,
436                                  EGLTime timeout,
437                                  EGLint *outResult)
438 {
439     ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
440 
441     bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
442     VkResult result;
443 
444     ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
445     if (mSyncHelper->clientWait(vk::GetImpl(display), contextVk, flush,
446                                 static_cast<uint64_t>(timeout), &result) == angle::Result::Stop)
447     {
448         return egl::Error(EGL_BAD_ALLOC);
449     }
450 
451     switch (result)
452     {
453         case VK_EVENT_SET:
454             // fall through.  EGL doesn't differentiate between event being already set, or set
455             // before timeout.
456         case VK_SUCCESS:
457             *outResult = EGL_CONDITION_SATISFIED_KHR;
458             return egl::NoError();
459 
460         case VK_TIMEOUT:
461             *outResult = EGL_TIMEOUT_EXPIRED_KHR;
462             return egl::NoError();
463 
464         default:
465             UNREACHABLE();
466             *outResult = EGL_FALSE;
467             return egl::Error(EGL_BAD_ALLOC);
468     }
469 }
470 
serverWait(const egl::Display * display,const gl::Context * context,EGLint flags)471 egl::Error EGLSyncVk::serverWait(const egl::Display *display,
472                                  const gl::Context *context,
473                                  EGLint flags)
474 {
475     // Server wait requires a valid bound context.
476     ASSERT(context);
477 
478     // No flags are currently implemented.
479     ASSERT(flags == 0);
480 
481     DisplayVk *displayVk = vk::GetImpl(display);
482     ContextVk *contextVk = vk::GetImpl(context);
483 
484     return angle::ToEGL(mSyncHelper->serverWait(contextVk), displayVk, EGL_BAD_ALLOC);
485 }
486 
getStatus(const egl::Display * display,EGLint * outStatus)487 egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
488 {
489     bool signaled = false;
490     if (mSyncHelper->getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
491     {
492         return egl::Error(EGL_BAD_ALLOC);
493     }
494 
495     *outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
496     return egl::NoError();
497 }
498 
dupNativeFenceFD(const egl::Display * display,EGLint * fdOut) const499 egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const
500 {
501     switch (mType)
502     {
503         case EGL_SYNC_NATIVE_FENCE_ANDROID:
504             return angle::ToEGL(mSyncHelper->dupNativeFenceFD(vk::GetImpl(display), fdOut),
505                                 vk::GetImpl(display), EGL_BAD_PARAMETER);
506         default:
507             return egl::EglBadDisplay();
508     }
509 }
510 
511 }  // namespace rx
512