/* -*- 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). */ }, { "get_string_indexed", GLVersion::GL3, GLESVersion::ES3, GLContext::Extension_None, { GLContext::Extensions_End } // glGetStringi }, { "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::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 } }, { "robustness", GLVersion::NONE, GLESVersion::NONE, GLContext::Extension_None, { GLContext::ARB_robustness, GLContext::EXT_robustness, GLContext::Extensions_End } }, { "sRGB_framebuffer", GLVersion::GL3, GLESVersion::ES3, GLContext::ARB_framebuffer_sRGB, { GLContext::EXT_framebuffer_sRGB, GLContext::EXT_sRGB_write_control, GLContext::Extensions_End } }, { "sRGB_texture", GLVersion::GL2_1, GLESVersion::ES3, GLContext::Extension_None, { GLContext::EXT_sRGB, GLContext::EXT_texture_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::NONE, GLESVersion::NONE, 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_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 */