【发布时间】:2015-04-24 07:33:54
【问题描述】:
我正在将应用程序的绘图代码从 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.1 毫秒,而约 0.35 毫秒)。但是,出于某种原因,函数调用偶尔会停顿很长一段时间 - 加起来超过 200 毫秒。有问题的调用直接来自 Direct2D API - FillRectangle 和 DrawTextLayout。奇怪的是,每次我运行应用程序时,这些停顿出现在同一个位置 - 第 73 次出现循环,然后是第 218 次,然后是第 290 次等等(差异中有一些模式,在每 ~ 73 次和每~145个周期)。这与它绘制的数据无关(当我告诉它跳过绘制第 73 个周期时,下一个周期只是变成第 73 个并因此停止)。
我认为这可能是 GPU/CPU 通信问题,所以我将渲染目标(我使用的是 HWnd 目标)设置为软件模式(D2D1_RENDER_TARGET_TYPE_SOFTWARE),结果更加奇怪。停顿时间从约 200 毫秒下降到约 20 毫秒(仍然不是很好,但是嘿),但有两个实例停顿超过 2500 毫秒! (这两个,就像其他的摊位一样,就作为第 n 个 API 调用而言是完全可重现的。
这相当令人沮丧,因为 99% 的循环速度比旧实现快几倍,但(不到)1% 的剩余循环时间异常长。
致所有 Direct2D 专家 - 这种停滞可能是什么类型的问题的征兆?一般来说,什么可能导致我的代码与 D2D 在后台执行的操作之间出现这种脱节?
【问题讨论】:
-
您提供的代码示例不足。请分享整个渲染周期(
RenderTarget.BeginDraw()-RenderTarget.EndDraw()之间的代码。顺便说一下,RenderTarget.FillRectangle应该立即执行 - 它只是向 direct2d 渲染器添加一个命令。所有命令在您调用时立即执行RenderTarget.Flush()或RenderTarget.EndDraw(). -
@PeterKostov 我曾希望我的第一个问题不会是代码转储,但我确实看到它可能有点稀疏。问题已更新 - 感谢您的反馈。如果我还有什么遗漏,请告诉我!
-
“不幸的是”,您的代码看起来没问题。至少,我看不出您描述的问题的主要原因。无论如何,
FillRectangle没有理由造成任何停顿。你是如何衡量绩效的?尝试ID2D1RenderTarget::DrawText而不是DrawTextLayout(测试目的)。另外,打开调试层到D2D1_DEBUG_LEVEL_INFORMATION并嗅探一些错误或警告。 -
如果在重新创建渲染目标时只调用
m_pRT->Resize(size);会发生什么?在每幅画上都这样做似乎是多余的。 -
@PeterKostov 我正在测试性能,方法是用一个调试宏包围有问题的代码,该宏执行一些 QueryPerformanceCounter/Frequency 调用并记录结果。这看起来确实很奇怪,但 FillRectangle 确实在软件和默认模式下都停止了。使用 ::DrawText 没有任何帮助(如果我记得,它只是做了一个临时布局)。感谢您迄今为止的帮助!
标签: performance direct2d