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 // OverlayWidgets.cpp:
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.
10 //
11
12 #include "libANGLE/Overlay.h"
13 #include "libANGLE/Overlay_font_autogen.h"
14
15 namespace gl
16 {
17 namespace
18 {
19 // Internally, every widget is either Text or Graph.
20 enum class WidgetInternalType
21 {
22 Text,
23 Graph,
24
25 InvalidEnum,
26 EnumCount = InvalidEnum,
27 };
28
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},
36 };
37
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;
45
46 constexpr angle::PackedEnumMap<WidgetInternalType, size_t> kWidgetInternalTypeMaxWidgets = {
47 {WidgetInternalType::Text, kMaxRenderableTextWidgets},
48 {WidgetInternalType::Graph, kMaxRenderableGraphWidgets},
49 };
50
51 constexpr angle::PackedEnumMap<WidgetInternalType, size_t> kWidgetInternalTypeWidgetOffsets = {
52 {WidgetInternalType::Text, 0},
53 {WidgetInternalType::Graph, kMaxRenderableTextWidgets},
54 };
55
56 ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
57
58 // Structure matching buffer in vulkan/shaders/src/OverlayCull.comp.
59 struct WidgetCoordinates
60 {
61 uint32_t coordinates[kMaxRenderableTextWidgets + kMaxRenderableGraphWidgets][4];
62 };
63
64 // Structures matching buffers in vulkan/shaders/src/OverlayDraw.comp.
65 struct TextWidgetData
66 {
67 uint32_t coordinates[4];
68 float color[4];
69 uint32_t fontSize[3];
70 uint32_t padding;
71 uint8_t text[kMaxTextLength];
72 };
73
74 struct GraphWidgetData
75 {
76 uint32_t coordinates[4];
77 float color[4];
78 uint32_t valueWidth;
79 uint32_t padding[3];
80 uint32_t values[kMaxGraphDataSize];
81 };
82
83 struct TextWidgets
84 {
85 TextWidgetData widgets[kMaxRenderableTextWidgets];
86 };
87
88 struct GraphWidgets
89 {
90 GraphWidgetData widgets[kMaxRenderableGraphWidgets];
91 };
92
93 ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
94
GetWidgetCoord(int32_t src,uint32_t extent)95 uint32_t GetWidgetCoord(int32_t src, uint32_t extent)
96 {
97 int32_t dst = src < 0 ? extent + src : src;
98
99 return std::min<uint32_t>(std::max(dst, 0), extent - 1);
100 }
101
GetWidgetCoordinates(const int32_t srcCoords[4],const gl::Extents & imageExtent,uint32_t dstCoordsOut[4])102 void GetWidgetCoordinates(const int32_t srcCoords[4],
103 const gl::Extents &imageExtent,
104 uint32_t dstCoordsOut[4])
105 {
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);
110 }
111
GetWidgetColor(const float srcColor[4],float dstColor[4])112 void GetWidgetColor(const float srcColor[4], float dstColor[4])
113 {
114 memcpy(dstColor, srcColor, 4 * sizeof(dstColor[0]));
115 }
116
GetTextFontSize(int srcFontSize,uint32_t dstFontSize[3])117 void GetTextFontSize(int srcFontSize, uint32_t dstFontSize[3])
118 {
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;
124 }
125
GetGraphValueWidth(const int32_t srcCoords[4],size_t valueCount,uint32_t * dstValueWidth)126 void GetGraphValueWidth(const int32_t srcCoords[4], size_t valueCount, uint32_t *dstValueWidth)
127 {
128 const int32_t graphWidth = std::abs(srcCoords[2] - srcCoords[0]);
129
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);
133
134 *dstValueWidth = graphWidth / valueCount;
135 }
136
GetTextString(const std::string & src,uint8_t textOut[kMaxTextLength])137 void GetTextString(const std::string &src, uint8_t textOut[kMaxTextLength])
138 {
139 for (size_t i = 0; i < src.length() && i < kMaxTextLength; ++i)
140 {
141 // The font image has 96 ASCII characters starting from ' '.
142 textOut[i] = src[i] - ' ';
143 }
144 }
145
GetGraphValues(const std::vector<size_t> srcValues,size_t startIndex,float scale,uint32_t valuesOut[kMaxGraphDataSize])146 void GetGraphValues(const std::vector<size_t> srcValues,
147 size_t startIndex,
148 float scale,
149 uint32_t valuesOut[kMaxGraphDataSize])
150 {
151 ASSERT(srcValues.size() <= kMaxGraphDataSize);
152
153 for (size_t i = 0; i < srcValues.size(); ++i)
154 {
155 size_t index = (startIndex + i) % srcValues.size();
156 valuesOut[i] = static_cast<uint32_t>(srcValues[index] * scale);
157 }
158 }
159
CreateHistogram(const std::vector<size_t> values)160 std::vector<size_t> CreateHistogram(const std::vector<size_t> values)
161 {
162 std::vector<size_t> histogram(values.size(), 0);
163
164 for (size_t rank : values)
165 {
166 ++histogram[rank];
167 }
168
169 return histogram;
170 }
171
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);
178 } // namespace
179
180 namespace overlay_impl
181 {
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
186 {
187 public:
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 AppendVulkanRenderPassCount(const overlay::Widget *widget,
209 const gl::Extents &imageExtent,
210 TextWidgetData *textWidget,
211 GraphWidgetData *graphWidget,
212 OverlayWidgetCounts *widgetCounts);
213 static void AppendVulkanSecondaryCommandBufferPoolWaste(const overlay::Widget *widget,
214 const gl::Extents &imageExtent,
215 TextWidgetData *textWidget,
216 GraphWidgetData *graphWidget,
217 OverlayWidgetCounts *widgetCounts);
218
219 private:
220 static std::ostream &OutputPerSecond(std::ostream &out, const overlay::PerSecond *perSecond);
221
222 static std::ostream &OutputText(std::ostream &out, const overlay::Text *text);
223
224 static std::ostream &OutputCount(std::ostream &out, const overlay::Count *count);
225
226 static void AppendTextCommon(const overlay::Widget *widget,
227 const gl::Extents &imageExtent,
228 const std::string &text,
229 TextWidgetData *textWidget,
230 OverlayWidgetCounts *widgetCounts);
231
232 static void AppendGraphCommon(const overlay::Widget *widget,
233 const gl::Extents &imageExtent,
234 const std::vector<size_t> runningValues,
235 size_t startIndex,
236 float scale,
237 GraphWidgetData *graphWidget,
238 OverlayWidgetCounts *widgetCounts);
239 };
240
AppendTextCommon(const overlay::Widget * widget,const gl::Extents & imageExtent,const std::string & text,TextWidgetData * textWidget,OverlayWidgetCounts * widgetCounts)241 void AppendWidgetDataHelper::AppendTextCommon(const overlay::Widget *widget,
242 const gl::Extents &imageExtent,
243 const std::string &text,
244 TextWidgetData *textWidget,
245 OverlayWidgetCounts *widgetCounts)
246 {
247 GetWidgetCoordinates(widget->coords, imageExtent, textWidget->coordinates);
248 GetWidgetColor(widget->color, textWidget->color);
249 GetTextFontSize(widget->fontSize, textWidget->fontSize);
250 GetTextString(text, textWidget->text);
251
252 ++(*widgetCounts)[WidgetInternalType::Text];
253 }
254
AppendGraphCommon(const overlay::Widget * widget,const gl::Extents & imageExtent,const std::vector<size_t> runningValues,size_t startIndex,float scale,GraphWidgetData * graphWidget,OverlayWidgetCounts * widgetCounts)255 void AppendWidgetDataHelper::AppendGraphCommon(const overlay::Widget *widget,
256 const gl::Extents &imageExtent,
257 const std::vector<size_t> runningValues,
258 size_t startIndex,
259 float scale,
260 GraphWidgetData *graphWidget,
261 OverlayWidgetCounts *widgetCounts)
262 {
263 const overlay::RunningGraph *widgetAsGraph = static_cast<const overlay::RunningGraph *>(widget);
264
265 GetWidgetCoordinates(widget->coords, imageExtent, graphWidget->coordinates);
266 GetWidgetColor(widget->color, graphWidget->color);
267 GetGraphValueWidth(widget->coords, widgetAsGraph->runningValues.size(),
268 &graphWidget->valueWidth);
269 GetGraphValues(runningValues, startIndex, scale, graphWidget->values);
270
271 ++(*widgetCounts)[WidgetInternalType::Graph];
272 }
273
AppendFPS(const overlay::Widget * widget,const gl::Extents & imageExtent,TextWidgetData * textWidget,GraphWidgetData * graphWidget,OverlayWidgetCounts * widgetCounts)274 void AppendWidgetDataHelper::AppendFPS(const overlay::Widget *widget,
275 const gl::Extents &imageExtent,
276 TextWidgetData *textWidget,
277 GraphWidgetData *graphWidget,
278 OverlayWidgetCounts *widgetCounts)
279 {
280 const overlay::PerSecond *fps = static_cast<const overlay::PerSecond *>(widget);
281 std::ostringstream text;
282 text << "FPS: ";
283 OutputPerSecond(text, fps);
284
285 AppendTextCommon(widget, imageExtent, text.str(), textWidget, widgetCounts);
286 }
287
AppendVulkanLastValidationMessage(const overlay::Widget * widget,const gl::Extents & imageExtent,TextWidgetData * textWidget,GraphWidgetData * graphWidget,OverlayWidgetCounts * widgetCounts)288 void AppendWidgetDataHelper::AppendVulkanLastValidationMessage(const overlay::Widget *widget,
289 const gl::Extents &imageExtent,
290 TextWidgetData *textWidget,
291 GraphWidgetData *graphWidget,
292 OverlayWidgetCounts *widgetCounts)
293 {
294 const overlay::Text *lastValidationMessage = static_cast<const overlay::Text *>(widget);
295 std::ostringstream text;
296 text << "Last VVL Message: ";
297 OutputText(text, lastValidationMessage);
298
299 AppendTextCommon(widget, imageExtent, text.str(), textWidget, widgetCounts);
300 }
301
AppendVulkanValidationMessageCount(const overlay::Widget * widget,const gl::Extents & imageExtent,TextWidgetData * textWidget,GraphWidgetData * graphWidget,OverlayWidgetCounts * widgetCounts)302 void AppendWidgetDataHelper::AppendVulkanValidationMessageCount(const overlay::Widget *widget,
303 const gl::Extents &imageExtent,
304 TextWidgetData *textWidget,
305 GraphWidgetData *graphWidget,
306 OverlayWidgetCounts *widgetCounts)
307 {
308 const overlay::Count *validationMessageCount = static_cast<const overlay::Count *>(widget);
309 std::ostringstream text;
310 text << "VVL Message Count: ";
311 OutputCount(text, validationMessageCount);
312
313 AppendTextCommon(widget, imageExtent, text.str(), textWidget, widgetCounts);
314 }
315
AppendVulkanCommandGraphSize(const overlay::Widget * widget,const gl::Extents & imageExtent,TextWidgetData * textWidget,GraphWidgetData * graphWidget,OverlayWidgetCounts * widgetCounts)316 void AppendWidgetDataHelper::AppendVulkanCommandGraphSize(const overlay::Widget *widget,
317 const gl::Extents &imageExtent,
318 TextWidgetData *textWidget,
319 GraphWidgetData *graphWidget,
320 OverlayWidgetCounts *widgetCounts)
321 {
322 const overlay::RunningGraph *commandGraphSize =
323 static_cast<const overlay::RunningGraph *>(widget);
324
325 const size_t maxValue = *std::max_element(commandGraphSize->runningValues.begin(),
326 commandGraphSize->runningValues.end());
327 const int32_t graphHeight = std::abs(widget->coords[3] - widget->coords[1]);
328 const float graphScale = static_cast<float>(graphHeight) / maxValue;
329
330 AppendGraphCommon(widget, imageExtent, commandGraphSize->runningValues,
331 commandGraphSize->lastValueIndex + 1, graphScale, graphWidget, widgetCounts);
332
333 if ((*widgetCounts)[WidgetInternalType::Text] <
334 kWidgetInternalTypeMaxWidgets[WidgetInternalType::Text])
335 {
336 std::ostringstream text;
337 text << "Command Graph Size (Max: " << maxValue << ")";
338 AppendTextCommon(&commandGraphSize->description, imageExtent, text.str(), textWidget,
339 widgetCounts);
340 }
341 }
342
AppendVulkanRenderPassCount(const overlay::Widget * widget,const gl::Extents & imageExtent,TextWidgetData * textWidget,GraphWidgetData * graphWidget,OverlayWidgetCounts * widgetCounts)343 void AppendWidgetDataHelper::AppendVulkanRenderPassCount(const overlay::Widget *widget,
344 const gl::Extents &imageExtent,
345 TextWidgetData *textWidget,
346 GraphWidgetData *graphWidget,
347 OverlayWidgetCounts *widgetCounts)
348 {
349 const overlay::RunningGraph *renderPassCount =
350 static_cast<const overlay::RunningGraph *>(widget);
351
352 const size_t maxValue = *std::max_element(renderPassCount->runningValues.begin(),
353 renderPassCount->runningValues.end());
354 const int32_t graphHeight = std::abs(widget->coords[3] - widget->coords[1]);
355 const float graphScale = static_cast<float>(graphHeight) / maxValue;
356
357 AppendGraphCommon(widget, imageExtent, renderPassCount->runningValues,
358 renderPassCount->lastValueIndex + 1, graphScale, graphWidget, widgetCounts);
359
360 if ((*widgetCounts)[WidgetInternalType::Text] <
361 kWidgetInternalTypeMaxWidgets[WidgetInternalType::Text])
362 {
363 std::ostringstream text;
364 text << "RenderPass Count (Max: " << maxValue << ")";
365 AppendTextCommon(&renderPassCount->description, imageExtent, text.str(), textWidget,
366 widgetCounts);
367 }
368 }
369
AppendVulkanSecondaryCommandBufferPoolWaste(const overlay::Widget * widget,const gl::Extents & imageExtent,TextWidgetData * textWidget,GraphWidgetData * graphWidget,OverlayWidgetCounts * widgetCounts)370 void AppendWidgetDataHelper::AppendVulkanSecondaryCommandBufferPoolWaste(
371 const overlay::Widget *widget,
372 const gl::Extents &imageExtent,
373 TextWidgetData *textWidget,
374 GraphWidgetData *graphWidget,
375 OverlayWidgetCounts *widgetCounts)
376 {
377 const overlay::RunningHistogram *secondaryCommandBufferPoolWaste =
378 static_cast<const overlay::RunningHistogram *>(widget);
379
380 std::vector<size_t> histogram = CreateHistogram(secondaryCommandBufferPoolWaste->runningValues);
381 auto maxValueIter = std::max_element(histogram.rbegin(), histogram.rend());
382 const size_t maxValue = *maxValueIter;
383 const int32_t graphHeight = std::abs(widget->coords[3] - widget->coords[1]);
384 const float graphScale = static_cast<float>(graphHeight) / maxValue;
385
386 AppendGraphCommon(widget, imageExtent, histogram, 0, graphScale, graphWidget, widgetCounts);
387
388 if ((*widgetCounts)[WidgetInternalType::Text] <
389 kWidgetInternalTypeMaxWidgets[WidgetInternalType::Text])
390 {
391 std::ostringstream text;
392 size_t peak = std::distance(maxValueIter, histogram.rend() - 1);
393 size_t peakPercent = (peak * 100 + 50) / histogram.size();
394
395 text << "CB Pool Waste (Peak: " << peakPercent << "%)";
396 AppendTextCommon(&secondaryCommandBufferPoolWaste->description, imageExtent, text.str(),
397 textWidget, widgetCounts);
398 }
399 }
400
OutputPerSecond(std::ostream & out,const overlay::PerSecond * perSecond)401 std::ostream &AppendWidgetDataHelper::OutputPerSecond(std::ostream &out,
402 const overlay::PerSecond *perSecond)
403 {
404 return out << perSecond->lastPerSecondCount;
405 }
406
OutputText(std::ostream & out,const overlay::Text * text)407 std::ostream &AppendWidgetDataHelper::OutputText(std::ostream &out, const overlay::Text *text)
408 {
409 return out << text->text;
410 }
411
OutputCount(std::ostream & out,const overlay::Count * count)412 std::ostream &AppendWidgetDataHelper::OutputCount(std::ostream &out, const overlay::Count *count)
413 {
414 return out << count->count;
415 }
416 } // namespace overlay_impl
417
418 namespace
419 {
420 constexpr angle::PackedEnumMap<WidgetId, AppendWidgetDataFunc> kWidgetIdToAppendDataFuncMap = {
421 {WidgetId::FPS, overlay_impl::AppendWidgetDataHelper::AppendFPS},
422 {WidgetId::VulkanLastValidationMessage,
423 overlay_impl::AppendWidgetDataHelper::AppendVulkanLastValidationMessage},
424 {WidgetId::VulkanValidationMessageCount,
425 overlay_impl::AppendWidgetDataHelper::AppendVulkanValidationMessageCount},
426 {WidgetId::VulkanRenderPassCount,
427 overlay_impl::AppendWidgetDataHelper::AppendVulkanRenderPassCount},
428 {WidgetId::VulkanSecondaryCommandBufferPoolWaste,
429 overlay_impl::AppendWidgetDataHelper::AppendVulkanSecondaryCommandBufferPoolWaste},
430 };
431 }
432
433 namespace overlay
434 {
RunningGraph(size_t n)435 RunningGraph::RunningGraph(size_t n) : runningValues(n, 0) {}
436 RunningGraph::~RunningGraph() = default;
437 } // namespace overlay
438
getWidgetCoordinatesBufferSize() const439 size_t OverlayState::getWidgetCoordinatesBufferSize() const
440 {
441 return sizeof(WidgetCoordinates);
442 }
443
getTextWidgetsBufferSize() const444 size_t OverlayState::getTextWidgetsBufferSize() const
445 {
446 return sizeof(TextWidgets);
447 }
448
getGraphWidgetsBufferSize() const449 size_t OverlayState::getGraphWidgetsBufferSize() const
450 {
451 return sizeof(GraphWidgets);
452 }
453
fillEnabledWidgetCoordinates(const gl::Extents & imageExtents,uint8_t * enabledWidgetsPtr) const454 void OverlayState::fillEnabledWidgetCoordinates(const gl::Extents &imageExtents,
455 uint8_t *enabledWidgetsPtr) const
456 {
457 WidgetCoordinates *enabledWidgets = reinterpret_cast<WidgetCoordinates *>(enabledWidgetsPtr);
458 memset(enabledWidgets, 0, sizeof(*enabledWidgets));
459
460 OverlayWidgetCounts widgetCounts = {};
461
462 for (const std::unique_ptr<overlay::Widget> &widget : mOverlayWidgets)
463 {
464 if (!widget->enabled)
465 {
466 continue;
467 }
468
469 WidgetInternalType internalType = kWidgetTypeToInternalMap[widget->type];
470 ASSERT(internalType != WidgetInternalType::InvalidEnum);
471
472 if (widgetCounts[internalType] >= kWidgetInternalTypeMaxWidgets[internalType])
473 {
474 continue;
475 }
476
477 size_t writeIndex =
478 kWidgetInternalTypeWidgetOffsets[internalType] + widgetCounts[internalType]++;
479
480 GetWidgetCoordinates(widget->coords, imageExtents, enabledWidgets->coordinates[writeIndex]);
481
482 // Graph widgets have a text widget attached as well.
483 if (internalType == WidgetInternalType::Graph)
484 {
485 WidgetInternalType textType = WidgetInternalType::Text;
486 if (widgetCounts[textType] >= kWidgetInternalTypeMaxWidgets[textType])
487 {
488 continue;
489 }
490
491 const overlay::RunningGraph *widgetAsGraph =
492 static_cast<const overlay::RunningGraph *>(widget.get());
493 writeIndex = kWidgetInternalTypeWidgetOffsets[textType] + widgetCounts[textType]++;
494
495 GetWidgetCoordinates(widgetAsGraph->description.coords, imageExtents,
496 enabledWidgets->coordinates[writeIndex]);
497 }
498 }
499 }
500
fillWidgetData(const gl::Extents & imageExtents,uint8_t * textData,uint8_t * graphData) const501 void OverlayState::fillWidgetData(const gl::Extents &imageExtents,
502 uint8_t *textData,
503 uint8_t *graphData) const
504 {
505 TextWidgets *textWidgets = reinterpret_cast<TextWidgets *>(textData);
506 GraphWidgets *graphWidgets = reinterpret_cast<GraphWidgets *>(graphData);
507
508 memset(textWidgets, overlay::kFontCharacters, sizeof(*textWidgets));
509 memset(graphWidgets, 0, sizeof(*graphWidgets));
510
511 OverlayWidgetCounts widgetCounts = {};
512
513 for (WidgetId id : angle::AllEnums<WidgetId>())
514 {
515 const std::unique_ptr<overlay::Widget> &widget = mOverlayWidgets[id];
516 if (!widget->enabled)
517 {
518 continue;
519 }
520
521 WidgetInternalType internalType = kWidgetTypeToInternalMap[widget->type];
522 ASSERT(internalType != WidgetInternalType::InvalidEnum);
523
524 if (widgetCounts[internalType] >= kWidgetInternalTypeMaxWidgets[internalType])
525 {
526 continue;
527 }
528
529 AppendWidgetDataFunc appendFunc = kWidgetIdToAppendDataFuncMap[id];
530 ASSERT(appendFunc);
531 appendFunc(widget.get(), imageExtents,
532 &textWidgets->widgets[widgetCounts[WidgetInternalType::Text]],
533 &graphWidgets->widgets[widgetCounts[WidgetInternalType::Graph]], &widgetCounts);
534 }
535 }
536
537 } // namespace gl
538