Skip to content

Commit b30eab3

Browse files
authored
Add software rendering flag for Windows (#1445)
Add ability to disable hardware acceleration when drawing Avalonia-based GUI prompts on Windows. This is a useful workaround for an issue on certain ARM64 devices where the windows appear empty. AvaloniaUI/Avalonia#10405 https://developercommunity.visualstudio.com/t/Git-authentication-dialog-is-invisible-o/10467795
2 parents e549586 + 3183801 commit b30eab3

File tree

8 files changed

+133
-11
lines changed

8 files changed

+133
-11
lines changed

docs/configuration.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,28 @@ Defaults to enabled.
233233

234234
---
235235

236+
### credential.guiSoftwareRendering
237+
238+
Force the use of software rendering for GUI prompts.
239+
240+
This is currently only applicable on Windows.
241+
242+
#### Example
243+
244+
```shell
245+
git config --global credential.guiSoftwareRendering true
246+
```
247+
248+
Defaults to false (use hardware acceleration where available).
249+
250+
> [!NOTE]
251+
> Windows on ARM devices defaults to using software rendering to work around a
252+
> known Avalonia issue: <https://github.com/AvaloniaUI/Avalonia/issues/10405>
253+
254+
**Also see: [GCM_GUI_SOFTWARE_RENDERING][gcm-gui-software-rendering]**
255+
256+
---
257+
236258
### credential.autoDetectTimeout
237259

238260
Set the maximum length of time, in milliseconds, that GCM should wait for a
@@ -978,6 +1000,7 @@ Defaults to disabled.
9781000
[gcm-github-authmodes]: environment.md#GCM_GITHUB_AUTHMODES
9791001
[gcm-gitlab-authmodes]:environment.md#GCM_GITLAB_AUTHMODES
9801002
[gcm-gui-prompt]: environment.md#GCM_GUI_PROMPT
1003+
[gcm-gui-software-rendering]: environment.md#GCM_GUI_SOFTWARE_RENDERING
9811004
[gcm-http-proxy]: environment.md#GCM_HTTP_PROXY-deprecated
9821005
[gcm-interactive]: environment.md#GCM_INTERACTIVE
9831006
[gcm-msauth-flow]: environment.md#GCM_MSAUTH_FLOW

docs/environment.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,36 @@ Defaults to enabled.
272272

273273
---
274274

275+
### GCM_GUI_SOFTWARE_RENDERING
276+
277+
Force the use of software rendering for GUI prompts.
278+
279+
This is currently only applicable on Windows.
280+
281+
#### Example
282+
283+
##### Windows
284+
285+
```batch
286+
SET GCM_GUI_SOFTWARE_RENDERING=1
287+
```
288+
289+
##### macOS/Linux
290+
291+
```bash
292+
export GCM_GUI_SOFTWARE_RENDERING=1
293+
```
294+
295+
Defaults to false (use hardware acceleration where available).
296+
297+
> [!NOTE]
298+
> Windows on ARM devices defaults to using software rendering to work around a
299+
> known Avalonia issue: <https://github.com/AvaloniaUI/Avalonia/issues/10405>
300+
301+
**Also see: [credential.guiSoftwareRendering][credential-guisoftwarerendering]**
302+
303+
---
304+
275305
### GCM_AUTODETECT_TIMEOUT
276306

277307
Set the maximum length of time, in milliseconds, that GCM should wait for a
@@ -1111,6 +1141,7 @@ Defaults to disabled.
11111141
[credential-githubauthmodes]: configuration.md#credentialgitHubAuthModes
11121142
[credential-gitlabauthmodes]: configuration.md#credentialgitLabAuthModes
11131143
[credential-guiprompt]: configuration.md#credentialguiprompt
1144+
[credential-guisoftwarerendering]: configuration.md#credentialguisoftwarerendering
11141145
[credential-httpproxy]: configuration.md#credentialhttpProxy-deprecated
11151146
[credential-interactive]: configuration.md#credentialinteractive
11161147
[credential-namespace]: configuration.md#credentialnamespace

src/shared/Core/ApplicationBase.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Text;
55
using System.Threading;
66
using System.Threading.Tasks;
7+
using GitCredentialManager.UI;
78

89
namespace GitCredentialManager
910
{
@@ -74,6 +75,12 @@ public Task<int> RunAsync(string[] args)
7475
Context.Trace.WriteLine("Tracing of secrets is enabled. Trace output may contain sensitive information.");
7576
}
7677

78+
// Set software rendering if defined in settings
79+
if (Context.Settings.UseSoftwareRendering)
80+
{
81+
AvaloniaUi.Initialize(win32SoftwareRendering: true);
82+
}
83+
7784
return RunInternalAsync(args);
7885
}
7986

src/shared/Core/Constants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ public static class EnvironmentVariables
118118
public const string OAuthClientAuthHeader = "GCM_OAUTH_USE_CLIENT_AUTH_HEADER";
119119
public const string OAuthDefaultUserName = "GCM_OAUTH_DEFAULT_USERNAME";
120120
public const string GcmDevUseLegacyUiHelpers = "GCM_DEV_USELEGACYUIHELPERS";
121+
public const string GcmGuiSoftwareRendering = "GCM_GUI_SOFTWARE_RENDERING";
121122
}
122123

123124
public static class Http
@@ -160,6 +161,7 @@ public static class Credential
160161
public const string UiHelper = "uiHelper";
161162
public const string DevUseLegacyUiHelpers = "devUseLegacyUiHelpers";
162163
public const string MsAuthUseDefaultAccount = "msauthUseDefaultAccount";
164+
public const string GuiSoftwareRendering = "guiSoftwareRendering";
163165

