Split quad::Pipeline into core and compatibility
This commit is contained in:
parent
3c5ab30117
commit
381052c50e
13 changed files with 755 additions and 220 deletions
234
glow/src/quad.rs
234
glow/src/quad.rs
|
|
@ -1,77 +1,24 @@
|
||||||
use crate::program;
|
mod compatibility;
|
||||||
|
mod core;
|
||||||
|
|
||||||
use crate::Transformation;
|
use crate::Transformation;
|
||||||
use glow::HasContext;
|
use glow::HasContext;
|
||||||
use iced_graphics::layer;
|
use iced_graphics::layer;
|
||||||
use iced_native::Rectangle;
|
use iced_native::Rectangle;
|
||||||
|
|
||||||
const MAX_INSTANCES: usize = 100_000;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pipeline {
|
pub enum Pipeline {
|
||||||
program: <glow::Context as HasContext>::Program,
|
Core(core::Pipeline),
|
||||||
vertex_array: <glow::Context as HasContext>::VertexArray,
|
Compatibility(compatibility::Pipeline),
|
||||||
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 {
|
impl Pipeline {
|
||||||
pub fn new(gl: &glow::Context) -> Pipeline {
|
pub fn new(gl: &glow::Context) -> Pipeline {
|
||||||
let program = unsafe {
|
let version = gl.version();
|
||||||
program::create(
|
if version.is_embedded || version.major == 2 {
|
||||||
gl,
|
Pipeline::Compatibility(compatibility::Pipeline::new(gl))
|
||||||
&[
|
} else {
|
||||||
(glow::VERTEX_SHADER, include_str!("shader/quad.vert")),
|
Pipeline::Core(core::Pipeline::new(gl))
|
||||||
(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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,152 +31,27 @@ impl Pipeline {
|
||||||
scale: f32,
|
scale: f32,
|
||||||
bounds: Rectangle<u32>,
|
bounds: Rectangle<u32>,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
match self {
|
||||||
gl.enable(glow::SCISSOR_TEST);
|
Pipeline::Core(pipeline) => {
|
||||||
gl.scissor(
|
pipeline.draw(
|
||||||
bounds.x as i32,
|
gl,
|
||||||
(target_height - (bounds.y + bounds.height)) as i32,
|
target_height,
|
||||||
bounds.width as i32,
|
instances,
|
||||||
bounds.height as i32,
|
transformation,
|
||||||
);
|
scale,
|
||||||
|
bounds,
|
||||||
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,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Pipeline::Compatibility(pipeline) => {
|
||||||
self.current_target_height = target_height;
|
pipeline.draw(
|
||||||
}
|
gl,
|
||||||
|
target_height,
|
||||||
let mut i = 0;
|
instances,
|
||||||
let total = instances.len();
|
transformation,
|
||||||
|
scale,
|
||||||
while i < total {
|
bounds,
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
347
glow/src/quad/compatibility.rs
Normal file
347
glow/src/quad/compatibility.rs
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
use crate::program;
|
||||||
|
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) -> Pipeline {
|
||||||
|
let program = unsafe {
|
||||||
|
program::create(
|
||||||
|
gl,
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
include_str!("../shader/compatibility/quad.vert"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
glow::FRAGMENT_SHADER,
|
||||||
|
include_str!("../shader/compatibility/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, 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
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
233
glow/src/quad/core.rs
Normal file
233
glow/src/quad/core.rs
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pipeline {
|
||||||
|
pub fn new(gl: &glow::Context) -> Pipeline {
|
||||||
|
let program = unsafe {
|
||||||
|
program::create(
|
||||||
|
gl,
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
include_str!("../shader/core/quad.vert"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
glow::FRAGMENT_SHADER,
|
||||||
|
include_str!("../shader/core/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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
62
glow/src/shader/compatibility/quad.frag
Normal file
62
glow/src/shader/compatibility/quad.frag
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
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(in vec2 frag_coord, in vec2 position, in 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);
|
||||||
|
}
|
||||||
46
glow/src/shader/compatibility/quad.vert
Normal file
46
glow/src/shader/compatibility/quad.vert
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#version 100
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
8
glow/src/shader/compatibility/triangle.frag
Normal file
8
glow/src/shader/compatibility/triangle.frag
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec4 v_Color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = v_Color;
|
||||||
|
}
|
||||||
13
glow/src/shader/compatibility/triangle.vert
Normal file
13
glow/src/shader/compatibility/triangle.vert
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#version 100
|
||||||
|
|
||||||
|
uniform mat4 u_Transform;
|
||||||
|
|
||||||
|
attribute vec2 i_Position;
|
||||||
|
attribute vec4 i_Color;
|
||||||
|
|
||||||
|
varying vec4 v_Color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
v_Color = i_Color;
|
||||||
|
gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#version 330
|
#version 130
|
||||||
|
|
||||||
uniform float u_ScreenHeight;
|
uniform float u_ScreenHeight;
|
||||||
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
#version 330
|
#version 130
|
||||||
|
|
||||||
uniform mat4 u_Transform;
|
uniform mat4 u_Transform;
|
||||||
uniform float u_Scale;
|
uniform float u_Scale;
|
||||||
|
|
||||||
layout(location = 0) in vec2 i_Pos;
|
in vec2 i_Pos;
|
||||||
layout(location = 1) in vec2 i_Scale;
|
in vec2 i_Scale;
|
||||||
layout(location = 2) in vec4 i_Color;
|
in vec4 i_Color;
|
||||||
layout(location = 3) in vec4 i_BorderColor;
|
in vec4 i_BorderColor;
|
||||||
layout(location = 4) in float i_BorderRadius;
|
in float i_BorderRadius;
|
||||||
layout(location = 5) in float i_BorderWidth;
|
in float i_BorderWidth;
|
||||||
|
|
||||||
out vec4 v_Color;
|
out vec4 v_Color;
|
||||||
out vec4 v_BorderColor;
|
out vec4 v_BorderColor;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#version 330
|
#version 130
|
||||||
|
|
||||||
in vec4 v_Color;
|
in vec4 v_Color;
|
||||||
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#version 330
|
#version 130
|
||||||
|
|
||||||
uniform mat4 u_Transform;
|
uniform mat4 u_Transform;
|
||||||
|
|
||||||
layout(location = 0) in vec2 i_Position;
|
in vec2 i_Position;
|
||||||
layout(location = 1) in vec4 i_Color;
|
in vec4 i_Color;
|
||||||
|
|
||||||
out vec4 v_Color;
|
out vec4 v_Color;
|
||||||
|
|
||||||
|
|
@ -26,10 +26,13 @@ impl Pipeline {
|
||||||
program::create(
|
program::create(
|
||||||
gl,
|
gl,
|
||||||
&[
|
&[
|
||||||
(glow::VERTEX_SHADER, include_str!("shader/triangle.vert")),
|
(
|
||||||
|
glow::VERTEX_SHADER,
|
||||||
|
include_str!("shader/compatibility/triangle.vert"),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
glow::FRAGMENT_SHADER,
|
glow::FRAGMENT_SHADER,
|
||||||
include_str!("shader/triangle.frag"),
|
include_str!("shader/compatibility/triangle.frag"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ where
|
||||||
|
|
||||||
let context = ContextBuilder::new()
|
let context = ContextBuilder::new()
|
||||||
.with_vsync(true)
|
.with_vsync(true)
|
||||||
|
// .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0)))
|
||||||
.with_multisampling(C::sample_count(&compositor_settings) as u16)
|
.with_multisampling(C::sample_count(&compositor_settings) as u16)
|
||||||
.build_windowed(builder, &event_loop)
|
.build_windowed(builder, &event_loop)
|
||||||
.map_err(|error| {
|
.map_err(|error| {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue