diff --git a/examples/generate-atlas.rs b/examples/generate-atlas.rs index 76761fa0..2d456dbe 100644 --- a/examples/generate-atlas.rs +++ b/examples/generate-atlas.rs @@ -66,8 +66,8 @@ fn main() { } } - let glyph_buffers = glyph_buffer_builder.finish(&rasterizer.device).unwrap(); - let batch = batch_builder.finish(&rasterizer.device).unwrap(); + let glyph_buffers = glyph_buffer_builder.finish().unwrap(); + let batch = batch_builder.finish(&glyph_buffer_builder).unwrap(); let atlas_size = Size2D::new(WIDTH, HEIGHT); let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap(); diff --git a/resources/shaders/draw.vs.glsl b/resources/shaders/draw.vs.glsl index 6d86a0fd..bfeef462 100644 --- a/resources/shaders/draw.vs.glsl +++ b/resources/shaders/draw.vs.glsl @@ -19,14 +19,14 @@ layout(std140) struct GlyphDescriptor { ivec4 extents; // The number of units per em in this glyph. uint unitsPerEm; - // The number of points in this glyph. - uint pointCount; - // The index of the first point. + // The index of the first point in the VBO. uint startPoint; + // The index of the first element in the IBO. + uint startIndex; }; // Information about the position of each glyph in the atlas. -layout(std140) struct ImageInfo { +layout(std140) struct ImageDescriptor { // The left/top/right/bottom positions of the glyph in the atlas. uvec4 atlasRect; // The font size in pixels. @@ -42,17 +42,17 @@ layout(std140) uniform ubGlyphDescriptors { GlyphDescriptor uGlyphs[MAX_GLYPHS]; }; -layout(std140) uniform ubImageInfo { - ImageInfo uImageInfo[MAX_GLYPHS]; +layout(std140) uniform ubImageDescriptors { + ImageDescriptor uImages[MAX_GLYPHS]; }; // The position of each vertex in glyph space. in ivec2 aPosition; -// Which image the vertex belongs to. +// Which glyph the vertex belongs to. // // TODO(pcwalton): See if this is faster as a binary search on the vertex ID. -in uint aImageIndex; +in uint aGlyphIndex; // The vertex ID, passed along onto the TCS. flat out uint vVertexID; @@ -60,13 +60,13 @@ flat out uint vVertexID; void main() { vVertexID = gl_VertexID; - ImageInfo imageInfo = uImageInfo[aImageIndex]; - GlyphDescriptor glyph = uGlyphs[imageInfo.glyphIndex]; + ImageDescriptor image = uImages[aGlyphIndex]; + GlyphDescriptor glyph = uGlyphs[aGlyphIndex]; float emsPerUnit = 1.0f / float(glyph.unitsPerEm); - vec2 glyphPos = vec2(aPosition.x - glyphInfo.extents.x, glyphInfo.extents.w - aPosition.y); - vec2 atlasPos = glyphPos * emsPerUnit * imageInfo.pointSize + vec2(imageInfo.atlasRect.xy); + vec2 glyphPos = vec2(aPosition.x - glyph.extents.x, glyph.extents.w - aPosition.y); + vec2 atlasPos = glyphPos * emsPerUnit * image.pointSize + vec2(image.atlasRect.xy); gl_Position = vec4(atlasPos, 0.0f, 1.0f); } diff --git a/src/batch.rs b/src/batch.rs index cee9e88a..e447e943 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -9,18 +9,17 @@ // except according to those terms. use atlas::Atlas; -use compute_shader::buffer::{Buffer, BufferData, HostAllocatedData, Protection}; -use compute_shader::device::Device; +use gl::types::{GLsizei, GLsizeiptr, GLuint}; +use gl; use glyph_buffer::GlyphBufferBuilder; +use std::mem; +use std::os::raw::c_void; use std::u16; -const POINTS_PER_SEGMENT: u32 = 32; - pub struct BatchBuilder { pub atlas: Atlas, - pub indices: Vec, pub images: Vec, - pub point_count: u32, + pub glyph_indices: Vec, } impl BatchBuilder { @@ -29,12 +28,12 @@ impl BatchBuilder { pub fn new(available_width: u32, shelf_height: u32) -> BatchBuilder { BatchBuilder { atlas: Atlas::new(available_width, shelf_height), - indices: vec![], images: vec![], - point_count: 0, + glyph_indices: vec![], } } + /// FIXME(pcwalton): Support the same glyph drawn at multiple point sizes. pub fn add_glyph(&mut self, glyph_buffer_builder: &GlyphBufferBuilder, glyph_index: u32, @@ -47,35 +46,83 @@ impl BatchBuilder { let pixel_size = descriptor.pixel_rect(point_size).size.ceil().cast().unwrap(); let atlas_origin = try!(self.atlas.place(&pixel_size)); - self.images.push(ImageDescriptor { + while self.images.len() < glyph_index as usize + 1 { + self.images.push(ImageDescriptor::default()) + } + + self.images[glyph_index as usize] = ImageDescriptor { atlas_x: atlas_origin.x, atlas_y: atlas_origin.y, point_size: point_size, glyph_index: glyph_index, - start_point_in_batch: self.point_count, - point_count: descriptor.point_count as u32, - }); + }; - self.point_count += descriptor.point_count as u32; + self.glyph_indices.push(glyph_index); Ok(()) } - pub fn finish(&mut self, device: &Device) -> Result { - let indices = BufferData::HostAllocated(HostAllocatedData::new(&self.indices)); - let images = BufferData::HostAllocated(HostAllocatedData::new(&self.images)); - Ok(Batch { - indices: try!(device.create_buffer(Protection::ReadOnly, indices).map_err(drop)), - images: try!(device.create_buffer(Protection::ReadOnly, images).map_err(drop)), - point_count: self.point_count, - }) + pub fn finish(&mut self, glyph_buffer_builder: &GlyphBufferBuilder) -> Result { + self.glyph_indices.sort(); + + let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]); + for &glyph_index in &self.glyph_indices { + let first_index = glyph_buffer_builder.descriptors[glyph_index as usize].start_index as + usize; + let last_index = match glyph_buffer_builder.descriptors.get(glyph_index as usize + 1) { + Some(ref descriptor) => descriptor.start_index as usize, + None => glyph_buffer_builder.indices.len(), + }; + + match current_range { + Some((current_first, current_last)) if first_index == current_last => { + current_range = Some((current_first, last_index)) + } + Some((current_first, current_last)) => { + counts.push((current_last - current_first) as GLsizei); + start_indices.push(current_first); + current_range = Some((first_index, last_index)) + } + None => current_range = Some((first_index, last_index)), + } + } + if let Some((current_first, current_last)) = current_range { + counts.push((current_last - current_first) as GLsizei); + start_indices.push(current_first); + } + + // TODO(pcwalton): Try using `glMapBuffer` here. + unsafe { + let mut images = 0; + gl::GenBuffers(1, &mut images); + + gl::BindBuffer(gl::UNIFORM_BUFFER, images); + gl::BufferData(gl::UNIFORM_BUFFER, + (self.images.len() * mem::size_of::()) as GLsizeiptr, + self.images.as_ptr() as *const ImageDescriptor as *const c_void, + gl::DYNAMIC_DRAW); + + Ok(Batch { + start_indices: start_indices, + counts: counts, + images: images, + }) + } } } pub struct Batch { - pub indices: Buffer, - pub images: Buffer, - pub point_count: u32, + pub start_indices: Vec, + pub counts: Vec, + pub images: GLuint, +} + +impl Drop for Batch { + fn drop(&mut self) { + unsafe { + gl::DeleteBuffers(1, &mut self.images); + } + } } #[derive(Clone, Copy, Debug)] @@ -116,13 +163,11 @@ impl Iterator for GlyphRangeIter { } #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Default, Debug)] pub struct ImageDescriptor { atlas_x: u32, atlas_y: u32, point_size: f32, glyph_index: u32, - start_point_in_batch: u32, - point_count: u32, } diff --git a/src/coverage.rs b/src/coverage.rs index 2429e401..f68471e7 100644 --- a/src/coverage.rs +++ b/src/coverage.rs @@ -14,7 +14,6 @@ use compute_shader::texture::{ExternalTexture, Format, Texture}; use euclid::size::Size2D; use gl::types::{GLint, GLuint}; use gl; -use std::mem; pub struct CoverageBuffer { pub texture: Texture, diff --git a/src/glyph_buffer.rs b/src/glyph_buffer.rs index 16e5087f..40793dc8 100644 --- a/src/glyph_buffer.rs +++ b/src/glyph_buffer.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use compute_shader::buffer::{Buffer, BufferData, HostAllocatedData, Protection}; -use compute_shader::device::Device; use euclid::{Point2D, Rect, Size2D}; use gl::types::{GLsizeiptr, GLuint}; use gl; @@ -19,14 +17,15 @@ use otf::loca::LocaTable; use std::mem; use std::os::raw::c_void; +static DUMMY_VERTEX: Vertex = Vertex { + x: 0, + y: 0, + glyph_index: 0, +}; + pub struct GlyphBufferBuilder { - pub coordinates: Vec<(i16, i16)>, - - /// TODO(pcwalton): Try omitting this and binary search the glyph descriptors in the vertex - /// shader. Might or might not help. - pub glyph_indices: Vec, - - pub operations: Vec, + pub vertices: Vec, + pub indices: Vec, pub descriptors: Vec, } @@ -34,9 +33,8 @@ impl GlyphBufferBuilder { #[inline] pub fn new() -> GlyphBufferBuilder { GlyphBufferBuilder { - coordinates: vec![], - glyph_indices: vec![], - operations: vec![], + vertices: vec![DUMMY_VERTEX], + indices: vec![], descriptors: vec![], } } @@ -49,38 +47,31 @@ impl GlyphBufferBuilder { -> Result<(), ()> { let glyph_index = self.descriptors.len() as u16; - let mut point_index = self.coordinates.len() / 2; + let mut point_index = self.vertices.len() as u32; + let start_index = self.indices.len() as u32; let start_point = point_index; - let mut operations = if point_index % 4 == 0 { - 0 - } else { - self.operations.pop().unwrap() - }; + let mut last_point_on_curve = true; try!(glyf_table.for_each_point(loca_table, glyph_id, |point| { - self.coordinates.push((point.position.x, point.position.y)); - self.glyph_indices.push(glyph_index); + self.vertices.push(Vertex { + x: point.position.x, + y: point.position.y, + glyph_index: glyph_index, + }); - let operation = if point.first_point_in_contour { - 0 - } else if point.on_curve { - 1 - } else { - 2 - }; - - operations |= operation << (point_index % 4 * 2); + if !point.first_point_in_contour && point.on_curve { + let indices = if last_point_on_curve { + [point_index - 2, 0, point_index - 1] + } else { + [point_index - 3, point_index - 2, point_index - 1] + }; + self.indices.extend(indices.iter().cloned()); + } point_index += 1; - if point_index % 4 == 0 { - self.operations.push(operation) - } + last_point_on_curve = point.on_curve })); - if point_index % 4 != 0 { - self.operations.push(operations) - } - // Add a glyph descriptor. let bounding_rect = try!(glyf_table.bounding_rect(loca_table, glyph_id)); self.descriptors.push(GlyphDescriptor { @@ -89,26 +80,33 @@ impl GlyphBufferBuilder { right: bounding_rect.max_x() as i32, top: bounding_rect.max_y() as i32, units_per_em: head_table.units_per_em as u32, - point_count: (point_index - start_point) as u32, start_point: start_point as u32, + start_index: start_index, pad: 0, }); Ok(()) } - pub fn finish(&self, device: &Device) -> Result { - // TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contours. + pub fn finish(&self) -> Result { + // TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contour types and + // counts. unsafe { - let (mut coordinates, mut descriptors) = (0, 0); - gl::GenBuffers(1, &mut coordinates); + let (mut vertices, mut indices, mut descriptors) = (0, 0, 0); + gl::GenBuffers(1, &mut vertices); + gl::GenBuffers(1, &mut indices); gl::GenBuffers(1, &mut descriptors); - let length = self.coordinates.len() * mem::size_of::<(i16, i16)>(); - gl::BindBuffer(gl::ARRAY_BUFFER, coordinates); + gl::BindBuffer(gl::ARRAY_BUFFER, vertices); gl::BufferData(gl::ARRAY_BUFFER, - length as GLsizeiptr, - self.coordinates.as_ptr() as *const (i16, i16) as *const c_void, + (self.vertices.len() * mem::size_of::()) as GLsizeiptr, + self.vertices.as_ptr() as *const Vertex as *const c_void, + gl::STATIC_DRAW); + + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indices); + gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, + (self.indices.len() * mem::size_of::()) as GLsizeiptr, + self.indices.as_ptr() as *const u32 as *const c_void, gl::STATIC_DRAW); let length = self.descriptors.len() * mem::size_of::(); @@ -119,7 +117,8 @@ impl GlyphBufferBuilder { gl::STATIC_DRAW); Ok(GlyphBuffers { - coordinates: coordinates, + vertices: vertices, + indices: indices, descriptors: descriptors, }) } @@ -127,7 +126,8 @@ impl GlyphBufferBuilder { } pub struct GlyphBuffers { - pub coordinates: GLuint, + pub vertices: GLuint, + pub indices: GLuint, pub descriptors: GLuint, } @@ -139,8 +139,8 @@ pub struct GlyphDescriptor { pub right: i32, pub top: i32, pub units_per_em: u32, - pub point_count: u32, pub start_point: u32, + pub start_index: u32, pub pad: u32, } @@ -150,7 +150,17 @@ impl GlyphDescriptor { let pixels_per_unit = point_size / self.units_per_em as f32; Rect::new(Point2D::new(self.left as f32, self.bottom as f32), Size2D::new((self.right - self.left) as f32, - (self.bottom - self.top) as f32)) * pixels_per_unit + (self.top - self.bottom) as f32)) * pixels_per_unit } } +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Vertex { + x: i16, + y: i16, + /// TODO(pcwalton): Try omitting this and binary search the glyph descriptors in the vertex + /// shader. Might or might not help. + glyph_index: u16, +} + diff --git a/src/rasterizer.rs b/src/rasterizer.rs index 1b8e42d9..52625732 100644 --- a/src/rasterizer.rs +++ b/src/rasterizer.rs @@ -16,9 +16,10 @@ use compute_shader::queue::{Queue, Uniform}; use compute_shader::texture::Texture; use coverage::CoverageBuffer; use euclid::rect::Rect; -use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint}; +use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint, GLvoid}; use gl; -use glyph_buffer::GlyphBuffers; +use glyph_buffer::{GlyphBuffers, Vertex}; +use std::mem; use std::ptr; // TODO(pcwalton): Don't force that these be compiled in. @@ -36,11 +37,19 @@ pub struct Rasterizer { pub queue: Queue, draw_program: GLuint, accum_program: Program, + draw_vertex_array: GLuint, + draw_position_attribute: GLint, + draw_image_index_attribute: GLint, + draw_atlas_size_uniform: GLint, + draw_glyph_descriptors_uniform: GLuint, + draw_image_info_uniform: GLuint, } impl Rasterizer { pub fn new(device: Device, queue: Queue) -> Result { - let draw_program; + let (draw_program, draw_position_attribute, draw_image_index_attribute); + let (draw_atlas_size_uniform, draw_glyph_descriptors_uniform, draw_image_info_uniform); + let mut draw_vertex_array = 0; unsafe { let shaders = [ try!(compile_gl_shader(gl::VERTEX_SHADER, @@ -68,7 +77,22 @@ impl Rasterizer { gl::LINK_STATUS, "Program", gl::GetProgramiv, - gl::GetProgramInfoLog)) + gl::GetProgramInfoLog)); + + gl::GenVertexArrays(1, &mut draw_vertex_array); + + draw_position_attribute = + gl::GetAttribLocation(draw_program, b"aPosition\0".as_ptr() as *const GLchar); + draw_image_index_attribute = + gl::GetAttribLocation(draw_program, b"aImageIndex\0".as_ptr() as *const GLchar); + + draw_atlas_size_uniform = + gl::GetUniformLocation(draw_program, b"uAtlasSize\0".as_ptr() as *const GLchar); + draw_glyph_descriptors_uniform = + gl::GetUniformBlockIndex(draw_program, + b"ubGlyphDescriptors\0".as_ptr() as *const GLchar); + draw_image_info_uniform = + gl::GetUniformBlockIndex(draw_program, b"ubImageInfo\0".as_ptr() as *const GLchar); } // FIXME(pcwalton): Don't panic if this fails to compile; just return an error. @@ -79,6 +103,12 @@ impl Rasterizer { queue: queue, draw_program: draw_program, accum_program: accum_program, + draw_vertex_array: draw_vertex_array, + draw_position_attribute: draw_position_attribute, + draw_image_index_attribute: draw_image_index_attribute, + draw_atlas_size_uniform: draw_atlas_size_uniform, + draw_glyph_descriptors_uniform: draw_glyph_descriptors_uniform, + draw_image_info_uniform: draw_image_info_uniform, }) } @@ -90,7 +120,57 @@ impl Rasterizer { coverage_buffer: &CoverageBuffer, texture: &Texture) -> Result { - // TODO(pcwalton) + unsafe { + gl::UseProgram(self.draw_program); + gl::BindVertexArray(self.draw_vertex_array); + + // Set up the buffer layout. + gl::BindBuffer(gl::ARRAY_BUFFER, glyph_buffers.vertices); + gl::VertexAttribIPointer(self.draw_position_attribute as GLuint, + 2, + gl::SHORT, + mem::size_of::() as GLint, + 0 as *const GLvoid); + gl::VertexAttribIPointer(self.draw_image_index_attribute as GLuint, + 1, + gl::UNSIGNED_SHORT, + mem::size_of::() as GLint, + mem::size_of::<(i16, i16)>() as *const GLvoid); + gl::EnableVertexAttribArray(self.draw_position_attribute as GLuint); + gl::EnableVertexAttribArray(self.draw_image_index_attribute as GLuint); + + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, glyph_buffers.indices); + + gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, glyph_buffers.descriptors); + gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, batch.images); + gl::UniformBlockBinding(self.draw_program, self.draw_glyph_descriptors_uniform, 1); + gl::UniformBlockBinding(self.draw_program, self.draw_image_info_uniform, 2); + + gl::Uniform2ui(self.draw_atlas_size_uniform, + atlas_rect.size.width, + atlas_rect.size.height); + + gl::PatchParameteri(gl::PATCH_VERTICES, 3); + + // Use blending on our floating point framebuffer to accumulate coverage. + gl::Enable(gl::BLEND); + gl::BlendEquation(gl::FUNC_ADD); + gl::BlendFunc(gl::ONE, gl::ONE); + + // Enable backface culling. See comments in `draw.tcs.glsl` for more information + // regarding why this is necessary. + gl::CullFace(gl::BACK); + gl::FrontFace(gl::CCW); + gl::Enable(gl::CULL_FACE); + + // Now draw the glyph ranges. + debug_assert!(batch.counts.len() == batch.start_indices.len()); + gl::MultiDrawElements(gl::PATCHES, + batch.counts.as_ptr(), + gl::UNSIGNED_INT, + batch.start_indices.as_ptr() as *const *const GLvoid, + batch.counts.len() as GLsizei); + } let atlas_rect_uniform = [ atlas_rect.origin.x,