Composite render targets using tiles, taking the Z-buffer into account.

We can do more tile-based optimization, but this should be enough to ensure a
correct rendering.

This temporarily breaks subpixel AA, but it should be fixable by modifying
`blur.fs.glsl`.

Closes #271.
This commit is contained in:
Patrick Walton 2020-03-02 20:04:09 -08:00
parent 0f35d9c817
commit f607b607b0
27 changed files with 1036 additions and 380 deletions

3
Cargo.lock generated
View File

@ -242,6 +242,7 @@ dependencies = [
"pathfinder_gpu 0.1.0", "pathfinder_gpu 0.1.0",
"pathfinder_metal 0.1.0", "pathfinder_metal 0.1.0",
"pathfinder_renderer 0.1.0", "pathfinder_renderer 0.1.0",
"pathfinder_resources 0.1.0",
"sdl2 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -293,6 +294,7 @@ dependencies = [
"pathfinder_gl 0.1.0", "pathfinder_gl 0.1.0",
"pathfinder_gpu 0.1.0", "pathfinder_gpu 0.1.0",
"pathfinder_renderer 0.1.0", "pathfinder_renderer 0.1.0",
"pathfinder_resources 0.1.0",
"sdl2 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1819,6 +1821,7 @@ dependencies = [
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]

View File

@ -36,3 +36,6 @@ path = "../../metal"
[dependencies.pathfinder_renderer] [dependencies.pathfinder_renderer]
path = "../../renderer" path = "../../renderer"
[dependencies.pathfinder_resources]
path = "../../resources"

View File

@ -31,3 +31,6 @@ path = "../../gpu"
[dependencies.pathfinder_renderer] [dependencies.pathfinder_renderer]
path = "../../renderer" path = "../../renderer"
[dependencies.pathfinder_resources]
path = "../../resources"

View File

@ -12,6 +12,7 @@ rayon = "1.0"
serde = "1.0" serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
smallvec = "1.2" smallvec = "1.2"
vec_map = "0.8"
[dependencies.log] [dependencies.log]
version = "0.4" version = "0.4"

View File

@ -13,7 +13,8 @@
use crate::concurrent::executor::Executor; use crate::concurrent::executor::Executor;
use crate::gpu::renderer::{BlendModeProgram, MASK_TILES_ACROSS}; use crate::gpu::renderer::{BlendModeProgram, MASK_TILES_ACROSS};
use crate::gpu_data::{AlphaTile, AlphaTileBatch, AlphaTileVertex, FillBatchPrimitive, MaskTile}; use crate::gpu_data::{AlphaTile, AlphaTileBatch, AlphaTileVertex, FillBatchPrimitive, MaskTile};
use crate::gpu_data::{MaskTileVertex, PaintPageId, RenderCommand}; use crate::gpu_data::{MaskTileVertex, PaintPageId, RenderCommand, RenderTargetTile};
use crate::gpu_data::{RenderTargetTileBatch, RenderTargetTileVertex};
use crate::gpu_data::{SolidTileBatch, TileObjectPrimitive}; use crate::gpu_data::{SolidTileBatch, TileObjectPrimitive};
use crate::options::{PreparedBuildOptions, RenderCommandListener}; use crate::options::{PreparedBuildOptions, RenderCommandListener};
use crate::paint::{PaintInfo, PaintMetadata}; use crate::paint::{PaintInfo, PaintMetadata};
@ -21,7 +22,7 @@ use crate::scene::{DisplayItem, Scene};
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler, TilingPathInfo}; use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler, TilingPathInfo};
use crate::z_buffer::ZBuffer; use crate::z_buffer::ZBuffer;
use pathfinder_content::effects::{BlendMode, Effects}; use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::pattern::RenderTargetId; use pathfinder_content::pattern::RenderTargetId;
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8}; use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
@ -219,92 +220,110 @@ impl<'a> SceneBuilder<'a> {
// Process first Z-buffer. // Process first Z-buffer.
let first_z_buffer = remaining_layer_z_buffers.pop().unwrap(); let first_z_buffer = remaining_layer_z_buffers.pop().unwrap();
let first_solid_tiles = first_z_buffer.build_solid_tiles(&self.scene.paths, let first_solid_tiles = first_z_buffer.build_solid_tiles(paint_metadata);
paint_metadata);
for batch in first_solid_tiles.batches { for batch in first_solid_tiles.batches {
culled_tiles.display_list.push(CulledDisplayItem::DrawSolidTiles(batch)); culled_tiles.display_list.push(CulledDisplayItem::DrawSolidTiles(batch));
} }
let mut layer_z_buffers_stack = vec![first_z_buffer]; let mut layer_z_buffers_stack = vec![first_z_buffer];
let mut current_depth = 1;
for display_item in &self.scene.display_list { for display_item in &self.scene.display_list {
// Pass all commands except `DrawPaths` through. match *display_item {
let (start_draw_path_index, end_draw_path_index) = match *display_item {
DisplayItem::PushRenderTarget(render_target_id) => { DisplayItem::PushRenderTarget(render_target_id) => {
culled_tiles.display_list culled_tiles.display_list
.push(CulledDisplayItem::PushRenderTarget(render_target_id)); .push(CulledDisplayItem::PushRenderTarget(render_target_id));
let z_buffer = remaining_layer_z_buffers.pop().unwrap(); let z_buffer = remaining_layer_z_buffers.pop().unwrap();
let solid_tiles = z_buffer.build_solid_tiles(&self.scene.paths, let solid_tiles = z_buffer.build_solid_tiles(paint_metadata);
paint_metadata);
for batch in solid_tiles.batches { for batch in solid_tiles.batches {
culled_tiles.display_list.push(CulledDisplayItem::DrawSolidTiles(batch)); culled_tiles.display_list.push(CulledDisplayItem::DrawSolidTiles(batch));
} }
layer_z_buffers_stack.push(z_buffer); layer_z_buffers_stack.push(z_buffer);
continue;
} }
DisplayItem::PopRenderTarget => { DisplayItem::PopRenderTarget => {
culled_tiles.display_list.push(CulledDisplayItem::PopRenderTarget); culled_tiles.display_list.push(CulledDisplayItem::PopRenderTarget);
layer_z_buffers_stack.pop(); layer_z_buffers_stack.pop();
continue;
} }
DisplayItem::DrawRenderTarget { render_target, effects } => { DisplayItem::DrawRenderTarget { render_target, effects } => {
culled_tiles.display_list.push(CulledDisplayItem::DrawRenderTarget { let effective_view_box = self.scene.effective_view_box(self.built_options);
render_target, let tile_rect = tiles::round_rect_out_to_tile_bounds(effective_view_box);
effects, let layer_z_buffer = layer_z_buffers_stack.last().unwrap();
}); let mut tiles = vec![];
continue; for tile_y in tile_rect.min_y()..tile_rect.max_y() {
} for tile_x in tile_rect.min_x()..tile_rect.max_x() {
DisplayItem::DrawPaths { start_index, end_index } => (start_index, end_index), let tile_coords = Vector2I::new(tile_x, tile_y);
}; if layer_z_buffer.test(tile_coords, current_depth) {
tiles.push(RenderTargetTile::new(tile_coords));
for draw_path_index in start_draw_path_index..end_draw_path_index { }
let built_draw_path = &built_draw_paths[draw_path_index as usize]; }
culled_tiles.push_mask_tiles(&built_draw_path.path);
// Create a new `DrawAlphaTiles` display item if we don't have one or if we have to
// break a batch due to blend mode or paint page. Note that every path with a blend
// mode that requires a readable framebuffer needs its own batch.
//
// TODO(pcwalton): If we really wanted to, we could use tile maps to avoid batch
// breaks in some cases…
match culled_tiles.display_list.last() {
Some(&CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
tiles: _,
paint_page,
blend_mode,
sampling_flags
})) if paint_page == built_draw_path.paint_page &&
blend_mode == built_draw_path.blend_mode &&
sampling_flags == built_draw_path.sampling_flags &&
!BlendModeProgram::from_blend_mode(
blend_mode).needs_readable_framebuffer() => {}
_ => {
culled_tiles.display_list
.push(CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
tiles: vec![],
paint_page: built_draw_path.paint_page,
blend_mode: built_draw_path.blend_mode,
sampling_flags: built_draw_path.sampling_flags,
}))
} }
let batch = RenderTargetTileBatch { tiles, render_target, effects };
culled_tiles.display_list
.push(CulledDisplayItem::DrawRenderTargetTiles(batch));
current_depth += 1;
} }
// Fetch the destination alpha tiles buffer. DisplayItem::DrawPaths {
let culled_alpha_tiles = match *culled_tiles.display_list.last_mut().unwrap() { start_index: start_draw_path_index,
CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch { end_index: end_draw_path_index,
tiles: ref mut culled_alpha_tiles, } => {
.. for draw_path_index in start_draw_path_index..end_draw_path_index {
}) => culled_alpha_tiles, let built_draw_path = &built_draw_paths[draw_path_index as usize];
_ => unreachable!(), culled_tiles.push_mask_tiles(&built_draw_path.path);
};
let layer_z_buffer = layer_z_buffers_stack.last().unwrap(); // Create a new `DrawAlphaTiles` display item if we don't have one or if we
for alpha_tile in &built_draw_path.path.alpha_tiles { // have to break a batch due to blend mode or paint page. Note that every
let alpha_tile_coords = alpha_tile.upper_left.tile_position(); // path with a blend mode that requires a readable framebuffer needs its
if layer_z_buffer.test(alpha_tile_coords, // own batch.
alpha_tile.upper_left.object_index as u32) { //
culled_alpha_tiles.push(*alpha_tile); // TODO(pcwalton): If we really wanted to, we could use tile maps to avoid
// batch breaks in some cases…
match culled_tiles.display_list.last() {
Some(&CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
tiles: _,
paint_page,
blend_mode,
sampling_flags
})) if paint_page == built_draw_path.paint_page &&
blend_mode == built_draw_path.blend_mode &&
sampling_flags == built_draw_path.sampling_flags &&
!BlendModeProgram::from_blend_mode(
blend_mode).needs_readable_framebuffer() => {}
_ => {
let batch = AlphaTileBatch {
tiles: vec![],
paint_page: built_draw_path.paint_page,
blend_mode: built_draw_path.blend_mode,
sampling_flags: built_draw_path.sampling_flags,
};
culled_tiles.display_list
.push(CulledDisplayItem::DrawAlphaTiles(batch))
}
}
// Fetch the destination alpha tiles buffer.
let culled_alpha_tiles = match *culled_tiles.display_list
.last_mut()
.unwrap() {
CulledDisplayItem::DrawAlphaTiles(AlphaTileBatch {
tiles: ref mut culled_alpha_tiles,
..
}) => culled_alpha_tiles,
_ => unreachable!(),
};
let layer_z_buffer = layer_z_buffers_stack.last().unwrap();
for alpha_tile in &built_draw_path.path.alpha_tiles {
let alpha_tile_coords = alpha_tile.upper_left.tile_position();
if layer_z_buffer.test(alpha_tile_coords, current_depth) {
culled_alpha_tiles.push(*alpha_tile);
}
}
current_depth += 1;
} }
} }
} }
@ -317,6 +336,7 @@ impl<'a> SceneBuilder<'a> {
let effective_view_box = self.scene.effective_view_box(self.built_options); let effective_view_box = self.scene.effective_view_box(self.built_options);
let mut z_buffers = vec![ZBuffer::new(effective_view_box)]; let mut z_buffers = vec![ZBuffer::new(effective_view_box)];
let mut z_buffer_index_stack = vec![0]; let mut z_buffer_index_stack = vec![0];
let mut current_depth = 0;
// Create Z-buffers. // Create Z-buffers.
for display_item in &self.scene.display_list { for display_item in &self.scene.display_list {
@ -333,12 +353,16 @@ impl<'a> SceneBuilder<'a> {
let z_buffer = &mut z_buffers[*z_buffer_index_stack.last().unwrap()]; let z_buffer = &mut z_buffers[*z_buffer_index_stack.last().unwrap()];
for (path_subindex, built_draw_path) in for (path_subindex, built_draw_path) in
built_draw_paths[start_index..end_index].iter().enumerate() { built_draw_paths[start_index..end_index].iter().enumerate() {
z_buffer.update(&built_draw_path.path.solid_tiles, let solid_tiles = &built_draw_path.path.solid_tiles;
(path_subindex + start_index) as u32); let path_index = (path_subindex + start_index) as u32;
let paint = self.scene.paths[path_index as usize].paint();
z_buffer.update(solid_tiles, current_depth, paint);
current_depth += 1;
} }
} }
DisplayItem::DrawRenderTarget { .. } => { DisplayItem::DrawRenderTarget { .. } => {
// FIXME(pcwalton): Not great that this doesn't participate in Z-buffering! // FIXME(pcwalton): Not great that this doesn't participate in Z-buffering!
current_depth += 1;
} }
} }
} }
@ -369,8 +393,8 @@ impl<'a> SceneBuilder<'a> {
CulledDisplayItem::DrawAlphaTiles(batch) => { CulledDisplayItem::DrawAlphaTiles(batch) => {
self.listener.send(RenderCommand::DrawAlphaTiles(batch)) self.listener.send(RenderCommand::DrawAlphaTiles(batch))
} }
CulledDisplayItem::DrawRenderTarget { render_target, effects } => { CulledDisplayItem::DrawRenderTargetTiles(batch) => {
self.listener.send(RenderCommand::DrawRenderTarget { render_target, effects }) self.listener.send(RenderCommand::DrawRenderTargetTiles(batch))
} }
CulledDisplayItem::PushRenderTarget(render_target_id) => { CulledDisplayItem::PushRenderTarget(render_target_id) => {
self.listener.send(RenderCommand::PushRenderTarget(render_target_id)) self.listener.send(RenderCommand::PushRenderTarget(render_target_id))
@ -449,7 +473,7 @@ struct CulledTiles {
enum CulledDisplayItem { enum CulledDisplayItem {
DrawSolidTiles(SolidTileBatch), DrawSolidTiles(SolidTileBatch),
DrawAlphaTiles(AlphaTileBatch), DrawAlphaTiles(AlphaTileBatch),
DrawRenderTarget { render_target: RenderTargetId, effects: Effects }, DrawRenderTargetTiles(RenderTargetTileBatch),
PushRenderTarget(RenderTargetId), PushRenderTarget(RenderTargetId),
PopRenderTarget, PopRenderTarget,
} }
@ -774,3 +798,20 @@ impl CulledTiles {
} }
} }
} }
impl RenderTargetTile {
fn new(tile_coords: Vector2I) -> RenderTargetTile {
RenderTargetTile {
upper_left: RenderTargetTileVertex::new(tile_coords),
upper_right: RenderTargetTileVertex::new(tile_coords + Vector2I::new(1, 0)),
lower_left: RenderTargetTileVertex::new(tile_coords + Vector2I::new(0, 1)),
lower_right: RenderTargetTileVertex::new(tile_coords + Vector2I::splat(1)),
}
}
}
impl RenderTargetTileVertex {
fn new(tile_coords: Vector2I) -> RenderTargetTileVertex {
RenderTargetTileVertex { tile_x: tile_coords.x() as i16, tile_y: tile_coords.y() as i16 }
}
}

