summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2022-01-26 13:54:21 +0700
committerLibravatar GitHub <noreply@github.com>2022-01-26 13:54:21 +0700
commit9b3cab82ef3bbc5b8129ac3bacec4011229f0fbc (patch)
treebc9fbd32c8c589bc56d2458bb77bc8293ba8a6bd
parent4aa943cbc63230dfcb995c469ceec9f74e6132e1 (diff)
parent522368e8af6947ea0676f62bf1ae5f68acb2058d (diff)
downloadiced-9b3cab82ef3bbc5b8129ac3bacec4011229f0fbc.tar.gz
iced-9b3cab82ef3bbc5b8129ac3bacec4011229f0fbc.tar.bz2
iced-9b3cab82ef3bbc5b8129ac3bacec4011229f0fbc.zip
Merge pull request #1160 from derezzedex/dev/arm-support
feat: add support to ARM devices (and older hardware)
-rw-r--r--.github/workflows/build.yml26
-rw-r--r--Cross.toml7
-rw-r--r--ECOSYSTEM.md2
-rw-r--r--README.md35
-rw-r--r--examples/game_of_life/Cargo.toml1
-rw-r--r--examples/game_of_life/src/main.rs2
-rw-r--r--examples/integration_opengl/Cargo.toml1
-rw-r--r--examples/integration_opengl/src/main.rs11
-rw-r--r--examples/integration_opengl/src/scene.rs1
-rw-r--r--glow/Cargo.toml4
-rw-r--r--glow/README.md51
-rw-r--r--glow/src/backend.rs7
-rw-r--r--glow/src/lib.rs2
-rw-r--r--glow/src/program.rs130
-rw-r--r--glow/src/quad.rs243
-rw-r--r--glow/src/quad/compatibility.rs360
-rw-r--r--glow/src/quad/core.rs246
-rw-r--r--glow/src/shader/common/triangle.frag18
-rw-r--r--glow/src/shader/common/triangle.vert (renamed from glow/src/shader/triangle.vert)8
-rw-r--r--glow/src/shader/compatibility/quad.frag67
-rw-r--r--glow/src/shader/compatibility/quad.vert44
-rw-r--r--glow/src/shader/core/quad.frag (renamed from glow/src/shader/quad.frag)25
-rw-r--r--glow/src/shader/core/quad.vert (renamed from glow/src/shader/quad.vert)16
-rw-r--r--glow/src/shader/triangle.frag9
-rw-r--r--glow/src/triangle.rs27
-rw-r--r--glow/src/window/compositor.rs7
-rw-r--r--glutin/src/application.rs19
-rw-r--r--glutin/src/lib.rs1
-rw-r--r--src/settings.rs12
-rw-r--r--winit/src/settings.rs6
30 files changed, 1108 insertions, 280 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 25909870..beec168b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -82,3 +82,29 @@ jobs:
with:
name: todos-x86_64-apple-darwin
path: target/release/todos
+
+ todos_raspberry:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: hecrj/setup-rust-action@v1
+ - uses: actions/checkout@master
+ - name: Install cross
+ run: cargo install cross
+ - name: Enable Link Time Optimizations
+ run: |
+ echo "[profile.release]" >> Cargo.toml
+ echo "lto = true" >> Cargo.toml
+ - name: Build todos binary for Raspberry Pi 3/4 (64 bits)
+ run: cross build --verbose --release --package todos --target aarch64-unknown-linux-gnu
+ - name: Archive todos binary
+ uses: actions/upload-artifact@v1
+ with:
+ name: todos-aarch64-unknown-linux-gnu
+ path: target/aarch64-unknown-linux-gnu/release/todos
+ - name: Build todos binary for Raspberry Pi 2/3/4 (32 bits)
+ run: cross build --verbose --release --package todos --target armv7-unknown-linux-gnueabihf
+ - name: Archive todos binary
+ uses: actions/upload-artifact@v1
+ with:
+ name: todos-armv7-unknown-linux-gnueabihf
+ path: target/armv7-unknown-linux-gnueabihf/release/todos
diff --git a/Cross.toml b/Cross.toml
new file mode 100644
index 00000000..17cbf9c1
--- /dev/null
+++ b/Cross.toml
@@ -0,0 +1,7 @@
+[target.aarch64-unknown-linux-gnu]
+image = "icedrs/iced:aarch64"
+xargo = false
+
+[target.armv7-unknown-linux-gnueabihf]
+image = "icedrs/iced:armv7"
+xargo = false
diff --git a/ECOSYSTEM.md b/ECOSYSTEM.md
index 82303130..fb0bd56d 100644
--- a/ECOSYSTEM.md
+++ b/ECOSYSTEM.md
@@ -45,7 +45,7 @@ The widgets of a _graphical_ user interface produce some primitives that eventua
Currently, there are two different official renderers:
- [`iced_wgpu`] is powered by [`wgpu`] and supports Vulkan, DirectX 12, and Metal.
-- [`iced_glow`] is powered by [`glow`] and supports OpenGL 3.3+.
+- [`iced_glow`] is powered by [`glow`] and supports OpenGL 2.1+ and OpenGL ES 2.0+.
Additionally, the [`iced_graphics`] subcrate contains a bunch of backend-agnostic types that can be leveraged to build renderers. Both of the renderers rely on the graphical foundations provided by this crate.
diff --git a/README.md b/README.md
index f6917fb4..943c5609 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,9 @@ Inspired by [Elm].
* First-class support for async actions (use futures!)
* [Modular ecosystem] split into reusable parts:
* A [renderer-agnostic native runtime] enabling integration with existing systems
- * A [built-in renderer] supporting Vulkan, Metal, DX11, and DX12
+ * Two [built-in renderers] leveraging [`wgpu`] and [`glow`]
+ * [`iced_wgpu`] supporting Vulkan, Metal and DX12
+ * [`iced_glow`] supporting OpenGL 2.1+ and OpenGL ES 2.0+
* A [windowing shell]
* A [web runtime] leveraging the DOM
@@ -49,7 +51,10 @@ __iced is currently experimental software.__ [Take a look at the roadmap],
[Modular ecosystem]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md
[renderer-agnostic native runtime]: https://github.com/hecrj/iced/tree/master/native
[`wgpu`]: https://github.com/gfx-rs/wgpu-rs
-[built-in renderer]: https://github.com/hecrj/iced/tree/master/wgpu
+[`glow`]: https://github.com/grovesNL/glow
+[`iced_wgpu`]: https://github.com/hecrj/iced/tree/master/wgpu
+[`iced_glow`]: https://github.com/hecrj/iced/tree/master/glow
+[built-in renderers]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md#Renderers
[windowing shell]: https://github.com/hecrj/iced/tree/master/winit
[`dodrio`]: https://github.com/fitzgen/dodrio
[web runtime]: https://github.com/hecrj/iced/tree/master/web
@@ -195,6 +200,32 @@ end-user-oriented GUI library, while keeping [the ecosystem] modular:
[`ggez`]: https://github.com/ggez/ggez
[the ecosystem]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md
+## Common problems
+
+1. `Error: GraphicsAdapterNotFound`
+
+ This occurs when the selected [built-in renderer] is not able to create a context.
+
+ Often this will occur while using [`iced_wgpu`] as the renderer without
+ supported hardware (needs Vulkan, Metal or DX12). In this case, you could try using the
+ [`iced_glow`] renderer:
+
+ First, check if it works with
+ ```console
+ $ cargo run --features "iced/glow iced/glow_canvas" --package game_of_life
+ ```
+
+ and then use it in your project with
+ ```toml
+ iced = { version = "0.3", default-features = false, features = ["glow"] }
+ ```
+
+ **NOTE:** Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0,
+ but if you don't, right now there's no software fallback, so it means your hardware
+ doesn't support Iced.
+
+[built-in renderer]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md#Renderers
+
## Contributing / Feedback
Contributions are greatly appreciated! If you want to contribute, please
read our [contributing guidelines] for more details.
diff --git a/examples/game_of_life/Cargo.toml b/examples/game_of_life/Cargo.toml
index ffd2f19e..9ee307ac 100644
--- a/examples/game_of_life/Cargo.toml
+++ b/examples/game_of_life/Cargo.toml
@@ -10,3 +10,4 @@ iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
tokio = { version = "1.0", features = ["sync"] }
itertools = "0.9"
rustc-hash = "1.1"
+env_logger = "0.9"
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 50112618..ab8b80e4 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -18,6 +18,8 @@ use preset::Preset;
use std::time::{Duration, Instant};
pub fn main() -> iced::Result {
+ env_logger::builder().format_timestamp(None).init();
+
GameOfLife::run(Settings {
antialiasing: true,
window: window::Settings {
diff --git a/examples/integration_opengl/Cargo.toml b/examples/integration_opengl/Cargo.toml
index 0917f2ec..7fb1f2dd 100644
--- a/examples/integration_opengl/Cargo.toml
+++ b/examples/integration_opengl/Cargo.toml
@@ -10,4 +10,3 @@ iced_glutin = { path = "../../glutin" }
iced_glow = { path = "../../glow" }
iced_winit = { path = "../../winit" }
env_logger = "0.8"
-glow = "0.6"
diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs
index 551aba67..1007b90f 100644
--- a/examples/integration_opengl/src/main.rs
+++ b/examples/integration_opengl/src/main.rs
@@ -4,16 +4,15 @@ mod scene;
use controls::Controls;
use scene::Scene;
-use glow;
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::glutin::event::{Event, WindowEvent};
-use iced_glutin::glutin::event_loop::ControlFlow;
use iced_glutin::{program, Clipboard, Debug, Size};
-use iced_winit::conversion;
-use iced_winit::winit;
-use winit::{dpi::PhysicalPosition, event::ModifiersState};
pub fn main() {
env_logger::init();
diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs
index a1245bf2..fc74b78a 100644
--- a/examples/integration_opengl/src/scene.rs
+++ b/examples/integration_opengl/src/scene.rs
@@ -1,4 +1,5 @@
use glow::*;
+use iced_glow::glow;
use iced_glow::Color;
pub struct Scene {
diff --git a/glow/Cargo.toml b/glow/Cargo.toml
index e40b8ba8..1fab7c53 100644
--- a/glow/Cargo.toml
+++ b/glow/Cargo.toml
@@ -16,8 +16,8 @@ image = []
svg = []
[dependencies]
-glow = "0.6"
-glow_glyph = "0.4"
+glow = "0.11.1"
+glow_glyph = "0.5.0"
glyph_brush = "0.7"
euclid = "0.22"
bytemuck = "1.4"
diff --git a/glow/README.md b/glow/README.md
new file mode 100644
index 00000000..c6b7245e
--- /dev/null
+++ b/glow/README.md
@@ -0,0 +1,51 @@
+# `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/hecrj/iced/blob/master/LICENSE)
+[![project chat](https://img.shields.io/badge/chat-on_zulip-brightgreen.svg)](https://iced.zulipchat.com)
+
+`iced_glow` is a [`glow`] renderer for [`iced_native`]. This renderer supports OpenGL 3.0+ and OpenGL ES 2.0.
+
+This is 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.2"
+```
+
+__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/hecrj/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
index 5ab7f922..89dc1aaa 100644
--- a/glow/src/backend.rs
+++ b/glow/src/backend.rs
@@ -1,3 +1,4 @@
+use crate::program;
use crate::quad;
use crate::text;
use crate::triangle;
@@ -30,8 +31,10 @@ impl Backend {
settings.text_multithreading,
);
- let quad_pipeline = quad::Pipeline::new(gl);
- let triangle_pipeline = triangle::Pipeline::new(gl);
+ let shader_version = program::Version::new(gl);
+
+ let quad_pipeline = quad::Pipeline::new(gl, &shader_version);
+ let triangle_pipeline = triangle::Pipeline::new(gl, &shader_version);
Self {
quad_pipeline,
diff --git a/glow/src/lib.rs b/glow/src/lib.rs
index 362933d4..4e5a75d7 100644
--- a/glow/src/lib.rs
+++ b/glow/src/lib.rs
@@ -13,6 +13,8 @@
#![forbid(rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg))]
+pub use glow;
+
mod backend;
mod program;
mod quad;
diff --git a/glow/src/program.rs b/glow/src/program.rs
index 601f9ce6..9a02d578 100644
--- a/glow/src/program.rs
+++ b/glow/src/program.rs
@@ -1,28 +1,122 @@
use glow::HasContext;
-pub unsafe fn create(
- gl: &glow::Context,
- shader_sources: &[(u32, &str)],
-) -> <glow::Context as HasContext>::Program {
- let program = gl.create_program().expect("Cannot create program");
+/// 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", version.minor + 3),
+ format!(
+ "#version 1{}0\n#define HIGHER_THAN_300 1",
+ version.minor + 3
+ ),
+ ),
+ // OpenGL 3.3+
+ (3 | 4, _, false) => (
+ format!("#version {}{}0", version.major, version.minor),
+ format!(
+ "#version {}{}0\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),
+ };
- let mut shaders = Vec::with_capacity(shader_sources.len());
+ log::info!("Shader directive: {}", vertex.lines().next().unwrap());
- for (shader_type, shader_source) in shader_sources.iter() {
- let shader = gl
- .create_shader(*shader_type)
- .expect("Cannot create shader");
+ 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, shader_source);
- gl.compile_shader(shader);
+ gl.shader_source(shader, &content);
+ gl.compile_shader(shader);
- if !gl.get_shader_compile_status(shader) {
- panic!("{}", gl.get_shader_info_log(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);
- gl.attach_shader(program, shader);
+ 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);
+ }
- shaders.push(shader);
+ for (i, name) in attributes {
+ gl.bind_attrib_location(program, *i, name);
}
gl.link_program(program);
@@ -31,8 +125,8 @@ pub unsafe fn create(
}
for shader in shaders {
- gl.detach_shader(program, shader);
- gl.delete_shader(shader);
+ gl.detach_shader(program, shader.0);
+ gl.delete_shader(shader.0);
}
program
diff --git a/glow/src/quad.rs b/glow/src/quad.rs
index a8fbb9e5..d9f1c6ae 100644
--- a/glow/src/quad.rs
+++ b/glow/src/quad.rs
@@ -1,77 +1,35 @@
+mod compatibility;
+mod core;
+
use crate::program;
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,
+pub enum Pipeline {
+ Core(core::Pipeline),
+ Compatibility(compatibility::Pipeline),
}
impl Pipeline {
- pub fn new(gl: &glow::Context) -> Pipeline {
- let program = unsafe {
- program::create(
+ 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,
- &[
- (glow::VERTEX_SHADER, include_str!("shader/quad.vert")),
- (glow::FRAGMENT_SHADER, include_str!("shader/quad.frag")),
- ],
- )
- };
-
- 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));
-
- let matrix: [f32; 16] = Transformation::identity().into();
- gl.uniform_matrix_4_f32_slice(
- Some(&transform_location),
- false,
- &matrix,
- );
-
- 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,
+ shader_version,
+ ))
}
}
@@ -84,152 +42,27 @@ impl Pipeline {
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 {
- let matrix: [f32; 16] = transformation.into();
- gl.uniform_matrix_4_f32_slice(
- Some(&self.transform_location),
- false,
- &matrix,
+ match self {
+ Pipeline::Core(pipeline) => {
+ pipeline.draw(
+ gl,
+ target_height,
+ instances,
+ transformation,
+ scale,
+ bounds,
);
-
- 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,
+ Pipeline::Compatibility(pipeline) => {
+ pipeline.draw(
+ gl,
+ target_height,
+ instances,
+ transformation,
+ scale,
+ bounds,
);
}
-
- self.current_target_height = target_height;
- }
-
- let mut i = 0;
- let total = instances.len();
-
- while i < total {
- let end = (i + MAX_INSTANCES).min(total);
- let amount = end - i;
-
- unsafe {
- gl.buffer_sub_data_u8_slice(
- glow::ARRAY_BUFFER,
- 0,
- bytemuck::cast_slice(&instances[i..end]),
- );
-
- gl.draw_arrays_instanced(
- glow::TRIANGLE_STRIP,
- 0,
- 4,
- amount as i32,
- );
- }
-
- i += MAX_INSTANCES;
- }
-
- 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,
- 1,
- 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 + 1),
- );
- 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/quad/compatibility.rs b/glow/src/quad/compatibility.rs
new file mode 100644
index 00000000..76f98ab7
--- /dev/null
+++ b/glow/src/quad/compatibility.rs
@@ -0,0 +1,360 @@
+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));
+
+ let matrix: [f32; 16] = Transformation::identity().into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&transform_location),
+ false,
+ &matrix,
+ );
+
+ 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(|quad| Vertex::from_quad(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| {
+ [
+ 0 + 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 {
+ let matrix: [f32; 16] = transformation.into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&self.transform_location),
+ false,
+ &matrix,
+ );
+
+ 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,
+ 1,
+ 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 + 1),
+ );
+
+ gl.enable_vertex_attrib_array(6);
+ gl.vertex_attrib_pointer_f32(
+ 6,
+ 2,
+ glow::FLOAT,
+ false,
+ stride,
+ 4 * (2 + 2 + 4 + 4 + 1 + 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,
+
+ /// 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
new file mode 100644
index 00000000..f37300f6
--- /dev/null
+++ b/glow/src/quad/core.rs
@@ -0,0 +1,246 @@
+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));
+
+ let matrix: [f32; 16] = Transformation::identity().into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&transform_location),
+ false,
+ &matrix,
+ );
+
+ 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 {
+ let matrix: [f32; 16] = transformation.into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&self.transform_location),
+ false,
+ &matrix,
+ );
+
+ 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,
+ 1,
+ 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 + 1),
+ );
+ 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/shader/common/triangle.frag b/glow/src/shader/common/triangle.frag
new file mode 100644
index 00000000..e8689f2e
--- /dev/null
+++ b/glow/src/shader/common/triangle.frag
@@ -0,0 +1,18 @@
+#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;
+} \ No newline at end of file
diff --git a/glow/src/shader/triangle.vert b/glow/src/shader/common/triangle.vert
index 5723436a..d0494a5f 100644
--- a/glow/src/shader/triangle.vert
+++ b/glow/src/shader/common/triangle.vert
@@ -1,13 +1,11 @@
-#version 330
-
uniform mat4 u_Transform;
-layout(location = 0) in vec2 i_Position;
-layout(location = 1) in vec4 i_Color;
+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;
-}
+} \ No newline at end of file
diff --git a/glow/src/shader/compatibility/quad.frag b/glow/src/shader/compatibility/quad.frag
new file mode 100644
index 00000000..8ea5693d
--- /dev/null
+++ b/glow/src/shader/compatibility/quad.frag
@@ -0,0 +1,67 @@
+#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 float 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);
+}
+
+void main() {
+ vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
+
+ float internal_border = max(v_BorderRadius - 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,
+ v_BorderRadius
+ );
+
+ float radius_alpha =
+ 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 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
new file mode 100644
index 00000000..abe70c0e
--- /dev/null
+++ b/glow/src/shader/compatibility/quad.vert
@@ -0,0 +1,44 @@
+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 float 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 float v_BorderRadius;
+varying float v_BorderWidth;
+
+
+void main() {
+ vec2 p_Pos = i_Pos * u_Scale;
+ vec2 p_Scale = i_Scale * u_Scale;
+
+ float i_BorderRadius = min(
+ i_BorderRadius,
+ 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/quad.frag b/glow/src/shader/core/quad.frag
index cea36bdc..57e2e8e7 100644
--- a/glow/src/shader/quad.frag
+++ b/glow/src/shader/core/quad.frag
@@ -1,4 +1,15 @@
-#version 330
+#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;
@@ -9,9 +20,7 @@ in vec2 v_Scale;
in float v_BorderRadius;
in float v_BorderWidth;
-out vec4 o_Color;
-
-float distance(in vec2 frag_coord, in vec2 position, in vec2 size, float radius)
+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;
@@ -35,10 +44,10 @@ void main() {
vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y);
// TODO: Remove branching (?)
- if(v_BorderWidth > 0) {
+ if(v_BorderWidth > 0.0) {
float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0);
- float internal_distance = distance(
+ float internal_distance = fDistance(
fragCoord,
v_Pos + vec2(v_BorderWidth),
v_Scale - vec2(v_BorderWidth * 2.0),
@@ -56,7 +65,7 @@ void main() {
mixed_color = v_Color;
}
- float d = distance(
+ float d = fDistance(
fragCoord,
v_Pos,
v_Scale,
@@ -66,5 +75,5 @@ void main() {
float radius_alpha =
1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d);
- o_Color = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
+ gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
}
diff --git a/glow/src/shader/quad.vert b/glow/src/shader/core/quad.vert
index 82417856..b1fb2365 100644
--- a/glow/src/shader/quad.vert
+++ b/glow/src/shader/core/quad.vert
@@ -1,14 +1,12 @@
-#version 330
-
uniform mat4 u_Transform;
uniform float u_Scale;
-layout(location = 0) in vec2 i_Pos;
-layout(location = 1) in vec2 i_Scale;
-layout(location = 2) in vec4 i_Color;
-layout(location = 3) in vec4 i_BorderColor;
-layout(location = 4) in float i_BorderRadius;
-layout(location = 5) in float i_BorderWidth;
+in vec2 i_Pos;
+in vec2 i_Scale;
+in vec4 i_Color;
+in vec4 i_BorderColor;
+in float i_BorderRadius;
+in float i_BorderWidth;
out vec4 v_Color;
out vec4 v_BorderColor;
@@ -17,7 +15,7 @@ out vec2 v_Scale;
out float v_BorderRadius;
out float v_BorderWidth;
-const vec2 positions[4] = vec2[](
+vec2 positions[4] = vec2[](
vec2(0.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 0.0),
diff --git a/glow/src/shader/triangle.frag b/glow/src/shader/triangle.frag
deleted file mode 100644
index d186784a..00000000
--- a/glow/src/shader/triangle.frag
+++ /dev/null
@@ -1,9 +0,0 @@
-#version 330
-
-in vec4 v_Color;
-
-out vec4 o_Color;
-
-void main() {
- o_Color = v_Color;
-}
diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs
index 9202bcb2..ae4f83ef 100644
--- a/glow/src/triangle.rs
+++ b/glow/src/triangle.rs
@@ -1,5 +1,5 @@
//! Draw meshes of triangles.
-use crate::program;
+use crate::program::{self, Shader};
use crate::Transformation;
use glow::HasContext;
use iced_graphics::layer;
@@ -21,17 +21,26 @@ pub(crate) struct Pipeline {
}
impl Pipeline {
- pub fn new(gl: &glow::Context) -> 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/triangle.vert"),
+ );
+ let fragment_shader = Shader::fragment(
+ gl,
+ shader_version,
+ include_str!("shader/common/triangle.frag"),
+ );
+
program::create(
gl,
- &[
- (glow::VERTEX_SHADER, include_str!("shader/triangle.vert")),
- (
- glow::FRAGMENT_SHADER,
- include_str!("shader/triangle.frag"),
- ),
- ],
+ &[vertex_shader, fragment_shader],
+ &[(0, "i_Position"), (1, "i_Color")],
)
};
diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs
index a85a4560..44019fb2 100644
--- a/glow/src/window/compositor.rs
+++ b/glow/src/window/compositor.rs
@@ -20,6 +20,13 @@ impl iced_graphics::window::GLCompositor for Compositor {
) -> Result<(Self, Self::Renderer), Error> {
let gl = glow::Context::from_loader_function(loader_function);
+ let version = gl.version();
+ log::info!("Version: {:?}", version);
+ log::info!("Embedded: {}", 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);
diff --git a/glutin/src/application.rs b/glutin/src/application.rs
index 437c17ee..27a932fc 100644
--- a/glutin/src/application.rs
+++ b/glutin/src/application.rs
@@ -61,10 +61,23 @@ where
settings.id,
);
- let context = ContextBuilder::new()
+ let opengl_builder = ContextBuilder::new()
.with_vsync(true)
- .with_multisampling(C::sample_count(&compositor_settings) as u16)
- .build_windowed(builder, &event_loop)
+ .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)
+ };
+
+ let context = first_builder
+ .build_windowed(builder.clone(), &event_loop)
+ .or_else(|_| second_builder.build_windowed(builder, &event_loop))
.map_err(|error| {
use glutin::CreationError;
diff --git a/glutin/src/lib.rs b/glutin/src/lib.rs
index 224296b7..72397791 100644
--- a/glutin/src/lib.rs
+++ b/glutin/src/lib.rs
@@ -20,6 +20,7 @@ pub use iced_native::*;
pub mod application;
pub use iced_winit::clipboard;
+pub use iced_winit::conversion;
pub use iced_winit::settings;
pub use iced_winit::window;
pub use iced_winit::{Error, Mode};
diff --git a/src/settings.rs b/src/settings.rs
index f7940a0b..c521a62a 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -55,6 +55,15 @@ 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> {
@@ -73,6 +82,7 @@ impl<Flags> Settings<Flags> {
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,
}
}
}
@@ -91,6 +101,7 @@ where
text_multithreading: false,
antialiasing: false,
exit_on_close_request: true,
+ try_opengles_first: false,
}
}
}
@@ -103,6 +114,7 @@ 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/winit/src/settings.rs b/winit/src/settings.rs
index 045cb156..9a93824a 100644
--- a/winit/src/settings.rs
+++ b/winit/src/settings.rs
@@ -38,6 +38,12 @@ pub struct Settings<Flags> {
/// Whether the [`Application`] should exit when the user requests the
/// window to close (e.g. the user presses the close button).
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.
+ pub try_opengles_first: bool,
}
/// The window settings of an application.