在QT窗口中进行DirectX11渲染而不会丢失其他小部件

时间:2018-04-24 10:35:09

标签: c++ qt qt5 directx-11

我正在使用DirectX渲染到我的QT窗口,在我开始添加控件之前效果很好。然后当directx超越它们时它们闪烁。 winforms中的解决方案是在表单的OnPaintBackground中进行渲染,但我没有在QT中找到等价物。是否有类似的东西或能以更好的方式完成?

我正在考虑渲染到纹理,然后以某种方式将其转换为像素图,然后我在paintEvent()中绘制。

对我来说,能够将按钮,面板和其他控件置于正在渲染的内容(实时)上非常重要。

EDIT。

Sergio Monteleone的回答几乎可以解决,控件上的闪烁消失了。我在代码中更改控件的父级并将其设置为主窗口而不是QFrame。但是现在,如果我在它们之间快速移动鼠标,我会在控件之间出现一个奇怪的灰色框。 enter image description here

PushButton后面和GroupBox上方的灰色矩形只能在几分之一秒内看到。知道为什么会这样吗?

编辑2。

我改变了问题的主题来描述实际问题,而不是一个解决方案。

2 个答案:

答案 0 :(得分:1)

我认为你使用QWidget的原生hWnd绘制DirectX。如果是这种情况,则另一个Qt控制闪烁,因为当窗口小部件重新绘制自身时,它也会导致完整的后台更新。然后,使用DirectX在该表面上绘制,丢弃到目前为止Qt控件绘制的内容。

那么为什么他们闪烁而不是完全消失?

QWidget 在内部使用双缓冲(查看文档here)。 所以它实际上取决于Qt后台缓冲区何时失效。

你可以使用一个简单的技巧:在窗口中添加 QFrame ,将Qt控件置于其上,然后使用框架的hWnd作为DirectX目标。此外,您可能需要将该QFrame的 autoFillBackground 属性设置为false。这个应该允许你渲染到后台。

答案 1 :(得分:0)

感谢Sergio Monteleone,我提出了两个我认为可以接受的解决方案。 我已将这两种方法上传到github: https://github.com/Zephyrox/QT5DX11

方法1 - 工作但很慢

  • 从QFRAME而不是主窗口获取HWND。
  • 将DX渲染转换为纹理
  • 将其转换为QBitmap并在qframe paintEvent
  • 中绘制
  • qframe必须每帧更新

方法2 - 几乎可以工作,速度快但可以产生闪烁(Sergio Monteleone的解决方案)

  • 在qframe上渲染DX,将控件放在qframe(不在其中)

    之上

    如果你像疯了一样在按钮之间移动鼠标,当vsync为ON时,它们之间会出现一个闪烁的框。 当VSync为ON时,此方法实际上在我的机器上较慢。这当然是因为没有渲染。我没有试过在这里做任何重渲染,但我怀疑如果fps下降到60左右,即使没有vsync也会看到闪烁。

修改。在渲染的qframe上将UpdatesEnabled设置为false,即使使用慢速fps或vsync也不会闪烁。

如果您有兴趣查看实施,请检查回购。将DX纹理转换为QImage非常简单:

    HRESULT hr = swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast< void** >(&pSurface));

    if(hr != S_OK)
        return;

    context->CopyResource(renderTargetTex, pSurface);

    D3D11_MAP eMapType = D3D11_MAP_READ;
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    context->Map(renderTargetTex, 0, eMapType, NULL, &mappedResource);

    BYTE* pYourBytes = (BYTE*)mappedResource.pData;

    bitmap = QImage(pYourBytes, windowWidth, windowHeight, QImage::Format_RGBA8888);

        context->Unmap(renderTargetTex, 0);

此代码仅用于测试方法,它缺少大多数错误检查,可能会泄漏大量内存。