• 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 {
83     renderer->collectGarbageAndReinit(&mUse, &mEvent);
84 }
85 
initialize(ContextVk * contextVk,bool isEglSyncObject)86 angle::Result SyncHelper::initialize(ContextVk *contextVk, bool isEglSyncObject)
87 {
88     ASSERT(!mEvent.valid());
89 
90     // Break the current render pass to ensure the proper ordering of the sync object in the
91     // commands.
92     ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass());
93 
94     RendererVk *renderer = contextVk->getRenderer();
95     VkDevice device      = renderer->getDevice();
96 
97     VkEventCreateInfo eventCreateInfo = {};
98     eventCreateInfo.sType             = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
99     eventCreateInfo.flags             = 0;
100 
101     DeviceScoped<Event> event(device);
102     ANGLE_VK_TRY(contextVk, event.get().init(device, eventCreateInfo));
103 
104     mEvent = event.release();
105 
106     vk::CommandBuffer *commandBuffer;
107     ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
108     commandBuffer->setEvent(mEvent.getHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
109     retain(&contextVk->getResourceUseList());
110 
111     if (isEglSyncObject)
112     {
113         contextVk->onEGLSyncHelperInitialize();
114     }
115     else
116     {
117         contextVk->onSyncHelperInitialize();
118     }
119 
120     return angle::Result::Continue;
121 }
122 
clientWait(Context * context,ContextVk * contextVk,bool flushCommands,uint64_t timeout,VkResult * outResult)123 angle::Result SyncHelper::clientWait(Context *context,
124                                      ContextVk *contextVk,
125                                      bool flushCommands,
126                                      uint64_t timeout,
127                                      VkResult *outResult)
128 {
129     RendererVk *renderer = context->getRenderer();
130 
131     // If the event is already set, don't wait
132     bool alreadySignaled = false;
133     ANGLE_TRY(getStatus(context, &alreadySignaled));
134     if (alreadySignaled)
135     {
136         *outResult = VK_EVENT_SET;
137         return angle::Result::Continue;
138     }
139 
140     // We defer (ignore) flushes, so it's possible that the glFence's signal operation is pending
141     // submission.
142     if (contextVk)
143     {
144         if (flushCommands || usedInRecordedCommands())
145         {
146             ANGLE_TRY(contextVk->flushImpl(nullptr));
147         }
148     }
149     else
150     {
151         if (!mUse.getSerial().valid())
152         {
153             // The sync object wasn't flushed before waiting, so the wait will always necessarily
154             // time out.
155             WARN() << "clientWaitSync called without flushing sync object and/or a valid context "
156                       "active.";
157             *outResult = VK_TIMEOUT;
158             return angle::Result::Continue;
159         }
160     }
161 
162     // If timeout is zero, there's no need to wait, so return timeout already.
163     // Do this after (possibly) flushing, since some apps/tests/traces are relying on this behavior.
164     if (timeout == 0)
165     {
166         *outResult = VK_TIMEOUT;
167         return angle::Result::Continue;
168     }
169 
170     ASSERT(mUse.getSerial().valid());
171 
172     VkResult status = VK_SUCCESS;
173     ANGLE_TRY(renderer->waitForSerialWithUserTimeout(context, mUse.getSerial(), timeout, &status));
174 
175     // Check for errors, but don't consider timeout as such.
176     if (status != VK_TIMEOUT)
177     {
178         ANGLE_VK_TRY(context, status);
179     }
180 
181     *outResult = status;
182     return angle::Result::Continue;
183 }
184 
serverWait(ContextVk * contextVk)185 angle::Result SyncHelper::serverWait(ContextVk *contextVk)
186 {
187     vk::CommandBuffer *commandBuffer;
188     ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
189     commandBuffer->waitEvents(1, mEvent.ptr(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
190                               VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, nullptr, 0, nullptr, 0,
191                               nullptr);
192     retain(&contextVk->getResourceUseList());
193     return angle::Result::Continue;
194 }
195 
getStatus(Context * context,bool * signaled) const196 angle::Result SyncHelper::getStatus(Context *context, bool *signaled) const
197 {
198     VkResult result = mEvent.getStatus(context->getDevice());
199     if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
200     {
201         ANGLE_VK_TRY(context, result);
202     }
203     *signaled = (result == VK_EVENT_SET);
204     return angle::Result::Continue;
205 }
206 
SyncHelperNativeFence()207 SyncHelperNativeFence::SyncHelperNativeFence() : mNativeFenceFd(kInvalidFenceFd) {}
208 
~SyncHelperNativeFence()209 SyncHelperNativeFence::~SyncHelperNativeFence()
210 {
211     if (mNativeFenceFd != kInvalidFenceFd)
212     {
213         close(mNativeFenceFd);
214     }
215 }
216 
releaseToRenderer(RendererVk * renderer)217 void SyncHelperNativeFence::releaseToRenderer(RendererVk *renderer)
218 {
219     renderer->collectGarbageAndReinit(&mUse, &mFenceWithFd);
220 }
221 
222 // Note: We have mFenceWithFd hold the FD, so that ownership is with ICD. Meanwhile we store a dup
223 // of FD in SyncHelperNativeFence for further reference, i.e. dup of FD. Any call to clientWait
224 // or serverWait will ensure the FD or dup of FD goes to application or ICD. At release, above
225 // it's Garbage collected/destroyed. Otherwise we can't time when to close(fd);
initializeWithFd(ContextVk * contextVk,int inFd)226 angle::Result SyncHelperNativeFence::initializeWithFd(ContextVk *contextVk, int inFd)
227 {
228     ASSERT(inFd >= kInvalidFenceFd);
229 
230     // If valid FD provided by application - import it to fence.
231     if (inFd > kInvalidFenceFd)
232     {
233         // File descriptor ownership: EGL_ANDROID_native_fence_sync
234         // Whenever a file descriptor is passed into or returned from an
235         // EGL call in this extension, ownership of that file descriptor is
236         // transferred. The recipient of the file descriptor must close it when it is
237         // no longer needed, and the provider of the file descriptor must dup it
238         // before providing it if they require continued use of the native fence.
239         mNativeFenceFd = inFd;
240         return angle::Result::Continue;
241     }
242 
243     RendererVk *renderer = contextVk->getRenderer();
244     VkDevice device      = renderer->getDevice();
245 
246     DeviceScoped<vk::Fence> fence(device);
247 
248     VkExportFenceCreateInfo exportCreateInfo = {};
249     exportCreateInfo.sType                   = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO;
250     exportCreateInfo.pNext                   = nullptr;
251     exportCreateInfo.handleTypes             = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
252 
253     // Create fenceInfo base.
254     VkFenceCreateInfo fenceCreateInfo = {};
255     fenceCreateInfo.sType             = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
256     fenceCreateInfo.flags             = 0;
257     fenceCreateInfo.pNext             = &exportCreateInfo;
258 
259     // Initialize/create a VkFence handle
260     ANGLE_VK_TRY(contextVk, fence.get().init(device, fenceCreateInfo));
261 
262     // invalid FD provided by application - create one with fence.
263     /*
264       Spec: "When a fence sync object is created or when an EGL native fence sync
265       object is created with the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute set to
266       EGL_NO_NATIVE_FENCE_FD_ANDROID, eglCreateSyncKHR also inserts a fence command
267       into the command stream of the bound client API's current context and associates it
268       with the newly created sync object.
269     */
270     // Flush first because the fence comes after current pending set of commands.
271     ANGLE_TRY(contextVk->flushImpl(nullptr));
272 
273     retain(&contextVk->getResourceUseList());
274 
275     Serial serialOut;
276     // exportFd is exporting VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR type handle which
277     // obeys copy semantics. This means that the fence must already be signaled or the work to
278     // signal it is in the graphics pipeline at the time we export the fd. Thus we need to
279     // EnsureSubmitted here.
280     ANGLE_TRY(renderer->queueSubmitOneOff(
281         contextVk, vk::PrimaryCommandBuffer(), contextVk->hasProtectedContent(),
282         contextVk->getPriority(), &fence.get(), vk::SubmitPolicy::EnsureSubmitted, &serialOut));
283 
284     VkFenceGetFdInfoKHR fenceGetFdInfo = {};
285     fenceGetFdInfo.sType               = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR;
286     fenceGetFdInfo.fence               = fence.get().getHandle();
287     fenceGetFdInfo.handleType          = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
288     ANGLE_VK_TRY(contextVk, fence.get().exportFd(device, fenceGetFdInfo, &mNativeFenceFd));
289 
290     mFenceWithFd = fence.release();
291 
292     return angle::Result::Continue;
293 }
294 
clientWait(Context * context,ContextVk * contextVk,bool flushCommands,uint64_t timeout,VkResult * outResult)295 angle::Result SyncHelperNativeFence::clientWait(Context *context,
296                                                 ContextVk *contextVk,
297                                                 bool flushCommands,
298                                                 uint64_t timeout,
299                                                 VkResult *outResult)
300 {
301     RendererVk *renderer = context->getRenderer();
302 
303     // If already signaled, don't wait
304     bool alreadySignaled = false;
305     ANGLE_TRY(getStatus(context, &alreadySignaled));
306     if (alreadySignaled)
307     {
308         *outResult = VK_SUCCESS;
309         return angle::Result::Continue;
310     }
311 
312     // If timeout is zero, there's no need to wait, so return timeout already.
313     if (timeout == 0)
314     {
315         *outResult = VK_TIMEOUT;
316         return angle::Result::Continue;
317     }
318 
319     if (flushCommands && contextVk)
320     {
321         ANGLE_TRY(contextVk->flushImpl(nullptr));
322     }
323 
324     VkResult status = VK_SUCCESS;
325     if (mUse.valid())
326     {
327         // We have a valid serial to wait on
328         ANGLE_TRY(
329             renderer->waitForSerialWithUserTimeout(context, mUse.getSerial(), timeout, &status));
330     }
331     else
332     {
333         // We need to wait on the file descriptor
334 
335         status = SyncWaitFd(mNativeFenceFd, timeout);
336         if (status != VK_TIMEOUT)
337         {
338             ANGLE_VK_TRY(contextVk, status);
339         }
340     }
341 
342     *outResult = status;
343     return angle::Result::Continue;
344 }
345 
serverWait(ContextVk * contextVk)346 angle::Result SyncHelperNativeFence::serverWait(ContextVk *contextVk)
347 {
348     RendererVk *renderer = contextVk->getRenderer();
349     VkDevice device      = renderer->getDevice();
350 
351     DeviceScoped<Semaphore> waitSemaphore(device);
352     // Wait semaphore for next vkQueueSubmit().
353     // Create a Semaphore with imported fenceFd.
354     ANGLE_VK_TRY(contextVk, waitSemaphore.get().init(device));
355 
356     VkImportSemaphoreFdInfoKHR importFdInfo = {};
357     importFdInfo.sType                      = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
358     importFdInfo.semaphore                  = waitSemaphore.get().getHandle();
359     importFdInfo.flags                      = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR;
360     importFdInfo.handleType                 = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR;
361     importFdInfo.fd                         = dup(mNativeFenceFd);
362     ANGLE_VK_TRY(contextVk, waitSemaphore.get().importFd(device, importFdInfo));
363 
364     // Flush current work, block after current pending commands.
365     ANGLE_TRY(contextVk->flushImpl(nullptr));
366 
367     // Add semaphore to next submit job.
368     contextVk->addWaitSemaphore(waitSemaphore.get().getHandle(),
369                                 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
370     contextVk->addGarbage(&waitSemaphore.get());  // This releases the handle.
371     return angle::Result::Continue;
372 }
373 
getStatus(Context * context,bool * signaled) const374 angle::Result SyncHelperNativeFence::getStatus(Context *context, bool *signaled) const
375 {
376     // We've got a serial, check if the serial is still in use
377     if (mUse.valid())
378     {
379         *signaled = !isCurrentlyInUse(context->getRenderer()->getLastCompletedQueueSerial());
380         return angle::Result::Continue;
381     }
382 
383     // We don't have a serial, check status of the file descriptor
384     VkResult result = SyncWaitFd(mNativeFenceFd, 0);
385     if (result != VK_TIMEOUT)
386     {
387         ANGLE_VK_TRY(context, result);
388     }
389     *signaled = (result == VK_SUCCESS);
390     return angle::Result::Continue;
391 }
392 
dupNativeFenceFD(Context * context,int * fdOut) const393 angle::Result SyncHelperNativeFence::dupNativeFenceFD(Context *context, int *fdOut) const
394 {
395     if (!mFenceWithFd.valid() || mNativeFenceFd == kInvalidFenceFd)
396     {
397         return angle::Result::Stop;
398     }
399 
400     *fdOut = dup(mNativeFenceFd);
401 
402     return angle::Result::Continue;
403 }
404 
405 }  // namespace vk
406 
SyncVk()407 SyncVk::SyncVk() : SyncImpl() {}
408 
~SyncVk()409 SyncVk::~SyncVk() {}
410 
onDestroy(const gl::Context * context)411 void SyncVk::onDestroy(const gl::Context *context)
412 {
413     mSyncHelper.releaseToRenderer(vk::GetImpl(context)->getRenderer());
414 }
415 
set(const gl::Context * context,GLenum condition,GLbitfield flags)416 angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
417 {
418     ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
419     ASSERT(flags == 0);
420 
421     return mSyncHelper.initialize(vk::GetImpl(context), false);
422 }
423 
clientWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout,GLenum * outResult)424 angle::Result SyncVk::clientWait(const gl::Context *context,
425                                  GLbitfield flags,
426                                  GLuint64 timeout,
427                                  GLenum *outResult)
428 {
429     ContextVk *contextVk = vk::GetImpl(context);
430 
431     ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
432 
433     bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
434     VkResult result;
435 
436     ANGLE_TRY(mSyncHelper.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout),
437                                      &result));
438 
439     switch (result)
440     {
441         case VK_EVENT_SET:
442             *outResult = GL_ALREADY_SIGNALED;
443             return angle::Result::Continue;
444 
445         case VK_SUCCESS:
446             *outResult = GL_CONDITION_SATISFIED;
447             return angle::Result::Continue;
448 
449         case VK_TIMEOUT:
450             *outResult = GL_TIMEOUT_EXPIRED;
451             return angle::Result::Incomplete;
452 
453         default:
454             UNREACHABLE();
455             *outResult = GL_WAIT_FAILED;
456             return angle::Result::Stop;
457     }
458 }
459 
serverWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout)460 angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
461 {
462     ASSERT(flags == 0);
463     ASSERT(timeout == GL_TIMEOUT_IGNORED);
464 
465     ContextVk *contextVk = vk::GetImpl(context);
466     return mSyncHelper.serverWait(contextVk);
467 }
468 
getStatus(const gl::Context * context,GLint * outResult)469 angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
470 {
471     ContextVk *contextVk = vk::GetImpl(context);
472     if (contextVk->getShareGroupVk()->isSyncObjectPendingFlush())
473     {
474         ANGLE_TRY(contextVk->flushImpl(nullptr));
475     }
476 
477     bool signaled = false;
478     ANGLE_TRY(mSyncHelper.getStatus(contextVk, &signaled));
479 
480     *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
481     return angle::Result::Continue;
482 }
483 
EGLSyncVk(const egl::AttributeMap & attribs)484 EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs)
485     : EGLSyncImpl(), mSyncHelper(nullptr), mAttribs(attribs)
486 {}
487 
~EGLSyncVk()488 EGLSyncVk::~EGLSyncVk()
489 {
490     SafeDelete<vk::SyncHelper>(mSyncHelper);
491 }
492 
onDestroy(const egl::Display * display)493 void EGLSyncVk::onDestroy(const egl::Display *display)
494 {
495     mSyncHelper->releaseToRenderer(vk::GetImpl(display)->getRenderer());
496 }
497 
initialize(const egl::Display * display,const gl::Context * context,EGLenum type)498 egl::Error EGLSyncVk::initialize(const egl::Display *display,
499                                  const gl::Context *context,
500                                  EGLenum type)
501 {
502     ASSERT(context != nullptr);
503     mType = type;
504 
505     switch (type)
506     {
507         case EGL_SYNC_FENCE_KHR:
508             ASSERT(mAttribs.isEmpty());
509             mSyncHelper = new vk::SyncHelper();
510             if (mSyncHelper->initialize(vk::GetImpl(context), true) == angle::Result::Stop)
511             {
512                 return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
513             }
514             return egl::NoError();
515         case EGL_SYNC_NATIVE_FENCE_ANDROID:
516         {
517             vk::SyncHelperNativeFence *syncHelper = new vk::SyncHelperNativeFence();
518             mSyncHelper                           = syncHelper;
519             int nativeFd = static_cast<EGLint>(mAttribs.getAsInt(EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
520                                                                  EGL_NO_NATIVE_FENCE_FD_ANDROID));
521             return angle::ToEGL(syncHelper->initializeWithFd(vk::GetImpl(context), nativeFd),
522                                 vk::GetImpl(display), EGL_BAD_ALLOC);
523         }
524         default:
525             UNREACHABLE();
526             return egl::Error(EGL_BAD_ALLOC);
527     }
528 }
529 
clientWait(const egl::Display * display,const gl::Context * context,EGLint flags,EGLTime timeout,EGLint * outResult)530 egl::Error EGLSyncVk::clientWait(const egl::Display *display,
531                                  const gl::Context *context,
532                                  EGLint flags,
533                                  EGLTime timeout,
534                                  EGLint *outResult)
535 {
536     ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
537 
538     bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
539     VkResult result;
540 
541     ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
542     if (mSyncHelper->clientWait(vk::GetImpl(display), contextVk, flush,
543                                 static_cast<uint64_t>(timeout), &result) == angle::Result::Stop)
544     {
545         return egl::Error(EGL_BAD_ALLOC);
546     }
547 
548     switch (result)
549     {
550         case VK_EVENT_SET:
551             // fall through.  EGL doesn't differentiate between event being already set, or set
552             // before timeout.
553         case VK_SUCCESS:
554             *outResult = EGL_CONDITION_SATISFIED_KHR;
555             return egl::NoError();
556 
557         case VK_TIMEOUT:
558             *outResult = EGL_TIMEOUT_EXPIRED_KHR;
559             return egl::NoError();
560 
561         default:
562             UNREACHABLE();
563             *outResult = EGL_FALSE;
564             return egl::Error(EGL_BAD_ALLOC);
565     }
566 }
567 
serverWait(const egl::Display * display,const gl::Context * context,EGLint flags)568 egl::Error EGLSyncVk::serverWait(const egl::Display *display,
569                                  const gl::Context *context,
570                                  EGLint flags)
571 {
572     // Server wait requires a valid bound context.
573     ASSERT(context);
574 
575     // No flags are currently implemented.
576     ASSERT(flags == 0);
577 
578     DisplayVk *displayVk = vk::GetImpl(display);
579     ContextVk *contextVk = vk::GetImpl(context);
580 
581     return angle::ToEGL(mSyncHelper->serverWait(contextVk), displayVk, EGL_BAD_ALLOC);
582 }
583 
getStatus(const egl::Display * display,EGLint * outStatus)584 egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
585 {
586     bool signaled = false;
587     if (mSyncHelper->getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
588     {
589         return egl::Error(EGL_BAD_ALLOC);
590     }
591 
592     *outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
593     return egl::NoError();
594 }
595 
dupNativeFenceFD(const egl::Display * display,EGLint * fdOut) const596 egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *fdOut) const
597 {
598     switch (mType)
599     {
600         case EGL_SYNC_NATIVE_FENCE_ANDROID:
601             return angle::ToEGL(mSyncHelper->dupNativeFenceFD(vk::GetImpl(display), fdOut),
602                                 vk::GetImpl(display), EGL_BAD_PARAMETER);
603         default:
604             return egl::EglBadDisplay();
605     }
606 }
607 
608 }  // namespace rx
609