diff options
Diffstat (limited to 'src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp')
-rw-r--r-- | src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp new file mode 100644 index 0000000..3a94851 --- /dev/null +++ b/src/plugins/gfxdrivers/powervr/pvreglscreen/pvreglscreen.cpp @@ -0,0 +1,390 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins 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 qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pvreglscreen.h" +#include "pvreglwindowsurface.h" +#include "pvrqwsdrawable_p.h" +#include <QRegExp> +#include <qwindowsystem_qws.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/kd.h> +#include <fcntl.h> +#include <unistd.h> + +PvrEglScreen::PvrEglScreen(int displayId) + : QGLScreen(displayId) +{ + setOptions(NativeWindows); + setSupportsBlitInClients(true); + setSurfaceFunctions(new PvrEglScreenSurfaceFunctions(this, displayId)); + fd = -1; + ttyfd = -1; + doGraphicsMode = true; + oldKdMode = KD_TEXT; + if (QWSServer::instance()) + holder = new PvrEglSurfaceHolder(); + else + holder = 0; +} + +PvrEglScreen::~PvrEglScreen() +{ + if (fd >= 0) + ::close(fd); + delete holder; +} + +bool PvrEglScreen::initDevice() +{ + openTty(); + return true; +} + +bool PvrEglScreen::connect(const QString &displaySpec) +{ + if (!pvrQwsDisplayOpen()) + return false; + + // Initialize the QScreen properties. + data = (uchar *)(pvrQwsDisplay.screens[0].mapped); + w = pvrQwsDisplay.screens[0].screenRect.width; + h = pvrQwsDisplay.screens[0].screenRect.height; + lstep = pvrQwsDisplay.screens[0].screenStride; + dw = w; + dh = h; + size = h * lstep; + mapsize = size; + switch (pvrQwsDisplay.screens[0].pixelFormat) { + case PVR2D_RGB565: + d = 16; + setPixelFormat(QImage::Format_RGB16); + break; + case PVR2D_ARGB4444: + d = 16; + setPixelFormat(QImage::Format_ARGB4444_Premultiplied); + break; + case PVR2D_ARGB8888: + d = 32; + setPixelFormat(QImage::Format_ARGB32); + break; + default: + pvrQwsDisplayClose(); + qWarning("PvrEglScreen::connect: unsupported pixel format %d", (int)(pvrQwsDisplay.screens[0].pixelFormat)); + return false; + } + + // Handle display physical size spec. + QStringList displayArgs = displaySpec.split(QLatin1Char(':')); + QRegExp mmWidthRx(QLatin1String("mmWidth=?(\\d+)")); + int dimIdxW = displayArgs.indexOf(mmWidthRx); + QRegExp mmHeightRx(QLatin1String("mmHeight=?(\\d+)")); + int dimIdxH = displayArgs.indexOf(mmHeightRx); + if (dimIdxW >= 0) { + mmWidthRx.exactMatch(displayArgs.at(dimIdxW)); + physWidth = mmWidthRx.cap(1).toInt(); + if (dimIdxH < 0) + physHeight = dh*physWidth/dw; + } + if (dimIdxH >= 0) { + mmHeightRx.exactMatch(displayArgs.at(dimIdxH)); + physHeight = mmHeightRx.cap(1).toInt(); + if (dimIdxW < 0) + physWidth = dw*physHeight/dh; + } + if (dimIdxW < 0 && dimIdxH < 0) { + const int dpi = 72; + physWidth = qRound(dw * 25.4 / dpi); + physHeight = qRound(dh * 25.4 / dpi); + } + + // Find the name of the tty device to use. + QRegExp ttyRegExp(QLatin1String("tty=(.*)")); + if (displayArgs.indexOf(ttyRegExp) != -1) + ttyDevice = ttyRegExp.cap(1); + if (displayArgs.contains(QLatin1String("nographicsmodeswitch"))) + doGraphicsMode = false; + + // The screen is ready. + return true; +} + +void PvrEglScreen::disconnect() +{ + pvrQwsDisplayClose(); +} + +void PvrEglScreen::shutdownDevice() +{ + closeTty(); +} + +void PvrEglScreen::blit(const QImage &img, const QPoint &topLeft, const QRegion ®ion) +{ + QGLScreen::blit(img, topLeft, region); + sync(); +} + +void PvrEglScreen::solidFill(const QColor &color, const QRegion ®ion) +{ + QGLScreen::solidFill(color, region); + sync(); +} + +bool PvrEglScreen::chooseContext + (QGLContext *context, const QGLContext *shareContext) +{ + // We use PvrEglScreenSurfaceFunctions instead. + Q_UNUSED(context); + Q_UNUSED(shareContext); + return false; +} + +bool PvrEglScreen::hasOpenGL() +{ + return true; +} + +QWSWindowSurface* PvrEglScreen::createSurface(QWidget *widget) const +{ + if (qobject_cast<QGLWidget*>(widget)) + return new PvrEglWindowSurface(widget, (QScreen *)this, displayId); + + return QScreen::createSurface(widget); +} + +QWSWindowSurface* PvrEglScreen::createSurface(const QString &key) const +{ + if (key == QLatin1String("PvrEgl")) + return new PvrEglWindowSurface(holder); + + return QScreen::createSurface(key); +} + +void PvrEglScreen::sync() +{ + // Put code here to synchronize 2D and 3D operations if necessary. +} + +void PvrEglScreen::openTty() +{ + const char *const devs[] = {"/dev/tty0", "/dev/tty", "/dev/console", 0}; + + if (ttyDevice.isEmpty()) { + for (const char * const *dev = devs; *dev; ++dev) { + ttyfd = ::open(*dev, O_RDWR); + if (ttyfd != -1) + break; + } + } else { + ttyfd = ::open(ttyDevice.toAscii().constData(), O_RDWR); + } + + if (ttyfd == -1) + return; + + ::fcntl(ttyfd, F_SETFD, FD_CLOEXEC); + + if (doGraphicsMode) { + ioctl(ttyfd, KDGETMODE, &oldKdMode); + if (oldKdMode != KD_GRAPHICS) { + int ret = ioctl(ttyfd, KDSETMODE, KD_GRAPHICS); + if (ret == -1) + doGraphicsMode = false; + } + } + + // No blankin' screen, no blinkin' cursor!, no cursor! + const char termctl[] = "\033[9;0]\033[?33l\033[?25l\033[?1c"; + ::write(ttyfd, termctl, sizeof(termctl)); +} + +void PvrEglScreen::closeTty() +{ + if (ttyfd == -1) + return; + + if (doGraphicsMode) + ioctl(ttyfd, KDSETMODE, oldKdMode); + + // Blankin' screen, blinkin' cursor! + const char termctl[] = "\033[9;15]\033[?33h\033[?25h\033[?0c"; + ::write(ttyfd, termctl, sizeof(termctl)); + + ::close(ttyfd); + ttyfd = -1; +} + +bool PvrEglScreenSurfaceFunctions::createNativeWindow(QWidget *widget, EGLNativeWindowType *native) +{ + QWSWindowSurface *surface = + static_cast<QWSWindowSurface *>(widget->windowSurface()); + if (!surface) { + // The widget does not have a surface yet, so give it one. + surface = new PvrEglWindowSurface(widget, screen, displayId); + widget->setWindowSurface(surface); + } else if (surface->key() != QLatin1String("PvrEgl")) { + // The application has attached a QGLContext to an ordinary QWidget. + // Replace the widget's window surface with a new one that can do GL. + QRect geometry = widget->frameGeometry(); + geometry.moveTo(widget->mapToGlobal(QPoint(0, 0))); + surface = new PvrEglWindowSurface(widget, screen, displayId); + surface->setGeometry(geometry); + widget->setWindowSurface(surface); + widget->setAttribute(Qt::WA_NoSystemBackground, true); + } + PvrEglWindowSurface *nsurface = static_cast<PvrEglWindowSurface*>(surface); + *native = (EGLNativeWindowType)(nsurface->nativeDrawable()); + return true; +} + +// The PowerVR engine on the device needs to allocate about 2Mb of +// contiguous physical memory to manage drawing into a surface. +// +// The problem is that once Qtopia begins its startup sequence, +// it allocates enough memory to severely fragment the physical +// address space on the device. This leaves the PowerVR engine +// unable to allocate the necessary contiguous physical memory +// when an EGL surface is created. +// +// A solution to this is to pre-allocate a dummy surface early +// in the startup sequence before memory becomes fragmented, +// reserving it for any future EGL applications to use. +// +// However, the PowerVR engine has problems managing multiple +// surfaces concurrently, and so real EGL applications end up +// with unacceptably slow frame rates unless the dummy surface +// is destroyed while the real EGL applications are running. +// +// In summary, we need to try to ensure that there is always at +// least one EGL surface active at any given time to reserve the +// memory but destroy the temporary surface when a real surface +// is using the device. That is the purpose of PvrEglSurfaceHolder. + +PvrEglSurfaceHolder::PvrEglSurfaceHolder(QObject *parent) + : QObject(parent) +{ + numRealSurfaces = 0; + + PvrQwsRect rect; + rect.x = 0; + rect.y = 0; + rect.width = 16; + rect.height = 16; + tempSurface = pvrQwsCreateWindow(0, -1, &rect); + + dpy = EGL_NO_DISPLAY; + config = 0; + surface = EGL_NO_SURFACE; + + dpy = eglGetDisplay((EGLNativeDisplayType)EGL_DEFAULT_DISPLAY); + if (!eglInitialize(dpy, 0, 0)) { + qWarning("Could not initialize EGL display - are the drivers loaded?"); + dpy = EGL_NO_DISPLAY; + return; + } + + EGLint attribList[16]; + int temp = 0; + attribList[temp++] = EGL_LEVEL; // Framebuffer level 0 + attribList[temp++] = 0; + attribList[temp++] = EGL_SURFACE_TYPE; + attribList[temp++] = EGL_WINDOW_BIT; + attribList[temp++] = EGL_NONE; + + EGLint numConfigs = 0; + if (!eglChooseConfig(dpy, attribList, &config, 1, &numConfigs) || numConfigs != 1) { + qWarning("Could not find a matching a EGL configuration"); + eglTerminate(dpy); + dpy = EGL_NO_DISPLAY; + return; + } + + surface = eglCreateWindowSurface + (dpy, config, (EGLNativeWindowType)(-1), NULL); + if (surface == EGL_NO_SURFACE) + qWarning("Could not create the temporary EGL surface"); +} + +PvrEglSurfaceHolder::~PvrEglSurfaceHolder() +{ + if (surface != EGL_NO_SURFACE) + eglDestroySurface(dpy, surface); + if (dpy != EGL_NO_DISPLAY) + eglTerminate(dpy); + if (tempSurface) + pvrQwsDestroyDrawable(tempSurface); +} + +// Add a real EGL surface to the system. +void PvrEglSurfaceHolder::addSurface() +{ + ++numRealSurfaces; + if (numRealSurfaces == 1) { + // Destroy the temporary surface while some other application + // is making use of the EGL sub-system for 3D rendering. + if (surface != EGL_NO_SURFACE) { + eglDestroySurface(dpy, surface); + surface = EGL_NO_SURFACE; + } + } +} + +// Remove an actual EGL surface from the system. +void PvrEglSurfaceHolder::removeSurface() +{ + if (numRealSurfaces > 0) { + --numRealSurfaces; + if (numRealSurfaces == 0) { + // The last real EGL surface has been destroyed, so re-create + // the temporary surface. There is a race condition here in + // that Qtopia could allocate a lot of memory just after + // the real EGL surface is destroyed but before we could + // create the temporary surface again. + if (surface == EGL_NO_SURFACE && dpy != EGL_NO_DISPLAY) { + surface = eglCreateWindowSurface + (dpy, config, (EGLNativeWindowType)tempSurface, NULL); + if (surface == EGL_NO_SURFACE) + qWarning("Could not re-create the temporary EGL surface"); + } + } + } +} |