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 skia::RefPtr
<SkColorMatrixFilter
> filter
=
43 skia::AdoptRef(new SkColorMatrixFilter(swizzleMatrix
));
44 paint
.setColorFilter(filter
.get());
49 HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeHostImpl
* hostImpl
, int id
)
50 : LayerImpl(hostImpl
, id
)
57 HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl()
61 void HeadsUpDisplayLayerImpl::setFontAtlas(scoped_ptr
<FontAtlas
> fontAtlas
)
63 m_fontAtlas
= fontAtlas
.Pass();
66 void HeadsUpDisplayLayerImpl::willDraw(ResourceProvider
* resourceProvider
)
68 LayerImpl::willDraw(resourceProvider
);
71 m_hudTexture
= ScopedResource::create(resourceProvider
);
73 // FIXME: Scale the HUD by deviceScale to make it more friendly under high DPI.
75 if (m_hudTexture
->size() != bounds())
78 if (!m_hudTexture
->id())
79 m_hudTexture
->Allocate(Renderer::ImplPool
, bounds(), GL_RGBA
, ResourceProvider::TextureUsageAny
);
82 void HeadsUpDisplayLayerImpl::appendQuads(QuadSink
& quadSink
, AppendQuadsData
& appendQuadsData
)
84 if (!m_hudTexture
->id())
87 SharedQuadState
* sharedQuadState
= quadSink
.useSharedQuadState(createSharedQuadState());
89 gfx::Rect
quadRect(gfx::Point(), bounds());
90 gfx::Rect
opaqueRect(contentsOpaque() ? quadRect
: gfx::Rect());
91 bool premultipliedAlpha
= true;
92 gfx::RectF
uvRect(0, 0, 1, 1);
94 scoped_ptr
<TextureDrawQuad
> quad
= TextureDrawQuad::Create();
95 quad
->SetNew(sharedQuadState
, quadRect
, opaqueRect
, m_hudTexture
->id(), premultipliedAlpha
, uvRect
, flipped
);
96 quadSink
.append(quad
.PassAs
<DrawQuad
>(), appendQuadsData
);
99 void HeadsUpDisplayLayerImpl::updateHudTexture(ResourceProvider
* resourceProvider
)
101 if (!m_hudTexture
->id())
106 canvasSize
= m_hudCanvas
->getDeviceSize();
108 canvasSize
.set(0, 0);
110 if (canvasSize
.fWidth
!= bounds().width() || canvasSize
.fHeight
!= bounds().height() || !m_hudCanvas
)
111 m_hudCanvas
= make_scoped_ptr(skia::CreateBitmapCanvas(bounds().width(), bounds().height(), false /* opaque */));
113 m_hudCanvas
->clear(SkColorSetARGB(0, 0, 0, 0));
114 drawHudContents(m_hudCanvas
.get());
116 const SkBitmap
* bitmap
= &m_hudCanvas
->getDevice()->accessBitmap(false);
117 SkAutoLockPixels
locker(*bitmap
);
119 gfx::Rect
layerRect(gfx::Point(), bounds());
120 DCHECK(bitmap
->config() == SkBitmap::kARGB_8888_Config
);
121 resourceProvider
->setPixels(m_hudTexture
->id(), static_cast<const uint8_t*>(bitmap
->getPixels()), layerRect
, layerRect
, gfx::Vector2d());
124 void HeadsUpDisplayLayerImpl::didDraw(ResourceProvider
* resourceProvider
)
126 LayerImpl::didDraw(resourceProvider
);
128 if (!m_hudTexture
->id())
131 // FIXME: the following assert will not be true when sending resources to a
132 // parent compositor. We will probably need to hold on to m_hudTexture for
133 // longer, and have several HUD textures in the pipeline.
134 DCHECK(!resourceProvider
->inUseByConsumer(m_hudTexture
->id()));
137 void HeadsUpDisplayLayerImpl::didLoseOutputSurface()
139 m_hudTexture
.reset();
142 bool HeadsUpDisplayLayerImpl::layerIsAlwaysDamaged() const
147 void HeadsUpDisplayLayerImpl::drawHudContents(SkCanvas
* canvas
)
149 const LayerTreeDebugState
& debugState
= layerTreeHostImpl()->debugState();
151 if (debugState
.showPlatformLayerTree
) {
152 SkPaint paint
= createPaint();
153 paint
.setColor(SkColorSetARGB(192, 0, 0, 0));
154 canvas
->drawRect(SkRect::MakeXYWH(0, 0, bounds().width(), bounds().height()), paint
);
157 int platformLayerTreeTop
= 0;
159 if (debugState
.showFPSCounter
)
160 platformLayerTreeTop
= drawFPSCounter(canvas
, layerTreeHostImpl()->fpsCounter());
162 if (debugState
.showPlatformLayerTree
&& m_fontAtlas
) {
163 std::string layerTree
= layerTreeHostImpl()->layerTreeAsText();
164 m_fontAtlas
->drawText(canvas
, createPaint(), layerTree
, gfx::Point(2, platformLayerTreeTop
), bounds());
167 if (debugState
.showHudRects())
168 drawDebugRects(canvas
, layerTreeHostImpl()->debugRectHistory());
171 int HeadsUpDisplayLayerImpl::drawFPSCounter(SkCanvas
* canvas
, FrameRateCounter
* fpsCounter
)
173 const int padding
= 4;
176 const int fontHeight
= m_fontAtlas
.get() ? m_fontAtlas
->fontHeight() : 0;
178 const int graphWidth
= 120;
179 const int graphHeight
= 40;
181 const int histogramWidth
= 37;
183 const int width
= graphWidth
+ histogramWidth
+ 4 * padding
;
184 const int height
= fontHeight
+ graphHeight
+ 4 * padding
+ 2;
186 const int left
= bounds().width() - width
- 2;
189 SkPaint paint
= createPaint();
192 paint
.setColor(SkColorSetARGB(215, 17, 17, 17));
193 canvas
->drawRect(SkRect::MakeXYWH(left
, top
, width
, height
), paint
);
195 SkRect textBounds
= SkRect::MakeXYWH(left
+ padding
, top
+ padding
, graphWidth
+ histogramWidth
+ gap
+ 2, fontHeight
);
196 SkRect graphBounds
= SkRect::MakeXYWH(left
+ padding
, textBounds
.bottom() + 2 * padding
, graphWidth
, graphHeight
);
197 SkRect histogramBounds
= SkRect::MakeXYWH(graphBounds
.right() + gap
, graphBounds
.top(), histogramWidth
, graphHeight
);
199 drawFPSCounterText(canvas
, paint
, fpsCounter
, textBounds
);
200 drawFPSCounterGraphAndHistogram(canvas
, paint
, fpsCounter
, graphBounds
, histogramBounds
);
205 void HeadsUpDisplayLayerImpl::drawFPSCounterText(SkCanvas
* canvas
, SkPaint
& paint
, FrameRateCounter
* fpsCounter
, SkRect bounds
)
207 // Update FPS text - not every frame so text is readable
208 if (base::TimeDelta(fpsCounter
->timeStampOfRecentFrame(0) - textUpdateTime
).InSecondsF() > 0.25) {
209 m_averageFPS
= fpsCounter
->getAverageFPS();
210 textUpdateTime
= fpsCounter
->timeStampOfRecentFrame(0);
214 if (m_fontAtlas
.get()) {
215 std::string fpsText
= base::StringPrintf("FPS:%5.1f", m_averageFPS
);
216 std::string minMaxText
= base::StringPrintf("%.0f-%.0f", std::min( m_minFPS
, m_maxFPS
), m_maxFPS
);
218 int minMaxWidth
= m_fontAtlas
->textSize(minMaxText
).width();
219 gfx::Size
textArea(bounds
.width(), bounds
.height());
221 paint
.setColor(SK_ColorRED
);
222 m_fontAtlas
->drawText(canvas
, paint
, fpsText
, gfx::Point(bounds
.left(), bounds
.top()), textArea
);
223 m_fontAtlas
->drawText(canvas
, paint
, minMaxText
, gfx::Point(bounds
.right() - minMaxWidth
, bounds
.top()), textArea
);
227 void HeadsUpDisplayLayerImpl::drawFPSCounterGraphAndHistogram(SkCanvas
* canvas
, SkPaint
& paint
, FrameRateCounter
* fpsCounter
, SkRect graphBounds
, SkRect histogramBounds
)
229 const double loFPS
= 0;
230 const double hiFPS
= std::max(m_maxFPS
+ 10.0, 80.0);
232 // Draw top and bottom line.
233 paint
.setColor(SkColorSetRGB(130, 130, 130));
234 canvas
->drawLine(graphBounds
.left(), graphBounds
.top() - 1, graphBounds
.right(), graphBounds
.top() - 1, paint
);
235 canvas
->drawLine(graphBounds
.left(), graphBounds
.bottom(), graphBounds
.right(), graphBounds
.bottom(), paint
);
238 const double top60
= graphBounds
.top() + graphBounds
.height() * (1 - ((60 - loFPS
) / (hiFPS
- loFPS
))) - 1;
239 paint
.setColor(SkColorSetRGB(100, 100, 100));
240 canvas
->drawLine(graphBounds
.left(), top60
, graphBounds
.right(), top60
, paint
);
242 // Collect graph and histogram data.
244 const double timeScale
= 60; // in pixels/second
247 m_minFPS
= std::numeric_limits
<double>::max();
250 const int histogramSize
= 20;
251 double histogram
[histogramSize
] = {0};
252 double maxBucketValue
= 0;
254 for (int i
= fpsCounter
->timeStampHistorySize() - 2; i
> 0 && x
<= graphBounds
.width(); --i
) {
255 base::TimeDelta delta
= fpsCounter
->timeStampOfRecentFrame(i
+ 1) - fpsCounter
->timeStampOfRecentFrame(i
);
257 // Skip this particular instantaneous frame rate if it is not likely to have been valid.
258 if (!fpsCounter
->isBadFrameInterval(delta
)) {
260 double fps
= 1.0 / delta
.InSecondsF();
262 m_minFPS
= std::min(fps
, m_minFPS
);
263 m_maxFPS
= std::max(fps
, m_maxFPS
);
265 // Clamp the FPS to the range we want to plot visually.
266 double p
= (fps
- loFPS
) / (hiFPS
- loFPS
);
272 // Plot this data point.
273 SkPoint cur
= SkPoint::Make(graphBounds
.right() - x
, graphBounds
.bottom() - p
* graphBounds
.height());
279 // Use the fps value to find the right bucket in the histogram.
280 int bucketIndex
= floor(p
* (histogramSize
- 1));
282 // Add the delta time to take the time spent at that fps rate into account.
283 histogram
[bucketIndex
] += delta
.InSecondsF();
284 maxBucketValue
= std::max(histogram
[bucketIndex
], maxBucketValue
);
287 x
+= delta
.InSecondsF() * timeScale
;
290 // Draw FPS histogram.
291 paint
.setColor(SkColorSetRGB(130, 130, 130));
292 canvas
->drawLine(histogramBounds
.left() - 1, histogramBounds
.top() - 1, histogramBounds
.left() - 1, histogramBounds
.bottom() + 1, paint
);
293 canvas
->drawLine(histogramBounds
.right() + 1, histogramBounds
.top() - 1, histogramBounds
.right() + 1, histogramBounds
.bottom() + 1, paint
);
295 paint
.setColor(SK_ColorRED
);
296 const double barHeight
= histogramBounds
.height() / histogramSize
;
298 for (int i
= histogramSize
- 1; i
>= 0; --i
) {
299 if (histogram
[i
] > 0) {
300 double barWidth
= histogram
[i
] / maxBucketValue
* histogramBounds
.width();
301 canvas
->drawRect(SkRect::MakeXYWH(histogramBounds
.left(), histogramBounds
.bottom() - (i
+ 1) * barHeight
, barWidth
, 1), paint
);
306 paint
.setAntiAlias(true);
307 paint
.setStyle(SkPaint::kStroke_Style
);
308 paint
.setStrokeWidth(1);
310 canvas
->drawPath(path
, paint
);
313 void HeadsUpDisplayLayerImpl::drawDebugRects(SkCanvas
* canvas
, DebugRectHistory
* debugRectHistory
)
315 const std::vector
<DebugRect
>& debugRects
= debugRectHistory
->debugRects();
316 float rectScale
= 1 / layerTreeHostImpl()->deviceScaleFactor();
319 canvas
->scale(rectScale
, rectScale
);
321 for (size_t i
= 0; i
< debugRects
.size(); ++i
) {
322 SkColor strokeColor
= 0;
323 SkColor fillColor
= 0;
324 float strokeWidth
= 0;
326 switch (debugRects
[i
].type
) {
328 strokeColor
= DebugColors::PaintRectBorderColor();
329 fillColor
= DebugColors::PaintRectFillColor();
330 strokeWidth
= DebugColors::PaintRectBorderWidth(layerTreeHostImpl());
332 case PropertyChangedRectType
:
333 strokeColor
= DebugColors::PropertyChangedRectBorderColor();
334 fillColor
= DebugColors::PropertyChangedRectFillColor();
335 strokeWidth
= DebugColors::PropertyChangedRectBorderWidth(layerTreeHostImpl());
337 case SurfaceDamageRectType
:
338 strokeColor
= DebugColors::SurfaceDamageRectBorderColor();
339 fillColor
= DebugColors::SurfaceDamageRectFillColor();
340 strokeWidth
= DebugColors::SurfaceDamageRectBorderWidth(layerTreeHostImpl());
342 case ReplicaScreenSpaceRectType
:
343 strokeColor
= DebugColors::ScreenSpaceSurfaceReplicaRectBorderColor();
344 fillColor
= DebugColors::ScreenSpaceSurfaceReplicaRectFillColor();
345 strokeWidth
= DebugColors::ScreenSpaceSurfaceReplicaRectBorderWidth(layerTreeHostImpl());
347 case ScreenSpaceRectType
:
348 strokeColor
= DebugColors::ScreenSpaceLayerRectBorderColor();
349 fillColor
= DebugColors::ScreenSpaceLayerRectFillColor();
350 strokeWidth
= DebugColors::ScreenSpaceLayerRectBorderWidth(layerTreeHostImpl());
352 case OccludingRectType
:
353 strokeColor
= DebugColors::OccludingRectBorderColor();
354 fillColor
= DebugColors::OccludingRectFillColor();
355 strokeWidth
= DebugColors::OccludingRectBorderWidth(layerTreeHostImpl());
357 case NonOccludingRectType
:
358 strokeColor
= DebugColors::NonOccludingRectBorderColor();
359 fillColor
= DebugColors::NonOccludingRectFillColor();
360 strokeWidth
= DebugColors::NonOccludingRectBorderWidth(layerTreeHostImpl());
364 const gfx::RectF
& rect
= debugRects
[i
].rect
;
365 SkRect skRect
= SkRect::MakeXYWH(rect
.x(), rect
.y(), rect
.width(), rect
.height());
366 SkPaint paint
= createPaint();
367 paint
.setColor(fillColor
);
368 canvas
->drawRect(skRect
, paint
);
370 paint
.setColor(strokeColor
);
371 paint
.setStyle(SkPaint::kStroke_Style
);
372 paint
.setStrokeWidth(SkFloatToScalar(strokeWidth
));
373 canvas
->drawRect(skRect
, paint
);
379 const char* HeadsUpDisplayLayerImpl::layerTypeAsString() const
381 return "HeadsUpDisplayLayer";