2024-02-07 10:47:05 +11:00
|
|
|
use crate::framebuffer::WgpuOutputView;
|
2024-02-06 17:45:31 +11:00
|
|
|
use crate::util;
|
2024-08-01 16:06:28 +10:00
|
|
|
use librashader_cache::cache_pipeline;
|
2023-12-14 11:24:00 +11:00
|
|
|
use librashader_reflect::back::wgsl::NagaWgslContext;
|
2023-12-16 22:28:41 +11:00
|
|
|
use librashader_reflect::back::ShaderCompilerOutput;
|
2023-11-30 17:49:22 +11:00
|
|
|
use librashader_reflect::reflect::ShaderReflection;
|
2024-02-22 16:41:29 +11:00
|
|
|
use librashader_runtime::quad::VertexInput;
|
2024-02-22 16:02:09 +11:00
|
|
|
use librashader_runtime::render_target::RenderTarget;
|
2023-12-16 22:28:41 +11:00
|
|
|
use std::borrow::Cow;
|
2024-08-01 16:06:28 +10:00
|
|
|
use std::convert::Infallible;
|
2023-12-16 22:28:41 +11:00
|
|
|
use std::sync::Arc;
|
2024-02-06 16:29:45 +11:00
|
|
|
use wgpu::{
|
2024-02-06 17:45:31 +11:00
|
|
|
BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType,
|
|
|
|
BufferBindingType, BufferSize, CommandEncoder, Operations, PipelineLayout, PushConstantRange,
|
|
|
|
RenderPass, RenderPassColorAttachment, RenderPassDescriptor, SamplerBindingType, ShaderModule,
|
|
|
|
ShaderSource, ShaderStages, TextureFormat, TextureSampleType, TextureViewDimension,
|
2024-02-06 16:29:45 +11:00
|
|
|
VertexBufferLayout,
|
|
|
|
};
|
2023-11-30 17:49:22 +11:00
|
|
|
|
|
|
|
pub struct WgpuGraphicsPipeline {
|
2024-01-26 12:05:31 +11:00
|
|
|
pub layout: PipelineLayoutObjects,
|
2023-12-16 22:28:41 +11:00
|
|
|
render_pipeline: wgpu::RenderPipeline,
|
2024-08-01 16:06:28 +10:00
|
|
|
cache: Option<wgpu::PipelineCache>,
|
2024-02-06 16:29:45 +11:00
|
|
|
pub format: wgpu::TextureFormat,
|
2023-11-30 17:49:22 +11:00
|
|
|
}
|
|
|
|
|
2023-12-13 11:02:49 +11:00
|
|
|
pub struct PipelineLayoutObjects {
|
2023-12-16 22:28:41 +11:00
|
|
|
layout: PipelineLayout,
|
2024-01-26 12:05:31 +11:00
|
|
|
pub main_bind_group_layout: BindGroupLayout,
|
|
|
|
pub sampler_bind_group_layout: BindGroupLayout,
|
2023-12-16 22:28:41 +11:00
|
|
|
fragment_entry_name: String,
|
|
|
|
vertex_entry_name: String,
|
|
|
|
vertex: ShaderModule,
|
|
|
|
fragment: ShaderModule,
|
2024-02-06 16:29:45 +11:00
|
|
|
device: Arc<wgpu::Device>,
|
2023-12-13 11:02:49 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PipelineLayoutObjects {
|
|
|
|
pub fn new(
|
|
|
|
reflection: &ShaderReflection,
|
2023-12-16 22:28:41 +11:00
|
|
|
shader_assembly: &ShaderCompilerOutput<String, NagaWgslContext>,
|
|
|
|
device: Arc<wgpu::Device>,
|
2023-12-13 11:02:49 +11:00
|
|
|
) -> Self {
|
2023-12-16 22:28:41 +11:00
|
|
|
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)),
|
|
|
|
});
|
2023-12-13 11:02:49 +11:00
|
|
|
|
2023-12-14 11:24:00 +11:00
|
|
|
let mut main_bindings = Vec::new();
|
|
|
|
let mut sampler_bindings = Vec::new();
|
|
|
|
|
|
|
|
let mut push_constant_range = Vec::new();
|
2023-12-13 11:02:49 +11:00
|
|
|
|
2023-12-16 22:28:41 +11:00
|
|
|
if let Some(push_meta) = reflection.push_constant.as_ref()
|
|
|
|
&& !push_meta.stage_mask.is_empty()
|
|
|
|
{
|
2023-12-14 11:24:00 +11:00
|
|
|
let push_mask = util::binding_stage_to_wgpu_stage(push_meta.stage_mask);
|
|
|
|
|
|
|
|
if let Some(binding) = push_meta.binding {
|
|
|
|
main_bindings.push(BindGroupLayoutEntry {
|
|
|
|
binding,
|
|
|
|
visibility: push_mask,
|
2023-12-13 11:02:49 +11:00
|
|
|
ty: BindingType::Buffer {
|
|
|
|
ty: BufferBindingType::Uniform,
|
|
|
|
has_dynamic_offset: false,
|
2023-12-14 11:24:00 +11:00
|
|
|
min_binding_size: BufferSize::new(push_meta.size as u64),
|
2023-12-13 11:02:49 +11:00
|
|
|
},
|
2023-12-14 11:24:00 +11:00
|
|
|
count: None,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
push_constant_range.push(PushConstantRange {
|
|
|
|
stages: push_mask,
|
|
|
|
range: 0..push_meta.size,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-16 22:28:41 +11:00
|
|
|
if let Some(ubo_meta) = reflection.ubo.as_ref()
|
|
|
|
&& !ubo_meta.stage_mask.is_empty()
|
|
|
|
{
|
2023-12-14 11:24:00 +11:00
|
|
|
let ubo_mask = util::binding_stage_to_wgpu_stage(ubo_meta.stage_mask);
|
|
|
|
main_bindings.push(BindGroupLayoutEntry {
|
|
|
|
binding: ubo_meta.binding,
|
|
|
|
visibility: ubo_mask,
|
|
|
|
ty: BindingType::Buffer {
|
|
|
|
ty: BufferBindingType::Uniform,
|
|
|
|
has_dynamic_offset: false,
|
|
|
|
min_binding_size: BufferSize::new(ubo_meta.size as u64),
|
|
|
|
},
|
|
|
|
count: None,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
for texture in reflection.meta.texture_meta.values() {
|
|
|
|
main_bindings.push(BindGroupLayoutEntry {
|
|
|
|
binding: texture.binding,
|
|
|
|
visibility: ShaderStages::FRAGMENT,
|
|
|
|
ty: BindingType::Texture {
|
|
|
|
sample_type: TextureSampleType::Float { filterable: true },
|
|
|
|
view_dimension: TextureViewDimension::D2,
|
|
|
|
multisampled: false,
|
|
|
|
},
|
|
|
|
count: None,
|
2023-12-13 11:02:49 +11:00
|
|
|
});
|
|
|
|
|
2023-12-14 11:24:00 +11:00
|
|
|
sampler_bindings.push(BindGroupLayoutEntry {
|
|
|
|
binding: texture.binding,
|
|
|
|
visibility: ShaderStages::FRAGMENT,
|
|
|
|
ty: BindingType::Sampler(SamplerBindingType::Filtering),
|
|
|
|
count: None,
|
|
|
|
})
|
2023-12-13 11:02:49 +11:00
|
|
|
}
|
2023-12-14 11:24:00 +11:00
|
|
|
let main_bind_group = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
|
|
|
label: Some("bind group 0"),
|
|
|
|
entries: &main_bindings,
|
|
|
|
});
|
|
|
|
|
|
|
|
let sampler_bind_group = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
|
|
|
label: Some("bind group 1"),
|
|
|
|
entries: &sampler_bindings,
|
|
|
|
});
|
|
|
|
|
2024-01-26 12:05:31 +11:00
|
|
|
let bind_group_layout_refs = [&main_bind_group, &sampler_bind_group];
|
2023-12-16 22:28:41 +11:00
|
|
|
|
2023-12-13 11:02:49 +11:00
|
|
|
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
|
|
label: Some("shader pipeline layout"),
|
2023-12-16 22:28:41 +11:00
|
|
|
bind_group_layouts: &bind_group_layout_refs,
|
2023-12-14 11:24:00 +11:00
|
|
|
push_constant_ranges: &push_constant_range.as_ref(),
|
2023-12-13 11:02:49 +11:00
|
|
|
});
|
|
|
|
|
|
|
|
Self {
|
|
|
|
layout,
|
2024-02-02 10:03:21 +11:00
|
|
|
main_bind_group_layout: main_bind_group,
|
|
|
|
sampler_bind_group_layout: sampler_bind_group,
|
2023-12-16 22:28:41 +11:00
|
|
|
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,
|
2023-12-13 11:02:49 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-01 16:06:28 +10:00
|
|
|
pub fn create_pipeline(
|
|
|
|
&self,
|
|
|
|
framebuffer_format: TextureFormat,
|
|
|
|
cache: Option<&wgpu::PipelineCache>,
|
|
|
|
) -> wgpu::RenderPipeline {
|
2024-02-06 16:29:45 +11:00
|
|
|
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,
|
2024-05-01 07:21:48 +10:00
|
|
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
2024-02-06 16:29:45 +11:00
|
|
|
buffers: &[VertexBufferLayout {
|
2024-02-22 16:41:29 +11:00
|
|
|
array_stride: std::mem::size_of::<VertexInput>() as wgpu::BufferAddress,
|
2024-02-06 16:29:45 +11:00
|
|
|
step_mode: wgpu::VertexStepMode::Vertex,
|
|
|
|
attributes: &[
|
|
|
|
wgpu::VertexAttribute {
|
2024-02-16 10:57:51 +11:00
|
|
|
format: wgpu::VertexFormat::Float32x4,
|
2024-02-22 16:41:29 +11:00
|
|
|
offset: bytemuck::offset_of!(VertexInput, position)
|
2024-02-16 10:57:51 +11:00
|
|
|
as wgpu::BufferAddress,
|
2024-02-06 16:29:45 +11:00
|
|
|
shader_location: 0,
|
|
|
|
},
|
|
|
|
wgpu::VertexAttribute {
|
|
|
|
format: wgpu::VertexFormat::Float32x2,
|
2024-02-22 16:41:29 +11:00
|
|
|
offset: bytemuck::offset_of!(VertexInput, texcoord)
|
2024-02-16 10:57:51 +11:00
|
|
|
as wgpu::BufferAddress,
|
2024-02-06 16:29:45 +11:00
|
|
|
shader_location: 1,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}],
|
|
|
|
},
|
|
|
|
fragment: Some(wgpu::FragmentState {
|
|
|
|
module: &self.fragment,
|
|
|
|
entry_point: &self.fragment_entry_name,
|
2024-05-01 07:21:48 +10:00
|
|
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
2024-02-06 16:29:45 +11:00
|
|
|
targets: &[Some(wgpu::ColorTargetState {
|
|
|
|
format: framebuffer_format,
|
2024-02-16 10:57:51 +11:00
|
|
|
blend: None,
|
2024-02-06 16:29:45 +11:00
|
|
|
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,
|
2024-08-01 16:06:28 +10:00
|
|
|
cache,
|
2024-02-06 16:29:45 +11:00
|
|
|
})
|
2023-12-16 22:28:41 +11:00
|
|
|
}
|
|
|
|
}
|
2023-12-13 11:02:49 +11:00
|
|
|
|
2023-12-16 22:28:41 +11:00
|
|
|
impl WgpuGraphicsPipeline {
|
|
|
|
pub fn new(
|
|
|
|
device: Arc<wgpu::Device>,
|
|
|
|
shader_assembly: &ShaderCompilerOutput<String, NagaWgslContext>,
|
|
|
|
reflection: &ShaderReflection,
|
|
|
|
render_pass_format: TextureFormat,
|
2024-08-01 16:06:28 +10:00
|
|
|
adapter_info: Option<&wgpu::AdapterInfo>,
|
|
|
|
bypass_cache: bool,
|
2023-12-16 22:28:41 +11:00
|
|
|
) -> Self {
|
2024-08-01 16:06:28 +10:00
|
|
|
let cache = if bypass_cache {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
let name = adapter_info
|
|
|
|
.and_then(|o| wgpu::util::pipeline_cache_key(o))
|
|
|
|
.unwrap_or_else(|| String::from("wgpu"));
|
|
|
|
|
|
|
|
cache_pipeline(
|
|
|
|
&name,
|
|
|
|
&[
|
|
|
|
&shader_assembly.vertex.as_str(),
|
|
|
|
&shader_assembly.fragment.as_str(),
|
|
|
|
],
|
|
|
|
|pipeline_data| {
|
|
|
|
let descriptor = wgpu::PipelineCacheDescriptor {
|
|
|
|
label: Some("librashader-wgpu"),
|
|
|
|
data: pipeline_data.as_deref(),
|
|
|
|
fallback: true,
|
|
|
|
};
|
|
|
|
|
|
|
|
let cache = unsafe { device.create_pipeline_cache(&descriptor) };
|
|
|
|
Ok::<_, Infallible>(cache)
|
|
|
|
},
|
|
|
|
|cache| Ok(cache.get_data()),
|
|
|
|
bypass_cache,
|
|
|
|
)
|
|
|
|
.ok()
|
|
|
|
};
|
|
|
|
|
2023-12-16 22:28:41 +11:00
|
|
|
let layout = PipelineLayoutObjects::new(reflection, shader_assembly, device);
|
2024-08-01 16:06:28 +10:00
|
|
|
let render_pipeline = layout.create_pipeline(render_pass_format, cache.as_ref());
|
2023-11-30 17:49:22 +11:00
|
|
|
Self {
|
2023-12-16 22:28:41 +11:00
|
|
|
layout,
|
|
|
|
render_pipeline,
|
2024-02-06 16:29:45 +11:00
|
|
|
format: render_pass_format,
|
2024-08-01 16:06:28 +10:00
|
|
|
cache,
|
2023-11-30 17:49:22 +11:00
|
|
|
}
|
|
|
|
}
|
2023-12-16 22:28:41 +11:00
|
|
|
|
|
|
|
pub fn recompile(&mut self, format: TextureFormat) {
|
2024-08-01 16:06:28 +10:00
|
|
|
let render_pipeline = self.layout.create_pipeline(format, self.cache.as_ref());
|
2023-12-16 22:28:41 +11:00
|
|
|
self.render_pipeline = render_pipeline;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn begin_rendering<'pass>(
|
|
|
|
&'pass self,
|
2024-02-07 10:47:05 +11:00
|
|
|
output: &RenderTarget<'pass, WgpuOutputView>,
|
2024-02-06 16:29:45 +11:00
|
|
|
cmd: &'pass mut CommandEncoder,
|
2023-12-16 22:28:41 +11:00
|
|
|
) -> 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,
|
|
|
|
});
|
|
|
|
|
2024-01-26 12:05:31 +11:00
|
|
|
render_pass.set_scissor_rect(
|
|
|
|
output.x as u32,
|
|
|
|
output.y as u32,
|
|
|
|
output.output.size.width,
|
2024-02-06 16:29:45 +11:00
|
|
|
output.output.size.height,
|
2024-01-26 12:05:31 +11:00
|
|
|
);
|
2024-02-06 16:29:45 +11:00
|
|
|
|
2023-12-16 22:28:41 +11:00
|
|
|
render_pass.set_viewport(
|
|
|
|
output.x,
|
|
|
|
output.y,
|
|
|
|
output.output.size.width as f32,
|
|
|
|
output.output.size.height as f32,
|
2024-02-17 18:42:03 +11:00
|
|
|
0.0,
|
2024-02-06 16:29:45 +11:00
|
|
|
1.0,
|
2023-12-16 22:28:41 +11:00
|
|
|
);
|
|
|
|
|
2024-02-06 16:29:45 +11:00
|
|
|
render_pass.set_pipeline(&self.render_pipeline);
|
2023-12-16 22:28:41 +11:00
|
|
|
render_pass
|
|
|
|
}
|
|
|
|
}
|