Skip to content

Commit

Permalink
visualize dirty regions
Browse files Browse the repository at this point in the history
  • Loading branch information
robmikh committed Jun 8, 2024
1 parent 79887c2 commit 0995fbb
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 3 deletions.
26 changes: 25 additions & 1 deletion Win32CaptureSample/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace winrt
{
using namespace Windows::Foundation;
using namespace Windows::Foundation::Metadata;
using namespace Windows::Graphics::Capture;
using namespace Windows::Graphics::DirectX;
using namespace Windows::Graphics::Imaging;
Expand Down Expand Up @@ -53,6 +54,12 @@ App::App(winrt::ContainerVisual root, winrt::GraphicsCapturePicker capturePicker
auto d3dDevice = util::CreateD3DDevice();
auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
m_device = CreateDirect3DDevice(dxgiDevice.get());

// Don't bother with a D2D device if we can't use dirty regions
if (winrt::ApiInformation::IsPropertyPresent(winrt::name_of<winrt::GraphicsCaptureSession>(), L"DirtyRegionMode"))
{
m_dirtyRegionVisualizer = std::make_shared<DirtyRegionVisualizer>(d3dDevice);
}
}

winrt::GraphicsCaptureItem App::TryStartCaptureFromWindowHandle(HWND hwnd)
Expand Down Expand Up @@ -196,7 +203,7 @@ winrt::IAsyncOperation<winrt::StorageFile> App::TakeSnapshotAsync()

void App::StartCaptureFromItem(winrt::GraphicsCaptureItem item)
{
m_capture = std::make_unique<SimpleCapture>(m_device, item, m_pixelFormat);
m_capture = std::make_unique<SimpleCapture>(m_device, m_dirtyRegionVisualizer, item, m_pixelFormat);

auto surface = m_capture->CreateSurface(m_compositor);
m_brush.Surface(surface);
Expand Down Expand Up @@ -277,4 +284,21 @@ void App::IncludeSecondaryWindows(bool value)
{
m_capture->IncludeSecondaryWindows(value);
}
}

bool App::VisualizeDirtyRegions()
{
if (m_capture != nullptr)
{
return m_capture->VisualizeDirtyRegions();
}
return false;
}

void App::VisualizeDirtyRegions(bool value)
{
if (m_capture != nullptr)
{
m_capture->VisualizeDirtyRegions(value);
}
}
5 changes: 5 additions & 0 deletions Win32CaptureSample/App.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include "SimpleCapture.h"
#include "DirtyRegionVisualizer.h"

class App
{
Expand All @@ -23,6 +24,9 @@ class App
bool IncludeSecondaryWindows();
void IncludeSecondaryWindows(bool value);

bool VisualizeDirtyRegions();
void VisualizeDirtyRegions(bool value);

void StopCapture();

private:
Expand All @@ -41,4 +45,5 @@ class App
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{ nullptr };
std::unique_ptr<SimpleCapture> m_capture{ nullptr };
winrt::Windows::Graphics::DirectX::DirectXPixelFormat m_pixelFormat = winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized;
std::shared_ptr<DirtyRegionVisualizer> m_dirtyRegionVisualizer;
};
71 changes: 71 additions & 0 deletions Win32CaptureSample/DirtyRegionVisualizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "pch.h"
#include "DirtyRegionVisualizer.h"

namespace winrt
{
using namespace Windows::Foundation;
using namespace Windows::Foundation::Numerics;
using namespace Windows::Graphics;
using namespace Windows::Graphics::Capture;
using namespace Windows::Graphics::DirectX;
using namespace Windows::Graphics::DirectX::Direct3D11;
using namespace Windows::System;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
}

namespace util
{
using namespace robmikh::common::uwp;
}

DirtyRegionVisualizer::DirtyRegionVisualizer(winrt::com_ptr<ID3D11Device> const& d3dDevice)
{
m_d2dFactory = util::CreateD2DFactory();
m_d2dDevice = util::CreateD2DDevice(m_d2dFactory, d3dDevice);

winrt::check_hresult(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, m_d2dContext.put()));

D2D1_COLOR_F color = { 1.0f, 0.0f, 0.0f, 0.6f };
winrt::check_hresult(m_d2dContext->CreateSolidColorBrush(color, m_brush.put()));
}

void DirtyRegionVisualizer::Render(
winrt::com_ptr<ID3D11Texture2D> const& renderTargetTexture,
winrt::Direct3D11CaptureFrame const& captureFrame)
{
auto dirtyRegion = captureFrame.DirtyRegions();
if (dirtyRegion.Size() > 0)
{
auto dxgiTexture = renderTargetTexture.as<IDXGISurface>();
winrt::com_ptr<ID2D1Bitmap1> d2dBitmap;
winrt::check_hresult(m_d2dContext->CreateBitmapFromDxgiSurface(dxgiTexture.get(), nullptr, d2dBitmap.put()));

m_d2dContext->SetTarget(d2dBitmap.get());
auto unsetTarget = wil::scope_exit([d2dContext = m_d2dContext]()
{
d2dContext->SetTarget(nullptr);
});

m_d2dContext->BeginDraw();
auto endDraw = wil::scope_exit([d2dContext = m_d2dContext]()
{
winrt::check_hresult(d2dContext->EndDraw());
});

for (auto&& dirtyRegion : dirtyRegion)
{
D2D1_RECT_F rect =
{
static_cast<float>(dirtyRegion.X),
static_cast<float>(dirtyRegion.Y),
static_cast<float>(dirtyRegion.X + dirtyRegion.Width),
static_cast<float>(dirtyRegion.Y + dirtyRegion.Height),
};

m_d2dContext->FillRectangle(
rect,
m_brush.get());
}
}
}
19 changes: 19 additions & 0 deletions Win32CaptureSample/DirtyRegionVisualizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

class DirtyRegionVisualizer
{
public:
DirtyRegionVisualizer(
winrt::com_ptr<ID3D11Device> const& d3dDevice);
~DirtyRegionVisualizer() {}

void Render(
winrt::com_ptr<ID3D11Texture2D> const& renderTargetTexture,
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame const& captureFrame);

private:
winrt::com_ptr<ID2D1Device> m_d2dDevice{ nullptr };
winrt::com_ptr<ID2D1Factory1> m_d2dFactory{ nullptr };
winrt::com_ptr<ID2D1DeviceContext> m_d2dContext{ nullptr };
winrt::com_ptr<ID2D1SolidColorBrush> m_brush{ nullptr };
};
17 changes: 17 additions & 0 deletions Win32CaptureSample/SampleWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ LRESULT SampleWindow::MessageHandler(UINT const message, WPARAM const wparam, LP
auto value = SendMessageW(m_secondaryWindowsCheckBox, BM_GETCHECK, 0, 0) == BST_CHECKED;
m_app->IncludeSecondaryWindows(value);
}
else if (hwnd == m_visualizeDirtyRegionCheckBox)
{
auto value = SendMessageW(m_visualizeDirtyRegionCheckBox, BM_GETCHECK, 0, 0) == BST_CHECKED;
m_app->VisualizeDirtyRegions(value);
}
}
break;
}
Expand Down Expand Up @@ -210,6 +215,7 @@ void SampleWindow::OnCaptureStarted(winrt::GraphicsCaptureItem const& item, Capt
}
SendMessageW(m_cursorCheckBox, BM_SETCHECK, BST_CHECKED, 0);
SendMessageW(m_borderRequiredCheckBox, BM_SETCHECK, BST_CHECKED, 0);
SendMessageW(m_visualizeDirtyRegionCheckBox, BM_SETCHECK, BST_UNCHECKED, 0);
EnableWindow(m_stopButton, true);
EnableWindow(m_snapshotButton, true);
}
Expand Down Expand Up @@ -251,6 +257,10 @@ void SampleWindow::CreateControls(HINSTANCE instance)
auto isBorderRequiredPresent = winrt::ApiInformation::IsPropertyPresent(winrt::name_of<winrt::GraphicsCaptureSession>(), L"IsBorderRequired");
auto borderEnableSytle = isBorderRequiredPresent ? 0 : WS_DISABLED;

// Dirty region mode
auto isDirtyRegionModePresent = winrt::ApiInformation::IsPropertyPresent(winrt::name_of<winrt::GraphicsCaptureSession>(), L"DirtyRegionMode");
auto dirtyRegionStyle = isDirtyRegionModePresent ? 0 : WS_DISABLED;

// Secondary windows configuration
m_isSecondaryWindowsFeaturePresent = winrt::ApiInformation::IsPropertyPresent(winrt::name_of<winrt::GraphicsCaptureSession>(), L"IncludeSecondaryWindows");

Expand Down Expand Up @@ -323,6 +333,12 @@ void SampleWindow::CreateControls(HINSTANCE instance)

// The default state is false for the secondary windows checkbox
SendMessageW(m_secondaryWindowsCheckBox, BM_SETCHECK, BST_UNCHECKED, 0);

// Border required checkbox
m_visualizeDirtyRegionCheckBox = controls.CreateControl(util::ControlType::CheckBox, L"Visualize dirty region", dirtyRegionStyle);

// The default state is false for dirty region checkbox
SendMessageW(m_visualizeDirtyRegionCheckBox, BM_SETCHECK, BST_UNCHECKED, 0);
}

void SampleWindow::SetSubTitle(std::wstring const& text)
Expand All @@ -344,6 +360,7 @@ void SampleWindow::StopCapture()
SendMessageW(m_cursorCheckBox, BM_SETCHECK, BST_CHECKED, 0);
SendMessageW(m_borderRequiredCheckBox, BM_SETCHECK, BST_CHECKED, 0);
SendMessageW(m_secondaryWindowsCheckBox, BM_SETCHECK, BST_UNCHECKED, 0);
SendMessageW(m_visualizeDirtyRegionCheckBox, BM_SETCHECK, BST_UNCHECKED, 0);
EnableWindow(m_stopButton, false);
EnableWindow(m_snapshotButton, false);
}
Expand Down
1 change: 1 addition & 0 deletions Win32CaptureSample/SampleWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct SampleWindow : robmikh::common::desktop::DesktopWindow<SampleWindow>
HWND m_captureExcludeCheckBox = nullptr;
HWND m_borderRequiredCheckBox = nullptr;
HWND m_secondaryWindowsCheckBox = nullptr;
HWND m_visualizeDirtyRegionCheckBox = nullptr;
std::unique_ptr<WindowList> m_windows;
std::unique_ptr<MonitorList> m_monitors;
std::vector<PixelFormatData> m_pixelFormats;
Expand Down
22 changes: 21 additions & 1 deletion Win32CaptureSample/SimpleCapture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@ namespace util
using namespace robmikh::common::uwp;
}

SimpleCapture::SimpleCapture(winrt::IDirect3DDevice const& device, winrt::GraphicsCaptureItem const& item, winrt::DirectXPixelFormat pixelFormat)
SimpleCapture::SimpleCapture(
winrt::IDirect3DDevice const& device,
std::shared_ptr<DirtyRegionVisualizer> const& dirtyRegionVisualizer,
winrt::GraphicsCaptureItem const& item,
winrt::DirectXPixelFormat pixelFormat)
{
m_item = item;
m_device = device;
m_pixelFormat = pixelFormat;
m_dirtyRegionVisualizer = dirtyRegionVisualizer;

auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
d3dDevice->GetImmediateContext(m_d3dContext.put());
Expand Down Expand Up @@ -54,6 +59,15 @@ winrt::ICompositionSurface SimpleCapture::CreateSurface(winrt::Compositor const&
return util::CreateCompositionSurfaceForSwapChain(compositor, m_swapChain.get());
}

void SimpleCapture::VisualizeDirtyRegions(bool value)
{
CheckClosed();
if (m_dirtyRegionVisualizer != nullptr) {
auto expected = !value;
m_visualizeDirtyRegions.compare_exchange_strong(expected, value);
}
}

void SimpleCapture::Close()
{
auto expected = false;
Expand Down Expand Up @@ -115,9 +129,15 @@ void SimpleCapture::OnFrameArrived(winrt::Direct3D11CaptureFramePool const& send

winrt::com_ptr<ID3D11Texture2D> backBuffer;
winrt::check_hresult(m_swapChain->GetBuffer(0, winrt::guid_of<ID3D11Texture2D>(), backBuffer.put_void()));

auto surfaceTexture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
// copy surfaceTexture to backBuffer
m_d3dContext->CopyResource(backBuffer.get(), surfaceTexture.get());

if (m_dirtyRegionVisualizer && m_visualizeDirtyRegions.load())
{
m_dirtyRegionVisualizer->Render(backBuffer, frame);
}
}

DXGI_PRESENT_PARAMETERS presentParameters{};
Expand Down
9 changes: 8 additions & 1 deletion Win32CaptureSample/SimpleCapture.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#pragma once
#include "DirtyRegionVisualizer.h"

class SimpleCapture
{
public:
SimpleCapture(
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice const& device,
std::shared_ptr<DirtyRegionVisualizer> const& dirtyRegionVisualizer,
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const& item,
winrt::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat);
~SimpleCapture() { Close(); }
Expand All @@ -28,6 +30,9 @@ class SimpleCapture
m_pixelFormatUpdate.exchange(newFormat);
}

bool VisualizeDirtyRegions() { CheckClosed(); return m_visualizeDirtyRegions.load(); }
void VisualizeDirtyRegions(bool value);

void Close();

private:
Expand Down Expand Up @@ -61,5 +66,7 @@ class SimpleCapture
std::atomic<std::optional<winrt::Windows::Graphics::DirectX::DirectXPixelFormat>> m_pixelFormatUpdate = std::nullopt;

std::atomic<bool> m_closed = false;
std::atomic<bool> m_captureNextImage = false;

std::shared_ptr<DirtyRegionVisualizer> m_dirtyRegionVisualizer;
std::atomic<bool> m_visualizeDirtyRegions = false;
};
2 changes: 2 additions & 0 deletions Win32CaptureSample/Win32CaptureSample.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
<ItemGroup>
<ClCompile Include="App.cpp" />
<ClCompile Include="CaptureSnapshot.cpp" />
<ClCompile Include="DirtyRegionVisualizer.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="MonitorList.cpp" />
<ClCompile Include="pch.cpp" />
Expand All @@ -152,6 +153,7 @@
<ItemGroup>
<ClInclude Include="App.h" />
<ClInclude Include="CaptureSnapshot.h" />
<ClInclude Include="DirtyRegionVisualizer.h" />
<ClInclude Include="dispatcherqueue.desktop.interop.h" />
<ClInclude Include="MonitorList.h" />
<ClInclude Include="pch.h" />
Expand Down
6 changes: 6 additions & 0 deletions Win32CaptureSample/Win32CaptureSample.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<ClCompile Include="CaptureSnapshot.cpp" />
<ClCompile Include="WindowList.cpp" />
<ClCompile Include="MonitorList.cpp" />
<ClCompile Include="DirtyRegionVisualizer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
Expand All @@ -18,8 +19,13 @@
<ClInclude Include="CaptureSnapshot.h" />
<ClInclude Include="WindowList.h" />
<ClInclude Include="MonitorList.h" />
<ClInclude Include="dispatcherqueue.desktop.interop.h" />
<ClInclude Include="DirtyRegionVisualizer.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>

0 comments on commit 0995fbb

Please sign in to comment.