I want to render a scene and display it on the monitor, while rendering another one to a texture.
Do I need to create two swapchains? How do I create the second swapchain in this case? I tried to call CreateSwapChainForCoreWindow but got memory access exceptions.
Swapchains are really just for displaying stuff.
To render to something, you have to add a render target view to the device via the OMSetRenderTargets() call. You can create render target views via CreateRenderTargetView(), which takes a resource as input. Textures are resources too... you just have to create them with the D3D11_BIND_RENDER_TARGET flag.
That's just a few cues that should be able to point you into the right direction.
Btw, Swapchains have buffers, which are resources that are used to create a render target view as well. That's how you render to a swapchain; it really doesn't have anything to do with "swapchains" at all.
Related
I want make a Direct2D GUI that will run on a DLL and will render with the Direct3D of the application that I inject into it.
I know that I can simply use ID2D1Factory::CreateDxgiSurfaceRenderTarget to make a DXGI surface and use it as d2d render target, but this require enabling the flag D3D11_CREATE_DEVICE_BGRA_SUPPORT on Direct3D's device.
The problem is that the application creates its device without enabling this flag and, for this reason, ID2D1Factory::CreateDxgiSurfaceRenderTarget fails.
I am trying to find a other way to draw on the application window (externally or inside window's render target) that also works if that window is in full-screen.
I tried these alternatives so far:
Create a d2d render target with ID2D1Factory::CreateDCRenderTarget. This worked, but the part I rendered was blinking/flashing (show and hide very fast in loop). I also called ID2D1DCRenderTarget::BindDC before ID2D1RenderTarget::BeginDraw, but it just blinks but a bit less, so I still had the same issue.
Create a new window that will always be on the top of every other window and render there with d2d but, if the application goes into full-screen, then this window does not show on screen.
Create a second D3D device with enabled the D3D11_CREATE_DEVICE_BGRA_SUPPORT flag and share an ID3D11Texture2D resource between the device of the window and my own, but I wasn't able to make it work... There are not a lot of examples on how to do it. The idea was to create a 2nd device, draw with d2d on that device and then sync the 2 D3D devices – I followed this example (with direct11).
Create a D2D device and share the data of d2d device with d3d device; but, when I call ID2D1Factory1::CreateDevice to create the device it fails because the D3D device is created without enabling the D3D11_CREATE_DEVICE_BGRA_SUPPORT flag. I started with this example.
I've heard of hardware overlay but it works only on some graphics cards and I think I will have problems with this https://learn.microsoft.com/el-gr/windows/win32/medfound/hardware-overlay-support.
I am currently at a dead end; I don't know what to do. Does anyone have any idea that may help me?
Maybe is there any way to draw on screen and work even if a window is in full-screen?
The #3 is the correct one. Here’s a few tips.
Don’t use keyed mutexes. Don’t use NT handles. The only flag you need is D3D11_RESOURCE_MISC_SHARED.
To properly synchronize access to the shared texture across devices, use queries. Specifically, you need a query of type D3D11_QUERY_EVENT. The workflow should look like following.
Create a shared texture on one device, open in another one. Doesn’t matter where it’s created and where imported. Don’t forget the D3D11_BIND_RENDER_TARGET flag. Also create a query.
Create D2D device with CreateDxgiSurfaceRenderTarget of the shared texture, render your overlay into the shared texture with D2D and/or DirectWrite.
On the immediate D3D device context with the BGRA flag which you use for D2D rendering, call ID3D11DeviceContext.End once, passing the query. Then wait for the ID3D11DeviceContext.GetData to return S_OK. If you care about electricity/thermals use Sleep(1), or if you prioritize latency, busy wait with _mm_pause() instructions.
Once ID3D11DeviceContext.GetData returned S_OK for that query, the GPU has finished rendering your 2D scene. You can now use that texture on another device to compose into 3D scene.
The way to compose your 2D content into the render target depends on how do you want to draw your 2D content.
If that’s a small opaque quad, you can probably CopySubresourceRegion into the render target texture.
Or, if your 2D content has transparent background, you need a vertex+pixel shaders to render a quad (4 vertices) textured with your shared texture. BTW you don’t necessarily need a vertex/index buffer for that, there’s a well-known trick to do without one. Don’t forget about blend state (you probably want alpha blending), depth/stencil state (you probably want to disable depth test when rendering that quad), also the D3D11_BIND_SHADER_RESOURCE flag for the shared texture.
P.S. There’s another way. Make sure your code runs in that process before the process created their Direct3D device. Then use something like minhook to intercept the call to D3D11.dll::D3D11CreateDeviceAndSwapChain, in the intercepted function set that BGRA bit you need then call the original function. Slightly less reliable because there’re multiple ways to create a D3D device, but easier to implement, will work faster, and use less memory.
I'd like to show my Qt Quick content on a virtual screen inside my OpenSceneGraph scene.
The approach I'm using right now is highly inefficient:
Render Qt Quick to the offscreen surface using FBO (FrameBufferObject)
Download pixels with QOpenGLFramebufferObject::toImage()
Upload pixels to the OSG
So it's GPU-CPU-GPU transfer. Source code
A proper solution should somehow utilize existing FBO and be able to transfer data solely inside the GPU.
There are two options exist:
Create FBO on the Qt side and use its texture on the OSG side
Create FBO on the OSG side and feed it to the Qt Quick renderer
The Qt part is OK. And I'm completely lost with the OSG. Could anyone provide me with some pointers?
Finally made it.
General idea:
Render QtQuick to texture using FBO - there are some examples over the internet available.
Use this texture inside OpenSceneGraph
The whole thing includes several tricks.
Context Sharing
To perform certain graphics operations, we must initialize OpenGL global state, also known as context.
When a texture is created, context stores it's id. Ids are not globally unique, so when another texture is created within another context, it may get the same id, but with different resource behind it.
If you just pass your texture's id to another renderer (operating within different context), expecting it to show your texture, you end up showing another texture or black screen or crash.
The remedy is context sharing, which effectively means sharing ids.
OpenSceneGraph and Qt abstractions are not compatible, so you need to tell OSG not to use its own context abstraction. This is done by calling setUpViewerAsEmbeddedInWindow
Code:
OsgWidget::OsgWidget(QWidget* parent, Qt::WindowFlags flags)
: QOpenGLWidget(parent, flags)
, m_osgViewer(new osgViewer::Viewer)
{
setFormat(defaultGraphicsSettings());
// ...
m_osgGraphicsContext = m_osgViewer->setUpViewerAsEmbeddedInWindow(x(), y(), width(), height());
}
// osg::ref_ptr<osgViewer::GraphicsWindowEmbedded> m_osgGraphicsContext;
From now on, the existing QOpenGLContext instance will be used as an OpenGL context for OSG rendering.
You will need to create another context for QtQuick rendering and set them shared:
void Widget::initializeGL()
{
QOpenGLContext* qmlGLContext = new QOpenGLContext(this);
// ...
qmlGLContext->setShareContext(context());
qmlGLContext->create();
}
Remember, there can be only one active context at a time.
Your scheme is:
0. create osg::Texture out of QOpenGLFrameBufferObject::texture()
1. make QtQuick context active
2. render QtQuick to texture
3. make primary (OSG) context active
4. render OSG
5. goto 1
Making of a proper osg::Texture
Since OSG and Qt API are incompatible you barely can link QOpenGLFrameBufferObject to osg::Texture2D as it is.
QOpenGLFrameBufferObject has QOpenGLFrameBufferObject::texture() method which returns opengl texture id, but osg::Texture manages all openGL stuff on its own.
Something like osg::Texture2D(uint textureId); could help us but it just doesn't exist.
Let's make one by ourselves.
osg::Texture is backed by osg::TextureObject which stores OpenGL texture id and some other data as well. If we construct osg::TextureObject with a given texture id and pass it to osg::Texture, the latter will use it as its own.
Code:
void Widget::createOsgTextureFromId(osg::Texture2D* texture, int textureId)
{
osg::Texture::TextureObject* textureObject = new osg::Texture::TextureObject(texture, textureId, GL_TEXTURE_2D);
textureObject->setAllocated();
osg::State* state = m_osgGraphicsContext->getState();
texture->setTextureObject(state->getContextID(), textureObject);
}
Complete demo project here
Goal:
In my application I'm trying to implement multiple viewports to allow the user to view a scene from multiple perspectives. Each of my viewports need to be able to switch between wireframe, shaded, lighting, etc. I can currently render from different perspectives in each viewport, but I have issues.
Problem:
When I try to set various settings such as glPolygonMode() or qglClearColor() within any viewport, these settings only seem to apply to a single viewport, generally the very last viewport that was created. This isn't a signals/slots issue, since these connections are handled internally within each widget, and cannot be mixed up between widgets.
Attempts at solving the problem:
Since I'm using Qt as the library for managing all UI related things, I'm sure there are a lot of things Qt has taken care of for creating and setting up each OpenGL instance for me, so there may be things that I'm overlooking that I don't know about.
I've checked the constructors available for QGLWidgets, and seen that a QGLWidget can take in another QGLWidget as a "sharedwidget", and also a QGLContext object.
I currently use the "sharedwidget" route, because without it for some reason I can't get textures to bind for more than 1 viewport. However, this doesn't solve the problem of not being able to switch between wireframe or shaded in each QGLWidget instance.
I've also tried the QGLContext route. By default each QGLWidget
creates a new context anyways, but when trying to assign new ones or
sharing a single Context between all of them I would just get issues
with my shaders not linking (I believe the initializeGL slot is not
getting called in that case), leading to a crash every time a context is shared to another QGLWidget:
ASSERT: "QOpenGLFunctions::isInitialized(d_ptr)" in file
c:\work\build\qt5_workdir\w\s\qtbase\include\qtgui../../src/gui/opengl/qopenglfunctions.h,
line 2018
Details:
Currently, my application takes on the following hierarchy:
Application
Window
ViewportWidget [dynamic array]
QGLWidget (custom variation)
The only thing each QGLWidget needs to share is the pointer to the current "map", so that each can render the map based on whatever settings are set within that particular widget's instance.
I perform the following functions for setting up a viewport:
I create a new ViewportWidget, parent it and add it to the appropriate frame and Layout. If the viewport isn't the first one, then it also passes the very first QGLWidget to be used as a "sharedwidget"
The viewport then creates a QGLFormat with a swap interval of 1, and passes said format into the constructor of a new QGLWidget.
I then am forced to call "makeCurrent()" for the viewport, otherwise I crash with the reason:
ASSERT: "false" in file qgl.cpp, line 122
Is it even possible to have separate QGLWidgets with different "polygonMode"'s, or "clearColor"'s? I'm just worried that I'm doing something wrong that will bite me in the butt later on, which I want to avoid.
I have a D3D11 application for Windows Store that currently does not use antialiasing, and I would like to enable it.
According to MSDN, MSAA is disabled in Windows Store apps:
DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
Use this flag to specify the flip presentation model and to specify that DXGI persist the contents of the back buffer after you call IDXGISwapChain1::Present1. This flag cannot be used with multisampling.
Note Windows Store apps must use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL.
Because this limitation applies only to the back buffer, it sounds like the best workaround is to create a new off-screen render target, enable MSAA on that, render everything to it, and immediately before Present, blit the off-screen target to the back buffer (probably with ResolveSubresource).
I've started implementing this, but I'm not sure how to modify my swap chain and render target view. Can anybody advise me as to the correct order of operations here?
What you suggest is correct. Create an MSAA render target using CreateTexture2D followed by CreateRenderTargetView. The DXGI_SAMPLE_DESC field of D3D11_TEXTURE2D_DESC contains the MSAA settings. Then render your scene to this render target, and ResolveSubresource into the back buffer. The swap chain doesn't need to change.
I am currently creating a D2DFactory with
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &Direct2DFactory);
and using Direct2DFactory to create a render target on my main window using:
Direct2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(WindowHandle, size),
&RenderTarget
);
I am attempting to draw from multiple classes to multiple parts of this window, at the minute, i am holding a list of all of these classes and calling OnRender for every one of them and passing RenderTarget as a param.
Is there a better way to do this? Can i create more than one render target, and then render those render targets with my main RenderTarget?
What's your current problem? boring of passing so many render target parameters? I think this is cheap than creating multiple render targets, just like the D3DDevice object in Direct3D apps, nearly every render-able class need a device object to manage resource and do rendering work, the most common way is to pass a device parameter to each class instead of creating multiple devices. so i think you are on the way.