Draft triangle pipeline in iced_glow

This commit is contained in:
Héctor Ramón Jiménez 2020-05-21 19:07:33 +02:00
parent d54f17c6aa
commit 60dcfc354e
6 changed files with 301 additions and 52 deletions

View File

@ -5,6 +5,7 @@
#![forbid(rust_2018_idioms)]
mod backend;
mod program;
mod quad;
mod text;
mod triangle;

39
glow/src/program.rs Normal file
View File

@ -0,0 +1,39 @@
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");
let mut shaders = Vec::with_capacity(shader_sources.len());
for (shader_type, shader_source) in shader_sources.iter() {
let shader = gl
.create_shader(*shader_type)
.expect("Cannot create shader");
gl.shader_source(shader, shader_source);
gl.compile_shader(shader);
if !gl.get_shader_compile_status(shader) {
panic!(gl.get_shader_info_log(shader));
}
gl.attach_shader(program, shader);
shaders.push(shader);
}
gl.link_program(program);
if !gl.get_program_link_status(program) {
panic!(gl.get_program_info_log(program));
}
for shader in shaders {
gl.detach_shader(program, shader);
gl.delete_shader(shader);
}
program
}

View File

@ -1,3 +1,4 @@
use crate::program;
use crate::Transformation;
use glow::HasContext;
use iced_graphics::layer;
@ -18,7 +19,7 @@ pub struct Pipeline {
impl Pipeline {
pub fn new(gl: &glow::Context) -> Pipeline {
let program = unsafe {
create_program(
program::create(
gl,
&[
(glow::VERTEX_SHADER, include_str!("shader/quad.vert")),
@ -138,44 +139,6 @@ impl Pipeline {
}
}
unsafe fn create_program(
gl: &glow::Context,
shader_sources: &[(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_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);
}
gl.link_program(program);
if !gl.get_program_link_status(program) {
panic!(gl.get_program_info_log(program));
}
for shader in shaders {
gl.detach_shader(program, shader);
gl.delete_shader(shader);
}
program
}
unsafe fn create_instance_buffer(
gl: &glow::Context,
size: usize,

View File

@ -0,0 +1,8 @@
#version 450
layout(location = 0) in vec4 i_Color;
layout(location = 0) out vec4 o_Color;
void main() {
o_Color = i_Color;
}

View File

@ -0,0 +1,13 @@
#version 450
layout(location = 0) uniform mat4 u_Transform;
layout(location = 0) in vec2 i_Position;
layout(location = 1) in vec4 i_Color;
layout(location = 0) out vec4 o_Color;
void main() {
gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
o_Color = i_Color;
}

View File

@ -1,22 +1,106 @@
//! Draw meshes of triangles.
use crate::{settings, Transformation};
use crate::program;
use crate::settings;
use crate::Transformation;
use glow::HasContext;
use iced_graphics::layer;
use std::marker::PhantomData;
pub use iced_graphics::triangle::Mesh2D;
pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
const UNIFORM_BUFFER_SIZE: usize = 100;
const VERTEX_BUFFER_SIZE: usize = 10_000;
const INDEX_BUFFER_SIZE: usize = 10_000;
#[derive(Debug)]
pub(crate) struct Pipeline {}
pub(crate) struct Pipeline {
program: <glow::Context as HasContext>::Program,
vertex_array: <glow::Context as HasContext>::VertexArray,
vertices: Buffer<Vertex2D>,
indices: Buffer<u32>,
current_transform: Transformation,
}
impl Pipeline {
pub fn new(
gl: &glow::Context,
antialiasing: Option<settings::Antialiasing>,
) -> Pipeline {
Pipeline {}
let program = unsafe {
program::create(
gl,
&[
(glow::VERTEX_SHADER, include_str!("shader/triangle.vert")),
(
glow::FRAGMENT_SHADER,
include_str!("shader/triangle.frag"),
),
],
)
};
unsafe {
gl.use_program(Some(program));
gl.uniform_matrix_4_f32_slice(
Some(0),
false,
&Transformation::identity().into(),
);
gl.use_program(None);
}
let vertex_array =
unsafe { gl.create_vertex_array().expect("Create vertex array") };
unsafe {
gl.bind_vertex_array(Some(vertex_array));
}
let vertices = unsafe {
Buffer::new(
gl,
glow::ARRAY_BUFFER,
glow::DYNAMIC_DRAW,
VERTEX_BUFFER_SIZE,
)
};
let indices = unsafe {
Buffer::new(
gl,
glow::ELEMENT_ARRAY_BUFFER,
glow::DYNAMIC_DRAW,
INDEX_BUFFER_SIZE,
)
};
unsafe {
let stride = std::mem::size_of::<Vertex2D>() as i32;
gl.enable_vertex_attrib_array(0);
gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
gl.enable_vertex_attrib_array(1);
gl.vertex_attrib_pointer_f32(
1,
4,
glow::FLOAT,
false,
stride,
4 * 2,
);
gl.bind_vertex_array(None);
}
Pipeline {
program,
vertex_array,
vertices,
indices,
current_transform: Transformation::identity(),
}
}
pub fn draw(
@ -28,6 +112,106 @@ impl Pipeline {
scale_factor: f32,
meshes: &[layer::Mesh<'_>],
) {
unsafe {
gl.enable(glow::SCISSOR_TEST);
gl.use_program(Some(self.program));
gl.bind_vertex_array(Some(self.vertex_array));
}
// This looks a bit crazy, but we are just counting how many vertices
// and indices we will need to handle.
// TODO: Improve readability
let (total_vertices, total_indices) = meshes
.iter()
.map(|layer::Mesh { buffers, .. }| {
(buffers.vertices.len(), buffers.indices.len())
})
.fold((0, 0), |(total_v, total_i), (v, i)| {
(total_v + v, total_i + i)
});
// Then we ensure the current buffers are big enough, resizing if
// necessary
unsafe {
self.vertices.bind(gl, total_vertices);
self.indices.bind(gl, total_indices);
}
// We upload all the vertices and indices upfront
let mut last_vertex = 0;
let mut last_index = 0;
for layer::Mesh { buffers, .. } in meshes {
unsafe {
gl.buffer_sub_data_u8_slice(
glow::ARRAY_BUFFER,
(last_vertex * std::mem::size_of::<Vertex2D>()) as i32,
bytemuck::cast_slice(&buffers.vertices),
);
gl.buffer_sub_data_u8_slice(
glow::ELEMENT_ARRAY_BUFFER,
(last_index * std::mem::size_of::<u32>()) as i32,
bytemuck::cast_slice(&buffers.indices),
);
last_vertex += buffers.vertices.len();
last_index += buffers.indices.len();
}
}
// Then we draw each mesh using offsets
let mut last_vertex = 0;
let mut last_index = 0;
for layer::Mesh {
buffers,
origin,
clip_bounds,
} in meshes
{
let transform =
transformation * Transformation::translate(origin.x, origin.y);
let clip_bounds = (*clip_bounds * scale_factor).round();
unsafe {
if self.current_transform != transform {
gl.uniform_matrix_4_f32_slice(
Some(0),
false,
&transform.into(),
);
self.current_transform = transform;
}
gl.scissor(
clip_bounds.x as i32,
(target_height - (clip_bounds.y + clip_bounds.height))
as i32,
clip_bounds.width as i32,
clip_bounds.height as i32,
);
gl.draw_elements_base_vertex(
glow::TRIANGLES,
buffers.indices.len() as i32,
glow::UNSIGNED_INT,
(last_index * std::mem::size_of::<u32>()) as i32,
last_vertex as i32,
);
last_vertex += buffers.vertices.len();
last_index += buffers.indices.len();
}
}
unsafe {
gl.bind_vertex_array(None);
gl.use_program(None);
gl.disable(glow::SCISSOR_TEST);
}
}
}
@ -35,18 +219,15 @@ impl Pipeline {
#[derive(Debug, Clone, Copy)]
struct Uniforms {
transform: [f32; 16],
// We need to align this to 256 bytes to please `wgpu`...
// TODO: Be smarter and stop wasting memory!
_padding_a: [f32; 32],
_padding_b: [f32; 16],
}
unsafe impl bytemuck::Zeroable for Uniforms {}
unsafe impl bytemuck::Pod for Uniforms {}
impl Default for Uniforms {
fn default() -> Self {
Self {
transform: *Transformation::identity().as_ref(),
_padding_a: [0.0; 32],
_padding_b: [0.0; 16],
}
}
}
@ -55,8 +236,52 @@ impl From<Transformation> for Uniforms {
fn from(transformation: Transformation) -> Uniforms {
Self {
transform: transformation.into(),
_padding_a: [0.0; 32],
_padding_b: [0.0; 16],
}
}
}
#[derive(Debug)]
struct Buffer<T> {
raw: <glow::Context as HasContext>::Buffer,
target: u32,
usage: u32,
size: usize,
phantom: PhantomData<T>,
}
impl<T> Buffer<T> {
pub unsafe fn new(
gl: &glow::Context,
target: u32,
usage: u32,
size: usize,
) -> Self {
let raw = gl.create_buffer().expect("Create buffer");
let mut buffer = Buffer {
raw,
target,
usage,
size: 0,
phantom: PhantomData,
};
buffer.bind(gl, size);
buffer
}
pub unsafe fn bind(&mut self, gl: &glow::Context, size: usize) {
gl.bind_buffer(self.target, Some(self.raw));
if self.size < size {
gl.buffer_data_size(
self.target,
(size * std::mem::size_of::<T>()) as i32,
self.usage,
);
self.size = size;
}
}
}