Is there faster way to create pixel array than using SkBitmap on iOS, Android, Windows Phone?

164 views
Skip to first unread message

Igor Khromov

unread,
Sep 10, 2016, 8:59:19 AM9/10/16
to skia-discuss
Hi!

I try to create textures like this:


/* Shared object (not in loop)  */

sk_sp<SkTypeface> _typeface = SkTypeface::MakeFromFile( FileUtils::getInstance()->fullPathForFilename( "font.ttf" ).c_str() );

_typefacePaint.setAntiAlias( true );

_typefacePaint.setColor( SK_ColorWHITE );

_typefacePaint.setStyle( SkPaint::kFill_Style );

_typefacePaint.setSubpixelText( true );

_typefacePaint.setTypeface( _typeface );

_typefacePaint.setTextSize( fontSize );

/* Drawing loop: begin */

SkRect textBounds;

(void)_typefacePaint.measureText( text.c_str(), text.size(), &textBounds );

float canvasWidth = textBounds.width();

        

SkPaint::FontMetrics metrics;

_typefacePaint.getFontMetrics( &metrics );

        

float canvasHeight = metrics.fAscent * (-1) + metrics.fDescent;


SkBitmap bitmap;

bitmap.allocPixels( SkImageInfo::MakeN32Premul( canvasWidth, canvasHeight ) );

bitmap.eraseColor( SK_ColorTRANSPARENT );


SkCanvas canvas( bitmap );

canvas.drawText( text.c_str(),

                             text.size(),

                             textPositionX,

                             textPositionY,

                             _typefacePaint );


auto pixels = bitmap.getPixels();


//Create some object here from pixels data ...

/* Drawing loop: end */

__________________________________________________________


So it works really slow on mobile devices(iOS, Android, Windows Phone).

Is there way to rasterise faster?


Thanks!

Mike Klein

unread,
Sep 10, 2016, 10:06:57 AM9/10/16
to skia-discuss
I'm not sure I have the picture of what you are doing.  Can you post some working code using https://fiddle.skia.org/?  The more information you can share with us the easier it is to diagnose.

Are you allocating your bitmap inside a loop that you want to go fast?  You might want to try not doing that.  Read allocPixels() as "malloc()", eraseColor as "memset()", and ~SkBitmap() as "free()".  These are all relatively slow operations that you will want to avoid when possible.

SkTypeface::getBounds() might help you allocate one bitmap that is big enough for all the drawing you will do.

--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.
To post to this group, send email to skia-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/skia-discuss.
For more options, visit https://groups.google.com/d/optout.

Igor Khromov

unread,
Sep 11, 2016, 7:06:16 AM9/11/16
to skia-discuss
Thanks for details.

I found some more (please look at the picture attached, all time metrics in microseconds, 102 symbols processed, font size 82.5f).

So the slowest operation is canvas.drawText(), it takes 14000 microseconds for only 1 symbol (iPhone 6), SkBitmap allocation and SkCanvas creation is only 68+14 = 82 microseconds.
In the end I need to create 102 symbol textures to place them separately in Sprites.

How can I improve that? Can I use GPU for text drawing on iOS, Android, Windows Phone?

Thanks!

суббота, 10 сентября 2016 г., 17:06:57 UTC+3 пользователь Mike Klein написал:
AvarageTime for Sprite creation.png

Igor Khromov

unread,
Sep 11, 2016, 7:09:34 AM9/11/16
to skia-discuss


суббота, 10 сентября 2016 г., 17:06:57 UTC+3 пользователь Mike Klein написал:
I'm not sure I have the picture of what you are doing.  Can you post some working code using https://fiddle.skia.org/?  The more information you can share with us the easier it is to diagnose.

Brian Osman

unread,
Sep 11, 2016, 12:24:00 PM9/11/16
to skia-d...@googlegroups.com

Yes, you should be able to use the GPU. Rather than using SkBitmap, try using SkSurface. If you allocate one as: sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(...), it will be GPU backed, and then you can get the canvas via surface->getCanvas() to draw your text.


