// // Copyright 2015 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // FunctionsGL.cpp: Implements the FuntionsGL class to contain loaded GL functions #include "libANGLE/renderer/gl/FunctionsGL.h" #include #include "common/string_utils.h" #include "libANGLE/AttributeMap.h" #include "libANGLE/renderer/gl/renderergl_utils.h" namespace rx { static void GetGLVersion(PFNGLGETSTRINGPROC getStringFunction, gl::Version *outVersion, StandardGL *outStandard) { const std::string version = reinterpret_cast(getStringFunction(GL_VERSION)); if (version.find("OpenGL ES") == std::string::npos) { // OpenGL spec states the GL_VERSION string will be in the following format: // // The version number is either of the form major number.minor number or major // number.minor number.release number, where the numbers all have one or more // digits *outStandard = STANDARD_GL_DESKTOP; *outVersion = gl::Version(version[0] - '0', version[2] - '0'); } else { // ES spec states that the GL_VERSION string will be in the following format: // "OpenGL ES N.M vendor-specific information" *outStandard = STANDARD_GL_ES; *outVersion = gl::Version(version[10] - '0', version[12] - '0'); } } static std::vector GetIndexedExtensions(PFNGLGETINTEGERVPROC getIntegerFunction, PFNGLGETSTRINGIPROC getStringIFunction) { std::vector result; GLint numExtensions; getIntegerFunction(GL_NUM_EXTENSIONS, &numExtensions); result.reserve(numExtensions); for (GLint i = 0; i < numExtensions; i++) { result.push_back(reinterpret_cast(getStringIFunction(GL_EXTENSIONS, i))); } return result; } #if defined(ANGLE_ENABLE_OPENGL_NULL) static GLenum INTERNAL_GL_APIENTRY StubCheckFramebufferStatus(GLenum) { return GL_FRAMEBUFFER_COMPLETE; } static void INTERNAL_GL_APIENTRY StubGetProgramiv(GLuint program, GLenum pname, GLint *params) { switch (pname) { case GL_LINK_STATUS: *params = GL_TRUE; break; case GL_VALIDATE_STATUS: *params = GL_TRUE; break; default: break; } } static void INTERNAL_GL_APIENTRY StubGetShaderiv(GLuint program, GLenum pname, GLint *params) { switch (pname) { case GL_COMPILE_STATUS: *params = GL_TRUE; break; default: break; } } #endif // defined(ANGLE_ENABLE_OPENGL_NULL) #define ASSIGN(NAME, FP) FP = reinterpret_cast(loadProcAddress(NAME)) FunctionsGL::FunctionsGL() : version(), standard(), extensions() {} FunctionsGL::~FunctionsGL() {} void FunctionsGL::initialize(const egl::AttributeMap &displayAttributes) { // Grab the version number ASSIGN("glGetString", getString); ASSIGN("glGetIntegerv", getIntegerv); GetGLVersion(getString, &version, &standard); // Grab the GL extensions if (isAtLeastGL(gl::Version(3, 0)) || isAtLeastGLES(gl::Version(3, 0))) { ASSIGN("glGetStringi", getStringi); extensions = GetIndexedExtensions(getIntegerv, getStringi); } else { const char *exts = reinterpret_cast(getString(GL_EXTENSIONS)); angle::SplitStringAlongWhitespace(std::string(exts), &extensions); } std::set extensionSet; for (const auto &extension : extensions) { extensionSet.insert(extension); } // Note: // Even though extensions are written against specific versions of GL, many drivers expose the // extensions in even older versions. Always try loading the extensions regardless of GL // version. // Load the entry points #if defined(ANGLE_ENABLE_OPENGL_NULL) EGLint deviceType = static_cast(displayAttributes.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_NONE)); #endif // defined(ANGLE_ENABLE_GL_NULL) switch (standard) { case STANDARD_GL_DESKTOP: { // Check the context profile profile = 0; if (isAtLeastGL(gl::Version(3, 2))) { getIntegerv(GL_CONTEXT_PROFILE_MASK, &profile); } #if defined(ANGLE_ENABLE_OPENGL_NULL) if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE) { initProcsDesktopGLNULL(version, extensionSet); } else #endif // defined(ANGLE_ENABLE_GL_NULL) { initProcsDesktopGL(version, extensionSet); } break; } case STANDARD_GL_ES: { // No profiles in GLES profile = 0; #if defined(ANGLE_ENABLE_OPENGL_NULL) if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE) { initProcsGLESNULL(version, extensionSet); } else #endif // defined(ANGLE_ENABLE_GL_NULL) { initProcsGLES(version, extensionSet); } break; } default: UNREACHABLE(); break; } #if defined(ANGLE_ENABLE_OPENGL_NULL) if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE) { initProcsSharedExtensionsNULL(extensionSet); initializeStubFunctionsForNULLDriver(extensionSet); } else #endif // defined(ANGLE_ENABLE_OPENGL_NULL) { initProcsSharedExtensions(extensionSet); } } bool FunctionsGL::isAtLeastGL(const gl::Version &glVersion) const { return standard == STANDARD_GL_DESKTOP && version >= glVersion; } bool FunctionsGL::isAtMostGL(const gl::Version &glVersion) const { return standard == STANDARD_GL_DESKTOP && glVersion >= version; } bool FunctionsGL::isAtLeastGLES(const gl::Version &glesVersion) const { return standard == STANDARD_GL_ES && version >= glesVersion; } bool FunctionsGL::isAtMostGLES(const gl::Version &glesVersion) const { return standard == STANDARD_GL_ES && glesVersion >= version; } bool FunctionsGL::hasExtension(const std::string &ext) const { return std::find(extensions.begin(), extensions.end(), ext) != extensions.end(); } bool FunctionsGL::hasGLExtension(const std::string &ext) const { return standard == STANDARD_GL_DESKTOP && hasExtension(ext); } bool FunctionsGL::hasGLESExtension(const std::string &ext) const { return standard == STANDARD_GL_ES && hasExtension(ext); } #if defined(ANGLE_ENABLE_OPENGL_NULL) void FunctionsGL::initializeStubFunctionsForNULLDriver(const std::set &extensionSet) { // This is a quick hack to get the NULL driver working, but we might want to implement a true // NULL/stub driver that never calls into the OS. See Chromium's implementation in // ui/gl/gl_stub_api.cc. This might be useful for testing things like perf scaling due to // the caps returned by the drivers (i.e. number of texture units) or a true NULL back-end // that could be used in a VM for things like fuzzing. // TODO(jmadill): Implement true no-op/stub back-end. ASSIGN("glGetString", getString); ASSIGN("glGetStringi", getStringi); ASSIGN("glGetIntegerv", getIntegerv); ASSIGN("glGetIntegeri_v", getIntegeri_v); getProgramiv = &StubGetProgramiv; getShaderiv = &StubGetShaderiv; checkFramebufferStatus = &StubCheckFramebufferStatus; if (isAtLeastGLES(gl::Version(3, 0)) || isAtLeastGL(gl::Version(4, 2)) || extensionSet.count("GL_ARB_internalformat_query") > 0) { ASSIGN("glGetInternalformativ", getInternalformativ); } if (isAtLeastGL(gl::Version(4, 3))) { ASSIGN("glGetInternalformati64v", getInternalformati64v); } if (extensionSet.count("GL_NV_internalformat_sample_query") > 0) { ASSIGN("glGetInternalformatSampleivNV", getInternalformatSampleivNV); } } #endif // defined(ANGLE_ENABLE_OPENGL_NULL) } // namespace rx