Rendering to existing openGL context solution

2,319 views
Skip to first unread message

Larry Weinberg

unread,
Jan 9, 2015, 3:07:15 PM1/9/15
to skia-d...@googlegroups.com
Because I was brand new to skia it took me several days of experimenting to get drawing working to an existing opengl context.
This seemed harder to get working than I expected -- mostly due to some missing documentation, lack of a very simple example,  and my lack of experience with the concepts behind 
SkGpuDevice, GrContext, GrGLInterface, GrRenderTarget, SkCanvas, etc.
There were also a few context settings that Skia needed  help with before things started working.

I decided I should post this to help make it easier for anyone else trying this the first time.
Here's what I did.  If there is an easier way, please let me know....

// First time creation:
    SkGpuDevice        *curGpuDevice         = NULL;
    GrContext             *curContext               = NULL;
    GrRenderTarget    *curRenderTarget     = NULL;
    SkCanvas             *curCanvas               = NULL;

    // Set up your OpenGL context which includes a stencil buffer
    //  this can be default back-buffer 0 or an FBO with its own stencil buffer attached (see end for example)
    ...

     // Save GL state before calling skia stuff
     glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

     // Using default backbuffer to draw to.  (Could be an FBO id)
     GLuint bufferID = 0; 
   
     // Create a GrGl native interface
     SkAutoTUnref<const GrGLInterface> glInterface;
     glInterface.reset(GrGLCreateNativeInterface());
 
     // Create the OpenGL backend GrContext 
     curContext = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) glInterface.get()));

     // Define render target description  to point to our buffer
     GrBackendRenderTargetDesc desc;       
     desc.fWidth                            = SkScalarRoundToInt(myWidth);
     desc.fHeight                           = SkScalarRoundToInt(myHeight);       
     desc.fConfig                           = kSkia8888_GrPixelConfig;
     desc.fOrigin                            = kBottomLeft_GrSurfaceOrigin;
     desc.fSampleCnt                    = 0;
     desc.fStencilBits                     = 8;
     desc.fRenderTargetHandle     = bufferID;
       
     // Create the render target
     SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
     GrRenderTarget *curRenderTarget = curContext->wrapBackendRenderTarget(desc));   

     // Create GPU device with rendertarget and use to create the canvas
     curGpuDevice     = SkGpuDevice::Create(curRenderTarget, surfaceProps));
     curCanvas           = new SkCanvas(curGpuDevice);
     curContext->resetContext();
 
     // Reset client state
     glPopClientAttrib();


// Every time you draw:

    // Bind to the buffer
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufferID);

    // Do your normal opengl drawing
    ...

    // Save the client state before skia drawing
    glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

    // Mark the skia context dirty now because we drew
    curContext->resetContext();

    // Skia doesn't seem to set this for itself. Needed if this is not the mode you left it in
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    // Do some skia drawing for example...
    // If drawing to FBO, clear it -- don't if drawing directly into context
    // curCanvas->drawColor(SK_ColorTRANSPARENT, SkXfermode::kSrc_Mode);
    // better solution for antialiasing would be to blit the backbuffer into FBO
    SkPaint paint;
    SkColor col = SkColorSetARGB(0x99, 0 ,0xFF,0);
    paint.setColor(col);
    paint.setAntiAlias(true);
    paint.setStyle(SkPaint::kFill_Style);   
    curCanvas->drawCircle(432, 332, 64, paint);

    // flush skia canvas to execute the Skia drawing command
    curCanvas->flush();

    // Restore gl client attributes after skia drawing
    glPopClientAttrib();

    // Important -- Turn off any program left from Skia before you draw your stuff again
    glUseProgram(0);

    // Bind back to main buffer if we are using FBO
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    DoSomethingWithYourFBO(); // or swapbuffers if using back-buffer

  
In case of using FBO I created it this way.  Antialiasing may be better if you draw directly to your backbuffer depending on what you
are doing with your FBO...

        glGenTextures(1, &mFBOTextureID);
        glBindTexture(GL_TEXTURE_2D, mFBOTextureID);        
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); //GL_REPEAT
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); //GL_REPEAT
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_LINEAR
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //GL_LINEAR       
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        glBindTexture(GL_TEXTURE_2D, 0);
 
         // Use a single buffer for stencil and depth
        glGenRenderbuffers(1, &mFboStencilId);
        glBindRenderbuffer(GL_RENDERBUFFER, mFboStencilId);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWidth, mHeight);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);

        // Generate Framebuffer and bind it
        glGenFramebuffers(1, &mFBOID);
        glBindFramebuffer(GL_FRAMEBUFFER, mFBOID);
       
        // Attach texture to color
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, mFBOTextureID, 0);
         
        // Attach depth buffer
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mFboStencilId);
       
        // Attach stencil buffer
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mFboStencilId);

        GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
            fprintf(stderr, "FBO Error in skia buffer!\n");
       
        // Unbind
        glBindFramebufferEXT(GL_FRAMEBUFFER, 0);



