Merge branch 'master' into beacon
This commit is contained in:
commit
8bd5de72ea
371 changed files with 33138 additions and 12950 deletions
|
|
@ -20,9 +20,10 @@ all-features = true
|
|||
[features]
|
||||
geometry = ["iced_graphics/geometry", "lyon"]
|
||||
image = ["iced_graphics/image"]
|
||||
svg = ["resvg/text"]
|
||||
svg = ["iced_graphics/svg", "resvg/text"]
|
||||
web-colors = ["iced_graphics/web-colors"]
|
||||
webgl = ["wgpu/webgl"]
|
||||
strict-assertions = []
|
||||
|
||||
[dependencies]
|
||||
iced_graphics.workspace = true
|
||||
|
|
@ -34,7 +35,6 @@ glam.workspace = true
|
|||
glyphon.workspace = true
|
||||
guillotiere.workspace = true
|
||||
log.workspace = true
|
||||
once_cell.workspace = true
|
||||
rustc-hash.workspace = true
|
||||
thiserror.workspace = true
|
||||
wgpu.workspace = true
|
||||
|
|
|
|||
|
|
@ -6,14 +6,7 @@
|
|||
|
||||
`iced_wgpu` is a [`wgpu`] renderer for [`iced_runtime`]. For now, it is the default renderer of Iced on [native platforms].
|
||||
|
||||
[`wgpu`] supports most modern graphics backends: Vulkan, Metal, 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.
|
||||
- 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.
|
||||
[`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX12, OpenGL, and WebGPU.
|
||||
|
||||
<p align="center">
|
||||
<img alt="The native target" src="../docs/graphs/native.png" width="80%">
|
||||
|
|
@ -25,29 +18,3 @@ Currently, `iced_wgpu` supports the following primitives:
|
|||
[native platforms]: https://github.com/gfx-rs/wgpu#supported-platforms
|
||||
[WebGPU API]: https://gpuweb.github.io/gpuweb/
|
||||
[`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
|
||||
|
||||
## Installation
|
||||
Add `iced_wgpu` as a dependency in your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
iced_wgpu = "0.10"
|
||||
```
|
||||
|
||||
__Iced moves fast and the `master` branch can contain breaking changes!__ If
|
||||
you want to learn about a specific release, check out [the release list].
|
||||
|
||||
[the release list]: https://github.com/iced-rs/iced/releases
|
||||
|
||||
## Current limitations
|
||||
|
||||
The current implementation is quite naive; it uses:
|
||||
|
||||
- A different pipeline/shader for each primitive
|
||||
- A very simplistic layer model: every `Clip` primitive will generate new layers
|
||||
- _Many_ render passes instead of preparing everything upfront
|
||||
- A glyph cache that is trimmed incorrectly when there are multiple layers (a [`glyph_brush`] limitation)
|
||||
|
||||
Some of these issues are already being worked on! If you want to help, [get in touch!]
|
||||
|
||||
[get in touch!]: ../CONTRIBUTING.md
|
||||
[`glyph_brush`]: https://github.com/alexheretic/glyph-brush
|
||||
|
|
|
|||
|
|
@ -108,12 +108,14 @@ pub fn convert(
|
|||
layout: Some(&pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
entry_point: Some("vs_main"),
|
||||
buffers: &[],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(
|
||||
),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
entry_point: Some("fs_main"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format,
|
||||
blend: Some(wgpu::BlendState {
|
||||
|
|
@ -130,6 +132,8 @@ pub fn convert(
|
|||
}),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(
|
||||
),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
|
|
@ -139,6 +143,7 @@ pub fn convert(
|
|||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Build and draw geometry.
|
||||
use crate::core::text::LineHeight;
|
||||
use crate::core::{
|
||||
Pixels, Point, Radians, Rectangle, Size, Transformation, Vector,
|
||||
self, Pixels, Point, Radians, Rectangle, Size, Svg, Transformation, Vector,
|
||||
};
|
||||
use crate::graphics::cache::{self, Cached};
|
||||
use crate::graphics::color;
|
||||
|
|
@ -11,7 +11,7 @@ use crate::graphics::geometry::{
|
|||
};
|
||||
use crate::graphics::gradient::{self, Gradient};
|
||||
use crate::graphics::mesh::{self, Mesh};
|
||||
use crate::graphics::{self, Text};
|
||||
use crate::graphics::{Image, Text};
|
||||
use crate::text;
|
||||
use crate::triangle;
|
||||
|
||||
|
|
@ -19,16 +19,22 @@ use lyon::geom::euclid;
|
|||
use lyon::tessellation;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Geometry {
|
||||
Live { meshes: Vec<Mesh>, text: Vec<Text> },
|
||||
Live {
|
||||
meshes: Vec<Mesh>,
|
||||
images: Vec<Image>,
|
||||
text: Vec<Text>,
|
||||
},
|
||||
Cached(Cache),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Cache {
|
||||
pub meshes: Option<triangle::Cache>,
|
||||
pub images: Option<Arc<[Image]>>,
|
||||
pub text: Option<text::Cache>,
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +51,17 @@ impl Cached for Geometry {
|
|||
previous: Option<Self::Cache>,
|
||||
) -> Self::Cache {
|
||||
match self {
|
||||
Self::Live { meshes, text } => {
|
||||
Self::Live {
|
||||
meshes,
|
||||
images,
|
||||
text,
|
||||
} => {
|
||||
let images = if images.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Arc::from(images))
|
||||
};
|
||||
|
||||
if let Some(mut previous) = previous {
|
||||
if let Some(cache) = &mut previous.meshes {
|
||||
cache.update(meshes);
|
||||
|
|
@ -59,10 +75,13 @@ impl Cached for Geometry {
|
|||
previous.text = text::Cache::new(group, text);
|
||||
}
|
||||
|
||||
previous.images = images;
|
||||
|
||||
previous
|
||||
} else {
|
||||
Cache {
|
||||
meshes: triangle::Cache::new(meshes),
|
||||
images,
|
||||
text: text::Cache::new(group, text),
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +97,7 @@ pub struct Frame {
|
|||
clip_bounds: Rectangle,
|
||||
buffers: BufferStack,
|
||||
meshes: Vec<Mesh>,
|
||||
images: Vec<Image>,
|
||||
text: Vec<Text>,
|
||||
transforms: Transforms,
|
||||
fill_tessellator: tessellation::FillTessellator,
|
||||
|
|
@ -96,6 +116,7 @@ impl Frame {
|
|||
clip_bounds: bounds,
|
||||
buffers: BufferStack::new(),
|
||||
meshes: Vec::new(),
|
||||
images: Vec::new(),
|
||||
text: Vec::new(),
|
||||
transforms: Transforms {
|
||||
previous: Vec::new(),
|
||||
|
|
@ -232,6 +253,44 @@ impl geometry::frame::Backend for Frame {
|
|||
.expect("Stroke path");
|
||||
}
|
||||
|
||||
fn stroke_rectangle<'a>(
|
||||
&mut self,
|
||||
top_left: Point,
|
||||
size: Size,
|
||||
stroke: impl Into<Stroke<'a>>,
|
||||
) {
|
||||
let stroke = stroke.into();
|
||||
|
||||
let mut buffer = self
|
||||
.buffers
|
||||
.get_stroke(&self.transforms.current.transform_style(stroke.style));
|
||||
|
||||
let top_left = self
|
||||
.transforms
|
||||
.current
|
||||
.0
|
||||
.transform_point(lyon::math::Point::new(top_left.x, top_left.y));
|
||||
|
||||
let size =
|
||||
self.transforms.current.0.transform_vector(
|
||||
lyon::math::Vector::new(size.width, size.height),
|
||||
);
|
||||
|
||||
let mut options = tessellation::StrokeOptions::default();
|
||||
options.line_width = stroke.width;
|
||||
options.start_cap = into_line_cap(stroke.line_cap);
|
||||
options.end_cap = into_line_cap(stroke.line_cap);
|
||||
options.line_join = into_line_join(stroke.line_join);
|
||||
|
||||
self.stroke_tessellator
|
||||
.tessellate_rectangle(
|
||||
&lyon::math::Box2D::new(top_left, top_left + size),
|
||||
&options,
|
||||
buffer.as_mut(),
|
||||
)
|
||||
.expect("Stroke rectangle");
|
||||
}
|
||||
|
||||
fn fill_text(&mut self, text: impl Into<geometry::Text>) {
|
||||
let text = text.into();
|
||||
|
||||
|
|
@ -270,7 +329,7 @@ impl geometry::frame::Backend for Frame {
|
|||
height: f32::INFINITY,
|
||||
};
|
||||
|
||||
self.text.push(graphics::Text::Cached {
|
||||
self.text.push(Text::Cached {
|
||||
content: text.content,
|
||||
bounds,
|
||||
color: text.color,
|
||||
|
|
@ -335,10 +394,12 @@ impl geometry::frame::Backend for Frame {
|
|||
Frame::with_clip(clip_bounds)
|
||||
}
|
||||
|
||||
fn paste(&mut self, frame: Frame, _at: Point) {
|
||||
fn paste(&mut self, frame: Frame) {
|
||||
self.meshes.extend(frame.meshes);
|
||||
self.meshes
|
||||
.extend(frame.buffers.into_meshes(frame.clip_bounds));
|
||||
|
||||
self.images.extend(frame.images);
|
||||
self.text.extend(frame.text);
|
||||
}
|
||||
|
||||
|
|
@ -348,9 +409,32 @@ impl geometry::frame::Backend for Frame {
|
|||
|
||||
Geometry::Live {
|
||||
meshes: self.meshes,
|
||||
images: self.images,
|
||||
text: self.text,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_image(&mut self, bounds: Rectangle, image: impl Into<core::Image>) {
|
||||
let mut image = image.into();
|
||||
|
||||
let (bounds, external_rotation) =
|
||||
self.transforms.current.transform_rectangle(bounds);
|
||||
|
||||
image.rotation += external_rotation;
|
||||
|
||||
self.images.push(Image::Raster(image, bounds));
|
||||
}
|
||||
|
||||
fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
|
||||
let mut svg = svg.into();
|
||||
|
||||
let (bounds, external_rotation) =
|
||||
self.transforms.current.transform_rectangle(bounds);
|
||||
|
||||
svg.rotation += external_rotation;
|
||||
|
||||
self.images.push(Image::Vector(svg, bounds));
|
||||
}
|
||||
}
|
||||
|
||||
enum Buffer {
|
||||
|
|
@ -518,6 +602,21 @@ impl Transform {
|
|||
|
||||
gradient
|
||||
}
|
||||
|
||||
fn transform_rectangle(
|
||||
&self,
|
||||
rectangle: Rectangle,
|
||||
) -> (Rectangle, Radians) {
|
||||
let top_left = self.transform_point(rectangle.position());
|
||||
let top_right = self.transform_point(
|
||||
rectangle.position() + Vector::new(rectangle.width, 0.0),
|
||||
);
|
||||
let bottom_left = self.transform_point(
|
||||
rectangle.position() + Vector::new(0.0, rectangle.height),
|
||||
);
|
||||
|
||||
Rectangle::with_vertices(top_left, top_right, bottom_left)
|
||||
}
|
||||
}
|
||||
struct GradientVertex2DBuilder {
|
||||
gradient: gradient::Packed,
|
||||
|
|
@ -614,7 +713,7 @@ fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule {
|
|||
|
||||
pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
|
||||
use lyon::algorithms::walk::{
|
||||
walk_along_path, RepeatedPattern, WalkerEvent,
|
||||
RepeatedPattern, WalkerEvent, walk_along_path,
|
||||
};
|
||||
use lyon::path::iterator::PathIterator;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ mod raster;
|
|||
#[cfg(feature = "svg")]
|
||||
mod vector;
|
||||
|
||||
use crate::core::{Rectangle, Size, Transformation};
|
||||
use crate::Buffer;
|
||||
use crate::core::{Rectangle, Size, Transformation};
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ impl Pipeline {
|
|||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
entry_point: Some("vs_main"),
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: mem::size_of::<Instance>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
|
|
@ -149,12 +149,16 @@ impl Pipeline {
|
|||
6 => Float32x2,
|
||||
// Layer
|
||||
7 => Sint32,
|
||||
// Snap
|
||||
8 => Uint32,
|
||||
),
|
||||
}],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
entry_point: Some("fs_main"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format,
|
||||
blend: Some(wgpu::BlendState {
|
||||
|
|
@ -171,6 +175,8 @@ impl Pipeline {
|
|||
}),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
|
|
@ -184,6 +190,7 @@ impl Pipeline {
|
|||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
Pipeline {
|
||||
|
|
@ -212,31 +219,24 @@ impl Pipeline {
|
|||
transformation: Transformation,
|
||||
scale: f32,
|
||||
) {
|
||||
let transformation = transformation * Transformation::scale(scale);
|
||||
|
||||
let nearest_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||
let linear_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||
|
||||
for image in images {
|
||||
match &image {
|
||||
#[cfg(feature = "image")]
|
||||
Image::Raster {
|
||||
handle,
|
||||
filter_method,
|
||||
bounds,
|
||||
rotation,
|
||||
opacity,
|
||||
} => {
|
||||
Image::Raster(image, bounds) => {
|
||||
if let Some(atlas_entry) =
|
||||
cache.upload_raster(device, encoder, handle)
|
||||
cache.upload_raster(device, encoder, &image.handle)
|
||||
{
|
||||
add_instances(
|
||||
[bounds.x, bounds.y],
|
||||
[bounds.width, bounds.height],
|
||||
f32::from(*rotation),
|
||||
*opacity,
|
||||
f32::from(image.rotation),
|
||||
image.opacity,
|
||||
image.snap,
|
||||
atlas_entry,
|
||||
match filter_method {
|
||||
match image.filter_method {
|
||||
crate::core::image::FilterMethod::Nearest => {
|
||||
nearest_instances
|
||||
}
|
||||
|
|
@ -251,23 +251,23 @@ impl Pipeline {
|
|||
Image::Raster { .. } => {}
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
Image::Vector {
|
||||
handle,
|
||||
color,
|
||||
bounds,
|
||||
rotation,
|
||||
opacity,
|
||||
} => {
|
||||
Image::Vector(svg, bounds) => {
|
||||
let size = [bounds.width, bounds.height];
|
||||
|
||||
if let Some(atlas_entry) = cache.upload_vector(
|
||||
device, encoder, handle, *color, size, scale,
|
||||
device,
|
||||
encoder,
|
||||
&svg.handle,
|
||||
svg.color,
|
||||
size,
|
||||
scale,
|
||||
) {
|
||||
add_instances(
|
||||
[bounds.x, bounds.y],
|
||||
size,
|
||||
f32::from(*rotation),
|
||||
*opacity,
|
||||
f32::from(svg.rotation),
|
||||
svg.opacity,
|
||||
true,
|
||||
atlas_entry,
|
||||
nearest_instances,
|
||||
);
|
||||
|
|
@ -300,6 +300,7 @@ impl Pipeline {
|
|||
nearest_instances,
|
||||
linear_instances,
|
||||
transformation,
|
||||
scale,
|
||||
);
|
||||
|
||||
self.prepare_layer += 1;
|
||||
|
|
@ -375,9 +376,12 @@ impl Layer {
|
|||
nearest_instances: &[Instance],
|
||||
linear_instances: &[Instance],
|
||||
transformation: Transformation,
|
||||
scale_factor: f32,
|
||||
) {
|
||||
let uniforms = Uniforms {
|
||||
transform: transformation.into(),
|
||||
scale_factor,
|
||||
_padding: [0.0; 3],
|
||||
};
|
||||
|
||||
let bytes = bytemuck::bytes_of(&uniforms);
|
||||
|
|
@ -492,6 +496,7 @@ struct Instance {
|
|||
_position_in_atlas: [f32; 2],
|
||||
_size_in_atlas: [f32; 2],
|
||||
_layer: u32,
|
||||
_snap: u32,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
|
|
@ -502,6 +507,10 @@ impl Instance {
|
|||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
struct Uniforms {
|
||||
transform: [f32; 16],
|
||||
scale_factor: f32,
|
||||
// Uniforms must be aligned to their largest member,
|
||||
// this uses a mat4x4<f32> which aligns to 16, so align to that
|
||||
_padding: [f32; 3],
|
||||
}
|
||||
|
||||
fn add_instances(
|
||||
|
|
@ -509,6 +518,7 @@ fn add_instances(
|
|||
image_size: [f32; 2],
|
||||
rotation: f32,
|
||||
opacity: f32,
|
||||
snap: bool,
|
||||
entry: &atlas::Entry,
|
||||
instances: &mut Vec<Instance>,
|
||||
) {
|
||||
|
|
@ -525,6 +535,7 @@ fn add_instances(
|
|||
image_size,
|
||||
rotation,
|
||||
opacity,
|
||||
snap,
|
||||
allocation,
|
||||
instances,
|
||||
);
|
||||
|
|
@ -554,8 +565,8 @@ fn add_instances(
|
|||
];
|
||||
|
||||
add_instance(
|
||||
position, center, size, rotation, opacity, allocation,
|
||||
instances,
|
||||
position, center, size, rotation, opacity, snap,
|
||||
allocation, instances,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -569,6 +580,7 @@ fn add_instance(
|
|||
size: [f32; 2],
|
||||
rotation: f32,
|
||||
opacity: f32,
|
||||
snap: bool,
|
||||
allocation: &atlas::Allocation,
|
||||
instances: &mut Vec<Instance>,
|
||||
) {
|
||||
|
|
@ -591,6 +603,7 @@ fn add_instance(
|
|||
(height as f32 - 1.0) / atlas::SIZE as f32,
|
||||
],
|
||||
_layer: layer as u32,
|
||||
_snap: snap as u32,
|
||||
};
|
||||
|
||||
instances.push(instance);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::core::image;
|
||||
use crate::core::Size;
|
||||
use crate::core::image;
|
||||
use crate::graphics;
|
||||
use crate::graphics::image::image_rs;
|
||||
use crate::image::atlas::{self, Atlas};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use crate::core::svg;
|
||||
use crate::core::{Color, Size};
|
||||
use crate::graphics::text;
|
||||
use crate::image::atlas::{self, Atlas};
|
||||
|
||||
use resvg::tiny_skia;
|
||||
use resvg::usvg::{self, TreeTextToPath};
|
||||
use resvg::usvg;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Entry in cache corresponding to an svg handle
|
||||
pub enum Svg {
|
||||
|
|
@ -21,7 +21,7 @@ impl Svg {
|
|||
pub fn viewport_dimensions(&self) -> Size<u32> {
|
||||
match self {
|
||||
Svg::Loaded(tree) => {
|
||||
let size = tree.size;
|
||||
let size = tree.size();
|
||||
|
||||
Size::new(size.width() as u32, size.height() as u32)
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ pub struct Cache {
|
|||
svg_hits: FxHashSet<u64>,
|
||||
rasterized_hits: FxHashSet<(u64, u32, u32, ColorFilter)>,
|
||||
should_trim: bool,
|
||||
fontdb: Option<Arc<usvg::fontdb::Database>>,
|
||||
}
|
||||
|
||||
type ColorFilter = Option<[u8; 4]>;
|
||||
|
|
@ -45,38 +46,43 @@ type ColorFilter = Option<[u8; 4]>;
|
|||
impl Cache {
|
||||
/// Load svg
|
||||
pub fn load(&mut self, handle: &svg::Handle) -> &Svg {
|
||||
use usvg::TreeParsing;
|
||||
|
||||
if self.svgs.contains_key(&handle.id()) {
|
||||
return self.svgs.get(&handle.id()).unwrap();
|
||||
}
|
||||
|
||||
let mut svg = match handle.data() {
|
||||
// TODO: Reuse `cosmic-text` font database
|
||||
if self.fontdb.is_none() {
|
||||
let mut fontdb = usvg::fontdb::Database::new();
|
||||
fontdb.load_system_fonts();
|
||||
|
||||
self.fontdb = Some(Arc::new(fontdb));
|
||||
}
|
||||
|
||||
let options = usvg::Options {
|
||||
fontdb: self
|
||||
.fontdb
|
||||
.as_ref()
|
||||
.expect("fontdb must be initialized")
|
||||
.clone(),
|
||||
..usvg::Options::default()
|
||||
};
|
||||
|
||||
let svg = match handle.data() {
|
||||
svg::Data::Path(path) => fs::read_to_string(path)
|
||||
.ok()
|
||||
.and_then(|contents| {
|
||||
usvg::Tree::from_str(&contents, &usvg::Options::default())
|
||||
.ok()
|
||||
usvg::Tree::from_str(&contents, &options).ok()
|
||||
})
|
||||
.map(Svg::Loaded)
|
||||
.unwrap_or(Svg::NotFound),
|
||||
svg::Data::Bytes(bytes) => {
|
||||
match usvg::Tree::from_data(bytes, &usvg::Options::default()) {
|
||||
match usvg::Tree::from_data(bytes, &options) {
|
||||
Ok(tree) => Svg::Loaded(tree),
|
||||
Err(_) => Svg::NotFound,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Svg::Loaded(svg) = &mut svg {
|
||||
if svg.has_text_nodes() {
|
||||
let mut font_system =
|
||||
text::font_system().write().expect("Write font system");
|
||||
|
||||
svg.convert_text(font_system.raw().db_mut());
|
||||
}
|
||||
}
|
||||
|
||||
self.should_trim = true;
|
||||
|
||||
let _ = self.svgs.insert(handle.id(), svg);
|
||||
|
|
@ -127,7 +133,7 @@ impl Cache {
|
|||
// It would be cool to be able to smooth resize the `svg` example.
|
||||
let mut img = tiny_skia::Pixmap::new(width, height)?;
|
||||
|
||||
let tree_size = tree.size.to_int_size();
|
||||
let tree_size = tree.size().to_int_size();
|
||||
|
||||
let target_size = if width > height {
|
||||
tree_size.scale_to_width(width)
|
||||
|
|
@ -147,8 +153,7 @@ impl Cache {
|
|||
tiny_skia::Transform::default()
|
||||
};
|
||||
|
||||
resvg::Tree::from_usvg(tree)
|
||||
.render(transform, &mut img.as_mut());
|
||||
resvg::render(tree, transform, &mut img.as_mut());
|
||||
|
||||
let mut rgba = img.take();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::core::{
|
||||
renderer, Background, Color, Point, Radians, Rectangle, Transformation,
|
||||
self, Background, Color, Point, Rectangle, Svg, Transformation, renderer,
|
||||
};
|
||||
use crate::graphics;
|
||||
use crate::graphics::Mesh;
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::layer;
|
||||
use crate::graphics::text::{Editor, Paragraph};
|
||||
use crate::graphics::Mesh;
|
||||
use crate::image::{self, Image};
|
||||
use crate::primitive::{self, Primitive};
|
||||
use crate::quad::{self, Quad};
|
||||
|
|
@ -20,8 +20,8 @@ pub struct Layer {
|
|||
pub quads: quad::Batch,
|
||||
pub triangles: triangle::Batch,
|
||||
pub primitives: primitive::Batch,
|
||||
pub text: text::Batch,
|
||||
pub images: image::Batch,
|
||||
pub text: text::Batch,
|
||||
pending_meshes: Vec<Mesh>,
|
||||
pending_text: Vec<Text>,
|
||||
}
|
||||
|
|
@ -112,42 +112,35 @@ impl Layer {
|
|||
self.pending_text.push(text);
|
||||
}
|
||||
|
||||
pub fn draw_image(
|
||||
pub fn draw_image(&mut self, image: Image, transformation: Transformation) {
|
||||
match image {
|
||||
Image::Raster(image, bounds) => {
|
||||
self.draw_raster(image, bounds, transformation);
|
||||
}
|
||||
Image::Vector(svg, bounds) => {
|
||||
self.draw_svg(svg, bounds, transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_raster(
|
||||
&mut self,
|
||||
handle: crate::core::image::Handle,
|
||||
filter_method: crate::core::image::FilterMethod,
|
||||
image: core::Image,
|
||||
bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
let image = Image::Raster {
|
||||
handle,
|
||||
filter_method,
|
||||
bounds: bounds * transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
};
|
||||
let image = Image::Raster(image, bounds * transformation);
|
||||
|
||||
self.images.push(image);
|
||||
}
|
||||
|
||||
pub fn draw_svg(
|
||||
&mut self,
|
||||
handle: crate::core::svg::Handle,
|
||||
color: Option<Color>,
|
||||
svg: Svg,
|
||||
bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
let svg = Image::Vector {
|
||||
handle,
|
||||
color,
|
||||
bounds: bounds * transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
};
|
||||
let svg = Image::Vector(svg, bounds * transformation);
|
||||
|
||||
self.images.push(svg);
|
||||
}
|
||||
|
|
|
|||
127
wgpu/src/lib.rs
127
wgpu/src/lib.rs
|
|
@ -63,8 +63,8 @@ pub use geometry::Geometry;
|
|||
use crate::core::{
|
||||
Background, Color, Font, Pixels, Point, Rectangle, Transformation,
|
||||
};
|
||||
use crate::graphics::text::{Editor, Paragraph};
|
||||
use crate::graphics::Viewport;
|
||||
use crate::graphics::text::{Editor, Paragraph};
|
||||
|
||||
/// A [`wgpu`] graphics renderer for [`iced`].
|
||||
///
|
||||
|
|
@ -142,7 +142,19 @@ impl Renderer {
|
|||
|
||||
self.text_viewport.update(queue, viewport.physical_size());
|
||||
|
||||
let physical_bounds = Rectangle::<f32>::from(Rectangle::with_size(
|
||||
viewport.physical_size(),
|
||||
));
|
||||
|
||||
for layer in self.layers.iter_mut() {
|
||||
if physical_bounds
|
||||
.intersection(&(layer.bounds * scale_factor))
|
||||
.and_then(Rectangle::snap)
|
||||
.is_none()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if !layer.quads.is_empty() {
|
||||
engine.quad_pipeline.prepare(
|
||||
device,
|
||||
|
|
@ -179,19 +191,6 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
engine.text_pipeline.prepare(
|
||||
device,
|
||||
queue,
|
||||
&self.text_viewport,
|
||||
encoder,
|
||||
&mut self.text_storage,
|
||||
&layer.text,
|
||||
layer.bounds,
|
||||
Transformation::scale(scale_factor),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
if !layer.images.is_empty() {
|
||||
engine.image_pipeline.prepare(
|
||||
|
|
@ -204,6 +203,19 @@ impl Renderer {
|
|||
scale_factor,
|
||||
);
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
engine.text_pipeline.prepare(
|
||||
device,
|
||||
queue,
|
||||
&self.text_viewport,
|
||||
encoder,
|
||||
&mut self.text_storage,
|
||||
&layer.text,
|
||||
layer.bounds,
|
||||
Transformation::scale(scale_factor),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +278,7 @@ impl Renderer {
|
|||
|
||||
for layer in self.layers.iter() {
|
||||
let Some(physical_bounds) =
|
||||
physical_bounds.intersection(&(layer.bounds * scale))
|
||||
physical_bounds.intersection(&(layer.bounds * scale_factor))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
|
@ -356,17 +368,6 @@ impl Renderer {
|
|||
));
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
text_layer += engine.text_pipeline.render(
|
||||
&self.text_viewport,
|
||||
&self.text_storage,
|
||||
text_layer,
|
||||
&layer.text,
|
||||
scissor_rect,
|
||||
&mut render_pass,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
if !layer.images.is_empty() {
|
||||
engine.image_pipeline.render(
|
||||
|
|
@ -378,6 +379,17 @@ impl Renderer {
|
|||
|
||||
image_layer += 1;
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
text_layer += engine.text_pipeline.render(
|
||||
&self.text_viewport,
|
||||
&self.text_storage,
|
||||
text_layer,
|
||||
&layer.text,
|
||||
scissor_rect,
|
||||
&mut render_pass,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = ManuallyDrop::into_inner(render_pass);
|
||||
|
|
@ -481,23 +493,9 @@ impl core::image::Renderer for Renderer {
|
|||
self.image_cache.borrow_mut().measure_image(handle)
|
||||
}
|
||||
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
handle: Self::Handle,
|
||||
filter_method: core::image::FilterMethod,
|
||||
bounds: Rectangle,
|
||||
rotation: core::Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
fn draw_image(&mut self, image: core::Image, bounds: Rectangle) {
|
||||
let (layer, transformation) = self.layers.current_mut();
|
||||
layer.draw_image(
|
||||
handle,
|
||||
filter_method,
|
||||
bounds,
|
||||
transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
);
|
||||
layer.draw_raster(image, bounds, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -507,28 +505,24 @@ impl core::svg::Renderer for Renderer {
|
|||
self.image_cache.borrow_mut().measure_svg(handle)
|
||||
}
|
||||
|
||||
fn draw_svg(
|
||||
&mut self,
|
||||
handle: core::svg::Handle,
|
||||
color_filter: Option<Color>,
|
||||
bounds: Rectangle,
|
||||
rotation: core::Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle) {
|
||||
let (layer, transformation) = self.layers.current_mut();
|
||||
layer.draw_svg(
|
||||
handle,
|
||||
color_filter,
|
||||
bounds,
|
||||
transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
);
|
||||
layer.draw_svg(svg, bounds, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
impl graphics::mesh::Renderer for Renderer {
|
||||
fn draw_mesh(&mut self, mesh: graphics::Mesh) {
|
||||
debug_assert!(
|
||||
!mesh.indices().is_empty(),
|
||||
"Mesh must not have empty indices"
|
||||
);
|
||||
|
||||
debug_assert!(
|
||||
mesh.indices().len() % 3 == 0,
|
||||
"Mesh indices length must be a multiple of 3"
|
||||
);
|
||||
|
||||
let (layer, transformation) = self.layers.current_mut();
|
||||
layer.draw_mesh(mesh, transformation);
|
||||
}
|
||||
|
|
@ -547,8 +541,17 @@ impl graphics::geometry::Renderer for Renderer {
|
|||
let (layer, transformation) = self.layers.current_mut();
|
||||
|
||||
match geometry {
|
||||
Geometry::Live { meshes, text } => {
|
||||
Geometry::Live {
|
||||
meshes,
|
||||
images,
|
||||
text,
|
||||
} => {
|
||||
layer.draw_mesh_group(meshes, transformation);
|
||||
|
||||
for image in images {
|
||||
layer.draw_image(image, transformation);
|
||||
}
|
||||
|
||||
layer.draw_text_group(text, transformation);
|
||||
}
|
||||
Geometry::Cached(cache) => {
|
||||
|
|
@ -556,6 +559,12 @@ impl graphics::geometry::Renderer for Renderer {
|
|||
layer.draw_mesh_cache(meshes, transformation);
|
||||
}
|
||||
|
||||
if let Some(images) = cache.images {
|
||||
for image in images.iter().cloned() {
|
||||
layer.draw_image(image, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(text) = cache.text {
|
||||
layer.draw_text_cache(text, transformation);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::Buffer;
|
||||
use crate::graphics::gradient;
|
||||
use crate::quad::{self, Quad};
|
||||
use crate::Buffer;
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use std::ops::Range;
|
||||
|
|
@ -124,7 +124,7 @@ impl Pipeline {
|
|||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "gradient_vs_main",
|
||||
entry_point: Some("gradient_vs_main"),
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Gradient>()
|
||||
as u64,
|
||||
|
|
@ -152,11 +152,15 @@ impl Pipeline {
|
|||
9 => Float32
|
||||
),
|
||||
}],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "gradient_fs_main",
|
||||
entry_point: Some("gradient_fs_main"),
|
||||
targets: &quad::color_target_state(format),
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
|
|
@ -170,6 +174,7 @@ impl Pipeline {
|
|||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -188,9 +193,6 @@ impl Pipeline {
|
|||
layer: &'a Layer,
|
||||
range: Range<usize>,
|
||||
) {
|
||||
#[cfg(feature = "tracing")]
|
||||
let _ = tracing::info_span!("Wgpu::Quad::Gradient", "DRAW").entered();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::Buffer;
|
||||
use crate::graphics::color;
|
||||
use crate::quad::{self, Quad};
|
||||
use crate::Buffer;
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use std::ops::Range;
|
||||
|
|
@ -89,7 +89,7 @@ impl Pipeline {
|
|||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "solid_vs_main",
|
||||
entry_point: Some("solid_vs_main"),
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Solid>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
|
|
@ -114,11 +114,15 @@ impl Pipeline {
|
|||
8 => Float32,
|
||||
),
|
||||
}],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "solid_fs_main",
|
||||
entry_point: Some("solid_fs_main"),
|
||||
targets: &quad::color_target_state(format),
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
|
|
@ -132,6 +136,7 @@ impl Pipeline {
|
|||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
Self { pipeline }
|
||||
|
|
@ -144,9 +149,6 @@ impl Pipeline {
|
|||
layer: &'a Layer,
|
||||
range: Range<usize>,
|
||||
) {
|
||||
#[cfg(feature = "tracing")]
|
||||
let _ = tracing::info_span!("Wgpu::Quad::Solid", "DRAW").entered();
|
||||
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, constants, &[]);
|
||||
render_pass.set_vertex_buffer(0, layer.instances.slice(..));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
struct Globals {
|
||||
transform: mat4x4<f32>,
|
||||
scale_factor: f32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> globals: Globals;
|
||||
|
|
@ -16,6 +17,7 @@ struct VertexInput {
|
|||
@location(5) atlas_pos: vec2<f32>,
|
||||
@location(6) atlas_scale: vec2<f32>,
|
||||
@location(7) layer: i32,
|
||||
@location(8) snap: u32,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
|
|
@ -38,7 +40,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
|||
out.opacity = input.opacity;
|
||||
|
||||
// Calculate the vertex position and move the center to the origin
|
||||
v_pos = round(input.pos) + v_pos * input.scale - input.center;
|
||||
v_pos = input.pos + v_pos * input.scale - input.center;
|
||||
|
||||
// Apply the rotation around the center of the image
|
||||
let cos_rot = cos(input.rotation);
|
||||
|
|
@ -51,7 +53,13 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
|||
);
|
||||
|
||||
// Calculate the final position of the vertex
|
||||
out.position = globals.transform * (vec4<f32>(input.center, 0.0, 0.0) + rotate * vec4<f32>(v_pos, 0.0, 1.0));
|
||||
out.position = vec4(vec2(globals.scale_factor), 1.0, 1.0) * (vec4<f32>(input.center, 0.0, 0.0) + rotate * vec4<f32>(v_pos, 0.0, 1.0));
|
||||
|
||||
if bool(input.snap) {
|
||||
out.position = round(out.position);
|
||||
}
|
||||
|
||||
out.position = globals.transform * out.position;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ fn rounded_box_sdf(to_center: vec2<f32>, size: vec2<f32>, radius: f32) -> f32 {
|
|||
return length(max(abs(to_center) - size + vec2<f32>(radius, radius), vec2<f32>(0.0, 0.0))) - radius;
|
||||
}
|
||||
|
||||
// Based on the fragment position and the center of the quad, select one of the 4 radi.
|
||||
// Based on the fragment position and the center of the quad, select one of the 4 radii.
|
||||
// Order matches CSS border radius attribute:
|
||||
// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left
|
||||
fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>) -> f32 {
|
||||
var rx = radi.x;
|
||||
var ry = radi.y;
|
||||
rx = select(radi.x, radi.y, position.x > center.x);
|
||||
ry = select(radi.w, radi.z, position.x > center.x);
|
||||
// radii.x = top-left, radii.y = top-right, radii.z = bottom-right, radii.w = bottom-left
|
||||
fn select_border_radius(radii: vec4<f32>, position: vec2<f32>, center: vec2<f32>) -> f32 {
|
||||
var rx = radii.x;
|
||||
var ry = radii.y;
|
||||
rx = select(radii.x, radii.y, position.x > center.x);
|
||||
ry = select(radii.w, radii.z, position.x > center.x);
|
||||
rx = select(rx, ry, position.y > center.y);
|
||||
return rx;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,15 @@ fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
|
|||
|
||||
var pos: vec2<f32> = (input.pos + min(input.shadow_offset, vec2<f32>(0.0, 0.0)) - input.shadow_blur_radius) * globals.scale;
|
||||
var scale: vec2<f32> = (input.scale + vec2<f32>(abs(input.shadow_offset.x), abs(input.shadow_offset.y)) + input.shadow_blur_radius * 2.0) * globals.scale;
|
||||
var snap: vec2<f32> = vec2<f32>(0.0, 0.0);
|
||||
|
||||
if input.scale.x == 1.0 {
|
||||
snap.x = round(pos.x) - pos.x;
|
||||
}
|
||||
|
||||
if input.scale.y == 1.0 {
|
||||
snap.y = round(pos.y) - pos.y;
|
||||
}
|
||||
|
||||
var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
|
||||
var border_radius: vec4<f32> = vec4<f32>(
|
||||
|
|
@ -43,13 +52,13 @@ fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
|
|||
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)
|
||||
vec4<f32>(pos - vec2<f32>(0.5, 0.5) + snap, 0.0, 1.0)
|
||||
);
|
||||
|
||||
out.position = globals.transform * transform * vec4<f32>(vertex_position(input.vertex_index), 0.0, 1.0);
|
||||
out.color = input.color;
|
||||
out.border_color = input.border_color;
|
||||
out.pos = input.pos * globals.scale;
|
||||
out.pos = input.pos * globals.scale + snap;
|
||||
out.scale = input.scale * globals.scale;
|
||||
out.border_radius = border_radius * globals.scale;
|
||||
out.border_width = input.border_width * globals.scale;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@ use crate::core::{Rectangle, Size, Transformation};
|
|||
use crate::graphics::cache;
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::text::cache::{self as text_cache, Cache as BufferCache};
|
||||
use crate::graphics::text::{font_system, to_color, Editor, Paragraph};
|
||||
use crate::graphics::text::{Editor, Paragraph, font_system, to_color};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::collections::hash_map;
|
||||
use std::rc::{self, Rc};
|
||||
use std::sync::atomic::{self, AtomicU64};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{self, Arc};
|
||||
|
||||
pub use crate::graphics::Text;
|
||||
|
||||
|
|
@ -37,7 +36,7 @@ pub enum Item {
|
|||
pub struct Cache {
|
||||
id: Id,
|
||||
group: cache::Group,
|
||||
text: Rc<[Text]>,
|
||||
text: Arc<[Text]>,
|
||||
version: usize,
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +54,7 @@ impl Cache {
|
|||
Some(Self {
|
||||
id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)),
|
||||
group,
|
||||
text: Rc::from(text),
|
||||
text: Arc::from(text),
|
||||
version: 0,
|
||||
})
|
||||
}
|
||||
|
|
@ -65,7 +64,7 @@ impl Cache {
|
|||
return;
|
||||
}
|
||||
|
||||
self.text = Rc::from(text);
|
||||
self.text = Arc::from(text);
|
||||
self.version += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -76,8 +75,8 @@ struct Upload {
|
|||
transformation: Transformation,
|
||||
version: usize,
|
||||
group_version: usize,
|
||||
text: rc::Weak<[Text]>,
|
||||
_atlas: rc::Weak<()>,
|
||||
text: sync::Weak<[Text]>,
|
||||
_atlas: sync::Weak<()>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -90,7 +89,7 @@ struct Group {
|
|||
atlas: glyphon::TextAtlas,
|
||||
version: usize,
|
||||
should_trim: bool,
|
||||
handle: Rc<()>, // Keeps track of active uploads
|
||||
handle: Arc<()>, // Keeps track of active uploads
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
|
|
@ -136,7 +135,7 @@ impl Storage {
|
|||
),
|
||||
version: 0,
|
||||
should_trim: false,
|
||||
handle: Rc::new(()),
|
||||
handle: Arc::new(()),
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -167,7 +166,7 @@ impl Storage {
|
|||
group.should_trim =
|
||||
group.should_trim || upload.version != cache.version;
|
||||
|
||||
upload.text = Rc::downgrade(&cache.text);
|
||||
upload.text = Arc::downgrade(&cache.text);
|
||||
upload.version = cache.version;
|
||||
upload.group_version = group.version;
|
||||
upload.transformation = new_transformation;
|
||||
|
|
@ -206,8 +205,8 @@ impl Storage {
|
|||
transformation: new_transformation,
|
||||
version: 0,
|
||||
group_version: group.version,
|
||||
text: Rc::downgrade(&cache.text),
|
||||
_atlas: Rc::downgrade(&group.handle),
|
||||
text: Arc::downgrade(&cache.text),
|
||||
_atlas: Arc::downgrade(&group.handle),
|
||||
});
|
||||
|
||||
group.should_trim = cache.group.is_singleton();
|
||||
|
|
@ -226,7 +225,7 @@ impl Storage {
|
|||
.retain(|_id, upload| upload.text.strong_count() > 0);
|
||||
|
||||
self.groups.retain(|id, group| {
|
||||
let active_uploads = Rc::weak_count(&group.handle);
|
||||
let active_uploads = Arc::weak_count(&group.handle);
|
||||
|
||||
if active_uploads == 0 {
|
||||
log::debug!("Dropping text atlas: {id:?}");
|
||||
|
|
@ -585,7 +584,13 @@ fn prepare(
|
|||
|
||||
(
|
||||
buffer.as_ref(),
|
||||
Rectangle::new(raw.position, Size::new(width, height)),
|
||||
Rectangle::new(
|
||||
raw.position,
|
||||
Size::new(
|
||||
width.unwrap_or(layer_bounds.width),
|
||||
height.unwrap_or(layer_bounds.height),
|
||||
),
|
||||
),
|
||||
alignment::Horizontal::Left,
|
||||
alignment::Vertical::Top,
|
||||
raw.color,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
//! Draw meshes of triangles.
|
||||
mod msaa;
|
||||
|
||||
use crate::core::{Rectangle, Size, Transformation};
|
||||
use crate::graphics::mesh::{self, Mesh};
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::Buffer;
|
||||
use crate::core::{Rectangle, Size, Transformation};
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::graphics::mesh::{self, Mesh};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::collections::hash_map;
|
||||
use std::rc::{self, Rc};
|
||||
use std::sync::atomic::{self, AtomicU64};
|
||||
use std::sync::{self, Arc};
|
||||
|
||||
const INITIAL_INDEX_COUNT: usize = 1_000;
|
||||
const INITIAL_VERTEX_COUNT: usize = 1_000;
|
||||
|
|
@ -31,7 +31,7 @@ pub enum Item {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Cache {
|
||||
id: Id,
|
||||
batch: Rc<[Mesh]>,
|
||||
batch: Arc<[Mesh]>,
|
||||
version: usize,
|
||||
}
|
||||
|
||||
|
|
@ -48,13 +48,13 @@ impl Cache {
|
|||
|
||||
Some(Self {
|
||||
id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)),
|
||||
batch: Rc::from(meshes),
|
||||
batch: Arc::from(meshes),
|
||||
version: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, meshes: Vec<Mesh>) {
|
||||
self.batch = Rc::from(meshes);
|
||||
self.batch = Arc::from(meshes);
|
||||
self.version += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ struct Upload {
|
|||
layer: Layer,
|
||||
transformation: Transformation,
|
||||
version: usize,
|
||||
batch: rc::Weak<[Mesh]>,
|
||||
batch: sync::Weak<[Mesh]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -113,7 +113,7 @@ impl Storage {
|
|||
new_transformation,
|
||||
);
|
||||
|
||||
upload.batch = Rc::downgrade(&cache.batch);
|
||||
upload.batch = Arc::downgrade(&cache.batch);
|
||||
upload.version = cache.version;
|
||||
upload.transformation = new_transformation;
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ impl Storage {
|
|||
layer,
|
||||
transformation: new_transformation,
|
||||
version: 0,
|
||||
batch: Rc::downgrade(&cache.batch),
|
||||
batch: Arc::downgrade(&cache.batch),
|
||||
});
|
||||
|
||||
log::debug!(
|
||||
|
|
@ -505,6 +505,14 @@ impl Layer {
|
|||
.intersection(&(mesh.clip_bounds() * transformation))
|
||||
.and_then(Rectangle::snap)
|
||||
else {
|
||||
match mesh {
|
||||
Mesh::Solid { .. } => {
|
||||
num_solids += 1;
|
||||
}
|
||||
Mesh::Gradient { .. } => {
|
||||
num_gradients += 1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
@ -636,10 +644,10 @@ impl Uniforms {
|
|||
}
|
||||
|
||||
mod solid {
|
||||
use crate::graphics::mesh;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::triangle;
|
||||
use crate::Buffer;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::graphics::mesh;
|
||||
use crate::triangle;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
|
|
@ -745,7 +753,7 @@ mod solid {
|
|||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "solid_vs_main",
|
||||
entry_point: Some("solid_vs_main"),
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<
|
||||
mesh::SolidVertex2D,
|
||||
|
|
@ -760,16 +768,21 @@ mod solid {
|
|||
1 => Float32x4,
|
||||
),
|
||||
}],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "solid_fs_main",
|
||||
entry_point: Some("solid_fs_main"),
|
||||
targets: &[Some(triangle::fragment_target(format))],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: triangle::primitive_state(),
|
||||
depth_stencil: None,
|
||||
multisample: triangle::multisample_state(antialiasing),
|
||||
multiview: None,
|
||||
cache: None,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -782,11 +795,11 @@ mod solid {
|
|||
}
|
||||
|
||||
mod gradient {
|
||||
use crate::Buffer;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::mesh;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::triangle;
|
||||
use crate::Buffer;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
|
|
@ -913,7 +926,7 @@ mod gradient {
|
|||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "gradient_vs_main",
|
||||
entry_point: Some("gradient_vs_main"),
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<
|
||||
mesh::GradientVertex2D,
|
||||
|
|
@ -937,16 +950,21 @@ mod gradient {
|
|||
6 => Float32x4
|
||||
),
|
||||
}],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "gradient_fs_main",
|
||||
entry_point: Some("gradient_fs_main"),
|
||||
targets: &[Some(triangle::fragment_target(format))],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: triangle::primitive_state(),
|
||||
depth_stencil: None,
|
||||
multisample: triangle::multisample_state(antialiasing),
|
||||
multiview: None,
|
||||
cache: None,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -110,12 +110,14 @@ impl Blit {
|
|||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
entry_point: Some("vs_main"),
|
||||
buffers: &[],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
entry_point: Some("fs_main"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format,
|
||||
blend: Some(
|
||||
|
|
@ -123,6 +125,8 @@ impl Blit {
|
|||
),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options:
|
||||
wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
|
|
@ -136,6 +140,7 @@ impl Blit {
|
|||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
Blit {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ impl Compositor {
|
|||
) -> Result<Self, Error> {
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
backends: settings.backends,
|
||||
flags: if cfg!(feature = "strict-assertions") {
|
||||
wgpu::InstanceFlags::debugging()
|
||||
} else {
|
||||
wgpu::InstanceFlags::empty()
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
|
|
@ -162,6 +167,7 @@ impl Compositor {
|
|||
),
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: required_limits.clone(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
},
|
||||
None,
|
||||
)
|
||||
|
|
@ -361,7 +367,6 @@ impl graphics::Compositor for Compositor {
|
|||
fn screenshot(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
_surface: &mut Self::Surface,
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
) -> Vec<u8> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue