Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Qt + Allegro working! To a point...

This thread is locked; no one can reply to it. rss feed Print
Qt + Allegro working! To a point...
Max Savenkov
Member #4,613
May 2004
avatar

Introduction

So, I feel strangely compelled to get Allegro and Qt working together lately. It's like there is nothing else I'd rather work on. I think this might be a Finnish secret service's experiment with hypnosis, actually, but I try to enjoy it while it lasts.

Maybe everything I wrote here is already known and was discarded as useless before. Still, I would like to present you with results of my research on theme.

So. For everyone who ever wanted to use Qt controls in his Allegro game, here are big news: I got them working. I haven't yet checked performance, so it might be a Pyrrhic victory, but at least after several evenings of staring at variations of black and white screens, it's very nice to finally see a Qt dialog rendered above some Allegro-rendered sprites.

If you ever check out this particular problem, you will find that there are many ways to do ANYTHING in Qt. But, in general, there are two ways of displaying GUI with this library at present time.

One is to use regular, old-school widgets. They are rendered in software. You can create them from your code, or using Editor, or by describing them in declarative file.

The other is to use Qt Quick. Qt Quick adds OpenGL-based hardware acceleration to Qt. You still can create your GUI in any of the ways described above.

Well, I found a way to make Allegro work with both of this methods, but there are downsides :)

If you're in a hurry, just download attached project for Visual Studio 2010 and check the code out yourself. The code is bad in style, it's just a proof-of-concept, born out of many attempts, but it's simple and short and you should have no problems in understanding and using it, if you want. Please not, that the project is dependent upon Qt 5.2.0 and Allegro 5.x, in monolith build (but you can change it to use usual Allegro libraries, of course).

If you're still reading this, here's what I did, in more details.

Regular Qt widgets

qtallegro1.png

My first approach was to create my own implementation of QPaintEngine, a class that performs all low-level rendering in Qt. Its function could be mapped without very much difficulty on Allegro calls, but I haven't got around to implementing them all. For starters, you only need to make drawPixmap work, which simple should render passed texture in Qt format.

But there is a slight problem here: Qt offers no way to tell all its widgets to use your Paint Engine. Global QPaintEngine instance in supplied by QPlatformPlugin class, which, in turn, is loaded from shared object file (dll on Windows) during QApplication creation. Even if you write your own implementation of QPlatrofmPlugin (which is non-trivial), you still can't make Qt use it in any sane way, because it only looks for plugin's name in command line arguments and environment variable. Anyway, this is more work than I bargained for.

The solution came to me from an advice on ClosedCircles. It's so happens that Qt have a nice little class called QGraphicsView, which is helpfully described in documentation as "displays QGraphicScene". Actually, this class can display all the usual Qt widgets if you just add them to said QGraphicScene. This wouldn't help us much at all (after all, there are a lot of Qt classes which can display other Qt classes inside them!), but QGraphicsView have a function named "render". This function does what no other portion of Qt would: it allows us to pass or own instance of QPainter and will use it to draw all its children!

So, here's the full code from project:

#SelectExpand
1#include <QtWidgets/QApplication> 2#include <qgraphicsview.h> 3 4#include <allegro5/allegro.h> 5#include <allegro5/allegro_primitives.h> 6#include <allegro5/allegro_image.h> 7#include <allegro5/allegro_opengl.h> 8 9#include "qtallegrotest.h" 10#include "QAllegroPaintDevice.h" 11 12int main(int argc, char *argv[]) 13{ 14 // Create QApplication, because no Qt widget classes can be created without it. We won't use it much otherwise. 15 QApplication a(argc, argv); 16 17 // This is our class, which contains our GUI window (a form and a button) 18 QtAllegroTest w; 19 20 // Let's create QGraphicsScene 21 QGraphicsScene scene; 22 // Add our widget to it 23 scene.addWidget( &w ); 24 // Create view to display the scene 25 QGraphicsView view( &scene ); 26 27 // Initialize Allegro, recklessly discarding all return codes :) 28 al_init(); 29 al_init_image_addon(); 30 al_init_primitives_addon(); 31 32 // Create a nice little window 33 ALLEGRO_DISPLAY *pDisp = al_create_display( 1024, 768 ); 34 35 // An implementation of QPaintDevice, the most important of which is to provide some metrics during rendering and AN INSTANCE OF QAllegroPaintEngine! 36 QAllegroPaintDevice d; 37 38 // Did you know that 'painter' is 'pintor' in Spanish? I guess it because a painter is always ready to drink a pint! 39 QPainter p( &d ); 40 41 // We only render widgets once, because frankly, my QAllegroPaintEngine isn't ready for a prime time yet, it creates a new bitmap on every render call. 42 view.render( &p ); 43 al_flip_display(); 44 a.exec(); 45}

I like this method, because it's controllable. I have full control over my implementation of QPaintEngine and it should never break as long as Qt and Allegro maintain some compatibility with the current versions.

However, it has two drawbacks:
1) I'm not entirely sure how to cache ALLEGRO_BITMAPs created for QPixmaps supplied by Qt, and without caching them, this method will be VERY slow.

2) This method does not works with Qt Quick, which is a pity, since Qt Quick is very nifty thing! Its QML format even allows you to specify state-machines for you GUI components!

So, on to number two:

Qt Quick widgets

