SkBitmap based SkCanvas very slow... How to improve draw speeds?

1,712 views
Skip to first unread message

Jona

unread,
Sep 10, 2015, 7:42:55 PM9/10/15
to skia-discuss
I'm working on a Nexus 4 device and I notice an SkCanvas backed by a SkBitmap is very slow for drawing.

mpDrawBitmap = new SkBitmap();
mpDrawBitmap->setInfo(SkImageInfo::MakeN32Premul(width, height));
mpDrawBitmap->allocPixels();
mpDrawCanvas = new SkCanvas(*mpDrawBitmap);

I'm using this draw bitmap for background drawing and later on it gets drawn into the SkCanvas backed by OpenGL. 

SkCanvas *pCanvas = mpSkSurface->getCanvas();
if (pCanvas)
{
if (!mpDirtyRect->isEmpty())
{
pCanvas->save();
pCanvas->clipRect(*mpDirtyRect);
pCanvas->drawBitmapRect(*mpDrawBitmap, *mpDirtyRect, *mpDirtyRect, NULL);
pCanvas->restore();

if (mpDebugPaint) {
pCanvas->drawRect(*mpDirtyRect, *mpDebugPaint);
}

mpDirtyRect->setEmpty();
}
}

mpContext->flush();

How can I optimize my draw canvas? Is there a way to create an OpenGL SkCanvas that doesn't draw to any surface?

Hal Canary

unread,
Sep 10, 2015, 9:10:17 PM9/10/15
to skia-d...@googlegroups.com
SkCanvas(const SkBitmap&) creates CPU canvas. You should try using the GPU backend to draw into a texture, then read those pixels back to a bitmap. 

    SkBitmap* drawViaGanesh(GrContext* grContext,
                            int width, int height,
                            void (*drawMP)(SkCanvas*)) {
        SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
        SkAutoTUnref<SkSurface> mpSurface(
                SkSurface::NewRenderTarget(
                        grContext, SkSurface::kNo_Budgeted, info));
        SkCanvas* mpDrawCanvas = mpSurface->getCanvas();
        drawMP(mpDrawCanvas);
        mpDrawCanvas->flush();
        SkBitmap* mpDrawBitmap = new SkBitmap;
        mpDrawBitmap->allocPixels(info);
        mpDrawCanvas->readPixels(mpDrawBitmap, 0, 0);
return mpDrawBitmap;
    }

Or even better, use an image rather than a bitmap:

    SkImage* drawViaGanesh(GrContext* grContext,
                           int width, int height,
                           void (*drawMP)(SkCanvas*)) {
        SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
        SkAutoTUnref<SkSurface> mpSurface(
                SkSurface::NewRenderTarget(
                        grContext, SkSurface::kNo_Budgeted, info));
        SkCanvas* mpDrawCanvas = mpSurface->getCanvas();
        drawMP(mpDrawCanvas);
        mpDrawCanvas->flush();
        return mpSurface->newImageSnapshot();
    }

Since Skia may be smart enough to skip reading the texture back to memory (is that implemented yet?).

Jona

unread,
Sep 11, 2015, 11:40:57 AM9/11/15
to skia-discuss
Hey Hal,

Thanks for the quick reply and providing some direction. Are you suggesting to use my main OpenGL backed canvas to draw into and then copy the state out from it? If that is the case I can't do that for 2 reasons. :/

1. I'm drawing a background bitmap and a foreground bitmap sandwiching my draw layer. This would make it hard to obtain the drawn pixels without including the background and foreground.
2. The touch input that draws comes in via main thread. The OpenGL context is not accessible via the main thread. A requestRender is posted so that the drawing happens on the proper thread holding the OpenGl context.

Your ideas and info is greatly appreciated.

Thanks!

cruz...@gmail.com

unread,
Sep 11, 2015, 1:35:41 PM9/11/15
to skia-discuss
Creating a new render target as shown in Hal's example will give you a blank canvas.  You'll ultimately need to draw the resulting image to the screen using a render target wrapping the default frame buffer (assuming everything is drawn with opengl).  If done in this way then you could avoid copying the image to cpu memory only to draw it back to the gpu.  This is my assumption anyway, please feel free to correct me if I'm mistaken.

Jona

unread,
Sep 11, 2015, 4:22:33 PM9/11/15
to skia-discuss
Thanks guys for the help but I think I'm getting a bit lost here... :/

Two questions:
1. Can the same OpenGL context be used to create a new surface? 
2. Can I create an OpenGL context with no display. Something like FBO? However, I'm not sure FBO are supported on OpenGL ES 2.0.

I'm new to OpenGL as you can tell and it's a steep learning curve.

Jona

unread,
Sep 13, 2015, 12:01:35 PM9/13/15
to skia-discuss
Ok, after playing around a bit more I was able to answer my one question. Yes, you can use the same OpenGL context for a new surface... as you shown on Hal's example code.

I did notice an incredible speed boost when using SkImage instead of SkBitmap when reading pixels from the surface. I'm wondering if clipping even matters when drawing an SkImage to a canvas?

For example:
pCanvas->save();
pCanvas->clipRect(*mpDirtyRect);
SkImage *pImage = mpDrawSurface->newImageSnapshot();
if (pImage)
{
pCanvas->drawImage(pImage, 0, 0, NULL);

pImage->unref();
}
pCanvas->restore();
Reply all
Reply to author
Forward
0 new messages