【问题标题】:How to support (on win7) GDI, D3D11 interoperability?如何支持(在win7上)GDI、D3D11互操作?
【发布时间】:2021-01-29 20:39:55
【问题描述】:

我创建了一个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;
}

【问题讨论】:

  • 究竟是什么错误?你启用调试层了吗?
  • "无法加载D2D调试层,DirectRenderDemo.exe中在0x76B3C5AF处抛出异常:Microsoft C++异常:_com_error,在内存位置0x0C23D040",问题是如何让GDI和D3D同时运行ID3D11Texture2D,不知道怎么设置。
  • 即使引用也没用:stackoverflow.com/questions/5979632/…,即使我动态创建 ID3D11Texture2D 而不是 back_buffer,第一次调用 GetDC 时发生异常(在 GetDC 之前调用 OMSetRenderTargets 和 ClearRenderTargetView)
  • 一些 _com_error 是正常的,它们只是第一次机会例外。你真的想启用调试层,它是一个救生员,它没有理由不起作用。或者展示一个小的复制项目。

标签: directx-11 direct3d11


【解决方案1】:

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

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

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

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

【讨论】:

  • OBS的window-capture.c的代码类似,但是我还没有完全看懂流程,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;如果(!tex2d->gdiSurface)返回nullptr; tex2d->gdiSurface->GetDC(true, &hDC);返回hDC; }
  • 当调试 obs 相同的“GetDC”时,我也发现异常:在 obs32.exe 中在 0x76B3C5AF 处引发异常:Microsoft C++ 异常:_com_error,位于内存位置 0x0C48F35C
猜你喜欢
  • 2012-01-30
  • 1970-01-01
  • 1970-01-01
  • 2011-05-25
  • 2016-10-09
  • 2012-10-15
  • 2022-08-15
  • 2015-03-08
  • 1970-01-01
相关资源
最近更新 更多