Implement the HSL filters (called "non-separable blend modes" in the spec).

These cannot be implemented with the standard OpenGL blending functions, so we
have to do them manually in a shader, which requires a good deal of machinery
to create intermediate framebuffers and so forth.
This commit is contained in:
Patrick Walton 2020-02-22 14:39:03 -08:00
parent 67d12adb6c
commit 5421525eaa
29 changed files with 904 additions and 106 deletions

View File

@ -531,6 +531,10 @@ pub enum CompositeOperation {
Lighter, Lighter,
Lighten, Lighten,
Darken, Darken,
Hue,
Saturation,
Color,
Luminosity,
} }
impl CompositeOperation { impl CompositeOperation {
@ -544,6 +548,10 @@ impl CompositeOperation {
CompositeOperation::Lighter => BlendMode::Lighter, CompositeOperation::Lighter => BlendMode::Lighter,
CompositeOperation::Lighten => BlendMode::Lighten, CompositeOperation::Lighten => BlendMode::Lighten,
CompositeOperation::Darken => BlendMode::Darken, CompositeOperation::Darken => BlendMode::Darken,
CompositeOperation::Hue => BlendMode::Hue,
CompositeOperation::Saturation => BlendMode::Saturation,
CompositeOperation::Color => BlendMode::Color,
CompositeOperation::Luminosity => BlendMode::Luminosity,
} }
} }
} }

View File

@ -61,7 +61,7 @@ pub enum CompositeOp {
SrcOver, SrcOver,
} }
/// Blend modes that can be applied to individual paths without creating layers for them. /// Blend modes that can be applied to individual paths.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum BlendMode { pub enum BlendMode {
Clear, Clear,
@ -73,6 +73,10 @@ pub enum BlendMode {
Lighter, Lighter,
Lighten, Lighten,
Darken, Darken,
Hue,
Saturation,
Color,
Luminosity,
} }
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
@ -105,7 +109,11 @@ impl BlendMode {
BlendMode::Xor | BlendMode::Xor |
BlendMode::Lighter | BlendMode::Lighter |
BlendMode::Lighten | BlendMode::Lighten |
BlendMode::Darken => false, BlendMode::Darken |
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => false,
} }
} }
} }

View File

@ -9,7 +9,8 @@
// except according to those terms. // except according to those terms.
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, FillStyle, LineJoin, Path2D}; use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D};
use pathfinder_canvas::{FillStyle, LineJoin, Path2D};
use pathfinder_color::{ColorF, ColorU}; use pathfinder_color::{ColorF, ColorU};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::gradient::{ColorStop, Gradient}; use pathfinder_content::gradient::{ColorStop, Gradient};

View File

