diff options
author | Emilio Cobos Álvarez <me@emiliocobos.me> | 2016-06-12 00:16:27 +0200 |
---|---|---|
committer | Emilio Cobos Álvarez <me@emiliocobos.me> | 2016-06-25 00:03:15 +0200 |
commit | 46c14aced2f761c39f0f602962c660e362d98416 (patch) | |
tree | 668c13ee03202b095d352f67b533b8fdac86b889 /components/script/dom/webglrenderingcontext.rs | |
parent | 8d81ee77a877f07e2d4f2779aa252f5f3bb98c7c (diff) | |
download | servo-46c14aced2f761c39f0f602962c660e362d98416.tar.gz servo-46c14aced2f761c39f0f602962c660e362d98416.zip |
webgl: Refactor texture validations to take advantage of rust type system
This commit introduces the `WebGLValidator` trait, and uses it for multiple
validations in the texture-related WebGL code, to move that logic out of the
already bloated `webglrenderingcontext.rs` file.
It also creates a type-safe wrapper for some WebGL types, removing all the
`unreachable!`s there, and introduces a macro for generating them conveniently.
This partially addresses #10693, pending refactor more code to use this
infrastructure, and (possibly?) introducing an `AsGLError` trait for the errors
to make the error handling happen in `WebGLContext`.
Diffstat (limited to 'components/script/dom/webglrenderingcontext.rs')
-rw-r--r-- | components/script/dom/webglrenderingcontext.rs | 591 |
1 files changed, 196 insertions, 395 deletions
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index b23ec913eca..46d562afa05 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -18,6 +18,10 @@ use dom::event::{Event, EventBubbles, EventCancelable}; use dom::htmlcanvaselement::HTMLCanvasElement; use dom::htmlcanvaselement::utils as canvas_utils; use dom::node::{Node, NodeDamage, window_from_node}; +use dom::webgl_validations::WebGLValidator; +use dom::webgl_validations::tex_image_2d::{CommonTexImage2DValidator, CommonTexImage2DValidatorResult}; +use dom::webgl_validations::tex_image_2d::{TexImage2DValidator, TexImage2DValidatorResult}; +use dom::webgl_validations::types::{TexFormat, TexImageTarget, TexDataType}; use dom::webglactiveinfo::WebGLActiveInfo; use dom::webglbuffer::WebGLBuffer; use dom::webglcontextevent::WebGLContextEvent; @@ -88,10 +92,6 @@ pub struct WebGLRenderingContext { current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>, } -fn log2(n: u32) -> u32 { - 31 - n.leading_zeros() -} - impl WebGLRenderingContext { fn new_inherited(global: GlobalRef, canvas: &HTMLCanvasElement, @@ -141,6 +141,22 @@ impl WebGLRenderingContext { } } + pub fn limits(&self) -> &GLLimits { + &self.limits + } + + pub fn bound_texture_for_target(&self, target: &TexImageTarget) -> Option<Root<WebGLTexture>> { + match *target { + TexImageTarget::Texture2D => self.bound_texture_2d.get(), + TexImageTarget::CubeMapPositiveX | + TexImageTarget::CubeMapNegativeX | + TexImageTarget::CubeMapPositiveY | + TexImageTarget::CubeMapNegativeY | + TexImageTarget::CubeMapPositiveZ | + TexImageTarget::CubeMapNegativeZ => self.bound_texture_cube_map.get(), + } + } + pub fn recreate(&self, size: Size2D<i32>) { self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap(); } @@ -239,31 +255,6 @@ impl WebGLRenderingContext { true } - fn texture_for_target(&self, target: u32) -> Option<Root<WebGLTexture>> { - match target { - constants::TEXTURE_2D => self.bound_texture_2d.get(), - constants::TEXTURE_CUBE_MAP_POSITIVE_X | constants::TEXTURE_CUBE_MAP_NEGATIVE_X | - constants::TEXTURE_CUBE_MAP_POSITIVE_Y | constants::TEXTURE_CUBE_MAP_NEGATIVE_Y | - constants::TEXTURE_CUBE_MAP_POSITIVE_Z | constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => { - self.bound_texture_cube_map.get() - }, - _ => None, - } - } - - fn face_index_for_target(&self, target: u32) -> Option<u8> { - match target { - constants::TEXTURE_2D => Some(0), - constants::TEXTURE_CUBE_MAP_POSITIVE_X => Some(0), - constants::TEXTURE_CUBE_MAP_NEGATIVE_X => Some(1), - constants::TEXTURE_CUBE_MAP_POSITIVE_Y => Some(2), - constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => Some(3), - constants::TEXTURE_CUBE_MAP_POSITIVE_Z => Some(4), - constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => Some(5), - _ => None - } - } - fn get_image_pixels(&self, source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) -> ImagePixelResult { @@ -331,68 +322,18 @@ impl WebGLRenderingContext { return Ok((pixels, size)); } - fn validate_tex_internal_format(&self, internal_format: u32) -> bool { - // GL_INVALID_VALUE is generated if internal_format is not an - // accepted format. - match internal_format { - constants::DEPTH_COMPONENT | - constants::ALPHA | - constants::RGB | - constants::RGBA | - constants::LUMINANCE | - constants::LUMINANCE_ALPHA => true, - - _ => { - self.webgl_error(InvalidValue); - false - }, - } - } - - fn validate_tex_format(&self, format: u32) -> bool { - // GL_INVALID_VALUE is generated if internal_format is not an - // accepted format. - match format { - constants::DEPTH_COMPONENT | - constants::ALPHA | - constants::RGB | - constants::RGBA | - constants::LUMINANCE | - constants::LUMINANCE_ALPHA => true, - - _ => { - self.webgl_error(InvalidEnum); - false - }, - } - } - + // TODO(emilio): Move this logic to a validator. #[allow(unsafe_code)] fn validate_tex_image_2d_data(&self, - width: i32, - height: i32, - format: u32, - data_type: u32, + width: u32, + height: u32, + format: TexFormat, + data_type: TexDataType, data: Option<*mut JSObject>) - -> Result<i32, ()> { - // TODO(emilio, #10693): Add type-safe wrappers to validations - let (element_size, components_per_element) = match data_type { - constants::UNSIGNED_BYTE => (1, 1), - constants::UNSIGNED_SHORT_5_6_5 => (2, 3), - constants::UNSIGNED_SHORT_5_5_5_1 | - constants::UNSIGNED_SHORT_4_4_4_4 => (2, 4), - _ => unreachable!(), // previously validated - }; - - let components = match format { - constants::DEPTH_COMPONENT => 1, - constants::ALPHA => 1, - constants::LUMINANCE => 1, - constants::LUMINANCE_ALPHA => 2, - constants::RGB => 3, - constants::RGBA => 4, - _ => unreachable!(), // previously validated - }; + -> Result<u32, ()> { + let element_size = data_type.element_size(); + let components_per_element = data_type.components_per_element(); + let components = format.components(); // If data is non-null, the type of pixels must match the type of the // data to be read. @@ -423,178 +364,18 @@ impl WebGLRenderingContext { return Ok(expected_byte_length); } - fn validate_tex_image_2d_parameters(&self, - target: u32, - level: i32, - internal_format: u32, - width: i32, - height: i32, - border: i32, - format: u32, - data_type: u32) -> bool { - // Validate common tex image parameters - if !self.validate_common_tex_image_parameters(target, level, width, height) { - return false; - } - - // GL_INVALID_ENUM is generated if data_type is not an accepted value. - match data_type { - constants::UNSIGNED_BYTE | - constants::UNSIGNED_SHORT_4_4_4_4 | - constants::UNSIGNED_SHORT_5_5_5_1 | - constants::UNSIGNED_SHORT_5_6_5 => {}, - _ => { - self.webgl_error(InvalidEnum); - return false; - }, - } - // Validate format - if !self.validate_tex_format(format) { - return false; - } - - // Validate internal_format - if !self.validate_tex_internal_format(internal_format) { - return false; - } - - // GL_INVALID_OPERATION is generated if format does not - // match internal_format. - if format != internal_format { - self.webgl_error(InvalidOperation); - return false; - } - - // GL_INVALID_VALUE is generated if border is not 0. - if border != 0 { - self.webgl_error(InvalidValue); - return false; - } - - // 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 { - constants::UNSIGNED_SHORT_4_4_4_4 | - constants::UNSIGNED_SHORT_5_5_5_1 if format != constants::RGBA => { - self.webgl_error(InvalidOperation); - return false; - }, - constants::UNSIGNED_SHORT_5_6_5 if format != constants::RGB => { - self.webgl_error(InvalidOperation); - return false; - }, - _ => {}, - } - - true - } - - fn validate_common_tex_image_parameters(&self, - target: u32, - level: i32, - width: i32, - height: i32) -> bool { - // 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. - // - // max_size is GL_MAX_TEXTURE_SIZE when target is GL_TEXTURE_2D or - // GL_MAX_CUBE_MAP_TEXTURE_SIZE when target is not GL_TEXTURE_2D. - let (texture, max) = match target { - constants::TEXTURE_2D - => (self.bound_texture_2d.get(), self.limits.max_tex_size), - constants::TEXTURE_CUBE_MAP_POSITIVE_X | - constants::TEXTURE_CUBE_MAP_NEGATIVE_X | - constants::TEXTURE_CUBE_MAP_POSITIVE_Y | - constants::TEXTURE_CUBE_MAP_NEGATIVE_Y | - constants::TEXTURE_CUBE_MAP_POSITIVE_Z | - constants::TEXTURE_CUBE_MAP_NEGATIVE_Z - => (self.bound_texture_cube_map.get(), self.limits.max_cube_map_tex_size), - _ => { - self.webgl_error(InvalidEnum); - return false; - }, - }; - - // If an attempt is made to call this function with no - // WebGLTexture bound, an INVALID_OPERATION error is generated. - if texture.is_none() { - self.webgl_error(InvalidOperation); - return false; - } - - let is_cubic = target != constants::TEXTURE_2D; - - // 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 is_cubic && width != height { - self.webgl_error(InvalidValue); - return false; - } - - // GL_INVALID_VALUE is generated if level is less than 0. - // - // GL_INVALID_VALUE is generated if width or height is less than 0 - if width < 0 || height < 0 || level < 0 || - width as u32 > max || height as u32 > max { - self.webgl_error(InvalidValue); - return false; - } - - // 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) as i32 { - self.webgl_error(InvalidValue); - return false; - } - - // GL_INVALID_VALUE is generated if level is greater than zero and the - // texture is not power of two. - if level > 0 && - (!(width as u32).is_power_of_two() || - !(height as u32).is_power_of_two()) { - self.webgl_error(InvalidValue); - return false; - } - - true - } - fn tex_image_2d(&self, - target: u32, - level: i32, - internal_format: u32, - width: i32, - height: i32, - border: i32, - format: u32, - data_type: u32, + texture: Root<WebGLTexture>, + target: TexImageTarget, + data_type: TexDataType, + internal_format: TexFormat, + level: u32, + width: u32, + height: u32, + _border: u32, pixels: Vec<u8>) { // NB: pixels should NOT be premultipied - // This should be validated before reaching this function - debug_assert!(self.validate_tex_image_2d_parameters(target, level, - internal_format, - width, height, - border, format, - data_type)); - - let slot = match target { - constants::TEXTURE_2D - => self.bound_texture_2d.get(), - _ => self.bound_texture_cube_map.get(), - }; - - let texture = slot.as_ref().expect("No bound texture found after validation"); - - if format == constants::RGBA && - data_type == constants::UNSIGNED_BYTE && + if internal_format == TexFormat::RGBA && + data_type == TexDataType::UnsignedByte && self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) { // TODO(emilio): premultiply here. } @@ -603,15 +384,18 @@ impl WebGLRenderingContext { // TexImage2D depth is always equal to 1 handle_potential_webgl_error!(self, texture.initialize(target, - width as u32, - height as u32, 1, + width, + height, 1, internal_format, - level as u32, + level, Some(data_type))); // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested - let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32, - width, height, format, data_type, pixels); + let msg = WebGLCommand::TexImage2D(target.as_gl_constant(), level as i32, + internal_format.as_gl_constant() as i32, + width as i32, height as i32, + internal_format.as_gl_constant(), + data_type.as_gl_constant(), pixels); self.ipc_renderer .send(CanvasMsg::WebGL(msg)) @@ -619,54 +403,29 @@ impl WebGLRenderingContext { } fn tex_sub_image_2d(&self, - target: u32, - level: i32, + texture: Root<WebGLTexture>, + target: TexImageTarget, + level: u32, xoffset: i32, yoffset: i32, - width: i32, - height: i32, - format: u32, - data_type: u32, + width: u32, + height: u32, + format: TexFormat, + data_type: TexDataType, pixels: Vec<u8>) { // NB: pixels should NOT be premultipied - // This should be validated before reaching this function - debug_assert!(self.validate_tex_image_2d_parameters(target, level, - format, - width, height, - 0, format, - data_type)); - - let slot = match target { - constants::TEXTURE_2D - => self.bound_texture_2d.get(), - constants::TEXTURE_CUBE_MAP - => self.bound_texture_cube_map.get(), - - _ => return self.webgl_error(InvalidEnum), - }; - - let texture = slot.as_ref().expect("No bound texture found after validation"); - - if format == constants::RGBA && - data_type == constants::UNSIGNED_BYTE && - self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) { - // TODO(emilio): premultiply here. - } - // We have already validated level - let face_index = self.face_index_for_target(target).unwrap(); - let image_info = texture.image_info_at_face(face_index, level as u32); + let image_info = texture.image_info_for_target(&target, level); // 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 xoffset < 0 || ((xoffset + width) as u32) > image_info.width() || - yoffset < 0 || ((yoffset + height) as u32) > image_info.height() { + if xoffset < 0 || (xoffset as u32 + width) > image_info.width() || + yoffset < 0 || (yoffset as u32 + height) > image_info.height() { return self.webgl_error(InvalidValue); } - // Using internal_format() to do this check - // because we are sure format is as same as internal_format. + // NB: format and internal_format must match. if format != image_info.internal_format().unwrap() || data_type != image_info.data_type().unwrap() { return self.webgl_error(InvalidOperation); @@ -675,8 +434,11 @@ impl WebGLRenderingContext { // TODO(emilio): Flip Y axis if necessary here // TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested - let msg = WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, - width, height, format, data_type, pixels); + let msg = WebGLCommand::TexSubImage2D(target.as_gl_constant(), + level as i32, xoffset, yoffset, + width as i32, height as i32, + format.as_gl_constant(), + data_type.as_gl_constant(), pixels); self.ipc_renderer .send(CanvasMsg::WebGL(msg)) @@ -1058,43 +820,39 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn CopyTexImage2D(&self, target: u32, level: i32, internal_format: u32, x: i32, y: i32, width: i32, height: i32, border: i32) { - // Validate common tex image parameters - if !self.validate_common_tex_image_parameters(target, level, width, height) || - !self.validate_tex_internal_format(internal_format) { - return; - } - - // GL_INVALID_VALUE is generated if the border is not 0 - if border != 0 { - self.webgl_error(InvalidValue); - return; - } - - let texture = self.texture_for_target(target).unwrap(); - let face_index = self.face_index_for_target(target).unwrap(); - - // We have already validated level - let image_info = texture.image_info_at_face(face_index, level as u32); - - // The color buffer components can be dropped during the conversion to the - // internal_format, but new components cannot be added - let invalid_format = match image_info.internal_format() { - Some(src_format) => match (src_format, internal_format) { - (constants::ALPHA, constants::ALPHA) | (constants::RGB, constants::RGB) | - (constants::RGB, constants::LUMINANCE) | (constants::RGBA, _) => false, - _ => true, - }, - None => false, + let validator = CommonTexImage2DValidator::new(self, target, level, + internal_format, width, + height, border); + let CommonTexImage2DValidatorResult { + texture, + target, + level, + internal_format, + width, + height, + border, + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, }; + let image_info = texture.image_info_for_target(&target, level); + + // The color buffer components can be dropped during the conversion to + // the internal_format, but new components cannot be added. + // + // Note that this only applies if we're copying to an already + // initialized texture. + // // GL_INVALID_OPERATION is generated if the color buffer cannot be - // converted to the internal_format - if invalid_format { - self.webgl_error(InvalidOperation); - return; + // converted to the internal_format. + if let Some(old_internal_format) = image_info.internal_format() { + if old_internal_format.components() > internal_format.components() { + return self.webgl_error(InvalidOperation); + } } - // TexImage2D depth is always equal to 1 + // NB: TexImage2D depth is always equal to 1 handle_potential_webgl_error!(self, texture.initialize(target, width as u32, height as u32, 1, @@ -1102,8 +860,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { level as u32, None)); - let msg = WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y, - width, height, border); + let msg = WebGLCommand::CopyTexImage2D(target.as_gl_constant(), + level as i32, + internal_format.as_gl_constant(), + x, y, + width as i32, height as i32, + border as i32); self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap() } @@ -1111,29 +873,39 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn CopyTexSubImage2D(&self, target: u32, level: i32, xoffset: i32, yoffset: i32, x: i32, y: i32, width: i32, height: i32) { - // Validate common tex image parameters - if !self.validate_common_tex_image_parameters(target, level, width, height) { - return; - } - - let texture = self.texture_for_target(target).unwrap(); - let face_index = self.face_index_for_target(target).unwrap(); + // NB: We use a dummy (valid) format and border in order to reuse the + // common validations, but this should have its own validator. + let validator = CommonTexImage2DValidator::new(self, target, level, + TexFormat::RGBA.as_gl_constant(), + width, height, 0); + let CommonTexImage2DValidatorResult { + texture, + target, + level, + width, + height, + .. + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, + }; - // We have already validated level - let image_info = texture.image_info_at_face(face_index, level as u32); + let image_info = texture.image_info_for_target(&target, level); // 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 xoffset < 0 || ((xoffset + width) as u32) > image_info.width() || - yoffset < 0 || ((yoffset + height) as u32) > image_info.height() { + if xoffset < 0 || (xoffset as u32 + width) > image_info.width() || + yoffset < 0 || (yoffset as u32 + height) > image_info.height() { self.webgl_error(InvalidValue); return; } - let msg = WebGLCommand::CopyTexSubImage2D(target, level, xoffset, yoffset, - x, y, width, height); + let msg = WebGLCommand::CopyTexSubImage2D(target.as_gl_constant(), + level as i32, xoffset, yoffset, + x, y, + width as i32, height as i32); self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap(); } @@ -2104,15 +1876,23 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { format: u32, data_type: u32, data: Option<*mut JSObject>) { - if !self.validate_tex_image_2d_parameters(target, - level, - internal_format, - width, height, - border, - format, - data_type) { - return; // Error handled in validate() - } + let validator = TexImage2DValidator::new(self, target, level, + internal_format, width, height, + border, format, data_type); + + let TexImage2DValidatorResult { + texture, + target, + width, + height, + level, + border, + format, + data_type, + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, // NB: The validator sets the correct error for us. + }; let expected_byte_length = match self.validate_tex_image_2d_data(width, height, @@ -2136,10 +1916,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return self.webgl_error(InvalidOperation); } - self.tex_image_2d(target, level, - internal_format, - width, height, border, - format, data_type, buff) + self.tex_image_2d(texture, target, data_type, format, + level, width, height, border, buff) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 @@ -2156,17 +1934,28 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Err(_) => return, }; - // NB: Border must be zero - if !self.validate_tex_image_2d_parameters(target, level, internal_format, - size.width, size.height, 0, - format, data_type) { - return; // Error handled in validate() - } - self.tex_image_2d(target, level, - internal_format, - size.width, size.height, 0, - format, data_type, pixels); + let validator = TexImage2DValidator::new(self, + target, level, internal_format, + size.width, size.height, + 0, format, data_type); + + let TexImage2DValidatorResult { + texture, + target, + width, + height, + level, + border, + format, + data_type, + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, // NB: The validator sets the correct error for us. + }; + + self.tex_image_2d(texture, target, data_type, format, + level, width, height, border, pixels); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 @@ -2181,15 +1970,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { format: u32, data_type: u32, data: Option<*mut JSObject>) { - if !self.validate_tex_image_2d_parameters(target, - level, - format, - width, height, - 0, - format, - data_type) { - return; // Error handled in validate() - } + let validator = TexImage2DValidator::new(self, target, level, + format, width, height, + 0, format, data_type); + let TexImage2DValidatorResult { + texture, + target, + width, + height, + level, + format, + data_type, + .. + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, // NB: The validator sets the correct error for us. + }; let expected_byte_length = match self.validate_tex_image_2d_data(width, height, @@ -2214,10 +2010,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return self.webgl_error(InvalidOperation); } - self.tex_sub_image_2d(target, level, - xoffset, yoffset, - width, height, - format, data_type, buff); + self.tex_sub_image_2d(texture, target, level, xoffset, yoffset, + width, height, format, data_type, buff); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 @@ -2229,23 +2023,30 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { format: u32, data_type: u32, source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) { - // Get pixels from image source let (pixels, size) = match self.get_image_pixels(source) { Ok((pixels, size)) => (pixels, size), Err(_) => return, }; - // NB: Border must be zero - if !self.validate_tex_image_2d_parameters(target, level, format, - size.width, size.height, 0, - format, data_type) { - return; // Error handled in validate() - } + let validator = TexImage2DValidator::new(self, target, level, format, + size.width, size.height, + 0, format, data_type); + let TexImage2DValidatorResult { + texture, + target, + width, + height, + level, + format, + data_type, + .. + } = match validator.validate() { + Ok(result) => result, + Err(_) => return, // NB: The validator sets the correct error for us. + }; - self.tex_sub_image_2d(target, level, - xoffset, yoffset, - size.width, size.height, - format, data_type, pixels); + self.tex_sub_image_2d(texture, target, level, xoffset, yoffset, + width, height, format, data_type, pixels); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 |