use crate::{
backend::texture::Texture as TextureBackend,
context::GraphicsContext,
depth_stencil::Comparison,
pixel::{Pixel, PixelFormat},
};
use std::{error, fmt, marker::PhantomData};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Wrap {
ClampToEdge,
Repeat,
MirroredRepeat,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MinFilter {
Nearest,
Linear,
NearestMipmapNearest,
NearestMipmapLinear,
LinearMipmapNearest,
LinearMipmapLinear,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MagFilter {
Nearest,
Linear,
}
pub trait Dimensionable {
type Size: Copy;
type Offset: Copy;
const ZERO_OFFSET: Self::Offset;
fn dim() -> Dim;
fn width(size: Self::Size) -> u32;
fn height(_: Self::Size) -> u32 {
1
}
fn depth(_: Self::Size) -> u32 {
1
}
fn x_offset(offset: Self::Offset) -> u32;
fn y_offset(_: Self::Offset) -> u32 {
1
}
fn z_offset(_: Self::Offset) -> u32 {
1
}
fn count(size: Self::Size) -> usize;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Dim {
Dim1,
Dim2,
Dim3,
Cubemap,
Dim1Array,
Dim2Array,
}
impl fmt::Display for Dim {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Dim::Dim1 => f.write_str("1D"),
Dim::Dim2 => f.write_str("2D"),
Dim::Dim3 => f.write_str("3D"),
Dim::Cubemap => f.write_str("cubemap"),
Dim::Dim1Array => f.write_str("1D array"),
Dim::Dim2Array => f.write_str("2D array"),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Dim1;
impl Dimensionable for Dim1 {
type Offset = u32;
type Size = u32;
const ZERO_OFFSET: Self::Offset = 0;
fn dim() -> Dim {
Dim::Dim1
}
fn width(w: Self::Size) -> u32 {
w
}
fn x_offset(off: Self::Offset) -> u32 {
off
}
fn count(size: Self::Size) -> usize {
size as usize
}
}
#[derive(Clone, Copy, Debug)]
pub struct Dim2;
impl Dimensionable for Dim2 {
type Offset = [u32; 2];
type Size = [u32; 2];
const ZERO_OFFSET: Self::Offset = [0, 0];
fn dim() -> Dim {
Dim::Dim2
}
fn width(size: Self::Size) -> u32 {
size[0]
}
fn height(size: Self::Size) -> u32 {
size[1]
}
fn x_offset(off: Self::Offset) -> u32 {
off[0]
}
fn y_offset(off: Self::Offset) -> u32 {
off[1]
}
fn count([width, height]: Self::Size) -> usize {
width as usize * height as usize
}
}
#[derive(Clone, Copy, Debug)]
pub struct Dim3;
impl Dimensionable for Dim3 {
type Offset = [u32; 3];
type Size = [u32; 3];
const ZERO_OFFSET: Self::Offset = [0, 0, 0];
fn dim() -> Dim {
Dim::Dim3
}
fn width(size: Self::Size) -> u32 {
size[0]
}
fn height(size: Self::Size) -> u32 {
size[1]
}
fn depth(size: Self::Size) -> u32 {
size[2]
}
fn x_offset(off: Self::Offset) -> u32 {
off[0]
}
fn y_offset(off: Self::Offset) -> u32 {
off[1]
}
fn z_offset(off: Self::Offset) -> u32 {
off[2]
}
fn count([width, height, depth]: Self::Size) -> usize {
width as usize * height as usize * depth as usize
}
}
#[derive(Clone, Copy, Debug)]
pub struct Cubemap;
impl Dimensionable for Cubemap {
type Offset = ([u32; 2], CubeFace);
type Size = u32;
const ZERO_OFFSET: Self::Offset = ([0, 0], CubeFace::PositiveX);
fn dim() -> Dim {
Dim::Cubemap
}
fn width(s: Self::Size) -> u32 {
s
}
fn height(s: Self::Size) -> u32 {
s
}
fn depth(_: Self::Size) -> u32 {
6
}
fn x_offset(off: Self::Offset) -> u32 {
off.0[0]
}
fn y_offset(off: Self::Offset) -> u32 {
off.0[1]
}
fn z_offset(off: Self::Offset) -> u32 {
match off.1 {
CubeFace::PositiveX => 0,
CubeFace::NegativeX => 1,
CubeFace::PositiveY => 2,
CubeFace::NegativeY => 3,
CubeFace::PositiveZ => 4,
CubeFace::NegativeZ => 5,
}
}
fn count(size: Self::Size) -> usize {
let size = size as usize;
size * size
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CubeFace {
PositiveX,
NegativeX,
PositiveY,
NegativeY,
PositiveZ,
NegativeZ,
}
#[derive(Clone, Copy, Debug)]
pub struct Dim1Array;
impl Dimensionable for Dim1Array {
type Offset = (u32, u32);
type Size = (u32, u32);
const ZERO_OFFSET: Self::Offset = (0, 0);
fn dim() -> Dim {
Dim::Dim1Array
}
fn width(size: Self::Size) -> u32 {
size.0
}
fn height(size: Self::Size) -> u32 {
size.1
}
fn x_offset(off: Self::Offset) -> u32 {
off.0
}
fn y_offset(off: Self::Offset) -> u32 {
off.1
}
fn count((width, layer): Self::Size) -> usize {
width as usize * layer as usize
}
}
#[derive(Clone, Copy, Debug)]
pub struct Dim2Array;
impl Dimensionable for Dim2Array {
type Offset = ([u32; 2], u32);
type Size = ([u32; 2], u32);
const ZERO_OFFSET: Self::Offset = ([0, 0], 0);
fn dim() -> Dim {
Dim::Dim2Array
}
fn width(size: Self::Size) -> u32 {
size.0[0]
}
fn height(size: Self::Size) -> u32 {
size.0[1]
}
fn depth(size: Self::Size) -> u32 {
size.1
}
fn x_offset(off: Self::Offset) -> u32 {
off.0[0]
}
fn y_offset(off: Self::Offset) -> u32 {
off.0[1]
}
fn z_offset(off: Self::Offset) -> u32 {
off.1
}
fn count(([width, height], layer): Self::Size) -> usize {
width as usize * height as usize * layer as usize
}
}
#[derive(Clone, Copy, Debug)]
pub struct Sampler {
pub wrap_r: Wrap,
pub wrap_s: Wrap,
pub wrap_t: Wrap,
pub min_filter: MinFilter,
pub mag_filter: MagFilter,
pub depth_comparison: Option<Comparison>,
}
impl Default for Sampler {
fn default() -> Self {
Sampler {
wrap_r: Wrap::ClampToEdge,
wrap_s: Wrap::ClampToEdge,
wrap_t: Wrap::ClampToEdge,
min_filter: MinFilter::NearestMipmapLinear,
mag_filter: MagFilter::Linear,
depth_comparison: None,
}
}
}
#[derive(Debug)]
pub enum TexelUpload<'a, T>
where
T: ?Sized,
{
BaseLevel {
texels: &'a T,
mipmaps: usize,
},
Levels(&'a [&'a T]),
Reserve {
mipmaps: usize,
},
}
impl<'a, T> TexelUpload<'a, T>
where
T: ?Sized,
{
pub fn base_level(texels: &'a T, mipmaps: usize) -> Self {
Self::BaseLevel { texels, mipmaps }
}
pub fn reserve(mipmaps: usize) -> Self {
Self::Reserve { mipmaps }
}
pub fn levels(texels: &'a [&'a T]) -> Self {
Self::Levels(texels)
}
pub fn mipmaps(&self) -> usize {
match self {
TexelUpload::BaseLevel { mipmaps, .. } => *mipmaps,
TexelUpload::Levels(levels) => levels.len(),
TexelUpload::Reserve { mipmaps } => *mipmaps,
}
}
pub fn get_base_level(&self) -> Option<&'a T> {
match self {
TexelUpload::BaseLevel { texels, .. } => Some(*texels),
TexelUpload::Levels(levels) => levels.get(0).map(|base_level| *base_level),
TexelUpload::Reserve { .. } => None,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TextureError {
TextureStorageCreationFailed(String),
NotEnoughPixels {
expected_bytes: usize,
provided_bytes: usize,
},
UnsupportedPixelFormat(PixelFormat),
CannotRetrieveTexels(String),
CannotUploadTexels(String),
}
impl TextureError {
pub fn texture_storage_creation_failed(reason: impl Into<String>) -> Self {
TextureError::TextureStorageCreationFailed(reason.into())
}
pub fn not_enough_pixels(expected_bytes: usize, provided_bytes: usize) -> Self {
TextureError::NotEnoughPixels {
expected_bytes,
provided_bytes,
}
}
pub fn unsupported_pixel_format(pf: PixelFormat) -> Self {
TextureError::UnsupportedPixelFormat(pf)
}
pub fn cannot_retrieve_texels(reason: impl Into<String>) -> Self {
TextureError::CannotRetrieveTexels(reason.into())
}
pub fn cannot_upload_texels(reason: impl Into<String>) -> Self {
TextureError::CannotUploadTexels(reason.into())
}
}
impl fmt::Display for TextureError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
TextureError::TextureStorageCreationFailed(ref e) => {
write!(f, "texture storage creation failed: {}", e)
}
TextureError::NotEnoughPixels {
ref expected_bytes,
ref provided_bytes,
} => write!(
f,
"not enough texels provided: expected {} bytes, provided {} bytes",
expected_bytes, provided_bytes
),
TextureError::UnsupportedPixelFormat(ref fmt) => {
write!(f, "unsupported pixel format: {:?}", fmt)
}
TextureError::CannotRetrieveTexels(ref e) => {
write!(f, "cannot retrieve texture’s texels: {}", e)
}
TextureError::CannotUploadTexels(ref e) => {
write!(f, "cannot upload texels to texture: {}", e)
}
}
}
}
impl error::Error for TextureError {}
pub struct Texture<B, D, P>
where
B: ?Sized + TextureBackend<D, P>,
D: Dimensionable,
P: Pixel,
{
pub(crate) repr: B::TextureRepr,
size: D::Size,
_phantom: PhantomData<*const P>,
}
impl<B, D, P> Texture<B, D, P>
where
B: ?Sized + TextureBackend<D, P>,
D: Dimensionable,
P: Pixel,
{
pub fn new<C>(
ctx: &mut C,
size: D::Size,
sampler: Sampler,
texels: TexelUpload<[P::Encoding]>,
) -> Result<Self, TextureError>
where
C: GraphicsContext<Backend = B>,
{
unsafe {
ctx
.backend()
.new_texture(size, sampler, texels)
.map(|repr| Texture {
repr,
size,
_phantom: PhantomData,
})
}
}
pub fn new_raw<C>(
ctx: &mut C,
size: D::Size,
sampler: Sampler,
texels: TexelUpload<[P::RawEncoding]>,
) -> Result<Self, TextureError>
where
C: GraphicsContext<Backend = B>,
{
unsafe {
ctx
.backend()
.new_texture_raw(size, sampler, texels)
.map(|repr| Texture {
repr,
size,
_phantom: PhantomData,
})
}
}
pub fn mipmaps(&self) -> usize {
unsafe { B::mipmaps(&self.repr) }
}
pub fn size(&self) -> D::Size {
self.size
}
pub fn resize(
&mut self,
size: D::Size,
texels: TexelUpload<[P::Encoding]>,
) -> Result<(), TextureError> {
self.size = size;
unsafe { B::resize(&mut self.repr, size, texels) }
}
pub fn resize_raw(
&mut self,
size: D::Size,
texels: TexelUpload<[P::RawEncoding]>,
) -> Result<(), TextureError> {
self.size = size;
unsafe { B::resize_raw(&mut self.repr, size, texels) }
}
pub fn upload_part(
&mut self,
offset: D::Offset,
size: D::Size,
texels: TexelUpload<[P::Encoding]>,
) -> Result<(), TextureError> {
unsafe { B::upload_part(&mut self.repr, offset, size, texels) }
}
pub fn upload(&mut self, texels: TexelUpload<[P::Encoding]>) -> Result<(), TextureError> {
unsafe { B::upload(&mut self.repr, self.size, texels) }
}
pub fn upload_part_raw(
&mut self,
offset: D::Offset,
size: D::Size,
texels: TexelUpload<[P::RawEncoding]>,
) -> Result<(), TextureError> {
unsafe { B::upload_part_raw(&mut self.repr, offset, size, texels) }
}
pub fn upload_raw(&mut self, texels: TexelUpload<[P::RawEncoding]>) -> Result<(), TextureError> {
unsafe { B::upload_raw(&mut self.repr, self.size, texels) }
}
pub fn get_raw_texels(&self) -> Result<Vec<P::RawEncoding>, TextureError>
where
P::RawEncoding: Copy + Default,
{
unsafe { B::get_raw_texels(&self.repr, self.size) }
}
}