rt(wgsl): mipmaps

This commit is contained in:
chyyran 2024-02-06 01:31:53 -05:00 committed by Ronny Chan
parent 11ab4b7c9a
commit 962a81c2e3
5 changed files with 203 additions and 17 deletions

View file

@ -0,0 +1,52 @@
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
};
// meant to be called with 3 vertex indices: 0, 1, 2
// draws one large triangle over the clip space like this:
// (the asterisks represent the clip space bounds)
//-1,1 1,1
// ---------------------------------
// | * .
// | * .
// | * .
// | * .
// | * .
// | * .
// |***************
// | . 1,-1
// | .
// | .
// | .
// | .
// |.
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
var result: VertexOutput;
let x = i32(vertex_index) / 2;
let y = i32(vertex_index) & 1;
let tc = vec2<f32>(
f32(x) * 2.0,
f32(y) * 2.0
);
result.position = vec4<f32>(
tc.x * 2.0 - 1.0,
1.0 - tc.y * 2.0,
0.0, 1.0
);
result.tex_coords = tc;
return result;
}
@group(0)
@binding(0)
var r_color: texture_2d<f32>;
@group(0)
@binding(1)
var r_sampler: sampler;
@fragment
fn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(r_color, r_sampler, vertex.tex_coords);
}

View file

@ -15,11 +15,12 @@ mod filter_pass;
mod framebuffer;
mod graphics_pipeline;
mod luts;
mod mipmap;
mod options;
mod samplers;
mod texture;
mod util;
pub use framebuffer::OutputView;
pub use filter_chain::FilterChainWGPU;
pub use filter_pass::FilterPass;
pub use framebuffer::OutputView;

View file

@ -1,10 +1,12 @@
use crate::mipmap::MipmapGen;
use crate::samplers::SamplerSet;
use crate::texture::{Handle, InputImage};
use librashader_common::{Size, WrapMode};
use librashader_presets::TextureConfig;
use librashader_runtime::image::{Image, BGRA8};
use librashader_runtime::scaling::MipmapSize;
use std::sync::Arc;
use wgpu::util::DeviceExt;
use wgpu::{ImageDataLayout, Label, TextureDescriptor};
use wgpu::TextureDescriptor;
pub(crate) struct LutTexture(InputImage);
impl AsRef<InputImage> for LutTexture {
@ -17,9 +19,11 @@ impl LutTexture {
pub fn new(
device: &wgpu::Device,
queue: &wgpu::Queue,
_cmd: &mut wgpu::CommandEncoder,
cmd: &mut wgpu::CommandEncoder,
image: Image,
config: &TextureConfig,
mipmapper: &mut MipmapGen,
sampler_set: &SamplerSet,
) -> LutTexture {
let texture = device.create_texture(&TextureDescriptor {
label: Some(&config.name),
@ -55,19 +59,20 @@ impl LutTexture {
image.size.into(),
);
// todo: mipmaps
if config.mipmap {
mipmapper.generate_mipmaps(
cmd,
&texture,
&*sampler_set.get(
WrapMode::ClampToEdge,
config.filter_mode,
config.filter_mode,
),
Size::<u32>::from(texture.size()).calculate_miplevels(),
);
}
// todo: fix this
let view = texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("lut view"),
format: None,
dimension: None,
aspect: Default::default(),
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let image = InputImage {
image: Arc::new(texture),

View file

@ -0,0 +1,119 @@
use rustc_hash::FxHashMap;
use std::borrow::Cow;
use std::sync::Arc;
pub struct MipmapGen {
device: Arc<wgpu::Device>,
shader: wgpu::ShaderModule,
pipeline_cache: FxHashMap<wgpu::TextureFormat, wgpu::RenderPipeline>,
}
impl MipmapGen {
fn create_pipeline(
device: &wgpu::Device,
shader: &wgpu::ShaderModule,
format: wgpu::TextureFormat,
) -> wgpu::RenderPipeline {
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("blit"),
layout: None,
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(format.into())],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
pipeline
}
pub fn new(device: Arc<wgpu::Device>) -> Self {
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shader/blit.wgsl"))),
});
Self {
device,
shader,
pipeline_cache: Default::default(),
}
}
pub fn generate_mipmaps(
&mut self,
cmd: &mut wgpu::CommandEncoder,
texture: &wgpu::Texture,
sampler: &wgpu::Sampler,
miplevels: u32,
) {
let format = texture.format();
let pipeline = &*self
.pipeline_cache
.entry(format)
.or_insert_with(|| Self::create_pipeline(&self.device, &self.shader, format));
let views = (0..miplevels)
.map(|mip| {
texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("mip"),
format: None,
dimension: None,
aspect: wgpu::TextureAspect::All,
base_mip_level: mip,
mip_level_count: Some(1),
base_array_layer: 0,
array_layer_count: None,
})
})
.collect::<Vec<_>>();
for target_mip in 1..miplevels as usize {
let bind_group_layout = pipeline.get_bind_group_layout(0);
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&views[target_mip - 1]),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
},
],
label: None,
});
let mut pass = cmd.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &views[target_mip],
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&pipeline);
pass.set_bind_group(0, &bind_group, &[]);
pass.draw(0..3, 0..1);
}
}
}

View file

@ -1,4 +1,6 @@
use crate::error::FilterChainError;
use crate::mipmap::MipmapGen;
use crate::samplers::SamplerSet;
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
@ -149,7 +151,14 @@ impl OwnedImage {
pub fn clear(&self, cmd: &mut wgpu::CommandEncoder) {
cmd.clear_texture(&self.image, &wgpu::ImageSubresourceRange::default());
}
pub fn generate_mipmaps(&self, cmd: &mut wgpu::CommandEncoder) {}
pub fn generate_mipmaps(
&self,
cmd: &mut wgpu::CommandEncoder,
mipmapper: &mut MipmapGen,
sampler: &wgpu::Sampler,
) {
mipmapper.generate_mipmaps(cmd, &self.image, sampler, self.max_miplevels);
}
}
impl ScaleFramebuffer for OwnedImage {