Creating cross platform OpenGL off-screen context - c++

I have a task to implement offscreen OpenGL renderer both for Window and Linux in C++.I have such a version already written in Java using LWJGL lib.There I used PBuffer object ,which under hood creates Pbuffers based on the used OS.First I thought to re-implement the full PBuffer creation logic just as it i done in native source of LWJGL.Then I read this post on StackOverflow.com where it is suggested using the standard context creation ,let's say using GLFW (which is cross platform) but not to create the actual window.Is it the right way to go? What are pros and cons vs using Pbuffer in such a case?
Update:
I just want to emphasize that I use FBOs to render the frames so my problem here is not how to render in offscreen mode but how to create a context without window both in Windows and Linux OSs.

I'd highly recommend not to use PBuffers anymore but to use Frame Buffer Objects (FBOs) instead. FBOs offer much better performance as using them does not require a context switch and they have several other advantages.
LWJGL supports FBOs, but GLFW is "just" for cross-platform setup of OpenGL and not for rendering. For convenient cross-platform FBO usage I'd recommend to use a library like OGLplus on top of GLFW. See here for a render-to-texture example.

The Simple DirectMedia Layer (SDL) library is worth a try. It simplifies cross-platform OpenGL context creation, with the ability to use memory surfaces for off-screen rendering.
The only thing you would have to do extra, is to include your OpenGL and SDL headers from different locations, depending on your platform. This can be done with simple pre-processor directives.

As far as I know there is no cross-platform way to create contexts, you will have to create your own abstraction and then implement it for each platform.
On windows I have used the following code to create a second context to do loading of content in a background thread (this program used GLFW but that should not matter):
void Program::someFunction()
{
HDC hdc = wglGetCurrentDC();
HGLRC hglrc = wglGetCurrentContext();
HGLRC hglrc_new = wglCreateContext(hdc);
wglShareLists(hglrc, hglrc_new);
loadThread = boost::thread(&Program::loadFunc, this, hdc, hglrc_new);
}
/**
* Imports all our assets. Supposed to run in its own thread with its own OpenGL context.
* #param hdc The current device context.
* #param hglrc A OpenGL context thats shares its display list with the main rendering context.
*/
void Program::loadFunc(HDC hdc, HGLRC hglrc)
{
wglMakeCurrent(hdc, hglrc);
//Do stuff...
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
}

Related

How to Get Unity Context into OpenGL Window

I want to get the unity context into opengl so I can display a unity render texture in an opengl glfw window. I tried using
oldContext = glfwGetCurrentContext(); but the value of oldContext is just null.
I am trying to use the low-level native unity plugin and Texture.GetNativeTexturePtr
Any help would be greatly appreciated!
OpenGL context cannot be queried like OpenGL state related objects via some glGet* API.Context is not part of OpenGL API,it is a part of the system you're running on and it exists to allow you maintaining of OpenGL state and issue command to the driver. You must access a system specific handle that points to the context via system specific API.On Windows (WinGDI)that's would be
HGLRC wglGetCurrentContext();
On linux see related GLX API. You need to find functions to access GLXContext
I did it once in Unity3D (framebuffer readout plugin). But it used Unity's OpenGL or DirectX context to issue API commands only.
Also,I am not sure you can 'inject' or share a context for a window that doesn't own that context. You see, when you (or Unity) init display it creates context and related GL resources,like the default FBO with all required attachments on its own,and that FBO is mapped to some system resource(device) which takes actually care of presenting those pixels on the screen. Therefore, I am not sure display context can be moved from Window to Window in the same manner that a context can be shared between threads.(But I can be wrong on this one)
You can create your plugin Window on some thread,with its own GL context. Then create and share a texture object between those two. Remember, GL textures are shareable. If you copy contents from Unity's screen FBO into that texture,then you can copy it into your plugin's screen FBO from that texture as well.
Btw,look at this SO question .You can see there vendor specific GL extensions which allow copying data into texture from different contexts without requiring shared context,share lists setup.
Regarding why GLFW returns you nullptr. In your example you use GLFW library.
glfwGetCurrentContext()
But if you look at the source code,you see this:
GLFWAPI GLFWwindow* glfwGetCurrentContext(void)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return _glfwPlatformGetTls(&_glfw.contextSlot);
}
Which probably means that it retrieves a pointer to GLFWWindow from its own cache and not from the system.And if you didn't create that context via GLFW,you won't get any valid pointer. So try working directly with your system related API as explained above.

OpenGL get Device Context

