D3D11:如何将 GDI 文本绘制到 GXDI 表面? (没有 D2D)
Posted
技术标签:
【中文标题】D3D11:如何将 GDI 文本绘制到 GXDI 表面? (没有 D2D)【英文标题】:D3D11: How to draw GDI Text to a GXDI Surface? (Without D2D) 【发布时间】:2011-08-24 04:35:31 【问题描述】:在使用 GDI 和 D3D11 将文本绘制到纹理上时,我需要一些帮助。我尝试使用 D2D/DirectWrite,但它只支持 D3D10 而不是我需要的 D3D11。到目前为止,我尝试的一切都失败了...... 现在我想使用 GDI 方法在纹理中写入。 所以我用这个参数创建了一个纹理:
Usage = D3D11_USAGE_DEFAULT;
Format = DXGI_FORMAT_B8G8R8A8_UNORM;
BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
CPUAccessFlags = 0;
MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE
然后我从这个纹理创建了一个普通的 RenderTargetView,就像微软在这里所说的那样:http://msdn.microsoft.com/en-us/library/ff476203%28v=vs.85%29.aspx
下一步:获取 DXGI 接口:
m_pTexFSText->QueryInterface(__uuidof(IDXGISurface1), (void **)(&m_pDXGISurface));
在渲染功能上我就是这样做的:
m_pDeviceContext->OMSetRenderTargets(1,&m_pTextRenderTarget,NULL);
HDC hDc = NULL;
if(FAILED(m_pDXGISurface->GetDC(TRUE,&hDc)))
return E_FAIL;
COLORREF bla = SetPixel(hDc,1,1,RGB(255,255,255));
bool hmm = TextOutA(hDc, 10, 10, "LALALA!", 7);
if(FAILED(m_pDXGISurface->ReleaseDC(NULL)))
return E_FAIL;
问题是,在 GDI 绘制之后纹理仍然是空的(也用 PIX 测试过)。 一切正常,没有错误消息。
我希望任何人都可以解释它是如何工作的。
谢谢,斯特凡
编辑:也用GetDC(FALSE,&hDc)
尝试过(根据文档):结果相同 -> 没有。
【问题讨论】:
你试过DXGI_FORMAT_R8G8B8A8_UINT
的格式吗?
根据上面链接上的文档,gdi 兼容纹理需要特殊格式“您必须将纹理格式设置为以下类型之一:DXGI_FORMAT_B8G8R8A8_UNORM、DXGI_FORMAT_B8G8R8A8_TYPELESS、DXGI_FORMAT_B8G8R8A8_UNORM_SRGB”
【参考方案1】:
实际上,我在上周曾多次解决这个问题 - 但我已经解决了所有问题!以下是您应该知道/做的事情的清单,以使一切正常:
在此处检查 GetDC 方法的表面要求:http://msdn.microsoft.com/en-us/library/windows/desktop/ff471345(v=vs.85).aspx使用此方法时请记住以下几点:
•您必须通过对表面使用 D3D11_RESOURCE_MISC_GDI_COMPATIBLE 标志或对交换链使用 DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE 标志来创建表面,否则此方法将失败。
•在发出任何新的 Direct3D 命令之前,您必须释放设备并调用 IDXGISurface1::ReleaseDC 方法。
•如果此方法已创建未完成的 DC,则此方法将失败。
•表面或交换链的格式必须是 DXGI_FORMAT_B8G8R8A8_UNORM_SRGB 或 DXGI_FORMAT_B8G8R8A8_UNORM。
•在 GetDC 上,Direct3D 管道的输出合并中的渲染目标未绑定到表面。在 GDI 渲染之后的 Direct3D 渲染之前,您必须在设备上调用 ID3D11DeviceContext::OMSetRenderTargets 方法。
•在调整缓冲区大小之前,您必须释放所有未完成的 DC。
如果您要在后台缓冲区中使用它,请记住在调用 ReleaseDC 之后重新绑定渲染目标。无需在调用 GetDC 之前手动解除绑定 RT,因为此方法会为您执行此操作。
您不能在 GetDC() 和 ReleaseDC() 调用之间使用任何 Direct3D 绘图,因为 GDI 的 DXGI 专门锁定了表面。但是,您可以混合使用 GDI 和 D3D 渲染,前提是您每次需要使用 GDI 时调用 GetDC()/ReleaseDC(),然后再转到 D3D。
这最后一点听起来很简单,但您会惊讶于有多少开发人员陷入了这个问题 - 当您在后台缓冲区上使用 GDI 绘图时,请记住这是 back 缓冲区,而不是一个帧缓冲区,所以为了真正看到你绘制的内容,你必须将 RT 重新绑定到 OM 并调用 swapChain->Present() 方法,这样后缓冲区将成为一个帧缓冲区,其内容将显示在屏幕上。
【讨论】:
嘿,谢谢你的回答。这听起来真的很有用,也许我知道问题出在我的代码中(释放 DC 并重新绑定 Rendertarget)。不幸的是,我现在没有时间尝试你的解决方案,但我会在未来......【参考方案2】:也许你做的一切都很好,只是文字绘制没有达到你的预期?
COLORREF bla = SetPixel(hDc,1,1,RGB(255,255,255));
bool hmm = TextOutA(hDc, 10, 10, "LALALA!", 7);
从这里我不明白你如何期望TextOutA
会猜测bla
应该用作文本颜色。 AFAIK 新创建/获得的 DC 中使用的默认文本颜色为黑色。不确定背景填充模式,但如果它默认为 TRANSPARENT
- 这完全解释了为什么什么都没有绘制。
我会将您的代码更改为以下内容:
COLORREF bla = SetPixel(hDc,1,1,RGB(255,255,255));
VERIFY(SetTextColor(hDc, bla) != CLR_INVALID);
CREct rc(0, 0, 30, 20); // put relevant coordinates
VERIFY(ExtTextOut(hDc, rc.left, rc.top, ETO_CLIPPED, &rc, "LALALA!", 7));
【讨论】:
嗨,这些行只是测试。将 1,1 的像素设置为白色,然后编写演示文本。我会试试你的版本。谢谢:) 我刚刚注意到,我在 GetDC() 函数上收到一条错误消息:“Microsoft C++-Exception: _com_error at memory position 0x0018f760”(翻译成英文)。我真的无法想象为什么会发生这种情况。也许这是主要问题,而不是 GDI。看来我需要等到 DirectWrite 支持 D3D11... 是的,检查返回值/异常是个好主意,尤其是在出现问题时:) 对不起,我忽略了调试控制台,因为没有错误消息框或负返回值...【参考方案3】:我将在后台缓冲区中使用它。我不确定它是否正确完成。我看不到图。它显示为黑色。
HDC GetSurfaceDC()
m_pSurface1 = nullptr;
HDC hdc;
//Setup the swapchain surface
IF_FAILED_THROW_HR(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_pSurface1)));
// Obtain the back buffer for this window which will be the final 3D render target.
ID3D11Texture2DPtr backBuffer;
IF_FAILED_THROW_HR(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)));
// Create a descriptor for the RenderTargetView.
CD3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc(D3D11_RTV_DIMENSION_TEXTURE2DARRAY, DXGI_FORMAT_B8G8R8A8_UNORM, 0, 0, 1);
ID3D11RenderTargetViewPtr renderTargetView;
// Create a view interface on the render target to use on bind for mono or left eye view.
IF_FAILED_THROW_HR(m_device->CreateRenderTargetView(backBuffer, &renderTargetViewDesc, &renderTargetView));
m_context->OMSetRenderTargets(1, &renderTargetView.GetInterfacePtr(), nullptr);
IF_FAILED_THROW_HR(m_pSurface1->GetDC(FALSE, &hdc));
return hdc;
void ReleaseSurfaceDC()
if (m_pSurface1 == nullptr)
return;
//When finish drawing release the DC
m_pSurface1->ReleaseDC(nullptr);
m_context->OMSetRenderTargets(1, &m_renderTargetView.GetInterfacePtr(), m_depthStencilView);
我用过交换链描述:
DXGI_SWAP_CHAIN_DESC swapChainDesc = 0 ;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
【讨论】:
以上是关于D3D11:如何将 GDI 文本绘制到 GXDI 表面? (没有 D2D)的主要内容,如果未能解决你的问题,请参考以下文章
Direct2D - 如何绘制尽可能接近 GDI 渲染的文本