2014-10-02 107 views
3

我对使用多个StretchBltStretchDIBits调用的DDB绘图操作进行了计时。Windows StretchBlt API性能

而且我发现,完成的时间与目标窗口大小成比例地增加/减少。
900x600窗口大约需要5ms,但1920x1080需要55ms(源图像是1280x640)。

它似乎拉伸.. APIs不使用任何硬件加速功能。

源图像(其实这是暂时的绘图画布)与CreateDIBSection因为我需要得到的(拉伸和合并)的位图的绘制为每帧的像素数据来创建。

我们假设,Windows GDI是无望的。那么有希望的选择是什么?

我认为D3D和D2D采用WIC方法(写入WIC位图并用D2D绘制,然后从WIC位图读回像素数据)。
我计划用WIC方法尝试D2D,因为我很快就需要使用大量的文本绘图功能。

但似乎WIC是不是有前途:What is the most effective pixel format for WIC bitmap processing?

+0

是的,StretchBlt真的很慢。如果您想要更高的质量(例如使用HALFTONE选项),则无法实时渲染。无论如何,你的软件瞄准什么操作系统? – 2014-10-02 07:16:57

+0

@AntonAngelov目标操作系统是Windows 7+。目前我的笔记本电脑可以顺利地(通过GDI)做900x600 @ 20fps,我的目标是1920x1080 @ 30fps。如果我在下周后得到结果,我可能会问另一个问题:) – 9dan 2014-10-02 08:37:31

+0

好吧。但是,你能更准确地提出你的问题吗?(例如,一个问题)? – 2014-10-02 08:53:59

回答

5

我实现D2D + WIC今天常规。测试结果非常好。

在我之前的GDI StretchDIBits版本中,将1280x640的DDB拖入1920x1080窗口需要20〜60ms的时间。切换到Direct2D + WIC后,通常需要5ms以下,画面质量也会更好。

我使用ID2D1HwndRenderTarget和WicBitmapRenderTarget,因为我需要读取/写入原始像素数据。

HWndRenderTarget仅用于屏幕绘制(WM_PAINT)。
HWndRenderTarget的主要优点是目标窗口大小不会影响绘图性能。

WicBitmapRenderTarget用作临时绘图画布(作为GDI绘图中的内存DC)。我们可以使用WIC位图对象(如GDI DIBSection)创建WicBitmapRenderTarget。我们可以随时读取/写入此WIC位图的原始像素数据。而且速度非常快。对于附注,有些类似的D3D GetFrontBufferData调用非常缓慢。

实际像素I/O通过IWICBitmap和IWICBitmapLock接口完成。

写作:

IWICBitmapPtr m_wicRemote; 
... 
const uint8* image = ...; 
... 
WICRect rcLock = { 0, 0, width, height }; 
IWICBitmapLockPtr wicLock; 
hr = m_wicRemote->Lock(&rcLock, WICBitmapLockWrite, &wicLock); 
if (SUCCEEDED(hr)) 
{ 
    UINT cbBufferSize = 0; 
    BYTE *pv = NULL; 
    hr = wicLock->GetDataPointer(&cbBufferSize, &pv); 
    if (SUCCEEDED(hr)) 
    { 
     memcpy(pv, image, cbBufferSize); 
    } 
} 

m_wicRenderTarget->BeginDraw(); 
m_wicRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); 
ID2D1BitmapPtr d2dBitmap; 
hr = m_wicRenderTarget->CreateBitmapFromWicBitmap(m_wicRemote, &d2dBitmap.GetInterfacePtr()); 
if (SUCCEEDED(hr)) 
{ 
    float cw = (renderTargetSize.width/2); 
    float ch = renderTargetSize.height; 
    float x, y, w, h; 
    FitFrameToCenter(cw, ch, (float)width, (float)height, x, y, w, h); 
    m_wicRenderTarget->DrawBitmap(d2dBitmap, D2D1::RectF(x, y, x + w, y + h)); 
} 
m_wicRenderTarget->EndDraw(); 

阅读:

IWICBitmapPtr m_wicCanvas; 
IWICBitmapLockPtr m_wicLockedData; 
... 
UINT width, height; 
HRESULT hr = m_wicCanvas->GetSize(&width, &height); 
if (SUCCEEDED(hr)) 
{ 
    WICRect rcLock = { 0, 0, width, height }; 
    hr = m_wicCanvas->Lock(&rcLock, WICBitmapLockRead, &m_wicLockedData); 
    if (SUCCEEDED(hr)) 
    { 
     UINT cbBufferSize = 0; 
     BYTE *pv = NULL; 
     hr = m_wicLockedData->GetDataPointer(&cbBufferSize, &pv); 
     if (SUCCEEDED(hr)) 
     { 
      return pv; // return data pointer 
      // need to Release m_wicLockedData after reading is done 
     } 
    } 
} 

图:

ID2D1HwndRenderTargetPtr m_renderTarget; 
.... 

D2D1_SIZE_F renderTargetSize = m_renderTarget->GetSize(); 
m_renderTarget->BeginDraw(); 
m_renderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); 
m_renderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black)); 

ID2D1BitmapPtr d2dBitmap; 
hr = m_renderTarget->CreateBitmapFromWicBitmap(m_wicCanvas, &d2dBitmap.GetInterfacePtr()); 
if (SUCCEEDED(hr)) 
{ 
    UINT width, height; 
    hr = m_wicCanvas->GetSize(&width, &height); 
    if (SUCCEEDED(hr)) 
    { 
     float x, y, w, h; 
     FitFrameToCenter(renderTargetSize.width, renderTargetSize.height, (float)width, (float)height, x, y, w, h); 

     m_renderTarget->DrawBitmap(d2dBitmap, D2D1::RectF(x, y, x + w, y + h)); 
    } 
} 
m_renderTarget->EndDraw(); 

在我看来,GDI Stretch.. API是完全无用的在Windows 7+设置(性能敏感应用程序)。

另请注意,与Direct3D不同,基本图形操作(如文本绘制,图形绘制)在Direct2D中非常简单。

+0

我还是不明白你为什么需要使用WIC渲染目标。在调整大小之后还是需要进行CPU像素操作?顺便说一下,您是否尝试过DirectShow,Media Foundation等多媒体框架? – 2014-10-03 06:55:11

+0

我认为我的表现收益完全来自D2D渲染。 WIC只是GDI DIB部分API的替代品,而不是性能。我使用WIC是因为我需要用于屏幕录制的最终像素数据。 DirectShow需要复杂的“引脚”接口编码和自己的时序规则。对MF不太了解。 – 9dan 2014-10-03 08:42:29

+1

我明白了。好吧,这很适合你。 :) – 2014-10-03 08:55:24