cc: Fix hudLayer size and paint rects scale for deviceScaleFactor != 1
[chromium-blink-merge.git] / cc / heads_up_display_layer_impl.cc
blob0ca7fe5ea60873d6c0128dfdd0f950a941826818
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 <limits>
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"
27 namespace cc {
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;
41 SkPaint paint;
42 paint.setColorFilter(new SkColorMatrixFilter(swizzleMatrix))->unref();
44 return paint;
47 HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(int id)
48 : LayerImpl(id)
49 , m_averageFPS(0)
50 , m_minFPS(0)
51 , m_maxFPS(0)
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);
68 if (!m_hudTexture)
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())
74 m_hudTexture->Free();
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())
83 return;
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);
91 bool flipped = false;
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())
100 return;
102 SkISize canvasSize;
103 if (m_hudCanvas)
104 canvasSize = m_hudCanvas->getDeviceSize();
105 else
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())
127 return;
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
142 return true;
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;
172 const int gap = 6;
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;
185 const int top = 2;
187 SkPaint paint = createPaint();
189 // Draw background.
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);
200 return top + height;
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);
211 // Draw FPS text.
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);
235 // Draw 60fps line.
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.
241 double x = 0;
242 const double timeScale = 60; // in pixels/second
243 SkPath path;
245 m_minFPS = std::numeric_limits<double>::max();
246 m_maxFPS = 0;
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);
265 if (p < 0)
266 p = 0;
267 if (p > 1)
268 p = 1;
270 // Plot this data point.
271 SkPoint cur = SkPoint::Make(graphBounds.right() - x, graphBounds.bottom() - p * graphBounds.height());
272 if (path.isEmpty())
273 path.moveTo(cur);
274 else
275 path.lineTo(cur);
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);
303 // Draw FPS graph.
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();
316 canvas->save();
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) {
325 case PaintRectType:
326 strokeColor = DebugColors::PaintRectBorderColor();
327 fillColor = DebugColors::PaintRectFillColor();
328 strokeWidth = DebugColors::PaintRectBorderWidth(layerTreeHostImpl());
329 break;
330 case PropertyChangedRectType:
331 strokeColor = DebugColors::PropertyChangedRectBorderColor();
332 fillColor = DebugColors::PropertyChangedRectFillColor();
333 strokeWidth = DebugColors::PropertyChangedRectBorderWidth(layerTreeHostImpl());
334 break;
335 case SurfaceDamageRectType:
336 strokeColor = DebugColors::SurfaceDamageRectBorderColor();
337 fillColor = DebugColors::SurfaceDamageRectFillColor();
338 strokeWidth = DebugColors::SurfaceDamageRectBorderWidth(layerTreeHostImpl());
339 break;
340 case ReplicaScreenSpaceRectType:
341 strokeColor = DebugColors::ScreenSpaceSurfaceReplicaRectBorderColor();
342 fillColor = DebugColors::ScreenSpaceSurfaceReplicaRectFillColor();
343 strokeWidth = DebugColors::ScreenSpaceSurfaceReplicaRectBorderWidth(layerTreeHostImpl());
344 break;
345 case ScreenSpaceRectType:
346 strokeColor = DebugColors::ScreenSpaceLayerRectBorderColor();
347 fillColor = DebugColors::ScreenSpaceLayerRectFillColor();
348 strokeWidth = DebugColors::ScreenSpaceLayerRectBorderWidth(layerTreeHostImpl());
349 break;
350 case OccludingRectType:
351 strokeColor = DebugColors::OccludingRectBorderColor();
352 fillColor = DebugColors::OccludingRectFillColor();
353 strokeWidth = DebugColors::OccludingRectBorderWidth(layerTreeHostImpl());
354 break;
355 case NonOccludingRectType:
356 strokeColor = DebugColors::NonOccludingRectBorderColor();
357 fillColor = DebugColors::NonOccludingRectFillColor();
358 strokeWidth = DebugColors::NonOccludingRectBorderWidth(layerTreeHostImpl());
359 break;
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);
374 canvas->restore();
377 const char* HeadsUpDisplayLayerImpl::layerTypeAsString() const
379 return "HeadsUpDisplayLayer";
382 } // namespace cc