Get things basically working

This commit is contained in:
Patrick Walton 2017-01-25 11:52:18 -08:00
parent 50ad78db2a
commit d5b61e382e
10 changed files with 88 additions and 68 deletions

View File

@ -13,7 +13,7 @@ use compute_shader::buffer;
use compute_shader::instance::Instance; use compute_shader::instance::Instance;
use compute_shader::texture::{ExternalTexture, Format}; use compute_shader::texture::{ExternalTexture, Format};
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use gl::types::GLint; use gl::types::{GLint, GLuint};
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode}; use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
use memmap::{Mmap, Protection}; use memmap::{Mmap, Protection};
use pathfinder::batch::{BatchBuilder, GlyphRange}; use pathfinder::batch::{BatchBuilder, GlyphRange};
@ -32,7 +32,7 @@ const SHELF_HEIGHT: u32 = 32;
fn main() { fn main() {
let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap();
glfw.window_hint(WindowHint::ContextVersion(3, 3)); glfw.window_hint(WindowHint::ContextVersion(4, 1));
glfw.window_hint(WindowHint::OpenGlForwardCompat(true)); glfw.window_hint(WindowHint::OpenGlForwardCompat(true));
glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core)); glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
let context = glfw.create_window(WIDTH, HEIGHT, "generate-atlas", WindowMode::Windowed); let context = glfw.create_window(WIDTH, HEIGHT, "generate-atlas", WindowMode::Windowed);
@ -40,6 +40,7 @@ fn main() {
let (mut window, events) = context.expect("Couldn't create a window!"); let (mut window, events) = context.expect("Couldn't create a window!");
window.make_current(); window.make_current();
gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void); gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void);
let (device_pixel_width, device_pixel_height) = window.get_framebuffer_size();
let instance = Instance::new().unwrap(); let instance = Instance::new().unwrap();
let device = instance.create_device().unwrap(); let device = instance.create_device().unwrap();
@ -48,7 +49,7 @@ fn main() {
let rasterizer = Rasterizer::new(device, queue).unwrap(); let rasterizer = Rasterizer::new(device, queue).unwrap();
let mut glyph_buffer_builder = GlyphBufferBuilder::new(); let mut glyph_buffer_builder = GlyphBufferBuilder::new();
let mut batch_builder = BatchBuilder::new(WIDTH, SHELF_HEIGHT); let mut batch_builder = BatchBuilder::new(device_pixel_width as GLuint, SHELF_HEIGHT);
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap(); let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
unsafe { unsafe {
@ -69,7 +70,7 @@ fn main() {
let glyph_buffers = glyph_buffer_builder.finish().unwrap(); let glyph_buffers = glyph_buffer_builder.finish().unwrap();
let batch = batch_builder.finish(&glyph_buffer_builder).unwrap(); let batch = batch_builder.finish(&glyph_buffer_builder).unwrap();
let atlas_size = Size2D::new(WIDTH, HEIGHT); let atlas_size = Size2D::new(device_pixel_width as GLuint, device_pixel_height as GLuint);
let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap(); let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap();
let texture = rasterizer.device let texture = rasterizer.device
@ -98,6 +99,7 @@ fn main() {
} }
unsafe { unsafe {
gl::Viewport(0, 0, device_pixel_width, device_pixel_height);
gl::ClearColor(1.0, 1.0, 1.0, 1.0); gl::ClearColor(1.0, 1.0, 1.0, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT); gl::Clear(gl::COLOR_BUFFER_BIT);
} }

View File

@ -31,7 +31,7 @@ __kernel void accum(__write_only image2d_t gTexture,
int2 coord = (int2)((int)column, (int)row); int2 coord = (int2)((int)column, (int)row);
coverage += read_imagef(gCoverage, SAMPLER, coord).r; coverage += read_imagef(gCoverage, SAMPLER, coord).r;
uint gray = 255 - convert_uint(clamp(coverage, 0.0f, 1.0f) * 255.0f); uint gray = convert_uint(clamp(coverage, 0.0f, 1.0f) * 255.0f);
write_imageui(gTexture, coord + (int2)kAtlasRect.xy, (uint4)(gray, 255, 255, 255)); write_imageui(gTexture, coord + (int2)kAtlasRect.xy, (uint4)(gray, 255, 255, 255));
} }
} }

View File

