Limitations of using GLFW and OpenGL for GUIs - qt

I would like to know what kind of limitations can result from using GLFW and OpenGL instead of using a traditional GUI toolkit like Qt or GTK.
Of course, I know that GLFW with OpenGL don't expose the same level of functionality, but if only a few kind of widgets are needed, I think that those could be easily implemented.
The question is, is there some feature that couldn't be implemented on top of GLFW/OpenGL in contrast to Qt or GTK?
For example, I'm worried about drawing menus outside the window region (I guess that an auxiliary non-decorated window could be use in this case).

I know that GLFW with OpenGL don't expose the same level of functionality, but if only a few kind of widgets are needed
When it comes to OpenGL, there isn't any limit per se. You can draw wherever and whatever you want. The area where you can draw is a limiting factor from the operating system's side of things.
Remember that some "simple" functionality like say a textbox, is already complicated. Not only do you have to handle rendering (and scalable text isn't always fun), but you also have to handle keyboard events. Drawing the cursor and text selection, etc.
For example, I'm worried about drawing menus outside the window region (I guess that an auxiliary non-decorated window could be use in this case).
When it comes to drawing outside the window region, this isn't directly OpenGL related. It's more a question depending on the OS.
For instance using the WinAPI, you can draw anywhere on the screen simply by doing:
#include <Windows.h>
int main(int argc, char **argv)
{
HWND desktop = GetDesktopWindow();
HDC dc = GetDC(desktop);
RECT rect = { 20, 20, 200, 200 };
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
FillRect(dc, &rect, brush);
return 0;
}
Note that the rectangle will disappear immediately when the screen redraws that area.
When you already have a window, then you can use SetWindowRgn() to change the area which your application is allowed to draw within. Note that you can't just change this area, and everything will be fine and dandy.
The question is, is there some feature that couldn't be implemented on top of GLFW/OpenGL in contrast to Qt or GTK?
Bottom line is no. There's isn't any feature you can't implement with OpenGL that is in Qt and GTK. The point is that it isn't just OpenGL, and that a lot of it depends on the operating system, thus needing OS specific code.

Related

qt - how to develop application independent of screen resolution? [duplicate]

I'm writing a Qt application that needs to run on high-dpi Windows (192dpi instead of 96dpi).
Unfortunately the Qt framework does not have support for high-dpi yet (at least on Windows), so my application and all its elements looks half the size it should.
Is there any way to force/simulate automatic upscaling of such apps by Windows?
Applications that use fixed coordinates and sizes will look small on high-DPI resolutions. Although even if using layouts there are some issues regarding element and font sizes and margins. Fortunately there is support for high-DPI displays since Qt 5.4 as there has been many high-DPI issue fixes.
An application on Windows can assume one of the following levels of "DPI Awareness" (From the Qt documentation) :
DPI Unaware: This level has been introduced in Windows-Vista. Windows will pretend to the application that it is running on a
standard display of 96 DPI of 1920x1080 and scale the application
accordingly. It is intended to accommodate older applications designed
for low DPI displays. Some artifacts may result from this type of
scaling.
System-DPI Aware: This level has been introduced in Windows-Vista. It differs from Per-Monitor DPI Aware only when multiple monitors are
connected. Windows will calculate a scaling suitable for all connected
monitors.
Per-Monitor DPI Aware: This level has been introduced in Windows 8.1. Windows does not perform any scaling at all.
Also it states that :
Qt applications by default are Per-Monitor DPI Aware on Windows 8.1 or
System-DPI Aware on older versions of Windows. As of Qt 5.4, the level
can be specified by passing a parameter to the platform plugin (see
Using qt.conf):
<application> -platform windows:dpiawareness=0,1,2
You can read more information here.
In general to have a good UI on high-DPI displays, consider the following :
Use the latest version of Qt
Use layouts and avoid fixed sizes (unless you calculate scaling ratios on your own)
Make appropriate DPI-related settings depending on your application needs, for example set Qt::AA_UseHighDpiPixmaps attribute if you work with QPainter and pixmaps, or calculate a scaling ratio for adjusting certain element sizes in special situations.
Qt fully supports high DPI monitors from Qt 5.6 onward, via attribute or environment variable (except on OS X where support is native). For the attribute method, use:
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // DPI support
QApplication app(argc, argv);
return app.exec();
}
or set the system environment variable:
QT_AUTO_SCREEN_SCALE_FACTOR=1
I've tested both methods on windows 10 with a high-DPI surfacebook monitor and the results are scaled properly as expected.
Here is the quickest way to get the issue solved on Windows.
Next to the executable, create a qt.conf file (if not already there) and add the following:
[Platforms]
WindowsArguments = dpiawareness=0
The window will appear blurry when scaled up. The big advantage of this solution is that Windows does the scaling, not Qt. Therefore the occurence of artifacts is minimized. Furthermore, this can apply to an already-distributed app as it does not require a rebuild.
Of course, this is not the most pleasant result but the quickest to get you out of trouble in short term, letting you develop the "real" DPI-aware version without pressure.
I' am using Qt 4.8. First, you should use layouts. My goal was to prevent user to resize dialogs, forms etc. too.
I achieved correct display results on different DPI by put this code in dialog constructor:
adjustSize();
setFixedSize(size());
First line adjustSize() adjust size of dialog to fit its content.
Second line setFixedSize(size()) fixes size of dialog after adjusting its size to content and prevent users to resize it. You haven't to set size policies.
I' am using Qt 4.8.7. This is my code.
static float dpiX = (float)0;
inline int autoDpiSize(int inSize)
{
if (dpiX == (float)0)
{
HDC screen = GetDC(0);
dpiX = static_cast<float >(GetDeviceCaps(screen, LOGPIXELSX)) / 96.0;
ReleaseDC(0, screen);
}
return dpiX * inSize;
}
someWidget->setFixedHeight(autoDpiSize(30));

