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