glium/texture/
any.rs

1
2use crate::gl;
3use crate::GlObject;
4
5use crate::backend::Facade;
6use crate::memory_object::MemoryObject;
7use crate::version::Version;
8use crate::context::Context;
9use crate::context::CommandContext;
10use crate::CapabilitiesSource;
11use crate::ContextExt;
12use crate::TextureExt;
13use crate::TextureMipmapExt;
14use crate::version::Api;
15use crate::Rect;
16
17use crate::image_format::{self, TextureFormatRequest, ClientFormatAny};
18use crate::texture::Texture2dDataSink;
19use crate::texture::TextureKind;
20use crate::texture::{MipmapsOption, TextureFormat, TextureCreationError, CubeLayer};
21use crate::texture::{get_format, InternalFormat, GetFormatError};
22use crate::texture::pixel::PixelValue;
23use crate::texture::pixel_buffer::PixelBuffer;
24
25use crate::fbo::ClearBufferData;
26
27use crate::buffer::BufferSlice;
28use crate::buffer::BufferAny;
29use crate::BufferExt;
30use crate::BufferSliceExt;
31
32use std::cmp;
33use std::fmt;
34use std::mem;
35use std::ptr;
36use std::borrow::Cow;
37use std::cell::Cell;
38use std::rc::Rc;
39use std::ops::Range;
40use std::ffi::c_void;
41
42use crate::ops;
43use crate::fbo;
44
45/// Type of a texture.
46#[derive(Copy, Clone, Debug, PartialEq, Eq)]
47#[allow(missing_docs)]      // TODO: document and remove
48pub enum Dimensions {
49    Texture1d { width: u32 },
50    Texture1dArray { width: u32, array_size: u32 },
51    Texture2d { width: u32, height: u32 },
52    Texture2dArray { width: u32, height: u32, array_size: u32 },
53    Texture2dMultisample { width: u32, height: u32, samples: u32 },
54    Texture2dMultisampleArray { width: u32, height: u32, array_size: u32, samples: u32 },
55    Texture3d { width: u32, height: u32, depth: u32 },
56    Cubemap { dimension: u32 },
57    CubemapArray { dimension: u32, array_size: u32 },
58}
59
60/// A texture whose type isn't fixed at compile-time.
61pub struct TextureAny {
62    context: Rc<Context>,
63    id: gl::types::GLuint,
64    requested_format: TextureFormatRequest,
65
66    /// Cache for the actual format of the texture. The outer Option is None if the format hasn't
67    /// been checked yet. The inner Result is Err if the format has been checked but is unknown.
68    actual_format: Cell<Option<Result<InternalFormat, GetFormatError>>>,
69
70    /// Type and dimensions of the texture.
71    ty: Dimensions,
72
73    /// Number of mipmap levels (`1` means just the main texture, `0` is not valid)
74    levels: u32,
75    /// Is automatic mipmap generation allowed for this texture?
76    generate_mipmaps: bool,
77
78    /// Is this texture owned by us? If not, we won't clean it up on drop.
79    owned: bool,
80
81    /// If this texture was created in Vulkan for example, it may be backed by external memory.
82    memory: Option<MemoryObject>,
83
84    /// ID of the draw call where the texture was last written as a texture unit.
85    latest_shader_write: Cell<u64>,
86}
87
88fn extract_dimensions(ty: Dimensions)
89                      -> (u32, Option<u32>, Option<u32>, Option<u32>, Option<u32>)
90{
91    match ty {
92        Dimensions::Texture1d { width } => (width, None, None, None, None),
93        Dimensions::Texture1dArray { width, array_size } => (width, None, None, Some(array_size), None),
94        Dimensions::Texture2d { width, height } => (width, Some(height), None, None, None),
95        Dimensions::Texture2dArray { width, height, array_size } => (width, Some(height), None, Some(array_size), None),
96        Dimensions::Texture2dMultisample { width, height, samples } => (width, Some(height), None, None, Some(samples)),
97        Dimensions::Texture2dMultisampleArray { width, height, array_size, samples } => (width, Some(height), None, Some(array_size), Some(samples)),
98        Dimensions::Texture3d { width, height, depth } => (width, Some(height), Some(depth), None, None),
99        Dimensions::Cubemap { dimension } => (dimension, Some(dimension), None, None, None),
100        Dimensions::CubemapArray { dimension, array_size } => (dimension, Some(dimension), None, Some(array_size * 6), None),
101    }
102}
103
104#[inline]
105fn get_bind_point(ty: Dimensions) -> gl::types::GLenum {
106    match ty {
107        Dimensions::Texture1d { .. } => gl::TEXTURE_1D,
108        Dimensions::Texture1dArray { .. } => gl::TEXTURE_1D_ARRAY,
109        Dimensions::Texture2d { .. } => gl::TEXTURE_2D,
110        Dimensions::Texture2dArray { .. } => gl::TEXTURE_2D_ARRAY,
111        Dimensions::Texture2dMultisample { .. } => gl::TEXTURE_2D_MULTISAMPLE,
112        Dimensions::Texture2dMultisampleArray { .. } => gl::TEXTURE_2D_MULTISAMPLE_ARRAY,
113        Dimensions::Texture3d { .. } => gl::TEXTURE_3D,
114        Dimensions::Cubemap { .. } => gl::TEXTURE_CUBE_MAP,
115        Dimensions::CubemapArray { .. } => gl::TEXTURE_CUBE_MAP_ARRAY,
116    }
117}
118
119unsafe fn generate_mipmaps(ctxt: &CommandContext<'_>,
120                           bind_point: gl::types::GLenum) {
121    if ctxt.version >= &Version(Api::Gl, 3, 0) ||
122       ctxt.version >= &Version(Api::GlEs, 2, 0)
123    {
124        ctxt.gl.GenerateMipmap(bind_point);
125    } else if ctxt.extensions.gl_ext_framebuffer_object {
126        ctxt.gl.GenerateMipmapEXT(bind_point);
127    } else {
128        unreachable!();
129    }
130}
131
132/// Builds a new texture.
133pub fn new_texture<'a, F: ?Sized, P>(facade: &F, format: TextureFormatRequest,
134                             data: Option<(ClientFormatAny, Cow<'a, [P]>)>,
135                             mipmaps: MipmapsOption, ty: Dimensions)
136                             -> Result<TextureAny, TextureCreationError>
137                             where P: Send + Clone + 'a, F: Facade
138{
139    // getting the width, height, depth, array_size, samples from the type
140    let (width, height, depth, array_size, samples) = extract_dimensions(ty);
141    let (is_client_compressed, data_bufsize) = match data {
142        Some((client_format, _)) => {
143            (client_format.is_compressed(),
144             client_format.get_buffer_size(width, height, depth, array_size))
145        },
146        None => (false, 0),
147    };
148
149    if let Some((_, ref data)) = data {
150        if data.len() * mem::size_of::<P>() != data_bufsize
151        {
152            return Err(TextureCreationError::DataSizeMismatch);
153        }
154    }
155
156    // getting the `GLenum` corresponding to this texture type
157    let bind_point = get_bind_point(ty);
158    if bind_point == gl::TEXTURE_CUBE_MAP || bind_point == gl::TEXTURE_CUBE_MAP_ARRAY {
159        assert!(data.is_none());        // TODO: not supported
160    }
161
162    // checking non-power-of-two
163    if facade.get_context().get_version() < &Version(Api::Gl, 2, 0) &&
164        !facade.get_context().get_extensions().gl_arb_texture_non_power_of_two && (!width.is_power_of_two() || !height.unwrap_or(2).is_power_of_two() ||
165            !depth.unwrap_or(2).is_power_of_two() || !array_size.unwrap_or(2).is_power_of_two()) {
166        return Err(TextureCreationError::DimensionsNotSupported);
167    }
168
169    let should_generate_mipmaps = mipmaps.should_generate();
170    let texture_levels = mipmaps.num_levels(width, height, depth) as gl::types::GLsizei;
171
172    let teximg_internal_format = image_format::format_request_to_glenum(facade.get_context(), format, image_format::RequestType::TexImage(data.as_ref().map(|&(c, _)| c)))?;
173    let storage_internal_format = image_format::format_request_to_glenum(facade.get_context(), format, image_format::RequestType::TexStorage).ok();
174
175    let (client_format, client_type) = match (&data, format) {
176        (&Some((client_format, _)), f) => image_format::client_format_to_glenum(facade.get_context(), client_format, f, false)?,
177        (&None, TextureFormatRequest::AnyDepth) => (gl::DEPTH_COMPONENT, gl::FLOAT),
178        (&None, TextureFormatRequest::Specific(TextureFormat::DepthFormat(_))) => (gl::DEPTH_COMPONENT, gl::FLOAT),
179        (&None, TextureFormatRequest::AnyDepthStencil) => (gl::DEPTH_STENCIL, gl::UNSIGNED_INT_24_8),
180        (&None, TextureFormatRequest::Specific(TextureFormat::DepthStencilFormat(_))) => (gl::DEPTH_STENCIL, gl::UNSIGNED_INT_24_8),
181        (&None, _) => (gl::RGBA, gl::UNSIGNED_BYTE),
182    };
183
184    let (filtering, mipmap_filtering) = match format {
185        TextureFormatRequest::Specific(TextureFormat::UncompressedIntegral(_)) => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
186        TextureFormatRequest::Specific(TextureFormat::UncompressedUnsigned(_)) => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
187        TextureFormatRequest::Specific(TextureFormat::StencilFormat(_)) => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
188        TextureFormatRequest::AnyIntegral => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
189        TextureFormatRequest::AnyUnsigned => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
190        TextureFormatRequest::AnyStencil => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
191        _ => (gl::LINEAR, gl::LINEAR_MIPMAP_LINEAR),
192    };
193
194    let is_multisampled = matches!(ty, Dimensions::Texture2dMultisample {..}
195        | Dimensions::Texture2dMultisampleArray {..});
196
197    let mut ctxt = facade.get_context().make_current();
198
199    let id = unsafe {
200        let has_mipmaps = texture_levels > 1;
201        let data = data;
202        let data_raw = if let Some((_, ref data)) = data {
203            data.as_ptr() as *const c_void
204        } else {
205            ptr::null()
206        };
207
208        if ctxt.state.pixel_store_unpack_alignment != 1 {
209            ctxt.state.pixel_store_unpack_alignment = 1;
210            ctxt.gl.PixelStorei(gl::UNPACK_ALIGNMENT, 1);
211        }
212
213        BufferAny::unbind_pixel_unpack(&mut ctxt);
214
215        let mut id: gl::types::GLuint = 0;
216        ctxt.gl.GenTextures(1, &mut id);
217
218        {
219            ctxt.gl.BindTexture(bind_point, id);
220            let act = ctxt.state.active_texture as usize;
221            ctxt.state.texture_units[act].texture = id;
222        }
223
224        if !is_multisampled {
225            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
226            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MAG_FILTER, filtering as i32);
227        }
228
229        match ty {
230            Dimensions::Texture1d { .. } => (),
231            Dimensions::Texture2dMultisample { .. } => (),
232            Dimensions::Texture2dMultisampleArray { .. } => (),
233            _ => {
234                ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
235            },
236        };
237
238        match ty {
239            Dimensions::Texture1d { .. } => (),
240            Dimensions::Texture2d { .. } => (),
241            Dimensions::Texture2dMultisample { .. } => (),
242            _ => {
243                ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_WRAP_R, gl::REPEAT as i32);
244            },
245        };
246
247        if has_mipmaps {
248            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MIN_FILTER,
249                                  mipmap_filtering as i32);
250        } else if !is_multisampled {
251            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MIN_FILTER,
252                                  filtering as i32);
253        }
254
255        if !has_mipmaps && (ctxt.version >= &Version(Api::Gl, 1, 2) ||
256                            ctxt.version >= &Version(Api::GlEs, 3, 0))
257        {
258            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_BASE_LEVEL, 0);
259            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MAX_LEVEL, 0);
260        }
261
262        if bind_point == gl::TEXTURE_3D || bind_point == gl::TEXTURE_2D_ARRAY ||
263           bind_point == gl::TEXTURE_CUBE_MAP_ARRAY
264        {
265            let mut data_raw = data_raw;
266
267            let width = match width as gl::types::GLsizei {
268                0 => { data_raw = ptr::null(); 1 },
269                a => a
270            };
271
272            let height = match height.unwrap() as gl::types::GLsizei {
273                0 => { data_raw = ptr::null(); 1 },
274                a => a
275            };
276
277            let depth = match depth.or(array_size).unwrap() as gl::types::GLsizei {
278                0 => { data_raw = ptr::null(); 1 },
279                a => a
280            };
281
282            if storage_internal_format.is_some() && (ctxt.version >= &Version(Api::Gl, 4, 2) || ctxt.extensions.gl_arb_texture_storage) {
283                ctxt.gl.TexStorage3D(bind_point, texture_levels,
284                                     storage_internal_format.unwrap() as gl::types::GLenum,
285                                     width, height, depth);
286
287                if !data_raw.is_null() {
288                    if is_client_compressed {
289                        ctxt.gl.CompressedTexSubImage3D(bind_point, 0, 0, 0, 0, width, height, depth,
290                                                         teximg_internal_format as u32,
291                                                         data_bufsize as i32, data_raw);
292                    } else {
293                        ctxt.gl.TexSubImage3D(bind_point, 0, 0, 0, 0, width, height, depth,
294                                              client_format, client_type, data_raw);
295                    }
296                }
297
298            } else if is_client_compressed && !data_raw.is_null() {
299                ctxt.gl.CompressedTexImage3D(bind_point, 0, teximg_internal_format as u32,
300                                   width, height, depth, 0, data_bufsize as i32, data_raw);
301            } else {
302                ctxt.gl.TexImage3D(bind_point, 0, teximg_internal_format as i32, width,
303                                   height, depth, 0, client_format as u32, client_type,
304                                   data_raw);
305            }
306
307        } else if bind_point == gl::TEXTURE_2D || bind_point == gl::TEXTURE_1D_ARRAY ||
308                  bind_point == gl::TEXTURE_CUBE_MAP
309        {
310            let mut data_raw = data_raw;
311
312            let width = match width as gl::types::GLsizei {
313                0 => { data_raw = ptr::null(); 1 },
314                a => a
315            };
316
317            let height = match height.or(array_size).unwrap() as gl::types::GLsizei {
318                0 => { data_raw = ptr::null(); 1 },
319                a => a
320            };
321
322            if storage_internal_format.is_some() && (ctxt.version >= &Version(Api::Gl, 4, 2) || ctxt.extensions.gl_arb_texture_storage) {
323                ctxt.gl.TexStorage2D(bind_point, texture_levels,
324                                     storage_internal_format.unwrap() as gl::types::GLenum,
325                                     width, height);
326
327                if !data_raw.is_null() {
328                    if is_client_compressed {
329                        ctxt.gl.CompressedTexSubImage2D(bind_point, 0, 0, 0, width, height,
330                                                         teximg_internal_format as u32,
331                                                         data_bufsize as i32, data_raw);
332                    } else {
333                        ctxt.gl.TexSubImage2D(bind_point, 0, 0, 0, width, height, client_format,
334                                              client_type, data_raw);
335                    }
336                }
337
338            } else if is_client_compressed && !data_raw.is_null() {
339                ctxt.gl.CompressedTexImage2D(bind_point, 0, teximg_internal_format as u32,
340                                   width, height, 0, data_bufsize as i32, data_raw);
341            } else {
342                ctxt.gl.TexImage2D(bind_point, 0, teximg_internal_format as i32, width,
343                                   height, 0, client_format as u32, client_type, data_raw);
344            }
345
346        } else if bind_point == gl::TEXTURE_2D_MULTISAMPLE {
347            assert!(data_raw.is_null());
348
349            let width = match width as gl::types::GLsizei {
350                0 => 1,
351                a => a
352            };
353
354            let height = match height.unwrap() as gl::types::GLsizei {
355                0 => 1,
356                a => a
357            };
358
359            if storage_internal_format.is_some() && (ctxt.version >= &Version(Api::Gl, 4, 2) || ctxt.extensions.gl_arb_texture_storage) {
360                ctxt.gl.TexStorage2DMultisample(gl::TEXTURE_2D_MULTISAMPLE,
361                                                samples.unwrap() as gl::types::GLsizei,
362                                                storage_internal_format.unwrap() as gl::types::GLenum,
363                                                width, height, gl::TRUE);
364
365            } else if ctxt.version >= &Version(Api::Gl, 3, 2) || ctxt.extensions.gl_arb_texture_multisample {
366                ctxt.gl.TexImage2DMultisample(gl::TEXTURE_2D_MULTISAMPLE,
367                                              samples.unwrap() as gl::types::GLsizei,
368                                              teximg_internal_format as gl::types::GLenum,
369                                              width, height, gl::TRUE);
370
371            } else {
372                unreachable!();
373            }
374
375        } else if bind_point == gl::TEXTURE_2D_MULTISAMPLE_ARRAY {
376            assert!(data_raw.is_null());
377
378            let width = match width as gl::types::GLsizei {
379                0 => 1,
380                a => a
381            };
382
383            let height = match height.unwrap() as gl::types::GLsizei {
384                0 => 1,
385                a => a
386            };
387
388            if storage_internal_format.is_some() && (ctxt.version >= &Version(Api::Gl, 4, 2) || ctxt.extensions.gl_arb_texture_storage) {
389                ctxt.gl.TexStorage3DMultisample(gl::TEXTURE_2D_MULTISAMPLE_ARRAY,
390                                                samples.unwrap() as gl::types::GLsizei,
391                                                storage_internal_format.unwrap() as gl::types::GLenum,
392                                                width, height, array_size.unwrap() as gl::types::GLsizei,
393                                                gl::TRUE);
394
395            } else if ctxt.version >= &Version(Api::Gl, 3, 2) || ctxt.extensions.gl_arb_texture_multisample {
396                ctxt.gl.TexImage3DMultisample(gl::TEXTURE_2D_MULTISAMPLE_ARRAY,
397                                              samples.unwrap() as gl::types::GLsizei,
398                                              teximg_internal_format as gl::types::GLenum,
399                                              width, height, array_size.unwrap() as gl::types::GLsizei,
400                                              gl::TRUE);
401
402            } else {
403                unreachable!();
404            }
405
406        } else if bind_point == gl::TEXTURE_1D {
407            let mut data_raw = data_raw;
408
409            let width = match width as gl::types::GLsizei {
410                0 => { data_raw = ptr::null(); 1 },
411                a => a
412            };
413
414            if storage_internal_format.is_some() && (ctxt.version >= &Version(Api::Gl, 4, 2) || ctxt.extensions.gl_arb_texture_storage) {
415                ctxt.gl.TexStorage1D(bind_point, texture_levels,
416                                     storage_internal_format.unwrap() as gl::types::GLenum,
417                                     width);
418
419                if !data_raw.is_null() {
420                    if is_client_compressed {
421                        ctxt.gl.CompressedTexSubImage1D(bind_point, 0, 0, width,
422                                                         teximg_internal_format as u32,
423                                                         data_bufsize as i32, data_raw);
424                    } else {
425                        ctxt.gl.TexSubImage1D(bind_point, 0, 0, width, client_format,
426                                              client_type, data_raw);
427                    }
428                }
429
430            } else if is_client_compressed && !data_raw.is_null() {
431                ctxt.gl.CompressedTexImage1D(bind_point, 0, teximg_internal_format as u32,
432                                   width, 0, data_bufsize as i32, data_raw);
433            } else {
434                ctxt.gl.TexImage1D(bind_point, 0, teximg_internal_format as i32, width,
435                                   0, client_format as u32, client_type, data_raw);
436            }
437
438        } else {
439            unreachable!();
440        }
441
442        // only generate mipmaps for color textures
443        if should_generate_mipmaps {
444            generate_mipmaps(&ctxt, bind_point);
445        }
446
447        id
448    };
449
450    Ok(TextureAny {
451        context: facade.get_context().clone(),
452        id,
453        requested_format: format,
454        actual_format: Cell::new(None),
455        ty,
456        levels: texture_levels as u32,
457        generate_mipmaps: should_generate_mipmaps,
458        owned: true,
459        memory: None,
460        latest_shader_write: Cell::new(0),
461    })
462}
463
464/// Builds a new texture reference from an existing, externally created OpenGL texture.
465/// If `owned` is true, this reference will take ownership of the texture and be responsible
466/// for cleaning it up. Otherwise, the texture must be cleaned up externally, but only
467/// after this reference's lifetime has ended.
468pub unsafe fn from_id<F: Facade + ?Sized>(facade: &F,
469                                 format: TextureFormatRequest,
470                                 id: gl::types::GLuint,
471                                 owned: bool,
472                                 mipmaps: MipmapsOption,
473                                 ty: Dimensions)
474                                 -> TextureAny {
475    let (width, height, depth, array_size, samples) = extract_dimensions(ty);
476    let mipmap_levels = mipmaps.num_levels(width, height, depth);
477    let should_generate_mipmaps = mipmaps.should_generate();
478    if should_generate_mipmaps {
479        let ctxt = facade.get_context().make_current();
480        generate_mipmaps(&ctxt, get_bind_point(ty));
481    }
482    TextureAny {
483        context: facade.get_context().clone(),
484        id,
485        requested_format: format,
486        actual_format: Cell::new(None),
487        ty,
488        levels: mipmap_levels,
489        generate_mipmaps: should_generate_mipmaps,
490        owned,
491        memory: None,
492        latest_shader_write: Cell::new(0),
493    }
494}
495
496/// Builds a new texture reference from an existing texture, externally created by a foreign
497/// API like Vulkan. The texture is imported via an opaque file descriptor.
498#[cfg(target_os = "linux")]
499pub unsafe fn new_from_fd<F: Facade + ?Sized>(facade: &F,
500                                              format: TextureFormat,
501                                              mipmaps: MipmapsOption,
502                                              ty: Dimensions,
503                                              params: super::ImportParameters,
504                                              fd: std::fs::File) -> Result<TextureAny,super::TextureImportError> {
505    use crate::ToGlEnum;
506    let memory = MemoryObject::new_from_fd(facade, params.dedicated_memory, fd, params.size)?;
507    
508    let (width, height, depth, array_size, samples) = extract_dimensions(ty);
509    let mipmap_levels = mipmaps.num_levels(width, height, depth);
510                                                  
511    let should_generate_mipmaps = mipmaps.should_generate();
512    let texture_levels = mipmaps.num_levels(width, height, depth) as gl::types::GLsizei;
513    
514    let bind_point = get_bind_point(ty);
515
516    let storage_internal_format = format.to_glenum();
517
518     let (filtering, mipmap_filtering) = match format {
519        TextureFormat::UncompressedIntegral(_) => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
520        TextureFormat::UncompressedUnsigned(_) => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
521        TextureFormat::StencilFormat(_) => (gl::NEAREST, gl::NEAREST_MIPMAP_NEAREST),
522        _ => (gl::LINEAR, gl::LINEAR_MIPMAP_LINEAR),
523     };
524
525    let is_multisampled = matches!(ty, Dimensions::Texture2dMultisample {..}
526                                   | Dimensions::Texture2dMultisampleArray {..});
527
528    let mut ctxt = facade.get_context().make_current();
529
530    let id = {
531        let has_mipmaps = texture_levels > 1;
532
533        let mut id: gl::types::GLuint = 0;
534        ctxt.gl.GenTextures(1, &mut id as *mut u32);
535
536        ctxt.gl.BindTexture(bind_point, id);
537        let act = ctxt.state.active_texture as usize;
538        ctxt.state.texture_units[act].texture = id;
539
540        let gl_tiling: crate::gl::types::GLenum = params.tiling.into();
541
542        ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_TILING_EXT, gl_tiling as i32);
543
544        if !is_multisampled {
545            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
546            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MAG_FILTER, filtering as i32);
547        }
548
549        match ty {
550            Dimensions::Texture1d { .. } => (),
551            Dimensions::Texture2dMultisample { .. } => (),
552            Dimensions::Texture2dMultisampleArray { .. } => (),
553            _ => {
554                ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
555            },
556        };
557
558        match ty {
559            Dimensions::Texture1d { .. } => (),
560            Dimensions::Texture2d { .. } => (),
561            Dimensions::Texture2dMultisample { .. } => (),
562            _ => {
563                ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_WRAP_R, gl::REPEAT as i32);
564            },
565        };
566
567        if has_mipmaps {
568            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MIN_FILTER,
569                                  mipmap_filtering as i32);
570        } else if !is_multisampled {
571            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MIN_FILTER,
572                                  filtering as i32);
573        }
574
575        if !has_mipmaps && (ctxt.version >= &Version(Api::Gl, 1, 2) ||
576                            ctxt.version >= &Version(Api::GlEs, 3, 0))
577        {
578            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_BASE_LEVEL, 0);
579            ctxt.gl.TexParameteri(bind_point, gl::TEXTURE_MAX_LEVEL, 0);
580        }
581
582        if bind_point == gl::TEXTURE_3D || bind_point == gl::TEXTURE_2D_ARRAY || bind_point == gl::TEXTURE_CUBE_MAP_ARRAY {
583            ctxt.gl.TexStorageMem3DEXT(bind_point, texture_levels, storage_internal_format,
584                                       width as gl::types::GLsizei, height.unwrap() as gl::types::GLsizei,
585                                       depth.unwrap() as gl::types::GLsizei, memory.get_id(), params.offset);
586        } else if bind_point == gl::TEXTURE_2D || bind_point == gl::TEXTURE_1D_ARRAY || bind_point == gl::TEXTURE_CUBE_MAP {
587            ctxt.gl.TexStorageMem2DEXT(bind_point, texture_levels, storage_internal_format as gl::types::GLenum,
588                                       width as gl::types::GLsizei, height.unwrap() as gl::types::GLsizei, memory.get_id(),
589                                       params.offset);          
590        } else if bind_point == gl::TEXTURE_2D_MULTISAMPLE {
591            ctxt.gl.TexStorageMem2DMultisampleEXT(bind_point, samples.unwrap() as gl::types::GLsizei,
592                                                  storage_internal_format, width as gl::types::GLsizei,
593                                                  height.unwrap() as gl::types::GLsizei, gl::TRUE, memory.get_id(), params.offset);
594        } else if bind_point == gl::TEXTURE_2D_MULTISAMPLE_ARRAY {
595            ctxt.gl.TexStorageMem3DMultisampleEXT(bind_point, samples.unwrap() as gl::types::GLsizei,
596                                                  storage_internal_format, width as gl::types::GLsizei,
597                                                  height.unwrap() as gl::types::GLsizei, array_size.unwrap() as gl::types::GLsizei,
598                                                  gl::TRUE, memory.get_id(), params.offset);
599        } else if bind_point == gl::TEXTURE_1D {
600            ctxt.gl.TexStorageMem1DEXT(bind_point, texture_levels, storage_internal_format, width as gl::types::GLsizei,
601                                       memory.get_id(), params.offset);
602        }
603
604        if should_generate_mipmaps {
605            generate_mipmaps(&ctxt, bind_point);
606        }
607
608        id
609    };
610    
611    Ok(TextureAny {
612        context: facade.get_context().clone(),
613        id,
614        requested_format: TextureFormatRequest::Specific(format),
615        actual_format: Cell::new(None),
616        ty,
617        levels: mipmap_levels,
618        generate_mipmaps: should_generate_mipmaps,
619        owned: false,
620        memory: Some(memory),
621        latest_shader_write: Cell::new(0),
622    })
623}
624
625impl TextureAny {
626    /// Returns the width of the texture.
627    #[inline]
628    pub fn get_width(&self) -> u32 {
629        match self.ty {
630            Dimensions::Texture1d { width, .. } => width,
631            Dimensions::Texture1dArray { width, .. } => width,
632            Dimensions::Texture2d { width, .. } => width,
633            Dimensions::Texture2dArray { width, .. } => width,
634            Dimensions::Texture2dMultisample { width, .. } => width,
635            Dimensions::Texture2dMultisampleArray { width, .. } => width,
636            Dimensions::Texture3d { width, .. } => width,
637            Dimensions::Cubemap { dimension, .. } => dimension,
638            Dimensions::CubemapArray { dimension, .. } => dimension,
639        }
640    }
641
642    /// Returns the height of the texture.
643    #[inline]
644    pub fn get_height(&self) -> Option<u32> {
645        match self.ty {
646            Dimensions::Texture1d { .. } => None,
647            Dimensions::Texture1dArray { .. } => None,
648            Dimensions::Texture2d { height, .. } => Some(height),
649            Dimensions::Texture2dArray { height, .. } => Some(height),
650            Dimensions::Texture2dMultisample { height, .. } => Some(height),
651            Dimensions::Texture2dMultisampleArray { height, .. } => Some(height),
652            Dimensions::Texture3d { height, .. } => Some(height),
653            Dimensions::Cubemap { dimension, .. } => Some(dimension),
654            Dimensions::CubemapArray { dimension, .. } => Some(dimension),
655        }
656    }
657
658    /// Returns the depth of the texture.
659    #[inline]
660    pub fn get_depth(&self) -> Option<u32> {
661        match self.ty {
662            Dimensions::Texture3d { depth, .. } => Some(depth),
663            _ => None
664        }
665    }
666
667    /// Returns the initial requested format.
668    #[inline]
669    #[doc(hidden)]
670    pub fn get_requested_format(&self) -> TextureFormatRequest {
671        self.requested_format
672    }
673
674    /// Returns the kind of texture.
675    #[inline]
676    pub fn kind(&self) -> TextureKind {
677        match self.requested_format {
678            TextureFormatRequest::Specific(TextureFormat::UncompressedFloat(_)) => TextureKind::Float,
679            TextureFormatRequest::Specific(TextureFormat::UncompressedIntegral(_)) => TextureKind::Integral,
680            TextureFormatRequest::Specific(TextureFormat::UncompressedUnsigned(_)) => TextureKind::Unsigned,
681            TextureFormatRequest::Specific(TextureFormat::Srgb(_)) => TextureKind::Float,
682            TextureFormatRequest::Specific(TextureFormat::CompressedFormat(_)) => TextureKind::Float,
683            TextureFormatRequest::Specific(TextureFormat::CompressedSrgbFormat(_)) => TextureKind::Float,
684            TextureFormatRequest::Specific(TextureFormat::DepthFormat(_)) => TextureKind::Depth,
685            TextureFormatRequest::Specific(TextureFormat::StencilFormat(_)) => TextureKind::Stencil,
686            TextureFormatRequest::Specific(TextureFormat::DepthStencilFormat(_)) => TextureKind::DepthStencil,
687            TextureFormatRequest::AnyFloatingPoint => TextureKind::Float,
688            TextureFormatRequest::AnyCompressed => TextureKind::Float,
689            TextureFormatRequest::AnySrgb => TextureKind::Float,
690            TextureFormatRequest::AnyCompressedSrgb => TextureKind::Float,
691            TextureFormatRequest::AnyIntegral => TextureKind::Integral,
692            TextureFormatRequest::AnyUnsigned => TextureKind::Unsigned,
693            TextureFormatRequest::AnyDepth => TextureKind::Depth,
694            TextureFormatRequest::AnyStencil => TextureKind::Stencil,
695            TextureFormatRequest::AnyDepthStencil => TextureKind::DepthStencil,
696        }
697    }
698
699    /// Returns the dimensions of the texture.
700    #[inline]
701    pub fn dimensions(&self) -> Dimensions {
702        self.ty
703    }
704
705    /// Returns the array size of the texture.
706    #[inline]
707    pub fn get_array_size(&self) -> Option<u32> {
708        match self.ty {
709            Dimensions::Texture1d { .. } => None,
710            Dimensions::Texture1dArray { array_size, .. } => Some(array_size),
711            Dimensions::Texture2d { .. } => None,
712            Dimensions::Texture2dArray { array_size, .. } => Some(array_size),
713            Dimensions::Texture2dMultisample { .. } => None,
714            Dimensions::Texture2dMultisampleArray { array_size, .. } => Some(array_size),
715            Dimensions::Texture3d { .. } => None,
716            Dimensions::Cubemap { .. } => None,
717            Dimensions::CubemapArray { array_size, .. } => Some(array_size),
718        }
719    }
720
721    /// Returns the number of samples of the texture if it is a multisampling texture.
722    #[inline]
723    pub fn get_samples(&self) -> Option<u32> {
724        match self.ty {
725            Dimensions::Texture2dMultisample { samples, .. } => Some(samples),
726            Dimensions::Texture2dMultisampleArray { samples, .. } => Some(samples),
727            _ => None
728        }
729    }
730
731    /// Returns a structure that represents the first layer of the texture. All textures have a
732    /// first layer.
733    #[inline]
734    pub fn first_layer(&self) -> TextureAnyLayer<'_> {
735        self.layer(0).unwrap()
736    }
737
738    /// Returns a structure that represents a specific layer of the texture.
739    ///
740    /// Non-array textures have only one layer. The number of layers can be queried with
741    /// `get_array_size`.
742    ///
743    /// Returns `None` if out of range.
744    #[inline]
745    pub fn layer(&self, layer: u32) -> Option<TextureAnyLayer<'_>> {
746        if layer >= self.get_array_size().unwrap_or(1) {
747            return None;
748        }
749
750        Some(TextureAnyLayer {
751            texture: self,
752            layer,
753        })
754    }
755
756    /// Returns the type of the texture (1D, 2D, 3D, etc.).
757    #[inline]
758    pub fn get_texture_type(&self) -> Dimensions {
759        self.ty
760    }
761
762    /// Determines the internal format of this texture.
763    #[inline]
764    pub fn get_internal_format(&self) -> Result<InternalFormat, GetFormatError> {
765        if let Some(format) = self.actual_format.get() {
766            format
767
768        } else {
769            let mut ctxt = self.context.make_current();
770            let format = get_format::get_format(&mut ctxt, self);
771            self.actual_format.set(Some(format));
772            format
773        }
774    }
775
776    /// Determines the number of depth and stencil bits in the format of this texture.
777    pub fn get_depth_stencil_bits(&self) -> (u16, u16) {
778        unsafe {
779            let ctxt = self.context.make_current();
780            let mut depth_bits: gl::types::GLint = 0;
781            let mut stencil_bits: gl::types::GLint = 0;
782            // FIXME: GL version considerations
783            ctxt.gl.GetTextureLevelParameteriv(self.id, 0, gl::TEXTURE_DEPTH_SIZE, &mut depth_bits);
784            ctxt.gl.GetTextureLevelParameteriv(self.id, 0, gl::TEXTURE_STENCIL_SIZE, &mut stencil_bits);
785            (depth_bits as u16, stencil_bits as u16)
786        }
787    }
788
789    /// Returns the number of mipmap levels of the texture.
790    #[inline]
791    pub fn get_mipmap_levels(&self) -> u32 {
792        self.levels
793    }
794
795    /// Returns a structure that represents the main mipmap level of the texture.
796    #[inline]
797    pub fn main_level(&self) -> TextureAnyMipmap<'_> {
798        self.mipmap(0).unwrap()
799    }
800
801    /// Returns a structure that represents a specific mipmap of the texture.
802    ///
803    /// Returns `None` if out of range.
804    #[inline]
805    pub fn mipmap(&self, level: u32) -> Option<TextureAnyMipmap<'_>> {
806        if level >= self.levels {
807            return None;
808        }
809
810        let pow = 2u32.pow(level);
811        Some(TextureAnyMipmap {
812            texture: self,
813            level,
814            width: cmp::max(1, self.get_width() / pow),
815            height: self.get_height().map(|height| cmp::max(1, height / pow)),
816            depth: self.get_depth().map(|depth| cmp::max(1, depth / pow)),
817        })
818    }
819
820    /// Binds this texture and generates mipmaps.
821    #[inline]
822    pub unsafe fn generate_mipmaps(&self) {
823        let mut ctxt = self.context.make_current();
824        self.bind_to_current(&mut ctxt);
825        generate_mipmaps(&ctxt, self.get_bind_point());
826    }
827}
828
829impl TextureExt for TextureAny {
830    #[inline]
831    fn get_texture_id(&self) -> gl::types::GLuint {
832        self.id
833    }
834
835    #[inline]
836    fn get_context(&self) -> &Rc<Context> {
837        &self.context
838    }
839
840    #[inline]
841    fn get_bind_point(&self) -> gl::types::GLenum {
842        get_bind_point(self.ty)
843    }
844
845    fn bind_to_current(&self, ctxt: &mut CommandContext<'_>) -> gl::types::GLenum {
846        let bind_point = self.get_bind_point();
847
848        let texture_unit = ctxt.state.active_texture;
849        if ctxt.state.texture_units[texture_unit as usize].texture != self.id {
850            unsafe { ctxt.gl.BindTexture(bind_point, self.id) };
851            ctxt.state.texture_units[texture_unit as usize].texture = self.id;
852        }
853
854        bind_point
855    }
856
857    fn prepare_for_access(&self, ctxt: &mut CommandContext<'_>, access_type: crate::TextureAccess) {
858        match access_type {
859            crate::TextureAccess::TextureFetch => {
860                if self.latest_shader_write.get() >= ctxt.state.latest_memory_barrier_texture_fetch {
861                    unsafe { ctxt.gl.MemoryBarrier(gl::TEXTURE_FETCH_BARRIER_BIT); }
862                    ctxt.state.latest_memory_barrier_texture_fetch = ctxt.state.next_draw_call_id;
863                }
864            },
865            crate::TextureAccess::ImageUnit { will_write } =>{
866                if self.latest_shader_write.get() >= ctxt.state.latest_memory_barrier_shader_image_access {
867                    unsafe { ctxt.gl.MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT); }
868                    ctxt.state.latest_memory_barrier_shader_image_access = ctxt.state.next_draw_call_id;
869                }
870
871                if will_write {
872                    self.latest_shader_write.set(ctxt.state.next_draw_call_id);
873                }
874            },
875            crate::TextureAccess::Framebuffer => {
876                if self.latest_shader_write.get() >= ctxt.state.latest_memory_barrier_framebuffer {
877                    unsafe { ctxt.gl.MemoryBarrier(gl::FRAMEBUFFER_BARRIER_BIT); }
878                    ctxt.state.latest_memory_barrier_framebuffer = ctxt.state.next_draw_call_id;
879                }
880            },
881        }        
882    }
883
884
885
886
887}
888
889impl GlObject for TextureAny {
890    type Id = gl::types::GLuint;
891
892    #[inline]
893    fn get_id(&self) -> gl::types::GLuint {
894        self.id
895    }
896}
897
898impl fmt::Debug for TextureAny {
899    #[inline]
900    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
901        write!(fmt, "Texture #{} (dimensions: {}x{}x{}x{})", self.id,
902               self.get_width(), self.get_height().unwrap_or(1), self.get_depth().unwrap_or(1),
903               self.get_array_size().unwrap_or(1))
904    }
905}
906
907impl Drop for TextureAny {
908    fn drop(&mut self) {
909        let mut ctxt = self.context.make_current();
910
911        // removing FBOs which contain this texture
912        fbo::FramebuffersContainer::purge_texture(&mut ctxt, self.id);
913
914        // resetting the bindings
915        for tex_unit in ctxt.state.texture_units.iter_mut() {
916            if tex_unit.texture == self.id {
917                tex_unit.texture = 0;
918            }
919        }
920
921        if self.owned {
922            unsafe { ctxt.gl.DeleteTextures(1, [ self.id ].as_ptr()); }
923        }
924    }
925}
926
927/// Represents a specific layer of an array texture and 3D textures.
928#[derive(Copy, Clone)]
929pub struct TextureAnyLayer<'a> {
930    /// The texture.
931    texture: &'a TextureAny,
932    /// The layer.
933    layer: u32,
934}
935
936impl<'a> TextureAnyLayer<'a> {
937    /// Returns the texture.
938    #[inline]
939    pub fn get_texture(&self) -> &'a TextureAny {
940        self.texture
941    }
942
943    /// Returns the number of samples of the texture.
944    #[inline]
945    pub fn get_samples(&self) -> Option<u32> {
946        self.texture.get_samples()
947    }
948
949    /// Returns the layer of the texture.
950    #[inline]
951    pub fn get_layer(&self) -> u32 {
952        self.layer
953    }
954
955    /// Returns a structure that represents the main mipmap level of this layer of the texture.
956    #[inline]
957    pub fn main_level(&self) -> TextureAnyLayerMipmap<'a> {
958        self.mipmap(0).unwrap()
959    }
960
961    /// Returns a structure that represents a specific mipmap of this layer of the texture.
962    ///
963    /// Returns `None` if out of range.
964    #[inline]
965    pub fn mipmap(&self, level: u32) -> Option<TextureAnyLayerMipmap<'a>> {
966        if level >= self.texture.levels {
967            return None;
968        }
969
970        let pow = 2u32.pow(level);
971
972        Some(TextureAnyLayerMipmap {
973            texture: self.texture,
974            level,
975            layer: self.layer,
976            width: cmp::max(1, self.texture.get_width() / pow),
977            height: self.texture.get_height().map(|height| cmp::max(1, height / pow)),
978        })
979    }
980}
981
982/// Represents a specific mipmap of a texture.
983#[derive(Copy, Clone)]
984pub struct TextureAnyMipmap<'a> {
985    /// The texture.
986    texture: &'a TextureAny,
987
988    /// Mipmap level.
989    level: u32,
990
991    /// Width of this mipmap level.
992    width: u32,
993    /// Height of this mipmap level.
994    height: Option<u32>,
995    /// Depth of this mipmap level.
996    depth: Option<u32>,
997}
998
999impl<'a> TextureAnyMipmap<'a> {
1000    /// Returns the width of the mipmap.
1001    #[inline]
1002    pub fn get_width(&self) -> u32 {
1003        self.width
1004    }
1005
1006    /// Returns the height of the mipmap.
1007    #[inline]
1008    pub fn get_height(&self) -> Option<u32> {
1009        self.height
1010    }
1011
1012    /// Returns the depth of the mipmap.
1013    #[inline]
1014    pub fn get_depth(&self) -> Option<u32> {
1015        self.depth
1016    }
1017
1018    /// Computes a tuple (width, height, depth) of mipmap dimensions,
1019    /// using 1 for unused dimensions.
1020    ///
1021    /// In the case of 1D texture arrays, use array size as width.
1022    /// In the case of 2D texture arrays, use array size as depth.
1023    #[inline]
1024    fn get_mipmap_dimensions(&self) -> (u32, u32, u32) {
1025        let tex_depth = match self.texture.ty {
1026            Dimensions::Texture2dArray { array_size, .. } => array_size,
1027            _ => self.depth.unwrap_or(1),
1028        };
1029        let tex_height = match self.texture.ty {
1030            Dimensions::Texture1dArray { array_size, .. } => array_size,
1031            _ => self.height.unwrap_or(1),
1032        };
1033        (self.width, tex_height, tex_depth)
1034    }
1035
1036    /// Returns the number of samples of the texture.
1037    #[inline]
1038    pub fn get_samples(&self) -> Option<u32> {
1039        self.texture.get_samples()
1040    }
1041
1042    /// Returns the texture.
1043    #[inline]
1044    pub fn get_texture(&self) -> &'a TextureAny {
1045        self.texture
1046    }
1047
1048    /// Returns the level of the texture.
1049    #[inline]
1050    pub fn get_level(&self) -> u32 {
1051        self.level
1052    }
1053
1054    /// Returns a structure that represents the first layer of this mipmap of the texture. All
1055    /// textures have a first layer.
1056    #[inline]
1057    pub fn first_layer(&self) -> TextureAnyLayerMipmap<'a> {
1058        self.layer(0).unwrap()
1059    }
1060
1061    /// Returns a structure that represents a specific layer of this mipmap of the texture.
1062    ///
1063    /// Non-array textures have only one layer. The number of layers can be queried with
1064    /// `get_array_size`.
1065    ///
1066    /// Returns `None` if out of range.
1067    #[inline]
1068    pub fn layer(&self, layer: u32) -> Option<TextureAnyLayerMipmap<'a>> {
1069        if let Some(array_size) = self.texture.get_array_size() {
1070            if layer >= array_size {
1071                return None;
1072            }
1073        }
1074
1075        if let Some(depth) = self.depth {
1076            if layer >= depth {
1077                return None;
1078            }
1079        }
1080
1081        if layer >= 1 && self.depth.is_none() && self.texture.get_array_size().is_none() {
1082            return None;
1083        }
1084
1085        Some(TextureAnyLayerMipmap {
1086            texture: self.texture,
1087            layer,
1088            level: self.level,
1089            width: self.width,
1090            height: self.height,
1091        })
1092    }
1093
1094    /// Returns the array size of the texture.
1095    #[inline]
1096    pub fn get_array_size(&self) -> Option<u32> {
1097        self.texture.get_array_size()
1098    }
1099
1100    /// Uploads data to the texture from a buffer.
1101    ///
1102    /// # Panic
1103    ///
1104    /// Panics if the offsets and dimensions are outside the boundaries of the texture. Panics
1105    /// if the buffer is not big enough to hold the data.
1106    #[inline]
1107    pub fn raw_upload_from_pixel_buffer<P>(&self, source: BufferSlice<'_, [P]>, x: Range<u32>,
1108                                           y: Range<u32>, z: Range<u32>)
1109                                           where P: PixelValue
1110    {
1111        self.raw_upload_from_pixel_buffer_impl(source, x, y, z, false);
1112    }
1113
1114    /// Uploads data to the texture from a buffer. The R, G and B components are flipped.
1115    ///
1116    /// # Panic
1117    ///
1118    /// Panics if the offsets and dimensions are outside the boundaries of the texture. Panics
1119    /// if the buffer is not big enough to hold the data.
1120    #[inline]
1121    pub fn raw_upload_from_pixel_buffer_inverted<P>(&self, source: BufferSlice<'_, [P]>,
1122                                                    x: Range<u32>, y: Range<u32>, z: Range<u32>)
1123                                                    where P: PixelValue
1124    {
1125        self.raw_upload_from_pixel_buffer_impl(source, x, y, z, true);
1126    }
1127
1128    fn raw_upload_from_pixel_buffer_impl<P>(&self, source: BufferSlice<'_, [P]>, x: Range<u32>,
1129                                            y: Range<u32>, z: Range<u32>, inverted: bool)
1130                                            where P: PixelValue
1131    {
1132        let tex_dim = self.get_mipmap_dimensions();
1133        assert!(x.start < tex_dim.0);
1134        assert!(y.start < tex_dim.1);
1135        assert!(z.start < tex_dim.2);
1136        assert!(x.end <= tex_dim.0);
1137        assert!(y.end <= tex_dim.1);
1138        assert!(z.end <= tex_dim.2);
1139
1140        let width = x.end - x.start;
1141        let height = y.end - y.start;
1142        let depth = z.end - z.start;
1143
1144        if source.len() < (width * height * depth) as usize {
1145            panic!("Buffer is too small");
1146        }
1147
1148        let (client_format, client_type) =
1149            image_format::client_format_to_glenum(&self.texture.context,
1150                                                  ClientFormatAny::ClientFormat(P::get_format()),
1151                                                  self.texture.requested_format, inverted).unwrap();
1152
1153        let mut ctxt = self.texture.context.make_current();
1154
1155        // binds the pixel buffer
1156        source.prepare_and_bind_for_pixel_unpack(&mut ctxt);
1157
1158        match self.texture.ty {
1159            Dimensions::Texture1d { .. } => {
1160                if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1161                   ctxt.extensions.gl_arb_direct_state_access
1162                {
1163                    unsafe {
1164                        ctxt.gl.TextureSubImage1D(self.texture.id,
1165                                                  self.level as gl::types::GLint,
1166                                                  x.start as gl::types::GLint,
1167                                                  width as gl::types::GLsizei,
1168                                                  client_format, client_type,
1169                                                  source.get_offset_bytes() as *const() as *const _);
1170                    }
1171
1172                }  else if ctxt.extensions.gl_ext_direct_state_access {
1173                    unsafe {
1174                        ctxt.gl.TextureSubImage1DEXT(self.texture.id, self.texture.get_bind_point(),
1175                                                     self.level as gl::types::GLint,
1176                                                     x.start as gl::types::GLint,
1177                                                     width as gl::types::GLsizei,
1178                                                     client_format, client_type,
1179                                                     source.get_offset_bytes() as *const() as *const _);
1180                    }
1181
1182                } else {
1183                    self.texture.bind_to_current(&mut ctxt);
1184                    unsafe {
1185                        ctxt.gl.TexSubImage1D(self.texture.get_bind_point(),
1186                                              self.level as gl::types::GLint,
1187                                              x.start as gl::types::GLint,
1188                                              width as gl::types::GLsizei,
1189                                              client_format, client_type,
1190                                              source.get_offset_bytes() as *const() as *const _);
1191                    }
1192                }
1193            },
1194
1195            Dimensions::Texture1dArray { .. } | Dimensions::Texture2d { .. } |
1196            Dimensions::Texture2dMultisample { .. } |
1197            Dimensions::Texture2dMultisampleArray { .. } => {
1198                if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1199                   ctxt.extensions.gl_arb_direct_state_access
1200                {
1201                    unsafe {
1202                        ctxt.gl.TextureSubImage2D(self.texture.id,
1203                                                  self.level as gl::types::GLint,
1204                                                  x.start as gl::types::GLint,
1205                                                  y.start as gl::types::GLint,
1206                                                  width as gl::types::GLsizei,
1207                                                  height as gl::types::GLsizei,
1208                                                  client_format, client_type,
1209                                                  source.get_offset_bytes() as *const() as *const _);
1210                    }
1211
1212                }  else if ctxt.extensions.gl_ext_direct_state_access {
1213                    unsafe {
1214                        ctxt.gl.TextureSubImage2DEXT(self.texture.id, self.texture.get_bind_point(),
1215                                                     self.level as gl::types::GLint,
1216                                                     x.start as gl::types::GLint,
1217                                                     y.start as gl::types::GLint,
1218                                                     width as gl::types::GLsizei,
1219                                                     height as gl::types::GLsizei,
1220                                                     client_format, client_type,
1221                                                     source.get_offset_bytes() as *const() as *const _);
1222                    }
1223
1224                } else {
1225                    self.texture.bind_to_current(&mut ctxt);
1226                    unsafe {
1227                        ctxt.gl.TexSubImage2D(self.texture.get_bind_point(),
1228                                              self.level as gl::types::GLint,
1229                                              x.start as gl::types::GLint,
1230                                              y.start as gl::types::GLint,
1231                                              width as gl::types::GLsizei,
1232                                              height as gl::types::GLsizei,
1233                                              client_format, client_type,
1234                                              source.get_offset_bytes() as *const() as *const _);
1235                    }
1236                }
1237            },
1238
1239            Dimensions::Texture2dArray { .. } | Dimensions::Texture3d { .. } => {
1240                if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1241                   ctxt.extensions.gl_arb_direct_state_access
1242                {
1243                    unsafe {
1244                        ctxt.gl.TextureSubImage3D(self.texture.id,
1245                                                  self.level as gl::types::GLint,
1246                                                  x.start as gl::types::GLint,
1247                                                  y.start as gl::types::GLint,
1248                                                  z.start as gl::types::GLint,
1249                                                  width as gl::types::GLsizei,
1250                                                  height as gl::types::GLsizei,
1251                                                  depth as gl::types::GLsizei,
1252                                                  client_format, client_type,
1253                                                  source.get_offset_bytes() as *const() as *const _);
1254                    }
1255
1256                }  else if ctxt.extensions.gl_ext_direct_state_access {
1257                    unsafe {
1258                        ctxt.gl.TextureSubImage3DEXT(self.texture.id, self.texture.get_bind_point(),
1259                                                     self.level as gl::types::GLint,
1260                                                     x.start as gl::types::GLint,
1261                                                     y.start as gl::types::GLint,
1262                                                     z.start as gl::types::GLint,
1263                                                     width as gl::types::GLsizei,
1264                                                     height as gl::types::GLsizei,
1265                                                     depth as gl::types::GLsizei,
1266                                                     client_format, client_type,
1267                                                     source.get_offset_bytes() as *const() as *const _);
1268                    }
1269
1270                } else {
1271                    self.texture.bind_to_current(&mut ctxt);
1272                    unsafe {
1273                        ctxt.gl.TexSubImage3D(self.texture.get_bind_point(),
1274                                              self.level as gl::types::GLint,
1275                                              x.start as gl::types::GLint,
1276                                              y.start as gl::types::GLint,
1277                                              z.start as gl::types::GLint,
1278                                              width as gl::types::GLsizei,
1279                                              height as gl::types::GLsizei,
1280                                              depth as gl::types::GLsizei,
1281                                              client_format, client_type,
1282                                              source.get_offset_bytes() as *const() as *const _);
1283                    }
1284                }
1285            },
1286
1287            Dimensions::Cubemap { .. } | Dimensions::CubemapArray { .. } => {
1288                panic!("Can't upload to cubemaps");     // TODO: better handling
1289            },
1290        }
1291
1292        // handling synchronization for the buffer
1293        if let Some(fence) = source.add_fence() {
1294            fence.insert(&mut ctxt);
1295        }
1296    }
1297}
1298
1299impl<'t> TextureMipmapExt for TextureAnyMipmap<'t> {
1300    fn upload_texture<'d, P>(&self, x_offset: u32, y_offset: u32, z_offset: u32,
1301                             (format, data): (ClientFormatAny, Cow<'d, [P]>), width: u32,
1302                             height: Option<u32>, depth: Option<u32>,
1303                             regen_mipmaps: bool)
1304                             -> Result<(), ()>   // TODO return a better Result!?
1305                             where P: Send + Copy + Clone + 'd
1306    {
1307        let id = self.texture.id;
1308        let level = self.level;
1309
1310        let (is_client_compressed, data_bufsize) = (format.is_compressed(),
1311                                                    format.get_buffer_size(width, height, depth, None));
1312        let regen_mipmaps = regen_mipmaps && self.texture.levels >= 2 &&
1313                            self.texture.generate_mipmaps && !is_client_compressed;
1314
1315        assert!(!regen_mipmaps || level == 0);  // when regen_mipmaps is true, level must be 0!
1316        assert!(x_offset <= self.width);
1317        assert!(y_offset <= self.height.unwrap_or(1));
1318        assert!(z_offset <= self.depth.unwrap_or(1));
1319        assert!(x_offset + width <= self.width);
1320        assert!(y_offset + height.unwrap_or(1) <= self.height.unwrap_or(1));
1321        assert!(z_offset + depth.unwrap_or(1) <= self.depth.unwrap_or(1));
1322
1323        if data.len() * mem::size_of::<P>() != data_bufsize
1324        {
1325            panic!("Texture data size mismatch");
1326        }
1327
1328        let (client_format, client_type) = image_format::client_format_to_glenum(&self.texture.context,
1329                                                                                 format,
1330                                                                                 self.texture.requested_format, false)
1331                                                                                 .map_err(|_| ())?;
1332
1333        let mut ctxt = self.texture.context.make_current();
1334
1335        unsafe {
1336            if ctxt.state.pixel_store_unpack_alignment != 1 {
1337                ctxt.state.pixel_store_unpack_alignment = 1;
1338                ctxt.gl.PixelStorei(gl::UNPACK_ALIGNMENT, 1);
1339            }
1340
1341            BufferAny::unbind_pixel_unpack(&mut ctxt);
1342            let bind_point = self.texture.bind_to_current(&mut ctxt);
1343
1344            if bind_point == gl::TEXTURE_3D || bind_point == gl::TEXTURE_2D_ARRAY {
1345                unimplemented!();
1346
1347            } else if bind_point == gl::TEXTURE_2D || bind_point == gl::TEXTURE_1D_ARRAY {
1348                assert!(z_offset == 0);
1349                // FIXME should glTexImage be used here somewhere or glTexSubImage does it just fine?
1350                if is_client_compressed {
1351                    ctxt.gl.CompressedTexSubImage2D(bind_point, level as gl::types::GLint,
1352                                                    x_offset as gl::types::GLint,
1353                                                    y_offset as gl::types::GLint,
1354                                                    width as gl::types::GLsizei,
1355                                                    height.unwrap_or(1) as gl::types::GLsizei,
1356                                                    client_format,
1357                                                    data_bufsize  as gl::types::GLsizei,
1358                                                    data.as_ptr() as *const _);
1359                } else {
1360                    ctxt.gl.TexSubImage2D(bind_point, level as gl::types::GLint,
1361                                          x_offset as gl::types::GLint,
1362                                          y_offset as gl::types::GLint,
1363                                          width as gl::types::GLsizei,
1364                                          height.unwrap_or(1) as gl::types::GLsizei,
1365                                          client_format, client_type,
1366                                          data.as_ptr() as *const _);
1367                }
1368
1369            } else {
1370                assert!(z_offset == 0);
1371                assert!(y_offset == 0);
1372
1373                unimplemented!();
1374            }
1375
1376            // regenerate mipmaps if there are some
1377            if regen_mipmaps {
1378                if ctxt.version >= &Version(Api::Gl, 3, 0) {
1379                    ctxt.gl.GenerateMipmap(bind_point);
1380                } else {
1381                    ctxt.gl.GenerateMipmapEXT(bind_point);
1382                }
1383            }
1384
1385            Ok(())
1386        }
1387    }
1388
1389    fn download_compressed_data(&self) -> Option<(ClientFormatAny, Vec<u8>)> {
1390        let texture = self.texture;
1391        let level = self.level as i32;
1392
1393        let mut ctxt = texture.context.make_current();
1394
1395        unsafe {
1396            let bind_point = texture.bind_to_current(&mut ctxt);
1397
1398            let mut is_compressed = 0;
1399            ctxt.gl.GetTexLevelParameteriv(bind_point, level, gl::TEXTURE_COMPRESSED, &mut is_compressed);
1400            if is_compressed != 0 {
1401
1402                let mut buffer_size = 0;
1403                ctxt.gl.GetTexLevelParameteriv(bind_point, level, gl::TEXTURE_COMPRESSED_IMAGE_SIZE, &mut buffer_size);
1404                let mut internal_format = 0;
1405                ctxt.gl.GetTexLevelParameteriv(bind_point, level, gl::TEXTURE_INTERNAL_FORMAT, &mut internal_format);
1406
1407                match ClientFormatAny::from_internal_compressed_format(internal_format as gl::types::GLenum) {
1408                    Some(known_format) => {
1409                        let mut buf = Vec::with_capacity(buffer_size as usize);
1410                        buf.set_len(buffer_size as usize);
1411
1412                        BufferAny::unbind_pixel_pack(&mut ctxt);
1413
1414                        // adjusting data alignement
1415                        let ptr = buf.as_ptr() as *const u8;
1416                        let ptr = ptr as usize;
1417                        if (ptr % 8) == 0 {
1418                        } else if (ptr % 4) == 0 && ctxt.state.pixel_store_pack_alignment != 4 {
1419                            ctxt.state.pixel_store_pack_alignment = 4;
1420                            ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 4);
1421                        } else if (ptr % 2) == 0 && ctxt.state.pixel_store_pack_alignment > 2 {
1422                            ctxt.state.pixel_store_pack_alignment = 2;
1423                            ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 2);
1424                        } else if ctxt.state.pixel_store_pack_alignment != 1 {
1425                            ctxt.state.pixel_store_pack_alignment = 1;
1426                            ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 1);
1427                        }
1428
1429                        ctxt.gl.GetCompressedTexImage(bind_point, level, buf.as_mut_ptr() as *mut _);
1430                        Some((known_format, buf))
1431                    },
1432                    None => None,
1433                }
1434
1435            } else {
1436                None
1437            }
1438        }
1439    }
1440}
1441
1442/// Represents a specific layer of a specific mipmap. This is the same as `TextureAnyImage`, except
1443/// for 3D textures, cubemaps and cubemap arrays.
1444#[derive(Copy, Clone)]
1445pub struct TextureAnyLayerMipmap<'a> {
1446    /// The texture.
1447    texture: &'a TextureAny,
1448
1449    /// Layer for array textures, or 0 for other textures.
1450    layer: u32,
1451    /// Mipmap level.
1452    level: u32,
1453
1454    /// Width of this layer of mipmap.
1455    width: u32,
1456    /// Height of this layer of mipmap.
1457    height: Option<u32>,
1458}
1459
1460impl<'a> TextureAnyLayerMipmap<'a> {
1461    /// Returns the texture.
1462    #[inline]
1463    pub fn get_texture(&self) -> &'a TextureAny {
1464        self.texture
1465    }
1466
1467    /// Returns the level of the texture.
1468    #[inline]
1469    pub fn get_level(&self) -> u32 {
1470        self.level
1471    }
1472
1473    /// Returns the layer of the texture.
1474    #[inline]
1475    pub fn get_layer(&self) -> u32 {
1476        self.layer
1477    }
1478
1479    /// Returns the width of this texture slice.
1480    #[inline]
1481    pub fn get_width(&self) -> u32 {
1482        self.width
1483    }
1484
1485    /// Returns the height of this texture slice.
1486    #[inline]
1487    pub fn get_height(&self) -> Option<u32> {
1488        self.height
1489    }
1490
1491    /// Returns the number of samples of the texture.
1492    #[inline]
1493    pub fn get_samples(&self) -> Option<u32> {
1494        self.texture.get_samples()
1495    }
1496
1497    /// Turns this into an image.
1498    ///
1499    /// Returns `None` if `cube_layer` is `None` and the texture is a cubemap. Returns `None` if
1500    /// `cube_layer` is `Some` and the texture is not a cubemap.
1501    #[inline]
1502    pub fn into_image(&self, cube_layer: Option<CubeLayer>) -> Option<TextureAnyImage<'a>> {
1503        match (self.texture.ty, cube_layer) {
1504            (Dimensions::Cubemap { .. }, Some(_)) => (),
1505            (Dimensions::CubemapArray { .. }, Some(_)) => (),
1506            (Dimensions::Cubemap { .. }, None) => return None,
1507            (Dimensions::CubemapArray { .. }, None) => return None,
1508            (_, Some(_)) => return None,
1509            _ => ()
1510        };
1511
1512        Some(TextureAnyImage {
1513            texture: self.texture,
1514            layer: self.layer,
1515            level: self.level,
1516            cube_layer,
1517            width: self.width,
1518            height: self.height,
1519        })
1520    }
1521}
1522
1523/// Represents a specific 2D image of a texture. 1D textures are considered as having a height of 1.
1524#[derive(Copy, Clone)]
1525pub struct TextureAnyImage<'a> {
1526    /// The texture.
1527    texture: &'a TextureAny,
1528
1529    /// Layer for array textures, or 0 for other textures.
1530    layer: u32,
1531    /// Mipmap level.
1532    level: u32,
1533    /// The layer of the cubemap if relevant.
1534    cube_layer: Option<CubeLayer>,
1535
1536    /// Width of this image.
1537    width: u32,
1538    /// Height of this image.
1539    height: Option<u32>,
1540}
1541
1542impl<'a> TextureAnyImage<'a> {
1543    /// Returns the texture.
1544    #[inline]
1545    pub fn get_texture(&self) -> &'a TextureAny {
1546        self.texture
1547    }
1548
1549    /// Returns the level of the texture.
1550    #[inline]
1551    pub fn get_level(&self) -> u32 {
1552        self.level
1553    }
1554
1555    /// Returns the layer of the texture.
1556    #[inline]
1557    pub fn get_layer(&self) -> u32 {
1558        self.layer
1559    }
1560
1561    /// Returns the cubemap layer of this image, or `None` if the texture is not a cubemap.
1562    #[inline]
1563    pub fn get_cubemap_layer(&self) -> Option<CubeLayer> {
1564        self.cube_layer
1565    }
1566
1567    /// Returns the width of this texture slice.
1568    #[inline]
1569    pub fn get_width(&self) -> u32 {
1570        self.width
1571    }
1572
1573    /// Returns the height of this texture slice.
1574    #[inline]
1575    pub fn get_height(&self) -> Option<u32> {
1576        self.height
1577    }
1578
1579    /// Returns the number of samples of the texture.
1580    #[inline]
1581    pub fn get_samples(&self) -> Option<u32> {
1582        self.texture.get_samples()
1583    }
1584
1585    /// Reads the content of the image.
1586    ///
1587    /// # Panic
1588    ///
1589    /// - Panics if the rect is out of range.
1590    /// - Panics if it fails to read the texture.
1591    ///
1592    pub fn raw_read<T, P>(&self, rect: &Rect) -> T where T: Texture2dDataSink<P>, P: PixelValue {
1593        assert!(rect.left + rect.width <= self.width);
1594        assert!(rect.bottom + rect.height <= self.height.unwrap_or(1));
1595
1596        let mut ctxt = self.texture.context.make_current();
1597
1598        let mut data = Vec::new();
1599        ops::read(&mut ctxt, &fbo::RegularAttachment::Texture(*self), &rect, &mut data, false)
1600            .unwrap();
1601
1602        T::from_raw(Cow::Owned(data), self.width, self.height.unwrap_or(1))
1603    }
1604
1605    /// Reads the content of the image to a pixel buffer.
1606    ///
1607    /// # Panic
1608    ///
1609    /// - Panics if the rect is out of range.
1610    /// - Panics if the buffer is not large enough.
1611    /// - Panics if it fails to read the texture.
1612    ///
1613    pub fn raw_read_to_pixel_buffer<P>(&self, rect: &Rect, dest: &PixelBuffer<P>)
1614        where P: PixelValue
1615    {
1616        assert!(rect.left + rect.width <= self.width);
1617        assert!(rect.bottom + rect.height <= self.height.unwrap_or(1));
1618        assert!(dest.len() >= rect.width as usize * rect.height as usize);
1619
1620        let size = rect.width as usize * rect.height as usize * 4;
1621        let mut ctxt = self.texture.context.make_current();
1622        ops::read(&mut ctxt, &fbo::RegularAttachment::Texture(*self), &rect, dest, false)
1623            .unwrap();
1624    }
1625
1626    /// Clears the content of the texture to a specific value.
1627    ///
1628    /// # Panic
1629    ///
1630    /// Panics if `data` does not match the kind of texture. For example passing a `[i32; 4]` when
1631    /// using a regular (float) texture.
1632    ///
1633    pub fn raw_clear_buffer<D>(&self, data: D)
1634        where D: Into<ClearBufferData>
1635    {
1636        unsafe {
1637            let mut ctxt = self.texture.context.make_current();
1638            let attachment = fbo::RegularAttachment::Texture(*self);
1639            fbo::FramebuffersContainer::clear_buffer(&mut ctxt, &attachment, data);
1640        }
1641    }
1642}