aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/webgl_validations
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <me@emiliocobos.me>2016-06-12 00:16:27 +0200
committerEmilio Cobos Álvarez <me@emiliocobos.me>2016-06-25 00:03:15 +0200
commit46c14aced2f761c39f0f602962c660e362d98416 (patch)
tree668c13ee03202b095d352f67b533b8fdac86b889 /components/script/dom/webgl_validations
parent8d81ee77a877f07e2d4f2779aa252f5f3bb98c7c (diff)
downloadservo-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/webgl_validations')
-rw-r--r--components/script/dom/webgl_validations/mod.rs13
-rw-r--r--components/script/dom/webgl_validations/tex_image_2d.rs353
-rw-r--r--components/script/dom/webgl_validations/types.rs109
3 files changed, 475 insertions, 0 deletions
diff --git a/components/script/dom/webgl_validations/mod.rs b/components/script/dom/webgl_validations/mod.rs
new file mode 100644
index 00000000000..2e070c6d6bc
--- /dev/null
+++ b/components/script/dom/webgl_validations/mod.rs
@@ -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 http://mozilla.org/MPL/2.0/. */
+
+pub trait WebGLValidator {
+ type ValidatedOutput;
+ type Error: ::std::error::Error;
+
+ fn validate(self) -> Result<Self::ValidatedOutput, Self::Error>;
+}
+
+pub mod tex_image_2d;
+pub mod types;
diff --git a/components/script/dom/webgl_validations/tex_image_2d.rs b/components/script/dom/webgl_validations/tex_image_2d.rs
new file mode 100644
index 00000000000..9c4b8fa39fc
--- /dev/null
+++ b/components/script/dom/webgl_validations/tex_image_2d.rs
@@ -0,0 +1,353 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::js::Root;
+use dom::webglrenderingcontext::WebGLRenderingContext;
+use dom::webgltexture::WebGLTexture;
+use std::{self, fmt};
+use super::WebGLValidator;
+use super::types::{TexImageTarget, TexDataType, TexFormat};
+use webrender_traits::WebGLError::*;
+
+/// The errors that the texImage* family of functions can generate.
+#[derive(Debug)]
+pub 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 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,
+}
+
+impl std::error::Error for TexImageValidationError {
+ fn description(&self) -> &str {
+ use self::TexImageValidationError::*;
+ match *self {
+ InvalidTextureTarget(_)
+ => "Invalid texture target",
+ TextureTargetNotBound(_)
+ => "Texture was not bound",
+ InvalidCubicTextureDimensions
+ => "Invalid dimensions were given for a cubic texture target",
+ NegativeLevel
+ => "A negative level was passed",
+ LevelTooHigh
+ => "Level too high",
+ 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",
+ }
+ }
+}
+
+impl fmt::Display for TexImageValidationError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "TexImageValidationError({})", std::error::Error::description(self))
+ }
+}
+
+fn log2(n: u32) -> u32 {
+ 31 - n.leading_zeros()
+}
+
+pub struct CommonTexImage2DValidator<'a> {
+ context: &'a WebGLRenderingContext,
+ target: u32,
+ level: i32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ border: i32,
+}
+
+pub struct CommonTexImage2DValidatorResult {
+ pub texture: Root<WebGLTexture>,
+ pub target: TexImageTarget,
+ pub level: u32,
+ pub internal_format: TexFormat,
+ pub width: u32,
+ pub height: u32,
+ pub border: u32,
+}
+
+impl<'a> WebGLValidator for CommonTexImage2DValidator<'a> {
+ 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) => target,
+ None => {
+ self.context.webgl_error(InvalidEnum);
+ return Err(TexImageValidationError::InvalidTextureTarget(self.target));
+ }
+ };
+
+ let texture = self.context.bound_texture_for_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) => format,
+ None => {
+ self.context.webgl_error(InvalidEnum);
+ return Err(TexImageValidationError::InvalidTextureFormat);
+ }
+ };
+
+ // 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);
+ }
+
+ // 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 self.width as u32 > max_size || self.height as u32 > max_size {
+ self.context.webgl_error(InvalidValue);
+ return Err(TexImageValidationError::TextureTooBig);
+ }
+
+ let width = self.width as u32;
+ let height = self.height as u32;
+ let level = self.level as u32;
+
+ // 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: texture,
+ target: target,
+ level: level,
+ internal_format: internal_format,
+ width: width,
+ height: height,
+ border: self.border as u32,
+ })
+ }
+}
+
+impl<'a> CommonTexImage2DValidator<'a> {
+ pub fn new(context: &'a WebGLRenderingContext,
+ target: u32, level: i32,
+ internal_format: u32,
+ width: i32, height: i32,
+ border: i32) -> Self {
+ CommonTexImage2DValidator {
+ context: context,
+ target: target,
+ level: level,
+ internal_format: internal_format,
+ width: width,
+ height: height,
+ border: border
+ }
+ }
+}
+
+pub struct TexImage2DValidator<'a> {
+ common_validator: CommonTexImage2DValidator<'a>,
+ format: u32,
+ data_type: u32,
+}
+
+impl<'a> TexImage2DValidator<'a> {
+ // TODO: Move data validation logic here.
+ pub 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: format,
+ data_type: data_type,
+ }
+ }
+}
+
+/// The validated result of a TexImage2DValidator-validated call.
+pub struct TexImage2DValidatorResult {
+ /// NB: width, height and level are already unsigned after validation.
+ pub width: u32,
+ pub height: u32,
+ pub level: u32,
+ pub border: u32,
+ pub texture: Root<WebGLTexture>,
+ pub target: TexImageTarget,
+ pub format: TexFormat,
+ pub data_type: TexDataType,
+}
+
+/// TexImage2d validator as per
+/// https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml
+impl<'a> WebGLValidator for TexImage2DValidator<'a> {
+ 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,
+ } = try!(self.common_validator.validate());
+
+ // 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() && width != height {
+ context.webgl_error(InvalidValue);
+ return Err(TexImageValidationError::InvalidCubicTextureDimensions);
+ }
+
+ // 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) => data_type,
+ None => {
+ context.webgl_error(InvalidEnum);
+ return Err(TexImageValidationError::InvalidDataType);
+ },
+ };
+
+ let format = match TexFormat::from_gl_constant(self.format) {
+ Some(format) => format,
+ None => {
+ context.webgl_error(InvalidEnum);
+ return Err(TexImageValidationError::InvalidTextureFormat);
+ }
+ };
+
+ // GL_INVALID_OPERATION is generated if format does not match
+ // internal_format.
+ if format != internal_format {
+ context.webgl_error(InvalidOperation);
+ return Err(TexImageValidationError::TextureFormatMismatch);
+ }
+
+
+ // 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: width,
+ height: height,
+ level: level,
+ border: border,
+ texture: texture,
+ target: target,
+ format: format,
+ data_type: data_type,
+ })
+ }
+}
diff --git a/components/script/dom/webgl_validations/types.rs b/components/script/dom/webgl_validations/types.rs
new file mode 100644
index 00000000000..f42eb548efd
--- /dev/null
+++ b/components/script/dom/webgl_validations/types.rs
@@ -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 http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
+
+/// This macro creates type-safe wrappers for WebGL types, associating variants
+/// with gl constants.
+macro_rules! type_safe_wrapper {
+ ($name: ident, $($variant:ident => $constant:ident, )+) => {
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, JSTraceable, HeapSizeOf)]
+ #[repr(u32)]
+ pub enum $name {
+ $(
+ $variant = constants::$constant,
+ )+
+ }
+
+ impl $name {
+ pub fn from_gl_constant(constant: u32) -> Option<Self> {
+ Some(match constant {
+ $(constants::$constant => $name::$variant, )+
+ _ => return None,
+ })
+ }
+
+ #[inline]
+ pub fn as_gl_constant(&self) -> u32 {
+ *self as u32
+ }
+ }
+ }
+}
+
+type_safe_wrapper! { TexImageTarget,
+ Texture2D => TEXTURE_2D,
+ CubeMapPositiveX => TEXTURE_CUBE_MAP_POSITIVE_X,
+ CubeMapNegativeX => TEXTURE_CUBE_MAP_NEGATIVE_X,
+ CubeMapPositiveY => TEXTURE_CUBE_MAP_POSITIVE_Y,
+ CubeMapNegativeY => TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ CubeMapPositiveZ => TEXTURE_CUBE_MAP_POSITIVE_Z,
+ CubeMapNegativeZ => TEXTURE_CUBE_MAP_NEGATIVE_Z,
+}
+
+impl TexImageTarget {
+ pub fn is_cubic(&self) -> bool {
+ match *self {
+ TexImageTarget::Texture2D => false,
+ _ => true,
+ }
+ }
+}
+
+type_safe_wrapper! { TexDataType,
+ UnsignedByte => UNSIGNED_BYTE,
+ UnsignedShort4444 => UNSIGNED_SHORT_4_4_4_4,
+ UnsignedShort5551 => UNSIGNED_SHORT_5_5_5_1,
+ UnsignedShort565 => UNSIGNED_SHORT_5_6_5,
+}
+
+impl TexDataType {
+ /// Returns the size in bytes of each element of data.
+ pub fn element_size(&self) -> u32 {
+ use self::TexDataType::*;
+ match *self {
+ UnsignedByte => 1,
+ UnsignedShort4444 |
+ UnsignedShort5551 |
+ UnsignedShort565 => 2,
+ }
+ }
+
+ /// Returns how many components a single element may hold. For example, a
+ /// UnsignedShort4444 holds four components, each with 4 bits of data.
+ pub fn components_per_element(&self) -> u32 {
+ use self::TexDataType::*;
+ match *self {
+ UnsignedByte => 1,
+ UnsignedShort565 => 3,
+ UnsignedShort5551 => 4,
+ UnsignedShort4444 => 4,
+ }
+ }
+}
+
+type_safe_wrapper! { TexFormat,
+ DepthComponent => DEPTH_COMPONENT,
+ Alpha => ALPHA,
+ RGB => RGB,
+ RGBA => RGBA,
+ Luminance => LUMINANCE,
+ LuminanceAlpha => LUMINANCE_ALPHA,
+}
+
+impl TexFormat {
+ /// Returns how many components does this format need. For example, RGBA
+ /// needs 4 components, while RGB requires 3.
+ pub fn components(&self) -> u32 {
+ use self::TexFormat::*;
+ match *self {
+ DepthComponent => 1,
+ Alpha => 1,
+ Luminance => 1,
+ LuminanceAlpha => 2,
+ RGB => 3,
+ RGBA => 4,
+ }
+ }
+}