1use crate::{server::GlGraphicsServer, ToGlConstant};
22use fyrox_graphics::{
23 error::FrameworkError,
24 gpu_texture::{
25 image_1d_size_bytes, image_2d_size_bytes, image_3d_size_bytes, CubeMapFace,
26 GpuTextureDescriptor, GpuTextureKind, GpuTextureTrait, PixelKind,
27 },
28};
29use glow::{HasContext, PixelUnpackData, COMPRESSED_RED_RGTC1, COMPRESSED_RG_RGTC2};
30use std::cell::Cell;
31use std::{
32 marker::PhantomData,
33 rc::{Rc, Weak},
34};
35
36impl ToGlConstant for GpuTextureKind {
37 fn into_gl(self) -> u32 {
38 match self {
39 Self::Line { .. } => glow::TEXTURE_1D,
40 Self::Rectangle { .. } => glow::TEXTURE_2D,
41 Self::Cube { .. } => glow::TEXTURE_CUBE_MAP,
42 Self::Volume { .. } => glow::TEXTURE_3D,
43 }
44 }
45}
46
47impl ToGlConstant for CubeMapFace {
48 fn into_gl(self) -> u32 {
49 match self {
50 Self::PositiveX => glow::TEXTURE_CUBE_MAP_POSITIVE_X,
51 Self::NegativeX => glow::TEXTURE_CUBE_MAP_NEGATIVE_X,
52 Self::PositiveY => glow::TEXTURE_CUBE_MAP_POSITIVE_Y,
53 Self::NegativeY => glow::TEXTURE_CUBE_MAP_NEGATIVE_Y,
54 Self::PositiveZ => glow::TEXTURE_CUBE_MAP_POSITIVE_Z,
55 Self::NegativeZ => glow::TEXTURE_CUBE_MAP_NEGATIVE_Z,
56 }
57 }
58}
59
60pub struct GlTexture {
61 state: Weak<GlGraphicsServer>,
62 texture: glow::Texture,
63 kind: Cell<GpuTextureKind>,
64 pixel_kind: Cell<PixelKind>,
65 size_bytes: Cell<usize>,
66 thread_mark: PhantomData<*const u8>,
68}
69
70const GL_COMPRESSED_RGB_S3TC_DXT1_EXT: u32 = 0x83F0;
71const GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: u32 = 0x83F1;
72const GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: u32 = 0x83F2;
73const GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: u32 = 0x83F3;
74
75pub struct PixelDescriptor {
76 pub data_type: u32,
77 pub format: u32,
78 pub internal_format: u32,
79 pub swizzle_mask: Option<[i32; 4]>,
80}
81
82impl From<PixelKind> for PixelDescriptor {
83 fn from(value: PixelKind) -> Self {
84 let (data_type, format, internal_format, swizzle_mask) = match value {
85 PixelKind::R32F => (glow::FLOAT, glow::RED, glow::R32F, None),
86 PixelKind::R32UI => (glow::UNSIGNED_INT, glow::RED_INTEGER, glow::R32UI, None),
87 PixelKind::R16F => (glow::FLOAT, glow::RED, glow::R16F, None),
88 PixelKind::RG16F => (glow::HALF_FLOAT, glow::RG, glow::RG16F, None),
89 PixelKind::D32F => (
90 glow::FLOAT,
91 glow::DEPTH_COMPONENT,
92 glow::DEPTH_COMPONENT32F,
93 None,
94 ),
95 PixelKind::D16 => (
96 glow::UNSIGNED_SHORT,
97 glow::DEPTH_COMPONENT,
98 glow::DEPTH_COMPONENT16,
99 None,
100 ),
101 PixelKind::D24S8 => (
102 glow::UNSIGNED_INT_24_8,
103 glow::DEPTH_STENCIL,
104 glow::DEPTH24_STENCIL8,
105 None,
106 ),
107 PixelKind::RGBA8 => (glow::UNSIGNED_BYTE, glow::RGBA, glow::RGBA8, None),
108 PixelKind::SRGBA8 => (glow::UNSIGNED_BYTE, glow::RGBA, glow::SRGB8_ALPHA8, None),
109 PixelKind::RGB8 => (glow::UNSIGNED_BYTE, glow::RGB, glow::RGB8, None),
110 PixelKind::SRGB8 => (glow::UNSIGNED_BYTE, glow::RGB, glow::SRGB8, None),
111 PixelKind::RG8 => (glow::UNSIGNED_BYTE, glow::RG, glow::RG8, None),
112 PixelKind::R8 => (glow::UNSIGNED_BYTE, glow::RED, glow::R8, None),
113 PixelKind::R8UI => (glow::UNSIGNED_BYTE, glow::RED_INTEGER, glow::R8UI, None),
114 PixelKind::BGRA8 => (glow::UNSIGNED_BYTE, glow::BGRA, glow::RGBA8, None),
115 PixelKind::BGR8 => (glow::UNSIGNED_BYTE, glow::BGR, glow::RGB8, None),
116 PixelKind::RG16 => (glow::UNSIGNED_SHORT, glow::RG, glow::RG16, None),
117 PixelKind::R16 => (glow::UNSIGNED_SHORT, glow::RED, glow::R16, None),
118 PixelKind::RGB16 => (glow::UNSIGNED_SHORT, glow::RGB, glow::RGB16, None),
119 PixelKind::RGBA16 => (glow::UNSIGNED_SHORT, glow::RGBA, glow::RGBA16, None),
120 PixelKind::RGB10A2 => (
121 glow::UNSIGNED_INT_2_10_10_10_REV,
122 glow::RGBA,
123 glow::RGB10_A2,
124 None,
125 ),
126 PixelKind::DXT1RGB => (0, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, None),
127 PixelKind::DXT1RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, None),
128 PixelKind::DXT3RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, None),
129 PixelKind::DXT5RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, None),
130 PixelKind::R8RGTC => (0, 0, COMPRESSED_RED_RGTC1, None),
131 PixelKind::RG8RGTC => (0, 0, COMPRESSED_RG_RGTC2, None),
132 PixelKind::RGB32F => (glow::FLOAT, glow::RGB, glow::RGB32F, None),
133 PixelKind::RGBA32F => (glow::FLOAT, glow::RGBA, glow::RGBA32F, None),
134 PixelKind::RGBA16F => (glow::HALF_FLOAT, glow::RGBA, glow::RGBA16F, None),
135 PixelKind::RGB16F => (glow::HALF_FLOAT, glow::RGB, glow::RGB16F, None),
136 PixelKind::R11G11B10F => (glow::FLOAT, glow::RGB, glow::R11F_G11F_B10F, None),
137 PixelKind::L8 => (
138 glow::UNSIGNED_BYTE,
139 glow::RED,
140 glow::R8,
141 Some([
142 glow::RED as i32,
143 glow::RED as i32,
144 glow::RED as i32,
145 glow::ONE as i32,
146 ]),
147 ),
148 PixelKind::LA8 => (
149 glow::UNSIGNED_BYTE,
150 glow::RG,
151 glow::RG8,
152 Some([
153 glow::RED as i32,
154 glow::RED as i32,
155 glow::RED as i32,
156 glow::GREEN as i32,
157 ]),
158 ),
159 PixelKind::LA16 => (
160 glow::UNSIGNED_SHORT,
161 glow::RG,
162 glow::RG16,
163 Some([
164 glow::RED as i32,
165 glow::RED as i32,
166 glow::RED as i32,
167 glow::GREEN as i32,
168 ]),
169 ),
170 PixelKind::L16 => (
171 glow::UNSIGNED_SHORT,
172 glow::RED,
173 glow::R16,
174 Some([
175 glow::RED as i32,
176 glow::RED as i32,
177 glow::RED as i32,
178 glow::ONE as i32,
179 ]),
180 ),
181 };
182
183 PixelDescriptor {
184 data_type,
185 format,
186 internal_format,
187 swizzle_mask,
188 }
189 }
190}
191
192struct TempBinding {
193 server: Rc<GlGraphicsServer>,
194 unit: u32,
195 target: u32,
196}
197
198impl TempBinding {
199 fn new(server: Rc<GlGraphicsServer>, texture: &GlTexture) -> Self {
200 let unit = server
201 .free_texture_unit()
202 .expect("Texture units limit exceeded!");
203 let target = texture.kind.get().into_gl();
204 server.set_texture(unit, target, Some(texture.texture));
205 Self {
206 server,
207 unit,
208 target,
209 }
210 }
211
212 fn set_base_level(&mut self, level: usize) {
213 unsafe {
214 self.server
215 .gl
216 .tex_parameter_i32(self.target, glow::TEXTURE_BASE_LEVEL, level as i32);
217 }
218 }
219
220 fn set_max_level(&mut self, level: usize) {
221 unsafe {
222 self.server
223 .gl
224 .tex_parameter_i32(self.target, glow::TEXTURE_MAX_LEVEL, level as i32);
225 }
226 }
227
228 fn generate_mipmap(&mut self, width: usize, height: usize, depth: usize) {
229 unsafe {
230 self.set_base_level(0);
231 self.set_max_level(
232 ((width.max(height).max(depth) as f32).log2().floor() + 1.0) as usize,
233 );
234 self.server.gl.generate_mipmap(self.target);
235 }
236 }
237}
238
239impl Drop for TempBinding {
240 fn drop(&mut self) {
241 self.server.set_texture(self.unit, self.target, None);
242 }
243}
244
245impl GlTexture {
246 pub fn new(
262 server: &GlGraphicsServer,
263 mut desc: GpuTextureDescriptor,
264 ) -> Result<Self, FrameworkError> {
265 let actual_max_level = desc.mip_count.saturating_sub(1);
267 if desc.max_level > actual_max_level {
268 desc.max_level = actual_max_level;
269 }
270 if desc.base_level > desc.max_level {
271 desc.base_level = desc.max_level;
272 }
273 if desc.base_level > actual_max_level {
274 desc.base_level = actual_max_level;
275 }
276
277 unsafe {
278 let texture = server.gl.create_texture()?;
279
280 let result = Self {
281 state: server.weak(),
282 texture,
283 kind: desc.kind.into(),
284 pixel_kind: desc.pixel_kind.into(),
285 size_bytes: Cell::new(0),
286 thread_mark: PhantomData,
287 };
288
289 let byte_count =
290 result.set_data(desc.kind, desc.pixel_kind, desc.mip_count, desc.data)?;
291
292 server.memory_usage.borrow_mut().textures += byte_count;
293
294 let mut binding = result.make_temp_binding();
295 #[cfg(not(target_arch = "wasm32"))]
296 if server.gl.supports_debug() && server.named_objects.get() {
297 server
298 .gl
299 .object_label(glow::TEXTURE, texture.0.get(), Some(desc.name));
300 }
301 binding.set_base_level(desc.base_level);
302 binding.set_max_level(desc.max_level);
303
304 Ok(result)
305 }
306 }
307
308 pub(crate) fn generate_mipmap(&self) {
309 let (width, height, depth) = match self.kind.get() {
310 GpuTextureKind::Line { length } => (length, 1, 1),
311 GpuTextureKind::Rectangle { width, height } => (width, height, 1),
312 GpuTextureKind::Cube { size } => (size, size, 1),
313 GpuTextureKind::Volume {
314 width,
315 height,
316 depth,
317 } => (width, height, depth),
318 };
319 self.make_temp_binding()
320 .generate_mipmap(width, height, depth);
321 }
322
323 pub fn bind(&self, server: &GlGraphicsServer, sampler_index: u32) {
324 server.set_texture(sampler_index, self.kind.get().into_gl(), Some(self.texture));
325 }
326
327 fn make_temp_binding(&self) -> TempBinding {
328 let server = self.state.upgrade().unwrap();
329 TempBinding::new(server, self)
330 }
331
332 pub fn id(&self) -> glow::Texture {
333 self.texture
334 }
335}
336
337impl Drop for GlTexture {
338 fn drop(&mut self) {
339 if let Some(state) = self.state.upgrade() {
340 state.memory_usage.borrow_mut().textures -= self.size_bytes.get();
341 state.delete_texture(self.texture)
342 }
343 }
344}
345
346fn desired_byte_count(kind: GpuTextureKind, mip_count: usize, pixel_kind: PixelKind) -> usize {
347 let mut desired_byte_count = 0;
348
349 'mip_loop: for mip in 0..mip_count {
350 match kind {
351 GpuTextureKind::Line { length } => {
352 if let Some(length) = length.checked_shr(mip as u32) {
353 desired_byte_count += image_1d_size_bytes(pixel_kind, length);
354 } else {
355 break 'mip_loop;
356 }
357 }
358 GpuTextureKind::Rectangle { width, height } => {
359 if let (Some(width), Some(height)) = (
360 width.checked_shr(mip as u32),
361 height.checked_shr(mip as u32),
362 ) {
363 desired_byte_count += image_2d_size_bytes(pixel_kind, width, height);
364 } else {
365 break 'mip_loop;
366 }
367 }
368 GpuTextureKind::Cube { size } => {
369 if let Some(size) = size.checked_shr(mip as u32) {
370 desired_byte_count += 6 * image_2d_size_bytes(pixel_kind, size, size);
371 } else {
372 break 'mip_loop;
373 }
374 }
375 GpuTextureKind::Volume {
376 width,
377 height,
378 depth,
379 } => {
380 if let (Some(width), Some(height), Some(depth)) = (
381 width.checked_shr(mip as u32),
382 height.checked_shr(mip as u32),
383 depth.checked_shr(mip as u32),
384 ) {
385 desired_byte_count += image_3d_size_bytes(pixel_kind, width, height, depth);
386 } else {
387 break 'mip_loop;
388 }
389 }
390 };
391 }
392
393 desired_byte_count
394}
395
396impl GpuTextureTrait for GlTexture {
397 fn set_data(
398 &self,
399 kind: GpuTextureKind,
400 pixel_kind: PixelKind,
401 mip_count: usize,
402 data: Option<&[u8]>,
403 ) -> Result<usize, FrameworkError> {
404 let mip_count = mip_count.max(1);
405
406 let desired_byte_count = desired_byte_count(kind, mip_count, pixel_kind);
407
408 if let Some(data) = data {
409 let actual_data_size = data.len();
410 if actual_data_size != desired_byte_count {
411 return Err(FrameworkError::InvalidTextureData {
412 expected_data_size: desired_byte_count,
413 actual_data_size,
414 });
415 }
416 }
417
418 self.size_bytes.set(desired_byte_count);
419
420 self.kind.set(kind);
421 self.pixel_kind.set(pixel_kind);
422
423 let mut temp_binding = self.make_temp_binding();
424 temp_binding.set_max_level(mip_count.saturating_sub(1));
425 let target = kind.into_gl();
426
427 unsafe {
428 let PixelDescriptor {
429 data_type,
430 format,
431 internal_format,
432 swizzle_mask,
433 } = PixelDescriptor::from(pixel_kind);
434
435 let is_compressed = pixel_kind.is_compressed();
436
437 if let Some(alignment) = pixel_kind.unpack_alignment() {
438 temp_binding
439 .server
440 .gl
441 .pixel_store_i32(glow::UNPACK_ALIGNMENT, alignment);
442 }
443
444 if let Some(swizzle_mask) = swizzle_mask {
445 if temp_binding
446 .server
447 .gl
448 .supported_extensions()
449 .contains("GL_ARB_texture_swizzle")
450 {
451 temp_binding.server.gl.tex_parameter_i32_slice(
452 target,
453 glow::TEXTURE_SWIZZLE_RGBA,
454 &swizzle_mask,
455 );
456 }
457 }
458
459 let mut mip_byte_offset = 0;
460 'mip_loop2: for mip in 0..mip_count {
461 match kind {
462 GpuTextureKind::Line { length } => {
463 if let Some(length) = length.checked_shr(mip as u32) {
464 let size = image_1d_size_bytes(pixel_kind, length) as i32;
465 let pixels = data.map(|data| {
466 &data[mip_byte_offset..(mip_byte_offset + size as usize)]
467 });
468
469 if is_compressed {
470 temp_binding.server.gl.compressed_tex_image_1d(
471 glow::TEXTURE_1D,
472 mip as i32,
473 internal_format as i32,
474 length as i32,
475 0,
476 size,
477 pixels.ok_or(FrameworkError::EmptyTextureData)?,
478 );
479 } else {
480 temp_binding.server.gl.tex_image_1d(
481 glow::TEXTURE_1D,
482 mip as i32,
483 internal_format as i32,
484 length as i32,
485 0,
486 format,
487 data_type,
488 PixelUnpackData::Slice(pixels),
489 );
490 }
491
492 mip_byte_offset += size as usize;
493 } else {
494 break 'mip_loop2;
496 }
497 }
498 GpuTextureKind::Rectangle { width, height } => {
499 if let (Some(width), Some(height)) = (
500 width.checked_shr(mip as u32),
501 height.checked_shr(mip as u32),
502 ) {
503 let size = image_2d_size_bytes(pixel_kind, width, height) as i32;
504 let pixels = data.map(|data| {
505 &data[mip_byte_offset..(mip_byte_offset + size as usize)]
506 });
507
508 if is_compressed {
509 temp_binding.server.gl.compressed_tex_image_2d(
510 glow::TEXTURE_2D,
511 mip as i32,
512 internal_format as i32,
513 width as i32,
514 height as i32,
515 0,
516 size,
517 pixels.ok_or(FrameworkError::EmptyTextureData)?,
518 );
519 } else {
520 temp_binding.server.gl.tex_image_2d(
521 glow::TEXTURE_2D,
522 mip as i32,
523 internal_format as i32,
524 width as i32,
525 height as i32,
526 0,
527 format,
528 data_type,
529 PixelUnpackData::Slice(pixels),
530 );
531 }
532
533 mip_byte_offset += size as usize;
534 } else {
535 break 'mip_loop2;
537 }
538 }
539 GpuTextureKind::Cube { size } => {
540 if let Some(size) = size.checked_shr(mip as u32) {
541 let bytes_per_face = image_2d_size_bytes(pixel_kind, size, size);
542
543 for face in 0..6 {
544 let begin = mip_byte_offset + face * bytes_per_face;
545 let end = mip_byte_offset + (face + 1) * bytes_per_face;
546 let face_pixels = data.map(|data| &data[begin..end]);
547
548 if is_compressed {
549 temp_binding.server.gl.compressed_tex_image_2d(
550 glow::TEXTURE_CUBE_MAP_POSITIVE_X + face as u32,
551 mip as i32,
552 internal_format as i32,
553 size as i32,
554 size as i32,
555 0,
556 bytes_per_face as i32,
557 face_pixels.ok_or(FrameworkError::EmptyTextureData)?,
558 );
559 } else {
560 temp_binding.server.gl.tex_image_2d(
561 glow::TEXTURE_CUBE_MAP_POSITIVE_X + face as u32,
562 mip as i32,
563 internal_format as i32,
564 size as i32,
565 size as i32,
566 0,
567 format,
568 data_type,
569 PixelUnpackData::Slice(face_pixels),
570 );
571 }
572 }
573
574 mip_byte_offset += 6 * bytes_per_face;
575 } else {
576 break 'mip_loop2;
578 }
579 }
580 GpuTextureKind::Volume {
581 width,
582 height,
583 depth,
584 } => {
585 if let (Some(width), Some(height), Some(depth)) = (
586 width.checked_shr(mip as u32),
587 height.checked_shr(mip as u32),
588 depth.checked_shr(mip as u32),
589 ) {
590 let size = image_3d_size_bytes(pixel_kind, width, height, depth) as i32;
591 let pixels = data.map(|data| {
592 &data[mip_byte_offset..(mip_byte_offset + size as usize)]
593 });
594
595 if is_compressed {
596 temp_binding.server.gl.compressed_tex_image_3d(
597 glow::TEXTURE_3D,
598 mip as i32,
599 internal_format as i32,
600 width as i32,
601 height as i32,
602 depth as i32,
603 0,
604 size,
605 pixels.ok_or(FrameworkError::EmptyTextureData)?,
606 );
607 } else {
608 temp_binding.server.gl.tex_image_3d(
609 glow::TEXTURE_3D,
610 mip as i32,
611 internal_format as i32,
612 width as i32,
613 height as i32,
614 depth as i32,
615 0,
616 format,
617 data_type,
618 PixelUnpackData::Slice(pixels),
619 );
620 }
621
622 mip_byte_offset += size as usize;
623 } else {
624 break 'mip_loop2;
626 }
627 }
628 }
629 }
630 }
631
632 Ok(desired_byte_count)
633 }
634
635 fn kind(&self) -> GpuTextureKind {
636 self.kind.get()
637 }
638
639 fn pixel_kind(&self) -> PixelKind {
640 self.pixel_kind.get()
641 }
642}