Merge pull request #1160 from derezzedex/dev/arm-support
feat: add support to ARM devices (and older hardware)
This commit is contained in:
commit
9b3cab82ef
30 changed files with 1109 additions and 281 deletions
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
|
|
@ -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
Cross.toml
Normal file
7
Cross.toml
Normal file
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
35
README.md
35
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.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -10,4 +10,3 @@ iced_glutin = { path = "../../glutin" }
|
|||
iced_glow = { path = "../../glow" }
|
||||
iced_winit = { path = "../../winit" }
|
||||
env_logger = "0.8"
|
||||
glow = "0.6"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use glow::*;
|
||||
use iced_glow::glow;
|
||||
use iced_glow::Color;
|
||||
|
||||
pub struct Scene {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
51
glow/README.md
Normal file
51
glow/README.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# `iced_glow`
|
||||
[][documentation]
|
||||
[](https://crates.io/crates/iced_glow)
|
||||
[](https://github.com/hecrj/iced/blob/master/LICENSE)
|
||||
[](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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
#![forbid(rust_2018_idioms)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
pub use glow;
|
||||
|
||||
mod backend;
|
||||
mod program;
|
||||
mod quad;
|
||||
|
|
|
|||
|
|
@ -1,28 +1,122 @@
|
|||
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", 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),
|
||||
};
|
||||
|
||||
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,
|
||||
shader_sources: &[(u32, &str)],
|
||||
shaders: &[Shader],
|
||||
attributes: &[(u32, &str)],
|
||||
) -> <glow::Context as HasContext>::Program {
|
||||
let program = gl.create_program().expect("Cannot create program");
|
||||
|
||||
let mut shaders = Vec::with_capacity(shader_sources.len());
|
||||
for shader in shaders {
|
||||
gl.attach_shader(program, shader.0);
|
||||
}
|
||||
|
||||
for (shader_type, shader_source) in shader_sources.iter() {
|
||||
let shader = gl
|
||||
.create_shader(*shader_type)
|
||||
.expect("Cannot create shader");
|
||||
|
||||
gl.shader_source(shader, 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);
|
||||
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
|
||||
|
|
|
|||
243
glow/src/quad.rs
243
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,
|
||||
);
|
||||
|
||||
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,
|
||||
match self {
|
||||
Pipeline::Core(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,
|
||||
Pipeline::Compatibility(pipeline) => {
|
||||
pipeline.draw(
|
||||
gl,
|
||||
target_height,
|
||||
instances,
|
||||
transformation,
|
||||
scale,
|
||||
bounds,
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
360
glow/src/quad/compatibility.rs
Normal file
360
glow/src/quad/compatibility.rs
Normal file
|
|
@ -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
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
246
glow/src/quad/core.rs
Normal file
246
glow/src/quad/core.rs
Normal file
|
|
@ -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)
|
||||
}
|
||||
18
glow/src/shader/common/triangle.frag
Normal file
18
glow/src/shader/common/triangle.frag
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
67
glow/src/shader/compatibility/quad.frag
Normal file
67
glow/src/shader/compatibility/quad.frag
Normal file
|
|
@ -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);
|
||||
}
|
||||
44
glow/src/shader/compatibility/quad.vert
Normal file
44
glow/src/shader/compatibility/quad.vert
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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),
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#version 330
|
||||
|
||||
in vec4 v_Color;
|
||||
|
||||
out vec4 o_Color;
|
||||
|
||||
void main() {
|
||||
o_Color = v_Color;
|
||||
}
|
||||
|
|
@ -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")],
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue