Flickering when drawing to IDCompositionSurface
See the question and my original answer on StackOverflowIf you want to use Direct2D with Direct Composition, you want to use IDCompositionSurface::BeginDraw asking for a D2D device context. Note for that you must create the Direct Composition device using a D2D device.
This code should work better:
int main()
{
constexpr const wchar_t* WINDOW_CLASS_NAME = _T("a");
constexpr const wchar_t* WINDOW_TITLE_NAME = _T("a");
HRESULT hr = S_OK;
CComPtr<ID3D11Device> d3dDevice;
hr = D3D11CreateDevice(
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG,
nullptr, 0, D3D11_SDK_VERSION, &d3dDevice, nullptr, nullptr
);
CComPtr<IDXGIDevice> dxgiDevice;
hr = d3dDevice->QueryInterface(IID_PPV_ARGS(&dxgiDevice));
CComPtr<ID2D1Device> d2dDevice;
D2D1_CREATION_PROPERTIES creationProc = {};
creationProc.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
hr = D2D1CreateDevice(dxgiDevice, creationProc, &d2dDevice);
// *use D2D device to create DComp device*
CComPtr<IDCompositionDesktopDevice> dcDesktopDevice;
hr = DCompositionCreateDevice3(d2dDevice, IID_PPV_ARGS(&dcDesktopDevice));
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_NOCLOSE | CS_VREDRAW | CS_HREDRAW;
wndClass.lpfnWndProc = DefWindowProc;
wndClass.hInstance = GetModuleHandle(NULL);
wndClass.lpszClassName = WINDOW_CLASS_NAME;
hr = RegisterClassEx(&wndClass) ? S_OK : E_FAIL;
HWND hwnd;
hwnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW | WS_EX_TRANSPARENT | WS_EX_NOREDIRECTIONBITMAP,
WINDOW_CLASS_NAME, WINDOW_TITLE_NAME,
WS_POPUP | WS_VISIBLE,
1100, 980, 800, 80,
nullptr, nullptr, GetModuleHandle(nullptr), nullptr
); hr = hwnd ? S_OK : E_FAIL;
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
CComPtr<IDCompositionTarget> dcTarget;
hr = dcDesktopDevice->CreateTargetForHwnd(hwnd, FALSE, &dcTarget);
CComPtr<IDCompositionVisual2> dcVisual;
hr = dcDesktopDevice->CreateVisual(&dcVisual);
dcTarget->SetRoot(dcVisual);
CComPtr<IDCompositionSurface> surface;
hr = dcDesktopDevice->CreateSurface(
800, 80,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_ALPHA_MODE_PREMULTIPLIED,
&surface
);
hr = dcVisual->SetContent(surface);
while (true)
{
POINT drawOffset = { 0,0 };
CComPtr<ID2D1DeviceContext> d2dContext;
hr = surface->BeginDraw(nullptr, IID_PPV_ARGS(&d2dContext), &drawOffset);
d2dContext->Clear();
// you *MUST* use the drawoffset you get back
d2dContext->SetTransform(D2D1::Matrix3x2F::Translation((FLOAT)drawOffset.x, (FLOAT)drawOffset.y));
CComPtr<ID2D1SolidColorBrush> brush;
hr = d2dContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &brush);
d2dContext->FillRoundedRectangle(D2D1_ROUNDED_RECT{ D2D1::RectF(10,10,200,50), 10, 10 }, brush);
hr = surface->EndDraw();
hr = dcDesktopDevice->Commit();
hr = dcDesktopDevice->WaitForCommitCompletion(); // optional
Sleep(50); // this and the loop is optional see remarks
}
}
Note you don't need to call Sleep and do it again as with Direct Composition, you compose your scene usually once (unless you want to animate it of course), you don't need to refresh it again at each vblank.
Also, it's much better to use Windows Visual Layer wich is simply the evolution of Direct Composition. It's not at all restricted to UWP, but it does require using WinRT API. There's an official example here https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/HelloComposition and a less bloated one here https://gist.github.com/kennykerr/62923cdacaba28fedc4f3dab6e0c12ec
With the Visual Layer the idea is roughly the same you can use a SpriteVisual and from that SpriteVisual you can get a D2D context to write https://learn.microsoft.com/en-us/windows/uwp/composition/composition-native-interop