Skip to main content

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 js::jsapi::Type;
6use js::typedarray::ArrayBufferView;
7use servo_canvas_traits::webgl::WebGLError::*;
8use servo_canvas_traits::webgl::{TexDataType, TexFormat};
9
10use super::WebGLValidator;
11use super::tex_image_2d::{TexImageValidationError, bound_texture, validate_internal_format};
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 = bound_texture(self.context, target)?;
53        let internal_format = validate_internal_format(self.context, self.internal_format)?;
54
55        // GL_INVALID_VALUE is generated if width, height, depth or level is less than 0.
56        if self.width < 0 || self.height < 0 || self.depth < 0 {
57            self.context.webgl_error(InvalidValue);
58            return Err(TexImageValidationError::NegativeDimension);
59        }
60        if self.level < 0 {
61            self.context.webgl_error(InvalidValue);
62            return Err(TexImageValidationError::NegativeLevel);
63        }
64        let width = self.width as u32;
65        let height = self.height as u32;
66        let depth = self.depth as u32;
67        let level = self.level as u32;
68
69        // A 2D array takes its width and height limit from GL_MAX_TEXTURE_SIZE and its
70        // layer count from GL_MAX_ARRAY_TEXTURE_LAYERS (OpenGL ES 3.0.6 section 3.8.3).
71        let limits = self.context.limits();
72        let (max_size, max_layers) = match target {
73            TexImageTarget::Texture2DArray => {
74                (limits.max_tex_size, Some(limits.max_array_texture_layers))
75            },
76            _ => (limits.max_3d_tex_size, None),
77        };
78
79        // GL_INVALID_VALUE may be generated if level is greater than log2(max_size).
80        if level > max_size.ilog2() {
81            self.context.webgl_error(InvalidValue);
82            return Err(TexImageValidationError::LevelTooHigh);
83        }
84
85        // The size limit halves at each level; the layer limit holds at every level.
86        let max_size_for_level = max_size >> level;
87        let max_depth = max_layers.unwrap_or(max_size_for_level);
88        if width > max_size_for_level || height > max_size_for_level || depth > max_depth {
89            self.context.webgl_error(InvalidValue);
90            return Err(TexImageValidationError::TextureTooBig);
91        }
92
93        // GL_INVALID_VALUE is generated if border is not 0 or 1.
94        if !(self.border == 0 || self.border == 1) {
95            self.context.webgl_error(InvalidValue);
96            return Err(TexImageValidationError::InvalidBorder);
97        }
98
99        Ok(CommonTexImage3DValidatorResult {
100            texture,
101            target,
102            level,
103            internal_format,
104            width,
105            height,
106            depth,
107            border: self.border as u32,
108        })
109    }
110}
111
112impl<'a> CommonTexImage3DValidator<'a> {
113    #[expect(clippy::too_many_arguments)]
114    pub(crate) fn new(
115        context: &'a WebGLRenderingContext,
116        target: u32,
117        level: i32,
118        internal_format: u32,
119        width: i32,
120        height: i32,
121        depth: i32,
122        border: i32,
123    ) -> Self {
124        CommonTexImage3DValidator {
125            context,
126            target,
127            level,
128            internal_format,
129            width,
130            height,
131            depth,
132            border,
133        }
134    }
135}
136
137pub(crate) struct TexImage3DValidator<'a> {
138    common_validator: CommonTexImage3DValidator<'a>,
139    format: u32,
140    data_type: u32,
141    data: &'a Option<ArrayBufferView>,
142}
143
144impl<'a> TexImage3DValidator<'a> {
145    /// TODO: Move data validation logic here.
146    #[expect(clippy::too_many_arguments)]
147    pub(crate) fn new(
148        context: &'a WebGLRenderingContext,
149        target: u32,
150        level: i32,
151        internal_format: u32,
152        width: i32,
153        height: i32,
154        depth: i32,
155        border: i32,
156        format: u32,
157        data_type: u32,
158        data: &'a Option<ArrayBufferView>,
159    ) -> Self {
160        TexImage3DValidator {
161            common_validator: CommonTexImage3DValidator::new(
162                context,
163                target,
164                level,
165                internal_format,
166                width,
167                height,
168                depth,
169                border,
170            ),
171            format,
172            data_type,
173            data,
174        }
175    }
176}
177
178/// The validated result of a TexImage2DValidator-validated call.
179pub(crate) struct TexImage3DValidatorResult {
180    /// NB: width, height and level are already unsigned after validation.
181    pub(crate) width: u32,
182    pub(crate) height: u32,
183    pub(crate) depth: u32,
184    pub(crate) level: u32,
185    pub(crate) border: u32,
186    pub(crate) texture: DomRoot<WebGLTexture>,
187    pub(crate) target: TexImageTarget,
188    pub(crate) internal_format: TexFormat,
189    pub(crate) format: TexFormat,
190    pub(crate) data_type: TexDataType,
191}
192
193/// TexImage3d validator as per
194/// <https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage3D.xml>
195impl WebGLValidator for TexImage3DValidator<'_> {
196    type ValidatedOutput = TexImage3DValidatorResult;
197    type Error = TexImageValidationError;
198
199    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
200        let context = self.common_validator.context;
201        let CommonTexImage3DValidatorResult {
202            texture,
203            target,
204            level,
205            internal_format,
206            width,
207            height,
208            depth,
209            border,
210        } = self.common_validator.validate()?;
211
212        // GL_INVALID_ENUM is generated if format is not an accepted format constant.
213        // Format constants other than GL_STENCIL_INDEX and GL_DEPTH_COMPONENT are accepted.
214        let version = context.webgl_version();
215        let data_type = match TexDataType::from_webgl_constant(self.data_type, version) {
216            Some(data_type) if data_type.required_webgl_version() <= version => data_type,
217            _ => {
218                context.webgl_error(InvalidEnum);
219                return Err(TexImageValidationError::InvalidDataType);
220            },
221        };
222        let format = match TexFormat::from_gl_constant(self.format) {
223            Some(format) if format.required_webgl_version() <= context.webgl_version() => format,
224            _ => {
225                context.webgl_error(InvalidEnum);
226                return Err(TexImageValidationError::InvalidTextureFormat);
227            },
228        };
229
230        // GL_INVALID_OPERATION is generated if format does not match
231        // internal_format.
232        if format != internal_format.to_unsized() {
233            context.webgl_error(InvalidOperation);
234            return Err(TexImageValidationError::TextureFormatMismatch);
235        }
236
237        if !internal_format.compatible_data_types().contains(&data_type) {
238            context.webgl_error(InvalidOperation);
239            return Err(TexImageValidationError::TextureFormatMismatch);
240        }
241
242        // GL_INVALID_OPERATION is generated if target is GL_TEXTURE_3D and
243        // format is GL_DEPTH_COMPONENT, or GL_DEPTH_STENCIL.
244        if target == TexImageTarget::Texture3D &&
245            (format == TexFormat::DepthComponent || format == TexFormat::DepthStencil)
246        {
247            context.webgl_error(InvalidOperation);
248            return Err(TexImageValidationError::InvalidTypeForFormat);
249        }
250
251        // If srcData is non-null, the type of srcData must match the type according to
252        // the above table; otherwise, generate an INVALID_OPERATION error.
253        let element_size = data_type.element_size();
254        let received_size = match self.data {
255            Some(buf) => match buf.get_array_type() {
256                Type::Int8 => 1,
257                Type::Uint8 => 1,
258                Type::Int16 => 2,
259                Type::Uint16 => 2,
260                Type::Int32 => 4,
261                Type::Uint32 => 4,
262                Type::Float32 => 4,
263                _ => {
264                    context.webgl_error(InvalidOperation);
265                    return Err(TexImageValidationError::InvalidTypeForFormat);
266                },
267            },
268            None => element_size,
269        };
270        if received_size != element_size {
271            context.webgl_error(InvalidOperation);
272            return Err(TexImageValidationError::InvalidTypeForFormat);
273        }
274
275        Ok(TexImage3DValidatorResult {
276            width,
277            height,
278            depth,
279            level,
280            border,
281            texture,
282            target,
283            internal_format,
284            format,
285            data_type,
286        })
287    }
288}