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