michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WebGLContext.h" michael@0: #include "WebGLContextUtils.h" michael@0: #include "WebGLExtensions.h" michael@0: #include "GLContext.h" michael@0: michael@0: #include "nsString.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "AccessCheck.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gl; michael@0: michael@0: /* static */ const char* michael@0: WebGLContext::GetExtensionString(WebGLExtensionID ext) michael@0: { michael@0: // must match WebGLExtensionID. michael@0: // Once we can use variadic templates, EnumeratedArray should get a constructor michael@0: // allowing to initialize it directly without using this auxiliary plain array. michael@0: static const char *kExtensionNames[] = { michael@0: "ANGLE_instanced_arrays", michael@0: "EXT_color_buffer_half_float", michael@0: "EXT_frag_depth", michael@0: "EXT_sRGB", michael@0: "EXT_texture_filter_anisotropic", michael@0: "OES_element_index_uint", michael@0: "OES_standard_derivatives", michael@0: "OES_texture_float", michael@0: "OES_texture_float_linear", michael@0: "OES_texture_half_float", michael@0: "OES_texture_half_float_linear", michael@0: "OES_vertex_array_object", michael@0: "WEBGL_color_buffer_float", michael@0: "WEBGL_compressed_texture_atc", michael@0: "WEBGL_compressed_texture_etc1", michael@0: "WEBGL_compressed_texture_pvrtc", michael@0: "WEBGL_compressed_texture_s3tc", michael@0: "WEBGL_debug_renderer_info", michael@0: "WEBGL_debug_shaders", michael@0: "WEBGL_depth_texture", michael@0: "WEBGL_draw_buffers", michael@0: "WEBGL_lose_context" michael@0: }; michael@0: michael@0: typedef EnumeratedArray michael@0: names_array_t; michael@0: static const names_array_t kExtensionNamesEnumeratedArray(kExtensionNames); michael@0: michael@0: return kExtensionNamesEnumeratedArray[ext]; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::IsExtensionEnabled(WebGLExtensionID ext) const { michael@0: return mExtensions[ext]; michael@0: } michael@0: michael@0: bool WebGLContext::IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const michael@0: { michael@0: bool allowPrivilegedExts = false; michael@0: michael@0: // Chrome contexts need access to debug information even when michael@0: // webgl.disable-extensions is set. This is used in the graphics michael@0: // section of about:support. michael@0: if (xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) michael@0: allowPrivilegedExts = true; michael@0: michael@0: if (Preferences::GetBool("webgl.enable-privileged-extensions", false)) michael@0: allowPrivilegedExts = true; michael@0: michael@0: if (allowPrivilegedExts) { michael@0: switch (ext) { michael@0: case WebGLExtensionID::WEBGL_debug_renderer_info: michael@0: return true; michael@0: case WebGLExtensionID::WEBGL_debug_shaders: michael@0: return true; michael@0: default: michael@0: // For warnings-as-errors. michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return IsExtensionSupported(ext); michael@0: } michael@0: michael@0: bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const michael@0: { michael@0: if (mDisableExtensions) { michael@0: return false; michael@0: } michael@0: michael@0: switch (ext) { michael@0: case WebGLExtensionID::OES_element_index_uint: michael@0: return gl->IsSupported(GLFeature::element_index_uint); michael@0: case WebGLExtensionID::OES_standard_derivatives: michael@0: return gl->IsSupported(GLFeature::standard_derivatives); michael@0: case WebGLExtensionID::WEBGL_lose_context: michael@0: // We always support this extension. michael@0: return true; michael@0: case WebGLExtensionID::OES_texture_float: michael@0: return gl->IsSupported(GLFeature::texture_float); michael@0: case WebGLExtensionID::OES_texture_float_linear: michael@0: return gl->IsSupported(GLFeature::texture_float_linear); michael@0: case WebGLExtensionID::OES_texture_half_float: michael@0: // If we have Feature::texture_half_float, we must not be on ES2 michael@0: // and need to translate HALF_FLOAT_OES -> HALF_FLOAT. We do that michael@0: // right before making the relevant calls. michael@0: return gl->IsExtensionSupported(GLContext::OES_texture_half_float) || michael@0: gl->IsSupported(GLFeature::texture_half_float); michael@0: case WebGLExtensionID::OES_texture_half_float_linear: michael@0: return gl->IsSupported(GLFeature::texture_half_float_linear); michael@0: case WebGLExtensionID::WEBGL_color_buffer_float: michael@0: return WebGLExtensionColorBufferFloat::IsSupported(this); michael@0: case WebGLExtensionID::EXT_color_buffer_half_float: michael@0: return WebGLExtensionColorBufferHalfFloat::IsSupported(this); michael@0: case WebGLExtensionID::OES_vertex_array_object: michael@0: return WebGLExtensionVertexArray::IsSupported(this); michael@0: case WebGLExtensionID::EXT_texture_filter_anisotropic: michael@0: return gl->IsExtensionSupported(GLContext::EXT_texture_filter_anisotropic); michael@0: case WebGLExtensionID::WEBGL_compressed_texture_s3tc: michael@0: if (gl->IsExtensionSupported(GLContext::EXT_texture_compression_s3tc)) { michael@0: return true; michael@0: } michael@0: else if (gl->IsExtensionSupported(GLContext::EXT_texture_compression_dxt1) && michael@0: gl->IsExtensionSupported(GLContext::ANGLE_texture_compression_dxt3) && michael@0: gl->IsExtensionSupported(GLContext::ANGLE_texture_compression_dxt5)) michael@0: { michael@0: return true; michael@0: } michael@0: return false; michael@0: case WebGLExtensionID::WEBGL_compressed_texture_atc: michael@0: return gl->IsExtensionSupported(GLContext::AMD_compressed_ATC_texture); michael@0: case WebGLExtensionID::WEBGL_compressed_texture_etc1: michael@0: return gl->IsExtensionSupported(GLContext::OES_compressed_ETC1_RGB8_texture); michael@0: case WebGLExtensionID::WEBGL_compressed_texture_pvrtc: michael@0: return gl->IsExtensionSupported(GLContext::IMG_texture_compression_pvrtc); michael@0: case WebGLExtensionID::WEBGL_depth_texture: michael@0: // WEBGL_depth_texture supports DEPTH_STENCIL textures michael@0: if (!gl->IsSupported(GLFeature::packed_depth_stencil)) { michael@0: return false; michael@0: } michael@0: return gl->IsSupported(GLFeature::depth_texture) || michael@0: gl->IsExtensionSupported(GLContext::ANGLE_depth_texture); michael@0: case WebGLExtensionID::ANGLE_instanced_arrays: michael@0: return WebGLExtensionInstancedArrays::IsSupported(this); michael@0: case WebGLExtensionID::EXT_sRGB: michael@0: return WebGLExtensionSRGB::IsSupported(this); michael@0: case WebGLExtensionID::WEBGL_draw_buffers: michael@0: return WebGLExtensionDrawBuffers::IsSupported(this); michael@0: case WebGLExtensionID::EXT_frag_depth: michael@0: return WebGLExtensionFragDepth::IsSupported(this); michael@0: default: michael@0: // For warnings-as-errors. michael@0: break; michael@0: } michael@0: // Uncomment this switch for any new extensions michael@0: #if 0 michael@0: if (Preferences::GetBool("webgl.enable-draft-extensions", false) || IsWebGL2()) { michael@0: switch (ext) { michael@0: default: michael@0: // For warnings-as-errors. michael@0: break; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: CompareWebGLExtensionName(const nsACString& name, const char *other) michael@0: { michael@0: return name.Equals(other, nsCaseInsensitiveCStringComparator()); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetExtension(JSContext *cx, const nsAString& aName, michael@0: JS::MutableHandle aRetval, michael@0: ErrorResult& rv) michael@0: { michael@0: if (IsContextLost()) { michael@0: aRetval.set(nullptr); michael@0: return; michael@0: } michael@0: michael@0: NS_LossyConvertUTF16toASCII name(aName); michael@0: michael@0: WebGLExtensionID ext = WebGLExtensionID::Unknown; michael@0: michael@0: // step 1: figure what extension is wanted michael@0: for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++) michael@0: { michael@0: WebGLExtensionID extension = WebGLExtensionID(i); michael@0: michael@0: if (CompareWebGLExtensionName(name, GetExtensionString(extension))) { michael@0: ext = extension; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (ext == WebGLExtensionID::Unknown) michael@0: { michael@0: /** michael@0: * We keep backward compatibility for these deprecated vendor-prefixed michael@0: * alias. Do not add new ones anymore. Hide it behind the michael@0: * webgl.enable-draft-extensions flag instead. michael@0: */ michael@0: if (CompareWebGLExtensionName(name, "MOZ_WEBGL_lose_context")) { michael@0: ext = WebGLExtensionID::WEBGL_lose_context; michael@0: } michael@0: else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_s3tc")) { michael@0: ext = WebGLExtensionID::WEBGL_compressed_texture_s3tc; michael@0: } michael@0: else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_atc")) { michael@0: ext = WebGLExtensionID::WEBGL_compressed_texture_atc; michael@0: } michael@0: else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_pvrtc")) { michael@0: ext = WebGLExtensionID::WEBGL_compressed_texture_pvrtc; michael@0: } michael@0: else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_depth_texture")) { michael@0: ext = WebGLExtensionID::WEBGL_depth_texture; michael@0: } michael@0: michael@0: if (ext != WebGLExtensionID::Unknown) { michael@0: GenerateWarning("getExtension('%s'): MOZ_ prefixed WebGL extension strings are deprecated. " michael@0: "Support for them will be removed in the future. Use unprefixed extension strings. " michael@0: "To get draft extensions, set the webgl.enable-draft-extensions preference.", michael@0: name.get()); michael@0: } michael@0: } michael@0: michael@0: if (ext == WebGLExtensionID::Unknown) { michael@0: aRetval.set(nullptr); michael@0: return; michael@0: } michael@0: michael@0: // step 2: check if the extension is supported michael@0: if (!IsExtensionSupported(cx, ext)) { michael@0: aRetval.set(nullptr); michael@0: return; michael@0: } michael@0: michael@0: // step 3: if the extension hadn't been previously been created, create it now, thus enabling it michael@0: if (!IsExtensionEnabled(ext)) { michael@0: EnableExtension(ext); michael@0: } michael@0: michael@0: aRetval.set(WebGLObjectAsJSObject(cx, mExtensions[ext].get(), rv)); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::EnableExtension(WebGLExtensionID ext) michael@0: { michael@0: MOZ_ASSERT(IsExtensionEnabled(ext) == false); michael@0: michael@0: WebGLExtensionBase* obj = nullptr; michael@0: switch (ext) { michael@0: case WebGLExtensionID::OES_element_index_uint: michael@0: obj = new WebGLExtensionElementIndexUint(this); michael@0: break; michael@0: case WebGLExtensionID::OES_standard_derivatives: michael@0: obj = new WebGLExtensionStandardDerivatives(this); michael@0: break; michael@0: case WebGLExtensionID::EXT_texture_filter_anisotropic: michael@0: obj = new WebGLExtensionTextureFilterAnisotropic(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_lose_context: michael@0: obj = new WebGLExtensionLoseContext(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_compressed_texture_s3tc: michael@0: obj = new WebGLExtensionCompressedTextureS3TC(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_compressed_texture_atc: michael@0: obj = new WebGLExtensionCompressedTextureATC(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_compressed_texture_etc1: michael@0: obj = new WebGLExtensionCompressedTextureETC1(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_compressed_texture_pvrtc: michael@0: obj = new WebGLExtensionCompressedTexturePVRTC(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_debug_renderer_info: michael@0: obj = new WebGLExtensionDebugRendererInfo(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_debug_shaders: michael@0: obj = new WebGLExtensionDebugShaders(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_depth_texture: michael@0: obj = new WebGLExtensionDepthTexture(this); michael@0: break; michael@0: case WebGLExtensionID::OES_texture_float: michael@0: obj = new WebGLExtensionTextureFloat(this); michael@0: break; michael@0: case WebGLExtensionID::OES_texture_float_linear: michael@0: obj = new WebGLExtensionTextureFloatLinear(this); michael@0: break; michael@0: case WebGLExtensionID::OES_texture_half_float: michael@0: obj = new WebGLExtensionTextureHalfFloat(this); michael@0: break; michael@0: case WebGLExtensionID::OES_texture_half_float_linear: michael@0: obj = new WebGLExtensionTextureHalfFloatLinear(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_color_buffer_float: michael@0: obj = new WebGLExtensionColorBufferFloat(this); michael@0: break; michael@0: case WebGLExtensionID::EXT_color_buffer_half_float: michael@0: obj = new WebGLExtensionColorBufferHalfFloat(this); michael@0: break; michael@0: case WebGLExtensionID::WEBGL_draw_buffers: michael@0: obj = new WebGLExtensionDrawBuffers(this); michael@0: break; michael@0: case WebGLExtensionID::OES_vertex_array_object: michael@0: obj = new WebGLExtensionVertexArray(this); michael@0: break; michael@0: case WebGLExtensionID::ANGLE_instanced_arrays: michael@0: obj = new WebGLExtensionInstancedArrays(this); michael@0: break; michael@0: case WebGLExtensionID::EXT_sRGB: michael@0: obj = new WebGLExtensionSRGB(this); michael@0: break; michael@0: case WebGLExtensionID::EXT_frag_depth: michael@0: obj = new WebGLExtensionFragDepth(this); michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "should not get there."); michael@0: } michael@0: michael@0: mExtensions[ext] = obj; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetSupportedExtensions(JSContext *cx, Nullable< nsTArray > &retval) michael@0: { michael@0: retval.SetNull(); michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: nsTArray& arr = retval.SetValue(); michael@0: michael@0: for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++) michael@0: { michael@0: WebGLExtensionID extension = WebGLExtensionID(i); michael@0: michael@0: if (IsExtensionSupported(cx, extension)) { michael@0: arr.AppendElement(NS_ConvertUTF8toUTF16(GetExtensionString(extension))); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * We keep backward compatibility for these deprecated vendor-prefixed michael@0: * alias. Do not add new ones anymore. Hide it behind the michael@0: * webgl.enable-draft-extensions flag instead. michael@0: */ michael@0: if (IsExtensionSupported(cx, WebGLExtensionID::WEBGL_lose_context)) michael@0: arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_lose_context")); michael@0: if (IsExtensionSupported(cx, WebGLExtensionID::WEBGL_compressed_texture_s3tc)) michael@0: arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_s3tc")); michael@0: if (IsExtensionSupported(cx, WebGLExtensionID::WEBGL_compressed_texture_atc)) michael@0: arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_atc")); michael@0: if (IsExtensionSupported(cx, WebGLExtensionID::WEBGL_compressed_texture_pvrtc)) michael@0: arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_pvrtc")); michael@0: if (IsExtensionSupported(cx, WebGLExtensionID::WEBGL_depth_texture)) michael@0: arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_depth_texture")); michael@0: } michael@0: