diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index ef2f88e60b5..2e3baf89e98 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -326,7 +326,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Tell the DX Engine to notify us when the swap chain changes. // We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems) - _renderEngine->SetCallback(std::bind(&ControlCore::_renderEngineSwapChainChanged, this)); + _renderEngine->SetCallback([this](auto handle) { _renderEngineSwapChainChanged(handle); }); _renderEngine->SetRetroTerminalEffect(_settings->RetroTerminalEffect()); _renderEngine->SetPixelShaderPath(_settings->PixelShaderPath()); @@ -596,24 +596,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::ToggleShaderEffects() { + const auto path = _settings->PixelShaderPath(); auto lock = _terminal->LockForWriting(); // Originally, this action could be used to enable the retro effects // even when they're set to `false` in the settings. If the user didn't // specify a custom pixel shader, manually enable the legacy retro // effect first. This will ensure that a toggle off->on will still work, // even if they currently have retro effect off. - if (_settings->PixelShaderPath().empty() && !_renderEngine->GetRetroTerminalEffect()) + if (path.empty()) { - // SetRetroTerminalEffect to true will enable the effect. In this - // case, the shader effect will already be disabled (because neither - // a pixel shader nor the retro effects were originally requested). - // So we _don't_ want to toggle it again below, because that would - // toggle it back off. - _renderEngine->SetRetroTerminalEffect(true); + _renderEngine->SetRetroTerminalEffect(!_renderEngine->GetRetroTerminalEffect()); } else { - _renderEngine->ToggleShaderEffects(); + _renderEngine->SetPixelShaderPath(_renderEngine->GetPixelShaderPath().empty() ? std::wstring_view{ path } : std::wstring_view{}); } // Always redraw after toggling effects. This way even if the control // does not have focus it will update immediately. @@ -1558,25 +1554,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - uint64_t ControlCore::SwapChainHandle() const - { - // This is called by: - // * TermControl::RenderEngineSwapChainChanged, who is only registered - // after Core::Initialize() is called. - // * TermControl::_InitializeTerminal, after the call to Initialize, for - // _AttachDxgiSwapChainToXaml. - // In both cases, we'll have a _renderEngine by then. - return reinterpret_cast(_renderEngine->GetSwapChainHandle()); - } - void ControlCore::_rendererWarning(const HRESULT hr) { _RendererWarningHandlers(*this, winrt::make(hr)); } - void ControlCore::_renderEngineSwapChainChanged() + void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle) { - _SwapChainChangedHandlers(*this, nullptr); + _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(handle))); } void ControlCore::_rendererBackgroundColorChanged() diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 1f9463ae1e4..6f765736354 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -75,7 +75,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); - uint64_t SwapChainHandle() const; void AdjustFontSize(int fontSizeDelta); void ResetFontSize(); @@ -315,7 +314,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation #pragma region RendererCallbacks void _rendererWarning(const HRESULT hr); - void _renderEngineSwapChainChanged(); + void _renderEngineSwapChainChanged(const HANDLE handle); void _rendererBackgroundColorChanged(); void _rendererTabColorChanged(); #pragma endregion diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 7ea4d7ee11b..200258fae01 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -76,8 +76,6 @@ namespace Microsoft.Terminal.Control IControlAppearance UnfocusedAppearance { get; }; Boolean HasUnfocusedAppearance(); - UInt64 SwapChainHandle { get; }; - Windows.Foundation.Size FontSize { get; }; String FontFaceName { get; }; UInt16 FontWeight { get; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index c5cd16cce82..5d4061ee4a3 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -731,19 +731,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ConnectionState(); } - winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/) + winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) { // This event is only registered during terminal initialization, // so we don't need to check _initializedTerminal. - // We also don't lock for things that come back from the renderer. - auto weakThis{ get_weak() }; + const auto weakThis{ get_weak() }; + + // Create a copy of the swap chain HANDLE in args, since we don't own that parameter. + // By the time we return from the co_await below, it might be deleted already. + winrt::handle handle; + const auto processHandle = GetCurrentProcess(); + const auto sourceHandle = reinterpret_cast(winrt::unbox_value(args)); + THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(processHandle, sourceHandle, processHandle, handle.put(), 0, FALSE, DUPLICATE_SAME_ACCESS)); co_await wil::resume_foreground(Dispatcher()); if (auto control{ weakThis.get() }) { - const auto chainHandle = reinterpret_cast(control->_core.SwapChainHandle()); - _AttachDxgiSwapChainToXaml(chainHandle); + _AttachDxgiSwapChainToXaml(handle.get()); } } @@ -830,21 +835,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } _interactivity.Initialize(); - _AttachDxgiSwapChainToXaml(reinterpret_cast(_core.SwapChainHandle())); - - // Tell the DX Engine to notify us when the swap chain changes. We do - // this after we initially set the swapchain so as to avoid unnecessary - // callbacks (and locking problems) _core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); - - // !! LOAD BEARING !! - // Make sure you enable painting _AFTER_ calling _AttachDxgiSwapChainToXaml - // - // If you EnablePainting first, then you almost certainly won't have any - // problems when running in Debug. However, in Release, you'll run into - // issues where the Renderer starts trying to paint before we've - // actually attached the swapchain to anything, and the DxEngine is not - // prepared to handle that. _core.EnablePainting(); auto bufferHeight = _core.BufferHeight(); diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 59dbb4f5ac4..8861704b5f6 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -291,6 +291,11 @@ HRESULT AtlasEngine::Enable() noexcept return S_OK; } +[[nodiscard]] std::wstring_view AtlasEngine::GetPixelShaderPath() noexcept +{ + return _api.customPixelShaderPath; +} + [[nodiscard]] bool AtlasEngine::GetRetroTerminalEffect() const noexcept { return _api.useRetroTerminalEffect; @@ -301,17 +306,6 @@ HRESULT AtlasEngine::Enable() noexcept return static_cast(_api.dpi) / static_cast(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); @@ -337,7 +331,7 @@ void AtlasEngine::SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasin } } -void AtlasEngine::SetCallback(std::function pfn) noexcept +void AtlasEngine::SetCallback(std::function pfn) noexcept { _api.swapChainChangedCallback = std::move(pfn); } @@ -428,10 +422,6 @@ void AtlasEngine::SetWarningCallback(std::function pfn) noexcept return S_OK; } -void AtlasEngine::ToggleShaderEffects() noexcept -{ -} - [[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept { static constexpr std::array fallbackFaceNames{ static_cast(nullptr), L"Consolas", L"Lucida Console", L"Courier New" }; diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index e33ac4fae22..91046f34d43 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -582,6 +582,9 @@ void AtlasEngine::_createResources() D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, }; auto hr = E_UNEXPECTED; @@ -624,16 +627,6 @@ void AtlasEngine::_createResources() _r.deviceContext = deviceContext.query(); } - { - wil::com_ptr dxgiAdapter; - THROW_IF_FAILED(_r.device.query()->GetParent(__uuidof(dxgiAdapter), dxgiAdapter.put_void())); - THROW_IF_FAILED(dxgiAdapter->GetParent(__uuidof(_r.dxgiFactory), _r.dxgiFactory.put_void())); - - DXGI_ADAPTER_DESC1 desc; - THROW_IF_FAILED(dxgiAdapter->GetDesc1(&desc)); - _r.d2dMode = debugForceD2DMode || WI_IsAnyFlagSet(desc.Flags, DXGI_ADAPTER_FLAG_REMOTE | DXGI_ADAPTER_FLAG_SOFTWARE); - } - #ifndef NDEBUG // D3D debug messages if (deviceFlags & D3D11_CREATE_DEVICE_DEBUG) @@ -646,6 +639,18 @@ void AtlasEngine::_createResources() } #endif // NDEBUG + const auto featureLevel = _r.device->GetFeatureLevel(); + + { + wil::com_ptr dxgiAdapter; + THROW_IF_FAILED(_r.device.query()->GetParent(__uuidof(dxgiAdapter), dxgiAdapter.put_void())); + THROW_IF_FAILED(dxgiAdapter->GetParent(__uuidof(_r.dxgiFactory), _r.dxgiFactory.put_void())); + + DXGI_ADAPTER_DESC1 desc; + THROW_IF_FAILED(dxgiAdapter->GetDesc1(&desc)); + _r.d2dMode = debugForceD2DMode || featureLevel < D3D_FEATURE_LEVEL_10_0 || WI_IsAnyFlagSet(desc.Flags, DXGI_ADAPTER_FLAG_REMOTE | DXGI_ADAPTER_FLAG_SOFTWARE); + } + if (!_r.d2dMode) { // Our constant buffer will never get resized @@ -663,7 +668,7 @@ void AtlasEngine::_createResources() if (!_api.customPixelShaderPath.empty()) { const char* target = nullptr; - switch (_r.device->GetFeatureLevel()) + switch (featureLevel) { case D3D_FEATURE_LEVEL_10_0: target = "ps_4_0"; @@ -811,10 +816,19 @@ void AtlasEngine::_createSwapChain() desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.BufferCount = 2; + // Sometimes up to 2 buffers are locked, for instance during screen capture or when moving the window. + // 3 buffers seems to guarantee a stable framerate at display frequency at all times. + desc.BufferCount = 3; desc.Scaling = DXGI_SCALING_NONE; - desc.SwapEffect = _sr.isWindows10OrGreater && !_r.d2dMode ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - // If our background is opaque we can enable "independent" flips by setting DXGI_SWAP_EFFECT_FLIP_DISCARD and DXGI_ALPHA_MODE_IGNORE. + // DXGI_SWAP_EFFECT_FLIP_DISCARD is a mode that was created at a time were display drivers + // lacked support for Multiplane Overlays (MPO) and were copying buffers was expensive. + // This allowed DWM to quickly draw overlays (like gamebars) on top of rendered content. + // With faster GPU memory in general and with support for MPO in particular this isn't + // really an advantage anymore. Instead DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL allows for a + // more "intelligent" composition and display updates to occur like Panel Self Refresh + // (PSR) which requires dirty rectangles (Present1 API) to work correctly. + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + // If our background is opaque we can enable "independent" flips by setting DXGI_ALPHA_MODE_IGNORE. // As our swap chain won't have to compose with DWM anymore it reduces the display latency dramatically. desc.AlphaMode = _api.backgroundOpaqueMixin ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; desc.Flags = debugGeneralPerformance ? 0 : DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; @@ -857,7 +871,7 @@ void AtlasEngine::_createSwapChain() { try { - _api.swapChainChangedCallback(); + _api.swapChainChangedCallback(_api.swapChainHandle.get()); } CATCH_LOG(); } diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 251595c47d2..5b6b9db3d39 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include @@ -60,14 +60,14 @@ namespace Microsoft::Console::Render // DxRenderer - getter HRESULT Enable() noexcept override; + [[nodiscard]] std::wstring_view GetPixelShaderPath() 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 pfn) noexcept override; + void SetCallback(std::function pfn) noexcept override; void EnableTransparentBackground(const bool isTransparent) noexcept override; void SetForceFullRepaintRendering(bool enable) noexcept override; [[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override; @@ -77,7 +77,6 @@ namespace Microsoft::Console::Render void SetSoftwareRendering(bool enable) noexcept override; void SetWarningCallback(std::function pfn) noexcept override; [[nodiscard]] HRESULT SetWindowSize(til::size pixels) noexcept override; - void ToggleShaderEffects() noexcept override; [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept override; void UpdateHyperlinkHoveredId(uint16_t hoveredId) noexcept override; @@ -1008,7 +1007,7 @@ namespace Microsoft::Console::Render // D2D resources wil::com_ptr atlasBuffer; wil::com_ptr atlasView; - wil::com_ptr d2dRenderTarget; + wil::com_ptr d2dRenderTarget; wil::com_ptr brush; wil::com_ptr textFormats[2][2]; Buffer textFormatAxes[2][2]; @@ -1038,7 +1037,6 @@ namespace Microsoft::Console::Render CachedCursorOptions cursorOptions; RenderInvalidations invalidations = RenderInvalidations::None; - til::rect previousDirtyRectInPx; til::rect dirtyRect; i16 scrollOffset = 0; bool d2dMode = false; @@ -1095,7 +1093,7 @@ namespace Microsoft::Console::Render i16 scrollOffset = 0; std::function warningCallback; - std::function swapChainChangedCallback; + std::function swapChainChangedCallback; wil::unique_handle swapChainHandle; HWND hwnd = nullptr; u16 dpi = USER_DEFAULT_SCREEN_DPI; // changes are flagged as ApiInvalidations::Font|Size diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index bdfaa4a9a51..e727570a4a4 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -63,6 +63,19 @@ using namespace Microsoft::Console::Render; [[nodiscard]] HRESULT AtlasEngine::Present() noexcept try { + const til::rect fullRect{ 0, 0, _r.cellCount.x, _r.cellCount.y }; + + // A change in the selection or background color (etc.) forces a full redraw. + if (WI_IsFlagSet(_r.invalidations, RenderInvalidations::ConstBuffer) || _r.customPixelShader) + { + _r.dirtyRect = fullRect; + } + + if (!_r.dirtyRect) + { + return S_OK; + } + // 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. @@ -102,14 +115,60 @@ try _r.deviceContext->OMSetRenderTargets(1, _r.renderTargetView.addressof(), nullptr); _r.deviceContext->Draw(3, 0); } + } - // > 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. + // 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(debugGeneralPerformance || _r.frameLatencyWaitableObjectUsed); + + if (_r.dirtyRect != fullRect) + { + auto dirtyRectInPx = _r.dirtyRect; + dirtyRectInPx.left *= _r.fontMetrics.cellSize.x; + dirtyRectInPx.top *= _r.fontMetrics.cellSize.y; + dirtyRectInPx.right *= _r.fontMetrics.cellSize.x; + dirtyRectInPx.bottom *= _r.fontMetrics.cellSize.y; + + RECT scrollRect{}; + POINT scrollOffset{}; + DXGI_PRESENT_PARAMETERS params{ + .DirtyRectsCount = 1, + .pDirtyRects = dirtyRectInPx.as_win32_rect(), + }; + + if (_r.scrollOffset) + { + scrollRect = { + 0, + std::max(0, _r.scrollOffset), + _r.cellCount.x, + _r.cellCount.y + std::min(0, _r.scrollOffset), + }; + scrollOffset = { + 0, + _r.scrollOffset, + }; + + scrollRect.top *= _r.fontMetrics.cellSize.y; + scrollRect.right *= _r.fontMetrics.cellSize.x; + scrollRect.bottom *= _r.fontMetrics.cellSize.y; + + scrollOffset.y *= _r.fontMetrics.cellSize.y; + + params.pScrollRect = &scrollRect; + params.pScrollOffset = &scrollOffset; + } + + THROW_IF_FAILED(_r.swapChain->Present1(1, 0, ¶ms)); + } + else + { THROW_IF_FAILED(_r.swapChain->Present(1, 0)); - _r.waitForPresentation = true; } + _r.waitForPresentation = true; + if (!_r.dxgiFactory->IsCurrent()) { WI_SetFlag(_api.invalidations, ApiInvalidations::Device); @@ -307,7 +366,9 @@ void AtlasEngine::_adjustAtlasSize() props.pixelFormat = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED }; props.dpiX = static_cast(_r.dpi); props.dpiY = static_cast(_r.dpi); - THROW_IF_FAILED(_sr.d2dFactory->CreateDxgiSurfaceRenderTarget(surface.get(), &props, _r.d2dRenderTarget.put())); + wil::com_ptr renderTarget; + THROW_IF_FAILED(_sr.d2dFactory->CreateDxgiSurfaceRenderTarget(surface.get(), &props, renderTarget.addressof())); + _r.d2dRenderTarget = renderTarget.query(); // We don't really use D2D for anything except DWrite, but it // can't hurt to ensure that everything it does is pixel aligned. @@ -749,102 +810,19 @@ void AtlasEngine::CachedGlyphLayout::undoScaling(ID2D1RenderTarget* d2dRenderTar void AtlasEngine::_d2dPresent() { - const til::rect fullRect{ 0, 0, _r.cellCount.x, _r.cellCount.y }; - - // A change in the selection or background color (etc.) forces a full redraw. - if (WI_IsFlagSet(_r.invalidations, RenderInvalidations::ConstBuffer)) + if (!_r.d2dRenderTarget) { - _r.dirtyRect = fullRect; + _d2dCreateRenderTarget(); } - if (!_r.dirtyRect) - { - return; - } - - auto dirtyRectInPx = _r.dirtyRect; - dirtyRectInPx.left *= _r.fontMetrics.cellSize.x; - dirtyRectInPx.top *= _r.fontMetrics.cellSize.y; - dirtyRectInPx.right *= _r.fontMetrics.cellSize.x; - dirtyRectInPx.bottom *= _r.fontMetrics.cellSize.y; - - if (const auto intersection = _r.previousDirtyRectInPx & dirtyRectInPx) - { - wil::com_ptr backBuffer; - wil::com_ptr frontBuffer; - THROW_IF_FAILED(_r.swapChain->GetBuffer(0, __uuidof(backBuffer), backBuffer.put_void())); - THROW_IF_FAILED(_r.swapChain->GetBuffer(1, __uuidof(frontBuffer), frontBuffer.put_void())); - - D3D11_BOX intersectBox; - intersectBox.left = intersection.left; - intersectBox.top = intersection.top; - intersectBox.front = 0; - intersectBox.right = intersection.right; - intersectBox.bottom = intersection.bottom; - intersectBox.back = 1; - _r.deviceContext->CopySubresourceRegion1(backBuffer.get(), 0, intersection.left, intersection.top, 0, frontBuffer.get(), 0, &intersectBox, 0); - } - - _d2dCreateRenderTarget(); _d2dDrawDirtyArea(); - // 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(debugGeneralPerformance || _r.frameLatencyWaitableObjectUsed); - - if (_r.dirtyRect != fullRect) - { - RECT scrollRect{}; - POINT scrollOffset{}; - DXGI_PRESENT_PARAMETERS params{ - .DirtyRectsCount = 1, - .pDirtyRects = dirtyRectInPx.as_win32_rect(), - }; - - if (_r.scrollOffset) - { - scrollRect = { - 0, - std::max(0, _r.scrollOffset), - _r.cellCount.x, - _r.cellCount.y + std::min(0, _r.scrollOffset), - }; - scrollOffset = { - 0, - _r.scrollOffset, - }; - - scrollRect.top *= _r.fontMetrics.cellSize.y; - scrollRect.right *= _r.fontMetrics.cellSize.x; - scrollRect.bottom *= _r.fontMetrics.cellSize.y; - - scrollOffset.y *= _r.fontMetrics.cellSize.y; - - params.pScrollRect = &scrollRect; - params.pScrollOffset = &scrollOffset; - } - - THROW_IF_FAILED(_r.swapChain->Present1(1, 0, ¶ms)); - } - else - { - THROW_IF_FAILED(_r.swapChain->Present(1, 0)); - } - _r.glyphQueue.clear(); - _r.previousDirtyRectInPx = dirtyRectInPx; - _r.waitForPresentation = true; WI_ClearAllFlags(_r.invalidations, RenderInvalidations::Cursor | RenderInvalidations::ConstBuffer); } void AtlasEngine::_d2dCreateRenderTarget() { - if (_r.d2dRenderTarget) - { - return; - } - { wil::com_ptr buffer; THROW_IF_FAILED(_r.swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), buffer.put_void())); @@ -856,7 +834,9 @@ void AtlasEngine::_d2dCreateRenderTarget() props.pixelFormat = { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED }; props.dpiX = static_cast(_r.dpi); props.dpiY = static_cast(_r.dpi); - THROW_IF_FAILED(_sr.d2dFactory->CreateDxgiSurfaceRenderTarget(surface.get(), &props, _r.d2dRenderTarget.put())); + wil::com_ptr renderTarget; + THROW_IF_FAILED(_sr.d2dFactory->CreateDxgiSurfaceRenderTarget(surface.get(), &props, renderTarget.addressof())); + _r.d2dRenderTarget = renderTarget.query(); // In case _api.realizedAntialiasingMode is D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE we'll // continuously adjust it in AtlasEngine::_drawGlyph. See _drawGlyph. @@ -944,6 +924,8 @@ void AtlasEngine::_d2dDrawDirtyArea() // Draw background. { + _r.d2dRenderTarget->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY); + auto x1 = beg; auto x2 = gsl::narrow_cast(x1 + 1); auto currentColor = cells[x1].color.y; @@ -965,6 +947,8 @@ void AtlasEngine::_d2dDrawDirtyArea() const u16r rect{ x1, y, x2, gsl::narrow_cast(y + 1) }; _d2dFillRectangle(rect, currentColor); } + + _r.d2dRenderTarget->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER); } // Draw text. diff --git a/src/renderer/atlas/pch.h b/src/renderer/atlas/pch.h index 5d59e23544b..a436ae580af 100644 --- a/src/renderer/atlas/pch.h +++ b/src/renderer/atlas/pch.h @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 9621dbb8bb7..540e166ad99 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -244,19 +244,6 @@ bool DxEngine::_HasTerminalEffects() const noexcept return _terminalEffectsEnabled && (_retroTerminalEffect || !_pixelShaderPath.empty()); } -// Routine Description: -// - Toggles terminal effects off and on. If no terminal effect is configured has no effect -// Arguments: -// Return Value: -// - Void -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()); -} - // Routine Description: // - Loads pixel shader source depending on _retroTerminalEffect and _pixelShaderPath // Arguments: @@ -744,7 +731,7 @@ try { try { - _pfn(); + _pfn(_swapChainHandle.get()); } CATCH_LOG(); // A failure in the notification function isn't a failure to prepare, so just log it and go on. } @@ -996,7 +983,7 @@ try } CATCH_RETURN(); -void DxEngine::SetCallback(std::function pfn) noexcept +void DxEngine::SetCallback(std::function pfn) noexcept { _pfn = std::move(pfn); } @@ -1025,6 +1012,11 @@ try } CATCH_LOG() +std::wstring_view DxEngine::GetPixelShaderPath() noexcept +{ + return _pixelShaderPath; +} + void DxEngine::SetPixelShaderPath(std::wstring_view value) noexcept try { @@ -1062,17 +1054,6 @@ try } CATCH_LOG() -HANDLE DxEngine::GetSwapChainHandle() noexcept -{ - if (!_swapChainHandle) - { -#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(); -} - void DxEngine::_InvalidateRectangle(const til::rect& rc) { const auto size = _invalidMap.size(); diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 7d49fe141cb..7a6e2b55de4 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -57,22 +57,19 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT SetWindowSize(const til::size pixels) noexcept override; - void SetCallback(std::function pfn) noexcept override; + void SetCallback(std::function pfn) noexcept override; void SetWarningCallback(std::function pfn) noexcept override; - void ToggleShaderEffects() noexcept override; - bool GetRetroTerminalEffect() const noexcept override; void SetRetroTerminalEffect(bool enable) noexcept override; + std::wstring_view GetPixelShaderPath() noexcept override; void SetPixelShaderPath(std::wstring_view value) noexcept override; void SetForceFullRepaintRendering(bool enable) noexcept override; void SetSoftwareRendering(bool enable) noexcept override; - HANDLE GetSwapChainHandle() noexcept override; - // IRenderEngine Members [[nodiscard]] HRESULT Invalidate(const til::rect* const psrRegion) noexcept override; [[nodiscard]] HRESULT InvalidateCursor(const til::rect* const psrRegion) noexcept override; @@ -162,7 +159,7 @@ namespace Microsoft::Console::Render float _scale; float _prevScale; - std::function _pfn; + std::function _pfn; std::function _pfnWarningCallback; bool _isEnabled; diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 259b432594f..dc641bb3861 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -94,18 +94,14 @@ namespace Microsoft::Console::Render // DxRenderer - getter virtual HRESULT Enable() noexcept { return S_OK; } + virtual [[nodiscard]] std::wstring_view GetPixelShaderPath() noexcept { return {}; } 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 pfn) noexcept {} + virtual void SetCallback(std::function pfn) noexcept {} virtual void EnableTransparentBackground(const bool isTransparent) noexcept {} virtual void SetForceFullRepaintRendering(bool enable) noexcept {} virtual [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; } @@ -115,7 +111,6 @@ namespace Microsoft::Console::Render virtual void SetSoftwareRendering(bool enable) noexcept {} virtual void SetWarningCallback(std::function pfn) noexcept {} virtual [[nodiscard]] HRESULT SetWindowSize(const til::size pixels) noexcept { return E_NOTIMPL; } - virtual void ToggleShaderEffects() noexcept {} virtual [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept { return E_NOTIMPL; } virtual void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept {} };