// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #pragma once #include "../../renderer/inc/RenderEngineBase.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CustomTextLayout.h" #include "CustomTextRenderer.h" #include "DxFontRenderData.h" #include "../../types/inc/Viewport.hpp" #include TRACELOGGING_DECLARE_PROVIDER(g_hDxRenderProvider); namespace Microsoft::Console::Render { class DxEngine final : public RenderEngineBase { public: DxEngine(); ~DxEngine(); DxEngine(const DxEngine&) = default; DxEngine(DxEngine&&) = default; DxEngine& operator=(const DxEngine&) = default; DxEngine& operator=(DxEngine&&) = default; // 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 override; [[nodiscard]] HRESULT Disable() noexcept; [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept override; [[nodiscard]] HRESULT SetWindowSize(const SIZE pixels) 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; 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 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; [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; [[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 PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; [[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 ScrollFrame() noexcept override; [[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override; [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] HRESULT PaintBufferLine(gsl::span 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 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 pData, 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& features, const std::unordered_map& axes) noexcept override; [[nodiscard]] HRESULT UpdateDpi(int const 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 GetDirtyArea(gsl::span& 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]] ::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 override; void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept override; void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override; void EnableTransparentBackground(const bool isTransparent) noexcept override; void SetIntenseIsBold(const bool opacity) noexcept override; void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept override; protected: [[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override; [[nodiscard]] HRESULT _PaintTerminalEffects() noexcept; [[nodiscard]] bool _FullRepaintNeeded() const noexcept; private: enum class SwapChainMode { ForHwnd, ForComposition }; SwapChainMode _chainMode; HWND _hwndTarget; til::size _sizeTarget; int _dpi; float _scale; float _prevScale; std::function _pfn; std::function _pfnWarningCallback; bool _isEnabled; bool _isPainting; til::size _displaySizePixels; D2D1_COLOR_F _defaultForegroundColor; D2D1_COLOR_F _defaultBackgroundColor; D2D1_COLOR_F _foregroundColor; D2D1_COLOR_F _backgroundColor; D2D1_COLOR_F _selectionBackground; uint16_t _hyperlinkHoveredId; bool _firstFrame; std::pmr::unsynchronized_pool_resource _pool; til::pmr::bitmap _invalidMap; til::point _invalidScroll; bool _allInvalid; bool _presentReady; std::vector _presentDirty; RECT _presentScroll; POINT _presentOffset; DXGI_PRESENT_PARAMETERS _presentParams; static std::atomic _tracelogCount; wil::unique_handle _swapChainHandle; // Device-Independent Resources ::Microsoft::WRL::ComPtr _d2dFactory; ::Microsoft::WRL::ComPtr _dwriteFactory; ::Microsoft::WRL::ComPtr _customLayout; ::Microsoft::WRL::ComPtr _customRenderer; ::Microsoft::WRL::ComPtr _strokeStyle; ::Microsoft::WRL::ComPtr _dashStrokeStyle; ::Microsoft::WRL::ComPtr _hyperlinkStrokeStyle; std::unique_ptr _fontRenderData; D2D1_STROKE_STYLE_PROPERTIES _strokeStyleProperties; D2D1_STROKE_STYLE_PROPERTIES _dashStrokeStyleProperties; // Device-Dependent Resources bool _recreateDeviceRequested; bool _haveDeviceResources; ::Microsoft::WRL::ComPtr _d3dDevice; ::Microsoft::WRL::ComPtr _d3dDeviceContext; ::Microsoft::WRL::ComPtr _d2dDevice; ::Microsoft::WRL::ComPtr _d2dDeviceContext; ::Microsoft::WRL::ComPtr _d2dBitmap; ::Microsoft::WRL::ComPtr _d2dBrushForeground; ::Microsoft::WRL::ComPtr _d2dBrushBackground; ::Microsoft::WRL::ComPtr _dxgiFactory2; ::Microsoft::WRL::ComPtr _dxgiFactoryMedia; ::Microsoft::WRL::ComPtr _dxgiDevice; ::Microsoft::WRL::ComPtr _dxgiSurface; DXGI_SWAP_CHAIN_DESC1 _swapChainDesc; ::Microsoft::WRL::ComPtr _dxgiSwapChain; wil::unique_handle _swapChainFrameLatencyWaitableObject; std::unique_ptr _drawingContext; // Terminal effects resources. // Controls if configured terminal effects are enabled bool _terminalEffectsEnabled; // Experimental and deprecated retro terminal effect // Preserved for backwards compatibility // Implemented in terms of the more generic pixel shader effect // Has precendence over pixel shader effect bool _retroTerminalEffect; // Experimental and pixel shader effect // Allows user to load a pixel shader from a few presets or from a file path std::wstring _pixelShaderPath; bool _pixelShaderLoaded{ false }; std::chrono::steady_clock::time_point _shaderStartTime; // DX resources needed for terminal effects ::Microsoft::WRL::ComPtr _renderTargetView; ::Microsoft::WRL::ComPtr _vertexShader; ::Microsoft::WRL::ComPtr _pixelShader; ::Microsoft::WRL::ComPtr _vertexLayout; ::Microsoft::WRL::ComPtr _screenQuadVertexBuffer; ::Microsoft::WRL::ComPtr _pixelShaderSettingsBuffer; ::Microsoft::WRL::ComPtr _samplerState; ::Microsoft::WRL::ComPtr _framebufferCapture; // Preferences and overrides bool _softwareRendering; bool _forceFullRepaintRendering; D2D1_TEXT_ANTIALIAS_MODE _antialiasingMode; bool _defaultBackgroundIsTransparent; bool _intenseIsBold; // DirectX constant buffers need to be a multiple of 16; align to pad the size. __declspec(align(16)) struct { // Note: This can be seen as API endpoint towards user provided pixel shaders. // Changes here can break existing pixel shaders so be careful with changing datatypes // and order of parameters float Time; float Scale; DirectX::XMFLOAT2 Resolution; DirectX::XMFLOAT4 Background; #pragma warning(suppress : 4324) // structure was padded due to __declspec(align()) } _pixelShaderSettings; [[nodiscard]] HRESULT _CreateDeviceResources(const bool createSwapChain) noexcept; [[nodiscard]] HRESULT _CreateSurfaceHandle() noexcept; bool _HasTerminalEffects() const noexcept; std::string _LoadPixelShaderFile() const; HRESULT _SetupTerminalEffects(); void _ComputePixelShaderSettings() noexcept; [[nodiscard]] HRESULT _PrepareRenderTarget() noexcept; void _ReleaseDeviceResources() noexcept; bool _ShouldForceGrayscaleAA() noexcept; [[nodiscard]] HRESULT _CreateTextLayout( _In_reads_(StringLength) PCWCHAR String, _In_ size_t StringLength, _Out_ IDWriteTextLayout** ppTextLayout) noexcept; [[nodiscard]] HRESULT _CopyFrontToBack() noexcept; [[nodiscard]] HRESULT _EnableDisplayAccess(const bool outputEnabled) noexcept; [[nodiscard]] til::size _GetClientSize() const; void _InvalidateRectangle(const til::rectangle& rc); bool _IsAllInvalid() const noexcept; [[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept; // Routine Description: // - Helps convert a Direct2D ColorF into a DXGI RGBA // Arguments: // - color - Direct2D Color F // Return Value: // - DXGI RGBA [[nodiscard]] constexpr DXGI_RGBA s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept { return { color.r, color.g, color.b, color.a }; } }; }