Krita Source Code Documentation
Loading...
Searching...
No Matches
KisOpenGLModeProber.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Alvin Wong <alvinhochun@gmail.com>
3 * SPDX-FileCopyrightText: 2019 Dmitry Kazakov <dimula73@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include <config-hdr.h>
11#include <QApplication>
12#include <QOpenGLContext>
13#include <QOpenGLFunctions>
14#include <QWindow>
15#include <QColorSpace>
16
17#ifdef HAVE_X11
18#include <qpa/qplatformnativeinterface.h>
19#endif
20
21#include <QGlobalStatic>
23
25
26
30
35
37{
38 return s_instance;
39}
40
42{
43 return isFormatHDR(QSurfaceFormat::defaultFormat());
44}
45
47{
48 // TODO: use information provided by KisOpenGL instead
49 QOpenGLContext *sharedContext = QOpenGLContext::globalShareContext();
50 QSurfaceFormat format = sharedContext ? sharedContext->format() : QSurfaceFormat::defaultFormat();
51 return format;
52}
53
55{
57
58 const auto surfaceColorSpace =
60
61 if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::sRGBColorSpace) {
62 // use the default one!
63#ifdef HAVE_HDR
64 } else if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
66 } else if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
68#endif
69 }
70
71 return profile;
72}
73
74namespace {
75struct AppAttributeSetter
76{
77 AppAttributeSetter(Qt::ApplicationAttribute attribute, bool useOpenGLES)
78 : m_attribute(attribute),
79 m_oldValue(QCoreApplication::testAttribute(attribute))
80 {
81 QCoreApplication::setAttribute(attribute, useOpenGLES);
82 }
83
84 ~AppAttributeSetter() {
85 QCoreApplication::setAttribute(m_attribute, m_oldValue);
86 }
87
88private:
89 Qt::ApplicationAttribute m_attribute;
90 bool m_oldValue = false;
91};
92
93struct SurfaceFormatSetter
94{
95 SurfaceFormatSetter(const QSurfaceFormat &format)
96 : m_oldFormat(QSurfaceFormat::defaultFormat())
97 {
98 QSurfaceFormat::setDefaultFormat(format);
99 }
100
101 ~SurfaceFormatSetter() {
102 QSurfaceFormat::setDefaultFormat(m_oldFormat);
103 }
104
105private:
106 QSurfaceFormat m_oldFormat;
107};
108
109
110struct EnvironmentSetter
111{
112 EnvironmentSetter(const QLatin1String &env, const QString &value)
113 : m_env(env)
114 {
115 if (!qEnvironmentVariableIsEmpty(m_env.latin1())) {
116 m_oldValue = qgetenv(env.latin1());
117 }
118 if (!value.isEmpty()) {
119 qputenv(env.latin1(), value.toLatin1());
120 } else {
121 qunsetenv(env.latin1());
122 }
123 }
124
125 ~EnvironmentSetter() {
126 if (m_oldValue) {
127 qputenv(m_env.latin1(), (*m_oldValue).toLatin1());
128 } else {
129 qunsetenv(m_env.latin1());
130 }
131 }
132
133private:
134 const QLatin1String m_env;
135 boost::optional<QString> m_oldValue;
136};
137
138}
139
140boost::optional<KisOpenGLModeProber::Result>
142 bool adjustGlobalState)
143{
144 const QSurfaceFormat &format = rendererConfig.format;
145
146 dbgOpenGL << "Probing format" << rendererConfig.rendererId() << rendererConfig.angleRenderer
147 << rendererConfig.format;
148
149 QScopedPointer<AppAttributeSetter> sharedContextSetter;
150 QScopedPointer<AppAttributeSetter> glSetter;
151 QScopedPointer<AppAttributeSetter> glesSetter;
152 QScopedPointer<SurfaceFormatSetter> formatSetter;
153 QScopedPointer<EnvironmentSetter> rendererSetter;
154 QScopedPointer<EnvironmentSetter> portalSetter;
155 QScopedPointer<QGuiApplication> application;
156
157 int argc = 1;
158 QByteArray probeAppName("krita");
159 char *argv = probeAppName.data();
160
161
162 if (adjustGlobalState) {
163 sharedContextSetter.reset(new AppAttributeSetter(Qt::AA_ShareOpenGLContexts, false));
164
165 if (format.renderableType() != QSurfaceFormat::DefaultRenderableType) {
166 glSetter.reset(new AppAttributeSetter(Qt::AA_UseDesktopOpenGL, format.renderableType() != QSurfaceFormat::OpenGLES));
167 glesSetter.reset(new AppAttributeSetter(Qt::AA_UseOpenGLES, format.renderableType() == QSurfaceFormat::OpenGLES));
168 }
169
170 if (!qEnvironmentVariableIsSet("QT_ANGLE_PLATFORM")) {
171 rendererSetter.reset(new EnvironmentSetter(QLatin1String("QT_ANGLE_PLATFORM"), angleRendererToString(rendererConfig.angleRenderer)));
172 }
173 portalSetter.reset(new EnvironmentSetter(QLatin1String("QT_NO_XDG_DESKTOP_PORTAL"), QLatin1String("1")));
174 formatSetter.reset(new SurfaceFormatSetter(format));
175
176 // Disable this workaround for plasma (BUG:408015), because it causes
177 // a crash on Windows with Qt 5.15.7
178 const bool runningInKDE = qEnvironmentVariableIsSet("KDE_FULL_SESSION");
179 const bool isInAppimage = qEnvironmentVariableIsSet("APPIMAGE");
180
181 if (runningInKDE && !isInAppimage) {
182 QGuiApplication::setDesktopSettingsAware(false);
183 }
184
185 application.reset(new QGuiApplication(argc, &argv));
186
187 if (runningInKDE && !isInAppimage) {
188 QGuiApplication::setDesktopSettingsAware(true);
189 }
190
191 }
192
193 QWindow surface;
194 surface.setFormat(format);
195 surface.setSurfaceType(QSurface::OpenGLSurface);
196 surface.create();
197 QOpenGLContext context;
198 context.setFormat(format);
199
200
201 if (!context.create()) {
202 dbgOpenGL << "OpenGL context cannot be created";
203 return boost::none;
204 }
205 if (!context.isValid()) {
206 dbgOpenGL << "OpenGL context is not valid while checking Qt's OpenGL status";
207 return boost::none;
208 }
209 if (!context.makeCurrent(&surface)) {
210 dbgOpenGL << "OpenGL context cannot be made current";
211 return boost::none;
212 }
213
215 KisSurfaceColorSpaceWrapper::fromQtColorSpace(context.format().colorSpace()),
216 KisSurfaceColorSpaceWrapper::fromQtColorSpace(format.colorSpace()))) {
217
218 dbgOpenGL << "Failed to create an OpenGL context with requested color space. Requested:" << format.colorSpace() << "Actual:" << context.format().colorSpace();
219 return boost::none;
220 }
221
222 if (format.redBufferSize() > 0 && format.greenBufferSize() > 0 && format.blueBufferSize() > 0
223 && (context.format().redBufferSize() != format.redBufferSize()
224 || context.format().greenBufferSize() != format.greenBufferSize()
225 || context.format().blueBufferSize() != format.blueBufferSize())) {
226
227 dbgOpenGL << "Failed to create an OpenGL context with requested bit depth. Requested:" << format.redBufferSize()
228 << "Actual:" << context.format().redBufferSize();
229 return boost::none;
230 }
231
232 Result result(context);
233
234 dbgOpenGL << "Probe returned" << result.rendererString() << result.driverVersionString() << result.isOpenGLES();
235
236 return result;
237}
238
247
248void KisOpenGLModeProber::initSurfaceFormatFromConfig(std::pair<KisSurfaceColorSpaceWrapper, int> rootSurfaceFormat,
249 QSurfaceFormat *format)
250{
251#ifdef HAVE_HDR
255 if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
256 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 10);
257 format->setRedBufferSize(10);
258 format->setGreenBufferSize(10);
259 format->setBlueBufferSize(10);
260 format->setAlphaBufferSize(2);
262 } else if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
263 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 16);
264 format->setRedBufferSize(16);
265 format->setGreenBufferSize(16);
266 format->setBlueBufferSize(16);
267 format->setAlphaBufferSize(16);
269 } else {
270 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 8);
271 format->setRedBufferSize(8);
272 format->setGreenBufferSize(8);
273 format->setBlueBufferSize(8);
274 format->setAlphaBufferSize(8);
275 // TODO: check if we can use real sRGB space here
276 format->setColorSpace(KisSurfaceColorSpaceWrapper());
277 }
278#else
283 if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
284 qWarning() << "WARNING: Bt.2020 PQ surface type is not supported by this build of Krita";
285 rootSurfaceFormat.first = KisSurfaceColorSpaceWrapper::DefaultColorSpace;
286 } else if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
287 qWarning() << "WARNING: scRGB surface type is not supported by this build of Krita";
288 rootSurfaceFormat.first = KisSurfaceColorSpaceWrapper::DefaultColorSpace;
289 } else {
291 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 8 || rootSurfaceFormat.second == 10);
292
293 if (rootSurfaceFormat.second == 10) {
294 format->setRedBufferSize(10);
295 format->setGreenBufferSize(10);
296 format->setBlueBufferSize(10);
297 format->setAlphaBufferSize(2);
298 // TODO: check if we can use real sRGB space here
299 format->setColorSpace(KisSurfaceColorSpaceWrapper());
300 } else {
301 format->setRedBufferSize(8);
302 format->setGreenBufferSize(8);
303 format->setBlueBufferSize(8);
304 format->setAlphaBufferSize(8);
305 // TODO: check if we can use real sRGB space here
306 format->setColorSpace(KisSurfaceColorSpaceWrapper());
307 }
308 }
309#endif
310}
311
312bool KisOpenGLModeProber::isFormatHDR(const QSurfaceFormat &format)
313{
314#ifdef HAVE_HDR
315
316 bool isBt2020PQ =
318 format.redBufferSize() == 10 &&
319 format.greenBufferSize() == 10 &&
320 format.blueBufferSize() == 10 &&
321 format.alphaBufferSize() == 2;
322
323 bool isBt709G10 =
324 format.colorSpace() == KisSurfaceColorSpaceWrapper::makeSCRGBColorSpace() &&
325 format.redBufferSize() == 16 &&
326 format.greenBufferSize() == 16 &&
327 format.blueBufferSize() == 16 &&
328 format.alphaBufferSize() == 16;
329
330 return isBt2020PQ || isBt709G10;
331#else
332 Q_UNUSED(format);
333 return false;
334#endif
335}
336
338{
339 QString value;
340
341 switch (renderer) {
343 break;
345 value = "d3d9";
346 break;
348 value = "d3d11";
349 break;
351 value = "warp";
352 break;
353 };
354
355 return value;
356}
357
358KisOpenGLModeProber::Result::Result(QOpenGLContext &context) {
359 if (!context.isValid()) {
360 return;
361 }
362
363 QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used
364
365 m_rendererString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_RENDERER)));
366 m_driverVersionString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VERSION)));
367 m_vendorString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VENDOR)));
368 m_shadingLanguageString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)));
369 m_glMajorVersion = context.format().majorVersion();
370 m_glMinorVersion = context.format().minorVersion();
371 m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions);
372 m_isOpenGLES = context.isOpenGLES();
373 m_format = context.format();
374 m_supportsFBO = context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
375
377 m_glMajorVersion >= 3 ||
378 context.hasExtension("GL_OES_mapbuffer") ||
379 context.hasExtension("GL_EXT_map_buffer_range") ||
380 context.hasExtension("GL_ARB_map_buffer_range");
381
383 ((m_glMajorVersion >= 4 && m_glMinorVersion >= 3) ||
384 context.hasExtension("GL_ARB_invalidate_subdata"));
385 m_supportsLod = context.format().majorVersion() >= 3 || (m_isOpenGLES && context.hasExtension("GL_EXT_shader_texture_lod"));
386
387#ifdef HAVE_X11
388 if (QApplication::platformName() == "xcb") {
389 QPlatformNativeInterface *native = qApp->platformNativeInterface();
390 if (native->nativeResourceFunctionForContext("eglcontext")) {
392 } else if (native->nativeResourceFunctionForContext("glxcontext")) {
394 } else {
395 qWarning() << "WARNING: Failed to detect QXcbGlIntegration type!";
396 }
397 }
398#endif /* HAVE_X11 */
399
400 m_extensions = context.extensions();
401 // Remove empty name extension that sometimes appears on NVIDIA output
402 m_extensions.remove("");
403}
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
std::optional< KisOpenGL::XcbGLProviderProtocol > m_xcbGlProviderProtocol
Result(QOpenGLContext &context)
static KisOpenGLModeProber * instance()
boost::optional< Result > probeFormat(const KisOpenGL::RendererConfig &rendererConfig, bool adjustGlobalState=true)
const KoColorProfile * rootSurfaceColorProfile() const
QSurfaceFormat surfaceformatInUse() const
static bool isFormatHDR(const QSurfaceFormat &format)
static void initSurfaceFormatFromConfig(std::pair< KisSurfaceColorSpaceWrapper, int > rootSurfaceFormat, QSurfaceFormat *format)
static QString angleRendererToString(KisOpenGL::AngleRenderer renderer)
static bool fuzzyCompareColorSpaces(const KisSurfaceColorSpaceWrapper &lhs, const KisSurfaceColorSpaceWrapper &rhs)
@ AngleRendererD3d11Warp
Definition kis_opengl.h:53
@ AngleRendererD3d11
Definition kis_opengl.h:51
@ AngleRendererD3d9
Definition kis_opengl.h:52
@ AngleRendererDefault
Definition kis_opengl.h:50
static constexpr KisSurfaceColorSpaceWrapper makeSCRGBColorSpace()
static KisSurfaceColorSpaceWrapper fromQtColorSpace(const QColorSpace &colorSpace)
static constexpr KisSurfaceColorSpaceWrapper makeBt2020PQColorSpace()
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define dbgOpenGL
Definition kis_debug.h:60
#define GL_RENDERER
AngleRenderer angleRenderer
Definition kis_opengl.h:58
QSurfaceFormat format
Definition kis_opengl.h:57
OpenGLRenderer rendererId() const
static KoColorSpaceRegistry * instance()
const KoColorProfile * p709G10Profile() const
const KoColorProfile * p709SRGBProfile() const
const KoColorProfile * p2020PQProfile() const