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