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