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