0

I created a D3D11 device and can perform operations such as rendering pictures smoothly, but in order to also support GDI, I tried several methods:

  1. Through swapchain -> GetBuffer(ID3D11Texture2D) -> CreateDxgiSurfaceRenderTarget -> ID2D1GdiInteropRenderTarget -> GetDC, finally get the DC. It runs normally on my Win10, but an exception report when running GetDC on Win7: _com_error.
  2. Via swapchain -> GetBuffer(IDXGISurface1) -> GetDC, same as 1.

I suspect that the ID3D11Texture2D/IDXGISurface1 obtained by GetBuffer on Win7 will have some restrictions on the use of GDI, so I changed to dynamically create a new ID3D11Texture2D by myself, and now use DC alone/D3D11 drawing interface alone It works fine, but if I interoperate, I will find that gdi opertaion is drawn on the custom-created ID3D11Texture2D instead of the back_buffer of swapchain:

_d3d->Clear();
_d3d->DrawImage();
HDC hdc = _d3d->GetDC();
DrawRectangleByGDI(hdc);
_d3d->ReleaseDC();
_d3d->Present();

So how to do it: Whether the D3D or DC methods is drawn, they are all on the same ID3D11Texture2D? This way, it is also convenient for my CopyResource.

HRESULT CGraphRender::Resize(const UINT32& width, const UINT32& height)
{
_back_texture2d = nullptr;
_back_rendertarget_view = nullptr;
_dc_texture2d = nullptr;
_dc_render_target = nullptr;

float dpi = GetDpiFromD2DFactory(_d2d_factory);

//Backbuffer
HRESULT hr = _swap_chain->ResizeBuffers(2, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, _is_gdi_compatible ? DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE : 0);
RETURN_ON_FAIL(hr);

hr = _swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&_back_texture2d);
RETURN_ON_FAIL(hr);

hr = CreateD3D11Texture2D(_d3d_device, width, height, &_dc_texture2d);
RETURN_ON_FAIL(hr);

D3D11_RENDER_TARGET_VIEW_DESC rtv;
rtv.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtv.Texture2D.MipSlice = 0;
hr = _d3d_device->CreateRenderTargetView(_back_texture2d, &rtv, &_back_rendertarget_view);
RETURN_ON_FAIL(hr);
 
...
}

HRESULT CGraphRender::Clear(float color[])
{
CComPtr<ID3D11DeviceContext> immediate_context;
_d3d_device->GetImmediateContext(&immediate_context);
if (!immediate_context)
{
    return E_UNEXPECTED;
}
ID3D11RenderTargetView* ref_renderTargetView = _back_rendertarget_view;
immediate_context->OMSetRenderTargets(1, &ref_renderTargetView, nullptr);
immediate_context->ClearRenderTargetView(_back_rendertarget_view, color);

return S_OK;
}

HDC     CGraphRender::GetDC()
{
if (_is_gdi_compatible)
{
    CComPtr<IDXGISurface1>  gdi_surface;
    HRESULT hr = _dc_texture2d->QueryInterface(__uuidof(IDXGISurface1), (void**)&gdi_surface);
    if (SUCCEEDED(hr))
    {
        HDC hdc = nullptr;
        hr = gdi_surface->GetDC(TRUE, &hdc);
        if (SUCCEEDED(hr))
        {
            return hdc;
        }
    }
}
return nullptr;
}

HRESULT CGraphRender::CopyTexture(ID3D11Texture2D* dst_texture, ID3D11Texture2D* src_texture, POINT* dst_topleft/* = nullptr*/, POINT* src_topleft/* = nullptr*/)
{
if (!dst_texture && !src_texture)
{
    return E_INVALIDARG;
}
CComPtr<ID3D11DeviceContext> immediate_context;
_d3d_device->GetImmediateContext(&immediate_context);
if (!immediate_context)
{
    return E_UNEXPECTED;
}

ID3D11Texture2D* dst_texture_real = dst_texture ? dst_texture : _dc_texture2d;
POINT dst_topleft_real = dst_topleft ? (*dst_topleft) : POINT{ 0, 0 };
ID3D11Texture2D* src_texture_real = src_texture ? src_texture : _dc_texture2d;
POINT src_topleft_real = src_topleft ? (*src_topleft) : POINT{ 0, 0 };

D3D11_TEXTURE2D_DESC src_desc = { 0 };
src_texture_real->GetDesc(&src_desc);
D3D11_TEXTURE2D_DESC dst_desc = { 0 };
dst_texture_real->GetDesc(&dst_desc);

if (!dst_topleft_real.x && !src_topleft_real.x && !dst_topleft_real.y && !src_topleft_real.y && dst_desc.Width == src_desc.Width && dst_desc.Height == src_desc.Height)
{
    immediate_context->CopyResource(dst_texture_real, src_texture_real);
}
else
{
    D3D11_BOX   src_box;
    src_box.left = min((UINT)src_topleft_real.x, (UINT)dst_topleft_real.x + dst_desc.Width);
    src_box.top = min((UINT)src_topleft_real.y, (UINT)dst_topleft_real.y + dst_desc.Height);
    src_box.right = min((UINT)src_box.left + src_desc.Width, (UINT)dst_topleft_real.x + dst_desc.Width);
    src_box.bottom = min((UINT)src_box.top + src_desc.Height, (UINT)dst_topleft_real.y + dst_desc.Height);
    src_box.front = 0;
    src_box.back = 1;

    ATLASSERT(src_box.left < src_box.right);
    ATLASSERT(src_box.top < src_box.bottom);

    immediate_context->CopySubresourceRegion(dst_texture_real, 0, dst_topleft_real.x, dst_topleft_real.y, 0, src_texture_real, 0, &src_box);
}

return S_OK;
}
fredirty2017
  • 103
  • 11
  • what are the errors exactly? have you enabled the debug layer? – Simon Mourier Oct 15 '20 at 18:45
  • "Unable to load D2D debug layer, Exception thrown at 0x76B3C5AF in DirectRenderDemo.exe: Microsoft C++ exception: _com_error, at memory location 0x0C23D040", The question is how to make GDI and D3D operate in the same ID3D11Texture2D, i don't known how to setting. – fredirty2017 Oct 16 '20 at 02:12
  • Even reference is useless: https://stackoverflow.com/questions/5979632/d3d11-how-to-draw-gdi-text-to-a-gxdi-surface-without-d2d, Even if I create ID3D11Texture2D dynamically instead of back_buffer, The first time I called GetDC, the exception occurred(calling OMSetRenderTargets and ClearRenderTargetView before GetDC) – fredirty2017 Oct 16 '20 at 11:24
  • some _com_error are normal, they are just 1st chance exceptions. You really want to enable the debug layer, it's a life saver, and there's no reason why it wouldn't work. Or show a small reproducing project. – Simon Mourier Oct 16 '20 at 17:16

1 Answers1

0

I don’t think Windows 7 supports what you’re trying to do. Here’s some alternatives.

  1. Switch from GDI to something else that can render 2D graphics with D3D11. Direct2D is the most straightforward choice here. And DirectWrite if you want text in addition to rectangles.

  2. If your 2D content is static or only changes rarely, you can use GDI+ to render into in-memory RGBA device context, create Direct3D11 texture with that data, and render a full-screen triangle with that texture.

  3. You can overlay another Win32 window on top of your Direct3D 11 rendering one, and use GDI to render into that one. The GDI window on top must have WS_EX_LAYERED expended style, and you must update it with UpdateLayeredWindow API. This method is the most complicated and least reliable, though.

Soonts
  • 20,079
  • 9
  • 57
  • 130
  • The code of OBS's window-capture.c is similar, but I haven't fully understood the process, it can run on win7. extern "C" EXPORT void *gs_ texture_ get_ dc(gs_ texture_ t *tex) { HDC hDC = nullptr; if (tex->type != GS_ TEXTURE_ 2D) return nullptr; Gs_ texture_ 2d *tex2d = static_ cast(tex); if (!TextureGDICompatible(tex2d, "gs_ texture_ get_ dc")) return nullptr; if (!tex2d->gdiSurface) return nullptr; tex2d->gdiSurface->GetDC(true, &hDC); return hDC; } – fredirty2017 Oct 16 '20 at 11:58
  • when debug obs same "GetDC", i found exception also: Exception thrown at 0x76B3C5AF in obs32.exe: Microsoft C++ exception: _com_error, at memory location 0x0C48F35C – fredirty2017 Oct 16 '20 at 12:09