【问题标题】:GDI Acceleration In Windows 7 / Drawing To Memory BitmapWindows 7 中的 GDI 加速/绘制到内存位图
【发布时间】:2012-06-06 02:46:14
【问题描述】:

我的 GDI 程序在 Windows XP 上运行良好,但在 Windows Vista 和 7 上,由于缺少 GDI 硬件加速,它看起来很糟糕。我记得几年前读过一篇文章,说 Windows 7 为一些 GDI 函数添加了硬件加速,包括 BitBlt() 函数。据推测,如果您绘制到内存位图,然后使用 BitBlt() 将图像复制到您的主窗口,它的运行速度与 XP 大致相同。这是真的吗?

如果是真的,你是怎么做到的?我在编程方面很糟糕,并且遇到了一些麻烦。我创建了下面的类来尝试让它工作:

class CMemBmpTest
{
private: 
    CDC         m_dcDeviceContext;
    CBitmap     m_bmpDrawSurface;

public:
    CMemBmpTest();
    ~CMemBmpTest();
    void Init();
    void Draw();
};

CMemBmpTest::CMemBmpTest()
{
}

CMemBmpTest::~CMemBmpTest()
{
    m_bmpDrawSurface.DeleteObject();
    m_dcDeviceContext.DeleteDC();
}

void CMemBmpTest::Init()
{  
    m_dcDeviceContext.CreateCompatibleDC(NULL);
    m_bmpDrawSurface.CreateCompatibleBitmap(&m_dcDeviceContext, 100, 100);
}

void CMemBmpTest::Draw()
{  
    m_dcDeviceContext.SelectObject(I.m_brshRedBrush);
    m_dcDeviceContext.PatBlt(0, 0, 100, 100, BLACKNESS);
}

在窗口的 OnPaint() 函数中我添加了一行:

pDC->BitBlt(2, 2, 100, 100, &m_MemBmp, 0, 0, SRCCOPY);

我希望在窗口的角落看到一个 100x100 的黑框,但它没有用。我可能做的一切都非常错误,所以如果有人能建议我如何正确地做到这一点,我将不胜感激。

感谢您提供的任何建议。

【问题讨论】:

  • 如果您使用 GDI,您不太可能从硬件加速中受益。至少在任何合理的范围内。更值得做的是检查您的代码并清理它以消除各种过度处理。您还可以尝试禁用 Aero 以可能回滚到“经典”GDI(而不是在 Direct3D 之上实现)。
  • 如果您只想做双缓冲(我不能完全理解您的问题,但我认为这就是您想要的),那么您最好使用 CMemDC,它是只需将 2 或 3 行代码添加到您的 OnPaint() 方法中,瞧,一切顺利。 (当然,如果您对每个像素使用 SetPixelV(),您的渲染仍然会很慢......)

标签: c++ mfc gdi


【解决方案1】:

AFAIK 您可以在所有版本的 Windows 上获得 GDI 功能的硬件加速(如果有人可以更详细地解释,我很乐意对此进行纠正)。但无论哪种方式,你都是正确的,相对于直接在屏幕上绘图,双缓冲(这就是你所说的)提供了巨大的性能提升(更重要的是没有闪烁)。

我已经做了很多这方面的工作,并提出了一种方法,允许您在绘图功能中同时使用 GDI 和 GDI+,但在绘图到屏幕时受益于 BitBlt 的硬件加速。 GDI+ 不是硬件加速的 AFAIK,但在许多更复杂的绘图技术中非常有用,因此可以选择。

所以,我的基本视图类将有以下成员:

Graphics *m_gr;
CDC *m_pMemDC;
CBitmap *m_pbmpMemBitmap;

那么类本身就会有类似这样的代码

    /*======================================================================================*/
    CBaseControlPanel::CBaseControlPanel()
    /*======================================================================================*/
    { 
        m_pMemDC = NULL;
        m_gr = NULL;
        m_pbmpMemBitmap = NULL;
    }

    /*======================================================================================*/
    CBaseControlPanel::~CBaseControlPanel()
    /*======================================================================================*/
    {
        // Clean up all the GDI and GDI+ objects we've used
        if(m_pMemDC)
        { delete m_pMemDC; m_pMemDC = NULL; }
        if(m_pbmpMemBitmap)
        { delete m_pbmpMemBitmap; m_pbmpMemBitmap = NULL; }
        if(m_gr)
        { delete m_gr; m_gr = NULL; }
    }   

