首页 > 解决方案 > 如何支持(在win7上)GDI、D3D11互操作?

问题描述

我创建了一个D3D11设备,可以流畅地进行渲染图片等操作,但是为了也支持GDI,我尝试了几种方法:

  1. 通过swapchain -> GetBuffer(ID3D11Texture2D) -> CreateDxgiSurfaceRenderTarget -> ID2D1GdiInteropRenderTarget -> GetDC,最终得到DC。在我的Win10上运行正常,但是在Win7上运行GetDC时报异常:_com_error。
  2. 通过swapchain -> GetBuffer(IDXGISurface1) -> GetDC,同1。

我怀疑在Win7上GetBuffer得到的ID3D11Texture2D/IDXGISurface1会对GDI的使用有一定的限制,所以我改成自己动态新建一个ID3D11Texture2D,现在单独使用DC/单独使用D3D11绘图界面就可以了,但是如果我互操作,会发现gdi操作是在自定义创建的ID3D11Texture2D上绘制的,而不是swapchain的back_buffer:

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

那么怎么做:无论是D3D还是DC方法绘制,都在同一个ID3D11Texture2D上?这样,我的 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;
}

标签: directx-11direct3d11

解决方案


我认为 Windows 7 不支持您尝试执行的操作。这里有一些替代方案。

  1. 从 GDI 切换到其他可以使用 D3D11 渲染 2D 图形的东西。Direct2D 是这里最直接的选择。如果你想要除了矩形之外的文本,还有 DirectWrite。

  2. 如果您的 2D 内容是静态的或很少更改,您可以使用 GDI+ 渲染到内存中的 RGBA 设备上下文中,使用该数据创建 Direct3D11 纹理,并使用该纹理渲染一个全屏三角形。

  3. 您可以在 Direct3D 11 渲染窗口之上覆盖另一个 Win32 窗口,然后使用 GDI 渲染到该窗口中。顶部的 GDI 窗口必须具有 WS_EX_LAYERED 扩展样式,并且您必须使用 UpdateLayeredWindow API 对其进行更新。但是,这种方法是最复杂和最不可靠的。


推荐阅读