To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss+unsubscribe@googlegroups.com.

Shawn Riordan

unread,
Sep 11, 2016, 12:54:03 PM9/11/16
to skia-discuss
If you are looking for speed for rendering static text, is it also useful to convert the text into glyph indices and pre-computed glyph positions ahead of time?

Jim Van Verth

unread,
Sep 12, 2016, 8:56:56 AM9/12/16
to skia-discuss
There's some overhead with looking up the glyph IDs and doing basic layout via DrawText, so you might get a small speedup using DrawPosText with that. But for best speed over multiple frames you should use SkTextBlobs, which cache all the necessary data.

To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss+unsubscribe@googlegroups.com.

Igor Khromov

unread,
Sep 12, 2016, 9:33:06 AM9/12/16
to skia-discuss
Ok, I tried to create RenderTarget, but it cause my openGL view somehow:
__________

auto tf = SkTypeface::MakeFromFile( FileUtils::getInstance()->fullPathForFilename( "NotoSans-Regular.ttf" ).c_str() );

        

SkPaint paint;

paint.setTextSize( 82.5f );

paint.setAntiAlias( true );

paint.setColor( SK_ColorWHITE );

paint.setStyle( SkPaint::kFill_Style );

paint.setSubpixelText( true );

paint.setTypeface( tf );

        

SkRect textBounds;

(void)paint.measureText( str.c_str(), str.size(), &textBounds );

auto canvasWidth  = textBounds.width();

        

SkPaint::FontMetrics metrics;

paint.getFontMetrics( &metrics );

        

auto canvasHeight = metrics.fAscent * (-1) + metrics.fDescent;


auto curIntf = GrGLCreateNativeInterface();


auto curContext = GrContext::Create( kOpenGL_GrBackend(GrBackendContext) curIntf);

        

SkImageInfo info = SkImageInfo::MakeN32Premul( SkScalarCeilToInt( canvasWidth  ), SkScalarCeilToInt( canvasHeight ) );

        

sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget( curContext, SkBudgeted::kNo, info );

__________


And after that I have my openGL view affected on iOS (pictures attached).



воскресенье, 11 сентября 2016 г., 19:24:00 UTC+3 пользователь Brian Osman написал:
After_Surface_Create.jpg
Before_Surface_Create.jpg

Igor Khromov

unread,
Sep 12, 2016, 9:36:22 AM9/12/16
to skia-discuss
Yes, I think it's useful thing.

I tried to create GrContext and draw on Canvas, it's much faster, fo 1 symbol "X" it's ~ 10 times faster (1000 microseconds for canvas.drawText(...), previous result 14000 microseconds), but I have problems with GrContext creation, it affect somehow openGL view (see my previous answer).

воскресенье, 11 сентября 2016 г., 19:54:03 UTC+3 пользователь Shawn Riordan написал:

Igor Khromov

unread,
Sep 13, 2016, 3:32:28 AM9/13/16
to skia-discuss
I found the code that makes my glView affected, it happens after call this function

void GrGLGpu::setScratchTextureUnit() {

    // Bind the last texture unit since it is the least likely to be used by GrGLProgram.

    int lastUnitIdx = fHWBoundTextureUniqueIDs.count() - 1;

    if (lastUnitIdx != fHWActiveTextureUnitIdx) {

        GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + lastUnitIdx));

        fHWActiveTextureUnitIdx = lastUnitIdx;

    }

    // clear out the this field so that if a program does use this unit it will rebind the correct

    // texture.

    fHWBoundTextureUniqueIDs[lastUnitIdx] = SK_InvalidUniqueID;

}


GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + lastUnitIdx)); - is a reason.

How can I fix that?

воскресенье, 11 сентября 2016 г., 19:24:00 UTC+3 пользователь Brian Osman написал:

Yes, you should be able to use the GPU. Rather than using SkBitmap, try using SkSurface. If you allocate one as: sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(...), it will be GPU backed, and then you can get the canvas via surface->getCanvas() to draw your text.

