// // 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 namespace rx { OverlayVk::OverlayVk(const gl::OverlayState &state) : OverlayImpl(state), mSupportsSubgroupBallot(false), mSupportsSubgroupArithmetic(false), mRefreshCulledWidgets(false), mSubgroupSize{}, mPresentImageExtent{} {} OverlayVk::~OverlayVk() = default; angle::Result OverlayVk::init(const gl::Context *context, bool *successOut) { *successOut = false; 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; ANGLE_TRY(contextVk->flushImpl(nullptr)); *successOut = true; return angle::Result::Continue; } void OverlayVk::onDestroy(const gl::Context *context) { RendererVk *renderer = vk::GetImpl(context)->getRenderer(); VkDevice device = renderer->getDevice(); mCulledWidgets.destroy(renderer); mCulledWidgetsView.destroy(device); mFontImage.destroy(renderer); 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 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(renderer, 0, fontDataBuffer.get().getSize())); fontDataBuffer.get().unmap(renderer); // Don't use robust resource init for overlay widgets. bool useRobustInit = false; // 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, gl::LevelIndex(0), 1, gl::overlay::kFontCount, useRobustInit, false)); ANGLE_TRY(mFontImage.initMemory(contextVk, false, renderer->getMemoryProperties(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); ANGLE_TRY(mFontImage.initImageView(contextVk, gl::TextureType::_2DArray, VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(), &mFontImageView, vk::LevelIndex(0), 1)); // Copy font data from staging buffer. vk::CommandBufferAccess access; access.onBufferTransferRead(&fontDataBuffer.get()); access.onImageTransferWrite(gl::LevelIndex(0), 1, 0, gl::overlay::kFontCount, VK_IMAGE_ASPECT_COLOR_BIT, &mFontImage); vk::CommandBuffer *fontDataUpload; ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &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, ©); return angle::Result::Continue; } angle::Result OverlayVk::cullWidgets(ContextVk *contextVk) { RendererVk *renderer = contextVk->getRenderer(); // Release old culledWidgets image mCulledWidgets.releaseImageFromShareContexts(renderer, contextVk); 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 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(renderer, 0, enabledWidgetsBuffer.get().getSize())); enabledWidgetsBuffer.get().unmap(renderer); // Allocate mCulledWidget and its view. VkExtent3D culledWidgetsExtent = { UnsignedCeilDivide(mPresentImageExtent.width, mSubgroupSize[0]), UnsignedCeilDivide(mPresentImageExtent.height, mSubgroupSize[1]), 1}; // Don't use robust resource init for overlay widgets. bool useRobustInit = false; 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, gl::LevelIndex(0), 1, 1, useRobustInit, false)); ANGLE_TRY(mCulledWidgets.initMemory(contextVk, false, renderer->getMemoryProperties(), VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); ANGLE_TRY(mCulledWidgets.initImageView(contextVk, gl::TextureType::_2D, VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(), &mCulledWidgetsView, vk::LevelIndex(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, bool is90DegreeRotation) { 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().actualImageFormatID, 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 textDataBuffer(renderer); vk::RendererScoped 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(renderer, 0, textDataBuffer.get().getSize())); ANGLE_TRY(graphDataBuffer.get().flush(renderer, 0, graphDataBuffer.get().getSize())); textDataBuffer.get().unmap(renderer); graphDataBuffer.get().unmap(renderer); UtilsVk::OverlayDrawParameters params; params.subgroupSize[0] = mSubgroupSize[0]; params.subgroupSize[1] = mSubgroupSize[1]; params.rotateXY = is90DegreeRotation; return contextVk->getUtils().drawOverlay( contextVk, &textDataBuffer.get(), &graphDataBuffer.get(), &mFontImage, &mFontImageView, &mCulledWidgets, &mCulledWidgetsView, imageToPresent, imageToPresentView, params); } } // namespace rx