script: Move WebGL DOM interfaces to script/dom/webgl/ (#38995)

Move interfaces defined by the WebGL spec to the `script/dom/webgl/
`module from `script/dom/`.

`script/dom/webgl*.rs` -> `script/dom/webgl/`
`script/dom/webgl_extensions` -> `script/dom/webgl/extensions`
`script/dom/webgl_validations` -> `script/dom/webgl/validations`

Testing: No changes, just a refactoring

Fixes (partially): #38901

Signed-off-by: Andrei Volykhin <volykhin.andrei@huawei.com>
Co-authored-by: Andrei Volykhin <volykhin.andrei@huawei.com>
This commit is contained in:
Andrei Volykhin 2025-08-28 20:50:05 +03:00 committed by GitHub
parent 6205c07114
commit ef544a4db4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 162 additions and 148 deletions

View file

@ -0,0 +1,96 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::{
ANGLEInstancedArraysConstants, ANGLEInstancedArraysMethods,
};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct ANGLEInstancedArrays {
reflector_: Reflector,
ctx: Dom<WebGLRenderingContext>,
}
impl ANGLEInstancedArrays {
fn new_inherited(ctx: &WebGLRenderingContext) -> Self {
Self {
reflector_: Reflector::new(),
ctx: Dom::from_ref(ctx),
}
}
}
impl WebGLExtension for ANGLEInstancedArrays {
type Extension = Self;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(
Box::new(ANGLEInstancedArrays::new_inherited(ctx)),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_any_gl_extension(&[
"GL_ANGLE_instanced_arrays",
"GL_ARB_instanced_arrays",
"GL_EXT_instanced_arrays",
"GL_NV_instanced_arrays",
])
}
fn enable(ext: &WebGLExtensions) {
ext.enable_get_vertex_attrib_name(
ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE,
);
}
fn name() -> &'static str {
"ANGLE_instanced_arrays"
}
}
impl ANGLEInstancedArraysMethods<crate::DomTypeHolder> for ANGLEInstancedArrays {
// https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
fn DrawArraysInstancedANGLE(&self, mode: u32, first: i32, count: i32, primcount: i32) {
handle_potential_webgl_error!(
self.ctx,
self.ctx
.draw_arrays_instanced(mode, first, count, primcount)
)
}
// https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
fn DrawElementsInstancedANGLE(
&self,
mode: u32,
count: i32,
type_: u32,
offset: i64,
primcount: i32,
) {
handle_potential_webgl_error!(
self.ctx,
self.ctx
.draw_elements_instanced(mode, count, type_, offset, primcount)
)
}
fn VertexAttribDivisorANGLE(&self, index: u32, divisor: u32) {
self.ctx.vertex_attrib_divisor(index, divisor);
}
}

View file

@ -0,0 +1,49 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct EXTBlendMinmax {
reflector_: Reflector,
}
impl EXTBlendMinmax {
fn new_inherited() -> Self {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for EXTBlendMinmax {
type Extension = Self;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited()), &*ctx.global(), can_gc)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_gl_extension("GL_EXT_blend_minmax")
}
fn enable(ext: &WebGLExtensions) {
ext.enable_blend_minmax();
}
fn name() -> &'static str {
"EXT_blend_minmax"
}
}

View file

@ -0,0 +1,51 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::extensions::oestexturehalffloat::OESTextureHalfFloat;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct EXTColorBufferHalfFloat {
reflector_: Reflector,
}
impl EXTColorBufferHalfFloat {
fn new_inherited() -> EXTColorBufferHalfFloat {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for EXTColorBufferHalfFloat {
type Extension = EXTColorBufferHalfFloat;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<EXTColorBufferHalfFloat> {
reflect_dom_object(
Box::new(EXTColorBufferHalfFloat::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
OESTextureHalfFloat::is_supported(ext)
}
fn enable(_ext: &WebGLExtensions) {}
fn name() -> &'static str {
"EXT_color_buffer_half_float"
}
}

View file

@ -0,0 +1,64 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{WebGLSLVersion, WebGLVersion};
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct EXTFragDepth {
reflector_: Reflector,
}
impl EXTFragDepth {
fn new_inherited() -> EXTFragDepth {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for EXTFragDepth {
type Extension = Self;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited()), &*ctx.global(), can_gc)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
let min_glsl_version = if ext.is_gles() {
WebGLSLVersion { major: 3, minor: 0 }
} else {
WebGLSLVersion {
major: 1,
minor: 10,
}
};
match (
ext.is_gles(),
ext.is_min_glsl_version_satisfied(min_glsl_version),
) {
// ANGLE's shader translator can't translate ESSL1 exts to ESSL3. (bug
// 1524804)
(true, true) => false,
(true, false) => ext.supports_gl_extension("GL_EXT_frag_depth"),
(false, is_min_glsl_version_satisfied) => is_min_glsl_version_satisfied,
}
}
fn enable(_ext: &WebGLExtensions) {}
fn name() -> &'static str {
"EXT_frag_depth"
}
}

View file

@ -0,0 +1,48 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct EXTShaderTextureLod {
reflector_: Reflector,
}
impl EXTShaderTextureLod {
fn new_inherited() -> Self {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for EXTShaderTextureLod {
type Extension = Self;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited()), &*ctx.global(), can_gc)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
// This extension is always available on desktop GL.
!ext.is_gles() || ext.supports_gl_extension("GL_EXT_shader_texture_lod")
}
fn enable(_ext: &WebGLExtensions) {}
fn name() -> &'static str {
"EXT_shader_texture_lod"
}
}

View file

@ -0,0 +1,55 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::codegen::Bindings::EXTTextureFilterAnisotropicBinding::EXTTextureFilterAnisotropicConstants;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct EXTTextureFilterAnisotropic {
reflector_: Reflector,
}
impl EXTTextureFilterAnisotropic {
fn new_inherited() -> EXTTextureFilterAnisotropic {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for EXTTextureFilterAnisotropic {
type Extension = EXTTextureFilterAnisotropic;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(Box::new(Self::new_inherited()), &*ctx.global(), can_gc)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_gl_extension("GL_EXT_texture_filter_anisotropic")
}
fn enable(ext: &WebGLExtensions) {
ext.enable_get_tex_parameter_name(
EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT,
);
ext.enable_get_parameter_name(
EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT,
);
}
fn name() -> &'static str {
"EXT_texture_filter_anisotropic"
}
}

View file

@ -0,0 +1,23 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
pub(crate) mod angleinstancedarrays;
pub(crate) mod extblendminmax;
pub(crate) mod extcolorbufferhalffloat;
pub(crate) mod extfragdepth;
pub(crate) mod extshadertexturelod;
pub(crate) mod exttexturefilteranisotropic;
pub(crate) mod oeselementindexuint;
pub(crate) mod oesstandardderivatives;
pub(crate) mod oestexturefloat;
pub(crate) mod oestexturefloatlinear;
pub(crate) mod oestexturehalffloat;
pub(crate) mod oestexturehalffloatlinear;
pub(crate) mod oesvertexarrayobject;
pub(crate) mod webglcolorbufferfloat;
pub(crate) mod webglcompressedtextureetc1;
pub(crate) mod webglcompressedtextures3tc;

View file