I am trying to create an OpenGL application on windows. As far as I can understand, one of the first things I must acquire is a Device Context, which must be passed on to a couple of functions that choose and set a pixel format and create a rendering context. I used the OpenGL wiki to get a rough idea about what I should do.
My code is something like:
#include <iostream>
#include <windef.h>
#include <wingdi.h>
HDC hdc;
int main() {
hdc = wglGetCurrentDC();
std::cout << "HDC: " << hdc << std::endl;
return 0;
}
This prints
HDC: 0
I assumed a Device Context refers to a physical device, but I read somewhere that it refers to any drawable "surface". In both cases is my question: how can I obtain a non-null DC? Or should I perform a completely different set of steps in order to set up this whole OpenGL system?
I found a lot of tutorials online, but they all use GLUT, GLEW, GLFW, X11, SDL etc. which are libraries. Libraries make certain things easier, but they usually do not perform tasks that are impossible without using them. This time, I want to try to do things the hard way and therefore use no libraries, just plain OpenGL.
I found, at last, a tutorial that only used the windows libraries for creating a window.
You did not state your OS but I assume Windows from the function names. The problem is exactly as Reto Koradi stated in the comment. To set up OpenGL you need to do this:
Obtain OS handle to object with valid device context
It can be OS window or OS bitmap. If you have just console app then you need to create a valid OS window first and use its handle (to my knowledge console does not have Canvas).
you can use GLUT for the window creation or If your compiler IDE has an window App you can use that. You can also combine OpenGL and Window components. VCL is also not a problem (I am using it for years with OpenGL)
In windows you can use CreateWindowEx so google an example for it...
Anyway you should have your handle in a variable like:
HWND hwin=NULL;
If you have no experience with windows applications then use GLUT for this. Otherwise you would need to learn a lot of stuff just to cover window creation, message handling of events and user/app interaction which can be really overwhelming for a rookie without guide.
Get Device context for that handle
HDC hdc = GetDC(hwin);
Set pixel format you need of device context
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory( &pfd, sizeof( pfd ) ); // set the pixel format for the DC
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
Create OpenGL rendering context for device context
HGLRC hrc = wglCreateContext(hdc);
Set it as default OpenGL context
wglMakeCurrent(hdc, hrc);
This is absolute minimum without any error checking , additional buffers etc. For more info and actual code see related QA's:
How to render an openGL frame in C++ builder? for oldstyle GL
simple complete GL+VAO/VBO+GLSL+shaders example in C++ with the new stuff
You can use GLUT for all of this. This is first hit I found by quick search:
How can I set an OpenGL display (Window created by OpenGL) to maximized?
Or follow OpenGL tutorials there are tons of them out there ...

OpenVR. Rendering in OpenGL without SDL

Is this possible in nature? What role of SDL library in OpenVR API? Does it needed for OpenGL context or only for mirroring the stereo image to SDL window?
It is possible. If you don't use SDL, you'll have to create your rendering context and window by yourself. The whole code would be too long for this answer, but on Windows you could use functions like CreateWindowEx and wglCreateContext. OpenVR doesn't require anything different from a normal context setup, but you need to use a somewhat modern version of OpenGL (4.1 at least works for me).

How can I draw inside two separate 3D windows within the same application on Windows using OpenGL?

