Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flickering when drawing to IDCompositionSurface

I tried drawing a rounded rectangle via the renderTarget, but encountered an issue where the image disappears every other frame—what could be the cause?

#include <d2d1_1.h>
#include <d2d1_2.h>
#include <d3d11.h>
#include <d3d11_2.h>
#include <dcomp.h>
#include <dxgi.h>
#include <dxgi1_3.h>
#include <atlbase.h>
#include <tchar.h>

#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dcomp.lib")

int main()
{
    constexpr const wchar_t* WINDOW_CLASS_NAME = _T("a");
    constexpr const wchar_t* WINDOW_TITLE_NAME = _T("a");

    HRESULT hr = S_OK;

    // Create DirectX Resources
    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.threadingMode = D2D1_THREADING_MODE_SINGLE_THREADED;
    creationProc.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
    creationProc.options = D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
    hr = D2D1CreateDevice(dxgiDevice, creationProc, &d2dDevice);

    CComPtr<IDCompositionDesktopDevice> dcDesktopDevice;
    hr = DCompositionCreateDevice3(dxgiDevice, IID_PPV_ARGS(&dcDesktopDevice));

    // Create Window
    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);


    // Draw
    CComPtr<ID2D1DeviceContext> d2dContext;
    hr = d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2dContext);

    CComPtr<ID2D1SolidColorBrush> brush;
    hr = d2dContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &brush);



    CComPtr<IDCompositionSurface> renderTarget;
    hr = dcDesktopDevice->CreateSurface(
        800, 80,
        DXGI_FORMAT_B8G8R8A8_UNORM,
        DXGI_ALPHA_MODE_PREMULTIPLIED,
        &renderTarget
    );
    hr = dcVisual->SetContent(renderTarget);


    while (true)
    {
        POINT drawOffset = { 0,0 };
        CComPtr<IDXGISurface> borderDxgiSurface;
        hr = renderTarget->BeginDraw(nullptr, IID_PPV_ARGS(&borderDxgiSurface), &drawOffset);

        CComPtr<ID2D1Bitmap1> borderDrawBitmap;
        const D2D1_BITMAP_PROPERTIES1 bitmapPRops = D2D1::BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)
        );
        hr = d2dContext->CreateBitmapFromDxgiSurface(borderDxgiSurface, bitmapPRops, &borderDrawBitmap);

        d2dContext->SetTarget(borderDrawBitmap);
        d2dContext->BeginDraw();
        d2dContext->Clear();
        d2dContext->FillRoundedRectangle(D2D1_ROUNDED_RECT{ D2D1::RectF(10,10,200,50), 10, 10 }, brush);

        hr = d2dContext->EndDraw();
        hr = renderTarget->EndDraw();
        hr =dcDesktopDevice->Commit();
        hr =dcDesktopDevice->WaitForCommitCompletion();

        Sleep(50);
    }
}
like image 511
feng lei Avatar asked Dec 17 '25 11:12

feng lei


1 Answers

If 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

like image 129
Simon Mourier Avatar answered Dec 19 '25 01:12

Simon Mourier



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!