Fun with Qt-Plugins

Featured

I tried to deploy a QML-application, which depends on the Qt-plugin for audio, called qtaudio_windows.dll. My application run fine in the QtCreator-environment. But when I tried to install it on a different PC, the audio I tried to play didn’t run as ecpected. After trying to play a sound the application crashed instead. So I started to figure out what went wrong.

I looked for the right strategy to deploy plugins for QML-based applications. Next to your executable you also have to put the runtime-dlls from your compiler ( for mingw using windows this dll is called libgcc_s_dw2-1.dll ). And you have to install the platform-abstraction-plugin also ( qwindows.dll ).

After reading the doc the structure of the folder shall look as follow:

myApp.exe
icudt52.dll
icuin52.dll
icuuc52.dll
libgcc_s_dw2-1.dll
libstdc++-6.dll
libwinpthread-1.dll
Qt5Core.dll
Qt5Gui.dll
Qt5Multimedia.dll
Qt5MultimediaQuick_p.dll
Qt5MultimediaWidgets.dll
Qt5Network.dll
Qt5OpenGL.dll
Qt5Qml.dll
Qt5Quick.dll
Qt5Widgets.dll
platforms/windows.dll
plugins/qaudio_windows.dll

Unfortunately this structure doesn’t work. The audio plugin will not be detected and the application was not able to play any sounds. And the crash still occurred. And the bad
So what can you do to figure out what went wrong and how to fix the issue on another PC.

  • Simulate a PC without a Qt-installation:

This is easy: go to your Qt-installation and rename the plugin-folder ( for instance from plugins to plugins_1 ):

  • Enable plugin debugging from Qt

Also easy: set the environment variable QT_DEBUG_PLUGINS=1, now you can use DebugView to see what’s ongoing during the plugin loading from the Qt-framework.

With these little steps I saw, that Qt5 is looking for the audio-plugin not in the plugin-folder. Instead of this it looks in the audio-folder: So I changed my installation to:

myApp.exe
icudt52.dll
icuin52.dll
icuuc52.dll
libgcc_s_dw2-1.dll
libstdc++-6.dll
libwinpthread-1.dll
Qt5Core.dll
Qt5Gui.dll
Qt5Multimedia.dll
Qt5MultimediaQuick_p.dll
Qt5MultimediaWidgets.dll
Qt5Network.dll
Qt5OpenGL.dll
Qt5Qml.dll
Qt5Quick.dll
Qt5Widgets.dll
platforms/windows.dll
audio/qaudio_windows.dll

Now the sound were played and everything worked as expected. Unfortunately I haven’t found any information about this
deployment inside of the Qt-documentation. Even the tool windeployqt.exe, which shall help to install all required files did this wrong.

  • What I learned

Try to run your Qt-App-installation on a system without Qt! You can easily simulate this by changing the Qt-Directories manually. And when nothing works check the debugging techniques from Qt. They can help you a lot. And last but not least: issues like this cannot be fund by using depends.exe, because checking my stuff with depends haven’t shown me any errors.

QML-Plugins under Windows: be careful with your environment

Featured

When you are using Qt5.0 or higher with QML2.0 you can write your own plugins in a real easy way. A QML-plugin gives you the possibility to deploy your own QML-types and use them for other applications. And you can also easily extend QML with C++-based stuff.

But of course there are some issues upcoming with this approach. An example:

You have a datatype defined in C++, which can be used in QML. This qml-type deals with some static data. The plugin is published to QML with the name Foo:

// Declaration of the repository
class Repo {
  static int s_data;
  static void setData( int data) { s_data = data };
  static int getData() { return s_data; }
};

// Declaration of the plugin
class FooPlugin : public QQuickItem {
...
public:
  void foo() { Repo::getData(); }
...
};

// QML
import Foo// use foo QML-types

Easy, we have some kind of a repo, we want to get data from it by calling the interface, easy going.

Now you want to use this kind of approach with your application, which depends on this storage. What can we do? We are linking the plugin to our application to get the C++ stuff. In the QML-declaration we are using the plugin as well. Now you are setting the repo-data with Repo::setData in the C++ part of your code to get some kind of effect in your QML-View. The operation system is WIndows 7.

What can happen: the setup of the repo-data has no effect to the QML-View. But why?

