gecko-dev/gfx/gl/GLContextFeatures.cpp
Barret Rennie b09ba3de12 Bug 1457546 - Use glMapBufferRange instead of glMapBuffer to capture screenshots r=jgilbert
It was pointed out in a review by jgilbert that glMapBuffer only supports
writing to the mapped range on an OpenGL ES profile and using it to read is
undefined behaviour. We now use glMapBufferRange when available, which does
support reading on both OpenGL and OpenGL ES profiles, and allows capturing
screenshots on Android. When it is not available, we fall back to glMapBuffer
(e.g., for macOS).

Differential Revision: https://phabricator.services.mozilla.com/D12341

--HG--
extra : moz-landing-system : lando
2018-11-20 18:32:10 +00:00

943 lines
25 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GLContext.h"
#include "nsPrintfCString.h"
namespace mozilla {
namespace gl {
const size_t kMAX_EXTENSION_GROUP_SIZE = 5;
enum class GLVersion : uint32_t {
NONE = 0, // Feature is not supported natively by GL
GL1_2 = 120,
GL1_3 = 130,
GL2 = 200,
GL2_1 = 210,
GL3 = 300,
GL3_1 = 310,
GL3_2 = 320,
GL3_3 = 330,
GL4 = 400,
GL4_1 = 410,
GL4_2 = 420,
GL4_3 = 430,
};
enum class GLESVersion : uint32_t {
NONE = 0, // Feature is not support natively by GL ES
ES2 = 200,
ES3 = 300,
ES3_1 = 310,
ES3_2 = 320,
};
// ARB_ES2_compatibility is natively supported in OpenGL 4.1.
static const GLVersion kGLCoreVersionForES2Compat = GLVersion::GL4_1;
// ARB_ES3_compatibility is natively supported in OpenGL 4.3.
static const GLVersion kGLCoreVersionForES3Compat = GLVersion::GL4_3;
struct FeatureInfo
{
const char* mName;
/* The (desktop) OpenGL version that provides this feature */
GLVersion mOpenGLVersion;
/* The OpenGL ES version that provides this feature */
GLESVersion mOpenGLESVersion;
/* If there is an ARB extension, and its function symbols are
* not decorated with an ARB suffix, then its extension ID should go
* here, and NOT in mExtensions. For example, ARB_vertex_array_object
* functions do not have an ARB suffix, because it is an extension that
* was created to match core GL functionality and will never differ.
* Some ARB extensions do have a suffix, if they were created before
* a core version of the functionality existed.
*
* If there is no such ARB extension, pass 0 (GLContext::Extension_None)
*/
GLContext::GLExtensions mARBExtensionWithoutARBSuffix;
/* Extensions that also provide this feature */
GLContext::GLExtensions mExtensions[kMAX_EXTENSION_GROUP_SIZE];
};
static const FeatureInfo sFeatureInfoArr[] = {
{
"bind_buffer_offset",
GLVersion::NONE,
GLESVersion::NONE,
GLContext::Extension_None,
{
GLContext::EXT_transform_feedback,
GLContext::NV_transform_feedback2,
GLContext::Extensions_End
}
},
{
"blend_minmax",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_blend_minmax,
GLContext::Extensions_End
}
},
{
"clear_buffers",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::Extensions_End
}
},
{
"copy_buffer",
GLVersion::GL3_1,
GLESVersion::ES3,
GLContext::ARB_copy_buffer,
{
GLContext::Extensions_End
}
},
{
"depth_texture",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_depth_texture,
GLContext::OES_depth_texture,
// Intentionally avoid putting ANGLE_depth_texture here,
// it does not offer quite the same functionality.
GLContext::Extensions_End
}
},
{
"draw_buffers",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_draw_buffers,
GLContext::EXT_draw_buffers,
GLContext::Extensions_End
}
},
{
"draw_instanced",
GLVersion::GL3_1,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_draw_instanced,
GLContext::EXT_draw_instanced,
GLContext::NV_draw_instanced,
GLContext::ANGLE_instanced_arrays,
GLContext::Extensions_End
}
},
{
"draw_range_elements",
GLVersion::GL1_2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_draw_range_elements,
GLContext::Extensions_End
}
},
{
"element_index_uint",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::OES_element_index_uint,
GLContext::Extensions_End
}
},
{
"ES2_compatibility",
kGLCoreVersionForES2Compat,
GLESVersion::ES2, // OpenGL ES version
GLContext::ARB_ES2_compatibility, // no suffix on ARB extension
{
GLContext::Extensions_End
}
},
{
"ES3_compatibility",
kGLCoreVersionForES3Compat,
GLESVersion::ES3, // OpenGL ES version
GLContext::ARB_ES3_compatibility, // no suffix on ARB extension
{
GLContext::Extensions_End
}
},
{
"EXT_color_buffer_float",
GLVersion::GL3,
GLESVersion::ES3_2,
GLContext::Extension_None,
{
GLContext::EXT_color_buffer_float,
GLContext::Extensions_End
}
},
{
// Removes clamping for float color outputs from frag shaders.
"frag_color_float",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_color_buffer_float,
GLContext::EXT_color_buffer_float,
GLContext::EXT_color_buffer_half_float,
GLContext::Extensions_End
}
},
{
"frag_depth",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_frag_depth,
GLContext::Extensions_End
}
},
{
// Check for just the blit framebuffer blit part of
// ARB_framebuffer_object
"framebuffer_blit",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_framebuffer_object,
{
GLContext::ANGLE_framebuffer_blit,
GLContext::EXT_framebuffer_blit,
GLContext::NV_framebuffer_blit,
GLContext::Extensions_End
}
},
{
// Check for just the multisample renderbuffer part of
// ARB_framebuffer_object
"framebuffer_multisample",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_framebuffer_object,
{
GLContext::ANGLE_framebuffer_multisample,
GLContext::APPLE_framebuffer_multisample,
GLContext::EXT_framebuffer_multisample,
GLContext::EXT_multisampled_render_to_texture,
GLContext::Extensions_End
}
},
{
// ARB_framebuffer_object support
"framebuffer_object",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_framebuffer_object,
{
GLContext::Extensions_End
}
},
{
// EXT_framebuffer_object/OES_framebuffer_object support
"framebuffer_object_EXT_OES",
GLVersion::GL3,
GLESVersion::ES2,
GLContext::Extension_None,
{
GLContext::EXT_framebuffer_object,
GLContext::OES_framebuffer_object,
GLContext::Extensions_End
}
},
{
"get_integer_indexed",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_draw_buffers2,
GLContext::Extensions_End
}
},
{
"get_integer64_indexed",
GLVersion::GL3_2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::Extensions_End
}
},
{
"get_query_object_i64v",
GLVersion::GL3_3,
GLESVersion::NONE,
GLContext::ARB_timer_query,
{
GLContext::ANGLE_timer_query,
GLContext::EXT_disjoint_timer_query,
GLContext::EXT_timer_query,
GLContext::Extensions_End
}
},
{
"get_query_object_iv",
GLVersion::GL2,
GLESVersion::NONE,
GLContext::Extension_None,
{
GLContext::Extensions_End
}
/*
* XXX_get_query_object_iv only provide GetQueryObjectiv provided by
* ARB_occlusion_query (added by OpenGL 2.0).
*/
},
{
"gpu_shader4",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_gpu_shader4,
GLContext::Extensions_End
}
},
{
"instanced_arrays",
GLVersion::GL3_3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_instanced_arrays,
GLContext::NV_instanced_arrays,
GLContext::ANGLE_instanced_arrays,
GLContext::Extensions_End
}
},
{
"instanced_non_arrays",
GLVersion::GL3_3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_instanced_arrays,
GLContext::Extensions_End
}
/* This is an expanded version of `instanced_arrays` that allows for all
* enabled active attrib arrays to have non-zero divisors.
* ANGLE_instanced_arrays and NV_instanced_arrays forbid this, but GLES3
* has no such restriction.
*/
},
{
"internalformat_query",
GLVersion::GL4_2,
GLESVersion::ES3,
GLContext::ARB_internalformat_query,
{
GLContext::Extensions_End
}
},
{
"invalidate_framebuffer",
GLVersion::GL4_3,
GLESVersion::ES3,
GLContext::ARB_invalidate_subdata,
{
GLContext::Extensions_End
}
},
{
"map_buffer_range",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_map_buffer_range,
{
GLContext::EXT_map_buffer_range,
GLContext::Extensions_End
}
},
{
"occlusion_query",
GLVersion::GL2,
GLESVersion::NONE,
GLContext::Extension_None,
{
GLContext::Extensions_End
}
// XXX_occlusion_query depend on ARB_occlusion_query (added in OpenGL 2.0)
},
{
"occlusion_query_boolean",
kGLCoreVersionForES3Compat,
GLESVersion::ES3,
GLContext::ARB_ES3_compatibility,
{
GLContext::EXT_occlusion_query_boolean,
GLContext::Extensions_End
}
/*
* XXX_occlusion_query_boolean provide ANY_SAMPLES_PASSED_CONSERVATIVE,
* but EXT_occlusion_query_boolean is only a OpenGL ES extension. But
* it is supported on desktop if ARB_ES3_compatibility because
* EXT_occlusion_query_boolean (added in OpenGL ES 3.0).
*/
},
{
"occlusion_query2",
GLVersion::GL3_3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_occlusion_query2,
GLContext::ARB_ES3_compatibility,
GLContext::EXT_occlusion_query_boolean,
GLContext::Extensions_End
}
/*
* XXX_occlusion_query2 (add in OpenGL 3.3) provide ANY_SAMPLES_PASSED,
* which is provided by ARB_occlusion_query2, EXT_occlusion_query_boolean
* (added in OpenGL ES 3.0) and ARB_ES3_compatibility
*/
},
{
"packed_depth_stencil",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_packed_depth_stencil,
GLContext::OES_packed_depth_stencil,
GLContext::Extensions_End
}
},
{
"prim_restart",
GLVersion::GL3_1,
GLESVersion::NONE,
GLContext::Extension_None,
{
//GLContext::NV_primitive_restart, // Has different enum values.
GLContext::Extensions_End
}
},
{
"prim_restart_fixed",
kGLCoreVersionForES3Compat,
GLESVersion::ES3,
GLContext::ARB_ES3_compatibility,
{
GLContext::Extensions_End
}
},
{
"query_counter",
GLVersion::GL3_3,
GLESVersion::NONE,
GLContext::ARB_timer_query,
{
GLContext::ANGLE_timer_query,
GLContext::EXT_disjoint_timer_query,
// EXT_timer_query does NOT support GL_TIMESTAMP retrieval with
// QueryCounter.
GLContext::Extensions_End
}
},
{
"query_objects",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ANGLE_timer_query,
GLContext::EXT_disjoint_timer_query,
GLContext::EXT_occlusion_query_boolean,
GLContext::Extensions_End
}
/*
* XXX_query_objects only provide entry points commonly supported by
* ARB_occlusion_query (added in OpenGL 2.0), EXT_occlusion_query_boolean
* (added in OpenGL ES 3.0), and ARB_timer_query (added in OpenGL 3.3)
*/
},
{
"query_time_elapsed",
GLVersion::GL3_3,
GLESVersion::NONE,
GLContext::ARB_timer_query,
{
GLContext::ANGLE_timer_query,
GLContext::EXT_disjoint_timer_query,
GLContext::EXT_timer_query,
GLContext::Extensions_End
}
},
{
"read_buffer",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::Extensions_End
}
},
{
"renderbuffer_color_float",
GLVersion::GL3,
GLESVersion::ES3_2,
GLContext::Extension_None,
{
GLContext::ARB_texture_float,
GLContext::EXT_color_buffer_float,
GLContext::Extensions_End
}
},
{
"renderbuffer_color_half_float",
GLVersion::GL3,
GLESVersion::ES3_2,
GLContext::Extension_None,
{
GLContext::ARB_texture_float,
GLContext::EXT_color_buffer_float,
GLContext::EXT_color_buffer_half_float,
GLContext::Extensions_End
}
},
{
"robust_buffer_access_behavior",
GLVersion::NONE,
GLESVersion::NONE,
GLContext::Extension_None,
{
GLContext::ARB_robust_buffer_access_behavior,
GLContext::KHR_robust_buffer_access_behavior,
GLContext::Extensions_End
}
},
{
"robustness",
GLVersion::NONE,
GLESVersion::NONE,
GLContext::Extension_None,
{
GLContext::ARB_robustness,
GLContext::EXT_robustness,
GLContext::KHR_robustness,
GLContext::Extensions_End
}
},
{
"sRGB",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_framebuffer_sRGB,
{
GLContext::EXT_sRGB,
GLContext::EXT_framebuffer_sRGB,
GLContext::Extensions_End
}
},
{
"sampler_objects",
GLVersion::GL3_3,
GLESVersion::ES3,
GLContext::ARB_sampler_objects,
{
GLContext::Extensions_End
}
},
{
"seamless_cube_map_opt_in",
GLVersion::GL3_2,
GLESVersion::NONE,
GLContext::ARB_seamless_cube_map,
{
GLContext::Extensions_End
}
},
{
"shader_texture_lod",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_shader_texture_lod,
GLContext::EXT_shader_texture_lod,
GLContext::Extensions_End
}
},
{
// Do we have separate DRAW and READ framebuffer bind points?
"split_framebuffer",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_framebuffer_object,
{
GLContext::ANGLE_framebuffer_blit,
GLContext::APPLE_framebuffer_multisample,
GLContext::EXT_framebuffer_blit,
GLContext::NV_framebuffer_blit,
GLContext::Extensions_End
}
},
{
"standard_derivatives",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::OES_standard_derivatives,
GLContext::Extensions_End
}
},
{
"sync",
GLVersion::GL3_2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_sync,
GLContext::APPLE_sync,
GLContext::Extensions_End
}
},
{
"texture_3D",
GLVersion::GL1_2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_texture3D,
GLContext::OES_texture_3D,
GLContext::Extensions_End
}
},
{
"texture_3D_compressed",
GLVersion::GL1_3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_texture_compression,
GLContext::OES_texture_3D,
GLContext::Extensions_End
}
},
{
"texture_3D_copy",
GLVersion::GL1_2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::EXT_copy_texture,
GLContext::OES_texture_3D,
GLContext::Extensions_End
}
},
{
"texture_compression_bptc",
GLVersion::GL4_2,
GLESVersion::NONE,
GLContext::Extension_None,
{
GLContext::ARB_texture_compression_bptc,
GLContext::EXT_texture_compression_bptc,
GLContext::Extensions_End
}
},
{
"texture_compression_rgtc",
GLVersion::GL3,
GLESVersion::NONE,
GLContext::Extension_None,
{
GLContext::ARB_texture_compression_rgtc,
GLContext::EXT_texture_compression_rgtc,
GLContext::Extensions_End
}
},
{
"texture_float",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_texture_float,
GLContext::OES_texture_float,
GLContext::Extensions_End
}
},
{
"texture_float_linear",
GLVersion::GL3_1,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_texture_float,
GLContext::OES_texture_float_linear,
GLContext::Extensions_End
}
},
{
"texture_half_float",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_half_float_pixel,
GLContext::ARB_texture_float,
GLContext::NV_half_float,
GLContext::Extensions_End
}
/**
* We are not including OES_texture_half_float in this feature, because:
* GL_HALF_FLOAT = 0x140B
* GL_HALF_FLOAT_ARB = 0x140B == GL_HALF_FLOAT
* GL_HALF_FLOAT_NV = 0x140B == GL_HALF_FLOAT
* GL_HALF_FLOAT_OES = 0x8D61 != GL_HALF_FLOAT
* WebGL handles this specifically with an OES_texture_half_float check.
*/
},
{
"texture_half_float_linear",
GLVersion::GL3_1,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_half_float_pixel,
GLContext::ARB_texture_float,
GLContext::NV_half_float,
GLContext::OES_texture_half_float_linear,
GLContext::Extensions_End
}
},
{
"texture_non_power_of_two",
GLVersion::GL2,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::ARB_texture_non_power_of_two,
GLContext::OES_texture_npot,
GLContext::Extensions_End
}
},
{
"texture_rg",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_texture_rg,
{
GLContext::Extensions_End
}
},
{
"texture_storage",
GLVersion::GL4_2,
GLESVersion::ES3,
GLContext::ARB_texture_storage,
{
/*
* Not including GL_EXT_texture_storage here because it
* doesn't guarantee glTexStorage3D, which is required for
* WebGL 2.
*/
GLContext::Extensions_End
}
},
{
"texture_swizzle",
GLVersion::GL3_3,
GLESVersion::ES3,
GLContext::ARB_texture_swizzle,
{
GLContext::Extensions_End
}
},
{
"transform_feedback2",
GLVersion::GL4,
GLESVersion::ES3,
GLContext::ARB_transform_feedback2,
{
GLContext::NV_transform_feedback2,
GLContext::Extensions_End
}
},
{
"uniform_buffer_object",
GLVersion::GL3_1,
GLESVersion::ES3,
GLContext::ARB_uniform_buffer_object,
{
GLContext::Extensions_End
}
},
{
"uniform_matrix_nonsquare",
GLVersion::GL2_1,
GLESVersion::ES3,
GLContext::Extension_None,
{
GLContext::Extensions_End
}
},
{
"vertex_array_object",
GLVersion::GL3,
GLESVersion::ES3,
GLContext::ARB_vertex_array_object, // ARB extension
{
GLContext::OES_vertex_array_object,
GLContext::APPLE_vertex_array_object,
GLContext::Extensions_End
}
}
};
static inline const FeatureInfo&
GetFeatureInfo(GLFeature feature)
{
static_assert(MOZ_ARRAY_LENGTH(sFeatureInfoArr) == size_t(GLFeature::EnumMax),
"Mismatched lengths for sFeatureInfoInfos and GLFeature enums");
MOZ_ASSERT(feature < GLFeature::EnumMax,
"GLContext::GetFeatureInfoInfo : unknown <feature>");
return sFeatureInfoArr[size_t(feature)];
}
static inline uint32_t
ProfileVersionForFeature(GLFeature feature, ContextProfile profile)
{
MOZ_ASSERT(profile != ContextProfile::Unknown,
"GLContext::ProfileVersionForFeature : unknown <profile>");
const FeatureInfo& featureInfo = GetFeatureInfo(feature);
if (profile == ContextProfile::OpenGLES)
return (uint32_t)featureInfo.mOpenGLESVersion;
return (uint32_t)featureInfo.mOpenGLVersion;
}
bool
IsFeaturePartOfProfileVersion(GLFeature feature,
ContextProfile profile, unsigned int version)
{
unsigned int profileVersion = ProfileVersionForFeature(feature, profile);
/**
* if `profileVersion` is zero, it means that no version of the profile
* added support for the feature.
*/
return profileVersion && version >= profileVersion;
}
bool
GLContext::IsFeatureProvidedByCoreSymbols(GLFeature feature)
{
if (IsFeaturePartOfProfileVersion(feature, mProfile, mVersion))
return true;
if (IsExtensionSupported(GetFeatureInfo(feature).mARBExtensionWithoutARBSuffix))
return true;
return false;
}
const char*
GLContext::GetFeatureName(GLFeature feature)
{
return GetFeatureInfo(feature).mName;
}
void
GLContext::InitFeatures()
{
for (size_t featureId = 0; featureId < size_t(GLFeature::EnumMax); featureId++) {
GLFeature feature = GLFeature(featureId);
if (IsFeaturePartOfProfileVersion(feature, mProfile, mVersion)) {
mAvailableFeatures[featureId] = true;
continue;
}
mAvailableFeatures[featureId] = false;
const FeatureInfo& featureInfo = GetFeatureInfo(feature);
if (IsExtensionSupported(featureInfo.mARBExtensionWithoutARBSuffix)) {
mAvailableFeatures[featureId] = true;
continue;
}
for (size_t j = 0; true; j++) {
MOZ_ASSERT(j < kMAX_EXTENSION_GROUP_SIZE,
"kMAX_EXTENSION_GROUP_SIZE too small");
if (featureInfo.mExtensions[j] == GLContext::Extensions_End)
break;
if (IsExtensionSupported(featureInfo.mExtensions[j])) {
mAvailableFeatures[featureId] = true;
break;
}
}
}
if (ShouldDumpExts()) {
for (size_t featureId = 0; featureId < size_t(GLFeature::EnumMax); featureId++) {
GLFeature feature = GLFeature(featureId);
printf_stderr("[%s] Feature::%s\n",
IsSupported(feature) ? "enabled" : "disabled",
GetFeatureName(feature));
}
}
}
void
GLContext::MarkUnsupported(GLFeature feature)
{
mAvailableFeatures[size_t(feature)] = false;
const FeatureInfo& featureInfo = GetFeatureInfo(feature);
for (size_t i = 0; true; i++) {
MOZ_ASSERT(i < kMAX_EXTENSION_GROUP_SIZE, "kMAX_EXTENSION_GROUP_SIZE too small");
if (featureInfo.mExtensions[i] == GLContext::Extensions_End)
break;
MarkExtensionUnsupported(featureInfo.mExtensions[i]);
}
MOZ_ASSERT(!IsSupported(feature), "GLContext::MarkUnsupported has failed!");
NS_WARNING(nsPrintfCString("%s marked as unsupported",
GetFeatureName(feature)).get());
}
} /* namespace gl */
} /* namespace mozilla */