Mike Reed

unread,
Jan 9, 2015, 3:31:44 PM1/9/15
to skia-d...@googlegroups.com
1. Thank for writing up a sample app.
2. You're right, we need to codify this and its related documentation
3. Perhaps you could submit this as a CL for a test app?
4. I can make some minor suggestions to it ...

Instead of SkGpuDevice (which is a private header), you should be able to this:

SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(...));
SkCanvas* canvas = surface->getCanvas();


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

Larry Weinberg

unread,
Jan 9, 2015, 4:05:11 PM1/9/15
to skia-d...@googlegroups.com
Thanks Mike.  Yes your suggestion works. I actually had something like that early on and it wasn't working for some other reason so I had switched to lower level.

So the change would be:

// at top:
SkSurface  *curSurface = NULL;

// Instead of GrGpu section...
    // curGpuDevice     = SkGpuDevice::Create(curRenderTarget, surfaceProps));
    // curCanvas           = new SkCanvas(curGpuDevice);
curSurface = SkSurface::NewRenderTargetDirect(curRenderTarget, &surfaceProps));

curCanvas = curSurface->getCanvas());


At end, I would unref the curSurface instead of curCanvas as it is owned by the Surface.



Mike Reed

unread,
Jan 9, 2015, 4:06:35 PM1/9/15
to skia-d...@googlegroups.com
correct!

Joel de Guzman

unread,
Mar 22, 2020, 10:39:47 PM3/22/20
to skia-discuss
Hi,

So I needed something like this. Many years later (2020), it sees this is still missing in documentation? But would there be a sample code already? I'm new to skia, I tried the code, but I get lots of errors, probably due to API changes. I'd really appreciate to see some new info on this.

Many thanks!
--Joel

On Saturday, January 10, 2015 at 4:07:15 AM UTC+8, Larry Weinberg wrote:
Because I was brand new to skia it took me several days of experimenting to get drawing working to an existing opengl context.
This seemed harder to get working I cam acr than I expected -- mostly due to some missing documentation, lack of a very simple example,  and my lack of experience with the concepts behind 
SkGpuDevice, GrContext, GrGLInterface, GrRenderTarget, SkCanvas, etc.
There were also a few context settings that Skia needed  help with before things started working.

[snip...]

Brian Salomon

unread,
Mar 23, 2020, 9:18:48 AM3/23/20
to skia-d...@googlegroups.com
There is an example here that uses SDL:

Setup would be similar for any other OpenGL environment. You create an OpenGL context (somehow, outside Skia), and then when it's current call GrContext::MakeGL(). That gets you a GrContext associated with the GL context. You use the GrContext to create SkSurfaces that you draw to. The MakeFromBackendRenderTarget() API lets you make SkSurfaces for framebuffer objects created outside of Skia. Most windowing systems have framebuffer object 0 as the main window's buffer. If you've created textures outside of Skia you'd like to draw into use SkSurface::MakeFromBackendTexture() instead. A single GrContext can be used to create many surfaces. The SkSurface::MakeRenderTarget() factory creates new surfaces where Skia manages the lifetime of the buffer rather than providing it externally. GrContext buffers up surfaces' drawing work internally. You call SkSurface::flush() to flush a single surface's work out to GL or GrContext::flush() to flush all surfaces' work.



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

Mike

unread,
Apr 9, 2020, 7:11:04 AM4/9/20
to skia-d...@googlegroups.com
I try the code below with X-Plane. It doesn't crash but x-plane is completely messed up.
Many thanks for any help.

 XPLMSetGraphicsState(0, 1, 0, 0, 1, 0, 0); // 1 texture and alpha blending
