10#include <boost/optional.hpp>
14#include <QOpenGLContext>
15#include <QOpenGLDebugLogger>
16#include <QOpenGLFunctions>
18#include <QApplication>
20#include <QPixmapCache>
25#include <QStandardPaths>
28#include <QRegularExpression>
32#include <klocalizedstring.h>
44#include <config-hdr.h>
45#include <config-use-surface-color-management-api.h>
48# define GL_RENDERER 0x1F01
51#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
54#if !defined APIENTRYP && defined GL_APIENTRYP
55#define APIENTRYP GL_APIENTRYP
58typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer);
60typedef void (QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer);
66 bool g_isDebugSynchronous =
false;
68 bool g_sanityDefaultFormatIsSet =
false;
70 boost::optional<KisOpenGLModeProber::Result> openGLCheckResult;
72 bool g_needsFenceWorkaround =
false;
74 QString g_surfaceFormatDetectionLog;
75 QString g_debugText(
"OpenGL Info\n **OpenGL not initialized**");
79 using DetectedRenderer = std::tuple<QString, QString, bool>;
81 KisOpenGL::OpenGLRenderers g_supportedRenderers;
84 bool g_useBufferInvalidation =
false;
85 PFNGLINVALIDATEBUFFERDATAPROC g_glInvalidateBufferData =
nullptr;
87 bool g_forceDisableTextureBuffers =
false;
90 g_supportedRenderers = supportedRenderers;
91 g_rendererPreferredByQt = preferredByQt;
94 void appendOpenGLWarningString(KLocalizedString warning)
96 g_openglWarningStrings << warning;
101 g_openglWarningStrings = warnings;
104 void openglOnMessageLogged(
const QOpenGLDebugMessage& debugMessage) {
105 qDebug() <<
"OpenGL:" << debugMessage;
115 if (rendererString.contains(
"basic render driver") ||
116 rendererString.contains(
"software")) {
130 if (openGLCheckResult)
return;
135 config.
format = QSurfaceFormat::defaultFormat();
142 if (!qEnvironmentVariableIsSet(
"KRITA_UNLOCK_TEXTURE_BUFFERS") &&
143 openGLCheckResult->rendererString().toUpper().contains(
"ANGLE")) {
159 g_forceDisableTextureBuffers =
true;
160 appendOpenGLWarningString(
161 ki18n(
"Texture buffers are explicitly disabled on ANGLE renderer due "
162 "to performance issues."));
168 QDebug debugOut(&g_debugText);
169 debugOut <<
"OpenGL Info\n";
171 if (openGLCheckResult) {
172 debugOut <<
"\n Qt Platform Name: " << QGuiApplication::platformName();
173 debugOut <<
"\n Vendor: " << openGLCheckResult->vendorString();
174 debugOut <<
"\n Renderer: " << openGLCheckResult->rendererString();
175 debugOut <<
"\n Driver version: " << openGLCheckResult->driverVersionString();
176 debugOut <<
"\n Shading language: " << openGLCheckResult->shadingLanguageString();
177 debugOut <<
"\n Requested format: " << QSurfaceFormat::defaultFormat();
178 debugOut <<
"\n Current format: " << openGLCheckResult->format();
180 QDebugStateSaver saver(debugOut);
181 debugOut.nospace() <<
"\n GL version: " << openGLCheckResult->glMajorVersion() <<
"."
182 << openGLCheckResult->glMinorVersion();
184 debugOut <<
"\n Supports deprecated functions" << openGLCheckResult->supportsDeprecatedFunctions();
185 debugOut <<
"\n Is OpenGL ES:" << openGLCheckResult->isOpenGLES();
186 debugOut <<
"\n supportsBufferMapping:" << openGLCheckResult->supportsBufferMapping();
187 debugOut <<
"\n supportsBufferInvalidation:" << openGLCheckResult->supportsBufferInvalidation();
188 debugOut <<
"\n forceDisableTextureBuffers:" << g_forceDisableTextureBuffers;
189 debugOut <<
"\n Extensions:";
191 QDebugStateSaver saver(debugOut);
192 Q_FOREACH (
const QByteArray &i, openGLCheckResult->extensions()) {
193 debugOut.noquote() <<
"\n " << QString::fromLatin1(i);
198 debugOut <<
"\n\nQPA OpenGL Detection Info";
199 debugOut <<
"\n supportsDesktopGL:" << bool(g_supportedRenderers &
RendererDesktopGL);
201 debugOut <<
"\n supportsAngleD3D11:" << bool(g_supportedRenderers &
RendererOpenGLES);
202 debugOut <<
"\n isQtPreferAngle:" << bool(g_rendererPreferredByQt ==
RendererOpenGLES);
204 debugOut <<
"\n supportsOpenGLES:" << bool(g_supportedRenderers &
RendererOpenGLES);
205 debugOut <<
"\n isQtPreferOpenGLES:" << bool(g_rendererPreferredByQt ==
RendererOpenGLES);
207 debugOut <<
"\n Detected renderers:";
209 QDebugStateSaver saver(debugOut);
210 Q_FOREACH (
const DetectedRenderer &x, g_detectedRenderers) {
211 debugOut.noquote().nospace() <<
"\n " << (std::get<2>(x) ?
"(Supported)" :
"(Unsupported)") <<
" "
212 << std::get<0>(x) <<
" (" << std::get<1>(x) <<
") ";
222 dbgOpenGL.noquote().nospace() << g_debugText;
225 if (!openGLCheckResult) {
231 bool isOnX11 =
false;
238 g_useBufferInvalidation = cfg.
readEntry(
"useBufferInvalidation",
false);
242 g_needsFenceWorkaround =
true;
258 const qreal devicePixelRatio = QGuiApplication::primaryScreen()->devicePixelRatio();
259 const QSize screenSize = QGuiApplication::primaryScreen()->size() * devicePixelRatio;
260 const int minCacheSize = 20 * 1024;
263 const int cacheSize = 2048 + 5 * 4 * screenSize.width() * screenSize.height() / 1024;
265 QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize));
274 const bool isDebugEnabled = ctx->format().testOption(QSurfaceFormat::DebugContext);
276 dbgUI <<
"OpenGL: Opening new context";
277 if (isDebugEnabled) {
281 QOpenGLDebugLogger* openglLogger =
new QOpenGLDebugLogger(ctx);
282 if (openglLogger->initialize()) {
283 qDebug() <<
"QOpenGLDebugLogger is initialized. Check whether you get a message below.";
284 QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged);
285 openglLogger->startLogging(g_isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging);
286 openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral(
"QOpenGLDebugLogger is logging.")));
288 qDebug() <<
"QOpenGLDebugLogger cannot be initialized.";
294 QSurfaceFormat format = ctx->format();
295 QOpenGLFunctions *f = ctx->functions();
296 f->initializeOpenGLFunctions();
298 if (openGLCheckResult->supportsBufferInvalidation()) {
299 QOpenGLContext *ctx = QOpenGLContext::currentContext();
300 g_glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)ctx->getProcAddress(
"glInvalidateBufferData");
303 QFile log(QStandardPaths::writableLocation(QStandardPaths::TempLocation) +
"/krita-opengl.txt");
304 log.open(QFile::WriteOnly);
305 QString vendor((
const char*)f->glGetString(GL_VENDOR));
306 log.write(vendor.toLatin1());
308 log.write(openGLCheckResult->rendererString().toLatin1());
310 QString version((
const char*)f->glGetString(GL_VERSION));
311 log.write(version.toLatin1());
323 Q_FOREACH (
const KLocalizedString &item, g_openglWarningStrings) {
324 strings << item.toString();
332 if (openGLCheckResult) {
333 return openGLCheckResult->driverVersionString();
343 return openGLCheckResult && openGLCheckResult->supportsLoD();
349 return openGLCheckResult && openGLCheckResult->hasOpenGL3();
355 return openGLCheckResult && openGLCheckResult->supportsVAO();
361 return openGLCheckResult && openGLCheckResult->isOpenGLES();
367 return openGLCheckResult && openGLCheckResult->supportsFenceSync();
373 return openGLCheckResult && openGLCheckResult->supportsBufferMapping();
379 return g_forceDisableTextureBuffers;
385 return !g_forceDisableTextureBuffers && userPreference;
391 return g_useBufferInvalidation &&
392 openGLCheckResult && openGLCheckResult->supportsBufferInvalidation();
398 return openGLCheckResult && openGLCheckResult->supportsFBO();
404 return g_needsFenceWorkaround;
414 g_isDebugSynchronous =
value;
419 g_glInvalidateBufferData(buffer);
425 return getRendererFromProbeResult(*openGLCheckResult);
430 return g_rendererPreferredByQt;
435 return g_supportedRenderers;
440 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
441 QSettings kritarc(configPath + QStringLiteral(
"/kritadisplayrc"), QSettings::IniFormat);
447 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
448 QSettings kritarc(configPath + QStringLiteral(
"/kritadisplayrc"), QSettings::IniFormat);
456 return QStringLiteral(
"none");
458 return QStringLiteral(
"software");
460 return QStringLiteral(
"desktop");
462 return QStringLiteral(
"angle");
464 return QStringLiteral(
"auto");
470 if (renderer ==
"desktop") {
472 }
else if (renderer ==
"angle") {
474 }
else if (renderer ==
"software") {
476 }
else if (renderer ==
"none") {
487 if (
format.renderableType() == QSurfaceFormat::OpenGLES &&
492 }
else if (
format.renderableType() == QSurfaceFormat::OpenGLES) {
496 }
else if (
format.renderableType() == QSurfaceFormat::OpenGL) {
498 }
else if (
format.renderableType() == QSurfaceFormat::DefaultRenderableType &&
510typedef std::pair<QSurfaceFormat::RenderableType, KisOpenGL::AngleRenderer> RendererInfo;
514 RendererInfo info = {QSurfaceFormat::DefaultRenderableType,
537QOpenGLContext::OpenGLModuleType determineOpenGLImplementation(
const RendererInfo &info)
539 switch (info.first) {
540 case QSurfaceFormat::OpenGLES:
541#if defined(Q_OS_WINDOWS)
543 switch (info.second) {
547 return QOpenGLContext::LibGLES;
552 return QOpenGLContext::LibGL;
556 return QOpenGLContext::LibGLES;
558 case QSurfaceFormat::DefaultRenderableType:
561 return QOpenGLContext::LibGL;
564#if defined(QT_OPENGL_ES_2)
565 return QOpenGLContext::LibGLES;
567 return QOpenGLContext::LibGL;
570 case QSurfaceFormat::OpenGL:
575 return QOpenGLContext::LibGL;
580 std::pair<KisSurfaceColorSpaceWrapper, int> rootSurfaceFormat,
583 RendererInfo info = getRendererInfo(renderer);
589 dbgOpenGL <<
"Requesting configuration for" << info.first << info.second;
590 dbgOpenGL <<
"Requesting root surface format" << rootSurfaceFormat;
592 QSurfaceFormat &format = config.
format;
593 const auto openGLModuleType = determineOpenGLImplementation(info);
594 switch (openGLModuleType) {
595 case QOpenGLContext::LibGL:
596#if defined Q_OS_MACOS
597 format.setVersion(4, 1);
598 format.setProfile(QSurfaceFormat::CoreProfile);
604 format.setVersion(3, 3);
607 format.setProfile(QSurfaceFormat::CompatibilityProfile);
612 format.setOptions(QSurfaceFormat::DeprecatedFunctions);
616 case QOpenGLContext::LibGLES:
617 format.setVersion(3, 0);
618 format.setProfile(QSurfaceFormat::NoProfile);
622 dbgOpenGL <<
"Version selected:" << openGLModuleType << format.version();
624 format.setDepthBufferSize(24);
625 format.setStencilBufferSize(8);
629 format.setRenderableType(info.first);
630 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
631 format.setSwapInterval(0);
635 format.setSwapInterval(1);
638 format.setOption(QSurfaceFormat::DebugContext,
true);
644bool isOpenGLRendererBlacklisted(
const QString &rendererString,
645 const QString &driverVersionString,
648 bool isBlacklisted =
false;
650 Q_UNUSED(rendererString);
651 Q_UNUSED(driverVersionString);
652 Q_UNUSED(warningMessage);
659 if (rendererString.startsWith(
"Intel")) {
660 KLocalizedString knownBadIntelWarning = ki18n(
"The Intel graphics driver in use is known to have issues with OpenGL.");
661 KLocalizedString grossIntelWarning = ki18n(
662 "Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. "
663 "You may manually switch to OpenGL but it is not guaranteed to work properly."
665 QRegularExpression regex(
"\\b\\d{1,2}\\.\\d{1,2}\\.(\\d{1,3})\\.(\\d{4})\\b");
666 QRegularExpressionMatch match = regex.match(driverVersionString);
667 if (match.hasMatch()) {
668 const int thirdPart = match.captured(1).toInt();
669 const int fourthPart = match.captured(2).toInt();
671 if (thirdPart >= 100) {
672 driverBuild = thirdPart * 10000 + fourthPart;
674 driverBuild = fourthPart;
676 qDebug() <<
"Detected Intel driver build number as" << driverBuild;
677 if (driverBuild > 4636 && driverBuild < 4729) {
682 qDebug() <<
"Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer";
683 isBlacklisted =
true;
684 *warningMessage << knownBadIntelWarning;
685 }
else if (driverBuild == 4358) {
688 qDebug() <<
"Detected Intel driver build 4358, making ANGLE the preferred renderer";
689 isBlacklisted =
true;
690 *warningMessage << knownBadIntelWarning;
695 qDebug() <<
"Detected Intel driver with unknown version format, making ANGLE the preferred renderer";
696 isBlacklisted =
true;
697 *warningMessage << grossIntelWarning;
701 return isBlacklisted;
704boost::optional<bool> orderPreference(
bool lhs,
bool rhs)
706 if (lhs == rhs)
return boost::none;
707 if (lhs && !rhs)
return true;
708 if (!lhs && rhs)
return false;
712#define ORDER_BY(lhs, rhs) if (auto res = orderPreference((lhs), (rhs))) { return *res; }
714class FormatPositionLess
729 isPreferredColorSpace(rhs.
format));
739 rhs.
rendererId() == m_preferredRendererByUser);
748 rhs.
rendererId() == m_preferredRendererByHDR);
758 rhs.
format.redBufferSize() == m_userPreferredBitDepth);
766 m_preferredColorSpace = preferredColorSpace;
770 m_preferredRendererByQt = preferredRendererByQt;
774 m_preferredRendererByUser = preferredRendererByUser;
778 m_preferredRendererByHDR = preferredRendererByHDR;
781 void setOpenGLBlacklisted(
bool openGLBlacklisted) {
782 m_openGLBlacklisted = openGLBlacklisted;
785 void setOpenGLESBlacklisted(
bool openGLESBlacklisted) {
786 m_openGLESBlacklisted = openGLESBlacklisted;
789 void setUserPreferredBitDepth(
int value) {
790 m_userPreferredBitDepth =
value;
793 bool isOpenGLBlacklisted()
const {
794 return m_openGLBlacklisted;
797 bool isOpenGLESBlacklisted()
const {
798 return m_openGLESBlacklisted;
802 return m_preferredColorSpace;
806 return m_preferredRendererByUser;
809 int userPreferredBitDepth()
const {
810 return m_userPreferredBitDepth;
814 bool isHDRFormat(
const QSurfaceFormat &f)
const {
840 bool doPreferHDR()
const {
849 bool isPreferredColorSpace(
const QSurfaceFormat & surfaceFormat)
const {
851 m_preferredColorSpace,
860 bool m_openGLBlacklisted =
false;
861 bool m_openGLESBlacklisted =
false;
862 int m_userPreferredBitDepth = 8;
865struct DetectionDebug :
public QDebug
867 DetectionDebug(QString *
string)
870 m_originalSize(string->
size())
872 ~DetectionDebug() {
dbgOpenGL << m_string->right(m_string->size() - m_originalSize); *
this << Qt::endl; }
879#define dbgDetection() DetectionDebug(&g_surfaceFormatDetectionLog)
888 using Info = boost::optional<KisOpenGLModeProber::Result>;
890 QHash<OpenGLRenderer, Info> renderersToTest;
900 auto makeDefaultSurfaceFormatPair = [] () -> std::pair<KisSurfaceColorSpaceWrapper, int> {
905 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
912#elif KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
913 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
919 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
926 makeDefaultSurfaceFormatPair(),
false);
933 makeDefaultSurfaceFormatPair(),
false);
944 const OpenGLRenderer defaultRenderer = getRendererFromProbeResult(*info);
957 FormatPositionLess compareOp;
958 compareOp.setPreferredRendererByQt(preferredAutoRenderer);
961 compareOp.setPreferredColorSpace(
966 Q_UNUSED(preferredRootSurfaceFormat);
973 compareOp.setPreferredRendererByUser(preferredRenderer);
974 compareOp.setOpenGLESBlacklisted(
false);
976#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
980 Q_UNUSED(preferredCanvasSurfaceBitMode)
983 renderersToTest[defaultRenderer] = info;
985 for (
auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
986 Info info = it.value();
989 const RendererConfig config = generateSurfaceConfig(it.key(), makeDefaultSurfaceFormatPair(),
false);
995 dbgOpenGL <<
"Already probed:" << it.key();
998 compareOp.setOpenGLBlacklisted(
1000 isOpenGLRendererBlacklisted(info->rendererString(),
1001 info->driverVersionString(),
1005 dbgOpenGL <<
"Result:" << info->rendererString() << info->driverVersionString()
1006 << info->isSupportedVersion();
1010 g_detectedRenderers << std::make_tuple(info->rendererString(),
1011 info->driverVersionString(),
1012 info->isSupportedVersion());
1015 if (info && info->isSupportedVersion()) {
1016 supportedRenderers |= it.key();
1024 compareOp.isOpenGLBlacklisted()) {
1030 compareOp.isOpenGLESBlacklisted()) {
1036 for (
auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
1038 if (!it.value())
continue;
1040 Q_FOREACH (
const auto &formatPair, formatSymbolPairs) {
1041 preferredConfigs << generateSurfaceConfig(it.key(), formatPair, enableDebug);
1045 std::stable_sort(preferredConfigs.begin(), preferredConfigs.end(), compareOp);
1047 dbgDetection() <<
"Supported renderers:" << supportedRenderers;
1062 if (info && info->isSupportedVersion()) {
1069 if (info->isUsingAngle() &&
1070 info->rendererString().contains(
"Direct3D9", Qt::CaseInsensitive)) {
1072 dbgDetection() <<
"Skipping Direct3D 9 Angle implementation, it shouldn't have happened.";
1081 resultConfig = config;
1087 const bool colorSpaceIsCorrect =
1091 const bool rendererIsCorrect =
1093 compareOp.preferredRendererByUser() == resultConfig.
rendererId();
1095 if (!rendererIsCorrect && colorSpaceIsCorrect) {
1096 warningMessages << ki18n(
"Preferred renderer doesn't support requested surface format. Another renderer has been selected.");
1097 }
else if (!colorSpaceIsCorrect) {
1098 warningMessages << ki18n(
"Preferred output format is not supported by available renderers");
1103 resultConfig.
format = QSurfaceFormat();
1107 overrideSupportedRenderers(supportedRenderers, preferredByQt);
1108 overrideOpenGLWarningString(warningMessages);
1110 return resultConfig;
1117 g_sanityDefaultFormatIsSet =
true;
1118 QSurfaceFormat::setDefaultFormat(config.
format);
1120 if (config.
format.renderableType() == QSurfaceFormat::OpenGLES) {
1121 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES,
true);
1123 if (!qEnvironmentVariableIsSet(
"QT_ANGLE_PLATFORM")) {
1129 }
else if (config.
format.renderableType() == QSurfaceFormat::OpenGL) {
1130 QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL,
true);
1136 return openGLCheckResult->isSupportedVersion();
float value(const T *src, size_t ch)
@ ASSISTANTS_DRAW_MODE_LARGE_PIXMAP_CACHE
bool forceOpenGLFenceWorkaround(bool defaultValue=false) const
T readEntry(const QString &name, const T &defaultValue=T())
CanvasSurfaceBitDepthMode
AssistantsDrawMode assistantsDrawMode(bool defaultValue=false) const
QString rendererString() const
static KisOpenGLModeProber * instance()
boost::optional< Result > probeFormat(const KisOpenGL::RendererConfig &rendererConfig, bool adjustGlobalState=true)
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)
static bool useFBOForToolOutlineRendering()
supportsRenderToFBO
static OpenGLRenderer getCurrentOpenGLRenderer()
static bool hasOpenGLES()
static RendererConfig selectSurfaceConfig(KisOpenGL::OpenGLRenderer preferredRenderer, KisConfig::RootSurfaceFormat preferredRootSurfaceFormat, KisConfig::CanvasSurfaceBitDepthMode preferredCanvasSurfaceBitMode, bool enableDebug)
static bool shouldUseTextureBuffers(bool userPreference)
static bool needsFenceWorkaround()
static void testingInitializeDefaultSurfaceFormat()
static OpenGLRenderers getSupportedOpenGLRenderers()
static bool useTextureBufferInvalidation()
static QStringList getOpenGLWarnings()
static void initialize()
Request OpenGL version 3.2.
static void glInvalidateBufferData(uint buffer)
static bool hasOpenGL()
Check for OpenGL.
static bool supportsLoD()
static QString currentDriver()
static void initializeContext(QOpenGLContext *ctx)
Initialize shared OpenGL context.
static OpenGLRenderer getUserPreferredOpenGLRendererConfig()
static OpenGLRenderer getQtPreferredOpenGLRenderer()
static OpenGLRenderer convertConfigToOpenGLRenderer(QString renderer)
static bool supportsFenceSync()
supportsFilter
static bool supportsBufferMapping()
static bool forceDisableTextureBuffers()
static void setDebugSynchronous(bool value)
static QString convertOpenGLRendererToConfig(OpenGLRenderer renderer)
static void setDefaultSurfaceConfig(const RendererConfig &config)
static bool supportsVAO()
static const QString & getDebugText()
static void setUserPreferredOpenGLRendererConfig(OpenGLRenderer renderer)
static constexpr KisSurfaceColorSpaceWrapper makeSCRGBColorSpace()
static KisSurfaceColorSpaceWrapper fromQtColorSpace(const QColorSpace &colorSpace)
static constexpr KisSurfaceColorSpaceWrapper makeBt2020PQColorSpace()
static void writeSysInfo(const QString &message)
Writes to the system information file and Krita log.
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
#define ORDER_BY(lhs, rhs)
int size(const Forest< T > &forest)
AngleRenderer angleRenderer
OpenGLRenderer rendererId() const