Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qopenglfunctions.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qopenglfunctions.h"
7#include "qdebug.h"
8#include <QtGui/private/qopenglcontext_p.h>
9#include <QtGui/private/qopengl_p.h>
10#include <QtGui/private/qguiapplication_p.h>
11#include <qpa/qplatformintegration.h>
12#include <qpa/qplatformnativeinterface.h>
13
14#ifdef Q_OS_INTEGRITY
15#include <EGL/egl.h>
16#endif
17
18#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE_EXT
19#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA
20#endif
21
23
24using namespace Qt::StringLiterals;
25
26#define QT_OPENGL_COUNT_FUNCTIONS(ret, name, args) +1
27#define QT_OPENGL_FUNCTION_NAMES(ret, name, args) \
28 "gl"#name"\0"
29#define QT_OPENGL_FLAGS(ret, name, args) \
30 0,
31#define QT_OPENGL_IMPLEMENT(CLASS, FUNCTIONS) \
32void CLASS::init(QOpenGLContext *context) \
33{ \
34 const char *names = FUNCTIONS(QT_OPENGL_FUNCTION_NAMES); \
35 const char *name = names; \
36 for (int i = 0; i < FUNCTIONS(QT_OPENGL_COUNT_FUNCTIONS); ++i) { \
37 functions[i] = QT_PREPEND_NAMESPACE(getProcAddress(context, name)); \
38 name += strlen(name) + 1; \
39 } \
40}
41
124// Hidden private fields for additional extension data.
126{
129 , QOpenGLSharedResource(context->shareGroup())
130 , m_features(-1)
131 , m_extensions(-1)
132 {}
133
134 void invalidateResource() override
135 {
136 m_features = -1;
137 m_extensions = -1;
138 }
139
141 {
142 // no gl resources to free
143 }
144
147};
148
149Q_GLOBAL_STATIC(QOpenGLMultiGroupSharedResource, qt_gl_functions_resource)
150
152{
153 if (!context)
157 qt_gl_functions_resource()->value<QOpenGLFunctionsPrivateEx>(context);
158 return funcs;
159}
160
169 : d_ptr(nullptr)
170{
171}
172
187 : d_ptr(nullptr)
188{
191 else
192 qWarning("QOpenGLFunctions created with non-current context");
193}
194
196{
197}
198
201{
202}
203
211{
213 QOpenGLExtensionMatcher extensions;
214 int features = 0;
215 if ((extensions.match("GL_KHR_blend_equation_advanced")
216 || extensions.match("GL_NV_blend_equation_advanced")) &&
217 (extensions.match("GL_KHR_blend_equation_advanced_coherent")
218 || extensions.match("GL_NV_blend_equation_advanced_coherent"))) {
219 // We need both the advanced equations and the coherency for us
220 // to be able to easily use the new blend equations
222 }
223 if (ctx->isOpenGLES()) {
224 // OpenGL ES
237 if (extensions.match("GL_IMG_texture_npot"))
239 if (extensions.match("GL_OES_texture_npot"))
242 if (ctx->format().majorVersion() >= 3 || extensions.match("GL_EXT_texture_rg")) {
243 // Mesa's GLES implementation (as of 10.6.0) is unable to handle this, even though it provides 3.0.
244 const char *renderer = reinterpret_cast<const char *>(ctx->functions()->glGetString(GL_RENDERER));
245 if (!(renderer && strstr(renderer, "Mesa")))
247 }
248 if (ctx->format().majorVersion() >= 3) {
250 if (ctx->format().minorVersion() >= 2 && extensions.match("GL_KHR_blend_equation_advanced_coherent")) {
251 // GL_KHR_blend_equation_advanced is included in OpenGL ES/3.2
253 }
254 }
255 return features;
256 } else {
257 // OpenGL
260
261 if (format.majorVersion() >= 3)
263 else if (extensions.match("GL_EXT_framebuffer_object") || extensions.match("GL_ARB_framebuffer_object"))
265
266 if (format.majorVersion() >= 2) {
267 features |= QOpenGLFunctions::BlendColor |
280 } else {
281 // Recognize features by extension name.
282 if (extensions.match("GL_ARB_multitexture"))
284 if (extensions.match("GL_ARB_shader_objects"))
285 features |= QOpenGLFunctions::Shaders;
286 if (extensions.match("GL_EXT_blend_color"))
288 if (extensions.match("GL_EXT_blend_equation_separate"))
290 if (extensions.match("GL_EXT_blend_subtract"))
292 if (extensions.match("GL_EXT_blend_func_separate"))
294 if (extensions.match("GL_ARB_texture_compression"))
296 if (extensions.match("GL_ARB_multisample"))
298 if (extensions.match("GL_ARB_texture_non_power_of_two"))
301 }
302
303 const QPair<int, int> version = format.version();
304 if (version < qMakePair(3, 0)
305 || (version == qMakePair(3, 0) && format.testOption(QSurfaceFormat::DeprecatedFunctions))
306 || (version == qMakePair(3, 1) && extensions.match("GL_ARB_compatibility"))
307 || (version >= qMakePair(3, 2) && format.profile() == QSurfaceFormat::CompatibilityProfile)) {
309 }
310 return features;
311 }
312}
313
315{
316 int extensions = 0;
317 QOpenGLExtensionMatcher extensionMatcher;
319 QSurfaceFormat format = ctx->format();
320
321 if (extensionMatcher.match("GL_EXT_bgra"))
323 if (extensionMatcher.match("GL_ARB_texture_rectangle"))
325 if (extensionMatcher.match("GL_ARB_texture_compression"))
327 if (extensionMatcher.match("GL_EXT_texture_compression_s3tc"))
329 if (extensionMatcher.match("GL_OES_compressed_ETC1_RGB8_texture"))
331 if (extensionMatcher.match("GL_IMG_texture_compression_pvrtc"))
333 if (extensionMatcher.match("GL_KHR_texture_compression_astc_ldr"))
335 if (extensionMatcher.match("GL_ARB_texture_mirrored_repeat"))
337 if (extensionMatcher.match("GL_EXT_stencil_two_side"))
339 if (extensionMatcher.match("GL_EXT_stencil_wrap"))
340 extensions |= QOpenGLExtensions::StencilWrap;
341 if (extensionMatcher.match("GL_NV_float_buffer"))
343 if (extensionMatcher.match("GL_ARB_pixel_buffer_object"))
345 if (extensionMatcher.match("GL_ARB_texture_swizzle") || extensionMatcher.match("GL_EXT_texture_swizzle"))
347 if (extensionMatcher.match("GL_OES_standard_derivatives"))
349 if (extensionMatcher.match("GL_ARB_half_float_vertex"))
351 if (extensionMatcher.match("GL_OVR_multiview"))
352 extensions |= QOpenGLExtensions::MultiView;
353 if (extensionMatcher.match("GL_OVR_multiview2"))
355
356 if (ctx->isOpenGLES()) {
357 if (format.majorVersion() >= 2)
359
360 if (format.majorVersion() >= 3) {
371#ifndef Q_OS_WASM
372 // WebGL 2.0 specification explicitly does not support texture swizzles
373 // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.19
375#endif
376 } else {
377 // Recognize features by extension name.
378 if (extensionMatcher.match("GL_OES_packed_depth_stencil"))
380 if (extensionMatcher.match("GL_OES_depth24"))
381 extensions |= QOpenGLExtensions::Depth24;
382 if (extensionMatcher.match("GL_ANGLE_framebuffer_blit"))
384 if (extensionMatcher.match("GL_ANGLE_framebuffer_multisample"))
386 if (extensionMatcher.match("GL_NV_framebuffer_blit"))
388 if (extensionMatcher.match("GL_NV_framebuffer_multisample"))
390 if (extensionMatcher.match("GL_OES_rgb8_rgba8"))
392 if (extensionMatcher.match("GL_OES_compressed_ETC2_RGB8_texture"))
394 }
395
396 if (extensionMatcher.match("GL_OES_mapbuffer"))
397 extensions |= QOpenGLExtensions::MapBuffer;
398 if (extensionMatcher.match("GL_OES_element_index_uint"))
400 // We don't match GL_APPLE_texture_format_BGRA8888 here because it has different semantics.
401 if (extensionMatcher.match("GL_IMG_texture_format_BGRA8888") || extensionMatcher.match("GL_EXT_texture_format_BGRA8888"))
403#ifdef Q_OS_ANDROID
404 QString *deviceName =
406 static bool wrongfullyReportsBgra8888Support = deviceName != 0
407 && (deviceName->compare("samsung SM-T211"_L1, Qt::CaseInsensitive) == 0
408 || deviceName->compare("samsung SM-T210"_L1, Qt::CaseInsensitive) == 0
409 || deviceName->compare("samsung SM-T215"_L1, Qt::CaseInsensitive) == 0);
410 if (wrongfullyReportsBgra8888Support)
411 extensions &= ~QOpenGLExtensions::BGRATextureFormat;
412#endif
413
414 if (extensionMatcher.match("GL_EXT_discard_framebuffer"))
416 if (extensionMatcher.match("GL_EXT_texture_norm16"))
418 } else {
422
423 if (format.version() >= qMakePair(1, 2))
425
426 if (format.version() >= qMakePair(1, 4) || extensionMatcher.match("GL_SGIS_generate_mipmap"))
428
429 if (format.majorVersion() >= 2)
431
432 if (format.majorVersion() >= 3 || extensionMatcher.match("GL_ARB_framebuffer_object")) {
437 } else {
438 // Recognize features by extension name.
439 if (extensionMatcher.match("GL_EXT_framebuffer_multisample"))
441 if (extensionMatcher.match("GL_EXT_framebuffer_blit"))
443 if (extensionMatcher.match("GL_EXT_packed_depth_stencil"))
445 }
446
447 if (format.version() >= qMakePair(3, 2) || extensionMatcher.match("GL_ARB_geometry_shader4"))
449
450 if (format.version() >= qMakePair(3, 3))
452
453 if (extensionMatcher.match("GL_ARB_map_buffer_range"))
455
456 if (extensionMatcher.match("GL_EXT_framebuffer_sRGB")) {
457 GLboolean srgbCapableFramebuffers = false;
458 ctx->functions()->glGetBooleanv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgbCapableFramebuffers);
459 if (srgbCapableFramebuffers)
461 }
462
463 if (extensionMatcher.match("GL_ARB_ES3_compatibility"))
465 }
466
467 return extensions;
468}
469
479QOpenGLFunctions::OpenGLFeatures QOpenGLFunctions::openGLFeatures() const
480{
482 if (!d)
483 return { };
484 if (d->m_features == -1)
485 d->m_features = qt_gl_resolve_features();
486 return QOpenGLFunctions::OpenGLFeatures(d->m_features);
487}
488
499{
501 if (!d)
502 return false;
503 if (d->m_features == -1)
504 d->m_features = qt_gl_resolve_features();
505 return (d->m_features & int(feature)) != 0;
506}
507
517QOpenGLExtensions::OpenGLExtensions QOpenGLExtensions::openGLExtensions()
518{
520 if (!d)
521 return { };
522 if (d->m_extensions == -1)
523 d->m_extensions = qt_gl_resolve_extensions();
524 return QOpenGLExtensions::OpenGLExtensions(d->m_extensions);
525}
526
537{
539 if (!d)
540 return false;
541 if (d->m_extensions == -1)
542 d->m_extensions = qt_gl_resolve_extensions();
543 return (d->m_extensions & int(extension)) != 0;
544}
545
555{
557}
558
2063namespace {
2064
2065// this function tries hard to get the opengl function we're looking for by also
2066// trying to resolve it with some of the common extensions if the generic name
2067// can't be found.
2068static QFunctionPointer getProcAddress(QOpenGLContext *context, const char *funcName)
2069{
2070 QFunctionPointer function = context->getProcAddress(funcName);
2071
2072 static const struct {
2073 const char *name;
2074 int len; // includes trailing \0
2075 } extensions[] = {
2076 { "ARB", 4 },
2077 { "OES", 4 },
2078 { "EXT", 4 },
2079 { "ANGLE", 6 },
2080 { "NV", 3 },
2081 };
2082
2083 if (!function) {
2084 char fn[512];
2085 size_t size = strlen(funcName);
2086 Q_ASSERT(size < 500);
2087 memcpy(fn, funcName, size);
2088 char *ext = fn + size;
2089
2090 for (const auto &e : extensions) {
2091 memcpy(ext, e.name, e.len);
2092 function = context->getProcAddress(fn);
2093 if (function)
2094 break;
2095 }
2096 }
2097
2098 return function;
2099}
2100
2101template <typename Func>
2102Func resolve(QOpenGLContext *context, const char *name, Func)
2103{
2104 return reinterpret_cast<Func>(getProcAddress(context, name));
2105}
2106
2107}
2108
2109#define RESOLVE(name) \
2110 resolve(context, "gl"#name, name)
2111
2112#if !QT_CONFIG(opengles2)
2113
2114// some fallback functions
2116{
2119 funcs->f.ClearDepth((GLdouble) depth);
2120}
2121
2123{
2126 funcs->f.DepthRange((GLdouble) zNear, (GLdouble) zFar);
2127}
2128
2130{
2133 range[0] = range[1] = precision[0] = 0;
2134}
2135
2137{
2138 return program != 0;
2139}
2140
2142{
2143 return shader != 0;
2144}
2145
2147{
2148}
2149
2150#endif // !QT_CONFIG(opengles2)
2151
2152
2154{
2155 init(c);
2156
2157#if !QT_CONFIG(opengles2)
2158 // setup fallbacks in case some methods couldn't get resolved
2160 if (!f.ClearDepthf || !es)
2161 f.ClearDepthf = qopenglfSpecialClearDepthf;
2162 if (!f.DepthRangef || !es)
2163 f.DepthRangef = qopenglfSpecialDepthRangef;
2164 if (!f.GetShaderPrecisionFormat)
2165 f.GetShaderPrecisionFormat = qopenglfSpecialGetShaderPrecisionFormat;
2166 if (!f.IsProgram)
2167 f.IsProgram = qopenglfSpecialIsProgram;
2168 if (!f.IsShader)
2169 f.IsShader = qopenglfSpecialIsShader;
2170 if (!f.ReleaseShaderCompiler)
2171 f.ReleaseShaderCompiler = qopenglfSpecialReleaseShaderCompiler;
2172#endif
2173}
2174
2175
2177
2178
5019{
5020}
5021
5034{
5035}
5036
5039{
5040 init(ctx);
5041}
5042
5044
5047 flushVendorChecked(false)
5048{
5050
5051 MapBuffer = RESOLVE(MapBuffer);
5052 GetBufferSubData = RESOLVE(GetBufferSubData);
5053 DiscardFramebuffer = RESOLVE(DiscardFramebuffer);
5054 }
5055
5057{
5058 Q_D(QOpenGLExtensions);
5059
5060 if (!d->flushVendorChecked) {
5061 d->flushVendorChecked = true;
5062 // It is not quite clear if glFlush() is sufficient to synchronize access to
5063 // resources between sharing contexts in the same thread. On most platforms this
5064 // is enough (e.g. iOS explicitly documents it), while certain drivers only work
5065 // properly when doing glFinish().
5066 d->flushIsSufficientToSyncContexts = false; // default to false, not guaranteed by the spec
5067 const char *vendor = (const char *) glGetString(GL_VENDOR);
5068 if (vendor) {
5069 static const char *const flushEnough[] = { "Apple", "ATI", "Intel", "NVIDIA" };
5070 for (size_t i = 0; i < sizeof(flushEnough) / sizeof(const char *); ++i) {
5071 if (strstr(vendor, flushEnough[i])) {
5073 break;
5074 }
5075 }
5076 }
5077 }
5078
5080 glFlush();
5081 else
5082 glFinish();
5083}
5084
static QPlatformNativeInterface * platformNativeInterface()
static QOpenGLContextGroup * currentContextGroup()
Returns the QOpenGLContextGroup corresponding to the current context.
\inmodule QtGui
QSurfaceFormat format() const
Returns the format of the underlying platform context, if create() has been called.
static QOpenGLContext * currentContext()
Returns the last context which called makeCurrent in the current thread, or \nullptr,...
bool isOpenGLES() const
Returns true if the context is an OpenGL ES context.
bool match(const QByteArray &extension) const
Definition qopengl_p.h:34
QOpenGLExtensionsPrivate * d() const
OpenGLExtensions openGLExtensions()
Returns the set of extensions that are present on this system's OpenGL implementation.
bool hasOpenGLExtension(QOpenGLExtensions::OpenGLExtension extension) const
Returns true if extension is present on this system's OpenGL implementation; false otherwise.
QOpenGLExtraFunctionsPrivate(QOpenGLContext *ctx)
The QOpenGLExtraFunctions class provides cross-platform access to the OpenGL ES 3....
QOpenGLExtraFunctions()
Constructs a default function resolver.
The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API.
OpenGLFeature
This enum defines OpenGL and OpenGL ES features whose presence may depend on the implementation.
QOpenGLFunctionsPrivate * d_ptr
void glFinish()
Convenience function that calls glFinish().
const GLubyte * glGetString(GLenum name)
Convenience function that calls glGetString(name).
void glFlush()
Convenience function that calls glFlush().
QOpenGLFunctions()
Constructs a default function resolver.
void initializeOpenGLFunctions()
Initializes OpenGL function resolution for the current context.
bool hasOpenGLFeature(QOpenGLFunctions::OpenGLFeature feature) const
Returns true if feature is present on this system's OpenGL implementation; false otherwise.
QOpenGLFunctions::OpenGLFeatures openGLFeatures() const
Returns the set of features that are present on this system's OpenGL implementation.
The QOpenGLMultiGroupSharedResource keeps track of a shared resource that might be needed from multip...
The QOpenGLSharedResource class is used to keep track of resources that are shared between OpenGL con...
virtual void * nativeResourceForIntegration(const QByteArray &resource)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
EGLContext ctx
static VulkanServerBufferGlFunctions * funcs
void extension()
[6]
Definition dialogs.cpp:230
double e
Combined button and popup list for selecting options.
@ CaseInsensitive
static void * context
std::pair< T1, T2 > QPair
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:162
#define QOPENGLF_APIENTRY
Definition qopengl.h:260
typedef GLint(GL_APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program
GLdouble GLdouble GLdouble GLdouble GLdouble GLdouble zFar
GLdouble GLdouble GLdouble GLdouble GLdouble zNear
GLint GLenum GLsizei GLsizei GLsizei depth
GLenum shadertype
GLenum GLuint GLintptr GLsizeiptr size
[1]
double GLdouble
GLfloat GLfloat f
GLsizei range
typedef GLenum(GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void)
GLuint program
typedef GLboolean(GL_APIENTRYP PFNGLISENABLEDIOESPROC)(GLenum target
GLuint name
GLint GLsizei GLsizei GLenum format
const GLubyte * c
GLenum precisiontype
const void * getProcAddress
GLuint shader
Definition qopenglext.h:665
GLenum GLsizei len
GLenum GLint GLint * precision
#define QT_OPENGL_EXTRA_FUNCTIONS(F)
#define RESOLVE(name)
#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT
static void QOPENGLF_APIENTRY qopenglfSpecialClearDepthf(GLclampf depth)
static void QOPENGLF_APIENTRY qopenglfSpecialDepthRangef(GLclampf zNear, GLclampf zFar)
static GLboolean QOPENGLF_APIENTRY qopenglfSpecialIsShader(GLuint shader)
#define QT_OPENGL_IMPLEMENT(CLASS, FUNCTIONS)
static void QOPENGLF_APIENTRY qopenglfSpecialGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision)
static int qt_gl_resolve_extensions()
static QOpenGLFunctionsPrivateEx * qt_gl_functions(QOpenGLContext *context=nullptr)
static void QOPENGLF_APIENTRY qopenglfSpecialReleaseShaderCompiler()
static int qt_gl_resolve_features()
static GLboolean QOPENGLF_APIENTRY qopenglfSpecialIsProgram(GLuint program)
#define QT_OPENGL_FUNCTIONS(F)
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define GLuint
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_UNUSED(x)
QObject::connect nullptr
QSvgRenderer * renderer
[0]
void freeResource(QOpenGLContext *) override
QOpenGLFunctionsPrivateEx(QOpenGLContext *context)
QOpenGLFunctionsPrivate(QOpenGLContext *ctx)