D3D11: How to draw GDI Text to a GXDI Surface? (Without D2D)
Asked Answered
S

3

8

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.

Silvey answered 12/5, 2011 at 14:33 Comment(2)
have you tried a format of DXGI_FORMAT_R8G8B8A8_UINT?Haggadist
according to the docu on the link above a gdi compatible texture needs special formats "You must set the texture format to one of the following types: DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_TYPELESS, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB"Silvey
O
7

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:

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.

Oreste answered 9/1, 2012 at 20:47 Comment(1)
hey, thanks for your answer. it sounds really useful and maybe I know where the problem is in my code (releasing DC and rebinding Rendertarget). Unfortunately I've got no time to try your solition now, but I will in the future...Silvey
U
3

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));
Usurer answered 16/5, 2011 at 9:19 Comment(4)
hi,these lines are just tests. set the pixel at 1,1 to white and secondly write a demo text. I will try your version. thanks :)Silvey
I just noticed, that im getting an error message on the GetDC() function: "Microsoft C++-Exception: _com_error at memory position 0x0018f760" (translated to english). I really can't imagine why this happens. Maybe THIS is the main problem and not GDI. Looks like I need to wait until DirectWrite supports D3D11...Silvey
Yeah, checking the return value/exceptions is a good idea, especially if something is wrong :)Usurer
sorry, I ignored the debug console as there was no error message box or negative return value...Silvey
M
0

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;
Minutia answered 28/1, 2019 at 6:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.