Skip to content

Commit

Permalink
Add software rendering flag for Windows (#1445)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mjcheetham committed Oct 25, 2023
2 parents e549586 + 3183801 commit b30eab3
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 11 deletions.
23 changes: 23 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,28 @@ Defaults to enabled.

---

### credential.guiSoftwareRendering

Force the use of software rendering for GUI prompts.

This is currently only applicable on Windows.

#### Example

```shell
git config --global credential.guiSoftwareRendering true
```

Defaults to false (use hardware acceleration where available).

> [!NOTE]
> Windows on ARM devices defaults to using software rendering to work around a
> known Avalonia issue: <https://github.com/AvaloniaUI/Avalonia/issues/10405>
**Also see: [GCM_GUI_SOFTWARE_RENDERING][gcm-gui-software-rendering]**

---

### credential.autoDetectTimeout

Set the maximum length of time, in milliseconds, that GCM should wait for a
Expand Down Expand Up @@ -978,6 +1000,7 @@ Defaults to disabled.
[gcm-github-authmodes]: environment.md#GCM_GITHUB_AUTHMODES
[gcm-gitlab-authmodes]:environment.md#GCM_GITLAB_AUTHMODES
[gcm-gui-prompt]: environment.md#GCM_GUI_PROMPT
[gcm-gui-software-rendering]: environment.md#GCM_GUI_SOFTWARE_RENDERING
[gcm-http-proxy]: environment.md#GCM_HTTP_PROXY-deprecated
[gcm-interactive]: environment.md#GCM_INTERACTIVE
[gcm-msauth-flow]: environment.md#GCM_MSAUTH_FLOW
Expand Down
31 changes: 31 additions & 0 deletions docs/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,36 @@ Defaults to enabled.

---

### GCM_GUI_SOFTWARE_RENDERING

Force the use of software rendering for GUI prompts.

This is currently only applicable on Windows.

#### Example

##### Windows

```batch
SET GCM_GUI_SOFTWARE_RENDERING=1
```

##### macOS/Linux

```bash
export GCM_GUI_SOFTWARE_RENDERING=1
```

Defaults to false (use hardware acceleration where available).

> [!NOTE]
> Windows on ARM devices defaults to using software rendering to work around a
> known Avalonia issue: <https://github.com/AvaloniaUI/Avalonia/issues/10405>
**Also see: [credential.guiSoftwareRendering][credential-guisoftwarerendering]**

---

### GCM_AUTODETECT_TIMEOUT

Set the maximum length of time, in milliseconds, that GCM should wait for a
Expand Down Expand Up @@ -1111,6 +1141,7 @@ Defaults to disabled.
[credential-githubauthmodes]: configuration.md#credentialgitHubAuthModes
[credential-gitlabauthmodes]: configuration.md#credentialgitLabAuthModes
[credential-guiprompt]: configuration.md#credentialguiprompt
[credential-guisoftwarerendering]: configuration.md#credentialguisoftwarerendering
[credential-httpproxy]: configuration.md#credentialhttpProxy-deprecated
[credential-interactive]: configuration.md#credentialinteractive
[credential-namespace]: configuration.md#credentialnamespace
Expand Down
7 changes: 7 additions & 0 deletions src/shared/Core/ApplicationBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GitCredentialManager.UI;

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

// Set software rendering if defined in settings
if (Context.Settings.UseSoftwareRendering)
{
AvaloniaUi.Initialize(win32SoftwareRendering: true);
}

return RunInternalAsync(args);
}

Expand Down
2 changes: 2 additions & 0 deletions src/shared/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public static class EnvironmentVariables
public const string OAuthClientAuthHeader = "GCM_OAUTH_USE_CLIENT_AUTH_HEADER";
public const string OAuthDefaultUserName = "GCM_OAUTH_DEFAULT_USERNAME";
public const string GcmDevUseLegacyUiHelpers = "GCM_DEV_USELEGACYUIHELPERS";
public const string GcmGuiSoftwareRendering = "GCM_GUI_SOFTWARE_RENDERING";
}

public static class Http
Expand Down Expand Up @@ -160,6 +161,7 @@ public static class Credential
public const string UiHelper = "uiHelper";
public const string DevUseLegacyUiHelpers = "devUseLegacyUiHelpers";
public const string MsAuthUseDefaultAccount = "msauthUseDefaultAccount";
public const string GuiSoftwareRendering = "guiSoftwareRendering";

public const string OAuthAuthenticationModes = "oauthAuthModes";
public const string OAuthClientId = "oauthClientId";
Expand Down
16 changes: 16 additions & 0 deletions src/shared/Core/PlatformUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ public static bool IsDevBox()
#endif
}

/// <summary>
/// Returns true if the current process is running on an ARM processor.
/// </summary>
/// <returns>True if ARM(v6,hf) or ARM64, false otherwise</returns>
public static bool IsArm()
{
switch (RuntimeInformation.OSArchitecture)
{
case Architecture.Arm:
case Architecture.Arm64:
return true;
default:
return false;
}
}

public static bool IsWindowsBrokerSupported()
{
if (!IsWindows())
Expand Down
21 changes: 21 additions & 0 deletions src/shared/Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ public interface ISettings : IDisposable
/// </summary>
bool UseMsAuthDefaultAccount { get; }

/// <summary>
/// True if software rendering should be used for graphical user interfaces, false otherwise.
/// </summary>
bool UseSoftwareRendering { get; }

/// <summary>
/// Get TRACE2 settings.
/// </summary>
Expand Down Expand Up @@ -559,6 +564,22 @@ public bool IsInteractionAllowed
KnownGitCfg.Credential.Trace,
out value) && !value.IsFalsey();

public bool UseSoftwareRendering
{
get
{
// WORKAROUND: Some Windows ARM devices have a graphics driver issue that causes transparent windows
// when using hardware rendering. Until this is fixed, we will default to software rendering on these
// devices. Users can always override this setting back to HW-accelerated rendering if they wish.
bool defaultValue = PlatformUtils.IsWindows() && PlatformUtils.IsArm();

return TryGetSetting(KnownEnvars.GcmGuiSoftwareRendering,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.GuiSoftwareRendering,
out string str) ? str.ToBooleanyOrDefault(defaultValue) : defaultValue;
}
}

public Trace2Settings GetTrace2Settings()
{
var settings = new Trace2Settings();
Expand Down
42 changes: 31 additions & 11 deletions src/shared/Core/UI/AvaloniaUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;
using GitCredentialManager.Interop.Windows.Native;
using GitCredentialManager.UI.Controls;
Expand All @@ -15,6 +14,24 @@ namespace GitCredentialManager.UI
public static class AvaloniaUi
{
private static bool _isAppStarted;
private static bool _win32SoftwareRendering;

/// <summary>
/// Configure the Avalonia application.
/// </summary>
/// <param name="win32SoftwareRendering">True to enable software rendering on Windows, false otherwise.</param>
/// <remarks>
/// This must be invoked before the Avalonia application loop has started.
/// </remarks>
public static void Initialize(bool win32SoftwareRendering)
{
if (_isAppStarted)
{
throw new InvalidOperationException("Setup must be called before the Avalonia application is started.");
}

_win32SoftwareRendering = win32SoftwareRendering;
}

public static Task ShowViewAsync(Func<Control> viewFunc, WindowViewModel viewModel, IntPtr parentHandle, CancellationToken ct) =>
ShowWindowAsync(() => new DialogWindow(viewFunc()), viewModel, parentHandle, ct);
Expand Down Expand Up @@ -46,22 +63,25 @@ public static Task ShowWindowAsync(Func<Window> windowFunc, object dataContext,
// This action only returns on our dispatcher shutdown.
Dispatcher.MainThread.Post(appCancelToken =>
{
AppBuilder.Configure<AvaloniaApp>()
var appBuilder = AppBuilder.Configure<AvaloniaApp>();
#if NETFRAMEWORK
// Set custom rendering options and modes if required
if (PlatformUtils.IsWindows() && _win32SoftwareRendering)
{
appBuilder.With(new Win32PlatformOptions
{ RenderingMode = new[] { Win32RenderingMode.Software } });
}
#endif
appBuilder
#if NETFRAMEWORK
.UseWin32()
.UseSkia()
#else
.UsePlatformDetect()
#endif
.LogToTrace()
// Workaround https://github.com/AvaloniaUI/Avalonia/issues/10296
// by always setting a application lifetime.
.SetupWithLifetime(
new ClassicDesktopStyleApplicationLifetime
{
ShutdownMode = ShutdownMode.OnExplicitShutdown
}
);
.LogToTrace();
appInitialized.Set();
Expand Down
2 changes: 2 additions & 0 deletions src/shared/TestInfrastructure/Objects/TestSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ ProxyConfiguration ISettings.GetProxyConfiguration()

bool ISettings.UseMsAuthDefaultAccount => UseMsAuthDefaultAccount;

bool ISettings.UseSoftwareRendering => false;

#endregion

#region IDisposable
Expand Down

0 comments on commit b30eab3

Please sign in to comment.