OpenGLDetection.hh 7.23 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

namespace
{
    
// Print human-readable GL profile strings
std::string profileToString(QSurfaceFormat::OpenGLContextProfile _profile)
{
    if(_profile == QSurfaceFormat::CompatibilityProfile)
        return "CompatibilityProfile";

    if(_profile == QSurfaceFormat::CoreProfile)
        return "CoreProfile";

    if(_profile == QSurfaceFormat::NoProfile)
        return "NoProfile";

    return "[Unknown]";
}

// Check whether a specific context request can be fulfilled
// Can also return the format of the actually created context
bool verifySpecificContextFormat(QSurfaceFormat format, QSurfaceFormat* resultingFormat = nullptr)
{
    // All created surfaces try to obey the given format
    QSurfaceFormat::setDefaultFormat(format);

    // We need a temporary qApp to create a surface and test the current context
    int tempArgC = 0;
    QApplication tempApp(tempArgC, nullptr);
    QOffscreenSurface *surface = new QOffscreenSurface();
    surface->create();

    auto shareContext = QOpenGLContext::globalShareContext();
    if(!shareContext)
    {
        std::cerr << "Error: Apparently no GL context was created!" << std::endl;
        return false;
    }

    // Make the globally shared OpenGLContext current
    shareContext->makeCurrent(surface);

    // The opengl surface properties that have actually been applied
    // (does not necessarily match the requested properties)
    auto resultFormat = QOpenGLContext::globalShareContext()->format();

    // Return the format of the actually created context (may be identical to the requested one)
    if(resultingFormat != nullptr)
        *resultingFormat = resultFormat;

    auto curVersion = resultFormat.version();

    // Human-readable name of requested profile
    auto reqProfileString = profileToString(format.profile());

    // Human-readable name of current profile
    auto curProfileString = profileToString(resultFormat.profile());

    
    // Example: OpenGL Version 4.6 -> 46
    auto reqVersionInt = format.version().first * 10 + format.version().second;
    auto curVersionInt = curVersion.first * 10 + curVersion.second;
    
    

    // We set the following guidelines:
    // 1. Whenever the actually received GL version is < than the requested one, the context is not the one requested
    // 2. If the received profile is not the requested one, the context is not the one requested
        
    if(curVersionInt < 32 && resultFormat.profile() == QSurfaceFormat::CoreProfile)
    {
        std::cerr << "Warning: Got an OpenGL core context with OpengGL version < 3.2 (" << curVersion.first << "." << curVersion.second << ")! This should not be possible." << std::endl;
        return false;
    }
    
    // Check whether the conditions above are met.
    // If not, print some error to the console
    if(curVersionInt < reqVersionInt ||
       format.profile()!= resultFormat.profile() )
    {
        std::cout << "[OpenGL context] Requested: "
                  << format.version().first << "." << format.version().second << " (" << reqProfileString << ")"
                  << ", Actually created: "
                  << curVersion.first << "." << curVersion.second << " (" << curProfileString << ")"
                  << std::endl;
        return false;
    }

    std::cout << "[OpenGL context] Successfully created OpenGL context with version " << curVersion.first << "."
              << curVersion.second << " (" << curProfileString << ")." << std::endl;

    return true;
}

// Create a QSurfaceFormat from the most important properties like version and profile
QSurfaceFormat createFormat(QSurfaceFormat::OpenGLContextProfile _profile, int _glMajor, int _glMinor, int _multisamplingSamples, bool _stereo, bool _debugContext)
{
    QSurfaceFormat format;
    format.setVersion(_glMajor, _glMinor);
    format.setProfile(_profile);
    format.setSamples(_multisamplingSamples);
    format.setStereo(_stereo);
    if(_profile != QSurfaceFormat::CoreProfile)
        format.setOption(QSurfaceFormat::DeprecatedFunctions);
    if (_debugContext)
        format.setOption(QSurfaceFormat::DebugContext);

    return format;
}

// This method tries to find the best possible OpenGL context format in the following order:
// 1. The profile/format requested via the settings
// 2. A 4.4 compatibility context (should contain all relevant GL functions)
// 3. A 3.2 core context (best choice e.g. on MacOS)
// 4. Return whatever context was applied instead of the requested ones
QSurfaceFormat getContextFormat()
{
    auto reqProfile = OpenFlipper::Options::coreProfile() ? QSurfaceFormat::CoreProfile : QSurfaceFormat::CompatibilityProfile;
    QPair<int,int> reqVersion = OpenFlipper::Options::glVersion();
    auto reqSamples = OpenFlipper::Options::samples();
    auto reqStereo = OpenFlipper::Options::glStereo();
    bool debugContext = OpenFlipper::Options::debug();



    /*
    // Debug: test all (possible and impossible) OpenGL versions and profiles and exit
    for(int majo = 1; majo < 5; ++majo)
        for(int mino = 0; mino < 10; ++mino)
    {
        std::cout << "========== " << majo << "." << mino << " ==========="<<std::endl;
        verifySpecificContextFormat(createFormat(QSurfaceFormat::CoreProfile, majo, mino, reqSamples, reqStereo, debugContext));
        verifySpecificContextFormat(createFormat(QSurfaceFormat::CompatibilityProfile, majo, mino, reqSamples, reqStereo, debugContext));
        verifySpecificContextFormat(createFormat(QSurfaceFormat::NoProfile, majo, mino, reqSamples, reqStereo, debugContext));
        std::cout << "================================" << std::endl;
        std::cout << std::endl;
    }
    exit(0);
    */



    QSurfaceFormat resultFormat;


    std::cout << "[OpenGL context] Trying to create a " << reqVersion.first << "." << reqVersion.second << " " << profileToString(reqProfile) << " context (default from settings)..." << std::endl;
    bool success = verifySpecificContextFormat(createFormat(reqProfile, reqVersion.first, reqVersion.second, reqSamples, reqStereo, debugContext), &resultFormat);

    // If that did not work...
    if(!success)
    {
        std::cout << "[OpenGL context] Trying to create a 4.4 compat context..." << std::endl;
        success = verifySpecificContextFormat(createFormat(QSurfaceFormat::CompatibilityProfile, 4, 4, reqSamples, reqStereo, debugContext), &resultFormat);
        
        if(!success)
        {
            std::cout << "[OpenGL context] Trying to create a 3.2 core context..." << std::endl;
            success = verifySpecificContextFormat(createFormat(QSurfaceFormat::CoreProfile, 3, 2, reqSamples, reqStereo, debugContext), &resultFormat);
            if(!success)
            {
                std::cerr << "[OpenGL context] Warning: Could not create any of the requested GL contexts." << std::endl;
                std::cerr << "[OpenGL context] The following context (proposed by the graphics driver) will be created:"  << std::endl;
                std::cerr << "[OpenGL context] Profile: " << profileToString(resultFormat.profile()) << ", Version: "
                          << resultFormat.version().first << "." << resultFormat.version().second << std::endl;
                std::cerr << "[OpenGL context] Please consider setting a supported OpenGL version and profile in the Options dialog." << std::endl;
            }
        }
    }

    return resultFormat;
}

}