{"name":"qtallegro2.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/0\/10a1f5caba4f7a1225fcc1b1efaaccce.png","w":400,"h":197,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/0\/10a1f5caba4f7a1225fcc1b1efaaccce"}qtallegro2.png

At first, Qt Quick seems to be impossible to integrate with. It's even more opaque than usual Qt rendering. There is nothing like QPaintEngine here, you simply can't supply your own interface to render Qt Quick controls your way. No workarounds, unless you're willing to rewire half of Qt. Unfortunately, Qt Quick-related classed are tightly coupled with Qt's OpenGL-related classes, so even if you're willing to mess around inside Qt sources, you still will have too much work routing it through Allegro.

Fortunately, there is an intended workaround here. An example from documentation shows us how to draw OpenGL scene under Qt Quick. Further, there is a working and well-commented example of how to integrate Qt Quick and Ogre, QmlOgre.

What I did, basically, is a simplified re-creation of QmlOgre's example with Allegro. Unfortunately, unlike Ogre, Allegro doesn't have an option to use external-provided window and OpenGL context, so the whole thing, presently, hinges on some side-effects that I don't really understand.

Namely, while Qt and Allegro both create their own OpenGL contexts, I still can make Allegro use Qt's context just by making it current to this thread. Which I do:

#SelectExpand
1void QAllegroItem::paint() 2{ 3 m_pQtContext = QOpenGLContext::currentContext(); 4 5 static ALLEGRO_BITMAP *pBmp = 0; 6 if ( !m_pAllegroContext ) 7 { 8 // Creating a shared OpenGL context 9 m_pAllegroContext = new QOpenGLContext(); 10 m_pAllegroContext->setFormat(window()->requestedFormat()); 11 m_pAllegroContext->setShareContext( m_pQtContext ); 12 m_pAllegroContext->create(); 13 14 pBmp = al_load_bitmap( "target01.png" ); 15 } 16 17 // This is copied from Ogre example, but does not help at all. 18 glPopAttrib(); 19 glPopClientAttrib(); 20 21 m_pQtContext->functions()->glUseProgram(0); 22 m_pQtContext->doneCurrent(); 23 24 // Make our special context current 25 m_pAllegroContext->makeCurrent( window() ); 26 27 // Render Allegro stuff 28 al_clear_to_color( al_map_rgb( 0, 255, 0 ) ); 29 30 al_draw_bitmap( pBmp, 0, 0, 0 ); 31 32 al_draw_filled_rectangle( 0, 0, 10, 10, al_map_rgb(255,0,0) ); 33 al_draw_filled_rectangle( 1024-10, 0, 10, 10, al_map_rgb(200,0,0) ); 34 al_draw_filled_rectangle( 0, 768-10, 10, 10, al_map_rgb(100,0,0) ); 35 al_draw_filled_rectangle( 1024-10,768-10, 10, 10, al_map_rgb(0,0,0) ); 36 37 // Make Qt context current again. There is even no need to call al_flip_display, since we're not using Allegro's display. 38 m_pAllegroContext->doneCurrent(); 39 m_pQtContext->makeCurrent( window() ); 40}

This works. Somehow. I'm not sure if this is a horrible hack, or SOP, but I feel like this is bad code, which can break at any minute.

In fact, it already does not work quite as expected. You may notice that I'm trying to draw four colored rectangles on bright green screen, and a bitmap. If you run this code, you will see, that bitmap is rendered at the lower part of screen, and not where you expect (0,0) to be if you're used to using Allegro, and rectangles are simple screwed up. That's because all this magic with contexts screwed projection matrix which Allegro sets up to reverse the usual OpenGL coordinate system, and I don't know how to restore it.

But there is more downsides to this method:
1) While Allegro uses Qt's context to do it rendering, and therefore uses Qt's window, it still creates its own window, which hangs around, unpainted and unloved.

2) The above also means that you won't get any input through usual Allegro means, only through Qt. Which isn't such a big deal until you notice that while Qt supports keyboard, mouse and touch, it has no support for joystics and certainly not for haptics introduced in recent Allegro versions.

3) This method only works with OpenGL (unlike the first one). If you want to use Allegro with DirectX, you're out of luck with Qt here, not to mention software rendering.

Still, this method should be faster than the first one, and it also DOES allow me to use Qt Quick and QML, which is super cool.

Conclusion

While both presented methods for getting Qt and Allegro to work together are, at best, a proof-of-concepts, I decided to make this post to start a discussion and maybe find some help or ideas on how to make it all work properly.

I think getting Qt to work with Allegro would be a big boon to Allegro developers, since, while it has its drawbacks, Qt is one of the best existing GUI libraries, with lots of documentation, tutorials and users. Its usage would almost certainly limit one to OpenGL, but this does not seem like a very big concern to me, as I've heard a lot of voices against DirectX on this very board.

So, if you're interested, let's get this things working! :)

P.S. Sorry for any errors in posted text and/or code. It's 00:35 here in Moscow and I should really go to sleep.

Next day edit:

First FPS tests:

Desktop Qt widgets: 60FPS on a simple example, falling of rapidly with addition of new controls. Down to 4FPS on a moderately complex form. I'll try to optimize that part of code a bit, but it does not look good. Locking a lot of bitmaps per frame is just too costly.

Qt Quick widgets with shared context: I still can't get projection right (although I figured that I need to use Allegro default shader for Qt context to make it work, probably), but FPS are high. I get steady 60FPS on moderately complex form (same as in example above: several text fields, group boxes etc.).

Go to: