From 21078bc3d1f06a608ebe6d6d8f623de4fe1879b7 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Mon, 11 May 2020 14:54:03 -0700 Subject: [PATCH] Add renderer settings to mitigate blurry text for some graphics devices ## Summary of the Pull Request Adds user settings to adjust rendering behavior to mitigate blurry text on some devices. ## References - #778 introduced this, almost certainly. ## PR Checklist * [x] Closes #5759, mostly * [x] I work here. * [ ] We need community verification that this will help. * [x] Updated schema and schema doc. * [x] Am core contributor. Discussed in Monday sync meeting and w/ @DHowett-MSFT. ## Detailed Description of the Pull Request / Additional comments When we switched from full-screen repaints to incremental rendering, it seems like we exposed a situation where some display drivers and hardware combinations do not handle scroll and/or dirty regions (from `IDXGISwapChain::Present1`) without blurring the data from the previous frame. As we're really close to ship, I'm offering two options to let people in this situation escape it on their own. We hope in the future to figure out what's actually going on here and mitigate it further in software, but until then, these escape hatches are available. 1. `experimental.rendering.forceFullRepaint` - This one restores the pre-778 behavior to the Terminal. On every single frame paint, we'll invalidate the entire screen and repaint it. 2. `experimental.rendering.software` - This one uses the software WARP renderer instead of using the hardware and display driver directly. The theory is that this will sidestep any driver bugs or hardware variations. One, the other, or both of these may be field-applied by users who are experiencing this behavior. Reverting #778 completely would also resolve this, but it would give back our largest performance win in the whole Terminal project. We don't believe that's acceptable when seemingly a majority of the users are experiencing the performance benefit with no detriment to graphical display. ## Validation Steps Performed - [x] Flipped them on and verified with the debugger that they are being applied to the rendering pipeline - [ ] Gave a private copy to community members in #5759 and had them try whether one, the other, or both resolved their issue. --- doc/cascadia/SettingsSchema.md | 2 + doc/cascadia/profiles.schema.json | 12 ++++- .../TerminalApp/GlobalAppSettings.cpp | 23 ++++++++++ src/cascadia/TerminalApp/GlobalAppSettings.h | 6 +++ src/cascadia/TerminalControl/TermControl.cpp | 4 +- .../TerminalSettings/IControlSettings.idl | 2 + .../TerminalSettings/TerminalSettings.cpp | 25 ++++++++++- .../TerminalSettings/terminalsettings.h | 8 ++++ src/renderer/dx/DxRenderer.cpp | 44 ++++++++++++++----- src/renderer/dx/DxRenderer.hpp | 8 ++++ 10 files changed, 118 insertions(+), 16 deletions(-) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index 5d38e8e15669..0ee88a6d5c55 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -22,6 +22,8 @@ Properties listed below affect the entire window, regardless of the profile sett | `wordDelimiters` | Optional | String |  /\()"'-:,.;<>~!@#$%^&*|+=[]{}~?│
_(`│` is `U+2502 BOX DRAWINGS LIGHT VERTICAL`)_ | Determines the delimiters used in a double click selection. | | `confirmCloseAllTabs` | Optional | Boolean | `true` | When set to `true` closing a window with multiple tabs open WILL require confirmation. When set to `false` closing a window with multiple tabs open WILL NOT require confirmation. | | `disabledProfileSources` | Optional | Array[String] | `[]` | Disables all the dynamic profile generators in this list, preventing them from adding their profiles to the list of profiles on startup. This array can contain any combination of `Windows.Terminal.Wsl`, `Windows.Terminal.Azure`, or `Windows.Terminal.PowershellCore`. For more information, see [UsingJsonSettings.md](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md#dynamic-profiles) | +| `experimental.rendering.forceFullRepaint` | Optional | Boolean | `false` | When set to true, we will redraw the entire screen each frame. When set to false, we will render only the updates to the screen between frames. | +| `experimental.rendering.software` | Optional | Boolean | `false` | When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one. | ## Profiles Properties listed below are specific to each unique profile. diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 882347f95e18..8ebcb9746e42 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -300,6 +300,14 @@ }, "type": "array" }, + "experimental.rendering.forceFullRepaint": { + "description": "When set to true, we will redraw the entire screen each frame. When set to false, we will render only the updates to the screen between frames.", + "type": "boolean" + }, + "experimental.rendering.software": { + "description": "When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one.", + "type": "boolean" + }, "initialCols": { "default": 120, "description": "The number of columns displayed in the window upon first load.", @@ -332,7 +340,7 @@ "description": "The number of rows to scroll at a time with the mouse wheel. This will override the system setting if the value is not zero or \"system\".", "maximum": 999, "minimum": 0, - "type": ["integer", "string"] + "type": [ "integer", "string" ] }, "keybindings": { "description": "Properties are specific to each custom key binding.", @@ -383,7 +391,7 @@ "confirmCloseAllTabs": { "default": true, "description": "When set to \"true\" closing a window with multiple tabs open will require confirmation. When set to \"false\", the confirmation dialog will not appear.", - "type":"boolean" + "type": "boolean" } }, "required": [ diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 62c814779f10..5e7ab9552469 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -42,6 +42,9 @@ static constexpr std::wstring_view LightThemeValue{ L"light" }; static constexpr std::wstring_view DarkThemeValue{ L"dark" }; static constexpr std::wstring_view SystemThemeValue{ L"system" }; +static constexpr std::string_view ForceFullRepaintRenderingKey{ "experimental.rendering.forceFullRepaint" }; +static constexpr std::string_view SoftwareRenderingKey{ "experimental.rendering.software" }; + static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" }; #ifdef _DEBUG @@ -70,6 +73,8 @@ GlobalAppSettings::GlobalAppSettings() : _copyOnSelect{ false }, _copyFormatting{ false }, _launchMode{ LaunchMode::DefaultMode }, + _forceFullRepaintRendering{ false }, + _softwareRendering{ false }, _debugFeatures{ debugFeaturesDefault } { } @@ -187,6 +192,16 @@ void GlobalAppSettings::SetConfirmCloseAllTabs(const bool confirmCloseAllTabs) n _confirmCloseAllTabs = confirmCloseAllTabs; } +bool GlobalAppSettings::GetForceFullRepaintRendering() noexcept +{ + return _forceFullRepaintRendering; +} + +bool GlobalAppSettings::GetSoftwareRendering() noexcept +{ + return _softwareRendering; +} + bool GlobalAppSettings::DebugFeaturesEnabled() const noexcept { return _debugFeatures; @@ -230,6 +245,8 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce settings.WordDelimiters(_wordDelimiters); settings.CopyOnSelect(_copyOnSelect); + settings.ForceFullRepaintRendering(_forceFullRepaintRendering); + settings.SoftwareRendering(_softwareRendering); } // Method Description: @@ -259,6 +276,8 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(KeybindingsKey)] = _keybindings->ToJson(); jsonObject[JsonKey(ConfirmCloseAllKey)] = _confirmCloseAllTabs; jsonObject[JsonKey(SnapToGridOnResizeKey)] = _SnapToGridOnResize; + jsonObject[JsonKey(ForceFullRepaintRenderingKey)] = _forceFullRepaintRendering; + jsonObject[JsonKey(SoftwareRenderingKey)] = _softwareRendering; jsonObject[JsonKey(DebugFeaturesKey)] = _debugFeatures; return jsonObject; @@ -350,6 +369,10 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) JsonUtils::GetBool(json, SnapToGridOnResizeKey, _SnapToGridOnResize); + JsonUtils::GetBool(json, ForceFullRepaintRenderingKey, _forceFullRepaintRendering); + + JsonUtils::GetBool(json, SoftwareRenderingKey, _softwareRendering); + // GetBool will only override the current value if the key exists JsonUtils::GetBool(json, DebugFeaturesKey, _debugFeatures); } diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 44435c3d5775..d390403e93ea 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -77,6 +77,9 @@ class TerminalApp::GlobalAppSettings final winrt::TerminalApp::LaunchMode GetLaunchMode() const noexcept; void SetLaunchMode(const winrt::TerminalApp::LaunchMode launchMode); + bool GetForceFullRepaintRendering() noexcept; + bool GetSoftwareRendering() noexcept; + bool DebugFeaturesEnabled() const noexcept; Json::Value ToJson() const; @@ -118,6 +121,9 @@ class TerminalApp::GlobalAppSettings final winrt::TerminalApp::LaunchMode _launchMode; + bool _softwareRendering; + bool _forceFullRepaintRendering; + bool _debugFeatures; static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 94878332c513..7b3aa1dce2df 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -586,9 +586,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _terminal->CreateFromSettings(_settings, renderTarget); - // TODO:GH#3927 - Make it possible to hot-reload this setting. Right + // TODO:GH#3927 - Make it possible to hot-reload these settings. Right // here, the setting will only be used when the Terminal is initialized. dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect()); + dxEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering()); + dxEngine->SetSoftwareRendering(_settings.SoftwareRendering()); // TODO:GH#3927 - hot-reload this one too // Update DxEngine's AntialiasingMode diff --git a/src/cascadia/TerminalSettings/IControlSettings.idl b/src/cascadia/TerminalSettings/IControlSettings.idl index 4f40c32691b8..ce750d01e833 100644 --- a/src/cascadia/TerminalSettings/IControlSettings.idl +++ b/src/cascadia/TerminalSettings/IControlSettings.idl @@ -52,6 +52,8 @@ namespace Microsoft.Terminal.Settings UInt32 SelectionBackground; Boolean RetroTerminalEffect; + Boolean ForceFullRepaintRendering; + Boolean SoftwareRendering; TextAntialiasingMode AntialiasingMode; }; diff --git a/src/cascadia/TerminalSettings/TerminalSettings.cpp b/src/cascadia/TerminalSettings/TerminalSettings.cpp index 46fe1b45d369..45174a5c85bb 100644 --- a/src/cascadia/TerminalSettings/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettings/TerminalSettings.cpp @@ -41,8 +41,9 @@ namespace winrt::Microsoft::Terminal::Settings::implementation _backgroundImageVerticalAlignment{ winrt::Windows::UI::Xaml::VerticalAlignment::Center }, _keyBindings{ nullptr }, _scrollbarState{ ScrollbarState::Visible }, - _antialiasingMode{ TextAntialiasingMode::Grayscale } - + _antialiasingMode{ TextAntialiasingMode::Grayscale }, + _forceFullRepaintRendering{ false }, + _softwareRendering{ false } { } @@ -387,6 +388,26 @@ namespace winrt::Microsoft::Terminal::Settings::implementation _retroTerminalEffect = value; } + bool TerminalSettings::ForceFullRepaintRendering() noexcept + { + return _forceFullRepaintRendering; + } + + void TerminalSettings::ForceFullRepaintRendering(bool value) noexcept + { + _forceFullRepaintRendering = value; + } + + bool TerminalSettings::SoftwareRendering() noexcept + { + return _softwareRendering; + } + + void TerminalSettings::SoftwareRendering(bool value) noexcept + { + _softwareRendering = value; + } + Settings::TextAntialiasingMode TerminalSettings::AntialiasingMode() const noexcept { return _antialiasingMode; diff --git a/src/cascadia/TerminalSettings/terminalsettings.h b/src/cascadia/TerminalSettings/terminalsettings.h index e78fa00901ef..6fa5c16dc9f4 100644 --- a/src/cascadia/TerminalSettings/terminalsettings.h +++ b/src/cascadia/TerminalSettings/terminalsettings.h @@ -104,6 +104,12 @@ namespace winrt::Microsoft::Terminal::Settings::implementation bool RetroTerminalEffect() noexcept; void RetroTerminalEffect(bool value) noexcept; + bool ForceFullRepaintRendering() noexcept; + void ForceFullRepaintRendering(bool value) noexcept; + + bool SoftwareRendering() noexcept; + void SoftwareRendering(bool value) noexcept; + TextAntialiasingMode AntialiasingMode() const noexcept; void AntialiasingMode(winrt::Microsoft::Terminal::Settings::TextAntialiasingMode const& value) noexcept; @@ -143,6 +149,8 @@ namespace winrt::Microsoft::Terminal::Settings::implementation Settings::ScrollbarState _scrollbarState; bool _retroTerminalEffect; + bool _forceFullRepaintRendering; + bool _softwareRendering; Settings::TextAntialiasingMode _antialiasingMode; }; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 1f2cbcdc7cfd..167853fa9dbe 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -84,6 +84,8 @@ DxEngine::DxEngine() : _boxDrawingEffect{}, _haveDeviceResources{ false }, _retroTerminalEffects{ false }, + _forceFullRepaintRendering{ false }, + _softwareRendering{ false }, _antialiasingMode{ D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE }, _defaultTextBackgroundOpacity{ 1.0f }, _hwndTarget{ static_cast(INVALID_HANDLE_VALUE) }, @@ -387,16 +389,23 @@ try // Trying hardware first for maximum performance, then trying WARP (software) renderer second // in case we're running inside a downlevel VM where hardware passthrough isn't enabled like // for Windows 7 in a VM. - const auto hardwareResult = D3D11CreateDevice(nullptr, - D3D_DRIVER_TYPE_HARDWARE, - nullptr, - DeviceFlags, - FeatureLevels.data(), - gsl::narrow_cast(FeatureLevels.size()), - D3D11_SDK_VERSION, - &_d3dDevice, - nullptr, - &_d3dDeviceContext); + HRESULT hardwareResult = E_NOT_SET; + + // If we're not forcing software rendering, try hardware first. + // Otherwise, let the error state fall down and create with the software renderer directly. + if (!_softwareRendering) + { + hardwareResult = D3D11CreateDevice(nullptr, + D3D_DRIVER_TYPE_HARDWARE, + nullptr, + DeviceFlags, + FeatureLevels.data(), + gsl::narrow_cast(FeatureLevels.size()), + D3D11_SDK_VERSION, + &_d3dDevice, + nullptr, + &_d3dDeviceContext); + } if (FAILED(hardwareResult)) { @@ -676,6 +685,16 @@ void DxEngine::SetRetroTerminalEffects(bool enable) noexcept _retroTerminalEffects = enable; } +void DxEngine::SetForceFullRepaintRendering(bool enable) noexcept +{ + _forceFullRepaintRendering = enable; +} + +void DxEngine::SetSoftwareRendering(bool enable) noexcept +{ + _softwareRendering = enable; +} + Microsoft::WRL::ComPtr DxEngine::GetSwapChain() { if (_dxgiSwapChain.Get() == nullptr) @@ -897,11 +916,14 @@ try { RETURN_HR_IF(E_NOT_VALID_STATE, _isPainting); // invalid to start a paint while painting. + // If someone explicitly requested differential rendering off, then we need to invalidate everything + // so the entire frame is repainted. + // // If retro terminal effects are on, we must invalidate everything for them to draw correctly. // Yes, this will further impact the performance of retro terminal effects. // But we're talking about running the entire display pipeline through a shader for // cosmetic effect, so performance isn't likely the top concern with this feature. - if (_retroTerminalEffects) + if (_forceFullRepaintRendering || _retroTerminalEffects) { _invalidMap.set_all(); } diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 22f73c3e2b2b..95d2d1b02f4a 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -55,6 +55,10 @@ namespace Microsoft::Console::Render void SetRetroTerminalEffects(bool enable) noexcept; + void SetForceFullRepaintRendering(bool enable) noexcept; + + void SetSoftwareRendering(bool enable) noexcept; + ::Microsoft::WRL::ComPtr GetSwapChain(); // IRenderEngine Members @@ -190,6 +194,10 @@ namespace Microsoft::Console::Render ::Microsoft::WRL::ComPtr _samplerState; ::Microsoft::WRL::ComPtr _framebufferCapture; + // Preferences and overrides + bool _softwareRendering; + bool _forceFullRepaintRendering; + D2D1_TEXT_ANTIALIAS_MODE _antialiasingMode; float _defaultTextBackgroundOpacity;