@ -0,0 +1,54 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct OESElementIndexUint {
reflector_: Reflector,
}
impl OESElementIndexUint {
fn new_inherited() -> Self {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for OESElementIndexUint {
type Extension = Self;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
reflect_dom_object(
Box::new(OESElementIndexUint::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
// This extension is always available in desktop OpenGL.
!ext.is_gles() || ext.supports_gl_extension("GL_OES_element_index_uint")
}
fn enable(ext: &WebGLExtensions) {
ext.enable_element_index_uint();
}
fn name() -> &'static str {
"OES_element_index_uint"
}
}

View file

@ -0,0 +1,59 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::codegen::Bindings::OESStandardDerivativesBinding::OESStandardDerivativesConstants;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct OESStandardDerivatives {
reflector_: Reflector,
}
impl OESStandardDerivatives {
fn new_inherited() -> OESStandardDerivatives {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for OESStandardDerivatives {
type Extension = OESStandardDerivatives;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<OESStandardDerivatives> {
reflect_dom_object(
Box::new(OESStandardDerivatives::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
// The standard derivatives are always available in desktop OpenGL.
!ext.is_gles() || ext.supports_any_gl_extension(&["GL_OES_standard_derivatives"])
}
fn enable(ext: &WebGLExtensions) {
ext.enable_hint_target(
OESStandardDerivativesConstants::FRAGMENT_SHADER_DERIVATIVE_HINT_OES,
);
ext.enable_get_parameter_name(
OESStandardDerivativesConstants::FRAGMENT_SHADER_DERIVATIVE_HINT_OES,
);
}
fn name() -> &'static str {
"OES_standard_derivatives"
}
}

View file

@ -0,0 +1,69 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{TexFormat, WebGLVersion};
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions, constants as webgl};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct OESTextureFloat {
reflector_: Reflector,
}
impl OESTextureFloat {
fn new_inherited() -> OESTextureFloat {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for OESTextureFloat {
type Extension = OESTextureFloat;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<OESTextureFloat> {
reflect_dom_object(
Box::new(OESTextureFloat::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_any_gl_extension(&[
"GL_OES_texture_float",
"GL_ARB_texture_float",
"GL_EXT_color_buffer_float",
])
}
fn enable(ext: &WebGLExtensions) {
ext.enable_tex_type(webgl::FLOAT);
ext.add_effective_tex_internal_format(TexFormat::RGBA, webgl::FLOAT, TexFormat::RGBA32f);
ext.add_effective_tex_internal_format(TexFormat::RGB, webgl::FLOAT, TexFormat::RGB32f);
ext.add_effective_tex_internal_format(
TexFormat::Luminance,
webgl::FLOAT,
TexFormat::Luminance32f,
);
ext.add_effective_tex_internal_format(TexFormat::Alpha, webgl::FLOAT, TexFormat::Alpha32f);
ext.add_effective_tex_internal_format(
TexFormat::LuminanceAlpha,
webgl::FLOAT,
TexFormat::LuminanceAlpha32f,
);
}
fn name() -> &'static str {
"OES_texture_float"
}
}

View file

@ -0,0 +1,51 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions, constants as webgl};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct OESTextureFloatLinear {
reflector_: Reflector,
}
impl OESTextureFloatLinear {
fn new_inherited() -> OESTextureFloatLinear {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for OESTextureFloatLinear {
type Extension = OESTextureFloatLinear;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<OESTextureFloatLinear> {
reflect_dom_object(
Box::new(OESTextureFloatLinear::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::All
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_any_gl_extension(&["GL_OES_texture_float_linear", "GL_ARB_texture_float"])
}
fn enable(ext: &WebGLExtensions) {
ext.enable_filterable_tex_type(webgl::FLOAT);
}
fn name() -> &'static str {
"OES_texture_float_linear"
}
}

View file

@ -0,0 +1,68 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{TexFormat, WebGLVersion};
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::codegen::Bindings::OESTextureHalfFloatBinding::OESTextureHalfFloatConstants;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct OESTextureHalfFloat {
reflector_: Reflector,
}
impl OESTextureHalfFloat {
fn new_inherited() -> OESTextureHalfFloat {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for OESTextureHalfFloat {
type Extension = OESTextureHalfFloat;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<OESTextureHalfFloat> {
reflect_dom_object(
Box::new(OESTextureHalfFloat::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_any_gl_extension(&[
"GL_OES_texture_half_float",
"GL_ARB_half_float_pixel",
"GL_NV_half_float",
"GL_EXT_color_buffer_half_float",
])
}
fn enable(ext: &WebGLExtensions) {
let hf = OESTextureHalfFloatConstants::HALF_FLOAT_OES;
ext.enable_tex_type(hf);
ext.add_effective_tex_internal_format(TexFormat::RGBA, hf, TexFormat::RGBA16f);
ext.add_effective_tex_internal_format(TexFormat::RGB, hf, TexFormat::RGB16f);
ext.add_effective_tex_internal_format(TexFormat::Luminance, hf, TexFormat::Luminance16f);
ext.add_effective_tex_internal_format(TexFormat::Alpha, hf, TexFormat::Alpha16f);
ext.add_effective_tex_internal_format(
TexFormat::LuminanceAlpha,
hf,
TexFormat::LuminanceAlpha16f,
);
}
fn name() -> &'static str {
"OES_texture_half_float"
}
}

View file

@ -0,0 +1,56 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::codegen::Bindings::OESTextureHalfFloatBinding::OESTextureHalfFloatConstants;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct OESTextureHalfFloatLinear {
reflector_: Reflector,
}
impl OESTextureHalfFloatLinear {
fn new_inherited() -> OESTextureHalfFloatLinear {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for OESTextureHalfFloatLinear {
type Extension = OESTextureHalfFloatLinear;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<OESTextureHalfFloatLinear> {
reflect_dom_object(
Box::new(OESTextureHalfFloatLinear::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::All
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_any_gl_extension(&[
"GL_OES_texture_float_linear",
"GL_ARB_half_float_pixel",
"GL_NV_half_float",
])
}
fn enable(ext: &WebGLExtensions) {
ext.enable_filterable_tex_type(OESTextureHalfFloatConstants::HALF_FLOAT_OES);
}
fn name() -> &'static str {
"OES_texture_half_float_linear"
}
}

View file

@ -0,0 +1,84 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::{
OESVertexArrayObjectConstants, OESVertexArrayObjectMethods,
};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct OESVertexArrayObject {
reflector_: Reflector,
ctx: Dom<WebGLRenderingContext>,
}
impl OESVertexArrayObject {
fn new_inherited(ctx: &WebGLRenderingContext) -> OESVertexArrayObject {
Self {
reflector_: Reflector::new(),
ctx: Dom::from_ref(ctx),
}
}
}
impl OESVertexArrayObjectMethods<crate::DomTypeHolder> for OESVertexArrayObject {
// https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
fn CreateVertexArrayOES(&self) -> Option<DomRoot<WebGLVertexArrayObjectOES>> {
self.ctx.create_vertex_array()
}
// https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
fn DeleteVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
self.ctx.delete_vertex_array(vao);
}
// https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
fn IsVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool {
self.ctx.is_vertex_array(vao)
}
// https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
fn BindVertexArrayOES(&self, vao: Option<&WebGLVertexArrayObjectOES>) {
self.ctx.bind_vertex_array(vao);
}
}
impl WebGLExtension for OESVertexArrayObject {
type Extension = OESVertexArrayObject;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<OESVertexArrayObject> {
reflect_dom_object(
Box::new(OESVertexArrayObject::new_inherited(ctx)),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_any_gl_extension(&[
"GL_OES_vertex_array_object",
"GL_ARB_vertex_array_object",
"GL_APPLE_vertex_array_object",
])
}
fn enable(ext: &WebGLExtensions) {
ext.enable_get_parameter_name(OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES);
}
fn name() -> &'static str {
"OES_vertex_array_object"
}
}

View file

@ -0,0 +1,51 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::extensions::oestexturefloat::OESTextureFloat;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WEBGLColorBufferFloat {
reflector_: Reflector,
}
impl WEBGLColorBufferFloat {
fn new_inherited() -> WEBGLColorBufferFloat {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for WEBGLColorBufferFloat {
type Extension = WEBGLColorBufferFloat;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<WEBGLColorBufferFloat> {
reflect_dom_object(
Box::new(WEBGLColorBufferFloat::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
OESTextureFloat::is_supported(ext)
}
fn enable(_ext: &WebGLExtensions) {}
fn name() -> &'static str {
"WEBGL_color_buffer_float"
}
}

View file

@ -0,0 +1,59 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{TexFormat, WebGLVersion};
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl::webgltexture::{TexCompression, TexCompressionValidation};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WEBGLCompressedTextureETC1 {
reflector_: Reflector,
}
impl WEBGLCompressedTextureETC1 {
fn new_inherited() -> WEBGLCompressedTextureETC1 {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for WEBGLCompressedTextureETC1 {
type Extension = WEBGLCompressedTextureETC1;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<WEBGLCompressedTextureETC1> {
reflect_dom_object(
Box::new(WEBGLCompressedTextureETC1::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_gl_extension("GL_OES_compressed_ETC1_RGB8_texture")
}
fn enable(ext: &WebGLExtensions) {
ext.add_tex_compression_formats(&[TexCompression {
format: TexFormat::CompressedRgbEtc1,
bytes_per_block: 8,
block_width: 4,
block_height: 4,
validation: TexCompressionValidation::None,
}]);
}
fn name() -> &'static str {
"WEBGL_compressed_texture_etc1"
}
}

View file

@ -0,0 +1,87 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{TexFormat, WebGLVersion};
use dom_struct::dom_struct;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl::webgltexture::{TexCompression, TexCompressionValidation};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WEBGLCompressedTextureS3TC {
reflector_: Reflector,
}
impl WEBGLCompressedTextureS3TC {
fn new_inherited() -> WEBGLCompressedTextureS3TC {
Self {
reflector_: Reflector::new(),
}
}
}
impl WebGLExtension for WEBGLCompressedTextureS3TC {
type Extension = WEBGLCompressedTextureS3TC;
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<WEBGLCompressedTextureS3TC> {
reflect_dom_object(
Box::new(WEBGLCompressedTextureS3TC::new_inherited()),
&*ctx.global(),
can_gc,
)
}
fn spec() -> WebGLExtensionSpec {
WebGLExtensionSpec::Specific(WebGLVersion::WebGL1)
}
fn is_supported(ext: &WebGLExtensions) -> bool {
ext.supports_gl_extension("GL_EXT_texture_compression_s3tc") ||
ext.supports_all_gl_extension(&[
"GL_EXT_texture_compression_dxt1",
"GL_ANGLE_texture_compression_dxt3",
"GL_ANGLE_texture_compression_dxt5",
])
}
fn enable(ext: &WebGLExtensions) {
ext.add_tex_compression_formats(&[
TexCompression {
format: TexFormat::CompressedRgbS3tcDxt1,
bytes_per_block: 8,
block_width: 4,
block_height: 4,
validation: TexCompressionValidation::S3TC,
},
TexCompression {
format: TexFormat::CompressedRgbaS3tcDxt1,
bytes_per_block: 8,
block_width: 4,
block_height: 4,
validation: TexCompressionValidation::S3TC,
},
TexCompression {
format: TexFormat::CompressedRgbaS3tcDxt3,
bytes_per_block: 16,
block_width: 4,
block_height: 4,
validation: TexCompressionValidation::S3TC,
},
TexCompression {
format: TexFormat::CompressedRgbaS3tcDxt5,
bytes_per_block: 16,
block_width: 4,
block_height: 4,
validation: TexCompressionValidation::S3TC,
},
]);
}
fn name() -> &'static str {
"WEBGL_compressed_texture_s3tc"
}
}

View file

@ -0,0 +1,43 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLVersion;
use super::WebGLExtensions;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
/// Trait implemented by WebGL extensions.
pub(crate) trait WebGLExtension: Sized
where
Self::Extension: DomObject + JSTraceable,
{
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
type Extension;
/// Creates the DOM object of the WebGL extension.
fn new(ctx: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self::Extension>;
/// Returns which WebGL spec is this extension written against.
fn spec() -> WebGLExtensionSpec;
/// Checks if the extension is supported.
fn is_supported(ext: &WebGLExtensions) -> bool;
/// Enable the extension.
fn enable(ext: &WebGLExtensions);
/// Name of the WebGL Extension.
fn name() -> &'static str;
}
pub(crate) enum WebGLExtensionSpec {
/// Extensions written against both WebGL and WebGL2 specs.
All,
/// Extensions writen against a specific WebGL version spec.
Specific(WebGLVersion),
}

View file

@ -0,0 +1,476 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::iter::FromIterator;
use std::ptr::NonNull;
use canvas_traits::webgl::{GlType, TexFormat, WebGLSLVersion, WebGLVersion};
use fnv::{FnvHashMap, FnvHashSet};
use js::jsapi::JSObject;
use malloc_size_of::MallocSizeOf;
type GLenum = u32;
use super::wrapper::{TypedWebGLExtensionWrapper, WebGLExtensionWrapper};
use super::{WebGLExtension, WebGLExtensionSpec, ext};
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
use crate::dom::bindings::codegen::Bindings::EXTTextureFilterAnisotropicBinding::EXTTextureFilterAnisotropicConstants;
use crate::dom::bindings::codegen::Bindings::OESStandardDerivativesBinding::OESStandardDerivativesConstants;
use crate::dom::bindings::codegen::Bindings::OESTextureHalfFloatBinding::OESTextureHalfFloatConstants;
use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::webgl::extensions::extcolorbufferhalffloat::EXTColorBufferHalfFloat;
use crate::dom::webgl::extensions::oestexturefloat::OESTextureFloat;
use crate::dom::webgl::extensions::oestexturehalffloat::OESTextureHalfFloat;
use crate::dom::webgl::extensions::webglcolorbufferfloat::WEBGLColorBufferFloat;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl::webgltexture::TexCompression;
// Data types that are implemented for texImage2D and texSubImage2D in a WebGL 1.0 context
// but must trigger a InvalidValue error until the related WebGL Extensions are enabled.
// Example: https://www.khronos.org/registry/webgl/extensions/OES_texture_float/
const DEFAULT_DISABLED_TEX_TYPES_WEBGL1: [GLenum; 2] = [
constants::FLOAT,
OESTextureHalfFloatConstants::HALF_FLOAT_OES,
];
// Data types that are implemented for textures in WebGLRenderingContext
// but not allowed to use with linear filtering until the related WebGL Extensions are enabled.
// Example: https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/
const DEFAULT_NOT_FILTERABLE_TEX_TYPES: [GLenum; 2] = [
constants::FLOAT,
OESTextureHalfFloatConstants::HALF_FLOAT_OES,
];
// Param names that are implemented for glGetParameter in a WebGL 1.0 context
// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
// Example: https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/
const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL1: [GLenum; 3] = [
EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT,
OESStandardDerivativesConstants::FRAGMENT_SHADER_DERIVATIVE_HINT_OES,
OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES,
];
// Param names that are implemented for glGetParameter in a WebGL 2.0 context
// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
// Example: https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/
const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL2: [GLenum; 1] =
[EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT];
// Param names that are implemented for glGetTexParameter in a WebGL 1.0 context
// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
// Example: https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/
const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL1: [GLenum; 1] =
[EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT];
// Param names that are implemented for glGetTexParameter in a WebGL 2.0 context
// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
// Example: https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/
const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL2: [GLenum; 1] =
[EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT];
// Param names that are implemented for glGetVertexAttrib in a WebGL 1.0 context
// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
// Example: https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
const DEFAULT_DISABLED_GET_VERTEX_ATTRIB_NAMES_WEBGL1: [GLenum; 1] =
[ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE];
/// WebGL features that are enabled/disabled by WebGL Extensions.
#[derive(JSTraceable, MallocSizeOf)]
struct WebGLExtensionFeatures {
gl_extensions: FnvHashSet<String>,
disabled_tex_types: FnvHashSet<GLenum>,
not_filterable_tex_types: FnvHashSet<GLenum>,
#[no_trace]
effective_tex_internal_formats: FnvHashMap<TexFormatType, TexFormat>,
/// WebGL Hint() targets enabled by extensions.
hint_targets: FnvHashSet<GLenum>,
/// WebGL GetParameter() names enabled by extensions.
disabled_get_parameter_names: FnvHashSet<GLenum>,
/// WebGL GetTexParameter() names enabled by extensions.
disabled_get_tex_parameter_names: FnvHashSet<GLenum>,
/// WebGL GetAttribVertex() names enabled by extensions.
disabled_get_vertex_attrib_names: FnvHashSet<GLenum>,
/// WebGL OES_element_index_uint extension.
element_index_uint_enabled: bool,
/// WebGL EXT_blend_minmax extension.
blend_minmax_enabled: bool,
/// WebGL supported texture compression formats enabled by extensions.
tex_compression_formats: FnvHashMap<GLenum, TexCompression>,
}
impl WebGLExtensionFeatures {
fn new(webgl_version: WebGLVersion) -> Self {
let (
disabled_tex_types,
disabled_get_parameter_names,
disabled_get_tex_parameter_names,
disabled_get_vertex_attrib_names,
not_filterable_tex_types,
element_index_uint_enabled,
blend_minmax_enabled,
) = match webgl_version {
WebGLVersion::WebGL1 => (
DEFAULT_DISABLED_TEX_TYPES_WEBGL1.iter().cloned().collect(),
DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL1
.iter()
.cloned()
.collect(),
DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL1
.iter()
.cloned()
.collect(),
DEFAULT_DISABLED_GET_VERTEX_ATTRIB_NAMES_WEBGL1
.iter()
.cloned()
.collect(),
DEFAULT_NOT_FILTERABLE_TEX_TYPES.iter().cloned().collect(),
false,
false,
),
WebGLVersion::WebGL2 => (
Default::default(),
DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL2
.iter()
.cloned()
.collect(),
DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL2
.iter()
.cloned()
.collect(),
Default::default(),
Default::default(),
true,
true,
),
};
Self {
gl_extensions: Default::default(),
disabled_tex_types,
not_filterable_tex_types,
effective_tex_internal_formats: Default::default(),
hint_targets: Default::default(),
disabled_get_parameter_names,
disabled_get_tex_parameter_names,
disabled_get_vertex_attrib_names,
element_index_uint_enabled,
blend_minmax_enabled,
tex_compression_formats: Default::default(),
}
}
}
/// Handles the list of implemented, supported and enabled WebGL extensions.
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) struct WebGLExtensions {
extensions: DomRefCell<HashMap<String, Box<dyn WebGLExtensionWrapper>>>,
features: DomRefCell<WebGLExtensionFeatures>,
#[no_trace]
webgl_version: WebGLVersion,
#[no_trace]
api_type: GlType,
#[no_trace]
glsl_version: WebGLSLVersion,
}
impl WebGLExtensions {
pub(crate) fn new(
webgl_version: WebGLVersion,
api_type: GlType,
glsl_version: WebGLSLVersion,
) -> WebGLExtensions {
Self {
extensions: DomRefCell::new(HashMap::new()),
features: DomRefCell::new(WebGLExtensionFeatures::new(webgl_version)),
webgl_version,
api_type,
glsl_version,
}
}
pub(crate) fn init_once<F>(&self, cb: F)
where
F: FnOnce() -> String,
{
if self.extensions.borrow().is_empty() {
let gl_str = cb();
self.features.borrow_mut().gl_extensions =
FnvHashSet::from_iter(gl_str.split(&[',', ' '][..]).map(|s| s.into()));
self.register_all_extensions();
}
}
pub(crate) fn register<T: 'static + WebGLExtension + JSTraceable + MallocSizeOf>(&self) {
let name = T::name().to_uppercase();
self.extensions
.borrow_mut()
.insert(name, Box::new(TypedWebGLExtensionWrapper::<T>::new()));
}
pub(crate) fn get_supported_extensions(&self) -> Vec<&'static str> {
self.extensions
.borrow()
.iter()
.filter(|v| {
if let WebGLExtensionSpec::Specific(version) = v.1.spec() {
if self.webgl_version != version {
return false;
}
}
v.1.is_supported(self)
})
.map(|ref v| v.1.name())
.collect()
}
pub(crate) fn get_or_init_extension(
&self,
name: &str,
ctx: &WebGLRenderingContext,
) -> Option<NonNull<JSObject>> {
let name = name.to_uppercase();
self.extensions.borrow().get(&name).and_then(|extension| {
if extension.is_supported(self) {
Some(extension.instance_or_init(ctx, self))
} else {
None
}
})
}
pub(crate) fn is_enabled<T>(&self) -> bool
where
T: 'static + WebGLExtension + JSTraceable + MallocSizeOf,
{
let name = T::name().to_uppercase();
self.extensions
.borrow()
.get(&name)
.is_some_and(|ext| ext.is_enabled())
}
pub(crate) fn supports_gl_extension(&self, name: &str) -> bool {
self.features.borrow().gl_extensions.contains(name)
}
pub(crate) fn supports_any_gl_extension(&self, names: &[&str]) -> bool {
let features = self.features.borrow();
names
.iter()
.any(|name| features.gl_extensions.contains(*name))
}
pub(crate) fn supports_all_gl_extension(&self, names: &[&str]) -> bool {
let features = self.features.borrow();
names
.iter()
.all(|name| features.gl_extensions.contains(*name))
}
pub(crate) fn enable_tex_type(&self, data_type: GLenum) {
self.features
.borrow_mut()
.disabled_tex_types
.remove(&data_type);
}
pub(crate) fn is_tex_type_enabled(&self, data_type: GLenum) -> bool {
!self
.features
.borrow()
.disabled_tex_types
.contains(&data_type)
}
pub(crate) fn add_effective_tex_internal_format(
&self,
source_internal_format: TexFormat,
source_data_type: u32,
effective_internal_format: TexFormat,
) {
let format = TexFormatType(source_internal_format, source_data_type);
self.features
.borrow_mut()
.effective_tex_internal_formats
.insert(format, effective_internal_format);
}
pub(crate) fn get_effective_tex_internal_format(
&self,
source_internal_format: TexFormat,
source_data_type: u32,
) -> TexFormat {
let format = TexFormatType(source_internal_format, source_data_type);
*(self
.features
.borrow()
.effective_tex_internal_formats
.get(&format)
.unwrap_or(&source_internal_format))
}
pub(crate) fn enable_filterable_tex_type(&self, text_data_type: GLenum) {
self.features
.borrow_mut()
.not_filterable_tex_types
.remove(&text_data_type);
}
pub(crate) fn is_filterable(&self, text_data_type: u32) -> bool {
!self
.features
.borrow()
.not_filterable_tex_types
.contains(&text_data_type)
}
pub(crate) fn enable_hint_target(&self, name: GLenum) {
self.features.borrow_mut().hint_targets.insert(name);
}
pub(crate) fn is_hint_target_enabled(&self, name: GLenum) -> bool {
self.features.borrow().hint_targets.contains(&name)
}
pub(crate) fn enable_get_parameter_name(&self, name: GLenum) {
self.features
.borrow_mut()
.disabled_get_parameter_names
.remove(&name);
}
pub(crate) fn is_get_parameter_name_enabled(&self, name: GLenum) -> bool {
!self
.features
.borrow()
.disabled_get_parameter_names
.contains(&name)
}
pub(crate) fn enable_get_tex_parameter_name(&self, name: GLenum) {
self.features
.borrow_mut()
.disabled_get_tex_parameter_names
.remove(&name);
}
pub(crate) fn is_get_tex_parameter_name_enabled(&self, name: GLenum) -> bool {
!self
.features
.borrow()
.disabled_get_tex_parameter_names
.contains(&name)
}
pub(crate) fn enable_get_vertex_attrib_name(&self, name: GLenum) {
self.features
.borrow_mut()
.disabled_get_vertex_attrib_names
.remove(&name);
}
pub(crate) fn is_get_vertex_attrib_name_enabled(&self, name: GLenum) -> bool {
!self
.features
.borrow()
.disabled_get_vertex_attrib_names
.contains(&name)
}
pub(crate) fn add_tex_compression_formats(&self, formats: &[TexCompression]) {
let formats: FnvHashMap<GLenum, TexCompression> = formats
.iter()
.map(|&compression| (compression.format.as_gl_constant(), compression))
.collect();
self.features
.borrow_mut()
.tex_compression_formats
.extend(formats.iter());
}
pub(crate) fn get_tex_compression_format(&self, format_id: GLenum) -> Option<TexCompression> {
self.features
.borrow()
.tex_compression_formats
.get(&format_id)
.cloned()
}
pub(crate) fn get_tex_compression_ids(&self) -> Vec<GLenum> {
self.features
.borrow()
.tex_compression_formats
.keys()
.copied()
.collect()
}
fn register_all_extensions(&self) {
self.register::<ext::angleinstancedarrays::ANGLEInstancedArrays>();
self.register::<ext::extblendminmax::EXTBlendMinmax>();
self.register::<ext::extcolorbufferhalffloat::EXTColorBufferHalfFloat>();
self.register::<ext::extfragdepth::EXTFragDepth>();
self.register::<ext::extshadertexturelod::EXTShaderTextureLod>();
self.register::<ext::exttexturefilteranisotropic::EXTTextureFilterAnisotropic>();
self.register::<ext::oeselementindexuint::OESElementIndexUint>();
self.register::<ext::oesstandardderivatives::OESStandardDerivatives>();
self.register::<ext::oestexturefloat::OESTextureFloat>();
self.register::<ext::oestexturefloatlinear::OESTextureFloatLinear>();
self.register::<ext::oestexturehalffloat::OESTextureHalfFloat>();
self.register::<ext::oestexturehalffloatlinear::OESTextureHalfFloatLinear>();
self.register::<ext::oesvertexarrayobject::OESVertexArrayObject>();
self.register::<ext::webglcolorbufferfloat::WEBGLColorBufferFloat>();
self.register::<ext::webglcompressedtextureetc1::WEBGLCompressedTextureETC1>();
self.register::<ext::webglcompressedtextures3tc::WEBGLCompressedTextureS3TC>();
}
pub(crate) fn enable_element_index_uint(&self) {
self.features.borrow_mut().element_index_uint_enabled = true;
}
pub(crate) fn is_element_index_uint_enabled(&self) -> bool {
self.features.borrow().element_index_uint_enabled
}
pub(crate) fn enable_blend_minmax(&self) {
self.features.borrow_mut().blend_minmax_enabled = true;
}
pub(crate) fn is_blend_minmax_enabled(&self) -> bool {
self.features.borrow().blend_minmax_enabled
}
pub(crate) fn is_float_buffer_renderable(&self) -> bool {
self.is_enabled::<WEBGLColorBufferFloat>() || self.is_enabled::<OESTextureFloat>()
}
pub(crate) fn is_min_glsl_version_satisfied(&self, min_glsl_version: WebGLSLVersion) -> bool {
self.glsl_version >= min_glsl_version
}
pub(crate) fn is_half_float_buffer_renderable(&self) -> bool {
self.is_enabled::<EXTColorBufferHalfFloat>() || self.is_enabled::<OESTextureHalfFloat>()
}
pub(crate) fn effective_type(&self, type_: u32) -> u32 {
if type_ == OESTextureHalfFloatConstants::HALF_FLOAT_OES &&
!self.supports_gl_extension("GL_OES_texture_half_float")
{
return glow::HALF_FLOAT;
}
type_
}
pub(crate) fn is_gles(&self) -> bool {
self.api_type == GlType::Gles
}
}
// Helper structs
#[derive(Eq, Hash, MallocSizeOf, PartialEq)]
struct TexFormatType(TexFormat, u32);

View file

@ -0,0 +1,13 @@
/* 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 https://mozilla.org/MPL/2.0/. */
pub(crate) mod ext;
mod extension;
#[allow(clippy::module_inception)]
mod extensions;
mod wrapper;
pub(crate) use self::ext::*;
pub(crate) use self::extension::{WebGLExtension, WebGLExtensionSpec};
pub(crate) use self::extensions::WebGLExtensions;

View file

@ -0,0 +1,88 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::ptr::NonNull;
use js::jsapi::JSObject;
use malloc_size_of::MallocSizeOf;
use super::{WebGLExtension, WebGLExtensionSpec, WebGLExtensions};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::MutNullableDom;
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::script_runtime::CanGc;
/// Trait used internally by WebGLExtensions to store and
/// handle the different WebGL extensions in a common list.
pub(crate) trait WebGLExtensionWrapper: JSTraceable + MallocSizeOf {
fn instance_or_init(
&self,
ctx: &WebGLRenderingContext,
ext: &WebGLExtensions,
) -> NonNull<JSObject>;
fn spec(&self) -> WebGLExtensionSpec;
fn is_supported(&self, _: &WebGLExtensions) -> bool;
fn is_enabled(&self) -> bool;
fn enable(&self, ext: &WebGLExtensions);
fn name(&self) -> &'static str;
}
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) struct TypedWebGLExtensionWrapper<T: WebGLExtension> {
extension: MutNullableDom<T::Extension>,
}
/// Typed WebGL Extension implementation.
/// Exposes the exact `MutNullableDom<DOMObject>` type defined by the extension.
impl<T: WebGLExtension> TypedWebGLExtensionWrapper<T> {
pub(crate) fn new() -> TypedWebGLExtensionWrapper<T> {
TypedWebGLExtensionWrapper {
extension: MutNullableDom::new(None),
}
}
}
impl<T> WebGLExtensionWrapper for TypedWebGLExtensionWrapper<T>
where
T: WebGLExtension + JSTraceable + MallocSizeOf + 'static,
{
#[allow(unsafe_code)]
fn instance_or_init(
&self,
ctx: &WebGLRenderingContext,
ext: &WebGLExtensions,
) -> NonNull<JSObject> {
let mut enabled = true;
let extension = self.extension.or_init(|| {
enabled = false;
T::new(ctx, CanGc::note())
});
if !enabled {
self.enable(ext);
}
unsafe { NonNull::new_unchecked(extension.reflector().get_jsobject().get()) }
}
fn spec(&self) -> WebGLExtensionSpec {
T::spec()
}
fn is_supported(&self, ext: &WebGLExtensions) -> bool {
self.is_enabled() || T::is_supported(ext)
}
fn is_enabled(&self) -> bool {
self.extension.get().is_some()
}
fn enable(&self, ext: &WebGLExtensions) {
T::enable(ext);
}
fn name(&self) -> &'static str {
T::name()
}
}

View file

@ -0,0 +1,26 @@
/* 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 https://mozilla.org/MPL/2.0/. */
pub(crate) mod extensions;
pub(crate) mod validations;
pub(crate) mod vertexarrayobject;
pub(crate) mod webgl2renderingcontext;
pub(crate) mod webglactiveinfo;
pub(crate) mod webglbuffer;
pub(crate) mod webglcontextevent;
pub(crate) mod webglframebuffer;
pub(crate) mod webglobject;
pub(crate) mod webglprogram;
pub(crate) mod webglquery;
pub(crate) mod webglrenderbuffer;
pub(crate) mod webglrenderingcontext;
pub(crate) mod webglsampler;
pub(crate) mod webglshader;
pub(crate) mod webglshaderprecisionformat;
pub(crate) mod webglsync;
pub(crate) mod webgltexture;
pub(crate) mod webgltransformfeedback;
pub(crate) mod webgluniformlocation;
pub(crate) mod webglvertexarrayobject;
pub(crate) mod webglvertexarrayobjectoes;

View file

@ -0,0 +1,14 @@
/* 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 https://mozilla.org/MPL/2.0/. */
pub(crate) trait WebGLValidator {
type ValidatedOutput;
type Error: ::std::error::Error;
fn validate(self) -> Result<Self::ValidatedOutput, Self::Error>;
}
pub(crate) mod tex_image_2d;
pub(crate) mod tex_image_3d;
pub(crate) mod types;

View file

@ -0,0 +1,798 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::{self, cmp, fmt};
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{TexDataType, TexFormat};
use super::WebGLValidator;
use super::types::TexImageTarget;
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl::webgltexture::{
ImageInfo, TexCompression, TexCompressionValidation, WebGLTexture,
};
/// The errors that the texImage* family of functions can generate.
#[derive(Debug)]
pub(crate) enum TexImageValidationError {
/// An invalid texture target was passed, it contains the invalid target.
InvalidTextureTarget(u32),
/// The passed texture target was not bound.
TextureTargetNotBound(u32),
/// Invalid texture dimensions were given.
InvalidCubicTextureDimensions,
/// A negative level was passed.
NegativeLevel,
/// A level too high to be allowed by the implementation was passed.
LevelTooHigh,
/// A level less than an allowed minimal value was passed.
LevelTooLow,
/// A depth less than an allowed minimal value was passed.
DepthTooLow,
/// A negative width and height was passed.
NegativeDimension,
/// A bigger with and height were passed than what the implementation
/// allows.
TextureTooBig,
/// An invalid data type was passed.
InvalidDataType,
/// An invalid texture format was passed.
InvalidTextureFormat,
/// Format did not match internal_format.
TextureFormatMismatch,
/// Invalid data type for the given format.
InvalidTypeForFormat,
/// Invalid border
InvalidBorder,
/// Expected a power of two texture.
NonPotTexture,
/// Unrecognized texture compression format.
InvalidCompressionFormat,
/// Invalid X/Y texture offset parameters.
InvalidOffsets,
}
impl std::error::Error for TexImageValidationError {}
impl fmt::Display for TexImageValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TexImageValidationError::*;
let description = match *self {
InvalidTextureTarget(texture_id) => &format!("Invalid texture target ({texture_id})"),
TextureTargetNotBound(texture_id) => &format!("Texture was not bound {texture_id}"),
InvalidCubicTextureDimensions => {
"Invalid dimensions were given for a cubic texture target"
},
NegativeLevel => "A negative level was passed",
LevelTooHigh => "Level too high",
LevelTooLow => "Level too low",
DepthTooLow => "Depth too low",
NegativeDimension => "Negative dimensions were passed",
TextureTooBig => "Dimensions given are too big",
InvalidDataType => "Invalid data type",
InvalidTextureFormat => "Invalid texture format",
TextureFormatMismatch => "Texture format mismatch",
InvalidTypeForFormat => "Invalid type for the given format",
InvalidBorder => "Invalid border",
NonPotTexture => "Expected a power of two texture",
InvalidCompressionFormat => "Unrecognized texture compression format",
InvalidOffsets => "Invalid X/Y texture offset parameters",
};
write!(f, "TexImageValidationError({})", description)
}
}
fn log2(n: u32) -> u32 {
31 - n.leading_zeros()
}
pub(crate) struct CommonTexImage2DValidator<'a> {
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
border: i32,
}
pub(crate) struct CommonTexImage2DValidatorResult {
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) level: u32,
pub(crate) internal_format: TexFormat,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) border: u32,
}
impl WebGLValidator for CommonTexImage2DValidator<'_> {
type Error = TexImageValidationError;
type ValidatedOutput = CommonTexImage2DValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
// GL_INVALID_ENUM is generated if target is not GL_TEXTURE_2D,
// GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
// GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
// GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
let target = match TexImageTarget::from_gl_constant(self.target) {
Some(target) if target.dimensions() == 2 => target,
_ => {
self.context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureTarget(self.target));
},
};
let texture = self
.context
.textures()
.active_texture_for_image_target(target);
let limits = self.context.limits();
let max_size = if target.is_cubic() {
limits.max_cube_map_tex_size
} else {
limits.max_tex_size
};
// If an attempt is made to call this function with no WebGLTexture
// bound, an INVALID_OPERATION error is generated.
let texture = match texture {
Some(texture) => texture,
None => {
self.context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureTargetNotBound(self.target));
},
};
// GL_INVALID_ENUM is generated if internal_format is not an accepted
// format.
let internal_format = match TexFormat::from_gl_constant(self.internal_format) {
Some(format)
if format.required_webgl_version() <= self.context.webgl_version() &&
format.usable_as_internal() =>
{
format
},
_ => {
self.context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
// GL_INVALID_VALUE is generated if target is one of the six cube map 2D
// image targets and the width and height parameters are not equal.
if target.is_cubic() && self.width != self.height {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::InvalidCubicTextureDimensions);
}
// GL_INVALID_VALUE is generated if level is less than 0.
if self.level < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeLevel);
}
// GL_INVALID_VALUE is generated if width or height is less than 0
if self.width < 0 || self.height < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeDimension);
}
let width = self.width as u32;
let height = self.height as u32;
let level = self.level as u32;
// GL_INVALID_VALUE is generated if width or height is greater than
// GL_MAX_TEXTURE_SIZE when target is GL_TEXTURE_2D or
// GL_MAX_CUBE_MAP_TEXTURE_SIZE when target is not GL_TEXTURE_2D.
if width > max_size >> level || height > max_size >> level {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::TextureTooBig);
}
// GL_INVALID_VALUE is generated if level is greater than zero and the
// texture is not power of two.
if level > 0 && (!width.is_power_of_two() || !height.is_power_of_two()) {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NonPotTexture);
}
// GL_INVALID_VALUE may be generated if level is greater than
// log_2(max), where max is the returned value of GL_MAX_TEXTURE_SIZE
// when target is GL_TEXTURE_2D or GL_MAX_CUBE_MAP_TEXTURE_SIZE when
// target is not GL_TEXTURE_2D.
if level > log2(max_size) {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::LevelTooHigh);
}
// GL_INVALID_VALUE is generated if border is not 0.
if self.border != 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::InvalidBorder);
}
Ok(CommonTexImage2DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
border: self.border as u32,
})
}
}
impl<'a> CommonTexImage2DValidator<'a> {
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
border: i32,
) -> Self {
CommonTexImage2DValidator {
context,
target,
level,
internal_format,
width,
height,
border,
}
}
}
pub(crate) struct TexImage2DValidator<'a> {
common_validator: CommonTexImage2DValidator<'a>,
format: u32,
data_type: u32,
}
impl<'a> TexImage2DValidator<'a> {
/// TODO: Move data validation logic here.
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
border: i32,
format: u32,
data_type: u32,
) -> Self {
TexImage2DValidator {
common_validator: CommonTexImage2DValidator::new(
context,
target,
level,
internal_format,
width,
height,
border,
),
format,
data_type,
}
}
}
/// The validated result of a TexImage2DValidator-validated call.
pub(crate) struct TexImage2DValidatorResult {
/// NB: width, height and level are already unsigned after validation.
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) level: u32,
pub(crate) border: u32,
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) internal_format: TexFormat,
pub(crate) format: TexFormat,
pub(crate) data_type: TexDataType,
}
/// TexImage2d validator as per
/// <https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml>
impl WebGLValidator for TexImage2DValidator<'_> {
type ValidatedOutput = TexImage2DValidatorResult;
type Error = TexImageValidationError;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.common_validator.context;
let CommonTexImage2DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
border,
} = self.common_validator.validate()?;
// GL_INVALID_ENUM is generated if format or data_type is not an
// accepted value.
let data_type = match TexDataType::from_gl_constant(self.data_type) {
Some(data_type) if data_type.required_webgl_version() <= context.webgl_version() => {
data_type
},
_ => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidDataType);
},
};
let format = match TexFormat::from_gl_constant(self.format) {
Some(format) if format.required_webgl_version() <= context.webgl_version() => format,
_ => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
// GL_INVALID_OPERATION is generated if format does not match
// internal_format.
if format != internal_format.to_unsized() {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
// NOTE: In WebGL2 data type check should be done based on the internal
// format, but in some functions this validator is called with the
// regular unsized format as parameter (eg. TexSubImage2D). For now
// it's left here to avoid duplication.
//
// GL_INVALID_OPERATION is generated if type is
// GL_UNSIGNED_SHORT_4_4_4_4 or GL_UNSIGNED_SHORT_5_5_5_1 and format is
// not GL_RGBA.
//
// GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_5_6_5
// and format is not GL_RGB.
match data_type {
TexDataType::UnsignedShort4444 | TexDataType::UnsignedShort5551
if format != TexFormat::RGBA =>
{
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
},
TexDataType::UnsignedShort565 if format != TexFormat::RGB => {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
},
_ => {},
}
Ok(TexImage2DValidatorResult {
width,
height,
level,
border,
texture,
target,
internal_format,
format,
data_type,
})
}
}
pub(crate) struct CommonCompressedTexImage2DValidator<'a> {
common_validator: CommonTexImage2DValidator<'a>,
data_len: usize,
}
impl<'a> CommonCompressedTexImage2DValidator<'a> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
width: i32,
height: i32,
border: i32,
compression_format: u32,
data_len: usize,
) -> Self {
CommonCompressedTexImage2DValidator {
common_validator: CommonTexImage2DValidator::new(
context,
target,
level,
compression_format,
width,
height,
border,
),
data_len,
}
}
}
pub(crate) struct CommonCompressedTexImage2DValidatorResult {
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) level: u32,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) compression: TexCompression,
}
fn valid_s3tc_dimension(level: u32, side_length: u32, block_size: u32) -> bool {
(side_length % block_size == 0) || (level > 0 && [0, 1, 2].contains(&side_length))
}
fn valid_compressed_data_len(
data_len: usize,
width: u32,
height: u32,
compression: &TexCompression,
) -> bool {
let block_width = compression.block_width as u32;
let block_height = compression.block_height as u32;
let required_blocks_hor = width.div_ceil(block_width);
let required_blocks_ver = height.div_ceil(block_height);
let required_blocks = required_blocks_hor * required_blocks_ver;
let required_bytes = required_blocks * compression.bytes_per_block as u32;
data_len == required_bytes as usize
}
fn is_subimage_blockaligned(
xoffset: u32,
yoffset: u32,
width: u32,
height: u32,
compression: &TexCompression,
tex_info: &ImageInfo,
) -> bool {
let block_width = compression.block_width as u32;
let block_height = compression.block_height as u32;
(xoffset % block_width == 0 && yoffset % block_height == 0) &&
(width % block_width == 0 || xoffset + width == tex_info.width()) &&
(height % block_height == 0 || yoffset + height == tex_info.height())
}
impl WebGLValidator for CommonCompressedTexImage2DValidator<'_> {
type Error = TexImageValidationError;
type ValidatedOutput = CommonCompressedTexImage2DValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.common_validator.context;
let CommonTexImage2DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
border: _,
} = self.common_validator.validate()?;
// GL_INVALID_ENUM is generated if internalformat is not a supported
// format returned in GL_COMPRESSED_TEXTURE_FORMATS.
let compression = context
.extension_manager()
.get_tex_compression_format(internal_format.as_gl_constant());
let compression = match compression {
Some(compression) => compression,
None => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidCompressionFormat);
},
};
// GL_INVALID_VALUE is generated if imageSize is not consistent with the
// format, dimensions, and contents of the specified compressed image data.
if !valid_compressed_data_len(self.data_len, width, height, &compression) {
context.webgl_error(InvalidValue);
return Err(TexImageValidationError::TextureFormatMismatch);
}
Ok(CommonCompressedTexImage2DValidatorResult {
texture,
target,
level,
width,
height,
compression,
})
}
}
pub(crate) struct CompressedTexImage2DValidator<'a> {
compression_validator: CommonCompressedTexImage2DValidator<'a>,
}
impl<'a> CompressedTexImage2DValidator<'a> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
width: i32,
height: i32,
border: i32,
compression_format: u32,
data_len: usize,
) -> Self {
CompressedTexImage2DValidator {
compression_validator: CommonCompressedTexImage2DValidator::new(
context,
target,
level,
width,
height,
border,
compression_format,
data_len,
),
}
}
}
impl WebGLValidator for CompressedTexImage2DValidator<'_> {
type Error = TexImageValidationError;
type ValidatedOutput = CommonCompressedTexImage2DValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.compression_validator.common_validator.context;
let CommonCompressedTexImage2DValidatorResult {
texture,
target,
level,
width,
height,
compression,
} = self.compression_validator.validate()?;
// GL_INVALID_OPERATION is generated if parameter combinations are not
// supported by the specific compressed internal format as specified
// in the specific texture compression extension.
let compression_valid = match compression.validation {
TexCompressionValidation::S3TC => {
let valid_width =
valid_s3tc_dimension(level, width, compression.block_width as u32);
let valid_height =
valid_s3tc_dimension(level, height, compression.block_height as u32);
valid_width && valid_height
},
TexCompressionValidation::None => true,
};
if !compression_valid {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
Ok(CommonCompressedTexImage2DValidatorResult {
texture,
target,
level,
width,
height,
compression,
})
}
}
pub(crate) struct CompressedTexSubImage2DValidator<'a> {
compression_validator: CommonCompressedTexImage2DValidator<'a>,
xoffset: i32,
yoffset: i32,
}
impl<'a> CompressedTexSubImage2DValidator<'a> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
xoffset: i32,
yoffset: i32,
width: i32,
height: i32,
compression_format: u32,
data_len: usize,
) -> Self {
CompressedTexSubImage2DValidator {
compression_validator: CommonCompressedTexImage2DValidator::new(
context,
target,
level,
width,
height,
0,
compression_format,
data_len,
),
xoffset,
yoffset,
}
}
}
impl WebGLValidator for CompressedTexSubImage2DValidator<'_> {
type Error = TexImageValidationError;
type ValidatedOutput = CommonCompressedTexImage2DValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.compression_validator.common_validator.context;
let CommonCompressedTexImage2DValidatorResult {
texture,
target,
level,
width,
height,
compression,
} = self.compression_validator.validate()?;
let tex_info = texture.image_info_for_target(&target, level).unwrap();
// GL_INVALID_VALUE is generated if:
// - xoffset or yoffset is less than 0
// - x offset plus the width is greater than the texture width
// - y offset plus the height is greater than the texture height
if self.xoffset < 0 ||
(self.xoffset as u32 + width) > tex_info.width() ||
self.yoffset < 0 ||
(self.yoffset as u32 + height) > tex_info.height()
{
context.webgl_error(InvalidValue);
return Err(TexImageValidationError::InvalidOffsets);
}
// GL_INVALID_OPERATION is generated if format does not match
// internal_format.
if compression.format != tex_info.internal_format() {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
// GL_INVALID_OPERATION is generated if parameter combinations are not
// supported by the specific compressed internal format as specified
// in the specific texture compression extension.
let compression_valid = match compression.validation {
TexCompressionValidation::S3TC => is_subimage_blockaligned(
self.xoffset as u32,
self.yoffset as u32,
width,
height,
&compression,
&tex_info,
),
TexCompressionValidation::None => true,
};
if !compression_valid {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
Ok(CommonCompressedTexImage2DValidatorResult {
texture,
target,
level,
width,
height,
compression,
})
}
}
pub(crate) struct TexStorageValidator<'a> {
common_validator: CommonTexImage2DValidator<'a>,
dimensions: u8,
depth: i32,
}
pub(crate) struct TexStorageValidatorResult {
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) levels: u32,
pub(crate) internal_format: TexFormat,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) depth: u32,
}
impl<'a> TexStorageValidator<'a> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
dimensions: u8,
target: u32,
levels: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
) -> Self {
TexStorageValidator {
common_validator: CommonTexImage2DValidator::new(
context,
target,
levels,
internal_format,
width,
height,
0,
),
dimensions,
depth,
}
}
}
impl WebGLValidator for TexStorageValidator<'_> {
type Error = TexImageValidationError;
type ValidatedOutput = TexStorageValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.common_validator.context;
let CommonTexImage2DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
border: _,
} = self.common_validator.validate()?;
if self.depth < 1 {
context.webgl_error(InvalidValue);
return Err(TexImageValidationError::DepthTooLow);
}
if level < 1 {
context.webgl_error(InvalidValue);
return Err(TexImageValidationError::LevelTooLow);
}
let dimensions_valid = match target {
TexImageTarget::Texture2D | TexImageTarget::CubeMap => self.dimensions == 2,
TexImageTarget::Texture3D | TexImageTarget::Texture2DArray => self.dimensions == 3,
_ => false,
};
if !dimensions_valid {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureTarget(
target.as_gl_constant(),
));
}
if !internal_format.is_sized() {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
}
let max_level = log2(cmp::max(width, height)) + 1;
if level > max_level {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::LevelTooHigh);
}
if texture.target().is_none() {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureTargetNotBound(
target.as_gl_constant(),
));
}
Ok(TexStorageValidatorResult {
texture,
target,
levels: level,
internal_format,
width,
height,
depth: self.depth as u32,
})
}
}

View file

@ -0,0 +1,311 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{TexDataType, TexFormat};
use js::jsapi::Type;
use js::typedarray::ArrayBufferView;
use super::WebGLValidator;
use super::tex_image_2d::TexImageValidationError;
use super::types::TexImageTarget;
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl::webgltexture::WebGLTexture;
fn log2(n: u32) -> u32 {
31 - n.leading_zeros()
}
pub(crate) struct CommonTexImage3DValidator<'a> {
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
border: i32,
}
pub(crate) struct CommonTexImage3DValidatorResult {
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) level: u32,
pub(crate) internal_format: TexFormat,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) depth: u32,
pub(crate) border: u32,
}
impl WebGLValidator for CommonTexImage3DValidator<'_> {
type Error = TexImageValidationError;
type ValidatedOutput = CommonTexImage3DValidatorResult;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
// GL_INVALID_ENUM is generated if target is not GL_TEXTURE_3D or GL_TEXTURE_2D_ARRAY.
let target = match TexImageTarget::from_gl_constant(self.target) {
Some(target) if target.dimensions() == 3 => target,
_ => {
self.context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureTarget(self.target));
},
};
let texture = self
.context
.textures()
.active_texture_for_image_target(target);
let limits = self.context.limits();
let max_size = limits.max_3d_tex_size;
// If an attempt is made to call this function with no WebGLTexture
// bound, an INVALID_OPERATION error is generated.
let texture = match texture {
Some(texture) => texture,
None => {
self.context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureTargetNotBound(self.target));
},
};
// GL_INVALID_ENUM is generated if internal_format is not an accepted
// format.
let internal_format = match TexFormat::from_gl_constant(self.internal_format) {
Some(format)
if format.required_webgl_version() <= self.context.webgl_version() &&
format.usable_as_internal() =>
{
format
},
_ => {
self.context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
// GL_INVALID_VALUE is generated if width, height, or depth is less than 0 or greater than
// GL_MAX_3D_TEXTURE_SIZE.
if self.width < 0 || self.height < 0 || self.depth < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeDimension);
}
let width = self.width as u32;
let height = self.height as u32;
let depth = self.depth as u32;
let level = self.level as u32;
if width > max_size || height > max_size || level > max_size {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::TextureTooBig);
}
// GL_INVALID_VALUE may be generated if level is greater than log2(max),
// where max is the returned value of GL_MAX_3D_TEXTURE_SIZE.
if self.level < 0 {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::NegativeLevel);
}
if level > log2(max_size) {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::LevelTooHigh);
}
// GL_INVALID_VALUE is generated if border is not 0 or 1.
if !(self.border == 0 || self.border == 1) {
self.context.webgl_error(InvalidValue);
return Err(TexImageValidationError::InvalidBorder);
}
Ok(CommonTexImage3DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
depth,
border: self.border as u32,
})
}
}
impl<'a> CommonTexImage3DValidator<'a> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
border: i32,
) -> Self {
CommonTexImage3DValidator {
context,
target,
level,
internal_format,
width,
height,
depth,
border,
}
}
}
pub(crate) struct TexImage3DValidator<'a> {
common_validator: CommonTexImage3DValidator<'a>,
format: u32,
data_type: u32,
data: &'a Option<ArrayBufferView>,
}
impl<'a> TexImage3DValidator<'a> {
/// TODO: Move data validation logic here.
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
context: &'a WebGLRenderingContext,
target: u32,
level: i32,
internal_format: u32,
width: i32,
height: i32,
depth: i32,
border: i32,
format: u32,
data_type: u32,
data: &'a Option<ArrayBufferView>,
) -> Self {
TexImage3DValidator {
common_validator: CommonTexImage3DValidator::new(
context,
target,
level,
internal_format,
width,
height,
depth,
border,
),
format,
data_type,
data,
}
}
}
/// The validated result of a TexImage2DValidator-validated call.
pub(crate) struct TexImage3DValidatorResult {
/// NB: width, height and level are already unsigned after validation.
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) depth: u32,
pub(crate) level: u32,
pub(crate) border: u32,
pub(crate) texture: DomRoot<WebGLTexture>,
pub(crate) target: TexImageTarget,
pub(crate) internal_format: TexFormat,
pub(crate) format: TexFormat,
pub(crate) data_type: TexDataType,
}
/// TexImage3d validator as per
/// <https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage3D.xml>
impl WebGLValidator for TexImage3DValidator<'_> {
type ValidatedOutput = TexImage3DValidatorResult;
type Error = TexImageValidationError;
fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
let context = self.common_validator.context;
let CommonTexImage3DValidatorResult {
texture,
target,
level,
internal_format,
width,
height,
depth,
border,
} = self.common_validator.validate()?;
// GL_INVALID_ENUM is generated if format is not an accepted format constant.
// Format constants other than GL_STENCIL_INDEX and GL_DEPTH_COMPONENT are accepted.
let data_type = match TexDataType::from_gl_constant(self.data_type) {
Some(data_type) if data_type.required_webgl_version() <= context.webgl_version() => {
data_type
},
_ => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidDataType);
},
};
let format = match TexFormat::from_gl_constant(self.format) {
Some(format) if format.required_webgl_version() <= context.webgl_version() => format,
_ => {
context.webgl_error(InvalidEnum);
return Err(TexImageValidationError::InvalidTextureFormat);
},
};
// GL_INVALID_OPERATION is generated if format does not match
// internal_format.
if format != internal_format.to_unsized() {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
if !internal_format.compatible_data_types().contains(&data_type) {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::TextureFormatMismatch);
}
// GL_INVALID_OPERATION is generated if target is GL_TEXTURE_3D and
// format is GL_DEPTH_COMPONENT, or GL_DEPTH_STENCIL.
if target == TexImageTarget::Texture3D &&
(format == TexFormat::DepthComponent || format == TexFormat::DepthStencil)
{
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
}
// If srcData is non-null, the type of srcData must match the type according to
// the above table; otherwise, generate an INVALID_OPERATION error.
let element_size = data_type.element_size();
let received_size = match self.data {
Some(buf) => match buf.get_array_type() {
Type::Int8 => 1,
Type::Uint8 => 1,
Type::Int16 => 2,
Type::Uint16 => 2,
Type::Int32 => 4,
Type::Uint32 => 4,
Type::Float32 => 4,
_ => {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
},
},
None => element_size,
};
if received_size != element_size {
context.webgl_error(InvalidOperation);
return Err(TexImageValidationError::InvalidTypeForFormat);
}
Ok(TexImage3DValidatorResult {
width,
height,
depth,
level,
border,
texture,
target,
internal_format,
format,
data_type,
})
}
}

