diff options
author | Nicolas Roard <nicolasroard@google.com> | 2012-04-06 11:35:50 -0700 |
---|---|---|
committer | Nicolas Roard <nicolasroard@google.com> | 2012-04-06 14:03:59 -0700 |
commit | 2e510fd5b5a30f1315c272d44ae3aa4cba355498 (patch) | |
tree | db3af5f32855d329856f190c3509ae11ae519851 /Source/WebCore/platform/graphics/android/rendering | |
parent | c88c88907b618e468ec3928b06a3a31d4f99b9c6 (diff) | |
download | external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.zip external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.tar.gz external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.tar.bz2 |
Reorganize platform/graphics/android
Change-Id: Idc67155cfa99784dcd931e705336bfa063ecae46
Diffstat (limited to 'Source/WebCore/platform/graphics/android/rendering')
50 files changed, 9041 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.cpp b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.cpp new file mode 100644 index 0000000..833aea9 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "BaseRenderer" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "BaseRenderer.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "GaneshRenderer.h" +#include "GLUtils.h" +#include "RasterRenderer.h" +#include "SkBitmap.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPicture.h" +#include "SkTypeface.h" +#include "Tile.h" +#include "TilesManager.h" + +#include <wtf/text/CString.h> + +#define UPDATE_COUNT_MASK 0xFF // displayed count wraps at 256 +#define UPDATE_COUNT_ALPHA_MASK 0x1F // alpha wraps at 32 + +namespace WebCore { + +BaseRenderer::RendererType BaseRenderer::g_currentType = BaseRenderer::Raster; + +BaseRenderer* BaseRenderer::createRenderer() +{ + if (g_currentType == Raster) + return new RasterRenderer(); + else if (g_currentType == Ganesh) + return new GaneshRenderer(); + return NULL; +} + +void BaseRenderer::swapRendererIfNeeded(BaseRenderer*& renderer) +{ + if (renderer->getType() == g_currentType) + return; + + delete renderer; + renderer = createRenderer(); +} + +void BaseRenderer::drawTileInfo(SkCanvas* canvas, + const TileRenderInfo& renderInfo, int updateCount, double renderDuration) +{ + static SkTypeface* s_typeface = 0; + if (!s_typeface) + s_typeface = SkTypeface::CreateFromName("", SkTypeface::kBold); + SkPaint paint; + paint.setTextSize(17); + char str[256]; + snprintf(str, 256, " (%d,%d) %.2fx %d %.1fms", renderInfo.x, renderInfo.y, + renderInfo.scale, updateCount, renderDuration); + paint.setARGB(128, 255, 255, 255); + canvas->drawRectCoords(0, 0, renderInfo.tileSize.fWidth, 17, paint); + paint.setARGB(255, 255, 0, 0); + paint.setTypeface(s_typeface); + canvas->drawText(str, strlen(str), 20, 15, paint); +} + +void BaseRenderer::renderTiledContent(TileRenderInfo& renderInfo) +{ + const bool visualIndicator = TilesManager::instance()->getShowVisualIndicator(); + const SkSize& tileSize = renderInfo.tileSize; + + SkCanvas canvas; + setupCanvas(renderInfo, &canvas); + + if (!canvas.getDevice()) { + // TODO: consider ALOGE + ALOGV("Error: No Device"); + return; + } + + double before; + if (visualIndicator) { + canvas.save(); + before = currentTimeMS(); + } + + setupPartialInval(renderInfo, &canvas); + canvas.translate(-renderInfo.x * tileSize.width(), -renderInfo.y * tileSize.height()); + canvas.scale(renderInfo.scale, renderInfo.scale); + renderInfo.tilePainter->paint(renderInfo.baseTile, &canvas); + if (renderInfo.baseTile && renderInfo.baseTile->backTexture()) + checkForPureColor(renderInfo, &canvas); + else + renderInfo.isPureColor = false; + + if (visualIndicator) { + double after = currentTimeMS(); + canvas.restore(); + unsigned int updateCount = renderInfo.tilePainter->getUpdateCount() & UPDATE_COUNT_MASK; + const int color = updateCount & UPDATE_COUNT_ALPHA_MASK; + + // only color the invalidated area + SkPaint paint; + paint.setARGB(color, 0, 255, 0); + if (renderInfo.invalRect) + canvas.drawIRect(*renderInfo.invalRect, paint); + else { + SkIRect rect; + rect.set(0, 0, tileSize.width(), tileSize.height()); + canvas.drawIRect(rect, paint); + } + + if (renderInfo.invalRect) { + // if partial inval... + int x = renderInfo.invalRect->fLeft; + int y = renderInfo.invalRect->fTop; + int w = renderInfo.invalRect->width(); + int h = renderInfo.invalRect->height(); + + paint.setARGB(128, 255, 255, 0); + canvas.drawLine(x, y, x + w, y + h, paint); + canvas.drawLine(x, y + h, x + w, y, paint); + } + drawTileInfo(&canvas, renderInfo, updateCount, after - before); + + // paint the tile boundaries + paint.setARGB(64, 255, 0, 0); + paint.setStrokeWidth(3); + canvas.drawLine(0, 0, tileSize.width(), tileSize.height(), paint); + paint.setARGB(64, 0, 255, 0); + canvas.drawLine(0, tileSize.height(), tileSize.width(), 0, paint); + paint.setARGB(128, 0, 0, 255); + canvas.drawLine(tileSize.width(), 0, tileSize.width(), tileSize.height(), paint); + } + renderingComplete(renderInfo, &canvas); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.h b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.h new file mode 100644 index 0000000..f225871 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.h @@ -0,0 +1,107 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BaseRenderer_h +#define BaseRenderer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "Color.h" +#include "SkRect.h" +#include <wtf/text/StringHash.h> + +class SkCanvas; +class SkDevice; + +namespace WebCore { + +class TextureInfo; +class TilePainter; +class Tile; + +struct TileRenderInfo { + // coordinates of the tile + int x; + int y; + + // current scale factor + float scale; + + // inval rectangle with coordinates in the tile's coordinate space + SkIRect* invalRect; + + // the expected size of the tile + SkSize tileSize; + + // the painter object in charge of drawing our content + TilePainter* tilePainter; + + // the base tile calling us + Tile* baseTile; + + // info about the texture that we are to render into + TextureInfo* textureInfo; + + bool isPureColor; + Color pureColor; +}; + +/** + * + */ +class BaseRenderer { +public: + enum RendererType { Raster, Ganesh }; + BaseRenderer(RendererType type) : m_type(type) {} + virtual ~BaseRenderer() {} + + void renderTiledContent(TileRenderInfo& renderInfo); + + RendererType getType() { return m_type; } + + static BaseRenderer* createRenderer(); + static void swapRendererIfNeeded(BaseRenderer*& renderer); + static RendererType getCurrentRendererType() { return g_currentType; } + static void setCurrentRendererType(RendererType type) { g_currentType = type; } + +protected: + + virtual void setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* canvas) = 0; + virtual void setupPartialInval(const TileRenderInfo& renderInfo, SkCanvas* canvas) {} + virtual void renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas) = 0; + virtual void checkForPureColor(TileRenderInfo& renderInfo, SkCanvas* canvas) = 0; + + void drawTileInfo(SkCanvas* canvas, const TileRenderInfo& renderInfo, + int updateCount, double renderDuration); + +private: + RendererType m_type; + static RendererType g_currentType; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // BaseRenderer_h diff --git a/Source/WebCore/platform/graphics/android/rendering/DrawQuadData.h b/Source/WebCore/platform/graphics/android/rendering/DrawQuadData.h new file mode 100644 index 0000000..687808d --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/DrawQuadData.h @@ -0,0 +1,172 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DrawQuadData_h +#define DrawQuadData_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "Color.h" +#include "SkRect.h" +#include <GLES2/gl2.h> + +namespace WebCore { + +class TransformationMatrix; + +enum DrawQuadType { + BaseQuad, + LayerQuad, + Blit // 1:1 straight pixel blit +}; + +// Both PureColorQuadData and TextureQuadData share the data from DrawQuadData. +class DrawQuadData { +public: + DrawQuadData(DrawQuadType type = BaseQuad, + const TransformationMatrix* drawMatrix = 0, + const SkRect* geometry = 0, + float opacity = 1.0f, + bool forceBlending = true) + : m_type(type) + , m_drawMatrix(drawMatrix) + , m_geometry(geometry) + , m_opacity(opacity) + , m_forceBlending(forceBlending) + { + } + + DrawQuadData(const DrawQuadData& data) + : m_type(data.m_type) + , m_drawMatrix(data.m_drawMatrix) + , m_geometry(data.m_geometry) + , m_opacity(data.m_opacity) + , m_forceBlending(data.m_forceBlending) + { + } + + virtual ~DrawQuadData() {}; + + DrawQuadType type() const { return m_type; } + const TransformationMatrix* drawMatrix() const { return m_drawMatrix; } + const SkRect* geometry() const { return m_geometry; } + float opacity() const { return m_opacity; } + bool forceBlending() const { return m_forceBlending; } + + void updateDrawMatrix(TransformationMatrix* matrix) { m_drawMatrix = matrix; } + void updateGeometry(SkRect* rect) { m_geometry = rect; } + void updateOpacity(float opacity) { m_opacity = opacity; } + + virtual bool pureColor() const { return false; } + + virtual Color quadColor() const { return Color(); } + + virtual int textureId() const { return 0; } + virtual GLint textureFilter() const { return 0; } + virtual GLenum textureTarget() const { return 0; } + +private: + DrawQuadType m_type; + const TransformationMatrix* m_drawMatrix; + const SkRect* m_geometry; + float m_opacity; + bool m_forceBlending; +}; + +class PureColorQuadData : public DrawQuadData { +public: + PureColorQuadData(Color color, + DrawQuadType type = BaseQuad, + const TransformationMatrix* drawMatrix = 0, + const SkRect* geometry = 0, + float opacity = 1.0f, + bool forceBlending = true) + : DrawQuadData(type, drawMatrix, geometry, opacity, forceBlending) + { + m_quadColor = color; + } + + PureColorQuadData(const DrawQuadData& data, Color color) + : DrawQuadData(data) + { + m_quadColor = color; + } + + virtual ~PureColorQuadData() {}; + virtual bool pureColor() const { return true; } + virtual Color quadColor() const { return m_quadColor; } + void updateColor(const Color& color) { m_quadColor = color; } + +private: + Color m_quadColor; +}; + +class TextureQuadData : public DrawQuadData { +public: + TextureQuadData(int textureId, + GLenum textureTarget = GL_TEXTURE_2D, + GLint textureFilter = GL_LINEAR, + DrawQuadType type = BaseQuad, + const TransformationMatrix* drawMatrix = 0, + const SkRect* geometry = 0, + float opacity = 1.0f, + bool forceBlending = true) + : DrawQuadData(type, drawMatrix, geometry, opacity, forceBlending) + { + m_textureId = textureId; + m_textureTarget = textureTarget; + m_textureFilter = textureFilter; + } + + TextureQuadData(const DrawQuadData& data, + int textureId, + GLenum textureTarget = GL_TEXTURE_2D, + GLint textureFilter = GL_LINEAR) + : DrawQuadData(data) + { + m_textureId = textureId; + m_textureTarget = textureTarget; + m_textureFilter = textureFilter; + } + + virtual ~TextureQuadData() {}; + virtual bool pureColor() const { return false; } + + virtual int textureId() const { return m_textureId; } + virtual GLint textureFilter() const { return m_textureFilter; } + virtual GLenum textureTarget() const { return m_textureTarget; } + + void updateTextureId(int newId) { m_textureId = newId; } + +private: + int m_textureId; + GLint m_textureFilter; + GLenum m_textureTarget; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // DrawQuadData_h diff --git a/Source/WebCore/platform/graphics/android/rendering/GLExtras.cpp b/Source/WebCore/platform/graphics/android/rendering/GLExtras.cpp new file mode 100644 index 0000000..6498ecf --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GLExtras.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "GLExtras" +#define LOG_NDEBUG 1 + +#include "config.h" + +#include "AndroidLog.h" +#include "DrawExtra.h" +#include "DrawQuadData.h" +#include "GLExtras.h" +#include "IntRect.h" +#include "SkPath.h" +#include "TilesManager.h" +#include "android_graphics.h" + +// Touch ring border width. This is doubled if the ring is not pressed +#define RING_BORDER_WIDTH 1 + +GLExtras::GLExtras() + : m_drawExtra(0) + , m_viewport() +{ +} + +GLExtras::~GLExtras() +{ +} + +void GLExtras::drawRing(SkRect& srcRect, Color color, const TransformationMatrix* drawMat) +{ + if (srcRect.fRight <= srcRect.fLeft || srcRect.fBottom <= srcRect.fTop) { + // Invalid rect, reject it + return; + } + ALOGV("drawQuad [%fx%f, %f, %f]", srcRect.fLeft, srcRect.fTop, + srcRect.width(), srcRect.height()); + // Pull the alpha out of the color so that the shader applies it correctly. + // Otherwise we either don't have blending enabled, or the alpha will get + // double applied + Color colorWithoutAlpha(0xFF000000 | color.rgb()); + float alpha = color.alpha() / (float) 255; + + PureColorQuadData data(colorWithoutAlpha, drawMat ? LayerQuad : BaseQuad, + drawMat, &srcRect, alpha, false); + TilesManager::instance()->shader()->drawQuad(&data); +} + +void GLExtras::drawRegion(const SkRegion& region, bool fill, bool drawBorder, + const TransformationMatrix* drawMat, Color color) +{ + if (region.isEmpty()) + return; + if (fill) { + SkRegion::Iterator rgnIter(region); + while (!rgnIter.done()) { + const SkIRect& ir = rgnIter.rect(); + SkRect r; + r.set(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom); + drawRing(r, color, drawMat); + rgnIter.next(); + } + } + if (fill && !drawBorder) + return; + SkPath path; + if (!region.getBoundaryPath(&path)) + return; + SkPath::Iter iter(path, true); + SkPath::Verb verb; + SkPoint pts[4]; + SkRegion clip; + SkIRect startRect; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + if (verb == SkPath::kLine_Verb) { + SkRect r; + r.set(pts, 2); + SkIRect line; + int borderWidth = RING_BORDER_WIDTH; + if (!fill) + borderWidth *= 2; + line.fLeft = r.fLeft - borderWidth; + line.fRight = r.fRight + borderWidth; + line.fTop = r.fTop - borderWidth; + line.fBottom = r.fBottom + borderWidth; + if (clip.intersects(line)) { + clip.op(line, SkRegion::kReverseDifference_Op); + if (clip.isEmpty()) + continue; // Nothing to draw, continue + line = clip.getBounds(); + if (SkIRect::Intersects(startRect, line)) { + clip.op(startRect, SkRegion::kDifference_Op); + if (clip.isEmpty()) + continue; // Nothing to draw, continue + line = clip.getBounds(); + } + } else { + clip.setRect(line); + } + r.set(line.fLeft, line.fTop, line.fRight, line.fBottom); + drawRing(r, color, drawMat); + if (startRect.isEmpty()) { + startRect.set(line.fLeft, line.fTop, line.fRight, line.fBottom); + } + } + if (verb == SkPath::kMove_Verb) { + startRect.setEmpty(); + } + } +} + +void GLExtras::drawGL(const LayerAndroid* layer) +{ + if (m_drawExtra) + m_drawExtra->drawGL(this, layer); +} diff --git a/Source/WebCore/platform/graphics/android/rendering/GLExtras.h b/Source/WebCore/platform/graphics/android/rendering/GLExtras.h new file mode 100644 index 0000000..59a7c3c --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GLExtras.h @@ -0,0 +1,60 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GLExtras_h +#define GLExtras_h + +#include "Color.h" +#include "DrawExtra.h" +#include "SkRect.h" +#include "SkRegion.h" + +namespace WebCore { + +class LayerAndroid; +class TransformationMatrix; + +class GLExtras { +public: + GLExtras(); + virtual ~GLExtras(); + + void drawGL(const LayerAndroid* layer); + void setDrawExtra(android::DrawExtra* extra) { m_drawExtra = extra; } + void setViewport(const SkRect & viewport) { m_viewport = viewport; } + + void drawRegion(const SkRegion& region, bool fill, bool drawBorder, + const TransformationMatrix* drawMat, Color color = COLOR_HOLO_LIGHT); + +private: + void drawRing(SkRect& srcRect, Color color, const TransformationMatrix* drawMat); + + android::DrawExtra* m_drawExtra; + SkRect m_viewport; +}; + +} // namespace WebCore + +#endif // GLExtras_h diff --git a/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp new file mode 100644 index 0000000..26bd55d --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp @@ -0,0 +1,669 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "GLUtils" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "GLUtils.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "BaseRenderer.h" +#include "TextureInfo.h" +#include "Tile.h" +#include "TilesManager.h" +#include "TransferQueue.h" + +#include <android/native_window.h> +#include <gui/SurfaceTexture.h> +#include <wtf/CurrentTime.h> + +// We will limit GL error logging for LOG_VOLUME_PER_CYCLE times every +// LOG_VOLUME_PER_CYCLE seconds. +#define LOG_CYCLE 30.0 +#define LOG_VOLUME_PER_CYCLE 20 + +struct ANativeWindowBuffer; + +namespace WebCore { + +using namespace android; + +///////////////////////////////////////////////////////////////////////////////////////// +// Matrix utilities +///////////////////////////////////////////////////////////////////////////////////////// + +void GLUtils::toGLMatrix(GLfloat* flattened, const TransformationMatrix& m) +{ + flattened[0] = m.m11(); // scaleX + flattened[1] = m.m12(); // skewY + flattened[2] = m.m13(); + flattened[3] = m.m14(); // persp0 + flattened[4] = m.m21(); // skewX + flattened[5] = m.m22(); // scaleY + flattened[6] = m.m23(); + flattened[7] = m.m24(); // persp1 + flattened[8] = m.m31(); + flattened[9] = m.m32(); + flattened[10] = m.m33(); + flattened[11] = m.m34(); + flattened[12] = m.m41(); // transX + flattened[13] = m.m42(); // transY + flattened[14] = m.m43(); + flattened[15] = m.m44(); // persp2 +} + +void GLUtils::toSkMatrix(SkMatrix& matrix, const TransformationMatrix& m) +{ + matrix[0] = m.m11(); // scaleX + matrix[1] = m.m21(); // skewX + matrix[2] = m.m41(); // transX + matrix[3] = m.m12(); // skewY + matrix[4] = m.m22(); // scaleY + matrix[5] = m.m42(); // transY + matrix[6] = m.m14(); // persp0 + matrix[7] = m.m24(); // persp1 + matrix[8] = m.m44(); // persp2 +} + +void GLUtils::setOrthographicMatrix(TransformationMatrix& ortho, float left, float top, + float right, float bottom, float nearZ, float farZ) +{ + float deltaX = right - left; + float deltaY = top - bottom; + float deltaZ = farZ - nearZ; + if (!deltaX || !deltaY || !deltaZ) + return; + + ortho.setM11(2.0f / deltaX); + ortho.setM41(-(right + left) / deltaX); + ortho.setM22(2.0f / deltaY); + ortho.setM42(-(top + bottom) / deltaY); + ortho.setM33(-2.0f / deltaZ); + ortho.setM43(-(nearZ + farZ) / deltaZ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// GL & EGL error checks +///////////////////////////////////////////////////////////////////////////////////////// + +double GLUtils::m_previousLogTime = 0; +int GLUtils::m_currentLogCounter = 0; + +bool GLUtils::allowGLLog() +{ + if (m_currentLogCounter < LOG_VOLUME_PER_CYCLE) { + m_currentLogCounter++; + return true; + } + + // when we are in Log cycle and over the log limit, just return false + double currentTime = WTF::currentTime(); + double delta = currentTime - m_previousLogTime; + bool inLogCycle = (delta <= LOG_CYCLE) && (delta > 0); + if (inLogCycle) + return false; + + // When we are out of Log Cycle and over the log limit, we need to reset + // the counter and timer. + m_previousLogTime = currentTime; + m_currentLogCounter = 0; + return false; +} + +static void crashIfOOM(GLint errorCode) +{ + const GLint OOM_ERROR_CODE = 0x505; + if (errorCode == OOM_ERROR_CODE) { + ALOGE("ERROR: Fatal OOM detected."); + CRASH(); + } +} + +void GLUtils::checkEglError(const char* op, EGLBoolean returnVal) +{ + if (returnVal != EGL_TRUE) { +#ifndef DEBUG + if (allowGLLog()) +#endif + ALOGE("EGL ERROR - %s() returned %d\n", op, returnVal); + } + + for (EGLint error = eglGetError(); error != EGL_SUCCESS; error = eglGetError()) { +#ifndef DEBUG + if (allowGLLog()) +#endif + ALOGE("after %s() eglError (0x%x)\n", op, error); + crashIfOOM(error); + } +} + +bool GLUtils::checkGlError(const char* op) +{ + bool ret = false; + for (GLint error = glGetError(); error; error = glGetError()) { +#ifndef DEBUG + if (allowGLLog()) +#endif + ALOGE("GL ERROR - after %s() glError (0x%x)\n", op, error); + crashIfOOM(error); + ret = true; + } + return ret; +} + +bool GLUtils::checkGlErrorOn(void* p, const char* op) +{ + bool ret = false; + for (GLint error = glGetError(); error; error = glGetError()) { +#ifndef DEBUG + if (allowGLLog()) +#endif + ALOGE("GL ERROR on %x - after %s() glError (0x%x)\n", p, op, error); + crashIfOOM(error); + ret = true; + } + return ret; +} + +void GLUtils::checkSurfaceTextureError(const char* functionName, int status) +{ + if (status != NO_ERROR) { +#ifndef DEBUG + if (allowGLLog()) +#endif + ALOGE("ERROR at calling %s status is (%d)", functionName, status); + } +} +///////////////////////////////////////////////////////////////////////////////////////// +// GL & EGL extension checks +///////////////////////////////////////////////////////////////////////////////////////// + +bool GLUtils::isEGLImageSupported() +{ + const char* eglExtensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS); + const char* glExtensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + + return eglExtensions && glExtensions + && strstr(eglExtensions, "EGL_KHR_image_base") + && strstr(eglExtensions, "EGL_KHR_gl_texture_2D_image") + && strstr(glExtensions, "GL_OES_EGL_image"); +} + +bool GLUtils::isEGLFenceSyncSupported() +{ + const char* eglExtensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS); + return eglExtensions && strstr(eglExtensions, "EGL_KHR_fence_sync"); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Textures utilities +///////////////////////////////////////////////////////////////////////////////////////// + +static GLenum getInternalFormat(SkBitmap::Config config) +{ + switch (config) { + case SkBitmap::kA8_Config: + return GL_ALPHA; + case SkBitmap::kARGB_4444_Config: + return GL_RGBA; + case SkBitmap::kARGB_8888_Config: + return GL_RGBA; + case SkBitmap::kRGB_565_Config: + return GL_RGB; + default: + return -1; + } +} + +static GLenum getType(SkBitmap::Config config) +{ + switch (config) { + case SkBitmap::kA8_Config: + return GL_UNSIGNED_BYTE; + case SkBitmap::kARGB_4444_Config: + return GL_UNSIGNED_SHORT_4_4_4_4; + case SkBitmap::kARGB_8888_Config: + return GL_UNSIGNED_BYTE; + case SkBitmap::kIndex8_Config: + return -1; // No type for compressed data. + case SkBitmap::kRGB_565_Config: + return GL_UNSIGNED_SHORT_5_6_5; + default: + return -1; + } +} + +static EGLConfig defaultPbufferConfig(EGLDisplay display) +{ + EGLConfig config; + EGLint numConfigs; + + static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + eglChooseConfig(display, configAttribs, &config, 1, &numConfigs); + GLUtils::checkEglError("eglPbufferConfig"); + if (numConfigs != 1) + ALOGI("eglPbufferConfig failed (%d)\n", numConfigs); + + return config; +} + +static EGLSurface createPbufferSurface(EGLDisplay display, const EGLConfig& config, + EGLint* errorCode) +{ + const EGLint attribList[] = { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE + }; + EGLSurface surface = eglCreatePbufferSurface(display, config, attribList); + + if (errorCode) + *errorCode = eglGetError(); + else + GLUtils::checkEglError("eglCreatePbufferSurface"); + + if (surface == EGL_NO_SURFACE) + return EGL_NO_SURFACE; + + return surface; +} + +EGLContext GLUtils::createBackgroundContext(EGLContext sharedContext) +{ + checkEglError("<init>"); + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + checkEglError("eglGetDisplay"); + if (display == EGL_NO_DISPLAY) { + ALOGE("eglGetDisplay returned EGL_NO_DISPLAY"); + return EGL_NO_CONTEXT; + } + + EGLint majorVersion; + EGLint minorVersion; + EGLBoolean returnValue = eglInitialize(display, &majorVersion, &minorVersion); + checkEglError("eglInitialize", returnValue); + if (returnValue != EGL_TRUE) { + ALOGE("eglInitialize failed\n"); + return EGL_NO_CONTEXT; + } + + EGLConfig config = defaultPbufferConfig(display); + EGLSurface surface = createPbufferSurface(display, config, 0); + + EGLint surfaceConfigId; + EGLBoolean success = eglGetConfigAttrib(display, config, EGL_CONFIG_ID, &surfaceConfigId); + + EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + EGLContext context = eglCreateContext(display, config, sharedContext, contextAttribs); + checkEglError("eglCreateContext"); + if (context == EGL_NO_CONTEXT) { + ALOGE("eglCreateContext failed\n"); + return EGL_NO_CONTEXT; + } + + returnValue = eglMakeCurrent(display, surface, surface, context); + checkEglError("eglMakeCurrent", returnValue); + if (returnValue != EGL_TRUE) { + ALOGE("eglMakeCurrent failed\n"); + return EGL_NO_CONTEXT; + } + + return context; +} + +void GLUtils::deleteTexture(GLuint* texture) +{ + glDeleteTextures(1, texture); + GLUtils::checkGlError("glDeleteTexture"); + *texture = 0; +} + +GLuint GLUtils::createSampleColorTexture(int r, int g, int b) +{ + GLuint texture; + glGenTextures(1, &texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + GLubyte pixels[4 *3] = { + r, g, b, + r, g, b, + r, g, b, + r, g, b + }; + glBindTexture(GL_TEXTURE_2D, texture); + GLUtils::checkGlError("glBindTexture"); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); + GLUtils::checkGlError("glTexImage2D"); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + return texture; +} + +GLuint GLUtils::createSampleTexture() +{ + GLuint texture; + glGenTextures(1, &texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + GLubyte pixels[4 *3] = { + 255, 0, 0, + 0, 255, 0, + 0, 0, 255, + 255, 255, 0 + }; + glBindTexture(GL_TEXTURE_2D, texture); + GLUtils::checkGlError("glBindTexture"); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); + GLUtils::checkGlError("glTexImage2D"); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + return texture; +} + +GLuint GLUtils::createTileGLTexture(int width, int height) +{ + GLuint texture; + glGenTextures(1, &texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + GLubyte* pixels = 0; +#ifdef DEBUG + int length = width * height * 4; + pixels = new GLubyte[length]; + for (int i = 0; i < length; i++) + pixels[i] = i % 256; +#endif + glBindTexture(GL_TEXTURE_2D, texture); + GLUtils::checkGlError("glBindTexture"); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GLUtils::checkGlError("glTexImage2D"); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#ifdef DEBUG + delete pixels; +#endif + return texture; +} + +bool GLUtils::isPureColorBitmap(const SkBitmap& bitmap, Color& pureColor) +{ + // If the bitmap is the pure color, skip the transfer step, and update the Tile Info. + // This check is taking < 1ms if we do full bitmap check per tile. + // TODO: use the SkPicture to determine whether or not a tile is single color. + pureColor = Color(Color::transparent); + bitmap.lockPixels(); + bool sameColor = true; + int bitmapWidth = bitmap.width(); + + // Create a row of pure color using the first pixel. + // TODO: improve the perf here, by either picking a random pixel, or + // creating an array of rows with pre-defined commonly used color, add + // smart LUT to speed things up if possible. + int* firstPixelPtr = static_cast<int*> (bitmap.getPixels()); + int* pixelsRow = new int[bitmapWidth]; + for (int i = 0; i < bitmapWidth; i++) + pixelsRow[i] = (*firstPixelPtr); + + // Then compare the pure color row with each row of the bitmap. + for (int j = 0; j < bitmap.height(); j++) { + if (memcmp(pixelsRow, &firstPixelPtr[bitmapWidth * j], 4 * bitmapWidth)) { + sameColor = false; + break; + } + } + delete pixelsRow; + pixelsRow = 0; + + if (sameColor) { + char* rgbaPtr = static_cast<char*>(bitmap.getPixels()); + pureColor = Color(rgbaPtr[0], rgbaPtr[1], rgbaPtr[2], rgbaPtr[3]); + ALOGV("sameColor tile found , %x at (%d, %d, %d, %d)", + *firstPixelPtr, rgbaPtr[0], rgbaPtr[1], rgbaPtr[2], rgbaPtr[3]); + } + bitmap.unlockPixels(); + + return sameColor; +} + +// Return true when the tile is pure color. +bool GLUtils::skipTransferForPureColor(const TileRenderInfo* renderInfo, + const SkBitmap& bitmap) +{ + bool skipTransfer = false; + Tile* tilePtr = renderInfo->baseTile; + + // TODO: use pure color for partial invals as well + if (renderInfo->invalRect) + return false; + + if (tilePtr) { + TileTexture* tileTexture = tilePtr->backTexture(); + // Check the bitmap, and make everything ready here. + if (tileTexture && renderInfo->isPureColor) { + // update basetile's info + // Note that we are skipping the whole TransferQueue. + renderInfo->textureInfo->m_width = bitmap.width(); + renderInfo->textureInfo->m_height = bitmap.height(); + renderInfo->textureInfo->m_internalFormat = GL_RGBA; + + TilesManager::instance()->transferQueue()->addItemInPureColorQueue(renderInfo); + + skipTransfer = true; + } + } + return skipTransfer; +} + +void GLUtils::paintTextureWithBitmap(const TileRenderInfo* renderInfo, + const SkBitmap& bitmap) +{ + if (!renderInfo) + return; + const SkSize& requiredSize = renderInfo->tileSize; + TextureInfo* textureInfo = renderInfo->textureInfo; + + if (skipTransferForPureColor(renderInfo, bitmap)) + return; + + if (requiredSize.equals(textureInfo->m_width, textureInfo->m_height)) + GLUtils::updateQueueWithBitmap(renderInfo, bitmap); + else { + if (!requiredSize.equals(bitmap.width(), bitmap.height())) { + ALOGV("The bitmap size (%d,%d) does not equal the texture size (%d,%d)", + bitmap.width(), bitmap.height(), + requiredSize.width(), requiredSize.height()); + } + GLUtils::updateQueueWithBitmap(renderInfo, bitmap); + + textureInfo->m_width = bitmap.width(); + textureInfo->m_height = bitmap.height(); + textureInfo->m_internalFormat = GL_RGBA; + } +} + +void GLUtils::updateQueueWithBitmap(const TileRenderInfo* renderInfo, const SkBitmap& bitmap) +{ + if (!renderInfo + || !renderInfo->textureInfo + || !renderInfo->baseTile) + return; + + TilesManager::instance()->transferQueue()->updateQueueWithBitmap(renderInfo, bitmap); +} + +bool GLUtils::updateSharedSurfaceTextureWithBitmap(ANativeWindow* anw, const SkBitmap& bitmap) +{ + SkAutoLockPixels alp(bitmap); + if (!bitmap.getPixels()) + return false; + ANativeWindow_Buffer buffer; + if (ANativeWindow_lock(anw, &buffer, 0)) + return false; + if (buffer.width < bitmap.width() || buffer.height < bitmap.height()) { + ALOGW("bitmap (%dx%d) too large for buffer (%dx%d)!", + bitmap.width(), bitmap.height(), + buffer.width, buffer.height); + ANativeWindow_unlockAndPost(anw); + return false; + } + uint8_t* img = (uint8_t*)buffer.bits; + int row; + int bpp = 4; // Now we only deal with RGBA8888 format. + bitmap.lockPixels(); + uint8_t* bitmapOrigin = static_cast<uint8_t*>(bitmap.getPixels()); + + if (buffer.stride != bitmap.width()) + // Copied line by line since we need to handle the offsets and stride. + for (row = 0 ; row < bitmap.height(); row ++) { + uint8_t* dst = &(img[buffer.stride * row * bpp]); + uint8_t* src = &(bitmapOrigin[bitmap.width() * row * bpp]); + memcpy(dst, src, bpp * bitmap.width()); + } + else + memcpy(img, bitmapOrigin, bpp * bitmap.width() * bitmap.height()); + + bitmap.unlockPixels(); + ANativeWindow_unlockAndPost(anw); + return true; +} + +void GLUtils::createTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, GLint filter) +{ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, texture); + GLUtils::checkGlError("glBindTexture"); + SkBitmap::Config config = bitmap.getConfig(); + int internalformat = getInternalFormat(config); + int type = getType(config); + bitmap.lockPixels(); + glTexImage2D(GL_TEXTURE_2D, 0, internalformat, bitmap.width(), bitmap.height(), + 0, internalformat, type, bitmap.getPixels()); + bitmap.unlockPixels(); + if (GLUtils::checkGlError("glTexImage2D")) { +#ifndef DEBUG + if (allowGLLog()) +#endif + ALOGE("GL ERROR: glTexImage2D parameters are : bitmap.width() %d, bitmap.height() %d," + " internalformat 0x%x, type 0x%x, bitmap.getPixels() %p", + bitmap.width(), bitmap.height(), internalformat, type, bitmap.getPixels()); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + + // The following is a workaround -- remove when EGLImage texture upload is fixed. + GLuint fboID; + glGenFramebuffers(1, &fboID); + glBindFramebuffer(GL_FRAMEBUFFER, fboID); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + glCheckFramebufferStatus(GL_FRAMEBUFFER); // should return GL_FRAMEBUFFER_COMPLETE + + glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO + glDeleteFramebuffers(1, &fboID); +} + +void GLUtils::updateTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, + const IntRect& inval, GLint filter) +{ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, texture); + GLUtils::checkGlError("glBindTexture"); + SkBitmap::Config config = bitmap.getConfig(); + int internalformat = getInternalFormat(config); + int type = getType(config); + bitmap.lockPixels(); + if (inval.isEmpty()) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), + internalformat, type, bitmap.getPixels()); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, inval.x(), inval.y(), inval.width(), inval.height(), + internalformat, type, bitmap.getPixels()); + } + bitmap.unlockPixels(); + if (GLUtils::checkGlError("glTexSubImage2D")) { +#ifndef DEBUG + if (allowGLLog()) +#endif + ALOGE("GL ERROR: glTexSubImage2D parameters are : bitmap.width() %d, bitmap.height() %d," + " internalformat 0x%x, type 0x%x, bitmap.getPixels() %p", + bitmap.width(), bitmap.height(), internalformat, type, bitmap.getPixels()); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); +} + +void GLUtils::createEGLImageFromTexture(GLuint texture, EGLImageKHR* image) +{ + EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(texture); + static const EGLint attr[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + *image = eglCreateImageKHR(eglGetCurrentDisplay(), eglGetCurrentContext(), + EGL_GL_TEXTURE_2D_KHR, buffer, attr); + GLUtils::checkEglError("eglCreateImage", (*image != EGL_NO_IMAGE_KHR)); +} + +void GLUtils::createTextureFromEGLImage(GLuint texture, EGLImageKHR image, GLint filter) +{ + glBindTexture(GL_TEXTURE_2D, texture); + GLUtils::checkGlError("glBindTexture"); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); +} + +void GLUtils::convertToTransformationMatrix(const float* matrix, TransformationMatrix& transformMatrix) +{ + transformMatrix.setMatrix( + matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5], matrix[6], matrix[7], + matrix[8], matrix[9], matrix[10], matrix[11], + matrix[12], matrix[13], matrix[14], matrix[15]); +} + +void GLUtils::clearBackgroundIfOpaque(const Color* backgroundColor) +{ + if (!backgroundColor->hasAlpha()) { + if (TilesManager::instance()->invertedScreen()) { + float color = 1.0 - ((((float) backgroundColor->red() / 255.0) + + ((float) backgroundColor->green() / 255.0) + + ((float) backgroundColor->blue() / 255.0)) / 3.0); + glClearColor(color, color, color, 1); + } else { + glClearColor((float)backgroundColor->red() / 255.0, + (float)backgroundColor->green() / 255.0, + (float)backgroundColor->blue() / 255.0, 1); + } + glClear(GL_COLOR_BUFFER_BIT); + } +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/GLUtils.h b/Source/WebCore/platform/graphics/android/rendering/GLUtils.h new file mode 100644 index 0000000..d4a2e84 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GLUtils.h @@ -0,0 +1,98 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GLUtils_h +#define GLUtils_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "Color.h" +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "TransformationMatrix.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +namespace android { + +class SurfaceTexture; + +} // namespace android + +namespace WebCore { + +class TileRenderInfo; + +class GLUtils { + +public: + // Matrix utilities + static void toGLMatrix(GLfloat* flattened, const TransformationMatrix& matrix); + static void toSkMatrix(SkMatrix& skmatrix, const TransformationMatrix& matrix); + static void setOrthographicMatrix(TransformationMatrix& ortho, float left, float top, + float right, float bottom, float nearZ, float farZ); + + // GL & EGL error checks + static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE); + static bool checkGlErrorOn(void* p, const char* op); + static bool checkGlError(const char* op); + static void checkSurfaceTextureError(const char* functionName, int status); + + // GL & EGL extension checks + static bool isEGLImageSupported(); + static bool isEGLFenceSyncSupported(); + + // Texture utilities + static EGLContext createBackgroundContext(EGLContext sharedContext); + static void deleteTexture(GLuint* texture); + static GLuint createSampleColorTexture(int r, int g, int b); + static GLuint createSampleTexture(); + static GLuint createTileGLTexture(int width, int height); + + static void createTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, GLint filter = GL_LINEAR); + static void updateTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, const IntRect&, GLint filter = GL_LINEAR); + static void createEGLImageFromTexture(GLuint texture, EGLImageKHR* image); + static void createTextureFromEGLImage(GLuint texture, EGLImageKHR image, GLint filter = GL_LINEAR); + + static void paintTextureWithBitmap(const TileRenderInfo* renderInfo, const SkBitmap& bitmap); + static void updateQueueWithBitmap(const TileRenderInfo* , const SkBitmap& bitmap); + static bool updateSharedSurfaceTextureWithBitmap(ANativeWindow* anw, const SkBitmap& bitmap); + static void convertToTransformationMatrix(const float* matrix, TransformationMatrix& transformMatrix); + + static bool isPureColorBitmap(const SkBitmap& bitmap, Color& pureColor); + static bool skipTransferForPureColor(const TileRenderInfo* renderInfo, + const SkBitmap& bitmap); + static void clearBackgroundIfOpaque(const Color* backgroundColor); + static bool allowGLLog(); + static double m_previousLogTime; + static int m_currentLogCounter; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // GLUtils_h diff --git a/Source/WebCore/platform/graphics/android/rendering/GaneshContext.cpp b/Source/WebCore/platform/graphics/android/rendering/GaneshContext.cpp new file mode 100644 index 0000000..bf43652 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GaneshContext.cpp @@ -0,0 +1,184 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "GaneshContext" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "GaneshContext.h" + +#include "AndroidLog.h" +#include "GLUtils.h" +#include "TextureInfo.h" +#include "TilesManager.h" +#include "TransferQueue.h" + +#include "android/native_window.h" + +#if USE(ACCELERATED_COMPOSITING) + +namespace WebCore { + +GaneshContext::GaneshContext() + : m_grContext(0) + , m_tileDeviceSurface(0) + , m_surfaceConfig(0) + , m_surfaceContext(EGL_NO_CONTEXT) +{ +} + +GaneshContext* GaneshContext::gInstance = 0; + +GaneshContext* GaneshContext::instance() +{ + if (!gInstance) + gInstance = new GaneshContext(); + return gInstance; +} + +GrContext* GaneshContext::getGrContext() +{ + if (!m_grContext) { + m_grContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL); + } + return m_grContext; +} + +void GaneshContext::flush() +{ + if (m_grContext) + m_grContext->flush(); +} + +SkDevice* GaneshContext::getDeviceForTile(const TileRenderInfo& renderInfo) +{ + // Ganesh should be the only code in the rendering thread that is using GL + // and setting the EGLContext. If this is not the case then we need to + // reset the Ganesh context to prevent rendering issues. + bool contextNeedsReset = false; + if (eglGetCurrentContext() != m_surfaceContext) { + ALOGV("Warning: EGLContext has Changed! %p, %p", + m_surfaceContext, eglGetCurrentContext()); + contextNeedsReset = true; + } + + EGLDisplay display; + + if (!m_surfaceContext) { + + if(eglGetCurrentContext() != EGL_NO_CONTEXT) { + ALOGV("ERROR: should not have a context yet"); + } + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + GLUtils::checkEglError("eglGetDisplay"); + + EGLint majorVersion; + EGLint minorVersion; + EGLBoolean returnValue = eglInitialize(display, &majorVersion, &minorVersion); + GLUtils::checkEglError("eglInitialize", returnValue); + + EGLint numConfigs; + static const EGLint configAttribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + + eglChooseConfig(display, configAttribs, &m_surfaceConfig, 1, &numConfigs); + GLUtils::checkEglError("eglChooseConfig"); + + static const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + m_surfaceContext = eglCreateContext(display, m_surfaceConfig, NULL, contextAttribs); + GLUtils::checkEglError("eglCreateContext"); + } else { + display = eglGetCurrentDisplay(); + GLUtils::checkEglError("eglGetCurrentDisplay"); + } + + TransferQueue* tileQueue = TilesManager::instance()->transferQueue(); + if (tileQueue->m_eglSurface == EGL_NO_SURFACE) { + + const float tileWidth = renderInfo.tileSize.width(); + const float tileHeight = renderInfo.tileSize.height(); + + ANativeWindow* anw = tileQueue->m_ANW.get(); + + int result = ANativeWindow_setBuffersGeometry(anw, (int)tileWidth, + (int)tileHeight, WINDOW_FORMAT_RGBA_8888); + + renderInfo.textureInfo->m_width = tileWidth; + renderInfo.textureInfo->m_height = tileHeight; + tileQueue->m_eglSurface = eglCreateWindowSurface(display, m_surfaceConfig, anw, NULL); + + GLUtils::checkEglError("eglCreateWindowSurface"); + ALOGV("eglCreateWindowSurface"); + } + + EGLBoolean returnValue = eglMakeCurrent(display, tileQueue->m_eglSurface, tileQueue->m_eglSurface, m_surfaceContext); + GLUtils::checkEglError("eglMakeCurrent", returnValue); + ALOGV("eglMakeCurrent"); + + if (!m_tileDeviceSurface) { + + GrPlatformRenderTargetDesc renderTargetDesc; + renderTargetDesc.fWidth = TilesManager::tileWidth(); + renderTargetDesc.fHeight = TilesManager::tileHeight(); + renderTargetDesc.fConfig = kRGBA_8888_PM_GrPixelConfig; + renderTargetDesc.fSampleCnt = 0; + renderTargetDesc.fStencilBits = 8; + renderTargetDesc.fRenderTargetHandle = 0; + + GrContext* grContext = getGrContext(); + GrRenderTarget* renderTarget = grContext->createPlatformRenderTarget(renderTargetDesc); + + m_tileDeviceSurface = new SkGpuDevice(grContext, renderTarget); + renderTarget->unref(); + ALOGV("generated device %p", m_tileDeviceSurface); + } + + GLUtils::checkGlError("getDeviceForTile"); + + // We must reset the Ganesh context only after we are sure we have + // re-established our EGLContext as the current context. + if (m_tileDeviceSurface && contextNeedsReset) + getGrContext()->resetContext(); + + return m_tileDeviceSurface; +} + + + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/GaneshContext.h b/Source/WebCore/platform/graphics/android/rendering/GaneshContext.h new file mode 100644 index 0000000..57e8e19 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GaneshContext.h @@ -0,0 +1,63 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GaneshContext_h +#define GaneshContext_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "BaseRenderer.h" +#include "GrContext.h" +#include "SkGpuDevice.h" +#include <EGL/egl.h> + +namespace WebCore { + +class GaneshContext { +public: + static GaneshContext* instance(); + + SkDevice* getDeviceForTile(const TileRenderInfo& renderInfo); + + void flush(); + +private: + + GaneshContext(); + + GrContext* getGrContext(); + GrContext* m_grContext; + + SkGpuDevice* m_tileDeviceSurface; + EGLConfig m_surfaceConfig; + EGLContext m_surfaceContext; + + static GaneshContext* gInstance; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // GaneshContext_h diff --git a/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.cpp b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.cpp new file mode 100644 index 0000000..208adb6 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "GaneshRenderer" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "GaneshRenderer.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "GaneshContext.h" +#include "SkCanvas.h" +#include "SkGpuDevice.h" +#include "TilesManager.h" +#include "TransferQueue.h" + +namespace WebCore { + +GaneshRenderer::GaneshRenderer() : BaseRenderer(BaseRenderer::Ganesh) +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("GaneshRenderer"); +#endif +} + +GaneshRenderer::~GaneshRenderer() +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("GaneshRenderer"); +#endif +} + +void GaneshRenderer::setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* canvas) +{ + GaneshContext* ganesh = GaneshContext::instance(); + + TransferQueue* tileQueue = TilesManager::instance()->transferQueue(); + + tileQueue->lockQueue(); + + bool ready = tileQueue->readyForUpdate(); + if (!ready) { + ALOGV("!ready"); + tileQueue->unlockQueue(); + return; + } + + SkDevice* device = NULL; + if (renderInfo.tileSize.width() == TilesManager::tileWidth() + && renderInfo.tileSize.height() == TilesManager::tileHeight()) { + device = ganesh->getDeviceForTile(renderInfo); + } else { + // TODO support arbitrary sizes for layers + ALOGV("ERROR: expected (%d,%d) actual (%d,%d)", + TilesManager::tileWidth(), TilesManager::tileHeight(), + renderInfo.tileSize.width(), renderInfo.tileSize.height()); + } + + // set the GPU device to the canvas + canvas->setDevice(device); +} + +void GaneshRenderer::setupPartialInval(const TileRenderInfo& renderInfo, SkCanvas* canvas) +{ + // set the clip to our invalRect + SkRect clipRect = SkRect::MakeLTRB(renderInfo.invalRect->fLeft, + renderInfo.invalRect->fTop, + renderInfo.invalRect->fRight, + renderInfo.invalRect->fBottom); + canvas->clipRect(clipRect); +} + +void GaneshRenderer::renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas) +{ + ALOGV("rendered to tile (%d,%d)", renderInfo.x, renderInfo.y); + + GaneshContext::instance()->flush(); + + // In SurfaceTextureMode we must call swapBuffers to unlock and post the + // tile's ANativeWindow (i.e. SurfaceTexture) buffer + TransferQueue* tileQueue = TilesManager::instance()->transferQueue(); + eglSwapBuffers(eglGetCurrentDisplay(), tileQueue->m_eglSurface); + tileQueue->addItemInTransferQueue(&renderInfo, GpuUpload, 0); + tileQueue->unlockQueue(); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.h b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.h new file mode 100644 index 0000000..d7eda24 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.h @@ -0,0 +1,61 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GaneshRenderer_h +#define GaneshRenderer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "BaseRenderer.h" +#include "SkRect.h" + +class SkCanvas; +class SkDevice; + +namespace WebCore { + +/** + * + */ +class GaneshRenderer : public BaseRenderer { +public: + GaneshRenderer(); + ~GaneshRenderer(); + +protected: + + virtual void setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* canvas); + virtual void setupPartialInval(const TileRenderInfo& renderInfo, SkCanvas* canvas); + virtual void renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas); + virtual void checkForPureColor(TileRenderInfo& renderInfo, SkCanvas* canvas) { + renderInfo.isPureColor = false; + } + +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // GaneshRenderer_h diff --git a/Source/WebCore/platform/graphics/android/rendering/ImageTexture.cpp b/Source/WebCore/platform/graphics/android/rendering/ImageTexture.cpp new file mode 100644 index 0000000..b2ead6a --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/ImageTexture.cpp @@ -0,0 +1,256 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "ImageTexture" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "ImageTexture.h" + +#include "AndroidLog.h" +#include "ClassTracker.h" +#include "ImagesManager.h" +#include "LayerAndroid.h" +#include "SkDevice.h" +#include "SkPicture.h" +#include "TileGrid.h" +#include "TilesManager.h" + +namespace WebCore { + +// CRC computation adapted from Tools/DumpRenderTree/CyclicRedundancyCheck.cpp +static void makeCrcTable(unsigned crcTable[256]) +{ + for (unsigned i = 0; i < 256; i++) { + unsigned c = i; + for (int k = 0; k < 8; k++) { + if (c & 1) + c = -306674912 ^ ((c >> 1) & 0x7fffffff); + else + c = c >> 1; + } + crcTable[i] = c; + } +} + +unsigned computeCrc(uint8_t* buffer, size_t size) +{ + static unsigned crcTable[256]; + static bool crcTableComputed = false; + if (!crcTableComputed) { + makeCrcTable(crcTable); + crcTableComputed = true; + } + + unsigned crc = 0xffffffffL; + for (size_t i = 0; i < size; ++i) + crc = crcTable[(crc ^ buffer[i]) & 0xff] ^ ((crc >> 8) & 0x00ffffffL); + return crc ^ 0xffffffffL; +} + +ImageTexture::ImageTexture(SkBitmap* bmp, unsigned crc) + : m_image(bmp) + , m_texture(0) + , m_layer(0) + , m_picture(0) + , m_crc(crc) +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("ImageTexture"); +#endif + if (!m_image) + return; + + // NOTE: This constructor is called on the webcore thread + + // Create a picture containing the image (needed for TileGrid) + m_picture = new SkPicture(); + SkCanvas* pcanvas = m_picture->beginRecording(m_image->width(), m_image->height()); + pcanvas->clear(SkColorSetARGBInline(0, 0, 0, 0)); + pcanvas->drawBitmap(*m_image, 0, 0); + m_picture->endRecording(); +} + +ImageTexture::~ImageTexture() +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("ImageTexture"); +#endif + delete m_image; + delete m_texture; + SkSafeUnref(m_picture); +} + +SkBitmap* ImageTexture::convertBitmap(SkBitmap* bitmap) +{ + SkBitmap* img = new SkBitmap(); + int w = bitmap->width(); + int h = bitmap->height(); + + // Create a copy of the image + img->setConfig(SkBitmap::kARGB_8888_Config, w, h); + img->allocPixels(); + SkDevice* device = new SkDevice(*img); + SkCanvas canvas; + canvas.setDevice(device); + device->unref(); + SkRect dest; + dest.set(0, 0, w, h); + img->setIsOpaque(false); + img->eraseARGB(0, 0, 0, 0); + canvas.drawBitmapRect(*bitmap, 0, dest); + + return img; +} + +unsigned ImageTexture::computeCRC(const SkBitmap* bitmap) +{ + if (!bitmap) + return 0; + bitmap->lockPixels(); + uint8_t* img = static_cast<uint8_t*>(bitmap->getPixels()); + unsigned crc = computeCrc(img, bitmap->getSize()); + bitmap->unlockPixels(); + return crc; +} + +bool ImageTexture::equalsCRC(unsigned crc) +{ + return m_crc == crc; +} + +int ImageTexture::nbTextures() +{ + if (!hasContentToShow()) + return 0; + if (!m_texture) + return 0; + + // TODO: take in account the visible clip (need to maintain + // a list of the clients layer, etc.) + IntRect visibleArea(0, 0, m_image->width(), m_image->height()); + int nbTextures = m_texture->nbTextures(visibleArea, 1.0); + ALOGV("ImageTexture %p, %d x %d needs %d textures", + this, m_image->width(), m_image->height(), + nbTextures); + return nbTextures; +} + +bool ImageTexture::hasContentToShow() +{ + // Don't display 1x1 image -- no need to allocate a full texture for this + if (!m_image) + return false; + if (m_image->width() == 1 && m_image->height() == 1) + return false; + return true; +} + +bool ImageTexture::prepareGL(GLWebViewState* state) +{ + if (!hasContentToShow()) + return false; + + if (!m_texture && m_picture) { + bool isLayerTile = true; + m_texture = new TileGrid(isLayerTile); + SkRegion region; + region.setRect(0, 0, m_image->width(), m_image->height()); + m_texture->markAsDirty(region); + } + + if (!m_texture) + return false; + + IntRect unclippedArea(0, 0, m_image->width(), m_image->height()); + m_texture->prepareGL(state, 1.0, unclippedArea, unclippedArea, this); + if (m_texture->isReady()) { + m_texture->swapTiles(); + return false; + } + return true; +} + +const TransformationMatrix* ImageTexture::transform() +{ + if (!m_layer) + return 0; + + FloatPoint p(0, 0); + p = m_layer->drawTransform()->mapPoint(p); + IntRect layerArea = m_layer->unclippedArea(); + float scaleW = static_cast<float>(layerArea.width()) / static_cast<float>(m_image->width()); + float scaleH = static_cast<float>(layerArea.height()) / static_cast<float>(m_image->height()); + TransformationMatrix d = *(m_layer->drawTransform()); + TransformationMatrix m; + m.scaleNonUniform(scaleW, scaleH); + m_layerMatrix = d.multiply(m); + return &m_layerMatrix; +} + +float ImageTexture::opacity() +{ + if (!m_layer) + return 1.0; + return m_layer->drawOpacity(); +} + +bool ImageTexture::paint(Tile* tile, SkCanvas* canvas) +{ + if (!m_picture) { + ALOGV("IT %p COULDNT PAINT, NO PICTURE", this); + return false; + } + + ALOGV("IT %p painting tile %d, %d with picture %p", this, tile->x(), tile->y(), m_picture); + canvas->drawPicture(*m_picture); + + return true; +} + +void ImageTexture::drawGL(LayerAndroid* layer, float opacity) +{ + if (!layer) + return; + if (!hasContentToShow()) + return; + + // TileGrid::draw() will call us back to know the + // transform and opacity, so we need to set m_layer + m_layer = layer; + if (m_texture) { + IntRect visibleArea = m_layer->visibleArea(); + m_texture->drawGL(visibleArea, opacity, transform()); + } + m_layer = 0; +} + +void ImageTexture::drawCanvas(SkCanvas* canvas, SkRect& rect) +{ + if (canvas && m_image) + canvas->drawBitmapRect(*m_image, 0, rect); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/ImageTexture.h b/Source/WebCore/platform/graphics/android/rendering/ImageTexture.h new file mode 100644 index 0000000..34430f1 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/ImageTexture.h @@ -0,0 +1,110 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageTexture_h +#define ImageTexture_h + +#include "GLUtils.h" +#include "SkBitmap.h" +#include "SkBitmapRef.h" +#include "SkPicture.h" +#include "SkRefCnt.h" +#include "TilePainter.h" + +namespace WebCore { + +class GLWebViewState; +class LayerAndroid; +class TexturesResult; +class TileGrid; + +///////////////////////////////////////////////////////////////////////////////// +// Image sharing codepath for layers +///////////////////////////////////////////////////////////////////////////////// +// +// Layers containing only an image take a slightly different codepath; +// GraphicsLayer::setContentsToImage() is called on the webcore thread, +// passing an Image instance. We get the native image (an SkBitmap) and create +// an ImageTexture instance with it (or increment the refcount of an existing +// instance if the SkBitmap is similar to one already stored in ImagesManager, +// i.e. if two GraphicsLayer share the same image). +// +// To detect if an image is similar, we compute and use a CRC. Each ImageTexture +// is stored in ImagesManager using its CRC as a hash key. +// Simply comparing the address is not enough -- different image could end up +// at the same address (i.e. the image is deallocated then a new one is +// reallocated at the old address) +// +// Each ImageTexture's CRC being unique, LayerAndroid instances simply store that +// and retain/release the corresponding ImageTexture (so that +// queued painting request will work correctly and not crash...). +// LayerAndroid running on the UI thread will get the corresponding +// ImageTexture at draw time. +// +// ImageTexture recopy the original SkBitmap so that they can safely be used +// on a different thread; it uses TileGrid to allocate and paint the image, +// so that we can share the same textures and limits as the rest of the layers. +// +///////////////////////////////////////////////////////////////////////////////// +class ImageTexture : public TilePainter { +public: + ImageTexture(SkBitmap* bmp, unsigned crc); + virtual ~ImageTexture(); + + bool prepareGL(GLWebViewState*); + void drawGL(LayerAndroid* layer, float opacity); + void drawCanvas(SkCanvas*, SkRect&); + bool hasContentToShow(); + SkBitmap* bitmap() { return m_image; } + unsigned imageCRC() { return m_crc; } + + static SkBitmap* convertBitmap(SkBitmap* bitmap); + + static unsigned computeCRC(const SkBitmap* bitmap); + bool equalsCRC(unsigned crc); + + // methods used by TileGrid + virtual bool paint(Tile* tile, SkCanvas* canvas); + virtual float opacity(); + + int nbTextures(); + + virtual SurfaceType type() { return TilePainter::Image; } + +private: + const TransformationMatrix* transform(); + + SkBitmapRef* m_imageRef; + SkBitmap* m_image; + TileGrid* m_texture; + LayerAndroid* m_layer; + SkPicture* m_picture; + TransformationMatrix m_layerMatrix; + unsigned m_crc; +}; + +} // namespace WebCore + +#endif // ImageTexture diff --git a/Source/WebCore/platform/graphics/android/rendering/ImagesManager.cpp b/Source/WebCore/platform/graphics/android/rendering/ImagesManager.cpp new file mode 100644 index 0000000..8452503 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/ImagesManager.cpp @@ -0,0 +1,134 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "ImagesManager" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "ImagesManager.h" + +#include "AndroidLog.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkRefCnt.h" +#include "ImageTexture.h" + +namespace WebCore { + +ImagesManager* ImagesManager::instance() +{ + if (!gInstance) + gInstance = new ImagesManager(); + + return gInstance; +} + +ImagesManager* ImagesManager::gInstance = 0; + +ImageTexture* ImagesManager::setImage(SkBitmapRef* imgRef) +{ + if (!imgRef) + return 0; + + SkBitmap* bitmap = &imgRef->bitmap(); + ImageTexture* image = 0; + SkBitmap* img = 0; + unsigned crc = 0; + + img = ImageTexture::convertBitmap(bitmap); + crc = ImageTexture::computeCRC(img); + + { + android::Mutex::Autolock lock(m_imagesLock); + if (m_images.contains(crc)) { + image = m_images.get(crc); + SkSafeRef(image); + return image; + } + } + + // the image is not in the map, we add it + + image = new ImageTexture(img, crc); + + android::Mutex::Autolock lock(m_imagesLock); + m_images.set(crc, image); + + return image; +} + +ImageTexture* ImagesManager::retainImage(unsigned imgCRC) +{ + if (!imgCRC) + return 0; + + android::Mutex::Autolock lock(m_imagesLock); + ImageTexture* image = 0; + if (m_images.contains(imgCRC)) { + image = m_images.get(imgCRC); + SkSafeRef(image); + } + return image; +} + +void ImagesManager::releaseImage(unsigned imgCRC) +{ + if (!imgCRC) + return; + + android::Mutex::Autolock lock(m_imagesLock); + if (m_images.contains(imgCRC)) { + ImageTexture* image = m_images.get(imgCRC); + if (image->getRefCnt() == 1) + m_images.remove(imgCRC); + SkSafeUnref(image); + } +} + +int ImagesManager::nbTextures() +{ + android::Mutex::Autolock lock(m_imagesLock); + HashMap<unsigned, ImageTexture*>::iterator end = m_images.end(); + int i = 0; + int nb = 0; + for (HashMap<unsigned, ImageTexture*>::iterator it = m_images.begin(); it != end; ++it) { + nb += it->second->nbTextures(); + i++; + } + return nb; +} + +bool ImagesManager::prepareTextures(GLWebViewState* state) +{ + bool ret = false; + android::Mutex::Autolock lock(m_imagesLock); + HashMap<unsigned, ImageTexture*>::iterator end = m_images.end(); + for (HashMap<unsigned, ImageTexture*>::iterator it = m_images.begin(); it != end; ++it) { + ret |= it->second->prepareGL(state); + } + return ret; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/ImagesManager.h b/Source/WebCore/platform/graphics/android/rendering/ImagesManager.h new file mode 100644 index 0000000..b915a46 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/ImagesManager.h @@ -0,0 +1,64 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImagesManager_h +#define ImagesManager_h + +#include "HashMap.h" +#include "SkBitmap.h" +#include "SkBitmapRef.h" +#include "SkRefCnt.h" +#include "Vector.h" + +#include <utils/threads.h> + +namespace WebCore { + +class ImageTexture; +class GLWebViewState; + +class ImagesManager { +public: + static ImagesManager* instance(); + + ImageTexture* setImage(SkBitmapRef* imgRef); + ImageTexture* retainImage(unsigned imgCRC); + void releaseImage(unsigned imgCRC); + + bool prepareTextures(GLWebViewState*); + int nbTextures(); + +private: + ImagesManager() {} + + static ImagesManager* gInstance; + + android::Mutex m_imagesLock; + HashMap<unsigned, ImageTexture*> m_images; +}; + +} // namespace WebCore + +#endif // ImagesManager diff --git a/Source/WebCore/platform/graphics/android/rendering/InspectorCanvas.cpp b/Source/WebCore/platform/graphics/android/rendering/InspectorCanvas.cpp new file mode 100644 index 0000000..f9edb74 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/InspectorCanvas.cpp @@ -0,0 +1,133 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "InspectorCanvas" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "InspectorCanvas.h" + +#include "AndroidLog.h" +#include "SkPicture.h" + +namespace WebCore { + + +void InspectorCanvas::setHasText() +{ + m_hasText = true; + setHasContent(); +} + +void InspectorCanvas::setHasContent() +{ + m_hasContent = true; + if (m_hasText) { + // has text. Have to paint properly, so no further + // information is useful + m_picture->abortPlayback(); + } +} + +void InspectorCanvas::setIsBackground(const SkPaint& paint) +{ + // TODO: if the paint is a solid color, opaque, and the last instruction in + // the picture, replace the picture with simple draw rect info + setHasContent(); +} + +void InspectorCanvas::commonDrawBitmap(const SkBitmap& bitmap, + const SkIRect* rect, + const SkMatrix&, + const SkPaint&) +{ + setHasContent(); +} + +void InspectorCanvas::drawPaint(const SkPaint& paint) +{ + setHasContent(); +} + +void InspectorCanvas::drawPath(const SkPath&, const SkPaint& paint) +{ + setHasContent(); +} +void InspectorCanvas::drawPoints(PointMode, size_t, + const SkPoint [], const SkPaint& paint) +{ + setHasContent(); +} + +void InspectorCanvas::drawRect(const SkRect& rect, const SkPaint& paint) +{ + if (rect.fLeft == 0 + && rect.fTop == 0 + && rect.width() >= m_picture->width() + && rect.height() >= m_picture->height()) { + // rect same size as canvas, treat layer as a single color rect until + // more content is drawn + setIsBackground(paint); + } else { + // regular rect drawing path + setHasContent(); + } + ALOGV("draw rect at %f %f, size %f %f, picture size %d %d", + rect.fLeft, rect.fTop, rect.width(), rect.height(), + m_picture->width(), m_picture->height()); +} +void InspectorCanvas::drawSprite(const SkBitmap& , int , int , + const SkPaint* paint) +{ + setHasContent(); +} + +void InspectorCanvas::drawText(const void*, size_t byteLength, SkScalar, + SkScalar, const SkPaint& paint) +{ + setHasText(); +} + +void InspectorCanvas::drawPosText(const void* , size_t byteLength, + const SkPoint [], const SkPaint& paint) +{ + setHasText(); +} + +void InspectorCanvas::drawPosTextH(const void*, size_t byteLength, + const SkScalar [], SkScalar, + const SkPaint& paint) +{ + setHasText(); +} + +void InspectorCanvas::drawTextOnPath(const void*, size_t byteLength, + const SkPath&, const SkMatrix*, + const SkPaint& paint) +{ + setHasText(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/InspectorCanvas.h b/Source/WebCore/platform/graphics/android/rendering/InspectorCanvas.h new file mode 100644 index 0000000..415a579 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/InspectorCanvas.h @@ -0,0 +1,102 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef InspectorCanvas_h +#define InspectorCanvas_h + +#include "SkBounder.h" +#include "SkCanvas.h" + +namespace WebCore { + +class InspectorBounder : public SkBounder { + virtual bool onIRect(const SkIRect& rect) + { + return false; + } +}; + +class InspectorCanvas : public SkCanvas { +public: + InspectorCanvas(SkBounder* bounder, SkPicture* picture) + : m_picture(picture) + , m_hasText(false) + , m_hasContent(false) + { + setBounder(bounder); + } + + bool hasText() {return m_hasText;} + bool hasContent() {return m_hasContent;} + + virtual bool clipPath(const SkPath&, SkRegion::Op) { + return true; + } + + virtual void commonDrawBitmap(const SkBitmap& bitmap, + const SkIRect* rect, + const SkMatrix&, + const SkPaint&); + + virtual void drawPaint(const SkPaint& paint); + virtual void drawPath(const SkPath&, const SkPaint& paint); + virtual void drawPoints(PointMode, size_t, + const SkPoint [], const SkPaint& paint); + + virtual void drawRect(const SkRect& , const SkPaint& paint); + virtual void drawSprite(const SkBitmap& , int , int , + const SkPaint* paint = NULL); + + virtual void drawText(const void*, size_t byteLength, SkScalar, + SkScalar, const SkPaint& paint); + virtual void drawPosText(const void* , size_t byteLength, + const SkPoint [], const SkPaint& paint); + virtual void drawPosTextH(const void*, size_t byteLength, + const SkScalar [], SkScalar, + const SkPaint& paint); + virtual void drawTextOnPath(const void*, size_t byteLength, + const SkPath&, const SkMatrix*, + const SkPaint& paint); + +private: + + // vector instructions exist, must repaint at any scale + void setHasText(); + + // painting is required + void setHasContent(); + + // rect covering entire content, don't need to use a texture if nothing else + // is painted + void setIsBackground(const SkPaint& paint); + + SkPicture* m_picture; + bool m_hasText; + bool m_hasContent; +}; + +} // namespace WebCore + +#endif // InspectorCanvas_h diff --git a/Source/WebCore/platform/graphics/android/rendering/PaintTileOperation.cpp b/Source/WebCore/platform/graphics/android/rendering/PaintTileOperation.cpp new file mode 100644 index 0000000..b5e435b --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/PaintTileOperation.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "PaintTileOperation" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "PaintTileOperation.h" + +#include "AndroidLog.h" +#include "GLWebViewState.h" +#include "ImageTexture.h" +#include "ImagesManager.h" +#include "LayerAndroid.h" +#include "TilesManager.h" + +namespace WebCore { + +PaintTileOperation::PaintTileOperation(Tile* tile, TilePainter* painter, + GLWebViewState* state, bool isLowResPrefetch) + : m_tile(tile) + , m_painter(painter) + , m_state(state) + , m_isLowResPrefetch(isLowResPrefetch) +{ + if (m_tile) + m_tile->setRepaintPending(true); + SkSafeRef(m_painter); +} + +PaintTileOperation::~PaintTileOperation() +{ + if (m_tile) { + m_tile->setRepaintPending(false); + m_tile = 0; + } + + if (m_painter && m_painter->type() == TilePainter::Image) { + ImageTexture* image = static_cast<ImageTexture*>(m_painter); + ImagesManager::instance()->releaseImage(image->imageCRC()); + } else { + SkSafeUnref(m_painter); + } +} + +bool PaintTileOperation::operator==(const QueuedOperation* operation) +{ + const PaintTileOperation* op = static_cast<const PaintTileOperation*>(operation); + return op->m_tile == m_tile; +} + +void PaintTileOperation::run() +{ + if (m_tile) { + m_tile->paintBitmap(m_painter); + m_tile->setRepaintPending(false); + m_tile = 0; + } +} + +int PaintTileOperation::priority() +{ + if (!m_tile) + return -1; + + int priority = 200000; + + // prioritize low res while scrolling + if (m_isLowResPrefetch) + priority = m_state->isScrolling() ? 0 : 400000; + + // prioritize higher draw count + unsigned long long currentDraw = TilesManager::instance()->getDrawGLCount(); + unsigned long long drawDelta = currentDraw - m_tile->drawCount(); + priority += 100000 * (int)std::min(drawDelta, (unsigned long long)1000); + + // prioritize unpainted tiles, within the same drawCount + if (m_tile->frontTexture()) + priority += 50000; + + // for base tiles, prioritize based on position + if (!m_tile->isLayerTile()) { + bool goingDown = m_state->goingDown(); + priority += m_tile->x(); + + if (goingDown) + priority += 100000 - (1 + m_tile->y()) * 1000; + else + priority += m_tile->y() * 1000; + } + + return priority; +} + +} diff --git a/Source/WebCore/platform/graphics/android/rendering/PaintTileOperation.h b/Source/WebCore/platform/graphics/android/rendering/PaintTileOperation.h new file mode 100644 index 0000000..1d376bf --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/PaintTileOperation.h @@ -0,0 +1,88 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PaintTileSetOperation_h +#define PaintTileSetOperation_h + +#include "Tile.h" +#include "QueuedOperation.h" +#include "SkRefCnt.h" + +namespace WebCore { + +class LayerAndroid; +class TilePainter; +class ImageTexture; + +class PaintTileOperation : public QueuedOperation { +public: + PaintTileOperation(Tile* tile, TilePainter* painter, + GLWebViewState* state, bool isLowResPrefetch); + virtual ~PaintTileOperation(); + virtual bool operator==(const QueuedOperation* operation); + virtual void run(); + // returns a rendering priority for m_tile, lower values are processed faster + virtual int priority(); + TilePainter* painter() { return m_painter; } + float scale() { return m_tile->scale(); } + +private: + Tile* m_tile; + TilePainter* m_painter; + GLWebViewState* m_state; + bool m_isLowResPrefetch; +}; + +class ScaleFilter : public OperationFilter { +public: + ScaleFilter(const TilePainter* painter, float scale) + : m_painter(painter) + , m_scale(scale) {} + virtual bool check(QueuedOperation* operation) + { + PaintTileOperation* op = static_cast<PaintTileOperation*>(operation); + return ((op->painter() == m_painter) && (op->scale() != m_scale)); + } +private: + const TilePainter* m_painter; + float m_scale; +}; + + +class TilePainterFilter : public OperationFilter { +public: + TilePainterFilter(TilePainter* painter) : m_painter(painter) {} + virtual bool check(QueuedOperation* operation) + { + PaintTileOperation* op = static_cast<PaintTileOperation*>(operation); + return op->painter() == m_painter; + } +private: + TilePainter* m_painter; +}; + +} + +#endif // PaintTileSetOperation_h diff --git a/Source/WebCore/platform/graphics/android/rendering/QueuedOperation.h b/Source/WebCore/platform/graphics/android/rendering/QueuedOperation.h new file mode 100644 index 0000000..f98efcd --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/QueuedOperation.h @@ -0,0 +1,47 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef QueuedOperation_h +#define QueuedOperation_h + +namespace WebCore { + +class QueuedOperation { +public: + virtual ~QueuedOperation() {} + virtual void run() = 0; + virtual bool operator==(const QueuedOperation* operation) = 0; + virtual int priority() = 0; +}; + +class OperationFilter { +public: + virtual ~OperationFilter() {} + virtual bool check(QueuedOperation* operation) = 0; +}; + +} + +#endif // QueuedOperation_h diff --git a/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.cpp b/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.cpp new file mode 100644 index 0000000..b880eef --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "RasterRenderer" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "RasterRenderer.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "GLUtils.h" +#include "SkBitmap.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "Tile.h" +#include "TilesManager.h" + +namespace WebCore { + +SkBitmap* RasterRenderer::g_bitmap = 0; + +RasterRenderer::RasterRenderer() : BaseRenderer(BaseRenderer::Raster) +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("RasterRenderer"); +#endif + if (!g_bitmap) { + g_bitmap = new SkBitmap(); + g_bitmap->setConfig(SkBitmap::kARGB_8888_Config, + TilesManager::instance()->tileWidth(), + TilesManager::instance()->tileHeight()); + g_bitmap->allocPixels(); + } +} + +RasterRenderer::~RasterRenderer() +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("RasterRenderer"); +#endif +} + +void RasterRenderer::setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* canvas) +{ + if (renderInfo.baseTile->isLayerTile()) { + g_bitmap->setIsOpaque(false); + g_bitmap->eraseARGB(0, 0, 0, 0); + } else { + Color defaultBackground = Color::white; + Color* background = renderInfo.tilePainter->background(); + if (!background) { + ALOGV("No background color for base layer!"); + background = &defaultBackground; + } + ALOGV("setupCanvas use background on Base Layer %x", background->rgb()); + g_bitmap->setIsOpaque(!background->hasAlpha()); + g_bitmap->eraseARGB(background->alpha(), background->red(), + background->green(), background->blue()); + } + + SkDevice* device = new SkDevice(*g_bitmap); + + canvas->setDevice(device); + + device->unref(); + + // If we have a partially painted bitmap + if (renderInfo.invalRect) { + SkRect clipRect = SkRect::MakeWH(renderInfo.invalRect->width(), + renderInfo.invalRect->height()); + // ensure the canvas origin is translated to the coordinates of our inval rect + canvas->clipRect(clipRect); + canvas->translate(-renderInfo.invalRect->fLeft, -renderInfo.invalRect->fTop); + } +} + +void RasterRenderer::renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas) +{ + const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(false); + GLUtils::paintTextureWithBitmap(&renderInfo, bitmap); +} + +void RasterRenderer::checkForPureColor(TileRenderInfo& renderInfo, SkCanvas* canvas) +{ + renderInfo.isPureColor = GLUtils::isPureColorBitmap(*g_bitmap, renderInfo.pureColor); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.h b/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.h new file mode 100644 index 0000000..39e00f2 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.h @@ -0,0 +1,62 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RasterRenderer_h +#define RasterRenderer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "BaseRenderer.h" +#include "SkBitmap.h" +#include "SkRect.h" + +class SkCanvas; +class SkDevice; + +namespace WebCore { + +/** + * + */ +class RasterRenderer : public BaseRenderer { +public: + RasterRenderer(); + ~RasterRenderer(); + +protected: + + virtual void setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* canvas); + virtual void renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas); + virtual void checkForPureColor(TileRenderInfo& renderInfo, SkCanvas* canvas); + +private: + static SkBitmap* g_bitmap; + +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // RasterRenderer_h diff --git a/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.cpp b/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.cpp new file mode 100644 index 0000000..a0d9e56 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.cpp @@ -0,0 +1,730 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "ShaderProgram" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "ShaderProgram.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "DrawQuadData.h" +#include "FloatPoint3D.h" +#include "GLUtils.h" +#include "TilesManager.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +namespace WebCore { + +static const char gVertexShader[] = + "attribute vec4 vPosition;\n" + "uniform mat4 projectionMatrix;\n" + "varying vec2 v_texCoord;\n" + "void main() {\n" + " gl_Position = projectionMatrix * vPosition;\n" + " v_texCoord = vec2(vPosition);\n" + "}\n"; + +static const char gFragmentShader[] = + "precision mediump float;\n" + "varying vec2 v_texCoord; \n" + "uniform float alpha; \n" + "uniform sampler2D s_texture; \n" + "void main() {\n" + " gl_FragColor = texture2D(s_texture, v_texCoord); \n" + " gl_FragColor *= alpha; " + "}\n"; + +// We could pass the pureColor into either Vertex or Frag Shader. +// The reason we passed the color into the Vertex Shader is that some driver +// might create redundant copy when uniforms in fragment shader changed. +static const char gPureColorVertexShader[] = + "attribute vec4 vPosition;\n" + "uniform mat4 projectionMatrix;\n" + "uniform vec4 inputColor;\n" + "varying vec4 v_color;\n" + "void main() {\n" + " gl_Position = projectionMatrix * vPosition;\n" + " v_color = inputColor;\n" + "}\n"; + +static const char gPureColorFragmentShader[] = + "precision mediump float;\n" + "varying vec4 v_color;\n" + "void main() {\n" + " gl_FragColor = v_color;\n" + "}\n"; + +static const char gFragmentShaderInverted[] = + "precision mediump float;\n" + "varying vec2 v_texCoord; \n" + "uniform float alpha; \n" + "uniform float contrast; \n" + "uniform sampler2D s_texture; \n" + "void main() {\n" + " vec4 pixel = texture2D(s_texture, v_texCoord); \n" + " float a = pixel.a; \n" + " float color = a - (0.2989 * pixel.r + 0.5866 * pixel.g + 0.1145 * pixel.b);\n" + " color = ((color - a/2.0) * contrast) + a/2.0; \n" + " pixel.rgb = vec3(color, color, color); \n " + " gl_FragColor = pixel; \n" + " gl_FragColor *= alpha; \n" + "}\n"; + +static const char gVideoVertexShader[] = + "attribute vec4 vPosition;\n" + "uniform mat4 textureMatrix;\n" + "uniform mat4 projectionMatrix;\n" + "varying vec2 v_texCoord;\n" + "void main() {\n" + " gl_Position = projectionMatrix * vPosition;\n" + " v_texCoord = vec2(textureMatrix * vec4(vPosition.x, 1.0 - vPosition.y, 0.0, 1.0));\n" + "}\n"; + +static const char gVideoFragmentShader[] = + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "uniform samplerExternalOES s_yuvTexture;\n" + "varying vec2 v_texCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(s_yuvTexture, v_texCoord);\n" + "}\n"; + +static const char gSurfaceTextureOESFragmentShader[] = + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "varying vec2 v_texCoord; \n" + "uniform float alpha; \n" + "uniform samplerExternalOES s_texture; \n" + "void main() {\n" + " gl_FragColor = texture2D(s_texture, v_texCoord); \n" + " gl_FragColor *= alpha; " + "}\n"; + +static const char gSurfaceTextureOESFragmentShaderInverted[] = + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "varying vec2 v_texCoord; \n" + "uniform float alpha; \n" + "uniform float contrast; \n" + "uniform samplerExternalOES s_texture; \n" + "void main() {\n" + " vec4 pixel = texture2D(s_texture, v_texCoord); \n" + " float a = pixel.a; \n" + " float color = a - (0.2989 * pixel.r + 0.5866 * pixel.g + 0.1145 * pixel.b);\n" + " color = ((color - a/2.0) * contrast) + a/2.0; \n" + " pixel.rgb = vec3(color, color, color); \n " + " gl_FragColor = pixel; \n" + " gl_FragColor *= alpha; \n" + "}\n"; + +GLuint ShaderProgram::loadShader(GLenum shaderType, const char* pSource) +{ + GLuint shader = glCreateShader(shaderType); + if (shader) { + glShaderSource(shader, 1, &pSource, 0); + glCompileShader(shader); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen) { + char* buf = (char*) malloc(infoLen); + if (buf) { + glGetShaderInfoLog(shader, infoLen, 0, buf); + ALOGE("could not compile shader %d:\n%s\n", shaderType, buf); + free(buf); + } + glDeleteShader(shader); + shader = 0; + } + } + } + return shader; +} + +GLint ShaderProgram::createProgram(const char* pVertexSource, const char* pFragmentSource) +{ + GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); + if (!vertexShader) { + ALOGE("couldn't load the vertex shader!"); + return -1; + } + + GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); + if (!pixelShader) { + ALOGE("couldn't load the pixel shader!"); + return -1; + } + + GLuint program = glCreateProgram(); + if (program) { + glAttachShader(program, vertexShader); + GLUtils::checkGlError("glAttachShader vertex"); + glAttachShader(program, pixelShader); + GLUtils::checkGlError("glAttachShader pixel"); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus != GL_TRUE) { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) { + char* buf = (char*) malloc(bufLength); + if (buf) { + glGetProgramInfoLog(program, bufLength, 0, buf); + ALOGE("could not link program:\n%s\n", buf); + free(buf); + } + } + glDeleteProgram(program); + program = -1; + } + } + + ShaderResource newResource(program, vertexShader, pixelShader); + m_resources.append(newResource); + return program; +} + +ShaderProgram::ShaderProgram() + : m_blendingEnabled(false) + , m_contrast(1) + , m_alphaLayer(false) + , m_currentScale(1.0f) + , m_needsInit(true) +{ +} + +void ShaderProgram::cleanupGLResources() +{ + for (unsigned int i = 0; i < m_resources.size(); i++) { + glDetachShader(m_resources[i].program, m_resources[i].vertexShader); + glDetachShader(m_resources[i].program, m_resources[i].fragmentShader); + glDeleteShader(m_resources[i].vertexShader); + glDeleteShader(m_resources[i].fragmentShader); + glDeleteProgram(m_resources[i].program); + } + glDeleteBuffers(1, m_textureBuffer); + + m_resources.clear(); + m_needsInit = true; + GLUtils::checkGlError("cleanupGLResources"); + + return; +} + +void ShaderProgram::initGLResources() +{ + // To detect whether or not resources for ShaderProgram allocated + // successfully, we clean up pre-existing errors here and will check for + // new errors at the end of this function. + GLUtils::checkGlError("before initGLResources"); + + GLint tex2DProgram = createProgram(gVertexShader, gFragmentShader); + GLint pureColorProgram = createProgram(gPureColorVertexShader, gPureColorFragmentShader); + GLint tex2DInvProgram = createProgram(gVertexShader, gFragmentShaderInverted); + GLint videoProgram = createProgram(gVideoVertexShader, gVideoFragmentShader); + GLint texOESProgram = + createProgram(gVertexShader, gSurfaceTextureOESFragmentShader); + GLint texOESInvProgram = + createProgram(gVertexShader, gSurfaceTextureOESFragmentShaderInverted); + + if (tex2DProgram == -1 + || pureColorProgram == -1 + || tex2DInvProgram == -1 + || videoProgram == -1 + || texOESProgram == -1 + || texOESInvProgram == -1) { + m_needsInit = true; + return; + } + + GLint pureColorPosition = glGetAttribLocation(pureColorProgram, "vPosition"); + GLint pureColorProjMtx = glGetUniformLocation(pureColorProgram, "projectionMatrix"); + GLint pureColorValue = glGetUniformLocation(pureColorProgram, "inputColor"); + m_handleArray[PureColor].init(-1, -1, pureColorPosition, pureColorProgram, + pureColorProjMtx, pureColorValue, -1, -1); + + GLint tex2DAlpha = glGetUniformLocation(tex2DProgram, "alpha"); + GLint tex2DPosition = glGetAttribLocation(tex2DProgram, "vPosition"); + GLint tex2DProjMtx = glGetUniformLocation(tex2DProgram, "projectionMatrix"); + GLint tex2DTexSampler = glGetUniformLocation(tex2DProgram, "s_texture"); + m_handleArray[Tex2D].init(tex2DAlpha, -1, tex2DPosition, tex2DProgram, + tex2DProjMtx, -1, tex2DTexSampler, -1); + + GLint tex2DInvAlpha = glGetUniformLocation(tex2DInvProgram, "alpha"); + GLint tex2DInvContrast = glGetUniformLocation(tex2DInvProgram, "contrast"); + GLint tex2DInvPosition = glGetAttribLocation(tex2DInvProgram, "vPosition"); + GLint tex2DInvProjMtx = glGetUniformLocation(tex2DInvProgram, "projectionMatrix"); + GLint tex2DInvTexSampler = glGetUniformLocation(tex2DInvProgram, "s_texture"); + m_handleArray[Tex2DInv].init(tex2DInvAlpha, tex2DInvContrast, + tex2DInvPosition, tex2DInvProgram, + tex2DInvProjMtx, -1, + tex2DInvTexSampler, -1); + + GLint texOESAlpha = glGetUniformLocation(texOESProgram, "alpha"); + GLint texOESPosition = glGetAttribLocation(texOESProgram, "vPosition"); + GLint texOESProjMtx = glGetUniformLocation(texOESProgram, "projectionMatrix"); + GLint texOESTexSampler = glGetUniformLocation(texOESProgram, "s_texture"); + m_handleArray[TexOES].init(texOESAlpha, -1, texOESPosition, texOESProgram, + texOESProjMtx, -1, texOESTexSampler, -1); + + GLint texOESInvAlpha = glGetUniformLocation(texOESInvProgram, "alpha"); + GLint texOESInvContrast = glGetUniformLocation(texOESInvProgram, "contrast"); + GLint texOESInvPosition = glGetAttribLocation(texOESInvProgram, "vPosition"); + GLint texOESInvProjMtx = glGetUniformLocation(texOESInvProgram, "projectionMatrix"); + GLint texOESInvTexSampler = glGetUniformLocation(texOESInvProgram, "s_texture"); + m_handleArray[TexOESInv].init(texOESInvAlpha, texOESInvContrast, + texOESInvPosition, texOESInvProgram, + texOESInvProjMtx, -1, + texOESInvTexSampler, -1); + + GLint videoPosition = glGetAttribLocation(videoProgram, "vPosition"); + GLint videoProjMtx = glGetUniformLocation(videoProgram, "projectionMatrix"); + GLint videoTexSampler = glGetUniformLocation(videoProgram, "s_yuvTexture"); + GLint videoTexMtx = glGetUniformLocation(videoProgram, "textureMatrix"); + m_handleArray[Video].init(-1, -1, videoPosition, videoProgram, + videoProjMtx, -1, videoTexSampler, + videoTexMtx); + + const GLfloat coord[] = { + 0.0f, 0.0f, // C + 1.0f, 0.0f, // D + 0.0f, 1.0f, // A + 1.0f, 1.0f // B + }; + + glGenBuffers(1, m_textureBuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); + glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), coord, GL_STATIC_DRAW); + + TransformationMatrix matrix; + // Map x,y from (0,1) to (-1, 1) + matrix.scale3d(2, 2, 1); + matrix.translate3d(-0.5, -0.5, 0); + GLUtils::toGLMatrix(m_transferProjMtx, matrix); + + m_needsInit = GLUtils::checkGlError("initGLResources"); + return; +} + +void ShaderProgram::resetBlending() +{ + glDisable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + m_blendingEnabled = false; +} + +void ShaderProgram::setBlendingState(bool enableBlending) +{ + if (enableBlending == m_blendingEnabled) + return; + + if (enableBlending) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + m_blendingEnabled = enableBlending; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Drawing +///////////////////////////////////////////////////////////////////////////////////////// + +void ShaderProgram::setupDrawing(const IntRect& viewRect, const SkRect& visibleRect, + const IntRect& webViewRect, int titleBarHeight, + const IntRect& screenClip, float scale) +{ + m_webViewRect = webViewRect; + m_titleBarHeight = titleBarHeight; + + //// viewport //// + TransformationMatrix ortho; + GLUtils::setOrthographicMatrix(ortho, visibleRect.fLeft, visibleRect.fTop, + visibleRect.fRight, visibleRect.fBottom, -1000, 1000); + // In most case , visibleRect / viewRect * scale should 1.0, but for the + // translation case, the scale factor can be 1 but visibleRect is smaller + // than viewRect, we need to tune in this factor to make sure we scale them + // right. Conceptually, that means, no matter how animation affects the + // visibleRect, the scaling should respect the viewRect if zoomScale is 1.0. + // Note that at TiledPage, we already scale the tile size inversely to make + // zooming animation right. + float orthoScaleX = scale * visibleRect.width() / viewRect.width(); + float orthoScaleY = scale * visibleRect.height() / viewRect.height(); + + TransformationMatrix orthoScale; + orthoScale.scale3d(orthoScaleX, orthoScaleY, 1.0); + + m_projectionMatrix = ortho * orthoScale; + m_viewport = visibleRect; + m_currentScale = scale; + + + //// viewRect //// + m_viewRect = viewRect; + + // We do clipping using glScissor, which needs to take + // coordinates in screen space. The following matrix transform + // content coordinates in screen coordinates. + TransformationMatrix viewTranslate; + viewTranslate.translate(1.0, 1.0); + + TransformationMatrix viewScale; + viewScale.scale3d(m_viewRect.width() * 0.5f, m_viewRect.height() * 0.5f, 1); + + m_documentToScreenMatrix = viewScale * viewTranslate * m_projectionMatrix; + + viewTranslate.scale3d(1, -1, 1); + m_documentToInvScreenMatrix = viewScale * viewTranslate * m_projectionMatrix; + + IntRect rect(0, 0, m_webViewRect.width(), m_webViewRect.height()); + m_documentViewport = m_documentToScreenMatrix.inverse().mapRect(rect); + + + //// clipping //// + IntRect mclip = screenClip; + + // the clip from frameworks is in full screen coordinates + mclip.setY(screenClip.y() - m_webViewRect.y() - m_titleBarHeight); + FloatRect tclip = convertInvScreenCoordToScreenCoord(mclip); + m_screenClip.setLocation(IntPoint(tclip.x(), tclip.y())); + // use ceilf to handle view -> doc -> view coord rounding errors + m_screenClip.setSize(IntSize(ceilf(tclip.width()), ceilf(tclip.height()))); + + resetBlending(); +} + +// Calculate the right color value sent into the shader considering the (0,1) +// clamp and alpha blending. +Color ShaderProgram::shaderColor(Color pureColor, float opacity) +{ + float r = pureColor.red() / 255.0; + float g = pureColor.green() / 255.0; + float b = pureColor.blue() / 255.0; + float a = pureColor.alpha() / 255.0; + + if (TilesManager::instance()->invertedScreen()) { + float intensity = a - (0.2989 * r + 0.5866 * g + 0.1145 * b); + intensity = ((intensity - a / 2.0) * m_contrast) + a / 2.0; + intensity *= opacity; + return Color(intensity, intensity, intensity, a * opacity); + } + return Color(r * opacity, g * opacity, b * opacity, a * opacity); +} + +// For shaders using texture, it is easy to get the type from the textureTarget. +ShaderType ShaderProgram::getTextureShaderType(GLenum textureTarget) +{ + ShaderType type = UndefinedShader; + if (textureTarget == GL_TEXTURE_2D) { + if (!TilesManager::instance()->invertedScreen()) + type = Tex2D; + else { + // With the new GPU texture upload path, we do not use an FBO + // to blit the texture we receive from the TexturesGenerator thread. + // To implement inverted rendering, we thus have to do the rendering + // live, by using a different shader. + type = Tex2DInv; + } + } else if (textureTarget == GL_TEXTURE_EXTERNAL_OES) { + if (!TilesManager::instance()->invertedScreen()) + type = TexOES; + else + type = TexOESInv; + } + return type; +} + +// This function transform a clip rect extracted from the current layer +// into a clip rect in screen coordinates -- used by the clipping rects +FloatRect ShaderProgram::rectInScreenCoord(const TransformationMatrix& drawMatrix, const IntSize& size) +{ + FloatRect srect(0, 0, size.width(), size.height()); + TransformationMatrix renderMatrix = m_documentToScreenMatrix * drawMatrix; + return renderMatrix.mapRect(srect); +} + +// used by the partial screen invals +FloatRect ShaderProgram::rectInInvScreenCoord(const TransformationMatrix& drawMatrix, const IntSize& size) +{ + FloatRect srect(0, 0, size.width(), size.height()); + TransformationMatrix renderMatrix = m_documentToInvScreenMatrix * drawMatrix; + return renderMatrix.mapRect(srect); +} + +FloatRect ShaderProgram::rectInInvScreenCoord(const FloatRect& rect) +{ + return m_documentToInvScreenMatrix.mapRect(rect); +} + +FloatRect ShaderProgram::rectInScreenCoord(const FloatRect& rect) +{ + return m_documentToScreenMatrix.mapRect(rect); +} + +FloatRect ShaderProgram::convertScreenCoordToDocumentCoord(const FloatRect& rect) +{ + return m_documentToScreenMatrix.inverse().mapRect(rect); +} + +FloatRect ShaderProgram::convertInvScreenCoordToScreenCoord(const FloatRect& rect) +{ + FloatRect documentRect = m_documentToInvScreenMatrix.inverse().mapRect(rect); + return rectInScreenCoord(documentRect); +} + +FloatRect ShaderProgram::convertScreenCoordToInvScreenCoord(const FloatRect& rect) +{ + FloatRect documentRect = m_documentToScreenMatrix.inverse().mapRect(rect); + return rectInInvScreenCoord(documentRect); +} + +// clip is in screen coordinates +void ShaderProgram::clip(const FloatRect& clip) +{ + if (clip == m_clipRect) + return; + + ALOGV("--clipping rect %f %f, %f x %f", + clip.x(), clip.y(), clip.width(), clip.height()); + + // we should only call glScissor in this function, so that we can easily + // track the current clipping rect. + + IntRect screenClip(clip.x(), + clip.y(), + clip.width(), clip.height()); + + if (!m_screenClip.isEmpty()) + screenClip.intersect(m_screenClip); + + screenClip.setY(screenClip.y() + m_viewRect.y()); + if (screenClip.x() < 0) { + int w = screenClip.width(); + w += screenClip.x(); + screenClip.setX(0); + screenClip.setWidth(w); + } + if (screenClip.y() < 0) { + int h = screenClip.height(); + h += screenClip.y(); + screenClip.setY(0); + screenClip.setHeight(h); + } + + glScissor(screenClip.x(), screenClip.y(), screenClip.width(), screenClip.height()); + + m_clipRect = clip; +} + +IntRect ShaderProgram::clippedRectWithViewport(const IntRect& rect, int margin) +{ + IntRect viewport(m_viewport.fLeft - margin, m_viewport.fTop - margin, + m_viewport.width() + margin, m_viewport.height() + margin); + viewport.intersect(rect); + return viewport; +} + +float ShaderProgram::zValue(const TransformationMatrix& drawMatrix, float w, float h) +{ + TransformationMatrix modifiedDrawMatrix = drawMatrix; + modifiedDrawMatrix.scale3d(w, h, 1); + TransformationMatrix renderMatrix = m_projectionMatrix * modifiedDrawMatrix; + FloatPoint3D point(0.5, 0.5, 0.0); + FloatPoint3D result = renderMatrix.mapPoint(point); + return result.z(); +} + +void ShaderProgram::drawQuadInternal(ShaderType type, const GLfloat* matrix, + int textureId, float opacity, + GLenum textureTarget, GLenum filter, + const Color& pureColor) +{ + glUseProgram(m_handleArray[type].programHandle); + glUniformMatrix4fv(m_handleArray[type].projMtxHandle, 1, GL_FALSE, matrix); + + if (type != PureColor) { + glActiveTexture(GL_TEXTURE0); + glUniform1i(m_handleArray[type].texSamplerHandle, 0); + glBindTexture(textureTarget, textureId); + glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, filter); + glUniform1f(m_handleArray[type].alphaHandle, opacity); + + GLint contrastHandle = m_handleArray[type].contrastHandle; + if (contrastHandle != -1) + glUniform1f(contrastHandle, m_contrast); + } else { + glUniform4f(m_handleArray[type].pureColorHandle, + pureColor.red() / 255.0, pureColor.green() / 255.0, + pureColor.blue() / 255.0, pureColor.alpha() / 255.0); + } + + GLint positionHandle = m_handleArray[type].positionHandle; + glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); + glEnableVertexAttribArray(positionHandle); + glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +// Calculate the matrix given the geometry. +GLfloat* ShaderProgram::getProjectionMatrix(const DrawQuadData* data) +{ + DrawQuadType type = data->type(); + const TransformationMatrix* matrix = data->drawMatrix(); + const SkRect* geometry = data->geometry(); + if (type == Blit) + return m_transferProjMtx; + TransformationMatrix modifiedDrawMatrix; + if (type == LayerQuad) + modifiedDrawMatrix = *matrix; + // move the drawing depending on where the texture is on the layer + modifiedDrawMatrix.translate(geometry->fLeft, geometry->fTop); + modifiedDrawMatrix.scale3d(geometry->width(), geometry->height(), 1); + + TransformationMatrix renderMatrix; + if (!m_alphaLayer) + renderMatrix = m_projectionMatrix * m_repositionMatrix + * m_webViewMatrix * modifiedDrawMatrix; + else + renderMatrix = m_projectionMatrix * modifiedDrawMatrix; + + GLUtils::toGLMatrix(m_tileProjMatrix, renderMatrix); + return m_tileProjMatrix; +} + +void ShaderProgram::drawQuad(const DrawQuadData* data) +{ + GLfloat* matrix = getProjectionMatrix(data); + + float opacity = data->opacity(); + bool forceBlending = data->forceBlending(); + bool enableBlending = forceBlending || opacity < 1.0; + + ShaderType shaderType = UndefinedShader; + int textureId = 0; + GLint textureFilter = 0; + GLenum textureTarget = 0; + + Color quadColor = data->quadColor(); + if (data->pureColor()) { + shaderType = PureColor; + quadColor = shaderColor(quadColor, opacity); + enableBlending = enableBlending || quadColor.hasAlpha(); + if (!quadColor.alpha() && enableBlending) + return; + } else { + textureId = data->textureId(); + textureFilter = data->textureFilter(); + textureTarget = data->textureTarget(); + shaderType = getTextureShaderType(textureTarget); + } + setBlendingState(enableBlending); + drawQuadInternal(shaderType, matrix, textureId, opacity, + textureTarget, textureFilter, quadColor); +} + +void ShaderProgram::drawVideoLayerQuad(const TransformationMatrix& drawMatrix, + float* textureMatrix, SkRect& geometry, + int textureId) +{ + // switch to our custom yuv video rendering program + glUseProgram(m_handleArray[Video].programHandle); + + TransformationMatrix modifiedDrawMatrix = drawMatrix; + modifiedDrawMatrix.translate(geometry.fLeft, geometry.fTop); + modifiedDrawMatrix.scale3d(geometry.width(), geometry.height(), 1); + TransformationMatrix renderMatrix = m_projectionMatrix * modifiedDrawMatrix; + + GLfloat projectionMatrix[16]; + GLUtils::toGLMatrix(projectionMatrix, renderMatrix); + glUniformMatrix4fv(m_handleArray[Video].projMtxHandle, 1, GL_FALSE, + projectionMatrix); + glUniformMatrix4fv(m_handleArray[Video].videoMtxHandle, 1, GL_FALSE, + textureMatrix); + + glActiveTexture(GL_TEXTURE0); + glUniform1i(m_handleArray[Video].texSamplerHandle, 0); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId); + + GLint videoPosition = m_handleArray[Video].positionHandle; + glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); + glEnableVertexAttribArray(videoPosition); + glVertexAttribPointer(videoPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); + + setBlendingState(false); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +void ShaderProgram::setWebViewMatrix(const float* matrix, bool alphaLayer) +{ + GLUtils::convertToTransformationMatrix(matrix, m_webViewMatrix); + m_alphaLayer = alphaLayer; +} + +void ShaderProgram::calculateAnimationDelta() +{ + // The matrix contains the scrolling info, so this rect is starting from + // the m_viewport. + // So we just need to map the webview's visible rect using the matrix, + // calculate the difference b/t transformed rect and the webViewRect, + // then we can get the delta x , y caused by the animation. + // Note that the Y is for reporting back to GL viewport, so it is inverted. + // When it is alpha animation, then we rely on the framework implementation + // such that there is no matrix applied in native webkit. + if (!m_alphaLayer) { + FloatRect rect(m_viewport.fLeft * m_currentScale, + m_viewport.fTop * m_currentScale, + m_webViewRect.width(), + m_webViewRect.height()); + rect = m_webViewMatrix.mapRect(rect); + m_animationDelta.setX(rect.x() - m_webViewRect.x() ); + m_animationDelta.setY(rect.y() + rect.height() - m_webViewRect.y() + - m_webViewRect.height() - m_titleBarHeight); + + m_repositionMatrix.makeIdentity(); + m_repositionMatrix.translate3d(-m_webViewRect.x(), -m_webViewRect.y() - m_titleBarHeight, 0); + m_repositionMatrix.translate3d(m_viewport.fLeft * m_currentScale, m_viewport.fTop * m_currentScale, 0); + m_repositionMatrix.translate3d(-m_animationDelta.x(), -m_animationDelta.y(), 0); + } else { + m_animationDelta.setX(0); + m_animationDelta.setY(0); + m_repositionMatrix.makeIdentity(); + } + +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.h b/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.h new file mode 100644 index 0000000..b233f2b --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/ShaderProgram.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ShaderProgram_h +#define ShaderProgram_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "Color.h" +#include "FloatRect.h" +#include "IntRect.h" +#include "SkRect.h" +#include "TransformationMatrix.h" +#include <GLES2/gl2.h> + +#define MAX_CONTRAST 5 + +namespace WebCore { + +class DrawQuadData; +class PureColorQuadData; +class TextureQuadData; + +enum ShaderType { + UndefinedShader = -1, + PureColor, + Tex2D, + Tex2DInv, + TexOES, + TexOESInv, + Video, + // When growing this enum list, make sure to insert before the + // MaxShaderNumber and init the m_handleArray accordingly. + MaxShaderNumber +}; + +struct ShaderHandles { + ShaderHandles() + : alphaHandle(-1) + , contrastHandle(-1) + , positionHandle(-1) + , programHandle(-1) + , projMtxHandle(-1) + , pureColorHandle(-1) + , texSamplerHandle(-1) + , videoMtxHandle(-1) + { + } + + void init(GLint alphaHdl, GLint contrastHdl, GLint posHdl, GLint pgmHdl, + GLint projMtxHdl, GLint colorHdl, GLint texSamplerHdl, + GLint videoMtxHdl) + { + alphaHandle = alphaHdl; + contrastHandle = contrastHdl; + positionHandle = posHdl; + programHandle = pgmHdl; + projMtxHandle = projMtxHdl; + pureColorHandle = colorHdl; + texSamplerHandle = texSamplerHdl; + videoMtxHandle = videoMtxHdl; + } + + GLint alphaHandle; + GLint contrastHandle; + GLint positionHandle; + GLint programHandle; + GLint projMtxHandle; + GLint pureColorHandle; + GLint texSamplerHandle; + GLint videoMtxHandle; +}; + +struct ShaderResource { + ShaderResource() + : program(-1) + , vertexShader(-1) + , fragmentShader(-1) + { + }; + + ShaderResource(GLuint prog, GLuint vertex, GLuint fragment) + : program(prog) + , vertexShader(vertex) + , fragmentShader(fragment) + { + }; + + GLuint program; + GLuint vertexShader; + GLuint fragmentShader; +}; + +class ShaderProgram { +public: + ShaderProgram(); + void initGLResources(); + void cleanupGLResources(); + // Drawing + void setupDrawing(const IntRect& viewRect, const SkRect& visibleRect, + const IntRect& webViewRect, int titleBarHeight, + const IntRect& screenClip, float scale); + float zValue(const TransformationMatrix& drawMatrix, float w, float h); + + // For drawQuad and drawLayerQuad, they can handle 3 cases for now: + // 1) textureTarget == GL_TEXTURE_2D + // Normal texture in GL_TEXTURE_2D target. + // 2) textureTarget == GL_TEXTURE_EXTERNAL_OES + // Surface texture in GL_TEXTURE_EXTERNAL_OES target. + // 3) textureId == 0 + // No texture needed, just a pureColor quad. + void drawQuad(const DrawQuadData* data); + void drawVideoLayerQuad(const TransformationMatrix& drawMatrix, + float* textureMatrix, SkRect& geometry, int textureId); + FloatRect rectInScreenCoord(const TransformationMatrix& drawMatrix, + const IntSize& size); + FloatRect rectInInvScreenCoord(const TransformationMatrix& drawMatrix, + const IntSize& size); + + FloatRect rectInInvScreenCoord(const FloatRect& rect); + FloatRect rectInScreenCoord(const FloatRect& rect); + FloatRect convertScreenCoordToDocumentCoord(const FloatRect& rect); + FloatRect convertInvScreenCoordToScreenCoord(const FloatRect& rect); + FloatRect convertScreenCoordToInvScreenCoord(const FloatRect& rect); + + void clip(const FloatRect& rect); + IntRect clippedRectWithViewport(const IntRect& rect, int margin = 0); + FloatRect documentViewport() { return m_documentViewport; } + + float contrast() { return m_contrast; } + void setContrast(float c) + { + float contrast = c; + if (contrast < 0) + contrast = 0; + if (contrast > MAX_CONTRAST) + contrast = MAX_CONTRAST; + m_contrast = contrast; + } + void setWebViewMatrix(const float* matrix, bool alphaLayer); + + // This delta is the delta from the layout pos and the current animation pos. + // Basically, in terms of layout, the webview is still in the original layout + // pos, as without animation. Such that the viewport and visible rect etc are + // still in that pos, too, except the clipping info. + // Our rendering approach is after applying all the matrix, webView is + // rendered as if it was at the original layout pos, but then offset the + // glViewport to match the animation. + void calculateAnimationDelta(); + int getAnimationDeltaX() { return m_animationDelta.x(); } + int getAnimationDeltaY() { return m_animationDelta.y(); } + bool needsInit() { return m_needsInit; } + +private: + GLuint loadShader(GLenum shaderType, const char* pSource); + GLint createProgram(const char* vertexSource, const char* fragmentSource); + GLfloat* getProjectionMatrix(const DrawQuadData* data); + void setBlendingState(bool enableBlending); + void drawQuadInternal(ShaderType type, const GLfloat* matrix, int textureId, + float opacity, GLenum textureTarget, GLenum filter, + const Color& pureColor); + Color shaderColor(Color pureColor, float opacity); + ShaderType getTextureShaderType(GLenum textureTarget); + void resetBlending(); + + bool m_blendingEnabled; + + TransformationMatrix m_projectionMatrix; + GLuint m_textureBuffer[1]; + + TransformationMatrix m_documentToScreenMatrix; + TransformationMatrix m_documentToInvScreenMatrix; + SkRect m_viewport; + IntRect m_viewRect; + FloatRect m_clipRect; + IntRect m_screenClip; + int m_titleBarHeight; + IntRect m_webViewRect; + + FloatRect m_documentViewport; + + float m_contrast; + + bool m_alphaLayer; + TransformationMatrix m_webViewMatrix; + float m_currentScale; + + // After the webViewTranform, we need to reposition the rect to match our viewport. + // Basically, the webViewTransformMatrix should apply on the screen resolution. + // So we start by doing the scale and translate to get each tile into screen coordinates. + // After applying the webViewTransformMatrix, b/c the way it currently set up + // for scroll and titlebar, we need to offset both of them. + // Finally, map everything back to (-1, 1) by using the m_projectionMatrix. + // TODO: Given that m_webViewMatrix contains most of the tranformation + // information, we should be able to get rid of some parameter we got from + // Java side and simplify our code. + TransformationMatrix m_repositionMatrix; + IntPoint m_animationDelta; + + // Put all the uniform location (handle) info into an array, and group them + // by the shader's type, this can help to clean up the interface. + // TODO: use the type and data comparison to skip GL call if possible. + ShaderHandles m_handleArray[MaxShaderNumber]; + + // If there is any GL error happens such that the Shaders are not initialized + // successfully at the first time, then we need to init again when we draw. + bool m_needsInit; + + // For transfer queue blitting, we need a special matrix map from (0,1) to + // (-1,1) + GLfloat m_transferProjMtx[16]; + + GLfloat m_tileProjMatrix[16]; + + Vector<ShaderResource> m_resources; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // ShaderProgram_h diff --git a/Source/WebCore/platform/graphics/android/rendering/Surface.cpp b/Source/WebCore/platform/graphics/android/rendering/Surface.cpp new file mode 100644 index 0000000..3ed3aad --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/Surface.cpp @@ -0,0 +1,346 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "Surface" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "Surface.h" + +#include "AndroidLog.h" +#include "ClassTracker.h" +#include "LayerAndroid.h" +#include "GLWebViewState.h" +#include "SkCanvas.h" +#include "SurfaceBacking.h" +#include "TilesManager.h" + +// Surfaces with an area larger than 2048*2048 should never be unclipped +#define MAX_UNCLIPPED_AREA 4194304 + +namespace WebCore { + +Surface::Surface() + : m_surfaceBacking(0) + , m_needsTexture(false) + , m_hasText(false) +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("Surface"); +#endif +} + +Surface::~Surface() +{ + for (unsigned int i = 0; i < m_layers.size(); i++) + SkSafeUnref(m_layers[i]); + if (m_surfaceBacking) + SkSafeUnref(m_surfaceBacking); +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("Surface"); +#endif +} + +bool Surface::tryUpdateSurface(Surface* oldSurface) +{ + if (!needsTexture() || !oldSurface->needsTexture()) + return false; + + // merge surfaces based on first layer ID + if (getFirstLayer()->uniqueId() != oldSurface->getFirstLayer()->uniqueId()) + return false; + + m_surfaceBacking = oldSurface->m_surfaceBacking; + SkSafeRef(m_surfaceBacking); + + ALOGV("%p taking old SurfBack %p from surface %p, nt %d", + this, m_surfaceBacking, oldSurface, oldSurface->needsTexture()); + + if (!m_surfaceBacking) { + // no SurfBack to inval, so don't worry about it. + return true; + } + + if (singleLayer() && oldSurface->singleLayer()) { + // both are single matching layers, simply apply inval + SkRegion* layerInval = getFirstLayer()->getInvalRegion(); + m_surfaceBacking->markAsDirty(*layerInval); + } else { + SkRegion invalRegion; + bool fullInval = m_layers.size() != oldSurface->m_layers.size(); + if (!fullInval) { + for (unsigned int i = 0; i < m_layers.size(); i++) { + if (m_layers[i]->uniqueId() != oldSurface->m_layers[i]->uniqueId()) { + // layer list has changed, fully invalidate + // TODO: partially invalidate based on layer size/position + fullInval = true; + break; + } else if (!m_layers[i]->getInvalRegion()->isEmpty()) { + // merge layer inval - translate the layer's inval region into surface coordinates + SkPoint pos = m_layers[i]->getPosition(); + m_layers[i]->getInvalRegion()->translate(pos.fX, pos.fY); + invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op); + break; + } + } + } + + if (fullInval) + invalRegion.setRect(-1e8, -1e8, 2e8, 2e8); + + m_surfaceBacking->markAsDirty(invalRegion); + } + return true; +} + +void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform) +{ + m_layers.append(layer); + SkSafeRef(layer); + + m_needsTexture |= layer->needsTexture(); + m_hasText |= layer->hasText(); + + // calculate area size for comparison later + IntRect rect = layer->unclippedArea(); + SkPoint pos = layer->getPosition(); + rect.setLocation(IntPoint(pos.fX, pos.fY)); + + if (layer->needsTexture()) { + if (m_unclippedArea.isEmpty()) { + m_drawTransform = transform; + m_drawTransform.translate3d(-pos.fX, -pos.fY, 0); + m_unclippedArea = rect; + } else + m_unclippedArea.unite(rect); + ALOGV("Surf %p adding LA %p, size %d, %d %dx%d, now Surf size %d,%d %dx%d", + this, layer, rect.x(), rect.y(), rect.width(), rect.height(), + m_unclippedArea.x(), m_unclippedArea.y(), + m_unclippedArea.width(), m_unclippedArea.height()); + } +} + +IntRect Surface::visibleArea() +{ + if (singleLayer()) + return getFirstLayer()->visibleArea(); + + IntRect rect = m_unclippedArea; + + // clip with the viewport in documents coordinate + IntRect documentViewport(TilesManager::instance()->shader()->documentViewport()); + rect.intersect(documentViewport); + + // TODO: handle recursive layer clip + + return rect; +} + +IntRect Surface::unclippedArea() +{ + if (singleLayer()) + return getFirstLayer()->unclippedArea(); + return m_unclippedArea; +} + +bool Surface::useAggressiveRendering() +{ + // When the background is semi-opaque, 0 < alpha < 255, we had to turn off + // low res to avoid artifacts from double drawing. + // TODO: avoid double drawing for low res tiles. + return isBase() + && (!m_background.alpha() + || !m_background.hasAlpha()); +} + +void Surface::prepareGL(bool layerTilesDisabled) +{ + bool tilesDisabled = layerTilesDisabled && !isBase(); + if (!m_surfaceBacking) { + ALOGV("prepareGL on Surf %p, no SurfBack, needsTexture? %d", + this, m_surfaceBacking, needsTexture()); + + if (!needsTexture()) + return; + + m_surfaceBacking = new SurfaceBacking(isBase()); + } + + if (tilesDisabled) { + m_surfaceBacking->discardTextures(); + } else { + bool allowZoom = hasText(); // only allow for scale > 1 if painting vectors + IntRect prepareArea = computePrepareArea(); + IntRect fullArea = unclippedArea(); + + ALOGV("prepareGL on Surf %p with SurfBack %p, %d layers", + this, m_surfaceBacking, m_layers.size()); + + m_surfaceBacking->prepareGL(getFirstLayer()->state(), allowZoom, + prepareArea, fullArea, + this, useAggressiveRendering()); + } +} + +bool Surface::drawGL(bool layerTilesDisabled) +{ + bool tilesDisabled = layerTilesDisabled && !isBase(); + if (!getFirstLayer()->visible()) + return false; + + if (!isBase()) { + // TODO: why are clipping regions wrong for base layer? + FloatRect drawClip = getFirstLayer()->drawClip(); + FloatRect clippingRect = TilesManager::instance()->shader()->rectInScreenCoord(drawClip); + TilesManager::instance()->shader()->clip(clippingRect); + } + + bool askRedraw = false; + if (m_surfaceBacking && !tilesDisabled) { + ALOGV("drawGL on Surf %p with SurfBack %p", this, m_surfaceBacking); + + // TODO: why this visibleArea is different from visibleRect at zooming for base? + IntRect drawArea = visibleArea(); + m_surfaceBacking->drawGL(drawArea, opacity(), drawTransform(), + useAggressiveRendering(), background()); + } + + // draw member layers (draws image textures, glextras) + for (unsigned int i = 0; i < m_layers.size(); i++) + askRedraw |= m_layers[i]->drawGL(tilesDisabled); + + return askRedraw; +} + +void Surface::swapTiles() +{ + if (!m_surfaceBacking) + return; + + m_surfaceBacking->swapTiles(); +} + +bool Surface::isReady() +{ + if (!m_surfaceBacking) + return true; + + return m_surfaceBacking->isReady(); +} + +IntRect Surface::computePrepareArea() { + IntRect area; + + if (!getFirstLayer()->contentIsScrollable() + && !isBase() + && getFirstLayer()->state()->layersRenderingMode() == GLWebViewState::kAllTextures) { + + area = unclippedArea(); + + double total = ((double) area.width()) * ((double) area.height()); + if (total > MAX_UNCLIPPED_AREA) + area = visibleArea(); + } else { + area = visibleArea(); + } + + return area; +} + +void Surface::computeTexturesAmount(TexturesResult* result) +{ + if (!m_surfaceBacking || isBase()) + return; + + m_surfaceBacking->computeTexturesAmount(result, getFirstLayer()); +} + +bool Surface::isBase() +{ + // base layer surface + // - doesn't use layer tiles (disables blending, doesn't compute textures amount) + // - ignores clip rects + // - only prepares clippedArea + return getFirstLayer()->subclassType() == LayerAndroid::BaseLayer; +} + +bool Surface::paint(Tile* tile, SkCanvas* canvas) +{ + if (singleLayer()) { + getFirstLayer()->contentDraw(canvas, Layer::UnmergedLayers); + + // TODO: double buffer by disabling SurfaceCollection swaps and position + // updates until painting complete + + // In single surface mode, draw layer content onto the base layer + if (isBase() + && getFirstLayer()->countChildren() + && getFirstLayer()->state()->layersRenderingMode() > GLWebViewState::kClippedTextures) + getFirstLayer()->getChild(0)->drawCanvas(canvas, true, Layer::FlattenedLayers); + } else { + SkAutoCanvasRestore acr(canvas, true); + SkMatrix matrix; + GLUtils::toSkMatrix(matrix, m_drawTransform); + + SkMatrix inverse; + inverse.reset(); + matrix.invert(&inverse); + + SkMatrix canvasMatrix = canvas->getTotalMatrix(); + inverse.postConcat(canvasMatrix); + canvas->setMatrix(inverse); + + for (unsigned int i=0; i<m_layers.size(); i++) + m_layers[i]->drawCanvas(canvas, false, Layer::MergedLayers); + } + return true; +} + +float Surface::opacity() +{ + if (singleLayer()) + return getFirstLayer()->drawOpacity(); + return 1.0; +} + +Color* Surface::background() +{ + if (!isBase() || !m_background.isValid()) + return 0; + return &m_background; +} + +const TransformationMatrix* Surface::drawTransform() +{ + // single layer surfaces query the layer's draw transform, while multi-layer + // surfaces copy the draw transform once, during initialization + // TODO: support fixed multi-layer surfaces by querying the changing drawTransform + if (singleLayer()) + return getFirstLayer()->drawTransform(); + + return &m_drawTransform; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/Surface.h b/Source/WebCore/platform/graphics/android/rendering/Surface.h new file mode 100644 index 0000000..27c997e --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/Surface.h @@ -0,0 +1,115 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Surface_h +#define Surface_h + +#include "Color.h" +#include "IntRect.h" +#include "TilePainter.h" +#include "Vector.h" + +class SkCanvas; +class SkRegion; + +namespace WebCore { + +class Tile; +class SurfaceBacking; +class LayerAndroid; +class TexturesResult; + +class Surface : public TilePainter { +public: + Surface(); + virtual ~Surface(); + + bool tryUpdateSurface(Surface* oldSurface); + + void addLayer(LayerAndroid* layer, const TransformationMatrix& transform); + void prepareGL(bool layerTilesDisabled); + bool drawGL(bool layerTilesDisabled); + void swapTiles(); + bool isReady(); + + void computeTexturesAmount(TexturesResult* result); + + LayerAndroid* getFirstLayer() { return m_layers[0]; } + bool needsTexture() { return m_needsTexture; } + bool hasText() { return m_hasText; } + bool isBase(); + void setBackground(Color background) { m_background = background; } + + // TilePainter methods + virtual bool paint(Tile* tile, SkCanvas* canvas); + virtual float opacity(); + virtual Color* background(); + +private: + IntRect computePrepareArea(); + IntRect visibleArea(); + IntRect unclippedArea(); + bool singleLayer() { return m_layers.size() == 1; } + void updateBackground(const Color& background); + bool useAggressiveRendering(); + + const TransformationMatrix* drawTransform(); + IntRect m_unclippedArea; + TransformationMatrix m_drawTransform; + + SurfaceBacking* m_surfaceBacking; + bool m_needsTexture; + bool m_hasText; + Vector<LayerAndroid*> m_layers; + + Color m_background; +}; + +class LayerMergeState { +public: + LayerMergeState(Vector<Surface*>* const allGroups) + : surfaceList(allGroups) + , currentSurface(0) + , nonMergeNestedLevel(-1) // start at -1 to ignore first LayerAndroid's clipping + , depth(0) + {} + + // vector storing all generated layer groups + Vector<Surface*>* const surfaceList; + + // currently merging group. if cleared, no more layers may join + Surface* currentSurface; + + // records depth within non-mergeable parents (clipping, fixed, scrolling) + // and disable merging therein. + int nonMergeNestedLevel; + + // counts layer tree depth for debugging + int depth; +}; + +} // namespace WebCore + +#endif //#define Surface_h diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.cpp b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.cpp new file mode 100644 index 0000000..7c8f570 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "SurfaceBacking" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "SurfaceBacking.h" + +#include "AndroidLog.h" +#include "Color.h" +#include "GLWebViewState.h" +#include "LayerAndroid.h" + +#define LOW_RES_PREFETCH_SCALE_MODIFIER 0.3f + +namespace WebCore { + +SurfaceBacking::SurfaceBacking(bool isBaseSurface) +{ + m_frontTexture = new TileGrid(isBaseSurface); + m_backTexture = new TileGrid(isBaseSurface); + m_scale = -1; + m_futureScale = -1; + m_zooming = false; +} + +SurfaceBacking::~SurfaceBacking() +{ + delete m_frontTexture; + delete m_backTexture; +} + +void SurfaceBacking::prepareGL(GLWebViewState* state, bool allowZoom, + const IntRect& prepareArea, const IntRect& unclippedArea, + TilePainter* painter, bool aggressiveRendering) +{ + float scale = state->scale(); + if (scale > 1 && !allowZoom) + scale = 1; + + if (m_scale == -1) { + m_scale = scale; + m_futureScale = scale; + } + + if (m_futureScale != scale) { + m_futureScale = scale; + m_zoomUpdateTime = WTF::currentTime() + SurfaceBacking::s_zoomUpdateDelay; + m_zooming = true; + } + + bool useExpandPrefetch = aggressiveRendering; + ALOGV("Prepare SurfBack %p, scale %.2f, m_scale %.2f, futScale: %.2f, zooming: %d, f %p, b %p", + this, scale, m_scale, m_futureScale, m_zooming, + m_frontTexture, m_backTexture); + + if (!m_zooming) { + m_frontTexture->prepareGL(state, m_scale, + prepareArea, unclippedArea, painter, false, useExpandPrefetch); + if (aggressiveRendering) { + // prepare the back tiled texture to render content in low res + float lowResPrefetchScale = m_scale * LOW_RES_PREFETCH_SCALE_MODIFIER; + m_backTexture->prepareGL(state, lowResPrefetchScale, + prepareArea, unclippedArea, painter, true, useExpandPrefetch); + m_backTexture->swapTiles(); + } + } else if (m_zoomUpdateTime < WTF::currentTime()) { + m_backTexture->prepareGL(state, m_futureScale, + prepareArea, unclippedArea, painter, false, useExpandPrefetch); + if (m_backTexture->isReady()) { + // zooming completed, swap the textures and new front tiles + swapTileGrids(); + + m_frontTexture->swapTiles(); + m_backTexture->discardTextures(); + + m_scale = m_futureScale; + m_zooming = false; + } + } +} + +void SurfaceBacking::drawGL(const IntRect& visibleArea, float opacity, + const TransformationMatrix* transform, + bool aggressiveRendering, const Color* background) +{ + // draw low res prefetch page, if needed + if (aggressiveRendering && !m_zooming && m_frontTexture->isMissingContent()) + m_backTexture->drawGL(visibleArea, opacity, transform); + + m_frontTexture->drawGL(visibleArea, opacity, transform, background); +} + +void SurfaceBacking::markAsDirty(const SkRegion& dirtyArea) +{ + m_backTexture->markAsDirty(dirtyArea); + m_frontTexture->markAsDirty(dirtyArea); +} + +void SurfaceBacking::swapTiles() +{ + m_backTexture->swapTiles(); + m_frontTexture->swapTiles(); +} + +void SurfaceBacking::computeTexturesAmount(TexturesResult* result, LayerAndroid* layer) +{ + // TODO: shouldn't use layer, as this SB may paint multiple layers + if (!layer) + return; + + IntRect unclippedArea = layer->unclippedArea(); + IntRect clippedVisibleArea = layer->visibleArea(); + + // get two numbers here: + // - textures needed for a clipped area + // - textures needed for an un-clipped area + TileGrid* tiledTexture = m_zooming ? m_backTexture : m_frontTexture; + int nbTexturesUnclipped = tiledTexture->nbTextures(unclippedArea, m_scale); + int nbTexturesClipped = tiledTexture->nbTextures(clippedVisibleArea, m_scale); + + // Set kFixedLayers level + if (layer->isPositionFixed()) + result->fixed += nbTexturesClipped; + + // Set kScrollableAndFixedLayers level + if (layer->contentIsScrollable() + || layer->isPositionFixed()) + result->scrollable += nbTexturesClipped; + + // Set kClippedTextures level + result->clipped += nbTexturesClipped; + + // Set kAllTextures level + if (layer->contentIsScrollable()) + result->full += nbTexturesClipped; + else + result->full += nbTexturesUnclipped; +} + +void SurfaceBacking::swapTileGrids() +{ + TileGrid* temp = m_frontTexture; + m_frontTexture = m_backTexture; + m_backTexture = temp; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.h b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.h new file mode 100644 index 0000000..b04e462 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.h @@ -0,0 +1,86 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SurfaceBacking_h +#define SurfaceBacking_h + +#include "SkRefCnt.h" +#include "TileGrid.h" + +namespace WebCore { + +class LayerAndroid; +class TexturesResult; +class TilePainter; + +class SurfaceBacking : public SkRefCnt { +// TODO: investigate webkit threadsafe ref counting +public: + SurfaceBacking(bool isBaseSurface); + ~SurfaceBacking(); + void prepareGL(GLWebViewState* state, bool allowZoom, + const IntRect& prepareArea, const IntRect& unclippedArea, + TilePainter* painter, bool aggressiveRendering); + void swapTiles(); + void drawGL(const IntRect& visibleArea, float opacity, + const TransformationMatrix* transform, bool aggressiveRendering, + const Color* background); + void markAsDirty(const SkRegion& dirtyArea); + void computeTexturesAmount(TexturesResult* result, LayerAndroid* layer); + void discardTextures() + { + m_frontTexture->discardTextures(); + m_backTexture->discardTextures(); + } + bool isReady() + { + return !m_zooming && m_frontTexture->isReady() && m_scale > 0; + } + + int nbTextures(IntRect& area, float scale) + { + // TODO: consider the zooming case for the backTexture + if (!m_frontTexture) + return 0; + return m_frontTexture->nbTextures(area, scale); + } + +private: + void swapTileGrids(); + + // Delay before we schedule a new tile at the new scale factor + static const double s_zoomUpdateDelay = 0.2; // 200 ms + + TileGrid* m_frontTexture; + TileGrid* m_backTexture; + float m_scale; + float m_futureScale; + double m_zoomUpdateTime; + bool m_zooming; +}; + +} // namespace WebCore + +#endif // SurfaceBacking_h diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.cpp b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.cpp new file mode 100644 index 0000000..0bbaf91 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "SurfaceCollection" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "SurfaceCollection.h" + +#include "AndroidLog.h" +#include "BaseLayerAndroid.h" +#include "ClassTracker.h" +#include "GLWebViewState.h" +#include "LayerAndroid.h" +#include "Surface.h" +#include "ScrollableLayerAndroid.h" +#include "TilesManager.h" + +namespace WebCore { + +//////////////////////////////////////////////////////////////////////////////// +// TILED PAINTING / SURFACES // +//////////////////////////////////////////////////////////////////////////////// + +SurfaceCollection::SurfaceCollection(LayerAndroid* layer) + : m_compositedRoot(layer) +{ + // layer must be non-null. + SkSafeRef(m_compositedRoot); + + // calculate draw transforms and z values + SkRect visibleRect = SkRect::MakeLTRB(0, 0, 1, 1); + m_compositedRoot->updateLayerPositions(visibleRect); + // TODO: updateGLPositionsAndScale? + + // allocate surfaces for layers, merging where possible + ALOGV("new tree, allocating surfaces for tree %p", m_baseLayer); + + LayerMergeState layerMergeState(&m_surfaces); + m_compositedRoot->assignSurfaces(&layerMergeState); + + // set the layersurfaces' update count, to be drawn on painted tiles + unsigned int updateCount = TilesManager::instance()->incWebkitContentUpdates(); + for (unsigned int i = 0; i < m_surfaces.size(); i++) { + m_surfaces[i]->setUpdateCount(updateCount); + if (m_surfaces[i]->isBase()) + m_surfaces[i]->setBackground(getBackground()); + } + +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("SurfaceCollection"); +#endif +} + +SurfaceCollection::~SurfaceCollection() +{ + SkSafeUnref(m_compositedRoot); + for (unsigned int i = 0; i < m_surfaces.size(); i++) + SkSafeUnref(m_surfaces[i]); + m_surfaces.clear(); + +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("SurfaceCollection"); +#endif +} + +void SurfaceCollection::prepareGL(const SkRect& visibleRect) +{ + updateLayerPositions(visibleRect); + bool layerTilesDisabled = m_compositedRoot->state()->layersRenderingMode() + > GLWebViewState::kClippedTextures; + for (unsigned int i = 0; i < m_surfaces.size(); i++) + m_surfaces[i]->prepareGL(layerTilesDisabled); +} + +bool SurfaceCollection::drawGL(const SkRect& visibleRect) +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->show(); +#endif + + bool needsRedraw = false; + updateLayerPositions(visibleRect); + bool layerTilesDisabled = m_compositedRoot->state()->layersRenderingMode() + > GLWebViewState::kClippedTextures; + for (unsigned int i = 0; i < m_surfaces.size(); i++) + needsRedraw |= m_surfaces[i]->drawGL(layerTilesDisabled); + + return needsRedraw; +} + +Color SurfaceCollection::getBackground() +{ + return static_cast<BaseLayerAndroid*>(m_compositedRoot)->getBackgroundColor(); +} + +void SurfaceCollection::swapTiles() +{ + for (unsigned int i = 0; i < m_surfaces.size(); i++) + m_surfaces[i]->swapTiles(); +} + +bool SurfaceCollection::isReady() +{ + // Override layer readiness check for single surface mode + if (m_compositedRoot->state()->layersRenderingMode() > GLWebViewState::kClippedTextures) { + // TODO: single surface mode should be properly double buffered + return true; + } + + for (unsigned int i = 0; i < m_surfaces.size(); i++) { + if (!m_surfaces[i]->isReady()) { + ALOGV("layer surface %p isn't ready", m_surfaces[i]); + return false; + } + } + return true; +} + +void SurfaceCollection::computeTexturesAmount(TexturesResult* result) +{ + for (unsigned int i = 0; i < m_surfaces.size(); i++) + m_surfaces[i]->computeTexturesAmount(result); +} + +//////////////////////////////////////////////////////////////////////////////// +// RECURSIVE ANIMATION / INVALS / LAYERS // +//////////////////////////////////////////////////////////////////////////////// + +void SurfaceCollection::setIsPainting(SurfaceCollection* drawingSurface) +{ + if (!drawingSurface) + return; + + for (unsigned int i = 0; i < m_surfaces.size(); i++) { + Surface* newSurface = m_surfaces[i]; + if (!newSurface->needsTexture()) + continue; + + for (unsigned int j = 0; j < drawingSurface->m_surfaces.size(); j++) { + Surface* oldSurface = drawingSurface->m_surfaces[j]; + if (newSurface->tryUpdateSurface(oldSurface)) + break; + } + } +} + +void SurfaceCollection::setIsDrawing() +{ + m_compositedRoot->initAnimations(); +} + +void SurfaceCollection::mergeInvalsInto(SurfaceCollection* replacementSurface) +{ + m_compositedRoot->mergeInvalsInto(replacementSurface->m_compositedRoot); +} + +void SurfaceCollection::evaluateAnimations(double currentTime) +{ + m_compositedRoot->evaluateAnimations(currentTime); +} + +bool SurfaceCollection::hasCompositedLayers() +{ + return m_compositedRoot->countChildren(); +} + +bool SurfaceCollection::hasCompositedAnimations() +{ + return m_compositedRoot->hasAnimations(); +} + +void SurfaceCollection::updateScrollableLayer(int layerId, int x, int y) +{ + LayerAndroid* layer = m_compositedRoot->findById(layerId); + if (layer && layer->contentIsScrollable()) + static_cast<ScrollableLayerAndroid*>(layer)->scrollTo(x, y); +} + +void SurfaceCollection::updateLayerPositions(const SkRect& visibleRect) +{ + TransformationMatrix ident; + m_compositedRoot->updateLayerPositions(visibleRect); + FloatRect clip(0, 0, 1e10, 1e10); + m_compositedRoot->updateGLPositionsAndScale( + ident, clip, 1, m_compositedRoot->state()->scale()); + +#ifdef DEBUG + m_compositedRoot->showLayer(0); + ALOGV("We have %d layers, %d textured", + m_compositedRoot->nbLayers(), + m_compositedRoot->nbTexturedLayers()); +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.h b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.h new file mode 100644 index 0000000..6450c9c --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.h @@ -0,0 +1,76 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SurfaceCollection_h +#define SurfaceCollection_h + +#include "Color.h" +#include "SkRect.h" +#include "SkRefCnt.h" + +#include <wtf/Vector.h> + +class SkCanvas; +class SkRegion; + +namespace WebCore { + +class LayerAndroid; +class Surface; +class TexturesResult; + +class SurfaceCollection : public SkRefCnt { +// TODO: investigate webkit threadsafe ref counting +public: + SurfaceCollection(LayerAndroid* compositedRoot); + virtual ~SurfaceCollection(); + + // Tiled painting methods (executed on groups) + void prepareGL(const SkRect& visibleRect); + bool drawGL(const SkRect& visibleRect); + Color getBackground(); + void swapTiles(); + bool isReady(); + void computeTexturesAmount(TexturesResult* result); + + // Recursive tree methods (animations, invals, etc) + void setIsPainting(SurfaceCollection* drawingSurfaceCollection); + void setIsDrawing(); + void mergeInvalsInto(SurfaceCollection* replacementSurfaceCollection); + void evaluateAnimations(double currentTime); + + bool hasCompositedLayers(); + bool hasCompositedAnimations(); + void updateScrollableLayer(int layerId, int x, int y); + +private: + void updateLayerPositions(const SkRect& visibleRect); + LayerAndroid* m_compositedRoot; + WTF::Vector<Surface*> m_surfaces; +}; + +} // namespace WebCore + +#endif //#define SurfaceCollection_h diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceCollectionManager.cpp b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollectionManager.cpp new file mode 100644 index 0000000..8fb4d4b --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollectionManager.cpp @@ -0,0 +1,258 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "SurfaceCollectionManager" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "SurfaceCollectionManager.h" + +#include "AndroidLog.h" +#include "private/hwui/DrawGlInfo.h" +#include "TilesManager.h" +#include "SurfaceCollection.h" + +namespace WebCore { + +SurfaceCollectionManager::SurfaceCollectionManager(GLWebViewState* state) + : m_state(state) + , m_drawingCollection(0) + , m_paintingCollection(0) + , m_queuedCollection(0) + , m_fastSwapMode(false) +{ +} + +SurfaceCollectionManager::~SurfaceCollectionManager() +{ + clearCollections(); +} + +// the painting collection has finished painting: +// discard the drawing collection +// swap the painting collection in place of the drawing collection +// and start painting the queued collection +void SurfaceCollectionManager::swap() +{ + // swap can't be called unless painting just finished + ASSERT(m_paintingCollection); + + android::Mutex::Autolock lock(m_paintSwapLock); + + ALOGV("SWAPPING, D %p, P %p, Q %p", + m_drawingCollection, m_paintingCollection, m_queuedCollection); + + // if we have a drawing collection, discard it since the painting collection is done + if (m_drawingCollection) { + ALOGV("destroying drawing collection %p", m_drawingCollection); + SkSafeUnref(m_drawingCollection); + } + + // painting collection becomes the drawing collection + ALOGV("drawing collection %p", m_paintingCollection); + m_paintingCollection->setIsDrawing(); // initialize animations + + if (m_queuedCollection) { + // start painting with the queued collection + ALOGV("now painting collection %p", m_queuedCollection); + m_queuedCollection->setIsPainting(m_paintingCollection); + } + m_drawingCollection = m_paintingCollection; + m_paintingCollection = m_queuedCollection; + m_queuedCollection = 0; + + ALOGV("SWAPPING COMPLETE, D %p, P %p, Q %p", + m_drawingCollection, m_paintingCollection, m_queuedCollection); +} + +// clear all of the content in the three collections held by the collection manager +void SurfaceCollectionManager::clearCollections() +{ + ALOGV("SurfaceCollectionManager %p removing PS from state %p", this, m_state); + + SkSafeUnref(m_drawingCollection); + m_drawingCollection = 0; + SkSafeUnref(m_paintingCollection); + m_paintingCollection = 0; + SkSafeUnref(m_queuedCollection); + m_queuedCollection = 0; +} + +// a new layer collection has arrived, queue it if we're painting something already, +// or start painting it if we aren't. Returns true if the manager has two collections +// already queued. +bool SurfaceCollectionManager::updateWithSurfaceCollection(SurfaceCollection* newCollection, + bool brandNew) +{ + // can't have a queued collection unless have a painting collection too + ASSERT(m_paintingCollection || !m_queuedCollection); + + android::Mutex::Autolock lock(m_paintSwapLock); + + if (!newCollection || brandNew) { + clearCollections(); + if (brandNew) { + m_paintingCollection = newCollection; + m_paintingCollection->setIsPainting(m_drawingCollection); + } + return false; + } + + ALOGV("updateWithSurfaceCollection - %p, has children %d, has animations %d", + newCollection, newCollection->hasCompositedLayers(), + newCollection->hasCompositedAnimations()); + + if (m_queuedCollection || m_paintingCollection) { + // currently painting, so defer this new collection + if (m_queuedCollection) { + // already have a queued collection, copy over invals so the regions are + // eventually repainted and let the old queued collection be discarded + m_queuedCollection->mergeInvalsInto(newCollection); + + if (!TilesManager::instance()->useDoubleBuffering()) { + // not double buffering, count discarded collection/webkit paint as an update + TilesManager::instance()->incContentUpdates(); + } + + ALOGV("DISCARDING collection - %p, has children %d, has animations %d", + newCollection, newCollection->hasCompositedLayers(), + newCollection->hasCompositedAnimations()); + } + SkSafeUnref(m_queuedCollection); + m_queuedCollection = newCollection; + } else { + // don't have painting collection, paint this one! + m_paintingCollection = newCollection; + m_paintingCollection->setIsPainting(m_drawingCollection); + } + return m_drawingCollection && TilesManager::instance()->useDoubleBuffering(); +} + +void SurfaceCollectionManager::updateScrollableLayer(int layerId, int x, int y) +{ + if (m_queuedCollection) + m_queuedCollection->updateScrollableLayer(layerId, x, y); + if (m_paintingCollection) + m_paintingCollection->updateScrollableLayer(layerId, x, y); + if (m_drawingCollection) + m_drawingCollection->updateScrollableLayer(layerId, x, y); +} + +int SurfaceCollectionManager::drawGL(double currentTime, IntRect& viewRect, + SkRect& visibleRect, float scale, + bool enterFastSwapMode, + bool* collectionsSwappedPtr, bool* newCollectionHasAnimPtr, + TexturesResult* texturesResultPtr, bool shouldDraw) +{ + m_fastSwapMode |= enterFastSwapMode; + + ALOGV("drawGL, D %p, P %p, Q %p, fastSwap %d shouldDraw %d", + m_drawingCollection, m_paintingCollection, + m_queuedCollection, m_fastSwapMode, shouldDraw); + + bool didCollectionSwap = false; + if (m_paintingCollection) { + ALOGV("preparing painting collection %p", m_paintingCollection); + + m_paintingCollection->evaluateAnimations(currentTime); + + m_paintingCollection->prepareGL(visibleRect); + m_paintingCollection->computeTexturesAmount(texturesResultPtr); + + if (!TilesManager::instance()->useDoubleBuffering() || m_paintingCollection->isReady()) { + ALOGV("have painting collection %p ready, swapping!", m_paintingCollection); + didCollectionSwap = true; + TilesManager::instance()->incContentUpdates(); + if (collectionsSwappedPtr) + *collectionsSwappedPtr = true; + if (newCollectionHasAnimPtr) + *newCollectionHasAnimPtr = m_paintingCollection->hasCompositedAnimations(); + swap(); + } + } else if (m_drawingCollection) { + ALOGV("preparing drawing collection %p", m_drawingCollection); + m_drawingCollection->prepareGL(visibleRect); + m_drawingCollection->computeTexturesAmount(texturesResultPtr); + } + + // ask for kStatusInvoke while painting, kStatusDraw if we have content to be redrawn next frame + // returning 0 indicates all painting complete, no framework inval needed. + int returnFlags = 0; + + if (m_paintingCollection) + returnFlags |= uirenderer::DrawGlInfo::kStatusInvoke; + + if (!shouldDraw) { + if (didCollectionSwap) { + m_drawingCollection->swapTiles(); + returnFlags |= uirenderer::DrawGlInfo::kStatusDraw; + } + + return returnFlags; + } + + // =========================================================================== + // Don't have a drawing collection, draw white background + Color background = Color::white; + if (m_drawingCollection) { + bool drawingReady = didCollectionSwap || m_drawingCollection->isReady(); + + // call the page swap callback if registration happened without more collections enqueued + if (collectionsSwappedPtr && drawingReady && !m_paintingCollection) + *collectionsSwappedPtr = true; + + if (didCollectionSwap || m_fastSwapMode || (drawingReady && !m_paintingCollection)) + m_drawingCollection->swapTiles(); + + if (drawingReady) { + // exit fast swap mode, as content is up to date + m_fastSwapMode = false; + } else { + // drawing isn't ready, must redraw + returnFlags |= uirenderer::DrawGlInfo::kStatusInvoke; + } + + m_drawingCollection->evaluateAnimations(currentTime); + + ALOGV("drawing collection %p", m_drawingCollection); + background = m_drawingCollection->getBackground(); + } else if (m_paintingCollection) { + // Use paintingCollection background color while tiles are not done painting. + background = m_paintingCollection->getBackground(); + } + + // Start doing the actual GL drawing. + ALOGV("background is %x", background.rgb()); + // If background is opaque, we can safely and efficiently clear it here. + // Otherwise, we have to calculate all the missing tiles and blend the background. + GLUtils::clearBackgroundIfOpaque(&background); + if (m_drawingCollection && m_drawingCollection->drawGL(visibleRect)) + returnFlags |= uirenderer::DrawGlInfo::kStatusDraw; + + ALOGV("returnFlags %d, m_paintingCollection %d ", returnFlags, m_paintingCollection); + return returnFlags; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceCollectionManager.h b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollectionManager.h new file mode 100644 index 0000000..cc98899 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollectionManager.h @@ -0,0 +1,74 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SurfaceCollectionManager_h +#define SurfaceCollectionManager_h + +#include "TestExport.h" +#include <utils/threads.h> + +class SkRect; +class SkCanvas; + +namespace WebCore { + +class GLWebViewState; +class IntRect; +class TexturesResult; +class SurfaceCollection; + +class TEST_EXPORT SurfaceCollectionManager { +public: + SurfaceCollectionManager(GLWebViewState* state); + + ~SurfaceCollectionManager(); + + bool updateWithSurfaceCollection(SurfaceCollection* collection, bool brandNew); + + void updateScrollableLayer(int layerId, int x, int y); + + int drawGL(double currentTime, IntRect& viewRect, + SkRect& visibleRect, float scale, + bool enterFastSwapMode, bool* collectionsSwappedPtr, bool* newCollectionHasAnimPtr, + TexturesResult* texturesResultPtr, bool shouldDraw); + +private: + void swap(); + void clearCollections(); + + android::Mutex m_paintSwapLock; + + GLWebViewState* m_state; + + SurfaceCollection* m_drawingCollection; + SurfaceCollection* m_paintingCollection; + SurfaceCollection* m_queuedCollection; + + bool m_fastSwapMode; +}; + +} // namespace WebCore + +#endif //#define SurfaceCollectionManager_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TextureInfo.cpp b/Source/WebCore/platform/graphics/android/rendering/TextureInfo.cpp new file mode 100644 index 0000000..f5c8b02 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TextureInfo.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextureInfo.h" + +#include "WebCoreJni.h" + +#include <JNIUtility.h> +#include <android/native_window.h> +#include <gui/SurfaceTexture.h> +#include <gui/SurfaceTextureClient.h> + +namespace WebCore { + +TextureInfo::TextureInfo() +{ + m_textureId = GL_NO_TEXTURE; + m_width = 0; + m_height = 0; + m_internalFormat = 0; +} + +bool TextureInfo::equalsAttributes(const TextureInfo* otherTexture) +{ + return otherTexture->m_width == m_width + && otherTexture->m_height == m_height + && otherTexture->m_internalFormat == m_internalFormat; +} + +void TextureInfo::copyAttributes(const TextureInfo* sourceTexture) +{ + m_width = sourceTexture->m_width; + m_height = sourceTexture->m_height; + m_internalFormat = sourceTexture->m_internalFormat; +} + +bool TextureInfo::operator==(const TextureInfo& otherTexture) +{ + return otherTexture.m_textureId == m_textureId && equalsAttributes(&otherTexture); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/TextureInfo.h b/Source/WebCore/platform/graphics/android/rendering/TextureInfo.h new file mode 100644 index 0000000..7d182c3 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TextureInfo.h @@ -0,0 +1,65 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextureInfo_h +#define TextureInfo_h + +#include <EGL/egl.h> +#include <GLES2/gl2.h> +#include <jni.h> +#include <ui/GraphicBuffer.h> +#include <utils/RefBase.h> +using android::sp; + +namespace android { +class SurfaceTexture; +} + +namespace WebCore { + +static const GLuint GL_NO_TEXTURE = 0; +/** + * TextureInfo is a class that stores both the texture and metadata about the + * texture. + */ + +class TextureInfo { +public: + TextureInfo(); + + bool equalsAttributes(const TextureInfo* otherTexture); + void copyAttributes(const TextureInfo* sourceTexture); + + bool operator==(const TextureInfo& otherTexture); + + GLuint m_textureId; + int32_t m_width; + int32_t m_height; + GLenum m_internalFormat; +}; + +} // namespace WebCore + +#endif // TextureInfo_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TextureOwner.h b/Source/WebCore/platform/graphics/android/rendering/TextureOwner.h new file mode 100644 index 0000000..b12d8b7 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TextureOwner.h @@ -0,0 +1,47 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextureOwner_h +#define TextureOwner_h + +class SkCanvas; +class Layer; + +namespace WebCore { + +class TileTexture; +class GLWebViewState; + +class TextureOwner { +public: + virtual ~TextureOwner() { } + virtual bool removeTexture(TileTexture* texture) = 0; + virtual bool isRepaintPending() = 0; + virtual unsigned long long drawCount() = 0; +}; + +} + +#endif // TextureOwner_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TexturesGenerator.cpp b/Source/WebCore/platform/graphics/android/rendering/TexturesGenerator.cpp new file mode 100644 index 0000000..f884e52 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TexturesGenerator.cpp @@ -0,0 +1,182 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "TexturesGenerator" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "TexturesGenerator.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "GLUtils.h" +#include "PaintTileOperation.h" +#include "TilesManager.h" +#include "TransferQueue.h" + +namespace WebCore { + +void TexturesGenerator::scheduleOperation(QueuedOperation* operation) +{ + { + android::Mutex::Autolock lock(mRequestedOperationsLock); + mRequestedOperations.append(operation); + } + mRequestedOperationsCond.signal(); +} + +void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter, bool waitForRunning) +{ + if (!filter) + return; + + android::Mutex::Autolock lock(mRequestedOperationsLock); + for (unsigned int i = 0; i < mRequestedOperations.size();) { + QueuedOperation* operation = mRequestedOperations[i]; + if (filter->check(operation)) { + mRequestedOperations.remove(i); + delete operation; + } else { + i++; + } + } + + if (waitForRunning && m_currentOperation) { + QueuedOperation* operation = m_currentOperation; + + if (operation && filter->check(operation)) { + m_waitForCompletion = true; + // The reason we are signaling the transferQueue is : + // TransferQueue may be waiting a slot to work on, but now UI + // thread is waiting for Tex Gen thread to finish first before the + // UI thread can free a slot for the transferQueue. + // Therefore, it could be a deadlock. + // The solution is use this as a flag to tell Tex Gen thread that + // UI thread is waiting now, Tex Gen thread should not wait for the + // queue any more. + m_tilesManager->transferQueue()->interruptTransferQueue(true); + } + + delete filter; + + // At this point, it means that we are currently executing an operation that + // we want to be removed -- we should wait until it is done, so that + // when we return our caller can be sure that there is no more operations + // in the queue matching the given filter. + while (m_waitForCompletion) + mRequestedOperationsCond.wait(mRequestedOperationsLock); + } else { + delete filter; + } +} + +status_t TexturesGenerator::readyToRun() +{ + ALOGV("Thread ready to run"); + return NO_ERROR; +} + +// Must be called from within a lock! +QueuedOperation* TexturesGenerator::popNext() +{ + // Priority can change between when it was added and now + // Hence why the entire queue is rescanned + QueuedOperation* current = mRequestedOperations.last(); + int currentPriority = current->priority(); + if (currentPriority < 0) { + mRequestedOperations.removeLast(); + return current; + } + int currentIndex = mRequestedOperations.size() - 1; + // Scan from the back to make removing faster (less items to copy) + for (int i = mRequestedOperations.size() - 2; i >= 0; i--) { + QueuedOperation *next = mRequestedOperations[i]; + int nextPriority = next->priority(); + if (nextPriority < 0) { + // Found a very high priority item, go ahead and just handle it now + mRequestedOperations.remove(i); + return next; + } + // pick items preferrably by priority, or if equal, by order of + // insertion (as we add items at the back of the queue) + if (nextPriority <= currentPriority) { + current = next; + currentPriority = nextPriority; + currentIndex = i; + } + } + mRequestedOperations.remove(currentIndex); + return current; +} + +bool TexturesGenerator::threadLoop() +{ + // Check if we have any pending operations. + mRequestedOperationsLock.lock(); + while (!mRequestedOperations.size()) + mRequestedOperationsCond.wait(mRequestedOperationsLock); + + ALOGV("threadLoop, got signal"); + mRequestedOperationsLock.unlock(); + + m_currentOperation = 0; + bool stop = false; + while (!stop) { + mRequestedOperationsLock.lock(); + ALOGV("threadLoop, %d operations in the queue", mRequestedOperations.size()); + if (mRequestedOperations.size()) + m_currentOperation = popNext(); + mRequestedOperationsLock.unlock(); + + if (m_currentOperation) { + ALOGV("threadLoop, painting the request with priority %d", + m_currentOperation->priority()); + m_currentOperation->run(); + } + + QueuedOperation* oldOperation = m_currentOperation; + mRequestedOperationsLock.lock(); + if (m_currentOperation) + m_currentOperation = 0; + if (!mRequestedOperations.size()) + stop = true; + if (m_waitForCompletion) { + m_waitForCompletion = false; + m_tilesManager->transferQueue()->interruptTransferQueue(false); + mRequestedOperationsCond.signal(); + } + mRequestedOperationsLock.unlock(); + if (oldOperation) + delete oldOperation; // delete outside lock + } + ALOGV("threadLoop empty"); + + return true; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/TexturesGenerator.h b/Source/WebCore/platform/graphics/android/rendering/TexturesGenerator.h new file mode 100644 index 0000000..08f69ae --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TexturesGenerator.h @@ -0,0 +1,70 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TexturesGenerator_h +#define TexturesGenerator_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "QueuedOperation.h" +#include "TilePainter.h" +#include <wtf/Vector.h> + +#include <utils/threads.h> + +namespace WebCore { + +using namespace android; + +class TilesManager; + +class TexturesGenerator : public Thread { +public: + TexturesGenerator(TilesManager* instance) : Thread(false) + , m_waitForCompletion(false) + , m_currentOperation(0) + , m_tilesManager(instance) { } + virtual ~TexturesGenerator() { } + virtual status_t readyToRun(); + + void removeOperationsForFilter(OperationFilter* filter, bool waitForRunning = true); + + void scheduleOperation(QueuedOperation* operation); + +private: + QueuedOperation* popNext(); + virtual bool threadLoop(); + WTF::Vector<QueuedOperation*> mRequestedOperations; + android::Mutex mRequestedOperationsLock; + android::Condition mRequestedOperationsCond; + bool m_waitForCompletion; + QueuedOperation* m_currentOperation; + TilesManager* m_tilesManager; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // TexturesGenerator_h diff --git a/Source/WebCore/platform/graphics/android/rendering/Tile.cpp b/Source/WebCore/platform/graphics/android/rendering/Tile.cpp new file mode 100644 index 0000000..35fded1 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/Tile.cpp @@ -0,0 +1,528 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "Tile" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "Tile.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "GLUtils.h" +#include "RasterRenderer.h" +#include "TextureInfo.h" +#include "TileTexture.h" +#include "TilesManager.h" + +// If the dirty portion of a tile exceeds this ratio, fully repaint. +// Lower values give fewer partial repaints, thus fewer front-to-back +// texture copies (cost will vary by device). It's a tradeoff between +// the rasterization cost and the FBO texture recopy cost when using +// GPU for the transfer queue. +#define MAX_INVAL_AREA 0.6 + +namespace WebCore { + +Tile::Tile(bool isLayerTile) + : m_x(-1) + , m_y(-1) + , m_frontTexture(0) + , m_backTexture(0) + , m_scale(1) + , m_dirty(true) + , m_repaintPending(false) + , m_fullRepaint(true) + , m_isLayerTile(isLayerTile) + , m_drawCount(0) + , m_state(Unpainted) +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("Tile"); +#endif + m_renderer = BaseRenderer::createRenderer(); +} + +Tile::~Tile() +{ + if (m_backTexture) + m_backTexture->release(this); + if (m_frontTexture) + m_frontTexture->release(this); + + delete m_renderer; + +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("Tile"); +#endif +} + +// All the following functions must be called from the main GL thread. + +void Tile::setContents(int x, int y, float scale, bool isExpandedPrefetchTile) +{ + // TODO: investigate whether below check/discard is necessary + if ((m_x != x) + || (m_y != y) + || (m_scale != scale)) { + // neither texture is relevant + discardTextures(); + } + + android::AutoMutex lock(m_atomicSync); + m_x = x; + m_y = y; + m_scale = scale; + m_drawCount = TilesManager::instance()->getDrawGLCount(); + if (isExpandedPrefetchTile) + m_drawCount--; // deprioritize expanded painting region +} + +void Tile::reserveTexture() +{ + TileTexture* texture = TilesManager::instance()->getAvailableTexture(this); + + android::AutoMutex lock(m_atomicSync); + if (texture && m_backTexture != texture) { + ALOGV("tile %p reserving texture %p, back was %p (front %p)", + this, texture, m_backTexture, m_frontTexture); + m_state = Unpainted; + m_backTexture = texture; + } + + if (m_state == UpToDate) { + ALOGV("moving tile %p to unpainted, since it reserved while up to date", this); + m_dirty = true; + m_state = Unpainted; + } +} + +bool Tile::removeTexture(TileTexture* texture) +{ + ALOGV("%p removeTexture %p, back %p front %p", + this, texture, m_backTexture, m_frontTexture); + // We update atomically, so paintBitmap() can see the correct value + android::AutoMutex lock(m_atomicSync); + if (m_frontTexture == texture) { + if (m_state == UpToDate) { + ALOGV("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture); + m_state = Unpainted; + } + + m_frontTexture = 0; + } + if (m_backTexture == texture) { + m_state = Unpainted; + m_backTexture = 0; + } + + // mark dirty regardless of which texture was taken - the back texture may + // have been ready to swap + m_dirty = true; + + return true; +} + +void Tile::markAsDirty(const SkRegion& dirtyArea) +{ + if (dirtyArea.isEmpty()) + return; + android::AutoMutex lock(m_atomicSync); + m_dirtyArea.op(dirtyArea, SkRegion::kUnion_Op); + + // Check if we actually intersect with the area + bool intersect = false; + SkRegion::Iterator cliperator(dirtyArea); + SkRect realTileRect; + SkRect dirtyRect; + while (!cliperator.done()) { + dirtyRect.set(cliperator.rect()); + if (intersectWithRect(m_x, m_y, TilesManager::tileWidth(), TilesManager::tileHeight(), + m_scale, dirtyRect, realTileRect)) { + intersect = true; + break; + } + cliperator.next(); + } + + if (!intersect) + return; + + m_dirty = true; + if (m_state == UpToDate) { + // We only mark a tile as unpainted in 'markAsDirty' if its status is + // UpToDate: marking dirty means we need to repaint, but don't stop the + // current paint + m_state = Unpainted; + } else if (m_state != Unpainted) { + // TODO: fix it so that they can paint while deferring the markAsDirty + // call (or block updates) + ALOGV("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d", + this, m_x, m_y, isLayerTile(), m_state); + + // prefetch tiles can be marked dirty while in the process of painting, + // due to not using an update lock. force them to fail validate step. + m_state = Unpainted; + } +} + +bool Tile::isDirty() +{ + android::AutoMutex lock(m_atomicSync); + return m_dirty; +} + +bool Tile::isRepaintPending() +{ + android::AutoMutex lock(m_atomicSync); + return m_repaintPending; +} + +void Tile::setRepaintPending(bool pending) +{ + android::AutoMutex lock(m_atomicSync); + m_repaintPending = pending; +} + +bool Tile::drawGL(float opacity, const SkRect& rect, float scale, + const TransformationMatrix* transform, + bool forceBlending) +{ + if (m_x < 0 || m_y < 0 || m_scale != scale) + return false; + + // No need to mutex protect reads of m_backTexture as it is only written to by + // the consumer thread. + if (!m_frontTexture) + return false; + + m_frontTexture->drawGL(isLayerTile(), rect, opacity, transform, forceBlending); + return true; +} + +bool Tile::isTileReady() +{ + // Return true if the tile's most recently drawn texture is up to date + android::AutoMutex lock(m_atomicSync); + TileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture; + + if (!texture) + return false; + + if (texture->owner() != this) + return false; + + if (m_dirty) + return false; + + if (m_state != ReadyToSwap && m_state != UpToDate) + return false; + + return true; +} + +bool Tile::intersectWithRect(int x, int y, int tileWidth, int tileHeight, + float scale, const SkRect& dirtyRect, + SkRect& realTileRect) +{ + // compute the rect to corresponds to pixels + realTileRect.fLeft = x * tileWidth; + realTileRect.fTop = y * tileHeight; + realTileRect.fRight = realTileRect.fLeft + tileWidth; + realTileRect.fBottom = realTileRect.fTop + tileHeight; + + // scale the dirtyRect for intersect computation. + SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale, + dirtyRect.height() * scale); + realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale); + + if (!realTileRect.intersect(realDirtyRect)) + return false; + return true; +} + +bool Tile::isTileVisible(const IntRect& viewTileBounds) +{ + return (m_x >= viewTileBounds.x() + && m_x < viewTileBounds.x() + viewTileBounds.width() + && m_y >= viewTileBounds.y() + && m_y < viewTileBounds.y() + viewTileBounds.height()); +} + +// This is called from the texture generation thread +void Tile::paintBitmap(TilePainter* painter) +{ + // We acquire the values below atomically. This ensures that we are reading + // values correctly across cores. Further, once we have these values they + // can be updated by other threads without consequence. + m_atomicSync.lock(); + bool dirty = m_dirty; + TileTexture* texture = m_backTexture; + SkRegion dirtyArea = m_dirtyArea; + float scale = m_scale; + const int x = m_x; + const int y = m_y; + + if (!dirty || !texture) { + m_atomicSync.unlock(); + return; + } + if (m_state != Unpainted) { + ALOGV("Warning: started painting tile %p, but was at state %d, ft %p bt %p", + this, m_state, m_frontTexture, m_backTexture); + } + m_state = PaintingStarted; + TextureInfo* textureInfo = texture->getTextureInfo(); + m_atomicSync.unlock(); + + // at this point we can safely check the ownership (if the texture got + // transferred to another Tile under us) + if (texture->owner() != this) { + return; + } + + // swap out the renderer if necessary + BaseRenderer::swapRendererIfNeeded(m_renderer); + // setup the common renderInfo fields; + TileRenderInfo renderInfo; + renderInfo.x = x; + renderInfo.y = y; + renderInfo.scale = scale; + renderInfo.tileSize = texture->getSize(); + renderInfo.tilePainter = painter; + renderInfo.baseTile = this; + renderInfo.textureInfo = textureInfo; + + const float tileWidth = renderInfo.tileSize.width(); + const float tileHeight = renderInfo.tileSize.height(); + + SkRegion::Iterator cliperator(dirtyArea); + + bool fullRepaint = false; + + if (m_fullRepaint + || textureInfo->m_width != tileWidth + || textureInfo->m_height != tileHeight) { + fullRepaint = true; + } + + // For now, only do full repaint + fullRepaint = true; + + if (!fullRepaint) { + // compute the partial inval area + SkIRect totalRect; + totalRect.set(0, 0, 0, 0); + float tileSurface = tileWidth * tileHeight; + float tileSurfaceCap = MAX_INVAL_AREA * tileSurface; + + // We join all the invals in the same tile for now + while (!fullRepaint && !cliperator.done()) { + SkRect realTileRect; + SkRect dirtyRect; + dirtyRect.set(cliperator.rect()); + bool intersect = intersectWithRect(x, y, tileWidth, tileHeight, + scale, dirtyRect, realTileRect); + if (intersect) { + // initialize finalRealRect to the rounded values of realTileRect + SkIRect finalRealRect; + realTileRect.roundOut(&finalRealRect); + + // stash the int values of the current width and height + const int iWidth = finalRealRect.width(); + const int iHeight = finalRealRect.height(); + + if (iWidth == tileWidth || iHeight == tileHeight) { + fullRepaint = true; + break; + } + + // translate the rect into tile space coordinates + finalRealRect.fLeft = finalRealRect.fLeft % static_cast<int>(tileWidth); + finalRealRect.fTop = finalRealRect.fTop % static_cast<int>(tileHeight); + finalRealRect.fRight = finalRealRect.fLeft + iWidth; + finalRealRect.fBottom = finalRealRect.fTop + iHeight; + totalRect.join(finalRealRect); + float repaintSurface = totalRect.width() * totalRect.height(); + + if (repaintSurface > tileSurfaceCap) { + fullRepaint = true; + break; + } + } + + cliperator.next(); + } + + if (!fullRepaint) { + renderInfo.invalRect = &totalRect; + m_renderer->renderTiledContent(renderInfo); + } + } + + // Do a full repaint if needed + if (fullRepaint) { + renderInfo.invalRect = 0; + m_renderer->renderTiledContent(renderInfo); + } + + m_atomicSync.lock(); + + if (texture == m_backTexture) { + // set the fullrepaint flags + m_fullRepaint = false; + + // The various checks to see if we are still dirty... + + m_dirty = false; + + if (m_scale != scale) + m_dirty = true; + + if (fullRepaint) + m_dirtyArea.setEmpty(); + else + m_dirtyArea.op(dirtyArea, SkRegion::kDifference_Op); + + if (!m_dirtyArea.isEmpty()) + m_dirty = true; + + ALOGV("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty); + + validatePaint(); + } else { + ALOGV("tile %p no longer owns texture %p, m_state %d. ft %p bt %p", + this, texture, m_state, m_frontTexture, m_backTexture); + } + + m_atomicSync.unlock(); +} + +void Tile::discardTextures() { + android::AutoMutex lock(m_atomicSync); + ALOGV("%p discarding bt %p, ft %p", + this, m_backTexture, m_frontTexture); + if (m_frontTexture) { + m_frontTexture->release(this); + m_frontTexture = 0; + } + if (m_backTexture) { + m_backTexture->release(this); + m_backTexture = 0; + } + m_dirtyArea.setEmpty(); + m_fullRepaint = true; + + m_dirty = true; + m_state = Unpainted; +} + +void Tile::discardBackTexture() { + android::AutoMutex lock(m_atomicSync); + if (m_backTexture) { + m_backTexture->release(this); + m_backTexture = 0; + } + m_state = Unpainted; + m_dirty = true; +} + +bool Tile::swapTexturesIfNeeded() { + android::AutoMutex lock(m_atomicSync); + if (m_state == ReadyToSwap) { + // discard old texture and swap the new one in its place + if (m_frontTexture) + m_frontTexture->release(this); + + m_frontTexture = m_backTexture; + m_backTexture = 0; + m_state = UpToDate; + ALOGV("display texture for %p at %d, %d front is now %p, back is %p", + this, m_x, m_y, m_frontTexture, m_backTexture); + + return true; + } + return false; +} + +void Tile::backTextureTransfer() { + android::AutoMutex lock(m_atomicSync); + if (m_state == PaintingStarted) + m_state = TransferredUnvalidated; + else if (m_state == ValidatedUntransferred) + m_state = ReadyToSwap; + else { + // shouldn't have transferred a tile in any other state, log + ALOGV("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d", + this, m_x, m_y, m_state); + } +} + +void Tile::backTextureTransferFail() { + // transfer failed for some reason, mark dirty so it will (repaint and) be + // retransferred. + android::AutoMutex lock(m_atomicSync); + m_state = Unpainted; + m_dirty = true; + // whether validatePaint is called before or after, it won't do anything +} + +void Tile::validatePaint() { + // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap()) + + if (!m_dirty) { + // since after the paint, the tile isn't dirty, 'validate' it - this + // may happed before or after the transfer queue operation. Only + // when both have happened, mark as 'ReadyToSwap' + if (m_state == PaintingStarted) + m_state = ValidatedUntransferred; + else if (m_state == TransferredUnvalidated) { + // When the backTexture has been marked pureColor, we will skip the + // transfer and marked as ReadyToSwap, in this case, we don't want + // to reset m_dirty bit to true. + m_state = ReadyToSwap; + } else { + ALOGV("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d", + this, m_x, m_y, m_state); + // failed transferring, in which case mark dirty (since + // paintBitmap() may have cleared m_dirty) + m_dirty = true; + } + + if (m_deferredDirty) { + ALOGV("Note: deferred dirty flag set, possibly a missed paint on tile %p", this); + m_deferredDirty = false; + } + } else { + ALOGV("Note: paint was unsuccessful."); + m_state = Unpainted; + } + +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/Tile.h b/Source/WebCore/platform/graphics/android/rendering/Tile.h new file mode 100644 index 0000000..7010301 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/Tile.h @@ -0,0 +1,191 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Tile_h +#define Tile_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "BaseRenderer.h" +#include "SkRect.h" +#include "SkRegion.h" +#include "TextureOwner.h" +#include "TilePainter.h" + +#include <utils/threads.h> + +namespace WebCore { + +class TextureInfo; +class TileTexture; +class GLWebViewState; + +/** + * An individual tile that is used to construct part of a webpage's BaseLayer of + * content. Each tile is assigned to a TiledPage and is responsible for drawing + * and displaying their section of the page. The lifecycle of a tile is: + * + * 1. Each tile is created on the main GL thread and assigned to a specific + * location within a TiledPage. + * 2. When needed the tile is passed to the background thread where it paints + * the BaseLayer's most recent PictureSet to a bitmap which is then uploaded + * to the GPU. + * 3. After the bitmap is uploaded to the GPU the main GL thread then uses the + * tile's drawGL() function to display the tile to the screen. + * 4. Steps 2-3 are repeated as necessary. + * 5. The tile is destroyed when the user navigates to a new page. + * + */ +class Tile : public TextureOwner { +public: + + // eventually, m_dirty might be rolled into the state machine, but note + // that a tile that's continually marked dirty from animation should still + // progress through the state machine and be drawn periodically (esp. for + // layers) + + // /-> TransferredUnvalidated (TQ interrupts paint) -\ (TQ & paint done) + // Unpainted -> PaintingStarted -- -> ReadyToSwap -> UpToDate + // ^ \-> ValidatedUntransferred (paint finish before TQ) -/ + // | + // \--... (From any state when marked dirty. should usually come from UpToDate if the updates are locked) + // + + enum TextureState{ + // back texture is completely unpainted + Unpainted = 0, + // has started painting, but haven't been transferred or validated + PaintingStarted = 1, + // back texture painted, transferred before validating in PaintBitmap() + TransferredUnvalidated = 2, + // back texture painted, validated before transferring in TransferQueue + ValidatedUntransferred = 3, + // back texture has been blitted, will be swapped when next available + ReadyToSwap = 4, + // has been swapped, is ready to draw, all is well + UpToDate = 5, + }; + + Tile(bool isLayerTile = false); + ~Tile(); + + bool isLayerTile() { return m_isLayerTile; } + + void setContents(int x, int y, float scale, bool isExpandedPrefetchTile); + + void reserveTexture(); + + bool isTileReady(); + + // Return false when real draw didn't happen for any reason. + bool drawGL(float opacity, const SkRect& rect, float scale, + const TransformationMatrix* transform, + bool forceBlending = false); + + // the only thread-safe function called by the background thread + void paintBitmap(TilePainter* painter); + + bool intersectWithRect(int x, int y, int tileWidth, int tileHeight, + float scale, const SkRect& dirtyRect, + SkRect& realTileRect); + bool isTileVisible(const IntRect& viewTileBounds); + + void markAsDirty(const SkRegion& dirtyArea); + bool isDirty(); + virtual bool isRepaintPending(); + void setRepaintPending(bool pending); + float scale() const { return m_scale; } + TextureState textureState() const { return m_state; } + + int x() const { return m_x; } + int y() const { return m_y; } + TileTexture* frontTexture() { return m_frontTexture; } + TileTexture* backTexture() { return m_backTexture; } + + // only used for prioritization - the higher, the more relevant the tile is + unsigned long long drawCount() { return m_drawCount; } + void discardTextures(); + void discardBackTexture(); + bool swapTexturesIfNeeded(); + void backTextureTransfer(); + void backTextureTransferFail(); + + // TextureOwner implementation + virtual bool removeTexture(TileTexture* texture); + +private: + void validatePaint(); + + int m_x; + int m_y; + + // The remaining variables can be updated throughout the lifetime of the object + + TileTexture* m_frontTexture; + TileTexture* m_backTexture; + float m_scale; + + // used to signal that the that the tile is out-of-date and needs to be + // redrawn in the backTexture + bool m_dirty; + + // currently only for debugging, to be used for tracking down dropped repaints + bool m_deferredDirty; + + // used to signal that a repaint is pending + bool m_repaintPending; + + // store the dirty region + SkRegion m_dirtyArea; + bool m_fullRepaint; + + // This mutex serves two purposes. (1) It ensures that certain operations + // happen atomically and (2) it makes sure those operations are synchronized + // across all threads and cores. + android::Mutex m_atomicSync; + + BaseRenderer* m_renderer; + + bool m_isLayerTile; + + // the most recent GL draw before this tile was prepared. used for + // prioritization and caching. tiles with old drawcounts and textures they + // own are used for new tiles and rendering + unsigned long long m_drawCount; + + // Tracks the state of painting for the tile. High level overview: + // 1) Unpainted - until paint starts (and if marked dirty, in most cases) + // 2) PaintingStarted - until paint completes + // 3) TransferredUnvalidated - if transferred first + // or ValidatedUntransferred - if validated first + // 4) ReadyToSwap - if painted and transferred, but not swapped + // 5) UpToDate - until marked dirty again + TextureState m_state; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // Tile_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp b/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp new file mode 100644 index 0000000..0e900a9 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp @@ -0,0 +1,376 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "TileGrid" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "TileGrid.h" + +#include "AndroidLog.h" +#include "DrawQuadData.h" +#include "GLWebViewState.h" +#include "PaintTileOperation.h" +#include "Tile.h" +#include "TilesManager.h" + +#include <wtf/CurrentTime.h> + +#define EXPANDED_BOUNDS_INFLATE 1 +#define EXPANDED_PREFETCH_BOUNDS_Y_INFLATE 1 + +namespace WebCore { + +TileGrid::TileGrid(bool isBaseSurface) + : m_prevTileY(0) + , m_scale(1) + , m_isBaseSurface(isBaseSurface) +{ + m_dirtyRegion.setEmpty(); +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("TileGrid"); +#endif +} + +TileGrid::~TileGrid() +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("TileGrid"); +#endif + removeTiles(); +} + +bool TileGrid::isReady() +{ + bool tilesAllReady = true; + bool tilesVisible = false; + for (unsigned int i = 0; i < m_tiles.size(); i++) { + Tile* tile = m_tiles[i]; + if (tile->isTileVisible(m_area)) { + tilesVisible = true; + if (!tile->isTileReady()) { + tilesAllReady = false; + break; + } + } + } + // For now, if no textures are available, consider ourselves as ready + // in order to unblock the zooming process. + // FIXME: have a better system -- maybe keeping the last scale factor + // able to fully render everything + ALOGV("TT %p, ready %d, visible %d, texturesRemain %d", + this, tilesAllReady, tilesVisible, + TilesManager::instance()->layerTexturesRemain()); + + return !TilesManager::instance()->layerTexturesRemain() + || !tilesVisible || tilesAllReady; +} + +bool TileGrid::isMissingContent() +{ + for (unsigned int i = 0; i < m_tiles.size(); i++) + if (m_tiles[i]->isTileVisible(m_area) && !m_tiles[i]->frontTexture()) + return true; + return false; +} + +void TileGrid::swapTiles() +{ + int swaps = 0; + for (unsigned int i = 0; i < m_tiles.size(); i++) + if (m_tiles[i]->swapTexturesIfNeeded()) + swaps++; + ALOGV("TT %p swapping, swaps = %d", this, swaps); +} + +IntRect TileGrid::computeTilesArea(const IntRect& contentArea, float scale) +{ + IntRect computedArea; + IntRect area(contentArea.x() * scale, + contentArea.y() * scale, + ceilf(contentArea.width() * scale), + ceilf(contentArea.height() * scale)); + + ALOGV("TT %p prepare, scale %f, area %d x %d", this, scale, area.width(), area.height()); + + if (area.width() == 0 && area.height() == 0) { + computedArea.setWidth(0); + computedArea.setHeight(0); + return computedArea; + } + + int tileWidth = TilesManager::tileWidth(); + int tileHeight = TilesManager::tileHeight(); + + computedArea.setX(area.x() / tileWidth); + computedArea.setY(area.y() / tileHeight); + float right = (area.x() + area.width()) / (float) tileWidth; + float bottom = (area.y() + area.height()) / (float) tileHeight; + computedArea.setWidth(ceilf(right) - computedArea.x()); + computedArea.setHeight(ceilf(bottom) - computedArea.y()); + return computedArea; +} + +void TileGrid::prepareGL(GLWebViewState* state, float scale, + const IntRect& prepareArea, const IntRect& unclippedArea, + TilePainter* painter, bool isLowResPrefetch, bool useExpandPrefetch) +{ + // first, how many tiles do we need + m_area = computeTilesArea(prepareArea, scale); + if (m_area.isEmpty()) + return; + + ALOGV("prepare TileGrid %p with scale %.2f, prepareArea " + " %d, %d - %d x %d, corresponding to %d, %d x - %d x %d tiles", + this, scale, + prepareArea.x(), prepareArea.y(), + prepareArea.width(), prepareArea.height(), + m_area.x(), m_area.y(), + m_area.width(), m_area.height()); + + bool goingDown = m_prevTileY < m_area.y(); + m_prevTileY = m_area.y(); + + if (scale != m_scale) + TilesManager::instance()->removeOperationsForFilter(new ScaleFilter(painter, m_scale)); + + m_scale = scale; + + // apply dirty region to affected tiles + if (!m_dirtyRegion.isEmpty()) { + for (unsigned int i = 0; i < m_tiles.size(); i++) + m_tiles[i]->markAsDirty(m_dirtyRegion); + + // log inval region for the base surface + if (m_isBaseSurface && TilesManager::instance()->getProfiler()->enabled()) { + SkRegion::Iterator iterator(m_dirtyRegion); + while (!iterator.done()) { + SkIRect r = iterator.rect(); + TilesManager::instance()->getProfiler()->nextInval(r, scale); + iterator.next(); + } + } + m_dirtyRegion.setEmpty(); + } + + // prepare standard bounds (clearing ExpandPrefetch flag) + for (int i = 0; i < m_area.width(); i++) { + if (goingDown) { + for (int j = 0; j < m_area.height(); j++) + prepareTile(m_area.x() + i, m_area.y() + j, + painter, state, isLowResPrefetch, false); + } else { + for (int j = m_area.height() - 1; j >= 0; j--) + prepareTile(m_area.x() + i, m_area.y() + j, + painter, state, isLowResPrefetch, false); + } + } + + // prepare expanded bounds + if (useExpandPrefetch) { + IntRect fullArea = computeTilesArea(unclippedArea, scale); + IntRect expandedArea = m_area; + expandedArea.inflate(EXPANDED_BOUNDS_INFLATE); + + if (isLowResPrefetch) + expandedArea.inflate(EXPANDED_PREFETCH_BOUNDS_Y_INFLATE); + + // clip painting area to content + expandedArea.intersect(fullArea); + + for (int i = expandedArea.x(); i < expandedArea.maxX(); i++) + for (int j = expandedArea.y(); j < expandedArea.maxY(); j++) + if (!m_area.contains(i, j)) + prepareTile(i, j, painter, state, isLowResPrefetch, true); + } +} + +void TileGrid::markAsDirty(const SkRegion& invalRegion) +{ + ALOGV("TT %p markAsDirty, current region empty %d, new empty %d", + this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty()); + m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op); +} + +void TileGrid::prepareTile(int x, int y, TilePainter* painter, + GLWebViewState* state, bool isLowResPrefetch, bool isExpandPrefetch) +{ + Tile* tile = getTile(x, y); + if (!tile) { + bool isLayerTile = !m_isBaseSurface; + tile = new Tile(isLayerTile); + m_tiles.append(tile); + } + + ALOGV("preparing tile %p at %d, %d, painter is %p", tile, x, y, painter); + + tile->setContents(x, y, m_scale, isExpandPrefetch); + + // TODO: move below (which is largely the same for layers / tiled page) into + // prepareGL() function + + if (tile->isDirty() || !tile->frontTexture()) + tile->reserveTexture(); + + if (tile->backTexture() && tile->isDirty() && !tile->isRepaintPending()) { + ALOGV("painting TT %p's tile %d %d for LG %p", this, x, y, painter); + PaintTileOperation *operation = new PaintTileOperation(tile, painter, + state, isLowResPrefetch); + TilesManager::instance()->scheduleOperation(operation); + } +} + +Tile* TileGrid::getTile(int x, int y) +{ + for (unsigned int i = 0; i <m_tiles.size(); i++) { + Tile* tile = m_tiles[i]; + if (tile->x() == x && tile->y() == y) + return tile; + } + return 0; +} + +int TileGrid::nbTextures(IntRect& area, float scale) +{ + IntRect tileBounds = computeTilesArea(area, scale); + int numberTextures = tileBounds.width() * tileBounds.height(); + + // add the number of dirty tiles in the bounds, as they take up double + // textures for double buffering + for (unsigned int i = 0; i <m_tiles.size(); i++) { + Tile* tile = m_tiles[i]; + if (tile->isDirty() + && tile->x() >= tileBounds.x() && tile->x() <= tileBounds.maxX() + && tile->y() >= tileBounds.y() && tile->y() <= tileBounds.maxY()) + numberTextures++; + } + return numberTextures; +} + +void TileGrid::drawGL(const IntRect& visibleArea, float opacity, + const TransformationMatrix* transform, + const Color* background) +{ + m_area = computeTilesArea(visibleArea, m_scale); + if (m_area.width() == 0 || m_area.height() == 0) + return; + + float invScale = 1 / m_scale; + const float tileWidth = TilesManager::tileWidth() * invScale; + const float tileHeight = TilesManager::tileHeight() * invScale; + + int drawn = 0; + + SkRegion missingRegion; + bool semiOpaqueBaseSurface = + background ? (background->hasAlpha() && background->alpha() > 0) : false; + if (semiOpaqueBaseSurface) { + SkIRect totalArea = SkIRect::MakeXYWH(m_area.x(), m_area.y(), + m_area.width(), m_area.height()); + missingRegion = SkRegion(totalArea); + } + + for (unsigned int i = 0; i < m_tiles.size(); i++) { + Tile* tile = m_tiles[i]; + + bool tileInView = tile->isTileVisible(m_area); + if (tileInView) { + SkRect rect; + rect.fLeft = tile->x() * tileWidth; + rect.fTop = tile->y() * tileHeight; + rect.fRight = rect.fLeft + tileWidth; + rect.fBottom = rect.fTop + tileHeight; + ALOGV("tile %p (layer tile: %d) %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d", + tile, tile->isLayerTile(), tile->x(), tile->y(), + tile->scale(), m_scale, tile->isTileReady(), tile->isDirty()); + + bool forceBaseBlending = background ? background->hasAlpha() : false; + bool success = tile->drawGL(opacity, rect, m_scale, transform, + forceBaseBlending); + if (semiOpaqueBaseSurface && success) { + // Cut the successful drawn tile area from the missing region. + missingRegion.op(SkIRect::MakeXYWH(tile->x(), tile->y(), 1, 1), + SkRegion::kDifference_Op); + } + if (tile->frontTexture()) + drawn++; + } + + if (semiOpaqueBaseSurface) + TilesManager::instance()->getProfiler()->nextTile(tile, invScale, tileInView); + } + + // Draw missing Regions with blend turned on + if (semiOpaqueBaseSurface) + drawMissingRegion(missingRegion, opacity, background); + + ALOGV("TT %p drew %d tiles, scale %f", + this, drawn, m_scale); +} + +void TileGrid::drawMissingRegion(const SkRegion& region, float opacity, + const Color* background) +{ + SkRegion::Iterator iterator(region); + const float tileWidth = TilesManager::tileWidth() / m_scale; + const float tileHeight = TilesManager::tileHeight() / m_scale; + ShaderProgram* shader = TilesManager::instance()->shader(); + while (!iterator.done()) { + SkIRect r = iterator.rect(); + SkRect rect; + rect.fLeft = r.x() * tileWidth; + rect.fTop = r.y() * tileHeight; + rect.fRight = rect.fLeft + tileWidth * r.width(); + rect.fBottom = rect.fTop + tileHeight * r.height(); + ALOGV("draw tile x y, %d %d (%d %d) opacity %f", r.x(), r.y(), + r.width(), r.height(), opacity); + // Skia is using pre-multiplied color. + Color postAlpha = Color(background->red() * background->alpha() / 255, + background->green() * background->alpha() / 255, + background->blue() * background->alpha() / 255, + background->alpha() ); + + PureColorQuadData backGroundData(postAlpha, BaseQuad, 0, &rect, opacity); + TilesManager::instance()->shader()->drawQuad(&backGroundData); + iterator.next(); + } +} + +void TileGrid::removeTiles() +{ + for (unsigned int i = 0; i < m_tiles.size(); i++) { + delete m_tiles[i]; + } + m_tiles.clear(); +} + +void TileGrid::discardTextures() +{ + ALOGV("TT %p discarding textures", this); + for (unsigned int i = 0; i < m_tiles.size(); i++) + m_tiles[i]->discardTextures(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/TileGrid.h b/Source/WebCore/platform/graphics/android/rendering/TileGrid.h new file mode 100644 index 0000000..ffb6c7e --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TileGrid.h @@ -0,0 +1,87 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TileGrid_h +#define TileGrid_h + +#include "IntRect.h" +#include "SkRegion.h" + +#include <wtf/Vector.h> + +namespace WebCore { + +class Color; +class GLWebViewState; +class Tile; +class TilePainter; +class TransformationMatrix; + +class TileGrid { +public: + TileGrid(bool isBaseSurface); + virtual ~TileGrid(); + + static IntRect computeTilesArea(const IntRect& contentArea, float scale); + + void prepareGL(GLWebViewState* state, float scale, + const IntRect& prepareArea, const IntRect& unclippedArea, + TilePainter* painter, bool isLowResPrefetch = false, + bool useExpandPrefetch = false); + void swapTiles(); + void drawGL(const IntRect& visibleArea, float opacity, + const TransformationMatrix* transform, const Color* background = 0); + + void prepareTile(int x, int y, TilePainter* painter, + GLWebViewState* state, bool isLowResPrefetch, bool isExpandPrefetch); + void markAsDirty(const SkRegion& dirtyArea); + + Tile* getTile(int x, int y); + + void removeTiles(); + void discardTextures(); + + bool isReady(); + bool isMissingContent(); + + int nbTextures(IntRect& area, float scale); + +private: + void drawMissingRegion(const SkRegion& region, float opacity, const Color* tileBackground); + WTF::Vector<Tile*> m_tiles; + + IntRect m_area; + + SkRegion m_dirtyRegion; + + int m_prevTileY; + float m_scale; + + bool m_isBaseSurface; +}; + +} // namespace WebCore + +#endif // TileGrid_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TilePainter.h b/Source/WebCore/platform/graphics/android/rendering/TilePainter.h new file mode 100644 index 0000000..d992aee --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TilePainter.h @@ -0,0 +1,58 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TilePainter_h +#define TilePainter_h + +#include "TransformationMatrix.h" +#include "SkRefCnt.h" + +class SkCanvas; + +namespace WebCore { + +class Tile; +class Color; + +class TilePainter : public SkRefCnt { +// TODO: investigate webkit threadsafe ref counting +public: + virtual ~TilePainter() { } + virtual bool paint(Tile* tile, SkCanvas* canvas) = 0; + virtual float opacity() { return 1.0; } + enum SurfaceType { Painted, Image }; + virtual SurfaceType type() { return Painted; } + virtual Color* background() { return 0; } + + unsigned int getUpdateCount() { return m_updateCount; } + void setUpdateCount(unsigned int updateCount) { m_updateCount = updateCount; } + +private: + unsigned int m_updateCount; +}; + +} + +#endif // TilePainter_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TileTexture.cpp b/Source/WebCore/platform/graphics/android/rendering/TileTexture.cpp new file mode 100644 index 0000000..39effd7 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TileTexture.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "TileTexture" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "TileTexture.h" + +#include "AndroidLog.h" +#include "Tile.h" +#include "ClassTracker.h" +#include "DrawQuadData.h" +#include "GLUtils.h" +#include "GLWebViewState.h" +#include "TextureOwner.h" +#include "TilesManager.h" + +namespace WebCore { + +TileTexture::TileTexture(uint32_t w, uint32_t h) + : m_owner(0) + , m_isPureColor(false) +{ + m_size.set(w, h); + m_ownTextureId = 0; + +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("TileTexture"); +#endif +} + +TileTexture::~TileTexture() +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("TileTexture"); +#endif +} + +void TileTexture::requireGLTexture() +{ + if (!m_ownTextureId) + m_ownTextureId = GLUtils::createTileGLTexture(m_size.width(), m_size.height()); +} + +void TileTexture::discardGLTexture() +{ + if (m_ownTextureId) + GLUtils::deleteTexture(&m_ownTextureId); + + if (m_owner) { + // clear both Tile->Texture and Texture->Tile links + m_owner->removeTexture(this); + release(m_owner); + } +} + +bool TileTexture::acquire(TextureOwner* owner, bool force) +{ + if (m_owner == owner) + return true; + + return setOwner(owner, force); +} + +bool TileTexture::setOwner(TextureOwner* owner, bool force) +{ + bool proceed = true; + if (m_owner && m_owner != owner) + proceed = m_owner->removeTexture(this); + + if (proceed) { + m_owner = owner; + return true; + } + + return false; +} + +bool TileTexture::release(TextureOwner* owner) +{ + ALOGV("texture %p releasing tile %p, m_owner %p", this, owner, m_owner); + if (m_owner != owner) + return false; + + m_owner = 0; + return true; +} + +void TileTexture::transferComplete() +{ + if (m_owner) { + Tile* owner = static_cast<Tile*>(m_owner); + owner->backTextureTransfer(); + } else + ALOGE("ERROR: owner missing after transfer of texture %p", this); +} + +void TileTexture::drawGL(bool isLayer, const SkRect& rect, float opacity, + const TransformationMatrix* transform, + bool forceBlending) +{ + ShaderProgram* shader = TilesManager::instance()->shader(); + + if (isLayer && !transform) { + ALOGE("ERROR: Missing tranform for layers!"); + return; + } + + // For base layer, we just follow the forceBlending, otherwise, blending is + // always turned on. + // TODO: Don't blend tiles if they are fully opaque. + forceBlending |= isLayer; + DrawQuadData commonData(isLayer ? LayerQuad : BaseQuad, transform, &rect, + opacity, forceBlending); + if (isPureColor()) { + PureColorQuadData data(commonData, pureColor()); + shader->drawQuad(&data); + } else { + TextureQuadData data(commonData, m_ownTextureId, GL_TEXTURE_2D, GL_LINEAR); + shader->drawQuad(&data); + } +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/TileTexture.h b/Source/WebCore/platform/graphics/android/rendering/TileTexture.h new file mode 100644 index 0000000..5fe43b0 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TileTexture.h @@ -0,0 +1,99 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TileTexture_h +#define TileTexture_h + +#include "TextureInfo.h" +#include "Color.h" +#include "SkBitmap.h" +#include "SkRect.h" +#include "SkSize.h" + +#include <GLES2/gl2.h> + +class SkCanvas; + +namespace WebCore { + +class TextureOwner; +class Tile; +class TransformationMatrix; + +class TileTexture { +public: + // This object is to be constructed on the consumer's thread and must have + // a width and height greater than 0. + TileTexture(uint32_t w, uint32_t h); + virtual ~TileTexture(); + + // allows consumer thread to assign ownership of the texture to the tile. It + // returns false if ownership cannot be transferred because the tile is busy + bool acquire(TextureOwner* owner, bool force = false); + bool release(TextureOwner* owner); + + // set the texture owner if not busy. Return false if busy, true otherwise. + bool setOwner(TextureOwner* owner, bool force = false); + + // private member accessor functions + TextureOwner* owner() { return m_owner; } // only used by the consumer thread + + const SkSize& getSize() const { return m_size; } + + // OpenGL ID of backing texture, 0 when not allocated + GLuint m_ownTextureId; + // these are used for dynamically (de)allocating backing graphics memory + void requireGLTexture(); + void discardGLTexture(); + + void transferComplete(); + + TextureInfo* getTextureInfo() { return &m_ownTextureInfo; } + + // Make sure the following pureColor getter/setter are only read/written + // in UI thread. Therefore no need for a lock. + void setPure(bool pure) { m_isPureColor = pure; } + bool isPureColor() {return m_isPureColor; } + void setPureColor(const Color& color) { m_pureColor = color; setPure(true); } + Color pureColor() { return m_pureColor; } + + void drawGL(bool isLayer, const SkRect& rect, float opacity, + const TransformationMatrix* transform, bool forceBlending = false); +private: + TextureInfo m_ownTextureInfo; + SkSize m_size; + + // Tile owning the texture, only modified by UI thread + TextureOwner* m_owner; + + // When the whole tile is single color, skip the transfer queue and draw + // it directly through shader. + bool m_isPureColor; + Color m_pureColor; +}; + +} // namespace WebCore + +#endif // TileTexture_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp b/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp new file mode 100644 index 0000000..e8b8cd1 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp @@ -0,0 +1,449 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "TilesManager" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "TilesManager.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "GLWebViewState.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "Tile.h" +#include "TileTexture.h" +#include "TransferQueue.h" + +#include <android/native_window.h> +#include <cutils/atomic.h> +#include <gui/SurfaceTexture.h> +#include <gui/SurfaceTextureClient.h> +#include <wtf/CurrentTime.h> + +// Important: We need at least twice as many textures as is needed to cover +// one viewport, otherwise the allocation may stall. +// We need n textures for one TiledPage, and another n textures for the +// second page used when scaling. +// In our case, we use 256*256 textures. On the tablet, this equates to +// at least 60 textures, or 112 with expanded tile boundaries. +// 112(tiles)*256*256*4(bpp)*2(pages) = 56MB +// It turns out the viewport dependent value m_maxTextureCount is a reasonable +// number to cap the layer tile texturs, it worked on both phones and tablets. +// TODO: after merge the pool of base tiles and layer tiles, we should revisit +// the logic of allocation management. +#define MAX_TEXTURE_ALLOCATION ((6+TILE_PREFETCH_DISTANCE*2)*(5+TILE_PREFETCH_DISTANCE*2)*4) +#define TILE_WIDTH 256 +#define TILE_HEIGHT 256 + +#define BYTES_PER_PIXEL 4 // 8888 config + +#define LAYER_TEXTURES_DESTROY_TIMEOUT 60 // If we do not need layers for 60 seconds, free the textures + +namespace WebCore { + +GLint TilesManager::getMaxTextureSize() +{ + static GLint maxTextureSize = 0; + if (!maxTextureSize) + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + return maxTextureSize; +} + +int TilesManager::getMaxTextureAllocation() +{ + return MAX_TEXTURE_ALLOCATION; +} + +TilesManager::TilesManager() + : m_layerTexturesRemain(true) + , m_highEndGfx(false) + , m_maxTextureCount(0) + , m_maxLayerTextureCount(0) + , m_generatorReady(false) + , m_showVisualIndicator(false) + , m_invertedScreen(false) + , m_useMinimalMemory(true) + , m_useDoubleBuffering(true) + , m_contentUpdates(0) + , m_webkitContentUpdates(0) + , m_queue(0) + , m_drawGLCount(1) + , m_lastTimeLayersUsed(0) + , m_hasLayerTextures(false) +{ + ALOGV("TilesManager ctor"); + m_textures.reserveCapacity(MAX_TEXTURE_ALLOCATION); + m_availableTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); + m_tilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); + m_availableTilesTextures.reserveCapacity(MAX_TEXTURE_ALLOCATION); + m_pixmapsGenerationThread = new TexturesGenerator(this); + m_pixmapsGenerationThread->run("TexturesGenerator"); +} + +void TilesManager::allocateTiles() +{ + int nbTexturesToAllocate = m_maxTextureCount - m_textures.size(); + ALOGV("%d tiles to allocate (%d textures planned)", nbTexturesToAllocate, m_maxTextureCount); + int nbTexturesAllocated = 0; + for (int i = 0; i < nbTexturesToAllocate; i++) { + TileTexture* texture = new TileTexture( + tileWidth(), tileHeight()); + // the atomic load ensures that the texture has been fully initialized + // before we pass a pointer for other threads to operate on + TileTexture* loadedTexture = + reinterpret_cast<TileTexture*>( + android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); + m_textures.append(loadedTexture); + nbTexturesAllocated++; + } + + int nbLayersTexturesToAllocate = m_maxLayerTextureCount - m_tilesTextures.size(); + ALOGV("%d layers tiles to allocate (%d textures planned)", + nbLayersTexturesToAllocate, m_maxLayerTextureCount); + int nbLayersTexturesAllocated = 0; + for (int i = 0; i < nbLayersTexturesToAllocate; i++) { + TileTexture* texture = new TileTexture( + tileWidth(), tileHeight()); + // the atomic load ensures that the texture has been fully initialized + // before we pass a pointer for other threads to operate on + TileTexture* loadedTexture = + reinterpret_cast<TileTexture*>( + android_atomic_acquire_load(reinterpret_cast<int32_t*>(&texture))); + m_tilesTextures.append(loadedTexture); + nbLayersTexturesAllocated++; + } + ALOGV("allocated %d textures for base (total: %d, %d Mb), %d textures for layers (total: %d, %d Mb)", + nbTexturesAllocated, m_textures.size(), + m_textures.size() * TILE_WIDTH * TILE_HEIGHT * 4 / 1024 / 1024, + nbLayersTexturesAllocated, m_tilesTextures.size(), + m_tilesTextures.size() * tileWidth() * tileHeight() * 4 / 1024 / 1024); +} + +void TilesManager::discardTextures(bool allTextures, bool glTextures) +{ + const unsigned int max = m_textures.size(); + + unsigned long long sparedDrawCount = ~0; // by default, spare no textures + if (!allTextures) { + // if we're not deallocating all textures, spare those with max drawcount + sparedDrawCount = 0; + for (unsigned int i = 0; i < max; i++) { + TextureOwner* owner = m_textures[i]->owner(); + if (owner) + sparedDrawCount = std::max(sparedDrawCount, owner->drawCount()); + } + } + discardTexturesVector(sparedDrawCount, m_textures, glTextures); + discardTexturesVector(sparedDrawCount, m_tilesTextures, glTextures); +} + +void TilesManager::discardTexturesVector(unsigned long long sparedDrawCount, + WTF::Vector<TileTexture*>& textures, + bool deallocateGLTextures) +{ + const unsigned int max = textures.size(); + int dealloc = 0; + WTF::Vector<int> discardedIndex; + for (unsigned int i = 0; i < max; i++) { + TextureOwner* owner = textures[i]->owner(); + if (!owner || owner->drawCount() < sparedDrawCount) { + if (deallocateGLTextures) { + // deallocate textures' gl memory + textures[i]->discardGLTexture(); + discardedIndex.append(i); + } else if (owner) { + // simply detach textures from owner + static_cast<Tile*>(owner)->discardTextures(); + } + dealloc++; + } + } + + bool base = textures == m_textures; + // Clean up the vector of TileTextures and reset the max texture count. + if (discardedIndex.size()) { + android::Mutex::Autolock lock(m_texturesLock); + for (int i = discardedIndex.size() - 1; i >= 0; i--) + textures.remove(discardedIndex[i]); + + int remainedTextureNumber = textures.size(); + int* countPtr = base ? &m_maxTextureCount : &m_maxLayerTextureCount; + if (remainedTextureNumber < *countPtr) { + ALOGV("reset maxTextureCount for %s tiles from %d to %d", + base ? "base" : "layer", *countPtr, remainedTextureNumber); + *countPtr = remainedTextureNumber; + } + + } + + ALOGV("Discarded %d %s textures (out of %d %s tiles)", + dealloc, (deallocateGLTextures ? "gl" : ""), + max, base ? "base" : "layer"); +} + +void TilesManager::gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures, + int* nbLayerTextures, int* nbAllocatedLayerTextures) +{ + *nbTextures = m_textures.size(); + for (unsigned int i = 0; i < m_textures.size(); i++) { + TileTexture* texture = m_textures[i]; + if (texture->m_ownTextureId) + *nbAllocatedTextures += 1; + } + *nbLayerTextures = m_tilesTextures.size(); + for (unsigned int i = 0; i < m_tilesTextures.size(); i++) { + TileTexture* texture = m_tilesTextures[i]; + if (texture->m_ownTextureId) + *nbAllocatedLayerTextures += 1; + } +} + +void TilesManager::printTextures() +{ +#ifdef DEBUG + ALOGV("++++++"); + for (unsigned int i = 0; i < m_textures.size(); i++) { + TileTexture* texture = m_textures[i]; + Tile* o = 0; + if (texture->owner()) + o = (Tile*) texture->owner(); + int x = -1; + int y = -1; + if (o) { + x = o->x(); + y = o->y(); + } + ALOGV("[%d] texture %x owner: %x (%d, %d) scale: %.2f", + i, texture, o, x, y, o ? o->scale() : 0); + } + ALOGV("------"); +#endif // DEBUG +} + +void TilesManager::gatherTextures() +{ + android::Mutex::Autolock lock(m_texturesLock); + m_availableTextures = m_textures; + m_availableTilesTextures = m_tilesTextures; + m_layerTexturesRemain = true; +} + +TileTexture* TilesManager::getAvailableTexture(Tile* owner) +{ + android::Mutex::Autolock lock(m_texturesLock); + + // Sanity check that the tile does not already own a texture + if (owner->backTexture() && owner->backTexture()->owner() == owner) { + ALOGV("same owner (%d, %d), getAvailableBackTexture(%x) => texture %x", + owner->x(), owner->y(), owner, owner->backTexture()); + if (owner->isLayerTile()) + m_availableTilesTextures.remove(m_availableTilesTextures.find(owner->backTexture())); + else + m_availableTextures.remove(m_availableTextures.find(owner->backTexture())); + return owner->backTexture(); + } + + WTF::Vector<TileTexture*>* availableTexturePool; + if (owner->isLayerTile()) { + availableTexturePool = &m_availableTilesTextures; + } else { + availableTexturePool = &m_availableTextures; + } + + // The heuristic for selecting a texture is as follows: + // 1. Skip textures currently being painted, they can't be painted while + // busy anyway + // 2. If a tile isn't owned, break with that one + // 3. Don't let tiles acquire their front textures + // 4. Otherwise, use the least recently prepared tile, but ignoring tiles + // drawn in the last frame to avoid flickering + + TileTexture* farthestTexture = 0; + unsigned long long oldestDrawCount = getDrawGLCount() - 1; + const unsigned int max = availableTexturePool->size(); + for (unsigned int i = 0; i < max; i++) { + TileTexture* texture = (*availableTexturePool)[i]; + Tile* currentOwner = static_cast<Tile*>(texture->owner()); + if (!currentOwner) { + // unused texture! take it! + farthestTexture = texture; + break; + } + + if (currentOwner == owner) { + // Don't let a tile acquire its own front texture, as the + // acquisition logic doesn't handle that + continue; + } + + unsigned long long textureDrawCount = currentOwner->drawCount(); + if (oldestDrawCount > textureDrawCount) { + farthestTexture = texture; + oldestDrawCount = textureDrawCount; + } + } + + if (farthestTexture) { + Tile* previousOwner = static_cast<Tile*>(farthestTexture->owner()); + if (farthestTexture->acquire(owner)) { + if (previousOwner) { + previousOwner->removeTexture(farthestTexture); + + ALOGV("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)", + owner->isLayerTile() ? "LAYER" : "BASE", + farthestTexture, previousOwner->x(), previousOwner->y(), + owner->x(), owner->y(), + oldestDrawCount, getDrawGLCount()); + } + + availableTexturePool->remove(availableTexturePool->find(farthestTexture)); + return farthestTexture; + } + } else { + if (owner->isLayerTile()) { + // couldn't find a tile for a layer, layers shouldn't request redraw + // TODO: once we do layer prefetching, don't set this for those + // tiles + m_layerTexturesRemain = false; + } + } + + ALOGV("Couldn't find an available texture for %s tile %x (%d, %d) out of %d available", + owner->isLayerTile() ? "LAYER" : "BASE", + owner, owner->x(), owner->y(), max); +#ifdef DEBUG + printTextures(); +#endif // DEBUG + return 0; +} + +void TilesManager::setHighEndGfx(bool highEnd) +{ + m_highEndGfx = highEnd; +} + +bool TilesManager::highEndGfx() +{ + return m_highEndGfx; +} + +int TilesManager::maxTextureCount() +{ + android::Mutex::Autolock lock(m_texturesLock); + return m_maxTextureCount; +} + +int TilesManager::maxLayerTextureCount() +{ + android::Mutex::Autolock lock(m_texturesLock); + return m_maxLayerTextureCount; +} + +void TilesManager::setMaxTextureCount(int max) +{ + ALOGV("setMaxTextureCount: %d (current: %d, total:%d)", + max, m_maxTextureCount, MAX_TEXTURE_ALLOCATION); + if (m_maxTextureCount == MAX_TEXTURE_ALLOCATION || + max <= m_maxTextureCount) + return; + + android::Mutex::Autolock lock(m_texturesLock); + + if (max < MAX_TEXTURE_ALLOCATION) + m_maxTextureCount = max; + else + m_maxTextureCount = MAX_TEXTURE_ALLOCATION; + + allocateTiles(); +} + +void TilesManager::setMaxLayerTextureCount(int max) +{ + ALOGV("setMaxLayerTextureCount: %d (current: %d, total:%d)", + max, m_maxLayerTextureCount, MAX_TEXTURE_ALLOCATION); + if (!max && m_hasLayerTextures) { + double secondsSinceLayersUsed = WTF::currentTime() - m_lastTimeLayersUsed; + if (secondsSinceLayersUsed > LAYER_TEXTURES_DESTROY_TIMEOUT) { + unsigned long long sparedDrawCount = ~0; // by default, spare no textures + bool deleteGLTextures = true; + discardTexturesVector(sparedDrawCount, m_tilesTextures, deleteGLTextures); + m_hasLayerTextures = false; + } + return; + } + m_lastTimeLayersUsed = WTF::currentTime(); + if (m_maxLayerTextureCount == MAX_TEXTURE_ALLOCATION || + max <= m_maxLayerTextureCount) + return; + + android::Mutex::Autolock lock(m_texturesLock); + + if (max < MAX_TEXTURE_ALLOCATION) + m_maxLayerTextureCount = max; + else + m_maxLayerTextureCount = MAX_TEXTURE_ALLOCATION; + + allocateTiles(); + m_hasLayerTextures = true; +} + +TransferQueue* TilesManager::transferQueue() +{ + // m_queue will be created on the UI thread, although it may + // be accessed from the TexturesGenerator. However, that can only happen after + // a previous transferQueue() call due to a prepare. + if (!m_queue) + m_queue = new TransferQueue(m_useMinimalMemory); + return m_queue; +} + +float TilesManager::tileWidth() +{ + return TILE_WIDTH; +} + +float TilesManager::tileHeight() +{ + return TILE_HEIGHT; +} + +TilesManager* TilesManager::instance() +{ + if (!gInstance) { + gInstance = new TilesManager(); + ALOGV("instance(), new gInstance is %x", gInstance); + } + return gInstance; +} + +TilesManager* TilesManager::gInstance = 0; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesManager.h b/Source/WebCore/platform/graphics/android/rendering/TilesManager.h new file mode 100644 index 0000000..92c56d3 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TilesManager.h @@ -0,0 +1,209 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TilesManager_h +#define TilesManager_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "LayerAndroid.h" +#include "ShaderProgram.h" +#include "TexturesGenerator.h" +#include "TilesProfiler.h" +#include "VideoLayerManager.h" +#include <utils/threads.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +class OperationFilter; +class Tile; +class TileTexture; +class TransferQueue; + +class TilesManager { +public: + // May only be called from the UI thread + static TilesManager* instance(); + static GLint getMaxTextureSize(); + static int getMaxTextureAllocation(); + + static bool hardwareAccelerationEnabled() + { + return gInstance != 0; + } + + void removeOperationsForFilter(OperationFilter* filter, bool waitForRunning = false) + { + m_pixmapsGenerationThread->removeOperationsForFilter(filter, waitForRunning); + } + + void scheduleOperation(QueuedOperation* operation) + { + m_pixmapsGenerationThread->scheduleOperation(operation); + } + + ShaderProgram* shader() { return &m_shader; } + TransferQueue* transferQueue(); + VideoLayerManager* videoLayerManager() { return &m_videoLayerManager; } + + void gatherTextures(); + bool layerTexturesRemain() { return m_layerTexturesRemain; } + void gatherTexturesNumbers(int* nbTextures, int* nbAllocatedTextures, + int* nbLayerTextures, int* nbAllocatedLayerTextures); + + TileTexture* getAvailableTexture(Tile* owner); + + void printTextures(); + + // m_highEndGfx is written/read only on UI thread, no need for a lock. + void setHighEndGfx(bool highEnd); + bool highEndGfx(); + + int maxTextureCount(); + int maxLayerTextureCount(); + void setMaxTextureCount(int max); + void setMaxLayerTextureCount(int max); + static float tileWidth(); + static float tileHeight(); + + void allocateTiles(); + + // remove all tiles from textures (and optionally deallocate gl memory) + void discardTextures(bool allTextures, bool glTextures); + + bool getShowVisualIndicator() + { + return m_showVisualIndicator; + } + + void setShowVisualIndicator(bool showVisualIndicator) + { + m_showVisualIndicator = showVisualIndicator; + } + + TilesProfiler* getProfiler() + { + return &m_profiler; + } + + bool invertedScreen() + { + return m_invertedScreen; + } + + void setInvertedScreen(bool invert) + { + m_invertedScreen = invert; + } + + void setInvertedScreenContrast(float contrast) + { + m_shader.setContrast(contrast); + } + + void setUseMinimalMemory(bool useMinimalMemory) + { + m_useMinimalMemory = useMinimalMemory; + } + + bool useMinimalMemory() + { + return m_useMinimalMemory; + } + + void setUseDoubleBuffering(bool useDoubleBuffering) + { + m_useDoubleBuffering = useDoubleBuffering; + } + bool useDoubleBuffering() { return m_useDoubleBuffering; } + + + unsigned int incWebkitContentUpdates() { return m_webkitContentUpdates++; } + + void incContentUpdates() { m_contentUpdates++; } + unsigned int getContentUpdates() { return m_contentUpdates; } + void clearContentUpdates() { m_contentUpdates = 0; } + + void incDrawGLCount() + { + m_drawGLCount++; + } + + unsigned long long getDrawGLCount() + { + return m_drawGLCount; + } + +private: + TilesManager(); + + void discardTexturesVector(unsigned long long sparedDrawCount, + WTF::Vector<TileTexture*>& textures, + bool deallocateGLTextures); + + WTF::Vector<TileTexture*> m_textures; + WTF::Vector<TileTexture*> m_availableTextures; + + WTF::Vector<TileTexture*> m_tilesTextures; + WTF::Vector<TileTexture*> m_availableTilesTextures; + bool m_layerTexturesRemain; + + bool m_highEndGfx; + int m_maxTextureCount; + int m_maxLayerTextureCount; + + bool m_generatorReady; + + bool m_showVisualIndicator; + bool m_invertedScreen; + + bool m_useMinimalMemory; + + bool m_useDoubleBuffering; + unsigned int m_contentUpdates; // nr of successful tiled paints + unsigned int m_webkitContentUpdates; // nr of paints from webkit + + sp<TexturesGenerator> m_pixmapsGenerationThread; + + android::Mutex m_texturesLock; + + static TilesManager* gInstance; + + ShaderProgram m_shader; + TransferQueue* m_queue; + + VideoLayerManager m_videoLayerManager; + + TilesProfiler m_profiler; + unsigned long long m_drawGLCount; + double m_lastTimeLayersUsed; + bool m_hasLayerTextures; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // TilesManager_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesProfiler.cpp b/Source/WebCore/platform/graphics/android/rendering/TilesProfiler.cpp new file mode 100644 index 0000000..4f0c6b5 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TilesProfiler.cpp @@ -0,0 +1,133 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "TilesProfiler" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "TilesProfiler.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "Tile.h" +#include "TilesManager.h" +#include <wtf/CurrentTime.h> + +// Hard limit on amount of frames (and thus memory) profiling can take +#define MAX_PROF_FRAMES 400 +#define INVAL_CODE -2 + +namespace WebCore { +TilesProfiler::TilesProfiler() + : m_enabled(false) +{ +} + +void TilesProfiler::start() +{ + m_enabled = true; + m_goodTiles = 0; + m_badTiles = 0; + m_records.clear(); + m_time = currentTimeMS(); + ALOGV("initializing tileprofiling"); +} + +float TilesProfiler::stop() +{ + m_enabled = false; + ALOGV("completed tile profiling, observed %d frames", m_records.size()); + return (1.0 * m_goodTiles) / (m_goodTiles + m_badTiles); +} + +void TilesProfiler::clear() +{ + ALOGV("clearing tile profiling of its %d frames", m_records.size()); + m_records.clear(); +} + +void TilesProfiler::nextFrame(int left, int top, int right, int bottom, float scale) +{ + if (!m_enabled || (m_records.size() > MAX_PROF_FRAMES)) + return; + + double currentTime = currentTimeMS(); + double timeDelta = currentTime - m_time; + m_time = currentTime; + +#ifdef DEBUG + if (m_records.size() != 0) { + ALOGD("completed tile profiling frame, observed %d tiles. %f ms since last", + m_records[0].size(), timeDelta); + } +#endif // DEBUG + + m_records.append(WTF::Vector<TileProfileRecord>()); + + //first record designates viewport + m_records.last().append(TileProfileRecord( + left, top, right, bottom, + scale, true, (int)(timeDelta * 1000))); +} + +void TilesProfiler::nextTile(Tile* tile, float scale, bool inView) +{ + if (!m_enabled || (m_records.size() > MAX_PROF_FRAMES) || (m_records.size() == 0)) + return; + + bool isReady = tile->isTileReady(); + int left = tile->x() * TilesManager::tileWidth(); + int top = tile->y() * TilesManager::tileWidth(); + int right = left + TilesManager::tileWidth(); + int bottom = top + TilesManager::tileWidth(); + + if (inView) { + if (isReady) + m_goodTiles++; + else + m_badTiles++; + } + m_records.last().append(TileProfileRecord( + left, top, right, bottom, + scale, isReady, (int)tile->drawCount())); + ALOGV("adding tile %d %d %d %d, scale %f", left, top, right, bottom, scale); +} + +void TilesProfiler::nextInval(const SkIRect& rect, float scale) +{ + if (!m_enabled || (m_records.size() > MAX_PROF_FRAMES) || (m_records.size() == 0)) + return; + + m_records.last().append(TileProfileRecord( + rect.x(), rect.y(), + rect.right(), rect.bottom(), scale, false, INVAL_CODE)); + ALOGV("adding inval region %d %d %d %d, scale %f", rect.x(), rect.y(), + rect.right(), rect.bottom(), scale); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesProfiler.h b/Source/WebCore/platform/graphics/android/rendering/TilesProfiler.h new file mode 100644 index 0000000..b39ae2f --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TilesProfiler.h @@ -0,0 +1,90 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TilesProfiler_h +#define TilesProfiler_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "IntRect.h" +#include "SkRect.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class Tile; + +struct TileProfileRecord { + TileProfileRecord(int left, int top, int right, int bottom, float scale, int isReady, int level) { + this->left = left; + this->top = top; + this->right = right; + this->bottom = bottom; + this->scale = scale; + this->isReady = isReady; + this->level = level; + } + int left, top, right, bottom; + bool isReady; + int level; + float scale; +}; + +class TilesProfiler { +public: + TilesProfiler(); + + void start(); + float stop(); + void clear(); + void nextFrame(int left, int top, int right, int bottom, float scale); + void nextTile(Tile* tile, float scale, bool inView); + void nextInval(const SkIRect& rect, float scale); + int numFrames() { + return m_records.size(); + }; + + int numTilesInFrame(int frame) { + return m_records[frame].size(); + } + + TileProfileRecord* getTile(int frame, int tile) { + return &m_records[frame][tile]; + } + + bool enabled() { return m_enabled; } + +private: + bool m_enabled; + unsigned int m_goodTiles; + unsigned int m_badTiles; + WTF::Vector<WTF::Vector<TileProfileRecord> > m_records; + double m_time; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // TilesProfiler_h diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp new file mode 100644 index 0000000..ec0d9e7 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp @@ -0,0 +1,644 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "TransferQueue" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "TransferQueue.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "AndroidLog.h" +#include "DrawQuadData.h" +#include "GLUtils.h" +#include "Tile.h" +#include "TileTexture.h" +#include "TilesManager.h" +#include <android/native_window.h> +#include <gui/SurfaceTexture.h> +#include <gui/SurfaceTextureClient.h> + +// For simple webView usage, MINIMAL_SIZE is recommended for memory saving. +// In browser case, EFFICIENT_SIZE is preferred. +#define MINIMAL_SIZE 1 +#define EFFICIENT_SIZE 6 + +// Set this to 1 if we would like to take the new GpuUpload approach which +// relied on the glCopyTexSubImage2D instead of a glDraw call +#define GPU_UPLOAD_WITHOUT_DRAW 1 + +namespace WebCore { + +TransferQueue::TransferQueue(bool useMinimalMem) + : m_eglSurface(EGL_NO_SURFACE) + , m_transferQueueIndex(0) + , m_fboID(0) + , m_sharedSurfaceTextureId(0) + , m_hasGLContext(true) + , m_interruptedByRemovingOp(false) + , m_currentDisplay(EGL_NO_DISPLAY) + , m_currentUploadType(DEFAULT_UPLOAD_TYPE) +{ + memset(&m_GLStateBeforeBlit, 0, sizeof(m_GLStateBeforeBlit)); + m_transferQueueSize = useMinimalMem ? MINIMAL_SIZE : EFFICIENT_SIZE; + m_emptyItemCount = m_transferQueueSize; + m_transferQueue = new TileTransferData[m_transferQueueSize]; +} + +TransferQueue::~TransferQueue() +{ + android::Mutex::Autolock lock(m_transferQueueItemLocks); + cleanupGLResources(); + delete[] m_transferQueue; +} + +// This should be called within the m_transferQueueItemLocks. +// Now only called by emptyQueue() and destructor. +void TransferQueue::cleanupGLResources() +{ + if (m_sharedSurfaceTexture.get()) { + m_sharedSurfaceTexture->abandon(); + m_sharedSurfaceTexture.clear(); + } + if (m_fboID) { + glDeleteFramebuffers(1, &m_fboID); + m_fboID = 0; + } + if (m_sharedSurfaceTextureId) { + glDeleteTextures(1, &m_sharedSurfaceTextureId); + m_sharedSurfaceTextureId = 0; + } +} + +void TransferQueue::initGLResources(int width, int height) +{ + android::Mutex::Autolock lock(m_transferQueueItemLocks); + if (!m_sharedSurfaceTextureId) { + glGenTextures(1, &m_sharedSurfaceTextureId); + sp<BufferQueue> bufferQueue(new BufferQueue(true)); + m_sharedSurfaceTexture = +#if GPU_UPLOAD_WITHOUT_DRAW + new android::SurfaceTexture(m_sharedSurfaceTextureId, true, + GL_TEXTURE_2D, false, bufferQueue); +#else + new android::SurfaceTexture(m_sharedSurfaceTextureId, true, + GL_TEXTURE_EXTERNAL_OES, true, + bufferQueue); +#endif + m_ANW = new android::SurfaceTextureClient(m_sharedSurfaceTexture); + m_sharedSurfaceTexture->setSynchronousMode(true); + + int extraBuffersNeeded = 0; + m_ANW->query(m_ANW.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &extraBuffersNeeded); + bufferQueue->setBufferCount(m_transferQueueSize + extraBuffersNeeded); + + int result = native_window_set_buffers_geometry(m_ANW.get(), + width, height, HAL_PIXEL_FORMAT_RGBA_8888); + GLUtils::checkSurfaceTextureError("native_window_set_buffers_geometry", result); + result = native_window_set_usage(m_ANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + GLUtils::checkSurfaceTextureError("native_window_set_usage", result); + } + + if (!m_fboID) + glGenFramebuffers(1, &m_fboID); +} + +// When bliting, if the item from the transfer queue is mismatching b/t the +// Tile and the content, then the item is considered as obsolete, and +// the content is discarded. +bool TransferQueue::checkObsolete(const TileTransferData* data) +{ + Tile* baseTilePtr = data->savedTilePtr; + if (!baseTilePtr) { + ALOGV("Invalid savedTilePtr , such that the tile is obsolete"); + return true; + } + + TileTexture* baseTileTexture = baseTilePtr->backTexture(); + if (!baseTileTexture || baseTileTexture != data->savedTileTexturePtr) { + ALOGV("Invalid baseTileTexture %p (vs expected %p), such that the tile is obsolete", + baseTileTexture, data->savedTileTexturePtr); + return true; + } + + return false; +} + +void TransferQueue::blitTileFromQueue(GLuint fboID, TileTexture* destTex, + TileTexture* frontTex, + GLuint srcTexId, GLenum srcTexTarget, + int index) +{ +#if GPU_UPLOAD_WITHOUT_DRAW + glBindFramebuffer(GL_FRAMEBUFFER, fboID); + glBindTexture(GL_TEXTURE_2D, destTex->m_ownTextureId); + + int textureWidth = destTex->getSize().width(); + int textureHeight = destTex->getSize().height(); + + IntRect inval = m_transferQueue[index].invalRect; + bool partialInval = !inval.isEmpty(); + + if (partialInval && frontTex) { + // recopy the previous texture to the new one, as + // the partial update will not cover the entire texture + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + frontTex->m_ownTextureId, + 0); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, + textureWidth, textureHeight); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + srcTexId, + 0); + + if (!partialInval) { + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, + textureWidth, textureHeight); + } else { + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, inval.x(), inval.y(), 0, 0, + inval.width(), inval.height()); + } + +#else + // Then set up the FBO and copy the SurfTex content in. + glBindFramebuffer(GL_FRAMEBUFFER, fboID); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + destTex->m_ownTextureId, + 0); + setGLStateForCopy(destTex->getSize().width(), + destTex->getSize().height()); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + ALOGV("Error: glCheckFramebufferStatus failed"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return; + } + + // Use empty rect to set up the special matrix to draw. + SkRect rect = SkRect::MakeEmpty(); + + TextureQuadData data(srcTexId, GL_NEAREST, srcTexTarget, Blit, 0, 0, 1.0, false); + TilesManager::instance()->shader()->drawQuad(&data); + + // To workaround a sync issue on some platforms, we should insert the sync + // here while in the current FBO. + // This will essentially kick off the GPU command buffer, and the Tex Gen + // thread will then have to wait for this buffer to finish before writing + // into the same memory. + EGLDisplay dpy = eglGetCurrentDisplay(); + if (m_currentDisplay != dpy) + m_currentDisplay = dpy; + if (m_currentDisplay != EGL_NO_DISPLAY) { + if (m_transferQueue[index].m_syncKHR != EGL_NO_SYNC_KHR) + eglDestroySyncKHR(m_currentDisplay, m_transferQueue[index].m_syncKHR); + m_transferQueue[index].m_syncKHR = eglCreateSyncKHR(m_currentDisplay, + EGL_SYNC_FENCE_KHR, + 0); + } + GLUtils::checkEglError("CreateSyncKHR"); +#endif +} + +void TransferQueue::interruptTransferQueue(bool interrupt) +{ + m_transferQueueItemLocks.lock(); + m_interruptedByRemovingOp = interrupt; + if (m_interruptedByRemovingOp) + m_transferQueueItemCond.signal(); + m_transferQueueItemLocks.unlock(); +} + +// This function must be called inside the m_transferQueueItemLocks, for the +// wait, m_interruptedByRemovingOp and getHasGLContext(). +// Only called by updateQueueWithBitmap() for now. +bool TransferQueue::readyForUpdate() +{ + if (!getHasGLContext()) + return false; + // Don't use a while loop since when the WebView tear down, the emptyCount + // will still be 0, and we bailed out b/c of GL context lost. + if (!m_emptyItemCount) { + if (m_interruptedByRemovingOp) + return false; + m_transferQueueItemCond.wait(m_transferQueueItemLocks); + if (m_interruptedByRemovingOp) + return false; + } + + if (!getHasGLContext()) + return false; + + // Disable this wait until we figure out why this didn't work on some + // drivers b/5332112. +#if 0 + if (m_currentUploadType == GpuUpload + && m_currentDisplay != EGL_NO_DISPLAY) { + // Check the GPU fence + EGLSyncKHR syncKHR = m_transferQueue[getNextTransferQueueIndex()].m_syncKHR; + if (syncKHR != EGL_NO_SYNC_KHR) + eglClientWaitSyncKHR(m_currentDisplay, + syncKHR, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + EGL_FOREVER_KHR); + } + GLUtils::checkEglError("WaitSyncKHR"); +#endif + + return true; +} + +// Both getHasGLContext and setHasGLContext should be called within the lock. +bool TransferQueue::getHasGLContext() +{ + return m_hasGLContext; +} + +void TransferQueue::setHasGLContext(bool hasContext) +{ + m_hasGLContext = hasContext; +} + +void TransferQueue::setPendingDiscardWithLock() +{ + android::Mutex::Autolock lock(m_transferQueueItemLocks); + setPendingDiscard(); +} + +void TransferQueue::emptyQueue() +{ + android::Mutex::Autolock lock(m_transferQueueItemLocks); + setPendingDiscard(); + cleanupPendingDiscard(); + cleanupGLResources(); +} + +// Set all the content in the queue to pendingDiscard, after this, there will +// be nothing added to the queue, and this can be called in any thread. +// However, in order to discard the content in the Surface Texture using +// updateTexImage, cleanupPendingDiscard need to be called on the UI thread. +// Must be called within a m_transferQueueItemLocks. +void TransferQueue::setPendingDiscard() +{ + for (int i = 0 ; i < m_transferQueueSize; i++) + if (m_transferQueue[i].status == pendingBlit) + m_transferQueue[i].status = pendingDiscard; + + m_pureColorTileQueue.clear(); + + bool GLContextExisted = getHasGLContext(); + // Unblock the Tex Gen thread first before Tile Page deletion. + // Otherwise, there will be a deadlock while removing operations. + setHasGLContext(false); + + // Only signal once when GL context lost. + if (GLContextExisted) + m_transferQueueItemCond.signal(); +} + +void TransferQueue::updatePureColorTiles() +{ + for (unsigned int i = 0 ; i < m_pureColorTileQueue.size(); i++) { + TileTransferData* data = &m_pureColorTileQueue[i]; + if (data->status == pendingBlit) { + TileTexture* destTexture = 0; + bool obsoleteTile = checkObsolete(data); + if (!obsoleteTile) { + destTexture = data->savedTilePtr->backTexture(); + destTexture->setPureColor(data->pureColor); + destTexture->transferComplete(); + } + } else if (data->status == emptyItem || data->status == pendingDiscard) { + // The queue should be clear instead of setting to different status. + ALOGV("Warning: Don't expect an emptyItem here."); + } + } + m_pureColorTileQueue.clear(); +} + +// Call on UI thread to copy from the shared Surface Texture to the Tile's texture. +void TransferQueue::updateDirtyTiles() +{ + android::Mutex::Autolock lock(m_transferQueueItemLocks); + + cleanupPendingDiscard(); + if (!getHasGLContext()) + setHasGLContext(true); + + // Check the pure color tile first, since it is simpler. + updatePureColorTiles(); + + // Start from the oldest item, we call the updateTexImage to retrive + // the texture and blit that into each Tile's texture. + const int nextItemIndex = getNextTransferQueueIndex(); + int index = nextItemIndex; + bool usedFboForUpload = false; + for (int k = 0; k < m_transferQueueSize ; k++) { + if (m_transferQueue[index].status == pendingBlit) { + bool obsoleteTile = checkObsolete(&m_transferQueue[index]); + // Save the needed info, update the Surf Tex, clean up the item in + // the queue. Then either move on to next item or copy the content. + TileTexture* destTexture = 0; + TileTexture* frontTexture = 0; + if (!obsoleteTile) { + destTexture = m_transferQueue[index].savedTilePtr->backTexture(); + // while destTexture is guaranteed to not be null, frontTexture + // might be (first transfer) + frontTexture = m_transferQueue[index].savedTilePtr->frontTexture(); + } + + if (m_transferQueue[index].uploadType == GpuUpload) { + status_t result = m_sharedSurfaceTexture->updateTexImage(); + if (result != OK) + ALOGE("unexpected error: updateTexImage return %d", result); + } + m_transferQueue[index].savedTilePtr = 0; + m_transferQueue[index].status = emptyItem; + if (obsoleteTile) { + ALOGV("Warning: the texture is obsolete for this baseTile"); + index = (index + 1) % m_transferQueueSize; + continue; + } + + // guarantee that we have a texture to blit into + destTexture->requireGLTexture(); + + if (m_transferQueue[index].uploadType == CpuUpload) { + // Here we just need to upload the bitmap content to the GL Texture + GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, + *m_transferQueue[index].bitmap, + m_transferQueue[index].invalRect); + } else { + if (!usedFboForUpload) { + saveGLState(); + usedFboForUpload = true; + } + blitTileFromQueue(m_fboID, destTexture, frontTexture, + m_sharedSurfaceTextureId, + m_sharedSurfaceTexture->getCurrentTextureTarget(), + index); + } + + destTexture->setPure(false); + destTexture->transferComplete(); + + ALOGV("Blit tile x, y %d %d with dest texture %p to destTexture->m_ownTextureId %d", + m_transferQueue[index].savedTilePtr, + destTexture, + destTexture->m_ownTextureId); + } + index = (index + 1) % m_transferQueueSize; + } + + // Clean up FBO setup. Doing this for both CPU/GPU upload can make the + // dynamic switch possible. Moving this out from the loop can save some + // milli-seconds. + if (usedFboForUpload) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO + restoreGLState(); + GLUtils::checkGlError("updateDirtyTiles"); + } + + m_emptyItemCount = m_transferQueueSize; + m_transferQueueItemCond.signal(); +} + +void TransferQueue::updateQueueWithBitmap(const TileRenderInfo* renderInfo, + const SkBitmap& bitmap) +{ + if (!tryUpdateQueueWithBitmap(renderInfo, bitmap)) { + // failed placing bitmap in queue, discard tile's texture so it will be + // re-enqueued (and repainted) + Tile* tile = renderInfo->baseTile; + if (tile) + tile->backTextureTransferFail(); + } +} + +bool TransferQueue::tryUpdateQueueWithBitmap(const TileRenderInfo* renderInfo, + const SkBitmap& bitmap) +{ + // This lock need to cover the full update since it is possible that queue + // will be cleaned up in the middle of this update without the lock. + // The Surface Texture will not block us since the readyForUpdate will check + // availability of the slots in the queue first. + android::Mutex::Autolock lock(m_transferQueueItemLocks); + bool ready = readyForUpdate(); + TextureUploadType currentUploadType = m_currentUploadType; + if (!ready) { + ALOGV("Quit bitmap update: not ready! for tile x y %d %d", + renderInfo->x, renderInfo->y); + return false; + } + if (currentUploadType == GpuUpload) { + // a) Dequeue the Surface Texture and write into the buffer + if (!m_ANW.get()) { + ALOGV("ERROR: ANW is null"); + return false; + } + + if (!GLUtils::updateSharedSurfaceTextureWithBitmap(m_ANW.get(), bitmap)) + return false; + } + + // b) After update the Surface Texture, now udpate the transfer queue info. + addItemInTransferQueue(renderInfo, currentUploadType, &bitmap); + + ALOGV("Bitmap updated x, y %d %d, baseTile %p", + renderInfo->x, renderInfo->y, renderInfo->baseTile); + return true; +} + +void TransferQueue::addItemInPureColorQueue(const TileRenderInfo* renderInfo) +{ + // The pure color tiles' queue will be read from UI thread and written in + // Tex Gen thread, thus we need to have a lock here. + android::Mutex::Autolock lock(m_transferQueueItemLocks); + TileTransferData data; + addItemCommon(renderInfo, GpuUpload, &data); + data.pureColor = renderInfo->pureColor; + m_pureColorTileQueue.append(data); +} + +// Translates the info from TileRenderInfo and others to TileTransferData. +// This is used by pure color tiles and normal tiles. +void TransferQueue::addItemCommon(const TileRenderInfo* renderInfo, + TextureUploadType type, + TileTransferData* data) +{ + data->savedTileTexturePtr = renderInfo->baseTile->backTexture(); + data->savedTilePtr = renderInfo->baseTile; + data->status = pendingBlit; + data->uploadType = type; + + IntRect inval(0, 0, 0, 0); + if (renderInfo->invalRect) { + inval.setX(renderInfo->invalRect->fLeft); + inval.setY(renderInfo->invalRect->fTop); + inval.setWidth(renderInfo->invalRect->width()); + inval.setHeight(renderInfo->invalRect->height()); + } + data->invalRect = inval; +} + +// Note that there should be lock/unlock around this function call. +// Currently only called by GLUtils::updateSharedSurfaceTextureWithBitmap. +void TransferQueue::addItemInTransferQueue(const TileRenderInfo* renderInfo, + TextureUploadType type, + const SkBitmap* bitmap) +{ + m_transferQueueIndex = (m_transferQueueIndex + 1) % m_transferQueueSize; + + int index = m_transferQueueIndex; + if (m_transferQueue[index].savedTilePtr + || m_transferQueue[index].status != emptyItem) { + ALOGV("ERROR update a tile which is dirty already @ index %d", index); + } + + TileTransferData* data = &m_transferQueue[index]; + addItemCommon(renderInfo, type, data); + if (type == CpuUpload && bitmap) { + // Lazily create the bitmap + if (!m_transferQueue[index].bitmap) { + m_transferQueue[index].bitmap = new SkBitmap(); + int w = bitmap->width(); + int h = bitmap->height(); + m_transferQueue[index].bitmap->setConfig(bitmap->config(), w, h); + } + bitmap->copyTo(m_transferQueue[index].bitmap, bitmap->config()); + } + + m_emptyItemCount--; +} + +void TransferQueue::setTextureUploadType(TextureUploadType type) +{ + android::Mutex::Autolock lock(m_transferQueueItemLocks); + if (m_currentUploadType == type) + return; + + setPendingDiscard(); + + m_currentUploadType = type; + ALOGD("Now we set the upload to %s", m_currentUploadType == GpuUpload ? "GpuUpload" : "CpuUpload"); +} + +// Note: this need to be called within the lock and on the UI thread. +// Only called by updateDirtyTiles() and emptyQueue() for now +void TransferQueue::cleanupPendingDiscard() +{ + int index = getNextTransferQueueIndex(); + + for (int i = 0 ; i < m_transferQueueSize; i++) { + if (m_transferQueue[index].status == pendingDiscard) { + // No matter what the current upload type is, as long as there has + // been a Surf Tex enqueue operation, this updateTexImage need to + // be called to keep things in sync. + if (m_transferQueue[index].uploadType == GpuUpload) { + status_t result = m_sharedSurfaceTexture->updateTexImage(); + if (result != OK) + ALOGE("unexpected error: updateTexImage return %d", result); + } + + // since tiles in the queue may be from another webview, remove + // their textures so that they will be repainted / retransferred + Tile* tile = m_transferQueue[index].savedTilePtr; + TileTexture* texture = m_transferQueue[index].savedTileTexturePtr; + if (tile && texture && texture->owner() == tile) { + // since tile destruction removes textures on the UI thread, the + // texture->owner ptr guarantees the tile is valid + tile->discardBackTexture(); + ALOGV("transfer queue discarded tile %p, removed texture", tile); + } + + m_transferQueue[index].savedTilePtr = 0; + m_transferQueue[index].savedTileTexturePtr = 0; + m_transferQueue[index].status = emptyItem; + } + index = (index + 1) % m_transferQueueSize; + } +} + +void TransferQueue::saveGLState() +{ + glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport); + glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor); + glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth); +#ifdef DEBUG + glGetFloatv(GL_COLOR_CLEAR_VALUE, m_GLStateBeforeBlit.clearColor); +#endif +} + +void TransferQueue::setGLStateForCopy(int width, int height) +{ + // Need to match the texture size. + glViewport(0, 0, width, height); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DEPTH_TEST); + // Clear the content is only for debug purpose. +#ifdef DEBUG + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); +#endif +} + +void TransferQueue::restoreGLState() +{ + glViewport(m_GLStateBeforeBlit.viewport[0], + m_GLStateBeforeBlit.viewport[1], + m_GLStateBeforeBlit.viewport[2], + m_GLStateBeforeBlit.viewport[3]); + + if (m_GLStateBeforeBlit.scissor[0]) + glEnable(GL_SCISSOR_TEST); + + if (m_GLStateBeforeBlit.depth[0]) + glEnable(GL_DEPTH_TEST); +#ifdef DEBUG + glClearColor(m_GLStateBeforeBlit.clearColor[0], + m_GLStateBeforeBlit.clearColor[1], + m_GLStateBeforeBlit.clearColor[2], + m_GLStateBeforeBlit.clearColor[3]); +#endif +} + +int TransferQueue::getNextTransferQueueIndex() +{ + return (m_transferQueueIndex + 1) % m_transferQueueSize; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h new file mode 100644 index 0000000..65ff116 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h @@ -0,0 +1,234 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TransferQueue_h +#define TransferQueue_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GLUtils.h" +#include "ShaderProgram.h" +#include "SkBitmap.h" +#include <utils/StrongPointer.h> +#include <utils/threads.h> + +namespace WebCore { + +class Tile; +class TileTexture; + +struct GLState { + GLint viewport[4]; + GLboolean scissor[1]; + GLboolean depth[1]; + GLfloat clearColor[4]; +}; + + +// While in the queue, the Tile can be re-used, the updated bitmap +// can be discarded. In order to track this obsolete base tiles, we save +// the Tile's Info to make the comparison. +// At the time of base tile's dtor or webview destroy, we want to discard +// all the data in the queue. However, we have to do the Surface Texture +// update in the same GL context as the UI thread. So we mark the status +// as pendingDiscard, and delay the Surface Texture operation to the next +// draw call. + +enum TransferItemStatus { + emptyItem = 0, // S.T. buffer ready for new content + pendingBlit = 1, // Ready for bliting into tile's GL Tex. + pendingDiscard = 2 // Waiting for the next draw call to discard +}; + +enum TextureUploadType { + CpuUpload = 0, + GpuUpload = 1 +}; + +#define DEFAULT_UPLOAD_TYPE GpuUpload + +class TileTransferData { +public: + TileTransferData() + : status(emptyItem) + , savedTilePtr(0) + , savedTileTexturePtr(0) + , uploadType(DEFAULT_UPLOAD_TYPE) + , bitmap(0) + , m_syncKHR(EGL_NO_SYNC_KHR) + { + } + + ~TileTransferData() + { + // Bitmap will be created lazily, need to delete them at dtor. + delete bitmap; + } + + TransferItemStatus status; + Tile* savedTilePtr; + TileTexture* savedTileTexturePtr; + IntRect invalRect; + TextureUploadType uploadType; + // This is only useful in Cpu upload code path, so it will be dynamically + // lazily allocated. + SkBitmap* bitmap; + + // Specific data to the pure color tiles' queue. + Color pureColor; + + // Sync object for GPU fence, this is the only the info passed from UI + // thread to Tex Gen thread. The reason of having this is due to the + // missing sync mechanism on Surface Texture on some vendor. b/5122031. + // Bascially the idea is that when UI thread utilize one buffer from + // the surface texture, we'll need to kick off the GPU commands, and only + // when those particular commands finish, we could write into this buffer + // again in Tex Gen thread. + EGLSyncKHR m_syncKHR; +}; + +class TransferQueue { +public: + TransferQueue(bool useMinimalMem); + ~TransferQueue(); + + // This will be called by the browser through nativeSetProperty + void setTextureUploadType(TextureUploadType type); + void cleanupGLResources(); + void updateDirtyTiles(); + + void initGLResources(int width, int height); + + // insert the bitmap into the queue, mark the tile dirty if failing + void updateQueueWithBitmap(const TileRenderInfo* renderInfo, + const SkBitmap& bitmap); + + void addItemInTransferQueue(const TileRenderInfo* info, + TextureUploadType type, + const SkBitmap* bitmap); + // Check if the item @ index is ready for update. + // The lock will be done when returning true. + bool readyForUpdate(); + + void interruptTransferQueue(bool); + + void lockQueue() { m_transferQueueItemLocks.lock(); } + void unlockQueue() { m_transferQueueItemLocks.unlock(); } + + void addItemInPureColorQueue(const TileRenderInfo* renderInfo); + + void setPendingDiscardWithLock(); + void emptyQueue(); + + bool needsInit() { return !m_sharedSurfaceTextureId; } + // This queue can be accessed from UI and TexGen thread, therefore, we need + // a lock to protect its access + TileTransferData* m_transferQueue; + + android::sp<ANativeWindow> m_ANW; + + // EGL wrapper around m_ANW for use by the GaneshRenderer + EGLSurface m_eglSurface; + +private: + // return true if successfully inserted into queue + bool tryUpdateQueueWithBitmap(const TileRenderInfo* renderInfo, + const SkBitmap& bitmap); + bool getHasGLContext(); + void setHasGLContext(bool hasContext); + + int getNextTransferQueueIndex(); + + // Save and restore the GL State while switching from/to FBO. + void saveGLState(); + void setGLStateForCopy(int width, int height); + void restoreGLState(); + + // Check the current transfer queue item is obsolete or not. + bool checkObsolete(const TileTransferData* data); + + void setPendingDiscard(); + // Before each draw call and the blit operation, clean up all the + // pendingDiscard items. + void cleanupPendingDiscard(); + + void blitTileFromQueue(GLuint fboID, TileTexture* destTex, + TileTexture* frontTex, + GLuint srcTexId, GLenum srcTexTarget, + int index); + + void addItemCommon(const TileRenderInfo* renderInfo, + TextureUploadType type, TileTransferData* data); + + void updatePureColorTiles(); + // Note that the m_transferQueueIndex only changed in the TexGen thread + // where we are going to move on to update the next item in the queue. + int m_transferQueueIndex; + + GLuint m_fboID; // The FBO used for copy the SurfTex to each tile + + GLuint m_sharedSurfaceTextureId; + + // GLContext can be lost when WebView destroyed. + bool m_hasGLContext; + + GLState m_GLStateBeforeBlit; + android::sp<android::SurfaceTexture> m_sharedSurfaceTexture; + + int m_emptyItemCount; + + bool m_interruptedByRemovingOp; + + // We are using wait/signal to handle our own queue sync. + // First of all, if we don't have our own lock, then while WebView is + // destroyed, the UI thread will wait for the Tex Gen to get out from + // dequeue operation, which will not succeed. B/c at this moment, we + // already lost the GL Context. + // Now we maintain a counter, which is m_emptyItemCount. When this reach + // 0, then we need the Tex Gen thread to wait. UI thread can signal this + // wait after calling updateTexImage at the draw call , or after WebView + // is destroyed. + android::Mutex m_transferQueueItemLocks; + android::Condition m_transferQueueItemCond; + + EGLDisplay m_currentDisplay; + + // This should be GpuUpload for production, but for debug purpose or working + // around driver/HW issue, we can set it to CpuUpload. + TextureUploadType m_currentUploadType; + + // The non-pure-color tile are 1 to 1 mapping with Surface Texture which is + // resource limited. To get better performance, it is better to separate + // the pure color tile into another queue. + WTF::Vector<TileTransferData> m_pureColorTileQueue; + + // The number of items transfer queue can buffer up. + int m_transferQueueSize; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) +#endif // TransferQueue_h |