@ -145,6 +145,9 @@ impl GLDevice {
UniformData::Float(value) => { UniformData::Float(value) => {
gl::Uniform1f(uniform.location, value); ck(); gl::Uniform1f(uniform.location, value); ck();
} }
UniformData::IVec3(value) => {
gl::Uniform3i(uniform.location, value[0], value[1], value[2]); ck();
}
UniformData::Int(value) => { UniformData::Int(value) => {
gl::Uniform1i(uniform.location, value); ck(); gl::Uniform1i(uniform.location, value); ck();
} }

View File

@ -164,6 +164,7 @@ pub enum ShaderKind {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum UniformData { pub enum UniformData {
Float(f32), Float(f32),
IVec3([i32; 3]),
Int(i32), Int(i32),
Mat2(F32x4), Mat2(F32x4),
Mat4([F32x4; 4]), Mat4([F32x4; 4]),

View File

@ -873,6 +873,11 @@ impl MetalDevice {
UniformData::Float(value) => { UniformData::Float(value) => {
uniform_buffer_data.write_f32::<NativeEndian>(value).unwrap() uniform_buffer_data.write_f32::<NativeEndian>(value).unwrap()
} }
UniformData::IVec3(values) => {
uniform_buffer_data.write_i32::<NativeEndian>(values[0]).unwrap();
uniform_buffer_data.write_i32::<NativeEndian>(values[1]).unwrap();
uniform_buffer_data.write_i32::<NativeEndian>(values[2]).unwrap();
}
UniformData::Int(value) => { UniformData::Int(value) => {
uniform_buffer_data.write_i32::<NativeEndian>(value).unwrap() uniform_buffer_data.write_i32::<NativeEndian>(value).unwrap()
} }
@ -1251,6 +1256,9 @@ impl UniformDataExt for UniformData {
UniformData::Float(ref data) => { UniformData::Float(ref data) => {
Some(slice::from_raw_parts(data as *const f32 as *const u8, 4 * 1)) Some(slice::from_raw_parts(data as *const f32 as *const u8, 4 * 1))
} }
UniformData::IVec3(ref data) => {
Some(slice::from_raw_parts(data as *const i32 as *const u8, 4 * 3))
}
UniformData::Int(ref data) => { UniformData::Int(ref data) => {
Some(slice::from_raw_parts(data as *const i32 as *const u8, 4 * 1)) Some(slice::from_raw_parts(data as *const i32 as *const u8, 4 * 1))
} }

View File

@ -11,7 +11,7 @@
//! Packs data onto the GPU. //! Packs data onto the GPU.
use crate::concurrent::executor::Executor; use crate::concurrent::executor::Executor;
use crate::gpu::renderer::MASK_TILES_ACROSS; use crate::gpu::renderer::{BlendModeProgram, MASK_TILES_ACROSS};
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex}; use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileBatch, TileObjectPrimitive}; use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileBatch, TileObjectPrimitive};
use crate::options::{PreparedBuildOptions, RenderCommandListener}; use crate::options::{PreparedBuildOptions, RenderCommandListener};
@ -90,13 +90,22 @@ impl<'a> SceneBuilder<'a> {
pub fn build<E>(&mut self, executor: &E) where E: Executor { pub fn build<E>(&mut self, executor: &E) where E: Executor {
let start_time = Instant::now(); let start_time = Instant::now();
// Send the start rendering command.
let bounding_quad = self.built_options.bounding_quad(); let bounding_quad = self.built_options.bounding_quad();
let clip_path_count = self.scene.clip_paths.len(); let clip_path_count = self.scene.clip_paths.len();
let draw_path_count = self.scene.paths.len(); let draw_path_count = self.scene.paths.len();
let total_path_count = clip_path_count + draw_path_count; let total_path_count = clip_path_count + draw_path_count;
self.listener.send(RenderCommand::Start { bounding_quad, path_count: total_path_count });
let needs_readable_framebuffer = self.needs_readable_framebuffer();
self.listener.send(RenderCommand::Start {
bounding_quad,
path_count: total_path_count,
needs_readable_framebuffer,
});
// Build paint data.
let PaintInfo { let PaintInfo {
data: paint_data, data: paint_data,
metadata: paint_metadata, metadata: paint_metadata,
@ -249,7 +258,8 @@ impl<'a> SceneBuilder<'a> {
culled_tiles.push_mask_tiles(&built_draw_path.path); 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 // 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. // 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 // TODO(pcwalton): If we really wanted to, we could use tile maps to avoid batch
// breaks in some cases… // breaks in some cases…
@ -259,7 +269,9 @@ impl<'a> SceneBuilder<'a> {
paint_page, paint_page,
blend_mode blend_mode
}) if paint_page == built_draw_path.paint_page && }) if paint_page == built_draw_path.paint_page &&
blend_mode == built_draw_path.blend_mode => {} blend_mode == built_draw_path.blend_mode &&
!BlendModeProgram::from_blend_mode(
built_draw_path.blend_mode).needs_readable_framebuffer() => {}
_ => { _ => {
culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles { culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles {
tiles: vec![], tiles: vec![],
@ -377,6 +389,30 @@ impl<'a> SceneBuilder<'a> {
// FIXME(pcwalton): Check for overflow! // FIXME(pcwalton): Check for overflow!
self.next_mask_tile_index.fetch_add(1, Ordering::Relaxed) as u16 self.next_mask_tile_index.fetch_add(1, Ordering::Relaxed) as u16
} }
fn needs_readable_framebuffer(&self) -> bool {
let mut framebuffer_nesting = 0;
for display_item in &self.scene.display_list {
match *display_item {
DisplayItem::DrawRenderTarget { .. } => {}
DisplayItem::PushRenderTarget(_) => framebuffer_nesting += 1,
DisplayItem::PopRenderTarget => framebuffer_nesting -= 1,
DisplayItem::DrawPaths { start_index, end_index } => {
if framebuffer_nesting > 0 {
continue;
}
for path_index in start_index..end_index {
let blend_mode = self.scene.paths[path_index as usize].blend_mode();
let blend_mode_program = BlendModeProgram::from_blend_mode(blend_mode);
if blend_mode_program.needs_readable_framebuffer() {
return true;
}
}
}
}
}
false
}
} }
impl BuiltPath { impl BuiltPath {

View File

@ -10,7 +10,8 @@
use crate::gpu::debug::DebugUIPresenter; use crate::gpu::debug::DebugUIPresenter;
use crate::gpu::options::{DestFramebuffer, RendererOptions}; use crate::gpu::options::{DestFramebuffer, RendererOptions};
use crate::gpu::shaders::{AlphaTileProgram, AlphaTileVertexArray, FillProgram, FillVertexArray}; use crate::gpu::shaders::{AlphaTileHSLProgram, AlphaTileProgram, AlphaTileVertexArray};
use crate::gpu::shaders::{CopyTileProgram, CopyTileVertexArray, FillProgram, FillVertexArray};
use crate::gpu::shaders::{FilterBasicProgram, FilterBasicVertexArray, FilterTextProgram}; use crate::gpu::shaders::{FilterBasicProgram, FilterBasicVertexArray, FilterTextProgram};
use crate::gpu::shaders::{FilterTextVertexArray, MAX_FILLS_PER_BATCH, MaskTileProgram}; use crate::gpu::shaders::{FilterTextVertexArray, MAX_FILLS_PER_BATCH, MaskTileProgram};
use crate::gpu::shaders::{MaskTileVertexArray, ReprojectionProgram, ReprojectionVertexArray}; use crate::gpu::shaders::{MaskTileVertexArray, ReprojectionProgram, ReprojectionVertexArray};
@ -18,6 +19,7 @@ use crate::gpu::shaders::{SolidTileProgram, SolidTileVertexArray};
use crate::gpu::shaders::{StencilProgram, StencilVertexArray}; use crate::gpu::shaders::{StencilProgram, StencilVertexArray};
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, SolidTileVertex};
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};
use pathfinder_content::effects::{BlendMode, CompositeOp, DefringingKernel, Effects, Filter}; use pathfinder_content::effects::{BlendMode, CompositeOp, DefringingKernel, Effects, Filter};
@ -51,6 +53,9 @@ const TEXTURE_CACHE_SIZE: usize = 8;
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32; const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32;
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * MASK_TILES_DOWN as i32; const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * MASK_TILES_DOWN as i32;
const BLEND_TERM_DEST: i32 = 0;
const BLEND_TERM_SRC: i32 = 1;
pub struct Renderer<D> pub struct Renderer<D>
where where
D: Device, D: Device,
@ -64,13 +69,18 @@ where
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>,
copy_tile_program: CopyTileProgram<D>,
solid_tile_program: SolidTileProgram<D>, solid_tile_program: SolidTileProgram<D>,
alpha_tile_program: AlphaTileProgram<D>, alpha_tile_program: AlphaTileProgram<D>,
alpha_tile_hsl_program: AlphaTileHSLProgram<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>,
solid_tile_vertex_array: SolidTileVertexArray<D>, solid_tile_vertex_array: SolidTileVertexArray<D>,
alpha_tile_vertex_array: AlphaTileVertexArray<D>, alpha_tile_vertex_array: AlphaTileVertexArray<D>,
alpha_tile_hsl_vertex_array: AlphaTileVertexArray<D>,
area_lut_texture: D::Texture, area_lut_texture: D::Texture,
alpha_tile_vertex_buffer: D::Buffer,
quad_vertex_positions_buffer: D::Buffer, quad_vertex_positions_buffer: D::Buffer,
quad_vertex_indices_buffer: D::Buffer, quad_vertex_indices_buffer: D::Buffer,
quads_vertex_indices_buffer: D::Buffer, quads_vertex_indices_buffer: D::Buffer,
@ -78,6 +88,8 @@ where
fill_vertex_array: FillVertexArray<D>, fill_vertex_array: FillVertexArray<D>,
fill_framebuffer: D::Framebuffer, fill_framebuffer: D::Framebuffer,
mask_framebuffer: D::Framebuffer, mask_framebuffer: D::Framebuffer,
dest_blend_framebuffer: D::Framebuffer,
intermediate_dest_framebuffer: D::Framebuffer,
paint_textures: Vec<PaintTexture<D>>, paint_textures: Vec<PaintTexture<D>>,
render_targets: Vec<RenderTargetInfo<D>>, render_targets: Vec<RenderTargetInfo<D>>,
render_target_stack: Vec<RenderTargetId>, render_target_stack: Vec<RenderTargetId>,
@ -115,7 +127,7 @@ where
pub debug_ui_presenter: DebugUIPresenter<D>, pub debug_ui_presenter: DebugUIPresenter<D>,
// Extra info // Extra info
use_depth: bool, flags: RendererFlags,
} }
impl<D> Renderer<D> impl<D> Renderer<D>
@ -134,8 +146,10 @@ where
let mask_evenodd_tile_program = MaskTileProgram::new(FillRule::EvenOdd, let mask_evenodd_tile_program = MaskTileProgram::new(FillRule::EvenOdd,
&device, &device,
resources); resources);
let copy_tile_program = CopyTileProgram::new(&device, resources);
let solid_tile_program = SolidTileProgram::new(&device, resources); let solid_tile_program = SolidTileProgram::new(&device, resources);
let alpha_tile_program = AlphaTileProgram::new(&device, resources); let alpha_tile_program = AlphaTileProgram::new(&device, resources);
let alpha_tile_hsl_program = AlphaTileHSLProgram::new(&device, resources);
let filter_basic_program = FilterBasicProgram::new(&device, resources); let filter_basic_program = FilterBasicProgram::new(&device, resources);
let filter_text_program = FilterTextProgram::new(&device, resources); let filter_text_program = FilterTextProgram::new(&device, resources);
let stencil_program = StencilProgram::new(&device, resources); let stencil_program = StencilProgram::new(&device, resources);
@ -144,6 +158,7 @@ where
let area_lut_texture = device.create_texture_from_png(resources, "area-lut"); let area_lut_texture = device.create_texture_from_png(resources, "area-lut");
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 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,
@ -176,9 +191,22 @@ where
&mask_evenodd_tile_program, &mask_evenodd_tile_program,
&quads_vertex_indices_buffer, &quads_vertex_indices_buffer,
); );
let copy_tile_vertex_array = CopyTileVertexArray::new(
&device,
&copy_tile_program,
&alpha_tile_vertex_buffer,
&quads_vertex_indices_buffer,
);
let alpha_tile_vertex_array = AlphaTileVertexArray::new( let alpha_tile_vertex_array = AlphaTileVertexArray::new(
&device, &device,
&alpha_tile_program, &alpha_tile_program,
&alpha_tile_vertex_buffer,
&quads_vertex_indices_buffer,
);
let alpha_tile_hsl_vertex_array = AlphaTileVertexArray::new(
&device,
&alpha_tile_hsl_program.alpha_tile_program,
&alpha_tile_vertex_buffer,
&quads_vertex_indices_buffer, &quads_vertex_indices_buffer,
); );
let solid_tile_vertex_array = SolidTileVertexArray::new( let solid_tile_vertex_array = SolidTileVertexArray::new(
@ -218,12 +246,17 @@ where
device.create_texture(TextureFormat::R8, mask_framebuffer_size); device.create_texture(TextureFormat::R8, mask_framebuffer_size);
let mask_framebuffer = device.create_framebuffer(mask_framebuffer_texture); let mask_framebuffer = device.create_framebuffer(mask_framebuffer_texture);
let window_size = dest_framebuffer.window_size(&device);
let dest_blend_texture = device.create_texture(TextureFormat::RGBA8, window_size);
let dest_blend_framebuffer = device.create_framebuffer(dest_blend_texture);
let intermediate_dest_texture = device.create_texture(TextureFormat::RGBA8, window_size);
let intermediate_dest_framebuffer = device.create_framebuffer(intermediate_dest_texture);
let clear_paint_texture = let clear_paint_texture =
device.create_texture_from_data(TextureFormat::RGBA8, device.create_texture_from_data(TextureFormat::RGBA8,
Vector2I::splat(1), Vector2I::splat(1),
TextureDataRef::U8(&[0, 0, 0, 255])); TextureDataRef::U8(&[0, 0, 0, 255]));
let window_size = dest_framebuffer.window_size(&device);
let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size); let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size);
Renderer { Renderer {
@ -234,13 +267,18 @@ where
fill_program, fill_program,
mask_winding_tile_program, mask_winding_tile_program,
mask_evenodd_tile_program, mask_evenodd_tile_program,
copy_tile_program,
solid_tile_program, solid_tile_program,
alpha_tile_program, alpha_tile_program,
alpha_tile_hsl_program,
mask_winding_tile_vertex_array, mask_winding_tile_vertex_array,
mask_evenodd_tile_vertex_array, mask_evenodd_tile_vertex_array,
solid_tile_vertex_array, solid_tile_vertex_array,
alpha_tile_vertex_array, alpha_tile_vertex_array,
copy_tile_vertex_array,
alpha_tile_hsl_vertex_array,
area_lut_texture, area_lut_texture,
alpha_tile_vertex_buffer,
quad_vertex_positions_buffer, quad_vertex_positions_buffer,
quad_vertex_indices_buffer, quad_vertex_indices_buffer,
quads_vertex_indices_buffer, quads_vertex_indices_buffer,
@ -248,6 +286,8 @@ where
fill_vertex_array, fill_vertex_array,
fill_framebuffer, fill_framebuffer,
mask_framebuffer, mask_framebuffer,
dest_blend_framebuffer,
intermediate_dest_framebuffer,
paint_textures: vec![], paint_textures: vec![],
render_targets: vec![], render_targets: vec![],
render_target_stack: vec![], render_target_stack: vec![],
@ -275,7 +315,7 @@ where
buffered_fills: vec![], buffered_fills: vec![],
texture_cache: TextureCache::new(), texture_cache: TextureCache::new(),
use_depth: false, flags: RendererFlags::empty(),
} }
} }
@ -287,11 +327,8 @@ where
pub fn render_command(&mut self, command: &RenderCommand) { pub fn render_command(&mut self, command: &RenderCommand) {
match *command { match *command {
RenderCommand::Start { bounding_quad, path_count } => { RenderCommand::Start { bounding_quad, path_count, needs_readable_framebuffer } => {
if self.use_depth { self.start_rendering(bounding_quad, path_count, needs_readable_framebuffer);
self.draw_stencil(&bounding_quad);
}
self.stats.path_count = path_count;
} }
RenderCommand::AddPaintData(ref paint_data) => self.upload_paint_data(paint_data), RenderCommand::AddPaintData(ref paint_data) => self.upload_paint_data(paint_data),
RenderCommand::AddFills(ref fills) => self.add_fills(fills), RenderCommand::AddFills(ref fills) => self.add_fills(fills),
@ -328,12 +365,31 @@ where
} }
pub fn end_scene(&mut self) { pub fn end_scene(&mut self) {
self.blit_intermediate_dest_framebuffer_if_necessary();
self.end_composite_timer_query(); self.end_composite_timer_query();
self.pending_timers.push_back(mem::replace(&mut self.current_timers, RenderTimers::new())); self.pending_timers.push_back(mem::replace(&mut self.current_timers, RenderTimers::new()));
self.device.end_commands(); self.device.end_commands();
} }
fn start_rendering(&mut self,
bounding_quad: BoundingQuad,
path_count: usize,
mut needs_readable_framebuffer: bool) {
if let DestFramebuffer::Other(_) = self.dest_framebuffer {
needs_readable_framebuffer = false;
}
if self.flags.contains(RendererFlags::USE_DEPTH) {
self.draw_stencil(&bounding_quad);
}
self.stats.path_count = path_count;
self.flags.set(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED,
needs_readable_framebuffer);
}
pub fn draw_debug_ui(&self) { pub fn draw_debug_ui(&self) {
self.debug_ui_presenter.draw(&self.device); self.debug_ui_presenter.draw(&self.device);
} }
@ -392,12 +448,12 @@ where
#[inline] #[inline]
pub fn disable_depth(&mut self) { pub fn disable_depth(&mut self) {
self.use_depth = false; self.flags.remove(RendererFlags::USE_DEPTH);
} }
#[inline] #[inline]
pub fn enable_depth(&mut self) { pub fn enable_depth(&mut self) {
self.use_depth = true; self.flags.insert(RendererFlags::USE_DEPTH);
} }
#[inline] #[inline]
@ -481,12 +537,10 @@ where
} }
fn upload_alpha_tiles(&mut self, alpha_tiles: &[AlphaTile]) { fn upload_alpha_tiles(&mut self, alpha_tiles: &[AlphaTile]) {
self.device.allocate_buffer( self.device.allocate_buffer(&self.alpha_tile_vertex_buffer,
&self.alpha_tile_vertex_array.vertex_buffer,
BufferData::Memory(&alpha_tiles), BufferData::Memory(&alpha_tiles),
BufferTarget::Vertex, BufferTarget::Vertex,
BufferUploadMode::Dynamic, BufferUploadMode::Dynamic);
);
self.ensure_index_buffer(alpha_tiles.len()); self.ensure_index_buffer(alpha_tiles.len());
} }
@ -645,18 +699,32 @@ where
tile_count: u32, tile_count: u32,
paint_page: PaintPageId, paint_page: PaintPageId,
blend_mode: BlendMode) { blend_mode: BlendMode) {
let blend_mode_program = BlendModeProgram::from_blend_mode(blend_mode);
if blend_mode_program.needs_readable_framebuffer() {
self.copy_alpha_tiles_to_dest_blend_texture(tile_count);
}
let clear_color = self.clear_color_for_draw_operation(); let clear_color = self.clear_color_for_draw_operation();
let (alpha_tile_program, alpha_tile_vertex_array) = match blend_mode_program {
BlendModeProgram::Regular => (&self.alpha_tile_program, &self.alpha_tile_vertex_array),
BlendModeProgram::HSL => {
(&self.alpha_tile_hsl_program.alpha_tile_program,
&self.alpha_tile_hsl_vertex_array)
}
};
let draw_viewport = self.draw_viewport();
let mut textures = vec![self.device.framebuffer_texture(&self.mask_framebuffer)]; let mut textures = vec![self.device.framebuffer_texture(&self.mask_framebuffer)];
let mut uniforms = vec![ let mut uniforms = vec![
(&self.alpha_tile_program.transform_uniform, (&alpha_tile_program.transform_uniform,
UniformData::Mat4(self.tile_transform().to_columns())), UniformData::Mat4(self.tile_transform().to_columns())),
(&self.alpha_tile_program.tile_size_uniform, (&alpha_tile_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
(&self.alpha_tile_program.stencil_texture_uniform, UniformData::TextureUnit(0)), (&alpha_tile_program.stencil_texture_uniform, UniformData::TextureUnit(0)),
(&self.alpha_tile_program.stencil_texture_size_uniform, (&alpha_tile_program.framebuffer_size_uniform,
UniformData::Vec2(F32x2::new(MASK_FRAMEBUFFER_WIDTH as f32, UniformData::Vec2(draw_viewport.size().to_f32().0)),
MASK_FRAMEBUFFER_HEIGHT as f32))),
]; ];
let paint_texture = match blend_mode { let paint_texture = match blend_mode {
@ -671,22 +739,24 @@ where
textures.push(paint_texture); textures.push(paint_texture);
uniforms.push((&self.alpha_tile_program.paint_texture_uniform, uniforms.push((&self.alpha_tile_program.paint_texture_uniform,
UniformData::TextureUnit(1))); UniformData::TextureUnit(1)));
uniforms.push((&self.alpha_tile_program.paint_texture_size_uniform,
UniformData::Vec2(self.device match blend_mode_program {
.texture_size(paint_texture) BlendModeProgram::Regular => {}
.0 BlendModeProgram::HSL => {
.to_f32x2()))); self.set_uniforms_for_hsl_blend_mode(&mut textures, &mut uniforms, blend_mode);
}
}
self.device.draw_elements(tile_count * 6, &RenderState { self.device.draw_elements(tile_count * 6, &RenderState {
target: &self.draw_render_target(), target: &self.draw_render_target(),
program: &self.alpha_tile_program.program, program: &alpha_tile_program.program,
vertex_array: &self.alpha_tile_vertex_array.vertex_array, vertex_array: &alpha_tile_vertex_array.vertex_array,
primitive: Primitive::Triangles, primitive: Primitive::Triangles,
textures: &textures, textures: &textures,
uniforms: &uniforms, uniforms: &uniforms,
viewport: self.draw_viewport(), viewport: draw_viewport,
options: RenderOptions { options: RenderOptions {
blend: Some(blend_mode.to_blend_state()), blend: blend_mode.to_blend_state(),
stencil: self.stencil_state(), stencil: self.stencil_state(),
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
..RenderOptions::default() ..RenderOptions::default()
@ -696,6 +766,60 @@ where
self.preserve_draw_framebuffer(); self.preserve_draw_framebuffer();
} }
fn set_uniforms_for_hsl_blend_mode<'a>(&'a self,
textures: &mut Vec<&'a D::Texture>,
uniforms: &mut Vec<(&'a D::Uniform, UniformData)>,
blend_mode: BlendMode) {
let hsl_terms = match blend_mode {
BlendMode::Hue => [BLEND_TERM_SRC, BLEND_TERM_DEST, BLEND_TERM_DEST],
BlendMode::Saturation => [BLEND_TERM_DEST, BLEND_TERM_SRC, BLEND_TERM_DEST],
BlendMode::Luminosity => [BLEND_TERM_DEST, BLEND_TERM_DEST, BLEND_TERM_SRC ],
BlendMode::Color => [BLEND_TERM_SRC, BLEND_TERM_SRC, BLEND_TERM_DEST],
_ => unreachable!(),
};
textures.push(self.device.framebuffer_texture(&self.dest_blend_framebuffer));
uniforms.push((&self.alpha_tile_hsl_program.dest_uniform, UniformData::TextureUnit(2)));
uniforms.push((&self.alpha_tile_hsl_program.blend_hsl_uniform,
UniformData::IVec3(hsl_terms)));
}
fn copy_alpha_tiles_to_dest_blend_texture(&mut self, tile_count: u32) {
let mut textures = vec![];
let mut uniforms = vec![
(&self.copy_tile_program.transform_uniform,
UniformData::Mat4(self.tile_transform().to_columns())),
(&self.copy_tile_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
];
let draw_framebuffer = match self.draw_render_target() {
RenderTarget::Framebuffer(framebuffer) => framebuffer,
RenderTarget::Default => panic!("Can't copy alpha tiles from default framebuffer!"),
};
let draw_texture = self.device.framebuffer_texture(&draw_framebuffer);
textures.push(draw_texture);
uniforms.push((&self.copy_tile_program.src_uniform, UniformData::TextureUnit(0)));
self.device.draw_elements(tile_count * 6, &RenderState {
target: &RenderTarget::Framebuffer(&self.dest_blend_framebuffer),
program: &self.copy_tile_program.program,
vertex_array: &self.copy_tile_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &textures,
uniforms: &uniforms,
viewport: self.draw_viewport(),
options: RenderOptions {
clear_ops: ClearOps {
color: Some(ColorF::transparent_black()),
..ClearOps::default()
},
..RenderOptions::default()
},
});
}
fn draw_solid_tiles(&mut self, tile_count: u32, paint_page: PaintPageId) { fn draw_solid_tiles(&mut self, tile_count: u32, paint_page: PaintPageId) {
let clear_color = self.clear_color_for_draw_operation(); let clear_color = self.clear_color_for_draw_operation();
@ -711,8 +835,6 @@ where
textures.push(paint_texture); textures.push(paint_texture);
uniforms.push((&self.solid_tile_program.paint_texture_uniform, uniforms.push((&self.solid_tile_program.paint_texture_uniform,
UniformData::TextureUnit(0))); UniformData::TextureUnit(0)));
uniforms.push((&self.solid_tile_program.paint_texture_size_uniform,
UniformData::Vec2(self.device.texture_size(paint_texture).0.to_f32x2())));
self.device.draw_elements(6 * tile_count, &RenderState { self.device.draw_elements(6 * tile_count, &RenderState {
target: &self.draw_render_target(), target: &self.draw_render_target(),
@ -800,7 +922,7 @@ where
], ],
viewport: self.draw_viewport(), viewport: self.draw_viewport(),
options: RenderOptions { options: RenderOptions {
blend: Some(BlendMode::SrcOver.to_blend_state()), blend: BlendMode::SrcOver.to_blend_state(),
depth: Some(DepthState { func: DepthFunc::Less, write: false, }), depth: Some(DepthState { func: DepthFunc::Less, write: false, }),
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
..RenderOptions::default() ..RenderOptions::default()
@ -817,6 +939,9 @@ where
RenderTarget::Framebuffer(framebuffer) RenderTarget::Framebuffer(framebuffer)
} }
None => { None => {
if self.flags.contains(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED) {
RenderTarget::Framebuffer(&self.intermediate_dest_framebuffer)
} else {
match self.dest_framebuffer { match self.dest_framebuffer {
DestFramebuffer::Default { .. } => RenderTarget::Default, DestFramebuffer::Default { .. } => RenderTarget::Default,
DestFramebuffer::Other(ref framebuffer) => { DestFramebuffer::Other(ref framebuffer) => {
@ -826,6 +951,7 @@ where
} }
} }
} }
}
fn push_render_target(&mut self, render_target_id: RenderTargetId) { fn push_render_target(&mut self, render_target_id: RenderTargetId) {
self.render_target_stack.push(render_target_id); self.render_target_stack.push(render_target_id);
@ -859,15 +985,12 @@ where
let clear_color = self.clear_color_for_draw_operation(); 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 main_viewport = self.main_viewport(); let main_viewport = self.main_viewport();
let uniforms = vec![ let uniforms = vec![
(&self.filter_basic_program.framebuffer_size_uniform, (&self.filter_basic_program.framebuffer_size_uniform,
UniformData::Vec2(main_viewport.size().to_f32().0)), UniformData::Vec2(main_viewport.size().to_f32().0)),
(&self.filter_basic_program.source_uniform, UniformData::TextureUnit(0)), (&self.filter_basic_program.source_uniform, UniformData::TextureUnit(0)),
(&self.filter_basic_program.source_size_uniform,
UniformData::Vec2(source_texture_size.0.to_f32x2())),
]; ];
let blend_state = match composite_op { let blend_state = match composite_op {
@ -884,7 +1007,7 @@ where
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: Some(blend_state), blend: blend_state,
..RenderOptions::default() ..RenderOptions::default()
}, },
}); });
@ -941,8 +1064,30 @@ where
}); });
} }
fn blit_intermediate_dest_framebuffer_if_necessary(&mut self) {
if !self.flags.contains(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED) {
return;
}
let main_viewport = self.main_viewport();
let uniforms = [(&self.filter_basic_program.source_uniform, UniformData::TextureUnit(0))];
let textures = [(self.device.framebuffer_texture(&self.intermediate_dest_framebuffer))];
self.device.draw_elements(6, &RenderState {
target: &RenderTarget::Default,
program: &self.filter_basic_program.program,
vertex_array: &self.filter_basic_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &textures[..],
uniforms: &uniforms[..],
viewport: main_viewport,
options: RenderOptions::default(),
});
}
fn stencil_state(&self) -> Option<StencilState> { fn stencil_state(&self) -> Option<StencilState> {
if !self.use_depth { if !self.flags.contains(RendererFlags::USE_DEPTH) {
return None; return None;
} }
@ -1163,93 +1308,145 @@ struct RenderTargetInfo<D> where D: Device {
} }
trait BlendModeExt { trait BlendModeExt {
fn to_blend_state(self) -> BlendState; fn to_blend_state(self) -> Option<BlendState>;
} }
impl BlendModeExt for BlendMode { impl BlendModeExt for BlendMode {
fn to_blend_state(self) -> BlendState { fn to_blend_state(self) -> Option<BlendState> {
match self { match self {
BlendMode::Clear => { BlendMode::Clear => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::Zero, src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::Zero, src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default() ..BlendState::default()
} })
} }
BlendMode::SrcOver => { BlendMode::SrcOver => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::One, src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::One, src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default() ..BlendState::default()
} })
} }
BlendMode::DestOver => { BlendMode::DestOver => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha, src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::DestAlpha, dest_rgb_factor: BlendFactor::DestAlpha,
src_alpha_factor: BlendFactor::OneMinusDestAlpha, src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::One, dest_alpha_factor: BlendFactor::One,
..BlendState::default() ..BlendState::default()
} })
} }
BlendMode::DestOut => { BlendMode::DestOut => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::Zero, src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::Zero, src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default() ..BlendState::default()
} })
} }
BlendMode::SrcAtop => { BlendMode::SrcAtop => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::DestAlpha, src_rgb_factor: BlendFactor::DestAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::DestAlpha, src_alpha_factor: BlendFactor::DestAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default() ..BlendState::default()
} })
} }
BlendMode::Xor => { BlendMode::Xor => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha, src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::OneMinusDestAlpha, src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default() ..BlendState::default()
} })
} }
BlendMode::Lighter => { BlendMode::Lighter => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::One, src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::One, dest_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::One, src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One, dest_alpha_factor: BlendFactor::One,
..BlendState::default() ..BlendState::default()
} })
} }
BlendMode::Lighten => { BlendMode::Lighten => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::One, src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::One, src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One, dest_alpha_factor: BlendFactor::One,
op: BlendOp::Max, op: BlendOp::Max,
} })
} }
BlendMode::Darken => { BlendMode::Darken => {
BlendState { Some(BlendState {
src_rgb_factor: BlendFactor::One, src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::One, src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One, dest_alpha_factor: BlendFactor::One,
op: BlendOp::Min, op: BlendOp::Min,
})
} }
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => {
// Blending is done manually in the shader.
None
} }
} }
} }
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum BlendModeProgram {
Regular,
HSL,
}
impl BlendModeProgram {
pub(crate) fn from_blend_mode(blend_mode: BlendMode) -> BlendModeProgram {
match blend_mode {
BlendMode::Clear |
BlendMode::SrcOver |
BlendMode::DestOver |
BlendMode::DestOut |
BlendMode::SrcAtop |
BlendMode::Xor |
BlendMode::Lighter |
BlendMode::Lighten |
BlendMode::Darken => BlendModeProgram::Regular,
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => BlendModeProgram::HSL,
}
}
pub(crate) fn needs_readable_framebuffer(self) -> bool {
match self {
BlendModeProgram::Regular => false,
BlendModeProgram::HSL => true,
}
}
}
bitflags! {
struct RendererFlags: u8 {
// Whether we need a depth buffer.
const USE_DEPTH = 0x01;
// Whether an intermediate destination framebuffer is needed.
//
// This will be true if any exotic blend modes are used at the top level (not inside a
// render target), *and* the output framebuffer is the default framebuffer.
const INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED = 0x02;
}
}

