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 {
24 mUse.init();
25 }
26
~SyncHelper()27 SyncHelper::~SyncHelper()
28 {
29 mUse.release();
30 }
31
releaseToRenderer(RendererVk * renderer)32 void SyncHelper::releaseToRenderer(RendererVk *renderer)
33 {
34 renderer->collectGarbageAndReinit(&mUse, &mEvent);
35 mFence.reset(renderer->getDevice());
36 }
37
initialize(ContextVk * contextVk)38 angle::Result SyncHelper::initialize(ContextVk *contextVk)
39 {
40 ASSERT(!mEvent.valid());
41
42 RendererVk *renderer = contextVk->getRenderer();
43 VkDevice device = renderer->getDevice();
44
45 VkEventCreateInfo eventCreateInfo = {};
46 eventCreateInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
47 eventCreateInfo.flags = 0;
48
49 DeviceScoped<Event> event(device);
50 ANGLE_VK_TRY(contextVk, event.get().init(device, eventCreateInfo));
51 ANGLE_TRY(contextVk->getNextSubmitFence(&mFence));
52
53 mEvent = event.release();
54
55 vk::PrimaryCommandBuffer *primary;
56 ANGLE_TRY(contextVk->flushAndGetPrimaryCommandBuffer(&primary));
57 primary->setEvent(mEvent.getHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
58 contextVk->getResourceUseList().add(mUse);
59
60 return angle::Result::Continue;
61 }
62
clientWait(Context * context,ContextVk * contextVk,bool flushCommands,uint64_t timeout,VkResult * outResult)63 angle::Result SyncHelper::clientWait(Context *context,
64 ContextVk *contextVk,
65 bool flushCommands,
66 uint64_t timeout,
67 VkResult *outResult)
68 {
69 RendererVk *renderer = context->getRenderer();
70
71 // If the event is already set, don't wait
72 bool alreadySignaled = false;
73 ANGLE_TRY(getStatus(context, &alreadySignaled));
74 if (alreadySignaled)
75 {
76 *outResult = VK_EVENT_SET;
77 return angle::Result::Continue;
78 }
79
80 // If timeout is zero, there's no need to wait, so return timeout already.
81 if (timeout == 0)
82 {
83 *outResult = VK_TIMEOUT;
84 return angle::Result::Continue;
85 }
86
87 if (flushCommands && contextVk)
88 {
89 ANGLE_TRY(contextVk->flushImpl(nullptr));
90 }
91
92 // Wait on the fence that's expected to be signaled on the first vkQueueSubmit after
93 // `initialize` was called. The first fence is the fence created to signal this sync.
94 ASSERT(mFence.get().valid());
95 VkResult status = mFence.get().wait(renderer->getDevice(), timeout);
96
97 // Check for errors, but don't consider timeout as such.
98 if (status != VK_TIMEOUT)
99 {
100 ANGLE_VK_TRY(context, status);
101 }
102
103 *outResult = status;
104 return angle::Result::Continue;
105 }
106
serverWait(ContextVk * contextVk)107 angle::Result SyncHelper::serverWait(ContextVk *contextVk)
108 {
109 vk::PrimaryCommandBuffer *primary;
110 ANGLE_TRY(contextVk->flushAndGetPrimaryCommandBuffer(&primary));
111 primary->waitEvents(1, mEvent.ptr(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
112 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, nullptr, 0, nullptr, 0, nullptr);
113 contextVk->getResourceUseList().add(mUse);
114 return angle::Result::Continue;
115 }
116
getStatus(Context * context,bool * signaled)117 angle::Result SyncHelper::getStatus(Context *context, bool *signaled)
118 {
119 VkResult result = mEvent.getStatus(context->getDevice());
120 if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
121 {
122 ANGLE_VK_TRY(context, result);
123 }
124 *signaled = result == VK_EVENT_SET;
125 return angle::Result::Continue;
126 }
127 } // namespace vk
128
SyncVk()129 SyncVk::SyncVk() : SyncImpl() {}
130
~SyncVk()131 SyncVk::~SyncVk() {}
132
onDestroy(const gl::Context * context)133 void SyncVk::onDestroy(const gl::Context *context)
134 {
135 mFenceSync.releaseToRenderer(vk::GetImpl(context)->getRenderer());
136 }
137
set(const gl::Context * context,GLenum condition,GLbitfield flags)138 angle::Result SyncVk::set(const gl::Context *context, GLenum condition, GLbitfield flags)
139 {
140 ASSERT(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
141 ASSERT(flags == 0);
142
143 return mFenceSync.initialize(vk::GetImpl(context));
144 }
145
clientWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout,GLenum * outResult)146 angle::Result SyncVk::clientWait(const gl::Context *context,
147 GLbitfield flags,
148 GLuint64 timeout,
149 GLenum *outResult)
150 {
151 ContextVk *contextVk = vk::GetImpl(context);
152
153 ASSERT((flags & ~GL_SYNC_FLUSH_COMMANDS_BIT) == 0);
154
155 bool flush = (flags & GL_SYNC_FLUSH_COMMANDS_BIT) != 0;
156 VkResult result;
157
158 ANGLE_TRY(mFenceSync.clientWait(contextVk, contextVk, flush, static_cast<uint64_t>(timeout),
159 &result));
160
161 switch (result)
162 {
163 case VK_EVENT_SET:
164 *outResult = GL_ALREADY_SIGNALED;
165 return angle::Result::Continue;
166
167 case VK_SUCCESS:
168 *outResult = GL_CONDITION_SATISFIED;
169 return angle::Result::Continue;
170
171 case VK_TIMEOUT:
172 *outResult = GL_TIMEOUT_EXPIRED;
173 return angle::Result::Incomplete;
174
175 default:
176 UNREACHABLE();
177 *outResult = GL_WAIT_FAILED;
178 return angle::Result::Stop;
179 }
180 }
181
serverWait(const gl::Context * context,GLbitfield flags,GLuint64 timeout)182 angle::Result SyncVk::serverWait(const gl::Context *context, GLbitfield flags, GLuint64 timeout)
183 {
184 ASSERT(flags == 0);
185 ASSERT(timeout == GL_TIMEOUT_IGNORED);
186
187 ContextVk *contextVk = vk::GetImpl(context);
188 return mFenceSync.serverWait(contextVk);
189 }
190
getStatus(const gl::Context * context,GLint * outResult)191 angle::Result SyncVk::getStatus(const gl::Context *context, GLint *outResult)
192 {
193 bool signaled = false;
194 ANGLE_TRY(mFenceSync.getStatus(vk::GetImpl(context), &signaled));
195
196 *outResult = signaled ? GL_SIGNALED : GL_UNSIGNALED;
197 return angle::Result::Continue;
198 }
199
EGLSyncVk(const egl::AttributeMap & attribs)200 EGLSyncVk::EGLSyncVk(const egl::AttributeMap &attribs) : EGLSyncImpl()
201 {
202 ASSERT(attribs.isEmpty());
203 }
204
~EGLSyncVk()205 EGLSyncVk::~EGLSyncVk() {}
206
onDestroy(const egl::Display * display)207 void EGLSyncVk::onDestroy(const egl::Display *display)
208 {
209 mFenceSync.releaseToRenderer(vk::GetImpl(display)->getRenderer());
210 }
211
initialize(const egl::Display * display,const gl::Context * context,EGLenum type)212 egl::Error EGLSyncVk::initialize(const egl::Display *display,
213 const gl::Context *context,
214 EGLenum type)
215 {
216 ASSERT(type == EGL_SYNC_FENCE_KHR);
217 ASSERT(context != nullptr);
218
219 if (mFenceSync.initialize(vk::GetImpl(context)) == angle::Result::Stop)
220 {
221 return egl::Error(EGL_BAD_ALLOC, "eglCreateSyncKHR failed to create sync object");
222 }
223
224 return egl::NoError();
225 }
226
clientWait(const egl::Display * display,const gl::Context * context,EGLint flags,EGLTime timeout,EGLint * outResult)227 egl::Error EGLSyncVk::clientWait(const egl::Display *display,
228 const gl::Context *context,
229 EGLint flags,
230 EGLTime timeout,
231 EGLint *outResult)
232 {
233 ASSERT((flags & ~EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) == 0);
234
235 bool flush = (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) != 0;
236 VkResult result;
237
238 ContextVk *contextVk = context ? vk::GetImpl(context) : nullptr;
239 if (mFenceSync.clientWait(vk::GetImpl(display), contextVk, flush,
240 static_cast<uint64_t>(timeout), &result) == angle::Result::Stop)
241 {
242 return egl::Error(EGL_BAD_ALLOC);
243 }
244
245 switch (result)
246 {
247 case VK_EVENT_SET:
248 // fall through. EGL doesn't differentiate between event being already set, or set
249 // before timeout.
250 case VK_SUCCESS:
251 *outResult = EGL_CONDITION_SATISFIED_KHR;
252 return egl::NoError();
253
254 case VK_TIMEOUT:
255 *outResult = EGL_TIMEOUT_EXPIRED_KHR;
256 return egl::NoError();
257
258 default:
259 UNREACHABLE();
260 *outResult = EGL_FALSE;
261 return egl::Error(EGL_BAD_ALLOC);
262 }
263 }
264
serverWait(const egl::Display * display,const gl::Context * context,EGLint flags)265 egl::Error EGLSyncVk::serverWait(const egl::Display *display,
266 const gl::Context *context,
267 EGLint flags)
268 {
269 // Server wait requires a valid bound context.
270 ASSERT(context);
271
272 // No flags are currently implemented.
273 ASSERT(flags == 0);
274
275 DisplayVk *displayVk = vk::GetImpl(display);
276 ContextVk *contextVk = vk::GetImpl(context);
277
278 return angle::ToEGL(mFenceSync.serverWait(contextVk), displayVk, EGL_BAD_ALLOC);
279 }
280
getStatus(const egl::Display * display,EGLint * outStatus)281 egl::Error EGLSyncVk::getStatus(const egl::Display *display, EGLint *outStatus)
282 {
283 bool signaled = false;
284 if (mFenceSync.getStatus(vk::GetImpl(display), &signaled) == angle::Result::Stop)
285 {
286 return egl::Error(EGL_BAD_ALLOC);
287 }
288
289 *outStatus = signaled ? EGL_SIGNALED_KHR : EGL_UNSIGNALED_KHR;
290 return egl::NoError();
291 }
292
dupNativeFenceFD(const egl::Display * display,EGLint * result) const293 egl::Error EGLSyncVk::dupNativeFenceFD(const egl::Display *display, EGLint *result) const
294 {
295 UNREACHABLE();
296 return egl::EglBadDisplay();
297 }
298
299 } // namespace rx
300