Context, Surface, RenderTarget, ... how do they play together?

1,854 views
Skip to first unread message

david...@ptvgroup.com

unread,
Nov 14, 2014, 10:19:11 AM11/14/14
to skia-d...@googlegroups.com
Hy everyone,

I'm a bit confused over all the classes in Skia which seem to play a part in the rendering pipeline at some stage.

In cairo, there's a context which takes care of the drawing, and a surface which is "subclassed" for different types of output and which is the target I'm drawing on.

For Skia, painting into a plain bitmap buffer is easy and just requires a SkCanvas object for drawing and a SkBitmap as storage.

Now I'm looking into hardware acceleration and that's where I get confused.

Can anyone give me a general overview of the responsibilities of these classes and/or how they interoperate?
  • contexts (like GrContext)
  • devices (like SkGpuDevice)
  • surfaces (like SkBitmapSurface)
  • interfaces (like GrGlInterface)
  • render targets
  • backends

I can't find any real documentation about them. Example codes seem outdated as they use mostly deprecated and/or internal functions at key steps.

Thanks for any answer that helps me get the idea.

Blessings,
David

Brian Salomon

unread,
Nov 14, 2014, 11:05:36 AM11/14/14
to skia-d...@googlegroups.com

Hi David,

GrGLInterface is really just a list of function pointers that allow Skia to talk to OpenGL. We use it to avoid the platform specific intricacies of getting to OpenGL functions and to allow us to use different implementations of OpenGL in the same binary (e.g. ANGLE and native OpenGL on windows).

GrContext should map one to one with OpenGL contexts with which you wish to use Skia. It uses GrGLInterface to make OpenGL calls. You pass one in to GrContext's Create factory function. If you're just using your systems native OpenGL you can pass NULL and one will be created internally (assuming you've built with the correct platform-specific GrGLCreateNativeInterface_<foo>.cpp file).

There are few functions of interest to a Skia client on GrContext. We plan to split it's interface apart to those parts that are relevant to clients and internal functions. It just hasn't been a high priority. The only real interesting functions are around wrapping OpenGL objects that you've created with Skia types so that you can draw to them using Skia.

If you want to use Skia to draw to an OpenGL texture that you created then you should use GrContext::wrapBackendTexture() and specify the kRenderTarget_GrBackendTextureFlag on the descriptor you pass in. You'd use SkSurface::NewRenderTargetDirect(texture->asRenderTarget()) to create an SkSurface (see below).There should be a shortcut for doing all these gymnastics via a SkSurface factory function (see below), and we will add this soon.

There is also the GrContext::wrapBackendRenderTarget function. This exists to bind Skia to FBOs in OpenGL that aren't backed by a texture, namely FBO 0. This used for drawing directly to the screen. However, because the FBO isn't texture backed, it is a destination for rendering only, not a source.

SkSurface is the preferred way to create a drawing context for Skia for both the sw-rasterizer and the gpu-rasterizer. I already mentioned NewRenderTargetDirect(). The non-Direct versions create the OpenGL objects internally within Skia.

No matter what backend you create a surface for it has a canvas accessed via SkSurface::getCanvas(). You issue draw calls to that canvas and can make immutable snapshots of its contents via SkSurface::newImageSnapshot(). You shouldn't have to worry about what subclass of SkSurface or SkImage you're using and SkDevice subclasses are only meant to be exposed internally to the library.

If you're not rendering directly to the screen, and your ready to compose your Skia rendered content using OpenGL you have to do two things: ensure Skia has emitted any internally buffered drawing commands, and then get the texture ID.

SkImage* image = surface->newImageSnapshot();
image->getTexture()->flushWrites();
GLint textureID = image->getTexture()->getTextureHandle();
// Do OpenGL stuff with textureID here.

// Let Skia know you changed the OpenGL context's state so it doesn't make assumptions about the state.
image->getTexture()->getContext()->resetContext();

Hope this helps!

Brian

--
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 http://groups.google.com/group/skia-discuss.
For more options, visit https://groups.google.com/d/optout.

david...@ptvgroup.com

unread,
Nov 20, 2014, 4:20:57 AM11/20/14
to skia-d...@googlegroups.com
Hi Brian,

thank you very much for your detailed answer and sorry for the delay on mine :)
 