@ -28,7 +28,8 @@ out vec4 oFragColor;
void main() { void main() {
// Compute the X boundaries of this pixel. // Compute the X boundaries of this pixel.
float xMin = floor(gl_FragCoord.x), xMax = ceil(gl_FragCoord.x); float xMin = floor(gl_FragCoord.x);
float xMax = xMin + 1.0f;
// Compute the horizontal span that the line segment covers across this pixel. // Compute the horizontal span that the line segment covers across this pixel.
float dX = min(xMax, vP1.x) - max(xMin, vP0.x); float dX = min(xMax, vP1.x) - max(xMin, vP0.x);

View File

@ -15,9 +15,6 @@
layout(vertices = 1) out; layout(vertices = 1) out;
// The size of the atlas in pixels.
uniform uvec2 uAtlasSize;
// The vertex ID, passed into this shader. // The vertex ID, passed into this shader.
flat in uint vVertexID[]; flat in uint vVertexID[];
@ -31,25 +28,26 @@ patch out vec2 vpP2;
patch out float vpDirection; patch out float vpDirection;
void main() { void main() {
vpP0 = gl_in[0].gl_Position.xy; vec2 p0 = gl_in[0].gl_Position.xy;
vpP1 = gl_in[1].gl_Position.xy; vec2 p1 = gl_in[1].gl_Position.xy;
vpP2 = gl_in[2].gl_Position.xy; vec2 p2 = gl_in[2].gl_Position.xy;
// Compute direction. Flip around if necessary so that p0 is to the left of p2. // Compute direction. Flip around if necessary so that p0 is to the left of p2.
if (vpP0.x < vpP2.x) { float direction;
vpDirection = 1.0f; if (p0.x < p2.x) {
direction = 1.0f;
} else { } else {
vpDirection = -1.0f; direction = -1.0f;
vec2 tmp = vpP0; vec2 tmp = p0;
vpP0 = vpP2; p0 = p2;
vpP2 = tmp; p2 = tmp;
} }
// Divide into lines. // Divide into lines.
float lineCount = 1.0f; float lineCount = 1.0f;
if (vVertexID[1] > 0) { if (vVertexID[1] > 0) {
// Quadratic curve. // Quadratic curve.
vec2 dev = vpP0 - 2.0f * vpP1 + vpP2; vec2 dev = p0 - 2.0f * p1 + p2;
float devSq = dot(dev, dev); float devSq = dot(dev, dev);
if (devSq >= CURVE_THRESHOLD) { if (devSq >= CURVE_THRESHOLD) {
// Inverse square root is likely no slower and may be faster than regular square root // Inverse square root is likely no slower and may be faster than regular square root
@ -100,10 +98,19 @@ void main() {
// so we're in the clear: the rasterizer will always discard the unshaded areas and render only // so we're in the clear: the rasterizer will always discard the unshaded areas and render only
// the shaded ones. // the shaded ones.
gl_TessLevelInner[0] = vpP0.x == vpP2.x ? 0.0f : lineCount * 2.0f - 1.0f; float tessLevel = p0.x == p2.x ? 0.0f : (lineCount * 2.0f - 1.0f);
gl_TessLevelOuter[1] = gl_TessLevelOuter[3] = gl_TessLevelInner[0]; gl_TessLevelInner[0] = tessLevel;
gl_TessLevelInner[1] = 1.0f;
gl_TessLevelOuter[0] = 1.0f;
gl_TessLevelOuter[1] = tessLevel;
gl_TessLevelOuter[2] = 1.0f;
gl_TessLevelOuter[3] = tessLevel;
// Don't split vertically at all. We only tessellate horizontally. // NB: These per-patch outputs must be assigned in this order, or Apple's compiler will
gl_TessLevelInner[1] = gl_TessLevelOuter[0] = gl_TessLevelOuter[2] = 1.0f; // miscompile us.
vpP0 = p0;
vpP1 = p1;
vpP2 = p2;
vpDirection = direction;
} }

View File

@ -51,7 +51,7 @@ void main() {
float t0 = float(lineIndex + 0) / float(lineCount); float t0 = float(lineIndex + 0) / float(lineCount);
float t1 = float(lineIndex + 1) / float(lineCount); float t1 = float(lineIndex + 1) / float(lineCount);
vP0 = mix(mix(vpP0, vpP1, t0), mix(vpP1, vpP2, t0), t0); vP0 = mix(mix(vpP0, vpP1, t0), mix(vpP1, vpP2, t0), t0);
vP1 = mix(mix(vpP0, vpP1, t1), mix(vpP1, vpP2, t0), t1); vP1 = mix(mix(vpP0, vpP1, t1), mix(vpP1, vpP2, t1), t1);
} }
// Compute Y extents and slope. // Compute Y extents and slope.

View File

@ -10,29 +10,19 @@
#version 410 #version 410
// FIXME(pcwalton): This should be higher. Dynamically query its maximum possible size, perhaps? #define MAX_GLYPHS 2048
#define MAX_GLYPHS 256
// Accessors to work around Apple driver bugs.
#define GLYPH_DESCRIPTOR_UNITS_PER_EM(d) (d).misc.x
#define IMAGE_DESCRIPTOR_ATLAS_POS(d) (d).xy
#define IMAGE_DESCRIPTOR_POINT_SIZE(d) (d).z
// Information about the metrics of each glyph. // Information about the metrics of each glyph.
layout(std140) struct GlyphDescriptor { layout(std140) struct GlyphDescriptor {
// The left/top/right/bottom offsets of the glyph from point (0, 0) in glyph space. // The left/bottom/right/top offsets of the glyph from point (0, 0) in glyph space.
ivec4 extents; ivec4 extents;
// The number of units per em in this glyph. // x: Units per em.
uint unitsPerEm; uvec4 misc;
// 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 ImageDescriptor {
// The left/top/right/bottom positions of the glyph in the atlas.
uvec4 atlasRect;
// The font size in pixels.
float pointSize;
// The index of the glyph.
uint glyphIndex;
}; };
// The size of the atlas in pixels. // The size of the atlas in pixels.
@ -43,7 +33,7 @@ layout(std140) uniform ubGlyphDescriptors {
}; };
layout(std140) uniform ubImageDescriptors { layout(std140) uniform ubImageDescriptors {
ImageDescriptor uImages[MAX_GLYPHS]; uvec4 uImages[MAX_GLYPHS];
}; };
// The position of each vertex in glyph space. // The position of each vertex in glyph space.
@ -60,13 +50,13 @@ flat out uint vVertexID;
void main() { void main() {
vVertexID = gl_VertexID; vVertexID = gl_VertexID;
ImageDescriptor image = uImages[aGlyphIndex]; uvec4 image = uImages[aGlyphIndex];
GlyphDescriptor glyph = uGlyphs[aGlyphIndex]; GlyphDescriptor glyph = uGlyphs[aGlyphIndex];
float emsPerUnit = 1.0f / float(glyph.unitsPerEm);
vec2 glyphPos = vec2(aPosition.x - glyph.extents.x, glyph.extents.w - aPosition.y); vec2 glyphPos = vec2(aPosition.x - glyph.extents.x, glyph.extents.w - aPosition.y);
vec2 atlasPos = glyphPos * emsPerUnit * image.pointSize + vec2(image.atlasRect.xy); float pointSize = float(IMAGE_DESCRIPTOR_POINT_SIZE(image)) / 65536.0f;
vec2 glyphPxPos = glyphPos * pointSize / GLYPH_DESCRIPTOR_UNITS_PER_EM(glyph);
vec2 atlasPos = glyphPxPos + vec2(IMAGE_DESCRIPTOR_ATLAS_POS(image));
gl_Position = vec4(atlasPos, 0.0f, 1.0f); gl_Position = vec4(atlasPos, 0.0f, 1.0f);
} }

View File

@ -53,7 +53,7 @@ impl BatchBuilder {
self.images[glyph_index as usize] = ImageDescriptor { self.images[glyph_index as usize] = ImageDescriptor {
atlas_x: atlas_origin.x, atlas_x: atlas_origin.x,
atlas_y: atlas_origin.y, atlas_y: atlas_origin.y,
point_size: point_size, point_size: (point_size * 65536.0) as u32,
glyph_index: glyph_index, glyph_index: glyph_index,
}; };
@ -167,7 +167,7 @@ impl Iterator for GlyphRangeIter {
pub struct ImageDescriptor { pub struct ImageDescriptor {
atlas_x: u32, atlas_x: u32,
atlas_y: u32, atlas_y: u32,
point_size: f32, point_size: u32,
glyph_index: u32, glyph_index: u32,
} }

View File

@ -48,6 +48,7 @@ impl CoverageBuffer {
gl::TEXTURE_RECTANGLE, gl::TEXTURE_RECTANGLE,
gl_texture, gl_texture,
0); 0);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
} }
Ok(CoverageBuffer { Ok(CoverageBuffer {

View File

@ -61,9 +61,9 @@ impl GlyphBufferBuilder {
if !point.first_point_in_contour && point.on_curve { if !point.first_point_in_contour && point.on_curve {
let indices = if last_point_on_curve { let indices = if last_point_on_curve {
[point_index - 2, 0, point_index - 1] [point_index - 1, 0, point_index]
} else { } else {
[point_index - 3, point_index - 2, point_index - 1] [point_index - 2, point_index - 1, point_index]
}; };
self.indices.extend(indices.iter().cloned()); self.indices.extend(indices.iter().cloned());
} }
@ -132,7 +132,7 @@ pub struct GlyphBuffers {
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub struct GlyphDescriptor { pub struct GlyphDescriptor {
pub left: i32, pub left: i32,
pub bottom: i32, pub bottom: i32,
@ -154,7 +154,7 @@ impl GlyphDescriptor {
} }
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
#[repr(C)] #[repr(C)]
pub struct Vertex { pub struct Vertex {
x: i16, x: i16,

View File

@ -39,16 +39,17 @@ pub struct Rasterizer {
accum_program: Program, accum_program: Program,
draw_vertex_array: GLuint, draw_vertex_array: GLuint,
draw_position_attribute: GLint, draw_position_attribute: GLint,
draw_image_index_attribute: GLint, draw_glyph_index_attribute: GLint,
draw_atlas_size_uniform: GLint, draw_atlas_size_uniform: GLint,
draw_glyph_descriptors_uniform: GLuint, draw_glyph_descriptors_uniform: GLuint,
draw_image_info_uniform: GLuint, draw_image_descriptors_uniform: GLuint,
} }
impl Rasterizer { impl Rasterizer {
pub fn new(device: Device, queue: Queue) -> Result<Rasterizer, ()> { pub fn new(device: Device, queue: Queue) -> Result<Rasterizer, ()> {
let (draw_program, draw_position_attribute, draw_image_index_attribute); let (draw_program, draw_position_attribute, draw_glyph_index_attribute);
let (draw_atlas_size_uniform, draw_glyph_descriptors_uniform, draw_image_info_uniform); let (draw_glyph_descriptors_uniform, draw_image_descriptors_uniform);
let draw_atlas_size_uniform;
let mut draw_vertex_array = 0; let mut draw_vertex_array = 0;
unsafe { unsafe {
let shaders = [ let shaders = [
@ -83,16 +84,17 @@ impl Rasterizer {
draw_position_attribute = draw_position_attribute =
gl::GetAttribLocation(draw_program, b"aPosition\0".as_ptr() as *const GLchar); gl::GetAttribLocation(draw_program, b"aPosition\0".as_ptr() as *const GLchar);
draw_image_index_attribute = draw_glyph_index_attribute =
gl::GetAttribLocation(draw_program, b"aImageIndex\0".as_ptr() as *const GLchar); gl::GetAttribLocation(draw_program, b"aGlyphIndex\0".as_ptr() as *const GLchar);
draw_atlas_size_uniform = draw_atlas_size_uniform =
gl::GetUniformLocation(draw_program, b"uAtlasSize\0".as_ptr() as *const GLchar); gl::GetUniformLocation(draw_program, b"uAtlasSize\0".as_ptr() as *const GLchar);
draw_glyph_descriptors_uniform = draw_glyph_descriptors_uniform =
gl::GetUniformBlockIndex(draw_program, gl::GetUniformBlockIndex(draw_program,
b"ubGlyphDescriptors\0".as_ptr() as *const GLchar); b"ubGlyphDescriptors\0".as_ptr() as *const GLchar);
draw_image_info_uniform = draw_image_descriptors_uniform =
gl::GetUniformBlockIndex(draw_program, b"ubImageInfo\0".as_ptr() as *const GLchar); gl::GetUniformBlockIndex(draw_program,
b"ubImageDescriptors\0".as_ptr() as *const GLchar);
} }
// FIXME(pcwalton): Don't panic if this fails to compile; just return an error. // FIXME(pcwalton): Don't panic if this fails to compile; just return an error.
@ -105,10 +107,10 @@ impl Rasterizer {
accum_program: accum_program, accum_program: accum_program,
draw_vertex_array: draw_vertex_array, draw_vertex_array: draw_vertex_array,
draw_position_attribute: draw_position_attribute, draw_position_attribute: draw_position_attribute,
draw_image_index_attribute: draw_image_index_attribute, draw_glyph_index_attribute: draw_glyph_index_attribute,
draw_atlas_size_uniform: draw_atlas_size_uniform, draw_atlas_size_uniform: draw_atlas_size_uniform,
draw_glyph_descriptors_uniform: draw_glyph_descriptors_uniform, draw_glyph_descriptors_uniform: draw_glyph_descriptors_uniform,
draw_image_info_uniform: draw_image_info_uniform, draw_image_descriptors_uniform: draw_image_descriptors_uniform,
}) })
} }
@ -121,8 +123,15 @@ impl Rasterizer {
texture: &Texture) texture: &Texture)
-> Result<Event, ()> { -> Result<Event, ()> {
unsafe { unsafe {
gl::UseProgram(self.draw_program); gl::BindFramebuffer(gl::FRAMEBUFFER, coverage_buffer.framebuffer);
gl::Viewport(0, 0, atlas_rect.size.width as GLint, atlas_rect.size.height as GLint);
// TODO(pcwalton): Scissor to the atlas rect to clear faster?
gl::ClearColor(0.0, 0.0, 0.0, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::BindVertexArray(self.draw_vertex_array); gl::BindVertexArray(self.draw_vertex_array);
gl::UseProgram(self.draw_program);
// Set up the buffer layout. // Set up the buffer layout.
gl::BindBuffer(gl::ARRAY_BUFFER, glyph_buffers.vertices); gl::BindBuffer(gl::ARRAY_BUFFER, glyph_buffers.vertices);
@ -131,20 +140,20 @@ impl Rasterizer {
gl::SHORT, gl::SHORT,
mem::size_of::<Vertex>() as GLint, mem::size_of::<Vertex>() as GLint,
0 as *const GLvoid); 0 as *const GLvoid);
gl::VertexAttribIPointer(self.draw_image_index_attribute as GLuint, gl::VertexAttribIPointer(self.draw_glyph_index_attribute as GLuint,
1, 1,
gl::UNSIGNED_SHORT, gl::UNSIGNED_SHORT,
mem::size_of::<Vertex>() as GLint, mem::size_of::<Vertex>() as GLint,
mem::size_of::<(i16, i16)>() as *const GLvoid); mem::size_of::<(i16, i16)>() as *const GLvoid);
gl::EnableVertexAttribArray(self.draw_position_attribute as GLuint); gl::EnableVertexAttribArray(self.draw_position_attribute as GLuint);
gl::EnableVertexAttribArray(self.draw_image_index_attribute as GLuint); gl::EnableVertexAttribArray(self.draw_glyph_index_attribute as GLuint);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, glyph_buffers.indices); gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, glyph_buffers.indices);
gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, glyph_buffers.descriptors); gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, glyph_buffers.descriptors);
gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, batch.images); 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_glyph_descriptors_uniform, 1);
gl::UniformBlockBinding(self.draw_program, self.draw_image_info_uniform, 2); gl::UniformBlockBinding(self.draw_program, self.draw_image_descriptors_uniform, 2);
gl::Uniform2ui(self.draw_atlas_size_uniform, gl::Uniform2ui(self.draw_atlas_size_uniform,
atlas_rect.size.width, atlas_rect.size.width,
@ -170,6 +179,16 @@ impl Rasterizer {
gl::UNSIGNED_INT, gl::UNSIGNED_INT,
batch.start_indices.as_ptr() as *const *const GLvoid, batch.start_indices.as_ptr() as *const *const GLvoid,
batch.counts.len() as GLsizei); batch.counts.len() as GLsizei);
gl::Disable(gl::CULL_FACE);
gl::Disable(gl::BLEND);
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
// FIXME(pcwalton): We should have some better synchronization here if we're using
// OpenCL, but I don't know how to do that portably (i.e. on Mac…) Just using
// `glFlush()` seems to work in practice.
gl::Flush();
} }
let atlas_rect_uniform = [ let atlas_rect_uniform = [