Skip to content

Commit

Permalink
Add renderer settings to mitigate blurry text for some graphics devices
Browse files Browse the repository at this point in the history
## Summary of the Pull Request
Adds user settings to adjust rendering behavior to mitigate blurry text on some devices.

## References
- microsoft#778 introduced this, almost certainly.

## PR Checklist
* [x] Closes microsoft#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 microsoft#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 microsoft#5759 and had them try whether one, the other, or both resolved their issue.
  • Loading branch information
miniksa authored and jelster committed May 28, 2020
1 parent 0b08edd commit 21078bc
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 16 deletions.
2 changes: 2 additions & 0 deletions doc/cascadia/SettingsSchema.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Properties listed below affect the entire window, regardless of the profile sett
| `wordDelimiters` | Optional | String | <code>&nbsp;&#x2f;&#x5c;&#x28;&#x29;&#x22;&#x27;&#x2d;&#x3a;&#x2c;&#x2e;&#x3b;&#x3c;&#x3e;&#x7e;&#x21;&#x40;&#x23;&#x24;&#x25;&#x5e;&#x26;&#x2a;&#x7c;&#x2b;&#x3d;&#x5b;&#x5d;&#x7b;&#x7d;&#x7e;&#x3f;│</code><br>_(`` 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.
Expand Down
12 changes: 10 additions & 2 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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.",
Expand Down Expand Up @@ -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": [
Expand Down
23 changes: 23 additions & 0 deletions src/cascadia/TerminalApp/GlobalAppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -70,6 +73,8 @@ GlobalAppSettings::GlobalAppSettings() :
_copyOnSelect{ false },
_copyFormatting{ false },
_launchMode{ LaunchMode::DefaultMode },
_forceFullRepaintRendering{ false },
_softwareRendering{ false },
_debugFeatures{ debugFeaturesDefault }
{
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/TerminalApp/GlobalAppSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettings/IControlSettings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ namespace Microsoft.Terminal.Settings

UInt32 SelectionBackground;
Boolean RetroTerminalEffect;
Boolean ForceFullRepaintRendering;
Boolean SoftwareRendering;

TextAntialiasingMode AntialiasingMode;
};
Expand Down
25 changes: 23 additions & 2 deletions src/cascadia/TerminalSettings/TerminalSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
{
}

Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions src/cascadia/TerminalSettings/terminalsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -143,6 +149,8 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
Settings::ScrollbarState _scrollbarState;

bool _retroTerminalEffect;
bool _forceFullRepaintRendering;
bool _softwareRendering;

Settings::TextAntialiasingMode _antialiasingMode;
};
Expand Down
44 changes: 33 additions & 11 deletions src/renderer/dx/DxRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<HWND>(INVALID_HANDLE_VALUE) },
Expand Down Expand Up @@ -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<UINT>(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<UINT>(FeatureLevels.size()),
D3D11_SDK_VERSION,
&_d3dDevice,
nullptr,
&_d3dDeviceContext);
}

if (FAILED(hardwareResult))
{
Expand Down Expand Up @@ -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<IDXGISwapChain1> DxEngine::GetSwapChain()
{
if (_dxgiSwapChain.Get() == nullptr)
Expand Down Expand Up @@ -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();
}
Expand Down
8 changes: 8 additions & 0 deletions src/renderer/dx/DxRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IDXGISwapChain1> GetSwapChain();

// IRenderEngine Members
Expand Down Expand Up @@ -190,6 +194,10 @@ namespace Microsoft::Console::Render
::Microsoft::WRL::ComPtr<ID3D11SamplerState> _samplerState;
::Microsoft::WRL::ComPtr<ID3D11Texture2D> _framebufferCapture;

// Preferences and overrides
bool _softwareRendering;
bool _forceFullRepaintRendering;

D2D1_TEXT_ANTIALIAS_MODE _antialiasingMode;

float _defaultTextBackgroundOpacity;
Expand Down

0 comments on commit 21078bc

Please sign in to comment.