1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qquicknvprfunctions_p.h"
41 
42 #if QT_CONFIG(opengl)
43 
44 #include <QOpenGLContext>
45 #include <QOffscreenSurface>
46 #include <QOpenGLExtraFunctions>
47 #include "qquicknvprfunctions_p_p.h"
48 
49 QT_BEGIN_NAMESPACE
50 
51 /*!
52     \class QQuickNvprFunctions
53 
54     \brief Function resolvers and other helpers for GL_NV_path_rendering
55     for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner
56     that does not distract builds that do not have NVPR support either at
57     compile or run time.
58 
59     \internal
60  */
61 
QQuickNvprFunctions()62 QQuickNvprFunctions::QQuickNvprFunctions()
63     : d(new QQuickNvprFunctionsPrivate(this))
64 {
65 }
66 
~QQuickNvprFunctions()67 QQuickNvprFunctions::~QQuickNvprFunctions()
68 {
69     delete d;
70 }
71 
72 /*!
73    \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top
74    of OpenGL 4.3 or OpenGL ES 3.1.
75  */
format()76 QSurfaceFormat QQuickNvprFunctions::format()
77 {
78     QSurfaceFormat fmt;
79     fmt.setDepthBufferSize(24);
80     fmt.setStencilBufferSize(8);
81     if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
82         fmt.setVersion(4, 3);
83         fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
84     } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
85         fmt.setVersion(3, 1);
86     }
87     return fmt;
88 }
89 
90 #define PROC(type, name) reinterpret_cast<type>(ctx->getProcAddress(#name))
91 
92 /*!
93   \return true if GL_NV_path_rendering is supported with the current OpenGL
94   context.
95 
96   When there is no current context, a temporary dummy one will be created and
97   made current.
98  */
isSupported()99 bool QQuickNvprFunctions::isSupported()
100 {
101     QOpenGLContext *ctx = QOpenGLContext::currentContext();
102     QScopedPointer<QOpenGLContext> tempContext;
103     QScopedPointer<QOffscreenSurface> tempSurface;
104     if (!ctx) {
105         tempContext.reset(new QOpenGLContext);
106         if (!tempContext->create())
107             return false;
108         ctx = tempContext.data();
109         tempSurface.reset(new QOffscreenSurface);
110         tempSurface->setFormat(ctx->format());
111         tempSurface->create();
112         if (!ctx->makeCurrent(tempSurface.data()))
113             return false;
114     }
115 
116     if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering")))
117         return false;
118 
119     // Check that GL_NV_Path_rendering extension is at least API revision 1.3
120     if (!PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV))
121         return false;
122 
123     // Do not check for DSA as the string may not be exposed on ES
124     // drivers, yet the functions we need are resolvable.
125 #if 0
126     if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) {
127         qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported");
128         return false;
129     }
130 #endif
131 
132     return true;
133 }
134 
135 /*!
136     Initializes using the current OpenGL context.
137 
138     \return true when GL_NV_path_rendering is supported and initialization was
139     successful.
140  */
create()141 bool QQuickNvprFunctions::create()
142 {
143     return isSupported() && d->resolve();
144 }
145 
146 /*!
147     Creates a program pipeline consisting of a separable fragment shader program.
148 
149     This is essential for using NVPR with OpenGL ES 3.1+ since normal,
150     GLES2-style programs would not work without a vertex shader.
151 
152     \note \a fragmentShaderSource should be a \c{version 310 es} shader since
153     this works both on desktop and embedded NVIDIA drivers, thus avoiding the
154     need to fight GLSL and GLSL ES differences.
155 
156     The pipeline object is stored into \a pipeline, the fragment shader program
157     into \a program.
158 
159     Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc.
160 
161     \return \c false on failure in which case the error log is printed on the
162     debug output. \c true on success.
163  */
createFragmentOnlyPipeline(const char * fragmentShaderSource,GLuint * pipeline,GLuint * program)164 bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program)
165 {
166     QOpenGLContext *ctx = QOpenGLContext::currentContext();
167     if (!ctx)
168         return false;
169 
170     QOpenGLExtraFunctions *f = ctx->extraFunctions();
171     *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource);
172     GLint status = 0;
173     f->glGetProgramiv(*program, GL_LINK_STATUS, &status);
174     if (!status) {
175         GLint len = 0;
176         f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len);
177         if (len) {
178             QByteArray s;
179             s.resize(len);
180             f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data());
181             qWarning("Failed to create separable shader program:\n%s", s.constData());
182         }
183         return false;
184     }
185 
186     f->glGenProgramPipelines(1, pipeline);
187     f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program);
188     f->glActiveShaderProgram(*pipeline, *program);
189 
190     f->glValidateProgramPipeline(*pipeline);
191     status = 0;
192     f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status);
193     if (!status) {
194         GLint len = 0;
195         f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len);
196         if (len) {
197             QByteArray s;
198             s.resize(len);
199             f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data());
200             qWarning("Program pipeline validation failed:\n%s", s.constData());
201         }
202         return false;
203     }
204 
205     return true;
206 }
207 
resolve()208 bool QQuickNvprFunctionsPrivate::resolve()
209 {
210     QOpenGLContext *ctx = QOpenGLContext::currentContext();
211 
212     q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV);
213     q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV);
214     q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV);
215     q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV);
216     q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV);
217     q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV);
218     q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV);
219     q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV);
220     q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV);
221     q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV);
222     q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV);
223     q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV);
224     q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV);
225     q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV);
226     q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV);
227     q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV);
228     q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV);
229     q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV);
230     q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV);
231     q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV);
232     q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV);
233     q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV);
234     q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV);
235     q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV);
236     q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV);
237     q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC,  glPathCoverDepthFuncNV);
238     q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV);
239     q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV);
240     q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV);
241     q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV);
242     q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV);
243     q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV);
244     q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV);
245     q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV);
246     q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV);
247     q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV);
248     q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV);
249     q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV);
250     q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV);
251     q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV);
252     q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV);
253     q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV);
254     q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV);
255     q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV);
256     q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV);
257     q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV);
258     q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV);
259     q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV);
260     q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV);
261     q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV);
262     q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV);
263     q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV);
264     q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV);
265     q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV);
266     q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV);
267     q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV);
268     q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV);
269 
270     q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT);
271     q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT);
272 
273     return q->genPaths != nullptr // base path rendering ext
274         && q->programPathFragmentInputGen != nullptr // updated path rendering ext
275         && q->matrixLoadf != nullptr // direct state access ext
276         && q->matrixLoadIdentity != nullptr;
277 }
278 
279 QT_END_NAMESPACE
280 
281 #endif // QT_CONFIG(opengl)
282