[go: up one dir, main page]

gfx_core/
texture.rs

1// Copyright 2014 The Gfx-rs Developers.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Texture creation and modification.
16//!
17//! "Texture" is an overloaded term. In gfx-rs, a texture consists of two
18//! separate pieces of information: image storage description (which is
19//! immutable for a single texture object), and image data. To actually use a
20//! texture, a "sampler" is needed, which provides a way of accessing the
21//! image data.  Image data consists of an array of "texture elements", or
22//! texels.
23
24use std::error::Error;
25use std::{fmt, cmp, hash};
26use memory::{Bind, Usage};
27use {format, state, target, Resources};
28pub use target::{Layer, Level};
29
30/// Maximum accessible mipmap level of a texture.
31pub const MAX_LEVEL: Level = 15;
32
33/// Untyped texture
34#[derive(Debug)]
35pub struct Raw<R: Resources> {
36    resource: R::Texture,
37    info: Info,
38}
39
40impl<R: Resources> Raw<R> {
41    #[doc(hidden)]
42    pub fn new(resource: R::Texture, info: Info) -> Self {
43        Raw {
44            resource: resource,
45            info: info,
46        }
47    }
48
49    #[doc(hidden)]
50    pub fn resource(&self) -> &R::Texture { &self.resource }
51
52    /// Get texture descriptor
53    pub fn get_info(&self) -> &Info { &self.info }
54}
55
56impl<R: Resources + cmp::PartialEq> cmp::PartialEq for Raw<R> {
57    fn eq(&self, other: &Self) -> bool {
58        self.resource().eq(other.resource())
59    }
60}
61
62impl<R: Resources + cmp::Eq> cmp::Eq for Raw<R> {}
63
64impl<R: Resources + hash::Hash> hash::Hash for Raw<R> {
65    fn hash<H: hash::Hasher>(&self, state: &mut H) {
66        self.resource().hash(state);
67    }
68}
69
70/// Pure texture object creation error.
71#[derive(Clone, Copy, Debug, PartialEq)]
72pub enum CreationError {
73    /// Failed to map a given format to the device.
74    Format(format::SurfaceType, Option<format::ChannelType>),
75    /// The kind doesn't support a particular operation.
76    Kind,
77    /// Failed to map a given multisampled kind to the device.
78    Samples(AaMode),
79    /// Unsupported size in one of the dimensions.
80    Size(Size),
81    /// The given data has a different size than the target texture slice.
82    Data(usize),
83    /// The mentioned usage mode is not supported
84    Usage(Usage),
85    /// The requested mipmap creation parameter is unsupported.
86    Mipmap,
87    /// The requested mipmap level count does not match the provided data.
88    Level(Level),
89}
90
91impl fmt::Display for CreationError {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        match *self {
94            CreationError::Format(surf, chan) => write!(f, "{}: ({:?}, {:?})",
95                                                self.description(), surf, chan),
96            CreationError::Samples(aa) => write!(f, "{}: {:?}", self.description(), aa),
97            CreationError::Size(size) => write!(f, "{}: {}", self.description(), size),
98            CreationError::Data(data) => write!(f, "{}: {}", self.description(), data),
99            CreationError::Usage(usage) => write!(f, "{}: {:?}", self.description(), usage),
100            _ => write!(f, "{}", self.description()),
101        }
102    }
103}
104
105impl Error for CreationError {
106    fn description(&self) -> &str {
107        match *self {
108            CreationError::Format(..) => "Failed to map a given format to the device",
109            CreationError::Kind => "The kind doesn't support a particular operation",
110            CreationError::Samples(_) => "Failed to map a given multisampled kind to the device",
111            CreationError::Size(_) => "Unsupported size in one of the dimensions",
112            CreationError::Data(_) => "The given data has a different size than the target texture slice",
113            CreationError::Usage(_) => "The expected texture usage mode is not supported by a graphic API",
114            CreationError::Mipmap => "The requested mipmap creation parameter is unsupported",
115            CreationError::Level(_) => "The requested mipmap level count does not match the provided data",
116        }
117    }
118}
119
120/// An error associated with selected texture layer.
121#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
122pub enum LayerError {
123    /// The source texture kind doesn't support array slices.
124    NotExpected(Kind),
125    /// Selected layer is outside of the provided range.
126    OutOfBounds(target::Layer, target::Layer),
127}
128
129impl fmt::Display for LayerError {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        match *self {
132            LayerError::NotExpected(kind) => write!(f, "{}: {:?}", self.description(), kind),
133            LayerError::OutOfBounds(layer, count) => write!(f, "{}: {}/{}", self.description(), layer, count),
134        }
135    }
136}
137
138impl Error for LayerError {
139    fn description(&self) -> &str {
140        match *self {
141            LayerError::NotExpected(_) => "The source texture kind doesn't support array slices",
142            LayerError::OutOfBounds(_, _) => "Selected layer is outside of the provided range",
143        }
144    }
145}
146
147/// Dimension size
148pub type Size = u16;
149/// Number of bits per component
150pub type Bits = u8;
151/// Number of MSAA samples
152pub type NumSamples = u8;
153/// Number of EQAA fragments
154pub type NumFragments = u8;
155
156/// Dimensions: width, height, depth, and samples.
157pub type Dimensions = (Size, Size, Size, AaMode);
158
159/// Describes the configuration of samples inside each texel.
160#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
161#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
162pub enum AaMode {
163    /// No additional sample information
164    Single,
165    /// MultiSampled Anti-Aliasing (MSAA)
166    Multi(NumSamples),
167    /// Coverage Sampling Anti-Aliasing (CSAA/EQAA)
168    Coverage(NumSamples, NumFragments),
169}
170
171impl From<NumSamples> for AaMode {
172    fn from(ns: NumSamples) -> AaMode {
173        if ns > 1 {
174            AaMode::Multi(ns)
175        } else {
176            AaMode::Single
177        }
178    }
179}
180
181impl AaMode {
182    /// Return the number of actual data fragments stored per texel.
183    pub fn get_num_fragments(&self) -> NumFragments {
184        match *self {
185            AaMode::Single => 1,
186            AaMode::Multi(n) => n,
187            AaMode::Coverage(_, nf) => nf,
188        }
189    }
190    /// Return true if the surface has to be resolved before sampling.
191    pub fn needs_resolve(&self) -> bool {
192        self.get_num_fragments() > 1
193    }
194}
195
196
197/// How to [filter](https://en.wikipedia.org/wiki/Texture_filtering) the
198/// texture when sampling. They correspond to increasing levels of quality,
199/// but also cost. Mipmap, trilinear and anisotropic filtering require
200/// mipmapping, but the other filtering methods do not.
201///
202/// These names are somewhat poor, in that "bilinear" is really just doing
203/// linear filtering on each axis, and it is only bilinear in the case of 2D
204/// textures. Similarly for trilinear, it is really Quadralinear(?) for 3D
205/// textures. Alas, these names are simple, and match certain intuitions
206/// ingrained by many years of public use of inaccurate terminology.
207#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
208#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
209pub enum FilterMethod {
210    /// The dumbest filtering possible, nearest-neighbor interpolation.
211    Scale,
212    /// Add simple mipmapping.
213    Mipmap,
214    /// Sample multiple texels within a single mipmap level to increase
215    /// quality.
216    Bilinear,
217    /// Sample multiple texels across two mipmap levels to increase quality.
218    Trilinear,
219    /// Anisotropic filtering with a given "max", must be between 1 and 16,
220    /// inclusive.
221    Anisotropic(u8)
222}
223
224/// The face of a cube texture to do an operation on.
225#[allow(missing_docs)]
226#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
227#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
228#[repr(u8)]
229pub enum CubeFace {
230    PosX,
231    NegX,
232    PosY,
233    NegY,
234    PosZ,
235    NegZ,
236}
237
238/// A constant array of cube faces in the order they map to the hardware.
239pub const CUBE_FACES: [CubeFace; 6] = [
240    CubeFace::PosX, CubeFace::NegX,
241    CubeFace::PosY, CubeFace::NegY,
242    CubeFace::PosZ, CubeFace::NegZ,
243];
244
245/// Specifies the kind of a texture storage to be allocated.
246#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
247#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
248pub enum Kind {
249    /// A single row of texels.
250    D1(Size),
251    /// An array of rows of texels. Equivalent to Texture2D except that texels
252    /// in a different row are not sampled.
253    D1Array(Size, Layer),
254    /// A traditional 2D texture, with rows arranged contiguously.
255    D2(Size, Size, AaMode),
256    /// An array of 2D textures. Equivalent to Texture3D except that texels in
257    /// a different depth level are not sampled.
258    D2Array(Size, Size, Layer, AaMode),
259    /// A volume texture, with each 2D layer arranged contiguously.
260    D3(Size, Size, Size),
261    /// A set of 6 2D textures, one for each face of a cube.
262    Cube(Size),
263    /// An array of Cube textures.
264    CubeArray(Size, Layer),
265}
266
267impl Kind {
268    /// Get texture dimensions, with 0 values where not applicable.
269    pub fn get_dimensions(&self) -> Dimensions {
270        let s0 = AaMode::Single;
271        match *self {
272            Kind::D1(w) => (w, 1, 1, s0),
273            Kind::D1Array(w, a) => (w, 1, a as Size, s0),
274            Kind::D2(w, h, s) => (w, h, 1, s),
275            Kind::D2Array(w, h, a, s) => (w, h, a as Size, s),
276            Kind::D3(w, h, d) => (w, h, d, s0),
277            Kind::Cube(w) => (w, w, 6, s0),
278            Kind::CubeArray(w, a) => (w, w, 6 * (a as Size), s0)
279        }
280    }
281    /// Get the dimensionality of a particular mipmap level.
282    pub fn get_level_dimensions(&self, level: Level) -> Dimensions {
283        use std::cmp::{max, min};
284        // unused dimensions must stay 0, all others must be at least 1
285        let map = |val| max(min(val, 1), val >> min(level, MAX_LEVEL));
286        let (w, h, da, _) = self.get_dimensions();
287        (map(w), map(h), map(da), AaMode::Single)
288    }
289    /// Count the number of mipmap levels.
290    pub fn get_num_levels(&self) -> Level {
291        use std::cmp::max;
292        let (w, h, d, aa) = self.get_dimensions();
293        let dominant = max(max(w, h), d);
294        if aa == AaMode::Single {
295            (1..).find(|level| dominant>>level <= 1).unwrap()
296        }else {
297            1 // anti-aliased textures can't have mipmaps
298        }
299    }
300    /// Return the number of slices for an array, or None for non-arrays.
301    pub fn get_num_slices(&self) -> Option<Layer> {
302        match *self {
303            Kind::D1(..) | Kind::D2(..) | Kind::D3(..) | Kind::Cube(..) => None,
304            Kind::D1Array(_, a) => Some(a),
305            Kind::D2Array(_, _, a, _) => Some(a),
306            Kind::CubeArray(_, a) => Some(a),
307        }
308    }
309    /// Check if it's one of the cube kinds.
310    pub fn is_cube(&self) -> bool {
311        match *self {
312            Kind::Cube(_) | Kind::CubeArray(_, _) => true,
313            _ => false,
314        }
315    }
316}
317
318/// The marker for the texture initializer to generate extra space for the mipmap generation.
319#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
320#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
321#[repr(u8)]
322pub enum Mipmap {
323	/// The mipmap data is provided as a part of bitmap data.
324	Provided,
325	/// The mipmap data is not provided, but the memory for it should be allocated
326	/// for the later generation/
327	Allocated, // TODO parameterize mipmap generation here?
328}
329
330/// Describes a subvolume of a texture, which image data can be uploaded into.
331#[allow(missing_docs)]
332#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
333#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
334pub struct ImageInfoCommon<F> {
335    pub xoffset: Size,
336    pub yoffset: Size,
337    pub zoffset: Size,
338    pub width: Size,
339    pub height: Size,
340    pub depth: Size,
341    /// Format of each texel.
342    pub format: F,
343    /// Which mipmap to select.
344    pub mipmap: Level,
345}
346
347/// New raw image info based on the universal format spec.
348pub type RawImageInfo = ImageInfoCommon<format::Format>;
349/// New image info based on the universal format spec.
350/// The format is suppsed to come from compile-time information
351/// as opposed to run-time enum values.
352pub type NewImageInfo = ImageInfoCommon<()>;
353
354impl<F> ImageInfoCommon<F> {
355    /// Get the total number of texels.
356    pub fn get_texel_count(&self) -> usize {
357        use std::cmp::max;
358        max(1, self.width) as usize *
359        max(1, self.height) as usize *
360        max(1, self.depth) as usize
361    }
362
363    /// Convert into a differently typed format.
364    pub fn convert<T>(&self, new_format: T) -> ImageInfoCommon<T> {
365        ImageInfoCommon {
366            xoffset: self.xoffset,
367            yoffset: self.yoffset,
368            zoffset: self.zoffset,
369            width: self.width,
370            height: self.height,
371            depth: self.depth,
372            format: new_format,
373            mipmap: self.mipmap,
374        }
375    }
376
377    /// Check if it fits inside given dimensions.
378    pub fn is_inside(&self, (w, h, d, aa): Dimensions) -> bool {
379        aa == AaMode::Single &&
380        self.xoffset + self.width <= w &&
381        self.yoffset + self.height <= h &&
382        self.zoffset + self.depth <= d
383    }
384}
385
386impl RawImageInfo {
387    /// Get the total number of bytes.
388    pub fn get_byte_count(&self) -> usize {
389        let texel_bytes = self.format.0.get_total_bits() / 8;
390        self.get_texel_count() * (texel_bytes as usize)
391    }
392}
393
394/// A texture region defined for copy operations
395#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
396#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
397pub struct TextureCopyRegion<T> {
398    /// Raw texture
399    pub texture: T,
400    /// Texture kind
401    pub kind: Kind,
402    /// Optional cube face
403    pub cube_face: Option<CubeFace>,
404    /// Dimensions, offsets, and format
405    pub info: RawImageInfo,
406}
407
408impl<T> TextureCopyRegion<T> {
409    /// Change the texture
410    pub fn with_texture<U>(self, texture: U) -> TextureCopyRegion<U> {
411        TextureCopyRegion {
412            texture,
413            kind: self.kind,
414            cube_face: self.cube_face,
415            info: self.info,
416        }
417    }
418}
419
420/// Specifies how texture coordinates outside the range `[0, 1]` are handled.
421#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
422#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
423pub enum WrapMode {
424    /// Tile the texture. That is, sample the coordinate modulo `1.0`. This is
425    /// the default.
426    Tile,
427    /// Mirror the texture. Like tile, but uses abs(coord) before the modulo.
428    Mirror,
429    /// Clamp the texture to the value at `0.0` or `1.0` respectively.
430    Clamp,
431    /// Use border color.
432    Border,
433}
434
435/// A wrapper for the LOD level of a texture.
436#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
437#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
438pub struct Lod(i16);
439
440impl From<f32> for Lod {
441    fn from(v: f32) -> Lod {
442        Lod((v * 8.0) as i16)
443    }
444}
445
446impl Into<f32> for Lod {
447    fn into(self) -> f32 {
448        self.0 as f32 / 8.0
449    }
450}
451
452/// A wrapper for the 8bpp RGBA color, encoded as u32.
453#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
454#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
455pub struct PackedColor(pub u32);
456
457impl From<[f32; 4]> for PackedColor {
458    fn from(c: [f32; 4]) -> PackedColor {
459        PackedColor(c.iter().rev().fold(0, |u, &c| {
460            (u<<8) + (c * 255.0) as u32
461        }))
462    }
463}
464
465impl Into<[f32; 4]> for PackedColor {
466    fn into(self) -> [f32; 4] {
467        let mut out = [0.0; 4];
468        for i in 0 .. 4 {
469            let byte = (self.0 >> (i<<3)) & 0xFF;
470            out[i] = byte as f32 / 255.0;
471        }
472        out
473    }
474}
475
476/// Specifies how to sample from a texture.
477// TODO: document the details of sampling.
478#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
479#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
480pub struct SamplerInfo {
481    /// Filter method to use.
482    pub filter: FilterMethod,
483    /// Wrapping mode for each of the U, V, and W axis (S, T, and R in OpenGL
484    /// speak).
485    pub wrap_mode: (WrapMode, WrapMode, WrapMode),
486    /// This bias is added to every computed mipmap level (N + lod_bias). For
487    /// example, if it would select mipmap level 2 and lod_bias is 1, it will
488    /// use mipmap level 3.
489    #[cfg_attr(feature = "serialize", serde(default = "SamplerInfo::def_lod_bias"))]
490    pub lod_bias: Lod,
491    /// This range is used to clamp LOD level used for sampling.
492    #[cfg_attr(feature = "serialize", serde(default = "SamplerInfo::def_lod_range"))]
493    pub lod_range: (Lod, Lod),
494    /// Comparison mode, used primary for a shadow map.
495    #[cfg_attr(feature = "serialize", serde(default))]
496    pub comparison: Option<state::Comparison>,
497    /// Border color is used when one of the wrap modes is set to border.
498    #[cfg_attr(feature = "serialize", serde(default = "SamplerInfo::def_border"))]
499    pub border: PackedColor,
500}
501
502impl SamplerInfo {
503    /// Create a new sampler description with a given filter method and wrapping mode, using no LOD
504    /// modifications.
505    pub fn new(filter: FilterMethod, wrap: WrapMode) -> SamplerInfo {
506        SamplerInfo {
507            filter: filter,
508            wrap_mode: (wrap, wrap, wrap),
509            lod_bias: Self::def_lod_bias(),
510            lod_range: Self::def_lod_range(),
511            comparison: None,
512            border: Self::def_border(),
513        }
514    }
515
516    fn def_lod_bias() -> Lod {
517        Lod(0)
518    }
519
520    fn def_lod_range() -> (Lod, Lod) {
521        (Lod(-8000), Lod(8000))
522    }
523
524    fn def_border() -> PackedColor {
525        PackedColor(0)
526    }
527}
528
529/// Texture storage descriptor.
530#[allow(missing_docs)]
531#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
532#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
533pub struct Info {
534    pub kind: Kind,
535    pub levels: Level,
536    pub format: format::SurfaceType,
537    pub bind: Bind,
538    pub usage: Usage,
539}
540
541impl Info {
542    /// Get image info for a given mip.
543    pub fn to_image_info(&self, mip: Level) -> NewImageInfo {
544        let (w, h, d, _) = self.kind.get_level_dimensions(mip);
545        ImageInfoCommon {
546            xoffset: 0,
547            yoffset: 0,
548            zoffset: 0,
549            width: w,
550            height: h,
551            depth: d,
552            format: (),
553            mipmap: mip,
554        }
555    }
556
557    /// Get the raw image info for a given mip and a channel type.
558    pub fn to_raw_image_info(&self, cty: format::ChannelType, mip: Level) -> RawImageInfo {
559        let format = format::Format(self.format, cty.into());
560        self.to_image_info(mip).convert(format)
561    }
562}
563
564/// Texture resource view descriptor.
565#[allow(missing_docs)]
566#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
567#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
568pub struct ResourceDesc {
569    pub channel: format::ChannelType,
570    pub layer: Option<Layer>,
571    pub min: Level,
572    pub max: Level,
573    pub swizzle: format::Swizzle,
574}
575
576/// Texture render view descriptor.
577#[allow(missing_docs)]
578#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
579#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
580pub struct RenderDesc {
581    pub channel: format::ChannelType,
582    pub level: Level,
583    pub layer: Option<Layer>,
584}
585
586bitflags!(
587    /// Depth-stencil read-only flags
588    #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
589    pub struct DepthStencilFlags: u8 {
590        /// Depth is read-only in the view.
591        const RO_DEPTH    = 0x1;
592        /// Stencil is read-only in the view.
593        const RO_STENCIL  = 0x2;
594        /// Both depth and stencil are read-only.
595        const RO_DEPTH_STENCIL = 0x3;
596    }
597);
598
599/// Texture depth-stencil view descriptor.
600#[allow(missing_docs)]
601#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
602#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
603pub struct DepthStencilDesc {
604    pub level: Level,
605    pub layer: Option<Layer>,
606    pub flags: DepthStencilFlags,
607}
608
609impl From<RenderDesc> for DepthStencilDesc {
610    fn from(rd: RenderDesc) -> DepthStencilDesc {
611        DepthStencilDesc {
612            level: rd.level,
613            layer: rd.layer,
614            flags: DepthStencilFlags::empty(),
615        }
616    }
617}