绘制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
相关产品推荐
相关产品推荐

