rt(wgpu): fix enough stuff to get it to draw a frame

This commit is contained in:
chyyran 2024-02-06 00:29:45 -05:00 committed by Ronny Chan
parent e39834547c
commit 31891e414f
12 changed files with 405 additions and 288 deletions

View file

@ -1,26 +1,27 @@
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use parking_lot::RwLock;
pub struct WgpuMappedBuffer {
pub struct WgpuStagedBuffer {
buffer: wgpu::Buffer,
shadow: Box<[u8]>,
}
impl WgpuMappedBuffer {
impl WgpuStagedBuffer {
pub fn new(
device: &Arc<wgpu::Device>,
usage: wgpu::BufferUsages,
size: wgpu::BufferAddress,
label: wgpu::Label<'static>
) -> WgpuMappedBuffer {
label: wgpu::Label<'static>,
) -> WgpuStagedBuffer {
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label,
size,
usage,
mapped_at_creation: true,
usage: usage | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
WgpuMappedBuffer {
WgpuStagedBuffer {
buffer,
shadow: vec![0u8; size as usize].into_boxed_slice(),
}
@ -31,13 +32,12 @@ impl WgpuMappedBuffer {
}
/// Write the contents of the backing buffer to the device buffer.
pub fn flush(&self) {
self.buffer.slice(..)
.get_mapped_range_mut().copy_from_slice(&self.shadow)
pub fn flush(&self, queue: &wgpu::Queue) {
queue.write_buffer(&self.buffer, 0, &self.shadow);
}
}
impl Deref for WgpuMappedBuffer {
impl Deref for WgpuStagedBuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
@ -45,7 +45,7 @@ impl Deref for WgpuMappedBuffer {
}
}
impl DerefMut for WgpuMappedBuffer {
impl DerefMut for WgpuStagedBuffer {
fn deref_mut(&mut self) -> &mut Self::Target {
self.shadow.deref_mut()
}

View file

@ -28,7 +28,7 @@ pub struct DrawQuad {
}
impl DrawQuad {
pub fn new(device: &Device, queue: &mut Queue) -> DrawQuad {
pub fn new(device: &Device) -> DrawQuad {
let buffer = device.create_buffer_init(&BufferInitDescriptor {
label: Some("librashader vbo"),
contents: bytemuck::cast_slice(&VBO_DATA),
@ -38,14 +38,12 @@ impl DrawQuad {
DrawQuad { buffer }
}
pub fn bind_vbo_for_frame<'a, 'b: 'a>(&'b self, cmd: &mut RenderPass<'a>) {
cmd.set_vertex_buffer(0, self.buffer.slice(0..));
}
pub fn draw_quad<'a, 'b: 'a>(&'b self, cmd: &mut RenderPass<'a>, vbo: QuadType) {
cmd.set_vertex_buffer(0, self.buffer.slice(0..));
let offset = match vbo {
QuadType::Offscreen => 0..3,
QuadType::Final => 1..4,
QuadType::Offscreen => 0..4,
QuadType::Final => 4..8,
};
cmd.draw(offset, 0..1)

View file

@ -16,20 +16,22 @@ use std::convert::Infallible;
use std::path::Path;
use std::sync::Arc;
use crate::buffer::WgpuStagedBuffer;
use crate::draw_quad::DrawQuad;
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_reflect::back::wgsl::WgslCompileOptions;
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer;
use rayon::prelude::*;
use wgpu::{BindGroupEntry, CommandBuffer, CommandEncoder, Device, Queue, TextureAspect, TextureFormat};
use crate::buffer::WgpuMappedBuffer;
use crate::draw_quad::DrawQuad;
use wgpu::{
BindGroupEntry, CommandBuffer, CommandEncoder, Device, Queue, TextureAspect, TextureFormat,
};
use crate::error;
use crate::error::FilterChainError;
use crate::filter_pass::FilterPass;
use crate::framebuffer::OutputImage;
use crate::framebuffer::OutputView;
use crate::graphics_pipeline::WgpuGraphicsPipeline;
use crate::luts::LutTexture;
use crate::options::FrameOptionsWGPU;
@ -73,6 +75,7 @@ pub(crate) struct FilterCommon {
pub internal_frame_count: i32,
pub(crate) draw_quad: DrawQuad,
device: Arc<Device>,
pub(crate) queue: Arc<wgpu::Queue>
}
impl FilterChainWGPU {
@ -85,7 +88,7 @@ impl FilterChainWGPU {
/// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame).
pub fn load_from_preset_deferred(
device: Arc<Device>,
queue: &mut wgpu::Queue,
queue: Arc<wgpu::Queue>,
cmd: &mut wgpu::CommandEncoder,
preset: ShaderPreset,
) -> error::Result<FilterChainWGPU> {
@ -94,11 +97,17 @@ impl FilterChainWGPU {
// // initialize passes
let filters = Self::init_passes(Arc::clone(&device), passes, &semantics)?;
//
let luts = FilterChainWGPU::load_luts(&device, queue, cmd, &preset.textures)?;
let luts = FilterChainWGPU::load_luts(&device, &queue, cmd, &preset.textures)?;
let samplers = SamplerSet::new(&device);
//
let framebuffer_gen =
|| Ok::<_, error::FilterChainError>(OwnedImage::new(Arc::clone(&device), Size::new(1, 1), 1, ImageFormat::R8G8B8A8Unorm));
let framebuffer_gen = || {
Ok::<_, error::FilterChainError>(OwnedImage::new(
Arc::clone(&device),
Size::new(1, 1),
1,
ImageFormat::R8G8B8A8Unorm,
))
};
let input_gen = || None;
let framebuffer_init = FramebufferInit::new(
filters.iter().map(|f| &f.reflection.meta),
@ -122,39 +131,39 @@ impl FilterChainWGPU {
// FrameResiduals::new(&device.device)
// });
let draw_quad = DrawQuad::new(&device, queue);
let draw_quad = DrawQuad::new(&device);
Ok(FilterChainWGPU {
common: FilterCommon {
luts,
samplers,
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
draw_quad,
device,
output_textures,
feedback_textures,
history_textures,
internal_frame_count: 0,
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
draw_quad,
device,
queue,
output_textures,
feedback_textures,
history_textures,
internal_frame_count: 0,
},
passes: filters,
output_framebuffers,
feedback_framebuffers,
history_framebuffers,
disable_mipmaps: false // todo: force no mipmaps,
output_framebuffers,
feedback_framebuffers,
history_framebuffers,
disable_mipmaps: false, // todo: force no mipmaps,
})
}
fn load_luts(
device: &wgpu::Device,
queue: &mut wgpu::Queue,
queue: &wgpu::Queue,
cmd: &mut wgpu::CommandEncoder,
textures: &[TextureConfig],
) -> error::Result<FxHashMap<usize, LutTexture>> {
@ -170,6 +179,29 @@ impl FilterChainWGPU {
Ok(luts)
}
fn push_history(&mut self, input: &wgpu::Texture, cmd: &mut wgpu::CommandEncoder) {
if let Some(mut back) = self.history_framebuffers.pop_back() {
if back.image.size() != input.size() || input.format() != back.image.format() {
// old back will get dropped.. do we need to defer?
let _old_back = std::mem::replace(
&mut back,
OwnedImage::new(
Arc::clone(&self.common.device),
input.size().into(),
1,
input.format().into(),
),
);
}
unsafe {
back.copy_from(cmd, input);
}
self.history_framebuffers.push_front(back)
}
}
fn init_passes(
device: Arc<Device>,
passes: Vec<ShaderPassMeta>,
@ -193,10 +225,19 @@ impl FilterChainWGPU {
.as_ref()
.map_or(0, |push| push.size as wgpu::BufferAddress);
let uniform_storage = UniformStorage::new_with_storage(
WgpuMappedBuffer::new(&device, wgpu::BufferUsages::UNIFORM, ubo_size as wgpu::BufferAddress, Some("ubo")),
WgpuMappedBuffer::new(&device, wgpu::BufferUsages::UNIFORM, push_size as wgpu::BufferAddress, Some("push"))
WgpuStagedBuffer::new(
&device,
wgpu::BufferUsages::UNIFORM,
ubo_size as wgpu::BufferAddress,
Some("ubo"),
),
WgpuStagedBuffer::new(
&device,
wgpu::BufferUsages::UNIFORM,
push_size as wgpu::BufferAddress,
Some("push"),
),
);
let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset());
@ -212,11 +253,9 @@ impl FilterChainWGPU {
Arc::clone(&device),
&wgsl,
&reflection,
render_pass_format.unwrap_or(TextureFormat::R8Unorm),
render_pass_format.unwrap_or(TextureFormat::Rgba8Unorm),
);
Ok(FilterPass {
device: Arc::clone(&device),
reflection,
@ -235,13 +274,13 @@ impl FilterChainWGPU {
Ok(filters.into_boxed_slice())
}
pub fn frame<'a>(&mut self,
pub fn frame<'a>(
&mut self,
input: Arc<wgpu::Texture>,
viewport: &Viewport<OutputImage<'a>>,
viewport: &Viewport<OutputView<'a>>,
cmd: &mut wgpu::CommandEncoder,
frame_count: usize,
options: Option<&FrameOptionsWGPU>,
options: Option<&FrameOptionsWGPU>,
) -> error::Result<()> {
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let passes = &mut self.passes[0..max];
@ -249,7 +288,7 @@ impl FilterChainWGPU {
if let Some(options) = &options {
if options.clear_history {
for history in &mut self.history_framebuffers {
// history.clear(cmd);
history.clear(cmd);
}
}
}
@ -258,21 +297,11 @@ impl FilterChainWGPU {
return Ok(());
}
let original_image_view = input.create_view(&wgpu::TextureViewDescriptor {
label: Some("original_image_view"),
format: Some(input.format()),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 1,
mip_level_count: None,
base_array_layer: 1,
array_layer_count: None,
});
let original_image_view = input.create_view(&wgpu::TextureViewDescriptor::default());
let filter = passes[0].config.filter;
let wrap_mode = passes[0].config.wrap_mode;
// update history
for (texture, image) in self
.common
@ -325,14 +354,13 @@ impl FilterChainWGPU {
let frame_direction = options.map_or(1, |f| f.frame_direction);
for (index, pass) in pass.iter_mut().enumerate() {
let target = &self.output_framebuffers[index];
source.filter_mode = pass.config.filter;
source.wrap_mode = pass.config.wrap_mode;
source.mip_filter = pass.config.filter;
let output_image = OutputImage::new(target);
let output_image = OutputView::new(target);
let out = RenderTarget::identity(&output_image);
pass.draw(
@ -345,11 +373,11 @@ impl FilterChainWGPU {
&original,
&source,
&out,
QuadType::Offscreen
QuadType::Offscreen,
)?;
if target.max_miplevels > 1 && !self.disable_mipmaps {
// todo: mipmaps
target.generate_mipmaps(cmd);
}
source = self.common.output_textures[index].clone().unwrap();
@ -378,13 +406,12 @@ impl FilterChainWGPU {
&original,
&source,
&out,
QuadType::Final
QuadType::Final,
)?;
}
// self.push_history(input, cmd)?;
self.push_history(&input, cmd);
self.common.internal_frame_count = self.common.internal_frame_count.wrapping_add(1);
Ok(())
}
}

View file

@ -1,37 +1,42 @@
use crate::buffer::WgpuStagedBuffer;
use crate::error;
use crate::filter_chain::FilterCommon;
use crate::framebuffer::OutputView;
use crate::graphics_pipeline::WgpuGraphicsPipeline;
use crate::samplers::SamplerSet;
use crate::texture::{InputImage, OwnedImage};
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_preprocess::ShaderSource;
use librashader_presets::ShaderPassConfig;
use librashader_reflect::back::wgsl::NagaWgslContext;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::semantics::{BindingStage, MemberOffset, TextureBinding, UniformBinding};
use librashader_reflect::reflect::semantics::{
BindingStage, MemberOffset, TextureBinding, UniformBinding,
};
use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess};
use rustc_hash::FxHashMap;
use std::sync::Arc;
use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferUsages, RenderPass, ShaderStages, TextureView};
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::quad::QuadType;
use librashader_runtime::render_target::RenderTarget;
use crate::buffer::WgpuMappedBuffer;
use crate::error;
use crate::filter_chain::FilterCommon;
use crate::framebuffer::OutputImage;
use crate::samplers::SamplerSet;
use crate::texture::{InputImage, OwnedImage};
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage, UniformStorageAccess};
use rustc_hash::FxHashMap;
use std::sync::Arc;
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use wgpu::{
BindGroupDescriptor, BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferUsages,
RenderPass, ShaderStages, TextureView,
};
pub struct FilterPass {
pub device: Arc<wgpu::Device>,
pub reflection: ShaderReflection,
pub(crate) compiled: ShaderCompilerOutput<String, NagaWgslContext>,
pub(crate) uniform_storage: UniformStorage<NoUniformBinder, Option<()>, WgpuMappedBuffer, WgpuMappedBuffer>,
pub(crate) uniform_storage:
UniformStorage<NoUniformBinder, Option<()>, WgpuStagedBuffer, WgpuStagedBuffer>,
pub uniform_bindings: FxHashMap<UniformBinding, MemberOffset>,
pub source: ShaderSource,
pub config: ShaderPassConfig,
pub graphics_pipeline: WgpuGraphicsPipeline,
}
impl TextureInput for InputImage {
@ -42,10 +47,10 @@ impl TextureInput for InputImage {
pub struct WgpuArcBinding<T> {
binding: u32,
resource: Arc<T>
resource: Arc<T>,
}
impl BindSemantics<NoUniformBinder, Option<()>, WgpuMappedBuffer, WgpuMappedBuffer> for FilterPass {
impl BindSemantics<NoUniformBinder, Option<()>, WgpuStagedBuffer, WgpuStagedBuffer> for FilterPass {
type InputTexture = InputImage;
type SamplerSet = SamplerSet;
type DescriptorSet<'a> = (
@ -66,15 +71,21 @@ impl BindSemantics<NoUniformBinder, Option<()>, WgpuMappedBuffer, WgpuMappedBuff
let sampler = samplers.get(texture.wrap_mode, texture.filter_mode, texture.mip_filter);
let (texture_binding, sampler_binding) = descriptors;
texture_binding.insert(binding.binding, WgpuArcBinding {
binding: binding.binding,
resource: Arc::clone(&texture.view)
});
texture_binding.insert(
binding.binding,
WgpuArcBinding {
binding: binding.binding,
resource: Arc::clone(&texture.view),
},
);
sampler_binding.insert(binding.binding, WgpuArcBinding {
binding: binding.binding,
resource: sampler,
});
sampler_binding.insert(
binding.binding,
WgpuArcBinding {
binding: binding.binding,
resource: sampler,
},
);
}
}
@ -86,13 +97,12 @@ impl FilterPass {
parent: &FilterCommon,
frame_count: u32,
frame_direction: i32,
viewport: &Viewport<OutputImage>,
viewport: &Viewport<OutputView>,
original: &InputImage,
source: &InputImage,
output: &RenderTarget<OutputImage>,
output: &RenderTarget<OutputView>,
vbo_type: QuadType,
) -> error::Result<()> {
let mut main_heap = FxHashMap::default();
let mut sampler_heap = FxHashMap::default();
@ -110,21 +120,20 @@ impl FilterPass {
&mut sampler_heap,
);
let mut main_heap_array = Vec::with_capacity(main_heap.len() + 1);
let mut sampler_heap_array = Vec::with_capacity(sampler_heap.len() + 1);
for binding in main_heap.values() {
main_heap_array.push(BindGroupEntry {
binding: binding.binding,
resource: BindingResource::TextureView(&binding.resource)
resource: BindingResource::TextureView(&binding.resource),
})
}
for binding in sampler_heap.values() {
sampler_heap_array.push(BindGroupEntry {
binding: binding.binding,
resource: BindingResource::Sampler(&binding.resource)
resource: BindingResource::Sampler(&binding.resource),
})
}
@ -157,31 +166,24 @@ impl FilterPass {
let main_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
label: Some("main bind group"),
layout: &self.graphics_pipeline.layout.main_bind_group_layout,
entries: &main_heap_array
entries: &main_heap_array,
});
let sampler_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
label: Some("sampler bind group"),
layout: &self.graphics_pipeline.layout.sampler_bind_group_layout,
entries: &sampler_heap_array
entries: &sampler_heap_array,
});
let mut render_pass = self.graphics_pipeline
.begin_rendering(output, cmd);
let mut render_pass = self.graphics_pipeline.begin_rendering(output, cmd);
render_pass.set_bind_group(
0,
&main_bind_group,
&[]
);
render_pass.set_bind_group(0, &main_bind_group, &[]);
render_pass.set_bind_group(
1,
&sampler_bind_group,
&[]
);
render_pass.set_bind_group(1, &sampler_bind_group, &[]);
if let Some(push) = &self.reflection.push_constant && !has_pcb_buffer {
if let Some(push) = &self.reflection.push_constant
&& !has_pcb_buffer
{
let mut stage_mask = ShaderStages::empty();
if push.stage_mask.contains(BindingStage::FRAGMENT) {
stage_mask |= ShaderStages::FRAGMENT;
@ -189,11 +191,7 @@ impl FilterPass {
if push.stage_mask.contains(BindingStage::VERTEX) {
stage_mask |= ShaderStages::VERTEX;
}
render_pass.set_push_constants(
stage_mask,
0,
self.uniform_storage.push_slice()
)
render_pass.set_push_constants(stage_mask, 0, self.uniform_storage.push_slice())
}
parent.draw_quad.draw_quad(&mut render_pass, vbo_type);
@ -213,7 +211,7 @@ impl FilterPass {
original: &InputImage,
source: &InputImage,
main_heap: &'a mut FxHashMap<u32, WgpuArcBinding<wgpu::TextureView>>,
sampler_heap: &'a mut FxHashMap<u32, WgpuArcBinding<wgpu::Sampler>>
sampler_heap: &'a mut FxHashMap<u32, WgpuArcBinding<wgpu::Sampler>>,
) {
Self::bind_semantics(
&self.device,
@ -240,8 +238,8 @@ impl FilterPass {
);
// flush to buffers
self.uniform_storage.inner_ubo().flush();
self.uniform_storage.inner_push().flush();
self.uniform_storage.inner_ubo().flush(&parent.queue);
self.uniform_storage.inner_push().flush(&parent.queue);
}
}

View file

@ -1,18 +1,20 @@
use librashader_common::Size;
use crate::texture::OwnedImage;
use librashader_common::Size;
pub struct OutputImage<'a> {
pub struct OutputView<'a> {
pub size: Size<u32>,
pub view: &'a wgpu::TextureView,
pub format: wgpu::TextureFormat,
}
impl<'a> OutputImage<'a> {
impl<'a> OutputView<'a> {
pub fn new(image: &'a OwnedImage) -> Self {
Self {
size: image.size,
view: &image.view,
format: image.image.format()
format: image.image.format(),
}
}
}

View file

@ -1,17 +1,24 @@
use crate::framebuffer::OutputView;
use crate::{error, util};
use librashader_reflect::back::wgsl::NagaWgslContext;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::render_target::RenderTarget;
use std::borrow::Cow;
use std::sync::Arc;
use wgpu::{BindGroup, BindGroupDescriptor, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize, CommandEncoder, Device, Operations, PipelineLayout, PushConstantRange, RenderPass, RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, SamplerBindingType, ShaderModule, ShaderSource, ShaderStages, TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute, VertexBufferLayout};
use librashader_runtime::render_target::RenderTarget;
use crate::framebuffer::OutputImage;
use wgpu::{
BindGroup, BindGroupDescriptor, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize, CommandEncoder, Device,
Operations, PipelineLayout, PushConstantRange, RenderPass, RenderPassColorAttachment,
RenderPassDescriptor, RenderPipelineDescriptor, SamplerBindingType, ShaderModule, ShaderSource,
ShaderStages, TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute,
VertexBufferLayout,
};
pub struct WgpuGraphicsPipeline {
pub layout: PipelineLayoutObjects,
render_pipeline: wgpu::RenderPipeline,
pub format: wgpu::TextureFormat
pub format: wgpu::TextureFormat,
}
pub struct PipelineLayoutObjects {
@ -22,7 +29,7 @@ pub struct PipelineLayoutObjects {
vertex_entry_name: String,
vertex: ShaderModule,
fragment: ShaderModule,
device: Arc<wgpu::Device>
device: Arc<wgpu::Device>,
}
impl PipelineLayoutObjects {
@ -138,55 +145,56 @@ impl PipelineLayoutObjects {
}
pub fn create_pipeline(&self, framebuffer_format: TextureFormat) -> wgpu::RenderPipeline {
self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&self.layout),
vertex: wgpu::VertexState {
module: &self.vertex,
entry_point: &self.vertex_entry_name,
buffers: &[VertexBufferLayout {
array_stride: 4 * std::mem::size_of::<f32>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: (2 * std::mem::size_of::<f32>()) as wgpu::BufferAddress,
shader_location: 1,
},
],
}],
},
fragment: Some(wgpu::FragmentState {
module: &self.fragment,
entry_point: &self.fragment_entry_name,
targets: &[Some(wgpu::ColorTargetState {
format: framebuffer_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
})
self.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&self.layout),
vertex: wgpu::VertexState {
module: &self.vertex,
entry_point: &self.vertex_entry_name,
buffers: &[VertexBufferLayout {
array_stride: 4 * std::mem::size_of::<f32>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: (2 * std::mem::size_of::<f32>()) as wgpu::BufferAddress,
shader_location: 1,
},
],
}],
},
fragment: Some(wgpu::FragmentState {
module: &self.fragment,
entry_point: &self.fragment_entry_name,
targets: &[Some(wgpu::ColorTargetState {
format: framebuffer_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
})
}
}
@ -202,7 +210,7 @@ impl WgpuGraphicsPipeline {
Self {
layout,
render_pipeline,
format: render_pass_format
format: render_pass_format,
}
}
@ -213,8 +221,8 @@ impl WgpuGraphicsPipeline {
pub(crate) fn begin_rendering<'pass>(
&'pass self,
output: &RenderTarget<'pass, OutputImage>,
cmd: &'pass mut CommandEncoder
output: &RenderTarget<'pass, OutputView>,
cmd: &'pass mut CommandEncoder,
) -> RenderPass<'pass> {
let mut render_pass = cmd.begin_render_pass(&RenderPassDescriptor {
label: Some("librashader"),
@ -240,7 +248,7 @@ impl WgpuGraphicsPipeline {
output.x as u32,
output.y as u32,
output.output.size.width,
output.output.size.height
output.output.size.height,
);
render_pass.set_viewport(
@ -249,10 +257,10 @@ impl WgpuGraphicsPipeline {
output.output.size.width as f32,
output.output.size.height as f32,
1.0,
1.0
1.0,
);
render_pass.set_pipeline(&self.render_pipeline);
render_pass
}
}

View file

@ -7,18 +7,19 @@
#![feature(let_chains)]
#![feature(strict_provenance)]
mod buffer;
mod draw_quad;
mod error;
mod filter_chain;
mod filter_pass;
mod framebuffer;
mod graphics_pipeline;
mod luts;
mod options;
mod samplers;
mod texture;
mod util;
mod framebuffer;
mod luts;
mod options;
mod buffer;
pub use framebuffer::OutputView;
pub use filter_chain::FilterChainWGPU;
pub use filter_pass::FilterPass;

View file

@ -1,10 +1,10 @@
use std::sync::Arc;
use wgpu::{ImageDataLayout, Label, TextureDescriptor};
use wgpu::util::DeviceExt;
use librashader_presets::TextureConfig;
use librashader_runtime::image::{BGRA8, Image};
use librashader_runtime::scaling::MipmapSize;
use crate::texture::{Handle, InputImage};
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};
pub(crate) struct LutTexture(InputImage);
impl AsRef<InputImage> for LutTexture {
@ -16,10 +16,10 @@ impl AsRef<InputImage> for LutTexture {
impl LutTexture {
pub fn new(
device: &wgpu::Device,
queue: &mut wgpu::Queue,
queue: &wgpu::Queue,
_cmd: &mut wgpu::CommandEncoder,
image: Image,
config: &TextureConfig
config: &TextureConfig,
) -> LutTexture {
let texture = device.create_texture(&TextureDescriptor {
label: Some(&config.name),
@ -39,7 +39,6 @@ impl LutTexture {
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
});
queue.write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
@ -53,7 +52,7 @@ impl LutTexture {
bytes_per_row: Some(4 * image.size.width),
rows_per_image: None,
},
image.size.into()
image.size.into(),
);
// todo: mipmaps

View file

@ -16,9 +16,12 @@ impl SamplerSet {
// SAFETY: the sampler set is complete for the matrix
// wrap x filter x mipmap
unsafe {
Arc::clone(&self.samplers
.get(&(wrap, filter, mipmap))
.unwrap_unchecked())
Arc::clone(
&self
.samplers
.get(&(wrap, filter, mipmap))
.unwrap_unchecked(),
)
}
}
@ -48,8 +51,8 @@ impl SamplerSet {
compare: None,
anisotropy_clamp: 1,
border_color: Some(SamplerBorderColor::TransparentBlack),
}),
));
})),
);
}
}
}

View file

@ -1,10 +1,10 @@
use std::ops::Deref;
use std::sync::Arc;
use wgpu::{TextureFormat, TextureView};
use crate::error::FilterChainError;
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ScaleFramebuffer, ViewportSize};
use crate::error::FilterChainError;
use std::ops::Deref;
use std::sync::Arc;
use wgpu::{ImageCopyTexture, TextureFormat, TextureView};
pub struct OwnedImage {
device: Arc<wgpu::Device>,
@ -17,14 +17,14 @@ pub struct OwnedImage {
pub enum Handle<'a, T> {
Borrowed(&'a T),
Owned(Arc<T>)
Owned(Arc<T>),
}
impl<T> Clone for Handle<'_, T> {
fn clone(&self) -> Self {
match self {
Handle::Borrowed(r) => Handle::Borrowed(r),
Handle::Owned(r) => Handle::Owned(Arc::clone(r))
Handle::Owned(r) => Handle::Owned(Arc::clone(r)),
}
}
}
@ -34,7 +34,7 @@ impl<T> Deref for Handle<'_, T> {
fn deref(&self) -> &Self::Target {
match self {
Handle::Borrowed(r) => &r,
Handle::Owned(r) => &r
Handle::Owned(r) => &r,
}
}
}
@ -56,12 +56,12 @@ impl AsRef<InputImage> for InputImage {
}
impl OwnedImage {
pub fn new(device: Arc<wgpu::Device>,
size: Size<u32>,
max_miplevels: u32,
pub fn new(
device: Arc<wgpu::Device>,
size: Size<u32>,
max_miplevels: u32,
format: ImageFormat,
) -> Self {
let format: Option<wgpu::TextureFormat> = format.into();
let format = format.unwrap_or(wgpu::TextureFormat::Rgba8Unorm);
@ -117,7 +117,12 @@ impl OwnedImage {
|| (!mipmap && self.max_miplevels != 1)
|| format != self.image.format()
{
let mut new = OwnedImage::new(Arc::clone(&self.device), size, self.max_miplevels, format.into());
let mut new = OwnedImage::new(
Arc::clone(&self.device),
size,
self.max_miplevels,
format.into(),
);
std::mem::swap(self, &mut new);
}
size
@ -132,25 +137,34 @@ impl OwnedImage {
mip_filter: filter,
}
}
pub fn copy_from(&self, cmd: &mut wgpu::CommandEncoder, source: &wgpu::Texture) {
cmd.copy_texture_to_texture(
source.as_image_copy(),
self.image.as_image_copy(),
source.size(),
)
}
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) {}
}
impl ScaleFramebuffer for OwnedImage {
type Error = FilterChainError;
type Context = ();
fn scale(&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
should_mipmap: bool,
_context: &Self::Context) -> Result<Size<u32>, Self::Error> {
Ok(self.scale(
scaling,
format,
viewport_size,
source_size,
should_mipmap,
))
fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
should_mipmap: bool,
_context: &Self::Context,
) -> Result<Size<u32>, Self::Error> {
Ok(self.scale(scaling, format, viewport_size, source_size, should_mipmap))
}
}

View file

@ -1,5 +1,5 @@
use std::sync::Arc;
use wgpu::{ Maintain, };
use wgpu::Maintain;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
@ -12,6 +12,7 @@ use wgpu::util::DeviceExt;
use winit::event_loop::EventLoopBuilder;
use winit::keyboard::{Key, KeyCode, PhysicalKey};
use winit::platform::windows::EventLoopBuilderExtWindows;
use librashader_common::Viewport;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
@ -64,7 +65,7 @@ const VERTICES: &[Vertex] = &[
struct State<'a> {
surface: wgpu::Surface<'a>,
device: Arc<wgpu::Device>,
queue: wgpu::Queue,
queue: Arc<wgpu::Queue>,
config: wgpu::SurfaceConfiguration,
size: winit::dpi::PhysicalSize<u32>,
clear_color: wgpu::Color,
@ -74,6 +75,7 @@ struct State<'a> {
vertex_buffer: wgpu::Buffer,
num_vertices: u32,
chain: FilterChainWGPU,
frame_count: usize
}
impl<'a> State<'a> {
async fn new(window: &'a Window) -> Self {
@ -91,7 +93,7 @@ impl<'a> State<'a> {
.await
.unwrap();
let (device, mut queue) = adapter
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
@ -106,7 +108,7 @@ impl<'a> State<'a> {
let swapchain_format = swapchain_capabilities.formats[0];
let mut config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
format: swapchain_format,
width: size.width,
height: size.height,
@ -117,16 +119,22 @@ impl<'a> State<'a> {
};
let device = Arc::new(device);
let queue = Arc::new(queue);
let mut cmd = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("start encoder"),
});
let preset =
ShaderPreset::try_parse("../test/shaders_slang/crt/crt-royale.slangp").unwrap();
ShaderPreset::try_parse("../test/basic.slangp").unwrap();
let chain = FilterChainWGPU::load_from_preset_deferred(Arc::clone(&device), &mut queue, &mut cmd, preset).unwrap();
let chain = FilterChainWGPU::load_from_preset_deferred(
Arc::clone(&device),
Arc::clone(&queue),
&mut cmd,
preset,
)
.unwrap();
let cmd = cmd.finish();
@ -202,6 +210,7 @@ impl<'a> State<'a> {
vertex_buffer,
num_vertices,
chain,
frame_count: 0,
}
}
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
@ -218,9 +227,46 @@ impl<'a> State<'a> {
fn update(&mut self) {}
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let output = self.surface.get_current_texture()?;
let view = output
.texture
let render_output = Arc::new(self.device.create_texture(
&wgpu::TextureDescriptor {
label: Some("rendertexture"),
size: output.texture.size(),
mip_level_count: output.texture.mip_level_count(),
sample_count: output.texture.sample_count(),
dimension: output.texture.dimension(),
format: output.texture.format(),
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[output.texture.format()],
}
));
let filter_output = Arc::new(self.device.create_texture(
&wgpu::TextureDescriptor {
label: Some("filteroutput"),
size: output.texture.size(),
mip_level_count: output.texture.mip_level_count(),
sample_count: output.texture.sample_count(),
dimension: output.texture.dimension(),
format: output.texture.format(),
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[output.texture.format()],
}
));
let view = render_output
.create_view(&wgpu::TextureViewDescriptor::default());
let filter_view = filter_output
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
@ -228,7 +274,8 @@ impl<'a> State<'a> {
});
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
@ -247,9 +294,32 @@ impl<'a> State<'a> {
render_pass.draw(0..self.num_vertices, 0..1);
}
self.chain
.frame(Arc::clone(&render_output),
&Viewport {
x: 0.0,
y: 0.0,
mvp: None,
output: librashader_runtime_wgpu::OutputView {
size: filter_output.size().into(),
view: &filter_view,
format: filter_output.format(),
},
},
&mut encoder,
self.frame_count, None
).expect("failed to draw frame");
encoder.copy_texture_to_texture(
filter_output.as_image_copy(),
output.texture.as_image_copy(),
output.texture.size()
);
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
self.frame_count += 1;
Ok(())
}
}
@ -266,39 +336,36 @@ pub fn run() {
pollster::block_on(async {
let mut state = State::new(&window).await;
event_loop.run(|event, target| {
match event {
Event::WindowEvent {
window_id: _,
event
} => match event {
WindowEvent::Resized(new_size) => {
state.resize(new_size);
// On macos the window needs to be redrawn manually after resizing
window.request_redraw();
}
WindowEvent::RedrawRequested => {
state.update();
match state.render() {
Ok(_) => {}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
Err(wgpu::SurfaceError::OutOfMemory) => target.exit(),
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
event_loop
.run(|event, target| {
match event {
Event::WindowEvent {
window_id: _,
event,
} => match event {
WindowEvent::Resized(new_size) => {
state.resize(new_size);
// On macos the window needs to be redrawn manually after resizing
window.request_redraw();
}
}
WindowEvent::CloseRequested => target.exit(),
WindowEvent::RedrawRequested => {
state.update();
match state.render() {
Ok(_) => {}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
Err(wgpu::SurfaceError::OutOfMemory) => target.exit(),
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
WindowEvent::CloseRequested => target.exit(),
_ => {}
},
Event::AboutToWait => window.request_redraw(),
_ => {}
}
Event::AboutToWait => {
window.request_redraw()
}
_ => {}
}
}).unwrap();
})
.unwrap();
});
}