mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
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`.
This commit is contained in:
parent
8d81ee77a8
commit
46c14aced2
7 changed files with 706 additions and 420 deletions
353
components/script/dom/webgl_validations/tex_image_2d.rs
Normal file
353
components/script/dom/webgl_validations/tex_image_2d.rs
Normal file
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue