1 From dbfdfdb1bc37dd18dd1b723b5d5b0b65c37f3f41 Mon Sep 17 00:00:00 2001
2 From: Gunnar Sletta <gunnar@trolltech.com>
3 Date: Tue, 1 Dec 2009 09:18:47 +0100
4 Subject: [PATCH 0860/1244] Added caching of vectorpaths to the GL paint engine.
6 The first time a path is drawn we call makeCachable on the path, which
7 means that if it is drawn again, we start caching it. This is a bit of
8 a trick to avoid caching paths that are drawn once and discared while
9 at the same time cache paths that are reused automatically.
11 The GL engine owns the vertex information and is responsible for cleaning
12 it up. If the vectorpath is destroyed first, it will call the cleanup function.
13 if the engine dies first, we still require some hooks to clean up the cache
14 in the path. More to come. When VBO's are used, these will be a leaked if the
15 path is destroyed after the engine.
19 src/gui/painting/qpaintengineex.cpp | 16 +++-
20 src/gui/painting/qvectorpath_p.h | 13 ++-
21 src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h | 2 +
22 .../gl2paintengineex/qpaintengineex_opengl2.cpp | 115 +++++++++++++++++++-
23 .../gl2paintengineex/qpaintengineex_opengl2_p.h | 4 +
24 5 files changed, 139 insertions(+), 11 deletions(-)
26 diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
27 index 7d1c109..9a0e319 100644
28 --- a/src/gui/painting/qpaintengineex.cpp
29 +++ b/src/gui/painting/qpaintengineex.cpp
30 @@ -56,6 +56,20 @@ QT_BEGIN_NAMESPACE
34 +QVectorPath::~QVectorPath()
36 + if (m_hints & ShouldUseCacheHint) {
37 + CacheEntry *e = m_cache;
40 + e->cleanup(e->engine, e->data);
41 + CacheEntry *n = e->next;
49 QRectF QVectorPath::controlPointRect() const
51 @@ -94,7 +108,7 @@ QRectF QVectorPath::controlPointRect() const
54 QVectorPath::CacheEntry *QVectorPath::addCacheData(QPaintEngineEx *engine, void *data,
55 - qvectorpath_cache_cleanup cleanup) {
56 + qvectorpath_cache_cleanup cleanup) const{
57 Q_ASSERT(!lookupCacheData(engine));
58 if ((m_hints & IsCachedHint) == 0) {
60 diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h
61 index ec27970..5eaddf4 100644
62 --- a/src/gui/painting/qvectorpath_p.h
63 +++ b/src/gui/painting/qvectorpath_p.h
64 @@ -68,7 +68,7 @@ QT_MODULE(Gui)
68 -typedef void (*qvectorpath_cache_cleanup)(void *data);
69 +typedef void (*qvectorpath_cache_cleanup)(QPaintEngineEx *engine, void *data);
73 @@ -118,6 +118,8 @@ public:
79 QRectF controlPointRect() const;
81 inline Hint shape() const { return (Hint) (m_hints & ShapeMask); }
82 @@ -128,6 +130,7 @@ public:
83 inline bool hasImplicitClose() const { return m_hints & ImplicitClose; }
84 inline bool hasWindingFill() const { return m_hints & WindingFill; }
86 + inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = 0; }
87 inline uint hints() const { return m_hints; }
89 inline const QPainterPath::ElementType *elements() const { return m_elements; }
90 @@ -146,9 +149,9 @@ public:
94 - CacheEntry *addCacheData(QPaintEngineEx *engine, void *data, qvectorpath_cache_cleanup cleanup);
95 + CacheEntry *addCacheData(QPaintEngineEx *engine, void *data, qvectorpath_cache_cleanup cleanup) const;
96 inline CacheEntry *lookupCacheData(QPaintEngineEx *engine) const {
97 - Q_ASSERT(m_hints & IsCachedHint);
98 + Q_ASSERT(m_hints & ShouldUseCacheHint);
99 CacheEntry *e = m_cache;
101 if (e->engine == engine)
102 @@ -162,14 +165,14 @@ public:
104 Q_DISABLE_COPY(QVectorPath)
106 - CacheEntry *m_cache;
108 const QPainterPath::ElementType *m_elements;
109 const qreal *m_points;
112 mutable uint m_hints;
113 mutable QRealRect m_cp_rect;
115 + mutable CacheEntry *m_cache;
118 Q_GUI_EXPORT const QVectorPath &qtVectorPathForPath(const QPainterPath &path);
119 diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
120 index 03aec17..98eaa91 100644
121 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
122 +++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h
123 @@ -112,6 +112,8 @@ public:
124 int stopCount() const { return vertexArrayStops.size(); }
125 QGLRect boundingRect() const;
127 + int vertexCount() const { return vertexArray.size(); }
129 void lineToArray(const GLfloat x, const GLfloat y);
132 diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
133 index 6a708b4..3fce384 100644
134 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
135 +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
137 and use the correct program when we really need it.
140 +// #define QT_OPENGL_CACHE_AS_VBOS
142 #include "qpaintengineex_opengl2_p.h"
144 #include <string.h> //for memcpy
145 @@ -344,6 +346,13 @@ extern QImage qt_imageForBrush(int brushStyle, bool invert);
146 QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
148 delete shaderManager;
150 + while (pathCaches.size()) {
151 + QVectorPath::CacheEntry *e = *(pathCaches.constBegin());
152 + e->cleanup(e->engine, e->data);
158 void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
159 @@ -846,6 +855,30 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
163 +struct QGL2PEVectorPathCache
165 +#ifdef QT_OPENGL_CACHE_AS_VBOS
171 + GLenum primitiveType;
175 +void qopengl2paintengine_cleanup_vectorpath(QPaintEngineEx *engine, void *data)
177 + QGL2PEVectorPathCache *c = (QGL2PEVectorPathCache *) data;
178 +#ifdef QT_OPENGL_CACHE_AS_VBOS
179 + QGL2PaintEngineExPrivate *d = QGL2PaintEngineExPrivate::getData((QGL2PaintEngineEx *) engine);
180 + d->unusedVBOSToClean << c->vbo;
182 + qFree(c->vertices);
187 // Assumes everything is configured for the brush you want to use
188 void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
190 @@ -863,10 +896,74 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
191 prepareForDraw(currentBrush->isOpaque());
193 } else if (path.isConvex()) {
194 - vertexCoordinateArray.clear();
195 - vertexCoordinateArray.addPath(path, inverseScale, false);
196 - prepareForDraw(currentBrush->isOpaque());
197 - drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
199 + if (path.isCacheable()) {
200 + QVectorPath::CacheEntry *data = path.lookupCacheData(q);
201 + QGL2PEVectorPathCache *cache;
204 + cache = (QGL2PEVectorPathCache *) data->data;
205 + // Check if scale factor is exceeded for curved paths and generate curves if so...
206 + if (path.isCurved()) {
207 + qreal scaleFactor = cache->iscale / inverseScale;
208 + if (scaleFactor < 0.5 || scaleFactor > 2.0) {
209 +#ifdef QT_OPENGL_CACHE_AS_VBOS
210 + glDeleteBuffers(1, &cache->vbo);
213 + qFree(cache->vertices);
215 + cache->vertexCount = 0;
219 + cache = new QGL2PEVectorPathCache;
220 + cache->vertexCount = 0;
221 + data = const_cast<QVectorPath &>(path).addCacheData(q, cache, qopengl2paintengine_cleanup_vectorpath);
224 + // Flatten the path at the current scale factor and fill it into the cache struct.
225 + if (!cache->vertexCount) {
226 + vertexCoordinateArray.clear();
227 + vertexCoordinateArray.addPath(path, inverseScale, false);
228 + int vertexCount = vertexCoordinateArray.vertexCount();
229 + int floatSizeInBytes = vertexCount * 2 * sizeof(float);
230 + cache->vertexCount = vertexCount;
231 + cache->primitiveType = GL_TRIANGLE_FAN;
232 + cache->iscale = inverseScale;
233 +#ifdef QT_OPENGL_CACHE_AS_VBOS
234 + glGenBuffers(1, &cache->vbo);
235 + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
236 + glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW);
238 + cache->vertices = (float *) qMalloc(floatSizeInBytes);
239 + memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes);
243 + prepareForDraw(currentBrush->isOpaque());
244 + glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR);
245 +#ifdef QT_OPENGL_CACHE_AS_VBOS
246 + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo);
247 + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, 0);
249 + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, false, 0, cache->vertices);
251 + glDrawArrays(cache->primitiveType, 0, cache->vertexCount);
254 + // printf(" - Marking path as cachable...\n");
255 + // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable
256 + // ### Remove before release...
257 + static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty();
258 + if (do_vectorpath_cache)
259 + path.makeCacheable();
260 + vertexCoordinateArray.clear();
261 + vertexCoordinateArray.addPath(path, inverseScale, false);
262 + prepareForDraw(currentBrush->isOpaque());
263 + drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
267 // The path is too complicated & needs the stencil technique
268 vertexCoordinateArray.clear();
269 @@ -1756,7 +1853,8 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
270 d->device->beginPaint();
272 #if !defined(QT_OPENGL_ES_2)
273 - bool success = qt_resolve_version_2_0_functions(d->ctx);
274 + bool success = qt_resolve_version_2_0_functions(d->ctx)
275 + && qt_resolve_buffer_extensions(d->ctx);
279 @@ -1817,6 +1915,13 @@ bool QGL2PaintEngineEx::end()
280 delete d->shaderManager;
281 d->shaderManager = 0;
283 +#ifdef QT_OPENGL_CACHE_AS_VBOS
284 + if (!d->unusedVBOSToClean.isEmpty()) {
285 + glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData());
286 + d->unusedVBOSToClean.clear();
293 diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
294 index b554f6d..0084476 100644
295 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
296 +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
297 @@ -221,6 +221,7 @@ public:
298 void restoreDepthRangeForRenderText();
300 static QGLEngineShaderManager* shaderManagerForEngine(QGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; }
301 + static QGL2PaintEngineExPrivate *getData(QGL2PaintEngineEx *engine) { return engine->d_func(); }
303 QGL2PaintEngineEx* q;
304 QGLPaintDevice* device;
305 @@ -294,6 +295,9 @@ public:
306 QScopedPointer<QPixmapFilter> fastBlurFilter;
307 QScopedPointer<QPixmapFilter> dropShadowFilter;
308 QScopedPointer<QPixmapFilter> fastDropShadowFilter;
310 + QSet<QVectorPath::CacheEntry *> pathCaches;
311 + QVector<GLuint> unusedVBOSToClean;