1 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // SemaphoreVk.cpp: Defines the class interface for SemaphoreVk, implementing
6 // SemaphoreImpl.
7
8 #include "libANGLE/renderer/vulkan/SemaphoreVk.h"
9
10 #include "common/debug.h"
11 #include "libANGLE/Context.h"
12 #include "libANGLE/renderer/vulkan/BufferVk.h"
13 #include "libANGLE/renderer/vulkan/ContextVk.h"
14 #include "libANGLE/renderer/vulkan/TextureVk.h"
15 #include "libANGLE/renderer/vulkan/vk_renderer.h"
16
17 namespace rx
18 {
19
20 SemaphoreVk::SemaphoreVk() = default;
21
22 SemaphoreVk::~SemaphoreVk() = default;
23
onDestroy(const gl::Context * context)24 void SemaphoreVk::onDestroy(const gl::Context *context)
25 {
26 ContextVk *contextVk = vk::GetImpl(context);
27 contextVk->addGarbage(&mSemaphore);
28 }
29
importFd(gl::Context * context,gl::HandleType handleType,GLint fd)30 angle::Result SemaphoreVk::importFd(gl::Context *context, gl::HandleType handleType, GLint fd)
31 {
32 ContextVk *contextVk = vk::GetImpl(context);
33
34 switch (handleType)
35 {
36 case gl::HandleType::OpaqueFd:
37 return importOpaqueFd(contextVk, fd);
38
39 default:
40 ANGLE_VK_UNREACHABLE(contextVk);
41 return angle::Result::Stop;
42 }
43 }
44
importZirconHandle(gl::Context * context,gl::HandleType handleType,GLuint handle)45 angle::Result SemaphoreVk::importZirconHandle(gl::Context *context,
46 gl::HandleType handleType,
47 GLuint handle)
48 {
49 ContextVk *contextVk = vk::GetImpl(context);
50
51 switch (handleType)
52 {
53 case gl::HandleType::ZirconEvent:
54 return importZirconEvent(contextVk, handle);
55
56 default:
57 ANGLE_VK_UNREACHABLE(contextVk);
58 return angle::Result::Stop;
59 }
60 }
61
wait(gl::Context * context,const gl::BufferBarrierVector & bufferBarriers,const gl::TextureBarrierVector & textureBarriers)62 angle::Result SemaphoreVk::wait(gl::Context *context,
63 const gl::BufferBarrierVector &bufferBarriers,
64 const gl::TextureBarrierVector &textureBarriers)
65 {
66 ContextVk *contextVk = vk::GetImpl(context);
67
68 if (!bufferBarriers.empty() || !textureBarriers.empty())
69 {
70 // Create one global memory barrier to cover all barriers.
71 ANGLE_TRY(contextVk->syncExternalMemory());
72 }
73
74 if (!bufferBarriers.empty())
75 {
76 // Perform a queue ownership transfer for each buffer.
77 for (gl::Buffer *buffer : bufferBarriers)
78 {
79 BufferVk *bufferVk = vk::GetImpl(buffer);
80 vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
81
82 vk::CommandBufferAccess access;
83 vk::OutsideRenderPassCommandBuffer *commandBuffer;
84 access.onBufferExternalAcquireRelease(&bufferHelper);
85 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
86
87 // Queue ownership transfer.
88 bufferHelper.acquireFromExternal(vk::kExternalDeviceQueueIndex,
89 contextVk->getDeviceQueueIndex(), commandBuffer);
90 }
91 }
92
93 if (!textureBarriers.empty())
94 {
95 // Perform a queue ownership transfer for each texture. Additionally, we are being
96 // informed that the layout of the image has been externally transitioned, so we need to
97 // update our internal state tracking.
98 for (const gl::TextureAndLayout &textureBarrier : textureBarriers)
99 {
100 TextureVk *textureVk = vk::GetImpl(textureBarrier.texture);
101 vk::ImageHelper &image = textureVk->getImage();
102 vk::ImageLayout layout =
103 vk::GetImageLayoutFromGLImageLayout(contextVk, textureBarrier.layout);
104
105 vk::CommandBufferAccess access;
106 vk::OutsideRenderPassCommandBuffer *commandBuffer;
107 access.onExternalAcquireRelease(&image);
108 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
109
110 // Image should not be accessed while unowned. Emulated formats may have staged updates
111 // to clear the image after initialization.
112 ASSERT(!image.hasStagedUpdatesInAllocatedLevels() || image.hasEmulatedImageChannels());
113
114 // Queue ownership transfer and layout transition.
115 image.acquireFromExternal(contextVk, vk::kExternalDeviceQueueIndex,
116 contextVk->getDeviceQueueIndex(), layout, commandBuffer);
117 }
118 }
119
120 contextVk->addWaitSemaphore(mSemaphore.getHandle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
121 return angle::Result::Continue;
122 }
123
signal(gl::Context * context,const gl::BufferBarrierVector & bufferBarriers,const gl::TextureBarrierVector & textureBarriers)124 angle::Result SemaphoreVk::signal(gl::Context *context,
125 const gl::BufferBarrierVector &bufferBarriers,
126 const gl::TextureBarrierVector &textureBarriers)
127 {
128 ContextVk *contextVk = vk::GetImpl(context);
129 vk::Renderer *renderer = contextVk->getRenderer();
130
131 if (!bufferBarriers.empty())
132 {
133 // Perform a queue ownership transfer for each buffer.
134 for (gl::Buffer *buffer : bufferBarriers)
135 {
136 BufferVk *bufferVk = vk::GetImpl(buffer);
137 vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
138
139 ANGLE_TRY(contextVk->onBufferReleaseToExternal(bufferHelper));
140 vk::CommandBufferAccess access;
141 vk::OutsideRenderPassCommandBuffer *commandBuffer;
142 access.onBufferExternalAcquireRelease(&bufferHelper);
143 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
144
145 // Queue ownership transfer.
146 bufferHelper.releaseToExternal(vk::kExternalDeviceQueueIndex, commandBuffer);
147 }
148 }
149
150 if (!textureBarriers.empty())
151 {
152 // Perform a queue ownership transfer for each texture. Additionally, transition the image
153 // to the requested layout.
154 for (const gl::TextureAndLayout &textureBarrier : textureBarriers)
155 {
156 TextureVk *textureVk = vk::GetImpl(textureBarrier.texture);
157 vk::ImageHelper &image = textureVk->getImage();
158 vk::ImageLayout layout =
159 vk::GetImageLayoutFromGLImageLayout(contextVk, textureBarrier.layout);
160
161 // Don't transition to Undefined layout. If external wants to transition the image away
162 // from Undefined after this operation, it's perfectly fine to keep the layout as is in
163 // ANGLE. Note that vk::ImageHelper doesn't expect transitions to Undefined.
164 if (layout == vk::ImageLayout::Undefined)
165 {
166 layout = image.getCurrentImageLayout();
167 }
168
169 ANGLE_TRY(textureVk->ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
170
171 ANGLE_TRY(contextVk->onImageReleaseToExternal(image));
172 vk::CommandBufferAccess access;
173 vk::OutsideRenderPassCommandBuffer *commandBuffer;
174 access.onExternalAcquireRelease(&image);
175 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
176
177 // Queue ownership transfer and layout transition.
178 image.releaseToExternal(contextVk, vk::kExternalDeviceQueueIndex, layout,
179 commandBuffer);
180 }
181 }
182
183 if (!bufferBarriers.empty() || !textureBarriers.empty())
184 {
185 // Create one global memory barrier to cover all barriers.
186 ANGLE_TRY(contextVk->syncExternalMemory());
187 }
188
189 ANGLE_TRY(contextVk->flushAndSubmitCommands(&mSemaphore, nullptr,
190 RenderPassClosureReason::ExternalSemaphoreSignal));
191
192 // The external has asked for the semaphore to be signaled. It will wait on this semaphore and
193 // so we must ensure that the above flush (resulting in vkQueueSubmit) has actually been
194 // submitted (as opposed to simply being scheduled as a task for another thread). Per the
195 // Vulkan spec:
196 //
197 // > ... when a semaphore wait operation is submitted to a queue:
198 // >
199 // > - A binary semaphore must be signaled, or have an associated semaphore signal operation
200 // > that is pending execution.
201 //
202 return renderer->waitForQueueSerialToBeSubmittedToDevice(
203 contextVk, contextVk->getLastSubmittedQueueSerial());
204 }
205
importOpaqueFd(ContextVk * contextVk,GLint fd)206 angle::Result SemaphoreVk::importOpaqueFd(ContextVk *contextVk, GLint fd)
207 {
208 vk::Renderer *renderer = contextVk->getRenderer();
209
210 if (!mSemaphore.valid())
211 {
212 mSemaphore.init(renderer->getDevice());
213 }
214
215 ASSERT(mSemaphore.valid());
216
217 VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo = {};
218 importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
219 importSemaphoreFdInfo.semaphore = mSemaphore.getHandle();
220 importSemaphoreFdInfo.flags = 0;
221 importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
222 importSemaphoreFdInfo.fd = fd;
223
224 ANGLE_VK_TRY(contextVk, vkImportSemaphoreFdKHR(renderer->getDevice(), &importSemaphoreFdInfo));
225
226 return angle::Result::Continue;
227 }
228
importZirconEvent(ContextVk * contextVk,GLuint handle)229 angle::Result SemaphoreVk::importZirconEvent(ContextVk *contextVk, GLuint handle)
230 {
231 vk::Renderer *renderer = contextVk->getRenderer();
232
233 if (!mSemaphore.valid())
234 {
235 mSemaphore.init(renderer->getDevice());
236 }
237
238 ASSERT(mSemaphore.valid());
239
240 VkImportSemaphoreZirconHandleInfoFUCHSIA importSemaphoreZirconHandleInfo = {};
241 importSemaphoreZirconHandleInfo.sType =
242 VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA;
243 importSemaphoreZirconHandleInfo.semaphore = mSemaphore.getHandle();
244 importSemaphoreZirconHandleInfo.flags = 0;
245 importSemaphoreZirconHandleInfo.handleType =
246 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
247 importSemaphoreZirconHandleInfo.zirconHandle = handle;
248
249 // TODO(spang): Add vkImportSemaphoreZirconHandleFUCHSIA to volk.
250 static PFN_vkImportSemaphoreZirconHandleFUCHSIA vkImportSemaphoreZirconHandleFUCHSIA =
251 reinterpret_cast<PFN_vkImportSemaphoreZirconHandleFUCHSIA>(
252 vkGetInstanceProcAddr(renderer->getInstance(), "vkImportSemaphoreZirconHandleFUCHSIA"));
253
254 ANGLE_VK_TRY(contextVk, vkImportSemaphoreZirconHandleFUCHSIA(renderer->getDevice(),
255 &importSemaphoreZirconHandleInfo));
256
257 return angle::Result::Continue;
258 }
259
260 } // namespace rx
261