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"
9 #include "base/stringprintf.h"
10 #include "cc/debug_colors.h"
11 #include "cc/debug_rect_history.h"
12 #include "cc/font_atlas.h"
13 #include "cc/frame_rate_counter.h"
14 #include "cc/layer_tree_host_impl.h"
15 #include "cc/quad_sink.h"
16 #include "cc/texture_draw_quad.h"
17 #include "skia/ext/platform_canvas.h"
18 #include "skia/ext/platform_canvas.h"
19 #include "third_party/khronos/GLES2/gl2.h"
20 #include "third_party/khronos/GLES2/gl2ext.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "third_party/skia/include/core/SkPaint.h"
23 #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
24 #include "ui/gfx/point.h"
25 #include "ui/gfx/size.h"
29 static inline SkPaint
createPaint()
31 // The SkCanvas is in RGBA but the shader is expecting BGRA, so we need to
32 // swizzle our colors when drawing to the SkCanvas.
33 SkColorMatrix swizzleMatrix
;
34 for (int i
= 0; i
< 20; ++i
)
35 swizzleMatrix
.fMat
[i
] = 0;
36 swizzleMatrix
.fMat
[0 + 5 * 2] = 1;
37 swizzleMatrix
.fMat
[1 + 5 * 1] = 1;
38 swizzleMatrix
.fMat
[2 + 5 * 0] = 1;
39 swizzleMatrix
.fMat
[3 + 5 * 3] = 1;
42 paint
.setColorFilter(new SkColorMatrixFilter(swizzleMatrix
))->unref();
47 HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(int id
)
55 HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl()
59 void HeadsUpDisplayLayerImpl::setFontAtlas(scoped_ptr
<FontAtlas
> fontAtlas
)
61 m_fontAtlas
= fontAtlas
.Pass();
64 void HeadsUpDisplayLayerImpl::willDraw(ResourceProvider
* resourceProvider
)
66 LayerImpl::willDraw(resourceProvider
);
69 m_hudTexture
= ScopedResource::create(resourceProvider
);
71 // FIXME: Scale the HUD by deviceScale to make it more friendly under high DPI.
73 if (m_hudTexture
->size() != bounds())
76 if (!m_hudTexture
->id())
77 m_hudTexture
->Allocate(Renderer::ImplPool
, bounds(), GL_RGBA
, ResourceProvider::TextureUsageAny
);
80 void HeadsUpDisplayLayerImpl::appendQuads(QuadSink
& quadSink
, AppendQuadsData
& appendQuadsData
)
82 if (!m_hudTexture
->id())
85 SharedQuadState
* sharedQuadState
= quadSink
.useSharedQuadState(createSharedQuadState());
87 gfx::Rect
quadRect(gfx::Point(), bounds());
88 gfx::Rect
opaqueRect(contentsOpaque() ? quadRect
: gfx::Rect());
89 bool premultipliedAlpha
= true;
90 gfx::RectF
uvRect(0, 0, 1, 1);
92 scoped_ptr
<TextureDrawQuad
> quad
= TextureDrawQuad::Create();
93 quad
->SetNew(sharedQuadState
, quadRect
, opaqueRect
, m_hudTexture
->id(), premultipliedAlpha
, uvRect
, flipped
);
94 quadSink
.append(quad
.PassAs
<DrawQuad
>(), appendQuadsData
);
97 void HeadsUpDisplayLayerImpl::updateHudTexture(ResourceProvider
* resourceProvider
)
99 if (!m_hudTexture
->id())
104 canvasSize
= m_hudCanvas
->getDeviceSize();
106 canvasSize
.set(0, 0);
108 if (canvasSize
.fWidth
!= bounds().width() || canvasSize
.fHeight
!= bounds().height() || !m_hudCanvas
)
109 m_hudCanvas
= make_scoped_ptr(skia::CreateBitmapCanvas(bounds().width(), bounds().height(), false /* opaque */));
111 m_hudCanvas
->clear(SkColorSetARGB(0, 0, 0, 0));
112 drawHudContents(m_hudCanvas
.get());
114 const SkBitmap
* bitmap
= &m_hudCanvas
->getDevice()->accessBitmap(false);
115 SkAutoLockPixels
locker(*bitmap
);
117 gfx::Rect
layerRect(gfx::Point(), bounds());
118 DCHECK(bitmap
->config() == SkBitmap::kARGB_8888_Config
);
119 resourceProvider
->setPixels(m_hudTexture
->id(), static_cast<const uint8_t*>(bitmap
->getPixels()), layerRect
, layerRect
, gfx::Vector2d());
122 void HeadsUpDisplayLayerImpl::didDraw(ResourceProvider
* resourceProvider
)
124 LayerImpl::didDraw(resourceProvider
);
126 if (!m_hudTexture
->id())
129 // FIXME: the following assert will not be true when sending resources to a
130 // parent compositor. We will probably need to hold on to m_hudTexture for
131 // longer, and have several HUD textures in the pipeline.
132 DCHECK(!resourceProvider
->inUseByConsumer(m_hudTexture
->id()));
135 void HeadsUpDisplayLayerImpl::didLoseContext()
137 m_hudTexture
.reset();
140 bool HeadsUpDisplayLayerImpl::layerIsAlwaysDamaged() const
145 void HeadsUpDisplayLayerImpl::drawHudContents(SkCanvas
* canvas
)
147 const LayerTreeDebugState
& debugState
= layerTreeHostImpl()->debugState();
149 if (debugState
.showPlatformLayerTree
) {
150 SkPaint paint
= createPaint();
151 paint
.setColor(SkColorSetARGB(192, 0, 0, 0));
152 canvas
->drawRect(SkRect::MakeXYWH(0, 0, bounds().width(), bounds().height()), paint
);
155 int platformLayerTreeTop
= 0;
157 if (debugState
.showFPSCounter
)
158 platformLayerTreeTop
= drawFPSCounter(canvas
, layerTreeHostImpl()->fpsCounter());
160 if (debugState
.showPlatformLayerTree
&& m_fontAtlas
) {
161 std::string layerTree
= layerTreeHostImpl()->layerTreeAsText();
162 m_fontAtlas
->drawText(canvas
, createPaint(), layerTree
, gfx::Point(2, platformLayerTreeTop
), bounds());
165 if (debugState
.showHudRects())
166 drawDebugRects(canvas
, layerTreeHostImpl()->debugRectHistory());
169 int HeadsUpDisplayLayerImpl::drawFPSCounter(SkCanvas
* canvas
, FrameRateCounter
* fpsCounter
)
171 const int padding
= 4;
174 const int fontHeight
= m_fontAtlas
.get() ? m_fontAtlas
->fontHeight() : 0;
176 const int graphWidth
= 120;
177 const int graphHeight
= 40;
179 const int histogramWidth
= 37;
181 const int width
= graphWidth
+ histogramWidth
+ 4 * padding
;
182 const int height
= fontHeight
+ graphHeight
+ 4 * padding
+ 2;
184 const int left
= bounds().width() - width
- 2;
187 SkPaint paint
= createPaint();
190 paint
.setColor(SkColorSetARGB(215, 17, 17, 17));
191 canvas
->drawRect(SkRect::MakeXYWH(left
, top
, width
, height
), paint
);
193 SkRect textBounds
= SkRect::MakeXYWH(left
+ padding
, top
+ padding
, graphWidth
+ histogramWidth
+ gap
+ 2, fontHeight
);
194 SkRect graphBounds
= SkRect::MakeXYWH(left
+ padding
, textBounds
.bottom() + 2 * padding
, graphWidth
, graphHeight
);
195 SkRect histogramBounds
= SkRect::MakeXYWH(graphBounds
.right() + gap
, graphBounds
.top(), histogramWidth
, graphHeight
);
197 drawFPSCounterText(canvas
, paint
, fpsCounter
, textBounds
);
198 drawFPSCounterGraphAndHistogram(canvas
, paint
, fpsCounter
, graphBounds
, histogramBounds
);
203 void HeadsUpDisplayLayerImpl::drawFPSCounterText(SkCanvas
* canvas
, SkPaint
& paint
, FrameRateCounter
* fpsCounter
, SkRect bounds
)
205 // Update FPS text - not every frame so text is readable
206 if (base::TimeDelta(fpsCounter
->timeStampOfRecentFrame(0) - textUpdateTime
).InSecondsF() > 0.25) {
207 m_averageFPS
= fpsCounter
->getAverageFPS();
208 textUpdateTime
= fpsCounter
->timeStampOfRecentFrame(0);
212 if (m_fontAtlas
.get()) {
213 std::string fpsText
= base::StringPrintf("FPS:%5.1f", m_averageFPS
);
214 std::string minMaxText
= base::StringPrintf("%.0f-%.0f", std::min( m_minFPS
, m_maxFPS
), m_maxFPS
);
216 int minMaxWidth
= m_fontAtlas
->textSize(minMaxText
).width();
217 gfx::Size
textArea(bounds
.width(), bounds
.height());
219 paint
.setColor(SK_ColorRED
);
220 m_fontAtlas
->drawText(canvas
, paint
, fpsText
, gfx::Point(bounds
.left(), bounds
.top()), textArea
);
221 m_fontAtlas
->drawText(canvas
, paint
, minMaxText
, gfx::Point(bounds
.right() - minMaxWidth
, bounds
.top()), textArea
);
225 void HeadsUpDisplayLayerImpl::drawFPSCounterGraphAndHistogram(SkCanvas
* canvas
, SkPaint
& paint
, FrameRateCounter
* fpsCounter
, SkRect graphBounds
, SkRect histogramBounds
)
227 const double loFPS
= 0;
228 const double hiFPS
= std::max(m_maxFPS
+ 10.0, 80.0);
230 // Draw top and bottom line.
231 paint
.setColor(SkColorSetRGB(130, 130, 130));
232 canvas
->drawLine(graphBounds
.left(), graphBounds
.top() - 1, graphBounds
.right(), graphBounds
.top() - 1, paint
);
233 canvas
->drawLine(graphBounds
.left(), graphBounds
.bottom(), graphBounds
.right(), graphBounds
.bottom(), paint
);
236 const double top60
= graphBounds
.top() + graphBounds
.height() * (1 - ((60 - loFPS
) / (hiFPS
- loFPS
))) - 1;
237 paint
.setColor(SkColorSetRGB(100, 100, 100));
238 canvas
->drawLine(graphBounds
.left(), top60
, graphBounds
.right(), top60
, paint
);
240 // Collect graph and histogram data.
242 const double timeScale
= 60; // in pixels/second
245 m_minFPS
= std::numeric_limits
<double>::max();
248 const int histogramSize
= 20;
249 double histogram
[histogramSize
] = {0};
250 double maxBucketValue
= 0;
252 for (int i
= fpsCounter
->timeStampHistorySize() - 2; i
> 0 && x
<= graphBounds
.width(); --i
) {
253 base::TimeDelta delta
= fpsCounter
->timeStampOfRecentFrame(i
+ 1) - fpsCounter
->timeStampOfRecentFrame(i
);
255 // Skip this particular instantaneous frame rate if it is not likely to have been valid.
256 if (!fpsCounter
->isBadFrameInterval(delta
)) {
258 double fps
= 1.0 / delta
.InSecondsF();
260 m_minFPS
= std::min(fps
, m_minFPS
);
261 m_maxFPS
= std::max(fps
, m_maxFPS
);
263 // Clamp the FPS to the range we want to plot visually.
264 double p
= (fps
- loFPS
) / (hiFPS
- loFPS
);
270 // Plot this data point.
271 SkPoint cur
= SkPoint::Make(graphBounds
.right() - x
, graphBounds
.bottom() - p
* graphBounds
.height());
277 // Use the fps value to find the right bucket in the histogram.
278 int bucketIndex
= floor(p
* (histogramSize
- 1));
280 // Add the delta time to take the time spent at that fps rate into account.
281 histogram
[bucketIndex
] += delta
.InSecondsF();
282 maxBucketValue
= std::max(histogram
[bucketIndex
], maxBucketValue
);
285 x
+= delta
.InSecondsF() * timeScale
;
288 // Draw FPS histogram.
289 paint
.setColor(SkColorSetRGB(130, 130, 130));
290 canvas
->drawLine(histogramBounds
.left() - 1, histogramBounds
.top() - 1, histogramBounds
.left() - 1, histogramBounds
.bottom() + 1, paint
);
291 canvas
->drawLine(histogramBounds
.right() + 1, histogramBounds
.top() - 1, histogramBounds
.right() + 1, histogramBounds
.bottom() + 1, paint
);
293 paint
.setColor(SK_ColorRED
);
294 const double barHeight
= histogramBounds
.height() / histogramSize
;
296 for (int i
= histogramSize
- 1; i
>= 0; --i
) {
297 if (histogram
[i
] > 0) {
298 double barWidth
= histogram
[i
] / maxBucketValue
* histogramBounds
.width();
299 canvas
->drawRect(SkRect::MakeXYWH(histogramBounds
.left(), histogramBounds
.bottom() - (i
+ 1) * barHeight
, barWidth
, 1), paint
);
304 paint
.setAntiAlias(true);
305 paint
.setStyle(SkPaint::kStroke_Style
);
306 paint
.setStrokeWidth(1);
308 canvas
->drawPath(path
, paint
);
311 void HeadsUpDisplayLayerImpl::drawDebugRects(SkCanvas
* canvas
, DebugRectHistory
* debugRectHistory
)
313 const std::vector
<DebugRect
>& debugRects
= debugRectHistory
->debugRects();
314 float rectScale
= 1 / layerTreeHostImpl()->deviceScaleFactor();
317 canvas
->scale(rectScale
, rectScale
);
319 for (size_t i
= 0; i
< debugRects
.size(); ++i
) {
320 SkColor strokeColor
= 0;
321 SkColor fillColor
= 0;
322 float strokeWidth
= 0;
324 switch (debugRects
[i
].type
) {
326 strokeColor
= DebugColors::PaintRectBorderColor();
327 fillColor
= DebugColors::PaintRectFillColor();
328 strokeWidth
= DebugColors::PaintRectBorderWidth(layerTreeHostImpl());
330 case PropertyChangedRectType
:
331 strokeColor
= DebugColors::PropertyChangedRectBorderColor();
332 fillColor
= DebugColors::PropertyChangedRectFillColor();
333 strokeWidth
= DebugColors::PropertyChangedRectBorderWidth(layerTreeHostImpl());
335 case SurfaceDamageRectType
:
336 strokeColor
= DebugColors::SurfaceDamageRectBorderColor();
337 fillColor
= DebugColors::SurfaceDamageRectFillColor();
338 strokeWidth
= DebugColors::SurfaceDamageRectBorderWidth(layerTreeHostImpl());
340 case ReplicaScreenSpaceRectType
:
341 strokeColor
= DebugColors::ScreenSpaceSurfaceReplicaRectBorderColor();
342 fillColor
= DebugColors::ScreenSpaceSurfaceReplicaRectFillColor();
343 strokeWidth
= DebugColors::ScreenSpaceSurfaceReplicaRectBorderWidth(layerTreeHostImpl());
345 case ScreenSpaceRectType
:
346 strokeColor
= DebugColors::ScreenSpaceLayerRectBorderColor();
347 fillColor
= DebugColors::ScreenSpaceLayerRectFillColor();
348 strokeWidth
= DebugColors::ScreenSpaceLayerRectBorderWidth(layerTreeHostImpl());
350 case OccludingRectType
:
351 strokeColor
= DebugColors::OccludingRectBorderColor();
352 fillColor
= DebugColors::OccludingRectFillColor();
353 strokeWidth
= DebugColors::OccludingRectBorderWidth(layerTreeHostImpl());
355 case NonOccludingRectType
:
356 strokeColor
= DebugColors::NonOccludingRectBorderColor();
357 fillColor
= DebugColors::NonOccludingRectFillColor();
358 strokeWidth
= DebugColors::NonOccludingRectBorderWidth(layerTreeHostImpl());
362 const gfx::RectF
& rect
= debugRects
[i
].rect
;
363 SkRect skRect
= SkRect::MakeXYWH(rect
.x(), rect
.y(), rect
.width(), rect
.height());
364 SkPaint paint
= createPaint();
365 paint
.setColor(fillColor
);
366 canvas
->drawRect(skRect
, paint
);
368 paint
.setColor(strokeColor
);
369 paint
.setStyle(SkPaint::kStroke_Style
);
370 paint
.setStrokeWidth(SkFloatToScalar(strokeWidth
));
371 canvas
->drawRect(skRect
, paint
);
377 const char* HeadsUpDisplayLayerImpl::layerTypeAsString() const
379 return "HeadsUpDisplayLayer";