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.
7
// Implements functions that interpret widget data. Data formats and limits correspond to the
8
// Vulkan implementation (as the only implementation). They are generic enough so other backends
9
// could respect them too, if they implement the overlay.
12
#include "libANGLE/Overlay.h"
13
#include "libANGLE/Overlay_font_autogen.h"
19
// Internally, every widget is either Text or Graph.
20
enum class WidgetInternalType
26
EnumCount = InvalidEnum,
29
// A map that says how the API-facing widget types map to internal types.
30
constexpr angle::PackedEnumMap<WidgetType, WidgetInternalType> kWidgetTypeToInternalMap = {
31
{WidgetType::Count, WidgetInternalType::Text},
32
{WidgetType::Text, WidgetInternalType::Text},
33
{WidgetType::PerSecond, WidgetInternalType::Text},
34
{WidgetType::RunningGraph, WidgetInternalType::Graph},
35
{WidgetType::RunningHistogram, WidgetInternalType::Graph},
38
// Structures and limits matching uniform buffers in vulkan/shaders/src/OverlayDraw.comp. The size
39
// of text and graph widgets is chosen such that they could fit in uniform buffers with minimum
40
// required Vulkan size.
41
constexpr size_t kMaxRenderableTextWidgets = 32;
42
constexpr size_t kMaxRenderableGraphWidgets = 32;
43
constexpr size_t kMaxTextLength = 256;
44
constexpr size_t kMaxGraphDataSize = 64;
46
constexpr angle::PackedEnumMap<WidgetInternalType, size_t> kWidgetInternalTypeMaxWidgets = {
47
{WidgetInternalType::Text, kMaxRenderableTextWidgets},
48
{WidgetInternalType::Graph, kMaxRenderableGraphWidgets},
51
constexpr angle::PackedEnumMap<WidgetInternalType, size_t> kWidgetInternalTypeWidgetOffsets = {
52
{WidgetInternalType::Text, 0},
53
{WidgetInternalType::Graph, kMaxRenderableTextWidgets},
56
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
58
// Structure matching buffer in vulkan/shaders/src/OverlayCull.comp.
59
struct WidgetCoordinates
61
uint32_t coordinates[kMaxRenderableTextWidgets + kMaxRenderableGraphWidgets][4];
64
// Structures matching buffers in vulkan/shaders/src/OverlayDraw.comp.
67
uint32_t coordinates[4];
71
uint8_t text[kMaxTextLength];
74
struct GraphWidgetData
76
uint32_t coordinates[4];
80
uint32_t values[kMaxGraphDataSize];
85
TextWidgetData widgets[kMaxRenderableTextWidgets];
90
GraphWidgetData widgets[kMaxRenderableGraphWidgets];
93
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
95
uint32_t GetWidgetCoord(int32_t src, uint32_t extent)
97
int32_t dst = src < 0 ? extent + src : src;
99
return std::min<uint32_t>(std::max(dst, 0), extent - 1);
102
void GetWidgetCoordinates(const int32_t srcCoords[4],
103
const gl::Extents &imageExtent,
104
uint32_t dstCoordsOut[4])
106
dstCoordsOut[0] = GetWidgetCoord(srcCoords[0], imageExtent.width);
107
dstCoordsOut[1] = GetWidgetCoord(srcCoords[1], imageExtent.height);
108
dstCoordsOut[2] = GetWidgetCoord(srcCoords[2], imageExtent.width);
109
dstCoordsOut[3] = GetWidgetCoord(srcCoords[3], imageExtent.height);
112
void GetWidgetColor(const float srcColor[4], float dstColor[4])
114
memcpy(dstColor, srcColor, 4 * sizeof(dstColor[0]));
117
void GetTextFontSize(int srcFontSize, uint32_t dstFontSize[3])
119
// .xy contains the font glyph width/height
120
dstFontSize[0] = overlay::kFontGlyphWidths[srcFontSize];
121
dstFontSize[1] = overlay::kFontGlyphHeights[srcFontSize];
122
// .z contains the layer
123
dstFontSize[2] = srcFontSize;
126
void GetGraphValueWidth(const int32_t srcCoords[4], size_t valueCount, uint32_t *dstValueWidth)
128
const int32_t graphWidth = std::abs(srcCoords[2] - srcCoords[0]);
130
// If valueCount doesn't divide graphWidth, the graph bars won't fit well in its frame.
131
// Fix initOverlayWidgets() in that case.
132
ASSERT(graphWidth % valueCount == 0);
134
*dstValueWidth = graphWidth / valueCount;
137
void GetTextString(const std::string &src, uint8_t textOut[kMaxTextLength])
139
for (size_t i = 0; i < src.length() && i < kMaxTextLength; ++i)
141
// The font image has 96 ASCII characters starting from ' '.
142
textOut[i] = src[i] - ' ';
146
void GetGraphValues(const std::vector<size_t> srcValues,
149
uint32_t valuesOut[kMaxGraphDataSize])
151
ASSERT(srcValues.size() <= kMaxGraphDataSize);
153
for (size_t i = 0; i < srcValues.size(); ++i)
155
size_t index = (startIndex + i) % srcValues.size();
156
valuesOut[i] = static_cast<uint32_t>(srcValues[index] * scale);
160
std::vector<size_t> CreateHistogram(const std::vector<size_t> values)
162
std::vector<size_t> histogram(values.size(), 0);
164
for (size_t rank : values)
172
using OverlayWidgetCounts = angle::PackedEnumMap<WidgetInternalType, size_t>;
173
using AppendWidgetDataFunc = void (*)(const overlay::Widget *widget,
174
const gl::Extents &imageExtent,
175
TextWidgetData *textWidget,
176
GraphWidgetData *graphWidget,
177
OverlayWidgetCounts *widgetCounts);
180
namespace overlay_impl
182
// This class interprets the generic data collected in every element into a human-understandable
183
// widget. This often means generating text specific to this item and scaling graph data to
184
// something sensible.
185
class AppendWidgetDataHelper
188
static void AppendFPS(const overlay::Widget *widget,
189
const gl::Extents &imageExtent,
190
TextWidgetData *textWidget,
191
GraphWidgetData *graphWidget,
192
OverlayWidgetCounts *widgetCounts);
193
static void AppendVulkanLastValidationMessage(const overlay::Widget *widget,
194
const gl::Extents &imageExtent,
195
TextWidgetData *textWidget,
196
GraphWidgetData *graphWidget,
197
OverlayWidgetCounts *widgetCounts);
198
static void AppendVulkanValidationMessageCount(const overlay::Widget *widget,
199
const gl::Extents &imageExtent,
200
TextWidgetData *textWidget,
201
GraphWidgetData *graphWidget,
202
OverlayWidgetCounts *widgetCounts);
203
static void AppendVulkanCommandGraphSize(const overlay::Widget *widget,
204
const gl::Extents &imageExtent,
205
TextWidgetData *textWidget,
206
GraphWidgetData *graphWidget,
207
OverlayWidgetCounts *widgetCounts);
208
static void AppendVulkanSecondaryCommandBufferPoolWaste(const overlay::Widget *widget,
209
const gl::Extents &imageExtent,
210
TextWidgetData *textWidget,
211
GraphWidgetData *graphWidget,
212
OverlayWidgetCounts *widgetCounts);
215
static std::ostream &OutputPerSecond(std::ostream &out, const overlay::PerSecond *perSecond);
217
static std::ostream &OutputText(std::ostream &out, const overlay::Text *text);
219
static std::ostream &OutputCount(std::ostream &out, const overlay::Count *count);
221
static void AppendTextCommon(const overlay::Widget *widget,
222
const gl::Extents &imageExtent,
223
const std::string &text,
224
TextWidgetData *textWidget,
225
OverlayWidgetCounts *widgetCounts);
227
static void AppendGraphCommon(const overlay::Widget *widget,
228
const gl::Extents &imageExtent,
229
const std::vector<size_t> runningValues,
232
GraphWidgetData *graphWidget,
233
OverlayWidgetCounts *widgetCounts);
236
void AppendWidgetDataHelper::AppendTextCommon(const overlay::Widget *widget,
237
const gl::Extents &imageExtent,
238
const std::string &text,
239
TextWidgetData *textWidget,
240
OverlayWidgetCounts *widgetCounts)
242
GetWidgetCoordinates(widget->coords, imageExtent, textWidget->coordinates);
243
GetWidgetColor(widget->color, textWidget->color);
244
GetTextFontSize(widget->fontSize, textWidget->fontSize);
245
GetTextString(text, textWidget->text);
247
++(*widgetCounts)[WidgetInternalType::Text];
250
void AppendWidgetDataHelper::AppendGraphCommon(const overlay::Widget *widget,
251
const gl::Extents &imageExtent,
252
const std::vector<size_t> runningValues,
255
GraphWidgetData *graphWidget,
256
OverlayWidgetCounts *widgetCounts)
258
const overlay::RunningGraph *widgetAsGraph = static_cast<const overlay::RunningGraph *>(widget);
260
GetWidgetCoordinates(widget->coords, imageExtent, graphWidget->coordinates);
261
GetWidgetColor(widget->color, graphWidget->color);
262
GetGraphValueWidth(widget->coords, widgetAsGraph->runningValues.size(),
263
&graphWidget->valueWidth);
264
GetGraphValues(runningValues, startIndex, scale, graphWidget->values);
266
++(*widgetCounts)[WidgetInternalType::Graph];
269
void AppendWidgetDataHelper::AppendFPS(const overlay::Widget *widget,
270
const gl::Extents &imageExtent,
271
TextWidgetData *textWidget,
272
GraphWidgetData *graphWidget,
273
OverlayWidgetCounts *widgetCounts)
275
const overlay::PerSecond *fps = static_cast<const overlay::PerSecond *>(widget);
276
std::ostringstream text;
278
OutputPerSecond(text, fps);
280
AppendTextCommon(widget, imageExtent, text.str(), textWidget, widgetCounts);
283
void AppendWidgetDataHelper::AppendVulkanLastValidationMessage(const overlay::Widget *widget,
284
const gl::Extents &imageExtent,
285
TextWidgetData *textWidget,
286
GraphWidgetData *graphWidget,
287
OverlayWidgetCounts *widgetCounts)
289
const overlay::Text *lastValidationMessage = static_cast<const overlay::Text *>(widget);
290
std::ostringstream text;
291
text << "Last VVL Message: ";
292
OutputText(text, lastValidationMessage);
294
AppendTextCommon(widget, imageExtent, text.str(), textWidget, widgetCounts);
297
void AppendWidgetDataHelper::AppendVulkanValidationMessageCount(const overlay::Widget *widget,
298
const gl::Extents &imageExtent,
299
TextWidgetData *textWidget,
300
GraphWidgetData *graphWidget,
301
OverlayWidgetCounts *widgetCounts)
303
const overlay::Count *validationMessageCount = static_cast<const overlay::Count *>(widget);
304
std::ostringstream text;
305
text << "VVL Message Count: ";
306
OutputCount(text, validationMessageCount);
308
AppendTextCommon(widget, imageExtent, text.str(), textWidget, widgetCounts);
311
void AppendWidgetDataHelper::AppendVulkanCommandGraphSize(const overlay::Widget *widget,
312
const gl::Extents &imageExtent,
313
TextWidgetData *textWidget,
314
GraphWidgetData *graphWidget,
315
OverlayWidgetCounts *widgetCounts)
317
const overlay::RunningGraph *commandGraphSize =
318
static_cast<const overlay::RunningGraph *>(widget);
320
const size_t maxValue = *std::max_element(commandGraphSize->runningValues.begin(),
321
commandGraphSize->runningValues.end());
322
const int32_t graphHeight = std::abs(widget->coords[3] - widget->coords[1]);
323
const float graphScale = static_cast<float>(graphHeight) / maxValue;
325
AppendGraphCommon(widget, imageExtent, commandGraphSize->runningValues,
326
commandGraphSize->lastValueIndex + 1, graphScale, graphWidget, widgetCounts);
328
if ((*widgetCounts)[WidgetInternalType::Text] <
329
kWidgetInternalTypeMaxWidgets[WidgetInternalType::Text])
331
std::ostringstream text;
332
text << "Command Graph Size (Max: " << maxValue << ")";
333
AppendTextCommon(&commandGraphSize->description, imageExtent, text.str(), textWidget,
338
void AppendWidgetDataHelper::AppendVulkanSecondaryCommandBufferPoolWaste(
339
const overlay::Widget *widget,
340
const gl::Extents &imageExtent,
341
TextWidgetData *textWidget,
342
GraphWidgetData *graphWidget,
343
OverlayWidgetCounts *widgetCounts)
345
const overlay::RunningHistogram *secondaryCommandBufferPoolWaste =
346
static_cast<const overlay::RunningHistogram *>(widget);
348
std::vector<size_t> histogram = CreateHistogram(secondaryCommandBufferPoolWaste->runningValues);
349
auto maxValueIter = std::max_element(histogram.rbegin(), histogram.rend());
350
const size_t maxValue = *maxValueIter;
351
const int32_t graphHeight = std::abs(widget->coords[3] - widget->coords[1]);
352
const float graphScale = static_cast<float>(graphHeight) / maxValue;
354
AppendGraphCommon(widget, imageExtent, histogram, 0, graphScale, graphWidget, widgetCounts);
356
if ((*widgetCounts)[WidgetInternalType::Text] <
357
kWidgetInternalTypeMaxWidgets[WidgetInternalType::Text])
359
std::ostringstream text;
360
size_t peak = std::distance(maxValueIter, histogram.rend() - 1);
361
size_t peakPercent = (peak * 100 + 50) / histogram.size();
363
text << "CB Pool Waste (Peak: " << peakPercent << "%)";
364
AppendTextCommon(&secondaryCommandBufferPoolWaste->description, imageExtent, text.str(),
365
textWidget, widgetCounts);
369
std::ostream &AppendWidgetDataHelper::OutputPerSecond(std::ostream &out,
370
const overlay::PerSecond *perSecond)
372
return out << perSecond->lastPerSecondCount;
375
std::ostream &AppendWidgetDataHelper::OutputText(std::ostream &out, const overlay::Text *text)
377
return out << text->text;
380
std::ostream &AppendWidgetDataHelper::OutputCount(std::ostream &out, const overlay::Count *count)
382
return out << count->count;
384
} // namespace overlay_impl
388
constexpr angle::PackedEnumMap<WidgetId, AppendWidgetDataFunc> kWidgetIdToAppendDataFuncMap = {
389
{WidgetId::FPS, overlay_impl::AppendWidgetDataHelper::AppendFPS},
390
{WidgetId::VulkanLastValidationMessage,
391
overlay_impl::AppendWidgetDataHelper::AppendVulkanLastValidationMessage},
392
{WidgetId::VulkanValidationMessageCount,
393
overlay_impl::AppendWidgetDataHelper::AppendVulkanValidationMessageCount},
394
{WidgetId::VulkanCommandGraphSize,
395
overlay_impl::AppendWidgetDataHelper::AppendVulkanCommandGraphSize},
396
{WidgetId::VulkanSecondaryCommandBufferPoolWaste,
397
overlay_impl::AppendWidgetDataHelper::AppendVulkanSecondaryCommandBufferPoolWaste},
403
RunningGraph::RunningGraph(size_t n) : runningValues(n, 0) {}
404
RunningGraph::~RunningGraph() = default;
405
} // namespace overlay
407
size_t OverlayState::getWidgetCoordinatesBufferSize() const
409
return sizeof(WidgetCoordinates);
412
size_t OverlayState::getTextWidgetsBufferSize() const
414
return sizeof(TextWidgets);
417
size_t OverlayState::getGraphWidgetsBufferSize() const
419
return sizeof(GraphWidgets);
422
void OverlayState::fillEnabledWidgetCoordinates(const gl::Extents &imageExtents,
423
uint8_t *enabledWidgetsPtr) const
425
WidgetCoordinates *enabledWidgets = reinterpret_cast<WidgetCoordinates *>(enabledWidgetsPtr);
426
memset(enabledWidgets, 0, sizeof(*enabledWidgets));
428
OverlayWidgetCounts widgetCounts = {};
430
for (const std::unique_ptr<overlay::Widget> &widget : mOverlayWidgets)
432
if (!widget->enabled)
437
WidgetInternalType internalType = kWidgetTypeToInternalMap[widget->type];
438
ASSERT(internalType != WidgetInternalType::InvalidEnum);
440
if (widgetCounts[internalType] >= kWidgetInternalTypeMaxWidgets[internalType])
446
kWidgetInternalTypeWidgetOffsets[internalType] + widgetCounts[internalType]++;
448
GetWidgetCoordinates(widget->coords, imageExtents, enabledWidgets->coordinates[writeIndex]);
450
// Graph widgets have a text widget attached as well.
451
if (internalType == WidgetInternalType::Graph)
453
WidgetInternalType textType = WidgetInternalType::Text;
454
if (widgetCounts[textType] >= kWidgetInternalTypeMaxWidgets[textType])
459
const overlay::RunningGraph *widgetAsGraph =
460
static_cast<const overlay::RunningGraph *>(widget.get());
461
writeIndex = kWidgetInternalTypeWidgetOffsets[textType] + widgetCounts[textType]++;
463
GetWidgetCoordinates(widgetAsGraph->description.coords, imageExtents,
464
enabledWidgets->coordinates[writeIndex]);
469
void OverlayState::fillWidgetData(const gl::Extents &imageExtents,
471
uint8_t *graphData) const
473
TextWidgets *textWidgets = reinterpret_cast<TextWidgets *>(textData);
474
GraphWidgets *graphWidgets = reinterpret_cast<GraphWidgets *>(graphData);
476
memset(textWidgets, overlay::kFontCharacters, sizeof(*textWidgets));
477
memset(graphWidgets, 0, sizeof(*graphWidgets));
479
OverlayWidgetCounts widgetCounts = {};
481
for (WidgetId id : angle::AllEnums<WidgetId>())
483
const std::unique_ptr<overlay::Widget> &widget = mOverlayWidgets[id];
484
if (!widget->enabled)
489
WidgetInternalType internalType = kWidgetTypeToInternalMap[widget->type];
490
ASSERT(internalType != WidgetInternalType::InvalidEnum);
492
if (widgetCounts[internalType] >= kWidgetInternalTypeMaxWidgets[internalType])
497
AppendWidgetDataFunc appendFunc = kWidgetIdToAppendDataFuncMap[id];
498
appendFunc(widget.get(), imageExtents,
499
&textWidgets->widgets[widgetCounts[WidgetInternalType::Text]],
500
&graphWidgets->widgets[widgetCounts[WidgetInternalType::Graph]], &widgetCounts);