Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

removing discard from the fragment shader significantly improves performance on iOS #214

Open
wtholliday opened this issue Mar 31, 2015 · 12 comments

Comments

@wtholliday
Copy link

Before:

before

After:

after

I did notice some rendering artifacts for self-intersecting paths:

img_0052

vs.

img_0053

How should I fix the artifacts? Should there be an option exposed to turn off discard (it's a no-no on iOS)?

(I'm running the nanovg demo: https://github.com/wtholliday/nanovg-ios-demo)

wtholliday added a commit to wtholliday/nanovg that referenced this issue Mar 31, 2015
@dougbinks
Copy link
Contributor

It should be possible to set the alpha value to 0.0 instead of using discard to make the pixel completely transparent. Discard is indeed an issue on many mobile configurations, so this should likely be done for all OpenGL ES configurations.

@wtholliday
Copy link
Author

@dougbinks, setting the alpha to 0.0 doesn't fix the artifacts. In fact it creates a new problem with strokes:

img_0054

I did the following:

    "#ifdef EDGE_AA\n"
    "   if (strokeAlpha < strokeThr) { result.a = 0.0; };\n"
    "#endif\n"

@dougbinks
Copy link
Contributor

The blend function is glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); so this is likely due to depth or stencil testing issues and so not easily solvable though Mikko might be able to come up with a solution.

Since the discard is only on the EDGE_AA path, I would not use the NVG_ANTIALIAS if you want performance.

@wtholliday
Copy link
Author

Good thought about the stenciling. Turning off NVG_STENCIL_STROKES gets rid of the artifacts, but of course then self-intersecting paths are drawn differently (which I can live with).

@memononen
Copy link
Owner

Good catch! I wonder if splitting the shaders would make it faster on mobile too? So that if one shader uses discard, all shaders don't pay the penalty?

The idea of the discard is that we first draw the "fill" of the stroke, but only once, and then on second pass we draw the AA bit of the stroke. It either needs two sets of geometry, or discard/alphatest. I chose to use discard.

@dougbinks
Copy link
Contributor

Splitting the shaders may help, since the discard is driven by data it can't be optimized out and tile based GPUs then pay a big penalty (there's a cost too on other GPUs, but it's far less).

@wtholliday
Copy link
Author

If two AA parts of a path each mostly cover a pixel, would the color be computed as if coverage is greater than the area of the pixel? I'm trying to understand the existing implementation.

@memononen
Copy link
Owner

The AA in nanovg works so that first the solid (non-AA) part of the shape is drawn, creating a stencil mask of the fill of the shape. Then the solid shape is filled using a quad. Finally, the stencil test is inverted, and an antialiased line is drawn around the perimeter (since AA-line drawing is iffy, this line is created as a triangle strip). The stencil mask is also used to handle the fill rule of the shape so that you can have holes.

The NVG_STENCIL_STROKES works similarly, but avoids the fill pass by drawing the solid shape and filling in the same pass. Essentially it uses the stencil buffer to only draw once at a given location. On the perimeter pass the stencil test is inverted and the AA edge of the stroke is drawn. The stroke geometry is build so that one segment of the path contains two triangles. The strokeThr is calculated so that it culls out the AA portion of the stroke during the solid pass.

That means that with both methods, the AA edge (I call it AA-fringe) can be slightly wrong, as it will be overdrawn in case the shape intersects or overaps. You can see this sometimes especially with light colors, but generally it works well. You can also configure NanoVG to not generate any of the AA fringes and set your frame buffer to use multisampling AA (i.e. MSAA) if correct coverage is required.

@wtholliday
Copy link
Author

I think you could draw the entire stroke in a single pass (with stenciling). Instead of overdraw of self-intersecting AA edges, you'd have underdraw. Would that work?

@memononen
Copy link
Owner

If you draw the entire stroke with stencil in single pass you'll get the artifacts as in your first screenshots. That light line is the anti-aliased bit of the vertical segment, which happens to be drawn first.

@wtholliday
Copy link
Author

Ah, of course!

Well, at least it seems like the discard isn't needed when NVG_STENCIL_STROKES is off, right?

@memononen
Copy link
Owner

That's correct.

olliwang added a commit to olliwang/nanovg that referenced this issue Sep 4, 2016
As discussed in memononen#214, the `discard` is not needed when `NVG_STENCIL_STROKES` is off. This commit follows the rule to deliver the best performance whenever possible.
QUItCoding pushed a commit to QUItCoding/nanovg that referenced this issue Sep 12, 2021
Discard can be slow with some GPUs so avoid it when NVG_STENCIL_STROKES
is not set. This patch is basically the same as in NanoVG issue memononen#214.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants