rt(wgpu): add structure to wgpu backend

This commit is contained in:
chyyran 2023-12-16 20:28:41 +09:00 committed by Ronny Chan
parent 8cfe1e9da2
commit 2b995539f2
19 changed files with 566 additions and 282 deletions

View file

@ -12,10 +12,8 @@
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
</component>
<component name="ChangeListManager">
<list default="true" id="02471831-07cd-4975-a00c-e042450023a1" name="Changes" comment="rt(wgpu): create pipeline bind group layouts">
<change afterPath="$PROJECT_DIR$/librashader-runtime-wgpu/src/samplers.rs" afterDir="false" />
<list default="true" id="02471831-07cd-4975-a00c-e042450023a1" name="Changes" comment="rt(wgpu): create pipeline">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/librashader-runtime-wgpu/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/librashader-runtime-wgpu/src/lib.rs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -58,39 +56,39 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Cargo.Build `Test back::wgsl::test::test_into`.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Build `Test reflect::cross::test::test_into`.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test back::wgsl::test::test_into.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test front::naga::test::naga_playground (1).executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test front::naga::test::naga_playground.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test reflect::cross::test::test_into.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test triangle_d3d11.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test triangle_d3d12.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test triangle_gl.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test triangle_gl46.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test triangle_vk.executor&quot;: &quot;Run&quot;,
&quot;Cargo.Test triangle_wgpu.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.RadMigrateCodeStyle&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;RunOnceActivity.readMode.enableVisualFormatting&quot;: &quot;true&quot;,
&quot;cf.first.check.clang-format&quot;: &quot;false&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;feat-wgpu-runtime&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/coding/librashader&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;org.rust.cargo.project.model.PROJECT_DISCOVERY&quot;: &quot;true&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;language.rust&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Cargo.Build `Test back::wgsl::test::test_into`.executor": "Run",
"Cargo.Build `Test reflect::cross::test::test_into`.executor": "Run",
"Cargo.Test back::wgsl::test::test_into.executor": "Run",
"Cargo.Test front::naga::test::naga_playground (1).executor": "Run",
"Cargo.Test front::naga::test::naga_playground.executor": "Run",
"Cargo.Test reflect::cross::test::test_into.executor": "Run",
"Cargo.Test triangle_d3d11.executor": "Run",
"Cargo.Test triangle_d3d12.executor": "Run",
"Cargo.Test triangle_gl.executor": "Run",
"Cargo.Test triangle_gl46.executor": "Run",
"Cargo.Test triangle_vk.executor": "Run",
"Cargo.Test triangle_wgpu.executor": "Run",
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.RadMigrateCodeStyle": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.cidr.known.project.marker": "true",
"RunOnceActivity.readMode.enableVisualFormatting": "true",
"cf.first.check.clang-format": "false",
"cidr.known.project.marker": "true",
"git-widget-placeholder": "feat-wgpu-runtime",
"last_opened_file_path": "C:/coding/glslang-rs",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"org.rust.cargo.project.model.PROJECT_DISCOVERY": "true",
"settings.editor.selected.configurable": "language.rust",
"vue.rearranger.settings.migration": "true"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="F:\coding\librashader\librashader-reflect\src\back\wgsl" />
@ -266,7 +264,8 @@
<workItem from="1702482450853" duration="399000" />
<workItem from="1702482871248" duration="556000" />
<workItem from="1702484102491" duration="5258000" />
<workItem from="1702561144428" duration="385000" />
<workItem from="1702561144428" duration="1836000" />
<workItem from="1702640983281" duration="967000" />
</task>
<task id="LOCAL-00001" summary="rt(wgpu): basic triangle example">
<option name="closed" value="true" />
@ -316,7 +315,23 @@
<option name="project" value="LOCAL" />
<updated>1702514856945</updated>
</task>
<option name="localTasksCounter" value="7" />
<task id="LOCAL-00007" summary="rt(wgpu): sampler set">
<option name="closed" value="true" />
<created>1702561653875</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1702561653875</updated>
</task>
<task id="LOCAL-00008" summary="rt(wgpu): create pipeline">
<option name="closed" value="true" />
<created>1702562564811</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1702562564811</updated>
</task>
<option name="localTasksCounter" value="9" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -330,7 +345,9 @@
<MESSAGE value="rt(wgpu): load shaders" />
<MESSAGE value="reflect(wgsl): give push uniform a non-overlapping bind group" />
<MESSAGE value="rt(wgpu): create pipeline bind group layouts" />
<option name="LAST_COMMIT_MESSAGE" value="rt(wgpu): create pipeline bind group layouts" />
<MESSAGE value="rt(wgpu): sampler set" />
<MESSAGE value="rt(wgpu): create pipeline" />
<option name="LAST_COMMIT_MESSAGE" value="rt(wgpu): create pipeline" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>

1
Cargo.lock generated
View file

@ -1830,6 +1830,7 @@ dependencies = [
name = "librashader-runtime-wgpu"
version = "0.1.0"
dependencies = [
"array-concat",
"bytemuck",
"config",
"env_logger",

View file

@ -175,3 +175,13 @@ impl From<WrapMode> for wgpu_types::AddressMode {
}
}
}
impl From<Size<u32>> for wgpu_types::Extent3d {
fn from(value: Size<u32>) -> Self {
wgpu_types::Extent3d {
width: value.width,
height: value.height,
depth_or_array_layers: 1,
}
}
}

View file

@ -126,7 +126,7 @@ impl FilterPass {
let residual = self
.graphics_pipeline
.begin_rendering(&parent.device, output, cmd)?;
.begin_rendering(output, cmd)?;
unsafe {
parent.device.cmd_bind_pipeline(
@ -178,7 +178,7 @@ impl FilterPass {
.device
.cmd_set_viewport(cmd, 0, &[output.output.size.into()]);
parent.draw_quad.draw_quad(cmd, vbo_type);
self.graphics_pipeline.end_rendering(&parent.device, cmd);
self.graphics_pipeline.end_rendering(cmd);
}
Ok(residual)
}

View file

@ -384,14 +384,13 @@ impl VulkanGraphicsPipeline {
#[inline(always)]
pub(crate) fn begin_rendering(
&self,
device: &ash::Device,
output: &RenderTarget<OutputImage>,
cmd: vk::CommandBuffer,
) -> error::Result<Option<vk::Framebuffer>> {
if let Some(render_pass) = &self.render_pass {
let attachments = [output.output.image_view];
let framebuffer = unsafe {
device.create_framebuffer(
self.device.create_framebuffer(
&vk::FramebufferCreateInfo::builder()
.render_pass(render_pass.handle)
.attachments(&attachments)
@ -418,7 +417,7 @@ impl VulkanGraphicsPipeline {
extent: output.output.size.into(),
});
unsafe {
device.cmd_begin_render_pass(cmd, &render_pass_info, vk::SubpassContents::INLINE);
self.device.cmd_begin_render_pass(cmd, &render_pass_info, vk::SubpassContents::INLINE);
}
Ok(Some(framebuffer))
} else {
@ -438,18 +437,18 @@ impl VulkanGraphicsPipeline {
.color_attachments(&attachments);
unsafe {
device.cmd_begin_rendering(cmd, &rendering_info);
self.device.cmd_begin_rendering(cmd, &rendering_info);
}
Ok(None)
}
}
pub(crate) fn end_rendering(&self, device: &ash::Device, cmd: vk::CommandBuffer) {
pub(crate) fn end_rendering(&self, cmd: vk::CommandBuffer) {
unsafe {
if self.render_pass.is_none() {
device.cmd_end_rendering(cmd);
self.device.cmd_end_rendering(cmd);
} else {
device.cmd_end_render_pass(cmd)
self.device.cmd_end_render_pass(cmd)
}
}
}

View file

@ -21,7 +21,7 @@ parking_lot = "0.12.1"
rayon = "1.8.0"
bytemuck = { version = "1.14.0", features = ["derive"] }
array-concat = "0.5.2"
[dev-dependencies]
config = { version = "0.13.4", features = [] }

View file

@ -0,0 +1,52 @@
use array_concat::concat_arrays;
use librashader_runtime::quad::QuadType;
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use wgpu::{Buffer, BufferDescriptor, Device, RenderPass};
#[rustfmt::skip]
const VBO_OFFSCREEN: [f32; 16] = [
// Offscreen
-1.0f32, -1.0, 0.0, 0.0,
-1.0, 1.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 1.0,
];
#[rustfmt::skip]
const VBO_DEFAULT_FINAL: [f32; 16] = [
// Final
0.0f32, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 1.0,
1.0, 0.0, 1.0, 0.0,
1.0, 1.0, 1.0, 1.0,
];
static VBO_DATA: &[f32; 32] = &concat_arrays!(VBO_OFFSCREEN, VBO_DEFAULT_FINAL);
pub struct DrawQuad {
buffer: Buffer,
}
impl DrawQuad {
pub fn new(device: &Device) -> DrawQuad {
let buffer = device.create_buffer_init(&BufferInitDescriptor {
label: Some("librashader vbo"),
contents: bytemuck::cast_slice(VBO_DATA),
usage: wgpu::BufferUsages::VERTEX,
});
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) {
let offset = match vbo {
QuadType::Offscreen => 0..3,
QuadType::Final => 1..4,
};
cmd.draw(offset, 0..1)
}
}

View file

@ -6,7 +6,7 @@ use librashader_runtime::image::ImageError;
use std::convert::Infallible;
use thiserror::Error;
/// Cumulative error type for Vulkan filter chains.
/// Cumulative error type for WGPU filter chains.
#[derive(Error, Debug)]
pub enum FilterChainError {
#[error("SPIRV reflection error")]

View file

@ -6,7 +6,7 @@ use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtif
use librashader_reflect::reflect::semantics::ShaderSemantics;
use librashader_reflect::reflect::ReflectShader;
use librashader_runtime::binding::BindingUtil;
use librashader_runtime::image::{Image, ImageError, UVDirection, BGRA8};
use librashader_runtime::image::{Image, ImageError, UVDirection, BGRA8, RGBA8};
use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::UniformStorage;
use parking_lot::RwLock;
@ -16,22 +16,25 @@ use std::convert::Infallible;
use std::path::Path;
use std::sync::Arc;
use librashader_cache::CachedCompilation;
use librashader_common::{ImageFormat, Size};
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::{CommandBuffer, CommandEncoder, Device, Queue, TextureFormat};
use librashader_common::ImageFormat;
use librashader_reflect::back::wgsl::WgslCompileOptions;
use crate::draw_quad::DrawQuad;
use crate::error;
use crate::error::FilterChainError;
use crate::filter_pass::FilterPass;
use crate::graphics_pipeline::WgpuGraphicsPipeline;
use crate::luts::LutTexture;
use crate::samplers::SamplerSet;
use crate::texture::{InputImage, OwnedImage};
type ShaderPassMeta =
ShaderPassArtifact<impl CompileReflectShader<WGSL, GlslangCompilation> + Send>;
ShaderPassArtifact<impl CompileReflectShader<WGSL, GlslangCompilation> + Send>;
fn compile_passes(
shaders: Vec<ShaderPassConfig>,
textures: &[TextureConfig],
@ -45,29 +48,35 @@ fn compile_passes(
pub struct FilterChainWGPU {
pub(crate) common: FilterCommon,
passes: Box<[FilterPass]>,
// vulkan: VulkanObjects,
// output_framebuffers: Box<[OwnedImage]>,
// feedback_framebuffers: Box<[OwnedImage]>,
// history_framebuffers: VecDeque<OwnedImage>,
// disable_mipmaps: bool,
output_framebuffers: Box<[OwnedImage]>,
feedback_framebuffers: Box<[OwnedImage]>,
history_framebuffers: VecDeque<OwnedImage>,
disable_mipmaps: bool,
// residuals: Box<[FrameResiduals]>,
}
pub struct FilterMutable {
// pub(crate) passes_enabled: usize,
// pub(crate) parameters: FxHashMap<String, f32>,
pub passes_enabled: usize,
pub(crate) parameters: FxHashMap<String, f32>,
}
pub(crate) struct FilterCommon {
// pub(crate) luts: FxHashMap<usize, LutTexture>,
// pub samplers: SamplerSet,
// pub(crate) draw_quad: DrawQuad,
// pub output_textures: Box<[Option<InputImage>]>,
// pub feedback_textures: Box<[Option<InputImage>]>,
// pub history_textures: Box<[Option<InputImage>]>,
pub output_textures: Box<[Option<InputImage>]>,
pub feedback_textures: Box<[Option<InputImage>]>,
pub history_textures: Box<[Option<InputImage>]>,
// pub config: FilterMutable,
// pub device: Arc<ash::Device>,
// pub(crate) internal_frame_count: usize,
luts: FxHashMap<usize, LutTexture>,
samplers: SamplerSet,
config: FilterMutable,
internal_frame_count: i32,
draw_quad: DrawQuad,
device: Arc<Device>,
}
impl FilterChainWGPU {
@ -79,92 +88,94 @@ impl FilterChainWGPU {
/// 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 fn load_from_preset_deferred(
device: &Device,
// cmd: &mut CommandEncoder,
device: Arc<Device>,
queue: &mut wgpu::Queue,
cmd: &mut wgpu::CommandEncoder,
preset: ShaderPreset,
) -> error::Result<FilterChainWGPU>
{
) -> error::Result<FilterChainWGPU> {
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures)?;
// let device = vulkan.try_into().map_err(From::from)?;
//
// let mut frames_in_flight = options.map_or(0, |o| o.frames_in_flight);
// if frames_in_flight == 0 {
// frames_in_flight = 3;
// }
//
// // initialize passes
let filters = Self::init_passes(
&device,
passes,
&semantics,
)?;
let filters = Self::init_passes(Arc::clone(&device), passes, &semantics)?;
//
// let luts = FilterChainVulkan::load_luts(&device, cmd, &preset.textures)?;
// let samplers = SamplerSet::new(&device.device)?;
let luts = FilterChainWGPU::load_luts(&device, queue, cmd, &preset.textures)?;
let samplers = SamplerSet::new(&device);
//
// let framebuffer_gen =
// || OwnedImage::new(&device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, 1);
// let input_gen = || None;
// let framebuffer_init = FramebufferInit::new(
// filters.iter().map(|f| &f.reflection.meta),
// &framebuffer_gen,
// &input_gen,
// );
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),
&framebuffer_gen,
&input_gen,
);
//
// // initialize output framebuffers
// let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
//
// // initialize feedback framebuffers
// let (feedback_framebuffers, feedback_textures) =
// framebuffer_init.init_output_framebuffers()?;
// initialize feedback framebuffers
let (feedback_framebuffers, feedback_textures) =
framebuffer_init.init_output_framebuffers()?;
//
// // initialize history
// let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
// initialize history
let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
//
// let mut intermediates = Vec::new();
// intermediates.resize_with(frames_in_flight as usize, || {
// FrameResiduals::new(&device.device)
// });
// Ok(FilterChainVulkan {
// 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: DrawQuad::new(&device.device, &device.alloc)?,
// device: device.device.clone(),
// output_textures,
// feedback_textures,
// history_textures,
// internal_frame_count: 0,
// },
// passes: filters,
// vulkan: device,
// output_framebuffers,
// feedback_framebuffers,
// history_framebuffers,
// residuals: intermediates.into_boxed_slice(),
// disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
// })
let draw_quad = DrawQuad::new(&device);
Ok(FilterChainWGPU {
common: FilterCommon {},
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,
},
passes: filters,
output_framebuffers,
feedback_framebuffers,
history_framebuffers,
disable_mipmaps: false // todo: force no mipmaps,
})
}
fn load_luts(
device: &wgpu::Device,
queue: &mut wgpu::Queue,
cmd: &mut wgpu::CommandEncoder,
textures: &[TextureConfig],
) -> error::Result<FxHashMap<usize, LutTexture>> {
let mut luts = FxHashMap::default();
let images = textures
.par_iter()
.map(|texture| Image::load(&texture.path, UVDirection::TopLeft))
.collect::<Result<Vec<Image>, ImageError>>()?;
for (index, (texture, image)) in textures.iter().zip(images).enumerate() {
let texture = LutTexture::new(device, queue, cmd, image, texture);
luts.insert(index, texture);
}
Ok(luts)
}
fn init_passes(
device: &Device,
device: Arc<Device>,
passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics,
) -> error::Result<Box<[FilterPass]>> {
@ -192,29 +203,20 @@ impl FilterChainWGPU {
let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset());
//
let render_pass_format: Option<TextureFormat> = if let Some(format) = config.get_format_override() {
format.into()
} else {
source.format.into()
};
let render_pass_format: Option<TextureFormat> =
if let Some(format) = config.get_format_override() {
format.into()
} else {
source.format.into()
};
let graphics_pipeline = WgpuGraphicsPipeline::new(
device,
Arc::clone(&device),
&wgsl,
&reflection,
render_pass_format.unwrap_or(TextureFormat::R8Unorm)
render_pass_format.unwrap_or(TextureFormat::R8Unorm),
);
// let graphics_pipeline = VulkanGraphicsPipeline::new(
// &vulkan.device,
// &spirv_words,
// &reflection,
// frames_in_flight,
// render_pass_format,
// disable_cache,
// )?;
Ok(FilterPass {
// device: vulkan.device.clone(),
reflection,
@ -233,6 +235,7 @@ impl FilterChainWGPU {
let filters: error::Result<Vec<FilterPass>> = filters.into_iter().collect();
let filters = filters?;
Ok(filters.into_boxed_slice())
}
}

View file

@ -1,13 +1,13 @@
use std::sync::Arc;
use rustc_hash::FxHashMap;
use crate::graphics_pipeline::WgpuGraphicsPipeline;
use librashader_preprocess::ShaderSource;
use librashader_presets::ShaderPassConfig;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::back::wgsl::NagaWgslContext;
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;
use rustc_hash::FxHashMap;
use std::sync::Arc;
pub struct FilterPass {
pub reflection: ShaderReflection,

View file

@ -0,0 +1,7 @@
use wgpu::TextureView;
use librashader_common::Size;
pub(crate) struct OutputImage {
pub size: Size<u32>,
pub view: TextureView
}

View file

@ -1,28 +1,43 @@
use std::borrow::Cow;
use std::num::NonZeroU32;
use std::sync::Arc;
use wgpu::{BindGroup, BindGroupDescriptor, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize, Device, PipelineLayout, PushConstantRange, SamplerBindingType, ShaderModule, ShaderSource, ShaderStages, TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute, VertexBufferLayout};
use librashader_reflect::back::ShaderCompilerOutput;
use crate::{error, util};
use librashader_reflect::back::wgsl::NagaWgslContext;
use librashader_reflect::reflect::semantics::BufferReflection;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::ShaderReflection;
use crate::util;
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;
pub struct WgpuGraphicsPipeline {
vertex: ShaderModule,
fragment: ShaderModule
layout: PipelineLayoutObjects,
render_pipeline: wgpu::RenderPipeline,
}
pub struct PipelineLayoutObjects {
pub layout: PipelineLayout,
pub bind_group_layouts: Vec<BindGroupLayout>
layout: PipelineLayout,
bind_group_layouts: Vec<BindGroupLayout>,
fragment_entry_name: String,
vertex_entry_name: String,
vertex: ShaderModule,
fragment: ShaderModule,
device: Arc<wgpu::Device>
}
impl PipelineLayoutObjects {
pub fn new(
reflection: &ShaderReflection,
device: &Device
shader_assembly: &ShaderCompilerOutput<String, NagaWgslContext>,
device: Arc<wgpu::Device>,
) -> Self {
let vertex = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("vertex"),
source: ShaderSource::Wgsl(Cow::from(&shader_assembly.vertex)),
});
let fragment = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("fragment"),
source: ShaderSource::Wgsl(Cow::from(&shader_assembly.fragment)),
});
let mut bind_group_layouts = Vec::new();
@ -31,7 +46,9 @@ impl PipelineLayoutObjects {
let mut push_constant_range = Vec::new();
if let Some(push_meta) = reflection.push_constant.as_ref() && !push_meta.stage_mask.is_empty() {
if let Some(push_meta) = reflection.push_constant.as_ref()
&& !push_meta.stage_mask.is_empty()
{
let push_mask = util::binding_stage_to_wgpu_stage(push_meta.stage_mask);
if let Some(binding) = push_meta.binding {
@ -53,7 +70,9 @@ impl PipelineLayoutObjects {
}
}
if let Some(ubo_meta) = reflection.ubo.as_ref() && !ubo_meta.stage_mask.is_empty() {
if let Some(ubo_meta) = reflection.ubo.as_ref()
&& !ubo_meta.stage_mask.is_empty()
{
let ubo_mask = util::binding_stage_to_wgpu_stage(ubo_meta.stage_mask);
main_bindings.push(BindGroupLayoutEntry {
binding: ubo_meta.binding,
@ -99,80 +118,56 @@ impl PipelineLayoutObjects {
bind_group_layouts.push(main_bind_group);
bind_group_layouts.push(sampler_bind_group);
let bind_group_layout_refs = bind_group_layouts.iter().collect::<Vec<_>>();
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("shader pipeline layout"),
bind_group_layouts: &bind_group_layouts.as_ref(),
bind_group_layouts: &bind_group_layout_refs,
push_constant_ranges: &push_constant_range.as_ref(),
});
Self {
layout,
bind_group_layouts
bind_group_layouts,
fragment_entry_name: shader_assembly.context.fragment.entry_points[0]
.name
.clone(),
vertex_entry_name: shader_assembly.context.vertex.entry_points[0].name.clone(),
vertex,
fragment,
device,
}
}
}
impl WgpuGraphicsPipeline {
pub fn new(
device: &Device,
shader_assembly: &ShaderCompilerOutput<String, NagaWgslContext>,
reflection: &ShaderReflection,
render_pass_format: TextureFormat,
) -> Self {
let vertex = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("vertex"),
source: ShaderSource::Wgsl(Cow::from(&shader_assembly.vertex))
});
let fragment = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("fragment"),
source: ShaderSource::Wgsl(Cow::from(&shader_assembly.fragment))
});
let layout = PipelineLayoutObjects::new(reflection, device);
let vao_layout = 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,
},
],
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
pub fn create_pipeline(&self, framebuffer_format: TextureFormat) -> wgpu::RenderPipeline {
self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&layout.layout),
layout: Some(&self.layout),
vertex: wgpu::VertexState {
module: &vertex,
entry_point: &shader_assembly
.context
.vertex
.entry_points[0]
.name,
buffers: &[
vao_layout
],
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: &fragment,
entry_point: &shader_assembly
.context
.fragment
.entry_points[0]
.name,
module: &self.fragment,
entry_point: &self.fragment_entry_name,
targets: &[Some(wgpu::ColorTargetState {
format: render_pass_format,
format: framebuffer_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
@ -193,11 +188,64 @@ impl WgpuGraphicsPipeline {
alpha_to_coverage_enabled: false,
},
multiview: None,
});
Self {
vertex,
fragment,
}
})
}
}
impl WgpuGraphicsPipeline {
pub fn new(
device: Arc<wgpu::Device>,
shader_assembly: &ShaderCompilerOutput<String, NagaWgslContext>,
reflection: &ShaderReflection,
render_pass_format: TextureFormat,
) -> Self {
let layout = PipelineLayoutObjects::new(reflection, shader_assembly, device);
let render_pipeline = layout.create_pipeline(render_pass_format);
Self {
layout,
render_pipeline,
}
}
pub fn recompile(&mut self, format: TextureFormat) {
let render_pipeline = self.layout.create_pipeline(format);
self.render_pipeline = render_pipeline;
}
pub(crate) fn begin_rendering<'pass>(
&'pass self,
output: &RenderTarget<'pass, OutputImage>,
cmd: &'pass mut CommandEncoder
) -> RenderPass<'pass> {
let mut render_pass = cmd.begin_render_pass(&RenderPassDescriptor {
label: Some("librashader"),
color_attachments: &[Some(RenderPassColorAttachment {
view: &output.output.view,
resolve_target: None,
ops: Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_viewport(
output.x,
output.y,
output.output.size.width as f32,
output.output.size.height as f32,
1.0,
1.0
);
render_pass
}
}

View file

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

View file

@ -0,0 +1,60 @@
use wgpu::{ImageDataLayout, Label, TextureDescriptor};
use wgpu::util::DeviceExt;
use librashader_presets::TextureConfig;
use librashader_runtime::image::{BGRA8, Image};
use librashader_runtime::scaling::MipmapSize;
pub(crate) struct LutTexture {
texture: wgpu::Texture,
}
impl LutTexture {
pub fn new(
device: &wgpu::Device,
queue: &mut wgpu::Queue,
_cmd: &mut wgpu::CommandEncoder,
image: Image,
config: &TextureConfig
) -> LutTexture {
let texture = device.create_texture(&TextureDescriptor {
label: Some(&config.name),
size: image.size.into(),
mip_level_count: if config.mipmap {
image.size.calculate_miplevels()
} else {
1
},
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_DST
// need render attachment for mipmaps...
| wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
});
queue.write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&image.bytes,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * image.size.width),
rows_per_image: None,
},
image.size.into()
);
// todo: mipmaps
Self {
texture
}
}
}

View file

@ -1,5 +1,5 @@
use librashader_common::{FilterMode, WrapMode};
use crate::error;
use librashader_common::{FilterMode, WrapMode};
use rustc_hash::FxHashMap;
use std::sync::Arc;
use wgpu::{Sampler, SamplerBorderColor, SamplerDescriptor};
@ -22,7 +22,7 @@ impl SamplerSet {
}
}
pub fn new(device: &wgpu::Device) -> error::Result<SamplerSet> {
pub fn new(device: &wgpu::Device) -> SamplerSet {
let mut samplers = FxHashMap::default();
let wrap_modes = &[
WrapMode::ClampToBorder,
@ -37,18 +37,18 @@ impl SamplerSet {
(*wrap_mode, *filter_mode, *mipmap_filter),
device.create_sampler(&SamplerDescriptor {
label: None,
address_mode_u: wrap_mode.into(),
address_mode_v: wrap_mode.into(),
address_mode_w: wrap_mode.into(),
mag_filter: filter_mode.into(),
min_filter: filter_mode.into(),
mipmap_filter: mipmap_filter.into(),
address_mode_u: (*wrap_mode).into(),
address_mode_v: (*wrap_mode).into(),
address_mode_w: (*wrap_mode).into(),
mag_filter: (*filter_mode).into(),
min_filter: (*filter_mode).into(),
mipmap_filter: (*mipmap_filter).into(),
lod_min_clamp: 0.0,
lod_max_clamp: 1000.0,
compare: None,
anisotropy_clamp: 1,
border_color: Some(SamplerBorderColor::TransparentBlack),
})
}),
);
}
}
@ -56,6 +56,6 @@ impl SamplerSet {
// assert all samplers were created.
assert_eq!(samplers.len(), wrap_modes.len() * 2 * 2);
Ok(SamplerSet { samplers })
SamplerSet { samplers }
}
}

View file

@ -0,0 +1,95 @@
use std::sync::Arc;
use wgpu::TextureFormat;
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::scaling::{MipmapSize, ViewportSize};
pub struct OwnedImage {
device: Arc<wgpu::Device>,
pub image: wgpu::Texture,
pub view: wgpu::TextureView,
pub max_miplevels: u32,
pub levels: u32,
pub size: Size<u32>,
pub format: wgpu::TextureFormat,
}
pub struct InputImage {
/// A handle to the `VkImage`.
pub image: wgpu::Texture,
pub image_view: wgpu::TextureView,
pub wrap_mode: WrapMode,
pub filter_mode: FilterMode,
pub mip_filter: FilterMode,
}
impl OwnedImage {
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);
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: size.into(),
mip_level_count: std::cmp::min(max_miplevels, size.calculate_miplevels()),
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[format.into()],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor {
label: None,
format: Some(format),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
Self {
device,
image: texture,
view,
max_miplevels,
levels: std::cmp::min(max_miplevels, size.calculate_miplevels()),
size,
format,
}
}
pub fn scale(
&mut self,
scaling: Scale2D,
format: ImageFormat,
viewport_size: &Size<u32>,
source_size: &Size<u32>,
mipmap: bool,
) -> Size<u32> {
let size = source_size.scale_viewport(scaling, *viewport_size);
let format: Option<wgpu::TextureFormat> = format.into();
let format = format.unwrap_or(wgpu::TextureFormat::Rgba8Unorm);
if self.size != size
|| (mipmap && self.max_miplevels == 1)
|| (!mipmap && self.max_miplevels != 1)
|| format != self.format
{
let mut new = OwnedImage::new(Arc::clone(&self.device), size, self.max_miplevels, format.into());
std::mem::swap(self, &mut new);
}
size
}
}

View file

@ -1,5 +1,5 @@
use wgpu::ShaderStages;
use librashader_reflect::reflect::semantics::BindingStage;
use wgpu::ShaderStages;
pub fn binding_stage_to_wgpu_stage(stage_mask: BindingStage) -> ShaderStages {
let mut mask = ShaderStages::empty();

View file

@ -4,11 +4,11 @@ use winit::{
window::{Window, WindowBuilder},
};
use librashader_presets::ShaderPreset;
use librashader_runtime_wgpu::FilterChainWGPU;
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::*;
@ -35,30 +35,29 @@ impl Vertex {
shader_location: 1,
format: wgpu::VertexFormat::Float32x3,
},
]
],
}
}
}
const VERTICES: &[Vertex] = &[
Vertex { // top
Vertex {
// top
position: [0.0, 0.5, 0.0],
color: [1.0, 0.0, 0.0],
},
Vertex { // bottom left
Vertex {
// bottom left
position: [-0.5, -0.5, 0.0],
color: [0.0, 1.0, 0.0],
},
Vertex { // bottom right
Vertex {
// bottom right
position: [0.5, -0.5, 0.0],
color: [0.0, 0.0, 1.0],
},
];
struct State {
surface: wgpu::Surface,
device: wgpu::Device,
@ -71,7 +70,7 @@ struct State {
vertex_buffer: wgpu::Buffer,
num_vertices: u32,
chain: FilterChainWGPU
chain: FilterChainWGPU,
}
impl State {
async fn new(window: &Window) -> Self {
@ -113,7 +112,8 @@ impl State {
view_formats: vec![],
};
let preset = ShaderPreset::try_parse("../test/shaders_slang/crt/crt-royale.slangp").unwrap();
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 {
@ -132,9 +132,7 @@ impl State {
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[
Vertex::desc(),
],
buffers: &[Vertex::desc()],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
@ -163,14 +161,11 @@ impl State {
multiview: None,
});
let vertex_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsages::VERTEX,
},
);
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsages::VERTEX,
});
let clear_color = wgpu::Color {
r: 0.1,
@ -189,7 +184,7 @@ impl State {
render_pipeline,
vertex_buffer,
num_vertices,
chain
chain,
}
}
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
@ -223,7 +218,7 @@ impl State {
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(self.clear_color),
store: wgpu::StoreOp::Store,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
@ -251,8 +246,6 @@ pub fn run() {
.build();
let window = WindowBuilder::new().build(&event_loop).unwrap();
pollster::block_on(async {
let mut state = State::new(&window).await;
event_loop.run(move |event, _, control_flow| {
@ -267,11 +260,11 @@ pub fn run() {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
@ -302,6 +295,4 @@ pub fn run() {
}
});
});
}

View file

@ -1,7 +1,6 @@
mod hello_triangle;
#[test]
fn triangle_wgpu()
{
fn triangle_wgpu() {
hello_triangle::run()
}