Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// the UIA Engine to the renderer. This prevents us from signaling changes to the cursor or buffer.
{
// Now create the renderer and initialize the render thread.
const auto& renderSettings = _terminal->GetRenderSettings();
auto& renderSettings = _terminal->GetRenderSettings();
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(renderSettings, _terminal.get());

_renderer->SetBackgroundColorChangedCallback([this]() { _rendererBackgroundColorChanged(); });
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/base/RenderSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ void RenderSettings::RestoreDefaultSettings() noexcept
{
_colorTable = _defaultColorTable;
_colorAliasIndices = _defaultColorAliasIndices;
// For now, DECSCNM is the only render mode we need to reset. The others are
// all user preferences that can't be changed programmatically.
_renderMode.reset(Mode::ScreenReversed);
// DECSCNM and Synchronized Output are the only render mode we need to reset.
// The others are all user preferences that can't be changed programmatically.
_renderMode.reset(Mode::ScreenReversed, Mode::SynchronizedOutput);
}

// Routine Description:
Expand Down
54 changes: 30 additions & 24 deletions src/renderer/base/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 };
// - pData - The interface to console data structures required for rendering
// Return Value:
// - An instance of a Renderer.
Renderer::Renderer(const RenderSettings& renderSettings, IRenderData* pData) :
Renderer::Renderer(RenderSettings& renderSettings, IRenderData* pData) :
_renderSettings(renderSettings),
_pData(pData)
{
Expand Down Expand Up @@ -187,31 +187,36 @@ void Renderer::NotifyPaintFrame() noexcept
}

// NOTE: You must be holding the console lock when calling this function.
void Renderer::SynchronizedOutputBegin() noexcept
void Renderer::SynchronizedOutputChanged() noexcept
{
// Kick the render thread into calling `_synchronizeWithOutput()`.
_isSynchronizingOutput = true;
}
const auto so = _renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput);
if (_isSynchronizingOutput == so)
{
return;
}

// NOTE: You must be holding the console lock when calling this function.
void Renderer::SynchronizedOutputEnd() noexcept
{
// Unblock `_synchronizeWithOutput()` from the `WaitOnAddress` call.
_isSynchronizingOutput = false;
WakeByAddressSingle(&_isSynchronizingOutput);

// It's crucial to give the render thread at least a chance to gain the lock.
// Otherwise, a VT application could continuously spam DECSET 2026 (Synchronized Output) and
// essentially drop our renderer to 10 FPS, because `_isSynchronizingOutput` is always true.
//
// Obviously calling LockConsole/UnlockConsole here is an awful, ugly hack,
// since there's no guarantee that this is the same lock as the one the VT parser uses.
// But the alternative is Denial-Of-Service of the render thread.
//
// Note that this causes raw throughput of DECSET 2026 to be comparatively low, but that's fine.
// Apps that use DECSET 2026 don't produce that sequence continuously, but rather at a fixed rate.
_pData->UnlockConsole();
_pData->LockConsole();
// If `_isSynchronizingOutput` is true, it'll kick the
// render thread into calling `_synchronizeWithOutput()`...
_isSynchronizingOutput = so;

if (!_isSynchronizingOutput)
{
// ...otherwise, unblock `_synchronizeWithOutput()` from the `WaitOnAddress` call.
WakeByAddressSingle(&_isSynchronizingOutput);

// It's crucial to give the render thread at least a chance to gain the lock.
// Otherwise, a VT application could continuously spam DECSET 2026 (Synchronized Output) and
// essentially drop our renderer to 10 FPS, because `_isSynchronizingOutput` is always true.
//
// Obviously calling LockConsole/UnlockConsole here is an awful, ugly hack,
// since there's no guarantee that this is the same lock as the one the VT parser uses.
// But the alternative is Denial-Of-Service of the render thread.
//
// Note that this causes raw throughput of DECSET 2026 to be comparatively low, but that's fine.
// Apps that use DECSET 2026 don't produce that sequence continuously, but rather at a fixed rate.
_pData->UnlockConsole();
_pData->LockConsole();
}
}

void Renderer::_synchronizeWithOutput() noexcept
Expand Down Expand Up @@ -249,6 +254,7 @@ void Renderer::_synchronizeWithOutput() noexcept
// If a timeout occurred, `_isSynchronizingOutput` may still be true.
// Set it to false now to skip calling `_synchronizeWithOutput()` on the next frame.
_isSynchronizingOutput = false;
_renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, false);
}

// Routine Description:
Expand Down
7 changes: 3 additions & 4 deletions src/renderer/base/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ namespace Microsoft::Console::Render
class Renderer
{
public:
Renderer(const RenderSettings& renderSettings, IRenderData* pData);
Renderer(RenderSettings& renderSettings, IRenderData* pData);

IRenderData* GetRenderData() const noexcept;

[[nodiscard]] HRESULT PaintFrame();

void NotifyPaintFrame() noexcept;
void SynchronizedOutputBegin() noexcept;
void SynchronizedOutputEnd() noexcept;
void SynchronizedOutputChanged() noexcept;
void TriggerSystemRedraw(const til::rect* const prcDirtyClient);
void TriggerRedraw(const Microsoft::Console::Types::Viewport& region);
void TriggerRedraw(const til::point* const pcoord);
Expand Down Expand Up @@ -113,7 +112,7 @@ namespace Microsoft::Console::Render
void _prepareNewComposition();
[[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine);

const RenderSettings& _renderSettings;
RenderSettings& _renderSettings;
std::array<IRenderEngine*, 2> _engines{};
IRenderData* _pData = nullptr; // Non-ownership pointer
static constexpr size_t _firstSoftFontChar = 0xEF20;
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/inc/RenderSettings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ namespace Microsoft::Console::Render
AlwaysDistinguishableColors,
IntenseIsBold,
IntenseIsBright,
ScreenReversed
ScreenReversed,
SynchronizedOutput,
};

RenderSettings() noexcept;
Expand Down
14 changes: 6 additions & 8 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1915,16 +1915,10 @@ void AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con
_api.SetSystemMode(ITerminalApi::Mode::BracketedPaste, enable);
break;
case DispatchTypes::ModeParams::SO_SynchronizedOutput:
_renderSettings.SetRenderMode(RenderSettings::Mode::SynchronizedOutput, enable);
if (_renderer)
{
if (enable)
{
_renderer->SynchronizedOutputBegin();
}
else
{
_renderer->SynchronizedOutputEnd();
}
_renderer->SynchronizedOutputChanged();
}
break;
case DispatchTypes::ModeParams::GCM_GraphemeClusterMode:
Expand Down Expand Up @@ -2065,6 +2059,9 @@ void AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param)
case DispatchTypes::ModeParams::XTERM_BracketedPasteMode:
state = mapTemp(_api.GetSystemMode(ITerminalApi::Mode::BracketedPaste));
break;
case DispatchTypes::ModeParams::SO_SynchronizedOutput:
state = mapTemp(_renderSettings.GetRenderMode(RenderSettings::Mode::SynchronizedOutput));
break;
case DispatchTypes::ModeParams::GCM_GraphemeClusterMode:
state = mapPerm(CodepointWidthDetector::Singleton().GetMode() == TextMeasurementMode::Graphemes);
break;
Expand Down Expand Up @@ -3050,6 +3047,7 @@ void AdaptDispatch::HardReset()
if (_renderer)
{
_renderer->TriggerRedrawAll(true, true);
_renderer->SynchronizedOutputChanged();
}

// Cursor to 1,1 - the Soft Reset guarantees this is absolute
Expand Down
Loading