Merge branch 'master' into remove-vertex-indexing
This commit is contained in:
commit
1781068e1c
240 changed files with 12776 additions and 3395 deletions
|
|
@ -32,7 +32,6 @@ glyphon.workspace = true
|
|||
guillotiere.workspace = true
|
||||
log.workspace = true
|
||||
once_cell.workspace = true
|
||||
raw-window-handle.workspace = true
|
||||
wgpu.workspace = true
|
||||
|
||||
lyon.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::core::{Color, Size};
|
||||
use crate::graphics;
|
||||
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,6 +26,7 @@ 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,
|
||||
|
|
@ -51,6 +52,7 @@ impl Backend {
|
|||
quad_pipeline,
|
||||
text_pipeline,
|
||||
triangle_pipeline,
|
||||
pipeline_storage: pipeline::Storage::default(),
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
image_pipeline,
|
||||
|
|
@ -67,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,
|
||||
|
|
@ -89,6 +92,7 @@ impl Backend {
|
|||
self.prepare(
|
||||
device,
|
||||
queue,
|
||||
format,
|
||||
encoder,
|
||||
scale_factor,
|
||||
target_size,
|
||||
|
|
@ -118,6 +122,7 @@ impl Backend {
|
|||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
format: wgpu::TextureFormat,
|
||||
_encoder: &mut wgpu::CommandEncoder,
|
||||
scale_factor: f32,
|
||||
target_size: Size<u32>,
|
||||
|
|
@ -180,6 +185,20 @@ impl Backend {
|
|||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,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,
|
||||
|
|
@ -222,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,
|
||||
},
|
||||
));
|
||||
|
||||
|
|
@ -264,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,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
@ -299,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);
|
||||
|
|
@ -310,10 +372,6 @@ impl crate::graphics::Backend for Backend {
|
|||
}
|
||||
|
||||
impl backend::Text for Backend {
|
||||
fn font_system(&self) -> &graphics::text::FontSystem {
|
||||
self.text_pipeline.font_system()
|
||||
}
|
||||
|
||||
fn load_font(&mut self, font: Cow<'static, [u8]>) {
|
||||
self.text_pipeline.load_font(font);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Build and draw geometry.
|
||||
use crate::core::{Point, Rectangle, Size, Vector};
|
||||
use crate::core::text::LineHeight;
|
||||
use crate::core::{Pixels, Point, Rectangle, Size, Vector};
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::geometry::fill::{self, Fill};
|
||||
use crate::graphics::geometry::{
|
||||
|
|
@ -115,19 +116,31 @@ struct Transforms {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Transform {
|
||||
raw: lyon::math::Transform,
|
||||
is_identity: bool,
|
||||
}
|
||||
struct Transform(lyon::math::Transform);
|
||||
|
||||
impl Transform {
|
||||
/// Transforms the given [Point] by the transformation matrix.
|
||||
fn transform_point(&self, point: &mut Point) {
|
||||
fn is_identity(&self) -> bool {
|
||||
self.0 == lyon::math::Transform::identity()
|
||||
}
|
||||
|
||||
fn is_scale_translation(&self) -> bool {
|
||||
self.0.m12.abs() < 2.0 * f32::EPSILON
|
||||
&& self.0.m21.abs() < 2.0 * f32::EPSILON
|
||||
}
|
||||
|
||||
fn scale(&self) -> (f32, f32) {
|
||||
(self.0.m11, self.0.m22)
|
||||
}
|
||||
|
||||
fn transform_point(&self, point: Point) -> Point {
|
||||
let transformed = self
|
||||
.raw
|
||||
.0
|
||||
.transform_point(euclid::Point2D::new(point.x, point.y));
|
||||
point.x = transformed.x;
|
||||
point.y = transformed.y;
|
||||
|
||||
Point {
|
||||
x: transformed.x,
|
||||
y: transformed.y,
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_style(&self, style: Style) -> Style {
|
||||
|
|
@ -142,8 +155,8 @@ impl Transform {
|
|||
fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
|
||||
match &mut gradient {
|
||||
Gradient::Linear(linear) => {
|
||||
self.transform_point(&mut linear.start);
|
||||
self.transform_point(&mut linear.end);
|
||||
linear.start = self.transform_point(linear.start);
|
||||
linear.end = self.transform_point(linear.end);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,10 +176,7 @@ impl Frame {
|
|||
primitives: Vec::new(),
|
||||
transforms: Transforms {
|
||||
previous: Vec::new(),
|
||||
current: Transform {
|
||||
raw: lyon::math::Transform::identity(),
|
||||
is_identity: true,
|
||||
},
|
||||
current: Transform(lyon::math::Transform::identity()),
|
||||
},
|
||||
fill_tessellator: tessellation::FillTessellator::new(),
|
||||
stroke_tessellator: tessellation::StrokeTessellator::new(),
|
||||
|
|
@ -209,14 +219,14 @@ impl Frame {
|
|||
let options = tessellation::FillOptions::default()
|
||||
.with_fill_rule(into_fill_rule(rule));
|
||||
|
||||
if self.transforms.current.is_identity {
|
||||
if self.transforms.current.is_identity() {
|
||||
self.fill_tessellator.tessellate_path(
|
||||
path.raw(),
|
||||
&options,
|
||||
buffer.as_mut(),
|
||||
)
|
||||
} else {
|
||||
let path = path.transform(&self.transforms.current.raw);
|
||||
let path = path.transform(&self.transforms.current.0);
|
||||
|
||||
self.fill_tessellator.tessellate_path(
|
||||
path.raw(),
|
||||
|
|
@ -241,13 +251,14 @@ impl Frame {
|
|||
.buffers
|
||||
.get_fill(&self.transforms.current.transform_style(style));
|
||||
|
||||
let top_left =
|
||||
self.transforms.current.raw.transform_point(
|
||||
lyon::math::Point::new(top_left.x, top_left.y),
|
||||
);
|
||||
let top_left = self
|
||||
.transforms
|
||||
.current
|
||||
.0
|
||||
.transform_point(lyon::math::Point::new(top_left.x, top_left.y));
|
||||
|
||||
let size =
|
||||
self.transforms.current.raw.transform_vector(
|
||||
self.transforms.current.0.transform_vector(
|
||||
lyon::math::Vector::new(size.width, size.height),
|
||||
);
|
||||
|
||||
|
|
@ -284,14 +295,14 @@ impl Frame {
|
|||
Cow::Owned(dashed(path, stroke.line_dash))
|
||||
};
|
||||
|
||||
if self.transforms.current.is_identity {
|
||||
if self.transforms.current.is_identity() {
|
||||
self.stroke_tessellator.tessellate_path(
|
||||
path.raw(),
|
||||
&options,
|
||||
buffer.as_mut(),
|
||||
)
|
||||
} else {
|
||||
let path = path.transform(&self.transforms.current.raw);
|
||||
let path = path.transform(&self.transforms.current.0);
|
||||
|
||||
self.stroke_tessellator.tessellate_path(
|
||||
path.raw(),
|
||||
|
|
@ -318,33 +329,57 @@ impl Frame {
|
|||
pub fn fill_text(&mut self, text: impl Into<Text>) {
|
||||
let text = text.into();
|
||||
|
||||
let position = if self.transforms.current.is_identity {
|
||||
text.position
|
||||
} else {
|
||||
let transformed = self.transforms.current.raw.transform_point(
|
||||
lyon::math::Point::new(text.position.x, text.position.y),
|
||||
);
|
||||
let (scale_x, scale_y) = self.transforms.current.scale();
|
||||
|
||||
Point::new(transformed.x, transformed.y)
|
||||
};
|
||||
if self.transforms.current.is_scale_translation()
|
||||
&& scale_x == scale_y
|
||||
&& scale_x > 0.0
|
||||
&& scale_y > 0.0
|
||||
{
|
||||
let (position, size, line_height) =
|
||||
if self.transforms.current.is_identity() {
|
||||
(text.position, text.size, text.line_height)
|
||||
} else {
|
||||
let position =
|
||||
self.transforms.current.transform_point(text.position);
|
||||
|
||||
// TODO: Use vectorial text instead of primitive
|
||||
self.primitives.push(Primitive::Text {
|
||||
content: text.content,
|
||||
bounds: Rectangle {
|
||||
let size = Pixels(text.size.0 * scale_y);
|
||||
|
||||
let line_height = match text.line_height {
|
||||
LineHeight::Absolute(size) => {
|
||||
LineHeight::Absolute(Pixels(size.0 * scale_y))
|
||||
}
|
||||
LineHeight::Relative(factor) => {
|
||||
LineHeight::Relative(factor)
|
||||
}
|
||||
};
|
||||
|
||||
(position, size, line_height)
|
||||
};
|
||||
|
||||
let bounds = Rectangle {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
width: f32::INFINITY,
|
||||
height: f32::INFINITY,
|
||||
},
|
||||
color: text.color,
|
||||
size: text.size,
|
||||
line_height: text.line_height,
|
||||
font: text.font,
|
||||
horizontal_alignment: text.horizontal_alignment,
|
||||
vertical_alignment: text.vertical_alignment,
|
||||
shaping: text.shaping,
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: Honor layering!
|
||||
self.primitives.push(Primitive::Text {
|
||||
content: text.content,
|
||||
bounds,
|
||||
color: text.color,
|
||||
size,
|
||||
line_height,
|
||||
font: text.font,
|
||||
horizontal_alignment: text.horizontal_alignment,
|
||||
vertical_alignment: text.vertical_alignment,
|
||||
shaping: text.shaping,
|
||||
clip_bounds: Rectangle::with_size(Size::INFINITY),
|
||||
});
|
||||
} else {
|
||||
text.draw_with(|path, color| self.fill(&path, color));
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the current transform of the [`Frame`] and executes the given
|
||||
|
|
@ -420,26 +455,24 @@ impl Frame {
|
|||
/// Applies a translation to the current transform of the [`Frame`].
|
||||
#[inline]
|
||||
pub fn translate(&mut self, translation: Vector) {
|
||||
self.transforms.current.raw = self
|
||||
.transforms
|
||||
.current
|
||||
.raw
|
||||
.pre_translate(lyon::math::Vector::new(
|
||||
translation.x,
|
||||
translation.y,
|
||||
));
|
||||
self.transforms.current.is_identity = false;
|
||||
self.transforms.current.0 =
|
||||
self.transforms
|
||||
.current
|
||||
.0
|
||||
.pre_translate(lyon::math::Vector::new(
|
||||
translation.x,
|
||||
translation.y,
|
||||
));
|
||||
}
|
||||
|
||||
/// Applies a rotation in radians to the current transform of the [`Frame`].
|
||||
#[inline]
|
||||
pub fn rotate(&mut self, angle: f32) {
|
||||
self.transforms.current.raw = self
|
||||
self.transforms.current.0 = self
|
||||
.transforms
|
||||
.current
|
||||
.raw
|
||||
.0
|
||||
.pre_rotate(lyon::math::Angle::radians(angle));
|
||||
self.transforms.current.is_identity = false;
|
||||
}
|
||||
|
||||
/// Applies a uniform scaling to the current transform of the [`Frame`].
|
||||
|
|
@ -455,9 +488,8 @@ impl Frame {
|
|||
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.x, scale.y);
|
||||
self.transforms.current.is_identity = false;
|
||||
self.transforms.current.0 =
|
||||
self.transforms.current.0.pre_scale(scale.x, scale.y);
|
||||
}
|
||||
|
||||
/// Produces the [`Primitive`] representing everything drawn on the [`Frame`].
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ pub struct Pipeline {
|
|||
vector_cache: RefCell<vector::Cache>,
|
||||
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
sampler: wgpu::Sampler,
|
||||
nearest_sampler: wgpu::Sampler,
|
||||
linear_sampler: wgpu::Sampler,
|
||||
texture: wgpu::BindGroup,
|
||||
texture_version: usize,
|
||||
texture_atlas: Atlas,
|
||||
|
|
@ -49,16 +50,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"),
|
||||
|
|
@ -67,6 +68,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,
|
||||
|
|
@ -75,7 +129,7 @@ impl Layer {
|
|||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer(
|
||||
wgpu::BufferBinding {
|
||||
buffer: &uniforms,
|
||||
buffer: uniforms,
|
||||
offset: 0,
|
||||
size: None,
|
||||
},
|
||||
|
|
@ -96,28 +150,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);
|
||||
|
||||
|
|
@ -134,12 +178,22 @@ impl Layer {
|
|||
|
||||
impl Pipeline {
|
||||
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
|
||||
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()
|
||||
});
|
||||
|
|
@ -286,7 +340,8 @@ impl Pipeline {
|
|||
vector_cache: RefCell::new(vector::Cache::default()),
|
||||
|
||||
pipeline,
|
||||
sampler,
|
||||
nearest_sampler,
|
||||
linear_sampler,
|
||||
texture,
|
||||
texture_version: texture_atlas.layer_count(),
|
||||
texture_atlas,
|
||||
|
|
@ -329,7 +384,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();
|
||||
|
|
@ -340,7 +396,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,
|
||||
|
|
@ -351,7 +411,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,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -379,7 +444,7 @@ impl Pipeline {
|
|||
[bounds.x, bounds.y],
|
||||
size,
|
||||
atlas_entry,
|
||||
instances,
|
||||
nearest_instances,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -388,7 +453,7 @@ impl Pipeline {
|
|||
}
|
||||
}
|
||||
|
||||
if instances.is_empty() {
|
||||
if nearest_instances.is_empty() && linear_instances.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -416,12 +481,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;
|
||||
}
|
||||
|
|
@ -470,7 +543,7 @@ struct Instance {
|
|||
}
|
||||
|
||||
impl Instance {
|
||||
pub const INITIAL: usize = 1_000;
|
||||
pub const INITIAL: usize = 20;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
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;
|
||||
use resvg::usvg::{self, TreeTextToPath};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
|
||||
|
|
@ -49,15 +50,15 @@ impl Cache {
|
|||
return self.svgs.get(&handle.id()).unwrap();
|
||||
}
|
||||
|
||||
let svg = match handle.data() {
|
||||
svg::Data::Path(path) => {
|
||||
let tree = fs::read_to_string(path).ok().and_then(|contents| {
|
||||
let mut 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()
|
||||
});
|
||||
|
||||
tree.map(Svg::Loaded).unwrap_or(Svg::NotFound)
|
||||
}
|
||||
})
|
||||
.map(Svg::Loaded)
|
||||
.unwrap_or(Svg::NotFound),
|
||||
svg::Data::Bytes(bytes) => {
|
||||
match usvg::Tree::from_data(bytes, &usvg::Options::default()) {
|
||||
Ok(tree) => Svg::Loaded(tree),
|
||||
|
|
@ -66,6 +67,15 @@ impl Cache {
|
|||
}
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
let _ = self.svgs.insert(handle.id(), svg);
|
||||
self.svgs.get(&handle.id()).unwrap()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
//! 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;
|
||||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +75,7 @@ impl<'a> Layer<'a> {
|
|||
horizontal_alignment: alignment::Horizontal::Left,
|
||||
vertical_alignment: alignment::Vertical::Top,
|
||||
shaping: core::text::Shaping::Basic,
|
||||
clip_bounds: Rectangle::with_size(Size::INFINITY),
|
||||
};
|
||||
|
||||
overlay.text.push(Text::Cached(text.clone()));
|
||||
|
|
@ -117,13 +124,30 @@ impl<'a> Layer<'a> {
|
|||
paragraph,
|
||||
position,
|
||||
color,
|
||||
clip_bounds,
|
||||
} => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
layer.text.push(Text::Managed {
|
||||
layer.text.push(Text::Paragraph {
|
||||
paragraph: paragraph.clone(),
|
||||
position: *position + translation,
|
||||
color: *color,
|
||||
clip_bounds: *clip_bounds + translation,
|
||||
});
|
||||
}
|
||||
Primitive::Editor {
|
||||
editor,
|
||||
position,
|
||||
color,
|
||||
clip_bounds,
|
||||
} => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
layer.text.push(Text::Editor {
|
||||
editor: editor.clone(),
|
||||
position: *position + translation,
|
||||
color: *color,
|
||||
clip_bounds: *clip_bounds + translation,
|
||||
});
|
||||
}
|
||||
Primitive::Text {
|
||||
|
|
@ -136,6 +160,7 @@ impl<'a> Layer<'a> {
|
|||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
shaping,
|
||||
clip_bounds,
|
||||
} => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
|
|
@ -149,6 +174,22 @@ impl<'a> Layer<'a> {
|
|||
horizontal_alignment: *horizontal_alignment,
|
||||
vertical_alignment: *vertical_alignment,
|
||||
shaping: *shaping,
|
||||
clip_bounds: *clip_bounds + translation,
|
||||
}));
|
||||
}
|
||||
graphics::Primitive::RawText(graphics::text::Raw {
|
||||
buffer,
|
||||
position,
|
||||
color,
|
||||
clip_bounds,
|
||||
}) => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
layer.text.push(Text::Raw(graphics::text::Raw {
|
||||
buffer: buffer.clone(),
|
||||
position: *position + translation,
|
||||
color: *color,
|
||||
clip_bounds: *clip_bounds + translation,
|
||||
}));
|
||||
}
|
||||
Primitive::Quad {
|
||||
|
|
@ -173,11 +214,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,
|
||||
});
|
||||
}
|
||||
|
|
@ -290,6 +336,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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
17
wgpu/src/layer/pipeline.rs
Normal file
17
wgpu/src/layer/pipeline.rs
Normal file
|
|
@ -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>,
|
||||
}
|
||||
|
|
@ -1,17 +1,33 @@
|
|||
use crate::core::alignment;
|
||||
use crate::core::text;
|
||||
use crate::core::{Color, Font, Pixels, Point, Rectangle};
|
||||
use crate::graphics;
|
||||
use crate::graphics::text::editor;
|
||||
use crate::graphics::text::paragraph;
|
||||
|
||||
/// A paragraph of text.
|
||||
/// A text primitive.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Text<'a> {
|
||||
Managed {
|
||||
/// A paragraph.
|
||||
#[allow(missing_docs)]
|
||||
Paragraph {
|
||||
paragraph: paragraph::Weak,
|
||||
position: Point,
|
||||
color: Color,
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// An editor.
|
||||
#[allow(missing_docs)]
|
||||
Editor {
|
||||
editor: editor::Weak,
|
||||
position: Point,
|
||||
color: Color,
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// Some cached text.
|
||||
Cached(Cached<'a>),
|
||||
/// Some raw text.
|
||||
Raw(graphics::text::Raw),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -42,4 +58,7 @@ pub struct Cached<'a> {
|
|||
|
||||
/// The shaping strategy of the text.
|
||||
pub shaping: text::Shaping,
|
||||
|
||||
/// The clip bounds of the text.
|
||||
pub clip_bounds: Rectangle,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#![forbid(rust_2018_idioms)]
|
||||
#![deny(
|
||||
missing_debug_implementations,
|
||||
//missing_docs,
|
||||
missing_docs,
|
||||
unsafe_code,
|
||||
unused_results,
|
||||
rustdoc::broken_intra_doc_links
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
116
wgpu/src/primitive/pipeline.rs
Normal file
116
wgpu/src/primitive/pipeline.rs
Normal file
|
|
@ -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 + Send>>,
|
||||
}
|
||||
|
||||
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 + Send>(&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.")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,15 @@ use crate::core::alignment;
|
|||
use crate::core::{Rectangle, Size};
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::text::cache::{self, Cache};
|
||||
use crate::graphics::text::{FontSystem, Paragraph};
|
||||
use crate::graphics::text::{font_system, to_color, Editor, Paragraph};
|
||||
use crate::layer::Text;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Pipeline {
|
||||
font_system: FontSystem,
|
||||
renderers: Vec<glyphon::TextRenderer>,
|
||||
atlas: glyphon::TextAtlas,
|
||||
prepare_layer: usize,
|
||||
|
|
@ -24,7 +24,6 @@ impl Pipeline {
|
|||
format: wgpu::TextureFormat,
|
||||
) -> Self {
|
||||
Pipeline {
|
||||
font_system: FontSystem::new(),
|
||||
renderers: Vec::new(),
|
||||
atlas: glyphon::TextAtlas::with_color_mode(
|
||||
device,
|
||||
|
|
@ -41,12 +40,11 @@ impl Pipeline {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn font_system(&self) -> &FontSystem {
|
||||
&self.font_system
|
||||
}
|
||||
|
||||
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
|
||||
self.font_system.load_font(bytes);
|
||||
font_system()
|
||||
.write()
|
||||
.expect("Write font system")
|
||||
.load_font(bytes);
|
||||
|
||||
self.cache = RefCell::new(Cache::new());
|
||||
}
|
||||
|
|
@ -69,21 +67,28 @@ impl Pipeline {
|
|||
));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
enum Allocation {
|
||||
Paragraph(Paragraph),
|
||||
Editor(Editor),
|
||||
Cache(cache::KeyHash),
|
||||
Raw(Arc<glyphon::Buffer>),
|
||||
}
|
||||
|
||||
let allocations: Vec<_> = sections
|
||||
.iter()
|
||||
.map(|section| match section {
|
||||
Text::Managed { paragraph, .. } => {
|
||||
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,
|
||||
|
|
@ -104,6 +109,7 @@ impl Pipeline {
|
|||
|
||||
Some(Allocation::Cache(key))
|
||||
}
|
||||
Text::Raw(text) => text.buffer.upgrade().map(Allocation::Raw),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -117,9 +123,13 @@ impl Pipeline {
|
|||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
color,
|
||||
clip_bounds,
|
||||
) = match section {
|
||||
Text::Managed {
|
||||
position, color, ..
|
||||
Text::Paragraph {
|
||||
position,
|
||||
color,
|
||||
clip_bounds,
|
||||
..
|
||||
} => {
|
||||
use crate::core::text::Paragraph as _;
|
||||
|
||||
|
|
@ -134,6 +144,29 @@ impl Pipeline {
|
|||
paragraph.horizontal_alignment(),
|
||||
paragraph.vertical_alignment(),
|
||||
*color,
|
||||
*clip_bounds,
|
||||
)
|
||||
}
|
||||
Text::Editor {
|
||||
position,
|
||||
color,
|
||||
clip_bounds,
|
||||
..
|
||||
} => {
|
||||
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,
|
||||
*clip_bounds,
|
||||
)
|
||||
}
|
||||
Text::Cached(text) => {
|
||||
|
|
@ -152,6 +185,26 @@ impl Pipeline {
|
|||
text.horizontal_alignment,
|
||||
text.vertical_alignment,
|
||||
text.color,
|
||||
text.clip_bounds,
|
||||
)
|
||||
}
|
||||
Text::Raw(text) => {
|
||||
let Some(Allocation::Raw(buffer)) = allocation else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (width, height) = buffer.size();
|
||||
|
||||
(
|
||||
buffer.as_ref(),
|
||||
Rectangle::new(
|
||||
text.position,
|
||||
Size::new(width, height),
|
||||
),
|
||||
alignment::Horizontal::Left,
|
||||
alignment::Vertical::Top,
|
||||
text.color,
|
||||
text.clip_bounds,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
|
@ -174,13 +227,8 @@ impl Pipeline {
|
|||
alignment::Vertical::Bottom => bounds.y - bounds.height,
|
||||
};
|
||||
|
||||
let section_bounds = Rectangle {
|
||||
x: left,
|
||||
y: top,
|
||||
..bounds
|
||||
};
|
||||
|
||||
let clip_bounds = layer_bounds.intersection(§ion_bounds)?;
|
||||
let clip_bounds =
|
||||
layer_bounds.intersection(&(clip_bounds * scale_factor))?;
|
||||
|
||||
Some(glyphon::TextArea {
|
||||
buffer,
|
||||
|
|
@ -193,16 +241,7 @@ impl Pipeline {
|
|||
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(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,
|
||||
)
|
||||
},
|
||||
default_color: to_color(color),
|
||||
})
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ use crate::graphics::compositor;
|
|||
use crate::graphics::{Error, Viewport};
|
||||
use crate::{Backend, Primitive, Renderer, Settings};
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A window graphics backend for iced powered by `wgpu`.
|
||||
|
|
@ -26,9 +24,9 @@ impl<Theme> Compositor<Theme> {
|
|||
/// Requests a new [`Compositor`] with the given [`Settings`].
|
||||
///
|
||||
/// Returns `None` if no compatible graphics adapter could be found.
|
||||
pub async fn request<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
pub async fn request<W: compositor::Window>(
|
||||
settings: Settings,
|
||||
compatible_window: Option<&W>,
|
||||
compatible_window: Option<W>,
|
||||
) -> Option<Self> {
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
backends: settings.internal_backend,
|
||||
|
|
@ -41,14 +39,15 @@ impl<Theme> Compositor<Theme> {
|
|||
if log::max_level() >= log::LevelFilter::Info {
|
||||
let available_adapters: Vec<_> = instance
|
||||
.enumerate_adapters(settings.internal_backend)
|
||||
.map(|adapter| adapter.get_info())
|
||||
.iter()
|
||||
.map(wgpu::Adapter::get_info)
|
||||
.collect();
|
||||
log::info!("Available adapters: {available_adapters:#?}");
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
let compatible_surface = compatible_window
|
||||
.and_then(|window| unsafe { instance.create_surface(window).ok() });
|
||||
.and_then(|window| instance.create_surface(window).ok());
|
||||
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
|
|
@ -100,14 +99,14 @@ impl<Theme> Compositor<Theme> {
|
|||
|
||||
let (device, queue) =
|
||||
loop {
|
||||
let limits = limits.next()?;
|
||||
let required_limits = limits.next()?;
|
||||
let device = adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: Some(
|
||||
"iced_wgpu::window::compositor device descriptor",
|
||||
),
|
||||
features: wgpu::Features::empty(),
|
||||
limits,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits,
|
||||
},
|
||||
None,
|
||||
).await.ok();
|
||||
|
|
@ -136,26 +135,24 @@ impl<Theme> Compositor<Theme> {
|
|||
|
||||
/// Creates a [`Compositor`] and its [`Backend`] for the given [`Settings`] and
|
||||
/// window.
|
||||
pub fn new<Theme, W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
pub fn new<W: compositor::Window, Theme>(
|
||||
settings: Settings,
|
||||
compatible_window: Option<&W>,
|
||||
) -> Result<(Compositor<Theme>, Backend), Error> {
|
||||
compatible_window: W,
|
||||
) -> Result<Compositor<Theme>, Error> {
|
||||
let compositor = futures::executor::block_on(Compositor::request(
|
||||
settings,
|
||||
compatible_window,
|
||||
Some(compatible_window),
|
||||
))
|
||||
.ok_or(Error::GraphicsAdapterNotFound)?;
|
||||
|
||||
let backend = compositor.create_backend();
|
||||
|
||||
Ok((compositor, backend))
|
||||
Ok(compositor)
|
||||
}
|
||||
|
||||
/// Presents the given primitives with the given [`Compositor`] and [`Backend`].
|
||||
pub fn present<Theme, T: AsRef<str>>(
|
||||
compositor: &mut Compositor<Theme>,
|
||||
backend: &mut Backend,
|
||||
surface: &mut wgpu::Surface,
|
||||
surface: &mut wgpu::Surface<'static>,
|
||||
primitives: &[Primitive],
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
|
|
@ -178,6 +175,7 @@ pub fn present<Theme, T: AsRef<str>>(
|
|||
&compositor.queue,
|
||||
&mut encoder,
|
||||
Some(background_color),
|
||||
frame.texture.format(),
|
||||
view,
|
||||
primitives,
|
||||
viewport,
|
||||
|
|
@ -208,32 +206,32 @@ pub fn present<Theme, T: AsRef<str>>(
|
|||
impl<Theme> graphics::Compositor for Compositor<Theme> {
|
||||
type Settings = Settings;
|
||||
type Renderer = Renderer<Theme>;
|
||||
type Surface = wgpu::Surface;
|
||||
type Surface = wgpu::Surface<'static>;
|
||||
|
||||
fn new<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
fn new<W: compositor::Window>(
|
||||
settings: Self::Settings,
|
||||
compatible_window: Option<&W>,
|
||||
) -> Result<(Self, Self::Renderer), Error> {
|
||||
let (compositor, backend) = new(settings, compatible_window)?;
|
||||
|
||||
Ok((
|
||||
compositor,
|
||||
Renderer::new(
|
||||
backend,
|
||||
settings.default_font,
|
||||
settings.default_text_size,
|
||||
),
|
||||
))
|
||||
compatible_window: W,
|
||||
) -> Result<Self, Error> {
|
||||
new(settings, compatible_window)
|
||||
}
|
||||
|
||||
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
fn create_renderer(&self) -> Self::Renderer {
|
||||
Renderer::new(
|
||||
self.create_backend(),
|
||||
self.settings.default_font,
|
||||
self.settings.default_text_size,
|
||||
)
|
||||
}
|
||||
|
||||
fn create_surface<W: compositor::Window>(
|
||||
&mut self,
|
||||
window: &W,
|
||||
window: W,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> wgpu::Surface {
|
||||
#[allow(unsafe_code)]
|
||||
let mut surface = unsafe { self.instance.create_surface(window) }
|
||||
) -> Self::Surface {
|
||||
let mut surface = self
|
||||
.instance
|
||||
.create_surface(window)
|
||||
.expect("Create surface");
|
||||
|
||||
self.configure_surface(&mut surface, width, height);
|
||||
|
|
@ -257,6 +255,7 @@ impl<Theme> graphics::Compositor for Compositor<Theme> {
|
|||
height,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
view_formats: vec![],
|
||||
desired_maximum_frame_latency: 2,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -357,6 +356,7 @@ pub fn screenshot<Theme, T: AsRef<str>>(
|
|||
&compositor.queue,
|
||||
&mut encoder,
|
||||
Some(background_color),
|
||||
texture.format(),
|
||||
&view,
|
||||
primitives,
|
||||
viewport,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue