blob: 53e31b14964c604b6a4deb84ad49a1f0f456777e [file] [log] [blame]
//
// Copyright 2012 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.
//
// renderergl_utils.cpp: Conversion functions and other utility routines
// specific to the OpenGL renderer.
#include "libANGLE/renderer/gl/renderergl_utils.h"
#include <array>
#include <limits>
#include "common/mathutil.h"
#include "common/platform.h"
#include "common/string_utils.h"
#include "gpu_info_util/SystemInfo.h"
#include "libANGLE/Buffer.h"
#include "libANGLE/Caps.h"
#include "libANGLE/Context.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/queryconversions.h"
#include "libANGLE/renderer/gl/ContextGL.h"
#include "libANGLE/renderer/gl/FenceNVGL.h"
#include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/QueryGL.h"
#include "libANGLE/renderer/gl/formatutilsgl.h"
#include "platform/FeaturesGL_autogen.h"
#include "platform/FrontendFeatures_autogen.h"
#include <EGL/eglext.h>
#include <algorithm>
#include <sstream>
using angle::CheckedNumeric;
namespace rx
{
namespace
{
const char *GetString(const FunctionsGL *functions, GLenum name)
{
return reinterpret_cast<const char *>(functions->getString(name));
}
bool IsMesa(const FunctionsGL *functions, std::array<int, 3> *version)
{
ASSERT(version);
if (functions->standard != STANDARD_GL_DESKTOP)
{
return false;
}
std::string nativeVersionString(GetString(functions, GL_VERSION));
size_t pos = nativeVersionString.find("Mesa");
if (pos == std::string::npos)
{
return false;
}
int *data = version->data();
data[0] = data[1] = data[2] = 0;
std::sscanf(nativeVersionString.c_str() + pos, "Mesa %d.%d.%d", data, data + 1, data + 2);
return true;
}
int getAdrenoNumber(const FunctionsGL *functions)
{
static int number = -1;
if (number == -1)
{
const char *nativeGLRenderer = GetString(functions, GL_RENDERER);
if (std::sscanf(nativeGLRenderer, "Adreno (TM) %d", &number) < 1 &&
std::sscanf(nativeGLRenderer, "FD%d", &number) < 1)
{
number = 0;
}
}
return number;
}
int getMaliTNumber(const FunctionsGL *functions)
{
static int number = -1;
if (number == -1)
{
const char *nativeGLRenderer = GetString(functions, GL_RENDERER);
if (std::sscanf(nativeGLRenderer, "Mali-T%d", &number) < 1)
{
number = 0;
}
}
return number;
}
int getMaliGNumber(const FunctionsGL *functions)
{
static int number = -1;
if (number == -1)
{
const char *nativeGLRenderer = GetString(functions, GL_RENDERER);
if (std::sscanf(nativeGLRenderer, "Mali-G%d", &number) < 1)
{
number = 0;
}
}
return number;
}
bool IsAdreno42xOr3xx(const FunctionsGL *functions)
{
int number = getAdrenoNumber(functions);
return number != 0 && getAdrenoNumber(functions) < 430;
}
bool IsAdreno4xx(const FunctionsGL *functions)
{
int number = getAdrenoNumber(functions);
return number != 0 && number >= 400 && number < 500;
}
bool IsAdreno5xxOrOlder(const FunctionsGL *functions)
{
int number = getAdrenoNumber(functions);
return number != 0 && number < 600;
}
bool IsAdreno5xx(const FunctionsGL *functions)
{
int number = getAdrenoNumber(functions);
return number != 0 && number >= 500 && number < 600;
}
bool IsMaliT8xxOrOlder(const FunctionsGL *functions)
{
int number = getMaliTNumber(functions);
return number != 0 && number < 900;
}
bool IsMaliG31OrOlder(const FunctionsGL *functions)
{
int number = getMaliGNumber(functions);
return number != 0 && number <= 31;
}
int GetAndroidSdkLevel()
{
if (!IsAndroid())
{
return 0;
}
angle::SystemInfo info;
if (!angle::GetSystemInfo(&info))
{
return 0;
}
return info.androidSdkLevel;
}
bool IsAndroidEmulator(const FunctionsGL *functions)
{
constexpr char androidEmulator[] = "Android Emulator";
const char *nativeGLRenderer = GetString(functions, GL_RENDERER);
return angle::BeginsWith(nativeGLRenderer, androidEmulator);
}
bool IsPowerVrRogue(const FunctionsGL *functions)
{
constexpr char powerVRRogue[] = "PowerVR Rogue";
const char *nativeGLRenderer = GetString(functions, GL_RENDERER);
return angle::BeginsWith(nativeGLRenderer, powerVRRogue);
}
void ClearErrors(const FunctionsGL *functions,
const char *file,
const char *function,
unsigned int line)
{
GLenum error = functions->getError();
while (error != GL_NO_ERROR)
{
INFO() << "Preexisting GL error " << gl::FmtHex(error) << " as of " << file << ", "
<< function << ":" << line << ". ";
error = functions->getError();
}
}
#define ANGLE_GL_CLEAR_ERRORS() ClearErrors(functions, __FILE__, __FUNCTION__, __LINE__)
} // namespace
SwapControlData::SwapControlData()
: targetSwapInterval(0), maxSwapInterval(-1), currentSwapInterval(-1)
{}
VendorID GetVendorID(const FunctionsGL *functions)
{
std::string nativeVendorString(GetString(functions, GL_VENDOR));
// Concatenate GL_RENDERER to the string being checked because some vendors put their names in
// GL_RENDERER
nativeVendorString += " ";
nativeVendorString += GetString(functions, GL_RENDERER);
if (nativeVendorString.find("NVIDIA") != std::string::npos)
{
return VENDOR_ID_NVIDIA;
}
else if (nativeVendorString.find("ATI") != std::string::npos ||
nativeVendorString.find("AMD") != std::string::npos ||
nativeVendorString.find("Radeon") != std::string::npos)
{
return VENDOR_ID_AMD;
}
else if (nativeVendorString.find("Qualcomm") != std::string::npos)
{
return VENDOR_ID_QUALCOMM;
}
else if (nativeVendorString.find("Intel") != std::string::npos)
{
return VENDOR_ID_INTEL;
}
else if (nativeVendorString.find("Imagination") != std::string::npos)
{
return VENDOR_ID_POWERVR;
}
else if (nativeVendorString.find("Vivante") != std::string::npos)
{
return VENDOR_ID_VIVANTE;
}
else
{
return VENDOR_ID_UNKNOWN;
}
}
uint32_t GetDeviceID(const FunctionsGL *functions)
{
std::string nativeRendererString(GetString(functions, GL_RENDERER));
constexpr std::pair<const char *, uint32_t> kKnownDeviceIDs[] = {
{"Adreno (TM) 418", ANDROID_DEVICE_ID_NEXUS5X},
{"Adreno (TM) 530", ANDROID_DEVICE_ID_PIXEL1XL},
{"Adreno (TM) 540", ANDROID_DEVICE_ID_PIXEL2},
};
for (const auto &knownDeviceID : kKnownDeviceIDs)
{
if (nativeRendererString.find(knownDeviceID.first) != std::string::npos)
{
return knownDeviceID.second;
}
}
return 0;
}
namespace nativegl_gl
{
static bool MeetsRequirements(const FunctionsGL *functions,
const nativegl::SupportRequirement &requirements)
{
bool hasRequiredExtensions = false;
for (const std::vector<std::string> &exts : requirements.requiredExtensions)
{
bool hasAllExtensionsInSet = true;
for (const std::string &extension : exts)
{
if (!functions->hasExtension(extension))
{
hasAllExtensionsInSet = false;
break;
}
}
if (hasAllExtensionsInSet)
{
hasRequiredExtensions = true;
break;
}
}
if (!requirements.requiredExtensions.empty() && !hasRequiredExtensions)
{
return false;
}
if (functions->version >= requirements.version)
{
return true;
}
else if (!requirements.versionExtensions.empty())
{
for (const std::string &extension : requirements.versionExtensions)
{
if (!functions->hasExtension(extension))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
static bool CheckSizedInternalFormatTextureRenderability(const FunctionsGL *functions,
const angle::FeaturesGL &features,
GLenum internalFormat)
{
const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
ASSERT(formatInfo.sized);
// Query the current texture so it can be rebound afterwards
GLint oldTextureBinding = 0;
functions->getIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureBinding);
// Create a small texture with the same format and type that gl::Texture would use
GLuint texture = 0;
functions->genTextures(1, &texture);
functions->bindTexture(GL_TEXTURE_2D, texture);
// Nearest filter needed for framebuffer completeness on some drivers.
functions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
functions, features, formatInfo.internalFormat, formatInfo.format, formatInfo.type);
constexpr GLsizei kTextureSize = 16;
functions->texImage2D(GL_TEXTURE_2D, 0, texImageFormat.internalFormat, kTextureSize,
kTextureSize, 0, texImageFormat.format, texImageFormat.type, nullptr);
// Query the current framebuffer so it can be rebound afterwards
GLint oldFramebufferBinding = 0;
functions->getIntegerv(GL_FRAMEBUFFER_BINDING, &oldFramebufferBinding);
// Bind the texture to the framebuffer and check renderability
GLuint fbo = 0;
functions->genFramebuffers(1, &fbo);
functions->bindFramebuffer(GL_FRAMEBUFFER, fbo);
functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
0);
bool supported = functions->checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
// Delete the framebuffer and restore the previous binding
functions->deleteFramebuffers(1, &fbo);
functions->bindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(oldFramebufferBinding));
// Delete the texture and restore the previous binding
functions->deleteTextures(1, &texture);
functions->bindTexture(GL_TEXTURE_2D, static_cast<GLuint>(oldTextureBinding));
if (!supported)
{
ANGLE_GL_CLEAR_ERRORS();
}
ASSERT(functions->getError() == GL_NO_ERROR);
return supported;
}
static bool CheckInternalFormatRenderbufferRenderability(const FunctionsGL *functions,
const angle::FeaturesGL &features,
GLenum internalFormat)
{
const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
ASSERT(formatInfo.sized);
// Query the current renderbuffer so it can be rebound afterwards
GLint oldRenderbufferBinding = 0;
functions->getIntegerv(GL_RENDERBUFFER_BINDING, &oldRenderbufferBinding);
// Create a small renderbuffer with the same format and type that gl::Renderbuffer would use
GLuint renderbuffer = 0;
functions->genRenderbuffers(1, &renderbuffer);
functions->bindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
nativegl::RenderbufferFormat renderbufferFormat =
nativegl::GetRenderbufferFormat(functions, features, formatInfo.internalFormat);
constexpr GLsizei kRenderbufferSize = 16;
functions->renderbufferStorage(GL_RENDERBUFFER, renderbufferFormat.internalFormat,
kRenderbufferSize, kRenderbufferSize);
// Query the current framebuffer so it can be rebound afterwards
GLint oldFramebufferBinding = 0;
functions->getIntegerv(GL_FRAMEBUFFER_BINDING, &oldFramebufferBinding);
// Bind the texture to the framebuffer and check renderability
GLuint fbo = 0;
functions->genFramebuffers(1, &fbo);
functions->bindFramebuffer(GL_FRAMEBUFFER, fbo);
functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
renderbuffer);
bool supported = functions->checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
// Delete the framebuffer and restore the previous binding
functions->deleteFramebuffers(1, &fbo);
functions->bindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(oldFramebufferBinding));
// Delete the renderbuffer and restore the previous binding
functions->deleteRenderbuffers(1, &renderbuffer);
functions->bindRenderbuffer(GL_RENDERBUFFER, static_cast<GLuint>(oldRenderbufferBinding));
if (!supported)
{
ANGLE_GL_CLEAR_ERRORS();
}
ASSERT(functions->getError() == GL_NO_ERROR);
return supported;
}
static void LimitVersion(gl::Version *curVersion, const gl::Version &maxVersion)
{
if (*curVersion >= maxVersion)
{
*curVersion = maxVersion;
}
}
static gl::TextureCaps GenerateTextureFormatCaps(const FunctionsGL *functions,
const angle::FeaturesGL &features,
GLenum internalFormat,
gl::Version *maxSupportedESVersion)
{
ASSERT(functions->getError() == GL_NO_ERROR);
gl::TextureCaps textureCaps;
const nativegl::InternalFormat &formatInfo =
nativegl::GetInternalFormatInfo(internalFormat, functions->standard);
textureCaps.texturable = MeetsRequirements(functions, formatInfo.texture);
textureCaps.filterable =
textureCaps.texturable && MeetsRequirements(functions, formatInfo.filter);
textureCaps.textureAttachment = MeetsRequirements(functions, formatInfo.textureAttachment);
textureCaps.renderbuffer = MeetsRequirements(functions, formatInfo.renderbuffer);
textureCaps.blendable = textureCaps.renderbuffer || textureCaps.textureAttachment;
// Do extra renderability validation for some formats.
if (internalFormat == GL_R16F || internalFormat == GL_RG16F || internalFormat == GL_RGB16F)
{
// SupportRequirement can't currently express a condition of the form (version && extension)
// || other extensions, so do the (version && extension) part here.
if (functions->isAtLeastGLES(gl::Version(3, 0)) &&
functions->hasGLESExtension("GL_EXT_color_buffer_half_float"))
{
textureCaps.textureAttachment = true;
textureCaps.renderbuffer = true;
}
}
// We require GL_RGBA16F is renderable to expose EXT_color_buffer_half_float but we can't know
// if the format is supported unless we try to create a framebuffer.
if (internalFormat == GL_RGBA16F)
{
if (textureCaps.textureAttachment)
{
textureCaps.textureAttachment =
CheckSizedInternalFormatTextureRenderability(functions, features, internalFormat);
}
if (textureCaps.renderbuffer)
{
textureCaps.renderbuffer =
CheckInternalFormatRenderbufferRenderability(functions, features, internalFormat);
}
}
// glGetInternalformativ is not available until version 4.2 but may be available through the 3.0
// extension GL_ARB_internalformat_query
if (textureCaps.renderbuffer && functions->getInternalformativ)
{
GLenum queryInternalFormat = internalFormat;
if (internalFormat == GL_BGRA8_EXT)
{
// Querying GL_NUM_SAMPLE_COUNTS for GL_BGRA8_EXT generates an INVALID_ENUM on some
// drivers. It seems however that allocating a multisampled renderbuffer of this format
// succeeds. To avoid breaking multisampling for this format, query the supported sample
// counts for GL_RGBA8 instead.
queryInternalFormat = GL_RGBA8;
}
ANGLE_GL_CLEAR_ERRORS();
GLint numSamples = 0;
functions->getInternalformativ(GL_RENDERBUFFER, queryInternalFormat, GL_NUM_SAMPLE_COUNTS,
1, &numSamples);
GLenum error = functions->getError();
if (error != GL_NO_ERROR)
{
ERR() << "glGetInternalformativ generated error " << gl::FmtHex(error) << " for format "
<< gl::FmtHex(queryInternalFormat) << ". Skipping multisample checks.";
numSamples = 0;
}
if (numSamples > 0)
{
std::vector<GLint> samples(numSamples);
functions->getInternalformativ(GL_RENDERBUFFER, queryInternalFormat, GL_SAMPLES,
static_cast<GLsizei>(samples.size()), &samples[0]);
for (size_t sampleIndex = 0; sampleIndex < samples.size(); sampleIndex++)
{
if (features.limitMaxMSAASamplesTo4.enabled && samples[sampleIndex] > 4)
{
continue;
}
// Some NVIDIA drivers expose multisampling modes implemented as a combination of
// multisampling and supersampling. These are non-conformant and should not be
// exposed through ANGLE. Query which formats are conformant from the driver if
// supported.
GLint conformant = GL_TRUE;
if (functions->getInternalformatSampleivNV)
{
ASSERT(functions->getError() == GL_NO_ERROR);
functions->getInternalformatSampleivNV(GL_RENDERBUFFER, queryInternalFormat,
samples[sampleIndex], GL_CONFORMANT_NV,
1, &conformant);
// getInternalFormatSampleivNV does not work for all formats on NVIDIA Shield TV
// drivers. Assume that formats with large sample counts are non-conformant in
// case the query generates an error.
if (functions->getError() != GL_NO_ERROR)
{
conformant = (samples[sampleIndex] <= 8) ? GL_TRUE : GL_FALSE;
}
}
if (conformant == GL_TRUE)
{
textureCaps.sampleCounts.insert(samples[sampleIndex]);
}
}
}
}
// GLES 3.0.5 section 4.4.2.2: "Implementations must support creation of renderbuffers in these
// required formats with up to the value of MAX_SAMPLES multisamples, with the exception of
// signed and unsigned integer formats."
const gl::InternalFormat &glFormatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
if (textureCaps.renderbuffer && !glFormatInfo.isInt() &&
glFormatInfo.isRequiredRenderbufferFormat(gl::Version(3, 0)) &&
textureCaps.getMaxSamples() < 4)
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
ASSERT(functions->getError() == GL_NO_ERROR);
return textureCaps;
}
static GLint QuerySingleGLInt(const FunctionsGL *functions, GLenum name)
{
GLint result = 0;
functions->getIntegerv(name, &result);
return result;
}
static GLint QuerySingleIndexGLInt(const FunctionsGL *functions, GLenum name, GLuint index)
{
GLint result;
functions->getIntegeri_v(name, index, &result);
return result;
}
static GLint QueryGLIntRange(const FunctionsGL *functions, GLenum name, size_t index)
{
GLint result[2] = {};
functions->getIntegerv(name, result);
return result[index];
}
static GLint64 QuerySingleGLInt64(const FunctionsGL *functions, GLenum name)
{
// Fall back to 32-bit int if 64-bit query is not available. This can become relevant for some
// caps that are defined as 64-bit values in core spec, but were introduced earlier in
// extensions as 32-bit. Triggered in some cases by RenderDoc's emulated OpenGL driver.
if (!functions->getInteger64v)
{
GLint result = 0;
functions->getIntegerv(name, &result);
return static_cast<GLint64>(result);
}
else
{
GLint64 result = 0;
functions->getInteger64v(name, &result);
return result;
}
}
static GLfloat QuerySingleGLFloat(const FunctionsGL *functions, GLenum name)
{
GLfloat result = 0.0f;
functions->getFloatv(name, &result);
return result;
}
static GLfloat QueryGLFloatRange(const FunctionsGL *functions, GLenum name, size_t index)
{
GLfloat result[2] = {};
functions->getFloatv(name, result);
return result[index];
}
static gl::TypePrecision QueryTypePrecision(const FunctionsGL *functions,
GLenum shaderType,
GLenum precisionType)
{
gl::TypePrecision precision;
functions->getShaderPrecisionFormat(shaderType, precisionType, precision.range.data(),
&precision.precision);
return precision;
}
static GLint QueryQueryValue(const FunctionsGL *functions, GLenum target, GLenum name)
{
GLint result;
functions->getQueryiv(target, name, &result);
return result;
}
void CapCombinedLimitToESShaders(GLint *combinedLimit, gl::ShaderMap<GLint> &perShaderLimit)
{
GLint combinedESLimit = 0;
for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
{
combinedESLimit += perShaderLimit[shaderType];
}
*combinedLimit = std::min(*combinedLimit, combinedESLimit);
}
void GenerateCaps(const FunctionsGL *functions,
const angle::FeaturesGL &features,
gl::Caps *caps,
gl::TextureCapsMap *textureCapsMap,
gl::Extensions *extensions,
gl::Limitations *limitations,
gl::Version *maxSupportedESVersion,
MultiviewImplementationTypeGL *multiviewImplementationType)
{
// Start by assuming ES3.1 support and work down
*maxSupportedESVersion = gl::Version(3, 1);
// Texture format support checks
const gl::FormatSet &allFormats = gl::GetAllSizedInternalFormats();
for (GLenum internalFormat : allFormats)
{
gl::TextureCaps textureCaps =
GenerateTextureFormatCaps(functions, features, internalFormat, maxSupportedESVersion);
textureCapsMap->insert(internalFormat, textureCaps);
}
// Table 6.28, implementation dependent values
if (functions->isAtLeastGL(gl::Version(4, 3)) ||
functions->hasGLExtension("GL_ARB_ES3_compatibility") ||
functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxElementIndex = QuerySingleGLInt64(functions, GL_MAX_ELEMENT_INDEX);
// Work around the null driver limitations.
if (caps->maxElementIndex == 0)
{
caps->maxElementIndex = 0xFFFF;
}
}
else
{
// Doesn't affect ES3 support, can use a pre-defined limit
caps->maxElementIndex = static_cast<GLint64>(std::numeric_limits<unsigned int>::max());
}
GLint textureSizeLimit = std::numeric_limits<GLint>::max();
if (features.limitMaxTextureSizeTo4096.enabled)
{
textureSizeLimit = 4096;
}
GLint max3dArrayTextureSizeLimit = std::numeric_limits<GLint>::max();
if (features.limitMax3dArrayTextureSizeTo1024.enabled)
{
max3dArrayTextureSizeLimit = 1024;
}
if (functions->isAtLeastGL(gl::Version(1, 2)) || functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_texture_3D"))
{
caps->max3DTextureSize = std::min({QuerySingleGLInt(functions, GL_MAX_3D_TEXTURE_SIZE),
textureSizeLimit, max3dArrayTextureSizeLimit});
}
else
{
// Can't support ES3 without 3D textures
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
caps->max2DTextureSize = std::min(QuerySingleGLInt(functions, GL_MAX_TEXTURE_SIZE),
textureSizeLimit); // GL 1.0 / ES 2.0
caps->maxCubeMapTextureSize =
std::min(QuerySingleGLInt(functions, GL_MAX_CUBE_MAP_TEXTURE_SIZE),
textureSizeLimit); // GL 1.3 / ES 2.0
if (functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_EXT_texture_array") ||
functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxArrayTextureLayers =
std::min({QuerySingleGLInt(functions, GL_MAX_ARRAY_TEXTURE_LAYERS), textureSizeLimit,
max3dArrayTextureSizeLimit});
}
else
{
// Can't support ES3 without array textures
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
if (functions->isAtLeastGL(gl::Version(1, 5)) ||
functions->hasGLExtension("GL_EXT_texture_lod_bias") ||
functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxLODBias = QuerySingleGLFloat(functions, GL_MAX_TEXTURE_LOD_BIAS);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
if (functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_EXT_framebuffer_object") ||
functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxRenderbufferSize = QuerySingleGLInt(functions, GL_MAX_RENDERBUFFER_SIZE);
caps->maxColorAttachments = QuerySingleGLInt(functions, GL_MAX_COLOR_ATTACHMENTS);
}
else if (functions->isAtLeastGLES(gl::Version(2, 0)))
{
caps->maxRenderbufferSize = QuerySingleGLInt(functions, GL_MAX_RENDERBUFFER_SIZE);
caps->maxColorAttachments = 1;
}
else
{
// Can't support ES2 without framebuffers and renderbuffers
LimitVersion(maxSupportedESVersion, gl::Version(0, 0));
}
if (functions->isAtLeastGL(gl::Version(2, 0)) ||
functions->hasGLExtension("ARB_draw_buffers") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_draw_buffers"))
{
caps->maxDrawBuffers = QuerySingleGLInt(functions, GL_MAX_DRAW_BUFFERS);
}
else
{
// Framebuffer is required to have at least one drawbuffer even if the extension is not
// supported
caps->maxDrawBuffers = 1;
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
caps->maxViewportWidth =
QueryGLIntRange(functions, GL_MAX_VIEWPORT_DIMS, 0); // GL 1.0 / ES 2.0
caps->maxViewportHeight =
QueryGLIntRange(functions, GL_MAX_VIEWPORT_DIMS, 1); // GL 1.0 / ES 2.0
if (functions->standard == STANDARD_GL_DESKTOP &&
(functions->profile & GL_CONTEXT_CORE_PROFILE_BIT) != 0)
{
// Desktop GL core profile deprecated the GL_ALIASED_POINT_SIZE_RANGE query. Use
// GL_POINT_SIZE_RANGE instead.
caps->minAliasedPointSize =
std::max(1.0f, QueryGLFloatRange(functions, GL_POINT_SIZE_RANGE, 0));
caps->maxAliasedPointSize = QueryGLFloatRange(functions, GL_POINT_SIZE_RANGE, 1);
}
else
{
caps->minAliasedPointSize =
std::max(1.0f, QueryGLFloatRange(functions, GL_ALIASED_POINT_SIZE_RANGE, 0));
caps->maxAliasedPointSize = QueryGLFloatRange(functions, GL_ALIASED_POINT_SIZE_RANGE, 1);
}
caps->minAliasedLineWidth =
QueryGLFloatRange(functions, GL_ALIASED_LINE_WIDTH_RANGE, 0); // GL 1.2 / ES 2.0
caps->maxAliasedLineWidth =
QueryGLFloatRange(functions, GL_ALIASED_LINE_WIDTH_RANGE, 1); // GL 1.2 / ES 2.0
// Table 6.29, implementation dependent values (cont.)
if (functions->isAtLeastGL(gl::Version(1, 2)) || functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxElementsIndices = QuerySingleGLInt(functions, GL_MAX_ELEMENTS_INDICES);
caps->maxElementsVertices = QuerySingleGLInt(functions, GL_MAX_ELEMENTS_VERTICES);
}
else
{
// Doesn't impact supported version
}
if (functions->isAtLeastGL(gl::Version(4, 1)) ||
functions->hasGLExtension("GL_ARB_get_program_binary") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_get_program_binary"))
{
// Able to support the GL_PROGRAM_BINARY_ANGLE format as long as another program binary
// format is available.
GLint numBinaryFormats = QuerySingleGLInt(functions, GL_NUM_PROGRAM_BINARY_FORMATS_OES);
if (numBinaryFormats > 0)
{
caps->programBinaryFormats.push_back(GL_PROGRAM_BINARY_ANGLE);
}
}
else
{
// Doesn't impact supported version
}
// glGetShaderPrecisionFormat is not available until desktop GL version 4.1 or
// GL_ARB_ES2_compatibility exists
if (functions->isAtLeastGL(gl::Version(4, 1)) ||
functions->hasGLExtension("GL_ARB_ES2_compatibility") ||
functions->isAtLeastGLES(gl::Version(2, 0)))
{
caps->vertexHighpFloat = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_HIGH_FLOAT);
caps->vertexMediumpFloat = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_MEDIUM_FLOAT);
caps->vertexLowpFloat = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_LOW_FLOAT);
caps->fragmentHighpFloat = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_HIGH_FLOAT);
caps->fragmentMediumpFloat =
QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT);
caps->fragmentLowpFloat = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_LOW_FLOAT);
caps->vertexHighpInt = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_HIGH_INT);
caps->vertexMediumpInt = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_MEDIUM_INT);
caps->vertexLowpInt = QueryTypePrecision(functions, GL_VERTEX_SHADER, GL_LOW_INT);
caps->fragmentHighpInt = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_HIGH_INT);
caps->fragmentMediumpInt = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_MEDIUM_INT);
caps->fragmentLowpInt = QueryTypePrecision(functions, GL_FRAGMENT_SHADER, GL_LOW_INT);
}
else
{
// Doesn't impact supported version, set some default values
caps->vertexHighpFloat.setIEEEFloat();
caps->vertexMediumpFloat.setIEEEFloat();
caps->vertexLowpFloat.setIEEEFloat();
caps->fragmentHighpFloat.setIEEEFloat();
caps->fragmentMediumpFloat.setIEEEFloat();
caps->fragmentLowpFloat.setIEEEFloat();
caps->vertexHighpInt.setTwosComplementInt(32);
caps->vertexMediumpInt.setTwosComplementInt(32);
caps->vertexLowpInt.setTwosComplementInt(32);
caps->fragmentHighpInt.setTwosComplementInt(32);
caps->fragmentMediumpInt.setTwosComplementInt(32);
caps->fragmentLowpInt.setTwosComplementInt(32);
}
if (functions->isAtLeastGL(gl::Version(3, 2)) || functions->hasGLExtension("GL_ARB_sync") ||
functions->isAtLeastGLES(gl::Version(3, 0)))
{
// Work around Linux NVIDIA driver bug where GL_TIMEOUT_IGNORED is returned.
caps->maxServerWaitTimeout =
std::max<GLint64>(QuerySingleGLInt64(functions, GL_MAX_SERVER_WAIT_TIMEOUT), 0);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
// Table 6.31, implementation dependent vertex shader limits
if (functions->isAtLeastGL(gl::Version(2, 0)) || functions->isAtLeastGLES(gl::Version(2, 0)))
{
caps->maxVertexAttributes = QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIBS);
caps->maxShaderUniformComponents[gl::ShaderType::Vertex] =
QuerySingleGLInt(functions, GL_MAX_VERTEX_UNIFORM_COMPONENTS);
caps->maxShaderTextureImageUnits[gl::ShaderType::Vertex] =
QuerySingleGLInt(functions, GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
}
else
{
// Can't support ES2 version without these caps
LimitVersion(maxSupportedESVersion, gl::Version(0, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 1)) ||
functions->hasGLExtension("GL_ARB_ES2_compatibility") ||
functions->isAtLeastGLES(gl::Version(2, 0)))
{
caps->maxVertexUniformVectors = QuerySingleGLInt(functions, GL_MAX_VERTEX_UNIFORM_VECTORS);
caps->maxFragmentUniformVectors =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_UNIFORM_VECTORS);
}
else
{
// Doesn't limit ES version, GL_MAX_VERTEX_UNIFORM_COMPONENTS / 4 is acceptable.
caps->maxVertexUniformVectors =
caps->maxShaderUniformComponents[gl::ShaderType::Vertex] / 4;
// Doesn't limit ES version, GL_MAX_FRAGMENT_UNIFORM_COMPONENTS / 4 is acceptable.
caps->maxFragmentUniformVectors =
caps->maxShaderUniformComponents[gl::ShaderType::Fragment] / 4;
}
if (functions->isAtLeastGL(gl::Version(3, 2)) || functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxVertexOutputComponents =
QuerySingleGLInt(functions, GL_MAX_VERTEX_OUTPUT_COMPONENTS);
}
else
{
// There doesn't seem, to be a desktop extension to add this cap, maybe it could be given a
// safe limit instead of limiting the supported ES version.
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
// Table 6.32, implementation dependent fragment shader limits
if (functions->isAtLeastGL(gl::Version(2, 0)) || functions->isAtLeastGLES(gl::Version(2, 0)))
{
caps->maxShaderUniformComponents[gl::ShaderType::Fragment] =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_UNIFORM_COMPONENTS);
caps->maxShaderTextureImageUnits[gl::ShaderType::Fragment] =
QuerySingleGLInt(functions, GL_MAX_TEXTURE_IMAGE_UNITS);
}
else
{
// Can't support ES2 version without these caps
LimitVersion(maxSupportedESVersion, gl::Version(0, 0));
}
if (functions->isAtLeastGL(gl::Version(3, 2)) || functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxFragmentInputComponents =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_INPUT_COMPONENTS);
}
else
{
// There doesn't seem, to be a desktop extension to add this cap, maybe it could be given a
// safe limit instead of limiting the supported ES version.
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
if (functions->isAtLeastGL(gl::Version(3, 0)) || functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->minProgramTexelOffset = QuerySingleGLInt(functions, GL_MIN_PROGRAM_TEXEL_OFFSET);
caps->maxProgramTexelOffset = QuerySingleGLInt(functions, GL_MAX_PROGRAM_TEXEL_OFFSET);
}
else
{
// Can't support ES3 without texel offset, could possibly be emulated in the shader
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
// Table 6.33, implementation dependent aggregate shader limits
if (functions->isAtLeastGL(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_uniform_buffer_object") ||
functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxShaderUniformBlocks[gl::ShaderType::Vertex] =
QuerySingleGLInt(functions, GL_MAX_VERTEX_UNIFORM_BLOCKS);
caps->maxShaderUniformBlocks[gl::ShaderType::Fragment] =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_UNIFORM_BLOCKS);
caps->maxUniformBufferBindings =
QuerySingleGLInt(functions, GL_MAX_UNIFORM_BUFFER_BINDINGS);
caps->maxUniformBlockSize = QuerySingleGLInt64(functions, GL_MAX_UNIFORM_BLOCK_SIZE);
caps->uniformBufferOffsetAlignment =
QuerySingleGLInt(functions, GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
caps->maxCombinedUniformBlocks =
QuerySingleGLInt(functions, GL_MAX_COMBINED_UNIFORM_BLOCKS);
caps->maxCombinedShaderUniformComponents[gl::ShaderType::Vertex] =
QuerySingleGLInt64(functions, GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS);
caps->maxCombinedShaderUniformComponents[gl::ShaderType::Fragment] =
QuerySingleGLInt64(functions, GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS);
}
else
{
// Can't support ES3 without uniform blocks
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
if (functions->isAtLeastGL(gl::Version(3, 2)) &&
(functions->profile & GL_CONTEXT_CORE_PROFILE_BIT) != 0)
{
caps->maxVaryingComponents = QuerySingleGLInt(functions, GL_MAX_VERTEX_OUTPUT_COMPONENTS);
}
else if (functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_ARB_ES2_compatibility") ||
functions->isAtLeastGLES(gl::Version(2, 0)))
{
caps->maxVaryingComponents = QuerySingleGLInt(functions, GL_MAX_VARYING_COMPONENTS);
}
else if (functions->isAtLeastGL(gl::Version(2, 0)))
{
caps->maxVaryingComponents = QuerySingleGLInt(functions, GL_MAX_VARYING_FLOATS);
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(0, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 1)) ||
functions->hasGLExtension("GL_ARB_ES2_compatibility") ||
functions->isAtLeastGLES(gl::Version(2, 0)))
{
caps->maxVaryingVectors = QuerySingleGLInt(functions, GL_MAX_VARYING_VECTORS);
}
else
{
// Doesn't limit ES version, GL_MAX_VARYING_COMPONENTS / 4 is acceptable.
caps->maxVaryingVectors = caps->maxVaryingComponents / 4;
}
// Determine the max combined texture image units by adding the vertex and fragment limits. If
// the real cap is queried, it would contain the limits for shader types that are not available
// to ES.
caps->maxCombinedTextureImageUnits =
QuerySingleGLInt(functions, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
// Table 6.34, implementation dependent transform feedback limits
if (functions->isAtLeastGL(gl::Version(4, 0)) ||
functions->hasGLExtension("GL_ARB_transform_feedback2") ||
functions->isAtLeastGLES(gl::Version(3, 0)))
{
caps->maxTransformFeedbackInterleavedComponents =
QuerySingleGLInt(functions, GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS);
caps->maxTransformFeedbackSeparateAttributes =
QuerySingleGLInt(functions, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS);
caps->maxTransformFeedbackSeparateComponents =
QuerySingleGLInt(functions, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS);
}
else
{
// Can't support ES3 without transform feedback
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
GLint sampleCountLimit = std::numeric_limits<GLint>::max();
if (features.limitMaxMSAASamplesTo4.enabled)
{
sampleCountLimit = 4;
}
// Table 6.35, Framebuffer Dependent Values
if (functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_EXT_framebuffer_multisample") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_multisampled_render_to_texture"))
{
caps->maxSamples = std::min(QuerySingleGLInt(functions, GL_MAX_SAMPLES), sampleCountLimit);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
// Non-constant sampler array indexing is required for OpenGL ES 2 and OpenGL ES after 3.2.
// However having it available on OpenGL ES 2 is a specification bug, and using this
// indexing in WebGL is undefined. Requiring this feature would break WebGL 1 for some users
// so we don't check for it. (it is present with ESSL 100, ESSL >= 320, GLSL >= 400 and
// GL_ARB_gpu_shader5)
// Check if sampler objects are supported
if (!functions->isAtLeastGL(gl::Version(3, 3)) &&
!functions->hasGLExtension("GL_ARB_sampler_objects") &&
!functions->isAtLeastGLES(gl::Version(3, 0)))
{
// Can't support ES3 without sampler objects
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
// Can't support ES3 without texture swizzling
if (!functions->isAtLeastGL(gl::Version(3, 3)) &&
!functions->hasGLExtension("GL_ARB_texture_swizzle") &&
!functions->hasGLExtension("GL_EXT_texture_swizzle") &&
!functions->isAtLeastGLES(gl::Version(3, 0)))
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
// Texture swizzling is required to work around the luminance texture format not being
// present in the core profile
if (functions->profile & GL_CONTEXT_CORE_PROFILE_BIT)
{
LimitVersion(maxSupportedESVersion, gl::Version(0, 0));
}
}
// Can't support ES3 without the GLSL packing builtins. We have a workaround for all
// desktop OpenGL versions starting from 3.3 with the bit packing extension.
if (!functions->isAtLeastGL(gl::Version(4, 2)) &&
!(functions->isAtLeastGL(gl::Version(3, 2)) &&
functions->hasGLExtension("GL_ARB_shader_bit_encoding")) &&
!functions->hasGLExtension("GL_ARB_shading_language_packing") &&
!functions->isAtLeastGLES(gl::Version(3, 0)))
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
// ES3 needs to support explicit layout location qualifiers, while it might be possible to
// fake them in our side, we currently don't support that.
if (!functions->isAtLeastGL(gl::Version(3, 3)) &&
!functions->hasGLExtension("GL_ARB_explicit_attrib_location") &&
!functions->isAtLeastGLES(gl::Version(3, 0)))
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_framebuffer_no_attachments"))
{
caps->maxFramebufferWidth = QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_WIDTH);
caps->maxFramebufferHeight = QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_HEIGHT);
caps->maxFramebufferSamples =
std::min(QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_SAMPLES), sampleCountLimit);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (functions->isAtLeastGL(gl::Version(3, 2)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_texture_multisample"))
{
caps->maxSampleMaskWords = QuerySingleGLInt(functions, GL_MAX_SAMPLE_MASK_WORDS);
caps->maxColorTextureSamples =
std::min(QuerySingleGLInt(functions, GL_MAX_COLOR_TEXTURE_SAMPLES), sampleCountLimit);
caps->maxDepthTextureSamples =
std::min(QuerySingleGLInt(functions, GL_MAX_DEPTH_TEXTURE_SAMPLES), sampleCountLimit);
caps->maxIntegerSamples =
std::min(QuerySingleGLInt(functions, GL_MAX_INTEGER_SAMPLES), sampleCountLimit);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_vertex_attrib_binding"))
{
caps->maxVertexAttribRelativeOffset =
QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET);
caps->maxVertexAttribBindings = QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIB_BINDINGS);
// OpenGL 4.3 has no limit on maximum value of stride.
// [OpenGL 4.3 (Core Profile) - February 14, 2013] Chapter 10.3.1 Page 298
if (features.emulateMaxVertexAttribStride.enabled ||
(functions->standard == STANDARD_GL_DESKTOP && functions->version == gl::Version(4, 3)))
{
caps->maxVertexAttribStride = 2048;
}
else
{
caps->maxVertexAttribStride = QuerySingleGLInt(functions, GL_MAX_VERTEX_ATTRIB_STRIDE);
}
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_shader_storage_buffer_object"))
{
caps->maxCombinedShaderOutputResources =
QuerySingleGLInt(functions, GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES);
caps->maxShaderStorageBlocks[gl::ShaderType::Fragment] =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS);
caps->maxShaderStorageBlocks[gl::ShaderType::Vertex] =
QuerySingleGLInt(functions, GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS);
caps->maxShaderStorageBufferBindings =
QuerySingleGLInt(functions, GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS);
caps->maxShaderStorageBlockSize =
QuerySingleGLInt64(functions, GL_MAX_SHADER_STORAGE_BLOCK_SIZE);
caps->maxCombinedShaderStorageBlocks =
QuerySingleGLInt(functions, GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS);
caps->shaderStorageBufferOffsetAlignment =
QuerySingleGLInt(functions, GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (nativegl::SupportsCompute(functions))
{
for (GLuint index = 0u; index < 3u; ++index)
{
caps->maxComputeWorkGroupCount[index] =
QuerySingleIndexGLInt(functions, GL_MAX_COMPUTE_WORK_GROUP_COUNT, index);
caps->maxComputeWorkGroupSize[index] =
QuerySingleIndexGLInt(functions, GL_MAX_COMPUTE_WORK_GROUP_SIZE, index);
}
caps->maxComputeWorkGroupInvocations =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS);
caps->maxShaderUniformBlocks[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_UNIFORM_BLOCKS);
caps->maxShaderTextureImageUnits[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS);
caps->maxComputeSharedMemorySize =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_SHARED_MEMORY_SIZE);
caps->maxShaderUniformComponents[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_UNIFORM_COMPONENTS);
caps->maxShaderAtomicCounterBuffers[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS);
caps->maxShaderAtomicCounters[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_ATOMIC_COUNTERS);
caps->maxShaderImageUniforms[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_IMAGE_UNIFORMS);
caps->maxCombinedShaderUniformComponents[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS);
caps->maxShaderStorageBlocks[gl::ShaderType::Compute] =
QuerySingleGLInt(functions, GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_explicit_uniform_location"))
{
caps->maxUniformLocations = QuerySingleGLInt(functions, GL_MAX_UNIFORM_LOCATIONS);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 0)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_texture_gather"))
{
caps->minProgramTextureGatherOffset =
QuerySingleGLInt(functions, GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET);
caps->maxProgramTextureGatherOffset =
QuerySingleGLInt(functions, GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 2)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_shader_image_load_store"))
{
caps->maxShaderImageUniforms[gl::ShaderType::Vertex] =
QuerySingleGLInt(functions, GL_MAX_VERTEX_IMAGE_UNIFORMS);
caps->maxShaderImageUniforms[gl::ShaderType::Fragment] =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_IMAGE_UNIFORMS);
caps->maxImageUnits = QuerySingleGLInt(functions, GL_MAX_IMAGE_UNITS);
caps->maxCombinedImageUniforms =
QuerySingleGLInt(functions, GL_MAX_COMBINED_IMAGE_UNIFORMS);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
if (functions->isAtLeastGL(gl::Version(4, 2)) || functions->isAtLeastGLES(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_shader_atomic_counters"))
{
caps->maxShaderAtomicCounterBuffers[gl::ShaderType::Vertex] =
QuerySingleGLInt(functions, GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS);
caps->maxShaderAtomicCounters[gl::ShaderType::Vertex] =
QuerySingleGLInt(functions, GL_MAX_VERTEX_ATOMIC_COUNTERS);
caps->maxShaderAtomicCounterBuffers[gl::ShaderType::Fragment] =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS);
caps->maxShaderAtomicCounters[gl::ShaderType::Fragment] =
QuerySingleGLInt(functions, GL_MAX_FRAGMENT_ATOMIC_COUNTERS);
caps->maxAtomicCounterBufferBindings =
QuerySingleGLInt(functions, GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS);
caps->maxAtomicCounterBufferSize =
QuerySingleGLInt(functions, GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE);
caps->maxCombinedAtomicCounterBuffers =
QuerySingleGLInt(functions, GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS);
caps->maxCombinedAtomicCounters =
QuerySingleGLInt(functions, GL_MAX_COMBINED_ATOMIC_COUNTERS);
}
else
{
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
// TODO(geofflang): The gl-uniform-arrays WebGL conformance test struggles to complete on time
// if the max uniform vectors is too large. Artificially limit the maximum until the test is
// updated.
caps->maxVertexUniformVectors = std::min(1024, caps->maxVertexUniformVectors);
caps->maxShaderUniformComponents[gl::ShaderType::Vertex] =
std::min(caps->maxVertexUniformVectors * 4,
caps->maxShaderUniformComponents[gl::ShaderType::Vertex]);
caps->maxFragmentUniformVectors = std::min(1024, caps->maxFragmentUniformVectors);
caps->maxShaderUniformComponents[gl::ShaderType::Fragment] =
std::min(caps->maxFragmentUniformVectors * 4,
caps->maxShaderUniformComponents[gl::ShaderType::Fragment]);
// If it is not possible to support reading buffer data back, a shadow copy of the buffers must
// be held. This disallows writing to buffers indirectly through transform feedback, thus
// disallowing ES3.
if (!CanMapBufferForRead(functions))
{
LimitVersion(maxSupportedESVersion, gl::Version(2, 0));
}
// GL_OES_texture_cube_map_array
if (functions->isAtLeastGL(gl::Version(4, 0)) ||
functions->hasGLESExtension("GL_OES_texture_cube_map_array") ||
functions->hasGLESExtension("GL_EXT_texture_cube_map_array") ||
functions->hasGLExtension("GL_ARB_texture_cube_map_array") ||
functions->isAtLeastGLES(gl::Version(3, 2)))
{
extensions->textureCubeMapArrayOES = true;
extensions->textureCubeMapArrayEXT = true;
}
else
{
// Can't support ES3.2 without cube map array textures
LimitVersion(maxSupportedESVersion, gl::Version(3, 1));
}
if (!nativegl::SupportsVertexArrayObjects(functions) ||
features.syncVertexArraysToDefault.enabled)
{
// ES 3.1 vertex bindings are not emulated on the default vertex array
LimitVersion(maxSupportedESVersion, gl::Version(3, 0));
}
// Extension support
extensions->setTextureExtensionSupport(*textureCapsMap);
// Expose this extension only when we support the formats or we're running on top of a native
// ES driver.
extensions->textureCompressionAstcLdrKHR =
extensions->textureCompressionAstcLdrKHR &&
(features.allowAstcFormats.enabled || functions->standard == STANDARD_GL_ES);
extensions->textureCompressionAstcHdrKHR =
extensions->textureCompressionAstcLdrKHR &&
functions->hasExtension("GL_KHR_texture_compression_astc_hdr");
extensions->textureCompressionAstcSliced3dKHR =
(extensions->textureCompressionAstcLdrKHR &&
functions->hasExtension("GL_KHR_texture_compression_astc_sliced_3d")) ||
extensions->textureCompressionAstcHdrKHR;
extensions->elementIndexUintOES = functions->standard == STANDARD_GL_DESKTOP ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_element_index_uint");
extensions->getProgramBinaryOES = caps->programBinaryFormats.size() > 0;
extensions->readFormatBgraEXT = functions->isAtLeastGL(gl::Version(1, 2)) ||
functions->hasGLExtension("GL_EXT_bgra") ||
functions->hasGLESExtension("GL_EXT_read_format_bgra");
extensions->pixelBufferObjectNV = functions->isAtLeastGL(gl::Version(2, 1)) ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_ARB_pixel_buffer_object") ||
functions->hasGLExtension("GL_EXT_pixel_buffer_object") ||
functions->hasGLESExtension("GL_NV_pixel_buffer_object");
extensions->syncARB = nativegl::SupportsFenceSync(functions);
extensions->mapbufferOES = functions->isAtLeastGL(gl::Version(1, 5)) ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_mapbuffer");
extensions->mapBufferRangeEXT = functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_ARB_map_buffer_range") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_map_buffer_range");
extensions->textureNpotOES = functions->standard == STANDARD_GL_DESKTOP ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_texture_npot");
// Note that we could emulate EXT_draw_buffers on ES 3.0's core functionality.
extensions->drawBuffersEXT = functions->isAtLeastGL(gl::Version(2, 0)) ||
functions->hasGLExtension("ARB_draw_buffers") ||
functions->hasGLESExtension("GL_EXT_draw_buffers");
extensions->drawBuffersIndexedEXT =
!features.disableDrawBuffersIndexed.enabled &&
(functions->isAtLeastGL(gl::Version(4, 0)) ||
(functions->hasGLExtension("GL_EXT_draw_buffers2") &&
functions->hasGLExtension("GL_ARB_draw_buffers_blend")) ||
functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_OES_draw_buffers_indexed") ||
functions->hasGLESExtension("GL_EXT_draw_buffers_indexed"));
extensions->drawBuffersIndexedOES = extensions->drawBuffersIndexedEXT;
extensions->textureStorageEXT = functions->standard == STANDARD_GL_DESKTOP ||
functions->hasGLESExtension("GL_EXT_texture_storage");
extensions->textureFilterAnisotropicEXT =
functions->hasGLExtension("GL_EXT_texture_filter_anisotropic") ||
functions->hasGLESExtension("GL_EXT_texture_filter_anisotropic");
extensions->occlusionQueryBooleanEXT = nativegl::SupportsOcclusionQueries(functions);
caps->maxTextureAnisotropy =
extensions->textureFilterAnisotropicEXT
? QuerySingleGLFloat(functions, GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)
: 0.0f;
extensions->fenceNV = FenceNVGL::Supported(functions) || FenceNVSyncGL::Supported(functions);
extensions->blendMinmaxEXT = functions->isAtLeastGL(gl::Version(1, 5)) ||
functions->hasGLExtension("GL_EXT_blend_minmax") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_blend_minmax");
extensions->framebufferBlitNV = functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_EXT_framebuffer_blit") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_NV_framebuffer_blit");
extensions->framebufferBlitANGLE =
extensions->framebufferBlitNV || functions->hasGLESExtension("GL_ANGLE_framebuffer_blit");
extensions->framebufferMultisampleANGLE =
extensions->framebufferBlitANGLE && caps->maxSamples > 0;
extensions->multisampledRenderToTextureEXT =
!features.disableMultisampledRenderToTexture.enabled &&
(functions->hasGLESExtension("GL_EXT_multisampled_render_to_texture") ||
functions->hasGLESExtension("GL_IMG_multisampled_render_to_texture"));
extensions->multisampledRenderToTexture2EXT =
!features.disableMultisampledRenderToTexture.enabled &&
extensions->multisampledRenderToTextureEXT &&
functions->hasGLESExtension("GL_EXT_multisampled_render_to_texture2");
extensions->standardDerivativesOES = functions->isAtLeastGL(gl::Version(2, 0)) ||
functions->hasGLExtension("GL_ARB_fragment_shader") ||
functions->hasGLESExtension("GL_OES_standard_derivatives");
extensions->shaderTextureLodEXT = functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_ARB_shader_texture_lod") ||
functions->hasGLESExtension("GL_EXT_shader_texture_lod");
extensions->fragDepthEXT = functions->standard == STANDARD_GL_DESKTOP ||
functions->hasGLESExtension("GL_EXT_frag_depth");
// Support video texture extension on non Android backends.
// TODO(crbug.com/776222): support Android and Apple devices.
extensions->videoTextureWEBGL = !IsAndroid() && !IsApple();
if (functions->hasGLExtension("GL_ARB_shader_viewport_layer_array") ||
functions->hasGLExtension("GL_NV_viewport_array2"))
{
extensions->multiviewOVR = true;
extensions->multiview2OVR = true;
// GL_MAX_ARRAY_TEXTURE_LAYERS is guaranteed to be at least 256.
const int maxLayers = QuerySingleGLInt(functions, GL_MAX_ARRAY_TEXTURE_LAYERS);
// GL_MAX_VIEWPORTS is guaranteed to be at least 16.
const int maxViewports = QuerySingleGLInt(functions, GL_MAX_VIEWPORTS);
caps->maxViews = static_cast<GLuint>(std::min(maxLayers, maxViewports));
*multiviewImplementationType = MultiviewImplementationTypeGL::NV_VIEWPORT_ARRAY2;
}
extensions->fboRenderMipmapOES = functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_EXT_framebuffer_object") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_fbo_render_mipmap");
extensions->textureBorderClampOES =
functions->standard == STANDARD_GL_DESKTOP ||
functions->hasGLESExtension("GL_OES_texture_border_clamp") ||
functions->hasGLESExtension("GL_EXT_texture_border_clamp") ||
functions->hasGLESExtension("GL_NV_texture_border_clamp");
extensions->multiDrawIndirectEXT = true;
extensions->instancedArraysANGLE = functions->isAtLeastGL(gl::Version(3, 1)) ||
(functions->hasGLExtension("GL_ARB_instanced_arrays") &&
(functions->hasGLExtension("GL_ARB_draw_instanced") ||
functions->hasGLExtension("GL_EXT_draw_instanced"))) ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_instanced_arrays");
extensions->instancedArraysEXT = extensions->instancedArraysANGLE;
extensions->unpackSubimageEXT = functions->standard == STANDARD_GL_DESKTOP ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_unpack_subimage");
extensions->shaderNoperspectiveInterpolationNV = functions->isAtLeastGL(gl::Version(3, 0));
extensions->packSubimageNV = functions->standard == STANDARD_GL_DESKTOP ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_NV_pack_subimage");
extensions->vertexArrayObjectOES = functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_ARB_vertex_array_object") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_vertex_array_object");
extensions->debugMarkerEXT = functions->isAtLeastGL(gl::Version(4, 3)) ||
functions->hasGLExtension("GL_KHR_debug") ||
functions->hasGLExtension("GL_EXT_debug_marker") ||
functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_KHR_debug") ||
functions->hasGLESExtension("GL_EXT_debug_marker");
extensions->EGLImageOES = functions->hasGLESExtension("GL_OES_EGL_image");
extensions->EGLImageExternalOES = functions->hasGLESExtension("GL_OES_EGL_image_external");
extensions->EGLImageExternalWrapModesEXT =
functions->hasExtension("GL_EXT_EGL_image_external_wrap_modes");
extensions->EGLImageExternalEssl3OES =
functions->hasGLESExtension("GL_OES_EGL_image_external_essl3");
extensions->EGLImageArrayEXT = functions->hasGLESExtension("GL_EXT_EGL_image_array");
extensions->EGLSyncOES = functions->hasGLESExtension("GL_OES_EGL_sync");
if (!features.disableTimestampQueries.enabled &&
(functions->isAtLeastGL(gl::Version(3, 3)) ||
functions->hasGLExtension("GL_ARB_timer_query") ||
functions->hasGLESExtension("GL_EXT_disjoint_timer_query")))
{
extensions->disjointTimerQueryEXT = true;
// If we can't query the counter bits, leave them at 0.
if (!features.queryCounterBitsGeneratesErrors.enabled)
{
caps->queryCounterBitsTimeElapsed =
QueryQueryValue(functions, GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS);
caps->queryCounterBitsTimestamp =
QueryQueryValue(functions, GL_TIMESTAMP, GL_QUERY_COUNTER_BITS);
}
}
// the EXT_multisample_compatibility is written against ES3.1 but can apply
// to earlier versions so therefore we're only checking for the extension string
// and not the specific GLES version.
extensions->multisampleCompatibilityEXT =
functions->isAtLeastGL(gl::Version(1, 3)) ||
functions->hasGLESExtension("GL_EXT_multisample_compatibility");
extensions->framebufferMixedSamplesCHROMIUM =
functions->hasGLExtension("GL_NV_framebuffer_mixed_samples") ||
functions->hasGLESExtension("GL_NV_framebuffer_mixed_samples");
extensions->robustnessEXT = functions->isAtLeastGL(gl::Version(4, 5)) ||
functions->hasGLExtension("GL_KHR_robustness") ||
functions->hasGLExtension("GL_ARB_robustness") ||
functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_KHR_robustness") ||
functions->hasGLESExtension("GL_EXT_robustness");
extensions->robustBufferAccessBehaviorKHR =
extensions->robustnessEXT &&
(functions->hasGLExtension("GL_ARB_robust_buffer_access_behavior") ||
functions->hasGLESExtension("GL_KHR_robust_buffer_access_behavior"));
extensions->copyTextureCHROMIUM = true;
extensions->syncQueryCHROMIUM = SyncQueryGL::IsSupported(functions);
// Note that OES_texture_storage_multisample_2d_array support could be extended down to GL 3.2
// if we emulated texStorage* API on top of texImage*.
extensions->textureStorageMultisample2dArrayOES =
functions->isAtLeastGL(gl::Version(4, 2)) || functions->isAtLeastGLES(gl::Version(3, 2));
extensions->multiviewMultisampleANGLE = extensions->textureStorageMultisample2dArrayOES &&
(extensions->multiviewOVR || extensions->multiview2OVR);
extensions->textureMultisampleANGLE = functions->isAtLeastGL(gl::Version(3, 2)) ||
functions->hasGLExtension("GL_ARB_texture_multisample");
extensions->textureSRGBDecodeEXT = functions->hasGLExtension("GL_EXT_texture_sRGB_decode") ||
functions->hasGLESExtension("GL_EXT_texture_sRGB_decode");
// ANGLE treats ETC1 as ETC2 for ES 3.0 and higher because it becomes a core format, and they
// are backwards compatible.
extensions->compressedETC1RGB8SubTextureEXT =
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_compressed_ETC1_RGB8_sub_texture");
#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
angle::SystemInfo info;
if (angle::GetSystemInfo(&info) && !info.needsEAGLOnMac)
{
VendorID vendor = GetVendorID(functions);
if ((IsAMD(vendor) || IsIntel(vendor)) && *maxSupportedESVersion >= gl::Version(3, 0))
{
// Apple Intel/AMD drivers do not correctly use the TEXTURE_SRGB_DECODE property of
// sampler states. Disable this extension when we would advertise any ES version
// that has samplers.
extensions->textureSRGBDecodeEXT = false;
}
}
#endif
extensions->sRGBWriteControlEXT = functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_EXT_framebuffer_sRGB") ||
functions->hasGLExtension("GL_ARB_framebuffer_sRGB") ||
functions->hasGLESExtension("GL_EXT_sRGB_write_control");
#if defined(ANGLE_PLATFORM_ANDROID)
// SRGB blending does not appear to work correctly on the Nexus 5. Writing to an SRGB
// framebuffer with GL_FRAMEBUFFER_SRGB enabled and then reading back returns the same value.
// Disabling GL_FRAMEBUFFER_SRGB will then convert in the wrong direction.
extensions->sRGBWriteControlEXT = false;
// BGRA formats do not appear to be accepted by the Nexus 5X driver despite the extension being
// exposed.
extensions->textureFormatBGRA8888EXT = false;
#endif
// EXT_discard_framebuffer can be implemented as long as glDiscardFramebufferEXT or
// glInvalidateFramebuffer is available
extensions->discardFramebufferEXT = functions->isAtLeastGL(gl::Version(4, 3)) ||
functions->hasGLExtension("GL_ARB_invalidate_subdata") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_discard_framebuffer") ||
functions->hasGLESExtension("GL_ARB_invalidate_subdata");
extensions->translatedShaderSourceANGLE = true;
if (functions->isAtLeastGL(gl::Version(3, 1)) ||
functions->hasGLExtension("GL_ARB_texture_rectangle"))
{
extensions->textureRectangleANGLE = true;
caps->maxRectangleTextureSize = std::min(
QuerySingleGLInt(functions, GL_MAX_RECTANGLE_TEXTURE_SIZE_ANGLE), textureSizeLimit);
}
// OpenGL 4.3 (and above) and OpenGL ES 3.2 can support all features and constants defined in
// GL_EXT_geometry_shader.
bool hasCoreGSSupport =
functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 2));
// OpenGL 4.0 adds the support for instanced geometry shader
// GL_ARB_shader_atomic_counters adds atomic counters to geometry shader
// GL_ARB_shader_storage_buffer_object adds shader storage buffers to geometry shader
// GL_ARB_shader_image_load_store adds images to geometry shader
bool hasInstancedGSSupport = functions->isAtLeastGL(gl::Version(4, 0)) &&
functions->hasGLExtension("GL_ARB_shader_atomic_counters") &&
functions->hasGLExtension("GL_ARB_shader_storage_buffer_object") &&
functions->hasGLExtension("GL_ARB_shader_image_load_store");
if (hasCoreGSSupport || functions->hasGLESExtension("GL_OES_geometry_shader") ||
functions->hasGLESExtension("GL_EXT_geometry_shader") || hasInstancedGSSupport)
{
extensions->geometryShaderEXT = functions->hasGLESExtension("GL_EXT_geometry_shader") ||
hasCoreGSSupport || hasInstancedGSSupport;
extensions->geometryShaderOES = functions->hasGLESExtension("GL_OES_geometry_shader") ||
hasCoreGSSupport || hasInstancedGSSupport;
caps->maxFramebufferLayers = QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_LAYERS_EXT);
// GL_PROVOKING_VERTEX isn't a valid return value of GL_LAYER_PROVOKING_VERTEX_EXT in
// GL_EXT_geometry_shader SPEC, however it is legal in desktop OpenGL, which means the value
// follows the one set by glProvokingVertex.
// [OpenGL 4.3] Chapter 11.3.4.6
// The vertex conventions followed for gl_Layer and gl_ViewportIndex may be determined by
// calling GetIntegerv with the symbolic constants LAYER_PROVOKING_VERTEX and
// VIEWPORT_INDEX_PROVOKING_VERTEX, respectively. For either query, if the value returned is
// PROVOKING_VERTEX, then vertex selection follows the convention specified by
// ProvokingVertex.
caps->layerProvokingVertex = QuerySingleGLInt(functions, GL_LAYER_PROVOKING_VERTEX_EXT);
if (caps->layerProvokingVertex == GL_PROVOKING_VERTEX)
{
// We should use GL_LAST_VERTEX_CONVENTION_EXT instead because desktop OpenGL SPEC
// requires the initial value of provoking vertex mode is LAST_VERTEX_CONVENTION.
// [OpenGL 4.3] Chapter 13.4
// The initial value of the provoking vertex mode is LAST_VERTEX_CONVENTION.
caps->layerProvokingVertex = GL_LAST_VERTEX_CONVENTION_EXT;
}
caps->maxShaderUniformComponents[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT);
caps->maxShaderUniformBlocks[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT);
caps->maxCombinedShaderUniformComponents[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT);
caps->maxGeometryInputComponents =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT);
caps->maxGeometryOutputComponents =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT);
caps->maxGeometryOutputVertices =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT);
caps->maxGeometryTotalOutputComponents =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT);
caps->maxGeometryShaderInvocations =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT);
caps->maxShaderTextureImageUnits[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT);
caps->maxShaderAtomicCounterBuffers[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT);
caps->maxShaderAtomicCounters[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT);
caps->maxShaderImageUniforms[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT);
caps->maxShaderStorageBlocks[gl::ShaderType::Geometry] =
QuerySingleGLInt(functions, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT);
}
// The real combined caps contain limits for shader types that are not available to ES, so limit
// the caps to the sum of vertex+fragment+geometry shader caps.
CapCombinedLimitToESShaders(&caps->maxCombinedUniformBlocks, caps->maxShaderUniformBlocks);
CapCombinedLimitToESShaders(&caps->maxCombinedTextureImageUnits,
caps->maxShaderTextureImageUnits);
CapCombinedLimitToESShaders(&caps->maxCombinedShaderStorageBlocks,
caps->maxShaderStorageBlocks);
CapCombinedLimitToESShaders(&caps->maxCombinedImageUniforms, caps->maxShaderImageUniforms);
CapCombinedLimitToESShaders(&caps->maxCombinedAtomicCounterBuffers,
caps->maxShaderAtomicCounterBuffers);
CapCombinedLimitToESShaders(&caps->maxCombinedAtomicCounters, caps->maxShaderAtomicCounters);
// EXT_blend_func_extended.
// Note that this could be implemented also on top of native EXT_blend_func_extended, but it's
// currently not fully implemented.
extensions->blendFuncExtendedEXT = !features.disableBlendFuncExtended.enabled &&
functions->standard == STANDARD_GL_DESKTOP &&
functions->hasGLExtension("GL_ARB_blend_func_extended");
if (extensions->blendFuncExtendedEXT)
{
// TODO(http://anglebug.com/1085): Support greater values of
// MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT queried from the driver. See comments in ProgramGL.cpp
// for more information about this limitation.
caps->maxDualSourceDrawBuffers = 1;
}
// EXT_float_blend
// Assume all desktop driver supports this by default.
extensions->floatBlendEXT = functions->standard == STANDARD_GL_DESKTOP ||
functions->hasGLESExtension("GL_EXT_float_blend") ||
functions->isAtLeastGLES(gl::Version(3, 2));
// ANGLE_base_vertex_base_instance
extensions->baseVertexBaseInstanceANGLE =
functions->isAtLeastGL(gl::Version(3, 2)) || functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_OES_draw_elements_base_vertex") ||
functions->hasGLESExtension("GL_EXT_draw_elements_base_vertex");
// ANGLE_base_vertex_base_instance_shader_builtin
extensions->baseVertexBaseInstanceShaderBuiltinANGLE = extensions->baseVertexBaseInstanceANGLE;
// OES_draw_elements_base_vertex
extensions->drawElementsBaseVertexOES =
functions->isAtLeastGL(gl::Version(3, 2)) || functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_OES_draw_elements_base_vertex");
// EXT_draw_elements_base_vertex
extensions->drawElementsBaseVertexEXT =
functions->isAtLeastGL(gl::Version(3, 2)) || functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_EXT_draw_elements_base_vertex");
// ANGLE_compressed_texture_etc
// Expose this extension only when we support the formats or we're running on top of a native
// ES driver.
extensions->compressedTextureEtcANGLE =
(features.allowETCFormats.enabled || functions->standard == STANDARD_GL_ES) &&
gl::DetermineCompressedTextureETCSupport(*textureCapsMap);
// When running on top of desktop OpenGL drivers and allow_etc_formats feature is not enabled,
// mark ETC1 as emulated to hide it from WebGL clients.
limitations->emulatedEtc1 =
!features.allowETCFormats.enabled && functions->standard == STANDARD_GL_DESKTOP;
// To work around broken unsized sRGB textures, sized sRGB textures are used. Disable EXT_sRGB
// if those formats are not available.
if (features.unsizedSRGBReadPixelsDoesntTransform.enabled &&
!functions->isAtLeastGLES(gl::Version(3, 0)))
{
extensions->sRGBEXT = false;
}
extensions->provokingVertexANGLE = functions->hasGLExtension("GL_ARB_provoking_vertex") ||
functions->hasGLExtension("GL_EXT_provoking_vertex") ||
functions->isAtLeastGL(gl::Version(3, 2));
extensions->textureExternalUpdateANGLE = true;
extensions->texture3DOES = functions->isAtLeastGL(gl::Version(1, 2)) ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_texture_3D");
extensions->memoryObjectEXT = functions->hasGLExtension("GL_EXT_memory_object") ||
functions->hasGLESExtension("GL_EXT_memory_object");
extensions->semaphoreEXT = functions->hasGLExtension("GL_EXT_semaphore") ||
functions->hasGLESExtension("GL_EXT_semaphore");
extensions->memoryObjectFdEXT = functions->hasGLExtension("GL_EXT_memory_object_fd") ||
functions->hasGLESExtension("GL_EXT_memory_object_fd");
extensions->semaphoreFdEXT = !features.disableSemaphoreFd.enabled &&
(functions->hasGLExtension("GL_EXT_semaphore_fd") ||
functions->hasGLESExtension("GL_EXT_semaphore_fd"));
extensions->gpuShader5EXT = functions->isAtLeastGL(gl::Version(4, 0)) ||
functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLExtension("GL_ARB_gpu_shader5") ||
functions->hasGLESExtension("GL_EXT_gpu_shader5");
extensions->shaderIoBlocksOES = functions->isAtLeastGL(gl::Version(3, 2)) ||
functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_OES_shader_io_blocks") ||
functions->hasGLESExtension("GL_EXT_shader_io_blocks");
extensions->shaderIoBlocksEXT = extensions->shaderIoBlocksOES;
extensions->shadowSamplersEXT = functions->isAtLeastGL(gl::Version(2, 0)) ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_shadow_samplers");
// GL_APPLE_clip_distance
extensions->clipDistanceAPPLE = functions->isAtLeastGL(gl::Version(3, 0));
if (extensions->clipDistanceAPPLE)
{
caps->maxClipDistances = QuerySingleGLInt(functions, GL_MAX_CLIP_DISTANCES_EXT);
}
else
{
caps->maxClipDistances = 0;
}
// GL_OES_shader_image_atomic
//
// Note that imageAtomicExchange() is allowed to accept float textures (of r32f format) in this
// extension, but that's not supported by ARB_shader_image_load_store which this extension is
// based on, neither in the spec it was merged into it. This support was only added to desktop
// GLSL in version 4.5
if (functions->isAtLeastGL(gl::Version(4, 5)) || functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_OES_shader_image_atomic"))
{
extensions->shaderImageAtomicOES = true;
}
// GL_OES_texture_buffer
if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 2)) ||
functions->hasGLESExtension("GL_OES_texture_buffer") ||
functions->hasGLESExtension("GL_EXT_texture_buffer") ||
functions->hasGLExtension("GL_ARB_texture_buffer_object"))
{
caps->maxTextureBufferSize = QuerySingleGLInt(functions, GL_MAX_TEXTURE_BUFFER_SIZE);
caps->textureBufferOffsetAlignment =
QuerySingleGLInt(functions, GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT);
extensions->textureBufferOES = true;
extensions->textureBufferEXT = true;
}
else
{
// Can't support ES3.2 without texture buffer objects
LimitVersion(maxSupportedESVersion, gl::Version(3, 1));
}
extensions->YUVTargetEXT = functions->hasGLESExtension("GL_EXT_YUV_target");
// GL_MESA_framebuffer_flip_y
extensions->framebufferFlipYMESA = functions->hasGLESExtension("GL_MESA_framebuffer_flip_y") ||
functions->hasGLExtension("GL_MESA_framebuffer_flip_y");
// GL_KHR_parallel_shader_compile
extensions->parallelShaderCompileKHR = true;
// PVRTC1 textures must be squares on Apple platforms.
if (IsApple())
{
limitations->squarePvrtc1 = true;
}
}
bool GetSystemInfoVendorIDAndDeviceID(const FunctionsGL *functions,
angle::SystemInfo *outSystemInfo,
angle::VendorID *outVendor,
angle::DeviceID *outDevice)
{
// Get vendor from GL itself, so on multi-GPU systems the correct GPU is selected.
*outVendor = GetVendorID(functions);
*outDevice = 0;
// Gather additional information from the system to detect multi-GPU scenarios.
bool isGetSystemInfoSuccess = angle::GetSystemInfo(outSystemInfo);
// Get the device id from system info, corresponding to the vendor of the active GPU.
if (isGetSystemInfoSuccess && !outSystemInfo->gpus.empty())
{
if (*outVendor == VENDOR_ID_UNKNOWN)
{
// If vendor ID is unknown, take the best estimate of the active GPU. Chances are there
// is only one GPU anyway.
*outVendor = outSystemInfo->gpus[outSystemInfo->activeGPUIndex].vendorId;
*outDevice = outSystemInfo->gpus[outSystemInfo->activeGPUIndex].deviceId;
}
else
{
for (const angle::GPUDeviceInfo &gpu : outSystemInfo->gpus)
{
if (*outVendor == gpu.vendorId)
{
// Note that deviceId may not necessarily have been possible to retrieve.
*outDevice = gpu.deviceId;
break;
}
}
}
}
else
{
// If system info is not available, attempt to deduce the device from GL itself.
*outDevice = GetDeviceID(functions);
}
return isGetSystemInfoSuccess;
}
bool Has9thGenIntelGPU(const angle::SystemInfo &systemInfo)
{
for (const angle::GPUDeviceInfo &deviceInfo : systemInfo.gpus)
{
if (IsIntel(deviceInfo.vendorId) && Is9thGenIntel(deviceInfo.deviceId))
{
return true;
}
}
return false;
}
void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *features)
{
angle::VendorID vendor;
angle::DeviceID device;
angle::SystemInfo systemInfo;
bool isGetSystemInfoSuccess =
GetSystemInfoVendorIDAndDeviceID(functions, &systemInfo, &vendor, &device);
bool isAMD = IsAMD(vendor);
bool isIntel = IsIntel(vendor);
bool isNvidia = IsNvidia(vendor);
bool isQualcomm = IsQualcomm(vendor);
bool isVMWare = IsVMWare(vendor);
bool hasAMD = systemInfo.hasAMDGPU();
std::array<int, 3> mesaVersion = {0, 0, 0};
bool isMesa = IsMesa(functions, &mesaVersion);
// Don't use 1-bit alpha formats on desktop GL with AMD drivers.
ANGLE_FEATURE_CONDITION(features, avoid1BitAlphaTextureFormats,
functions->standard == STANDARD_GL_DESKTOP && isAMD);
ANGLE_FEATURE_CONDITION(features, RGBA4IsNotSupportedForColorRendering,
functions->standard == STANDARD_GL_DESKTOP && isIntel);
// Although "Sandy Bridge", "Ivy Bridge", and "Haswell" may support GL_ARB_ES3_compatibility
// extension, ETC2/EAC formats are emulated there. Newer Intel GPUs support them natively.
ANGLE_FEATURE_CONDITION(
features, allowETCFormats,
isIntel && !IsSandyBridge(device) && !IsIvyBridge(device) && !IsHaswell(device));
// Mesa always exposes ASTC extension but only Intel Gen9, Gen11, and Gen12 have hardware
// support for it. Newer Intel GPUs (Gen12.5+) do not support ASTC.
ANGLE_FEATURE_CONDITION(features, allowAstcFormats,
!isMesa || isIntel && (Is9thGenIntel(device) || IsGeminiLake(device) ||
IsCoffeeLake(device) || Is11thGenIntel(device) ||
Is12thGenIntel(device)));
// Ported from gpu_driver_bug_list.json (#183)
ANGLE_FEATURE_CONDITION(features, emulateAbsIntFunction, IsApple() && isIntel);
ANGLE_FEATURE_CONDITION(features, addAndTrueToLoopCondition, IsApple() && isIntel);
// Ported from gpu_driver_bug_list.json (#191)
ANGLE_FEATURE_CONDITION(
features, emulateIsnanFloat,
isIntel && IsApple() && IsSkylake(device) && GetMacOSVersion() < OSVersion(10, 13, 2));
ANGLE_FEATURE_CONDITION(features, doesSRGBClearsOnLinearFramebufferAttachments,
isIntel || isAMD);
ANGLE_FEATURE_CONDITION(features, emulateMaxVertexAttribStride,
IsLinux() && functions->standard == STANDARD_GL_DESKTOP && isAMD);
ANGLE_FEATURE_CONDITION(
features, useUnusedBlocksWithStandardOrSharedLayout,
(IsApple() && functions->standard == STANDARD_GL_DESKTOP) || (IsLinux() && isAMD));
// Ported from gpu_driver_bug_list.json (#187)
ANGLE_FEATURE_CONDITION(features, doWhileGLSLCausesGPUHang,
IsApple() && functions->standard == STANDARD_GL_DESKTOP &&
GetMacOSVersion() < OSVersion(10, 11, 0));
// Ported from gpu_driver_bug_list.json (#211)
ANGLE_FEATURE_CONDITION(features, rewriteFloatUnaryMinusOperator,
IsApple() && isIntel && GetMacOSVersion() < OSVersion(10, 12, 0));
ANGLE_FEATURE_CONDITION(features, vertexIDDoesNotIncludeBaseVertex, IsApple() && isAMD);
// Triggers a bug on Marshmallow Adreno (4xx?) driver.
// http://anglebug.com/2046
ANGLE_FEATURE_CONDITION(features, dontInitializeUninitializedLocals, IsAndroid() && isQualcomm);
ANGLE_FEATURE_CONDITION(features, finishDoesNotCauseQueriesToBeAvailable,
functions->standard == STANDARD_GL_DESKTOP && isNvidia);
// TODO(cwallez): Disable this workaround for MacOSX versions 10.9 or later.
ANGLE_FEATURE_CONDITION(features, alwaysCallUseProgramAfterLink, true);
ANGLE_FEATURE_CONDITION(features, unpackOverlappingRowsSeparatelyUnpackBuffer, isNvidia);
ANGLE_FEATURE_CONDITION(features, packOverlappingRowsSeparatelyPackBuffer, isNvidia);
ANGLE_FEATURE_CONDITION(features, initializeCurrentVertexAttributes, isNvidia);
ANGLE_FEATURE_CONDITION(features, unpackLastRowSeparatelyForPaddingInclusion,
IsApple() || isNvidia);
ANGLE_FEATURE_CONDITION(features, packLastRowSeparatelyForPaddingInclusion,
IsApple() || isNvidia);
ANGLE_FEATURE_CONDITION(features, removeInvariantAndCentroidForESSL3,
functions->isAtMostGL(gl::Version(4, 1)) ||
(functions->standard == STANDARD_GL_DESKTOP && isAMD));
// TODO(oetuaho): Make this specific to the affected driver versions. Versions that came after
// 364 are known to be affected, at least up to 375.
ANGLE_FEATURE_CONDITION(features, emulateAtan2Float, isNvidia);
ANGLE_FEATURE_CONDITION(features, reapplyUBOBindingsAfterUsingBinaryProgram,
isAMD || IsAndroid());
// TODO(oetuaho): Make this specific to the affected driver versions. Versions at least up to
// 390 are known to be affected. Versions after that are expected not to be affected.
ANGLE_FEATURE_CONDITION(features, clampFragDepth, isNvidia);
// TODO(oetuaho): Make this specific to the affected driver versions. Versions since 397.31 are
// not affected.
ANGLE_FEATURE_CONDITION(features, rewriteRepeatedAssignToSwizzled, isNvidia);
// TODO(jmadill): Narrow workaround range for specific devices.
ANGLE_FEATURE_CONDITION(features, clampPointSize, IsAndroid() || isNvidia);
// Ported from gpu_driver_bug_list.json (#246, #258)
ANGLE_FEATURE_CONDITION(features, dontUseLoopsToInitializeVariables,
(IsAndroid() && isQualcomm) || (isIntel && IsApple()));
ANGLE_FEATURE_CONDITION(features, disableBlendFuncExtended, isAMD || isIntel);
ANGLE_FEATURE_CONDITION(features, unsizedSRGBReadPixelsDoesntTransform,
IsAndroid() && isQualcomm);
ANGLE_FEATURE_CONDITION(features, queryCounterBitsGeneratesErrors, IsNexus5X(vendor, device));
ANGLE_FEATURE_CONDITION(features, dontRelinkProgramsInParallel,
IsAndroid() || (IsWindows() && isIntel));
// TODO(jie.a.chen@intel.com): Clean up the bugs.
// anglebug.com/3031
// crbug.com/922936
// crbug.com/1184692
// crbug.com/1202928
ANGLE_FEATURE_CONDITION(features, disableWorkerContexts,
(IsWindows() && (isIntel || isAMD)) || (IsLinux() && isNvidia) ||
IsIOS() || IsAndroid() || IsAndroidEmulator(functions));
bool limitMaxTextureSize = isIntel && IsLinux() && GetLinuxOSVersion() < OSVersion(5, 0, 0);
ANGLE_FEATURE_CONDITION(features, limitMaxTextureSizeTo4096,
IsAndroid() || limitMaxTextureSize);
// On Apple switchable graphics, GL_MAX_SAMPLES may differ between the GPUs.
// 4 is a lowest common denominator that is always supported.
ANGLE_FEATURE_CONDITION(features, limitMaxMSAASamplesTo4,
IsAndroid() || (IsApple() && (isIntel || isAMD || isNvidia)));
ANGLE_FEATURE_CONDITION(features, limitMax3dArrayTextureSizeTo1024, limitMaxTextureSize);
ANGLE_FEATURE_CONDITION(features, allowClearForRobustResourceInit, IsApple());
// The WebGL conformance/uniforms/out-of-bounds-uniform-array-access test has been seen to fail
// on AMD and Android devices.
// This test is also flaky on Linux Nvidia. So we just turn it on everywhere and don't rely on
// driver since security is important.
ANGLE_FEATURE_CONDITION(
features, clampArrayAccess,
IsAndroid() || isAMD || !functions->hasExtension("GL_KHR_robust_buffer_access_behavior"));
ANGLE_FEATURE_CONDITION(features, resetTexImage2DBaseLevel,
IsApple() && isIntel && GetMacOSVersion() >= OSVersion(10, 12, 4));
ANGLE_FEATURE_CONDITION(features, clearToZeroOrOneBroken,
IsApple() && isIntel && GetMacOSVersion() < OSVersion(10, 12, 6));
ANGLE_FEATURE_CONDITION(features, adjustSrcDstRegionForBlitFramebuffer,
IsLinux() || (IsAndroid() && isNvidia) || (IsWindows() && isNvidia) ||
(IsApple() && functions->standard == STANDARD_GL_ES));
ANGLE_FEATURE_CONDITION(features, clipSrcRegionForBlitFramebuffer,
IsApple() || (IsLinux() && isAMD));
ANGLE_FEATURE_CONDITION(features, RGBDXT1TexturesSampleZeroAlpha, IsApple());
ANGLE_FEATURE_CONDITION(features, unfoldShortCircuits, IsApple());
ANGLE_FEATURE_CONDITION(features, emulatePrimitiveRestartFixedIndex,
functions->standard == STANDARD_GL_DESKTOP &&
functions->isAtLeastGL(gl::Version(3, 1)) &&
!functions->isAtLeastGL(gl::Version(4, 3)));
ANGLE_FEATURE_CONDITION(
features, setPrimitiveRestartFixedIndexForDrawArrays,
features->emulatePrimitiveRestartFixedIndex.enabled && IsApple() && isIntel);
ANGLE_FEATURE_CONDITION(features, removeDynamicIndexingOfSwizzledVector,
IsApple() || IsAndroid() || IsWindows());
// Ported from gpu_driver_bug_list.json (#89)
ANGLE_FEATURE_CONDITION(features, regenerateStructNames, IsApple());
// Ported from gpu_driver_bug_list.json (#184)
ANGLE_FEATURE_CONDITION(features, preAddTexelFetchOffsets, IsApple() && isIntel);
// Workaround for the widespread OpenGL ES driver implementaion bug
ANGLE_FEATURE_CONDITION(features, readPixelsUsingImplementationColorReadFormatForNorm16,
!isIntel && functions->standard == STANDARD_GL_ES &&
functions->isAtLeastGLES(gl::Version(3, 1)) &&
functions->hasGLESExtension("GL_EXT_texture_norm16"));
// anglebug.com/4267
ANGLE_FEATURE_CONDITION(features, flushBeforeDeleteTextureIfCopiedTo, IsApple() && isIntel);
// anglebug.com/2273
// Seems to affect both Intel and AMD GPUs. Enable workaround for all GPUs on macOS.
ANGLE_FEATURE_CONDITION(features, rewriteRowMajorMatrices,
// IsApple() && functions->standard == STANDARD_GL_DESKTOP);
// TODO(anglebug.com/2273): diagnose crashes with this workaround.
false);
ANGLE_FEATURE_CONDITION(features, disableDrawBuffersIndexed, IsWindows() && isAMD);
ANGLE_FEATURE_CONDITION(
features, disableSemaphoreFd,
IsLinux() && isAMD && isMesa && mesaVersion < (std::array<int, 3>{19, 3, 5}));
ANGLE_FEATURE_CONDITION(
features, disableTimestampQueries,
(IsLinux() && isVMWare) || (IsAndroid() && isNvidia) ||
(IsAndroid() && GetAndroidSdkLevel() < 27 && IsAdreno5xxOrOlder(functions)) ||
(IsAndroid() && IsMaliT8xxOrOlder(functions)) ||
(IsAndroid() && IsMaliG31OrOlder(functions)));
ANGLE_FEATURE_CONDITION(features, decodeEncodeSRGBForGenerateMipmap, IsApple());
// anglebug.com/4674
// The (redundant) explicit exclusion of Windows AMD is because the workaround fails
// Texture2DRGTest.TextureRGUNormTest on that platform, and the test is skipped. If
// you'd like to enable the workaround on Windows AMD, please fix the test first.
ANGLE_FEATURE_CONDITION(
features, emulateCopyTexImage2DFromRenderbuffers,
IsApple() && functions->standard == STANDARD_GL_ES && !(isAMD && IsWindows()));
// Don't attempt to use the discrete GPU on NVIDIA-based MacBook Pros, since the
// driver is unstable in this situation.
//
// Note that this feature is only set here in order to advertise this workaround
// externally. GPU switching support must be enabled or disabled early, during display
// initialization, before these features are set up.
bool isDualGPUMacWithNVIDIA = false;
if (IsApple() && functions->standard == STANDARD_GL_DESKTOP)
{
if (isGetSystemInfoSuccess)
{
// The full system information must be queried to see whether it's a dual-GPU
// NVIDIA MacBook Pro since it's likely that the integrated GPU will be active
// when these features are initialized.
isDualGPUMacWithNVIDIA = systemInfo.isMacSwitchable && systemInfo.hasNVIDIAGPU();
}
}
ANGLE_FEATURE_CONDITION(features, disableGPUSwitchingSupport, isDualGPUMacWithNVIDIA);
// Workaround issue in NVIDIA GL driver on Linux when TSAN is enabled
// http://crbug.com/1094869
bool isTSANBuild = false;
#ifdef THREAD_SANITIZER
isTSANBuild = true;
#endif
ANGLE_FEATURE_CONDITION(features, disableNativeParallelCompile,
isTSANBuild && IsLinux() && isNvidia);
// anglebug.com/4849
// This workaround is definitely needed on Intel and AMD GPUs. To
// determine whether it's needed on iOS and Apple Silicon, the
// workaround's being restricted to existing desktop GPUs.
ANGLE_FEATURE_CONDITION(features, emulatePackSkipRowsAndPackSkipPixels,
IsApple() && (isAMD || isIntel || isNvidia));
// http://crbug.com/1042393
// XWayland defaults to a 1hz refresh rate when the "surface is not visible", which sometimes
// causes issues in Chrome. To get around this, default to a 30Hz refresh rate if we see bogus
// from the driver.
ANGLE_FEATURE_CONDITION(features, clampMscRate, IsLinux() && IsWayland());
ANGLE_FEATURE_CONDITION(features, bindTransformFeedbackBufferBeforeBindBufferRange, IsApple());
// http://crbug.com/1137851
// Speculative fix for above issue, users can enable it via flags.
// http://crbug.com/1187475
// Disable on Mesa 20 / Intel
ANGLE_FEATURE_CONDITION(features, disableSyncControlSupport,
IsLinux() && isIntel && isMesa && mesaVersion[0] == 20);
ANGLE_FEATURE_CONDITION(features, keepBufferShadowCopy, !CanMapBufferForRead(functions));
ANGLE_FEATURE_CONDITION(features, setZeroLevelBeforeGenerateMipmap, IsApple());
ANGLE_FEATURE_CONDITION(features, promotePackedFormatsTo8BitPerChannel, IsApple() && hasAMD);
// crbug.com/1171371
// If output variable gl_FragColor is written by fragment shader, it may cause context lost with
// Adreno 42x and 3xx.
ANGLE_FEATURE_CONDITION(features, initFragmentOutputVariables, IsAdreno42xOr3xx(functions));
// http://crbug.com/1144207
// The Mac bot with Intel Iris GPU seems unaffected by this bug. Exclude the Haswell family for
// now.
ANGLE_FEATURE_CONDITION(features, shiftInstancedArrayDataWithOffset,
IsApple() && IsIntel(vendor) && !IsHaswell(device));
ANGLE_FEATURE_CONDITION(features, syncVertexArraysToDefault,
!nativegl::SupportsVertexArrayObjects(functions));
// http://crbug.com/1181193
// On desktop Linux/AMD when using the amdgpu drivers, the precise kernel and DRM version are
// leaked via GL_RENDERER. We workaround this too improve user security.
ANGLE_FEATURE_CONDITION(features, sanitizeAMDGPURendererString, IsLinux() && hasAMD);
// http://crbug.com/1187513
// Imagination drivers are buggy with context switching. It needs to unbind fbo before context
// switching to workadround the driver issues.
ANGLE_FEATURE_CONDITION(features, unbindFBOBeforeSwitchingContext, IsPowerVR(vendor));
// http://crbug.com/1181068 and http://crbug.com/783979
ANGLE_FEATURE_CONDITION(features, flushOnFramebufferChange,
IsApple() && Has9thGenIntelGPU(systemInfo));
// Disable GL_EXT_multisampled_render_to_texture on a bunch of different configurations:
// http://crbug.com/490379
// http://crbug.com/767913
bool isAdreno4xxOnAndroidLessThan51 =
IsAndroid() && IsAdreno4xx(functions) && GetAndroidSdkLevel() < 22;
// http://crbug.com/612474
bool isAdreno4xxOnAndroid70 =
IsAndroid() && IsAdreno4xx(functions) && GetAndroidSdkLevel() == 24;
bool isAdreno5xxOnAndroidLessThan70 =
IsAndroid() && IsAdreno5xx(functions) && GetAndroidSdkLevel() < 24;
// http://crbug.com/663811
bool isAdreno5xxOnAndroid71 =
IsAndroid() && IsAdreno5xx(functions) && GetAndroidSdkLevel() == 25;
// http://crbug.com/594016
bool isLinuxVivante = IsLinux() && IsVivante(device);
// Temporarily disable on all of Android. http://crbug.com/1238327
ANGLE_FEATURE_CONDITION(features, disableMultisampledRenderToTexture,
isAdreno4xxOnAndroidLessThan51 || isAdreno4xxOnAndroid70 ||
isAdreno5xxOnAndroidLessThan70 || isAdreno5xxOnAndroid71 ||
isLinuxVivante);
// http://crbug.com/1181068
ANGLE_FEATURE_CONDITION(features, uploadTextureDataInChunks, IsApple());
// https://crbug.com/1060012
ANGLE_FEATURE_CONDITION(features, emulateImmutableCompressedTexture3D, isQualcomm);
// https://crbug.com/1300575
ANGLE_FEATURE_CONDITION(features, emulateRGB10, functions->standard == STANDARD_GL_DESKTOP);
// https://anglebug.com/5536
ANGLE_FEATURE_CONDITION(features, alwaysUnbindFramebufferTexture2D,
isNvidia && (IsWindows() || IsLinux()));
}
void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)
{
VendorID vendor = GetVendorID(functions);
bool isQualcomm = IsQualcomm(vendor);
ANGLE_FEATURE_CONDITION(features, disableProgramCachingForTransformFeedback,
IsAndroid() && isQualcomm);
// https://crbug.com/480992
// Disable shader program cache to workaround PowerVR Rogue issues.
ANGLE_FEATURE_CONDITION(features, disableProgramBinary, IsPowerVrRogue(functions));
}
void ReInitializeFeaturesAtGPUSwitch(const FunctionsGL *functions, angle::FeaturesGL *features)
{
angle::VendorID vendor;
angle::DeviceID device;
angle::SystemInfo systemInfo;
GetSystemInfoVendorIDAndDeviceID(functions, &systemInfo, &vendor, &device);
// http://crbug.com/1144207
// The Mac bot with Intel Iris GPU seems unaffected by this bug. Exclude the Haswell family for
// now.
// We need to reinitialize this feature when switching between buggy and non-buggy GPUs.
ANGLE_FEATURE_CONDITION(features, shiftInstancedArrayDataWithOffset,
IsApple() && IsIntel(vendor) && !IsHaswell(device));
}
} // namespace nativegl_gl
namespace nativegl
{
bool SupportsVertexArrayObjects(const FunctionsGL *functions)
{
return functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_OES_vertex_array_object") ||
functions->isAtLeastGL(gl::Version(3, 0)) ||
functions->hasGLExtension("GL_ARB_vertex_array_object");
}
bool CanUseDefaultVertexArrayObject(const FunctionsGL *functions)
{
return (functions->profile & GL_CONTEXT_CORE_PROFILE_BIT) == 0;
}
bool SupportsCompute(const FunctionsGL *functions)
{
// OpenGL 4.2 is required for GL_ARB_compute_shader, some platform drivers have the extension,
// but their maximum supported GL versions are less than 4.2. Explicitly limit the minimum
// GL version to 4.2.
return (functions->isAtLeastGL(gl::Version(4, 3)) ||
functions->isAtLeastGLES(gl::Version(3, 1)) ||
(functions->isAtLeastGL(gl::Version(4, 2)) &&
functions->hasGLExtension("GL_ARB_compute_shader") &&
functions->hasGLExtension("GL_ARB_shader_storage_buffer_object")));
}
bool SupportsFenceSync(const FunctionsGL *functions)
{
return functions->isAtLeastGL(gl::Version(3, 2)) || functions->hasGLExtension("GL_ARB_sync") ||
functions->isAtLeastGLES(gl::Version(3, 0));
}
bool SupportsOcclusionQueries(const FunctionsGL *functions)
{
return functions->isAtLeastGL(gl::Version(1, 5)) ||
functions->hasGLExtension("GL_ARB_occlusion_query2") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_occlusion_query_boolean");
}
bool SupportsNativeRendering(const FunctionsGL *functions,
gl::TextureType type,
GLenum internalFormat)
{
// Some desktop drivers allow rendering to formats that are not required by the spec, this is
// exposed through the GL_FRAMEBUFFER_RENDERABLE query.
bool hasInternalFormatQuery = functions->isAtLeastGL(gl::Version(4, 3)) ||
functions->hasGLExtension("GL_ARB_internalformat_query2");
// Some Intel drivers have a bug that returns GL_FULL_SUPPORT when asked if they support
// rendering to compressed texture formats yet return framebuffer incomplete when attempting to
// render to the format. Skip any native queries for compressed formats.
const gl::InternalFormat &internalFormatInfo = gl::GetSizedInternalFormatInfo(internalFormat);
if (hasInternalFormatQuery && !internalFormatInfo.compressed)
{
GLint framebufferRenderable = GL_NONE;
functions->getInternalformativ(ToGLenum(type), internalFormat, GL_FRAMEBUFFER_RENDERABLE, 1,
&framebufferRenderable);
return framebufferRenderable != GL_NONE;
}
else
{
const nativegl::InternalFormat &nativeInfo =
nativegl::GetInternalFormatInfo(internalFormat, functions->standard);
return nativegl_gl::MeetsRequirements(functions, nativeInfo.textureAttachment);
}
}
bool SupportsTexImage(gl::TextureType type)
{
switch (type)
{
// Multi-sample texture types only support TexStorage data upload
case gl::TextureType::_2DMultisample:
case gl::TextureType::_2DMultisampleArray:
return false;
default:
return true;
}
}
bool UseTexImage2D(gl::TextureType textureType)
{
return textureType == gl::TextureType::_2D || textureType == gl::TextureType::CubeMap ||
textureType == gl::TextureType::Rectangle ||
textureType == gl::TextureType::_2DMultisample ||
textureType == gl::TextureType::External || textureType == gl::TextureType::VideoImage;
}
bool UseTexImage3D(gl::TextureType textureType)
{
return textureType == gl::TextureType::_2DArray || textureType == gl::TextureType::_3D ||
textureType == gl::TextureType::_2DMultisampleArray ||
textureType == gl::TextureType::CubeMapArray;
}
GLenum GetTextureBindingQuery(gl::TextureType textureType)
{
switch (textureType)
{
case gl::TextureType::_2D:
return GL_TEXTURE_BINDING_2D;
case gl::TextureType::_2DArray:
return GL_TEXTURE_BINDING_2D_ARRAY;
case gl::TextureType::_2DMultisample:
return GL_TEXTURE_BINDING_2D_MULTISAMPLE;
case gl::TextureType::_2DMultisampleArray:
return GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY;
case gl::TextureType::_3D:
return GL_TEXTURE_BINDING_3D;
case gl::TextureType::External:
return GL_TEXTURE_BINDING_EXTERNAL_OES;
case gl::TextureType::Rectangle:
return GL_TEXTURE_BINDING_RECTANGLE;
case gl::TextureType::CubeMap:
return GL_TEXTURE_BINDING_CUBE_MAP;
case gl::TextureType::CubeMapArray:
return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_OES;
case gl::TextureType::Buffer:
return GL_TEXTURE_BINDING_BUFFER;
default:
UNREACHABLE();
return 0;
}
}
GLenum GetTextureBindingTarget(gl::TextureType textureType)
{
return ToGLenum(GetNativeTextureType(textureType));
}
GLenum GetTextureBindingTarget(gl::TextureTarget textureTarget)
{
return ToGLenum(GetNativeTextureTarget(textureTarget));
}
GLenum GetBufferBindingQuery(gl::BufferBinding bufferBinding)
{
switch (bufferBinding)
{
case gl::BufferBinding::Array:
return GL_ARRAY_BUFFER_BINDING;
case gl::BufferBinding::AtomicCounter:
return GL_ATOMIC_COUNTER_BUFFER_BINDING;
case gl::BufferBinding::CopyRead:
return GL_COPY_READ_BUFFER_BINDING;
case gl::BufferBinding::CopyWrite:
return GL_COPY_WRITE_BUFFER_BINDING;
case gl::BufferBinding::DispatchIndirect:
return GL_DISPATCH_INDIRECT_BUFFER_BINDING;
case gl::BufferBinding::DrawIndirect:
return GL_DRAW_INDIRECT_BUFFER_BINDING;
case gl::BufferBinding::ElementArray:
return GL_ELEMENT_ARRAY_BUFFER_BINDING;
case gl::BufferBinding::PixelPack:
return GL_PIXEL_PACK_BUFFER_BINDING;
case gl::BufferBinding::PixelUnpack:
return GL_PIXEL_UNPACK_BUFFER_BINDING;
case gl::BufferBinding::ShaderStorage:
return GL_SHADER_STORAGE_BUFFER_BINDING;
case gl::BufferBinding::TransformFeedback:
return GL_TRANSFORM_FEEDBACK_BUFFER_BINDING;
case gl::BufferBinding::Uniform:
return GL_UNIFORM_BUFFER_BINDING;
case gl::BufferBinding::Texture:
return GL_TEXTURE_BUFFER_BINDING;
default:
UNREACHABLE();
return 0;
}
}
std::string GetBufferBindingString(gl::BufferBinding bufferBinding)
{
std::ostringstream os;
os << bufferBinding << "_BINDING";
return os.str();
}
gl::TextureType GetNativeTextureType(gl::TextureType type)
{
// VideoImage texture type is a WebGL type. It doesn't have
// directly mapping type in native OpenGL/OpenGLES.
// Actually, it will be translated to different texture type
// (TEXTURE2D, TEXTURE_EXTERNAL_OES and TEXTURE_RECTANGLE)
// based on OS and other conditions.
// This will introduce problem that binding VideoImage may
// unbind native image implicitly. Please make sure state
// manager is aware of this implicit unbind behaviour.
if (type != gl::TextureType::VideoImage)
{
return type;
}
// TODO(http://anglebug.com/3889): need to figure out rectangle texture and
// external image when these backend are implemented.
return gl::TextureType::_2D;
}
gl::TextureTarget GetNativeTextureTarget(gl::TextureTarget target)
{
// VideoImage texture type is a WebGL type. It doesn't have
// directly mapping type in native OpenGL/OpenGLES.
// Actually, it will be translated to different texture target
// (TEXTURE2D, TEXTURE_EXTERNAL_OES and TEXTURE_RECTANGLE)
// based on OS and other conditions.
// This will introduce problem that binding VideoImage may
// unbind native image implicitly. Please make sure state
// manager is aware of this implicit unbind behaviour.
if (target != gl::TextureTarget::VideoImage)
{
return target;
}
// TODO(http://anglebug.com/3889): need to figure out rectangle texture and
// external image when these backend are implemented.
return gl::TextureTarget::_2D;
}
} // namespace nativegl
const FunctionsGL *GetFunctionsGL(const gl::Context *context)
{
return GetImplAs<ContextGL>(context)->getFunctions();
}
StateManagerGL *GetStateManagerGL(const gl::Context *context)
{
return GetImplAs<ContextGL>(context)->getStateManager();
}
BlitGL *GetBlitGL(const gl::Context *context)
{
return GetImplAs<ContextGL>(context)->getBlitter();
}
ClearMultiviewGL *GetMultiviewClearer(const gl::Context *context)
{
return GetImplAs<ContextGL>(context)->getMultiviewClearer();
}
const angle::FeaturesGL &GetFeaturesGL(const gl::Context *context)
{
return GetImplAs<ContextGL>(context)->getFeaturesGL();
}
void ClearErrors(const gl::Context *context,
const char *file,
const char *function,
unsigned int line)
{
const FunctionsGL *functions = GetFunctionsGL(context);
ClearErrors(functions, file, function, line);
}
angle::Result CheckError(const gl::Context *context,
const char *call,
const char *file,
const char *function,
unsigned int line)
{
const FunctionsGL *functions = GetFunctionsGL(context);
GLenum error = functions->getError();
if (ANGLE_UNLIKELY(error != GL_NO_ERROR))
{
ContextGL *contextGL = GetImplAs<ContextGL>(context);
contextGL->handleError(error, "Unexpected driver error.", file, function, line);
ERR() << "GL call " << call << " generated error " << gl::FmtHex(error) << " in " << file
<< ", " << function << ":" << line << ". ";
// Check that only one GL error was generated, ClearErrors should have been called first.
// Skip GL_CONTEXT_LOST errors, they will be generated continuously and result in an
// infinite loop.
GLenum nextError = functions->getError();
while (nextError != GL_NO_ERROR && nextError != GL_CONTEXT_LOST)
{
ERR() << "Additional GL error " << gl::FmtHex(nextError) << " generated.";
nextError = functions->getError();
}
return angle::Result::Stop;
}
return angle::Result::Continue;
}
bool CanMapBufferForRead(const FunctionsGL *functions)
{
return (functions->mapBufferRange != nullptr) ||
(functions->mapBuffer != nullptr && functions->standard == STANDARD_GL_DESKTOP);
}
uint8_t *MapBufferRangeWithFallback(const FunctionsGL *functions,
GLenum target,
size_t offset,
size_t length,
GLbitfield access)
{
if (functions->mapBufferRange != nullptr)
{
return static_cast<uint8_t *>(functions->mapBufferRange(target, offset, length, access));
}
else if (functions->mapBuffer != nullptr &&
(functions->standard == STANDARD_GL_DESKTOP || access == GL_MAP_WRITE_BIT))
{
// Only the read and write bits are supported
ASSERT((access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) != 0);
GLenum accessEnum = 0;
if (access == (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT))
{
accessEnum = GL_READ_WRITE;
}
else if (access == GL_MAP_READ_BIT)
{
accessEnum = GL_READ_ONLY;
}
else if (access == GL_MAP_WRITE_BIT)
{
accessEnum = GL_WRITE_ONLY;
}
else
{
UNREACHABLE();
return nullptr;
}
return static_cast<uint8_t *>(functions->mapBuffer(target, accessEnum)) + offset;
}
else
{
// No options available
UNREACHABLE();
return nullptr;
}
}
angle::Result ShouldApplyLastRowPaddingWorkaround(ContextGL *contextGL,
const gl::Extents &size,
const gl::PixelStoreStateBase &state,
const gl::Buffer *pixelBuffer,
GLenum format,
GLenum type,
bool is3D,
const void *pixels,
bool *shouldApplyOut)
{
if (pixelBuffer == nullptr)
{
*shouldApplyOut = false;
return angle::Result::Continue;
}
// We are using an pack or unpack buffer, compute what the driver thinks is going to be the
// last byte read or written. If it is past the end of the buffer, we will need to use the
// workaround otherwise the driver will generate INVALID_OPERATION and not do the operation.
const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type);
GLuint endByte = 0;
ANGLE_CHECK_GL_MATH(contextGL,
glFormat.computePackUnpackEndByte(type, size, state, is3D, &endByte));
GLuint rowPitch = 0;
ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeRowPitch(type, size.width, state.alignment,
state.rowLength, &rowPitch));
CheckedNumeric<size_t> checkedPixelBytes = glFormat.computePixelBytes(type);
CheckedNumeric<size_t> checkedEndByte =
angle::CheckedNumeric<size_t>(endByte) + reinterpret_cast<intptr_t>(pixels);
// At this point checkedEndByte is the actual last byte read.
// The driver adds an extra row padding (if any), mimic it.
ANGLE_CHECK_GL_MATH(contextGL, checkedPixelBytes.IsValid());
if (static_cast<size_t>(checkedPixelBytes.ValueOrDie()) * size.width < rowPitch)
{
checkedEndByte += rowPitch - checkedPixelBytes * size.width;
}
ANGLE_CHECK_GL_MATH(contextGL, checkedEndByte.IsValid());
*shouldApplyOut = checkedEndByte.ValueOrDie() > static_cast<size_t>(pixelBuffer->getSize());
return angle::Result::Continue;
}
std::vector<ContextCreationTry> GenerateContextCreationToTry(EGLint requestedType, bool isMesaGLX)
{
using Type = ContextCreationTry::Type;
constexpr EGLint kPlatformOpenGL = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
constexpr EGLint kPlatformOpenGLES = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
std::vector<ContextCreationTry> contextsToTry;
if (requestedType == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE || requestedType == kPlatformOpenGL)
{
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(4, 5));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(4, 4));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(4, 3));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(4, 2));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(4, 1));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(4, 0));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(3, 3));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_CORE, gl::Version(3, 2));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(3, 3));
// On Mesa, do not try to create OpenGL context versions between 3.0 and
// 3.2 because of compatibility problems. See crbug.com/659030
if (!isMesaGLX)
{
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(3, 2));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(3, 1));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(3, 0));
}
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(2, 1));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(2, 0));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(1, 5));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(1, 4));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(1, 3));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(1, 2));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(1, 1));
contextsToTry.emplace_back(kPlatformOpenGL, Type::DESKTOP_LEGACY, gl::Version(1, 0));
}
if (requestedType == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE ||
requestedType == kPlatformOpenGLES)
{
contextsToTry.emplace_back(kPlatformOpenGLES, Type::ES, gl::Version(3, 2));
contextsToTry.emplace_back(kPlatformOpenGLES, Type::ES, gl::Version(3, 1));
contextsToTry.emplace_back(kPlatformOpenGLES, Type::ES, gl::Version(3, 0));
contextsToTry.emplace_back(kPlatformOpenGLES, Type::ES, gl::Version(2, 0));
}
return contextsToTry;
}
std::string GetRendererString(const FunctionsGL *functions)
{
return GetString(functions, GL_RENDERER);
}
std::string GetVendorString(const FunctionsGL *functions)
{
return GetString(functions, GL_VENDOR);
}
std::string GetVersionString(const FunctionsGL *functions)
{
return GetString(functions, GL_VERSION);
}
} // namespace rx