View File

@ -176,16 +176,16 @@ impl<D> MaskTileVertexArray<D> where D: Device {
pub struct AlphaTileVertexArray<D> where D: Device { pub struct AlphaTileVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray, pub vertex_array: D::VertexArray,
pub vertex_buffer: D::Buffer,
} }
impl<D> AlphaTileVertexArray<D> where D: Device { impl<D> AlphaTileVertexArray<D> where D: Device {
pub fn new( pub fn new(
device: &D, device: &D,
alpha_tile_program: &AlphaTileProgram<D>, alpha_tile_program: &AlphaTileProgram<D>,
alpha_tile_vertex_buffer: &D::Buffer,
quads_vertex_indices_buffer: &D::Buffer, quads_vertex_indices_buffer: &D::Buffer,
) -> AlphaTileVertexArray<D> { ) -> AlphaTileVertexArray<D> {
let (vertex_array, vertex_buffer) = (device.create_vertex_array(), device.create_buffer()); let vertex_array = device.create_vertex_array();
let tile_position_attr = let tile_position_attr =
device.get_vertex_attr(&alpha_tile_program.program, "TilePosition").unwrap(); device.get_vertex_attr(&alpha_tile_program.program, "TilePosition").unwrap();
@ -194,7 +194,7 @@ impl<D> AlphaTileVertexArray<D> where D: Device {
let mask_tex_coord_attr = device.get_vertex_attr(&alpha_tile_program.program, let mask_tex_coord_attr = device.get_vertex_attr(&alpha_tile_program.program,
"MaskTexCoord").unwrap(); "MaskTexCoord").unwrap();
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_array, alpha_tile_vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_position_attr, &VertexAttrDescriptor { device.configure_vertex_attr(&vertex_array, &tile_position_attr, &VertexAttrDescriptor {
size: 2, size: 2,
class: VertexAttrClass::Int, class: VertexAttrClass::Int,
@ -224,7 +224,7 @@ impl<D> AlphaTileVertexArray<D> where D: Device {
}); });
device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index); device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index);
AlphaTileVertexArray { vertex_array, vertex_buffer } AlphaTileVertexArray { vertex_array }
} }
} }
@ -281,6 +281,38 @@ where
} }
} }
pub struct CopyTileVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray,
}
impl<D> CopyTileVertexArray<D> where D: Device {
pub fn new(
device: &D,
copy_tile_program: &CopyTileProgram<D>,
copy_tile_vertex_buffer: &D::Buffer,
quads_vertex_indices_buffer: &D::Buffer,
) -> CopyTileVertexArray<D> {
let vertex_array = device.create_vertex_array();
let tile_position_attr =
device.get_vertex_attr(&copy_tile_program.program, "TilePosition").unwrap();
device.bind_buffer(&vertex_array, copy_tile_vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: ALPHA_TILE_VERTEX_SIZE,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index);
CopyTileVertexArray { vertex_array }
}
}
pub struct FillProgram<D> pub struct FillProgram<D>
where where
D: Device, D: Device,
@ -337,7 +369,6 @@ pub struct SolidTileProgram<D> where D: Device {
pub transform_uniform: D::Uniform, pub transform_uniform: D::Uniform,
pub tile_size_uniform: D::Uniform, pub tile_size_uniform: D::Uniform,
pub paint_texture_uniform: D::Uniform, pub paint_texture_uniform: D::Uniform,
pub paint_texture_size_uniform: D::Uniform,
} }
impl<D> SolidTileProgram<D> where D: Device { impl<D> SolidTileProgram<D> where D: Device {
@ -346,13 +377,11 @@ impl<D> SolidTileProgram<D> where D: Device {
let transform_uniform = device.get_uniform(&program, "Transform"); let transform_uniform = device.get_uniform(&program, "Transform");
let tile_size_uniform = device.get_uniform(&program, "TileSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize");
let paint_texture_uniform = device.get_uniform(&program, "PaintTexture"); let paint_texture_uniform = device.get_uniform(&program, "PaintTexture");
let paint_texture_size_uniform = device.get_uniform(&program, "PaintTextureSize");
SolidTileProgram { SolidTileProgram {
program, program,
transform_uniform, transform_uniform,
tile_size_uniform, tile_size_uniform,
paint_texture_uniform, paint_texture_uniform,
paint_texture_size_uniform,
} }
} }
} }
@ -361,37 +390,78 @@ pub struct AlphaTileProgram<D> where D: Device {
pub program: D::Program, pub program: D::Program,
pub transform_uniform: D::Uniform, pub transform_uniform: D::Uniform,
pub tile_size_uniform: D::Uniform, pub tile_size_uniform: D::Uniform,
pub framebuffer_size_uniform: D::Uniform,
pub stencil_texture_uniform: D::Uniform, pub stencil_texture_uniform: D::Uniform,
pub stencil_texture_size_uniform: D::Uniform,
pub paint_texture_uniform: D::Uniform, pub paint_texture_uniform: D::Uniform,
pub paint_texture_size_uniform: D::Uniform,
} }
impl<D> AlphaTileProgram<D> where D: Device { impl<D> AlphaTileProgram<D> where D: Device {
#[inline]
pub fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileProgram<D> { pub fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileProgram<D> {
let program = device.create_program(resources, "tile_alpha"); AlphaTileProgram::from_fragment_shader_name(device, resources, "tile_alpha")
}
fn from_fragment_shader_name(device: &D,
resources: &dyn ResourceLoader,
fragment_shader_name: &str)
-> AlphaTileProgram<D> {
let program = device.create_program_from_shader_names(resources,
fragment_shader_name,
"tile_alpha",
fragment_shader_name);
let transform_uniform = device.get_uniform(&program, "Transform"); let transform_uniform = device.get_uniform(&program, "Transform");
let tile_size_uniform = device.get_uniform(&program, "TileSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let stencil_texture_uniform = device.get_uniform(&program, "StencilTexture"); let stencil_texture_uniform = device.get_uniform(&program, "StencilTexture");
let stencil_texture_size_uniform = device.get_uniform(&program, "StencilTextureSize");
let paint_texture_uniform = device.get_uniform(&program, "PaintTexture"); let paint_texture_uniform = device.get_uniform(&program, "PaintTexture");
let paint_texture_size_uniform = device.get_uniform(&program, "PaintTextureSize");
AlphaTileProgram { AlphaTileProgram {
program, program,
transform_uniform, transform_uniform,
tile_size_uniform, tile_size_uniform,
framebuffer_size_uniform,
stencil_texture_uniform, stencil_texture_uniform,
stencil_texture_size_uniform,
paint_texture_uniform, paint_texture_uniform,
paint_texture_size_uniform,
} }
} }
} }
pub struct CopyTileProgram<D> where D: Device {
pub program: D::Program,
pub transform_uniform: D::Uniform,
pub tile_size_uniform: D::Uniform,
pub src_uniform: D::Uniform,
}
impl<D> CopyTileProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> CopyTileProgram<D> {
let program = device.create_program(resources, "tile_copy");
let transform_uniform = device.get_uniform(&program, "Transform");
let tile_size_uniform = device.get_uniform(&program, "TileSize");
let src_uniform = device.get_uniform(&program, "Src");
CopyTileProgram { program, transform_uniform, tile_size_uniform, src_uniform }
}
}
pub struct AlphaTileHSLProgram<D> where D: Device {
pub alpha_tile_program: AlphaTileProgram<D>,
pub dest_uniform: D::Uniform,
pub blend_hsl_uniform: D::Uniform,
}
impl<D> AlphaTileHSLProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileHSLProgram<D> {
let alpha_tile_program = AlphaTileProgram::from_fragment_shader_name(device,
resources,
"tile_alpha_hsl");
let dest_uniform = device.get_uniform(&alpha_tile_program.program, "Dest");
let blend_hsl_uniform = device.get_uniform(&alpha_tile_program.program, "BlendHSL");
AlphaTileHSLProgram { alpha_tile_program, dest_uniform, blend_hsl_uniform }
}
}
pub struct FilterBasicProgram<D> where D: Device { pub struct FilterBasicProgram<D> where D: Device {
pub program: D::Program, pub program: D::Program,
pub source_uniform: D::Uniform, pub source_uniform: D::Uniform,
pub source_size_uniform: D::Uniform,
pub framebuffer_size_uniform: D::Uniform, pub framebuffer_size_uniform: D::Uniform,
} }
@ -402,14 +472,8 @@ impl<D> FilterBasicProgram<D> where D: Device {
"filter", "filter",
"filter_basic"); "filter_basic");
let source_uniform = device.get_uniform(&program, "Source"); let source_uniform = device.get_uniform(&program, "Source");
let source_size_uniform = device.get_uniform(&program, "SourceSize");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
FilterBasicProgram { FilterBasicProgram { program, source_uniform, framebuffer_size_uniform }
program,
source_uniform,
source_size_uniform,
framebuffer_size_uniform,
}
} }
} }

View File

@ -22,7 +22,19 @@ use std::time::Duration;
pub enum RenderCommand { pub enum RenderCommand {
// Starts rendering a frame. // Starts rendering a frame.
Start { path_count: usize, bounding_quad: BoundingQuad }, Start {
/// The number of paths that will be rendered.
path_count: usize,
/// A bounding quad for the scene.
bounding_quad: BoundingQuad,
/// Whether the framebuffer we're rendering to must be readable.
///
/// This is needed if a path that renders directly to the output framebuffer (i.e. not to a
/// render target) uses one of the more exotic blend modes.
needs_readable_framebuffer: bool,
},
// Uploads paint data for use with subsequent rendering commands to the GPU. // Uploads paint data for use with subsequent rendering commands to the GPU.
AddPaintData(PaintData), AddPaintData(PaintData),

View File

@ -261,11 +261,18 @@ impl Palette {
Transform2F::from_translation(texture_origin_uv) * Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(gradient_tile_scale / view_box_size.to_f32()) Transform2F::from_scale(gradient_tile_scale / view_box_size.to_f32())
} }
Paint::Pattern(_) => { Paint::Pattern(Pattern { source: PatternSource::Image(_), .. }) => {
let texture_origin_uv = rect_to_uv(metadata.tex_rect, texture_scale).origin(); let texture_origin_uv = rect_to_uv(metadata.tex_rect, texture_scale).origin();
Transform2F::from_translation(texture_origin_uv) * Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(texture_scale) Transform2F::from_scale(texture_scale)
} }
Paint::Pattern(Pattern { source: PatternSource::RenderTarget(_), .. }) => {
// FIXME(pcwalton): Only do this in GL, not Metal!
let texture_origin_uv = rect_to_uv(metadata.tex_rect,
texture_scale).lower_left();
Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(texture_scale.scale_xy(Vector2F::new(1.0, -1.0)))
}
} }
} }

