From d5b61e382eebbae414ef80b3e3f15ba91c942640 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 25 Jan 2017 11:52:18 -0800 Subject: [PATCH] Get things basically working --- examples/generate-atlas.rs | 10 ++++--- resources/shaders/accum.cl | 2 +- resources/shaders/draw.fs.glsl | 3 ++- resources/shaders/draw.tcs.glsl | 41 ++++++++++++++++------------ resources/shaders/draw.tes.glsl | 2 +- resources/shaders/draw.vs.glsl | 38 ++++++++++---------------- src/batch.rs | 4 +-- src/coverage.rs | 1 + src/glyph_buffer.rs | 8 +++--- src/rasterizer.rs | 47 +++++++++++++++++++++++---------- 10 files changed, 88 insertions(+), 68 deletions(-) diff --git a/examples/generate-atlas.rs b/examples/generate-atlas.rs index 2d456dbe..71391b26 100644 --- a/examples/generate-atlas.rs +++ b/examples/generate-atlas.rs @@ -13,7 +13,7 @@ use compute_shader::buffer; use compute_shader::instance::Instance; use compute_shader::texture::{ExternalTexture, Format}; use euclid::{Point2D, Rect, Size2D}; -use gl::types::GLint; +use gl::types::{GLint, GLuint}; use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode}; use memmap::{Mmap, Protection}; use pathfinder::batch::{BatchBuilder, GlyphRange}; @@ -32,7 +32,7 @@ const SHELF_HEIGHT: u32 = 32; fn main() { 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::OpenGlProfile(OpenGlProfileHint::Core)); 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!"); window.make_current(); 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 device = instance.create_device().unwrap(); @@ -48,7 +49,7 @@ fn main() { let rasterizer = Rasterizer::new(device, queue).unwrap(); 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(); unsafe { @@ -69,7 +70,7 @@ fn main() { 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 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 texture = rasterizer.device @@ -98,6 +99,7 @@ fn main() { } unsafe { + gl::Viewport(0, 0, device_pixel_width, device_pixel_height); gl::ClearColor(1.0, 1.0, 1.0, 1.0); gl::Clear(gl::COLOR_BUFFER_BIT); } diff --git a/resources/shaders/accum.cl b/resources/shaders/accum.cl index a6022900..5da39682 100644 --- a/resources/shaders/accum.cl +++ b/resources/shaders/accum.cl @@ -31,7 +31,7 @@ __kernel void accum(__write_only image2d_t gTexture, int2 coord = (int2)((int)column, (int)row); 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)); } } diff --git a/resources/shaders/draw.fs.glsl b/resources/shaders/draw.fs.glsl index 2aa9d1ce..7e348c06 100644 --- a/resources/shaders/draw.fs.glsl +++ b/resources/shaders/draw.fs.glsl @@ -28,7 +28,8 @@ out vec4 oFragColor; void main() { // 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. float dX = min(xMax, vP1.x) - max(xMin, vP0.x); diff --git a/resources/shaders/draw.tcs.glsl b/resources/shaders/draw.tcs.glsl index 6a69321e..c967caa8 100644 --- a/resources/shaders/draw.tcs.glsl +++ b/resources/shaders/draw.tcs.glsl @@ -15,9 +15,6 @@ layout(vertices = 1) out; -// The size of the atlas in pixels. -uniform uvec2 uAtlasSize; - // The vertex ID, passed into this shader. flat in uint vVertexID[]; @@ -31,25 +28,26 @@ patch out vec2 vpP2; patch out float vpDirection; void main() { - vpP0 = gl_in[0].gl_Position.xy; - vpP1 = gl_in[1].gl_Position.xy; - vpP2 = gl_in[2].gl_Position.xy; + vec2 p0 = gl_in[0].gl_Position.xy; + vec2 p1 = gl_in[1].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. - if (vpP0.x < vpP2.x) { - vpDirection = 1.0f; + float direction; + if (p0.x < p2.x) { + direction = 1.0f; } else { - vpDirection = -1.0f; - vec2 tmp = vpP0; - vpP0 = vpP2; - vpP2 = tmp; + direction = -1.0f; + vec2 tmp = p0; + p0 = p2; + p2 = tmp; } // Divide into lines. float lineCount = 1.0f; if (vVertexID[1] > 0) { // Quadratic curve. - vec2 dev = vpP0 - 2.0f * vpP1 + vpP2; + vec2 dev = p0 - 2.0f * p1 + p2; float devSq = dot(dev, dev); if (devSq >= CURVE_THRESHOLD) { // 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 // the shaded ones. - gl_TessLevelInner[0] = vpP0.x == vpP2.x ? 0.0f : lineCount * 2.0f - 1.0f; - gl_TessLevelOuter[1] = gl_TessLevelOuter[3] = gl_TessLevelInner[0]; + float tessLevel = p0.x == p2.x ? 0.0f : (lineCount * 2.0f - 1.0f); + 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. - gl_TessLevelInner[1] = gl_TessLevelOuter[0] = gl_TessLevelOuter[2] = 1.0f; + // NB: These per-patch outputs must be assigned in this order, or Apple's compiler will + // miscompile us. + vpP0 = p0; + vpP1 = p1; + vpP2 = p2; + vpDirection = direction; } diff --git a/resources/shaders/draw.tes.glsl b/resources/shaders/draw.tes.glsl index 6b50546f..b6e6481a 100644 --- a/resources/shaders/draw.tes.glsl +++ b/resources/shaders/draw.tes.glsl @@ -51,7 +51,7 @@ void main() { float t0 = float(lineIndex + 0) / float(lineCount); float t1 = float(lineIndex + 1) / float(lineCount); 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. diff --git a/resources/shaders/draw.vs.glsl b/resources/shaders/draw.vs.glsl index bfeef462..308bd76b 100644 --- a/resources/shaders/draw.vs.glsl +++ b/resources/shaders/draw.vs.glsl @@ -10,29 +10,19 @@ #version 410 -// FIXME(pcwalton): This should be higher. Dynamically query its maximum possible size, perhaps? -#define MAX_GLYPHS 256 +#define MAX_GLYPHS 2048 + +// 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. 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; - // The number of units per em in this glyph. - uint unitsPerEm; - // 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; + // x: Units per em. + uvec4 misc; }; // The size of the atlas in pixels. @@ -43,7 +33,7 @@ layout(std140) uniform ubGlyphDescriptors { }; layout(std140) uniform ubImageDescriptors { - ImageDescriptor uImages[MAX_GLYPHS]; + uvec4 uImages[MAX_GLYPHS]; }; // The position of each vertex in glyph space. @@ -60,13 +50,13 @@ flat out uint vVertexID; void main() { vVertexID = gl_VertexID; - ImageDescriptor image = uImages[aGlyphIndex]; + uvec4 image = uImages[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 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); } diff --git a/src/batch.rs b/src/batch.rs index e447e943..22774eb4 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -53,7 +53,7 @@ impl BatchBuilder { self.images[glyph_index as usize] = ImageDescriptor { atlas_x: atlas_origin.x, atlas_y: atlas_origin.y, - point_size: point_size, + point_size: (point_size * 65536.0) as u32, glyph_index: glyph_index, }; @@ -167,7 +167,7 @@ impl Iterator for GlyphRangeIter { pub struct ImageDescriptor { atlas_x: u32, atlas_y: u32, - point_size: f32, + point_size: u32, glyph_index: u32, } diff --git a/src/coverage.rs b/src/coverage.rs index f68471e7..18f1beb5 100644 --- a/src/coverage.rs +++ b/src/coverage.rs @@ -48,6 +48,7 @@ impl CoverageBuffer { gl::TEXTURE_RECTANGLE, gl_texture, 0); + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); } Ok(CoverageBuffer { diff --git a/src/glyph_buffer.rs b/src/glyph_buffer.rs index 40793dc8..4ea81ac7 100644 --- a/src/glyph_buffer.rs +++ b/src/glyph_buffer.rs @@ -61,9 +61,9 @@ impl GlyphBufferBuilder { if !point.first_point_in_contour && point.on_curve { let indices = if last_point_on_curve { - [point_index - 2, 0, point_index - 1] + [point_index - 1, 0, point_index] } else { - [point_index - 3, point_index - 2, point_index - 1] + [point_index - 2, point_index - 1, point_index] }; self.indices.extend(indices.iter().cloned()); } @@ -132,7 +132,7 @@ pub struct GlyphBuffers { } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct GlyphDescriptor { pub left: i32, pub bottom: i32, @@ -154,7 +154,7 @@ impl GlyphDescriptor { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] #[repr(C)] pub struct Vertex { x: i16, diff --git a/src/rasterizer.rs b/src/rasterizer.rs index 52625732..bc8f9f7d 100644 --- a/src/rasterizer.rs +++ b/src/rasterizer.rs @@ -39,16 +39,17 @@ pub struct Rasterizer { accum_program: Program, draw_vertex_array: GLuint, draw_position_attribute: GLint, - draw_image_index_attribute: GLint, + draw_glyph_index_attribute: GLint, draw_atlas_size_uniform: GLint, draw_glyph_descriptors_uniform: GLuint, - draw_image_info_uniform: GLuint, + draw_image_descriptors_uniform: GLuint, } impl Rasterizer { pub fn new(device: Device, queue: Queue) -> Result { - let (draw_program, draw_position_attribute, draw_image_index_attribute); - let (draw_atlas_size_uniform, draw_glyph_descriptors_uniform, draw_image_info_uniform); + let (draw_program, draw_position_attribute, draw_glyph_index_attribute); + let (draw_glyph_descriptors_uniform, draw_image_descriptors_uniform); + let draw_atlas_size_uniform; let mut draw_vertex_array = 0; unsafe { let shaders = [ @@ -83,16 +84,17 @@ impl Rasterizer { 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_glyph_index_attribute = + gl::GetAttribLocation(draw_program, b"aGlyphIndex\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); + draw_image_descriptors_uniform = + 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. @@ -105,10 +107,10 @@ impl Rasterizer { 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_glyph_index_attribute: draw_glyph_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, + draw_image_descriptors_uniform: draw_image_descriptors_uniform, }) } @@ -121,8 +123,15 @@ impl Rasterizer { texture: &Texture) -> Result { 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::UseProgram(self.draw_program); // Set up the buffer layout. gl::BindBuffer(gl::ARRAY_BUFFER, glyph_buffers.vertices); @@ -131,20 +140,20 @@ impl Rasterizer { gl::SHORT, mem::size_of::() as GLint, 0 as *const GLvoid); - gl::VertexAttribIPointer(self.draw_image_index_attribute as GLuint, + gl::VertexAttribIPointer(self.draw_glyph_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::EnableVertexAttribArray(self.draw_glyph_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::UniformBlockBinding(self.draw_program, self.draw_image_descriptors_uniform, 2); gl::Uniform2ui(self.draw_atlas_size_uniform, atlas_rect.size.width, @@ -170,6 +179,16 @@ impl Rasterizer { gl::UNSIGNED_INT, batch.start_indices.as_ptr() as *const *const GLvoid, 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 = [