Windows StretchBlt API 性能

标签 windows direct3d direct2d wic stretchdibits

我为使用多个 StretchBltStretchDIBits 调用的 DDB 绘图操作计时。

而且我发现,完成时间与目标窗口大小成比例地增加/减少。
对于 900x600 窗口,它需要大约 5 毫秒,但对于 1920x1080,它需要长达 55 毫秒(源图像为 1280x640)。

似乎 Stretch.. API 不使用任何硬件加速功能。

源图像(实际上这是临时绘图 Canvas )是使用 CreateDIBSection 创建的,因为我需要为绘制的每一帧生成(拉伸(stretch)和合并的)位图像素数据。。 p>

让我们假设,Windows GDI 是没有希望的。那么有希望的替代方案是什么?

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

但似乎 WIC 并没有那么有前途:What is the most effective pixel format for WIC bitmap processing?

最佳答案

我今天实现了 D2D + WIC 例程。测试结果真的很好。

使用我之前的 GDI StretchDIBits 版本,将 1280x640 DDB 绘制到 1920x1080 窗口需要 20 ~ 60 毫秒的时间。切换到Direct2D + WIC后,通常需要不到5ms,而且画质看起来更好。

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

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

WicBitmapRenderTarget 用作临时绘图 Canvas (作为 GDI 绘图中的 Memory DC)。我们可以使用 WIC 位图对象(如 GDI DIBSection)创建 WicBitmapRenderTarget。我们可以随时从/向此 WIC 位图读取/写入原始像素数据。而且它非常快。附带说明一下,有点类似的 D3D GetFrontBufferData 调用真的很慢。

实际的像素 I/O 是通过 IWICBitmap 和 IWICBitmapLock 接口(interface)完成的。

写作:

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 中,基本图形操作(例如文本绘制、线条绘制)非常简单。

关于Windows StretchBlt API 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26153633/

相关文章:

c++ - 从 Visual Studio Express 迁移到 Netbeans。 #include <dwmapi.h> 不工作

c++ - 相当于 Direct2D 中的 GDI 笔

c# - 使用 Windows 身份验证从非域计算机访问 SQL Server 2005

c++ - wcslen 是 ISO/IEC 14882 :2003 C++ standard library? 的一部分吗

windows - 运行 git pull 所有子目录窗口命令提示符

windows - 多线程:事件驱动与消息驱动

c++ - D3D 设备创建后无法进行双重计算

.net - 寻找比 GDI 更快的渲染动态数据图的解决方案

c++ - Direct2D 加载和绘制位图

c++ - 为什么在D3D11下绘制D2D图?