View File

@ -20,7 +20,6 @@
precision highp float; precision highp float;
uniform sampler2D uSource; uniform sampler2D uSource;
uniform vec2 uSourceSize;
in vec2 vTexCoord; in vec2 vTexCoord;

View File

@ -16,11 +16,10 @@ precision highp float;
uniform sampler2D uStencilTexture; uniform sampler2D uStencilTexture;
uniform sampler2D uPaintTexture; uniform sampler2D uPaintTexture;
uniform vec2 uPaintTextureSize; uniform vec2 uFramebufferSize;
in vec2 vColorTexCoord; in vec2 vColorTexCoord;
in vec2 vMaskTexCoord; in vec2 vMaskTexCoord;
in vec4 vColor;
out vec4 oFragColor; out vec4 oFragColor;

View File

@ -16,7 +16,6 @@ precision highp float;
uniform mat4 uTransform; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uStencilTextureSize;
in ivec2 aTilePosition; in ivec2 aTilePosition;
in vec2 aColorTexCoord; in vec2 aColorTexCoord;

View File

@ -0,0 +1,90 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#extension GL_GOOGLE_include_directive : enable
precision highp float;
uniform sampler2D uStencilTexture;
uniform sampler2D uPaintTexture;
uniform sampler2D uDest;
uniform ivec3 uBlendHSL;
uniform vec2 uFramebufferSize;
in vec2 vColorTexCoord;
in vec2 vMaskTexCoord;
out vec4 oFragColor;
vec3 convertHSLToRGB(vec3 hsl){
float a = hsl . y * min(hsl . z, 1.0 - hsl . z);
vec3 ks = mod(vec3(0.0, 8.0, 4.0)+ vec3(hsl . x * 1.9098593171027443), 12.0);
return hsl . zzz - clamp(min(ks - vec3(3.0), vec3(9.0)- ks), - 1.0, 1.0)* a;
}
vec3 convertRGBToHSL(vec3 rgb){
float v = max((rgb . x, rgb . y), rgb . z);
float c = v - min((rgb . x, rgb . y), rgb . z);
float l = v - 0.5 * c;
vec3 tmp = vec3(0.0);
bvec3 is_v = equal(rgb, vec3(v));
if(is_v . r)
tmp = vec3(0.0, rgb . gb);
else if(is_v . g)
tmp = vec3(2.0, rgb . br);
else if(is_v . b)
tmp = vec3(4.0, rgb . rg);
float h = 1.0471975511965976 *(tmp . x +(tmp . y - tmp . z)/ c);
float s = 0.0;
if(l > 0.0 && l < 1.0)
s =(v - l)/ min(l, 1.0 - l);
return vec3(h, s, l);
}
void main(){
float coverage = texture(uStencilTexture, vMaskTexCoord). r;
vec4 srcRGBA = texture(uPaintTexture, vColorTexCoord);
srcRGBA . a *= coverage;
vec2 destTexCoord = gl_FragCoord . xy / uFramebufferSize;
vec4 destRGBA = texture(uDest, destTexCoord);
vec3 destHSL = convertRGBToHSL(destRGBA . rgb);
vec3 srcHSL = convertRGBToHSL(srcRGBA . rgb);
bvec3 blendDest = equal(uBlendHSL, ivec3(0));
vec3 blendedHSL = vec3(blendDest . x ? destHSL . x : srcHSL . x,
blendDest . y ? destHSL . y : srcHSL . y,
blendDest . z ? destHSL . z : srcHSL . z);
vec3 blendedRGB = convertHSLToRGB(blendedHSL);
vec4 color = vec4(srcRGBA . a *(1.0 - destRGBA . a)* srcRGBA . rgb +
srcRGBA . a * destRGBA . a * blendedRGB +
(1.0 - srcRGBA . a)* destRGBA . a * destRGBA . rgb,
1.0);
oFragColor = color;
}