I am implementing a plug-in inside a 3rd party program in C++ on Windows.
The 3rd party program has a window that displays 3D graphics using OpenGL.
However I need the plug-in to create another window that also displays 3D graphics using OpenGL.
Do I need to create a new OpenGL rendering context for my window or is there some way that I can "reuse" the OpenGL rendering context used by the 3rd party program?
I assumed that I had to create a new OpenGL rendering context and tried the following:
// create a rendering context
hglrc = wglCreateContext (hdc);
// make it the calling thread's current rendering context
wglMakeCurrent (hdc, hglrc);
However the last function failed.
Reading the documentation of wglMakeCurrent I notice that
A thread can have one current rendering context. A process can have multiple rendering contexts by means of multithreading.
Does this mean that my window need to run in a separate thread from the 3rd party program?
You didn't post error code generated by wglMakeCurrent(), so I won't be guessing the reason. It's not the binding itself, however. Sentence 'A thread can have one current rendering context' means, that new context will 'replace' the old one and become the current. I don't know why are you trying to set two contexts as current (or run another thread), but it's not the way to go. Avoid multithreading in rendering unless it's absolutely necessary.
So, answering your question:
Yes, you CAN 'reuse' OpenGL rendering context.
Why, you may ask? Rendering context is created for specific device context (HDC), which is exclusive property of each window (HWND)! How is this possible, then?!
Well, it seems somehow impossible because of function prototypes:
HWND my_window = CreateWindow(...);
HDC my_dc = GetDC(my_new_window);
//Setup pixel format for 'my_dc'...
HGLRC my_rc = wglCreateContext(my_dc);
wglMakeCurrent(my_dc, my_rc);
This really lets you think that rendering context is bound to this specific device context and valid only for it. But it's not.
The critical part is the comment (setup pixel format). Rendering context is created for specific CLASS of DCs, to be more precise: for DCs with the same pixel format. So code below is perfectly valid:
//window_1 = main window, window_2 = your window
HDC dc_1 = GetDC(window_1);
Set_pixel_format_for_dc_1(); //Usual stuff
HGLRC rc = wglCreateContext(dc_1);
wglMakeCurrent(dc_1, rc);
ultra_super_draw();
//.....
HDC dc_2 = GetDC(window_2);
//Get dc_1's PF to make sure it's compatible with rc.
int pf_index = GetPixelFormat(dc_1);
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
DescribePixelFormat(dc_1, pf_index, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
SetPixelFormat(dc_2, pf_index, &pfd);
wglMakeCurrent(dc_2, rc);
another_awesome_render();
wglMakeCurrent(NULL, NULL);
If you are still not convinced, MSDN:
wglMakeCurrent(hdc, hglrc): The hdc parameter must refer to a drawing surface supported by OpenGL. It need not be the same hdc that was passed to wglCreateContext when hglrc was created, but it must be on the same device and have the same pixel format.
I guess you are already familiar with these calls. Now, I don't know what are the conditions that your rendering must meet, but without additional requirements, I don't see any difficulties from this point:
HDC my_dc = Create_my_DC();
//...
void my_new_render
{
//Probably you want to save current binding:
HDC current_dc = wglGetCurrentDC();
HGLRC current_context = wglGetCurrentContext();
wglMakeCurrent(my_dc, current_context);
MyUltraSuperRender(...);
wglMakeCurrent(current_dc, current_context);
}
Hope this helps :)
First things first, you actually should create a separate OpenGL context for your plugin, for the simple reason that it gives you a separate state space that doesn't interfere with the main programs OpenGL context.
You misunderstood the part about multiple rendering contexts though. It's perfectly possible to have an arbitrary number of OpenGL contexts for a process. But each thread of the process can bind only one context at a time. That one binding also includes the window DC the context is bound to. It is however perfectly legal change a context binding at any time. Either you change the window a given context is bound to, or you switch the context or you do both at the same time.
So in your situation I suggest you create a custom context for your plug-in, that you use for all the windows your plug-in creates.
That your simple context "creation" code fails has one simple reason: Your window will most likely not have a pixel format descriptor set.
I suggest you use the following method to create your new windows and contexts:
/* first get hold of the HDC/HRC of the parent */
HDC parentDC = wglGetCurrentDC();
HRC parentRC = wglGetCurrentContext();
int pixelformatID = GetPixelFormat(parentDC);
/* we use the same PFD as the parent */
PIXELFORMATDESCRIPTOR pixelformat;
memset(pixelformat, 0, sizeof(pixelformat);
DescribePixelFormat(parentDC, pixelformatID, sizeof(pixelformat), &pixelformat);
/* create a window and set it's pixelformat to the parent one's */
HWND myWND = create_my_window();
HDC myDC = GetDC(myWND);
SetPixelFormat(myDC, pixelformatID, &pixelformat);
/* finally we can create a rendering context
* it doesn't matter if we create it against
* the parent or our own DC.
*/
HRC myRC = wglCreateContext(myDC);
/* we're done here... */
Now whenever your plugin wants to render something it should bind its own context, do its thing and bind the context that was bound before:
HDC prevDC = wglGetCurrentDC();
HRC prevRC = wglGetCurrentContext();
wglMakeCurrent(myDC, myRC);
/* do OpenGL stuff */
wglMakeCurrent(prevDC, prevRC);

OpenGL in my HWND

Right now, I'm trying to port a Direct3D renderer from my engine. I'm and OpenGL begginer so i dont have much knowlegde about OpenGL, as now i can create windows and do my render via glut, but i can't use glut for my project, because the HWND is created in my code and then sent to the renderer DLL
// Where pWindow is already a valid HWND target of the renderer
//(Currently Direct3D9 and Direct3D10
pRenderer = pCreateGraphics(800, 600, false, pWindow);
My question is: Is there library similar to GLUT that has a similar behaviour as GLUT, but allowing my to use my own window handle?
Note: I prefer using a lib instead of reinventing the wheel, but i will do if there is not a library that can help me
Well you can have a look at GLFW's source code or NeHe tutorials and grab the OpenGL init code.
SDL lets you get the window handle.
I don't know if the used pixel format can be assumed to be DirectX compatible though.