fyrox_graphics/gpu_texture.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Texture is an image that used to fill faces to add details to them. It could also be used as a
22//! generic and mostly unlimited capacity storage for arbitrary data.
23
24#![warn(missing_docs)]
25
26use crate::{define_shared_wrapper, error::FrameworkError};
27use fyrox_core::define_as_any_trait;
28
29/// A kind of GPU texture.
30#[derive(Copy, Clone)]
31pub enum GpuTextureKind {
32 /// 1D texture.
33 Line {
34 /// Length of the texture.
35 length: usize,
36 },
37 /// 2D texture.
38 Rectangle {
39 /// Width of the texture.
40 width: usize,
41 /// Height of the texture.
42 height: usize,
43 },
44 /// Six 2D textures forming a cube.
45 Cube {
46 /// Size of the texture.
47 size: usize,
48 },
49 /// Volumetric texture that consists of `depth` textures with `width x height` size.
50 Volume {
51 /// Width of the texture.
52 width: usize,
53 /// Height of the texture.
54 height: usize,
55 /// Depth of the texture.
56 depth: usize,
57 },
58}
59
60/// Pixel kind of GPU texture.
61#[derive(Copy, Clone, Debug, PartialEq)]
62pub enum PixelKind {
63 /// Floating point 32-bit pixel.
64 R32F,
65 /// Unsigned integer 32-bit pixel.
66 R32UI,
67 /// Floating point 16-bit pixel.
68 R16F,
69 /// Floating point 32-bit depth pixel.
70 D32F,
71 /// Integer 16-bit depth pixel.
72 D16,
73 /// Integer 24-bit depth pixel + 8-bit stencil.
74 D24S8,
75 /// Red, Green, Blue, Alpha; all by 8-bit.
76 RGBA8,
77 /// Red, Green, Blue, Alpha in sRGB color space; all by 8-bit.
78 SRGBA8,
79 /// Red, Green, Blue; all by 8-bit.
80 RGB8,
81 /// Red, Green, Blue in sRGB color space; all by 8-bit.
82 SRGB8,
83 /// Blue, Green, Red, Alpha; all by 8-bit.
84 BGRA8,
85 /// Blue, Green, Red; all by 8-bit.
86 BGR8,
87 /// Red, Green; all by 8-bit.
88 RG8,
89 /// Luminance, Alpha; all by 8-bit.
90 LA8,
91 /// Luminance, Alpha; all by 16-bit.
92 LA16,
93 /// Red, Green; all by 16-bit.
94 RG16,
95 /// Red, Green; all by 16-bit floating point.
96 RG16F,
97 /// Red, Green; 16-bit.
98 R8,
99 /// Luminance; 8-bit.
100 L8,
101 /// Luminance; 16-bit.
102 L16,
103 /// Red, unsigned integer; 8-bit.
104 R8UI,
105 /// Red, signed integer; 16-bit.
106 R16,
107 /// Red, Green, Blue; all by 16-bit.
108 RGB16,
109 /// Red, Green, Blue, Alpha; all by 8-bit.
110 RGBA16,
111 /// Compressed S3TC DXT1 RGB.
112 DXT1RGB,
113 /// Compressed S3TC DXT1 RGBA.
114 DXT1RGBA,
115 /// Compressed S3TC DXT3 RGBA.
116 DXT3RGBA,
117 /// Compressed S3TC DXT5 RGBA.
118 DXT5RGBA,
119 /// Floating-point RGB texture with 32-bit depth.
120 RGB32F,
121 /// Floating-point RGBA texture with 32-bit depth.
122 RGBA32F,
123 /// Floating-point RGB texture with 16-bit depth.
124 RGB16F,
125 /// Floating-point RGBA texture with 16-bit depth.
126 RGBA16F,
127 /// Compressed R8 texture (RGTC).
128 R8RGTC,
129 /// Compressed RG8 texture (RGTC).
130 RG8RGTC,
131 /// Floating-point RGB texture with 11-bit for Red and Green channels, 10-bit for Blue channel.
132 R11G11B10F,
133 /// Red, Green, Blue (8-bit) + Alpha (2-bit).
134 RGB10A2,
135}
136
137/// Element kind of pixel.
138pub enum PixelElementKind {
139 /// Floating-point pixel.
140 Float,
141 /// Normalized unsigned integer.
142 NormalizedUnsignedInteger,
143 /// Integer.
144 Integer,
145 /// Unsigned integer.
146 UnsignedInteger,
147}
148
149impl PixelKind {
150 #[doc(hidden)]
151 pub fn unpack_alignment(self) -> Option<i32> {
152 match self {
153 Self::RGBA16
154 | Self::RGBA16F
155 | Self::RGB16
156 | Self::RGB16F
157 | Self::RGBA32F
158 | Self::RGB32F
159 | Self::RGBA8
160 | Self::SRGBA8
161 | Self::BGRA8
162 | Self::RG16
163 | Self::RG16F
164 | Self::LA16
165 | Self::D24S8
166 | Self::D32F
167 | Self::R32F
168 | Self::R32UI
169 | Self::RGB10A2 => Some(4),
170 Self::RG8 | Self::LA8 | Self::D16 | Self::R16F | Self::L16 | Self::R16 => Some(2),
171 Self::R8
172 | Self::L8
173 | Self::R8UI
174 | Self::SRGB8
175 | Self::RGB8
176 | Self::BGR8
177 | Self::R11G11B10F => Some(1),
178 Self::DXT1RGB
179 | Self::DXT1RGBA
180 | Self::DXT3RGBA
181 | Self::DXT5RGBA
182 | Self::R8RGTC
183 | Self::RG8RGTC => None,
184 }
185 }
186
187 /// Returns `true` if the pixel kind is compressed, `false` - otherwise.
188 pub fn is_compressed(self) -> bool {
189 match self {
190 Self::DXT1RGB
191 | Self::DXT1RGBA
192 | Self::DXT3RGBA
193 | Self::DXT5RGBA
194 | Self::R8RGTC
195 | Self::RG8RGTC => true,
196 // Explicit match for rest of formats instead of _ will help to not forget
197 // to add new entry here.
198 Self::RGBA16
199 | Self::RGBA16F
200 | Self::RGB16
201 | Self::RGB16F
202 | Self::RGBA8
203 | Self::SRGBA8
204 | Self::RGB8
205 | Self::SRGB8
206 | Self::BGRA8
207 | Self::BGR8
208 | Self::RG16
209 | Self::RG16F
210 | Self::R16
211 | Self::D24S8
212 | Self::D32F
213 | Self::R32F
214 | Self::R32UI
215 | Self::RG8
216 | Self::D16
217 | Self::R16F
218 | Self::R8
219 | Self::R8UI
220 | Self::RGB32F
221 | Self::RGBA32F
222 | Self::R11G11B10F
223 | Self::RGB10A2
224 | Self::L8
225 | Self::LA8
226 | Self::L16
227 | Self::LA16 => false,
228 }
229 }
230
231 /// Returns element kind of the pixel.
232 pub fn element_kind(self) -> PixelElementKind {
233 match self {
234 Self::R32F
235 | Self::R16F
236 | Self::RG16F
237 | Self::RGB32F
238 | Self::RGBA32F
239 | Self::RGBA16F
240 | Self::RGB16F
241 | Self::D32F
242 | Self::R11G11B10F => PixelElementKind::Float,
243 Self::D16
244 | Self::D24S8
245 | Self::RGBA8
246 | Self::SRGBA8
247 | Self::RGB8
248 | Self::SRGB8
249 | Self::BGRA8
250 | Self::BGR8
251 | Self::RG8
252 | Self::RG16
253 | Self::R8
254 | Self::R16
255 | Self::RGB16
256 | Self::RGBA16
257 | Self::DXT1RGB
258 | Self::DXT1RGBA
259 | Self::DXT3RGBA
260 | Self::DXT5RGBA
261 | Self::R8RGTC
262 | Self::RG8RGTC
263 | Self::RGB10A2
264 | Self::LA8
265 | Self::L8
266 | Self::LA16
267 | Self::L16 => PixelElementKind::NormalizedUnsignedInteger,
268 Self::R8UI | Self::R32UI => PixelElementKind::UnsignedInteger,
269 }
270 }
271}
272
273fn ceil_div_4(x: usize) -> usize {
274 x.div_ceil(4)
275}
276
277/// Calculates size in bytes of a volume texture using the given size of the texture and its pixel
278/// kind.
279pub fn image_3d_size_bytes(
280 pixel_kind: PixelKind,
281 width: usize,
282 height: usize,
283 depth: usize,
284) -> usize {
285 let pixel_count = width * height * depth;
286 match pixel_kind {
287 PixelKind::RGBA32F => 16 * pixel_count,
288 PixelKind::RGB32F => 12 * pixel_count,
289 PixelKind::RGBA16 | PixelKind::RGBA16F => 8 * pixel_count,
290 PixelKind::RGB16 | PixelKind::RGB16F => 6 * pixel_count,
291 PixelKind::RGBA8
292 | PixelKind::SRGBA8
293 | PixelKind::BGRA8
294 | PixelKind::RG16
295 | PixelKind::RG16F
296 | PixelKind::LA16
297 | PixelKind::D24S8
298 | PixelKind::D32F
299 | PixelKind::R32F
300 | PixelKind::R32UI
301 | PixelKind::R11G11B10F
302 | PixelKind::RGB10A2 => 4 * pixel_count,
303 PixelKind::RGB8 | PixelKind::SRGB8 | PixelKind::BGR8 => 3 * pixel_count,
304 PixelKind::RG8
305 | PixelKind::LA8
306 | PixelKind::R16
307 | PixelKind::L16
308 | PixelKind::D16
309 | PixelKind::R16F => 2 * pixel_count,
310 PixelKind::R8 | PixelKind::L8 | PixelKind::R8UI => pixel_count,
311 PixelKind::DXT1RGB | PixelKind::DXT1RGBA | PixelKind::R8RGTC => {
312 let block_size = 8;
313 ceil_div_4(width) * ceil_div_4(height) * ceil_div_4(depth) * block_size
314 }
315 PixelKind::DXT3RGBA | PixelKind::DXT5RGBA | PixelKind::RG8RGTC => {
316 let block_size = 16;
317 ceil_div_4(width) * ceil_div_4(height) * ceil_div_4(depth) * block_size
318 }
319 }
320}
321
322/// Calculates size in bytes of a rectangular texture using the given size of the texture and its pixel
323/// kind.
324pub fn image_2d_size_bytes(pixel_kind: PixelKind, width: usize, height: usize) -> usize {
325 let pixel_count = width * height;
326 match pixel_kind {
327 PixelKind::RGBA32F => 16 * pixel_count,
328 PixelKind::RGB32F => 12 * pixel_count,
329 PixelKind::RGBA16 | PixelKind::RGBA16F => 8 * pixel_count,
330 PixelKind::RGB16 | PixelKind::RGB16F => 6 * pixel_count,
331 PixelKind::RGBA8
332 | PixelKind::SRGBA8
333 | PixelKind::BGRA8
334 | PixelKind::RG16
335 | PixelKind::RG16F
336 | PixelKind::LA16
337 | PixelKind::D24S8
338 | PixelKind::D32F
339 | PixelKind::R32F
340 | PixelKind::R32UI
341 | PixelKind::R11G11B10F
342 | PixelKind::RGB10A2 => 4 * pixel_count,
343 PixelKind::RGB8 | PixelKind::SRGB8 | PixelKind::BGR8 => 3 * pixel_count,
344 PixelKind::RG8
345 | PixelKind::LA8
346 | PixelKind::R16
347 | PixelKind::L16
348 | PixelKind::D16
349 | PixelKind::R16F => 2 * pixel_count,
350 PixelKind::R8 | PixelKind::L8 | PixelKind::R8UI => pixel_count,
351 PixelKind::DXT1RGB | PixelKind::DXT1RGBA | PixelKind::R8RGTC => {
352 let block_size = 8;
353 ceil_div_4(width) * ceil_div_4(height) * block_size
354 }
355 PixelKind::DXT3RGBA | PixelKind::DXT5RGBA | PixelKind::RG8RGTC => {
356 let block_size = 16;
357 ceil_div_4(width) * ceil_div_4(height) * block_size
358 }
359 }
360}
361
362/// Calculates size in bytes of a linear texture using the given size of the texture and its pixel
363/// kind.
364pub fn image_1d_size_bytes(pixel_kind: PixelKind, length: usize) -> usize {
365 match pixel_kind {
366 PixelKind::RGBA32F => 16 * length,
367 PixelKind::RGB32F => 12 * length,
368 PixelKind::RGBA16 | PixelKind::RGBA16F => 8 * length,
369 PixelKind::RGB16 | PixelKind::RGB16F => 6 * length,
370 PixelKind::RGBA8
371 | PixelKind::SRGBA8
372 | PixelKind::BGRA8
373 | PixelKind::RG16
374 | PixelKind::RG16F
375 | PixelKind::LA16
376 | PixelKind::D24S8
377 | PixelKind::D32F
378 | PixelKind::R32F
379 | PixelKind::R32UI
380 | PixelKind::R11G11B10F
381 | PixelKind::RGB10A2 => 4 * length,
382 PixelKind::RGB8 | PixelKind::SRGB8 | PixelKind::BGR8 => 3 * length,
383 PixelKind::RG8
384 | PixelKind::LA8
385 | PixelKind::L16
386 | PixelKind::R16
387 | PixelKind::D16
388 | PixelKind::R16F => 2 * length,
389 PixelKind::R8 | PixelKind::L8 | PixelKind::R8UI => length,
390 PixelKind::DXT1RGB | PixelKind::DXT1RGBA | PixelKind::R8RGTC => {
391 let block_size = 8;
392 ceil_div_4(length) * block_size
393 }
394 PixelKind::DXT3RGBA | PixelKind::DXT5RGBA | PixelKind::RG8RGTC => {
395 let block_size = 16;
396 ceil_div_4(length) * block_size
397 }
398 }
399}
400
401/// Face of a cube map.
402#[derive(Copy, Clone, Eq, PartialEq, Debug)]
403pub enum CubeMapFace {
404 /// +X face.
405 PositiveX,
406 /// -X face.
407 NegativeX,
408 /// +Y face.
409 PositiveY,
410 /// -Y face.
411 NegativeY,
412 /// +Z face.
413 PositiveZ,
414 /// -Z face.
415 NegativeZ,
416}
417
418/// Descriptor of a texture that is used to request textures from a graphics server.
419pub struct GpuTextureDescriptor<'a> {
420 /// Name of the texture. This name is used only for debug purposes.
421 pub name: &'a str,
422 /// Kind of the texture. See [`GpuTextureKind`] docs for more info.
423 pub kind: GpuTextureKind,
424 /// Pixel kind of the texture. See [`PixelKind`] docs for more info.
425 pub pixel_kind: PixelKind,
426 /// Total number of mips in the texture. Texture data must contain at least this number of
427 /// mips.
428 pub mip_count: usize,
429 /// Optional data of the texture. If present, then the total number of bytes must match the
430 /// required number of bytes defined by the texture kind, pixel kind, mip count.
431 pub data: Option<&'a [u8]>,
432 /// Specifies the index of the lowest defined mipmap level. Keep in mind, that the texture data
433 /// should provide the actual mip map level defined by the provided value, otherwise the
434 /// rendering will be incorrect (probably just black on majority of implementations) and glitchy.
435 pub base_level: usize,
436 /// Sets the index of the highest defined mipmap level. Keep in mind, that the texture data
437 /// should provide the actual mip map level defined by the provided value, otherwise the
438 /// rendering will be incorrect (probably just black on majority of implementations) and glitchy.
439 pub max_level: usize,
440}
441
442impl Default for GpuTextureDescriptor<'_> {
443 // WARNING: Do NOT change these default values. This will affect a lot of places in the engine
444 // and may potentially lead to weird behavior!
445 fn default() -> Self {
446 Self {
447 name: "",
448 kind: GpuTextureKind::Rectangle {
449 width: 1,
450 height: 1,
451 },
452 pixel_kind: PixelKind::RGBA8,
453 mip_count: 1,
454 data: None,
455 base_level: 0,
456 max_level: 1000,
457 }
458 }
459}
460
461define_as_any_trait!(GpuTextureAsAny => GpuTextureTrait);
462
463/// Texture is an image that used to fill faces to add details to them. It could also be used as a
464/// generic and mostly unlimited capacity storage for arbitrary data.
465///
466/// In most cases textures are just 2D images, however there are some exclusions to that - for example
467/// cube maps, that may be used for environment mapping. Fyrox supports 1D, 2D, 3D and Cube textures.
468///
469/// ## Example
470///
471/// ```rust
472/// use fyrox_graphics::{
473/// error::FrameworkError,
474/// gpu_texture::{
475/// GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind,
476/// },
477/// server::GraphicsServer,
478/// };
479/// use std::{cell::RefCell, rc::Rc};
480///
481/// fn create_texture(
482/// server: &dyn GraphicsServer,
483/// ) -> Result<GpuTexture, FrameworkError> {
484/// server.create_texture(GpuTextureDescriptor {
485/// kind: GpuTextureKind::Rectangle {
486/// width: 1,
487/// height: 1,
488/// },
489/// pixel_kind: PixelKind::RGBA8,
490/// mip_count: 1,
491/// // Opaque red pixel.
492/// data: Some(&[255, 0, 0, 255]),
493/// // Take the defaults for the rest of parameters.
494/// ..Default::default()
495/// })
496/// }
497/// ```
498pub trait GpuTextureTrait: GpuTextureAsAny {
499 /// Sets the new data of the texture. This method is also able to change the kind of the texture
500 /// and its pixel kind. Returns the number of bytes occupied by the texture in the video memory.
501 /// If there's no data specified, the texture will still take the space defined by its metrics.
502 fn set_data(
503 &self,
504 kind: GpuTextureKind,
505 pixel_kind: PixelKind,
506 mip_count: usize,
507 data: Option<&[u8]>,
508 ) -> Result<usize, FrameworkError>;
509
510 /// Returns kind of the texture.
511 fn kind(&self) -> GpuTextureKind;
512
513 /// Returns pixel kind of the texture.
514 fn pixel_kind(&self) -> PixelKind;
515}
516
517define_shared_wrapper!(GpuTexture<dyn GpuTextureTrait>);