From a60c2dd3c89ebec22c91b38d3331124ed73f1a0f Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 21 Nov 2020 11:39:23 -0800 Subject: [PATCH] Scratch buffer for clip stack We keep a small window of the clip stack in registers in the fine rasterization kernel, and when that window is exceeded, spill to global memory, so the clip stack can be unbounded. --- piet-gpu-hal/src/lib.rs | 2 +- piet-gpu-hal/src/vulkan.rs | 5 ++- piet-gpu/shader/kernel4.comp | 74 +++++++++++++++++++++++++++++------ piet-gpu/shader/kernel4.spv | Bin 31800 -> 34772 bytes piet-gpu/src/lib.rs | 46 ++++++++++++++++++++-- 5 files changed, 107 insertions(+), 20 deletions(-) diff --git a/piet-gpu-hal/src/lib.rs b/piet-gpu-hal/src/lib.rs index 698ae43..1e04b8d 100644 --- a/piet-gpu-hal/src/lib.rs +++ b/piet-gpu-hal/src/lib.rs @@ -148,7 +148,7 @@ pub trait CmdBuf { /// This is readily supported in Vulkan, but for portability it is remarkably /// tricky (unimplemented in gfx-hal right now). Possibly best to write a compute /// kernel, or organize the code not to need it. - unsafe fn clear_buffer(&self, buffer: &D::Buffer); + unsafe fn clear_buffer(&self, buffer: &D::Buffer, size: Option); unsafe fn copy_buffer(&self, src: &D::Buffer, dst: &D::Buffer); diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index b40576b..53d575b 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -902,9 +902,10 @@ impl crate::CmdBuf for CmdBuf { ); } - unsafe fn clear_buffer(&self, buffer: &Buffer) { + unsafe fn clear_buffer(&self, buffer: &Buffer, size: Option) { let device = &self.device.device; - device.cmd_fill_buffer(self.cmd_buf, buffer.buffer, 0, vk::WHOLE_SIZE, 0); + let size = size.unwrap_or(vk::WHOLE_SIZE); + device.cmd_fill_buffer(self.cmd_buf, buffer.buffer, 0, size, 0); } unsafe fn copy_buffer(&self, src: &Buffer, dst: &Buffer) { diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index a7d5e92..6c98c4b 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -22,13 +22,39 @@ layout(set = 0, binding = 1) buffer TileBuf { uint[] tile; }; -layout(rgba8, set = 0, binding = 2) uniform writeonly image2D image; +layout(set = 0, binding = 2) buffer ClipScratchBuf { + uint[] clip_scratch; +}; + +layout(rgba8, set = 0, binding = 3) uniform writeonly image2D image; #include "ptcl.h" #include "tile.h" #define BLEND_STACK_SIZE 4 +// Layout of clip_scratch buffer: +// [0] is the alloc bump offset (in units of 32 bit words, initially 0) +// Starting at 1 is a sequence of frames. +// Each frame is WIDTH * HEIGHT 32-bit words, then a link reference. + +#define CLIP_LINK_OFFSET (TILE_WIDTH_PX * TILE_HEIGHT_PX) +#define CLIP_BUF_SIZE (CLIP_LINK_OFFSET + 1) + +shared uint sh_clip_alloc; + +// Allocate a scratch buffer for clipping. Unlike offsets in the rest of the code, +// it counts 32-bit words. +uint alloc_clip_buf(uint link) { + if (gl_LocalInvocationID.x == 0 && gl_LocalInvocationID.y == 0) { + uint alloc = atomicAdd(clip_scratch[0], CLIP_BUF_SIZE) + 1; + sh_clip_alloc = alloc; + clip_scratch[alloc + CLIP_LINK_OFFSET] = link; + } + barrier(); + return sh_clip_alloc; +} + // Calculate coverage based on backdrop + coverage of each line segment float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { // Probably better to store as float, but conversion is no doubt cheap. @@ -72,7 +98,9 @@ void main() { vec3 rgb[CHUNK]; float mask[CHUNK]; uint blend_stack[BLEND_STACK_SIZE][CHUNK]; + uint blend_spill = 0; uint blend_sp = 0; + uint clip_tos = 0; for (uint i = 0; i < CHUNK; i++) { rgb[i] = vec3(0.5); mask[i] = 1.0; @@ -142,26 +170,46 @@ void main() { } break; case Cmd_BeginClip: - CmdBeginClip begin_clip = Cmd_BeginClip_read(cmd_ref); - area = computeArea(xy, begin_clip.backdrop, begin_clip.tile_ref); - for (uint k = 0; k < CHUNK; k++) { - blend_stack[blend_sp][k] = packUnorm4x8(vec4(rgb[k], clamp(abs(area[k]), 0.0, 1.0))); - } - blend_sp++; - break; case Cmd_BeginSolidClip: - CmdBeginSolidClip begin_solid_clip = Cmd_BeginSolidClip_read(cmd_ref); - float solid_alpha = begin_solid_clip.alpha; - for (uint k = 0; k < CHUNK; k++) { - blend_stack[blend_sp][k] = packUnorm4x8(vec4(rgb[k], solid_alpha)); + uint blend_slot = blend_sp % BLEND_STACK_SIZE; + if (blend_sp == blend_spill + BLEND_STACK_SIZE) { + // spill to scratch buffer + clip_tos = alloc_clip_buf(clip_tos); + uint base_ix = clip_tos + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; + for (uint k = 0; k < CHUNK; k++) { + clip_scratch[base_ix + k * TILE_WIDTH_PX * CHUNK_DY] = blend_stack[blend_slot][k]; + } + blend_spill++; + } + if (tag == Cmd_BeginClip) { + CmdBeginClip begin_clip = Cmd_BeginClip_read(cmd_ref); + area = computeArea(xy, begin_clip.backdrop, begin_clip.tile_ref); + for (uint k = 0; k < CHUNK; k++) { + blend_stack[blend_slot][k] = packUnorm4x8(vec4(rgb[k], clamp(abs(area[k]), 0.0, 1.0))); + } + } else { + CmdBeginSolidClip begin_solid_clip = Cmd_BeginSolidClip_read(cmd_ref); + float solid_alpha = begin_solid_clip.alpha; + for (uint k = 0; k < CHUNK; k++) { + blend_stack[blend_slot][k] = packUnorm4x8(vec4(rgb[k], solid_alpha)); + } } blend_sp++; break; case Cmd_EndClip: CmdEndClip end_clip = Cmd_EndClip_read(cmd_ref); + blend_slot = (blend_sp - 1) % BLEND_STACK_SIZE; + if (blend_sp == blend_spill) { + uint base_ix = clip_tos + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; + for (uint k = 0; k < CHUNK; k++) { + blend_stack[blend_slot][k] = clip_scratch[base_ix + k * TILE_WIDTH_PX * CHUNK_DY]; + } + clip_tos = clip_scratch[clip_tos + CLIP_LINK_OFFSET]; + blend_spill--; + } blend_sp--; for (uint k = 0; k < CHUNK; k++) { - vec4 rgba = unpackUnorm4x8(blend_stack[blend_sp][k]); + vec4 rgba = unpackUnorm4x8(blend_stack[blend_slot][k]); rgb[k] = mix(rgba.rgb, rgb[k], end_clip.alpha * rgba.a); } break; diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index a02387a44179da376d0a018757f2cfac88fd55e6..538e783526e6b89294d561a56016d15196b149da 100644 GIT binary patch literal 34772 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 dR|2c$d}~uH*U@U+lO4{{xlVXbb=V literal 31800 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 diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 6eff190..0a6152d 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -73,7 +73,8 @@ pub fn render_scene(rc: &mut impl RenderContext) { 5.0, ); //render_cardioid(rc); - render_tiger(rc); + render_clip_test(rc); + //render_tiger(rc); } #[allow(unused)] @@ -94,6 +95,33 @@ fn render_cardioid(rc: &mut impl RenderContext) { rc.stroke(&path, &Color::BLACK, 2.0); } +#[allow(unused)] +fn render_clip_test(rc: &mut impl RenderContext) { + const N: usize = 16; + const X0: f64 = 50.0; + const Y0: f64 = 50.0; + const X1: f64 = 100.0; + const Y1: f64 = 100.0; + let step = 1.0 / ((N + 1) as f64); + for i in 0..N { + let t = ((i + 1) as f64) * step; + rc.save(); + let mut path = BezPath::new(); + path.move_to((X0, Y0)); + path.line_to((X1, Y0)); + path.line_to((X1, Y0 + t * (Y1 - Y0))); + path.line_to((X1 + t * (X0 - X1), Y1)); + path.line_to((X0, Y1)); + path.close_path(); + rc.clip(path); + } + let rect = piet::kurbo::Rect::new(X0, Y0, X1, Y1); + rc.fill(rect, &Color::BLACK); + for _ in 0..N { + rc.restore(); + } +} + fn render_tiger(rc: &mut impl RenderContext) { let xml_str = std::str::from_utf8(include_bytes!("../Ghostscript_Tiger.svg")).unwrap(); let start = std::time::Instant::now(); @@ -163,6 +191,8 @@ pub struct Renderer { coarse_alloc_buf_host: hub::Buffer, coarse_alloc_buf_dev: hub::Buffer, + clip_scratch_buf: hub::Buffer, + k4_pipeline: hub::Pipeline, k4_ds: hub::DescriptorSet, @@ -278,6 +308,8 @@ impl Renderer { &[], )?; + let clip_scratch_buf = session.create_buffer(1024 * 1024, dev)?; + let mut coarse_alloc_buf_host = session.create_buffer(8, host)?; let coarse_alloc_buf_dev = session.create_buffer(8, dev)?; @@ -298,10 +330,14 @@ impl Renderer { )?; let k4_code = include_bytes!("../shader/kernel4.spv"); - let k4_pipeline = session.create_simple_compute_pipeline(k4_code, 2, 1)?; + let k4_pipeline = session.create_simple_compute_pipeline(k4_code, 3, 1)?; let k4_ds = session.create_descriptor_set( &k4_pipeline, - &[ptcl_buf.vk_buffer(), tile_buf.vk_buffer()], + &[ + ptcl_buf.vk_buffer(), + tile_buf.vk_buffer(), + clip_scratch_buf.vk_buffer(), + ], &[image_dev.vk_image()], )?; @@ -335,6 +371,7 @@ impl Renderer { bin_alloc_buf_dev, coarse_alloc_buf_host, coarse_alloc_buf_dev, + clip_scratch_buf, n_elements, n_paths, n_pathseg, @@ -355,7 +392,8 @@ impl Renderer { self.coarse_alloc_buf_host.vk_buffer(), self.coarse_alloc_buf_dev.vk_buffer(), ); - cmd_buf.clear_buffer(self.state_buf.vk_buffer()); + cmd_buf.clear_buffer(self.state_buf.vk_buffer(), None); + cmd_buf.clear_buffer(self.clip_scratch_buf.vk_buffer(), Some(4)); cmd_buf.memory_barrier(); cmd_buf.image_barrier( self.image_dev.vk_image(),