summaryrefslogtreecommitdiffstats
path: root/wgpu/src
diff options
context:
space:
mode:
Diffstat (limited to 'wgpu/src')
-rw-r--r--wgpu/src/backend.rs205
-rw-r--r--wgpu/src/buffer.rs2
-rw-r--r--wgpu/src/color.rs10
-rw-r--r--wgpu/src/geometry.rs24
-rw-r--r--wgpu/src/image.rs129
-rw-r--r--wgpu/src/image/atlas.rs12
-rw-r--r--wgpu/src/image/vector.rs2
-rw-r--r--wgpu/src/layer.rs71
-rw-r--r--wgpu/src/layer/image.rs3
-rw-r--r--wgpu/src/layer/pipeline.rs17
-rw-r--r--wgpu/src/layer/text.rs32
-rw-r--r--wgpu/src/lib.rs18
-rw-r--r--wgpu/src/primitive.rs9
-rw-r--r--wgpu/src/primitive/pipeline.rs116
-rw-r--r--wgpu/src/quad/gradient.rs19
-rw-r--r--wgpu/src/quad/solid.rs6
-rw-r--r--wgpu/src/settings.rs6
-rw-r--r--wgpu/src/shader/color/linear_rgb.wgsl3
-rw-r--r--wgpu/src/shader/color/oklab.wgsl26
-rw-r--r--wgpu/src/shader/quad.wgsl306
-rw-r--r--wgpu/src/shader/quad/gradient.wgsl205
-rw-r--r--wgpu/src/shader/quad/solid.wgsl99
-rw-r--r--wgpu/src/shader/triangle.wgsl160
-rw-r--r--wgpu/src/shader/triangle/gradient.wgsl134
-rw-r--r--wgpu/src/shader/triangle/solid.wgsl24
-rw-r--r--wgpu/src/text.rs547
-rw-r--r--wgpu/src/triangle.rs54
-rw-r--r--wgpu/src/triangle/msaa.rs4
-rw-r--r--wgpu/src/window/compositor.rs45
29 files changed, 1208 insertions, 1080 deletions
diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 4a0c54f0..25134d68 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -1,8 +1,8 @@
-use crate::core;
-use crate::core::{Color, Font, Point, Size};
+use crate::core::{Color, Size};
use crate::graphics::backend;
use crate::graphics::color;
use crate::graphics::{Transformation, Viewport};
+use crate::primitive::pipeline;
use crate::primitive::{self, Primitive};
use crate::quad;
use crate::text;
@@ -26,12 +26,10 @@ pub struct Backend {
quad_pipeline: quad::Pipeline,
text_pipeline: text::Pipeline,
triangle_pipeline: triangle::Pipeline,
+ pipeline_storage: pipeline::Storage,
#[cfg(any(feature = "image", feature = "svg"))]
image_pipeline: image::Pipeline,
-
- default_font: Font,
- default_text_size: f32,
}
impl Backend {
@@ -54,12 +52,10 @@ impl Backend {
quad_pipeline,
text_pipeline,
triangle_pipeline,
+ pipeline_storage: pipeline::Storage::default(),
#[cfg(any(feature = "image", feature = "svg"))]
image_pipeline,
-
- default_font: settings.default_font,
- default_text_size: settings.default_text_size,
}
}
@@ -73,6 +69,7 @@ impl Backend {
queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
clear_color: Option<Color>,
+ format: wgpu::TextureFormat,
frame: &wgpu::TextureView,
primitives: &[Primitive],
viewport: &Viewport,
@@ -87,25 +84,22 @@ impl Backend {
let transformation = viewport.projection();
let mut layers = Layer::generate(primitives, viewport);
- layers.push(Layer::overlay(overlay_text, viewport));
+
+ if !overlay_text.is_empty() {
+ layers.push(Layer::overlay(overlay_text, viewport));
+ }
self.prepare(
device,
queue,
+ format,
encoder,
scale_factor,
+ target_size,
transformation,
&layers,
);
- while !self.prepare_text(
- device,
- queue,
- scale_factor,
- target_size,
- &layers,
- ) {}
-
self.render(
device,
encoder,
@@ -124,44 +118,14 @@ impl Backend {
self.image_pipeline.end_frame();
}
- fn prepare_text(
- &mut self,
- device: &wgpu::Device,
- queue: &wgpu::Queue,
- scale_factor: f32,
- target_size: Size<u32>,
- layers: &[Layer<'_>],
- ) -> bool {
- for layer in layers {
- let bounds = (layer.bounds * scale_factor).snap();
-
- if bounds.width < 1 || bounds.height < 1 {
- continue;
- }
-
- if !layer.text.is_empty()
- && !self.text_pipeline.prepare(
- device,
- queue,
- &layer.text,
- layer.bounds,
- scale_factor,
- target_size,
- )
- {
- return false;
- }
- }
-
- true
- }
-
fn prepare(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
+ format: wgpu::TextureFormat,
_encoder: &mut wgpu::CommandEncoder,
scale_factor: f32,
+ target_size: Size<u32>,
transformation: Transformation,
layers: &[Layer<'_>],
) {
@@ -210,6 +174,31 @@ impl Backend {
);
}
}
+
+ if !layer.text.is_empty() {
+ self.text_pipeline.prepare(
+ device,
+ queue,
+ &layer.text,
+ layer.bounds,
+ scale_factor,
+ target_size,
+ );
+ }
+
+ if !layer.pipelines.is_empty() {
+ for pipeline in &layer.pipelines {
+ pipeline.primitive.prepare(
+ format,
+ device,
+ queue,
+ pipeline.bounds,
+ target_size,
+ scale_factor,
+ &mut self.pipeline_storage,
+ );
+ }
+ }
}
}
@@ -233,7 +222,7 @@ impl Backend {
let mut render_pass = ManuallyDrop::new(encoder.begin_render_pass(
&wgpu::RenderPassDescriptor {
- label: Some("iced_wgpu::quad render pass"),
+ label: Some("iced_wgpu render pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target,
resolve_target: None,
@@ -252,10 +241,12 @@ impl Backend {
}),
None => wgpu::LoadOp::Load,
},
- store: true,
+ store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
},
));
@@ -263,7 +254,7 @@ impl Backend {
let bounds = (layer.bounds * scale_factor).snap();
if bounds.width < 1 || bounds.height < 1 {
- return;
+ continue;
}
if !layer.quads.is_empty() {
@@ -294,18 +285,20 @@ impl Backend {
render_pass = ManuallyDrop::new(encoder.begin_render_pass(
&wgpu::RenderPassDescriptor {
- label: Some("iced_wgpu::quad render pass"),
+ label: Some("iced_wgpu render pass"),
color_attachments: &[Some(
wgpu::RenderPassColorAttachment {
view: target,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
- store: true,
+ store: wgpu::StoreOp::Store,
},
},
)],
depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
},
));
}
@@ -329,6 +322,45 @@ impl Backend {
text_layer += 1;
}
+
+ if !layer.pipelines.is_empty() {
+ let _ = ManuallyDrop::into_inner(render_pass);
+
+ for pipeline in &layer.pipelines {
+ let viewport = (pipeline.viewport * scale_factor).snap();
+
+ if viewport.width < 1 || viewport.height < 1 {
+ continue;
+ }
+
+ pipeline.primitive.render(
+ &self.pipeline_storage,
+ target,
+ target_size,
+ viewport,
+ encoder,
+ );
+ }
+
+ render_pass = ManuallyDrop::new(encoder.begin_render_pass(
+ &wgpu::RenderPassDescriptor {
+ label: Some("iced_wgpu render pass"),
+ color_attachments: &[Some(
+ wgpu::RenderPassColorAttachment {
+ view: target,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Load,
+ store: wgpu::StoreOp::Store,
+ },
+ },
+ )],
+ depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
+ },
+ ));
+ }
}
let _ = ManuallyDrop::into_inner(render_pass);
@@ -337,67 +369,9 @@ impl Backend {
impl crate::graphics::Backend for Backend {
type Primitive = primitive::Custom;
-
- fn trim_measurements(&mut self) {
- self.text_pipeline.trim_measurements();
- }
}
impl backend::Text for Backend {
- const ICON_FONT: Font = Font::with_name("Iced-Icons");
- const CHECKMARK_ICON: char = '\u{f00c}';
- const ARROW_DOWN_ICON: char = '\u{e800}';
-
- fn default_font(&self) -> Font {
- self.default_font
- }
-
- fn default_size(&self) -> f32 {
- self.default_text_size
- }
-
- fn measure(
- &self,
- contents: &str,
- size: f32,
- line_height: core::text::LineHeight,
- font: Font,
- bounds: Size,
- shaping: core::text::Shaping,
- ) -> Size {
- self.text_pipeline.measure(
- contents,
- size,
- line_height,
- font,
- bounds,
- shaping,
- )
- }
-
- fn hit_test(
- &self,
- contents: &str,
- size: f32,
- line_height: core::text::LineHeight,
- font: Font,
- bounds: Size,
- shaping: core::text::Shaping,
- point: Point,
- nearest_only: bool,
- ) -> Option<core::text::Hit> {
- self.text_pipeline.hit_test(
- contents,
- size,
- line_height,
- font,
- bounds,
- shaping,
- point,
- nearest_only,
- )
- }
-
fn load_font(&mut self, font: Cow<'static, [u8]>) {
self.text_pipeline.load_font(font);
}
@@ -405,14 +379,17 @@ impl backend::Text for Backend {
#[cfg(feature = "image")]
impl backend::Image for Backend {
- fn dimensions(&self, handle: &core::image::Handle) -> Size<u32> {
+ fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
self.image_pipeline.dimensions(handle)
}
}
#[cfg(feature = "svg")]
impl backend::Svg for Backend {
- fn viewport_dimensions(&self, handle: &core::svg::Handle) -> Size<u32> {
+ fn viewport_dimensions(
+ &self,
+ handle: &crate::core::svg::Handle,
+ ) -> Size<u32> {
self.image_pipeline.viewport_dimensions(handle)
}
}
diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs
index 94122187..ef00c58f 100644
--- a/wgpu/src/buffer.rs
+++ b/wgpu/src/buffer.rs
@@ -87,7 +87,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
/// Clears any temporary data (i.e. offsets) from the buffer.
pub fn clear(&mut self) {
- self.offsets.clear()
+ self.offsets.clear();
}
/// Returns the offset at `index`, if it exists.
diff --git a/wgpu/src/color.rs b/wgpu/src/color.rs
index a1025601..4598b0a6 100644
--- a/wgpu/src/color.rs
+++ b/wgpu/src/color.rs
@@ -12,7 +12,7 @@ pub fn convert(
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("iced_wgpu.offscreen.sampler"),
- ..Default::default()
+ ..wgpu::SamplerDescriptor::default()
});
//sampler in 0
@@ -102,10 +102,10 @@ pub fn convert(
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
front_face: wgpu::FrontFace::Cw,
- ..Default::default()
+ ..wgpu::PrimitiveState::default()
},
depth_stencil: None,
- multisample: Default::default(),
+ multisample: wgpu::MultisampleState::default(),
multiview: None,
});
@@ -143,10 +143,12 @@ pub fn convert(
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
- store: true,
+ store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
});
pass.set_pipeline(&pipeline);
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index e421e0b0..655362b7 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -310,13 +310,11 @@ impl Frame {
/// resulting glyphs will not be rotated or scaled properly.
///
/// Additionally, all text will be rendered on top of all the layers of
- /// a [`Canvas`]. Therefore, it is currently only meant to be used for
+ /// a `Canvas`. Therefore, it is currently only meant to be used for
/// overlays, which is the most common use case.
///
/// Support for vectorial text is planned, and should address all these
/// limitations.
- ///
- /// [`Canvas`]: crate::widget::Canvas
pub fn fill_text(&mut self, text: impl Into<Text>) {
let text = text.into();
@@ -444,11 +442,21 @@ impl Frame {
self.transforms.current.is_identity = false;
}
- /// Applies a scaling to the current transform of the [`Frame`].
+ /// Applies a uniform scaling to the current transform of the [`Frame`].
+ #[inline]
+ pub fn scale(&mut self, scale: impl Into<f32>) {
+ let scale = scale.into();
+
+ self.scale_nonuniform(Vector { x: scale, y: scale });
+ }
+
+ /// Applies a non-uniform scaling to the current transform of the [`Frame`].
#[inline]
- pub fn scale(&mut self, scale: f32) {
+ pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
+ let scale = scale.into();
+
self.transforms.current.raw =
- self.transforms.current.raw.pre_scale(scale, scale);
+ self.transforms.current.raw.pre_scale(scale.x, scale.y);
self.transforms.current.is_identity = false;
}
@@ -472,7 +480,7 @@ impl Frame {
},
size: self.size,
}),
- ))
+ ));
}
}
Buffer::Gradient(buffer) => {
@@ -485,7 +493,7 @@ impl Frame {
},
size: self.size,
}),
- ))
+ ));
}
}
}
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 553ba330..b78802c7 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -37,7 +37,8 @@ pub struct Pipeline {
pipeline: wgpu::RenderPipeline,
vertices: wgpu::Buffer,
indices: wgpu::Buffer,
- sampler: wgpu::Sampler,
+ nearest_sampler: wgpu::Sampler,
+ linear_sampler: wgpu::Sampler,
texture: wgpu::BindGroup,
texture_version: usize,
texture_atlas: Atlas,
@@ -51,16 +52,16 @@ pub struct Pipeline {
#[derive(Debug)]
struct Layer {
uniforms: wgpu::Buffer,
- constants: wgpu::BindGroup,
- instances: Buffer<Instance>,
- instance_count: usize,
+ nearest: Data,
+ linear: Data,
}
impl Layer {
fn new(
device: &wgpu::Device,
constant_layout: &wgpu::BindGroupLayout,
- sampler: &wgpu::Sampler,
+ nearest_sampler: &wgpu::Sampler,
+ linear_sampler: &wgpu::Sampler,
) -> Self {
let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("iced_wgpu::image uniforms buffer"),
@@ -69,6 +70,59 @@ impl Layer {
mapped_at_creation: false,
});
+ let nearest =
+ Data::new(device, constant_layout, nearest_sampler, &uniforms);
+
+ let linear =
+ Data::new(device, constant_layout, linear_sampler, &uniforms);
+
+ Self {
+ uniforms,
+ nearest,
+ linear,
+ }
+ }
+
+ fn prepare(
+ &mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ nearest_instances: &[Instance],
+ linear_instances: &[Instance],
+ transformation: Transformation,
+ ) {
+ queue.write_buffer(
+ &self.uniforms,
+ 0,
+ bytemuck::bytes_of(&Uniforms {
+ transform: transformation.into(),
+ }),
+ );
+
+ self.nearest.upload(device, queue, nearest_instances);
+ self.linear.upload(device, queue, linear_instances);
+ }
+
+ fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
+ self.nearest.render(render_pass);
+ self.linear.render(render_pass);
+ }
+}
+
+#[derive(Debug)]
+struct Data {
+ constants: wgpu::BindGroup,
+ instances: Buffer<Instance>,
+ instance_count: usize,
+}
+
+impl Data {
+ pub fn new(
+ device: &wgpu::Device,
+ constant_layout: &wgpu::BindGroupLayout,
+ sampler: &wgpu::Sampler,
+ uniforms: &wgpu::Buffer,
+ ) -> Self {
let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("iced_wgpu::image constants bind group"),
layout: constant_layout,
@@ -77,7 +131,7 @@ impl Layer {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
- buffer: &uniforms,
+ buffer: uniforms,
offset: 0,
size: None,
},
@@ -98,28 +152,18 @@ impl Layer {
);
Self {
- uniforms,
constants,
instances,
instance_count: 0,
}
}
- fn prepare(
+ fn upload(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
instances: &[Instance],
- transformation: Transformation,
) {
- queue.write_buffer(
- &self.uniforms,
- 0,
- bytemuck::bytes_of(&Uniforms {
- transform: transformation.into(),
- }),
- );
-
let _ = self.instances.resize(device, instances.len());
let _ = self.instances.write(queue, 0, instances);
@@ -142,12 +186,22 @@ impl Pipeline {
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
use wgpu::util::DeviceExt;
- let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+ let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+ address_mode_u: wgpu::AddressMode::ClampToEdge,
+ address_mode_v: wgpu::AddressMode::ClampToEdge,
+ address_mode_w: wgpu::AddressMode::ClampToEdge,
+ min_filter: wgpu::FilterMode::Nearest,
+ mag_filter: wgpu::FilterMode::Nearest,
+ mipmap_filter: wgpu::FilterMode::Nearest,
+ ..Default::default()
+ });
+
+ let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
- mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
+ mag_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Linear,
..Default::default()
});
@@ -312,7 +366,8 @@ impl Pipeline {
pipeline,
vertices,
indices,
- sampler,
+ nearest_sampler,
+ linear_sampler,
texture,
texture_version: texture_atlas.layer_count(),
texture_atlas,
@@ -355,7 +410,8 @@ impl Pipeline {
#[cfg(feature = "tracing")]
let _ = info_span!("Wgpu::Image", "DRAW").entered();
- let instances: &mut Vec<Instance> = &mut Vec::new();
+ let nearest_instances: &mut Vec<Instance> = &mut Vec::new();
+ let linear_instances: &mut Vec<Instance> = &mut Vec::new();
#[cfg(feature = "image")]
let mut raster_cache = self.raster_cache.borrow_mut();
@@ -366,7 +422,11 @@ impl Pipeline {
for image in images {
match &image {
#[cfg(feature = "image")]
- layer::Image::Raster { handle, bounds } => {
+ layer::Image::Raster {
+ handle,
+ filter_method,
+ bounds,
+ } => {
if let Some(atlas_entry) = raster_cache.upload(
device,
encoder,
@@ -377,7 +437,12 @@ impl Pipeline {
[bounds.x, bounds.y],
[bounds.width, bounds.height],
atlas_entry,
- instances,
+ match filter_method {
+ image::FilterMethod::Nearest => {
+ nearest_instances
+ }
+ image::FilterMethod::Linear => linear_instances,
+ },
);
}
}
@@ -405,7 +470,7 @@ impl Pipeline {
[bounds.x, bounds.y],
size,
atlas_entry,
- instances,
+ nearest_instances,
);
}
}
@@ -414,7 +479,7 @@ impl Pipeline {
}
}
- if instances.is_empty() {
+ if nearest_instances.is_empty() && linear_instances.is_empty() {
return;
}
@@ -442,12 +507,20 @@ impl Pipeline {
self.layers.push(Layer::new(
device,
&self.constant_layout,
- &self.sampler,
+ &self.nearest_sampler,
+ &self.linear_sampler,
));
}
let layer = &mut self.layers[self.prepare_layer];
- layer.prepare(device, queue, instances, transformation);
+
+ layer.prepare(
+ device,
+ queue,
+ nearest_instances,
+ linear_instances,
+ transformation,
+ );
self.prepare_layer += 1;
}
@@ -524,7 +597,7 @@ struct Instance {
}
impl Instance {
- pub const INITIAL: usize = 1_000;
+ pub const INITIAL: usize = 20;
}
#[repr(C)]
diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index e3de1290..789e35b4 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -86,7 +86,7 @@ impl Atlas {
entry
};
- log::info!("Allocated atlas entry: {:?}", entry);
+ log::info!("Allocated atlas entry: {entry:?}");
// It is a webgpu requirement that:
// BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0
@@ -104,7 +104,7 @@ impl Atlas {
padded_data[offset..offset + 4 * width as usize].copy_from_slice(
&data[row * 4 * width as usize..(row + 1) * 4 * width as usize],
- )
+ );
}
match &entry {
@@ -139,13 +139,13 @@ impl Atlas {
}
}
- log::info!("Current atlas: {:?}", self);
+ log::info!("Current atlas: {self:?}");
Some(entry)
}
pub fn remove(&mut self, entry: &Entry) {
- log::info!("Removing atlas entry: {:?}", entry);
+ log::info!("Removing atlas entry: {entry:?}");
match entry {
Entry::Contiguous(allocation) => {
@@ -237,7 +237,7 @@ impl Atlas {
}));
}
}
- _ => {}
+ Layer::Full => {}
}
}
@@ -258,7 +258,7 @@ impl Atlas {
}
fn deallocate(&mut self, allocation: &Allocation) {
- log::info!("Deallocating atlas: {:?}", allocation);
+ log::info!("Deallocating atlas: {allocation:?}");
match allocation {
Allocation::Full { layer } => {
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 2c03d36b..6582bb82 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -152,7 +152,7 @@ impl Cache {
let allocation =
atlas.upload(device, encoder, width, height, &rgba)?;
- log::debug!("allocating {} {}x{}", id, width, height);
+ log::debug!("allocating {id} {width}x{height}");
let _ = self.svg_hits.insert(id);
let _ = self.rasterized_hits.insert(key);
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index b8f32db1..98e49f1a 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -1,16 +1,18 @@
//! Organize rendering primitives into a flattened list of layers.
mod image;
+mod pipeline;
mod text;
pub mod mesh;
pub use image::Image;
pub use mesh::Mesh;
+pub use pipeline::Pipeline;
pub use text::Text;
use crate::core;
use crate::core::alignment;
-use crate::core::{Color, Font, Point, Rectangle, Size, Vector};
+use crate::core::{Color, Font, Pixels, Point, Rectangle, Size, Vector};
use crate::graphics;
use crate::graphics::color;
use crate::graphics::Viewport;
@@ -34,6 +36,9 @@ pub struct Layer<'a> {
/// The images of the [`Layer`].
pub images: Vec<Image>,
+
+ /// The custom pipelines of this [`Layer`].
+ pub pipelines: Vec<Pipeline>,
}
impl<'a> Layer<'a> {
@@ -45,6 +50,7 @@ impl<'a> Layer<'a> {
meshes: Vec::new(),
text: Vec::new(),
images: Vec::new(),
+ pipelines: Vec::new(),
}
}
@@ -56,14 +62,14 @@ impl<'a> Layer<'a> {
Layer::new(Rectangle::with_size(viewport.logical_size()));
for (i, line) in lines.iter().enumerate() {
- let text = Text {
+ let text = text::Cached {
content: line.as_ref(),
bounds: Rectangle::new(
Point::new(11.0, 11.0 + 25.0 * i as f32),
Size::INFINITY,
),
color: Color::new(0.9, 0.9, 0.9, 1.0),
- size: 20.0,
+ size: Pixels(20.0),
line_height: core::text::LineHeight::default(),
font: Font::MONOSPACE,
horizontal_alignment: alignment::Horizontal::Left,
@@ -71,13 +77,13 @@ impl<'a> Layer<'a> {
shaping: core::text::Shaping::Basic,
};
- overlay.text.push(text);
+ overlay.text.push(Text::Cached(text.clone()));
- overlay.text.push(Text {
+ overlay.text.push(Text::Cached(text::Cached {
bounds: text.bounds + Vector::new(-1.0, -1.0),
color: Color::BLACK,
..text
- });
+ }));
}
overlay
@@ -113,6 +119,32 @@ impl<'a> Layer<'a> {
current_layer: usize,
) {
match primitive {
+ Primitive::Paragraph {
+ paragraph,
+ position,
+ color,
+ } => {
+ let layer = &mut layers[current_layer];
+
+ layer.text.push(Text::Paragraph {
+ paragraph: paragraph.clone(),
+ position: *position + translation,
+ color: *color,
+ });
+ }
+ Primitive::Editor {
+ editor,
+ position,
+ color,
+ } => {
+ let layer = &mut layers[current_layer];
+
+ layer.text.push(Text::Editor {
+ editor: editor.clone(),
+ position: *position + translation,
+ color: *color,
+ });
+ }
Primitive::Text {
content,
bounds,
@@ -126,7 +158,7 @@ impl<'a> Layer<'a> {
} => {
let layer = &mut layers[current_layer];
- layer.text.push(Text {
+ layer.text.push(Text::Cached(text::Cached {
content,
bounds: *bounds + translation,
size: *size,
@@ -136,7 +168,7 @@ impl<'a> Layer<'a> {
horizontal_alignment: *horizontal_alignment,
vertical_alignment: *vertical_alignment,
shaping: *shaping,
- });
+ }));
}
Primitive::Quad {
bounds,
@@ -160,11 +192,16 @@ impl<'a> Layer<'a> {
layer.quads.add(quad, background);
}
- Primitive::Image { handle, bounds } => {
+ Primitive::Image {
+ handle,
+ filter_method,
+ bounds,
+ } => {
let layer = &mut layers[current_layer];
layer.images.push(Image::Raster {
handle: handle.clone(),
+ filter_method: *filter_method,
bounds: *bounds + translation,
});
}
@@ -189,7 +226,7 @@ impl<'a> Layer<'a> {
translation,
primitive,
current_layer,
- )
+ );
}
}
Primitive::Clip { bounds, content } => {
@@ -277,6 +314,20 @@ impl<'a> Layer<'a> {
}
}
},
+ primitive::Custom::Pipeline(pipeline) => {
+ let layer = &mut layers[current_layer];
+ let bounds = pipeline.bounds + translation;
+
+ if let Some(clip_bounds) =
+ layer.bounds.intersection(&bounds)
+ {
+ layer.pipelines.push(Pipeline {
+ bounds,
+ viewport: clip_bounds,
+ primitive: pipeline.primitive.clone(),
+ });
+ }
+ }
},
}
}
diff --git a/wgpu/src/layer/image.rs b/wgpu/src/layer/image.rs
index 0de589f8..facbe192 100644
--- a/wgpu/src/layer/image.rs
+++ b/wgpu/src/layer/image.rs
@@ -10,6 +10,9 @@ pub enum Image {
/// The handle of a raster image.
handle: image::Handle,
+ /// The filter method of a raster image.
+ filter_method: image::FilterMethod,
+
/// The bounds of the image.
bounds: Rectangle,
},
diff --git a/wgpu/src/layer/pipeline.rs b/wgpu/src/layer/pipeline.rs
new file mode 100644
index 00000000..6dfe6750
--- /dev/null
+++ b/wgpu/src/layer/pipeline.rs
@@ -0,0 +1,17 @@
+use crate::core::Rectangle;
+use crate::primitive::pipeline::Primitive;
+
+use std::sync::Arc;
+
+#[derive(Clone, Debug)]
+/// A custom primitive which can be used to render primitives associated with a custom pipeline.
+pub struct Pipeline {
+ /// The bounds of the [`Pipeline`].
+ pub bounds: Rectangle,
+
+ /// The viewport of the [`Pipeline`].
+ pub viewport: Rectangle,
+
+ /// The [`Primitive`] to render.
+ pub primitive: Arc<dyn Primitive>,
+}
diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs
index ba1bdca8..66417cec 100644
--- a/wgpu/src/layer/text.rs
+++ b/wgpu/src/layer/text.rs
@@ -1,10 +1,32 @@
use crate::core::alignment;
use crate::core::text;
-use crate::core::{Color, Font, Rectangle};
+use crate::core::{Color, Font, Pixels, Point, Rectangle};
+use crate::graphics::text::editor;
+use crate::graphics::text::paragraph;
+
+/// A text primitive.
+#[derive(Debug, Clone)]
+pub enum Text<'a> {
+ /// A paragraph.
+ #[allow(missing_docs)]
+ Paragraph {
+ paragraph: paragraph::Weak,
+ position: Point,
+ color: Color,
+ },
+ /// An editor.
+ #[allow(missing_docs)]
+ Editor {
+ editor: editor::Weak,
+ position: Point,
+ color: Color,
+ },
+ /// A cached text.
+ Cached(Cached<'a>),
+}
-/// A paragraph of text.
-#[derive(Debug, Clone, Copy)]
-pub struct Text<'a> {
+#[derive(Debug, Clone)]
+pub struct Cached<'a> {
/// The content of the [`Text`].
pub content: &'a str,
@@ -15,7 +37,7 @@ pub struct Text<'a> {
pub color: Color,
/// The size of the [`Text`] in logical pixels.
- pub size: f32,
+ pub size: Pixels,
/// The line height of the [`Text`].
pub line_height: text::LineHeight,
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index deb223ef..424dfeb3 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -1,41 +1,33 @@
-//! A [`wgpu`] renderer for [`iced_native`].
+//! A [`wgpu`] renderer for [Iced].
//!
//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
//!
-//! For now, it is the default renderer of [Iced] in native platforms.
-//!
//! [`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX11, and
//! DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the
//! incoming [WebGPU API].
//!
//! Currently, `iced_wgpu` supports the following primitives:
-//! - Text, which is rendered using [`wgpu_glyph`]. No shaping at all.
+//! - Text, which is rendered using [`glyphon`].
//! - Quads or rectangles, with rounded borders and a solid background color.
//! - Clip areas, useful to implement scrollables or hide overflowing content.
//! - Images and SVG, loaded from memory or the file system.
//! - Meshes of triangles, useful to draw geometry freely.
//!
//! [Iced]: https://github.com/iced-rs/iced
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [WebGPU API]: https://gpuweb.github.io/gpuweb/
-//! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
+//! [`glyphon`]: https://github.com/grovesNL/glyphon
#![doc(
html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
)]
+#![forbid(rust_2018_idioms)]
#![deny(
missing_debug_implementations,
missing_docs,
unsafe_code,
unused_results,
- clippy::extra_unused_lifetimes,
- clippy::from_over_into,
- clippy::needless_borrow,
- clippy::new_without_default,
- clippy::useless_conversion
+ rustdoc::broken_intra_doc_links
)]
-#![forbid(rust_2018_idioms)]
-#![allow(clippy::inherent_to_string, clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod layer;
pub mod primitive;
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
index 8dbf3008..fff927ea 100644
--- a/wgpu/src/primitive.rs
+++ b/wgpu/src/primitive.rs
@@ -1,7 +1,13 @@
//! Draw using different graphical primitives.
+pub mod pipeline;
+
+pub use pipeline::Pipeline;
+
use crate::core::Rectangle;
use crate::graphics::{Damage, Mesh};
+use std::fmt::Debug;
+
/// The graphical primitives supported by `iced_wgpu`.
pub type Primitive = crate::graphics::Primitive<Custom>;
@@ -10,12 +16,15 @@ pub type Primitive = crate::graphics::Primitive<Custom>;
pub enum Custom {
/// A mesh primitive.
Mesh(Mesh),
+ /// A custom pipeline primitive.
+ Pipeline(Pipeline),
}
impl Damage for Custom {
fn bounds(&self) -> Rectangle {
match self {
Self::Mesh(mesh) => mesh.bounds(),
+ Self::Pipeline(pipeline) => pipeline.bounds,
}
}
}
diff --git a/wgpu/src/primitive/pipeline.rs b/wgpu/src/primitive/pipeline.rs
new file mode 100644
index 00000000..302e38f6
--- /dev/null
+++ b/wgpu/src/primitive/pipeline.rs
@@ -0,0 +1,116 @@
+//! Draw primitives using custom pipelines.
+use crate::core::{Rectangle, Size};
+
+use std::any::{Any, TypeId};
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::sync::Arc;
+
+#[derive(Clone, Debug)]
+/// A custom primitive which can be used to render primitives associated with a custom pipeline.
+pub struct Pipeline {
+ /// The bounds of the [`Pipeline`].
+ pub bounds: Rectangle,
+
+ /// The [`Primitive`] to render.
+ pub primitive: Arc<dyn Primitive>,
+}
+
+impl Pipeline {
+ /// Creates a new [`Pipeline`] with the given [`Primitive`].
+ pub fn new(bounds: Rectangle, primitive: impl Primitive) -> Self {
+ Pipeline {
+ bounds,
+ primitive: Arc::new(primitive),
+ }
+ }
+}
+
+impl PartialEq for Pipeline {
+ fn eq(&self, other: &Self) -> bool {
+ self.primitive.type_id() == other.primitive.type_id()
+ }
+}
+
+/// A set of methods which allows a [`Primitive`] to be rendered.
+pub trait Primitive: Debug + Send + Sync + 'static {
+ /// Processes the [`Primitive`], allowing for GPU buffer allocation.
+ fn prepare(
+ &self,
+ format: wgpu::TextureFormat,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ bounds: Rectangle,
+ target_size: Size<u32>,
+ scale_factor: f32,
+ storage: &mut Storage,
+ );
+
+ /// Renders the [`Primitive`].
+ fn render(
+ &self,
+ storage: &Storage,
+ target: &wgpu::TextureView,
+ target_size: Size<u32>,
+ viewport: Rectangle<u32>,
+ encoder: &mut wgpu::CommandEncoder,
+ );
+}
+
+/// A renderer than can draw custom pipeline primitives.
+pub trait Renderer: crate::core::Renderer {
+ /// Draws a custom pipeline primitive.
+ fn draw_pipeline_primitive(
+ &mut self,
+ bounds: Rectangle,
+ primitive: impl Primitive,
+ );
+}
+
+impl<Theme> Renderer for crate::Renderer<Theme> {
+ fn draw_pipeline_primitive(
+ &mut self,
+ bounds: Rectangle,
+ primitive: impl Primitive,
+ ) {
+ self.draw_primitive(super::Primitive::Custom(super::Custom::Pipeline(
+ Pipeline::new(bounds, primitive),
+ )));
+ }
+}
+
+/// Stores custom, user-provided pipelines.
+#[derive(Default, Debug)]
+pub struct Storage {
+ pipelines: HashMap<TypeId, Box<dyn Any>>,
+}
+
+impl Storage {
+ /// Returns `true` if `Storage` contains a pipeline with type `T`.
+ pub fn has<T: 'static>(&self) -> bool {
+ self.pipelines.get(&TypeId::of::<T>()).is_some()
+ }
+
+ /// Inserts the pipeline `T` in to [`Storage`].
+ pub fn store<T: 'static>(&mut self, pipeline: T) {
+ let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
+ }
+
+ /// Returns a reference to pipeline with type `T` if it exists in [`Storage`].
+ pub fn get<T: 'static>(&self) -> Option<&T> {
+ self.pipelines.get(&TypeId::of::<T>()).map(|pipeline| {
+ pipeline
+ .downcast_ref::<T>()
+ .expect("Pipeline with this type does not exist in Storage.")
+ })
+ }
+
+ /// Returns a mutable reference to pipeline `T` if it exists in [`Storage`].
+ pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
+ self.pipelines.get_mut(&TypeId::of::<T>()).map(|pipeline| {
+ pipeline
+ .downcast_mut::<T>()
+ .expect("Pipeline with this type does not exist in Storage.")
+ })
+ }
+}
diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs
index 6db37252..a8e83d01 100644
--- a/wgpu/src/quad/gradient.rs
+++ b/wgpu/src/quad/gradient.rs
@@ -1,3 +1,4 @@
+use crate::graphics::color;
use crate::graphics::gradient;
use crate::quad::{self, Quad};
use crate::Buffer;
@@ -78,7 +79,23 @@ impl Pipeline {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu.quad.gradient.shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
- include_str!("../shader/quad.wgsl"),
+ if color::GAMMA_CORRECTION {
+ concat!(
+ include_str!("../shader/quad.wgsl"),
+ "\n",
+ include_str!("../shader/quad/gradient.wgsl"),
+ "\n",
+ include_str!("../shader/color/oklab.wgsl")
+ )
+ } else {
+ concat!(
+ include_str!("../shader/quad.wgsl"),
+ "\n",
+ include_str!("../shader/quad/gradient.wgsl"),
+ "\n",
+ include_str!("../shader/color/linear_rgb.wgsl")
+ )
+ },
)),
});
diff --git a/wgpu/src/quad/solid.rs b/wgpu/src/quad/solid.rs
index f8f1e3a5..9bc6b466 100644
--- a/wgpu/src/quad/solid.rs
+++ b/wgpu/src/quad/solid.rs
@@ -72,7 +72,11 @@ impl Pipeline {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu.quad.solid.shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
- include_str!("../shader/quad.wgsl"),
+ concat!(
+ include_str!("../shader/quad.wgsl"),
+ "\n",
+ include_str!("../shader/quad/solid.wgsl"),
+ ),
)),
});
diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs
index 266a2c87..c9338fec 100644
--- a/wgpu/src/settings.rs
+++ b/wgpu/src/settings.rs
@@ -1,5 +1,5 @@
//! Configure a renderer.
-use crate::core::Font;
+use crate::core::{Font, Pixels};
use crate::graphics::Antialiasing;
/// The settings of a [`Backend`].
@@ -21,7 +21,7 @@ pub struct Settings {
/// The default size of text.
///
/// By default, it will be set to `16.0`.
- pub default_text_size: f32,
+ pub default_text_size: Pixels,
/// The antialiasing strategy that will be used for triangle primitives.
///
@@ -59,7 +59,7 @@ impl Default for Settings {
present_mode: wgpu::PresentMode::AutoVsync,
internal_backend: wgpu::Backends::all(),
default_font: Font::default(),
- default_text_size: 16.0,
+ default_text_size: Pixels(16.0),
antialiasing: None,
}
}
diff --git a/wgpu/src/shader/color/linear_rgb.wgsl b/wgpu/src/shader/color/linear_rgb.wgsl
new file mode 100644
index 00000000..a5cf45d4
--- /dev/null
+++ b/wgpu/src/shader/color/linear_rgb.wgsl
@@ -0,0 +1,3 @@
+fn interpolate_color(from_: vec4<f32>, to_: vec4<f32>, factor: f32) -> vec4<f32> {
+ return mix(from_, to_, factor);
+}
diff --git a/wgpu/src/shader/color/oklab.wgsl b/wgpu/src/shader/color/oklab.wgsl
new file mode 100644
index 00000000..0dc37ba6
--- /dev/null
+++ b/wgpu/src/shader/color/oklab.wgsl
@@ -0,0 +1,26 @@
+const to_lms = mat3x4<f32>(
+ vec4<f32>(0.4121656120, 0.2118591070, 0.0883097947, 0.0),
+ vec4<f32>(0.5362752080, 0.6807189584, 0.2818474174, 0.0),
+ vec4<f32>(0.0514575653, 0.1074065790, 0.6302613616, 0.0),
+);
+
+const to_rgb = mat3x4<f32>(
+ vec4<f32>( 4.0767245293, -3.3072168827, 0.2307590544, 0.0),
+ vec4<f32>(-1.2681437731, 2.6093323231, -0.3411344290, 0.0),
+ vec4<f32>(-0.0041119885, -0.7034763098, 1.7068625689, 0.0),
+);
+
+fn interpolate_color(from_: vec4<f32>, to_: vec4<f32>, factor: f32) -> vec4<f32> {
+ // To Oklab
+ let lms_a = pow(from_ * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
+ let lms_b = pow(to_ * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
+ let mixed = mix(lms_a, lms_b, factor);
+
+ // Back to linear RGB
+ var color = to_rgb * (mixed * mixed * mixed);
+
+ // Alpha interpolation
+ color.a = mix(from_.a, to_.a, factor);
+
+ return color;
+}
diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl
index fb402158..f919cfe2 100644
--- a/wgpu/src/shader/quad.wgsl
+++ b/wgpu/src/shader/quad.wgsl
@@ -37,309 +37,3 @@ fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>)
rx = select(rx, ry, position.y > center.y);
return rx;
}
-
-fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
- let rg: vec2<f32> = unpack2x16float(color.x);
- let ba: vec2<f32> = unpack2x16float(color.y);
-
- return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
-}
-
-struct SolidVertexInput {
- @location(0) v_pos: vec2<f32>,
- @location(1) color: vec4<f32>,
- @location(2) pos: vec2<f32>,
- @location(3) scale: vec2<f32>,
- @location(4) border_color: vec4<f32>,
- @location(5) border_radius: vec4<f32>,
- @location(6) border_width: f32,
-}
-
-struct SolidVertexOutput {
- @builtin(position) position: vec4<f32>,
- @location(0) color: vec4<f32>,
- @location(1) border_color: vec4<f32>,
- @location(2) pos: vec2<f32>,
- @location(3) scale: vec2<f32>,
- @location(4) border_radius: vec4<f32>,
- @location(5) border_width: f32,
-}
-
-@vertex
-fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
- var out: SolidVertexOutput;
-
- var pos: vec2<f32> = input.pos * globals.scale;
- var scale: vec2<f32> = input.scale * globals.scale;
-
- var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
- var border_radius: vec4<f32> = vec4<f32>(
- min(input.border_radius.x, min_border_radius),
- min(input.border_radius.y, min_border_radius),
- min(input.border_radius.z, min_border_radius),
- min(input.border_radius.w, min_border_radius)
- );
-
- var transform: mat4x4<f32> = mat4x4<f32>(
- vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
- vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
- vec4<f32>(0.0, 0.0, 1.0, 0.0),
- vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
- );
-
- out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
- out.color = input.color;
- out.border_color = input.border_color;
- out.pos = pos;
- out.scale = scale;
- out.border_radius = border_radius * globals.scale;
- out.border_width = input.border_width * globals.scale;
-
- return out;
-}
-
-@fragment
-fn solid_fs_main(
- input: SolidVertexOutput
-) -> @location(0) vec4<f32> {
- var mixed_color: vec4<f32> = input.color;
-
- var border_radius = select_border_radius(
- input.border_radius,
- input.position.xy,
- (input.pos + input.scale * 0.5).xy
- );
-
- if (input.border_width > 0.0) {
- var internal_border: f32 = max(border_radius - input.border_width, 0.0);
-
- var internal_distance: f32 = distance_alg(
- input.position.xy,
- input.pos + vec2<f32>(input.border_width, input.border_width),
- input.scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
- internal_border
- );
-
- var border_mix: f32 = smoothstep(
- max(internal_border - 0.5, 0.0),
- internal_border + 0.5,
- internal_distance
- );
-
- mixed_color = mix(input.color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
- }
-
- var dist: f32 = distance_alg(
- vec2<f32>(input.position.x, input.position.y),
- input.pos,
- input.scale,
- border_radius
- );
-
- var radius_alpha: f32 = 1.0 - smoothstep(
- max(border_radius - 0.5, 0.0),
- border_radius + 0.5,
- dist
- );
-
- return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
-}
-
-struct GradientVertexInput {
- @location(0) v_pos: vec2<f32>,
- @location(1) colors_1: vec4<u32>,
- @location(2) colors_2: vec4<u32>,
- @location(3) colors_3: vec4<u32>,
- @location(4) colors_4: vec4<u32>,
- @location(5) offsets: vec4<u32>,
- @location(6) direction: vec4<f32>,
- @location(7) position_and_scale: vec4<f32>,
- @location(8) border_color: vec4<f32>,
- @location(9) border_radius: vec4<f32>,
- @location(10) border_width: f32,
-}
-
-struct GradientVertexOutput {
- @builtin(position) position: vec4<f32>,
- @location(1) colors_1: vec4<u32>,
- @location(2) colors_2: vec4<u32>,
- @location(3) colors_3: vec4<u32>,
- @location(4) colors_4: vec4<u32>,
- @location(5) offsets: vec4<u32>,
- @location(6) direction: vec4<f32>,
- @location(7) position_and_scale: vec4<f32>,
- @location(8) border_color: vec4<f32>,
- @location(9) border_radius: vec4<f32>,
- @location(10) border_width: f32,
-}
-
-@vertex
-fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
- var out: GradientVertexOutput;
-
- var pos: vec2<f32> = input.position_and_scale.xy * globals.scale;
- var scale: vec2<f32> = input.position_and_scale.zw * globals.scale;
-
- var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5;
- var border_radius: vec4<f32> = vec4<f32>(
- min(input.border_radius.x, min_border_radius),
- min(input.border_radius.y, min_border_radius),
- min(input.border_radius.z, min_border_radius),
- min(input.border_radius.w, min_border_radius)
- );
-
- var transform: mat4x4<f32> = mat4x4<f32>(
- vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
- vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
- vec4<f32>(0.0, 0.0, 1.0, 0.0),
- vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
- );
-
- out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
- out.colors_1 = input.colors_1;
- out.colors_2 = input.colors_2;
- out.colors_3 = input.colors_3;
- out.colors_4 = input.colors_4;
- out.offsets = input.offsets;
- out.direction = input.direction * globals.scale;
- out.position_and_scale = vec4<f32>(pos, scale);
- out.border_color = input.border_color;
- out.border_radius = border_radius * globals.scale;
- out.border_width = input.border_width * globals.scale;
-
- return out;
-}
-
-fn random(coords: vec2<f32>) -> f32 {
- return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
-}
-
-/// Returns the current interpolated color with a max 8-stop gradient
-fn gradient(
- raw_position: vec2<f32>,
- direction: vec4<f32>,
- colors: array<vec4<f32>, 8>,
- offsets: array<f32, 8>,
- last_index: i32
-) -> vec4<f32> {
- let start = direction.xy;
- let end = direction.zw;
-
- let v1 = end - start;
- let v2 = raw_position - start;
- let unit = normalize(v1);
- let coord_offset = dot(unit, v2) / length(v1);
-
- //need to store these as a var to use dynamic indexing in a loop
- //this is already added to wgsl spec but not in wgpu yet
- var colors_arr = colors;
- var offsets_arr = offsets;
-
- var color: vec4<f32>;
-
- let noise_granularity: f32 = 0.3/255.0;
-
- for (var i: i32 = 0; i < last_index; i++) {
- let curr_offset = offsets_arr[i];
- let next_offset = offsets_arr[i+1];
-
- if (coord_offset <= offsets_arr[0]) {
- color = colors_arr[0];
- }
-
- if (curr_offset <= coord_offset && coord_offset <= next_offset) {
- color = mix(colors_arr[i], colors_arr[i+1], smoothstep(
- curr_offset,
- next_offset,
- coord_offset,
- ));
- }
-
- if (coord_offset >= offsets_arr[last_index]) {
- color = colors_arr[last_index];
- }
- }
-
- return color + mix(-noise_granularity, noise_granularity, random(raw_position));
-}
-
-@fragment
-fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
- let colors = array<vec4<f32>, 8>(
- unpack_u32(input.colors_1.xy),
- unpack_u32(input.colors_1.zw),
- unpack_u32(input.colors_2.xy),
- unpack_u32(input.colors_2.zw),
- unpack_u32(input.colors_3.xy),
- unpack_u32(input.colors_3.zw),
- unpack_u32(input.colors_4.xy),
- unpack_u32(input.colors_4.zw),
- );
-
- let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
- let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
-
- var offsets = array<f32, 8>(
- offsets_1.x,
- offsets_1.y,
- offsets_1.z,
- offsets_1.w,
- offsets_2.x,
- offsets_2.y,
- offsets_2.z,
- offsets_2.w,
- );
-
- //TODO could just pass this in to the shader but is probably more performant to just check it here
- var last_index = 7;
- for (var i: i32 = 0; i <= 7; i++) {
- if (offsets[i] > 1.0) {
- last_index = i - 1;
- break;
- }
- }
-
- var mixed_color: vec4<f32> = gradient(input.position.xy, input.direction, colors, offsets, last_index);
-
- let pos = input.position_and_scale.xy;
- let scale = input.position_and_scale.zw;
-
- var border_radius = select_border_radius(
- input.border_radius,
- input.position.xy,
- (pos + scale * 0.5).xy
- );
-
- if (input.border_width > 0.0) {
- var internal_border: f32 = max(border_radius - input.border_width, 0.0);
-
- var internal_distance: f32 = distance_alg(
- input.position.xy,
- pos + vec2<f32>(input.border_width, input.border_width),
- scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
- internal_border
- );
-
- var border_mix: f32 = smoothstep(
- max(internal_border - 0.5, 0.0),
- internal_border + 0.5,
- internal_distance
- );
-
- mixed_color = mix(mixed_color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
- }
-
- var dist: f32 = distance_alg(
- input.position.xy,
- pos,
- scale,
- border_radius
- );
-
- var radius_alpha: f32 = 1.0 - smoothstep(
- max(border_radius - 0.5, 0.0),
- border_radius + 0.5,
- dist);
-
- return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
-}
diff --git a/wgpu/src/shader/quad/gradient.wgsl b/wgpu/src/shader/quad/gradient.wgsl
new file mode 100644
index 00000000..0754e97f
--- /dev/null
+++ b/wgpu/src/shader/quad/gradient.wgsl
@@ -0,0 +1,205 @@
+struct GradientVertexInput {
+ @location(0) v_pos: vec2<f32>,
+ @location(1) @interpolate(flat) colors_1: vec4<u32>,
+ @location(2) @interpolate(flat) colors_2: vec4<u32>,
+ @location(3) @interpolate(flat) colors_3: vec4<u32>,
+ @location(4) @interpolate(flat) colors_4: vec4<u32>,
+ @location(5) @interpolate(flat) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
+ @location(7) position_and_scale: vec4<f32>,
+ @location(8) border_color: vec4<f32>,
+ @location(9) border_radius: vec4<f32>,
+ @location(10) border_width: f32,
+}
+
+struct GradientVertexOutput {
+ @builtin(position) position: vec4<f32>,
+ @location(1) @interpolate(flat) colors_1: vec4<u32>,
+ @location(2) @interpolate(flat) colors_2: vec4<u32>,
+ @location(3) @interpolate(flat) colors_3: vec4<u32>,
+ @location(4) @interpolate(flat) colors_4: vec4<u32>,
+ @location(5) @interpolate(flat) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
+ @location(7) position_and_scale: vec4<f32>,
+ @location(8) border_color: vec4<f32>,
+ @location(9) border_radius: vec4<f32>,
+ @location(10) border_width: f32,
+}
+
+@vertex
+fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
+ var out: GradientVertexOutput;
+
+ var pos: vec2<f32> = input.position_and_scale.xy * globals.scale;
+ var scale: vec2<f32> = input.position_and_scale.zw * globals.scale;
+
+ var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5;
+ var border_radius: vec4<f32> = vec4<f32>(
+ min(input.border_radius.x, min_border_radius),
+ min(input.border_radius.y, min_border_radius),
+ min(input.border_radius.z, min_border_radius),
+ min(input.border_radius.w, min_border_radius)
+ );
+
+ var transform: mat4x4<f32> = mat4x4<f32>(
+ vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
+ vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
+ vec4<f32>(0.0, 0.0, 1.0, 0.0),
+ vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
+ );
+
+ out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
+ out.colors_1 = input.colors_1;
+ out.colors_2 = input.colors_2;
+ out.colors_3 = input.colors_3;
+ out.colors_4 = input.colors_4;
+ out.offsets = input.offsets;
+ out.direction = input.direction * globals.scale;
+ out.position_and_scale = vec4<f32>(pos, scale);
+ out.border_color = input.border_color;
+ out.border_radius = border_radius * globals.scale;
+ out.border_width = input.border_width * globals.scale;
+
+ return out;
+}
+
+fn random(coords: vec2<f32>) -> f32 {
+ return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
+}
+
+/// Returns the current interpolated color with a max 8-stop gradient
+fn gradient(
+ raw_position: vec2<f32>,
+ direction: vec4<f32>,
+ colors: array<vec4<f32>, 8>,
+ offsets: array<f32, 8>,
+ last_index: i32
+) -> vec4<f32> {
+ let start = direction.xy;
+ let end = direction.zw;
+
+ let v1 = end - start;
+ let v2 = raw_position - start;
+ let unit = normalize(v1);
+ let coord_offset = dot(unit, v2) / length(v1);
+
+ //need to store these as a var to use dynamic indexing in a loop
+ //this is already added to wgsl spec but not in wgpu yet
+ var colors_arr = colors;
+ var offsets_arr = offsets;
+
+ var color: vec4<f32>;
+
+ let noise_granularity: f32 = 0.3/255.0;
+
+ for (var i: i32 = 0; i < last_index; i++) {
+ let curr_offset = offsets_arr[i];
+ let next_offset = offsets_arr[i+1];
+
+ if (coord_offset <= offsets_arr[0]) {
+ color = colors_arr[0];
+ }
+
+ if (curr_offset <= coord_offset && coord_offset <= next_offset) {
+ let from_ = colors_arr[i];
+ let to_ = colors_arr[i+1];
+ let factor = smoothstep(curr_offset, next_offset, coord_offset);
+
+ color = interpolate_color(from_, to_, factor);
+ }
+
+ if (coord_offset >= offsets_arr[last_index]) {
+ color = colors_arr[last_index];
+ }
+ }
+
+ return color + mix(-noise_granularity, noise_granularity, random(raw_position));
+}
+
+@fragment
+fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
+ let colors = array<vec4<f32>, 8>(
+ unpack_u32(input.colors_1.xy),
+ unpack_u32(input.colors_1.zw),
+ unpack_u32(input.colors_2.xy),
+ unpack_u32(input.colors_2.zw),
+ unpack_u32(input.colors_3.xy),
+ unpack_u32(input.colors_3.zw),
+ unpack_u32(input.colors_4.xy),
+ unpack_u32(input.colors_4.zw),
+ );
+
+ let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
+ let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
+
+ var offsets = array<f32, 8>(
+ offsets_1.x,
+ offsets_1.y,
+ offsets_1.z,
+ offsets_1.w,
+ offsets_2.x,
+ offsets_2.y,
+ offsets_2.z,
+ offsets_2.w,
+ );
+
+ //TODO could just pass this in to the shader but is probably more performant to just check it here
+ var last_index = 7;
+ for (var i: i32 = 0; i <= 7; i++) {
+ if (offsets[i] > 1.0) {
+ last_index = i - 1;
+ break;
+ }
+ }
+
+ var mixed_color: vec4<f32> = gradient(input.position.xy, input.direction, colors, offsets, last_index);
+
+ let pos = input.position_and_scale.xy;
+ let scale = input.position_and_scale.zw;
+
+ var border_radius = select_border_radius(
+ input.border_radius,
+ input.position.xy,
+ (pos + scale * 0.5).xy
+ );
+
+ if (input.border_width > 0.0) {
+ var internal_border: f32 = max(border_radius - input.border_width, 0.0);
+
+ var internal_distance: f32 = distance_alg(
+ input.position.xy,
+ pos + vec2<f32>(input.border_width, input.border_width),
+ scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
+ internal_border
+ );
+
+ var border_mix: f32 = smoothstep(
+ max(internal_border - 0.5, 0.0),
+ internal_border + 0.5,
+ internal_distance
+ );
+
+ mixed_color = mix(mixed_color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
+ }
+
+ var dist: f32 = distance_alg(
+ input.position.xy,
+ pos,
+ scale,
+ border_radius
+ );
+
+ var radius_alpha: f32 = 1.0 - smoothstep(
+ max(border_radius - 0.5, 0.0),
+ border_radius + 0.5,
+ dist);
+
+ return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
+}
+
+fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
+ let rg: vec2<f32> = unpack2x16float(color.x);
+ let ba: vec2<f32> = unpack2x16float(color.y);
+
+ return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
+}
diff --git a/wgpu/src/shader/quad/solid.wgsl b/wgpu/src/shader/quad/solid.wgsl
new file mode 100644
index 00000000..ebd6d877
--- /dev/null
+++ b/wgpu/src/shader/quad/solid.wgsl
@@ -0,0 +1,99 @@
+struct SolidVertexInput {
+ @location(0) v_pos: vec2<f32>,
+ @location(1) color: vec4<f32>,
+ @location(2) pos: vec2<f32>,
+ @location(3) scale: vec2<f32>,
+ @location(4) border_color: vec4<f32>,
+ @location(5) border_radius: vec4<f32>,
+ @location(6) border_width: f32,
+}
+
+struct SolidVertexOutput {
+ @builtin(position) position: vec4<f32>,
+ @location(0) color: vec4<f32>,
+ @location(1) border_color: vec4<f32>,
+ @location(2) pos: vec2<f32>,
+ @location(3) scale: vec2<f32>,
+ @location(4) border_radius: vec4<f32>,
+ @location(5) border_width: f32,
+}
+
+@vertex
+fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
+ var out: SolidVertexOutput;
+
+ var pos: vec2<f32> = input.pos * globals.scale;
+ var scale: vec2<f32> = input.scale * globals.scale;
+
+ var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
+ var border_radius: vec4<f32> = vec4<f32>(
+ min(input.border_radius.x, min_border_radius),
+ min(input.border_radius.y, min_border_radius),
+ min(input.border_radius.z, min_border_radius),
+ min(input.border_radius.w, min_border_radius)
+ );
+
+ var transform: mat4x4<f32> = mat4x4<f32>(
+ vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
+ vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
+ vec4<f32>(0.0, 0.0, 1.0, 0.0),
+ vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
+ );
+
+ out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
+ out.color = input.color;
+ out.border_color = input.border_color;
+ out.pos = pos;
+ out.scale = scale;
+ out.border_radius = border_radius * globals.scale;
+ out.border_width = input.border_width * globals.scale;
+
+ return out;
+}
+
+@fragment
+fn solid_fs_main(
+ input: SolidVertexOutput
+) -> @location(0) vec4<f32> {
+ var mixed_color: vec4<f32> = input.color;
+
+ var border_radius = select_border_radius(
+ input.border_radius,
+ input.position.xy,
+ (input.pos + input.scale * 0.5).xy
+ );
+
+ if (input.border_width > 0.0) {
+ var internal_border: f32 = max(border_radius - input.border_width, 0.0);
+
+ var internal_distance: f32 = distance_alg(
+ input.position.xy,
+ input.pos + vec2<f32>(input.border_width, input.border_width),
+ input.scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
+ internal_border
+ );
+
+ var border_mix: f32 = smoothstep(
+ max(internal_border - 0.5, 0.0),
+ internal_border + 0.5,
+ internal_distance
+ );
+
+ mixed_color = mix(input.color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
+ }
+
+ var dist: f32 = distance_alg(
+ vec2<f32>(input.position.x, input.position.y),
+ input.pos,
+ input.scale,
+ border_radius
+ );
+
+ var radius_alpha: f32 = 1.0 - smoothstep(
+ max(border_radius - 0.5, 0.0),
+ border_radius + 0.5,
+ dist
+ );
+
+ return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
+}
diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl
index 9f512d14..e4c19344 100644
--- a/wgpu/src/shader/triangle.wgsl
+++ b/wgpu/src/shader/triangle.wgsl
@@ -3,163 +3,3 @@ struct Globals {
}
@group(0) @binding(0) var<uniform> globals: Globals;
-
-fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
- let rg: vec2<f32> = unpack2x16float(color.x);
- let ba: vec2<f32> = unpack2x16float(color.y);
-
- return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
-}
-
-struct SolidVertexInput {
- @location(0) position: vec2<f32>,
- @location(1) color: vec4<f32>,
-}
-
-struct SolidVertexOutput {
- @builtin(position) position: vec4<f32>,
- @location(0) color: vec4<f32>,
-}
-
-@vertex
-fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
- var out: SolidVertexOutput;
-
- out.color = input.color;
- out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
-
- return out;
-}
-
-@fragment
-fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
- return input.color;
-}
-
-struct GradientVertexInput {
- @location(0) v_pos: vec2<f32>,
- @location(1) colors_1: vec4<u32>,
- @location(2) colors_2: vec4<u32>,
- @location(3) colors_3: vec4<u32>,
- @location(4) colors_4: vec4<u32>,
- @location(5) offsets: vec4<u32>,
- @location(6) direction: vec4<f32>,
-}
-
-struct GradientVertexOutput {
- @builtin(position) position: vec4<f32>,
- @location(0) raw_position: vec2<f32>,
- @location(1) colors_1: vec4<u32>,
- @location(2) colors_2: vec4<u32>,
- @location(3) colors_3: vec4<u32>,
- @location(4) colors_4: vec4<u32>,
- @location(5) offsets: vec4<u32>,
- @location(6) direction: vec4<f32>,
-}
-
-@vertex
-fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
- var output: GradientVertexOutput;
-
- output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
- output.raw_position = input.v_pos;
- output.colors_1 = input.colors_1;
- output.colors_2 = input.colors_2;
- output.colors_3 = input.colors_3;
- output.colors_4 = input.colors_4;
- output.offsets = input.offsets;
- output.direction = input.direction;
-
- return output;
-}
-
-fn random(coords: vec2<f32>) -> f32 {
- return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
-}
-
-/// Returns the current interpolated color with a max 8-stop gradient
-fn gradient(
- raw_position: vec2<f32>,
- direction: vec4<f32>,
- colors: array<vec4<f32>, 8>,
- offsets: array<f32, 8>,
- last_index: i32
-) -> vec4<f32> {
- let start = direction.xy;
- let end = direction.zw;
-
- let v1 = end - start;
- let v2 = raw_position - start;
- let unit = normalize(v1);
- let coord_offset = dot(unit, v2) / length(v1);
-
- //need to store these as a var to use dynamic indexing in a loop
- //this is already added to wgsl spec but not in wgpu yet
- var colors_arr = colors;
- var offsets_arr = offsets;
-
- var color: vec4<f32>;
-
- let noise_granularity: f32 = 0.3/255.0;
-
- for (var i: i32 = 0; i < last_index; i++) {
- let curr_offset = offsets_arr[i];
- let next_offset = offsets_arr[i+1];
-
- if (coord_offset <= offsets_arr[0]) {
- color = colors_arr[0];
- }
-
- if (curr_offset <= coord_offset && coord_offset <= next_offset) {
- color = mix(colors_arr[i], colors_arr[i+1], smoothstep(
- curr_offset,
- next_offset,
- coord_offset,
- ));
- }
-
- if (coord_offset >= offsets_arr[last_index]) {
- color = colors_arr[last_index];
- }
- }
-
- return color + mix(-noise_granularity, noise_granularity, random(raw_position));
-}
-
-@fragment
-fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
- let colors = array<vec4<f32>, 8>(
- unpack_u32(input.colors_1.xy),
- unpack_u32(input.colors_1.zw),
- unpack_u32(input.colors_2.xy),
- unpack_u32(input.colors_2.zw),
- unpack_u32(input.colors_3.xy),
- unpack_u32(input.colors_3.zw),
- unpack_u32(input.colors_4.xy),
- unpack_u32(input.colors_4.zw),
- );
-
- let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
- let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
-
- var offsets = array<f32, 8>(
- offsets_1.x,
- offsets_1.y,
- offsets_1.z,
- offsets_1.w,
- offsets_2.x,
- offsets_2.y,
- offsets_2.z,
- offsets_2.w,
- );
-
- var last_index = 7;
- for (var i: i32 = 0; i <= 7; i++) {
- if (offsets[i] >= 1.0) {
- last_index = i;
- break;
- }
- }
-
- return gradient(input.raw_position, input.direction, colors, offsets, last_index);
-}
diff --git a/wgpu/src/shader/triangle/gradient.wgsl b/wgpu/src/shader/triangle/gradient.wgsl
new file mode 100644
index 00000000..1a8ae3b5
--- /dev/null
+++ b/wgpu/src/shader/triangle/gradient.wgsl
@@ -0,0 +1,134 @@
+struct GradientVertexInput {
+ @location(0) v_pos: vec2<f32>,
+ @location(1) @interpolate(flat) colors_1: vec4<u32>,
+ @location(2) @interpolate(flat) colors_2: vec4<u32>,
+ @location(3) @interpolate(flat) colors_3: vec4<u32>,
+ @location(4) @interpolate(flat) colors_4: vec4<u32>,
+ @location(5) @interpolate(flat) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
+}
+
+struct GradientVertexOutput {
+ @builtin(position) position: vec4<f32>,
+ @location(0) raw_position: vec2<f32>,
+ @location(1) @interpolate(flat) colors_1: vec4<u32>,
+ @location(2) @interpolate(flat) colors_2: vec4<u32>,
+ @location(3) @interpolate(flat) colors_3: vec4<u32>,
+ @location(4) @interpolate(flat) colors_4: vec4<u32>,
+ @location(5) @interpolate(flat) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
+}
+
+@vertex
+fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
+ var output: GradientVertexOutput;
+
+ output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
+ output.raw_position = input.v_pos;
+ output.colors_1 = input.colors_1;
+ output.colors_2 = input.colors_2;
+ output.colors_3 = input.colors_3;
+ output.colors_4 = input.colors_4;
+ output.offsets = input.offsets;
+ output.direction = input.direction;
+
+ return output;
+}
+
+/// Returns the current interpolated color with a max 8-stop gradient
+fn gradient(
+ raw_position: vec2<f32>,
+ direction: vec4<f32>,
+ colors: array<vec4<f32>, 8>,
+ offsets: array<f32, 8>,
+ last_index: i32
+) -> vec4<f32> {
+ let start = direction.xy;
+ let end = direction.zw;
+
+ let v1 = end - start;
+ let v2 = raw_position - start;
+ let unit = normalize(v1);
+ let coord_offset = dot(unit, v2) / length(v1);
+
+ //need to store these as a var to use dynamic indexing in a loop
+ //this is already added to wgsl spec but not in wgpu yet
+ var colors_arr = colors;
+ var offsets_arr = offsets;
+
+ var color: vec4<f32>;
+
+ let noise_granularity: f32 = 0.3/255.0;
+
+ for (var i: i32 = 0; i < last_index; i++) {
+ let curr_offset = offsets_arr[i];
+ let next_offset = offsets_arr[i+1];
+
+ if (coord_offset <= offsets_arr[0]) {
+ color = colors_arr[0];
+ }
+
+ if (curr_offset <= coord_offset && coord_offset <= next_offset) {
+ let from_ = colors_arr[i];
+ let to_ = colors_arr[i+1];
+ let factor = smoothstep(curr_offset, next_offset, coord_offset);
+
+ color = interpolate_color(from_, to_, factor);
+ }
+
+ if (coord_offset >= offsets_arr[last_index]) {
+ color = colors_arr[last_index];
+ }
+ }
+
+ return color + mix(-noise_granularity, noise_granularity, random(raw_position));
+}
+
+@fragment
+fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
+ let colors = array<vec4<f32>, 8>(
+ unpack_u32(input.colors_1.xy),
+ unpack_u32(input.colors_1.zw),
+ unpack_u32(input.colors_2.xy),
+ unpack_u32(input.colors_2.zw),
+ unpack_u32(input.colors_3.xy),
+ unpack_u32(input.colors_3.zw),
+ unpack_u32(input.colors_4.xy),
+ unpack_u32(input.colors_4.zw),
+ );
+
+ let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
+ let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
+
+ var offsets = array<f32, 8>(
+ offsets_1.x,
+ offsets_1.y,
+ offsets_1.z,
+ offsets_1.w,
+ offsets_2.x,
+ offsets_2.y,
+ offsets_2.z,
+ offsets_2.w,
+ );
+
+ var last_index = 7;
+ for (var i: i32 = 0; i <= 7; i++) {
+ if (offsets[i] >= 1.0) {
+ last_index = i;
+ break;
+ }
+ }
+
+ return gradient(input.raw_position, input.direction, colors, offsets, last_index);
+}
+
+fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
+ let rg: vec2<f32> = unpack2x16float(color.x);
+ let ba: vec2<f32> = unpack2x16float(color.y);
+
+ return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
+}
+
+fn random(coords: vec2<f32>) -> f32 {
+ return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
+}
diff --git a/wgpu/src/shader/triangle/solid.wgsl b/wgpu/src/shader/triangle/solid.wgsl
new file mode 100644
index 00000000..9ef81982
--- /dev/null
+++ b/wgpu/src/shader/triangle/solid.wgsl
@@ -0,0 +1,24 @@
+struct SolidVertexInput {
+ @location(0) position: vec2<f32>,
+ @location(1) color: vec4<f32>,
+}
+
+struct SolidVertexOutput {
+ @builtin(position) position: vec4<f32>,
+ @location(0) color: vec4<f32>,
+}
+
+@vertex
+fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
+ var out: SolidVertexOutput;
+
+ out.color = input.color;
+ out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
+
+ return out;
+}
+
+@fragment
+fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
+ return input.color;
+}
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 65d3b818..08a8bea6 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -1,20 +1,15 @@
use crate::core::alignment;
-use crate::core::font::{self, Font};
-use crate::core::text::{Hit, LineHeight, Shaping};
-use crate::core::{Pixels, Point, Rectangle, Size};
+use crate::core::{Rectangle, Size};
use crate::graphics::color;
+use crate::graphics::text::cache::{self, Cache};
+use crate::graphics::text::{font_system, to_color, Editor, Paragraph};
use crate::layer::Text;
-use rustc_hash::{FxHashMap, FxHashSet};
use std::borrow::Cow;
use std::cell::RefCell;
-use std::collections::hash_map;
-use std::hash::{BuildHasher, Hash, Hasher};
-use std::sync::Arc;
#[allow(missing_debug_implementations)]
pub struct Pipeline {
- font_system: RefCell<glyphon::FontSystem>,
renderers: Vec<glyphon::TextRenderer>,
atlas: glyphon::TextAtlas,
prepare_layer: usize,
@@ -28,14 +23,8 @@ impl Pipeline {
format: wgpu::TextureFormat,
) -> Self {
Pipeline {
- font_system: RefCell::new(glyphon::FontSystem::new_with_fonts(
- [glyphon::fontdb::Source::Binary(Arc::new(
- include_bytes!("../fonts/Iced-Icons.ttf").as_slice(),
- ))]
- .into_iter(),
- )),
renderers: Vec::new(),
- atlas: glyphon::TextAtlas::new(
+ atlas: glyphon::TextAtlas::with_color_mode(
device,
queue,
format,
@@ -51,9 +40,10 @@ impl Pipeline {
}
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
- let _ = self.font_system.get_mut().db_mut().load_font_source(
- glyphon::fontdb::Source::Binary(Arc::new(bytes.into_owned())),
- );
+ font_system()
+ .write()
+ .expect("Write font system")
+ .load_font(bytes);
self.cache = RefCell::new(Cache::new());
}
@@ -63,114 +53,171 @@ impl Pipeline {
device: &wgpu::Device,
queue: &wgpu::Queue,
sections: &[Text<'_>],
- bounds: Rectangle,
+ layer_bounds: Rectangle,
scale_factor: f32,
target_size: Size<u32>,
- ) -> bool {
+ ) {
if self.renderers.len() <= self.prepare_layer {
self.renderers.push(glyphon::TextRenderer::new(
&mut self.atlas,
device,
- Default::default(),
+ wgpu::MultisampleState::default(),
None,
));
}
- let font_system = self.font_system.get_mut();
+ let mut font_system = font_system().write().expect("Write font system");
+ let font_system = font_system.raw();
+
let renderer = &mut self.renderers[self.prepare_layer];
let cache = self.cache.get_mut();
- if self.prepare_layer == 0 {
- cache.trim(Purpose::Drawing);
+ enum Allocation {
+ Paragraph(Paragraph),
+ Editor(Editor),
+ Cache(cache::KeyHash),
}
- let keys: Vec<_> = sections
+ let allocations: Vec<_> = sections
.iter()
- .map(|section| {
- let (key, _) = cache.allocate(
- font_system,
- Key {
- content: section.content,
- size: section.size,
- line_height: f32::from(
- section
- .line_height
- .to_absolute(Pixels(section.size)),
- ),
- font: section.font,
- bounds: Size {
- width: section.bounds.width,
- height: section.bounds.height,
+ .map(|section| match section {
+ Text::Paragraph { paragraph, .. } => {
+ paragraph.upgrade().map(Allocation::Paragraph)
+ }
+ Text::Editor { editor, .. } => {
+ editor.upgrade().map(Allocation::Editor)
+ }
+ Text::Cached(text) => {
+ let (key, _) = cache.allocate(
+ font_system,
+ cache::Key {
+ content: text.content,
+ size: text.size.into(),
+ line_height: f32::from(
+ text.line_height.to_absolute(text.size),
+ ),
+ font: text.font,
+ bounds: Size {
+ width: text.bounds.width,
+ height: text.bounds.height,
+ },
+ shaping: text.shaping,
},
- shaping: section.shaping,
- },
- Purpose::Drawing,
- );
+ );
- key
+ Some(Allocation::Cache(key))
+ }
})
.collect();
- let bounds = bounds * scale_factor;
-
- let text_areas =
- sections
- .iter()
- .zip(keys.iter())
- .filter_map(|(section, key)| {
- let entry = cache.get(key).expect("Get cached buffer");
-
- let x = section.bounds.x * scale_factor;
- let y = section.bounds.y * scale_factor;
-
- let max_width = entry.bounds.width * scale_factor;
- let total_height = entry.bounds.height * scale_factor;
-
- let left = match section.horizontal_alignment {
- alignment::Horizontal::Left => x,
- alignment::Horizontal::Center => x - max_width / 2.0,
- alignment::Horizontal::Right => x - max_width,
- };
-
- let top = match section.vertical_alignment {
- alignment::Vertical::Top => y,
- alignment::Vertical::Center => y - total_height / 2.0,
- alignment::Vertical::Bottom => y - total_height,
- };
-
- let section_bounds = Rectangle {
- x: left,
- y: top,
- width: section.bounds.width * scale_factor,
- height: section.bounds.height * scale_factor,
- };
-
- let clip_bounds = bounds.intersection(&section_bounds)?;
-
- Some(glyphon::TextArea {
- buffer: &entry.buffer,
- left,
- top,
- scale: scale_factor,
- bounds: glyphon::TextBounds {
- left: clip_bounds.x as i32,
- top: clip_bounds.y as i32,
- right: (clip_bounds.x + clip_bounds.width) as i32,
- bottom: (clip_bounds.y + clip_bounds.height) as i32,
- },
- default_color: {
- let [r, g, b, a] =
- color::pack(section.color).components();
-
- glyphon::Color::rgba(
- (r * 255.0) as u8,
- (g * 255.0) as u8,
- (b * 255.0) as u8,
- (a * 255.0) as u8,
- )
- },
- })
- });
+ let layer_bounds = layer_bounds * scale_factor;
+
+ let text_areas = sections.iter().zip(allocations.iter()).filter_map(
+ |(section, allocation)| {
+ let (
+ buffer,
+ bounds,
+ horizontal_alignment,
+ vertical_alignment,
+ color,
+ ) = match section {
+ Text::Paragraph {
+ position, color, ..
+ } => {
+ use crate::core::text::Paragraph as _;
+
+ let Some(Allocation::Paragraph(paragraph)) = allocation
+ else {
+ return None;
+ };
+
+ (
+ paragraph.buffer(),
+ Rectangle::new(*position, paragraph.min_bounds()),
+ paragraph.horizontal_alignment(),
+ paragraph.vertical_alignment(),
+ *color,
+ )
+ }
+ Text::Editor {
+ position, color, ..
+ } => {
+ use crate::core::text::Editor as _;
+
+ let Some(Allocation::Editor(editor)) = allocation
+ else {
+ return None;
+ };
+
+ (
+ editor.buffer(),
+ Rectangle::new(*position, editor.bounds()),
+ alignment::Horizontal::Left,
+ alignment::Vertical::Top,
+ *color,
+ )
+ }
+ Text::Cached(text) => {
+ let Some(Allocation::Cache(key)) = allocation else {
+ return None;
+ };
+
+ let entry = cache.get(key).expect("Get cached buffer");
+
+ (
+ &entry.buffer,
+ Rectangle::new(
+ text.bounds.position(),
+ entry.min_bounds,
+ ),
+ text.horizontal_alignment,
+ text.vertical_alignment,
+ text.color,
+ )
+ }
+ };
+
+ let bounds = bounds * scale_factor;
+
+ let left = match horizontal_alignment {
+ alignment::Horizontal::Left => bounds.x,
+ alignment::Horizontal::Center => {
+ bounds.x - bounds.width / 2.0
+ }
+ alignment::Horizontal::Right => bounds.x - bounds.width,
+ };
+
+ let top = match vertical_alignment {
+ alignment::Vertical::Top => bounds.y,
+ alignment::Vertical::Center => {
+ bounds.y - bounds.height / 2.0
+ }
+ alignment::Vertical::Bottom => bounds.y - bounds.height,
+ };
+
+ let section_bounds = Rectangle {
+ x: left,
+ y: top,
+ ..bounds
+ };
+
+ let clip_bounds = layer_bounds.intersection(&section_bounds)?;
+
+ Some(glyphon::TextArea {
+ buffer,
+ left,
+ top,
+ scale: scale_factor,
+ bounds: glyphon::TextBounds {
+ left: clip_bounds.x as i32,
+ top: clip_bounds.y as i32,
+ right: (clip_bounds.x + clip_bounds.width) as i32,
+ bottom: (clip_bounds.y + clip_bounds.height) as i32,
+ },
+ default_color: to_color(color),
+ })
+ },
+ );
let result = renderer.prepare(
device,
@@ -188,21 +235,11 @@ impl Pipeline {
match result {
Ok(()) => {
self.prepare_layer += 1;
-
- true
}
- Err(glyphon::PrepareError::AtlasFull(content_type)) => {
- self.prepare_layer = 0;
-
- #[allow(clippy::needless_bool)]
- if self.atlas.grow(device, content_type) {
- false
- } else {
- // If the atlas cannot grow, then all bets are off.
- // Instead of panicking, we will just pray that the result
- // will be somewhat readable...
- true
- }
+ Err(glyphon::PrepareError::AtlasFull) => {
+ // If the atlas cannot grow, then all bets are off.
+ // Instead of panicking, we will just pray that the result
+ // will be somewhat readable...
}
}
}
@@ -229,278 +266,8 @@ impl Pipeline {
pub fn end_frame(&mut self) {
self.atlas.trim();
+ self.cache.get_mut().trim();
self.prepare_layer = 0;
}
-
- pub fn trim_measurements(&mut self) {
- self.cache.get_mut().trim(Purpose::Measuring);
- }
-
- pub fn measure(
- &self,
- content: &str,
- size: f32,
- line_height: LineHeight,
- font: Font,
- bounds: Size,
- shaping: Shaping,
- ) -> Size {
- let mut cache = self.cache.borrow_mut();
-
- let line_height = f32::from(line_height.to_absolute(Pixels(size)));
-
- let (_, entry) = cache.allocate(
- &mut self.font_system.borrow_mut(),
- Key {
- content,
- size,
- line_height,
- font,
- bounds,
- shaping,
- },
- Purpose::Measuring,
- );
-
- entry.bounds
- }
-
- pub fn hit_test(
- &self,
- content: &str,
- size: f32,
- line_height: LineHeight,
- font: Font,
- bounds: Size,
- shaping: Shaping,
- point: Point,
- _nearest_only: bool,
- ) -> Option<Hit> {
- let mut cache = self.cache.borrow_mut();
-
- let line_height = f32::from(line_height.to_absolute(Pixels(size)));
-
- let (_, entry) = cache.allocate(
- &mut self.font_system.borrow_mut(),
- Key {
- content,
- size,
- line_height,
- font,
- bounds,
- shaping,
- },
- Purpose::Measuring,
- );
-
- let cursor = entry.buffer.hit(point.x, point.y)?;
-
- Some(Hit::CharOffset(cursor.index))
- }
-}
-
-fn measure(buffer: &glyphon::Buffer) -> Size {
- let (width, total_lines) = buffer
- .layout_runs()
- .fold((0.0, 0usize), |(width, total_lines), run| {
- (run.line_w.max(width), total_lines + 1)
- });
-
- Size::new(width, total_lines as f32 * buffer.metrics().line_height)
-}
-
-fn to_family(family: font::Family) -> glyphon::Family<'static> {
- match family {
- font::Family::Name(name) => glyphon::Family::Name(name),
- font::Family::SansSerif => glyphon::Family::SansSerif,
- font::Family::Serif => glyphon::Family::Serif,
- font::Family::Cursive => glyphon::Family::Cursive,
- font::Family::Fantasy => glyphon::Family::Fantasy,
- font::Family::Monospace => glyphon::Family::Monospace,
- }
}
-
-fn to_weight(weight: font::Weight) -> glyphon::Weight {
- match weight {
- font::Weight::Thin => glyphon::Weight::THIN,
- font::Weight::ExtraLight => glyphon::Weight::EXTRA_LIGHT,
- font::Weight::Light => glyphon::Weight::LIGHT,
- font::Weight::Normal => glyphon::Weight::NORMAL,
- font::Weight::Medium => glyphon::Weight::MEDIUM,
- font::Weight::Semibold => glyphon::Weight::SEMIBOLD,
- font::Weight::Bold => glyphon::Weight::BOLD,
- font::Weight::ExtraBold => glyphon::Weight::EXTRA_BOLD,
- font::Weight::Black => glyphon::Weight::BLACK,
- }
-}
-
-fn to_stretch(stretch: font::Stretch) -> glyphon::Stretch {
- match stretch {
- font::Stretch::UltraCondensed => glyphon::Stretch::UltraCondensed,
- font::Stretch::ExtraCondensed => glyphon::Stretch::ExtraCondensed,
- font::Stretch::Condensed => glyphon::Stretch::Condensed,
- font::Stretch::SemiCondensed => glyphon::Stretch::SemiCondensed,
- font::Stretch::Normal => glyphon::Stretch::Normal,
- font::Stretch::SemiExpanded => glyphon::Stretch::SemiExpanded,
- font::Stretch::Expanded => glyphon::Stretch::Expanded,
- font::Stretch::ExtraExpanded => glyphon::Stretch::ExtraExpanded,
- font::Stretch::UltraExpanded => glyphon::Stretch::UltraExpanded,
- }
-}
-
-fn to_shaping(shaping: Shaping) -> glyphon::Shaping {
- match shaping {
- Shaping::Basic => glyphon::Shaping::Basic,
- Shaping::Advanced => glyphon::Shaping::Advanced,
- }
-}
-
-struct Cache {
- entries: FxHashMap<KeyHash, Entry>,
- aliases: FxHashMap<KeyHash, KeyHash>,
- recently_measured: FxHashSet<KeyHash>,
- recently_drawn: FxHashSet<KeyHash>,
- hasher: HashBuilder,
-}
-
-struct Entry {
- buffer: glyphon::Buffer,
- bounds: Size,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum Purpose {
- Measuring,
- Drawing,
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-type HashBuilder = twox_hash::RandomXxHashBuilder64;
-
-#[cfg(target_arch = "wasm32")]
-type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>;
-
-impl Cache {
- fn new() -> Self {
- Self {
- entries: FxHashMap::default(),
- aliases: FxHashMap::default(),
- recently_measured: FxHashSet::default(),
- recently_drawn: FxHashSet::default(),
- hasher: HashBuilder::default(),
- }
- }
-
- fn get(&self, key: &KeyHash) -> Option<&Entry> {
- self.entries.get(key)
- }
-
- fn allocate(
- &mut self,
- font_system: &mut glyphon::FontSystem,
- key: Key<'_>,
- purpose: Purpose,
- ) -> (KeyHash, &mut Entry) {
- let hash = key.hash(self.hasher.build_hasher());
-
- let recently_used = match purpose {
- Purpose::Measuring => &mut self.recently_measured,
- Purpose::Drawing => &mut self.recently_drawn,
- };
-
- if let Some(hash) = self.aliases.get(&hash) {
- let _ = recently_used.insert(*hash);
-
- return (*hash, self.entries.get_mut(hash).unwrap());
- }
-
- if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
- let metrics = glyphon::Metrics::new(key.size, key.line_height);
- let mut buffer = glyphon::Buffer::new(font_system, metrics);
-
- buffer.set_size(
- font_system,
- key.bounds.width,
- key.bounds.height.max(key.line_height),
- );
- buffer.set_text(
- font_system,
- key.content,
- glyphon::Attrs::new()
- .family(to_family(key.font.family))
- .weight(to_weight(key.font.weight))
- .stretch(to_stretch(key.font.stretch)),
- to_shaping(key.shaping),
- );
-
- let bounds = measure(&buffer);
- let _ = entry.insert(Entry { buffer, bounds });
-
- for bounds in [
- bounds,
- Size {
- width: key.bounds.width,
- ..bounds
- },
- ] {
- if key.bounds != bounds {
- let _ = self.aliases.insert(
- Key { bounds, ..key }.hash(self.hasher.build_hasher()),
- hash,
- );
- }
- }
- }
-
- let _ = recently_used.insert(hash);
-
- (hash, self.entries.get_mut(&hash).unwrap())
- }
-
- fn trim(&mut self, purpose: Purpose) {
- self.entries.retain(|key, _| {
- self.recently_measured.contains(key)
- || self.recently_drawn.contains(key)
- });
- self.aliases.retain(|_, value| {
- self.recently_measured.contains(value)
- || self.recently_drawn.contains(value)
- });
-
- match purpose {
- Purpose::Measuring => {
- self.recently_measured.clear();
- }
- Purpose::Drawing => {
- self.recently_drawn.clear();
- }
- }
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-struct Key<'a> {
- content: &'a str,
- size: f32,
- line_height: f32,
- font: Font,
- bounds: Size,
- shaping: Shaping,
-}
-
-impl Key<'_> {
- fn hash<H: Hasher>(self, mut hasher: H) -> KeyHash {
- self.content.hash(&mut hasher);
- self.size.to_bits().hash(&mut hasher);
- self.line_height.to_bits().hash(&mut hasher);
- self.font.hash(&mut hasher);
- self.bounds.width.to_bits().hash(&mut hasher);
- self.bounds.height.to_bits().hash(&mut hasher);
- self.shaping.hash(&mut hasher);
-
- hasher.finish()
- }
-}
-
-type KeyHash = u64;
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index d8b23dfe..69270a73 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -300,10 +300,15 @@ impl Pipeline {
wgpu::RenderPassColorAttachment {
view: attachment,
resolve_target,
- ops: wgpu::Operations { load, store: true },
+ ops: wgpu::Operations {
+ load,
+ store: wgpu::StoreOp::Store,
+ },
},
)],
depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
});
let layer = &mut self.layers[layer];
@@ -329,12 +334,12 @@ impl Pipeline {
fn fragment_target(
texture_format: wgpu::TextureFormat,
-) -> Option<wgpu::ColorTargetState> {
- Some(wgpu::ColorTargetState {
+) -> wgpu::ColorTargetState {
+ wgpu::ColorTargetState {
format: texture_format,
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
- })
+ }
}
fn primitive_state() -> wgpu::PrimitiveState {
@@ -349,7 +354,7 @@ fn multisample_state(
antialiasing: Option<Antialiasing>,
) -> wgpu::MultisampleState {
wgpu::MultisampleState {
- count: antialiasing.map(|a| a.sample_count()).unwrap_or(1),
+ count: antialiasing.map(Antialiasing::sample_count).unwrap_or(1),
mask: !0,
alpha_to_coverage_enabled: false,
}
@@ -487,8 +492,10 @@ mod solid {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu.triangle.solid.shader"),
source: wgpu::ShaderSource::Wgsl(
- std::borrow::Cow::Borrowed(include_str!(
- "shader/triangle.wgsl"
+ std::borrow::Cow::Borrowed(concat!(
+ include_str!("shader/triangle.wgsl"),
+ "\n",
+ include_str!("shader/triangle/solid.wgsl"),
)),
),
});
@@ -519,7 +526,7 @@ mod solid {
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "solid_fs_main",
- targets: &[triangle::fragment_target(format)],
+ targets: &[Some(triangle::fragment_target(format))],
}),
primitive: triangle::primitive_state(),
depth_stencil: None,
@@ -537,6 +544,7 @@ mod solid {
}
mod gradient {
+ use crate::graphics::color;
use crate::graphics::mesh;
use crate::graphics::Antialiasing;
use crate::triangle;
@@ -633,9 +641,31 @@ mod gradient {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu.triangle.gradient.shader"),
source: wgpu::ShaderSource::Wgsl(
- std::borrow::Cow::Borrowed(include_str!(
- "shader/triangle.wgsl"
- )),
+ std::borrow::Cow::Borrowed(
+ if color::GAMMA_CORRECTION {
+ concat!(
+ include_str!("shader/triangle.wgsl"),
+ "\n",
+ include_str!(
+ "shader/triangle/gradient.wgsl"
+ ),
+ "\n",
+ include_str!("shader/color/oklab.wgsl")
+ )
+ } else {
+ concat!(
+ include_str!("shader/triangle.wgsl"),
+ "\n",
+ include_str!(
+ "shader/triangle/gradient.wgsl"
+ ),
+ "\n",
+ include_str!(
+ "shader/color/linear_rgb.wgsl"
+ )
+ )
+ },
+ ),
),
});
@@ -673,7 +703,7 @@ mod gradient {
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "gradient_fs_main",
- targets: &[triangle::fragment_target(format)],
+ targets: &[Some(triangle::fragment_target(format))],
}),
primitive: triangle::primitive_state(),
depth_stencil: None,
diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs
index 320b5b12..14abd20b 100644
--- a/wgpu/src/triangle/msaa.rs
+++ b/wgpu/src/triangle/msaa.rs
@@ -167,10 +167,12 @@ impl Blit {
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
- store: true,
+ store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
+ timestamp_writes: None,
+ occlusion_query_set: None,
});
render_pass.set_pipeline(&self.pipeline);
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 814269f3..21406134 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -6,8 +6,6 @@ use crate::graphics::compositor;
use crate::graphics::{Error, Viewport};
use crate::{Backend, Primitive, Renderer, Settings};
-use futures::stream::{self, StreamExt};
-
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use std::marker::PhantomData;
@@ -37,7 +35,7 @@ impl<Theme> Compositor<Theme> {
..Default::default()
});
- log::info!("{:#?}", settings);
+ log::info!("{settings:#?}");
#[cfg(not(target_arch = "wasm32"))]
if log::max_level() >= log::LevelFilter::Info {
@@ -45,7 +43,7 @@ impl<Theme> Compositor<Theme> {
.enumerate_adapters(settings.internal_backend)
.map(|adapter| adapter.get_info())
.collect();
- log::info!("Available adapters: {:#?}", available_adapters);
+ log::info!("Available adapters: {available_adapters:#?}");
}
#[allow(unsafe_code)]
@@ -85,7 +83,7 @@ impl<Theme> Compositor<Theme> {
})
})?;
- log::info!("Selected format: {:?}", format);
+ log::info!("Selected format: {format:?}");
#[cfg(target_arch = "wasm32")]
let limits = [wgpu::Limits::downlevel_webgl2_defaults()
@@ -95,14 +93,15 @@ impl<Theme> Compositor<Theme> {
let limits =
[wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()];
- let limits = limits.into_iter().map(|limits| wgpu::Limits {
+ let mut limits = limits.into_iter().map(|limits| wgpu::Limits {
max_bind_groups: 2,
..limits
});
- let (device, queue) = stream::iter(limits)
- .filter_map(|limits| async {
- adapter.request_device(
+ let (device, queue) =
+ loop {
+ let limits = limits.next()?;
+ let device = adapter.request_device(
&wgpu::DeviceDescriptor {
label: Some(
"iced_wgpu::window::compositor device descriptor",
@@ -111,11 +110,12 @@ impl<Theme> Compositor<Theme> {
limits,
},
None,
- ).await.ok()
- })
- .boxed()
- .next()
- .await?;
+ ).await.ok();
+
+ if let Some(device) = device {
+ break Some(device);
+ }
+ }?;
Some(Compositor {
instance,
@@ -178,6 +178,7 @@ pub fn present<Theme, T: AsRef<str>>(
&compositor.queue,
&mut encoder,
Some(background_color),
+ frame.texture.format(),
view,
primitives,
viewport,
@@ -216,11 +217,22 @@ impl<Theme> graphics::Compositor for Compositor<Theme> {
) -> Result<(Self, Self::Renderer), Error> {
let (compositor, backend) = new(settings, compatible_window)?;
- Ok((compositor, Renderer::new(backend)))
+ Ok((
+ compositor,
+ Renderer::new(
+ backend,
+ settings.default_font,
+ settings.default_text_size,
+ ),
+ ))
}
fn renderer(&self) -> Self::Renderer {
- Renderer::new(self.create_backend())
+ Renderer::new(
+ self.create_backend(),
+ self.settings.default_font,
+ self.settings.default_text_size,
+ )
}
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
@@ -354,6 +366,7 @@ pub fn screenshot<Theme, T: AsRef<str>>(
&compositor.queue,
&mut encoder,
Some(background_color),
+ texture.format(),
&view,
primitives,
viewport,