WM_PAINT is a message that must not be sent (APIs like RedrawWindow to force a repaint)
I just did a test with DirectComposition, by drawing a simple circle in WM_SIZE :
#include <windows.h>
#include <tchar.h>
#include <d3d11.h>
#pragma comment (lib, "D3D11")
#include <d2d1.h>
#pragma comment (lib, "D2d1")
#include <d2d1_1.h>
#include <d2d1_3.h>
#include <dxgi1_2.h>
#include <dcomp.h>
#pragma comment(lib, "dcomp")
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int nWidth = 800, nHeight = 400;
#define IDC_STATIC 10
#define IDC_BUTTON 11
HRESULT CreateD2DBitmapFromURL(LPCWSTR wsURL, ID2D1Bitmap** pD2DBitmap);
HRESULT CreateD2DBitmapFromFile(LPCWSTR szFileName, ID2D1Bitmap** pD2DBitmap);
ID2D1Factory1* m_pD2DFactory1 = NULL;
ID3D11Device* m_pD3D11Device = NULL;
ID3D11DeviceContext* m_pD3D11DeviceContext = NULL;
IDXGIDevice1* m_pDXGIDevice = NULL;
ID2D1Device* m_pD2DDevice = NULL;
ID2D1DeviceContext3* m_pD2DDeviceContext3 = NULL;
IDXGISwapChain1* m_pDXGISwapChain1 = NULL;
ID2D1Bitmap1* m_pD2DTargetBitmap = NULL;
ID2D1SolidColorBrush* m_pD2DBrushBlack = NULL;
ID2D1SolidColorBrush* m_pD2DBrushWhite = NULL;
ID2D1SolidColorBrush* m_pD2DBrushBlue = NULL;
ID2D1SolidColorBrush* m_pD2DBrushGreen = NULL;
IDCompositionDevice* m_pDCompositionDevice = NULL;
IDCompositionTarget* m_pDCompositionTarget = NULL;
template <class T> void SafeRelease(T** ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
HRESULT CreateD2D1Factory();
HRESULT CreateD3D11Device();
HRESULT CreateDeviceResources();
HRESULT CreateSwapChain(HWND hWnd);
HRESULT ConfigureSwapChain(HWND hWnd);
HRESULT CreateDirectComposition(HWND hWnd);
void OnResize(HWND hWnd, UINT nWidth, UINT nHeight);
void Clean();
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
hInst = hInstance;
WNDCLASSEX wcex =
{
sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, TEXT("WindowClass"), NULL,
};
if (!RegisterClassEx(&wcex))
return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK);
int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
HWND hWnd = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP, wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
//HWND hWnd = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP, wcex.lpszClassName, TEXT("Test"), WS_POPUP, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
if (!hWnd)
return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK);
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
HRESULT Render()
{
HRESULT hr = S_OK;
if (m_pD2DDeviceContext3 && m_pDXGISwapChain1)
{
m_pD2DDeviceContext3->BeginDraw();
D2D1_SIZE_F size = m_pD2DDeviceContext3->GetSize();
m_pD2DDeviceContext3->Clear(D2D1::ColorF(D2D1::ColorF::Black, 1.0f));
auto nOldAntialiasMode = m_pD2DDeviceContext3->GetAntialiasMode();
m_pD2DDeviceContext3->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
m_pD2DDeviceContext3->FillEllipse(D2D1::Ellipse(D2D1::Point2F(size.width / 2, size.height / 2), 100.0f, 100.0f), m_pD2DBrushBlue);
m_pD2DDeviceContext3->SetAntialiasMode(nOldAntialiasMode);
HRESULT hr = m_pD2DDeviceContext3->EndDraw();
hr = m_pDXGISwapChain1->Present(1, 0);
}
return hr;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_CREATE:
{
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = CreateD2D1Factory();
if (SUCCEEDED(hr))
{
hr = CreateD3D11Device();
hr = CreateDeviceResources();
hr = CreateSwapChain(NULL);
if (SUCCEEDED(hr))
{
hr = ConfigureSwapChain(hWnd);
hr = CreateDirectComposition(hWnd);
}
}
}
return 0;
}
break;
case WM_SIZE:
{
UINT nWidth = LOWORD(lParam);
UINT nHeight = HIWORD(lParam);
OnResize(hWnd, nWidth, nHeight);
Render();
return 0;
}
break;
case WM_DESTROY:
{
Clean();
CoUninitialize();
PostQuitMessage(0);
return 0;
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
HRESULT CreateD2D1Factory()
{
HRESULT hr = S_OK;
D2D1_FACTORY_OPTIONS options;
ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
options.debugLevel = D2D1_DEBUG_LEVEL::D2D1_DEBUG_LEVEL_INFORMATION;
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory1), &options, (void**)&m_pD2DFactory1);
return hr;
}
HRESULT CreateSwapChain(HWND hWnd)
{
HRESULT hr = S_OK;
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
swapChainDesc.Width = 1;
swapChainDesc.Height = 1;
swapChainDesc.Format = DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1; // don't use multi-sampling
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // use double buffering to enable flip
swapChainDesc.Scaling = (hWnd != NULL) ? DXGI_SCALING::DXGI_SCALING_NONE : DXGI_SCALING::DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT::DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swapChainDesc.Flags = 0;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
IDXGIAdapter* pDXGIAdapter = nullptr;
hr = m_pDXGIDevice->GetAdapter(&pDXGIAdapter);
if (SUCCEEDED(hr))
{
IDXGIFactory2* pDXGIFactory2 = nullptr;
hr = pDXGIAdapter->GetParent(IID_PPV_ARGS(&pDXGIFactory2));
if (SUCCEEDED(hr))
{
if (hWnd != NULL)
{
hr = pDXGIFactory2->CreateSwapChainForHwnd(m_pD3D11Device, hWnd, &swapChainDesc, nullptr, nullptr, &m_pDXGISwapChain1);
}
else
{
hr = pDXGIFactory2->CreateSwapChainForComposition(m_pD3D11Device, &swapChainDesc, nullptr, &m_pDXGISwapChain1);
}
if (SUCCEEDED(hr))
hr = m_pDXGIDevice->SetMaximumFrameLatency(1);
SafeRelease(&pDXGIFactory2);
}
SafeRelease(&pDXGIAdapter);
}
return hr;
}
HRESULT ConfigureSwapChain(HWND hWnd)
{
HRESULT hr = S_OK;
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS::D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS::D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE::D2D1_ALPHA_MODE_PREMULTIPLIED),
0,
0,
NULL
);
// https://learn.microsoft.com/en-us/windows/win32/directwrite/how-to-ensure-that-your-application-displays-properly-on-high-dpi-displays
// For Windows 10
//unsigned int nDPI = GetDpiForWindow(hWnd);
// For Windows 8.1
FLOAT dpiX, dpiY;
HDC hDCScreen = GetDC(NULL);
dpiX = static_cast<FLOAT>(GetDeviceCaps(hDCScreen, LOGPIXELSX));
dpiY = static_cast<FLOAT>(GetDeviceCaps(hDCScreen, LOGPIXELSY));
ReleaseDC(NULL, hDCScreen);
bitmapProperties.dpiX = dpiX;
bitmapProperties.dpiY = dpiY;
IDXGISurface* pDXGISurface;
if (m_pDXGISwapChain1)
{
hr = m_pDXGISwapChain1->GetBuffer(0, IID_PPV_ARGS(&pDXGISurface));
if (SUCCEEDED(hr))
{
hr = m_pD2DDeviceContext3->CreateBitmapFromDxgiSurface(pDXGISurface, bitmapProperties, &m_pD2DTargetBitmap);
if (SUCCEEDED(hr))
{
m_pD2DDeviceContext3->SetTarget(m_pD2DTargetBitmap);
}
SafeRelease(&pDXGISurface);
}
}
return hr;
}
// https://docs.microsoft.com/en-us/windows/win32/direct2d/devices-and-device-contexts
HRESULT CreateD3D11Device()
{
HRESULT hr = S_OK;
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
// This array defines the set of DirectX hardware feature levels this app supports.
// The ordering is important and you should preserve it.
// Don't forget to declare your app's minimum required feature level in its
// description. All apps are assumed to support 9.1 unless otherwise stated.
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
D3D_FEATURE_LEVEL featureLevel;
hr = D3D11CreateDevice(
nullptr, // specify null to use the default adapter
D3D_DRIVER_TYPE_HARDWARE,
0,
creationFlags, // optionally set debug and Direct2D compatibility flags
featureLevels, // list of feature levels this app can support
ARRAYSIZE(featureLevels), // number of possible feature levels
D3D11_SDK_VERSION,
&m_pD3D11Device, // returns the Direct3D device created
&featureLevel, // returns feature level of device created
&m_pD3D11DeviceContext // returns the device immediate context
);
if (SUCCEEDED(hr))
{
// Obtain the underlying DXGI device of the Direct3D11 device.
hr = m_pD3D11Device->QueryInterface((IDXGIDevice1**)&m_pDXGIDevice);
if (SUCCEEDED(hr))
{
// Obtain the Direct2D device for 2-D rendering.
hr = m_pD2DFactory1->CreateDevice(m_pDXGIDevice, &m_pD2DDevice);
if (SUCCEEDED(hr))
{
// Get Direct2D device's corresponding device context object.
ID2D1DeviceContext* pD2DDeviceContext = NULL;
hr = m_pD2DDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &pD2DDeviceContext);
if (SUCCEEDED(hr))
hr = pD2DDeviceContext->QueryInterface((ID2D1DeviceContext3**)&m_pD2DDeviceContext3);
SafeRelease(&pD2DDeviceContext);
}
}
}
return hr;
}
HRESULT CreateDeviceResources()
{
HRESULT hr = S_OK;
if (m_pD2DDeviceContext3)
{
hr = m_pD2DDeviceContext3->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black, 0.5f), &m_pD2DBrushBlack);
hr = m_pD2DDeviceContext3->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White, 0.5f), &m_pD2DBrushWhite);
hr = m_pD2DDeviceContext3->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Blue, 1.0f), &m_pD2DBrushBlue);
hr = m_pD2DDeviceContext3->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Green, 1.0f), &m_pD2DBrushGreen);
}
return hr;
}
HRESULT CreateDirectComposition(HWND hWnd)
{
HRESULT hr = S_OK;
hr = DCompositionCreateDevice(m_pDXGIDevice, __uuidof(m_pDCompositionDevice), (void**)(&m_pDCompositionDevice));
if (SUCCEEDED(hr))
{
hr = m_pDCompositionDevice->CreateTargetForHwnd(hWnd, true, &m_pDCompositionTarget);
if (SUCCEEDED(hr))
{
IDCompositionVisual* pDCompositionVisual = NULL;
hr = m_pDCompositionDevice->CreateVisual(&pDCompositionVisual);
if (SUCCEEDED(hr))
{
hr = pDCompositionVisual->SetContent(m_pDXGISwapChain1);
hr = m_pDCompositionTarget->SetRoot(pDCompositionVisual);
hr = m_pDCompositionDevice->Commit();
SafeRelease(&pDCompositionVisual);
}
}
}
return hr;
}
void OnResize(HWND hWnd, UINT nWidth, UINT nHeight)
{
if (m_pDXGISwapChain1)
{
HRESULT hr = S_OK;
if (nWidth != 0 && nHeight != 0)
{
m_pD2DDeviceContext3->SetTarget(nullptr);
SafeRelease(&m_pD2DTargetBitmap);
hr = m_pDXGISwapChain1->ResizeBuffers(
2, // Double-buffered swap chain.
nWidth,
nHeight,
DXGI_FORMAT_B8G8R8A8_UNORM,
0
);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
CreateD3D11Device();
CreateSwapChain(NULL);
return;
}
else
{
//DX::ThrowIfFailed(hr);
}
ConfigureSwapChain(hWnd);
}
}
}
void CleanDeviceResources()
{
SafeRelease(&m_pD2DBrushBlack);
SafeRelease(&m_pD2DBrushWhite);
SafeRelease(&m_pD2DBrushBlue);
SafeRelease(&m_pD2DBrushGreen);
}
void Clean()
{
SafeRelease(&m_pD2DDevice);
SafeRelease(&m_pD2DDeviceContext3);
SafeRelease(&m_pD2DTargetBitmap);
CleanDeviceResources();
SafeRelease(&m_pDXGISwapChain1);
SafeRelease(&m_pDXGIDevice);
SafeRelease(&m_pD3D11Device);
SafeRelease(&m_pD3D11DeviceContext);
SafeRelease(&m_pD2DFactory1);
SafeRelease(&m_pDCompositionDevice);
SafeRelease(&m_pDCompositionTarget);
}