You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

绘制IDCompositionSurface时出现隔帧闪烁,求原因分析

问题原因及解决方案

你遇到的闪烁问题(图像每隔一帧消失)主要由以下几个核心原因导致:

1. 矛盾的Bitmap属性标志

代码中创建ID2D1Bitmap1时同时设置了D2D1_BITMAP_OPTIONS_TARGET和D2D1_BITMAP_OPTIONS_CANNOT_DRAW两个互斥的标志:

  • D2D1_BITMAP_OPTIONS_TARGET表示该Bitmap将作为渲染目标使用
  • D2D1_BITMAP_OPTIONS_CANNOT_DRAW则明确禁止将其作为渲染目标

这种矛盾会导致D2D上下文无法正确绑定到Bitmap进行绘制,使得部分帧的绘制操作失败,出现空白帧。

2. 未检查HRESULT返回值

所有API调用的返回结果(HRESULT)都没有进行错误检查。如果某一步操作失败(比如创建Bitmap失败),代码仍会继续执行后续绘制逻辑,直接导致无效的帧输出。

3. 重复创建Bitmap资源

在每帧循环中重复创建ID2D1Bitmap1不仅效率低下,还可能导致资源状态不一致。实际上,只要渲染目标Surface的尺寸和格式没有变化,Bitmap只需创建一次即可。


修复后的代码示例

#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")

// 辅助宏:检查HRESULT错误
#define CHECK_HR(hr) if(FAILED(hr)) { return hr; }

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

    HRESULT hr = S_OK;

    // 创建DirectX资源
    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
    );
    CHECK_HR(hr);

    CComPtr<IDXGIDevice> dxgiDevice;
    hr = d3dDevice->QueryInterface(IID_PPV_ARGS(&dxgiDevice));
    CHECK_HR(hr);

    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);
    CHECK_HR(hr);

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

    // 创建窗口
    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;
    if (!RegisterClassEx(&wndClass)) {
        return 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
    );
    if (!hwnd) {
        return E_FAIL;
    }

    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

    CComPtr<IDCompositionTarget> dcTarget;
    hr = dcDesktopDevice->CreateTargetForHwnd(hwnd, FALSE, &dcTarget);
    CHECK_HR(hr);

    CComPtr<IDCompositionVisual2> dcVisual;
    hr = dcDesktopDevice->CreateVisual(&dcVisual);
    CHECK_HR(hr);

    hr = dcTarget->SetRoot(dcVisual);
    CHECK_HR(hr);

    // 初始化绘制资源
    CComPtr<ID2D1DeviceContext> d2dContext;
    hr = d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2dContext);
    CHECK_HR(hr);

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

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

    hr = dcVisual->SetContent(renderTarget);
    CHECK_HR(hr);

    // 预创建Bitmap,避免每帧重复创建
    POINT drawOffset = { 0,0 };
    CComPtr<IDXGISurface> borderDxgiSurface;
    hr = renderTarget->BeginDraw(nullptr, IID_PPV_ARGS(&borderDxgiSurface), &drawOffset);
    CHECK_HR(hr);

    CComPtr<ID2D1Bitmap1> borderDrawBitmap;
    const D2D1_BITMAP_PROPERTIES1 bitmapPRops = D2D1::BitmapProperties1(
        D2D1_BITMAP_OPTIONS_TARGET,  // 仅保留TARGET标志
        D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)
    );
    hr = d2dContext->CreateBitmapFromDxgiSurface(borderDxgiSurface, bitmapPRops, &borderDrawBitmap);
    CHECK_HR(hr);

    hr = renderTarget->EndDraw();
    CHECK_HR(hr);

    while (true)
    {
        // 处理窗口消息,避免程序假死
        MSG msg;
        while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        hr = renderTarget->BeginDraw(nullptr, IID_PPV_ARGS(&borderDxgiSurface), &drawOffset);
        CHECK_HR(hr);

        d2dContext->SetTarget(borderDrawBitmap);
        d2dContext->BeginDraw();
        d2dContext->Clear(D2D1::ColorF(D2D1::ColorF::Transparent)); // 明确透明背景
        d2dContext->FillRoundedRectangle(D2D1_ROUNDED_RECT{ D2D1::RectF(10,10,200,50), 10, 10 }, brush);

        hr = d2dContext->EndDraw();
        CHECK_HR(hr);

        hr = renderTarget->EndDraw();
        CHECK_HR(hr);

        hr = dcDesktopDevice->Commit();
        CHECK_HR(hr);

        hr = dcDesktopDevice->WaitForCommitCompletion();
        CHECK_HR(hr);

        Sleep(50);
    }
}

额外说明

  • 新增CHECK_HR宏来检查API调用错误,便于快速定位问题
  • 在循环中添加窗口消息处理逻辑,避免程序失去响应
  • 明确设置Clear为透明背景,确保窗口透明区域正确显示

内容的提问来源于stack exchange,提问作者feng lei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 00:35:00