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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
47#[allow(missing_docs)] pub 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
60pub struct TextureAny {
62 context: Rc<Context>,
63 id: gl::types::GLuint,
64 requested_format: TextureFormatRequest,
65
66 actual_format: Cell<Option<Result<InternalFormat, GetFormatError>>>,
69
70 ty: Dimensions,
72
73 levels: u32,
75 generate_mipmaps: bool,
77
78 owned: bool,
80
81 memory: Option<MemoryObject>,
83
84 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
132pub 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 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 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()); }
161
162 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 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
464pub 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#[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 #[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 #[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 #[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 #[inline]
669 #[doc(hidden)]
670 pub fn get_requested_format(&self) -> TextureFormatRequest {
671 self.requested_format
672 }
673
674 #[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 #[inline]
701 pub fn dimensions(&self) -> Dimensions {
702 self.ty
703 }
704
705 #[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 #[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 #[inline]
734 pub fn first_layer(&self) -> TextureAnyLayer<'_> {
735 self.layer(0).unwrap()
736 }
737
738 #[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 #[inline]
758 pub fn get_texture_type(&self) -> Dimensions {
759 self.ty
760 }
761
762 #[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 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 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 #[inline]
791 pub fn get_mipmap_levels(&self) -> u32 {
792 self.levels
793 }
794
795 #[inline]
797 pub fn main_level(&self) -> TextureAnyMipmap<'_> {
798 self.mipmap(0).unwrap()
799 }
800
801 #[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 #[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 fbo::FramebuffersContainer::purge_texture(&mut ctxt, self.id);
913
914 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#[derive(Copy, Clone)]
929pub struct TextureAnyLayer<'a> {
930 texture: &'a TextureAny,
932 layer: u32,
934}
935
936impl<'a> TextureAnyLayer<'a> {
937 #[inline]
939 pub fn get_texture(&self) -> &'a TextureAny {
940 self.texture
941 }
942
943 #[inline]
945 pub fn get_samples(&self) -> Option<u32> {
946 self.texture.get_samples()
947 }
948
949 #[inline]
951 pub fn get_layer(&self) -> u32 {
952 self.layer
953 }
954
955 #[inline]
957 pub fn main_level(&self) -> TextureAnyLayerMipmap<'a> {
958 self.mipmap(0).unwrap()
959 }
960
961 #[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#[derive(Copy, Clone)]
984pub struct TextureAnyMipmap<'a> {
985 texture: &'a TextureAny,
987
988 level: u32,
990
991 width: u32,
993 height: Option<u32>,
995 depth: Option<u32>,
997}
998
999impl<'a> TextureAnyMipmap<'a> {
1000 #[inline]
1002 pub fn get_width(&self) -> u32 {
1003 self.width
1004 }
1005
1006 #[inline]
1008 pub fn get_height(&self) -> Option<u32> {
1009 self.height
1010 }
1011
1012 #[inline]
1014 pub fn get_depth(&self) -> Option<u32> {
1015 self.depth
1016 }
1017
1018 #[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 #[inline]
1038 pub fn get_samples(&self) -> Option<u32> {
1039 self.texture.get_samples()
1040 }
1041
1042 #[inline]
1044 pub fn get_texture(&self) -> &'a TextureAny {
1045 self.texture
1046 }
1047
1048 #[inline]
1050 pub fn get_level(&self) -> u32 {
1051 self.level
1052 }
1053
1054 #[inline]
1057 pub fn first_layer(&self) -> TextureAnyLayerMipmap<'a> {
1058 self.layer(0).unwrap()
1059 }
1060
1061 #[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 #[inline]
1096 pub fn get_array_size(&self) -> Option<u32> {
1097 self.texture.get_array_size()
1098 }
1099
1100 #[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 #[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 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"); },
1290 }
1291
1292 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<(), ()> 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); 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 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 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 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#[derive(Copy, Clone)]
1445pub struct TextureAnyLayerMipmap<'a> {
1446 texture: &'a TextureAny,
1448
1449 layer: u32,
1451 level: u32,
1453
1454 width: u32,
1456 height: Option<u32>,
1458}
1459
1460impl<'a> TextureAnyLayerMipmap<'a> {
1461 #[inline]
1463 pub fn get_texture(&self) -> &'a TextureAny {
1464 self.texture
1465 }
1466
1467 #[inline]
1469 pub fn get_level(&self) -> u32 {
1470 self.level
1471 }
1472
1473 #[inline]
1475 pub fn get_layer(&self) -> u32 {
1476 self.layer
1477 }
1478
1479 #[inline]
1481 pub fn get_width(&self) -> u32 {
1482 self.width
1483 }
1484
1485 #[inline]
1487 pub fn get_height(&self) -> Option<u32> {
1488 self.height
1489 }
1490
1491 #[inline]
1493 pub fn get_samples(&self) -> Option<u32> {
1494 self.texture.get_samples()
1495 }
1496
1497 #[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#[derive(Copy, Clone)]
1525pub struct TextureAnyImage<'a> {
1526 texture: &'a TextureAny,
1528
1529 layer: u32,
1531 level: u32,
1533 cube_layer: Option<CubeLayer>,
1535
1536 width: u32,
1538 height: Option<u32>,
1540}
1541
1542impl<'a> TextureAnyImage<'a> {
1543 #[inline]
1545 pub fn get_texture(&self) -> &'a TextureAny {
1546 self.texture
1547 }
1548
1549 #[inline]
1551 pub fn get_level(&self) -> u32 {
1552 self.level
1553 }
1554
1555 #[inline]
1557 pub fn get_layer(&self) -> u32 {
1558 self.layer
1559 }
1560
1561 #[inline]
1563 pub fn get_cubemap_layer(&self) -> Option<CubeLayer> {
1564 self.cube_layer
1565 }
1566
1567 #[inline]
1569 pub fn get_width(&self) -> u32 {
1570 self.width
1571 }
1572
1573 #[inline]
1575 pub fn get_height(&self) -> Option<u32> {
1576 self.height
1577 }
1578
1579 #[inline]
1581 pub fn get_samples(&self) -> Option<u32> {
1582 self.texture.get_samples()
1583 }
1584
1585 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 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 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}