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_HDR
18// for fetching the legacy 80-nits PQ space
19#include <KoColorProfileQuery.h>
20#endif
21
22#ifdef HAVE_X11
23#include <qpa/qplatformnativeinterface.h>
24#endif
25
26#include <QGlobalStatic>
28
30
31
35
40
42{
43 return s_instance;
44}
45
47{
48 return isFormatHDR(QSurfaceFormat::defaultFormat());
49}
50
52{
53 // TODO: use information provided by KisOpenGL instead
54 QOpenGLContext *sharedContext = QOpenGLContext::globalShareContext();
55 QSurfaceFormat format = sharedContext ? sharedContext->format() : QSurfaceFormat::defaultFormat();
56 return format;
57}
58
60{
62
63 const auto surfaceColorSpace =
65
66 if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::sRGBColorSpace) {
67 // use the default one!
68#ifdef HAVE_HDR
69 } else if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
71 } else if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
72 // On Windows we use the legacy PQ profile, explicitly pinned to
73 // 80 nits. It is baked in the Qt's source code
75 query.hdrReferenceWhite = 80.0;
77#endif
78 }
79
80 return profile;
81}
82
83namespace {
84struct AppAttributeSetter
85{
86 AppAttributeSetter(Qt::ApplicationAttribute attribute, bool useOpenGLES)
87 : m_attribute(attribute),
88 m_oldValue(QCoreApplication::testAttribute(attribute))
89 {
90 QCoreApplication::setAttribute(attribute, useOpenGLES);
91 }
92
93 ~AppAttributeSetter() {
94 QCoreApplication::setAttribute(m_attribute, m_oldValue);
95 }
96
97private:
98 Qt::ApplicationAttribute m_attribute;
99 bool m_oldValue = false;
100};
101
102struct SurfaceFormatSetter
103{
104 SurfaceFormatSetter(const QSurfaceFormat &format)
105 : m_oldFormat(QSurfaceFormat::defaultFormat())
106 {
107 QSurfaceFormat::setDefaultFormat(format);
108 }
109
110 ~SurfaceFormatSetter() {
111 QSurfaceFormat::setDefaultFormat(m_oldFormat);
112 }
113
114private:
115 QSurfaceFormat m_oldFormat;
116};
117
118
119struct EnvironmentSetter
120{
121 EnvironmentSetter(const QLatin1String &env, const QString &value)
122 : m_env(env)
123 {
124 if (!qEnvironmentVariableIsEmpty(m_env.latin1())) {
125 m_oldValue = qgetenv(env.latin1());
126 }
127 if (!value.isEmpty()) {
128 qputenv(env.latin1(), value.toLatin1());
129 } else {
130 qunsetenv(env.latin1());
131 }
132 }
133
134 ~EnvironmentSetter() {
135 if (m_oldValue) {
136 qputenv(m_env.latin1(), (*m_oldValue).toLatin1());
137 } else {
138 qunsetenv(m_env.latin1());
139 }
140 }
141
142private:
143 const QLatin1String m_env;
144 boost::optional<QString> m_oldValue;
145};
146
147}
148
149boost::optional<KisOpenGLModeProber::Result>
151 bool adjustGlobalState)
152{
153 const QSurfaceFormat &format = rendererConfig.format;
154
155 dbgOpenGL << "Probing format" << rendererConfig.rendererId() << rendererConfig.angleRenderer
156 << rendererConfig.format;
157
158 QScopedPointer<AppAttributeSetter> sharedContextSetter;
159 QScopedPointer<AppAttributeSetter> glSetter;
160 QScopedPointer<AppAttributeSetter> glesSetter;
161 QScopedPointer<SurfaceFormatSetter> formatSetter;
162 QScopedPointer<EnvironmentSetter> rendererSetter;
163 QScopedPointer<EnvironmentSetter> portalSetter;
164 QScopedPointer<QGuiApplication> application;
165
166 int argc = 1;
167 QByteArray probeAppName("krita");
168 char *argv = probeAppName.data();
169
170
171 if (adjustGlobalState) {
172 sharedContextSetter.reset(new AppAttributeSetter(Qt::AA_ShareOpenGLContexts, false));
173
174 if (format.renderableType() != QSurfaceFormat::DefaultRenderableType) {
175 glSetter.reset(new AppAttributeSetter(Qt::AA_UseDesktopOpenGL, format.renderableType() != QSurfaceFormat::OpenGLES));
176 glesSetter.reset(new AppAttributeSetter(Qt::AA_UseOpenGLES, format.renderableType() == QSurfaceFormat::OpenGLES));
177 }
178
179 if (!qEnvironmentVariableIsSet("QT_ANGLE_PLATFORM")) {
180 rendererSetter.reset(new EnvironmentSetter(QLatin1String("QT_ANGLE_PLATFORM"), angleRendererToString(rendererConfig.angleRenderer)));
181 }
182 portalSetter.reset(new EnvironmentSetter(QLatin1String("QT_NO_XDG_DESKTOP_PORTAL"), QLatin1String("1")));
183 formatSetter.reset(new SurfaceFormatSetter(format));
184
185 // Disable this workaround for plasma (BUG:408015), because it causes
186 // a crash on Windows with Qt 5.15.7
187 const bool runningInKDE = qEnvironmentVariableIsSet("KDE_FULL_SESSION");
188 const bool isInAppimage = qEnvironmentVariableIsSet("APPIMAGE");
189
190 if (runningInKDE && !isInAppimage) {
191 QGuiApplication::setDesktopSettingsAware(false);
192 }
193
194 application.reset(new QGuiApplication(argc, &argv));
195
196 if (runningInKDE && !isInAppimage) {
197 QGuiApplication::setDesktopSettingsAware(true);
198 }
199
200 }
201
202 QWindow surface;
203 surface.setFormat(format);
204 surface.setSurfaceType(QSurface::OpenGLSurface);
205 surface.create();
206 QOpenGLContext context;
207 context.setFormat(format);
208
209
210 if (!context.create()) {
211 dbgOpenGL << "OpenGL context cannot be created";
212 return boost::none;
213 }
214 if (!context.isValid()) {
215 dbgOpenGL << "OpenGL context is not valid while checking Qt's OpenGL status";
216 return boost::none;
217 }
218 if (!context.makeCurrent(&surface)) {
219 dbgOpenGL << "OpenGL context cannot be made current";
220 return boost::none;
221 }
222
224 KisSurfaceColorSpaceWrapper::fromQtColorSpace(context.format().colorSpace()),
225 KisSurfaceColorSpaceWrapper::fromQtColorSpace(format.colorSpace()))) {
226
227 dbgOpenGL << "Failed to create an OpenGL context with requested color space. Requested:" << format.colorSpace() << "Actual:" << context.format().colorSpace();
228 return boost::none;
229 }
230
231 if (format.redBufferSize() > 0 && format.greenBufferSize() > 0 && format.blueBufferSize() > 0
232 && (context.format().redBufferSize() != format.redBufferSize()
233 || context.format().greenBufferSize() != format.greenBufferSize()
234 || context.format().blueBufferSize() != format.blueBufferSize())) {
235
236 dbgOpenGL << "Failed to create an OpenGL context with requested bit depth. Requested:" << format.redBufferSize()
237 << "Actual:" << context.format().redBufferSize();
238 return boost::none;
239 }
240
241 Result result(context);
242
243 dbgOpenGL << "Probe returned" << result.rendererString() << result.driverVersionString() << result.isOpenGLES();
244
245 return result;
246}
247
256
257void KisOpenGLModeProber::initSurfaceFormatFromConfig(std::pair<KisSurfaceColorSpaceWrapper, int> rootSurfaceFormat,
258 QSurfaceFormat *format)
259{
260#ifdef HAVE_HDR
264 if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
265 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 10);
266 format->setRedBufferSize(10);
267 format->setGreenBufferSize(10);
268 format->setBlueBufferSize(10);
269 format->setAlphaBufferSize(2);
271 } else if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
272 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 16);
273 format->setRedBufferSize(16);
274 format->setGreenBufferSize(16);
275 format->setBlueBufferSize(16);
276 format->setAlphaBufferSize(16);
278 } else {
279 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 8);
280 format->setRedBufferSize(8);
281 format->setGreenBufferSize(8);
282 format->setBlueBufferSize(8);
283 format->setAlphaBufferSize(8);
284 // TODO: check if we can use real sRGB space here
285 format->setColorSpace(KisSurfaceColorSpaceWrapper());
286 }
287#else
292 if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
293 qWarning() << "WARNING: Bt.2020 PQ surface type is not supported by this build of Krita";
294 rootSurfaceFormat.first = KisSurfaceColorSpaceWrapper::DefaultColorSpace;
295 } else if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
296 qWarning() << "WARNING: scRGB surface type is not supported by this build of Krita";
297 rootSurfaceFormat.first = KisSurfaceColorSpaceWrapper::DefaultColorSpace;
298 } else {
300 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 8 || rootSurfaceFormat.second == 10);
301
302 if (rootSurfaceFormat.second == 10) {
303 format->setRedBufferSize(10);
304 format->setGreenBufferSize(10);
305 format->setBlueBufferSize(10);
306 format->setAlphaBufferSize(2);
307 // TODO: check if we can use real sRGB space here
308 format->setColorSpace(KisSurfaceColorSpaceWrapper());
309 } else {
310 format->setRedBufferSize(8);
311 format->setGreenBufferSize(8);
312 format->setBlueBufferSize(8);
313 format->setAlphaBufferSize(8);
314 // TODO: check if we can use real sRGB space here
315 format->setColorSpace(KisSurfaceColorSpaceWrapper());
316 }
317 }
318#endif
319}
320
321bool KisOpenGLModeProber::isFormatHDR(const QSurfaceFormat &format)
322{
323#ifdef HAVE_HDR
324
325 bool isBt2020PQ =
327 format.redBufferSize() == 10 &&
328 format.greenBufferSize() == 10 &&
329 format.blueBufferSize() == 10 &&
330 format.alphaBufferSize() == 2;
331
332 bool isBt709G10 =
333 format.colorSpace() == KisSurfaceColorSpaceWrapper::makeSCRGBColorSpace() &&
334 format.redBufferSize() == 16 &&
335 format.greenBufferSize() == 16 &&
336 format.blueBufferSize() == 16 &&
337 format.alphaBufferSize() == 16;
338
339 return isBt2020PQ || isBt709G10;
340#else
341 Q_UNUSED(format);
342 return false;
343#endif
344}
345
347{
348 QString value;
349
350 switch (renderer) {
352 break;
354 value = "d3d9";
355 break;
357 value = "d3d11";
358 break;
360 value = "warp";
361 break;
362 };
363
364 return value;
365}
366
367KisOpenGLModeProber::Result::Result(QOpenGLContext &context) {
368 if (!context.isValid()) {
369 return;
370 }
371
372 QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used
373
374 m_rendererString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_RENDERER)));
375 m_driverVersionString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VERSION)));
376 m_vendorString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VENDOR)));
377 m_shadingLanguageString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)));
378 m_glMajorVersion = context.format().majorVersion();
379 m_glMinorVersion = context.format().minorVersion();
380 m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions);
381 m_isOpenGLES = context.isOpenGLES();
382 m_format = context.format();
383 m_supportsFBO = context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
384
386 m_glMajorVersion >= 3 ||
387 context.hasExtension("GL_OES_mapbuffer") ||
388 context.hasExtension("GL_EXT_map_buffer_range") ||
389 context.hasExtension("GL_ARB_map_buffer_range");
390
392 ((m_glMajorVersion >= 4 && m_glMinorVersion >= 3) ||
393 context.hasExtension("GL_ARB_invalidate_subdata"));
394 m_supportsLod = context.format().majorVersion() >= 3 || (m_isOpenGLES && context.hasExtension("GL_EXT_shader_texture_lod"));
395
396#ifdef HAVE_X11
397 if (QApplication::platformName() == "xcb") {
398 QPlatformNativeInterface *native = qApp->platformNativeInterface();
399 if (native->nativeResourceFunctionForContext("eglcontext")) {
401 } else if (native->nativeResourceFunctionForContext("glxcontext")) {
403 } else {
404 qWarning() << "WARNING: Failed to detect QXcbGlIntegration type!";
405 }
406 }
407#endif /* HAVE_X11 */
408
409 m_extensions = context.extensions();
410 // Remove empty name extension that sometimes appears on NVIDIA output
411 m_extensions.remove("");
412}
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
@ PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
@ TRC_ITU_R_BT_2100_0_PQ
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:63
#define GL_RENDERER
AngleRenderer angleRenderer
Definition kis_opengl.h:58
QSurfaceFormat format
Definition kis_opengl.h:57
OpenGLRenderer rendererId() const
The KoColorProfileQuery struct.
std::optional< double > hdrReferenceWhite
CICP-compatible enum value representing the transfer function.
static KoColorSpaceRegistry * instance()
const KoColorProfile * profileFor(const KoColorProfileQuery &query, const bool generate=true) const
profileFor tries to find the profile that matches these characteristics, if no such profile is found,...
const KoColorProfile * p709G10Profile() const
const KoColorProfile * p709SRGBProfile() const