/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \example qws/ahigl \title OpenGL for Embedded Systems Example \section1 Introduction This example demonstrates how you can use OpenGL for Embedded Systems (ES) in your own screen driver and \l{add your graphics driver to Qt for Embedded Linux}. In \l{Qt for Embedded Linux}, painting is done in software, normally performed in two steps: First, each client renders its windows onto its window surface in memory using a paint engine. Then the server uses the screen driver to compose the window surface images and copy the composition to the screen. (See the \l{Qt for Embedded Linux Architecture} documentation for details.) This example is not for the novice. It assumes the reader is familiar with both OpenGL and the screen driver framework demonstrated in the \l {Accelerated Graphics Driver Example}. An OpenGL screen driver for Qt for Embedded Linux can use OpenGL ES in three ways. First, the \l{QWSServer}{Qt for Embedded Linux server} can use the driver to compose multiple window images and then show the composition on the screen. Second, clients can use the driver to accelerate OpenGL painting operations using the QOpenGLPaintEngine class. Finally, clients can use the driver to do OpenGL operations with instances of the QGLWidget class. This example implements all three cases. The example uses an implementation of OpenGL ES from \l {http://ati.amd.com}{ATI} for the \l {http://ati.amd.com/products/imageon238x/}{Imageon 2380}. The OpenGL include files gl.h and egl.h must be installed to compile the example, and the OpenGL and EGL libraries must be installed for linking. If your target device is different, you must install the include files and libraries for that device, and you also might need to modify the example source code, if any API signatures in your EGL library differ from the ones used here. After compiling and linking the example source, install the screen driver plugin with the command \c {make install}. To start an application that uses the plugin, you can either set the environment variable \l QWS_DISPLAY and then start the application, or just start the application with the \c -display switch, as follows: \snippet doc/src/snippets/code/doc_src_examples_ahigl.qdoc 0 The example driver also implements an animated transition effect for use when showing new windows or reshowing windows that have been minimized. To enable this transition effect, run the application with \c {-display ahigl:effects}. \section1 The Class Definitions The example comprises three main classes plus some helper classes. The three main classes are the plugin (QAhiGLScreenPlugin), which is defined in qscreenahiglplugin.cpp, the screen driver (QAhiGLScreen), which is defined in qscreenahigl_qws.h, and the window surface (QAhiGLWindowSurface), which is defined in qwindowsurface_ahigl_p.h. The "Ahi" prefix in these class names stands for \e {ATI Handheld Interface}. The example was written for the ATI Imageon 2380, but it can also be used as a template for other ATI handheld devices. \section2 The Plugin Class Definition The screen driver plugin is class QAhiGLScreenPlugin. \snippet examples/qws/ahigl/qscreenahiglplugin.cpp 0 QAhiGLScreenPlugin is derived from class QScreenDriverPlugin, which in turn is derived from QObject. \section2 The Screen Driver Class Definitions The screen driver classes are the public class QAhiGLScreen and its private implementation class QAhiGLScreenPrivate. QAhiGLScreen is derived from QGLScreen, which is derived from QScreen. If your screen driver will only do window compositions and display them, then you can derive your screen driver class directly from QScreen. But if your screen driver will do accelerated graphics rendering operations with the QOpenGLPaintEngine, or if it will handle instances of class QGLWidget, then you must derive your screen driver class from QGLScreen. \snippet examples/qws/ahigl/qscreenahigl_qws.h 0 All functions in the public API of class QAhiGLScreen are virtual functions declared in its base classes. hasOpenGL() is declared in QGLScreen. It simply returns true indicating our example screen driver does support OpenGL operations. The other functions in the public API are declared in QScreen. They are called by the \l{QWSServer}{Qt for Embedded Linux server} at the appropriate times. Note that class QScreen is a documented class but class QGLScreen is not. This is because the design of class QGLScreen is not yet final. The only data member in class QAhiGLScreen is a standard d_ptr, which points to an instance of the driver's private implementation class QAhiGLScreenPrivate. The driver's internal state is stored in the private class. Using the so-called d-pointer pattern allows you to make changes to the driver's internal design without breaking binary compatibility. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 0 Class QAhiGLScreenPrivate is derived from QObject so that it can use the Qt signal/slot mechanism. QAhiGLScreen is not a QObject, so it can't use the signal/slot mechanism. Signals meant for our screen driver are received by slots in the private implementation class, in this case, windowEvent() and redrawScreen(). \section2 The Window Surface Class Definitions The window surface classes are QAhiGLWindowSurface and its private implementation class QAhiGLWindowSurfacePrivate. We create class QAhiGLWindowSurface so the screen driver can use the OpenGL paint engine and the OpenGL widget, classes QOpenGLPaintEngine and QGLWidget. QAhiGLWindowSurface is derived from the more general OpenGL window surface class, QWSGLWindowSurface, which is derived from QWSWindowSurface. \snippet examples/qws/ahigl/qwindowsurface_ahigl_p.h 0 In addition to implementing the standard functionality required by any new subclass of QWSWindowSurface, QAhiGLWindowSurface also contains the textureId() function used by QAhiGLScreen. The same d-pointer pattern is used in this window surface class. The private implementation class is QAhiGLWindowSurfacePrivate. It allows making changes to the state variables of the window surface without breaking binary compatibility. \snippet examples/qws/ahigl/qwindowsurface_ahigl.cpp 0 In this case, our private implementation class has no member functions except for its constructor. It contains only public data members which hold state information for the window surface. \section2 The Helper Classes The example screen driver maintains a static \l {QMap} {map} of all the \l {QWSWindow} {windows} it is showing on the screen. Each window is mapped to an instance of struct WindowInfo. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 2 As each new window is created, an instance of struct WindowInfo is allocated and inserted into the window map. WindowInfo uses a GLuint to identify the OpenGL texture it creates for the window. Note that the example driver, in addition to drawing windows using OpenGL, also supports drawing windows in the normal way without OpenGL, but it uses an OpenGL texture for the rendering operations in either case. Top-level windows that are drawn without OpenGL are first rendered in the normal way into a shared memory segment, which is then converted to a OpenGL texture and drawn to the screen. To animate the window transition effect, WindowInfo uses an instance of the helper class ShowAnimation. The animation is created by the windowEvent() slot in QAhiGLScreenPrivate, whenever a \l {QWSServer::WindowEvent} {Show} window event is emitted by the \l {QWSServer} {window server}. The server emits this signal when a window is shown the first time and again later, when the window is reshown after having been minimized. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 1 Class ShowAnimation is derived from the QTimeLine class, which is used for controlling animations. QTimeLine is a QObject, so ShowAnimation can use the Qt signal/slot mechanism. We will see how the timeline's \l {QTimeLine::valueChanged()} {valueChanged()} and \l {QTimeLine::finished()} {finished()} signals are used to control the animation and then destroy the instance of ShowAnimation, when the animation ends. The ShowAnimation constructor needs the pointer to the screen driver's private implementation class so it can set up these signal/slot connections. \section1 The Class Implementations \section2 The Plugin Class Implementation QAhiGLScreenPlugin is a straightforward derivation of QScreenDriverPlugin. It reimplements \l{QScreenDriverPlugin::}{keys()} and \l{QScreenDriverPlugin::}{create()}. They are called as needed by the \l{QWSServer}{Qt for Embedded Linux server.} Recall that the server detects that the ahigl screen driver has been requested, either by including "ahigl" in the value for the environment variable QWS_DISPLAY, or by running your application with a command line like the following. \snippet doc/src/snippets/code/doc_src_examples_ahigl.qdoc 1 The server calls \l {QScreenDriverPlugin::} {keys()}, which returns a \l {QStringList} containing the singleton "ahigl" matching the requested screen driver and telling the server that it can use our example screen driver. The server then calls \l {QScreenDriverPlugin::} {create()}, which creates the instance of QAhiGLScreen. \snippet examples/qws/ahigl/qscreenahiglplugin.cpp 1 In the code snippet above, the macro Q_EXPORT_PLUGIN2 is used to export the plugin class, QAhiGLScreen, for the qahiglscreen plugin. Further information regarding plugins and how to create them can be found at \l{How to Create Qt Plugins}. \section2 The Screen Driver Class Implementations The plugin creates the singleton instance of QAhiGLScreen. The constructor is passed a \c displayId, which is used in the base class QGLScreen to identify the server that the screen driver is connected to. The constructor also creates its instance of QAhiGLScreenPrivate, which instantiates a QTimer. The timeout() signal of this timer is connected to the redrawScreen() slot so the timer can be used to limit the frequency of actual drawing operations in the hardware. The public API of class QAhiGLScreen consists of implementations of virtual functions declared in its base classes. The function hasOpenGL() is declared in base class QGLScreen. The others are declared in base class QScreen. The \l {QScreen::}{connect()} function is the first one called by the server after the screen driver is constructed. It initializes the QScreen data members to hardcoded values that describe the ATI screen. A better implementation would query the hardware for the corresponding values in its current state and use those. It asks whether the screen driver was started with the \c effects option and sets the \c doEffects flag accordingly. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 7 The \l {QScreen::}{initDevice()} function is called by the server after \l {QScreen::}{connect()}. It uses EGL library functions to initialize the ATI hardware. Note that some data structures used in this example are specific to the EGL implementation used, e.g., the DummyScreen structure. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 8 Note the signal/slot connection at the bottom of initDevice(). We connect the server's QWSServer::windowEvent() signal to the windowEvent() slot in the screen driver's private implementation class. The windowEvent() slot handles three window events, QWSServer::Create, QWSServer::Destroy, and QWSServer::Show. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 5 The function manages instances of the helper classes associated with each window. When a QWSServer::Create event occurs, it means a new top-level \l {QWSWindow} {window} has been created. In this case, an instance of helper class WindowInfo is created and inserted into the window map with the pointer to the new \l {QWSWindow} {window} as its key. When a QWSServer::Destroy event occurs, a window is being destroyed, and its mapping is removed from the window map. These two events are straightforward. The tricky bits happen when a QWSServer::Show event occurs. This case occurs when a window is shown for the first time and when it is reshown after having been minimized. If the window transition effect has been enabled, a new instance of the helper class ShowAnimation is created and stored in a QPointer in the window's instance of WindowInfo. The constructor of ShowAnimation automatically \l {QTimeLine::start()} {starts} the animation of the transition effect. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 3 To ensure that a ShowAnimation is not deleted until its animation ends, the \l {QTimeLine::finished()} {finished()} signal is connected to the \l {QObject::deleteLater()} {deleteLater()} slot. When the animation ends, the finished() signal is emitted and the deleteLater() slot deletes the ShowAnimation. The key here is that the pointer to the ShowAnimation is stored in a QPointer in the WindowInfo class. This QPointer will also be notified when the ShowAnimation is deleted, so the QPointer in WindowInfo can null itself out, if and only if it is still pointing to the instance of ShowAnimation being deleted. The \l {QTimeLine::valueForTime()} {valueForTime()} function in QTimeLine is reimplemented in ShowAnimation to return time values that represent a curved path for the window transition effect. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 4 valueForTime() is called internally, when the time interval it computed during the previous call has elapsed. If it computes a next time value that is different from the one computed previously, the \l {QTimeLine::valueChanged()} {valueChanged()} signal is emitted. The ShowAnimation constructor shown above connects this signal to the redrawScreen() slot in the screen driver's private implementation class. This is how the animation actually happens. The screen driver's implementation of \l {QScreen::} {exposeRegion()} is where the main work of the screen driver is meant to be done, i.e., updating the screen. It is called by the \l {QWSServer} {window system} to update a particular window's region of the screen. But note that it doesn't actually update the screen, i.e., it doesn't actually call redrawScreen() directly, but starts the updateTimer, which causes redrawScreen() to be called once for each updateTimer interval. This means that all calls to exposeRegion() during an updateTimer interval are handled by a single call to redrawScreen(). Thus updateTimer can be used to limit the frequency of screen updates. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 13 The call to the private function invalidateTexture() destroys the window's existing texture (image). This ensures that a new texture will be created for the window, when redrawScreen() is eventually called. But there is a caveat to using updateTimer to limit the frequency of screen updates. When the driver's animated transition effect for new windows is enabled and a new window is being shown for the first time or reshown after having been minimized, an instance of ShowAnimation is created to run the animation. The valueChanged() signal of this ShowAnimation is also connected to the redrawScreen() slot, and QTimeLine, the base class of our ShowAnimation, uses its own, internal timer to limit the speed of the animation. This means that in the driver as currently written, if the window transition effect is enabled (i.e. if the plugin is started, with \c {-display ahigl:effects}), then redrawScreen() can be called both when the update timer times out and when the ShowAnimation timer times out, so the screen might get updated more often than the frequency established by the update timer. This may or may not be a bug, depending on your own hardware, if you use this example as a template for your own OpenGL driver. The screen driver's private function redrawScreen() constructs the window compositions. It is called only by the function of the same name in the screen driver's private implementation class. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 6 Recall that this redrawScreen() in the private implementation class is a slot function connected to two signals, the \c timeout() signal of the updateTimer in the private implementation class, and the valueChanged() signal of the helper class ShowAnimation. Thus, the screen is only ever updated when a timeout of one of the two timers occurs. This is important for two reasons. First, the screen is meant to be updated no more than once per updateTimer interval. Second, however, if the animated window transition effect is requested, the screen might be updated more often than that, and this might be a bug if the hardware can't handle more frequent updates. The redrawScreen() in QAhiGLScreen begins by using standard OpenGL to fill the screen with the background color. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 10 Next it iterates over the list of all \l {QWSWindow} {client windows} obtained from the \l {QWSServer} {server}, extracting from each window its instance of QWSWIndowSurface, then using that window surface to create an OpenGL texture, and finally calling the helper function drawWindow() to draw the texture on the screen. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 11 Note the call to glBindTexture() immediately before the call to drawWindow(). This call binds the identifer \c GL_TEXTURE_2D to the texture we have just created. This makes our texture accessible to functions in the OpenGL libraries. If you miss that point, digging into the internals of drawWindow() won't make much sense. Finally, the cursor is added to the window composition, and in the last statement, the whole thing is displayed on the screen. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 12 The call to \c drawWindow(win,progress), in addition to passing a pointer to the window to be redrawn, also passes the \c progress parameter obtained by calling \l {QTimeLine::currentValue()} on the window's instance of ShowAnimation. Recall that the current value of the timeline is updated internally by a timer local to the timeline, and the redrawScreen() slot is called whenever the current value changes. The progress value will only be used if the animated transition effect has been enabled. These extra calls of redrawScreen() may cause the screen to be updated more often than the rate determined by updateTimer. This must be taken into account, if you set your updateTimer to timeout at the maximum screen update frequency your hardware can handle. The drawWindow() function is not shown here and not explained further, but the call to drawWindow() is the entry point to a hierarchy of private helper functions that execute sequences of OpenGL and EGL library calls. The reader is assumed to be familiar enough with the OpenGL and EGL APIs to understand the code in these helper functions on his own. Besides drawWindow(), the list of these helper functions includes drawQuad(), drawQuadWavyFlag(), the two overloadings of drawQuad_helper() (used by drawQuad() and drawQuadWacyFlag()), and setRectCoords(). Note the two different ways the window's texture can be created in redrawScreen(). If the window surface is an OpenGL window surface (QAhiGLWindowSurface described below), the texture is obtained from the window surface directly by calling its textureId() function. But when the window surface is not an OpenGL one, the static function createTexture() is called with the window surface's \l {QImage} {image} to copy that image into an OpenGL texture. This is done with the EGL functions glTexImage2D() and glTexSubImage2D(). createTexture() is another function that should be understandable for exsperienced OpenGL users, so it is not shown or explained further here. The two implementations of \l {QScreen::}{createSurface()} are for instantiating new window surfaces. The overloading with the widget parameter is called in the client. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 14 If the parameter is an \l {QGLWidget} {OpenGL widget}, or, when it isn't an OpenGL widget but its size is no bigger than 256 x 256, we instantiate our subclass QAhiGLWindowSurface. Otherwise, we instantiate a QWSWindowSurface. The size contraint is a limitation of the OpenGL ES libraries we are using for our ATI device. Note the test at the top of the function asking if our application process is the \l {QApplication::GuiServer} {server}. We only create instances of QAhiGLWindowSurface if our client is in the server process. This is because of an implementation restriction required by the OpenGL library used in the example. They only support use of OpenGL in the server process. Hence a client can use the QAhiGLWindowSurface if the client is in the server process. The other overloading of createSurface() is called by the server to create a window surface that will hold a copy of a client side window surface. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 15 This overloading accepts a QString parameter identifying the type of window surface to instantiate. QAhiGLWindowSurface is instantiated if the parameter is \c ahigl. Otherwise, a normal QWSWindowSurface is instantiated. The client's window surface communicates its image data to the server's window surface through shared memory. The implementation of \l {QScreen::}{setMode()}, is a stub in this example. It would normally reset the frame buffer's resolution. Its parameters are the \e width, \e height, and the bit \e depth for the frame buffer's new resolution. If you implement setMode() in your screen driver, remember that it must emit a signal to warn other applications to redraw their frame buffers with the new resolution. There is no significance to setMode() not being implemented in this example. It simply wasn't implemented. However, the stub had to be included because QScreen declares setMode() to be pure virtual. Before the application exits, the server will call \l {QScreen::} {shutdownDevice()} to release the hardware resources. This is also done using EGL library functions. \snippet examples/qws/ahigl/qscreenahigl_qws.cpp 9 The server will also call \l {QScreen::}{disconnect()}, but this function is only a stub in this example. \section2 The window Surface Class Implementations QAhiGLScreen creates instances of QAhiGLWindowSurface in its two createSurface() functions, and there are two constructors for QAhiGLWindowSurface that correspond to these two versions of createSurface(). The constructor accepting a \l {QWidget} {widget} parameter is called by the client side version of createSurface(), and the constructor without the \l {QWidget} {widget} parameter is called by the server side version. There will be a window surface constructed on the server side for each one constructed on the client side. \snippet examples/qws/ahigl/qwindowsurface_ahigl.cpp 1 \codeline \snippet examples/qws/ahigl/qwindowsurface_ahigl.cpp 2 The constructors create an instance of QAhiGLWindowSurfacePrivate, the private implementation class, which contains all the state variables for QAhiGLWindowSurface. The client side constructor also creates an instance of QWSGLPaintDevice, the OpenGL paint device, for return by \l {QWSWindowSurface::} {paintDevice()}. This ensures that all \l {QPainter}s used on this surface will use an OpenGL enabled QPaintEngine. It is a bit of jiggery pokery, which is required because \l {QWSWindowSurface::} {paintDevice()} is declared pure virtual. Normally, the client side constructor will be called with an \l {QGLWidget}{OpenGL widget}, which has its own \l {QWidget::} {paintEngine()} function that returns the global static OpenGL paint engine, but because the constructor also accepts a normal \l {QWidget}{widget}, it must be able to find the OpenGL paint engine in that case as well, so since \l {QWSWindowSurface::} {paintDevice()} must be implemented anyway, the constructor creates an instance of QWSGLPaintDevice, which can always return the global static pointer to QOpenGLPaintEngine. The OpenGL library implementation used for this example only supports one OpenGL context. This context is therefore shared among the single instance of QAhiGLScreen and all instances of QAhiGLWindowSurface. It is passed to both constructors. This example uses the OpenGL frame buffer object extension, which allows for accelerating OpenGL painting operations. Using this OpenGL extension, painting operations are performed in a frame buffer object, which QAhiGLScreen later uses to construct window compositions on the screen. Allocation of the frame buffer object is performed in \l {QWindowSurface::} {setGeometry()}. A safer way to use this extension would be to first test to see if the extension is supported by your OpenGL library, and use a different approach if it is not. \snippet examples/qws/ahigl/qwindowsurface_ahigl.cpp 3 Since there can be several instances of the QAhiGLWindowSurface, we need to make sure that the correct framebuffer object is active before painting. This is done by reimplementing \l QWindowSurface::beginPaint(): \snippet examples/qws/ahigl/qwindowsurface_ahigl.cpp 4 Finally we need to make sure that whenever a widget grows beyond the size supported by this driver (256 x 256), the surface is deleted and a new standard surface is created instead. This is handled by reimplementing \l QWSWindowSurface::isValid(): \snippet examples/qws/ahigl/qwindowsurface_ahigl.cpp 5 */