Inconsistent MSAA

Skip to first unread message

Jeru Sanders

Oct 27, 2021, 7:17:30 PMOct 27
to skia-discuss
I'm having an issue with MSAA. I'm creating am MSAA opengl framebuffer and creating the surface using SkSurface::MakeFromBackendRenderTarget()

AA artifacts appear randomly from frame to frame. Here's a video demonstrating:

I did some digging around in renderdoc and found that the issue occurs whenever glDrawArraysInstancedBaseInstance is called instead of glDrawArrays.

It looks correct here with glDrawArrays. goodDraw.png

But looks wrong here when draw using instancing.

I was ignoring it because I thought it was a skia bug after switching away from the NV extensions, but it's been happening for a few months now. I'm guessing it's an optimization that's getting trigged in certain situations.

I've tried a few hacks like setting GrGLCaps::fMaxInstancesPerDrawWithoutCrashing to 1, but it still does an instance draw, just with a single instance. And I can't find a way of modifying the shader to fix it, I'm pretty sure the vectors are generated at the wrong size in a previous step.

The artifacts looks like analytical AA, but I'm using MSAA to avoid that because it's not compatible with my Flash vectors.

Any advice? I'm kind of out of ideas, I don't see the path that avoids glDrawArraysInstancedBaseInstance.

Jeru Sanders

Oct 27, 2021, 7:18:35 PMOct 27
to skia-discuss
I should have noted that this only happens on Windows, emscripten+webgl is fine.

Jeru Sanders

Nov 1, 2021, 6:55:26 PMNov 1
to skia-discuss
Any advice? I really don't want to replace Skia in my project over something that looks like it could be a single boolean change.

Greg Daniel

Nov 2, 2021, 6:26:10 PMNov 2
to skia-discuss
Hi. You can try disabling instancing the same way you did for fMaxInstancesPerDrawWithoutCrashing, but instead set fDrawInstancedSupport = false; You can see where we set it in that file and just make sure you set it to false after we've done the normal setting of it.

What GPU are you running on? We do test many gpus on windows and haven't seen this instancing issue before there.

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
To view this discussion on the web visit

Nov 3, 2021, 10:28:21 AMNov 3
to skia-discuss
Can you try setting fBaseVertexBaseInstanceSupport to false? That should help us figure out whether the problem is glDrawArraysInstancedBaseInstance or something else.

Jeru Sanders

Nov 3, 2021, 11:00:25 PMNov 3
to skia-discuss
Wow, fDrawInstancedSupport=false works, dunno how I missed the the first time around.

fBaseVertexBaseInstanceSupport=false doesn't seem to matter.

I'm using an EVGA GTX 1050TI

Jeru Sanders

Nov 4, 2021, 10:36:51 AMNov 4
to skia-discuss
Actually, to not waste your time, this also happens on other gpus, and on an integrated Intel gpu GrDirectContext::MakeGL() just returns NULL if I ask for MSAA. I'm guessing it's that Skia doesn't like the framebuffer I give it, or some OpenGL state is getting mixed up, even though I call resetContext before every flushAndSubmit.

Greg Daniel

Nov 4, 2021, 10:50:30 AMNov 4
So other Skia commands/functions besides flushAndSubmit may use or change GL state. Are you able to try calling resetContext before you start doing any Skia work for a frame?

When you say GrDirectContext::MakeGL() returns null when you ask for MSAA, are you saying that specific function returns null or when you later try to create an msaa surface it returns null? I don't think there is any "request msaa" when you make a GrDirectContext.

Jeru Sanders

Nov 4, 2021, 11:06:10 AMNov 4
to skia-discuss
>  So other Skia commands/functions besides flushAndSubmit may use or change GL state.

Yeah, this is probably problem them, I've asked before what gpu state skia touches was wasn't able to get an answer. I guess I can make a list of drawing commands instead of executing them

>  I don't think there is any "request msaa" when you make a GrDirectContext.
Yeah, whoops, I meant SkSurface::MakeFromBackendRenderTarget()

