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(scaling, *viewport_size, *original_size);
85        if self.size != size
86            || (mipmap && self.max_miplevels == 1)
87            || (!mipmap && self.max_miplevels != 1)
88            || format != self.image.format()
89        {
90            let mut new = OwnedImage::new(device, size, self.max_miplevels, format.into());
91            std::mem::swap(self, &mut new);
92        }
93        size
94    }
95
96    pub(crate) fn as_input(&self, filter: FilterMode, wrap_mode: WrapMode) -> InputImage {
97        InputImage {
98            image: self.image.clone(),
99            view: self.view.clone(),
100            wrap_mode,
101            filter_mode: filter,
102            mip_filter: filter,
103        }
104    }
105
106    pub fn copy_from(
107        &mut self,
108        device: &wgpu::Device,
109        cmd: &mut wgpu::CommandEncoder,
110        source: &wgpu::Texture,
111    ) {
112        let source_size = source.size().into();
113        if source.format() != self.image.format() || self.size != source_size {
114            let mut new = OwnedImage::new(device, source_size, self.max_miplevels, source.format());
115            std::mem::swap(self, &mut new);
116        }
117
118        cmd.copy_texture_to_texture(
119            source.as_image_copy(),
120            self.image.as_image_copy(),
121            source.size(),
122        )
123    }
124
125    pub fn clear(&self, cmd: &mut wgpu::CommandEncoder) {
126        cmd.clear_texture(&self.image, &wgpu::ImageSubresourceRange::default());
127    }
128
129    pub fn generate_mipmaps(
130        &self,
131        device: &wgpu::Device,
132        cmd: &mut wgpu::CommandEncoder,
133        mipmapper: &mut MipmapGen,
134        sampler: &wgpu::Sampler,
135    ) {
136        mipmapper.generate_mipmaps(device, cmd, &self.image, sampler, self.max_miplevels);
137    }
138}
139
140impl ScaleFramebuffer for OwnedImage {
141    type Error = FilterChainError;
142    type Context = wgpu::Device;
143
144    fn scale(
145        &mut self,
146        scaling: Scale2D,
147        format: ImageFormat,
148        viewport_size: &Size<u32>,
149        source_size: &Size<u32>,
150        original_size: &Size<u32>,
151        should_mipmap: bool,
152        device: &Self::Context,
153    ) -> Result<Size<u32>, Self::Error> {
154        let format: Option<wgpu::TextureFormat> = format.into();
155        let format = format.unwrap_or(TextureFormat::Bgra8Unorm);
156        Ok(self.scale(
157            device,
158            scaling,
159            format,
160            viewport_size,
161            source_size,
162            original_size,
163            should_mipmap,
164        ))
165    }
166}
167
168impl GetSize<u32> for WgpuOutputView<'_> {
169    type Error = std::convert::Infallible;
170
171    fn size(&self) -> Result<Size<u32>, Self::Error> {
172        Ok(self.size)
173    }
174}