Merge lp:~loic.molinari/qtmir/qtmir-custom-mirsurfacenode into lp:qtmir
- qtmir-custom-mirsurfacenode
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~loic.molinari/qtmir/qtmir-custom-mirsurfacenode |
Merge into: | lp:qtmir |
Diff against target: |
1279 lines (+1095/-21) 13 files modified
CMakeLists.txt (+2/-1) src/modules/Unity/Application/CMakeLists.txt (+2/-0) src/modules/Unity/Application/mirsurfaceitem.cpp (+44/-20) src/modules/Unity/Application/mirsurfaceitem.h (+11/-0) src/modules/Unity/Application/mirsurfacenode.cpp (+736/-0) src/modules/Unity/Application/mirsurfacenode.h (+55/-0) src/modules/Unity/Application/resources.qrc (+10/-0) src/modules/Unity/Application/shaders/edge.frag (+24/-0) src/modules/Unity/Application/shaders/edge.vert (+74/-0) src/modules/Unity/Application/shaders/fill.frag (+24/-0) src/modules/Unity/Application/shaders/fill.vert (+26/-0) src/modules/Unity/Application/shaders/offsetfill.vert (+64/-0) src/modules/Unity/Application/shaders/opaquefill.frag (+23/-0) |
To merge this branch: | bzr merge lp:~loic.molinari/qtmir/qtmir-custom-mirsurfacenode |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir development team | Pending | ||
Review via email: mp+274869@code.launchpad.net |
Commit message
Switched to a custom mir surface node.
MirSurfaceNode is a Qt scene graph node that renders a texture with a given filtering. It can be compared to the image node provided by QtQuick (although simpler) with the addition of a more efficient antialiasing technique that keeps the same quality.
QtQuick provides 2 different antialiasing techniques. The first one is called multisampling and is proposed by the underlying hardware. It can be enabled at window creation time by defining a number of samples on the QSurfaceFormat associated to the created QtQuick scene (window). The efficiency of that technique is highly dependent on the hardware and often not adapted. The second technique is called vertex antialiasing, when the antialiasing property is set to true on a QtQuick item, QtQuick switches to that technique which adds 4 new vertices with additional attributes and lets the vertex shader accurately set their position and opacity fall-off. The drawback of this technique is that it forces the whole node to be rendered in the translucent pass of the renderer even though the whole content of the node is opaque, another drawback is that it oten prevents batching.
The antialiasing technique provided by this node prevents both issues by creating 2 geometry nodes, a FillNode that is made to fill the content and an EdgeNode that is made to render the antialiased edge. The edge node uses exactly the same vertex-based algorithm than the default QtQuick nodes.
This technique is perfectly adapted to the rendering of the Spread, where there is quite a lot of overdraw, by bringing back the ability for GPUs to use early-Z cull optimisations.
This is a first step and the performance boost implied by that change can't be noticed directly since the surface nodes are currently always backing ARGB surfaces. This QtMir branch (lp:~unity-team/qtubuntu/requested-surface-format-fix) will fix that.
Description of the change
Switched to a custom mir surface node.
MirSurfaceNode is a Qt scene graph node that renders a texture with a given filtering. It can be compared to the image node provided by QtQuick (although simpler) with the addition of a more efficient antialiasing technique that keeps the same quality.
QtQuick provides 2 different antialiasing techniques. The first one is called multisampling and is proposed by the underlying hardware. It can be enabled at window creation time by defining a number of samples on the QSurfaceFormat associated to the created QtQuick scene (window). The efficiency of that technique is highly dependent on the hardware and often not adapted. The second technique is called vertex antialiasing, when the antialiasing property is set to true on a QtQuick item, QtQuick switches to that technique which adds 4 new vertices with additional attributes and lets the vertex shader accurately set their position and opacity fall-off. The drawback of this technique is that it forces the whole node to be rendered in the translucent pass of the renderer even though the whole content of the node is opaque, another drawback is that it oten prevents batching.
The antialiasing technique provided by this node prevents both issues by creating 2 geometry nodes, a FillNode that is made to fill the content and an EdgeNode that is made to render the antialiased edge. The edge node uses exactly the same vertex-based algorithm than the default QtQuick nodes.
This technique is perfectly adapted to the rendering of the Spread, where there is quite a lot of overdraw, by bringing back the ability for GPUs to use early-Z cull optimisations.
This is a first step and the performance boost implied by that change can't be noticed directly since the surface nodes are currently always backing ARGB surfaces. This QtMir branch (lp:~unity-team/qtubuntu/requested-surface-format-fix) will fix that.
- 386. By Loïc Molinari
-
Converted comment to 80 chars line wrap as expected by Qt coding guidelines.
Unmerged revisions
- 386. By Loïc Molinari
-
Converted comment to 80 chars line wrap as expected by Qt coding guidelines.
- 385. By Loïc Molinari
-
Switched to a custom MirSurfaceNode.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-09-17 20:17:17 +0000 |
3 | +++ CMakeLists.txt 2015-10-19 10:47:33 +0000 |
4 | @@ -16,8 +16,9 @@ |
5 | # add custom cmake modules |
6 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) |
7 | |
8 | -# Instruct CMake to run moc automatically when needed. |
9 | +# Instruct CMake to run moc and rcc automatically when needed. |
10 | set(CMAKE_AUTOMOC ON) |
11 | +set(CMAKE_AUTORCC ON) |
12 | |
13 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror") |
14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fno-strict-aliasing -Werror -Wextra") |
15 | |
16 | === modified file 'src/modules/Unity/Application/CMakeLists.txt' |
17 | --- src/modules/Unity/Application/CMakeLists.txt 2015-09-17 16:17:17 +0000 |
18 | +++ src/modules/Unity/Application/CMakeLists.txt 2015-10-19 10:47:33 +0000 |
19 | @@ -36,6 +36,7 @@ |
20 | ubuntukeyboardinfo.cpp |
21 | mirsurface.cpp |
22 | mirsurfaceinterface.h |
23 | + mirsurfacenode.cpp |
24 | mirsurfaceitem.cpp |
25 | mirbuffersgtexture.cpp |
26 | proc_info.cpp |
27 | @@ -59,6 +60,7 @@ |
28 | |
29 | add_library(unityapplicationplugin SHARED |
30 | ${QMLMIRPLUGIN_SRC} |
31 | + resources.qrc |
32 | ) |
33 | |
34 | target_link_libraries( |
35 | |
36 | === modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp' |
37 | --- src/modules/Unity/Application/mirsurfaceitem.cpp 2015-09-28 14:24:48 +0000 |
38 | +++ src/modules/Unity/Application/mirsurfaceitem.cpp 2015-10-19 10:47:33 +0000 |
39 | @@ -17,6 +17,7 @@ |
40 | // local |
41 | #include "application.h" |
42 | #include "session.h" |
43 | +#include "mirsurfacenode.h" |
44 | #include "mirsurfaceitem.h" |
45 | #include "logging.h" |
46 | #include "ubuntukeyboardinfo.h" |
47 | @@ -31,7 +32,6 @@ |
48 | #include <QQmlEngine> |
49 | #include <QQuickWindow> |
50 | #include <QScreen> |
51 | -#include <private/qsgdefaultimagenode_p.h> |
52 | #include <QTimer> |
53 | #include <QSGTextureProvider> |
54 | |
55 | @@ -89,6 +89,7 @@ |
56 | , m_surfaceHeight(0) |
57 | , m_orientationAngle(nullptr) |
58 | , m_consumesInput(false) |
59 | + , m_flags(0) |
60 | { |
61 | qCDebug(QTMIR_SURFACES) << "MirSurfaceItem::MirSurfaceItem"; |
62 | |
63 | @@ -104,6 +105,7 @@ |
64 | connect(&m_updateMirSurfaceSizeTimer, &QTimer::timeout, this, &MirSurfaceItem::updateMirSurfaceSize); |
65 | |
66 | connect(this, &QQuickItem::activeFocusChanged, this, &MirSurfaceItem::updateMirSurfaceFocus); |
67 | + connect(this, &QQuickItem::smoothChanged, this, &MirSurfaceItem::onSmoothChanged); |
68 | } |
69 | |
70 | MirSurfaceItem::~MirSurfaceItem() |
71 | @@ -173,6 +175,15 @@ |
72 | return m_surface && m_surface->live(); |
73 | } |
74 | |
75 | +void MirSurfaceItem::itemChange(ItemChange change, const ItemChangeData &data) |
76 | +{ |
77 | + if (change == ItemAntialiasingHasChanged) { |
78 | + m_flags |= DirtyAntialiasing; |
79 | + update(); |
80 | + } |
81 | + QQuickItem::itemChange(change, data); |
82 | +} |
83 | + |
84 | // Called from the rendering (scene graph) thread |
85 | QSGTextureProvider *MirSurfaceItem::textureProvider() const |
86 | { |
87 | @@ -229,34 +240,40 @@ |
88 | |
89 | m_textureProvider->smooth = smooth(); |
90 | |
91 | - QSGDefaultImageNode *node = static_cast<QSGDefaultImageNode*>(oldNode); |
92 | - if (!node) { |
93 | - node = new QSGDefaultImageNode; |
94 | - node->setTexture(m_textureProvider->texture()); |
95 | - |
96 | - node->setMipmapFiltering(QSGTexture::None); |
97 | - node->setHorizontalWrapMode(QSGTexture::ClampToEdge); |
98 | - node->setVerticalWrapMode(QSGTexture::ClampToEdge); |
99 | - node->setSubSourceRect(QRectF(0, 0, 1, 1)); |
100 | + MirSurfaceNode *node; |
101 | + if (oldNode) { |
102 | + node = static_cast<MirSurfaceNode*>(oldNode); |
103 | + if (m_flags & DirtyFiltering) { |
104 | + node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); |
105 | + } |
106 | + if (m_flags & DirtyAntialiasing) { |
107 | + node->setAntialiasing(antialiasing()); |
108 | + } |
109 | + // FIXME(loicm) This code was here before the switch to a dedicated node |
110 | + // and it is maintained as it is to prevent potential issues, but I'm |
111 | + // not sure if it really makes sense to explicitly mark the material as |
112 | + // dirty since the texture id stays the same and the node will be |
113 | + // rendered after this update pass anyway. Commenting it out seems not |
114 | + // to change anything, so that would need to be investigated IMO. |
115 | + if (!m_lastFrameNumberRendered |
116 | + || (*m_lastFrameNumberRendered != m_surface->currentFrameNumber())) { |
117 | + node->updateMaterial(); |
118 | + } |
119 | } else { |
120 | - if (!m_lastFrameNumberRendered || (*m_lastFrameNumberRendered != m_surface->currentFrameNumber())) { |
121 | - node->markDirty(QSGNode::DirtyMaterial); |
122 | - } |
123 | + node = new MirSurfaceNode( |
124 | + m_textureProvider->texture(), |
125 | + smooth() ? QSGTexture::Linear : QSGTexture::Nearest, antialiasing()); |
126 | } |
127 | |
128 | - node->setTargetRect(QRectF(0, 0, width(), height())); |
129 | - node->setInnerTargetRect(QRectF(0, 0, width(), height())); |
130 | - |
131 | - node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); |
132 | - node->setAntialiasing(antialiasing()); |
133 | - |
134 | - node->update(); |
135 | + node->updateGeometry(width(), height()); |
136 | |
137 | if (!m_lastFrameNumberRendered) { |
138 | m_lastFrameNumberRendered = new unsigned int; |
139 | } |
140 | *m_lastFrameNumberRendered = m_surface->currentFrameNumber(); |
141 | |
142 | + m_flags = 0; |
143 | + |
144 | return node; |
145 | } |
146 | |
147 | @@ -676,6 +693,13 @@ |
148 | setImplicitSize(size.width(), size.height()); |
149 | } |
150 | |
151 | +void MirSurfaceItem::onSmoothChanged(bool smooth) |
152 | +{ |
153 | + Q_UNUSED(smooth); |
154 | + m_flags |= DirtyFiltering; |
155 | + update(); |
156 | +} |
157 | + |
158 | int MirSurfaceItem::surfaceHeight() const |
159 | { |
160 | return m_surfaceHeight; |
161 | |
162 | === modified file 'src/modules/Unity/Application/mirsurfaceitem.h' |
163 | --- src/modules/Unity/Application/mirsurfaceitem.h 2015-09-18 20:11:50 +0000 |
164 | +++ src/modules/Unity/Application/mirsurfaceitem.h 2015-10-19 10:47:33 +0000 |
165 | @@ -103,6 +103,8 @@ |
166 | |
167 | void touchEvent(QTouchEvent *event) override; |
168 | |
169 | + void itemChange(ItemChange change, const ItemChangeData &data); |
170 | + |
171 | QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *); |
172 | |
173 | void releaseResources() override; |
174 | @@ -115,7 +117,14 @@ |
175 | |
176 | void onActualSurfaceSizeChanged(const QSize &size); |
177 | |
178 | + void onSmoothChanged(bool smooth); |
179 | + |
180 | private: |
181 | + enum { |
182 | + DirtyFiltering = (1 << 0), |
183 | + DirtyAntialiasing = (1 << 1) |
184 | + }; |
185 | + |
186 | void ensureTextureProvider(); |
187 | |
188 | bool hasTouchInsideUbuntuKeyboard(const QList<QTouchEvent::TouchPoint> &touchPoints); |
189 | @@ -163,6 +172,8 @@ |
190 | Mir::OrientationAngle *m_orientationAngle; |
191 | |
192 | bool m_consumesInput; |
193 | + |
194 | + quint8 m_flags; |
195 | }; |
196 | |
197 | } // namespace qtmir |
198 | |
199 | === added file 'src/modules/Unity/Application/mirsurfacenode.cpp' |
200 | --- src/modules/Unity/Application/mirsurfacenode.cpp 1970-01-01 00:00:00 +0000 |
201 | +++ src/modules/Unity/Application/mirsurfacenode.cpp 2015-10-19 10:47:33 +0000 |
202 | @@ -0,0 +1,736 @@ |
203 | +/* |
204 | + * Copyright (C) 2015 Canonical, Ltd. |
205 | + * |
206 | + * This program is free software: you can redistribute it and/or modify it under |
207 | + * the terms of the GNU Lesser General Public License version 3, as published by |
208 | + * the Free Software Foundation. |
209 | + * |
210 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
211 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
212 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
213 | + * Lesser General Public License for more details. |
214 | + * |
215 | + * You should have received a copy of the GNU Lesser General Public License |
216 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
217 | + */ |
218 | + |
219 | +#include "mirsurfacenode.h" |
220 | +#include <QtQuick/QSGMaterial> |
221 | + |
222 | +// --- Vertex layouts --- |
223 | + |
224 | +struct Vertex { |
225 | + float position[2]; |
226 | + float textureCoordinate[2]; |
227 | +}; |
228 | + |
229 | +struct OffsetVertex { |
230 | + float position[2]; |
231 | + float textureCoordinate[2]; |
232 | + float positionOffset[2]; |
233 | + float textureCoordinateOffset[2]; |
234 | +}; |
235 | + |
236 | +// --- Shader classes --- |
237 | + |
238 | +class OpaqueFillShader : public QSGMaterialShader |
239 | +{ |
240 | +public: |
241 | + OpaqueFillShader(); |
242 | + virtual char const *const *attributeNames() const; |
243 | + virtual void initialize(); |
244 | + virtual void updateState( |
245 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); |
246 | + |
247 | +private: |
248 | + int m_matrixId; |
249 | +}; |
250 | + |
251 | +class FillShader : public OpaqueFillShader |
252 | +{ |
253 | +public: |
254 | + FillShader(); |
255 | + virtual void initialize(); |
256 | + virtual void updateState( |
257 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); |
258 | + |
259 | +private: |
260 | + int m_opacityId; |
261 | +}; |
262 | + |
263 | +class OffsetOpaqueFillShader : public OpaqueFillShader |
264 | +{ |
265 | +public: |
266 | + OffsetOpaqueFillShader(); |
267 | + virtual char const *const *attributeNames() const; |
268 | + virtual void initialize(); |
269 | + virtual void updateState( |
270 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); |
271 | + |
272 | +private: |
273 | + int m_pixelSizeId; |
274 | +}; |
275 | + |
276 | +class OffsetFillShader : public FillShader |
277 | +{ |
278 | +public: |
279 | + OffsetFillShader(); |
280 | + virtual char const *const *attributeNames() const; |
281 | + virtual void initialize(); |
282 | + virtual void updateState( |
283 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); |
284 | + |
285 | +private: |
286 | + int m_pixelSizeId; |
287 | +}; |
288 | + |
289 | +class EdgeShader : public OffsetFillShader |
290 | +{ |
291 | +public: |
292 | + EdgeShader(); |
293 | +}; |
294 | + |
295 | +// --- Material classes --- |
296 | + |
297 | +class OpaqueFillMaterial : public QSGMaterial |
298 | +{ |
299 | +public: |
300 | + OpaqueFillMaterial(QSGTexture *texture, QSGTexture::Filtering filtering); |
301 | + virtual QSGMaterialType *type() const; |
302 | + virtual QSGMaterialShader *createShader() const; |
303 | + virtual int compare(const QSGMaterial *other) const; |
304 | + |
305 | + QSGTexture *texture() const { return m_texture; } |
306 | + QSGTexture::Filtering filtering() const { return m_filtering; } |
307 | + void setFiltering(QSGTexture::Filtering filtering) { m_filtering = filtering; } |
308 | + |
309 | +private: |
310 | + QSGTexture *m_texture; |
311 | + QSGTexture::Filtering m_filtering; |
312 | +}; |
313 | + |
314 | +class FillMaterial : public OpaqueFillMaterial |
315 | +{ |
316 | +public: |
317 | + FillMaterial(QSGTexture *texture, QSGTexture::Filtering filtering); |
318 | + virtual QSGMaterialType *type() const; |
319 | + virtual QSGMaterialShader *createShader() const; |
320 | +}; |
321 | + |
322 | +class OffsetOpaqueFillMaterial : public OpaqueFillMaterial |
323 | +{ |
324 | +public: |
325 | + OffsetOpaqueFillMaterial(QSGTexture *texture, QSGTexture::Filtering filtering); |
326 | + virtual QSGMaterialType *type() const; |
327 | + virtual QSGMaterialShader *createShader() const; |
328 | +}; |
329 | + |
330 | +class OffsetFillMaterial : public FillMaterial |
331 | +{ |
332 | +public: |
333 | + OffsetFillMaterial(QSGTexture *texture, QSGTexture::Filtering filtering); |
334 | + virtual QSGMaterialType *type() const; |
335 | + virtual QSGMaterialShader *createShader() const; |
336 | +}; |
337 | + |
338 | +class EdgeMaterial : public OpaqueFillMaterial |
339 | +{ |
340 | +public: |
341 | + EdgeMaterial(QSGTexture *texture, QSGTexture::Filtering filtering); |
342 | + virtual QSGMaterialType *type() const; |
343 | + virtual QSGMaterialShader *createShader() const; |
344 | +}; |
345 | + |
346 | +// --- Node classes --- |
347 | + |
348 | +class FillNode : public QSGGeometryNode |
349 | +{ |
350 | +public: |
351 | + FillNode(QSGTexture *texture, QSGTexture::Filtering filtering, bool offset); |
352 | + |
353 | + void setOffset(bool offset); |
354 | + void setFiltering(QSGTexture::Filtering filtering); |
355 | + void updateGeometry(float width, float height); |
356 | + |
357 | +private: |
358 | + static const unsigned short *indices(); |
359 | + static const QSGGeometry::AttributeSet &attributeSet(); |
360 | + static const QSGGeometry::AttributeSet &offsetAttributeSet(); |
361 | + |
362 | + enum { Offset = (1 << 0) }; |
363 | + |
364 | + OpaqueFillMaterial m_opaqueMaterial; |
365 | + FillMaterial m_material; |
366 | + OffsetOpaqueFillMaterial m_offsetOpaqueMaterial; |
367 | + OffsetFillMaterial m_offsetMaterial; |
368 | + QSGGeometry m_geometry; |
369 | + QSGGeometry m_offsetGeometry; |
370 | + quint8 m_flags; |
371 | +}; |
372 | + |
373 | +class EdgeNode : public QSGGeometryNode |
374 | +{ |
375 | +public: |
376 | + EdgeNode(QSGTexture *texture, QSGTexture::Filtering filtering, bool visible); |
377 | + virtual bool isSubtreeBlocked() const { return !!(m_flags & Visible); } |
378 | + |
379 | + void setVisible(bool visible); |
380 | + void setFiltering(QSGTexture::Filtering filtering); |
381 | + void updateGeometry(float width, float height); |
382 | + |
383 | +private: |
384 | + static const unsigned short *indices(); |
385 | + static const QSGGeometry::AttributeSet &attributeSet(); |
386 | + |
387 | + enum { Visible = (1 << 0) }; |
388 | + |
389 | + EdgeMaterial m_material; |
390 | + QSGGeometry m_geometry; |
391 | + quint8 m_flags; |
392 | +}; |
393 | + |
394 | +// --- Opaque fill shader --- |
395 | + |
396 | +OpaqueFillShader::OpaqueFillShader() |
397 | +{ |
398 | + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/shaders/fill.vert")); |
399 | + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/shaders/opaquefill.frag")); |
400 | +} |
401 | + |
402 | +char const *const *OpaqueFillShader::attributeNames() const |
403 | +{ |
404 | + static char const *const attributes[] = { |
405 | + "positionAttrib", "textureCoordAttrib", 0 |
406 | + }; |
407 | + return attributes; |
408 | +} |
409 | + |
410 | +void OpaqueFillShader::initialize() |
411 | +{ |
412 | + QSGMaterialShader::initialize(); |
413 | + program()->bind(); |
414 | + program()->setUniformValue("texture", 0); |
415 | + m_matrixId = program()->uniformLocation("matrix"); |
416 | +} |
417 | + |
418 | +void OpaqueFillShader::updateState( |
419 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
420 | +{ |
421 | + Q_UNUSED(oldEffect); |
422 | + |
423 | + // Bind and update texturing states. Note that (as mentioned in the scene |
424 | + // graph doc and as it is also expected in the standard texture materials) |
425 | + // getting a NULL texture pointer here is the result of a buggy node |
426 | + // implementation. |
427 | + OpaqueFillMaterial *material = static_cast<OpaqueFillMaterial*>(newEffect); |
428 | + QSGTexture *texture = material->texture(); |
429 | + Q_ASSERT_X(texture, "updateState", "NULL texture pointer, node must be fixed"); |
430 | + texture->setFiltering(material->filtering()); |
431 | + texture->bind(); |
432 | + |
433 | + if (state.isMatrixDirty()) { |
434 | + program()->setUniformValue(m_matrixId, state.combinedMatrix()); |
435 | + } |
436 | +} |
437 | + |
438 | +// --- Fill shader --- |
439 | + |
440 | +FillShader::FillShader() |
441 | + : OpaqueFillShader() |
442 | +{ |
443 | + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/shaders/fill.frag")); |
444 | +} |
445 | + |
446 | +void FillShader::initialize() |
447 | +{ |
448 | + OpaqueFillShader::initialize(); |
449 | + m_opacityId = program()->uniformLocation("opacity"); |
450 | +} |
451 | + |
452 | +void FillShader::updateState( |
453 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
454 | +{ |
455 | + OpaqueFillShader::updateState(state, newEffect, oldEffect); |
456 | + if (state.isOpacityDirty()) { |
457 | + program()->setUniformValue(m_opacityId, state.opacity()); |
458 | + } |
459 | +} |
460 | + |
461 | +// --- Offset opaque fill shader --- |
462 | + |
463 | +OffsetOpaqueFillShader::OffsetOpaqueFillShader() |
464 | + : OpaqueFillShader() |
465 | +{ |
466 | + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/shaders/offsetfill.vert")); |
467 | +} |
468 | + |
469 | +char const *const *OffsetOpaqueFillShader::attributeNames() const |
470 | +{ |
471 | + static char const *const attributes[] = { |
472 | + "positionAttrib", "textureCoordAttrib", "positionOffsetAttrib", "textureCoordOffsetAttrib", |
473 | + 0 |
474 | + }; |
475 | + return attributes; |
476 | +} |
477 | + |
478 | +void OffsetOpaqueFillShader::initialize() |
479 | +{ |
480 | + OpaqueFillShader::initialize(); |
481 | + m_pixelSizeId = program()->uniformLocation("pixelSize"); |
482 | +} |
483 | + |
484 | +void OffsetOpaqueFillShader::updateState( |
485 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
486 | +{ |
487 | + OpaqueFillShader::updateState(state, newEffect, oldEffect); |
488 | + if (!oldEffect) { |
489 | + // Set the pixel size only once since the viewport is constant. |
490 | + const QRect rect = state.viewportRect(); |
491 | + program()->setUniformValue(m_pixelSizeId, 2.0f / rect.width(), 2.0f / rect.height()); |
492 | + } |
493 | +} |
494 | + |
495 | +// --- Offset fill shader --- |
496 | + |
497 | +OffsetFillShader::OffsetFillShader() |
498 | + : FillShader() |
499 | +{ |
500 | + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/shaders/offsetfill.vert")); |
501 | +} |
502 | + |
503 | +char const *const *OffsetFillShader::attributeNames() const |
504 | +{ |
505 | + static char const *const attributes[] = { |
506 | + "positionAttrib", "textureCoordAttrib", "positionOffsetAttrib", "textureCoordOffsetAttrib", |
507 | + 0 |
508 | + }; |
509 | + return attributes; |
510 | +} |
511 | + |
512 | +void OffsetFillShader::initialize() |
513 | +{ |
514 | + FillShader::initialize(); |
515 | + m_pixelSizeId = program()->uniformLocation("pixelSize"); |
516 | +} |
517 | + |
518 | +void OffsetFillShader::updateState( |
519 | + const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) |
520 | +{ |
521 | + FillShader::updateState(state, newEffect, oldEffect); |
522 | + if (!oldEffect) { |
523 | + // Set the pixel size only once since the viewport is constant. |
524 | + const QRect rect = state.viewportRect(); |
525 | + program()->setUniformValue(m_pixelSizeId, 2.0f / rect.width(), 2.0f / rect.height()); |
526 | + } |
527 | +} |
528 | + |
529 | +// --- Edge shader --- |
530 | + |
531 | +EdgeShader::EdgeShader() |
532 | + : OffsetFillShader() |
533 | +{ |
534 | + setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/shaders/edge.vert")); |
535 | + setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/shaders/edge.frag")); |
536 | +} |
537 | + |
538 | +// --- Opaque fill material --- |
539 | + |
540 | +OpaqueFillMaterial::OpaqueFillMaterial(QSGTexture *texture, QSGTexture::Filtering filtering) |
541 | + : m_texture(texture) |
542 | + , m_filtering(filtering) |
543 | +{ |
544 | + Q_ASSERT(texture); |
545 | + |
546 | + // It might seem a bit counterintuitive to set the blending flag in an |
547 | + // opaque material so here's a quick explanation. A geometry node can have a |
548 | + // standard and an opaque material, the latter one being used by the |
549 | + // renderer when the accumulated opacity on a node is 1. The point is to |
550 | + // simplify the shader used in that particular case. That said, an opaque |
551 | + // material can still render translucent fragments coming, for instance, |
552 | + // from an RGBA texture. In that case blending must be enabled and this is |
553 | + // exactly what happens here. |
554 | + setFlag(Blending, texture->hasAlphaChannel()); |
555 | +} |
556 | + |
557 | +QSGMaterialType *OpaqueFillMaterial::type() const |
558 | +{ |
559 | + static QSGMaterialType type; |
560 | + return &type; |
561 | +} |
562 | + |
563 | +QSGMaterialShader *OpaqueFillMaterial::createShader() const |
564 | +{ |
565 | + return new OpaqueFillShader; |
566 | +} |
567 | + |
568 | +int OpaqueFillMaterial::compare(const QSGMaterial *other) const |
569 | +{ |
570 | + const OpaqueFillMaterial *otherMaterial = static_cast<const OpaqueFillMaterial*>(other); |
571 | + if (const int diff = m_texture->textureId() - otherMaterial->texture()->textureId()) { |
572 | + return diff; |
573 | + } |
574 | + return static_cast<int>(m_filtering) - static_cast<int>(otherMaterial->filtering()); |
575 | +} |
576 | + |
577 | +// --- Fill material --- |
578 | + |
579 | +FillMaterial::FillMaterial(QSGTexture *texture, QSGTexture::Filtering filtering) |
580 | + : OpaqueFillMaterial(texture, filtering) |
581 | +{ |
582 | +} |
583 | + |
584 | +QSGMaterialType *FillMaterial::type() const |
585 | +{ |
586 | + static QSGMaterialType type; |
587 | + return &type; |
588 | +} |
589 | + |
590 | +QSGMaterialShader *FillMaterial::createShader() const |
591 | +{ |
592 | + return new FillShader; |
593 | +} |
594 | + |
595 | +/// --- Offset opaque fill material --- |
596 | + |
597 | +OffsetOpaqueFillMaterial::OffsetOpaqueFillMaterial( |
598 | + QSGTexture *texture, QSGTexture::Filtering filtering) |
599 | + : OpaqueFillMaterial(texture, filtering) |
600 | +{ |
601 | + setFlag(RequiresFullMatrixExceptTranslate, true); |
602 | +} |
603 | + |
604 | +QSGMaterialType *OffsetOpaqueFillMaterial::type() const |
605 | +{ |
606 | + static QSGMaterialType type; |
607 | + return &type; |
608 | +} |
609 | + |
610 | +QSGMaterialShader *OffsetOpaqueFillMaterial::createShader() const |
611 | +{ |
612 | + return new OffsetOpaqueFillShader; |
613 | +} |
614 | + |
615 | +/// --- Offset fill material --- |
616 | + |
617 | +OffsetFillMaterial::OffsetFillMaterial(QSGTexture *texture, QSGTexture::Filtering filtering) |
618 | + : FillMaterial(texture, filtering) |
619 | +{ |
620 | + setFlag(RequiresFullMatrixExceptTranslate, true); |
621 | +} |
622 | + |
623 | +QSGMaterialType *OffsetFillMaterial::type() const |
624 | +{ |
625 | + static QSGMaterialType type; |
626 | + return &type; |
627 | +} |
628 | + |
629 | +QSGMaterialShader *OffsetFillMaterial::createShader() const |
630 | +{ |
631 | + return new OffsetFillShader; |
632 | +} |
633 | + |
634 | +// --- Edge material --- |
635 | + |
636 | +EdgeMaterial::EdgeMaterial(QSGTexture *texture, QSGTexture::Filtering filtering) |
637 | + : OpaqueFillMaterial(texture, filtering) |
638 | +{ |
639 | + setFlag(Blending | RequiresFullMatrixExceptTranslate, true); |
640 | +} |
641 | + |
642 | +QSGMaterialType *EdgeMaterial::type() const |
643 | +{ |
644 | + static QSGMaterialType type; |
645 | + return &type; |
646 | +} |
647 | + |
648 | +QSGMaterialShader *EdgeMaterial::createShader() const |
649 | +{ |
650 | + return new EdgeShader; |
651 | +} |
652 | + |
653 | +// --- Fill node --- |
654 | + |
655 | +FillNode::FillNode(QSGTexture *texture, QSGTexture::Filtering filtering, bool offset) |
656 | + : QSGGeometryNode() |
657 | + , m_opaqueMaterial(texture, filtering) |
658 | + , m_material(texture, filtering) |
659 | + , m_offsetOpaqueMaterial(texture, filtering) |
660 | + , m_offsetMaterial(texture, filtering) |
661 | + , m_geometry(attributeSet(), 4, 4, GL_UNSIGNED_SHORT) |
662 | + , m_offsetGeometry(offsetAttributeSet(), 4, 4, GL_UNSIGNED_SHORT) |
663 | + , m_flags(0) |
664 | +{ |
665 | + Q_ASSERT(texture); |
666 | + |
667 | + memcpy(m_geometry.indexData(), indices(), 4 * sizeof(unsigned short)); |
668 | + m_geometry.setIndexDataPattern(QSGGeometry::StaticPattern); |
669 | + memcpy(m_offsetGeometry.indexData(), indices(), 4 * sizeof(unsigned short)); |
670 | + m_offsetGeometry.setIndexDataPattern(QSGGeometry::StaticPattern); |
671 | + setOffset(offset); |
672 | + |
673 | + qsgnode_set_description(this, QLatin1String("mirsurfacefill")); |
674 | +} |
675 | + |
676 | +// static |
677 | +const unsigned short *FillNode::indices() |
678 | +{ |
679 | + // Triangle strip layout: |
680 | + // 0 - 1 |
681 | + // | / | |
682 | + // 2 - 3 |
683 | + static const unsigned short indices[] = { 0, 2, 1, 3 }; |
684 | + return indices; |
685 | +} |
686 | + |
687 | +// static |
688 | +const QSGGeometry::AttributeSet &FillNode::attributeSet() |
689 | +{ |
690 | + static const QSGGeometry::Attribute attributes[] = { |
691 | + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), |
692 | + QSGGeometry::Attribute::create(1, 2, GL_FLOAT), |
693 | + }; |
694 | + static const QSGGeometry::AttributeSet attributeSet = { |
695 | + 2, sizeof(Vertex), attributes |
696 | + }; |
697 | + return attributeSet; |
698 | +} |
699 | + |
700 | +// static |
701 | +const QSGGeometry::AttributeSet &FillNode::offsetAttributeSet() |
702 | +{ |
703 | + static const QSGGeometry::Attribute attributes[] = { |
704 | + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), |
705 | + QSGGeometry::Attribute::create(1, 2, GL_FLOAT), |
706 | + QSGGeometry::Attribute::create(2, 2, GL_FLOAT), |
707 | + QSGGeometry::Attribute::create(3, 2, GL_FLOAT) |
708 | + }; |
709 | + static const QSGGeometry::AttributeSet attributeSet = { |
710 | + 4, sizeof(OffsetVertex), attributes |
711 | + }; |
712 | + return attributeSet; |
713 | +} |
714 | + |
715 | +void FillNode::setOffset(bool offset) |
716 | +{ |
717 | + if (!offset) { |
718 | + setGeometry(&m_geometry); |
719 | + setOpaqueMaterial(&m_opaqueMaterial); |
720 | + setMaterial(&m_material); |
721 | + m_flags &= ~Offset; |
722 | + } else { |
723 | + setGeometry(&m_offsetGeometry); |
724 | + setOpaqueMaterial(&m_offsetOpaqueMaterial); |
725 | + setMaterial(&m_offsetMaterial); |
726 | + m_flags |= Offset; |
727 | + } |
728 | +} |
729 | + |
730 | +void FillNode::setFiltering(QSGTexture::Filtering filtering) |
731 | +{ |
732 | + m_opaqueMaterial.setFiltering(filtering); |
733 | + m_material.setFiltering(filtering); |
734 | + m_offsetOpaqueMaterial.setFiltering(filtering); |
735 | + m_offsetMaterial.setFiltering(filtering); |
736 | + markDirty(DirtyMaterial); |
737 | +} |
738 | + |
739 | +void FillNode::updateGeometry(float width, float height) |
740 | +{ |
741 | + if (!(m_flags & Offset)) { |
742 | + Vertex *v = reinterpret_cast<Vertex*>(m_geometry.vertexData()); |
743 | + v[0].position[0] = 0.0f; |
744 | + v[0].position[1] = 0.0f; |
745 | + v[0].textureCoordinate[0] = 0.0f; |
746 | + v[0].textureCoordinate[1] = 0.0f; |
747 | + v[1].position[0] = width; |
748 | + v[1].position[1] = 0.0f; |
749 | + v[1].textureCoordinate[0] = 1.0f; |
750 | + v[1].textureCoordinate[1] = 0.0f; |
751 | + v[2].position[0] = 0.0f; |
752 | + v[2].position[1] = height; |
753 | + v[2].textureCoordinate[0] = 0.0f; |
754 | + v[2].textureCoordinate[1] = 1.0f; |
755 | + v[3].position[0] = width; |
756 | + v[3].position[1] = height; |
757 | + v[3].textureCoordinate[0] = 1.0f; |
758 | + v[3].textureCoordinate[1] = 1.0f; |
759 | + } else { |
760 | + OffsetVertex *v = reinterpret_cast<OffsetVertex*>(m_offsetGeometry.vertexData()); |
761 | + const float delta = qMin(width, height) * 0.5f; |
762 | + const float sx = 1.0f / width; |
763 | + const float sy = 1.0f / height; |
764 | + v[0].position[0] = 0.0f; |
765 | + v[0].position[1] = 0.0f; |
766 | + v[0].textureCoordinate[0] = 0.0f; |
767 | + v[0].textureCoordinate[1] = 0.0f; |
768 | + v[0].positionOffset[0] = delta; |
769 | + v[0].positionOffset[1] = delta; |
770 | + v[0].textureCoordinateOffset[0] = delta * sx; |
771 | + v[0].textureCoordinateOffset[1] = delta * sy; |
772 | + v[1].position[0] = width; |
773 | + v[1].position[1] = 0.0f; |
774 | + v[1].textureCoordinate[0] = 1.0f; |
775 | + v[1].textureCoordinate[1] = 0.0f; |
776 | + v[1].positionOffset[0] = -delta; |
777 | + v[1].positionOffset[1] = delta; |
778 | + v[1].textureCoordinateOffset[0] = -delta * sx; |
779 | + v[1].textureCoordinateOffset[1] = delta * sy; |
780 | + v[2].position[0] = 0.0f; |
781 | + v[2].position[1] = height; |
782 | + v[2].textureCoordinate[0] = 0.0f; |
783 | + v[2].textureCoordinate[1] = 1.0f; |
784 | + v[2].positionOffset[0] = delta; |
785 | + v[2].positionOffset[1] = -delta; |
786 | + v[2].textureCoordinateOffset[0] = delta * sx; |
787 | + v[2].textureCoordinateOffset[1] = -delta * sy; |
788 | + v[3].position[0] = width; |
789 | + v[3].position[1] = height; |
790 | + v[3].textureCoordinate[0] = 1.0f; |
791 | + v[3].textureCoordinate[1] = 1.0f; |
792 | + v[3].positionOffset[0] = -delta; |
793 | + v[3].positionOffset[1] = -delta; |
794 | + v[3].textureCoordinateOffset[0] = -delta * sx; |
795 | + v[3].textureCoordinateOffset[1] = -delta * sy; |
796 | + } |
797 | + |
798 | + markDirty(DirtyGeometry); |
799 | +} |
800 | + |
801 | +// --- Edge node --- |
802 | + |
803 | +EdgeNode::EdgeNode(QSGTexture *texture, QSGTexture::Filtering filtering, bool visible) |
804 | + : QSGGeometryNode() |
805 | + , m_material(texture, filtering) |
806 | + , m_geometry(attributeSet(), 8, 10, GL_UNSIGNED_SHORT) |
807 | + , m_flags(0) |
808 | +{ |
809 | + Q_ASSERT(texture); |
810 | + |
811 | + memcpy(m_geometry.indexData(), indices(), 10 * sizeof(unsigned short)); |
812 | + m_geometry.setIndexDataPattern(QSGGeometry::StaticPattern); |
813 | + setGeometry(&m_geometry); |
814 | + setMaterial(&m_material); |
815 | + setVisible(visible); |
816 | + |
817 | + qsgnode_set_description(this, QLatin1String("mirsurfaceedge")); |
818 | +} |
819 | + |
820 | +// static |
821 | +const unsigned short* EdgeNode::indices() |
822 | +{ |
823 | + // Triangle strip layout: |
824 | + // 0 ----- 1 |
825 | + // | 4 - 5 | |
826 | + // | | | | |
827 | + // | 6 - 7 | |
828 | + // 2 ----- 3 |
829 | + static const unsigned short indices[] = { 0, 4, 1, 5, 3, 7, 2, 6, 0, 4 }; |
830 | + return indices; |
831 | +} |
832 | + |
833 | +// static |
834 | +const QSGGeometry::AttributeSet& EdgeNode::attributeSet() |
835 | +{ |
836 | + static const QSGGeometry::Attribute attributes[] = { |
837 | + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), |
838 | + QSGGeometry::Attribute::create(1, 2, GL_FLOAT), |
839 | + QSGGeometry::Attribute::create(2, 2, GL_FLOAT), |
840 | + QSGGeometry::Attribute::create(3, 2, GL_FLOAT) |
841 | + }; |
842 | + static const QSGGeometry::AttributeSet attributeSet = { |
843 | + 4, sizeof(OffsetVertex), attributes |
844 | + }; |
845 | + return attributeSet; |
846 | +} |
847 | + |
848 | +void EdgeNode::setVisible(bool visible) |
849 | +{ |
850 | + if (!(m_flags & Visible) != visible) { |
851 | + m_flags ^= Visible; |
852 | + markDirty(DirtySubtreeBlocked); |
853 | + } |
854 | +} |
855 | + |
856 | +void EdgeNode::setFiltering(QSGTexture::Filtering filtering) |
857 | +{ |
858 | + m_material.setFiltering(filtering); |
859 | + markDirty(DirtyMaterial); |
860 | +} |
861 | + |
862 | +void EdgeNode::updateGeometry(float width, float height) |
863 | +{ |
864 | + OffsetVertex *v = reinterpret_cast<OffsetVertex*>(m_geometry.vertexData()); |
865 | + const float delta = qMin(width, height) * 0.5f; |
866 | + const float sx = 1.0f / width; |
867 | + const float sy = 1.0f / height; |
868 | + for (int i = 0, d = -1; i <= 4; i += 4, d += 2) { |
869 | + v[i+0].position[0] = 0.0f; |
870 | + v[i+0].position[1] = 0.0f; |
871 | + v[i+0].textureCoordinate[0] = 0.0f; |
872 | + v[i+0].textureCoordinate[1] = 0.0f; |
873 | + v[i+0].positionOffset[0] = delta * d; |
874 | + v[i+0].positionOffset[1] = delta * d; |
875 | + v[i+0].textureCoordinateOffset[0] = d < 0 ? 0.0f : delta * d * sx; |
876 | + v[i+0].textureCoordinateOffset[1] = d < 0 ? 0.0f : delta * d * sy; |
877 | + v[i+1].position[0] = width; |
878 | + v[i+1].position[1] = 0.0f; |
879 | + v[i+1].textureCoordinate[0] = 1.0f; |
880 | + v[i+1].textureCoordinate[1] = 0.0f; |
881 | + v[i+1].positionOffset[0] = -delta * d; |
882 | + v[i+1].positionOffset[1] = delta * d; |
883 | + v[i+1].textureCoordinateOffset[0] = d < 0 ? 0.0f : -delta * d * sx; |
884 | + v[i+1].textureCoordinateOffset[1] = d < 0 ? 0.0f : delta * d * sy; |
885 | + v[i+2].position[0] = 0.0f; |
886 | + v[i+2].position[1] = height; |
887 | + v[i+2].textureCoordinate[0] = 0.0f; |
888 | + v[i+2].textureCoordinate[1] = 1.0f; |
889 | + v[i+2].positionOffset[0] = delta * d; |
890 | + v[i+2].positionOffset[1] = -delta * d; |
891 | + v[i+2].textureCoordinateOffset[0] = d < 0 ? 0.0f : delta * d * sx; |
892 | + v[i+2].textureCoordinateOffset[1] = d < 0 ? 0.0f : -delta * d * sy; |
893 | + v[i+3].position[0] = width; |
894 | + v[i+3].position[1] = height; |
895 | + v[i+3].textureCoordinate[0] = 1.0f; |
896 | + v[i+3].textureCoordinate[1] = 1.0f; |
897 | + v[i+3].positionOffset[0] = -delta * d; |
898 | + v[i+3].positionOffset[1] = -delta * d; |
899 | + v[i+3].textureCoordinateOffset[0] = d < 0 ? 0.0f : -delta * d * sx; |
900 | + v[i+3].textureCoordinateOffset[1] = d < 0 ? 0.0f : -delta * d * sy; |
901 | + } |
902 | + |
903 | + markDirty(DirtyGeometry); |
904 | +} |
905 | + |
906 | +// --- MirSurface node --- |
907 | + |
908 | +MirSurfaceNode::MirSurfaceNode( |
909 | + QSGTexture *texture, QSGTexture::Filtering filtering, bool antialiasing) |
910 | +{ |
911 | + Q_ASSERT(texture); |
912 | + appendChildNode(new FillNode(texture, filtering, antialiasing)); |
913 | + appendChildNode(new EdgeNode(texture, filtering, antialiasing)); |
914 | +} |
915 | + |
916 | +void MirSurfaceNode::setAntialiasing(bool antialiasing) |
917 | +{ |
918 | + static_cast<FillNode*>(firstChild())->setOffset(antialiasing); |
919 | + static_cast<EdgeNode*>(lastChild())->setVisible(antialiasing); |
920 | +} |
921 | + |
922 | +void MirSurfaceNode::setFiltering(QSGTexture::Filtering filtering) |
923 | +{ |
924 | + static_cast<FillNode*>(firstChild())->setFiltering(filtering); |
925 | + static_cast<EdgeNode*>(lastChild())->setFiltering(filtering); |
926 | +} |
927 | + |
928 | +void MirSurfaceNode::updateGeometry(float width, float height) |
929 | +{ |
930 | + static_cast<FillNode*>(firstChild())->updateGeometry(width, height); |
931 | + static_cast<EdgeNode*>(lastChild())->updateGeometry(width, height); |
932 | +} |
933 | + |
934 | +void MirSurfaceNode::updateMaterial() |
935 | +{ |
936 | + firstChild()->markDirty(QSGNode::DirtyMaterial); |
937 | + lastChild()->markDirty(QSGNode::DirtyMaterial); |
938 | +} |
939 | |
940 | === added file 'src/modules/Unity/Application/mirsurfacenode.h' |
941 | --- src/modules/Unity/Application/mirsurfacenode.h 1970-01-01 00:00:00 +0000 |
942 | +++ src/modules/Unity/Application/mirsurfacenode.h 2015-10-19 10:47:33 +0000 |
943 | @@ -0,0 +1,55 @@ |
944 | +/* |
945 | + * Copyright (C) 2015 Canonical, Ltd. |
946 | + * |
947 | + * This program is free software: you can redistribute it and/or modify it under |
948 | + * the terms of the GNU Lesser General Public License version 3, as published by |
949 | + * the Free Software Foundation. |
950 | + * |
951 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
952 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
953 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
954 | + * Lesser General Public License for more details. |
955 | + * |
956 | + * You should have received a copy of the GNU Lesser General Public License |
957 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
958 | + */ |
959 | + |
960 | +#include <QtQuick/QSGNode> |
961 | +#include <QtQuick/QSGTexture> |
962 | + |
963 | +// MirSurfaceNode is a Qt scene graph node that renders a texture with a given |
964 | +// filtering. It can be compared to the image node provided by QtQuick (although |
965 | +// simpler) with the addition of a more efficient antialiasing technique that |
966 | +// keeps the same quality. |
967 | +// |
968 | +// QtQuick provides 2 different antialiasing techniques. The first one is called |
969 | +// multisampling and is proposed by the underlying hardware. It can be enabled |
970 | +// at window creation time by defining a number of samples on the QSurfaceFormat |
971 | +// associated to the created QtQuick scene (window). The efficiency of that |
972 | +// technique is highly dependent on the hardware and often not adapted. The |
973 | +// second technique is called vertex antialiasing, when the antialiasing |
974 | +// property is set to true on a QtQuick item, QtQuick switches to that technique |
975 | +// which adds 4 new vertices with additional attributes and lets the vertex |
976 | +// shader accurately set their position and opacity fall-off. The drawback of |
977 | +// this technique is that it forces the whole node to be rendered in the |
978 | +// translucent pass of the renderer even though the whole content of the node is |
979 | +// opaque, another drawback is that it oten prevents batching. |
980 | +// |
981 | +// The antialiasing technique provided by this node prevents both issues by |
982 | +// creating 2 geometry nodes, a FillNode that is made to fill the content and an |
983 | +// EdgeNode that is made to render the antialiased edge. The edge node uses |
984 | +// exactly the same vertex-based algorithm than the default QtQuick nodes. |
985 | +// |
986 | +// This technique is perfectly adapted to the rendering of the Spread, where |
987 | +// there is quite a lot of overdraw, by bringing back the ability for GPUs to |
988 | +// use early-Z cull optimisations. |
989 | +class MirSurfaceNode : public QSGNode |
990 | +{ |
991 | +public: |
992 | + MirSurfaceNode(QSGTexture *texture, QSGTexture::Filtering filtering, bool antialiasing); |
993 | + |
994 | + void setFiltering(QSGTexture::Filtering filtering); |
995 | + void setAntialiasing(bool antialiasing); |
996 | + void updateGeometry(float width, float height); |
997 | + void updateMaterial(); |
998 | +}; |
999 | |
1000 | === added file 'src/modules/Unity/Application/resources.qrc' |
1001 | --- src/modules/Unity/Application/resources.qrc 1970-01-01 00:00:00 +0000 |
1002 | +++ src/modules/Unity/Application/resources.qrc 2015-10-19 10:47:33 +0000 |
1003 | @@ -0,0 +1,10 @@ |
1004 | +<!DOCTYPE RCC><RCC version="1.0"> |
1005 | + <qresource> |
1006 | + <file>shaders/fill.vert</file> |
1007 | + <file>shaders/fill.frag</file> |
1008 | + <file>shaders/opaquefill.frag</file> |
1009 | + <file>shaders/offsetfill.vert</file> |
1010 | + <file>shaders/edge.vert</file> |
1011 | + <file>shaders/edge.frag</file> |
1012 | + </qresource> |
1013 | +</RCC> |
1014 | |
1015 | === added directory 'src/modules/Unity/Application/shaders' |
1016 | === added file 'src/modules/Unity/Application/shaders/edge.frag' |
1017 | --- src/modules/Unity/Application/shaders/edge.frag 1970-01-01 00:00:00 +0000 |
1018 | +++ src/modules/Unity/Application/shaders/edge.frag 2015-10-19 10:47:33 +0000 |
1019 | @@ -0,0 +1,24 @@ |
1020 | +/* |
1021 | + * Copyright (C) 2015 Canonical, Ltd. |
1022 | + * |
1023 | + * This program is free software: you can redistribute it and/or modify it under |
1024 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1025 | + * the Free Software Foundation. |
1026 | + * |
1027 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1028 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1029 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1030 | + * Lesser General Public License for more details. |
1031 | + * |
1032 | + * You should have received a copy of the GNU Lesser General Public License |
1033 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1034 | + */ |
1035 | + |
1036 | +uniform sampler2D texture; |
1037 | +varying mediump vec2 textureCoord; |
1038 | +varying lowp float vertexOpacity; |
1039 | + |
1040 | +void main(void) |
1041 | +{ |
1042 | + gl_FragColor = texture2D(texture, textureCoord) * vec4(vertexOpacity); |
1043 | +} |
1044 | |
1045 | === added file 'src/modules/Unity/Application/shaders/edge.vert' |
1046 | --- src/modules/Unity/Application/shaders/edge.vert 1970-01-01 00:00:00 +0000 |
1047 | +++ src/modules/Unity/Application/shaders/edge.vert 2015-10-19 10:47:33 +0000 |
1048 | @@ -0,0 +1,74 @@ |
1049 | +/* |
1050 | + * Copyright (C) 2015 Canonical, Ltd. |
1051 | + * |
1052 | + * This program is free software: you can redistribute it and/or modify it under |
1053 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1054 | + * the Free Software Foundation. |
1055 | + * |
1056 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1057 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1058 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1059 | + * Lesser General Public License for more details. |
1060 | + * |
1061 | + * You should have received a copy of the GNU Lesser General Public License |
1062 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1063 | + */ |
1064 | + |
1065 | +// Uses the vertex antialiasing code from QtQuick. |
1066 | + |
1067 | +uniform highp mat4 matrix; |
1068 | +uniform lowp float opacity; |
1069 | +uniform highp vec2 pixelSize; |
1070 | +attribute highp vec4 positionAttrib; |
1071 | +attribute mediump vec2 textureCoordAttrib; |
1072 | +attribute highp vec2 positionOffsetAttrib; |
1073 | +attribute highp vec2 textureCoordOffsetAttrib; |
1074 | +varying mediump vec2 textureCoord; |
1075 | +varying lowp float vertexOpacity; |
1076 | + |
1077 | +void main() |
1078 | +{ |
1079 | + textureCoord = textureCoordAttrib; |
1080 | + highp vec4 pos = matrix * positionAttrib; |
1081 | + gl_Position = pos; |
1082 | + |
1083 | + if (positionOffsetAttrib.x != 0.0) { |
1084 | + highp vec4 delta = matrix[0] * positionOffsetAttrib.x; |
1085 | + highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; |
1086 | + highp vec2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize); |
1087 | + dir -= ndir * delta.w * pos.w; |
1088 | + highp float numerator = dot(dir, ndir * pos.w * pos.w); |
1089 | + highp float scale = 0.0; |
1090 | + if (numerator < 0.0) { |
1091 | + scale = 1.0; |
1092 | + } else { |
1093 | + scale = min(1.0, numerator / dot(dir, dir)); |
1094 | + } |
1095 | + gl_Position += scale * delta; |
1096 | + textureCoord.x += scale * textureCoordOffsetAttrib.x; |
1097 | + } |
1098 | + |
1099 | + if (positionOffsetAttrib.y != 0.0) { |
1100 | + highp vec4 delta = matrix[1] * positionOffsetAttrib.y; |
1101 | + highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; |
1102 | + highp vec2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize); |
1103 | + dir -= ndir * delta.w * pos.w; |
1104 | + highp float numerator = dot(dir, ndir * pos.w * pos.w); |
1105 | + highp float scale = 0.0; |
1106 | + if (numerator < 0.0) { |
1107 | + scale = 1.0; |
1108 | + } else { |
1109 | + scale = min(1.0, numerator / dot(dir, dir)); |
1110 | + } |
1111 | + gl_Position += scale * delta; |
1112 | + textureCoord.y += scale * textureCoordOffsetAttrib.y; |
1113 | + } |
1114 | + |
1115 | + bool onEdge = any(notEqual(positionOffsetAttrib, vec2(0.0))); |
1116 | + bool outerEdge = all(equal(textureCoordOffsetAttrib, vec2(0.0))); |
1117 | + if (onEdge && outerEdge) { |
1118 | + vertexOpacity = 0.0; |
1119 | + } else { |
1120 | + vertexOpacity = opacity; |
1121 | + } |
1122 | +} |
1123 | |
1124 | === added file 'src/modules/Unity/Application/shaders/fill.frag' |
1125 | --- src/modules/Unity/Application/shaders/fill.frag 1970-01-01 00:00:00 +0000 |
1126 | +++ src/modules/Unity/Application/shaders/fill.frag 2015-10-19 10:47:33 +0000 |
1127 | @@ -0,0 +1,24 @@ |
1128 | +/* |
1129 | + * Copyright (C) 2015 Canonical, Ltd. |
1130 | + * |
1131 | + * This program is free software: you can redistribute it and/or modify it under |
1132 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1133 | + * the Free Software Foundation. |
1134 | + * |
1135 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1136 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1137 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1138 | + * Lesser General Public License for more details. |
1139 | + * |
1140 | + * You should have received a copy of the GNU Lesser General Public License |
1141 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1142 | + */ |
1143 | + |
1144 | +uniform sampler2D texture; |
1145 | +uniform lowp float opacity; |
1146 | +varying mediump vec2 textureCoord; |
1147 | + |
1148 | +void main(void) |
1149 | +{ |
1150 | + gl_FragColor = texture2D(texture, textureCoord) * vec4(opacity); |
1151 | +} |
1152 | |
1153 | === added file 'src/modules/Unity/Application/shaders/fill.vert' |
1154 | --- src/modules/Unity/Application/shaders/fill.vert 1970-01-01 00:00:00 +0000 |
1155 | +++ src/modules/Unity/Application/shaders/fill.vert 2015-10-19 10:47:33 +0000 |
1156 | @@ -0,0 +1,26 @@ |
1157 | +/* |
1158 | + * Copyright (C) 2015 Canonical, Ltd. |
1159 | + * |
1160 | + * This program is free software: you can redistribute it and/or modify it under |
1161 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1162 | + * the Free Software Foundation. |
1163 | + * |
1164 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1165 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1166 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1167 | + * Lesser General Public License for more details. |
1168 | + * |
1169 | + * You should have received a copy of the GNU Lesser General Public License |
1170 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1171 | + */ |
1172 | + |
1173 | +uniform highp mat4 matrix; |
1174 | +attribute highp vec4 positionAttrib; |
1175 | +attribute mediump vec2 textureCoordAttrib; |
1176 | +varying mediump vec2 textureCoord; |
1177 | + |
1178 | +void main() |
1179 | +{ |
1180 | + textureCoord = textureCoordAttrib; |
1181 | + gl_Position = matrix * positionAttrib; |
1182 | +} |
1183 | |
1184 | === added file 'src/modules/Unity/Application/shaders/offsetfill.vert' |
1185 | --- src/modules/Unity/Application/shaders/offsetfill.vert 1970-01-01 00:00:00 +0000 |
1186 | +++ src/modules/Unity/Application/shaders/offsetfill.vert 2015-10-19 10:47:33 +0000 |
1187 | @@ -0,0 +1,64 @@ |
1188 | +/* |
1189 | + * Copyright (C) 2015 Canonical, Ltd. |
1190 | + * |
1191 | + * This program is free software: you can redistribute it and/or modify it under |
1192 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1193 | + * the Free Software Foundation. |
1194 | + * |
1195 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1196 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1197 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1198 | + * Lesser General Public License for more details. |
1199 | + * |
1200 | + * You should have received a copy of the GNU Lesser General Public License |
1201 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1202 | + */ |
1203 | + |
1204 | +// Uses the vertex antialiasing code from QtQuick. |
1205 | + |
1206 | +uniform highp mat4 matrix; |
1207 | +uniform highp vec2 pixelSize; |
1208 | +attribute highp vec4 positionAttrib; |
1209 | +attribute mediump vec2 textureCoordAttrib; |
1210 | +attribute highp vec2 positionOffsetAttrib; |
1211 | +attribute highp vec2 textureCoordOffsetAttrib; |
1212 | +varying mediump vec2 textureCoord; |
1213 | + |
1214 | +void main() |
1215 | +{ |
1216 | + textureCoord = textureCoordAttrib; |
1217 | + highp vec4 pos = matrix * positionAttrib; |
1218 | + gl_Position = pos; |
1219 | + |
1220 | + if (positionOffsetAttrib.x != 0.0) { |
1221 | + highp vec4 delta = matrix[0] * positionOffsetAttrib.x; |
1222 | + highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; |
1223 | + highp vec2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize); |
1224 | + dir -= ndir * delta.w * pos.w; |
1225 | + highp float numerator = dot(dir, ndir * pos.w * pos.w); |
1226 | + highp float scale = 0.0; |
1227 | + if (numerator < 0.0) { |
1228 | + scale = 1.0; |
1229 | + } else { |
1230 | + scale = min(1.0, numerator / dot(dir, dir)); |
1231 | + } |
1232 | + gl_Position += scale * delta; |
1233 | + textureCoord.x += scale * textureCoordOffsetAttrib.x; |
1234 | + } |
1235 | + |
1236 | + if (positionOffsetAttrib.y != 0.0) { |
1237 | + highp vec4 delta = matrix[1] * positionOffsetAttrib.y; |
1238 | + highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w; |
1239 | + highp vec2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize); |
1240 | + dir -= ndir * delta.w * pos.w; |
1241 | + highp float numerator = dot(dir, ndir * pos.w * pos.w); |
1242 | + highp float scale = 0.0; |
1243 | + if (numerator < 0.0) { |
1244 | + scale = 1.0; |
1245 | + } else { |
1246 | + scale = min(1.0, numerator / dot(dir, dir)); |
1247 | + } |
1248 | + gl_Position += scale * delta; |
1249 | + textureCoord.y += scale * textureCoordOffsetAttrib.y; |
1250 | + } |
1251 | +} |
1252 | |
1253 | === added file 'src/modules/Unity/Application/shaders/opaquefill.frag' |
1254 | --- src/modules/Unity/Application/shaders/opaquefill.frag 1970-01-01 00:00:00 +0000 |
1255 | +++ src/modules/Unity/Application/shaders/opaquefill.frag 2015-10-19 10:47:33 +0000 |
1256 | @@ -0,0 +1,23 @@ |
1257 | +/* |
1258 | + * Copyright (C) 2015 Canonical, Ltd. |
1259 | + * |
1260 | + * This program is free software: you can redistribute it and/or modify it under |
1261 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1262 | + * the Free Software Foundation. |
1263 | + * |
1264 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1265 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1266 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1267 | + * Lesser General Public License for more details. |
1268 | + * |
1269 | + * You should have received a copy of the GNU Lesser General Public License |
1270 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1271 | + */ |
1272 | + |
1273 | +uniform sampler2D texture; |
1274 | +varying mediump vec2 textureCoord; |
1275 | + |
1276 | +void main(void) |
1277 | +{ |
1278 | + gl_FragColor = texture2D(texture, textureCoord); |
1279 | +} |