2014-10-02 40 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