Search code examples
c++openglcompatibilityprofile

Detect the OpenGL context profile before version 3.2


I'm trying to detect a profile of a default OpenGL context created using glfw. This is the way I've implemented it (based on the wiki page)

std::string openGLProfile(){
   // Prints the profile of the current context

   GLint profile = 0;
   glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile);

   bool core_profile = profile & GL_CONTEXT_CORE_PROFILE_BIT;
   bool compatibility_profile = profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT;

   std::string str = "No profile";

   if (core_profile == true)
       str = "Core profile";

   else if (compatibility_profile == true)
       str = "Compatibility";

   std::cout << str;
}

The problem is that this enumerator GL_CONTEXT_PROFILE_MASK is apparently not available in some OpenGL versions, like 2.1 and this causes an error.

My question is - how to do the profile check for OpenGL versions that do not have the GL_CONTEXT_PROFILE_MASK? Also, if somebody could point me to the documentation where this profile mask was first introduced, I'd find it very useful. Apparently, the doc for OpenGL 4.xx about glGetIntegerv does not even mention the profile mask, which confuses me.


Solution

  • how to do the profile check for OpenGL versions that do not have the GL_CONTEXT_PROFILE_MASK?

    To be precise, OpenGL didn't have notion of profiles until version 3.2, because the most versions before (up to 2.1) were backward compatible. That became a problem when OpenGL 3.0 introduced deprecated functionality and OpenGL 3.1 just removed some functionality from specification. Different vendors, however, kept supporting the backward compatibility via extensions and you had different versions of OpenGL specification existing at the same time. In order to differentiate between them the profiles were introduced, but only in version 3.2.

    The most correct answer to your question, is that profiles didn't exist before OpenGL 3.2 and technically there is no profile. However if we boil down this question to how to express profiles dichotomy in older OpenGL versions, then the answer would be as follows:

    • OpenGL up to 2.1 - supports compatibility profile only (all contexts were backwards compatible)
    • OpenGL 3.0 - despite deprecating many legacy features, they still worked for 3.0, so predominantly 3.0 is compatibility profile, with one exception - if it has GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, it means 3.0 simulates 3.1 experience, where the backward compatibility is removed and thus it's core profile.
    • OpenGL 3.1 - as the documentation says for this version if GL_ARB_compatibility is present among extensions string, then all deprecated functions are supported, so it can be treated as compatibility (and core otherwise)
    • OpenGL 3.2 and newer - gives this information with use of GL_CONTEXT_PROFILE_MASK

    Actual code would look like this (sorry for the verbose OpenGL version utility functions, but they come handy here)

    constexpr GLint GLV(GLint major, GLint minor) {
        constexpr auto majorShift = std::numeric_limits<GLint>::digits / 2 - 1;
        return (major << majorShift) | minor;
    }
    
    GLint glVersion() {
        constexpr auto invalid_version = -1;
        const auto glVersionString{ reinterpret_cast<const char*>(glGetString(GL_VERSION)) };
        
        GLint major;
        const auto majorResult = std::from_chars(glVersionString, glVersionString + 1, major).ec;
        if (majorResult == std::errc::invalid_argument) {
            return invalid_version;
        }
        
        GLint minor;
        const auto minorResult = std::from_chars(glVersionString + 2, glVersionString + 3, minor).ec;
        if (minorResult == std::errc::invalid_argument) {
            return invalid_version;
        }
        
        return GLV(major, minor);
    }
    
    void reportGLProfile() {  
        const auto glV = glVersion();
        if (glV <= GLV(2, 1)) {
            std::cout << "Compatibility profile" << std::endl;
        } else if (glV == GLV(3, 0)) {
            GLint flags;
            glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
            if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) {
                std::cout << "Core profile" << std::endl;
            } else {
                std::cout << "Compatibility profile" << std::endl;
            }
        } else if (glV == GLV(3, 1)) {
            GLint extensionsNum;
            glGetIntegerv(GL_NUM_EXTENSIONS, &extensionsNum);
            while (extensionsNum--) {
                const auto extensionName = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, extensionsNum));
                if (std::strcmp(extensionName, "GL_ARB_compatibility") == 0) {
                    std::cout << "Compatibility profile" << std::endl;
                    return;
                }
            }
            std::cout << "Core profile" << std::endl;
        } else {
            GLint profile;
            glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile);
            
            if (const auto errorCode = glGetError()) {
                std::cout << "Pending GL error while obtaining profile: " << errorCode << std::endl;
                return;
            }
            
            if (profile & GL_CONTEXT_CORE_PROFILE_BIT) {
                std::cout << "Core profile" << std::endl;
            } else {
                std::cout << "Compatibility profile" << std::endl;
            }
        }
    }
    

    if somebody could point me to the documentation where this profile mask was first introduced, I'd find it very useful

    That is covered by Context Types documentation:

    The 3.0 form of context creation did not have the concept of profiles. There was only one form of OpenGL: the core specification. In 3.2, OpenGL was effectively split into two profiles, core and compatibility. An implementation was only required to define core, so compatibility is not guaranteed to be available