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