我正在学习如何使用 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++ 会有时导致窗口实际渲染到屏幕上(一次)。
对于初学者来说,您应该在应用程序启动时创建工厂,并使用窗口(HWND)创建渲染目标。这些不应该是仅在 OnPaint (WM_PAINT) 执行期间存在的“临时”对象。
除了 Rick 建议将 HWNDRenderTarget 转换为永久对象之外,我建议调用
BeginPaint()
-EndPaint()
(或创建一个自动执行此操作的本地 CPaintDC
对象),即使您不这样做使用 PAINTSTRUCT
。请参阅MainWindow::OnPaint
此处。
如果这没有帮助,您可以随时启用 Direct2D 调试层 以获取更多调试信息。
除了使用软件渲染我还是没有办法。我相当确定这是一个图形驱动程序错误,特别是竞争条件。
证据:
反证:
我将其发布为“答案”,因为其他一些人也看到了类似的症状并问我是否找到了答案。我仍然愿意阅读其他想法,并会投票赞成好的答案。
我建议使用相同的工厂和像素格式为您的子窗口或其他[共享]资源创建渲染目标。
ID2D1HwndRenderTarget *hwnd_tgt_main = nullptr;
ID2D1HwndRenderTarget *hwnd_tgt_child = nullptr;
HWND app_hwnd = nullptr;
HWND child_hwnd = nullptr;
D2D1_RENDER_TARGET_PROPERTIES my_goto_render_props = D2D1::RenderTargetProperty();
// render targets and bitmaps and other resources inherit from ID2D1Resource
static bool Create_HWND_Render_Tgt
(
HWND arg_hwnd,
ID2D1Resource *arg_resource,
ID2D1HwndRenderTarget **pp_hwnd_tgt,
ID2D1_SIZE_U *arg_size_overide = nullptr,
D2D1_PRESENT_OPTIONS_NONE arg_pres_opt = D2D1_PRESENT_OPTIONS_NONE,
D2D1_RENDER_TARGET_PROPERTIES *arg_render_props = &my_goto_render_props
)
{
bool func_msg_debug = true;
bool func_msg_success = false;
HRESULT hr = S_OK;
// scoped to this function
CComPtr<ID2D1Factory> ccptr_d2d_factory = nullptr; // = nullptr is not necessary
if (arg_resource)
{
hr = arg_resource->GetFactory(&ccptr_d2d_factory);
if (func_msg_debug) { /* using the same d2d factory...*/ }
}
else
{
hr = D2D1CreateFactory(&ccptr_d2d_factory);
if (func_msg_debug) { /* creating a new d2d factory... */ }
}
if (FAILED(hr))
{
if (fung_msg_bedug) { /* hr error message */ }
return false;
}
// ommit this line if pp_hwnd_tgt is an external com ptr. releasing com ptrs throws errors
SafeRelease(pp_hwnd_tgt);
// Resolve the hwnd render target properties here
D2D1_SIZE_U local_size = { 512, 512 }; // 512x512x4 = 1MB
D2D1_HWND_RENDER_TARGET_PROPERTIES local_hwnd_props;
D2D1_RENDER_TARGET_PROPERTIES local_render_props = D2D1::RenderTargetPropeties();
local_hwnd_props.hwnd = arg_hwnd;
local_hwnd_props.pixelSize = local_size;
loacl_hwnd_props.presentOptions = arg_pres_opt;
if (arg_size_overide) { local_hwnd_props.pixelSize = *arg_size_overide; }
if (arg_props) { local_render_props = *arg_props; }
hr = ccptr_d2d_factory->CreateHwndRenderTarget
(
&local_hwnd_props,
&local_render_props,
pp_hwnd_tgt
);
if (FAILED(hr))
{
if (fung_msg_bedug) { /* hr error message */ }
return false;
}
if (func_msg_success) { /* SUCCESS message */ }
return true;
} // all CComPtrs defined within this function go out of scope and are automatically released
void Init(HWND arg_hwnd)
{
if (!Create_HWND_Render_Tgt(arg_hwnd, nullptr, &hwnd_tgt_main))
{
/* error message*/
// end the app
}
app_hwnd = arg_hwnd;
// Create the child hwnd
// use the same factory from the main window's render target to create the new render tgt
if (!Create_HWND_Render_Tgt(child_hwnd, hwnd_tgt_main, &hwnd_tgt_child))
{
/*error message*/
}
}