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#include <QGlobalStatic>
19
21
22
26
31
33{
34 return s_instance;
35}
36
38{
39 return isFormatHDR(QSurfaceFormat::defaultFormat());
40}
41
43{
44 // TODO: use information provided by KisOpenGL instead
45 QOpenGLContext *sharedContext = QOpenGLContext::globalShareContext();
46 QSurfaceFormat format = sharedContext ? sharedContext->format() : QSurfaceFormat::defaultFormat();
47 return format;
48}
49
51{
53
54 const auto surfaceColorSpace =
56
57 if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::sRGBColorSpace) {
58 // use the default one!
59#ifdef HAVE_HDR
60 } else if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
62 } else if (surfaceColorSpace == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
64#endif
65 }
66
67 return profile;
68}
69
70namespace {
71struct AppAttributeSetter
72{
73 AppAttributeSetter(Qt::ApplicationAttribute attribute, bool useOpenGLES)
74 : m_attribute(attribute),
75 m_oldValue(QCoreApplication::testAttribute(attribute))
76 {
77 QCoreApplication::setAttribute(attribute, useOpenGLES);
78 }
79
80 ~AppAttributeSetter() {
81 QCoreApplication::setAttribute(m_attribute, m_oldValue);
82 }
83
84private:
85 Qt::ApplicationAttribute m_attribute;
86 bool m_oldValue = false;
87};
88
89struct SurfaceFormatSetter
90{
91 SurfaceFormatSetter(const QSurfaceFormat &format)
92 : m_oldFormat(QSurfaceFormat::defaultFormat())
93 {
94 QSurfaceFormat::setDefaultFormat(format);
95 }
96
97 ~SurfaceFormatSetter() {
98 QSurfaceFormat::setDefaultFormat(m_oldFormat);
99 }
100
101private:
102 QSurfaceFormat m_oldFormat;
103};
104
105
106struct EnvironmentSetter
107{
108 EnvironmentSetter(const QLatin1String &env, const QString &value)
109 : m_env(env)
110 {
111 if (!qEnvironmentVariableIsEmpty(m_env.latin1())) {
112 m_oldValue = qgetenv(env.latin1());
113 }
114 if (!value.isEmpty()) {
115 qputenv(env.latin1(), value.toLatin1());
116 } else {
117 qunsetenv(env.latin1());
118 }
119 }
120
121 ~EnvironmentSetter() {
122 if (m_oldValue) {
123 qputenv(m_env.latin1(), (*m_oldValue).toLatin1());
124 } else {
125 qunsetenv(m_env.latin1());
126 }
127 }
128
129private:
130 const QLatin1String m_env;
131 boost::optional<QString> m_oldValue;
132};
133
134}
135
136boost::optional<KisOpenGLModeProber::Result>
138 bool adjustGlobalState)
139{
140 const QSurfaceFormat &format = rendererConfig.format;
141
142 dbgOpenGL << "Probing format" << rendererConfig.rendererId() << rendererConfig.angleRenderer
143 << rendererConfig.format;
144
145 QScopedPointer<AppAttributeSetter> sharedContextSetter;
146 QScopedPointer<AppAttributeSetter> glSetter;
147 QScopedPointer<AppAttributeSetter> glesSetter;
148 QScopedPointer<SurfaceFormatSetter> formatSetter;
149 QScopedPointer<EnvironmentSetter> rendererSetter;
150 QScopedPointer<EnvironmentSetter> portalSetter;
151 QScopedPointer<QGuiApplication> application;
152
153 int argc = 1;
154 QByteArray probeAppName("krita");
155 char *argv = probeAppName.data();
156
157
158 if (adjustGlobalState) {
159 sharedContextSetter.reset(new AppAttributeSetter(Qt::AA_ShareOpenGLContexts, false));
160
161 if (format.renderableType() != QSurfaceFormat::DefaultRenderableType) {
162 glSetter.reset(new AppAttributeSetter(Qt::AA_UseDesktopOpenGL, format.renderableType() != QSurfaceFormat::OpenGLES));
163 glesSetter.reset(new AppAttributeSetter(Qt::AA_UseOpenGLES, format.renderableType() == QSurfaceFormat::OpenGLES));
164 }
165
166 if (!qEnvironmentVariableIsSet("QT_ANGLE_PLATFORM")) {
167 rendererSetter.reset(new EnvironmentSetter(QLatin1String("QT_ANGLE_PLATFORM"), angleRendererToString(rendererConfig.angleRenderer)));
168 }
169 portalSetter.reset(new EnvironmentSetter(QLatin1String("QT_NO_XDG_DESKTOP_PORTAL"), QLatin1String("1")));
170 formatSetter.reset(new SurfaceFormatSetter(format));
171
172 // Disable this workaround for plasma (BUG:408015), because it causes
173 // a crash on Windows with Qt 5.15.7
174 const bool runningInKDE = qEnvironmentVariableIsSet("KDE_FULL_SESSION");
175 const bool isInAppimage = qEnvironmentVariableIsSet("APPIMAGE");
176
177 if (runningInKDE && !isInAppimage) {
178 QGuiApplication::setDesktopSettingsAware(false);
179 }
180
181 application.reset(new QGuiApplication(argc, &argv));
182
183 if (runningInKDE && !isInAppimage) {
184 QGuiApplication::setDesktopSettingsAware(true);
185 }
186
187 }
188
189 QWindow surface;
190 surface.setFormat(format);
191 surface.setSurfaceType(QSurface::OpenGLSurface);
192 surface.create();
193 QOpenGLContext context;
194 context.setFormat(format);
195
196
197 if (!context.create()) {
198 dbgOpenGL << "OpenGL context cannot be created";
199 return boost::none;
200 }
201 if (!context.isValid()) {
202 dbgOpenGL << "OpenGL context is not valid while checking Qt's OpenGL status";
203 return boost::none;
204 }
205 if (!context.makeCurrent(&surface)) {
206 dbgOpenGL << "OpenGL context cannot be made current";
207 return boost::none;
208 }
209
211 KisSurfaceColorSpaceWrapper::fromQtColorSpace(context.format().colorSpace()),
212 KisSurfaceColorSpaceWrapper::fromQtColorSpace(format.colorSpace()))) {
213
214 dbgOpenGL << "Failed to create an OpenGL context with requested color space. Requested:" << format.colorSpace() << "Actual:" << context.format().colorSpace();
215 return boost::none;
216 }
217
218 if (format.redBufferSize() > 0 && format.greenBufferSize() > 0 && format.blueBufferSize() > 0
219 && (context.format().redBufferSize() != format.redBufferSize()
220 || context.format().greenBufferSize() != format.greenBufferSize()
221 || context.format().blueBufferSize() != format.blueBufferSize())) {
222
223 dbgOpenGL << "Failed to create an OpenGL context with requested bit depth. Requested:" << format.redBufferSize()
224 << "Actual:" << context.format().redBufferSize();
225 return boost::none;
226 }
227
228 Result result(context);
229
230 dbgOpenGL << "Probe returned" << result.rendererString() << result.driverVersionString() << result.isOpenGLES();
231
232 return result;
233}
234
243
244void KisOpenGLModeProber::initSurfaceFormatFromConfig(std::pair<KisSurfaceColorSpaceWrapper, int> rootSurfaceFormat,
245 QSurfaceFormat *format)
246{
247#ifdef HAVE_HDR
251 if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
252 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 10);
253 format->setRedBufferSize(10);
254 format->setGreenBufferSize(10);
255 format->setBlueBufferSize(10);
256 format->setAlphaBufferSize(2);
258 } else if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
259 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 16);
260 format->setRedBufferSize(16);
261 format->setGreenBufferSize(16);
262 format->setBlueBufferSize(16);
263 format->setAlphaBufferSize(16);
265 } else {
266 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 8);
267 format->setRedBufferSize(8);
268 format->setGreenBufferSize(8);
269 format->setBlueBufferSize(8);
270 format->setAlphaBufferSize(8);
271 // TODO: check if we can use real sRGB space here
272 format->setColorSpace(KisSurfaceColorSpaceWrapper());
273 }
274#else
279 if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace) {
280 qWarning() << "WARNING: Bt.2020 PQ surface type is not supported by this build of Krita";
281 rootSurfaceFormat.first = KisSurfaceColorSpaceWrapper::DefaultColorSpace;
282 } else if (rootSurfaceFormat.first == KisSurfaceColorSpaceWrapper::scRGBColorSpace) {
283 qWarning() << "WARNING: scRGB surface type is not supported by this build of Krita";
284 rootSurfaceFormat.first = KisSurfaceColorSpaceWrapper::DefaultColorSpace;
285 } else {
287 KIS_SAFE_ASSERT_RECOVER_NOOP(rootSurfaceFormat.second == 8 || rootSurfaceFormat.second == 10);
288
289 if (rootSurfaceFormat.second == 10) {
290 format->setRedBufferSize(10);
291 format->setGreenBufferSize(10);
292 format->setBlueBufferSize(10);
293 format->setAlphaBufferSize(2);
294 // TODO: check if we can use real sRGB space here
295 format->setColorSpace(KisSurfaceColorSpaceWrapper());
296 } else {
297 format->setRedBufferSize(8);
298 format->setGreenBufferSize(8);
299 format->setBlueBufferSize(8);
300 format->setAlphaBufferSize(8);
301 // TODO: check if we can use real sRGB space here
302 format->setColorSpace(KisSurfaceColorSpaceWrapper());
303 }
304 }
305#endif
306}
307
308bool KisOpenGLModeProber::isFormatHDR(const QSurfaceFormat &format)
309{
310#ifdef HAVE_HDR
311
312 bool isBt2020PQ =
314 format.redBufferSize() == 10 &&
315 format.greenBufferSize() == 10 &&
316 format.blueBufferSize() == 10 &&
317 format.alphaBufferSize() == 2;
318
319 bool isBt709G10 =
320 format.colorSpace() == KisSurfaceColorSpaceWrapper::makeSCRGBColorSpace() &&
321 format.redBufferSize() == 16 &&
322 format.greenBufferSize() == 16 &&
323 format.blueBufferSize() == 16 &&
324 format.alphaBufferSize() == 16;
325
326 return isBt2020PQ || isBt709G10;
327#else
328 Q_UNUSED(format);
329 return false;
330#endif
331}
332
334{
335 QString value;
336
337 switch (renderer) {
339 break;
341 value = "d3d9";
342 break;
344 value = "d3d11";
345 break;
347 value = "warp";
348 break;
349 };
350
351 return value;
352}
353
354KisOpenGLModeProber::Result::Result(QOpenGLContext &context) {
355 if (!context.isValid()) {
356 return;
357 }
358
359 QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used
360
361 m_rendererString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_RENDERER)));
362 m_driverVersionString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VERSION)));
363 m_vendorString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VENDOR)));
364 m_shadingLanguageString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)));
365 m_glMajorVersion = context.format().majorVersion();
366 m_glMinorVersion = context.format().minorVersion();
367 m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions);
368 m_isOpenGLES = context.isOpenGLES();
369 m_format = context.format();
370 m_supportsFBO = context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers);
371
373 m_glMajorVersion >= 3 ||
374 context.hasExtension("GL_OES_mapbuffer") ||
375 context.hasExtension("GL_EXT_map_buffer_range") ||
376 context.hasExtension("GL_ARB_map_buffer_range");
377
379 ((m_glMajorVersion >= 4 && m_glMinorVersion >= 3) ||
380 context.hasExtension("GL_ARB_invalidate_subdata"));
381 m_supportsLod = context.format().majorVersion() >= 3 || (m_isOpenGLES && context.hasExtension("GL_EXT_shader_texture_lod"));
382
383 m_extensions = context.extensions();
384 // Remove empty name extension that sometimes appears on NVIDIA output
385 m_extensions.remove("");
386}
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
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