diff options
Diffstat (limited to 'WebCore/platform/graphics/chromium')
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; }; } |