| // Copyright 2019 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // SemaphoreVk.cpp: Defines the class interface for SemaphoreVk, implementing |
| // SemaphoreImpl. |
| |
| #include "libANGLE/renderer/vulkan/SemaphoreVk.h" |
| |
| #include "common/debug.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/renderer/vulkan/BufferVk.h" |
| #include "libANGLE/renderer/vulkan/ContextVk.h" |
| #include "libANGLE/renderer/vulkan/RendererVk.h" |
| #include "libANGLE/renderer/vulkan/TextureVk.h" |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| vk::ImageLayout GetVulkanImageLayout(GLenum layout) |
| { |
| switch (layout) |
| { |
| case GL_NONE: |
| return vk::ImageLayout::Undefined; |
| case GL_LAYOUT_GENERAL_EXT: |
| return vk::ImageLayout::ExternalShadersWrite; |
| case GL_LAYOUT_COLOR_ATTACHMENT_EXT: |
| return vk::ImageLayout::ColorAttachment; |
| case GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT: |
| case GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT: |
| case GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT: |
| case GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT: |
| // Note: once VK_KHR_separate_depth_stencil_layouts becomes core or ubiquitous, we |
| // should optimize depth/stencil image layout transitions to only be performed on the |
| // aspect that needs transition. In that case, these four layouts can be distinguished |
| // and optimized. Note that the exact equivalent of these layouts are specified in |
| // VK_KHR_maintenance2, which are also usable, granted we transition the pair of |
| // depth/stencil layouts accordingly elsewhere in ANGLE. |
| return vk::ImageLayout::DepthStencilAttachment; |
| case GL_LAYOUT_SHADER_READ_ONLY_EXT: |
| return vk::ImageLayout::ExternalShadersReadOnly; |
| case GL_LAYOUT_TRANSFER_SRC_EXT: |
| return vk::ImageLayout::TransferSrc; |
| case GL_LAYOUT_TRANSFER_DST_EXT: |
| return vk::ImageLayout::TransferDst; |
| default: |
| UNREACHABLE(); |
| return vk::ImageLayout::Undefined; |
| } |
| } |
| } // anonymous namespace |
| |
| SemaphoreVk::SemaphoreVk() = default; |
| |
| SemaphoreVk::~SemaphoreVk() = default; |
| |
| void SemaphoreVk::onDestroy(const gl::Context *context) |
| { |
| ContextVk *contextVk = vk::GetImpl(context); |
| contextVk->addGarbage(&mSemaphore); |
| } |
| |
| angle::Result SemaphoreVk::importFd(gl::Context *context, gl::HandleType handleType, GLint fd) |
| { |
| switch (handleType) |
| { |
| case gl::HandleType::OpaqueFd: |
| return importOpaqueFd(context, fd); |
| |
| default: |
| ANGLE_VK_UNREACHABLE(vk::GetImpl(context)); |
| return angle::Result::Stop; |
| } |
| } |
| |
| angle::Result SemaphoreVk::wait(gl::Context *context, |
| const gl::BufferBarrierVector &bufferBarriers, |
| const gl::TextureBarrierVector &textureBarriers) |
| { |
| ContextVk *contextVk = vk::GetImpl(context); |
| |
| if (!bufferBarriers.empty() || !textureBarriers.empty()) |
| { |
| // Create one global memory barrier to cover all barriers. |
| contextVk->getCommandGraph()->syncExternalMemory(); |
| } |
| |
| uint32_t rendererQueueFamilyIndex = contextVk->getRenderer()->getQueueFamilyIndex(); |
| |
| if (!bufferBarriers.empty()) |
| { |
| // Perform a queue ownership transfer for each buffer. |
| for (gl::Buffer *buffer : bufferBarriers) |
| { |
| BufferVk *bufferVk = vk::GetImpl(buffer); |
| vk::BufferHelper &bufferHelper = bufferVk->getBuffer(); |
| |
| // If there were GL commands using this buffer prior to this call, that's a |
| // synchronization error on behalf of the program. |
| ASSERT(!bufferHelper.hasRecordedCommands()); |
| |
| vk::CommandBuffer *queueChange; |
| ANGLE_TRY(bufferHelper.recordCommands(contextVk, &queueChange)); |
| |
| // Queue ownership transfer. |
| bufferHelper.changeQueue(rendererQueueFamilyIndex, queueChange); |
| } |
| } |
| |
| if (!textureBarriers.empty()) |
| { |
| // Perform a queue ownership transfer for each texture. Additionally, we are being |
| // informed that the layout of the image has been externally transitioned, so we need to |
| // update our internal state tracking. |
| for (const gl::TextureAndLayout &textureAndLayout : textureBarriers) |
| { |
| TextureVk *textureVk = vk::GetImpl(textureAndLayout.texture); |
| vk::ImageHelper &image = textureVk->getImage(); |
| vk::ImageLayout layout = GetVulkanImageLayout(textureAndLayout.layout); |
| |
| // If there were GL commands using this image prior to this call, that's a |
| // synchronization error on behalf of the program. |
| ASSERT(!image.hasRecordedCommands()); |
| |
| // Inform the image that the layout has been externally changed. |
| image.onExternalLayoutChange(layout); |
| |
| vk::CommandBuffer *queueChange; |
| ANGLE_TRY(image.recordCommands(contextVk, &queueChange)); |
| |
| // Queue ownership transfer. |
| image.changeLayoutAndQueue(image.getAspectFlags(), layout, rendererQueueFamilyIndex, |
| queueChange); |
| } |
| } |
| |
| contextVk->insertWaitSemaphore(&mSemaphore); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result SemaphoreVk::signal(gl::Context *context, |
| const gl::BufferBarrierVector &bufferBarriers, |
| const gl::TextureBarrierVector &textureBarriers) |
| { |
| ContextVk *contextVk = vk::GetImpl(context); |
| |
| if (!bufferBarriers.empty()) |
| { |
| // Perform a queue ownership transfer for each buffer. |
| for (gl::Buffer *buffer : bufferBarriers) |
| { |
| BufferVk *bufferVk = vk::GetImpl(buffer); |
| vk::BufferHelper &bufferHelper = bufferVk->getBuffer(); |
| |
| vk::CommandBuffer *queueChange; |
| ANGLE_TRY(bufferHelper.recordCommands(contextVk, &queueChange)); |
| |
| // Queue ownership transfer. |
| bufferHelper.changeQueue(VK_QUEUE_FAMILY_EXTERNAL, queueChange); |
| } |
| } |
| |
| if (!textureBarriers.empty()) |
| { |
| // Perform a queue ownership transfer for each texture. Additionally, transition the image |
| // to the requested layout. |
| for (const gl::TextureAndLayout &textureAndLayout : textureBarriers) |
| { |
| TextureVk *textureVk = vk::GetImpl(textureAndLayout.texture); |
| vk::ImageHelper &image = textureVk->getImage(); |
| vk::ImageLayout layout = GetVulkanImageLayout(textureAndLayout.layout); |
| |
| // Don't transition to Undefined layout. If external wants to transition the image away |
| // from Undefined after this operation, it's perfectly fine to keep the layout as is in |
| // ANGLE. Note that vk::ImageHelper doesn't expect transitions to Undefined. |
| if (layout == vk::ImageLayout::Undefined) |
| { |
| layout = image.getCurrentImageLayout(); |
| } |
| |
| vk::CommandBuffer *layoutChange; |
| ANGLE_TRY(image.recordCommands(contextVk, &layoutChange)); |
| |
| // Queue ownership transfer and layout transition. |
| image.changeLayoutAndQueue(image.getAspectFlags(), layout, VK_QUEUE_FAMILY_EXTERNAL, |
| layoutChange); |
| } |
| } |
| |
| if (!bufferBarriers.empty() || !textureBarriers.empty()) |
| { |
| // Create one global memory barrier to cover all barriers. |
| contextVk->getCommandGraph()->syncExternalMemory(); |
| } |
| |
| return contextVk->flushImpl(&mSemaphore); |
| } |
| |
| angle::Result SemaphoreVk::importOpaqueFd(gl::Context *context, GLint fd) |
| { |
| ContextVk *contextVk = vk::GetImpl(context); |
| RendererVk *renderer = contextVk->getRenderer(); |
| |
| if (!mSemaphore.valid()) |
| { |
| mSemaphore.init(renderer->getDevice()); |
| } |
| |
| ASSERT(mSemaphore.valid()); |
| |
| VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo = {}; |
| importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; |
| importSemaphoreFdInfo.semaphore = mSemaphore.getHandle(); |
| importSemaphoreFdInfo.flags = 0; |
| importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; |
| importSemaphoreFdInfo.fd = fd; |
| |
| ANGLE_VK_TRY(contextVk, vkImportSemaphoreFdKHR(renderer->getDevice(), &importSemaphoreFdInfo)); |
| |
| return angle::Result::Continue; |
| } |
| |
| } // namespace rx |