glColor4f(pInstrBri[2], pInstrBri[2], pInstrBri[2], sgierDR28value);
//char array[10];
//sprintf(array, "%f", pInstrBri[2]);
//XPLMDebugString(array);
// glPushMatrix();
//glBlendFunc(GL_ONE, GL_ONE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// setup GrContext
glViewport(0, 0, 10, 10);
glClearColor(1, 1, 1, 1);
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

auto interfaceT = GrGLMakeNativeInterface();

// setup contexts
sk_sp<GrContext> grContext(GrContext::MakeGL(interfaceT));
SkASSERT(grContext);

// Wrap the frame buffer object attached to the screen in a Skia render target so Skia can
// render to it
GrGLint buffer;
GR_GL_GetIntegerv(interfaceT.get(), GR_GL_FRAMEBUFFER_BINDING, &buffer);
GrGLFramebufferInfo info;
info.fFBOID = (GrGLuint)buffer;
SkColorType colorType;
info.fFormat = GR_GL_RGBA8;
colorType = kRGBA_8888_SkColorType;

GrBackendRenderTarget target(10, 10, 0, 8, info);

// setup SkSurface
// To use distance field text, use commented out SkSurfaceProps instead
// SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
//                      SkSurfaceProps::kLegacyFontHost_InitType);
SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);

sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
kBottomLeft_GrSurfaceOrigin,
colorType, nullptr, &props));


SkCanvas* canvas = surface->getCanvas();

// Clear background
canvas->clear(SK_ColorWHITE);

SkPaint paint;
paint.setColor(SK_ColorRED);

// Draw a rectangle with red paint
SkRect rect = SkRect::MakeXYWH(10, 10, 128, 128);
canvas->drawRect(rect, paint);

// Set up a linear gradient and draw a circle
{
SkPoint linearPoints[] = { { 0, 0 }, { 300, 300 } };
SkColor linearColors[] = { SK_ColorGREEN, SK_ColorBLACK };
paint.setShader(SkGradientShader::MakeLinear(linearPoints, linearColors, nullptr, 2,
SkTileMode::kMirror));
paint.setAntiAlias(true);

canvas->drawCircle(200, 200, 64, paint);

// Detach shader
paint.setShader(nullptr);
}

// Draw a message with a nice black paint
SkFont font;
font.setSubpixel(true);
font.setSize(20);
paint.setColor(SK_ColorBLACK);

canvas->save();
static const char message[] = "Hello World";

// Translate and rotate
canvas->translate(300, 300);
//fRotationAngle += 0.2f;
//if (fRotationAngle > 360) {
// fRotationAngle -= 360;
//}
//canvas->rotate(fRotationAngle);

// Draw the text
canvas->drawSimpleText(message, strlen(message), SkTextEncoding::kUTF8, 0, 0, font, paint);

canvas->restore();

canvas->flush();

Mike

unread,
Apr 11, 2020, 4:03:22 AM4/11/20
to skia-discuss
I only manage to draw a rectangle which seems inverted/mirrored or such.
Thanks for help

Brian Salomon

unread,
Apr 13, 2020, 9:08:45 AM4/13/20
to skia-d...@googlegroups.com
I don't know the first thing about X-Plane, but if it is using OpenGL then it could be that Skia and X-Plane are stomping on each other's expected OpenGL state. OpenGL is a stateful API and so is difficult for two different pieces of code to share a context. Skia can be told that the OpenGL state was modified outside of Skia using GrContext::resetContext(). This resets Skia's tracking of the GL state after it has been used externally.

If the content is showing up upside down then I'd try changing the origin value you pass when creating the SkSurface. Also keep in mind that Skia's coordinate system has (0,0) at the top left with the y-axis pointing down.

On Sat, Apr 11, 2020 at 4:03 AM Mike <mcyb...@gmail.com> wrote:
I only manage to draw a rectangle which seems inverted/mirrored or such.
Thanks for help

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

Mike

unread,
Apr 14, 2020, 5:03:22 AM4/14/20
to skia-d...@googlegroups.com
Thanks Brian. resetcontext does nothing as my x-plane is still messed up. Changing the origin does work but I need the something like the normal flipped.

Reply all
Reply to author
Forward
0 new messages