summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2023-02-24 20:52:10 +0100
committerLibravatar GitHub <noreply@github.com>2023-02-24 20:52:10 +0100
commit368cadd25a8b57ee5c41e45d1abe8d1dfb194c69 (patch)
tree191cb7cc7807a5fe513b3d485b2fda21d3bf0bde
parent573d27eb52bbfacf1b06983b4282f00eb5265bdc (diff)
parent8059c40142d601588e01c152ce1bb72a1295dde8 (diff)
downloadiced-368cadd25a8b57ee5c41e45d1abe8d1dfb194c69.tar.gz
iced-368cadd25a8b57ee5c41e45d1abe8d1dfb194c69.tar.bz2
iced-368cadd25a8b57ee5c41e45d1abe8d1dfb194c69.zip
Merge pull request #1697 from iced-rs/text-glyphon
Text shaping, font fallback, and `iced_wgpu` overhaul
-rw-r--r--.github/workflows/test.yml2
-rw-r--r--Cargo.toml23
-rw-r--r--core/src/font.rs55
-rw-r--r--core/src/lib.rs2
-rw-r--r--examples/checkbox/fonts/icons.ttfbin1272 -> 1784 bytes
-rw-r--r--examples/checkbox/src/main.rs28
-rw-r--r--examples/integration/.gitignore (renamed from examples/integration_wgpu/.gitignore)0
-rw-r--r--examples/integration/Cargo.toml (renamed from examples/integration_wgpu/Cargo.toml)2
-rw-r--r--examples/integration/README.md (renamed from examples/integration_wgpu/README.md)0
-rw-r--r--examples/integration/index.html (renamed from examples/integration_wgpu/index.html)4
-rw-r--r--examples/integration/src/controls.rs (renamed from examples/integration_wgpu/src/controls.rs)0
-rw-r--r--examples/integration/src/main.rs (renamed from examples/integration_wgpu/src/main.rs)19
-rw-r--r--examples/integration/src/scene.rs (renamed from examples/integration_wgpu/src/scene.rs)0
-rw-r--r--examples/integration/src/shader/frag.wgsl (renamed from examples/integration_wgpu/src/shader/frag.wgsl)0
-rw-r--r--examples/integration/src/shader/vert.wgsl (renamed from examples/integration_wgpu/src/shader/vert.wgsl)0
-rw-r--r--examples/integration_opengl/Cargo.toml12
-rw-r--r--examples/integration_opengl/README.md16
-rw-r--r--examples/integration_opengl/src/controls.rs101
-rw-r--r--examples/integration_opengl/src/main.rs187
-rw-r--r--examples/integration_opengl/src/scene.rs102
-rw-r--r--examples/todos/fonts/icons.ttfbin5596 -> 5732 bytes
-rw-r--r--examples/todos/src/main.rs20
-rw-r--r--glow/Cargo.toml51
-rw-r--r--glow/README.md51
-rw-r--r--glow/src/backend.rs280
-rw-r--r--glow/src/image.rs254
-rw-r--r--glow/src/image/storage.rs78
-rw-r--r--glow/src/lib.rs53
-rw-r--r--glow/src/program.rs133
-rw-r--r--glow/src/quad.rs74
-rw-r--r--glow/src/quad/compatibility.rs349
-rw-r--r--glow/src/quad/core.rs244
-rw-r--r--glow/src/settings.rs61
-rw-r--r--glow/src/shader/common/gradient.frag59
-rw-r--r--glow/src/shader/common/gradient.vert9
-rw-r--r--glow/src/shader/common/image.frag22
-rw-r--r--glow/src/shader/common/image.vert9
-rw-r--r--glow/src/shader/common/solid.frag18
-rw-r--r--glow/src/shader/common/solid.vert11
-rw-r--r--glow/src/shader/compatibility/quad.frag83
-rw-r--r--glow/src/shader/compatibility/quad.vert46
-rw-r--r--glow/src/shader/core/quad.frag95
-rw-r--r--glow/src/shader/core/quad.vert52
-rw-r--r--glow/src/text.rs257
-rw-r--r--glow/src/triangle.rs595
-rw-r--r--glow/src/window.rs4
-rw-r--r--glow/src/window/compositor.rs111
-rw-r--r--glutin/Cargo.toml42
-rw-r--r--glutin/README.md29
-rw-r--r--glutin/src/application.rs508
-rw-r--r--glutin/src/lib.rs33
-rw-r--r--graphics/Cargo.toml7
-rw-r--r--graphics/fonts/Lato-Regular.ttfbin75136 -> 0 bytes
-rw-r--r--graphics/fonts/OFL.txt93
-rw-r--r--graphics/src/backend.rs8
-rw-r--r--graphics/src/font.rs35
-rw-r--r--graphics/src/font/source.rs45
-rw-r--r--graphics/src/layer.rs11
-rw-r--r--graphics/src/layer/text.rs4
-rw-r--r--graphics/src/lib.rs1
-rw-r--r--graphics/src/renderer.rs9
-rw-r--r--graphics/src/widget/canvas/text.rs2
-rw-r--r--native/src/command/action.rs16
-rw-r--r--native/src/font.rs19
-rw-r--r--native/src/lib.rs6
-rw-r--r--native/src/overlay/menu.rs23
-rw-r--r--native/src/program.rs3
-rw-r--r--native/src/renderer/null.rs12
-rw-r--r--native/src/text.rs23
-rw-r--r--native/src/widget/checkbox.rs16
-rw-r--r--native/src/widget/pick_list.rs45
-rw-r--r--native/src/widget/radio.rs10
-rw-r--r--native/src/widget/text.rs22
-rw-r--r--native/src/widget/text_input.rs57
-rw-r--r--native/src/widget/toggler.rs12
-rw-r--r--src/application.rs1
-rw-r--r--src/lib.rs17
-rw-r--r--src/settings.rs34
-rw-r--r--wgpu/Cargo.toml20
-rw-r--r--wgpu/fonts/Iced-Icons.ttf (renamed from graphics/fonts/Icons.ttf)bin5032 -> 5108 bytes
-rw-r--r--wgpu/src/backend.rs381
-rw-r--r--wgpu/src/buffer.rs86
-rw-r--r--wgpu/src/buffer/dynamic.rs23
-rw-r--r--wgpu/src/buffer/static.rs32
-rw-r--r--wgpu/src/image.rs255
-rw-r--r--wgpu/src/image/atlas.rs49
-rw-r--r--wgpu/src/lib.rs7
-rw-r--r--wgpu/src/quad.rs228
-rw-r--r--wgpu/src/settings.rs37
-rw-r--r--wgpu/src/text.rs571
-rw-r--r--wgpu/src/triangle.rs561
-rw-r--r--wgpu/src/window/compositor.rs43
-rw-r--r--winit/src/application.rs10
-rw-r--r--winit/src/settings.rs8
94 files changed, 1639 insertions, 5387 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 38b81842..390e7bf3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -38,4 +38,4 @@ jobs:
- name: Check compilation of `todos` example
run: cargo build --package todos --target wasm32-unknown-unknown
- name: Check compilation of `integration_wgpu` example
- run: cargo build --package integration_wgpu --target wasm32-unknown-unknown
+ run: cargo build --package integration --target wasm32-unknown-unknown
diff --git a/Cargo.toml b/Cargo.toml
index d26ec2b6..551e12ac 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,21 +12,14 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[features]
-default = ["wgpu"]
# Enables the `Image` widget
-image = ["iced_wgpu?/image", "iced_glow?/image", "image_rs"]
+image = ["iced_wgpu/image", "image_rs"]
# Enables the `Svg` widget
-svg = ["iced_wgpu?/svg", "iced_glow?/svg"]
+svg = ["iced_wgpu/svg"]
# Enables the `Canvas` widget
canvas = ["iced_graphics/canvas"]
# Enables the `QRCode` widget
qr_code = ["iced_graphics/qr_code"]
-# Enables the `iced_wgpu` renderer
-wgpu = ["iced_wgpu"]
-# Enables using system fonts
-default_system_font = ["iced_wgpu?/default_system_font", "iced_glow?/default_system_font"]
-# Enables the `iced_glow` renderer. Overrides `iced_wgpu`
-glow = ["iced_glow", "iced_glutin"]
# Enables a debug view in native platforms (press F12)
debug = ["iced_winit/debug"]
# Enables `tokio` as the `executor::Default` on native platforms
@@ -42,9 +35,7 @@ system = ["iced_winit/system"]
# Enables chrome traces
chrome-trace = [
"iced_winit/chrome-trace",
- "iced_glutin?/trace",
- "iced_wgpu?/tracing",
- "iced_glow?/tracing",
+ "iced_wgpu/tracing",
]
[badges]
@@ -55,8 +46,6 @@ members = [
"core",
"futures",
"graphics",
- "glow",
- "glutin",
"lazy",
"native",
"style",
@@ -71,8 +60,6 @@ iced_futures = { version = "0.6", path = "futures" }
iced_native = { version = "0.9", path = "native" }
iced_graphics = { version = "0.7", path = "graphics" }
iced_winit = { version = "0.8", path = "winit", features = ["application"] }
-iced_glutin = { version = "0.7", path = "glutin", optional = true }
-iced_glow = { version = "0.7", path = "glow", optional = true }
thiserror = "1.0"
[dependencies.image_rs]
@@ -81,10 +68,10 @@ package = "image"
optional = true
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-iced_wgpu = { version = "0.9", path = "wgpu", optional = true }
+iced_wgpu = { version = "0.9", path = "wgpu" }
[target.'cfg(target_arch = "wasm32")'.dependencies]
-iced_wgpu = { version = "0.9", path = "wgpu", features = ["webgl"], optional = true }
+iced_wgpu = { version = "0.9", path = "wgpu", features = ["webgl"] }
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
diff --git a/core/src/font.rs b/core/src/font.rs
index 3f9ad2b5..b67c08af 100644
--- a/core/src/font.rs
+++ b/core/src/font.rs
@@ -1,24 +1,45 @@
+//! Load and use fonts.
+use std::hash::Hash;
+
/// A font.
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Font {
- /// The default font.
- ///
- /// This is normally a font configured in a renderer or loaded from the
- /// system.
- Default,
+ /// The name of a font family of choice.
+ Name(&'static str),
+
+ /// Serif fonts represent the formal text style for a script.
+ Serif,
+
+ /// Glyphs in sans-serif fonts, as the term is used in CSS, are generally low
+ /// contrast and have stroke endings that are plain — without any flaring,
+ /// cross stroke, or other ornamentation.
+ SansSerif,
+
+ /// Glyphs in cursive fonts generally use a more informal script style, and
+ /// the result looks more like handwritten pen or brush writing than printed
+ /// letterwork.
+ Cursive,
- /// An external font.
- External {
- /// The name of the external font
- name: &'static str,
+ /// Fantasy fonts are primarily decorative or expressive fonts that contain
+ /// decorative or expressive representations of characters.
+ Fantasy,
- /// The bytes of the external font
- bytes: &'static [u8],
- },
+ /// The sole criterion of a monospace font is that all glyphs have the same
+ /// fixed width.
+ Monospace,
}
-impl Default for Font {
- fn default() -> Font {
- Font::Default
- }
+/// The weight of some text.
+#[allow(missing_docs)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Weight {
+ Thin,
+ ExtraLight,
+ Light,
+ Normal,
+ Medium,
+ Semibold,
+ Bold,
+ ExtraBold,
+ Black,
}
diff --git a/core/src/lib.rs b/core/src/lib.rs
index d3596b4d..d7314851 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -25,6 +25,7 @@
#![forbid(unsafe_code, rust_2018_idioms)]
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
pub mod alignment;
+pub mod font;
pub mod keyboard;
pub mod mouse;
pub mod time;
@@ -32,7 +33,6 @@ pub mod time;
mod background;
mod color;
mod content_fit;
-mod font;
mod length;
mod padding;
mod pixels;
diff --git a/examples/checkbox/fonts/icons.ttf b/examples/checkbox/fonts/icons.ttf
index a2046844..82f28481 100644
--- a/examples/checkbox/fonts/icons.ttf
+++ b/examples/checkbox/fonts/icons.ttf
Binary files differ
diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs
index 09950bb8..77111490 100644
--- a/examples/checkbox/src/main.rs
+++ b/examples/checkbox/src/main.rs
@@ -1,10 +1,9 @@
+use iced::executor;
+use iced::font::{self, Font};
use iced::widget::{checkbox, column, container};
-use iced::{Element, Font, Length, Sandbox, Settings};
+use iced::{Application, Command, Element, Length, Settings, Theme};
-const ICON_FONT: Font = Font::External {
- name: "Icons",
- bytes: include_bytes!("../fonts/icons.ttf"),
-};
+const ICON_FONT: Font = Font::Name("icons");
pub fn main() -> iced::Result {
Example::run(Settings::default())
@@ -20,24 +19,35 @@ struct Example {
enum Message {
DefaultChecked(bool),
CustomChecked(bool),
+ FontLoaded(Result<(), font::Error>),
}
-impl Sandbox for Example {
+impl Application for Example {
type Message = Message;
+ type Flags = ();
+ type Executor = executor::Default;
+ type Theme = Theme;
- fn new() -> Self {
- Default::default()
+ fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
+ (
+ Self::default(),
+ font::load(include_bytes!("../fonts/icons.ttf").as_ref())
+ .map(Message::FontLoaded),
+ )
}
fn title(&self) -> String {
String::from("Checkbox - Iced")
}
- fn update(&mut self, message: Message) {
+ fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::DefaultChecked(value) => self.default_checkbox = value,
Message::CustomChecked(value) => self.custom_checkbox = value,
+ Message::FontLoaded(_) => (),
}
+
+ Command::none()
}
fn view(&self) -> Element<Message> {
diff --git a/examples/integration_wgpu/.gitignore b/examples/integration/.gitignore
index e188dc28..e188dc28 100644
--- a/examples/integration_wgpu/.gitignore
+++ b/examples/integration/.gitignore
diff --git a/examples/integration_wgpu/Cargo.toml b/examples/integration/Cargo.toml
index eaa1df7e..200306aa 100644
--- a/examples/integration_wgpu/Cargo.toml
+++ b/examples/integration/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "integration_wgpu"
+name = "integration"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
diff --git a/examples/integration_wgpu/README.md b/examples/integration/README.md
index ece9ba1e..ece9ba1e 100644
--- a/examples/integration_wgpu/README.md
+++ b/examples/integration/README.md
diff --git a/examples/integration_wgpu/index.html b/examples/integration/index.html
index 461e67a4..920bc4a0 100644
--- a/examples/integration_wgpu/index.html
+++ b/examples/integration/index.html
@@ -8,8 +8,8 @@
<h1>integration_wgpu</h1>
<canvas id="iced_canvas"></canvas>
<script type="module">
- import init from "./integration_wgpu.js";
- init('./integration_wgpu_bg.wasm');
+ import init from "./integration.js";
+ init('./integration_bg.wasm');
</script>
<style>
body {
diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration/src/controls.rs
index 533cb6e2..533cb6e2 100644
--- a/examples/integration_wgpu/src/controls.rs
+++ b/examples/integration/src/controls.rs
diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration/src/main.rs
index 2a56b6fa..a7a90ced 100644
--- a/examples/integration_wgpu/src/main.rs
+++ b/examples/integration/src/main.rs
@@ -125,17 +125,18 @@ pub fn main() {
let mut resized = false;
- // Initialize staging belt
- let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024);
-
// Initialize scene and GUI controls
let scene = Scene::new(&device, format);
let controls = Controls::new();
// Initialize iced
let mut debug = Debug::new();
- let mut renderer =
- Renderer::new(Backend::new(&device, Settings::default(), format));
+ let mut renderer = Renderer::new(Backend::new(
+ &device,
+ &queue,
+ Settings::default(),
+ format,
+ ));
let mut state = program::State::new(
controls,
@@ -247,8 +248,9 @@ pub fn main() {
renderer.with_primitives(|backend, primitive| {
backend.present(
&device,
- &mut staging_belt,
+ &queue,
&mut encoder,
+ None,
&view,
primitive,
&viewport,
@@ -257,7 +259,6 @@ pub fn main() {
});
// Then we submit the work
- staging_belt.finish();
queue.submit(Some(encoder.finish()));
frame.present();
@@ -267,10 +268,6 @@ pub fn main() {
state.mouse_interaction(),
),
);
-
- // And recall staging buffers
- staging_belt.recall();
-
}
Err(error) => match error {
wgpu::SurfaceError::OutOfMemory => {
diff --git a/examples/integration_wgpu/src/scene.rs b/examples/integration/src/scene.rs
index 3e41fbda..3e41fbda 100644
--- a/examples/integration_wgpu/src/scene.rs
+++ b/examples/integration/src/scene.rs
diff --git a/examples/integration_wgpu/src/shader/frag.wgsl b/examples/integration/src/shader/frag.wgsl
index cf27bb56..cf27bb56 100644
--- a/examples/integration_wgpu/src/shader/frag.wgsl
+++ b/examples/integration/src/shader/frag.wgsl
diff --git a/examples/integration_wgpu/src/shader/vert.wgsl b/examples/integration/src/shader/vert.wgsl
index e353e6ba..e353e6ba 100644
--- a/examples/integration_wgpu/src/shader/vert.wgsl
+++ b/examples/integration/src/shader/vert.wgsl
diff --git a/examples/integration_opengl/Cargo.toml b/examples/integration_opengl/Cargo.toml
deleted file mode 100644
index 6dac999c..00000000
--- a/examples/integration_opengl/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "integration_opengl"
-version = "0.1.0"
-authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
-edition = "2021"
-publish = false
-
-[dependencies]
-iced_glutin = { path = "../../glutin" }
-iced_glow = { path = "../../glow" }
-iced_winit = { path = "../../winit" }
-env_logger = "0.8"
diff --git a/examples/integration_opengl/README.md b/examples/integration_opengl/README.md
deleted file mode 100644
index b7c2c074..00000000
--- a/examples/integration_opengl/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## OpenGL integration
-
-A demonstration of how to integrate Iced in an existing graphical OpenGL application.
-
-The __[`main`]__ file contains all the code of the example.
-
-<div align="center">
- <a href="https://imgbox.com/9P9ETcod" target="_blank"><img src="https://images2.imgbox.com/2a/51/9P9ETcod_o.gif" alt="image host"/></a>
-</div>
-
-You can run it with `cargo run`:
-```
-cargo run --package integration_opengl
-```
-
-[`main`]: src/main.rs
diff --git a/examples/integration_opengl/src/controls.rs b/examples/integration_opengl/src/controls.rs
deleted file mode 100644
index c3648f44..00000000
--- a/examples/integration_opengl/src/controls.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use iced_glow::Renderer;
-use iced_glutin::widget::Slider;
-use iced_glutin::widget::{Column, Row, Text};
-use iced_glutin::{Alignment, Color, Command, Element, Length, Program};
-
-pub struct Controls {
- background_color: Color,
-}
-
-#[derive(Debug, Clone)]
-pub enum Message {
- BackgroundColorChanged(Color),
-}
-
-impl Controls {
- pub fn new() -> Controls {
- Controls {
- background_color: Color::BLACK,
- }
- }
-
- pub fn background_color(&self) -> Color {
- self.background_color
- }
-}
-
-impl Program for Controls {
- type Renderer = Renderer;
- type Message = Message;
-
- fn update(&mut self, message: Message) -> Command<Message> {
- match message {
- Message::BackgroundColorChanged(color) => {
- self.background_color = color;
- }
- }
-
- Command::none()
- }
-
- fn view(&self) -> Element<Message, Renderer> {
- let background_color = self.background_color;
-
- let sliders = Row::new()
- .width(500)
- .spacing(20)
- .push(
- Slider::new(0.0..=1.0, background_color.r, move |r| {
- Message::BackgroundColorChanged(Color {
- r,
- ..background_color
- })
- })
- .step(0.01),
- )
- .push(
- Slider::new(0.0..=1.0, background_color.g, move |g| {
- Message::BackgroundColorChanged(Color {
- g,
- ..background_color
- })
- })
- .step(0.01),
- )
- .push(
- Slider::new(0.0..=1.0, background_color.b, move |b| {
- Message::BackgroundColorChanged(Color {
- b,
- ..background_color
- })
- })
- .step(0.01),
- );
-
- Row::new()
- .width(Length::Fill)
- .height(Length::Fill)
- .align_items(Alignment::End)
- .push(
- Column::new()
- .width(Length::Fill)
- .align_items(Alignment::End)
- .push(
- Column::new()
- .padding(10)
- .spacing(10)
- .push(
- Text::new("Background color")
- .style(Color::WHITE),
- )
- .push(sliders)
- .push(
- Text::new(format!("{background_color:?}"))
- .size(14)
- .style(Color::WHITE),
- ),
- ),
- )
- .into()
- }
-}
diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs
deleted file mode 100644
index f161c8a0..00000000
--- a/examples/integration_opengl/src/main.rs
+++ /dev/null
@@ -1,187 +0,0 @@
-mod controls;
-mod scene;
-
-use controls::Controls;
-use scene::Scene;
-
-use glow::*;
-use glutin::dpi::PhysicalPosition;
-use glutin::event::{Event, ModifiersState, WindowEvent};
-use glutin::event_loop::ControlFlow;
-use iced_glow::glow;
-use iced_glow::{Backend, Renderer, Settings, Viewport};
-use iced_glutin::conversion;
-use iced_glutin::glutin;
-use iced_glutin::renderer;
-use iced_glutin::{program, Clipboard, Color, Debug, Size};
-
-pub fn main() {
- env_logger::init();
- let (gl, event_loop, windowed_context, shader_version) = {
- let el = glutin::event_loop::EventLoop::new();
-
- let wb = glutin::window::WindowBuilder::new()
- .with_title("OpenGL integration example")
- .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0));
-
- let windowed_context = glutin::ContextBuilder::new()
- .with_vsync(true)
- .build_windowed(wb, &el)
- .unwrap();
-
- unsafe {
- let windowed_context = windowed_context.make_current().unwrap();
-
- let gl = glow::Context::from_loader_function(|s| {
- windowed_context.get_proc_address(s) as *const _
- });
-
- // Enable auto-conversion from/to sRGB
- gl.enable(glow::FRAMEBUFFER_SRGB);
-
- // Enable alpha blending
- gl.enable(glow::BLEND);
- gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
-
- // Disable multisampling by default
- gl.disable(glow::MULTISAMPLE);
-
- (gl, el, windowed_context, "#version 410")
- }
- };
-
- let physical_size = windowed_context.window().inner_size();
- let mut viewport = Viewport::with_physical_size(
- Size::new(physical_size.width, physical_size.height),
- windowed_context.window().scale_factor(),
- );
-
- let mut cursor_position = PhysicalPosition::new(-1.0, -1.0);
- let mut modifiers = ModifiersState::default();
- let mut clipboard = Clipboard::connect(windowed_context.window());
-
- let mut renderer = Renderer::new(Backend::new(&gl, Settings::default()));
-
- let mut debug = Debug::new();
-
- let controls = Controls::new();
- let mut state = program::State::new(
- controls,
- viewport.logical_size(),
- &mut renderer,
- &mut debug,
- );
- let mut resized = false;
-
- let scene = Scene::new(&gl, shader_version);
-
- event_loop.run(move |event, _, control_flow| {
- *control_flow = ControlFlow::Wait;
-
- match event {
- Event::WindowEvent { event, .. } => {
- match event {
- WindowEvent::CursorMoved { position, .. } => {
- cursor_position = position;
- }
- WindowEvent::ModifiersChanged(new_modifiers) => {
- modifiers = new_modifiers;
- }
- WindowEvent::Resized(physical_size) => {
- viewport = Viewport::with_physical_size(
- Size::new(
- physical_size.width,
- physical_size.height,
- ),
- windowed_context.window().scale_factor(),
- );
-
- resized = true;
- }
- WindowEvent::CloseRequested => {
- scene.cleanup(&gl);
- *control_flow = ControlFlow::Exit
- }
- _ => (),
- }
-
- // Map window event to iced event
- if let Some(event) = iced_winit::conversion::window_event(
- &event,
- windowed_context.window().scale_factor(),
- modifiers,
- ) {
- state.queue_event(event);
- }
- }
- Event::MainEventsCleared => {
- // If there are events pending
- if !state.is_queue_empty() {
- // We update iced
- let _ = state.update(
- viewport.logical_size(),
- conversion::cursor_position(
- cursor_position,
- viewport.scale_factor(),
- ),
- &mut renderer,
- &iced_glow::Theme::Dark,
- &renderer::Style {
- text_color: Color::WHITE,
- },
- &mut clipboard,
- &mut debug,
- );
-
- // and request a redraw
- windowed_context.window().request_redraw();
- }
- }
- Event::RedrawRequested(_) => {
- if resized {
- let size = windowed_context.window().inner_size();
-
- unsafe {
- gl.viewport(
- 0,
- 0,
- size.width as i32,
- size.height as i32,
- );
- }
-
- resized = false;
- }
-
- let program = state.program();
- {
- // We clear the frame
- scene.clear(&gl, program.background_color());
-
- // Draw the scene
- scene.draw(&gl);
- }
-
- // And then iced on top
- renderer.with_primitives(|backend, primitive| {
- backend.present(
- &gl,
- primitive,
- &viewport,
- &debug.overlay(),
- );
- });
-
- // Update the mouse cursor
- windowed_context.window().set_cursor_icon(
- iced_winit::conversion::mouse_interaction(
- state.mouse_interaction(),
- ),
- );
-
- windowed_context.swap_buffers().unwrap();
- }
- _ => (),
- }
- });
-}
diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs
deleted file mode 100644
index c1d05b65..00000000
--- a/examples/integration_opengl/src/scene.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use glow::*;
-use iced_glow::glow;
-use iced_glow::Color;
-
-pub struct Scene {
- program: glow::Program,
- vertex_array: glow::VertexArray,
-}
-
-impl Scene {
- pub fn new(gl: &glow::Context, shader_version: &str) -> Self {
- unsafe {
- let vertex_array = gl
- .create_vertex_array()
- .expect("Cannot create vertex array");
- gl.bind_vertex_array(Some(vertex_array));
-
- let program = gl.create_program().expect("Cannot create program");
-
- let (vertex_shader_source, fragment_shader_source) = (
- r#"const vec2 verts[3] = vec2[3](
- vec2(0.5f, 1.0f),
- vec2(0.0f, 0.0f),
- vec2(1.0f, 0.0f)
- );
- out vec2 vert;
- void main() {
- vert = verts[gl_VertexID];
- gl_Position = vec4(vert - 0.5, 0.0, 1.0);
- }"#,
- r#"precision highp float;
- in vec2 vert;
- out vec4 color;
- void main() {
- color = vec4(vert, 0.5, 1.0);
- }"#,
- );
-
- let shader_sources = [
- (glow::VERTEX_SHADER, vertex_shader_source),
- (glow::FRAGMENT_SHADER, fragment_shader_source),
- ];
-
- let mut shaders = Vec::with_capacity(shader_sources.len());
-
- for (shader_type, shader_source) in shader_sources.iter() {
- let shader = gl
- .create_shader(*shader_type)
- .expect("Cannot create shader");
- gl.shader_source(
- shader,
- &format!("{shader_version}\n{shader_source}"),
- );
- gl.compile_shader(shader);
- if !gl.get_shader_compile_status(shader) {
- panic!("{}", gl.get_shader_info_log(shader));
- }
- gl.attach_shader(program, shader);
- shaders.push(shader);
- }
-
- gl.link_program(program);
- if !gl.get_program_link_status(program) {
- panic!("{}", gl.get_program_info_log(program));
- }
-
- for shader in shaders {
- gl.detach_shader(program, shader);
- gl.delete_shader(shader);
- }
-
- gl.use_program(Some(program));
- Self {
- program,
- vertex_array,
- }
- }
- }
-
- pub fn clear(&self, gl: &glow::Context, background_color: Color) {
- let [r, g, b, a] = background_color.into_linear();
- unsafe {
- gl.clear_color(r, g, b, a);
- gl.clear(glow::COLOR_BUFFER_BIT);
- }
- }
-
- pub fn draw(&self, gl: &glow::Context) {
- unsafe {
- gl.bind_vertex_array(Some(self.vertex_array));
- gl.use_program(Some(self.program));
- gl.draw_arrays(glow::TRIANGLES, 0, 3);
- }
- }
-
- pub fn cleanup(&self, gl: &glow::Context) {
- unsafe {
- gl.delete_program(self.program);
- gl.delete_vertex_array(self.vertex_array);
- }
- }
-}
diff --git a/examples/todos/fonts/icons.ttf b/examples/todos/fonts/icons.ttf
index 4498299d..7b65fd36 100644
--- a/examples/todos/fonts/icons.ttf
+++ b/examples/todos/fonts/icons.ttf
Binary files differ
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index 6408f09c..5df4e968 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -1,5 +1,6 @@
use iced::alignment::{self, Alignment};
use iced::event::{self, Event};
+use iced::font::{self, Font};
use iced::keyboard;
use iced::subscription;
use iced::theme::{self, Theme};
@@ -9,7 +10,7 @@ use iced::widget::{
};
use iced::window;
use iced::{Application, Element};
-use iced::{Color, Command, Font, Length, Settings, Subscription};
+use iced::{Color, Command, Length, Settings, Subscription};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
@@ -44,6 +45,7 @@ struct State {
#[derive(Debug, Clone)]
enum Message {
Loaded(Result<SavedState, LoadError>),
+ FontLoaded(Result<(), font::Error>),
Saved(Result<(), SaveError>),
InputChanged(String),
CreateTask,
@@ -61,7 +63,11 @@ impl Application for Todos {
fn new(_flags: ()) -> (Todos, Command<Message>) {
(
Todos::Loading,
- Command::perform(SavedState::load(), Message::Loaded),
+ Command::batch(vec![
+ font::load(include_bytes!("../fonts/icons.ttf").as_slice())
+ .map(Message::FontLoaded),
+ Command::perform(SavedState::load(), Message::Loaded),
+ ]),
)
}
@@ -384,7 +390,7 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element<Message> {
let tasks_left = tasks.iter().filter(|task| !task.completed).count();
let filter_button = |label, filter, current_filter| {
- let label = text(label).size(16);
+ let label = text(label);
let button = button(label).style(if filter == current_filter {
theme::Button::Primary
@@ -401,8 +407,7 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element<Message> {
tasks_left,
if tasks_left == 1 { "task" } else { "tasks" }
))
- .width(Length::Fill)
- .size(16),
+ .width(Length::Fill),
row![
filter_button("All", Filter::All, current_filter),
filter_button("Active", Filter::Active, current_filter),
@@ -466,10 +471,7 @@ fn empty_message(message: &str) -> Element<'_, Message> {
}
// Fonts
-const ICONS: Font = Font::External {
- name: "Icons",
- bytes: include_bytes!("../../todos/fonts/icons.ttf"),
-};
+const ICONS: Font = Font::Name("Iced-Todos-Icons");
fn icon(unicode: char) -> Text<'static> {
text(unicode.to_string())
diff --git a/glow/Cargo.toml b/glow/Cargo.toml
deleted file mode 100644
index 1a848ab7..00000000
--- a/glow/Cargo.toml
+++ /dev/null
@@ -1,51 +0,0 @@
-[package]
-name = "iced_glow"
-version = "0.7.0"
-authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
-edition = "2021"
-description = "A glow renderer for iced"
-license = "MIT AND OFL-1.1"
-repository = "https://github.com/iced-rs/iced"
-
-[features]
-svg = ["iced_graphics/svg"]
-image = ["iced_graphics/image"]
-png = ["iced_graphics/png"]
-jpeg = ["iced_graphics/jpeg"]
-jpeg_rayon = ["iced_graphics/jpeg_rayon"]
-gif = ["iced_graphics/gif"]
-webp = ["iced_graphics/webp"]
-pnm = ["iced_graphics/pnm"]
-ico = ["iced_graphics/ico"]
-bmp = ["iced_graphics/bmp"]
-hdr = ["iced_graphics/hdr"]
-dds = ["iced_graphics/dds"]
-farbfeld = ["iced_graphics/farbfeld"]
-canvas = ["iced_graphics/canvas"]
-qr_code = ["iced_graphics/qr_code"]
-default_system_font = ["iced_graphics/font-source"]
-
-[dependencies]
-glow = "0.11.1"
-glow_glyph = "0.5.0"
-glyph_brush = "0.7"
-euclid = "0.22"
-bytemuck = "1.4"
-log = "0.4"
-
-[dependencies.iced_native]
-version = "0.9"
-path = "../native"
-
-[dependencies.iced_graphics]
-version = "0.7"
-path = "../graphics"
-features = ["font-fallback", "font-icons", "opengl"]
-
-[dependencies.tracing]
-version = "0.1.6"
-optional = true
-
-[package.metadata.docs.rs]
-rustdoc-args = ["--cfg", "docsrs"]
-all-features = true
diff --git a/glow/README.md b/glow/README.md
deleted file mode 100644
index 95c9d62a..00000000
--- a/glow/README.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# `iced_glow`
-[![Documentation](https://docs.rs/iced_glow/badge.svg)][documentation]
-[![Crates.io](https://img.shields.io/crates/v/iced_glow.svg)](https://crates.io/crates/iced_glow)
-[![License](https://img.shields.io/crates/l/iced_glow.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
-[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
-
-`iced_glow` is a [`glow`] renderer for [`iced_native`]. This renderer supports OpenGL 3.0+ and OpenGL ES 2.0.
-
-This renderer is mostly used as a fallback for hardware that doesn't support [`wgpu`] (Vulkan, Metal or DX12).
-
-Currently, `iced_glow` supports the following primitives:
-- Text, which is rendered using [`glow_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.
-- Meshes of triangles, useful to draw geometry freely.
-
-<p align="center">
- <img alt="The native target" src="../docs/graphs/native.png" width="80%">
-</p>
-
-[documentation]: https://docs.rs/iced_glow
-[`iced_native`]: ../native
-[`glow`]: https://github.com/grovesNL/glow
-[`wgpu`]: https://github.com/gfx-rs/wgpu
-[`glow_glyph`]: https://github.com/hecrj/glow_glyph
-
-## Installation
-Add `iced_glow` as a dependency in your `Cargo.toml`:
-
-```toml
-iced_glow = "0.7"
-```
-
-__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
diff --git a/glow/src/backend.rs b/glow/src/backend.rs
deleted file mode 100644
index 36a34eda..00000000
--- a/glow/src/backend.rs
+++ /dev/null
@@ -1,280 +0,0 @@
-#[cfg(any(feature = "image", feature = "svg"))]
-use crate::image;
-use crate::quad;
-use crate::text;
-use crate::{program, triangle};
-use crate::{Settings, Transformation, Viewport};
-
-use iced_graphics::backend;
-use iced_graphics::font;
-use iced_graphics::{Layer, Primitive};
-use iced_native::alignment;
-use iced_native::{Font, Size};
-
-/// A [`glow`] graphics backend for [`iced`].
-///
-/// [`glow`]: https://github.com/grovesNL/glow
-/// [`iced`]: https://github.com/iced-rs/iced
-#[derive(Debug)]
-pub struct Backend {
- #[cfg(any(feature = "image", feature = "svg"))]
- image_pipeline: image::Pipeline,
- quad_pipeline: quad::Pipeline,
- text_pipeline: text::Pipeline,
- triangle_pipeline: triangle::Pipeline,
- default_text_size: f32,
-}
-
-impl Backend {
- /// Creates a new [`Backend`].
- pub fn new(gl: &glow::Context, settings: Settings) -> Self {
- let text_pipeline = text::Pipeline::new(
- gl,
- settings.default_font,
- settings.text_multithreading,
- );
-
- let shader_version = program::Version::new(gl);
-
- #[cfg(any(feature = "image", feature = "svg"))]
- let image_pipeline = image::Pipeline::new(gl, &shader_version);
- let quad_pipeline = quad::Pipeline::new(gl, &shader_version);
- let triangle_pipeline = triangle::Pipeline::new(gl, &shader_version);
-
- Self {
- #[cfg(any(feature = "image", feature = "svg"))]
- image_pipeline,
- quad_pipeline,
- text_pipeline,
- triangle_pipeline,
- default_text_size: settings.default_text_size,
- }
- }
-
- /// Draws the provided primitives in the default framebuffer.
- ///
- /// The text provided as overlay will be rendered on top of the primitives.
- /// This is useful for rendering debug information.
- pub fn present<T: AsRef<str>>(
- &mut self,
- gl: &glow::Context,
- primitives: &[Primitive],
- viewport: &Viewport,
- overlay_text: &[T],
- ) {
- let viewport_size = viewport.physical_size();
- let scale_factor = viewport.scale_factor() as f32;
- let projection = viewport.projection();
-
- let mut layers = Layer::generate(primitives, viewport);
- layers.push(Layer::overlay(overlay_text, viewport));
-
- for layer in layers {
- self.flush(
- gl,
- scale_factor,
- projection,
- &layer,
- viewport_size.height,
- );
- }
-
- #[cfg(any(feature = "image", feature = "svg"))]
- self.image_pipeline.trim_cache(gl);
- }
-
- fn flush(
- &mut self,
- gl: &glow::Context,
- scale_factor: f32,
- transformation: Transformation,
- layer: &Layer<'_>,
- target_height: u32,
- ) {
- let mut bounds = (layer.bounds * scale_factor).snap();
-
- if bounds.width < 1 || bounds.height < 1 {
- return;
- }
-
- bounds.height = bounds.height.min(target_height);
-
- if !layer.quads.is_empty() {
- self.quad_pipeline.draw(
- gl,
- target_height,
- &layer.quads,
- transformation,
- scale_factor,
- bounds,
- );
- }
-
- if !layer.meshes.is_empty() {
- let scaled = transformation
- * Transformation::scale(scale_factor, scale_factor);
-
- self.triangle_pipeline.draw(
- &layer.meshes,
- gl,
- target_height,
- scaled,
- scale_factor,
- );
- }
-
- #[cfg(any(feature = "image", feature = "svg"))]
- if !layer.images.is_empty() {
- let scaled = transformation
- * Transformation::scale(scale_factor, scale_factor);
-
- self.image_pipeline.draw(
- gl,
- target_height,
- scaled,
- scale_factor,
- &layer.images,
- bounds,
- );
- }
-
- if !layer.text.is_empty() {
- for text in layer.text.iter() {
- // Target physical coordinates directly to avoid blurry text
- let text = glow_glyph::Section {
- // TODO: We `round` here to avoid rerasterizing text when
- // its position changes slightly. This can make text feel a
- // bit "jumpy". We may be able to do better once we improve
- // our text rendering/caching pipeline.
- screen_position: (
- (text.bounds.x * scale_factor).round(),
- (text.bounds.y * scale_factor).round(),
- ),
- // TODO: Fix precision issues with some scale factors.
- //
- // The `ceil` here can cause some words to render on the
- // same line when they should not.
- //
- // Ideally, `wgpu_glyph` should be able to compute layout
- // using logical positions, and then apply the proper
- // scaling when rendering. This would ensure that both
- // measuring and rendering follow the same layout rules.
- bounds: (
- (text.bounds.width * scale_factor).ceil(),
- (text.bounds.height * scale_factor).ceil(),
- ),
- text: vec![glow_glyph::Text {
- text: text.content,
- scale: glow_glyph::ab_glyph::PxScale {
- x: text.size * scale_factor,
- y: text.size * scale_factor,
- },
- font_id: self.text_pipeline.find_font(text.font),
- extra: glow_glyph::Extra {
- color: text.color,
- z: 0.0,
- },
- }],
- layout: glow_glyph::Layout::default()
- .h_align(match text.horizontal_alignment {
- alignment::Horizontal::Left => {
- glow_glyph::HorizontalAlign::Left
- }
- alignment::Horizontal::Center => {
- glow_glyph::HorizontalAlign::Center
- }
- alignment::Horizontal::Right => {
- glow_glyph::HorizontalAlign::Right
- }
- })
- .v_align(match text.vertical_alignment {
- alignment::Vertical::Top => {
- glow_glyph::VerticalAlign::Top
- }
- alignment::Vertical::Center => {
- glow_glyph::VerticalAlign::Center
- }
- alignment::Vertical::Bottom => {
- glow_glyph::VerticalAlign::Bottom
- }
- }),
- };
-
- self.text_pipeline.queue(text);
- }
-
- self.text_pipeline.draw_queued(
- gl,
- transformation,
- glow_glyph::Region {
- x: bounds.x,
- y: target_height - (bounds.y + bounds.height),
- width: bounds.width,
- height: bounds.height,
- },
- );
- }
- }
-}
-
-impl iced_graphics::Backend for Backend {
- fn trim_measurements(&mut self) {
- self.text_pipeline.trim_measurement_cache()
- }
-}
-
-impl backend::Text for Backend {
- const ICON_FONT: Font = font::ICONS;
- const CHECKMARK_ICON: char = font::CHECKMARK_ICON;
- const ARROW_DOWN_ICON: char = font::ARROW_DOWN_ICON;
-
- fn default_size(&self) -> f32 {
- self.default_text_size
- }
-
- fn measure(
- &self,
- contents: &str,
- size: f32,
- font: Font,
- bounds: Size,
- ) -> (f32, f32) {
- self.text_pipeline.measure(contents, size, font, bounds)
- }
-
- fn hit_test(
- &self,
- contents: &str,
- size: f32,
- font: Font,
- bounds: Size,
- point: iced_native::Point,
- nearest_only: bool,
- ) -> Option<text::Hit> {
- self.text_pipeline.hit_test(
- contents,
- size,
- font,
- bounds,
- point,
- nearest_only,
- )
- }
-}
-
-#[cfg(feature = "image")]
-impl backend::Image for Backend {
- fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> {
- self.image_pipeline.dimensions(handle)
- }
-}
-
-#[cfg(feature = "svg")]
-impl backend::Svg for Backend {
- fn viewport_dimensions(
- &self,
- handle: &iced_native::svg::Handle,
- ) -> Size<u32> {
- self.image_pipeline.viewport_dimensions(handle)
- }
-}
diff --git a/glow/src/image.rs b/glow/src/image.rs
deleted file mode 100644
index d3a25b5b..00000000
--- a/glow/src/image.rs
+++ /dev/null
@@ -1,254 +0,0 @@
-mod storage;
-
-use storage::Storage;
-
-pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
-
-use crate::program::{self, Shader};
-use crate::Transformation;
-
-#[cfg(feature = "image")]
-use iced_graphics::image::raster;
-
-#[cfg(feature = "svg")]
-use iced_graphics::image::vector;
-
-use iced_graphics::layer;
-use iced_graphics::Rectangle;
-use iced_graphics::Size;
-
-use glow::HasContext;
-
-use std::cell::RefCell;
-
-#[cfg(feature = "tracing")]
-use tracing::info_span;
-
-#[derive(Debug)]
-pub(crate) struct Pipeline {
- program: <glow::Context as HasContext>::Program,
- vertex_array: <glow::Context as HasContext>::VertexArray,
- vertex_buffer: <glow::Context as HasContext>::Buffer,
- transform_location: <glow::Context as HasContext>::UniformLocation,
- storage: Storage,
- #[cfg(feature = "image")]
- raster_cache: RefCell<raster::Cache<Storage>>,
- #[cfg(feature = "svg")]
- vector_cache: RefCell<vector::Cache<Storage>>,
-}
-
-impl Pipeline {
- pub fn new(
- gl: &glow::Context,
- shader_version: &program::Version,
- ) -> Pipeline {
- let program = unsafe {
- let vertex_shader = Shader::vertex(
- gl,
- shader_version,
- include_str!("shader/common/image.vert"),
- );
- let fragment_shader = Shader::fragment(
- gl,
- shader_version,
- include_str!("shader/common/image.frag"),
- );
-
- program::create(
- gl,
- &[vertex_shader, fragment_shader],
- &[(0, "i_Position")],
- )
- };
-
- let transform_location =
- unsafe { gl.get_uniform_location(program, "u_Transform") }
- .expect("Get transform location");
-
- unsafe {
- gl.use_program(Some(program));
-
- let transform: [f32; 16] = Transformation::identity().into();
- gl.uniform_matrix_4_f32_slice(
- Some(&transform_location),
- false,
- &transform,
- );
-
- gl.use_program(None);
- }
-
- let vertex_buffer =
- unsafe { gl.create_buffer().expect("Create vertex buffer") };
- let vertex_array =
- unsafe { gl.create_vertex_array().expect("Create vertex array") };
-
- unsafe {
- gl.bind_vertex_array(Some(vertex_array));
- gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
-
- let vertices = &[0u8, 0, 1, 0, 0, 1, 1, 1];
- gl.buffer_data_size(
- glow::ARRAY_BUFFER,
- vertices.len() as i32,
- glow::STATIC_DRAW,
- );
- gl.buffer_sub_data_u8_slice(
- glow::ARRAY_BUFFER,
- 0,
- bytemuck::cast_slice(vertices),
- );
-
- gl.enable_vertex_attrib_array(0);
- gl.vertex_attrib_pointer_f32(
- 0,
- 2,
- glow::UNSIGNED_BYTE,
- false,
- 0,
- 0,
- );
-
- gl.bind_buffer(glow::ARRAY_BUFFER, None);
- gl.bind_vertex_array(None);
- }
-
- Pipeline {
- program,
- vertex_array,
- vertex_buffer,
- transform_location,
- storage: Storage::default(),
- #[cfg(feature = "image")]
- raster_cache: RefCell::new(raster::Cache::default()),
- #[cfg(feature = "svg")]
- vector_cache: RefCell::new(vector::Cache::default()),
- }
- }
-
- #[cfg(feature = "image")]
- pub fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> {
- self.raster_cache.borrow_mut().load(handle).dimensions()
- }
-
- #[cfg(feature = "svg")]
- pub fn viewport_dimensions(
- &self,
- handle: &iced_native::svg::Handle,
- ) -> Size<u32> {
- let mut cache = self.vector_cache.borrow_mut();
- let svg = cache.load(handle);
-
- svg.viewport_dimensions()
- }
-
- pub fn draw(
- &mut self,
- mut gl: &glow::Context,
- target_height: u32,
- transformation: Transformation,
- _scale_factor: f32,
- images: &[layer::Image],
- layer_bounds: Rectangle<u32>,
- ) {
- #[cfg(feature = "tracing")]
- let _ = info_span!("Glow::Image", "DRAW").entered();
-
- unsafe {
- gl.use_program(Some(self.program));
- gl.bind_vertex_array(Some(self.vertex_array));
- gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
- gl.enable(glow::SCISSOR_TEST);
- }
-
- #[cfg(feature = "image")]
- let mut raster_cache = self.raster_cache.borrow_mut();
-
- #[cfg(feature = "svg")]
- let mut vector_cache = self.vector_cache.borrow_mut();
-
- for image in images {
- let (entry, bounds) = match &image {
- #[cfg(feature = "image")]
- layer::Image::Raster { handle, bounds } => (
- raster_cache.upload(handle, &mut gl, &mut self.storage),
- bounds,
- ),
- #[cfg(not(feature = "image"))]
- layer::Image::Raster { handle: _, bounds } => (None, bounds),
-
- #[cfg(feature = "svg")]
- layer::Image::Vector {
- handle,
- color,
- bounds,
- } => {
- let size = [bounds.width, bounds.height];
- (
- vector_cache.upload(
- handle,
- *color,
- size,
- _scale_factor,
- &mut gl,
- &mut self.storage,
- ),
- bounds,
- )
- }
-
- #[cfg(not(feature = "svg"))]
- layer::Image::Vector { bounds, .. } => (None, bounds),
- };
-
- unsafe {
- gl.scissor(
- layer_bounds.x as i32,
- (target_height - (layer_bounds.y + layer_bounds.height))
- as i32,
- layer_bounds.width as i32,
- layer_bounds.height as i32,
- );
-
- if let Some(storage::Entry { texture, .. }) = entry {
- gl.bind_texture(glow::TEXTURE_2D, Some(*texture))
- } else {
- continue;
- }
-
- let translate = Transformation::translate(bounds.x, bounds.y);
- let scale = Transformation::scale(bounds.width, bounds.height);
- let transformation = transformation * translate * scale;
- let matrix: [f32; 16] = transformation.into();
- gl.uniform_matrix_4_f32_slice(
- Some(&self.transform_location),
- false,
- &matrix,
- );
-
- gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
-
- gl.bind_texture(glow::TEXTURE_2D, None);
- }
- }
-
- unsafe {
- gl.bind_buffer(glow::ARRAY_BUFFER, None);
- gl.bind_vertex_array(None);
- gl.use_program(None);
- gl.disable(glow::SCISSOR_TEST);
- }
- }
-
- pub fn trim_cache(&mut self, mut gl: &glow::Context) {
- #[cfg(feature = "image")]
- self.raster_cache
- .borrow_mut()
- .trim(&mut self.storage, &mut gl);
-
- #[cfg(feature = "svg")]
- self.vector_cache
- .borrow_mut()
- .trim(&mut self.storage, &mut gl);
- }
-}
diff --git a/glow/src/image/storage.rs b/glow/src/image/storage.rs
deleted file mode 100644
index 9bc20641..00000000
--- a/glow/src/image/storage.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use iced_graphics::image;
-use iced_graphics::Size;
-
-use glow::HasContext;
-
-#[derive(Debug, Default)]
-pub struct Storage;
-
-impl image::Storage for Storage {
- type Entry = Entry;
- type State<'a> = &'a glow::Context;
-
- fn upload(
- &mut self,
- width: u32,
- height: u32,
- data: &[u8],
- gl: &mut &glow::Context,
- ) -> Option<Self::Entry> {
- unsafe {
- let texture = gl.create_texture().expect("create texture");
- gl.bind_texture(glow::TEXTURE_2D, Some(texture));
- gl.tex_image_2d(
- glow::TEXTURE_2D,
- 0,
- glow::SRGB8_ALPHA8 as i32,
- width as i32,
- height as i32,
- 0,
- glow::RGBA,
- glow::UNSIGNED_BYTE,
- Some(data),
- );
- gl.tex_parameter_i32(
- glow::TEXTURE_2D,
- glow::TEXTURE_WRAP_S,
- glow::CLAMP_TO_EDGE as _,
- );
- gl.tex_parameter_i32(
- glow::TEXTURE_2D,
- glow::TEXTURE_WRAP_T,
- glow::CLAMP_TO_EDGE as _,
- );
- gl.tex_parameter_i32(
- glow::TEXTURE_2D,
- glow::TEXTURE_MIN_FILTER,
- glow::LINEAR as _,
- );
- gl.tex_parameter_i32(
- glow::TEXTURE_2D,
- glow::TEXTURE_MAG_FILTER,
- glow::LINEAR as _,
- );
- gl.bind_texture(glow::TEXTURE_2D, None);
-
- Some(Entry {
- size: Size::new(width, height),
- texture,
- })
- }
- }
-
- fn remove(&mut self, entry: &Entry, gl: &mut &glow::Context) {
- unsafe { gl.delete_texture(entry.texture) }
- }
-}
-
-#[derive(Debug)]
-pub struct Entry {
- size: Size<u32>,
- pub(super) texture: glow::NativeTexture,
-}
-
-impl image::storage::Entry for Entry {
- fn size(&self) -> Size<u32> {
- self.size
- }
-}
diff --git a/glow/src/lib.rs b/glow/src/lib.rs
deleted file mode 100644
index 9e7de0d9..00000000
--- a/glow/src/lib.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-//! A [`glow`] renderer for [`iced_native`].
-//!
-//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
-//!
-//! [`glow`]: https://github.com/grovesNL/glow
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
-)]
-#![deny(
- missing_debug_implementations,
- missing_docs,
- unused_results,
- clippy::extra_unused_lifetimes,
- clippy::from_over_into,
- clippy::needless_borrow,
- clippy::new_without_default,
- clippy::useless_conversion
-)]
-#![forbid(rust_2018_idioms)]
-#![allow(clippy::inherent_to_string, clippy::type_complexity)]
-#![cfg_attr(docsrs, feature(doc_cfg))]
-
-pub use glow;
-
-mod backend;
-#[cfg(any(feature = "image", feature = "svg"))]
-mod image;
-mod program;
-mod quad;
-mod text;
-mod triangle;
-
-pub mod settings;
-pub mod window;
-
-pub use backend::Backend;
-pub use settings::Settings;
-
-pub(crate) use iced_graphics::Transformation;
-
-pub use iced_graphics::{Error, Viewport};
-pub use iced_native::Theme;
-
-pub use iced_native::alignment;
-pub use iced_native::{Alignment, Background, Color, Command, Length, Vector};
-
-/// A [`glow`] graphics renderer for [`iced`].
-///
-/// [`glow`]: https://github.com/grovesNL/glow
-/// [`iced`]: https://github.com/iced-rs/iced
-pub type Renderer<Theme = iced_native::Theme> =
- iced_graphics::Renderer<Backend, Theme>;
diff --git a/glow/src/program.rs b/glow/src/program.rs
deleted file mode 100644
index 95437fcd..00000000
--- a/glow/src/program.rs
+++ /dev/null
@@ -1,133 +0,0 @@
-use glow::HasContext;
-
-/// The [`Version`] of a `Program`.
-pub struct Version {
- vertex: String,
- fragment: String,
-}
-
-impl Version {
- pub fn new(gl: &glow::Context) -> Version {
- let version = gl.version();
-
- let (vertex, fragment) = match (
- version.major,
- version.minor,
- version.is_embedded,
- ) {
- // OpenGL 3.0+
- (3, 0 | 1 | 2, false) => (
- format!("#version 1{}0\n#extension GL_ARB_explicit_attrib_location : enable", version.minor + 3),
- format!(
- "#version 1{}0\n#extension GL_ARB_explicit_attrib_location : enable\n#define HIGHER_THAN_300 1",
- version.minor + 3
- ),
- ),
- // OpenGL 3.3+
- (3 | 4, _, false) => (
- format!("#version {}{}0\n#extension GL_ARB_explicit_attrib_location : enable", version.major, version.minor),
- format!(
- "#version {}{}0\n#extension GL_ARB_explicit_attrib_location : enable\n#define HIGHER_THAN_300 1",
- version.major, version.minor
- ),
- ),
- // OpenGL ES 3.0+
- (3, _, true) => (
- format!("#version 3{}0 es", version.minor),
- format!(
- "#version 3{}0 es\n#define HIGHER_THAN_300 1",
- version.minor
- ),
- ),
- // OpenGL ES 2.0+
- (2, _, true) => (
- String::from(
- "#version 100\n#define in attribute\n#define out varying",
- ),
- String::from("#version 100\n#define in varying"),
- ),
- // OpenGL 2.1
- (2, _, false) => (
- String::from(
- "#version 120\n#define in attribute\n#define out varying",
- ),
- String::from("#version 120\n#define in varying"),
- ),
- // OpenGL 1.1+
- _ => panic!("Incompatible context version: {version:?}"),
- };
-
- log::info!("Shader directive: {}", vertex.lines().next().unwrap());
-
- Version { vertex, fragment }
- }
-}
-
-pub struct Shader(<glow::Context as HasContext>::Shader);
-
-impl Shader {
- fn compile(gl: &glow::Context, stage: u32, content: &str) -> Shader {
- unsafe {
- let shader = gl.create_shader(stage).expect("Cannot create shader");
-
- gl.shader_source(shader, content);
- gl.compile_shader(shader);
-
- if !gl.get_shader_compile_status(shader) {
- panic!("{}", gl.get_shader_info_log(shader));
- }
-
- Shader(shader)
- }
- }
-
- /// Creates a vertex [`Shader`].
- pub fn vertex(
- gl: &glow::Context,
- version: &Version,
- content: &'static str,
- ) -> Self {
- let content = format!("{}\n{}", version.vertex, content);
-
- Shader::compile(gl, glow::VERTEX_SHADER, &content)
- }
-
- /// Creates a fragment [`Shader`].
- pub fn fragment(
- gl: &glow::Context,
- version: &Version,
- content: &'static str,
- ) -> Self {
- let content = format!("{}\n{}", version.fragment, content);
-
- Shader::compile(gl, glow::FRAGMENT_SHADER, &content)
- }
-}
-
-pub unsafe fn create(
- gl: &glow::Context,
- shaders: &[Shader],
- attributes: &[(u32, &str)],
-) -> <glow::Context as HasContext>::Program {
- let program = gl.create_program().expect("Cannot create program");
-
- for shader in shaders {
- gl.attach_shader(program, shader.0);
- }
-
- for (i, name) in attributes {
- gl.bind_attrib_location(program, *i, name);
- }
-
- gl.link_program(program);
- if !gl.get_program_link_status(program) {
- panic!("{}", gl.get_program_info_log(program));
- }
-
- for shader in shaders {
- gl.detach_shader(program, shader.0);
- gl.delete_shader(shader.0);
- }
-
- program
-}
diff --git a/glow/src/quad.rs b/glow/src/quad.rs
deleted file mode 100644
index 67d9a098..00000000
--- a/glow/src/quad.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-mod compatibility;
-mod core;
-
-use crate::program;
-use crate::Transformation;
-use glow::HasContext;
-use iced_graphics::layer;
-use iced_native::Rectangle;
-
-#[cfg(feature = "tracing")]
-use tracing::info_span;
-
-#[derive(Debug)]
-pub enum Pipeline {
- Core(core::Pipeline),
- Compatibility(compatibility::Pipeline),
-}
-
-impl Pipeline {
- pub fn new(
- gl: &glow::Context,
- shader_version: &program::Version,
- ) -> Pipeline {
- let gl_version = gl.version();
-
- // OpenGL 3.0+ and OpenGL ES 3.0+ have instancing (which is what separates `core` from `compatibility`)
- if gl_version.major >= 3 {
- log::info!("Mode: core");
- Pipeline::Core(core::Pipeline::new(gl, shader_version))
- } else {
- log::info!("Mode: compatibility");
- Pipeline::Compatibility(compatibility::Pipeline::new(
- gl,
- shader_version,
- ))
- }
- }
-
- pub fn draw(
- &mut self,
- gl: &glow::Context,
- target_height: u32,
- instances: &[layer::Quad],
- transformation: Transformation,
- scale: f32,
- bounds: Rectangle<u32>,
- ) {
- #[cfg(feature = "tracing")]
- let _ = info_span!("Glow::Quad", "DRAW").enter();
-
- match self {
- Pipeline::Core(pipeline) => {
- pipeline.draw(
- gl,
- target_height,
- instances,
- transformation,
- scale,
- bounds,
- );
- }
- Pipeline::Compatibility(pipeline) => {
- pipeline.draw(
- gl,
- target_height,
- instances,
- transformation,
- scale,
- bounds,
- );
- }
- }
- }
-}
diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs
deleted file mode 100644
index e909162c..00000000
--- a/glow/src/quad/compatibility.rs
+++ /dev/null
@@ -1,349 +0,0 @@
-use crate::program::{self, Shader};
-use crate::Transformation;
-use glow::HasContext;
-use iced_graphics::layer;
-use iced_native::Rectangle;
-
-// Only change `MAX_QUADS`, otherwise you could cause problems
-// by splitting a triangle into different render passes.
-const MAX_QUADS: usize = 100_000;
-const MAX_VERTICES: usize = MAX_QUADS * 4;
-const MAX_INDICES: usize = MAX_QUADS * 6;
-
-#[derive(Debug)]
-pub struct Pipeline {
- program: <glow::Context as HasContext>::Program,
- vertex_array: <glow::Context as HasContext>::VertexArray,
- vertex_buffer: <glow::Context as HasContext>::Buffer,
- index_buffer: <glow::Context as HasContext>::Buffer,
- transform_location: <glow::Context as HasContext>::UniformLocation,
- scale_location: <glow::Context as HasContext>::UniformLocation,
- screen_height_location: <glow::Context as HasContext>::UniformLocation,
- current_transform: Transformation,
- current_scale: f32,
- current_target_height: u32,
-}
-
-impl Pipeline {
- pub fn new(
- gl: &glow::Context,
- shader_version: &program::Version,
- ) -> Pipeline {
- let program = unsafe {
- let vertex_shader = Shader::vertex(
- gl,
- shader_version,
- include_str!("../shader/compatibility/quad.vert"),
- );
- let fragment_shader = Shader::fragment(
- gl,
- shader_version,
- include_str!("../shader/compatibility/quad.frag"),
- );
-
- program::create(
- gl,
- &[vertex_shader, fragment_shader],
- &[
- (0, "i_Pos"),
- (1, "i_Scale"),
- (2, "i_Color"),
- (3, "i_BorderColor"),
- (4, "i_BorderRadius"),
- (5, "i_BorderWidth"),
- ],
- )
- };
-
- let transform_location =
- unsafe { gl.get_uniform_location(program, "u_Transform") }
- .expect("Get transform location");
-
- let scale_location =
- unsafe { gl.get_uniform_location(program, "u_Scale") }
- .expect("Get scale location");
-
- let screen_height_location =
- unsafe { gl.get_uniform_location(program, "u_ScreenHeight") }
- .expect("Get target height location");
-
- unsafe {
- gl.use_program(Some(program));
-
- gl.uniform_matrix_4_f32_slice(
- Some(&transform_location),
- false,
- Transformation::identity().as_ref(),
- );
-
- gl.uniform_1_f32(Some(&scale_location), 1.0);
- gl.uniform_1_f32(Some(&screen_height_location), 0.0);
-
- gl.use_program(None);
- }
-
- let (vertex_array, vertex_buffer, index_buffer) =
- unsafe { create_buffers(gl, MAX_VERTICES) };
-
- Pipeline {
- program,
- vertex_array,
- vertex_buffer,
- index_buffer,
- transform_location,
- scale_location,
- screen_height_location,
- current_transform: Transformation::identity(),
- current_scale: 1.0,
- current_target_height: 0,
- }
- }
-
- pub fn draw(
- &mut self,
- gl: &glow::Context,
- target_height: u32,
- instances: &[layer::Quad],
- transformation: Transformation,
- scale: f32,
- bounds: Rectangle<u32>,
- ) {
- // TODO: Remove this allocation (probably by changing the shader and removing the need of two `position`)
- let vertices: Vec<Vertex> =
- instances.iter().flat_map(Vertex::from_quad).collect();
-
- // TODO: Remove this allocation (or allocate only when needed)
- let indices: Vec<i32> = (0..instances.len().min(MAX_QUADS) as i32)
- .flat_map(|i| {
- [i * 4, 1 + i * 4, 2 + i * 4, 2 + i * 4, 1 + i * 4, 3 + i * 4]
- })
- .cycle()
- .take(instances.len() * 6)
- .collect();
-
- unsafe {
- gl.enable(glow::SCISSOR_TEST);
- gl.scissor(
- bounds.x as i32,
- (target_height - (bounds.y + bounds.height)) as i32,
- bounds.width as i32,
- bounds.height as i32,
- );
-
- gl.use_program(Some(self.program));
- gl.bind_vertex_array(Some(self.vertex_array));
- gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
- gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer));
- }
-
- if transformation != self.current_transform {
- unsafe {
- gl.uniform_matrix_4_f32_slice(
- Some(&self.transform_location),
- false,
- transformation.as_ref(),
- );
-
- self.current_transform = transformation;
- }
- }
-
- if scale != self.current_scale {
- unsafe {
- gl.uniform_1_f32(Some(&self.scale_location), scale);
- }
-
- self.current_scale = scale;
- }
-
- if target_height != self.current_target_height {
- unsafe {
- gl.uniform_1_f32(
- Some(&self.screen_height_location),
- target_height as f32,
- );
- }
-
- self.current_target_height = target_height;
- }
-
- let passes = vertices
- .chunks(MAX_VERTICES)
- .zip(indices.chunks(MAX_INDICES));
-
- for (vertices, indices) in passes {
- unsafe {
- gl.buffer_sub_data_u8_slice(
- glow::ARRAY_BUFFER,
- 0,
- bytemuck::cast_slice(vertices),
- );
-
- gl.buffer_sub_data_u8_slice(
- glow::ELEMENT_ARRAY_BUFFER,
- 0,
- bytemuck::cast_slice(indices),
- );
-
- gl.draw_elements(
- glow::TRIANGLES,
- indices.len() as i32,
- glow::UNSIGNED_INT,
- 0,
- );
- }
- }
-
- unsafe {
- gl.bind_vertex_array(None);
- gl.use_program(None);
- gl.disable(glow::SCISSOR_TEST);
- }
- }
-}
-
-unsafe fn create_buffers(
- gl: &glow::Context,
- size: usize,
-) -> (
- <glow::Context as HasContext>::VertexArray,
- <glow::Context as HasContext>::Buffer,
- <glow::Context as HasContext>::Buffer,
-) {
- let vertex_array = gl.create_vertex_array().expect("Create vertex array");
- let vertex_buffer = gl.create_buffer().expect("Create vertex buffer");
- let index_buffer = gl.create_buffer().expect("Create index buffer");
-
- gl.bind_vertex_array(Some(vertex_array));
-
- gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer));
- gl.buffer_data_size(
- glow::ELEMENT_ARRAY_BUFFER,
- 12 * size as i32,
- glow::DYNAMIC_DRAW,
- );
-
- gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
- gl.buffer_data_size(
- glow::ARRAY_BUFFER,
- (size * Vertex::SIZE) as i32,
- glow::DYNAMIC_DRAW,
- );
-
- let stride = Vertex::SIZE as i32;
-
- gl.enable_vertex_attrib_array(0);
- gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
-
- gl.enable_vertex_attrib_array(1);
- gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, stride, 4 * 2);
-
- gl.enable_vertex_attrib_array(2);
- gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2));
-
- gl.enable_vertex_attrib_array(3);
- gl.vertex_attrib_pointer_f32(
- 3,
- 4,
- glow::FLOAT,
- false,
- stride,
- 4 * (2 + 2 + 4),
- );
-
- gl.enable_vertex_attrib_array(4);
- gl.vertex_attrib_pointer_f32(
- 4,
- 4,
- glow::FLOAT,
- false,
- stride,
- 4 * (2 + 2 + 4 + 4),
- );
-
- gl.enable_vertex_attrib_array(5);
- gl.vertex_attrib_pointer_f32(
- 5,
- 1,
- glow::FLOAT,
- false,
- stride,
- 4 * (2 + 2 + 4 + 4 + 4),
- );
-
- gl.enable_vertex_attrib_array(6);
- gl.vertex_attrib_pointer_f32(
- 6,
- 2,
- glow::FLOAT,
- false,
- stride,
- 4 * (2 + 2 + 4 + 4 + 4 + 1),
- );
-
- gl.bind_vertex_array(None);
- gl.bind_buffer(glow::ARRAY_BUFFER, None);
- gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
-
- (vertex_array, vertex_buffer, index_buffer)
-}
-
-/// The vertex of a colored rectangle with a border.
-///
-/// This type can be directly uploaded to GPU memory.
-#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
-#[repr(C)]
-pub struct Vertex {
- /// The position of the [`Vertex`].
- pub position: [f32; 2],
-
- /// The size of the [`Vertex`].
- pub size: [f32; 2],
-
- /// The color of the [`Vertex`], in __linear RGB__.
- pub color: [f32; 4],
-
- /// The border color of the [`Vertex`], in __linear RGB__.
- pub border_color: [f32; 4],
-
- /// The border radius of the [`Vertex`].
- pub border_radius: [f32; 4],
-
- /// The border width of the [`Vertex`].
- pub border_width: f32,
-
- /// The __quad__ position of the [`Vertex`].
- pub q_position: [f32; 2],
-}
-
-impl Vertex {
- const SIZE: usize = std::mem::size_of::<Self>();
-
- fn from_quad(quad: &layer::Quad) -> [Vertex; 4] {
- let base = Vertex {
- position: quad.position,
- size: quad.size,
- color: quad.color,
- border_color: quad.color,
- border_radius: quad.border_radius,
- border_width: quad.border_width,
- q_position: [0.0, 0.0],
- };
-
- [
- base,
- Self {
- q_position: [0.0, 1.0],
- ..base
- },
- Self {
- q_position: [1.0, 0.0],
- ..base
- },
- Self {
- q_position: [1.0, 1.0],
- ..base
- },
- ]
- }
-}
diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs
deleted file mode 100644
index 89036530..00000000
--- a/glow/src/quad/core.rs
+++ /dev/null
@@ -1,244 +0,0 @@
-use crate::program::{self, Shader};
-use crate::Transformation;
-use glow::HasContext;
-use iced_graphics::layer;
-use iced_native::Rectangle;
-
-const MAX_INSTANCES: usize = 100_000;
-
-#[derive(Debug)]
-pub struct Pipeline {
- program: <glow::Context as HasContext>::Program,
- vertex_array: <glow::Context as HasContext>::VertexArray,
- instances: <glow::Context as HasContext>::Buffer,
- transform_location: <glow::Context as HasContext>::UniformLocation,
- scale_location: <glow::Context as HasContext>::UniformLocation,
- screen_height_location: <glow::Context as HasContext>::UniformLocation,
- current_transform: Transformation,
- current_scale: f32,
- current_target_height: u32,
-}
-
-impl Pipeline {
- pub fn new(
- gl: &glow::Context,
- shader_version: &program::Version,
- ) -> Pipeline {
- let program = unsafe {
- let vertex_shader = Shader::vertex(
- gl,
- shader_version,
- include_str!("../shader/core/quad.vert"),
- );
- let fragment_shader = Shader::fragment(
- gl,
- shader_version,
- include_str!("../shader/core/quad.frag"),
- );
-
- program::create(
- gl,
- &[vertex_shader, fragment_shader],
- &[
- (0, "i_Pos"),
- (1, "i_Scale"),
- (2, "i_Color"),
- (3, "i_BorderColor"),
- (4, "i_BorderRadius"),
- (5, "i_BorderWidth"),
- ],
- )
- };
-
- let transform_location =
- unsafe { gl.get_uniform_location(program, "u_Transform") }
- .expect("Get transform location");
-
- let scale_location =
- unsafe { gl.get_uniform_location(program, "u_Scale") }
- .expect("Get scale location");
-
- let screen_height_location =
- unsafe { gl.get_uniform_location(program, "u_ScreenHeight") }
- .expect("Get target height location");
-
- unsafe {
- gl.use_program(Some(program));
-
- gl.uniform_matrix_4_f32_slice(
- Some(&transform_location),
- false,
- Transformation::identity().as_ref(),
- );
-
- gl.uniform_1_f32(Some(&scale_location), 1.0);
- gl.uniform_1_f32(Some(&screen_height_location), 0.0);
-
- gl.use_program(None);
- }
-
- let (vertex_array, instances) =
- unsafe { create_instance_buffer(gl, MAX_INSTANCES) };
-
- Pipeline {
- program,
- vertex_array,
- instances,
- transform_location,
- scale_location,
- screen_height_location,
- current_transform: Transformation::identity(),
- current_scale: 1.0,
- current_target_height: 0,
- }
- }
-
- pub fn draw(
- &mut self,
- gl: &glow::Context,
- target_height: u32,
- instances: &[layer::Quad],
- transformation: Transformation,
- scale: f32,
- bounds: Rectangle<u32>,
- ) {
- unsafe {
- gl.enable(glow::SCISSOR_TEST);
- gl.scissor(
- bounds.x as i32,
- (target_height - (bounds.y + bounds.height)) as i32,
- bounds.width as i32,
- bounds.height as i32,
- );
-
- gl.use_program(Some(self.program));
- gl.bind_vertex_array(Some(self.vertex_array));
- gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.instances));
- }
-
- if transformation != self.current_transform {
- unsafe {
- gl.uniform_matrix_4_f32_slice(
- Some(&self.transform_location),
- false,
- transformation.as_ref(),
- );
-
- self.current_transform = transformation;
- }
- }
-
- if scale != self.current_scale {
- unsafe {
- gl.uniform_1_f32(Some(&self.scale_location), scale);
- }
-
- self.current_scale = scale;
- }
-
- if target_height != self.current_target_height {
- unsafe {
- gl.uniform_1_f32(
- Some(&self.screen_height_location),
- target_height as f32,
- );
- }
-
- self.current_target_height = target_height;
- }
-
- for instances in instances.chunks(MAX_INSTANCES) {
- unsafe {
- gl.buffer_sub_data_u8_slice(
- glow::ARRAY_BUFFER,
- 0,
- bytemuck::cast_slice(instances),
- );
-
- gl.draw_arrays_instanced(
- glow::TRIANGLE_STRIP,
- 0,
- 4,
- instances.len() as i32,
- );
- }
- }
-
- unsafe {
- gl.bind_vertex_array(None);
- gl.use_program(None);
- gl.disable(glow::SCISSOR_TEST);
- }
- }
-}
-
-unsafe fn create_instance_buffer(
- gl: &glow::Context,
- size: usize,
-) -> (
- <glow::Context as HasContext>::VertexArray,
- <glow::Context as HasContext>::Buffer,
-) {
- let vertex_array = gl.create_vertex_array().expect("Create vertex array");
- let buffer = gl.create_buffer().expect("Create instance buffer");
-
- gl.bind_vertex_array(Some(vertex_array));
- gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer));
- gl.buffer_data_size(
- glow::ARRAY_BUFFER,
- (size * std::mem::size_of::<layer::Quad>()) as i32,
- glow::DYNAMIC_DRAW,
- );
-
- let stride = std::mem::size_of::<layer::Quad>() as i32;
-
- gl.enable_vertex_attrib_array(0);
- gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
- gl.vertex_attrib_divisor(0, 1);
-
- gl.enable_vertex_attrib_array(1);
- gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, stride, 4 * 2);
- gl.vertex_attrib_divisor(1, 1);
-
- gl.enable_vertex_attrib_array(2);
- gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2));
- gl.vertex_attrib_divisor(2, 1);
-
- gl.enable_vertex_attrib_array(3);
- gl.vertex_attrib_pointer_f32(
- 3,
- 4,
- glow::FLOAT,
- false,
- stride,
- 4 * (2 + 2 + 4),
- );
- gl.vertex_attrib_divisor(3, 1);
-
- gl.enable_vertex_attrib_array(4);
- gl.vertex_attrib_pointer_f32(
- 4,
- 4,
- glow::FLOAT,
- false,
- stride,
- 4 * (2 + 2 + 4 + 4),
- );
- gl.vertex_attrib_divisor(4, 1);
-
- gl.enable_vertex_attrib_array(5);
- gl.vertex_attrib_pointer_f32(
- 5,
- 1,
- glow::FLOAT,
- false,
- stride,
- 4 * (2 + 2 + 4 + 4 + 4),
- );
- gl.vertex_attrib_divisor(5, 1);
-
- gl.bind_vertex_array(None);
- gl.bind_buffer(glow::ARRAY_BUFFER, None);
-
- (vertex_array, buffer)
-}
diff --git a/glow/src/settings.rs b/glow/src/settings.rs
deleted file mode 100644
index 6aaa0d55..00000000
--- a/glow/src/settings.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-//! Configure a renderer.
-pub use iced_graphics::Antialiasing;
-
-/// The settings of a [`Backend`].
-///
-/// [`Backend`]: crate::Backend
-#[derive(Clone, Copy, PartialEq)]
-pub struct Settings {
- /// The bytes of the font that will be used by default.
- ///
- /// If `None` is provided, a default system font will be chosen.
- pub default_font: Option<&'static [u8]>,
-
- /// The default size of text.
- ///
- /// By default, it will be set to `20.0`.
- pub default_text_size: f32,
-
- /// If enabled, spread text workload in multiple threads when multiple cores
- /// are available.
- ///
- /// By default, it is disabled.
- pub text_multithreading: bool,
-
- /// The antialiasing strategy that will be used for triangle primitives.
- ///
- /// By default, it is `None`.
- pub antialiasing: Option<Antialiasing>,
-}
-
-impl Default for Settings {
- fn default() -> Settings {
- Settings {
- default_font: None,
- default_text_size: 20.0,
- text_multithreading: false,
- antialiasing: None,
- }
- }
-}
-
-impl std::fmt::Debug for Settings {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("Settings")
- // Instead of printing the font bytes, we simply show a `bool` indicating if using a default font or not.
- .field("default_font", &self.default_font.is_some())
- .field("default_text_size", &self.default_text_size)
- .field("text_multithreading", &self.text_multithreading)
- .field("antialiasing", &self.antialiasing)
- .finish()
- }
-}
-
-impl Settings {
- /// Creates new [`Settings`] using environment configuration.
- ///
- /// Currently, this is equivalent to calling [`Settings::default`].
- pub fn from_env() -> Self {
- Self::default()
- }
-}
diff --git a/glow/src/shader/common/gradient.frag b/glow/src/shader/common/gradient.frag
deleted file mode 100644
index 9af0cb6e..00000000
--- a/glow/src/shader/common/gradient.frag
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifdef GL_ES
-#ifdef GL_FRAGMENT_PRECISION_HIGH
-precision highp float;
-#else
-precision mediump float;
-#endif
-#endif
-
-#ifdef HIGHER_THAN_300
-layout (location = 0) out vec4 fragColor;
-#define gl_FragColor fragColor
-#endif
-
-in vec2 raw_position;
-
-uniform vec4 gradient_direction;
-uniform int color_stops_size;
-// GLSL does not support dynamically sized arrays without SSBOs so this is capped to 16 stops
-//stored as color(vec4) -> offset(vec4) sequentially;
-uniform vec4 color_stops[32];
-
-//TODO: rewrite without branching to make ALUs happy
-void main() {
- vec2 start = gradient_direction.xy;
- vec2 end = gradient_direction.zw;
- vec2 gradient_vec = vec2(end - start);
- vec2 current_vec = vec2(raw_position.xy - start);
- vec2 unit = normalize(gradient_vec);
- float coord_offset = dot(unit, current_vec) / length(gradient_vec);
- //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill
- gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
-
- float min_offset = color_stops[1].x;
- float max_offset = color_stops[color_stops_size - 1].x;
-
- for (int i = 0; i < color_stops_size - 2; i += 2) {
- float curr_offset = color_stops[i+1].x;
- float next_offset = color_stops[i+3].x;
-
- if (coord_offset <= min_offset) {
- //current coordinate is before the first defined offset, set it to the start color
- gl_FragColor = color_stops[0];
- }
-
- if (curr_offset <= coord_offset && coord_offset <= next_offset) {
- //current fragment is between the current offset processing & the next one, interpolate colors
- gl_FragColor = mix(color_stops[i], color_stops[i+2], smoothstep(
- curr_offset,
- next_offset,
- coord_offset
- ));
- }
-
- if (coord_offset >= max_offset) {
- //current coordinate is before the last defined offset, set it to the last color
- gl_FragColor = color_stops[color_stops_size - 2];
- }
- }
-}
diff --git a/glow/src/shader/common/gradient.vert b/glow/src/shader/common/gradient.vert
deleted file mode 100644
index fe505997..00000000
--- a/glow/src/shader/common/gradient.vert
+++ /dev/null
@@ -1,9 +0,0 @@
-uniform mat4 u_Transform;
-
-in vec2 i_Position;
-out vec2 raw_position;
-
-void main() {
- gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
- raw_position = i_Position;
-}
diff --git a/glow/src/shader/common/image.frag b/glow/src/shader/common/image.frag
deleted file mode 100644
index 5e05abdf..00000000
--- a/glow/src/shader/common/image.frag
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifdef GL_ES
-#ifdef GL_FRAGMENT_PRECISION_HIGH
-precision highp float;
-#else
-precision mediump float;
-#endif
-#endif
-
-uniform sampler2D tex;
-in vec2 tex_pos;
-
-#ifdef HIGHER_THAN_300
-out vec4 fragColor;
-#define gl_FragColor fragColor
-#endif
-#ifdef GL_ES
-#define texture texture2D
-#endif
-
-void main() {
- gl_FragColor = texture(tex, tex_pos);
-}
diff --git a/glow/src/shader/common/image.vert b/glow/src/shader/common/image.vert
deleted file mode 100644
index 93e541f2..00000000
--- a/glow/src/shader/common/image.vert
+++ /dev/null
@@ -1,9 +0,0 @@
-uniform mat4 u_Transform;
-
-in vec2 i_Position;
-out vec2 tex_pos;
-
-void main() {
- gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
- tex_pos = i_Position;
-}
diff --git a/glow/src/shader/common/solid.frag b/glow/src/shader/common/solid.frag
deleted file mode 100644
index 174ffdd3..00000000
--- a/glow/src/shader/common/solid.frag
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifdef GL_ES
-#ifdef GL_FRAGMENT_PRECISION_HIGH
-precision highp float;
-#else
-precision mediump float;
-#endif
-#endif
-
-#ifdef HIGHER_THAN_300
-out vec4 fragColor;
-#define gl_FragColor fragColor
-#endif
-
-in vec4 v_Color;
-
-void main() {
- gl_FragColor = v_Color;
-}
diff --git a/glow/src/shader/common/solid.vert b/glow/src/shader/common/solid.vert
deleted file mode 100644
index 59ed88e5..00000000
--- a/glow/src/shader/common/solid.vert
+++ /dev/null
@@ -1,11 +0,0 @@
-uniform mat4 u_Transform;
-
-in vec2 i_Position;
-in vec4 i_Color;
-
-out vec4 v_Color;
-
-void main() {
- gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
- v_Color = i_Color;
-}
diff --git a/glow/src/shader/compatibility/quad.frag b/glow/src/shader/compatibility/quad.frag
deleted file mode 100644
index bb9d8122..00000000
--- a/glow/src/shader/compatibility/quad.frag
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifdef GL_ES
-#ifdef GL_FRAGMENT_PRECISION_HIGH
-precision highp float;
-#else
-precision mediump float;
-#endif
-#endif
-
-uniform float u_ScreenHeight;
-
-varying vec4 v_Color;
-varying vec4 v_BorderColor;
-varying vec2 v_Pos;
-varying vec2 v_Scale;
-varying vec4 v_BorderRadius;
-varying float v_BorderWidth;
-
-float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius)
-{
- // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN
- vec2 inner_size = size - vec2(radius, radius) * 2.0;
- vec2 top_left = position + vec2(radius, radius);
- vec2 bottom_right = top_left + inner_size;
-
- vec2 top_left_distance = top_left - frag_coord;
- vec2 bottom_right_distance = frag_coord - bottom_right;
-
- vec2 distance = vec2(
- max(max(top_left_distance.x, bottom_right_distance.x), 0.0),
- max(max(top_left_distance.y, bottom_right_distance.y), 0.0)
- );
-
- return sqrt(distance.x * distance.x + distance.y * distance.y);
-}
-
-float selectBorderRadius(vec4 radi, vec2 position, vec2 center)
-{
- float rx = radi.x;
- float ry = radi.y;
- rx = position.x > center.x ? radi.y : radi.x;
- ry = position.x > center.x ? radi.z : radi.w;
- rx = position.y > center.y ? ry : rx;
- return rx;
-}
-
-void main() {
- vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
-
- float border_radius = selectBorderRadius(
- v_BorderRadius,
- fragCoord,
- (v_Pos + v_Scale * 0.5).xy
- );
-
- float internal_border = max(border_radius - v_BorderWidth, 0.0);
-
- float internal_distance = _distance(
- fragCoord,
- v_Pos + vec2(v_BorderWidth),
- v_Scale - vec2(v_BorderWidth * 2.0),
- internal_border
- );
-
- float border_mix = smoothstep(
- max(internal_border - 0.5, 0.0),
- internal_border + 0.5,
- internal_distance
- );
-
- vec4 mixed_color = mix(v_Color, v_BorderColor, border_mix);
-
- float d = _distance(
- fragCoord,
- v_Pos,
- v_Scale,
- border_radius
- );
-
- float radius_alpha =
- 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d);
-
- gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
-}
diff --git a/glow/src/shader/compatibility/quad.vert b/glow/src/shader/compatibility/quad.vert
deleted file mode 100644
index 89931f06..00000000
--- a/glow/src/shader/compatibility/quad.vert
+++ /dev/null
@@ -1,46 +0,0 @@
-uniform mat4 u_Transform;
-uniform float u_Scale;
-
-attribute vec2 i_Pos;
-attribute vec2 i_Scale;
-attribute vec4 i_Color;
-attribute vec4 i_BorderColor;
-attribute vec4 i_BorderRadius;
-attribute float i_BorderWidth;
-attribute vec2 q_Pos;
-
-varying vec4 v_Color;
-varying vec4 v_BorderColor;
-varying vec2 v_Pos;
-varying vec2 v_Scale;
-varying vec4 v_BorderRadius;
-varying float v_BorderWidth;
-
-
-void main() {
- vec2 p_Pos = i_Pos * u_Scale;
- vec2 p_Scale = i_Scale * u_Scale;
-
- vec4 i_BorderRadius = vec4(
- min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0),
- min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0),
- min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0),
- min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0)
- );
-
- mat4 i_Transform = mat4(
- vec4(p_Scale.x + 1.0, 0.0, 0.0, 0.0),
- vec4(0.0, p_Scale.y + 1.0, 0.0, 0.0),
- vec4(0.0, 0.0, 1.0, 0.0),
- vec4(p_Pos - vec2(0.5, 0.5), 0.0, 1.0)
- );
-
- v_Color = i_Color;
- v_BorderColor = i_BorderColor;
- v_Pos = p_Pos;
- v_Scale = p_Scale;
- v_BorderRadius = i_BorderRadius * u_Scale;
- v_BorderWidth = i_BorderWidth * u_Scale;
-
- gl_Position = u_Transform * i_Transform * vec4(q_Pos, 0.0, 1.0);
-}
diff --git a/glow/src/shader/core/quad.frag b/glow/src/shader/core/quad.frag
deleted file mode 100644
index 71147aa5..00000000
--- a/glow/src/shader/core/quad.frag
+++ /dev/null
@@ -1,95 +0,0 @@
-#ifdef GL_ES
-#ifdef GL_FRAGMENT_PRECISION_HIGH
-precision highp float;
-#else
-precision mediump float;
-#endif
-#endif
-
-#ifdef HIGHER_THAN_300
-out vec4 fragColor;
-#define gl_FragColor fragColor
-#endif
-
-uniform float u_ScreenHeight;
-
-in vec4 v_Color;
-in vec4 v_BorderColor;
-in vec2 v_Pos;
-in vec2 v_Scale;
-in vec4 v_BorderRadius;
-in float v_BorderWidth;
-
-float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius)
-{
- // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN
- vec2 inner_size = size - vec2(radius, radius) * 2.0;
- vec2 top_left = position + vec2(radius, radius);
- vec2 bottom_right = top_left + inner_size;
-
- vec2 top_left_distance = top_left - frag_coord;
- vec2 bottom_right_distance = frag_coord - bottom_right;
-
- vec2 distance = vec2(
- max(max(top_left_distance.x, bottom_right_distance.x), 0.0),
- max(max(top_left_distance.y, bottom_right_distance.y), 0.0)
- );
-
- return sqrt(distance.x * distance.x + distance.y * distance.y);
-}
-
-float selectBorderRadius(vec4 radi, vec2 position, vec2 center)
-{
- float rx = radi.x;
- float ry = radi.y;
- rx = position.x > center.x ? radi.y : radi.x;
- ry = position.x > center.x ? radi.z : radi.w;
- rx = position.y > center.y ? ry : rx;
- return rx;
-}
-
-void main() {
- vec4 mixed_color;
-
- vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
-
- float border_radius = selectBorderRadius(
- v_BorderRadius,
- fragCoord,
- (v_Pos + v_Scale * 0.5).xy
- );
-
- // TODO: Remove branching (?)
- if(v_BorderWidth > 0.0) {
- float internal_border = max(border_radius - v_BorderWidth, 0.0);
-
- float internal_distance = fDistance(
- fragCoord,
- v_Pos + vec2(v_BorderWidth),
- v_Scale - vec2(v_BorderWidth * 2.0),
- internal_border
- );
-
- float border_mix = smoothstep(
- max(internal_border - 0.5, 0.0),
- internal_border + 0.5,
- internal_distance
- );
-
- mixed_color = mix(v_Color, v_BorderColor, border_mix);
- } else {
- mixed_color = v_Color;
- }
-
- float d = fDistance(
- fragCoord,
- v_Pos,
- v_Scale,
- border_radius
- );
-
- float radius_alpha =
- 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d);
-
- gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
-}
diff --git a/glow/src/shader/core/quad.vert b/glow/src/shader/core/quad.vert
deleted file mode 100644
index 17c3e641..00000000
--- a/glow/src/shader/core/quad.vert
+++ /dev/null
@@ -1,52 +0,0 @@
-uniform mat4 u_Transform;
-uniform float u_Scale;
-
-in vec2 i_Pos;
-in vec2 i_Scale;
-in vec4 i_Color;
-in vec4 i_BorderColor;
-in vec4 i_BorderRadius;
-in float i_BorderWidth;
-
-out vec4 v_Color;
-out vec4 v_BorderColor;
-out vec2 v_Pos;
-out vec2 v_Scale;
-out vec4 v_BorderRadius;
-out float v_BorderWidth;
-
-vec2 positions[4] = vec2[](
- vec2(0.0, 0.0),
- vec2(0.0, 1.0),
- vec2(1.0, 0.0),
- vec2(1.0, 1.0)
-);
-
-void main() {
- vec2 q_Pos = positions[gl_VertexID];
- vec2 p_Pos = i_Pos * u_Scale;
- vec2 p_Scale = i_Scale * u_Scale;
-
- vec4 i_BorderRadius = vec4(
- min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0),
- min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0),
- min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0),
- min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0)
- );
-
- mat4 i_Transform = mat4(
- vec4(p_Scale.x + 1.0, 0.0, 0.0, 0.0),
- vec4(0.0, p_Scale.y + 1.0, 0.0, 0.0),
- vec4(0.0, 0.0, 1.0, 0.0),
- vec4(p_Pos - vec2(0.5, 0.5), 0.0, 1.0)
- );
-
- v_Color = i_Color;
- v_BorderColor = i_BorderColor;
- v_Pos = p_Pos;
- v_Scale = p_Scale;
- v_BorderRadius = i_BorderRadius * u_Scale;
- v_BorderWidth = i_BorderWidth * u_Scale;
-
- gl_Position = u_Transform * i_Transform * vec4(q_Pos, 0.0, 1.0);
-}
diff --git a/glow/src/text.rs b/glow/src/text.rs
deleted file mode 100644
index 37ccdece..00000000
--- a/glow/src/text.rs
+++ /dev/null
@@ -1,257 +0,0 @@
-use crate::Transformation;
-
-use iced_graphics::font;
-
-use glow_glyph::ab_glyph;
-use std::{cell::RefCell, collections::HashMap};
-
-pub use iced_native::text::Hit;
-
-#[derive(Debug)]
-pub struct Pipeline {
- draw_brush: RefCell<glow_glyph::GlyphBrush>,
- draw_font_map: RefCell<HashMap<String, glow_glyph::FontId>>,
- measure_brush: RefCell<glyph_brush::GlyphBrush<()>>,
-}
-
-impl Pipeline {
- pub fn new(
- gl: &glow::Context,
- default_font: Option<&[u8]>,
- multithreading: bool,
- ) -> Self {
- let default_font = default_font.map(|slice| slice.to_vec());
-
- // TODO: Font customization
- #[cfg(feature = "default_system_font")]
- let default_font = {
- default_font.or_else(|| {
- font::Source::new()
- .load(&[font::Family::SansSerif, font::Family::Serif])
- .ok()
- })
- };
-
- let default_font =
- default_font.unwrap_or_else(|| font::FALLBACK.to_vec());
-
- let font = ab_glyph::FontArc::try_from_vec(default_font)
- .unwrap_or_else(|_| {
- log::warn!(
- "System font failed to load. Falling back to \
- embedded font..."
- );
-
- ab_glyph::FontArc::try_from_slice(font::FALLBACK)
- .expect("Load fallback font")
- });
-
- let draw_brush_builder =
- glow_glyph::GlyphBrushBuilder::using_font(font.clone())
- .initial_cache_size((2048, 2048))
- .draw_cache_multithread(multithreading);
-
- #[cfg(target_arch = "wasm32")]
- let draw_brush_builder = draw_brush_builder.draw_cache_align_4x4(true);
-
- let draw_brush = draw_brush_builder.build(gl);
-
- let measure_brush =
- glyph_brush::GlyphBrushBuilder::using_font(font).build();
-
- Pipeline {
- draw_brush: RefCell::new(draw_brush),
- draw_font_map: RefCell::new(HashMap::new()),
- measure_brush: RefCell::new(measure_brush),
- }
- }
-
- pub fn queue(&mut self, section: glow_glyph::Section<'_>) {
- self.draw_brush.borrow_mut().queue(section);
- }
-
- pub fn draw_queued(
- &mut self,
- gl: &glow::Context,
- transformation: Transformation,
- region: glow_glyph::Region,
- ) {
- self.draw_brush
- .borrow_mut()
- .draw_queued_with_transform_and_scissoring(
- gl,
- transformation.into(),
- region,
- )
- .expect("Draw text");
- }
-
- pub fn measure(
- &self,
- content: &str,
- size: f32,
- font: iced_native::Font,
- bounds: iced_native::Size,
- ) -> (f32, f32) {
- use glow_glyph::GlyphCruncher;
-
- let glow_glyph::FontId(font_id) = self.find_font(font);
-
- let section = glow_glyph::Section {
- bounds: (bounds.width, bounds.height),
- text: vec![glow_glyph::Text {
- text: content,
- scale: size.into(),
- font_id: glow_glyph::FontId(font_id),
- extra: glow_glyph::Extra::default(),
- }],
- ..Default::default()
- };
-
- if let Some(bounds) =
- self.measure_brush.borrow_mut().glyph_bounds(section)
- {
- (bounds.width().ceil(), bounds.height().ceil())
- } else {
- (0.0, 0.0)
- }
- }
-
- pub fn hit_test(
- &self,
- content: &str,
- size: f32,
- font: iced_native::Font,
- bounds: iced_native::Size,
- point: iced_native::Point,
- nearest_only: bool,
- ) -> Option<Hit> {
- use glow_glyph::GlyphCruncher;
-
- let glow_glyph::FontId(font_id) = self.find_font(font);
-
- let section = glow_glyph::Section {
- bounds: (bounds.width, bounds.height),
- text: vec![glow_glyph::Text {
- text: content,
- scale: size.into(),
- font_id: glow_glyph::FontId(font_id),
- extra: glow_glyph::Extra::default(),
- }],
- ..Default::default()
- };
-
- let mut mb = self.measure_brush.borrow_mut();
-
- // The underlying type is FontArc, so clones are cheap.
- use ab_glyph::{Font, ScaleFont};
- let font = mb.fonts()[font_id].clone().into_scaled(size);
-
- // Implements an iterator over the glyph bounding boxes.
- let bounds = mb.glyphs(section).map(
- |glow_glyph::SectionGlyph {
- byte_index, glyph, ..
- }| {
- (
- *byte_index,
- iced_native::Rectangle::new(
- iced_native::Point::new(
- glyph.position.x - font.h_side_bearing(glyph.id),
- glyph.position.y - font.ascent(),
- ),
- iced_native::Size::new(
- font.h_advance(glyph.id),
- font.ascent() - font.descent(),
- ),
- ),
- )
- },
- );
-
- // Implements computation of the character index based on the byte index
- // within the input string.
- let char_index = |byte_index| {
- let mut b_count = 0;
- for (i, utf8_len) in
- content.chars().map(|c| c.len_utf8()).enumerate()
- {
- if byte_index < (b_count + utf8_len) {
- return i;
- }
- b_count += utf8_len;
- }
-
- byte_index
- };
-
- if !nearest_only {
- for (idx, bounds) in bounds.clone() {
- if bounds.contains(point) {
- return Some(Hit::CharOffset(char_index(idx)));
- }
- }
- }
-
- let nearest = bounds
- .map(|(index, bounds)| (index, bounds.center()))
- .min_by(|(_, center_a), (_, center_b)| {
- center_a
- .distance(point)
- .partial_cmp(&center_b.distance(point))
- .unwrap_or(std::cmp::Ordering::Greater)
- });
-
- nearest.map(|(idx, center)| {
- Hit::NearestCharOffset(char_index(idx), point - center)
- })
- }
-
- pub fn trim_measurement_cache(&mut self) {
- // TODO: We should probably use a `GlyphCalculator` for this. However,
- // it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop.
- // This makes stuff quite inconvenient. A manual method for trimming the
- // cache would make our lives easier.
- loop {
- let action = self
- .measure_brush
- .borrow_mut()
- .process_queued(|_, _| {}, |_| {});
-
- match action {
- Ok(_) => break,
- Err(glyph_brush::BrushError::TextureTooSmall { suggested }) => {
- let (width, height) = suggested;
-
- self.measure_brush
- .borrow_mut()
- .resize_texture(width, height);
- }
- }
- }
- }
-
- pub fn find_font(&self, font: iced_native::Font) -> glow_glyph::FontId {
- match font {
- iced_native::Font::Default => glow_glyph::FontId(0),
- iced_native::Font::External { name, bytes } => {
- if let Some(font_id) = self.draw_font_map.borrow().get(name) {
- return *font_id;
- }
-
- let font = ab_glyph::FontArc::try_from_slice(bytes)
- .expect("Load font");
-
- let _ = self.measure_brush.borrow_mut().add_font(font.clone());
-
- let font_id = self.draw_brush.borrow_mut().add_font(font);
-
- let _ = self
- .draw_font_map
- .borrow_mut()
- .insert(String::from(name), font_id);
-
- font_id
- }
- }
- }
-}
diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs
deleted file mode 100644
index 42c88455..00000000
--- a/glow/src/triangle.rs
+++ /dev/null
@@ -1,595 +0,0 @@
-//! Draw meshes of triangles.
-use crate::program;
-use crate::Transformation;
-
-use iced_graphics::gradient::Gradient;
-use iced_graphics::layer::mesh::{self, Mesh};
-use iced_graphics::triangle::{ColoredVertex2D, Vertex2D};
-
-use glow::HasContext;
-use std::marker::PhantomData;
-
-#[cfg(feature = "tracing")]
-use tracing::info_span;
-
-const DEFAULT_VERTICES: usize = 1_000;
-const DEFAULT_INDICES: usize = 1_000;
-
-#[derive(Debug)]
-pub(crate) struct Pipeline {
- indices: Buffer<u32>,
- solid: solid::Program,
- gradient: gradient::Program,
-}
-
-impl Pipeline {
- pub fn new(gl: &glow::Context, shader_version: &program::Version) -> Self {
- let mut indices = unsafe {
- Buffer::new(
- gl,
- glow::ELEMENT_ARRAY_BUFFER,
- glow::DYNAMIC_DRAW,
- DEFAULT_INDICES,
- )
- };
-
- let solid = solid::Program::new(gl, shader_version);
- let gradient = gradient::Program::new(gl, shader_version);
-
- unsafe {
- gl.bind_vertex_array(Some(solid.vertex_array));
- indices.bind(gl, 0);
-
- gl.bind_vertex_array(Some(gradient.vertex_array));
- indices.bind(gl, 0);
-
- gl.bind_vertex_array(None);
- }
-
- Self {
- indices,
- solid,
- gradient,
- }
- }
-
- pub fn draw(
- &mut self,
- meshes: &[Mesh<'_>],
- gl: &glow::Context,
- target_height: u32,
- transformation: Transformation,
- scale_factor: f32,
- ) {
- #[cfg(feature = "tracing")]
- let _ = info_span!("Glow::Triangle", "DRAW").enter();
-
- unsafe {
- gl.enable(glow::MULTISAMPLE);
- gl.enable(glow::SCISSOR_TEST);
- }
-
- // Count the total amount of vertices & indices we need to handle
- let count = mesh::attribute_count_of(meshes);
-
- // Then we ensure the current attribute buffers are big enough, resizing if necessary
- unsafe {
- self.indices.bind(gl, count.indices);
- }
-
- // We upload all the vertices and indices upfront
- let mut solid_vertex_offset = 0;
- let mut gradient_vertex_offset = 0;
- let mut index_offset = 0;
-
- for mesh in meshes {
- let indices = mesh.indices();
-
- unsafe {
- gl.buffer_sub_data_u8_slice(
- glow::ELEMENT_ARRAY_BUFFER,
- (index_offset * std::mem::size_of::<u32>()) as i32,
- bytemuck::cast_slice(indices),
- );
-
- index_offset += indices.len();
- }
-
- match mesh {
- Mesh::Solid { buffers, .. } => {
- unsafe {
- self.solid.vertices.bind(gl, count.solid_vertices);
-
- gl.buffer_sub_data_u8_slice(
- glow::ARRAY_BUFFER,
- (solid_vertex_offset
- * std::mem::size_of::<ColoredVertex2D>())
- as i32,
- bytemuck::cast_slice(&buffers.vertices),
- );
- }
-
- solid_vertex_offset += buffers.vertices.len();
- }
- Mesh::Gradient { buffers, .. } => {
- unsafe {
- self.gradient
- .vertices
- .bind(gl, count.gradient_vertices);
-
- gl.buffer_sub_data_u8_slice(
- glow::ARRAY_BUFFER,
- (gradient_vertex_offset
- * std::mem::size_of::<Vertex2D>())
- as i32,
- bytemuck::cast_slice(&buffers.vertices),
- );
- }
-
- gradient_vertex_offset += buffers.vertices.len();
- }
- }
- }
-
- // Then we draw each mesh using offsets
- let mut last_solid_vertex = 0;
- let mut last_gradient_vertex = 0;
- let mut last_index = 0;
-
- for mesh in meshes {
- let indices = mesh.indices();
- let origin = mesh.origin();
-
- let transform =
- transformation * Transformation::translate(origin.x, origin.y);
-
- let clip_bounds = (mesh.clip_bounds() * scale_factor).snap();
-
- unsafe {
- gl.scissor(
- clip_bounds.x as i32,
- (target_height - (clip_bounds.y + clip_bounds.height))
- as i32,
- clip_bounds.width as i32,
- clip_bounds.height as i32,
- );
- }
-
- match mesh {
- Mesh::Solid { buffers, .. } => unsafe {
- gl.use_program(Some(self.solid.program));
- gl.bind_vertex_array(Some(self.solid.vertex_array));
-
- if transform != self.solid.uniforms.transform {
- gl.uniform_matrix_4_f32_slice(
- Some(&self.solid.uniforms.transform_location),
- false,
- transform.as_ref(),
- );
-
- self.solid.uniforms.transform = transform;
- }
-
- gl.draw_elements_base_vertex(
- glow::TRIANGLES,
- indices.len() as i32,
- glow::UNSIGNED_INT,
- (last_index * std::mem::size_of::<u32>()) as i32,
- last_solid_vertex as i32,
- );
-
- last_solid_vertex += buffers.vertices.len();
- },
- Mesh::Gradient {
- buffers, gradient, ..
- } => unsafe {
- gl.use_program(Some(self.gradient.program));
- gl.bind_vertex_array(Some(self.gradient.vertex_array));
-
- if transform != self.gradient.uniforms.transform {
- gl.uniform_matrix_4_f32_slice(
- Some(&self.gradient.uniforms.locations.transform),
- false,
- transform.as_ref(),
- );
-
- self.gradient.uniforms.transform = transform;
- }
-
- if &self.gradient.uniforms.gradient != *gradient {
- match gradient {
- Gradient::Linear(linear) => {
- gl.uniform_4_f32(
- Some(
- &self
- .gradient
- .uniforms
- .locations
- .gradient_direction,
- ),
- linear.start.x,
- linear.start.y,
- linear.end.x,
- linear.end.y,
- );
-
- gl.uniform_1_i32(
- Some(
- &self
- .gradient
- .uniforms
- .locations
- .color_stops_size,
- ),
- (linear.color_stops.len() * 2) as i32,
- );
-
- let mut stops = [0.0; 128];
-
- for (index, stop) in linear
- .color_stops
- .iter()
- .enumerate()
- .take(16)
- {
- let [r, g, b, a] = stop.color.into_linear();
-
- stops[index * 8] = r;
- stops[(index * 8) + 1] = g;
- stops[(index * 8) + 2] = b;
- stops[(index * 8) + 3] = a;
- stops[(index * 8) + 4] = stop.offset;
- stops[(index * 8) + 5] = 0.;
- stops[(index * 8) + 6] = 0.;
- stops[(index * 8) + 7] = 0.;
- }
-
- gl.uniform_4_f32_slice(
- Some(
- &self
- .gradient
- .uniforms
- .locations
- .color_stops,
- ),
- &stops,
- );
- }
- }
-
- self.gradient.uniforms.gradient = (*gradient).clone();
- }
-
- gl.draw_elements_base_vertex(
- glow::TRIANGLES,
- indices.len() as i32,
- glow::UNSIGNED_INT,
- (last_index * std::mem::size_of::<u32>()) as i32,
- last_gradient_vertex as i32,
- );
-
- last_gradient_vertex += buffers.vertices.len();
- },
- }
-
- last_index += indices.len();
- }
-
- unsafe {
- gl.bind_vertex_array(None);
- gl.disable(glow::SCISSOR_TEST);
- gl.disable(glow::MULTISAMPLE);
- }
- }
-}
-
-#[derive(Debug)]
-pub struct Buffer<T> {
- raw: <glow::Context as HasContext>::Buffer,
- target: u32,
- usage: u32,
- size: usize,
- phantom: PhantomData<T>,
-}
-
-impl<T> Buffer<T> {
- pub unsafe fn new(
- gl: &glow::Context,
- target: u32,
- usage: u32,
- size: usize,
- ) -> Self {
- let raw = gl.create_buffer().expect("Create buffer");
-
- let mut buffer = Buffer {
- raw,
- target,
- usage,
- size: 0,
- phantom: PhantomData,
- };
-
- buffer.bind(gl, size);
-
- buffer
- }
-
- pub unsafe fn bind(&mut self, gl: &glow::Context, size: usize) {
- gl.bind_buffer(self.target, Some(self.raw));
-
- if self.size < size {
- gl.buffer_data_size(
- self.target,
- (size * std::mem::size_of::<T>()) as i32,
- self.usage,
- );
-
- self.size = size;
- }
- }
-}
-
-mod solid {
- use crate::program;
- use crate::triangle;
- use glow::{Context, HasContext, NativeProgram};
- use iced_graphics::triangle::ColoredVertex2D;
- use iced_graphics::Transformation;
-
- #[derive(Debug)]
- pub struct Program {
- pub program: <Context as HasContext>::Program,
- pub vertex_array: <glow::Context as HasContext>::VertexArray,
- pub vertices: triangle::Buffer<ColoredVertex2D>,
- pub uniforms: Uniforms,
- }
-
- impl Program {
- pub fn new(gl: &Context, shader_version: &program::Version) -> Self {
- let program = unsafe {
- let vertex_shader = program::Shader::vertex(
- gl,
- shader_version,
- include_str!("shader/common/solid.vert"),
- );
-
- let fragment_shader = program::Shader::fragment(
- gl,
- shader_version,
- include_str!("shader/common/solid.frag"),
- );
-
- program::create(
- gl,
- &[vertex_shader, fragment_shader],
- &[(0, "i_Position"), (1, "i_Color")],
- )
- };
-
- let vertex_array = unsafe {
- gl.create_vertex_array().expect("Create vertex array")
- };
-
- let vertices = unsafe {
- triangle::Buffer::new(
- gl,
- glow::ARRAY_BUFFER,
- glow::DYNAMIC_DRAW,
- super::DEFAULT_VERTICES,
- )
- };
-
- unsafe {
- gl.bind_vertex_array(Some(vertex_array));
-
- let stride = std::mem::size_of::<ColoredVertex2D>() as i32;
-
- gl.enable_vertex_attrib_array(0);
- gl.vertex_attrib_pointer_f32(
- 0,
- 2,
- glow::FLOAT,
- false,
- stride,
- 0,
- );
-
- gl.enable_vertex_attrib_array(1);
- gl.vertex_attrib_pointer_f32(
- 1,
- 4,
- glow::FLOAT,
- false,
- stride,
- 4 * 2,
- );
-
- gl.bind_vertex_array(None);
- };
-
- Self {
- program,
- vertex_array,
- vertices,
- uniforms: Uniforms::new(gl, program),
- }
- }
- }
-
- #[derive(Debug)]
- pub struct Uniforms {
- pub transform: Transformation,
- pub transform_location: <Context as HasContext>::UniformLocation,
- }
-
- impl Uniforms {
- fn new(gl: &Context, program: NativeProgram) -> Self {
- let transform = Transformation::identity();
- let transform_location =
- unsafe { gl.get_uniform_location(program, "u_Transform") }
- .expect("Solid - Get u_Transform.");
-
- unsafe {
- gl.use_program(Some(program));
-
- gl.uniform_matrix_4_f32_slice(
- Some(&transform_location),
- false,
- transform.as_ref(),
- );
-
- gl.use_program(None);
- }
-
- Self {
- transform,
- transform_location,
- }
- }
- }
-}
-
-mod gradient {
- use crate::program;
- use crate::triangle;
- use glow::{Context, HasContext, NativeProgram};
- use iced_graphics::gradient::{self, Gradient};
- use iced_graphics::triangle::Vertex2D;
- use iced_graphics::Transformation;
-
- #[derive(Debug)]
- pub struct Program {
- pub program: <Context as HasContext>::Program,
- pub vertex_array: <glow::Context as HasContext>::VertexArray,
- pub vertices: triangle::Buffer<Vertex2D>,
- pub uniforms: Uniforms,
- }
-
- impl Program {
- pub fn new(gl: &Context, shader_version: &program::Version) -> Self {
- let program = unsafe {
- let vertex_shader = program::Shader::vertex(
- gl,
- shader_version,
- include_str!("shader/common/gradient.vert"),
- );
-
- let fragment_shader = program::Shader::fragment(
- gl,
- shader_version,
- include_str!("shader/common/gradient.frag"),
- );
-
- program::create(
- gl,
- &[vertex_shader, fragment_shader],
- &[(0, "i_Position")],
- )
- };
-
- let vertex_array = unsafe {
- gl.create_vertex_array().expect("Create vertex array")
- };
-
- let vertices = unsafe {
- triangle::Buffer::new(
- gl,
- glow::ARRAY_BUFFER,
- glow::DYNAMIC_DRAW,
- super::DEFAULT_VERTICES,
- )
- };
-
- unsafe {
- gl.bind_vertex_array(Some(vertex_array));
-
- let stride = std::mem::size_of::<Vertex2D>() as i32;
-
- gl.enable_vertex_attrib_array(0);
- gl.vertex_attrib_pointer_f32(
- 0,
- 2,
- glow::FLOAT,
- false,
- stride,
- 0,
- );
-
- gl.bind_vertex_array(None);
- };
-
- Self {
- program,
- vertex_array,
- vertices,
- uniforms: Uniforms::new(gl, program),
- }
- }
- }
-
- #[derive(Debug)]
- pub struct Uniforms {
- pub gradient: Gradient,
- pub transform: Transformation,
- pub locations: Locations,
- }
-
- #[derive(Debug)]
- pub struct Locations {
- pub gradient_direction: <Context as HasContext>::UniformLocation,
- pub color_stops_size: <Context as HasContext>::UniformLocation,
- //currently the maximum number of stops is 16 due to lack of SSBO in GL2.1
- pub color_stops: <Context as HasContext>::UniformLocation,
- pub transform: <Context as HasContext>::UniformLocation,
- }
-
- impl Uniforms {
- fn new(gl: &Context, program: NativeProgram) -> Self {
- let gradient_direction = unsafe {
- gl.get_uniform_location(program, "gradient_direction")
- }
- .expect("Gradient - Get gradient_direction.");
-
- let color_stops_size =
- unsafe { gl.get_uniform_location(program, "color_stops_size") }
- .expect("Gradient - Get color_stops_size.");
-
- let color_stops = unsafe {
- gl.get_uniform_location(program, "color_stops")
- .expect("Gradient - Get color_stops.")
- };
-
- let transform = Transformation::identity();
- let transform_location =
- unsafe { gl.get_uniform_location(program, "u_Transform") }
- .expect("Solid - Get u_Transform.");
-
- unsafe {
- gl.use_program(Some(program));
-
- gl.uniform_matrix_4_f32_slice(
- Some(&transform_location),
- false,
- transform.as_ref(),
- );
-
- gl.use_program(None);
- }
-
- Self {
- gradient: Gradient::Linear(gradient::Linear {
- start: Default::default(),
- end: Default::default(),
- color_stops: vec![],
- }),
- transform: Transformation::identity(),
- locations: Locations {
- gradient_direction,
- color_stops_size,
- color_stops,
- transform: transform_location,
- },
- }
- }
- }
-}
diff --git a/glow/src/window.rs b/glow/src/window.rs
deleted file mode 100644
index aac5fb9e..00000000
--- a/glow/src/window.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-//! Display rendering results on windows.
-mod compositor;
-
-pub use compositor::Compositor;
diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs
deleted file mode 100644
index 20756032..00000000
--- a/glow/src/window/compositor.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-use crate::{Backend, Color, Error, Renderer, Settings, Viewport};
-
-use glow::HasContext;
-use iced_graphics::{compositor, Antialiasing, Size};
-
-use core::ffi::c_void;
-use std::marker::PhantomData;
-
-/// A window graphics backend for iced powered by `glow`.
-#[allow(missing_debug_implementations)]
-pub struct Compositor<Theme> {
- gl: glow::Context,
- theme: PhantomData<Theme>,
-}
-
-impl<Theme> iced_graphics::window::GLCompositor for Compositor<Theme> {
- type Settings = Settings;
- type Renderer = Renderer<Theme>;
-
- unsafe fn new(
- settings: Self::Settings,
- loader_function: impl FnMut(&str) -> *const c_void,
- ) -> Result<(Self, Self::Renderer), Error> {
- let gl = glow::Context::from_loader_function(loader_function);
-
- log::info!("{:#?}", settings);
-
- let version = gl.version();
- log::info!(
- "OpenGL version: {:?} (Embedded: {})",
- version,
- version.is_embedded
- );
-
- let renderer = gl.get_parameter_string(glow::RENDERER);
- log::info!("Renderer: {}", renderer);
-
- // Enable auto-conversion from/to sRGB
- gl.enable(glow::FRAMEBUFFER_SRGB);
-
- // Enable alpha blending
- gl.enable(glow::BLEND);
- gl.blend_func_separate(
- glow::SRC_ALPHA,
- glow::ONE_MINUS_SRC_ALPHA,
- glow::ONE,
- glow::ONE_MINUS_SRC_ALPHA,
- );
-
- // Disable multisampling by default
- gl.disable(glow::MULTISAMPLE);
-
- let renderer = Renderer::new(Backend::new(&gl, settings));
-
- Ok((
- Self {
- gl,
- theme: PhantomData,
- },
- renderer,
- ))
- }
-
- fn sample_count(settings: &Settings) -> u32 {
- settings
- .antialiasing
- .map(Antialiasing::sample_count)
- .unwrap_or(0)
- }
-
- fn resize_viewport(&mut self, physical_size: Size<u32>) {
- unsafe {
- self.gl.viewport(
- 0,
- 0,
- physical_size.width as i32,
- physical_size.height as i32,
- );
- }
- }
-
- fn fetch_information(&self) -> compositor::Information {
- let adapter = unsafe { self.gl.get_parameter_string(glow::RENDERER) };
-
- compositor::Information {
- backend: format!("{:?}", self.gl.version()),
- adapter,
- }
- }
-
- fn present<T: AsRef<str>>(
- &mut self,
- renderer: &mut Self::Renderer,
- viewport: &Viewport,
- color: Color,
- overlay: &[T],
- ) {
- let gl = &self.gl;
-
- let [r, g, b, a] = color.into_linear();
-
- unsafe {
- gl.clear_color(r, g, b, a);
- gl.clear(glow::COLOR_BUFFER_BIT);
- }
-
- renderer.with_primitives(|backend, primitive| {
- backend.present(gl, primitive, viewport, overlay);
- });
- }
-}
diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml
deleted file mode 100644
index 10d3778b..00000000
--- a/glutin/Cargo.toml
+++ /dev/null
@@ -1,42 +0,0 @@
-[package]
-name = "iced_glutin"
-version = "0.7.0"
-authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
-edition = "2021"
-description = "A glutin runtime for Iced"
-license = "MIT"
-repository = "https://github.com/iced-rs/iced"
-documentation = "https://docs.rs/iced_glutin"
-keywords = ["gui", "ui", "graphics", "interface", "widgets"]
-categories = ["gui"]
-
-[features]
-trace = ["iced_winit/trace"]
-debug = ["iced_winit/debug"]
-system = ["iced_winit/system"]
-
-[dependencies]
-log = "0.4"
-
-[dependencies.glutin]
-version = "0.29"
-git = "https://github.com/iced-rs/glutin"
-rev = "da8d291486b4c9bec12487a46c119c4b1d386abf"
-
-[dependencies.iced_native]
-version = "0.9"
-path = "../native"
-
-[dependencies.iced_winit]
-version = "0.8"
-path = "../winit"
-features = ["application"]
-
-[dependencies.iced_graphics]
-version = "0.7"
-path = "../graphics"
-features = ["opengl"]
-
-[dependencies.tracing]
-version = "0.1.6"
-optional = true
diff --git a/glutin/README.md b/glutin/README.md
deleted file mode 100644
index 45e8ee6b..00000000
--- a/glutin/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# `iced_glutin`
-[![Documentation](https://docs.rs/iced_glutin/badge.svg)][documentation]
-[![Crates.io](https://img.shields.io/crates/v/iced_glutin.svg)](https://crates.io/crates/iced_glutin)
-[![License](https://img.shields.io/crates/l/iced_glutin.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
-[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
-
-`iced_glutin` offers some convenient abstractions on top of [`iced_native`] to quickstart development when using [`glutin`].
-
-It exposes a renderer-agnostic `Application` trait that can be implemented and then run with a simple call. The use of this trait is optional. A `conversion` module is provided for users that decide to implement a custom event loop.
-
-<p align="center">
- <img alt="The native target" src="../docs/graphs/native.png" width="80%">
-</p>
-
-[documentation]: https://docs.rs/iced_glutin
-[`iced_native`]: ../native
-[`glutin`]: https://github.com/rust-windowing/glutin
-
-## Installation
-Add `iced_glutin` as a dependency in your `Cargo.toml`:
-
-```toml
-iced_glutin = "0.7"
-```
-
-__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
diff --git a/glutin/src/application.rs b/glutin/src/application.rs
deleted file mode 100644
index 5921bdd0..00000000
--- a/glutin/src/application.rs
+++ /dev/null
@@ -1,508 +0,0 @@
-//! Create interactive, native cross-platform applications.
-use crate::mouse;
-use crate::{Error, Executor, Runtime};
-
-pub use iced_winit::application::StyleSheet;
-pub use iced_winit::Application;
-
-use iced_graphics::window;
-use iced_winit::application;
-use iced_winit::conversion;
-use iced_winit::futures;
-use iced_winit::futures::channel::mpsc;
-use iced_winit::renderer;
-use iced_winit::time::Instant;
-use iced_winit::user_interface;
-use iced_winit::{Clipboard, Command, Debug, Event, Proxy, Settings};
-
-use glutin::window::Window;
-use std::mem::ManuallyDrop;
-
-#[cfg(feature = "tracing")]
-use tracing::{info_span, instrument::Instrument};
-
-/// Runs an [`Application`] with an executor, compositor, and the provided
-/// settings.
-pub fn run<A, E, C>(
- settings: Settings<A::Flags>,
- compositor_settings: C::Settings,
-) -> Result<(), Error>
-where
- A: Application + 'static,
- E: Executor + 'static,
- C: window::GLCompositor<Renderer = A::Renderer> + 'static,
- <A::Renderer as iced_native::Renderer>::Theme: StyleSheet,
-{
- use futures::task;
- use futures::Future;
- use glutin::event_loop::EventLoopBuilder;
- use glutin::platform::run_return::EventLoopExtRunReturn;
- use glutin::ContextBuilder;
-
- #[cfg(feature = "trace")]
- let _guard = iced_winit::Profiler::init();
-
- let mut debug = Debug::new();
- debug.startup_started();
-
- #[cfg(feature = "tracing")]
- let _ = info_span!("Application::Glutin", "RUN").entered();
-
- let mut event_loop = EventLoopBuilder::with_user_event().build();
- let proxy = event_loop.create_proxy();
-
- let runtime = {
- let executor = E::new().map_err(Error::ExecutorCreationFailed)?;
- let proxy = Proxy::new(event_loop.create_proxy());
-
- Runtime::new(executor, proxy)
- };
-
- let (application, init_command) = {
- let flags = settings.flags;
-
- runtime.enter(|| A::new(flags))
- };
-
- let context = {
- let builder = settings.window.into_builder(
- &application.title(),
- event_loop.primary_monitor(),
- settings.id,
- );
-
- log::debug!("Window builder: {:#?}", builder);
-
- let opengl_builder = ContextBuilder::new()
- .with_vsync(true)
- .with_multisampling(C::sample_count(&compositor_settings) as u16);
-
- let opengles_builder = opengl_builder.clone().with_gl(
- glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0)),
- );
-
- let (first_builder, second_builder) = if settings.try_opengles_first {
- (opengles_builder, opengl_builder)
- } else {
- (opengl_builder, opengles_builder)
- };
-
- log::info!("Trying first builder: {:#?}", first_builder);
-
- let context = first_builder
- .build_windowed(builder.clone(), &event_loop)
- .or_else(|_| {
- log::info!("Trying second builder: {:#?}", second_builder);
- second_builder.build_windowed(builder, &event_loop)
- })
- .map_err(|error| {
- use glutin::CreationError;
- use iced_graphics::Error as ContextError;
-
- match error {
- CreationError::Window(error) => {
- Error::WindowCreationFailed(error)
- }
- CreationError::OpenGlVersionNotSupported => {
- Error::GraphicsCreationFailed(
- ContextError::VersionNotSupported,
- )
- }
- CreationError::NoAvailablePixelFormat => {
- Error::GraphicsCreationFailed(
- ContextError::NoAvailablePixelFormat,
- )
- }
- error => Error::GraphicsCreationFailed(
- ContextError::BackendError(error.to_string()),
- ),
- }
- })?;
-
- #[allow(unsafe_code)]
- unsafe {
- context.make_current().expect("Make OpenGL context current")
- }
- };
-
- #[allow(unsafe_code)]
- let (compositor, renderer) = unsafe {
- C::new(compositor_settings, |address| {
- context.get_proc_address(address)
- })?
- };
-
- let (mut event_sender, event_receiver) = mpsc::unbounded();
- let (control_sender, mut control_receiver) = mpsc::unbounded();
-
- let mut instance = Box::pin({
- let run_instance = run_instance::<A, E, C>(
- application,
- compositor,
- renderer,
- runtime,
- proxy,
- debug,
- event_receiver,
- control_sender,
- context,
- init_command,
- settings.exit_on_close_request,
- );
-
- #[cfg(feature = "tracing")]
- let run_instance =
- run_instance.instrument(info_span!("Application", "LOOP"));
-
- run_instance
- });
-
- let mut context = task::Context::from_waker(task::noop_waker_ref());
-
- let _ = event_loop.run_return(move |event, _, control_flow| {
- use glutin::event_loop::ControlFlow;
-
- if let ControlFlow::ExitWithCode(_) = control_flow {
- return;
- }
-
- let event = match event {
- glutin::event::Event::WindowEvent {
- event:
- glutin::event::WindowEvent::ScaleFactorChanged {
- new_inner_size,
- ..
- },
- window_id,
- } => Some(glutin::event::Event::WindowEvent {
- event: glutin::event::WindowEvent::Resized(*new_inner_size),
- window_id,
- }),
- _ => event.to_static(),
- };
-
- if let Some(event) = event {
- event_sender.start_send(event).expect("Send event");
-
- let poll = instance.as_mut().poll(&mut context);
-
- match poll {
- task::Poll::Pending => {
- if let Ok(Some(flow)) = control_receiver.try_next() {
- *control_flow = flow;
- }
- }
- task::Poll::Ready(_) => {
- *control_flow = ControlFlow::Exit;
- }
- }
- }
- });
-
- Ok(())
-}
-
-async fn run_instance<A, E, C>(
- mut application: A,
- mut compositor: C,
- mut renderer: A::Renderer,
- mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
- mut proxy: glutin::event_loop::EventLoopProxy<A::Message>,
- mut debug: Debug,
- mut event_receiver: mpsc::UnboundedReceiver<
- glutin::event::Event<'_, A::Message>,
- >,
- mut control_sender: mpsc::UnboundedSender<glutin::event_loop::ControlFlow>,
- mut context: glutin::ContextWrapper<glutin::PossiblyCurrent, Window>,
- init_command: Command<A::Message>,
- exit_on_close_request: bool,
-) where
- A: Application + 'static,
- E: Executor + 'static,
- C: window::GLCompositor<Renderer = A::Renderer> + 'static,
- <A::Renderer as iced_native::Renderer>::Theme: StyleSheet,
-{
- use glutin::event;
- use glutin::event_loop::ControlFlow;
- use iced_winit::futures::stream::StreamExt;
-
- let mut clipboard = Clipboard::connect(context.window());
- let mut cache = user_interface::Cache::default();
- let mut state = application::State::new(&application, context.window());
- let mut viewport_version = state.viewport_version();
- let mut should_exit = false;
-
- application::run_command(
- &application,
- &mut cache,
- &state,
- &mut renderer,
- init_command,
- &mut runtime,
- &mut clipboard,
- &mut should_exit,
- &mut proxy,
- &mut debug,
- context.window(),
- || compositor.fetch_information(),
- );
- runtime.track(application.subscription());
-
- let mut user_interface =
- ManuallyDrop::new(application::build_user_interface(
- &application,
- user_interface::Cache::default(),
- &mut renderer,
- state.logical_size(),
- &mut debug,
- ));
-
- let mut mouse_interaction = mouse::Interaction::default();
- let mut events = Vec::new();
- let mut messages = Vec::new();
- let mut redraw_pending = false;
-
- debug.startup_finished();
-
- while let Some(event) = event_receiver.next().await {
- match event {
- event::Event::NewEvents(start_cause) => {
- redraw_pending = matches!(
- start_cause,
- event::StartCause::Init
- | event::StartCause::Poll
- | event::StartCause::ResumeTimeReached { .. }
- );
- }
- event::Event::MainEventsCleared => {
- if !redraw_pending && events.is_empty() && messages.is_empty() {
- continue;
- }
-
- debug.event_processing_started();
-
- let (interface_state, statuses) = user_interface.update(
- &events,
- state.cursor_position(),
- &mut renderer,
- &mut clipboard,
- &mut messages,
- );
-
- debug.event_processing_finished();
-
- for event in events.drain(..).zip(statuses.into_iter()) {
- runtime.broadcast(event);
- }
-
- if !messages.is_empty()
- || matches!(
- interface_state,
- user_interface::State::Outdated
- )
- {
- let mut cache =
- ManuallyDrop::into_inner(user_interface).into_cache();
-
- // Update application
- application::update(
- &mut application,
- &mut cache,
- &state,
- &mut renderer,
- &mut runtime,
- &mut clipboard,
- &mut should_exit,
- &mut proxy,
- &mut debug,
- &mut messages,
- context.window(),
- || compositor.fetch_information(),
- );
-
- // Update window
- state.synchronize(&application, context.window());
-
- user_interface =
- ManuallyDrop::new(application::build_user_interface(
- &application,
- cache,
- &mut renderer,
- state.logical_size(),
- &mut debug,
- ));
-
- if should_exit {
- break;
- }
- }
-
- // TODO: Avoid redrawing all the time by forcing widgets to
- // request redraws on state changes
- //
- // Then, we can use the `interface_state` here to decide if a redraw
- // is needed right away, or simply wait until a specific time.
- let redraw_event = Event::Window(
- crate::window::Event::RedrawRequested(Instant::now()),
- );
-
- let (interface_state, _) = user_interface.update(
- &[redraw_event.clone()],
- state.cursor_position(),
- &mut renderer,
- &mut clipboard,
- &mut messages,
- );
-
- debug.draw_started();
- let new_mouse_interaction = user_interface.draw(
- &mut renderer,
- state.theme(),
- &renderer::Style {
- text_color: state.text_color(),
- },
- state.cursor_position(),
- );
- debug.draw_finished();
-
- if new_mouse_interaction != mouse_interaction {
- context.window().set_cursor_icon(
- conversion::mouse_interaction(new_mouse_interaction),
- );
-
- mouse_interaction = new_mouse_interaction;
- }
-
- context.window().request_redraw();
- runtime
- .broadcast((redraw_event, crate::event::Status::Ignored));
-
- let _ = control_sender.start_send(match interface_state {
- user_interface::State::Updated {
- redraw_request: Some(redraw_request),
- } => match redraw_request {
- crate::window::RedrawRequest::NextFrame => {
- ControlFlow::Poll
- }
- crate::window::RedrawRequest::At(at) => {
- ControlFlow::WaitUntil(at)
- }
- },
- _ => ControlFlow::Wait,
- });
-
- redraw_pending = false;
- }
- event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
- event::MacOS::ReceivedUrl(url),
- )) => {
- use iced_native::event;
- events.push(iced_native::Event::PlatformSpecific(
- event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
- url,
- )),
- ));
- }
- event::Event::UserEvent(message) => {
- messages.push(message);
- }
- event::Event::RedrawRequested(_) => {
- #[cfg(feature = "tracing")]
- let _ = info_span!("Application", "FRAME").entered();
-
- debug.render_started();
-
- #[allow(unsafe_code)]
- unsafe {
- if !context.is_current() {
- context = context
- .make_current()
- .expect("Make OpenGL context current");
- }
- }
-
- let current_viewport_version = state.viewport_version();
-
- if viewport_version != current_viewport_version {
- let physical_size = state.physical_size();
- let logical_size = state.logical_size();
-
- debug.layout_started();
- user_interface = ManuallyDrop::new(
- ManuallyDrop::into_inner(user_interface)
- .relayout(logical_size, &mut renderer),
- );
- debug.layout_finished();
-
- debug.draw_started();
- let new_mouse_interaction = user_interface.draw(
- &mut renderer,
- state.theme(),
- &renderer::Style {
- text_color: state.text_color(),
- },
- state.cursor_position(),
- );
- debug.draw_finished();
-
- if new_mouse_interaction != mouse_interaction {
- context.window().set_cursor_icon(
- conversion::mouse_interaction(
- new_mouse_interaction,
- ),
- );
-
- mouse_interaction = new_mouse_interaction;
- }
-
- context.resize(glutin::dpi::PhysicalSize::new(
- physical_size.width,
- physical_size.height,
- ));
-
- compositor.resize_viewport(physical_size);
-
- viewport_version = current_viewport_version;
- }
-
- compositor.present(
- &mut renderer,
- state.viewport(),
- state.background_color(),
- &debug.overlay(),
- );
-
- context.swap_buffers().expect("Swap buffers");
-
- debug.render_finished();
-
- // TODO: Handle animations!
- // Maybe we can use `ControlFlow::WaitUntil` for this.
- }
- event::Event::WindowEvent {
- event: window_event,
- ..
- } => {
- if application::requests_exit(&window_event, state.modifiers())
- && exit_on_close_request
- {
- break;
- }
-
- state.update(context.window(), &window_event, &mut debug);
-
- if let Some(event) = conversion::window_event(
- &window_event,
- state.scale_factor(),
- state.modifiers(),
- ) {
- events.push(event);
- }
- }
- _ => {}
- }
- }
-
- // Manually drop the user interface
- drop(ManuallyDrop::into_inner(user_interface));
-}
diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs
deleted file mode 100644
index 33afd664..00000000
--- a/glutin/src/lib.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-//! A windowing shell for [`iced`], on top of [`glutin`].
-//!
-//! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
-//!
-//! [`iced`]: https://github.com/iced-rs/iced
-//! [`glutin`]: https://github.com/rust-windowing/glutin
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
-)]
-#![deny(
- missing_docs,
- missing_debug_implementations,
- unsafe_code,
- unused_results,
- clippy::extra_unused_lifetimes,
- clippy::from_over_into,
- clippy::needless_borrow,
- clippy::new_without_default,
- clippy::useless_conversion
-)]
-#![forbid(rust_2018_idioms)]
-#![allow(clippy::inherent_to_string, clippy::type_complexity)]
-#![cfg_attr(docsrs, feature(doc_cfg))]
-
-pub use glutin;
-
-#[doc(no_inline)]
-pub use iced_winit::*;
-
-pub mod application;
-
-#[doc(no_inline)]
-pub use application::Application;
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index 13ab61d8..19d6af0c 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -26,9 +26,6 @@ dds = ["image_rs/dds"]
farbfeld = ["image_rs/farbfeld"]
canvas = ["lyon"]
qr_code = ["qrcode", "canvas"]
-font-source = ["font-kit"]
-font-fallback = []
-font-icons = []
opengl = []
image_rs = ["kamadak-exif"]
@@ -60,10 +57,6 @@ version = "0.12"
optional = true
default-features = false
-[dependencies.font-kit]
-version = "0.10"
-optional = true
-
[dependencies.image_rs]
version = "0.24"
package = "image"
diff --git a/graphics/fonts/Lato-Regular.ttf b/graphics/fonts/Lato-Regular.ttf
deleted file mode 100644
index 33eba8b1..00000000
--- a/graphics/fonts/Lato-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/graphics/fonts/OFL.txt b/graphics/fonts/OFL.txt
deleted file mode 100644
index dfca0da4..00000000
--- a/graphics/fonts/OFL.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato"
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs
index 256b7ab5..8658cffe 100644
--- a/graphics/src/backend.rs
+++ b/graphics/src/backend.rs
@@ -4,6 +4,8 @@ use iced_native::svg;
use iced_native::text;
use iced_native::{Font, Point, Size};
+use std::borrow::Cow;
+
/// The graphics backend of a [`Renderer`].
///
/// [`Renderer`]: crate::Renderer
@@ -31,6 +33,9 @@ pub trait Text {
/// [`ICON_FONT`]: Self::ICON_FONT
const ARROW_DOWN_ICON: char;
+ /// Returns the default [`Font`].
+ fn default_font(&self) -> Font;
+
/// Returns the default size of text.
fn default_size(&self) -> f32;
@@ -61,6 +66,9 @@ pub trait Text {
point: Point,
nearest_only: bool,
) -> Option<text::Hit>;
+
+ /// Loads a [`Font`] from its bytes.
+ fn load_font(&mut self, font: Cow<'static, [u8]>);
}
/// A graphics backend that supports image rendering.
diff --git a/graphics/src/font.rs b/graphics/src/font.rs
deleted file mode 100644
index d55d0faf..00000000
--- a/graphics/src/font.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-//! Find system fonts or use the built-in ones.
-#[cfg(feature = "font-source")]
-mod source;
-
-#[cfg(feature = "font-source")]
-#[cfg_attr(docsrs, doc(cfg(feature = "font-source")))]
-pub use source::Source;
-
-#[cfg(feature = "font-source")]
-#[cfg_attr(docsrs, doc(cfg(feature = "font-source")))]
-pub use font_kit::{
- error::SelectionError as LoadError, family_name::FamilyName as Family,
-};
-
-/// A built-in fallback font, for convenience.
-#[cfg(feature = "font-fallback")]
-#[cfg_attr(docsrs, doc(cfg(feature = "font-fallback")))]
-pub const FALLBACK: &[u8] = include_bytes!("../fonts/Lato-Regular.ttf");
-
-/// A built-in icon font, for convenience.
-#[cfg(feature = "font-icons")]
-#[cfg_attr(docsrs, doc(cfg(feature = "font-icons")))]
-pub const ICONS: iced_native::Font = iced_native::Font::External {
- name: "iced_wgpu icons",
- bytes: include_bytes!("../fonts/Icons.ttf"),
-};
-
-/// The `char` representing a ✔ icon in the built-in [`ICONS`] font.
-#[cfg(feature = "font-icons")]
-#[cfg_attr(docsrs, doc(cfg(feature = "font-icons")))]
-pub const CHECKMARK_ICON: char = '\u{F00C}';
-
-/// The `char` representing a ▼ icon in the built-in [`ICONS`] font.
-#[cfg(feature = "font-icons")]
-pub const ARROW_DOWN_ICON: char = '\u{E800}';
diff --git a/graphics/src/font/source.rs b/graphics/src/font/source.rs
deleted file mode 100644
index c0b50e1d..00000000
--- a/graphics/src/font/source.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use crate::font::{Family, LoadError};
-
-/// A font source that can find and load system fonts.
-#[allow(missing_debug_implementations)]
-pub struct Source {
- raw: font_kit::source::SystemSource,
-}
-
-impl Source {
- /// Creates a new [`Source`].
- pub fn new() -> Self {
- Source {
- raw: font_kit::source::SystemSource::new(),
- }
- }
-
- /// Finds and loads a font matching the set of provided family priorities.
- pub fn load(&self, families: &[Family]) -> Result<Vec<u8>, LoadError> {
- let font = self.raw.select_best_match(
- families,
- &font_kit::properties::Properties::default(),
- )?;
-
- match font {
- font_kit::handle::Handle::Path { path, .. } => {
- use std::io::Read;
-
- let mut buf = Vec::new();
- let mut reader = std::fs::File::open(path).expect("Read font");
- let _ = reader.read_to_end(&mut buf);
-
- Ok(buf)
- }
- font_kit::handle::Handle::Memory { bytes, .. } => {
- Ok(bytes.as_ref().clone())
- }
- }
- }
-}
-
-impl Default for Source {
- fn default() -> Self {
- Self::new()
- }
-}
diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs
index 1d453caa..f6eb2fdd 100644
--- a/graphics/src/layer.rs
+++ b/graphics/src/layer.rs
@@ -12,7 +12,8 @@ pub use text::Text;
use crate::alignment;
use crate::{
- Background, Font, Point, Primitive, Rectangle, Size, Vector, Viewport,
+ Background, Color, Font, Point, Primitive, Rectangle, Size, Vector,
+ Viewport,
};
/// A group of primitives that should be clipped together.
@@ -60,9 +61,9 @@ impl<'a> Layer<'a> {
Point::new(11.0, 11.0 + 25.0 * i as f32),
Size::INFINITY,
),
- color: [0.9, 0.9, 0.9, 1.0],
+ color: Color::new(0.9, 0.9, 0.9, 1.0),
size: 20.0,
- font: Font::Default,
+ font: Font::Monospace,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
};
@@ -71,7 +72,7 @@ impl<'a> Layer<'a> {
overlay.text.push(Text {
bounds: text.bounds + Vector::new(-1.0, -1.0),
- color: [0.0, 0.0, 0.0, 1.0],
+ color: Color::BLACK,
..text
});
}
@@ -136,7 +137,7 @@ impl<'a> Layer<'a> {
content,
bounds: *bounds + translation,
size: *size,
- color: color.into_linear(),
+ color: *color,
font: *font,
horizontal_alignment: *horizontal_alignment,
vertical_alignment: *vertical_alignment,
diff --git a/graphics/src/layer/text.rs b/graphics/src/layer/text.rs
index 74f7a676..38d62616 100644
--- a/graphics/src/layer/text.rs
+++ b/graphics/src/layer/text.rs
@@ -1,4 +1,4 @@
-use crate::{alignment, Font, Rectangle};
+use crate::{alignment, Color, Font, Rectangle};
/// A paragraph of text.
#[derive(Debug, Clone, Copy)]
@@ -10,7 +10,7 @@ pub struct Text<'a> {
pub bounds: Rectangle,
/// The color of the [`Text`], in __linear RGB_.
- pub color: [f32; 4],
+ pub color: Color,
/// The size of the [`Text`].
pub size: f32,
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index d39dd90c..41bef2c3 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -28,7 +28,6 @@ mod transformation;
mod viewport;
pub mod backend;
-pub mod font;
pub mod gradient;
pub mod image;
pub mod layer;
diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs
index 34b6eb1d..b052c094 100644
--- a/graphics/src/renderer.rs
+++ b/graphics/src/renderer.rs
@@ -10,6 +10,7 @@ use iced_native::{Background, Color, Element, Font, Point, Rectangle, Size};
pub use iced_native::renderer::Style;
+use std::borrow::Cow;
use std::marker::PhantomData;
/// A backend-agnostic renderer that supports all the built-in widgets.
@@ -130,6 +131,10 @@ where
const CHECKMARK_ICON: char = B::CHECKMARK_ICON;
const ARROW_DOWN_ICON: char = B::ARROW_DOWN_ICON;
+ fn default_font(&self) -> Self::Font {
+ self.backend().default_font()
+ }
+
fn default_size(&self) -> f32 {
self.backend().default_size()
}
@@ -163,6 +168,10 @@ where
)
}
+ fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
+ self.backend.load_font(bytes);
+ }
+
fn fill_text(&mut self, text: Text<'_, Self::Font>) {
self.primitives.push(Primitive::Text {
content: text.content.to_string(),
diff --git a/graphics/src/widget/canvas/text.rs b/graphics/src/widget/canvas/text.rs
index 056f8204..8c0b2dfb 100644
--- a/graphics/src/widget/canvas/text.rs
+++ b/graphics/src/widget/canvas/text.rs
@@ -34,7 +34,7 @@ impl Default for Text {
position: Point::ORIGIN,
color: Color::BLACK,
size: 16.0,
- font: Font::Default,
+ font: Font::SansSerif,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
}
diff --git a/native/src/command/action.rs b/native/src/command/action.rs
index a51b8c21..d1589c05 100644
--- a/native/src/command/action.rs
+++ b/native/src/command/action.rs
@@ -1,10 +1,12 @@
use crate::clipboard;
+use crate::font;
use crate::system;
use crate::widget;
use crate::window;
use iced_futures::MaybeSend;
+use std::borrow::Cow;
use std::fmt;
/// An action that a [`Command`] can perform.
@@ -27,6 +29,15 @@ pub enum Action<T> {
/// Run a widget action.
Widget(widget::Action<T>),
+
+ /// Load a font from its bytes.
+ LoadFont {
+ /// The bytes of the font to load.
+ bytes: Cow<'static, [u8]>,
+
+ /// The message to produce when the font has been loaded.
+ tagger: Box<dyn Fn(Result<(), font::Error>) -> T>,
+ },
}
impl<T> Action<T> {
@@ -49,6 +60,10 @@ impl<T> Action<T> {
Self::Window(window) => Action::Window(window.map(f)),
Self::System(system) => Action::System(system.map(f)),
Self::Widget(widget) => Action::Widget(widget.map(f)),
+ Self::LoadFont { bytes, tagger } => Action::LoadFont {
+ bytes,
+ tagger: Box::new(move |result| f(tagger(result))),
+ },
}
}
}
@@ -63,6 +78,7 @@ impl<T> fmt::Debug for Action<T> {
Self::Window(action) => write!(f, "Action::Window({action:?})"),
Self::System(action) => write!(f, "Action::System({action:?})"),
Self::Widget(_action) => write!(f, "Action::Widget"),
+ Self::LoadFont { .. } => write!(f, "Action::LoadFont"),
}
}
}
diff --git a/native/src/font.rs b/native/src/font.rs
new file mode 100644
index 00000000..15359694
--- /dev/null
+++ b/native/src/font.rs
@@ -0,0 +1,19 @@
+//! Load and use fonts.
+pub use iced_core::font::*;
+
+use crate::command::{self, Command};
+use std::borrow::Cow;
+
+/// An error while loading a font.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Error {}
+
+/// Load a font from its bytes.
+pub fn load(
+ bytes: impl Into<Cow<'static, [u8]>>,
+) -> Command<Result<(), Error>> {
+ Command::single(command::Action::LoadFont {
+ bytes: bytes.into(),
+ tagger: Box::new(std::convert::identity),
+ })
+}
diff --git a/native/src/lib.rs b/native/src/lib.rs
index ebdc8490..27b6fc0d 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -47,6 +47,7 @@
pub mod clipboard;
pub mod command;
pub mod event;
+pub mod font;
pub mod image;
pub mod keyboard;
pub mod layout;
@@ -80,8 +81,8 @@ mod debug;
pub use iced_core::alignment;
pub use iced_core::time;
pub use iced_core::{
- color, Alignment, Background, Color, ContentFit, Font, Length, Padding,
- Pixels, Point, Rectangle, Size, Vector,
+ color, Alignment, Background, Color, ContentFit, Length, Padding, Pixels,
+ Point, Rectangle, Size, Vector,
};
pub use iced_futures::{executor, futures};
pub use iced_style::application;
@@ -95,6 +96,7 @@ pub use command::Command;
pub use debug::Debug;
pub use element::Element;
pub use event::Event;
+pub use font::Font;
pub use hasher::Hasher;
pub use layout::Layout;
pub use overlay::Overlay;
diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs
index 50f741ef..bd58a122 100644
--- a/native/src/overlay/menu.rs
+++ b/native/src/overlay/menu.rs
@@ -31,7 +31,7 @@ where
width: f32,
padding: Padding,
text_size: Option<f32>,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -58,7 +58,7 @@ where
width: 0.0,
padding: Padding::ZERO,
text_size: None,
- font: Default::default(),
+ font: None,
style: Default::default(),
}
}
@@ -82,8 +82,8 @@ where
}
/// Sets the font of the [`Menu`].
- pub fn font(mut self, font: Renderer::Font) -> Self {
- self.font = font;
+ pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
+ self.font = Some(font.into());
self
}
@@ -311,7 +311,7 @@ where
last_selection: &'a mut Option<T>,
padding: Padding,
text_size: Option<f32>,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -344,7 +344,7 @@ where
let size = {
let intrinsic = Size::new(
0.0,
- (text_size + self.padding.vertical())
+ (text_size * 1.2 + self.padding.vertical())
* self.options.len() as f32,
);
@@ -386,7 +386,7 @@ where
*self.hovered_option = Some(
((cursor_position.y - bounds.y)
- / (text_size + self.padding.vertical()))
+ / (text_size * 1.2 + self.padding.vertical()))
as usize,
);
}
@@ -401,7 +401,7 @@ where
*self.hovered_option = Some(
((cursor_position.y - bounds.y)
- / (text_size + self.padding.vertical()))
+ / (text_size * 1.2 + self.padding.vertical()))
as usize,
);
@@ -450,7 +450,8 @@ where
let text_size =
self.text_size.unwrap_or_else(|| renderer.default_size());
- let option_height = (text_size + self.padding.vertical()) as usize;
+ let option_height =
+ (text_size * 1.2 + self.padding.vertical()) as usize;
let offset = viewport.y - bounds.y;
let start = (offset / option_height as f32) as usize;
@@ -467,7 +468,7 @@ where
x: bounds.x,
y: bounds.y + (option_height * i) as f32,
width: bounds.width,
- height: text_size + self.padding.vertical(),
+ height: text_size * 1.2 + self.padding.vertical(),
};
if is_selected {
@@ -491,7 +492,7 @@ where
..bounds
},
size: text_size,
- font: self.font.clone(),
+ font: self.font.unwrap_or_else(|| renderer.default_font()),
color: if is_selected {
appearance.selected_text_color
} else {
diff --git a/native/src/program.rs b/native/src/program.rs
index c71c237f..25cab332 100644
--- a/native/src/program.rs
+++ b/native/src/program.rs
@@ -1,4 +1,5 @@
//! Build interactive programs using The Elm Architecture.
+use crate::text;
use crate::{Command, Element, Renderer};
mod state;
@@ -8,7 +9,7 @@ pub use state::State;
/// The core of a user interface application following The Elm Architecture.
pub trait Program: Sized {
/// The graphics backend to use to draw the [`Program`].
- type Renderer: Renderer;
+ type Renderer: Renderer + text::Renderer;
/// The type of __messages__ your [`Program`] will produce.
type Message: std::fmt::Debug + Send;
diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs
index 9376d540..150ee786 100644
--- a/native/src/renderer/null.rs
+++ b/native/src/renderer/null.rs
@@ -2,6 +2,8 @@ use crate::renderer::{self, Renderer};
use crate::text::{self, Text};
use crate::{Background, Font, Point, Rectangle, Size, Theme, Vector};
+use std::borrow::Cow;
+
/// A renderer that does nothing.
///
/// It can be useful if you are writing tests!
@@ -40,14 +42,20 @@ impl Renderer for Null {
impl text::Renderer for Null {
type Font = Font;
- const ICON_FONT: Font = Font::Default;
+ const ICON_FONT: Font = Font::SansSerif;
const CHECKMARK_ICON: char = '0';
const ARROW_DOWN_ICON: char = '0';
+ fn default_font(&self) -> Self::Font {
+ Font::SansSerif
+ }
+
fn default_size(&self) -> f32 {
- 20.0
+ 16.0
}
+ fn load_font(&mut self, _font: Cow<'static, [u8]>) {}
+
fn measure(
&self,
_content: &str,
diff --git a/native/src/text.rs b/native/src/text.rs
index 55c3cfd3..4c72abc3 100644
--- a/native/src/text.rs
+++ b/native/src/text.rs
@@ -1,6 +1,8 @@
//! Draw and interact with text.
use crate::alignment;
-use crate::{Color, Point, Rectangle, Size, Vector};
+use crate::{Color, Point, Rectangle, Size};
+
+use std::borrow::Cow;
/// A paragraph.
#[derive(Debug, Clone, Copy)]
@@ -32,10 +34,6 @@ pub struct Text<'a, Font> {
pub enum Hit {
/// The point was within the bounds of the returned character index.
CharOffset(usize),
- /// The provided point was not within the bounds of a glyph. The index
- /// of the character with the closest centeroid position is returned,
- /// as well as its delta.
- NearestCharOffset(usize, Vector),
}
impl Hit {
@@ -43,13 +41,6 @@ impl Hit {
pub fn cursor(self) -> usize {
match self {
Self::CharOffset(i) => i,
- Self::NearestCharOffset(i, delta) => {
- if delta.x > f32::EPSILON {
- i + 1
- } else {
- i
- }
- }
}
}
}
@@ -57,7 +48,7 @@ impl Hit {
/// A renderer capable of measuring and drawing [`Text`].
pub trait Renderer: crate::Renderer {
/// The font type used.
- type Font: Default + Clone;
+ type Font: Copy;
/// The icon font of the backend.
const ICON_FONT: Self::Font;
@@ -72,6 +63,9 @@ pub trait Renderer: crate::Renderer {
/// [`ICON_FONT`]: Self::ICON_FONT
const ARROW_DOWN_ICON: char;
+ /// Returns the default [`Self::Font`].
+ fn default_font(&self) -> Self::Font;
+
/// Returns the default size of [`Text`].
fn default_size(&self) -> f32;
@@ -109,6 +103,9 @@ pub trait Renderer: crate::Renderer {
nearest_only: bool,
) -> Option<Hit>;
+ /// Loads a [`Self::Font`] from its bytes.
+ fn load_font(&mut self, font: Cow<'static, [u8]>);
+
/// Draws the given [`Text`].
fn fill_text(&mut self, text: Text<'_, Self::Font>);
}
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index 9b69e574..cd8b9c6b 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -55,7 +55,7 @@ where
size: f32,
spacing: f32,
text_size: Option<f32>,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
icon: Icon<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -91,7 +91,7 @@ where
size: Self::DEFAULT_SIZE,
spacing: Self::DEFAULT_SPACING,
text_size: None,
- font: Renderer::Font::default(),
+ font: None,
icon: Icon {
font: Renderer::ICON_FONT,
code_point: Renderer::CHECKMARK_ICON,
@@ -128,8 +128,8 @@ where
/// Sets the [`Font`] of the text of the [`Checkbox`].
///
/// [`Font`]: crate::text::Renderer::Font
- pub fn font(mut self, font: Renderer::Font) -> Self {
- self.font = font;
+ pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
+ self.font = Some(font.into());
self
}
@@ -175,7 +175,7 @@ where
.push(Row::new().width(self.size).height(self.size))
.push(
Text::new(&self.label)
- .font(self.font.clone())
+ .font(self.font.unwrap_or_else(|| renderer.default_font()))
.width(self.width)
.size(
self.text_size
@@ -267,12 +267,12 @@ where
code_point,
size,
} = &self.icon;
- let size = size.map(f32::from).unwrap_or(bounds.height * 0.7);
+ let size = size.unwrap_or(bounds.height * 0.7);
if self.is_checked {
renderer.fill_text(text::Text {
content: &code_point.to_string(),
- font: font.clone(),
+ font: *font,
size,
bounds: Rectangle {
x: bounds.center_x(),
@@ -295,7 +295,7 @@ where
label_layout,
&self.label,
self.text_size,
- self.font.clone(),
+ self.font,
widget::text::Appearance {
color: custom_style.text_color,
},
diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs
index 17528db4..8ff82f3b 100644
--- a/native/src/widget/pick_list.rs
+++ b/native/src/widget/pick_list.rs
@@ -35,7 +35,7 @@ where
width: Length,
padding: Padding,
text_size: Option<f32>,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
handle: Handle<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -70,7 +70,7 @@ where
width: Length::Shrink,
padding: Self::DEFAULT_PADDING,
text_size: None,
- font: Default::default(),
+ font: None,
handle: Default::default(),
style: Default::default(),
}
@@ -101,8 +101,8 @@ where
}
/// Sets the font of the [`PickList`].
- pub fn font(mut self, font: Renderer::Font) -> Self {
- self.font = font;
+ pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
+ self.font = Some(font.into());
self
}
@@ -163,7 +163,7 @@ where
self.width,
self.padding,
self.text_size,
- &self.font,
+ self.font,
self.placeholder.as_deref(),
&self.options,
)
@@ -212,6 +212,7 @@ where
cursor_position: Point,
_viewport: &Rectangle,
) {
+ let font = self.font.unwrap_or_else(|| renderer.default_font());
draw(
renderer,
theme,
@@ -219,7 +220,7 @@ where
cursor_position,
self.padding,
self.text_size,
- &self.font,
+ font,
self.placeholder.as_deref(),
self.selected.as_ref(),
&self.handle,
@@ -232,7 +233,7 @@ where
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
- _renderer: &Renderer,
+ renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
let state = tree.state.downcast_mut::<State<T>>();
@@ -241,7 +242,7 @@ where
state,
self.padding,
self.text_size,
- self.font.clone(),
+ self.font.unwrap_or_else(|| renderer.default_font()),
&self.options,
self.style.clone(),
)
@@ -343,7 +344,7 @@ pub fn layout<Renderer, T>(
width: Length,
padding: Padding,
text_size: Option<f32>,
- font: &Renderer::Font,
+ font: Option<Renderer::Font>,
placeholder: Option<&str>,
options: &[T],
) -> layout::Node
@@ -362,7 +363,7 @@ where
let (width, _) = renderer.measure(
label,
text_size,
- font.clone(),
+ font.unwrap_or_else(|| renderer.default_font()),
Size::new(f32::INFINITY, f32::INFINITY),
);
@@ -384,7 +385,7 @@ where
let size = {
let intrinsic =
- Size::new(max_width + text_size + padding.left, text_size);
+ Size::new(max_width + text_size + padding.left, text_size * 1.2);
limits.resolve(intrinsic).pad(padding)
};
@@ -560,7 +561,7 @@ pub fn draw<'a, T, Renderer>(
cursor_position: Point,
padding: Padding,
text_size: Option<f32>,
- font: &Renderer::Font,
+ font: Renderer::Font,
placeholder: Option<&str>,
selected: Option<&T>,
handle: &Handle<Renderer::Font>,
@@ -599,12 +600,12 @@ pub fn draw<'a, T, Renderer>(
font,
code_point,
size,
- }) => Some((font.clone(), *code_point, *size)),
+ }) => Some((*font, *code_point, *size)),
Handle::Dynamic { open, closed } => {
if state().is_open {
- Some((open.font.clone(), open.code_point, open.size))
+ Some((open.font, open.code_point, open.size))
} else {
- Some((closed.font.clone(), closed.code_point, closed.size))
+ Some((closed.font, closed.code_point, closed.size))
}
}
Handle::None => None,
@@ -620,12 +621,12 @@ pub fn draw<'a, T, Renderer>(
color: style.handle_color,
bounds: Rectangle {
x: bounds.x + bounds.width - padding.horizontal(),
- y: bounds.center_y() - size / 2.0,
- height: size,
+ y: bounds.center_y(),
+ height: size * 1.2,
..bounds
},
horizontal_alignment: alignment::Horizontal::Right,
- vertical_alignment: alignment::Vertical::Top,
+ vertical_alignment: alignment::Vertical::Center,
});
}
@@ -637,7 +638,7 @@ pub fn draw<'a, T, Renderer>(
renderer.fill_text(Text {
content: label,
size: text_size,
- font: font.clone(),
+ font,
color: if is_selected {
style.text_color
} else {
@@ -645,12 +646,12 @@ pub fn draw<'a, T, Renderer>(
},
bounds: Rectangle {
x: bounds.x + padding.left,
- y: bounds.center_y() - text_size / 2.0,
+ y: bounds.center_y(),
width: bounds.width - padding.horizontal(),
- height: text_size,
+ height: text_size * 1.2,
},
horizontal_alignment: alignment::Horizontal::Left,
- vertical_alignment: alignment::Vertical::Top,
+ vertical_alignment: alignment::Vertical::Center,
});
}
}
diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs
index 9daddfbc..5f60eaef 100644
--- a/native/src/widget/radio.rs
+++ b/native/src/widget/radio.rs
@@ -53,7 +53,7 @@ where
size: f32,
spacing: f32,
text_size: Option<f32>,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -95,7 +95,7 @@ where
size: Self::DEFAULT_SIZE,
spacing: Self::DEFAULT_SPACING, //15
text_size: None,
- font: Default::default(),
+ font: None,
style: Default::default(),
}
}
@@ -125,8 +125,8 @@ where
}
/// Sets the text font of the [`Radio`] button.
- pub fn font(mut self, font: Renderer::Font) -> Self {
- self.font = font;
+ pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
+ self.font = Some(font.into());
self
}
@@ -275,7 +275,7 @@ where
label_layout,
&self.label,
self.text_size,
- self.font.clone(),
+ self.font,
widget::text::Appearance {
color: custom_style.text_color,
},
diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs
index 3fee48f2..aede754a 100644
--- a/native/src/widget/text.rs
+++ b/native/src/widget/text.rs
@@ -37,7 +37,7 @@ where
height: Length,
horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -51,7 +51,7 @@ where
Text {
content: content.into(),
size: None,
- font: Default::default(),
+ font: None,
width: Length::Shrink,
height: Length::Shrink,
horizontal_alignment: alignment::Horizontal::Left,
@@ -70,7 +70,7 @@ where
///
/// [`Font`]: crate::text::Renderer::Font
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
- self.font = font.into();
+ self.font = Some(font.into());
self
}
@@ -138,8 +138,12 @@ where
let bounds = limits.max();
- let (width, height) =
- renderer.measure(&self.content, size, self.font.clone(), bounds);
+ let (width, height) = renderer.measure(
+ &self.content,
+ size,
+ self.font.unwrap_or_else(|| renderer.default_font()),
+ bounds,
+ );
let size = limits.resolve(Size::new(width, height));
@@ -162,7 +166,7 @@ where
layout,
&self.content,
self.size,
- self.font.clone(),
+ self.font,
theme.appearance(self.style),
self.horizontal_alignment,
self.vertical_alignment,
@@ -186,7 +190,7 @@ pub fn draw<Renderer>(
layout: Layout<'_>,
content: &str,
size: Option<f32>,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
appearance: Appearance,
horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical,
@@ -212,7 +216,7 @@ pub fn draw<Renderer>(
size: size.unwrap_or_else(|| renderer.default_size()),
bounds: Rectangle { x, y, ..bounds },
color: appearance.color.unwrap_or(style.text_color),
- font,
+ font: font.unwrap_or_else(|| renderer.default_font()),
horizontal_alignment,
vertical_alignment,
});
@@ -242,7 +246,7 @@ where
height: self.height,
horizontal_alignment: self.horizontal_alignment,
vertical_alignment: self.vertical_alignment,
- font: self.font.clone(),
+ font: self.font,
style: self.style,
}
}
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index ee0473ea..65a9bd3b 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -61,7 +61,7 @@ where
placeholder: String,
value: Value,
is_secure: bool,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
width: Length,
padding: Padding,
size: Option<f32>,
@@ -92,7 +92,7 @@ where
placeholder: String::from(placeholder),
value: Value::new(value),
is_secure: false,
- font: Default::default(),
+ font: None,
width: Length::Fill,
padding: Padding::new(5.0),
size: None,
@@ -129,7 +129,7 @@ where
///
/// [`Font`]: text::Renderer::Font
pub fn font(mut self, font: Renderer::Font) -> Self {
- self.font = font;
+ self.font = Some(font);
self
}
/// Sets the width of the [`TextInput`].
@@ -188,7 +188,7 @@ where
value.unwrap_or(&self.value),
&self.placeholder,
self.size,
- &self.font,
+ self.font,
self.is_secure,
&self.style,
)
@@ -258,7 +258,7 @@ where
shell,
&mut self.value,
self.size,
- &self.font,
+ self.font,
self.is_secure,
self.on_change.as_ref(),
self.on_paste.as_deref(),
@@ -286,7 +286,7 @@ where
&self.value,
&self.placeholder,
self.size,
- &self.font,
+ self.font,
self.is_secure,
&self.style,
)
@@ -385,9 +385,8 @@ where
Renderer: text::Renderer,
{
let text_size = size.unwrap_or_else(|| renderer.default_size());
-
let padding = padding.fit(Size::ZERO, limits.max());
- let limits = limits.width(width).pad(padding).height(text_size);
+ let limits = limits.width(width).pad(padding).height(text_size * 1.2);
let mut text = layout::Node::new(limits.resolve(Size::ZERO));
text.move_to(Point::new(padding.left, padding.top));
@@ -406,7 +405,7 @@ pub fn update<'a, Message, Renderer>(
shell: &mut Shell<'_, Message>,
value: &mut Value,
size: Option<f32>,
- font: &Renderer::Font,
+ font: Option<Renderer::Font>,
is_secure: bool,
on_change: &dyn Fn(String) -> Message,
on_paste: Option<&dyn Fn(String) -> Message>,
@@ -455,7 +454,7 @@ where
find_cursor_position(
renderer,
text_layout.bounds(),
- font.clone(),
+ font,
size,
&value,
state,
@@ -483,7 +482,7 @@ where
let position = find_cursor_position(
renderer,
text_layout.bounds(),
- font.clone(),
+ font,
size,
value,
state,
@@ -532,7 +531,7 @@ where
let position = find_cursor_position(
renderer,
text_layout.bounds(),
- font.clone(),
+ font,
size,
&value,
state,
@@ -812,7 +811,7 @@ pub fn draw<Renderer>(
value: &Value,
placeholder: &str,
size: Option<f32>,
- font: &Renderer::Font,
+ font: Option<Renderer::Font>,
is_secure: bool,
style: &<Renderer::Theme as StyleSheet>::Style,
) where
@@ -846,6 +845,7 @@ pub fn draw<Renderer>(
);
let text = value.to_string();
+ let font = font.unwrap_or_else(|| renderer.default_font());
let size = size.unwrap_or_else(|| renderer.default_size());
let (cursor, offset) = if let Some(focus) = &state.is_focused {
@@ -858,7 +858,7 @@ pub fn draw<Renderer>(
value,
size,
position,
- font.clone(),
+ font,
);
let is_cursor_visible = ((focus.now - focus.updated_at)
@@ -899,7 +899,7 @@ pub fn draw<Renderer>(
value,
size,
left,
- font.clone(),
+ font,
);
let (right_position, right_offset) =
@@ -909,7 +909,7 @@ pub fn draw<Renderer>(
value,
size,
right,
- font.clone(),
+ font,
);
let width = right_position - left_position;
@@ -944,7 +944,7 @@ pub fn draw<Renderer>(
let text_width = renderer.measure_width(
if text.is_empty() { placeholder } else { &text },
size,
- font.clone(),
+ font,
);
let render = |renderer: &mut Renderer| {
@@ -959,7 +959,7 @@ pub fn draw<Renderer>(
} else {
theme.value_color(style)
},
- font: font.clone(),
+ font,
bounds: Rectangle {
y: text_bounds.center_y(),
width: f32::INFINITY,
@@ -1180,7 +1180,7 @@ where
fn find_cursor_position<Renderer>(
renderer: &Renderer,
text_bounds: Rectangle,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
size: Option<f32>,
value: &Value,
state: &State,
@@ -1189,21 +1189,30 @@ fn find_cursor_position<Renderer>(
where
Renderer: text::Renderer,
{
+ let font = font.unwrap_or_else(|| renderer.default_font());
let size = size.unwrap_or_else(|| renderer.default_size());
- let offset =
- offset(renderer, text_bounds, font.clone(), size, value, state);
+ let offset = offset(renderer, text_bounds, font, size, value, state);
+ let value = value.to_string();
- renderer
+ let char_offset = renderer
.hit_test(
- &value.to_string(),
+ &value,
size,
font,
Size::INFINITY,
Point::new(x + offset, text_bounds.height / 2.0),
true,
)
- .map(text::Hit::cursor)
+ .map(text::Hit::cursor)?;
+
+ Some(
+ unicode_segmentation::UnicodeSegmentation::graphemes(
+ &value[..char_offset],
+ true,
+ )
+ .count(),
+ )
}
const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500;
diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs
index a434af65..d9c80ebe 100644
--- a/native/src/widget/toggler.rs
+++ b/native/src/widget/toggler.rs
@@ -42,7 +42,7 @@ where
text_size: Option<f32>,
text_alignment: alignment::Horizontal,
spacing: f32,
- font: Renderer::Font,
+ font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -79,7 +79,7 @@ where
text_size: None,
text_alignment: alignment::Horizontal::Left,
spacing: 0.0,
- font: Renderer::Font::default(),
+ font: None,
style: Default::default(),
}
}
@@ -117,8 +117,8 @@ where
/// Sets the [`Font`] of the text of the [`Toggler`]
///
/// [`Font`]: crate::text::Renderer::Font
- pub fn font(mut self, font: Renderer::Font) -> Self {
- self.font = font;
+ pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
+ self.font = Some(font.into());
self
}
@@ -160,7 +160,7 @@ where
row = row.push(
Text::new(label)
.horizontal_alignment(self.text_alignment)
- .font(self.font.clone())
+ .font(self.font.unwrap_or_else(|| renderer.default_font()))
.width(self.width)
.size(
self.text_size
@@ -243,7 +243,7 @@ where
label_layout,
label,
self.text_size,
- self.font.clone(),
+ self.font,
Default::default(),
self.text_alignment,
alignment::Vertical::Center,
diff --git a/src/application.rs b/src/application.rs
index 1db5c93f..9a1c1855 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -197,7 +197,6 @@ pub trait Application: Sized {
let renderer_settings = crate::renderer::Settings {
default_font: settings.default_font,
default_text_size: settings.default_text_size,
- text_multithreading: settings.text_multithreading,
antialiasing: if settings.antialiasing {
Some(crate::renderer::settings::Antialiasing::MSAAx4)
} else {
diff --git a/src/lib.rs b/src/lib.rs
index 318852f9..31ec4f48 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -182,20 +182,12 @@ pub mod touch;
pub mod widget;
pub mod window;
-#[cfg(all(not(feature = "glow"), feature = "wgpu"))]
-use iced_winit as runtime;
-
-#[cfg(feature = "glow")]
-use iced_glutin as runtime;
-
-#[cfg(all(not(feature = "glow"), feature = "wgpu"))]
use iced_wgpu as renderer;
-
-#[cfg(feature = "glow")]
-use iced_glow as renderer;
+use iced_winit as runtime;
pub use iced_native::theme;
pub use runtime::event;
+pub use runtime::font;
pub use runtime::subscription;
pub use application::Application;
@@ -203,6 +195,7 @@ pub use element::Element;
pub use error::Error;
pub use event::Event;
pub use executor::Executor;
+pub use font::Font;
pub use renderer::Renderer;
pub use result::Result;
pub use sandbox::Sandbox;
@@ -213,8 +206,8 @@ pub use theme::Theme;
pub use runtime::alignment;
pub use runtime::futures;
pub use runtime::{
- color, Alignment, Background, Color, Command, ContentFit, Font, Length,
- Padding, Point, Rectangle, Size, Vector,
+ color, Alignment, Background, Color, Command, ContentFit, Length, Padding,
+ Point, Rectangle, Size, Vector,
};
#[cfg(feature = "system")]
diff --git a/src/settings.rs b/src/settings.rs
index 0eb3e62d..13b3af7c 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -1,5 +1,6 @@
//! Configure your application.
use crate::window;
+use crate::Font;
/// The settings of an application.
#[derive(Debug, Clone)]
@@ -20,23 +21,16 @@ pub struct Settings<Flags> {
/// [`Application`]: crate::Application
pub flags: Flags,
- /// The bytes of the font that will be used by default.
+ /// The default [`Font`] to be used.
///
- /// If `None` is provided, a default system font will be chosen.
- // TODO: Add `name` for web compatibility
- pub default_font: Option<&'static [u8]>,
+ /// By default, it uses [`Font::SansSerif`].
+ pub default_font: Font,
/// The text size that will be used by default.
///
- /// The default value is `20.0`.
+ /// The default value is `16.0`.
pub default_text_size: f32,
- /// If enabled, spread text workload in multiple threads when multiple cores
- /// are available.
- ///
- /// By default, it is disabled.
- pub text_multithreading: bool,
-
/// If set to true, the renderer will try to perform antialiasing for some
/// primitives.
///
@@ -55,15 +49,6 @@ pub struct Settings<Flags> {
///
/// [`Application`]: crate::Application
pub exit_on_close_request: bool,
-
- /// Whether the [`Application`] should try to build the context
- /// using OpenGL ES first then OpenGL.
- ///
- /// By default, it is disabled.
- /// **Note:** Only works for the `glow` backend.
- ///
- /// [`Application`]: crate::Application
- pub try_opengles_first: bool,
}
impl<Flags> Settings<Flags> {
@@ -79,10 +64,8 @@ impl<Flags> Settings<Flags> {
window: default_settings.window,
default_font: default_settings.default_font,
default_text_size: default_settings.default_text_size,
- text_multithreading: default_settings.text_multithreading,
antialiasing: default_settings.antialiasing,
exit_on_close_request: default_settings.exit_on_close_request,
- try_opengles_first: default_settings.try_opengles_first,
}
}
}
@@ -96,12 +79,10 @@ where
id: None,
window: Default::default(),
flags: Default::default(),
- default_font: Default::default(),
- default_text_size: 20.0,
- text_multithreading: false,
+ default_font: Font::SansSerif,
+ default_text_size: 16.0,
antialiasing: false,
exit_on_close_request: true,
- try_opengles_first: false,
}
}
}
@@ -113,7 +94,6 @@ impl<Flags> From<Settings<Flags>> for iced_winit::Settings<Flags> {
window: settings.window.into(),
flags: settings.flags,
exit_on_close_request: settings.exit_on_close_request,
- try_opengles_first: settings.try_opengles_first,
}
}
}
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index f1e22cf6..632873a3 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -23,19 +23,27 @@ dds = ["iced_graphics/dds"]
farbfeld = ["iced_graphics/farbfeld"]
canvas = ["iced_graphics/canvas"]
qr_code = ["iced_graphics/qr_code"]
-default_system_font = ["iced_graphics/font-source"]
spirv = ["wgpu/spirv"]
webgl = ["wgpu/webgl"]
[dependencies]
wgpu = "0.14"
-wgpu_glyph = "0.18"
-glyph_brush = "0.7"
raw-window-handle = "0.5"
log = "0.4"
guillotiere = "0.6"
futures = "0.3"
bitflags = "1.2"
+once_cell = "1.0"
+rustc-hash = "1.1"
+ouroboros = "0.15"
+
+[dependencies.twox-hash]
+version = "1.6"
+default-features = false
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies.twox-hash]
+version = "1.6.1"
+features = ["std"]
[dependencies.bytemuck]
version = "1.9"
@@ -48,7 +56,11 @@ path = "../native"
[dependencies.iced_graphics]
version = "0.7"
path = "../graphics"
-features = ["font-fallback", "font-icons"]
+
+[dependencies.glyphon]
+version = "0.2"
+git = "https://github.com/hecrj/glyphon.git"
+rev = "65b481d758f50fd13fc21af2cc5ef62ddee64955"
[dependencies.tracing]
version = "0.1.6"
diff --git a/graphics/fonts/Icons.ttf b/wgpu/fonts/Iced-Icons.ttf
index 5e455b69..7112f086 100644
--- a/graphics/fonts/Icons.ttf
+++ b/wgpu/fonts/Iced-Icons.ttf
Binary files differ
diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 6a299425..e650d9a5 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -4,11 +4,8 @@ use crate::triangle;
use crate::{Settings, Transformation};
use iced_graphics::backend;
-use iced_graphics::font;
use iced_graphics::layer::Layer;
-use iced_graphics::{Primitive, Viewport};
-use iced_native::alignment;
-use iced_native::{Font, Size};
+use iced_graphics::{Color, Font, Primitive, Size, Viewport};
#[cfg(feature = "tracing")]
use tracing::info_span;
@@ -16,11 +13,13 @@ use tracing::info_span;
#[cfg(any(feature = "image", feature = "svg"))]
use crate::image;
+use std::borrow::Cow;
+
/// A [`wgpu`] graphics backend for [`iced`].
///
/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
/// [`iced`]: https://github.com/iced-rs/iced
-#[derive(Debug)]
+#[allow(missing_debug_implementations)]
pub struct Backend {
quad_pipeline: quad::Pipeline,
text_pipeline: text::Pipeline,
@@ -29,6 +28,7 @@ pub struct Backend {
#[cfg(any(feature = "image", feature = "svg"))]
image_pipeline: image::Pipeline,
+ default_font: Font,
default_text_size: f32,
}
@@ -36,16 +36,11 @@ impl Backend {
/// Creates a new [`Backend`].
pub fn new(
device: &wgpu::Device,
+ queue: &wgpu::Queue,
settings: Settings,
format: wgpu::TextureFormat,
) -> Self {
- let text_pipeline = text::Pipeline::new(
- device,
- format,
- settings.default_font,
- settings.text_multithreading,
- );
-
+ let text_pipeline = text::Pipeline::new(device, queue, format);
let quad_pipeline = quad::Pipeline::new(device, format);
let triangle_pipeline =
triangle::Pipeline::new(device, format, settings.antialiasing);
@@ -61,6 +56,7 @@ impl Backend {
#[cfg(any(feature = "image", feature = "svg"))]
image_pipeline,
+ default_font: settings.default_font,
default_text_size: settings.default_text_size,
}
}
@@ -72,8 +68,9 @@ impl Backend {
pub fn present<T: AsRef<str>>(
&mut self,
device: &wgpu::Device,
- staging_belt: &mut wgpu::util::StagingBelt,
+ queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
+ clear_color: Option<Color>,
frame: &wgpu::TextureView,
primitives: &[Primitive],
viewport: &Viewport,
@@ -90,167 +87,245 @@ impl Backend {
let mut layers = Layer::generate(primitives, viewport);
layers.push(Layer::overlay(overlay_text, viewport));
+ self.prepare(
+ device,
+ queue,
+ encoder,
+ scale_factor,
+ transformation,
+ &layers,
+ );
+
+ while !self.prepare_text(
+ device,
+ queue,
+ scale_factor,
+ target_size,
+ &layers,
+ ) {}
+
+ self.render(
+ device,
+ encoder,
+ frame,
+ clear_color,
+ scale_factor,
+ target_size,
+ &layers,
+ );
+
+ self.quad_pipeline.end_frame();
+ self.text_pipeline.end_frame();
+ self.triangle_pipeline.end_frame();
+
+ #[cfg(any(feature = "image", feature = "svg"))]
+ self.image_pipeline.end_frame(device, queue, encoder);
+ }
+
+ fn prepare_text(
+ &mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ scale_factor: f32,
+ target_size: Size<u32>,
+ layers: &[Layer<'_>],
+ ) -> bool {
for layer in layers {
- self.flush(
- device,
- scale_factor,
- transformation,
- &layer,
- staging_belt,
- encoder,
- frame,
- target_size,
- );
+ let bounds = (layer.bounds * scale_factor).snap();
+
+ if bounds.width < 1 || bounds.height < 1 {
+ continue;
+ }
+
+ if !layer.text.is_empty()
+ && !self.text_pipeline.prepare(
+ device,
+ queue,
+ &layer.text,
+ layer.bounds,
+ scale_factor,
+ target_size,
+ )
+ {
+ return false;
+ }
}
- #[cfg(any(feature = "image", feature = "svg"))]
- self.image_pipeline.trim_cache(device, encoder);
+ true
}
- fn flush(
+ fn prepare(
&mut self,
device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ _encoder: &mut wgpu::CommandEncoder,
scale_factor: f32,
transformation: Transformation,
- layer: &Layer<'_>,
- staging_belt: &mut wgpu::util::StagingBelt,
+ layers: &[Layer<'_>],
+ ) {
+ for layer in layers {
+ let bounds = (layer.bounds * scale_factor).snap();
+
+ if bounds.width < 1 || bounds.height < 1 {
+ continue;
+ }
+
+ if !layer.quads.is_empty() {
+ self.quad_pipeline.prepare(
+ device,
+ queue,
+ &layer.quads,
+ transformation,
+ scale_factor,
+ );
+ }
+
+ if !layer.meshes.is_empty() {
+ let scaled = transformation
+ * Transformation::scale(scale_factor, scale_factor);
+
+ self.triangle_pipeline.prepare(
+ device,
+ queue,
+ &layer.meshes,
+ scaled,
+ );
+ }
+
+ #[cfg(any(feature = "image", feature = "svg"))]
+ {
+ if !layer.images.is_empty() {
+ let scaled = transformation
+ * Transformation::scale(scale_factor, scale_factor);
+
+ self.image_pipeline.prepare(
+ device,
+ queue,
+ _encoder,
+ &layer.images,
+ scaled,
+ scale_factor,
+ );
+ }
+ }
+ }
+ }
+
+ fn render(
+ &mut self,
+ device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
+ clear_color: Option<Color>,
+ scale_factor: f32,
target_size: Size<u32>,
+ layers: &[Layer<'_>],
) {
- let bounds = (layer.bounds * scale_factor).snap();
+ use std::mem::ManuallyDrop;
- if bounds.width < 1 || bounds.height < 1 {
- return;
- }
+ let mut quad_layer = 0;
+ let mut triangle_layer = 0;
+ #[cfg(any(feature = "image", feature = "svg"))]
+ let mut image_layer = 0;
+ let mut text_layer = 0;
+
+ let mut render_pass = ManuallyDrop::new(encoder.begin_render_pass(
+ &wgpu::RenderPassDescriptor {
+ label: Some("iced_wgpu::quad render pass"),
+ color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+ view: target,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: match clear_color {
+ Some(background_color) => wgpu::LoadOp::Clear({
+ let [r, g, b, a] =
+ background_color.into_linear();
+
+ wgpu::Color {
+ r: f64::from(r),
+ g: f64::from(g),
+ b: f64::from(b),
+ a: f64::from(a),
+ }
+ }),
+ None => wgpu::LoadOp::Load,
+ },
+ store: true,
+ },
+ })],
+ depth_stencil_attachment: None,
+ },
+ ));
- if !layer.quads.is_empty() {
- self.quad_pipeline.draw(
- device,
- staging_belt,
- encoder,
- &layer.quads,
- transformation,
- scale_factor,
- bounds,
- target,
- );
- }
+ for layer in layers {
+ let bounds = (layer.bounds * scale_factor).snap();
- if !layer.meshes.is_empty() {
- let scaled = transformation
- * Transformation::scale(scale_factor, scale_factor);
-
- self.triangle_pipeline.draw(
- device,
- staging_belt,
- encoder,
- target,
- target_size,
- scaled,
- scale_factor,
- &layer.meshes,
- );
- }
+ if bounds.width < 1 || bounds.height < 1 {
+ return;
+ }
- #[cfg(any(feature = "image", feature = "svg"))]
- {
- if !layer.images.is_empty() {
- let scaled = transformation
- * Transformation::scale(scale_factor, scale_factor);
+ if !layer.quads.is_empty() {
+ self.quad_pipeline
+ .render(quad_layer, bounds, &mut render_pass);
+
+ quad_layer += 1;
+ }
- self.image_pipeline.draw(
+ if !layer.meshes.is_empty() {
+ let _ = ManuallyDrop::into_inner(render_pass);
+
+ self.triangle_pipeline.render(
device,
- staging_belt,
encoder,
- &layer.images,
- scaled,
- bounds,
target,
+ triangle_layer,
+ target_size,
+ &layer.meshes,
scale_factor,
);
+
+ triangle_layer += 1;
+
+ render_pass = ManuallyDrop::new(encoder.begin_render_pass(
+ &wgpu::RenderPassDescriptor {
+ label: Some("iced_wgpu::quad render pass"),
+ color_attachments: &[Some(
+ wgpu::RenderPassColorAttachment {
+ view: target,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Load,
+ store: true,
+ },
+ },
+ )],
+ depth_stencil_attachment: None,
+ },
+ ));
}
- }
- if !layer.text.is_empty() {
- for text in layer.text.iter() {
- // Target physical coordinates directly to avoid blurry text
- let text = wgpu_glyph::Section {
- // TODO: We `round` here to avoid rerasterizing text when
- // its position changes slightly. This can make text feel a
- // bit "jumpy". We may be able to do better once we improve
- // our text rendering/caching pipeline.
- screen_position: (
- (text.bounds.x * scale_factor).round(),
- (text.bounds.y * scale_factor).round(),
- ),
- // TODO: Fix precision issues with some scale factors.
- //
- // The `ceil` here can cause some words to render on the
- // same line when they should not.
- //
- // Ideally, `wgpu_glyph` should be able to compute layout
- // using logical positions, and then apply the proper
- // scaling when rendering. This would ensure that both
- // measuring and rendering follow the same layout rules.
- bounds: (
- (text.bounds.width * scale_factor).ceil(),
- (text.bounds.height * scale_factor).ceil(),
- ),
- text: vec![wgpu_glyph::Text {
- text: text.content,
- scale: wgpu_glyph::ab_glyph::PxScale {
- x: text.size * scale_factor,
- y: text.size * scale_factor,
- },
- font_id: self.text_pipeline.find_font(text.font),
- extra: wgpu_glyph::Extra {
- color: text.color,
- z: 0.0,
- },
- }],
- layout: wgpu_glyph::Layout::default()
- .h_align(match text.horizontal_alignment {
- alignment::Horizontal::Left => {
- wgpu_glyph::HorizontalAlign::Left
- }
- alignment::Horizontal::Center => {
- wgpu_glyph::HorizontalAlign::Center
- }
- alignment::Horizontal::Right => {
- wgpu_glyph::HorizontalAlign::Right
- }
- })
- .v_align(match text.vertical_alignment {
- alignment::Vertical::Top => {
- wgpu_glyph::VerticalAlign::Top
- }
- alignment::Vertical::Center => {
- wgpu_glyph::VerticalAlign::Center
- }
- alignment::Vertical::Bottom => {
- wgpu_glyph::VerticalAlign::Bottom
- }
- }),
- };
-
- self.text_pipeline.queue(text);
+ #[cfg(any(feature = "image", feature = "svg"))]
+ {
+ if !layer.images.is_empty() {
+ self.image_pipeline.render(
+ image_layer,
+ bounds,
+ &mut render_pass,
+ );
+
+ image_layer += 1;
+ }
}
- self.text_pipeline.draw_queued(
- device,
- staging_belt,
- encoder,
- target,
- transformation,
- wgpu_glyph::Region {
- x: bounds.x,
- y: bounds.y,
- width: bounds.width,
- height: bounds.height,
- },
- );
+ if !layer.text.is_empty() {
+ self.text_pipeline
+ .render(text_layer, bounds, &mut render_pass);
+
+ text_layer += 1;
+ }
}
+
+ let _ = ManuallyDrop::into_inner(render_pass);
}
}
@@ -261,9 +336,13 @@ impl iced_graphics::Backend for Backend {
}
impl backend::Text for Backend {
- const ICON_FONT: Font = font::ICONS;
- const CHECKMARK_ICON: char = font::CHECKMARK_ICON;
- const ARROW_DOWN_ICON: char = font::ARROW_DOWN_ICON;
+ const ICON_FONT: Font = Font::Name("Iced-Icons");
+ const CHECKMARK_ICON: char = '\u{f00c}';
+ const ARROW_DOWN_ICON: char = '\u{e800}';
+
+ fn default_font(&self) -> Font {
+ self.default_font
+ }
fn default_size(&self) -> f32 {
self.default_text_size
@@ -297,6 +376,10 @@ impl backend::Text for Backend {
nearest_only,
)
}
+
+ fn load_font(&mut self, font: Cow<'static, [u8]>) {
+ self.text_pipeline.load_font(font);
+ }
}
#[cfg(feature = "image")]
diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs
index 7c092d0b..c210dd4e 100644
--- a/wgpu/src/buffer.rs
+++ b/wgpu/src/buffer.rs
@@ -1,3 +1,89 @@
//! Utilities for buffer operations.
pub mod dynamic;
pub mod r#static;
+
+use std::marker::PhantomData;
+use std::ops::RangeBounds;
+
+#[derive(Debug)]
+pub struct Buffer<T> {
+ label: &'static str,
+ size: u64,
+ usage: wgpu::BufferUsages,
+ raw: wgpu::Buffer,
+ type_: PhantomData<T>,
+}
+
+impl<T: bytemuck::Pod> Buffer<T> {
+ pub fn new(
+ device: &wgpu::Device,
+ label: &'static str,
+ amount: usize,
+ usage: wgpu::BufferUsages,
+ ) -> Self {
+ let size = next_copy_size::<T>(amount);
+
+ let raw = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some(label),
+ size,
+ usage,
+ mapped_at_creation: false,
+ });
+
+ Self {
+ label,
+ size,
+ usage,
+ raw,
+ type_: PhantomData,
+ }
+ }
+
+ pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool {
+ let new_size = (std::mem::size_of::<T>() * new_count) as u64;
+
+ if self.size < new_size {
+ self.raw = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some(self.label),
+ size: new_size,
+ usage: self.usage,
+ mapped_at_creation: false,
+ });
+
+ self.size = new_size;
+
+ true
+ } else {
+ false
+ }
+ }
+
+ pub fn write(
+ &self,
+ queue: &wgpu::Queue,
+ offset_count: usize,
+ contents: &[T],
+ ) {
+ queue.write_buffer(
+ &self.raw,
+ (std::mem::size_of::<T>() * offset_count) as u64,
+ bytemuck::cast_slice(contents),
+ );
+ }
+
+ pub fn slice(
+ &self,
+ bounds: impl RangeBounds<wgpu::BufferAddress>,
+ ) -> wgpu::BufferSlice<'_> {
+ self.raw.slice(bounds)
+ }
+}
+
+fn next_copy_size<T>(amount: usize) -> u64 {
+ let align_mask = wgpu::COPY_BUFFER_ALIGNMENT - 1;
+
+ (((std::mem::size_of::<T>() * amount).next_power_of_two() as u64
+ + align_mask)
+ & !align_mask)
+ .max(wgpu::COPY_BUFFER_ALIGNMENT)
+}
diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs
index 88289b98..43fc47ac 100644
--- a/wgpu/src/buffer/dynamic.rs
+++ b/wgpu/src/buffer/dynamic.rs
@@ -112,25 +112,8 @@ impl<T: ShaderType + WriteInto> Buffer<T> {
}
/// Write the contents of this dynamic buffer to the GPU via staging belt command.
- pub fn write(
- &mut self,
- device: &wgpu::Device,
- staging_belt: &mut wgpu::util::StagingBelt,
- encoder: &mut wgpu::CommandEncoder,
- ) {
- let size = self.cpu.get_ref().len();
-
- if let Some(buffer_size) = wgpu::BufferSize::new(size as u64) {
- let mut buffer = staging_belt.write_buffer(
- encoder,
- &self.gpu,
- 0,
- buffer_size,
- device,
- );
-
- buffer.copy_from_slice(self.cpu.get_ref());
- }
+ pub fn write(&mut self, queue: &wgpu::Queue) {
+ queue.write_buffer(&self.gpu, 0, self.cpu.get_ref());
}
// Gets the aligned offset at the given index from the CPU buffer.
@@ -184,7 +167,7 @@ impl Internal {
}
/// Returns bytearray of aligned CPU buffer.
- pub(super) fn get_ref(&self) -> &Vec<u8> {
+ pub(super) fn get_ref(&self) -> &[u8] {
match self {
Internal::Uniform(buf) => buf.as_ref(),
#[cfg(not(target_arch = "wasm32"))]
diff --git a/wgpu/src/buffer/static.rs b/wgpu/src/buffer/static.rs
index ef87422f..d8ae116e 100644
--- a/wgpu/src/buffer/static.rs
+++ b/wgpu/src/buffer/static.rs
@@ -2,8 +2,7 @@ use bytemuck::{Pod, Zeroable};
use std::marker::PhantomData;
use std::mem;
-//128 triangles/indices
-const DEFAULT_STATIC_BUFFER_COUNT: wgpu::BufferAddress = 128;
+const DEFAULT_COUNT: wgpu::BufferAddress = 128;
/// A generic buffer struct useful for items which have no alignment requirements
/// (e.g. Vertex, Index buffers) & no dynamic offsets.
@@ -25,7 +24,7 @@ impl<T: Pod + Zeroable> Buffer<T> {
label: &'static str,
usages: wgpu::BufferUsages,
) -> Self {
- let size = (mem::size_of::<T>() as u64) * DEFAULT_STATIC_BUFFER_COUNT;
+ let size = (mem::size_of::<T>() as u64) * DEFAULT_COUNT;
Self {
offsets: Vec::new(),
@@ -57,9 +56,13 @@ impl<T: Pod + Zeroable> Buffer<T> {
let size = (mem::size_of::<T>() * new_count) as u64;
if self.size < size {
+ self.size =
+ (mem::size_of::<T>() * (new_count + new_count / 2)) as u64;
+
+ self.gpu =
+ Self::gpu_buffer(device, self.label, self.size, self.usages);
+
self.offsets.clear();
- self.size = size;
- self.gpu = Self::gpu_buffer(device, self.label, size, self.usages);
true
} else {
false
@@ -71,28 +74,15 @@ impl<T: Pod + Zeroable> Buffer<T> {
/// Returns the size of the written bytes.
pub fn write(
&mut self,
- device: &wgpu::Device,
- staging_belt: &mut wgpu::util::StagingBelt,
- encoder: &mut wgpu::CommandEncoder,
+ queue: &wgpu::Queue,
offset: u64,
content: &[T],
) -> u64 {
let bytes = bytemuck::cast_slice(content);
let bytes_size = bytes.len() as u64;
- if let Some(buffer_size) = wgpu::BufferSize::new(bytes_size) {
- let mut buffer = staging_belt.write_buffer(
- encoder,
- &self.gpu,
- offset,
- buffer_size,
- device,
- );
-
- buffer.copy_from_slice(bytes);
-
- self.offsets.push(offset);
- }
+ queue.write_buffer(&self.gpu, offset, bytes);
+ self.offsets.push(offset);
bytes_size
}
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index a5e63b17..db05d2ff 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -6,7 +6,7 @@ use iced_graphics::image::raster;
#[cfg(feature = "svg")]
use iced_graphics::image::vector;
-use crate::Transformation;
+use crate::{Buffer, Transformation};
use atlas::Atlas;
use iced_graphics::layer;
@@ -34,15 +34,107 @@ pub struct Pipeline {
vector_cache: RefCell<vector::Cache<Atlas>>,
pipeline: wgpu::RenderPipeline,
- uniforms: wgpu::Buffer,
vertices: wgpu::Buffer,
indices: wgpu::Buffer,
- instances: wgpu::Buffer,
- constants: wgpu::BindGroup,
+ sampler: wgpu::Sampler,
texture: wgpu::BindGroup,
texture_version: usize,
- texture_layout: wgpu::BindGroupLayout,
texture_atlas: Atlas,
+ texture_layout: wgpu::BindGroupLayout,
+ constant_layout: wgpu::BindGroupLayout,
+
+ layers: Vec<Layer>,
+ prepare_layer: usize,
+}
+
+#[derive(Debug)]
+struct Layer {
+ uniforms: wgpu::Buffer,
+ constants: wgpu::BindGroup,
+ instances: Buffer<Instance>,
+ instance_count: usize,
+}
+
+impl Layer {
+ fn new(
+ device: &wgpu::Device,
+ constant_layout: &wgpu::BindGroupLayout,
+ sampler: &wgpu::Sampler,
+ ) -> Self {
+ let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some("iced_wgpu::image uniforms buffer"),
+ size: mem::size_of::<Uniforms>() as u64,
+ usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+
+ let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::image constants bind group"),
+ layout: constant_layout,
+ entries: &[
+ wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::Buffer(
+ wgpu::BufferBinding {
+ buffer: &uniforms,
+ offset: 0,
+ size: None,
+ },
+ ),
+ },
+ wgpu::BindGroupEntry {
+ binding: 1,
+ resource: wgpu::BindingResource::Sampler(sampler),
+ },
+ ],
+ });
+
+ let instances = Buffer::new(
+ device,
+ "iced_wgpu::image instance buffer",
+ Instance::INITIAL,
+ wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+ );
+
+ Self {
+ uniforms,
+ constants,
+ instances,
+ instance_count: 0,
+ }
+ }
+
+ fn prepare(
+ &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());
+ self.instances.write(queue, 0, instances);
+
+ self.instance_count = instances.len();
+ }
+
+ fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
+ render_pass.set_bind_group(0, &self.constants, &[]);
+ render_pass.set_vertex_buffer(1, self.instances.slice(..));
+
+ render_pass.draw_indexed(
+ 0..QUAD_INDICES.len() as u32,
+ 0,
+ 0..self.instance_count as u32,
+ );
+ }
}
impl Pipeline {
@@ -86,35 +178,6 @@ impl Pipeline {
],
});
- let uniforms_buffer = device.create_buffer(&wgpu::BufferDescriptor {
- label: Some("iced_wgpu::image uniforms buffer"),
- size: mem::size_of::<Uniforms>() as u64,
- usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
- mapped_at_creation: false,
- });
-
- let constant_bind_group =
- device.create_bind_group(&wgpu::BindGroupDescriptor {
- label: Some("iced_wgpu::image constants bind group"),
- layout: &constant_layout,
- entries: &[
- wgpu::BindGroupEntry {
- binding: 0,
- resource: wgpu::BindingResource::Buffer(
- wgpu::BufferBinding {
- buffer: &uniforms_buffer,
- offset: 0,
- size: None,
- },
- ),
- },
- wgpu::BindGroupEntry {
- binding: 1,
- resource: wgpu::BindingResource::Sampler(&sampler),
- },
- ],
- });
-
let texture_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("iced_wgpu::image texture atlas layout"),
@@ -225,13 +288,6 @@ impl Pipeline {
usage: wgpu::BufferUsages::INDEX,
});
- let instances = device.create_buffer(&wgpu::BufferDescriptor {
- label: Some("iced_wgpu::image instance buffer"),
- size: mem::size_of::<Instance>() as u64 * Instance::MAX as u64,
- usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
- mapped_at_creation: false,
- });
-
let texture_atlas = Atlas::new(device);
let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
@@ -253,15 +309,17 @@ impl Pipeline {
vector_cache: RefCell::new(vector::Cache::default()),
pipeline,
- uniforms: uniforms_buffer,
vertices,
indices,
- instances,
- constants: constant_bind_group,
+ sampler,
texture,
texture_version: texture_atlas.layer_count(),
- texture_layout,
texture_atlas,
+ texture_layout,
+ constant_layout,
+
+ layers: Vec::new(),
+ prepare_layer: 0,
}
}
@@ -281,18 +339,19 @@ impl Pipeline {
svg.viewport_dimensions()
}
- pub fn draw(
+ pub fn prepare(
&mut self,
device: &wgpu::Device,
- staging_belt: &mut wgpu::util::StagingBelt,
+ queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
images: &[layer::Image],
transformation: Transformation,
- bounds: Rectangle<u32>,
- target: &wgpu::TextureView,
_scale: f32,
) {
#[cfg(feature = "tracing")]
+ let _ = info_span!("Wgpu::Image", "PREPARE").entered();
+
+ #[cfg(feature = "tracing")]
let _ = info_span!("Wgpu::Image", "DRAW").entered();
let instances: &mut Vec<Instance> = &mut Vec::new();
@@ -309,7 +368,7 @@ impl Pipeline {
layer::Image::Raster { handle, bounds } => {
if let Some(atlas_entry) = raster_cache.upload(
handle,
- &mut (device, encoder),
+ &mut (device, queue, encoder),
&mut self.texture_atlas,
) {
add_instances(
@@ -336,7 +395,7 @@ impl Pipeline {
*color,
size,
_scale,
- &mut (device, encoder),
+ &mut (device, queue, encoder),
&mut self.texture_atlas,
) {
add_instances(
@@ -376,68 +435,28 @@ impl Pipeline {
self.texture_version = texture_version;
}
- {
- let mut uniforms_buffer = staging_belt.write_buffer(
- encoder,
- &self.uniforms,
- 0,
- wgpu::BufferSize::new(mem::size_of::<Uniforms>() as u64)
- .unwrap(),
+ if self.layers.len() <= self.prepare_layer {
+ self.layers.push(Layer::new(
device,
- );
-
- uniforms_buffer.copy_from_slice(bytemuck::bytes_of(&Uniforms {
- transform: transformation.into(),
- }));
+ &self.constant_layout,
+ &self.sampler,
+ ));
}
- let mut i = 0;
- let total = instances.len();
-
- while i < total {
- let end = (i + Instance::MAX).min(total);
- let amount = end - i;
-
- let mut instances_buffer = staging_belt.write_buffer(
- encoder,
- &self.instances,
- 0,
- wgpu::BufferSize::new(
- (amount * std::mem::size_of::<Instance>()) as u64,
- )
- .unwrap(),
- device,
- );
-
- instances_buffer.copy_from_slice(bytemuck::cast_slice(
- &instances[i..i + amount],
- ));
+ let layer = &mut self.layers[self.prepare_layer];
+ layer.prepare(device, queue, instances, transformation);
- let mut render_pass =
- encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
- label: Some("iced_wgpu::image render pass"),
- color_attachments: &[Some(
- wgpu::RenderPassColorAttachment {
- view: target,
- resolve_target: None,
- ops: wgpu::Operations {
- load: wgpu::LoadOp::Load,
- store: true,
- },
- },
- )],
- depth_stencil_attachment: None,
- });
+ self.prepare_layer += 1;
+ }
+ pub fn render<'a>(
+ &'a self,
+ layer: usize,
+ bounds: Rectangle<u32>,
+ render_pass: &mut wgpu::RenderPass<'a>,
+ ) {
+ if let Some(layer) = self.layers.get(layer) {
render_pass.set_pipeline(&self.pipeline);
- render_pass.set_bind_group(0, &self.constants, &[]);
- render_pass.set_bind_group(1, &self.texture, &[]);
- render_pass.set_index_buffer(
- self.indices.slice(..),
- wgpu::IndexFormat::Uint16,
- );
- render_pass.set_vertex_buffer(0, self.vertices.slice(..));
- render_pass.set_vertex_buffer(1, self.instances.slice(..));
render_pass.set_scissor_rect(
bounds.x,
@@ -446,30 +465,34 @@ impl Pipeline {
bounds.height,
);
- render_pass.draw_indexed(
- 0..QUAD_INDICES.len() as u32,
- 0,
- 0..amount as u32,
+ render_pass.set_bind_group(1, &self.texture, &[]);
+ render_pass.set_index_buffer(
+ self.indices.slice(..),
+ wgpu::IndexFormat::Uint16,
);
+ render_pass.set_vertex_buffer(0, self.vertices.slice(..));
- i += Instance::MAX;
+ layer.render(render_pass);
}
}
- pub fn trim_cache(
+ pub fn end_frame(
&mut self,
device: &wgpu::Device,
+ queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
) {
#[cfg(feature = "image")]
self.raster_cache
.borrow_mut()
- .trim(&mut self.texture_atlas, &mut (device, encoder));
+ .trim(&mut self.texture_atlas, &mut (device, queue, encoder));
#[cfg(feature = "svg")]
self.vector_cache
.borrow_mut()
- .trim(&mut self.texture_atlas, &mut (device, encoder));
+ .trim(&mut self.texture_atlas, &mut (device, queue, encoder));
+
+ self.prepare_layer = 0;
}
}
@@ -507,7 +530,7 @@ struct Instance {
}
impl Instance {
- pub const MAX: usize = 1_000;
+ pub const INITIAL: usize = 1_000;
}
#[repr(C)]
diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index eafe2f96..7df67abd 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -185,13 +185,13 @@ impl Atlas {
fn upload_allocation(
&mut self,
- buffer: &wgpu::Buffer,
+ data: &[u8],
image_width: u32,
image_height: u32,
padding: u32,
offset: usize,
allocation: &Allocation,
- encoder: &mut wgpu::CommandEncoder,
+ queue: &wgpu::Queue,
) {
let (x, y) = allocation.position();
let Size { width, height } = allocation.size();
@@ -203,15 +203,7 @@ impl Atlas {
depth_or_array_layers: 1,
};
- encoder.copy_buffer_to_texture(
- wgpu::ImageCopyBuffer {
- buffer,
- layout: wgpu::ImageDataLayout {
- offset: offset as u64,
- bytes_per_row: NonZeroU32::new(4 * image_width + padding),
- rows_per_image: NonZeroU32::new(image_height),
- },
- },
+ queue.write_texture(
wgpu::ImageCopyTexture {
texture: &self.texture,
mip_level: 0,
@@ -222,6 +214,12 @@ impl Atlas {
},
aspect: wgpu::TextureAspect::default(),
},
+ data,
+ wgpu::ImageDataLayout {
+ offset: offset as u64,
+ bytes_per_row: NonZeroU32::new(4 * image_width + padding),
+ rows_per_image: NonZeroU32::new(image_height),
+ },
extent,
);
}
@@ -301,17 +299,19 @@ impl Atlas {
impl image::Storage for Atlas {
type Entry = Entry;
- type State<'a> = (&'a wgpu::Device, &'a mut wgpu::CommandEncoder);
+ type State<'a> = (
+ &'a wgpu::Device,
+ &'a wgpu::Queue,
+ &'a mut wgpu::CommandEncoder,
+ );
fn upload(
&mut self,
width: u32,
height: u32,
data: &[u8],
- (device, encoder): &mut Self::State<'_>,
+ (device, queue, encoder): &mut Self::State<'_>,
) -> Option<Self::Entry> {
- use wgpu::util::DeviceExt;
-
let entry = {
let current_size = self.layers.len();
let entry = self.allocate(width, height)?;
@@ -344,17 +344,16 @@ impl image::Storage for Atlas {
)
}
- let buffer =
- device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
- label: Some("iced_wgpu::image staging buffer"),
- contents: &padded_data,
- usage: wgpu::BufferUsages::COPY_SRC,
- });
-
match &entry {
Entry::Contiguous(allocation) => {
self.upload_allocation(
- &buffer, width, height, padding, 0, allocation, encoder,
+ &padded_data,
+ width,
+ height,
+ padding,
+ 0,
+ allocation,
+ queue,
);
}
Entry::Fragmented { fragments, .. } => {
@@ -363,13 +362,13 @@ impl image::Storage for Atlas {
let offset = (y * padded_width as u32 + 4 * x) as usize;
self.upload_allocation(
- &buffer,
+ &padded_data,
width,
height,
padding,
offset,
&fragment.allocation,
- encoder,
+ queue,
);
}
}
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 1a293681..9da40572 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -47,14 +47,17 @@ mod quad;
mod text;
mod triangle;
-pub use iced_graphics::{Antialiasing, Color, Error, Primitive, Viewport};
+pub use iced_graphics::{
+ Antialiasing, Color, Error, Font, Primitive, Viewport,
+};
pub use iced_native::Theme;
pub use wgpu;
pub use backend::Backend;
pub use settings::Settings;
-pub(crate) use iced_graphics::Transformation;
+use crate::buffer::Buffer;
+use iced_graphics::Transformation;
#[cfg(any(feature = "image", feature = "svg"))]
mod image;
diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs
index 2f5fcc6b..246cc5e1 100644
--- a/wgpu/src/quad.rs
+++ b/wgpu/src/quad.rs
@@ -1,4 +1,4 @@
-use crate::Transformation;
+use crate::{Buffer, Transformation};
use iced_graphics::layer;
use iced_native::Rectangle;
@@ -12,11 +12,11 @@ use tracing::info_span;
#[derive(Debug)]
pub struct Pipeline {
pipeline: wgpu::RenderPipeline,
- constants: wgpu::BindGroup,
- constants_buffer: wgpu::Buffer,
+ constant_layout: wgpu::BindGroupLayout,
vertices: wgpu::Buffer,
indices: wgpu::Buffer,
- instances: wgpu::Buffer,
+ layers: Vec<Layer>,
+ prepare_layer: usize,
}
impl Pipeline {
@@ -38,22 +38,6 @@ impl Pipeline {
}],
});
- let constants_buffer = device.create_buffer(&wgpu::BufferDescriptor {
- label: Some("iced_wgpu::quad uniforms buffer"),
- size: mem::size_of::<Uniforms>() as wgpu::BufferAddress,
- usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
- mapped_at_creation: false,
- });
-
- let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
- label: Some("iced_wgpu::quad uniforms bind group"),
- layout: &constant_layout,
- entries: &[wgpu::BindGroupEntry {
- binding: 0,
- resource: constants_buffer.as_entire_binding(),
- }],
- });
-
let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu::quad pipeline layout"),
@@ -148,118 +132,146 @@ impl Pipeline {
usage: wgpu::BufferUsages::INDEX,
});
- let instances = device.create_buffer(&wgpu::BufferDescriptor {
- label: Some("iced_wgpu::quad instance buffer"),
- size: mem::size_of::<layer::Quad>() as u64 * MAX_INSTANCES as u64,
- usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
- mapped_at_creation: false,
- });
-
Pipeline {
pipeline,
- constants,
- constants_buffer,
+ constant_layout,
vertices,
indices,
- instances,
+ layers: Vec::new(),
+ prepare_layer: 0,
}
}
- pub fn draw(
+ pub fn prepare(
&mut self,
device: &wgpu::Device,
- staging_belt: &mut wgpu::util::StagingBelt,
- encoder: &mut wgpu::CommandEncoder,
+ queue: &wgpu::Queue,
instances: &[layer::Quad],
transformation: Transformation,
scale: f32,
- bounds: Rectangle<u32>,
- target: &wgpu::TextureView,
) {
- #[cfg(feature = "tracing")]
- let _ = info_span!("Wgpu::Quad", "DRAW").entered();
+ if self.layers.len() <= self.prepare_layer {
+ self.layers.push(Layer::new(device, &self.constant_layout));
+ }
- let uniforms = Uniforms::new(transformation, scale);
+ let layer = &mut self.layers[self.prepare_layer];
+ layer.prepare(device, queue, instances, transformation, scale);
+
+ self.prepare_layer += 1;
+ }
+
+ pub fn render<'a>(
+ &'a self,
+ layer: usize,
+ bounds: Rectangle<u32>,
+ render_pass: &mut wgpu::RenderPass<'a>,
+ ) {
+ if let Some(layer) = self.layers.get(layer) {
+ render_pass.set_pipeline(&self.pipeline);
+
+ render_pass.set_scissor_rect(
+ bounds.x,
+ bounds.y,
+ bounds.width,
+ bounds.height,
+ );
- {
- let mut constants_buffer = staging_belt.write_buffer(
- encoder,
- &self.constants_buffer,
- 0,
- wgpu::BufferSize::new(mem::size_of::<Uniforms>() as u64)
- .unwrap(),
- device,
+ render_pass.set_index_buffer(
+ self.indices.slice(..),
+ wgpu::IndexFormat::Uint16,
);
+ render_pass.set_vertex_buffer(0, self.vertices.slice(..));
- constants_buffer.copy_from_slice(bytemuck::bytes_of(&uniforms));
+ layer.draw(render_pass);
}
+ }
- let mut i = 0;
- let total = instances.len();
+ pub fn end_frame(&mut self) {
+ self.prepare_layer = 0;
+ }
+}
- while i < total {
- let end = (i + MAX_INSTANCES).min(total);
- let amount = end - i;
+#[derive(Debug)]
+struct Layer {
+ constants: wgpu::BindGroup,
+ constants_buffer: wgpu::Buffer,
+ instances: Buffer<layer::Quad>,
+ instance_count: usize,
+}
- let instance_bytes = bytemuck::cast_slice(&instances[i..end]);
+impl Layer {
+ pub fn new(
+ device: &wgpu::Device,
+ constant_layout: &wgpu::BindGroupLayout,
+ ) -> Self {
+ let constants_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+ label: Some("iced_wgpu::quad uniforms buffer"),
+ size: mem::size_of::<Uniforms>() as wgpu::BufferAddress,
+ usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
- let mut instance_buffer = staging_belt.write_buffer(
- encoder,
- &self.instances,
- 0,
- wgpu::BufferSize::new(instance_bytes.len() as u64).unwrap(),
- device,
- );
+ let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::quad uniforms bind group"),
+ layout: constant_layout,
+ entries: &[wgpu::BindGroupEntry {
+ binding: 0,
+ resource: constants_buffer.as_entire_binding(),
+ }],
+ });
- instance_buffer.copy_from_slice(instance_bytes);
-
- #[cfg(feature = "tracing")]
- let _ = info_span!("Wgpu::Quad", "BEGIN_RENDER_PASS").enter();
-
- {
- let mut render_pass =
- encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
- label: Some("iced_wgpu::quad render pass"),
- color_attachments: &[Some(
- wgpu::RenderPassColorAttachment {
- view: target,
- resolve_target: None,
- ops: wgpu::Operations {
- load: wgpu::LoadOp::Load,
- store: true,
- },
- },
- )],
- depth_stencil_attachment: None,
- });
-
- render_pass.set_pipeline(&self.pipeline);
- render_pass.set_bind_group(0, &self.constants, &[]);
- render_pass.set_index_buffer(
- self.indices.slice(..),
- wgpu::IndexFormat::Uint16,
- );
- render_pass.set_vertex_buffer(0, self.vertices.slice(..));
- render_pass.set_vertex_buffer(1, self.instances.slice(..));
-
- render_pass.set_scissor_rect(
- bounds.x,
- bounds.y,
- bounds.width,
- // TODO: Address anti-aliasing adjustments properly
- bounds.height,
- );
-
- render_pass.draw_indexed(
- 0..QUAD_INDICES.len() as u32,
- 0,
- 0..amount as u32,
- );
- }
-
- i += MAX_INSTANCES;
+ let instances = Buffer::new(
+ device,
+ "iced_wgpu::quad instance buffer",
+ INITIAL_INSTANCES,
+ wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+ );
+
+ Self {
+ constants,
+ constants_buffer,
+ instances,
+ instance_count: 0,
}
}
+
+ pub fn prepare(
+ &mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ instances: &[layer::Quad],
+ transformation: Transformation,
+ scale: f32,
+ ) {
+ #[cfg(feature = "tracing")]
+ let _ = info_span!("Wgpu::Quad", "PREPARE").entered();
+
+ let uniforms = Uniforms::new(transformation, scale);
+
+ queue.write_buffer(
+ &self.constants_buffer,
+ 0,
+ bytemuck::bytes_of(&uniforms),
+ );
+
+ let _ = self.instances.resize(device, instances.len());
+ self.instances.write(queue, 0, instances);
+ self.instance_count = instances.len();
+ }
+
+ pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
+ #[cfg(feature = "tracing")]
+ let _ = info_span!("Wgpu::Quad", "DRAW").entered();
+
+ render_pass.set_bind_group(0, &self.constants, &[]);
+ render_pass.set_vertex_buffer(1, self.instances.slice(..));
+
+ render_pass.draw_indexed(
+ 0..QUAD_INDICES.len() as u32,
+ 0,
+ 0..self.instance_count as u32,
+ );
+ }
}
#[repr(C)]
@@ -285,7 +297,7 @@ const QUAD_VERTS: [Vertex; 4] = [
},
];
-const MAX_INSTANCES: usize = 100_000;
+const INITIAL_INSTANCES: usize = 10_000;
#[repr(C)]
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs
index 5ef79499..bd9cf473 100644
--- a/wgpu/src/settings.rs
+++ b/wgpu/src/settings.rs
@@ -1,12 +1,12 @@
//! Configure a renderer.
-use std::fmt;
-
pub use crate::Antialiasing;
+use crate::Font;
+
/// The settings of a [`Backend`].
///
/// [`Backend`]: crate::Backend
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Settings {
/// The present mode of the [`Backend`].
///
@@ -16,42 +16,20 @@ pub struct Settings {
/// The internal graphics backend to use.
pub internal_backend: wgpu::Backends,
- /// The bytes of the font that will be used by default.
- ///
- /// If `None` is provided, a default system font will be chosen.
- pub default_font: Option<&'static [u8]>,
+ /// The default [`Font`] to use.
+ pub default_font: Font,
/// The default size of text.
///
/// By default, it will be set to `16.0`.
pub default_text_size: f32,
- /// If enabled, spread text workload in multiple threads when multiple cores
- /// are available.
- ///
- /// By default, it is disabled.
- pub text_multithreading: bool,
-
/// The antialiasing strategy that will be used for triangle primitives.
///
/// By default, it is `None`.
pub antialiasing: Option<Antialiasing>,
}
-impl fmt::Debug for Settings {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Settings")
- .field("present_mode", &self.present_mode)
- .field("internal_backend", &self.internal_backend)
- // Instead of printing the font bytes, we simply show a `bool` indicating if using a default font or not.
- .field("default_font", &self.default_font.is_some())
- .field("default_text_size", &self.default_text_size)
- .field("text_multithreading", &self.text_multithreading)
- .field("antialiasing", &self.antialiasing)
- .finish()
- }
-}
-
impl Settings {
/// Creates new [`Settings`] using environment configuration.
///
@@ -81,9 +59,8 @@ impl Default for Settings {
Settings {
present_mode: wgpu::PresentMode::AutoVsync,
internal_backend: wgpu::Backends::all(),
- default_font: None,
- default_text_size: 20.0,
- text_multithreading: false,
+ default_font: Font::SansSerif,
+ default_text_size: 16.0,
antialiasing: None,
}
}
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index e17b84c1..dea6ab18 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -1,128 +1,267 @@
-use crate::Transformation;
-
-use iced_graphics::font;
+pub use iced_native::text::Hit;
-use std::{cell::RefCell, collections::HashMap};
-use wgpu_glyph::ab_glyph;
+use iced_graphics::layer::Text;
+use iced_native::alignment;
+use iced_native::{Color, Font, Rectangle, Size};
-pub use iced_native::text::Hit;
+use rustc_hash::{FxHashMap, FxHashSet};
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::hash_map;
+use std::hash::{BuildHasher, Hash, Hasher};
+use std::sync::Arc;
-#[derive(Debug)]
+#[allow(missing_debug_implementations)]
pub struct Pipeline {
- draw_brush: RefCell<wgpu_glyph::GlyphBrush<()>>,
- draw_font_map: RefCell<HashMap<String, wgpu_glyph::FontId>>,
- measure_brush: RefCell<glyph_brush::GlyphBrush<()>>,
+ system: Option<System>,
+ renderers: Vec<glyphon::TextRenderer>,
+ atlas: glyphon::TextAtlas,
+ prepare_layer: usize,
+}
+
+#[ouroboros::self_referencing]
+struct System {
+ fonts: glyphon::FontSystem,
+
+ #[borrows(fonts)]
+ #[not_covariant]
+ measurement_cache: RefCell<Cache<'this>>,
+
+ #[borrows(fonts)]
+ #[not_covariant]
+ render_cache: Cache<'this>,
}
impl Pipeline {
pub fn new(
device: &wgpu::Device,
+ queue: &wgpu::Queue,
format: wgpu::TextureFormat,
- default_font: Option<&[u8]>,
- multithreading: bool,
) -> Self {
- let default_font = default_font.map(|slice| slice.to_vec());
-
- // TODO: Font customization
- #[cfg(not(target_os = "ios"))]
- #[cfg(feature = "default_system_font")]
- let default_font = {
- default_font.or_else(|| {
- font::Source::new()
- .load(&[font::Family::SansSerif, font::Family::Serif])
- .ok()
- })
- };
+ Pipeline {
+ system: Some(
+ SystemBuilder {
+ fonts: glyphon::FontSystem::new_with_fonts(
+ [glyphon::fontdb::Source::Binary(Arc::new(
+ include_bytes!("../fonts/Iced-Icons.ttf")
+ .as_slice(),
+ ))]
+ .into_iter(),
+ ),
+ measurement_cache_builder: |_| RefCell::new(Cache::new()),
+ render_cache_builder: |_| Cache::new(),
+ }
+ .build(),
+ ),
+ renderers: Vec::new(),
+ atlas: glyphon::TextAtlas::new(device, queue, format),
+ prepare_layer: 0,
+ }
+ }
- let default_font =
- default_font.unwrap_or_else(|| font::FALLBACK.to_vec());
+ pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
+ let heads = self.system.take().unwrap().into_heads();
- let font = ab_glyph::FontArc::try_from_vec(default_font)
- .unwrap_or_else(|_| {
- log::warn!(
- "System font failed to load. Falling back to \
- embedded font..."
- );
+ let (locale, mut db) = heads.fonts.into_locale_and_db();
- ab_glyph::FontArc::try_from_slice(font::FALLBACK)
- .expect("Load fallback font")
- });
+ db.load_font_source(glyphon::fontdb::Source::Binary(Arc::new(
+ bytes.into_owned(),
+ )));
+
+ self.system = Some(
+ SystemBuilder {
+ fonts: glyphon::FontSystem::new_with_locale_and_db(locale, db),
+ measurement_cache_builder: |_| RefCell::new(Cache::new()),
+ render_cache_builder: |_| Cache::new(),
+ }
+ .build(),
+ );
+ }
- let draw_brush_builder =
- wgpu_glyph::GlyphBrushBuilder::using_font(font.clone())
- .initial_cache_size((2048, 2048))
- .draw_cache_multithread(multithreading);
+ pub fn prepare(
+ &mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ sections: &[Text<'_>],
+ bounds: Rectangle,
+ scale_factor: f32,
+ target_size: Size<u32>,
+ ) -> bool {
+ self.system.as_mut().unwrap().with_mut(|fields| {
+ if self.renderers.len() <= self.prepare_layer {
+ self.renderers
+ .push(glyphon::TextRenderer::new(device, queue));
+ }
- #[cfg(target_arch = "wasm32")]
- let draw_brush_builder = draw_brush_builder.draw_cache_align_4x4(true);
+ let renderer = &mut self.renderers[self.prepare_layer];
+
+ let keys: Vec<_> = sections
+ .iter()
+ .map(|section| {
+ let (key, _) = fields.render_cache.allocate(
+ fields.fonts,
+ Key {
+ content: section.content,
+ size: section.size * scale_factor,
+ font: section.font,
+ bounds: Size {
+ width: (section.bounds.width * scale_factor)
+ .ceil(),
+ height: (section.bounds.height * scale_factor)
+ .ceil(),
+ },
+ color: section.color,
+ },
+ );
+
+ key
+ })
+ .collect();
+
+ let bounds = glyphon::TextBounds {
+ left: (bounds.x * scale_factor) as i32,
+ top: (bounds.y * scale_factor) as i32,
+ right: ((bounds.x + bounds.width) * scale_factor) as i32,
+ bottom: ((bounds.y + bounds.height) * scale_factor) as i32,
+ };
+
+ let text_areas =
+ sections.iter().zip(keys.iter()).map(|(section, key)| {
+ let buffer = fields
+ .render_cache
+ .get(key)
+ .expect("Get cached buffer");
+
+ let x = section.bounds.x * scale_factor;
+ let y = section.bounds.y * scale_factor;
+
+ let (total_lines, max_width) = buffer
+ .layout_runs()
+ .enumerate()
+ .fold((0, 0.0), |(_, max), (i, buffer)| {
+ (i + 1, buffer.line_w.max(max))
+ });
+
+ let total_height =
+ total_lines as f32 * section.size * 1.2 * scale_factor;
+
+ let left = match section.horizontal_alignment {
+ alignment::Horizontal::Left => x,
+ alignment::Horizontal::Center => x - max_width / 2.0,
+ alignment::Horizontal::Right => x - max_width,
+ };
+
+ let top = match section.vertical_alignment {
+ alignment::Vertical::Top => y,
+ alignment::Vertical::Center => y - total_height / 2.0,
+ alignment::Vertical::Bottom => y - total_height,
+ };
+
+ glyphon::TextArea {
+ buffer,
+ left: left as i32,
+ top: top as i32,
+ bounds,
+ }
+ });
+
+ let result = renderer.prepare(
+ device,
+ queue,
+ &mut self.atlas,
+ glyphon::Resolution {
+ width: target_size.width,
+ height: target_size.height,
+ },
+ text_areas,
+ glyphon::Color::rgb(0, 0, 0),
+ &mut glyphon::SwashCache::new(fields.fonts),
+ );
+
+ match result {
+ Ok(()) => {
+ self.prepare_layer += 1;
+
+ true
+ }
+ Err(glyphon::PrepareError::AtlasFull(content_type)) => {
+ self.prepare_layer = 0;
+
+ #[allow(clippy::needless_bool)]
+ if self.atlas.grow(device, content_type) {
+ false
+ } else {
+ // If the atlas cannot grow, then all bets are off.
+ // Instead of panicking, we will just pray that the result
+ // will be somewhat readable...
+ true
+ }
+ }
+ }
+ })
+ }
- let draw_brush = draw_brush_builder.build(device, format);
+ pub fn render<'a>(
+ &'a self,
+ layer: usize,
+ bounds: Rectangle<u32>,
+ render_pass: &mut wgpu::RenderPass<'a>,
+ ) {
+ let renderer = &self.renderers[layer];
- let measure_brush =
- glyph_brush::GlyphBrushBuilder::using_font(font).build();
+ render_pass.set_scissor_rect(
+ bounds.x,
+ bounds.y,
+ bounds.width,
+ bounds.height,
+ );
- Pipeline {
- draw_brush: RefCell::new(draw_brush),
- draw_font_map: RefCell::new(HashMap::new()),
- measure_brush: RefCell::new(measure_brush),
- }
+ renderer
+ .render(&self.atlas, render_pass)
+ .expect("Render text");
}
- pub fn queue(&mut self, section: wgpu_glyph::Section<'_>) {
- self.draw_brush.borrow_mut().queue(section);
- }
+ pub fn end_frame(&mut self) {
+ self.atlas.trim();
- pub fn draw_queued(
- &mut self,
- device: &wgpu::Device,
- staging_belt: &mut wgpu::util::StagingBelt,
- encoder: &mut wgpu::CommandEncoder,
- target: &wgpu::TextureView,
- transformation: Transformation,
- region: wgpu_glyph::Region,
- ) {
- self.draw_brush
- .borrow_mut()
- .draw_queued_with_transform_and_scissoring(
- device,
- staging_belt,
- encoder,
- target,
- transformation.into(),
- region,
- )
- .expect("Draw text");
+ self.system
+ .as_mut()
+ .unwrap()
+ .with_render_cache_mut(|cache| cache.trim());
+
+ self.prepare_layer = 0;
}
pub fn measure(
&self,
content: &str,
size: f32,
- font: iced_native::Font,
- bounds: iced_native::Size,
+ font: Font,
+ bounds: Size,
) -> (f32, f32) {
- use wgpu_glyph::GlyphCruncher;
-
- let wgpu_glyph::FontId(font_id) = self.find_font(font);
-
- let section = wgpu_glyph::Section {
- bounds: (bounds.width, bounds.height),
- text: vec![wgpu_glyph::Text {
- text: content,
- scale: size.into(),
- font_id: wgpu_glyph::FontId(font_id),
- extra: wgpu_glyph::Extra::default(),
- }],
- ..Default::default()
- };
-
- if let Some(bounds) =
- self.measure_brush.borrow_mut().glyph_bounds(section)
- {
- (bounds.width().ceil(), bounds.height().ceil())
- } else {
- (0.0, 0.0)
- }
+ self.system.as_ref().unwrap().with(|fields| {
+ let mut measurement_cache = fields.measurement_cache.borrow_mut();
+
+ let (_, paragraph) = measurement_cache.allocate(
+ fields.fonts,
+ Key {
+ content,
+ size,
+ font,
+ bounds,
+ color: Color::BLACK,
+ },
+ );
+
+ let (total_lines, max_width) = paragraph
+ .layout_runs()
+ .enumerate()
+ .fold((0, 0.0), |(_, max), (i, buffer)| {
+ (i + 1, buffer.line_w.max(max))
+ });
+
+ (max_width, size * 1.2 * total_lines as f32)
+ })
}
pub fn hit_test(
@@ -132,134 +271,148 @@ impl Pipeline {
font: iced_native::Font,
bounds: iced_native::Size,
point: iced_native::Point,
- nearest_only: bool,
+ _nearest_only: bool,
) -> Option<Hit> {
- use wgpu_glyph::GlyphCruncher;
-
- let wgpu_glyph::FontId(font_id) = self.find_font(font);
-
- let section = wgpu_glyph::Section {
- bounds: (bounds.width, bounds.height),
- text: vec![wgpu_glyph::Text {
- text: content,
- scale: size.into(),
- font_id: wgpu_glyph::FontId(font_id),
- extra: wgpu_glyph::Extra::default(),
- }],
- ..Default::default()
- };
+ self.system.as_ref().unwrap().with(|fields| {
+ let mut measurement_cache = fields.measurement_cache.borrow_mut();
+
+ let (_, paragraph) = measurement_cache.allocate(
+ fields.fonts,
+ Key {
+ content,
+ size,
+ font,
+ bounds,
+ color: Color::BLACK,
+ },
+ );
+
+ let cursor = paragraph.hit(point.x, point.y)?;
+
+ Some(Hit::CharOffset(cursor.index))
+ })
+ }
- let mut mb = self.measure_brush.borrow_mut();
-
- // The underlying type is FontArc, so clones are cheap.
- use wgpu_glyph::ab_glyph::{Font, ScaleFont};
- let font = mb.fonts()[font_id].clone().into_scaled(size);
-
- // Implements an iterator over the glyph bounding boxes.
- let bounds = mb.glyphs(section).map(
- |wgpu_glyph::SectionGlyph {
- byte_index, glyph, ..
- }| {
- (
- *byte_index,
- iced_native::Rectangle::new(
- iced_native::Point::new(
- glyph.position.x - font.h_side_bearing(glyph.id),
- glyph.position.y - font.ascent(),
- ),
- iced_native::Size::new(
- font.h_advance(glyph.id),
- font.ascent() - font.descent(),
- ),
- ),
- )
- },
- );
+ pub fn trim_measurement_cache(&mut self) {
+ self.system
+ .as_mut()
+ .unwrap()
+ .with_measurement_cache_mut(|cache| cache.borrow_mut().trim());
+ }
+}
- // Implements computation of the character index based on the byte index
- // within the input string.
- let char_index = |byte_index| {
- let mut b_count = 0;
- for (i, utf8_len) in
- content.chars().map(|c| c.len_utf8()).enumerate()
- {
- if byte_index < (b_count + utf8_len) {
- return i;
- }
- b_count += utf8_len;
- }
+fn to_family(font: Font) -> glyphon::Family<'static> {
+ match font {
+ Font::Name(name) => glyphon::Family::Name(name),
+ Font::SansSerif => glyphon::Family::SansSerif,
+ Font::Serif => glyphon::Family::Serif,
+ Font::Cursive => glyphon::Family::Cursive,
+ Font::Fantasy => glyphon::Family::Fantasy,
+ Font::Monospace => glyphon::Family::Monospace,
+ }
+}
- byte_index
- };
+struct Cache<'a> {
+ entries: FxHashMap<KeyHash, glyphon::Buffer<'a>>,
+ recently_used: FxHashSet<KeyHash>,
+ hasher: HashBuilder,
+ trim_count: usize,
+}
- if !nearest_only {
- for (idx, bounds) in bounds.clone() {
- if bounds.contains(point) {
- return Some(Hit::CharOffset(char_index(idx)));
- }
- }
- }
+#[cfg(not(target_arch = "wasm32"))]
+type HashBuilder = twox_hash::RandomXxHashBuilder64;
- let nearest = bounds
- .map(|(index, bounds)| (index, bounds.center()))
- .min_by(|(_, center_a), (_, center_b)| {
- center_a
- .distance(point)
- .partial_cmp(&center_b.distance(point))
- .unwrap_or(std::cmp::Ordering::Greater)
- });
-
- nearest.map(|(idx, center)| {
- Hit::NearestCharOffset(char_index(idx), point - center)
- })
- }
+#[cfg(target_arch = "wasm32")]
+type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>;
- pub fn trim_measurement_cache(&mut self) {
- // TODO: We should probably use a `GlyphCalculator` for this. However,
- // it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop.
- // This makes stuff quite inconvenient. A manual method for trimming the
- // cache would make our lives easier.
- loop {
- let action = self
- .measure_brush
- .borrow_mut()
- .process_queued(|_, _| {}, |_| {});
-
- match action {
- Ok(_) => break,
- Err(glyph_brush::BrushError::TextureTooSmall { suggested }) => {
- let (width, height) = suggested;
-
- self.measure_brush
- .borrow_mut()
- .resize_texture(width, height);
- }
- }
+impl<'a> Cache<'a> {
+ const TRIM_INTERVAL: usize = 300;
+
+ fn new() -> Self {
+ Self {
+ entries: FxHashMap::default(),
+ recently_used: FxHashSet::default(),
+ hasher: HashBuilder::default(),
+ trim_count: 0,
}
}
- pub fn find_font(&self, font: iced_native::Font) -> wgpu_glyph::FontId {
- match font {
- iced_native::Font::Default => wgpu_glyph::FontId(0),
- iced_native::Font::External { name, bytes } => {
- if let Some(font_id) = self.draw_font_map.borrow().get(name) {
- return *font_id;
- }
+ fn get(&self, key: &KeyHash) -> Option<&glyphon::Buffer<'a>> {
+ self.entries.get(key)
+ }
- let font = ab_glyph::FontArc::try_from_slice(bytes)
- .expect("Load font");
+ fn allocate(
+ &mut self,
+ fonts: &'a glyphon::FontSystem,
+ key: Key<'_>,
+ ) -> (KeyHash, &mut glyphon::Buffer<'a>) {
+ let hash = {
+ let mut hasher = self.hasher.build_hasher();
+
+ key.content.hash(&mut hasher);
+ key.size.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.color.into_rgba8().hash(&mut hasher);
+
+ hasher.finish()
+ };
- let _ = self.measure_brush.borrow_mut().add_font(font.clone());
+ if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
+ let metrics = glyphon::Metrics::new(key.size, key.size * 1.2);
+ let mut buffer = glyphon::Buffer::new(fonts, metrics);
+
+ buffer.set_size(
+ key.bounds.width,
+ key.bounds.height.max(key.size * 1.2),
+ );
+ buffer.set_text(
+ key.content,
+ glyphon::Attrs::new()
+ .family(to_family(key.font))
+ .color({
+ let [r, g, b, a] = key.color.into_linear();
+
+ glyphon::Color::rgba(
+ (r * 255.0) as u8,
+ (g * 255.0) as u8,
+ (b * 255.0) as u8,
+ (a * 255.0) as u8,
+ )
+ })
+ .monospaced(matches!(key.font, Font::Monospace)),
+ );
+
+ let _ = entry.insert(buffer);
+ }
- let font_id = self.draw_brush.borrow_mut().add_font(font);
+ let _ = self.recently_used.insert(hash);
- let _ = self
- .draw_font_map
- .borrow_mut()
- .insert(String::from(name), font_id);
+ (hash, self.entries.get_mut(&hash).unwrap())
+ }
- font_id
- }
+ fn trim(&mut self) {
+ if self.trim_count >= Self::TRIM_INTERVAL {
+ self.entries
+ .retain(|key, _| self.recently_used.contains(key));
+
+ self.recently_used.clear();
+
+ self.trim_count = 0;
+ } else {
+ self.trim_count += 1;
}
}
}
+
+#[derive(Debug, Clone, Copy)]
+struct Key<'a> {
+ content: &'a str,
+ size: f32,
+ font: Font,
+ bounds: Size,
+ color: Color,
+}
+
+type KeyHash = u64;
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index efdd214b..4b4fa16d 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -14,50 +14,55 @@ use tracing::info_span;
#[derive(Debug)]
pub struct Pipeline {
blit: Option<msaa::Blit>,
- index_buffer: Buffer<u32>,
- index_strides: Vec<u32>,
solid: solid::Pipeline,
/// Gradients are currently not supported on WASM targets due to their need of storage buffers.
#[cfg(not(target_arch = "wasm32"))]
gradient: gradient::Pipeline,
+
+ layers: Vec<Layer>,
+ prepare_layer: usize,
}
-impl Pipeline {
- pub fn new(
+#[derive(Debug)]
+struct Layer {
+ index_buffer: Buffer<u32>,
+ index_strides: Vec<u32>,
+ solid: solid::Layer,
+
+ #[cfg(not(target_arch = "wasm32"))]
+ gradient: gradient::Layer,
+}
+
+impl Layer {
+ fn new(
device: &wgpu::Device,
- format: wgpu::TextureFormat,
- antialiasing: Option<settings::Antialiasing>,
- ) -> Pipeline {
- Pipeline {
- blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
+ solid: &solid::Pipeline,
+ #[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline,
+ ) -> Self {
+ Self {
index_buffer: Buffer::new(
device,
- "iced_wgpu::triangle vertex buffer",
+ "iced_wgpu::triangle index buffer",
wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
),
index_strides: Vec::new(),
- solid: solid::Pipeline::new(device, format, antialiasing),
+ solid: solid::Layer::new(device, &solid.constants_layout),
#[cfg(not(target_arch = "wasm32"))]
- gradient: gradient::Pipeline::new(device, format, antialiasing),
+ gradient: gradient::Layer::new(device, &gradient.constants_layout),
}
}
- pub fn draw(
+ fn prepare(
&mut self,
device: &wgpu::Device,
- staging_belt: &mut wgpu::util::StagingBelt,
- encoder: &mut wgpu::CommandEncoder,
- target: &wgpu::TextureView,
- target_size: Size<u32>,
- transformation: Transformation,
- scale_factor: f32,
+ queue: &wgpu::Queue,
+ solid: &solid::Pipeline,
+ #[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline,
meshes: &[Mesh<'_>],
+ transformation: Transformation,
) {
- #[cfg(feature = "tracing")]
- let _ = info_span!("Wgpu::Triangle", "DRAW").entered();
-
// Count the total amount of vertices & indices we need to handle
let count = mesh::attribute_count_of(meshes);
@@ -75,6 +80,7 @@ impl Pipeline {
.resize(device, count.gradient_vertices);
// Prepare dynamic buffers & data store for writing
+ self.index_buffer.clear();
self.index_strides.clear();
self.solid.vertices.clear();
self.solid.uniforms.clear();
@@ -99,13 +105,8 @@ impl Pipeline {
let transform =
transformation * Transformation::translate(origin.x, origin.y);
- let new_index_offset = self.index_buffer.write(
- device,
- staging_belt,
- encoder,
- index_offset,
- indices,
- );
+ let new_index_offset =
+ self.index_buffer.write(queue, index_offset, indices);
index_offset += new_index_offset;
self.index_strides.push(indices.len() as u32);
@@ -116,9 +117,7 @@ impl Pipeline {
self.solid.uniforms.push(&solid::Uniforms::new(transform));
let written_bytes = self.solid.vertices.write(
- device,
- staging_belt,
- encoder,
+ queue,
solid_vertex_offset,
&buffers.vertices,
);
@@ -130,9 +129,7 @@ impl Pipeline {
buffers, gradient, ..
} => {
let written_bytes = self.gradient.vertices.write(
- device,
- staging_belt,
- encoder,
+ queue,
gradient_vertex_offset,
&buffers.vertices,
);
@@ -196,14 +193,14 @@ impl Pipeline {
let uniforms_resized = self.solid.uniforms.resize(device);
if uniforms_resized {
- self.solid.bind_group = solid::Pipeline::bind_group(
+ self.solid.constants = solid::Layer::bind_group(
device,
self.solid.uniforms.raw(),
- &self.solid.bind_group_layout,
+ &solid.constants_layout,
)
}
- self.solid.uniforms.write(device, staging_belt, encoder);
+ self.solid.uniforms.write(queue);
}
#[cfg(not(target_arch = "wasm32"))]
@@ -218,22 +215,169 @@ impl Pipeline {
let storage_resized = self.gradient.storage.resize(device);
if uniforms_resized || storage_resized {
- self.gradient.bind_group = gradient::Pipeline::bind_group(
+ self.gradient.constants = gradient::Layer::bind_group(
device,
self.gradient.uniforms.raw(),
self.gradient.storage.raw(),
- &self.gradient.bind_group_layout,
+ &gradient.constants_layout,
);
}
// Write to GPU
- self.gradient.uniforms.write(device, staging_belt, encoder);
- self.gradient.storage.write(device, staging_belt, encoder);
+ self.gradient.uniforms.write(queue);
+ self.gradient.storage.write(queue);
// Cleanup
self.gradient.color_stop_offset = 0;
self.gradient.color_stops_pending_write.color_stops.clear();
}
+ }
+
+ fn render<'a>(
+ &'a self,
+ solid: &'a solid::Pipeline,
+ #[cfg(not(target_arch = "wasm32"))] gradient: &'a gradient::Pipeline,
+ meshes: &[Mesh<'_>],
+ scale_factor: f32,
+ render_pass: &mut wgpu::RenderPass<'a>,
+ ) {
+ let mut num_solids = 0;
+ #[cfg(not(target_arch = "wasm32"))]
+ let mut num_gradients = 0;
+ let mut last_is_solid = None;
+
+ for (index, mesh) in meshes.iter().enumerate() {
+ let clip_bounds = (mesh.clip_bounds() * scale_factor).snap();
+
+ render_pass.set_scissor_rect(
+ clip_bounds.x,
+ clip_bounds.y,
+ clip_bounds.width,
+ clip_bounds.height,
+ );
+
+ match mesh {
+ Mesh::Solid { .. } => {
+ if !last_is_solid.unwrap_or(false) {
+ render_pass.set_pipeline(&solid.pipeline);
+
+ last_is_solid = Some(true);
+ }
+
+ render_pass.set_bind_group(
+ 0,
+ &self.solid.constants,
+ &[self.solid.uniforms.offset_at_index(num_solids)],
+ );
+
+ render_pass.set_vertex_buffer(
+ 0,
+ self.solid.vertices.slice_from_index(num_solids),
+ );
+
+ num_solids += 1;
+ }
+ #[cfg(not(target_arch = "wasm32"))]
+ Mesh::Gradient { .. } => {
+ if last_is_solid.unwrap_or(true) {
+ render_pass.set_pipeline(&gradient.pipeline);
+
+ last_is_solid = Some(false);
+ }
+
+ render_pass.set_bind_group(
+ 0,
+ &self.gradient.constants,
+ &[self
+ .gradient
+ .uniforms
+ .offset_at_index(num_gradients)],
+ );
+
+ render_pass.set_vertex_buffer(
+ 0,
+ self.gradient.vertices.slice_from_index(num_gradients),
+ );
+
+ num_gradients += 1;
+ }
+ #[cfg(target_arch = "wasm32")]
+ Mesh::Gradient { .. } => {}
+ };
+
+ render_pass.set_index_buffer(
+ self.index_buffer.slice_from_index(index),
+ wgpu::IndexFormat::Uint32,
+ );
+
+ render_pass.draw_indexed(0..self.index_strides[index], 0, 0..1);
+ }
+ }
+}
+
+impl Pipeline {
+ pub fn new(
+ device: &wgpu::Device,
+ format: wgpu::TextureFormat,
+ antialiasing: Option<settings::Antialiasing>,
+ ) -> Pipeline {
+ Pipeline {
+ blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
+ solid: solid::Pipeline::new(device, format, antialiasing),
+
+ #[cfg(not(target_arch = "wasm32"))]
+ gradient: gradient::Pipeline::new(device, format, antialiasing),
+
+ layers: Vec::new(),
+ prepare_layer: 0,
+ }
+ }
+
+ pub fn prepare(
+ &mut self,
+ device: &wgpu::Device,
+ queue: &wgpu::Queue,
+ meshes: &[Mesh<'_>],
+ transformation: Transformation,
+ ) {
+ #[cfg(feature = "tracing")]
+ let _ = info_span!("Wgpu::Triangle", "PREPARE").entered();
+
+ if self.layers.len() <= self.prepare_layer {
+ self.layers.push(Layer::new(
+ device,
+ &self.solid,
+ #[cfg(not(target_arch = "wasm32"))]
+ &self.gradient,
+ ));
+ }
+
+ let layer = &mut self.layers[self.prepare_layer];
+ layer.prepare(
+ device,
+ queue,
+ &self.solid,
+ #[cfg(not(target_arch = "wasm32"))]
+ &self.gradient,
+ meshes,
+ transformation,
+ );
+
+ self.prepare_layer += 1;
+ }
+
+ pub fn render(
+ &mut self,
+ device: &wgpu::Device,
+ encoder: &mut wgpu::CommandEncoder,
+ target: &wgpu::TextureView,
+ layer: usize,
+ target_size: Size<u32>,
+ meshes: &[Mesh<'_>],
+ scale_factor: f32,
+ ) {
+ #[cfg(feature = "tracing")]
+ let _ = info_span!("Wgpu::Triangle", "DRAW").entered();
// Configure render pass
{
@@ -268,87 +412,26 @@ impl Pipeline {
depth_stencil_attachment: None,
});
- let mut num_solids = 0;
- #[cfg(not(target_arch = "wasm32"))]
- let mut num_gradients = 0;
- let mut last_is_solid = None;
-
- for (index, mesh) in meshes.iter().enumerate() {
- let clip_bounds = (mesh.clip_bounds() * scale_factor).snap();
-
- render_pass.set_scissor_rect(
- clip_bounds.x,
- clip_bounds.y,
- clip_bounds.width,
- clip_bounds.height,
- );
-
- match mesh {
- Mesh::Solid { .. } => {
- if !last_is_solid.unwrap_or(false) {
- render_pass.set_pipeline(&self.solid.pipeline);
-
- last_is_solid = Some(true);
- }
-
- render_pass.set_bind_group(
- 0,
- &self.solid.bind_group,
- &[self.solid.uniforms.offset_at_index(num_solids)],
- );
-
- render_pass.set_vertex_buffer(
- 0,
- self.solid.vertices.slice_from_index(num_solids),
- );
-
- num_solids += 1;
- }
- #[cfg(not(target_arch = "wasm32"))]
- Mesh::Gradient { .. } => {
- if last_is_solid.unwrap_or(true) {
- render_pass.set_pipeline(&self.gradient.pipeline);
-
- last_is_solid = Some(false);
- }
-
- render_pass.set_bind_group(
- 0,
- &self.gradient.bind_group,
- &[self
- .gradient
- .uniforms
- .offset_at_index(num_gradients)],
- );
-
- render_pass.set_vertex_buffer(
- 0,
- self.gradient
- .vertices
- .slice_from_index(num_gradients),
- );
-
- num_gradients += 1;
- }
- #[cfg(target_arch = "wasm32")]
- Mesh::Gradient { .. } => {}
- };
-
- render_pass.set_index_buffer(
- self.index_buffer.slice_from_index(index),
- wgpu::IndexFormat::Uint32,
- );
+ let layer = &mut self.layers[layer];
- render_pass.draw_indexed(0..self.index_strides[index], 0, 0..1);
- }
+ layer.render(
+ &self.solid,
+ #[cfg(not(target_arch = "wasm32"))]
+ &self.gradient,
+ meshes,
+ scale_factor,
+ &mut render_pass,
+ );
}
- self.index_buffer.clear();
-
if let Some(blit) = &mut self.blit {
blit.draw(encoder, target);
}
}
+
+ pub fn end_frame(&mut self) {
+ self.prepare_layer = 0;
+ }
}
fn fragment_target(
@@ -390,10 +473,62 @@ mod solid {
#[derive(Debug)]
pub struct Pipeline {
pub pipeline: wgpu::RenderPipeline,
+ pub constants_layout: wgpu::BindGroupLayout,
+ }
+
+ #[derive(Debug)]
+ pub struct Layer {
pub vertices: Buffer<triangle::ColoredVertex2D>,
pub uniforms: dynamic::Buffer<Uniforms>,
- pub bind_group_layout: wgpu::BindGroupLayout,
- pub bind_group: wgpu::BindGroup,
+ pub constants: wgpu::BindGroup,
+ }
+
+ impl Layer {
+ pub fn new(
+ device: &wgpu::Device,
+ constants_layout: &wgpu::BindGroupLayout,
+ ) -> Self {
+ let vertices = Buffer::new(
+ device,
+ "iced_wgpu::triangle::solid vertex buffer",
+ wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+ );
+
+ let uniforms = dynamic::Buffer::uniform(
+ device,
+ "iced_wgpu::triangle::solid uniforms",
+ );
+
+ let constants =
+ Self::bind_group(device, uniforms.raw(), constants_layout);
+
+ Self {
+ vertices,
+ uniforms,
+ constants,
+ }
+ }
+
+ pub fn bind_group(
+ device: &wgpu::Device,
+ buffer: &wgpu::Buffer,
+ layout: &wgpu::BindGroupLayout,
+ ) -> wgpu::BindGroup {
+ device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::triangle::solid bind group"),
+ layout,
+ entries: &[wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::Buffer(
+ wgpu::BufferBinding {
+ buffer,
+ offset: 0,
+ size: Some(Uniforms::min_size()),
+ },
+ ),
+ }],
+ })
+ }
}
#[derive(Debug, Clone, Copy, ShaderType)]
@@ -416,18 +551,7 @@ mod solid {
format: wgpu::TextureFormat,
antialiasing: Option<settings::Antialiasing>,
) -> Self {
- let vertices = Buffer::new(
- device,
- "iced_wgpu::triangle::solid vertex buffer",
- wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
- );
-
- let uniforms = dynamic::Buffer::uniform(
- device,
- "iced_wgpu::triangle::solid uniforms",
- );
-
- let bind_group_layout = device.create_bind_group_layout(
+ let constants_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
label: Some("iced_wgpu::triangle::solid bind group layout"),
entries: &[wgpu::BindGroupLayoutEntry {
@@ -443,13 +567,10 @@ mod solid {
},
);
- let bind_group =
- Self::bind_group(device, uniforms.raw(), &bind_group_layout);
-
let layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu::triangle::solid pipeline layout"),
- bind_group_layouts: &[&bind_group_layout],
+ bind_group_layouts: &[&constants_layout],
push_constant_ranges: &[],
},
);
@@ -501,33 +622,9 @@ mod solid {
Self {
pipeline,
- vertices,
- uniforms,
- bind_group_layout,
- bind_group,
+ constants_layout,
}
}
-
- pub fn bind_group(
- device: &wgpu::Device,
- buffer: &wgpu::Buffer,
- layout: &wgpu::BindGroupLayout,
- ) -> wgpu::BindGroup {
- device.create_bind_group(&wgpu::BindGroupDescriptor {
- label: Some("iced_wgpu::triangle::solid bind group"),
- layout,
- entries: &[wgpu::BindGroupEntry {
- binding: 0,
- resource: wgpu::BindingResource::Buffer(
- wgpu::BufferBinding {
- buffer,
- offset: 0,
- size: Some(Uniforms::min_size()),
- },
- ),
- }],
- })
- }
}
}
@@ -545,15 +642,90 @@ mod gradient {
#[derive(Debug)]
pub struct Pipeline {
pub pipeline: wgpu::RenderPipeline,
+ pub constants_layout: wgpu::BindGroupLayout,
+ }
+
+ #[derive(Debug)]
+ pub struct Layer {
pub vertices: Buffer<Vertex2D>,
pub uniforms: dynamic::Buffer<Uniforms>,
pub storage: dynamic::Buffer<Storage>,
+ pub constants: wgpu::BindGroup,
pub color_stop_offset: i32,
//Need to store these and then write them all at once
//or else they will be padded to 256 and cause gaps in the storage buffer
pub color_stops_pending_write: Storage,
- pub bind_group_layout: wgpu::BindGroupLayout,
- pub bind_group: wgpu::BindGroup,
+ }
+
+ impl Layer {
+ pub fn new(
+ device: &wgpu::Device,
+ constants_layout: &wgpu::BindGroupLayout,
+ ) -> Self {
+ let vertices = Buffer::new(
+ device,
+ "iced_wgpu::triangle::gradient vertex buffer",
+ wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
+ );
+
+ let uniforms = dynamic::Buffer::uniform(
+ device,
+ "iced_wgpu::triangle::gradient uniforms",
+ );
+
+ // Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static
+ // sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work
+ let storage = dynamic::Buffer::storage(
+ device,
+ "iced_wgpu::triangle::gradient storage",
+ );
+
+ let constants = Self::bind_group(
+ device,
+ uniforms.raw(),
+ storage.raw(),
+ constants_layout,
+ );
+
+ Self {
+ vertices,
+ uniforms,
+ storage,
+ constants,
+ color_stop_offset: 0,
+ color_stops_pending_write: Storage {
+ color_stops: vec![],
+ },
+ }
+ }
+
+ pub fn bind_group(
+ device: &wgpu::Device,
+ uniform_buffer: &wgpu::Buffer,
+ storage_buffer: &wgpu::Buffer,
+ layout: &wgpu::BindGroupLayout,
+ ) -> wgpu::BindGroup {
+ device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: Some("iced_wgpu::triangle::gradient bind group"),
+ layout,
+ entries: &[
+ wgpu::BindGroupEntry {
+ binding: 0,
+ resource: wgpu::BindingResource::Buffer(
+ wgpu::BufferBinding {
+ buffer: uniform_buffer,
+ offset: 0,
+ size: Some(Uniforms::min_size()),
+ },
+ ),
+ },
+ wgpu::BindGroupEntry {
+ binding: 1,
+ resource: storage_buffer.as_entire_binding(),
+ },
+ ],
+ })
+ }
}
#[derive(Debug, ShaderType)]
@@ -584,25 +756,7 @@ mod gradient {
format: wgpu::TextureFormat,
antialiasing: Option<settings::Antialiasing>,
) -> Self {
- let vertices = Buffer::new(
- device,
- "iced_wgpu::triangle::gradient vertex buffer",
- wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
- );
-
- let uniforms = dynamic::Buffer::uniform(
- device,
- "iced_wgpu::triangle::gradient uniforms",
- );
-
- //Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static
- // sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work
- let storage = dynamic::Buffer::storage(
- device,
- "iced_wgpu::triangle::gradient storage",
- );
-
- let bind_group_layout = device.create_bind_group_layout(
+ let constants_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
label: Some(
"iced_wgpu::triangle::gradient bind group layout",
@@ -634,19 +788,12 @@ mod gradient {
},
);
- let bind_group = Pipeline::bind_group(
- device,
- uniforms.raw(),
- storage.raw(),
- &bind_group_layout,
- );
-
let layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
label: Some(
"iced_wgpu::triangle::gradient pipeline layout",
),
- bind_group_layouts: &[&bind_group_layout],
+ bind_group_layouts: &[&constants_layout],
push_constant_ranges: &[],
},
);
@@ -694,44 +841,8 @@ mod gradient {
Self {
pipeline,
- vertices,
- uniforms,
- storage,
- color_stop_offset: 0,
- color_stops_pending_write: Storage {
- color_stops: vec![],
- },
- bind_group_layout,
- bind_group,
+ constants_layout,
}
}
-
- pub fn bind_group(
- device: &wgpu::Device,
- uniform_buffer: &wgpu::Buffer,
- storage_buffer: &wgpu::Buffer,
- layout: &wgpu::BindGroupLayout,
- ) -> wgpu::BindGroup {
- device.create_bind_group(&wgpu::BindGroupDescriptor {
- label: Some("iced_wgpu::triangle::gradient bind group"),
- layout,
- entries: &[
- wgpu::BindGroupEntry {
- binding: 0,
- resource: wgpu::BindingResource::Buffer(
- wgpu::BufferBinding {
- buffer: uniform_buffer,
- offset: 0,
- size: Some(Uniforms::min_size()),
- },
- ),
- },
- wgpu::BindGroupEntry {
- binding: 1,
- resource: storage_buffer.as_entire_binding(),
- },
- ],
- })
- }
}
}
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 6d0c36f6..365cb603 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -16,14 +16,11 @@ pub struct Compositor<Theme> {
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
- staging_belt: wgpu::util::StagingBelt,
format: wgpu::TextureFormat,
theme: PhantomData<Theme>,
}
impl<Theme> Compositor<Theme> {
- const CHUNK_SIZE: u64 = 10 * 1024;
-
/// Requests a new [`Compositor`] with the given [`Settings`].
///
/// Returns `None` if no compatible graphics adapter could be found.
@@ -98,15 +95,12 @@ impl<Theme> Compositor<Theme> {
.next()
.await?;
- let staging_belt = wgpu::util::StagingBelt::new(Self::CHUNK_SIZE);
-
Some(Compositor {
instance,
settings,
adapter,
device,
queue,
- staging_belt,
format,
theme: PhantomData,
})
@@ -114,7 +108,7 @@ impl<Theme> Compositor<Theme> {
/// Creates a new rendering [`Backend`] for this [`Compositor`].
pub fn create_backend(&self) -> Backend {
- Backend::new(&self.device, self.settings, self.format)
+ Backend::new(&self.device, &self.queue, self.settings, self.format)
}
}
@@ -196,39 +190,12 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
- let _ =
- encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
- label: Some(
- "iced_wgpu::window::Compositor render pass",
- ),
- color_attachments: &[Some(
- wgpu::RenderPassColorAttachment {
- view,
- resolve_target: None,
- ops: wgpu::Operations {
- load: wgpu::LoadOp::Clear({
- let [r, g, b, a] =
- background_color.into_linear();
-
- wgpu::Color {
- r: f64::from(r),
- g: f64::from(g),
- b: f64::from(b),
- a: f64::from(a),
- }
- }),
- store: true,
- },
- },
- )],
- depth_stencil_attachment: None,
- });
-
renderer.with_primitives(|backend, primitives| {
backend.present(
&self.device,
- &mut self.staging_belt,
+ &self.queue,
&mut encoder,
+ Some(background_color),
view,
primitives,
viewport,
@@ -237,13 +204,9 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
});
// Submit work
- self.staging_belt.finish();
let _submission = self.queue.submit(Some(encoder.finish()));
frame.present();
- // Recall staging buffers
- self.staging_belt.recall();
-
Ok(())
}
Err(error) => match error {
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 9781a453..889becad 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -851,6 +851,16 @@ pub fn run_command<A, E>(
current_cache = user_interface.into_cache();
*cache = current_cache;
}
+ command::Action::LoadFont { bytes, tagger } => {
+ use crate::text::Renderer;
+
+ // TODO: Error handling (?)
+ renderer.load_font(bytes);
+
+ proxy
+ .send_event(tagger(Ok(())))
+ .expect("Send message to event loop");
+ }
}
}
}
diff --git a/winit/src/settings.rs b/winit/src/settings.rs
index 78d58000..5c64727b 100644
--- a/winit/src/settings.rs
+++ b/winit/src/settings.rs
@@ -51,14 +51,6 @@ pub struct Settings<Flags> {
///
/// [`Application`]: crate::Application
pub exit_on_close_request: bool,
-
- /// Whether the [`Application`] should try to build the context
- /// using OpenGL ES first then OpenGL.
- ///
- /// NOTE: Only works for the `glow` backend.
- ///
- /// [`Application`]: crate::Application
- pub try_opengles_first: bool,
}
/// The window settings of an application.