Here's a simplified version of the init code that always assumes the gpu backend, it's kinda hacked together from various SDL and opengl examples, I don't know if there's official up-to-date instructions for the gpu backend:

void resetSkia(Vec2 size, Vec2 scale, bool useGpu, int msaaSamples) {
if (!skiaSys) {
skiaSys = (SkiaSys *)zalloc(sizeof(SkiaSys));

skiaSys->width = size.x;
skiaSys->height = size.y;
skiaSys->scale = scale;
skiaSys->useGpu = useGpu;

if (skiaSys->backTexture) destroyTexture(skiaSys->backTexture);
skiaSys->backTexture = createTexture(skiaSys->width, skiaSys->height);

if (skiaSys->gpuSurface) {
delete skiaSys->gpuSurface;
skiaSys->gpuSurface = NULL;

if (!skiaSys->grInterface) {
sk_sp<const GrGLInterface> grInterface = GrGLMakeNativeInterface();
if (!grInterface || !grInterface.get()) logf("NULL native interface\n");
skiaSys->grInterface = grInterface.release();

if (!skiaSys->grDirectContext) {
sk_sp<GrDirectContext> grDirectContext = GrDirectContext::MakeGL();
if (!grDirectContext || !grDirectContext.get()) logf("Failed to create grDirectContext\n");
skiaSys->grDirectContext = grDirectContext.release();

if (!skiaSys->flatFramebuffer) skiaSys->flatFramebuffer = createFramebuffer();
setColorAttachment(skiaSys->flatFramebuffer, skiaSys->backTexture, 0);

if (!skiaSys->multiSampleFramebuffer) skiaSys->multiSampleFramebuffer = createFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, skiaSys->multiSampleFramebuffer);

if (skiaSys->msaaColorRenderbuffer) glDeleteRenderbuffers(1, &skiaSys->msaaColorRenderbuffer);
glGenRenderbuffers(1, &skiaSys->msaaColorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, skiaSys->msaaColorRenderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaSamples, GL_RGBA8, skiaSys->width, skiaSys->height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, skiaSys->msaaColorRenderbuffer);

if (skiaSys->msaaDepthRenderbuffer) glDeleteRenderbuffers(1, &skiaSys->msaaDepthRenderbuffer);
glGenRenderbuffers(1, &skiaSys->msaaDepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, skiaSys->msaaDepthRenderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaSamples, GL_DEPTH24_STENCIL8, skiaSys->width, skiaSys->height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, skiaSys->msaaDepthRenderbuffer);

GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) logf("Bad fb\n");
glBindFramebuffer(GL_FRAMEBUFFER, skiaSys->multiSampleFramebuffer);

GrGLFramebufferInfo framebufferInfo;
framebufferInfo.fFBOID = skiaSys->multiSampleFramebuffer->id;
framebufferInfo.fFormat = GL_RGBA8;

GrBackendRenderTarget backendRenderTarget(
8, // stencil bits

SkSurfaceProps surfaceProps(0, SkPixelGeometry::kUnknown_SkPixelGeometry);

skiaSys->gpuSurface = SkSurface::MakeFromBackendRenderTarget(

if (!skiaSys->gpuSurface) {
if (msaaSamples == 16) {
logf("Failed to create skia MSAA surface, falling back\n");
resetSkia(size, scale, true, 8);
} else if (msaaSamples == 8) {
logf("Failed to create skia MSAA surface, falling back\n");
resetSkia(size, scale, true, 4);
} else if (msaaSamples == 4) {
logf("Failed to create skia MSAA surface, falling back\n");
resetSkia(size, scale, true, 2);
} else if (msaaSamples == 2) {
logf("Failed to create skia MSAA surface, falling back\n");
resetSkia(size, scale, true, 0);
} else {
logf("Failed to create skia GPU surface, falling back\n");
resetSkia(size, scale, false, 0); // This would take a cpu branch, which is omitted but always works

skiaSys->mainCanvas = skiaSys->gpuSurface->getCanvas();
if (!skiaSys->mainCanvas) logf("Failed to create skia canvas\n");
Reply all
Reply to author
0 new messages