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

Implement subpixel morphological antialiasing, or SMAA. #13423

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

pcwalton
Copy link
Contributor

@pcwalton pcwalton commented May 18, 2024

This commit implements a large subset of subpixel morphological antialiasing, better known as SMAA. SMAA is a 2011 antialiasing technique that detects jaggies in an aliased image and smooths them out. Despite its age, it's been a continual staple of games for over a decade. Four quality presets are available: low, medium, high, and ultra. I set the default to high, on account of modern GPUs being significantly faster than they were in 2011.

Like the already-implemented FXAA, SMAA works on an unaliased image. Unlike FXAA, it requires three passes: (1) edge detection; (2) blending weight calculation; (3) neighborhood blending. Each of the first two passes writes an intermediate texture for use by the next pass. The first pass also writes to a stencil buffer in order to dramatically reduce the number of pixels that the second pass has to examine. Also unlike FXAA, two built-in lookup textures are required; I bundle them into the library in compressed KTX2 format.

The reference implementation of SMAA is in HLSL, with abundant use of preprocessor macros to achieve GLSL compatibility. Unfortunately, the reference implementation predates WGSL by over a decade, so I had to translate the HLSL to WGSL manually. As much as was reasonably possible without sacrificing readability, I tried to translate line by line, preserving comments, both to aid reviewing and to allow patches to the HLSL to more easily apply to the WGSL. Most of SMAA's features are supported, but in the interests of making this patch somewhat less huge, I skipped a few of the more exotic ones:

  • The temporal variant is currently unsupported. This is and has been used in shipping games, so supporting temporal SMAA would be useful follow-up work. It would, however, require some significant work on TAA to ensure compatibility, so I opted to skip it in this patch.

  • Depth- and chroma-based edge detection are unimplemented; only luma is. Depth is lower-quality, but faster; chroma is higher-quality, but slower. Luma is the suggested default edge detection algorithm. (Note that depth-based edge detection wouldn't work on WebGL 2 anyway, because of the Naga bug whereby depth sampling is miscompiled in GLSL. This is the same bug that prevents depth of field from working on that platform.)

  • Predicated thresholding is currently unsupported.

  • My implementation is incompatible with SSAA and MSAA, unlike the original; MSAA must be turned off to use SMAA in Bevy. I believe this feature was rarely used in practice.

The anti_aliasing example has been updated to allow experimentation with and testing of the different SMAA quality presets. Along the way, I refactored the example's help text rendering code a bit to eliminate code repetition.

SMAA is fully supported on WebGL 2.

Fixes #9819.

Changelog

Added

  • Subpixel morphological antialiasing, or SMAA, is now available. To use it, add the SmaaSettings component to your Camera.

Screenshot 2024-05-18 134311

@pcwalton pcwalton added C-Enhancement A new feature A-Rendering Drawing game state to the screen labels May 18, 2024
@pcwalton
Copy link
Contributor Author

pcwalton commented May 18, 2024

How do I fix the typo false positives again? @mockersf

This commit implements a large subset of [*subpixel morphological
antialiasing*], better known as SMAA. SMAA is a 2011 antialiasing
technique that detects jaggies in an aliased image and smooths them out.
Despite its age, it's been a continual staple of games for over a
decade. Four quality presets are available: *low*, *medium*, *high*, and
*ultra*. I set the default to *high*, on account of modern GPUs being
significantly faster than they were in 2011.

Like the already-implemented FXAA, SMAA works on an unaliased image.
Unlike FXAA, it requires three passes: (1) edge detection; (2) blending
weight calculation; (3) neighborhood blending. Each of the first two
passes writes an intermediate texture for use by the third pass. The
first pass also writes to a stencil buffer in order to dramatically
reduce the number of pixels that the second pass has to examine. Also
unlike FXAA, two built-in lookup textures are required; I bundle them
into the library in compressed KTX2 format.

The [reference implementation of SMAA] is in HLSL, with abundant use of
preprocessor macros to achieve GLSL compatibility. Unfortunately, the
reference implementation predates WGSL by over a decade, so I had to
translate the HLSL to WGSL manually. As much as was reasonably possible
without sacrificing readability, I tried to translate line by line,
preserving comments, both to aid reviewing and to allow patches to the
HLSL to more easily apply to the WGSL. Most of SMAA's features are
supported, but in the interests of making this patch somewhat less huge,
I skipped a few of the more exotic ones:

* The temporal variant is currently unsupported. This is and has been
  used in shipping games, so supporting temporal SMAA would be useful
  follow-up work. It would, however, require some significant work on
  TAA to ensure compatibility, so I opted to skip it in this patch.

* Depth- and chroma-based edge detection are unimplemented; only luma
  is. Depth is lower-quality, but faster; chroma is higher-quality, but
  slower. Luma is the suggested default edge detection algorithm. (Note
  that depth-based edge detection wouldn't work on WebGL 2 anyway,
  because of the Naga bug whereby depth sampling is miscompiled in GLSL.
  This is the same bug that prevents depth of field from working on that
  platform.)

* Predicated thresholding is currently unsupported.

* My implementation is incompatible with SSAA and MSAA, unlike the
  original; MSAA must be turned off to use SMAA in Bevy. I believe this
  feature was rarely used in practice.

The `anti_aliasing` example has been updated to allow experimentation
with and testing of the different SMAA quality presets. Along the way, I
refactored the example's help text rendering code a bit to eliminate
code repetition.

SMAA is fully supported on WebGL 2.

[*subpixel morphological antialiasing*]: https://www.iryoku.com/smaa/

[reference implementation of SMAA]: https://github.com/iryoku/smaa
@alice-i-cecile alice-i-cecile added D-Complex Quite challenging from either a design or technical perspective. Ask for help! X-Uncontroversial This work is generally agreed upon S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 18, 2024
@alice-i-cecile
Copy link
Member

How do I fix the typo false positives again? @mockersf

https://github.com/bevyengine/bevy/blob/main/typos.toml#L26

@pcwalton
Copy link
Contributor Author

The CI failure is odd since it works here. I can try on Linux.

@pcwalton
Copy link
Contributor Author

I can't reproduce the CI failure on Linux either. Help?

@pcwalton
Copy link
Contributor Author

The problem is fixed and this is ready for review.

@Elabajaba Elabajaba self-requested a review May 21, 2024 04:32
@pcwalton pcwalton added this to the 0.15 milestone May 21, 2024
Copy link
Contributor

@Elabajaba Elabajaba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at the rust side code in smaa/mod.rs yet.

Originally this was filled with code changes for explicit fmas, but at least for AMD gpus the compiled GPU ISA was identical.

Some spots of the giant included readme don't make sense for the wgsl version (eg. section 6. mentioning SMAA_HLSL_/SMAA_GLSL_), but I'm not sure if that really matters.

crates/bevy_core_pipeline/src/smaa/smaa.wgsl Show resolved Hide resolved
crates/bevy_core_pipeline/src/smaa/smaa.wgsl Show resolved Hide resolved

fn decode_diag_bilinear_access_4(e: vec4<f32>) -> vec4<f32> {
let e_rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75);
return round(vec4(e_rb.x, e.g, e_rb.y, e.a));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not actionable, but this is hideous. I understand why you did it as .xgya to avoid .rgga or .xyya, but it doesn't mean I don't hate it 😭.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I didn't like it either. If you have a better proposal I'd be happy to change it, but what I wrote there was the least bad thing I could think of.

crates/bevy_core_pipeline/src/smaa/smaa.wgsl Outdated Show resolved Hide resolved
var end = vec2(0.0);
if (e.r > 0.0) {
let d_xz = search_diag_1(tex_coord, vec2(-1.0, 1.0), &end);
d = vec4(d_xz.x, d.y, d_xz.y, d.w);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not being able to do swizzled assignments and having to use these ugly temp variables makes me sad :/

gpuweb/gpuweb#737

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, me too. It's easy to get wrong as well--I lost hours to debugging an issue from it!

crates/bevy_core_pipeline/src/smaa/smaa.wgsl Outdated Show resolved Hide resolved
crates/bevy_core_pipeline/src/smaa/smaa.wgsl Outdated Show resolved Hide resolved
crates/bevy_core_pipeline/src/smaa/smaa.wgsl Outdated Show resolved Hide resolved
crates/bevy_core_pipeline/src/smaa/smaa.wgsl Outdated Show resolved Hide resolved
crates/bevy_core_pipeline/src/smaa/smaa.wgsl Outdated Show resolved Hide resolved
@pcwalton
Copy link
Contributor Author

Addressed review comments.

@pcwalton pcwalton requested a review from Elabajaba May 26, 2024 22:40
Copy link
Contributor

@Olle-Lukowski Olle-Lukowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: This is the first bigger PR I am reviewing, so I would not count this as a full review, more like half a review.

I just spent a few hours reading, and trying to understand the code as well as possible. I think I understand the big picture, but not all of the finer details. But I tried the example, even tried it in some custom code, and the results are quite nice!

Code wise, I have no feedback other than good job, not too hard to read, proper comments are nice, etc.

TLDR: Good code quality, good results. I think this is ready to be merged, but I am not entitled to comment on implementation details.

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 31, 2024
@alice-i-cecile
Copy link
Member

Thanks @Olle-Lukowski :) I'll take a closer look on Monday to check what I can myself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Enhancement A new feature D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Uncontroversial This work is generally agreed upon
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SMAA (Sub-pixel morphological antialiasing) support
4 participants