summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/chromium
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/chromium')
-rw-r--r--WebCore/platform/graphics/chromium/FontCacheLinux.cpp7
-rw-r--r--WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp14
-rw-r--r--WebCore/platform/graphics/chromium/LayerChromium.cpp88
-rw-r--r--WebCore/platform/graphics/chromium/LayerChromium.h17
-rw-r--r--WebCore/platform/graphics/chromium/LayerRendererChromium.cpp669
-rw-r--r--WebCore/platform/graphics/chromium/LayerRendererChromium.h85
6 files changed, 772 insertions, 108 deletions
diff --git a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp
index ececd13..53f4a52 100644
--- a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp
+++ b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp
@@ -128,9 +128,10 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD
break;
}
}
- // if we fall out of the loop, it's ok for name to still be 0
- }
- else { // convert the name to utf8
+ if (!name)
+ name = "";
+ } else {
+ // convert the name to utf8
s = family.string().utf8();
name = s.data();
}
diff --git a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp
index 259cc0c..095ded2 100644
--- a/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp
+++ b/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp
@@ -101,12 +101,6 @@ GraphicsLayerChromium::GraphicsLayerChromium(GraphicsLayerClient* client)
GraphicsLayerChromium::~GraphicsLayerChromium()
{
- // Clean up the Skia layer.
- if (m_layer)
- m_layer->removeFromSuperlayer();
-
- if (m_transformLayer)
- m_transformLayer->removeFromSuperlayer();
}
void GraphicsLayerChromium::setName(const String& inName)
@@ -485,6 +479,11 @@ void GraphicsLayerChromium::updateLayerPreserves3D()
void GraphicsLayerChromium::updateLayerDrawsContent()
{
+ // Since only layers that draw content have a valid context
+ // we need to call updateGraphicsContext() here to make sure one
+ // gets created.
+ m_layer->drawsContentUpdated();
+
if (m_drawsContent)
m_layer->setNeedsDisplay();
@@ -533,7 +532,8 @@ void GraphicsLayerChromium::updateContentsRect()
if (!m_contentsLayer)
return;
- m_contentsLayer->setPosition(FloatPoint(m_contentsRect.x(), m_contentsRect.y()));
+ // The position of the layer is the center of quad.
+ m_contentsLayer->setPosition(FloatPoint(m_contentsRect.x() + m_contentsRect.width() / 2, m_contentsRect.y() + m_contentsRect.height() / 2));
m_contentsLayer->setBounds(IntSize(m_contentsRect.width(), m_contentsRect.height()));
}
diff --git a/WebCore/platform/graphics/chromium/LayerChromium.cpp b/WebCore/platform/graphics/chromium/LayerChromium.cpp
index 05be15c..8fb28ef 100644
--- a/WebCore/platform/graphics/chromium/LayerChromium.cpp
+++ b/WebCore/platform/graphics/chromium/LayerChromium.cpp
@@ -55,9 +55,10 @@ LayerChromium::LayerChromium(LayerType type, GraphicsLayerChromium* owner)
, m_borderWidth(0)
, m_borderColor(0, 0, 0, 0)
, m_backgroundColor(0, 0, 0, 0)
+ , m_anchorPoint(0.5, 0.5)
, m_anchorPointZ(0)
, m_clearsContext(false)
- , m_doubleSided(false)
+ , m_doubleSided(true)
, m_edgeAntialiasingMask(0)
, m_hidden(false)
, m_masksToBounds(false)
@@ -69,9 +70,10 @@ LayerChromium::LayerChromium(LayerType type, GraphicsLayerChromium* owner)
, m_skiaContext(0)
, m_graphicsContext(0)
, m_geometryFlipped(false)
+ , m_contentsDirty(false)
, m_contents(0)
+ , m_hasContext(false)
{
- updateGraphicsContext(m_backingStoreRect);
}
LayerChromium::~LayerChromium()
@@ -79,14 +81,30 @@ LayerChromium::~LayerChromium()
// Our superlayer should be holding a reference to us so there should be no
// way for us to be destroyed while we still have a superlayer.
ASSERT(!superlayer());
+
+ // Remove the superlayer reference from all sublayers.
+ removeAllSublayers();
}
-void LayerChromium::updateGraphicsContext(const IntSize& size)
+void LayerChromium::updateGraphicsContext()
{
+ // If the layer doesn't draw anything (e.g. it's a container layer) then we
+ // don't create a canvas / context for it. The root layer is a special
+ // case as even if it's marked as a container layer it does actually have
+ // content that it draws.
+ RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client());
+ if (!drawsContent() && !(this == rootLayer())) {
+ m_graphicsContext.clear();
+ m_skiaContext.clear();
+ m_canvas.clear();
+ m_hasContext = false;
+ return;
+ }
+
#if PLATFORM(SKIA)
// Create new canvas and context. OwnPtr takes care of freeing up
// the old ones.
- m_canvas = new skia::PlatformCanvas(size.width(), size.height(), false);
+ m_canvas = new skia::PlatformCanvas(m_backingStoreSize.width(), m_backingStoreSize.height(), false);
m_skiaContext = new PlatformContextSkia(m_canvas.get());
// This is needed to get text to show up correctly. Without it,
@@ -95,31 +113,32 @@ void LayerChromium::updateGraphicsContext(const IntSize& size)
m_skiaContext->setDrawingToImageBuffer(true);
m_graphicsContext = new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(m_skiaContext.get()));
+
+ m_hasContext = true;
+ m_contentsDirty = true;
#else
#error "Need to implement for your platform."
#endif
- // The backing store allocated for a layer can be smaller than the layer's bounds.
- // This is mostly true for the root layer whose backing store is sized based on the visible
- // portion of the layer rather than the actual page size.
- m_backingStoreRect = size;
+
+ return;
+}
+
+void LayerChromium::drawsContentUpdated()
+{
+ // Create a drawing context if the layer now draws content
+ // or delete the existing context if the layer doesn't draw
+ // content anymore.
+ updateGraphicsContext();
}
void LayerChromium::updateContents()
{
RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client());
- if (backing && !backing->paintingGoesToWindow())
+ if (backing && !backing->paintingGoesToWindow() && drawsContent())
m_owner->paintGraphicsLayerContents(*m_graphicsContext, IntRect(0, 0, m_bounds.width(), m_bounds.height()));
-}
-void LayerChromium::drawDebugBorder()
-{
- m_graphicsContext->setStrokeColor(m_borderColor, DeviceColorSpace);
- m_graphicsContext->setStrokeThickness(m_borderWidth);
- m_graphicsContext->drawLine(IntPoint(0, 0), IntPoint(m_bounds.width(), 0));
- m_graphicsContext->drawLine(IntPoint(0, 0), IntPoint(0, m_bounds.height()));
- m_graphicsContext->drawLine(IntPoint(m_bounds.width(), 0), IntPoint(m_bounds.width(), m_bounds.height()));
- m_graphicsContext->drawLine(IntPoint(0, m_bounds.height()), IntPoint(m_bounds.width(), m_bounds.height()));
+ m_contentsDirty = false;
}
void LayerChromium::setContents(NativeImagePtr contents)
@@ -128,6 +147,7 @@ void LayerChromium::setContents(NativeImagePtr contents)
if (m_contents == contents)
return;
m_contents = contents;
+ m_contentsDirty = true;
}
void LayerChromium::setNeedsCommit()
@@ -201,28 +221,29 @@ int LayerChromium::indexOfSublayer(const LayerChromium* reference)
return -1;
}
-void LayerChromium::setBackingStoreRect(const IntSize& rect)
+// This method can be called to overide the size of the backing store
+// used for the layer. It's typically called on the root layer to limit
+// its size to the actual visible size.
+void LayerChromium::setBackingStoreSize(const IntSize& size)
{
- if (m_backingStoreRect == rect)
+ if (m_backingStoreSize == size)
return;
- updateGraphicsContext(rect);
+ m_backingStoreSize = size;
+ updateGraphicsContext();
+ setNeedsCommit();
}
-void LayerChromium::setBounds(const IntSize& rect)
+void LayerChromium::setBounds(const IntSize& size)
{
- if (rect == m_bounds)
+ if (m_bounds == size)
return;
- m_bounds = rect;
+ m_bounds = size;
+ m_backingStoreSize = size;
// Re-create the canvas and associated contexts.
- updateGraphicsContext(m_bounds);
-
- // Layer contents need to be redrawn as the backing surface
- // was recreated above.
- updateContents();
-
+ updateGraphicsContext();
setNeedsCommit();
}
@@ -270,8 +291,11 @@ LayerChromium* LayerChromium::superlayer() const
void LayerChromium::setNeedsDisplay(const FloatRect& dirtyRect)
{
- // Redraw the contents of the layer.
- updateContents();
+ // Simply mark the contents as dirty. The actual redraw will
+ // happen when it's time to do the compositing.
+ // FIXME: Should only update the dirty rect instead of marking
+ // the entire layer dirty.
+ m_contentsDirty = true;
setNeedsCommit();
}
diff --git a/WebCore/platform/graphics/chromium/LayerChromium.h b/WebCore/platform/graphics/chromium/LayerChromium.h
index 5b93f77..1b807a5 100644
--- a/WebCore/platform/graphics/chromium/LayerChromium.h
+++ b/WebCore/platform/graphics/chromium/LayerChromium.h
@@ -151,6 +151,7 @@ public:
bool geometryFlipped() const { return m_geometryFlipped; }
void updateContents();
+ bool contentsDirty() { return m_contentsDirty; }
void setContents(NativeImagePtr contents);
NativeImagePtr contents() const { return m_contents; }
@@ -158,9 +159,13 @@ public:
skia::PlatformCanvas* platformCanvas() { return m_canvas.get(); }
GraphicsContext* graphicsContext() { return m_graphicsContext.get(); }
- void setBackingStoreRect(const IntSize&);
+ void setBackingStoreSize(const IntSize&);
- void drawDebugBorder();
+ bool drawsContent() { return m_owner && m_owner->drawsContent(); }
+
+ // This method should be called every time the status drawsContent()
+ // status changes to ensure that the internal graphics context is in sync.
+ void drawsContentUpdated();
private:
LayerChromium(LayerType, GraphicsLayerChromium* owner);
@@ -184,7 +189,8 @@ private:
// Re-creates the canvas and graphics context. This method
// must be called every time the layer is resized. Only layers
- void updateGraphicsContext(const IntSize&);
+ // that do drawing and the root layer get a context.
+ void updateGraphicsContext();
Vector<RefPtr<LayerChromium> > m_sublayers;
LayerChromium* m_superlayer;
@@ -195,11 +201,12 @@ private:
OwnPtr<PlatformContextSkia> m_skiaContext;
OwnPtr<GraphicsContext> m_graphicsContext;
#endif
+ bool m_hasContext;
LayerType m_layerType;
IntSize m_bounds;
- IntSize m_backingStoreRect;
+ IntSize m_backingStoreSize;
FloatPoint m_position;
FloatPoint m_anchorPoint;
Color m_backgroundColor;
@@ -223,6 +230,8 @@ private:
bool m_geometryFlipped;
bool m_needsDisplayOnBoundsChange;
+ bool m_contentsDirty;
+
ContentsGravityType m_contentsGravity;
NativeImagePtr m_contents;
String m_name;
diff --git a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp
index 722c80c..fbdb9e2 100644
--- a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp
+++ b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp
@@ -34,105 +34,682 @@
#if USE(ACCELERATED_COMPOSITING)
#include "LayerRendererChromium.h"
+#include "GLES2Context.h"
#include "LayerChromium.h"
+#include "NotImplemented.h"
+#include "Page.h"
+#if PLATFORM(SKIA)
+#include "NativeImageSkia.h"
#include "PlatformContextSkia.h"
-#include "skia/ext/platform_canvas.h"
+#endif
+
+#include <GLES2/gl2.h>
namespace WebCore {
-PassOwnPtr<LayerRendererChromium> LayerRendererChromium::create()
+static WTFLogChannel LogLayerRenderer = { 0x00000000, "LayerRenderer", WTFLogChannelOn };
+
+static void checkGLError()
+{
+#ifndef NDEBUG
+ GLenum error = glGetError();
+ if (error)
+ LOG_ERROR("GL Error: %d " , error);
+#endif
+}
+
+static GLuint loadShader(GLenum type, const char* shaderSource)
+{
+ GLuint shader = glCreateShader(type);
+ if (!shader)
+ return 0;
+ glShaderSource(shader, 1, &shaderSource, 0);
+ glCompileShader(shader);
+ GLint compiled;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ glDeleteShader(shader);
+ return 0;
+ }
+ return shader;
+}
+
+static GLuint loadShaderProgram(const char* vertexShaderSource, const char* fragmentShaderSource)
+{
+ GLuint vertexShader;
+ GLuint fragmentShader;
+ GLuint programObject;
+ GLint linked;
+ vertexShader = loadShader(GL_VERTEX_SHADER, vertexShaderSource);
+ if (!vertexShader)
+ return 0;
+ fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
+ if (!fragmentShader) {
+ glDeleteShader(vertexShader);
+ return 0;
+ }
+ programObject = glCreateProgram();
+ if (!programObject)
+ return 0;
+ glAttachShader(programObject, vertexShader);
+ glAttachShader(programObject, fragmentShader);
+ glLinkProgram(programObject);
+ glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
+ if (!linked) {
+ glDeleteProgram(programObject);
+ return 0;
+ }
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ return programObject;
+}
+
+static void toGLMatrix(float* flattened, const TransformationMatrix& m)
+{
+ flattened[0] = m.m11();
+ flattened[1] = m.m12();
+ flattened[2] = m.m13();
+ flattened[3] = m.m14();
+ flattened[4] = m.m21();
+ flattened[5] = m.m22();
+ flattened[6] = m.m23();
+ flattened[7] = m.m24();
+ flattened[8] = m.m31();
+ flattened[9] = m.m32();
+ flattened[10] = m.m33();
+ flattened[11] = m.m34();
+ flattened[12] = m.m41();
+ flattened[13] = m.m42();
+ flattened[14] = m.m43();
+ flattened[15] = m.m44();
+}
+
+static TransformationMatrix orthoMatrix(float left, float right, float bottom, float top, float nearZ, float farZ)
{
- return new LayerRendererChromium();
+ float deltaX = right - left;
+ float deltaY = top - bottom;
+ float deltaZ = farZ - nearZ;
+ TransformationMatrix ortho;
+ if (!deltaX || !deltaY || !deltaZ)
+ return ortho;
+ 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);
+ return ortho;
}
-LayerRendererChromium::LayerRendererChromium()
+// Creates a GL texture object to be used for transfering the layer's bitmap into.
+static GLuint createLayerTexture()
+{
+ GLuint textureId = 0;
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ // Do basic linear filtering on resize.
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ // NPOT textures in GL ES only work when the wrap mode is set to GL_CLAMP_TO_EDGE.
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ return textureId;
+}
+
+
+PassOwnPtr<LayerRendererChromium> LayerRendererChromium::create(Page* page)
+{
+ return new LayerRendererChromium(page);
+}
+
+LayerRendererChromium::LayerRendererChromium(Page* page)
: m_rootLayer(0)
, m_needsDisplay(false)
+ , m_layerProgramObject(0)
+ , m_borderProgramObject(0)
+ , m_scrollProgramObject(0)
+ , m_positionLocation(0)
+ , m_texCoordLocation(1)
+ , m_page(page)
+ , m_rootLayerTextureWidth(0)
+ , m_rootLayerTextureHeight(0)
{
+ m_quadVboIds[Vertices] = m_quadVboIds[LayerElements] = 0;
+ m_hardwareCompositing = (initGL() && initializeSharedGLObjects());
}
LayerRendererChromium::~LayerRendererChromium()
{
+ if (m_hardwareCompositing) {
+ makeContextCurrent();
+ glDeleteBuffers(3, m_quadVboIds);
+ glDeleteProgram(m_layerProgramObject);
+ glDeleteProgram(m_scrollProgramObject);
+ glDeleteProgram(m_borderProgramObject);
+ }
}
-void LayerRendererChromium::updateLayerContents()
+void LayerRendererChromium::drawTexturedQuad(const TransformationMatrix& matrix, float width, float height, float opacity, bool scrolling)
{
- if (m_rootLayer)
- updateLayerContentsRecursive(m_rootLayer.get());
+ static GLfloat glMatrix[16];
+
+ TransformationMatrix renderMatrix = matrix;
+
+ // Apply a scaling factor to size the quad from 1x1 to its intended size.
+ renderMatrix.scale3d(width, height, 1);
+
+ // Apply the projection matrix before sending the transform over to the shader.
+ renderMatrix.multiply(m_projectionMatrix);
+
+ toGLMatrix(&glMatrix[0], renderMatrix);
+
+ int matrixLocation = (scrolling ? m_scrollMatrixLocation : m_matrixLocation);
+ glUniformMatrix4fv(matrixLocation, 1, false, &glMatrix[0]);
+
+ if (!scrolling)
+ glUniform1f(m_alphaLocation, opacity);
+
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
}
-#if PLATFORM(SKIA)
-void LayerRendererChromium::drawLayersInCanvas(skia::PlatformCanvas* canvas, const IntRect& clipRect)
+
+// Updates the contents of the root layer texture that fall inside the updateRect
+// and re-composits all sublayers.
+void LayerRendererChromium::drawLayers(const IntRect& updateRect, const IntRect& visibleRect,
+ const IntRect& contentRect, const IntPoint& scrollPosition)
{
+ ASSERT(m_hardwareCompositing);
+
if (!m_rootLayer)
return;
- canvas->save();
- canvas->clipRect(SkRect(clipRect));
+ // If the size of the visible area has changed then allocate a new texture
+ // to store the contents of the root layer and adjust the projection matrix
+ // and viewport.
+ makeContextCurrent();
+
+ glBindTexture(GL_TEXTURE_2D, m_rootLayerTextureId);
+
+ unsigned int visibleRectWidth = visibleRect.width();
+ unsigned int visibleRectHeight = visibleRect.height();
+ if (visibleRectWidth != m_rootLayerTextureWidth || visibleRectHeight != m_rootLayerTextureHeight) {
+ m_rootLayerTextureWidth = visibleRect.width();
+ m_rootLayerTextureHeight = visibleRect.height();
+
+ m_projectionMatrix = orthoMatrix(0, visibleRectWidth + 0.5, visibleRectHeight + 0.5, 0, -1000, 1000);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_rootLayerTextureWidth, m_rootLayerTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ }
+
+ // The GL viewport covers the entire visible area, including the scrollbars.
+ glViewport(0, 0, visibleRectWidth, visibleRectHeight);
+
+ // The layer, scroll and debug border shaders all use the same vertex attributes
+ // so we can bind them only once.
+ glBindBuffer(GL_ARRAY_BUFFER, m_quadVboIds[Vertices]);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_quadVboIds[LayerElements]);
+ GLuint offset = 0;
+ glVertexAttribPointer(m_positionLocation, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(offset));
+ offset += 3 * sizeof(GLfloat);
+ glVertexAttribPointer(m_texCoordLocation, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(offset));
+ glEnableVertexAttribArray(m_positionLocation);
+ glEnableVertexAttribArray(m_texCoordLocation);
+ glActiveTexture(GL_TEXTURE0);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
- // First composite the root layer into the canvas.
- canvas->drawBitmap(m_rootLayer->platformCanvas()->getDevice()->accessBitmap(false), 0, 0, 0);
+ IntPoint scrollDelta = toPoint(scrollPosition - m_scrollPosition);
+ // Scroll only when the updateRect contains pixels for the newly uncovered region to avoid flashing.
+ if ((scrollDelta.x() && updateRect.width() >= abs(scrollDelta.x()) && updateRect.height() >= contentRect.height())
+ || (scrollDelta.y() && updateRect.height() >= abs(scrollDelta.y()) && updateRect.width() >= contentRect.width())) {
+ // Scrolling works as follows: We render a quad with the current root layer contents
+ // translated by the amount the page has scrolled since the last update and then read the
+ // pixels of the content area (visible area excluding the scroll bars) back into the
+ // root layer texture. The newly exposed area is subesquently filled as usual with
+ // the contents of the updateRect.
+ TransformationMatrix scrolledLayerMatrix;
+ scrolledLayerMatrix.translate3d((int)floorf(0.5 * visibleRect.width() + 0.5) - scrollDelta.x(),
+ (int)floorf(0.5 * visibleRect.height() + 0.5) + scrollDelta.y(), 0);
+ scrolledLayerMatrix.scale3d(1, -1, 1);
+
+ // Switch shaders to avoid RGB swizzling.
+ glUseProgram(m_scrollProgramObject);
+ glUniform1i(m_scrollSamplerLocation, 0);
+
+ drawTexturedQuad(scrolledLayerMatrix, visibleRect.width(), visibleRect.height(), 1, true);
+
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, contentRect.width(), contentRect.height());
+
+ checkGLError();
+ m_scrollPosition = scrollPosition;
+ }
+
+ // FIXME: The following check should go away when the compositor renders independently from its own thread.
+ // Ignore a 1x1 update rect at (0, 0) as that's used a way to kick off a redraw for the compositor.
+ if (!(!updateRect.x() && !updateRect.y() && updateRect.width() == 1 && updateRect.height() == 1)) {
+ // Update the root layer texture.
+ ASSERT((updateRect.x() + updateRect.width() <= m_rootLayerTextureWidth)
+ && (updateRect.y() + updateRect.height() <= m_rootLayerTextureHeight));
+
+#if PLATFORM(SKIA)
+ // Get the contents of the updated rect.
+ const SkBitmap bitmap = m_rootLayer->platformCanvas()->getDevice()->accessBitmap(false);
+ int rootLayerWidth = bitmap.width();
+ int rootLayerHeight = bitmap.height();
+ ASSERT(rootLayerWidth == updateRect.width() && rootLayerHeight == updateRect.height());
+ void* pixels = bitmap.getPixels();
- // Account for the scroll offset before compositing the remaining layers.
- // Note that the root layer's painting takes into account the scroll offset already.
- canvas->translate(-m_scrollFrame.fLeft, -m_scrollFrame.fTop);
+ checkGLError();
+ // Copy the contents of the updated rect to the root layer texture.
+ glTexSubImage2D(GL_TEXTURE_2D, 0, updateRect.x(), updateRect.y(), updateRect.width(), updateRect.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ checkGLError();
+#else
+#error Must port to your platform
+#endif
+ }
- float opacity = 1.0f;
+ glClearColor(0, 0, 1, 1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // Render the root layer using a quad that takes up the entire visible area of the window.
+ glUseProgram(m_layerProgramObject);
+ glUniform1i(m_samplerLocation, 0);
+ TransformationMatrix layerMatrix;
+ layerMatrix.translate3d(visibleRect.width() / 2, visibleRect.height() / 2, 0);
+ drawTexturedQuad(layerMatrix, visibleRect.width(), visibleRect.height(), 1, false);
+
+ // If culling is enabled then we will cull the backface.
+ glCullFace(GL_BACK);
+ // The orthographic projection is setup such that Y starts at zero and
+ // increases going down the page so we need to adjust the winding order of
+ // front facing triangles.
+ glFrontFace(GL_CW);
+
+ // The shader used to render layers returns pre-multiplied alpha colors
+ // so we need to send the blending mode appropriately.
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ checkGLError();
+
+ // FIXME: Need to prevent composited layers from drawing over the scroll
+ // bars.
+
+ // FIXME: Sublayers need to be sorted in Z to get the correct transparency effect.
+
+ // Translate all the composited layers by the scroll position.
+ TransformationMatrix matrix;
+ matrix.translate3d(-m_scrollPosition.x(), -m_scrollPosition.y(), 0);
+ float opacity = 1;
const Vector<RefPtr<LayerChromium> >& sublayers = m_rootLayer->getSublayers();
for (size_t i = 0; i < sublayers.size(); i++)
- drawLayerInCanvasRecursive(canvas, sublayers[i].get(), opacity);
+ compositeLayersRecursive(sublayers[i].get(), matrix, opacity, visibleRect);
- canvas->restore();
+ glFlush();
+ m_gles2Context->swapBuffers();
m_needsDisplay = false;
}
-void LayerRendererChromium::drawLayerInCanvasRecursive(skia::PlatformCanvas* canvas, LayerChromium* layer, float opacity)
+// Returns the id of the texture currently associated with the layer or
+// -1 if the id hasn't been registered yet.
+int LayerRendererChromium::getTextureId(LayerChromium* layer)
{
- // Guarantees that the canvas is restored to a known state on destruction.
- SkAutoCanvasRestore autoRestoreCanvas(canvas, true);
+ TextureIdMap::iterator textureId = m_textureIdMap.find(layer);
+ if (textureId != m_textureIdMap.end())
+ return textureId->second;
- FloatPoint position = layer->position();
- FloatPoint anchorPoint = layer->anchorPoint();
- SkMatrix transform = layer->transform().toAffineTransform();
+ return -1;
+}
+
+// Allocates a new texture for the layer and registers it in the textureId map.
+// FIXME: We will need to come up with a more sophisticated allocation strategy here.
+// FIXME: We need to free up the associated texture upon layer destruction.
+int LayerRendererChromium::assignTextureForLayer(LayerChromium* layer)
+{
+ GLuint textureId = createLayerTexture();
+
+ // FIXME: Check that textureId is valid
+ m_textureIdMap.set(layer, textureId);
+ return textureId;
+}
+
+bool LayerRendererChromium::freeLayerTexture(LayerChromium* layer)
+{
+ TextureIdMap::iterator textureId = m_textureIdMap.find(layer);
+ if (textureId == m_textureIdMap.end())
+ return false;
+ // Free up the texture.
+ glDeleteTextures(1, &(textureId->second));
+ m_textureIdMap.remove(textureId);
+ return true;
+}
+
+// Draws a debug border around the layer's bounds.
+void LayerRendererChromium::drawDebugBorder(LayerChromium* layer, const TransformationMatrix& matrix)
+{
+ static GLfloat glMatrix[16];
+ Color borderColor = layer->borderColor();
+ if (!borderColor.alpha())
+ return;
+
+ glUseProgram(m_borderProgramObject);
+ TransformationMatrix renderMatrix = matrix;
IntSize bounds = layer->bounds();
+ renderMatrix.scale3d(bounds.width(), bounds.height(), 1);
+ renderMatrix.multiply(m_projectionMatrix);
+ toGLMatrix(&glMatrix[0], renderMatrix);
+ glUniformMatrix4fv(m_borderMatrixLocation, 1, false, &glMatrix[0]);
- canvas->translate(position.x(), position.y());
+ glUniform4f(m_borderColorLocation, borderColor.red() / 255.0,
+ borderColor.green() / 255.0,
+ borderColor.blue() / 255.0,
+ 1);
- SkScalar tx = SkScalarMul(anchorPoint.x(), bounds.width());
- SkScalar ty = SkScalarMul(anchorPoint.y(), bounds.height());
- canvas->translate(tx, ty);
- canvas->concat(transform);
- canvas->translate(-tx, -ty);
+ glLineWidth(layer->borderWidth());
- // The position we get is for the center of the layer, but
- // drawBitmap starts at the upper-left corner, and therefore
- // we need to adjust our transform.
- canvas->translate(-0.5f * bounds.width(), -0.5f * bounds.height());
+ // The indices for the line are stored in the same array as the triangle indices.
+ glDrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, (void*)(6 * sizeof(unsigned short)));
+ checkGLError();
- layer->drawDebugBorder();
+ // Switch back to the shader program used for layer contents.
+ glUseProgram(m_layerProgramObject);
+}
+
+// Returns true if any part of the layer falls within the visibleRect
+bool LayerRendererChromium::isLayerVisible(LayerChromium* layer, const TransformationMatrix& matrix, const IntRect& visibleRect)
+{
+ // Form the matrix used by the shader to map the corners of the layer's
+ // bounds into clip space.
+ TransformationMatrix renderMatrix = matrix;
+ renderMatrix.scale3d(layer->bounds().width(), layer->bounds().height(), 1);
+ renderMatrix.multiply(m_projectionMatrix);
+
+ FloatRect layerRect(-0.5, -0.5, 1, 1);
+ FloatRect mappedRect = renderMatrix.mapRect(layerRect);
+
+ // The layer is visible if it intersects any part of a rectangle whose origin
+ // is at (-1, -1) and size is 2x2.
+ return mappedRect.intersects(FloatRect(-1, -1, 2, 2));
+}
+
+void LayerRendererChromium::compositeLayersRecursive(LayerChromium* layer, const TransformationMatrix& matrix, float opacity, const IntRect& visibleRect)
+{
+ static GLfloat glMatrix[16];
- SkPaint opacityPaint;
+ // Compute the new matrix transformation that will be applied to this layer and
+ // all its sublayers.
+ // The basic transformation chain for the layer is (using the Matrix x Vector order):
+ // M = M[p] * T[l] * T[a] * M[l] * T[-a]
+ // Where M[p] is the parent matrix passed down to the function
+ // T[l] is the translation of the layer's center
+ // T[a] and T[-a] is a translation/inverse translation by the anchor point
+ // M[l] is the layer's matrix
+ // Note that the final matrix used by the shader for the layer is P * M * S . This final product
+ // is effectively computed in drawTexturedQuad().
+ // Where: P is the projection matrix
+ // M is the layer's matrix computed above
+ // S is the scale adjustment (to scale up to the layer size)
+ IntSize bounds = layer->bounds();
+ FloatPoint anchorPoint = layer->anchorPoint();
+ FloatPoint position = layer->position();
+ float anchorX = (anchorPoint.x() - 0.5) * bounds.width();
+ float anchorY = (0.5 - anchorPoint.y()) * bounds.height();
+
+ // M = M[p]
+ TransformationMatrix localMatrix = matrix;
+ // M = M[p] * T[l]
+ localMatrix.translate3d(position.x(), position.y(), 0);
+ // M = M[p] * T[l] * T[a]
+ localMatrix.translate3d(anchorX, anchorY, 0);
+ // M = M[p] * T[l] * T[a] * M[l]
+ localMatrix.multLeft(layer->transform());
+ // M = M[p] * T[l] * T[a] * M[l] * T[-a]
+ localMatrix.translate3d(-anchorX, -anchorY, 0);
+
+ bool skipLayer = false;
+ if (bounds.width() > 2048 || bounds.height() > 2048) {
+ LOG(LayerRenderer, "Skipping layer with size %d %d", bounds.width(), bounds.height());
+ skipLayer = true;
+ }
+
+ // Calculate the layer's opacity.
opacity *= layer->opacity();
- opacityPaint.setAlpha(opacity * 255);
- canvas->drawBitmap(layer->platformCanvas()->getDevice()->accessBitmap(false), 0, 0, &opacityPaint);
+ bool layerVisible = isLayerVisible(layer, localMatrix, visibleRect);
+
+ // Note that there are two types of layers:
+ // 1. Layers that have their own GraphicsContext and can draw their contents on demand (layer->drawsContent() == true).
+ // 2. Layers that are just containers of images/video/etc that don't own a GraphicsContext (layer->contents() == true).
+ if ((layer->drawsContent() || layer->contents()) && !skipLayer && layerVisible) {
+ int textureId = getTextureId(layer);
+ // If no texture has been created for the layer yet then create one now.
+ if (textureId == -1)
+ textureId = assignTextureForLayer(layer);
+
+ // Redraw the contents of the layer if necessary.
+ if ((layer->drawsContent() || layer->contents()) && layer->contentsDirty()) {
+ // Update the contents of the layer before taking a snapshot. For layers that
+ // are simply containers, the following call just clears the dirty flag but doesn't
+ // actually do any draws/copies.
+ layer->updateContents();
+
+ const SkBitmap* skiaBitmap = 0;
+ void* pixels = 0;
+ if (layer->drawsContent()) { // Layer has its own GraphicsContext.
+ // The contents of the layer are stored in the canvas associated with it.
+ const SkBitmap& bitmap = layer->platformCanvas()->getDevice()->accessBitmap(false);
+ skiaBitmap = &bitmap;
+ } else { // Layer is a container.
+ // The layer contains an Image.
+ NativeImageSkia* skiaImage = static_cast<NativeImageSkia*>(layer->contents());
+ skiaBitmap = skiaImage;
+ }
+
+ ASSERT(skiaBitmap);
+ SkBitmap::Config skiaConfig = skiaBitmap->config();
+ // FIXME: must support more image configurations.
+ if (skiaConfig == SkBitmap::kARGB_8888_Config) {
+ SkAutoLockPixels lock(*skiaBitmap);
+ int bitmapWidth = skiaBitmap->width();
+ int bitmapHeight = skiaBitmap->height();
+ int rowBytes = skiaBitmap->rowBytes();
+ ASSERT(rowBytes == bitmapWidth * 4);
+
+ // Copy the layer contents into the texture.
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ void* pixels = skiaBitmap->getPixels();
+ if (pixels) {
+ // FIXME. We can be smart here and call glTexSubImage2D if the new bitmap has the same
+ // size as the old one which will save us one unecessary allocation / deallocation.
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmapWidth, bitmapHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ checkGLError();
+ }
+ }
+ }
+
+ if (layer->doubleSided())
+ glDisable(GL_CULL_FACE);
+ else
+ glEnable(GL_CULL_FACE);
+
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ drawTexturedQuad(localMatrix, bounds.width(), bounds.height(), opacity, false);
+ }
+ // Draw the debug border if there is one.
+ drawDebugBorder(layer, localMatrix);
+
+ // Apply the sublayer transform.
+ localMatrix.multLeft(layer->sublayerTransform());
+
+ // The origin of the sublayers is actually the left top corner of the layer
+ // instead of the center. The matrix passed down to the sublayers is therefore:
+ // M[s] = M * T[-center]
+ localMatrix.translate3d(-bounds.width() * 0.5, -bounds.height() * 0.5, 0);
const Vector<RefPtr<LayerChromium> >& sublayers = layer->getSublayers();
for (size_t i = 0; i < sublayers.size(); i++)
- drawLayerInCanvasRecursive(canvas, sublayers[i].get(), opacity);
+ compositeLayersRecursive(sublayers[i].get(), localMatrix, opacity, visibleRect);
}
-#endif // PLATFORM(SKIA)
-void LayerRendererChromium::updateLayerContentsRecursive(LayerChromium* layer)
+bool LayerRendererChromium::makeContextCurrent()
{
- layer->updateContents();
+ return m_gles2Context->makeCurrent();
+}
- const Vector<RefPtr<LayerChromium> >& sublayers = layer->getSublayers();
- for (size_t i = 0; i < sublayers.size(); i++)
- updateLayerContentsRecursive(sublayers[i].get());
+bool LayerRendererChromium::initGL()
+{
+ m_gles2Context = GLES2Context::create(m_page);
+
+ if (!m_gles2Context)
+ return false;
+
+ return true;
}
+// Binds the given attribute name to a common location across all three programs
+// used by the compositor. This allows the code to bind the attributes only once
+// even when switching between programs.
+void LayerRendererChromium::bindCommonAttribLocation(int location, char* attribName)
+{
+ glBindAttribLocation(m_layerProgramObject, location, attribName);
+ glBindAttribLocation(m_borderProgramObject, location, attribName);
+ glBindAttribLocation(m_scrollProgramObject, location, attribName);
+}
+
+bool LayerRendererChromium::initializeSharedGLObjects()
+{
+ // Shaders for drawing the layer contents.
+ char vertexShaderString[] =
+ "attribute vec4 a_position; \n"
+ "attribute vec2 a_texCoord; \n"
+ "uniform mat4 matrix; \n"
+ "varying vec2 v_texCoord; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = matrix * a_position; \n"
+ " v_texCoord = a_texCoord; \n"
+ "} \n";
+ char fragmentShaderString[] =
+ // FIXME: Re-introduce precision qualifier when we need GL ES shaders.
+ "//precision mediump float; \n"
+ "varying vec2 v_texCoord; \n"
+ "uniform sampler2D s_texture; \n"
+ "uniform float alpha; \n"
+ "void main() \n"
+ "{ \n"
+ " vec4 texColor = texture2D(s_texture, v_texCoord); \n"
+ " gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w) * alpha; \n"
+ "} \n";
+
+ // Fragment shader used for rendering the scrolled root layer quad. It differs
+ // from fragmentShaderString in that it doesn't swizzle the colors and doesn't
+ // take an alpha value.
+ char scrollFragmentShaderString[] =
+ // FIXME: Re-introduce precision qualifier when we need GL ES shaders.
+ "//precision mediump float; \n"
+ "varying vec2 v_texCoord; \n"
+ "uniform sampler2D s_texture; \n"
+ "void main() \n"
+ "{ \n"
+ " vec4 texColor = texture2D(s_texture, v_texCoord); \n"
+ " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w); \n"
+ "} \n";
+
+ // Shaders for drawing the debug borders around the layers.
+ char borderVertexShaderString[] =
+ "attribute vec4 a_position; \n"
+ "uniform mat4 matrix; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = matrix * a_position; \n"
+ "} \n";
+ char borderFragmentShaderString[] =
+ // FIXME: Re-introduce precision qualifier when we need GL ES shaders.
+ "//precision mediump float; \n"
+ "uniform vec4 color; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = color; \n"
+ "} \n";
+
+ GLfloat vertices[] = { -0.5f, 0.5f, 0.0f, // Position 0
+ 0.0f, 1.0f, // TexCoord 0
+ -0.5f, -0.5f, 0.0f, // Position 1
+ 0.0f, 0.0f, // TexCoord 1
+ 0.5f, -0.5f, 0.0f, // Position 2
+ 1.0f, 0.0f, // TexCoord 2
+ 0.5f, 0.5f, 0.0f, // Position 3
+ 1.0f, 1.0f // TexCoord 3
+ };
+ GLushort indices[] = { 0, 1, 2, 0, 2, 3, // The two triangles that make up the layer quad.
+ 0, 1, 2, 3}; // A line path for drawing the layer border.
+
+ makeContextCurrent();
+ m_layerProgramObject = loadShaderProgram(vertexShaderString, fragmentShaderString);
+ if (!m_layerProgramObject) {
+ LOG_ERROR("Failed to create shader program for layers");
+ return false;
+ }
+
+ m_scrollProgramObject = loadShaderProgram(vertexShaderString, scrollFragmentShaderString);
+ if (!m_scrollProgramObject) {
+ LOG_ERROR("Failed to create shader program for scrolling layer");
+ return false;
+ }
+
+ m_borderProgramObject = loadShaderProgram(borderVertexShaderString, borderFragmentShaderString);
+ if (!m_borderProgramObject) {
+ LOG_ERROR("Failed to create shader program for debug borders");
+ return false;
+ }
+
+ // Specify the attrib location for the position and make it the same for all three programs to
+ // avoid binding re-binding the vertex attributes.
+ bindCommonAttribLocation(m_positionLocation, "a_position");
+ bindCommonAttribLocation(m_texCoordLocation, "a_texCoord");
+
+ checkGLError();
+
+ // Re-link the shaders to get the new attrib location to take effect.
+ glLinkProgram(m_layerProgramObject);
+ glLinkProgram(m_borderProgramObject);
+ glLinkProgram(m_scrollProgramObject);
+
+ checkGLError();
+
+ // Get locations of uniforms for the layer content shader program.
+ m_samplerLocation = glGetUniformLocation(m_layerProgramObject, "s_texture");
+ m_matrixLocation = glGetUniformLocation(m_layerProgramObject, "matrix");
+ m_alphaLocation = glGetUniformLocation(m_layerProgramObject, "alpha");
+
+ m_scrollMatrixLocation = glGetUniformLocation(m_scrollProgramObject, "matrix");
+ m_scrollSamplerLocation = glGetUniformLocation(m_scrollProgramObject, "s_texture");
+
+ // Get locations of uniforms for the debug border shader program.
+ m_borderMatrixLocation = glGetUniformLocation(m_borderProgramObject, "matrix");
+ m_borderColorLocation = glGetUniformLocation(m_borderProgramObject, "color");
+
+ glGenBuffers(3, m_quadVboIds);
+ glBindBuffer(GL_ARRAY_BUFFER, m_quadVboIds[Vertices]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_quadVboIds[LayerElements]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
+
+ // Create a texture object to hold the contents of the root layer.
+ m_rootLayerTextureId = createLayerTexture();
+ if (m_rootLayerTextureId == -1) {
+ LOG_ERROR("Failed to create texture for root layer");
+ return false;
+ }
+ // Turn off filtering for the root layer to avoid blurring from the repeated
+ // writes and reads to the framebuffer that happen while scrolling.
+ glBindTexture(GL_TEXTURE_2D, m_rootLayerTextureId);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ return true;
+}
} // namespace WebCore
#endif // USE(ACCELERATED_COMPOSITING)
diff --git a/WebCore/platform/graphics/chromium/LayerRendererChromium.h b/WebCore/platform/graphics/chromium/LayerRendererChromium.h
index 7eb429f..498678e 100644
--- a/WebCore/platform/graphics/chromium/LayerRendererChromium.h
+++ b/WebCore/platform/graphics/chromium/LayerRendererChromium.h
@@ -36,45 +36,98 @@
#include "IntRect.h"
#include "LayerChromium.h"
+#include "SkBitmap.h"
+#include <wtf/HashMap.h>
#include <wtf/Noncopyable.h>
#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
-namespace skia {
-class PlatformCanvas;
-}
-
namespace WebCore {
+class GLES2Context;
+class Page;
+
+// Class that handles drawing of composited render layers using GL.
class LayerRendererChromium : public Noncopyable {
public:
- static PassOwnPtr<LayerRendererChromium> create();
+ static PassOwnPtr<LayerRendererChromium> create(Page* page);
- LayerRendererChromium();
+ LayerRendererChromium(Page* page);
~LayerRendererChromium();
-#if PLATFORM(SKIA)
- void drawLayersInCanvas(skia::PlatformCanvas*, const IntRect& clipRect);
-#endif
- void updateLayerContents();
+ // Updates the contents of the root layer that fall inside the updateRect and recomposites
+ // all the layers.
+ void drawLayers(const IntRect& updateRect, const IntRect& visibleRect, const IntRect& contentRect, const IntPoint& scrollPosition);
void setRootLayer(PassRefPtr<LayerChromium> layer) { m_rootLayer = layer; }
LayerChromium* rootLayer() { return m_rootLayer.get(); }
void setNeedsDisplay() { m_needsDisplay = true; }
- void setScrollFrame(SkIRect& scrollFrame) { m_scrollFrame = scrollFrame; }
+ // Frees the texture associated with the given layer.
+ bool freeLayerTexture(LayerChromium*);
+
+ bool hardwareCompositing() const { return m_hardwareCompositing; }
private:
-#if PLATFORM(SKIA)
- void drawLayerInCanvasRecursive(skia::PlatformCanvas*, LayerChromium*, float opacity);
-#endif
- void updateLayerContentsRecursive(LayerChromium*);
+ void compositeLayersRecursive(LayerChromium*, const TransformationMatrix&, float opacity, const IntRect& visibleRect);
+
+ void drawDebugBorder(LayerChromium*, const TransformationMatrix&);
+
+ void drawTexturedQuad(const TransformationMatrix& matrix, float width, float height, float opacity, bool scrolling);
+
+ bool isLayerVisible(LayerChromium*, const TransformationMatrix&, const IntRect& visibleRect);
+
+ void bindCommonAttribLocation(int location, char* attribName);
+
+ enum VboIds { Vertices, LayerElements };
+
+ // These are here only temporarily and should be removed once we switch over to GGL
+ bool initGL();
+ bool makeContextCurrent();
+
+ bool initializeSharedGLObjects();
+ int getTextureId(LayerChromium*);
+ int assignTextureForLayer(LayerChromium*);
+
+ // GL shader program object IDs.
+ unsigned int m_layerProgramObject;
+ unsigned int m_borderProgramObject;
+ unsigned int m_scrollProgramObject;
+
+ unsigned int m_rootLayerTextureId;
+ int m_rootLayerTextureWidth;
+ int m_rootLayerTextureHeight;
+
+ // Shader uniform and attribute locations.
+ const int m_positionLocation;
+ const int m_texCoordLocation;
+ int m_samplerLocation;
+ int m_matrixLocation;
+ int m_alphaLocation;
+ int m_scrollMatrixLocation;
+ int m_scrollSamplerLocation;
+
+ int m_borderMatrixLocation;
+ int m_borderColorLocation;
+
+ unsigned int m_quadVboIds[3];
+ TransformationMatrix m_projectionMatrix;
RefPtr<LayerChromium> m_rootLayer;
bool m_needsDisplay;
- SkIRect m_scrollFrame;
+ IntPoint m_scrollPosition;
+ bool m_hardwareCompositing;
+
+ // Map associating layers with textures ids used by the GL compositor.
+ typedef HashMap<LayerChromium*, unsigned int> TextureIdMap;
+ TextureIdMap m_textureIdMap;
+
+ OwnPtr<GLES2Context> m_gles2Context;
+
+ // The WebCore Page that the compositor renders into.
+ Page* m_page;
};
}