add a use_alsa gyp setting
[chromium-blink-merge.git] / cc / heads_up_display_layer_impl.cc
blob661fb8a0f5afe2f2d5fa94945653c5bd3886c971
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/heads_up_display_layer_impl.h"
7 #include "base/string_split.h"
8 #include "base/stringprintf.h"
9 #include "cc/debug_colors.h"
10 #include "cc/debug_rect_history.h"
11 #include "cc/frame_rate_counter.h"
12 #include "cc/layer_tree_impl.h"
13 #include "cc/memory_history.h"
14 #include "cc/paint_time_counter.h"
15 #include "cc/quad_sink.h"
16 #include "cc/renderer.h"
17 #include "cc/texture_draw_quad.h"
18 #include "cc/tile_manager.h"
19 #include "skia/ext/platform_canvas.h"
20 #include "skia/ext/platform_canvas.h"
21 #include "third_party/khronos/GLES2/gl2.h"
22 #include "third_party/khronos/GLES2/gl2ext.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24 #include "third_party/skia/include/core/SkFontHost.h"
25 #include "third_party/skia/include/core/SkPaint.h"
26 #include "third_party/skia/include/core/SkTypeface.h"
27 #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
28 #include "ui/gfx/point.h"
29 #include "ui/gfx/size.h"
31 namespace cc {
33 static inline SkPaint createPaint()
35 SkPaint paint;
36 #if (SK_R32_SHIFT || SK_B32_SHIFT != 16)
37 // The SkCanvas is in RGBA but the shader is expecting BGRA, so we need to
38 // swizzle our colors when drawing to the SkCanvas.
39 SkColorMatrix swizzleMatrix;
40 for (int i = 0; i < 20; ++i)
41 swizzleMatrix.fMat[i] = 0;
42 swizzleMatrix.fMat[0 + 5 * 2] = 1;
43 swizzleMatrix.fMat[1 + 5 * 1] = 1;
44 swizzleMatrix.fMat[2 + 5 * 0] = 1;
45 swizzleMatrix.fMat[3 + 5 * 3] = 1;
47 skia::RefPtr<SkColorMatrixFilter> filter =
48 skia::AdoptRef(new SkColorMatrixFilter(swizzleMatrix));
49 paint.setColorFilter(filter.get());
50 #endif
51 return paint;
54 HeadsUpDisplayLayerImpl::Graph::Graph(double indicatorValue, double startUpperBound)
55 : value(0)
56 , min(0)
57 , max(0)
58 , currentUpperBound(startUpperBound)
59 , defaultUpperBound(startUpperBound)
60 , indicator(indicatorValue)
64 double HeadsUpDisplayLayerImpl::Graph::updateUpperBound(Graph* graph)
66 double targetUpperBound = std::max(graph->max, graph->defaultUpperBound);
67 graph->currentUpperBound += (targetUpperBound - graph->currentUpperBound) * 0.5;
68 return graph->currentUpperBound;
71 HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeImpl* treeImpl, int id)
72 : LayerImpl(treeImpl, id)
73 , m_fpsGraph(60.0, 80.0)
74 , m_paintTimeGraph(16.0, 48.0)
75 , m_typeface(skia::AdoptRef(SkFontHost::CreateTypeface(NULL, "monospace", SkTypeface::kBold)))
79 HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl()
83 scoped_ptr<LayerImpl> HeadsUpDisplayLayerImpl::createLayerImpl(LayerTreeImpl* treeImpl)
85 return HeadsUpDisplayLayerImpl::create(treeImpl, id()).PassAs<LayerImpl>();
88 void HeadsUpDisplayLayerImpl::willDraw(ResourceProvider* resourceProvider)
90 LayerImpl::willDraw(resourceProvider);
92 if (!m_hudTexture)
93 m_hudTexture = ScopedResource::create(resourceProvider);
95 // TODO(danakj): Scale the HUD by deviceScale to make it more friendly under high DPI.
97 // TODO(danakj): The HUD could swap between two textures instead of creating a texture every frame in ubercompositor.
98 if (m_hudTexture->size() != bounds() || resourceProvider->inUseByConsumer(m_hudTexture->id()))
99 m_hudTexture->Free();
101 if (!m_hudTexture->id()) {
102 m_hudTexture->Allocate(bounds(), GL_RGBA, ResourceProvider::TextureUsageAny);
103 // TODO(epenner): This texture was being used before setPixels was called,
104 // which is now not allowed (it's an uninitialized read). This should be fixed
105 // and this allocateForTesting() removed.
106 // http://crbug.com/166784
107 resourceProvider->allocateForTesting(m_hudTexture->id());
111 void HeadsUpDisplayLayerImpl::appendQuads(QuadSink& quadSink, AppendQuadsData& appendQuadsData)
113 if (!m_hudTexture->id())
114 return;
116 SharedQuadState* sharedQuadState = quadSink.useSharedQuadState(createSharedQuadState());
118 gfx::Rect quadRect(gfx::Point(), bounds());
119 gfx::Rect opaqueRect(contentsOpaque() ? quadRect : gfx::Rect());
120 bool premultipliedAlpha = true;
121 gfx::PointF uv_top_left(0.f, 0.f);
122 gfx::PointF uv_bottom_right(1.f, 1.f);
123 const float vertex_opacity[] = {1.f, 1.f, 1.f, 1.f};
124 bool flipped = false;
125 scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
126 quad->SetNew(sharedQuadState, quadRect, opaqueRect, m_hudTexture->id(), premultipliedAlpha, uv_top_left, uv_bottom_right, vertex_opacity, flipped);
127 quadSink.append(quad.PassAs<DrawQuad>(), appendQuadsData);
130 void HeadsUpDisplayLayerImpl::updateHudTexture(ResourceProvider* resourceProvider)
132 if (!m_hudTexture->id())
133 return;
135 SkISize canvasSize;
136 if (m_hudCanvas)
137 canvasSize = m_hudCanvas->getDeviceSize();
138 else
139 canvasSize.set(0, 0);
141 if (canvasSize.fWidth != bounds().width() || canvasSize.fHeight != bounds().height() || !m_hudCanvas)
142 m_hudCanvas = make_scoped_ptr(skia::CreateBitmapCanvas(bounds().width(), bounds().height(), false /* opaque */));
144 m_hudCanvas->clear(SkColorSetARGB(0, 0, 0, 0));
145 drawHudContents(m_hudCanvas.get());
147 const SkBitmap* bitmap = &m_hudCanvas->getDevice()->accessBitmap(false);
148 SkAutoLockPixels locker(*bitmap);
150 gfx::Rect layerRect(gfx::Point(), bounds());
151 DCHECK(bitmap->config() == SkBitmap::kARGB_8888_Config);
152 resourceProvider->setPixels(m_hudTexture->id(), static_cast<const uint8_t*>(bitmap->getPixels()), layerRect, layerRect, gfx::Vector2d());
155 void HeadsUpDisplayLayerImpl::didDraw(ResourceProvider* resourceProvider)
157 LayerImpl::didDraw(resourceProvider);
159 if (!m_hudTexture->id())
160 return;
162 // FIXME: the following assert will not be true when sending resources to a
163 // parent compositor. We will probably need to hold on to m_hudTexture for
164 // longer, and have several HUD textures in the pipeline.
165 DCHECK(!resourceProvider->inUseByConsumer(m_hudTexture->id()));
168 void HeadsUpDisplayLayerImpl::didLoseOutputSurface()
170 m_hudTexture.reset();
173 bool HeadsUpDisplayLayerImpl::layerIsAlwaysDamaged() const
175 return true;
178 void HeadsUpDisplayLayerImpl::drawHudContents(SkCanvas* canvas)
180 const LayerTreeDebugState& debugState = layerTreeImpl()->debug_state();
182 if (debugState.showPlatformLayerTree)
183 drawPlaformLayerTree(canvas);
185 if (debugState.continuousPainting || debugState.showFPSCounter) {
186 FrameRateCounter* fpsCounter = layerTreeImpl()->frame_rate_counter();
187 PaintTimeCounter* paintTimeCounter = layerTreeImpl()->paint_time_counter();
189 // Update numbers not every frame so text is readable
190 base::TimeTicks now = base::TimeTicks::Now();
191 if (base::TimeDelta(now - m_timeOfLastGraphUpdate).InSecondsF() > 0.25) {
192 m_fpsGraph.value = fpsCounter->getAverageFPS();
193 fpsCounter->getMinAndMaxFPS(m_fpsGraph.min, m_fpsGraph.max);
195 base::TimeDelta latest, min, max;
196 if (paintTimeCounter->End())
197 latest = paintTimeCounter->End()->total_time();
198 paintTimeCounter->GetMinAndMaxPaintTime(&min, &max);
200 m_paintTimeGraph.value = latest.InMillisecondsF();
201 m_paintTimeGraph.min = min.InMillisecondsF();
202 m_paintTimeGraph.max = max.InMillisecondsF();
204 m_timeOfLastGraphUpdate = now;
207 int top = 2;
208 if (debugState.continuousPainting)
209 top = drawPaintTimeDisplay(canvas, paintTimeCounter, top);
210 // Don't show the FPS display when continuous painting is enabled, because it would show misleading numbers.
211 else if (debugState.showFPSCounter)
212 top = drawFPSDisplay(canvas, fpsCounter, top);
214 drawMemoryDisplay(canvas, layerTreeImpl()->memory_history(), top);
217 if (debugState.showHudRects())
218 drawDebugRects(canvas, layerTreeImpl()->debug_rect_history());
221 void HeadsUpDisplayLayerImpl::drawText(SkCanvas* canvas, SkPaint* paint, const std::string& text, const SkPaint::Align& align, const int& size, const int& x, const int& y)
223 const bool antiAlias = paint->isAntiAlias();
224 paint->setAntiAlias(true);
226 paint->setTextSize(size);
227 paint->setTextAlign(align);
228 paint->setTypeface(m_typeface.get());
229 canvas->drawText(text.c_str(), text.length(), x, y, *paint);
231 paint->setAntiAlias(antiAlias);
234 void HeadsUpDisplayLayerImpl::drawText(SkCanvas* canvas, SkPaint* paint, const std::string& text, const SkPaint::Align& align, const int& size, const SkPoint& pos)
236 drawText(canvas, paint, text, align, size, pos.x(), pos.y());
239 void HeadsUpDisplayLayerImpl::drawGraphBackground(SkCanvas* canvas, SkPaint* paint, const SkRect& bounds)
241 paint->setColor(SkColorSetARGB(215, 17, 17, 17));
242 canvas->drawRect(bounds, *paint);
245 void HeadsUpDisplayLayerImpl::drawGraphLines(SkCanvas* canvas, SkPaint* paint, const SkRect& bounds, const Graph& graph)
247 // Draw top and bottom line.
248 paint->setColor(SkColorSetRGB(130, 130, 130));
249 canvas->drawLine(bounds.left(), bounds.top() - 1, bounds.right(), bounds.top() - 1, *paint);
250 canvas->drawLine(bounds.left(), bounds.bottom(), bounds.right(), bounds.bottom(), *paint);
252 // Draw indicator line.
253 paint->setColor(SkColorSetRGB(100, 100, 100));
254 const double indicatorTop = bounds.height() * (1 - graph.indicator / graph.currentUpperBound) - 1;
255 canvas->drawLine(bounds.left(), bounds.top() + indicatorTop, bounds.right(), bounds.top() + indicatorTop, *paint);
258 void HeadsUpDisplayLayerImpl::drawPlaformLayerTree(SkCanvas* canvas)
260 const int fontHeight = 14;
261 SkPaint paint = createPaint();
262 drawGraphBackground(canvas, &paint, SkRect::MakeXYWH(0, 0, bounds().width(), bounds().height()));
264 std::string layerTree = layerTreeImpl()->layer_tree_as_text();
265 std::vector<std::string> lines;
266 base::SplitString(layerTree, '\n', &lines);
268 paint.setColor(DebugColors::PlatformLayerTreeTextColor());
269 for (size_t i = 0; i < lines.size() && static_cast<int>(2 + i * fontHeight) < bounds().height(); ++i) {
270 drawText(canvas, &paint, lines[i], SkPaint::kLeft_Align, fontHeight, 2, 2 + (i + 1) * fontHeight);
274 int HeadsUpDisplayLayerImpl::drawFPSDisplay(SkCanvas* canvas, FrameRateCounter* fpsCounter, const int& top)
276 const int padding = 4;
277 const int gap = 6;
279 const int fontHeight = 15;
281 const int graphWidth = fpsCounter->timeStampHistorySize() - 2;
282 const int graphHeight = 40;
284 const int histogramWidth = 37;
286 const int width = graphWidth + histogramWidth + 4 * padding;
287 const int height = fontHeight + graphHeight + 4 * padding + 2;
289 const int left = bounds().width() - width - 2;
291 SkPaint paint = createPaint();
292 drawGraphBackground(canvas, &paint, SkRect::MakeXYWH(left, top, width, height));
294 SkRect textBounds = SkRect::MakeXYWH(left + padding, top + padding, graphWidth + histogramWidth + gap + 2, fontHeight);
295 SkRect graphBounds = SkRect::MakeXYWH(left + padding, textBounds.bottom() + 2 * padding, graphWidth, graphHeight);
296 SkRect histogramBounds = SkRect::MakeXYWH(graphBounds.right() + gap, graphBounds.top(), histogramWidth, graphHeight);
298 const std::string valueText = base::StringPrintf("FPS:%5.1f", m_fpsGraph.value);
299 const std::string minMaxText = base::StringPrintf("%.0f-%.0f", m_fpsGraph.min, m_fpsGraph.max);
301 paint.setColor(DebugColors::FPSDisplayTextAndGraphColor());
302 drawText(canvas, &paint, valueText, SkPaint::kLeft_Align, fontHeight, textBounds.left(), textBounds.bottom());
303 drawText(canvas, &paint, minMaxText, SkPaint::kRight_Align, fontHeight, textBounds.right(), textBounds.bottom());
305 const double upperBound = Graph::updateUpperBound(&m_fpsGraph);
306 drawGraphLines(canvas, &paint, graphBounds, m_fpsGraph);
308 // Collect graph and histogram data.
309 SkPath path;
311 const int histogramSize = 20;
312 double histogram[histogramSize] = {0};
313 double maxBucketValue = 0;
315 for (FrameRateCounter::RingBufferType::Iterator it = --fpsCounter->end(); it; --it) {
316 base::TimeDelta delta = fpsCounter->recentFrameInterval(it.index() + 1);
318 // Skip this particular instantaneous frame rate if it is not likely to have been valid.
319 if (!fpsCounter->isBadFrameInterval(delta)) {
321 double fps = 1.0 / delta.InSecondsF();
323 // Clamp the FPS to the range we want to plot visually.
324 double p = fps / upperBound;
325 if (p > 1)
326 p = 1;
328 // Plot this data point.
329 SkPoint cur = SkPoint::Make(graphBounds.left() + it.index(), graphBounds.bottom() - p * graphBounds.height());
330 if (path.isEmpty())
331 path.moveTo(cur);
332 else
333 path.lineTo(cur);
335 // Use the fps value to find the right bucket in the histogram.
336 int bucketIndex = floor(p * (histogramSize - 1));
338 // Add the delta time to take the time spent at that fps rate into account.
339 histogram[bucketIndex] += delta.InSecondsF();
340 maxBucketValue = std::max(histogram[bucketIndex], maxBucketValue);
344 // Draw FPS histogram.
345 paint.setColor(SkColorSetRGB(130, 130, 130));
346 canvas->drawLine(histogramBounds.left() - 1, histogramBounds.top() - 1, histogramBounds.left() - 1, histogramBounds.bottom() + 1, paint);
347 canvas->drawLine(histogramBounds.right() + 1, histogramBounds.top() - 1, histogramBounds.right() + 1, histogramBounds.bottom() + 1, paint);
349 paint.setColor(DebugColors::FPSDisplayTextAndGraphColor());
350 const double barHeight = histogramBounds.height() / histogramSize;
352 for (int i = histogramSize - 1; i >= 0; --i) {
353 if (histogram[i] > 0) {
354 double barWidth = histogram[i] / maxBucketValue * histogramBounds.width();
355 canvas->drawRect(SkRect::MakeXYWH(histogramBounds.left(), histogramBounds.bottom() - (i + 1) * barHeight, barWidth, 1), paint);
359 // Draw FPS graph.
360 paint.setAntiAlias(true);
361 paint.setStyle(SkPaint::kStroke_Style);
362 paint.setStrokeWidth(1);
364 canvas->drawPath(path, paint);
366 return top + height + 2;
369 int HeadsUpDisplayLayerImpl::drawMemoryDisplay(SkCanvas* canvas, MemoryHistory* memoryHistory, const int& initial_top)
371 // Don't draw the display if there is no data in it.
372 if (!memoryHistory->End())
373 return initial_top;
375 const MemoryHistory::Entry curEntry = **memoryHistory->End();
377 // Move up by 2 to create no gap between us and previous counter.
378 const int top = initial_top - 2;
379 const int padding = 4;
380 const int fontHeight = 11;
382 const int width = 187;
383 const int height = 3 * fontHeight + 4 * padding;
385 const int left = bounds().width() - width - 2;
386 const double megabyte = static_cast<double>(1024*1024);
388 SkPaint paint = createPaint();
389 drawGraphBackground(canvas, &paint, SkRect::MakeXYWH(left, top, width, height));
391 SkPoint titlePos = SkPoint::Make(left + padding, top + fontHeight);
392 SkPoint stat1Pos = SkPoint::Make(left + width - padding - 1, top + padding + 2 * fontHeight);
393 SkPoint stat2Pos = SkPoint::Make(left + width - padding - 1, top + 2 * padding + 3 * fontHeight);
395 paint.setColor(SkColorSetRGB(220, 220, 220));
396 drawText(canvas, &paint, "GPU memory", SkPaint::kLeft_Align, fontHeight, titlePos);
398 std::string text = base::StringPrintf(
399 "%6.1f MB used",
400 (curEntry.bytes_unreleasable + curEntry.bytes_allocated) / megabyte);
401 drawText(canvas, &paint, text, SkPaint::kRight_Align, fontHeight, stat1Pos);
403 if (curEntry.bytes_over) {
404 paint.setColor(SK_ColorRED);
405 text = base::StringPrintf("%6.1f MB over",
406 curEntry.bytes_over / megabyte);
407 } else {
408 text = base::StringPrintf("%6.1f MB max ",
409 curEntry.total_budget_in_bytes / megabyte);
411 drawText(canvas, &paint, text, SkPaint::kRight_Align, fontHeight, stat2Pos);
413 return top + height + 2;
416 int HeadsUpDisplayLayerImpl::drawPaintTimeDisplay(SkCanvas* canvas, PaintTimeCounter* paintTimeCounter, const int& top)
418 const int padding = 4;
419 const int fontHeight = 15;
421 const int graphWidth = paintTimeCounter->HistorySize() * 2 - 1;
422 const int graphHeight = 40;
424 const int width = graphWidth + 2 * padding;
425 const int height = fontHeight + graphHeight + 4 * padding + 2 + fontHeight + padding;
427 const int left = bounds().width() - width - 2;
429 SkPaint paint = createPaint();
430 drawGraphBackground(canvas, &paint, SkRect::MakeXYWH(left, top, width, height));
432 SkRect textBounds = SkRect::MakeXYWH(left + padding, top + padding, graphWidth, fontHeight);
433 SkRect textBounds2 = SkRect::MakeXYWH(left + padding, textBounds.bottom() + padding, graphWidth, fontHeight);
434 SkRect graphBounds = SkRect::MakeXYWH(left + padding, textBounds2.bottom() + 2 * padding, graphWidth, graphHeight);
436 const std::string valueText = base::StringPrintf("%.1f", m_paintTimeGraph.value);
437 const std::string minMaxText = base::StringPrintf("%.1f-%.1f", m_paintTimeGraph.min, m_paintTimeGraph.max);
439 paint.setColor(DebugColors::PaintTimeDisplayTextAndGraphColor());
440 drawText(canvas, &paint, "Page paint time (ms)", SkPaint::kLeft_Align, fontHeight, textBounds.left(), textBounds.bottom());
441 drawText(canvas, &paint, valueText, SkPaint::kLeft_Align, fontHeight, textBounds2.left(), textBounds2.bottom());
442 drawText(canvas, &paint, minMaxText, SkPaint::kRight_Align, fontHeight, textBounds2.right(), textBounds2.bottom());
444 const double upperBound = Graph::updateUpperBound(&m_paintTimeGraph);
445 drawGraphLines(canvas, &paint, graphBounds, m_paintTimeGraph);
447 paint.setColor(DebugColors::PaintTimeDisplayTextAndGraphColor());
448 for (PaintTimeCounter::RingBufferType::Iterator it = paintTimeCounter->End(); it; --it) {
449 double pt = it->total_time().InMillisecondsF();
451 if (pt == 0.0)
452 continue;
454 double p = pt / upperBound;
455 if (p > 1)
456 p = 1;
458 canvas->drawRect(SkRect::MakeXYWH(graphBounds.left() + it.index() * 2, graphBounds.bottom() - p * graphBounds.height(), 1, p * graphBounds.height()), paint);
461 return top + height + 2;
464 void HeadsUpDisplayLayerImpl::drawDebugRects(SkCanvas* canvas, DebugRectHistory* debugRectHistory)
466 const std::vector<DebugRect>& debugRects = debugRectHistory->debugRects();
467 float rectScale = 1 / layerTreeImpl()->device_scale_factor();
469 canvas->save();
470 canvas->scale(rectScale, rectScale);
472 for (size_t i = 0; i < debugRects.size(); ++i) {
473 SkColor strokeColor = 0;
474 SkColor fillColor = 0;
475 float strokeWidth = 0;
477 switch (debugRects[i].type) {
478 case PaintRectType:
479 strokeColor = DebugColors::PaintRectBorderColor();
480 fillColor = DebugColors::PaintRectFillColor();
481 strokeWidth = DebugColors::PaintRectBorderWidth(layerTreeImpl());
482 break;
483 case PropertyChangedRectType:
484 strokeColor = DebugColors::PropertyChangedRectBorderColor();
485 fillColor = DebugColors::PropertyChangedRectFillColor();
486 strokeWidth = DebugColors::PropertyChangedRectBorderWidth(layerTreeImpl());
487 break;
488 case SurfaceDamageRectType:
489 strokeColor = DebugColors::SurfaceDamageRectBorderColor();
490 fillColor = DebugColors::SurfaceDamageRectFillColor();
491 strokeWidth = DebugColors::SurfaceDamageRectBorderWidth(layerTreeImpl());
492 break;
493 case ReplicaScreenSpaceRectType:
494 strokeColor = DebugColors::ScreenSpaceSurfaceReplicaRectBorderColor();
495 fillColor = DebugColors::ScreenSpaceSurfaceReplicaRectFillColor();
496 strokeWidth = DebugColors::ScreenSpaceSurfaceReplicaRectBorderWidth(layerTreeImpl());
497 break;
498 case ScreenSpaceRectType:
499 strokeColor = DebugColors::ScreenSpaceLayerRectBorderColor();
500 fillColor = DebugColors::ScreenSpaceLayerRectFillColor();
501 strokeWidth = DebugColors::ScreenSpaceLayerRectBorderWidth(layerTreeImpl());
502 break;
503 case OccludingRectType:
504 strokeColor = DebugColors::OccludingRectBorderColor();
505 fillColor = DebugColors::OccludingRectFillColor();
506 strokeWidth = DebugColors::OccludingRectBorderWidth(layerTreeImpl());
507 break;
508 case NonOccludingRectType:
509 strokeColor = DebugColors::NonOccludingRectBorderColor();
510 fillColor = DebugColors::NonOccludingRectFillColor();
511 strokeWidth = DebugColors::NonOccludingRectBorderWidth(layerTreeImpl());
512 break;
515 const gfx::RectF& rect = debugRects[i].rect;
516 SkRect skRect = SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height());
517 SkPaint paint = createPaint();
518 paint.setColor(fillColor);
519 canvas->drawRect(skRect, paint);
521 paint.setColor(strokeColor);
522 paint.setStyle(SkPaint::kStroke_Style);
523 paint.setStrokeWidth(SkFloatToScalar(strokeWidth));
524 canvas->drawRect(skRect, paint);
527 canvas->restore();
530 const char* HeadsUpDisplayLayerImpl::layerTypeAsString() const
532 return "HeadsUpDisplayLayer";
535 } // namespace cc