164166
public const string OAuthAuthenticationModes = "oauthAuthModes";
165167
public const string OAuthClientId = "oauthClientId";

src/shared/Core/PlatformUtils.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ public static bool IsDevBox()
5353
#endif
5454
}
5555

56+
/// <summary>
57+
/// Returns true if the current process is running on an ARM processor.
58+
/// </summary>
59+
/// <returns>True if ARM(v6,hf) or ARM64, false otherwise</returns>
60+
public static bool IsArm()
61+
{
62+
switch (RuntimeInformation.OSArchitecture)
63+
{
64+
case Architecture.Arm:
65+
case Architecture.Arm64:
66+
return true;
67+
default:
68+
return false;
69+
}
70+
}
71+
5672
public static bool IsWindowsBrokerSupported()
5773
{
5874
if (!IsWindows())

src/shared/Core/Settings.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ public interface ISettings : IDisposable
184184
/// </summary>
185185
bool UseMsAuthDefaultAccount { get; }
186186

187+
/// <summary>
188+
/// True if software rendering should be used for graphical user interfaces, false otherwise.
189+
/// </summary>
190+
bool UseSoftwareRendering { get; }
191+
187192
/// <summary>
188193
/// Get TRACE2 settings.
189194
/// </summary>
@@ -559,6 +564,22 @@ public bool GetTracingEnabled(out string value) =>
559564
KnownGitCfg.Credential.Trace,
560565
out value) && !value.IsFalsey();
561566

567+
public bool UseSoftwareRendering
568+
{
569+
get
570+
{
571+
// WORKAROUND: Some Windows ARM devices have a graphics driver issue that causes transparent windows
572+
// when using hardware rendering. Until this is fixed, we will default to software rendering on these
573+
// devices. Users can always override this setting back to HW-accelerated rendering if they wish.
574+
bool defaultValue = PlatformUtils.IsWindows() && PlatformUtils.IsArm();
575+
576+
return TryGetSetting(KnownEnvars.GcmGuiSoftwareRendering,
577+
KnownGitCfg.Credential.SectionName,
578+
KnownGitCfg.Credential.GuiSoftwareRendering,
579+
out string str) ? str.ToBooleanyOrDefault(defaultValue) : defaultValue;
580+
}
581+
}
582+
562583
public Trace2Settings GetTrace2Settings()
563584
{
564585
var settings = new Trace2Settings();

src/shared/Core/UI/AvaloniaUi.cs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Threading.Tasks;
44
using Avalonia;
55
using Avalonia.Controls;
6-
using Avalonia.Controls.ApplicationLifetimes;
76
using Avalonia.Threading;
87
using GitCredentialManager.Interop.Windows.Native;
98
using GitCredentialManager.UI.Controls;
@@ -15,6 +14,24 @@ namespace GitCredentialManager.UI
1514
public static class AvaloniaUi
1615
{
1716
private static bool _isAppStarted;
17+
private static bool _win32SoftwareRendering;
18+
19+
/// <summary>
20+
/// Configure the Avalonia application.
21+
/// </summary>
22+
/// <param name="win32SoftwareRendering">True to enable software rendering on Windows, false otherwise.</param>
23+
/// <remarks>
24+
/// This must be invoked before the Avalonia application loop has started.
25+
/// </remarks>
26+
public static void Initialize(bool win32SoftwareRendering)
27+
{
28+
if (_isAppStarted)
29+
{
30+
throw new InvalidOperationException("Setup must be called before the Avalonia application is started.");
31+
}
32+
33+
_win32SoftwareRendering = win32SoftwareRendering;
34+
}
1835

1936
public static Task ShowViewAsync(Func<Control> viewFunc, WindowViewModel viewModel, IntPtr parentHandle, CancellationToken ct) =>
2037
ShowWindowAsync(() => new DialogWindow(viewFunc()), viewModel, parentHandle, ct);
@@ -46,22 +63,25 @@ public static Task ShowWindowAsync(Func<Window> windowFunc, object dataContext,
4663
// This action only returns on our dispatcher shutdown.
4764
Dispatcher.MainThread.Post(appCancelToken =>
4865
{
49-
AppBuilder.Configure<AvaloniaApp>()
66+
var appBuilder = AppBuilder.Configure<AvaloniaApp>();
67+
68+
#if NETFRAMEWORK
69+
// Set custom rendering options and modes if required
70+
if (PlatformUtils.IsWindows() && _win32SoftwareRendering)
71+
{
72+
appBuilder.With(new Win32PlatformOptions
73+
{ RenderingMode = new[] { Win32RenderingMode.Software } });
74+
}
75+
#endif
76+
77+
appBuilder
5078
#if NETFRAMEWORK
5179
.UseWin32()
5280
.UseSkia()
5381
#else
5482
.UsePlatformDetect()
5583
#endif
56-
.LogToTrace()
57-
// Workaround https://github.com/AvaloniaUI/Avalonia/issues/10296
58-
// by always setting a application lifetime.
59-
.SetupWithLifetime(
60-
new ClassicDesktopStyleApplicationLifetime
61-
{
62-
ShutdownMode = ShutdownMode.OnExplicitShutdown
63-
}
64-
);
84+
.LogToTrace();
6585

6686
appInitialized.Set();
6787

src/shared/TestInfrastructure/Objects/TestSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ ProxyConfiguration ISettings.GetProxyConfiguration()
187187

188188
bool ISettings.UseMsAuthDefaultAccount => UseMsAuthDefaultAccount;
189189

190+
bool ISettings.UseSoftwareRendering => false;
191+
190192
#endregion
191193

192194
#region IDisposable

0 commit comments

Comments
 (0)