mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +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
|
@ -394,6 +394,7 @@ pub mod validation;
|
||||||
pub mod validitystate;
|
pub mod validitystate;
|
||||||
pub mod values;
|
pub mod values;
|
||||||
pub mod virtualmethods;
|
pub mod virtualmethods;
|
||||||
|
pub mod webgl_validations;
|
||||||
pub mod webglactiveinfo;
|
pub mod webglactiveinfo;
|
||||||
pub mod webglbuffer;
|
pub mod webglbuffer;
|
||||||
pub mod webglcontextevent;
|
pub mod webglcontextevent;
|
||||||
|
|
13
components/script/dom/webgl_validations/mod.rs
Normal file
13
components/script/dom/webgl_validations/mod.rs
Normal 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 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;
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
109
components/script/dom/webgl_validations/types.rs
Normal file
109
components/script/dom/webgl_validations/types.rs
Normal 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 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,10 @@ use dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::htmlcanvaselement::HTMLCanvasElement;
|
use dom::htmlcanvaselement::HTMLCanvasElement;
|
||||||
use dom::htmlcanvaselement::utils as canvas_utils;
|
use dom::htmlcanvaselement::utils as canvas_utils;
|
||||||
use dom::node::{Node, NodeDamage, window_from_node};
|
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::webglactiveinfo::WebGLActiveInfo;
|
||||||
use dom::webglbuffer::WebGLBuffer;
|
use dom::webglbuffer::WebGLBuffer;
|
||||||
use dom::webglcontextevent::WebGLContextEvent;
|
use dom::webglcontextevent::WebGLContextEvent;
|
||||||
|
@ -88,10 +92,6 @@ pub struct WebGLRenderingContext {
|
||||||
current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>,
|
current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log2(n: u32) -> u32 {
|
|
||||||
31 - n.leading_zeros()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WebGLRenderingContext {
|
impl WebGLRenderingContext {
|
||||||
fn new_inherited(global: GlobalRef,
|
fn new_inherited(global: GlobalRef,
|
||||||
canvas: &HTMLCanvasElement,
|
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>) {
|
pub fn recreate(&self, size: Size2D<i32>) {
|
||||||
self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap();
|
self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -239,31 +255,6 @@ impl WebGLRenderingContext {
|
||||||
true
|
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,
|
fn get_image_pixels(&self,
|
||||||
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>)
|
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>)
|
||||||
-> ImagePixelResult {
|
-> ImagePixelResult {
|
||||||
|
@ -331,68 +322,18 @@ impl WebGLRenderingContext {
|
||||||
return Ok((pixels, size));
|
return Ok((pixels, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_tex_internal_format(&self, internal_format: u32) -> bool {
|
// TODO(emilio): Move this logic to a validator.
|
||||||
// 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
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn validate_tex_image_2d_data(&self,
|
fn validate_tex_image_2d_data(&self,
|
||||||
width: i32,
|
width: u32,
|
||||||
height: i32,
|
height: u32,
|
||||||
format: u32,
|
format: TexFormat,
|
||||||
data_type: u32,
|
data_type: TexDataType,
|
||||||
data: Option<*mut JSObject>)
|
data: Option<*mut JSObject>)
|
||||||
-> Result<i32, ()> {
|
-> Result<u32, ()> {
|
||||||
// TODO(emilio, #10693): Add type-safe wrappers to validations
|
let element_size = data_type.element_size();
|
||||||
let (element_size, components_per_element) = match data_type {
|
let components_per_element = data_type.components_per_element();
|
||||||
constants::UNSIGNED_BYTE => (1, 1),
|
let components = format.components();
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
// If data is non-null, the type of pixels must match the type of the
|
// If data is non-null, the type of pixels must match the type of the
|
||||||
// data to be read.
|
// data to be read.
|
||||||
|
@ -423,178 +364,18 @@ impl WebGLRenderingContext {
|
||||||
return Ok(expected_byte_length);
|
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,
|
fn tex_image_2d(&self,
|
||||||
target: u32,
|
texture: Root<WebGLTexture>,
|
||||||
level: i32,
|
target: TexImageTarget,
|
||||||
internal_format: u32,
|
data_type: TexDataType,
|
||||||
width: i32,
|
internal_format: TexFormat,
|
||||||
height: i32,
|
level: u32,
|
||||||
border: i32,
|
width: u32,
|
||||||
format: u32,
|
height: u32,
|
||||||
data_type: u32,
|
_border: u32,
|
||||||
pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
|
pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
|
||||||
// This should be validated before reaching this function
|
if internal_format == TexFormat::RGBA &&
|
||||||
debug_assert!(self.validate_tex_image_2d_parameters(target, level,
|
data_type == TexDataType::UnsignedByte &&
|
||||||
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 &&
|
|
||||||
self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) {
|
self.texture_unpacking_settings.get().contains(PREMULTIPLY_ALPHA) {
|
||||||
// TODO(emilio): premultiply here.
|
// TODO(emilio): premultiply here.
|
||||||
}
|
}
|
||||||
|
@ -603,15 +384,18 @@ impl WebGLRenderingContext {
|
||||||
|
|
||||||
// TexImage2D depth is always equal to 1
|
// TexImage2D depth is always equal to 1
|
||||||
handle_potential_webgl_error!(self, texture.initialize(target,
|
handle_potential_webgl_error!(self, texture.initialize(target,
|
||||||
width as u32,
|
width,
|
||||||
height as u32, 1,
|
height, 1,
|
||||||
internal_format,
|
internal_format,
|
||||||
level as u32,
|
level,
|
||||||
Some(data_type)));
|
Some(data_type)));
|
||||||
|
|
||||||
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
|
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
|
||||||
let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
|
let msg = WebGLCommand::TexImage2D(target.as_gl_constant(), level as i32,
|
||||||
width, height, format, data_type, pixels);
|
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
|
self.ipc_renderer
|
||||||
.send(CanvasMsg::WebGL(msg))
|
.send(CanvasMsg::WebGL(msg))
|
||||||
|
@ -619,54 +403,29 @@ impl WebGLRenderingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tex_sub_image_2d(&self,
|
fn tex_sub_image_2d(&self,
|
||||||
target: u32,
|
texture: Root<WebGLTexture>,
|
||||||
level: i32,
|
target: TexImageTarget,
|
||||||
|
level: u32,
|
||||||
xoffset: i32,
|
xoffset: i32,
|
||||||
yoffset: i32,
|
yoffset: i32,
|
||||||
width: i32,
|
width: u32,
|
||||||
height: i32,
|
height: u32,
|
||||||
format: u32,
|
format: TexFormat,
|
||||||
data_type: u32,
|
data_type: TexDataType,
|
||||||
pixels: Vec<u8>) { // NB: pixels should NOT be premultipied
|
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
|
// We have already validated level
|
||||||
let face_index = self.face_index_for_target(target).unwrap();
|
let image_info = texture.image_info_for_target(&target, level);
|
||||||
let image_info = texture.image_info_at_face(face_index, level as u32);
|
|
||||||
|
|
||||||
// GL_INVALID_VALUE is generated if:
|
// GL_INVALID_VALUE is generated if:
|
||||||
// - xoffset or yoffset is less than 0
|
// - xoffset or yoffset is less than 0
|
||||||
// - x offset plus the width is greater than the texture width
|
// - x offset plus the width is greater than the texture width
|
||||||
// - y offset plus the height is greater than the texture height
|
// - y offset plus the height is greater than the texture height
|
||||||
if xoffset < 0 || ((xoffset + width) as u32) > image_info.width() ||
|
if xoffset < 0 || (xoffset as u32 + width) > image_info.width() ||
|
||||||
yoffset < 0 || ((yoffset + height) as u32) > image_info.height() {
|
yoffset < 0 || (yoffset as u32 + height) > image_info.height() {
|
||||||
return self.webgl_error(InvalidValue);
|
return self.webgl_error(InvalidValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using internal_format() to do this check
|
// NB: format and internal_format must match.
|
||||||
// because we are sure format is as same as internal_format.
|
|
||||||
if format != image_info.internal_format().unwrap() ||
|
if format != image_info.internal_format().unwrap() ||
|
||||||
data_type != image_info.data_type().unwrap() {
|
data_type != image_info.data_type().unwrap() {
|
||||||
return self.webgl_error(InvalidOperation);
|
return self.webgl_error(InvalidOperation);
|
||||||
|
@ -675,8 +434,11 @@ impl WebGLRenderingContext {
|
||||||
// TODO(emilio): Flip Y axis if necessary here
|
// TODO(emilio): Flip Y axis if necessary here
|
||||||
|
|
||||||
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
|
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
|
||||||
let msg = WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset,
|
let msg = WebGLCommand::TexSubImage2D(target.as_gl_constant(),
|
||||||
width, height, format, data_type, pixels);
|
level as i32, xoffset, yoffset,
|
||||||
|
width as i32, height as i32,
|
||||||
|
format.as_gl_constant(),
|
||||||
|
data_type.as_gl_constant(), pixels);
|
||||||
|
|
||||||
self.ipc_renderer
|
self.ipc_renderer
|
||||||
.send(CanvasMsg::WebGL(msg))
|
.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
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
fn CopyTexImage2D(&self, target: u32, level: i32, internal_format: u32,
|
fn CopyTexImage2D(&self, target: u32, level: i32, internal_format: u32,
|
||||||
x: i32, y: i32, width: i32, height: i32, border: i32) {
|
x: i32, y: i32, width: i32, height: i32, border: i32) {
|
||||||
// Validate common tex image parameters
|
let validator = CommonTexImage2DValidator::new(self, target, level,
|
||||||
if !self.validate_common_tex_image_parameters(target, level, width, height) ||
|
internal_format, width,
|
||||||
!self.validate_tex_internal_format(internal_format) {
|
height, border);
|
||||||
return;
|
let CommonTexImage2DValidatorResult {
|
||||||
}
|
texture,
|
||||||
|
target,
|
||||||
// GL_INVALID_VALUE is generated if the border is not 0
|
level,
|
||||||
if border != 0 {
|
internal_format,
|
||||||
self.webgl_error(InvalidValue);
|
width,
|
||||||
return;
|
height,
|
||||||
}
|
border,
|
||||||
|
} = match validator.validate() {
|
||||||
let texture = self.texture_for_target(target).unwrap();
|
Ok(result) => result,
|
||||||
let face_index = self.face_index_for_target(target).unwrap();
|
Err(_) => return,
|
||||||
|
|
||||||
// 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 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
|
// GL_INVALID_OPERATION is generated if the color buffer cannot be
|
||||||
// converted to the internal_format
|
// converted to the internal_format.
|
||||||
if invalid_format {
|
if let Some(old_internal_format) = image_info.internal_format() {
|
||||||
self.webgl_error(InvalidOperation);
|
if old_internal_format.components() > internal_format.components() {
|
||||||
return;
|
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,
|
handle_potential_webgl_error!(self, texture.initialize(target,
|
||||||
width as u32,
|
width as u32,
|
||||||
height as u32, 1,
|
height as u32, 1,
|
||||||
|
@ -1102,8 +860,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
level as u32,
|
level as u32,
|
||||||
None));
|
None));
|
||||||
|
|
||||||
let msg = WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y,
|
let msg = WebGLCommand::CopyTexImage2D(target.as_gl_constant(),
|
||||||
width, height, border);
|
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()
|
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
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
fn CopyTexSubImage2D(&self, target: u32, level: i32, xoffset: i32, yoffset: i32,
|
fn CopyTexSubImage2D(&self, target: u32, level: i32, xoffset: i32, yoffset: i32,
|
||||||
x: i32, y: i32, width: i32, height: i32) {
|
x: i32, y: i32, width: i32, height: i32) {
|
||||||
// Validate common tex image parameters
|
// NB: We use a dummy (valid) format and border in order to reuse the
|
||||||
if !self.validate_common_tex_image_parameters(target, level, width, height) {
|
// common validations, but this should have its own validator.
|
||||||
return;
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
let texture = self.texture_for_target(target).unwrap();
|
let image_info = texture.image_info_for_target(&target, level);
|
||||||
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);
|
|
||||||
|
|
||||||
// GL_INVALID_VALUE is generated if:
|
// GL_INVALID_VALUE is generated if:
|
||||||
// - xoffset or yoffset is less than 0
|
// - xoffset or yoffset is less than 0
|
||||||
// - x offset plus the width is greater than the texture width
|
// - x offset plus the width is greater than the texture width
|
||||||
// - y offset plus the height is greater than the texture height
|
// - y offset plus the height is greater than the texture height
|
||||||
if xoffset < 0 || ((xoffset + width) as u32) > image_info.width() ||
|
if xoffset < 0 || (xoffset as u32 + width) > image_info.width() ||
|
||||||
yoffset < 0 || ((yoffset + height) as u32) > image_info.height() {
|
yoffset < 0 || (yoffset as u32 + height) > image_info.height() {
|
||||||
self.webgl_error(InvalidValue);
|
self.webgl_error(InvalidValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = WebGLCommand::CopyTexSubImage2D(target, level, xoffset, yoffset,
|
let msg = WebGLCommand::CopyTexSubImage2D(target.as_gl_constant(),
|
||||||
x, y, width, height);
|
level as i32, xoffset, yoffset,
|
||||||
|
x, y,
|
||||||
|
width as i32, height as i32);
|
||||||
|
|
||||||
self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap();
|
self.ipc_renderer.send(CanvasMsg::WebGL(msg)).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -2104,15 +1876,23 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
format: u32,
|
format: u32,
|
||||||
data_type: u32,
|
data_type: u32,
|
||||||
data: Option<*mut JSObject>) {
|
data: Option<*mut JSObject>) {
|
||||||
if !self.validate_tex_image_2d_parameters(target,
|
let validator = TexImage2DValidator::new(self, target, level,
|
||||||
level,
|
internal_format, width, height,
|
||||||
internal_format,
|
border, format, data_type);
|
||||||
width, height,
|
|
||||||
border,
|
let TexImage2DValidatorResult {
|
||||||
format,
|
texture,
|
||||||
data_type) {
|
target,
|
||||||
return; // Error handled in validate()
|
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,
|
let expected_byte_length = match self.validate_tex_image_2d_data(width,
|
||||||
height,
|
height,
|
||||||
|
@ -2136,10 +1916,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
return self.webgl_error(InvalidOperation);
|
return self.webgl_error(InvalidOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tex_image_2d(target, level,
|
self.tex_image_2d(texture, target, data_type, format,
|
||||||
internal_format,
|
level, width, height, border, buff)
|
||||||
width, height, border,
|
|
||||||
format, data_type, buff)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
|
@ -2156,17 +1934,28 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
Err(_) => return,
|
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,
|
let validator = TexImage2DValidator::new(self,
|
||||||
internal_format,
|
target, level, internal_format,
|
||||||
size.width, size.height, 0,
|
size.width, size.height,
|
||||||
format, data_type, pixels);
|
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
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
|
@ -2181,15 +1970,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
format: u32,
|
format: u32,
|
||||||
data_type: u32,
|
data_type: u32,
|
||||||
data: Option<*mut JSObject>) {
|
data: Option<*mut JSObject>) {
|
||||||
if !self.validate_tex_image_2d_parameters(target,
|
let validator = TexImage2DValidator::new(self, target, level,
|
||||||
level,
|
format, width, height,
|
||||||
format,
|
0, format, data_type);
|
||||||
width, height,
|
let TexImage2DValidatorResult {
|
||||||
0,
|
texture,
|
||||||
format,
|
target,
|
||||||
data_type) {
|
width,
|
||||||
return; // Error handled in validate()
|
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,
|
let expected_byte_length = match self.validate_tex_image_2d_data(width,
|
||||||
height,
|
height,
|
||||||
|
@ -2214,10 +2010,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
return self.webgl_error(InvalidOperation);
|
return self.webgl_error(InvalidOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tex_sub_image_2d(target, level,
|
self.tex_sub_image_2d(texture, target, level, xoffset, yoffset,
|
||||||
xoffset, yoffset,
|
width, height, format, data_type, buff);
|
||||||
width, height,
|
|
||||||
format, data_type, buff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
|
@ -2229,23 +2023,30 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
|
||||||
format: u32,
|
format: u32,
|
||||||
data_type: u32,
|
data_type: u32,
|
||||||
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
|
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
|
||||||
// Get pixels from image source
|
|
||||||
let (pixels, size) = match self.get_image_pixels(source) {
|
let (pixels, size) = match self.get_image_pixels(source) {
|
||||||
Ok((pixels, size)) => (pixels, size),
|
Ok((pixels, size)) => (pixels, size),
|
||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
// NB: Border must be zero
|
let validator = TexImage2DValidator::new(self, target, level, format,
|
||||||
if !self.validate_tex_image_2d_parameters(target, level, format,
|
size.width, size.height,
|
||||||
size.width, size.height, 0,
|
0, format, data_type);
|
||||||
format, data_type) {
|
let TexImage2DValidatorResult {
|
||||||
return; // Error handled in validate()
|
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,
|
self.tex_sub_image_2d(texture, target, level, xoffset, yoffset,
|
||||||
xoffset, yoffset,
|
width, height, format, data_type, pixels);
|
||||||
size.width, size.height,
|
|
||||||
format, data_type, pixels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
|
||||||
|
|
|
@ -10,6 +10,7 @@ use dom::bindings::codegen::Bindings::WebGLTextureBinding;
|
||||||
use dom::bindings::global::GlobalRef;
|
use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::bindings::reflector::reflect_dom_object;
|
use dom::bindings::reflector::reflect_dom_object;
|
||||||
|
use dom::webgl_validations::types::{TexImageTarget, TexFormat, TexDataType};
|
||||||
use dom::webglobject::WebGLObject;
|
use dom::webglobject::WebGLObject;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -104,13 +105,13 @@ impl WebGLTexture {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize(&self,
|
pub fn initialize(&self,
|
||||||
target: u32,
|
target: TexImageTarget,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
internal_format: u32,
|
internal_format: TexFormat,
|
||||||
level: u32,
|
level: u32,
|
||||||
data_type: Option<u32>) -> WebGLResult<()> {
|
data_type: Option<TexDataType>) -> WebGLResult<()> {
|
||||||
let image_info = ImageInfo {
|
let image_info = ImageInfo {
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
@ -120,17 +121,8 @@ impl WebGLTexture {
|
||||||
data_type: data_type,
|
data_type: data_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
let face = match target {
|
let face_index = self.face_index_for_target(&target);
|
||||||
constants::TEXTURE_2D | constants::TEXTURE_CUBE_MAP_POSITIVE_X => 0,
|
self.set_image_infos_at_level_and_face(level, face_index, image_info);
|
||||||
constants::TEXTURE_CUBE_MAP_NEGATIVE_X => 1,
|
|
||||||
constants::TEXTURE_CUBE_MAP_POSITIVE_Y => 2,
|
|
||||||
constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => 3,
|
|
||||||
constants::TEXTURE_CUBE_MAP_POSITIVE_Z => 4,
|
|
||||||
constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => 5,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.set_image_infos_at_level_and_face(level, face, image_info);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +304,27 @@ impl WebGLTexture {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn image_info_at_face(&self, face: u8, level: u32) -> ImageInfo {
|
fn face_index_for_target(&self,
|
||||||
|
target: &TexImageTarget) -> u8 {
|
||||||
|
match *target {
|
||||||
|
TexImageTarget::Texture2D => 0,
|
||||||
|
TexImageTarget::CubeMapPositiveX => 0,
|
||||||
|
TexImageTarget::CubeMapNegativeX => 1,
|
||||||
|
TexImageTarget::CubeMapPositiveY => 2,
|
||||||
|
TexImageTarget::CubeMapNegativeY => 3,
|
||||||
|
TexImageTarget::CubeMapPositiveZ => 4,
|
||||||
|
TexImageTarget::CubeMapNegativeZ => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn image_info_for_target(&self,
|
||||||
|
target: &TexImageTarget,
|
||||||
|
level: u32) -> ImageInfo {
|
||||||
|
let face_index = self.face_index_for_target(&target);
|
||||||
|
self.image_info_at_face(face_index, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_info_at_face(&self, face: u8, level: u32) -> ImageInfo {
|
||||||
let pos = (level * self.face_count.get() as u32) + face as u32;
|
let pos = (level * self.face_count.get() as u32) + face as u32;
|
||||||
self.image_info_array.borrow()[pos as usize]
|
self.image_info_array.borrow()[pos as usize]
|
||||||
}
|
}
|
||||||
|
@ -347,9 +359,9 @@ pub struct ImageInfo {
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
internal_format: Option<u32>,
|
internal_format: Option<TexFormat>,
|
||||||
is_initialized: bool,
|
is_initialized: bool,
|
||||||
data_type: Option<u32>,
|
data_type: Option<TexDataType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageInfo {
|
impl ImageInfo {
|
||||||
|
@ -372,16 +384,18 @@ impl ImageInfo {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn internal_format(&self) -> Option<u32> {
|
pub fn internal_format(&self) -> Option<TexFormat> {
|
||||||
self.internal_format
|
self.internal_format
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_type(&self) -> Option<u32> {
|
pub fn data_type(&self) -> Option<TexDataType> {
|
||||||
self.data_type
|
self.data_type
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_power_of_two(&self) -> bool {
|
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()
|
self.width.is_power_of_two() &&
|
||||||
|
self.height.is_power_of_two() &&
|
||||||
|
self.depth.is_power_of_two()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_initialized(&self) -> bool {
|
fn is_initialized(&self) -> bool {
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[texSubImage2D.html]
|
|
||||||
type: testharness
|
|
||||||
[WebGL test #0: testTexSubImage2D]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue