Changeset 83949 in webkit


Ignore:
Timestamp:
Apr 14, 2011 10:43:27 PM (13 years ago)
Author:
commit-queue@webkit.org
Message:

2011-04-14 Justin Novosad <junov@chromium.org>

Reviewed by Kenneth Russell.

[Chromium] Accelerated 2D Canvas is slow to execute putImageData
https://bugs.webkit.org/show_bug.cgi?id=57960

2011-04-14 Justin Novosad <junov@chromium.org>

Reviewed by Kenneth Russell.

[Chromium] Accelerated 2D Canvas is slow to execute putImageData
https://bugs.webkit.org/show_bug.cgi?id=57960

  • platform/graphics/chromium/GLES2Canvas.cpp: (WebCore::GLES2Canvas::drawTexturedRect): Added an option for using the blend ops for alpha multiplication instead of compositing. (WebCore::GLES2Canvas::applyClipping): (WebCore::GLES2Canvas::putImageData): New method for drawing raw pixel data from memory to the canvas (WebCore::GLES2Canvas::putUnmultipliedImageData): Wrapper for putImageData (WebCore::GLES2Canvas::putPremultipliedImageData): Wrapper for putImageData
  • platform/graphics/chromium/GLES2Canvas.h:
  • platform/graphics/gpu/Texture.cpp: (WebCore::copySubRect): (WebCore::Texture::load): (WebCore::Texture::updateSubRect): Added an overload of the updateSubRect method that can receive a pixel of a size that is different from texture size. Improved the performance of updateSubrect by avoiding the allocation of a temporary buffer when not required.
  • platform/graphics/gpu/Texture.h:
  • platform/graphics/skia/ImageBufferSkia.cpp: (WebCore::getImageData): In the unmultiplied path, division by alpha now performs proper rounding in order to avoid generational degradation with putImageData (WebCore::putImageData): Alpha multiplication now performs proper rounding in order to be consistent with the hardware rendering path: OpenGL always rounds when converting to fixed point representation. (WebCore::ImageBuffer::putUnmultipliedImageData): Now supports a hardware rendering path, which eliminates the need for a readback from the GPU (WebCore::ImageBuffer::putPremultipliedImageData): Now supports a hardware rendering path, which eliminates the need for a readback from the GPU
Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r83948 r83949  
     12011-04-14  Justin Novosad  <junov@chromium.org>
     2
     3        Reviewed by Kenneth Russell.
     4
     5        [Chromium] Accelerated 2D Canvas is slow to execute putImageData
     6        https://bugs.webkit.org/show_bug.cgi?id=57960
     7
     8        * platform/chromium/test_expectations.txt:
     9        added expected failure for test
     10        canvas/philip/tests/2d.gradient.interpolate.colouralpha.html
     11        Logged as chromium issue:
     12        http://code.google.com/p/chromium/issues/detail?id=79477
     13
    1142011-04-14  Adrienne Walker  <enne@google.com>
    215
  • trunk/LayoutTests/platform/chromium/test_expectations.txt

    r83948 r83949  
    699699// selection
    700700BUGCR10369 LINUX : editing/selection/5354455-2.html = FAIL
     701
     702BUGCR79477 LINUX WIN : canvas/philip/tests/2d.gradient.interpolate.colouralpha.html = TEXT
    701703
    702704// TODO(erg): Attempted to rebaseline these tests as part of the above
  • trunk/Source/WebCore/ChangeLog

    r83945 r83949  
     12011-04-14  Justin Novosad  <junov@chromium.org>
     2
     3        Reviewed by Kenneth Russell.
     4
     5        [Chromium] Accelerated 2D Canvas is slow to execute putImageData
     6        https://bugs.webkit.org/show_bug.cgi?id=57960
     7
     8        * platform/graphics/chromium/GLES2Canvas.cpp:
     9        (WebCore::GLES2Canvas::drawTexturedRect):
     10        Added an option for using the blend ops for alpha multiplication
     11        instead of compositing.
     12        (WebCore::GLES2Canvas::applyClipping):
     13        (WebCore::GLES2Canvas::putImageData):
     14        New method for drawing raw pixel data from memory to the canvas
     15        (WebCore::GLES2Canvas::putUnmultipliedImageData):
     16        Wrapper for putImageData
     17        (WebCore::GLES2Canvas::putPremultipliedImageData):
     18        Wrapper for putImageData
     19        * platform/graphics/chromium/GLES2Canvas.h:
     20        * platform/graphics/gpu/Texture.cpp:
     21        (WebCore::copySubRect):
     22        (WebCore::Texture::load):
     23        (WebCore::Texture::updateSubRect):
     24        Added an overload of the updateSubRect method that can receive a pixel
     25        of a size that is different from texture size. Improved the performance
     26        of updateSubrect by avoiding the allocation of a temporary buffer when
     27        not required.
     28        * platform/graphics/gpu/Texture.h:
     29        * platform/graphics/skia/ImageBufferSkia.cpp:
     30        (WebCore::getImageData):
     31        In the unmultiplied path, division by alpha now performs proper
     32        rounding in order to avoid generational degradation with putImageData
     33        (WebCore::putImageData):
     34        Alpha multiplication now performs proper rounding in order to be
     35        consistent with the hardware rendering path: OpenGL always rounds when
     36        converting to fixed point representation.
     37        (WebCore::ImageBuffer::putUnmultipliedImageData):
     38        Now supports a hardware rendering path, which eliminates the need
     39        for a readback from the GPU
     40        (WebCore::ImageBuffer::putPremultipliedImageData):
     41        Now supports a hardware rendering path, which eliminates the need
     42        for a readback from the GPU
     43
    1442011-04-14  Joone Hur  <joone.hur@collabora.co.uk>
    245
  • trunk/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp

    r82614 r83949  
    3333#include "GLES2Canvas.h"
    3434
     35#include "ByteArray.h"
    3536#include "DrawingBuffer.h"
    3637#include "FloatRect.h"
     
    5859namespace WebCore {
    5960
     61using WTF::ByteArray;
     62
    6063// Number of line segments used to approximate bezier curves.
    6164const int pathTesselation = 30;
     
    407410void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace colorSpace, CompositeOperator compositeOp)
    408411{
    409     drawTexturedRect(texture, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha, colorSpace, compositeOp, m_state->clippingEnabled());
    410 }
    411 
    412 
    413 void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha, ColorSpace colorSpace, CompositeOperator compositeOp, bool clip)
     412    drawTexturedRect(texture, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha, colorSpace, compositeOp, m_state->clippingEnabled() ? ApplyClipping : 0);
     413}
     414
     415void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha, ColorSpace colorSpace, CompositeOperator compositeOp, unsigned drawTextureFlags)
    414416{
    415417    bindFramebuffer();
    416     m_context->applyCompositeOperator(compositeOp);
    417     applyClipping(clip);
     418   
     419    if (drawTextureFlags & MultiplySourceAlpha) {
     420        // The multiply option hijacks the blend func, so it can't be combined with a compositing op
     421        ASSERT(CompositeCopy == compositeOp);
     422        // Custom composite operation that performs alpha multiplication on color components
     423        m_context->graphicsContext3D()->enable(GraphicsContext3D::BLEND);
     424        m_context->graphicsContext3D()->blendFuncSeparate(GraphicsContext3D::SRC_ALPHA, GraphicsContext3D::ZERO, GraphicsContext3D::ONE, GraphicsContext3D::ZERO);
     425    } else
     426        m_context->applyCompositeOperator(compositeOp);
     427    applyClipping(drawTextureFlags & ApplyClipping);
    418428    const TilingData& tiles = texture->tiles();
    419429    IntRect tileIdxRect = tiles.overlappedTileIndices(srcRect);
     
    904914}
    905915
     916void GLES2Canvas::putImageData(void* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, Texture::Format format, float alpha, ColorSpace colorSpace, CompositeOperator op, unsigned drawTextureFlags)
     917{
     918    ASSERT(source);
     919    IntRect clippedSourceRect(0, 0, sourceSize.width(), sourceSize.height());
     920    clippedSourceRect.intersect(sourceRect);
     921    int deltaX = clippedSourceRect.x() - sourceRect.x();
     922    int deltaY = clippedSourceRect.y() - sourceRect.y();
     923    IntPoint adjustedDestPoint(destPoint.x() + deltaX, destPoint.y() + deltaY);   
     924    IntRect destRect(adjustedDestPoint, clippedSourceRect.size());
     925    IntRect canvasRect(0, 0, m_size.width(), m_size.height());
     926    destRect.intersect(canvasRect);
     927    deltaX = destRect.x() - adjustedDestPoint.x();
     928    deltaY = destRect.y() - adjustedDestPoint.y();
     929    clippedSourceRect.move(deltaX, deltaY);
     930    clippedSourceRect.setSize(destRect.size());
     931    if (!clippedSourceRect.width() || !clippedSourceRect.height())
     932        return;
     933    RefPtr<Texture> uploadTexture = m_context->createTexture(format, clippedSourceRect.width(), clippedSourceRect.height());
     934    IntRect texUpdateRect(0, 0, clippedSourceRect.width(), clippedSourceRect.height());
     935    // To get the row stride right, we still take the width of the full buffer for the source size.
     936    IntSize adjustedSourceSize(sourceSize.width(), texUpdateRect.height());
     937    uploadTexture->updateSubRect(((char*)source) + (clippedSourceRect.y() * sourceSize.width() + clippedSourceRect.x()) * 4, adjustedSourceSize, texUpdateRect);
     938    AffineTransform identity;
     939    drawTexturedRect(uploadTexture.get(), texUpdateRect, destRect, identity, alpha, colorSpace, op, drawTextureFlags);
     940}
     941
     942void GLES2Canvas::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
     943{
     944    IntPoint newDestPoint(destPoint.x() + sourceRect.x(), destPoint.y() + sourceRect.y());
     945    putImageData(source->data(), sourceSize, sourceRect, newDestPoint, Texture::RGBA8, 1.0f, ColorSpaceDeviceRGB, CompositeCopy, MultiplySourceAlpha);
     946}
     947
     948void GLES2Canvas::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
     949{
     950    IntPoint newDestPoint(destPoint.x() + sourceRect.x(), destPoint.y() + sourceRect.y());
     951    putImageData(source->data(), sourceSize, sourceRect, newDestPoint, Texture::RGBA8, 1.0f, ColorSpaceDeviceRGB, CompositeCopy, 0);
     952}
     953
    906954void GLES2Canvas::checkGLError(const char* header)
    907955{
  • trunk/Source/WebCore/platform/graphics/chromium/GLES2Canvas.h

    r82614 r83949  
    4444#include <wtf/Vector.h>
    4545
     46namespace WTF {
     47class ByteArray;
     48}
     49
    4650namespace WebCore {
    4751
     
    5660    WTF_MAKE_NONCOPYABLE(GLES2Canvas);
    5761public:
     62    enum DrawTextureFlags {
     63        ApplyClipping =         1 << 0,
     64        MultiplySourceAlpha =   1 << 1
     65    };
     66
    5867    GLES2Canvas(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&);
    5968    ~GLES2Canvas();
     
    8998    // This version is called by BitmapImage::draw().
    9099    void drawTexturedRect(Texture*, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace, CompositeOperator);
    91     // This version is called by the above, and by the software->hardware uploads.
    92     void drawTexturedRect(Texture*, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha, ColorSpace, CompositeOperator, bool clip);
     100    // This version is called by the above, by putImageData, and by the software->hardware uploads.
     101    void drawTexturedRect(Texture*, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform&, float alpha, ColorSpace, CompositeOperator, unsigned drawTextureFlags);
    93102    Texture* createTexture(NativeImagePtr, Texture::Format, int width, int height);
    94103    Texture* getTexture(NativeImagePtr);
     104    void putImageData(void* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, Texture::Format, float alpha, ColorSpace, CompositeOperator, unsigned drawTextureFlags);
     105    void putUnmultipliedImageData(WTF::ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint);
     106    void putPremultipliedImageData(WTF::ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint);
    95107
    96108    SharedGraphicsContext3D* context() const { return m_context; }
  • trunk/Source/WebCore/platform/graphics/gpu/Texture.cpp

    r77785 r83949  
    121121
    122122template <bool swizzle>
    123 static uint32_t* copySubRect(uint32_t* src, int srcX, int srcY, uint32_t* dst, int width, int height, int srcStride)
     123static void copySubRect(uint32_t* src, int srcX, int srcY, uint32_t* dst, int width, int height, int srcStride)
    124124{
    125125    uint32_t* srcOffset = src + srcX + srcY * srcStride;
    126 
    127     if (!swizzle && width == srcStride)
    128         return srcOffset;
    129 
    130126    if (swizzle) {
    131127        uint32_t* dstPixel = dst;
     
    142138        }
    143139    }
    144     return dst;
    145140}
    146141
    147142void Texture::load(void* pixels)
    148143{
    149     updateSubRect(pixels, IntRect(0, 0, m_tiles.totalSizeX(), m_tiles.totalSizeY()));
     144    updateSubRect(pixels, IntSize(m_tiles.totalSizeX(), m_tiles.totalSizeY()), IntRect(0, 0, m_tiles.totalSizeX(), m_tiles.totalSizeY()));
    150145}
    151146
    152147void Texture::updateSubRect(void* pixels, const IntRect& updateRect)
    153148{
     149    updateSubRect(pixels, IntSize(m_tiles.totalSizeX(), m_tiles.totalSizeY()), updateRect);
     150}
     151
     152void Texture::updateSubRect(void* pixels, const IntSize& pixelBufferSize, const IntRect& updateRect)
     153{
     154    IntSize updateBounds = pixelBufferSize.shrunkTo(IntSize(m_tiles.totalSizeX(), m_tiles.totalSizeY()));
    154155    IntRect updateRectSanitized(updateRect);
    155     updateRectSanitized.intersect(IntRect(0, 0, m_tiles.totalSizeX(), m_tiles.totalSizeY()));
     156    updateRectSanitized.intersect(IntRect(0, 0, updateBounds.width(), updateBounds.height()));
    156157
    157158    uint32_t* pixels32 = static_cast<uint32_t*>(pixels);
     
    164165        // FIXME:  This could use PBO's to save doing an extra copy here.
    165166    }
     167
    166168    int tempBuffSize = // Temporary buffer size is the smaller of the max texture size or the updateRectSanitized
    167169        min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRectSanitized.width()) *
    168170        min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRectSanitized.height());
    169     OwnArrayPtr<uint32_t> tempBuff = adoptArrayPtr(new uint32_t[tempBuffSize]);
     171    bool needTempBuff = swizzle || updateRect.width() != m_tiles.totalSizeX() || updateRect.width() != pixelBufferSize.width() || m_tiles.numTilesX() > 1;
     172    OwnArrayPtr<uint32_t> tempBuff;
     173    if (needTempBuff)
     174        tempBuff = adoptArrayPtr(new uint32_t[tempBuffSize]);
    170175
    171176    for (int tile = 0; tile < m_tiles.numTiles(); tile++) {
     
    182187            continue;
    183188
    184         // Copy sub rectangle out of larger pixel data
    185189        uint32_t* uploadBuff = 0;
    186         if (swizzle) {
    187             uploadBuff = copySubRect<true>(
    188             pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
    189             tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSizeX());
     190        if (needTempBuff) {
     191            uploadBuff = tempBuff.get();
     192            // Copy sub rectangle out of larger pixel data
     193            if (swizzle) {
     194                copySubRect<true>(
     195                pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
     196                uploadBuff, updateRectIntersected.width(), updateRectIntersected.height(), pixelBufferSize.width());
     197            } else {
     198                copySubRect<false>(
     199                pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
     200                uploadBuff, updateRectIntersected.width(), updateRectIntersected.height(), pixelBufferSize.width());
     201            }
    190202        } else {
    191             uploadBuff = copySubRect<false>(
    192             pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
    193             tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSizeX());
     203            uploadBuff = pixels32 + updateRectIntersected.x() + updateRectIntersected.y() * pixelBufferSize.width();
    194204        }
    195205
  • trunk/Source/WebCore/platform/graphics/gpu/Texture.h

    r73831 r83949  
    4343
    4444class IntRect;
     45class IntSize;
    4546
    4647class Texture : public RefCounted<Texture> {
     
    5253    void load(void* pixels);
    5354    void updateSubRect(void* pixels, const IntRect&);
     55    void updateSubRect(void* pixels, const IntSize&, const IntRect&);
    5456    Format format() const { return m_format; }
    5557    const TilingData& tiles() const { return m_tiles; }
  • trunk/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp

    r83941 r83949  
    232232            if (multiplied == Unmultiplied) {
    233233                unsigned char a = SkGetPackedA32(srcPMColor);
    234                 destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0;
    235                 destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0;
    236                 destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0;
     234                unsigned char halfA = a >> 1;
     235                destPixel[0] = a ? (SkGetPackedR32(srcPMColor) * 255 + halfA) / a : 0;
     236                destPixel[1] = a ? (SkGetPackedG32(srcPMColor) * 255 + halfA) / a : 0;
     237                destPixel[2] = a ? (SkGetPackedB32(srcPMColor) * 255 + halfA) / a : 0;
    237238                destPixel[3] = a;
    238239            } else {
     
    318319            if (multiplied == Unmultiplied) {
    319320                unsigned char alpha = srcPixel[3];
    320                 unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha);
    321                 unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha);
    322                 unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha);
     321                unsigned char r = SkMulDiv255Round(srcPixel[0], alpha);
     322                unsigned char g = SkMulDiv255Round(srcPixel[1], alpha);
     323                unsigned char b = SkMulDiv255Round(srcPixel[2], alpha);
    323324                destRow[x] = SkPackARGB32(alpha, r, g, b);
    324325            } else
     
    335336void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
    336337{
    337     context()->platformContext()->syncSoftwareCanvas();
    338     putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
     338    PlatformContextSkia* platformContext = context()->platformContext();
     339    if (platformContext->useGPU()) {
     340        platformContext->prepareForHardwareDraw();
     341        platformContext->gpuCanvas()->putUnmultipliedImageData(source, sourceSize, sourceRect, destPoint);
     342        return;
     343    }
     344    putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, platformContext->canvas()->getDevice(), m_size);
    339345}
    340346
    341347void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
    342348{
    343     context()->platformContext()->syncSoftwareCanvas();
     349    PlatformContextSkia* platformContext = context()->platformContext();
     350    if (platformContext->useGPU()) {
     351        platformContext->prepareForHardwareDraw();
     352        platformContext->gpuCanvas()->putPremultipliedImageData(source, sourceSize, sourceRect, destPoint);
     353        return;
     354    }
    344355    putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
    345356}
Note: See TracChangeset for help on using the changeset viewer.