View file

@ -0,0 +1,36 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::gl_enums;
use serde::{Deserialize, Serialize};
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
gl_enums! {
pub enum TexImageTarget {
Texture2D = constants::TEXTURE_2D,
Texture2DArray = constants::TEXTURE_2D_ARRAY,
Texture3D = constants::TEXTURE_3D,
CubeMap = constants::TEXTURE_CUBE_MAP,
CubeMapPositiveX = constants::TEXTURE_CUBE_MAP_POSITIVE_X,
CubeMapNegativeX = constants::TEXTURE_CUBE_MAP_NEGATIVE_X,
CubeMapPositiveY = constants::TEXTURE_CUBE_MAP_POSITIVE_Y,
CubeMapNegativeY = constants::TEXTURE_CUBE_MAP_NEGATIVE_Y,
CubeMapPositiveZ = constants::TEXTURE_CUBE_MAP_POSITIVE_Z,
CubeMapNegativeZ = constants::TEXTURE_CUBE_MAP_NEGATIVE_Z,
}
}
impl TexImageTarget {
pub(crate) fn is_cubic(&self) -> bool {
!matches!(*self, TexImageTarget::Texture2D)
}
pub(crate) fn dimensions(self) -> u8 {
match self {
TexImageTarget::Texture3D | TexImageTarget::Texture2DArray => 3,
_ => 2,
}
}
}

View file

@ -0,0 +1,316 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use canvas_traits::webgl::{
ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVersion, WebGLVertexArrayId,
};
use crate::dom::bindings::cell::{DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::root::{Dom, MutNullableDom};
use crate::dom::webgl::webglbuffer::WebGLBuffer;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
#[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct VertexArrayObject {
context: Dom<WebGLRenderingContext>,
#[no_trace]
id: Option<WebGLVertexArrayId>,
ever_bound: Cell<bool>,
is_deleted: Cell<bool>,
vertex_attribs: DomRefCell<Box<[VertexAttribData]>>,
element_array_buffer: MutNullableDom<WebGLBuffer>,
}
impl VertexArrayObject {
pub(crate) fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self {
let max_vertex_attribs = context.limits().max_vertex_attribs as usize;
Self {
context: Dom::from_ref(context),
id,
ever_bound: Default::default(),
is_deleted: Default::default(),
vertex_attribs: DomRefCell::new(vec![Default::default(); max_vertex_attribs].into()),
element_array_buffer: Default::default(),
}
}
pub(crate) fn id(&self) -> Option<WebGLVertexArrayId> {
self.id
}
pub(crate) fn is_deleted(&self) -> bool {
self.is_deleted.get()
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
assert!(self.id.is_some());
if self.is_deleted.get() {
return;
}
self.is_deleted.set(true);
let cmd = WebGLCommand::DeleteVertexArray(self.id.unwrap());
match operation_fallibility {
Operation::Fallible => self.context.send_command_ignored(cmd),
Operation::Infallible => self.context.send_command(cmd),
}
for attrib_data in &**self.vertex_attribs.borrow() {
if let Some(buffer) = attrib_data.buffer() {
buffer.decrement_attached_counter(operation_fallibility);
}
}
if let Some(buffer) = self.element_array_buffer.get() {
buffer.decrement_attached_counter(operation_fallibility);
}
}
pub(crate) fn ever_bound(&self) -> bool {
self.ever_bound.get()
}
pub(crate) fn set_ever_bound(&self) {
self.ever_bound.set(true);
}
pub(crate) fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> {
&self.element_array_buffer
}
pub(crate) fn get_vertex_attrib(&self, index: u32) -> Option<Ref<'_, VertexAttribData>> {
Ref::filter_map(self.vertex_attribs.borrow(), |attribs| {
attribs.get(index as usize)
})
.ok()
}
pub(crate) fn set_vertex_attrib_type(&self, index: u32, type_: u32) {
self.vertex_attribs.borrow_mut()[index as usize].type_ = type_;
}
pub(crate) fn vertex_attrib_pointer(
&self,
index: u32,
size: i32,
type_: u32,
normalized: bool,
stride: i32,
offset: i64,
) -> WebGLResult<()> {
let mut attribs = self.vertex_attribs.borrow_mut();
let data = attribs
.get_mut(index as usize)
.ok_or(WebGLError::InvalidValue)?;
if !(1..=4).contains(&size) {
return Err(WebGLError::InvalidValue);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#BUFFER_OFFSET_AND_STRIDE
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#VERTEX_STRIDE
if !(0..=255).contains(&stride) || offset < 0 {
return Err(WebGLError::InvalidValue);
}
let is_webgl2 = matches!(self.context.webgl_version(), WebGLVersion::WebGL2);
let bytes_per_component: i32 = match type_ {
constants::BYTE | constants::UNSIGNED_BYTE => 1,
constants::SHORT | constants::UNSIGNED_SHORT => 2,
constants::FLOAT => 4,
constants::INT | constants::UNSIGNED_INT if is_webgl2 => 4,
constants2::HALF_FLOAT if is_webgl2 => 2,
glow::FIXED if is_webgl2 => 4,
constants2::INT_2_10_10_10_REV | constants2::UNSIGNED_INT_2_10_10_10_REV
if is_webgl2 && size == 4 =>
{
4
},
_ => return Err(WebGLError::InvalidEnum),
};
if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 {
return Err(WebGLError::InvalidOperation);
}
let buffer = self.context.array_buffer();
match buffer {
Some(ref buffer) => buffer.increment_attached_counter(),
None if offset != 0 => {
// https://github.com/KhronosGroup/WebGL/pull/2228
return Err(WebGLError::InvalidOperation);
},
_ => {},
}
self.context.send_command(WebGLCommand::VertexAttribPointer(
index,
size,
type_,
normalized,
stride,
offset as u32,
));
if let Some(old) = data.buffer() {
old.decrement_attached_counter(Operation::Infallible);
}
*data = VertexAttribData {
enabled_as_array: data.enabled_as_array,
size: size as u8,
type_,
bytes_per_vertex: size as u8 * bytes_per_component as u8,
normalized,
stride: stride as u8,
offset: offset as u32,
buffer: buffer.map(|b| Dom::from_ref(&*b)),
divisor: data.divisor,
};
Ok(())
}
pub(crate) fn vertex_attrib_divisor(&self, index: u32, value: u32) {
self.vertex_attribs.borrow_mut()[index as usize].divisor = value;
}
pub(crate) fn enabled_vertex_attrib_array(&self, index: u32, value: bool) {
self.vertex_attribs.borrow_mut()[index as usize].enabled_as_array = value;
}
pub(crate) fn unbind_buffer(&self, buffer: &WebGLBuffer) {
for attrib in &mut **self.vertex_attribs.borrow_mut() {
if let Some(b) = attrib.buffer() {
if b.id() != buffer.id() {
continue;
}
b.decrement_attached_counter(Operation::Infallible);
}
attrib.buffer = None;
}
if self
.element_array_buffer
.get()
.is_some_and(|b| buffer == &*b)
{
buffer.decrement_attached_counter(Operation::Infallible);
self.element_array_buffer.set(None);
}
}
pub(crate) fn validate_for_draw(
&self,
required_len: u32,
instance_count: u32,
active_attribs: &[ActiveAttribInfo],
) -> WebGLResult<()> {
// TODO(nox): Cache limits per VAO.
let attribs = self.vertex_attribs.borrow();
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2
if attribs
.iter()
.any(|data| data.enabled_as_array && data.buffer.is_none())
{
return Err(WebGLError::InvalidOperation);
}
let mut has_active_attrib = false;
let mut has_divisor_0 = false;
for active_info in active_attribs {
let Some(location) = active_info.location else {
continue;
};
has_active_attrib = true;
let attrib = &attribs[location as usize];
if attrib.divisor == 0 {
has_divisor_0 = true;
}
if !attrib.enabled_as_array {
continue;
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6
if required_len > 0 && instance_count > 0 {
let max_vertices = attrib.max_vertices();
if attrib.divisor == 0 {
if max_vertices < required_len {
return Err(WebGLError::InvalidOperation);
}
} else if max_vertices
.checked_mul(attrib.divisor)
.is_some_and(|v| v < instance_count)
{
return Err(WebGLError::InvalidOperation);
}
}
}
if has_active_attrib && !has_divisor_0 {
return Err(WebGLError::InvalidOperation);
}
Ok(())
}
}
impl Drop for VertexArrayObject {
fn drop(&mut self) {
if self.id.is_some() {
self.delete(Operation::Fallible);
}
}
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct VertexAttribData {
pub(crate) enabled_as_array: bool,
pub(crate) size: u8,
pub(crate) type_: u32,
bytes_per_vertex: u8,
pub(crate) normalized: bool,
pub(crate) stride: u8,
pub(crate) offset: u32,
pub(crate) buffer: Option<Dom<WebGLBuffer>>,
pub(crate) divisor: u32,
}
impl Default for VertexAttribData {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
fn default() -> Self {
Self {
enabled_as_array: false,
size: 4,
type_: constants::FLOAT,
bytes_per_vertex: 16,
normalized: false,
stride: 0,
offset: 0,
buffer: None,
divisor: 0,
}
}
}
impl VertexAttribData {
pub(crate) fn buffer(&self) -> Option<&WebGLBuffer> {
self.buffer.as_deref()
}
pub(crate) fn max_vertices(&self) -> u32 {
let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset);
if capacity < self.bytes_per_vertex as u32 {
0
} else if self.stride == 0 {
capacity / self.bytes_per_vertex as u32
} else if self.stride < self.bytes_per_vertex {
(capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32
} else {
let mut max = capacity / self.stride as u32;
if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 {
max += 1;
}
max
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::WebGLActiveInfoBinding::WebGLActiveInfoMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLActiveInfo {
reflector_: Reflector,
size: i32,
// NOTE: `ty` stands for `type`, which is a reserved keyword
ty: u32,
name: DOMString,
}
impl WebGLActiveInfo {
fn new_inherited(size: i32, ty: u32, name: DOMString) -> WebGLActiveInfo {
WebGLActiveInfo {
reflector_: Reflector::new(),
size,
ty,
name,
}
}
pub(crate) fn new(
window: &Window,
size: i32,
ty: u32,
name: DOMString,
can_gc: CanGc,
) -> DomRoot<WebGLActiveInfo> {
reflect_dom_object(
Box::new(WebGLActiveInfo::new_inherited(size, ty, name)),
window,
can_gc,
)
}
}
impl WebGLActiveInfoMethods<crate::DomTypeHolder> for WebGLActiveInfo {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.11.1
fn Size(&self) -> i32 {
self.size
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.11.1
fn Type(&self) -> u32 {
self.ty
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.11.1
fn Name(&self) -> DOMString {
self.name.clone()
}
}

View file

@ -0,0 +1,265 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use std::cell::Cell;
use canvas_traits::webgl::{WebGLBufferId, WebGLCommand, WebGLError, WebGLResult, webgl_channel};
use dom_struct::dom_struct;
use ipc_channel::ipc;
use script_bindings::weakref::WeakRef;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
fn target_is_copy_buffer(target: u32) -> bool {
target == WebGL2RenderingContextConstants::COPY_READ_BUFFER ||
target == WebGL2RenderingContextConstants::COPY_WRITE_BUFFER
}
#[derive(JSTraceable, MallocSizeOf)]
struct DroppableWebGLBuffer {
#[no_trace]
id: WebGLBufferId,
marked_for_deletion: Cell<bool>,
attached_counter: Cell<u32>,
context: WeakRef<WebGLRenderingContext>,
}
impl DroppableWebGLBuffer {
pub(crate) fn new(
id: WebGLBufferId,
marked_for_deletion: Cell<bool>,
attached_counter: Cell<u32>,
context: WeakRef<WebGLRenderingContext>,
) -> Self {
Self {
id,
marked_for_deletion,
attached_counter,
context,
}
}
}
impl DroppableWebGLBuffer {
pub(crate) fn is_marked_for_deletion(&self) -> bool {
self.marked_for_deletion.get()
}
pub(crate) fn set_marked_for_deletion(&self, marked_for_deletion: bool) {
self.marked_for_deletion.set(marked_for_deletion);
}
pub(crate) fn get_attached_counter(&self) -> u32 {
self.attached_counter.get()
}
pub(crate) fn set_attached_counter(&self, attached_counter: u32) {
self.attached_counter.set(attached_counter);
}
pub(crate) fn id(&self) -> WebGLBufferId {
self.id
}
pub(crate) fn is_attached(&self) -> bool {
self.get_attached_counter() != 0
}
pub(crate) fn is_deleted(&self) -> bool {
self.is_marked_for_deletion() && !self.is_attached()
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
assert!(self.is_deleted());
if let Some(context) = self.context.root() {
let cmd = WebGLCommand::DeleteBuffer(self.id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
}
}
pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
if self.is_marked_for_deletion() {
return;
}
self.set_marked_for_deletion(true);
if self.is_deleted() {
self.delete(operation_fallibility);
}
}
}
impl Drop for DroppableWebGLBuffer {
fn drop(&mut self) {
self.mark_for_deletion(Operation::Fallible);
}
}
#[dom_struct]
pub(crate) struct WebGLBuffer {
webgl_object: WebGLObject,
/// The target to which this buffer was bound the first time
target: Cell<Option<u32>>,
capacity: Cell<usize>,
/// <https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glGetBufferParameteriv.xml>
usage: Cell<u32>,
droppable: DroppableWebGLBuffer,
}
impl WebGLBuffer {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLBufferId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
target: Default::default(),
capacity: Default::default(),
usage: Cell::new(WebGLRenderingContextConstants::STATIC_DRAW),
droppable: DroppableWebGLBuffer::new(
id,
Default::default(),
Default::default(),
WeakRef::new(context),
),
}
}
pub(crate) fn maybe_new(
context: &WebGLRenderingContext,
can_gc: CanGc,
) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateBuffer(sender));
receiver
.recv()
.unwrap()
.map(|id| WebGLBuffer::new(context, id, can_gc))
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: WebGLBufferId,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLBuffer::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
}
impl WebGLBuffer {
pub(crate) fn id(&self) -> WebGLBufferId {
self.droppable.id()
}
pub(crate) fn buffer_data(&self, target: u32, data: &[u8], usage: u32) -> WebGLResult<()> {
match usage {
WebGLRenderingContextConstants::STREAM_DRAW |
WebGLRenderingContextConstants::STATIC_DRAW |
WebGLRenderingContextConstants::DYNAMIC_DRAW |
WebGL2RenderingContextConstants::STATIC_READ |
WebGL2RenderingContextConstants::DYNAMIC_READ |
WebGL2RenderingContextConstants::STREAM_READ |
WebGL2RenderingContextConstants::STATIC_COPY |
WebGL2RenderingContextConstants::DYNAMIC_COPY |
WebGL2RenderingContextConstants::STREAM_COPY => (),
_ => return Err(WebGLError::InvalidEnum),
}
self.capacity.set(data.len());
self.usage.set(usage);
let (sender, receiver) = ipc::bytes_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::BufferData(target, receiver, usage));
sender.send(data).unwrap();
Ok(())
}
pub(crate) fn capacity(&self) -> usize {
self.capacity.get()
}
pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
self.droppable.mark_for_deletion(operation_fallibility);
}
fn delete(&self, operation_fallibility: Operation) {
self.droppable.delete(operation_fallibility);
}
pub(crate) fn is_marked_for_deletion(&self) -> bool {
self.droppable.is_marked_for_deletion()
}
fn get_attached_counter(&self) -> u32 {
self.droppable.get_attached_counter()
}
fn set_attached_counter(&self, attached_counter: u32) {
self.droppable.set_attached_counter(attached_counter);
}
pub(crate) fn is_deleted(&self) -> bool {
self.droppable.is_deleted()
}
pub(crate) fn target(&self) -> Option<u32> {
self.target.get()
}
fn can_bind_to(&self, new_target: u32) -> bool {
if let Some(current_target) = self.target.get() {
if [current_target, new_target]
.contains(&WebGLRenderingContextConstants::ELEMENT_ARRAY_BUFFER)
{
return target_is_copy_buffer(new_target) || new_target == current_target;
}
}
true
}
pub(crate) fn set_target_maybe(&self, target: u32) -> WebGLResult<()> {
if !self.can_bind_to(target) {
return Err(WebGLError::InvalidOperation);
}
if !target_is_copy_buffer(target) {
self.target.set(Some(target));
}
Ok(())
}
pub(crate) fn increment_attached_counter(&self) {
self.set_attached_counter(
self.get_attached_counter()
.checked_add(1)
.expect("refcount overflowed"),
);
}
pub(crate) fn decrement_attached_counter(&self, operation_fallibility: Operation) {
self.set_attached_counter(
self.get_attached_counter()
.checked_sub(1)
.expect("refcount underflowed"),
);
if self.is_deleted() {
self.delete(operation_fallibility);
}
}
pub(crate) fn usage(&self) -> u32 {
self.usage.get()
}
}

View file

@ -0,0 +1,118 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use js::rust::HandleObject;
use stylo_atoms::Atom;
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::WebGLContextEventBinding::{
WebGLContextEventInit, WebGLContextEventMethods,
};
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLContextEvent {
event: Event,
status_message: DOMString,
}
impl WebGLContextEventMethods<crate::DomTypeHolder> for WebGLContextEvent {
// https://registry.khronos.org/webgl/specs/latest/1.0/#5.15
fn Constructor(
window: &Window,
proto: Option<HandleObject>,
can_gc: CanGc,
type_: DOMString,
init: &WebGLContextEventInit,
) -> Fallible<DomRoot<WebGLContextEvent>> {
let status_message = match init.statusMessage.as_ref() {
Some(message) => message.clone(),
None => DOMString::new(),
};
let bubbles = EventBubbles::from(init.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.cancelable);
Ok(WebGLContextEvent::new_with_proto(
window,
proto,
Atom::from(type_),
bubbles,
cancelable,
status_message,
can_gc,
))
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15
fn StatusMessage(&self) -> DOMString {
self.status_message.clone()
}
// https://dom.spec.whatwg.org/#dom-event-istrusted
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
}
impl WebGLContextEvent {
fn new_inherited(status_message: DOMString) -> WebGLContextEvent {
WebGLContextEvent {
event: Event::new_inherited(),
status_message,
}
}
pub(crate) fn new(
window: &Window,
type_: Atom,
bubbles: EventBubbles,
cancelable: EventCancelable,
status_message: DOMString,
can_gc: CanGc,
) -> DomRoot<WebGLContextEvent> {
Self::new_with_proto(
window,
None,
type_,
bubbles,
cancelable,
status_message,
can_gc,
)
}
fn new_with_proto(
window: &Window,
proto: Option<HandleObject>,
type_: Atom,
bubbles: EventBubbles,
cancelable: EventCancelable,
status_message: DOMString,
can_gc: CanGc,
) -> DomRoot<WebGLContextEvent> {
let event = reflect_dom_object_with_proto(
Box::new(WebGLContextEvent::new_inherited(status_message)),
window,
proto,
can_gc,
);
{
let parent = event.upcast::<Event>();
parent.init_event(type_, bool::from(bubbles), bool::from(cancelable));
}
event
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use dom_struct::dom_struct;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::WebGLObjectBinding::WebGLObjectMethods;
use crate::dom::bindings::reflector::Reflector;
use crate::dom::bindings::root::Dom;
use crate::dom::bindings::str::USVString;
use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
#[dom_struct]
pub(crate) struct WebGLObject {
reflector_: Reflector,
context: Dom<WebGLRenderingContext>,
label: DomRefCell<USVString>,
}
impl WebGLObject {
pub(crate) fn new_inherited(context: &WebGLRenderingContext) -> WebGLObject {
WebGLObject {
reflector_: Reflector::new(),
context: Dom::from_ref(context),
label: DomRefCell::new(USVString::default()),
}
}
pub(crate) fn context(&self) -> &WebGLRenderingContext {
&self.context
}
}
impl WebGLObjectMethods<crate::DomTypeHolder> for WebGLObject {
/// <https://registry.khronos.org/webgl/specs/latest/1.0/#5.3>
fn Label(&self) -> USVString {
self.label.borrow().clone()
}
/// <https://registry.khronos.org/webgl/specs/latest/1.0/#5.3>
fn SetLabel(&self, value: USVString) {
*self.label.borrow_mut() = value;
}
}

View file

@ -0,0 +1,749 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use std::cell::Cell;
use canvas_traits::webgl::{
ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, WebGLCommand, WebGLError,
WebGLProgramId, WebGLResult, webgl_channel,
};
use dom_struct::dom_struct;
use fnv::FnvHashSet;
use crate::canvas_context::CanvasContext;
use crate::dom::bindings::cell::{DomRefCell, Ref};
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants2;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::webgl::webglactiveinfo::WebGLActiveInfo;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::dom::webgl::webglshader::WebGLShader;
use crate::dom::webgl::webgluniformlocation::WebGLUniformLocation;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLProgram {
webgl_object: WebGLObject,
#[no_trace]
id: WebGLProgramId,
is_in_use: Cell<bool>,
marked_for_deletion: Cell<bool>,
link_called: Cell<bool>,
linked: Cell<bool>,
link_generation: Cell<u64>,
fragment_shader: MutNullableDom<WebGLShader>,
vertex_shader: MutNullableDom<WebGLShader>,
#[no_trace]
active_attribs: DomRefCell<Box<[ActiveAttribInfo]>>,
#[no_trace]
active_uniforms: DomRefCell<Box<[ActiveUniformInfo]>>,
#[no_trace]
active_uniform_blocks: DomRefCell<Box<[ActiveUniformBlockInfo]>>,
transform_feedback_varyings_length: Cell<i32>,
transform_feedback_mode: Cell<i32>,
}
impl WebGLProgram {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLProgramId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
id,
is_in_use: Default::default(),
marked_for_deletion: Default::default(),
link_called: Default::default(),
linked: Default::default(),
link_generation: Default::default(),
fragment_shader: Default::default(),
vertex_shader: Default::default(),
active_attribs: DomRefCell::new(vec![].into()),
active_uniforms: DomRefCell::new(vec![].into()),
active_uniform_blocks: DomRefCell::new(vec![].into()),
transform_feedback_varyings_length: Default::default(),
transform_feedback_mode: Default::default(),
}
}
pub(crate) fn maybe_new(
context: &WebGLRenderingContext,
can_gc: CanGc,
) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateProgram(sender));
receiver
.recv()
.unwrap()
.map(|id| WebGLProgram::new(context, id, can_gc))
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: WebGLProgramId,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLProgram::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
}
impl WebGLProgram {
pub(crate) fn id(&self) -> WebGLProgramId {
self.id
}
/// glDeleteProgram
pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
if self.marked_for_deletion.get() {
return;
}
self.marked_for_deletion.set(true);
let cmd = WebGLCommand::DeleteProgram(self.id);
let context = self.upcast::<WebGLObject>().context();
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
if self.is_deleted() {
self.detach_shaders();
}
}
pub(crate) fn in_use(&self, value: bool) {
if self.is_in_use.get() == value {
return;
}
self.is_in_use.set(value);
if self.is_deleted() {
self.detach_shaders();
}
}
fn detach_shaders(&self) {
assert!(self.is_deleted());
if let Some(shader) = self.fragment_shader.get() {
shader.decrement_attached_counter();
self.fragment_shader.set(None);
}
if let Some(shader) = self.vertex_shader.get() {
shader.decrement_attached_counter();
self.vertex_shader.set(None);
}
}
pub(crate) fn is_in_use(&self) -> bool {
self.is_in_use.get()
}
pub(crate) fn is_marked_for_deletion(&self) -> bool {
self.marked_for_deletion.get()
}
pub(crate) fn is_deleted(&self) -> bool {
self.marked_for_deletion.get() && !self.is_in_use.get()
}
pub(crate) fn is_linked(&self) -> bool {
self.linked.get()
}
/// glLinkProgram
pub(crate) fn link(&self) -> WebGLResult<()> {
self.linked.set(false);
self.link_generation
.set(self.link_generation.get().checked_add(1).unwrap());
*self.active_attribs.borrow_mut() = Box::new([]);
*self.active_uniforms.borrow_mut() = Box::new([]);
*self.active_uniform_blocks.borrow_mut() = Box::new([]);
match self.fragment_shader.get() {
Some(ref shader) if shader.successfully_compiled() => {},
_ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
}
match self.vertex_shader.get() {
Some(ref shader) if shader.successfully_compiled() => {},
_ => return Ok(()), // callers use gl.LINK_STATUS to check link errors
}
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::LinkProgram(self.id, sender));
let link_info = receiver.recv().unwrap();
{
let mut used_locs = FnvHashSet::default();
let mut used_names = FnvHashSet::default();
for active_attrib in &*link_info.active_attribs {
let Some(location) = active_attrib.location else {
continue;
};
let columns = match active_attrib.type_ {
constants::FLOAT_MAT2 => 2,
constants::FLOAT_MAT3 => 3,
constants::FLOAT_MAT4 => 4,
_ => 1,
};
assert!(used_names.insert(&*active_attrib.name));
for column in 0..columns {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.31
if !used_locs.insert(location + column) {
return Ok(());
}
}
}
for active_uniform in &*link_info.active_uniforms {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.41
if !used_names.insert(&*active_uniform.base_name) {
return Ok(());
}
}
}
self.linked.set(link_info.linked);
self.link_called.set(true);
self.transform_feedback_varyings_length
.set(link_info.transform_feedback_length);
self.transform_feedback_mode
.set(link_info.transform_feedback_mode);
*self.active_attribs.borrow_mut() = link_info.active_attribs;
*self.active_uniforms.borrow_mut() = link_info.active_uniforms;
*self.active_uniform_blocks.borrow_mut() = link_info.active_uniform_blocks;
Ok(())
}
pub(crate) fn active_attribs(&self) -> Ref<'_, [ActiveAttribInfo]> {
Ref::map(self.active_attribs.borrow(), |attribs| &**attribs)
}
pub(crate) fn active_uniforms(&self) -> Ref<'_, [ActiveUniformInfo]> {
Ref::map(self.active_uniforms.borrow(), |uniforms| &**uniforms)
}
pub(crate) fn active_uniform_blocks(&self) -> Ref<'_, [ActiveUniformBlockInfo]> {
Ref::map(self.active_uniform_blocks.borrow(), |blocks| &**blocks)
}
/// glValidateProgram
pub(crate) fn validate(&self) -> WebGLResult<()> {
if self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::ValidateProgram(self.id));
Ok(())
}
/// glAttachShader
pub(crate) fn attach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
if self.is_deleted() || shader.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
let shader_slot = match shader.gl_type() {
constants::FRAGMENT_SHADER => &self.fragment_shader,
constants::VERTEX_SHADER => &self.vertex_shader,
_ => {
error!("detachShader: Unexpected shader type");
return Err(WebGLError::InvalidValue);
},
};
if shader_slot.get().is_some() {
return Err(WebGLError::InvalidOperation);
}
shader_slot.set(Some(shader));
shader.increment_attached_counter();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::AttachShader(self.id, shader.id()));
Ok(())
}
/// glDetachShader
pub(crate) fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
if self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
let shader_slot = match shader.gl_type() {
constants::FRAGMENT_SHADER => &self.fragment_shader,
constants::VERTEX_SHADER => &self.vertex_shader,
_ => return Err(WebGLError::InvalidValue),
};
match shader_slot.get() {
Some(ref attached_shader) if attached_shader.id() != shader.id() => {
return Err(WebGLError::InvalidOperation);
},
None => return Err(WebGLError::InvalidOperation),
_ => {},
}
shader_slot.set(None);
shader.decrement_attached_counter();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::DetachShader(self.id, shader.id()));
Ok(())
}
/// glBindAttribLocation
pub(crate) fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
if self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
if !validate_glsl_name(&name)? {
return Ok(());
}
if name.starts_with("gl_") {
return Err(WebGLError::InvalidOperation);
}
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::BindAttribLocation(
self.id,
index,
name.into(),
));
Ok(())
}
pub(crate) fn get_active_uniform(
&self,
index: u32,
can_gc: CanGc,
) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
if self.is_deleted() {
return Err(WebGLError::InvalidValue);
}
let uniforms = self.active_uniforms.borrow();
let data = uniforms
.get(index as usize)
.ok_or(WebGLError::InvalidValue)?;
Ok(WebGLActiveInfo::new(
self.global().as_window(),
data.size.unwrap_or(1),
data.type_,
data.name().into(),
can_gc,
))
}
/// glGetActiveAttrib
pub(crate) fn get_active_attrib(
&self,
index: u32,
can_gc: CanGc,
) -> WebGLResult<DomRoot<WebGLActiveInfo>> {
if self.is_deleted() {
return Err(WebGLError::InvalidValue);
}
let attribs = self.active_attribs.borrow();
let data = attribs
.get(index as usize)
.ok_or(WebGLError::InvalidValue)?;
Ok(WebGLActiveInfo::new(
self.global().as_window(),
data.size,
data.type_,
data.name.clone().into(),
can_gc,
))
}
/// glGetAttribLocation
pub(crate) fn get_attrib_location(&self, name: DOMString) -> WebGLResult<i32> {
if !self.is_linked() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
if !validate_glsl_name(&name)? {
return Ok(-1);
}
if name.starts_with("gl_") {
return Ok(-1);
}
let location = self
.active_attribs
.borrow()
.iter()
.find(|attrib| attrib.name == *name)
.and_then(|attrib| attrib.location.map(|l| l as i32))
.unwrap_or(-1);
Ok(location)
}
/// glGetFragDataLocation
pub(crate) fn get_frag_data_location(&self, name: DOMString) -> WebGLResult<i32> {
if !self.is_linked() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
if !validate_glsl_name(&name)? {
return Ok(-1);
}
if name.starts_with("gl_") {
return Ok(-1);
}
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::GetFragDataLocation(
self.id,
name.into(),
sender,
));
Ok(receiver.recv().unwrap())
}
/// glGetUniformLocation
pub(crate) fn get_uniform_location(
&self,
name: DOMString,
can_gc: CanGc,
) -> WebGLResult<Option<DomRoot<WebGLUniformLocation>>> {
if !self.is_linked() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
if !validate_glsl_name(&name)? {
return Ok(None);
}
if name.starts_with("gl_") {
return Ok(None);
}
let (size, type_) = {
let (base_name, array_index) = match parse_uniform_name(&name) {
Some((name, index)) if index.is_none_or(|i| i >= 0) => (name, index),
_ => return Ok(None),
};
let uniforms = self.active_uniforms.borrow();
match uniforms
.iter()
.find(|attrib| &*attrib.base_name == base_name)
{
Some(uniform) if array_index.is_none() || array_index < uniform.size => (
uniform
.size
.map(|size| size - array_index.unwrap_or_default()),
uniform.type_,
),
_ => return Ok(None),
}
};
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::GetUniformLocation(
self.id,
name.into(),
sender,
));
let location = receiver.recv().unwrap();
let context_id = self.upcast::<WebGLObject>().context().context_id();
Ok(Some(WebGLUniformLocation::new(
self.global().as_window(),
location,
context_id,
self.id,
self.link_generation.get(),
size,
type_,
can_gc,
)))
}
pub(crate) fn get_uniform_block_index(&self, name: DOMString) -> WebGLResult<u32> {
if !self.link_called.get() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
if !validate_glsl_name(&name)? {
return Ok(constants2::INVALID_INDEX);
}
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::GetUniformBlockIndex(
self.id,
name.into(),
sender,
));
Ok(receiver.recv().unwrap())
}
pub(crate) fn get_uniform_indices(&self, names: Vec<DOMString>) -> WebGLResult<Vec<u32>> {
if !self.link_called.get() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
let validation_errors = names
.iter()
.map(|name| validate_glsl_name(name))
.collect::<Vec<_>>();
let first_validation_error = validation_errors.iter().find(|result| result.is_err());
if let Some(error) = first_validation_error {
return Err(error.unwrap_err());
}
let names = names
.iter()
.map(|name| name.to_string())
.collect::<Vec<_>>();
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::GetUniformIndices(self.id, names, sender));
Ok(receiver.recv().unwrap())
}
pub(crate) fn get_active_uniforms(
&self,
indices: Vec<u32>,
pname: u32,
) -> WebGLResult<Vec<i32>> {
if !self.is_linked() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
match pname {
constants2::UNIFORM_TYPE |
constants2::UNIFORM_SIZE |
constants2::UNIFORM_BLOCK_INDEX |
constants2::UNIFORM_OFFSET |
constants2::UNIFORM_ARRAY_STRIDE |
constants2::UNIFORM_MATRIX_STRIDE |
constants2::UNIFORM_IS_ROW_MAJOR => {},
_ => return Err(WebGLError::InvalidEnum),
}
if indices.len() > self.active_uniforms.borrow().len() {
return Err(WebGLError::InvalidValue);
}
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::GetActiveUniforms(
self.id, indices, pname, sender,
));
Ok(receiver.recv().unwrap())
}
pub(crate) fn get_active_uniform_block_parameter(
&self,
block_index: u32,
pname: u32,
) -> WebGLResult<Vec<i32>> {
if !self.link_called.get() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
if block_index as usize >= self.active_uniform_blocks.borrow().len() {
return Err(WebGLError::InvalidValue);
}
match pname {
constants2::UNIFORM_BLOCK_BINDING |
constants2::UNIFORM_BLOCK_DATA_SIZE |
constants2::UNIFORM_BLOCK_ACTIVE_UNIFORMS |
constants2::UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES |
constants2::UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER |
constants2::UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER => {},
_ => return Err(WebGLError::InvalidEnum),
}
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>().context().send_command(
WebGLCommand::GetActiveUniformBlockParameter(self.id, block_index, pname, sender),
);
Ok(receiver.recv().unwrap())
}
pub(crate) fn get_active_uniform_block_name(&self, block_index: u32) -> WebGLResult<String> {
if !self.link_called.get() || self.is_deleted() {
return Err(WebGLError::InvalidOperation);
}
if block_index as usize >= self.active_uniform_blocks.borrow().len() {
return Err(WebGLError::InvalidValue);
}
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>().context().send_command(
WebGLCommand::GetActiveUniformBlockName(self.id, block_index, sender),
);
Ok(receiver.recv().unwrap())
}
pub(crate) fn bind_uniform_block(
&self,
block_index: u32,
block_binding: u32,
) -> WebGLResult<()> {
if block_index as usize >= self.active_uniform_blocks.borrow().len() {
return Err(WebGLError::InvalidValue);
}
let mut active_uniforms = self.active_uniforms.borrow_mut();
if active_uniforms.len() > block_binding as usize {
active_uniforms[block_binding as usize].bind_index = Some(block_binding);
}
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::UniformBlockBinding(
self.id,
block_index,
block_binding,
));
Ok(())
}
/// glGetProgramInfoLog
pub(crate) fn get_info_log(&self) -> WebGLResult<String> {
if self.is_deleted() {
return Err(WebGLError::InvalidValue);
}
if self.link_called.get() {
let shaders_compiled = match (self.fragment_shader.get(), self.vertex_shader.get()) {
(Some(fs), Some(vs)) => fs.successfully_compiled() && vs.successfully_compiled(),
_ => false,
};
if !shaders_compiled {
return Ok("One or more shaders failed to compile".to_string());
}
}
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::GetProgramInfoLog(self.id, sender));
Ok(receiver.recv().unwrap())
}
pub(crate) fn attached_shaders(&self) -> WebGLResult<Vec<DomRoot<WebGLShader>>> {
if self.marked_for_deletion.get() {
return Err(WebGLError::InvalidValue);
}
Ok(
match (self.vertex_shader.get(), self.fragment_shader.get()) {
(Some(vertex_shader), Some(fragment_shader)) => {
vec![vertex_shader, fragment_shader]
},
(Some(shader), None) | (None, Some(shader)) => vec![shader],
(None, None) => vec![],
},
)
}
pub(crate) fn link_generation(&self) -> u64 {
self.link_generation.get()
}
pub(crate) fn transform_feedback_varyings_length(&self) -> i32 {
self.transform_feedback_varyings_length.get()
}
pub(crate) fn transform_feedback_buffer_mode(&self) -> i32 {
self.transform_feedback_mode.get()
}
}
impl Drop for WebGLProgram {
fn drop(&mut self) {
self.in_use(false);
self.mark_for_deletion(Operation::Fallible);
}
}
fn validate_glsl_name(name: &str) -> WebGLResult<bool> {
if name.is_empty() {
return Ok(false);
}
if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {
return Err(WebGLError::InvalidValue);
}
for c in name.chars() {
validate_glsl_char(c)?;
}
if name.starts_with("webgl_") || name.starts_with("_webgl_") {
return Err(WebGLError::InvalidOperation);
}
Ok(true)
}
fn validate_glsl_char(c: char) -> WebGLResult<()> {
match c {
'a'..='z' |
'A'..='Z' |
'0'..='9' |
' ' |
'\t' |
'\u{11}' |
'\u{12}' |
'\r' |
'\n' |
'_' |
'.' |
'+' |
'-' |
'/' |
'*' |
'%' |
'<' |
'>' |
'[' |
']' |
'(' |
')' |
'{' |
'}' |
'^' |
'|' |
'&' |
'~' |
'=' |
'!' |
':' |
';' |
',' |
'?' => Ok(()),
_ => Err(WebGLError::InvalidValue),
}
}
fn parse_uniform_name(name: &str) -> Option<(&str, Option<i32>)> {
if !name.ends_with(']') {
return Some((name, None));
}
let bracket_pos = name[..name.len() - 1].rfind('[')?;
let index = name[(bracket_pos + 1)..(name.len() - 1)]
.parse::<i32>()
.ok()?;
Some((&name[..bracket_pos], Some(index)))
}
pub(crate) const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256;

View file

@ -0,0 +1,192 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{WebGLCommand, WebGLQueryId, webgl_channel};
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLQuery {
webgl_object: WebGLObject,
#[no_trace]
gl_id: WebGLQueryId,
gl_target: Cell<Option<u32>>,
marked_for_deletion: Cell<bool>,
query_result_available: Cell<Option<u32>>,
query_result: Cell<u32>,
}
impl WebGLQuery {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLQueryId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
gl_id: id,
gl_target: Cell::new(None),
marked_for_deletion: Cell::new(false),
query_result_available: Cell::new(None),
query_result: Cell::new(0),
}
}
pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::GenerateQuery(sender));
let id = receiver.recv().unwrap();
reflect_dom_object(
Box::new(Self::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
pub(crate) fn begin(
&self,
context: &WebGLRenderingContext,
target: u32,
) -> Result<(), canvas_traits::webgl::WebGLError> {
if self.marked_for_deletion.get() {
return Err(InvalidOperation);
}
if let Some(current_target) = self.gl_target.get() {
if current_target != target {
return Err(InvalidOperation);
}
}
match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE |
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => (),
_ => return Err(InvalidEnum),
}
self.gl_target.set(Some(target));
context.send_command(WebGLCommand::BeginQuery(target, self.gl_id));
Ok(())
}
pub(crate) fn end(
&self,
context: &WebGLRenderingContext,
target: u32,
) -> Result<(), canvas_traits::webgl::WebGLError> {
if self.marked_for_deletion.get() {
return Err(InvalidOperation);
}
if let Some(current_target) = self.gl_target.get() {
if current_target != target {
return Err(InvalidOperation);
}
}
match target {
constants::ANY_SAMPLES_PASSED |
constants::ANY_SAMPLES_PASSED_CONSERVATIVE |
constants::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN => (),
_ => return Err(InvalidEnum),
}
context.send_command(WebGLCommand::EndQuery(target));
Ok(())
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
if !self.marked_for_deletion.get() {
self.marked_for_deletion.set(true);
let context = self.upcast::<WebGLObject>().context();
let command = WebGLCommand::DeleteQuery(self.gl_id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(command),
Operation::Infallible => context.send_command(command),
}
}
}
pub(crate) fn is_valid(&self) -> bool {
!self.marked_for_deletion.get() && self.target().is_some()
}
pub(crate) fn target(&self) -> Option<u32> {
self.gl_target.get()
}
fn update_results(&self, context: &WebGLRenderingContext) {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::GetQueryState(
sender,
self.gl_id,
constants::QUERY_RESULT_AVAILABLE,
));
let is_available = receiver.recv().unwrap();
if is_available == 0 {
self.query_result_available.set(None);
return;
}
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::GetQueryState(
sender,
self.gl_id,
constants::QUERY_RESULT,
));
self.query_result.set(receiver.recv().unwrap());
self.query_result_available.set(Some(is_available));
}
#[rustfmt::skip]
pub(crate) fn get_parameter(
&self,
context: &WebGLRenderingContext,
pname: u32,
) -> Result<u32, canvas_traits::webgl::WebGLError> {
if !self.is_valid() {
return Err(InvalidOperation);
}
match pname {
constants::QUERY_RESULT |
constants::QUERY_RESULT_AVAILABLE => {},
_ => return Err(InvalidEnum),
}
if self.query_result_available.get().is_none() {
self.query_result_available.set(Some(0));
let this = Trusted::new(self);
let context = Trusted::new(context);
let task = task!(request_query_state: move || {
let this = this.root();
let context = context.root();
this.update_results(&context);
});
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task);
}
match pname {
constants::QUERY_RESULT => Ok(self.query_result.get()),
constants::QUERY_RESULT_AVAILABLE => Ok(self.query_result_available.get().unwrap()),
_ => unreachable!(),
}
}
}
impl Drop for WebGLQuery {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}

View file

@ -0,0 +1,290 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use std::cell::Cell;
use canvas_traits::webgl::{
GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult,
WebGLVersion, webgl_channel,
};
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::EXTColorBufferHalfFloatBinding::EXTColorBufferHalfFloatConstants;
use crate::dom::bindings::codegen::Bindings::WEBGLColorBufferFloatBinding::WEBGLColorBufferFloatConstants;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::webgl::webglframebuffer::WebGLFramebuffer;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLRenderbuffer {
webgl_object: WebGLObject,
#[no_trace]
id: WebGLRenderbufferId,
ever_bound: Cell<bool>,
is_deleted: Cell<bool>,
size: Cell<Option<(i32, i32)>>,
internal_format: Cell<Option<u32>>,
is_initialized: Cell<bool>,
attached_framebuffer: MutNullableDom<WebGLFramebuffer>,
}
impl WebGLRenderbuffer {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLRenderbufferId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
id,
ever_bound: Cell::new(false),
is_deleted: Cell::new(false),
internal_format: Cell::new(None),
size: Cell::new(None),
is_initialized: Cell::new(false),
attached_framebuffer: Default::default(),
}
}
pub(crate) fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateRenderbuffer(sender));
receiver
.recv()
.unwrap()
.map(|id| WebGLRenderbuffer::new(context, id, CanGc::note()))
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: WebGLRenderbufferId,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLRenderbuffer::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
}
impl WebGLRenderbuffer {
pub(crate) fn id(&self) -> WebGLRenderbufferId {
self.id
}
pub(crate) fn size(&self) -> Option<(i32, i32)> {
self.size.get()
}
pub(crate) fn internal_format(&self) -> u32 {
self.internal_format.get().unwrap_or(constants::RGBA4)
}
pub(crate) fn mark_initialized(&self) {
self.is_initialized.set(true);
}
pub(crate) fn is_initialized(&self) -> bool {
self.is_initialized.get()
}
pub(crate) fn bind(&self, target: u32) {
self.ever_bound.set(true);
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::BindRenderbuffer(target, Some(self.id)));
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
if !self.is_deleted.get() {
self.is_deleted.set(true);
let context = self.upcast::<WebGLObject>().context();
/*
If a renderbuffer object is deleted while its image is attached to one or more
attachment points in a currently bound framebuffer object, then it is as if
FramebufferRenderbuffer had been called, with a renderbuffer of zero, for each
attachment point to which this image was attached in that framebuffer object.
In other words,the renderbuffer image is first detached from all attachment points
in that frame-buffer object.
- GLES 3.0, 4.4.2.3, "Attaching Renderbuffer Images to a Framebuffer"
*/
if let Some(fb) = context.get_draw_framebuffer_slot().get() {
let _ = fb.detach_renderbuffer(self);
}
if let Some(fb) = context.get_read_framebuffer_slot().get() {
let _ = fb.detach_renderbuffer(self);
}
let cmd = WebGLCommand::DeleteRenderbuffer(self.id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
}
}
pub(crate) fn is_deleted(&self) -> bool {
self.is_deleted.get()
}
pub(crate) fn ever_bound(&self) -> bool {
self.ever_bound.get()
}
pub(crate) fn storage(
&self,
api_type: GlType,
sample_count: i32,
internal_format: u32,
width: i32,
height: i32,
) -> WebGLResult<()> {
let is_gles = api_type == GlType::Gles;
let webgl_version = self.upcast().context().webgl_version();
// Validate the internal_format, and save it for completeness
// validation.
let actual_format = match internal_format {
constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => {
internal_format
},
constants::R8 |
constants::R8UI |
constants::R8I |
constants::R16UI |
constants::R16I |
constants::R32UI |
constants::R32I |
constants::RG8 |
constants::RG8UI |
constants::RG8I |
constants::RG16UI |
constants::RG16I |
constants::RG32UI |
constants::RG32I |
constants::RGB8 |
constants::RGBA8 |
constants::SRGB8_ALPHA8 |
constants::RGB10_A2 |
constants::RGBA8UI |
constants::RGBA8I |
constants::RGB10_A2UI |
constants::RGBA16UI |
constants::RGBA16I |
constants::RGBA32I |
constants::RGBA32UI |
constants::DEPTH_COMPONENT24 |
constants::DEPTH_COMPONENT32F |
constants::DEPTH24_STENCIL8 |
constants::DEPTH32F_STENCIL8 => match webgl_version {
WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum),
_ => internal_format,
},
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.8
constants::DEPTH_STENCIL => constants::DEPTH24_STENCIL8,
constants::RGB5_A1 => {
// 16-bit RGBA formats are not supported on desktop GL.
if is_gles {
constants::RGB5_A1
} else {
constants::RGBA8
}
},
constants::RGB565 => {
// RGB565 is not supported on desktop GL.
if is_gles {
constants::RGB565
} else {
constants::RGB8
}
},
EXTColorBufferHalfFloatConstants::RGBA16F_EXT |
EXTColorBufferHalfFloatConstants::RGB16F_EXT => {
if !self
.upcast()
.context()
.extension_manager()
.is_half_float_buffer_renderable()
{
return Err(WebGLError::InvalidEnum);
}
internal_format
},
WEBGLColorBufferFloatConstants::RGBA32F_EXT => {
if !self
.upcast()
.context()
.extension_manager()
.is_float_buffer_renderable()
{
return Err(WebGLError::InvalidEnum);
}
internal_format
},
_ => return Err(WebGLError::InvalidEnum),
};
if webgl_version != WebGLVersion::WebGL1 {
let (sender, receiver) = webgl_channel().unwrap();
self.upcast::<WebGLObject>().context().send_command(
WebGLCommand::GetInternalFormatIntVec(
constants::RENDERBUFFER,
internal_format,
InternalFormatIntVec::Samples,
sender,
),
);
let samples = receiver.recv().unwrap();
if sample_count < 0 || sample_count > samples.first().cloned().unwrap_or(0) {
return Err(WebGLError::InvalidOperation);
}
}
self.internal_format.set(Some(internal_format));
self.is_initialized.set(false);
if let Some(fb) = self.attached_framebuffer.get() {
fb.update_status();
}
let command = match sample_count {
0 => WebGLCommand::RenderbufferStorage(
constants::RENDERBUFFER,
actual_format,
width,
height,
),
_ => WebGLCommand::RenderbufferStorageMultisample(
constants::RENDERBUFFER,
sample_count,
actual_format,
width,
height,
),
};
self.upcast::<WebGLObject>().context().send_command(command);
self.size.set(Some((width, height)));
Ok(())
}
pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
self.attached_framebuffer.set(Some(fb));
}
pub(crate) fn detach_from_framebuffer(&self) {
self.attached_framebuffer.set(None);
}
}
impl Drop for WebGLRenderbuffer {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,189 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use canvas_traits::webgl::WebGLError::*;
use canvas_traits::webgl::{WebGLCommand, WebGLSamplerId, webgl_channel};
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLSampler {
webgl_object: WebGLObject,
#[no_trace]
gl_id: WebGLSamplerId,
marked_for_deletion: Cell<bool>,
}
#[derive(Clone, Copy)]
pub(crate) enum WebGLSamplerValue {
Float(f32),
GLenum(u32),
}
fn validate_params(pname: u32, value: WebGLSamplerValue) -> bool {
match value {
WebGLSamplerValue::GLenum(value) => {
let allowed_values = match pname {
constants::TEXTURE_MIN_FILTER => &[
constants::NEAREST,
constants::LINEAR,
constants::NEAREST_MIPMAP_NEAREST,
constants::LINEAR_MIPMAP_NEAREST,
constants::NEAREST_MIPMAP_LINEAR,
constants::LINEAR_MIPMAP_LINEAR,
][..],
constants::TEXTURE_MAG_FILTER => &[constants::NEAREST, constants::LINEAR][..],
constants::TEXTURE_WRAP_R |
constants::TEXTURE_WRAP_S |
constants::TEXTURE_WRAP_T => &[
constants::CLAMP_TO_EDGE,
constants::MIRRORED_REPEAT,
constants::REPEAT,
][..],
constants::TEXTURE_COMPARE_MODE => {
&[constants::NONE, constants::COMPARE_REF_TO_TEXTURE][..]
},
constants::TEXTURE_COMPARE_FUNC => &[
constants::LEQUAL,
constants::GEQUAL,
constants::LESS,
constants::GREATER,
constants::EQUAL,
constants::NOTEQUAL,
constants::ALWAYS,
constants::NEVER,
][..],
_ => &[][..],
};
allowed_values.contains(&value)
},
WebGLSamplerValue::Float(_) => matches!(
pname,
constants::TEXTURE_MIN_LOD | constants::TEXTURE_MAX_LOD
),
}
}
impl WebGLSampler {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLSamplerId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
gl_id: id,
marked_for_deletion: Cell::new(false),
}
}
pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::GenerateSampler(sender));
let id = receiver.recv().unwrap();
reflect_dom_object(
Box::new(Self::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
if !self.marked_for_deletion.get() {
self.marked_for_deletion.set(true);
let command = WebGLCommand::DeleteSampler(self.gl_id);
let context = self.upcast::<WebGLObject>().context();
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(command),
Operation::Infallible => context.send_command(command),
}
}
}
pub(crate) fn is_valid(&self) -> bool {
!self.marked_for_deletion.get()
}
pub(crate) fn bind(
&self,
context: &WebGLRenderingContext,
unit: u32,
) -> Result<(), canvas_traits::webgl::WebGLError> {
if !self.is_valid() {
return Err(InvalidOperation);
}
context.send_command(WebGLCommand::BindSampler(unit, self.gl_id));
Ok(())
}
pub(crate) fn set_parameter(
&self,
context: &WebGLRenderingContext,
pname: u32,
value: WebGLSamplerValue,
) -> Result<(), canvas_traits::webgl::WebGLError> {
if !self.is_valid() {
return Err(InvalidOperation);
}
if !validate_params(pname, value) {
return Err(InvalidEnum);
}
let command = match value {
WebGLSamplerValue::GLenum(value) => {
WebGLCommand::SetSamplerParameterInt(self.gl_id, pname, value as i32)
},
WebGLSamplerValue::Float(value) => {
WebGLCommand::SetSamplerParameterFloat(self.gl_id, pname, value)
},
};
context.send_command(command);
Ok(())
}
pub(crate) fn get_parameter(
&self,
context: &WebGLRenderingContext,
pname: u32,
) -> Result<WebGLSamplerValue, canvas_traits::webgl::WebGLError> {
if !self.is_valid() {
return Err(InvalidOperation);
}
match pname {
constants::TEXTURE_MIN_FILTER |
constants::TEXTURE_MAG_FILTER |
constants::TEXTURE_WRAP_R |
constants::TEXTURE_WRAP_S |
constants::TEXTURE_WRAP_T |
constants::TEXTURE_COMPARE_FUNC |
constants::TEXTURE_COMPARE_MODE => {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::GetSamplerParameterInt(
self.gl_id, pname, sender,
));
Ok(WebGLSamplerValue::GLenum(receiver.recv().unwrap() as u32))
},
constants::TEXTURE_MIN_LOD | constants::TEXTURE_MAX_LOD => {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::GetSamplerParameterFloat(
self.gl_id, pname, sender,
));
Ok(WebGLSamplerValue::Float(receiver.recv().unwrap()))
},
_ => Err(InvalidEnum),
}
}
}
impl Drop for WebGLSampler {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}

View file

