D3D11: How to draw GDI Text to a GXDI Surface? (Wi

2020-06-04 03:29发布

问题:

I need some help with drawing a text to a texture with GDI and D3D11. I tried using D2D/DirectWrite, but it supports just D3D10 and not D3D11 as I need. Everything I tried failed so far... Now I want to use GDI methodes to write in the texture. So I created a texture with this params:

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

Then I created a normal RenderTargetView from this texture as Microsoft sais here: http://msdn.microsoft.com/en-us/library/ff476203%28v=vs.85%29.aspx

Next Step: Get The DXGI Interface:

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

On the Render function I do just this:

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;

The problem is, that the texture is still empty after that GDI drawing (Also tested with PIX). Everything works and there are no error messages.

I hope that anybody can explain how it works.

Thanks, Stefan

EDIT: Tried it also with GetDC(FALSE,&hDc) (according to the documentation): same results -> nothing.

回答1:

I actually fought this problem a lot during last week - but I've got it all working! Here is a list of things you should know/do to make it all work:

  • Check the surface requirements for a GetDC method to work here: http://msdn.microsoft.com/en-us/library/windows/desktop/ff471345(v=vs.85).aspx

Keep the following in mind when using this method:

•You must create the surface by using the D3D11_RESOURCE_MISC_GDI_COMPATIBLE flag for a surface or by using the DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE flag for swap chains, otherwise this method fails.

•You must release the device and call the IDXGISurface1::ReleaseDC method before you issue any new Direct3D commands.

•This method fails if an outstanding DC has already been created by this method.

•The format for the surface or swap chain must be DXGI_FORMAT_B8G8R8A8_UNORM_SRGB or DXGI_FORMAT_B8G8R8A8_UNORM.

•On GetDC, the render target in the output merger of the Direct3D pipeline is unbound from the surface. You must call the ID3D11DeviceContext::OMSetRenderTargets method on the device prior to Direct3D rendering after GDI rendering.

•Prior to resizing buffers you must release all outstanding DCs.

  • If you're going to use it in the back buffer, remember to re-bind render target after you've called ReleaseDC. It is not neccessary to manually unbind RT before calling GetDC as this method does that for you.

  • You can not use any Direct3D drawing between GetDC() and ReleaseDC() calls as the surface is excusively locked out by DXGI for GDI. However you can mix GDI and D3D rendering provided that you call GetDC()/ReleaseDC() every time you need to use GDI, before moving on to D3D.

  • This last bit may sounds easy, but you'd be surprised how many developers fall into this issue - when you draw with GDI on the back buffer, remember that this is the back buffer, not a framebuffer, so in order to actually see what you've drawn, you have to re-bind RT to OM and call swapChain->Present() method so the backbuffer will become a framebuffer and its contents will be displayed on the screen.



回答2:

Maybe you're doing everything fine, it's just the text drawing doesn't do what you expect?

COLORREF bla = SetPixel(hDc,1,1,RGB(255,255,255));
bool hmm = TextOutA(hDc, 10, 10, "LALALA!", 7);

I don't understand from this how do you expect that TextOutA will guess that bla should be used as the text color. AFAIK the default text color used in the newly created/obtained DC is black. Not sure about the background fill mode, but if it's TRANSPARENT by default - this fully explains why nothing is drawing.

I'd change your code to the following:

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));


回答3:

I am going to use it in the back buffer. I am not sure if it's done correctly. I can't see the drawing. It's showing black.

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);
}

I have used swap chain desc:

    DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;