View File

@ -0,0 +1,26 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
precision highp float;
uniform vec2 uFramebufferSize;
uniform sampler2D uSrc;
out vec4 oFragColor;
void main(){
vec2 texCoord = gl_FragCoord . xy / uFramebufferSize;
oFragColor = texture(uSrc, texCoord);
}

View File

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

View File

@ -15,7 +15,6 @@
precision highp float; precision highp float;
uniform sampler2D uPaintTexture; uniform sampler2D uPaintTexture;
uniform vec2 uPaintTextureSize;
in vec2 vColorTexCoord; in vec2 vColorTexCoord;

View File

@ -0,0 +1,127 @@
// 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> uStencilTexture [[id(0)]];
sampler uStencilTextureSmplr [[id(1)]];
texture2d<float> uPaintTexture [[id(2)]];
sampler uPaintTextureSmplr [[id(3)]];
constant float2* uFramebufferSize [[id(4)]];
texture2d<float> uDest [[id(5)]];
sampler uDestSmplr [[id(6)]];
constant int3* uBlendHSL [[id(7)]];
};
struct main0_out
{
float4 oFragColor [[color(0)]];
};
struct main0_in
{
float2 vColorTexCoord [[user(locn0)]];
float2 vMaskTexCoord [[user(locn1)]];
};
// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()
template<typename Tx, typename Ty>
Tx mod(Tx x, Ty y)
{
return x - y * floor(x / y);
}
float3 convertRGBToHSL(thread const float3& rgb)
{
float v = fast::max(rgb.y, rgb.z);
float c = v - fast::min(rgb.y, rgb.z);
float l = v - (0.5 * c);
float3 tmp = float3(0.0);
bool3 is_v = rgb == float3(v);
if (is_v.x)
{
tmp = float3(0.0, rgb.yz);
}
else
{
if (is_v.y)
{
tmp = float3(2.0, rgb.zx);
}
else
{
if (is_v.z)
{
tmp = float3(4.0, rgb.xy);
}
}
}
float h = 1.0471975803375244140625 * (tmp.x + ((tmp.y - tmp.z) / c));
float s = 0.0;
if ((l > 0.0) && (l < 1.0))
{
s = (v - l) / fast::min(l, 1.0 - l);
}
return float3(h, s, l);
}
float3 convertHSLToRGB(thread const float3& hsl)
{
float a = hsl.y * fast::min(hsl.z, 1.0 - hsl.z);
float3 ks = mod(float3(0.0, 8.0, 4.0) + float3(hsl.x * 1.90985929965972900390625), float3(12.0));
return hsl.zzz - (fast::clamp(fast::min(ks - float3(3.0), float3(9.0) - ks), float3(-1.0), float3(1.0)) * a);
}
fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], float4 gl_FragCoord [[position]])
{
main0_out out = {};
float coverage = spvDescriptorSet0.uStencilTexture.sample(spvDescriptorSet0.uStencilTextureSmplr, in.vMaskTexCoord).x;
float4 srcRGBA = spvDescriptorSet0.uPaintTexture.sample(spvDescriptorSet0.uPaintTextureSmplr, in.vColorTexCoord);
srcRGBA.w *= coverage;
float2 destTexCoord = gl_FragCoord.xy / (*spvDescriptorSet0.uFramebufferSize);
float4 destRGBA = spvDescriptorSet0.uDest.sample(spvDescriptorSet0.uDestSmplr, destTexCoord);
float3 param = destRGBA.xyz;
float3 destHSL = convertRGBToHSL(param);
float3 param_1 = srcRGBA.xyz;
float3 srcHSL = convertRGBToHSL(param_1);
bool3 blendDest = (*spvDescriptorSet0.uBlendHSL) == int3(0);
float _225;
if (blendDest.x)
{
_225 = destHSL.x;
}
else
{
_225 = srcHSL.x;
}
float _236;
if (blendDest.y)
{
_236 = destHSL.y;
}
else
{
_236 = srcHSL.y;
}
float _247;
if (blendDest.z)
{
_247 = destHSL.z;
}
else
{
_247 = srcHSL.z;
}
float3 blendedHSL = float3(_225, _236, _247);
float3 param_2 = blendedHSL;
float3 blendedRGB = convertHSLToRGB(param_2);
float4 color = float4(((srcRGBA.xyz * (srcRGBA.w * (1.0 - destRGBA.w))) + (blendedRGB * (srcRGBA.w * destRGBA.w))) + (destRGBA.xyz * ((1.0 - srcRGBA.w) * destRGBA.w)), 1.0);
out.oFragColor = color;
return out;
}

View File

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

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
{
constant float2* uTileSize [[id(0)]];
constant float4x4* uTransform [[id(1)]];
};
struct main0_out
{
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.gl_Position = (*spvDescriptorSet0.uTransform) * float4(position, 0.0, 1.0);
return out;
}

View File

@ -23,6 +23,9 @@ SHADERS=\
stencil.vs.glsl \ stencil.vs.glsl \
tile_alpha.fs.glsl \ tile_alpha.fs.glsl \
tile_alpha.vs.glsl \ tile_alpha.vs.glsl \
tile_alpha_hsl.fs.glsl \
tile_copy.fs.glsl \
tile_copy.vs.glsl \
tile_solid.fs.glsl \ tile_solid.fs.glsl \
tile_solid.vs.glsl \ tile_solid.vs.glsl \
$(EMPTY) $(EMPTY)

View File

@ -18,7 +18,6 @@
precision highp float; precision highp float;
uniform sampler2D uSource; uniform sampler2D uSource;
uniform vec2 uSourceSize;
in vec2 vTexCoord; in vec2 vTexCoord;

View File

@ -14,11 +14,10 @@ precision highp float;
uniform sampler2D uStencilTexture; uniform sampler2D uStencilTexture;
uniform sampler2D uPaintTexture; uniform sampler2D uPaintTexture;
uniform vec2 uPaintTextureSize; uniform vec2 uFramebufferSize;
in vec2 vColorTexCoord; in vec2 vColorTexCoord;
in vec2 vMaskTexCoord; in vec2 vMaskTexCoord;
in vec4 vColor;
out vec4 oFragColor; out vec4 oFragColor;

View File

@ -14,7 +14,6 @@ precision highp float;
uniform mat4 uTransform; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uStencilTextureSize;
in ivec2 aTilePosition; in ivec2 aTilePosition;
in vec2 aColorTexCoord; in vec2 aColorTexCoord;

View File

@ -0,0 +1,87 @@
#version 330
// pathfinder/shaders/tile_alpha_hsl.fs.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.
#extension GL_GOOGLE_include_directive : enable
#define BLEND_TERM_DEST 0
#define BLEND_TERM_SRC 1
precision highp float;
uniform sampler2D uStencilTexture;
uniform sampler2D uPaintTexture;
uniform sampler2D uDest;
uniform ivec3 uBlendHSL;
uniform vec2 uFramebufferSize;
in vec2 vColorTexCoord;
in vec2 vMaskTexCoord;
out vec4 oFragColor;
#define PI_2 6.283185307179586
#define DEG_30_INV 1.9098593171027443
#define DEG_60 1.0471975511965976
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative
vec3 convertHSLToRGB(vec3 hsl) {
float a = hsl.y * min(hsl.z, 1.0 - hsl.z);
vec3 ks = mod(vec3(0.0, 8.0, 4.0) + vec3(hsl.x * DEG_30_INV), 12.0);
return hsl.zzz - clamp(min(ks - vec3(3.0), vec3(9.0) - ks), -1.0, 1.0) * a;
}
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
vec3 convertRGBToHSL(vec3 rgb) {
float v = max((rgb.x, rgb.y), rgb.z);
float c = v - min((rgb.x, rgb.y), rgb.z);
float l = v - 0.5 * c;
vec3 tmp = vec3(0.0);
bvec3 is_v = equal(rgb, vec3(v));
if (is_v.r)
tmp = vec3(0.0, rgb.gb);
else if (is_v.g)
tmp = vec3(2.0, rgb.br);
else if (is_v.b)
tmp = vec3(4.0, rgb.rg);
float h = DEG_60 * (tmp.x + (tmp.y - tmp.z) / c);
float s = 0.0;
if (l > 0.0 && l < 1.0)
s = (v - l) / min(l, 1.0 - l);
return vec3(h, s, l);
}
void main() {
float coverage = texture(uStencilTexture, vMaskTexCoord).r;
vec4 srcRGBA = texture(uPaintTexture, vColorTexCoord);
srcRGBA.a *= coverage;
vec2 destTexCoord = gl_FragCoord.xy / uFramebufferSize;
vec4 destRGBA = texture(uDest, destTexCoord);
vec3 destHSL = convertRGBToHSL(destRGBA.rgb);
vec3 srcHSL = convertRGBToHSL(srcRGBA.rgb);
bvec3 blendDest = equal(uBlendHSL, ivec3(BLEND_TERM_DEST));
vec3 blendedHSL = vec3(blendDest.x ? destHSL.x : srcHSL.x,
blendDest.y ? destHSL.y : srcHSL.y,
blendDest.z ? destHSL.z : srcHSL.z);
vec3 blendedRGB = convertHSLToRGB(blendedHSL);
// FIXME(pcwalton): What should the output alpha be here?
vec4 color = vec4(srcRGBA.a * (1.0 - destRGBA.a) * srcRGBA.rgb +
srcRGBA.a * destRGBA.a * blendedRGB +
(1.0 - srcRGBA.a) * destRGBA.a * destRGBA.rgb,
1.0);
oFragColor = color;
}

23
shaders/tile_copy.fs.glsl Normal file
View File

@ -0,0 +1,23 @@
#version 330
// pathfinder/shaders/tile_copy.fs.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;
uniform vec2 uFramebufferSize;
uniform sampler2D uSrc;
out vec4 oFragColor;
void main() {
vec2 texCoord = gl_FragCoord.xy / uFramebufferSize;
oFragColor = texture(uSrc, texCoord);
}

23
shaders/tile_copy.vs.glsl Normal file
View File

@ -0,0 +1,23 @@
#version 330
// pathfinder/shaders/tile_copy.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;
uniform mat4 uTransform;
uniform vec2 uTileSize;
in ivec2 aTilePosition;
void main() {
vec2 position = aTilePosition * uTileSize;
gl_Position = uTransform * vec4(position, 0.0, 1.0);
}

View File

@ -13,7 +13,6 @@
precision highp float; precision highp float;
uniform sampler2D uPaintTexture; uniform sampler2D uPaintTexture;
uniform vec2 uPaintTextureSize;
in vec2 vColorTexCoord; in vec2 vColorTexCoord;