| // |
| // 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. |
| // |
| // OverlayVk.cpp: |
| // Implements the OverlayVk class. |
| // |
| |
| #include "libANGLE/renderer/vulkan/OverlayVk.h" |
| |
| #include "common/system_utils.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Overlay_font_autogen.h" |
| #include "libANGLE/renderer/vulkan/ContextVk.h" |
| |
| #include <numeric> |
| |
| namespace rx |
| { |
| OverlayVk::OverlayVk(const gl::OverlayState &state) |
| : OverlayImpl(state), |
| mSupportsSubgroupBallot(false), |
| mSupportsSubgroupArithmetic(false), |
| mRefreshCulledWidgets(false) |
| {} |
| OverlayVk::~OverlayVk() = default; |
| |
| angle::Result OverlayVk::init(const gl::Context *context) |
| { |
| ContextVk *contextVk = vk::GetImpl(context); |
| RendererVk *rendererVk = contextVk->getRenderer(); |
| |
| const VkPhysicalDeviceSubgroupProperties &subgroupProperties = |
| rendererVk->getPhysicalDeviceSubgroupProperties(); |
| uint32_t subgroupSize = subgroupProperties.subgroupSize; |
| |
| // Currently, only subgroup sizes 32 and 64 are supported. |
| if (subgroupSize != 32 && subgroupSize != 64) |
| { |
| return angle::Result::Continue; |
| } |
| |
| mSubgroupSize[0] = 8; |
| mSubgroupSize[1] = subgroupSize / 8; |
| |
| constexpr VkSubgroupFeatureFlags kSubgroupBallotOperations = |
| VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT; |
| constexpr VkSubgroupFeatureFlags kSubgroupArithmeticOperations = |
| VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT; |
| |
| // If not all operations used in the shaders are supported, disable the overlay. |
| if ((subgroupProperties.supportedOperations & kSubgroupBallotOperations) == |
| kSubgroupBallotOperations) |
| { |
| mSupportsSubgroupBallot = true; |
| } |
| else if ((subgroupProperties.supportedOperations & kSubgroupArithmeticOperations) == |
| kSubgroupArithmeticOperations) |
| { |
| mSupportsSubgroupArithmetic = true; |
| } |
| |
| ANGLE_TRY(createFont(contextVk)); |
| |
| mRefreshCulledWidgets = true; |
| |
| return contextVk->flushImpl(nullptr); |
| } |
| |
| void OverlayVk::onDestroy(const gl::Context *context) |
| { |
| VkDevice device = vk::GetImpl(context)->getDevice(); |
| |
| mCulledWidgets.destroy(device); |
| mCulledWidgetsView.destroy(device); |
| |
| mFontImage.destroy(device); |
| mFontImageView.destroy(device); |
| } |
| |
| angle::Result OverlayVk::createFont(ContextVk *contextVk) |
| { |
| RendererVk *renderer = contextVk->getRenderer(); |
| |
| // Create a buffer to stage font data upload. |
| VkBufferCreateInfo bufferCreateInfo = {}; |
| bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| bufferCreateInfo.size = |
| gl::overlay::kFontCount * gl::overlay::kFontImageWidth * gl::overlay::kFontImageHeight; |
| bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; |
| bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| |
| vk::RendererScoped<vk::BufferHelper> fontDataBuffer(renderer); |
| |
| ANGLE_TRY(fontDataBuffer.get().init(contextVk, bufferCreateInfo, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); |
| |
| uint8_t *fontData; |
| ANGLE_TRY(fontDataBuffer.get().map(contextVk, &fontData)); |
| |
| mState.initFontData(fontData); |
| |
| ANGLE_TRY(fontDataBuffer.get().flush(contextVk, 0, fontDataBuffer.get().getSize())); |
| fontDataBuffer.get().unmap(contextVk->getDevice()); |
| |
| fontDataBuffer.get().onExternalWrite(VK_ACCESS_HOST_WRITE_BIT); |
| |
| // Create the font image. |
| ANGLE_TRY( |
| mFontImage.init(contextVk, gl::TextureType::_2D, |
| VkExtent3D{gl::overlay::kFontImageWidth, gl::overlay::kFontImageHeight, 1}, |
| renderer->getFormat(angle::FormatID::R8_UNORM), 1, |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 0, 0, 1, |
| gl::overlay::kFontCount)); |
| ANGLE_TRY(mFontImage.initMemory(contextVk, renderer->getMemoryProperties(), |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); |
| ANGLE_TRY(mFontImage.initImageView(contextVk, gl::TextureType::_2DArray, |
| VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(), |
| &mFontImageView, 0, 1)); |
| |
| // Copy font data from staging buffer. |
| vk::CommandBuffer *fontDataUpload; |
| ANGLE_TRY(mFontImage.recordCommands(contextVk, &fontDataUpload)); |
| |
| fontDataBuffer.get().onRead(contextVk, &mFontImage, VK_ACCESS_TRANSFER_READ_BIT); |
| |
| mFontImage.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferDst, |
| fontDataUpload); |
| |
| VkBufferImageCopy copy = {}; |
| copy.bufferRowLength = gl::overlay::kFontImageWidth; |
| copy.bufferImageHeight = gl::overlay::kFontImageHeight; |
| copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| copy.imageSubresource.layerCount = gl::overlay::kFontCount; |
| copy.imageExtent.width = gl::overlay::kFontImageWidth; |
| copy.imageExtent.height = gl::overlay::kFontImageHeight; |
| copy.imageExtent.depth = 1; |
| |
| fontDataUpload->copyBufferToImage(fontDataBuffer.get().getBuffer().getHandle(), |
| mFontImage.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| 1, ©); |
| |
| mFontImage.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ComputeShaderReadOnly, |
| fontDataUpload); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result OverlayVk::cullWidgets(ContextVk *contextVk) |
| { |
| RendererVk *renderer = contextVk->getRenderer(); |
| |
| // Release old culledWidgets image |
| mCulledWidgets.releaseImage(renderer); |
| contextVk->addGarbage(&mCulledWidgetsView); |
| |
| // Create a buffer to contain coordinates of enabled text and graph widgets. This buffer will |
| // be used to perform tiled culling and can be discarded immediately after. |
| VkBufferCreateInfo bufferCreateInfo = {}; |
| bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| bufferCreateInfo.size = mState.getWidgetCoordinatesBufferSize(); |
| bufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| |
| vk::RendererScoped<vk::BufferHelper> enabledWidgetsBuffer(renderer); |
| |
| ANGLE_TRY(enabledWidgetsBuffer.get().init(contextVk, bufferCreateInfo, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); |
| |
| // Fill the buffer with coordinate information from enabled widgets. |
| uint8_t *enabledWidgets; |
| ANGLE_TRY(enabledWidgetsBuffer.get().map(contextVk, &enabledWidgets)); |
| |
| gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1); |
| mState.fillEnabledWidgetCoordinates(presentImageExtents, enabledWidgets); |
| |
| ANGLE_TRY(enabledWidgetsBuffer.get().flush(contextVk, 0, enabledWidgetsBuffer.get().getSize())); |
| enabledWidgetsBuffer.get().unmap(contextVk->getDevice()); |
| |
| enabledWidgetsBuffer.get().onExternalWrite(VK_ACCESS_HOST_WRITE_BIT); |
| |
| // Allocate mCulledWidget and its view. |
| VkExtent3D culledWidgetsExtent = { |
| UnsignedCeilDivide(mPresentImageExtent.width, mSubgroupSize[0]), |
| UnsignedCeilDivide(mPresentImageExtent.height, mSubgroupSize[1]), 1}; |
| |
| ANGLE_TRY(mCulledWidgets.init(contextVk, gl::TextureType::_2D, culledWidgetsExtent, |
| renderer->getFormat(angle::FormatID::R32G32_UINT), 1, |
| VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 0, 0, 1, |
| 1)); |
| ANGLE_TRY(mCulledWidgets.initMemory(contextVk, renderer->getMemoryProperties(), |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); |
| ANGLE_TRY(mCulledWidgets.initImageView(contextVk, gl::TextureType::_2D, |
| VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(), |
| &mCulledWidgetsView, 0, 1)); |
| |
| UtilsVk::OverlayCullParameters params; |
| params.subgroupSize[0] = mSubgroupSize[0]; |
| params.subgroupSize[1] = mSubgroupSize[1]; |
| params.supportsSubgroupBallot = mSupportsSubgroupBallot; |
| params.supportsSubgroupArithmetic = mSupportsSubgroupArithmetic; |
| |
| return contextVk->getUtils().cullOverlayWidgets(contextVk, &enabledWidgetsBuffer.get(), |
| &mCulledWidgets, &mCulledWidgetsView, params); |
| } |
| |
| angle::Result OverlayVk::onPresent(ContextVk *contextVk, |
| vk::ImageHelper *imageToPresent, |
| const vk::ImageView *imageToPresentView) |
| { |
| if (mState.getEnabledWidgetCount() == 0) |
| { |
| return angle::Result::Continue; |
| } |
| |
| RendererVk *renderer = contextVk->getRenderer(); |
| |
| // If the swapchain image doesn't support storage image, we can't output to it. |
| VkFormatFeatureFlags featureBits = renderer->getImageFormatFeatureBits( |
| imageToPresent->getFormat().vkImageFormat, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT); |
| if ((featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0) |
| { |
| return angle::Result::Continue; |
| } |
| |
| const VkExtent3D &imageExtent = imageToPresent->getExtents(); |
| |
| mRefreshCulledWidgets = mRefreshCulledWidgets || |
| mPresentImageExtent.width != imageExtent.width || |
| mPresentImageExtent.height != imageExtent.height; |
| |
| if (mRefreshCulledWidgets) |
| { |
| mPresentImageExtent.width = imageExtent.width; |
| mPresentImageExtent.height = imageExtent.height; |
| |
| ANGLE_TRY(cullWidgets(contextVk)); |
| |
| mRefreshCulledWidgets = false; |
| } |
| |
| vk::RendererScoped<vk::BufferHelper> textDataBuffer(renderer); |
| vk::RendererScoped<vk::BufferHelper> graphDataBuffer(renderer); |
| |
| VkBufferCreateInfo textBufferCreateInfo = {}; |
| textBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| textBufferCreateInfo.size = mState.getTextWidgetsBufferSize(); |
| textBufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| textBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| |
| VkBufferCreateInfo graphBufferCreateInfo = textBufferCreateInfo; |
| graphBufferCreateInfo.size = mState.getGraphWidgetsBufferSize(); |
| |
| ANGLE_TRY(textDataBuffer.get().init(contextVk, textBufferCreateInfo, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); |
| ANGLE_TRY(graphDataBuffer.get().init(contextVk, graphBufferCreateInfo, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); |
| |
| uint8_t *textData; |
| uint8_t *graphData; |
| ANGLE_TRY(textDataBuffer.get().map(contextVk, &textData)); |
| ANGLE_TRY(graphDataBuffer.get().map(contextVk, &graphData)); |
| |
| gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1); |
| mState.fillWidgetData(presentImageExtents, textData, graphData); |
| |
| ANGLE_TRY(textDataBuffer.get().flush(contextVk, 0, textDataBuffer.get().getSize())); |
| ANGLE_TRY(graphDataBuffer.get().flush(contextVk, 0, graphDataBuffer.get().getSize())); |
| textDataBuffer.get().unmap(contextVk->getDevice()); |
| graphDataBuffer.get().unmap(contextVk->getDevice()); |
| |
| UtilsVk::OverlayDrawParameters params; |
| params.subgroupSize[0] = mSubgroupSize[0]; |
| params.subgroupSize[1] = mSubgroupSize[1]; |
| |
| return contextVk->getUtils().drawOverlay( |
| contextVk, &textDataBuffer.get(), &graphDataBuffer.get(), &mFontImage, &mFontImageView, |
| &mCulledWidgets, &mCulledWidgetsView, imageToPresent, imageToPresentView, params); |
| } |
| |
| } // namespace rx |