If I got everything right, all it takes to use the GPU as rasterizing backend for a bitmap is:

context = GrContext::Create(kOpenGL_GrBackend, NULL); // default opengl backend
surface = SkSurface::NewRenderTarget(context, image_info); // opengl backed surface
canvas = surface->getCanvas();

// draw to canvas here

bitmap->setInfo(canvas->imageInfo()); // setup bitmap
canvas->readPixels(bitmap); // write to bitmap

To be clear, I don't need to use any OpenGL functionality directly (yet), I just want to make sure, drawing operations make use of hardware acceleration.

Blessings,
David

Brian Salomon

unread,
Nov 20, 2014, 5:20:42 PM11/20/14
to skia-d...@googlegroups.com
Hi David,

That looks correct to me. Though, I should add that readPixels is pretty expensive and, depending on what you're drawing, using it may mitigate any benefit of the gpu backend.

Brian

--
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+unsubscribe@googlegroups.com.

david...@ptvgroup.com

unread,
Nov 21, 2014, 4:32:49 AM11/21/14
to skia-d...@googlegroups.com
Hi Brian,

Thanks, I actually got it working, though performance using WGL ist currently about 5-15 times worse than software rendering.
Note that I'm only measuring the time the drawing calls take, excluding initialization, file output and cleanup.

Thanks for the hint about readPixels, is there a faster way? Or is it a bad idea to use the GPU when I render to some memory backend instead of the screen?

Blessings,
David



Am Donnerstag, 20. November 2014 23:20:42 UTC+1 schrieb Brian Salomon:
Hi David,

That looks correct to me. Though, I should add that readPixels is pretty expensive and, depending on what you're drawing, using it may mitigate any benefit of the gpu backend.

Brian

On Thu Nov 20 2014 at 4:20:59 AM <david...@ptvgroup.com> wrote:
Hi Brian,

thank you very much for your detailed answer and sorry for the delay on mine :)
 
If I got everything right, all it takes to use the GPU as rasterizing backend for a bitmap is:

context = GrContext::Create(kOpenGL_GrBackend, NULL); // default opengl backend
surface = SkSurface::NewRenderTarget(context, image_info); // opengl backed surface
canvas = surface->getCanvas();

// draw to canvas here

bitmap->setInfo(canvas->imageInfo()); // setup bitmap
canvas->readPixels(bitmap); // write to bitmap

To be clear, I don't need to use any OpenGL functionality directly (yet), I just want to make sure, drawing operations make use of hardware acceleration.

Blessings,
David

--
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.

david...@ptvgroup.com

unread,
Nov 21, 2014, 7:36:13 AM11/21/14
to skia-d...@googlegroups.com
I could achieve some speedup by using Skias convenience API for creating the platform dependent GL context instead of my own hack, but it's still about 3-10 times slower than not using GL:

glcontext = SkCreatePlatformGLContext();
glcontext->init(kGL_GrGLStandard, width, height);
context = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) glcontext->gl());
[...]

Brian Salomon

unread,
Nov 21, 2014, 9:41:07 AM11/21/14
to skia-d...@googlegroups.com
Hi David,

I think you'll find that the GPU backend is well suited for a sort of "feed forward" use case where your application is sending commands to the GPU which eventually make their way to the screen. If you're rendering to memory that you want to access on the CPU, the software backend is probably your best bet. Depending on your system there may be a way to make the GPU render directly to CPU accessible memory but you'd have to use platform-specific ways of synchronizing access to that memory between the GPU and your application.

Brian

--

david...@ptvgroup.com

unread,
Nov 28, 2014, 4:44:32 AM11/28/14
to skia-d...@googlegroups.com
Hi Brian,

thanx, thats what I feared.
Maybe the only useful option would be to prepare a whole batch of drawing operations, run them on the GPU, then copy back the result...
But I guess the performance wouldn't increase that much because of all the traffic between cpu-ram-gpu it would imply.

Blessings,
David
Reply all
Reply to author
Forward
0 new messages