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/RendererVk.h"
15 #include "libANGLE/renderer/vulkan/TextureVk.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 uint32_t rendererQueueFamilyIndex = contextVk->getRenderer()->getQueueFamilyIndex();
75
76 if (!bufferBarriers.empty())
77 {
78 // Perform a queue ownership transfer for each buffer.
79 for (gl::Buffer *buffer : bufferBarriers)
80 {
81 BufferVk *bufferVk = vk::GetImpl(buffer);
82 vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
83
84 vk::OutsideRenderPassCommandBuffer *commandBuffer;
85 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
86
87 // Queue ownership transfer.
88 bufferHelper.acquireFromExternal(contextVk, VK_QUEUE_FAMILY_EXTERNAL,
89 rendererQueueFamilyIndex, 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 = vk::GetImageLayoutFromGLImageLayout(textureBarrier.layout);
103
104 vk::OutsideRenderPassCommandBuffer *commandBuffer;
105 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
106
107 // Image should not be accessed while unowned. Emulated formats may have staged updates
108 // to clear the image after initialization.
109 ASSERT(!image.hasStagedUpdatesInAllocatedLevels() || image.hasEmulatedImageChannels());
110
111 // Queue ownership transfer and layout transition.
112 image.acquireFromExternal(contextVk, VK_QUEUE_FAMILY_EXTERNAL, rendererQueueFamilyIndex,
113 layout, commandBuffer);
114 }
115 }
116
117 contextVk->addWaitSemaphore(mSemaphore.getHandle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
118 return angle::Result::Continue;
119 }
120
signal(gl::Context * context,const gl::BufferBarrierVector & bufferBarriers,const gl::TextureBarrierVector & textureBarriers)121 angle::Result SemaphoreVk::signal(gl::Context *context,
122 const gl::BufferBarrierVector &bufferBarriers,
123 const gl::TextureBarrierVector &textureBarriers)
124 {
125 ContextVk *contextVk = vk::GetImpl(context);
126 RendererVk *renderer = contextVk->getRenderer();
127
128 uint32_t rendererQueueFamilyIndex = renderer->getQueueFamilyIndex();
129
130 if (!bufferBarriers.empty())
131 {
132 // Perform a queue ownership transfer for each buffer.
133 for (gl::Buffer *buffer : bufferBarriers)
134 {
135 BufferVk *bufferVk = vk::GetImpl(buffer);
136 vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
137
138 ANGLE_TRY(contextVk->onBufferReleaseToExternal(bufferHelper));
139 vk::OutsideRenderPassCommandBuffer *commandBuffer;
140 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
141
142 // Queue ownership transfer.
143 bufferHelper.releaseToExternal(contextVk, rendererQueueFamilyIndex,
144 VK_QUEUE_FAMILY_EXTERNAL, commandBuffer);
145 }
146 }
147
148 if (!textureBarriers.empty())
149 {
150 // Perform a queue ownership transfer for each texture. Additionally, transition the image
151 // to the requested layout.
152 for (const gl::TextureAndLayout &textureBarrier : textureBarriers)
153 {
154 TextureVk *textureVk = vk::GetImpl(textureBarrier.texture);
155 vk::ImageHelper &image = textureVk->getImage();
156 vk::ImageLayout layout = vk::GetImageLayoutFromGLImageLayout(textureBarrier.layout);
157
158 // Don't transition to Undefined layout. If external wants to transition the image away
159 // from Undefined after this operation, it's perfectly fine to keep the layout as is in
160 // ANGLE. Note that vk::ImageHelper doesn't expect transitions to Undefined.
161 if (layout == vk::ImageLayout::Undefined)
162 {
163 layout = image.getCurrentImageLayout();
164 }
165
166 ANGLE_TRY(textureVk->ensureImageInitialized(contextVk, ImageMipLevels::EnabledLevels));
167
168 ANGLE_TRY(contextVk->onImageReleaseToExternal(image));
169 vk::OutsideRenderPassCommandBuffer *commandBuffer;
170 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
171
172 // Queue ownership transfer and layout transition.
173 image.releaseToExternal(contextVk, rendererQueueFamilyIndex, VK_QUEUE_FAMILY_EXTERNAL,
174 layout, commandBuffer);
175 }
176 }
177
178 if (!bufferBarriers.empty() || !textureBarriers.empty())
179 {
180 // Create one global memory barrier to cover all barriers.
181 ANGLE_TRY(contextVk->syncExternalMemory());
182 }
183
184 ANGLE_TRY(contextVk->flushImpl(&mSemaphore, RenderPassClosureReason::ExternalSemaphoreSignal));
185
186 // The external has asked for the semaphore to be signaled. It will wait on this semaphore and
187 // so we must ensure that the above flush (resulting in vkQueueSubmit) has actually been
188 // submitted (as opposed to simply being scheduled as a task for another thread). Per the
189 // Vulkan spec:
190 //
191 // > ... when a semaphore wait operation is submitted to a queue:
192 // >
193 // > - A binary semaphore must be signaled, or have an associated semaphore signal operation
194 // > that is pending execution.
195 //
196 return renderer->ensureNoPendingWork(contextVk);
197 }
198
importOpaqueFd(ContextVk * contextVk,GLint fd)199 angle::Result SemaphoreVk::importOpaqueFd(ContextVk *contextVk, GLint fd)
200 {
201 RendererVk *renderer = contextVk->getRenderer();
202
203 if (!mSemaphore.valid())
204 {
205 mSemaphore.init(renderer->getDevice());
206 }
207
208 ASSERT(mSemaphore.valid());
209
210 VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo = {};
211 importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
212 importSemaphoreFdInfo.semaphore = mSemaphore.getHandle();
213 importSemaphoreFdInfo.flags = 0;
214 importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
215 importSemaphoreFdInfo.fd = fd;
216
217 ANGLE_VK_TRY(contextVk, vkImportSemaphoreFdKHR(renderer->getDevice(), &importSemaphoreFdInfo));
218
219 return angle::Result::Continue;
220 }
221
importZirconEvent(ContextVk * contextVk,GLuint handle)222 angle::Result SemaphoreVk::importZirconEvent(ContextVk *contextVk, GLuint handle)
223 {
224 RendererVk *renderer = contextVk->getRenderer();
225
226 if (!mSemaphore.valid())
227 {
228 mSemaphore.init(renderer->getDevice());
229 }
230
231 ASSERT(mSemaphore.valid());
232
233 VkImportSemaphoreZirconHandleInfoFUCHSIA importSemaphoreZirconHandleInfo = {};
234 importSemaphoreZirconHandleInfo.sType =
235 VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA;
236 importSemaphoreZirconHandleInfo.semaphore = mSemaphore.getHandle();
237 importSemaphoreZirconHandleInfo.flags = 0;
238 importSemaphoreZirconHandleInfo.handleType =
239 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA;
240 importSemaphoreZirconHandleInfo.zirconHandle = handle;
241
242 // TODO(spang): Add vkImportSemaphoreZirconHandleFUCHSIA to volk.
243 static PFN_vkImportSemaphoreZirconHandleFUCHSIA vkImportSemaphoreZirconHandleFUCHSIA =
244 reinterpret_cast<PFN_vkImportSemaphoreZirconHandleFUCHSIA>(
245 vkGetInstanceProcAddr(renderer->getInstance(), "vkImportSemaphoreZirconHandleFUCHSIA"));
246
247 ANGLE_VK_TRY(contextVk, vkImportSemaphoreZirconHandleFUCHSIA(renderer->getDevice(),
248 &importSemaphoreZirconHandleInfo));
249
250 return angle::Result::Continue;
251 }
252
253 } // namespace rx
254