librashader_runtime_wgpu/
texture.rs

1use crate::error::FilterChainError;
2use crate::mipmap::MipmapGen;
3use crate::WgpuOutputView;
4use librashader_common::{FilterMode, GetSize, ImageFormat, Size, WrapMode};
5use librashader_presets::Scale2D;
6use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
7use wgpu::TextureFormat;
8
9pub struct OwnedImage {
10    pub image: wgpu::Texture,
11    pub view: wgpu::TextureView,
12    pub max_miplevels: u32,
13    pub levels: u32,
14    pub size: Size<u32>,
15}
16
17#[derive(Clone)]
18pub struct InputImage {
19    pub image: wgpu::Texture,
20    pub view: wgpu::TextureView,
21    pub wrap_mode: WrapMode,
22    pub filter_mode: FilterMode,
23    pub mip_filter: FilterMode,
24}
25
26impl AsRef<InputImage> for InputImage {
27    fn as_ref(&self) -> &InputImage {
28        &self
29    }
30}
31
32impl OwnedImage {
33    pub fn new(
34        device: &wgpu::Device,
35        size: Size<u32>,
36        max_miplevels: u32,
37        format: TextureFormat,
38    ) -> Self {
39        let texture = device.create_texture(&wgpu::TextureDescriptor {
40            label: None,
41            size: size.into(),
42            mip_level_count: std::cmp::min(max_miplevels, size.calculate_miplevels()),
43            sample_count: 1,
44            dimension: wgpu::TextureDimension::D2,
45            format,
46            usage: wgpu::TextureUsages::TEXTURE_BINDING
47                | wgpu::TextureUsages::RENDER_ATTACHMENT
48                | wgpu::TextureUsages::COPY_DST
49                | wgpu::TextureUsages::COPY_SRC,
50            view_formats: &[format.into()],
51        });
52
53        let view = texture.create_view(&wgpu::TextureViewDescriptor {
54            label: None,
55            format: Some(format),
56            dimension: Some(wgpu::TextureViewDimension::D2),
57            usage: None,
58            aspect: wgpu::TextureAspect::All,
59            base_mip_level: 0,
60            mip_level_count: None,
61            base_array_layer: 0,
62            array_layer_count: None,
63        });
64
65        Self {
66            image: texture,
67            view,
68            max_miplevels,
69            levels: std::cmp::min(max_miplevels, size.calculate_miplevels()),
70            size,
71        }
72    }
73
74    pub fn scale(
75        &mut self,
76        device: &wgpu::Device,
77        scaling: Scale2D,
78        format: TextureFormat,
79        viewport_size: &Size<u32>,
80        source_size: &Size<u32>,
81        original_size: &Size<u32>,
82        mipmap: bool,
83    ) -> Size<u32> {
84        let size = source_size.scale_viewport(
85            scaling,
86            *viewport_size,
87            *original_size,
88            Some(device.limits().max_texture_dimension_2d),
89        );
90
91        if self.size != size
92            || (mipmap && self.max_miplevels == 1)
93            || (!mipmap && self.max_miplevels != 1)
94            || format != self.image.format()
95        {
96            let mut new = OwnedImage::new(device, size, self.max_miplevels, format.into());
97            std::mem::swap(self, &mut new);
98        }
99        size
100    }
101
102    pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> InputImage {
103        InputImage {
104            image: self.image.clone(),
105            view: self.view.clone(),
106            wrap_mode,
107            filter_mode: filter,
108            mip_filter: filter,
109        }
110    }
111
112    pub fn copy_from(
113        &mut self,
114        device: &wgpu::Device,
115        cmd: &mut wgpu::CommandEncoder,
116        source: &wgpu::Texture,
117    ) {
118        let source_size = source.size().into();
119        if source.format() != self.image.format() || self.size != source_size {
120            let mut new = OwnedImage::new(device, source_size, self.max_miplevels, source.format());
121            std::mem::swap(self, &mut new);
122        }
123
124        cmd.copy_texture_to_texture(
125            source.as_image_copy(),
126            self.image.as_image_copy(),
127            source.size(),
128        )
129    }
130
131    pub fn clear(&self, cmd: &mut wgpu::CommandEncoder) {
132        cmd.clear_texture(&self.image, &wgpu::ImageSubresourceRange::default());
133    }
134
135    pub fn generate_mipmaps(
136        &self,
137        device: &wgpu::Device,
138        cmd: &mut wgpu::CommandEncoder,
139        mipmapper: &mut MipmapGen,
140        sampler: &wgpu::Sampler,
141    ) {
142        mipmapper.generate_mipmaps(device, cmd, &self.image, sampler, self.max_miplevels);
143    }
144}
145
146impl ScaleFramebuffer for OwnedImage {
147    type Error = FilterChainError;
148    type Context = wgpu::Device;
149
150    fn scale(
151        &mut self,
152        scaling: Scale2D,
153        format: ImageFormat,
154        viewport_size: &Size<u32>,
155        source_size: &Size<u32>,
156        original_size: &Size<u32>,
157        should_mipmap: bool,
158        device: &Self::Context,
159    ) -> Result<Size<u32>, Self::Error> {
160        let format: Option<wgpu::TextureFormat> = format.into();
161        let format = format.unwrap_or(TextureFormat::Bgra8Unorm);
162        Ok(self.scale(
163            device,
164            scaling,
165            format,
166            viewport_size,
167            source_size,
168            original_size,
169            should_mipmap,
170        ))
171    }
172}
173
174impl GetSize<u32> for WgpuOutputView<'_> {
175    type Error = std::convert::Infallible;
176
177    fn size(&self) -> Result<Size<u32>, Self::Error> {
178        Ok(self.size)
179    }
180}