script/dom/webgl/validations/
tex_image_3d.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use canvas_traits::webgl::WebGLError::*;
6use canvas_traits::webgl::{TexDataType, TexFormat};
7use js::jsapi::Type;
8use js::typedarray::ArrayBufferView;
9
10use super::WebGLValidator;
11use super::tex_image_2d::TexImageValidationError;
12use super::types::TexImageTarget;
13use crate::dom::bindings::root::DomRoot;
14use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
15use crate::dom::webgl::webgltexture::WebGLTexture;
16
17fn log2(n: u32) -> u32 {
18    31 - n.leading_zeros()
19}
20
21pub(crate) struct CommonTexImage3DValidator<'a> {
22    context: &'a WebGLRenderingContext,
23    target: u32,
24    level: i32,
25    internal_format: u32,
26    width: i32,
27    height: i32,
28    depth: i32,
29    border: i32,
30}
31
32pub(crate) struct CommonTexImage3DValidatorResult {
33    pub(crate) texture: DomRoot<WebGLTexture>,
34    pub(crate) target: TexImageTarget,
35    pub(crate) level: u32,
36    pub(crate) internal_format: TexFormat,
37    pub(crate) width: u32,
38    pub(crate) height: u32,
39    pub(crate) depth: u32,
40    pub(crate) border: u32,
41}
42
43impl WebGLValidator for CommonTexImage3DValidator<'_> {
44    type Error = TexImageValidationError;
45    type ValidatedOutput = CommonTexImage3DValidatorResult;
46    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
47        // GL_INVALID_ENUM is generated if target is not GL_TEXTURE_3D or GL_TEXTURE_2D_ARRAY.
48        let target = match TexImageTarget::from_gl_constant(self.target) {
49            Some(target) if target.dimensions() == 3 => target,
50            _ => {
51                self.context.webgl_error(InvalidEnum);
52                return Err(TexImageValidationError::InvalidTextureTarget(self.target));
53            },
54        };
55
56        let texture = self
57            .context
58            .textures()
59            .active_texture_for_image_target(target);
60        let limits = self.context.limits();
61
62        let max_size = limits.max_3d_tex_size;
63
64        //  If an attempt is made to call this function with no WebGLTexture
65        //  bound, an INVALID_OPERATION error is generated.
66        let texture = match texture {
67            Some(texture) => texture,
68            None => {
69                self.context.webgl_error(InvalidOperation);
70                return Err(TexImageValidationError::TextureTargetNotBound(self.target));
71            },
72        };
73
74        // GL_INVALID_ENUM is generated if internal_format is not an accepted
75        // format.
76        let internal_format = match TexFormat::from_gl_constant(self.internal_format) {
77            Some(format)
78                if format.required_webgl_version() <= self.context.webgl_version() &&
79                    format.usable_as_internal() =>
80            {
81                format
82            },
83            _ => {
84                self.context.webgl_error(InvalidEnum);
85                return Err(TexImageValidationError::InvalidTextureFormat);
86            },
87        };
88
89        // GL_INVALID_VALUE is generated if width, height, or depth is less than 0 or greater than
90        // GL_MAX_3D_TEXTURE_SIZE.
91        if self.width < 0 || self.height < 0 || self.depth < 0 {
92            self.context.webgl_error(InvalidValue);
93            return Err(TexImageValidationError::NegativeDimension);
94        }
95        let width = self.width as u32;
96        let height = self.height as u32;
97        let depth = self.depth as u32;
98        let level = self.level as u32;
99        if width > max_size || height > max_size || level > max_size {
100            self.context.webgl_error(InvalidValue);
101            return Err(TexImageValidationError::TextureTooBig);
102        }
103
104        // GL_INVALID_VALUE may be generated if level is greater than log2(max),
105        // where max is the returned value of GL_MAX_3D_TEXTURE_SIZE.
106        if self.level < 0 {
107            self.context.webgl_error(InvalidValue);
108            return Err(TexImageValidationError::NegativeLevel);
109        }
110        if level > log2(max_size) {
111            self.context.webgl_error(InvalidValue);
112            return Err(TexImageValidationError::LevelTooHigh);
113        }
114
115        // GL_INVALID_VALUE is generated if border is not 0 or 1.
116        if !(self.border == 0 || self.border == 1) {
117            self.context.webgl_error(InvalidValue);
118            return Err(TexImageValidationError::InvalidBorder);
119        }
120
121        Ok(CommonTexImage3DValidatorResult {
122            texture,
123            target,
124            level,
125            internal_format,
126            width,
127            height,
128            depth,
129            border: self.border as u32,
130        })
131    }
132}
133
134impl<'a> CommonTexImage3DValidator<'a> {
135    #[allow(clippy::too_many_arguments)]
136    pub(crate) fn new(
137        context: &'a WebGLRenderingContext,
138        target: u32,
139        level: i32,
140        internal_format: u32,
141        width: i32,
142        height: i32,
143        depth: i32,
144        border: i32,
145    ) -> Self {
146        CommonTexImage3DValidator {
147            context,
148            target,
149            level,
150            internal_format,
151            width,
152            height,
153            depth,
154            border,
155        }
156    }
157}
158
159pub(crate) struct TexImage3DValidator<'a> {
160    common_validator: CommonTexImage3DValidator<'a>,
161    format: u32,
162    data_type: u32,
163    data: &'a Option<ArrayBufferView>,
164}
165
166impl<'a> TexImage3DValidator<'a> {
167    /// TODO: Move data validation logic here.
168    #[allow(clippy::too_many_arguments)]
169    pub(crate) fn new(
170        context: &'a WebGLRenderingContext,
171        target: u32,
172        level: i32,
173        internal_format: u32,
174        width: i32,
175        height: i32,
176        depth: i32,
177        border: i32,
178        format: u32,
179        data_type: u32,
180        data: &'a Option<ArrayBufferView>,
181    ) -> Self {
182        TexImage3DValidator {
183            common_validator: CommonTexImage3DValidator::new(
184                context,
185                target,
186                level,
187                internal_format,
188                width,
189                height,
190                depth,
191                border,
192            ),
193            format,
194            data_type,
195            data,
196        }
197    }
198}
199
200/// The validated result of a TexImage2DValidator-validated call.
201pub(crate) struct TexImage3DValidatorResult {
202    /// NB: width, height and level are already unsigned after validation.
203    pub(crate) width: u32,
204    pub(crate) height: u32,
205    pub(crate) depth: u32,
206    pub(crate) level: u32,
207    pub(crate) border: u32,
208    pub(crate) texture: DomRoot<WebGLTexture>,
209    pub(crate) target: TexImageTarget,
210    pub(crate) internal_format: TexFormat,
211    pub(crate) format: TexFormat,
212    pub(crate) data_type: TexDataType,
213}
214
215/// TexImage3d validator as per
216/// <https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage3D.xml>
217impl WebGLValidator for TexImage3DValidator<'_> {
218    type ValidatedOutput = TexImage3DValidatorResult;
219    type Error = TexImageValidationError;
220
221    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
222        let context = self.common_validator.context;
223        let CommonTexImage3DValidatorResult {
224            texture,
225            target,
226            level,
227            internal_format,
228            width,
229            height,
230            depth,
231            border,
232        } = self.common_validator.validate()?;
233
234        // GL_INVALID_ENUM is generated if format is not an accepted format constant.
235        // Format constants other than GL_STENCIL_INDEX and GL_DEPTH_COMPONENT are accepted.
236        let data_type = match TexDataType::from_gl_constant(self.data_type) {
237            Some(data_type) if data_type.required_webgl_version() <= context.webgl_version() => {
238                data_type
239            },
240            _ => {
241                context.webgl_error(InvalidEnum);
242                return Err(TexImageValidationError::InvalidDataType);
243            },
244        };
245        let format = match TexFormat::from_gl_constant(self.format) {
246            Some(format) if format.required_webgl_version() <= context.webgl_version() => format,
247            _ => {
248                context.webgl_error(InvalidEnum);
249                return Err(TexImageValidationError::InvalidTextureFormat);
250            },
251        };
252
253        // GL_INVALID_OPERATION is generated if format does not match
254        // internal_format.
255        if format != internal_format.to_unsized() {
256            context.webgl_error(InvalidOperation);
257            return Err(TexImageValidationError::TextureFormatMismatch);
258        }
259
260        if !internal_format.compatible_data_types().contains(&data_type) {
261            context.webgl_error(InvalidOperation);
262            return Err(TexImageValidationError::TextureFormatMismatch);
263        }
264
265        // GL_INVALID_OPERATION is generated if target is GL_TEXTURE_3D and
266        // format is GL_DEPTH_COMPONENT, or GL_DEPTH_STENCIL.
267        if target == TexImageTarget::Texture3D &&
268            (format == TexFormat::DepthComponent || format == TexFormat::DepthStencil)
269        {
270            context.webgl_error(InvalidOperation);
271            return Err(TexImageValidationError::InvalidTypeForFormat);
272        }
273
274        // If srcData is non-null, the type of srcData must match the type according to
275        // the above table; otherwise, generate an INVALID_OPERATION error.
276        let element_size = data_type.element_size();
277        let received_size = match self.data {
278            Some(buf) => match buf.get_array_type() {
279                Type::Int8 => 1,
280                Type::Uint8 => 1,
281                Type::Int16 => 2,
282                Type::Uint16 => 2,
283                Type::Int32 => 4,
284                Type::Uint32 => 4,
285                Type::Float32 => 4,
286                _ => {
287                    context.webgl_error(InvalidOperation);
288                    return Err(TexImageValidationError::InvalidTypeForFormat);
289                },
290            },
291            None => element_size,
292        };
293        if received_size != element_size {
294            context.webgl_error(InvalidOperation);
295            return Err(TexImageValidationError::InvalidTypeForFormat);
296        }
297
298        Ok(TexImage3DValidatorResult {
299            width,
300            height,
301            depth,
302            level,
303            border,
304            texture,
305            target,
306            internal_format,
307            format,
308            data_type,
309        })
310    }
311}