[Screen Ruler] optimize d3d -> d2d texture copying + other fixes (#20138)

* [Screen Ruler] optimize d3d -> d2d texture copying + other fixes

* [Screen Ruler] hide cursor when using the bounds tool
This commit is contained in:
Andrey Nekrasov
2022-08-29 16:16:28 +03:00
committed by GitHub
parent a06a62b986
commit 66c8d38e59
10 changed files with 113 additions and 105 deletions

View File

@@ -9,6 +9,8 @@
#endif
#include <cassert>
#include <limits>
#include <d3d11.h>
//#define DEBUG_TEXTURE
@@ -125,3 +127,43 @@ struct BGRATextureView
void SaveAsBitmap(const char* filename) const;
#endif
};
class MappedTextureView
{
winrt::com_ptr<ID3D11DeviceContext> context;
winrt::com_ptr<ID3D11Texture2D> texture;
public:
BGRATextureView view;
MappedTextureView(winrt::com_ptr<ID3D11Texture2D> _texture,
winrt::com_ptr<ID3D11DeviceContext> _context,
const size_t textureWidth,
const size_t textureHeight) :
texture{ std::move(_texture) }, context{ std::move(_context) }
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
D3D11_MAPPED_SUBRESOURCE resource = {};
winrt::check_hresult(context->Map(texture.get(), D3D11CalcSubresource(0, 0, 0), D3D11_MAP_READ, 0, &resource));
view.pixels = static_cast<const uint32_t*>(resource.pData);
view.pitch = resource.RowPitch / 4;
view.width = textureWidth;
view.height = textureHeight;
}
MappedTextureView(MappedTextureView&&) = default;
MappedTextureView& operator=(MappedTextureView&&) = default;
inline winrt::com_ptr<ID3D11Texture2D> GetTexture() const
{
return texture;
}
~MappedTextureView()
{
if (context && texture)
context->Unmap(texture.get(), D3D11CalcSubresource(0, 0, 0));
}
};

View File

