H-uru / Plasma

Cyan Worlds's Plasma game engine
http://h-uru.github.io/Plasma/
GNU General Public License v3.0
202 stars 80 forks source link

Plasma Antialiasing Support + Metal Performance #1544

Open colincornaby opened 6 months ago

colincornaby commented 6 months ago

This issue is a summary of the findings while investigating issues with Plasma on Intel Iris 5100 hardware first noted by @Hoikas. Specifically - Plasma with max antialiasing performed far worse than it did on Windows.

Antialiasing in Plasma

Plasma supports four antialiasing modes - with a slider used to change between them in graphics options. 1 2 4 6
1x (AA Off) 2x 4x 6x

6x antialiasing

Plasma's support of 6x antialiasing has been a bit of a mystery. Typical antialiasing modes are 2x/4x/8x.

It appears 6x antialiasing was a Radeon specific feature back around the time Uru was released. References have been found to it for:

I could not find any mention of 6x antialiasing on the Radeon 2000 series or newer. It's unclear if 6x antialiasing continued to be supported on AMD GPUs. Additionally - no other vendor ever supported 6x antialiasing.

The D3D pipeline will automatically pick the next lowest antialiasing setting is one is not available. This means on most GPUs - the AA settings look like this.

1 2 4 6
1x (AA Off) 2x 4x 4x

On most hardware - the two highest settings are identical.

Metal also does not support 6x AA on AMD hardware. It's unclear if any other API besides D3D9 does.

Intel GPUs

2x antialiasing was not supported until the Intel 6000 series of graphics. That means on 5000 series graphics and lower - the AA settings look like this under D3D:

1 2 4 6
1x (AA Off) 1x (AA Off) 4x 4x

On older Intel hardware, the first two AA settings are identical, and the last two AA settings are identical.

Metal Pipeline

The Metal pipeline will select 8x antialiasing where it sees 6x antialiasing - so it's AA settings look like the following: 1 2 4 6
1x (AA Off) 2x 4x 8x

This means it's selecting very different AA settings than the D3D pipeline. For example, on older Intel hardware (such as the Iris 5100) - this is how it compares to Windows.

Platform 1 2 4 6
Windows 1x (AA Off) 1x (AA Off) 4x 4x
Mac (Metal) 1x (AA Off) 1x (AA Off) 4x 8x

The last AA setting will be much more intensive on macOS.

Possible changes

It's unclear what to do about the 6x AA setting. 8x AA seems very heavy, and would change the meaning behind the setting. However - most modern hardware does not implement 6x AA, so the highest AA setting is an illusion.

The Metal pipeline should follow the DX pipeline and not allow higher than 6x AA. 8x AA seems extremely problematic on older hardware.

Deledrius commented 6 months ago

At the very least, it sounds like we need to dump the 6x option and change it to the modern/valid 8x so that it's at least an option where supported.

colincornaby commented 6 months ago

I'm not sure how I would feel about that. The 8x AA mode runs incredibly poorly on @Hoikas's Mac Mini. Most games on that system don't allow me into the 8x mode. So we'd probably need to gate 8x to certain hardware - which would be its own decision to make. 8x has certainly been untested, and it would have a major performance impact.

From my reading - it seems like 6x antialiasing was some sort of "secret sauce" thing on the Radeon side. So it could be that the Radeons had an optimized algorithm for performing it.

We'd also be bumping users from what has been effectively 4x antialiasing (in since newer hardware will treat 6x as 4x) to 8x antialiasing. That's a straight 2x increase in complexity.

dpogue commented 6 months ago

There are other sliders (namely the resolution slider) where the number of options is dynamic based on what can be supported. Can we just make the slider max out at 4x, unless 6x is available?

I'm also fine with dropping 6x support entirely, since that seems like it's not well supported anyways.

colincornaby commented 6 months ago

Strangely - the pipeline class does support querying the highest AA mode (GetMaxAnisotropicSamples). But I haven't seen that used anywhere.

It's also not sufficient - in since drivers/GPUs do not have to support every AA sample below the maximum. (Such as the Iris 5100 which supports 4x/8x but not 2x/6x.)

Hoikas commented 6 months ago

After discussion in #1545, I propose the following. A new enumeration is created that matches the old MSAA modes to retain backwards compatibility and also adds 8x MSAA. Thusly:

enum plAntiAliasingMode
{
    kNone    = 1,
    kMSAA_2x = 2,
    kMSAA_4x = 4,
    kMSAA_6x = 6,
    kMSAA_8x,
};

The enumeration must remain backwards compatible with the old integer sample method - but any new values are unimportant. This enum will be used by all pipelines. My assumption in this enum is that the old AA console command is specifying the number of samples.

plPipeline gains a new virtual function that returns the AA mode supported per-pipeline and as appropriate for whatever system. This method will be std::vector<plAntiAliasingMode> plPipeline::GetSupportedAntiAliasingModes() const = 0;. For D3D9, this can simply be implemented as follows:

std::vector<plAntiAliasingMode> plDXPipeline::GetSupportedAntiAliasingModes() const
{
    return { plAntiAliasingMode::kNone, plAntiAliasingMode::kMSAA_1x, plAntiAliasingMode::kMSAA_2x, plAntiAliasingMode::kMSAA_4x, plAntiAliasingMode::kMSAA_8x };
}

Other pipelines, such as Metal, can use whatever heuristics they like to hide AA modes that, while theoretically supported, would result in an inferior experience. In this case, I would suggest not returning most, if not all, of the AA modes on Intel GPUs on Metal.

The pipelines should be modified such that any attempt to set an AA mode should check against the supported AA modes and basically take the result of min(plPipeline::GetSupportedAAModes(), whateverTheyRequested) as the final AA value.

Further, the options menu should be modified to display a text string describing the selected AA mode, like the resolution slider currently does. For example, None, 2x MSAA, 4x MSAA, etc. This will require modifying xKIGUI.max to get the required text box, but I think it is worth the effort because the slider position will change if we make the user facing portion realize that 6x MSAA is simply nonsense. The actual effect of this on rendering should be a no-op, however. We're going to be using any exact match or the next lowest, whichever comes first.

colincornaby commented 6 months ago

plPipeline gains a new virtual function that returns the AA mode supported per-pipeline and as appropriate for whatever system. This method will be std::vector<plAntiAliasingMode> plPipeline::GetSupportedAntiAliasingModes() const = 0;. For D3D9, this can simply be implemented as follows:

std::vector<plAntiAliasingMode> plDXPipeline::GetSupportedAntiAliasingModes() const
{
    return { plAntiAliasingMode::kNone, plAntiAliasingMode::kMSAA_1x, plAntiAliasingMode::kMSAA_2x, plAntiAliasingMode::kMSAA_4x, plAntiAliasingMode::kMSAA_8x };
}

Edit: Originally there was a comment here that we'd need to make a D3D call to get the supported antialiasing modes. Cyan already does this as part of display enumeration, populated by IFindFSAATypes. We should return the modes in the pCurrMode->fFSAATypes array.

On the topic of 6x MSAA - there do seem to be users out there actively asking for it. There was discussion of this on a FreeCAD bug here (in addition to some OpenGL specific details on implementation): https://tracker.freecad.org/view.php?id=1901

Hoikas commented 6 months ago

That sounds good - we'll just need to be sure to map those entries to the proposed enum.