// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <set> #include <string> #include "gpu/command_buffer/service/feature_info.h" #include "gpu/command_buffer/service/gl_utils.h" #include "ui/gfx/gl/gl_implementation.h" namespace gpu { namespace gles2 { FeatureInfo::FeatureInfo() { } FeatureInfo::~FeatureInfo() { } // Helps query for extensions. class ExtensionHelper { public: ExtensionHelper(const char* extensions, const char* desired_features) : desire_all_features_(false) { // Check for "*" if (desired_features && desired_features[0] == '*' && desired_features[1] == '\0') { desired_features = NULL; } InitStringSet(extensions, &have_extensions_); InitStringSet(desired_features, &desired_extensions_); if (!desired_features) { desire_all_features_ = true; } } // Returns true if extension exists. bool Have(const char* extension) { return have_extensions_.find(extension) != have_extensions_.end(); } // Returns true of an extension is desired. It may not exist. bool Desire(const char* extension) { return desire_all_features_ || desired_extensions_.find(extension) != desired_extensions_.end(); } // Returns true if an extension exists and is desired. bool HaveAndDesire(const char* extension) { return Have(extension) && Desire(extension); } private: void InitStringSet(const char* s, std::set<std::string>* string_set) { std::string str(s ? s : ""); std::string::size_type lastPos = 0; while (true) { std::string::size_type pos = str.find_first_of(" ", lastPos); if (pos != std::string::npos) { if (pos - lastPos) { string_set->insert(str.substr(lastPos, pos - lastPos)); } lastPos = pos + 1; } else { string_set->insert(str.substr(lastPos)); break; } } } bool desire_all_features_; // Extensions that exist. std::set<std::string> have_extensions_; // Extensions that are desired but may not exist. std::set<std::string> desired_extensions_; }; bool FeatureInfo::Initialize(const char* allowed_features) { disallowed_features_ = DisallowedFeatures(); AddFeatures(allowed_features); return true; } bool FeatureInfo::Initialize(const DisallowedFeatures& disallowed_features, const char* allowed_features) { disallowed_features_ = disallowed_features; AddFeatures(allowed_features); return true; } void FeatureInfo::AddFeatures(const char* desired_features) { // Figure out what extensions to turn on. ExtensionHelper ext( reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)), desired_features); bool npot_ok = false; AddExtensionString("GL_CHROMIUM_resource_safe"); AddExtensionString("GL_CHROMIUM_resize"); AddExtensionString("GL_CHROMIUM_strict_attribs"); AddExtensionString("GL_CHROMIUM_swapbuffers_complete_callback"); AddExtensionString("GL_CHROMIUM_rate_limit_offscreen_context"); AddExtensionString("GL_ANGLE_translated_shader_source"); // Only turn this feature on if it is requested. Not by default. if (desired_features && ext.Desire("GL_CHROMIUM_webglsl")) { AddExtensionString("GL_CHROMIUM_webglsl"); feature_flags_.chromium_webglsl = true; } // Check if we should allow GL_EXT_texture_compression_dxt1 and // GL_EXT_texture_compression_s3tc. bool enable_dxt1 = false; bool enable_dxt3 = false; bool enable_dxt5 = false; bool have_s3tc = ext.Have("GL_EXT_texture_compression_s3tc"); bool have_dxt3 = have_s3tc || ext.Have("GL_ANGLE_texture_compression_dxt3"); bool have_dxt5 = have_s3tc || ext.Have("GL_ANGLE_texture_compression_dxt5"); if (ext.Desire("GL_EXT_texture_compression_dxt1") && (ext.Have("GL_EXT_texture_compression_dxt1") || have_s3tc)) { enable_dxt1 = true; } if (have_dxt3 && ext.Desire("GL_CHROMIUM_texture_compression_dxt3")) { enable_dxt3 = true; } if (have_dxt5 && ext.Desire("GL_CHROMIUM_texture_compression_dxt5")) { enable_dxt5 = true; } if (enable_dxt1) { AddExtensionString("GL_EXT_texture_compression_dxt1"); validators_.compressed_texture_format.AddValue( GL_COMPRESSED_RGB_S3TC_DXT1_EXT); validators_.compressed_texture_format.AddValue( GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); } if (enable_dxt3) { // The difference between GL_EXT_texture_compression_s3tc and // GL_CHROMIUM_texture_compression_dxt3 is that the former // requires on the fly compression. The latter does not. AddExtensionString("GL_CHROMIUM_texture_compression_dxt3"); validators_.compressed_texture_format.AddValue( GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); } if (enable_dxt5) { // The difference between GL_EXT_texture_compression_s3tc and // GL_CHROMIUM_texture_compression_dxt5 is that the former // requires on the fly compression. The latter does not. AddExtensionString("GL_CHROMIUM_texture_compression_dxt5"); validators_.compressed_texture_format.AddValue( GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); } // Check if we should enable GL_EXT_texture_filter_anisotropic. if (ext.HaveAndDesire("GL_EXT_texture_filter_anisotropic")) { AddExtensionString("GL_EXT_texture_filter_anisotropic"); validators_.texture_parameter.AddValue( GL_TEXTURE_MAX_ANISOTROPY_EXT); validators_.g_l_state.AddValue( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT); } // Check if we should support GL_OES_packed_depth_stencil and/or // GL_GOOGLE_depth_texture. // NOTE: GL_OES_depth_texture requires support for depth // cubemaps. GL_ARB_depth_texture requires other features that // GL_OES_packed_depth_stencil does not provide. Therefore we made up // GL_GOOGLE_depth_texture. bool enable_depth_texture = false; if (ext.Desire("GL_GOOGLE_depth_texture") && (ext.Have("GL_ARB_depth_texture") || ext.Have("GL_OES_depth_texture"))) { enable_depth_texture = true; AddExtensionString("GL_GOOGLE_depth_texture"); validators_.texture_internal_format.AddValue(GL_DEPTH_COMPONENT); validators_.texture_format.AddValue(GL_DEPTH_COMPONENT); validators_.pixel_type.AddValue(GL_UNSIGNED_SHORT); validators_.pixel_type.AddValue(GL_UNSIGNED_INT); } // TODO(gman): Add depth types fo ElementsPerGroup and BytesPerElement if (ext.Desire("GL_OES_packed_depth_stencil") && (ext.Have("GL_EXT_packed_depth_stencil") || ext.Have("GL_OES_packed_depth_stencil"))) { AddExtensionString("GL_OES_packed_depth_stencil"); if (enable_depth_texture) { validators_.texture_internal_format.AddValue(GL_DEPTH_STENCIL); validators_.texture_format.AddValue(GL_DEPTH_STENCIL); validators_.pixel_type.AddValue(GL_UNSIGNED_INT_24_8); } validators_.render_buffer_format.AddValue(GL_DEPTH24_STENCIL8); } bool enable_texture_format_bgra8888 = false; bool enable_read_format_bgra = false; // Check if we should allow GL_EXT_texture_format_BGRA8888 if (ext.Desire("GL_EXT_texture_format_BGRA8888") && (ext.Have("GL_EXT_texture_format_BGRA8888") || ext.Have("GL_APPLE_texture_format_BGRA8888") || ext.Have("GL_EXT_bgra"))) { enable_texture_format_bgra8888 = true; } if (ext.HaveAndDesire("GL_EXT_bgra")) { enable_texture_format_bgra8888 = true; enable_read_format_bgra = true; } if (ext.Desire("GL_EXT_read_format_bgra") && (ext.Have("GL_EXT_read_format_bgra") || ext.Have("GL_EXT_bgra"))) { enable_read_format_bgra = true; } if (enable_texture_format_bgra8888) { AddExtensionString("GL_EXT_texture_format_BGRA8888"); validators_.texture_internal_format.AddValue(GL_BGRA_EXT); validators_.texture_format.AddValue(GL_BGRA_EXT); } if (enable_read_format_bgra) { AddExtensionString("GL_EXT_read_format_bgra"); validators_.read_pixel_format.AddValue(GL_BGRA_EXT); } if (ext.Desire("GL_OES_rgb8_rgba8")) { if (ext.Have("GL_OES_rgb8_rgba8") || gfx::HasDesktopGLFeatures()) { AddExtensionString("GL_OES_rgb8_rgba8"); validators_.render_buffer_format.AddValue(GL_RGB8_OES); validators_.render_buffer_format.AddValue(GL_RGBA8_OES); } } // Check if we should allow GL_OES_texture_npot if (ext.Desire("GL_OES_texture_npot") && (ext.Have("GL_ARB_texture_non_power_of_two") || ext.Have("GL_OES_texture_npot"))) { AddExtensionString("GL_OES_texture_npot"); npot_ok = true; } // Check if we should allow GL_OES_texture_float, GL_OES_texture_half_float, // GL_OES_texture_float_linear, GL_OES_texture_half_float_linear bool enable_texture_float = false; bool enable_texture_float_linear = false; bool enable_texture_half_float = false; bool enable_texture_half_float_linear = false; bool have_arb_texture_float = ext.Have("GL_ARB_texture_float"); if (have_arb_texture_float && ext.Desire("GL_ARB_texture_float")) { enable_texture_float = true; enable_texture_float_linear = true; enable_texture_half_float = true; enable_texture_half_float_linear = true; } else { if (ext.HaveAndDesire("GL_OES_texture_float") || (have_arb_texture_float && ext.Desire("GL_OES_texture_float"))) { enable_texture_float = true; if (ext.HaveAndDesire("GL_OES_texture_float_linear") || (have_arb_texture_float && ext.Desire("GL_OES_texture_float_linear"))) { enable_texture_float_linear = true; } } if (ext.HaveAndDesire("GL_OES_texture_half_float") || (have_arb_texture_float && ext.Desire("GL_OES_texture_half_float"))) { enable_texture_half_float = true; if (ext.HaveAndDesire("GL_OES_texture_half_float_linear") || (have_arb_texture_float && ext.Desire("GL_OES_texture_half_float_linear"))) { enable_texture_half_float_linear = true; } } } if (enable_texture_float) { validators_.pixel_type.AddValue(GL_FLOAT); AddExtensionString("GL_OES_texture_float"); if (enable_texture_float_linear) { AddExtensionString("GL_OES_texture_float_linear"); } } if (enable_texture_half_float) { validators_.pixel_type.AddValue(GL_HALF_FLOAT_OES); AddExtensionString("GL_OES_texture_half_float"); if (enable_texture_half_float_linear) { AddExtensionString("GL_OES_texture_half_float_linear"); } } // Check for multisample support if (!disallowed_features_.multisampling && ext.Desire("GL_CHROMIUM_framebuffer_multisample") && (ext.Have("GL_EXT_framebuffer_multisample") || ext.Have("GL_ANGLE_framebuffer_multisample"))) { feature_flags_.chromium_framebuffer_multisample = true; validators_.frame_buffer_target.AddValue(GL_READ_FRAMEBUFFER_EXT); validators_.frame_buffer_target.AddValue(GL_DRAW_FRAMEBUFFER_EXT); validators_.g_l_state.AddValue(GL_READ_FRAMEBUFFER_BINDING_EXT); validators_.g_l_state.AddValue(GL_MAX_SAMPLES_EXT); validators_.render_buffer_parameter.AddValue(GL_RENDERBUFFER_SAMPLES_EXT); AddExtensionString("GL_CHROMIUM_framebuffer_multisample"); } if (ext.HaveAndDesire("GL_OES_depth24") || (gfx::HasDesktopGLFeatures() && ext.Desire("GL_OES_depth24"))) { AddExtensionString("GL_OES_depth24"); validators_.render_buffer_format.AddValue(GL_DEPTH_COMPONENT24); } if (ext.HaveAndDesire("GL_OES_standard_derivatives") || (gfx::HasDesktopGLFeatures() && ext.Desire("GL_OES_standard_derivatives"))) { AddExtensionString("GL_OES_standard_derivatives"); feature_flags_.oes_standard_derivatives = true; validators_.hint_target.AddValue(GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES); validators_.g_l_state.AddValue(GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES); } if (ext.HaveAndDesire("GL_OES_EGL_image_external")) { AddExtensionString("GL_OES_EGL_image_external"); feature_flags_.oes_egl_image_external = true; validators_.texture_bind_target.AddValue(GL_TEXTURE_EXTERNAL_OES); validators_.get_tex_param_target.AddValue(GL_TEXTURE_EXTERNAL_OES); validators_.texture_parameter.AddValue(GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES); validators_.g_l_state.AddValue(GL_TEXTURE_BINDING_EXTERNAL_OES); } if (ext.Desire("GL_CHROMIUM_stream_texture")) { AddExtensionString("GL_CHROMIUM_stream_texture"); feature_flags_.chromium_stream_texture = true; } // TODO(gman): Add support for these extensions. // GL_OES_depth32 // GL_OES_element_index_uint feature_flags_.enable_texture_float_linear = enable_texture_float_linear; feature_flags_.enable_texture_half_float_linear = enable_texture_half_float_linear; feature_flags_.npot_ok = npot_ok; } void FeatureInfo::AddExtensionString(const std::string& str) { if (extensions_.find(str) == std::string::npos) { extensions_ += (extensions_.empty() ? "" : " ") + str; } } } // namespace gles2 } // namespace gpu