234 lines
8.3 KiB
Rust
234 lines
8.3 KiB
Rust
use std::borrow::Cow;
|
|
use std::sync::atomic::AtomicBool;
|
|
use std::sync::Arc;
|
|
|
|
use pathfinder_color::ColorU;
|
|
use pathfinder_geometry::rect::RectF;
|
|
use pathfinder_geometry::vector::vec2f;
|
|
use wgpu::util::BufferInitDescriptor;
|
|
use wgpu::{BindGroupLayout, ColorTargetState, Device, RenderPass, RenderPipeline};
|
|
|
|
use super::util::create_buffer_init;
|
|
use crate::rendering::get_best_dash_gap;
|
|
use crate::rendering::wgpu::shader_types::BorderWidth;
|
|
use crate::rendering::wgpu::{resources, shader_types};
|
|
use crate::scene::Layer;
|
|
use crate::Scene;
|
|
|
|
pub(super) struct Pipeline {
|
|
render_pipeline: RenderPipeline,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub(super) struct PerFrameState {
|
|
rect_data: Vec<shader_types::RectData>,
|
|
buffer: Option<wgpu::Buffer>,
|
|
}
|
|
|
|
pub(super) struct LayerState {
|
|
start_offset: usize,
|
|
len: usize,
|
|
}
|
|
|
|
impl Pipeline {
|
|
pub(super) fn new(
|
|
uniform_bind_group_layout: &BindGroupLayout,
|
|
device: &Device,
|
|
color_target: ColorTargetState,
|
|
) -> Self {
|
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
label: Some("Rect Shader"),
|
|
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(
|
|
"../shaders/rect_shader.wgsl"
|
|
))),
|
|
});
|
|
|
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
label: Some("Rect pipeline layout"),
|
|
bind_group_layouts: &[Some(uniform_bind_group_layout)],
|
|
immediate_size: 0,
|
|
});
|
|
|
|
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
label: Some("Rect render pipeline"),
|
|
layout: Some(&pipeline_layout),
|
|
vertex: wgpu::VertexState {
|
|
module: &shader,
|
|
entry_point: Some("vs_main"),
|
|
buffers: &[shader_types::Vertex::desc(), shader_types::RectData::desc()],
|
|
compilation_options: Default::default(),
|
|
},
|
|
fragment: Some(wgpu::FragmentState {
|
|
module: &shader,
|
|
entry_point: Some("rect_fs_main"),
|
|
targets: &[Some(color_target)],
|
|
compilation_options: Default::default(),
|
|
}),
|
|
primitive: wgpu::PrimitiveState::default(),
|
|
depth_stencil: None,
|
|
multisample: wgpu::MultisampleState::default(),
|
|
multiview_mask: None,
|
|
// Don't use a pipeline cache. Most desktop GPU drivers have their own internal caches,
|
|
// so we are unlikely to get much value out of this for the platforms Warp supports.
|
|
cache: None,
|
|
});
|
|
|
|
Self { render_pipeline }
|
|
}
|
|
|
|
pub(super) fn initialize_for_layer(
|
|
&self,
|
|
layer: &Layer,
|
|
scene: &Scene,
|
|
per_frame_state: &mut PerFrameState,
|
|
) -> Option<LayerState> {
|
|
if layer.rects.is_empty() {
|
|
// It's a mac assertion error to create an empty metal buffer, so exit early
|
|
return None;
|
|
}
|
|
|
|
let scale_factor = scene.scale_factor();
|
|
let mut rect_instance_data = Vec::with_capacity(layer.rects.len());
|
|
for rect in &layer.rects {
|
|
let bounds = rect.bounds * scale_factor;
|
|
|
|
if let Some(drop_shadow) = rect.drop_shadow {
|
|
let sigma = drop_shadow.blur_radius * scale_factor;
|
|
let padding = drop_shadow.spread_radius * scale_factor;
|
|
let shadow_origin = bounds.origin() + drop_shadow.offset * scale_factor - padding;
|
|
let shadow_size = bounds.size() + vec2f(2. * padding, 2. * padding);
|
|
|
|
let min_dimension = f32::min(shadow_size.x(), shadow_size.y());
|
|
let corner_radius = crate::rendering::CornerRadius::from_ui_corner_radius(
|
|
rect.corner_radius,
|
|
scale_factor,
|
|
min_dimension,
|
|
);
|
|
let bounds = RectF::new(shadow_origin, shadow_size);
|
|
let shadow_color = shader_types::Color {
|
|
start: vec2f(0., 0.).into(),
|
|
start_color: drop_shadow.color.into(),
|
|
end: vec2f(1., 0.).into(),
|
|
end_color: drop_shadow.color.into(),
|
|
};
|
|
|
|
let border_color = shader_types::Color {
|
|
start: vec2f(0., 0.).into(),
|
|
start_color: ColorU::transparent_black().into(),
|
|
end: vec2f(1., 0.).into(),
|
|
end_color: ColorU::transparent_black().into(),
|
|
};
|
|
|
|
rect_instance_data.push(shader_types::RectData::new(
|
|
bounds,
|
|
shadow_color,
|
|
border_color,
|
|
corner_radius.clone(),
|
|
BorderWidth::default(),
|
|
sigma,
|
|
padding,
|
|
0.,
|
|
vec2f(0., 0.),
|
|
));
|
|
}
|
|
|
|
let min_dimension = f32::min(bounds.height(), bounds.width());
|
|
let corner_radius = crate::rendering::CornerRadius::from_ui_corner_radius(
|
|
rect.corner_radius,
|
|
scale_factor,
|
|
min_dimension,
|
|
);
|
|
let background_color = shader_types::Color {
|
|
start: rect.background.start().into(),
|
|
start_color: (rect.background.start_color().into()),
|
|
end: rect.background.end().into(),
|
|
end_color: (rect.background.end_color().into()),
|
|
};
|
|
|
|
let border_color = shader_types::Color {
|
|
start: rect.border.color.start().into(),
|
|
start_color: (rect.border.color.start_color().into()),
|
|
end: rect.border.color.end().into(),
|
|
end_color: (rect.border.color.end_color().into()),
|
|
};
|
|
|
|
let border_width = shader_types::BorderWidth {
|
|
top: rect.border.top_width() * scale_factor,
|
|
right: rect.border.right_width() * scale_factor,
|
|
bottom: rect.border.bottom_width() * scale_factor,
|
|
left: rect.border.left_width() * scale_factor,
|
|
};
|
|
|
|
let dash = rect
|
|
.border
|
|
.dash
|
|
.map(|mut dash| {
|
|
dash.dash_length *= scale_factor;
|
|
dash.gap_length *= scale_factor;
|
|
dash
|
|
})
|
|
.unwrap_or_default();
|
|
let horizontal_gap = get_best_dash_gap(bounds.width(), dash);
|
|
let vertical_gap = get_best_dash_gap(bounds.height(), dash);
|
|
let gap_lengths = vec2f(horizontal_gap, vertical_gap);
|
|
|
|
let rect_data = shader_types::RectData::new(
|
|
bounds,
|
|
background_color,
|
|
border_color,
|
|
corner_radius,
|
|
border_width,
|
|
0.,
|
|
0.,
|
|
dash.dash_length,
|
|
gap_lengths,
|
|
);
|
|
rect_instance_data.push(rect_data);
|
|
}
|
|
|
|
let start_offset = per_frame_state.rect_data.len();
|
|
let len = rect_instance_data.len();
|
|
per_frame_state.rect_data.append(&mut rect_instance_data);
|
|
|
|
Some(LayerState { start_offset, len })
|
|
}
|
|
|
|
pub(super) fn finalize_per_frame_state(
|
|
per_frame_state: &mut PerFrameState,
|
|
device: &Device,
|
|
device_lost: &Arc<AtomicBool>,
|
|
) {
|
|
per_frame_state.buffer = create_buffer_init(
|
|
device,
|
|
device_lost,
|
|
&BufferInitDescriptor {
|
|
label: Some("Rect instance buffer"),
|
|
contents: bytemuck::cast_slice(&per_frame_state.rect_data),
|
|
usage: wgpu::BufferUsages::VERTEX,
|
|
},
|
|
)
|
|
.ok();
|
|
}
|
|
|
|
pub(super) fn draw<'a>(
|
|
&'a self,
|
|
render_pass: &mut RenderPass<'a>,
|
|
layer_state: &LayerState,
|
|
per_frame_state: &'a PerFrameState,
|
|
) {
|
|
let Some(buffer) = per_frame_state.buffer.as_ref() else {
|
|
return;
|
|
};
|
|
|
|
render_pass.set_pipeline(&self.render_pipeline);
|
|
render_pass.set_vertex_buffer(1, buffer.slice(..));
|
|
|
|
let end_offset = layer_state.start_offset + layer_state.len;
|
|
render_pass.draw_indexed(
|
|
0..resources::quad::INDICES.len() as u32,
|
|
0,
|
|
layer_state.start_offset as u32..end_offset as u32,
|
|
);
|
|
}
|
|
}
|