@@ -25,6 +25,8 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
break;
case WM_LBUTTONDOWN:
{
for (; ShowCursor(false) >= 0;)
;
auto toolState = GetWindowParam<BoundsToolState*>(window);
if (!toolState)
break;
@@ -36,6 +38,9 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
}
case WM_CURSOR_LEFT_MONITOR:
{
for (; ShowCursor(true) < 0;)
;
auto toolState = GetWindowParam<BoundsToolState*>(window);
if (!toolState)
break;
@@ -44,6 +49,9 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
}
case WM_LBUTTONUP:
{
for (; ShowCursor(true) < 0;)
;
auto toolState = GetWindowParam<BoundsToolState*>(window);
if (!toolState || !toolState->perScreen[window].currentRegionStart)
break;
@@ -67,6 +75,9 @@ LRESULT CALLBACK BoundsToolWndProc(HWND window, UINT message, WPARAM wparam, LPA
}
case WM_RBUTTONUP:
{
for (; ShowCursor(true) < 0;)
;
auto toolState = GetWindowParam<BoundsToolState*>(window);
if (!toolState)
break;

View File

@@ -36,33 +36,23 @@ namespace
}
winrt::com_ptr<ID2D1Bitmap> ConvertID3D11Texture2DToD2D1Bitmap(wil::com_ptr<ID2D1HwndRenderTarget> rt,
winrt::com_ptr<ID3D11Texture2D> texture)
const MappedTextureView* capturedScreenTexture)
{
std::lock_guard guard{ gpuAccessLock };
auto dxgiSurface = texture.try_as<IDXGISurface>();
if (!dxgiSurface)
return nullptr;
DXGI_MAPPED_RECT bitmap2Dmap = {};
HRESULT hr = dxgiSurface->Map(&bitmap2Dmap, DXGI_MAP_READ);
if (FAILED(hr))
{
return nullptr;
}
capturedScreenTexture->view.pixels;
D2D1_BITMAP_PROPERTIES props = { .pixelFormat = rt->GetPixelFormat() };
rt->GetDpi(&props.dpiX, &props.dpiY);
const auto sizeF = rt->GetSize();
winrt::com_ptr<ID2D1Bitmap> bitmap;
if (FAILED(rt->CreateBitmap(D2D1::SizeU(static_cast<uint32_t>(sizeF.width),
static_cast<uint32_t>(sizeF.height)),
bitmap2Dmap.pBits,
bitmap2Dmap.Pitch,
auto hr = rt->CreateBitmap(D2D1::SizeU(static_cast<uint32_t>(capturedScreenTexture->view.width),
static_cast<uint32_t>(capturedScreenTexture->view.height)),
capturedScreenTexture->view.pixels,
static_cast<uint32_t>(capturedScreenTexture->view.pitch * 4),
props,
bitmap.put())))
return nullptr;
if (FAILED(dxgiSurface->Unmap()))
bitmap.put());
if (FAILED(hr))
return nullptr;
return bitmap;
@@ -90,7 +80,7 @@ LRESULT CALLBACK MeasureToolWndProc(HWND window, UINT message, WPARAM wparam, LP
StoreWindowParam(window, state);
#if !defined(DEBUG_OVERLAY)
for (; ShowCursor(false) > 0;)
for (; ShowCursor(false) >= 0;)
;
#endif
break;
@@ -142,7 +132,7 @@ void DrawMeasureToolTick(const CommonState& commonState,
RECT measuredEdges{};
MeasureToolState::Mode mode = {};
winrt::com_ptr<ID2D1Bitmap> backgroundBitmap;
winrt::com_ptr<ID3D11Texture2D> backgroundTextureToConvert;
const MappedTextureView* backgroundTextureToConvert = nullptr;
toolState.Read([&](const MeasureToolState& state) {
continuousCapture = state.global.continuousCapture;

View File

@@ -39,7 +39,12 @@ HWND CreateOverlayUIWindow(const CommonState& commonState,
std::call_once(windowClassesCreatedFlag, CreateOverlayWindowClasses);
const auto screenArea = monitor.GetScreenSize(true);
HWND window{ CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
DWORD windowStyle = WS_EX_TOOLWINDOW;
#if !defined(DEBUG_OVERLAY)
windowStyle |= WS_EX_TOPMOST;
#endif
HWND window{
CreateWindowExW(windowStyle,
windowClass,
L"PowerToys.MeasureToolOverlay",
WS_POPUP,
@@ -50,7 +55,8 @@ HWND CreateOverlayUIWindow(const CommonState& commonState,
HWND_DESKTOP,
nullptr,
GetModuleHandleW(nullptr),
extraParam) };
extraParam)
};
winrt::check_bool(window);
ShowWindow(window, SW_SHOWNORMAL);
#if !defined(DEBUG_OVERLAY)

View File

@@ -6,9 +6,9 @@ PerGlyphOpacityTextRender::PerGlyphOpacityTextRender(
wil::com_ptr<ID2D1Factory> pD2DFactory,
wil::com_ptr<ID2D1HwndRenderTarget> rt,
wil::com_ptr<ID2D1SolidColorBrush> baseBrush) :
_pD2DFactory{ pD2DFactory },
_rt{ rt },
_baseBrush{ baseBrush }
_pD2DFactory{ pD2DFactory.get() },
_rt{ rt.get() },
_baseBrush{ baseBrush.get() }
{
}
@@ -18,20 +18,22 @@ HRESULT __stdcall PerGlyphOpacityTextRender::DrawGlyphRun(void* /*clientDrawingC
DWRITE_MEASURING_MODE measuringMode,
_In_ const DWRITE_GLYPH_RUN* glyphRun,
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/,
IUnknown* clientDrawingEffect) noexcept
IUnknown* clientDrawingEffect_) noexcept
{
HRESULT hr = S_OK;
if (!clientDrawingEffect)
if (!clientDrawingEffect_)
{
_rt->DrawGlyphRun(D2D1_POINT_2F{ .x = baselineOriginX, .y = baselineOriginY }, glyphRun, _baseBrush.get(), measuringMode);
_rt->DrawGlyphRun(D2D1_POINT_2F{ .x = baselineOriginX, .y = baselineOriginY }, glyphRun, _baseBrush, measuringMode);
return hr;
}
wil::com_ptr<IUnknown> clientDrawingEffect{ clientDrawingEffect_ };
// Create the path geometry.
wil::com_ptr<ID2D1PathGeometry> pathGeometry;
hr = _pD2DFactory->CreatePathGeometry(&pathGeometry);
// Write to the path geometry using the geometry sink.
ID2D1GeometrySink* pSink = nullptr;
wil::com_ptr<ID2D1GeometrySink> pSink;
if (SUCCEEDED(hr))
{
hr = pathGeometry->Open(&pSink);
@@ -49,11 +51,10 @@ HRESULT __stdcall PerGlyphOpacityTextRender::DrawGlyphRun(void* /*clientDrawingC
glyphRun->glyphCount,
glyphRun->isSideways,
glyphRun->bidiLevel % 2,
pSink);
pSink.get());
}
// Close the geometry sink
if (SUCCEEDED(hr))
if (pSink)
{
hr = pSink->Close();
}
@@ -70,21 +71,15 @@ HRESULT __stdcall PerGlyphOpacityTextRender::DrawGlyphRun(void* /*clientDrawingC
}
float prevOpacity = _baseBrush->GetOpacity();
OpacityEffect* opacityEffect = nullptr;
if (SUCCEEDED(hr))
{
hr = clientDrawingEffect->QueryInterface(__uuidof(IDrawingEffect), reinterpret_cast<void**>(&opacityEffect));
}
auto opacityEffect = clientDrawingEffect.try_query<IDrawingEffect>();
if (opacityEffect)
_baseBrush->SetOpacity(static_cast<OpacityEffect*>(opacityEffect.get())->alpha);
if (SUCCEEDED(hr))
{
_baseBrush->SetOpacity(opacityEffect->alpha);
}
if (SUCCEEDED(hr))
{
_rt->DrawGeometry(pTransformedGeometry.get(), _baseBrush.get());
_rt->FillGeometry(pTransformedGeometry.get(), _baseBrush.get());
_rt->DrawGeometry(pTransformedGeometry.get(), _baseBrush);
_rt->FillGeometry(pTransformedGeometry.get(), _baseBrush);
_baseBrush->SetOpacity(prevOpacity);
}

View File

@@ -16,9 +16,9 @@ struct OpacityEffect : winrt::implements<OpacityEffect, IDrawingEffect>
struct PerGlyphOpacityTextRender : winrt::implements<PerGlyphOpacityTextRender, IDWriteTextRenderer>
{
wil::com_ptr<ID2D1Factory> _pD2DFactory;
wil::com_ptr<ID2D1HwndRenderTarget> _rt;
wil::com_ptr<ID2D1SolidColorBrush> _baseBrush;
ID2D1Factory * _pD2DFactory = nullptr;
ID2D1HwndRenderTarget* _rt = nullptr;
ID2D1SolidColorBrush* _baseBrush = nullptr;
PerGlyphOpacityTextRender(
wil::com_ptr<ID2D1Factory> pD2DFactory,

View File

@@ -31,8 +31,8 @@ namespace winrt::PowerToys::MeasureToolCore::implementation
}
Core::Core() :
_mouseCaptureThread{ [this] { MouseCaptureThread(); } },
_stopMouseCaptureThreadSignal{ wil::EventOptions::ManualReset }
_stopMouseCaptureThreadSignal{ wil::EventOptions::ManualReset },
_mouseCaptureThread{ [this] { MouseCaptureThread(); } }
{
Trace::RegisterProvider();
LoggerHelpers::init_logger(L"Measure Tool", L"Core", "Measure Tool");

View File

@@ -21,9 +21,9 @@ namespace winrt::PowerToys::MeasureToolCore::implementation
float GetDPIScaleForWindow(uint64_t windowHandle);
void MouseCaptureThread();
wil::shared_event _stopMouseCaptureThreadSignal;
std::thread _mouseCaptureThread;
std::vector<std::thread> _screenCaptureThreads;
wil::shared_event _stopMouseCaptureThreadSignal;
std::vector<std::unique_ptr<OverlayUIState>> _overlayUIStates;
Serialized<MeasureToolState> _measureToolState;

View File

@@ -10,46 +10,6 @@
//#define DEBUG_EDGES
class MappedTextureView
{
winrt::com_ptr<ID3D11DeviceContext> context;
winrt::com_ptr<ID3D11Texture2D> texture;
public:
BGRATextureView view;
MappedTextureView(winrt::com_ptr<ID3D11Texture2D> _texture,
winrt::com_ptr<ID3D11DeviceContext> _context,
const size_t textureWidth,
const size_t textureHeight) :
texture{ std::move(_texture) }, context{ std::move(_context) }
{
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
D3D11_MAPPED_SUBRESOURCE resource = {};
winrt::check_hresult(context->Map(texture.get(), D3D11CalcSubresource(0, 0, 0), D3D11_MAP_READ, 0, &resource));
view.pixels = static_cast<const uint32_t*>(resource.pData);
view.pitch = resource.RowPitch / 4;
view.width = textureWidth;
view.height = textureHeight;
}
MappedTextureView(MappedTextureView&&) = default;
MappedTextureView& operator=(MappedTextureView&&) = default;
inline winrt::com_ptr<ID3D11Texture2D> GetTexture() const
{
return texture;
}
~MappedTextureView()
{
if (context && texture)
context->Unmap(texture.get(), D3D11CalcSubresource(0, 0, 0));
}
};
class D3DCaptureState final
{
winrt::com_ptr<ID3D11Device> d3dDevice;
@@ -171,8 +131,10 @@ void D3DCaptureState::OnFrameArrived(const winrt::Direct3D11CaptureFramePool& se
}
winrt::check_hresult(swapChain->GetBuffer(0, winrt::guid_of<ID3D11Texture2D>(), texture.put_void()));
auto gpuTexture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
auto surface = frame.Surface();
auto gpuTexture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(surface);
texture = CopyFrameToCPU(gpuTexture);
surface.Close();
MappedTextureView textureView{ texture, context, static_cast<size_t>(frameSize.Width), static_cast<size_t>(frameSize.Height) };
frameCallback(std::move(textureView));
@@ -228,6 +190,7 @@ std::unique_ptr<D3DCaptureState> D3DCaptureState::Create(winrt::GraphicsCaptureI
}
winrt::check_hresult(hr);
auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
winrt::com_ptr<IInspectable> d3dDeviceInspectable;
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(), d3dDeviceInspectable.put()));
@@ -413,7 +376,7 @@ std::thread StartCapturingThread(const CommonState& commonState,
const auto textureView = captureState->CaptureSingleFrame();
state.Access([&](MeasureToolState& s) {
s.perScreen[window].capturedScreenTexture = textureView.GetTexture();
s.perScreen[window].capturedScreenTexture = &textureView;
});
while (IsWindow(window) && !commonState.closeOnOtherMonitors)

View File

@@ -15,6 +15,7 @@
#include <common/utils/serialized.h>
//#define DEBUG_OVERLAY
#include "BGRATextureView.h"
struct OverlayBoxText
{
@@ -69,7 +70,7 @@ struct MeasureToolState
RECT measuredEdges = {};
// While not in a continuous capturing mode, we need to draw captured backgrounds. These are passed
// directly from a capturing thread.
winrt::com_ptr<ID3D11Texture2D> capturedScreenTexture;
const MappedTextureView* capturedScreenTexture = nullptr;
// After the drawing thread finds its capturedScreenTexture, it converts it to
// a Direct2D compatible bitmap and caches it here
winrt::com_ptr<ID2D1Bitmap> capturedScreenBitmap;