I digged deeper into this issue and found the reason: when using code from a dll under windows normally you are using an export library to get your linker happy. An export library under windows just defines the symbols, which are part of the dll without having the code inside. So you can link against this during your build. When you want to start the application, which uses these calls, the dll will be loaded at runtime and try to resolve the symbols by loading the dll . This mechanism is called implicit linking ( see http://msdn.microsoft.com/de-de/library/d14wsce5.aspx ).When loading a QML-plugin the QML-parser sees your foo statement, checks your import paths for a directory with a corresponding qmldir, which contains this plugin. If this is the case the dll of the plugin will be loaded explicitly by calling LoadLibrary on its own ( see http://msdn.microsoft.com/de-de/library/784bt7z7.aspx ). You are using in c++ and QML the same dll, so my expectation was: I have it already, I am using the same dll, all fine. When you have only one place with your dll this is true.

When you have to export the plugin into its own structure ( folder named for the dll, contains dll, qmldir, some other dependencies ) you have to do a deploy step. It can happen, that you have now two dlls placed in different folders, which are the same exepting their absolute pathes in the filesystem. Windows is doing the distinction between two different dlls by looking at these absolute paths. The absolute paths differs, two dlls, which are the same, will be loaded. The static data is loaded twice as well. So what happened: you are setting in your C++ code the repo-data in the implicitly loaded dll, in QML you are looking for this data in the explicitly loaded dll, where is nothing set. In other words: nothing happens.

So when using something like that please make sure that you have only one dll in your environment path.

 

ZFXCE2 goes to Linux

Featured

I am happy to announce that the Linux-version of the current ZFXCE2 is working. To be able to support this I added a SDL2-based platform-plugin to support windows-management, input-, thread-handling for Linux. The setup of the OpenGL-renderbackend is still implemented by using GLWE. I can reuse this implementation for the Win32-API-based plugin as well.

The biggest topics/issues of my Linux-port:

  • Port the Win32-API based threads and bring up the thread environment under Linux.
  • Bring up the first rendering window. At the beginning I didn’t get a window on my linux screen at all. I only got my log output which told me where something went wrong. And I learned where some more logging should be useful to see when an error occurrs. After a while I got a nice render context in my render thread on my linux box, which had OpenGL 4.4 support. But I still was not able to see any window at all. So what went wrong? The setup of the display was wrong. I struggled a while to understand, where the root issue was of this.

So when you have two GPU’s in your linux box ( for a notebook this seems to be quiet common on today’s notebooks ) and you have a Nvidia-GPU: check your /var/log/X.org log for erroreous EDID-messages: their are good signs that the SDL2-application is not able to get the right display parameters to setup your window in a proper way. With the tool nvidia-settings you can fix them easily: just setup the display resolution manually instead of selecting the option Auto.

I am not sure if this is a common issue of the Nvidia-driver with SDL2. It just occurs on my notebook with my Linux ( Ubuntu 14.04-64bit )

 

You can find the code here: https://github.com/kimkulling/zfxce2

Platform abstraction in the ZFXCE2

Featured

I am using a new concept to support multiple platforms in the ZFC-Community-Engine 2 and I want to explain the ideas behind this a little bit.

Where do I come from?

When I had to support more than one platform the first time I tried two different approaches to do this:

  • Use platform-specific #ifdef directives for every place where a system call from the operation system has to be done. This is an easy to use straid-forward concept which is widely used in many other projects. Unfortunately it brings some serious pitfalls with it: your code is full of dead sections because there are only used for one platform. This makes the code hard to read, hard to test ( special when you have to adapt one feature and you missed all other supported platforms ) and your API calls will be spread all over your code. When you have to support any new platform you have to check each place where an #ifdef is placed.
  • Use a platform-abstraction library like SDL2 and use all the platform-specific stuff offered by this framework. For getting your daily jobs like creating a window this solution works fine. And you don’t have to check all your code for special #ifdef’s. Unfortunately this solution has also some serious pitfalls: when you need a special platform-specific feature this must be supported by your platform-abstraction framework. If it is not supported you have to implement this on your own and put it into #ifdef-blocks. And after time this will cause again a lot of spreaded ifdefs all over the place. And what happens when you have to support another platform which is not part of your platform-abstraction framework?

And one big disadvantage of both concepts: they are really hard to mockup for a test.

What was my next step?

After dealing with these two solutions for quite a while my next step to improve this was building interfaces which will implementing the features for the supported platforms. For instance I build an interface like:

// The interface
class IWindow {};

// The implementation for window
class Win32Window : public IWindow {};

// The implementation for x-server based systems
class XWindow : public IWindow {};

So much better. You have an implementation for each platform, you can build mockup-implementations for your tests. In your make-file you can specify which platform shall been build.

But one issue is still there: you are spreading all the platform-specific implementation all over your code. When you have to port somethink you still have to figure our which interafces your have to implement for the platform.  From this point I did some research how other frameworks are dealing with situations like this. And I found the Qt5-approach.

The Qt Platform Abstraction

Qt is a cross-platform framework for User Interfaces, whcih also offers a wide list of cross-platform features like UI-developments, network-programming and much more. They are using a concept calle QPA ( Qt PLatform Abstraction ). Check this link to learn more about it: http://qt-project.org/videos/watch/qpa-the-qt-platform-abstraction .

In short: it offers you all needed platform abstraction at one place. New platforms can be integrated via a new plugin. The place where to find the plugins for your platform is well defined. If you have to support a special feature it will be done only in this platform-plugin. And you can configure via make which stuff will be build for the several platforms. Each implementations gets its own folder. So when you look for a platform-specific implementation you can look easily at

Code/Infrastructure/Platfom/<Impl-Type>/

What is the main advantage from my point of view:

  • You still have a bundle of interfaces to provide all the platform-specific stuff.
  • There is one place where all these implementation is placed in the code. So it is much easier to maintain it
  • Providing new platforms is easiy: just provide the new implementatiosn.
  • You still can use SDL2 for instance: it is just another plugin, which runs on different platforms

How to integrate this into the ZFX-Community-Engine 2

In the ZFX-Community-Engine I am currently offering a platform-integration called PlatformInterface. It currently contains:

  • Windows system abstraction to get a render window
  • Event handling abstraction for platform-specific event handling
  • Render Context abstraction for OpenGL 4.x
  • Thread abstraction

Two different implementation are already there: one for cross-platform based on SDL2 and one for Windows based on Win32-API. At the moment the classes are part of the Infrastructure library in the ZFXCE2. You can get the implementation by calling an static getter for the abstraction:

AbstractRenderContext *pRC = nullptr;
pRC = PlatformInterface::getInstance().getRenderCtx();

This works fine at the moment. Currently I haven’t planned to bring the platform-specific implementation in several plugins, but I will see.

Run a QML-Application in CodeXL

Featured

I just tried to run my QML-based application in CodeXL to get an overview about my hotspots. You have to setup the path to your executable for the CodeXL-project.

Unfortunately with a qmake-based build the executable and the QT-libraries are not at the same place. So per default your app will not start.

Solving this is really easy: just add your QT-installation path to the PATH vaiable, if you are using only one QT-installation.

If you have to switch between two different installations you can use a startup-script, which adds your QT-installation path to the PATH-variable in windows.

Chicken Korma

Featured

For my english training I just translated my favourite recepy for Chicken Korma into english. And of course I want to share this one:

Chicken Korma:
==============

800 g chicken breasts, cut in little pieces without skin or bones
One big onion cut in half rings
One red chilli peppers
Three cloves of garlic
One pieces of ginger ( size of your thump )
Almond slices
Shredded coconut
1 shrub of coriander
Curry paste ( Korma, you can do this on your own )
1 peace of butter
400 g chickpeas
400 g coconut milk
Salt
Black pepper
200 g natural yoghurt

At first produce the curry paste on your own or just buy it. Separate the leafs and the stalk
of the shrub of coriander. Now take your wok, put some oil in and heat it up until it is hot. Now you can sear the chicken meat until it get a light brown color all
around. Put the chilli, the mashed ginger, the onions and the stalk of
coriander with the butter into the wok. Cook this 10 minutes or so while you
are stiring the whole soup.
Mx the curry paste, the coconut milk, the almond leaves, the chickpeas,
the desiccated coconut and 200 ml of water into the wok.

Let it cook about 30 minutes. Now season to taste it with salt and
pepper.

Serve it with the leaves of coriander, the natural yoghurt and rice.

Enjoy your dinner!

Asset Importer Library news

Featured

I am really happy to announce that we made a new major release for the Asset-Importer-Library. Currently we have only a new source package out, but the SDK is on its way as well.
The guy’s from Debian asked for a new stable release, because they wanted to add the assimp-package into the next Ubuntu-release and assimp changed the API. And this API-change will cause binary-incompatibilies with applications, which belongs to the 2.0-version.
The API-changes are already documented and you can find them here:
http://assimp.sourceforge.net/lib_html/usage.html

So what is new in Asset Importer Library 3.0. A lot I guess:

New features:

  • New export interface similar to the import API. Supported export formats are: collada, obj, ply and stl.
  • New import formats: XGL/ZGL, M3 (experimental)
  • New postprocessing step: Debone
  • Vastly improved IFC (Industry Foundation Classes) support
  • Introduced API to query importer meta information (such as supported format versions, full name, maintainer info).
  • Reworked Ogre XML import
House keeping / refactorings:
  • The API changed to optimize the usability
  • Unified naming and cleanup of public headers
  • Supporting a Debian-Package
  • IMprove CMake-Build system
  • Better CMake-Support for Linux and MacOS
We will release our release notes as fast as possible. Currently we are all really spare on time.
Thanks to all the helping hands, the patches and all the nice and really constructive feedback about our work.