Skip to content

Instantly share code, notes, and snippets.

@dongle-the-gadget
Last active November 24, 2022 10:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dongle-the-gadget/b182ed36d9c30836a8c2281a980b6d4f to your computer and use it in GitHub Desktop.
Save dongle-the-gadget/b182ed36d9c30836a8c2281a980b6d4f to your computer and use it in GitHub Desktop.
WinUI 3 + Windows.UI.Composition
<Window
x:Class="WinUIThreeTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinUIThreeTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<SwapChainPanel Name="SwapChainPanel">
<Button Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="myButton" Click="myButton_Click">Click Me</Button>
</SwapChainPanel>
</Window>
// WinUI 3 + Windows.UI.Composition
// Thanks to:
// ysc3839 (GitHub): SetWindowCompositionAttribute implementation.
// ADeltaX for InteropComposition implementation (https://blog.adeltax.com/interopcompositor-and-coredispatcher/).
// Ahmed Walid for the first implementation of Windows.UI.Composition for WinUI 3.
// This is a reimplementation based on Ahmed's tutorial: https://twitter.com/AhmedWalid605/status/1513871287361409028.
// NOTE: This code is not ready for production use.
#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
typedef enum _WINDOWCOMPOSITIONATTRIB
{
WCA_ACCENT_POLICY = 19
} WINDOWCOMPOSITIONATTRIB;
typedef struct _WINDOWCOMPOSITIONATTRIBDATA
{
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
} WINDOWCOMPOSITIONATTRIBDATA;
typedef enum _ACCENT_STATE
{
ACCENT_ENABLE_HOSTBACKDROP = 5
} ACCENT_STATE;
typedef struct _ACCENT_POLICY
{
ACCENT_STATE AccentState;
DWORD AccentFlags;
DWORD GradientColor;
DWORD AnimationId;
} ACCENT_POLICY;
typedef BOOL(WINAPI* pfnGetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
typedef BOOL(WINAPI* pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
#include <comutil.h>
#include <dcomp.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_2.h>
#include <d2d1_2helper.h>
#include <winrt/Windows.UI.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Composition.h>
#include <windows.ui.composition.interop.h>
#include <microsoft.ui.xaml.window.h>
#include <winrt/microsoft.ui.interop.h>
#include <winrt/Microsoft.UI.Windowing.h>
#include <microsoft.ui.xaml.media.dxinterop.h>
#pragma comment(lib, "dxgi")
#pragma comment(lib, "d3d11")
#pragma comment(lib, "d2d1")
#pragma comment(lib, "dcomp")
using namespace winrt;
using namespace winrt::Microsoft::UI::Xaml;
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Core;
LRESULT CALLBACK WndProc(
HWND,
UINT,
WPARAM,
LPARAM
);
WNDPROC prevWndProc;
DECLARE_INTERFACE_IID_(IInternalCoreDispatcherStatic, IInspectable, "4B4D0861-D718-4F7C-BEC7-735C065F7C73")
{
STDMETHOD(GetForCurrentThread)(
CoreDispatcher * ppDispatcher
) PURE;
STDMETHOD(GetOrCreateForCurrentThread)(
CoreDispatcher * ppDispatcher
) PURE;
};
DECLARE_INTERFACE_IID_(HwndTarget, IUnknown, "6677DA68-C80C-407A-A4D2-3AA118AD7C46")
{
STDMETHOD(GetRoot)(THIS_
OUT ABI::Windows::UI::Composition::IVisual * *value) PURE;
STDMETHOD(SetRoot)(THIS_
IN ABI::Windows::UI::Composition::IVisual * value) PURE;
};
DECLARE_INTERFACE_IID_(InteropCompositionTarget, IUnknown, "EACDD04C-117E-4E17-88F4-D1B12B0E3D89")
{
STDMETHOD(SetRoot)(THIS_
IN IDCompositionVisual * visual) PURE;
};
DECLARE_INTERFACE_IID_(IInteropCompositorPartner, IUnknown, "e7894c70-af56-4f52-b382-4b3cd263dc6f")
{
STDMETHOD(MarkDirty)(THIS_) PURE;
STDMETHOD(ClearCallback)(THIS_) PURE;
STDMETHOD(CreateManipulationTransform)(THIS_
IN IDCompositionTransform * transform,
IN REFIID iid,
OUT VOID * *result) PURE;
STDMETHOD(RealClose)(THIS_) PURE;
};
DECLARE_INTERFACE_IID_(IInteropCompositorPartnerCallback, IUnknown, "9bb59fc9-3326-4c32-bf06-d6b415ac2bc5")
{
STDMETHOD(NotifyDirty)(THIS_) PURE;
STDMETHOD(NotifyDeferralState)(THIS_
bool deferRequested) PURE;
};
DECLARE_INTERFACE_IID_(IInteropCompositorFactoryPartner, IInspectable, "22118adf-23f1-4801-bcfa-66cbf48cc51b")
{
STDMETHOD(CreateInteropCompositor)(THIS_
IN IUnknown * renderingDevice,
IN IInteropCompositorPartnerCallback * callback,
IN REFIID iid,
OUT VOID * *instance
) PURE;
STDMETHOD(CheckEnabled)(THIS_
OUT bool* enableInteropCompositor,
OUT bool* enableExposeVisual
) PURE;
};
com_ptr<IDCompositionDesktopDevice> dcompDevice;
com_ptr<ID2D1Device> d2dDevice;
bool InitializeInteropCompositor(HWND hwnd, IUnknown* d2dDevice, Compositor* compositor, IUnknown** compositionTarget, ContainerVisual* rootVisual)
{
auto interopCompositorFactory = winrt::get_activation_factory<Compositor, IInteropCompositorFactoryPartner>();
com_ptr<IInteropCompositorPartner> interopCompositor;
auto interopRes = interopCompositorFactory->CreateInteropCompositor(d2dDevice, NULL, winrt::guid_of<IInteropCompositorPartner>(), interopCompositor.put_void());
if (interopRes != S_OK)
return false;
auto m_compositor = interopCompositor.as<Compositor>();
dcompDevice = interopCompositor.as<IDCompositionDesktopDevice>();
com_ptr<IDCompositionTarget> dcompTarget;
auto res = dcompDevice->CreateTargetForHwnd(hwnd, FALSE, dcompTarget.put());
if (res != S_OK)
return false;
auto containerVisual = m_compositor.CreateContainerVisual();
auto compTarget = dcompTarget.try_as<CompositionTarget>();
if (compTarget)
{
compTarget.Root(containerVisual);
*compositionTarget = compTarget.as<IUnknown>().detach();
}
else
{
winrt::com_ptr<ABI::Windows::UI::Composition::IVisual> visualAbi;
winrt::get_unknown(containerVisual)->QueryInterface(visualAbi.put());
auto hwndTarget = dcompTarget.as<HwndTarget>();
hwndTarget->SetRoot(visualAbi.get());
*compositionTarget = hwndTarget.as<IUnknown>().detach();
}
*compositor = m_compositor;
*rootVisual = containerVisual;
return true;
}
winrt::com_ptr< ::IDXGISwapChain1 > swapChain;
SpriteVisual spriteVisual{ nullptr };
namespace winrt::WinUIThreeTest::implementation
{
MainWindow::MainWindow()
{
InitializeComponent();
}
void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&)
{
HRESULT result;
com_ptr<ID3D11Device> direct3dDevice;
com_ptr<IDXGIDevice1> dxgiDevice;
com_ptr<ID2D1Factory2> d2dFactory2;
result = D3D11CreateDevice(0,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
NULL,
0,
D3D11_SDK_VERSION,
direct3dDevice.put(),
nullptr,
nullptr);
result = direct3dDevice->QueryInterface(dxgiDevice.put());
result = D2D1CreateFactory(
D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory2),
(void**)d2dFactory2.put());
d2dFactory2->CreateDevice(
dxgiDevice.get(),
d2dDevice.put());
result = DCompositionCreateDevice3(
dxgiDevice.get(),
__uuidof(dcompDevice),
(void**)dcompDevice.put());
com_ptr<ID2D1DeviceContext> m_d2dContext;
d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
m_d2dContext.put()
);
Windows::UI::Composition::Compositor compositor{ nullptr };
ContainerVisual root{ nullptr };
IUnknown* target{ nullptr };
auto windowNative{ this->try_as<::IWindowNative>() };
winrt::check_bool(windowNative);
HWND hWnd{ 0 };
windowNative->get_WindowHandle(&hWnd);
InitializeInteropCompositor(hWnd, d2dDevice.get(), &compositor, &target, &root);
// Here I'm just lazy so I used AppWindow, feel free to replace this code with GetWindowRect.
// Note that swap chain and composition uses logical pixels.
// Start of sizing code
auto windowId = Microsoft::UI::GetWindowIdFromWindow(hWnd);
auto appWindow = Microsoft::UI::Windowing::AppWindow::GetFromWindowId(windowId);
auto windowSize = appWindow.Size();
auto m_dpi = GetDpiForWindow(hWnd);
windowSize.Width /= m_dpi / 96;
windowSize.Height /= m_dpi / 96;
// End of sizing code
DXGI_SWAP_CHAIN_DESC1 swapChainDesc{ 0 };
swapChainDesc.Width = windowSize.Width;
swapChainDesc.Height = windowSize.Height;
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swapChainDesc.Flags = 0;
winrt::com_ptr< ::IDXGIAdapter > dxgiAdapter;
dxgiDevice->GetAdapter(dxgiAdapter.put());
winrt::com_ptr< ::IDXGIFactory2 > dxgiFactory;
dxgiFactory.capture(dxgiAdapter, &IDXGIAdapter::GetParent);
dxgiFactory->CreateSwapChainForComposition(
direct3dDevice.get(),
&swapChainDesc,
nullptr,
swapChain.put());
spriteVisual = compositor.CreateSpriteVisual();
Windows::Foundation::Numerics::float2 float2;
float2.x = (float)windowSize.Width;
float2.y = (float)windowSize.Height;
spriteVisual.Size(float2);
// Here we set the composition attribute to allow host backdrops.
// Remove the following lines of code if you don't need it.
// NOTE: If you remove the host backdrop code you must replace compositor.CreateHostBackdropBrush() with a composition brush of your choice.
// Start of host backdrop code.
HMODULE hUser = GetModuleHandle(L"user32.dll");
if (hUser)
{
pfnSetWindowCompositionAttribute setWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");
if (setWindowCompositionAttribute)
{
ACCENT_POLICY accent = { ACCENT_ENABLE_HOSTBACKDROP, 0, 0, 0 };
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
data.cbData = sizeof(accent);
setWindowCompositionAttribute(hWnd, &data);
}
}
// End of host backdrop code.
spriteVisual.Brush(compositor.CreateHostBackdropBrush());
root.Children().InsertAtTop(spriteVisual);
com_ptr<ID3D11Texture2D> backBuffer;
swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
D2D1_BITMAP_PROPERTIES1 bitmapProperties =
D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
m_dpi,
m_dpi
);
com_ptr<IDXGISurface> dxgiBackBuffer;
swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer));
com_ptr<ID2D1Bitmap1> m_d2dTargetBitmap;
m_d2dContext->CreateBitmapFromDxgiSurface(
dxgiBackBuffer.get(),
&bitmapProperties,
m_d2dTargetBitmap.put()
);
m_d2dContext->SetTarget(m_d2dTargetBitmap.get());
SwapChainPanel().as<ISwapChainPanelNative>()->SetSwapChain(swapChain.get());
#ifdef GWL_WNDPROC
prevWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WndProc);
#else
prevWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc);
#endif
myButton().Content(box_value(L"Clicked"));
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_SIZE)
{
auto width = LOWORD(lParam);
auto height = HIWORD(lParam);
DXGI_MODE_DESC modeDesc;
modeDesc.Width = width;
modeDesc.Height = height;
swapChain->ResizeTarget(&modeDesc);
Windows::Foundation::Numerics::float2 float2;
float2.x = (float)width;
float2.y = (float)height;
spriteVisual.Size(float2);
}
return CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
}
#pragma once
#include "MainWindow.g.h"
namespace winrt::WinUIThreeTest::implementation
{
struct MainWindow : MainWindowT<MainWindow>
{
MainWindow();
void myButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
};
}
namespace winrt::WinUIThreeTest::factory_implementation
{
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}
#pragma once
#include <windows.h>
#include <unknwn.h>
#include <restrictederrorinfo.h>
#include <hstring.h>
// Undefine GetCurrentTime macro to prevent
// conflict with Storyboard::GetCurrentTime
#undef GetCurrentTime
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Xaml.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/microsoft.UI.Xaml.Input.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Microsoft.UI.Xaml.Data.h>
#include <winrt/Microsoft.UI.Xaml.Interop.h>
#include <winrt/Microsoft.UI.Xaml.Markup.h>
#include <winrt/Microsoft.UI.Xaml.Media.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
#include <winrt/Microsoft.UI.Dispatching.h>
#include <wil/cppwinrt_helpers.h>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment