1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // OverlayVk.cpp:
7 //    Implements the OverlayVk class.
8 //
9 
10 #include "libANGLE/renderer/vulkan/OverlayVk.h"
11 
12 #include "common/system_utils.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/Overlay_font_autogen.h"
15 #include "libANGLE/renderer/vulkan/ContextVk.h"
16 
17 #include <numeric>
18 
19 namespace rx
20 {
OverlayVk(const gl::OverlayState & state)21 OverlayVk::OverlayVk(const gl::OverlayState &state)
22     : OverlayImpl(state),
23       mSupportsSubgroupBallot(false),
24       mSupportsSubgroupArithmetic(false),
25       mRefreshCulledWidgets(false),
26       mSubgroupSize{},
27       mPresentImageExtent{}
28 {}
29 OverlayVk::~OverlayVk() = default;
30 
init(const gl::Context * context,bool * successOut)31 angle::Result OverlayVk::init(const gl::Context *context, bool *successOut)
32 {
33     *successOut = false;
34 
35     ContextVk *contextVk   = vk::GetImpl(context);
36     RendererVk *rendererVk = contextVk->getRenderer();
37 
38     const VkPhysicalDeviceSubgroupProperties &subgroupProperties =
39         rendererVk->getPhysicalDeviceSubgroupProperties();
40     uint32_t subgroupSize = subgroupProperties.subgroupSize;
41 
42     // Currently, only subgroup sizes 32 and 64 are supported.
43     if (subgroupSize != 32 && subgroupSize != 64)
44     {
45         return angle::Result::Continue;
46     }
47 
48     mSubgroupSize[0] = 8;
49     mSubgroupSize[1] = subgroupSize / 8;
50 
51     constexpr VkSubgroupFeatureFlags kSubgroupBallotOperations =
52         VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT;
53     constexpr VkSubgroupFeatureFlags kSubgroupArithmeticOperations =
54         VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_ARITHMETIC_BIT;
55 
56     // If not all operations used in the shaders are supported, disable the overlay.
57     if ((subgroupProperties.supportedOperations & kSubgroupBallotOperations) ==
58         kSubgroupBallotOperations)
59     {
60         mSupportsSubgroupBallot = true;
61     }
62     else if ((subgroupProperties.supportedOperations & kSubgroupArithmeticOperations) ==
63              kSubgroupArithmeticOperations)
64     {
65         mSupportsSubgroupArithmetic = true;
66     }
67 
68     ANGLE_TRY(createFont(contextVk));
69 
70     mRefreshCulledWidgets = true;
71 
72     ANGLE_TRY(contextVk->flushImpl(nullptr));
73     *successOut = true;
74     return angle::Result::Continue;
75 }
76 
onDestroy(const gl::Context * context)77 void OverlayVk::onDestroy(const gl::Context *context)
78 {
79     RendererVk *renderer = vk::GetImpl(context)->getRenderer();
80     VkDevice device      = renderer->getDevice();
81 
82     mCulledWidgets.destroy(renderer);
83     mCulledWidgetsView.destroy(device);
84 
85     mFontImage.destroy(renderer);
86     mFontImageView.destroy(device);
87 }
88 
createFont(ContextVk * contextVk)89 angle::Result OverlayVk::createFont(ContextVk *contextVk)
90 {
91     RendererVk *renderer = contextVk->getRenderer();
92 
93     // Create a buffer to stage font data upload.
94     VkBufferCreateInfo bufferCreateInfo = {};
95     bufferCreateInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
96     bufferCreateInfo.size =
97         gl::overlay::kFontCount * gl::overlay::kFontImageWidth * gl::overlay::kFontImageHeight;
98     bufferCreateInfo.usage       = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
99     bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
100 
101     vk::RendererScoped<vk::BufferHelper> fontDataBuffer(renderer);
102 
103     ANGLE_TRY(fontDataBuffer.get().init(contextVk, bufferCreateInfo,
104                                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
105 
106     uint8_t *fontData;
107     ANGLE_TRY(fontDataBuffer.get().map(contextVk, &fontData));
108 
109     mState.initFontData(fontData);
110 
111     ANGLE_TRY(fontDataBuffer.get().flush(renderer, 0, fontDataBuffer.get().getSize()));
112     fontDataBuffer.get().unmap(renderer);
113 
114     // Don't use robust resource init for overlay widgets.
115     bool useRobustInit = false;
116 
117     // Create the font image.
118     ANGLE_TRY(
119         mFontImage.init(contextVk, gl::TextureType::_2D,
120                         VkExtent3D{gl::overlay::kFontImageWidth, gl::overlay::kFontImageHeight, 1},
121                         renderer->getFormat(angle::FormatID::R8_UNORM), 1,
122                         VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
123                         gl::LevelIndex(0), 1, gl::overlay::kFontCount, useRobustInit, false));
124     ANGLE_TRY(mFontImage.initMemory(contextVk, false, renderer->getMemoryProperties(),
125                                     VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
126     ANGLE_TRY(mFontImage.initImageView(contextVk, gl::TextureType::_2DArray,
127                                        VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
128                                        &mFontImageView, vk::LevelIndex(0), 1));
129 
130     // Copy font data from staging buffer.
131     vk::CommandBufferAccess access;
132     access.onBufferTransferRead(&fontDataBuffer.get());
133     access.onImageTransferWrite(gl::LevelIndex(0), 1, 0, gl::overlay::kFontCount,
134                                 VK_IMAGE_ASPECT_COLOR_BIT, &mFontImage);
135     vk::CommandBuffer *fontDataUpload;
136     ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &fontDataUpload));
137 
138     VkBufferImageCopy copy           = {};
139     copy.bufferRowLength             = gl::overlay::kFontImageWidth;
140     copy.bufferImageHeight           = gl::overlay::kFontImageHeight;
141     copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
142     copy.imageSubresource.layerCount = gl::overlay::kFontCount;
143     copy.imageExtent.width           = gl::overlay::kFontImageWidth;
144     copy.imageExtent.height          = gl::overlay::kFontImageHeight;
145     copy.imageExtent.depth           = 1;
146 
147     fontDataUpload->copyBufferToImage(fontDataBuffer.get().getBuffer().getHandle(),
148                                       mFontImage.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
149                                       1, &copy);
150 
151     return angle::Result::Continue;
152 }
153 
cullWidgets(ContextVk * contextVk)154 angle::Result OverlayVk::cullWidgets(ContextVk *contextVk)
155 {
156     RendererVk *renderer = contextVk->getRenderer();
157 
158     // Release old culledWidgets image
159     mCulledWidgets.releaseImageFromShareContexts(renderer, contextVk);
160     contextVk->addGarbage(&mCulledWidgetsView);
161 
162     // Create a buffer to contain coordinates of enabled text and graph widgets.  This buffer will
163     // be used to perform tiled culling and can be discarded immediately after.
164     VkBufferCreateInfo bufferCreateInfo = {};
165     bufferCreateInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
166     bufferCreateInfo.size               = mState.getWidgetCoordinatesBufferSize();
167     bufferCreateInfo.usage              = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
168     bufferCreateInfo.sharingMode        = VK_SHARING_MODE_EXCLUSIVE;
169 
170     vk::RendererScoped<vk::BufferHelper> enabledWidgetsBuffer(renderer);
171 
172     ANGLE_TRY(enabledWidgetsBuffer.get().init(contextVk, bufferCreateInfo,
173                                               VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
174 
175     // Fill the buffer with coordinate information from enabled widgets.
176     uint8_t *enabledWidgets;
177     ANGLE_TRY(enabledWidgetsBuffer.get().map(contextVk, &enabledWidgets));
178 
179     gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1);
180     mState.fillEnabledWidgetCoordinates(presentImageExtents, enabledWidgets);
181 
182     ANGLE_TRY(enabledWidgetsBuffer.get().flush(renderer, 0, enabledWidgetsBuffer.get().getSize()));
183     enabledWidgetsBuffer.get().unmap(renderer);
184 
185     // Allocate mCulledWidget and its view.
186     VkExtent3D culledWidgetsExtent = {
187         UnsignedCeilDivide(mPresentImageExtent.width, mSubgroupSize[0]),
188         UnsignedCeilDivide(mPresentImageExtent.height, mSubgroupSize[1]), 1};
189 
190     // Don't use robust resource init for overlay widgets.
191     bool useRobustInit = false;
192 
193     ANGLE_TRY(mCulledWidgets.init(contextVk, gl::TextureType::_2D, culledWidgetsExtent,
194                                   renderer->getFormat(angle::FormatID::R32G32_UINT), 1,
195                                   VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
196                                   gl::LevelIndex(0), 1, 1, useRobustInit, false));
197     ANGLE_TRY(mCulledWidgets.initMemory(contextVk, false, renderer->getMemoryProperties(),
198                                         VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
199     ANGLE_TRY(mCulledWidgets.initImageView(contextVk, gl::TextureType::_2D,
200                                            VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
201                                            &mCulledWidgetsView, vk::LevelIndex(0), 1));
202 
203     UtilsVk::OverlayCullParameters params;
204     params.subgroupSize[0]            = mSubgroupSize[0];
205     params.subgroupSize[1]            = mSubgroupSize[1];
206     params.supportsSubgroupBallot     = mSupportsSubgroupBallot;
207     params.supportsSubgroupArithmetic = mSupportsSubgroupArithmetic;
208 
209     return contextVk->getUtils().cullOverlayWidgets(contextVk, &enabledWidgetsBuffer.get(),
210                                                     &mCulledWidgets, &mCulledWidgetsView, params);
211 }
212 
onPresent(ContextVk * contextVk,vk::ImageHelper * imageToPresent,const vk::ImageView * imageToPresentView,bool is90DegreeRotation)213 angle::Result OverlayVk::onPresent(ContextVk *contextVk,
214                                    vk::ImageHelper *imageToPresent,
215                                    const vk::ImageView *imageToPresentView,
216                                    bool is90DegreeRotation)
217 {
218     if (mState.getEnabledWidgetCount() == 0)
219     {
220         return angle::Result::Continue;
221     }
222 
223     RendererVk *renderer = contextVk->getRenderer();
224 
225     // If the swapchain image doesn't support storage image, we can't output to it.
226     VkFormatFeatureFlags featureBits = renderer->getImageFormatFeatureBits(
227         imageToPresent->getFormat().actualImageFormatID, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
228     if ((featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
229     {
230         return angle::Result::Continue;
231     }
232 
233     const VkExtent3D &imageExtent = imageToPresent->getExtents();
234 
235     mRefreshCulledWidgets = mRefreshCulledWidgets ||
236                             mPresentImageExtent.width != imageExtent.width ||
237                             mPresentImageExtent.height != imageExtent.height;
238 
239     if (mRefreshCulledWidgets)
240     {
241         mPresentImageExtent.width  = imageExtent.width;
242         mPresentImageExtent.height = imageExtent.height;
243 
244         ANGLE_TRY(cullWidgets(contextVk));
245 
246         mRefreshCulledWidgets = false;
247     }
248 
249     vk::RendererScoped<vk::BufferHelper> textDataBuffer(renderer);
250     vk::RendererScoped<vk::BufferHelper> graphDataBuffer(renderer);
251 
252     VkBufferCreateInfo textBufferCreateInfo = {};
253     textBufferCreateInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
254     textBufferCreateInfo.size               = mState.getTextWidgetsBufferSize();
255     textBufferCreateInfo.usage              = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
256     textBufferCreateInfo.sharingMode        = VK_SHARING_MODE_EXCLUSIVE;
257 
258     VkBufferCreateInfo graphBufferCreateInfo = textBufferCreateInfo;
259     graphBufferCreateInfo.size               = mState.getGraphWidgetsBufferSize();
260 
261     ANGLE_TRY(textDataBuffer.get().init(contextVk, textBufferCreateInfo,
262                                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
263     ANGLE_TRY(graphDataBuffer.get().init(contextVk, graphBufferCreateInfo,
264                                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
265 
266     uint8_t *textData;
267     uint8_t *graphData;
268     ANGLE_TRY(textDataBuffer.get().map(contextVk, &textData));
269     ANGLE_TRY(graphDataBuffer.get().map(contextVk, &graphData));
270 
271     gl::Extents presentImageExtents(mPresentImageExtent.width, mPresentImageExtent.height, 1);
272     mState.fillWidgetData(presentImageExtents, textData, graphData);
273 
274     ANGLE_TRY(textDataBuffer.get().flush(renderer, 0, textDataBuffer.get().getSize()));
275     ANGLE_TRY(graphDataBuffer.get().flush(renderer, 0, graphDataBuffer.get().getSize()));
276     textDataBuffer.get().unmap(renderer);
277     graphDataBuffer.get().unmap(renderer);
278 
279     UtilsVk::OverlayDrawParameters params;
280     params.subgroupSize[0] = mSubgroupSize[0];
281     params.subgroupSize[1] = mSubgroupSize[1];
282     params.rotateXY        = is90DegreeRotation;
283 
284     return contextVk->getUtils().drawOverlay(
285         contextVk, &textDataBuffer.get(), &graphDataBuffer.get(), &mFontImage, &mFontImageView,
286         &mCulledWidgets, &mCulledWidgetsView, imageToPresent, imageToPresentView, params);
287 }
288 
289 }  // namespace rx
290