前言:
闪烁问题,之前的经验是使用双缓冲,借此机会,把双缓冲的研究心得总结下。
双缓冲的含义:
缓冲这个词,相信大家都不陌生,Cache。主要是为了解决上下游(或者模块、或者系统)等性能不匹配问题。如果把上游看成“生产者”,下游看成“消费者”,当“生产者”与“消费者”的处理速度不同时,为了避免干等,中间会加一些缓冲区。
无缓冲,双方都容易阻塞。
最常用的生产者-消费者模型,增加弹性缓冲,缺点是每生产+每消费都需要加锁
双缓冲,等消费者队列为空时加锁,执行exchange(如swap方法),大大减少了加锁次数。
双缓冲在界面的使用:
GDI双缓冲:
WM_ERASEBKGND : 返回1,禁止背景重绘
HDC hDc = ::GetDC( m_hWnd) ; RECT rcDest; HDC hMemDc = CreateCompatibleDC(hDc); HBITMAP hMemBitmap = CreateCompatibleBitmap(hDc); //内存画布,放到内存DC中就可以在内存中画图了 HBITMAP hOldBitmap = ::SelectObject (hDC, hBitmap); //....hMemDc作画 ::BitBlt (m_hDestDC, rc.left, rc.top, rc.Width(), rc.Height(), hDC, rc.left, rc.top, SRCCOPY); //拷贝到显示器缓冲 //资源释放与还原 ::SelectObject (hDC, hOldBitmap); DeleteObject(hMemBitmap); DeleteDC(hMemDc); ReleaseDC(m_hWnd,hDc);
WTL双缓冲:
CDoubleBufferImpl 在AtlFrame.h中。
1.首先继承自CDoubleBufferImpl
class TCtrl: public CWindowImpl< TCtrl>, public WTL::CDoubleBufferImpl<TCtrl> // 继承双缓冲类
2.由于双缓冲类中已经处理了WM_ERASEBKGND 和WM_PAINT消息,所以需要从你的代码中删除对这些消息的处理。然后加上双缓冲的消息处理即可。
BEGIN_MSG_MAP(TCtrl) // MESSAGE_HANDLER(WM_PAINT, OnPaint) CHAIN_MSG_MAP( WTL::CDoubleBufferImpl<TCtrl>) END_MSG_MAP()
3.增加一个DoPaint函数,函数声明如下:
void DoPaint(CDCHandle dc);
4.将原来OnPaint函数中的代码移到DoPaint中,注意原来的CPaintDC需要改用参数中的CDCHandler
void TCtrl::DoPaint( CDCHandle dc ) { //CPaintDC dc(m_hWnd);
dc.MoveTo( xx… )
}
Duilib双缓冲:
Duilib使用的GDI+引擎,也已经支持双缓冲,这里简单介绍下它的实现过程
1、Duilib也默认处理了WM_REASEBKGND 并返回1;
2、IRenderContext 类负责渲染。
目前由RenderContext_GdiPlus来实现。
1)构造函数中,创建内存DC
2) 获取绘图区域后,创建内存Bitmap
然后作画,细节暂时略,后续源码剖析会补充,敬请期待。
最后,拷贝到显示器缓冲。