我正在努力将应用程序的绘图代码从GDI / GDI +迁移到Direct2D。到目前为止,事情进展顺利 - 然而,在测试新代码时,我注意到了一些奇怪的表现。我一直在调查的执行流程如下(我已尽力删除不相关的代码):
创建D2D工厂(创建应用时)
HRESULT hr = S_OK;
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &m_pD2DFactory);
if (hr == S_FALSE) {
ASSERT(FALSE);
throw Exception(CExtString(_T("Failed to create Direct2D factory")));
}
OnDraw回拨
HWND hwnd = GetSafeHwnd();
RECT rc;
GetClientRect(&rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
// Create a render target if it has been destroyed
if (!m_pRT) {
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT);
GetD2DFactory()->CreateHwndRenderTarget(props,
D2D1::HwndRenderTargetProperties(hwnd, size),
&m_pRT);
}
m_pRT->Resize(size);
m_pRT->BeginDraw();
// Begin drawing the layers, given the
// transformation matrix and some geometric information
Draw(m_pRT, matrixD2D, rectClipWorld, rectClipDP);
HRESULT hr = m_pRT->EndDraw();
if (hr == D2DERR_RECREATE_TARGET) {
SafeRelease(m_pRT);
}
Draw方法的内容
draw方法会产生很多与此测试无关的绒毛(因为我已经关闭了所有无关的层),但它最终绘制了一个执行此方法数千次的层:
void DrawStringWithEffects(ID2D1RenderTarget* m_pRT, const CString& text, const D2D1_POINT_2F& point, const COLORREF rgbFore, const COLORREF rgbBack, IDWriteTextFormat* pfont) {
// The text will be vertically centered around point.y, with point.x on the left hand side
// Create a TextLayout for the string
IDWriteTextLayout* textLayout = NULL;
GetDWriteFactory()->CreateTextLayout(text,
text.GetLength(),
pfont,
std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity(),
&textLayout);
DWRITE_TEXT_METRICS metrics = {0};
textLayout->GetMetrics(&metrics);
D2D1_RECT_F rect = D2D1::RectF(point.x, point.y - metrics.height/2, point.x + metrics.width, point.y + metrics.height/2);
D2D1_POINT_2F pointDraw = point;
pointDraw.y -= metrics.height/2;
ID2D1SolidColorBrush* brush = NULL;
m_pRT->CreateSolidColorBrush(ColorD2DFromCOLORREF(rgbBack), &brush);
m_pRT->FillRectangle(rect, brush);
// ^^ this is sometimes very slow!
brush->SetColor(ColorD2DFromCOLORREF(rgbFore));
m_pRT->DrawTextLayout(pointDraw, textLayout, brush, D2D1_DRAW_TEXT_OPTIONS_NONE);
// ^^ this is also sometimes very slow!
SafeRelease(&brush);
SafeRelease(&textLayout);
绝大多数情况下,Direct2D调用的执行速度比GDI +等价快〜3-4倍,这很好(一般为0.1ms,相比之下为~0.35ms)。但是,出于某种原因,函数调用偶尔会停滞很长一段时间 - 总计超过200毫秒。违规调用直接来自Direct2D API - FillRectangle和DrawTextLayout。奇怪的是,每次运行应用程序时,这些停顿都出现在同一个位置 - 第73次出现循环,然后是第218次,然后是第290次,依此类推(差异中有一些模式,在每次~73次之间交替出现)每~145周期)。这与它绘制的数据无关(当我告诉它跳过绘制第73个循环时,下一个循环只是变成了第73个并因此停止)。
我认为这可能是GPU / CPU通信问题,因此我将渲染目标(我使用的是HWnd目标)设置为软件模式(D2D1_RENDER_TARGET_TYPE_SOFTWARE),结果更加奇怪。失速时间从〜200ms下降到~20ms(仍然不是很好,但是嘿),但有两个实例停滞超过2500ms! (这两个与其他摊位一样,在第n个API调用方面完全可以重现。)
这是相当令人沮丧的,因为99%的循环比旧的实现快几倍,但是(少于)1%的循环在异常长时间内保持挂起。
对于那里的任何Direct2D专家 - 这种停滞的问题可能是什么类型的问题?通常情况下,我的代码与D2D在后台进行的操作之间可能会出现这种情况?
答案 0 :(得分:0)
Direct2D缓冲绘图命令(可能是为了优化它们)。您无法查看单个绘图命令的效果,您必须查看BeginDraw()
和EndDraw()
之间的总时间。如果要强制立即执行每个绘图命令,则必须通过调用Flush()
来跟踪每个绘图命令。但这对于表现来说可能是一个坏主意。
https://msdn.microsoft.com/en-us/library/windows/desktop/dd371768(v=vs.85).aspx
调用BeginDraw后,渲染目标通常会构建一个 批处理渲染命令,但推迟处理这些命令 直到内部缓冲区已满,调用Flush方法, 或直到调用EndDraw。