diff --git a/librashader-common/Cargo.toml b/librashader-common/Cargo.toml index 1469558..3dd71c3 100644 --- a/librashader-common/Cargo.toml +++ b/librashader-common/Cargo.toml @@ -18,10 +18,12 @@ d3d11 = ["windows", "dxgi"] d3d12 = ["windows", "dxgi"] dxgi = ["windows"] vulkan = ["ash"] +wgpu = ["wgpu-types"] [dependencies] gl = { version = "0.14.0", optional = true } ash = { version = "0.37", optional = true } +wgpu-types = { version = "0.18.0", optional = true } num-traits = "0.2.15" diff --git a/librashader-common/src/lib.rs b/librashader-common/src/lib.rs index 1f78b7f..477750b 100644 --- a/librashader-common/src/lib.rs +++ b/librashader-common/src/lib.rs @@ -8,6 +8,10 @@ pub mod gl; #[cfg(feature = "vulkan")] pub mod vk; +/// WGPU common conversions. +#[cfg(feature = "wgpu")] +pub mod wgpu; + /// DXGI common conversions. #[cfg(all(target_os = "windows", feature = "dxgi"))] pub mod dxgi; @@ -21,6 +25,7 @@ pub mod d3d11; pub mod d3d12; mod viewport; + pub use viewport::Viewport; use num_traits::AsPrimitive; diff --git a/librashader-common/src/wgpu.rs b/librashader-common/src/wgpu.rs new file mode 100644 index 0000000..fa44238 --- /dev/null +++ b/librashader-common/src/wgpu.rs @@ -0,0 +1,177 @@ +use crate::{FilterMode, ImageFormat, Size, WrapMode}; + +impl From for Option { + fn from(format: ImageFormat) -> Self { + match format { + ImageFormat::Unknown => None, + ImageFormat::R8Unorm => Some(wgpu_types::TextureFormat::R8Unorm), + ImageFormat::R8Uint => Some(wgpu_types::TextureFormat::R8Uint), + ImageFormat::R8Sint => Some(wgpu_types::TextureFormat::R8Sint), + ImageFormat::R8G8Unorm => Some(wgpu_types::TextureFormat::Rg8Unorm), + ImageFormat::R8G8Uint => Some(wgpu_types::TextureFormat::Rg8Uint), + ImageFormat::R8G8Sint => Some(wgpu_types::TextureFormat::Rg8Sint), + ImageFormat::R8G8B8A8Unorm => Some(wgpu_types::TextureFormat::Rgba8Unorm), + ImageFormat::R8G8B8A8Uint => Some(wgpu_types::TextureFormat::Rgba8Uint), + ImageFormat::R8G8B8A8Sint => Some(wgpu_types::TextureFormat::Rgba8Sint), + ImageFormat::R8G8B8A8Srgb => Some(wgpu_types::TextureFormat::Rgba8UnormSrgb), + ImageFormat::A2B10G10R10UnormPack32 => Some(wgpu_types::TextureFormat::Rgb10a2Unorm), + ImageFormat::A2B10G10R10UintPack32 => Some(wgpu_types::TextureFormat::Rgb10a2Uint), + ImageFormat::R16Uint => Some(wgpu_types::TextureFormat::R16Uint), + ImageFormat::R16Sint => Some(wgpu_types::TextureFormat::R16Sint), + ImageFormat::R16Sfloat => Some(wgpu_types::TextureFormat::R16Float), + ImageFormat::R16G16Uint => Some(wgpu_types::TextureFormat::Rg16Uint), + ImageFormat::R16G16Sint => Some(wgpu_types::TextureFormat::Rg16Sint), + ImageFormat::R16G16Sfloat => Some(wgpu_types::TextureFormat::Rg16Float), + ImageFormat::R16G16B16A16Uint => Some(wgpu_types::TextureFormat::Rgba16Uint), + ImageFormat::R16G16B16A16Sint => Some(wgpu_types::TextureFormat::Rgba16Sint), + ImageFormat::R16G16B16A16Sfloat => Some(wgpu_types::TextureFormat::Rgba16Float), + ImageFormat::R32Uint => Some(wgpu_types::TextureFormat::R32Uint), + ImageFormat::R32Sint => Some(wgpu_types::TextureFormat::R32Sint), + ImageFormat::R32Sfloat => Some(wgpu_types::TextureFormat::R32Float), + ImageFormat::R32G32Uint => Some(wgpu_types::TextureFormat::Rg32Uint), + ImageFormat::R32G32Sint => Some(wgpu_types::TextureFormat::Rg32Sint), + ImageFormat::R32G32Sfloat => Some(wgpu_types::TextureFormat::Rg32Float), + ImageFormat::R32G32B32A32Uint => Some(wgpu_types::TextureFormat::Rgba32Uint), + ImageFormat::R32G32B32A32Sint => Some(wgpu_types::TextureFormat::Rgba32Sint), + ImageFormat::R32G32B32A32Sfloat => Some(wgpu_types::TextureFormat::Rgba32Float), + } + } +} + +impl From for ImageFormat { + fn from(format: wgpu_types::TextureFormat) -> Self { + match format { + wgpu_types::TextureFormat::R8Unorm => ImageFormat::R8Unorm, + wgpu_types::TextureFormat::R8Uint => ImageFormat::R8Uint, + wgpu_types::TextureFormat::R8Sint => ImageFormat::R8Sint, + wgpu_types::TextureFormat::Rg8Unorm => ImageFormat::R8G8Unorm, + wgpu_types::TextureFormat::Rg8Uint => ImageFormat::R8G8Uint, + wgpu_types::TextureFormat::Rg8Sint => ImageFormat::R8G8Sint, + wgpu_types::TextureFormat::Rgba8Unorm => ImageFormat::R8G8B8A8Unorm, + wgpu_types::TextureFormat::Rgba8Uint => ImageFormat::R8G8B8A8Uint, + wgpu_types::TextureFormat::Rgba8Sint => ImageFormat::R8G8B8A8Sint, + wgpu_types::TextureFormat::Rgba8UnormSrgb => ImageFormat::R8G8B8A8Srgb, + wgpu_types::TextureFormat::Rgb10a2Unorm => ImageFormat::A2B10G10R10UnormPack32, + wgpu_types::TextureFormat::Rgb10a2Uint => ImageFormat::A2B10G10R10UintPack32, + wgpu_types::TextureFormat::R16Uint => ImageFormat::R16Uint, + wgpu_types::TextureFormat::R16Sint => ImageFormat::R16Sint, + wgpu_types::TextureFormat::R16Float => ImageFormat::R16Sfloat, + wgpu_types::TextureFormat::Rg16Uint => ImageFormat::R16G16Uint, + wgpu_types::TextureFormat::Rg16Sint => ImageFormat::R16G16Sint, + wgpu_types::TextureFormat::Rg16Float => ImageFormat::R16G16Sfloat, + wgpu_types::TextureFormat::Rgba16Uint => ImageFormat::R16G16B16A16Uint, + wgpu_types::TextureFormat::Rgba16Sint => ImageFormat::R16G16B16A16Sint, + wgpu_types::TextureFormat::Rgba16Float => ImageFormat::R16G16B16A16Sfloat, + wgpu_types::TextureFormat::R32Uint => ImageFormat::R32Uint, + wgpu_types::TextureFormat::R32Sint => ImageFormat::R32Sint, + wgpu_types::TextureFormat::R32Float => ImageFormat::R32Sfloat, + wgpu_types::TextureFormat::Rg32Uint => ImageFormat::R32G32Uint, + wgpu_types::TextureFormat::Rg32Sint => ImageFormat::R32G32Sint, + wgpu_types::TextureFormat::Rg32Float => ImageFormat::R32G32Sfloat, + wgpu_types::TextureFormat::Rgba32Uint => ImageFormat::R32G32B32A32Uint, + wgpu_types::TextureFormat::Rgba32Sint => ImageFormat::R32G32B32A32Sint, + wgpu_types::TextureFormat::Rgba32Float => ImageFormat::R32G32B32A32Sfloat, + _ => ImageFormat::Unknown + } + } +} + +impl From> for ImageFormat { + fn from(format: Option) -> Self { + let Some(format) = format else { + return ImageFormat::Unknown; + }; + ImageFormat::from(format) + } +} + +// +// impl From> for vk::Extent3D { +// fn from(value: Size) -> Self { +// vk::Extent3D { +// width: value.width, +// height: value.height, +// depth: 1, +// } +// } +// } +// +// impl From> for vk::Extent2D { +// fn from(value: Size) -> Self { +// vk::Extent2D { +// width: value.width, +// height: value.height, +// } +// } +// } +// +// impl From for Size { +// fn from(value: vk::Extent3D) -> Self { +// Size { +// width: value.width, +// height: value.height, +// } +// } +// } +// +// impl From for Size { +// fn from(value: vk::Extent2D) -> Self { +// Size { +// width: value.width, +// height: value.height, +// } +// } +// } +// +// impl From for Size { +// fn from(value: vk::Viewport) -> Self { +// Size { +// width: value.width as u32, +// height: value.height as u32, +// } +// } +// } +// +// impl From<&vk::Viewport> for Size { +// fn from(value: &vk::Viewport) -> Self { +// Size { +// width: value.width as u32, +// height: value.height as u32, +// } +// } +// } +// +// impl From> for vk::Viewport { +// fn from(value: Size) -> Self { +// vk::Viewport { +// x: 0.0, +// y: 0.0, +// width: value.width as f32, +// height: value.height as f32, +// min_depth: 0.0, +// max_depth: 1.0, +// } +// } +// } + +impl From for wgpu_types::FilterMode { + fn from(value: FilterMode) -> Self { + match value { + FilterMode::Linear => wgpu_types::FilterMode::Linear, + FilterMode::Nearest => wgpu_types::FilterMode::Nearest, + } + } +} + + + +impl From for wgpu_types::AddressMode { + fn from(value: WrapMode) -> Self { + match value { + WrapMode::ClampToBorder => wgpu_types::AddressMode::ClampToBorder, + WrapMode::ClampToEdge => wgpu_types::AddressMode::ClampToEdge, + WrapMode::Repeat => wgpu_types::AddressMode::Repeat, + WrapMode::MirroredRepeat => wgpu_types::AddressMode::MirrorRepeat, + } + } +} diff --git a/librashader-runtime-wgpu/Cargo.toml b/librashader-runtime-wgpu/Cargo.toml new file mode 100644 index 0000000..2364aeb --- /dev/null +++ b/librashader-runtime-wgpu/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "librashader-runtime-wgpu" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +librashader-common = { path = "../librashader-common", features = ["wgpu"], version = "0.1.4" } +librashader-presets = { path = "../librashader-presets", version = "0.1.4" } +librashader-preprocess = { path = "../librashader-preprocess", version = "0.1.4" } +librashader-reflect = { path = "../librashader-reflect", version = "0.1.4", features = [] } +librashader-runtime = { path = "../librashader-runtime" , version = "0.1.4" } +librashader-cache = { path = "../librashader-cache", version = "0.1.4" } + +wgpu = { version = "0.18.0", features = ["spirv"] } +rustc-hash = "1.1.0" +image = "0.24.7" +thiserror = "1.0.50" +spirv_cross = { package = "librashader-spirv-cross", version = "0.23" } +parking_lot = "0.12.1" +rayon = "1.8.0" +bytemuck = { version = "1.14.0", features = ["derive"] } + + + +[dev-dependencies] +config = { version = "0.13.4", features = [] } +env_logger = "0.10.1" +raw-window-handle = "0.5" +winit = "0.28.7" +pollster = "0.3" +log = "0.4.20" + + +[[test]] +name = "triangle" \ No newline at end of file diff --git a/librashader-runtime-wgpu/src/filter_chain.rs b/librashader-runtime-wgpu/src/filter_chain.rs index 1b799c6..9892e4d 100644 --- a/librashader-runtime-wgpu/src/filter_chain.rs +++ b/librashader-runtime-wgpu/src/filter_chain.rs @@ -21,10 +21,13 @@ use librashader_runtime::framebuffer::FramebufferInit; use librashader_runtime::render_target::RenderTarget; use librashader_runtime::scaling::ScaleFramebuffer; use rayon::prelude::*; +use wgpu::{CommandBuffer, CommandEncoder, Device, Queue, TextureFormat}; +use librashader_common::ImageFormat; use crate::error; use crate::error::FilterChainError; use crate::filter_pass::FilterPass; +use crate::graphics_pipeline::WgpuGraphicsPipeline; type ShaderPassMeta = ShaderPassArtifact + Send>; @@ -47,7 +50,7 @@ fn compile_passes( /// A Vulkan filter chain. pub struct FilterChainWGPU { pub(crate) common: FilterCommon, - // passes: Box<[FilterPass]>, + passes: Box<[FilterPass]>, // vulkan: VulkanObjects, // output_framebuffers: Box<[OwnedImage]>, // feedback_framebuffers: Box<[OwnedImage]>, @@ -81,7 +84,9 @@ impl FilterChainWGPU { /// The provided command buffer must be ready for recording and contain no prior commands. /// The caller is responsible for ending the command buffer and immediately submitting it to a /// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame). - pub unsafe fn load_from_preset_deferred( + pub fn load_from_preset_deferred( + device: &Device, + // cmd: &mut CommandEncoder, preset: ShaderPreset, ) -> error::Result @@ -90,7 +95,6 @@ impl FilterChainWGPU { let disable_cache = true; let (passes, semantics) = compile_passes(preset.shaders, &preset.textures, disable_cache)?; - todo!() // let device = vulkan.try_into().map_err(From::from)?; // // let mut frames_in_flight = options.map_or(0, |o| o.frames_in_flight); @@ -99,14 +103,12 @@ impl FilterChainWGPU { // } // // // initialize passes - // let filters = Self::init_passes( - // &device, - // passes, - // &semantics, - // frames_in_flight, - // options.map_or(false, |o| o.use_render_pass), - // disable_cache, - // )?; + let filters = Self::init_passes( + &device, + passes, + &semantics, + disable_cache, + )?; // // let luts = FilterChainVulkan::load_luts(&device, cmd, &preset.textures)?; // let samplers = SamplerSet::new(&device.device)?; @@ -162,9 +164,15 @@ impl FilterChainWGPU { // residuals: intermediates.into_boxed_slice(), // disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps), // }) + + Ok(FilterChainWGPU { + common: FilterCommon {}, + passes: filters, + }) } fn init_passes( + device: &Device, passes: Vec, semantics: &ShaderSemantics, disable_cache: bool, @@ -179,31 +187,31 @@ impl FilterChainWGPU { let spirv_words = reflect.compile(None)?; let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize); - // let uniform_storage = UniformStorage::new_with_ubo_storage( - // RawVulkanBuffer::new( - // &vulkan.device, - // &vulkan.alloc, - // vk::BufferUsageFlags::UNIFORM_BUFFER, - // ubo_size, - // )?, - // reflection - // .push_constant - // .as_ref() - // .map_or(0, |push| push.size as usize), - // ); - // + + let uniform_storage = UniformStorage::new( + ubo_size, + reflection + .push_constant + .as_ref() + .map_or(0, |push| push.size as usize), + ); + let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); // - // let render_pass_format = if !use_render_pass { - // vk::Format::UNDEFINED - // } else if let Some(format) = config.get_format_override() { - // format.into() - // } else if source.format != ImageFormat::Unknown { - // source.format.into() - // } else { - // ImageFormat::R8G8B8A8Unorm.into() - // }; - // + let render_pass_format: Option = if let Some(format) = config.get_format_override() { + format.into() + } else { + source.format.into() + }; + + + let graphics_pipeline = WgpuGraphicsPipeline::new( + device, + &spirv_words, + &reflection, + render_pass_format.unwrap_or(TextureFormat::R8Unorm) + ); + // let graphics_pipeline = VulkanGraphicsPipeline::new( // &vulkan.device, // &spirv_words, @@ -217,11 +225,11 @@ impl FilterChainWGPU { // device: vulkan.device.clone(), reflection, compiled: spirv_words, - // uniform_storage, + uniform_storage, uniform_bindings, source, config, - // graphics_pipeline, + graphics_pipeline, // // ubo_ring, // frames_in_flight, }) diff --git a/librashader-runtime-wgpu/src/filter_pass.rs b/librashader-runtime-wgpu/src/filter_pass.rs index 426c02a..e29eea2 100644 --- a/librashader-runtime-wgpu/src/filter_pass.rs +++ b/librashader-runtime-wgpu/src/filter_pass.rs @@ -6,15 +6,16 @@ use librashader_reflect::back::ShaderCompilerOutput; use librashader_reflect::reflect::semantics::{MemberOffset, UniformBinding}; use librashader_reflect::reflect::ShaderReflection; use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage}; +use crate::graphics_pipeline::WgpuGraphicsPipeline; pub struct FilterPass { pub reflection: ShaderReflection, pub(crate) compiled: ShaderCompilerOutput>, - // pub(crate) uniform_storage: UniformStorage, RawVulkanBuffer>, + pub(crate) uniform_storage: UniformStorage, pub uniform_bindings: FxHashMap, pub source: ShaderSource, pub config: ShaderPassConfig, - // pub graphics_pipeline: VulkanGraphicsPipeline, + pub graphics_pipeline: WgpuGraphicsPipeline, // pub ubo_ring: VkUboRing, // pub frames_in_flight: u32, } diff --git a/librashader-runtime-wgpu/src/graphics_pipeline.rs b/librashader-runtime-wgpu/src/graphics_pipeline.rs new file mode 100644 index 0000000..f177568 --- /dev/null +++ b/librashader-runtime-wgpu/src/graphics_pipeline.rs @@ -0,0 +1,44 @@ +use std::borrow::Cow; +use std::sync::Arc; +use wgpu::{Device, ShaderModule, ShaderSource, TextureFormat}; +use librashader_reflect::back::ShaderCompilerOutput; +use librashader_reflect::reflect::ShaderReflection; + +pub struct WgpuGraphicsPipeline { + vertex: ShaderModule, + fragment: ShaderModule +} + +impl WgpuGraphicsPipeline { + pub fn new( + device: &Device, + shader_assembly: &ShaderCompilerOutput>, + reflection: &ShaderReflection, + render_pass_format: TextureFormat, + ) -> Self { + let vertex = unsafe { + device.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV { + label: Some("vertex"), + source: Cow::from(&shader_assembly.vertex), + }) + }; + + let fragment = unsafe { + device.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV { + label: Some("fragment"), + source: Cow::from(&shader_assembly.fragment), + }) + }; + + // let render_pipeline_layout = + // device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + // label: Some("Render Pipeline Layout"), + // bind_group_layouts: &[], + // push_constant_ranges: &[], + // }); + Self { + vertex, + fragment + } + } +} \ No newline at end of file diff --git a/librashader-runtime-wgpu/src/lib.rs b/librashader-runtime-wgpu/src/lib.rs new file mode 100644 index 0000000..997e944 --- /dev/null +++ b/librashader-runtime-wgpu/src/lib.rs @@ -0,0 +1,18 @@ +//! librashader WGPU runtime +//! +//! This crate should not be used directly. +//! See [`librashader::runtime::wgpu`](https://docs.rs/librashader/latest/librashader/runtime/wgpu/index.html) instead. +#![deny(unsafe_op_in_unsafe_fn)] +#![feature(type_alias_impl_trait)] +#![feature(let_chains)] +#![feature(strict_provenance)] + + +mod filter_chain; +mod error; +mod texture; +mod filter_pass; +mod graphics_pipeline; + +pub use filter_chain::FilterChainWGPU; +pub use filter_pass::FilterPass; \ No newline at end of file diff --git a/librashader-runtime-wgpu/tests/hello_triangle.rs b/librashader-runtime-wgpu/tests/hello_triangle.rs index 33bb5a1..5017b3f 100644 --- a/librashader-runtime-wgpu/tests/hello_triangle.rs +++ b/librashader-runtime-wgpu/tests/hello_triangle.rs @@ -7,6 +7,8 @@ use winit::{ use wgpu::util::DeviceExt; use winit::event_loop::EventLoopBuilder; use winit::platform::windows::EventLoopBuilderExtWindows; +use librashader_presets::ShaderPreset; +use librashader_runtime_wgpu::FilterChainWGPU; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -69,6 +71,7 @@ struct State { vertex_buffer: wgpu::Buffer, num_vertices: u32, + chain: FilterChainWGPU } impl State { async fn new(window: &Window) -> Self { @@ -89,7 +92,7 @@ impl State { let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { - features: wgpu::Features::empty(), + features: wgpu::Features::PUSH_CONSTANTS | wgpu::Features::SPIRV_SHADER_PASSTHROUGH, limits: wgpu::Limits::default(), label: None, }, @@ -110,6 +113,9 @@ impl State { view_formats: vec![], }; + let preset = ShaderPreset::try_parse("../test/shaders_slang/crt/crt-royale.slangp").unwrap(); + let chain = FilterChainWGPU::load_from_preset_deferred(&device, preset).unwrap(); + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("Shader"), source: wgpu::ShaderSource::Wgsl(include_str!("../shader/triangle.wgsl").into()), @@ -183,6 +189,7 @@ impl State { render_pipeline, vertex_buffer, num_vertices, + chain } } fn resize(&mut self, new_size: winit::dpi::PhysicalSize) {