@ -0,0 +1,293 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use std::cell::Cell;
use std::os::raw::c_int;
use std::sync::Once;
use canvas_traits::webgl::{
GLLimits, GlType, WebGLCommand, WebGLError, WebGLResult, WebGLSLVersion, WebGLShaderId,
WebGLVersion, webgl_channel,
};
use dom_struct::dom_struct;
use mozangle::shaders::{BuiltInResources, CompileOptions, Output, ShaderValidator};
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::webgl::extensions::WebGLExtensions;
use crate::dom::webgl::extensions::extfragdepth::EXTFragDepth;
use crate::dom::webgl::extensions::extshadertexturelod::EXTShaderTextureLod;
use crate::dom::webgl::extensions::oesstandardderivatives::OESStandardDerivatives;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub(crate) enum ShaderCompilationStatus {
NotCompiled,
Succeeded,
Failed,
}
#[dom_struct]
pub(crate) struct WebGLShader {
webgl_object: WebGLObject,
#[no_trace]
id: WebGLShaderId,
gl_type: u32,
source: DomRefCell<DOMString>,
info_log: DomRefCell<DOMString>,
marked_for_deletion: Cell<bool>,
attached_counter: Cell<u32>,
compilation_status: Cell<ShaderCompilationStatus>,
}
static GLSLANG_INITIALIZATION: Once = Once::new();
impl WebGLShader {
fn new_inherited(context: &WebGLRenderingContext, id: WebGLShaderId, shader_type: u32) -> Self {
GLSLANG_INITIALIZATION.call_once(|| ::mozangle::shaders::initialize().unwrap());
Self {
webgl_object: WebGLObject::new_inherited(context),
id,
gl_type: shader_type,
source: Default::default(),
info_log: Default::default(),
marked_for_deletion: Cell::new(false),
attached_counter: Cell::new(0),
compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled),
}
}
pub(crate) fn maybe_new(
context: &WebGLRenderingContext,
shader_type: u32,
) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateShader(shader_type, sender));
receiver
.recv()
.unwrap()
.map(|id| WebGLShader::new(context, id, shader_type, CanGc::note()))
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: WebGLShaderId,
shader_type: u32,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLShader::new_inherited(context, id, shader_type)),
&*context.global(),
can_gc,
)
}
}
impl WebGLShader {
pub(crate) fn id(&self) -> WebGLShaderId {
self.id
}
pub(crate) fn gl_type(&self) -> u32 {
self.gl_type
}
/// glCompileShader
pub(crate) fn compile(
&self,
api_type: GlType,
webgl_version: WebGLVersion,
glsl_version: WebGLSLVersion,
limits: &GLLimits,
ext: &WebGLExtensions,
) -> WebGLResult<()> {
if self.marked_for_deletion.get() && !self.is_attached() {
return Err(WebGLError::InvalidValue);
}
if self.compilation_status.get() != ShaderCompilationStatus::NotCompiled {
debug!("Compiling already compiled shader {}", self.id);
}
let source = self.source.borrow();
let mut params = BuiltInResources {
MaxVertexAttribs: limits.max_vertex_attribs as c_int,
MaxVertexUniformVectors: limits.max_vertex_uniform_vectors as c_int,
MaxVertexTextureImageUnits: limits.max_vertex_texture_image_units as c_int,
MaxCombinedTextureImageUnits: limits.max_combined_texture_image_units as c_int,
MaxTextureImageUnits: limits.max_texture_image_units as c_int,
MaxFragmentUniformVectors: limits.max_fragment_uniform_vectors as c_int,
MaxVertexOutputVectors: limits.max_vertex_output_vectors as c_int,
MaxFragmentInputVectors: limits.max_fragment_input_vectors as c_int,
MaxVaryingVectors: limits.max_varying_vectors as c_int,
OES_standard_derivatives: ext.is_enabled::<OESStandardDerivatives>() as c_int,
EXT_shader_texture_lod: ext.is_enabled::<EXTShaderTextureLod>() as c_int,
EXT_frag_depth: ext.is_enabled::<EXTFragDepth>() as c_int,
FragmentPrecisionHigh: 1,
..Default::default()
};
if webgl_version == WebGLVersion::WebGL2 {
params.MinProgramTexelOffset = limits.min_program_texel_offset as c_int;
params.MaxProgramTexelOffset = limits.max_program_texel_offset as c_int;
params.MaxDrawBuffers = limits.max_draw_buffers as c_int;
}
let validator = match webgl_version {
WebGLVersion::WebGL1 => {
let output_format = if api_type == GlType::Gles {
Output::Essl
} else {
Output::Glsl
};
ShaderValidator::for_webgl(self.gl_type, output_format, &params).unwrap()
},
WebGLVersion::WebGL2 => {
let output_format = if api_type == GlType::Gles {
Output::Essl
} else {
match (glsl_version.major, glsl_version.minor) {
(1, 30) => Output::Glsl130,
(1, 40) => Output::Glsl140,
(1, 50) => Output::Glsl150Core,
(3, 30) => Output::Glsl330Core,
(4, 0) => Output::Glsl400Core,
(4, 10) => Output::Glsl410Core,
(4, 20) => Output::Glsl420Core,
(4, 30) => Output::Glsl430Core,
(4, 40) => Output::Glsl440Core,
(4, _) => Output::Glsl450Core,
_ => Output::Glsl140,
}
};
ShaderValidator::for_webgl2(self.gl_type, output_format, &params).unwrap()
},
};
// Replicating
// https://searchfox.org/mozilla-esr115/rev/f1fb0868dc63b89ccf9eea157960d1ec27fb55a2/dom/canvas/WebGLShaderValidator.cpp#29
let mut options = CompileOptions::mozangle();
options.set_variables(1);
options.set_enforcePackingRestrictions(1);
options.set_objectCode(1);
options.set_initGLPosition(1);
options.set_initializeUninitializedLocals(1);
options.set_initOutputVariables(1);
options.set_limitExpressionComplexity(1);
options.set_limitCallStackDepth(1);
if cfg!(target_os = "macos") {
options.set_removeInvariantAndCentroidForESSL3(1);
// Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
// https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
options.set_unfoldShortCircuit(1);
// Work around that Mac drivers handle struct scopes incorrectly.
options.set_regenerateStructNames(1);
// TODO: Only apply this workaround to Intel hardware
// Work around that Intel drivers on Mac OSX handle for-loop incorrectly.
options.set_addAndTrueToLoopCondition(1);
options.set_rewriteTexelFetchOffsetToTexelFetch(1);
} else {
// We want to do this everywhere, but to do this on Mac, we need
// to do it only on Mac OSX > 10.6 as this causes the shader
// compiler in 10.6 to crash
options.set_clampIndirectArrayBounds(1);
}
match validator.compile(&[&source], options) {
Ok(()) => {
let translated_source = validator.object_code();
debug!("Shader translated: {}", translated_source);
// NOTE: At this point we should be pretty sure that the compilation in the paint thread
// will succeed.
// It could be interesting to retrieve the info log from the paint thread though
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::CompileShader(self.id, translated_source));
self.compilation_status
.set(ShaderCompilationStatus::Succeeded);
},
Err(error) => {
self.compilation_status.set(ShaderCompilationStatus::Failed);
debug!("Shader {} compilation failed: {}", self.id, error);
},
}
*self.info_log.borrow_mut() = validator.info_log().into();
Ok(())
}
/// Mark this shader as deleted (if it wasn't previously)
/// and delete it as if calling glDeleteShader.
/// Currently does not check if shader is attached
pub(crate) fn mark_for_deletion(&self, operation_fallibility: Operation) {
if !self.marked_for_deletion.get() {
self.marked_for_deletion.set(true);
let context = self.upcast::<WebGLObject>().context();
let cmd = WebGLCommand::DeleteShader(self.id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
}
}
pub(crate) fn is_marked_for_deletion(&self) -> bool {
self.marked_for_deletion.get()
}
pub(crate) fn is_deleted(&self) -> bool {
self.marked_for_deletion.get() && !self.is_attached()
}
pub(crate) fn is_attached(&self) -> bool {
self.attached_counter.get() > 0
}
pub(crate) fn increment_attached_counter(&self) {
self.attached_counter.set(self.attached_counter.get() + 1);
}
pub(crate) fn decrement_attached_counter(&self) {
assert!(self.attached_counter.get() > 0);
self.attached_counter.set(self.attached_counter.get() - 1);
}
/// glGetShaderInfoLog
pub(crate) fn info_log(&self) -> DOMString {
self.info_log.borrow().clone()
}
/// Get the shader source
pub(crate) fn source(&self) -> DOMString {
self.source.borrow().clone()
}
/// glShaderSource
pub(crate) fn set_source(&self, source: DOMString) {
*self.source.borrow_mut() = source;
}
pub(crate) fn successfully_compiled(&self) -> bool {
self.compilation_status.get() == ShaderCompilationStatus::Succeeded
}
}
impl Drop for WebGLShader {
fn drop(&mut self) {
self.mark_for_deletion(Operation::Fallible);
}
}

View file

@ -0,0 +1,66 @@
/* 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 https://mozilla.org/MPL/2.0/. */
#![allow(dead_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::WebGLShaderPrecisionFormatBinding::WebGLShaderPrecisionFormatMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLShaderPrecisionFormat {
reflector_: Reflector,
range_min: i32,
range_max: i32,
precision: i32,
}
impl WebGLShaderPrecisionFormat {
fn new_inherited(range_min: i32, range_max: i32, precision: i32) -> WebGLShaderPrecisionFormat {
WebGLShaderPrecisionFormat {
reflector_: Reflector::new(),
range_min,
range_max,
precision,
}
}
pub(crate) fn new(
window: &Window,
range_min: i32,
range_max: i32,
precision: i32,
can_gc: CanGc,
) -> DomRoot<WebGLShaderPrecisionFormat> {
reflect_dom_object(
Box::new(WebGLShaderPrecisionFormat::new_inherited(
range_min, range_max, precision,
)),
window,
can_gc,
)
}
}
impl WebGLShaderPrecisionFormatMethods<crate::DomTypeHolder> for WebGLShaderPrecisionFormat {
// https://www.khronos.org/registry/webgl/specs/1.0/#5.12.1
fn RangeMin(&self) -> i32 {
self.range_min
}
// https://www.khronos.org/registry/webgl/specs/1.0/#5.12.1
fn RangeMax(&self) -> i32 {
self.range_max
}
// https://www.khronos.org/registry/webgl/specs/1.0/#5.12.1
fn Precision(&self) -> i32 {
self.precision
}
}

View file

@ -0,0 +1,137 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use canvas_traits::webgl::{WebGLCommand, WebGLSyncId, webgl_channel};
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLSync {
webgl_object: WebGLObject,
#[no_trace]
sync_id: WebGLSyncId,
marked_for_deletion: Cell<bool>,
client_wait_status: Cell<Option<u32>>,
sync_status: Cell<Option<u32>>,
}
impl WebGLSync {
fn new_inherited(context: &WebGLRenderingContext, sync_id: WebGLSyncId) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
sync_id,
marked_for_deletion: Cell::new(false),
client_wait_status: Cell::new(None),
sync_status: Cell::new(None),
}
}
pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::FenceSync(sender));
let sync_id = receiver.recv().unwrap();
reflect_dom_object(
Box::new(WebGLSync::new_inherited(context, sync_id)),
&*context.global(),
can_gc,
)
}
}
impl WebGLSync {
pub(crate) fn client_wait_sync(
&self,
context: &WebGLRenderingContext,
flags: u32,
timeout: u64,
) -> Option<u32> {
match self.client_wait_status.get() {
Some(constants::TIMEOUT_EXPIRED) | Some(constants::WAIT_FAILED) | None => {
let this = Trusted::new(self);
let context = Trusted::new(context);
let task = task!(request_client_wait_status: move || {
let this = this.root();
let context = context.root();
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::ClientWaitSync(
this.sync_id,
flags,
timeout,
sender,
));
this.client_wait_status.set(Some(receiver.recv().unwrap()));
});
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task);
},
_ => {},
}
self.client_wait_status.get()
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
if self.is_valid() {
self.marked_for_deletion.set(true);
let context = self.upcast::<WebGLObject>().context();
let cmd = WebGLCommand::DeleteSync(self.sync_id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
}
}
pub(crate) fn get_sync_status(
&self,
pname: u32,
context: &WebGLRenderingContext,
) -> Option<u32> {
match self.sync_status.get() {
Some(constants::UNSIGNALED) | None => {
let this = Trusted::new(self);
let context = Trusted::new(context);
let task = task!(request_sync_status: move || {
let this = this.root();
let context = context.root();
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::GetSyncParameter(this.sync_id, pname, sender));
this.sync_status.set(Some(receiver.recv().unwrap()));
});
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task);
},
_ => {},
}
self.sync_status.get()
}
pub(crate) fn is_valid(&self) -> bool {
!self.marked_for_deletion.get()
}
pub(crate) fn id(&self) -> WebGLSyncId {
self.sync_id
}
}
impl Drop for WebGLSync {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}

View file

