Skip to main content

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>);