View File

@ -12,15 +12,14 @@ use crate::gpu::debug::DebugUIPresenter;
use crate::gpu::options::{DestFramebuffer, RendererOptions}; use crate::gpu::options::{DestFramebuffer, RendererOptions};
use crate::gpu::shaders::{AlphaTileBlendModeProgram, AlphaTileDodgeBurnProgram}; use crate::gpu::shaders::{AlphaTileBlendModeProgram, AlphaTileDodgeBurnProgram};
use crate::gpu::shaders::{AlphaTileHSLProgram, AlphaTileOverlayProgram}; use crate::gpu::shaders::{AlphaTileHSLProgram, AlphaTileOverlayProgram};
use crate::gpu::shaders::{AlphaTileProgram, AlphaTileVertexArray, CopyTileProgram}; use crate::gpu::shaders::{AlphaTileProgram, AlphaTileVertexArray, BlitProgram, BlitVertexArray, CopyTileProgram};
use crate::gpu::shaders::{CopyTileVertexArray, FillProgram, FillVertexArray, FilterBasicProgram}; use crate::gpu::shaders::{CopyTileVertexArray, FillProgram, FillVertexArray};
use crate::gpu::shaders::{FilterBasicVertexArray,FilterBlurProgram, FilterBlurVertexArray}; use crate::gpu::shaders::{MAX_FILLS_PER_BATCH};
use crate::gpu::shaders::{FilterTextProgram, FilterTextVertexArray, MAX_FILLS_PER_BATCH};
use crate::gpu::shaders::{MaskTileProgram, MaskTileVertexArray, ReprojectionProgram}; use crate::gpu::shaders::{MaskTileProgram, MaskTileVertexArray, ReprojectionProgram};
use crate::gpu::shaders::{ReprojectionVertexArray, SolidTileProgram, SolidTileVertexArray}; use crate::gpu::shaders::{ReprojectionVertexArray, SolidTileProgram, SolidTileVertexArray};
use crate::gpu::shaders::{StencilProgram, StencilVertexArray}; use crate::gpu::shaders::{StencilProgram, StencilVertexArray, TileFilterBasicProgram, TileFilterBlurProgram, TileFilterProgram, TileFilterTextProgram, TileFilterVertexArray};
use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData, PaintPageContents}; use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData, PaintPageContents};
use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileVertex}; use crate::gpu_data::{PaintPageId, RenderCommand, RenderTargetTile, SolidTileVertex};
use crate::options::BoundingQuad; use crate::options::BoundingQuad;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_color::{self as color, ColorF}; use pathfinder_color::{self as color, ColorF};
@ -78,6 +77,7 @@ where
// Core data // Core data
dest_framebuffer: DestFramebuffer<D>, dest_framebuffer: DestFramebuffer<D>,
options: RendererOptions, options: RendererOptions,
blit_program: BlitProgram<D>,
fill_program: FillProgram<D>, fill_program: FillProgram<D>,
mask_winding_tile_program: MaskTileProgram<D>, mask_winding_tile_program: MaskTileProgram<D>,
mask_evenodd_tile_program: MaskTileProgram<D>, mask_evenodd_tile_program: MaskTileProgram<D>,
@ -90,6 +90,7 @@ where
alpha_tile_difference_program: AlphaTileBlendModeProgram<D>, alpha_tile_difference_program: AlphaTileBlendModeProgram<D>,
alpha_tile_exclusion_program: AlphaTileBlendModeProgram<D>, alpha_tile_exclusion_program: AlphaTileBlendModeProgram<D>,
alpha_tile_hsl_program: AlphaTileHSLProgram<D>, alpha_tile_hsl_program: AlphaTileHSLProgram<D>,
blit_vertex_array: BlitVertexArray<D>,
mask_winding_tile_vertex_array: MaskTileVertexArray<D>, mask_winding_tile_vertex_array: MaskTileVertexArray<D>,
mask_evenodd_tile_vertex_array: MaskTileVertexArray<D>, mask_evenodd_tile_vertex_array: MaskTileVertexArray<D>,
copy_tile_vertex_array: CopyTileVertexArray<D>, copy_tile_vertex_array: CopyTileVertexArray<D>,
@ -122,12 +123,13 @@ where
clear_paint_texture: D::Texture, clear_paint_texture: D::Texture,
// Filter shaders // Filter shaders
filter_basic_program: FilterBasicProgram<D>, tile_filter_basic_program: TileFilterBasicProgram<D>,
filter_basic_vertex_array: FilterBasicVertexArray<D>, tile_filter_blur_program: TileFilterBlurProgram<D>,
filter_blur_program: FilterBlurProgram<D>, tile_filter_text_program: TileFilterTextProgram<D>,
filter_blur_vertex_array: FilterBlurVertexArray<D>, tile_filter_basic_vertex_array: TileFilterVertexArray<D>,
filter_text_program: FilterTextProgram<D>, tile_filter_blur_vertex_array: TileFilterVertexArray<D>,
filter_text_vertex_array: FilterTextVertexArray<D>, tile_filter_text_vertex_array: TileFilterVertexArray<D>,
tile_filter_vertex_buffer: D::Buffer,
gamma_lut_texture: D::Texture, gamma_lut_texture: D::Texture,
// Stencil shader // Stencil shader
@ -163,6 +165,7 @@ where
dest_framebuffer: DestFramebuffer<D>, dest_framebuffer: DestFramebuffer<D>,
options: RendererOptions) options: RendererOptions)
-> Renderer<D> { -> Renderer<D> {
let blit_program = BlitProgram::new(&device, resources);
let fill_program = FillProgram::new(&device, resources); let fill_program = FillProgram::new(&device, resources);
let mask_winding_tile_program = MaskTileProgram::new(FillRule::Winding, let mask_winding_tile_program = MaskTileProgram::new(FillRule::Winding,
&device, &device,
@ -184,9 +187,9 @@ where
resources, resources,
"tile_alpha_exclusion"); "tile_alpha_exclusion");
let alpha_tile_hsl_program = AlphaTileHSLProgram::new(&device, resources); let alpha_tile_hsl_program = AlphaTileHSLProgram::new(&device, resources);
let filter_basic_program = FilterBasicProgram::new(&device, resources); let tile_filter_basic_program = TileFilterBasicProgram::new(&device, resources);
let filter_blur_program = FilterBlurProgram::new(&device, resources); let tile_filter_blur_program = TileFilterBlurProgram::new(&device, resources);
let filter_text_program = FilterTextProgram::new(&device, resources); let tile_filter_text_program = TileFilterTextProgram::new(&device, resources);
let stencil_program = StencilProgram::new(&device, resources); let stencil_program = StencilProgram::new(&device, resources);
let reprojection_program = ReprojectionProgram::new(&device, resources); let reprojection_program = ReprojectionProgram::new(&device, resources);
@ -194,6 +197,7 @@ where
let gamma_lut_texture = device.create_texture_from_png(resources, "gamma-lut"); let gamma_lut_texture = device.create_texture_from_png(resources, "gamma-lut");
let alpha_tile_vertex_buffer = device.create_buffer(); let alpha_tile_vertex_buffer = device.create_buffer();
let tile_filter_vertex_buffer = device.create_buffer();
let quad_vertex_positions_buffer = device.create_buffer(); let quad_vertex_positions_buffer = device.create_buffer();
device.allocate_buffer( device.allocate_buffer(
&quad_vertex_positions_buffer, &quad_vertex_positions_buffer,
@ -210,6 +214,12 @@ where
); );
let quads_vertex_indices_buffer = device.create_buffer(); let quads_vertex_indices_buffer = device.create_buffer();
let blit_vertex_array = BlitVertexArray::new(
&device,
&blit_program,
&quad_vertex_positions_buffer,
&quad_vertex_indices_buffer,
);
let fill_vertex_array = FillVertexArray::new( let fill_vertex_array = FillVertexArray::new(
&device, &device,
&fill_program, &fill_program,
@ -279,23 +289,23 @@ where
&solid_tile_program, &solid_tile_program,
&quads_vertex_indices_buffer, &quads_vertex_indices_buffer,
); );
let filter_basic_vertex_array = FilterBasicVertexArray::new( let tile_filter_basic_vertex_array = TileFilterVertexArray::new(
&device, &device,
&filter_basic_program, &tile_filter_basic_program.tile_filter_program,
&quad_vertex_positions_buffer, &tile_filter_vertex_buffer,
&quad_vertex_indices_buffer, &quads_vertex_indices_buffer,
); );
let filter_blur_vertex_array = FilterBlurVertexArray::new( let tile_filter_blur_vertex_array = TileFilterVertexArray::new(
&device, &device,
&filter_blur_program, &tile_filter_blur_program.tile_filter_program,
&quad_vertex_positions_buffer, &tile_filter_vertex_buffer,
&quad_vertex_indices_buffer, &quads_vertex_indices_buffer,
); );
let filter_text_vertex_array = FilterTextVertexArray::new( let tile_filter_text_vertex_array = TileFilterVertexArray::new(
&device, &device,
&filter_text_program, &tile_filter_text_program.tile_filter_program,
&quad_vertex_positions_buffer, &tile_filter_vertex_buffer,
&quad_vertex_indices_buffer, &quads_vertex_indices_buffer,
); );
let stencil_vertex_array = StencilVertexArray::new(&device, &stencil_program); let stencil_vertex_array = StencilVertexArray::new(&device, &stencil_program);
let reprojection_vertex_array = ReprojectionVertexArray::new( let reprojection_vertex_array = ReprojectionVertexArray::new(
@ -335,6 +345,7 @@ where
dest_framebuffer, dest_framebuffer,
options, options,
blit_program,
fill_program, fill_program,
mask_winding_tile_program, mask_winding_tile_program,
mask_evenodd_tile_program, mask_evenodd_tile_program,
@ -347,6 +358,7 @@ where
alpha_tile_difference_program, alpha_tile_difference_program,
alpha_tile_exclusion_program, alpha_tile_exclusion_program,
alpha_tile_hsl_program, alpha_tile_hsl_program,
blit_vertex_array,
mask_winding_tile_vertex_array, mask_winding_tile_vertex_array,
mask_evenodd_tile_vertex_array, mask_evenodd_tile_vertex_array,
copy_tile_vertex_array, copy_tile_vertex_array,
@ -374,12 +386,13 @@ where
render_target_stack: vec![], render_target_stack: vec![],
clear_paint_texture, clear_paint_texture,
filter_basic_program, tile_filter_basic_program,
filter_basic_vertex_array, tile_filter_basic_vertex_array,
filter_blur_program, tile_filter_blur_program,
filter_blur_vertex_array, tile_filter_blur_vertex_array,
filter_text_program, tile_filter_text_program,
filter_text_vertex_array, tile_filter_text_vertex_array,
tile_filter_vertex_buffer,
gamma_lut_texture, gamma_lut_texture,
stencil_program, stencil_program,
@ -428,8 +441,10 @@ where
self.push_render_target(render_target_id) self.push_render_target(render_target_id)
} }
RenderCommand::PopRenderTarget => self.pop_render_target(), RenderCommand::PopRenderTarget => self.pop_render_target(),
RenderCommand::DrawRenderTarget { render_target, effects } => { RenderCommand::DrawRenderTargetTiles(ref batch) => {
self.draw_entire_render_target(render_target, effects) let count = batch.tiles.len();
self.upload_render_target_tiles(&batch.tiles);
self.draw_render_target_tiles(count as u32, batch.render_target, batch.effects)
} }
RenderCommand::DrawSolidTiles(ref batch) => { RenderCommand::DrawSolidTiles(ref batch) => {
let count = batch.vertices.len() / 4; let count = batch.vertices.len() / 4;
@ -630,6 +645,14 @@ where
self.ensure_index_buffer(alpha_tiles.len()); self.ensure_index_buffer(alpha_tiles.len());
} }
fn upload_render_target_tiles(&mut self, render_target_tiles: &[RenderTargetTile]) {
self.device.allocate_buffer(&self.tile_filter_vertex_buffer,
BufferData::Memory(&render_target_tiles),
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
self.ensure_index_buffer(render_target_tiles.len());
}
fn ensure_index_buffer(&mut self, mut length: usize) { fn ensure_index_buffer(&mut self, mut length: usize) {
length = length.next_power_of_two(); length = length.next_power_of_two();
if self.quads_vertex_indices_length >= length { if self.quads_vertex_indices_length >= length {
@ -1152,118 +1175,92 @@ where
} }
// FIXME(pcwalton): This is inefficient and should eventually go away. // FIXME(pcwalton): This is inefficient and should eventually go away.
fn draw_entire_render_target(&mut self, render_target_id: RenderTargetId, effects: Effects) { fn draw_render_target_tiles(&mut self,
count: u32,
render_target_id: RenderTargetId,
effects: Effects) {
match effects.filter { match effects.filter {
Filter::Composite(composite_op) => { Filter::Composite(composite_op) => {
self.composite_render_target(render_target_id, composite_op) self.composite_render_target(count, render_target_id, composite_op)
} }
Filter::Text { fg_color, bg_color, defringing_kernel, gamma_correction } => { Filter::Text { fg_color, bg_color, defringing_kernel, gamma_correction } => {
self.draw_text_render_target(render_target_id, self.draw_text_render_target(count,
render_target_id,
fg_color, fg_color,
bg_color, bg_color,
defringing_kernel, defringing_kernel,
gamma_correction) gamma_correction)
} }
Filter::Blur { direction, sigma } => { Filter::Blur { direction, sigma } => {
self.draw_blur_render_target(render_target_id, direction, sigma) self.draw_blur_render_target(count, render_target_id, direction, sigma)
} }
} }
self.preserve_draw_framebuffer(); self.preserve_draw_framebuffer();
} }
fn composite_render_target(&self, fn composite_render_target(&mut self,
tile_count: u32,
render_target_id: RenderTargetId, render_target_id: RenderTargetId,
composite_op: CompositeOp) { composite_op: CompositeOp) {
let clear_color = self.clear_color_for_draw_operation(); self.finish_drawing_render_target_tiles(&self.tile_filter_basic_program
let source_framebuffer = &self.render_targets[render_target_id.0 as usize].framebuffer; .tile_filter_program,
let source_texture = self.device.framebuffer_texture(source_framebuffer); &self.tile_filter_basic_vertex_array,
let main_viewport = self.main_viewport(); tile_count,
render_target_id,
vec![],
vec![],
composite_op.to_blend_state());
let uniforms = vec![ self.preserve_draw_framebuffer();
(&self.filter_basic_program.framebuffer_size_uniform,
UniformData::Vec2(main_viewport.size().to_f32().0)),
(&self.filter_basic_program.source_uniform, UniformData::TextureUnit(0)),
];
let blend_state = composite_op.to_blend_state();
self.device.draw_elements(6, &RenderState {
target: &self.draw_render_target(),
program: &self.filter_basic_program.program,
vertex_array: &self.filter_basic_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[&source_texture],
uniforms: &uniforms,
viewport: main_viewport,
options: RenderOptions {
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
blend: blend_state,
..RenderOptions::default()
},
});
} }
fn draw_text_render_target(&self, fn draw_text_render_target(&mut self,
tile_count: u32,
render_target_id: RenderTargetId, render_target_id: RenderTargetId,
fg_color: ColorF, fg_color: ColorF,
bg_color: ColorF, bg_color: ColorF,
defringing_kernel: Option<DefringingKernel>, defringing_kernel: Option<DefringingKernel>,
gamma_correction: bool) { gamma_correction: bool) {
let clear_color = self.clear_color_for_draw_operation(); let textures = vec![&self.gamma_lut_texture];
let source_framebuffer = &self.render_targets[render_target_id.0 as usize].framebuffer;
let source_texture = self.device.framebuffer_texture(source_framebuffer);
let source_texture_size = self.device.texture_size(source_texture);
let main_viewport = self.main_viewport();
let mut uniforms = vec![ let mut uniforms = vec![
(&self.filter_text_program.framebuffer_size_uniform, (&self.tile_filter_text_program.gamma_lut_uniform, UniformData::TextureUnit(0)),
UniformData::Vec2(main_viewport.size().to_f32().0)), (&self.tile_filter_text_program.fg_color_uniform, UniformData::Vec4(fg_color.0)),
(&self.filter_text_program.source_uniform, UniformData::TextureUnit(0)), (&self.tile_filter_text_program.bg_color_uniform, UniformData::Vec4(bg_color.0)),
(&self.filter_text_program.source_size_uniform, (&self.tile_filter_text_program.gamma_correction_enabled_uniform,
UniformData::Vec2(source_texture_size.0.to_f32x2())),
(&self.filter_text_program.gamma_lut_uniform, UniformData::TextureUnit(1)),
(&self.filter_text_program.fg_color_uniform, UniformData::Vec4(fg_color.0)),
(&self.filter_text_program.bg_color_uniform, UniformData::Vec4(bg_color.0)),
(&self.filter_text_program.gamma_correction_enabled_uniform,
UniformData::Int(gamma_correction as i32)), UniformData::Int(gamma_correction as i32)),
]; ];
match defringing_kernel { match defringing_kernel {
Some(ref kernel) => { Some(ref kernel) => {
uniforms.push((&self.filter_text_program.kernel_uniform, uniforms.push((&self.tile_filter_text_program.kernel_uniform,
UniformData::Vec4(F32x4::from_slice(&kernel.0)))); UniformData::Vec4(F32x4::from_slice(&kernel.0))));
} }
None => { None => {
uniforms.push((&self.filter_text_program.kernel_uniform, uniforms.push((&self.tile_filter_text_program.kernel_uniform,
UniformData::Vec4(F32x4::default()))); UniformData::Vec4(F32x4::default())));
} }
} }
self.device.draw_elements(6, &RenderState { self.finish_drawing_render_target_tiles(&self.tile_filter_text_program.tile_filter_program,
target: &self.draw_render_target(), &self.tile_filter_text_vertex_array,
program: &self.filter_text_program.program, tile_count,
vertex_array: &self.filter_text_vertex_array.vertex_array, render_target_id,
primitive: Primitive::Triangles, uniforms,
textures: &[&source_texture, &self.gamma_lut_texture], textures,
uniforms: &uniforms, None);
viewport: main_viewport,
options: RenderOptions { self.preserve_draw_framebuffer();
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
..RenderOptions::default()
},
});
} }
fn draw_blur_render_target(&self, fn draw_blur_render_target(&mut self,
tile_count: u32,
render_target_id: RenderTargetId, render_target_id: RenderTargetId,
direction: BlurDirection, direction: BlurDirection,
sigma: f32) { sigma: f32) {
let clear_color = self.clear_color_for_draw_operation();
let source_framebuffer = &self.render_targets[render_target_id.0 as usize].framebuffer; let source_framebuffer = &self.render_targets[render_target_id.0 as usize].framebuffer;
let source_texture = self.device.framebuffer_texture(source_framebuffer); let source_texture = self.device.framebuffer_texture(source_framebuffer);
let source_texture_size = self.device.texture_size(source_texture); let source_texture_size = self.device.texture_size(source_texture);
let main_viewport = self.main_viewport();
let sigma_inv = 1.0 / sigma; let sigma_inv = 1.0 / sigma;
let gauss_coeff_x = SQRT_2_PI_INV * sigma_inv; let gauss_coeff_x = SQRT_2_PI_INV * sigma_inv;
@ -1277,28 +1274,62 @@ where
let src_offset_scale = src_offset / source_texture_size.to_f32(); let src_offset_scale = src_offset / source_texture_size.to_f32();
let uniforms = vec![ let uniforms = vec![
(&self.filter_blur_program.framebuffer_size_uniform, (&self.tile_filter_blur_program.src_offset_scale_uniform,
UniformData::Vec2(main_viewport.size().to_f32().0)),
(&self.filter_blur_program.src_uniform, UniformData::TextureUnit(0)),
(&self.filter_blur_program.src_offset_scale_uniform,
UniformData::Vec2(src_offset_scale.0)), UniformData::Vec2(src_offset_scale.0)),
(&self.filter_blur_program.initial_gauss_coeff_uniform, (&self.tile_filter_blur_program.initial_gauss_coeff_uniform,
UniformData::Vec3([gauss_coeff_x, gauss_coeff_y, gauss_coeff_z])), UniformData::Vec3([gauss_coeff_x, gauss_coeff_y, gauss_coeff_z])),
(&self.filter_blur_program.support_uniform, (&self.tile_filter_blur_program.support_uniform,
UniformData::Int(f32::ceil(1.5 * sigma) as i32 * 2)), UniformData::Int(f32::ceil(1.5 * sigma) as i32 * 2)),
]; ];
self.device.draw_elements(6, &RenderState { self.finish_drawing_render_target_tiles(&self.tile_filter_blur_program.tile_filter_program,
&self.tile_filter_blur_vertex_array,
tile_count,
render_target_id,
uniforms,
vec![],
CompositeOp::SrcOver.to_blend_state());
self.preserve_draw_framebuffer();
}
fn finish_drawing_render_target_tiles<'a>(
&'a self,
tile_filter_program: &'a TileFilterProgram<D>,
tile_filter_vertex_array: &'a TileFilterVertexArray<D>,
tile_count: u32,
render_target_id: RenderTargetId,
mut uniforms: Vec<(&'a D::Uniform, UniformData)>,
mut textures: Vec<&'a D::Texture>,
blend_state: Option<BlendState>) {
let clear_color = self.clear_color_for_draw_operation();
let main_viewport = self.main_viewport();
let src_framebuffer = &self.render_targets[render_target_id.0 as usize].framebuffer;
let src_texture = self.device.framebuffer_texture(src_framebuffer);
let src_texture_size = self.device.texture_size(src_texture);
uniforms.extend_from_slice(&[
(&tile_filter_program.src_uniform, UniformData::TextureUnit(textures.len() as u32)),
(&tile_filter_program.src_size_uniform,
UniformData::Vec2(src_texture_size.0.to_f32x2())),
(&tile_filter_program.transform_uniform,
UniformData::Mat4(self.tile_transform().to_columns())),
(&tile_filter_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
]);
textures.push(src_texture);
self.device.draw_elements(6 * tile_count, &RenderState {
target: &self.draw_render_target(), target: &self.draw_render_target(),
program: &self.filter_blur_program.program, program: &tile_filter_program.program,
vertex_array: &self.filter_blur_vertex_array.vertex_array, vertex_array: &tile_filter_vertex_array.vertex_array,
primitive: Primitive::Triangles, primitive: Primitive::Triangles,
textures: &[&source_texture], textures: &textures,
uniforms: &uniforms, uniforms: &uniforms,
viewport: main_viewport, viewport: main_viewport,
options: RenderOptions { options: RenderOptions {
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
blend: CompositeOp::SrcOver.to_blend_state(), blend: blend_state,
..RenderOptions::default() ..RenderOptions::default()
}, },
}); });
@ -1311,13 +1342,13 @@ where
let main_viewport = self.main_viewport(); let main_viewport = self.main_viewport();
let uniforms = [(&self.filter_basic_program.source_uniform, UniformData::TextureUnit(0))]; let uniforms = [(&self.blit_program.src_uniform, UniformData::TextureUnit(0))];
let textures = [(self.device.framebuffer_texture(&self.intermediate_dest_framebuffer))]; let textures = [(self.device.framebuffer_texture(&self.intermediate_dest_framebuffer))];
self.device.draw_elements(6, &RenderState { self.device.draw_elements(6, &RenderState {
target: &RenderTarget::Default, target: &RenderTarget::Default,
program: &self.filter_basic_program.program, program: &self.blit_program.program,
vertex_array: &self.filter_basic_vertex_array.vertex_array, vertex_array: &self.blit_vertex_array.vertex_array,
primitive: Primitive::Triangles, primitive: Primitive::Triangles,
textures: &textures[..], textures: &textures[..],
uniforms: &uniforms[..], uniforms: &uniforms[..],

View File

@ -18,10 +18,40 @@ use pathfinder_resources::ResourceLoader;
const FILL_INSTANCE_SIZE: usize = 8; const FILL_INSTANCE_SIZE: usize = 8;
const SOLID_TILE_VERTEX_SIZE: usize = 16; const SOLID_TILE_VERTEX_SIZE: usize = 16;
const ALPHA_TILE_VERTEX_SIZE: usize = 20; const ALPHA_TILE_VERTEX_SIZE: usize = 20;
const FILTER_TILE_VERTEX_SIZE: usize = 4;
const MASK_TILE_VERTEX_SIZE: usize = 12; const MASK_TILE_VERTEX_SIZE: usize = 12;
pub const MAX_FILLS_PER_BATCH: usize = 0x4000; pub const MAX_FILLS_PER_BATCH: usize = 0x4000;
pub struct BlitVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray,
}
impl<D> BlitVertexArray<D> where D: Device {
pub fn new(device: &D,
blit_program: &BlitProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> BlitVertexArray<D> {
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&blit_program.program, "Position").unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
BlitVertexArray { vertex_array }
}
}
pub struct FillVertexArray<D> pub struct FillVertexArray<D>
where where
D: Device, D: Device,
@ -323,6 +353,19 @@ impl<D> CopyTileVertexArray<D> where D: Device {
} }
} }
pub struct BlitProgram<D> where D: Device {
pub program: D::Program,
pub src_uniform: D::Uniform,
}
impl<D> BlitProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> BlitProgram<D> {
let program = device.create_program(resources, "blit");
let src_uniform = device.get_uniform(&program, "Src");
BlitProgram { program, src_uniform }
}
}
pub struct FillProgram<D> pub struct FillProgram<D>
where where
D: Device, D: Device,
@ -523,79 +566,101 @@ impl<D> AlphaTileDodgeBurnProgram<D> where D: Device {
} }
} }
pub struct FilterBasicProgram<D> where D: Device { pub struct TileFilterProgram<D> where D: Device {
pub program: D::Program, pub program: D::Program,
pub source_uniform: D::Uniform, pub transform_uniform: D::Uniform,
pub framebuffer_size_uniform: D::Uniform, pub tile_size_uniform: D::Uniform,
pub src_uniform: D::Uniform,
pub src_size_uniform: D::Uniform,
} }
impl<D> FilterBasicProgram<D> where D: Device { impl<D> TileFilterProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> FilterBasicProgram<D> { pub fn new(device: &D,
resources: &dyn ResourceLoader,
program_name: &str,
fragment_shader_name: &str)
-> TileFilterProgram<D> {
let program = device.create_program_from_shader_names(resources, let program = device.create_program_from_shader_names(resources,
"filter_basic", program_name,
"filter", "tile_filter",
"filter_basic"); fragment_shader_name);
let source_uniform = device.get_uniform(&program, "Source"); let transform_uniform = device.get_uniform(&program, "Transform");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize");
FilterBasicProgram { program, source_uniform, framebuffer_size_uniform } let src_uniform = device.get_uniform(&program, "Src");
let src_size_uniform = device.get_uniform(&program, "SrcSize");
TileFilterProgram {
program,
transform_uniform,
tile_size_uniform,
src_uniform,
src_size_uniform,
}
} }
} }
pub struct FilterBasicVertexArray<D> where D: Device { pub struct TileFilterBasicProgram<D> where D: Device {
pub tile_filter_program: TileFilterProgram<D>,
}
impl<D> TileFilterBasicProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> TileFilterBasicProgram<D> {
TileFilterBasicProgram {
tile_filter_program: TileFilterProgram::new(device, resources, "filter_basic", "blit"),
}
}
}
pub struct TileFilterVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray, pub vertex_array: D::VertexArray,
} }
impl<D> FilterBasicVertexArray<D> where D: Device { impl<D> TileFilterVertexArray<D> where D: Device {
pub fn new( pub fn new(device: &D,
device: &D, tile_filter_program: &TileFilterProgram<D>,
fill_basic_program: &FilterBasicProgram<D>, tile_filter_vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer, quads_vertex_indices_buffer: &D::Buffer)
quad_vertex_indices_buffer: &D::Buffer, -> TileFilterVertexArray<D> {
) -> FilterBasicVertexArray<D> {
let vertex_array = device.create_vertex_array(); let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&fill_basic_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); let tile_position_attr =
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { device.get_vertex_attr(&tile_filter_program.program, "TilePosition").unwrap();
device.bind_buffer(&vertex_array, tile_filter_vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_position_attr, &VertexAttrDescriptor {
size: 2, size: 2,
class: VertexAttrClass::Int, class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16, attr_type: VertexAttrType::I16,
stride: 4, stride: FILTER_TILE_VERTEX_SIZE,
offset: 0, offset: 0,
divisor: 0, divisor: 0,
buffer_index: 0, buffer_index: 0,
}); });
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index);
FilterBasicVertexArray { vertex_array } TileFilterVertexArray { vertex_array }
} }
} }
pub struct FilterBlurProgram<D> where D: Device { pub struct TileFilterBlurProgram<D> where D: Device {
pub program: D::Program, pub tile_filter_program: TileFilterProgram<D>,
pub framebuffer_size_uniform: D::Uniform,
pub src_uniform: D::Uniform,
pub src_offset_scale_uniform: D::Uniform, pub src_offset_scale_uniform: D::Uniform,
pub initial_gauss_coeff_uniform: D::Uniform, pub initial_gauss_coeff_uniform: D::Uniform,
pub support_uniform: D::Uniform, pub support_uniform: D::Uniform,
} }
impl<D> FilterBlurProgram<D> where D: Device { impl<D> TileFilterBlurProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> FilterBlurProgram<D> { pub fn new(device: &D, resources: &dyn ResourceLoader) -> TileFilterBlurProgram<D> {
let program = device.create_program_from_shader_names(resources, let tile_filter_program = TileFilterProgram::new(device,
"filter_blur", resources,
"filter", "tile_filter_blur",
"filter_blur"); "tile_filter_blur");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let src_offset_scale_uniform = device.get_uniform(&tile_filter_program.program,
let src_uniform = device.get_uniform(&program, "Src"); "SrcOffsetScale");
let src_offset_scale_uniform = device.get_uniform(&program, "SrcOffsetScale"); let initial_gauss_coeff_uniform = device.get_uniform(&tile_filter_program.program,
let initial_gauss_coeff_uniform = device.get_uniform(&program, "InitialGaussCoeff"); "InitialGaussCoeff");
let support_uniform = device.get_uniform(&program, "Support"); let support_uniform = device.get_uniform(&tile_filter_program.program, "Support");
FilterBlurProgram { TileFilterBlurProgram {
program, tile_filter_program,
framebuffer_size_uniform,
src_uniform,
src_offset_scale_uniform, src_offset_scale_uniform,
initial_gauss_coeff_uniform, initial_gauss_coeff_uniform,
support_uniform, support_uniform,
@ -603,42 +668,8 @@ impl<D> FilterBlurProgram<D> where D: Device {
} }
} }
pub struct FilterBlurVertexArray<D> where D: Device { pub struct TileFilterTextProgram<D> where D: Device {
pub vertex_array: D::VertexArray, pub tile_filter_program: TileFilterProgram<D>,
}
impl<D> FilterBlurVertexArray<D> where D: Device {
pub fn new(
device: &D,
fill_blur_program: &FilterBlurProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> FilterBlurVertexArray<D> {
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&fill_blur_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
FilterBlurVertexArray { vertex_array }
}
}
pub struct FilterTextProgram<D> where D: Device {
pub program: D::Program,
pub source_uniform: D::Uniform,
pub source_size_uniform: D::Uniform,
pub framebuffer_size_uniform: D::Uniform,
pub kernel_uniform: D::Uniform, pub kernel_uniform: D::Uniform,
pub gamma_lut_uniform: D::Uniform, pub gamma_lut_uniform: D::Uniform,
pub gamma_correction_enabled_uniform: D::Uniform, pub gamma_correction_enabled_uniform: D::Uniform,
@ -646,26 +677,20 @@ pub struct FilterTextProgram<D> where D: Device {
pub bg_color_uniform: D::Uniform, pub bg_color_uniform: D::Uniform,
} }
impl<D> FilterTextProgram<D> where D: Device { impl<D> TileFilterTextProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> FilterTextProgram<D> { pub fn new(device: &D, resources: &dyn ResourceLoader) -> TileFilterTextProgram<D> {
let program = device.create_program_from_shader_names(resources, let tile_filter_program = TileFilterProgram::new(device,
"filter_text", resources,
"filter", "tile_filter_text",
"filter_text"); "tile_filter_text");
let source_uniform = device.get_uniform(&program, "Source"); let kernel_uniform = device.get_uniform(&tile_filter_program.program, "Kernel");
let source_size_uniform = device.get_uniform(&program, "SourceSize"); let gamma_lut_uniform = device.get_uniform(&tile_filter_program.program, "GammaLUT");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let gamma_correction_enabled_uniform = device.get_uniform(&tile_filter_program.program,
let kernel_uniform = device.get_uniform(&program, "Kernel");
let gamma_lut_uniform = device.get_uniform(&program, "GammaLUT");
let gamma_correction_enabled_uniform = device.get_uniform(&program,
"GammaCorrectionEnabled"); "GammaCorrectionEnabled");
let fg_color_uniform = device.get_uniform(&program, "FGColor"); let fg_color_uniform = device.get_uniform(&tile_filter_program.program, "FGColor");
let bg_color_uniform = device.get_uniform(&program, "BGColor"); let bg_color_uniform = device.get_uniform(&tile_filter_program.program, "BGColor");
FilterTextProgram { TileFilterTextProgram {
program, tile_filter_program,
source_uniform,
source_size_uniform,
framebuffer_size_uniform,
kernel_uniform, kernel_uniform,
gamma_lut_uniform, gamma_lut_uniform,
gamma_correction_enabled_uniform, gamma_correction_enabled_uniform,
@ -675,37 +700,6 @@ impl<D> FilterTextProgram<D> where D: Device {
} }
} }
pub struct FilterTextVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray,
}
impl<D> FilterTextVertexArray<D> where D: Device {
pub fn new(
device: &D,
fill_text_program: &FilterTextProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> FilterTextVertexArray<D> {
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&fill_text_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
FilterTextVertexArray { vertex_array }
}
}
pub struct StencilProgram<D> pub struct StencilProgram<D>
where where
D: Device, D: Device,

View File

@ -62,12 +62,12 @@ pub enum RenderCommand {
// Draws a batch of solid tiles to the render target on top of the stack. // Draws a batch of solid tiles to the render target on top of the stack.
DrawSolidTiles(SolidTileBatch), DrawSolidTiles(SolidTileBatch),
// Draws an entire render target to the render target on top of the stack. // Draws a batch of render target tiles to the render target on top of the stack.
// //
// FIXME(pcwalton): This draws the entire render target, so it's inefficient. We should get rid // FIXME(pcwalton): We should get rid of this command and transition all uses to
// of this command and transition all uses to `DrawAlphaTiles`/`DrawSolidTiles`. The reason it // `DrawAlphaTiles`/`DrawSolidTiles`. The reason it exists is that we don't have logic to
// exists is that we don't have logic to create tiles for blur bounding regions yet. // create tiles for blur bounding regions yet.
DrawRenderTarget { render_target: RenderTargetId, effects: Effects }, DrawRenderTargetTiles(RenderTargetTileBatch),
// Presents a rendered frame. // Presents a rendered frame.
Finish { build_time: Duration }, Finish { build_time: Duration },
@ -108,6 +108,13 @@ pub struct SolidTileBatch {
pub sampling_flags: TextureSamplingFlags, pub sampling_flags: TextureSamplingFlags,
} }
#[derive(Clone, Debug)]
pub struct RenderTargetTileBatch {
pub tiles: Vec<RenderTargetTile>,
pub render_target: RenderTargetId,
pub effects: Effects,
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct FillObjectPrimitive { pub struct FillObjectPrimitive {
pub px: LineSegmentU4, pub px: LineSegmentU4,
@ -162,6 +169,15 @@ pub struct AlphaTile {
pub lower_right: AlphaTileVertex, pub lower_right: AlphaTileVertex,
} }
#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct RenderTargetTile {
pub upper_left: RenderTargetTileVertex,
pub upper_right: RenderTargetTileVertex,
pub lower_left: RenderTargetTileVertex,
pub lower_right: RenderTargetTileVertex,
}
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
#[repr(C)] #[repr(C)]
pub struct MaskTileVertex { pub struct MaskTileVertex {
@ -187,6 +203,13 @@ pub struct AlphaTileVertex {
pub pad: u8, pub pad: u8,
} }
#[derive(Clone, Copy, Debug, Default)]
#[repr(C)]
pub struct RenderTargetTileVertex {
pub tile_x: i16,
pub tile_y: i16,
}
impl Debug for RenderCommand { impl Debug for RenderCommand {
fn fmt(&self, formatter: &mut Formatter) -> DebugResult { fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
match *self { match *self {
@ -203,9 +226,6 @@ impl Debug for RenderCommand {
write!(formatter, "PushRenderTarget({:?})", render_target_id) write!(formatter, "PushRenderTarget({:?})", render_target_id)
} }
RenderCommand::PopRenderTarget => write!(formatter, "PopRenderTarget"), RenderCommand::PopRenderTarget => write!(formatter, "PopRenderTarget"),
RenderCommand::DrawRenderTarget { render_target, .. } => {
write!(formatter, "DrawRenderTarget({:?})", render_target)
}
RenderCommand::DrawAlphaTiles(ref batch) => { RenderCommand::DrawAlphaTiles(ref batch) => {
write!(formatter, write!(formatter,
"DrawAlphaTiles(x{}, {:?}, {:?}, {:?})", "DrawAlphaTiles(x{}, {:?}, {:?}, {:?})",
@ -221,6 +241,12 @@ impl Debug for RenderCommand {
batch.paint_page, batch.paint_page,
batch.sampling_flags) batch.sampling_flags)
} }
RenderCommand::DrawRenderTargetTiles(ref batch) => {
write!(formatter,
"DrawRenderTarget(x{}, {:?})",
batch.tiles.len(),
batch.render_target)
}
RenderCommand::Finish { .. } => write!(formatter, "Finish"), RenderCommand::Finish { .. } => write!(formatter, "Finish"),
} }
} }

View File

@ -12,15 +12,16 @@
use crate::builder::SolidTile; use crate::builder::SolidTile;
use crate::gpu_data::{SolidTileBatch, SolidTileVertex}; use crate::gpu_data::{SolidTileBatch, SolidTileVertex};
use crate::paint::PaintMetadata; use crate::paint::{PaintId, PaintMetadata};
use crate::scene::DrawPath;
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use crate::tiles; use crate::tiles;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
use vec_map::VecMap;
pub(crate) struct ZBuffer { pub(crate) struct ZBuffer {
buffer: DenseTileMap<u32>, buffer: DenseTileMap<u32>,
depth_to_paint_id: VecMap<PaintId>,
} }
pub(crate) struct SolidTiles { pub(crate) struct SolidTiles {
@ -32,24 +33,25 @@ impl ZBuffer {
let tile_rect = tiles::round_rect_out_to_tile_bounds(view_box); let tile_rect = tiles::round_rect_out_to_tile_bounds(view_box);
ZBuffer { ZBuffer {
buffer: DenseTileMap::from_builder(|_| 0, tile_rect), buffer: DenseTileMap::from_builder(|_| 0, tile_rect),
depth_to_paint_id: VecMap::new(),
} }
} }
pub(crate) fn test(&self, coords: Vector2I, object_index: u32) -> bool { pub(crate) fn test(&self, coords: Vector2I, depth: u32) -> bool {
let tile_index = self.buffer.coords_to_index_unchecked(coords); let tile_index = self.buffer.coords_to_index_unchecked(coords);
self.buffer.data[tile_index as usize] < object_index + 1 self.buffer.data[tile_index as usize] < depth
} }
pub(crate) fn update(&mut self, solid_tiles: &[SolidTile], object_index: u32) { pub(crate) fn update(&mut self, solid_tiles: &[SolidTile], depth: u32, paint_id: PaintId) {
self.depth_to_paint_id.insert(depth as usize, paint_id);
for solid_tile in solid_tiles { for solid_tile in solid_tiles {
let tile_index = self.buffer.coords_to_index_unchecked(solid_tile.coords); let tile_index = self.buffer.coords_to_index_unchecked(solid_tile.coords);
let z_dest = &mut self.buffer.data[tile_index as usize]; let z_dest = &mut self.buffer.data[tile_index as usize];
*z_dest = u32::max(*z_dest, object_index + 1); *z_dest = u32::max(*z_dest, depth);
} }
} }
pub(crate) fn build_solid_tiles(&self, paths: &[DrawPath], paint_metadata: &[PaintMetadata]) pub(crate) fn build_solid_tiles(&self, paint_metadata: &[PaintMetadata]) -> SolidTiles {
-> SolidTiles {
let mut solid_tiles = SolidTiles { batches: vec![] }; let mut solid_tiles = SolidTiles { batches: vec![] };
for tile_index in 0..self.buffer.data.len() { for tile_index in 0..self.buffer.data.len() {
@ -59,13 +61,11 @@ impl ZBuffer {
} }
let tile_coords = self.buffer.index_to_coords(tile_index); let tile_coords = self.buffer.index_to_coords(tile_index);
let object_index = (depth - 1) as u32;
let paint_id = paths[object_index as usize].paint(); let paint_id = self.depth_to_paint_id[depth as usize];
let paint_metadata = &paint_metadata[paint_id.0 as usize]; let paint_metadata = &paint_metadata[paint_id.0 as usize];
let tile_position = tile_coords + self.buffer.rect.origin(); let tile_position = tile_coords + self.buffer.rect.origin();
let object_index = object_index as u16;
// Create a batch if necessary. // Create a batch if necessary.
match solid_tiles.batches.last() { match solid_tiles.batches.last() {
@ -83,16 +83,10 @@ impl ZBuffer {
let batch = solid_tiles.batches.last_mut().unwrap(); let batch = solid_tiles.batches.last_mut().unwrap();
batch.vertices.extend_from_slice(&[ batch.vertices.extend_from_slice(&[
SolidTileVertex::new(tile_position, object_index, paint_metadata), SolidTileVertex::new(tile_position, paint_metadata),
SolidTileVertex::new(tile_position + Vector2I::new(1, 0), SolidTileVertex::new(tile_position + Vector2I::new(1, 0), paint_metadata),
object_index, SolidTileVertex::new(tile_position + Vector2I::new(0, 1), paint_metadata),
paint_metadata), SolidTileVertex::new(tile_position + Vector2I::new(1, 1), paint_metadata),
SolidTileVertex::new(tile_position + Vector2I::new(0, 1),
object_index,
paint_metadata),
SolidTileVertex::new(tile_position + Vector2I::new(1, 1),
object_index,
paint_metadata),
]); ]);
} }
@ -101,13 +95,12 @@ impl ZBuffer {
} }
impl SolidTileVertex { impl SolidTileVertex {
fn new(tile_position: Vector2I, object_index: u16, paint_metadata: &PaintMetadata) fn new(tile_position: Vector2I, paint_metadata: &PaintMetadata) -> SolidTileVertex {
-> SolidTileVertex {
let color_uv = paint_metadata.calculate_tex_coords(tile_position); let color_uv = paint_metadata.calculate_tex_coords(tile_position);
SolidTileVertex { SolidTileVertex {
tile_x: tile_position.x() as i16, tile_x: tile_position.x() as i16,
tile_y: tile_position.y() as i16, tile_y: tile_position.y() as i16,
object_index: object_index, object_index: 0,
color_u: color_uv.x(), color_u: color_uv.x(),
color_v: color_uv.y(), color_v: color_uv.y(),
pad: 0, pad: 0,

View File

@ -0,0 +1,27 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
precision highp float;
uniform sampler2D uSrc;
in vec2 vTexCoord;
out vec4 oFragColor;
void main(){
vec4 color = texture(uSrc, vTexCoord);
oFragColor = vec4(color . rgb * color . a, color . a);
}

View File

@ -0,0 +1,25 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
precision highp float;
in vec2 aPosition;
out vec2 vTexCoord;
void main(){
vTexCoord = aPosition;
gl_Position = vec4(mix(aPosition, vec2(- 1.0), vec2(1.0)), 0.0, 1.0);
}

View File

@ -0,0 +1,31 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
precision highp float;
uniform mat4 uTransform;
uniform vec2 uTileSize;
uniform vec2 uSrcSize;
in ivec2 aTilePosition;
out vec2 vTexCoord;
void main(){
vec2 position = vec2(aTilePosition)* uTileSize;
vTexCoord = position / uSrcSize;
gl_Position = uTransform * vec4(position, 0.0, 1.0);
}

View File

@ -0,0 +1,69 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#extension GL_GOOGLE_include_directive : enable
precision highp float;
uniform sampler2D uSrc;
uniform vec2 uSrcOffsetScale;
uniform vec3 uInitialGaussCoeff;
uniform int uSupport;
in vec2 vTexCoord;
out vec4 oFragColor;
void main(){
vec3 gaussCoeff = uInitialGaussCoeff;
float gaussSum = gaussCoeff . x;
vec4 color = texture(uSrc, vTexCoord)* gaussCoeff . x;
gaussCoeff . xy *= gaussCoeff . yz;
for(int i = 1;i <= uSupport;i += 2){
float gaussPartialSum = gaussCoeff . x;
gaussCoeff . xy *= gaussCoeff . yz;
gaussPartialSum += gaussCoeff . x;
vec2 srcOffset = uSrcOffsetScale *(float(i)+ gaussCoeff . x / gaussPartialSum);
color +=(texture(uSrc, vTexCoord - srcOffset)+ texture(uSrc, vTexCoord + srcOffset))*
gaussPartialSum;
gaussSum += 2.0 * gaussPartialSum;
gaussCoeff . xy *= gaussCoeff . yz;
}
color /= gaussSum;
color . rgb *= color . a;
oFragColor = color;
}

View File

@ -0,0 +1,124 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#extension GL_GOOGLE_include_directive : enable
precision highp float;
uniform sampler2D uSrc;
uniform vec2 uSrcSize;
uniform vec4 uFGColor;
uniform vec4 uBGColor;
uniform int uGammaCorrectionEnabled;
in vec2 vTexCoord;
out vec4 oFragColor;
uniform sampler2D uGammaLUT;
float gammaCorrectChannel(float bgColor, float fgColor){
return texture(uGammaLUT, vec2(fgColor, 1.0 - bgColor)). r;
}
vec3 gammaCorrect(vec3 bgColor, vec3 fgColor){
return vec3(gammaCorrectChannel(bgColor . r, fgColor . r),
gammaCorrectChannel(bgColor . g, fgColor . g),
gammaCorrectChannel(bgColor . b, fgColor . b));
}
uniform vec4 uKernel;
float sample1Tap(float offset);
void sample9Tap(out vec4 outAlphaLeft,
out float outAlphaCenter,
out vec4 outAlphaRight,
float onePixel){
outAlphaLeft = vec4(uKernel . x > 0.0 ? sample1Tap(- 4.0 * onePixel): 0.0,
sample1Tap(- 3.0 * onePixel),
sample1Tap(- 2.0 * onePixel),
sample1Tap(- 1.0 * onePixel));
outAlphaCenter = sample1Tap(0.0);
outAlphaRight = vec4(sample1Tap(1.0 * onePixel),
sample1Tap(2.0 * onePixel),
sample1Tap(3.0 * onePixel),
uKernel . x > 0.0 ? sample1Tap(4.0 * onePixel): 0.0);
}
float convolve7Tap(vec4 alpha0, vec3 alpha1){
return dot(alpha0, uKernel)+ dot(alpha1, uKernel . zyx);
}
float sample1Tap(float offset){
return texture(uSrc, vec2(vTexCoord . x + offset, vTexCoord . y)). r;
}
void main(){
vec3 alpha;
if(uKernel . w == 0.0){
alpha = texture(uSrc, vTexCoord). rrr;
} else {
vec4 alphaLeft, alphaRight;
float alphaCenter;
sample9Tap(alphaLeft, alphaCenter, alphaRight, 1.0 / uSrcSize . x);
float r = convolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight . xy));
float g = convolve7Tap(vec4(alphaLeft . yzw, alphaCenter), alphaRight . xyz);
float b = convolve7Tap(vec4(alphaLeft . zw, alphaCenter, alphaRight . x), alphaRight . yzw);
alpha = vec3(r, g, b);
}
if(uGammaCorrectionEnabled != 0)
alpha = gammaCorrect(uBGColor . rgb, alpha);
oFragColor = vec4(mix(uBGColor . rgb, uFGColor . rgb, alpha), 1.0);
}

View File

@ -0,0 +1,30 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct spvDescriptorSetBuffer0
{
texture2d<float> uSrc [[id(0)]];
sampler uSrcSmplr [[id(1)]];
};
struct main0_out
{
float4 oFragColor [[color(0)]];
};
struct main0_in
{
float2 vTexCoord [[user(locn0)]];
};
fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{
main0_out out = {};
float4 color = spvDescriptorSet0.uSrc.sample(spvDescriptorSet0.uSrcSmplr, in.vTexCoord);
out.oFragColor = float4(color.xyz * color.w, color.w);
return out;
}

View File

@ -0,0 +1,25 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float2 vTexCoord [[user(locn0)]];
float4 gl_Position [[position]];
};
struct main0_in
{
float2 aPosition [[attribute(0)]];
};
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
out.vTexCoord = in.aPosition;
out.gl_Position = float4(mix(in.aPosition, float2(-1.0), float2(1.0)), 0.0, 1.0);
return out;
}

View File

@ -0,0 +1,33 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct spvDescriptorSetBuffer0
{
constant float2* uTileSize [[id(0)]];
constant float2* uSrcSize [[id(1)]];
constant float4x4* uTransform [[id(2)]];
};
struct main0_out
{
float2 vTexCoord [[user(locn0)]];
float4 gl_Position [[position]];
};
struct main0_in
{
int2 aTilePosition [[attribute(0)]];
};
vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{
main0_out out = {};
float2 position = float2(in.aTilePosition) * (*spvDescriptorSet0.uTileSize);
out.vTexCoord = position / (*spvDescriptorSet0.uSrcSize);
out.gl_Position = (*spvDescriptorSet0.uTransform) * float4(position, 0.0, 1.0);
return out;
}

View File

@ -0,0 +1,52 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct spvDescriptorSetBuffer0
{
constant float3* uInitialGaussCoeff [[id(0)]];
texture2d<float> uSrc [[id(1)]];
sampler uSrcSmplr [[id(2)]];
constant int* uSupport [[id(3)]];
constant float2* uSrcOffsetScale [[id(4)]];
};
struct main0_out
{
float4 oFragColor [[color(0)]];
};
struct main0_in
{
float2 vTexCoord [[user(locn0)]];
};
fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{
main0_out out = {};
float3 gaussCoeff = (*spvDescriptorSet0.uInitialGaussCoeff);
float gaussSum = gaussCoeff.x;
float4 color = spvDescriptorSet0.uSrc.sample(spvDescriptorSet0.uSrcSmplr, in.vTexCoord) * gaussCoeff.x;
float2 _39 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_39.x, _39.y, gaussCoeff.z);
for (int i = 1; i <= (*spvDescriptorSet0.uSupport); i += 2)
{
float gaussPartialSum = gaussCoeff.x;
float2 _64 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_64.x, _64.y, gaussCoeff.z);
gaussPartialSum += gaussCoeff.x;
float2 srcOffset = (*spvDescriptorSet0.uSrcOffsetScale) * (float(i) + (gaussCoeff.x / gaussPartialSum));
color += ((spvDescriptorSet0.uSrc.sample(spvDescriptorSet0.uSrcSmplr, (in.vTexCoord - srcOffset)) + spvDescriptorSet0.uSrc.sample(spvDescriptorSet0.uSrcSmplr, (in.vTexCoord + srcOffset))) * gaussPartialSum);
gaussSum += (2.0 * gaussPartialSum);
float2 _108 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_108.x, _108.y, gaussCoeff.z);
}
color /= float4(gaussSum);
float3 _123 = color.xyz * color.w;
color = float4(_123.x, _123.y, _123.z, color.w);
out.oFragColor = color;
return out;
}

View File

@ -0,0 +1,130 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct spvDescriptorSetBuffer0
{
texture2d<float> uGammaLUT [[id(0)]];
sampler uGammaLUTSmplr [[id(1)]];
constant float4* uKernel [[id(2)]];
texture2d<float> uSrc [[id(3)]];
sampler uSrcSmplr [[id(4)]];
constant float2* uSrcSize [[id(5)]];
constant int* uGammaCorrectionEnabled [[id(6)]];
constant float4* uBGColor [[id(7)]];
constant float4* uFGColor [[id(8)]];
};
struct main0_out
{
float4 oFragColor [[color(0)]];
};
struct main0_in
{
float2 vTexCoord [[user(locn0)]];
};
float sample1Tap(thread const float& offset, thread texture2d<float> uSrc, thread const sampler uSrcSmplr, thread float2& vTexCoord)
{
return uSrc.sample(uSrcSmplr, float2(vTexCoord.x + offset, vTexCoord.y)).x;
}
void sample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCenter, thread float4& outAlphaRight, thread const float& onePixel, thread float4 uKernel, thread texture2d<float> uSrc, thread const sampler uSrcSmplr, thread float2& vTexCoord)
{
float _89;
if (uKernel.x > 0.0)
{
float param = (-4.0) * onePixel;
_89 = sample1Tap(param, uSrc, uSrcSmplr, vTexCoord);
}
else
{
_89 = 0.0;
}
float param_1 = (-3.0) * onePixel;
float param_2 = (-2.0) * onePixel;
float param_3 = (-1.0) * onePixel;
outAlphaLeft = float4(_89, sample1Tap(param_1, uSrc, uSrcSmplr, vTexCoord), sample1Tap(param_2, uSrc, uSrcSmplr, vTexCoord), sample1Tap(param_3, uSrc, uSrcSmplr, vTexCoord));
float param_4 = 0.0;
outAlphaCenter = sample1Tap(param_4, uSrc, uSrcSmplr, vTexCoord);
float param_5 = 1.0 * onePixel;
float param_6 = 2.0 * onePixel;
float param_7 = 3.0 * onePixel;
float _134;
if (uKernel.x > 0.0)
{
float param_8 = 4.0 * onePixel;
_134 = sample1Tap(param_8, uSrc, uSrcSmplr, vTexCoord);
}
else
{
_134 = 0.0;
}
outAlphaRight = float4(sample1Tap(param_5, uSrc, uSrcSmplr, vTexCoord), sample1Tap(param_6, uSrc, uSrcSmplr, vTexCoord), sample1Tap(param_7, uSrc, uSrcSmplr, vTexCoord), _134);
}
float convolve7Tap(thread const float4& alpha0, thread const float3& alpha1, thread float4 uKernel)
{
return dot(alpha0, uKernel) + dot(alpha1, uKernel.zyx);
}
float gammaCorrectChannel(thread const float& bgColor, thread const float& fgColor, thread texture2d<float> uGammaLUT, thread const sampler uGammaLUTSmplr)
{
return uGammaLUT.sample(uGammaLUTSmplr, float2(fgColor, 1.0 - bgColor)).x;
}
float3 gammaCorrect(thread const float3& bgColor, thread const float3& fgColor, thread texture2d<float> uGammaLUT, thread const sampler uGammaLUTSmplr)
{
float param = bgColor.x;
float param_1 = fgColor.x;
float param_2 = bgColor.y;
float param_3 = fgColor.y;
float param_4 = bgColor.z;
float param_5 = fgColor.z;
return float3(gammaCorrectChannel(param, param_1, uGammaLUT, uGammaLUTSmplr), gammaCorrectChannel(param_2, param_3, uGammaLUT, uGammaLUTSmplr), gammaCorrectChannel(param_4, param_5, uGammaLUT, uGammaLUTSmplr));
}
fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{
main0_out out = {};
float3 alpha;
if ((*spvDescriptorSet0.uKernel).w == 0.0)
{
alpha = spvDescriptorSet0.uSrc.sample(spvDescriptorSet0.uSrcSmplr, in.vTexCoord).xxx;
}
else
{
float param_3 = 1.0 / (*spvDescriptorSet0.uSrcSize).x;
float4 param;
float param_1;
float4 param_2;
sample9Tap(param, param_1, param_2, param_3, (*spvDescriptorSet0.uKernel), spvDescriptorSet0.uSrc, spvDescriptorSet0.uSrcSmplr, in.vTexCoord);
float4 alphaLeft = param;
float alphaCenter = param_1;
float4 alphaRight = param_2;
float4 param_4 = alphaLeft;
float3 param_5 = float3(alphaCenter, alphaRight.xy);
float r = convolve7Tap(param_4, param_5, (*spvDescriptorSet0.uKernel));
float4 param_6 = float4(alphaLeft.yzw, alphaCenter);
float3 param_7 = alphaRight.xyz;
float g = convolve7Tap(param_6, param_7, (*spvDescriptorSet0.uKernel));
float4 param_8 = float4(alphaLeft.zw, alphaCenter, alphaRight.x);
float3 param_9 = alphaRight.yzw;
float b = convolve7Tap(param_8, param_9, (*spvDescriptorSet0.uKernel));
alpha = float3(r, g, b);
}
if ((*spvDescriptorSet0.uGammaCorrectionEnabled) != 0)
{
float3 param_10 = (*spvDescriptorSet0.uBGColor).xyz;
float3 param_11 = alpha;
alpha = gammaCorrect(param_10, param_11, spvDescriptorSet0.uGammaLUT, spvDescriptorSet0.uGammaLUTSmplr);
}
out.oFragColor = float4(mix((*spvDescriptorSet0.uBGColor).xyz, (*spvDescriptorSet0.uFGColor).xyz, alpha), 1.0);
return out;
}

View File

@ -3,6 +3,8 @@ TARGET_DIR?=../resources/shaders
EMPTY= EMPTY=
SHADERS=\ SHADERS=\
blit.fs.glsl \
blit.vs.glsl \
debug_solid.fs.glsl \ debug_solid.fs.glsl \
debug_solid.vs.glsl \ debug_solid.vs.glsl \
debug_texture.fs.glsl \ debug_texture.fs.glsl \
@ -11,10 +13,6 @@ SHADERS=\
demo_ground.vs.glsl \ demo_ground.vs.glsl \
fill.fs.glsl \ fill.fs.glsl \
fill.vs.glsl \ fill.vs.glsl \
filter.vs.glsl \
filter_basic.fs.glsl \
filter_blur.fs.glsl \
filter_text.fs.glsl \
mask.vs.glsl \ mask.vs.glsl \
mask_evenodd.fs.glsl \ mask_evenodd.fs.glsl \
mask_winding.fs.glsl \ mask_winding.fs.glsl \
@ -32,14 +30,17 @@ SHADERS=\
tile_alpha_softlight.fs.glsl \ tile_alpha_softlight.fs.glsl \
tile_copy.fs.glsl \ tile_copy.fs.glsl \
tile_copy.vs.glsl \ tile_copy.vs.glsl \
tile_filter.vs.glsl \
tile_filter_blur.fs.glsl \
tile_filter_text.fs.glsl \
tile_solid.fs.glsl \ tile_solid.fs.glsl \
tile_solid.vs.glsl \ tile_solid.vs.glsl \
$(EMPTY) $(EMPTY)
INCLUDES=\ INCLUDES=\
filter_text_convolve.inc.glsl \
filter_text_gamma_correct.inc.glsl \
tile_alpha_sample.inc.glsl \ tile_alpha_sample.inc.glsl \
tile_filter_text_convolve.inc.glsl \
tile_filter_text_gamma_correct.inc.glsl \
$(EMPTY) $(EMPTY)
OUT=\ OUT=\

View File

@ -1,6 +1,6 @@
#version 330 #version 330
// pathfinder/shaders/filter_basic.fs.glsl // pathfinder/shaders/blit.fs.glsl
// //
// Copyright © 2020 The Pathfinder Project Developers. // Copyright © 2020 The Pathfinder Project Developers.
// //
@ -10,20 +10,15 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// TODO(pcwalton): This could be significantly optimized by operating on a
// sparse per-tile basis.
#extension GL_GOOGLE_include_directive : enable
precision highp float; precision highp float;
uniform sampler2D uSource; uniform sampler2D uSrc;
in vec2 vTexCoord; in vec2 vTexCoord;
out vec4 oFragColor; out vec4 oFragColor;
void main() { void main() {
vec4 color = texture(uSource, vTexCoord); vec4 color = texture(uSrc, vTexCoord);
oFragColor = vec4(color.rgb * color.a, color.a); oFragColor = vec4(color.rgb * color.a, color.a);
} }

22
shaders/blit.vs.glsl Normal file
View File

@ -0,0 +1,22 @@
#version 330
// pathfinder/shaders/blit.vs.glsl
//
// Copyright © 2020 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.
precision highp float;
in vec2 aPosition;
out vec2 vTexCoord;
void main() {
vTexCoord = aPosition;
gl_Position = vec4(mix(aPosition, vec2(-1.0), vec2(1.0)), 0.0, 1.0);
}

View File

@ -1,6 +1,6 @@
#version 330 #version 330
// pathfinder/shaders/filter.vs.glsl // pathfinder/shaders/tile_filter.vs.glsl
// //
// Copyright © 2020 The Pathfinder Project Developers. // Copyright © 2020 The Pathfinder Project Developers.
// //
@ -12,17 +12,17 @@
precision highp float; precision highp float;
in ivec2 aPosition; uniform mat4 uTransform;
uniform vec2 uTileSize;
uniform vec2 uSrcSize;
in ivec2 aTilePosition;
out vec2 vTexCoord; out vec2 vTexCoord;
void main() { void main() {
vec2 position = vec2(aPosition); vec2 position = vec2(aTilePosition) * uTileSize;
vTexCoord = position;
#ifdef PF_ORIGIN_UPPER_LEFT vTexCoord = position / uSrcSize;
// FIXME(pcwalton): This is wrong. gl_Position = uTransform * vec4(position, 0.0, 1.0);
position.y = 1.0 - position.y;
#endif
gl_Position = vec4(vec2(position) * 2.0 - 1.0, 0.0, 1.0);
} }

View File

@ -1,6 +1,6 @@
#version 330 #version 330
// pathfinder/shaders/filter_text.fs.glsl // pathfinder/shaders/tile_filter_text.fs.glsl
// //
// Copyright © 2019 The Pathfinder Project Developers. // Copyright © 2019 The Pathfinder Project Developers.
// //
@ -10,15 +10,12 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// TODO(pcwalton): This could be significantly optimized by operating on a
// sparse per-tile basis.
#extension GL_GOOGLE_include_directive : enable #extension GL_GOOGLE_include_directive : enable
precision highp float; precision highp float;
uniform sampler2D uSource; uniform sampler2D uSrc;
uniform vec2 uSourceSize; uniform vec2 uSrcSize;
uniform vec4 uFGColor; uniform vec4 uFGColor;
uniform vec4 uBGColor; uniform vec4 uBGColor;
uniform int uGammaCorrectionEnabled; uniform int uGammaCorrectionEnabled;
@ -27,23 +24,23 @@ in vec2 vTexCoord;
out vec4 oFragColor; out vec4 oFragColor;
#include "filter_text_gamma_correct.inc.glsl" #include "tile_filter_text_gamma_correct.inc.glsl"
#include "filter_text_convolve.inc.glsl" #include "tile_filter_text_convolve.inc.glsl"
// Convolve horizontally in this pass. // Convolve horizontally in this pass.
float sample1Tap(float offset) { float sample1Tap(float offset) {
return texture(uSource, vec2(vTexCoord.x + offset, vTexCoord.y)).r; return texture(uSrc, vec2(vTexCoord.x + offset, vTexCoord.y)).r;
} }
void main() { void main() {
// Apply defringing if necessary. // Apply defringing if necessary.
vec3 alpha; vec3 alpha;
if (uKernel.w == 0.0) { if (uKernel.w == 0.0) {
alpha = texture(uSource, vTexCoord).rrr; alpha = texture(uSrc, vTexCoord).rrr;
} else { } else {
vec4 alphaLeft, alphaRight; vec4 alphaLeft, alphaRight;
float alphaCenter; float alphaCenter;
sample9Tap(alphaLeft, alphaCenter, alphaRight, 1.0 / uSourceSize.x); sample9Tap(alphaLeft, alphaCenter, alphaRight, 1.0 / uSrcSize.x);
float r = convolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight.xy)); float r = convolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight.xy));
float g = convolve7Tap(vec4(alphaLeft.yzw, alphaCenter), alphaRight.xyz); float g = convolve7Tap(vec4(alphaLeft.yzw, alphaCenter), alphaRight.xyz);

View File

@ -1,6 +1,6 @@
// pathfinder/shaders/filter_text_convolve.inc.glsl // pathfinder/shaders/tile_filter_text_convolve.inc.glsl
// //
// Copyright © 2019 The Pathfinder Project Developers. // Copyright © 2020 The Pathfinder Project Developers.
// //
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license