How can I hunt down these OpenGL calls that are distorting objects in my scene?

I'm mixing two libraries that use OpenGL: Qt and OpenSceneGraph. I'm targeting OpenGL ES 2, so everything is done with shaders and ES 2 compatible calls.
I'm specifically using OSG with QtDeclarative by trying to paint OSG onto a QDeclarativeItem. I do this the way suggested in Qt documentation: wrap all OpenGL calls between beginNativePainting()/endNativePainting().
This works fine until I use textures in my OpenSceneGraph scene. When I do this, my QML window gets "messed up" for lack of a better word. To keep it as simple as possible, my OSG scene consists of a plane with a texture applied to it. I recreated the scene using basic OpenGL calls and the problem no longer occurs. Here's the problem summarized as a bunch of pictures:
The QtDeclarative engine uses OpenGL to paint stuff. I set up a simple QML page:
I create a simple scene using OpenGL directly. It's a plane with a texture painted onto it.
Now I try to set up the same scene in OSG... identical shaders, etc.
You can see something odd is going on with the last screenshot. Don't worry about the black background where the original OpenGL scene was transparent, that's just OSG using a black clear color. The problem is that the other items set up with QML (the rectangles) get messed up.
Edit: To clarify what happens: The rectangles I draw with QML are all stretched out to the right edge of the screen. I also noticed if I draw rectangles after the OpenSceneGraph item in QML, they don't show up (I didn't notice this before). I draw the purpley black rectangle after the OSG item in the following screenshots... note that it disappears. There might be more weird stuff happening, but this is all I've observed playing with rectangles.
Before
After
I'm fairly new to OpenGL so I don't know what kind of call/state setting would cause something like this to happen. I think that OpenSceneGraph makes some OpenGL state change that's messing up Qt's paint engine. I also know that this only occurs when OSG uses textures... if I don't apply textures in my OSG scene, this doesn't happen. This is where I'm stuck.
Also, I tried to use BuGLe to get an OpenGL call trace with and without textures enabled in OSG to see if I could figure out the problematic state change(s). I found a few differences, and even some global state that OSG changed (such as glPixelStorei()) between the two, but resetting the changes I found made no difference. It would help a lot if I knew what to look for. If anyone's feeling insane, I also have the stack traces:
OSG with texturing: http://pastie.org/4223182 (osg texture stuff is lines 637~650)
OSG without texturing: http://pastie.org/4223197
Edit 2:
Here's a diff that might be helpful. You'll need to scroll way down before the relevant lines are apparent.
http://www.mergely.com/nUEePufa/
Edit 3:
Woah! Okay, that diff helped me out quite a bit. OSG enables VertexAttribArray 3 but doesn't disable it. Calling glDisableVertexAttribArray(3) after OSG renders its frame seems to partially solve the problem; there's no more stretching of the QML rectangles. However, rectangles drawn after the OSG item still don't show up.
After obsessing over the trace logs, I think I've found two OpenGL things that need to be reset before passing control back to Qt to cause the issues above to go away. I mentioned one in an edit... I'll summarize both in this answer.
Rectangle/QML Item distortion
QPainter uses Vertex Attributes 3, 4, and 5 directly for something that looks like its related to the geometry of those rectangles. This can be seen in the trace:
[INFO] trace.call: glVertexAttrib3fv(3, 0x2d94a14 -> { 0.00195312, 0, 0 })
[INFO] trace.call: glVertexAttrib3fv(4, 0x2d94a20 -> { 0, -0.00333333, 0 })
[INFO] trace.call: glVertexAttrib3fv(5, 0x2d94a2c -> { 0.2, 0.4, 1 })
Disabling the corresponding vertex attribute arrays fixes the stretchy rectangles issue:
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
Items drawn after the OSG Item don't render
In retrospect, this was one was easy and didn't have anything to do with texturing. I hadn't noticed this before trying to add textures to my scene though, so mixing the two issues was my fault. I also screwed up with the traces and diff I posted; I never updated them to account for the ordering problem after I discovered it (sorry!)
Anyways, QPainter expects depth testing to be turned off. Qt will turn depth testing off when you call beginNativePainting(), and also when it starts to paint its items... but you're expected to turn it back off whenever handing control back:
QPainter paints stuff (DEPTH_TEST = off)
OSG draws stuff (DEPTH_TEST = on)
QPainter paints more stuff [expects DEPTH_TEST = off]
The right trace logs showed that I wasn't doing this... So the fix is
glDisable(GL_DEPTH_TEST)
Maybe you just need to reenable GL_TEXTURE_2D? I notice in your example with textures that OSG enables, and subsequently disables GL_TEXTURE_2D. Thus the difference between your two cases (with texture vs without), is that the one that uses textures finishes with texturing disabled, while the one without texturing leaves GL_TEXTURE_2D in it's initial state.
If Qt needs/expects texturing enabled to draw quads it could cause nothing to show up.

Can't embed OpenGL window into QWidget with XReparentWindow

I'm trying to add better UI for an OpenGL-based program with Qt. Since I can modify that program it's not hard to get the window ID. So I think embedding it into a QWidget would be a good idea. However, it doesn't work like I expected:
After XReparentWindow is called, the OpenGL window lose its decoration, but the position didn't change.
If I use XConfigureWindow to move it to position (0, 0) relative to parent it goes to the top-left corner of the screen, but not the QWidget.
After reparenting, a third window can cover the QWidget, but nothing can cover the OpenGL window.
X11 reported no errors during the whole operation.
It seems the parent of the OpenGL window has been set to the root window instead of my QWidget. What should I do to make it work correctly?
You can replace your current OpenGL window with a QGLWidget which provides an OpenGL context and can be placed into a Qt window directly.
I'm not sure Qt supports XReparentWindow calls like that. The docs don't seem to say it does, so it's probably a bad idea to use it. You could try QWidget::create() instead.

Cut a QGLWidget?

Is it possible to cut a QGLWidget? I mean I would like to cut it so i get access to the gui below. It sounds strange, but it would be a lot of work for me to divide my QGLWidget into two. I hope you understand what I mean.
I don't fully understand what you mean by "cut" and GUI below. In Qt with layout mechanisms properly used a widget consumes the area it covers and there are no widgets beneath it.
Do you want to render multiple views into a single QGLWidget? This is easily achieved by proper use of glViewport + glScissor.
EDIT due to comment
There are two kinds of windows:
Top level (those you can freely move around on the screen)
Child windows (subwindows like widgets or panes in a top level window)
Child windows again come in two characteristics:
logical child
real child
A logical child window just consists of its position, dimension and layer and are managed by the toolkit. From the view of the operating system there's just one top level window. The toolkit is it that manages its internal state to give the impression of independent sibling windows in the toplevel window.
A real child window is manages by the operating/graphics system. Such real child windows may share their graphics context with their parent and sibling. However OpenGL only works well if the window into which a OpenGL context is created has its very own graphics context. Thus any OpenGL child window inevitably will have its very own graphics context and graphics system window object. Most graphics systems out there don't properly support applying shapes onto child windows (only toplevel windows, and then this also conflicts with OpenGL).
So this boils all down that it's virtually impossible, nor advisible to try to "layer" an OpenGL window on top of a sibling. It may work in some circumstances, but most of the time it won't.
That's the bad news.
The good news are, that you simply looked in a slightly wrong direction. I hereby direct your view towards QGraphicsView. QGraphicsView supports OpenGL as a backend, you can also write your own OpenGL renderer code to be executed within a QGraphicsView. But furthermore QGraphicsView can also be used for rendering widgets, also using OpenGL. So all you have to do is putting both your OpenGL rendering code and your widget into a common QGraphicsView scene and are done. And here is a tutorial http://www.crossplatform.ru/node/612 the result of the tutorial looks like this:

Qt & double buffering - are there any neat tricks to capture pixels or manipulate the back buffer?

I'm migrating an application to Qt from MFC.
The MFC app would use GDI calls to construct the window (a graph plot, basically). It would draw to a memory bitmap back buffer, and then BitBlt that to the screen. Qt, however, already does double buffering.
When the user clicks and drags in the graph, I'd like that section of the window to be inverted.
I'd like to find the best way to do this. Is there a way to do something like grabWindow() that will grab from the widget's back buffer, not the screen? ... maybe a BitBlt(..., DST_INVERT) equivalent?
I saw setCompositionMode() in QPainter, but the docs say that only works on painters operating on QImage. (Otherwise I could composite a solid rectangle image onto my widget with a fancy composition mode to get something like the invert effect)
I could do the same thing as MFC, painting to a QImage back buffer... but I read that hardware acceleration may not work this way. It seems like it'd be a waste to reimplement the double buffering already provided to you in Qt. I'm also not so sure what the side effects of turning off the widget's double-buffering may be (to avoid triple-buffering).
At one point, I had a convoluted QPixmap::grabWidget() call with recursion-preventing flags protecting it, but that rendered everything twice and is obviously worse than just drawing to a QImage. (and it's specifically warned against in the docs)
Should I give up and draw everything to a QImage doing it basically like I did in MFC?
EDIT:
Okay, a QPixmap painter runs at approximately the same speed as direct now. So, using a QPixmap back-buffer seems to be the best way to do this.
The solution was not obvious to me, but possibly if I looked at more examples (like Ariya's Monster demo) I would have just coded it the way it was expected to be done and it would have worked just fine.
Here's the difference. I saw help system demos using this:
QPainter painter(this)
in the start of paintEvent(). So, it seemed to naturally follow to me that to double buffer to a QPixmap then paint on the screen, you needed to do this:
QPainter painter(&pixmap);
QPainter painterWidget(this);
... draw using 'painter' ...
painterWidget.drawPixmap(QPoint(0,0), pixmap);
when in fact you are apparently supposed to do this:
QPainter painter;
painter.begin(&pixmap);
... draw using 'painter' ...
painter.end();
painter.begin(this);
painter.drawPixmap(QPoint(0,0), pixmap);
painter.end();
I can see that my way had two active painters at the same time. I'm not entirely sure why it's faster, but intuitively I like the latter one better. It's a single QPainter object, and it's only doing one thing at a time. Maybe someone can explain why the first method is bad? (In terms of broken assumptions in the Qt rendering engine)
Assuming you don't really want to pixel values from your offscreen buffer (but rather, just drawing something again on top of it and blit again to the screen), you should use QPixmap as the buffer, not QImage. Using the latter disables all painting acceleration as Qt falls back using its software raster engine, hence the use QPixmap. If you use OpenGL graphics system, you can still benefit from it.
For an example on how to do this, check my last code on running the Monster demo, it needs to have an offscreen pixmap to due the motion blur effect via repeated painting with source over composition mode.
To disable Qt's backing store (which is generally not a good idea), use the Qt::WA_PaintOnScreen for your top-level widget.
A bit unrelated, but you might want to have a look QRubberBand widget.
Drawing on top of the graph area you should be able to use composition modes to invert. Draw white using the Difference composition mode. The following example is a subclass of a QLabel showing a pixmap:
void Widget::paintEvent(QPaintEvent *pe)
{
// make sure we paint background
QLabel::paintEvent(pe);
// paint the overlay
if (!selectionRect.isNull()) {
QPainter p(this);
p.setCompositionMode(QPainter::CompositionMode_Difference);
p.fillRect(selectionRect,QColor("#FFFFFF"));
}
}
alt text http://chaos.troll.no/~hhartz/yesManInverted.png
The simplist, most straightforward answer I know of is to do it like you were doing before, to a QImage, and use the QImage as the source for your widget on the screen.
Another option might be to add a transparent widget over your graph, which only draws the inverted part of the graph. I don't think this will optimize the drawing at all, however. It will likely cause the underlying graph to be drawn, and then the overlay of the inverted part.

Resources