blob: fd7f2b328c1063602a04e905d7e113be6dcdb6a1 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtOpenGL module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** 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 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QGL_P_H
#define QGL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of the QGLWidget class. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include "QtOpenGL/qgl.h"
#include "QtOpenGL/qglcolormap.h"
#include "QtCore/qmap.h"
#include "QtCore/qthread.h"
#include "QtCore/qthreadstorage.h"
#include "QtCore/qhash.h"
#include "QtCore/qatomic.h"
#include "QtWidgets/private/qwidget_p.h"
#include "QtGui/private/qopenglcontext_p.h"
#include "QtGui/private/qopenglextensions_p.h"
#include "qcache.h"
#include "qglpaintdevice_p.h"
#include <QtGui/QOpenGLContext>
QT_BEGIN_NAMESPACE
class QGLContext;
class QGLOverlayWidget;
class QPixmap;
class QOpenGLExtensions;
class QGLFormatPrivate
{
public:
QGLFormatPrivate()
: ref(1)
{
opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering
| QGL::StencilBuffer | QGL::DeprecatedFunctions;
pln = 0;
depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1;
numSamples = -1;
swapInterval = -1;
majorVersion = 2;
minorVersion = 0;
profile = QGLFormat::NoProfile;
}
QGLFormatPrivate(const QGLFormatPrivate *other)
: ref(1),
opts(other->opts),
pln(other->pln),
depthSize(other->depthSize),
accumSize(other->accumSize),
stencilSize(other->stencilSize),
redSize(other->redSize),
greenSize(other->greenSize),
blueSize(other->blueSize),
alphaSize(other->alphaSize),
numSamples(other->numSamples),
swapInterval(other->swapInterval),
majorVersion(other->majorVersion),
minorVersion(other->minorVersion),
profile(other->profile)
{
}
QAtomicInt ref;
QGL::FormatOptions opts;
int pln;
int depthSize;
int accumSize;
int stencilSize;
int redSize;
int greenSize;
int blueSize;
int alphaSize;
int numSamples;
int swapInterval;
int majorVersion;
int minorVersion;
QGLFormat::OpenGLContextProfile profile;
};
class Q_OPENGL_EXPORT QGLWidgetPrivate : public QWidgetPrivate
{
Q_DECLARE_PUBLIC(QGLWidget)
public:
QGLWidgetPrivate() : QWidgetPrivate()
, disable_clear_on_painter_begin(false)
, parent_changing(false)
{
}
~QGLWidgetPrivate() {}
void init(QGLContext *context, const QGLWidget* shareWidget);
void initContext(QGLContext *context, const QGLWidget* shareWidget);
bool renderCxPm(QPixmap *pixmap);
void cleanupColormaps();
void aboutToDestroy() Q_DECL_OVERRIDE {
if (glcx && !parent_changing)
glcx->reset();
}
QGLContext *glcx;
QGLWidgetGLPaintDevice glDevice;
bool autoSwap;
QGLColormap cmap;
bool disable_clear_on_painter_begin;
bool parent_changing;
};
// QGLContextPrivate has the responsibility of creating context groups.
// QGLContextPrivate maintains the reference counter and destroys
// context groups when needed.
class QGLContextGroup
{
public:
~QGLContextGroup();
const QGLContext *context() const {return m_context;}
bool isSharing() const { return m_shares.size() >= 2; }
QList<const QGLContext *> shares() const { return m_shares; }
static void addShare(const QGLContext *context, const QGLContext *share);
static void removeShare(const QGLContext *context);
private:
QGLContextGroup(const QGLContext *context);
const QGLContext *m_context; // context group's representative
QList<const QGLContext *> m_shares;
QAtomicInt m_refs;
friend class QGLContext;
friend class QGLContextPrivate;
friend class QGLContextGroupResourceBase;
};
// Get the context that resources for "ctx" will transfer to once
// "ctx" is destroyed. Returns null if nothing is sharing with ctx.
const QGLContext *qt_gl_transfer_context(const QGLContext *);
/*
QGLTemporaryContext - the main objective of this class is to have a way of
creating a GL context and making it current, without going via QGLWidget
and friends. At certain points during GL initialization we need a current
context in order decide what GL features are available, and to resolve GL
extensions. Having a light-weight way of creating such a context saves
initial application startup time, and it doesn't wind up creating recursive
conflicts.
The class currently uses a private d pointer to hide the platform specific
types. This could possibly been done inline with #ifdef'ery, but it causes
major headaches on e.g. X11 due to namespace pollution.
*/
class QGLTemporaryContextPrivate;
class QGLTemporaryContext {
public:
explicit QGLTemporaryContext(bool directRendering = true, QWidget *parent = 0);
~QGLTemporaryContext();
private:
QScopedPointer<QGLTemporaryContextPrivate> d;
};
class QGLTexture;
class QGLTextureDestroyer;
// This probably needs to grow to GL_MAX_VERTEX_ATTRIBS, but 3 is ok for now as that's
// all the GL2 engine uses:
#define QT_GL_VERTEX_ARRAY_TRACKED_COUNT 3
class QGLContextResourceBase;
class QGLContextPrivate
{
Q_DECLARE_PUBLIC(QGLContext)
public:
explicit QGLContextPrivate(QGLContext *context);
~QGLContextPrivate();
QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format,
QGLContext::BindOptions options);
QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key,
QGLContext::BindOptions options);
QGLTexture *bindTexture(const QPixmap &pixmap, GLenum target, GLint format,
QGLContext::BindOptions options);
QGLTexture *textureCacheLookup(const qint64 key, GLenum target);
void init(QPaintDevice *dev, const QGLFormat &format);
int maxTextureSize();
void cleanup();
void setVertexAttribArrayEnabled(int arrayIndex, bool enabled = true);
void syncGlState(); // Makes sure the GL context's state is what we think it is
void swapRegion(const QRegion &region);
QOpenGLContext *guiGlContext;
// true if QGLContext owns the QOpenGLContext (for who deletes who)
bool ownContext;
void setupSharing();
void refreshCurrentFbo();
void setCurrentFbo(GLuint fbo);
QGLFormat glFormat;
QGLFormat reqFormat;
GLuint fbo;
uint valid : 1;
uint sharing : 1;
uint initDone : 1;
uint crWin : 1;
uint internal_context : 1;
uint version_flags_cached : 1;
// workarounds for driver/hw bugs on different platforms
uint workaround_needsFullClearOnEveryFrame : 1;
uint workaround_brokenFBOReadBack : 1;
uint workaround_brokenTexSubImage : 1;
uint workaroundsCached : 1;
uint workaround_brokenTextureFromPixmap : 1;
uint workaround_brokenTextureFromPixmap_init : 1;
uint workaround_brokenAlphaTexSubImage : 1;
uint workaround_brokenAlphaTexSubImage_init : 1;
QPaintDevice *paintDevice;
QSize readback_target_size;
QColor transpColor;
QGLContext *q_ptr;
QGLFormat::OpenGLVersionFlags version_flags;
QGLContextGroup *group;
GLint max_texture_size;
GLuint current_fbo;
GLuint default_fbo;
QPaintEngine *active_engine;
QGLTextureDestroyer *texture_destroyer;
QGLFunctions *functions;
bool vertexAttributeArraysEnabledState[QT_GL_VERTEX_ARRAY_TRACKED_COUNT];
static inline QGLContextGroup *contextGroup(const QGLContext *ctx) { return ctx->d_ptr->group; }
static void setCurrentContext(QGLContext *context);
};
// Temporarily make a context current if not already current or
// shared with the current contex. The previous context is made
// current when the object goes out of scope.
class Q_OPENGL_EXPORT QGLShareContextScope
{
public:
QGLShareContextScope(const QGLContext *ctx)
: m_oldContext(0)
{
QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
if (currentContext != ctx && !QGLContext::areSharing(ctx, currentContext)) {
m_oldContext = currentContext;
m_ctx = const_cast<QGLContext *>(ctx);
m_ctx->makeCurrent();
} else {
m_ctx = currentContext;
}
}
operator QGLContext *()
{
return m_ctx;
}
QGLContext *operator->()
{
return m_ctx;
}
~QGLShareContextScope()
{
if (m_oldContext)
m_oldContext->makeCurrent();
}
private:
QGLContext *m_oldContext;
QGLContext *m_ctx;
};
QT_END_NAMESPACE
Q_DECLARE_METATYPE(GLuint)
QT_BEGIN_NAMESPACE
class Q_OPENGL_EXPORT QGLTextureDestroyer
{
public:
void emitFreeTexture(QGLContext *context, QPlatformPixmap *, GLuint id) {
if (context->contextHandle())
(new QOpenGLSharedResourceGuard(context->contextHandle(), id, freeTextureFunc))->free();
}
private:
static void freeTextureFunc(QOpenGLFunctions *, GLuint id) {
QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &id);
}
};
class Q_OPENGL_EXPORT QGLSignalProxy : public QObject
{
Q_OBJECT
public:
void emitAboutToDestroyContext(const QGLContext *context) {
emit aboutToDestroyContext(context);
}
static QGLSignalProxy *instance();
Q_SIGNALS:
void aboutToDestroyContext(const QGLContext *context);
};
class QGLTexture {
public:
explicit QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D,
QGLContext::BindOptions opt = QGLContext::DefaultBindOption)
: context(ctx),
id(tx_id),
target(tx_target),
options(opt)
{}
~QGLTexture() {
if (options & QGLContext::MemoryManagedBindOption) {
Q_ASSERT(context);
QPlatformPixmap *boundPixmap = 0;
context->d_ptr->texture_destroyer->emitFreeTexture(context, boundPixmap, id);
}
}
QGLContext *context;
GLuint id;
GLenum target;
QGLContext::BindOptions options;
bool canBindCompressedTexture
(const char *buf, int len, const char *format, bool *hasAlpha);
QSize bindCompressedTexture
(const QString& fileName, const char *format = 0);
QSize bindCompressedTexture
(const char *buf, int len, const char *format = 0);
QSize bindCompressedTextureDDS(const char *buf, int len);
QSize bindCompressedTexturePVR(const char *buf, int len);
};
struct QGLTextureCacheKey {
qint64 key;
QGLContextGroup *group;
};
inline bool operator==(const QGLTextureCacheKey &a, const QGLTextureCacheKey &b)
{
return a.key == b.key && a.group == b.group;
}
inline uint qHash(const QGLTextureCacheKey &key)
{
return qHash(key.key) ^ qHash(key.group);
}
class Q_AUTOTEST_EXPORT QGLTextureCache {
public:
QGLTextureCache();
~QGLTextureCache();
void insert(QGLContext *ctx, qint64 key, QGLTexture *texture, int cost);
void remove(qint64 key);
inline int size();
inline void setMaxCost(int newMax);
inline int maxCost();
inline QGLTexture* getTexture(QGLContext *ctx, qint64 key);
bool remove(QGLContext *ctx, GLuint textureId);
void removeContextTextures(QGLContext *ctx);
static QGLTextureCache *instance();
static void cleanupTexturesForCacheKey(qint64 cacheKey);
static void cleanupTexturesForPixampData(QPlatformPixmap* pixmap);
static void cleanupBeforePixmapDestruction(QPlatformPixmap* pixmap);
private:
QCache<QGLTextureCacheKey, QGLTexture> m_cache;
QReadWriteLock m_lock;
};
int QGLTextureCache::size() {
QReadLocker locker(&m_lock);
return m_cache.size();
}
void QGLTextureCache::setMaxCost(int newMax)
{
QWriteLocker locker(&m_lock);
m_cache.setMaxCost(newMax);
}
int QGLTextureCache::maxCost()
{
QReadLocker locker(&m_lock);
return m_cache.maxCost();
}
QGLTexture* QGLTextureCache::getTexture(QGLContext *ctx, qint64 key)
{
// Can't be a QReadLocker since QCache::object() modifies the cache (reprioritizes the object)
QWriteLocker locker(&m_lock);
const QGLTextureCacheKey cacheKey = {key, QGLContextPrivate::contextGroup(ctx)};
return m_cache.object(cacheKey);
}
Q_OPENGL_EXPORT extern QPaintEngine* qt_qgl_paint_engine();
QOpenGLExtensions* qgl_extensions();
bool qgl_hasFeature(QOpenGLFunctions::OpenGLFeature feature);
bool qgl_hasExtension(QOpenGLExtensions::OpenGLExtension extension);
// Put a guard around a GL object identifier and its context.
// When the context goes away, a shared context will be used
// in its place. If there are no more shared contexts, then
// the identifier is returned as zero - it is assumed that the
// context destruction cleaned up the identifier in this case.
class QGLSharedResourceGuardBase : public QOpenGLSharedResource
{
public:
QGLSharedResourceGuardBase(QGLContext *context, GLuint id)
: QOpenGLSharedResource(context->contextHandle()->shareGroup())
, m_id(id)
{
}
GLuint id() const
{
return m_id;
}
protected:
void invalidateResource() Q_DECL_OVERRIDE
{
m_id = 0;
}
void freeResource(QOpenGLContext *context) Q_DECL_OVERRIDE
{
if (m_id) {
freeResource(QGLContext::fromOpenGLContext(context), m_id);
}
}
virtual void freeResource(QGLContext *ctx, GLuint id) = 0;
private:
GLuint m_id;
};
template <typename Func>
class QGLSharedResourceGuard : public QGLSharedResourceGuardBase
{
public:
QGLSharedResourceGuard(QGLContext *context, GLuint id, Func func)
: QGLSharedResourceGuardBase(context, id)
, m_func(func)
{
}
protected:
void freeResource(QGLContext *ctx, GLuint id) Q_DECL_OVERRIDE
{
m_func(ctx, id);
}
private:
Func m_func;
};
template <typename Func>
QGLSharedResourceGuardBase *createSharedResourceGuard(QGLContext *context, GLuint id, Func cleanupFunc)
{
return new QGLSharedResourceGuard<Func>(context, id, cleanupFunc);
}
// this is a class that wraps a QThreadStorage object for storing
// thread local instances of the GL 1 and GL 2 paint engines
template <class T>
class QGLEngineThreadStorage
{
public:
QPaintEngine *engine() {
QPaintEngine *&localEngine = storage.localData();
if (!localEngine)
localEngine = new T;
return localEngine;
}
private:
QThreadStorage<QPaintEngine *> storage;
};
QT_END_NAMESPACE
#endif // QGL_P_H