diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ee5ae03 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +**/shader/* linguist-language=glsl diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 3b81590..5771082 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -1,6 +1,8 @@ use piet_gpu_hal::{Error, ImageLayout, Instance, Session, SubmittedCmdBuf}; -use piet_gpu::{render_scene, PietGpuRenderContext, Renderer, HEIGHT, WIDTH}; +use piet_gpu::{render_scene, PietGpuRenderContext, Renderer, HEIGHT, WIDTH, render_svg}; + +use clap::{App, Arg}; use winit::{ event::{Event, WindowEvent}, @@ -11,6 +13,17 @@ use winit::{ const NUM_FRAMES: usize = 2; fn main() -> Result<(), Error> { + let matches = App::new("piet-gpu test") + .arg(Arg::with_name("INPUT").index(1)) + .arg(Arg::with_name("flip").short("f").long("flip")) + .arg( + Arg::with_name("scale") + .short("s") + .long("scale") + .takes_value(true), + ) + .get_matches(); + let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_inner_size(winit::dpi::LogicalSize { @@ -36,7 +49,18 @@ fn main() -> Result<(), Error> { .collect::, Error>>()?; let mut ctx = PietGpuRenderContext::new(); - render_scene(&mut ctx); + if let Some(input) = matches.value_of("INPUT") { + let mut scale = matches + .value_of("scale") + .map(|scale| scale.parse().unwrap()) + .unwrap_or(8.0); + if matches.is_present("flip") { + scale = -scale; + } + render_svg(&mut ctx, input, scale); + } else { + render_scene(&mut ctx); + } let mut renderer = Renderer::new(&session)?; renderer.upload_render_ctx(&mut ctx)?; diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index b09994b..b541893 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -148,12 +148,6 @@ void main() { uint part_start_ix = 0; uint ready_ix = 0; - // Leave room for the fine rasterizer scratch allocation. - Alloc scratch_alloc = slice_mem(cmd_alloc, 0, Alloc_size); - cmd_ref.offset += Alloc_size; - - uint num_begin_slots = 0; - uint begin_slot = 0; bool mem_ok = mem_error == NO_ERROR; while (true) { for (uint i = 0; i < N_SLICE; i++) { @@ -390,8 +384,6 @@ void main() { if (clip_depth < 32) { clip_one_mask &= ~(1 << clip_depth); } - begin_slot++; - num_begin_slots = max(num_begin_slots, begin_slot); } clip_depth++; break; @@ -403,7 +395,6 @@ void main() { } Cmd_Solid_write(cmd_alloc, cmd_ref); cmd_ref.offset += 4; - begin_slot--; Cmd_EndClip_write(cmd_alloc, cmd_ref); cmd_ref.offset += 4; } @@ -431,13 +422,5 @@ void main() { } if (bin_tile_x + tile_x < conf.width_in_tiles && bin_tile_y + tile_y < conf.height_in_tiles) { Cmd_End_write(cmd_alloc, cmd_ref); - if (num_begin_slots > 0) { - // Write scratch allocation: one state per BeginClip per rasterizer chunk. - uint scratch_size = num_begin_slots * TILE_WIDTH_PX * TILE_HEIGHT_PX * CLIP_STATE_SIZE * 4; - MallocResult scratch = malloc(scratch_size); - // Ignore scratch.failed; we don't use the allocation and kernel4 - // checks for memory overflow before using it. - alloc_write(scratch_alloc, scratch_alloc.offset, scratch.alloc); - } } } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 4352ba1..eabb301 100644 Binary files a/piet-gpu/shader/coarse.spv and b/piet-gpu/shader/coarse.spv differ diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index cdb7930..3230c0b 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -35,6 +35,7 @@ layout(rgba8, set = 0, binding = 4) uniform restrict readonly image2D gradients; #include "ptcl.h" #include "tile.h" +#define MAX_BLEND_STACK 128 mediump vec3 tosRGB(mediump vec3 rgb) { bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308)); mediump vec3 below = vec3(12.92)*rgb; @@ -84,13 +85,11 @@ void main() { Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC); CmdRef cmd_ref = CmdRef(cmd_alloc.offset); - // Read scrach space allocation, written first in the command list. - Alloc scratch_alloc = alloc_read(cmd_alloc, cmd_ref.offset); - cmd_ref.offset += Alloc_size; - uvec2 xy_uint = uvec2(gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_WorkGroupID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); vec2 xy = vec2(xy_uint); mediump vec4 rgba[CHUNK]; + uint blend_stack[MAX_BLEND_STACK][CHUNK]; + mediump float blend_alpha_stack[MAX_BLEND_STACK][CHUNK]; for (uint i = 0; i < CHUNK; i++) { rgba[i] = vec4(0.0); } @@ -203,14 +202,12 @@ void main() { cmd_ref.offset += 4 + CmdImage_size; break; case Cmd_BeginClip: - uint base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX + - gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y); for (uint k = 0; k < CHUNK; k++) { - uvec2 offset = chunk_offset(k); - uint srgb = packsRGB(vec4(rgba[k])); - mediump float alpha = clamp(abs(area[k]), 0.0, 1.0); - write_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), srgb); - write_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), floatBitsToUint(alpha)); + // We reject any inputs that might overflow in render_ctx.rs. + // The following is a sanity check so we don't corrupt memory should there be malformed inputs. + uint d = min(clip_depth, MAX_BLEND_STACK - 1); + blend_stack[d][k] = packsRGB(vec4(rgba[k])); + blend_alpha_stack[d][k] = clamp(abs(area[k]), 0.0, 1.0); rgba[k] = vec4(0.0); } clip_depth++; @@ -218,14 +215,10 @@ void main() { break; case Cmd_EndClip: clip_depth--; - base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX + - gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y); for (uint k = 0; k < CHUNK; k++) { - uvec2 offset = chunk_offset(k); - uint srgb = read_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX)); - uint alpha = read_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX)); - mediump vec4 bg = unpacksRGB(srgb); - mediump vec4 fg = rgba[k] * area[k] * uintBitsToFloat(alpha); + uint d = min(clip_depth, MAX_BLEND_STACK - 1); + mediump vec4 bg = unpacksRGB(blend_stack[d][k]); + mediump vec4 fg = rgba[k] * area[k] * blend_alpha_stack[d][k]; rgba[k] = bg * (1.0 - fg.a) + fg; } cmd_ref.offset += 4; diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index e7c1ce5..2e8f752 100644 Binary files a/piet-gpu/shader/kernel4.spv and b/piet-gpu/shader/kernel4.spv differ diff --git a/piet-gpu/shader/kernel4_idx.spv b/piet-gpu/shader/kernel4_idx.spv index 17d2f47..953eae1 100644 Binary files a/piet-gpu/shader/kernel4_idx.spv and b/piet-gpu/shader/kernel4_idx.spv differ diff --git a/piet-gpu/shader/path_coarse.comp b/piet-gpu/shader/path_coarse.comp index 147c8a5..ea525f5 100644 --- a/piet-gpu/shader/path_coarse.comp +++ b/piet-gpu/shader/path_coarse.comp @@ -30,6 +30,7 @@ layout(set = 0, binding = 1) readonly buffer ConfigBuf { #define Q_ACCURACY (ACCURACY * 0.1) #define REM_ACCURACY (ACCURACY - Q_ACCURACY) #define MAX_HYPOT2 (432.0 * Q_ACCURACY * Q_ACCURACY) +#define MAX_QUADS 16 vec2 eval_quad(vec2 p0, vec2 p1, vec2 p2, float t) { float mt = 1.0 - t; @@ -113,6 +114,8 @@ void main() { float err = err_v.x * err_v.x + err_v.y * err_v.y; // The number of quadratics. uint n_quads = max(uint(ceil(pow(err * (1.0 / MAX_HYPOT2), 1.0 / 6.0))), 1); + n_quads = min(n_quads, MAX_QUADS); + SubdivResult keep_params[MAX_QUADS]; // Iterate over quadratics and tote up the estimated number of segments. float val = 0.0; vec2 qp0 = cubic.p0; @@ -123,6 +126,7 @@ void main() { vec2 qp1 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t - 0.5 * step); qp1 = 2.0 * qp1 - 0.5 * (qp0 + qp2); SubdivResult params = estimate_subdiv(qp0, qp1, qp2, sqrt(REM_ACCURACY)); + keep_params[i] = params; val += params.val; qp0 = qp2; @@ -144,7 +148,7 @@ void main() { vec2 qp2 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t); vec2 qp1 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t - 0.5 * step); qp1 = 2.0 * qp1 - 0.5 * (qp0 + qp2); - SubdivResult params = estimate_subdiv(qp0, qp1, qp2, sqrt(REM_ACCURACY)); + SubdivResult params = keep_params[i]; float u0 = approx_parabola_inv_integral(params.a0); float u2 = approx_parabola_inv_integral(params.a2); float uscale = 1.0 / (u2 - u0); diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 08d61a4..d86ff9b 100644 Binary files a/piet-gpu/shader/path_coarse.spv and b/piet-gpu/shader/path_coarse.spv differ diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 6dd447e..a8ccf32 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -34,6 +34,8 @@ const WIDTH_IN_TILES: usize = 128; const HEIGHT_IN_TILES: usize = 96; const PTCL_INITIAL_ALLOC: usize = 1024; +const MAX_BLEND_STACK: usize = 128; + const N_CIRCLES: usize = 0; pub fn render_svg(rc: &mut impl RenderContext, filename: &str, scale: f64) { diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 5f25f1e..23fe3e3 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -8,6 +8,7 @@ use piet::{ Color, Error, FixedGradient, FontFamily, HitTestPoint, ImageFormat, InterpolationMode, IntoBrush, LineMetric, RenderContext, StrokeStyle, Text, TextLayout, TextLayoutBuilder, }; +use crate::MAX_BLEND_STACK; use piet_gpu_types::encoder::{Encode, Encoder}; use piet_gpu_types::scene::{ @@ -215,6 +216,9 @@ impl RenderContext for PietGpuRenderContext { self.elements.push(Element::BeginClip(Clip { bbox: Default::default(), })); + if self.clip_stack.len() >= MAX_BLEND_STACK { + panic!("Maximum clip/blend stack size {} exceeded", MAX_BLEND_STACK); + } self.clip_stack.push(ClipElement { bbox: None, begin_ix,