/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtOpenVG module 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 Technology Preview License Agreement accompanying
** this package.
**
** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwindowsurface_vgegl_p.h"
#include "qpaintengine_vg_p.h"
#include "qpixmapdata_vg_p.h"
#include "qvgimagepool_p.h"
#include "qvg_p.h"
#if !defined(QT_NO_EGL)
QT_BEGIN_NAMESPACE
// Turn off "direct to window" rendering if EGL cannot support it.
#if !defined(EGL_RENDER_BUFFER) || !defined(EGL_SINGLE_BUFFER)
#if defined(QVG_DIRECT_TO_WINDOW)
#undef QVG_DIRECT_TO_WINDOW
#endif
#endif
// Determine if preserved window contents should be used.
#if !defined(EGL_SWAP_BEHAVIOR) || !defined(EGL_BUFFER_PRESERVED)
#if !defined(QVG_NO_PRESERVED_SWAP)
#define QVG_NO_PRESERVED_SWAP 1
#endif
#endif
VGImageFormat qt_vg_config_to_vg_format(QEglContext *context)
{
return qt_vg_image_to_vg_format
(qt_vg_config_to_image_format(context));
}
QImage::Format qt_vg_config_to_image_format(QEglContext *context)
{
EGLint red = 0;
EGLint green = 0;
EGLint blue = 0;
EGLint alpha = 0;
context->configAttrib(EGL_RED_SIZE, &red);
context->configAttrib(EGL_GREEN_SIZE, &green);
context->configAttrib(EGL_BLUE_SIZE, &blue);
context->configAttrib(EGL_ALPHA_SIZE, &alpha);
QImage::Format argbFormat;
#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
EGLint type = 0;
context->configAttrib(EGL_SURFACE_TYPE, &type);
if ((type & EGL_VG_ALPHA_FORMAT_PRE_BIT) != 0)
argbFormat = QImage::Format_ARGB32_Premultiplied;
else
argbFormat = QImage::Format_ARGB32;
#else
argbFormat = QImage::Format_ARGB32;
#endif
if (red == 8 && green == 8 && blue == 8 && alpha == 8)
return argbFormat;
else if (red == 8 && green == 8 && blue == 8 && alpha == 0)
return QImage::Format_RGB32;
else if (red == 5 && green == 6 && blue == 5 && alpha == 0)
return QImage::Format_RGB16;
else if (red == 4 && green == 4 && blue == 4 && alpha == 4)
return QImage::Format_ARGB4444_Premultiplied;
else
return argbFormat; // XXX
}
#if !defined(QVG_NO_SINGLE_CONTEXT)
class QVGSharedContext
{
public:
QVGSharedContext();
~QVGSharedContext();
QEglContext *context;
int refCount;
int widgetRefCount;
QVGPaintEngine *engine;
EGLSurface surface;
QVGPixmapData *firstPixmap;
};
QVGSharedContext::QVGSharedContext()
: context(0)
, refCount(0)
, widgetRefCount(0)
, engine(0)
, surface(EGL_NO_SURFACE)
, firstPixmap(0)
{
}
QVGSharedContext::~QVGSharedContext()
{
// Don't accidentally destroy the QEglContext if the reference
// count falls to zero while deleting the paint engine.
++refCount;
if (context)
context->makeCurrent(qt_vg_shared_surface());
delete engine;
if (context)
context->doneCurrent();
if (context && surface != EGL_NO_SURFACE)
context->destroySurface(surface);
delete context;
}
Q_GLOBAL_STATIC(QVGSharedContext, sharedContext);
QVGPaintEngine *qt_vg_create_paint_engine(void)
{
QVGSharedContext *shared = sharedContext();
if (!shared->engine)
shared->engine = new QVGPaintEngine();
return shared->engine;
}
void qt_vg_destroy_paint_engine(QVGPaintEngine *engine)
{
Q_UNUSED(engine);
}
void qt_vg_register_pixmap(QVGPixmapData *pd)
{
QVGSharedContext *shared = sharedContext();
pd->next = shared->firstPixmap;
pd->prev = 0;
if (shared->firstPixmap)
shared->firstPixmap->prev = pd;
shared->firstPixmap = pd;
}
void qt_vg_unregister_pixmap(QVGPixmapData *pd)
{
if (pd->next)
pd->next->prev = pd->prev;
if (pd->prev) {
pd->prev->next = pd->next;
} else {
QVGSharedContext *shared = sharedContext();
if (shared) {
shared->firstPixmap = pd->next;
}
}
}
#else
QVGPaintEngine *qt_vg_create_paint_engine(void)
{
return new QVGPaintEngine();
}
void qt_vg_destroy_paint_engine(QVGPaintEngine *engine)
{
delete engine;
}
void qt_vg_register_pixmap(QVGPixmapData *pd)
{
Q_UNUSED(pd);
}
void qt_vg_unregister_pixmap(QVGPixmapData *pd)
{
Q_UNUSED(pd);
}
#endif
#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
static bool isPremultipliedContext(const QEglContext *context)
{
EGLint value = 0;
if (context->configAttrib(EGL_SURFACE_TYPE, &value))
return (value & EGL_VG_ALPHA_FORMAT_PRE_BIT) != 0;
else
return false;
}
#endif
static QEglContext *createContext(QPaintDevice *device)
{
QEglContext *context;
// Create the context object and open the display.
context = new QEglContext();
context->setApi(QEgl::OpenVG);
if (!context->openDisplay(device)) {
delete context;
return 0;
}
// Set the swap interval for the display.
QByteArray interval = qgetenv("QT_VG_SWAP_INTERVAL");
if (!interval.isEmpty())
eglSwapInterval(context->display(), interval.toInt());
else
eglSwapInterval(context->display(), 1);
#ifdef EGL_RENDERABLE_TYPE
// Has the user specified an explicit EGL configuration to use?
QByteArray configId = qgetenv("QT_VG_EGL_CONFIG");
if (!configId.isEmpty()) {
EGLint cfgId = configId.toInt();
EGLint properties[] = {
EGL_CONFIG_ID, cfgId,
EGL_NONE
};
EGLint matching = 0;
EGLConfig cfg;
if (eglChooseConfig
(context->display(), properties, &cfg, 1, &matching) &&
matching > 0) {
// Check that the selected configuration actually supports OpenVG
// and then create the context with it.
EGLint id = 0;
EGLint type = 0;
eglGetConfigAttrib
(context->display(), cfg, EGL_CONFIG_ID, &id);
eglGetConfigAttrib
(context->display(), cfg, EGL_RENDERABLE_TYPE, &type);
if (cfgId == id && (type & EGL_OPENVG_BIT) != 0) {
context->setConfig(cfg);
if (!context->createContext()) {
delete context;
return 0;
}
return context;
} else {
qWarning("QT_VG_EGL_CONFIG: %d is not a valid OpenVG configuration", int(cfgId));
}
}
}
#endif
// Choose an appropriate configuration for rendering into the device.
QEglProperties configProps;
configProps.setPaintDeviceFormat(device);
int redSize = configProps.value(EGL_RED_SIZE);
if (redSize == EGL_DONT_CARE || redSize == 0)
configProps.setPixelFormat(QImage::Format_ARGB32); // XXX
#ifndef QVG_SCISSOR_CLIP
// If we are using the mask to clip, then explicitly request a mask.
configProps.setValue(EGL_ALPHA_MASK_SIZE, 1);
#endif
#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT |
EGL_VG_ALPHA_FORMAT_PRE_BIT);
configProps.setRenderableType(QEgl::OpenVG);
if (!context->chooseConfig(configProps)) {
// Try again without the "pre" bit.
configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
if (!context->chooseConfig(configProps)) {
delete context;
return 0;
}
}
#else
configProps.setValue(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
configProps.setRenderableType(QEgl::OpenVG);
if (!context->chooseConfig(configProps)) {
delete context;
return 0;
}
#endif
// Construct a new EGL context for the selected configuration.
if (!context->createContext()) {
delete context;
return 0;
}
return context;
}
#if !defined(QVG_NO_SINGLE_CONTEXT)
QEglContext *qt_vg_create_context(QPaintDevice *device, int devType)
{
QVGSharedContext *shared = sharedContext();
if (devType == QInternal::Widget)
++(shared->widgetRefCount);
if (shared->context) {
++(shared->refCount);
return shared->context;
} else {
shared->context = createContext(device);
shared->refCount = 1;
return shared->context;
}
}
static void qt_vg_destroy_shared_context(QVGSharedContext *shared)
{
shared->context->makeCurrent(qt_vg_shared_surface());
delete shared->engine;
shared->engine = 0;
shared->context->doneCurrent();
if (shared->surface != EGL_NO_SURFACE) {
eglDestroySurface(shared->context->display(), shared->surface);
shared->surface = EGL_NO_SURFACE;
}
delete shared->context;
shared->context = 0;
}
void qt_vg_hibernate_pixmaps(QVGSharedContext *shared)
{
// Artificially increase the reference count to prevent the
// context from being destroyed until after we have finished
// the hibernation process.
++(shared->refCount);
// We need a context current to hibernate the VGImage objects.
shared->context->makeCurrent(qt_vg_shared_surface());
// Scan all QVGPixmapData objects in the system and hibernate them.
QVGPixmapData *pd = shared->firstPixmap;
while (pd != 0) {
pd->hibernate();
pd = pd->next;
}
// Hibernate any remaining VGImage's in the image pool.
QVGImagePool::instance()->hibernate();
// Don't need the current context any more.
shared->context->lazyDoneCurrent();
// Decrease the reference count and destroy the context if necessary.
if (--(shared->refCount) <= 0)
qt_vg_destroy_shared_context(shared);
}
void qt_vg_destroy_context(QEglContext *context, int devType)
{
QVGSharedContext *shared = sharedContext();
if (shared->context != context) {
// This is not the shared context. Shouldn't happen!
delete context;
return;
}
if (devType == QInternal::Widget)
--(shared->widgetRefCount);
if (--(shared->refCount) <= 0) {
qt_vg_destroy_shared_context(shared);
} else if (shared->widgetRefCount <= 0 && devType == QInternal::Widget) {
// All of the widget window surfaces have been destroyed
// but we still have VG pixmaps active. Ask them to hibernate
// to free up GPU resources until a widget is shown again.
// This may eventually cause the EGLContext to be destroyed
// because nothing in the system needs a context, which will
// free up even more GPU resources.
qt_vg_hibernate_pixmaps(shared);
}
}
EGLSurface qt_vg_shared_surface(void)
{
QVGSharedContext *shared = sharedContext();
if (shared->surface == EGL_NO_SURFACE) {
EGLint attribs[7];
attribs[0] = EGL_WIDTH;
attribs[1] = 16;
attribs[2] = EGL_HEIGHT;
attribs[3] = 16;
#ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
if (isPremultipliedContext(shared->context)) {
attribs[4] = EGL_VG_ALPHA_FORMAT;
attribs[5] = EGL_VG_ALPHA_FORMAT_PRE;
attribs[6] = EGL_NONE;
} else
#endif
{
attribs[4] = EGL_NONE;
}
shared->surface = eglCreatePbufferSurface
(shared->context->display(), shared->context->config(), attribs);
}
return shared->surface;
}
#else
QEglContext *qt_vg_create_context(QPaintDevice *device, int devType)
{
Q_UNUSED(devType);
return createContext(device);
}
void qt_vg_destroy_context(QEglContext *context, int devType)
{
Q_UNUSED(devType);
delete context;
}
EGLSurface qt_vg_shared_surface(void)
{
return EGL_NO_SURFACE;
}
#endif
QVGEGLWindowSurfacePrivate::QVGEGLWindowSurfacePrivate(QWindowSurface *win)
{
winSurface = win;
engine = 0;
}
QVGEGLWindowSurfacePrivate::~QVGEGLWindowSurfacePrivate()
{
// Destroy the paint engine if it hasn't been destroyed already.
destroyPaintEngine();
}
QVGPaintEngine *QVGEGLWindowSurfacePrivate::paintEngine()
{
if (!engine)
engine = qt_vg_create_paint_engine();
return engine;
}
VGImage QVGEGLWindowSurfacePrivate::surfaceImage() const
{
return VG_INVALID_HANDLE;
}
void QVGEGLWindowSurfacePrivate::destroyPaintEngine()
{
if (engine) {
qt_vg_destroy_paint_engine(engine);
engine = 0;
}
}
QSize QVGEGLWindowSurfacePrivate::windowSurfaceSize(QWidget *widget) const
{
Q_UNUSED(widget);
QRect rect = winSurface->geometry();
QSize newSize = rect.size();
#if defined(Q_WS_QWS)
// Account for the widget mask, if any.
if (widget && !widget->mask().isEmpty()) {
const QRegion region = widget->mask()
& rect.translated(-widget->geometry().topLeft());
newSize = region.boundingRect().size();
}
#endif
return newSize;
}
#if defined(QVG_VGIMAGE_BACKBUFFERS)
QVGEGLWindowSurfaceVGImage::QVGEGLWindowSurfaceVGImage(QWindowSurface *win)
: QVGEGLWindowSurfacePrivate(win)
, context(0)
, backBuffer(VG_INVALID_HANDLE)
, backBufferSurface(EGL_NO_SURFACE)
, recreateBackBuffer(false)
, isPaintingActive(false)
, windowSurface(EGL_NO_SURFACE)
{
}
QVGEGLWindowSurfaceVGImage::~QVGEGLWindowSurfaceVGImage()
{
destroyPaintEngine();
if (context) {
if (backBufferSurface != EGL_NO_SURFACE) {
// We need a current context to be able to destroy the image.
// We use the shared surface because the native window handle
// associated with "windowSurface" may have been destroyed already.
context->makeCurrent(qt_vg_shared_surface());
context->destroySurface(backBufferSurface);
vgDestroyImage(backBuffer);
context->doneCurrent();
}
if (windowSurface != EGL_NO_SURFACE)
context->destroySurface(windowSurface);
qt_vg_destroy_context(context, QInternal::Widget);
}
}
QEglContext *QVGEGLWindowSurfaceVGImage::ensureContext(QWidget *widget)
{
QSize newSize = windowSurfaceSize(widget);
if (context && size != newSize) {
// The surface size has changed, so we need to recreate
// the back buffer. Keep the same context and paint engine.
size = newSize;
if (isPaintingActive)
context->doneCurrent();
isPaintingActive = false;
recreateBackBuffer = true;
}
if (!context) {
// Create a new EGL context. We create the surface in beginPaint().
size = newSize;
context = qt_vg_create_context(widget, QInternal::Widget);
if (!context)
return 0;
isPaintingActive = false;
}
return context;
}
void QVGEGLWindowSurfaceVGImage::beginPaint(QWidget *widget)
{
QEglContext *context = ensureContext(widget);
if (context) {
if (recreateBackBuffer || backBufferSurface == EGL_NO_SURFACE) {
// Create a VGImage object to act as the back buffer
// for this window. We have to create the VGImage with a
// current context, so activate the main surface for the window.
context->makeCurrent(mainSurface());
recreateBackBuffer = false;
if (backBufferSurface != EGL_NO_SURFACE) {
eglDestroySurface(context->display(), backBufferSurface);
backBufferSurface = EGL_NO_SURFACE;
}
if (backBuffer != VG_INVALID_HANDLE) {
vgDestroyImage(backBuffer);
}
VGImageFormat format = qt_vg_config_to_vg_format(context);
backBuffer = vgCreateImage
(format, size.width(), size.height(),
VG_IMAGE_QUALITY_FASTER);
if (backBuffer != VG_INVALID_HANDLE) {
// Create an EGL surface for rendering into the VGImage.
backBufferSurface = eglCreatePbufferFromClientBuffer
(context->display(), EGL_OPENVG_IMAGE,
(EGLClientBuffer)(backBuffer),
context->config(), NULL);
if (backBufferSurface == EGL_NO_SURFACE) {
vgDestroyImage(backBuffer);
backBuffer = VG_INVALID_HANDLE;
}
}
}
if (backBufferSurface != EGL_NO_SURFACE)
context->makeCurrent(backBufferSurface);
else
context->makeCurrent(mainSurface());
isPaintingActive = true;
}
}
void QVGEGLWindowSurfaceVGImage::endPaint
(QWidget *widget, const QRegion& region, QImage *image)
{
Q_UNUSED(region);
Q_UNUSED(image);
QEglContext *context = ensureContext(widget);
if (context) {
if (backBufferSurface != EGL_NO_SURFACE) {
if (isPaintingActive)
vgFlush();
context->lazyDoneCurrent();
}
isPaintingActive = false;
}
}
VGImage QVGEGLWindowSurfaceVGImage::surfaceImage() const
{
return backBuffer;
}
EGLSurface QVGEGLWindowSurfaceVGImage::mainSurface() const
{
if (windowSurface != EGL_NO_SURFACE)
return windowSurface;
else
return qt_vg_shared_surface();
}
#endif // QVG_VGIMAGE_BACKBUFFERS
QVGEGLWindowSurfaceDirect::QVGEGLWindowSurfaceDirect(QWindowSurface *win)
: QVGEGLWindowSurfacePrivate(win)
, context(0)
, isPaintingActive(false)
, needToSwap(false)
, windowSurface(EGL_NO_SURFACE)
{
}
QVGEGLWindowSurfaceDirect::~QVGEGLWindowSurfaceDirect()
{
destroyPaintEngine();
if (context) {
if (windowSurface != EGL_NO_SURFACE)
context->destroySurface(windowSurface);
qt_vg_destroy_context(context, QInternal::Widget);
}
}
QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget)
{
QSize newSize = windowSurfaceSize(widget);
QEglProperties surfaceProps;
#if defined(QVG_RECREATE_ON_SIZE_CHANGE)
#if !defined(QVG_NO_SINGLE_CONTEXT)
if (context && size != newSize) {
// The surface size has changed, so we need to recreate it.
// We can keep the same context and paint engine.
size = newSize;
if (isPaintingActive)
context->doneCurrent();
context->destroySurface(windowSurface);
#if defined(EGL_VG_ALPHA_FORMAT_PRE_BIT)
if (isPremultipliedContext(context)) {
surfaceProps.setValue
(EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE);
} else {
surfaceProps.removeValue(EGL_VG_ALPHA_FORMAT);
}
#endif
windowSurface = context->createSurface(widget, &surfaceProps);
isPaintingActive = false;
}
#else
if (context && size != newSize) {
// The surface size has changed, so we need to recreate
// the EGL context for the widget. We also need to recreate
// the surface's paint engine if context sharing is not
// enabled because we cannot reuse the existing paint objects
// in the new context.
qt_vg_destroy_paint_engine(engine);
engine = 0;
context->destroySurface(windowSurface);
qt_vg_destroy_context(context, QInternal::Widget);
context = 0;
windowSurface = EGL_NO_SURFACE;
}
#endif
#endif
if (!context) {
// Create a new EGL context and bind it to the widget surface.
size = newSize;
context = qt_vg_create_context(widget, QInternal::Widget);
if (!context)
return 0;
// We want a direct to window rendering surface if possible.
#if defined(QVG_DIRECT_TO_WINDOW)
surfaceProps.setValue(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
#endif
#if defined(EGL_VG_ALPHA_FORMAT_PRE_BIT)
if (isPremultipliedContext(context)) {
surfaceProps.setValue
(EGL_VG_ALPHA_FORMAT, EGL_VG_ALPHA_FORMAT_PRE);
} else {
surfaceProps.removeValue(EGL_VG_ALPHA_FORMAT);
}
#endif
EGLSurface surface = context->createSurface(widget, &surfaceProps);
if (surface == EGL_NO_SURFACE) {
qt_vg_destroy_context(context, QInternal::Widget);
context = 0;
return 0;
}
needToSwap = true;
#if defined(QVG_DIRECT_TO_WINDOW)
// Did we get a direct to window rendering surface?
EGLint buffer = 0;
if (eglQueryContext(context->display(), context->context(),
EGL_RENDER_BUFFER, &buffer) &&
buffer == EGL_SINGLE_BUFFER) {
needToSwap = false;
}
#endif
#if !defined(QVG_NO_PRESERVED_SWAP)
// Try to force the surface back buffer to preserve its contents.
if (needToSwap) {
eglGetError(); // Clear error state first.
eglSurfaceAttrib(context->display(), surface,
EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
if (eglGetError() != EGL_SUCCESS) {
qWarning("QVG: could not enable preserved swap");
}
}
#endif
windowSurface = surface;
isPaintingActive = false;
}
return context;
}
void QVGEGLWindowSurfaceDirect::beginPaint(QWidget *widget)
{
QEglContext *context = ensureContext(widget);
if (context) {
context->makeCurrent(windowSurface);
isPaintingActive = true;
}
}
void QVGEGLWindowSurfaceDirect::endPaint
(QWidget *widget, const QRegion& region, QImage *image)
{
Q_UNUSED(region);
Q_UNUSED(image);
QEglContext *context = ensureContext(widget);
if (context) {
if (needToSwap) {
if (!isPaintingActive)
context->makeCurrent(windowSurface);
context->swapBuffers(windowSurface);
context->lazyDoneCurrent();
} else if (isPaintingActive) {
vgFlush();
context->lazyDoneCurrent();
}
isPaintingActive = false;
}
}
QT_END_NAMESPACE
#endif