summaryrefslogtreecommitdiffstats
path: root/wgpu
diff options
context:
space:
mode:
Diffstat (limited to 'wgpu')
-rw-r--r--wgpu/Cargo.toml2
-rw-r--r--wgpu/src/backend.rs11
-rw-r--r--wgpu/src/color.rs165
-rw-r--r--wgpu/src/geometry.rs66
-rw-r--r--wgpu/src/layer.rs93
-rw-r--r--wgpu/src/layer/mesh.rs6
-rw-r--r--wgpu/src/lib.rs3
-rw-r--r--wgpu/src/primitive.rs21
-rw-r--r--wgpu/src/quad/gradient.rs40
-rw-r--r--wgpu/src/shader/quad.wgsl107
-rw-r--r--wgpu/src/shader/triangle.wgsl105
-rw-r--r--wgpu/src/text.rs173
-rw-r--r--wgpu/src/triangle.rs107
-rw-r--r--wgpu/src/triangle/msaa.rs11
-rw-r--r--wgpu/src/window/compositor.rs156
15 files changed, 725 insertions, 341 deletions
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index f3a83acb..15db5b5d 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -45,7 +45,7 @@ path = "../graphics"
[dependencies.glyphon]
version = "0.2"
git = "https://github.com/hecrj/glyphon.git"
-rev = "8dbf36020e5759fa9144517b321372266160113e"
+rev = "8324f20158a62f8520bad4ed09f6aa5552f8f2a6"
[dependencies.glam]
version = "0.24"
diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index b524c615..4a0c54f0 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -2,7 +2,8 @@ use crate::core;
use crate::core::{Color, Font, Point, Size};
use crate::graphics::backend;
use crate::graphics::color;
-use crate::graphics::{Primitive, Transformation, Viewport};
+use crate::graphics::{Transformation, Viewport};
+use crate::primitive::{self, Primitive};
use crate::quad;
use crate::text;
use crate::triangle;
@@ -334,9 +335,11 @@ impl Backend {
}
}
-impl iced_graphics::Backend for Backend {
+impl crate::graphics::Backend for Backend {
+ type Primitive = primitive::Custom;
+
fn trim_measurements(&mut self) {
- self.text_pipeline.trim_measurement_cache()
+ self.text_pipeline.trim_measurements();
}
}
@@ -361,7 +364,7 @@ impl backend::Text for Backend {
font: Font,
bounds: Size,
shaping: core::text::Shaping,
- ) -> (f32, f32) {
+ ) -> Size {
self.text_pipeline.measure(
contents,
size,
diff --git a/wgpu/src/color.rs b/wgpu/src/color.rs
new file mode 100644
index 00000000..a1025601
--- /dev/null
+++ b/wgpu/src/color.rs
@@ -0,0 +1,165 @@
+use std::borrow::Cow;
+
+pub fn convert(
+ device: &wgpu::Device,
+ encoder: &mut wgpu::CommandEncoder,
+ source: wgpu::Texture,
+ format: wgpu::TextureFormat,
+) -> wgpu::Texture {
+ if source.format() == format {
+ return source;
+ }
+
+ let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+ label: Some("iced_wgpu.offscreen.sampler"),
+ ..Default::default()
+ });
+
+ //sampler in 0
+ let sampler_layout =
+ device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
+ label: Some("iced_wgpu.offscreen.blit.sampler_layout"),
+ entries: &[wgpu::BindGroupLayoutEntry {
+ binding: 0,
+ visibility: wgpu::ShaderStages::FRAGMENT,
+ ty: wgpu::BindingType::Sampler(
+ wgpu::SamplerBindingType::NonFiltering,
+ ),
+ count: None,
+ }],
+ });
+
+ let sampler_bind_group =
+ device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu.offscreen.sampler.bind_group"),
+ layout: &sampler_layout,
+ entries: &[wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::Sampler(&sampler),
+ }],
+ });
+
+ let texture_layout =
+ device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
+ label: Some("iced_wgpu.offscreen.blit.texture_layout"),
+ entries: &[wgpu::BindGroupLayoutEntry {
+ binding: 0,
+ visibility: wgpu::ShaderStages::FRAGMENT,
+ ty: wgpu::BindingType::Texture {
+ sample_type: wgpu::TextureSampleType::Float {
+ filterable: false,
+ },
+ view_dimension: wgpu::TextureViewDimension::D2,
+ multisampled: false,
+ },
+ count: None,
+ }],
+ });
+
+ let pipeline_layout =
+ device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+ label: Some("iced_wgpu.offscreen.blit.pipeline_layout"),
+ bind_group_layouts: &[&sampler_layout, &texture_layout],
+ push_constant_ranges: &[],
+ });
+
+ let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
+ label: Some("iced_wgpu.offscreen.blit.shader"),
+ source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(
+ "shader/blit.wgsl"
+ ))),
+ });
+
+ let pipeline =
+ device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
+ label: Some("iced_wgpu.offscreen.blit.pipeline"),
+ layout: Some(&pipeline_layout),
+ vertex: wgpu::VertexState {
+ module: &shader,
+ entry_point: "vs_main",
+ buffers: &[],
+ },
+ fragment: Some(wgpu::FragmentState {
+ module: &shader,
+ entry_point: "fs_main",
+ targets: &[Some(wgpu::ColorTargetState {
+ format,
+ blend: Some(wgpu::BlendState {
+ color: wgpu::BlendComponent {
+ src_factor: wgpu::BlendFactor::SrcAlpha,
+ dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
+ operation: wgpu::BlendOperation::Add,
+ },
+ alpha: wgpu::BlendComponent {
+ src_factor: wgpu::BlendFactor::One,
+ dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
+ operation: wgpu::BlendOperation::Add,
+ },
+ }),
+ write_mask: wgpu::ColorWrites::ALL,
+ })],
+ }),
+ primitive: wgpu::PrimitiveState {
+ topology: wgpu::PrimitiveTopology::TriangleList,
+ front_face: wgpu::FrontFace::Cw,
+ ..Default::default()
+ },
+ depth_stencil: None,
+ multisample: Default::default(),
+ multiview: None,
+ });
+
+ let texture = device.create_texture(&wgpu::TextureDescriptor {
+ label: Some("iced_wgpu.offscreen.conversion.source_texture"),
+ size: source.size(),
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: wgpu::TextureDimension::D2,
+ format,
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT
+ | wgpu::TextureUsages::COPY_SRC,
+ view_formats: &[],
+ });
+
+ let view = &texture.create_view(&wgpu::TextureViewDescriptor::default());
+
+ let texture_bind_group =
+ device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu.offscreen.blit.texture_bind_group"),
+ layout: &texture_layout,
+ entries: &[wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::TextureView(
+ &source
+ .create_view(&wgpu::TextureViewDescriptor::default()),
+ ),
+ }],
+ });
+
+ let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+ label: Some("iced_wgpu.offscreen.blit.render_pass"),
+ color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+ view,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Load,
+ store: true,
+ },
+ })],
+ depth_stencil_attachment: None,
+ });
+
+ pass.set_pipeline(&pipeline);
+ pass.set_bind_group(0, &sampler_bind_group, &[]);
+ pass.set_bind_group(1, &texture_bind_group, &[]);
+ pass.draw(0..6, 0..1);
+
+ texture
+}
+
+#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
+#[repr(C)]
+struct Vertex {
+ ndc: [f32; 2],
+ uv: [f32; 2],
+}
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index f81b5b2f..e421e0b0 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -5,10 +5,10 @@ use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::{
LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,
};
-use crate::graphics::primitive::{self, Primitive};
-use crate::graphics::Gradient;
+use crate::graphics::gradient::{self, Gradient};
+use crate::graphics::mesh::{self, Mesh};
+use crate::primitive::{self, Primitive};
-use iced_graphics::gradient;
use lyon::geom::euclid;
use lyon::tessellation;
use std::borrow::Cow;
@@ -25,8 +25,8 @@ pub struct Frame {
}
enum Buffer {
- Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>),
- Gradient(tessellation::VertexBuffers<primitive::GradientVertex2D, u32>),
+ Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>),
+ Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>),
}
struct BufferStack {
@@ -464,24 +464,28 @@ impl Frame {
match buffer {
Buffer::Solid(buffer) => {
if !buffer.indices.is_empty() {
- self.primitives.push(Primitive::SolidMesh {
- buffers: primitive::Mesh2D {
- vertices: buffer.vertices,
- indices: buffer.indices,
- },
- size: self.size,
- })
+ self.primitives.push(Primitive::Custom(
+ primitive::Custom::Mesh(Mesh::Solid {
+ buffers: mesh::Indexed {
+ vertices: buffer.vertices,
+ indices: buffer.indices,
+ },
+ size: self.size,
+ }),
+ ))
}
}
Buffer::Gradient(buffer) => {
if !buffer.indices.is_empty() {
- self.primitives.push(Primitive::GradientMesh {
- buffers: primitive::Mesh2D {
- vertices: buffer.vertices,
- indices: buffer.indices,
- },
- size: self.size,
- })
+ self.primitives.push(Primitive::Custom(
+ primitive::Custom::Mesh(Mesh::Gradient {
+ buffers: mesh::Indexed {
+ vertices: buffer.vertices,
+ indices: buffer.indices,
+ },
+ size: self.size,
+ }),
+ ))
}
}
}
@@ -495,32 +499,32 @@ struct GradientVertex2DBuilder {
gradient: gradient::Packed,
}
-impl tessellation::FillVertexConstructor<primitive::GradientVertex2D>
+impl tessellation::FillVertexConstructor<mesh::GradientVertex2D>
for GradientVertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::FillVertex<'_>,
- ) -> primitive::GradientVertex2D {
+ ) -> mesh::GradientVertex2D {
let position = vertex.position();
- primitive::GradientVertex2D {
+ mesh::GradientVertex2D {
position: [position.x, position.y],
gradient: self.gradient,
}
}
}
-impl tessellation::StrokeVertexConstructor<primitive::GradientVertex2D>
+impl tessellation::StrokeVertexConstructor<mesh::GradientVertex2D>
for GradientVertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::StrokeVertex<'_, '_>,
- ) -> primitive::GradientVertex2D {
+ ) -> mesh::GradientVertex2D {
let position = vertex.position();
- primitive::GradientVertex2D {
+ mesh::GradientVertex2D {
position: [position.x, position.y],
gradient: self.gradient,
}
@@ -529,32 +533,32 @@ impl tessellation::StrokeVertexConstructor<primitive::GradientVertex2D>
struct TriangleVertex2DBuilder(color::Packed);
-impl tessellation::FillVertexConstructor<primitive::ColoredVertex2D>
+impl tessellation::FillVertexConstructor<mesh::SolidVertex2D>
for TriangleVertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::FillVertex<'_>,
- ) -> primitive::ColoredVertex2D {
+ ) -> mesh::SolidVertex2D {
let position = vertex.position();
- primitive::ColoredVertex2D {
+ mesh::SolidVertex2D {
position: [position.x, position.y],
color: self.0,
}
}
}
-impl tessellation::StrokeVertexConstructor<primitive::ColoredVertex2D>
+impl tessellation::StrokeVertexConstructor<mesh::SolidVertex2D>
for TriangleVertex2DBuilder
{
fn new_vertex(
&mut self,
vertex: tessellation::StrokeVertex<'_, '_>,
- ) -> primitive::ColoredVertex2D {
+ ) -> mesh::SolidVertex2D {
let position = vertex.position();
- primitive::ColoredVertex2D {
+ mesh::SolidVertex2D {
position: [position.x, position.y],
color: self.0,
}
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index 71570e3d..b8f32db1 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -11,8 +11,10 @@ pub use text::Text;
use crate::core;
use crate::core::alignment;
use crate::core::{Color, Font, Point, Rectangle, Size, Vector};
+use crate::graphics;
use crate::graphics::color;
-use crate::graphics::{Primitive, Viewport};
+use crate::graphics::Viewport;
+use crate::primitive::{self, Primitive};
use crate::quad::{self, Quad};
/// A group of primitives that should be clipped together.
@@ -179,40 +181,6 @@ impl<'a> Layer<'a> {
bounds: *bounds + translation,
});
}
- Primitive::SolidMesh { buffers, size } => {
- let layer = &mut layers[current_layer];
-
- let bounds = Rectangle::new(
- Point::new(translation.x, translation.y),
- *size,
- );
-
- // Only draw visible content
- if let Some(clip_bounds) = layer.bounds.intersection(&bounds) {
- layer.meshes.push(Mesh::Solid {
- origin: Point::new(translation.x, translation.y),
- buffers,
- clip_bounds,
- });
- }
- }
- Primitive::GradientMesh { buffers, size } => {
- let layer = &mut layers[current_layer];
-
- let bounds = Rectangle::new(
- Point::new(translation.x, translation.y),
- *size,
- );
-
- // Only draw visible content
- if let Some(clip_bounds) = layer.bounds.intersection(&bounds) {
- layer.meshes.push(Mesh::Gradient {
- origin: Point::new(translation.x, translation.y),
- buffers,
- clip_bounds,
- });
- }
- }
Primitive::Group { primitives } => {
// TODO: Inspect a bit and regroup (?)
for primitive in primitives {
@@ -262,13 +230,54 @@ impl<'a> Layer<'a> {
current_layer,
);
}
- _ => {
- // Not supported!
- log::warn!(
- "Unsupported primitive in `iced_wgpu`: {:?}",
- primitive
- );
- }
+ Primitive::Custom(custom) => match custom {
+ primitive::Custom::Mesh(mesh) => match mesh {
+ graphics::Mesh::Solid { buffers, size } => {
+ let layer = &mut layers[current_layer];
+
+ let bounds = Rectangle::new(
+ Point::new(translation.x, translation.y),
+ *size,
+ );
+
+ // Only draw visible content
+ if let Some(clip_bounds) =
+ layer.bounds.intersection(&bounds)
+ {
+ layer.meshes.push(Mesh::Solid {
+ origin: Point::new(
+ translation.x,
+ translation.y,
+ ),
+ buffers,
+ clip_bounds,
+ });
+ }
+ }
+ graphics::Mesh::Gradient { buffers, size } => {
+ let layer = &mut layers[current_layer];
+
+ let bounds = Rectangle::new(
+ Point::new(translation.x, translation.y),
+ *size,
+ );
+
+ // Only draw visible content
+ if let Some(clip_bounds) =
+ layer.bounds.intersection(&bounds)
+ {
+ layer.meshes.push(Mesh::Gradient {
+ origin: Point::new(
+ translation.x,
+ translation.y,
+ ),
+ buffers,
+ clip_bounds,
+ });
+ }
+ }
+ },
+ },
}
}
}
diff --git a/wgpu/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs
index b7dd9a0b..7c6206cd 100644
--- a/wgpu/src/layer/mesh.rs
+++ b/wgpu/src/layer/mesh.rs
@@ -1,6 +1,6 @@
//! A collection of triangle primitives.
use crate::core::{Point, Rectangle};
-use crate::graphics::primitive;
+use crate::graphics::mesh;
/// A mesh of triangles.
#[derive(Debug, Clone, Copy)]
@@ -11,7 +11,7 @@ pub enum Mesh<'a> {
origin: Point,
/// The vertex and index buffers of the [`Mesh`].
- buffers: &'a primitive::Mesh2D<primitive::ColoredVertex2D>,
+ buffers: &'a mesh::Indexed<mesh::SolidVertex2D>,
/// The clipping bounds of the [`Mesh`].
clip_bounds: Rectangle<f32>,
@@ -22,7 +22,7 @@ pub enum Mesh<'a> {
origin: Point,
/// The vertex and index buffers of the [`Mesh`].
- buffers: &'a primitive::Mesh2D<primitive::GradientVertex2D>,
+ buffers: &'a mesh::Indexed<mesh::GradientVertex2D>,
/// The clipping bounds of the [`Mesh`].
clip_bounds: Rectangle<f32>,
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 0a5726b5..deb223ef 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -38,6 +38,7 @@
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod layer;
+pub mod primitive;
pub mod settings;
pub mod window;
@@ -46,6 +47,7 @@ pub mod geometry;
mod backend;
mod buffer;
+mod color;
mod quad;
mod text;
mod triangle;
@@ -59,6 +61,7 @@ pub use wgpu;
pub use backend::Backend;
pub use layer::Layer;
+pub use primitive::Primitive;
pub use settings::Settings;
#[cfg(any(feature = "image", feature = "svg"))]
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
new file mode 100644
index 00000000..8dbf3008
--- /dev/null
+++ b/wgpu/src/primitive.rs
@@ -0,0 +1,21 @@
+//! Draw using different graphical primitives.
+use crate::core::Rectangle;
+use crate::graphics::{Damage, Mesh};
+
+/// The graphical primitives supported by `iced_wgpu`.
+pub type Primitive = crate::graphics::Primitive<Custom>;
+
+/// The custom primitives supported by `iced_wgpu`.
+#[derive(Debug, Clone, PartialEq)]
+pub enum Custom {
+ /// A mesh primitive.
+ Mesh(Mesh),
+}
+
+impl Damage for Custom {
+ fn bounds(&self) -> Rectangle {
+ match self {
+ Self::Mesh(mesh) => mesh.bounds(),
+ }
+ }
+}
diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs
index 2b56d594..6db37252 100644
--- a/wgpu/src/quad/gradient.rs
+++ b/wgpu/src/quad/gradient.rs
@@ -96,36 +96,26 @@ impl Pipeline {
as u64,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &wgpu::vertex_attr_array!(
- // Color 1
- 1 => Float32x4,
- // Color 2
- 2 => Float32x4,
- // Color 3
- 3 => Float32x4,
- // Color 4
- 4 => Float32x4,
- // Color 5
- 5 => Float32x4,
- // Color 6
- 6 => Float32x4,
- // Color 7
- 7 => Float32x4,
- // Color 8
- 8 => Float32x4,
- // Offsets 1-4
- 9 => Float32x4,
- // Offsets 5-8
- 10 => Float32x4,
+ // Colors 1-2
+ 1 => Uint32x4,
+ // Colors 3-4
+ 2 => Uint32x4,
+ // Colors 5-6
+ 3 => Uint32x4,
+ // Colors 7-8
+ 4 => Uint32x4,
+ // Offsets 1-8
+ 5 => Uint32x4,
// Direction
- 11 => Float32x4,
+ 6 => Float32x4,
// Position & Scale
- 12 => Float32x4,
+ 7 => Float32x4,
// Border color
- 13 => Float32x4,
+ 8 => Float32x4,
// Border radius
- 14 => Float32x4,
+ 9 => Float32x4,
// Border width
- 15 => Float32
+ 10 => Float32
),
},
],
diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl
index 3232bdbe..fb402158 100644
--- a/wgpu/src/shader/quad.wgsl
+++ b/wgpu/src/shader/quad.wgsl
@@ -38,6 +38,13 @@ fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>)
return rx;
}
+fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
+ let rg: vec2<f32> = unpack2x16float(color.x);
+ let ba: vec2<f32> = unpack2x16float(color.y);
+
+ return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
+}
+
struct SolidVertexInput {
@location(0) v_pos: vec2<f32>,
@location(1) color: vec4<f32>,
@@ -140,40 +147,30 @@ fn solid_fs_main(
struct GradientVertexInput {
@location(0) v_pos: vec2<f32>,
- @location(1) color_1: vec4<f32>,
- @location(2) color_2: vec4<f32>,
- @location(3) color_3: vec4<f32>,
- @location(4) color_4: vec4<f32>,
- @location(5) color_5: vec4<f32>,
- @location(6) color_6: vec4<f32>,
- @location(7) color_7: vec4<f32>,
- @location(8) color_8: vec4<f32>,
- @location(9) offsets_1: vec4<f32>,
- @location(10) offsets_2: vec4<f32>,
- @location(11) direction: vec4<f32>,
- @location(12) position_and_scale: vec4<f32>,
- @location(13) border_color: vec4<f32>,
- @location(14) border_radius: vec4<f32>,
- @location(15) border_width: f32
+ @location(1) colors_1: vec4<u32>,
+ @location(2) colors_2: vec4<u32>,
+ @location(3) colors_3: vec4<u32>,
+ @location(4) colors_4: vec4<u32>,
+ @location(5) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
+ @location(7) position_and_scale: vec4<f32>,
+ @location(8) border_color: vec4<f32>,
+ @location(9) border_radius: vec4<f32>,
+ @location(10) border_width: f32,
}
struct GradientVertexOutput {
@builtin(position) position: vec4<f32>,
- @location(1) color_1: vec4<f32>,
- @location(2) color_2: vec4<f32>,
- @location(3) color_3: vec4<f32>,
- @location(4) color_4: vec4<f32>,
- @location(5) color_5: vec4<f32>,
- @location(6) color_6: vec4<f32>,
- @location(7) color_7: vec4<f32>,
- @location(8) color_8: vec4<f32>,
- @location(9) offsets_1: vec4<f32>,
- @location(10) offsets_2: vec4<f32>,
- @location(11) direction: vec4<f32>,
- @location(12) position_and_scale: vec4<f32>,
- @location(13) border_color: vec4<f32>,
- @location(14) border_radius: vec4<f32>,
- @location(15) border_width: f32
+ @location(1) colors_1: vec4<u32>,
+ @location(2) colors_2: vec4<u32>,
+ @location(3) colors_3: vec4<u32>,
+ @location(4) colors_4: vec4<u32>,
+ @location(5) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
+ @location(7) position_and_scale: vec4<f32>,
+ @location(8) border_color: vec4<f32>,
+ @location(9) border_radius: vec4<f32>,
+ @location(10) border_width: f32,
}
@vertex
@@ -199,16 +196,11 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
);
out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
- out.color_1 = input.color_1;
- out.color_2 = input.color_2;
- out.color_3 = input.color_3;
- out.color_4 = input.color_4;
- out.color_5 = input.color_5;
- out.color_6 = input.color_6;
- out.color_7 = input.color_7;
- out.color_8 = input.color_8;
- out.offsets_1 = input.offsets_1;
- out.offsets_2 = input.offsets_2;
+ out.colors_1 = input.colors_1;
+ out.colors_2 = input.colors_2;
+ out.colors_3 = input.colors_3;
+ out.colors_4 = input.colors_4;
+ out.offsets = input.offsets;
out.direction = input.direction * globals.scale;
out.position_and_scale = vec4<f32>(pos, scale);
out.border_color = input.border_color;
@@ -274,25 +266,28 @@ fn gradient(
@fragment
fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
let colors = array<vec4<f32>, 8>(
- input.color_1,
- input.color_2,
- input.color_3,
- input.color_4,
- input.color_5,
- input.color_6,
- input.color_7,
- input.color_8,
+ unpack_u32(input.colors_1.xy),
+ unpack_u32(input.colors_1.zw),
+ unpack_u32(input.colors_2.xy),
+ unpack_u32(input.colors_2.zw),
+ unpack_u32(input.colors_3.xy),
+ unpack_u32(input.colors_3.zw),
+ unpack_u32(input.colors_4.xy),
+ unpack_u32(input.colors_4.zw),
);
+ let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
+ let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
+
var offsets = array<f32, 8>(
- input.offsets_1.x,
- input.offsets_1.y,
- input.offsets_1.z,
- input.offsets_1.w,
- input.offsets_2.x,
- input.offsets_2.y,
- input.offsets_2.z,
- input.offsets_2.w,
+ offsets_1.x,
+ offsets_1.y,
+ offsets_1.z,
+ offsets_1.w,
+ offsets_2.x,
+ offsets_2.y,
+ offsets_2.z,
+ offsets_2.w,
);
//TODO could just pass this in to the shader but is probably more performant to just check it here
diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl
index 625fa46e..9f512d14 100644
--- a/wgpu/src/shader/triangle.wgsl
+++ b/wgpu/src/shader/triangle.wgsl
@@ -4,6 +4,13 @@ struct Globals {
@group(0) @binding(0) var<uniform> globals: Globals;
+fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
+ let rg: vec2<f32> = unpack2x16float(color.x);
+ let ba: vec2<f32> = unpack2x16float(color.y);
+
+ return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
+}
+
struct SolidVertexInput {
@location(0) position: vec2<f32>,
@location(1) color: vec4<f32>,
@@ -29,52 +36,39 @@ fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
return input.color;
}
+struct GradientVertexInput {
+ @location(0) v_pos: vec2<f32>,
+ @location(1) colors_1: vec4<u32>,
+ @location(2) colors_2: vec4<u32>,
+ @location(3) colors_3: vec4<u32>,
+ @location(4) colors_4: vec4<u32>,
+ @location(5) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
+}
+
struct GradientVertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) raw_position: vec2<f32>,
- @location(1) color_1: vec4<f32>,
- @location(2) color_2: vec4<f32>,
- @location(3) color_3: vec4<f32>,
- @location(4) color_4: vec4<f32>,
- @location(5) color_5: vec4<f32>,
- @location(6) color_6: vec4<f32>,
- @location(7) color_7: vec4<f32>,
- @location(8) color_8: vec4<f32>,
- @location(9) offsets_1: vec4<f32>,
- @location(10) offsets_2: vec4<f32>,
- @location(11) direction: vec4<f32>,
+ @location(1) colors_1: vec4<u32>,
+ @location(2) colors_2: vec4<u32>,
+ @location(3) colors_3: vec4<u32>,
+ @location(4) colors_4: vec4<u32>,
+ @location(5) offsets: vec4<u32>,
+ @location(6) direction: vec4<f32>,
}
@vertex
-fn gradient_vs_main(
- @location(0) input: vec2<f32>,
- @location(1) color_1: vec4<f32>,
- @location(2) color_2: vec4<f32>,
- @location(3) color_3: vec4<f32>,
- @location(4) color_4: vec4<f32>,
- @location(5) color_5: vec4<f32>,
- @location(6) color_6: vec4<f32>,
- @location(7) color_7: vec4<f32>,
- @location(8) color_8: vec4<f32>,
- @location(9) offsets_1: vec4<f32>,
- @location(10) offsets_2: vec4<f32>,
- @location(11) direction: vec4<f32>,
-) -> GradientVertexOutput {
+fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
var output: GradientVertexOutput;
- output.position = globals.transform * vec4<f32>(input.xy, 0.0, 1.0);
- output.raw_position = input;
- output.color_1 = color_1;
- output.color_2 = color_2;
- output.color_3 = color_3;
- output.color_4 = color_4;
- output.color_5 = color_5;
- output.color_6 = color_6;
- output.color_7 = color_7;
- output.color_8 = color_8;
- output.offsets_1 = offsets_1;
- output.offsets_2 = offsets_2;
- output.direction = direction;
+ output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
+ output.raw_position = input.v_pos;
+ output.colors_1 = input.colors_1;
+ output.colors_2 = input.colors_2;
+ output.colors_3 = input.colors_3;
+ output.colors_4 = input.colors_4;
+ output.offsets = input.offsets;
+ output.direction = input.direction;
return output;
}
@@ -135,25 +129,28 @@ fn gradient(
@fragment
fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
let colors = array<vec4<f32>, 8>(
- input.color_1,
- input.color_2,
- input.color_3,
- input.color_4,
- input.color_5,
- input.color_6,
- input.color_7,
- input.color_8,
+ unpack_u32(input.colors_1.xy),
+ unpack_u32(input.colors_1.zw),
+ unpack_u32(input.colors_2.xy),
+ unpack_u32(input.colors_2.zw),
+ unpack_u32(input.colors_3.xy),
+ unpack_u32(input.colors_3.zw),
+ unpack_u32(input.colors_4.xy),
+ unpack_u32(input.colors_4.zw),
);
+ let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
+ let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
+
var offsets = array<f32, 8>(
- input.offsets_1.x,
- input.offsets_1.y,
- input.offsets_1.z,
- input.offsets_1.w,
- input.offsets_2.x,
- input.offsets_2.y,
- input.offsets_2.z,
- input.offsets_2.w,
+ offsets_1.x,
+ offsets_1.y,
+ offsets_1.z,
+ offsets_1.w,
+ offsets_2.x,
+ offsets_2.y,
+ offsets_2.z,
+ offsets_2.w,
);
var last_index = 7;
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 6a552270..65d3b818 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -18,8 +18,7 @@ pub struct Pipeline {
renderers: Vec<glyphon::TextRenderer>,
atlas: glyphon::TextAtlas,
prepare_layer: usize,
- measurement_cache: RefCell<Cache>,
- render_cache: Cache,
+ cache: RefCell<Cache>,
}
impl Pipeline {
@@ -47,8 +46,7 @@ impl Pipeline {
},
),
prepare_layer: 0,
- measurement_cache: RefCell::new(Cache::new()),
- render_cache: Cache::new(),
+ cache: RefCell::new(Cache::new()),
}
}
@@ -56,6 +54,8 @@ impl Pipeline {
let _ = self.font_system.get_mut().db_mut().load_font_source(
glyphon::fontdb::Source::Binary(Arc::new(bytes.into_owned())),
);
+
+ self.cache = RefCell::new(Cache::new());
}
pub fn prepare(
@@ -78,28 +78,33 @@ impl Pipeline {
let font_system = self.font_system.get_mut();
let renderer = &mut self.renderers[self.prepare_layer];
+ let cache = self.cache.get_mut();
+
+ if self.prepare_layer == 0 {
+ cache.trim(Purpose::Drawing);
+ }
let keys: Vec<_> = sections
.iter()
.map(|section| {
- let (key, _) = self.render_cache.allocate(
+ let (key, _) = cache.allocate(
font_system,
Key {
content: section.content,
- size: section.size * scale_factor,
+ size: section.size,
line_height: f32::from(
section
.line_height
.to_absolute(Pixels(section.size)),
- ) * scale_factor,
+ ),
font: section.font,
bounds: Size {
- width: (section.bounds.width * scale_factor).ceil(),
- height: (section.bounds.height * scale_factor)
- .ceil(),
+ width: section.bounds.width,
+ height: section.bounds.height,
},
shaping: section.shaping,
},
+ Purpose::Drawing,
);
key
@@ -113,14 +118,14 @@ impl Pipeline {
.iter()
.zip(keys.iter())
.filter_map(|(section, key)| {
- let buffer =
- self.render_cache.get(key).expect("Get cached buffer");
-
- let (max_width, total_height) = measure(buffer);
+ let entry = cache.get(key).expect("Get cached buffer");
let x = section.bounds.x * scale_factor;
let y = section.bounds.y * scale_factor;
+ let max_width = entry.bounds.width * scale_factor;
+ let total_height = entry.bounds.height * scale_factor;
+
let left = match section.horizontal_alignment {
alignment::Horizontal::Left => x,
alignment::Horizontal::Center => x - max_width / 2.0,
@@ -142,14 +147,11 @@ impl Pipeline {
let clip_bounds = bounds.intersection(&section_bounds)?;
- // TODO: Subpixel glyph positioning
- let left = left.round() as i32;
- let top = top.round() as i32;
-
Some(glyphon::TextArea {
- buffer,
+ buffer: &entry.buffer,
left,
top,
+ scale: scale_factor,
bounds: glyphon::TextBounds {
left: clip_bounds.x as i32,
top: clip_bounds.y as i32,
@@ -227,11 +229,14 @@ impl Pipeline {
pub fn end_frame(&mut self) {
self.atlas.trim();
- self.render_cache.trim();
self.prepare_layer = 0;
}
+ pub fn trim_measurements(&mut self) {
+ self.cache.get_mut().trim(Purpose::Measuring);
+ }
+
pub fn measure(
&self,
content: &str,
@@ -240,12 +245,12 @@ impl Pipeline {
font: Font,
bounds: Size,
shaping: Shaping,
- ) -> (f32, f32) {
- let mut measurement_cache = self.measurement_cache.borrow_mut();
+ ) -> Size {
+ let mut cache = self.cache.borrow_mut();
let line_height = f32::from(line_height.to_absolute(Pixels(size)));
- let (_, paragraph) = measurement_cache.allocate(
+ let (_, entry) = cache.allocate(
&mut self.font_system.borrow_mut(),
Key {
content,
@@ -255,9 +260,10 @@ impl Pipeline {
bounds,
shaping,
},
+ Purpose::Measuring,
);
- measure(paragraph)
+ entry.bounds
}
pub fn hit_test(
@@ -271,11 +277,11 @@ impl Pipeline {
point: Point,
_nearest_only: bool,
) -> Option<Hit> {
- let mut measurement_cache = self.measurement_cache.borrow_mut();
+ let mut cache = self.cache.borrow_mut();
let line_height = f32::from(line_height.to_absolute(Pixels(size)));
- let (_, paragraph) = measurement_cache.allocate(
+ let (_, entry) = cache.allocate(
&mut self.font_system.borrow_mut(),
Key {
content,
@@ -285,26 +291,23 @@ impl Pipeline {
bounds,
shaping,
},
+ Purpose::Measuring,
);
- let cursor = paragraph.hit(point.x, point.y)?;
+ let cursor = entry.buffer.hit(point.x, point.y)?;
Some(Hit::CharOffset(cursor.index))
}
-
- pub fn trim_measurement_cache(&mut self) {
- self.measurement_cache.borrow_mut().trim();
- }
}
-fn measure(buffer: &glyphon::Buffer) -> (f32, f32) {
+fn measure(buffer: &glyphon::Buffer) -> Size {
let (width, total_lines) = buffer
.layout_runs()
.fold((0.0, 0usize), |(width, total_lines), run| {
(run.line_w.max(width), total_lines + 1)
});
- (width, total_lines as f32 * buffer.metrics().line_height)
+ Size::new(width, total_lines as f32 * buffer.metrics().line_height)
}
fn to_family(family: font::Family) -> glyphon::Family<'static> {
@@ -354,11 +357,24 @@ fn to_shaping(shaping: Shaping) -> glyphon::Shaping {
}
struct Cache {
- entries: FxHashMap<KeyHash, glyphon::Buffer>,
- recently_used: FxHashSet<KeyHash>,
+ entries: FxHashMap<KeyHash, Entry>,
+ aliases: FxHashMap<KeyHash, KeyHash>,
+ recently_measured: FxHashSet<KeyHash>,
+ recently_drawn: FxHashSet<KeyHash>,
hasher: HashBuilder,
}
+struct Entry {
+ buffer: glyphon::Buffer,
+ bounds: Size,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum Purpose {
+ Measuring,
+ Drawing,
+}
+
#[cfg(not(target_arch = "wasm32"))]
type HashBuilder = twox_hash::RandomXxHashBuilder64;
@@ -369,12 +385,14 @@ impl Cache {
fn new() -> Self {
Self {
entries: FxHashMap::default(),
- recently_used: FxHashSet::default(),
+ aliases: FxHashMap::default(),
+ recently_measured: FxHashSet::default(),
+ recently_drawn: FxHashSet::default(),
hasher: HashBuilder::default(),
}
}
- fn get(&self, key: &KeyHash) -> Option<&glyphon::Buffer> {
+ fn get(&self, key: &KeyHash) -> Option<&Entry> {
self.entries.get(key)
}
@@ -382,21 +400,21 @@ impl Cache {
&mut self,
font_system: &mut glyphon::FontSystem,
key: Key<'_>,
- ) -> (KeyHash, &mut glyphon::Buffer) {
- let hash = {
- let mut hasher = self.hasher.build_hasher();
-
- key.content.hash(&mut hasher);
- key.size.to_bits().hash(&mut hasher);
- key.line_height.to_bits().hash(&mut hasher);
- key.font.hash(&mut hasher);
- key.bounds.width.to_bits().hash(&mut hasher);
- key.bounds.height.to_bits().hash(&mut hasher);
- key.shaping.hash(&mut hasher);
-
- hasher.finish()
+ purpose: Purpose,
+ ) -> (KeyHash, &mut Entry) {
+ let hash = key.hash(self.hasher.build_hasher());
+
+ let recently_used = match purpose {
+ Purpose::Measuring => &mut self.recently_measured,
+ Purpose::Drawing => &mut self.recently_drawn,
};
+ if let Some(hash) = self.aliases.get(&hash) {
+ let _ = recently_used.insert(*hash);
+
+ return (*hash, self.entries.get_mut(hash).unwrap());
+ }
+
if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
let metrics = glyphon::Metrics::new(key.size, key.line_height);
let mut buffer = glyphon::Buffer::new(font_system, metrics);
@@ -416,19 +434,48 @@ impl Cache {
to_shaping(key.shaping),
);
- let _ = entry.insert(buffer);
+ let bounds = measure(&buffer);
+ let _ = entry.insert(Entry { buffer, bounds });
+
+ for bounds in [
+ bounds,
+ Size {
+ width: key.bounds.width,
+ ..bounds
+ },
+ ] {
+ if key.bounds != bounds {
+ let _ = self.aliases.insert(
+ Key { bounds, ..key }.hash(self.hasher.build_hasher()),
+ hash,
+ );
+ }
+ }
}
- let _ = self.recently_used.insert(hash);
+ let _ = recently_used.insert(hash);
(hash, self.entries.get_mut(&hash).unwrap())
}
- fn trim(&mut self) {
- self.entries
- .retain(|key, _| self.recently_used.contains(key));
+ fn trim(&mut self, purpose: Purpose) {
+ self.entries.retain(|key, _| {
+ self.recently_measured.contains(key)
+ || self.recently_drawn.contains(key)
+ });
+ self.aliases.retain(|_, value| {
+ self.recently_measured.contains(value)
+ || self.recently_drawn.contains(value)
+ });
- self.recently_used.clear();
+ match purpose {
+ Purpose::Measuring => {
+ self.recently_measured.clear();
+ }
+ Purpose::Drawing => {
+ self.recently_drawn.clear();
+ }
+ }
}
}
@@ -442,4 +489,18 @@ struct Key<'a> {
shaping: Shaping,
}
+impl Key<'_> {
+ fn hash<H: Hasher>(self, mut hasher: H) -> KeyHash {
+ self.content.hash(&mut hasher);
+ self.size.to_bits().hash(&mut hasher);
+ self.line_height.to_bits().hash(&mut hasher);
+ self.font.hash(&mut hasher);
+ self.bounds.width.to_bits().hash(&mut hasher);
+ self.bounds.height.to_bits().hash(&mut hasher);
+ self.shaping.hash(&mut hasher);
+
+ hasher.finish()
+ }
+}
+
type KeyHash = u64;
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index 6f32f182..d8b23dfe 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -393,7 +393,7 @@ impl Uniforms {
}
mod solid {
- use crate::graphics::primitive;
+ use crate::graphics::mesh;
use crate::graphics::Antialiasing;
use crate::triangle;
use crate::Buffer;
@@ -406,7 +406,7 @@ mod solid {
#[derive(Debug)]
pub struct Layer {
- pub vertices: Buffer<primitive::ColoredVertex2D>,
+ pub vertices: Buffer<mesh::SolidVertex2D>,
pub uniforms: Buffer<triangle::Uniforms>,
pub constants: wgpu::BindGroup,
}
@@ -493,38 +493,40 @@ mod solid {
),
});
- let pipeline = device.create_render_pipeline(
- &wgpu::RenderPipelineDescriptor {
- label: Some("iced_wgpu::triangle::solid pipeline"),
- layout: Some(&layout),
- vertex: wgpu::VertexState {
- module: &shader,
- entry_point: "solid_vs_main",
- buffers: &[wgpu::VertexBufferLayout {
- array_stride: std::mem::size_of::<
- primitive::ColoredVertex2D,
- >()
- as u64,
- step_mode: wgpu::VertexStepMode::Vertex,
- attributes: &wgpu::vertex_attr_array!(
- // Position
- 0 => Float32x2,
- // Color
- 1 => Float32x4,
- ),
- }],
+ let pipeline =
+ device.create_render_pipeline(
+ &wgpu::RenderPipelineDescriptor {
+ label: Some("iced_wgpu::triangle::solid pipeline"),
+ layout: Some(&layout),
+ vertex: wgpu::VertexState {
+ module: &shader,
+ entry_point: "solid_vs_main",
+ buffers: &[wgpu::VertexBufferLayout {
+ array_stride: std::mem::size_of::<
+ mesh::SolidVertex2D,
+ >(
+ )
+ as u64,
+ step_mode: wgpu::VertexStepMode::Vertex,
+ attributes: &wgpu::vertex_attr_array!(
+ // Position
+ 0 => Float32x2,
+ // Color
+ 1 => Float32x4,
+ ),
+ }],
+ },
+ fragment: Some(wgpu::FragmentState {
+ module: &shader,
+ entry_point: "solid_fs_main",
+ targets: &[triangle::fragment_target(format)],
+ }),
+ primitive: triangle::primitive_state(),
+ depth_stencil: None,
+ multisample: triangle::multisample_state(antialiasing),
+ multiview: None,
},
- fragment: Some(wgpu::FragmentState {
- module: &shader,
- entry_point: "solid_fs_main",
- targets: &[triangle::fragment_target(format)],
- }),
- primitive: triangle::primitive_state(),
- depth_stencil: None,
- multisample: triangle::multisample_state(antialiasing),
- multiview: None,
- },
- );
+ );
Self {
pipeline,
@@ -535,7 +537,8 @@ mod solid {
}
mod gradient {
- use crate::graphics::{primitive, Antialiasing};
+ use crate::graphics::mesh;
+ use crate::graphics::Antialiasing;
use crate::triangle;
use crate::Buffer;
@@ -547,7 +550,7 @@ mod gradient {
#[derive(Debug)]
pub struct Layer {
- pub vertices: Buffer<primitive::GradientVertex2D>,
+ pub vertices: Buffer<mesh::GradientVertex2D>,
pub uniforms: Buffer<triangle::Uniforms>,
pub constants: wgpu::BindGroup,
}
@@ -645,35 +648,25 @@ mod gradient {
entry_point: "gradient_vs_main",
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<
- primitive::GradientVertex2D,
+ mesh::GradientVertex2D,
>()
as u64,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array!(
// Position
0 => Float32x2,
- // Color 1
- 1 => Float32x4,
- // Color 2
- 2 => Float32x4,
- // Color 3
- 3 => Float32x4,
- // Color 4
- 4 => Float32x4,
- // Color 5
- 5 => Float32x4,
- // Color 6
- 6 => Float32x4,
- // Color 7
- 7 => Float32x4,
- // Color 8
- 8 => Float32x4,
- // Offsets 1-4
- 9 => Float32x4,
- // Offsets 5-8
- 10 => Float32x4,
+ // Colors 1-2
+ 1 => Uint32x4,
+ // Colors 3-4
+ 2 => Uint32x4,
+ // Colors 5-6
+ 3 => Uint32x4,
+ // Colors 7-8
+ 4 => Uint32x4,
+ // Offsets
+ 5 => Uint32x4,
// Direction
- 11 => Float32x4
+ 6 => Float32x4
),
}],
},
diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs
index 4afbdb32..320b5b12 100644
--- a/wgpu/src/triangle/msaa.rs
+++ b/wgpu/src/triangle/msaa.rs
@@ -16,15 +16,8 @@ impl Blit {
format: wgpu::TextureFormat,
antialiasing: graphics::Antialiasing,
) -> Blit {
- let 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::Nearest,
- min_filter: wgpu::FilterMode::Nearest,
- mipmap_filter: wgpu::FilterMode::Nearest,
- ..Default::default()
- });
+ let sampler =
+ device.create_sampler(&wgpu::SamplerDescriptor::default());
let constant_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 2eaafde0..cd5b20cc 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -1,10 +1,10 @@
//! Connect a window with a renderer.
-use crate::core::Color;
+use crate::core::{Color, Size};
use crate::graphics;
use crate::graphics::color;
use crate::graphics::compositor;
-use crate::graphics::{Error, Primitive, Viewport};
-use crate::{Backend, Renderer, Settings};
+use crate::graphics::{Error, Viewport};
+use crate::{Backend, Primitive, Renderer, Settings};
use futures::stream::{self, StreamExt};
@@ -283,4 +283,154 @@ impl<Theme> graphics::Compositor for Compositor<Theme> {
)
})
}
+
+ fn screenshot<T: AsRef<str>>(
+ &mut self,
+ renderer: &mut Self::Renderer,
+ _surface: &mut Self::Surface,
+ viewport: &Viewport,
+ background_color: Color,
+ overlay: &[T],
+ ) -> Vec<u8> {
+ renderer.with_primitives(|backend, primitives| {
+ screenshot(
+ self,
+ backend,
+ primitives,
+ viewport,
+ background_color,
+ overlay,
+ )
+ })
+ }
+}
+
+/// Renders the current surface to an offscreen buffer.
+///
+/// Returns RGBA bytes of the texture data.
+pub fn screenshot<Theme, T: AsRef<str>>(
+ compositor: &Compositor<Theme>,
+ backend: &mut Backend,
+ primitives: &[Primitive],
+ viewport: &Viewport,
+ background_color: Color,
+ overlay: &[T],
+) -> Vec<u8> {
+ let mut encoder = compositor.device.create_command_encoder(
+ &wgpu::CommandEncoderDescriptor {
+ label: Some("iced_wgpu.offscreen.encoder"),
+ },
+ );
+
+ let dimensions = BufferDimensions::new(viewport.physical_size());
+
+ let texture_extent = wgpu::Extent3d {
+ width: dimensions.width,
+ height: dimensions.height,
+ depth_or_array_layers: 1,
+ };
+
+ let texture = compositor.device.create_texture(&wgpu::TextureDescriptor {
+ label: Some("iced_wgpu.offscreen.source_texture"),
+ size: texture_extent,
+ mip_level_count: 1,
+ sample_count: 1,
+ dimension: wgpu::TextureDimension::D2,
+ format: compositor.format,
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT
+ | wgpu::TextureUsages::COPY_SRC
+ | wgpu::TextureUsages::TEXTURE_BINDING,
+ view_formats: &[],
+ });
+
+ let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
+
+ backend.present(
+ &compositor.device,
+ &compositor.queue,
+ &mut encoder,
+ Some(background_color),
+ &view,
+ primitives,
+ viewport,
+ overlay,
+ );
+
+ let texture = crate::color::convert(
+ &compositor.device,
+ &mut encoder,
+ texture,
+ if color::GAMMA_CORRECTION {
+ wgpu::TextureFormat::Rgba8UnormSrgb
+ } else {
+ wgpu::TextureFormat::Rgba8Unorm
+ },
+ );
+
+ let output_buffer =
+ compositor.device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some("iced_wgpu.offscreen.output_texture_buffer"),
+ size: (dimensions.padded_bytes_per_row * dimensions.height as usize)
+ as u64,
+ usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+
+ encoder.copy_texture_to_buffer(
+ texture.as_image_copy(),
+ wgpu::ImageCopyBuffer {
+ buffer: &output_buffer,
+ layout: wgpu::ImageDataLayout {
+ offset: 0,
+ bytes_per_row: Some(dimensions.padded_bytes_per_row as u32),
+ rows_per_image: None,
+ },
+ },
+ texture_extent,
+ );
+
+ let index = compositor.queue.submit(Some(encoder.finish()));
+
+ let slice = output_buffer.slice(..);
+ slice.map_async(wgpu::MapMode::Read, |_| {});
+
+ let _ = compositor
+ .device
+ .poll(wgpu::Maintain::WaitForSubmissionIndex(index));
+
+ let mapped_buffer = slice.get_mapped_range();
+
+ mapped_buffer.chunks(dimensions.padded_bytes_per_row).fold(
+ vec![],
+ |mut acc, row| {
+ acc.extend(&row[..dimensions.unpadded_bytes_per_row]);
+ acc
+ },
+ )
+}
+
+#[derive(Clone, Copy, Debug)]
+struct BufferDimensions {
+ width: u32,
+ height: u32,
+ unpadded_bytes_per_row: usize,
+ padded_bytes_per_row: usize,
+}
+
+impl BufferDimensions {
+ fn new(size: Size<u32>) -> Self {
+ let unpadded_bytes_per_row = size.width as usize * 4; //slice of buffer per row; always RGBA
+ let alignment = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize; //256
+ let padded_bytes_per_row_padding =
+ (alignment - unpadded_bytes_per_row % alignment) % alignment;
+ let padded_bytes_per_row =
+ unpadded_bytes_per_row + padded_bytes_per_row_padding;
+
+ Self {
+ width: size.width,
+ height: size.height,
+ unpadded_bytes_per_row,
+ padded_bytes_per_row,
+ }
+ }
}