2011-03-24 Benjamin Poulain <benjamin.poulain@nokia.com>

        Reviewed by Kenneth Rohde Christiansen.

        [Qt] When we render WebGL offscreen, color conversion cost a lot of CPU cycles
        https://bugs.webkit.org/show_bug.cgi?id=40884

        The software fallback is now only needed for corner cases like a manual rendering
        of the page to QImage.

        Keeping the image with the last pixel values is no longer needed. Removing it reduce the
        performance for real-time rendering on software surface, but this case should no longer be
        supported.

        The conversion from OpenGL color space and coordinates is done manually for performance. This
        also fix the bug of the inverted X axis due to the transformation.

        The tests and benchmarks are done through Qt API tests.

        * platform/graphics/qt/GraphicsContext3DQt.cpp:
        (WebCore::swapBgrToRgb):
        (WebCore::GraphicsContext3DInternal::paint):
        (WebCore::GraphicsContext3D::reshape):
2011-03-24  Benjamin Poulain  <benjamin.poulain@nokia.com>

        Reviewed by Kenneth Rohde Christiansen.

        [Qt] When we render WebGL offscreen, color conversion cost a lot of CPU cycles
        https://bugs.webkit.org/show_bug.cgi?id=40884

        Add tests and benchmarks for the software fallback of WebGL.

        * tests/benchmarks/webgl/10000_triangles.html: Added.
        * tests/benchmarks/webgl/tst_webgl.cpp: Added.
        (GraphicsView::GraphicsView):
        (GraphicsView::resizeEvent):
        (tst_WebGlPerformance::init):
        (tst_WebGlPerformance::cleanup):
        (tst_WebGlPerformance::benchSoftwareFallbackRgb16):
        (tst_WebGlPerformance::benchSoftwareFallbackRgb32):
        (tst_WebGlPerformance::benchSoftwareFallbackArgb32):
        (tst_WebGlPerformance::benchSoftwareFallbackArgb32Premultiplied):
        (tst_WebGlPerformance::benchmarkFrameRenderingOnImage):
        * tests/benchmarks/webgl/tst_webgl.qrc: Added.
        * tests/benchmarks/webgl/webgl.pro: Added.
        * tests/qgraphicswebview/qgraphicswebview.pro:
        * tests/qgraphicswebview/resources/pointing_right.html: Added.
        * tests/qgraphicswebview/resources/pointing_up.html: Added.
        * tests/qgraphicswebview/tst_qgraphicswebview.cpp:
        (compareImagesFuzzyPixelCount):
        (GraphicsView::GraphicsView):
        (tst_QGraphicsWebView::webglSoftwareFallbackVerticalOrientation):
        (tst_QGraphicsWebView::webglSoftwareFallbackHorizontalOrientation):
        (tst_QGraphicsWebView::compareCanvasToImage):
        * tests/qgraphicswebview/tst_qgraphicswebview.qrc:
        * tests/tests.pro:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@81886 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 9e19dc24
2011-03-24 Benjamin Poulain <benjamin.poulain@nokia.com>
Reviewed by Kenneth Rohde Christiansen.
[Qt] When we render WebGL offscreen, color conversion cost a lot of CPU cycles
https://bugs.webkit.org/show_bug.cgi?id=40884
The software fallback is now only needed for corner cases like a manual rendering
of the page to QImage.
Keeping the image with the last pixel values is no longer needed. Removing it reduce the
performance for real-time rendering on software surface, but this case should no longer be
supported.
The conversion from OpenGL color space and coordinates is done manually for performance. This
also fix the bug of the inverted X axis due to the transformation.
The tests and benchmarks are done through Qt API tests.
* platform/graphics/qt/GraphicsContext3DQt.cpp:
(WebCore::swapBgrToRgb):
(WebCore::GraphicsContext3DInternal::paint):
(WebCore::GraphicsContext3D::reshape):
2011-03-24 Nat Duca <nduca@chromium.org>
Reviewed by James Robinson.
......@@ -263,7 +263,6 @@ public:
GLuint m_mainFbo;
GLuint m_currentFbo;
GLuint m_depthBuffer;
QImage m_pixels;
bool m_layerComposited;
ListHashSet<unsigned int> m_syntheticErrors;
......@@ -481,6 +480,11 @@ QGLWidget* GraphicsContext3DInternal::getViewportGLWidget()
return 0;
}
static inline quint32 swapBgrToRgb(quint32 pixel)
{
return ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00);
}
void GraphicsContext3DInternal::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
Q_UNUSED(widget);
......@@ -496,11 +500,39 @@ void GraphicsContext3DInternal::paint(QPainter* painter, const QStyleOptionGraph
}
// Alternatively read pixels to a memory buffer.
QImage offscreenImage(rect.width(), rect.height(), QImage::Format_ARGB32);
quint32* imagePixels = reinterpret_cast<quint32*>(offscreenImage.bits());
m_glWidget->makeCurrent();
bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_mainFbo);
glReadPixels(/* x */ 0, /* y */ 0, rect.width(), rect.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, m_pixels.bits());
painter->drawImage(/* x */ 0, /* y */ 0, m_pixels.rgbSwapped().transformed(QMatrix().rotate(180)));
glReadPixels(/* x */ 0, /* y */ 0, rect.width(), rect.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, imagePixels);
bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_currentFbo);
// OpenGL gives us ABGR on 32 bits, and with the origin at the bottom left
// We need RGB32 or ARGB32_PM, with the origin at the top left.
quint32* pixelsSrc = imagePixels;
const int height = static_cast<int>(rect.height());
const int width = static_cast<int>(rect.width());
const int halfHeight = height / 2;
for (int row = 0; row < halfHeight; ++row) {
const int targetIdx = (height - 1 - row) * width;
quint32* pixelsDst = imagePixels + targetIdx;
for (int column = 0; column < width; ++column) {
quint32 tempPixel = *pixelsSrc;
*pixelsSrc = swapBgrToRgb(*pixelsDst);
*pixelsDst = swapBgrToRgb(tempPixel);
++pixelsSrc;
++pixelsDst;
}
}
if (static_cast<int>(height) % 2) {
for (int column = 0; column < width; ++column) {
*pixelsSrc = swapBgrToRgb(*pixelsSrc);
++pixelsSrc;
}
}
painter->drawImage(/* x */ 0, /* y */ 0, offscreenImage);
}
QRectF GraphicsContext3DInternal::boundingRect() const
......@@ -590,7 +622,6 @@ void GraphicsContext3D::reshape(int width, int height)
m_currentHeight = height;
m_internal->m_boundingRect = QRectF(QPointF(0, 0), QSizeF(width, height));
m_internal->m_pixels = QImage(m_currentWidth, m_currentHeight, QImage::Format_ARGB32);
m_internal->m_glWidget->makeCurrent();
......
2011-03-24 Benjamin Poulain <benjamin.poulain@nokia.com>
Reviewed by Kenneth Rohde Christiansen.
[Qt] When we render WebGL offscreen, color conversion cost a lot of CPU cycles
https://bugs.webkit.org/show_bug.cgi?id=40884
Add tests and benchmarks for the software fallback of WebGL.
* tests/benchmarks/webgl/10000_triangles.html: Added.
* tests/benchmarks/webgl/tst_webgl.cpp: Added.
(GraphicsView::GraphicsView):
(GraphicsView::resizeEvent):
(tst_WebGlPerformance::init):
(tst_WebGlPerformance::cleanup):
(tst_WebGlPerformance::benchSoftwareFallbackRgb16):
(tst_WebGlPerformance::benchSoftwareFallbackRgb32):
(tst_WebGlPerformance::benchSoftwareFallbackArgb32):
(tst_WebGlPerformance::benchSoftwareFallbackArgb32Premultiplied):
(tst_WebGlPerformance::benchmarkFrameRenderingOnImage):
* tests/benchmarks/webgl/tst_webgl.qrc: Added.
* tests/benchmarks/webgl/webgl.pro: Added.
* tests/qgraphicswebview/qgraphicswebview.pro:
* tests/qgraphicswebview/resources/pointing_right.html: Added.
* tests/qgraphicswebview/resources/pointing_up.html: Added.
* tests/qgraphicswebview/tst_qgraphicswebview.cpp:
(compareImagesFuzzyPixelCount):
(GraphicsView::GraphicsView):
(tst_QGraphicsWebView::webglSoftwareFallbackVerticalOrientation):
(tst_QGraphicsWebView::webglSoftwareFallbackHorizontalOrientation):
(tst_QGraphicsWebView::compareCanvasToImage):
* tests/qgraphicswebview/tst_qgraphicswebview.qrc:
* tests/tests.pro:
2011-03-24 Kristian Amlie <kristian.amlie@nokia.com>
Reviewed by Benjamin Poulain.
......
<html>
<body style="margin: 0">
<canvas width="1000" height="1000"></canvas>
</body>
</html>
<script>
var canvas = document.getElementsByTagName("canvas")[0];
gl = canvas.getContext("experimental-webgl");
gl.clearColor(0.0, 1.0, 0.0, 1.0);
gl.viewport(0, 0, canvas.width, canvas.height);
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, "attribute vec4 vPosition;\nvoid main() { gl_Position = vPosition; }");
gl.compileShader(vertexShader);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }");
gl.compileShader(fragmentShader);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.bindAttribLocation(shaderProgram, 0, "vPosition");
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var vertices = [];
var seedX = -1.0;
var seedY = 1.0;
for (var i = 1; i <= 10000; ++i) {
vertices.push(seedX);
vertices.push(seedY);
vertices.push(0);
seedX += 0.01;
vertices.push(seedX);
vertices.push(seedY - 0.02);
vertices.push(0);
seedX += 0.01;
vertices.push(seedX);
vertices.push(seedY);
vertices.push(0);
if (!(i % 100)) {
seedX = -1.0;
seedY -= 0.02;
}
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 30000);
gl.flush();
</script>
/*
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "../../util.h"
#include <QGLWidget>
#include <QGraphicsView>
#include <QGraphicsWebView>
#include <QScopedPointer>
#include <QWebFrame>
#include <QtTest/QtTest>
class GraphicsView;
class tst_WebGlPerformance : public QObject {
Q_OBJECT
private slots:
void init();
void cleanup();
void benchSoftwareFallbackRgb16();
void benchSoftwareFallbackRgb32();
void benchSoftwareFallbackArgb32();
void benchSoftwareFallbackArgb32Premultiplied();
private:
void benchmarkFrameRenderingOnImage(QImage::Format);
QScopedPointer<GraphicsView> m_view;
};
class GraphicsView : public QGraphicsView {
public:
GraphicsView();
QGraphicsWebView* m_webView;
protected:
void resizeEvent(QResizeEvent*);
};
GraphicsView::GraphicsView()
{
QGraphicsScene* const scene = new QGraphicsScene(this);
setScene(scene);
m_webView = new QGraphicsWebView;
scene->addItem(m_webView);
m_webView->page()->settings()->setAttribute(QWebSettings::WebGLEnabled, true);
resize(800, 600);
setFrameShape(QFrame::NoFrame);
setViewport(new QGLWidget);
}
void GraphicsView::resizeEvent(QResizeEvent* event)
{
QGraphicsView::resizeEvent(event);
QRectF rect(QPoint(0, 0), event->size());
m_webView->setGeometry(rect);
scene()->setSceneRect(rect);
}
void tst_WebGlPerformance::init()
{
m_view.reset(new GraphicsView);
m_view->showMaximized();
QTest::qWaitForWindowShown(m_view.data());
}
void tst_WebGlPerformance::cleanup()
{
m_view.reset();
}
void tst_WebGlPerformance::benchSoftwareFallbackRgb16()
{
benchmarkFrameRenderingOnImage(QImage::Format_RGB16);
}
void tst_WebGlPerformance::benchSoftwareFallbackRgb32()
{
benchmarkFrameRenderingOnImage(QImage::Format_RGB32);
}
void tst_WebGlPerformance::benchSoftwareFallbackArgb32()
{
benchmarkFrameRenderingOnImage(QImage::Format_ARGB32);
}
void tst_WebGlPerformance::benchSoftwareFallbackArgb32Premultiplied()
{
benchmarkFrameRenderingOnImage(QImage::Format_ARGB32_Premultiplied);
}
void tst_WebGlPerformance::benchmarkFrameRenderingOnImage(QImage::Format format)
{
m_view->m_webView->load(QUrl(QLatin1String("qrc:///testcases/10000_triangles.html")));
const bool pageLoaded = waitForSignal(m_view->m_webView, SIGNAL(loadFinished(bool)));
Q_ASSERT(pageLoaded);
Q_UNUSED(pageLoaded);
QImage target(m_view->size(), format);
QBENCHMARK {
QPainter painter(&target);
m_view->render(&painter);
painter.end();
}
}
QTEST_MAIN(tst_WebGlPerformance)
#include "tst_webgl.moc"
<RCC>
<qresource prefix="/testcases">
<file>10000_triangles.html</file>
</qresource>
</RCC>
isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../../..
include(../../tests.pri)
exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
QT += opengl
isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../..
include(../tests.pri)
exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc
contains(DEFINES, ENABLE_WEBGL=1) {
QT += opengl
}
<html>
<body style="margin: 0">
<canvas width="100" height="100"></canvas>
</body>
</html>
<script>
var canvas = document.getElementsByTagName("canvas")[0];
gl = canvas.getContext("experimental-webgl");
gl.clearColor(0.0, 1.0, 0.0, 1.0);
gl.viewport(0, 0, canvas.width, canvas.height);
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, "attribute vec4 vPosition;\nvoid main() { gl_Position = vPosition; }");
gl.compileShader(vertexShader);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }");
gl.compileShader(fragmentShader);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.bindAttribLocation(shaderProgram, 0, "vPosition");
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var vertices = [-1.0, -1.0,
0.0, 0.0,
-1.0, 1.0];
var seedX = -1.0;
var seedY = 1.0;
vertices
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.flush();
</script>
<html>
<body style="margin: 0">
<canvas width="100" height="100"></canvas>
</body>
</html>
<script>
var canvas = document.getElementsByTagName("canvas")[0];
gl = canvas.getContext("experimental-webgl");
gl.clearColor(0.0, 1.0, 0.0, 1.0);
gl.viewport(0, 0, canvas.width, canvas.height);
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, "attribute vec4 vPosition;\nvoid main() { gl_Position = vPosition; }");
gl.compileShader(vertexShader);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }");
gl.compileShader(fragmentShader);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.bindAttribLocation(shaderProgram, 0, "vPosition");
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var vertices = [-1.0, -1.0,
0.0, 0.0,
1.0, -1.0];
var seedX = -1.0;
var seedY = 1.0;
vertices
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.flush();
gl.finish();
</script>
......@@ -25,6 +25,10 @@
#include <qwebpage.h>
#include <qwebframe.h>
#if defined(ENABLE_WEBGL) && ENABLE_WEBGL
#include <QGLWidget>
#endif
class tst_QGraphicsWebView : public QObject
{
Q_OBJECT
......@@ -39,6 +43,14 @@ private slots:
void setPalette_data();
void setPalette();
void renderHints();
#if defined(ENABLE_WEBGL) && ENABLE_WEBGL
void webglSoftwareFallbackVerticalOrientation();
void webglSoftwareFallbackHorizontalOrientation();
private:
void compareCanvasToImage(const QUrl&, const QImage&);
#endif
};
void tst_QGraphicsWebView::qgraphicswebview()
......@@ -444,6 +456,112 @@ void tst_QGraphicsWebView::renderHints()
QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing));
}
class GraphicsView : public QGraphicsView {
public:
GraphicsView();
QGraphicsWebView* m_webView;
};
#if defined(ENABLE_WEBGL) && ENABLE_WEBGL
bool compareImagesFuzzyPixelCount(const QImage& image1, const QImage& image2, qreal tolerance = 0.05)
{
if (image1.size() != image2.size())
return false;
unsigned diffPixelCount = 0;
for (int row = 0; row < image1.size().width(); ++row) {
for (int column = 0; column < image1.size().height(); ++column)
if (image1.pixel(row, column) != image2.pixel(row, column))
++diffPixelCount;
}
if (diffPixelCount > (image1.size().width() * image1.size().height()) * tolerance)
return false;
return true;
}
GraphicsView::GraphicsView()
{
QGraphicsScene* const scene = new QGraphicsScene(this);
setScene(scene);
m_webView = new QGraphicsWebView;
scene->addItem(m_webView);
m_webView->page()->settings()->setAttribute(QWebSettings::WebGLEnabled, true);
m_webView->setResizesToContents(true);
setFrameShape(QFrame::NoFrame);
setViewport(new QGLWidget);
}
void tst_QGraphicsWebView::webglSoftwareFallbackVerticalOrientation()
{
QSize canvasSize(100, 100);
QImage reference(canvasSize, QImage::Format_ARGB32);
reference.fill(0xFF00FF00);
{ // Reference.
QPainter painter(&reference);
QPolygonF triangleUp;
triangleUp << QPointF(0, canvasSize.height())
<< QPointF(canvasSize.width(), canvasSize.height())
<< QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0);
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::red);
painter.drawPolygon(triangleUp);
}
compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_up.html")), reference);
}
void tst_QGraphicsWebView::webglSoftwareFallbackHorizontalOrientation()
{
QSize canvasSize(100, 100);
QImage reference(canvasSize, QImage::Format_ARGB32);
reference.fill(0xFF00FF00);
{ // Reference.
QPainter painter(&reference);
QPolygonF triangleUp;
triangleUp << QPointF(0, 0)
<< QPointF(0, canvasSize.height())
<< QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0);
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::red);
painter.drawPolygon(triangleUp);
}
compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_right.html")), reference);
}
void tst_QGraphicsWebView::compareCanvasToImage(const QUrl& url, const QImage& reference)
{
GraphicsView view;
view.show();
QTest::qWaitForWindowShown(&view);
QGraphicsWebView* const graphicsWebView = view.m_webView;
graphicsWebView->load(url);
QVERIFY(waitForSignal(graphicsWebView, SIGNAL(loadFinished(bool))));
{ // Force a render, to create the accelerated compositing tree.
QPixmap pixmap(view.size());
QPainter painter(&pixmap);
view.render(&painter);
}
QApplication::syncX();
const QSize imageSize = reference.size();
QImage target(imageSize, QImage::Format_ARGB32);
{ // Web page content.
QPainter painter(&target);
QRectF renderRect(0, 0, imageSize.width(), imageSize.height());
view.scene()->render(&painter, renderRect, renderRect);
}
QVERIFY(compareImagesFuzzyPixelCount(target, reference, 0.01));
}
#endif
QTEST_MAIN(tst_QGraphicsWebView)
#include "tst_qgraphicswebview.moc"
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>resources/input_types.html</file>
</qresource>
<RCC>
<qresource prefix="/">
<file>resources/input_types.html</file>
<file>resources/pointing_right.html</file>
<file>resources/pointing_up.html</file>
</qresource>
</RCC>
......@@ -3,3 +3,6 @@ TEMPLATE = subdirs
SUBDIRS = qwebframe qwebpage qwebelement qgraphicswebview qwebhistoryinterface qwebview qwebhistory qwebinspector hybridPixmap
contains(QT_CONFIG, declarative): SUBDIRS += qdeclarativewebview
SUBDIRS += benchmarks/painting benchmarks/loading
contains(DEFINES, ENABLE_WEBGL=1) {
SUBDIRS += benchmarks/webgl
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment