1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1.  Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *     notice, this list of conditions and the following disclaimer in the
11 *     documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26
27#if USE(ACCELERATED_COMPOSITING)
28#include "CCHeadsUpDisplay.h"
29
30#include "CurrentTime.h"
31#include "Font.h"
32#include "FontDescription.h"
33#include "GraphicsContext3D.h"
34#include "LayerChromium.h"
35#include "LayerRendererChromium.h"
36#include "LayerTexture.h"
37#include "TextRun.h"
38#include "TextStream.h"
39#include "TextureManager.h"
40#include <wtf/text/CString.h>
41#include <wtf/text/WTFString.h>
42
43namespace WebCore {
44
45using namespace std;
46
47CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner)
48    : m_currentFrameNumber(1)
49    , m_filteredFrameTime(0)
50    , m_layerRenderer(owner)
51    , m_showFPSCounter(false)
52    , m_showPlatformLayerTree(false)
53{
54    m_presentTimeHistoryInSec[0] = currentTime();
55    m_presentTimeHistoryInSec[1] = m_presentTimeHistoryInSec[0];
56    for (int i = 2; i < kPresentHistorySize; i++)
57        m_presentTimeHistoryInSec[i] = 0;
58
59    FontDescription mediumFontDesc;
60    mediumFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
61    mediumFontDesc.setComputedSize(20);
62
63    m_mediumFont = adoptPtr(new Font(mediumFontDesc, 0, 0));
64    m_mediumFont->update(0);
65
66    FontDescription smallFontDesc;
67    smallFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
68    smallFontDesc.setComputedSize(10);
69
70    m_smallFont = adoptPtr(new Font(smallFontDesc, 0, 0));
71    m_smallFont->update(0);
72}
73
74CCHeadsUpDisplay::~CCHeadsUpDisplay()
75{
76}
77
78void CCHeadsUpDisplay::draw()
79{
80    GraphicsContext3D* context = m_layerRenderer->context();
81    if (!m_hudTexture)
82        m_hudTexture = LayerTexture::create(context, m_layerRenderer->textureManager());
83
84    // Use a fullscreen texture only if we need to...
85    IntSize hudSize;
86    if (m_showPlatformLayerTree) {
87        hudSize.setWidth(min(2048, m_layerRenderer->viewportSize().width()));
88        hudSize.setHeight(min(2048, m_layerRenderer->viewportSize().height()));
89    } else {
90        hudSize.setWidth(512);
91        hudSize.setHeight(128);
92    }
93
94    m_hudTexture->reserve(hudSize, GraphicsContext3D::RGBA);
95
96    // Render pixels into the texture.
97    PlatformCanvas canvas;
98    canvas.resize(hudSize);
99    {
100        PlatformCanvas::Painter painter(&canvas, PlatformCanvas::Painter::GrayscaleText);
101        drawHudContents(painter.context(), hudSize);
102    }
103
104    // Upload to GL.
105    {
106        PlatformCanvas::AutoLocker locker(&canvas);
107
108        m_hudTexture->bindTexture();
109        GLC(context.get(), context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, canvas.size().width(), canvas.size().height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, locker.pixels()));
110    }
111
112    // Draw the HUD onto the default render surface.
113    const Program* program = m_layerRenderer->headsUpDisplayProgram();
114    ASSERT(program && program->initialized());
115    GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0));
116    m_hudTexture->bindTexture();
117    m_layerRenderer->useShader(program->program());
118    GLC(context, context->uniform1i(program->fragmentShader().samplerLocation(), 0));
119
120    TransformationMatrix matrix;
121    matrix.translate3d(hudSize.width() * 0.5, hudSize.height() * 0.5, 0);
122    LayerChromium::drawTexturedQuad(context, m_layerRenderer->projectionMatrix(),
123                                    matrix, hudSize.width(), hudSize.height(),
124                                    1.0f, program->vertexShader().matrixLocation(),
125                                    program->fragmentShader().alphaLocation());
126}
127
128void CCHeadsUpDisplay::drawHudContents(GraphicsContext* ctx, const IntSize& hudSize)
129{
130    if (m_showPlatformLayerTree) {
131        ctx->setFillColor(Color(0, 0, 0, 192), ColorSpaceDeviceRGB);
132        ctx->fillRect(FloatRect(0, 0, hudSize.width(), hudSize.height()));
133    }
134
135    int fpsCounterHeight = m_mediumFont->fontMetrics().floatHeight() + 2;
136    int fpsCounterTop = 2;
137    int platformLayerTreeTop;
138    if (m_showFPSCounter)
139        platformLayerTreeTop = fpsCounterTop + fpsCounterHeight + 2;
140    else
141        platformLayerTreeTop = 0;
142
143    if (m_showFPSCounter)
144        drawFPSCounter(ctx, fpsCounterTop, fpsCounterHeight);
145
146    if (m_showPlatformLayerTree)
147        drawPlatformLayerTree(ctx, platformLayerTreeTop);
148}
149
150void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* ctx, int top, int height)
151{
152    // Note that since we haven't finished the current frame, the FPS counter
153    // actually reports the last frame's time.
154    double secForLastFrame = m_presentTimeHistoryInSec[(m_currentFrameNumber + kPresentHistorySize - 1) % kPresentHistorySize] -
155                             m_presentTimeHistoryInSec[(m_currentFrameNumber + kPresentHistorySize - 2) % kPresentHistorySize];
156
157    // Filter the frame times to avoid spikes.
158    const float alpha = 0.1;
159    if (!m_filteredFrameTime) {
160        if (m_currentFrameNumber == 2)
161            m_filteredFrameTime = secForLastFrame;
162    } else
163        m_filteredFrameTime = ((1.0 - alpha) * m_filteredFrameTime) + (alpha * secForLastFrame);
164
165    // Create & measure FPS text.
166    String text(String::format("FPS: %5.1f", 1.0 / m_filteredFrameTime));
167    TextRun run(text);
168    float textWidth = m_mediumFont->width(run) + 2.0f;
169    float graphWidth = kPresentHistorySize;
170
171    // Draw background.
172    ctx->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
173    ctx->fillRect(FloatRect(2, top, textWidth + graphWidth, height));
174
175    // Draw FPS text.
176    if (m_filteredFrameTime) {
177        ctx->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
178        ctx->drawText(*m_mediumFont, run, IntPoint(3, top + height - 6));
179    }
180
181    // Draw FPS graph.
182    const double loFPS = 0.0;
183    const double hiFPS = 120.0;
184    ctx->setStrokeStyle(SolidStroke);
185    ctx->setStrokeColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
186    int graphLeft = static_cast<int>(textWidth + 3);
187    IntPoint prev(-1, 0);
188    int x = 0;
189    double h = static_cast<double>(height - 2);
190    for (int i = m_currentFrameNumber % kPresentHistorySize; i != (m_currentFrameNumber - 1) % kPresentHistorySize; i = (i + 1) % kPresentHistorySize) {
191        int j = (i + 1) % kPresentHistorySize;
192        double fps = 1.0 / (m_presentTimeHistoryInSec[j] - m_presentTimeHistoryInSec[i]);
193        double p = 1 - ((fps - loFPS) / (hiFPS - loFPS));
194        if (p < 0)
195            p = 0;
196        if (p > 1)
197            p = 1;
198        IntPoint cur(graphLeft + x, 1 + top + p*h);
199        if (prev.x() != -1)
200            ctx->drawLine(prev, cur);
201        prev = cur;
202        x += 1;
203    }
204}
205
206void CCHeadsUpDisplay::drawPlatformLayerTree(GraphicsContext* ctx, int top)
207{
208    float smallFontHeight = m_smallFont->fontMetrics().floatHeight();
209    int y = top + smallFontHeight - 4;
210    ctx->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
211    Vector<String> lines;
212    m_layerRenderer->layerTreeAsText().split('\n', lines);
213    for (size_t i = 0; i < lines.size(); ++i) {
214        ctx->drawText(*m_smallFont, TextRun(lines[i]), IntPoint(2, y));
215        y += smallFontHeight;
216    }
217}
218
219void CCHeadsUpDisplay::onPresent()
220{
221    m_presentTimeHistoryInSec[m_currentFrameNumber % kPresentHistorySize] = currentTime();
222    m_currentFrameNumber += 1;
223}
224
225}
226
227#endif // USE(ACCELERATED_COMPOSITING)
228