Visual artifacts on Adreno GPUs when toggling MSAA Count in runtime

Hello,

We’ve discovered full-screen visual artifacts that appear when the MSAA Count is toggled at runtime.

[Image Removed]

This issue occurs only on specific Adreno GPUs (Adreno 725, 730, and 740). For comparison, everything works fine on the Mali G610.

Additionally, the problem appears only with Vulkan - using OpenGL on the same device produces no artifacts.

It seems the issue is related to how render target or render pass parameters are updated when the MSAA Count changes. Interestingly, the artifacts can be cleared after they appear by changing r.ScreenPercentage to any other value (for example, from 80 to 81). This likely forces a recreation of certain render resources, which resolves the problem.

Once the issue is resolved for MSAA 2x, you can switch back to MSAA 4x (which initially worked fine), but the same type of visual artifacts will appear again - and they can be resolved in the same way, which shows that there are no hardware support issues for a particular sample count.

Is this a known issue? If not, could you please share any insights on what might be causing problems with render resource updates when the MSAA Count is toggled?

[Attachment Removed]

Hi Alexander,

Can you confirm by which mechanism you are toggling MSAA count?

Best regards.

[Attachment Removed]

Sure, it’s r.MSAACount. For testing purposes, I’m toggling it via the console, while in our game we change it in the code when the user adjusts the setting in the UI.

[Attachment Removed]

Hi Alexander,

I have confirmed the issue as occuring on Adreno drivers after V[Content removed] likely corresponding to devices with the chipsets you mentioned. We are discussing this directly with Qualcomm and will report back if there is a workaround

Best regards.

[Attachment Removed]

Hello Stephane,

Got it, thanks! About the fact that changing the screen percentage fixes the artifacts, could this somehow be used to hide the issue? Is there any difference in Vulkan render resource recreation when r.MSAACount and r.ScreenPercentage are changed, which could be fixed in order to resolve these artifacts?

Kind regards.

[Attachment Removed]

Hi Alexander,

Qualcomm confirmed that the issue was fixed in driver version V[Content removed] a probable workaround would be to trigger the screen percentage based reinitialization when MSAACount changes occur.

Best regards.

[Attachment Removed]

Hi Stephane,

I would prefer to implement this additional reinitialization, as I believe most users won’t have the most recent drivers. Could you please help me find the proper reinitialization logic that is executed when the ScreenPercentage changes, so I can try applying it to MSAACount changes?

[Attachment Removed]

Hello, just a kind reminder regarding my previous request

[Attachment Removed]

Hello, I’ve implemented the following fix, and it seems to work well

namespace MSAAToggleFix
{
	static int32 GEnableMSAAToggleFix = 1;
	static FAutoConsoleVariableRef CVarEnableMSAAFix(TEXT("r.MSAAToggleFix"), GEnableMSAAToggleFix,
		TEXT("Enable workaround that forces RT recreation when r.MSAACount changes.\n"), ECVF_Default);
 
	static constexpr int32 RestoreDelayTicks = 2;
	static bool bRestoreScheduled = false;
	static int32 TicksLeft = 0;
	static int32 LastMSAA = -1;
 
	static void OnCVarChanged()
	{
		if (!GEnableMSAAToggleFix)
		{
			return;
		}
 
		IConsoleVariable* MSAACVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.MSAACount"));
		IConsoleVariable* ScreenPercentageCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ScreenPercentage"));
		if (!MSAACVar || !ScreenPercentageCVar)
		{
			return;
		}
 
		const int32 CurrentMSAA = MSAACVar->GetInt();
		if (CurrentMSAA == LastMSAA)
		{
			return;
		}
 
		if (CurrentMSAA > 0 && LastMSAA > 0)
		{
			const float OriginalSP = ScreenPercentageCVar->GetFloat();
 
			ScreenPercentageCVar->SetWithCurrentPriority(OriginalSP - 1.0f);
 
			if (!bRestoreScheduled)
			{
				bRestoreScheduled = true;
 
				TicksLeft = RestoreDelayTicks;
 
				FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([OriginalSP](float) {
					if (--TicksLeft > 0)
					{
						return true; // keep ticking
					}
 
					if (IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ScreenPercentage")))
					{
						CVar->SetWithCurrentPriority(OriginalSP);
					}
 
					bRestoreScheduled = false;
 
					return false; // stop ticker
				}));
			}
		}
 
		LastMSAA = CurrentMSAA;
	}
 
	static FAutoConsoleVariableSink CVarSink(FConsoleCommandDelegate::CreateStatic(&OnCVarChanged));
} // namespace MSAAToggleFix

[Attachment Removed]

Hi Alexander,

That fix implements the manual workaround you initially identified which Ièm glad to hear works in an automation setting. I’ll circle back and communicate engine side workaround once the dev team shares it with me.

Best regards.

[Attachment Removed]