/*======================================================================================*/
void CBaseControlPanel::OnPaint()
/*======================================================================================*/
{
    pDC->BitBlt(rcUpdate.left, rcUpdate.top, rcUpdate.Width(), rcUpdate.Height(),
                        m_pMemDC, rcUpdate.left, rcUpdate.top, SRCCOPY);
}

/*======================================================================================*/
void CBaseControlPanel::vCreateScreenBuffer(const CSize szPanel, CDC *pDesktopDC)
// In : 
//      szPanel = The size that we want the double buffer bitmap to be
// Out : None
/*======================================================================================*/
{
    // Delete anything we're already using first
    if(m_pMemDC)
    {
        delete m_gr;
        m_gr = NULL;
        delete m_pMemDC;
        m_pMemDC = NULL;
        delete m_pbmpMemBitmap;
        m_pbmpMemBitmap = NULL;
    }
    // Make a compatible DC
    m_pMemDC = new CDC;
    m_pMemDC->CreateCompatibleDC(pDesktopDC);           
    // Create a new bitmap
    m_pbmpMemBitmap = new CBitmap;
    // Create the new bitmap
    m_pbmpMemBitmap->CreateCompatibleBitmap(pDesktopDC, szPanel.cx, szPanel.cy);
    m_pbmpMemBitmap->SetBitmapDimension(szPanel.cx, szPanel.cy);
    // Select the new bitmap into the memory DC
    m_pMemDC->SelectObject(m_pbmpMemBitmap);
    // Then create a GDI+ Graphics object
    m_gr = Graphics::FromHDC(m_pMemDC->m_hDC);
    // And update the bitmap
    rcUpdateBitmap(rcNewSize, true);
}

/*======================================================================================*/
CRect CBaseControlPanel::rcUpdateBitmap(const CRect &rcInvalid, const bool bInvalidate, const bool bDrawBackground /*=true*/)
// Redraws an area of the double buffered bitmap
// In : 
//      rcInvalid - The rect to redraw
//      bInvalidate - Whether to refresh to the screen when we're done
//      bDrawBackground - Whether to draw the background first (can give speed benefits if we don't need to)
// Out : None
/*======================================================================================*/
{
   // The memory bitmap is actually updated here

   // Then make the screen update
   if(bInvalidate)
   { InvalidateRect(rcInvalid); }
}

因此,您可以直接绘制到内存 DC 并调用 InvalidateRect() 或将所有绘图代码放在 rcUpdateBitmap() 中,这对于我使用它的方式更方便。您需要在 OnSize() 中调用 vCreateScreenBuffer()。

希望无论如何都能给你一些想法。双缓冲绝对是追求速度和不闪烁 UI 的方法。开始可能需要一些努力,但绝对值得。

【讨论】:

  • 非常感谢您的详细回答。我会试着在周末让它工作。至于 GDI 加速,根据 Windows Vista 下的 Wikipedia 图形设备界面条目“GDI 不再由显卡驱动程序硬件加速”,而在 Windows 7 中“Windows 7 包括用于 blitting 操作的 GDI 硬件加速”。与 XP 相比,在 Vista 和 7 下肯定存在巨大的性能差异。希望一旦我将其更改为双缓冲,它会运行得更快,并且我最终能够切换到 Windows 7。再次感谢您的回答。
  • GDI 在 Vista 上没有加速,它在 WDDM 1.1 的 7 中再次添加回来(并非所有内容都被加速,只有一小部分功能,请参阅 msdn.microsoft.com/en-us/library/windows/hardware/…
  • 好的,感谢您提供有关硬件加速的信息,我不知道这一点。关于绘图速度的额外建议 - GDI+ 在许多方面比 GDI 慢很多,尤其是位图副本。这就是为什么我同时保留指向 GDI 和 GDI+ 设备上下文的指针,所以我只在无法在 GDI 中找到我想要的方法时才使用 GDI+。
【解决方案2】:
猜你喜欢
  • 2014-08-01
  • 1970-01-01
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 2012-03-31
  • 2013-02-03
  • 2010-09-16
相关资源
最近更新 更多