Add basic postprocessing support

This commit is contained in:
Patrick Walton 2019-02-08 13:55:31 -08:00
parent 035baf656b
commit ba5a0f987c
7 changed files with 207 additions and 34 deletions

View File

@ -18,15 +18,17 @@ precision highp float;
uniform sampler2D uSource;
uniform sampler2D uGammaLUT;
uniform vec2 uFramebufferSize;
// Zero if no subpixel AA is to be performed.
uniform vec4 uKernel;
uniform vec4 uBGColor;
// Zero if no gamma correction is to be performed.
uniform vec4 uGammaCorrectionBGColor;
in vec2 vTexCoord;
out vec4 oFragColor;
float gammaCorrectChannel(float fgColor) {
return texture(uGammaLUT, vec2(fgColor, 1.0 - uBGColor)).r;
return texture(uGammaLUT, vec2(fgColor, 1.0 - uGammaCorrectionBGColor)).r;
}
// `fgColor` is in linear space.
@ -76,7 +78,7 @@ void main() {
}
// Apply gamma correction if necessary.
if (uBGColor.a > 0.0)
if (uGammaCorrectionBGColor.a > 0.0)
fgColor = gammaCorrect(fgColor);
// Finish.

View File

@ -12,13 +12,11 @@
precision highp float;
uniform vec2 uFramebufferSize;
in vec2 aPosition;
out vec2 vTexCoord;
void main() {
vTexCoord = aPosition;
gl_Position = vec4(aPosition / uFramebufferSize * 2.0 - 1.0, 0.0, 1.0);
gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);
}

View File

@ -21,6 +21,8 @@ use pathfinder_gl::device::Texture;
use pathfinder_gl::renderer::Renderer;
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
use pathfinder_renderer::gpu_data::BuiltScene;
use pathfinder_renderer::paint::ColorU;
use pathfinder_renderer::post::DEFRINGING_KERNEL_CORE_GRAPHICS;
use pathfinder_renderer::scene::Scene;
use pathfinder_renderer::z_buffer::ZBuffer;
use pathfinder_svg::SceneExt;
@ -46,7 +48,7 @@ const MAIN_FRAMEBUFFER_HEIGHT: u32 = 800;
const MOUSELOOK_ROTATION_SPEED: f32 = 0.007;
const CAMERA_VELOCITY: f32 = 25.0;
const BACKGROUND_COLOR: f32 = 0.22;
const BACKGROUND_COLOR: ColorU = ColorU { r: 32, g: 32, b: 32, a: 255 };
const EFFECTS_WINDOW_WIDTH: i32 = 550;
const EFFECTS_WINDOW_HEIGHT: i32 = BUTTON_HEIGHT * 3 + PADDING * 4;
@ -129,7 +131,9 @@ fn main() {
let count = if frame_counter == 0 { 2 } else { 1 };
for _ in 0..count {
scene_thread_proxy.sender.send(MainToSceneMsg::Build(perspective)).unwrap();
scene_thread_proxy.sender.send(MainToSceneMsg::Build(BuildOptions {
perspective
})).unwrap();
}
// FIXME(pcwalton): This can cause us to miss UI events if things get backed up...
@ -204,8 +208,24 @@ fn main() {
tile_time
} = scene_thread_proxy.receiver.recv().unwrap();
unsafe {
gl::ClearColor(BACKGROUND_COLOR, BACKGROUND_COLOR, BACKGROUND_COLOR, 1.0);
gl::ClearColor(BACKGROUND_COLOR.r as f32 / 255.0,
BACKGROUND_COLOR.g as f32 / 255.0,
BACKGROUND_COLOR.b as f32 / 255.0,
BACKGROUND_COLOR.a as f32 / 255.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
if demo_ui.gamma_correction_effect_enabled {
renderer.enable_gamma_correction(BACKGROUND_COLOR);
} else {
renderer.disable_gamma_correction();
}
if demo_ui.subpixel_aa_effect_enabled {
renderer.enable_subpixel_aa(&DEFRINGING_KERNEL_CORE_GRAPHICS);
} else {
renderer.disable_subpixel_aa();
}
renderer.render_scene(&built_scene);
let rendering_time = renderer.shift_timer_query();
@ -268,9 +288,9 @@ impl SceneThread {
RectF32::new(Point2DF32::default(),
Point2DF32::new(size.width as f32, size.height as f32));
}
MainToSceneMsg::Build(perspective) => {
MainToSceneMsg::Build(build_options) => {
let start_time = Instant::now();
let built_scene = build_scene(&self.scene, perspective, &self.options);
let built_scene = build_scene(&self.scene, build_options, self.options.jobs);
let tile_time = Instant::now() - start_time;
self.sender.send(SceneToMainMsg::Render { built_scene, tile_time }).unwrap();
}
@ -281,7 +301,11 @@ impl SceneThread {
enum MainToSceneMsg {
SetDrawableSize(Size2D<u32>),
Build(Option<Perspective>),
Build(BuildOptions),
}
struct BuildOptions {
perspective: Option<Perspective>,
}
enum SceneToMainMsg {
@ -344,11 +368,11 @@ fn load_scene(options: &Options) -> Scene {
scene
}
fn build_scene(scene: &Scene, perspective: Option<Perspective>, options: &Options) -> BuiltScene {
fn build_scene(scene: &Scene, build_options: BuildOptions, jobs: Option<usize>) -> BuiltScene {
let z_buffer = ZBuffer::new(scene.view_box);
let render_options = RenderOptions {
transform: match perspective {
transform: match build_options.perspective {
None => RenderTransform::Transform2D(Transform2DF32::default()),
Some(perspective) => RenderTransform::Perspective(perspective),
},
@ -356,7 +380,7 @@ fn build_scene(scene: &Scene, perspective: Option<Perspective>, options: &Option
};
let built_objects = panic::catch_unwind(|| {
match options.jobs {
match jobs {
Some(1) => scene.build_objects_sequentially(render_options, &z_buffer),
_ => scene.build_objects(render_options, &z_buffer),
}

View File

@ -70,8 +70,7 @@ pub struct Framebuffer {
}
impl Framebuffer {
pub fn new(size: &Size2D<u32>) -> Framebuffer {
let texture = Texture::new_r16f(size);
pub fn new(texture: Texture) -> Framebuffer {
let mut gl_framebuffer = 0;
unsafe {
gl::GenFramebuffers(1, &mut gl_framebuffer);
@ -271,7 +270,7 @@ pub struct Texture {
}
impl Texture {
fn new_r16f(size: &Size2D<u32>) -> Texture {
pub fn new_r16f(size: &Size2D<u32>) -> Texture {
let mut texture = Texture { gl_texture: 0, size: *size };
unsafe {
gl::GenTextures(1, &mut texture.gl_texture);

View File

@ -14,9 +14,11 @@ use crate::device::{TimerQuery, Uniform, VertexAttr};
use euclid::Size2D;
use gl::types::{GLfloat, GLint, GLuint};
use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
use pathfinder_renderer::paint::ObjectShader;
use pathfinder_renderer::paint::{ColorU, ObjectShader};
use pathfinder_renderer::post::DefringingKernel;
use pathfinder_renderer::tiles::{TILE_HEIGHT, TILE_WIDTH};
use std::collections::VecDeque;
use std::ptr;
use std::time::Duration;
static QUAD_VERTEX_POSITIONS: [u8; 8] = [0, 0, 1, 0, 1, 1, 0, 1];
@ -47,6 +49,7 @@ pub struct Renderer {
fill_colors_texture: Texture,
// Postprocessing shader
postprocess_source_framebuffer: Option<Framebuffer>,
postprocess_program: PostprocessProgram,
postprocess_vertex_array: PostprocessVertexArray,
gamma_lut_texture: Texture,
@ -58,6 +61,7 @@ pub struct Renderer {
// Extra info
main_framebuffer_size: Size2D<u32>,
postprocess_options: PostprocessOptions,
}
impl Renderer {
@ -85,8 +89,9 @@ impl Renderer {
let postprocess_vertex_array = PostprocessVertexArray::new(&postprocess_program,
&quad_vertex_positions_buffer);
let mask_framebuffer = Framebuffer::new(&Size2D::new(MASK_FRAMEBUFFER_WIDTH,
let mask_framebuffer_texture = Texture::new_r16f(&Size2D::new(MASK_FRAMEBUFFER_WIDTH,
MASK_FRAMEBUFFER_HEIGHT));
let mask_framebuffer = Framebuffer::new(mask_framebuffer_texture);
let fill_colors_texture = Texture::new_rgba(&Size2D::new(FILL_COLORS_TEXTURE_WIDTH,
FILL_COLORS_TEXTURE_HEIGHT));
@ -105,6 +110,7 @@ impl Renderer {
mask_framebuffer,
fill_colors_texture,
postprocess_source_framebuffer: None,
postprocess_program,
postprocess_vertex_array,
gamma_lut_texture,
@ -115,10 +121,13 @@ impl Renderer {
debug_ui,
main_framebuffer_size: *main_framebuffer_size,
postprocess_options: PostprocessOptions::default(),
}
}
pub fn render_scene(&mut self, built_scene: &BuiltScene) {
self.init_postprocessing_framebuffer();
let timer_query = self.free_timer_queries.pop().unwrap_or_else(|| TimerQuery::new());
timer_query.begin();
@ -133,6 +142,10 @@ impl Renderer {
self.draw_batch_mask_tiles(batch);
}
if self.postprocessing_needed() {
self.postprocess();
}
timer_query.end();
self.pending_timer_queries.push_back(timer_query);
}
@ -148,11 +161,32 @@ impl Renderer {
Some(result)
}
#[inline]
pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: &Size2D<u32>) {
self.main_framebuffer_size = *new_framebuffer_size;
self.debug_ui.set_framebuffer_size(new_framebuffer_size);
}
#[inline]
pub fn disable_subpixel_aa(&mut self) {
self.postprocess_options.defringing_kernel = None;
}
#[inline]
pub fn enable_subpixel_aa(&mut self, defringing_kernel: &DefringingKernel) {
self.postprocess_options.defringing_kernel = Some(*defringing_kernel);
}
#[inline]
pub fn disable_gamma_correction(&mut self) {
self.postprocess_options.gamma_correction_bg_color = None;
}
#[inline]
pub fn enable_gamma_correction(&mut self, bg_color: ColorU) {
self.postprocess_options.gamma_correction_bg_color = Some(bg_color);
}
fn upload_shaders(&mut self, shaders: &[ObjectShader]) {
let size = Size2D::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT);
let mut fill_colors = vec![0; size.width as usize * size.height as usize * 4];
@ -208,11 +242,8 @@ impl Renderer {
fn draw_batch_mask_tiles(&mut self, batch: &Batch) {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::Viewport(0,
0,
self.main_framebuffer_size.width as GLint,
self.main_framebuffer_size.height as GLint);
self.bind_draw_framebuffer();
self.set_main_viewport();
gl::BindVertexArray(self.mask_tile_vertex_array.gl_vertex_array);
gl::UseProgram(self.mask_tile_program.program.gl_program);
@ -244,11 +275,8 @@ impl Renderer {
fn draw_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::Viewport(0,
0,
self.main_framebuffer_size.width as GLint,
self.main_framebuffer_size.height as GLint);
self.bind_draw_framebuffer();
self.set_main_viewport();
gl::BindVertexArray(self.solid_tile_vertex_array.gl_vertex_array);
gl::UseProgram(self.solid_tile_program.program.gl_program);
@ -269,6 +297,100 @@ impl Renderer {
gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, solid_tiles.len() as GLint);
}
}
fn postprocess(&mut self) {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
self.set_main_viewport();
gl::BindVertexArray(self.postprocess_vertex_array.gl_vertex_array);
gl::UseProgram(self.postprocess_program.program.gl_program);
gl::Uniform2f(self.postprocess_program.framebuffer_size_uniform.location,
self.main_framebuffer_size.width as GLfloat,
self.main_framebuffer_size.height as GLfloat);
match self.postprocess_options.defringing_kernel {
Some(ref kernel) => {
debug_assert!(kernel.0.len() == 4);
let data: *const f32 = kernel.0.as_ptr();
gl::Uniform4fv(self.postprocess_program.kernel_uniform.location, 1, data);
}
None => {
gl::Uniform4f(self.postprocess_program.kernel_uniform.location,
0.0,
0.0,
0.0,
0.0);
}
}
self.postprocess_source_framebuffer.as_ref().unwrap().texture.bind(0);
gl::Uniform1i(self.postprocess_program.source_uniform.location, 0);
self.gamma_lut_texture.bind(1);
gl::Uniform1i(self.postprocess_program.gamma_lut_uniform.location, 1);
let gamma_correction_bg_color_uniform_location =
self.postprocess_program.gamma_correction_bg_color_uniform.location;
match self.postprocess_options.gamma_correction_bg_color {
None => {
gl::Uniform4f(gamma_correction_bg_color_uniform_location, 0.0, 0.0, 0.0, 0.0);
}
Some(color) => {
gl::Uniform4f(gamma_correction_bg_color_uniform_location,
color.r as f32 / 255.0,
color.g as f32 / 255.0,
color.b as f32 / 255.0,
color.a as f32 / 255.0);
}
}
gl::Disable(gl::BLEND);
gl::DrawArrays(gl::TRIANGLE_FAN, 0, 4);
}
}
fn bind_draw_framebuffer(&self) {
unsafe {
if self.postprocessing_needed() {
let fbo = self.postprocess_source_framebuffer.as_ref().unwrap().gl_framebuffer;
gl::BindFramebuffer(gl::FRAMEBUFFER, fbo);
} else {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
}
}
}
fn set_main_viewport(&self) {
unsafe {
gl::Viewport(0,
0,
self.main_framebuffer_size.width as GLint,
self.main_framebuffer_size.height as GLint);
}
}
fn init_postprocessing_framebuffer(&mut self) {
if !self.postprocessing_needed() {
self.postprocess_source_framebuffer = None;
return;
}
if let Some(ref existing_framebuffer) = self.postprocess_source_framebuffer {
if existing_framebuffer.texture.size == self.main_framebuffer_size {
return;
}
}
self.postprocess_source_framebuffer =
Some(Framebuffer::new(Texture::new_rgba(&self.main_framebuffer_size)));
}
fn postprocessing_needed(&self) -> bool {
self.postprocess_options.defringing_kernel.is_some() ||
self.postprocess_options.gamma_correction_bg_color.is_some()
}
}
#[derive(Clone, Copy, Default)]
struct PostprocessOptions {
defringing_kernel: Option<DefringingKernel>,
gamma_correction_bg_color: Option<ColorU>,
}
struct FillVertexArray {
@ -481,7 +603,7 @@ struct PostprocessProgram {
framebuffer_size_uniform: Uniform,
kernel_uniform: Uniform,
gamma_lut_uniform: Uniform,
bg_color_uniform: Uniform,
gamma_correction_bg_color_uniform: Uniform,
}
impl PostprocessProgram {
@ -491,14 +613,14 @@ impl PostprocessProgram {
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
let kernel_uniform = Uniform::new(&program, "Kernel");
let gamma_lut_uniform = Uniform::new(&program, "GammaLUT");
let bg_color_uniform = Uniform::new(&program, "BGColor");
let gamma_correction_bg_color_uniform = Uniform::new(&program, "GammaCorrectionBGColor");
PostprocessProgram {
program,
source_uniform,
framebuffer_size_uniform,
kernel_uniform,
gamma_lut_uniform,
bg_color_uniform,
gamma_correction_bg_color_uniform,
}
}
}

View File

@ -13,6 +13,7 @@
pub mod builder;
pub mod gpu_data;
pub mod paint;
pub mod post;
pub mod scene;
pub mod serialization;
pub mod tiles;

27
renderer/src/post.rs Normal file
View File

@ -0,0 +1,27 @@
// pathfinder/renderer/src/post.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Functionality related to postprocessing effects.
//!
//! Since these effects run on GPU as fragment shaders, this contains no
//! implementations, just shared declarations.
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct DefringingKernel(pub [f32; 4]);
/// This intentionally does not precisely match what Core Graphics does (a
/// Lanczos function), because we don't want any ringing artefacts.
pub static DEFRINGING_KERNEL_CORE_GRAPHICS: DefringingKernel = DefringingKernel([
0.033165660, 0.102074051, 0.221434336, 0.286651906
]);
pub static DEFRINGING_KERNEL_FREETYPE: DefringingKernel = DefringingKernel([
0.0, 0.031372549, 0.301960784, 0.337254902
]);