blob: 4b6f4407ade1314fd60118805d839e0906dab67f [file] [log] [blame]
//
// 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 <algorithm>
#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<const char *>(getStringFunction(GL_VERSION));
if (version.find("OpenGL ES") == std::string::npos)
{
// OpenGL spec states the GL_VERSION string will be in the following format:
// <version number><space><vendor-specific information>
// 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<std::string> GetIndexedExtensions(PFNGLGETINTEGERVPROC getIntegerFunction,
PFNGLGETSTRINGIPROC getStringIFunction)
{
std::vector<std::string> result;
GLint numExtensions;
getIntegerFunction(GL_NUM_EXTENSIONS, &numExtensions);
result.reserve(numExtensions);
for (GLint i = 0; i < numExtensions; i++)
{
result.push_back(reinterpret_cast<const char *>(getStringIFunction(GL_EXTENSIONS, i)));
}
return result;
}
#if defined(ANGLE_ENABLE_OPENGL_NULL)
static GLenum INTERNAL_GL_APIENTRY DummyCheckFramebufferStatus(GLenum)
{
return GL_FRAMEBUFFER_COMPLETE;
}
static void INTERNAL_GL_APIENTRY DummyGetProgramiv(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 DummyGetShaderiv(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<decltype(FP)>(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<const char *>(getString(GL_EXTENSIONS));
angle::SplitStringAlongWhitespace(std::string(exts), &extensions);
}
std::set<std::string> 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<EGLint>(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);
initializeDummyFunctionsForNULLDriver(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::initializeDummyFunctionsForNULLDriver(const std::set<std::string> &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 = &DummyGetProgramiv;
getShaderiv = &DummyGetShaderiv;
checkFramebufferStatus = &DummyCheckFramebufferStatus;
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