Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support to ARM devices (and older hardware) #1160

Merged
merged 13 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions Cross.toml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion ECOSYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions examples/game_of_life/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 2 additions & 0 deletions examples/game_of_life/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 0 additions & 1 deletion examples/integration_opengl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ iced_glutin = { path = "../../glutin" }
iced_glow = { path = "../../glow" }
iced_winit = { path = "../../winit" }
env_logger = "0.8"
glow = "0.6"
11 changes: 5 additions & 6 deletions examples/integration_opengl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions examples/integration_opengl/src/scene.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use glow::*;
use iced_glow::glow;
use iced_glow::Color;

pub struct Scene {
Expand Down
4 changes: 2 additions & 2 deletions glow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
51 changes: 51 additions & 0 deletions glow/README.md
Original file line number Diff line number Diff line change
@@ -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
hecrj marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 5 additions & 2 deletions glow/src/backend.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::program;
use crate::quad;
use crate::text;
use crate::triangle;
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions glow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#![forbid(rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg))]

pub use glow;

mod backend;
mod program;
mod quad;
Expand Down
130 changes: 112 additions & 18 deletions glow/src/program.rs
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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
Expand Down
Loading