I am porting an app to Qt and have troubles with the integration of the Syphon framework (http://syphon.v002.info/) which is used for video streams sharing between applications via the GPU (Mac OS X only).
Two C++ implementations for Syphon are available, one for Cinder (github/astellato/Cinder-Syphon) and one for openFrameworks (github/astellato/ofxSyphon). I started from the Cinder implementation (both are quite similar) and tried to port it to Qt but I can't find a way to create a QOpenGlTexture using an already created texture.
Here is the code from Cinder-Syphon that I'm trying to get to work (file syphonClient.mm) :
void syphonClient::bind()
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
if(bSetup)
{
[(SyphonNameboundClient*)mClient lockClient];
SyphonClient *client = [(SyphonNameboundClient*)mClient client];
latestImage = [client newFrameImageForContext:CGLGetCurrentContext()];
NSSize texSize = [(SyphonImage*)latestImage textureSize];
GLuint m_id = [(SyphonImage*)latestImage textureName];
mTex = ci::gl::Texture::create(GL_TEXTURE_RECTANGLE_ARB, m_id,
texSize.width, texSize.height, true);
mTex->setFlipped();
mTex->bind();
}
else
std::cout<<"syphonClient is not setup, or is not properly connected to server. Cannot bind.\n";
[pool drain];
}
In the Qt version I'm writing, mTex is of type QOpenGlTexture but I couldn't find an equivalent of mTex = ci::gl::Texture::create(GL_TEXTURE_RECTANGLE_ARB, m_id, texSize.width, texSize.height, true);, that is, creating a QOpenGLTexture using an existing texture ID without having to allocate storage again.
Did I miss something in the Qt OpenGL API or is it just not possible ? If not I guess I will have to use direct OpenGL calls or pulling in the whole Cinder OpenGL API ?
Related
Lately I've been trying to build a Qt/QML WebAssembly using Qt 5.15.2 ( compiled with -opengl es2 ) and Emscripten 1.39.7 with C++ on Windows.
This works as intended. I'm using a simple QML ApplicationWindow and it displays
just fine. From this point I assume that if QML is able to draw something, there must be
a context associated with it.
The next step for me is to grab the said context used by Qt to draw the QML Scene Graph.
I then proceed to create a simple QQuickItem of my own to test that.
From what I gathered from examples all around, I set this item handleWindowChanged signal to connect the beforeSynchronizing of the new Window in charge of the said item.
Long story short, I think I am now able to query whatever is at hand for the current context, the first thing I try is to get the the current display, context and surface like so :
EGLDisplay display = eglGetCurrentDisplay();
EGLContext context = eglGetCurrentContext();
EGLSurface surface = eglGetCurrentSurface (EGL_DRAW);
But to no avail, they all return null pointers.
I do not know much about Qt WebGL implementation, so at this point I think that perhaps I must query those by other means.
QOpenGLContext
I stumble upon QOpenGLContext which I found can be retrieved from my Window ( QQuickWindow ) for instance, so in my custom item :
QOpenGLContext* qtcontext = this->window()->openglContext();
qDebug() << "openGlModuleType : " << qtcontext->openGLModuleType();
qDebug() << "openGlModuleHandle : " << qtcontext->openGLModuleHandle();
qDebug() << "shared context openGlModuleHandle ? : " << qtcontext->shareContext()->openGLModuleHandle();
Output :
openGlModuleType : 1
openGlModuleHandle : 0x0
shared context openGlModuleHandle ? : 0x0
From this I gather that the context is of type QOpenGLContext::LibGLES ( OpenGL ES 2.0 or higher ) but can't retrieve the handle apparently either way ( own or shared one ).
QEGLNativeContext
Then, I tried another shot at QEGLNativeContext which can be made available by using the
private header ( QtPlatformHeaders/QEGLNativeContext ), only to find that nativeHandle was also returning a null value :
if(qtcontext->nativeHandle() == QVariant()) qDebug() << "null native handle";
QEGLNativeContext qteglcontext = qvariant_cast<QEGLNativeContext>(qtcontext->nativeHandle());
Emscripten
I found out that I can query the WebGL current context by Emscripten too :
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE emcontext = emscripten_webgl_get_current_context();
This seems to return a non-null value, yet I found no way to retrieve the context, display or surface handle from this.
My goal here is to retrieve a valid EGLDisplay, EGLContext and EGLSurface.
I am not really familiar with WebGL, OpenGL ES, so I think I must be doing wrong calls or simply missing the point here.
Can someone share their experience on this matter ? Or point me in the right direction ?
That'd be nice.
Have a good day all.
I am writing a C++ program using SDL 2 for the platform layer and opengl for graphics and rendering. I have a full working prototype with keyboard and mouse input. Now I am now trying to use SDL's game controller API to connect a gamepad (to replace or supplement keyboard controls). Unfortunately the controller does not seem to be recognized despite the fact that it works perfectly with other software. It's a Sony Dualshock 4 (for the Playstation 4 system). My system is Mac OS 10.9.5, and I am using SDL 2.0.5 with the official community controller database for SDL 2.0.5, which contains ps4 controller mappings:
030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X
I also added a new mapping using one of the official tools. That also loads successfully according to the relevant function call.
The following is my code, and it's about as close to a minimal example as I can get:
// in main
// window and graphics context initialization here
// initialize SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0) {
fprintf(stderr, "%s\n", "SDL could not initialize");
return EXIT_FAILURE;
}
// load controller mappings, I tested this and 35 mappings load successfully, which is expected
SDL_GameControllerAddMappingsFromFile("./mapping/gamecontrollerdb_205.txt");
// the controller handle
SDL_GameController* controller = nullptr;
// max_joysticks is 1, which means that the device connects at least
int max_joysticks = SDL_NumJoysticks();
if (max_joysticks < 1) {
return EXIT_FAILURE;
}
// this returns, which means that the joystick exists, but it isn't recognized as a game controller.
if (!SDL_IsGameController(0)) {
return EXIT_FAILURE;
}
// I never get passed this.
controller = SDL_GameControllerOpen(0);
fprintf(stdout, "CONTROLLER: %s\n", SDL_GameControllerName(controller));
Has anyone encountered this problem? I've done some preliminary searching as I mentioned, but it seems that usually either the number of joysticks is 0, or everything is recognized.
Also, SDL_CONTROLLERDEVICEADDED isn't firing when I connect the controller.
The controller is connected via USB before I start the program. Also, this is one of the new controllers, and I'm not sure whether the mappings work with that new one. I assume so considering that there are two distinct entries.
Thank you.
EDIT:
I double checked and the PS4 controller works fine as a joystick, but it isn't recognized as a controller, which means that the mapping is incorrect or non-existent. This may be because my controller is "version 2" of the dualshock 4, and I'm not sure whether a 2.0.5-compatible mapping was added. hmmm
The controller was recognized as a joystick but not as a controller, meaning that none of the available mappings I could find (in 2.0.5 controller mapping format) corresponded with the controller. Updating from SDL 2.0.5 to 2.0.8 also updated available mappings it seems, and now the controller is recognized as a game controller.
Note: normally it is a terrible idea to upgrade tools mid-project, but in this case it was safe to do.
I have found several unofficial sources for how to get OpenCL to play nice with OpenGL and Qt5, each with different levels of complexity:
https://github.com/smistad/Qt-OpenGL-OpenCL-Interoperability
https://github.com/petoknm/QtOpenCLGLInterop
http://www.krazer.com/?p=109
Having these examples is nice, however they don't answer the following question: What exact steps are the minimum required to have a Qt5 widgets program display the result of a calculation made in OpenCL kernel and then transferred directly to attached OpenGL context initiated by Qt5?
Sub-questions include:
What is the correct way to expose the OpenGL context in Qt5 to OpenCL?
How do I initiate my Qt5 app in the first place to make sure that the OpenGL context is correctly set up for use with OpenCL?
How should OpenCL be initiated to be compatible with the OpenGL context in Qt5?
What quirks must I look out for to have this working across the platforms that Qt5 supports?
Is there such a thing as an "official" way to do this, or is Digia working on one?
Note, I am primarily interested in using OpenGL as a widget as opposed to a window/full-screen.
I have used Qt5 and OpenCL together on mac, linux and windows with the following strategy:
Create a QGLWidget and GL context (this example creates two GL context, one for Qt/visualization in the QGLwidget and one for OpenCL called mainGLContext, useful when doing multi-threading. These two context will be able to share data.)
QGLWidget* widget = new QGLWidget;
QGLContext* mainGLContext = new QGLContext(QGLFormat::defaultFormat(), widget);
mainGLContext->create();
Create an OpenCL context using the OpenGL context. This is platform specific. For linux you use glx, for windows wgl, and mac cgl sharegroups. Below is the function I use to create the context properties for interoperability. The display variable is used on linux and windows and you can get it with glXGetCurrentDisplay() and wglGetCurrentDC().
cl_context_properties* createInteropContextProperties(
const cl::Platform &platform,
cl_context_properties OpenGLContext,
cl_context_properties display) {
#if defined(__APPLE__) || defined(__MACOSX)
CGLSetCurrentContext((CGLContextObj)OpenGLContext);
CGLShareGroupObj shareGroup = CGLGetShareGroup((CGLContextObj)OpenGLContext);
if(shareGroup == NULL)
throw Exception("Not able to get sharegroup");
cl_context_properties * cps = new cl_context_properties[3];
cps[0] = CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE;
cps[1] = (cl_context_properties)shareGroup;
cps[2] = 0;
#else
#ifdef _WIN32
// Windows
cl_context_properties * cps = new cl_context_properties[7];
cps[0] = CL_GL_CONTEXT_KHR;
cps[1] = OpenGLContext;
cps[2] = CL_WGL_HDC_KHR;
cps[3] = display;
cps[4] = CL_CONTEXT_PLATFORM;
cps[5] = (cl_context_properties) (platform)();
cps[6] = 0;
#else
cl_context_properties * cps = new cl_context_properties[7];
cps[0] = CL_GL_CONTEXT_KHR;
cps[1] = OpenGLContext;
cps[2] = CL_GLX_DISPLAY_KHR;
cps[3] = display;
cps[4] = CL_CONTEXT_PLATFORM;
cps[5] = (cl_context_properties) (platform)();
cps[6] = 0;
#endif
#endif
return cps;
}
Often you want to do multi-threading, having one thread do the Qt event handling, while doing some OpenCL processing in another thread. Remember to make the GL context "current" in each thread. Use the makeCurrent and moveToThread function on the QGLContext object for this. You can find details on how I have done this here: https://github.com/smistad/FAST/blob/master/source/FAST/Visualization/Window.cpp
I don't know of a Qt OpenCL wrapper to create the OpenCL context.
After working on this some more I felt compelled to add some more info. Erik Smistad's answer is correct and will remain accepted, however it is only one of several ways to do this.
Based on this article there are at least 3 ways we can have OpenGL vs. OpenCL interop:
Sharing an OpenGL texture directly with OpenCL. PROS: Fastest route since everything is zero-copy. CONS: Severely limits available supported data formats.
Sharing an OpenGL PBO with OpenCL and copy from this into a Texture. Second quickest, but will force the need for a memory copy.
Mapping output buffer in OpenCL to host memory and uploading texture from there. Slowest when using OpenCL on GPU. Fastest when using OpenCL on CPU. Forces data to be copied to host memory and back. Most flexible with respect to available data formats.
I am creating an application using SDL2 & OpenGL, and it worked fine on 3 different computers. But on another computer (an updated arch linux), it doesn't, and it crashes with this error:
OpenGL context already created
So my question is: How do I check if the OpenGL context has already been created? And then, if it is already created, how do I get a handle for it?
If I can't do this, how do I bypass this issue?
SDL2 does not in fact create an OpenGL context without you asking to make one. However, if you ask it to create an OpenGL context when OpenGL doesn't work at all, SDL2 likes to, erm, freestyle a bit. (The actual reason is that it does a bad job in error checking, so if X fails to create an OpenGL context, it assumes that it's because a context was already created)
So, to answer the third question ("how do I bypass this issue"), you have to fix OpenGL before attempting to use it. Figures, right?
To answer the first and second, well, no API call that I know of... but you can do it a slightly different way:
SDL_Window* window = NULL;
SDL_GLContext* context = NULL; // NOTE: This is a pointer!
...
int main(int argc, char** argv) {
// Stuff here, initialize 'window'
*context = SDL_GL_CreateContext(window);
// More stuff here
if (context) {
// context is initialized!! yay!
}
return 2; // Just to confuse people a bit =P
}
I'm creating (for learning purposes) a 2D game in embarcadero's Firemonkey XE2 platform. It's a game similar to space invanders in terms of visual representation of the game field but vastly more complex.
I'm trying to spawn animated enemies. I created a template TImage and assigned it a BitmapListAnimation with six pictures. Then i just clone it using Clone(). Here is the cloning code:
virtual void CreateMe(TForm* pForm);
void CBaseEnemy::CreateMe(TForm *pForm)
{
TImage* pSource = dynamic_cast<TImage*>(this->pToClone);
pSource->Visible = true;
TImage* pDest = dynamic_cast<TImage*>(pSource->Clone(pForm));
pDest->Parent = pForm;
TBitmapListAnimation* pAnimSource = dynamic_cast<TBitmapListAnimation*>(pForm->FindComponent("BitmapListAnimation1"));
pAnimSource->Parent = pDest;
pAnimSource->Start();
TBitmapListAnimation* pAnimDest = dynamic_cast<TBitmapListAnimation*>(pAnimSource->Clone(pForm));
}
And it works, It's just extremely complicated, could you suggest a way to clone an entire template (A Component such as TImage along with all it's children) ?
An Embarcadero blog entry just released covers this: blog