Reputation: 441
I'm rendering to my QT window using DirectX, works great until I start adding controls. Then they flicker as directx draws over them. The solution in winforms is do the rendering in OnPaintBackground of the form but I haven't found an equivalent in QT. Is there something similiar or can it be done in a better way?
I'm thinking about rendering to a texture and then somehow converting that to pixmap, which I then draw in paintEvent().
It's important to me to be able to place buttons, panels and other controls on top of what's being rendered (in realtime).
EDIT.
Sergio Monteleone's answer almost works, the flickering on controls are gone. I change the parent of the controls in code and set it to the main window instead of the QFrame. However now I get a weird gray box between the controls if I move the mouse fast between them.
The gray rectangle behind PushButton and above GroupBox is only visible a fraction of a second. Any idea why this is?
EDIT 2.
I changed the topic of the question to describe the actual problem and not ONE solution.
Upvotes: 0
Views: 3140
Reputation: 441
Thanks to Sergio Monteleone's I've come up with two solutions that I find acceptable. I have uploaded both methods to github: https://github.com/Zephyrox/QT5DX11
METHOD 1 - Works but slow
METHOD 2 - Almost works, fast but can produce flicker (Sergio Monteleone's solution)
Render DX on qframe, place controls on top of qframe (not in)
If you move the mouse between the buttons like crazy, a flicker occurs as a box between them, when vsync is ON. When VSync is ON, this method is actually slower on my machine. This is of course because nothings being rendered. I have not tried doing any heavy rendering here but I suspect that if the fps drops to around 60, the flickering will be visible even without vsync.
EDIT. Set UpdatesEnabled to false on the qframe being rendered to and there will be no flickering even with slow fps or vsync.
Check the repo if you are interested in seeing the implementations. Converting the DX texture to a QImage turned out to be quite simple:
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);
This code is only for testing out the method, it lacks most error checks and probably leaks huge amounts of memory.
Upvotes: 1
Reputation: 2886
I presume you are drawing with DirectX using the native hWnd of the QWidget. If that's the case, the other Qt controls flicker because when the widget repaints itself it causes a full background update as well. Then, you draw using DirectX on top of that surface, discarding whatever the Qt controls have drawn so far.
So why they flicker instead of disappearing completely?
QWidget uses double buffering internally (have a look at the documentation here). So it really depends on when Qt back buffer is invalidated.
You can use a simple trick though: Add a QFrame to the window, place the Qt controls on top of it and then use the frame's hWnd as the DirectX target. Also, you may need to set the autoFillBackground property of that QFrame to false. This should allow you to render to the background.
Upvotes: 1