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 | |
62 | QQuickNvprFunctions::QQuickNvprFunctions() |
63 | : d(new QQuickNvprFunctionsPrivate(this)) |
64 | { |
65 | } |
66 | |
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 | */ |
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(major: 4, minor: 3); |
83 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); |
84 | } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { |
85 | fmt.setVersion(major: 3, minor: 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 | */ |
99 | bool QQuickNvprFunctions::isSupported() |
100 | { |
101 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
102 | QScopedPointer<QOpenGLContext> tempContext; |
103 | QScopedPointer<QOffscreenSurface> tempSurface; |
104 | if (!ctx) { |
105 | tempContext.reset(other: new QOpenGLContext); |
106 | if (!tempContext->create()) |
107 | return false; |
108 | ctx = tempContext.data(); |
109 | tempSurface.reset(other: new QOffscreenSurface); |
110 | tempSurface->setFormat(ctx->format()); |
111 | tempSurface->create(); |
112 | if (!ctx->makeCurrent(surface: 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 | */ |
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 | */ |
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, count: 1, strings: &fragmentShaderSource); |
172 | GLint status = 0; |
173 | f->glGetProgramiv(program: *program, GL_LINK_STATUS, params: &status); |
174 | if (!status) { |
175 | GLint len = 0; |
176 | f->glGetProgramiv(program: *program, GL_INFO_LOG_LENGTH, params: &len); |
177 | if (len) { |
178 | QByteArray s; |
179 | s.resize(size: len); |
180 | f->glGetProgramInfoLog(program: *program, bufsize: s.count(), length: nullptr, infolog: s.data()); |
181 | qWarning(msg: "Failed to create separable shader program:\n%s" , s.constData()); |
182 | } |
183 | return false; |
184 | } |
185 | |
186 | f->glGenProgramPipelines(n: 1, pipelines: pipeline); |
187 | f->glUseProgramStages(pipeline: *pipeline, GL_FRAGMENT_SHADER_BIT, program: *program); |
188 | f->glActiveShaderProgram(pipeline: *pipeline, program: *program); |
189 | |
190 | f->glValidateProgramPipeline(pipeline: *pipeline); |
191 | status = 0; |
192 | f->glGetProgramPipelineiv(pipeline: *pipeline, GL_VALIDATE_STATUS, params: &status); |
193 | if (!status) { |
194 | GLint len = 0; |
195 | f->glGetProgramPipelineiv(pipeline: *pipeline, GL_INFO_LOG_LENGTH, params: &len); |
196 | if (len) { |
197 | QByteArray s; |
198 | s.resize(size: len); |
199 | f->glGetProgramPipelineInfoLog(pipeline: *pipeline, bufSize: s.count(), length: nullptr, infoLog: s.data()); |
200 | qWarning(msg: "Program pipeline validation failed:\n%s" , s.constData()); |
201 | } |
202 | return false; |
203 | } |
204 | |
205 | return true; |
206 | } |
207 | |
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 | |