D3D11:如何将GDI文本绘制到GXDI表面?(无需D2D)

8

我需要一些关于使用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

然后我按照Microsoft在这里所说的方式从这个纹理创建了一个普通的RenderTargetView:http://msdn.microsoft.com/en-us/library/ff476203%28v=vs.85%29.aspx

下一步:获取DXGI接口:

m_pTexFSText->QueryInterface(__uuidof(IDXGISurface1), (void **)(&m_pDXGISurface));

在Render函数中,我只是这样做:

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进行了测试)。 一切都正常,没有错误消息。
我希望有人能解释一下它的工作原理。
谢谢,Stefan
编辑:也尝试了根据文档使用GetDC(FALSE,&hDc):相同的结果-> 什么也没有。

你尝试过使用 DXGI_FORMAT_R8G8B8A8_UINT 格式吗? - Necrolis
根据上面链接中的文档,与 GDI 兼容的纹理需要特殊格式。"您必须将纹理格式设置为以下类型之一:DXGI_FORMAT_B8G8R8A8_UNORM、DXGI_FORMAT_B8G8R8A8_TYPELESS、DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"。 - sandicz
3个回答

7

上周我遇到了这个问题,经过努力我终于把它解决了!以下是使它正常工作的事项:

使用此方法时请注意以下事项:

•您必须使用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绘制,因为表面被DXGI排他性锁定以供GDI使用。但是,您可以混合使用GDI和D3D渲染,只要在需要使用GDI时每次调用GetDC()/ReleaseDC(),然后转到D3D。

  • 最后一点可能听起来很容易,但您会惊讶地看到有多少开发人员陷入了这个问题-当您在后台缓冲区上进行GDI绘制时,请记住这是后台缓冲区,而不是帧缓冲区,因此为了实际看到您所绘制的内容,您必须重新绑定RT到OM并调用swapChain->Present()方法,使后备缓冲区成为帧缓冲区,并显示其内容在屏幕上。


嘿,谢谢你的回答。听起来非常有用,也许我知道我的代码出了什么问题(释放DC并重新绑定Rendertarget)。不幸的是,我现在没有时间尝试你的解决方案,但我将来会尝试... - sandicz

3
也许你做的一切都没问题,只是文本绘制不符合你的预期?
COLORREF bla = SetPixel(hDc,1,1,RGB(255,255,255));
bool hmm = TextOutA(hDc, 10, 10, "LALALA!", 7);

从这里我不明白你是如何期望TextOutA猜测应该使用bla作为文本颜色的。据我所知,在新创建/获取的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白色,然后编写演示文本。我会尝试您的版本。谢谢 :) - sandicz
我刚刚注意到,在GetDC()函数上出现了一个错误消息:“Microsoft C++-Exception: _com_error at memory position 0x0018f760”(翻译成英语)。我真的无法想象为什么会发生这种情况。也许这才是主要问题,而不是GDI。看起来我需要等待DirectWrite支持D3D11... - sandicz
是的,检查返回值/异常是一个好主意,特别是当出现问题时 :) - valdo
抱歉,我忽略了调试控制台,因为没有错误消息框或负返回值... - sandicz

0

我将在后备缓冲区中使用它。我不确定是否正确完成了。我看不到绘图。它显示为黑色。

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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接