【问题标题】:Direct2D renders in main window but not child windowDirect2D 在主窗口而不是子窗口中呈现
【发布时间】:2014-01-23 10:39:20
【问题描述】:

我正在学习如何使用 Direct2D 和 DirectWrite。我编写了一个示例应用程序,它使用这些库直接在主(顶级)窗口中呈现内容。它工作正常。

现在我正在尝试为另一个应用程序中的子窗口重写绘画代码。原始代码使用 GDI 并且工作正常。但是当我将它转换为使用 Direct2D 时,什么都没有出现(子窗口显示为黑色)。

我已将绘制代码简化为客户区的简单填充。

LRESULT PlotWnd::OnPaint(UINT, WPARAM, LPARAM, BOOL &) {
    ATL::CComPtr<ID2D1Factory> pD2DFactory;
    HRESULT hr = ::D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
    assert(SUCCEEDED(hr));
    ATL::CComPtr<ID2D1HwndRenderTarget> pRT;
    RECT rc;
    GetClientRect(&rc);
    D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
    hr = pD2DFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(/* D2D1_RENDER_TARGET_TYPE_SOFTWARE */),
        D2D1::HwndRenderTargetProperties(m_hWnd, size), &pRT);
    assert(SUCCEEDED(hr));

    pRT->BeginDraw();
    pRT->Clear(D2D1::ColorF(D2D1::ColorF::Beige));
    hr = pRT->EndDraw();
    assert(SUCCEEDED(hr));
    ValidateRect(nullptr);

    return 0;
}

所有的 Direct2D 调用都以S_OK 成功,所以我有点不知道在哪里寻找问题。在这一点上,我可以看到我的工作实验和这个程序之间的唯一显着区别是,在这个程序中,我只在一个子窗口中使用 Direct2D。

所以我的问题是:在子窗口中使用 Direct2D 时是否存在我遗漏的问题?有什么提示可以进一步调试吗?

更新:我删除了静态参数以避免混淆。我正在用每一种颜料完全设置和拆除 Direct2D。只有一个绘制调用,具有真实大小。

如果我切换到软件渲染(通过在渲染目标属性中使用D2D1_RENDER_TARGET_TYPE_SOFTWARE),问题就消失了——我得到了预期的米色填充。

这让我怀疑我的显卡驱动程序已经过时了。但是我现在更新了我的图形驱动程序,但它仍然以同样的方式失败。当软件渲染按预期工作时,还有什么可能导致硬件渲染失败?

更新 2:我仍在尝试重现这是一个小型、独立的示例。同时,我注意到简单地启动 Spy++有时会导致窗口实际呈现到屏幕上(一次)。

【问题讨论】:

  • 您是否覆盖了WM_ERASEBKGND 处理程序,这样它就不会一直让您的窗口空白?
  • @Roger Rowland:是的,我的 WM_ERASKBKGND 处理程序什么都不做。如果我让它填充任意颜色,那么我会看到该颜色而不是 Direct2D Clear 命令所期望的米色填充。
  • 好的,只是运行一些“显而易见”的东西——父窗口是否设置了WS_CLIPCHILDREN 样式?
  • 是的,两个祖先都有 WS_CLIPCHILDREN(用 Spy++ 确认)。
  • 您是否验证过OnPaint 首先以非零大小调用?只是认为您的 GetClientRect 可能被调用得太快(例如,如果您使用虚拟大小创建然后移动/调整子窗口大小)。

标签: debugging winapi direct2d


【解决方案1】:

好吧,对于初学者来说,您应该在应用程序启动时创建工厂,并使用窗口 (HWND) 创建渲染目标。这些不应该是“临时”对象,只要您的 OnPaint (WM_PAINT) 正在执行,它们就会存在。

【讨论】:

  • 我最初就是这样开始的,但是当它不起作用时,我将代码简化为这样,以便能够将它们全部呈现在一个地方。如果我按需创建这些对象应该不会有什么不同,特别是因为油漆处理程序只被调用一次。而且,事实上,在 WM_PAINT 处理程序中完成这一切在我构建的原始示例(我使用 Direct2D 绘制主窗口)中工作得很好。
  • 如果释放 HwndRenderTarget 会导致绘制问题,我不会感到惊讶。来自msdn.microsoft.com/en-us/library/windows/desktop/… 的引用:您的应用程序应该创建一次渲染目标并在应用程序的整个生命周期中保留它们,或者直到渲染目标的 EndDraw 方法返回 D2DERR_RECREATE_TARGET 错误。
  • @vt.我知道渲染目标应该持续窗口的生命周期,但这并没有改变行为。
【解决方案2】:

除了 Rick 建议将 HWNDRenderTarget 转换为永久对象之外,我建议调用 BeginPaint()-EndPaint()(或创建一个自动执行此操作的本地 CPaintDC 对象)即使您不使用 @987654329 @。见MainWindow::OnPainthere

如果这没有帮助,您可以随时启用Direct2D Debug Layer 以获取更多调试信息。

【讨论】:

  • 没有一个 MSDN Direct2D 示例使用 BeginPaint/EndPaint。 BeginPaint 获取窗口 DC,设置剪切区域,发送 WM_ERASEBKGND,并填写 PAINTSTUCT。 EndPaint 然后验证该区域。使用 Direct2D 时,只有验证很重要(我正在这样做)。不过,我尝试按照您的建议修改代码以使用 BeginPaint/EndPaint,但没有效果。
【解决方案3】:

除了使用软件渲染之外,我仍然没有其他解决方案。我相当肯定这是一个图形驱动程序错误,特别是竞争条件。

证据:

  • 大约十分之一,它可以很好地绘制,因此问题频繁但间歇性,提示存在时间问题。
  • 仅在启动应用程序后的第一次绘制时才会出现此问题。 (是的,此时窗口的大小是正确的。)以后重新绘制工作正常。幸运的是,我的应用程序每隔几分钟就会重新绘制一次窗口,因此在您启动它后它只会在几分钟内保持黑色。这表明应用程序启动存在时间问题。
  • 我无法使用小程序进行重现,因此问题可能对应用程序初始化所需的时间很敏感。也许驱动程序执行了一些异步初始化,但这些初始化并不总是及时完成以处理初始绘制命令。
  • 在我尝试过的其他使用不同显卡的计算机上没有发生这种情况。
  • 使用软件渲染时,绘画每次都完美无缺。这表明该错误不在我的代码中。

反证:

  • 过去一年的几轮驱动程序更新并未对行为产生任何影响。

我将此作为“答案”发布,因为其他一些人也看到了类似的症状,并问我是否找到了答案。我仍然愿意阅读其他想法,并会为好的答案投票。

【讨论】:

    猜你喜欢
    • 2014-02-09
    • 2019-07-14
    • 2013-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多