Brian Salomon

unread,
Sep 13, 2016, 9:07:29 AM9/13/16
to skia-discuss
This is an instance of a general problem where Skia rendering changes OpenGL state. You either have to give Skia its own OpenGL context or assume that the OpenGL state has been modified after using Skia and respecify the state (in this example using glActiveTexture(GL_TEXTURE<n>)) when you transition from Skia rendering to your own OpenGL rendering. Also, Skia needs to know when you've change OpenGL state which is communicated by calling GrContext::resetContext() before resuming Skia rendering.

Briam

Igor Khromov

unread,
Sep 13, 2016, 11:26:48 AM9/13/16
to skia-discuss
Thanks Brian!

I tried to do next:

1. Create GrContext:

auto curIntf         = GrGLCreateNativeInterface();

auto curContext  = GrContext::Create( kOpenGL_GrBackend(GrBackendContext) curIntf );


2. Get current active texture:

GLint activeTexture;

glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);


3. Create  surface, and draw on canvas:


sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget( curContext, .., .., .. );

auto canvas = surface->getCanvas();
canvas->drawText(...);

4. Reset ActiveTexture to one I got on second step.

glActiveTexture(activeTexture);

Result: It fixed problem with active glView and it's pixels not affected any more, 
 but when I try to draw on it, I see error: OpenGL error 0x0502. 

So as I understood I try to call some gl functions that cannot be called before glBegin(...) and glEnd().

Is it possible to create separate grContext from skia?

thanks!

вторник, 13 сентября 2016 г., 16:07:29 UTC+3 пользователь Brian Salomon написал:

Igor Khromov

unread,
Sep 14, 2016, 7:34:43 AM9/14/16
to skia-discuss
Thanks Brian!

I almost made it work...

Added one more native GLContext:

std::string str("012345678901234567890123456789");

        

auto tf = SkTypeface::MakeFromFile( ... );

        

SkPaint paint;

paint.setTextSize( 52.5f );

paint.setAntiAlias( true );

paint.setColor( SK_ColorWHITE );

paint.setStyle( SkPaint::kFill_Style );

paint.setSubpixelText( true );

paint.setTypeface( tf );

        

SkRect textBounds;

(void)paint.measureText( str.c_str(), str.size(), &textBounds );

auto canvasWidth  = textBounds.width();

        

SkPaint::FontMetrics metrics;

paint.getFontMetrics( &metrics );

        

auto canvasHeight = metrics.fAscent * (-1) + metrics.fDescent;


setSkiaContextAsCurrent(); /* Makes own Skia active */


SkImageInfo info = SkImageInfo::MakeN32Premul( SkScalarCeilToInt( canvasWidth  ), SkScalarCeilToInt( canvasHeight ) );

        

sk_sp<SkSurface> surface =

     SkSurface::MakeRenderTarget( curContext,

                                                         SkBudgeted::kNo,

                                                         info,

                                                         0,

                                                         GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,

                                                         nullptr );

        

auto canvas = surface->getCanvas();

auto baseLinePositionY = metrics.fAscent * (-1);

        

canvas->drawText(...);


/* сreate pixel array from canvas */


setGLViewContextAsCurrent(); /* Make GLView context active */


The only thing I see is text has affected somehow (please see picture attached), maybe I need to add some settings?

Thanks, Igor.


вторник, 13 сентября 2016 г., 16:07:29 UTC+3 пользователь Brian Salomon написал:
This is an instance of a general problem where Skia rendering changes OpenGL state. You either have to give Skia its own OpenGL context or assume that the OpenGL state has been modified after using Skia and respecify the state (in this example using glActiveTexture(GL_TEXTURE<n>)) when you transition from Skia rendering to your own OpenGL rendering. Also, Skia needs to know when you've change OpenGL state which is communicated by calling GrContext::resetContext() before resuming Skia rendering.

Briam
Text_from_SkiaContext.jpg
Reply all
Reply to author
Forward
0 new messages