Migrate the glyph buffers to be OpenGL buffers
This commit is contained in:
parent
0569831969
commit
1cd3a1e6bd
|
@ -10,24 +10,40 @@
|
||||||
|
|
||||||
#version 410
|
#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.
|
// Information about the position of each glyph in the atlas.
|
||||||
layout(std140) struct ImageInfo {
|
layout(std140) struct ImageInfo {
|
||||||
// The left/top/right/bottom positions of the glyph in the atlas.
|
// The left/top/right/bottom positions of the glyph in the atlas.
|
||||||
uvec4 atlasRect;
|
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.
|
// The font size in pixels.
|
||||||
float pointSize;
|
float pointSize;
|
||||||
|
// The index of the glyph.
|
||||||
|
uint glyphIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The size of the atlas in pixels.
|
// The size of the atlas in pixels.
|
||||||
uniform uvec2 uAtlasSize;
|
uniform uvec2 uAtlasSize;
|
||||||
|
|
||||||
// The number of ems per unit (reciprocal of units per em).
|
layout(std140) uniform ubGlyphDescriptors {
|
||||||
uniform float uEmsPerUnit;
|
GlyphDescriptor uGlyphs[MAX_GLYPHS];
|
||||||
|
};
|
||||||
|
|
||||||
layout(std140) uniform ubImageInfo {
|
layout(std140) uniform ubImageInfo {
|
||||||
ImageInfo uImageInfo[256];
|
ImageInfo uImageInfo[MAX_GLYPHS];
|
||||||
};
|
};
|
||||||
|
|
||||||
// The position of each vertex in glyph space.
|
// The position of each vertex in glyph space.
|
||||||
|
@ -45,8 +61,13 @@ void main() {
|
||||||
vVertexID = gl_VertexID;
|
vVertexID = gl_VertexID;
|
||||||
|
|
||||||
ImageInfo imageInfo = uImageInfo[aImageIndex];
|
ImageInfo imageInfo = uImageInfo[aImageIndex];
|
||||||
vec2 glyphPos = vec2(aPosition.x - imageInfo.extents.x, imageInfo.extents.w - aPosition.y);
|
GlyphDescriptor glyph = uGlyphs[imageInfo.glyphIndex];
|
||||||
vec2 atlasPos = glyphPos * uEmsPerUnit * imageInfo.pointSize + vec2(imageInfo.atlasRect.xy);
|
|
||||||
|
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);
|
gl_Position = vec4(atlasPos, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ const POINTS_PER_SEGMENT: u32 = 32;
|
||||||
|
|
||||||
pub struct BatchBuilder {
|
pub struct BatchBuilder {
|
||||||
pub atlas: Atlas,
|
pub atlas: Atlas,
|
||||||
pub indices: Vec<u16>,
|
pub indices: Vec<u32>,
|
||||||
pub images: Vec<ImageDescriptor>,
|
pub images: Vec<ImageDescriptor>,
|
||||||
pub point_count: u32,
|
pub point_count: u32,
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,6 @@ impl BatchBuilder {
|
||||||
let pixel_size = descriptor.pixel_rect(point_size).size.ceil().cast().unwrap();
|
let pixel_size = descriptor.pixel_rect(point_size).size.ceil().cast().unwrap();
|
||||||
let atlas_origin = try!(self.atlas.place(&pixel_size));
|
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 {
|
self.images.push(ImageDescriptor {
|
||||||
atlas_x: atlas_origin.x,
|
atlas_x: atlas_origin.x,
|
||||||
atlas_y: atlas_origin.y,
|
atlas_y: atlas_origin.y,
|
||||||
|
|
|
@ -10,21 +10,68 @@
|
||||||
|
|
||||||
use compute_shader::buffer::Protection;
|
use compute_shader::buffer::Protection;
|
||||||
use compute_shader::device::Device;
|
use compute_shader::device::Device;
|
||||||
use compute_shader::texture::{Format, Texture};
|
use compute_shader::texture::{ExternalTexture, Format, Texture};
|
||||||
use euclid::size::Size2D;
|
use euclid::size::Size2D;
|
||||||
|
use gl::types::{GLint, GLuint};
|
||||||
|
use gl;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
pub struct CoverageBuffer {
|
pub struct CoverageBuffer {
|
||||||
pub texture: Texture,
|
pub texture: Texture,
|
||||||
|
pub framebuffer: GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoverageBuffer {
|
impl CoverageBuffer {
|
||||||
pub fn new(device: &Device, size: &Size2D<u32>) -> Result<CoverageBuffer, ()> {
|
pub fn new(device: &Device, size: &Size2D<u32>) -> Result<CoverageBuffer, ()> {
|
||||||
let texture = try!(device.create_texture(Format::R32F, Protection::ReadWrite, size)
|
let texture = try!(device.create_texture(Format::R32F, Protection::ReadWrite, size)
|
||||||
.map_err(drop));
|
.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 {
|
Ok(CoverageBuffer {
|
||||||
texture: texture,
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,21 @@
|
||||||
use compute_shader::buffer::{Buffer, BufferData, HostAllocatedData, Protection};
|
use compute_shader::buffer::{Buffer, BufferData, HostAllocatedData, Protection};
|
||||||
use compute_shader::device::Device;
|
use compute_shader::device::Device;
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
|
use gl::types::{GLsizeiptr, GLuint};
|
||||||
|
use gl;
|
||||||
use otf::glyf::GlyfTable;
|
use otf::glyf::GlyfTable;
|
||||||
use otf::head::HeadTable;
|
use otf::head::HeadTable;
|
||||||
use otf::loca::LocaTable;
|
use otf::loca::LocaTable;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
pub struct GlyphBufferBuilder {
|
pub struct GlyphBufferBuilder {
|
||||||
pub coordinates: Vec<(i16, i16)>,
|
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<u16>,
|
||||||
|
|
||||||
pub operations: Vec<u8>,
|
pub operations: Vec<u8>,
|
||||||
pub descriptors: Vec<GlyphDescriptor>,
|
pub descriptors: Vec<GlyphDescriptor>,
|
||||||
}
|
}
|
||||||
|
@ -26,6 +35,7 @@ impl GlyphBufferBuilder {
|
||||||
pub fn new() -> GlyphBufferBuilder {
|
pub fn new() -> GlyphBufferBuilder {
|
||||||
GlyphBufferBuilder {
|
GlyphBufferBuilder {
|
||||||
coordinates: vec![],
|
coordinates: vec![],
|
||||||
|
glyph_indices: vec![],
|
||||||
operations: vec![],
|
operations: vec![],
|
||||||
descriptors: vec![],
|
descriptors: vec![],
|
||||||
}
|
}
|
||||||
|
@ -37,6 +47,8 @@ impl GlyphBufferBuilder {
|
||||||
loca_table: &LocaTable,
|
loca_table: &LocaTable,
|
||||||
glyf_table: &GlyfTable)
|
glyf_table: &GlyfTable)
|
||||||
-> Result<(), ()> {
|
-> Result<(), ()> {
|
||||||
|
let glyph_index = self.descriptors.len() as u16;
|
||||||
|
|
||||||
let mut point_index = self.coordinates.len() / 2;
|
let mut point_index = self.coordinates.len() / 2;
|
||||||
let start_point = point_index;
|
let start_point = point_index;
|
||||||
let mut operations = if point_index % 4 == 0 {
|
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| {
|
try!(glyf_table.for_each_point(loca_table, glyph_id, |point| {
|
||||||
self.coordinates.push((point.position.x, point.position.y));
|
self.coordinates.push((point.position.x, point.position.y));
|
||||||
|
self.glyph_indices.push(glyph_index);
|
||||||
|
|
||||||
let operation = if point.first_point_in_contour {
|
let operation = if point.first_point_in_contour {
|
||||||
0
|
0
|
||||||
|
@ -68,50 +81,67 @@ impl GlyphBufferBuilder {
|
||||||
self.operations.push(operations)
|
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));
|
let bounding_rect = try!(glyf_table.bounding_rect(loca_table, glyph_id));
|
||||||
self.descriptors.push(GlyphDescriptor {
|
self.descriptors.push(GlyphDescriptor {
|
||||||
left: bounding_rect.origin.x,
|
left: bounding_rect.origin.x as i32,
|
||||||
bottom: bounding_rect.origin.y,
|
bottom: bounding_rect.origin.y as i32,
|
||||||
width: bounding_rect.size.width,
|
right: bounding_rect.max_x() as i32,
|
||||||
height: bounding_rect.size.height,
|
top: bounding_rect.max_y() as i32,
|
||||||
units_per_em: head_table.units_per_em,
|
units_per_em: head_table.units_per_em as u32,
|
||||||
point_count: (point_index - start_point) as u16,
|
point_count: (point_index - start_point) as u32,
|
||||||
start_point: start_point as u32,
|
start_point: start_point as u32,
|
||||||
|
pad: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(&self, device: &Device) -> Result<GlyphBuffers, ()> {
|
pub fn finish(&self, device: &Device) -> Result<GlyphBuffers, ()> {
|
||||||
let coordinates = BufferData::HostAllocated(HostAllocatedData::new(&self.coordinates));
|
// TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contours.
|
||||||
let operations = BufferData::HostAllocated(HostAllocatedData::new(&self.operations));
|
unsafe {
|
||||||
let descriptors = BufferData::HostAllocated(HostAllocatedData::new(&self.descriptors));
|
let (mut coordinates, mut descriptors) = (0, 0);
|
||||||
Ok(GlyphBuffers {
|
gl::GenBuffers(1, &mut coordinates);
|
||||||
coordinates: try!(device.create_buffer(Protection::ReadOnly, coordinates)
|
gl::GenBuffers(1, &mut descriptors);
|
||||||
.map_err(drop)),
|
|
||||||
operations: try!(device.create_buffer(Protection::ReadOnly, operations).map_err(drop)),
|
let length = self.coordinates.len() * mem::size_of::<(i16, i16)>();
|
||||||
descriptors: try!(device.create_buffer(Protection::ReadOnly, descriptors)
|
gl::BindBuffer(gl::ARRAY_BUFFER, coordinates);
|
||||||
.map_err(drop)),
|
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::<GlyphDescriptor>();
|
||||||
|
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 struct GlyphBuffers {
|
||||||
pub coordinates: Buffer,
|
pub coordinates: GLuint,
|
||||||
pub operations: Buffer,
|
pub descriptors: GLuint,
|
||||||
pub descriptors: Buffer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct GlyphDescriptor {
|
pub struct GlyphDescriptor {
|
||||||
pub left: i16,
|
pub left: i32,
|
||||||
pub bottom: i16,
|
pub bottom: i32,
|
||||||
pub width: i16,
|
pub right: i32,
|
||||||
pub height: i16,
|
pub top: i32,
|
||||||
pub units_per_em: u16,
|
pub units_per_em: u32,
|
||||||
pub point_count: u16,
|
pub point_count: u32,
|
||||||
pub start_point: u32,
|
pub start_point: u32,
|
||||||
|
pub pad: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlyphDescriptor {
|
impl GlyphDescriptor {
|
||||||
|
@ -119,7 +149,8 @@ impl GlyphDescriptor {
|
||||||
pub fn pixel_rect(&self, point_size: f32) -> Rect<f32> {
|
pub fn pixel_rect(&self, point_size: f32) -> Rect<f32> {
|
||||||
let pixels_per_unit = point_size / self.units_per_em as f32;
|
let pixels_per_unit = point_size / self.units_per_em as f32;
|
||||||
Rect::new(Point2D::new(self.left as f32, self.bottom 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue