From 1cd3a1e6bde369b1e82dd9eb39cbdff66e5cc6bd Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 23 Jan 2017 22:03:40 -0800 Subject: [PATCH] Migrate the glyph buffers to be OpenGL buffers --- resources/shaders/draw.vs.glsl | 35 +++++++++++--- src/batch.rs | 6 +-- src/coverage.rs | 49 +++++++++++++++++++- src/glyph_buffer.rs | 85 +++++++++++++++++++++++----------- 4 files changed, 135 insertions(+), 40 deletions(-) diff --git a/resources/shaders/draw.vs.glsl b/resources/shaders/draw.vs.glsl index ec551807..6d86a0fd 100644 --- a/resources/shaders/draw.vs.glsl +++ b/resources/shaders/draw.vs.glsl @@ -10,24 +10,40 @@ #version 410 +// FIXME(pcwalton): This should be higher. Dynamically query its maximum possible size, perhaps? +#define MAX_GLYPHS 256 + +// Information about the metrics of each glyph. +layout(std140) struct GlyphDescriptor { + // The left/top/right/bottom offsets of the glyph from point (0, 0) in glyph space. + 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. + uint startPoint; +}; + // Information about the position of each glyph in the atlas. layout(std140) struct ImageInfo { // The left/top/right/bottom positions of the glyph in the atlas. uvec4 atlasRect; - // The left/top/right/bottom offsets of the glyph from point (0, 0) in glyph space. - ivec4 extents; // The font size in pixels. float pointSize; + // The index of the glyph. + uint glyphIndex; }; // The size of the atlas in pixels. uniform uvec2 uAtlasSize; -// The number of ems per unit (reciprocal of units per em). -uniform float uEmsPerUnit; +layout(std140) uniform ubGlyphDescriptors { + GlyphDescriptor uGlyphs[MAX_GLYPHS]; +}; layout(std140) uniform ubImageInfo { - ImageInfo uImageInfo[256]; + ImageInfo uImageInfo[MAX_GLYPHS]; }; // The position of each vertex in glyph space. @@ -45,8 +61,13 @@ void main() { vVertexID = gl_VertexID; ImageInfo imageInfo = uImageInfo[aImageIndex]; - vec2 glyphPos = vec2(aPosition.x - imageInfo.extents.x, imageInfo.extents.w - aPosition.y); - vec2 atlasPos = glyphPos * uEmsPerUnit * imageInfo.pointSize + vec2(imageInfo.atlasRect.xy); + GlyphDescriptor glyph = uGlyphs[imageInfo.glyphIndex]; + + 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); + gl_Position = vec4(atlasPos, 0.0f, 1.0f); } diff --git a/src/batch.rs b/src/batch.rs index 4b063d40..cee9e88a 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -18,7 +18,7 @@ const POINTS_PER_SEGMENT: u32 = 32; pub struct BatchBuilder { pub atlas: Atlas, - pub indices: Vec, + pub indices: Vec, pub images: Vec, pub point_count: u32, } @@ -47,10 +47,6 @@ impl BatchBuilder { let pixel_size = descriptor.pixel_rect(point_size).size.ceil().cast().unwrap(); let atlas_origin = try!(self.atlas.place(&pixel_size)); - if self.point_count % POINTS_PER_SEGMENT == 0 { - self.indices.push(self.images.len() as u16) - } - self.images.push(ImageDescriptor { atlas_x: atlas_origin.x, atlas_y: atlas_origin.y, diff --git a/src/coverage.rs b/src/coverage.rs index 9be4c571..2429e401 100644 --- a/src/coverage.rs +++ b/src/coverage.rs @@ -10,21 +10,68 @@ use compute_shader::buffer::Protection; use compute_shader::device::Device; -use compute_shader::texture::{Format, Texture}; +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, + pub framebuffer: GLuint, } impl CoverageBuffer { pub fn new(device: &Device, size: &Size2D) -> Result { let texture = try!(device.create_texture(Format::R32F, Protection::ReadWrite, size) .map_err(drop)); + + let mut framebuffer = 0; + unsafe { + let mut gl_texture = 0; + gl::GenTextures(1, &mut gl_texture); + try!(texture.bind_to(&ExternalTexture::Gl(gl_texture)).map_err(drop)); + + gl::BindTexture(gl::TEXTURE_RECTANGLE, gl_texture); + gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); + gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); + gl::TexParameteri(gl::TEXTURE_RECTANGLE, + gl::TEXTURE_WRAP_S, + gl::CLAMP_TO_EDGE as GLint); + gl::TexParameteri(gl::TEXTURE_RECTANGLE, + gl::TEXTURE_WRAP_T, + gl::CLAMP_TO_EDGE as GLint); + + gl::GenFramebuffers(1, &mut framebuffer); + gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer); + gl::FramebufferTexture2D(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_RECTANGLE, + gl_texture, + 0); + } + Ok(CoverageBuffer { texture: texture, + framebuffer: framebuffer, }) } } +impl Drop for CoverageBuffer { + fn drop(&mut self) { + unsafe { + let mut gl_texture = 0; + gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer); + gl::GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + &mut gl_texture as *mut GLuint as *mut GLint); + gl::DeleteTextures(1, &mut gl_texture); + + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + gl::DeleteFramebuffers(1, &mut self.framebuffer); + } + } +} + diff --git a/src/glyph_buffer.rs b/src/glyph_buffer.rs index adeb5f17..16e5087f 100644 --- a/src/glyph_buffer.rs +++ b/src/glyph_buffer.rs @@ -11,12 +11,21 @@ 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; use otf::glyf::GlyfTable; use otf::head::HeadTable; use otf::loca::LocaTable; +use std::mem; +use std::os::raw::c_void; 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 descriptors: Vec, } @@ -26,6 +35,7 @@ impl GlyphBufferBuilder { pub fn new() -> GlyphBufferBuilder { GlyphBufferBuilder { coordinates: vec![], + glyph_indices: vec![], operations: vec![], descriptors: vec![], } @@ -37,6 +47,8 @@ impl GlyphBufferBuilder { loca_table: &LocaTable, glyf_table: &GlyfTable) -> Result<(), ()> { + let glyph_index = self.descriptors.len() as u16; + let mut point_index = self.coordinates.len() / 2; let start_point = point_index; let mut operations = if point_index % 4 == 0 { @@ -47,6 +59,7 @@ impl GlyphBufferBuilder { 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); let operation = if point.first_point_in_contour { 0 @@ -68,50 +81,67 @@ impl GlyphBufferBuilder { self.operations.push(operations) } - // TODO(pcwalton): Add a glyph descriptor. + // Add a glyph descriptor. let bounding_rect = try!(glyf_table.bounding_rect(loca_table, glyph_id)); self.descriptors.push(GlyphDescriptor { - left: bounding_rect.origin.x, - bottom: bounding_rect.origin.y, - width: bounding_rect.size.width, - height: bounding_rect.size.height, - units_per_em: head_table.units_per_em, - point_count: (point_index - start_point) as u16, + left: bounding_rect.origin.x as i32, + bottom: bounding_rect.origin.y as i32, + 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, + pad: 0, }); Ok(()) } pub fn finish(&self, device: &Device) -> Result { - let coordinates = BufferData::HostAllocated(HostAllocatedData::new(&self.coordinates)); - let operations = BufferData::HostAllocated(HostAllocatedData::new(&self.operations)); - let descriptors = BufferData::HostAllocated(HostAllocatedData::new(&self.descriptors)); - Ok(GlyphBuffers { - coordinates: try!(device.create_buffer(Protection::ReadOnly, coordinates) - .map_err(drop)), - operations: try!(device.create_buffer(Protection::ReadOnly, operations).map_err(drop)), - descriptors: try!(device.create_buffer(Protection::ReadOnly, descriptors) - .map_err(drop)), - }) + // TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contours. + unsafe { + let (mut coordinates, mut descriptors) = (0, 0); + gl::GenBuffers(1, &mut coordinates); + gl::GenBuffers(1, &mut descriptors); + + let length = self.coordinates.len() * mem::size_of::<(i16, i16)>(); + gl::BindBuffer(gl::ARRAY_BUFFER, coordinates); + gl::BufferData(gl::ARRAY_BUFFER, + length as GLsizeiptr, + self.coordinates.as_ptr() as *const (i16, i16) as *const c_void, + gl::STATIC_DRAW); + + let length = self.descriptors.len() * mem::size_of::(); + gl::BindBuffer(gl::UNIFORM_BUFFER, descriptors); + gl::BufferData(gl::UNIFORM_BUFFER, + length as GLsizeiptr, + self.descriptors.as_ptr() as *const GlyphDescriptor as *const c_void, + gl::STATIC_DRAW); + + Ok(GlyphBuffers { + coordinates: coordinates, + descriptors: descriptors, + }) + } } } pub struct GlyphBuffers { - pub coordinates: Buffer, - pub operations: Buffer, - pub descriptors: Buffer, + pub coordinates: GLuint, + pub descriptors: GLuint, } #[repr(C)] +#[derive(Clone, Copy)] pub struct GlyphDescriptor { - pub left: i16, - pub bottom: i16, - pub width: i16, - pub height: i16, - pub units_per_em: u16, - pub point_count: u16, + pub left: i32, + pub bottom: i32, + pub right: i32, + pub top: i32, + pub units_per_em: u32, + pub point_count: u32, pub start_point: u32, + pub pad: u32, } impl GlyphDescriptor { @@ -119,7 +149,8 @@ impl GlyphDescriptor { pub fn pixel_rect(&self, point_size: f32) -> Rect { 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.width as f32, self.height as f32)) * pixels_per_unit + Size2D::new((self.right - self.left) as f32, + (self.bottom - self.top) as f32)) * pixels_per_unit } }