fyrox_graphics/gl/
texture.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::{
22    core::color::Color,
23    error::FrameworkError,
24    gl::{server::GlGraphicsServer, ToGlConstant},
25    gpu_texture::{
26        image_1d_size_bytes, image_2d_size_bytes, image_3d_size_bytes, Coordinate, CubeMapFace,
27        GpuTexture, GpuTextureDescriptor, GpuTextureKind, MagnificationFilter, MinificationFilter,
28        PixelKind, WrapMode,
29    },
30};
31use glow::{HasContext, PixelPackData, PixelUnpackData, COMPRESSED_RED_RGTC1, COMPRESSED_RG_RGTC2};
32use std::{
33    marker::PhantomData,
34    rc::{Rc, Weak},
35};
36
37impl GpuTextureKind {
38    pub fn gl_texture_target(&self) -> u32 {
39        match self {
40            Self::Line { .. } => glow::TEXTURE_1D,
41            Self::Rectangle { .. } => glow::TEXTURE_2D,
42            Self::Cube { .. } => glow::TEXTURE_CUBE_MAP,
43            Self::Volume { .. } => glow::TEXTURE_3D,
44        }
45    }
46}
47
48impl ToGlConstant for MinificationFilter {
49    fn into_gl(self) -> u32 {
50        match self {
51            Self::Nearest => glow::NEAREST,
52            Self::NearestMipMapNearest => glow::NEAREST_MIPMAP_NEAREST,
53            Self::NearestMipMapLinear => glow::NEAREST_MIPMAP_LINEAR,
54            Self::Linear => glow::LINEAR,
55            Self::LinearMipMapNearest => glow::LINEAR_MIPMAP_NEAREST,
56            Self::LinearMipMapLinear => glow::LINEAR_MIPMAP_LINEAR,
57        }
58    }
59}
60
61impl ToGlConstant for MagnificationFilter {
62    fn into_gl(self) -> u32 {
63        match self {
64            Self::Nearest => glow::NEAREST,
65            Self::Linear => glow::LINEAR,
66        }
67    }
68}
69
70impl ToGlConstant for WrapMode {
71    fn into_gl(self) -> u32 {
72        match self {
73            Self::Repeat => glow::REPEAT,
74            Self::ClampToEdge => glow::CLAMP_TO_EDGE,
75            Self::ClampToBorder => glow::CLAMP_TO_BORDER,
76            Self::MirroredRepeat => glow::MIRRORED_REPEAT,
77            Self::MirrorClampToEdge => glow::MIRROR_CLAMP_TO_EDGE,
78        }
79    }
80}
81
82impl ToGlConstant for Coordinate {
83    fn into_gl(self) -> u32 {
84        match self {
85            Self::S => glow::TEXTURE_WRAP_S,
86            Self::T => glow::TEXTURE_WRAP_T,
87            Self::R => glow::TEXTURE_WRAP_R,
88        }
89    }
90}
91
92impl ToGlConstant for CubeMapFace {
93    fn into_gl(self) -> u32 {
94        match self {
95            Self::PositiveX => glow::TEXTURE_CUBE_MAP_POSITIVE_X,
96            Self::NegativeX => glow::TEXTURE_CUBE_MAP_NEGATIVE_X,
97            Self::PositiveY => glow::TEXTURE_CUBE_MAP_POSITIVE_Y,
98            Self::NegativeY => glow::TEXTURE_CUBE_MAP_NEGATIVE_Y,
99            Self::PositiveZ => glow::TEXTURE_CUBE_MAP_POSITIVE_Z,
100            Self::NegativeZ => glow::TEXTURE_CUBE_MAP_NEGATIVE_Z,
101        }
102    }
103}
104
105pub struct GlTexture {
106    state: Weak<GlGraphicsServer>,
107    texture: glow::Texture,
108    kind: GpuTextureKind,
109    min_filter: MinificationFilter,
110    mag_filter: MagnificationFilter,
111    s_wrap_mode: WrapMode,
112    t_wrap_mode: WrapMode,
113    r_wrap_mode: WrapMode,
114    anisotropy: f32,
115    pixel_kind: PixelKind,
116    base_level: usize,
117    max_level: usize,
118    min_lod: f32,
119    max_lod: f32,
120    lod_bias: f32,
121    // Force compiler to not implement Send and Sync, because OpenGL is not thread-safe.
122    thread_mark: PhantomData<*const u8>,
123}
124
125const GL_COMPRESSED_RGB_S3TC_DXT1_EXT: u32 = 0x83F0;
126const GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: u32 = 0x83F1;
127const GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: u32 = 0x83F2;
128const GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: u32 = 0x83F3;
129
130pub struct PixelDescriptor {
131    pub data_type: u32,
132    pub format: u32,
133    pub internal_format: u32,
134    pub swizzle_mask: Option<[i32; 4]>,
135}
136
137impl PixelKind {
138    pub(crate) fn pixel_descriptor(self) -> PixelDescriptor {
139        let (data_type, format, internal_format, swizzle_mask) = match self {
140            PixelKind::R32F => (glow::FLOAT, glow::RED, glow::R32F, None),
141            PixelKind::R32UI => (glow::UNSIGNED_INT, glow::RED_INTEGER, glow::R32UI, None),
142            PixelKind::R16F => (glow::FLOAT, glow::RED, glow::R16F, None),
143            PixelKind::D32F => (
144                glow::FLOAT,
145                glow::DEPTH_COMPONENT,
146                glow::DEPTH_COMPONENT32F,
147                None,
148            ),
149            PixelKind::D16 => (
150                glow::UNSIGNED_SHORT,
151                glow::DEPTH_COMPONENT,
152                glow::DEPTH_COMPONENT16,
153                None,
154            ),
155            PixelKind::D24S8 => (
156                glow::UNSIGNED_INT_24_8,
157                glow::DEPTH_STENCIL,
158                glow::DEPTH24_STENCIL8,
159                None,
160            ),
161            PixelKind::RGBA8 => (glow::UNSIGNED_BYTE, glow::RGBA, glow::RGBA8, None),
162            PixelKind::SRGBA8 => (glow::UNSIGNED_BYTE, glow::RGBA, glow::SRGB8_ALPHA8, None),
163            PixelKind::RGB8 => (glow::UNSIGNED_BYTE, glow::RGB, glow::RGB8, None),
164            PixelKind::SRGB8 => (glow::UNSIGNED_BYTE, glow::RGB, glow::SRGB8, None),
165            PixelKind::RG8 => (glow::UNSIGNED_BYTE, glow::RG, glow::RG8, None),
166            PixelKind::R8 => (glow::UNSIGNED_BYTE, glow::RED, glow::R8, None),
167            PixelKind::R8UI => (glow::UNSIGNED_BYTE, glow::RED_INTEGER, glow::R8UI, None),
168            PixelKind::BGRA8 => (glow::UNSIGNED_BYTE, glow::BGRA, glow::RGBA8, None),
169            PixelKind::BGR8 => (glow::UNSIGNED_BYTE, glow::BGR, glow::RGB8, None),
170            PixelKind::RG16 => (glow::UNSIGNED_SHORT, glow::RG, glow::RG16, None),
171            PixelKind::R16 => (glow::UNSIGNED_SHORT, glow::RED, glow::R16, None),
172            PixelKind::RGB16 => (glow::UNSIGNED_SHORT, glow::RGB, glow::RGB16, None),
173            PixelKind::RGBA16 => (glow::UNSIGNED_SHORT, glow::RGBA, glow::RGBA16, None),
174            PixelKind::RGB10A2 => (
175                glow::UNSIGNED_INT_2_10_10_10_REV,
176                glow::RGBA,
177                glow::RGB10_A2,
178                None,
179            ),
180            PixelKind::DXT1RGB => (0, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, None),
181            PixelKind::DXT1RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, None),
182            PixelKind::DXT3RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, None),
183            PixelKind::DXT5RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, None),
184            PixelKind::R8RGTC => (0, 0, COMPRESSED_RED_RGTC1, None),
185            PixelKind::RG8RGTC => (0, 0, COMPRESSED_RG_RGTC2, None),
186            PixelKind::RGB32F => (glow::FLOAT, glow::RGB, glow::RGB32F, None),
187            PixelKind::RGBA32F => (glow::FLOAT, glow::RGBA, glow::RGBA32F, None),
188            PixelKind::RGBA16F => (glow::HALF_FLOAT, glow::RGBA, glow::RGBA16F, None),
189            PixelKind::RGB16F => (glow::HALF_FLOAT, glow::RGB, glow::RGB16F, None),
190            PixelKind::R11G11B10F => (glow::FLOAT, glow::RGB, glow::R11F_G11F_B10F, None),
191            PixelKind::L8 => (
192                glow::UNSIGNED_BYTE,
193                glow::RED,
194                glow::R8,
195                Some([
196                    glow::RED as i32,
197                    glow::RED as i32,
198                    glow::RED as i32,
199                    glow::ONE as i32,
200                ]),
201            ),
202            PixelKind::LA8 => (
203                glow::UNSIGNED_BYTE,
204                glow::RG,
205                glow::RG8,
206                Some([
207                    glow::RED as i32,
208                    glow::RED as i32,
209                    glow::RED as i32,
210                    glow::GREEN as i32,
211                ]),
212            ),
213            PixelKind::LA16 => (
214                glow::UNSIGNED_SHORT,
215                glow::RG,
216                glow::RG16,
217                Some([
218                    glow::RED as i32,
219                    glow::RED as i32,
220                    glow::RED as i32,
221                    glow::GREEN as i32,
222                ]),
223            ),
224            PixelKind::L16 => (
225                glow::UNSIGNED_SHORT,
226                glow::RED,
227                glow::R16,
228                Some([
229                    glow::RED as i32,
230                    glow::RED as i32,
231                    glow::RED as i32,
232                    glow::ONE as i32,
233                ]),
234            ),
235        };
236
237        PixelDescriptor {
238            data_type,
239            format,
240            internal_format,
241            swizzle_mask,
242        }
243    }
244}
245
246struct TempBinding {
247    server: Rc<GlGraphicsServer>,
248    unit: u32,
249    target: u32,
250}
251
252impl TempBinding {
253    fn new(server: Rc<GlGraphicsServer>, texture: &GlTexture) -> Self {
254        let unit = server
255            .free_texture_unit()
256            .expect("Texture units limit exceeded!");
257        let target = texture.kind.gl_texture_target();
258        server.set_texture(unit, target, Some(texture.texture));
259        Self {
260            server,
261            unit,
262            target,
263        }
264    }
265
266    fn set_minification_filter(&mut self, min_filter: MinificationFilter) {
267        unsafe {
268            self.server.gl.tex_parameter_i32(
269                self.target,
270                glow::TEXTURE_MIN_FILTER,
271                min_filter.into_gl() as i32,
272            );
273        }
274    }
275
276    fn set_magnification_filter(&mut self, mag_filter: MagnificationFilter) {
277        unsafe {
278            self.server.gl.tex_parameter_i32(
279                self.target,
280                glow::TEXTURE_MAG_FILTER,
281                mag_filter.into_gl() as i32,
282            );
283        }
284    }
285
286    fn set_anisotropy(&mut self, anisotropy: f32) {
287        unsafe {
288            let max = self
289                .server
290                .gl
291                .get_parameter_f32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT);
292            self.server.gl.tex_parameter_f32(
293                self.target,
294                glow::TEXTURE_MAX_ANISOTROPY_EXT,
295                anisotropy.clamp(1.0, max),
296            );
297        }
298    }
299
300    fn set_wrap(&mut self, coordinate: Coordinate, wrap: WrapMode) {
301        unsafe {
302            self.server.gl.tex_parameter_i32(
303                self.target,
304                coordinate.into_gl(),
305                wrap.into_gl() as i32,
306            );
307        }
308    }
309
310    fn set_base_level(&mut self, level: usize) {
311        unsafe {
312            self.server
313                .gl
314                .tex_parameter_i32(self.target, glow::TEXTURE_BASE_LEVEL, level as i32);
315        }
316    }
317
318    fn set_max_level(&mut self, level: usize) {
319        unsafe {
320            self.server
321                .gl
322                .tex_parameter_i32(self.target, glow::TEXTURE_MAX_LEVEL, level as i32);
323        }
324    }
325
326    fn set_min_lod(&mut self, min_lod: f32) {
327        unsafe {
328            self.server
329                .gl
330                .tex_parameter_f32(self.target, glow::TEXTURE_MIN_LOD, min_lod);
331        }
332    }
333
334    fn set_max_lod(&mut self, max_lod: f32) {
335        unsafe {
336            self.server
337                .gl
338                .tex_parameter_f32(self.target, glow::TEXTURE_MAX_LOD, max_lod);
339        }
340    }
341
342    fn set_lod_bias(&mut self, bias: f32) {
343        unsafe {
344            self.server
345                .gl
346                .tex_parameter_f32(self.target, glow::TEXTURE_LOD_BIAS, bias);
347        }
348    }
349}
350
351impl Drop for TempBinding {
352    fn drop(&mut self) {
353        self.server.set_texture(self.unit, self.target, None);
354    }
355}
356
357impl GlTexture {
358    /// Creates new GPU texture of specified kind. Mip count must be at least 1, it means
359    /// that there is only main level of detail.
360    ///
361    /// # Data layout
362    ///
363    /// In case of Cube texture, `bytes` should contain all 6 cube faces ordered like so,
364    /// +X, -X, +Y, -Y, +Z, -Z. Cube mips must follow one after another.
365    ///
366    /// Produced texture can be used as render target for framebuffer, in this case `data`
367    /// parameter can be None.
368    ///
369    /// # Compressed textures
370    ///
371    /// For compressed textures data must contain all mips, where each mip must be 2 times
372    /// smaller than previous.
373    pub fn new(
374        server: &GlGraphicsServer,
375        mut desc: GpuTextureDescriptor,
376    ) -> Result<Self, FrameworkError> {
377        // Clamp the mip level values to sensible range to prevent weird behavior.
378        let actual_max_level = desc.mip_count.saturating_sub(1);
379        if desc.max_level > actual_max_level {
380            desc.max_level = actual_max_level;
381        }
382        if desc.base_level > desc.max_level {
383            desc.base_level = desc.max_level;
384        }
385        if desc.base_level > actual_max_level {
386            desc.base_level = actual_max_level;
387        }
388
389        unsafe {
390            let texture = server.gl.create_texture()?;
391
392            let mut result = Self {
393                state: server.weak(),
394                texture,
395                kind: desc.kind,
396                min_filter: desc.min_filter,
397                mag_filter: desc.mag_filter,
398                s_wrap_mode: desc.s_wrap_mode,
399                t_wrap_mode: desc.t_wrap_mode,
400                r_wrap_mode: desc.r_wrap_mode,
401                anisotropy: desc.anisotropy,
402                pixel_kind: desc.pixel_kind,
403                base_level: desc.base_level,
404                max_level: desc.max_level,
405                min_lod: desc.min_lod,
406                max_lod: desc.max_lod,
407                lod_bias: desc.lod_bias,
408                thread_mark: PhantomData,
409            };
410
411            result.set_data(desc.kind, desc.pixel_kind, desc.mip_count, desc.data)?;
412
413            let mut binding = result.make_temp_binding();
414            binding.set_magnification_filter(desc.mag_filter);
415            binding.set_minification_filter(desc.min_filter);
416            binding.set_wrap(Coordinate::S, desc.s_wrap_mode);
417            binding.set_wrap(Coordinate::T, desc.t_wrap_mode);
418            binding.set_wrap(Coordinate::R, desc.r_wrap_mode);
419            binding.set_anisotropy(desc.anisotropy);
420            binding.set_base_level(desc.base_level);
421            binding.set_max_level(desc.max_level);
422            binding.set_min_lod(desc.min_lod);
423            binding.set_max_lod(desc.max_lod);
424            binding.set_lod_bias(desc.lod_bias);
425
426            Ok(result)
427        }
428    }
429
430    pub fn bind(&self, server: &GlGraphicsServer, sampler_index: u32) {
431        server.set_texture(
432            sampler_index,
433            self.kind.gl_texture_target(),
434            Some(self.texture),
435        );
436    }
437
438    fn make_temp_binding(&self) -> TempBinding {
439        let server = self.state.upgrade().unwrap();
440        TempBinding::new(server, self)
441    }
442
443    pub fn id(&self) -> glow::Texture {
444        self.texture
445    }
446}
447
448impl Drop for GlTexture {
449    fn drop(&mut self) {
450        if let Some(state) = self.state.upgrade() {
451            unsafe {
452                state.gl.delete_texture(self.texture);
453            }
454        }
455    }
456}
457
458impl GpuTexture for GlTexture {
459    fn set_anisotropy(&mut self, anisotropy: f32) {
460        self.make_temp_binding().set_anisotropy(anisotropy);
461        self.anisotropy = anisotropy;
462    }
463
464    fn anisotropy(&self) -> f32 {
465        self.anisotropy
466    }
467
468    fn set_minification_filter(&mut self, filter: MinificationFilter) {
469        self.make_temp_binding().set_minification_filter(filter);
470        self.min_filter = filter;
471    }
472
473    fn minification_filter(&self) -> MinificationFilter {
474        self.min_filter
475    }
476
477    fn set_magnification_filter(&mut self, filter: MagnificationFilter) {
478        self.make_temp_binding().set_magnification_filter(filter);
479        self.mag_filter = filter;
480    }
481
482    fn magnification_filter(&self) -> MagnificationFilter {
483        self.mag_filter
484    }
485
486    fn set_wrap(&mut self, coordinate: Coordinate, wrap: WrapMode) {
487        self.make_temp_binding().set_wrap(coordinate, wrap);
488        match coordinate {
489            Coordinate::S => self.s_wrap_mode = wrap,
490            Coordinate::T => self.t_wrap_mode = wrap,
491            Coordinate::R => self.r_wrap_mode = wrap,
492        }
493    }
494
495    fn wrap_mode(&self, coordinate: Coordinate) -> WrapMode {
496        match coordinate {
497            Coordinate::S => self.s_wrap_mode,
498            Coordinate::T => self.t_wrap_mode,
499            Coordinate::R => self.r_wrap_mode,
500        }
501    }
502
503    fn set_border_color(&mut self, #[allow(unused_variables)] color: Color) {
504        #[cfg(not(target_arch = "wasm32"))]
505        unsafe {
506            let temp_binding = self.make_temp_binding();
507            let color = color.as_frgba();
508            let color = [color.x, color.y, color.z, color.w];
509
510            temp_binding.server.gl.tex_parameter_f32_slice(
511                self.kind.gl_texture_target(),
512                glow::TEXTURE_BORDER_COLOR,
513                &color,
514            );
515        }
516    }
517
518    fn set_data(
519        &mut self,
520        kind: GpuTextureKind,
521        pixel_kind: PixelKind,
522        mip_count: usize,
523        data: Option<&[u8]>,
524    ) -> Result<(), FrameworkError> {
525        let mip_count = mip_count.max(1);
526
527        let mut desired_byte_count = 0;
528
529        'mip_loop: for mip in 0..mip_count {
530            match kind {
531                GpuTextureKind::Line { length } => {
532                    if let Some(length) = length.checked_shr(mip as u32) {
533                        desired_byte_count += image_1d_size_bytes(pixel_kind, length);
534                    } else {
535                        break 'mip_loop;
536                    }
537                }
538                GpuTextureKind::Rectangle { width, height } => {
539                    if let (Some(width), Some(height)) = (
540                        width.checked_shr(mip as u32),
541                        height.checked_shr(mip as u32),
542                    ) {
543                        desired_byte_count += image_2d_size_bytes(pixel_kind, width, height);
544                    } else {
545                        break 'mip_loop;
546                    }
547                }
548                GpuTextureKind::Cube { width, height } => {
549                    if let (Some(width), Some(height)) = (
550                        width.checked_shr(mip as u32),
551                        height.checked_shr(mip as u32),
552                    ) {
553                        desired_byte_count += 6 * image_2d_size_bytes(pixel_kind, width, height);
554                    } else {
555                        break 'mip_loop;
556                    }
557                }
558                GpuTextureKind::Volume {
559                    width,
560                    height,
561                    depth,
562                } => {
563                    if let (Some(width), Some(height), Some(depth)) = (
564                        width.checked_shr(mip as u32),
565                        height.checked_shr(mip as u32),
566                        depth.checked_shr(mip as u32),
567                    ) {
568                        desired_byte_count += image_3d_size_bytes(pixel_kind, width, height, depth);
569                    } else {
570                        break 'mip_loop;
571                    }
572                }
573            };
574        }
575
576        if let Some(data) = data {
577            let actual_data_size = data.len();
578            if actual_data_size != desired_byte_count {
579                return Err(FrameworkError::InvalidTextureData {
580                    expected_data_size: desired_byte_count,
581                    actual_data_size,
582                });
583            }
584        }
585
586        self.kind = kind;
587        self.pixel_kind = pixel_kind;
588
589        let mut temp_binding = self.make_temp_binding();
590        temp_binding.set_max_level(mip_count.saturating_sub(1));
591        let target = kind.gl_texture_target();
592
593        unsafe {
594            let PixelDescriptor {
595                data_type,
596                format,
597                internal_format,
598                swizzle_mask,
599            } = pixel_kind.pixel_descriptor();
600
601            let is_compressed = pixel_kind.is_compressed();
602
603            if let Some(alignment) = pixel_kind.unpack_alignment() {
604                temp_binding
605                    .server
606                    .gl
607                    .pixel_store_i32(glow::UNPACK_ALIGNMENT, alignment);
608            }
609
610            if let Some(swizzle_mask) = swizzle_mask {
611                if temp_binding
612                    .server
613                    .gl
614                    .supported_extensions()
615                    .contains("GL_ARB_texture_swizzle")
616                {
617                    temp_binding.server.gl.tex_parameter_i32_slice(
618                        target,
619                        glow::TEXTURE_SWIZZLE_RGBA,
620                        &swizzle_mask,
621                    );
622                }
623            }
624
625            let mut mip_byte_offset = 0;
626            'mip_loop2: for mip in 0..mip_count {
627                match kind {
628                    GpuTextureKind::Line { length } => {
629                        if let Some(length) = length.checked_shr(mip as u32) {
630                            let size = image_1d_size_bytes(pixel_kind, length) as i32;
631                            let pixels = data.map(|data| {
632                                &data[mip_byte_offset..(mip_byte_offset + size as usize)]
633                            });
634
635                            if is_compressed {
636                                temp_binding.server.gl.compressed_tex_image_1d(
637                                    glow::TEXTURE_1D,
638                                    mip as i32,
639                                    internal_format as i32,
640                                    length as i32,
641                                    0,
642                                    size,
643                                    pixels.ok_or(FrameworkError::EmptyTextureData)?,
644                                );
645                            } else {
646                                temp_binding.server.gl.tex_image_1d(
647                                    glow::TEXTURE_1D,
648                                    mip as i32,
649                                    internal_format as i32,
650                                    length as i32,
651                                    0,
652                                    format,
653                                    data_type,
654                                    PixelUnpackData::Slice(pixels),
655                                );
656                            }
657
658                            mip_byte_offset += size as usize;
659                        } else {
660                            // No need to add degenerated mips (0x1, 0x2, 4x0, etc).
661                            break 'mip_loop2;
662                        }
663                    }
664                    GpuTextureKind::Rectangle { width, height } => {
665                        if let (Some(width), Some(height)) = (
666                            width.checked_shr(mip as u32),
667                            height.checked_shr(mip as u32),
668                        ) {
669                            let size = image_2d_size_bytes(pixel_kind, width, height) as i32;
670                            let pixels = data.map(|data| {
671                                &data[mip_byte_offset..(mip_byte_offset + size as usize)]
672                            });
673
674                            if is_compressed {
675                                temp_binding.server.gl.compressed_tex_image_2d(
676                                    glow::TEXTURE_2D,
677                                    mip as i32,
678                                    internal_format as i32,
679                                    width as i32,
680                                    height as i32,
681                                    0,
682                                    size,
683                                    pixels.ok_or(FrameworkError::EmptyTextureData)?,
684                                );
685                            } else {
686                                temp_binding.server.gl.tex_image_2d(
687                                    glow::TEXTURE_2D,
688                                    mip as i32,
689                                    internal_format as i32,
690                                    width as i32,
691                                    height as i32,
692                                    0,
693                                    format,
694                                    data_type,
695                                    PixelUnpackData::Slice(pixels),
696                                );
697                            }
698
699                            mip_byte_offset += size as usize;
700                        } else {
701                            // No need to add degenerated mips (0x1, 0x2, 4x0, etc).
702                            break 'mip_loop2;
703                        }
704                    }
705                    GpuTextureKind::Cube { width, height } => {
706                        if let (Some(width), Some(height)) = (
707                            width.checked_shr(mip as u32),
708                            height.checked_shr(mip as u32),
709                        ) {
710                            let bytes_per_face = image_2d_size_bytes(pixel_kind, width, height);
711
712                            for face in 0..6 {
713                                let begin = mip_byte_offset + face * bytes_per_face;
714                                let end = mip_byte_offset + (face + 1) * bytes_per_face;
715                                let face_pixels = data.map(|data| &data[begin..end]);
716
717                                if is_compressed {
718                                    temp_binding.server.gl.compressed_tex_image_2d(
719                                        glow::TEXTURE_CUBE_MAP_POSITIVE_X + face as u32,
720                                        mip as i32,
721                                        internal_format as i32,
722                                        width as i32,
723                                        height as i32,
724                                        0,
725                                        bytes_per_face as i32,
726                                        face_pixels.ok_or(FrameworkError::EmptyTextureData)?,
727                                    );
728                                } else {
729                                    temp_binding.server.gl.tex_image_2d(
730                                        glow::TEXTURE_CUBE_MAP_POSITIVE_X + face as u32,
731                                        mip as i32,
732                                        internal_format as i32,
733                                        width as i32,
734                                        height as i32,
735                                        0,
736                                        format,
737                                        data_type,
738                                        PixelUnpackData::Slice(face_pixels),
739                                    );
740                                }
741                            }
742
743                            mip_byte_offset += 6 * bytes_per_face;
744                        } else {
745                            // No need to add degenerated mips (0x1, 0x2, 4x0, etc).
746                            break 'mip_loop2;
747                        }
748                    }
749                    GpuTextureKind::Volume {
750                        width,
751                        height,
752                        depth,
753                    } => {
754                        if let (Some(width), Some(height), Some(depth)) = (
755                            width.checked_shr(mip as u32),
756                            height.checked_shr(mip as u32),
757                            depth.checked_shr(mip as u32),
758                        ) {
759                            let size = image_3d_size_bytes(pixel_kind, width, height, depth) as i32;
760                            let pixels = data.map(|data| {
761                                &data[mip_byte_offset..(mip_byte_offset + size as usize)]
762                            });
763
764                            if is_compressed {
765                                temp_binding.server.gl.compressed_tex_image_3d(
766                                    glow::TEXTURE_3D,
767                                    mip as i32,
768                                    internal_format as i32,
769                                    width as i32,
770                                    height as i32,
771                                    depth as i32,
772                                    0,
773                                    size,
774                                    pixels.ok_or(FrameworkError::EmptyTextureData)?,
775                                );
776                            } else {
777                                temp_binding.server.gl.tex_image_3d(
778                                    glow::TEXTURE_3D,
779                                    mip as i32,
780                                    internal_format as i32,
781                                    width as i32,
782                                    height as i32,
783                                    depth as i32,
784                                    0,
785                                    format,
786                                    data_type,
787                                    PixelUnpackData::Slice(pixels),
788                                );
789                            }
790
791                            mip_byte_offset += size as usize;
792                        } else {
793                            // No need to add degenerated mips (0x1, 0x2, 4x0, etc).
794                            break 'mip_loop2;
795                        }
796                    }
797                }
798            }
799        }
800
801        Ok(())
802    }
803
804    fn get_image(&self, level: usize) -> Vec<u8> {
805        let temp_binding = self.make_temp_binding();
806        unsafe {
807            let desc = self.pixel_kind.pixel_descriptor();
808            let (kind, buffer_size) = match self.kind {
809                GpuTextureKind::Line { length } => (
810                    glow::TEXTURE_1D,
811                    image_1d_size_bytes(self.pixel_kind, length),
812                ),
813                GpuTextureKind::Rectangle { width, height } => (
814                    glow::TEXTURE_2D,
815                    image_2d_size_bytes(self.pixel_kind, width, height),
816                ),
817                GpuTextureKind::Cube { width, height } => (
818                    glow::TEXTURE_CUBE_MAP,
819                    6 * image_2d_size_bytes(self.pixel_kind, width, height),
820                ),
821                GpuTextureKind::Volume {
822                    width,
823                    height,
824                    depth,
825                } => (
826                    glow::TEXTURE_3D,
827                    image_3d_size_bytes(self.pixel_kind, width, height, depth),
828                ),
829            };
830
831            let mut bytes = vec![0; buffer_size];
832            temp_binding.server.gl.get_tex_image(
833                kind,
834                level as i32,
835                desc.format,
836                desc.data_type,
837                PixelPackData::Slice(Some(bytes.as_mut_slice())),
838            );
839            bytes
840        }
841    }
842
843    fn read_pixels(&self) -> Vec<u8> {
844        let temp_binding = self.make_temp_binding();
845        unsafe {
846            if let GpuTextureKind::Rectangle { width, height } = self.kind {
847                let pixel_info = self.pixel_kind.pixel_descriptor();
848                let mut buffer = vec![0; image_2d_size_bytes(self.pixel_kind, width, height)];
849                temp_binding.server.gl.read_pixels(
850                    0,
851                    0,
852                    width as i32,
853                    height as i32,
854                    pixel_info.format,
855                    pixel_info.data_type,
856                    PixelPackData::Slice(Some(buffer.as_mut_slice())),
857                );
858                buffer
859            } else {
860                Default::default()
861            }
862        }
863    }
864
865    fn kind(&self) -> GpuTextureKind {
866        self.kind
867    }
868
869    fn pixel_kind(&self) -> PixelKind {
870        self.pixel_kind
871    }
872
873    fn set_base_level(&mut self, level: usize) {
874        self.make_temp_binding().set_base_level(level);
875        self.base_level = level;
876    }
877
878    fn base_level(&self) -> usize {
879        self.base_level
880    }
881
882    fn set_max_level(&mut self, level: usize) {
883        self.make_temp_binding().set_max_level(level);
884        self.max_level = level;
885    }
886
887    fn max_level(&self) -> usize {
888        self.max_level
889    }
890
891    fn set_min_lod(&mut self, min_lod: f32) {
892        self.make_temp_binding().set_min_lod(min_lod);
893        self.min_lod = min_lod;
894    }
895
896    fn min_lod(&self) -> f32 {
897        self.min_lod
898    }
899
900    fn set_max_lod(&mut self, max_lod: f32) {
901        self.make_temp_binding().set_max_lod(max_lod);
902        self.max_lod = max_lod;
903    }
904
905    fn max_lod(&self) -> f32 {
906        self.max_lod
907    }
908
909    fn set_lod_bias(&mut self, bias: f32) {
910        self.make_temp_binding().set_lod_bias(bias);
911        self.lod_bias = bias;
912    }
913
914    fn lod_bias(&self) -> f32 {
915        self.lod_bias
916    }
917}