diff options
author | Bram Moolenaar <Bram@vim.org> | 2017-12-05 13:22:16 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2017-12-05 13:22:16 +0100 |
commit | 92467d3351853de769329f62121bf34d28647546 (patch) | |
tree | 14e67e16c6f61c36a1fc691ba1ee361dd34d54de /src/gui_dwrite.cpp | |
parent | ce6179c799468e471c3b7fc71c9924f57a2253c5 (diff) | |
download | vim-git-92467d3351853de769329f62121bf34d28647546.tar.gz |
patch 8.0.1369: MS-Windows: drawing underline slow, mFallbackDC not updatedv8.0.1369
Problem: MS-Windows: drawing underline, curl and strike-throw is slow,
mFallbackDC not properly updated.
Solution: Several performance improvements. (Ken Takata, Taro Muraoka,
Yasuhiro Matsumoto, closes #2401)
Diffstat (limited to 'src/gui_dwrite.cpp')
-rw-r--r-- | src/gui_dwrite.cpp | 287 |
1 files changed, 236 insertions, 51 deletions
diff --git a/src/gui_dwrite.cpp b/src/gui_dwrite.cpp index bd76383c5..e1d19db8d 100644 --- a/src/gui_dwrite.cpp +++ b/src/gui_dwrite.cpp @@ -263,14 +263,24 @@ private: } }; +enum DrawingMode { + DM_GDI = 0, + DM_DIRECTX = 1, + DM_INTEROP = 2, +}; + struct DWriteContext { HDC mHDC; + RECT mBindRect; + DrawingMode mDMode; + HDC mInteropHDC; bool mDrawing; bool mFallbackDC; ID2D1Factory *mD2D1Factory; ID2D1DCRenderTarget *mRT; + ID2D1GdiInteropRenderTarget *mGDIRT; ID2D1SolidColorBrush *mBrush; IDWriteFactory *mDWriteFactory; @@ -292,6 +302,10 @@ struct DWriteContext { virtual ~DWriteContext(); + HRESULT CreateDeviceResources(); + + void DiscardDeviceResources(); + HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, IDWriteTextFormat **ppTextFormat); @@ -299,17 +313,21 @@ struct DWriteContext { void SetFont(HFONT hFont); - void BindDC(HDC hdc, RECT *rect); + void BindDC(HDC hdc, const RECT *rect); - void AssureDrawing(); + HRESULT SetDrawingMode(DrawingMode mode); ID2D1Brush* SolidBrush(COLORREF color); - void DrawText(const WCHAR* text, int len, + void DrawText(const WCHAR *text, int len, int x, int y, int w, int h, int cellWidth, COLORREF color, - UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx); + UINT fuOptions, const RECT *lprc, const INT *lpDx); - void FillRect(RECT *rc, COLORREF color); + void FillRect(const RECT *rc, COLORREF color); + + void DrawLine(int x1, int y1, int x2, int y2, COLORREF color); + + void SetPixel(int x, int y, COLORREF color); void Flush(); @@ -561,10 +579,14 @@ private: DWriteContext::DWriteContext() : mHDC(NULL), + mBindRect(), + mDMode(DM_GDI), + mInteropHDC(NULL), mDrawing(false), mFallbackDC(false), mD2D1Factory(NULL), mRT(NULL), + mGDIRT(NULL), mBrush(NULL), mDWriteFactory(NULL), mDWriteFactory2(NULL), @@ -584,25 +606,7 @@ DWriteContext::DWriteContext() : _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory); if (SUCCEEDED(hr)) - { - D2D1_RENDER_TARGET_PROPERTIES props = { - D2D1_RENDER_TARGET_TYPE_DEFAULT, - { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE }, - 0, 0, - D2D1_RENDER_TARGET_USAGE_NONE, - D2D1_FEATURE_LEVEL_DEFAULT - }; - hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT); - _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT); - } - - if (SUCCEEDED(hr)) - { - hr = mRT->CreateSolidColorBrush( - D2D1::ColorF(D2D1::ColorF::Black), - &mBrush); - _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush); - } + hr = CreateDeviceResources(); if (SUCCEEDED(hr)) { @@ -645,11 +649,67 @@ DWriteContext::~DWriteContext() SafeRelease(&mDWriteFactory); SafeRelease(&mDWriteFactory2); SafeRelease(&mBrush); + SafeRelease(&mGDIRT); SafeRelease(&mRT); SafeRelease(&mD2D1Factory); } HRESULT +DWriteContext::CreateDeviceResources() +{ + HRESULT hr; + + if (mRT != NULL) + return S_OK; + + D2D1_RENDER_TARGET_PROPERTIES props = { + D2D1_RENDER_TARGET_TYPE_DEFAULT, + { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE }, + 0, 0, + D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, + D2D1_FEATURE_LEVEL_DEFAULT + }; + hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT); + _RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT); + + if (SUCCEEDED(hr)) + { + // This always succeeds. + mRT->QueryInterface( + __uuidof(ID2D1GdiInteropRenderTarget), + reinterpret_cast<void**>(&mGDIRT)); + _RPT1(_CRT_WARN, "GdiInteropRenderTarget: p=%p\n", mGDIRT); + } + + if (SUCCEEDED(hr)) + { + hr = mRT->CreateSolidColorBrush( + D2D1::ColorF(D2D1::ColorF::Black), + &mBrush); + _RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush); + } + + if (SUCCEEDED(hr)) + { + if (mHDC != NULL) + { + mRT->BindDC(mHDC, &mBindRect); + mRT->SetTransform(D2D1::IdentityMatrix()); + } + } + + return hr; +} + + void +DWriteContext::DiscardDeviceResources() +{ + SafeRelease(&mBrush); + SafeRelease(&mGDIRT); + SafeRelease(&mRT); +} + + HRESULT DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, IDWriteTextFormat **ppTextFormat) { @@ -817,27 +877,77 @@ DWriteContext::SetFont(HFONT hFont) item.pTextFormat = mTextFormat; item.fontWeight = mFontWeight; item.fontStyle = mFontStyle; + mFallbackDC = false; } + else + mFallbackDC = true; mFontCache.put(item); } void -DWriteContext::BindDC(HDC hdc, RECT *rect) +DWriteContext::BindDC(HDC hdc, const RECT *rect) { Flush(); mRT->BindDC(hdc, rect); mRT->SetTransform(D2D1::IdentityMatrix()); mHDC = hdc; + mBindRect = *rect; } - void -DWriteContext::AssureDrawing() + HRESULT +DWriteContext::SetDrawingMode(DrawingMode mode) { - if (mDrawing == false) + HRESULT hr = S_OK; + + switch (mode) { - mRT->BeginDraw(); - mDrawing = true; + default: + case DM_GDI: + if (mInteropHDC != NULL) + { + mGDIRT->ReleaseDC(NULL); + mInteropHDC = NULL; + } + if (mDrawing) + { + hr = mRT->EndDraw(); + if (hr == D2DERR_RECREATE_TARGET) + { + hr = S_OK; + DiscardDeviceResources(); + CreateDeviceResources(); + } + mDrawing = false; + } + break; + + case DM_DIRECTX: + if (mInteropHDC != NULL) + { + mGDIRT->ReleaseDC(NULL); + mInteropHDC = NULL; + } + else if (mDrawing == false) + { + CreateDeviceResources(); + mRT->BeginDraw(); + mDrawing = true; + } + break; + + case DM_INTEROP: + if (mDrawing == false) + { + CreateDeviceResources(); + mRT->BeginDraw(); + mDrawing = true; + } + if (mInteropHDC == NULL) + hr = mGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &mInteropHDC); + break; } + mDMode = mode; + return hr; } ID2D1Brush* @@ -849,22 +959,31 @@ DWriteContext::SolidBrush(COLORREF color) } void -DWriteContext::DrawText(const WCHAR* text, int len, +DWriteContext::DrawText(const WCHAR *text, int len, int x, int y, int w, int h, int cellWidth, COLORREF color, - UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx) + UINT fuOptions, const RECT *lprc, const INT *lpDx) { if (mFallbackDC) { - Flush(); - ExtTextOutW(mHDC, x, y, fuOptions, lprc, text, len, lpDx); + // Fall back to GDI rendering. + HRESULT hr = SetDrawingMode(DM_INTEROP); + if (SUCCEEDED(hr)) + { + HGDIOBJ hFont = ::GetCurrentObject(mHDC, OBJ_FONT); + HGDIOBJ hOldFont = ::SelectObject(mInteropHDC, hFont); + ::SetTextColor(mInteropHDC, color); + ::SetBkMode(mInteropHDC, ::GetBkMode(mHDC)); + ::ExtTextOutW(mInteropHDC, x, y, fuOptions, lprc, text, len, lpDx); + ::SelectObject(mInteropHDC, hOldFont); + } return; } - AssureDrawing(); - HRESULT hr; IDWriteTextLayout *textLayout = NULL; + SetDrawingMode(DM_DIRECTX); + hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat, FLOAT(w), FLOAT(h), &textLayout); @@ -883,26 +1002,77 @@ DWriteContext::DrawText(const WCHAR* text, int len, } void -DWriteContext::FillRect(RECT *rc, COLORREF color) +DWriteContext::FillRect(const RECT *rc, COLORREF color) { - AssureDrawing(); - mRT->FillRectangle( - D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top), - FLOAT(rc->right), FLOAT(rc->bottom)), - SolidBrush(color)); + if (mDMode == DM_INTEROP) + { + // GDI functions are used before this call. Keep using GDI. + // (Switching to Direct2D causes terrible slowdown.) + HBRUSH hbr = ::CreateSolidBrush(color); + ::FillRect(mInteropHDC, rc, hbr); + ::DeleteObject(HGDIOBJ(hbr)); + } + else + { + SetDrawingMode(DM_DIRECTX); + mRT->FillRectangle( + D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top), + FLOAT(rc->right), FLOAT(rc->bottom)), + SolidBrush(color)); + } } void -DWriteContext::Flush() +DWriteContext::DrawLine(int x1, int y1, int x2, int y2, COLORREF color) { - if (mDrawing) + if (mDMode == DM_INTEROP) { - mRT->EndDraw(); - mDrawing = false; + // GDI functions are used before this call. Keep using GDI. + // (Switching to Direct2D causes terrible slowdown.) + HPEN hpen = ::CreatePen(PS_SOLID, 1, color); + HGDIOBJ old_pen = ::SelectObject(mInteropHDC, HGDIOBJ(hpen)); + ::MoveToEx(mInteropHDC, x1, y1, NULL); + ::LineTo(mInteropHDC, x2, y2); + ::SelectObject(mInteropHDC, old_pen); + ::DeleteObject(HGDIOBJ(hpen)); + } + else + { + SetDrawingMode(DM_DIRECTX); + mRT->DrawLine( + D2D1::Point2F(FLOAT(x1), FLOAT(y1) + 0.5f), + D2D1::Point2F(FLOAT(x2), FLOAT(y2) + 0.5f), + SolidBrush(color)); + } +} + + void +DWriteContext::SetPixel(int x, int y, COLORREF color) +{ + if (mDMode == DM_INTEROP) + { + // GDI functions are used before this call. Keep using GDI. + // (Switching to Direct2D causes terrible slowdown.) + ::SetPixel(mInteropHDC, x, y, color); + } + else + { + SetDrawingMode(DM_DIRECTX); + // Direct2D doesn't have SetPixel API. Use DrawLine instead. + mRT->DrawLine( + D2D1::Point2F(FLOAT(x), FLOAT(y) + 0.5f), + D2D1::Point2F(FLOAT(x+1), FLOAT(y) + 0.5f), + SolidBrush(color)); } } void +DWriteContext::Flush() +{ + SetDrawingMode(DM_GDI); +} + + void DWriteContext::SetRenderingParams( const DWriteRenderingParams *params) { @@ -1000,7 +1170,7 @@ DWriteContext_Open(void) } void -DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect) +DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, const RECT *rect) { if (ctx != NULL) ctx->BindDC(hdc, rect); @@ -1016,7 +1186,7 @@ DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) void DWriteContext_DrawText( DWriteContext *ctx, - const WCHAR* text, + const WCHAR *text, int len, int x, int y, @@ -1025,8 +1195,8 @@ DWriteContext_DrawText( int cellWidth, COLORREF color, UINT fuOptions, - CONST RECT *lprc, - CONST INT * lpDx) + const RECT *lprc, + const INT *lpDx) { if (ctx != NULL) ctx->DrawText(text, len, x, y, w, h, cellWidth, color, @@ -1034,13 +1204,28 @@ DWriteContext_DrawText( } void -DWriteContext_FillRect(DWriteContext *ctx, RECT *rc, COLORREF color) +DWriteContext_FillRect(DWriteContext *ctx, const RECT *rc, COLORREF color) { if (ctx != NULL) ctx->FillRect(rc, color); } void +DWriteContext_DrawLine(DWriteContext *ctx, int x1, int y1, int x2, int y2, + COLORREF color) +{ + if (ctx != NULL) + ctx->DrawLine(x1, y1, x2, y2, color); +} + + void +DWriteContext_SetPixel(DWriteContext *ctx, int x, int y, COLORREF color) +{ + if (ctx != NULL) + ctx->SetPixel(x, y, color); +} + + void DWriteContext_Flush(DWriteContext *ctx) { if (ctx != NULL) |