Merge remote-tracking branch 'origin/main' into dev/migrie/f/non-terminal-content-elevation-warning
This commit is contained in:
commit
fd72b7992e
11
.github/actions/spelling/allow/allow.txt
vendored
11
.github/actions/spelling/allow/allow.txt
vendored
|
@ -1,13 +1,14 @@
|
|||
admins
|
||||
apc
|
||||
Apc
|
||||
bsd
|
||||
calt
|
||||
ccmp
|
||||
changelog
|
||||
cybersecurity
|
||||
Apc
|
||||
clickable
|
||||
clig
|
||||
copyable
|
||||
cybersecurity
|
||||
dalet
|
||||
dcs
|
||||
Dcs
|
||||
|
@ -35,17 +36,17 @@ It'd
|
|||
kje
|
||||
liga
|
||||
lje
|
||||
locl
|
||||
lorem
|
||||
Llast
|
||||
Lmid
|
||||
locl
|
||||
lorem
|
||||
Lorigin
|
||||
maxed
|
||||
mkmk
|
||||
mnt
|
||||
mru
|
||||
noreply
|
||||
nje
|
||||
noreply
|
||||
ogonek
|
||||
ok'd
|
||||
overlined
|
||||
|
|
1
.github/actions/spelling/excludes.txt
vendored
1
.github/actions/spelling/excludes.txt
vendored
|
@ -61,6 +61,7 @@ SUMS$
|
|||
^src/host/runft\.bat$
|
||||
^src/host/runut\.bat$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/atlas/
|
||||
^src/renderer/wddmcon/WddmConRenderer\.
|
||||
^src/terminal/adapter/ut_adapter/run\.bat$
|
||||
^src/terminal/parser/delfuzzpayload\.bat$
|
||||
|
|
|
@ -400,6 +400,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests",
|
|||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-ms-win-core-synch-l1-2-0", "src\api-ms-win-core-synch-l1-2-0\api-ms-win-core-synch-l1-2-0.vcxproj", "{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererAtlas", "src\renderer\atlas\atlas.vcxproj", "{8222900C-8B6C-452A-91AC-BE95DB04B95F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|Any CPU = AuditMode|Any CPU
|
||||
|
@ -3339,6 +3341,46 @@ Global
|
|||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x64.Build.0 = Release|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.ActiveCfg = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.Build.0 = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x64.ActiveCfg = AuditMode|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x64.Build.0 = AuditMode|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.Build.0 = Debug|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x86.Build.0 = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x64.Build.0 = Fuzzing|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x86.Build.0 = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.ActiveCfg = Release|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.Build.0 = Release|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x86.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -3438,6 +3480,7 @@ Global
|
|||
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
<ProjectReference Include="$(SolutionDir)src\types\lib\types.vcxproj">
|
||||
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -3,16 +3,18 @@
|
|||
|
||||
#include "pch.h"
|
||||
#include "ControlCore.h"
|
||||
#include <argb.h>
|
||||
|
||||
#include <DefaultSettings.h>
|
||||
#include <unicode.hpp>
|
||||
#include <Utf16Parser.hpp>
|
||||
#include <Utils.h>
|
||||
#include <WinUser.h>
|
||||
#include <LibraryResources.h>
|
||||
|
||||
#include "EventArgs.h"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
#include "../../buffer/out/search.h"
|
||||
#include "../../renderer/atlas/AtlasEngine.h"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
|
||||
#include "ControlCore.g.cpp"
|
||||
|
||||
|
@ -202,6 +204,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
const double actualHeight,
|
||||
const double compositionScale)
|
||||
{
|
||||
assert(_settings);
|
||||
|
||||
_panelWidth = actualWidth;
|
||||
_panelHeight = actualHeight;
|
||||
_compositionScale = compositionScale;
|
||||
|
@ -222,10 +226,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
return false;
|
||||
}
|
||||
|
||||
// Set up the DX Engine
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
_renderEngine = std::move(dxEngine);
|
||||
if (Feature_AtlasEngine::IsEnabled() && _settings.UseAtlasEngine())
|
||||
{
|
||||
_renderEngine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_renderEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
}
|
||||
|
||||
_renderer->AddRenderEngine(_renderEngine.get());
|
||||
|
||||
// Initialize our font with the renderer
|
||||
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
|
||||
|
@ -271,11 +281,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
||||
_renderEngine->SetIntenseIsBold(_settings.IntenseIsBold());
|
||||
|
||||
_updateAntiAliasingMode(_renderEngine.get());
|
||||
_updateAntiAliasingMode();
|
||||
|
||||
// GH#5098: Inform the engine of the opacity of the default text background.
|
||||
// GH#11315: Always do this, even if they don't have acrylic on.
|
||||
_renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.Opacity()));
|
||||
const auto backgroundIsOpaque = _settings.Opacity() == 1.0 && _settings.BackgroundImage().empty();
|
||||
_renderEngine->SetDefaultTextBackgroundOpacity(static_cast<float>(backgroundIsOpaque));
|
||||
|
||||
THROW_IF_FAILED(_renderEngine->Enable());
|
||||
|
||||
|
@ -616,7 +627,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
||||
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
||||
|
||||
_updateAntiAliasingMode(_renderEngine.get());
|
||||
_updateAntiAliasingMode();
|
||||
|
||||
// Refresh our font with the renderer
|
||||
const auto actualFontOldSize = _actualFont.GetSize();
|
||||
|
@ -650,22 +661,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void ControlCore::_updateAntiAliasingMode(::Microsoft::Console::Render::DxEngine* const dxEngine)
|
||||
void ControlCore::_updateAntiAliasingMode()
|
||||
{
|
||||
// Update DxEngine's AntialiasingMode
|
||||
D2D1_TEXT_ANTIALIAS_MODE mode;
|
||||
|
||||
switch (_settings.AntialiasingMode())
|
||||
{
|
||||
case TextAntialiasingMode::Cleartype:
|
||||
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
|
||||
mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
|
||||
break;
|
||||
case TextAntialiasingMode::Aliased:
|
||||
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
||||
mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
|
||||
break;
|
||||
case TextAntialiasingMode::Grayscale:
|
||||
default:
|
||||
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
||||
mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
|
||||
break;
|
||||
}
|
||||
|
||||
_renderEngine->SetAntialiasingMode(mode);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1296,7 +1309,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
if (_renderEngine)
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(opacity));
|
||||
const auto backgroundIsOpaque = opacity == 1.0 && _settings.BackgroundImage().empty();
|
||||
_renderEngine->SetDefaultTextBackgroundOpacity(static_cast<float>(backgroundIsOpaque));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "EventArgs.h"
|
||||
#include "ControlCore.g.h"
|
||||
#include "../../renderer/base/Renderer.hpp"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#include "../../renderer/uia/UiaRenderer.hpp"
|
||||
#include "../../cascadia/TerminalCore/Terminal.hpp"
|
||||
#include "../buffer/out/search.h"
|
||||
#include "cppwinrt_utils.h"
|
||||
|
@ -188,7 +185,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
// As _renderer has a dependency on _renderEngine (through a raw pointer)
|
||||
// we must ensure the _renderer is deallocated first.
|
||||
// (C++ class members are destroyed in reverse order.)
|
||||
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine{ nullptr };
|
||||
std::unique_ptr<::Microsoft::Console::Render::IRenderEngine> _renderEngine{ nullptr };
|
||||
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr };
|
||||
|
||||
IControlSettings _settings{ nullptr };
|
||||
|
@ -248,7 +245,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
#pragma endregion
|
||||
|
||||
void _raiseReadOnlyWarning();
|
||||
void _updateAntiAliasingMode(::Microsoft::Console::Render::DxEngine* const dxEngine);
|
||||
void _updateAntiAliasingMode();
|
||||
void _connectionOutputHandler(const hstring& hstr);
|
||||
void _updateHoveredCell(const std::optional<til::point> terminalPosition);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "cppwinrt_utils.h"
|
||||
|
||||
#include "ControlCore.h"
|
||||
#include "../../renderer/uia/UiaRenderer.hpp"
|
||||
|
||||
namespace ControlUnitTests
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Microsoft.Terminal.Control
|
|||
|
||||
Boolean UseAcrylic;
|
||||
ScrollbarState ScrollState;
|
||||
|
||||
Boolean UseAtlasEngine;
|
||||
String FontFace;
|
||||
Int32 FontSize;
|
||||
Windows.UI.Text.FontWeight FontWeight;
|
||||
|
|
|
@ -3,17 +3,16 @@
|
|||
|
||||
#include "pch.h"
|
||||
#include "TermControl.h"
|
||||
#include <argb.h>
|
||||
#include <DefaultSettings.h>
|
||||
|
||||
#include <unicode.hpp>
|
||||
#include <Utf16Parser.hpp>
|
||||
#include <Utils.h>
|
||||
#include <LibraryResources.h>
|
||||
|
||||
#include "TermControlAutomationPeer.h"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
#include "../../renderer/atlas/AtlasEngine.h"
|
||||
|
||||
#include "TermControl.g.cpp"
|
||||
#include "TermControlAutomationPeer.h"
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace ::Microsoft::Console::VirtualTerminal;
|
||||
|
@ -1828,13 +1827,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
const winrt::Windows::Foundation::Size initialSize{ cols, rows };
|
||||
|
||||
return GetProposedDimensions(initialSize,
|
||||
settings.FontSize(),
|
||||
settings.FontWeight(),
|
||||
settings.FontFace(),
|
||||
settings.ScrollState(),
|
||||
settings.Padding(),
|
||||
dpi);
|
||||
return GetProposedDimensions(settings, dpi, initialSize);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
|
@ -1855,16 +1848,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
// caller knows what monitor the control is about to appear on.
|
||||
// Return Value:
|
||||
// - a size containing the requested dimensions in pixels.
|
||||
winrt::Windows::Foundation::Size TermControl::GetProposedDimensions(const winrt::Windows::Foundation::Size& initialSizeInChars,
|
||||
const int32_t& fontHeight,
|
||||
const winrt::Windows::UI::Text::FontWeight& fontWeight,
|
||||
const winrt::hstring& fontFace,
|
||||
const ScrollbarState& scrollState,
|
||||
const winrt::hstring& padding,
|
||||
const uint32_t dpi)
|
||||
winrt::Windows::Foundation::Size TermControl::GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi, const winrt::Windows::Foundation::Size& initialSizeInChars)
|
||||
{
|
||||
const auto cols = ::base::saturated_cast<int>(initialSizeInChars.Width);
|
||||
const auto rows = ::base::saturated_cast<int>(initialSizeInChars.Height);
|
||||
const auto fontSize = settings.FontSize();
|
||||
const auto fontWeight = settings.FontWeight();
|
||||
const auto fontFace = settings.FontFace();
|
||||
const auto scrollState = settings.ScrollState();
|
||||
const auto padding = settings.Padding();
|
||||
|
||||
// Initialize our font information.
|
||||
// The font width doesn't terribly matter, we'll only be using the
|
||||
|
@ -1873,28 +1865,39 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
// The family is only used to determine if the font is truetype or
|
||||
// not, but DX doesn't use that info at all.
|
||||
// The Codepage is additionally not actually used by the DX engine at all.
|
||||
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, gsl::narrow_cast<short>(fontHeight) }, CP_UTF8, false };
|
||||
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, gsl::narrow_cast<short>(fontSize) }, CP_UTF8, false };
|
||||
FontInfoDesired desiredFont = { actualFont };
|
||||
|
||||
// Create a DX engine and initialize it with our font and DPI. We'll
|
||||
// then use it to measure how much space the requested rows and columns
|
||||
// will take up.
|
||||
// TODO: MSFT:21254947 - use a static function to do this instead of
|
||||
// instantiating a DxEngine
|
||||
// instantiating a DxEngine/AtlasEngine.
|
||||
// GH#10211 - UNDER NO CIRCUMSTANCE should this fail. If it does, the
|
||||
// whole app will crash instantaneously on launch, which is no good.
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
LOG_IF_FAILED(dxEngine->UpdateDpi(dpi));
|
||||
LOG_IF_FAILED(dxEngine->UpdateFont(desiredFont, actualFont));
|
||||
double scale;
|
||||
if (Feature_AtlasEngine::IsEnabled() && settings.UseAtlasEngine())
|
||||
{
|
||||
auto engine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
LOG_IF_FAILED(engine->UpdateDpi(dpi));
|
||||
LOG_IF_FAILED(engine->UpdateFont(desiredFont, actualFont));
|
||||
scale = engine->GetScaling();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto engine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
LOG_IF_FAILED(engine->UpdateDpi(dpi));
|
||||
LOG_IF_FAILED(engine->UpdateFont(desiredFont, actualFont));
|
||||
scale = engine->GetScaling();
|
||||
}
|
||||
|
||||
const auto scale = dxEngine->GetScaling();
|
||||
const auto fontSize = actualFont.GetSize();
|
||||
const auto actualFontSize = actualFont.GetSize();
|
||||
|
||||
// UWP XAML scrollbars aren't guaranteed to be the same size as the
|
||||
// ComCtl scrollbars, but it's certainly close enough.
|
||||
const auto scrollbarSize = GetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
|
||||
|
||||
double width = cols * fontSize.X;
|
||||
double width = cols * actualFontSize.X;
|
||||
|
||||
// Reserve additional space if scrollbar is intended to be visible
|
||||
if (scrollState == ScrollbarState::Visible)
|
||||
|
@ -1902,7 +1905,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
width += scrollbarSize;
|
||||
}
|
||||
|
||||
double height = rows * fontSize.Y;
|
||||
double height = rows * actualFontSize.Y;
|
||||
const auto thickness = ParseThicknessFromPadding(padding);
|
||||
// GH#2061 - make sure to account for the size the padding _will be_ scaled to
|
||||
width += scale * (thickness.Left + thickness.Right);
|
||||
|
@ -1962,13 +1965,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
const winrt::Windows::Foundation::Size minSize{ 1, 1 };
|
||||
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
const auto dpi = ::base::saturated_cast<uint32_t>(USER_DEFAULT_SCREEN_DPI * scaleFactor);
|
||||
return GetProposedDimensions(minSize,
|
||||
_settings.FontSize(),
|
||||
_settings.FontWeight(),
|
||||
_settings.FontFace(),
|
||||
_settings.ScrollState(),
|
||||
_settings.Padding(),
|
||||
dpi);
|
||||
return GetProposedDimensions(_settings, dpi, minSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,13 +92,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
void Settings(IControlSettings newSettings);
|
||||
|
||||
static Windows::Foundation::Size GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi);
|
||||
static Windows::Foundation::Size GetProposedDimensions(const winrt::Windows::Foundation::Size& initialSizeInChars,
|
||||
const int32_t& fontSize,
|
||||
const winrt::Windows::UI::Text::FontWeight& fontWeight,
|
||||
const winrt::hstring& fontFace,
|
||||
const ScrollbarState& scrollState,
|
||||
const winrt::hstring& padding,
|
||||
const uint32_t dpi);
|
||||
static Windows::Foundation::Size GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi, const winrt::Windows::Foundation::Size& initialSizeInChars);
|
||||
|
||||
void BellLightOn();
|
||||
|
||||
|
|
|
@ -147,6 +147,7 @@
|
|||
<ProjectReference Include="..\..\types\lib\types.vcxproj" />
|
||||
<ProjectReference Include="..\..\buffer\out\lib\bufferout.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\base\lib\base.vcxproj" />
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj" />
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj" />
|
||||
<ProjectReference Include="..\..\renderer\uia\lib\uia.vcxproj" />
|
||||
<ProjectReference Include="..\..\terminal\parser\lib\parser.vcxproj" />
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
<ProjectReference Include="$(OpenConsoleDir)src\terminal\parser\lib\parser.vcxproj">
|
||||
<Project>{3ae13314-1939-4dfa-9c14-38ca0834050c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -246,13 +246,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
return _profile.HasUnfocusedAppearance();
|
||||
}
|
||||
|
||||
bool ProfileViewModel::EditableUnfocusedAppearance()
|
||||
bool ProfileViewModel::EditableUnfocusedAppearance() const noexcept
|
||||
{
|
||||
if constexpr (Feature_EditableUnfocusedAppearance::IsEnabled())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return Feature_EditableUnfocusedAppearance::IsEnabled();
|
||||
}
|
||||
|
||||
bool ProfileViewModel::ShowUnfocusedAppearance()
|
||||
|
@ -286,6 +282,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
return _unfocusedAppearanceViewModel;
|
||||
}
|
||||
|
||||
bool ProfileViewModel::AtlasEngineAvailable() const noexcept
|
||||
{
|
||||
return Feature_AtlasEngine::IsEnabled();
|
||||
}
|
||||
|
||||
bool ProfileViewModel::UseParentProcessDirectory()
|
||||
{
|
||||
return StartingDirectory().empty();
|
||||
|
|
|
@ -61,12 +61,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
Editor::AppearanceViewModel DefaultAppearance();
|
||||
Editor::AppearanceViewModel UnfocusedAppearance();
|
||||
bool HasUnfocusedAppearance();
|
||||
bool EditableUnfocusedAppearance();
|
||||
bool EditableUnfocusedAppearance() const noexcept;
|
||||
bool ShowUnfocusedAppearance();
|
||||
|
||||
void CreateUnfocusedAppearance(const Windows::Foundation::Collections::IMapView<hstring, Model::ColorScheme>& schemes,
|
||||
const IHostedInWindow& windowRoot);
|
||||
void DeleteUnfocusedAppearance();
|
||||
bool AtlasEngineAvailable() const noexcept;
|
||||
|
||||
WINRT_PROPERTY(bool, IsBaseLayer, false);
|
||||
|
||||
|
@ -95,6 +95,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
OBSERVABLE_PROJECTED_SETTING(_profile, SnapOnInput);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, AltGrAliasing);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, BellStyle);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, UseAtlasEngine);
|
||||
|
||||
private:
|
||||
Model::Profile _profile;
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
|||
Boolean EditableUnfocusedAppearance { get; };
|
||||
Boolean ShowUnfocusedAppearance { get; };
|
||||
AppearanceViewModel UnfocusedAppearance { get; };
|
||||
Boolean AtlasEngineAvailable { get; };
|
||||
|
||||
void CreateUnfocusedAppearance(Windows.Foundation.Collections.IMapView<String, Microsoft.Terminal.Settings.Model.ColorScheme> Schemes, IHostedInWindow WindowRoot);
|
||||
void DeleteUnfocusedAppearance();
|
||||
|
@ -61,6 +62,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
|||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SnapOnInput);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AltGrAliasing);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.BellStyle, BellStyle);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, UseAtlasEngine);
|
||||
}
|
||||
|
||||
runtimeclass DeleteProfileEventArgs
|
||||
|
|
|
@ -319,7 +319,7 @@
|
|||
</StackPanel>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Visibility="{x:Bind State.Profile.EditableUnfocusedAppearance, Mode=OneWay}">
|
||||
Visibility="{x:Bind State.Profile.EditableUnfocusedAppearance}">
|
||||
<TextBlock x:Uid="Profile_UnfocusedAppearanceTextBlock"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
<Button x:Uid="Profile_CreateUnfocusedAppearanceButton"
|
||||
|
@ -477,6 +477,15 @@
|
|||
IsChecked="{x:Bind IsBellStyleFlagSet(4), BindBack=SetBellStyleTaskbar, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- AtlasEngine -->
|
||||
<local:SettingContainer x:Uid="Profile_UseAtlasEngine"
|
||||
ClearSettingValue="{x:Bind State.Profile.ClearUseAtlasEngine}"
|
||||
HasSettingValue="{x:Bind State.Profile.HasUseAtlasEngine, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind State.Profile.UseAtlasEngineOverrideSource, Mode=OneWay}"
|
||||
Visibility="{x:Bind State.Profile.AtlasEngineAvailable}">
|
||||
<ToggleSwitch IsOn="{x:Bind State.Profile.UseAtlasEngine, Mode=TwoWay}" />
|
||||
</local:SettingContainer>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</PivotItem>
|
||||
|
|
|
@ -927,6 +927,10 @@
|
|||
<value>Controls what happens when the application emits a BEL character.</value>
|
||||
<comment>A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"}</comment>
|
||||
</data>
|
||||
<data name="Profile_UseAtlasEngine.Header" xml:space="preserve">
|
||||
<value>Enable experimental text rendering engine</value>
|
||||
<comment>An option to enable an experimental text rendering engine</comment>
|
||||
</data>
|
||||
<data name="Profile_BellStyleAudible.Content" xml:space="preserve">
|
||||
<value>Audible</value>
|
||||
<comment>An option to choose from for the "bell style" setting. When selected, an audible cue is used to notify the user.</comment>
|
||||
|
|
|
@ -73,7 +73,8 @@ Author(s):
|
|||
X(hstring, Icon, "icon", L"\uE756") \
|
||||
X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Graceful) \
|
||||
X(hstring, TabTitle, "tabTitle") \
|
||||
X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible)
|
||||
X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \
|
||||
X(bool, UseAtlasEngine, "experimental.useAtlasEngine", false)
|
||||
|
||||
#define MTSM_FONT_SETTINGS(X) \
|
||||
X(hstring, FontFace, "face", DEFAULT_FONT_FACE) \
|
||||
|
|
|
@ -79,5 +79,6 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
INHERITABLE_PROFILE_SETTING(Boolean, SnapOnInput);
|
||||
INHERITABLE_PROFILE_SETTING(Boolean, AltGrAliasing);
|
||||
INHERITABLE_PROFILE_SETTING(BellStyle, BellStyle);
|
||||
INHERITABLE_PROFILE_SETTING(Boolean, UseAtlasEngine);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -298,6 +298,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
_SuppressApplicationTitle = profile.SuppressApplicationTitle();
|
||||
}
|
||||
|
||||
_UseAtlasEngine = profile.UseAtlasEngine();
|
||||
_ScrollState = profile.ScrollState();
|
||||
|
||||
_AntialiasingMode = profile.AntialiasingMode();
|
||||
|
|
|
@ -148,6 +148,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
INHERITABLE_SETTING(Model::TerminalSettings, hstring, EnvironmentVariables);
|
||||
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::ScrollbarState, ScrollState, Microsoft::Terminal::Control::ScrollbarState::Visible);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAtlasEngine, false);
|
||||
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale);
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ namespace ControlUnitTests
|
|||
WINRT_PROPERTY(winrt::hstring, EnvironmentVariables);
|
||||
|
||||
WINRT_PROPERTY(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible);
|
||||
WINRT_PROPERTY(bool, UseAtlasEngine, false);
|
||||
|
||||
WINRT_PROPERTY(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale);
|
||||
|
||||
|
|
|
@ -79,4 +79,14 @@
|
|||
<!-- This feature will not ship to Stable until it is complete. -->
|
||||
<alwaysDisabledReleaseTokens />
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_AtlasEngine</name>
|
||||
<description>If enabled, AtlasEngine and the experimental.useAtlasEngine setting are compiled into the project</description>
|
||||
<stage>AlwaysEnabled</stage>
|
||||
<alwaysDisabledBrandingTokens>
|
||||
<brandingToken>Release</brandingToken>
|
||||
<brandingToken>WindowsInbox</brandingToken>
|
||||
</alwaysDisabledBrandingTokens>
|
||||
</feature>
|
||||
</featureStaging>
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
<ProjectReference Include="..\..\renderer\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
<ProjectReference Include="..\..\renderer\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -71,6 +74,7 @@
|
|||
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
|
|
@ -2320,9 +2320,6 @@ void ReadStringWithReadConsoleInputAHelper(HANDLE hIn, PCSTR pszExpectedText, si
|
|||
|
||||
while (cchRead < cchExpectedText)
|
||||
{
|
||||
// expected read is either the size of the buffer or the number of characters remaining, whichever is smaller.
|
||||
DWORD const dwReadExpected = (DWORD)std::min(cbBuffer, cchExpectedText - cchRead);
|
||||
|
||||
DWORD dwRead;
|
||||
if (!VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleInputA(hIn, irRead, (DWORD)cbBuffer, &dwRead), L"Attempt to read input into buffer."))
|
||||
{
|
||||
|
|
|
@ -22,10 +22,7 @@ Revision History:
|
|||
#include "ConsoleArguments.hpp"
|
||||
#include "ApiRoutines.h"
|
||||
|
||||
#include "../renderer/inc/IRenderData.hpp"
|
||||
#include "../renderer/inc/IRenderEngine.hpp"
|
||||
#include "../renderer/inc/IRenderer.hpp"
|
||||
#include "../renderer/inc/IFontDefaultList.hpp"
|
||||
#include "../renderer/base/Renderer.hpp"
|
||||
|
||||
#include "../server/DeviceComm.h"
|
||||
#include "../server/ConDrvDeviceComm.h"
|
||||
|
@ -62,7 +59,7 @@ public:
|
|||
|
||||
std::vector<wchar_t> WordDelimiters;
|
||||
|
||||
Microsoft::Console::Render::IRenderer* pRender;
|
||||
Microsoft::Console::Render::Renderer* pRender;
|
||||
|
||||
Microsoft::Console::Render::IFontDefaultList* pFontDefaultList;
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ Settings::Settings() :
|
|||
_fInterceptCopyPaste(0),
|
||||
_DefaultForeground(INVALID_COLOR),
|
||||
_DefaultBackground(INVALID_COLOR),
|
||||
_fUseDx(false),
|
||||
_fUseDx(UseDx::Disabled),
|
||||
_fCopyColor(false)
|
||||
{
|
||||
_dwScreenBufferSize.X = 80;
|
||||
|
@ -820,12 +820,9 @@ void Settings::SetTerminalScrolling(const bool terminalScrollingEnabled) noexcep
|
|||
_TerminalScrolling = terminalScrollingEnabled;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Determines whether our primary renderer should be DirectX or GDI.
|
||||
// - This is based on user preference and velocity hold back state.
|
||||
// Return Value:
|
||||
// - True means use DirectX renderer. False means use GDI renderer.
|
||||
bool Settings::GetUseDx() const noexcept
|
||||
// Determines whether our primary renderer should be DirectX or GDI.
|
||||
// This is based on user preference and velocity hold back state.
|
||||
UseDx Settings::GetUseDx() const noexcept
|
||||
{
|
||||
return _fUseDx;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,13 @@ constexpr unsigned short MIN_WINDOW_OPACITY = 0x4D; // 0x4D is approximately 30%
|
|||
#include "ConsoleArguments.hpp"
|
||||
#include "../inc/conattrs.hpp"
|
||||
|
||||
enum class UseDx : DWORD
|
||||
{
|
||||
Disabled = 0,
|
||||
DxEngine,
|
||||
AtlasEngine,
|
||||
};
|
||||
|
||||
class Settings
|
||||
{
|
||||
public:
|
||||
|
@ -188,7 +195,7 @@ public:
|
|||
bool IsTerminalScrolling() const noexcept;
|
||||
void SetTerminalScrolling(const bool terminalScrollingEnabled) noexcept;
|
||||
|
||||
bool GetUseDx() const noexcept;
|
||||
UseDx GetUseDx() const noexcept;
|
||||
bool GetCopyColor() const noexcept;
|
||||
|
||||
private:
|
||||
|
@ -232,7 +239,7 @@ private:
|
|||
bool _fAutoReturnOnNewline;
|
||||
bool _fRenderGridWorldwide;
|
||||
bool _fScreenReversed;
|
||||
bool _fUseDx;
|
||||
UseDx _fUseDx;
|
||||
bool _fCopyColor;
|
||||
|
||||
std::array<COLORREF, XTERM_COLOR_TABLE_SIZE> _colorTable;
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
<ProjectReference Include="..\..\internal\internal.vcxproj">
|
||||
<Project>{ef3e32a7-5ff6-42b4-b6e2-96cd7d033f00}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<RootNamespace>win32</RootNamespace>
|
||||
<ProjectName>InteractivityWin32</ProjectName>
|
||||
<TargetName>ConInteractivityWin32Lib</TargetName>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemDefinitionGroup>
|
||||
|
@ -61,6 +61,9 @@
|
|||
<ClInclude Include="..\windowUiaProvider.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
<ProjectReference Include="..\..\..\internal\internal.vcxproj">
|
||||
<Project>{ef3e32a7-5ff6-42b4-b6e2-96cd7d033f00}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -76,4 +79,4 @@
|
|||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.tests.props" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -735,7 +735,6 @@ class UiaTextRangeTests
|
|||
TEST_METHOD(CanMoveByCharacter)
|
||||
{
|
||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().RightInclusive();
|
||||
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||
|
||||
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||
// instead of parsing through thousands of empty lines of text.
|
||||
|
@ -824,7 +823,6 @@ class UiaTextRangeTests
|
|||
TEST_METHOD(CanMoveByLine)
|
||||
{
|
||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||
|
||||
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||
// instead of parsing through thousands of empty lines of text.
|
||||
|
@ -913,7 +911,6 @@ class UiaTextRangeTests
|
|||
TEST_METHOD(CanMoveEndpointByUnitCharacter)
|
||||
{
|
||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||
const SHORT bottomRow = static_cast<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||
|
||||
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||
// instead of parsing through thousands of empty lines of text.
|
||||
|
@ -1197,7 +1194,6 @@ class UiaTextRangeTests
|
|||
|
||||
TEST_METHOD(CanMoveEndpointByUnitDocument)
|
||||
{
|
||||
const SHORT lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
|
||||
const SHORT bottomRow = gsl::narrow<SHORT>(_pTextBuffer->TotalRowCount() - 1);
|
||||
|
||||
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
|
||||
|
|
|
@ -9,17 +9,14 @@
|
|||
#include "window.hpp"
|
||||
#include "windowio.hpp"
|
||||
#include "windowdpiapi.hpp"
|
||||
#include "windowmetrics.hpp"
|
||||
#include "WindowMetrics.hpp"
|
||||
|
||||
#include "../../inc/conint.h"
|
||||
|
||||
#include "../../host/globals.h"
|
||||
#include "../../host/dbcs.h"
|
||||
#include "../../host/getset.h"
|
||||
#include "../../host/misc.h"
|
||||
#include "../../host/_output.h"
|
||||
#include "../../host/output.h"
|
||||
#include "../../host/renderData.hpp"
|
||||
#include "../../host/scrolling.hpp"
|
||||
#include "../../host/srvinit.h"
|
||||
#include "../../host/stream.h"
|
||||
|
@ -29,6 +26,9 @@
|
|||
#include "../../renderer/base/renderer.hpp"
|
||||
#include "../../renderer/gdi/gdirenderer.hpp"
|
||||
|
||||
#if TIL_FEATURE_ATLASENGINE_ENABLED
|
||||
#include "../../renderer/atlas/AtlasEngine.h"
|
||||
#endif
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#endif
|
||||
|
@ -209,16 +209,20 @@ void Window::_UpdateSystemMetrics() const
|
|||
// Ensure we have appropriate system metrics before we start constructing the window.
|
||||
_UpdateSystemMetrics();
|
||||
|
||||
const bool useDx = pSettings->GetUseDx();
|
||||
const auto useDx = pSettings->GetUseDx();
|
||||
GdiEngine* pGdiEngine = nullptr;
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
[[maybe_unused]] DxEngine* pDxEngine = nullptr;
|
||||
DxEngine* pDxEngine = nullptr;
|
||||
#endif
|
||||
#if TIL_FEATURE_ATLASENGINE_ENABLED
|
||||
AtlasEngine* pAtlasEngine = nullptr;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
if (useDx)
|
||||
switch (useDx)
|
||||
{
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
case UseDx::DxEngine:
|
||||
pDxEngine = new DxEngine();
|
||||
// TODO: MSFT:21255595 make this less gross
|
||||
// Manually set the Dx Engine to Hwnd mode. When we're trying to
|
||||
|
@ -227,12 +231,18 @@ void Window::_UpdateSystemMetrics() const
|
|||
// math in the hwnd mode, not the Composition mode.
|
||||
THROW_IF_FAILED(pDxEngine->SetHwnd(nullptr));
|
||||
g.pRender->AddRenderEngine(pDxEngine);
|
||||
}
|
||||
else
|
||||
break;
|
||||
#endif
|
||||
{
|
||||
#if TIL_FEATURE_ATLASENGINE_ENABLED
|
||||
case UseDx::AtlasEngine:
|
||||
pAtlasEngine = new AtlasEngine();
|
||||
g.pRender->AddRenderEngine(pAtlasEngine);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
pGdiEngine = new GdiEngine();
|
||||
g.pRender->AddRenderEngine(pGdiEngine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
|
@ -324,7 +334,7 @@ void Window::_UpdateSystemMetrics() const
|
|||
_hWnd = hWnd;
|
||||
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
if (useDx)
|
||||
if (pDxEngine)
|
||||
{
|
||||
status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pDxEngine->SetHwnd(hWnd))));
|
||||
|
||||
|
@ -334,6 +344,13 @@ void Window::_UpdateSystemMetrics() const
|
|||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#if TIL_FEATURE_ATLASENGINE_ENABLED
|
||||
if (pAtlasEngine)
|
||||
{
|
||||
status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pAtlasEngine->SetHwnd(hWnd))));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pGdiEngine->SetHwnd(hWnd))));
|
||||
|
|
|
@ -63,7 +63,7 @@ const RegistrySerialization::_RegPropertyMap RegistrySerialization::s_PropertyMa
|
|||
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_DEFAULTFOREGROUND, SET_FIELD_AND_SIZE(_DefaultForeground) },
|
||||
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_DEFAULTBACKGROUND, SET_FIELD_AND_SIZE(_DefaultBackground) },
|
||||
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_TERMINALSCROLLING, SET_FIELD_AND_SIZE(_TerminalScrolling) },
|
||||
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) },
|
||||
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) },
|
||||
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_COPYCOLOR, SET_FIELD_AND_SIZE(_fCopyColor) }
|
||||
|
||||
};
|
||||
|
@ -251,7 +251,8 @@ NTSTATUS RegistrySerialization::s_OpenKey(_In_opt_ HKEY const hKey, _In_ PCWSTR
|
|||
[[nodiscard]]
|
||||
NTSTATUS RegistrySerialization::s_DeleteValue(const HKEY hKey, _In_ PCWSTR const pwszValueName)
|
||||
{
|
||||
return NTSTATUS_FROM_WIN32(RegDeleteKeyValueW(hKey, nullptr, pwszValueName));
|
||||
const auto result = RegDeleteKeyValueW(hKey, nullptr, pwszValueName);
|
||||
return result == ERROR_FILE_NOT_FOUND ? S_OK : NTSTATUS_FROM_WIN32(result);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
605
src/renderer/atlas/AtlasEngine.api.cpp
Normal file
605
src/renderer/atlas/AtlasEngine.api.cpp
Normal file
|
@ -0,0 +1,605 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AtlasEngine.h"
|
||||
|
||||
// #### NOTE ####
|
||||
// If you see any code in here that contains "_r." you might be seeing a race condition.
|
||||
// The AtlasEngine::Present() method is called on a background thread without any locks,
|
||||
// while any of the API methods (like AtlasEngine::Invalidate) might be called concurrently.
|
||||
// The usage of _r fields is unsafe as those are accessed and written to by the Present() method.
|
||||
|
||||
#pragma warning(disable : 4100) // '...': unreferenced formal parameter
|
||||
// Disable a bunch of warnings which get in the way of writing performant code.
|
||||
#pragma warning(disable : 26429) // Symbol 'data' is never tested for nullness, it can be marked as not_null (f.23).
|
||||
#pragma warning(disable : 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
|
||||
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
#pragma warning(disable : 26482) // Only index into arrays using constant expressions (bounds.2).
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
// Like gsl::narrow but returns a HRESULT.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26472) // Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow (type.1).
|
||||
template<typename T, typename U>
|
||||
constexpr HRESULT api_narrow(U val, T& out) noexcept
|
||||
{
|
||||
out = static_cast<T>(val);
|
||||
return static_cast<U>(out) != val || (std::is_signed_v<T> != std::is_signed_v<U> && out < T{} != val < U{}) ? HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW) : S_OK;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr HRESULT vec2_narrow(U x, U y, AtlasEngine::vec2<T>& out) noexcept
|
||||
{
|
||||
return api_narrow(x, out.x) | api_narrow(y, out.y);
|
||||
}
|
||||
|
||||
#pragma region IRenderEngine
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::Invalidate(const SMALL_RECT* const psrRegion) noexcept
|
||||
{
|
||||
//assert(psrRegion->Top < psrRegion->Bottom && psrRegion->Top >= 0 && psrRegion->Bottom <= _api.cellCount.y);
|
||||
|
||||
// BeginPaint() protects against invalid out of bounds numbers.
|
||||
_api.invalidatedRows.x = std::min(_api.invalidatedRows.x, gsl::narrow_cast<u16>(psrRegion->Top));
|
||||
_api.invalidatedRows.y = std::max(_api.invalidatedRows.y, gsl::narrow_cast<u16>(psrRegion->Bottom));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept
|
||||
{
|
||||
//assert(psrRegion->Left <= psrRegion->Right && psrRegion->Left >= 0 && psrRegion->Right <= _api.cellCount.x);
|
||||
//assert(psrRegion->Top <= psrRegion->Bottom && psrRegion->Top >= 0 && psrRegion->Bottom <= _api.cellCount.y);
|
||||
|
||||
const auto left = gsl::narrow_cast<u16>(psrRegion->Left);
|
||||
const auto top = gsl::narrow_cast<u16>(psrRegion->Top);
|
||||
const auto right = gsl::narrow_cast<u16>(psrRegion->Right);
|
||||
const auto bottom = gsl::narrow_cast<u16>(psrRegion->Bottom);
|
||||
|
||||
// BeginPaint() protects against invalid out of bounds numbers.
|
||||
_api.invalidatedCursorArea.left = std::min(_api.invalidatedCursorArea.left, left);
|
||||
_api.invalidatedCursorArea.top = std::min(_api.invalidatedCursorArea.top, top);
|
||||
_api.invalidatedCursorArea.right = std::max(_api.invalidatedCursorArea.right, right);
|
||||
_api.invalidatedCursorArea.bottom = std::max(_api.invalidatedCursorArea.bottom, bottom);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateSystem(const RECT* const prcDirtyClient) noexcept
|
||||
{
|
||||
const auto top = prcDirtyClient->top / _api.fontMetrics.cellSize.y;
|
||||
const auto bottom = prcDirtyClient->bottom / _api.fontMetrics.cellSize.y;
|
||||
|
||||
// BeginPaint() protects against invalid out of bounds numbers.
|
||||
SMALL_RECT rect;
|
||||
rect.Top = gsl::narrow_cast<SHORT>(top);
|
||||
rect.Bottom = gsl::narrow_cast<SHORT>(bottom);
|
||||
return Invalidate(&rect);
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateSelection(const std::vector<SMALL_RECT>& rectangles) noexcept
|
||||
{
|
||||
for (const auto& rect : rectangles)
|
||||
{
|
||||
// BeginPaint() protects against invalid out of bounds numbers.
|
||||
// TODO: rect can contain invalid out of bounds coordinates when the selection is being
|
||||
// dragged outside of the viewport (and the window begins scrolling automatically).
|
||||
_api.invalidatedRows.x = gsl::narrow_cast<u16>(std::min<int>(_api.invalidatedRows.x, std::max<int>(0, rect.Top)));
|
||||
_api.invalidatedRows.y = gsl::narrow_cast<u16>(std::max<int>(_api.invalidatedRows.y, std::max<int>(0, rect.Bottom)));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept
|
||||
{
|
||||
const auto delta = pcoordDelta->Y;
|
||||
if (delta == 0)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
_api.scrollOffset = gsl::narrow_cast<i16>(clamp<int>(_api.scrollOffset + delta, i16min, i16max));
|
||||
|
||||
// InvalidateScroll() is a "synchronous" API. Any Invalidate()s after
|
||||
// a InvalidateScroll() refer to the new viewport after the scroll.
|
||||
// --> We need to shift the current invalidation rectangles as well.
|
||||
|
||||
_api.invalidatedCursorArea.top = gsl::narrow_cast<u16>(clamp<int>(_api.invalidatedCursorArea.top + delta, u16min, u16max));
|
||||
_api.invalidatedCursorArea.bottom = gsl::narrow_cast<u16>(clamp<int>(_api.invalidatedCursorArea.bottom + delta, u16min, u16max));
|
||||
|
||||
if (delta < 0)
|
||||
{
|
||||
_api.invalidatedRows.x = gsl::narrow_cast<u16>(clamp<int>(_api.invalidatedRows.x + delta, u16min, u16max));
|
||||
_api.invalidatedRows.y = _api.cellCount.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
_api.invalidatedRows.x = 0;
|
||||
_api.invalidatedRows.y = gsl::narrow_cast<u16>(clamp<int>(_api.invalidatedRows.y + delta, u16min, u16max));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateAll() noexcept
|
||||
{
|
||||
_api.invalidatedRows = invalidatedRowsAll;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateCircling(_Out_ bool* const pForcePaint) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pForcePaint);
|
||||
*pForcePaint = false;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::InvalidateTitle(const std::wstring_view proposedTitle) noexcept
|
||||
{
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Title);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, _Out_ FontInfo& fontInfo) noexcept
|
||||
{
|
||||
return UpdateFont(fontInfoDesired, fontInfo, {}, {});
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::UpdateSoftFont(const gsl::span<const uint16_t> bitPattern, const SIZE cellSize, const size_t centeringHint) noexcept
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::UpdateDpi(const int dpi) noexcept
|
||||
{
|
||||
u16 newDPI;
|
||||
RETURN_IF_FAILED(api_narrow(dpi, newDPI));
|
||||
|
||||
if (_api.dpi != newDPI)
|
||||
{
|
||||
_api.dpi = newDPI;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Font);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::GetProposedFont(const FontInfoDesired& fontInfoDesired, _Out_ FontInfo& fontInfo, const int dpi) noexcept
|
||||
try
|
||||
{
|
||||
// One day I'm going to implement GDI for AtlasEngine...
|
||||
// Until then this code is work in progress.
|
||||
#if 0
|
||||
wil::unique_hfont hfont;
|
||||
|
||||
// This block of code (for GDI fonts) is unfinished.
|
||||
if (fontInfoDesired.IsDefaultRasterFont())
|
||||
{
|
||||
hfont.reset(static_cast<HFONT>(GetStockObject(OEM_FIXED_FONT)));
|
||||
RETURN_HR_IF(E_FAIL, !hfont);
|
||||
}
|
||||
else if (requestedFaceName == DEFAULT_RASTER_FONT_FACENAME)
|
||||
{
|
||||
// GDI Windows Font Mapping reference:
|
||||
// https://msdn.microsoft.com/en-us/library/ms969909.aspx
|
||||
|
||||
LOGFONTW lf;
|
||||
lf.lfHeight = -MulDiv(requestedSize.Y, dpi, 72);
|
||||
lf.lfWidth = 0;
|
||||
lf.lfEscapement = 0;
|
||||
lf.lfOrientation = 0;
|
||||
lf.lfWeight = requestedWeight;
|
||||
lf.lfItalic = FALSE;
|
||||
lf.lfUnderline = FALSE;
|
||||
lf.lfStrikeOut = FALSE;
|
||||
lf.lfCharSet = OEM_CHARSET;
|
||||
lf.lfOutPrecision = OUT_RASTER_PRECIS;
|
||||
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
lf.lfQuality = PROOF_QUALITY; // disables scaling for rasterized fonts
|
||||
lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
|
||||
// .size() only includes regular characters, but we also want to copy the trailing \0, so +1 it is.
|
||||
memcpy(&lf.lfFaceName[0], &DEFAULT_RASTER_FONT_FACENAME[0], sizeof(DEFAULT_RASTER_FONT_FACENAME));
|
||||
|
||||
hfont.reset(CreateFontIndirectW(&lf));
|
||||
RETURN_HR_IF(E_FAIL, !hfont);
|
||||
}
|
||||
|
||||
if (hfont)
|
||||
{
|
||||
// wil::unique_any_t's constructor says: "should not be WI_NOEXCEPT (may forward to a throwing constructor)".
|
||||
// The constructor we use by default doesn't throw.
|
||||
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function '...' which may throw exceptions (f.6).
|
||||
wil::unique_hdc hdc{ CreateCompatibleDC(nullptr) };
|
||||
RETURN_HR_IF(E_FAIL, !hdc);
|
||||
|
||||
DeleteObject(SelectObject(hdc.get(), hfont.get()));
|
||||
|
||||
SIZE sz;
|
||||
RETURN_HR_IF(E_FAIL, !GetTextExtentPoint32W(hdc.get(), L"M", 1, &sz));
|
||||
resultingCellSize.X = gsl::narrow<SHORT>(sz.cx);
|
||||
resultingCellSize.Y = gsl::narrow<SHORT>(sz.cy);
|
||||
}
|
||||
#endif
|
||||
|
||||
_resolveFontMetrics(fontInfoDesired, fontInfo);
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
|
||||
{
|
||||
area = gsl::span{ &_api.dirtyRect, 1 };
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::GetFontSize(_Out_ COORD* const pFontSize) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pFontSize);
|
||||
pFontSize->X = gsl::narrow_cast<SHORT>(_api.fontMetrics.cellSize.x);
|
||||
pFontSize->Y = gsl::narrow_cast<SHORT>(_api.fontMetrics.cellSize.y);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pResult);
|
||||
|
||||
wil::com_ptr<IDWriteTextLayout> textLayout;
|
||||
RETURN_IF_FAILED(_sr.dwriteFactory->CreateTextLayout(glyph.data(), gsl::narrow_cast<uint32_t>(glyph.size()), _getTextFormat(false, false), FLT_MAX, FLT_MAX, textLayout.addressof()));
|
||||
|
||||
DWRITE_TEXT_METRICS metrics;
|
||||
RETURN_IF_FAILED(textLayout->GetMetrics(&metrics));
|
||||
|
||||
*pResult = static_cast<unsigned int>(std::ceil(metrics.width)) > _api.fontMetrics.cellSize.x;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::UpdateTitle(const std::wstring_view newTitle) noexcept
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DxRenderer
|
||||
|
||||
HRESULT AtlasEngine::Enable() noexcept
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool AtlasEngine::GetRetroTerminalEffect() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] float AtlasEngine::GetScaling() const noexcept
|
||||
{
|
||||
return static_cast<float>(_api.dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
|
||||
}
|
||||
|
||||
[[nodiscard]] HANDLE AtlasEngine::GetSwapChainHandle()
|
||||
{
|
||||
if (WI_IsFlagSet(_api.invalidations, ApiInvalidations::Device))
|
||||
{
|
||||
_createResources();
|
||||
WI_ClearFlag(_api.invalidations, ApiInvalidations::Device);
|
||||
}
|
||||
|
||||
return _api.swapChainHandle.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::Console::Types::Viewport AtlasEngine::GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept
|
||||
{
|
||||
assert(_api.fontMetrics.cellSize.x != 0);
|
||||
assert(_api.fontMetrics.cellSize.y != 0);
|
||||
return Types::Viewport::FromDimensions(viewInPixels.Origin(), COORD{ gsl::narrow_cast<short>(viewInPixels.Width() / _api.fontMetrics.cellSize.x), gsl::narrow_cast<short>(viewInPixels.Height() / _api.fontMetrics.cellSize.y) });
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::Console::Types::Viewport AtlasEngine::GetViewportInPixels(const Types::Viewport& viewInCharacters) const noexcept
|
||||
{
|
||||
assert(_api.fontMetrics.cellSize.x != 0);
|
||||
assert(_api.fontMetrics.cellSize.y != 0);
|
||||
return Types::Viewport::FromDimensions(viewInCharacters.Origin(), COORD{ gsl::narrow_cast<short>(viewInCharacters.Width() * _api.fontMetrics.cellSize.x), gsl::narrow_cast<short>(viewInCharacters.Height() * _api.fontMetrics.cellSize.y) });
|
||||
}
|
||||
|
||||
void AtlasEngine::SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept
|
||||
{
|
||||
const auto mode = gsl::narrow_cast<u16>(antialiasingMode);
|
||||
if (_api.antialiasingMode != mode)
|
||||
{
|
||||
_api.antialiasingMode = mode;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Font);
|
||||
}
|
||||
}
|
||||
|
||||
void AtlasEngine::SetCallback(std::function<void()> pfn) noexcept
|
||||
{
|
||||
_api.swapChainChangedCallback = std::move(pfn);
|
||||
}
|
||||
|
||||
void AtlasEngine::SetDefaultTextBackgroundOpacity(const float opacity) noexcept
|
||||
{
|
||||
const auto mixin = opacity == 1.0f ? 0xff000000 : 0x00000000;
|
||||
if (_api.backgroundOpaqueMixin != mixin)
|
||||
{
|
||||
_api.backgroundOpaqueMixin = mixin;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::SwapChain);
|
||||
}
|
||||
}
|
||||
|
||||
void AtlasEngine::SetForceFullRepaintRendering(bool enable) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::SetHwnd(const HWND hwnd) noexcept
|
||||
{
|
||||
if (_api.hwnd != hwnd)
|
||||
{
|
||||
_api.hwnd = hwnd;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::SwapChain);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void AtlasEngine::SetPixelShaderPath(std::wstring_view value) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void AtlasEngine::SetRetroTerminalEffect(bool enable) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void AtlasEngine::SetSelectionBackground(const COLORREF color, const float alpha) noexcept
|
||||
{
|
||||
const u32 selectionColor = (color & 0xffffff) | gsl::narrow_cast<u32>(std::lroundf(alpha * 255.0f)) << 24;
|
||||
if (_api.selectionColor != selectionColor)
|
||||
{
|
||||
_api.selectionColor = selectionColor;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Settings);
|
||||
}
|
||||
}
|
||||
|
||||
void AtlasEngine::SetSoftwareRendering(bool enable) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void AtlasEngine::SetIntenseIsBold(bool enable) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void AtlasEngine::SetWarningCallback(std::function<void(HRESULT)> pfn) noexcept
|
||||
{
|
||||
_api.warningCallback = std::move(pfn);
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::SetWindowSize(const SIZE pixels) noexcept
|
||||
{
|
||||
u16x2 newSize;
|
||||
RETURN_IF_FAILED(vec2_narrow(pixels.cx, pixels.cy, newSize));
|
||||
|
||||
// At the time of writing:
|
||||
// When Win+D is pressed, `TriggerRedrawCursor` is called and a render pass is initiated.
|
||||
// As conhost is in the background, GetClientRect will return {0,0} and we'll get called with {0,0}.
|
||||
// This isn't a valid value for _api.sizeInPixel and would crash _recreateSizeDependentResources().
|
||||
if (_api.sizeInPixel != newSize && newSize != u16x2{})
|
||||
{
|
||||
_api.sizeInPixel = newSize;
|
||||
_api.cellCount = _api.sizeInPixel / _api.fontMetrics.cellSize;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void AtlasEngine::ToggleShaderEffects() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept
|
||||
try
|
||||
{
|
||||
std::vector<DWRITE_FONT_FEATURE> fontFeatures;
|
||||
if (!features.empty())
|
||||
{
|
||||
fontFeatures.reserve(features.size() + 3);
|
||||
|
||||
// All of these features are enabled by default by DirectWrite.
|
||||
// If you want to (and can) peek into the source of DirectWrite
|
||||
// you can look for the "GenericDefaultGsubFeatures" and "GenericDefaultGposFeatures" arrays.
|
||||
// Gsub is for GetGlyphs() and Gpos for GetGlyphPlacements().
|
||||
//
|
||||
// GH#10774: Apparently specifying all of the features is just redundant.
|
||||
fontFeatures.emplace_back(DWRITE_FONT_FEATURE{ DWRITE_FONT_FEATURE_TAG_STANDARD_LIGATURES, 1 });
|
||||
fontFeatures.emplace_back(DWRITE_FONT_FEATURE{ DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_LIGATURES, 1 });
|
||||
fontFeatures.emplace_back(DWRITE_FONT_FEATURE{ DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES, 1 });
|
||||
|
||||
for (const auto& p : features)
|
||||
{
|
||||
if (p.first.size() == 4)
|
||||
{
|
||||
const auto s = p.first.data();
|
||||
switch (const auto tag = DWRITE_MAKE_FONT_FEATURE_TAG(s[0], s[1], s[2], s[3]))
|
||||
{
|
||||
case DWRITE_FONT_FEATURE_TAG_STANDARD_LIGATURES:
|
||||
fontFeatures[0].parameter = p.second;
|
||||
break;
|
||||
case DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_LIGATURES:
|
||||
fontFeatures[1].parameter = p.second;
|
||||
break;
|
||||
case DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES:
|
||||
fontFeatures[2].parameter = p.second;
|
||||
break;
|
||||
default:
|
||||
fontFeatures.emplace_back(DWRITE_FONT_FEATURE{ tag, p.second });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DWRITE_FONT_AXIS_VALUE> fontAxisValues;
|
||||
if (!axes.empty())
|
||||
{
|
||||
fontAxisValues.reserve(axes.size() + 3);
|
||||
|
||||
// AtlasEngine::_recreateFontDependentResources() relies on these fields to
|
||||
// exist in this particular order in order to create appropriate default axes.
|
||||
fontAxisValues.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_WEIGHT, -1.0f });
|
||||
fontAxisValues.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_ITALIC, -1.0f });
|
||||
fontAxisValues.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_SLANT, -1.0f });
|
||||
|
||||
for (const auto& p : axes)
|
||||
{
|
||||
if (p.first.size() == 4)
|
||||
{
|
||||
const auto s = p.first.data();
|
||||
switch (const auto tag = DWRITE_MAKE_FONT_AXIS_TAG(s[0], s[1], s[2], s[3]))
|
||||
{
|
||||
case DWRITE_FONT_AXIS_TAG_WEIGHT:
|
||||
fontAxisValues[0].value = p.second;
|
||||
break;
|
||||
case DWRITE_FONT_AXIS_TAG_ITALIC:
|
||||
fontAxisValues[1].value = p.second;
|
||||
break;
|
||||
case DWRITE_FONT_AXIS_TAG_SLANT:
|
||||
fontAxisValues[2].value = p.second;
|
||||
break;
|
||||
default:
|
||||
fontAxisValues.emplace_back(DWRITE_FONT_AXIS_VALUE{ tag, p.second });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto previousCellSize = _api.fontMetrics.cellSize;
|
||||
_resolveFontMetrics(fontInfoDesired, fontInfo, &_api.fontMetrics);
|
||||
_api.fontFeatures = std::move(fontFeatures);
|
||||
_api.fontAxisValues = std::move(fontAxisValues);
|
||||
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Font);
|
||||
|
||||
if (previousCellSize != _api.fontMetrics.cellSize)
|
||||
{
|
||||
_api.cellCount = _api.sizeInPixel / _api.fontMetrics.cellSize;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics) const
|
||||
{
|
||||
auto requestedFaceName = fontInfoDesired.GetFaceName().c_str();
|
||||
const auto requestedFamily = fontInfoDesired.GetFamily();
|
||||
auto requestedWeight = fontInfoDesired.GetWeight();
|
||||
auto requestedSize = fontInfoDesired.GetEngineSize();
|
||||
|
||||
if (!requestedFaceName)
|
||||
{
|
||||
requestedFaceName = L"Consolas";
|
||||
}
|
||||
if (!requestedSize.Y)
|
||||
{
|
||||
requestedSize = { 0, 12 };
|
||||
}
|
||||
if (!requestedWeight)
|
||||
{
|
||||
requestedWeight = DWRITE_FONT_WEIGHT_NORMAL;
|
||||
}
|
||||
|
||||
wil::com_ptr<IDWriteFontCollection> systemFontCollection;
|
||||
THROW_IF_FAILED(_sr.dwriteFactory->GetSystemFontCollection(systemFontCollection.addressof(), false));
|
||||
|
||||
u32 index = 0;
|
||||
BOOL exists = false;
|
||||
THROW_IF_FAILED(systemFontCollection->FindFamilyName(requestedFaceName, &index, &exists));
|
||||
THROW_HR_IF(DWRITE_E_NOFONT, !exists);
|
||||
|
||||
wil::com_ptr<IDWriteFontFamily> fontFamily;
|
||||
THROW_IF_FAILED(systemFontCollection->GetFontFamily(index, fontFamily.addressof()));
|
||||
|
||||
wil::com_ptr<IDWriteFont> font;
|
||||
THROW_IF_FAILED(fontFamily->GetFirstMatchingFont(static_cast<DWRITE_FONT_WEIGHT>(requestedWeight), DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, font.addressof()));
|
||||
|
||||
wil::com_ptr<IDWriteFontFace> fontFace;
|
||||
THROW_IF_FAILED(font->CreateFontFace(fontFace.addressof()));
|
||||
|
||||
DWRITE_FONT_METRICS metrics;
|
||||
fontFace->GetMetrics(&metrics);
|
||||
|
||||
// According to Wikipedia:
|
||||
// > One em was traditionally defined as the width of the capital 'M' in the current typeface and point size,
|
||||
// > because the 'M' was commonly cast the full-width of the square blocks [...] which are used in printing presses.
|
||||
// Even today M is often the widest character in a font that supports ASCII.
|
||||
// In the future a more robust solution could be written, until then this simple solution works for most cases.
|
||||
static constexpr u32 codePoint = L'M';
|
||||
u16 glyphIndex;
|
||||
THROW_IF_FAILED(fontFace->GetGlyphIndicesW(&codePoint, 1, &glyphIndex));
|
||||
|
||||
DWRITE_GLYPH_METRICS glyphMetrics;
|
||||
THROW_IF_FAILED(fontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics));
|
||||
|
||||
// Point sizes are commonly treated at a 72 DPI scale
|
||||
// (including by OpenType), whereas DirectWrite uses 96 DPI.
|
||||
// Since we want the height in px we multiply by the display's DPI.
|
||||
const auto fontSizeInPx = std::ceil(requestedSize.Y / 72.0 * _api.dpi);
|
||||
|
||||
const auto designUnitsPerPx = fontSizeInPx / static_cast<double>(metrics.designUnitsPerEm);
|
||||
const auto ascentInPx = static_cast<double>(metrics.ascent) * designUnitsPerPx;
|
||||
const auto descentInPx = static_cast<double>(metrics.descent) * designUnitsPerPx;
|
||||
const auto lineGapInPx = static_cast<double>(metrics.lineGap) * designUnitsPerPx;
|
||||
const auto advanceWidthInPx = static_cast<double>(glyphMetrics.advanceWidth) * designUnitsPerPx;
|
||||
|
||||
const auto halfGapInPx = lineGapInPx / 2.0;
|
||||
const auto baseline = std::ceil(ascentInPx + halfGapInPx);
|
||||
const auto cellWidth = gsl::narrow<u16>(std::ceil(advanceWidthInPx));
|
||||
const auto cellHeight = gsl::narrow<u16>(std::ceil(baseline + descentInPx + halfGapInPx));
|
||||
|
||||
{
|
||||
COORD resultingCellSize;
|
||||
resultingCellSize.X = gsl::narrow<SHORT>(cellWidth);
|
||||
resultingCellSize.Y = gsl::narrow<SHORT>(cellHeight);
|
||||
fontInfo.SetFromEngine(requestedFaceName, requestedFamily, requestedWeight, false, resultingCellSize, requestedSize);
|
||||
}
|
||||
|
||||
if (fontMetrics)
|
||||
{
|
||||
const auto underlineOffsetInPx = static_cast<double>(-metrics.underlinePosition) * designUnitsPerPx;
|
||||
const auto underlineThicknessInPx = static_cast<double>(metrics.underlineThickness) * designUnitsPerPx;
|
||||
const auto strikethroughOffsetInPx = static_cast<double>(-metrics.strikethroughPosition) * designUnitsPerPx;
|
||||
const auto strikethroughThicknessInPx = static_cast<double>(metrics.strikethroughThickness) * designUnitsPerPx;
|
||||
const auto lineThickness = gsl::narrow<u16>(std::round(std::min(underlineThicknessInPx, strikethroughThicknessInPx)));
|
||||
const auto underlinePos = gsl::narrow<u16>(std::round(baseline + underlineOffsetInPx - lineThickness / 2.0));
|
||||
const auto strikethroughPos = gsl::narrow<u16>(std::round(baseline + strikethroughOffsetInPx - lineThickness / 2.0));
|
||||
|
||||
auto fontName = wil::make_process_heap_string(requestedFaceName);
|
||||
const auto fontWeight = gsl::narrow<u16>(requestedWeight);
|
||||
|
||||
// NOTE: From this point onward no early returns or throwing code should exist,
|
||||
// as we might cause _api to be in an inconsistent state otherwise.
|
||||
|
||||
fontMetrics->fontName = std::move(fontName);
|
||||
fontMetrics->fontSizeInDIP = static_cast<float>(fontSizeInPx / static_cast<double>(_api.dpi) * 96.0);
|
||||
fontMetrics->baselineInDIP = static_cast<float>(baseline / static_cast<double>(_api.dpi) * 96.0);
|
||||
fontMetrics->cellSize = { cellWidth, cellHeight };
|
||||
fontMetrics->fontWeight = fontWeight;
|
||||
fontMetrics->underlinePos = underlinePos;
|
||||
fontMetrics->strikethroughPos = strikethroughPos;
|
||||
fontMetrics->lineThickness = lineThickness;
|
||||
}
|
||||
}
|
1458
src/renderer/atlas/AtlasEngine.cpp
Normal file
1458
src/renderer/atlas/AtlasEngine.cpp
Normal file
File diff suppressed because it is too large
Load diff
761
src/renderer/atlas/AtlasEngine.h
Normal file
761
src/renderer/atlas/AtlasEngine.h
Normal file
|
@ -0,0 +1,761 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <d2d1.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
#include "../../renderer/inc/IRenderEngine.hpp"
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
class AtlasEngine final : public IRenderEngine
|
||||
{
|
||||
public:
|
||||
explicit AtlasEngine();
|
||||
|
||||
AtlasEngine(const AtlasEngine&) = delete;
|
||||
AtlasEngine& operator=(const AtlasEngine&) = delete;
|
||||
|
||||
// IRenderEngine
|
||||
[[nodiscard]] HRESULT StartPaint() noexcept override;
|
||||
[[nodiscard]] HRESULT EndPaint() noexcept override;
|
||||
[[nodiscard]] bool RequiresContinuousRedraw() noexcept override;
|
||||
void WaitUntilCanRender() noexcept override;
|
||||
[[nodiscard]] HRESULT Present() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* pForcePaint) noexcept override;
|
||||
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
|
||||
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSystem(const RECT* prcDirtyClient) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<SMALL_RECT>& rectangles) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateScroll(const COORD* pcoordDelta) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* pForcePaint) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
|
||||
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, COORD coord, bool fTrimLeft, bool lineWrapped) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, COORD coordTarget) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintSelection(SMALL_RECT rect) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, gsl::not_null<IRenderData*> pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateSoftFont(gsl::span<const uint16_t> bitPattern, SIZE cellSize, size_t centeringHint) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateViewport(SMALL_RECT srNewViewport) noexcept override;
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateTitle(std::wstring_view newTitle) noexcept override;
|
||||
|
||||
// DxRenderer - getter
|
||||
HRESULT Enable() noexcept override;
|
||||
[[nodiscard]] bool GetRetroTerminalEffect() const noexcept override;
|
||||
[[nodiscard]] float GetScaling() const noexcept override;
|
||||
[[nodiscard]] HANDLE GetSwapChainHandle() override;
|
||||
[[nodiscard]] Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept override;
|
||||
[[nodiscard]] Types::Viewport GetViewportInPixels(const Types::Viewport& viewInCharacters) const noexcept override;
|
||||
// DxRenderer - setter
|
||||
void SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override;
|
||||
void SetCallback(std::function<void()> pfn) noexcept override;
|
||||
void SetDefaultTextBackgroundOpacity(float opacity) noexcept override;
|
||||
void SetForceFullRepaintRendering(bool enable) noexcept override;
|
||||
[[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override;
|
||||
void SetPixelShaderPath(std::wstring_view value) noexcept override;
|
||||
void SetRetroTerminalEffect(bool enable) noexcept override;
|
||||
void SetSelectionBackground(COLORREF color, float alpha = 0.5f) noexcept override;
|
||||
void SetSoftwareRendering(bool enable) noexcept override;
|
||||
void SetIntenseIsBold(bool enable) noexcept override;
|
||||
void SetWarningCallback(std::function<void(HRESULT)> pfn) noexcept override;
|
||||
[[nodiscard]] HRESULT SetWindowSize(SIZE pixels) noexcept override;
|
||||
void ToggleShaderEffects() noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept override;
|
||||
void UpdateHyperlinkHoveredId(uint16_t hoveredId) noexcept override;
|
||||
|
||||
// Some helper classes for the implementation.
|
||||
// public because I don't want to sprinkle the code with friends.
|
||||
public:
|
||||
#define ATLAS_POD_OPS(type) \
|
||||
constexpr bool operator==(const type& rhs) const noexcept \
|
||||
{ \
|
||||
return __builtin_memcmp(this, &rhs, sizeof(rhs)) == 0; \
|
||||
} \
|
||||
\
|
||||
constexpr bool operator!=(const type& rhs) const noexcept \
|
||||
{ \
|
||||
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0; \
|
||||
}
|
||||
|
||||
#define ATLAS_FLAG_OPS(type, underlying) \
|
||||
friend constexpr type operator~(type v) noexcept { return static_cast<type>(~static_cast<underlying>(v)); } \
|
||||
friend constexpr type operator|(type lhs, type rhs) noexcept { return static_cast<type>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs)); } \
|
||||
friend constexpr type operator&(type lhs, type rhs) noexcept { return static_cast<type>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs)); } \
|
||||
friend constexpr void operator|=(type& lhs, type rhs) noexcept { lhs = lhs | rhs; } \
|
||||
friend constexpr void operator&=(type& lhs, type rhs) noexcept { lhs = lhs & rhs; }
|
||||
|
||||
template<typename T>
|
||||
struct vec2
|
||||
{
|
||||
T x{};
|
||||
T y{};
|
||||
|
||||
ATLAS_POD_OPS(vec2)
|
||||
|
||||
constexpr vec2 operator/(const vec2& rhs) noexcept
|
||||
{
|
||||
assert(rhs.x != 0 && rhs.y != 0);
|
||||
return { gsl::narrow_cast<T>(x / rhs.x), gsl::narrow_cast<T>(y / rhs.y) };
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct vec4
|
||||
{
|
||||
T x{};
|
||||
T y{};
|
||||
T z{};
|
||||
T w{};
|
||||
|
||||
ATLAS_POD_OPS(vec4)
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct rect
|
||||
{
|
||||
T left{};
|
||||
T top{};
|
||||
T right{};
|
||||
T bottom{};
|
||||
|
||||
ATLAS_POD_OPS(rect)
|
||||
|
||||
constexpr bool non_empty() noexcept
|
||||
{
|
||||
return (left < right) & (top < bottom);
|
||||
}
|
||||
};
|
||||
|
||||
using u8 = uint8_t;
|
||||
|
||||
using u16 = uint16_t;
|
||||
using u16x2 = vec2<u16>;
|
||||
using u16r = rect<u16>;
|
||||
|
||||
using i16 = int16_t;
|
||||
|
||||
using u32 = uint32_t;
|
||||
using u32x2 = vec2<u32>;
|
||||
|
||||
using i32 = int32_t;
|
||||
|
||||
using f32 = float;
|
||||
using f32x2 = vec2<f32>;
|
||||
using f32x4 = vec4<f32>;
|
||||
|
||||
struct TextAnalyzerResult
|
||||
{
|
||||
u32 textPosition = 0;
|
||||
u32 textLength = 0;
|
||||
|
||||
// These 2 fields represent DWRITE_SCRIPT_ANALYSIS.
|
||||
// Not using DWRITE_SCRIPT_ANALYSIS drops the struct size from 20 down to 12 bytes.
|
||||
u16 script = 0;
|
||||
u8 shapes = 0;
|
||||
|
||||
u8 bidiLevel = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
template<typename T, size_t Alignment = alignof(T)>
|
||||
struct Buffer
|
||||
{
|
||||
constexpr Buffer() noexcept = default;
|
||||
|
||||
explicit Buffer(size_t size) :
|
||||
_data{ allocate(size) },
|
||||
_size{ size }
|
||||
{
|
||||
}
|
||||
|
||||
Buffer(const T* data, size_t size) :
|
||||
_data{ allocate(size) },
|
||||
_size{ size }
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
memcpy(_data, data, size * sizeof(T));
|
||||
}
|
||||
|
||||
~Buffer()
|
||||
{
|
||||
deallocate(_data);
|
||||
}
|
||||
|
||||
Buffer(Buffer&& other) noexcept :
|
||||
_data{ std::exchange(other._data, nullptr) },
|
||||
_size{ std::exchange(other._size, 0) }
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning(suppress : 26432) // If you define or delete any default operation in the type '...', define or delete them all (c.21).
|
||||
Buffer& operator=(Buffer&& other) noexcept
|
||||
{
|
||||
deallocate(_data);
|
||||
_data = std::exchange(other._data, nullptr);
|
||||
_size = std::exchange(other._size, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return _data != nullptr;
|
||||
}
|
||||
|
||||
T& operator[](size_t index) noexcept
|
||||
{
|
||||
assert(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const noexcept
|
||||
{
|
||||
assert(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
T* data() noexcept
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
const T* data() const noexcept
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
size_t size() const noexcept
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
// These two functions don't need to use scoped objects or standard allocators,
|
||||
// since this class is in fact an scoped allocator object itself.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26402) // Return a scoped object instead of a heap-allocated if it has a move constructor (r.3).
|
||||
#pragma warning(disable : 26409) // Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11).
|
||||
static T* allocate(size_t size)
|
||||
{
|
||||
if constexpr (Alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
{
|
||||
return static_cast<T*>(::operator new(size * sizeof(T)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<T*>(::operator new(size * sizeof(T), static_cast<std::align_val_t>(Alignment)));
|
||||
}
|
||||
}
|
||||
|
||||
static void deallocate(T* data) noexcept
|
||||
{
|
||||
if constexpr (Alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||
{
|
||||
::operator delete(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(data, static_cast<std::align_val_t>(Alignment));
|
||||
}
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
T* _data = nullptr;
|
||||
size_t _size = 0;
|
||||
};
|
||||
|
||||
// This structure works similar to how std::string works:
|
||||
// You can think of a std::string as a structure consisting of:
|
||||
// char* data;
|
||||
// size_t size;
|
||||
// size_t capacity;
|
||||
// where data is some backing memory allocated on the heap.
|
||||
//
|
||||
// But std::string employs an optimization called "small string optimization" (SSO).
|
||||
// To simplify things it could be explained as:
|
||||
// If the string capacity is small, then the characters are stored inside the "data"
|
||||
// pointer and you make sure to set the lowest bit in the pointer one way or another.
|
||||
// Heap allocations are always aligned by at least 4-8 bytes on any platform.
|
||||
// If the address of the "data" pointer is not even you know data is stored inline.
|
||||
template<typename T>
|
||||
union SmallObjectOptimizer
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
static_assert(std::has_unique_object_representations_v<T>);
|
||||
|
||||
T* allocated = nullptr;
|
||||
T inlined;
|
||||
|
||||
constexpr SmallObjectOptimizer() = default;
|
||||
|
||||
SmallObjectOptimizer(const SmallObjectOptimizer& other)
|
||||
{
|
||||
const auto otherData = other.data();
|
||||
const auto otherSize = other.size();
|
||||
const auto data = initialize(otherSize);
|
||||
memcpy(data, otherData, otherSize);
|
||||
}
|
||||
|
||||
SmallObjectOptimizer& operator=(const SmallObjectOptimizer& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
delete this;
|
||||
new (this) SmallObjectOptimizer(other);
|
||||
}
|
||||
return &this;
|
||||
}
|
||||
|
||||
SmallObjectOptimizer(SmallObjectOptimizer&& other) noexcept
|
||||
{
|
||||
memcpy(this, &other, std::max(sizeof(allocated), sizeof(inlined)));
|
||||
other.allocated = nullptr;
|
||||
}
|
||||
|
||||
SmallObjectOptimizer& operator=(SmallObjectOptimizer&& other) noexcept
|
||||
{
|
||||
return *new (this) SmallObjectOptimizer(other);
|
||||
}
|
||||
|
||||
~SmallObjectOptimizer()
|
||||
{
|
||||
if (!is_inline())
|
||||
{
|
||||
#pragma warning(suppress : 26408) // Avoid malloc() and free(), prefer the nothrow version of new with delete (r.10).
|
||||
free(allocated);
|
||||
}
|
||||
}
|
||||
|
||||
T* initialize(size_t byteSize)
|
||||
{
|
||||
if (would_inline(byteSize))
|
||||
{
|
||||
return &inlined;
|
||||
}
|
||||
|
||||
#pragma warning(suppress : 26408) // Avoid malloc() and free(), prefer the nothrow version of new with delete (r.10).
|
||||
allocated = THROW_IF_NULL_ALLOC(static_cast<T*>(malloc(byteSize)));
|
||||
return allocated;
|
||||
}
|
||||
|
||||
constexpr bool would_inline(size_t byteSize) const noexcept
|
||||
{
|
||||
return byteSize <= sizeof(T);
|
||||
}
|
||||
|
||||
bool is_inline() const noexcept
|
||||
{
|
||||
return (__builtin_bit_cast(uintptr_t, allocated) & 1) != 0;
|
||||
}
|
||||
|
||||
const T* data() const noexcept
|
||||
{
|
||||
return is_inline() ? &inlined : allocated;
|
||||
}
|
||||
|
||||
size_t size() const noexcept
|
||||
{
|
||||
return is_inline() ? sizeof(inlined) : _msize(allocated);
|
||||
}
|
||||
};
|
||||
|
||||
struct FontMetrics
|
||||
{
|
||||
wil::unique_process_heap_string fontName;
|
||||
float baselineInDIP = 0.0f;
|
||||
float fontSizeInDIP = 0.0f;
|
||||
u16x2 cellSize;
|
||||
u16 fontWeight = 0;
|
||||
u16 underlinePos = 0;
|
||||
u16 strikethroughPos = 0;
|
||||
u16 lineThickness = 0;
|
||||
};
|
||||
|
||||
// These flags are shared with shader_ps.hlsl.
|
||||
// If you change this be sure to copy it over to shader_ps.hlsl.
|
||||
//
|
||||
// clang-format off
|
||||
enum class CellFlags : u32
|
||||
{
|
||||
None = 0x00000000,
|
||||
Inlined = 0x00000001,
|
||||
|
||||
ColoredGlyph = 0x00000002,
|
||||
ThinFont = 0x00000004,
|
||||
|
||||
Cursor = 0x00000008,
|
||||
Selected = 0x00000010,
|
||||
|
||||
BorderLeft = 0x00000020,
|
||||
BorderTop = 0x00000040,
|
||||
BorderRight = 0x00000080,
|
||||
BorderBottom = 0x00000100,
|
||||
Underline = 0x00000200,
|
||||
UnderlineDotted = 0x00000400,
|
||||
UnderlineDouble = 0x00000800,
|
||||
Strikethrough = 0x00001000,
|
||||
};
|
||||
// clang-format on
|
||||
ATLAS_FLAG_OPS(CellFlags, u32)
|
||||
|
||||
// This structure is shared with the GPU shader and needs to follow certain alignment rules.
|
||||
// You can generally assume that only u32 or types of that alignment are allowed.
|
||||
struct Cell
|
||||
{
|
||||
alignas(u32) u16x2 tileIndex;
|
||||
alignas(u32) CellFlags flags = CellFlags::None;
|
||||
u32x2 color;
|
||||
};
|
||||
|
||||
struct AtlasKeyAttributes
|
||||
{
|
||||
u16 inlined : 1;
|
||||
u16 bold : 1;
|
||||
u16 italic : 1;
|
||||
u16 cellCount : 13;
|
||||
|
||||
ATLAS_POD_OPS(AtlasKeyAttributes)
|
||||
};
|
||||
|
||||
struct AtlasKeyData
|
||||
{
|
||||
AtlasKeyAttributes attributes;
|
||||
u16 charCount;
|
||||
wchar_t chars[14];
|
||||
};
|
||||
|
||||
struct AtlasKey
|
||||
{
|
||||
AtlasKey(AtlasKeyAttributes attributes, u16 charCount, const wchar_t* chars)
|
||||
{
|
||||
const auto size = dataSize(charCount);
|
||||
const auto data = _data.initialize(size);
|
||||
attributes.inlined = _data.would_inline(size);
|
||||
data->attributes = attributes;
|
||||
data->charCount = charCount;
|
||||
memcpy(&data->chars[0], chars, static_cast<size_t>(charCount) * sizeof(AtlasKeyData::chars[0]));
|
||||
}
|
||||
|
||||
const AtlasKeyData* data() const noexcept
|
||||
{
|
||||
return _data.data();
|
||||
}
|
||||
|
||||
size_t hash() const noexcept
|
||||
{
|
||||
const auto d = data();
|
||||
#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
|
||||
return std::_Fnv1a_append_bytes(std::_FNV_offset_basis, reinterpret_cast<const u8*>(d), dataSize(d->charCount));
|
||||
}
|
||||
|
||||
bool operator==(const AtlasKey& rhs) const noexcept
|
||||
{
|
||||
const auto a = data();
|
||||
const auto b = rhs.data();
|
||||
return a->charCount == b->charCount && memcmp(a, b, dataSize(a->charCount)) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
SmallObjectOptimizer<AtlasKeyData> _data;
|
||||
|
||||
static constexpr size_t dataSize(u16 charCount) noexcept
|
||||
{
|
||||
// This returns the actual byte size of a AtlasKeyData struct for the given charCount.
|
||||
// The `wchar_t chars[2]` is only a buffer for the inlined variant after
|
||||
// all and the actual charCount can be smaller or larger. Due to this we
|
||||
// remove the size of the `chars` array and add it's true length on top.
|
||||
return sizeof(AtlasKeyData) - sizeof(AtlasKeyData::chars) + static_cast<size_t>(charCount) * sizeof(AtlasKeyData::chars[0]);
|
||||
}
|
||||
};
|
||||
|
||||
struct AtlasKeyHasher
|
||||
{
|
||||
size_t operator()(const AtlasKey& key) const noexcept
|
||||
{
|
||||
return key.hash();
|
||||
}
|
||||
};
|
||||
|
||||
struct AtlasValueData
|
||||
{
|
||||
CellFlags flags = CellFlags::None;
|
||||
u16x2 coords[7];
|
||||
};
|
||||
|
||||
struct AtlasValue
|
||||
{
|
||||
constexpr AtlasValue() = default;
|
||||
|
||||
u16x2* initialize(CellFlags flags, u16 cellCount)
|
||||
{
|
||||
const auto size = dataSize(cellCount);
|
||||
const auto data = _data.initialize(size);
|
||||
WI_SetFlagIf(flags, CellFlags::Inlined, _data.would_inline(size));
|
||||
data->flags = flags;
|
||||
return &data->coords[0];
|
||||
}
|
||||
|
||||
const AtlasValueData* data() const noexcept
|
||||
{
|
||||
return _data.data();
|
||||
}
|
||||
|
||||
private:
|
||||
SmallObjectOptimizer<AtlasValueData> _data;
|
||||
|
||||
static constexpr size_t dataSize(u16 coordCount) noexcept
|
||||
{
|
||||
return sizeof(AtlasValueData) - sizeof(AtlasValueData::coords) + static_cast<size_t>(coordCount) * sizeof(AtlasValueData::coords[0]);
|
||||
}
|
||||
};
|
||||
|
||||
struct AtlasQueueItem
|
||||
{
|
||||
const AtlasKey* key;
|
||||
const AtlasValue* value;
|
||||
float scale;
|
||||
};
|
||||
|
||||
struct CachedCursorOptions
|
||||
{
|
||||
u32 cursorColor = INVALID_COLOR;
|
||||
u16 cursorType = gsl::narrow_cast<u16>(CursorType::Legacy);
|
||||
u8 heightPercentage = 20;
|
||||
|
||||
ATLAS_POD_OPS(CachedCursorOptions)
|
||||
};
|
||||
|
||||
struct BufferLineMetadata
|
||||
{
|
||||
u32x2 colors;
|
||||
CellFlags flags = CellFlags::None;
|
||||
};
|
||||
|
||||
// NOTE: D3D constant buffers sizes must be a multiple of 16 bytes.
|
||||
struct alignas(16) ConstBuffer
|
||||
{
|
||||
// WARNING: Modify this carefully after understanding how HLSL struct packing works.
|
||||
// The gist is:
|
||||
// * Minimum alignment is 4 bytes (like `#pragma pack 4`)
|
||||
// * Members cannot straddle 16 byte boundaries
|
||||
// This means a structure like {u32; u32; u32; u32x2} would require
|
||||
// padding so that it is {u32; u32; u32; <4 byte padding>; u32x2}.
|
||||
alignas(sizeof(f32x4)) f32x4 viewport;
|
||||
alignas(sizeof(f32x4)) f32x4 gammaRatios;
|
||||
alignas(sizeof(f32)) f32 grayscaleEnhancedContrast = 0;
|
||||
alignas(sizeof(u32)) u32 cellCountX = 0;
|
||||
alignas(sizeof(u32x2)) u32x2 cellSize;
|
||||
alignas(sizeof(u32x2)) u32x2 underlinePos;
|
||||
alignas(sizeof(u32x2)) u32x2 strikethroughPos;
|
||||
alignas(sizeof(u32)) u32 backgroundColor = 0;
|
||||
alignas(sizeof(u32)) u32 cursorColor = 0;
|
||||
alignas(sizeof(u32)) u32 selectionColor = 0;
|
||||
#pragma warning(suppress : 4324) // 'ConstBuffer': structure was padded due to alignment specifier
|
||||
};
|
||||
|
||||
// Handled in BeginPaint()
|
||||
enum class ApiInvalidations : u8
|
||||
{
|
||||
None = 0,
|
||||
Title = 1 << 0,
|
||||
Device = 1 << 1,
|
||||
SwapChain = 1 << 2,
|
||||
Size = 1 << 3,
|
||||
Font = 1 << 4,
|
||||
Settings = 1 << 5,
|
||||
};
|
||||
ATLAS_FLAG_OPS(ApiInvalidations, u8)
|
||||
|
||||
// Handled in Present()
|
||||
enum class RenderInvalidations : u8
|
||||
{
|
||||
None = 0,
|
||||
Cursor = 1 << 0,
|
||||
ConstBuffer = 1 << 1,
|
||||
};
|
||||
ATLAS_FLAG_OPS(RenderInvalidations, u8)
|
||||
|
||||
// MSVC STL (version 22000) implements std::clamp<T>(T, T, T) in terms of the generic
|
||||
// std::clamp<T, Predicate>(T, T, T, Predicate) with std::less{} as the argument,
|
||||
// which introduces branching. While not perfect, this is still better than std::clamp.
|
||||
template<typename T>
|
||||
static constexpr T clamp(T val, T min, T max)
|
||||
{
|
||||
return std::max(min, std::min(max, val));
|
||||
}
|
||||
|
||||
// AtlasEngine.cpp
|
||||
[[nodiscard]] HRESULT _handleException(const wil::ResultException& exception) noexcept;
|
||||
__declspec(noinline) void _createResources();
|
||||
void _releaseSwapChain();
|
||||
__declspec(noinline) void _createSwapChain();
|
||||
__declspec(noinline) void _recreateSizeDependentResources();
|
||||
__declspec(noinline) void _recreateFontDependentResources();
|
||||
IDWriteTextFormat* _getTextFormat(bool bold, bool italic) const noexcept;
|
||||
const Buffer<DWRITE_FONT_AXIS_VALUE>& _getTextFormatAxis(bool bold, bool italic) const noexcept;
|
||||
Cell* _getCell(u16 x, u16 y) noexcept;
|
||||
void _setCellFlags(SMALL_RECT coords, CellFlags mask, CellFlags bits) noexcept;
|
||||
u16x2 _allocateAtlasTile() noexcept;
|
||||
void _flushBufferLine();
|
||||
void _emplaceGlyph(IDWriteFontFace* fontFace, float scale, size_t bufferPos1, size_t bufferPos2);
|
||||
|
||||
// AtlasEngine.api.cpp
|
||||
void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics = nullptr) const;
|
||||
|
||||
// AtlasEngine.r.cpp
|
||||
void _setShaderResources() const;
|
||||
static f32x4 _getGammaRatios(float gamma) noexcept;
|
||||
void _updateConstantBuffer() const noexcept;
|
||||
void _adjustAtlasSize();
|
||||
void _reserveScratchpadSize(u16 minWidth);
|
||||
void _processGlyphQueue();
|
||||
void _drawGlyph(const AtlasQueueItem& item) const;
|
||||
void _drawCursor();
|
||||
void _copyScratchpadTile(uint32_t scratchpadIndex, u16x2 target, uint32_t copyFlags = 0) const noexcept;
|
||||
|
||||
static constexpr bool debugGlyphGenerationPerformance = false;
|
||||
static constexpr bool debugGeneralPerformance = false || debugGlyphGenerationPerformance;
|
||||
static constexpr bool continuousRedraw = false || debugGeneralPerformance;
|
||||
|
||||
static constexpr u16 u16min = 0x0000;
|
||||
static constexpr u16 u16max = 0xffff;
|
||||
static constexpr i16 i16min = -0x8000;
|
||||
static constexpr i16 i16max = 0x7fff;
|
||||
static constexpr u16r invalidatedAreaNone = { u16max, u16max, u16min, u16min };
|
||||
static constexpr u16x2 invalidatedRowsNone{ u16max, u16min };
|
||||
static constexpr u16x2 invalidatedRowsAll{ u16min, u16max };
|
||||
|
||||
struct StaticResources
|
||||
{
|
||||
wil::com_ptr<ID2D1Factory> d2dFactory;
|
||||
wil::com_ptr<IDWriteFactory1> dwriteFactory;
|
||||
wil::com_ptr<IDWriteFontFallback> systemFontFallback;
|
||||
wil::com_ptr<IDWriteTextAnalyzer1> textAnalyzer;
|
||||
bool isWindows10OrGreater = true;
|
||||
|
||||
#ifndef NDEBUG
|
||||
wil::unique_folder_change_reader_nothrow sourceCodeWatcher;
|
||||
std::atomic<int64_t> sourceCodeInvalidationTime{ INT64_MAX };
|
||||
#endif
|
||||
} _sr;
|
||||
|
||||
struct Resources
|
||||
{
|
||||
// D3D resources
|
||||
wil::com_ptr<ID3D11Device> device;
|
||||
wil::com_ptr<ID3D11DeviceContext1> deviceContext;
|
||||
wil::com_ptr<IDXGISwapChain1> swapChain;
|
||||
wil::unique_handle frameLatencyWaitableObject;
|
||||
wil::com_ptr<ID3D11RenderTargetView> renderTargetView;
|
||||
wil::com_ptr<ID3D11VertexShader> vertexShader;
|
||||
wil::com_ptr<ID3D11PixelShader> pixelShader;
|
||||
wil::com_ptr<ID3D11Buffer> constantBuffer;
|
||||
wil::com_ptr<ID3D11Buffer> cellBuffer;
|
||||
wil::com_ptr<ID3D11ShaderResourceView> cellView;
|
||||
|
||||
// D2D resources
|
||||
wil::com_ptr<ID3D11Texture2D> atlasBuffer;
|
||||
wil::com_ptr<ID3D11ShaderResourceView> atlasView;
|
||||
wil::com_ptr<ID3D11Texture2D> atlasScratchpad;
|
||||
wil::com_ptr<ID2D1RenderTarget> d2dRenderTarget;
|
||||
wil::com_ptr<ID2D1Brush> brush;
|
||||
wil::com_ptr<IDWriteTextFormat> textFormats[2][2];
|
||||
Buffer<DWRITE_FONT_AXIS_VALUE> textFormatAxes[2][2];
|
||||
wil::com_ptr<IDWriteTypography> typography;
|
||||
|
||||
Buffer<Cell, 32> cells; // invalidated by ApiInvalidations::Size
|
||||
f32x2 cellSizeDIP; // invalidated by ApiInvalidations::Font, caches _api.cellSize but in DIP
|
||||
u16x2 cellSize; // invalidated by ApiInvalidations::Font, caches _api.cellSize
|
||||
u16x2 cellCount; // invalidated by ApiInvalidations::Font|Size, caches _api.cellCount
|
||||
u16 underlinePos = 0;
|
||||
u16 strikethroughPos = 0;
|
||||
u16 lineThickness = 0;
|
||||
u16 dpi = USER_DEFAULT_SCREEN_DPI; // invalidated by ApiInvalidations::Font, caches _api.dpi
|
||||
u16 maxEncounteredCellCount = 0;
|
||||
u16 scratchpadCellWidth = 0;
|
||||
u16x2 atlasSizeInPixelLimit; // invalidated by ApiInvalidations::Font
|
||||
u16x2 atlasSizeInPixel; // invalidated by ApiInvalidations::Font
|
||||
u16x2 atlasPosition;
|
||||
std::unordered_map<AtlasKey, AtlasValue, AtlasKeyHasher> glyphs;
|
||||
std::vector<AtlasQueueItem> glyphQueue;
|
||||
|
||||
f32 gamma = 0;
|
||||
f32 grayscaleEnhancedContrast = 0;
|
||||
u32 backgroundColor = 0xff000000;
|
||||
u32 selectionColor = 0x7fffffff;
|
||||
|
||||
CachedCursorOptions cursorOptions;
|
||||
RenderInvalidations invalidations = RenderInvalidations::None;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// See documentation for IDXGISwapChain2::GetFrameLatencyWaitableObject method:
|
||||
// > For every frame it renders, the app should wait on this handle before starting any rendering operations.
|
||||
// > Note that this requirement includes the first frame the app renders with the swap chain.
|
||||
bool frameLatencyWaitableObjectUsed = false;
|
||||
#endif
|
||||
} _r;
|
||||
|
||||
struct ApiState
|
||||
{
|
||||
// This structure is loosely sorted in chunks from "very often accessed together"
|
||||
// to seldom accessed and/or usually not together.
|
||||
|
||||
std::vector<wchar_t> bufferLine;
|
||||
std::vector<u16> bufferLineColumn;
|
||||
Buffer<BufferLineMetadata> bufferLineMetadata;
|
||||
std::vector<TextAnalyzerResult> analysisResults;
|
||||
Buffer<u16> clusterMap;
|
||||
Buffer<DWRITE_SHAPING_TEXT_PROPERTIES> textProps;
|
||||
Buffer<u16> glyphIndices;
|
||||
Buffer<DWRITE_SHAPING_GLYPH_PROPERTIES> glyphProps;
|
||||
std::vector<DWRITE_FONT_FEATURE> fontFeatures; // changes are flagged as ApiInvalidations::Font|Size
|
||||
std::vector<DWRITE_FONT_AXIS_VALUE> fontAxisValues; // changes are flagged as ApiInvalidations::Font|Size
|
||||
FontMetrics fontMetrics; // changes are flagged as ApiInvalidations::Font|Size
|
||||
|
||||
u16x2 cellCount; // caches `sizeInPixel / cellSize`
|
||||
u16x2 sizeInPixel; // changes are flagged as ApiInvalidations::Size
|
||||
|
||||
// UpdateDrawingBrushes()
|
||||
u32 backgroundOpaqueMixin = 0xff000000; // changes are flagged as ApiInvalidations::Device
|
||||
u32x2 currentColor;
|
||||
AtlasKeyAttributes attributes{};
|
||||
u16 currentRow = 0;
|
||||
CellFlags flags = CellFlags::None;
|
||||
// SetSelectionBackground()
|
||||
u32 selectionColor = 0x7fffffff;
|
||||
|
||||
// dirtyRect is a computed value based on invalidatedRows.
|
||||
til::rectangle dirtyRect;
|
||||
// These "invalidation" fields are reset in EndPaint()
|
||||
u16r invalidatedCursorArea = invalidatedAreaNone;
|
||||
u16x2 invalidatedRows = invalidatedRowsNone; // x is treated as "top" and y as "bottom"
|
||||
i16 scrollOffset = 0;
|
||||
|
||||
std::function<void(HRESULT)> warningCallback;
|
||||
std::function<void()> swapChainChangedCallback;
|
||||
wil::unique_handle swapChainHandle;
|
||||
HWND hwnd = nullptr;
|
||||
u16 dpi = USER_DEFAULT_SCREEN_DPI; // changes are flagged as ApiInvalidations::Font|Size
|
||||
u16 antialiasingMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; // changes are flagged as ApiInvalidations::Font
|
||||
|
||||
ApiInvalidations invalidations = ApiInvalidations::Device;
|
||||
} _api;
|
||||
|
||||
#undef ATLAS_POD_OPS
|
||||
#undef ATLAS_FLAG_OPS
|
||||
};
|
||||
}
|
460
src/renderer/atlas/AtlasEngine.r.cpp
Normal file
460
src/renderer/atlas/AtlasEngine.r.cpp
Normal file
|
@ -0,0 +1,460 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AtlasEngine.h"
|
||||
|
||||
// #### NOTE ####
|
||||
// If you see any code in here that contains "_api." you might be seeing a race condition.
|
||||
// The AtlasEngine::Present() method is called on a background thread without any locks,
|
||||
// while any of the API methods (like AtlasEngine::Invalidate) might be called concurrently.
|
||||
// The usage of the _r field is safe as its members are in practice
|
||||
// only ever written to by the caller of Present() (the "Renderer" class).
|
||||
// The _api fields on the other hand are concurrently written to by others.
|
||||
|
||||
#pragma warning(disable : 4100) // '...': unreferenced formal parameter
|
||||
// Disable a bunch of warnings which get in the way of writing performant code.
|
||||
#pragma warning(disable : 26429) // Symbol 'data' is never tested for nullness, it can be marked as not_null (f.23).
|
||||
#pragma warning(disable : 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
|
||||
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
#pragma warning(disable : 26482) // Only index into arrays using constant expressions (bounds.2).
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
#pragma region IRenderEngine
|
||||
|
||||
// Present() is called without the console buffer lock being held.
|
||||
// --> Put as much in here as possible.
|
||||
[[nodiscard]] HRESULT AtlasEngine::Present() noexcept
|
||||
try
|
||||
{
|
||||
_adjustAtlasSize();
|
||||
_reserveScratchpadSize(_r.maxEncounteredCellCount);
|
||||
_processGlyphQueue();
|
||||
|
||||
if (WI_IsFlagSet(_r.invalidations, RenderInvalidations::Cursor))
|
||||
{
|
||||
_drawCursor();
|
||||
WI_ClearFlag(_r.invalidations, RenderInvalidations::Cursor);
|
||||
}
|
||||
|
||||
// The values the constant buffer depends on are potentially updated after BeginPaint().
|
||||
if (WI_IsFlagSet(_r.invalidations, RenderInvalidations::ConstBuffer))
|
||||
{
|
||||
_updateConstantBuffer();
|
||||
WI_ClearFlag(_r.invalidations, RenderInvalidations::ConstBuffer);
|
||||
}
|
||||
|
||||
{
|
||||
#pragma warning(suppress : 26494) // Variable 'mapped' is uninitialized. Always initialize an object (type.5).
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
THROW_IF_FAILED(_r.deviceContext->Map(_r.cellBuffer.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped));
|
||||
assert(mapped.RowPitch >= _r.cells.size() * sizeof(Cell));
|
||||
memcpy(mapped.pData, _r.cells.data(), _r.cells.size() * sizeof(Cell));
|
||||
_r.deviceContext->Unmap(_r.cellBuffer.get(), 0);
|
||||
}
|
||||
|
||||
// After Present calls, the back buffer needs to explicitly be
|
||||
// re-bound to the D3D11 immediate context before it can be used again.
|
||||
_r.deviceContext->OMSetRenderTargets(1, _r.renderTargetView.addressof(), nullptr);
|
||||
_r.deviceContext->Draw(3, 0);
|
||||
|
||||
// See documentation for IDXGISwapChain2::GetFrameLatencyWaitableObject method:
|
||||
// > For every frame it renders, the app should wait on this handle before starting any rendering operations.
|
||||
// > Note that this requirement includes the first frame the app renders with the swap chain.
|
||||
assert(_r.frameLatencyWaitableObjectUsed);
|
||||
|
||||
// > IDXGISwapChain::Present: Partial Presentation (using a dirty rects or scroll) is not supported
|
||||
// > for SwapChains created with DXGI_SWAP_EFFECT_DISCARD or DXGI_SWAP_EFFECT_FLIP_DISCARD.
|
||||
// ---> No need to call IDXGISwapChain1::Present1.
|
||||
// TODO: Would IDXGISwapChain1::Present1 and its dirty rects have benefits for remote desktop?
|
||||
THROW_IF_FAILED(_r.swapChain->Present(1, 0));
|
||||
|
||||
// On some GPUs with tile based deferred rendering (TBDR) architectures, binding
|
||||
// RenderTargets that already have contents in them (from previous rendering) incurs a
|
||||
// cost for having to copy the RenderTarget contents back into tile memory for rendering.
|
||||
//
|
||||
// On Windows 10 with DXGI_SWAP_EFFECT_FLIP_DISCARD we get this for free.
|
||||
if (!_sr.isWindows10OrGreater)
|
||||
{
|
||||
_r.deviceContext->DiscardView(_r.renderTargetView.get());
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
catch (const wil::ResultException& exception)
|
||||
{
|
||||
return _handleException(exception);
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void AtlasEngine::_setShaderResources() const
|
||||
{
|
||||
_r.deviceContext->VSSetShader(_r.vertexShader.get(), nullptr, 0);
|
||||
_r.deviceContext->PSSetShader(_r.pixelShader.get(), nullptr, 0);
|
||||
|
||||
// Our vertex shader uses a trick from Bill Bilodeau published in
|
||||
// "Vertex Shader Tricks" at GDC14 to draw a fullscreen triangle
|
||||
// without vertex/index buffers. This prepares our context for this.
|
||||
_r.deviceContext->IASetVertexBuffers(0, 0, nullptr, nullptr, nullptr);
|
||||
_r.deviceContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_UNKNOWN, 0);
|
||||
_r.deviceContext->IASetInputLayout(nullptr);
|
||||
_r.deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
|
||||
_r.deviceContext->PSSetConstantBuffers(0, 1, _r.constantBuffer.addressof());
|
||||
|
||||
const std::array resources{ _r.cellView.get(), _r.atlasView.get() };
|
||||
_r.deviceContext->PSSetShaderResources(0, gsl::narrow_cast<UINT>(resources.size()), resources.data());
|
||||
}
|
||||
|
||||
AtlasEngine::f32x4 AtlasEngine::_getGammaRatios(float gamma) noexcept
|
||||
{
|
||||
static constexpr f32x4 gammaIncorrectTargetRatios[13]{
|
||||
{ 0.0000f / 4.f, 0.0000f / 4.f, 0.0000f / 4.f, 0.0000f / 4.f }, // gamma = 1.0
|
||||
{ 0.0166f / 4.f, -0.0807f / 4.f, 0.2227f / 4.f, -0.0751f / 4.f }, // gamma = 1.1
|
||||
{ 0.0350f / 4.f, -0.1760f / 4.f, 0.4325f / 4.f, -0.1370f / 4.f }, // gamma = 1.2
|
||||
{ 0.0543f / 4.f, -0.2821f / 4.f, 0.6302f / 4.f, -0.1876f / 4.f }, // gamma = 1.3
|
||||
{ 0.0739f / 4.f, -0.3963f / 4.f, 0.8167f / 4.f, -0.2287f / 4.f }, // gamma = 1.4
|
||||
{ 0.0933f / 4.f, -0.5161f / 4.f, 0.9926f / 4.f, -0.2616f / 4.f }, // gamma = 1.5
|
||||
{ 0.1121f / 4.f, -0.6395f / 4.f, 1.1588f / 4.f, -0.2877f / 4.f }, // gamma = 1.6
|
||||
{ 0.1300f / 4.f, -0.7649f / 4.f, 1.3159f / 4.f, -0.3080f / 4.f }, // gamma = 1.7
|
||||
{ 0.1469f / 4.f, -0.8911f / 4.f, 1.4644f / 4.f, -0.3234f / 4.f }, // gamma = 1.8
|
||||
{ 0.1627f / 4.f, -1.0170f / 4.f, 1.6051f / 4.f, -0.3347f / 4.f }, // gamma = 1.9
|
||||
{ 0.1773f / 4.f, -1.1420f / 4.f, 1.7385f / 4.f, -0.3426f / 4.f }, // gamma = 2.0
|
||||
{ 0.1908f / 4.f, -1.2652f / 4.f, 1.8650f / 4.f, -0.3476f / 4.f }, // gamma = 2.1
|
||||
{ 0.2031f / 4.f, -1.3864f / 4.f, 1.9851f / 4.f, -0.3501f / 4.f }, // gamma = 2.2
|
||||
};
|
||||
static constexpr auto norm13 = static_cast<float>(static_cast<double>(0x10000) / (255 * 255) * 4);
|
||||
static constexpr auto norm24 = static_cast<float>(static_cast<double>(0x100) / (255) * 4);
|
||||
|
||||
gamma = clamp(gamma, 1.0f, 2.2f);
|
||||
|
||||
const size_t index = gsl::narrow_cast<size_t>(std::round((gamma - 1.0f) / 1.2f * 12.0f));
|
||||
const auto& ratios = gammaIncorrectTargetRatios[index];
|
||||
return { norm13 * ratios.x, norm24 * ratios.y, norm13 * ratios.z, norm24 * ratios.w };
|
||||
}
|
||||
|
||||
void AtlasEngine::_updateConstantBuffer() const noexcept
|
||||
{
|
||||
ConstBuffer data;
|
||||
data.viewport.x = 0;
|
||||
data.viewport.y = 0;
|
||||
data.viewport.z = static_cast<float>(_r.cellCount.x * _r.cellSize.x);
|
||||
data.viewport.w = static_cast<float>(_r.cellCount.y * _r.cellSize.y);
|
||||
data.gammaRatios = _getGammaRatios(_r.gamma);
|
||||
data.grayscaleEnhancedContrast = _r.grayscaleEnhancedContrast;
|
||||
data.cellCountX = _r.cellCount.x;
|
||||
data.cellSize.x = _r.cellSize.x;
|
||||
data.cellSize.y = _r.cellSize.y;
|
||||
data.underlinePos.x = _r.underlinePos;
|
||||
data.underlinePos.y = _r.underlinePos + _r.lineThickness;
|
||||
data.strikethroughPos.x = _r.strikethroughPos;
|
||||
data.strikethroughPos.y = _r.strikethroughPos + _r.lineThickness;
|
||||
data.backgroundColor = _r.backgroundColor;
|
||||
data.cursorColor = _r.cursorOptions.cursorColor;
|
||||
data.selectionColor = _r.selectionColor;
|
||||
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function '...' which may throw exceptions (f.6).
|
||||
_r.deviceContext->UpdateSubresource(_r.constantBuffer.get(), 0, nullptr, &data, 0, 0);
|
||||
}
|
||||
|
||||
void AtlasEngine::_adjustAtlasSize()
|
||||
{
|
||||
if (_r.atlasPosition.y < _r.atlasSizeInPixel.y && _r.atlasPosition.x < _r.atlasSizeInPixel.x)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 limitX = _r.atlasSizeInPixelLimit.x;
|
||||
const u32 limitY = _r.atlasSizeInPixelLimit.y;
|
||||
const u32 posX = _r.atlasPosition.x;
|
||||
const u32 posY = _r.atlasPosition.y;
|
||||
const u32 cellX = _r.cellSize.x;
|
||||
const u32 cellY = _r.cellSize.y;
|
||||
const auto perCellArea = cellX * cellY;
|
||||
|
||||
// The texture atlas is filled like this:
|
||||
// x →
|
||||
// y +--------------+
|
||||
// ↓ |XXXXXXXXXXXXXX|
|
||||
// |XXXXXXXXXXXXXX|
|
||||
// |XXXXX↖ |
|
||||
// | | |
|
||||
// +------|-------+
|
||||
// This is where _r.atlasPosition points at.
|
||||
//
|
||||
// Each X is a glyph texture tile that's occupied.
|
||||
// We can compute the area of pixels consumed by adding the first
|
||||
// two lines of X (rectangular) together with the last line of X.
|
||||
const auto currentArea = posY * limitX + posX * cellY;
|
||||
// minArea reserves enough room for 64 cells in all cases (mainly during startup).
|
||||
const auto minArea = 64 * perCellArea;
|
||||
auto newArea = std::max(minArea, currentArea);
|
||||
|
||||
// I want the texture to grow exponentially similar to std::vector, as this
|
||||
// ensures we don't need to resize the texture again right after having done.
|
||||
// This rounds newArea up to the next power of 2.
|
||||
unsigned long int index;
|
||||
_BitScanReverse(&index, newArea); // newArea can't be 0
|
||||
newArea = u32{ 1 } << (index + 1);
|
||||
|
||||
const auto pixelPerRow = limitX * cellY;
|
||||
// newArea might be just large enough that it spans N full rows of cells and one additional row
|
||||
// just barely. This algorithm rounds up newArea to the _next_ multiple of cellY.
|
||||
const auto wantedHeight = (newArea + pixelPerRow - 1) / pixelPerRow * cellY;
|
||||
// The atlas might either be a N rows of full width (xLimit) or just one
|
||||
// row (where wantedHeight == cellY) that doesn't quite fill it's maximum width yet.
|
||||
const auto wantedWidth = wantedHeight != cellY ? limitX : newArea / perCellArea * cellX;
|
||||
|
||||
// We know that limitX/limitY were u16 originally, and thus it's safe to narrow_cast it back.
|
||||
const auto height = gsl::narrow_cast<u16>(std::min(limitY, wantedHeight));
|
||||
const auto width = gsl::narrow_cast<u16>(std::min(limitX, wantedWidth));
|
||||
|
||||
assert(width != 0);
|
||||
assert(height != 0);
|
||||
|
||||
wil::com_ptr<ID3D11Texture2D> atlasBuffer;
|
||||
wil::com_ptr<ID3D11ShaderResourceView> atlasView;
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc = { 1, 0 };
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
THROW_IF_FAILED(_r.device->CreateTexture2D(&desc, nullptr, atlasBuffer.addressof()));
|
||||
THROW_IF_FAILED(_r.device->CreateShaderResourceView(atlasBuffer.get(), nullptr, atlasView.addressof()));
|
||||
}
|
||||
|
||||
// If a _r.atlasBuffer already existed, we can copy its glyphs
|
||||
// over to the new texture without re-rendering everything.
|
||||
const auto copyFromExisting = _r.atlasSizeInPixel != u16x2{};
|
||||
if (copyFromExisting)
|
||||
{
|
||||
D3D11_BOX box;
|
||||
box.left = 0;
|
||||
box.top = 0;
|
||||
box.front = 0;
|
||||
box.right = _r.atlasSizeInPixel.x;
|
||||
box.bottom = _r.atlasSizeInPixel.y;
|
||||
box.back = 1;
|
||||
_r.deviceContext->CopySubresourceRegion1(atlasBuffer.get(), 0, 0, 0, 0, _r.atlasBuffer.get(), 0, &box, D3D11_COPY_NO_OVERWRITE);
|
||||
}
|
||||
|
||||
_r.atlasSizeInPixel = u16x2{ width, height };
|
||||
_r.atlasBuffer = std::move(atlasBuffer);
|
||||
_r.atlasView = std::move(atlasView);
|
||||
_setShaderResources();
|
||||
|
||||
WI_SetFlagIf(_r.invalidations, RenderInvalidations::Cursor, !copyFromExisting);
|
||||
}
|
||||
|
||||
void AtlasEngine::_reserveScratchpadSize(u16 minWidth)
|
||||
{
|
||||
if (minWidth <= _r.scratchpadCellWidth)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The new size is the greater of ... cells wide:
|
||||
// * 2
|
||||
// * minWidth
|
||||
// * current size * 1.5
|
||||
const auto newWidth = std::max<UINT>(std::max<UINT>(2, minWidth), _r.scratchpadCellWidth + (_r.scratchpadCellWidth >> 1));
|
||||
|
||||
_r.d2dRenderTarget.reset();
|
||||
_r.atlasScratchpad.reset();
|
||||
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
desc.Width = _r.cellSize.x * newWidth;
|
||||
desc.Height = _r.cellSize.y;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc = { 1, 0 };
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
|
||||
THROW_IF_FAILED(_r.device->CreateTexture2D(&desc, nullptr, _r.atlasScratchpad.put()));
|
||||
}
|
||||
{
|
||||
const auto surface = _r.atlasScratchpad.query<IDXGISurface>();
|
||||
|
||||
wil::com_ptr<IDWriteRenderingParams1> defaultParams;
|
||||
THROW_IF_FAILED(_sr.dwriteFactory->CreateRenderingParams(reinterpret_cast<IDWriteRenderingParams**>(defaultParams.addressof())));
|
||||
wil::com_ptr<IDWriteRenderingParams1> renderingParams;
|
||||
THROW_IF_FAILED(_sr.dwriteFactory->CreateCustomRenderingParams(1.0f, 0.0f, 0.0f, defaultParams->GetClearTypeLevel(), defaultParams->GetPixelGeometry(), defaultParams->GetRenderingMode(), renderingParams.addressof()));
|
||||
|
||||
_r.gamma = defaultParams->GetGamma();
|
||||
_r.grayscaleEnhancedContrast = defaultParams->GetGrayscaleEnhancedContrast();
|
||||
|
||||
D2D1_RENDER_TARGET_PROPERTIES props{};
|
||||
props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
|
||||
props.pixelFormat = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED };
|
||||
props.dpiX = static_cast<float>(_r.dpi);
|
||||
props.dpiY = static_cast<float>(_r.dpi);
|
||||
THROW_IF_FAILED(_sr.d2dFactory->CreateDxgiSurfaceRenderTarget(surface.get(), &props, _r.d2dRenderTarget.put()));
|
||||
|
||||
// We don't really use D2D for anything except DWrite, but it
|
||||
// can't hurt to ensure that everything it does is pixel aligned.
|
||||
_r.d2dRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
// Ensure that D2D uses the exact same gamma as our shader uses.
|
||||
_r.d2dRenderTarget->SetTextRenderingParams(renderingParams.get());
|
||||
// We can't set the antialiasingMode here, as D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
|
||||
// will force the alpha channel to be 0 for _all_ text.
|
||||
//_r.d2dRenderTarget->SetTextAntialiasMode(static_cast<D2D1_TEXT_ANTIALIAS_MODE>(_api.antialiasingMode));
|
||||
}
|
||||
{
|
||||
static constexpr D2D1_COLOR_F color{ 1, 1, 1, 1 };
|
||||
wil::com_ptr<ID2D1SolidColorBrush> brush;
|
||||
THROW_IF_FAILED(_r.d2dRenderTarget->CreateSolidColorBrush(&color, nullptr, brush.addressof()));
|
||||
_r.brush = brush.query<ID2D1Brush>();
|
||||
}
|
||||
|
||||
_r.scratchpadCellWidth = _r.maxEncounteredCellCount;
|
||||
WI_SetAllFlags(_r.invalidations, RenderInvalidations::ConstBuffer);
|
||||
}
|
||||
|
||||
void AtlasEngine::_processGlyphQueue()
|
||||
{
|
||||
if (_r.glyphQueue.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& pair : _r.glyphQueue)
|
||||
{
|
||||
_drawGlyph(pair);
|
||||
}
|
||||
|
||||
_r.glyphQueue.clear();
|
||||
}
|
||||
|
||||
void AtlasEngine::_drawGlyph(const AtlasQueueItem& item) const
|
||||
{
|
||||
const auto key = item.key->data();
|
||||
const auto value = item.value->data();
|
||||
const auto coords = &value->coords[0];
|
||||
const auto charsLength = key->charCount;
|
||||
const auto cells = static_cast<u32>(key->attributes.cellCount);
|
||||
const auto textFormat = _getTextFormat(key->attributes.bold, key->attributes.italic);
|
||||
|
||||
// See D2DFactory::DrawText
|
||||
wil::com_ptr<IDWriteTextLayout> textLayout;
|
||||
THROW_IF_FAILED(_sr.dwriteFactory->CreateTextLayout(&key->chars[0], charsLength, textFormat, cells * _r.cellSizeDIP.x, _r.cellSizeDIP.y, textLayout.addressof()));
|
||||
if (item.scale != 1.0f)
|
||||
{
|
||||
const auto f = textFormat->GetFontSize();
|
||||
textLayout->SetFontSize(f * item.scale, { 0, charsLength });
|
||||
}
|
||||
if (_r.typography)
|
||||
{
|
||||
textLayout->SetTypography(_r.typography.get(), { 0, charsLength });
|
||||
}
|
||||
|
||||
auto options = D2D1_DRAW_TEXT_OPTIONS_NONE;
|
||||
// D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT enables a bunch of internal machinery
|
||||
// which doesn't have to run if we know we can't use it anyways in the shader.
|
||||
WI_SetFlagIf(options, D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT, WI_IsFlagSet(value->flags, CellFlags::ColoredGlyph));
|
||||
|
||||
_r.d2dRenderTarget->BeginDraw();
|
||||
// We could call
|
||||
// _r.d2dRenderTarget->PushAxisAlignedClip(&rect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
// now to reduce the surface that needs to be cleared, but this decreases
|
||||
// performance by 10% (tested using debugGlyphGenerationPerformance).
|
||||
_r.d2dRenderTarget->Clear();
|
||||
_r.d2dRenderTarget->DrawTextLayout({}, textLayout.get(), _r.brush.get(), options);
|
||||
THROW_IF_FAILED(_r.d2dRenderTarget->EndDraw());
|
||||
|
||||
for (uint32_t i = 0; i < cells; ++i)
|
||||
{
|
||||
// Specifying NO_OVERWRITE means that the system can assume that existing references to the surface that
|
||||
// may be in flight on the GPU will not be affected by the update, so the copy can proceed immediately
|
||||
// (avoiding either a batch flush or the system maintaining multiple copies of the resource behind the scenes).
|
||||
//
|
||||
// Since our shader only draws whatever is in the atlas, and since we don't replace glyph tiles that are in use,
|
||||
// we can safely (?) tell the GPU that we don't overwrite parts of our atlas that are in use.
|
||||
_copyScratchpadTile(i, coords[i], D3D11_COPY_NO_OVERWRITE);
|
||||
}
|
||||
}
|
||||
|
||||
void AtlasEngine::_drawCursor()
|
||||
{
|
||||
_reserveScratchpadSize(1);
|
||||
|
||||
// lineWidth is in D2D's DIPs. For instance if we have a 150-200% zoom scale we want to draw a 2px wide line.
|
||||
// At 150% scale lineWidth thus needs to be 1.33333... because at a zoom scale of 1.5 this results in a 2px wide line.
|
||||
const auto lineWidth = std::max(1.0f, static_cast<float>((_r.dpi + USER_DEFAULT_SCREEN_DPI / 2) / USER_DEFAULT_SCREEN_DPI * USER_DEFAULT_SCREEN_DPI) / static_cast<float>(_r.dpi));
|
||||
const auto cursorType = static_cast<CursorType>(_r.cursorOptions.cursorType);
|
||||
D2D1_RECT_F rect;
|
||||
rect.left = 0.0f;
|
||||
rect.top = 0.0f;
|
||||
rect.right = _r.cellSizeDIP.x;
|
||||
rect.bottom = _r.cellSizeDIP.y;
|
||||
|
||||
switch (cursorType)
|
||||
{
|
||||
case CursorType::Legacy:
|
||||
rect.top = _r.cellSizeDIP.y * static_cast<float>(100 - _r.cursorOptions.heightPercentage) / 100.0f;
|
||||
break;
|
||||
case CursorType::VerticalBar:
|
||||
rect.right = lineWidth;
|
||||
break;
|
||||
case CursorType::EmptyBox:
|
||||
{
|
||||
// EmptyBox is drawn as a line and unlike filled rectangles those are drawn centered on their
|
||||
// coordinates in such a way that the line border extends half the width to each side.
|
||||
// --> Our coordinates have to be 0.5 DIP off in order to draw a 2px line on a 200% scaling.
|
||||
const auto halfWidth = lineWidth / 2.0f;
|
||||
rect.left = halfWidth;
|
||||
rect.top = halfWidth;
|
||||
rect.right -= halfWidth;
|
||||
rect.bottom -= halfWidth;
|
||||
break;
|
||||
}
|
||||
case CursorType::Underscore:
|
||||
case CursorType::DoubleUnderscore:
|
||||
rect.top = _r.cellSizeDIP.y - lineWidth;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_r.d2dRenderTarget->BeginDraw();
|
||||
_r.d2dRenderTarget->Clear();
|
||||
|
||||
if (cursorType == CursorType::EmptyBox)
|
||||
{
|
||||
_r.d2dRenderTarget->DrawRectangle(&rect, _r.brush.get(), lineWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
_r.d2dRenderTarget->FillRectangle(&rect, _r.brush.get());
|
||||
}
|
||||
|
||||
if (cursorType == CursorType::DoubleUnderscore)
|
||||
{
|
||||
rect.top -= 2.0f;
|
||||
rect.bottom -= 2.0f;
|
||||
_r.d2dRenderTarget->FillRectangle(&rect, _r.brush.get());
|
||||
}
|
||||
|
||||
THROW_IF_FAILED(_r.d2dRenderTarget->EndDraw());
|
||||
|
||||
_copyScratchpadTile(0, {});
|
||||
}
|
||||
|
||||
void AtlasEngine::_copyScratchpadTile(uint32_t scratchpadIndex, u16x2 target, uint32_t copyFlags) const noexcept
|
||||
{
|
||||
D3D11_BOX box;
|
||||
box.left = scratchpadIndex * _r.cellSize.x;
|
||||
box.top = 0;
|
||||
box.front = 0;
|
||||
box.right = box.left + _r.cellSize.x;
|
||||
box.bottom = _r.cellSize.y;
|
||||
box.back = 1;
|
||||
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function '...' which may throw exceptions (f.6).
|
||||
_r.deviceContext->CopySubresourceRegion1(_r.atlasBuffer.get(), 0, target.x, target.y, 0, _r.atlasScratchpad.get(), 0, &box, copyFlags);
|
||||
}
|
55
src/renderer/atlas/atlas.vcxproj
Normal file
55
src/renderer/atlas/atlas.vcxproj
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>atlas</RootNamespace>
|
||||
<ProjectName>RendererAtlas</ProjectName>
|
||||
<TargetName>ConRenderAtlas</TargetName>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AtlasEngine.api.cpp" />
|
||||
<ClCompile Include="AtlasEngine.r.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AtlasEngine.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="AtlasEngine.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<FxCompile Include="shader_ps.hlsl">
|
||||
<ShaderType>Pixel</ShaderType>
|
||||
<ShaderModel>4.1</ShaderModel>
|
||||
<AllResourcesBound>true</AllResourcesBound>
|
||||
<VariableName>shader_ps</VariableName>
|
||||
<ObjectFileOutput />
|
||||
<HeaderFileOutput>$(OutDir)$(ProjectName)\%(Filename).h</HeaderFileOutput>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalOptions>/Zpc %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="'$(Configuration)'=='Release'">/O3 /Qstrip_debug /Qstrip_reflect %(AdditionalOptions)</AdditionalOptions>
|
||||
</FxCompile>
|
||||
<FxCompile Include="shader_vs.hlsl">
|
||||
<ShaderType>Vertex</ShaderType>
|
||||
<ShaderModel>4.1</ShaderModel>
|
||||
<AllResourcesBound>true</AllResourcesBound>
|
||||
<VariableName>shader_vs</VariableName>
|
||||
<ObjectFileOutput />
|
||||
<HeaderFileOutput>$(OutDir)$(ProjectName)\%(Filename).h</HeaderFileOutput>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalOptions>/Zpc %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="'$(Configuration)'=='Release'">/O3 /Qstrip_debug /Qstrip_reflect %(AdditionalOptions)</AdditionalOptions>
|
||||
</FxCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>$(OutDir)$(ProjectName)\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
4
src/renderer/atlas/pch.cpp
Normal file
4
src/renderer/atlas/pch.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
52
src/renderer/atlas/pch.h
Normal file
52
src/renderer/atlas/pch.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <d2d1.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <dwrite_3.h>
|
||||
#include <dcomp.h>
|
||||
#include <dxgi1_3.h>
|
||||
#include <dxgidebug.h>
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include <gsl/gsl_util>
|
||||
#include <gsl/pointers>
|
||||
#include <gsl/span>
|
||||
#include <wil/com.h>
|
||||
#include <wil/filesystem.h>
|
||||
#include <wil/result_macros.h>
|
||||
#include <wil/stl.h>
|
||||
#include <wil/win32_helpers.h>
|
||||
|
||||
// Dynamic Bitset (optional dependency on LibPopCnt for perf at bit counting)
|
||||
// Variable-size compressed-storage header-only bit flag storage library.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702) // unreachable code
|
||||
#include <dynamic_bitset.hpp>
|
||||
#pragma warning(pop)
|
||||
|
||||
// Chromium Numerics (safe math)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100) // '...': unreferenced formal parameter
|
||||
#pragma warning(disable : 26812) // The enum type '...' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||
#include <base/numerics/safe_math.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
#include <til.h>
|
||||
#include <til/bit.h>
|
182
src/renderer/atlas/shader_ps.hlsl
Normal file
182
src/renderer/atlas/shader_ps.hlsl
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#define INVALID_COLOR 0xffffffff
|
||||
|
||||
// These flags are shared with AtlasEngine::CellFlags.
|
||||
//
|
||||
// clang-format off
|
||||
#define CellFlags_None 0x00000000
|
||||
#define CellFlags_Inlined 0x00000001
|
||||
|
||||
#define CellFlags_ColoredGlyph 0x00000002
|
||||
#define CellFlags_ThinFont 0x00000004
|
||||
|
||||
#define CellFlags_Cursor 0x00000008
|
||||
#define CellFlags_Selected 0x00000010
|
||||
|
||||
#define CellFlags_BorderLeft 0x00000020
|
||||
#define CellFlags_BorderTop 0x00000040
|
||||
#define CellFlags_BorderRight 0x00000080
|
||||
#define CellFlags_BorderBottom 0x00000100
|
||||
#define CellFlags_Underline 0x00000200
|
||||
#define CellFlags_UnderlineDotted 0x00000400
|
||||
#define CellFlags_UnderlineDouble 0x00000800
|
||||
#define CellFlags_Strikethrough 0x00001000
|
||||
// clang-format on
|
||||
|
||||
// According to Nvidia's "Understanding Structured Buffer Performance" guide
|
||||
// one should aim for structures with sizes divisible by 128 bits (16 bytes).
|
||||
// This prevents elements from spanning cache lines.
|
||||
struct Cell
|
||||
{
|
||||
uint glyphPos;
|
||||
uint flags;
|
||||
uint2 color; // x: foreground, y: background
|
||||
};
|
||||
|
||||
cbuffer ConstBuffer : register(b0)
|
||||
{
|
||||
float4 viewport;
|
||||
float4 gammaRatios;
|
||||
float grayscaleEnhancedContrast;
|
||||
uint cellCountX;
|
||||
uint2 cellSize;
|
||||
uint2 underlinePos;
|
||||
uint2 strikethroughPos;
|
||||
uint backgroundColor;
|
||||
uint cursorColor;
|
||||
uint selectionColor;
|
||||
};
|
||||
StructuredBuffer<Cell> cells : register(t0);
|
||||
Texture2D<float4> glyphs : register(t1);
|
||||
|
||||
float4 decodeRGBA(uint i)
|
||||
{
|
||||
uint r = i & 0xff;
|
||||
uint g = (i >> 8) & 0xff;
|
||||
uint b = (i >> 16) & 0xff;
|
||||
uint a = i >> 24;
|
||||
float4 c = float4(r, g, b, a) / 255.0f;
|
||||
// Convert to premultiplied alpha for simpler alpha blending.
|
||||
c.rgb *= c.a;
|
||||
return c;
|
||||
}
|
||||
|
||||
uint2 decodeU16x2(uint i)
|
||||
{
|
||||
return uint2(i & 0xffff, i >> 16);
|
||||
}
|
||||
|
||||
float4 alphaBlendPremultiplied(float4 bottom, float4 top)
|
||||
{
|
||||
float ia = 1 - top.a;
|
||||
return float4(bottom.rgb * ia + top.rgb, bottom.a * ia + top.a);
|
||||
}
|
||||
|
||||
float applyLightOnDarkContrastAdjustment(float3 color)
|
||||
{
|
||||
float lightness = 0.30f * color.r + 0.59f * color.g + 0.11f * color.b;
|
||||
float multiplier = saturate(4.0f * (0.75f - lightness));
|
||||
return grayscaleEnhancedContrast * multiplier;
|
||||
}
|
||||
|
||||
float calcColorIntensity(float3 color)
|
||||
{
|
||||
return (color.r + color.g + color.g + color.b) / 4.0f;
|
||||
}
|
||||
|
||||
float enhanceContrast(float alpha, float k)
|
||||
{
|
||||
return alpha * (k + 1.0f) / (alpha * k + 1.0f);
|
||||
}
|
||||
|
||||
float applyAlphaCorrection(float a, float f, float4 g)
|
||||
{
|
||||
return a + a * (1 - a) * ((g.x * f + g.y) * a + (g.z * f + g.w));
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
float4 main(float4 pos: SV_Position): SV_Target
|
||||
// clang-format on
|
||||
{
|
||||
if (any(pos.xy < viewport.xy) || any(pos.xy >= viewport.zw))
|
||||
{
|
||||
return decodeRGBA(backgroundColor);
|
||||
}
|
||||
|
||||
// If you want to write test a before/after change simultaneously
|
||||
// you can turn the image into a checkerboard by writing:
|
||||
// if ((uint(pos.x) ^ uint(pos.y)) / 4 & 1) { return float4(1, 0, 0, 1); }
|
||||
// This will generate a checkerboard of 4*4px red squares.
|
||||
// Of course you wouldn't just return a red color there, but instead
|
||||
// for instance run your new code and compare it with the old.
|
||||
|
||||
uint2 viewportPos = pos.xy - viewport.xy;
|
||||
uint2 cellIndex = viewportPos / cellSize;
|
||||
uint2 cellPos = viewportPos % cellSize;
|
||||
Cell cell = cells[cellIndex.y * cellCountX + cellIndex.x];
|
||||
|
||||
// Layer 0:
|
||||
// The cell's background color
|
||||
float4 color = decodeRGBA(cell.color.y);
|
||||
float4 fg = decodeRGBA(cell.color.x);
|
||||
|
||||
// Layer 1 (optional):
|
||||
// Colored cursors are drawn "in between" the background color and the text of a cell.
|
||||
if ((cell.flags & CellFlags_Cursor) && cursorColor != INVALID_COLOR)
|
||||
{
|
||||
// The cursor texture is stored at the top-left-most glyph cell.
|
||||
// Cursor pixels are either entirely transparent or opaque.
|
||||
// --> We can just use .a as a mask to flip cursor pixels on or off.
|
||||
color = alphaBlendPremultiplied(color, decodeRGBA(cursorColor) * glyphs[cellPos].a);
|
||||
}
|
||||
|
||||
// Layer 2:
|
||||
// Step 1: Underlines
|
||||
if ((cell.flags & CellFlags_Underline) && cellPos.y >= underlinePos.x && cellPos.y < underlinePos.y)
|
||||
{
|
||||
color = alphaBlendPremultiplied(color, fg);
|
||||
}
|
||||
if ((cell.flags & CellFlags_UnderlineDotted) && cellPos.y >= underlinePos.x && cellPos.y < underlinePos.y && (viewportPos.x / (underlinePos.y - underlinePos.x) & 1))
|
||||
{
|
||||
color = alphaBlendPremultiplied(color, fg);
|
||||
}
|
||||
// Step 2: The cell's glyph, potentially drawn in the foreground color
|
||||
{
|
||||
float4 glyph = glyphs[decodeU16x2(cell.glyphPos) + cellPos];
|
||||
|
||||
if (!(cell.flags & CellFlags_ColoredGlyph))
|
||||
{
|
||||
float contrastBoost = (cell.flags & CellFlags_ThinFont) == 0 ? 0.0f : 0.5f;
|
||||
float enhancedContrast = contrastBoost + applyLightOnDarkContrastAdjustment(fg.rgb);
|
||||
float intensity = calcColorIntensity(fg.rgb);
|
||||
float contrasted = enhanceContrast(glyph.a, enhancedContrast);
|
||||
float correctedAlpha = applyAlphaCorrection(contrasted, intensity, gammaRatios);
|
||||
glyph = fg * correctedAlpha;
|
||||
}
|
||||
|
||||
color = alphaBlendPremultiplied(color, glyph);
|
||||
}
|
||||
// Step 3: Lines, but not "under"lines
|
||||
if ((cell.flags & CellFlags_Strikethrough) && cellPos.y >= strikethroughPos.x && cellPos.y < strikethroughPos.y)
|
||||
{
|
||||
color = alphaBlendPremultiplied(color, fg);
|
||||
}
|
||||
|
||||
// Layer 3 (optional):
|
||||
// Uncolored cursors invert the cells color.
|
||||
if ((cell.flags & CellFlags_Cursor) && cursorColor == INVALID_COLOR)
|
||||
{
|
||||
color.rgb = abs(glyphs[cellPos].rgb - color.rgb);
|
||||
}
|
||||
|
||||
// Layer 4:
|
||||
// The current selection is drawn semi-transparent on top.
|
||||
if (cell.flags & CellFlags_Selected)
|
||||
{
|
||||
color = alphaBlendPremultiplied(color, decodeRGBA(selectionColor));
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
17
src/renderer/atlas/shader_vs.hlsl
Normal file
17
src/renderer/atlas/shader_vs.hlsl
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
// clang-format off
|
||||
float4 main(uint id: SV_VERTEXID): SV_POSITION
|
||||
// clang-format on
|
||||
{
|
||||
// The algorithm below is a fast way to generate a full screen triangle,
|
||||
// published by Bill Bilodeau "Vertex Shader Tricks" at GDC14.
|
||||
// It covers the entire viewport and is faster for the GPU than a quad/rectangle.
|
||||
return float4(
|
||||
float(id / 2) * 4.0 - 1.0,
|
||||
float(id % 2) * 4.0 - 1.0,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
}
|
|
@ -74,5 +74,6 @@ HRESULT RenderEngineBase::PrepareLineTransform(const LineRendition /*lineRenditi
|
|||
// - Blocks until the engine is able to render without blocking.
|
||||
void RenderEngineBase::WaitUntilCanRender() noexcept
|
||||
{
|
||||
// do nothing by default
|
||||
// Throttle the render loop a bit by default (~60 FPS), improving throughput.
|
||||
Sleep(8);
|
||||
}
|
||||
|
|
|
@ -213,12 +213,6 @@ DWORD WINAPI RenderThread::_ThreadProc()
|
|||
LOG_IF_FAILED(_pRenderer->PaintFrame());
|
||||
|
||||
SetEvent(_hPaintCompletedEvent);
|
||||
|
||||
// extra check before we sleep since it's a "long" activity, relatively speaking.
|
||||
if (_fKeepRunning)
|
||||
{
|
||||
Sleep(s_FrameLimitMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
|
|
@ -37,8 +37,6 @@ namespace Microsoft::Console::Render
|
|||
static DWORD WINAPI s_ThreadProc(_In_ LPVOID lpParameter);
|
||||
DWORD WINAPI _ThreadProc();
|
||||
|
||||
static DWORD const s_FrameLimitMilliseconds = 8;
|
||||
|
||||
HANDLE _hThread;
|
||||
HANDLE _hEvent;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
@ -404,9 +404,9 @@ CATCH_RETURN()
|
|||
std::vector<DWRITE_SHAPING_GLYPH_PROPERTIES> glyphProps(maxGlyphCount);
|
||||
|
||||
// Get the features to apply to the font
|
||||
auto features = _fontRenderData->DefaultFontFeatures();
|
||||
DWRITE_FONT_FEATURE* featureList = features.data();
|
||||
DWRITE_TYPOGRAPHIC_FEATURES typographicFeatures = { &featureList[0], gsl::narrow<uint32_t>(features.size()) };
|
||||
const auto& features = _fontRenderData->DefaultFontFeatures();
|
||||
#pragma warning(suppress : 26492) // Don't use const_cast to cast away const or volatile (type.3).
|
||||
DWRITE_TYPOGRAPHIC_FEATURES typographicFeatures = { const_cast<DWRITE_FONT_FEATURE*>(features.data()), gsl::narrow<uint32_t>(features.size()) };
|
||||
DWRITE_TYPOGRAPHIC_FEATURES const* typographicFeaturesPointer = &typographicFeatures;
|
||||
const uint32_t fontFeatureLengths[] = { textLength };
|
||||
|
||||
|
|
|
@ -487,6 +487,7 @@ CATCH_RETURN()
|
|||
{
|
||||
// Color glyph rendering sourced from https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/DWriteColorGlyph
|
||||
|
||||
#pragma warning(suppress : 26429) // Symbol 'drawingContext' is never tested for nullness, it can be marked as not_null (f.23).
|
||||
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
|
||||
// Since we've delegated the drawing of the background of the text into this function, the origin passed in isn't actually the baseline.
|
||||
|
|
|
@ -244,10 +244,11 @@ bool DxEngine::_HasTerminalEffects() const noexcept
|
|||
// Arguments:
|
||||
// Return Value:
|
||||
// - Void
|
||||
void DxEngine::ToggleShaderEffects()
|
||||
void DxEngine::ToggleShaderEffects() noexcept
|
||||
{
|
||||
_terminalEffectsEnabled = !_terminalEffectsEnabled;
|
||||
_recreateDeviceRequested = true;
|
||||
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function 'Log_IfFailed()' which may throw exceptions (f.6).
|
||||
LOG_IF_FAILED(InvalidateAll());
|
||||
}
|
||||
|
||||
|
@ -969,14 +970,14 @@ try
|
|||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
void DxEngine::SetCallback(std::function<void()> pfn)
|
||||
void DxEngine::SetCallback(std::function<void()> pfn) noexcept
|
||||
{
|
||||
_pfn = pfn;
|
||||
_pfn = std::move(pfn);
|
||||
}
|
||||
|
||||
void DxEngine::SetWarningCallback(std::function<void(const HRESULT)> pfn)
|
||||
void DxEngine::SetWarningCallback(std::function<void(const HRESULT)> pfn) noexcept
|
||||
{
|
||||
_pfnWarningCallback = pfn;
|
||||
_pfnWarningCallback = std::move(pfn);
|
||||
}
|
||||
|
||||
bool DxEngine::GetRetroTerminalEffect() const noexcept
|
||||
|
@ -1046,11 +1047,12 @@ try
|
|||
}
|
||||
CATCH_LOG()
|
||||
|
||||
HANDLE DxEngine::GetSwapChainHandle()
|
||||
HANDLE DxEngine::GetSwapChainHandle() noexcept
|
||||
{
|
||||
if (!_swapChainHandle)
|
||||
{
|
||||
THROW_IF_FAILED(_CreateDeviceResources(true));
|
||||
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function 'Log_IfFailed()' which may throw exceptions (f.6).
|
||||
LOG_IF_FAILED(_CreateDeviceResources(true));
|
||||
}
|
||||
|
||||
return _swapChainHandle.get();
|
||||
|
@ -1498,18 +1500,13 @@ CATCH_RETURN()
|
|||
// - See https://docs.microsoft.com/en-us/windows/uwp/gaming/reduce-latency-with-dxgi-1-3-swap-chains.
|
||||
void DxEngine::WaitUntilCanRender() noexcept
|
||||
{
|
||||
if (!_swapChainFrameLatencyWaitableObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Throttle the DxEngine a bit down to ~60 FPS.
|
||||
// This improves throughput for rendering complex or colored text.
|
||||
Sleep(8);
|
||||
|
||||
const auto ret = WaitForSingleObjectEx(
|
||||
_swapChainFrameLatencyWaitableObject.get(),
|
||||
1000, // 1 second timeout (shouldn't ever occur)
|
||||
true);
|
||||
if (ret != WAIT_OBJECT_0)
|
||||
if (_swapChainFrameLatencyWaitableObject)
|
||||
{
|
||||
LOG_WIN32_MSG(ret, "Waiting for swap chain frame latency waitable object returned error or timeout.");
|
||||
WaitForSingleObjectEx(_swapChainFrameLatencyWaitableObject.get(), 100, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2023,7 +2020,7 @@ try
|
|||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
[[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) noexcept
|
||||
[[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) const noexcept
|
||||
{
|
||||
const short widthInChars = base::saturated_cast<short>(viewInPixels.Width() / _fontRenderData->GlyphCell().width());
|
||||
const short heightInChars = base::saturated_cast<short>(viewInPixels.Height() / _fontRenderData->GlyphCell().height());
|
||||
|
@ -2031,7 +2028,7 @@ CATCH_RETURN();
|
|||
return Viewport::FromDimensions(viewInPixels.Origin(), { widthInChars, heightInChars });
|
||||
}
|
||||
|
||||
[[nodiscard]] Viewport DxEngine::GetViewportInPixels(const Viewport& viewInCharacters) noexcept
|
||||
[[nodiscard]] Viewport DxEngine::GetViewportInPixels(const Viewport& viewInCharacters) const noexcept
|
||||
{
|
||||
const short widthInPixels = base::saturated_cast<short>(viewInCharacters.Width() * _fontRenderData->GlyphCell().width());
|
||||
const short heightInPixels = base::saturated_cast<short>(viewInCharacters.Height() * _fontRenderData->GlyphCell().height());
|
||||
|
|
|
@ -49,28 +49,28 @@ namespace Microsoft::Console::Render
|
|||
// Used to release device resources so that another instance of
|
||||
// conhost can render to the screen (i.e. only one DirectX
|
||||
// application may control the screen at a time.)
|
||||
[[nodiscard]] HRESULT Enable() noexcept;
|
||||
[[nodiscard]] HRESULT Enable() noexcept override;
|
||||
[[nodiscard]] HRESULT Disable() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept;
|
||||
[[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT SetWindowSize(const SIZE pixels) noexcept;
|
||||
[[nodiscard]] HRESULT SetWindowSize(const SIZE pixels) noexcept override;
|
||||
|
||||
void SetCallback(std::function<void()> pfn);
|
||||
void SetWarningCallback(std::function<void(const HRESULT)> pfn);
|
||||
void SetCallback(std::function<void()> pfn) noexcept override;
|
||||
void SetWarningCallback(std::function<void(const HRESULT)> pfn) noexcept override;
|
||||
|
||||
void ToggleShaderEffects();
|
||||
void ToggleShaderEffects() noexcept override;
|
||||
|
||||
bool GetRetroTerminalEffect() const noexcept;
|
||||
void SetRetroTerminalEffect(bool enable) noexcept;
|
||||
bool GetRetroTerminalEffect() const noexcept override;
|
||||
void SetRetroTerminalEffect(bool enable) noexcept override;
|
||||
|
||||
void SetPixelShaderPath(std::wstring_view value) noexcept;
|
||||
void SetPixelShaderPath(std::wstring_view value) noexcept override;
|
||||
|
||||
void SetForceFullRepaintRendering(bool enable) noexcept;
|
||||
void SetForceFullRepaintRendering(bool enable) noexcept override;
|
||||
|
||||
void SetSoftwareRendering(bool enable) noexcept;
|
||||
void SetSoftwareRendering(bool enable) noexcept override;
|
||||
|
||||
HANDLE GetSwapChainHandle();
|
||||
HANDLE GetSwapChainHandle() noexcept override;
|
||||
|
||||
// IRenderEngine Members
|
||||
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override;
|
||||
|
@ -110,7 +110,7 @@ namespace Microsoft::Console::Render
|
|||
const bool usingSoftFont,
|
||||
const bool isSettingDefaultBrushes) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override;
|
||||
|
||||
|
@ -121,17 +121,17 @@ namespace Microsoft::Console::Render
|
|||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
[[nodiscard]] ::Microsoft::Console::Types::Viewport GetViewportInCharacters(const ::Microsoft::Console::Types::Viewport& viewInPixels) noexcept;
|
||||
[[nodiscard]] ::Microsoft::Console::Types::Viewport GetViewportInPixels(const ::Microsoft::Console::Types::Viewport& viewInCharacters) noexcept;
|
||||
[[nodiscard]] ::Microsoft::Console::Types::Viewport GetViewportInCharacters(const ::Microsoft::Console::Types::Viewport& viewInPixels) const noexcept override;
|
||||
[[nodiscard]] ::Microsoft::Console::Types::Viewport GetViewportInPixels(const ::Microsoft::Console::Types::Viewport& viewInCharacters) const noexcept override;
|
||||
|
||||
float GetScaling() const noexcept;
|
||||
float GetScaling() const noexcept override;
|
||||
|
||||
void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept;
|
||||
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept;
|
||||
void SetDefaultTextBackgroundOpacity(const float opacity) noexcept;
|
||||
void SetIntenseIsBold(const bool opacity) noexcept;
|
||||
void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept override;
|
||||
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override;
|
||||
void SetDefaultTextBackgroundOpacity(const float opacity) noexcept override;
|
||||
void SetIntenseIsBold(const bool opacity) noexcept override;
|
||||
|
||||
void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept;
|
||||
void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override;
|
||||
|
|
|
@ -14,12 +14,16 @@ Author(s):
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <d2d1.h>
|
||||
|
||||
#include "CursorOptions.h"
|
||||
#include "Cluster.hpp"
|
||||
#include "FontInfoDesired.hpp"
|
||||
#include "IRenderData.hpp"
|
||||
#include "../../buffer/out/LineRendition.hpp"
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100) // '...': unreferenced formal parameter
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
struct RenderFrameInfo
|
||||
|
@ -44,78 +48,75 @@ namespace Microsoft::Console::Render
|
|||
};
|
||||
using GridLineSet = til::enumset<GridLines>;
|
||||
|
||||
virtual ~IRenderEngine() = 0;
|
||||
#pragma warning(suppress : 26432) // If you define or delete any default operation in the type '...', define or delete them all (c.21).
|
||||
virtual ~IRenderEngine()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
IRenderEngine() = default;
|
||||
IRenderEngine(const IRenderEngine&) = default;
|
||||
IRenderEngine(IRenderEngine&&) = default;
|
||||
IRenderEngine& operator=(const IRenderEngine&) = default;
|
||||
IRenderEngine& operator=(IRenderEngine&&) = default;
|
||||
|
||||
public:
|
||||
[[nodiscard]] virtual HRESULT StartPaint() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT EndPaint() noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual bool RequiresContinuousRedraw() noexcept = 0;
|
||||
virtual void WaitUntilCanRender() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT Present() noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT PrepareForTeardown(_Out_ bool* pForcePaint) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT ScrollFrame() noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT Invalidate(const SMALL_RECT* psrRegion) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateCursor(const SMALL_RECT* psrRegion) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateSystem(const RECT* prcDirtyClient) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector<SMALL_RECT>& rectangles) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* pcoordDelta) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT InvalidateCircling(_Out_ bool* pForcePaint) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT ResetLineTransform() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PrepareLineTransform(const LineRendition lineRendition,
|
||||
const size_t targetRow,
|
||||
const size_t viewportLeft) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
const COORD coord,
|
||||
const bool fTrimLeft,
|
||||
const bool lineWrapped) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBufferGridLines(const GridLineSet lines,
|
||||
const COLORREF color,
|
||||
const size_t cchLine,
|
||||
const COORD coordTarget) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintSelection(const SMALL_RECT rect) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, COORD coord, bool fTrimLeft, bool lineWrapped) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, COORD coordTarget) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintSelection(SMALL_RECT rect) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes,
|
||||
const gsl::not_null<IRenderData*> pData,
|
||||
const bool usingSoftFont,
|
||||
const bool isSettingDefaultBrushes) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired,
|
||||
_Out_ FontInfo& FontInfo) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateSoftFont(const gsl::span<const uint16_t> bitPattern,
|
||||
const SIZE cellSize,
|
||||
const size_t centeringHint) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateDpi(const int iDpi) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired,
|
||||
_Out_ FontInfo& FontInfo,
|
||||
const int iDpi) noexcept = 0;
|
||||
|
||||
[[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, gsl::not_null<IRenderData*> pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateSoftFont(gsl::span<const uint16_t> bitPattern, SIZE cellSize, size_t centeringHint) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateDpi(int iDpi) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateViewport(SMALL_RECT srNewViewport) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, int iDpi) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept = 0;
|
||||
};
|
||||
[[nodiscard]] virtual HRESULT GetFontSize(_Out_ COORD* pFontSize) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateTitle(std::wstring_view newTitle) noexcept = 0;
|
||||
|
||||
inline Microsoft::Console::Render::IRenderEngine::~IRenderEngine() {}
|
||||
// The following functions used to be specific to the DxRenderer and they should
|
||||
// be abstracted away and integrated into the above or simply get removed.
|
||||
|
||||
// DxRenderer - getter
|
||||
virtual HRESULT Enable() noexcept { return S_OK; }
|
||||
virtual [[nodiscard]] bool GetRetroTerminalEffect() const noexcept { return false; }
|
||||
virtual [[nodiscard]] float GetScaling() const noexcept { return 1; }
|
||||
#pragma warning(suppress : 26440) // Function '...' can be declared 'noexcept' (f.6).
|
||||
virtual [[nodiscard]] HANDLE GetSwapChainHandle()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
virtual [[nodiscard]] Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept { return Types::Viewport::Empty(); }
|
||||
virtual [[nodiscard]] Types::Viewport GetViewportInPixels(const Types::Viewport& viewInCharacters) const noexcept { return Types::Viewport::Empty(); }
|
||||
// DxRenderer - setter
|
||||
virtual void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept {}
|
||||
virtual void SetCallback(std::function<void()> pfn) noexcept {}
|
||||
virtual void SetDefaultTextBackgroundOpacity(const float opacity) noexcept {}
|
||||
virtual void SetForceFullRepaintRendering(bool enable) noexcept {}
|
||||
virtual [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; }
|
||||
virtual void SetPixelShaderPath(std::wstring_view value) noexcept {}
|
||||
virtual void SetRetroTerminalEffect(bool enable) noexcept {}
|
||||
virtual void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept {}
|
||||
virtual void SetSoftwareRendering(bool enable) noexcept {}
|
||||
virtual void SetIntenseIsBold(bool enable) noexcept {}
|
||||
virtual void SetWarningCallback(std::function<void(HRESULT)> pfn) noexcept {}
|
||||
virtual [[nodiscard]] HRESULT SetWindowSize(const SIZE pixels) noexcept { return E_NOTIMPL; }
|
||||
virtual void ToggleShaderEffects() noexcept {}
|
||||
virtual [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept { return E_NOTIMPL; }
|
||||
virtual void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept {}
|
||||
};
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
|
|
@ -261,6 +261,13 @@ CATCH_RETURN();
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
// RenderEngineBase defines a WaitUntilCanRender() that sleeps for 8ms to throttle rendering.
|
||||
// But UiaEngine is never the only engine running. Overriding this function prevents
|
||||
// us from sleeping 16ms per frame, when the other engine also sleeps for 8ms.
|
||||
void UiaEngine::WaitUntilCanRender() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Used to perform longer running presentation steps outside the lock so the
|
||||
// other threads can continue.
|
||||
|
|
|
@ -30,18 +30,16 @@ namespace Microsoft::Console::Render
|
|||
// Only one UiaEngine may present information at a time.
|
||||
// This ensures that an automation client isn't overwhelmed
|
||||
// by events when there are multiple TermControls
|
||||
[[nodiscard]] HRESULT Enable() noexcept;
|
||||
[[nodiscard]] HRESULT Enable() noexcept override;
|
||||
[[nodiscard]] HRESULT Disable() noexcept;
|
||||
|
||||
// IRenderEngine Members
|
||||
[[nodiscard]] HRESULT StartPaint() noexcept override;
|
||||
[[nodiscard]] HRESULT EndPaint() noexcept override;
|
||||
void WaitUntilCanRender() noexcept override;
|
||||
[[nodiscard]] HRESULT Present() noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override;
|
||||
|
@ -49,27 +47,16 @@ namespace Microsoft::Console::Render
|
|||
[[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
COORD const coord,
|
||||
bool const fTrimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters, const COORD coord, const bool fTrimLeft, const bool lineWrapped) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF color, const size_t cchLine, const COORD coordTarget) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes,
|
||||
const gsl::not_null<IRenderData*> pData,
|
||||
const bool usingSoftFont,
|
||||
const bool isSettingDefaultBrushes) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const gsl::not_null<IRenderData*> pData, const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDpi(const int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, const int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
|
|
@ -43,70 +43,41 @@ namespace Microsoft::Console::Render
|
|||
VtEngine(_In_ wil::unique_hfile hPipe,
|
||||
const Microsoft::Console::Types::Viewport initialViewport);
|
||||
|
||||
virtual ~VtEngine() override = default;
|
||||
|
||||
// IRenderEngine
|
||||
[[nodiscard]] HRESULT StartPaint() noexcept override;
|
||||
[[nodiscard]] HRESULT EndPaint() noexcept override;
|
||||
[[nodiscard]] HRESULT Present() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* pForcePaint) noexcept override;
|
||||
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSystem(const RECT* prcDirtyClient) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<SMALL_RECT>& rectangles) noexcept override;
|
||||
[[nodiscard]] virtual HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept = 0;
|
||||
[[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override;
|
||||
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* const psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override;
|
||||
|
||||
[[nodiscard]] virtual HRESULT StartPaint() noexcept override;
|
||||
[[nodiscard]] virtual HRESULT EndPaint() noexcept override;
|
||||
[[nodiscard]] virtual HRESULT Present() noexcept override;
|
||||
|
||||
[[nodiscard]] virtual HRESULT ScrollFrame() noexcept = 0;
|
||||
|
||||
[[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* pForcePaint) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span<const Cluster> const clusters,
|
||||
const COORD coord,
|
||||
const bool trimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines,
|
||||
const COLORREF color,
|
||||
const size_t cchLine,
|
||||
const COORD coordTarget) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override;
|
||||
|
||||
[[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept override;
|
||||
|
||||
[[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes,
|
||||
const gsl::not_null<IRenderData*> pData,
|
||||
const bool usingSoftFont,
|
||||
const bool isSettingDefaultBrushes) noexcept = 0;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired,
|
||||
_Out_ FontInfo& pfiFontInfo) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDpi(const int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontDesired,
|
||||
_Out_ FontInfo& Font,
|
||||
const int iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, COORD coord, bool fTrimLeft, bool lineWrapped) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, COORD coordTarget) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintSelection(SMALL_RECT rect) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateViewport(SMALL_RECT srNewViewport) noexcept override;
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept override;
|
||||
|
||||
// VtEngine
|
||||
[[nodiscard]] HRESULT SuppressResizeRepaint() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT RequestCursor() noexcept;
|
||||
[[nodiscard]] HRESULT InheritCursor(const COORD coordCursor) noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT WriteTerminalUtf8(const std::string_view str) noexcept;
|
||||
|
||||
[[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0;
|
||||
|
||||
void SetTerminalOwner(Microsoft::Console::ITerminalOwner* const terminalOwner);
|
||||
void BeginResizeRequest();
|
||||
void EndResizeRequest();
|
||||
|
||||
void SetResizeQuirk(const bool resizeQuirk);
|
||||
|
||||
[[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT RequestWin32Input() noexcept;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Microsoft::Console::Render
|
|||
// Used to release device resources so that another instance of
|
||||
// conhost can render to the screen (i.e. only one DirectX
|
||||
// application may control the screen at a time.)
|
||||
[[nodiscard]] HRESULT Enable() noexcept;
|
||||
[[nodiscard]] HRESULT Enable() noexcept override;
|
||||
[[nodiscard]] HRESULT Disable() noexcept;
|
||||
|
||||
RECT GetDisplaySize();
|
||||
|
|
Loading…
Reference in a new issue