@ -0,0 +1,660 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use std::cell::Cell;
use std::cmp;
use canvas_traits::webgl::{
TexDataType, TexFormat, TexParameter, TexParameterBool, TexParameterInt, WebGLCommand,
WebGLError, WebGLResult, WebGLTextureId, WebGLVersion, webgl_channel,
};
use dom_struct::dom_struct;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::EXTTextureFilterAnisotropicBinding::EXTTextureFilterAnisotropicConstants;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
#[cfg(feature = "webxr")]
use crate::dom::bindings::root::Dom;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::webgl::validations::types::TexImageTarget;
use crate::dom::webgl::webglframebuffer::WebGLFramebuffer;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
#[cfg(feature = "webxr")]
use crate::dom::xrsession::XRSession;
use crate::script_runtime::CanGc;
pub(crate) enum TexParameterValue {
Float(f32),
Int(i32),
Bool(bool),
}
// Textures generated for WebXR are owned by the WebXR device, not by the WebGL thread
// so the GL texture should not be deleted when the texture is garbage collected.
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
#[derive(JSTraceable, MallocSizeOf)]
enum WebGLTextureOwner {
WebGL,
#[cfg(feature = "webxr")]
WebXR(Dom<XRSession>),
}
const MAX_LEVEL_COUNT: usize = 31;
const MAX_FACE_COUNT: usize = 6;
#[dom_struct]
pub(crate) struct WebGLTexture {
webgl_object: WebGLObject,
#[no_trace]
id: WebGLTextureId,
/// The target to which this texture was bound the first time
target: Cell<Option<u32>>,
is_deleted: Cell<bool>,
owner: WebGLTextureOwner,
/// Stores information about mipmap levels and cubemap faces.
#[ignore_malloc_size_of = "Arrays are cumbersome"]
image_info_array: DomRefCell<[Option<ImageInfo>; MAX_LEVEL_COUNT * MAX_FACE_COUNT]>,
/// Face count can only be 1 or 6
face_count: Cell<u8>,
base_mipmap_level: u32,
// Store information for min and mag filters
min_filter: Cell<u32>,
mag_filter: Cell<u32>,
/// Framebuffer that this texture is attached to.
attached_framebuffer: MutNullableDom<WebGLFramebuffer>,
/// Number of immutable levels.
immutable_levels: Cell<Option<u32>>,
}
impl WebGLTexture {
fn new_inherited(
context: &WebGLRenderingContext,
id: WebGLTextureId,
#[cfg(feature = "webxr")] owner: Option<&XRSession>,
) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
id,
target: Cell::new(None),
is_deleted: Cell::new(false),
#[cfg(feature = "webxr")]
owner: owner
.map(|session| WebGLTextureOwner::WebXR(Dom::from_ref(session)))
.unwrap_or(WebGLTextureOwner::WebGL),
#[cfg(not(feature = "webxr"))]
owner: WebGLTextureOwner::WebGL,
immutable_levels: Cell::new(None),
face_count: Cell::new(0),
base_mipmap_level: 0,
min_filter: Cell::new(constants::NEAREST_MIPMAP_LINEAR),
mag_filter: Cell::new(constants::LINEAR),
image_info_array: DomRefCell::new([None; MAX_LEVEL_COUNT * MAX_FACE_COUNT]),
attached_framebuffer: Default::default(),
}
}
pub(crate) fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateTexture(sender));
receiver
.recv()
.unwrap()
.map(|id| WebGLTexture::new(context, id, CanGc::note()))
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: WebGLTextureId,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLTexture::new_inherited(
context,
id,
#[cfg(feature = "webxr")]
None,
)),
&*context.global(),
can_gc,
)
}
#[cfg(feature = "webxr")]
pub(crate) fn new_webxr(
context: &WebGLRenderingContext,
id: WebGLTextureId,
session: &XRSession,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLTexture::new_inherited(context, id, Some(session))),
&*context.global(),
can_gc,
)
}
}
impl WebGLTexture {
pub(crate) fn id(&self) -> WebGLTextureId {
self.id
}
// NB: Only valid texture targets come here
pub(crate) fn bind(&self, target: u32) -> WebGLResult<()> {
if self.is_invalid() {
return Err(WebGLError::InvalidOperation);
}
if let Some(previous_target) = self.target.get() {
if target != previous_target {
return Err(WebGLError::InvalidOperation);
}
} else {
// This is the first time binding
let face_count = match target {
constants::TEXTURE_2D | constants::TEXTURE_2D_ARRAY | constants::TEXTURE_3D => 1,
constants::TEXTURE_CUBE_MAP => 6,
_ => return Err(WebGLError::InvalidEnum),
};
self.face_count.set(face_count);
self.target.set(Some(target));
}
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::BindTexture(target, Some(self.id)));
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn initialize(
&self,
target: TexImageTarget,
width: u32,
height: u32,
depth: u32,
internal_format: TexFormat,
level: u32,
data_type: Option<TexDataType>,
) -> WebGLResult<()> {
let image_info = ImageInfo {
width,
height,
depth,
internal_format,
data_type,
};
let face_index = self.face_index_for_target(&target);
self.set_image_infos_at_level_and_face(level, face_index, image_info);
if let Some(fb) = self.attached_framebuffer.get() {
fb.update_status();
}
Ok(())
}
pub(crate) fn generate_mipmap(&self) -> WebGLResult<()> {
let target = match self.target.get() {
Some(target) => target,
None => {
error!("Cannot generate mipmap on texture that has no target!");
return Err(WebGLError::InvalidOperation);
},
};
let base_image_info = self.base_image_info().ok_or(WebGLError::InvalidOperation)?;
let is_cubic = target == constants::TEXTURE_CUBE_MAP;
if is_cubic && !self.is_cube_complete() {
return Err(WebGLError::InvalidOperation);
}
if !base_image_info.is_power_of_two() {
return Err(WebGLError::InvalidOperation);
}
if base_image_info.is_compressed_format() {
return Err(WebGLError::InvalidOperation);
}
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::GenerateMipmap(target));
if self.base_mipmap_level + base_image_info.get_max_mimap_levels() == 0 {
return Err(WebGLError::InvalidOperation);
}
let last_level = self.base_mipmap_level + base_image_info.get_max_mimap_levels() - 1;
self.populate_mip_chain(self.base_mipmap_level, last_level)
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
if !self.is_deleted.get() {
self.is_deleted.set(true);
let context = self.upcast::<WebGLObject>().context();
/*
If a texture object is deleted while its image is attached to one or more attachment
points in a currently bound framebuffer, then it is as if FramebufferTexture had been
called, with a texture of zero, for each attachment point to which this im-age was
attached in that framebuffer. In other words, this texture image is firstdetached from
all attachment points in a currently bound framebuffer.
- GLES 3.0, 4.4.2.3, "Attaching Texture Images to a Framebuffer"
*/
if let Some(fb) = context.get_draw_framebuffer_slot().get() {
let _ = fb.detach_texture(self);
}
if let Some(fb) = context.get_read_framebuffer_slot().get() {
let _ = fb.detach_texture(self);
}
// We don't delete textures owned by WebXR
#[cfg(feature = "webxr")]
if let WebGLTextureOwner::WebXR(_) = self.owner {
return;
}
let cmd = WebGLCommand::DeleteTexture(self.id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
}
}
pub(crate) fn is_invalid(&self) -> bool {
// https://immersive-web.github.io/layers/#xrwebglsubimagetype
#[cfg(feature = "webxr")]
if let WebGLTextureOwner::WebXR(ref session) = self.owner {
if session.is_outside_raf() {
return true;
}
}
self.is_deleted.get()
}
pub(crate) fn is_immutable(&self) -> bool {
self.immutable_levels.get().is_some()
}
pub(crate) fn target(&self) -> Option<u32> {
self.target.get()
}
pub(crate) fn maybe_get_tex_parameter(&self, param: TexParameter) -> Option<TexParameterValue> {
match param {
TexParameter::Int(TexParameterInt::TextureImmutableLevels) => Some(
TexParameterValue::Int(self.immutable_levels.get().unwrap_or(0) as i32),
),
TexParameter::Bool(TexParameterBool::TextureImmutableFormat) => {
Some(TexParameterValue::Bool(self.is_immutable()))
},
_ => None,
}
}
/// We have to follow the conversion rules for GLES 2.0. See:
/// <https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html>
pub(crate) fn tex_parameter(&self, param: u32, value: TexParameterValue) -> WebGLResult<()> {
let target = self.target().unwrap();
let (int_value, float_value) = match value {
TexParameterValue::Int(int_value) => (int_value, int_value as f32),
TexParameterValue::Float(float_value) => (float_value as i32, float_value),
TexParameterValue::Bool(_) => unreachable!("no settable tex params should be booleans"),
};
let context = self.upcast::<WebGLObject>().context();
let is_webgl2 = context.webgl_version() == WebGLVersion::WebGL2;
let update_filter = |filter: &Cell<u32>| {
if filter.get() == int_value as u32 {
return Ok(());
}
filter.set(int_value as u32);
context.send_command(WebGLCommand::TexParameteri(target, param, int_value));
Ok(())
};
if is_webgl2 {
match param {
constants::TEXTURE_BASE_LEVEL | constants::TEXTURE_MAX_LEVEL => {
context.send_command(WebGLCommand::TexParameteri(target, param, int_value));
return Ok(());
},
constants::TEXTURE_COMPARE_FUNC => match int_value as u32 {
constants::LEQUAL |
constants::GEQUAL |
constants::LESS |
constants::GREATER |
constants::EQUAL |
constants::NOTEQUAL |
constants::ALWAYS |
constants::NEVER => {
context.send_command(WebGLCommand::TexParameteri(target, param, int_value));
return Ok(());
},
_ => return Err(WebGLError::InvalidEnum),
},
constants::TEXTURE_COMPARE_MODE => match int_value as u32 {
constants::COMPARE_REF_TO_TEXTURE | constants::NONE => {
context.send_command(WebGLCommand::TexParameteri(target, param, int_value));
return Ok(());
},
_ => return Err(WebGLError::InvalidEnum),
},
constants::TEXTURE_MAX_LOD | constants::TEXTURE_MIN_LOD => {
context.send_command(WebGLCommand::TexParameterf(target, param, float_value));
return Ok(());
},
constants::TEXTURE_WRAP_R => match int_value as u32 {
constants::CLAMP_TO_EDGE | constants::MIRRORED_REPEAT | constants::REPEAT => {
self.upcast::<WebGLObject>()
.context()
.send_command(WebGLCommand::TexParameteri(target, param, int_value));
return Ok(());
},
_ => return Err(WebGLError::InvalidEnum),
},
_ => {},
}
}
match param {
constants::TEXTURE_MIN_FILTER => match int_value as u32 {
constants::NEAREST |
constants::LINEAR |
constants::NEAREST_MIPMAP_NEAREST |
constants::LINEAR_MIPMAP_NEAREST |
constants::NEAREST_MIPMAP_LINEAR |
constants::LINEAR_MIPMAP_LINEAR => update_filter(&self.min_filter),
_ => Err(WebGLError::InvalidEnum),
},
constants::TEXTURE_MAG_FILTER => match int_value as u32 {
constants::NEAREST | constants::LINEAR => update_filter(&self.mag_filter),
_ => Err(WebGLError::InvalidEnum),
},
constants::TEXTURE_WRAP_S | constants::TEXTURE_WRAP_T => match int_value as u32 {
constants::CLAMP_TO_EDGE | constants::MIRRORED_REPEAT | constants::REPEAT => {
context.send_command(WebGLCommand::TexParameteri(target, param, int_value));
Ok(())
},
_ => Err(WebGLError::InvalidEnum),
},
EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT => {
// NaN is not less than 1., what a time to be alive.
if float_value < 1. || !float_value.is_normal() {
return Err(WebGLError::InvalidValue);
}
context.send_command(WebGLCommand::TexParameterf(target, param, float_value));
Ok(())
},
_ => Err(WebGLError::InvalidEnum),
}
}
pub(crate) fn min_filter(&self) -> u32 {
self.min_filter.get()
}
pub(crate) fn mag_filter(&self) -> u32 {
self.mag_filter.get()
}
pub(crate) fn is_using_linear_filtering(&self) -> bool {
let filters = [self.min_filter.get(), self.mag_filter.get()];
filters.iter().any(|filter| {
matches!(
*filter,
constants::LINEAR |
constants::NEAREST_MIPMAP_LINEAR |
constants::LINEAR_MIPMAP_NEAREST |
constants::LINEAR_MIPMAP_LINEAR
)
})
}
pub(crate) fn populate_mip_chain(&self, first_level: u32, last_level: u32) -> WebGLResult<()> {
let base_image_info = self
.image_info_at_face(0, first_level)
.ok_or(WebGLError::InvalidOperation)?;
let mut ref_width = base_image_info.width;
let mut ref_height = base_image_info.height;
if ref_width == 0 || ref_height == 0 {
return Err(WebGLError::InvalidOperation);
}
for level in (first_level + 1)..last_level {
if ref_width == 1 && ref_height == 1 {
break;
}
ref_width = cmp::max(1, ref_width / 2);
ref_height = cmp::max(1, ref_height / 2);
let image_info = ImageInfo {
width: ref_width,
height: ref_height,
depth: 0,
internal_format: base_image_info.internal_format,
data_type: base_image_info.data_type,
};
self.set_image_infos_at_level(level, image_info);
}
Ok(())
}
fn is_cube_complete(&self) -> bool {
debug_assert_eq!(self.face_count.get(), 6);
let image_info = match self.base_image_info() {
Some(info) => info,
None => return false,
};
let ref_width = image_info.width;
let ref_format = image_info.internal_format;
for face in 0..self.face_count.get() {
let current_image_info = match self.image_info_at_face(face, self.base_mipmap_level) {
Some(info) => info,
None => return false,
};
// Compares height with width to enforce square dimensions
if current_image_info.internal_format != ref_format ||
current_image_info.width != ref_width ||
current_image_info.height != ref_width
{
return false;
}
}
true
}
fn face_index_for_target(&self, target: &TexImageTarget) -> u8 {
match *target {
TexImageTarget::CubeMapPositiveX => 0,
TexImageTarget::CubeMapNegativeX => 1,
TexImageTarget::CubeMapPositiveY => 2,
TexImageTarget::CubeMapNegativeY => 3,
TexImageTarget::CubeMapPositiveZ => 4,
TexImageTarget::CubeMapNegativeZ => 5,
_ => 0,
}
}
pub(crate) fn image_info_for_target(
&self,
target: &TexImageTarget,
level: u32,
) -> Option<ImageInfo> {
let face_index = self.face_index_for_target(target);
self.image_info_at_face(face_index, level)
}
pub(crate) fn image_info_at_face(&self, face: u8, level: u32) -> Option<ImageInfo> {
let pos = (level * self.face_count.get() as u32) + face as u32;
self.image_info_array.borrow()[pos as usize]
}
fn set_image_infos_at_level(&self, level: u32, image_info: ImageInfo) {
for face in 0..self.face_count.get() {
self.set_image_infos_at_level_and_face(level, face, image_info);
}
}
fn set_image_infos_at_level_and_face(&self, level: u32, face: u8, image_info: ImageInfo) {
debug_assert!(face < self.face_count.get());
let pos = (level * self.face_count.get() as u32) + face as u32;
self.image_info_array.borrow_mut()[pos as usize] = Some(image_info);
}
fn base_image_info(&self) -> Option<ImageInfo> {
assert!((self.base_mipmap_level as usize) < MAX_LEVEL_COUNT);
self.image_info_at_face(0, self.base_mipmap_level)
}
pub(crate) fn attach_to_framebuffer(&self, fb: &WebGLFramebuffer) {
self.attached_framebuffer.set(Some(fb));
}
pub(crate) fn detach_from_framebuffer(&self) {
self.attached_framebuffer.set(None);
}
pub(crate) fn storage(
&self,
target: TexImageTarget,
levels: u32,
internal_format: TexFormat,
width: u32,
height: u32,
depth: u32,
) -> WebGLResult<()> {
// Handled by the caller
assert!(!self.is_immutable());
assert!(self.target().is_some());
let target_id = target.as_gl_constant();
let command = match target {
TexImageTarget::Texture2D | TexImageTarget::CubeMap => {
WebGLCommand::TexStorage2D(target_id, levels, internal_format, width, height)
},
TexImageTarget::Texture3D | TexImageTarget::Texture2DArray => {
WebGLCommand::TexStorage3D(target_id, levels, internal_format, width, height, depth)
},
_ => unreachable!(), // handled by the caller
};
self.upcast::<WebGLObject>().context().send_command(command);
let mut width = width;
let mut height = height;
let mut depth = depth;
for level in 0..levels {
let image_info = ImageInfo {
width,
height,
depth,
internal_format,
data_type: None,
};
self.set_image_infos_at_level(level, image_info);
width = cmp::max(1, width / 2);
height = cmp::max(1, height / 2);
depth = cmp::max(1, depth / 2);
}
self.immutable_levels.set(Some(levels));
if let Some(fb) = self.attached_framebuffer.get() {
fb.update_status();
}
Ok(())
}
}
impl Drop for WebGLTexture {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub(crate) struct ImageInfo {
width: u32,
height: u32,
depth: u32,
#[no_trace]
internal_format: TexFormat,
#[no_trace]
data_type: Option<TexDataType>,
}
impl ImageInfo {
pub(crate) fn width(&self) -> u32 {
self.width
}
pub(crate) fn height(&self) -> u32 {
self.height
}
pub(crate) fn internal_format(&self) -> TexFormat {
self.internal_format
}
pub(crate) fn data_type(&self) -> Option<TexDataType> {
self.data_type
}
fn is_power_of_two(&self) -> bool {
self.width.is_power_of_two() &&
self.height.is_power_of_two() &&
self.depth.is_power_of_two()
}
fn get_max_mimap_levels(&self) -> u32 {
let largest = cmp::max(cmp::max(self.width, self.height), self.depth);
if largest == 0 {
return 0;
}
// FloorLog2(largest) + 1
(largest as f64).log2() as u32 + 1
}
fn is_compressed_format(&self) -> bool {
self.internal_format.is_compressed()
}
}
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
pub(crate) enum TexCompressionValidation {
None,
S3TC,
}
#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
pub(crate) struct TexCompression {
#[no_trace]
pub(crate) format: TexFormat,
pub(crate) bytes_per_block: u8,
pub(crate) block_width: u8,
pub(crate) block_height: u8,
pub(crate) validation: TexCompressionValidation,
}

View file

@ -0,0 +1,134 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use canvas_traits::webgl::{WebGLCommand, webgl_channel};
use dom_struct::dom_struct;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLTransformFeedback {
webgl_object: WebGLObject,
id: u32,
marked_for_deletion: Cell<bool>,
has_been_bound: Cell<bool>,
is_active: Cell<bool>,
is_paused: Cell<bool>,
}
impl WebGLTransformFeedback {
fn new_inherited(context: &WebGLRenderingContext, id: u32) -> Self {
Self {
webgl_object: WebGLObject::new_inherited(context),
id,
marked_for_deletion: Cell::new(false),
has_been_bound: Cell::new(false),
is_active: Cell::new(false),
is_paused: Cell::new(false),
}
}
pub(crate) fn new(context: &WebGLRenderingContext, can_gc: CanGc) -> DomRoot<Self> {
let (sender, receiver) = webgl_channel().unwrap();
context.send_command(WebGLCommand::CreateTransformFeedback(sender));
let id = receiver.recv().unwrap();
reflect_dom_object(
Box::new(WebGLTransformFeedback::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
}
impl WebGLTransformFeedback {
pub(crate) fn bind(&self, context: &WebGLRenderingContext, target: u32) {
context.send_command(WebGLCommand::BindTransformFeedback(target, self.id()));
self.has_been_bound.set(true);
}
pub(crate) fn begin(&self, context: &WebGLRenderingContext, primitive_mode: u32) {
if self.has_been_bound.get() && !self.is_active() {
context.send_command(WebGLCommand::BeginTransformFeedback(primitive_mode));
self.set_active(true);
}
}
pub(crate) fn end(&self, context: &WebGLRenderingContext) {
if self.has_been_bound.get() && self.is_active() {
if self.is_paused() {
context.send_command(WebGLCommand::ResumeTransformFeedback());
}
context.send_command(WebGLCommand::EndTransformFeedback());
self.set_active(false);
}
}
pub(crate) fn resume(&self, context: &WebGLRenderingContext) {
if self.is_active() && self.is_paused() {
context.send_command(WebGLCommand::ResumeTransformFeedback());
self.set_pause(false);
}
}
pub(crate) fn pause(&self, context: &WebGLRenderingContext) {
if self.is_active() && !self.is_paused() {
context.send_command(WebGLCommand::PauseTransformFeedback());
self.set_pause(true);
}
}
pub(crate) fn id(&self) -> u32 {
self.id
}
pub(crate) fn is_valid(&self) -> bool {
!self.marked_for_deletion.get()
}
pub(crate) fn is_active(&self) -> bool {
self.is_active.get()
}
pub(crate) fn is_paused(&self) -> bool {
self.is_paused.get()
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
if self.is_valid() && self.id() != 0 {
self.marked_for_deletion.set(true);
let context = self.upcast::<WebGLObject>().context();
let cmd = WebGLCommand::DeleteTransformFeedback(self.id);
match operation_fallibility {
Operation::Fallible => context.send_command_ignored(cmd),
Operation::Infallible => context.send_command(cmd),
}
}
}
pub(crate) fn set_active(&self, value: bool) {
if self.is_valid() && self.has_been_bound.get() {
self.is_active.set(value);
}
}
pub(crate) fn set_pause(&self, value: bool) {
if self.is_valid() && self.is_active() {
self.is_active.set(value);
}
}
}
impl Drop for WebGLTransformFeedback {
fn drop(&mut self) {
self.delete(Operation::Fallible);
}
}

View file

@ -0,0 +1,95 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::webgl::{WebGLContextId, WebGLProgramId};
use dom_struct::dom_struct;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLUniformLocation {
reflector_: Reflector,
id: i32,
#[no_trace]
context_id: WebGLContextId,
#[no_trace]
program_id: WebGLProgramId,
link_generation: u64,
size: Option<i32>,
type_: u32,
}
impl WebGLUniformLocation {
fn new_inherited(
id: i32,
context_id: WebGLContextId,
program_id: WebGLProgramId,
link_generation: u64,
size: Option<i32>,
type_: u32,
) -> Self {
Self {
reflector_: Reflector::new(),
id,
context_id,
program_id,
link_generation,
size,
type_,
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
window: &Window,
id: i32,
context_id: WebGLContextId,
program_id: WebGLProgramId,
link_generation: u64,
size: Option<i32>,
type_: u32,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(Self::new_inherited(
id,
context_id,
program_id,
link_generation,
size,
type_,
)),
window,
can_gc,
)
}
pub(crate) fn id(&self) -> i32 {
self.id
}
pub(crate) fn program_id(&self) -> WebGLProgramId {
self.program_id
}
pub(crate) fn context_id(&self) -> WebGLContextId {
self.context_id
}
pub(crate) fn link_generation(&self) -> u64 {
self.link_generation
}
pub(crate) fn size(&self) -> Option<i32> {
self.size
}
pub(crate) fn type_(&self) -> u32 {
self.type_
}
}

View file

@ -0,0 +1,109 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId};
use dom_struct::dom_struct;
use crate::dom::bindings::cell::Ref;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::webgl::vertexarrayobject::{VertexArrayObject, VertexAttribData};
use crate::dom::webgl::webglbuffer::WebGLBuffer;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLVertexArrayObject {
webgl_object_: WebGLObject,
array_object: VertexArrayObject,
}
impl WebGLVertexArrayObject {
fn new_inherited(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self {
Self {
webgl_object_: WebGLObject::new_inherited(context),
array_object: VertexArrayObject::new(context, id),
}
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: Option<WebGLVertexArrayId>,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLVertexArrayObject::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
pub(crate) fn id(&self) -> Option<WebGLVertexArrayId> {
self.array_object.id()
}
pub(crate) fn is_deleted(&self) -> bool {
self.array_object.is_deleted()
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
self.array_object.delete(operation_fallibility);
}
pub(crate) fn ever_bound(&self) -> bool {
self.array_object.ever_bound()
}
pub(crate) fn set_ever_bound(&self) {
self.array_object.set_ever_bound();
}
pub(crate) fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> {
self.array_object.element_array_buffer()
}
pub(crate) fn get_vertex_attrib(&self, index: u32) -> Option<Ref<'_, VertexAttribData>> {
self.array_object.get_vertex_attrib(index)
}
pub(crate) fn set_vertex_attrib_type(&self, index: u32, type_: u32) {
self.array_object.set_vertex_attrib_type(index, type_);
}
pub(crate) fn vertex_attrib_pointer(
&self,
index: u32,
size: i32,
type_: u32,
normalized: bool,
stride: i32,
offset: i64,
) -> WebGLResult<()> {
self.array_object
.vertex_attrib_pointer(index, size, type_, normalized, stride, offset)
}
pub(crate) fn vertex_attrib_divisor(&self, index: u32, value: u32) {
self.array_object.vertex_attrib_divisor(index, value);
}
pub(crate) fn enabled_vertex_attrib_array(&self, index: u32, value: bool) {
self.array_object.enabled_vertex_attrib_array(index, value);
}
pub(crate) fn unbind_buffer(&self, buffer: &WebGLBuffer) {
self.array_object.unbind_buffer(buffer);
}
pub(crate) fn validate_for_draw(
&self,
required_len: u32,
instance_count: u32,
active_attribs: &[ActiveAttribInfo],
) -> WebGLResult<()> {
self.array_object
.validate_for_draw(required_len, instance_count, active_attribs)
}
}

View file

@ -0,0 +1,109 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId};
use dom_struct::dom_struct;
use crate::dom::bindings::cell::Ref;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::webgl::vertexarrayobject::{VertexArrayObject, VertexAttribData};
use crate::dom::webgl::webglbuffer::WebGLBuffer;
use crate::dom::webgl::webglobject::WebGLObject;
use crate::dom::webgl::webglrenderingcontext::{Operation, WebGLRenderingContext};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct WebGLVertexArrayObjectOES {
webgl_object_: WebGLObject,
array_object: VertexArrayObject,
}
impl WebGLVertexArrayObjectOES {
fn new_inherited(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self {
Self {
webgl_object_: WebGLObject::new_inherited(context),
array_object: VertexArrayObject::new(context, id),
}
}
pub(crate) fn new(
context: &WebGLRenderingContext,
id: Option<WebGLVertexArrayId>,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(WebGLVertexArrayObjectOES::new_inherited(context, id)),
&*context.global(),
can_gc,
)
}
pub(crate) fn id(&self) -> Option<WebGLVertexArrayId> {
self.array_object.id()
}
pub(crate) fn is_deleted(&self) -> bool {
self.array_object.is_deleted()
}
pub(crate) fn delete(&self, operation_fallibility: Operation) {
self.array_object.delete(operation_fallibility);
}
pub(crate) fn ever_bound(&self) -> bool {
self.array_object.ever_bound()
}
pub(crate) fn set_ever_bound(&self) {
self.array_object.set_ever_bound();
}
pub(crate) fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> {
self.array_object.element_array_buffer()
}
pub(crate) fn get_vertex_attrib(&self, index: u32) -> Option<Ref<'_, VertexAttribData>> {
self.array_object.get_vertex_attrib(index)
}
pub(crate) fn set_vertex_attrib_type(&self, index: u32, type_: u32) {
self.array_object.set_vertex_attrib_type(index, type_);
}
pub(crate) fn vertex_attrib_pointer(
&self,
index: u32,
size: i32,
type_: u32,
normalized: bool,
stride: i32,
offset: i64,
) -> WebGLResult<()> {
self.array_object
.vertex_attrib_pointer(index, size, type_, normalized, stride, offset)
}
pub(crate) fn vertex_attrib_divisor(&self, index: u32, value: u32) {
self.array_object.vertex_attrib_divisor(index, value);
}
pub(crate) fn enabled_vertex_attrib_array(&self, index: u32, value: bool) {
self.array_object.enabled_vertex_attrib_array(index, value);
}
pub(crate) fn unbind_buffer(&self, buffer: &WebGLBuffer) {
self.array_object.unbind_buffer(buffer);
}
pub(crate) fn validate_for_draw(
&self,
required_len: u32,
instance_count: u32,
active_attribs: &[ActiveAttribInfo],
) -> WebGLResult<()> {
self.array_object
.validate_for_draw(required_len, instance_count, active_attribs)
}
}