script/dom/webgl/validations/
tex_image_2d.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 std::{self, cmp, fmt};
6
7use canvas_traits::webgl::WebGLError::*;
8use canvas_traits::webgl::{TexDataType, TexFormat};
9
10use super::WebGLValidator;
11use super::types::TexImageTarget;
12use crate::dom::bindings::root::DomRoot;
13use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
14use crate::dom::webgl::webgltexture::{
15    ImageInfo, TexCompression, TexCompressionValidation, WebGLTexture,
16};
17
18/// The errors that the texImage* family of functions can generate.
19#[derive(Debug)]
20pub(crate) enum TexImageValidationError {
21    /// An invalid texture target was passed, it contains the invalid target.
22    InvalidTextureTarget(u32),
23    /// The passed texture target was not bound.
24    TextureTargetNotBound(u32),
25    /// Invalid texture dimensions were given.
26    InvalidCubicTextureDimensions,
27    /// A negative level was passed.
28    NegativeLevel,
29    /// A level too high to be allowed by the implementation was passed.
30    LevelTooHigh,
31    /// A level less than an allowed minimal value was passed.
32    LevelTooLow,
33    /// A depth less than an allowed minimal value was passed.
34    DepthTooLow,
35    /// A negative width and height was passed.
36    NegativeDimension,
37    /// A bigger with and height were passed than what the implementation
38    /// allows.
39    TextureTooBig,
40    /// An invalid data type was passed.
41    InvalidDataType,
42    /// An invalid texture format was passed.
43    InvalidTextureFormat,
44    /// Format did not match internal_format.
45    TextureFormatMismatch,
46    /// Invalid data type for the given format.
47    InvalidTypeForFormat,
48    /// Invalid border
49    InvalidBorder,
50    /// Expected a power of two texture.
51    NonPotTexture,
52    /// Unrecognized texture compression format.
53    InvalidCompressionFormat,
54    /// Invalid X/Y texture offset parameters.
55    InvalidOffsets,
56}
57
58impl std::error::Error for TexImageValidationError {}
59
60impl fmt::Display for TexImageValidationError {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        use self::TexImageValidationError::*;
63        let description = match *self {
64            InvalidTextureTarget(texture_id) => &format!("Invalid texture target ({texture_id})"),
65            TextureTargetNotBound(texture_id) => &format!("Texture was not bound {texture_id}"),
66            InvalidCubicTextureDimensions => {
67                "Invalid dimensions were given for a cubic texture target"
68            },
69            NegativeLevel => "A negative level was passed",
70            LevelTooHigh => "Level too high",
71            LevelTooLow => "Level too low",
72            DepthTooLow => "Depth too low",
73            NegativeDimension => "Negative dimensions were passed",
74            TextureTooBig => "Dimensions given are too big",
75            InvalidDataType => "Invalid data type",
76            InvalidTextureFormat => "Invalid texture format",
77            TextureFormatMismatch => "Texture format mismatch",
78            InvalidTypeForFormat => "Invalid type for the given format",
79            InvalidBorder => "Invalid border",
80            NonPotTexture => "Expected a power of two texture",
81            InvalidCompressionFormat => "Unrecognized texture compression format",
82            InvalidOffsets => "Invalid X/Y texture offset parameters",
83        };
84        write!(f, "TexImageValidationError({})", description)
85    }
86}
87
88fn log2(n: u32) -> u32 {
89    31 - n.leading_zeros()
90}
91
92pub(crate) struct CommonTexImage2DValidator<'a> {
93    context: &'a WebGLRenderingContext,
94    target: u32,
95    level: i32,
96    internal_format: u32,
97    width: i32,
98    height: i32,
99    border: i32,
100}
101
102pub(crate) struct CommonTexImage2DValidatorResult {
103    pub(crate) texture: DomRoot<WebGLTexture>,
104    pub(crate) target: TexImageTarget,
105    pub(crate) level: u32,
106    pub(crate) internal_format: TexFormat,
107    pub(crate) width: u32,
108    pub(crate) height: u32,
109    pub(crate) border: u32,
110}
111
112impl WebGLValidator for CommonTexImage2DValidator<'_> {
113    type Error = TexImageValidationError;
114    type ValidatedOutput = CommonTexImage2DValidatorResult;
115    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
116        // GL_INVALID_ENUM is generated if target is not GL_TEXTURE_2D,
117        // GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
118        // GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
119        // GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.
120        let target = match TexImageTarget::from_gl_constant(self.target) {
121            Some(target) if target.dimensions() == 2 => target,
122            _ => {
123                self.context.webgl_error(InvalidEnum);
124                return Err(TexImageValidationError::InvalidTextureTarget(self.target));
125            },
126        };
127
128        let texture = self
129            .context
130            .textures()
131            .active_texture_for_image_target(target);
132        let limits = self.context.limits();
133
134        let max_size = if target.is_cubic() {
135            limits.max_cube_map_tex_size
136        } else {
137            limits.max_tex_size
138        };
139
140        //  If an attempt is made to call this function with no WebGLTexture
141        //  bound, an INVALID_OPERATION error is generated.
142        let texture = match texture {
143            Some(texture) => texture,
144            None => {
145                self.context.webgl_error(InvalidOperation);
146                return Err(TexImageValidationError::TextureTargetNotBound(self.target));
147            },
148        };
149
150        // GL_INVALID_ENUM is generated if internal_format is not an accepted
151        // format.
152        let internal_format = match TexFormat::from_gl_constant(self.internal_format) {
153            Some(format)
154                if format.required_webgl_version() <= self.context.webgl_version() &&
155                    format.usable_as_internal() =>
156            {
157                format
158            },
159            _ => {
160                self.context.webgl_error(InvalidEnum);
161                return Err(TexImageValidationError::InvalidTextureFormat);
162            },
163        };
164
165        // GL_INVALID_VALUE is generated if target is one of the six cube map 2D
166        // image targets and the width and height parameters are not equal.
167        if target.is_cubic() && self.width != self.height {
168            self.context.webgl_error(InvalidValue);
169            return Err(TexImageValidationError::InvalidCubicTextureDimensions);
170        }
171
172        // GL_INVALID_VALUE is generated if level is less than 0.
173        if self.level < 0 {
174            self.context.webgl_error(InvalidValue);
175            return Err(TexImageValidationError::NegativeLevel);
176        }
177
178        // GL_INVALID_VALUE is generated if width or height is less than 0
179        if self.width < 0 || self.height < 0 {
180            self.context.webgl_error(InvalidValue);
181            return Err(TexImageValidationError::NegativeDimension);
182        }
183
184        let width = self.width as u32;
185        let height = self.height as u32;
186        let level = self.level as u32;
187
188        // GL_INVALID_VALUE is generated if width or height is greater than
189        // GL_MAX_TEXTURE_SIZE when target is GL_TEXTURE_2D or
190        // GL_MAX_CUBE_MAP_TEXTURE_SIZE when target is not GL_TEXTURE_2D.
191        if width > max_size >> level || height > max_size >> level {
192            self.context.webgl_error(InvalidValue);
193            return Err(TexImageValidationError::TextureTooBig);
194        }
195
196        // GL_INVALID_VALUE is generated if level is greater than zero and the
197        // texture is not power of two.
198        if level > 0 && (!width.is_power_of_two() || !height.is_power_of_two()) {
199            self.context.webgl_error(InvalidValue);
200            return Err(TexImageValidationError::NonPotTexture);
201        }
202
203        // GL_INVALID_VALUE may be generated if level is greater than
204        // log_2(max), where max is the returned value of GL_MAX_TEXTURE_SIZE
205        // when target is GL_TEXTURE_2D or GL_MAX_CUBE_MAP_TEXTURE_SIZE when
206        // target is not GL_TEXTURE_2D.
207        if level > log2(max_size) {
208            self.context.webgl_error(InvalidValue);
209            return Err(TexImageValidationError::LevelTooHigh);
210        }
211
212        // GL_INVALID_VALUE is generated if border is not 0.
213        if self.border != 0 {
214            self.context.webgl_error(InvalidValue);
215            return Err(TexImageValidationError::InvalidBorder);
216        }
217
218        Ok(CommonTexImage2DValidatorResult {
219            texture,
220            target,
221            level,
222            internal_format,
223            width,
224            height,
225            border: self.border as u32,
226        })
227    }
228}
229
230impl<'a> CommonTexImage2DValidator<'a> {
231    pub(crate) fn new(
232        context: &'a WebGLRenderingContext,
233        target: u32,
234        level: i32,
235        internal_format: u32,
236        width: i32,
237        height: i32,
238        border: i32,
239    ) -> Self {
240        CommonTexImage2DValidator {
241            context,
242            target,
243            level,
244            internal_format,
245            width,
246            height,
247            border,
248        }
249    }
250}
251
252pub(crate) struct TexImage2DValidator<'a> {
253    common_validator: CommonTexImage2DValidator<'a>,
254    format: u32,
255    data_type: u32,
256}
257
258impl<'a> TexImage2DValidator<'a> {
259    /// TODO: Move data validation logic here.
260    #[allow(clippy::too_many_arguments)]
261    pub(crate) fn new(
262        context: &'a WebGLRenderingContext,
263        target: u32,
264        level: i32,
265        internal_format: u32,
266        width: i32,
267        height: i32,
268        border: i32,
269        format: u32,
270        data_type: u32,
271    ) -> Self {
272        TexImage2DValidator {
273            common_validator: CommonTexImage2DValidator::new(
274                context,
275                target,
276                level,
277                internal_format,
278                width,
279                height,
280                border,
281            ),
282            format,
283            data_type,
284        }
285    }
286}
287
288/// The validated result of a TexImage2DValidator-validated call.
289pub(crate) struct TexImage2DValidatorResult {
290    /// NB: width, height and level are already unsigned after validation.
291    pub(crate) width: u32,
292    pub(crate) height: u32,
293    pub(crate) level: u32,
294    pub(crate) border: u32,
295    pub(crate) texture: DomRoot<WebGLTexture>,
296    pub(crate) target: TexImageTarget,
297    pub(crate) internal_format: TexFormat,
298    pub(crate) format: TexFormat,
299    pub(crate) data_type: TexDataType,
300}
301
302/// TexImage2d validator as per
303/// <https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml>
304impl WebGLValidator for TexImage2DValidator<'_> {
305    type ValidatedOutput = TexImage2DValidatorResult;
306    type Error = TexImageValidationError;
307
308    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
309        let context = self.common_validator.context;
310        let CommonTexImage2DValidatorResult {
311            texture,
312            target,
313            level,
314            internal_format,
315            width,
316            height,
317            border,
318        } = self.common_validator.validate()?;
319
320        // GL_INVALID_ENUM is generated if format or data_type is not an
321        // accepted value.
322        let data_type = match TexDataType::from_gl_constant(self.data_type) {
323            Some(data_type) if data_type.required_webgl_version() <= context.webgl_version() => {
324                data_type
325            },
326            _ => {
327                context.webgl_error(InvalidEnum);
328                return Err(TexImageValidationError::InvalidDataType);
329            },
330        };
331
332        let format = match TexFormat::from_gl_constant(self.format) {
333            Some(format) if format.required_webgl_version() <= context.webgl_version() => format,
334            _ => {
335                context.webgl_error(InvalidEnum);
336                return Err(TexImageValidationError::InvalidTextureFormat);
337            },
338        };
339
340        // GL_INVALID_OPERATION is generated if format does not match
341        // internal_format.
342        if format != internal_format.to_unsized() {
343            context.webgl_error(InvalidOperation);
344            return Err(TexImageValidationError::TextureFormatMismatch);
345        }
346
347        // NOTE: In WebGL2 data type check should be done based on the internal
348        // format, but in some functions this validator is called with the
349        // regular unsized format as parameter (eg. TexSubImage2D). For now
350        // it's left here to avoid duplication.
351        //
352        // GL_INVALID_OPERATION is generated if type is
353        // GL_UNSIGNED_SHORT_4_4_4_4 or GL_UNSIGNED_SHORT_5_5_5_1 and format is
354        // not GL_RGBA.
355        //
356        // GL_INVALID_OPERATION is generated if type is GL_UNSIGNED_SHORT_5_6_5
357        // and format is not GL_RGB.
358        match data_type {
359            TexDataType::UnsignedShort4444 | TexDataType::UnsignedShort5551
360                if format != TexFormat::RGBA =>
361            {
362                context.webgl_error(InvalidOperation);
363                return Err(TexImageValidationError::InvalidTypeForFormat);
364            },
365            TexDataType::UnsignedShort565 if format != TexFormat::RGB => {
366                context.webgl_error(InvalidOperation);
367                return Err(TexImageValidationError::InvalidTypeForFormat);
368            },
369            _ => {},
370        }
371
372        Ok(TexImage2DValidatorResult {
373            width,
374            height,
375            level,
376            border,
377            texture,
378            target,
379            internal_format,
380            format,
381            data_type,
382        })
383    }
384}
385
386pub(crate) struct CommonCompressedTexImage2DValidator<'a> {
387    common_validator: CommonTexImage2DValidator<'a>,
388    data_len: usize,
389}
390
391impl<'a> CommonCompressedTexImage2DValidator<'a> {
392    #[allow(clippy::too_many_arguments)]
393    pub(crate) fn new(
394        context: &'a WebGLRenderingContext,
395        target: u32,
396        level: i32,
397        width: i32,
398        height: i32,
399        border: i32,
400        compression_format: u32,
401        data_len: usize,
402    ) -> Self {
403        CommonCompressedTexImage2DValidator {
404            common_validator: CommonTexImage2DValidator::new(
405                context,
406                target,
407                level,
408                compression_format,
409                width,
410                height,
411                border,
412            ),
413            data_len,
414        }
415    }
416}
417
418pub(crate) struct CommonCompressedTexImage2DValidatorResult {
419    pub(crate) texture: DomRoot<WebGLTexture>,
420    pub(crate) target: TexImageTarget,
421    pub(crate) level: u32,
422    pub(crate) width: u32,
423    pub(crate) height: u32,
424    pub(crate) compression: TexCompression,
425}
426
427fn valid_s3tc_dimension(level: u32, side_length: u32, block_size: u32) -> bool {
428    (side_length % block_size == 0) || (level > 0 && [0, 1, 2].contains(&side_length))
429}
430
431fn valid_compressed_data_len(
432    data_len: usize,
433    width: u32,
434    height: u32,
435    compression: &TexCompression,
436) -> bool {
437    let block_width = compression.block_width as u32;
438    let block_height = compression.block_height as u32;
439
440    let required_blocks_hor = width.div_ceil(block_width);
441    let required_blocks_ver = height.div_ceil(block_height);
442    let required_blocks = required_blocks_hor * required_blocks_ver;
443
444    let required_bytes = required_blocks * compression.bytes_per_block as u32;
445    data_len == required_bytes as usize
446}
447
448fn is_subimage_blockaligned(
449    xoffset: u32,
450    yoffset: u32,
451    width: u32,
452    height: u32,
453    compression: &TexCompression,
454    tex_info: &ImageInfo,
455) -> bool {
456    let block_width = compression.block_width as u32;
457    let block_height = compression.block_height as u32;
458
459    (xoffset % block_width == 0 && yoffset % block_height == 0) &&
460        (width % block_width == 0 || xoffset + width == tex_info.width()) &&
461        (height % block_height == 0 || yoffset + height == tex_info.height())
462}
463
464impl WebGLValidator for CommonCompressedTexImage2DValidator<'_> {
465    type Error = TexImageValidationError;
466    type ValidatedOutput = CommonCompressedTexImage2DValidatorResult;
467
468    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
469        let context = self.common_validator.context;
470        let CommonTexImage2DValidatorResult {
471            texture,
472            target,
473            level,
474            internal_format,
475            width,
476            height,
477            border: _,
478        } = self.common_validator.validate()?;
479
480        // GL_INVALID_ENUM is generated if internalformat is not a supported
481        // format returned in GL_COMPRESSED_TEXTURE_FORMATS.
482        let compression = context
483            .extension_manager()
484            .get_tex_compression_format(internal_format.as_gl_constant());
485        let compression = match compression {
486            Some(compression) => compression,
487            None => {
488                context.webgl_error(InvalidEnum);
489                return Err(TexImageValidationError::InvalidCompressionFormat);
490            },
491        };
492
493        // GL_INVALID_VALUE is generated if imageSize is not consistent with the
494        // format, dimensions, and contents of the specified compressed image data.
495        if !valid_compressed_data_len(self.data_len, width, height, &compression) {
496            context.webgl_error(InvalidValue);
497            return Err(TexImageValidationError::TextureFormatMismatch);
498        }
499
500        Ok(CommonCompressedTexImage2DValidatorResult {
501            texture,
502            target,
503            level,
504            width,
505            height,
506            compression,
507        })
508    }
509}
510
511pub(crate) struct CompressedTexImage2DValidator<'a> {
512    compression_validator: CommonCompressedTexImage2DValidator<'a>,
513}
514
515impl<'a> CompressedTexImage2DValidator<'a> {
516    #[allow(clippy::too_many_arguments)]
517    pub(crate) fn new(
518        context: &'a WebGLRenderingContext,
519        target: u32,
520        level: i32,
521        width: i32,
522        height: i32,
523        border: i32,
524        compression_format: u32,
525        data_len: usize,
526    ) -> Self {
527        CompressedTexImage2DValidator {
528            compression_validator: CommonCompressedTexImage2DValidator::new(
529                context,
530                target,
531                level,
532                width,
533                height,
534                border,
535                compression_format,
536                data_len,
537            ),
538        }
539    }
540}
541
542impl WebGLValidator for CompressedTexImage2DValidator<'_> {
543    type Error = TexImageValidationError;
544    type ValidatedOutput = CommonCompressedTexImage2DValidatorResult;
545
546    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
547        let context = self.compression_validator.common_validator.context;
548        let CommonCompressedTexImage2DValidatorResult {
549            texture,
550            target,
551            level,
552            width,
553            height,
554            compression,
555        } = self.compression_validator.validate()?;
556
557        // GL_INVALID_OPERATION is generated if parameter combinations are not
558        // supported by the specific compressed internal format as specified
559        // in the specific texture compression extension.
560        let compression_valid = match compression.validation {
561            TexCompressionValidation::S3TC => {
562                let valid_width =
563                    valid_s3tc_dimension(level, width, compression.block_width as u32);
564                let valid_height =
565                    valid_s3tc_dimension(level, height, compression.block_height as u32);
566                valid_width && valid_height
567            },
568            TexCompressionValidation::None => true,
569        };
570        if !compression_valid {
571            context.webgl_error(InvalidOperation);
572            return Err(TexImageValidationError::TextureFormatMismatch);
573        }
574
575        Ok(CommonCompressedTexImage2DValidatorResult {
576            texture,
577            target,
578            level,
579            width,
580            height,
581            compression,
582        })
583    }
584}
585
586pub(crate) struct CompressedTexSubImage2DValidator<'a> {
587    compression_validator: CommonCompressedTexImage2DValidator<'a>,
588    xoffset: i32,
589    yoffset: i32,
590}
591
592impl<'a> CompressedTexSubImage2DValidator<'a> {
593    #[allow(clippy::too_many_arguments)]
594    pub(crate) fn new(
595        context: &'a WebGLRenderingContext,
596        target: u32,
597        level: i32,
598        xoffset: i32,
599        yoffset: i32,
600        width: i32,
601        height: i32,
602        compression_format: u32,
603        data_len: usize,
604    ) -> Self {
605        CompressedTexSubImage2DValidator {
606            compression_validator: CommonCompressedTexImage2DValidator::new(
607                context,
608                target,
609                level,
610                width,
611                height,
612                0,
613                compression_format,
614                data_len,
615            ),
616            xoffset,
617            yoffset,
618        }
619    }
620}
621
622impl WebGLValidator for CompressedTexSubImage2DValidator<'_> {
623    type Error = TexImageValidationError;
624    type ValidatedOutput = CommonCompressedTexImage2DValidatorResult;
625
626    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
627        let context = self.compression_validator.common_validator.context;
628        let CommonCompressedTexImage2DValidatorResult {
629            texture,
630            target,
631            level,
632            width,
633            height,
634            compression,
635        } = self.compression_validator.validate()?;
636
637        let tex_info = texture.image_info_for_target(&target, level).unwrap();
638
639        // GL_INVALID_VALUE is generated if:
640        //   - xoffset or yoffset is less than 0
641        //   - x offset plus the width is greater than the texture width
642        //   - y offset plus the height is greater than the texture height
643        if self.xoffset < 0 ||
644            (self.xoffset as u32 + width) > tex_info.width() ||
645            self.yoffset < 0 ||
646            (self.yoffset as u32 + height) > tex_info.height()
647        {
648            context.webgl_error(InvalidValue);
649            return Err(TexImageValidationError::InvalidOffsets);
650        }
651
652        // GL_INVALID_OPERATION is generated if format does not match
653        // internal_format.
654        if compression.format != tex_info.internal_format() {
655            context.webgl_error(InvalidOperation);
656            return Err(TexImageValidationError::TextureFormatMismatch);
657        }
658
659        // GL_INVALID_OPERATION is generated if parameter combinations are not
660        // supported by the specific compressed internal format as specified
661        // in the specific texture compression extension.
662        let compression_valid = match compression.validation {
663            TexCompressionValidation::S3TC => is_subimage_blockaligned(
664                self.xoffset as u32,
665                self.yoffset as u32,
666                width,
667                height,
668                &compression,
669                &tex_info,
670            ),
671            TexCompressionValidation::None => true,
672        };
673        if !compression_valid {
674            context.webgl_error(InvalidOperation);
675            return Err(TexImageValidationError::TextureFormatMismatch);
676        }
677
678        Ok(CommonCompressedTexImage2DValidatorResult {
679            texture,
680            target,
681            level,
682            width,
683            height,
684            compression,
685        })
686    }
687}
688
689pub(crate) struct TexStorageValidator<'a> {
690    common_validator: CommonTexImage2DValidator<'a>,
691    dimensions: u8,
692    depth: i32,
693}
694
695pub(crate) struct TexStorageValidatorResult {
696    pub(crate) texture: DomRoot<WebGLTexture>,
697    pub(crate) target: TexImageTarget,
698    pub(crate) levels: u32,
699    pub(crate) internal_format: TexFormat,
700    pub(crate) width: u32,
701    pub(crate) height: u32,
702    pub(crate) depth: u32,
703}
704
705impl<'a> TexStorageValidator<'a> {
706    #[allow(clippy::too_many_arguments)]
707    pub(crate) fn new(
708        context: &'a WebGLRenderingContext,
709        dimensions: u8,
710        target: u32,
711        levels: i32,
712        internal_format: u32,
713        width: i32,
714        height: i32,
715        depth: i32,
716    ) -> Self {
717        TexStorageValidator {
718            common_validator: CommonTexImage2DValidator::new(
719                context,
720                target,
721                levels,
722                internal_format,
723                width,
724                height,
725                0,
726            ),
727            dimensions,
728            depth,
729        }
730    }
731}
732
733impl WebGLValidator for TexStorageValidator<'_> {
734    type Error = TexImageValidationError;
735    type ValidatedOutput = TexStorageValidatorResult;
736
737    fn validate(self) -> Result<Self::ValidatedOutput, TexImageValidationError> {
738        let context = self.common_validator.context;
739        let CommonTexImage2DValidatorResult {
740            texture,
741            target,
742            level,
743            internal_format,
744            width,
745            height,
746            border: _,
747        } = self.common_validator.validate()?;
748
749        if self.depth < 1 {
750            context.webgl_error(InvalidValue);
751            return Err(TexImageValidationError::DepthTooLow);
752        }
753        if level < 1 {
754            context.webgl_error(InvalidValue);
755            return Err(TexImageValidationError::LevelTooLow);
756        }
757
758        let dimensions_valid = match target {
759            TexImageTarget::Texture2D | TexImageTarget::CubeMap => self.dimensions == 2,
760            TexImageTarget::Texture3D | TexImageTarget::Texture2DArray => self.dimensions == 3,
761            _ => false,
762        };
763        if !dimensions_valid {
764            context.webgl_error(InvalidEnum);
765            return Err(TexImageValidationError::InvalidTextureTarget(
766                target.as_gl_constant(),
767            ));
768        }
769
770        if !internal_format.is_sized() {
771            context.webgl_error(InvalidEnum);
772            return Err(TexImageValidationError::InvalidTextureFormat);
773        }
774
775        let max_level = log2(cmp::max(width, height)) + 1;
776        if level > max_level {
777            context.webgl_error(InvalidOperation);
778            return Err(TexImageValidationError::LevelTooHigh);
779        }
780
781        if texture.target().is_none() {
782            context.webgl_error(InvalidOperation);
783            return Err(TexImageValidationError::TextureTargetNotBound(
784                target.as_gl_constant(),
785            ));
786        }
787
788        Ok(TexStorageValidatorResult {
789            texture,
790            target,
791            levels: level,
792            internal_format,
793            width,
794            height,
795            depth: self.depth as u32,
796        })
797    }
798}