Merge lp:~loic.molinari/qtmir/qtmir-custom-mirsurfacenode into lp:qtmir

Proposed by Loïc Molinari
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
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.

To post a comment you must log in.
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches