Make zooming work better; fix atlas sizing bugs

This commit is contained in:
Patrick Walton 2017-01-30 18:33:44 -08:00
parent a927294790
commit 5bccf88445
6 changed files with 533 additions and 181 deletions

View File

@ -11,7 +11,8 @@ gl = "0.6"
memmap = "0.5"
[dependencies.compute-shader]
git = "https://github.com/pcwalton/compute-shader.git"
# git = "https://github.com/pcwalton/compute-shader.git"
path = "/Users/pcwalton/Source/rust-packages/compute-shader"
[dev-dependencies]
quickcheck = "0.4"

View File

@ -19,25 +19,34 @@ use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode};
use memmap::{Mmap, Protection};
use pathfinder::batch::BatchBuilder;
use pathfinder::charmap::CodepointRanges;
use pathfinder::charmap::CodepointRange;
use pathfinder::coverage::CoverageBuffer;
use pathfinder::glyph_buffer::{GlyphBufferBuilder, GlyphBuffers};
use pathfinder::glyph_range::GlyphRanges;
use pathfinder::otf::Font;
use pathfinder::rasterizer::{Rasterizer, RasterizerOptions};
use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions};
use pathfinder::shaper;
use std::env;
use std::mem;
use std::os::raw::c_void;
const ATLAS_SIZE: u32 = 2048;
const WIDTH: u32 = 512;
const HEIGHT: u32 = 384;
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
const SCROLL_SPEED: f64 = 6.0;
const UNITS_PER_EM: u32 = 2048;
const INITIAL_POINT_SIZE: f32 = 24.0;
const MIN_POINT_SIZE: f32 = 6.0;
const MAX_POINT_SIZE: f32 = 256.0;
const FPS_DISPLAY_POINT_SIZE: f32 = 24.0;
const FPS_PADDING: i32 = 6;
static FPS_BACKGROUND_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 0.7];
static FPS_FOREGROUND_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
static TEXT_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
fn main() {
let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap();
glfw.window_hint(WindowHint::ContextVersion(3, 3));
@ -58,15 +67,13 @@ fn main() {
let mut chars: Vec<char> = TEXT.chars().collect();
chars.sort();
let codepoint_ranges = CodepointRanges::from_sorted_chars(&chars);
let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)];
let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap();
let (font, shaped_glyph_positions, glyph_ranges);
unsafe {
font = Font::new(file.as_slice()).unwrap();
glyph_ranges = font.cmap
.glyph_ranges_for_codepoint_ranges(&codepoint_ranges.ranges)
.unwrap();
glyph_ranges = font.cmap.glyph_ranges_for_codepoint_ranges(&codepoint_ranges).unwrap();
shaped_glyph_positions = shaper::shape_text(&font, &glyph_ranges, TEXT)
}
@ -78,7 +85,8 @@ fn main() {
let line_spacing = UNITS_PER_EM;
let (mut current_x, mut current_y) = (0, line_spacing);
for glyph_position in &shaped_glyph_positions {
if current_x + glyph_position.advance as u32 > paragraph_width {
current_x += glyph_position.advance as u32;
if current_x > paragraph_width {
current_x = 0;
current_y += line_spacing;
}
@ -87,11 +95,11 @@ fn main() {
y: current_y,
glyph_id: glyph_position.glyph_id,
});
current_x += glyph_position.advance as u32;
}
let renderer = Renderer::new();
let mut point_size = INITIAL_POINT_SIZE;
let mut translation = Point2D::new(0, 0);
let mut dirty = true;
let mut glyph_buffer_builder = GlyphBufferBuilder::new();
@ -102,26 +110,56 @@ fn main() {
}
let glyph_buffers = glyph_buffer_builder.create_buffers().unwrap();
let mut fps_batch = renderer.create_fps_batch(&glyph_buffer_builder,
&glyph_buffers,
glyph_count);
while !window.should_close() {
if dirty {
renderer.redraw(point_size,
let events = renderer.redraw(point_size,
&glyph_buffer_builder,
&glyph_buffers,
glyph_count,
&glyph_positions,
&device_pixel_size);
&device_pixel_size,
&translation);
let mut draw_time = 0u64;
unsafe {
gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_time);
}
let draw_time = draw_time as f64;
let accum_time = events.accum.time_elapsed().unwrap() as f64;
let timing = renderer.get_timing_in_ms();
renderer.draw_fps(&font,
&mut fps_batch,
&glyph_buffer_builder,
&device_pixel_size,
&glyph_ranges,
draw_time,
accum_time,
timing,
glyph_count);
window.swap_buffers();
dirty = false
}
glfw.wait_events();
for (_, event) in glfw::flush_messages(&events) {
match event {
WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
window.set_should_close(true)
}
WindowEvent::Scroll(_, y) => {
point_size += y as f32;
WindowEvent::Scroll(x, y) => {
if window.get_key(Key::LeftAlt) == Action::Press ||
window.get_key(Key::RightAlt) == Action::Press {
let old_point_size = point_size;
point_size = old_point_size + y as f32;
if point_size < MIN_POINT_SIZE {
point_size = MIN_POINT_SIZE
@ -129,6 +167,21 @@ fn main() {
point_size = MAX_POINT_SIZE
}
let mut center = Point2D::new(
translation.x as f32 - device_pixel_size.width as f32 * 0.5,
translation.y as f32 - device_pixel_size.height as f32 * 0.5);
center.x = center.x * point_size / old_point_size;
center.y = center.y * point_size / old_point_size;
translation.x = (center.x + device_pixel_size.width as f32 * 0.5).round()
as i32;
translation.y = (center.y + device_pixel_size.height as f32 * 0.5).round()
as i32;
} else {
translation.x += (x * SCROLL_SPEED).round() as i32;
translation.y += (y * SCROLL_SPEED).round() as i32;
}
dirty = true
}
WindowEvent::Size(_, _) | WindowEvent::FramebufferSize(_, _) => {
@ -145,20 +198,32 @@ fn main() {
struct Renderer {
rasterizer: Rasterizer,
program: GLuint,
atlas_uniform: GLint,
transform_uniform: GLint,
translation_uniform: GLint,
composite_program: GLuint,
composite_atlas_uniform: GLint,
composite_transform_uniform: GLint,
composite_translation_uniform: GLint,
composite_color_uniform: GLint,
vertex_array: GLuint,
vertex_buffer: GLuint,
index_buffer: GLuint,
composite_vertex_array: GLuint,
composite_vertex_buffer: GLuint,
composite_index_buffer: GLuint,
solid_color_program: GLuint,
solid_color_color_uniform: GLint,
solid_color_vertex_array: GLuint,
solid_color_vertex_buffer: GLuint,
solid_color_index_buffer: GLuint,
atlas_size: Size2D<u32>,
coverage_buffer: CoverageBuffer,
compute_texture: Texture,
gl_texture: GLuint,
main_compute_texture: Texture,
main_gl_texture: GLuint,
fps_compute_texture: Texture,
fps_gl_texture: GLuint,
query: GLuint,
}
impl Renderer {
@ -170,62 +235,87 @@ impl Renderer {
let rasterizer_options = RasterizerOptions::from_env().unwrap();
let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap();
let (program, position_attribute, tex_coord_attribute, atlas_uniform);
let (transform_uniform, translation_uniform);
let (mut vertex_array, mut vertex_buffer, mut index_buffer) = (0, 0, 0);
let (composite_program, composite_position_attribute, composite_tex_coord_attribute);
let (composite_atlas_uniform, composite_transform_uniform);
let (composite_translation_uniform, composite_color_uniform);
let (solid_color_program, solid_color_position_attribute, solid_color_color_uniform);
let (mut composite_vertex_array, mut solid_color_vertex_array) = (0, 0);
let (mut composite_vertex_buffer, mut composite_index_buffer) = (0, 0);
let (mut solid_color_vertex_buffer, mut solid_color_index_buffer) = (0, 0);
unsafe {
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
gl::ShaderSource(vertex_shader,
1,
&(VERTEX_SHADER.as_ptr() as *const u8 as *const GLchar),
&(VERTEX_SHADER.len() as GLint));
gl::ShaderSource(fragment_shader,
1,
&(FRAGMENT_SHADER.as_ptr() as *const u8 as *const GLchar),
&(FRAGMENT_SHADER.len() as GLint));
gl::CompileShader(vertex_shader);
gl::CompileShader(fragment_shader);
program = gl::CreateProgram();
gl::AttachShader(program, vertex_shader);
gl::AttachShader(program, fragment_shader);
gl::LinkProgram(program);
gl::UseProgram(program);
position_attribute = gl::GetAttribLocation(program,
"aPosition\0".as_ptr() as *const GLchar);
tex_coord_attribute = gl::GetAttribLocation(program,
"aTexCoord\0".as_ptr() as *const GLchar);
atlas_uniform = gl::GetUniformLocation(program, "uAtlas\0".as_ptr() as *const GLchar);
transform_uniform = gl::GetUniformLocation(program,
composite_program = create_program(COMPOSITE_VERTEX_SHADER, COMPOSITE_FRAGMENT_SHADER);
composite_position_attribute =
gl::GetAttribLocation(composite_program, "aPosition\0".as_ptr() as *const GLchar);
composite_tex_coord_attribute =
gl::GetAttribLocation(composite_program, "aTexCoord\0".as_ptr() as *const GLchar);
composite_atlas_uniform =
gl::GetUniformLocation(composite_program, "uAtlas\0".as_ptr() as *const GLchar);
composite_transform_uniform =
gl::GetUniformLocation(composite_program,
"uTransform\0".as_ptr() as *const GLchar);
translation_uniform =
gl::GetUniformLocation(program, "uTranslation\0".as_ptr() as *const GLchar);
composite_translation_uniform =
gl::GetUniformLocation(composite_program,
"uTranslation\0".as_ptr() as *const GLchar);
composite_color_uniform =
gl::GetUniformLocation(composite_program, "uColor\0".as_ptr() as *const GLchar);
gl::GenVertexArrays(1, &mut vertex_array);
gl::BindVertexArray(vertex_array);
solid_color_program = create_program(SOLID_COLOR_VERTEX_SHADER,
SOLID_COLOR_FRAGMENT_SHADER);
solid_color_position_attribute =
gl::GetAttribLocation(solid_color_program,
"aPosition\0".as_ptr() as *const GLchar);
solid_color_color_uniform =
gl::GetUniformLocation(solid_color_program, "uColor\0".as_ptr() as *const GLchar);
gl::GenBuffers(1, &mut vertex_buffer);
gl::GenBuffers(1, &mut index_buffer);
gl::UseProgram(composite_program);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer);
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
gl::GenVertexArrays(1, &mut composite_vertex_array);
gl::BindVertexArray(composite_vertex_array);
gl::VertexAttribPointer(position_attribute as GLuint,
gl::GenBuffers(1, &mut composite_vertex_buffer);
gl::GenBuffers(1, &mut composite_index_buffer);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, composite_index_buffer);
gl::BindBuffer(gl::ARRAY_BUFFER, composite_vertex_buffer);
gl::VertexAttribPointer(composite_position_attribute as GLuint,
2,
gl::INT,
gl::FALSE,
mem::size_of::<Vertex>() as GLsizei,
0 as *const GLvoid);
gl::VertexAttribPointer(tex_coord_attribute as GLuint,
gl::VertexAttribPointer(composite_tex_coord_attribute as GLuint,
2,
gl::UNSIGNED_INT,
gl::FALSE,
mem::size_of::<Vertex>() as GLsizei,
(mem::size_of::<f32>() * 2) as *const GLvoid);
gl::EnableVertexAttribArray(position_attribute as GLuint);
gl::EnableVertexAttribArray(tex_coord_attribute as GLuint);
gl::EnableVertexAttribArray(composite_position_attribute as GLuint);
gl::EnableVertexAttribArray(composite_tex_coord_attribute as GLuint);
gl::UseProgram(solid_color_program);
gl::GenVertexArrays(1, &mut solid_color_vertex_array);
gl::BindVertexArray(solid_color_vertex_array);
gl::GenBuffers(1, &mut solid_color_vertex_buffer);
gl::GenBuffers(1, &mut solid_color_index_buffer);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, solid_color_index_buffer);
gl::BindBuffer(gl::ARRAY_BUFFER, solid_color_vertex_buffer);
gl::VertexAttribPointer(solid_color_position_attribute as GLuint,
2,
gl::FLOAT,
gl::FALSE,
mem::size_of::<i32>() as GLsizei * 2,
0 as *const GLvoid);
gl::EnableVertexAttribArray(solid_color_position_attribute as GLuint);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
(RECT_INDICES.len() * mem::size_of::<u32>()) as GLsizeiptr,
RECT_INDICES.as_ptr() as *const GLvoid,
gl::STATIC_DRAW);
}
// FIXME(pcwalton)
@ -233,43 +323,43 @@ impl Renderer {
let coverage_buffer = CoverageBuffer::new(&rasterizer.device, &atlas_size).unwrap();
let compute_texture = rasterizer.device.create_texture(Format::R8,
buffer::Protection::WriteOnly,
&atlas_size).unwrap();
let (main_compute_texture, main_gl_texture) = create_texture(&rasterizer, &atlas_size);
let (fps_compute_texture, fps_gl_texture) = create_texture(&rasterizer, &atlas_size);
let mut gl_texture = 0;
let mut query = 0;
unsafe {
gl::GenTextures(1, &mut gl_texture);
compute_texture.bind_to(&ExternalTexture::Gl(gl_texture)).unwrap();
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::GenQueries(1, &mut query);
}
Renderer {
rasterizer: rasterizer,
program: program,
atlas_uniform: atlas_uniform,
transform_uniform: transform_uniform,
translation_uniform: translation_uniform,
composite_program: composite_program,
composite_atlas_uniform: composite_atlas_uniform,
composite_transform_uniform: composite_transform_uniform,
composite_translation_uniform: composite_translation_uniform,
composite_color_uniform: composite_color_uniform,
vertex_array: vertex_array,
vertex_buffer: vertex_buffer,
index_buffer: index_buffer,
composite_vertex_array: composite_vertex_array,
composite_vertex_buffer: composite_vertex_buffer,
composite_index_buffer: composite_index_buffer,
solid_color_program: solid_color_program,
solid_color_color_uniform: solid_color_color_uniform,
solid_color_vertex_array: solid_color_vertex_array,
solid_color_vertex_buffer: solid_color_vertex_buffer,
solid_color_index_buffer: solid_color_index_buffer,
atlas_size: atlas_size,
coverage_buffer: coverage_buffer,
compute_texture: compute_texture,
gl_texture: gl_texture,
main_compute_texture: main_compute_texture,
main_gl_texture: main_gl_texture,
fps_compute_texture: fps_compute_texture,
fps_gl_texture: fps_gl_texture,
query: query,
}
}
@ -279,38 +369,120 @@ impl Renderer {
glyph_buffers: &GlyphBuffers,
glyph_count: usize,
glyph_positions: &[GlyphPos],
device_pixel_size: &Size2D<u32>) {
device_pixel_size: &Size2D<u32>,
translation: &Point2D<i32>)
-> DrawAtlasProfilingEvents {
// FIXME(pcwalton)
let shelf_height = (point_size * 2.0).ceil() as u32;
let mut batch_builder = BatchBuilder::new(device_pixel_size.width, shelf_height);
let mut batch_builder = BatchBuilder::new(ATLAS_SIZE, shelf_height);
for glyph_index in 0..(glyph_count as u32) {
batch_builder.add_glyph(&glyph_buffer_builder, glyph_index, point_size).unwrap()
}
let batch = batch_builder.create_batch(&glyph_buffer_builder).unwrap();
let pixels_per_unit = point_size as f32 / UNITS_PER_EM as f32;
self.rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), self.atlas_size),
shelf_height,
let events = self.rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), self.atlas_size),
&batch_builder.atlas,
glyph_buffers,
&batch,
&self.coverage_buffer,
&self.compute_texture).unwrap();
&self.main_compute_texture).unwrap();
self.rasterizer.queue.flush().unwrap();
unsafe {
gl::UseProgram(self.program);
gl::BindVertexArray(self.vertex_array);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.index_buffer);
gl::Viewport(0,
0,
device_pixel_size.width as GLint,
device_pixel_size.height as GLint);
gl::ClearColor(1.0, 1.0, 1.0, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
self.draw_glyphs(&mut batch_builder,
glyph_buffer_builder,
glyph_positions,
device_pixel_size,
translation,
self.main_gl_texture,
point_size,
&TEXT_COLOR);
events
}
fn get_timing_in_ms(&self) -> f64 {
unsafe {
let mut result = 0;
gl::GetQueryObjectui64v(self.query, gl::QUERY_RESULT, &mut result);
(result as f64) / (1_000_000.0)
}
}
fn draw_glyphs(&self,
batch_builder: &mut BatchBuilder,
glyph_buffer_builder: &GlyphBufferBuilder,
glyph_positions: &[GlyphPos],
device_pixel_size: &Size2D<u32>,
translation: &Point2D<i32>,
texture: GLuint,
point_size: f32,
color: &[f32]) {
unsafe {
gl::UseProgram(self.composite_program);
gl::BindVertexArray(self.composite_vertex_array);
gl::BindBuffer(gl::ARRAY_BUFFER, self.composite_vertex_buffer);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.composite_index_buffer);
let vertex_count = self.upload_quads_for_text(batch_builder,
glyph_buffer_builder,
glyph_positions,
point_size);
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_RECTANGLE, texture);
gl::Uniform1i(self.composite_atlas_uniform, 0);
let matrix = [
2.0 / device_pixel_size.width as f32, 0.0,
0.0, -2.0 / device_pixel_size.height as f32,
];
gl::UniformMatrix2fv(self.composite_transform_uniform, 1, gl::FALSE, matrix.as_ptr());
gl::Uniform2f(self.composite_translation_uniform,
-1.0 + 2.0 * translation.x as f32 / device_pixel_size.width as f32,
1.0 - 2.0 * translation.y as f32 / device_pixel_size.height as f32);
gl::Uniform4fv(self.composite_color_uniform, 1, color.as_ptr());
gl::Enable(gl::BLEND);
gl::BlendEquation(gl::FUNC_ADD);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::BeginQuery(gl::TIME_ELAPSED, self.query);
gl::DrawElements(gl::TRIANGLES,
vertex_count as GLsizei,
gl::UNSIGNED_SHORT,
0 as *const GLvoid);
gl::EndQuery(gl::TIME_ELAPSED);
}
}
fn upload_quads_for_text(&self,
batch_builder: &mut BatchBuilder,
glyph_buffer_builder: &GlyphBufferBuilder,
glyph_positions: &[GlyphPos],
point_size: f32)
-> usize {
let pixels_per_unit = point_size as f32 / UNITS_PER_EM as f32;
let (mut vertices, mut indices) = (vec![], vec![]);
for position in glyph_positions {
let glyph_index = batch_builder.glyph_index_for(position.glyph_id).unwrap();
let glyph_index = match batch_builder.glyph_index_for(position.glyph_id) {
None => continue,
Some(glyph_index) => glyph_index,
};
let glyph_bounds = glyph_buffer_builder.glyph_bounds(glyph_index);
let uv_rect = batch_builder.atlas_rect(glyph_index);
let (uv_bl, uv_tr) = (uv_rect.origin, uv_rect.bottom_right());
@ -328,9 +500,10 @@ impl Renderer {
vertices.push(Vertex::new(right_pos, top_pos, uv_tr.x, uv_bl.y));
vertices.push(Vertex::new(left_pos, top_pos, uv_bl.x, uv_bl.y));
indices.extend([0, 1, 3, 1, 2, 3].iter().map(|index| first_index + index));
indices.extend(RECT_INDICES.iter().map(|index| first_index + index));
}
unsafe {
gl::BufferData(gl::ARRAY_BUFFER,
(vertices.len() * mem::size_of::<Vertex>()) as GLsizeiptr,
vertices.as_ptr() as *const GLvoid,
@ -339,33 +512,104 @@ impl Renderer {
(indices.len() * mem::size_of::<u16>()) as GLsizeiptr,
indices.as_ptr() as *const GLvoid,
gl::STATIC_DRAW);
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_RECTANGLE, self.gl_texture);
gl::Uniform1i(self.atlas_uniform, 0);
let matrix = [
2.0 / device_pixel_size.width as f32, 0.0,
0.0, -2.0 / device_pixel_size.height as f32,
];
gl::UniformMatrix2fv(self.transform_uniform, 1, gl::FALSE, matrix.as_ptr());
gl::Uniform2f(self.translation_uniform,
-1.0,
1.0);
gl::Viewport(0,
0,
device_pixel_size.width as GLint,
device_pixel_size.height as GLint);
gl::ClearColor(1.0, 1.0, 1.0, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::DrawElements(gl::TRIANGLES,
indices.len() as GLsizei,
gl::UNSIGNED_SHORT,
0 as *const GLvoid);
}
indices.len()
}
fn create_fps_batch(&self,
glyph_buffer_builder: &GlyphBufferBuilder,
glyph_buffers: &GlyphBuffers,
glyph_count: usize)
-> BatchBuilder {
// FIXME(pcwalton)
let shelf_height = (FPS_DISPLAY_POINT_SIZE * 2.0).ceil() as u32;
let mut batch_builder = BatchBuilder::new(ATLAS_SIZE, shelf_height);
for glyph_index in 0..(glyph_count as u32) {
batch_builder.add_glyph(&glyph_buffer_builder,
glyph_index,
FPS_DISPLAY_POINT_SIZE).unwrap()
}
let batch = batch_builder.create_batch(&glyph_buffer_builder).unwrap();
self.rasterizer.draw_atlas(&Rect::new(Point2D::new(0, 0), self.atlas_size),
&batch_builder.atlas,
glyph_buffers,
&batch,
&self.coverage_buffer,
&self.fps_compute_texture).unwrap();
batch_builder
}
fn draw_fps(&self,
font: &Font,
batch_builder: &mut BatchBuilder,
glyph_buffer_builder: &GlyphBufferBuilder,
device_pixel_size: &Size2D<u32>,
glyph_ranges: &GlyphRanges,
draw_time: f64,
accum_time: f64,
composite_time: f64,
glyph_count: usize) {
// Draw the background color.
unsafe {
gl::BindVertexArray(self.solid_color_vertex_array);
gl::UseProgram(self.solid_color_program);
gl::BindBuffer(gl::ARRAY_BUFFER, self.solid_color_vertex_buffer);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.solid_color_index_buffer);
let tl = Point2D::new(
-1.0,
-1.0 + (FPS_DISPLAY_POINT_SIZE + FPS_PADDING as f32 * 2.0) /
(device_pixel_size.height as f32) * 2.0);
let br = Point2D::new(1.0, -1.0);
let vertices = [(tl.x, tl.y), (br.x, tl.y), (br.x, br.y), (tl.x, br.y)];
gl::BufferData(gl::ARRAY_BUFFER,
(vertices.len() * mem::size_of::<(f32, f32)>()) as GLsizeiptr,
vertices.as_ptr() as *const GLvoid,
gl::DYNAMIC_DRAW);
gl::Uniform4fv(self.solid_color_color_uniform, 1, FPS_BACKGROUND_COLOR.as_ptr());
gl::Enable(gl::BLEND);
gl::BlendEquation(gl::FUNC_ADD);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_SHORT, 0 as *const GLvoid);
}
let fps_text = format!("draw: {:.3}ms ({:.3}us/glyph), \
accum: {:.3}ms ({:.3}us/glyph), \
composite: {:.3}ms ({:.3}us/glyph)",
draw_time / 1_000_000.0,
draw_time / (1000.0 * glyph_count as f64),
accum_time / 1_000_000.0,
accum_time / (1000.0 * glyph_count as f64),
composite_time,
(composite_time * 1000.0) / (glyph_count as f64));
let mut fps_glyphs = vec![];
let mut current_x = 0;
for glyph_pos in &shaper::shape_text(&font, &glyph_ranges, &fps_text) {
current_x += glyph_pos.advance as u32;
fps_glyphs.push(GlyphPos {
x: current_x,
y: 0,
glyph_id: glyph_pos.glyph_id,
});
}
self.draw_glyphs(batch_builder,
glyph_buffer_builder,
&fps_glyphs,
device_pixel_size,
&Point2D::new(FPS_PADDING, device_pixel_size.height as i32 - FPS_PADDING),
self.fps_gl_texture,
FPS_DISPLAY_POINT_SIZE,
&FPS_FOREGROUND_COLOR);
}
}
@ -396,7 +640,50 @@ struct GlyphPos {
glyph_id: u16,
}
static VERTEX_SHADER: &'static str = "\
fn create_program(vertex_shader_source: &str, fragment_shader_source: &str) -> GLuint {
unsafe {
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
gl::ShaderSource(vertex_shader,
1,
&(vertex_shader_source.as_ptr() as *const u8 as *const GLchar),
&(vertex_shader_source.len() as GLint));
gl::ShaderSource(fragment_shader,
1,
&(fragment_shader_source.as_ptr() as *const u8 as *const GLchar),
&(fragment_shader_source.len() as GLint));
gl::CompileShader(vertex_shader);
gl::CompileShader(fragment_shader);
let program = gl::CreateProgram();
gl::AttachShader(program, vertex_shader);
gl::AttachShader(program, fragment_shader);
gl::LinkProgram(program);
program
}
}
fn create_texture(rasterizer: &Rasterizer, atlas_size: &Size2D<u32>) -> (Texture, GLuint) {
let compute_texture = rasterizer.device.create_texture(Format::R8,
buffer::Protection::WriteOnly,
&atlas_size).unwrap();
let mut gl_texture = 0;
unsafe {
gl::GenTextures(1, &mut gl_texture);
compute_texture.bind_to(&ExternalTexture::Gl(gl_texture)).unwrap();
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);
}
(compute_texture, gl_texture)
}
static COMPOSITE_VERTEX_SHADER: &'static str = "\
#version 330
uniform mat2 uTransform;
@ -413,21 +700,46 @@ void main() {
}
";
static FRAGMENT_SHADER: &'static str = "\
static COMPOSITE_FRAGMENT_SHADER: &'static str = "\
#version 330
uniform sampler2DRect uAtlas;
uniform vec4 uColor;
in vec2 vTexCoord;
out vec4 oFragColor;
void main() {
float value = 1.0f - texture(uAtlas, vTexCoord).r;
oFragColor = vec4(value, value, value, 1.0f);
float value = texture(uAtlas, vTexCoord).r;
oFragColor = vec4(uColor.rgb, uColor.a * value);
}
";
static SOLID_COLOR_VERTEX_SHADER: &'static str = "\
#version 330
in vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0f, 1.0f);
}
";
static SOLID_COLOR_FRAGMENT_SHADER: &'static str = "\
#version 330
uniform vec4 uColor;
out vec4 oFragColor;
void main() {
oFragColor = uColor;
}
";
static RECT_INDICES: [u16; 6] = [0, 1, 3, 1, 2, 3];
static TEXT: &'static str = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur scelerisque pellentesque risus quis vehicula. Ut sollicitudin aliquet diam, vel lobortis orci porta in. Sed eu nisi egestas odio tincidunt cursus eget ut lorem. Fusce lacinia ex nec lectus rutrum mollis. Donec in ultrices purus. Integer id suscipit magna. Suspendisse congue pulvinar neque id ultrices. Curabitur nec tellus et est pellentesque posuere. Duis ut metus euismod, feugiat arcu vitae, posuere libero. \
Curabitur nunc urna, rhoncus vitae scelerisque quis, viverra et odio. Suspendisse accumsan pretium mi, nec fringilla metus condimentum id. Duis dignissim quam eu felis lobortis, eget dignissim lectus fermentum. Nunc et massa id orci pellentesque rutrum. Nam imperdiet quam vel ligula efficitur ultricies vel eu tellus. Maecenas luctus risus a erat euismod ultricies. Pellentesque neque mauris, laoreet vitae finibus quis, molestie ut velit. Donec laoreet justo risus. In id mi sed odio placerat interdum ut vitae erat. Fusce quis mollis mauris, sit amet efficitur libero. \

View File

@ -98,7 +98,7 @@ void main() {
// so we're in the clear: the rasterizer will always discard the unshaded areas and render only
// the shaded ones.
float tessLevel = p0.x == p2.x ? 0.0f : (lineCount * 2.0f - 1.0f);
float tessLevel = min(p0.x == p2.x ? 0.0f : (lineCount * 2.0f - 1.0f), 31.0f);
gl_TessLevelInner[0] = tessLevel;
gl_TessLevelInner[1] = 1.0f;
gl_TessLevelOuter[0] = 1.0f;

View File

@ -10,12 +10,13 @@
use euclid::{Point2D, Rect, Size2D};
/// TODO(pcwalton): Track width of last shelf.
pub struct Atlas {
free_rects: Vec<Rect<u32>>,
available_width: u32,
shelf_height: u32,
shelf_count: u32,
/// The amount of horizontal space allocated in the last shelf.
width_of_last_shelf: u32,
}
impl Atlas {
@ -26,6 +27,7 @@ impl Atlas {
available_width: available_width,
shelf_height: shelf_height,
shelf_count: 0,
width_of_last_shelf: 0,
}
}
@ -46,7 +48,8 @@ impl Atlas {
// Make a new shelf.
chosen_rect = Rect::new(Point2D::new(0, self.shelf_height * self.shelf_count),
Size2D::new(self.available_width, self.shelf_height));
self.shelf_count += 1
self.shelf_count += 1;
self.width_of_last_shelf = 0
}
Some((index, rect)) => {
self.free_rects.swap_remove(index);
@ -70,6 +73,12 @@ impl Atlas {
self.free_rects.push(free_to_right);
}
// Update width of last shelf if necessary.
let on_last_shelf = chosen_rect.max_y() >= self.shelf_height * (self.shelf_count - 1);
if on_last_shelf && self.width_of_last_shelf < chosen_rect.max_x() {
self.width_of_last_shelf = chosen_rect.max_x()
}
Ok(chosen_rect.origin)
}
@ -82,6 +91,17 @@ impl Atlas {
pub fn shelf_height(&self) -> u32 {
self.shelf_height
}
#[inline]
pub fn shelf_columns(&self) -> u32 {
let full_shelf_count = if self.shelf_count == 0 {
0
} else {
self.shelf_count - 1
};
full_shelf_count * self.available_width + self.width_of_last_shelf
}
}
#[inline]

View File

@ -8,10 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use atlas::Atlas;
use batch::Batch;
use compute_shader::device::Device;
use compute_shader::event::Event;
use compute_shader::instance::{Instance, ShadingLanguage};
use compute_shader::profile_event::ProfileEvent;
use compute_shader::program::Program;
use compute_shader::queue::{Queue, Uniform};
use compute_shader::texture::Texture;
@ -47,16 +48,22 @@ pub struct Rasterizer {
draw_atlas_size_uniform: GLint,
draw_glyph_descriptors_uniform: GLuint,
draw_image_descriptors_uniform: GLuint,
draw_query: GLuint,
options: RasterizerOptions,
}
pub struct DrawAtlasProfilingEvents {
pub draw: GLuint,
pub accum: ProfileEvent,
}
impl Rasterizer {
pub fn new(instance: &Instance, device: Device, queue: Queue, options: RasterizerOptions)
-> Result<Rasterizer, ()> {
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;
let (mut draw_vertex_array, mut draw_query) = (0, 0);
unsafe {
draw_program = gl::CreateProgram();
@ -109,6 +116,8 @@ impl Rasterizer {
draw_image_descriptors_uniform =
gl::GetUniformBlockIndex(draw_program,
b"ubImageDescriptors\0".as_ptr() as *const GLchar);
gl::GenQueries(1, &mut draw_query)
}
// FIXME(pcwalton): Don't panic if this fails to compile; just return an error.
@ -129,18 +138,19 @@ impl Rasterizer {
draw_atlas_size_uniform: draw_atlas_size_uniform,
draw_glyph_descriptors_uniform: draw_glyph_descriptors_uniform,
draw_image_descriptors_uniform: draw_image_descriptors_uniform,
draw_query: draw_query,
options: options,
})
}
pub fn draw_atlas(&self,
atlas_rect: &Rect<u32>,
atlas_shelf_height: u32,
atlas: &Atlas,
glyph_buffers: &GlyphBuffers,
batch: &Batch,
coverage_buffer: &CoverageBuffer,
texture: &Texture)
-> Result<Event, ()> {
-> Result<DrawAtlasProfilingEvents, ()> {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, coverage_buffer.framebuffer);
gl::Viewport(0, 0, atlas_rect.size.width as GLint, atlas_rect.size.height as GLint);
@ -198,14 +208,15 @@ impl Rasterizer {
} else {
gl::PATCHES
};
// Now draw the glyph ranges.
debug_assert!(batch.counts.len() == batch.start_indices.len());
gl::BeginQuery(gl::TIME_ELAPSED, self.draw_query);
gl::MultiDrawElements(primitive,
batch.counts.as_ptr(),
gl::UNSIGNED_INT,
batch.start_indices.as_ptr() as *const *const GLvoid,
batch.counts.len() as GLsizei);
gl::EndQuery(gl::TIME_ELAPSED);
gl::Disable(gl::CULL_FACE);
gl::Disable(gl::BLEND);
@ -229,15 +240,18 @@ impl Rasterizer {
(0, Uniform::Texture(texture)),
(1, Uniform::Texture(&coverage_buffer.texture)),
(2, Uniform::UVec4(atlas_rect_uniform)),
(3, Uniform::U32(atlas_shelf_height)),
(3, Uniform::U32(atlas.shelf_height())),
];
let accum_columns = atlas_rect.size.width * (atlas_rect.size.height / atlas_shelf_height);
self.queue.submit_compute(&self.accum_program,
&[accum_columns],
let accum_event = try!(self.queue.submit_compute(&self.accum_program,
&[atlas.shelf_columns()],
&accum_uniforms,
&[]).map_err(drop)
&[]).map_err(drop));
Ok(DrawAtlasProfilingEvents {
draw: self.draw_query,
accum: accum_event,
})
}
}

View File

@ -16,18 +16,23 @@
use glyph_range::GlyphRanges;
use otf::Font;
use std::cmp;
pub fn shape_text(font: &Font, glyph_ranges: &GlyphRanges, string: &str) -> Vec<GlyphPos> {
let mut advance = 0;
string.chars().map(|ch| {
let glyph_id = glyph_ranges.glyph_for(ch as u32).unwrap_or(0);
let advance = match font.hmtx.metrics_for_glyph(&font.hhea, glyph_id) {
Ok(metrics) => metrics.advance_width,
Err(_) => 0,
};
GlyphPos {
let metrics = font.hmtx.metrics_for_glyph(&font.hhea, glyph_id);
let pos = GlyphPos {
glyph_id: glyph_id,
advance: advance,
advance: cmp::max(0, advance) as u16,
};
if let Ok(ref metrics) = metrics {
advance = metrics.advance_width as i32
}
pos
}).collect()
}