From 753b97c3420c523c32dd579c15d10e87bea1b9c7 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 11 Apr 2022 05:30:08 -0400 Subject: [PATCH 01/13] Rebase on radial branch --- Cargo.lock | 12 +- Cargo.toml | 1 + piet-gpu-types/src/ptcl.rs | 14 +- piet-gpu/bin/cli.rs | 8 +- piet-gpu/bin/winit.rs | 32 +- piet-gpu/shader/coarse.comp | 22 +- piet-gpu/shader/draw_leaf.comp | 36 +- piet-gpu/shader/drawtag.h | 5 +- piet-gpu/shader/gen/binning.dxil | Bin 6336 -> 6336 bytes piet-gpu/shader/gen/coarse.dxil | Bin 10544 -> 11652 bytes piet-gpu/shader/gen/coarse.hlsl | 505 +++++++++++++++--------- piet-gpu/shader/gen/coarse.msl | 544 ++++++++++++++++---------- piet-gpu/shader/gen/coarse.spv | Bin 52244 -> 58964 bytes piet-gpu/shader/gen/draw_leaf.dxil | Bin 6072 -> 6760 bytes piet-gpu/shader/gen/draw_leaf.hlsl | 128 +++--- piet-gpu/shader/gen/draw_leaf.msl | 112 ++++-- piet-gpu/shader/gen/draw_leaf.spv | Bin 16412 -> 20104 bytes piet-gpu/shader/gen/draw_reduce.dxil | Bin 4256 -> 4264 bytes piet-gpu/shader/gen/draw_reduce.hlsl | 26 +- piet-gpu/shader/gen/draw_reduce.msl | 18 +- piet-gpu/shader/gen/draw_reduce.spv | Bin 7124 -> 7140 bytes piet-gpu/shader/gen/draw_root.dxil | Bin 4468 -> 4468 bytes piet-gpu/shader/gen/kernel4.dxil | Bin 14236 -> 15108 bytes piet-gpu/shader/gen/kernel4.hlsl | 288 +++++++++----- piet-gpu/shader/gen/kernel4.msl | 335 ++++++++++------ piet-gpu/shader/gen/kernel4.spv | Bin 58596 -> 65556 bytes piet-gpu/shader/gen/kernel4_gray.dxil | Bin 14140 -> 15016 bytes piet-gpu/shader/gen/kernel4_gray.hlsl | 286 +++++++++----- piet-gpu/shader/gen/kernel4_gray.msl | 333 ++++++++++------ piet-gpu/shader/gen/kernel4_gray.spv | Bin 58352 -> 65312 bytes piet-gpu/shader/gen/tile_alloc.dxil | Bin 5132 -> 5136 bytes piet-gpu/shader/kernel4.comp | 19 +- piet-gpu/shader/ptcl.h | 77 +++- piet-gpu/src/encoder.rs | 30 +- piet-gpu/src/gradient.rs | 51 ++- piet-gpu/src/lib.rs | 3 +- piet-gpu/src/render_ctx.rs | 25 +- piet-gpu/src/test_scenes.rs | 29 +- piet-scene/Cargo.toml | 9 + piet-scene/src/brush/color.rs | 62 +++ piet-scene/src/brush/gradient.rs | 87 ++++ piet-scene/src/brush/image.rs | 96 +++++ piet-scene/src/brush/mod.rs | 35 ++ piet-scene/src/geometry.rs | 189 +++++++++ piet-scene/src/lib.rs | 21 + piet-scene/src/main.rs | 30 ++ piet-scene/src/path.rs | 311 +++++++++++++++ piet-scene/src/resource/mod.rs | 37 ++ piet-scene/src/resource/ramp_cache.rs | 138 +++++++ piet-scene/src/scene/blend.rs | 102 +++++ piet-scene/src/scene/builder.rs | 433 ++++++++++++++++++++ piet-scene/src/scene/mod.rs | 97 +++++ piet-scene/src/scene/style.rs | 71 ++++ 53 files changed, 3653 insertions(+), 1004 deletions(-) create mode 100644 piet-scene/Cargo.toml create mode 100644 piet-scene/src/brush/color.rs create mode 100644 piet-scene/src/brush/gradient.rs create mode 100644 piet-scene/src/brush/image.rs create mode 100644 piet-scene/src/brush/mod.rs create mode 100644 piet-scene/src/geometry.rs create mode 100644 piet-scene/src/lib.rs create mode 100644 piet-scene/src/main.rs create mode 100644 piet-scene/src/path.rs create mode 100644 piet-scene/src/resource/mod.rs create mode 100644 piet-scene/src/resource/ramp_cache.rs create mode 100644 piet-scene/src/scene/blend.rs create mode 100644 piet-scene/src/scene/builder.rs create mode 100644 piet-scene/src/scene/mod.rs create mode 100644 piet-scene/src/scene/style.rs diff --git a/Cargo.lock b/Cargo.lock index 737c033..78b6326 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -949,6 +949,14 @@ dependencies = [ "piet-gpu-derive", ] +[[package]] +name = "piet-scene" +version = "0.1.0" +dependencies = [ + "bytemuck", + "smallvec", +] + [[package]] name = "pkg-config" version = "0.3.22" @@ -1139,9 +1147,9 @@ checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" [[package]] name = "smallvec" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "smithay-client-toolkit" diff --git a/Cargo.toml b/Cargo.toml index bfa0030..b94b82b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,6 @@ members = [ "piet-gpu-derive", "piet-gpu-hal", "piet-gpu-types", + "piet-scene", "tests" ] diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index e8c29c3..14831ca 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -24,6 +24,14 @@ piet_gpu! { line_y: f32, line_c: f32, } + struct CmdRadGrad { + index: u32, + mat: [f32; 4], + xlat: [f32; 2], + c1: [f32; 2], + ra: f32, + roff: f32, + } struct CmdImage { index: u32, offset: [i16; 2], @@ -31,6 +39,9 @@ piet_gpu! { struct CmdAlpha { alpha: f32, } + struct CmdEndClip { + blend: u32, + } struct CmdJump { new_ref: u32, } @@ -42,9 +53,10 @@ piet_gpu! { Alpha(CmdAlpha), Color(CmdColor), LinGrad(CmdLinGrad), + RadGrad(CmdRadGrad), Image(CmdImage), BeginClip, - EndClip, + EndClip(CmdEndClip), Jump(CmdJump), } } diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index abe6ae1..70023af 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -6,7 +6,7 @@ use clap::{App, Arg}; use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session}; -use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, Renderer}; +use piet_gpu::{test_scenes, PietGpuRenderContext, Renderer}; const WIDTH: usize = 2048; const HEIGHT: usize = 1536; @@ -243,11 +243,7 @@ fn main() -> Result<(), Error> { if matches.is_present("flip") { scale = -scale; } - let xml_str = std::fs::read_to_string(input).unwrap(); - let start = std::time::Instant::now(); - let svg = PicoSvg::load(&xml_str, scale).unwrap(); - println!("parsing time: {:?}", start.elapsed()); - test_scenes::render_svg(&mut ctx, &svg); + test_scenes::render_svg(&mut ctx, input, scale); } else { test_scenes::render_scene(&mut ctx); } diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index b1db5e0..3ca0742 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -2,7 +2,7 @@ use piet::kurbo::Point; use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; use piet_gpu_hal::{CmdBuf, Error, ImageLayout, Instance, Session, SubmittedCmdBuf}; -use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, Renderer}; +use piet_gpu::{test_scenes, PietGpuRenderContext, Renderer}; use clap::{App, Arg}; @@ -29,25 +29,6 @@ fn main() -> Result<(), Error> { ) .get_matches(); - // Collect SVG if input - let svg = match matches.value_of("INPUT") { - Some(file) => { - let mut scale = matches - .value_of("scale") - .map(|scale| scale.parse().unwrap()) - .unwrap_or(8.0); - if matches.is_present("flip") { - scale = -scale; - } - let xml_str = std::fs::read_to_string(file).unwrap(); - let start = std::time::Instant::now(); - let svg = PicoSvg::load(&xml_str, scale).unwrap(); - println!("parsing time: {:?}", start.elapsed()); - Some(svg) - } - None => None, - }; - let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_inner_size(winit::dpi::LogicalSize { @@ -125,8 +106,15 @@ fn main() -> Result<(), Error> { } let mut ctx = PietGpuRenderContext::new(); - if let Some(svg) = &svg { - test_scenes::render_svg(&mut ctx, svg); + 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; + } + test_scenes::render_svg(&mut ctx, input, scale); } else { use piet_gpu::{Blend, BlendMode::*, CompositionMode::*}; let blends = [ diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 454371c..adbedfd 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -229,6 +229,7 @@ void main() { case Drawtag_FillColor: case Drawtag_FillImage: case Drawtag_FillLinGradient: + case Drawtag_FillRadGradient: case Drawtag_BeginClip: case Drawtag_EndClip: uint drawmonoid_base = drawmonoid_start + 4 * element_ix; @@ -305,7 +306,7 @@ void main() { is_blend = (blend != BlendComp_default); } include_tile = tile.tile.offset != 0 || (tile.backdrop == 0) == is_clip - || is_blend; + || (is_clip && is_blend); } if (include_tile) { uint el_slice = el_ix / 32; @@ -373,6 +374,25 @@ void main() { Cmd_LinGrad_write(cmd_alloc, cmd_ref, cmd_lin); cmd_ref.offset += 4 + CmdLinGrad_size; break; + case Drawtag_FillRadGradient: + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { + break; + } + linewidth = uintBitsToFloat(memory[di]); + write_fill(cmd_alloc, cmd_ref, tile, linewidth); + CmdRadGrad cmd_rad; + cmd_rad.index = scene[dd]; + // Given that this is basically a memcpy, we might consider + // letting the fine raster read the info itself. + cmd_rad.mat = uintBitsToFloat(uvec4(memory[di + 1], memory[di + 2], + memory[di + 3], memory[di + 4])); + cmd_rad.xlat = uintBitsToFloat(uvec2(memory[di + 5], memory[di + 6])); + cmd_rad.c1 = uintBitsToFloat(uvec2(memory[di + 7], memory[di + 8])); + cmd_rad.ra = uintBitsToFloat(memory[di + 9]); + cmd_rad.roff = uintBitsToFloat(memory[di + 10]); + Cmd_RadGrad_write(cmd_alloc, cmd_ref, cmd_rad); + cmd_ref.offset += 4 + CmdRadGrad_size; + break; case Drawtag_FillImage: linewidth = uintBitsToFloat(memory[di]); if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { diff --git a/piet-gpu/shader/draw_leaf.comp b/piet-gpu/shader/draw_leaf.comp index 1cee0ef..ef369c9 100644 --- a/piet-gpu/shader/draw_leaf.comp +++ b/piet-gpu/shader/draw_leaf.comp @@ -94,8 +94,8 @@ void main() { // pipeline. However, going forward we'll get rid of that, and have // later stages read scene + bbox etc. tag_word = scene[drawtag_base + ix + i]; - if (tag_word == Drawtag_FillColor || tag_word == Drawtag_FillLinGradient || tag_word == Drawtag_FillImage || - tag_word == Drawtag_BeginClip) { + if (tag_word == Drawtag_FillColor || tag_word == Drawtag_FillLinGradient || tag_word == Drawtag_FillRadGradient || + tag_word == Drawtag_FillImage || tag_word == Drawtag_BeginClip) { uint bbox_offset = (conf.path_bbox_alloc.offset >> 2) + 6 * m.path_ix; float bbox_l = float(memory[bbox_offset]) - 32768.0; float bbox_t = float(memory[bbox_offset + 1]) - 32768.0; @@ -106,11 +106,11 @@ void main() { uint fill_mode = uint(linewidth >= 0.0); vec4 mat; vec2 translate; - if (linewidth >= 0.0 || tag_word == Drawtag_FillLinGradient) { + if (linewidth >= 0.0 || tag_word == Drawtag_FillLinGradient || tag_word == Drawtag_FillRadGradient) { uint trans_ix = memory[bbox_offset + 5]; uint t = (conf.trans_alloc.offset >> 2) + 6 * trans_ix; mat = uintBitsToFloat(uvec4(memory[t], memory[t + 1], memory[t + 2], memory[t + 3])); - if (tag_word == Drawtag_FillLinGradient) { + if (tag_word == Drawtag_FillLinGradient || tag_word == Drawtag_FillRadGradient) { translate = uintBitsToFloat(uvec2(memory[t + 4], memory[t + 5])); } } @@ -125,7 +125,6 @@ void main() { break; case Drawtag_FillLinGradient: memory[di] = floatBitsToUint(linewidth); - uint index = scene[dd]; vec2 p0 = uintBitsToFloat(uvec2(scene[dd + 1], scene[dd + 2])); vec2 p1 = uintBitsToFloat(uvec2(scene[dd + 3], scene[dd + 4])); p0 = mat.xy * p0.x + mat.zw * p0.y + translate; @@ -139,6 +138,33 @@ void main() { memory[di + 2] = floatBitsToUint(line_y); memory[di + 3] = floatBitsToUint(line_c); break; + case Drawtag_FillRadGradient: + p0 = uintBitsToFloat(uvec2(scene[dd + 1], scene[dd + 2])); + p1 = uintBitsToFloat(uvec2(scene[dd + 3], scene[dd + 4])); + float r0 = uintBitsToFloat(scene[dd + 5]); + float r1 = uintBitsToFloat(scene[dd + 6]); + float inv_det = 1.0 / (mat.x * mat.w - mat.y * mat.z); + vec4 inv_mat = inv_det * vec4(mat.w, -mat.y, -mat.z, mat.x); + vec2 inv_tr = inv_mat.xz * translate.x + inv_mat.yw * translate.y; + inv_tr += p0; + vec2 center1 = p1 - p0; + float rr = r1 / (r1 - r0); + float rainv = rr / (r1 * r1 - dot(center1, center1)); + vec2 c1 = center1 * rainv; + float ra = rr * rainv; + float roff = rr - 1.0; + memory[di] = floatBitsToUint(linewidth); + memory[di + 1] = floatBitsToUint(inv_mat.x); + memory[di + 2] = floatBitsToUint(inv_mat.y); + memory[di + 3] = floatBitsToUint(inv_mat.z); + memory[di + 4] = floatBitsToUint(inv_mat.w); + memory[di + 5] = floatBitsToUint(inv_tr.x); + memory[di + 6] = floatBitsToUint(inv_tr.y); + memory[di + 7] = floatBitsToUint(c1.x); + memory[di + 8] = floatBitsToUint(c1.y); + memory[di + 9] = floatBitsToUint(ra); + memory[di + 10] = floatBitsToUint(roff); + break; case Drawtag_BeginClip: break; } diff --git a/piet-gpu/shader/drawtag.h b/piet-gpu/shader/drawtag.h index 7f73546..1e35318 100644 --- a/piet-gpu/shader/drawtag.h +++ b/piet-gpu/shader/drawtag.h @@ -4,11 +4,12 @@ // Design of draw tag: & 0x1c gives scene size in bytes // & 1 gives clip -// (tag >> 4) & 0x1c is info size in bytes +// (tag >> 4) & 0x3c is info size in bytes #define Drawtag_Nop 0 #define Drawtag_FillColor 0x44 #define Drawtag_FillLinGradient 0x114 +#define Drawtag_FillRadGradient 0x2dc #define Drawtag_FillImage 0x48 #define Drawtag_BeginClip 0x05 #define Drawtag_EndClip 0x25 @@ -36,5 +37,5 @@ DrawMonoid combine_draw_monoid(DrawMonoid a, DrawMonoid b) { DrawMonoid map_tag(uint tag_word) { // TODO: at some point, EndClip should not generate a path uint has_path = uint(tag_word != Drawtag_Nop); - return DrawMonoid(has_path, tag_word & 1, tag_word & 0x1c, (tag_word >> 4) & 0x1c); + return DrawMonoid(has_path, tag_word & 1, tag_word & 0x1c, (tag_word >> 4) & 0x3c); } diff --git a/piet-gpu/shader/gen/binning.dxil b/piet-gpu/shader/gen/binning.dxil index 3050aa83bdb31b0655c4e6a2c8d80987840b6532..4a4f0734c4735cdf4f37ec0ade7e7e7cacccabbc 100644 GIT binary patch delta 2206 zcmZ8ieNYo;8h4vW=hjc?DaB6R|5NWEwfHF#mI!gc#6)|VMYbiam z2@q2ZHO%4{p1sA=k+!sk)1f%$9|<6bbJ~LE&EOqggXPrg^m<^=n|8F*+m%3XZvV+X z&+qqq{od!@gp47h+EQIq4F2yKQeQ=J)mIlzS6=^paLciKhi*Zop-U0eE^$c_=GJ$i z{%_nzH^%-Lg|#G-KoYfyM;T@SM-6J;#sWNQFq2;ZXxRt_VkHtXim-;`Fl>$*p!gja zWtR=_MjZ~MQELO%Vj=+=wTMTD%`3^Nc}V)dkWZ+z99HiJK*2$*ZMhS?iQpy=nocN!vV5dc~U0Jd{Rtjwi?*}qVv%t%wxw?i;Ake@L=ijPgvdsDhp-T(coi6aXzL;?~PtoY-)gR zPZ(}?z5SW}tUY;Fzw3;C)g)1KI_2H>FXtBy=LPys^_|e**?l~(2)hbsYg^M;#7z@T z#CjvzrWhmqG!ejCTkHNx^|*=lus4j|Ah9eI4AbNkAE!UN>+F_f+Zp}c9&(__`@pdy ztKigs?&oKcy@9zaGaW;BPFydO`PbLKp8INJZGFR`vV-*vP1`*ESkgPkcHWt_pPQ|| zGuy*_Q0#rM^*&be{HfT-C-0XO6lBg^qL6?@SxW-&yl~1YxF$zD#S|_RFLfp1JEWE_ z&;!=h!Hpk(b3e(iDtu-&!h3(lZo)26ap54O42*)RUm(F! zU!QiffygH%+X#036wn%HN7uk(h=q-lR=koNO7rftb}%z6T2%}7RZ6SJgYpk?Opil9 z!OZLSgJZ-SeHu@#G|kxo4IJgn%50q^b(SvUotje0!HPXHjJ%(@S;V=OI+ruckfw-D*cE|@wSdw6&!jU2ZzDV`rVAG()PY4U?fe3$bPJkxm@5&%* z7WYYs8+mHWO7i&24Dwoq2v%4McgC~;4OZb^+ZZzuV5qaUotTS_|Y`i-6{8 z+jcx*rK!Y!JLBk={8?})rpy+MTUb+<%8UcZjAJ1t^O0L4`8837vhv>F0c~X4mW?ap zmwsEE1OFh7uQN5!YeN^vBdk&i1DXPDy@bq;S{)_>$Zh0eRSFlBSW)0`iV~Msx5Izx zB2omk-09lK?BR4IRv10v?|2En>kC7Iw1xF_VmlS3+j?NbvbawzTb@cbjEoB^q)3`% z7l3A@eLGN5jwvy-!>oOwHXbU&O|;516#ogZk8KJ0XsM9V<#XG{;q`F7VWz!o#X%& zb(+(wvt8ZVCr<4?qadAT3a)bM3ZvV%pgXXb&^V_R&k8Cx$F#ba#3B1iYAD{gq{!BN z{hJv7l7Rim`@?XQ^U*{NJh>#Gb#Pjnus6!NoqRw^L^)R`>>I;A{WF!V7Z9L~TkfDH2_05e zRhZwqwvP*F2y{q9dhA!Zpnya~NbYHTiU?l=rBWt*4H$x*^VV8I%*i>MC++DMC74!_H&Ubg+}f46ru)gr zab`RMgnhlN=~MhUbbPuU4Dk89=gCwyMl_?a@pJ>z%ZxMPO6RJR)+EeSE7gk$KQwmR zJbQRgn;zlLX>(nMJpB_xnkYR#r{5u56Q6IpIc<5j*!1-CdZ@E{wI^-$BYmb1UEYv+ z^sTQ-0%_LHerqTDhb6Ic9QU64p580*&w0ViLf=rQCweoasg>P-8|c;~30KPIY2+n1 zIiJ*wz7AvDW*rIjar<@pv>4o-CRt^q01mfhlpoFzhB&$oYO^69#+9a6VOYa+13JTV z2M=j_WL1T8$eXkt6-ZFT-caVFid(lO-tu0RUhflg?6U-kM;JRQ#z+;@r zs|piMNkIl1#_SYgM_!-=+HMhjcuaI2Q3@foCXpE^5eVMfs1VL4i&4ANg`CnRkvz~P xl82v>3q6cK2&oPwm@CLW3vkQ1*X;+ahX zX{u1dt!){wy&zcW6njA}jvYFaK(6MhT*cBGu0vb6<8FFa&jP16(>boaU5WJepWWyA z{hsG}pWpku@A`B6InvVF;*z3!w?2Q$@9B+keEFa~aC&WAI*H;&bHVg;<~M#gcw6;w zn5oFVwusjAZ2z+Fyv1i;jZm#6fXzp{ypslK*rY-Xfbg*4_Z)_J`mjN(d-?Da1C*#x zlnx`nRV2m%6t=K-F$s8KgIF``0Twp2w&htGkYS4oeF{PPu;KSaVE+##AwPzB1Z=iw zmZ($pOXEb9o^E04vme1Fyb22T5+7g0BRyU;EfGq?|7--I3lMn%q1|yne2B~4P5|&z z_8rf!^ca~!tjwlFf@CUs$S3M2!JMnpB)1X-ZywK;z`2X5=!FT3`mHV!K^!Dif;W>1 zLXe80hKt!*GtCYU@+TM?O-1SVEq<4UZ>ZfjG33pl?iGyOoLy}{+P2HtF`U^p%#SOH zVh-lM_DAH_UPSj$cl|1_I7KSGu3kw+kJ#vD;)7UhJ7y;6fXjdd0aot*@Wb(DB1B?- zYTAS}07L>Weoo?tOT;}pFYMw@GEa1gDer?bf^)8MnaVQhh=QV+I#Hk^f!@}3Jc0r?;8NDb& zQLI`tR1LMtDz#R2)KG+?qApQ04#qB0*yzYO<;YZHF*v_FYTwJ*=4hzK8@V6K^t&M* zO%gOzFj-8%KJ5Sw%6zO=AoBl`on3wSL zVSnDzrvE!S<`h1{)Qhui--1XVh~U@za z=}r|D1dtwQM=#}}UFP@|QH9b`16P53@%eK_o9$Pwmt?_Y4%@yWx51F@zwA9ji})}w zXn}Kp6xO2^)?78%+Q>vmqHQ9vDc{=4NnBm)hCl5g5?HnDN%DWwCz6l|w)MsC-#vqW z;SNBov;{xkE$-BY>2>|E`iZ#fox9eHpPme|DkSilgqMMAvU4vk(OU0vnXP)c))Hf= z!t-`Z^2TFsfO>wV-)-Qt8EtNRT?pO*=c^ZjRSTt0U*I*#GNB|SnypMCjRwg-@a0aA zq_9u&ZEKY8mv`W9B9~hrG1^t-EAdIDs_zG@gU+1~$lr+ge6~e;c`Dfv1#}szq_h>%8)u!TjTi>t0GK-W8oCvP#yUKo2;FwBF@47pqm*XYhUEXDdk=f%-7s(VAgk@%(n9Ohg>>fz9a zfaPOL^4KF`&Rl$phzN6TBIcriTlrY!^4~%VNVm``rLiHEEvA7M+xQQE50^oBnG}`kFGO8+|e%%jK`!H+nVhv!m{_^sNoCvaD-c zzBe4GgJMg+)7^H5mz*b*_hnF+2tFaSJaXvjZfz^{pJ@QlC6{A*j4Z)5hqB?`crNNd5}T9X#F0mPIP%0}az001c}!m6$fSxRr~ZLs dmz;qce3!I+Ud+91Ubk4zWz-3S?4Kh1{0|^9yNdt- diff --git a/piet-gpu/shader/gen/coarse.dxil b/piet-gpu/shader/gen/coarse.dxil index 12e88dde300ffb33e17ea6c31c7c14b60812b006..879b7c8390fc9b8adc5e4950306e92c00760dfe2 100644 GIT binary patch delta 8239 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 delta 7124 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 uint(16), raw1 & 65535u, raw1 >> uint(16)); - TileRef _391 = { raw2 }; - s.tiles = _391; + TileRef _409 = { raw2 }; + s.tiles = _409; return s; } @@ -255,11 +270,11 @@ void write_tile_alloc(uint el_ix, Alloc a) Alloc read_tile_alloc(uint el_ix, bool mem_ok) { - uint _741; - _242.GetDimensions(_741); - _741 = (_741 - 8) / 4; + uint _892; + _260.GetDimensions(_892); + _892 = (_892 - 8) / 4; uint param = 0u; - uint param_1 = uint(int(_741) * 4); + uint param_1 = uint(int(_892) * 4); bool param_2 = mem_ok; return new_alloc(param, param_1, param_2); } @@ -273,31 +288,31 @@ Tile Tile_read(Alloc a, TileRef ref) Alloc param_2 = a; uint param_3 = ix + 1u; uint raw1 = read_mem(param_2, param_3); - TileSegRef _416 = { raw0 }; + TileSegRef _434 = { raw0 }; Tile s; - s.tile = _416; + s.tile = _434; s.backdrop = int(raw1); return s; } MallocResult malloc(uint size) { - uint _248; - _242.InterlockedAdd(0, size, _248); - uint offset = _248; - uint _255; - _242.GetDimensions(_255); - _255 = (_255 - 8) / 4; + uint _266; + _260.InterlockedAdd(0, size, _266); + uint offset = _266; + uint _273; + _260.GetDimensions(_273); + _273 = (_273 - 8) / 4; MallocResult r; - r.failed = (offset + size) > uint(int(_255) * 4); + r.failed = (offset + size) > uint(int(_273) * 4); uint param = offset; uint param_1 = size; bool param_2 = !r.failed; r.alloc = new_alloc(param, param_1, param_2); if (r.failed) { - uint _277; - _242.InterlockedMax(4, 1u, _277); + uint _295; + _260.InterlockedMax(4, 1u, _295); return r; } return r; @@ -311,7 +326,7 @@ void write_mem(Alloc alloc, uint offset, uint val) { return; } - _242.Store(offset * 4 + 8, val); + _260.Store(offset * 4 + 8, val); } void CmdJump_write(Alloc a, CmdJumpRef ref, CmdJump s) @@ -327,11 +342,11 @@ void Cmd_Jump_write(Alloc a, CmdRef ref, CmdJump s) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint param_2 = 10u; + uint param_2 = 11u; write_mem(param, param_1, param_2); - CmdJumpRef _734 = { ref.offset + 4u }; + CmdJumpRef _885 = { ref.offset + 4u }; Alloc param_3 = a; - CmdJumpRef param_4 = _734; + CmdJumpRef param_4 = _885; CmdJump param_5 = s; CmdJump_write(param_3, param_4, param_5); } @@ -343,22 +358,22 @@ bool alloc_cmd(inout Alloc cmd_alloc, inout CmdRef cmd_ref, inout uint cmd_limit return true; } uint param = 1024u; - MallocResult _762 = malloc(param); - MallocResult new_cmd = _762; + MallocResult _913 = malloc(param); + MallocResult new_cmd = _913; if (new_cmd.failed) { return false; } - CmdJump _772 = { new_cmd.alloc.offset }; - CmdJump jump = _772; + CmdJump _923 = { new_cmd.alloc.offset }; + CmdJump jump = _923; Alloc param_1 = cmd_alloc; CmdRef param_2 = cmd_ref; CmdJump param_3 = jump; Cmd_Jump_write(param_1, param_2, param_3); cmd_alloc = new_cmd.alloc; - CmdRef _784 = { cmd_alloc.offset }; - cmd_ref = _784; - cmd_limit = (cmd_alloc.offset + 1024u) - 60u; + CmdRef _935 = { cmd_alloc.offset }; + cmd_ref = _935; + cmd_limit = (cmd_alloc.offset + 1024u) - 144u; return true; } @@ -381,9 +396,9 @@ void Cmd_Fill_write(Alloc a, CmdRef ref, CmdFill s) uint param_1 = ref.offset >> uint(2); uint param_2 = 1u; write_mem(param, param_1, param_2); - CmdFillRef _604 = { ref.offset + 4u }; + CmdFillRef _742 = { ref.offset + 4u }; Alloc param_3 = a; - CmdFillRef param_4 = _604; + CmdFillRef param_4 = _742; CmdFill param_5 = s; CmdFill_write(param_3, param_4, param_5); } @@ -415,9 +430,9 @@ void Cmd_Stroke_write(Alloc a, CmdRef ref, CmdStroke s) uint param_1 = ref.offset >> uint(2); uint param_2 = 2u; write_mem(param, param_1, param_2); - CmdStrokeRef _622 = { ref.offset + 4u }; + CmdStrokeRef _760 = { ref.offset + 4u }; Alloc param_3 = a; - CmdStrokeRef param_4 = _622; + CmdStrokeRef param_4 = _760; CmdStroke param_5 = s; CmdStroke_write(param_3, param_4, param_5); } @@ -428,8 +443,8 @@ void write_fill(Alloc alloc, inout CmdRef cmd_ref, Tile tile, float linewidth) { if (tile.tile.offset != 0u) { - CmdFill _807 = { tile.tile.offset, tile.backdrop }; - CmdFill cmd_fill = _807; + CmdFill _958 = { tile.tile.offset, tile.backdrop }; + CmdFill cmd_fill = _958; Alloc param = alloc; CmdRef param_1 = cmd_ref; CmdFill param_2 = cmd_fill; @@ -446,8 +461,8 @@ void write_fill(Alloc alloc, inout CmdRef cmd_ref, Tile tile, float linewidth) } else { - CmdStroke _837 = { tile.tile.offset, 0.5f * linewidth }; - CmdStroke cmd_stroke = _837; + CmdStroke _988 = { tile.tile.offset, 0.5f * linewidth }; + CmdStroke cmd_stroke = _988; Alloc param_5 = alloc; CmdRef param_6 = cmd_ref; CmdStroke param_7 = cmd_stroke; @@ -471,9 +486,9 @@ void Cmd_Color_write(Alloc a, CmdRef ref, CmdColor s) uint param_1 = ref.offset >> uint(2); uint param_2 = 5u; write_mem(param, param_1, param_2); - CmdColorRef _649 = { ref.offset + 4u }; + CmdColorRef _786 = { ref.offset + 4u }; Alloc param_3 = a; - CmdColorRef param_4 = _649; + CmdColorRef param_4 = _786; CmdColor param_5 = s; CmdColor_write(param_3, param_4, param_5); } @@ -505,13 +520,75 @@ void Cmd_LinGrad_write(Alloc a, CmdRef ref, CmdLinGrad s) uint param_1 = ref.offset >> uint(2); uint param_2 = 6u; write_mem(param, param_1, param_2); - CmdLinGradRef _668 = { ref.offset + 4u }; + CmdLinGradRef _804 = { ref.offset + 4u }; Alloc param_3 = a; - CmdLinGradRef param_4 = _668; + CmdLinGradRef param_4 = _804; CmdLinGrad param_5 = s; CmdLinGrad_write(param_3, param_4, param_5); } +void CmdRadGrad_write(Alloc a, CmdRadGradRef ref, CmdRadGrad s) +{ + uint ix = ref.offset >> uint(2); + Alloc param = a; + uint param_1 = ix + 0u; + uint param_2 = s.index; + write_mem(param, param_1, param_2); + Alloc param_3 = a; + uint param_4 = ix + 1u; + uint param_5 = asuint(s.mat.x); + write_mem(param_3, param_4, param_5); + Alloc param_6 = a; + uint param_7 = ix + 2u; + uint param_8 = asuint(s.mat.y); + write_mem(param_6, param_7, param_8); + Alloc param_9 = a; + uint param_10 = ix + 3u; + uint param_11 = asuint(s.mat.z); + write_mem(param_9, param_10, param_11); + Alloc param_12 = a; + uint param_13 = ix + 4u; + uint param_14 = asuint(s.mat.w); + write_mem(param_12, param_13, param_14); + Alloc param_15 = a; + uint param_16 = ix + 5u; + uint param_17 = asuint(s.xlat.x); + write_mem(param_15, param_16, param_17); + Alloc param_18 = a; + uint param_19 = ix + 6u; + uint param_20 = asuint(s.xlat.y); + write_mem(param_18, param_19, param_20); + Alloc param_21 = a; + uint param_22 = ix + 7u; + uint param_23 = asuint(s.c1.x); + write_mem(param_21, param_22, param_23); + Alloc param_24 = a; + uint param_25 = ix + 8u; + uint param_26 = asuint(s.c1.y); + write_mem(param_24, param_25, param_26); + Alloc param_27 = a; + uint param_28 = ix + 9u; + uint param_29 = asuint(s.ra); + write_mem(param_27, param_28, param_29); + Alloc param_30 = a; + uint param_31 = ix + 10u; + uint param_32 = asuint(s.roff); + write_mem(param_30, param_31, param_32); +} + +void Cmd_RadGrad_write(Alloc a, CmdRef ref, CmdRadGrad s) +{ + Alloc param = a; + uint param_1 = ref.offset >> uint(2); + uint param_2 = 7u; + write_mem(param, param_1, param_2); + CmdRadGradRef _822 = { ref.offset + 4u }; + Alloc param_3 = a; + CmdRadGradRef param_4 = _822; + CmdRadGrad param_5 = s; + CmdRadGrad_write(param_3, param_4, param_5); +} + void CmdImage_write(Alloc a, CmdImageRef ref, CmdImage s) { uint ix = ref.offset >> uint(2); @@ -529,11 +606,11 @@ void Cmd_Image_write(Alloc a, CmdRef ref, CmdImage s) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint param_2 = 7u; + uint param_2 = 8u; write_mem(param, param_1, param_2); - CmdImageRef _687 = { ref.offset + 4u }; + CmdImageRef _840 = { ref.offset + 4u }; Alloc param_3 = a; - CmdImageRef param_4 = _687; + CmdImageRef param_4 = _840; CmdImage param_5 = s; CmdImage_write(param_3, param_4, param_5); } @@ -542,7 +619,7 @@ void Cmd_BeginClip_write(Alloc a, CmdRef ref) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint param_2 = 8u; + uint param_2 = 9u; write_mem(param, param_1, param_2); } @@ -559,11 +636,11 @@ void Cmd_EndClip_write(Alloc a, CmdRef ref, CmdEndClip s) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint param_2 = 9u; + uint param_2 = 10u; write_mem(param, param_1, param_2); - CmdEndClipRef _715 = { ref.offset + 4u }; + CmdEndClipRef _866 = { ref.offset + 4u }; Alloc param_3 = a; - CmdEndClipRef param_4 = _715; + CmdEndClipRef param_4 = _866; CmdEndClip param_5 = s; CmdEndClip_write(param_3, param_4, param_5); } @@ -578,80 +655,81 @@ void Cmd_End_write(Alloc a, CmdRef ref) void comp_main() { - uint width_in_bins = ((_854.Load(8) + 16u) - 1u) / 16u; + uint width_in_bins = ((_1005.Load(8) + 16u) - 1u) / 16u; uint bin_ix = (width_in_bins * gl_WorkGroupID.y) + gl_WorkGroupID.x; uint partition_ix = 0u; - uint n_partitions = ((_854.Load(0) + 256u) - 1u) / 256u; + uint n_partitions = ((_1005.Load(0) + 256u) - 1u) / 256u; uint th_ix = gl_LocalInvocationID.x; uint bin_tile_x = 16u * gl_WorkGroupID.x; uint bin_tile_y = 16u * gl_WorkGroupID.y; uint tile_x = gl_LocalInvocationID.x % 16u; uint tile_y = gl_LocalInvocationID.x / 16u; - uint this_tile_ix = (((bin_tile_y + tile_y) * _854.Load(8)) + bin_tile_x) + tile_x; - Alloc _919; - _919.offset = _854.Load(24); + uint this_tile_ix = (((bin_tile_y + tile_y) * _1005.Load(8)) + bin_tile_x) + tile_x; + Alloc _1070; + _1070.offset = _1005.Load(24); Alloc param; - param.offset = _919.offset; + param.offset = _1070.offset; uint param_1 = this_tile_ix * 1024u; uint param_2 = 1024u; Alloc cmd_alloc = slice_mem(param, param_1, param_2); - CmdRef _928 = { cmd_alloc.offset }; - CmdRef cmd_ref = _928; - uint cmd_limit = (cmd_ref.offset + 1024u) - 60u; + CmdRef _1079 = { cmd_alloc.offset }; + CmdRef cmd_ref = _1079; + uint cmd_limit = (cmd_ref.offset + 1024u) - 144u; uint clip_depth = 0u; uint clip_zero_depth = 0u; uint rd_ix = 0u; uint wr_ix = 0u; uint part_start_ix = 0u; uint ready_ix = 0u; - uint drawmonoid_start = _854.Load(44) >> uint(2); - uint drawtag_start = _854.Load(100) >> uint(2); - uint drawdata_start = _854.Load(104) >> uint(2); - uint drawinfo_start = _854.Load(68) >> uint(2); - bool mem_ok = _242.Load(4) == 0u; + uint drawmonoid_start = _1005.Load(44) >> uint(2); + uint drawtag_start = _1005.Load(100) >> uint(2); + uint drawdata_start = _1005.Load(104) >> uint(2); + uint drawinfo_start = _1005.Load(68) >> uint(2); + bool mem_ok = _260.Load(4) == 0u; Alloc param_3; Alloc param_5; - uint _1154; + uint _1304; uint element_ix; Alloc param_14; uint tile_count; - uint _1455; + uint _1605; float linewidth; CmdLinGrad cmd_lin; + CmdRadGrad cmd_rad; while (true) { for (uint i = 0u; i < 8u; i++) { sh_bitmaps[i][th_ix] = 0u; } - bool _1206; + bool _1356; for (;;) { if ((ready_ix == wr_ix) && (partition_ix < n_partitions)) { part_start_ix = ready_ix; uint count = 0u; - bool _1003 = th_ix < 256u; - bool _1011; - if (_1003) + bool _1154 = th_ix < 256u; + bool _1162; + if (_1154) { - _1011 = (partition_ix + th_ix) < n_partitions; + _1162 = (partition_ix + th_ix) < n_partitions; } else { - _1011 = _1003; + _1162 = _1154; } - if (_1011) + if (_1162) { - uint in_ix = (_854.Load(20) >> uint(2)) + ((((partition_ix + th_ix) * 256u) + bin_ix) * 2u); - Alloc _1029; - _1029.offset = _854.Load(20); - param_3.offset = _1029.offset; + uint in_ix = (_1005.Load(20) >> uint(2)) + ((((partition_ix + th_ix) * 256u) + bin_ix) * 2u); + Alloc _1179; + _1179.offset = _1005.Load(20); + param_3.offset = _1179.offset; uint param_4 = in_ix; count = read_mem(param_3, param_4); - Alloc _1040; - _1040.offset = _854.Load(20); - param_5.offset = _1040.offset; + Alloc _1190; + _1190.offset = _1005.Load(20); + param_5.offset = _1190.offset; uint param_6 = in_ix + 1u; uint offset = read_mem(param_5, param_6); uint param_7 = offset; @@ -697,16 +775,16 @@ void comp_main() } if (part_ix > 0u) { - _1154 = sh_part_count[part_ix - 1u]; + _1304 = sh_part_count[part_ix - 1u]; } else { - _1154 = part_start_ix; + _1304 = part_start_ix; } - ix -= _1154; + ix -= _1304; Alloc bin_alloc = sh_part_elements[part_ix]; - BinInstanceRef _1173 = { bin_alloc.offset }; - BinInstanceRef inst_ref = _1173; + BinInstanceRef _1323 = { bin_alloc.offset }; + BinInstanceRef inst_ref = _1323; BinInstanceRef param_10 = inst_ref; uint param_11 = ix; Alloc param_12 = bin_alloc; @@ -716,16 +794,16 @@ void comp_main() } GroupMemoryBarrierWithGroupSync(); wr_ix = min((rd_ix + 256u), ready_ix); - bool _1196 = (wr_ix - rd_ix) < 256u; - if (_1196) + bool _1346 = (wr_ix - rd_ix) < 256u; + if (_1346) { - _1206 = (wr_ix < ready_ix) || (partition_ix < n_partitions); + _1356 = (wr_ix < ready_ix) || (partition_ix < n_partitions); } else { - _1206 = _1196; + _1356 = _1346; } - if (_1206) + if (_1356) { continue; } @@ -738,23 +816,24 @@ void comp_main() if ((th_ix + rd_ix) < wr_ix) { element_ix = sh_elements[th_ix]; - tag = _1222.Load((drawtag_start + element_ix) * 4 + 0); + tag = _1372.Load((drawtag_start + element_ix) * 4 + 0); } switch (tag) { case 68u: case 72u: case 276u: + case 732u: case 5u: case 37u: { uint drawmonoid_base = drawmonoid_start + (4u * element_ix); - uint path_ix = _242.Load(drawmonoid_base * 4 + 8); - PathRef _1247 = { _854.Load(16) + (path_ix * 12u) }; - Alloc _1250; - _1250.offset = _854.Load(16); - param_14.offset = _1250.offset; - PathRef param_15 = _1247; + uint path_ix = _260.Load(drawmonoid_base * 4 + 8); + PathRef _1397 = { _1005.Load(16) + (path_ix * 12u) }; + Alloc _1400; + _1400.offset = _1005.Load(16); + param_14.offset = _1400.offset; + PathRef param_15 = _1397; Path path = Path_read(param_14, param_15); uint stride = path.bbox.z - path.bbox.x; sh_tile_stride[th_ix] = stride; @@ -810,16 +889,16 @@ void comp_main() } } uint element_ix_1 = sh_elements[el_ix]; - uint tag_1 = _1222.Load((drawtag_start + element_ix_1) * 4 + 0); + uint tag_1 = _1372.Load((drawtag_start + element_ix_1) * 4 + 0); if (el_ix > 0u) { - _1455 = sh_tile_count[el_ix - 1u]; + _1605 = sh_tile_count[el_ix - 1u]; } else { - _1455 = 0u; + _1605 = 0u; } - uint seq_ix = ix_1 - _1455; + uint seq_ix = ix_1 - _1605; uint width = sh_tile_width[el_ix]; uint x = sh_tile_x0[el_ix] + (seq_ix % width); uint y = sh_tile_y0[el_ix] + (seq_ix / width); @@ -828,38 +907,47 @@ void comp_main() { uint param_21 = el_ix; bool param_22 = mem_ok; - TileRef _1507 = { sh_tile_base[el_ix] + (((sh_tile_stride[el_ix] * y) + x) * 8u) }; + TileRef _1657 = { sh_tile_base[el_ix] + (((sh_tile_stride[el_ix] * y) + x) * 8u) }; Alloc param_23 = read_tile_alloc(param_21, param_22); - TileRef param_24 = _1507; + TileRef param_24 = _1657; Tile tile = Tile_read(param_23, param_24); bool is_clip = (tag_1 & 1u) != 0u; bool is_blend = false; if (is_clip) { uint drawmonoid_base_1 = drawmonoid_start + (4u * element_ix_1); - uint scene_offset = _242.Load((drawmonoid_base_1 + 2u) * 4 + 8); + uint scene_offset = _260.Load((drawmonoid_base_1 + 2u) * 4 + 8); uint dd = drawdata_start + (scene_offset >> uint(2)); - uint blend = _1222.Load(dd * 4 + 0); + uint blend = _1372.Load(dd * 4 + 0); is_blend = blend != 3u; } - bool _1542 = tile.tile.offset != 0u; - bool _1551; - if (!_1542) + bool _1692 = tile.tile.offset != 0u; + bool _1701; + if (!_1692) { - _1551 = (tile.backdrop == 0) == is_clip; + _1701 = (tile.backdrop == 0) == is_clip; } else { - _1551 = _1542; + _1701 = _1692; } - include_tile = _1551 || is_blend; + bool _1708; + if (!_1701) + { + _1708 = is_clip && is_blend; + } + else + { + _1708 = _1701; + } + include_tile = _1708; } if (include_tile) { uint el_slice = el_ix / 32u; uint el_mask = 1u << (el_ix & 31u); - uint _1573; - InterlockedOr(sh_bitmaps[el_slice][(y * 16u) + x], el_mask, _1573); + uint _1728; + InterlockedOr(sh_bitmaps[el_slice][(y * 16u) + x], el_mask, _1728); } } GroupMemoryBarrierWithGroupSync(); @@ -883,33 +971,33 @@ void comp_main() uint element_ref_ix = (slice_ix * 32u) + uint(int(firstbitlow(bitmap))); uint element_ix_2 = sh_elements[element_ref_ix]; bitmap &= (bitmap - 1u); - uint drawtag = _1222.Load((drawtag_start + element_ix_2) * 4 + 0); + uint drawtag = _1372.Load((drawtag_start + element_ix_2) * 4 + 0); if (clip_zero_depth == 0u) { uint param_25 = element_ref_ix; bool param_26 = mem_ok; - TileRef _1650 = { sh_tile_base[element_ref_ix] + (((sh_tile_stride[element_ref_ix] * tile_y) + tile_x) * 8u) }; + TileRef _1805 = { sh_tile_base[element_ref_ix] + (((sh_tile_stride[element_ref_ix] * tile_y) + tile_x) * 8u) }; Alloc param_27 = read_tile_alloc(param_25, param_26); - TileRef param_28 = _1650; + TileRef param_28 = _1805; Tile tile_1 = Tile_read(param_27, param_28); uint drawmonoid_base_2 = drawmonoid_start + (4u * element_ix_2); - uint scene_offset_1 = _242.Load((drawmonoid_base_2 + 2u) * 4 + 8); - uint info_offset = _242.Load((drawmonoid_base_2 + 3u) * 4 + 8); + uint scene_offset_1 = _260.Load((drawmonoid_base_2 + 2u) * 4 + 8); + uint info_offset = _260.Load((drawmonoid_base_2 + 3u) * 4 + 8); uint dd_1 = drawdata_start + (scene_offset_1 >> uint(2)); uint di = drawinfo_start + (info_offset >> uint(2)); switch (drawtag) { case 68u: { - linewidth = asfloat(_242.Load(di * 4 + 8)); + linewidth = asfloat(_260.Load(di * 4 + 8)); Alloc param_29 = cmd_alloc; CmdRef param_30 = cmd_ref; uint param_31 = cmd_limit; - bool _1697 = alloc_cmd(param_29, param_30, param_31); + bool _1853 = alloc_cmd(param_29, param_30, param_31); cmd_alloc = param_29; cmd_ref = param_30; cmd_limit = param_31; - if (!_1697) + if (!_1853) { break; } @@ -919,11 +1007,11 @@ void comp_main() float param_35 = linewidth; write_fill(param_32, param_33, param_34, param_35); cmd_ref = param_33; - uint rgba = _1222.Load(dd_1 * 4 + 0); - CmdColor _1720 = { rgba }; + uint rgba = _1372.Load(dd_1 * 4 + 0); + CmdColor _1876 = { rgba }; Alloc param_36 = cmd_alloc; CmdRef param_37 = cmd_ref; - CmdColor param_38 = _1720; + CmdColor param_38 = _1876; Cmd_Color_write(param_36, param_37, param_38); cmd_ref.offset += 8u; break; @@ -933,25 +1021,25 @@ void comp_main() Alloc param_39 = cmd_alloc; CmdRef param_40 = cmd_ref; uint param_41 = cmd_limit; - bool _1738 = alloc_cmd(param_39, param_40, param_41); + bool _1894 = alloc_cmd(param_39, param_40, param_41); cmd_alloc = param_39; cmd_ref = param_40; cmd_limit = param_41; - if (!_1738) + if (!_1894) { break; } - linewidth = asfloat(_242.Load(di * 4 + 8)); + linewidth = asfloat(_260.Load(di * 4 + 8)); Alloc param_42 = cmd_alloc; CmdRef param_43 = cmd_ref; Tile param_44 = tile_1; float param_45 = linewidth; write_fill(param_42, param_43, param_44, param_45); cmd_ref = param_43; - cmd_lin.index = _1222.Load(dd_1 * 4 + 0); - cmd_lin.line_x = asfloat(_242.Load((di + 1u) * 4 + 8)); - cmd_lin.line_y = asfloat(_242.Load((di + 2u) * 4 + 8)); - cmd_lin.line_c = asfloat(_242.Load((di + 3u) * 4 + 8)); + cmd_lin.index = _1372.Load(dd_1 * 4 + 0); + cmd_lin.line_x = asfloat(_260.Load((di + 1u) * 4 + 8)); + cmd_lin.line_y = asfloat(_260.Load((di + 2u) * 4 + 8)); + cmd_lin.line_c = asfloat(_260.Load((di + 3u) * 4 + 8)); Alloc param_46 = cmd_alloc; CmdRef param_47 = cmd_ref; CmdLinGrad param_48 = cmd_lin; @@ -959,69 +1047,102 @@ void comp_main() cmd_ref.offset += 20u; break; } - case 72u: + case 732u: { - linewidth = asfloat(_242.Load(di * 4 + 8)); Alloc param_49 = cmd_alloc; CmdRef param_50 = cmd_ref; uint param_51 = cmd_limit; - bool _1806 = alloc_cmd(param_49, param_50, param_51); + bool _1958 = alloc_cmd(param_49, param_50, param_51); cmd_alloc = param_49; cmd_ref = param_50; cmd_limit = param_51; - if (!_1806) + if (!_1958) { break; } + linewidth = asfloat(_260.Load(di * 4 + 8)); Alloc param_52 = cmd_alloc; CmdRef param_53 = cmd_ref; Tile param_54 = tile_1; float param_55 = linewidth; write_fill(param_52, param_53, param_54, param_55); cmd_ref = param_53; - uint index = _1222.Load(dd_1 * 4 + 0); - uint raw1 = _1222.Load((dd_1 + 1u) * 4 + 0); - int2 offset_1 = int2(int(raw1 << uint(16)) >> 16, int(raw1) >> 16); - CmdImage _1845 = { index, offset_1 }; + cmd_rad.index = _1372.Load(dd_1 * 4 + 0); + cmd_rad.mat = asfloat(uint4(_260.Load((di + 1u) * 4 + 8), _260.Load((di + 2u) * 4 + 8), _260.Load((di + 3u) * 4 + 8), _260.Load((di + 4u) * 4 + 8))); + cmd_rad.xlat = asfloat(uint2(_260.Load((di + 5u) * 4 + 8), _260.Load((di + 6u) * 4 + 8))); + cmd_rad.c1 = asfloat(uint2(_260.Load((di + 7u) * 4 + 8), _260.Load((di + 8u) * 4 + 8))); + cmd_rad.ra = asfloat(_260.Load((di + 9u) * 4 + 8)); + cmd_rad.roff = asfloat(_260.Load((di + 10u) * 4 + 8)); Alloc param_56 = cmd_alloc; CmdRef param_57 = cmd_ref; - CmdImage param_58 = _1845; - Cmd_Image_write(param_56, param_57, param_58); + CmdRadGrad param_58 = cmd_rad; + Cmd_RadGrad_write(param_56, param_57, param_58); + cmd_ref.offset += 48u; + break; + } + case 72u: + { + linewidth = asfloat(_260.Load(di * 4 + 8)); + Alloc param_59 = cmd_alloc; + CmdRef param_60 = cmd_ref; + uint param_61 = cmd_limit; + bool _2064 = alloc_cmd(param_59, param_60, param_61); + cmd_alloc = param_59; + cmd_ref = param_60; + cmd_limit = param_61; + if (!_2064) + { + break; + } + Alloc param_62 = cmd_alloc; + CmdRef param_63 = cmd_ref; + Tile param_64 = tile_1; + float param_65 = linewidth; + write_fill(param_62, param_63, param_64, param_65); + cmd_ref = param_63; + uint index = _1372.Load(dd_1 * 4 + 0); + uint raw1 = _1372.Load((dd_1 + 1u) * 4 + 0); + int2 offset_1 = int2(int(raw1 << uint(16)) >> 16, int(raw1) >> 16); + CmdImage _2103 = { index, offset_1 }; + Alloc param_66 = cmd_alloc; + CmdRef param_67 = cmd_ref; + CmdImage param_68 = _2103; + Cmd_Image_write(param_66, param_67, param_68); cmd_ref.offset += 12u; break; } case 5u: { - bool _1859 = tile_1.tile.offset == 0u; - bool _1865; - if (_1859) + bool _2117 = tile_1.tile.offset == 0u; + bool _2123; + if (_2117) { - _1865 = tile_1.backdrop == 0; + _2123 = tile_1.backdrop == 0; } else { - _1865 = _1859; + _2123 = _2117; } - if (_1865) + if (_2123) { clip_zero_depth = clip_depth + 1u; } else { - Alloc param_59 = cmd_alloc; - CmdRef param_60 = cmd_ref; - uint param_61 = cmd_limit; - bool _1877 = alloc_cmd(param_59, param_60, param_61); - cmd_alloc = param_59; - cmd_ref = param_60; - cmd_limit = param_61; - if (!_1877) + Alloc param_69 = cmd_alloc; + CmdRef param_70 = cmd_ref; + uint param_71 = cmd_limit; + bool _2135 = alloc_cmd(param_69, param_70, param_71); + cmd_alloc = param_69; + cmd_ref = param_70; + cmd_limit = param_71; + if (!_2135) { break; } - Alloc param_62 = cmd_alloc; - CmdRef param_63 = cmd_ref; - Cmd_BeginClip_write(param_62, param_63); + Alloc param_72 = cmd_alloc; + CmdRef param_73 = cmd_ref; + Cmd_BeginClip_write(param_72, param_73); cmd_ref.offset += 4u; } clip_depth++; @@ -1030,29 +1151,29 @@ void comp_main() case 37u: { clip_depth--; - Alloc param_64 = cmd_alloc; - CmdRef param_65 = cmd_ref; - uint param_66 = cmd_limit; - bool _1905 = alloc_cmd(param_64, param_65, param_66); - cmd_alloc = param_64; - cmd_ref = param_65; - cmd_limit = param_66; - if (!_1905) + Alloc param_74 = cmd_alloc; + CmdRef param_75 = cmd_ref; + uint param_76 = cmd_limit; + bool _2163 = alloc_cmd(param_74, param_75, param_76); + cmd_alloc = param_74; + cmd_ref = param_75; + cmd_limit = param_76; + if (!_2163) { break; } - Alloc param_67 = cmd_alloc; - CmdRef param_68 = cmd_ref; - Tile param_69 = tile_1; - float param_70 = -1.0f; - write_fill(param_67, param_68, param_69, param_70); - cmd_ref = param_68; - uint blend_1 = _1222.Load(dd_1 * 4 + 0); - CmdEndClip _1928 = { blend_1 }; - Alloc param_71 = cmd_alloc; - CmdRef param_72 = cmd_ref; - CmdEndClip param_73 = _1928; - Cmd_EndClip_write(param_71, param_72, param_73); + Alloc param_77 = cmd_alloc; + CmdRef param_78 = cmd_ref; + Tile param_79 = tile_1; + float param_80 = -1.0f; + write_fill(param_77, param_78, param_79, param_80); + cmd_ref = param_78; + uint blend_1 = _1372.Load(dd_1 * 4 + 0); + CmdEndClip _2186 = { blend_1 }; + Alloc param_81 = cmd_alloc; + CmdRef param_82 = cmd_ref; + CmdEndClip param_83 = _2186; + Cmd_EndClip_write(param_81, param_82, param_83); cmd_ref.offset += 8u; break; } @@ -1086,21 +1207,21 @@ void comp_main() break; } } - bool _1975 = (bin_tile_x + tile_x) < _854.Load(8); - bool _1984; - if (_1975) + bool _2233 = (bin_tile_x + tile_x) < _1005.Load(8); + bool _2242; + if (_2233) { - _1984 = (bin_tile_y + tile_y) < _854.Load(12); + _2242 = (bin_tile_y + tile_y) < _1005.Load(12); } else { - _1984 = _1975; + _2242 = _2233; } - if (_1984) + if (_2242) { - Alloc param_74 = cmd_alloc; - CmdRef param_75 = cmd_ref; - Cmd_End_write(param_74, param_75); + Alloc param_84 = cmd_alloc; + CmdRef param_85 = cmd_ref; + Cmd_End_write(param_84, param_85); } } diff --git a/piet-gpu/shader/gen/coarse.msl b/piet-gpu/shader/gen/coarse.msl index 4226352..abd636b 100644 --- a/piet-gpu/shader/gen/coarse.msl +++ b/piet-gpu/shader/gen/coarse.msl @@ -107,6 +107,21 @@ struct CmdLinGrad float line_c; }; +struct CmdRadGradRef +{ + uint offset; +}; + +struct CmdRadGrad +{ + uint index; + float4 mat; + float2 xlat; + float2 c1; + float ra; + float roff; +}; + struct CmdImageRef { uint offset; @@ -211,7 +226,7 @@ bool touch_mem(thread const Alloc& alloc, thread const uint& offset) } static inline __attribute__((always_inline)) -uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memory& v_242, constant uint& v_242BufferSize) +uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = alloc; uint param_1 = offset; @@ -219,7 +234,7 @@ uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memor { return 0u; } - uint v = v_242.memory[offset]; + uint v = v_260.memory[offset]; return v; } @@ -238,30 +253,30 @@ BinInstanceRef BinInstance_index(thread const BinInstanceRef& ref, thread const } static inline __attribute__((always_inline)) -BinInstance BinInstance_read(thread const Alloc& a, thread const BinInstanceRef& ref, device Memory& v_242, constant uint& v_242BufferSize) +BinInstance BinInstance_read(thread const Alloc& a, thread const BinInstanceRef& ref, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_242, v_242BufferSize); + uint raw0 = read_mem(param, param_1, v_260, v_260BufferSize); BinInstance s; s.element_ix = raw0; return s; } static inline __attribute__((always_inline)) -Path Path_read(thread const Alloc& a, thread const PathRef& ref, device Memory& v_242, constant uint& v_242BufferSize) +Path Path_read(thread const Alloc& a, thread const PathRef& ref, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_242, v_242BufferSize); + uint raw0 = read_mem(param, param_1, v_260, v_260BufferSize); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_242, v_242BufferSize); + uint raw1 = read_mem(param_2, param_3, v_260, v_260BufferSize); Alloc param_4 = a; uint param_5 = ix + 2u; - uint raw2 = read_mem(param_4, param_5, v_242, v_242BufferSize); + uint raw2 = read_mem(param_4, param_5, v_260, v_260BufferSize); Path s; s.bbox = uint4(raw0 & 65535u, raw0 >> uint(16), raw1 & 65535u, raw1 >> uint(16)); s.tiles = TileRef{ raw2 }; @@ -274,24 +289,24 @@ void write_tile_alloc(thread const uint& el_ix, thread const Alloc& a) } static inline __attribute__((always_inline)) -Alloc read_tile_alloc(thread const uint& el_ix, thread const bool& mem_ok, device Memory& v_242, constant uint& v_242BufferSize) +Alloc read_tile_alloc(thread const uint& el_ix, thread const bool& mem_ok, device Memory& v_260, constant uint& v_260BufferSize) { uint param = 0u; - uint param_1 = uint(int((v_242BufferSize - 8) / 4) * 4); + uint param_1 = uint(int((v_260BufferSize - 8) / 4) * 4); bool param_2 = mem_ok; return new_alloc(param, param_1, param_2); } static inline __attribute__((always_inline)) -Tile Tile_read(thread const Alloc& a, thread const TileRef& ref, device Memory& v_242, constant uint& v_242BufferSize) +Tile Tile_read(thread const Alloc& a, thread const TileRef& ref, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_242, v_242BufferSize); + uint raw0 = read_mem(param, param_1, v_260, v_260BufferSize); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_242, v_242BufferSize); + uint raw1 = read_mem(param_2, param_3, v_260, v_260BufferSize); Tile s; s.tile = TileSegRef{ raw0 }; s.backdrop = int(raw1); @@ -299,26 +314,26 @@ Tile Tile_read(thread const Alloc& a, thread const TileRef& ref, device Memory& } static inline __attribute__((always_inline)) -MallocResult malloc(thread const uint& size, device Memory& v_242, constant uint& v_242BufferSize) +MallocResult malloc(thread const uint& size, device Memory& v_260, constant uint& v_260BufferSize) { - uint _248 = atomic_fetch_add_explicit((device atomic_uint*)&v_242.mem_offset, size, memory_order_relaxed); - uint offset = _248; + uint _266 = atomic_fetch_add_explicit((device atomic_uint*)&v_260.mem_offset, size, memory_order_relaxed); + uint offset = _266; MallocResult r; - r.failed = (offset + size) > uint(int((v_242BufferSize - 8) / 4) * 4); + r.failed = (offset + size) > uint(int((v_260BufferSize - 8) / 4) * 4); uint param = offset; uint param_1 = size; bool param_2 = !r.failed; r.alloc = new_alloc(param, param_1, param_2); if (r.failed) { - uint _277 = atomic_fetch_max_explicit((device atomic_uint*)&v_242.mem_error, 1u, memory_order_relaxed); + uint _295 = atomic_fetch_max_explicit((device atomic_uint*)&v_260.mem_error, 1u, memory_order_relaxed); return r; } return r; } static inline __attribute__((always_inline)) -void write_mem(thread const Alloc& alloc, thread const uint& offset, thread const uint& val, device Memory& v_242, constant uint& v_242BufferSize) +void write_mem(thread const Alloc& alloc, thread const uint& offset, thread const uint& val, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = alloc; uint param_1 = offset; @@ -326,42 +341,42 @@ void write_mem(thread const Alloc& alloc, thread const uint& offset, thread cons { return; } - v_242.memory[offset] = val; + v_260.memory[offset] = val; } static inline __attribute__((always_inline)) -void CmdJump_write(thread const Alloc& a, thread const CmdJumpRef& ref, thread const CmdJump& s, device Memory& v_242, constant uint& v_242BufferSize) +void CmdJump_write(thread const Alloc& a, thread const CmdJumpRef& ref, thread const CmdJump& s, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; uint param_2 = s.new_ref; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_Jump_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdJump& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_Jump_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdJump& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint param_2 = 10u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + uint param_2 = 11u; + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; CmdJumpRef param_4 = CmdJumpRef{ ref.offset + 4u }; CmdJump param_5 = s; - CmdJump_write(param_3, param_4, param_5, v_242, v_242BufferSize); + CmdJump_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -bool alloc_cmd(thread Alloc& cmd_alloc, thread CmdRef& cmd_ref, thread uint& cmd_limit, device Memory& v_242, constant uint& v_242BufferSize) +bool alloc_cmd(thread Alloc& cmd_alloc, thread CmdRef& cmd_ref, thread uint& cmd_limit, device Memory& v_260, constant uint& v_260BufferSize) { if (cmd_ref.offset < cmd_limit) { return true; } uint param = 1024u; - MallocResult _762 = malloc(param, v_242, v_242BufferSize); - MallocResult new_cmd = _762; + MallocResult _913 = malloc(param, v_260, v_260BufferSize); + MallocResult new_cmd = _913; if (new_cmd.failed) { return false; @@ -370,78 +385,78 @@ bool alloc_cmd(thread Alloc& cmd_alloc, thread CmdRef& cmd_ref, thread uint& cmd Alloc param_1 = cmd_alloc; CmdRef param_2 = cmd_ref; CmdJump param_3 = jump; - Cmd_Jump_write(param_1, param_2, param_3, v_242, v_242BufferSize); + Cmd_Jump_write(param_1, param_2, param_3, v_260, v_260BufferSize); cmd_alloc = new_cmd.alloc; cmd_ref = CmdRef{ cmd_alloc.offset }; - cmd_limit = (cmd_alloc.offset + 1024u) - 60u; + cmd_limit = (cmd_alloc.offset + 1024u) - 144u; return true; } static inline __attribute__((always_inline)) -void CmdFill_write(thread const Alloc& a, thread const CmdFillRef& ref, thread const CmdFill& s, device Memory& v_242, constant uint& v_242BufferSize) +void CmdFill_write(thread const Alloc& a, thread const CmdFillRef& ref, thread const CmdFill& s, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; uint param_2 = s.tile_ref; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; uint param_4 = ix + 1u; uint param_5 = uint(s.backdrop); - write_mem(param_3, param_4, param_5, v_242, v_242BufferSize); + write_mem(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_Fill_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdFill& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_Fill_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdFill& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 1u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; CmdFillRef param_4 = CmdFillRef{ ref.offset + 4u }; CmdFill param_5 = s; - CmdFill_write(param_3, param_4, param_5, v_242, v_242BufferSize); + CmdFill_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_Solid_write(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_Solid_write(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 3u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void CmdStroke_write(thread const Alloc& a, thread const CmdStrokeRef& ref, thread const CmdStroke& s, device Memory& v_242, constant uint& v_242BufferSize) +void CmdStroke_write(thread const Alloc& a, thread const CmdStrokeRef& ref, thread const CmdStroke& s, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; uint param_2 = s.tile_ref; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; uint param_4 = ix + 1u; uint param_5 = as_type(s.half_width); - write_mem(param_3, param_4, param_5, v_242, v_242BufferSize); + write_mem(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_Stroke_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdStroke& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_Stroke_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdStroke& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 2u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; CmdStrokeRef param_4 = CmdStrokeRef{ ref.offset + 4u }; CmdStroke param_5 = s; - CmdStroke_write(param_3, param_4, param_5, v_242, v_242BufferSize); + CmdStroke_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void write_fill(thread const Alloc& alloc, thread CmdRef& cmd_ref, thread const Tile& tile, thread const float& linewidth, device Memory& v_242, constant uint& v_242BufferSize) +void write_fill(thread const Alloc& alloc, thread CmdRef& cmd_ref, thread const Tile& tile, thread const float& linewidth, device Memory& v_260, constant uint& v_260BufferSize) { if (linewidth < 0.0) { @@ -451,14 +466,14 @@ void write_fill(thread const Alloc& alloc, thread CmdRef& cmd_ref, thread const Alloc param = alloc; CmdRef param_1 = cmd_ref; CmdFill param_2 = cmd_fill; - Cmd_Fill_write(param, param_1, param_2, v_242, v_242BufferSize); + Cmd_Fill_write(param, param_1, param_2, v_260, v_260BufferSize); cmd_ref.offset += 12u; } else { Alloc param_3 = alloc; CmdRef param_4 = cmd_ref; - Cmd_Solid_write(param_3, param_4, v_242, v_242BufferSize); + Cmd_Solid_write(param_3, param_4, v_260, v_260BufferSize); cmd_ref.offset += 4u; } } @@ -468,138 +483,201 @@ void write_fill(thread const Alloc& alloc, thread CmdRef& cmd_ref, thread const Alloc param_5 = alloc; CmdRef param_6 = cmd_ref; CmdStroke param_7 = cmd_stroke; - Cmd_Stroke_write(param_5, param_6, param_7, v_242, v_242BufferSize); + Cmd_Stroke_write(param_5, param_6, param_7, v_260, v_260BufferSize); cmd_ref.offset += 12u; } } static inline __attribute__((always_inline)) -void CmdColor_write(thread const Alloc& a, thread const CmdColorRef& ref, thread const CmdColor& s, device Memory& v_242, constant uint& v_242BufferSize) +void CmdColor_write(thread const Alloc& a, thread const CmdColorRef& ref, thread const CmdColor& s, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; uint param_2 = s.rgba_color; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_Color_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdColor& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_Color_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdColor& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 5u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; CmdColorRef param_4 = CmdColorRef{ ref.offset + 4u }; CmdColor param_5 = s; - CmdColor_write(param_3, param_4, param_5, v_242, v_242BufferSize); + CmdColor_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void CmdLinGrad_write(thread const Alloc& a, thread const CmdLinGradRef& ref, thread const CmdLinGrad& s, device Memory& v_242, constant uint& v_242BufferSize) +void CmdLinGrad_write(thread const Alloc& a, thread const CmdLinGradRef& ref, thread const CmdLinGrad& s, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; uint param_2 = s.index; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; uint param_4 = ix + 1u; uint param_5 = as_type(s.line_x); - write_mem(param_3, param_4, param_5, v_242, v_242BufferSize); + write_mem(param_3, param_4, param_5, v_260, v_260BufferSize); Alloc param_6 = a; uint param_7 = ix + 2u; uint param_8 = as_type(s.line_y); - write_mem(param_6, param_7, param_8, v_242, v_242BufferSize); + write_mem(param_6, param_7, param_8, v_260, v_260BufferSize); Alloc param_9 = a; uint param_10 = ix + 3u; uint param_11 = as_type(s.line_c); - write_mem(param_9, param_10, param_11, v_242, v_242BufferSize); + write_mem(param_9, param_10, param_11, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_LinGrad_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdLinGrad& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_LinGrad_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdLinGrad& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 6u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; CmdLinGradRef param_4 = CmdLinGradRef{ ref.offset + 4u }; CmdLinGrad param_5 = s; - CmdLinGrad_write(param_3, param_4, param_5, v_242, v_242BufferSize); + CmdLinGrad_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void CmdImage_write(thread const Alloc& a, thread const CmdImageRef& ref, thread const CmdImage& s, device Memory& v_242, constant uint& v_242BufferSize) +void CmdRadGrad_write(thread const Alloc& a, thread const CmdRadGradRef& ref, thread const CmdRadGrad& s, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; uint param_2 = s.index; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; uint param_4 = ix + 1u; - uint param_5 = (uint(s.offset.x) & 65535u) | (uint(s.offset.y) << uint(16)); - write_mem(param_3, param_4, param_5, v_242, v_242BufferSize); + uint param_5 = as_type(s.mat.x); + write_mem(param_3, param_4, param_5, v_260, v_260BufferSize); + Alloc param_6 = a; + uint param_7 = ix + 2u; + uint param_8 = as_type(s.mat.y); + write_mem(param_6, param_7, param_8, v_260, v_260BufferSize); + Alloc param_9 = a; + uint param_10 = ix + 3u; + uint param_11 = as_type(s.mat.z); + write_mem(param_9, param_10, param_11, v_260, v_260BufferSize); + Alloc param_12 = a; + uint param_13 = ix + 4u; + uint param_14 = as_type(s.mat.w); + write_mem(param_12, param_13, param_14, v_260, v_260BufferSize); + Alloc param_15 = a; + uint param_16 = ix + 5u; + uint param_17 = as_type(s.xlat.x); + write_mem(param_15, param_16, param_17, v_260, v_260BufferSize); + Alloc param_18 = a; + uint param_19 = ix + 6u; + uint param_20 = as_type(s.xlat.y); + write_mem(param_18, param_19, param_20, v_260, v_260BufferSize); + Alloc param_21 = a; + uint param_22 = ix + 7u; + uint param_23 = as_type(s.c1.x); + write_mem(param_21, param_22, param_23, v_260, v_260BufferSize); + Alloc param_24 = a; + uint param_25 = ix + 8u; + uint param_26 = as_type(s.c1.y); + write_mem(param_24, param_25, param_26, v_260, v_260BufferSize); + Alloc param_27 = a; + uint param_28 = ix + 9u; + uint param_29 = as_type(s.ra); + write_mem(param_27, param_28, param_29, v_260, v_260BufferSize); + Alloc param_30 = a; + uint param_31 = ix + 10u; + uint param_32 = as_type(s.roff); + write_mem(param_30, param_31, param_32, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_Image_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdImage& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_RadGrad_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdRadGrad& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 7u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; - CmdImageRef param_4 = CmdImageRef{ ref.offset + 4u }; - CmdImage param_5 = s; - CmdImage_write(param_3, param_4, param_5, v_242, v_242BufferSize); + CmdRadGradRef param_4 = CmdRadGradRef{ ref.offset + 4u }; + CmdRadGrad param_5 = s; + CmdRadGrad_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_BeginClip_write(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_242, constant uint& v_242BufferSize) +void CmdImage_write(thread const Alloc& a, thread const CmdImageRef& ref, thread const CmdImage& s, device Memory& v_260, constant uint& v_260BufferSize) +{ + uint ix = ref.offset >> uint(2); + Alloc param = a; + uint param_1 = ix + 0u; + uint param_2 = s.index; + write_mem(param, param_1, param_2, v_260, v_260BufferSize); + Alloc param_3 = a; + uint param_4 = ix + 1u; + uint param_5 = (uint(s.offset.x) & 65535u) | (uint(s.offset.y) << uint(16)); + write_mem(param_3, param_4, param_5, v_260, v_260BufferSize); +} + +static inline __attribute__((always_inline)) +void Cmd_Image_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdImage& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 8u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); + Alloc param_3 = a; + CmdImageRef param_4 = CmdImageRef{ ref.offset + 4u }; + CmdImage param_5 = s; + CmdImage_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void CmdEndClip_write(thread const Alloc& a, thread const CmdEndClipRef& ref, thread const CmdEndClip& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_BeginClip_write(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_260, constant uint& v_260BufferSize) +{ + Alloc param = a; + uint param_1 = ref.offset >> uint(2); + uint param_2 = 9u; + write_mem(param, param_1, param_2, v_260, v_260BufferSize); +} + +static inline __attribute__((always_inline)) +void CmdEndClip_write(thread const Alloc& a, thread const CmdEndClipRef& ref, thread const CmdEndClip& s, device Memory& v_260, constant uint& v_260BufferSize) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; uint param_2 = s.blend; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_EndClip_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdEndClip& s, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_EndClip_write(thread const Alloc& a, thread const CmdRef& ref, thread const CmdEndClip& s, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint param_2 = 9u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + uint param_2 = 10u; + write_mem(param, param_1, param_2, v_260, v_260BufferSize); Alloc param_3 = a; CmdEndClipRef param_4 = CmdEndClipRef{ ref.offset + 4u }; CmdEndClip param_5 = s; - CmdEndClip_write(param_3, param_4, param_5, v_242, v_242BufferSize); + CmdEndClip_write(param_3, param_4, param_5, v_260, v_260BufferSize); } static inline __attribute__((always_inline)) -void Cmd_End_write(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_242, constant uint& v_242BufferSize) +void Cmd_End_write(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_260, constant uint& v_260BufferSize) { Alloc param = a; uint param_1 = ref.offset >> uint(2); uint param_2 = 0u; - write_mem(param, param_1, param_2, v_242, v_242BufferSize); + write_mem(param, param_1, param_2, v_260, v_260BufferSize); } -kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device Memory& v_242 [[buffer(0)]], const device ConfigBuf& _854 [[buffer(1)]], const device SceneBuf& _1222 [[buffer(2)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device Memory& v_260 [[buffer(0)]], const device ConfigBuf& _1005 [[buffer(1)]], const device SceneBuf& _1372 [[buffer(2)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) { threadgroup uint sh_bitmaps[8][256]; threadgroup Alloc sh_part_elements[256]; @@ -611,76 +689,77 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M threadgroup uint sh_tile_y0[256]; threadgroup uint sh_tile_base[256]; threadgroup uint sh_tile_count[256]; - constant uint& v_242BufferSize = spvBufferSizeConstants[0]; - uint width_in_bins = ((_854.conf.width_in_tiles + 16u) - 1u) / 16u; + constant uint& v_260BufferSize = spvBufferSizeConstants[0]; + uint width_in_bins = ((_1005.conf.width_in_tiles + 16u) - 1u) / 16u; uint bin_ix = (width_in_bins * gl_WorkGroupID.y) + gl_WorkGroupID.x; uint partition_ix = 0u; - uint n_partitions = ((_854.conf.n_elements + 256u) - 1u) / 256u; + uint n_partitions = ((_1005.conf.n_elements + 256u) - 1u) / 256u; uint th_ix = gl_LocalInvocationID.x; uint bin_tile_x = 16u * gl_WorkGroupID.x; uint bin_tile_y = 16u * gl_WorkGroupID.y; uint tile_x = gl_LocalInvocationID.x % 16u; uint tile_y = gl_LocalInvocationID.x / 16u; - uint this_tile_ix = (((bin_tile_y + tile_y) * _854.conf.width_in_tiles) + bin_tile_x) + tile_x; + uint this_tile_ix = (((bin_tile_y + tile_y) * _1005.conf.width_in_tiles) + bin_tile_x) + tile_x; Alloc param; - param.offset = _854.conf.ptcl_alloc.offset; + param.offset = _1005.conf.ptcl_alloc.offset; uint param_1 = this_tile_ix * 1024u; uint param_2 = 1024u; Alloc cmd_alloc = slice_mem(param, param_1, param_2); CmdRef cmd_ref = CmdRef{ cmd_alloc.offset }; - uint cmd_limit = (cmd_ref.offset + 1024u) - 60u; + uint cmd_limit = (cmd_ref.offset + 1024u) - 144u; uint clip_depth = 0u; uint clip_zero_depth = 0u; uint rd_ix = 0u; uint wr_ix = 0u; uint part_start_ix = 0u; uint ready_ix = 0u; - uint drawmonoid_start = _854.conf.drawmonoid_alloc.offset >> uint(2); - uint drawtag_start = _854.conf.drawtag_offset >> uint(2); - uint drawdata_start = _854.conf.drawdata_offset >> uint(2); - uint drawinfo_start = _854.conf.drawinfo_alloc.offset >> uint(2); - bool mem_ok = v_242.mem_error == 0u; + uint drawmonoid_start = _1005.conf.drawmonoid_alloc.offset >> uint(2); + uint drawtag_start = _1005.conf.drawtag_offset >> uint(2); + uint drawdata_start = _1005.conf.drawdata_offset >> uint(2); + uint drawinfo_start = _1005.conf.drawinfo_alloc.offset >> uint(2); + bool mem_ok = v_260.mem_error == 0u; Alloc param_3; Alloc param_5; - uint _1154; + uint _1304; uint element_ix; Alloc param_14; uint tile_count; - uint _1455; + uint _1605; float linewidth; CmdLinGrad cmd_lin; + CmdRadGrad cmd_rad; while (true) { for (uint i = 0u; i < 8u; i++) { sh_bitmaps[i][th_ix] = 0u; } - bool _1206; + bool _1356; for (;;) { if ((ready_ix == wr_ix) && (partition_ix < n_partitions)) { part_start_ix = ready_ix; uint count = 0u; - bool _1003 = th_ix < 256u; - bool _1011; - if (_1003) + bool _1154 = th_ix < 256u; + bool _1162; + if (_1154) { - _1011 = (partition_ix + th_ix) < n_partitions; + _1162 = (partition_ix + th_ix) < n_partitions; } else { - _1011 = _1003; + _1162 = _1154; } - if (_1011) + if (_1162) { - uint in_ix = (_854.conf.bin_alloc.offset >> uint(2)) + ((((partition_ix + th_ix) * 256u) + bin_ix) * 2u); - param_3.offset = _854.conf.bin_alloc.offset; + uint in_ix = (_1005.conf.bin_alloc.offset >> uint(2)) + ((((partition_ix + th_ix) * 256u) + bin_ix) * 2u); + param_3.offset = _1005.conf.bin_alloc.offset; uint param_4 = in_ix; - count = read_mem(param_3, param_4, v_242, v_242BufferSize); - param_5.offset = _854.conf.bin_alloc.offset; + count = read_mem(param_3, param_4, v_260, v_260BufferSize); + param_5.offset = _1005.conf.bin_alloc.offset; uint param_6 = in_ix + 1u; - uint offset = read_mem(param_5, param_6, v_242, v_242BufferSize); + uint offset = read_mem(param_5, param_6, v_260, v_260BufferSize); uint param_7 = offset; uint param_8 = count * 4u; bool param_9 = mem_ok; @@ -724,34 +803,34 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M } if (part_ix > 0u) { - _1154 = sh_part_count[part_ix - 1u]; + _1304 = sh_part_count[part_ix - 1u]; } else { - _1154 = part_start_ix; + _1304 = part_start_ix; } - ix -= _1154; + ix -= _1304; Alloc bin_alloc = sh_part_elements[part_ix]; BinInstanceRef inst_ref = BinInstanceRef{ bin_alloc.offset }; BinInstanceRef param_10 = inst_ref; uint param_11 = ix; Alloc param_12 = bin_alloc; BinInstanceRef param_13 = BinInstance_index(param_10, param_11); - BinInstance inst = BinInstance_read(param_12, param_13, v_242, v_242BufferSize); + BinInstance inst = BinInstance_read(param_12, param_13, v_260, v_260BufferSize); sh_elements[th_ix] = inst.element_ix; } threadgroup_barrier(mem_flags::mem_threadgroup); wr_ix = min((rd_ix + 256u), ready_ix); - bool _1196 = (wr_ix - rd_ix) < 256u; - if (_1196) + bool _1346 = (wr_ix - rd_ix) < 256u; + if (_1346) { - _1206 = (wr_ix < ready_ix) || (partition_ix < n_partitions); + _1356 = (wr_ix < ready_ix) || (partition_ix < n_partitions); } else { - _1206 = _1196; + _1356 = _1346; } - if (_1206) + if (_1356) { continue; } @@ -764,21 +843,22 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M if ((th_ix + rd_ix) < wr_ix) { element_ix = sh_elements[th_ix]; - tag = _1222.scene[drawtag_start + element_ix]; + tag = _1372.scene[drawtag_start + element_ix]; } switch (tag) { case 68u: case 72u: case 276u: + case 732u: case 5u: case 37u: { uint drawmonoid_base = drawmonoid_start + (4u * element_ix); - uint path_ix = v_242.memory[drawmonoid_base]; - param_14.offset = _854.conf.tile_alloc.offset; - PathRef param_15 = PathRef{ _854.conf.tile_alloc.offset + (path_ix * 12u) }; - Path path = Path_read(param_14, param_15, v_242, v_242BufferSize); + uint path_ix = v_260.memory[drawmonoid_base]; + param_14.offset = _1005.conf.tile_alloc.offset; + PathRef param_15 = PathRef{ _1005.conf.tile_alloc.offset + (path_ix * 12u) }; + Path path = Path_read(param_14, param_15, v_260, v_260BufferSize); uint stride = path.bbox.z - path.bbox.x; sh_tile_stride[th_ix] = stride; int dx = int(path.bbox.x) - int(bin_tile_x); @@ -833,16 +913,16 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M } } uint element_ix_1 = sh_elements[el_ix]; - uint tag_1 = _1222.scene[drawtag_start + element_ix_1]; + uint tag_1 = _1372.scene[drawtag_start + element_ix_1]; if (el_ix > 0u) { - _1455 = sh_tile_count[el_ix - 1u]; + _1605 = sh_tile_count[el_ix - 1u]; } else { - _1455 = 0u; + _1605 = 0u; } - uint seq_ix = ix_1 - _1455; + uint seq_ix = ix_1 - _1605; uint width = sh_tile_width[el_ix]; uint x = sh_tile_x0[el_ix] + (seq_ix % width); uint y = sh_tile_y0[el_ix] + (seq_ix / width); @@ -851,36 +931,45 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M { uint param_21 = el_ix; bool param_22 = mem_ok; - Alloc param_23 = read_tile_alloc(param_21, param_22, v_242, v_242BufferSize); + Alloc param_23 = read_tile_alloc(param_21, param_22, v_260, v_260BufferSize); TileRef param_24 = TileRef{ sh_tile_base[el_ix] + (((sh_tile_stride[el_ix] * y) + x) * 8u) }; - Tile tile = Tile_read(param_23, param_24, v_242, v_242BufferSize); + Tile tile = Tile_read(param_23, param_24, v_260, v_260BufferSize); bool is_clip = (tag_1 & 1u) != 0u; bool is_blend = false; if (is_clip) { uint drawmonoid_base_1 = drawmonoid_start + (4u * element_ix_1); - uint scene_offset = v_242.memory[drawmonoid_base_1 + 2u]; + uint scene_offset = v_260.memory[drawmonoid_base_1 + 2u]; uint dd = drawdata_start + (scene_offset >> uint(2)); - uint blend = _1222.scene[dd]; + uint blend = _1372.scene[dd]; is_blend = blend != 3u; } - bool _1542 = tile.tile.offset != 0u; - bool _1551; - if (!_1542) + bool _1692 = tile.tile.offset != 0u; + bool _1701; + if (!_1692) { - _1551 = (tile.backdrop == 0) == is_clip; + _1701 = (tile.backdrop == 0) == is_clip; } else { - _1551 = _1542; + _1701 = _1692; } - include_tile = _1551 || is_blend; + bool _1708; + if (!_1701) + { + _1708 = is_clip && is_blend; + } + else + { + _1708 = _1701; + } + include_tile = _1708; } if (include_tile) { uint el_slice = el_ix / 32u; uint el_mask = 1u << (el_ix & 31u); - uint _1573 = atomic_fetch_or_explicit((threadgroup atomic_uint*)&sh_bitmaps[el_slice][(y * 16u) + x], el_mask, memory_order_relaxed); + uint _1728 = atomic_fetch_or_explicit((threadgroup atomic_uint*)&sh_bitmaps[el_slice][(y * 16u) + x], el_mask, memory_order_relaxed); } } threadgroup_barrier(mem_flags::mem_threadgroup); @@ -904,32 +993,32 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M uint element_ref_ix = (slice_ix * 32u) + uint(int(spvFindLSB(bitmap))); uint element_ix_2 = sh_elements[element_ref_ix]; bitmap &= (bitmap - 1u); - uint drawtag = _1222.scene[drawtag_start + element_ix_2]; + uint drawtag = _1372.scene[drawtag_start + element_ix_2]; if (clip_zero_depth == 0u) { uint param_25 = element_ref_ix; bool param_26 = mem_ok; - Alloc param_27 = read_tile_alloc(param_25, param_26, v_242, v_242BufferSize); + Alloc param_27 = read_tile_alloc(param_25, param_26, v_260, v_260BufferSize); TileRef param_28 = TileRef{ sh_tile_base[element_ref_ix] + (((sh_tile_stride[element_ref_ix] * tile_y) + tile_x) * 8u) }; - Tile tile_1 = Tile_read(param_27, param_28, v_242, v_242BufferSize); + Tile tile_1 = Tile_read(param_27, param_28, v_260, v_260BufferSize); uint drawmonoid_base_2 = drawmonoid_start + (4u * element_ix_2); - uint scene_offset_1 = v_242.memory[drawmonoid_base_2 + 2u]; - uint info_offset = v_242.memory[drawmonoid_base_2 + 3u]; + uint scene_offset_1 = v_260.memory[drawmonoid_base_2 + 2u]; + uint info_offset = v_260.memory[drawmonoid_base_2 + 3u]; uint dd_1 = drawdata_start + (scene_offset_1 >> uint(2)); uint di = drawinfo_start + (info_offset >> uint(2)); switch (drawtag) { case 68u: { - linewidth = as_type(v_242.memory[di]); + linewidth = as_type(v_260.memory[di]); Alloc param_29 = cmd_alloc; CmdRef param_30 = cmd_ref; uint param_31 = cmd_limit; - bool _1697 = alloc_cmd(param_29, param_30, param_31, v_242, v_242BufferSize); + bool _1853 = alloc_cmd(param_29, param_30, param_31, v_260, v_260BufferSize); cmd_alloc = param_29; cmd_ref = param_30; cmd_limit = param_31; - if (!_1697) + if (!_1853) { break; } @@ -937,13 +1026,13 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M CmdRef param_33 = cmd_ref; Tile param_34 = tile_1; float param_35 = linewidth; - write_fill(param_32, param_33, param_34, param_35, v_242, v_242BufferSize); + write_fill(param_32, param_33, param_34, param_35, v_260, v_260BufferSize); cmd_ref = param_33; - uint rgba = _1222.scene[dd_1]; + uint rgba = _1372.scene[dd_1]; Alloc param_36 = cmd_alloc; CmdRef param_37 = cmd_ref; CmdColor param_38 = CmdColor{ rgba }; - Cmd_Color_write(param_36, param_37, param_38, v_242, v_242BufferSize); + Cmd_Color_write(param_36, param_37, param_38, v_260, v_260BufferSize); cmd_ref.offset += 8u; break; } @@ -952,94 +1041,127 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M Alloc param_39 = cmd_alloc; CmdRef param_40 = cmd_ref; uint param_41 = cmd_limit; - bool _1738 = alloc_cmd(param_39, param_40, param_41, v_242, v_242BufferSize); + bool _1894 = alloc_cmd(param_39, param_40, param_41, v_260, v_260BufferSize); cmd_alloc = param_39; cmd_ref = param_40; cmd_limit = param_41; - if (!_1738) + if (!_1894) { break; } - linewidth = as_type(v_242.memory[di]); + linewidth = as_type(v_260.memory[di]); Alloc param_42 = cmd_alloc; CmdRef param_43 = cmd_ref; Tile param_44 = tile_1; float param_45 = linewidth; - write_fill(param_42, param_43, param_44, param_45, v_242, v_242BufferSize); + write_fill(param_42, param_43, param_44, param_45, v_260, v_260BufferSize); cmd_ref = param_43; - cmd_lin.index = _1222.scene[dd_1]; - cmd_lin.line_x = as_type(v_242.memory[di + 1u]); - cmd_lin.line_y = as_type(v_242.memory[di + 2u]); - cmd_lin.line_c = as_type(v_242.memory[di + 3u]); + cmd_lin.index = _1372.scene[dd_1]; + cmd_lin.line_x = as_type(v_260.memory[di + 1u]); + cmd_lin.line_y = as_type(v_260.memory[di + 2u]); + cmd_lin.line_c = as_type(v_260.memory[di + 3u]); Alloc param_46 = cmd_alloc; CmdRef param_47 = cmd_ref; CmdLinGrad param_48 = cmd_lin; - Cmd_LinGrad_write(param_46, param_47, param_48, v_242, v_242BufferSize); + Cmd_LinGrad_write(param_46, param_47, param_48, v_260, v_260BufferSize); cmd_ref.offset += 20u; break; } - case 72u: + case 732u: { - linewidth = as_type(v_242.memory[di]); Alloc param_49 = cmd_alloc; CmdRef param_50 = cmd_ref; uint param_51 = cmd_limit; - bool _1806 = alloc_cmd(param_49, param_50, param_51, v_242, v_242BufferSize); + bool _1958 = alloc_cmd(param_49, param_50, param_51, v_260, v_260BufferSize); cmd_alloc = param_49; cmd_ref = param_50; cmd_limit = param_51; - if (!_1806) + if (!_1958) { break; } + linewidth = as_type(v_260.memory[di]); Alloc param_52 = cmd_alloc; CmdRef param_53 = cmd_ref; Tile param_54 = tile_1; float param_55 = linewidth; - write_fill(param_52, param_53, param_54, param_55, v_242, v_242BufferSize); + write_fill(param_52, param_53, param_54, param_55, v_260, v_260BufferSize); cmd_ref = param_53; - uint index = _1222.scene[dd_1]; - uint raw1 = _1222.scene[dd_1 + 1u]; - int2 offset_1 = int2(int(raw1 << uint(16)) >> 16, int(raw1) >> 16); + cmd_rad.index = _1372.scene[dd_1]; + cmd_rad.mat = as_type(uint4(v_260.memory[di + 1u], v_260.memory[di + 2u], v_260.memory[di + 3u], v_260.memory[di + 4u])); + cmd_rad.xlat = as_type(uint2(v_260.memory[di + 5u], v_260.memory[di + 6u])); + cmd_rad.c1 = as_type(uint2(v_260.memory[di + 7u], v_260.memory[di + 8u])); + cmd_rad.ra = as_type(v_260.memory[di + 9u]); + cmd_rad.roff = as_type(v_260.memory[di + 10u]); Alloc param_56 = cmd_alloc; CmdRef param_57 = cmd_ref; - CmdImage param_58 = CmdImage{ index, offset_1 }; - Cmd_Image_write(param_56, param_57, param_58, v_242, v_242BufferSize); + CmdRadGrad param_58 = cmd_rad; + Cmd_RadGrad_write(param_56, param_57, param_58, v_260, v_260BufferSize); + cmd_ref.offset += 48u; + break; + } + case 72u: + { + linewidth = as_type(v_260.memory[di]); + Alloc param_59 = cmd_alloc; + CmdRef param_60 = cmd_ref; + uint param_61 = cmd_limit; + bool _2064 = alloc_cmd(param_59, param_60, param_61, v_260, v_260BufferSize); + cmd_alloc = param_59; + cmd_ref = param_60; + cmd_limit = param_61; + if (!_2064) + { + break; + } + Alloc param_62 = cmd_alloc; + CmdRef param_63 = cmd_ref; + Tile param_64 = tile_1; + float param_65 = linewidth; + write_fill(param_62, param_63, param_64, param_65, v_260, v_260BufferSize); + cmd_ref = param_63; + uint index = _1372.scene[dd_1]; + uint raw1 = _1372.scene[dd_1 + 1u]; + int2 offset_1 = int2(int(raw1 << uint(16)) >> 16, int(raw1) >> 16); + Alloc param_66 = cmd_alloc; + CmdRef param_67 = cmd_ref; + CmdImage param_68 = CmdImage{ index, offset_1 }; + Cmd_Image_write(param_66, param_67, param_68, v_260, v_260BufferSize); cmd_ref.offset += 12u; break; } case 5u: { - bool _1859 = tile_1.tile.offset == 0u; - bool _1865; - if (_1859) + bool _2117 = tile_1.tile.offset == 0u; + bool _2123; + if (_2117) { - _1865 = tile_1.backdrop == 0; + _2123 = tile_1.backdrop == 0; } else { - _1865 = _1859; + _2123 = _2117; } - if (_1865) + if (_2123) { clip_zero_depth = clip_depth + 1u; } else { - Alloc param_59 = cmd_alloc; - CmdRef param_60 = cmd_ref; - uint param_61 = cmd_limit; - bool _1877 = alloc_cmd(param_59, param_60, param_61, v_242, v_242BufferSize); - cmd_alloc = param_59; - cmd_ref = param_60; - cmd_limit = param_61; - if (!_1877) + Alloc param_69 = cmd_alloc; + CmdRef param_70 = cmd_ref; + uint param_71 = cmd_limit; + bool _2135 = alloc_cmd(param_69, param_70, param_71, v_260, v_260BufferSize); + cmd_alloc = param_69; + cmd_ref = param_70; + cmd_limit = param_71; + if (!_2135) { break; } - Alloc param_62 = cmd_alloc; - CmdRef param_63 = cmd_ref; - Cmd_BeginClip_write(param_62, param_63, v_242, v_242BufferSize); + Alloc param_72 = cmd_alloc; + CmdRef param_73 = cmd_ref; + Cmd_BeginClip_write(param_72, param_73, v_260, v_260BufferSize); cmd_ref.offset += 4u; } clip_depth++; @@ -1048,28 +1170,28 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M case 37u: { clip_depth--; - Alloc param_64 = cmd_alloc; - CmdRef param_65 = cmd_ref; - uint param_66 = cmd_limit; - bool _1905 = alloc_cmd(param_64, param_65, param_66, v_242, v_242BufferSize); - cmd_alloc = param_64; - cmd_ref = param_65; - cmd_limit = param_66; - if (!_1905) + Alloc param_74 = cmd_alloc; + CmdRef param_75 = cmd_ref; + uint param_76 = cmd_limit; + bool _2163 = alloc_cmd(param_74, param_75, param_76, v_260, v_260BufferSize); + cmd_alloc = param_74; + cmd_ref = param_75; + cmd_limit = param_76; + if (!_2163) { break; } - Alloc param_67 = cmd_alloc; - CmdRef param_68 = cmd_ref; - Tile param_69 = tile_1; - float param_70 = -1.0; - write_fill(param_67, param_68, param_69, param_70, v_242, v_242BufferSize); - cmd_ref = param_68; - uint blend_1 = _1222.scene[dd_1]; - Alloc param_71 = cmd_alloc; - CmdRef param_72 = cmd_ref; - CmdEndClip param_73 = CmdEndClip{ blend_1 }; - Cmd_EndClip_write(param_71, param_72, param_73, v_242, v_242BufferSize); + Alloc param_77 = cmd_alloc; + CmdRef param_78 = cmd_ref; + Tile param_79 = tile_1; + float param_80 = -1.0; + write_fill(param_77, param_78, param_79, param_80, v_260, v_260BufferSize); + cmd_ref = param_78; + uint blend_1 = _1372.scene[dd_1]; + Alloc param_81 = cmd_alloc; + CmdRef param_82 = cmd_ref; + CmdEndClip param_83 = CmdEndClip{ blend_1 }; + Cmd_EndClip_write(param_81, param_82, param_83, v_260, v_260BufferSize); cmd_ref.offset += 8u; break; } @@ -1103,21 +1225,21 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M break; } } - bool _1975 = (bin_tile_x + tile_x) < _854.conf.width_in_tiles; - bool _1984; - if (_1975) + bool _2233 = (bin_tile_x + tile_x) < _1005.conf.width_in_tiles; + bool _2242; + if (_2233) { - _1984 = (bin_tile_y + tile_y) < _854.conf.height_in_tiles; + _2242 = (bin_tile_y + tile_y) < _1005.conf.height_in_tiles; } else { - _1984 = _1975; + _2242 = _2233; } - if (_1984) + if (_2242) { - Alloc param_74 = cmd_alloc; - CmdRef param_75 = cmd_ref; - Cmd_End_write(param_74, param_75, v_242, v_242BufferSize); + Alloc param_84 = cmd_alloc; + CmdRef param_85 = cmd_ref; + Cmd_End_write(param_84, param_85, v_260, v_260BufferSize); } } diff --git a/piet-gpu/shader/gen/coarse.spv b/piet-gpu/shader/gen/coarse.spv index b85fd8cd546262ba315eca88fc6be6357bb82392..fdc10a08e6089784d240cac387f52eef71f95253 100644 GIT binary patch literal 58964 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

%@;%E^{BJKyBd7@2A{FPXKL^{8how>pS!^q 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

VEY4F1u{O|@pw!x2Y@Dm#R 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 E5B6kiRsaA1 diff --git a/piet-gpu/shader/gen/draw_leaf.dxil b/piet-gpu/shader/gen/draw_leaf.dxil index 77396c1d0bfd610df15affb66d2501573c7ca0d8..6353f1986811fff190667a153540dfa7eb872b0f 100644 GIT binary patch literal 6760 zcmeG4$+8c`YI_h}QI#znE6CNs3v{8JZr52Q;?NE!fWAV}6*#{C=+qLffbMLx$?pm|g z+Gl_7{q1jmdw=^RWW}q%ZyEyeg6RM-;TQ{hD(u;?7sBofnP+eS`e9$5w1kAii?(F> z;!qb(jW7QOS1->b4?#NlLgUxKf51lnB?-<&`9gIJcCmAW!wWixLSUo#RHHfjZ|Nwc zQ{6?U*7nQhqPy%+yyY3nj3y|k0dz0|ok+-9l1fc1fIK~<$E)mjBwPT#fHaPgn4H@K z@|cEz9m;NO2}2-@-iesFfppy4539-1ixX+_J2~-P0+}cZ7cXVYa?HtA5ur*l9|YdA z_~B%}RT$8vR;C*)Lls|b$3l}B>2T4MZ$6emDm+XD?GUeWi~>I=N*02EYGNwxGJi9RSi24L2smJZpL?cetH?doV&Ry&QU&^U zUqn3121v7j8vp{uW+^nYwm}P*nh$zxfIa`58YHHR-Wb6JTON7`|DnS#eq3eq_M*`p zzDl(P-vkuzfBnJG_)DXbS$_yT(AZQyd@3DA2Q<7-^9RHcDJq~ayQ*Jd*IpH+cMPxZ zPblaV2zQ=LPgEvIK|xH~UjLM)=bm?3s~wdR|5@u=wj0fAppIM&*iaLAh)V2L8#PrC z1y!31Pzdw>P0cTI@VCG(aN9yMOKi+if>~tcZ*T#dT)+kmlM-++zW#++nS!}R!`$d# zj0l)zQ<54!v@j((0$DaDlqnkILpx3jlavK+v#gSU!~{^*1A3Qv>u=3Q$(TIgH!e_6 zj!rgmI8+a)qd+xHcP>Dv+@YG?dI8XgNf0KJ#EpjOod{69N5@|vm9ad*WU^CS?&cr@ zFr0GY@hk;^**Dq9;dVVBoCv|efDpnmzb3J_Fx6^d1uZ_0sc0!0%qH%6AMddV*j5{$ zvs-|HeE^`_$88%?_MwcP?PqC{b(sFNHyF}~>X;ORTh{ZJGktZsbE5QNYMO##toWvA&mooD6jUd%$ z=i+L7od(x*Iuw0ayASIAnk*{Z1Dnl_n3s?z?6POyq!;3?JggD1W6eYa@L?G5MPOf4 zYWri|i?r*CJxp%gARL4%!C_vfnA^!P;%F#Q!8^xb=rb9Xj^W{s@w5GR&Uf^m?HYg3 zGZKXB6Q!xkg6Dl#^KH>Q%eOVPh3`PuvrSWy4=#_~b_s&>FHH$PEIj6tTN~R21+VrD zo6Q%uwbeE^Ub;f&ZsoM>+ed)uHDMn`f855RweCnRouv2*KbD;dIrR~Ff<<$LHqY&R zwzp+#{ah&PSMzaLBqcjmWWNk}R~5V)dfsh2?~cNVhMUpoCUn6eH0CrK+l?;lN8=84 zoX@{GvNI`jBq=fwKWrUp>uEI`AX)JTl+D*Y_+QPEDlI6zt`QGn+>idd#)t>A= zpH<$UH-Ooy%KQE+BYw<$DKax6GO&Bjj-fWkSM81Twzf~MY`X$YTuNh|SpaS16>lur ztX{vacw^~uqwGO^<$c-X%=<|zgCZjX2dO)U%9nIF%5lAK*N|_%2w7-WMVUG+@p|Uw zP0Z4!4crmn_N{>bgPt$v#`|@&&$!&|n@`dw#VUMy(S_Zrg%)&S?!Y9OJ;Ov{qo7`m@^eI4psC? zH1(HbIAGf3L544X@fDt}1D&+T3KA z_i&8=aFG8uJAaf!bGn;-hnszSC%*$)-yVr?mtjVamT|+XitbiLH%)#|cSbd+qRePa zLuUw_^tmzS8XD8u$?cLb%XR1$X;qoN%KM6nKQYJ{CiwRU8IJ}0aXWv=>h_)5?FX{L z(2dL-P*4+fXlGCWb@nz803^bZE?^kG^!@4Gy05fP#XRh?t$j|q-`U77AO3zX7h|kx zW#bk0_3!+A`NBB)wuM1^-Jxlex_!BC{f{we3|hspES5<*7`gZl_j5Nl#U-Hzc|mJR zL)CYeK6>_U5Nc5FKFa7I#`d-R<@@8YsWV#)cJHvPz@E+@M?U{)S-rt-dov%uq|D`mWmxvv?r98ROeDgWt<% z!UFEhDLDL}6>zGs!}=}yZTH|kl%ZH?*oqa15A97fbPXVtQruaMzuQZI>lo4+!To|PB+RwnM~L~I&q)jgIy;^^q^8Qy!R zyQlvHY>Brlc3RtswvQIH9Y4`FyM09!_p^h&wabFtzL|fiwzxQD4EbkW`e$AG->6Gk z0)S5V6Y7Djo=4py@HqsqULeX*%w~uZ+C@nQ(X!@->_Ab0V#zhdY|*P)&#Y#5(ZhHp zLzE%$T(9_=Au3TkVPqudWh5w>qSeTC!9w^irs%=#Vgxr5Ki3NN1b+7~WD+nExUmCX z6afqtFlxxJ5wRG)bBf3uD)leq;j0~idb zt+5&pxc1EN|;4}_tYvwwxG7;Uc=696|F64|Bt!j0r!0q}f|WGWQ&W?cXS zr;Ht>`9&%HEJ2uEr_!^vIz$kD)abQ=`O3E08u|ts$9L9B-8MS^>fAFOG$zYSqcv2B zILFCCh2SFK`Up@N=wz65Tze}b6rto&0XGAX=uOtR*=m%^oV%6tR_~}A9zE^F&@6q||G<*lG zDlDq1z@VCLs|bT$bW>h*hEiT=oL*?^7uYqYB@Q~-lqWE1y6iw9;BvPR+uq7*Z*?0L z9M$w!l?gJ*(TW)ET=C&5#KaRgz?2bDat-dn%QsI}3?Okq;S9Pn~- zMb2*!g`0%8U5KJUNh&6qr)_E;m7E@`3?wR6RT}Z_!W??%l0j9gacI`KGOJzZ{**kb ziK>2^FarjBL(#+upasXb>uuCWv&$wv1Ozl*rGX734$ZHTP!b3*bU1KuXhtOue@Cp> zqB%a4zYY`S?+FdCq_D)?NbCtg{V!@b~8fL6XGnn#Jx>m=_-f^*9?3z2mYwyj9qCBSO^vr3X|vH z1ZtT|s74RzBaSkK+Pis(OpV~o{bIQ3CEeAbY9yKYE`^l!SBDM=n6U_SD#g9Q*Mc3@ z6mb3p&m>Qtdl{cAzD=7~>B%h;dST9ZRNxxXafOg4dZZ8vd)=kbhSf@07qh$xi0qt1 zq9Ed2!#R4^wa#SIIp}YO-79L!U#~S5dYpy63i_s?uQxbFzu2~Fv@>o0q$n{h>$%-y zWxOjCkIucK?^@PM@p|3op`Pp->AiYhhBWwn%gYh!X1iGycT2I&KcHt~Eo9bCF(W3K zh}rVHMYnV+VmgemgEX+#Ax1i1s+`_VV>U>6S_7KWbiUP&1TL$X+;X@?0b=owllpUQ z=1{$V56EZjg?p||)q2AONb=l8A=y)i*RH*qdvhMxXtz$Ym!V1A=>Tg7eYMvFHJWhi z^{m?KiW>J+7yWbIb#aY8TyR6fyOCTo{RRDA?RBEY7H+*!Poy+YX(WG=11@e6YqA3r!Ew@~Q%8SH)*{8vi8iUsd@Mn=Nq@hbpMduUT{>H;nd} zvLQqEfhOU6FNQREM7JoNfUX)nFAUYDsQba7T8o*h;N5Dum9(xmN>ggq-jI<1rrfBb@jI%Ski?lIoCQbnpOV{@1oYa9rtjq^~kdBghfW#i?yb7 zW6oW@S|9eLPLW21S}Zp>x9q7YEn!wcJq&eJ{4I}$@Ppjp+@;>#7k0_4b)k90Ob>jb zNnyWWlI!bOuY;M(Z!6+Xoivmeu0x-Q`xxV)wCXxKT7ES7ZZ3k%^kF<{B4A)Z+~WY` z?NNLvEBc4onx%X--Feo~-7A&#M5ZaLx-n}*{VL;;g!q-mjM=Uwolnek z%xY?+vL`;%6{^xdHiokb^I)jnQ9s7==Kj&s8Lv0jY@{zUuA$E(qu zI+3NZUx<>K>o%+v%sg^pqRG>7;n-4`anw`A(Q7#^N>DZlx-OXYabCoDD6*rDS5?ks zO`8~q0joDjdf8p~qrI)NxgW|Nl+0)i-_Ny(!>C$xUOrb9to#(8 z-zS^j5_}U`P496j#r4TX+|O9XWFglaPT?(-tQHYb^7q0*pZQ*MGnRsfz5dQiZjulRu-iM>GpLL4V*8^ Y%-==Ge;V|mUJZqmh1>4%IXv0^4ru(<)Bpeg delta 3915 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 P0HdNf%R>LB%w7Kl+8#4O diff --git a/piet-gpu/shader/gen/draw_leaf.hlsl b/piet-gpu/shader/gen/draw_leaf.hlsl index f812f52..734d21e 100644 --- a/piet-gpu/shader/gen/draw_leaf.hlsl +++ b/piet-gpu/shader/gen/draw_leaf.hlsl @@ -46,10 +46,10 @@ static const uint3 gl_WorkGroupSize = uint3(256u, 1u, 1u); static const DrawMonoid _23 = { 0u, 0u, 0u, 0u }; -ByteAddressBuffer _92 : register(t1, space0); -ByteAddressBuffer _102 : register(t2, space0); -ByteAddressBuffer _202 : register(t3, space0); -RWByteAddressBuffer _284 : register(u0, space0); +ByteAddressBuffer _93 : register(t1, space0); +ByteAddressBuffer _103 : register(t2, space0); +ByteAddressBuffer _203 : register(t3, space0); +RWByteAddressBuffer _285 : register(u0, space0); static uint3 gl_WorkGroupID; static uint3 gl_LocalInvocationID; @@ -66,8 +66,8 @@ groupshared DrawMonoid sh_scratch[256]; DrawMonoid map_tag(uint tag_word) { uint has_path = uint(tag_word != 0u); - DrawMonoid _75 = { has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 28u }; - return _75; + DrawMonoid _76 = { has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 60u }; + return _76; } DrawMonoid combine_draw_monoid(DrawMonoid a, DrawMonoid b) @@ -88,15 +88,15 @@ DrawMonoid draw_monoid_identity() void comp_main() { uint ix = gl_GlobalInvocationID.x * 8u; - uint drawtag_base = _92.Load(100) >> uint(2); - uint tag_word = _102.Load((drawtag_base + ix) * 4 + 0); + uint drawtag_base = _93.Load(100) >> uint(2); + uint tag_word = _103.Load((drawtag_base + ix) * 4 + 0); uint param = tag_word; DrawMonoid agg = map_tag(param); DrawMonoid local[8]; local[0] = agg; for (uint i = 1u; i < 8u; i++) { - tag_word = _102.Load(((drawtag_base + ix) + i) * 4 + 0); + tag_word = _103.Load(((drawtag_base + ix) + i) * 4 + 0); uint param_1 = tag_word; DrawMonoid param_2 = agg; DrawMonoid param_3 = map_tag(param_1); @@ -121,15 +121,15 @@ void comp_main() DrawMonoid row = draw_monoid_identity(); if (gl_WorkGroupID.x > 0u) { - DrawMonoid _208; - _208.path_ix = _202.Load((gl_WorkGroupID.x - 1u) * 16 + 0); - _208.clip_ix = _202.Load((gl_WorkGroupID.x - 1u) * 16 + 4); - _208.scene_offset = _202.Load((gl_WorkGroupID.x - 1u) * 16 + 8); - _208.info_offset = _202.Load((gl_WorkGroupID.x - 1u) * 16 + 12); - row.path_ix = _208.path_ix; - row.clip_ix = _208.clip_ix; - row.scene_offset = _208.scene_offset; - row.info_offset = _208.info_offset; + DrawMonoid _209; + _209.path_ix = _203.Load((gl_WorkGroupID.x - 1u) * 16 + 0); + _209.clip_ix = _203.Load((gl_WorkGroupID.x - 1u) * 16 + 4); + _209.scene_offset = _203.Load((gl_WorkGroupID.x - 1u) * 16 + 8); + _209.info_offset = _203.Load((gl_WorkGroupID.x - 1u) * 16 + 12); + row.path_ix = _209.path_ix; + row.clip_ix = _209.clip_ix; + row.scene_offset = _209.scene_offset; + row.info_offset = _209.info_offset; } if (gl_LocalInvocationID.x > 0u) { @@ -137,13 +137,15 @@ void comp_main() DrawMonoid param_7 = sh_scratch[gl_LocalInvocationID.x - 1u]; row = combine_draw_monoid(param_6, param_7); } - uint drawdata_base = _92.Load(104) >> uint(2); - uint drawinfo_base = _92.Load(68) >> uint(2); + uint drawdata_base = _93.Load(104) >> uint(2); + uint drawinfo_base = _93.Load(68) >> uint(2); uint out_ix = gl_GlobalInvocationID.x * 8u; - uint out_base = (_92.Load(44) >> uint(2)) + (out_ix * 4u); - uint clip_out_base = _92.Load(48) >> uint(2); + uint out_base = (_93.Load(44) >> uint(2)) + (out_ix * 4u); + uint clip_out_base = _93.Load(48) >> uint(2); float4 mat; float2 translate; + float2 p0; + float2 p1; for (uint i_2 = 0u; i_2 < 8u; i_2++) { DrawMonoid m = row; @@ -153,31 +155,31 @@ void comp_main() DrawMonoid param_9 = local[i_2 - 1u]; m = combine_draw_monoid(param_8, param_9); } - _284.Store((out_base + (i_2 * 4u)) * 4 + 8, m.path_ix); - _284.Store(((out_base + (i_2 * 4u)) + 1u) * 4 + 8, m.clip_ix); - _284.Store(((out_base + (i_2 * 4u)) + 2u) * 4 + 8, m.scene_offset); - _284.Store(((out_base + (i_2 * 4u)) + 3u) * 4 + 8, m.info_offset); + _285.Store((out_base + (i_2 * 4u)) * 4 + 8, m.path_ix); + _285.Store(((out_base + (i_2 * 4u)) + 1u) * 4 + 8, m.clip_ix); + _285.Store(((out_base + (i_2 * 4u)) + 2u) * 4 + 8, m.scene_offset); + _285.Store(((out_base + (i_2 * 4u)) + 3u) * 4 + 8, m.info_offset); uint dd = drawdata_base + (m.scene_offset >> uint(2)); uint di = drawinfo_base + (m.info_offset >> uint(2)); - tag_word = _102.Load(((drawtag_base + ix) + i_2) * 4 + 0); - if ((((tag_word == 68u) || (tag_word == 276u)) || (tag_word == 72u)) || (tag_word == 5u)) + tag_word = _103.Load(((drawtag_base + ix) + i_2) * 4 + 0); + if (((((tag_word == 68u) || (tag_word == 276u)) || (tag_word == 732u)) || (tag_word == 72u)) || (tag_word == 5u)) { - uint bbox_offset = (_92.Load(40) >> uint(2)) + (6u * m.path_ix); - float bbox_l = float(_284.Load(bbox_offset * 4 + 8)) - 32768.0f; - float bbox_t = float(_284.Load((bbox_offset + 1u) * 4 + 8)) - 32768.0f; - float bbox_r = float(_284.Load((bbox_offset + 2u) * 4 + 8)) - 32768.0f; - float bbox_b = float(_284.Load((bbox_offset + 3u) * 4 + 8)) - 32768.0f; + uint bbox_offset = (_93.Load(40) >> uint(2)) + (6u * m.path_ix); + float bbox_l = float(_285.Load(bbox_offset * 4 + 8)) - 32768.0f; + float bbox_t = float(_285.Load((bbox_offset + 1u) * 4 + 8)) - 32768.0f; + float bbox_r = float(_285.Load((bbox_offset + 2u) * 4 + 8)) - 32768.0f; + float bbox_b = float(_285.Load((bbox_offset + 3u) * 4 + 8)) - 32768.0f; float4 bbox = float4(bbox_l, bbox_t, bbox_r, bbox_b); - float linewidth = asfloat(_284.Load((bbox_offset + 4u) * 4 + 8)); + float linewidth = asfloat(_285.Load((bbox_offset + 4u) * 4 + 8)); uint fill_mode = uint(linewidth >= 0.0f); - if ((linewidth >= 0.0f) || (tag_word == 276u)) + if (((linewidth >= 0.0f) || (tag_word == 276u)) || (tag_word == 732u)) { - uint trans_ix = _284.Load((bbox_offset + 5u) * 4 + 8); - uint t = (_92.Load(36) >> uint(2)) + (6u * trans_ix); - mat = asfloat(uint4(_284.Load(t * 4 + 8), _284.Load((t + 1u) * 4 + 8), _284.Load((t + 2u) * 4 + 8), _284.Load((t + 3u) * 4 + 8))); - if (tag_word == 276u) + uint trans_ix = _285.Load((bbox_offset + 5u) * 4 + 8); + uint t = (_93.Load(36) >> uint(2)) + (6u * trans_ix); + mat = asfloat(uint4(_285.Load(t * 4 + 8), _285.Load((t + 1u) * 4 + 8), _285.Load((t + 2u) * 4 + 8), _285.Load((t + 3u) * 4 + 8))); + if ((tag_word == 276u) || (tag_word == 732u)) { - translate = asfloat(uint2(_284.Load((t + 4u) * 4 + 8), _284.Load((t + 5u) * 4 + 8))); + translate = asfloat(uint2(_285.Load((t + 4u) * 4 + 8), _285.Load((t + 5u) * 4 + 8))); } } if (linewidth >= 0.0f) @@ -189,15 +191,14 @@ void comp_main() case 68u: case 72u: { - _284.Store(di * 4 + 8, asuint(linewidth)); + _285.Store(di * 4 + 8, asuint(linewidth)); break; } case 276u: { - _284.Store(di * 4 + 8, asuint(linewidth)); - uint index = _102.Load(dd * 4 + 0); - float2 p0 = asfloat(uint2(_102.Load((dd + 1u) * 4 + 0), _102.Load((dd + 2u) * 4 + 0))); - float2 p1 = asfloat(uint2(_102.Load((dd + 3u) * 4 + 0), _102.Load((dd + 4u) * 4 + 0))); + _285.Store(di * 4 + 8, asuint(linewidth)); + p0 = asfloat(uint2(_103.Load((dd + 1u) * 4 + 0), _103.Load((dd + 2u) * 4 + 0))); + p1 = asfloat(uint2(_103.Load((dd + 3u) * 4 + 0), _103.Load((dd + 4u) * 4 + 0))); p0 = ((mat.xy * p0.x) + (mat.zw * p0.y)) + translate; p1 = ((mat.xy * p1.x) + (mat.zw * p1.y)) + translate; float2 dxy = p1 - p0; @@ -205,9 +206,38 @@ void comp_main() float line_x = dxy.x * scale; float line_y = dxy.y * scale; float line_c = -((p0.x * line_x) + (p0.y * line_y)); - _284.Store((di + 1u) * 4 + 8, asuint(line_x)); - _284.Store((di + 2u) * 4 + 8, asuint(line_y)); - _284.Store((di + 3u) * 4 + 8, asuint(line_c)); + _285.Store((di + 1u) * 4 + 8, asuint(line_x)); + _285.Store((di + 2u) * 4 + 8, asuint(line_y)); + _285.Store((di + 3u) * 4 + 8, asuint(line_c)); + break; + } + case 732u: + { + p0 = asfloat(uint2(_103.Load((dd + 1u) * 4 + 0), _103.Load((dd + 2u) * 4 + 0))); + p1 = asfloat(uint2(_103.Load((dd + 3u) * 4 + 0), _103.Load((dd + 4u) * 4 + 0))); + float r0 = asfloat(_103.Load((dd + 5u) * 4 + 0)); + float r1 = asfloat(_103.Load((dd + 6u) * 4 + 0)); + float inv_det = 1.0f / ((mat.x * mat.w) - (mat.y * mat.z)); + float4 inv_mat = float4(mat.w, -mat.y, -mat.z, mat.x) * inv_det; + float2 inv_tr = (inv_mat.xz * translate.x) + (inv_mat.yw * translate.y); + inv_tr += p0; + float2 center1 = p1 - p0; + float rr = r1 / (r1 - r0); + float rainv = rr / ((r1 * r1) - dot(center1, center1)); + float2 c1 = center1 * rainv; + float ra = rr * rainv; + float roff = rr - 1.0f; + _285.Store(di * 4 + 8, asuint(linewidth)); + _285.Store((di + 1u) * 4 + 8, asuint(inv_mat.x)); + _285.Store((di + 2u) * 4 + 8, asuint(inv_mat.y)); + _285.Store((di + 3u) * 4 + 8, asuint(inv_mat.z)); + _285.Store((di + 4u) * 4 + 8, asuint(inv_mat.w)); + _285.Store((di + 5u) * 4 + 8, asuint(inv_tr.x)); + _285.Store((di + 6u) * 4 + 8, asuint(inv_tr.y)); + _285.Store((di + 7u) * 4 + 8, asuint(c1.x)); + _285.Store((di + 8u) * 4 + 8, asuint(c1.y)); + _285.Store((di + 9u) * 4 + 8, asuint(ra)); + _285.Store((di + 10u) * 4 + 8, asuint(roff)); break; } case 5u: @@ -223,7 +253,7 @@ void comp_main() { path_ix = m.path_ix; } - _284.Store((clip_out_base + m.clip_ix) * 4 + 8, path_ix); + _285.Store((clip_out_base + m.clip_ix) * 4 + 8, path_ix); } } } diff --git a/piet-gpu/shader/gen/draw_leaf.msl b/piet-gpu/shader/gen/draw_leaf.msl index a8516ae..c11e21b 100644 --- a/piet-gpu/shader/gen/draw_leaf.msl +++ b/piet-gpu/shader/gen/draw_leaf.msl @@ -124,7 +124,7 @@ static inline __attribute__((always_inline)) DrawMonoid map_tag(thread const uint& tag_word) { uint has_path = uint(tag_word != 0u); - return DrawMonoid{ has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 28u }; + return DrawMonoid{ has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 60u }; } static inline __attribute__((always_inline)) @@ -144,19 +144,19 @@ DrawMonoid draw_monoid_identity() return DrawMonoid{ 0u, 0u, 0u, 0u }; } -kernel void main0(device Memory& _284 [[buffer(0)]], const device ConfigBuf& _92 [[buffer(1)]], const device SceneBuf& _102 [[buffer(2)]], const device ParentBuf& _202 [[buffer(3)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +kernel void main0(device Memory& _285 [[buffer(0)]], const device ConfigBuf& _93 [[buffer(1)]], const device SceneBuf& _103 [[buffer(2)]], const device ParentBuf& _203 [[buffer(3)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) { threadgroup DrawMonoid sh_scratch[256]; uint ix = gl_GlobalInvocationID.x * 8u; - uint drawtag_base = _92.conf.drawtag_offset >> uint(2); - uint tag_word = _102.scene[drawtag_base + ix]; + uint drawtag_base = _93.conf.drawtag_offset >> uint(2); + uint tag_word = _103.scene[drawtag_base + ix]; uint param = tag_word; DrawMonoid agg = map_tag(param); spvUnsafeArray local; local[0] = agg; for (uint i = 1u; i < 8u; i++) { - tag_word = _102.scene[(drawtag_base + ix) + i]; + tag_word = _103.scene[(drawtag_base + ix) + i]; uint param_1 = tag_word; DrawMonoid param_2 = agg; DrawMonoid param_3 = map_tag(param_1); @@ -181,11 +181,11 @@ kernel void main0(device Memory& _284 [[buffer(0)]], const device ConfigBuf& _92 DrawMonoid row = draw_monoid_identity(); if (gl_WorkGroupID.x > 0u) { - uint _205 = gl_WorkGroupID.x - 1u; - row.path_ix = _202.parent[_205].path_ix; - row.clip_ix = _202.parent[_205].clip_ix; - row.scene_offset = _202.parent[_205].scene_offset; - row.info_offset = _202.parent[_205].info_offset; + uint _206 = gl_WorkGroupID.x - 1u; + row.path_ix = _203.parent[_206].path_ix; + row.clip_ix = _203.parent[_206].clip_ix; + row.scene_offset = _203.parent[_206].scene_offset; + row.info_offset = _203.parent[_206].info_offset; } if (gl_LocalInvocationID.x > 0u) { @@ -193,13 +193,15 @@ kernel void main0(device Memory& _284 [[buffer(0)]], const device ConfigBuf& _92 DrawMonoid param_7 = sh_scratch[gl_LocalInvocationID.x - 1u]; row = combine_draw_monoid(param_6, param_7); } - uint drawdata_base = _92.conf.drawdata_offset >> uint(2); - uint drawinfo_base = _92.conf.drawinfo_alloc.offset >> uint(2); + uint drawdata_base = _93.conf.drawdata_offset >> uint(2); + uint drawinfo_base = _93.conf.drawinfo_alloc.offset >> uint(2); uint out_ix = gl_GlobalInvocationID.x * 8u; - uint out_base = (_92.conf.drawmonoid_alloc.offset >> uint(2)) + (out_ix * 4u); - uint clip_out_base = _92.conf.clip_alloc.offset >> uint(2); + uint out_base = (_93.conf.drawmonoid_alloc.offset >> uint(2)) + (out_ix * 4u); + uint clip_out_base = _93.conf.clip_alloc.offset >> uint(2); float4 mat; float2 translate; + float2 p0; + float2 p1; for (uint i_2 = 0u; i_2 < 8u; i_2++) { DrawMonoid m = row; @@ -209,31 +211,31 @@ kernel void main0(device Memory& _284 [[buffer(0)]], const device ConfigBuf& _92 DrawMonoid param_9 = local[i_2 - 1u]; m = combine_draw_monoid(param_8, param_9); } - _284.memory[out_base + (i_2 * 4u)] = m.path_ix; - _284.memory[(out_base + (i_2 * 4u)) + 1u] = m.clip_ix; - _284.memory[(out_base + (i_2 * 4u)) + 2u] = m.scene_offset; - _284.memory[(out_base + (i_2 * 4u)) + 3u] = m.info_offset; + _285.memory[out_base + (i_2 * 4u)] = m.path_ix; + _285.memory[(out_base + (i_2 * 4u)) + 1u] = m.clip_ix; + _285.memory[(out_base + (i_2 * 4u)) + 2u] = m.scene_offset; + _285.memory[(out_base + (i_2 * 4u)) + 3u] = m.info_offset; uint dd = drawdata_base + (m.scene_offset >> uint(2)); uint di = drawinfo_base + (m.info_offset >> uint(2)); - tag_word = _102.scene[(drawtag_base + ix) + i_2]; - if ((((tag_word == 68u) || (tag_word == 276u)) || (tag_word == 72u)) || (tag_word == 5u)) + tag_word = _103.scene[(drawtag_base + ix) + i_2]; + if (((((tag_word == 68u) || (tag_word == 276u)) || (tag_word == 732u)) || (tag_word == 72u)) || (tag_word == 5u)) { - uint bbox_offset = (_92.conf.path_bbox_alloc.offset >> uint(2)) + (6u * m.path_ix); - float bbox_l = float(_284.memory[bbox_offset]) - 32768.0; - float bbox_t = float(_284.memory[bbox_offset + 1u]) - 32768.0; - float bbox_r = float(_284.memory[bbox_offset + 2u]) - 32768.0; - float bbox_b = float(_284.memory[bbox_offset + 3u]) - 32768.0; + uint bbox_offset = (_93.conf.path_bbox_alloc.offset >> uint(2)) + (6u * m.path_ix); + float bbox_l = float(_285.memory[bbox_offset]) - 32768.0; + float bbox_t = float(_285.memory[bbox_offset + 1u]) - 32768.0; + float bbox_r = float(_285.memory[bbox_offset + 2u]) - 32768.0; + float bbox_b = float(_285.memory[bbox_offset + 3u]) - 32768.0; float4 bbox = float4(bbox_l, bbox_t, bbox_r, bbox_b); - float linewidth = as_type(_284.memory[bbox_offset + 4u]); + float linewidth = as_type(_285.memory[bbox_offset + 4u]); uint fill_mode = uint(linewidth >= 0.0); - if ((linewidth >= 0.0) || (tag_word == 276u)) + if (((linewidth >= 0.0) || (tag_word == 276u)) || (tag_word == 732u)) { - uint trans_ix = _284.memory[bbox_offset + 5u]; - uint t = (_92.conf.trans_alloc.offset >> uint(2)) + (6u * trans_ix); - mat = as_type(uint4(_284.memory[t], _284.memory[t + 1u], _284.memory[t + 2u], _284.memory[t + 3u])); - if (tag_word == 276u) + uint trans_ix = _285.memory[bbox_offset + 5u]; + uint t = (_93.conf.trans_alloc.offset >> uint(2)) + (6u * trans_ix); + mat = as_type(uint4(_285.memory[t], _285.memory[t + 1u], _285.memory[t + 2u], _285.memory[t + 3u])); + if ((tag_word == 276u) || (tag_word == 732u)) { - translate = as_type(uint2(_284.memory[t + 4u], _284.memory[t + 5u])); + translate = as_type(uint2(_285.memory[t + 4u], _285.memory[t + 5u])); } } if (linewidth >= 0.0) @@ -245,15 +247,14 @@ kernel void main0(device Memory& _284 [[buffer(0)]], const device ConfigBuf& _92 case 68u: case 72u: { - _284.memory[di] = as_type(linewidth); + _285.memory[di] = as_type(linewidth); break; } case 276u: { - _284.memory[di] = as_type(linewidth); - uint index = _102.scene[dd]; - float2 p0 = as_type(uint2(_102.scene[dd + 1u], _102.scene[dd + 2u])); - float2 p1 = as_type(uint2(_102.scene[dd + 3u], _102.scene[dd + 4u])); + _285.memory[di] = as_type(linewidth); + p0 = as_type(uint2(_103.scene[dd + 1u], _103.scene[dd + 2u])); + p1 = as_type(uint2(_103.scene[dd + 3u], _103.scene[dd + 4u])); p0 = ((mat.xy * p0.x) + (mat.zw * p0.y)) + translate; p1 = ((mat.xy * p1.x) + (mat.zw * p1.y)) + translate; float2 dxy = p1 - p0; @@ -261,9 +262,38 @@ kernel void main0(device Memory& _284 [[buffer(0)]], const device ConfigBuf& _92 float line_x = dxy.x * scale; float line_y = dxy.y * scale; float line_c = -((p0.x * line_x) + (p0.y * line_y)); - _284.memory[di + 1u] = as_type(line_x); - _284.memory[di + 2u] = as_type(line_y); - _284.memory[di + 3u] = as_type(line_c); + _285.memory[di + 1u] = as_type(line_x); + _285.memory[di + 2u] = as_type(line_y); + _285.memory[di + 3u] = as_type(line_c); + break; + } + case 732u: + { + p0 = as_type(uint2(_103.scene[dd + 1u], _103.scene[dd + 2u])); + p1 = as_type(uint2(_103.scene[dd + 3u], _103.scene[dd + 4u])); + float r0 = as_type(_103.scene[dd + 5u]); + float r1 = as_type(_103.scene[dd + 6u]); + float inv_det = 1.0 / ((mat.x * mat.w) - (mat.y * mat.z)); + float4 inv_mat = float4(mat.w, -mat.y, -mat.z, mat.x) * inv_det; + float2 inv_tr = (inv_mat.xz * translate.x) + (inv_mat.yw * translate.y); + inv_tr += p0; + float2 center1 = p1 - p0; + float rr = r1 / (r1 - r0); + float rainv = rr / ((r1 * r1) - dot(center1, center1)); + float2 c1 = center1 * rainv; + float ra = rr * rainv; + float roff = rr - 1.0; + _285.memory[di] = as_type(linewidth); + _285.memory[di + 1u] = as_type(inv_mat.x); + _285.memory[di + 2u] = as_type(inv_mat.y); + _285.memory[di + 3u] = as_type(inv_mat.z); + _285.memory[di + 4u] = as_type(inv_mat.w); + _285.memory[di + 5u] = as_type(inv_tr.x); + _285.memory[di + 6u] = as_type(inv_tr.y); + _285.memory[di + 7u] = as_type(c1.x); + _285.memory[di + 8u] = as_type(c1.y); + _285.memory[di + 9u] = as_type(ra); + _285.memory[di + 10u] = as_type(roff); break; } case 5u: @@ -279,7 +309,7 @@ kernel void main0(device Memory& _284 [[buffer(0)]], const device ConfigBuf& _92 { path_ix = m.path_ix; } - _284.memory[clip_out_base + m.clip_ix] = path_ix; + _285.memory[clip_out_base + m.clip_ix] = path_ix; } } } diff --git a/piet-gpu/shader/gen/draw_leaf.spv b/piet-gpu/shader/gen/draw_leaf.spv index d18b2877431a9e97c53c8343f35149e714cb73b8..58dde4387fddb65154909ba8a846f351b6115758 100644 GIT binary patch literal 20104 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 diff --git a/piet-gpu/shader/gen/draw_reduce.dxil b/piet-gpu/shader/gen/draw_reduce.dxil index 4df0ec51bdaf0e94f98038ec143b59dbf32c351f..c101fc8c7f1997dba7d8584f3ec61030fb5e0059 100644 GIT binary patch delta 1034 zcmZ8gZ%kWN6hD1^yuQBo=pJj!URRd94yXgMwp*br)}&A*ve>1@uA>Xl*V@&^h?!=Y z8i{@V_f`=|v!Ov^me>7pWPeO&XF_3%rHdq!F(D(dgP@EcOZ0>DlYa1nw=%zYlY4W1 zzkAO)zjJPmY??3`x_Ud^@V})%c;dQ&#+x5S;@~uH?+m1a0x8};d~Y6Ch;0ptb@qoM zTO_hEGx?x<{b@q=M*bjvS-Jn`@WH~hzK?%PeEVVFtJ_YMyLx+G8Y2M&r~)$vP{Dx0 zvnYdz_)d56Cldp(D{muzSE%jQ=XQ?ms=hmNrinUb(tPKweZfnlydr*qPA-$MYJl(e zfi8laKi~k|XBH_>REw`Yu<~kK$D{?*;;@`yX*c6`gBItPGYsu^BHM_o#`$m=S7glO z@HatIgES^d=k^G0Z$H1A1~XaJaqgLx`Tn?kNQ(>OJS<5uqg0J&`&T7wPMo_^nvMLp zdAi%b+3jc~FJ~SW%Aekuzj&+QQFo@z;rA^kgKwQY`tB>(d5z)hT-^~a@Oj^TW5BR7 z$~U>CN4pQV{po+o;cs&^=6*%aqlLvgzjWNXSXi9*sGX2sp=t7Rti?>I%_)$$krec=5DVd%sQjO)(6aIo`Do4#&8m4pf e^a9m7`5%8nGhL(<&$iqw(l^(rJWisa1o#JgZAotc delta 963 zcmZ9KacmP+9LL|?y*u98P7k_nHwJp$CYElgN1f|7nt1EV=myMAY3YcNca5710YZ!> zg_!K_+RmCVT`a#M?13LKt&cm5_a*G*vwt3`o4vgImtd@G z?<_}IA05y|4&~H?kSK3)UcF{K9lihB4!)fd9h5cc~ zhU6JJKLQFDDZ|gm3P*auNr5zH7cBdf8g5S9hR*H6{~{&1T=PzQA9@)hoGke5skQSW zSb(_?n;U~u{2g=eB0P%n<#0b`!kieIN{>(P-2CUS;IK0bu!{g6aN* zXDpXwru$NY>Gm0;%uSymh2AyE<^a@jWkAQp*H2FzTFkW5swlMJ0y@+=BGdz55B~2-1E$N9t=5DDk{rt{6rmxh>IWB#^Lf{ z0N8wRkmst@yiz`ep1ab*d<<-_o)v{Ws3=-FH(>AUnnk9*zy>X=PZ4>Zy04$2^Wr1N z=Kv-1wpza{*%(SLq=0Z7{VLi#pf~y0;KpM{WQmHda|f?85r6pl5VOmV-*%b!sx!k# zWBt62=S)D_Z4sR8jVT%Sc@!1mxr&ryI3}f)w6n5D6Pm?LKNv30v{$PN69{p5Nkya7 z*b@Q)+Y?I04SFKgr%iCuc}4bQ=E10 zU$^djs4upz=x#Wz-V1o{(G6$he{_2rvB4R2Rrjn08on-C{`SAnMr`nNb#2zOrZ>!Q J5+D4bzW{pEE_MI_ diff --git a/piet-gpu/shader/gen/draw_reduce.hlsl b/piet-gpu/shader/gen/draw_reduce.hlsl index 7220b7e..8311155 100644 --- a/piet-gpu/shader/gen/draw_reduce.hlsl +++ b/piet-gpu/shader/gen/draw_reduce.hlsl @@ -44,10 +44,10 @@ struct Config static const uint3 gl_WorkGroupSize = uint3(256u, 1u, 1u); -ByteAddressBuffer _86 : register(t1, space0); -ByteAddressBuffer _96 : register(t2, space0); -RWByteAddressBuffer _187 : register(u3, space0); -RWByteAddressBuffer _205 : register(u0, space0); +ByteAddressBuffer _87 : register(t1, space0); +ByteAddressBuffer _97 : register(t2, space0); +RWByteAddressBuffer _188 : register(u3, space0); +RWByteAddressBuffer _206 : register(u0, space0); static uint3 gl_WorkGroupID; static uint3 gl_LocalInvocationID; @@ -64,8 +64,8 @@ groupshared DrawMonoid sh_scratch[256]; DrawMonoid map_tag(uint tag_word) { uint has_path = uint(tag_word != 0u); - DrawMonoid _69 = { has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 28u }; - return _69; + DrawMonoid _70 = { has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 60u }; + return _70; } DrawMonoid combine_draw_monoid(DrawMonoid a, DrawMonoid b) @@ -81,13 +81,13 @@ DrawMonoid combine_draw_monoid(DrawMonoid a, DrawMonoid b) void comp_main() { uint ix = gl_GlobalInvocationID.x * 8u; - uint drawtag_base = _86.Load(100) >> uint(2); - uint tag_word = _96.Load((drawtag_base + ix) * 4 + 0); + uint drawtag_base = _87.Load(100) >> uint(2); + uint tag_word = _97.Load((drawtag_base + ix) * 4 + 0); uint param = tag_word; DrawMonoid agg = map_tag(param); for (uint i = 1u; i < 8u; i++) { - uint tag_word_1 = _96.Load(((drawtag_base + ix) + i) * 4 + 0); + uint tag_word_1 = _97.Load(((drawtag_base + ix) + i) * 4 + 0); uint param_1 = tag_word_1; DrawMonoid param_2 = agg; DrawMonoid param_3 = map_tag(param_1); @@ -109,10 +109,10 @@ void comp_main() } if (gl_LocalInvocationID.x == 0u) { - _187.Store(gl_WorkGroupID.x * 16 + 0, agg.path_ix); - _187.Store(gl_WorkGroupID.x * 16 + 4, agg.clip_ix); - _187.Store(gl_WorkGroupID.x * 16 + 8, agg.scene_offset); - _187.Store(gl_WorkGroupID.x * 16 + 12, agg.info_offset); + _188.Store(gl_WorkGroupID.x * 16 + 0, agg.path_ix); + _188.Store(gl_WorkGroupID.x * 16 + 4, agg.clip_ix); + _188.Store(gl_WorkGroupID.x * 16 + 8, agg.scene_offset); + _188.Store(gl_WorkGroupID.x * 16 + 12, agg.info_offset); } } diff --git a/piet-gpu/shader/gen/draw_reduce.msl b/piet-gpu/shader/gen/draw_reduce.msl index 8e409a8..759267c 100644 --- a/piet-gpu/shader/gen/draw_reduce.msl +++ b/piet-gpu/shader/gen/draw_reduce.msl @@ -85,7 +85,7 @@ static inline __attribute__((always_inline)) DrawMonoid map_tag(thread const uint& tag_word) { uint has_path = uint(tag_word != 0u); - return DrawMonoid{ has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 28u }; + return DrawMonoid{ has_path, tag_word & 1u, tag_word & 28u, (tag_word >> uint(4)) & 60u }; } static inline __attribute__((always_inline)) @@ -99,17 +99,17 @@ DrawMonoid combine_draw_monoid(thread const DrawMonoid& a, thread const DrawMono return c; } -kernel void main0(const device ConfigBuf& _86 [[buffer(1)]], const device SceneBuf& _96 [[buffer(2)]], device OutBuf& _187 [[buffer(3)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +kernel void main0(const device ConfigBuf& _87 [[buffer(1)]], const device SceneBuf& _97 [[buffer(2)]], device OutBuf& _188 [[buffer(3)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) { threadgroup DrawMonoid sh_scratch[256]; uint ix = gl_GlobalInvocationID.x * 8u; - uint drawtag_base = _86.conf.drawtag_offset >> uint(2); - uint tag_word = _96.scene[drawtag_base + ix]; + uint drawtag_base = _87.conf.drawtag_offset >> uint(2); + uint tag_word = _97.scene[drawtag_base + ix]; uint param = tag_word; DrawMonoid agg = map_tag(param); for (uint i = 1u; i < 8u; i++) { - uint tag_word_1 = _96.scene[(drawtag_base + ix) + i]; + uint tag_word_1 = _97.scene[(drawtag_base + ix) + i]; uint param_1 = tag_word_1; DrawMonoid param_2 = agg; DrawMonoid param_3 = map_tag(param_1); @@ -131,10 +131,10 @@ kernel void main0(const device ConfigBuf& _86 [[buffer(1)]], const device SceneB } if (gl_LocalInvocationID.x == 0u) { - _187.outbuf[gl_WorkGroupID.x].path_ix = agg.path_ix; - _187.outbuf[gl_WorkGroupID.x].clip_ix = agg.clip_ix; - _187.outbuf[gl_WorkGroupID.x].scene_offset = agg.scene_offset; - _187.outbuf[gl_WorkGroupID.x].info_offset = agg.info_offset; + _188.outbuf[gl_WorkGroupID.x].path_ix = agg.path_ix; + _188.outbuf[gl_WorkGroupID.x].clip_ix = agg.clip_ix; + _188.outbuf[gl_WorkGroupID.x].scene_offset = agg.scene_offset; + _188.outbuf[gl_WorkGroupID.x].info_offset = agg.info_offset; } } diff --git a/piet-gpu/shader/gen/draw_reduce.spv b/piet-gpu/shader/gen/draw_reduce.spv index 4daf43af5dd87dfacfc4f1c5727b15274bdd4266..d6c6fb75fa1dc5d7d65bfa5c1ab890d474b0ee68 100644 GIT binary patch literal 7140 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 T+~3O~YjWLfkUsWJ+`j!AnWXVL literal 7124 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 O^WF$)W8cK>+kXJO$?lT? diff --git a/piet-gpu/shader/gen/draw_root.dxil b/piet-gpu/shader/gen/draw_root.dxil index 4ea23f7699a63ef9a4dcac871e1f234a5ea5b0ef..873fa29da1ffca54fc149ad5b31f42a47e79f5ba 100644 GIT binary patch delta 859 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 delta 9976 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 diff --git a/piet-gpu/shader/gen/kernel4.hlsl b/piet-gpu/shader/gen/kernel4.hlsl index f17b240..92fe05b 100644 --- a/piet-gpu/shader/gen/kernel4.hlsl +++ b/piet-gpu/shader/gen/kernel4.hlsl @@ -48,6 +48,21 @@ struct CmdLinGrad float line_c; }; +struct CmdRadGradRef +{ + uint offset; +}; + +struct CmdRadGrad +{ + uint index; + float4 mat; + float2 xlat; + float2 c1; + float ra; + float roff; +}; + struct CmdImageRef { uint offset; @@ -146,8 +161,8 @@ struct Config static const uint3 gl_WorkGroupSize = uint3(8u, 4u, 1u); -RWByteAddressBuffer _278 : register(u0, space0); -ByteAddressBuffer _1521 : register(t1, space0); +RWByteAddressBuffer _291 : register(u0, space0); +ByteAddressBuffer _1666 : register(t1, space0); RWTexture2D image_atlas : register(u3, space0); RWTexture2D gradients : register(u4, space0); RWTexture2D image : register(u2, space0); @@ -174,8 +189,8 @@ float4 spvUnpackUnorm4x8(uint value) Alloc slice_mem(Alloc a, uint offset, uint size) { - Alloc _291 = { a.offset + offset }; - return _291; + Alloc _304 = { a.offset + offset }; + return _304; } bool touch_mem(Alloc alloc, uint offset) @@ -191,7 +206,7 @@ uint read_mem(Alloc alloc, uint offset) { return 0u; } - uint v = _278.Load(offset * 4 + 8); + uint v = _291.Load(offset * 4 + 8); return v; } @@ -200,8 +215,8 @@ CmdTag Cmd_tag(Alloc a, CmdRef ref) Alloc param = a; uint param_1 = ref.offset >> uint(2); uint tag_and_flags = read_mem(param, param_1); - CmdTag _525 = { tag_and_flags & 65535u, tag_and_flags >> uint(16) }; - return _525; + CmdTag _663 = { tag_and_flags & 65535u, tag_and_flags >> uint(16) }; + return _663; } CmdStroke CmdStroke_read(Alloc a, CmdStrokeRef ref) @@ -221,9 +236,9 @@ CmdStroke CmdStroke_read(Alloc a, CmdStrokeRef ref) CmdStroke Cmd_Stroke_read(Alloc a, CmdRef ref) { - CmdStrokeRef _542 = { ref.offset + 4u }; + CmdStrokeRef _679 = { ref.offset + 4u }; Alloc param = a; - CmdStrokeRef param_1 = _542; + CmdStrokeRef param_1 = _679; return CmdStroke_read(param, param_1); } @@ -259,8 +274,8 @@ TileSeg TileSeg_read(Alloc a, TileSegRef ref) s.origin = float2(asfloat(raw0), asfloat(raw1)); s._vector = float2(asfloat(raw2), asfloat(raw3)); s.y_edge = asfloat(raw4); - TileSegRef _675 = { raw5 }; - s.next = _675; + TileSegRef _820 = { raw5 }; + s.next = _820; return s; } @@ -286,9 +301,9 @@ CmdFill CmdFill_read(Alloc a, CmdFillRef ref) CmdFill Cmd_Fill_read(Alloc a, CmdRef ref) { - CmdFillRef _532 = { ref.offset + 4u }; + CmdFillRef _669 = { ref.offset + 4u }; Alloc param = a; - CmdFillRef param_1 = _532; + CmdFillRef param_1 = _669; return CmdFill_read(param, param_1); } @@ -305,9 +320,9 @@ CmdAlpha CmdAlpha_read(Alloc a, CmdAlphaRef ref) CmdAlpha Cmd_Alpha_read(Alloc a, CmdRef ref) { - CmdAlphaRef _552 = { ref.offset + 4u }; + CmdAlphaRef _689 = { ref.offset + 4u }; Alloc param = a; - CmdAlphaRef param_1 = _552; + CmdAlphaRef param_1 = _689; return CmdAlpha_read(param, param_1); } @@ -324,9 +339,9 @@ CmdColor CmdColor_read(Alloc a, CmdColorRef ref) CmdColor Cmd_Color_read(Alloc a, CmdRef ref) { - CmdColorRef _562 = { ref.offset + 4u }; + CmdColorRef _699 = { ref.offset + 4u }; Alloc param = a; - CmdColorRef param_1 = _562; + CmdColorRef param_1 = _699; return CmdColor_read(param, param_1); } @@ -370,12 +385,66 @@ CmdLinGrad CmdLinGrad_read(Alloc a, CmdLinGradRef ref) CmdLinGrad Cmd_LinGrad_read(Alloc a, CmdRef ref) { - CmdLinGradRef _572 = { ref.offset + 4u }; + CmdLinGradRef _709 = { ref.offset + 4u }; Alloc param = a; - CmdLinGradRef param_1 = _572; + CmdLinGradRef param_1 = _709; return CmdLinGrad_read(param, param_1); } +CmdRadGrad CmdRadGrad_read(Alloc a, CmdRadGradRef ref) +{ + uint ix = ref.offset >> uint(2); + Alloc param = a; + uint param_1 = ix + 0u; + uint raw0 = read_mem(param, param_1); + Alloc param_2 = a; + uint param_3 = ix + 1u; + uint raw1 = read_mem(param_2, param_3); + Alloc param_4 = a; + uint param_5 = ix + 2u; + uint raw2 = read_mem(param_4, param_5); + Alloc param_6 = a; + uint param_7 = ix + 3u; + uint raw3 = read_mem(param_6, param_7); + Alloc param_8 = a; + uint param_9 = ix + 4u; + uint raw4 = read_mem(param_8, param_9); + Alloc param_10 = a; + uint param_11 = ix + 5u; + uint raw5 = read_mem(param_10, param_11); + Alloc param_12 = a; + uint param_13 = ix + 6u; + uint raw6 = read_mem(param_12, param_13); + Alloc param_14 = a; + uint param_15 = ix + 7u; + uint raw7 = read_mem(param_14, param_15); + Alloc param_16 = a; + uint param_17 = ix + 8u; + uint raw8 = read_mem(param_16, param_17); + Alloc param_18 = a; + uint param_19 = ix + 9u; + uint raw9 = read_mem(param_18, param_19); + Alloc param_20 = a; + uint param_21 = ix + 10u; + uint raw10 = read_mem(param_20, param_21); + CmdRadGrad s; + s.index = raw0; + s.mat = float4(asfloat(raw1), asfloat(raw2), asfloat(raw3), asfloat(raw4)); + s.xlat = float2(asfloat(raw5), asfloat(raw6)); + s.c1 = float2(asfloat(raw7), asfloat(raw8)); + s.ra = asfloat(raw9); + s.roff = asfloat(raw10); + return s; +} + +CmdRadGrad Cmd_RadGrad_read(Alloc a, CmdRef ref) +{ + CmdRadGradRef _719 = { ref.offset + 4u }; + Alloc param = a; + CmdRadGradRef param_1 = _719; + return CmdRadGrad_read(param, param_1); +} + CmdImage CmdImage_read(Alloc a, CmdImageRef ref) { uint ix = ref.offset >> uint(2); @@ -393,9 +462,9 @@ CmdImage CmdImage_read(Alloc a, CmdImageRef ref) CmdImage Cmd_Image_read(Alloc a, CmdRef ref) { - CmdImageRef _582 = { ref.offset + 4u }; + CmdImageRef _729 = { ref.offset + 4u }; Alloc param = a; - CmdImageRef param_1 = _582; + CmdImageRef param_1 = _729; return CmdImage_read(param, param_1); } @@ -408,10 +477,10 @@ void fillImage(out float4 spvReturnValue[8], uint2 xy, CmdImage cmd_img) int2 uv = int2(xy + chunk_offset(param)) + cmd_img.offset; float4 fg_rgba = image_atlas[uv]; float3 param_1 = fg_rgba.xyz; - float3 _1493 = fromsRGB(param_1); - fg_rgba.x = _1493.x; - fg_rgba.y = _1493.y; - fg_rgba.z = _1493.z; + float3 _1638 = fromsRGB(param_1); + fg_rgba.x = _1638.x; + fg_rgba.y = _1638.y; + fg_rgba.z = _1638.z; rgba[i] = fg_rgba; } spvReturnValue = rgba; @@ -445,9 +514,9 @@ CmdEndClip CmdEndClip_read(Alloc a, CmdEndClipRef ref) CmdEndClip Cmd_EndClip_read(Alloc a, CmdRef ref) { - CmdEndClipRef _592 = { ref.offset + 4u }; + CmdEndClipRef _739 = { ref.offset + 4u }; Alloc param = a; - CmdEndClipRef param_1 = _592; + CmdEndClipRef param_1 = _739; return CmdEndClip_read(param, param_1); } @@ -637,8 +706,8 @@ float3 set_lum(float3 c, float l) { float3 param = c; float3 param_1 = c + (l - lum(param)).xxx; - float3 _901 = clip_color(param_1); - return _901; + float3 _1046 = clip_color(param_1); + return _1046; } float3 mix_blend(float3 cb, float3 cs, uint mode) @@ -726,9 +795,9 @@ float3 mix_blend(float3 cb, float3 cs, uint mode) float3 param_20 = cb; float3 param_21 = cs; float param_22 = sat(param_20); - float3 _1192 = set_sat(param_21, param_22); + float3 _1337 = set_sat(param_21, param_22); float3 param_23 = cb; - float3 param_24 = _1192; + float3 param_24 = _1337; float param_25 = lum(param_23); b = set_lum(param_24, param_25); break; @@ -738,9 +807,9 @@ float3 mix_blend(float3 cb, float3 cs, uint mode) float3 param_26 = cs; float3 param_27 = cb; float param_28 = sat(param_26); - float3 _1206 = set_sat(param_27, param_28); + float3 _1351 = set_sat(param_27, param_28); float3 param_29 = cb; - float3 param_30 = _1206; + float3 param_30 = _1351; float param_31 = lum(param_29); b = set_lum(param_30, param_31); break; @@ -877,24 +946,24 @@ CmdJump CmdJump_read(Alloc a, CmdJumpRef ref) CmdJump Cmd_Jump_read(Alloc a, CmdRef ref) { - CmdJumpRef _602 = { ref.offset + 4u }; + CmdJumpRef _749 = { ref.offset + 4u }; Alloc param = a; - CmdJumpRef param_1 = _602; + CmdJumpRef param_1 = _749; return CmdJump_read(param, param_1); } void comp_main() { - uint tile_ix = (gl_WorkGroupID.y * _1521.Load(8)) + gl_WorkGroupID.x; - Alloc _1536; - _1536.offset = _1521.Load(24); + uint tile_ix = (gl_WorkGroupID.y * _1666.Load(8)) + gl_WorkGroupID.x; + Alloc _1681; + _1681.offset = _1666.Load(24); Alloc param; - param.offset = _1536.offset; + param.offset = _1681.offset; uint param_1 = tile_ix * 1024u; uint param_2 = 1024u; Alloc cmd_alloc = slice_mem(param, param_1, param_2); - CmdRef _1545 = { cmd_alloc.offset }; - CmdRef cmd_ref = _1545; + CmdRef _1690 = { cmd_alloc.offset }; + CmdRef cmd_ref = _1690; uint2 xy_uint = uint2(gl_LocalInvocationID.x + (16u * gl_WorkGroupID.x), gl_LocalInvocationID.y + (16u * gl_WorkGroupID.y)); float2 xy = float2(xy_uint); float4 rgba[8]; @@ -903,7 +972,7 @@ void comp_main() rgba[i] = 0.0f.xxxx; } uint clip_depth = 0u; - bool mem_ok = _278.Load(4) == 0u; + bool mem_ok = _291.Load(4) == 0u; float df[8]; TileSegRef tile_seg_ref; float area[8]; @@ -928,8 +997,8 @@ void comp_main() { df[k] = 1000000000.0f; } - TileSegRef _1638 = { stroke.tile_ref }; - tile_seg_ref = _1638; + TileSegRef _1784 = { stroke.tile_ref }; + tile_seg_ref = _1784; do { uint param_7 = tile_seg_ref.offset; @@ -965,8 +1034,8 @@ void comp_main() { area[k_3] = float(fill.backdrop); } - TileSegRef _1758 = { fill.tile_ref }; - tile_seg_ref = _1758; + TileSegRef _1904 = { fill.tile_ref }; + tile_seg_ref = _1904; do { uint param_15 = tile_seg_ref.offset; @@ -1055,11 +1124,12 @@ void comp_main() int x = int(round(clamp(my_d, 0.0f, 1.0f) * 511.0f)); float4 fg_rgba = gradients[int2(x, int(lin.index))]; float3 param_29 = fg_rgba.xyz; - float3 _2092 = fromsRGB(param_29); - fg_rgba.x = _2092.x; - fg_rgba.y = _2092.y; - fg_rgba.z = _2092.z; - rgba[k_9] = fg_rgba; + float3 _2238 = fromsRGB(param_29); + fg_rgba.x = _2238.x; + fg_rgba.y = _2238.y; + fg_rgba.z = _2238.z; + float4 fg_k_1 = fg_rgba * area[k_9]; + rgba[k_9] = (rgba[k_9] * (1.0f - fg_k_1.w)) + fg_k_1; } cmd_ref.offset += 20u; break; @@ -1068,74 +1138,100 @@ void comp_main() { Alloc param_30 = cmd_alloc; CmdRef param_31 = cmd_ref; - CmdImage fill_img = Cmd_Image_read(param_30, param_31); - uint2 param_32 = xy_uint; - CmdImage param_33 = fill_img; - float4 _2121[8]; - fillImage(_2121, param_32, param_33); - float4 img[8] = _2121; + CmdRadGrad rad = Cmd_RadGrad_read(param_30, param_31); for (uint k_10 = 0u; k_10 < 8u; k_10++) { - float4 fg_k_1 = img[k_10] * area[k_10]; - rgba[k_10] = (rgba[k_10] * (1.0f - fg_k_1.w)) + fg_k_1; + uint param_32 = k_10; + float2 my_xy_1 = xy + float2(chunk_offset(param_32)); + my_xy_1 = ((rad.mat.xz * my_xy_1.x) + (rad.mat.yw * my_xy_1.y)) - rad.xlat; + float ba = dot(my_xy_1, rad.c1); + float ca = rad.ra * dot(my_xy_1, my_xy_1); + float t_2 = (sqrt((ba * ba) + ca) - ba) - rad.roff; + int x_1 = int(round(clamp(t_2, 0.0f, 1.0f) * 511.0f)); + float4 fg_rgba_1 = gradients[int2(x_1, int(rad.index))]; + float3 param_33 = fg_rgba_1.xyz; + float3 _2348 = fromsRGB(param_33); + fg_rgba_1.x = _2348.x; + fg_rgba_1.y = _2348.y; + fg_rgba_1.z = _2348.z; + float4 fg_k_2 = fg_rgba_1 * area[k_10]; + rgba[k_10] = (rgba[k_10] * (1.0f - fg_k_2.w)) + fg_k_2; } - cmd_ref.offset += 12u; + cmd_ref.offset += 48u; break; } case 8u: { + Alloc param_34 = cmd_alloc; + CmdRef param_35 = cmd_ref; + CmdImage fill_img = Cmd_Image_read(param_34, param_35); + uint2 param_36 = xy_uint; + CmdImage param_37 = fill_img; + float4 _2391[8]; + fillImage(_2391, param_36, param_37); + float4 img[8] = _2391; for (uint k_11 = 0u; k_11 < 8u; k_11++) + { + float4 fg_k_3 = img[k_11] * area[k_11]; + rgba[k_11] = (rgba[k_11] * (1.0f - fg_k_3.w)) + fg_k_3; + } + cmd_ref.offset += 12u; + break; + } + case 9u: + { + for (uint k_12 = 0u; k_12 < 8u; k_12++) { uint d_2 = min(clip_depth, 127u); - float4 param_34 = float4(rgba[k_11]); - uint _2184 = packsRGB(param_34); - blend_stack[d_2][k_11] = _2184; - rgba[k_11] = 0.0f.xxxx; + float4 param_38 = float4(rgba[k_12]); + uint _2454 = packsRGB(param_38); + blend_stack[d_2][k_12] = _2454; + rgba[k_12] = 0.0f.xxxx; } clip_depth++; cmd_ref.offset += 4u; break; } - case 9u: + case 10u: { - Alloc param_35 = cmd_alloc; - CmdRef param_36 = cmd_ref; - CmdEndClip end_clip = Cmd_EndClip_read(param_35, param_36); + Alloc param_39 = cmd_alloc; + CmdRef param_40 = cmd_ref; + CmdEndClip end_clip = Cmd_EndClip_read(param_39, param_40); uint blend_mode = end_clip.blend >> uint(8); uint comp_mode = end_clip.blend & 255u; clip_depth--; - for (uint k_12 = 0u; k_12 < 8u; k_12++) + for (uint k_13 = 0u; k_13 < 8u; k_13++) { uint d_3 = min(clip_depth, 127u); - uint param_37 = blend_stack[d_3][k_12]; - float4 bg = unpacksRGB(param_37); - float4 fg_1 = rgba[k_12] * area[k_12]; - float3 param_38 = bg.xyz; - float3 param_39 = fg_1.xyz; - uint param_40 = blend_mode; - float3 blend = mix_blend(param_38, param_39, param_40); - float4 _2251 = fg_1; - float _2255 = fg_1.w; - float3 _2262 = lerp(_2251.xyz, blend, float((_2255 * bg.w) > 0.0f).xxx); - fg_1.x = _2262.x; - fg_1.y = _2262.y; - fg_1.z = _2262.z; - float3 param_41 = bg.xyz; - float3 param_42 = fg_1.xyz; - float param_43 = bg.w; - float param_44 = fg_1.w; - uint param_45 = comp_mode; - rgba[k_12] = mix_compose(param_41, param_42, param_43, param_44, param_45); + uint param_41 = blend_stack[d_3][k_13]; + float4 bg = unpacksRGB(param_41); + float4 fg_1 = rgba[k_13] * area[k_13]; + float3 param_42 = bg.xyz; + float3 param_43 = fg_1.xyz; + uint param_44 = blend_mode; + float3 blend = mix_blend(param_42, param_43, param_44); + float4 _2521 = fg_1; + float _2525 = fg_1.w; + float3 _2532 = lerp(_2521.xyz, blend, float((_2525 * bg.w) > 0.0f).xxx); + fg_1.x = _2532.x; + fg_1.y = _2532.y; + fg_1.z = _2532.z; + float3 param_45 = bg.xyz; + float3 param_46 = fg_1.xyz; + float param_47 = bg.w; + float param_48 = fg_1.w; + uint param_49 = comp_mode; + rgba[k_13] = mix_compose(param_45, param_46, param_47, param_48, param_49); } cmd_ref.offset += 8u; break; } - case 10u: + case 11u: { - Alloc param_46 = cmd_alloc; - CmdRef param_47 = cmd_ref; - CmdRef _2299 = { Cmd_Jump_read(param_46, param_47).new_ref }; - cmd_ref = _2299; + Alloc param_50 = cmd_alloc; + CmdRef param_51 = cmd_ref; + CmdRef _2569 = { Cmd_Jump_read(param_50, param_51).new_ref }; + cmd_ref = _2569; cmd_alloc.offset = cmd_ref.offset; break; } @@ -1143,9 +1239,9 @@ void comp_main() } for (uint i_1 = 0u; i_1 < 8u; i_1++) { - uint param_48 = i_1; - float3 param_49 = rgba[i_1].xyz; - image[int2(xy_uint + chunk_offset(param_48))] = float4(tosRGB(param_49), rgba[i_1].w); + uint param_52 = i_1; + float3 param_53 = rgba[i_1].xyz; + image[int2(xy_uint + chunk_offset(param_52))] = float4(tosRGB(param_53), rgba[i_1].w); } } diff --git a/piet-gpu/shader/gen/kernel4.msl b/piet-gpu/shader/gen/kernel4.msl index c1f41af..6489563 100644 --- a/piet-gpu/shader/gen/kernel4.msl +++ b/piet-gpu/shader/gen/kernel4.msl @@ -94,6 +94,21 @@ struct CmdLinGrad float line_c; }; +struct CmdRadGradRef +{ + uint offset; +}; + +struct CmdRadGrad +{ + uint index; + float4 mat; + float2 xlat; + float2 c1; + float ra; + float roff; +}; + struct CmdImageRef { uint offset; @@ -222,7 +237,7 @@ bool touch_mem(thread const Alloc& alloc, thread const uint& offset) } static inline __attribute__((always_inline)) -uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memory& v_278) +uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memory& v_291) { Alloc param = alloc; uint param_1 = offset; @@ -230,29 +245,29 @@ uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memor { return 0u; } - uint v = v_278.memory[offset]; + uint v = v_291.memory[offset]; return v; } static inline __attribute__((always_inline)) -CmdTag Cmd_tag(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdTag Cmd_tag(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint tag_and_flags = read_mem(param, param_1, v_278); + uint tag_and_flags = read_mem(param, param_1, v_291); return CmdTag{ tag_and_flags & 65535u, tag_and_flags >> uint(16) }; } static inline __attribute__((always_inline)) -CmdStroke CmdStroke_read(thread const Alloc& a, thread const CmdStrokeRef& ref, device Memory& v_278) +CmdStroke CmdStroke_read(thread const Alloc& a, thread const CmdStrokeRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); CmdStroke s; s.tile_ref = raw0; s.half_width = as_type(raw1); @@ -260,11 +275,11 @@ CmdStroke CmdStroke_read(thread const Alloc& a, thread const CmdStrokeRef& ref, } static inline __attribute__((always_inline)) -CmdStroke Cmd_Stroke_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdStroke Cmd_Stroke_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdStrokeRef param_1 = CmdStrokeRef{ ref.offset + 4u }; - return CmdStroke_read(param, param_1, v_278); + return CmdStroke_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -276,27 +291,27 @@ Alloc new_alloc(thread const uint& offset, thread const uint& size, thread const } static inline __attribute__((always_inline)) -TileSeg TileSeg_read(thread const Alloc& a, thread const TileSegRef& ref, device Memory& v_278) +TileSeg TileSeg_read(thread const Alloc& a, thread const TileSegRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); Alloc param_4 = a; uint param_5 = ix + 2u; - uint raw2 = read_mem(param_4, param_5, v_278); + uint raw2 = read_mem(param_4, param_5, v_291); Alloc param_6 = a; uint param_7 = ix + 3u; - uint raw3 = read_mem(param_6, param_7, v_278); + uint raw3 = read_mem(param_6, param_7, v_291); Alloc param_8 = a; uint param_9 = ix + 4u; - uint raw4 = read_mem(param_8, param_9, v_278); + uint raw4 = read_mem(param_8, param_9, v_291); Alloc param_10 = a; uint param_11 = ix + 5u; - uint raw5 = read_mem(param_10, param_11, v_278); + uint raw5 = read_mem(param_10, param_11, v_291); TileSeg s; s.origin = float2(as_type(raw0), as_type(raw1)); s.vector = float2(as_type(raw2), as_type(raw3)); @@ -312,15 +327,15 @@ uint2 chunk_offset(thread const uint& i) } static inline __attribute__((always_inline)) -CmdFill CmdFill_read(thread const Alloc& a, thread const CmdFillRef& ref, device Memory& v_278) +CmdFill CmdFill_read(thread const Alloc& a, thread const CmdFillRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); CmdFill s; s.tile_ref = raw0; s.backdrop = int(raw1); @@ -328,51 +343,51 @@ CmdFill CmdFill_read(thread const Alloc& a, thread const CmdFillRef& ref, device } static inline __attribute__((always_inline)) -CmdFill Cmd_Fill_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdFill Cmd_Fill_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdFillRef param_1 = CmdFillRef{ ref.offset + 4u }; - return CmdFill_read(param, param_1, v_278); + return CmdFill_read(param, param_1, v_291); } static inline __attribute__((always_inline)) -CmdAlpha CmdAlpha_read(thread const Alloc& a, thread const CmdAlphaRef& ref, device Memory& v_278) +CmdAlpha CmdAlpha_read(thread const Alloc& a, thread const CmdAlphaRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdAlpha s; s.alpha = as_type(raw0); return s; } static inline __attribute__((always_inline)) -CmdAlpha Cmd_Alpha_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdAlpha Cmd_Alpha_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdAlphaRef param_1 = CmdAlphaRef{ ref.offset + 4u }; - return CmdAlpha_read(param, param_1, v_278); + return CmdAlpha_read(param, param_1, v_291); } static inline __attribute__((always_inline)) -CmdColor CmdColor_read(thread const Alloc& a, thread const CmdColorRef& ref, device Memory& v_278) +CmdColor CmdColor_read(thread const Alloc& a, thread const CmdColorRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdColor s; s.rgba_color = raw0; return s; } static inline __attribute__((always_inline)) -CmdColor Cmd_Color_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdColor Cmd_Color_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdColorRef param_1 = CmdColorRef{ ref.offset + 4u }; - return CmdColor_read(param, param_1, v_278); + return CmdColor_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -393,21 +408,21 @@ float4 unpacksRGB(thread const uint& srgba) } static inline __attribute__((always_inline)) -CmdLinGrad CmdLinGrad_read(thread const Alloc& a, thread const CmdLinGradRef& ref, device Memory& v_278) +CmdLinGrad CmdLinGrad_read(thread const Alloc& a, thread const CmdLinGradRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); Alloc param_4 = a; uint param_5 = ix + 2u; - uint raw2 = read_mem(param_4, param_5, v_278); + uint raw2 = read_mem(param_4, param_5, v_291); Alloc param_6 = a; uint param_7 = ix + 3u; - uint raw3 = read_mem(param_6, param_7, v_278); + uint raw3 = read_mem(param_6, param_7, v_291); CmdLinGrad s; s.index = raw0; s.line_x = as_type(raw1); @@ -417,23 +432,78 @@ CmdLinGrad CmdLinGrad_read(thread const Alloc& a, thread const CmdLinGradRef& re } static inline __attribute__((always_inline)) -CmdLinGrad Cmd_LinGrad_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdLinGrad Cmd_LinGrad_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdLinGradRef param_1 = CmdLinGradRef{ ref.offset + 4u }; - return CmdLinGrad_read(param, param_1, v_278); + return CmdLinGrad_read(param, param_1, v_291); } static inline __attribute__((always_inline)) -CmdImage CmdImage_read(thread const Alloc& a, thread const CmdImageRef& ref, device Memory& v_278) +CmdRadGrad CmdRadGrad_read(thread const Alloc& a, thread const CmdRadGradRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); + Alloc param_4 = a; + uint param_5 = ix + 2u; + uint raw2 = read_mem(param_4, param_5, v_291); + Alloc param_6 = a; + uint param_7 = ix + 3u; + uint raw3 = read_mem(param_6, param_7, v_291); + Alloc param_8 = a; + uint param_9 = ix + 4u; + uint raw4 = read_mem(param_8, param_9, v_291); + Alloc param_10 = a; + uint param_11 = ix + 5u; + uint raw5 = read_mem(param_10, param_11, v_291); + Alloc param_12 = a; + uint param_13 = ix + 6u; + uint raw6 = read_mem(param_12, param_13, v_291); + Alloc param_14 = a; + uint param_15 = ix + 7u; + uint raw7 = read_mem(param_14, param_15, v_291); + Alloc param_16 = a; + uint param_17 = ix + 8u; + uint raw8 = read_mem(param_16, param_17, v_291); + Alloc param_18 = a; + uint param_19 = ix + 9u; + uint raw9 = read_mem(param_18, param_19, v_291); + Alloc param_20 = a; + uint param_21 = ix + 10u; + uint raw10 = read_mem(param_20, param_21, v_291); + CmdRadGrad s; + s.index = raw0; + s.mat = float4(as_type(raw1), as_type(raw2), as_type(raw3), as_type(raw4)); + s.xlat = float2(as_type(raw5), as_type(raw6)); + s.c1 = float2(as_type(raw7), as_type(raw8)); + s.ra = as_type(raw9); + s.roff = as_type(raw10); + return s; +} + +static inline __attribute__((always_inline)) +CmdRadGrad Cmd_RadGrad_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) +{ + Alloc param = a; + CmdRadGradRef param_1 = CmdRadGradRef{ ref.offset + 4u }; + return CmdRadGrad_read(param, param_1, v_291); +} + +static inline __attribute__((always_inline)) +CmdImage CmdImage_read(thread const Alloc& a, thread const CmdImageRef& ref, device Memory& v_291) +{ + uint ix = ref.offset >> uint(2); + Alloc param = a; + uint param_1 = ix + 0u; + uint raw0 = read_mem(param, param_1, v_291); + Alloc param_2 = a; + uint param_3 = ix + 1u; + uint raw1 = read_mem(param_2, param_3, v_291); CmdImage s; s.index = raw0; s.offset = int2(int(raw1 << uint(16)) >> 16, int(raw1) >> 16); @@ -441,11 +511,11 @@ CmdImage CmdImage_read(thread const Alloc& a, thread const CmdImageRef& ref, dev } static inline __attribute__((always_inline)) -CmdImage Cmd_Image_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdImage Cmd_Image_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdImageRef param_1 = CmdImageRef{ ref.offset + 4u }; - return CmdImage_read(param, param_1, v_278); + return CmdImage_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -458,10 +528,10 @@ spvUnsafeArray fillImage(thread const uint2& xy, thread const CmdImag int2 uv = int2(xy + chunk_offset(param)) + cmd_img.offset; float4 fg_rgba = image_atlas.read(uint2(uv)); float3 param_1 = fg_rgba.xyz; - float3 _1493 = fromsRGB(param_1); - fg_rgba.x = _1493.x; - fg_rgba.y = _1493.y; - fg_rgba.z = _1493.z; + float3 _1638 = fromsRGB(param_1); + fg_rgba.x = _1638.x; + fg_rgba.y = _1638.y; + fg_rgba.z = _1638.z; rgba[i] = fg_rgba; } return rgba; @@ -485,23 +555,23 @@ uint packsRGB(thread float4& rgba) } static inline __attribute__((always_inline)) -CmdEndClip CmdEndClip_read(thread const Alloc& a, thread const CmdEndClipRef& ref, device Memory& v_278) +CmdEndClip CmdEndClip_read(thread const Alloc& a, thread const CmdEndClipRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdEndClip s; s.blend = raw0; return s; } static inline __attribute__((always_inline)) -CmdEndClip Cmd_EndClip_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdEndClip Cmd_EndClip_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdEndClipRef param_1 = CmdEndClipRef{ ref.offset + 4u }; - return CmdEndClip_read(param, param_1, v_278); + return CmdEndClip_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -701,8 +771,8 @@ float3 set_lum(thread const float3& c, thread const float& l) { float3 param = c; float3 param_1 = c + float3(l - lum(param)); - float3 _901 = clip_color(param_1); - return _901; + float3 _1046 = clip_color(param_1); + return _1046; } static inline __attribute__((always_inline)) @@ -791,9 +861,9 @@ float3 mix_blend(thread const float3& cb, thread const float3& cs, thread const float3 param_20 = cb; float3 param_21 = cs; float param_22 = sat(param_20); - float3 _1192 = set_sat(param_21, param_22); + float3 _1337 = set_sat(param_21, param_22); float3 param_23 = cb; - float3 param_24 = _1192; + float3 param_24 = _1337; float param_25 = lum(param_23); b = set_lum(param_24, param_25); break; @@ -803,9 +873,9 @@ float3 mix_blend(thread const float3& cb, thread const float3& cs, thread const float3 param_26 = cs; float3 param_27 = cb; float param_28 = sat(param_26); - float3 _1206 = set_sat(param_27, param_28); + float3 _1351 = set_sat(param_27, param_28); float3 param_29 = cb; - float3 param_30 = _1206; + float3 param_30 = _1351; float param_31 = lum(param_29); b = set_lum(param_30, param_31); break; @@ -931,30 +1001,30 @@ float4 mix_compose(thread const float3& cb, thread const float3& cs, thread cons } static inline __attribute__((always_inline)) -CmdJump CmdJump_read(thread const Alloc& a, thread const CmdJumpRef& ref, device Memory& v_278) +CmdJump CmdJump_read(thread const Alloc& a, thread const CmdJumpRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdJump s; s.new_ref = raw0; return s; } static inline __attribute__((always_inline)) -CmdJump Cmd_Jump_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdJump Cmd_Jump_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdJumpRef param_1 = CmdJumpRef{ ref.offset + 4u }; - return CmdJump_read(param, param_1, v_278); + return CmdJump_read(param, param_1, v_291); } -kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1521 [[buffer(1)]], texture2d image [[texture(2)]], texture2d image_atlas [[texture(3)]], texture2d gradients [[texture(4)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +kernel void main0(device Memory& v_291 [[buffer(0)]], const device ConfigBuf& _1666 [[buffer(1)]], texture2d image [[texture(2)]], texture2d image_atlas [[texture(3)]], texture2d gradients [[texture(4)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) { - uint tile_ix = (gl_WorkGroupID.y * _1521.conf.width_in_tiles) + gl_WorkGroupID.x; + uint tile_ix = (gl_WorkGroupID.y * _1666.conf.width_in_tiles) + gl_WorkGroupID.x; Alloc param; - param.offset = _1521.conf.ptcl_alloc.offset; + param.offset = _1666.conf.ptcl_alloc.offset; uint param_1 = tile_ix * 1024u; uint param_2 = 1024u; Alloc cmd_alloc = slice_mem(param, param_1, param_2); @@ -967,7 +1037,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 rgba[i] = float4(0.0); } uint clip_depth = 0u; - bool mem_ok = v_278.mem_error == 0u; + bool mem_ok = v_291.mem_error == 0u; spvUnsafeArray df; TileSegRef tile_seg_ref; spvUnsafeArray area; @@ -976,7 +1046,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_3 = cmd_alloc; CmdRef param_4 = cmd_ref; - uint tag = Cmd_tag(param_3, param_4, v_278).tag; + uint tag = Cmd_tag(param_3, param_4, v_291).tag; if (tag == 0u) { break; @@ -987,7 +1057,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_5 = cmd_alloc; CmdRef param_6 = cmd_ref; - CmdStroke stroke = Cmd_Stroke_read(param_5, param_6, v_278); + CmdStroke stroke = Cmd_Stroke_read(param_5, param_6, v_291); for (uint k = 0u; k < 8u; k++) { df[k] = 1000000000.0; @@ -1000,7 +1070,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 bool param_9 = mem_ok; Alloc param_10 = new_alloc(param_7, param_8, param_9); TileSegRef param_11 = tile_seg_ref; - TileSeg seg = TileSeg_read(param_10, param_11, v_278); + TileSeg seg = TileSeg_read(param_10, param_11, v_291); float2 line_vec = seg.vector; for (uint k_1 = 0u; k_1 < 8u; k_1++) { @@ -1023,7 +1093,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_13 = cmd_alloc; CmdRef param_14 = cmd_ref; - CmdFill fill = Cmd_Fill_read(param_13, param_14, v_278); + CmdFill fill = Cmd_Fill_read(param_13, param_14, v_291); for (uint k_3 = 0u; k_3 < 8u; k_3++) { area[k_3] = float(fill.backdrop); @@ -1036,7 +1106,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 bool param_17 = mem_ok; Alloc param_18 = new_alloc(param_15, param_16, param_17); TileSegRef param_19 = tile_seg_ref; - TileSeg seg_1 = TileSeg_read(param_18, param_19, v_278); + TileSeg seg_1 = TileSeg_read(param_18, param_19, v_291); for (uint k_4 = 0u; k_4 < 8u; k_4++) { uint param_20 = k_4; @@ -1080,7 +1150,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_21 = cmd_alloc; CmdRef param_22 = cmd_ref; - CmdAlpha alpha = Cmd_Alpha_read(param_21, param_22, v_278); + CmdAlpha alpha = Cmd_Alpha_read(param_21, param_22, v_291); for (uint k_7 = 0u; k_7 < 8u; k_7++) { area[k_7] = alpha.alpha; @@ -1092,7 +1162,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_23 = cmd_alloc; CmdRef param_24 = cmd_ref; - CmdColor color = Cmd_Color_read(param_23, param_24, v_278); + CmdColor color = Cmd_Color_read(param_23, param_24, v_291); uint param_25 = color.rgba_color; float4 fg = unpacksRGB(param_25); for (uint k_8 = 0u; k_8 < 8u; k_8++) @@ -1107,7 +1177,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_26 = cmd_alloc; CmdRef param_27 = cmd_ref; - CmdLinGrad lin = Cmd_LinGrad_read(param_26, param_27, v_278); + CmdLinGrad lin = Cmd_LinGrad_read(param_26, param_27, v_291); float d_1 = ((lin.line_x * xy.x) + (lin.line_y * xy.y)) + lin.line_c; for (uint k_9 = 0u; k_9 < 8u; k_9++) { @@ -1117,11 +1187,12 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 int x = int(round(fast::clamp(my_d, 0.0, 1.0) * 511.0)); float4 fg_rgba = gradients.read(uint2(int2(x, int(lin.index)))); float3 param_29 = fg_rgba.xyz; - float3 _2092 = fromsRGB(param_29); - fg_rgba.x = _2092.x; - fg_rgba.y = _2092.y; - fg_rgba.z = _2092.z; - rgba[k_9] = fg_rgba; + float3 _2238 = fromsRGB(param_29); + fg_rgba.x = _2238.x; + fg_rgba.y = _2238.y; + fg_rgba.z = _2238.z; + float4 fg_k_1 = fg_rgba * area[k_9]; + rgba[k_9] = (rgba[k_9] * (1.0 - fg_k_1.w)) + fg_k_1; } cmd_ref.offset += 20u; break; @@ -1130,72 +1201,98 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_30 = cmd_alloc; CmdRef param_31 = cmd_ref; - CmdImage fill_img = Cmd_Image_read(param_30, param_31, v_278); - uint2 param_32 = xy_uint; - CmdImage param_33 = fill_img; - spvUnsafeArray img; - img = fillImage(param_32, param_33, image_atlas); + CmdRadGrad rad = Cmd_RadGrad_read(param_30, param_31, v_291); for (uint k_10 = 0u; k_10 < 8u; k_10++) { - float4 fg_k_1 = img[k_10] * area[k_10]; - rgba[k_10] = (rgba[k_10] * (1.0 - fg_k_1.w)) + fg_k_1; + uint param_32 = k_10; + float2 my_xy_1 = xy + float2(chunk_offset(param_32)); + my_xy_1 = ((rad.mat.xz * my_xy_1.x) + (rad.mat.yw * my_xy_1.y)) - rad.xlat; + float ba = dot(my_xy_1, rad.c1); + float ca = rad.ra * dot(my_xy_1, my_xy_1); + float t_2 = (sqrt((ba * ba) + ca) - ba) - rad.roff; + int x_1 = int(round(fast::clamp(t_2, 0.0, 1.0) * 511.0)); + float4 fg_rgba_1 = gradients.read(uint2(int2(x_1, int(rad.index)))); + float3 param_33 = fg_rgba_1.xyz; + float3 _2348 = fromsRGB(param_33); + fg_rgba_1.x = _2348.x; + fg_rgba_1.y = _2348.y; + fg_rgba_1.z = _2348.z; + float4 fg_k_2 = fg_rgba_1 * area[k_10]; + rgba[k_10] = (rgba[k_10] * (1.0 - fg_k_2.w)) + fg_k_2; } - cmd_ref.offset += 12u; + cmd_ref.offset += 48u; break; } case 8u: { + Alloc param_34 = cmd_alloc; + CmdRef param_35 = cmd_ref; + CmdImage fill_img = Cmd_Image_read(param_34, param_35, v_291); + uint2 param_36 = xy_uint; + CmdImage param_37 = fill_img; + spvUnsafeArray img; + img = fillImage(param_36, param_37, image_atlas); for (uint k_11 = 0u; k_11 < 8u; k_11++) + { + float4 fg_k_3 = img[k_11] * area[k_11]; + rgba[k_11] = (rgba[k_11] * (1.0 - fg_k_3.w)) + fg_k_3; + } + cmd_ref.offset += 12u; + break; + } + case 9u: + { + for (uint k_12 = 0u; k_12 < 8u; k_12++) { uint d_2 = min(clip_depth, 127u); - float4 param_34 = float4(rgba[k_11]); - uint _2184 = packsRGB(param_34); - blend_stack[d_2][k_11] = _2184; - rgba[k_11] = float4(0.0); + float4 param_38 = float4(rgba[k_12]); + uint _2454 = packsRGB(param_38); + blend_stack[d_2][k_12] = _2454; + rgba[k_12] = float4(0.0); } clip_depth++; cmd_ref.offset += 4u; break; } - case 9u: + case 10u: { - Alloc param_35 = cmd_alloc; - CmdRef param_36 = cmd_ref; - CmdEndClip end_clip = Cmd_EndClip_read(param_35, param_36, v_278); + Alloc param_39 = cmd_alloc; + CmdRef param_40 = cmd_ref; + CmdEndClip end_clip = Cmd_EndClip_read(param_39, param_40, v_291); uint blend_mode = end_clip.blend >> uint(8); uint comp_mode = end_clip.blend & 255u; clip_depth--; - for (uint k_12 = 0u; k_12 < 8u; k_12++) + for (uint k_13 = 0u; k_13 < 8u; k_13++) { uint d_3 = min(clip_depth, 127u); - uint param_37 = blend_stack[d_3][k_12]; - float4 bg = unpacksRGB(param_37); - float4 fg_1 = rgba[k_12] * area[k_12]; - float3 param_38 = bg.xyz; - float3 param_39 = fg_1.xyz; - uint param_40 = blend_mode; - float3 blend = mix_blend(param_38, param_39, param_40); - float4 _2251 = fg_1; - float _2255 = fg_1.w; - float3 _2262 = mix(_2251.xyz, blend, float3(float((_2255 * bg.w) > 0.0))); - fg_1.x = _2262.x; - fg_1.y = _2262.y; - fg_1.z = _2262.z; - float3 param_41 = bg.xyz; - float3 param_42 = fg_1.xyz; - float param_43 = bg.w; - float param_44 = fg_1.w; - uint param_45 = comp_mode; - rgba[k_12] = mix_compose(param_41, param_42, param_43, param_44, param_45); + uint param_41 = blend_stack[d_3][k_13]; + float4 bg = unpacksRGB(param_41); + float4 fg_1 = rgba[k_13] * area[k_13]; + float3 param_42 = bg.xyz; + float3 param_43 = fg_1.xyz; + uint param_44 = blend_mode; + float3 blend = mix_blend(param_42, param_43, param_44); + float4 _2521 = fg_1; + float _2525 = fg_1.w; + float3 _2532 = mix(_2521.xyz, blend, float3(float((_2525 * bg.w) > 0.0))); + fg_1.x = _2532.x; + fg_1.y = _2532.y; + fg_1.z = _2532.z; + float3 param_45 = bg.xyz; + float3 param_46 = fg_1.xyz; + float param_47 = bg.w; + float param_48 = fg_1.w; + uint param_49 = comp_mode; + rgba[k_13] = mix_compose(param_45, param_46, param_47, param_48, param_49); } cmd_ref.offset += 8u; break; } - case 10u: + case 11u: { - Alloc param_46 = cmd_alloc; - CmdRef param_47 = cmd_ref; - cmd_ref = CmdRef{ Cmd_Jump_read(param_46, param_47, v_278).new_ref }; + Alloc param_50 = cmd_alloc; + CmdRef param_51 = cmd_ref; + cmd_ref = CmdRef{ Cmd_Jump_read(param_50, param_51, v_291).new_ref }; cmd_alloc.offset = cmd_ref.offset; break; } @@ -1203,9 +1300,9 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 } for (uint i_1 = 0u; i_1 < 8u; i_1++) { - uint param_48 = i_1; - float3 param_49 = rgba[i_1].xyz; - image.write(float4(tosRGB(param_49), rgba[i_1].w), uint2(int2(xy_uint + chunk_offset(param_48)))); + uint param_52 = i_1; + float3 param_53 = rgba[i_1].xyz; + image.write(float4(tosRGB(param_53), rgba[i_1].w), uint2(int2(xy_uint + chunk_offset(param_52)))); } } diff --git a/piet-gpu/shader/gen/kernel4.spv b/piet-gpu/shader/gen/kernel4.spv index 91272da225f52879a626483478c6068cdabb6a3d..70612634a5e2d8a7713d8107b6f1da6aafffa287 100644 GIT binary patch literal 65556 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 literal 58596 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 diff --git a/piet-gpu/shader/gen/kernel4_gray.dxil b/piet-gpu/shader/gen/kernel4_gray.dxil index 18c4b7eab4faa745a72e49be704b48803ad300d8..a594d502fa09859dac2cb640e38b2553cc692204 100644 GIT binary patch delta 10729 zcmbVydstJ)_V&(gCxiqOZUzD*5TINo0lBI%2^bL2BBDjHZ6Fm7|iqq!kQjV20G z)Ow5s6%{2BwP>wPNHk!v#unRnsy0w;@p3G1tPR$-zuA!Z{myru=lkRHL9%D(owe4U zdEYf_W^;8_YfW%YsURErzy6^>yEV%gtirOIaQWvbp*I&aOE9}mV=*ywl>Pz_g{k@k z#as)^?LiQ6S-lBcL6=A*(F99JczDBh8e9jZVQ*o}NnGFelQTITGjO*tS=#lNW-pFi z+jIER@8oJ~+C+mOas64+9GPEj|2;O-i^C^i z_L{Ke^0Y{67sXjpATAQ|I}k{IDtA(S+=&d|HcF7yCmBVWlOx zki;i1@SpWPwp^cJ-Z}3VAh?B3LSgT54`E2P$2~?&nYWC z^aD1WZ1CzT`cOu*GU;iQ9NGtB!!ef$8t#^F5Bi~u7JBrvw9lq?re8@;&(d)&urr18 zmw#9-S<1{#%8rj`mI(NKI(ljov&DR|JVQ0LNJUl!wJCxnG>4qrC(k}ae^6&8f5#)I zkya1GD~h)7xpnSV)SkB9J*)C5CE=E}#`yZf1zXl0&i19u=^kG4tjqE2=HLmeFf@7k z$C(${=aVm=NI#!^DVAScN4h=hoaFi0xvSbFtMcu4LFd@D&GlcYHW#dIK780$hw4c8 zA){rDLk&l{WMKc^=kf1ZEYZ2!4b z*uP{1ZdlDz9FXyrH~jrK9~H2W+>jdt>*j~CpeWuAnqpJ?nP+>bLv5KmL~J3XWD;az zY}U%JAEyiLwomDxv5K+#TDp+(?+nq=E#s|A>B8aX%Qu0qm;|LONki3N{`&ATwve)4 zq~0LLuG{k}GhJvH_<0j(WW}AkSns0b$(V${WHb%+#n!I|u1xq*cF|A-;18a#la7WU z7VHHnMf!V!P`Nq2QyHhBMM4CnV!iiz3fyido?%z$AkZOr%6$dRAxJQEsrhI*lU8+E zju(r(FWHH11CIq^4dz6rGG0TY0Rt$$k8F+Hn+3zbI2;BR2jDyF!E* ztU)+M0GbnoKsgQL{RHC;Drh9gY8=n`VU1Z57aRDv zB-H~Shq---kCq7^!Q(zA8hp?w-m#kT9Qd$JaseMtcSuX043DX}h!6U>j|&q%#v4@7 zNT{KyWF}x&4Ec^3`pQWH0i!WXqsV&(yvX?02{$3*ZYCOh&?w%KLi1V=;)f&e_!NYG zt&hA9oCY;nry-s~$368;cp7g|K_j82R8=3d^SIASxo@P66VHG4AQbgs1Ta6*M$7Cg2Z$ zvfrONe9XwCW-k=`KE;Zq^(K=9>;h)5Z;Q1`84NXhNuUw~zL?M8;a*lRz_JTOy}ry2 zY&Y*ZlSEA};&jV;DB)4v*b=sHR)rj)+mW|hYh$qqoI?~SpjYLSj-82uJ4j|Ksh*6E zi0$=z*Ix+V>&SdB0A8)TfC}ctaEj7+xwE}v?wmIVU9BpDf1=6x?KPyvnct6<{?yOL z&L=gH?WkipH{oT_0TdQ4!k61|i*y)jCf&aSE4z`P2x>U*S7gqw!c9l9U;>bW{mAvg ztAZjw@K{ATg(f#?SCspmr#P)u0!UKB#$7DhU5<&U+JSOBsV1&82U_49rw_((7Zuz*}yutedbDAPn^d8x7)9-Dh^l(l73CfjIR zb{Tijw$vFru)VzNrsS`}*eAR>D8{$AgL17`uCy>?VE*;xOK>g6YC7SCy5J3}d2i8{ zsv$-;)-{or_+9ihBN+mBQkuQG7x#jv^+IhGl6)Yn*$HXy5b`RlPKZY#?L`l>>*E3= zWWUS{HZR0)gC^}ki#4fq=vWiWtA4p83mRL&9wE{ zkswY6M8T@EXKrQ%4DY0>Fc6=e@e(r~ZE!rX`;KxL_0B+lnM|F_4rIZ2z>8{m-X`o) z<6VCA^K34dV;&7!j1!moTbE5djgN1p7D{wKW(@T=LSMxh%ba2SHb4Od8+t*!!H!qo zc1IQ46~-@fhkGpISL)U43-SLTQ2zuBL6<{SftvC%%2BE+bUdmhkzDj;+C;oR%^G6q zcc1ksFOT@OUN<>FmQukmzw5TdXN^$@fu)Ypef~icmL_Y;mr~w@7;;Z5PjZLYP5qnj z8icsm)V+Hm!~>K5Ky_gA9$R+taDN;NFc937*}!6rPUi1KC_@cLyH)a7y;CieN5}vT zs}<#Cq0cIE*M9&=T#IO8^7L<5Koe|)_*aLhAxJP4fZT)IwL{&2-2Ol-{G@HzTriF% zL=an0(Xq)A9@Ylr<=o94+XtqKhiK@1_3S1P9X}cBz}k7&?SThgmY5aF~UN>DC^P znAV}AuTs@Lh)Mr9CXZ-(R%4g0(Hg&}ZP{)7n6_mH5Xl`s-gVL2MckqQXU~G7w-#HD zig26jQ-c_(tc624ty(v)J~%!X`-gi5#S4dXOr9^QqTa53P1{RB|MJ6=>ae!@ok5Ak zrsE}bBDX@SIR{KR2}M*7wK-ExR8ZV})D@h@#wJ%(BZ)KjvOZrI;r^nj;4~$!g3n~o z;jt!))rPJo1eeHMkKRGE-K=kZ-x@{n^Cpf-YUKMi-@&ZJy1*a{*w78U3k}2uy@!Zx z7Z6)9BDSq4AU1CzAwmdo^3sb0#h_lY6cd=8xU!z7=*K>b5FdlbU%E_ z&+jVJgJfyUo8dGl0uL09lpp3#lbL>Q-rh+b_3iF4pg!_3L%K|w*lI%3Lt|smLH9WL zG@bfbKNpZ?9Rbw!X4*v2VlZiXuC0)aA1Pp4+W=tF_Pf3cSU>ts>kf6&dj*vtG8B5f2@MABxWSj?e zUV^0#xq62PS~2q#uF^|))@QXKWoinbZc8#q<<6E$NhMH0{!+kp>&{!&UcmOIn}F3a zUH##M2LY`!4giI)-^%by0Sb}F0VOErt`U9!9*(&HVf>LH($|O{Pb1=)b}#4%5RY;} zkUV1FA;lteOa9;odUX9ADh3ONd8q5e(&DI%2k+EEjwhzK%Kg$No2G$SEWqJ#y)pc- zw=)#N@d` z(ik*{ASbR77F5&QM9VIy{~O)*6#{q$->abnCsjm9zI4T3T^1V zeYf2T;5N)O<1l+@Q<0+XMfQvAtQdc3OsxR?fV9Xy#Kbmxsn~~jBnvg?73s%n>@lt- zJ%rzOd-z^CchvZkbWn_c)ot^Cq0q)Y#xUmh;kjw8TB)RzG3GFFM}s(Q8N!RGy(_BR zvYfS7;<;S9cU6_4f7_rK^H;a6?)96XHne10-0^L^@aA%pHQTbz!d&nzSyeJ8om71B zE^7UgA#x164!2!e7&9oo&SbErk0sQ&j?6f%I_~qOC3iij(`6f84f!-?z23{{M^SGu zyJ$Hy6+zteI&#FQ*J%Tk(l*+l7#Gf9MYw2GvrSFl3Uh{m>>?_lIx?Y(CNIYfP+^fn zUads2=T}A1Na@()SWuaUPHuQ`l?tlbn)Fs|fOFqMUA}t-3Qbh_{*C=~Dp-MLxnxw& zN?OqTVkSR!4_Jair66^U*PD;`$L=hW=Oatd%`mgO-u$8=Kgz%D8?XeGq`a}2Ve}>9 z&LK8&zj;ii&uzaT;3ps^=WOtpJXoJ_xrkSQm|U1>R?FNQuaW|e|NU{B!Z$Xj_4|@> zR}h=m|Mb}WeeUP$viV7f&8;UrHhb!m9v4A#fz9eqj338SW@(6(?qsDR_s3fKCynH8CO4KwM zT&2}kn-azK6lr(!a4l2ai(=`)5>b9&+kTm+k5~LQ;uKnyRgb7G!g%H6{KcoBfQ2iL zgCC$J(NsP`=m(ciRnY2Kg!RGK`U#&N0U1eIeX_rUb!1SyBdf=@w`RXK_9>Pz-{HkQ zalub2sd{V7;hZ~a>_0M?;$9x#?eBAMpbOr8)W?=h+}{1}<35p!+heQSx?f9v9eLEf zR=zlWvd(?^^5&aM*OQZVZ}QM8TFZ(vmW2KQ29kn^V@*Zfwq1O#aO3ErC(OCqqKE~d z*2nF_@>8k|MPO_~4dtXg4i}-}*K*Wsm@2zr*qlEg=7%%lv16s2ZW~d6Fq{*i)KaNZ zjoJy;uN*6~skXBru#~fqZC^hYBkF+4kR(>*4U(A2=^dy2h6vWYZ<;?>5?|)$PNbra z#56Kq(dAs5;sV^3M?W;C}ZQf^mTAz|uA`_LpW0ou@f3ZEl`Q2Uq+eIkU zcDr8*&!pyKK#iXB_|ZFwQDxc8vhz`8=TiX{o4o|=Lu^8`mz5pD@4Y)r&ZVqNm4f{9 zF8OF^brfUTKt6YU_W^0)zIazt@;k$v?dP0g%)4M@uYi$VALSX@mrp-KMplMYBrfl( zo>6`ke-)++3b<+NMyZ6BIObj~d@BZ1-Ioy+k35&j6+fpIf?SjOfSq?2Nj?Rk+n#*c zD>`L)Tj2--spb?V8>E_`cUnw3M1U>LU62u^YaR%10WQ5-lEe(8uXi47Q z-+{R{D9)Ak*nGzJdsZR6u=1um;U8Q0=JM0Z%3b8&q8RjNV7Bgm?9nmj;#9gj4P>UK zdM^1FQ|S_W(wM{W{-{y$C0J8rW?S7~O0A6i7w+hPXvw|wFDkewaTf%O=BIiJg2iR2 z74Ex>#i;bxxtPUr$8axon&-0r;+xCIsnc0|X+NvW_sWpp zD|8pn=p7M)f8`S{5sN?fRi2$@N!OVPw=cFN2 z3bi^QN0^RI0@e6?7}cV>6t+rV`qh2?lBeH`8lh9)?@DcjzFX%mBWgas805X?Lr_M< z(b5x8-^ngiJ-sbzWRgKK^U%lcOjp&rtKw4+OXjdI676$VAaU&ODdU;hai-M7V-F1I zcfXC;NRD@=CjRvOFsb<*`w5|WFZ-r?CKwGp7!AQ3u26kQTE{}aL%-Iq?!OBr_fz@B z{*;!!LGi9rJ+`L#_1eUf(G2goGBWq)mP68nyi`}|g%zVl(Or<~$Ilmc-+El;-cx*a z>tA%w_TuZ2A4HgTAm{h$wd8Bj9yxD({-a02n|%g7m(nSN|CuOHsg6Dlp(oS~cN|#> z)V1w~Z0#zbsoR^MlaXvVQ2>a~WFOrb8u}jJo)%)Id;?Ku}}7zV}t3{?cc9rx=f8%%1|v)*f3! z0k@Jg!V#-w+MsM{rPp(F)|z^4&7)|>)80AUv?01w;z)NnOsj{3_R=u>PhZZ@^#qbF@A`9_l4Sa{1^O3?(JNz>)<~a zR&#=RhWp#_ZO11Q*LgO3u7mHz*ggBD!3~>_@$enk-$9Zl#!i-Ykk&wWb2T=sZOQPh zCMAeg>@?q;n%W5`iZGX@83nBcWumlr1yPGD7J2G zGlj7m=`EW$!09{s{4wZWJ;_47Xjp4b|H^>hL?Pw2bbXEcF~Jfx#23xj;5}j#1q`K3 z6c9W9`t#v!9K4(+xPswc@n6n%n! zB2mnc3q&i^eMXGVKu;8SBK)uK*DEf+OF6noxrP0neA*Z-$$CVpnLId`o7NO2m1N9t zIpmuGizs*pU=iUX^%sCe6#P9mizsx>`OVI0W2G}*9Few#y`EdY`K=F>LKg_~EgpTNzMXw%!=G{6407>B(^fCvZh4El{p+%z3SD(UtbbLjj4 zwcY|-sxh+-excHm9|OQP0XOY+tyJRjdb6bNGEagl+_K&s1B++UQsYo^tfGXp0S zg3woD0vS7+L1&MVa$Q(&_w2e(sWvlYF;{R=?4ngDCd0l%rV-c zp`+5mzX9FvW%#up%-eaZJ-8S_XIxS8uZF7RAuG_|I zR%;b7)sKPM6V8qPO$0IOrEIx&_Dq_o{+pN*+3%vy`-4Y}532XL9IHBoo_zn9&vLgg(W=d)`^_`l+cGd532nq5AA??)VNfUBFNcJ9E;b6{yIMNq{1d_#gO#BGery!;aOmCp- zoegRbQZESUQbd9;qhKEhsX-wn3Ezw4-iT00C4`}r0+F|cns`0}m@fk69h&Yu008Dq z@Tie~YM;xY`9Hxo1hvI}f?i2%H62pM8BQ38)plZAtN~3k$-v!q$fpB*ks!}bG@Rgh zW0uyfB%Xx4Tb%%`w#2Ek<2z57i05KWN`Q49>PqN5!Sv2a>>S6!65>goPb+j7V7uZ> zC+tK6Fq~;ZONi~fTj2M3=&&Hs1h6dF8#!41b1AbCbx^ajN%sMJze1J=yYDM1*^MX; zAu4G6z84?aA&HeJ2Au>BJra;Jv;~Jyi(wU|^#k30R6gi7eok$uqI1uCumU* zxX%ZFPBGomm!RoN7b(|%vn>W>!Hj1Du%^U!AOyH(0tCRGb?gEJc+r)Gt&IQ7ml2O1 zbDSN_mlh)PZt$^CKi?mJGhOx-#pg?qOP>+j0WmQdZP}vFzlk3)M(MO;H`}eYT7+6p z*&(PYUq%4S0|p<(PVY{uP0!<|O&RcI_#uRCvsBk!*6jv_ZNg+C8mgFrFv{mCg?8nW z0%I{B~OK;4&VmYZZ*#76~W}Z<4D?WU|2+?gBS)|5fEYYGz`cL9Z*FNWbwHF%`j| zRIv$ej%t?6KjnkthVl0`{1fJ#FplHiv>$|Hf5Z5++kA3bA_T2PQ-4lVp;tbP7GM&grq>^#S{9js8Dr2{ID_h zjyy+2CA)7E@5&2RpO_CG=G@WeV0_7igaNVnYm=?^2&kK;VZb{W9N0W|7N+KXE21a^ z$R$(r?syL87m^33(rVo-UXt4Mt)AfK`^LMxxhf^l!uZJ-W}zyRyjPS_(Ry1Bl;aE! zLl?<0_Zi5Njw47lhUwJ!$Kb=%UgQpwJ z)Bfu-H}9=rKy}sh``IFE0E1<4(e_=pEu3lxrod4cRyIZh+x@9YTp{)aN1)eIFhm8u7yCfV>LT6VxPn(C-bB9dJhfgRDdvcI*`DD z%b-VqLf5>kQ6C1!kyK$0(SLo`$ykw@EOLOM^1g zC|{)NXQU1vSmK<#MCZLhzGuA?x5>U^Bm0?x^hELdH}d<>adYdebn(V?&9_$JgHHU8GhrI|7BW+=- zHaowVYEPYJznEpeI8EO%Q=bBUifA!DBu)fwMlf!^FK&d0+q@RDbtOh8#@s2x{Bu6$ z{T|#lGwyP=4DYDIzo)}*RpTvT-TJVNN#w%C2F(7zC%o^T%Uib3WG^>Q>LdC0Ze5kf zwNO!~;I0jdd6o9JrM>#2t|tD>5u*YWE!)S>98`Y+mH!EJ@d|Ua{Mxa-g-SC%mJzX4 z|58D6^*_ej7^h_$F}f<1PM%Xi5)Sy}Rg}iMGxIY2_ z_UbcJLPUSu6{)qjTxr4*OawQOIb!^XyvtRp^Stid`z6_$M*CjxS(DbOjO6ZArdy+g z2XcvO4H?eQ8f&N9KjgKW zJsf`q*U79=qP0?>J?M{WR>0fr3rS{Sd%^%q!TSTA4&wLt5~iu5}fiW zv{WxJigPRU7G9x%oR1F3`+r=b$@xnEJn;7jw5ajp?|H$<$u}XY961XwBEqJJJsU8{ z3HT+t+?nBX$_4gmoNBjCj>09Hv5aMKjdy#TnHg@E&; zkf1veFy&(eJUR}39{~>+AmD8X7)nOKf@lQXgMecWBH$Q5gAABnil7aJ2)c0`or0h% z#?iMBbn7@e!wdkC0!7%{y90hg~rz>YbH<*NwTl#YP^ x8V3)J$1&{)f^J37{oBUT#fa%)B0#fOAn46gHmVY{AK+GlsK1nxk;!xa`yZ#24k!Qs delta 9756 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 b^$N^1)$@tCLkXywN6(y=MF4Z<`uG0;Guw+I diff --git a/piet-gpu/shader/gen/kernel4_gray.hlsl b/piet-gpu/shader/gen/kernel4_gray.hlsl index de95771..019a73c 100644 --- a/piet-gpu/shader/gen/kernel4_gray.hlsl +++ b/piet-gpu/shader/gen/kernel4_gray.hlsl @@ -48,6 +48,21 @@ struct CmdLinGrad float line_c; }; +struct CmdRadGradRef +{ + uint offset; +}; + +struct CmdRadGrad +{ + uint index; + float4 mat; + float2 xlat; + float2 c1; + float ra; + float roff; +}; + struct CmdImageRef { uint offset; @@ -146,8 +161,8 @@ struct Config static const uint3 gl_WorkGroupSize = uint3(8u, 4u, 1u); -RWByteAddressBuffer _278 : register(u0, space0); -ByteAddressBuffer _1521 : register(t1, space0); +RWByteAddressBuffer _291 : register(u0, space0); +ByteAddressBuffer _1666 : register(t1, space0); RWTexture2D image_atlas : register(u3, space0); RWTexture2D gradients : register(u4, space0); RWTexture2D image : register(u2, space0); @@ -174,8 +189,8 @@ float4 spvUnpackUnorm4x8(uint value) Alloc slice_mem(Alloc a, uint offset, uint size) { - Alloc _291 = { a.offset + offset }; - return _291; + Alloc _304 = { a.offset + offset }; + return _304; } bool touch_mem(Alloc alloc, uint offset) @@ -191,7 +206,7 @@ uint read_mem(Alloc alloc, uint offset) { return 0u; } - uint v = _278.Load(offset * 4 + 8); + uint v = _291.Load(offset * 4 + 8); return v; } @@ -200,8 +215,8 @@ CmdTag Cmd_tag(Alloc a, CmdRef ref) Alloc param = a; uint param_1 = ref.offset >> uint(2); uint tag_and_flags = read_mem(param, param_1); - CmdTag _525 = { tag_and_flags & 65535u, tag_and_flags >> uint(16) }; - return _525; + CmdTag _663 = { tag_and_flags & 65535u, tag_and_flags >> uint(16) }; + return _663; } CmdStroke CmdStroke_read(Alloc a, CmdStrokeRef ref) @@ -221,9 +236,9 @@ CmdStroke CmdStroke_read(Alloc a, CmdStrokeRef ref) CmdStroke Cmd_Stroke_read(Alloc a, CmdRef ref) { - CmdStrokeRef _542 = { ref.offset + 4u }; + CmdStrokeRef _679 = { ref.offset + 4u }; Alloc param = a; - CmdStrokeRef param_1 = _542; + CmdStrokeRef param_1 = _679; return CmdStroke_read(param, param_1); } @@ -259,8 +274,8 @@ TileSeg TileSeg_read(Alloc a, TileSegRef ref) s.origin = float2(asfloat(raw0), asfloat(raw1)); s._vector = float2(asfloat(raw2), asfloat(raw3)); s.y_edge = asfloat(raw4); - TileSegRef _675 = { raw5 }; - s.next = _675; + TileSegRef _820 = { raw5 }; + s.next = _820; return s; } @@ -286,9 +301,9 @@ CmdFill CmdFill_read(Alloc a, CmdFillRef ref) CmdFill Cmd_Fill_read(Alloc a, CmdRef ref) { - CmdFillRef _532 = { ref.offset + 4u }; + CmdFillRef _669 = { ref.offset + 4u }; Alloc param = a; - CmdFillRef param_1 = _532; + CmdFillRef param_1 = _669; return CmdFill_read(param, param_1); } @@ -305,9 +320,9 @@ CmdAlpha CmdAlpha_read(Alloc a, CmdAlphaRef ref) CmdAlpha Cmd_Alpha_read(Alloc a, CmdRef ref) { - CmdAlphaRef _552 = { ref.offset + 4u }; + CmdAlphaRef _689 = { ref.offset + 4u }; Alloc param = a; - CmdAlphaRef param_1 = _552; + CmdAlphaRef param_1 = _689; return CmdAlpha_read(param, param_1); } @@ -324,9 +339,9 @@ CmdColor CmdColor_read(Alloc a, CmdColorRef ref) CmdColor Cmd_Color_read(Alloc a, CmdRef ref) { - CmdColorRef _562 = { ref.offset + 4u }; + CmdColorRef _699 = { ref.offset + 4u }; Alloc param = a; - CmdColorRef param_1 = _562; + CmdColorRef param_1 = _699; return CmdColor_read(param, param_1); } @@ -370,12 +385,66 @@ CmdLinGrad CmdLinGrad_read(Alloc a, CmdLinGradRef ref) CmdLinGrad Cmd_LinGrad_read(Alloc a, CmdRef ref) { - CmdLinGradRef _572 = { ref.offset + 4u }; + CmdLinGradRef _709 = { ref.offset + 4u }; Alloc param = a; - CmdLinGradRef param_1 = _572; + CmdLinGradRef param_1 = _709; return CmdLinGrad_read(param, param_1); } +CmdRadGrad CmdRadGrad_read(Alloc a, CmdRadGradRef ref) +{ + uint ix = ref.offset >> uint(2); + Alloc param = a; + uint param_1 = ix + 0u; + uint raw0 = read_mem(param, param_1); + Alloc param_2 = a; + uint param_3 = ix + 1u; + uint raw1 = read_mem(param_2, param_3); + Alloc param_4 = a; + uint param_5 = ix + 2u; + uint raw2 = read_mem(param_4, param_5); + Alloc param_6 = a; + uint param_7 = ix + 3u; + uint raw3 = read_mem(param_6, param_7); + Alloc param_8 = a; + uint param_9 = ix + 4u; + uint raw4 = read_mem(param_8, param_9); + Alloc param_10 = a; + uint param_11 = ix + 5u; + uint raw5 = read_mem(param_10, param_11); + Alloc param_12 = a; + uint param_13 = ix + 6u; + uint raw6 = read_mem(param_12, param_13); + Alloc param_14 = a; + uint param_15 = ix + 7u; + uint raw7 = read_mem(param_14, param_15); + Alloc param_16 = a; + uint param_17 = ix + 8u; + uint raw8 = read_mem(param_16, param_17); + Alloc param_18 = a; + uint param_19 = ix + 9u; + uint raw9 = read_mem(param_18, param_19); + Alloc param_20 = a; + uint param_21 = ix + 10u; + uint raw10 = read_mem(param_20, param_21); + CmdRadGrad s; + s.index = raw0; + s.mat = float4(asfloat(raw1), asfloat(raw2), asfloat(raw3), asfloat(raw4)); + s.xlat = float2(asfloat(raw5), asfloat(raw6)); + s.c1 = float2(asfloat(raw7), asfloat(raw8)); + s.ra = asfloat(raw9); + s.roff = asfloat(raw10); + return s; +} + +CmdRadGrad Cmd_RadGrad_read(Alloc a, CmdRef ref) +{ + CmdRadGradRef _719 = { ref.offset + 4u }; + Alloc param = a; + CmdRadGradRef param_1 = _719; + return CmdRadGrad_read(param, param_1); +} + CmdImage CmdImage_read(Alloc a, CmdImageRef ref) { uint ix = ref.offset >> uint(2); @@ -393,9 +462,9 @@ CmdImage CmdImage_read(Alloc a, CmdImageRef ref) CmdImage Cmd_Image_read(Alloc a, CmdRef ref) { - CmdImageRef _582 = { ref.offset + 4u }; + CmdImageRef _729 = { ref.offset + 4u }; Alloc param = a; - CmdImageRef param_1 = _582; + CmdImageRef param_1 = _729; return CmdImage_read(param, param_1); } @@ -408,10 +477,10 @@ void fillImage(out float4 spvReturnValue[8], uint2 xy, CmdImage cmd_img) int2 uv = int2(xy + chunk_offset(param)) + cmd_img.offset; float4 fg_rgba = image_atlas[uv]; float3 param_1 = fg_rgba.xyz; - float3 _1493 = fromsRGB(param_1); - fg_rgba.x = _1493.x; - fg_rgba.y = _1493.y; - fg_rgba.z = _1493.z; + float3 _1638 = fromsRGB(param_1); + fg_rgba.x = _1638.x; + fg_rgba.y = _1638.y; + fg_rgba.z = _1638.z; rgba[i] = fg_rgba; } spvReturnValue = rgba; @@ -445,9 +514,9 @@ CmdEndClip CmdEndClip_read(Alloc a, CmdEndClipRef ref) CmdEndClip Cmd_EndClip_read(Alloc a, CmdRef ref) { - CmdEndClipRef _592 = { ref.offset + 4u }; + CmdEndClipRef _739 = { ref.offset + 4u }; Alloc param = a; - CmdEndClipRef param_1 = _592; + CmdEndClipRef param_1 = _739; return CmdEndClip_read(param, param_1); } @@ -637,8 +706,8 @@ float3 set_lum(float3 c, float l) { float3 param = c; float3 param_1 = c + (l - lum(param)).xxx; - float3 _901 = clip_color(param_1); - return _901; + float3 _1046 = clip_color(param_1); + return _1046; } float3 mix_blend(float3 cb, float3 cs, uint mode) @@ -726,9 +795,9 @@ float3 mix_blend(float3 cb, float3 cs, uint mode) float3 param_20 = cb; float3 param_21 = cs; float param_22 = sat(param_20); - float3 _1192 = set_sat(param_21, param_22); + float3 _1337 = set_sat(param_21, param_22); float3 param_23 = cb; - float3 param_24 = _1192; + float3 param_24 = _1337; float param_25 = lum(param_23); b = set_lum(param_24, param_25); break; @@ -738,9 +807,9 @@ float3 mix_blend(float3 cb, float3 cs, uint mode) float3 param_26 = cs; float3 param_27 = cb; float param_28 = sat(param_26); - float3 _1206 = set_sat(param_27, param_28); + float3 _1351 = set_sat(param_27, param_28); float3 param_29 = cb; - float3 param_30 = _1206; + float3 param_30 = _1351; float param_31 = lum(param_29); b = set_lum(param_30, param_31); break; @@ -877,24 +946,24 @@ CmdJump CmdJump_read(Alloc a, CmdJumpRef ref) CmdJump Cmd_Jump_read(Alloc a, CmdRef ref) { - CmdJumpRef _602 = { ref.offset + 4u }; + CmdJumpRef _749 = { ref.offset + 4u }; Alloc param = a; - CmdJumpRef param_1 = _602; + CmdJumpRef param_1 = _749; return CmdJump_read(param, param_1); } void comp_main() { - uint tile_ix = (gl_WorkGroupID.y * _1521.Load(8)) + gl_WorkGroupID.x; - Alloc _1536; - _1536.offset = _1521.Load(24); + uint tile_ix = (gl_WorkGroupID.y * _1666.Load(8)) + gl_WorkGroupID.x; + Alloc _1681; + _1681.offset = _1666.Load(24); Alloc param; - param.offset = _1536.offset; + param.offset = _1681.offset; uint param_1 = tile_ix * 1024u; uint param_2 = 1024u; Alloc cmd_alloc = slice_mem(param, param_1, param_2); - CmdRef _1545 = { cmd_alloc.offset }; - CmdRef cmd_ref = _1545; + CmdRef _1690 = { cmd_alloc.offset }; + CmdRef cmd_ref = _1690; uint2 xy_uint = uint2(gl_LocalInvocationID.x + (16u * gl_WorkGroupID.x), gl_LocalInvocationID.y + (16u * gl_WorkGroupID.y)); float2 xy = float2(xy_uint); float4 rgba[8]; @@ -903,7 +972,7 @@ void comp_main() rgba[i] = 0.0f.xxxx; } uint clip_depth = 0u; - bool mem_ok = _278.Load(4) == 0u; + bool mem_ok = _291.Load(4) == 0u; float df[8]; TileSegRef tile_seg_ref; float area[8]; @@ -928,8 +997,8 @@ void comp_main() { df[k] = 1000000000.0f; } - TileSegRef _1638 = { stroke.tile_ref }; - tile_seg_ref = _1638; + TileSegRef _1784 = { stroke.tile_ref }; + tile_seg_ref = _1784; do { uint param_7 = tile_seg_ref.offset; @@ -965,8 +1034,8 @@ void comp_main() { area[k_3] = float(fill.backdrop); } - TileSegRef _1758 = { fill.tile_ref }; - tile_seg_ref = _1758; + TileSegRef _1904 = { fill.tile_ref }; + tile_seg_ref = _1904; do { uint param_15 = tile_seg_ref.offset; @@ -1055,11 +1124,12 @@ void comp_main() int x = int(round(clamp(my_d, 0.0f, 1.0f) * 511.0f)); float4 fg_rgba = gradients[int2(x, int(lin.index))]; float3 param_29 = fg_rgba.xyz; - float3 _2092 = fromsRGB(param_29); - fg_rgba.x = _2092.x; - fg_rgba.y = _2092.y; - fg_rgba.z = _2092.z; - rgba[k_9] = fg_rgba; + float3 _2238 = fromsRGB(param_29); + fg_rgba.x = _2238.x; + fg_rgba.y = _2238.y; + fg_rgba.z = _2238.z; + float4 fg_k_1 = fg_rgba * area[k_9]; + rgba[k_9] = (rgba[k_9] * (1.0f - fg_k_1.w)) + fg_k_1; } cmd_ref.offset += 20u; break; @@ -1068,74 +1138,100 @@ void comp_main() { Alloc param_30 = cmd_alloc; CmdRef param_31 = cmd_ref; - CmdImage fill_img = Cmd_Image_read(param_30, param_31); - uint2 param_32 = xy_uint; - CmdImage param_33 = fill_img; - float4 _2121[8]; - fillImage(_2121, param_32, param_33); - float4 img[8] = _2121; + CmdRadGrad rad = Cmd_RadGrad_read(param_30, param_31); for (uint k_10 = 0u; k_10 < 8u; k_10++) { - float4 fg_k_1 = img[k_10] * area[k_10]; - rgba[k_10] = (rgba[k_10] * (1.0f - fg_k_1.w)) + fg_k_1; + uint param_32 = k_10; + float2 my_xy_1 = xy + float2(chunk_offset(param_32)); + my_xy_1 = ((rad.mat.xz * my_xy_1.x) + (rad.mat.yw * my_xy_1.y)) - rad.xlat; + float ba = dot(my_xy_1, rad.c1); + float ca = rad.ra * dot(my_xy_1, my_xy_1); + float t_2 = (sqrt((ba * ba) + ca) - ba) - rad.roff; + int x_1 = int(round(clamp(t_2, 0.0f, 1.0f) * 511.0f)); + float4 fg_rgba_1 = gradients[int2(x_1, int(rad.index))]; + float3 param_33 = fg_rgba_1.xyz; + float3 _2348 = fromsRGB(param_33); + fg_rgba_1.x = _2348.x; + fg_rgba_1.y = _2348.y; + fg_rgba_1.z = _2348.z; + float4 fg_k_2 = fg_rgba_1 * area[k_10]; + rgba[k_10] = (rgba[k_10] * (1.0f - fg_k_2.w)) + fg_k_2; } - cmd_ref.offset += 12u; + cmd_ref.offset += 48u; break; } case 8u: { + Alloc param_34 = cmd_alloc; + CmdRef param_35 = cmd_ref; + CmdImage fill_img = Cmd_Image_read(param_34, param_35); + uint2 param_36 = xy_uint; + CmdImage param_37 = fill_img; + float4 _2391[8]; + fillImage(_2391, param_36, param_37); + float4 img[8] = _2391; for (uint k_11 = 0u; k_11 < 8u; k_11++) + { + float4 fg_k_3 = img[k_11] * area[k_11]; + rgba[k_11] = (rgba[k_11] * (1.0f - fg_k_3.w)) + fg_k_3; + } + cmd_ref.offset += 12u; + break; + } + case 9u: + { + for (uint k_12 = 0u; k_12 < 8u; k_12++) { uint d_2 = min(clip_depth, 127u); - float4 param_34 = float4(rgba[k_11]); - uint _2184 = packsRGB(param_34); - blend_stack[d_2][k_11] = _2184; - rgba[k_11] = 0.0f.xxxx; + float4 param_38 = float4(rgba[k_12]); + uint _2454 = packsRGB(param_38); + blend_stack[d_2][k_12] = _2454; + rgba[k_12] = 0.0f.xxxx; } clip_depth++; cmd_ref.offset += 4u; break; } - case 9u: + case 10u: { - Alloc param_35 = cmd_alloc; - CmdRef param_36 = cmd_ref; - CmdEndClip end_clip = Cmd_EndClip_read(param_35, param_36); + Alloc param_39 = cmd_alloc; + CmdRef param_40 = cmd_ref; + CmdEndClip end_clip = Cmd_EndClip_read(param_39, param_40); uint blend_mode = end_clip.blend >> uint(8); uint comp_mode = end_clip.blend & 255u; clip_depth--; - for (uint k_12 = 0u; k_12 < 8u; k_12++) + for (uint k_13 = 0u; k_13 < 8u; k_13++) { uint d_3 = min(clip_depth, 127u); - uint param_37 = blend_stack[d_3][k_12]; - float4 bg = unpacksRGB(param_37); - float4 fg_1 = rgba[k_12] * area[k_12]; - float3 param_38 = bg.xyz; - float3 param_39 = fg_1.xyz; - uint param_40 = blend_mode; - float3 blend = mix_blend(param_38, param_39, param_40); - float4 _2251 = fg_1; - float _2255 = fg_1.w; - float3 _2262 = lerp(_2251.xyz, blend, float((_2255 * bg.w) > 0.0f).xxx); - fg_1.x = _2262.x; - fg_1.y = _2262.y; - fg_1.z = _2262.z; - float3 param_41 = bg.xyz; - float3 param_42 = fg_1.xyz; - float param_43 = bg.w; - float param_44 = fg_1.w; - uint param_45 = comp_mode; - rgba[k_12] = mix_compose(param_41, param_42, param_43, param_44, param_45); + uint param_41 = blend_stack[d_3][k_13]; + float4 bg = unpacksRGB(param_41); + float4 fg_1 = rgba[k_13] * area[k_13]; + float3 param_42 = bg.xyz; + float3 param_43 = fg_1.xyz; + uint param_44 = blend_mode; + float3 blend = mix_blend(param_42, param_43, param_44); + float4 _2521 = fg_1; + float _2525 = fg_1.w; + float3 _2532 = lerp(_2521.xyz, blend, float((_2525 * bg.w) > 0.0f).xxx); + fg_1.x = _2532.x; + fg_1.y = _2532.y; + fg_1.z = _2532.z; + float3 param_45 = bg.xyz; + float3 param_46 = fg_1.xyz; + float param_47 = bg.w; + float param_48 = fg_1.w; + uint param_49 = comp_mode; + rgba[k_13] = mix_compose(param_45, param_46, param_47, param_48, param_49); } cmd_ref.offset += 8u; break; } - case 10u: + case 11u: { - Alloc param_46 = cmd_alloc; - CmdRef param_47 = cmd_ref; - CmdRef _2299 = { Cmd_Jump_read(param_46, param_47).new_ref }; - cmd_ref = _2299; + Alloc param_50 = cmd_alloc; + CmdRef param_51 = cmd_ref; + CmdRef _2569 = { Cmd_Jump_read(param_50, param_51).new_ref }; + cmd_ref = _2569; cmd_alloc.offset = cmd_ref.offset; break; } @@ -1143,8 +1239,8 @@ void comp_main() } for (uint i_1 = 0u; i_1 < 8u; i_1++) { - uint param_48 = i_1; - image[int2(xy_uint + chunk_offset(param_48))] = rgba[i_1].w.x; + uint param_52 = i_1; + image[int2(xy_uint + chunk_offset(param_52))] = rgba[i_1].w.x; } } diff --git a/piet-gpu/shader/gen/kernel4_gray.msl b/piet-gpu/shader/gen/kernel4_gray.msl index 5128e99..6402c6f 100644 --- a/piet-gpu/shader/gen/kernel4_gray.msl +++ b/piet-gpu/shader/gen/kernel4_gray.msl @@ -94,6 +94,21 @@ struct CmdLinGrad float line_c; }; +struct CmdRadGradRef +{ + uint offset; +}; + +struct CmdRadGrad +{ + uint index; + float4 mat; + float2 xlat; + float2 c1; + float ra; + float roff; +}; + struct CmdImageRef { uint offset; @@ -222,7 +237,7 @@ bool touch_mem(thread const Alloc& alloc, thread const uint& offset) } static inline __attribute__((always_inline)) -uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memory& v_278) +uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memory& v_291) { Alloc param = alloc; uint param_1 = offset; @@ -230,29 +245,29 @@ uint read_mem(thread const Alloc& alloc, thread const uint& offset, device Memor { return 0u; } - uint v = v_278.memory[offset]; + uint v = v_291.memory[offset]; return v; } static inline __attribute__((always_inline)) -CmdTag Cmd_tag(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdTag Cmd_tag(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; uint param_1 = ref.offset >> uint(2); - uint tag_and_flags = read_mem(param, param_1, v_278); + uint tag_and_flags = read_mem(param, param_1, v_291); return CmdTag{ tag_and_flags & 65535u, tag_and_flags >> uint(16) }; } static inline __attribute__((always_inline)) -CmdStroke CmdStroke_read(thread const Alloc& a, thread const CmdStrokeRef& ref, device Memory& v_278) +CmdStroke CmdStroke_read(thread const Alloc& a, thread const CmdStrokeRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); CmdStroke s; s.tile_ref = raw0; s.half_width = as_type(raw1); @@ -260,11 +275,11 @@ CmdStroke CmdStroke_read(thread const Alloc& a, thread const CmdStrokeRef& ref, } static inline __attribute__((always_inline)) -CmdStroke Cmd_Stroke_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdStroke Cmd_Stroke_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdStrokeRef param_1 = CmdStrokeRef{ ref.offset + 4u }; - return CmdStroke_read(param, param_1, v_278); + return CmdStroke_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -276,27 +291,27 @@ Alloc new_alloc(thread const uint& offset, thread const uint& size, thread const } static inline __attribute__((always_inline)) -TileSeg TileSeg_read(thread const Alloc& a, thread const TileSegRef& ref, device Memory& v_278) +TileSeg TileSeg_read(thread const Alloc& a, thread const TileSegRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); Alloc param_4 = a; uint param_5 = ix + 2u; - uint raw2 = read_mem(param_4, param_5, v_278); + uint raw2 = read_mem(param_4, param_5, v_291); Alloc param_6 = a; uint param_7 = ix + 3u; - uint raw3 = read_mem(param_6, param_7, v_278); + uint raw3 = read_mem(param_6, param_7, v_291); Alloc param_8 = a; uint param_9 = ix + 4u; - uint raw4 = read_mem(param_8, param_9, v_278); + uint raw4 = read_mem(param_8, param_9, v_291); Alloc param_10 = a; uint param_11 = ix + 5u; - uint raw5 = read_mem(param_10, param_11, v_278); + uint raw5 = read_mem(param_10, param_11, v_291); TileSeg s; s.origin = float2(as_type(raw0), as_type(raw1)); s.vector = float2(as_type(raw2), as_type(raw3)); @@ -312,15 +327,15 @@ uint2 chunk_offset(thread const uint& i) } static inline __attribute__((always_inline)) -CmdFill CmdFill_read(thread const Alloc& a, thread const CmdFillRef& ref, device Memory& v_278) +CmdFill CmdFill_read(thread const Alloc& a, thread const CmdFillRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); CmdFill s; s.tile_ref = raw0; s.backdrop = int(raw1); @@ -328,51 +343,51 @@ CmdFill CmdFill_read(thread const Alloc& a, thread const CmdFillRef& ref, device } static inline __attribute__((always_inline)) -CmdFill Cmd_Fill_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdFill Cmd_Fill_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdFillRef param_1 = CmdFillRef{ ref.offset + 4u }; - return CmdFill_read(param, param_1, v_278); + return CmdFill_read(param, param_1, v_291); } static inline __attribute__((always_inline)) -CmdAlpha CmdAlpha_read(thread const Alloc& a, thread const CmdAlphaRef& ref, device Memory& v_278) +CmdAlpha CmdAlpha_read(thread const Alloc& a, thread const CmdAlphaRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdAlpha s; s.alpha = as_type(raw0); return s; } static inline __attribute__((always_inline)) -CmdAlpha Cmd_Alpha_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdAlpha Cmd_Alpha_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdAlphaRef param_1 = CmdAlphaRef{ ref.offset + 4u }; - return CmdAlpha_read(param, param_1, v_278); + return CmdAlpha_read(param, param_1, v_291); } static inline __attribute__((always_inline)) -CmdColor CmdColor_read(thread const Alloc& a, thread const CmdColorRef& ref, device Memory& v_278) +CmdColor CmdColor_read(thread const Alloc& a, thread const CmdColorRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdColor s; s.rgba_color = raw0; return s; } static inline __attribute__((always_inline)) -CmdColor Cmd_Color_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdColor Cmd_Color_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdColorRef param_1 = CmdColorRef{ ref.offset + 4u }; - return CmdColor_read(param, param_1, v_278); + return CmdColor_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -393,21 +408,21 @@ float4 unpacksRGB(thread const uint& srgba) } static inline __attribute__((always_inline)) -CmdLinGrad CmdLinGrad_read(thread const Alloc& a, thread const CmdLinGradRef& ref, device Memory& v_278) +CmdLinGrad CmdLinGrad_read(thread const Alloc& a, thread const CmdLinGradRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); Alloc param_4 = a; uint param_5 = ix + 2u; - uint raw2 = read_mem(param_4, param_5, v_278); + uint raw2 = read_mem(param_4, param_5, v_291); Alloc param_6 = a; uint param_7 = ix + 3u; - uint raw3 = read_mem(param_6, param_7, v_278); + uint raw3 = read_mem(param_6, param_7, v_291); CmdLinGrad s; s.index = raw0; s.line_x = as_type(raw1); @@ -417,23 +432,78 @@ CmdLinGrad CmdLinGrad_read(thread const Alloc& a, thread const CmdLinGradRef& re } static inline __attribute__((always_inline)) -CmdLinGrad Cmd_LinGrad_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdLinGrad Cmd_LinGrad_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdLinGradRef param_1 = CmdLinGradRef{ ref.offset + 4u }; - return CmdLinGrad_read(param, param_1, v_278); + return CmdLinGrad_read(param, param_1, v_291); } static inline __attribute__((always_inline)) -CmdImage CmdImage_read(thread const Alloc& a, thread const CmdImageRef& ref, device Memory& v_278) +CmdRadGrad CmdRadGrad_read(thread const Alloc& a, thread const CmdRadGradRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); Alloc param_2 = a; uint param_3 = ix + 1u; - uint raw1 = read_mem(param_2, param_3, v_278); + uint raw1 = read_mem(param_2, param_3, v_291); + Alloc param_4 = a; + uint param_5 = ix + 2u; + uint raw2 = read_mem(param_4, param_5, v_291); + Alloc param_6 = a; + uint param_7 = ix + 3u; + uint raw3 = read_mem(param_6, param_7, v_291); + Alloc param_8 = a; + uint param_9 = ix + 4u; + uint raw4 = read_mem(param_8, param_9, v_291); + Alloc param_10 = a; + uint param_11 = ix + 5u; + uint raw5 = read_mem(param_10, param_11, v_291); + Alloc param_12 = a; + uint param_13 = ix + 6u; + uint raw6 = read_mem(param_12, param_13, v_291); + Alloc param_14 = a; + uint param_15 = ix + 7u; + uint raw7 = read_mem(param_14, param_15, v_291); + Alloc param_16 = a; + uint param_17 = ix + 8u; + uint raw8 = read_mem(param_16, param_17, v_291); + Alloc param_18 = a; + uint param_19 = ix + 9u; + uint raw9 = read_mem(param_18, param_19, v_291); + Alloc param_20 = a; + uint param_21 = ix + 10u; + uint raw10 = read_mem(param_20, param_21, v_291); + CmdRadGrad s; + s.index = raw0; + s.mat = float4(as_type(raw1), as_type(raw2), as_type(raw3), as_type(raw4)); + s.xlat = float2(as_type(raw5), as_type(raw6)); + s.c1 = float2(as_type(raw7), as_type(raw8)); + s.ra = as_type(raw9); + s.roff = as_type(raw10); + return s; +} + +static inline __attribute__((always_inline)) +CmdRadGrad Cmd_RadGrad_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) +{ + Alloc param = a; + CmdRadGradRef param_1 = CmdRadGradRef{ ref.offset + 4u }; + return CmdRadGrad_read(param, param_1, v_291); +} + +static inline __attribute__((always_inline)) +CmdImage CmdImage_read(thread const Alloc& a, thread const CmdImageRef& ref, device Memory& v_291) +{ + uint ix = ref.offset >> uint(2); + Alloc param = a; + uint param_1 = ix + 0u; + uint raw0 = read_mem(param, param_1, v_291); + Alloc param_2 = a; + uint param_3 = ix + 1u; + uint raw1 = read_mem(param_2, param_3, v_291); CmdImage s; s.index = raw0; s.offset = int2(int(raw1 << uint(16)) >> 16, int(raw1) >> 16); @@ -441,11 +511,11 @@ CmdImage CmdImage_read(thread const Alloc& a, thread const CmdImageRef& ref, dev } static inline __attribute__((always_inline)) -CmdImage Cmd_Image_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdImage Cmd_Image_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdImageRef param_1 = CmdImageRef{ ref.offset + 4u }; - return CmdImage_read(param, param_1, v_278); + return CmdImage_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -458,10 +528,10 @@ spvUnsafeArray fillImage(thread const uint2& xy, thread const CmdImag int2 uv = int2(xy + chunk_offset(param)) + cmd_img.offset; float4 fg_rgba = image_atlas.read(uint2(uv)); float3 param_1 = fg_rgba.xyz; - float3 _1493 = fromsRGB(param_1); - fg_rgba.x = _1493.x; - fg_rgba.y = _1493.y; - fg_rgba.z = _1493.z; + float3 _1638 = fromsRGB(param_1); + fg_rgba.x = _1638.x; + fg_rgba.y = _1638.y; + fg_rgba.z = _1638.z; rgba[i] = fg_rgba; } return rgba; @@ -485,23 +555,23 @@ uint packsRGB(thread float4& rgba) } static inline __attribute__((always_inline)) -CmdEndClip CmdEndClip_read(thread const Alloc& a, thread const CmdEndClipRef& ref, device Memory& v_278) +CmdEndClip CmdEndClip_read(thread const Alloc& a, thread const CmdEndClipRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdEndClip s; s.blend = raw0; return s; } static inline __attribute__((always_inline)) -CmdEndClip Cmd_EndClip_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdEndClip Cmd_EndClip_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdEndClipRef param_1 = CmdEndClipRef{ ref.offset + 4u }; - return CmdEndClip_read(param, param_1, v_278); + return CmdEndClip_read(param, param_1, v_291); } static inline __attribute__((always_inline)) @@ -701,8 +771,8 @@ float3 set_lum(thread const float3& c, thread const float& l) { float3 param = c; float3 param_1 = c + float3(l - lum(param)); - float3 _901 = clip_color(param_1); - return _901; + float3 _1046 = clip_color(param_1); + return _1046; } static inline __attribute__((always_inline)) @@ -791,9 +861,9 @@ float3 mix_blend(thread const float3& cb, thread const float3& cs, thread const float3 param_20 = cb; float3 param_21 = cs; float param_22 = sat(param_20); - float3 _1192 = set_sat(param_21, param_22); + float3 _1337 = set_sat(param_21, param_22); float3 param_23 = cb; - float3 param_24 = _1192; + float3 param_24 = _1337; float param_25 = lum(param_23); b = set_lum(param_24, param_25); break; @@ -803,9 +873,9 @@ float3 mix_blend(thread const float3& cb, thread const float3& cs, thread const float3 param_26 = cs; float3 param_27 = cb; float param_28 = sat(param_26); - float3 _1206 = set_sat(param_27, param_28); + float3 _1351 = set_sat(param_27, param_28); float3 param_29 = cb; - float3 param_30 = _1206; + float3 param_30 = _1351; float param_31 = lum(param_29); b = set_lum(param_30, param_31); break; @@ -931,30 +1001,30 @@ float4 mix_compose(thread const float3& cb, thread const float3& cs, thread cons } static inline __attribute__((always_inline)) -CmdJump CmdJump_read(thread const Alloc& a, thread const CmdJumpRef& ref, device Memory& v_278) +CmdJump CmdJump_read(thread const Alloc& a, thread const CmdJumpRef& ref, device Memory& v_291) { uint ix = ref.offset >> uint(2); Alloc param = a; uint param_1 = ix + 0u; - uint raw0 = read_mem(param, param_1, v_278); + uint raw0 = read_mem(param, param_1, v_291); CmdJump s; s.new_ref = raw0; return s; } static inline __attribute__((always_inline)) -CmdJump Cmd_Jump_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_278) +CmdJump Cmd_Jump_read(thread const Alloc& a, thread const CmdRef& ref, device Memory& v_291) { Alloc param = a; CmdJumpRef param_1 = CmdJumpRef{ ref.offset + 4u }; - return CmdJump_read(param, param_1, v_278); + return CmdJump_read(param, param_1, v_291); } -kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1521 [[buffer(1)]], texture2d image [[texture(2)]], texture2d image_atlas [[texture(3)]], texture2d gradients [[texture(4)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +kernel void main0(device Memory& v_291 [[buffer(0)]], const device ConfigBuf& _1666 [[buffer(1)]], texture2d image [[texture(2)]], texture2d image_atlas [[texture(3)]], texture2d gradients [[texture(4)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) { - uint tile_ix = (gl_WorkGroupID.y * _1521.conf.width_in_tiles) + gl_WorkGroupID.x; + uint tile_ix = (gl_WorkGroupID.y * _1666.conf.width_in_tiles) + gl_WorkGroupID.x; Alloc param; - param.offset = _1521.conf.ptcl_alloc.offset; + param.offset = _1666.conf.ptcl_alloc.offset; uint param_1 = tile_ix * 1024u; uint param_2 = 1024u; Alloc cmd_alloc = slice_mem(param, param_1, param_2); @@ -967,7 +1037,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 rgba[i] = float4(0.0); } uint clip_depth = 0u; - bool mem_ok = v_278.mem_error == 0u; + bool mem_ok = v_291.mem_error == 0u; spvUnsafeArray df; TileSegRef tile_seg_ref; spvUnsafeArray area; @@ -976,7 +1046,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_3 = cmd_alloc; CmdRef param_4 = cmd_ref; - uint tag = Cmd_tag(param_3, param_4, v_278).tag; + uint tag = Cmd_tag(param_3, param_4, v_291).tag; if (tag == 0u) { break; @@ -987,7 +1057,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_5 = cmd_alloc; CmdRef param_6 = cmd_ref; - CmdStroke stroke = Cmd_Stroke_read(param_5, param_6, v_278); + CmdStroke stroke = Cmd_Stroke_read(param_5, param_6, v_291); for (uint k = 0u; k < 8u; k++) { df[k] = 1000000000.0; @@ -1000,7 +1070,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 bool param_9 = mem_ok; Alloc param_10 = new_alloc(param_7, param_8, param_9); TileSegRef param_11 = tile_seg_ref; - TileSeg seg = TileSeg_read(param_10, param_11, v_278); + TileSeg seg = TileSeg_read(param_10, param_11, v_291); float2 line_vec = seg.vector; for (uint k_1 = 0u; k_1 < 8u; k_1++) { @@ -1023,7 +1093,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_13 = cmd_alloc; CmdRef param_14 = cmd_ref; - CmdFill fill = Cmd_Fill_read(param_13, param_14, v_278); + CmdFill fill = Cmd_Fill_read(param_13, param_14, v_291); for (uint k_3 = 0u; k_3 < 8u; k_3++) { area[k_3] = float(fill.backdrop); @@ -1036,7 +1106,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 bool param_17 = mem_ok; Alloc param_18 = new_alloc(param_15, param_16, param_17); TileSegRef param_19 = tile_seg_ref; - TileSeg seg_1 = TileSeg_read(param_18, param_19, v_278); + TileSeg seg_1 = TileSeg_read(param_18, param_19, v_291); for (uint k_4 = 0u; k_4 < 8u; k_4++) { uint param_20 = k_4; @@ -1080,7 +1150,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_21 = cmd_alloc; CmdRef param_22 = cmd_ref; - CmdAlpha alpha = Cmd_Alpha_read(param_21, param_22, v_278); + CmdAlpha alpha = Cmd_Alpha_read(param_21, param_22, v_291); for (uint k_7 = 0u; k_7 < 8u; k_7++) { area[k_7] = alpha.alpha; @@ -1092,7 +1162,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_23 = cmd_alloc; CmdRef param_24 = cmd_ref; - CmdColor color = Cmd_Color_read(param_23, param_24, v_278); + CmdColor color = Cmd_Color_read(param_23, param_24, v_291); uint param_25 = color.rgba_color; float4 fg = unpacksRGB(param_25); for (uint k_8 = 0u; k_8 < 8u; k_8++) @@ -1107,7 +1177,7 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_26 = cmd_alloc; CmdRef param_27 = cmd_ref; - CmdLinGrad lin = Cmd_LinGrad_read(param_26, param_27, v_278); + CmdLinGrad lin = Cmd_LinGrad_read(param_26, param_27, v_291); float d_1 = ((lin.line_x * xy.x) + (lin.line_y * xy.y)) + lin.line_c; for (uint k_9 = 0u; k_9 < 8u; k_9++) { @@ -1117,11 +1187,12 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 int x = int(round(fast::clamp(my_d, 0.0, 1.0) * 511.0)); float4 fg_rgba = gradients.read(uint2(int2(x, int(lin.index)))); float3 param_29 = fg_rgba.xyz; - float3 _2092 = fromsRGB(param_29); - fg_rgba.x = _2092.x; - fg_rgba.y = _2092.y; - fg_rgba.z = _2092.z; - rgba[k_9] = fg_rgba; + float3 _2238 = fromsRGB(param_29); + fg_rgba.x = _2238.x; + fg_rgba.y = _2238.y; + fg_rgba.z = _2238.z; + float4 fg_k_1 = fg_rgba * area[k_9]; + rgba[k_9] = (rgba[k_9] * (1.0 - fg_k_1.w)) + fg_k_1; } cmd_ref.offset += 20u; break; @@ -1130,72 +1201,98 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 { Alloc param_30 = cmd_alloc; CmdRef param_31 = cmd_ref; - CmdImage fill_img = Cmd_Image_read(param_30, param_31, v_278); - uint2 param_32 = xy_uint; - CmdImage param_33 = fill_img; - spvUnsafeArray img; - img = fillImage(param_32, param_33, image_atlas); + CmdRadGrad rad = Cmd_RadGrad_read(param_30, param_31, v_291); for (uint k_10 = 0u; k_10 < 8u; k_10++) { - float4 fg_k_1 = img[k_10] * area[k_10]; - rgba[k_10] = (rgba[k_10] * (1.0 - fg_k_1.w)) + fg_k_1; + uint param_32 = k_10; + float2 my_xy_1 = xy + float2(chunk_offset(param_32)); + my_xy_1 = ((rad.mat.xz * my_xy_1.x) + (rad.mat.yw * my_xy_1.y)) - rad.xlat; + float ba = dot(my_xy_1, rad.c1); + float ca = rad.ra * dot(my_xy_1, my_xy_1); + float t_2 = (sqrt((ba * ba) + ca) - ba) - rad.roff; + int x_1 = int(round(fast::clamp(t_2, 0.0, 1.0) * 511.0)); + float4 fg_rgba_1 = gradients.read(uint2(int2(x_1, int(rad.index)))); + float3 param_33 = fg_rgba_1.xyz; + float3 _2348 = fromsRGB(param_33); + fg_rgba_1.x = _2348.x; + fg_rgba_1.y = _2348.y; + fg_rgba_1.z = _2348.z; + float4 fg_k_2 = fg_rgba_1 * area[k_10]; + rgba[k_10] = (rgba[k_10] * (1.0 - fg_k_2.w)) + fg_k_2; } - cmd_ref.offset += 12u; + cmd_ref.offset += 48u; break; } case 8u: { + Alloc param_34 = cmd_alloc; + CmdRef param_35 = cmd_ref; + CmdImage fill_img = Cmd_Image_read(param_34, param_35, v_291); + uint2 param_36 = xy_uint; + CmdImage param_37 = fill_img; + spvUnsafeArray img; + img = fillImage(param_36, param_37, image_atlas); for (uint k_11 = 0u; k_11 < 8u; k_11++) + { + float4 fg_k_3 = img[k_11] * area[k_11]; + rgba[k_11] = (rgba[k_11] * (1.0 - fg_k_3.w)) + fg_k_3; + } + cmd_ref.offset += 12u; + break; + } + case 9u: + { + for (uint k_12 = 0u; k_12 < 8u; k_12++) { uint d_2 = min(clip_depth, 127u); - float4 param_34 = float4(rgba[k_11]); - uint _2184 = packsRGB(param_34); - blend_stack[d_2][k_11] = _2184; - rgba[k_11] = float4(0.0); + float4 param_38 = float4(rgba[k_12]); + uint _2454 = packsRGB(param_38); + blend_stack[d_2][k_12] = _2454; + rgba[k_12] = float4(0.0); } clip_depth++; cmd_ref.offset += 4u; break; } - case 9u: + case 10u: { - Alloc param_35 = cmd_alloc; - CmdRef param_36 = cmd_ref; - CmdEndClip end_clip = Cmd_EndClip_read(param_35, param_36, v_278); + Alloc param_39 = cmd_alloc; + CmdRef param_40 = cmd_ref; + CmdEndClip end_clip = Cmd_EndClip_read(param_39, param_40, v_291); uint blend_mode = end_clip.blend >> uint(8); uint comp_mode = end_clip.blend & 255u; clip_depth--; - for (uint k_12 = 0u; k_12 < 8u; k_12++) + for (uint k_13 = 0u; k_13 < 8u; k_13++) { uint d_3 = min(clip_depth, 127u); - uint param_37 = blend_stack[d_3][k_12]; - float4 bg = unpacksRGB(param_37); - float4 fg_1 = rgba[k_12] * area[k_12]; - float3 param_38 = bg.xyz; - float3 param_39 = fg_1.xyz; - uint param_40 = blend_mode; - float3 blend = mix_blend(param_38, param_39, param_40); - float4 _2251 = fg_1; - float _2255 = fg_1.w; - float3 _2262 = mix(_2251.xyz, blend, float3(float((_2255 * bg.w) > 0.0))); - fg_1.x = _2262.x; - fg_1.y = _2262.y; - fg_1.z = _2262.z; - float3 param_41 = bg.xyz; - float3 param_42 = fg_1.xyz; - float param_43 = bg.w; - float param_44 = fg_1.w; - uint param_45 = comp_mode; - rgba[k_12] = mix_compose(param_41, param_42, param_43, param_44, param_45); + uint param_41 = blend_stack[d_3][k_13]; + float4 bg = unpacksRGB(param_41); + float4 fg_1 = rgba[k_13] * area[k_13]; + float3 param_42 = bg.xyz; + float3 param_43 = fg_1.xyz; + uint param_44 = blend_mode; + float3 blend = mix_blend(param_42, param_43, param_44); + float4 _2521 = fg_1; + float _2525 = fg_1.w; + float3 _2532 = mix(_2521.xyz, blend, float3(float((_2525 * bg.w) > 0.0))); + fg_1.x = _2532.x; + fg_1.y = _2532.y; + fg_1.z = _2532.z; + float3 param_45 = bg.xyz; + float3 param_46 = fg_1.xyz; + float param_47 = bg.w; + float param_48 = fg_1.w; + uint param_49 = comp_mode; + rgba[k_13] = mix_compose(param_45, param_46, param_47, param_48, param_49); } cmd_ref.offset += 8u; break; } - case 10u: + case 11u: { - Alloc param_46 = cmd_alloc; - CmdRef param_47 = cmd_ref; - cmd_ref = CmdRef{ Cmd_Jump_read(param_46, param_47, v_278).new_ref }; + Alloc param_50 = cmd_alloc; + CmdRef param_51 = cmd_ref; + cmd_ref = CmdRef{ Cmd_Jump_read(param_50, param_51, v_291).new_ref }; cmd_alloc.offset = cmd_ref.offset; break; } @@ -1203,8 +1300,8 @@ kernel void main0(device Memory& v_278 [[buffer(0)]], const device ConfigBuf& _1 } for (uint i_1 = 0u; i_1 < 8u; i_1++) { - uint param_48 = i_1; - image.write(float4(rgba[i_1].w), uint2(int2(xy_uint + chunk_offset(param_48)))); + uint param_52 = i_1; + image.write(float4(rgba[i_1].w), uint2(int2(xy_uint + chunk_offset(param_52)))); } } diff --git a/piet-gpu/shader/gen/kernel4_gray.spv b/piet-gpu/shader/gen/kernel4_gray.spv index 791b76cbe5790b68def82df39f9028af0fbc4b61..4633401878da028ffb233d5fda00be249864c016 100644 GIT binary patch literal 65312 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 CD&wO7 literal 58352 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 LKhH(6=i>hd17EVE diff --git a/piet-gpu/shader/gen/tile_alloc.dxil b/piet-gpu/shader/gen/tile_alloc.dxil index 7759910ca527b4ff12fbe9ba3ee03fdb6a428ca7..7b130e013427d747d2d4199badc1dcd7fb8e8297 100644 GIT binary patch delta 1185 zcmZ8hYfM{Z82(N>)fywCeQ zKR&-Ypw1|_n#|yT4`Fk-knujR?Bc+0q_X1Oo&r1B_DjAp7&TJ| zxS%%;HEd}W{9+^Y4$&wwrdKV8VI&6GIH*@e!f=g^F~*(GYYo=H`EwCd6=5V+OX}!; za?4Pj4r-y_UEXLN{|TKW7Wo`Y9R%kh^TG(`iIzCcUCrcrSCwn(mp^~l8Nr@ZHtbY# zb^Xuff7f0(E4(^O^tSFg+8xQh8nwFJ46!tyj@A=41=cd({4{DK^n}BLwOIOwR_9jp zhGy;#Icwl)WyG~mL`|Ih&@~~3b;JjA;M2Ny+T;Rv2cd{B2vM7m@PER z-K?_#jI}El;{%T^a2!NWE)t^KkYM(USJ0nfv9 zao1%3hN+o;se-K{5^l4WWHSBvs6qJIFr){N_@>To3w9!ll0!*m&~4vj+D%|MhudHR z=^^*kXTTr1?@_0qwAYjAGxFWv>xgo#os84oSKl=zW0Tr(2;TaG`2-r0yMyU5B*8Z- z=!qqN8E;BdjVtm)zS$VJ8P0@Ro~A%YUN5VU$Lgb3OzL6do?wcPbQ%}e>BA)r_!j|; z(XdC;)DBG5A1H1DMax6OYq=Q4!h!>4H7WtExIEgnOalWXhR;j;wmD?&t)#!6S>X1h zOjAXog5GUUcFa>2-!#A=ta9z|op^tlUz!y+Cn@zh zMG~<60Pfb>JL3k_zPj%Tr{rSXxPpzyF*KT(d9sIJc{u_0rhW^hdKGm~q~Z9IC`5d$ zez!9vg89S*sr(ywAN135+BSj|cphM9v|R0&qiQV3bKMkqlYvn?W(jAVSE@{QRb`H} zxD-G%3C{}~B~|tkB`Ad4&~`TUkywI-0Q$e;6+ zZRl*AN y({e>iD`%D}8khR2PrpvZ(xj@}wmU)Gn9-5(u~O|l8mUGd^A372$o?Jwy?+3ac!wAO delta 1180 zcmYLJe@q)?82;{hcfE2QTuVx?EsQG^>|EWhY$A2h^a@T;HXDOk`C%-DDH=e=HnZs> zZGm#j8ZBP8q0?E0b4^yv_%R%bGi`wdHh&aEDN0DeEkRA@Vy2skAzrhh-(T-P;vA z^KO}~qOzF)zz$IG6AVD|4g@$UFsg*Sr8K;(l>#s~62TYYTLhWpQiF4_Q{p!Inuuo)o}v9vF$;nc*xEvk*2m4HZLCEfB7!C9 zueKgKw*2GLTw;0c`qG;DS}b~#+H?MLWay+zdR0mfhMZOjinickyW5H_cvc9JG0q%m zpfL?rM2n34N9aQ^JiuQ=X+_{y-^#due0Js*d-eSI?ALoFB>;@%V%0Ej&#-iu<^h`z z3d}PUguCh6v&_5<2LNgUARm(ex7~M{eKY{6xd_L?PMT_LwJTn;V-1YM2WT8y)YN&@ zAxPz=D1f^o2gEV;e<7*yQnZkTlwt?d714ML5%Z>x{bZPflAYQP+p(v!c`d2z_{7z| z7-_Nqf01~f1t>zUcODQI#H|2*i}`>ABS_SY(#T&x`WSMI#3nXHHC?R4^-YnqjBz-V zN|y97R)O^=!7uz7IZ2w)Hv)=zC?(-^+M6--Lb9mT)uNj>Bn&3R^<=0cD$K6)H5UYg zopE93@RVEcXYZs9NT4=$`1{F?F8hd2eOSaGED}a%YJSH4P#GCOBBEQ$b@~dR8&=Iv z=3|)9Ai#C%5hZ9Oa;9II3v>lmFVeWzS6T4F6&WtZfs#wP_$P6%7%A@jadw+4JPheegsZ+ZS zI#)c2oUr{Aw8g!G)s%>tzWwEW0q)=xStklurq8;X-LBPH9J<|kl(8gf22n7{D$5-^ zwcue&90a+%ORmILbD}HR&+upEG<>Ot56jhvq*vd=FUz%1^u5fjFD7zZPnp$0&{e)S zkVXd&w@C&|oX0PQ)5kogow80$7bj#QNP%0~=hiq@BshGewKp?gHab-^`c&;` zcSTiw7PCasjm!;eb;Jkw E0q+okRR910 diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index a97715a..c49e2fa 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -192,10 +192,27 @@ void main() { int x = int(round(clamp(my_d, 0.0, 1.0) * float(GRADIENT_WIDTH - 1))); mediump vec4 fg_rgba = imageLoad(gradients, ivec2(x, int(lin.index))); fg_rgba.rgb = fromsRGB(fg_rgba.rgb); - rgba[k] = fg_rgba; + mediump vec4 fg_k = fg_rgba * area[k]; + rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k; } cmd_ref.offset += 4 + CmdLinGrad_size; break; + case Cmd_RadGrad: + CmdRadGrad rad = Cmd_RadGrad_read(cmd_alloc, cmd_ref); + for (uint k = 0; k < CHUNK; k++) { + vec2 my_xy = xy + vec2(chunk_offset(k)); + my_xy = rad.mat.xz * my_xy.x + rad.mat.yw * my_xy.y - rad.xlat; + float ba = dot(my_xy, rad.c1); + float ca = rad.ra * dot(my_xy, my_xy); + float t = sqrt(ba * ba + ca) - ba - rad.roff; + int x = int(round(clamp(t, 0.0, 1.0) * float(GRADIENT_WIDTH - 1))); + mediump vec4 fg_rgba = imageLoad(gradients, ivec2(x, int(rad.index))); + fg_rgba.rgb = fromsRGB(fg_rgba.rgb); + mediump vec4 fg_k = fg_rgba * area[k]; + rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k; + } + cmd_ref.offset += 4 + CmdRadGrad_size; + break; case Cmd_Image: CmdImage fill_img = Cmd_Image_read(cmd_alloc, cmd_ref); mediump vec4 img[CHUNK] = fillImage(xy_uint, fill_img); diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index 9b9b341..54dcc9e 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -18,6 +18,10 @@ struct CmdLinGradRef { uint offset; }; +struct CmdRadGradRef { + uint offset; +}; + struct CmdImageRef { uint offset; }; @@ -83,6 +87,21 @@ CmdLinGradRef CmdLinGrad_index(CmdLinGradRef ref, uint index) { return CmdLinGradRef(ref.offset + index * CmdLinGrad_size); } +struct CmdRadGrad { + uint index; + vec4 mat; + vec2 xlat; + vec2 c1; + float ra; + float roff; +}; + +#define CmdRadGrad_size 44 + +CmdRadGradRef CmdRadGrad_index(CmdRadGradRef ref, uint index) { + return CmdRadGradRef(ref.offset + index * CmdRadGrad_size); +} + struct CmdImage { uint index; ivec2 offset; @@ -131,11 +150,12 @@ CmdJumpRef CmdJump_index(CmdJumpRef ref, uint index) { #define Cmd_Alpha 4 #define Cmd_Color 5 #define Cmd_LinGrad 6 -#define Cmd_Image 7 -#define Cmd_BeginClip 8 -#define Cmd_EndClip 9 -#define Cmd_Jump 10 -#define Cmd_size 20 +#define Cmd_RadGrad 7 +#define Cmd_Image 8 +#define Cmd_BeginClip 9 +#define Cmd_EndClip 10 +#define Cmd_Jump 11 +#define Cmd_size 48 CmdRef Cmd_index(CmdRef ref, uint index) { return CmdRef(ref.offset + index * Cmd_size); @@ -213,6 +233,44 @@ void CmdLinGrad_write(Alloc a, CmdLinGradRef ref, CmdLinGrad s) { write_mem(a, ix + 3, floatBitsToUint(s.line_c)); } +CmdRadGrad CmdRadGrad_read(Alloc a, CmdRadGradRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + uint raw6 = read_mem(a, ix + 6); + uint raw7 = read_mem(a, ix + 7); + uint raw8 = read_mem(a, ix + 8); + uint raw9 = read_mem(a, ix + 9); + uint raw10 = read_mem(a, ix + 10); + CmdRadGrad s; + s.index = raw0; + s.mat = vec4(uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3), uintBitsToFloat(raw4)); + s.xlat = vec2(uintBitsToFloat(raw5), uintBitsToFloat(raw6)); + s.c1 = vec2(uintBitsToFloat(raw7), uintBitsToFloat(raw8)); + s.ra = uintBitsToFloat(raw9); + s.roff = uintBitsToFloat(raw10); + return s; +} + +void CmdRadGrad_write(Alloc a, CmdRadGradRef ref, CmdRadGrad s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.index); + write_mem(a, ix + 1, floatBitsToUint(s.mat.x)); + write_mem(a, ix + 2, floatBitsToUint(s.mat.y)); + write_mem(a, ix + 3, floatBitsToUint(s.mat.z)); + write_mem(a, ix + 4, floatBitsToUint(s.mat.w)); + write_mem(a, ix + 5, floatBitsToUint(s.xlat.x)); + write_mem(a, ix + 6, floatBitsToUint(s.xlat.y)); + write_mem(a, ix + 7, floatBitsToUint(s.c1.x)); + write_mem(a, ix + 8, floatBitsToUint(s.c1.y)); + write_mem(a, ix + 9, floatBitsToUint(s.ra)); + write_mem(a, ix + 10, floatBitsToUint(s.roff)); +} + CmdImage CmdImage_read(Alloc a, CmdImageRef ref) { uint ix = ref.offset >> 2; uint raw0 = read_mem(a, ix + 0); @@ -293,6 +351,10 @@ CmdLinGrad Cmd_LinGrad_read(Alloc a, CmdRef ref) { return CmdLinGrad_read(a, CmdLinGradRef(ref.offset + 4)); } +CmdRadGrad Cmd_RadGrad_read(Alloc a, CmdRef ref) { + return CmdRadGrad_read(a, CmdRadGradRef(ref.offset + 4)); +} + CmdImage Cmd_Image_read(Alloc a, CmdRef ref) { return CmdImage_read(a, CmdImageRef(ref.offset + 4)); } @@ -338,6 +400,11 @@ void Cmd_LinGrad_write(Alloc a, CmdRef ref, CmdLinGrad s) { CmdLinGrad_write(a, CmdLinGradRef(ref.offset + 4), s); } +void Cmd_RadGrad_write(Alloc a, CmdRef ref, CmdRadGrad s) { + write_mem(a, ref.offset >> 2, Cmd_RadGrad); + CmdRadGrad_write(a, CmdRadGradRef(ref.offset + 4), s); +} + void Cmd_Image_write(Alloc a, CmdRef ref, CmdImage s) { write_mem(a, ref.offset >> 2, Cmd_Image); CmdImage_write(a, CmdImageRef(ref.offset + 4), s); diff --git a/piet-gpu/src/encoder.rs b/piet-gpu/src/encoder.rs index 62c59c4..2f4b85e 100644 --- a/piet-gpu/src/encoder.rs +++ b/piet-gpu/src/encoder.rs @@ -62,6 +62,7 @@ const ANNOTATED_SIZE: usize = 40; // Tags for draw objects. See shader/drawtag.h for the authoritative source. const DRAWTAG_FILLCOLOR: u32 = 0x44; const DRAWTAG_FILLLINGRADIENT: u32 = 0x114; +const DRAWTAG_FILLRADGRADIENT: u32 = 0x2dc; const DRAWTAG_BEGINCLIP: u32 = 0x05; const DRAWTAG_ENDCLIP: u32 = 0x25; @@ -79,6 +80,16 @@ pub struct FillLinGradient { p1: [f32; 2], } +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +pub struct FillRadGradient { + index: u32, + p0: [f32; 2], + p1: [f32; 2], + r0: f32, + r1: f32, +} + #[allow(unused)] #[repr(C)] #[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] @@ -123,6 +134,13 @@ impl Encoder { self.transform_stream.push(transform); } + // Swap the last two tags in the tag stream; used for transformed + // gradients. + pub fn swap_last_tags(&mut self) { + let len = self.tag_stream.len(); + self.tag_stream.swap(len - 1, len - 2); + } + // -1.0 means "fill" pub fn linewidth(&mut self, linewidth: f32) { self.tag_stream.push(0x40); @@ -147,6 +165,16 @@ impl Encoder { self.drawdata_stream.extend(bytemuck::bytes_of(&element)); } + + /// Encode a fill radial gradient draw object. + /// + /// This should be encoded after a path. + pub fn fill_rad_gradient(&mut self, index: u32, p0: [f32; 2], p1: [f32; 2], r0: f32, r1: f32) { + self.drawtag_stream.push(DRAWTAG_FILLRADGRADIENT); + let element = FillRadGradient { index, p0, p1, r0, r1 }; + self.drawdata_stream.extend(bytemuck::bytes_of(&element)); + } + /// Start a clip. pub fn begin_clip(&mut self, blend: Option) { self.drawtag_stream.push(DRAWTAG_BEGINCLIP); @@ -220,7 +248,7 @@ impl Encoder { alloc += n_drawobj * DRAW_BBOX_SIZE; let drawinfo_alloc = alloc; // TODO: not optimized; it can be accumulated during encoding or summed from drawtags - const MAX_DRAWINFO_SIZE: usize = 16; + const MAX_DRAWINFO_SIZE: usize = 44; alloc += n_drawobj * MAX_DRAWINFO_SIZE; let config = Config { diff --git a/piet-gpu/src/gradient.rs b/piet-gpu/src/gradient.rs index 20982e9..e655908 100644 --- a/piet-gpu/src/gradient.rs +++ b/piet-gpu/src/gradient.rs @@ -18,15 +18,29 @@ use std::collections::hash_map::{Entry, HashMap}; -use piet::{Color, FixedLinearGradient, GradientStop}; +use piet::kurbo::Point; +use piet::{Color, FixedLinearGradient, GradientStop, FixedRadialGradient}; + +/// Radial gradient compatible with COLRv1 spec +#[derive(Debug, Clone)] +pub struct Colrv1RadialGradient { + /// The center of the iner circle. + pub center0: Point, + /// The offset of the origin relative to the center. + pub center1: Point, + /// The radius of the inner circle. + pub radius0: f64, + /// The radius of the outer circle. + pub radius1: f64, + /// The stops. + pub stops: Vec, +} #[derive(Clone)] pub struct BakedGradient { ramp: Vec, } -/// This is basically the same type as scene::FillLinGradient, so could -/// potentially use that directly. #[derive(Clone)] pub struct LinearGradient { pub(crate) start: [f32; 2], @@ -34,6 +48,15 @@ pub struct LinearGradient { pub(crate) ramp_id: u32, } +#[derive(Clone)] +pub struct RadialGradient { + pub(crate) start: [f32; 2], + pub(crate) end: [f32; 2], + pub(crate) r0: f32, + pub(crate) r1: f32, + pub(crate) ramp_id: u32, +} + #[derive(Default)] pub struct RampCache { ramps: Vec, @@ -154,6 +177,28 @@ impl RampCache { } } + pub fn add_radial_gradient(&mut self, rad: &FixedRadialGradient) -> RadialGradient { + let ramp_id = self.add_ramp(&rad.stops); + RadialGradient { + ramp_id: ramp_id as u32, + start: crate::render_ctx::to_f32_2(rad.center + rad.origin_offset), + end: crate::render_ctx::to_f32_2(rad.center), + r0: 0.0, + r1: rad.radius as f32, + } + } + + pub fn add_radial_gradient_colrv1(&mut self, rad: &Colrv1RadialGradient) -> RadialGradient { + let ramp_id = self.add_ramp(&rad.stops); + RadialGradient { + ramp_id: ramp_id as u32, + start: crate::render_ctx::to_f32_2(rad.center0), + end: crate::render_ctx::to_f32_2(rad.center1), + r0: rad.radius0 as f32, + r1: rad.radius1 as f32, + } + } + /// Dump the contents of a gradient. This is for debugging. #[allow(unused)] pub(crate) fn dump_gradient(&self, lin: &LinearGradient) { diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 249735a..475d723 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -12,6 +12,7 @@ use std::convert::TryInto; pub use blend::{Blend, BlendMode, CompositionMode}; pub use render_ctx::PietGpuRenderContext; +pub use gradient::Colrv1RadialGradient; use piet::kurbo::Vec2; use piet::{ImageFormat, RenderContext}; @@ -21,7 +22,7 @@ use piet_gpu_hal::{ ImageLayout, Pipeline, QueryPool, Session, }; -pub use pico_svg::PicoSvg; +use pico_svg::PicoSvg; use stages::{ClipBinding, ElementBinding, ElementCode}; use crate::stages::{ClipCode, Config, ElementStage}; diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 024dd2b..dca03eb 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -13,7 +13,7 @@ use piet_gpu_hal::BufWrite; use piet_gpu_types::encoder::{Encode, Encoder}; use piet_gpu_types::scene::Element; -use crate::gradient::{LinearGradient, RampCache}; +use crate::gradient::{LinearGradient, RadialGradient, RampCache, Colrv1RadialGradient}; use crate::text::Font; pub use crate::text::{PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder}; use crate::Blend; @@ -50,6 +50,7 @@ pub struct PietGpuRenderContext { pub enum PietGpuBrush { Solid(u32), LinGradient(LinearGradient), + RadGradient(RadialGradient), } #[derive(Default)] @@ -187,6 +188,10 @@ impl RenderContext for PietGpuRenderContext { let lin = self.ramp_cache.add_linear_gradient(&lin); Ok(PietGpuBrush::LinGradient(lin)) } + FixedGradient::Radial(rad) => { + let rad = self.ramp_cache.add_radial_gradient(&rad); + Ok(PietGpuBrush::RadGradient(rad)) + } _ => todo!("don't do radial gradients yet"), } } @@ -338,6 +343,20 @@ impl PietGpuRenderContext { } } + pub fn radial_gradient_colrv1(&mut self, rad: &Colrv1RadialGradient) -> PietGpuBrush { + PietGpuBrush::RadGradient(self.ramp_cache.add_radial_gradient_colrv1(rad)) + } + + pub fn fill_transform(&mut self, shape: impl Shape, brush: &PietGpuBrush, transform: Affine) { + let path = shape.path_elements(TOLERANCE); + self.encode_linewidth(-1.0); + self.encode_path(path, true); + self.encode_transform(Transform::from_kurbo(transform)); + self.new_encoder.swap_last_tags(); + self.encode_brush(&brush); + self.encode_transform(Transform::from_kurbo(transform.inverse())); + } + fn encode_path(&mut self, path: impl Iterator, is_fill: bool) { if is_fill { self.encode_path_inner( @@ -420,6 +439,10 @@ impl PietGpuRenderContext { self.new_encoder .fill_lin_gradient(lin.ramp_id, lin.start, lin.end); } + PietGpuBrush::RadGradient(rad) => { + self.new_encoder + .fill_rad_gradient(rad.ramp_id, rad.start, rad.end, rad.r0, rad.r1); + } } } } diff --git a/piet-gpu/src/test_scenes.rs b/piet-gpu/src/test_scenes.rs index 350b9dd..cf5a50d 100644 --- a/piet-gpu/src/test_scenes.rs +++ b/piet-gpu/src/test_scenes.rs @@ -2,10 +2,10 @@ use rand::{Rng, RngCore}; -use crate::{Blend, BlendMode, CompositionMode, PietGpuRenderContext}; +use crate::{Blend, BlendMode, CompositionMode, PietGpuRenderContext, Colrv1RadialGradient}; use piet::kurbo::{Affine, BezPath, Circle, Line, Point, Rect, Shape}; use piet::{ - Color, FixedGradient, FixedLinearGradient, GradientStop, Text, TextAttribute, TextLayoutBuilder, + Color, FixedGradient, FixedRadialGradient, GradientStop, Text, TextAttribute, TextLayoutBuilder, }; use crate::{PicoSvg, RenderContext, Vec2}; @@ -21,13 +21,18 @@ pub fn render_blend_test(rc: &mut PietGpuRenderContext, i: usize, blend: Blend) rc.restore().unwrap(); } -pub fn render_svg(rc: &mut impl RenderContext, svg: &PicoSvg) { +pub fn render_svg(rc: &mut impl RenderContext, filename: &str, scale: f64) { + let xml_str = std::fs::read_to_string(filename).unwrap(); + let start = std::time::Instant::now(); + let svg = PicoSvg::load(&xml_str, scale).unwrap(); + println!("parsing time: {:?}", start.elapsed()); + let start = std::time::Instant::now(); svg.render(rc); println!("flattening and encoding time: {:?}", start.elapsed()); } -pub fn render_scene(rc: &mut impl RenderContext) { +pub fn render_scene(rc: &mut PietGpuRenderContext) { const WIDTH: usize = 2048; const HEIGHT: usize = 1536; let mut rng = rand::thread_rng(); @@ -137,7 +142,7 @@ fn render_alpha_test(rc: &mut impl RenderContext) { } #[allow(unused)] -fn render_gradient_test(rc: &mut impl RenderContext) { +fn render_gradient_test(rc: &mut PietGpuRenderContext) { let stops = vec![ GradientStop { color: Color::rgb8(0, 255, 0), @@ -148,14 +153,18 @@ fn render_gradient_test(rc: &mut impl RenderContext) { pos: 1.0, }, ]; - let lin = FixedLinearGradient { - start: Point::new(0.0, 100.0), - end: Point::new(0.0, 300.0), + let rad = Colrv1RadialGradient { + center0: Point::new(200.0, 200.0), + center1: Point::new(250.0, 200.0), + radius0: 50.0, + radius1: 100.0, stops, }; - let brush = FixedGradient::Linear(lin); + let brush = rc.radial_gradient_colrv1(&rad); + //let brush = FixedGradient::Radial(rad); //let brush = Color::rgb8(0, 128, 0); - rc.fill(Rect::new(100.0, 100.0, 300.0, 300.0), &brush); + let transform = Affine::new([1.0, 0.0, 0.0, 0.5, 0.0, 100.0]); + rc.fill_transform(Rect::new(100.0, 100.0, 300.0, 300.0), &brush, transform); } fn diamond(origin: Point) -> impl Shape { diff --git a/piet-scene/Cargo.toml b/piet-scene/Cargo.toml new file mode 100644 index 0000000..8706119 --- /dev/null +++ b/piet-scene/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "piet-scene" +version = "0.1.0" +license = "MIT/Apache-2.0" +edition = "2021" + +[dependencies] +bytemuck = { version = "1.7.2", features = ["derive"] } +smallvec = "1.8.0" diff --git a/piet-scene/src/brush/color.rs b/piet-scene/src/brush/color.rs new file mode 100644 index 0000000..d079210 --- /dev/null +++ b/piet-scene/src/brush/color.rs @@ -0,0 +1,62 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +impl Color { + pub const fn rgb8(r: u8, g: u8, b: u8) -> Self { + Self { r, g, b, a: 255 } + } + + pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Self { + Self { r, g, b, a } + } + + pub fn rgb>(r: C, g: C, b: C) -> Self { + Self::rgb8( + (r.into() / 255.0) as u8, + (g.into() / 255.0) as u8, + (b.into() / 255.0) as u8, + ) + } + + pub fn rgba>(r: C, g: C, b: C, a: C) -> Self { + Self::rgba8( + (r.into() / 255.0) as u8, + (g.into() / 255.0) as u8, + (b.into() / 255.0) as u8, + (a.into() / 255.0) as u8, + ) + } + + pub fn pack(self) -> u32 { + (self.b as u32) << 24 | (self.g as u32) << 16 | (self.r as u32) << 8 | self.a as u32 + } + + pub fn to_premul_u32(self) -> u32 { + let a = self.a as f64 / 255.0; + let r = (self.r as f64 * a) as u32; + let g = (self.g as f64 * a) as u32; + let b = (self.b as f64 * a) as u32; + r | (g << 8) | (b << 16) | ((self.a as u32) << 24) + } +} diff --git a/piet-scene/src/brush/gradient.rs b/piet-scene/src/brush/gradient.rs new file mode 100644 index 0000000..51d558e --- /dev/null +++ b/piet-scene/src/brush/gradient.rs @@ -0,0 +1,87 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use super::color::Color; +use crate::geometry::Point; +use smallvec::SmallVec; +use std::hash::{Hash, Hasher}; + +#[derive(Copy, Clone, PartialOrd, Default, Debug)] +pub struct Stop { + pub offset: f32, + pub color: Color, +} + +impl Hash for Stop { + fn hash(&self, state: &mut H) { + self.offset.to_bits().hash(state); + self.color.hash(state); + } +} + +// Override PartialEq to use to_bits for the offset to match with the Hash impl +impl std::cmp::PartialEq for Stop { + fn eq(&self, other: &Self) -> bool { + self.offset.to_bits() == other.offset.to_bits() && self.color == other.color + } +} + +impl std::cmp::Eq for Stop {} + +pub type StopVec = SmallVec<[Stop; 4]>; + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Extend { + Pad, + Repeat, + Reflect, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Space { + Object, + Global, +} + +#[derive(Clone, Debug)] +pub struct LinearGradient { + pub space: Space, + pub start: Point, + pub end: Point, + pub stops: StopVec, + pub extend: Extend, +} + +#[derive(Clone, Debug)] +pub struct RadialGradient { + pub space: Space, + pub center0: Point, + pub radius0: f32, + pub center1: Point, + pub radius1: f32, + pub stops: StopVec, + pub extend: Extend, +} + +#[derive(Clone, Debug)] +pub struct SweepGradient { + pub space: Space, + pub center: Point, + pub start_angle: f32, + pub end_angle: f32, + pub stops: StopVec, + pub extend: Extend, +} diff --git a/piet-scene/src/brush/image.rs b/piet-scene/src/brush/image.rs new file mode 100644 index 0000000..07157e7 --- /dev/null +++ b/piet-scene/src/brush/image.rs @@ -0,0 +1,96 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use std::result::Result; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Format { + A8, + Rgba8, +} + +impl Format { + pub fn data_size(self, width: u32, height: u32) -> Option { + (width as usize) + .checked_mul(height as usize) + .and_then(|size| { + size.checked_mul(match self { + Self::A8 => 1, + Self::Rgba8 => 4, + }) + }) + } +} + +#[derive(Clone, Debug)] +pub struct Image(Arc); + +#[derive(Clone, Debug)] +struct Inner { + id: u64, + format: Format, + width: u32, + height: u32, + data: Vec, +} + +impl Image { + pub fn new( + format: Format, + width: u32, + height: u32, + mut data: Vec, + ) -> Result { + let data_size = format.data_size(width, height).ok_or(DataSizeError)?; + if data.len() < data_size { + return Err(DataSizeError); + } + data.truncate(data_size); + static ID: AtomicU64 = AtomicU64::new(1); + Ok(Self(Arc::new(Inner { + id: ID.fetch_add(1, Ordering::Relaxed), + format, + width, + height, + data, + }))) + } + + pub fn id(&self) -> u64 { + self.0.id + } + + pub fn format(&self) -> Format { + self.0.format + } + + pub fn width(&self) -> u32 { + self.0.width + } + + pub fn height(&self) -> u32 { + self.0.height + } + + pub fn data(&self) -> &[u8] { + &self.0.data + } +} + +#[derive(Clone, Debug)] +pub struct DataSizeError; diff --git a/piet-scene/src/brush/mod.rs b/piet-scene/src/brush/mod.rs new file mode 100644 index 0000000..9cde1fb --- /dev/null +++ b/piet-scene/src/brush/mod.rs @@ -0,0 +1,35 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +mod color; +mod gradient; +mod image; + +pub use color::Color; +pub use gradient::*; +pub use image::*; + +use crate::resource::PersistentBrush; + +#[derive(Clone, Debug)] +pub enum Brush { + Solid(Color), + LinearGradient(LinearGradient), + RadialGradient(RadialGradient), + SweepGradient(SweepGradient), + Image(Image), + Persistent(PersistentBrush), +} diff --git a/piet-scene/src/geometry.rs b/piet-scene/src/geometry.rs new file mode 100644 index 0000000..a40cc40 --- /dev/null +++ b/piet-scene/src/geometry.rs @@ -0,0 +1,189 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use core::borrow::Borrow; +use core::hash::{Hash, Hasher}; + +/// Two dimensional point. +#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug)] +#[repr(C)] +pub struct Point { + pub x: f32, + pub y: f32, +} + +impl Hash for Point { + fn hash(&self, state: &mut H) { + self.x.to_bits().hash(state); + self.y.to_bits().hash(state); + } +} + +impl Point { + pub const fn new(x: f32, y: f32) -> Self { + Self { x, y } + } + + pub fn transform(&self, affine: &Affine) -> Self { + Self { + x: self.x * affine.xx + self.y * affine.yx + affine.dx, + y: self.y * affine.yy + self.y * affine.xy + affine.dy, + } + } +} + +impl From<[f32; 2]> for Point { + fn from(value: [f32; 2]) -> Self { + Self::new(value[0], value[1]) + } +} + +impl From<(f32, f32)> for Point { + fn from(value: (f32, f32)) -> Self { + Self::new(value.0, value.1) + } +} + +/// Affine transformation matrix. +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct Affine { + pub xx: f32, + pub yx: f32, + pub xy: f32, + pub yy: f32, + pub dx: f32, + pub dy: f32, +} + +impl Affine { + pub const fn new(elements: &[f32; 6]) -> Self { + Self { + xx: elements[0], + yx: elements[1], + xy: elements[2], + yy: elements[3], + dx: elements[4], + dy: elements[5], + } + } + + /// Creates a new affine transform representing the specified scale along the + /// x and y axes. + pub fn scale(x: f32, y: f32) -> Self { + Self::new(&[x, 0., 0., y, 0., 0.]) + } + + /// Creates a new affine transform representing the specified translation. + pub fn translate(x: f32, y: f32) -> Self { + Self::new(&[1., 0., 0., 1., x, y]) + } + + /// Creates a new affine transform representing a counter-clockwise + /// rotation for the specified angle in radians. + pub fn rotate(th: f32) -> Self { + let (s, c) = th.sin_cos(); + Self::new(&[c, s, -s, c, 0., 0.]) + } + + /// Creates a new skew transform + pub fn skew(x: f32, y: f32) -> Self { + Self::new(&[1., x.tan(), y.tan(), 1., 0., 0.]) + } + + pub fn around_center(&self, x: f32, y: f32) -> Self { + Self::translate(x, y) * *self * Self::translate(-x, -y) + } + + /// Transforms the specified point. + pub fn transform_point(&self, point: Point) -> Point { + Point { + x: point.x * self.xx + point.y * self.yx + self.dx, + y: point.y * self.yy + point.y * self.xy + self.dy, + } + } +} + +impl std::ops::Mul for Affine { + type Output = Self; + fn mul(self, other: Self) -> Self { + Self::new(&[ + self.xx * other.xx + self.xy * other.yx, + self.yx * other.xx + self.yy * other.yx, + self.xx * other.xy + self.xy * other.yy, + self.yx * other.xy + self.yy * other.yy, + self.xx * other.dx + self.xy * other.dy + self.dx, + self.yx * other.dx + self.yy * other.dy + self.dy, + ]) + } +} + +/// Axis-aligned rectangle represented as minimum and maximum points. +#[derive(Copy, Clone, Default, Debug)] +#[repr(C)] +pub struct Rect { + pub min: Point, + pub max: Point, +} + +impl Rect { + /// Creates a new rectangle that encloses the specified collection of + /// points. + pub fn from_points(points: I) -> Self + where + I: IntoIterator, + I::Item: Borrow, + { + let mut rect = Self { + min: Point::new(f32::MAX, f32::MAX), + max: Point::new(f32::MIN, f32::MIN), + }; + let mut count = 0; + for point in points { + rect.add(*point.borrow()); + count += 1; + } + if count != 0 { + rect + } else { + Self::default() + } + } + + /// Returns the width of the rectangle. + pub fn width(&self) -> f32 { + self.max.x - self.min.x + } + + /// Returns the height of the rectangle. + pub fn height(&self) -> f32 { + self.max.y - self.min.y + } + + /// Extends the rectangle to include the specified point. + pub fn add(&mut self, point: Point) { + self.min.x = self.min.x.min(point.x); + self.min.y = self.min.y.min(point.y); + self.max.x = self.max.x.max(point.x); + self.max.y = self.max.y.max(point.y); + } + + /// Returns a new rectangle that encloses the minimum and maximum points + /// of this rectangle after applying the specified transform to each. + pub fn transform(&self, affine: &Affine) -> Self { + Self::from_points([self.min.transform(affine), self.max.transform(affine)]) + } +} diff --git a/piet-scene/src/lib.rs b/piet-scene/src/lib.rs new file mode 100644 index 0000000..6b23f6d --- /dev/null +++ b/piet-scene/src/lib.rs @@ -0,0 +1,21 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +pub mod brush; +pub mod geometry; +pub mod path; +pub mod resource; +pub mod scene; diff --git a/piet-scene/src/main.rs b/piet-scene/src/main.rs new file mode 100644 index 0000000..8fd361a --- /dev/null +++ b/piet-scene/src/main.rs @@ -0,0 +1,30 @@ +use piet_scene::geometry::*; +use piet_scene::path::*; +use piet_scene::scene::*; +use piet_scene::{geometry::*, path::*, resource::ResourceContext, scene::*}; + +fn main() { + let mut scene = Scene::default(); + let mut rcx = ResourceContext::new(); + let mut sb = build_scene(&mut scene, &mut rcx); + + sb.push_layer(Blend::default(), Rect::default().elements()); + + let mut path = Path::new(); + let mut b = PathBuilder::new(&mut path); + b.move_to(100., 100.); + b.line_to(200., 200.); + b.close_path(); + b.move_to(50., 50.); + b.line_to(600., 150.); + b.move_to(4., 2.); + b.quad_to(8., 8., 9., 9.); + b.close_path(); + println!("{:?}", path); + for el in path.elements() { + println!("{:?}", el); + } + //sb.push_layer(path.elements(), BlendMode::default()); + + sb.push_layer(Blend::default(), [Element::MoveTo((0., 0.).into())]); +} diff --git a/piet-scene/src/path.rs b/piet-scene/src/path.rs new file mode 100644 index 0000000..ffbce3e --- /dev/null +++ b/piet-scene/src/path.rs @@ -0,0 +1,311 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use super::geometry::{Point, Rect}; + +/// Action of a path element. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Verb { + MoveTo, + LineTo, + QuadTo, + CurveTo, + Close, +} + +/// Element of a path represented by a verb and its associated points. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Element { + MoveTo(Point), + LineTo(Point), + QuadTo(Point, Point), + CurveTo(Point, Point, Point), + Close, +} + +impl Element { + /// Returns the verb that describes the action of the path element. + pub fn verb(&self) -> Verb { + match self { + Self::MoveTo(..) => Verb::MoveTo, + Self::LineTo(..) => Verb::LineTo, + Self::QuadTo(..) => Verb::QuadTo, + Self::CurveTo(..) => Verb::CurveTo, + Self::Close => Verb::Close, + } + } +} + +/// Encoded collection of path elements. +#[derive(Clone, Default, Debug)] +pub struct Path { + tag_stream: Vec, + pathseg_stream: Vec, + n_path: u32, + n_pathseg: u32, +} + +impl Path { + pub fn new() -> Self { + Self::default() + } + + pub fn elements(&self) -> Elements { + Elements::new(&self) + } +} + +#[derive(Clone)] +pub struct Elements<'a> { + tag_stream: &'a [u8], + points: &'a [[f32; 2]], + tag_ix: usize, + point_ix: usize, + next_element: Option, + close: bool, +} + +impl<'a> Elements<'a> { + fn new(path: &'a Path) -> Self { + let points: &'a [[f32; 2]] = bytemuck::cast_slice(&path.pathseg_stream); + let (point_ix, next_element) = match points.get(0) { + Some(&point) => (1, Some(Element::MoveTo(point.into()))), + None => (0, None), + }; + Self { + tag_stream: &path.tag_stream, + points, + tag_ix: 0, + point_ix, + next_element, + close: false, + } + } +} + +impl<'a> Iterator for Elements<'a> { + type Item = Element; + + fn next(&mut self) -> Option { + // println!("n_points: {}", self.points.len()); + // println!("tag_ix: {}, point_ix: {}, el: {:?}, close: {}", self.tag_ix, self.point_ix, self.next_element, self.close); + if self.close { + self.close = false; + return Some(Element::Close); + } + if let Some(next_el) = self.next_element.take() { + return Some(next_el); + } + let tag = *self.tag_stream.get(self.tag_ix)?; + self.tag_ix += 1; + let end = tag & 4 != 0; + let el = match tag & 3 { + 1 => { + let p0 = *self.points.get(self.point_ix)?; + self.point_ix += 1; + Element::LineTo(p0.into()) + } + 2 => { + let p0 = *self.points.get(self.point_ix)?; + let p1 = *self.points.get(self.point_ix + 1)?; + self.point_ix += 2; + Element::QuadTo(p0.into(), p1.into()) + } + 3 => { + let p0 = *self.points.get(self.point_ix)?; + let p1 = *self.points.get(self.point_ix + 1)?; + let p2 = *self.points.get(self.point_ix + 2)?; + self.point_ix += 3; + Element::CurveTo(p0.into(), p1.into(), p2.into()) + } + _ => return None, + }; + if end { + // println!("END!"); + if let Some(&p0) = self.points.get(self.point_ix) { + self.point_ix += 1; + self.next_element = Some(Element::MoveTo(p0.into())); + } + self.close = tag & 0x80 != 0; + } + Some(el) + } +} + +pub struct PathBuilder<'a> { + tag_stream: &'a mut Vec, + // If we're never going to use the i16 encoding, it might be + // slightly faster to store this as Vec, we'd get aligned + // stores on ARM etc. + pathseg_stream: &'a mut Vec, + first_pt: [f32; 2], + state: State, + n_pathseg: u32, +} + +#[derive(PartialEq)] +enum State { + Start, + MoveTo, + NonemptySubpath, +} + +impl<'a> PathBuilder<'a> { + pub fn new(path: &'a mut Path) -> Self { + Self { + tag_stream: &mut path.tag_stream, + pathseg_stream: &mut path.pathseg_stream, + first_pt: [0.0, 0.0], + state: State::Start, + n_pathseg: 0, + } + } + + fn new_inner(tags: &'a mut Vec, pathsegs: &'a mut Vec) -> PathBuilder<'a> { + PathBuilder { + tag_stream: tags, + pathseg_stream: pathsegs, + first_pt: [0.0, 0.0], + state: State::Start, + n_pathseg: 0, + } + } + + pub fn move_to(&mut self, x: f32, y: f32) { + let buf = [x, y]; + let bytes = bytemuck::bytes_of(&buf); + self.first_pt = buf; + if self.state == State::MoveTo { + let new_len = self.pathseg_stream.len() - 8; + self.pathseg_stream.truncate(new_len); + } + if self.state == State::NonemptySubpath { + if let Some(tag) = self.tag_stream.last_mut() { + *tag |= 4; + } + } + self.pathseg_stream.extend_from_slice(bytes); + self.state = State::MoveTo; + } + + pub fn line_to(&mut self, x: f32, y: f32) { + if self.state == State::Start { + // should warn or error + return; + } + let buf = [x, y]; + let bytes = bytemuck::bytes_of(&buf); + self.pathseg_stream.extend_from_slice(bytes); + self.tag_stream.push(9); + self.state = State::NonemptySubpath; + self.n_pathseg += 1; + } + + pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) { + if self.state == State::Start { + return; + } + let buf = [x1, y1, x2, y2]; + let bytes = bytemuck::bytes_of(&buf); + self.pathseg_stream.extend_from_slice(bytes); + self.tag_stream.push(10); + self.state = State::NonemptySubpath; + self.n_pathseg += 1; + } + + pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) { + if self.state == State::Start { + return; + } + let buf = [x1, y1, x2, y2, x3, y3]; + let bytes = bytemuck::bytes_of(&buf); + self.pathseg_stream.extend_from_slice(bytes); + self.tag_stream.push(11); + self.state = State::NonemptySubpath; + self.n_pathseg += 1; + } + + pub fn close_path(&mut self) { + match self.state { + State::Start => return, + State::MoveTo => { + let new_len = self.pathseg_stream.len() - 8; + self.pathseg_stream.truncate(new_len); + self.state = State::Start; + return; + } + State::NonemptySubpath => (), + } + let len = self.pathseg_stream.len(); + if len < 8 { + // can't happen + return; + } + let first_bytes = bytemuck::bytes_of(&self.first_pt); + if &self.pathseg_stream[len - 8..len] != first_bytes { + self.pathseg_stream.extend_from_slice(first_bytes); + self.tag_stream.push(0x80 | 13); + self.n_pathseg += 1; + } else { + if let Some(tag) = self.tag_stream.last_mut() { + *tag |= 0x80 | 4; + } + } + self.state = State::Start; + } + + fn finish(&mut self) { + if self.state == State::MoveTo { + let new_len = self.pathseg_stream.len() - 8; + self.pathseg_stream.truncate(new_len); + } + if let Some(tag) = self.tag_stream.last_mut() { + *tag |= 4; + } + } + + /// Finish encoding a path. + /// + /// Encode this after encoding path segments. + pub fn path(&mut self) { + self.finish(); + // maybe don't encode if path is empty? might throw off sync though + self.tag_stream.push(0x10); + } + + /// Get the number of path segments. + /// + /// This is the number of path segments that will be written by the + /// path stage; use this for allocating the output buffer. + /// + /// Also note: it takes `self` for lifetime reasons. + pub fn n_pathseg(self) -> u32 { + self.n_pathseg + } +} + +impl Rect { + pub fn elements(&self) -> impl Iterator + Clone { + let elements = [ + Element::MoveTo((self.min.x, self.min.y).into()), + Element::LineTo((self.max.x, self.min.y).into()), + Element::LineTo((self.max.x, self.max.y).into()), + Element::LineTo((self.min.x, self.max.y).into()), + Element::Close, + ]; + (0..5).map(move |i| elements[i]) + } +} diff --git a/piet-scene/src/resource/mod.rs b/piet-scene/src/resource/mod.rs new file mode 100644 index 0000000..36e6419 --- /dev/null +++ b/piet-scene/src/resource/mod.rs @@ -0,0 +1,37 @@ +mod ramp_cache; + +use crate::brush::{Brush, Stop}; +use ramp_cache::RampCache; + +/// Context for caching resources across rendering operations. +#[derive(Default)] +pub struct ResourceContext { + ramp_cache: RampCache, +} + +impl ResourceContext { + pub fn new() -> Self { + Self::default() + } + + pub fn advance(&mut self) { + self.ramp_cache.advance(); + } + + pub fn add_ramp(&mut self, stops: &[Stop]) -> u32 { + self.ramp_cache.add(stops) + } + + pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush { + PersistentBrush { kind: 0, id: 0 } + } + + pub fn destroy_brush(&mut self, brush: PersistentBrush) {} +} + +/// Handle for a brush that is managed by the resource context. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct PersistentBrush { + kind: u8, + id: u64, +} diff --git a/piet-scene/src/resource/ramp_cache.rs b/piet-scene/src/resource/ramp_cache.rs new file mode 100644 index 0000000..0c509e4 --- /dev/null +++ b/piet-scene/src/resource/ramp_cache.rs @@ -0,0 +1,138 @@ +use crate::brush::{Color, Stop, StopVec}; +use std::collections::HashMap; + +const N_SAMPLES: usize = 512; +const RETAINED_COUNT: usize = 64; + +#[derive(Default)] +pub struct RampCache { + epoch: u64, + map: HashMap, + data: Vec, +} + +impl RampCache { + pub fn new() -> Self { + Self::default() + } + + pub fn advance(&mut self) { + self.epoch += 1; + if self.map.len() > RETAINED_COUNT { + self.map + .retain(|_key, value| value.0 < RETAINED_COUNT as u32); + self.data.truncate(RETAINED_COUNT * N_SAMPLES); + } + } + + pub fn clear(&mut self) { + self.epoch = 0; + self.map.clear(); + self.data.clear(); + } + + pub fn add(&mut self, stops: &[Stop]) -> u32 { + if let Some(entry) = self.map.get_mut(stops) { + entry.1 = self.epoch; + entry.0 + } else if self.map.len() < RETAINED_COUNT { + let id = (self.data.len() / N_SAMPLES) as u32; + self.data.extend(make_ramp(stops)); + self.map.insert(stops.into(), (id, self.epoch)); + id + } else { + let mut reuse = None; + for (stops, (id, epoch)) in &self.map { + if *epoch + 2 < self.epoch { + reuse = Some((stops.to_owned(), *id)); + break; + } + } + if let Some((old_stops, id)) = reuse { + self.map.remove(&old_stops); + let start = id as usize * N_SAMPLES; + for (dst, src) in self.data[start..start + N_SAMPLES] + .iter_mut() + .zip(make_ramp(stops)) + { + *dst = src; + } + self.map.insert(stops.into(), (id, self.epoch)); + id + } else { + let id = (self.data.len() / N_SAMPLES) as u32; + self.data.extend(make_ramp(stops)); + self.map.insert(stops.into(), (id, self.epoch)); + id + } + } + } + + pub fn data(&self) -> &[u32] { + &self.data + } +} + +fn make_ramp<'a>(stops: &'a [Stop]) -> impl Iterator + 'a { + let mut last_u = 0.0; + let mut last_c = ColorF64::from_color(stops[0].color); + let mut this_u = last_u; + let mut this_c = last_c; + let mut j = 0; + (0..N_SAMPLES).map(move |i| { + let u = (i as f64) / (N_SAMPLES - 1) as f64; + while u > this_u { + last_u = this_u; + last_c = this_c; + if let Some(s) = stops.get(j + 1) { + this_u = s.offset as f64; + this_c = ColorF64::from_color(s.color); + j += 1; + } else { + break; + } + } + let du = this_u - last_u; + let c = if du < 1e-9 { + this_c + } else { + last_c.lerp(&this_c, (u - last_u) / du) + }; + c.to_premul_u32() + }) +} + +#[derive(Copy, Clone)] +struct ColorF64([f64; 4]); + +impl ColorF64 { + fn from_color(color: Color) -> Self { + Self([ + color.r as f64 / 255.0, + color.g as f64 / 255.0, + color.b as f64 / 255.0, + color.a as f64 / 255.0, + ]) + } + + fn lerp(&self, other: &Self, a: f64) -> Self { + fn l(x: f64, y: f64, a: f64) -> f64 { + x + (y - x) * a + } + Self([ + l(self.0[0], other.0[0], a), + l(self.0[1], other.0[1], a), + l(self.0[2], other.0[2], a), + l(self.0[3], other.0[3], a), + ]) + } + + fn to_premul_u32(&self) -> u32 { + let a = self.0[3].min(1.0).max(0.0); + let r = ((self.0[0] * a).min(1.0).max(0.0) / 255.0) as u32; + let g = ((self.0[1] * a).min(1.0).max(0.0) / 255.0) as u32; + let b = ((self.0[2] * a).min(1.0).max(0.0) / 255.0) as u32; + let a = (a / 255.0) as u32; + r | (g << 8) | (b << 16) | (a << 24) + } +} diff --git a/piet-scene/src/scene/blend.rs b/piet-scene/src/scene/blend.rs new file mode 100644 index 0000000..7edc6cd --- /dev/null +++ b/piet-scene/src/scene/blend.rs @@ -0,0 +1,102 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +/// Defines the color mixing function for a blend operation. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(C)] +pub enum Mix { + Normal = 0, + Multiply = 1, + Screen = 2, + Overlay = 3, + Darken = 4, + Lighten = 5, + ColorDodge = 6, + ColorBurn = 7, + HardLight = 8, + SoftLight = 9, + Difference = 10, + Exclusion = 11, + Hue = 12, + Saturation = 13, + Color = 14, + Luminosity = 15, +} + +/// Defines the layer composition function for a blend operation. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(C)] +pub enum Compose { + Clear = 0, + Copy = 1, + Dest = 2, + SrcOver = 3, + DestOver = 4, + SrcIn = 5, + DestIn = 6, + SrcOut = 7, + DestOut = 8, + SrcAtop = 9, + DestAtop = 10, + Xor = 11, + Plus = 12, + PlusDarker = 13, + PlusLighter = 14, +} + +/// Blend mode consisting of mixing and composition functions. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Blend { + pub mix: Mix, + pub compose: Compose, +} + +impl Blend { + pub fn new(mix: Mix, compose: Compose) -> Self { + Self { mix, compose } + } + + pub fn pack(&self) -> u32 { + (self.mix as u32) << 8 | self.compose as u32 + } +} + +impl Default for Blend { + fn default() -> Self { + Self { + mix: Mix::Normal, + compose: Compose::SrcOver, + } + } +} + +impl From for Blend { + fn from(mix: Mix) -> Self { + Self { + mix, + compose: Compose::SrcOver, + } + } +} + +impl From for Blend { + fn from(compose: Compose) -> Self { + Self { + mix: Mix::Normal, + compose, + } + } +} diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs new file mode 100644 index 0000000..59e0a15 --- /dev/null +++ b/piet-scene/src/scene/builder.rs @@ -0,0 +1,433 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use super::style::{Fill, Stroke}; +use super::{Affine, Blend, Element, Fragment, FragmentResources, ResourcePatch, Scene, SceneData}; +use crate::brush::*; +use crate::resource::ResourceContext; +use bytemuck::{Pod, Zeroable}; +use core::borrow::Borrow; + +/// Creates a new builder for constructing a scene. +pub fn build_scene<'a>(scene: &'a mut Scene, resources: &'a mut ResourceContext) -> Builder<'a> { + Builder::new(&mut scene.data, ResourceData::Scene(resources)) +} + +/// Creates a new builder for construction a scene fragment. +pub fn build_fragment<'a>(fragment: &'a mut Fragment) -> Builder<'a> { + Builder::new( + &mut fragment.data, + ResourceData::Fragment(&mut fragment.resources), + ) +} + +/// Builder for constructing a scene or scene fragment. +pub struct Builder<'a> { + scene: &'a mut SceneData, + resources: ResourceData<'a>, +} + +impl<'a> Builder<'a> { + /// Creates a new builder for constructing a scene. + fn new(scene: &'a mut SceneData, resources: ResourceData<'a>) -> Self { + Self { scene, resources } + } + + /// Pushes a transform matrix onto the stack. + pub fn push_transform(&mut self, transform: &Affine) {} + + /// Pops the current transform matrix. + pub fn pop_transform(&mut self) {} + + /// Pushes a new layer bound by the specifed shape and composed with + /// previous layers using the specified blend mode. + pub fn push_layer<'s, E>(&mut self, blend: Blend, elements: E) + where + E: IntoIterator, + E::IntoIter: Clone, + E::Item: Borrow, + { + let elements = elements.into_iter(); + self.encode_path(elements, true); + } + + /// Pops the current layer. + pub fn pop_layer(&mut self) {} + + /// Fills a shape using the specified style and brush. + pub fn fill<'s, E>( + &mut self, + style: Fill, + brush: &Brush, + brush_transform: Option, + elements: E, + ) where + E: IntoIterator, + E::IntoIter: Clone, + E::Item: Borrow, + { + let elements = elements.into_iter(); + self.encode_path(elements, true); + } + + /// Strokes a shape using the specified style and brush. + pub fn stroke<'s, D, E>( + &mut self, + style: &Stroke, + brush: &Brush, + brush_transform: Option, + elements: E, + ) where + D: Borrow<[f32]>, + E: IntoIterator, + E::IntoIter: Clone, + E::Item: Borrow, + { + let elements = elements.into_iter(); + self.encode_path(elements, false); + } + + /// Appends a fragment to the scene. + pub fn append(&mut self, fragment: &Fragment) { + let drawdata_base = self.scene.drawdata_stream.len(); + self.scene.append(&fragment.data); + match &mut self.resources { + ResourceData::Scene(res) => { + for patch in &fragment.resources.patches { + match patch { + ResourcePatch::Ramp { + drawdata_offset, + stops, + } => { + let stops = &fragment.resources.stops[stops.clone()]; + let ramp_id = res.add_ramp(stops); + let patch_base = *drawdata_offset + drawdata_base; + (&mut self.scene.drawdata_stream[patch_base..patch_base + 4]) + .copy_from_slice(bytemuck::bytes_of(&ramp_id)); + } + } + } + } + ResourceData::Fragment(res) => { + let stops_base = res.stops.len(); + res.stops.extend_from_slice(&fragment.resources.stops); + res.patches.extend(fragment.resources.patches.iter().map( + |pending| match pending { + ResourcePatch::Ramp { + drawdata_offset, + stops, + } => ResourcePatch::Ramp { + drawdata_offset: drawdata_offset + drawdata_base, + stops: stops.start + stops_base..stops.end + stops_base, + }, + }, + )); + } + } + } + + /// Completes construction and finalizes the underlying scene. + pub fn finish(self) {} + + fn encode_path(&mut self, elements: E, is_fill: bool) + where + E: Iterator, + E::Item: Borrow, + { + if is_fill { + self.encode_path_inner( + elements + .map(|el| *el.borrow()) + .flat_map(|el| { + match el { + Element::MoveTo(..) => Some(Element::Close), + _ => None, + } + .into_iter() + .chain(Some(el)) + }) + .chain(Some(Element::Close)), + ) + } else { + self.encode_path_inner(elements.map(|el| *el.borrow())) + } + } + + fn encode_path_inner(&mut self, elements: impl Iterator) { + let mut b = PathBuilder::new(&mut self.scene.tag_stream, &mut self.scene.pathseg_stream); + for el in elements { + match el { + Element::MoveTo(p0) => b.move_to(p0.x, p0.y), + Element::LineTo(p0) => b.line_to(p0.x, p0.y), + Element::QuadTo(p0, p1) => b.quad_to(p0.x, p0.y, p1.x, p1.y), + Element::CurveTo(p0, p1, p2) => b.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y), + Element::Close => b.close_path(), + } + } + b.path(); + let n_pathseg = b.n_pathseg(); + self.scene.n_path += 1; + self.scene.n_pathseg += n_pathseg; + } + + fn encode_brush(&mut self, brush: &Brush) { + match brush { + Brush::Solid(color) => { + self.scene.drawtag_stream.push(DRAWTAG_FILLCOLOR); + let rgba_color = color.to_premul_u32(); + self.scene + .drawdata_stream + .extend(bytemuck::bytes_of(&FillColor { rgba_color })); + } + Brush::LinearGradient(gradient) => { + let index = self.add_ramp(&gradient.stops); + self.scene.drawtag_stream.push(DRAWTAG_FILLLINGRADIENT); + self.scene + .drawdata_stream + .extend(bytemuck::bytes_of(&FillLinGradient { + index, + p0: [gradient.start.x, gradient.start.y], + p1: [gradient.end.x, gradient.end.y], + })); + } + Brush::RadialGradient(gradient) => { + let index = self.add_ramp(&gradient.stops); + self.scene.drawtag_stream.push(DRAWTAG_FILLRADGRADIENT); + self.scene + .drawdata_stream + .extend(bytemuck::bytes_of(&FillRadGradient { + index, + p0: [gradient.center0.x, gradient.center0.y], + p1: [gradient.center1.x, gradient.center1.y], + r0: gradient.radius0, + r1: gradient.radius1, + })); + } + Brush::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"), + Brush::Image(_image) => todo!("images aren't done yet!"), + Brush::Persistent(_) => todo!("persistent brushes aren't done yet!"), + } + } + + fn add_ramp(&mut self, stops: &[Stop]) -> u32 { + match &mut self.resources { + ResourceData::Scene(res) => res.add_ramp(stops), + ResourceData::Fragment(res) => { + let stops_start = res.stops.len(); + res.stops.extend_from_slice(stops); + let id = res.patches.len() as u32; + res.patches.push(ResourcePatch::Ramp { + drawdata_offset: self.scene.drawdata_stream.len(), + stops: stops_start..stops_start + stops.len(), + }); + id + } + } + } +} + +enum ResourceData<'a> { + Fragment(&'a mut FragmentResources), + Scene(&'a mut ResourceContext), +} + +// Tags for draw objects. See shader/drawtag.h for the authoritative source. +const DRAWTAG_FILLCOLOR: u32 = 0x44; +const DRAWTAG_FILLLINGRADIENT: u32 = 0x114; +const DRAWTAG_FILLRADGRADIENT: u32 = 0x2dc; +const DRAWTAG_BEGINCLIP: u32 = 0x05; +const DRAWTAG_ENDCLIP: u32 = 0x25; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +pub struct FillColor { + rgba_color: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +pub struct FillLinGradient { + index: u32, + p0: [f32; 2], + p1: [f32; 2], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +pub struct FillRadGradient { + index: u32, + p0: [f32; 2], + p1: [f32; 2], + r0: f32, + r1: f32, +} + +#[allow(unused)] +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +pub struct FillImage { + index: u32, + // [i16; 2] + offset: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)] +pub struct Clip { + blend: u32, +} + +struct PathBuilder<'a> { + tag_stream: &'a mut Vec, + // If we're never going to use the i16 encoding, it might be + // slightly faster to store this as Vec, we'd get aligned + // stores on ARM etc. + pathseg_stream: &'a mut Vec, + first_pt: [f32; 2], + state: PathState, + n_pathseg: u32, +} + +#[derive(PartialEq)] +enum PathState { + Start, + MoveTo, + NonemptySubpath, +} + +impl<'a> PathBuilder<'a> { + pub fn new(tags: &'a mut Vec, pathsegs: &'a mut Vec) -> PathBuilder<'a> { + PathBuilder { + tag_stream: tags, + pathseg_stream: pathsegs, + first_pt: [0.0, 0.0], + state: PathState::Start, + n_pathseg: 0, + } + } + + pub fn move_to(&mut self, x: f32, y: f32) { + let buf = [x, y]; + let bytes = bytemuck::bytes_of(&buf); + self.first_pt = buf; + if self.state == PathState::MoveTo { + let new_len = self.pathseg_stream.len() - 8; + self.pathseg_stream.truncate(new_len); + } + if self.state == PathState::NonemptySubpath { + if let Some(tag) = self.tag_stream.last_mut() { + *tag |= 4; + } + } + self.pathseg_stream.extend_from_slice(bytes); + self.state = PathState::MoveTo; + } + + pub fn line_to(&mut self, x: f32, y: f32) { + if self.state == PathState::Start { + // should warn or error + return; + } + let buf = [x, y]; + let bytes = bytemuck::bytes_of(&buf); + self.pathseg_stream.extend_from_slice(bytes); + self.tag_stream.push(9); + self.state = PathState::NonemptySubpath; + self.n_pathseg += 1; + } + + pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) { + if self.state == PathState::Start { + return; + } + let buf = [x1, y1, x2, y2]; + let bytes = bytemuck::bytes_of(&buf); + self.pathseg_stream.extend_from_slice(bytes); + self.tag_stream.push(10); + self.state = PathState::NonemptySubpath; + self.n_pathseg += 1; + } + + pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) { + if self.state == PathState::Start { + return; + } + let buf = [x1, y1, x2, y2, x3, y3]; + let bytes = bytemuck::bytes_of(&buf); + self.pathseg_stream.extend_from_slice(bytes); + self.tag_stream.push(11); + self.state = PathState::NonemptySubpath; + self.n_pathseg += 1; + } + + pub fn close_path(&mut self) { + match self.state { + PathState::Start => return, + PathState::MoveTo => { + let new_len = self.pathseg_stream.len() - 8; + self.pathseg_stream.truncate(new_len); + self.state = PathState::Start; + return; + } + PathState::NonemptySubpath => (), + } + let len = self.pathseg_stream.len(); + if len < 8 { + // can't happen + return; + } + let first_bytes = bytemuck::bytes_of(&self.first_pt); + if &self.pathseg_stream[len - 8..len] != first_bytes { + self.pathseg_stream.extend_from_slice(first_bytes); + self.tag_stream.push(13); + self.n_pathseg += 1; + } else { + if let Some(tag) = self.tag_stream.last_mut() { + *tag |= 4; + } + } + self.state = PathState::Start; + } + + fn finish(&mut self) { + if self.state == PathState::MoveTo { + let new_len = self.pathseg_stream.len() - 8; + self.pathseg_stream.truncate(new_len); + } + if let Some(tag) = self.tag_stream.last_mut() { + *tag |= 4; + } + } + + /// Finish encoding a path. + /// + /// Encode this after encoding path segments. + pub fn path(&mut self) { + self.finish(); + // maybe don't encode if path is empty? might throw off sync though + self.tag_stream.push(0x10); + } + + /// Get the number of path segments. + /// + /// This is the number of path segments that will be written by the + /// path stage; use this for allocating the output buffer. + /// + /// Also note: it takes `self` for lifetime reasons. + pub fn n_pathseg(self) -> u32 { + self.n_pathseg + } +} diff --git a/piet-scene/src/scene/mod.rs b/piet-scene/src/scene/mod.rs new file mode 100644 index 0000000..df9db90 --- /dev/null +++ b/piet-scene/src/scene/mod.rs @@ -0,0 +1,97 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +mod blend; +mod builder; +mod style; + +pub use blend::{Blend, Compose, Mix}; +pub use builder::{build_fragment, build_scene, Builder}; +pub use style::*; + +use super::brush::*; +use super::geometry::{Affine, Rect}; +use super::path::Element; +use core::ops::Range; + +#[derive(Default)] +struct SceneData { + transform_stream: Vec, + tag_stream: Vec, + pathseg_stream: Vec, + linewidth_stream: Vec, + drawtag_stream: Vec, + drawdata_stream: Vec, + n_path: u32, + n_pathseg: u32, + n_clip: u32, +} + +impl SceneData { + fn clear(&mut self) { + self.transform_stream.clear(); + self.tag_stream.clear(); + self.pathseg_stream.clear(); + self.linewidth_stream.clear(); + self.drawtag_stream.clear(); + self.drawdata_stream.clear(); + self.n_path = 0; + self.n_pathseg = 0; + self.n_clip = 0; + } + + fn append(&mut self, other: &SceneData) { + self.transform_stream + .extend_from_slice(&other.transform_stream); + self.tag_stream.extend_from_slice(&other.tag_stream); + self.pathseg_stream.extend_from_slice(&other.pathseg_stream); + self.linewidth_stream + .extend_from_slice(&other.linewidth_stream); + self.drawtag_stream.extend_from_slice(&other.drawtag_stream); + self.drawdata_stream + .extend_from_slice(&other.drawdata_stream); + self.n_path += other.n_path; + self.n_pathseg += other.n_pathseg; + self.n_clip += other.n_clip; + } +} + +/// Encoded definition of a scene that is ready for rendering when paired with +/// an associated resource context. +#[derive(Default)] +pub struct Scene { + data: SceneData, +} + +/// Encoded definition of a scene fragment and associated resources. +#[derive(Default)] +pub struct Fragment { + data: SceneData, + resources: FragmentResources, +} + +#[derive(Default)] +struct FragmentResources { + patches: Vec, + stops: Vec, +} + +enum ResourcePatch { + Ramp { + drawdata_offset: usize, + stops: Range, + }, +} diff --git a/piet-scene/src/scene/style.rs b/piet-scene/src/scene/style.rs new file mode 100644 index 0000000..0aded61 --- /dev/null +++ b/piet-scene/src/scene/style.rs @@ -0,0 +1,71 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use core::borrow::Borrow; + +/// Describes the winding rule that determines the interior portion of a path. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Fill { + NonZero, + EvenOdd, +} + +/// Defines the connection between two segments of a stroke. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Join { + /// A straight line connecting the segments. + Bevel, + /// The segments are extended to their natural intersection point. + Miter, + /// An arc between the segments. + Round, +} + +/// Defines the shape to be drawn at the ends of a stroke. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Cap { + /// Flat cap. + Butt, + /// Square cap with dimensions equal to half the stroke width. + Square, + /// Rounded cap with radius equal to half the stroke width. + Round, +} + +/// Describes the visual style of a stroke. +#[derive(Copy, Clone, Debug)] +pub struct Stroke +where + D: Borrow<[f32]>, +{ + /// Width of the stroke. + pub width: f32, + /// Style for connecting segments of the stroke. + pub join: Join, + /// Limit for miter joins. + pub miter_limit: f32, + /// Style for capping the beginning of an open subpath. + pub start_cap: Cap, + /// Style for capping the end of an open subpath. + pub end_cap: Cap, + /// Lengths of dashes in alternating on/off order. + pub dash_pattern: D, + /// Offset of the first dash. + pub dash_offset: f32, + /// True if the stroke width should be affected by the scale of a + /// transform. + pub scale: bool, +} From f8f91e4207835128514aaf84d8b325f16cad8a51 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 11 Apr 2022 06:05:40 -0400 Subject: [PATCH 02/13] Add layer encoding --- piet-scene/src/geometry.rs | 20 ++++ .../resource/{ramp_cache.rs => gradient.rs} | 0 piet-scene/src/resource/mod.rs | 26 ++++- piet-scene/src/scene/builder.rs | 96 ++++++++++++++++++- 4 files changed, 134 insertions(+), 8 deletions(-) rename piet-scene/src/resource/{ramp_cache.rs => gradient.rs} (100%) diff --git a/piet-scene/src/geometry.rs b/piet-scene/src/geometry.rs index a40cc40..b71c293 100644 --- a/piet-scene/src/geometry.rs +++ b/piet-scene/src/geometry.rs @@ -115,6 +115,26 @@ impl Affine { y: point.y * self.yy + point.y * self.xy + self.dy, } } + + /// Compute the determinant of this transform. + pub fn determinant(self) -> f32 { + self.xx * self.yy - self.yx * self.xy + } + + /// Compute the inverse transform. + /// + /// Produces NaN values when the determinant is zero. + pub fn inverse(self) -> Self { + let inv_det = self.determinant().recip(); + Self::new(&[ + inv_det * self.yy, + -inv_det * self.yx, + -inv_det * self.xy, + inv_det * self.xx, + inv_det * (self.xy * self.dy - self.yy * self.dx), + inv_det * (self.yx * self.dx - self.xx * self.dy), + ]) + } } impl std::ops::Mul for Affine { diff --git a/piet-scene/src/resource/ramp_cache.rs b/piet-scene/src/resource/gradient.rs similarity index 100% rename from piet-scene/src/resource/ramp_cache.rs rename to piet-scene/src/resource/gradient.rs diff --git a/piet-scene/src/resource/mod.rs b/piet-scene/src/resource/mod.rs index 36e6419..a811b99 100644 --- a/piet-scene/src/resource/mod.rs +++ b/piet-scene/src/resource/mod.rs @@ -1,12 +1,14 @@ -mod ramp_cache; +mod gradient; use crate::brush::{Brush, Stop}; -use ramp_cache::RampCache; +use gradient::RampCache; +use std::collections::HashMap; /// Context for caching resources across rendering operations. #[derive(Default)] pub struct ResourceContext { - ramp_cache: RampCache, + ramps: RampCache, + persistent_map: HashMap, } impl ResourceContext { @@ -15,14 +17,23 @@ impl ResourceContext { } pub fn advance(&mut self) { - self.ramp_cache.advance(); + self.ramps.advance(); + } + + pub fn clear(&mut self) { + self.ramps.clear(); + self.persistent_map.clear(); } pub fn add_ramp(&mut self, stops: &[Stop]) -> u32 { - self.ramp_cache.add(stops) + self.ramps.add(stops) } pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush { + match brush { + Brush::Persistent(dup) => return *dup, + _ => {} + } PersistentBrush { kind: 0, id: 0 } } @@ -35,3 +46,8 @@ pub struct PersistentBrush { kind: u8, id: u64, } + +struct PersistentBrushData { + brush: Brush, + +} diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs index 59e0a15..0f2c2a7 100644 --- a/piet-scene/src/scene/builder.rs +++ b/piet-scene/src/scene/builder.rs @@ -21,6 +21,8 @@ use crate::resource::ResourceContext; use bytemuck::{Pod, Zeroable}; use core::borrow::Borrow; +const MAX_BLEND_STACK: usize = 256; + /// Creates a new builder for constructing a scene. pub fn build_scene<'a>(scene: &'a mut Scene, resources: &'a mut ResourceContext) -> Builder<'a> { Builder::new(&mut scene.data, ResourceData::Scene(resources)) @@ -38,12 +40,15 @@ pub fn build_fragment<'a>(fragment: &'a mut Fragment) -> Builder<'a> { pub struct Builder<'a> { scene: &'a mut SceneData, resources: ResourceData<'a>, + layers: Vec, } impl<'a> Builder<'a> { /// Creates a new builder for constructing a scene. fn new(scene: &'a mut SceneData, resources: ResourceData<'a>) -> Self { - Self { scene, resources } + scene.clear(); + resources.clear(); + Self { scene, resources, layers: vec![] } } /// Pushes a transform matrix onto the stack. @@ -60,12 +65,22 @@ impl<'a> Builder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.linewidth(-1.0); let elements = elements.into_iter(); self.encode_path(elements, true); + self.begin_clip(Some(blend)); + if self.layers.len() >= MAX_BLEND_STACK { + panic!("Maximum clip/blend stack size {} exceeded", MAX_BLEND_STACK); + } + self.layers.push(blend); } /// Pops the current layer. - pub fn pop_layer(&mut self) {} + pub fn pop_layer(&mut self) { + if let Some(layer) = self.layers.pop() { + self.end_clip(Some(layer)); + } + } /// Fills a shape using the specified style and brush. pub fn fill<'s, E>( @@ -79,8 +94,17 @@ impl<'a> Builder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.linewidth(-1.0); let elements = elements.into_iter(); self.encode_path(elements, true); + if let Some(brush_transform) = brush_transform { + self.transform(brush_transform); + self.swap_last_tags(); + self.encode_brush(brush); + self.transform(brush_transform.inverse()); + } else { + self.encode_brush(brush); + } } /// Strokes a shape using the specified style and brush. @@ -96,8 +120,17 @@ impl<'a> Builder<'a> { E::IntoIter: Clone, E::Item: Borrow, { + self.linewidth(style.width); let elements = elements.into_iter(); self.encode_path(elements, false); + if let Some(brush_transform) = brush_transform { + self.transform(brush_transform); + self.swap_last_tags(); + self.encode_brush(brush); + self.transform(brush_transform.inverse()); + } else { + self.encode_brush(brush); + } } /// Appends a fragment to the scene. @@ -140,8 +173,14 @@ impl<'a> Builder<'a> { } /// Completes construction and finalizes the underlying scene. - pub fn finish(self) {} + pub fn finish(self) { + while let Some(layer) = self.layers.pop() { + self.end_clip(Some(layer)); + } + } +} +impl<'a> Builder<'a> { fn encode_path(&mut self, elements: E, is_fill: bool) where E: Iterator, @@ -183,6 +222,24 @@ impl<'a> Builder<'a> { self.scene.n_pathseg += n_pathseg; } + fn transform(&mut self, transform: Affine) { + self.scene.tag_stream.push(0x20); + self.scene.transform_stream.push(transform); + } + + // Swap the last two tags in the tag stream; used for transformed + // gradients. + fn swap_last_tags(&mut self) { + let len = self.scene.tag_stream.len(); + self.scene.tag_stream.swap(len - 1, len - 2); + } + + // -1.0 means "fill" + fn linewidth(&mut self, linewidth: f32) { + self.scene.tag_stream.push(0x40); + self.scene.linewidth_stream.push(linewidth); + } + fn encode_brush(&mut self, brush: &Brush) { match brush { Brush::Solid(color) => { @@ -237,6 +294,28 @@ impl<'a> Builder<'a> { } } } + + /// Start a clip. + fn begin_clip(&mut self, blend: Option) { + self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP); + let element = Clip { + blend: blend.unwrap_or(Blend::default()).pack(), + }; + self.scene.drawdata_stream.extend(bytemuck::bytes_of(&element)); + self.scene.n_clip += 1; + } + + fn end_clip(&mut self, blend: Option) { + self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP); + let element = Clip { + blend: blend.unwrap_or(Blend::default()).pack(), + }; + self.scene.drawdata_stream.extend(bytemuck::bytes_of(&element)); + // This is a dummy path, and will go away with the new clip impl. + self.scene.tag_stream.push(0x10); + self.scene.n_path += 1; + self.scene.n_clip += 1; + } } enum ResourceData<'a> { @@ -244,6 +323,17 @@ enum ResourceData<'a> { Scene(&'a mut ResourceContext), } +impl ResourceData<'_> { + fn clear(&mut self) { + match self { + Self::Fragment(res) { + res.patches.clear(); + res.stops.clear(); + } + } + } +} + // Tags for draw objects. See shader/drawtag.h for the authoritative source. const DRAWTAG_FILLCOLOR: u32 = 0x44; const DRAWTAG_FILLLINGRADIENT: u32 = 0x114; From db2c4d21c9818e7f3596b7fc8573d3db0932745b Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 11 Apr 2022 06:14:58 -0400 Subject: [PATCH 03/13] Remove unused path representation --- piet-scene/src/path.rs | 248 -------------------------------- piet-scene/src/resource/mod.rs | 1 - piet-scene/src/scene/builder.rs | 21 ++- 3 files changed, 15 insertions(+), 255 deletions(-) diff --git a/piet-scene/src/path.rs b/piet-scene/src/path.rs index ffbce3e..3ad0ac1 100644 --- a/piet-scene/src/path.rs +++ b/piet-scene/src/path.rs @@ -49,254 +49,6 @@ impl Element { } } -/// Encoded collection of path elements. -#[derive(Clone, Default, Debug)] -pub struct Path { - tag_stream: Vec, - pathseg_stream: Vec, - n_path: u32, - n_pathseg: u32, -} - -impl Path { - pub fn new() -> Self { - Self::default() - } - - pub fn elements(&self) -> Elements { - Elements::new(&self) - } -} - -#[derive(Clone)] -pub struct Elements<'a> { - tag_stream: &'a [u8], - points: &'a [[f32; 2]], - tag_ix: usize, - point_ix: usize, - next_element: Option, - close: bool, -} - -impl<'a> Elements<'a> { - fn new(path: &'a Path) -> Self { - let points: &'a [[f32; 2]] = bytemuck::cast_slice(&path.pathseg_stream); - let (point_ix, next_element) = match points.get(0) { - Some(&point) => (1, Some(Element::MoveTo(point.into()))), - None => (0, None), - }; - Self { - tag_stream: &path.tag_stream, - points, - tag_ix: 0, - point_ix, - next_element, - close: false, - } - } -} - -impl<'a> Iterator for Elements<'a> { - type Item = Element; - - fn next(&mut self) -> Option { - // println!("n_points: {}", self.points.len()); - // println!("tag_ix: {}, point_ix: {}, el: {:?}, close: {}", self.tag_ix, self.point_ix, self.next_element, self.close); - if self.close { - self.close = false; - return Some(Element::Close); - } - if let Some(next_el) = self.next_element.take() { - return Some(next_el); - } - let tag = *self.tag_stream.get(self.tag_ix)?; - self.tag_ix += 1; - let end = tag & 4 != 0; - let el = match tag & 3 { - 1 => { - let p0 = *self.points.get(self.point_ix)?; - self.point_ix += 1; - Element::LineTo(p0.into()) - } - 2 => { - let p0 = *self.points.get(self.point_ix)?; - let p1 = *self.points.get(self.point_ix + 1)?; - self.point_ix += 2; - Element::QuadTo(p0.into(), p1.into()) - } - 3 => { - let p0 = *self.points.get(self.point_ix)?; - let p1 = *self.points.get(self.point_ix + 1)?; - let p2 = *self.points.get(self.point_ix + 2)?; - self.point_ix += 3; - Element::CurveTo(p0.into(), p1.into(), p2.into()) - } - _ => return None, - }; - if end { - // println!("END!"); - if let Some(&p0) = self.points.get(self.point_ix) { - self.point_ix += 1; - self.next_element = Some(Element::MoveTo(p0.into())); - } - self.close = tag & 0x80 != 0; - } - Some(el) - } -} - -pub struct PathBuilder<'a> { - tag_stream: &'a mut Vec, - // If we're never going to use the i16 encoding, it might be - // slightly faster to store this as Vec, we'd get aligned - // stores on ARM etc. - pathseg_stream: &'a mut Vec, - first_pt: [f32; 2], - state: State, - n_pathseg: u32, -} - -#[derive(PartialEq)] -enum State { - Start, - MoveTo, - NonemptySubpath, -} - -impl<'a> PathBuilder<'a> { - pub fn new(path: &'a mut Path) -> Self { - Self { - tag_stream: &mut path.tag_stream, - pathseg_stream: &mut path.pathseg_stream, - first_pt: [0.0, 0.0], - state: State::Start, - n_pathseg: 0, - } - } - - fn new_inner(tags: &'a mut Vec, pathsegs: &'a mut Vec) -> PathBuilder<'a> { - PathBuilder { - tag_stream: tags, - pathseg_stream: pathsegs, - first_pt: [0.0, 0.0], - state: State::Start, - n_pathseg: 0, - } - } - - pub fn move_to(&mut self, x: f32, y: f32) { - let buf = [x, y]; - let bytes = bytemuck::bytes_of(&buf); - self.first_pt = buf; - if self.state == State::MoveTo { - let new_len = self.pathseg_stream.len() - 8; - self.pathseg_stream.truncate(new_len); - } - if self.state == State::NonemptySubpath { - if let Some(tag) = self.tag_stream.last_mut() { - *tag |= 4; - } - } - self.pathseg_stream.extend_from_slice(bytes); - self.state = State::MoveTo; - } - - pub fn line_to(&mut self, x: f32, y: f32) { - if self.state == State::Start { - // should warn or error - return; - } - let buf = [x, y]; - let bytes = bytemuck::bytes_of(&buf); - self.pathseg_stream.extend_from_slice(bytes); - self.tag_stream.push(9); - self.state = State::NonemptySubpath; - self.n_pathseg += 1; - } - - pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) { - if self.state == State::Start { - return; - } - let buf = [x1, y1, x2, y2]; - let bytes = bytemuck::bytes_of(&buf); - self.pathseg_stream.extend_from_slice(bytes); - self.tag_stream.push(10); - self.state = State::NonemptySubpath; - self.n_pathseg += 1; - } - - pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) { - if self.state == State::Start { - return; - } - let buf = [x1, y1, x2, y2, x3, y3]; - let bytes = bytemuck::bytes_of(&buf); - self.pathseg_stream.extend_from_slice(bytes); - self.tag_stream.push(11); - self.state = State::NonemptySubpath; - self.n_pathseg += 1; - } - - pub fn close_path(&mut self) { - match self.state { - State::Start => return, - State::MoveTo => { - let new_len = self.pathseg_stream.len() - 8; - self.pathseg_stream.truncate(new_len); - self.state = State::Start; - return; - } - State::NonemptySubpath => (), - } - let len = self.pathseg_stream.len(); - if len < 8 { - // can't happen - return; - } - let first_bytes = bytemuck::bytes_of(&self.first_pt); - if &self.pathseg_stream[len - 8..len] != first_bytes { - self.pathseg_stream.extend_from_slice(first_bytes); - self.tag_stream.push(0x80 | 13); - self.n_pathseg += 1; - } else { - if let Some(tag) = self.tag_stream.last_mut() { - *tag |= 0x80 | 4; - } - } - self.state = State::Start; - } - - fn finish(&mut self) { - if self.state == State::MoveTo { - let new_len = self.pathseg_stream.len() - 8; - self.pathseg_stream.truncate(new_len); - } - if let Some(tag) = self.tag_stream.last_mut() { - *tag |= 4; - } - } - - /// Finish encoding a path. - /// - /// Encode this after encoding path segments. - pub fn path(&mut self) { - self.finish(); - // maybe don't encode if path is empty? might throw off sync though - self.tag_stream.push(0x10); - } - - /// Get the number of path segments. - /// - /// This is the number of path segments that will be written by the - /// path stage; use this for allocating the output buffer. - /// - /// Also note: it takes `self` for lifetime reasons. - pub fn n_pathseg(self) -> u32 { - self.n_pathseg - } -} - impl Rect { pub fn elements(&self) -> impl Iterator + Clone { let elements = [ diff --git a/piet-scene/src/resource/mod.rs b/piet-scene/src/resource/mod.rs index a811b99..1dfaa60 100644 --- a/piet-scene/src/resource/mod.rs +++ b/piet-scene/src/resource/mod.rs @@ -49,5 +49,4 @@ pub struct PersistentBrush { struct PersistentBrushData { brush: Brush, - } diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs index 0f2c2a7..a92267b 100644 --- a/piet-scene/src/scene/builder.rs +++ b/piet-scene/src/scene/builder.rs @@ -45,10 +45,14 @@ pub struct Builder<'a> { impl<'a> Builder<'a> { /// Creates a new builder for constructing a scene. - fn new(scene: &'a mut SceneData, resources: ResourceData<'a>) -> Self { + fn new(scene: &'a mut SceneData, mut resources: ResourceData<'a>) -> Self { scene.clear(); resources.clear(); - Self { scene, resources, layers: vec![] } + Self { + scene, + resources, + layers: vec![], + } } /// Pushes a transform matrix onto the stack. @@ -173,7 +177,7 @@ impl<'a> Builder<'a> { } /// Completes construction and finalizes the underlying scene. - pub fn finish(self) { + pub fn finish(mut self) { while let Some(layer) = self.layers.pop() { self.end_clip(Some(layer)); } @@ -301,7 +305,9 @@ impl<'a> Builder<'a> { let element = Clip { blend: blend.unwrap_or(Blend::default()).pack(), }; - self.scene.drawdata_stream.extend(bytemuck::bytes_of(&element)); + self.scene + .drawdata_stream + .extend(bytemuck::bytes_of(&element)); self.scene.n_clip += 1; } @@ -310,7 +316,9 @@ impl<'a> Builder<'a> { let element = Clip { blend: blend.unwrap_or(Blend::default()).pack(), }; - self.scene.drawdata_stream.extend(bytemuck::bytes_of(&element)); + self.scene + .drawdata_stream + .extend(bytemuck::bytes_of(&element)); // This is a dummy path, and will go away with the new clip impl. self.scene.tag_stream.push(0x10); self.scene.n_path += 1; @@ -326,10 +334,11 @@ enum ResourceData<'a> { impl ResourceData<'_> { fn clear(&mut self) { match self { - Self::Fragment(res) { + Self::Fragment(res) => { res.patches.clear(); res.stops.clear(); } + _ => {} } } } From d243d38b04a8e7f622cb8927fe108dcf354fdeb7 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 18 Apr 2022 03:48:10 -0400 Subject: [PATCH 04/13] render using the new scene API This commit implements actual rendering for the new piet-scene crate. In particular, it adds a new EncodedSceneRef type in piet_gpu::encoder to store references to the raw stream data. This is used as a proxy for both PietGpuRenderContext and piet_scene::scene::Scene to feed data into the renderer. Much of this is still hacky for expedience and because the intention is to be a transitional state that maintains both interfaces until we can move to the new scene API and add a wrapper for the traditional piet API. The general structure and relationships between these crates need further discussion. --- Cargo.lock | 8 + piet-gpu/src/encoder.rs | 156 ++++++++++ piet-gpu/src/lib.rs | 39 ++- piet-gpu/src/render_ctx.rs | 6 +- piet-scene/Cargo.toml | 10 + piet-scene/src/brush/color.rs | 2 +- piet-scene/src/geometry.rs | 18 +- piet-scene/src/main.rs | 450 ++++++++++++++++++++++++++-- piet-scene/src/resource/gradient.rs | 12 +- piet-scene/src/resource/mod.rs | 4 + piet-scene/src/scene/builder.rs | 28 +- piet-scene/src/scene/mod.rs | 33 +- 12 files changed, 707 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78b6326..207dc02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -954,7 +954,15 @@ name = "piet-scene" version = "0.1.0" dependencies = [ "bytemuck", + "clap", + "piet", + "piet-gpu", + "piet-gpu-hal", + "png", + "rand", + "roxmltree", "smallvec", + "winit", ] [[package]] diff --git a/piet-gpu/src/encoder.rs b/piet-gpu/src/encoder.rs index 2f4b85e..8f21485 100644 --- a/piet-gpu/src/encoder.rs +++ b/piet-gpu/src/encoder.rs @@ -37,6 +37,147 @@ pub struct Encoder { n_clip: u32, } +#[derive(Copy, Clone, Debug)] +pub struct EncodedSceneRef<'a, T: Copy + Pod> { + pub transform_stream: &'a [T], + pub tag_stream: &'a [u8], + pub pathseg_stream: &'a [u8], + pub linewidth_stream: &'a [f32], + pub drawtag_stream: &'a [u32], + pub drawdata_stream: &'a [u8], + pub n_path: u32, + pub n_pathseg: u32, + pub n_clip: u32, + pub ramp_data: &'a [u32], +} + +impl<'a, T: Copy + Pod> EncodedSceneRef<'a, T> { + /// Return a config for the element processing pipeline. + /// + /// This does not include further pipeline processing. Also returns the + /// beginning of free memory. + pub fn stage_config(&self) -> (Config, usize) { + // Layout of scene buffer + let drawtag_offset = 0; + let n_drawobj = self.n_drawobj(); + let n_drawobj_padded = align_up(n_drawobj, DRAW_PART_SIZE as usize); + let drawdata_offset = drawtag_offset + n_drawobj_padded * DRAWTAG_SIZE; + let trans_offset = drawdata_offset + self.drawdata_stream.len(); + let n_trans = self.transform_stream.len(); + let n_trans_padded = align_up(n_trans, TRANSFORM_PART_SIZE as usize); + let linewidth_offset = trans_offset + n_trans_padded * TRANSFORM_SIZE; + let n_linewidth = self.linewidth_stream.len(); + let pathtag_offset = linewidth_offset + n_linewidth * LINEWIDTH_SIZE; + let n_pathtag = self.tag_stream.len(); + let n_pathtag_padded = align_up(n_pathtag, PATHSEG_PART_SIZE as usize); + let pathseg_offset = pathtag_offset + n_pathtag_padded; + + // Layout of memory + let mut alloc = 0; + let trans_alloc = alloc; + alloc += trans_alloc + n_trans_padded * TRANSFORM_SIZE; + let pathseg_alloc = alloc; + alloc += pathseg_alloc + self.n_pathseg as usize * PATHSEG_SIZE; + let path_bbox_alloc = alloc; + let n_path = self.n_path as usize; + alloc += path_bbox_alloc + n_path * PATH_BBOX_SIZE; + let drawmonoid_alloc = alloc; + alloc += n_drawobj_padded * DRAWMONOID_SIZE; + let anno_alloc = alloc; + alloc += n_drawobj * ANNOTATED_SIZE; + let clip_alloc = alloc; + let n_clip = self.n_clip as usize; + const CLIP_SIZE: usize = 4; + alloc += n_clip * CLIP_SIZE; + let clip_bic_alloc = alloc; + const CLIP_BIC_SIZE: usize = 8; + // This can round down, as we only reduce the prefix + alloc += (n_clip / CLIP_PART_SIZE as usize) * CLIP_BIC_SIZE; + let clip_stack_alloc = alloc; + const CLIP_EL_SIZE: usize = 20; + alloc += n_clip * CLIP_EL_SIZE; + let clip_bbox_alloc = alloc; + const CLIP_BBOX_SIZE: usize = 16; + alloc += align_up(n_clip as usize, CLIP_PART_SIZE as usize) * CLIP_BBOX_SIZE; + let draw_bbox_alloc = alloc; + alloc += n_drawobj * DRAW_BBOX_SIZE; + let drawinfo_alloc = alloc; + // TODO: not optimized; it can be accumulated during encoding or summed from drawtags + const MAX_DRAWINFO_SIZE: usize = 44; + alloc += n_drawobj * MAX_DRAWINFO_SIZE; + + let config = Config { + n_elements: n_drawobj as u32, + n_pathseg: self.n_pathseg, + pathseg_alloc: pathseg_alloc as u32, + anno_alloc: anno_alloc as u32, + trans_alloc: trans_alloc as u32, + path_bbox_alloc: path_bbox_alloc as u32, + drawmonoid_alloc: drawmonoid_alloc as u32, + clip_alloc: clip_alloc as u32, + clip_bic_alloc: clip_bic_alloc as u32, + clip_stack_alloc: clip_stack_alloc as u32, + clip_bbox_alloc: clip_bbox_alloc as u32, + draw_bbox_alloc: draw_bbox_alloc as u32, + drawinfo_alloc: drawinfo_alloc as u32, + n_trans: n_trans as u32, + n_path: self.n_path, + n_clip: self.n_clip, + trans_offset: trans_offset as u32, + linewidth_offset: linewidth_offset as u32, + pathtag_offset: pathtag_offset as u32, + pathseg_offset: pathseg_offset as u32, + drawtag_offset: drawtag_offset as u32, + drawdata_offset: drawdata_offset as u32, + ..Default::default() + }; + (config, alloc) + } + + pub fn write_scene(&self, buf: &mut BufWrite) { + buf.extend_slice(&self.drawtag_stream); + let n_drawobj = self.drawtag_stream.len(); + buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE); + buf.extend_slice(&self.drawdata_stream); + buf.extend_slice(&self.transform_stream); + let n_trans = self.transform_stream.len(); + buf.fill_zero(padding(n_trans, TRANSFORM_PART_SIZE as usize) * TRANSFORM_SIZE); + buf.extend_slice(&self.linewidth_stream); + buf.extend_slice(&self.tag_stream); + let n_pathtag = self.tag_stream.len(); + buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize)); + buf.extend_slice(&self.pathseg_stream); + } + + /// The number of draw objects in the draw object stream. + pub(crate) fn n_drawobj(&self) -> usize { + self.drawtag_stream.len() + } + + /// The number of paths. + pub(crate) fn n_path(&self) -> u32 { + self.n_path + } + + /// The number of path segments. + pub(crate) fn n_pathseg(&self) -> u32 { + self.n_pathseg + } + + pub(crate) fn n_transform(&self) -> usize { + self.transform_stream.len() + } + + /// The number of tags in the path stream. + pub(crate) fn n_pathtag(&self) -> usize { + self.tag_stream.len() + } + + pub(crate) fn n_clip(&self) -> u32 { + self.n_clip + } +} + /// A scene fragment encoding a glyph. /// /// This is a reduced version of the full encoder. @@ -330,6 +471,21 @@ impl Encoder { self.n_path += glyph.n_path; self.n_pathseg += glyph.n_pathseg; } + + pub(crate) fn scene_ref(&self) -> EncodedSceneRef { + EncodedSceneRef { + transform_stream: &self.transform_stream, + tag_stream: &self.tag_stream, + pathseg_stream: &self.pathseg_stream, + linewidth_stream: &self.linewidth_stream, + drawtag_stream: &self.drawtag_stream, + drawdata_stream: &self.drawdata_stream, + n_path: self.n_path, + n_pathseg: self.n_pathseg, + n_clip: self.n_clip, + ramp_data: &[], + } + } } fn align_up(x: usize, align: usize) -> usize { diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 475d723..b3ead90 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -10,7 +10,10 @@ mod text; use std::convert::TryInto; +use bytemuck::Pod; + pub use blend::{Blend, BlendMode, CompositionMode}; +pub use encoder::EncodedSceneRef; pub use render_ctx::PietGpuRenderContext; pub use gradient::Colrv1RadialGradient; @@ -355,16 +358,27 @@ impl Renderer { render_ctx: &mut PietGpuRenderContext, buf_ix: usize, ) -> Result<(), Error> { - let (mut config, mut alloc) = render_ctx.stage_config(); - let n_drawobj = render_ctx.n_drawobj(); + let mut scene = render_ctx.encoded_scene(); + let ramp_data = render_ctx.get_ramp_data(); + scene.ramp_data = &ramp_data; + self.upload_scene(&scene, buf_ix) + } + + pub fn upload_scene( + &mut self, + scene: &EncodedSceneRef, + buf_ix: usize, + ) -> Result<(), Error> { + let (mut config, mut alloc) = scene.stage_config(); + let n_drawobj = scene.n_drawobj(); // TODO: be more consistent in size types - let n_path = render_ctx.n_path() as usize; + let n_path = scene.n_path() as usize; self.n_paths = n_path; - self.n_transform = render_ctx.n_transform(); - self.n_drawobj = render_ctx.n_drawobj(); - self.n_pathseg = render_ctx.n_pathseg() as usize; - self.n_pathtag = render_ctx.n_pathtag(); - self.n_clip = render_ctx.n_clip(); + self.n_transform = scene.n_transform(); + self.n_drawobj = scene.n_drawobj(); + self.n_pathseg = scene.n_pathseg() as usize; + self.n_pathtag = scene.n_pathtag(); + self.n_clip = scene.n_clip(); // These constants depend on encoding and may need to be updated. // Perhaps we can plumb these from piet-gpu-derive? @@ -388,19 +402,18 @@ impl Renderer { // TODO: reallocate scene buffer if size is inadequate { let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?; - render_ctx.write_scene(&mut mapped_scene); + scene.write_scene(&mut mapped_scene); } self.config_bufs[buf_ix].write(&[config])?; self.memory_buf_host[buf_ix].write(&[alloc as u32, 0 /* Overflow flag */])?; // Upload gradient data. - let ramp_data = render_ctx.get_ramp_data(); - if !ramp_data.is_empty() { + if !scene.ramp_data.is_empty() { assert!( self.gradient_bufs[buf_ix].size() as usize - >= std::mem::size_of_val(&*ramp_data) + >= std::mem::size_of_val(&*scene.ramp_data) ); - self.gradient_bufs[buf_ix].write(&ramp_data)?; + self.gradient_bufs[buf_ix].write(scene.ramp_data)?; } } Ok(()) diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index dca03eb..ad608ca 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::encoder::GlyphEncoder; +use crate::encoder::{EncodedSceneRef, GlyphEncoder}; use crate::stages::{Config, Transform}; use crate::MAX_BLEND_STACK; use piet::kurbo::{Affine, Insets, PathEl, Point, Rect, Shape}; @@ -97,6 +97,10 @@ impl PietGpuRenderContext { self.new_encoder.stage_config() } + pub fn encoded_scene(&self) -> EncodedSceneRef { + self.new_encoder.scene_ref() + } + /// Number of draw objects. /// /// This is for the new element processing pipeline. It's not necessarily the diff --git a/piet-scene/Cargo.toml b/piet-scene/Cargo.toml index 8706119..fd9a6c5 100644 --- a/piet-scene/Cargo.toml +++ b/piet-scene/Cargo.toml @@ -7,3 +7,13 @@ edition = "2021" [dependencies] bytemuck = { version = "1.7.2", features = ["derive"] } smallvec = "1.8.0" + +# remove these and move demo to another directory +piet-gpu = { path = "../piet-gpu" } +piet-gpu-hal = { path = "../piet-gpu-hal" } +winit = "0.25" +piet = "0.2.0" +png = "0.16.2" +rand = "0.7.3" +roxmltree = "0.13" +clap = "2.33" diff --git a/piet-scene/src/brush/color.rs b/piet-scene/src/brush/color.rs index d079210..b9cbe52 100644 --- a/piet-scene/src/brush/color.rs +++ b/piet-scene/src/brush/color.rs @@ -57,6 +57,6 @@ impl Color { let r = (self.r as f64 * a) as u32; let g = (self.g as f64 * a) as u32; let b = (self.b as f64 * a) as u32; - r | (g << 8) | (b << 16) | ((self.a as u32) << 24) + (r << 24) | (g << 16) | (b << 8) | self.a as u32 } } diff --git a/piet-scene/src/geometry.rs b/piet-scene/src/geometry.rs index b71c293..fbc8765 100644 --- a/piet-scene/src/geometry.rs +++ b/piet-scene/src/geometry.rs @@ -14,6 +14,7 @@ // // Also licensed under MIT license, at your choice. +use bytemuck::{Pod, Zeroable}; use core::borrow::Borrow; use core::hash::{Hash, Hasher}; @@ -58,7 +59,7 @@ impl From<(f32, f32)> for Point { } /// Affine transformation matrix. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] #[repr(C)] pub struct Affine { pub xx: f32, @@ -70,6 +71,15 @@ pub struct Affine { } impl Affine { + pub const IDENTITY: Self = Self { + xx: 1.0, + yx: 0.0, + xy: 0.0, + yy: 1.0, + dx: 0.0, + dy: 0.0, + }; + pub const fn new(elements: &[f32; 6]) -> Self { Self { xx: elements[0], @@ -137,6 +147,12 @@ impl Affine { } } +impl Default for Affine { + fn default() -> Self { + Self::IDENTITY + } +} + impl std::ops::Mul for Affine { type Output = Self; fn mul(self, other: Self) -> Self { diff --git a/piet-scene/src/main.rs b/piet-scene/src/main.rs index 8fd361a..ce62b43 100644 --- a/piet-scene/src/main.rs +++ b/piet-scene/src/main.rs @@ -1,30 +1,434 @@ -use piet_scene::geometry::*; -use piet_scene::path::*; -use piet_scene::scene::*; -use piet_scene::{geometry::*, path::*, resource::ResourceContext, scene::*}; +use piet::kurbo::Point; +use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; +use piet_gpu_hal::{CmdBuf, Error, ImageLayout, Instance, Session, SubmittedCmdBuf}; + +use piet_gpu::{test_scenes, EncodedSceneRef, PietGpuRenderContext, Renderer}; + +use piet_scene::resource::ResourceContext; +use piet_scene::scene::{build_scene, Scene}; + +use clap::{App, Arg}; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; + +const NUM_FRAMES: usize = 2; + +const WIDTH: usize = 2048; +const HEIGHT: usize = 1536; + +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 { + width: (WIDTH / 2) as f64, + height: (HEIGHT / 2) as f64, + }) + .with_resizable(false) // currently not supported + .build(&event_loop)?; + + let (instance, surface) = Instance::new(Some(&window), Default::default())?; + let mut info_string = "info".to_string(); -fn main() { let mut scene = Scene::default(); let mut rcx = ResourceContext::new(); - let mut sb = build_scene(&mut scene, &mut rcx); + unsafe { + let device = instance.device(surface.as_ref())?; + let mut swapchain = + instance.swapchain(WIDTH / 2, HEIGHT / 2, &device, surface.as_ref().unwrap())?; + let session = Session::new(device); - sb.push_layer(Blend::default(), Rect::default().elements()); + let mut current_frame = 0; + let present_semaphores = (0..NUM_FRAMES) + .map(|_| session.create_semaphore()) + .collect::, Error>>()?; + let query_pools = (0..NUM_FRAMES) + .map(|_| session.create_query_pool(8)) + .collect::, Error>>()?; + let mut cmd_bufs: [Option; NUM_FRAMES] = Default::default(); + let mut submitted: [Option; NUM_FRAMES] = Default::default(); - let mut path = Path::new(); - let mut b = PathBuilder::new(&mut path); - b.move_to(100., 100.); - b.line_to(200., 200.); - b.close_path(); - b.move_to(50., 50.); - b.line_to(600., 150.); - b.move_to(4., 2.); - b.quad_to(8., 8., 9., 9.); - b.close_path(); - println!("{:?}", path); - for el in path.elements() { - println!("{:?}", el); + let mut renderer = Renderer::new(&session, WIDTH, HEIGHT, NUM_FRAMES)?; + let mut mode = 0usize; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event + + match event { + Event::WindowEvent { event, window_id } if window_id == window.id() => { + use winit::event::{ElementState, VirtualKeyCode}; + match event { + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + WindowEvent::KeyboardInput { input, .. } => { + if input.state == ElementState::Pressed { + match input.virtual_keycode { + Some(VirtualKeyCode::Left) => mode = mode.wrapping_sub(1), + Some(VirtualKeyCode::Right) => mode = mode.wrapping_add(1), + _ => {} + } + } + } + _ => (), + } + } + Event::MainEventsCleared => { + window.request_redraw(); + } + Event::RedrawRequested(window_id) if window_id == window.id() => { + let frame_idx = current_frame % NUM_FRAMES; + + if let Some(submitted) = submitted[frame_idx].take() { + cmd_bufs[frame_idx] = submitted.wait().unwrap(); + let ts = session.fetch_query_pool(&query_pools[frame_idx]).unwrap(); + if !ts.is_empty() { + info_string = format!( + "{:.3}ms :: e:{:.3}ms|alloc:{:.3}ms|cp:{:.3}ms|bd:{:.3}ms|bin:{:.3}ms|cr:{:.3}ms|r:{:.3}ms", + ts[6] * 1e3, + ts[0] * 1e3, + (ts[1] - ts[0]) * 1e3, + (ts[2] - ts[1]) * 1e3, + (ts[3] - ts[2]) * 1e3, + (ts[4] - ts[3]) * 1e3, + (ts[5] - ts[4]) * 1e3, + (ts[6] - ts[5]) * 1e3, + ); + } + } + + let mut ctx = PietGpuRenderContext::new(); + 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; + } + test_scenes::render_svg(&mut ctx, input, scale); + } else { + use piet_gpu::{Blend, BlendMode::*, CompositionMode::*}; + let blends = [ + Blend::new(Normal, SrcOver), + Blend::new(Multiply, SrcOver), + Blend::new(Screen, SrcOver), + Blend::new(Overlay, SrcOver), + Blend::new(Darken, SrcOver), + Blend::new(Lighten, SrcOver), + Blend::new(ColorDodge, SrcOver), + Blend::new(ColorBurn, SrcOver), + Blend::new(HardLight, SrcOver), + Blend::new(SoftLight, SrcOver), + Blend::new(Difference, SrcOver), + Blend::new(Exclusion, SrcOver), + Blend::new(Hue, SrcOver), + Blend::new(Saturation, SrcOver), + Blend::new(Color, SrcOver), + Blend::new(Luminosity, SrcOver), + Blend::new(Normal, Clear), + Blend::new(Normal, Copy), + Blend::new(Normal, Dest), + Blend::new(Normal, SrcOver), + Blend::new(Normal, DestOver), + Blend::new(Normal, SrcIn), + Blend::new(Normal, DestIn), + Blend::new(Normal, SrcOut), + Blend::new(Normal, DestOut), + Blend::new(Normal, SrcAtop), + Blend::new(Normal, DestAtop), + Blend::new(Normal, Xor), + Blend::new(Normal, Plus), + ]; + let blend = blends[mode % blends.len()]; + test_scenes::render_blend_test(&mut ctx, current_frame, blend); + info_string = format!("{:?}", blend); + } + render_info_string(&mut ctx, &info_string); + + ctx = PietGpuRenderContext::new(); + test_scene1_old(&mut ctx); + let mut encoded_scene_old = ctx.encoded_scene(); + let ramp_data = ctx.get_ramp_data(); + encoded_scene_old.ramp_data = &ramp_data; + test_scene1(&mut scene, &mut rcx); + let encoded_scene = scene_to_encoded_scene(&scene, &rcx); + // println!("{:?}\n============\n{:?}", encoded_scene_old, encoded_scene); + // panic!(); + let res = if mode & 1 == 0 { + render_info_string(&mut ctx, &info_string); + renderer.upload_render_ctx(&mut ctx, frame_idx) + } else { + renderer.upload_scene(&encoded_scene, frame_idx) + }; + if let Err(e) = res { + println!("error in uploading: {}", e); + } + + let (image_idx, acquisition_semaphore) = swapchain.next().unwrap(); + let swap_image = swapchain.image(image_idx); + let query_pool = &query_pools[frame_idx]; + let mut cmd_buf = cmd_bufs[frame_idx].take().unwrap_or_else(|| session.cmd_buf().unwrap()); + cmd_buf.begin(); + renderer.record(&mut cmd_buf, &query_pool, frame_idx); + + // Image -> Swapchain + cmd_buf.image_barrier( + &swap_image, + ImageLayout::Undefined, + ImageLayout::BlitDst, + ); + cmd_buf.blit_image(&renderer.image_dev, &swap_image); + cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); + cmd_buf.finish(); + + submitted[frame_idx] = Some(session + .run_cmd_buf( + cmd_buf, + &[&acquisition_semaphore], + &[&present_semaphores[frame_idx]], + ) + .unwrap()); + + swapchain + .present(image_idx, &[&present_semaphores[frame_idx]]) + .unwrap(); + + current_frame += 1; + } + Event::LoopDestroyed => { + for cmd_buf in &mut submitted { + // Wait for command list submission, otherwise dropping of renderer may + // cause validation errors (and possibly crashes). + if let Some(cmd_buf) = cmd_buf.take() { + cmd_buf.wait().unwrap(); + } + } + } + _ => (), + } + }) } - //sb.push_layer(path.elements(), BlendMode::default()); - - sb.push_layer(Blend::default(), [Element::MoveTo((0., 0.).into())]); } + +fn test_scene1_old(ctx: &mut PietGpuRenderContext) { + use piet::kurbo::{Affine, Rect, Vec2}; + use piet::{Color, GradientStop}; + ctx.transform(Affine::translate(Vec2::new(200., 200.)) * Affine::rotate(45f64.to_radians())); + let linear = ctx + .gradient(piet::FixedGradient::Linear(piet::FixedLinearGradient { + start: Point::new(0., 0.), + end: Point::new(200., 100.), + stops: vec![ + GradientStop { + pos: 0.0, + color: Color::rgb8(0, 0, 255), + }, + GradientStop { + pos: 0.5, + color: Color::rgb8(0, 255, 0), + }, + GradientStop { + pos: 1.0, + color: Color::rgb8(255, 0, 0), + }, + ], + })) + .unwrap(); + let radial = ctx + .gradient(piet::FixedGradient::Radial(piet::FixedRadialGradient { + center: Point::new(50., 50.), + origin_offset: Vec2::new(0., 0.), + radius: 240., + stops: vec![ + GradientStop { + pos: 0.0, + color: Color::rgb8(0, 0, 255), + }, + GradientStop { + pos: 0.5, + color: Color::rgb8(0, 255, 0), + }, + GradientStop { + pos: 1.0, + color: Color::rgb8(255, 0, 0), + }, + ], + })) + .unwrap(); + ctx.fill_transform( + Rect { + x0: 0., + y0: 0., + x1: 200., + y1: 100., + }, + // &piet::PaintBrush::Color(piet::Color::rgb8(0, 255, 0)), + &radial, + // &piet::FixedGradient::Linear(piet::FixedLinearGradient { + // start: Point::new(0., 0.), + // end: Point::new(200., 100.), + // stops: vec![ + // GradientStop { + // pos: 0.0, + // color: Color::rgb8(0, 0, 255) + // }, + // GradientStop { + // pos: 0.5, + // color: Color::rgb8(0, 255, 0) + // }, + // GradientStop { + // pos: 1.0, + // color: Color::rgb8(255, 0, 0) + // }, + // ], + // }), + Affine::default(), // rotate(90f64.to_radians()), + ); +} + +fn test_scene1(scene: &mut Scene, rcx: &mut ResourceContext) { + use piet_scene::brush::*; + use piet_scene::geometry::{Affine, Point, Rect}; + use piet_scene::scene::*; + let mut fragment = Fragment::default(); + let mut b = build_fragment(&mut fragment); + let linear = Brush::LinearGradient(LinearGradient { + space: Space::Global, + start: Point::new(0., 0.), + end: Point::new(200., 100.), + extend: Extend::Pad, + stops: (&[ + Stop { + offset: 0., + color: Color::rgb8(0, 0, 255), + }, + Stop { + offset: 0.5, + color: Color::rgb8(0, 255, 0), + }, + Stop { + offset: 1., + color: Color::rgb8(255, 0, 0), + }, + ][..]) + .into(), + }); + let radial = Brush::RadialGradient(RadialGradient { + space: Space::Global, + center0: Point::new(50., 50.), + center1: Point::new(50., 50.), + radius0: 0., + radius1: 240., + extend: Extend::Pad, + stops: (&[ + Stop { + offset: 0., + color: Color::rgb8(0, 0, 255), + }, + Stop { + offset: 0.5, + color: Color::rgb8(0, 255, 0), + }, + Stop { + offset: 1., + color: Color::rgb8(255, 0, 0), + }, + ][..]) + .into(), + }); + //b.push_transform(Affine::translate(200., 200.) * Affine::rotate(45f32.to_radians())); + b.fill( + Fill::NonZero, + // &Brush::Solid(Color::rgba8(0, 255, 0, 255)), + &radial, + None, //Some(Affine::rotate(90f32.to_radians())), + Rect { + min: Point::new(0., 0.), + max: Point::new(200., 100.), + } + .elements(), + ); + b.finish(); + let mut b = build_scene(scene, rcx); + b.push_transform(Affine::translate(200., 200.) * Affine::rotate(45f32.to_radians())); + b.append(&fragment); + b.pop_transform(); + b.push_transform(Affine::translate(400., 600.)); + b.append(&fragment); + b.finish(); +} + +fn scene_to_encoded_scene<'a>( + scene: &'a Scene, + rcx: &'a ResourceContext, +) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> { + let d = scene.data(); + EncodedSceneRef { + transform_stream: &d.transform_stream, + tag_stream: &d.tag_stream, + pathseg_stream: &d.pathseg_stream, + linewidth_stream: &d.linewidth_stream, + drawtag_stream: &d.drawtag_stream, + drawdata_stream: &d.drawdata_stream, + n_path: d.n_path, + n_pathseg: d.n_pathseg, + n_clip: d.n_clip, + ramp_data: rcx.ramp_data(), + } +} + +fn render_info_string(rc: &mut impl RenderContext, info: &str) { + let layout = rc + .text() + .new_text_layout(info.to_string()) + .default_attribute(TextAttribute::FontSize(40.0)) + .build() + .unwrap(); + rc.draw_text(&layout, Point::new(110.0, 50.0)); +} + +// use piet_scene::geometry::*; +// use piet_scene::path::*; +// use piet_scene::scene::*; +// use piet_scene::{geometry::*, path::*, resource::ResourceContext, scene::*}; + +// fn main() { +// let mut scene = Scene::default(); +// let mut rcx = ResourceContext::new(); +// let mut sb = build_scene(&mut scene, &mut rcx); + +// sb.push_layer(Blend::default(), Rect::default().elements()); + +// // let mut path = Path::new(); +// // let mut b = PathBuilder::new(&mut path); +// // b.move_to(100., 100.); +// // b.line_to(200., 200.); +// // b.close_path(); +// // b.move_to(50., 50.); +// // b.line_to(600., 150.); +// // b.move_to(4., 2.); +// // b.quad_to(8., 8., 9., 9.); +// // b.close_path(); +// // println!("{:?}", path); +// // for el in path.elements() { +// // println!("{:?}", el); +// // } +// //sb.push_layer(path.elements(), BlendMode::default()); + +// sb.push_layer(Blend::default(), [Element::MoveTo((0., 0.).into())]); +// } diff --git a/piet-scene/src/resource/gradient.rs b/piet-scene/src/resource/gradient.rs index 0c509e4..bd2491c 100644 --- a/piet-scene/src/resource/gradient.rs +++ b/piet-scene/src/resource/gradient.rs @@ -102,7 +102,7 @@ fn make_ramp<'a>(stops: &'a [Stop]) -> impl Iterator + 'a { }) } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] struct ColorF64([f64; 4]); impl ColorF64 { @@ -117,7 +117,7 @@ impl ColorF64 { fn lerp(&self, other: &Self, a: f64) -> Self { fn l(x: f64, y: f64, a: f64) -> f64 { - x + (y - x) * a + x * (1.0 - a) + y * a } Self([ l(self.0[0], other.0[0], a), @@ -129,10 +129,10 @@ impl ColorF64 { fn to_premul_u32(&self) -> u32 { let a = self.0[3].min(1.0).max(0.0); - let r = ((self.0[0] * a).min(1.0).max(0.0) / 255.0) as u32; - let g = ((self.0[1] * a).min(1.0).max(0.0) / 255.0) as u32; - let b = ((self.0[2] * a).min(1.0).max(0.0) / 255.0) as u32; - let a = (a / 255.0) as u32; + let r = ((self.0[0] * a).min(1.0).max(0.0) * 255.0) as u32; + let g = ((self.0[1] * a).min(1.0).max(0.0) * 255.0) as u32; + let b = ((self.0[2] * a).min(1.0).max(0.0) * 255.0) as u32; + let a = (a * 255.0) as u32; r | (g << 8) | (b << 16) | (a << 24) } } diff --git a/piet-scene/src/resource/mod.rs b/piet-scene/src/resource/mod.rs index 1dfaa60..a1ea58b 100644 --- a/piet-scene/src/resource/mod.rs +++ b/piet-scene/src/resource/mod.rs @@ -38,6 +38,10 @@ impl ResourceContext { } pub fn destroy_brush(&mut self, brush: PersistentBrush) {} + + pub fn ramp_data(&self) -> &[u32] { + &self.ramps.data() + } } /// Handle for a brush that is managed by the resource context. diff --git a/piet-scene/src/scene/builder.rs b/piet-scene/src/scene/builder.rs index a92267b..85d75b2 100644 --- a/piet-scene/src/scene/builder.rs +++ b/piet-scene/src/scene/builder.rs @@ -41,25 +41,38 @@ pub struct Builder<'a> { scene: &'a mut SceneData, resources: ResourceData<'a>, layers: Vec, + transforms: Vec, } impl<'a> Builder<'a> { /// Creates a new builder for constructing a scene. fn new(scene: &'a mut SceneData, mut resources: ResourceData<'a>) -> Self { - scene.clear(); + let is_fragment = match resources { + ResourceData::Fragment(_) => true, + _ => false, + }; + scene.reset(is_fragment); resources.clear(); Self { scene, resources, layers: vec![], + transforms: vec![], } } /// Pushes a transform matrix onto the stack. - pub fn push_transform(&mut self, transform: &Affine) {} + pub fn push_transform(&mut self, transform: Affine) { + self.transform(transform); + self.transforms.push(transform); + } /// Pops the current transform matrix. - pub fn pop_transform(&mut self) {} + pub fn pop_transform(&mut self) { + if let Some(transform) = self.transforms.pop() { + self.transform(transform.inverse()); + } + } /// Pushes a new layer bound by the specifed shape and composed with /// previous layers using the specified blend mode. @@ -181,6 +194,15 @@ impl<'a> Builder<'a> { while let Some(layer) = self.layers.pop() { self.end_clip(Some(layer)); } + match self.resources { + ResourceData::Fragment(_) => { + // Make sure the transform state is invariant for fragments + while !self.transforms.is_empty() { + self.pop_transform(); + } + } + _ => {} + } } } diff --git a/piet-scene/src/scene/mod.rs b/piet-scene/src/scene/mod.rs index df9db90..ba0b069 100644 --- a/piet-scene/src/scene/mod.rs +++ b/piet-scene/src/scene/mod.rs @@ -28,20 +28,20 @@ use super::path::Element; use core::ops::Range; #[derive(Default)] -struct SceneData { - transform_stream: Vec, - tag_stream: Vec, - pathseg_stream: Vec, - linewidth_stream: Vec, - drawtag_stream: Vec, - drawdata_stream: Vec, - n_path: u32, - n_pathseg: u32, - n_clip: u32, +pub struct SceneData { + pub transform_stream: Vec, + pub tag_stream: Vec, + pub pathseg_stream: Vec, + pub linewidth_stream: Vec, + pub drawtag_stream: Vec, + pub drawdata_stream: Vec, + pub n_path: u32, + pub n_pathseg: u32, + pub n_clip: u32, } impl SceneData { - fn clear(&mut self) { + fn reset(&mut self, is_fragment: bool) { self.transform_stream.clear(); self.tag_stream.clear(); self.pathseg_stream.clear(); @@ -51,6 +51,11 @@ impl SceneData { self.n_path = 0; self.n_pathseg = 0; self.n_clip = 0; + if !is_fragment { + self.transform_stream + .push(Affine::new(&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0])); + self.linewidth_stream.push(-1.0); + } } fn append(&mut self, other: &SceneData) { @@ -76,6 +81,12 @@ pub struct Scene { data: SceneData, } +impl Scene { + pub fn data(&self) -> &SceneData { + &self.data + } +} + /// Encoded definition of a scene fragment and associated resources. #[derive(Default)] pub struct Fragment { From ba7f85731c9e5fb9c859aa75f5c9e7fe52b61f7a Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 2 May 2022 04:15:48 -0400 Subject: [PATCH 05/13] add glyph provider API This exposes a new uniform API for generating scene fragments for glyph outlines. --- Cargo.lock | 253 +++++++++++++++-------------- piet-scene/Cargo.toml | 2 + piet-scene/src/brush/gradient.rs | 9 -- piet-scene/src/glyph/mod.rs | 263 +++++++++++++++++++++++++++++++ piet-scene/src/lib.rs | 1 + piet-scene/src/main.rs | 2 - 6 files changed, 401 insertions(+), 129 deletions(-) create mode 100644 piet-scene/src/glyph/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 207dc02..4e98c9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] @@ -48,7 +48,7 @@ version = "0.33.3+1.2.191" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219" dependencies = [ - "libloading 0.7.1", + "libloading 0.7.3", ] [[package]] @@ -58,7 +58,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f91ce4c6be1a2ba99d3d6cd57d5bae9ac6d6f903b5ae53d6b1dee2edf872af" dependencies = [ "ash", - "raw-window-handle", + "raw-window-handle 0.3.4", "raw-window-metal", ] @@ -75,9 +75,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" @@ -93,18 +93,18 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bytemuck" -version = "1.7.2" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" +checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" +checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e" dependencies = [ "proc-macro2", "quote", @@ -129,9 +129,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -147,9 +147,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", @@ -169,7 +169,7 @@ dependencies = [ "bitflags", "block", "cocoa-foundation", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics 0.22.3", "foreign-types", "libc", @@ -184,7 +184,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ "bitflags", "block", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics-types", "foreign-types", "libc", @@ -203,9 +203,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys 0.8.3", "libc", @@ -242,7 +242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics-types", "foreign-types", "libc", @@ -255,7 +255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ "bitflags", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "foreign-types", "libc", ] @@ -275,9 +275,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] @@ -298,9 +298,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -319,10 +319,11 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ + "autocfg", "cfg-if 1.0.0", "crossbeam-utils", "lazy_static", @@ -332,9 +333,9 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -342,14 +343,20 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if 1.0.0", "lazy_static", ] +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + [[package]] name = "darling" version = "0.10.2" @@ -395,31 +402,20 @@ dependencies = [ "byteorder", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dirs" -version = "3.0.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -447,7 +443,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" dependencies = [ - "libloading 0.7.1", + "libloading 0.7.3", ] [[package]] @@ -490,9 +486,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if 1.0.0", "libc", @@ -552,9 +548,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.107" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" [[package]] name = "libloading" @@ -568,9 +564,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" dependencies = [ "cfg-if 1.0.0", "winapi", @@ -578,18 +574,19 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" dependencies = [ "cfg-if 1.0.0", ] @@ -611,9 +608,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" @@ -626,9 +623,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] @@ -677,9 +674,9 @@ dependencies = [ [[package]] name = "mio-misc" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ddf05411bb159cdb5801bb10002afb66cb4572be656044315e363460ce69dc2" +checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c" dependencies = [ "crossbeam", "crossbeam-queue", @@ -696,6 +693,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "moscato" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8372f6cdc8b2c431750a9c4edbc8d9c511ef1a68472aaa02500493414a407c64" +dependencies = [ + "pinot", +] + [[package]] name = "ndk" version = "0.3.0" @@ -737,9 +743,9 @@ dependencies = [ [[package]] name = "ndk-sys" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" [[package]] name = "nix" @@ -767,41 +773,39 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] name = "ntapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi", ] [[package]] name = "num_enum" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" dependencies = [ - "derivative", "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" dependencies = [ - "proc-macro-crate 1.1.0", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn", @@ -828,9 +832,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "owned_ttf_parser" @@ -896,7 +900,7 @@ dependencies = [ "piet-gpu-types", "png", "rand", - "raw-window-handle", + "raw-window-handle 0.3.4", "roxmltree", "swash", "winit", @@ -923,7 +927,7 @@ dependencies = [ "cocoa-foundation", "metal", "objc", - "raw-window-handle", + "raw-window-handle 0.3.4", "smallvec", "winapi", "wio", @@ -955,9 +959,11 @@ version = "0.1.0" dependencies = [ "bytemuck", "clap", + "moscato", "piet", "piet-gpu", "piet-gpu-hal", + "pinot", "png", "rand", "roxmltree", @@ -966,10 +972,16 @@ dependencies = [ ] [[package]] -name = "pkg-config" -version = "0.3.22" +name = "pinot" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" +checksum = "6ba3013ff85036c414a4a3cf826135db204de2bd80d684728550e7130421809a" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "png" @@ -985,9 +997,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro-crate" @@ -1000,9 +1012,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror", "toml", @@ -1010,18 +1022,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -1069,11 +1081,21 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" +checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" dependencies = [ "libc", + "raw-window-handle 0.4.3", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +dependencies = [ + "cty", ] [[package]] @@ -1085,26 +1107,27 @@ dependencies = [ "cocoa", "core-graphics 0.22.3", "objc", - "raw-window-handle", + "raw-window-handle 0.3.4", ] [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.6", "redox_syscall", + "thiserror", ] [[package]] @@ -1149,9 +1172,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.130" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" [[package]] name = "smallvec" @@ -1202,9 +1225,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.81" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" dependencies = [ "proc-macro2", "quote", @@ -1222,18 +1245,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -1242,9 +1265,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] @@ -1324,12 +1347,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - [[package]] name = "walkdir" version = "2.3.2" @@ -1465,7 +1482,7 @@ checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" dependencies = [ "bitflags", "cocoa", - "core-foundation 0.9.2", + "core-foundation 0.9.3", "core-graphics 0.22.3", "core-video-sys", "dispatch", @@ -1481,7 +1498,7 @@ dependencies = [ "objc", "parking_lot", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.3.4", "scopeguard", "smithay-client-toolkit", "wayland-client", @@ -1520,9 +1537,9 @@ dependencies = [ [[package]] name = "xdg" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803" +checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" dependencies = [ "dirs", ] diff --git a/piet-scene/Cargo.toml b/piet-scene/Cargo.toml index fd9a6c5..0e9ca25 100644 --- a/piet-scene/Cargo.toml +++ b/piet-scene/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" [dependencies] bytemuck = { version = "1.7.2", features = ["derive"] } smallvec = "1.8.0" +pinot = "0.1.5" +moscato = "0.1.2" # remove these and move demo to another directory piet-gpu = { path = "../piet-gpu" } diff --git a/piet-scene/src/brush/gradient.rs b/piet-scene/src/brush/gradient.rs index 51d558e..161604b 100644 --- a/piet-scene/src/brush/gradient.rs +++ b/piet-scene/src/brush/gradient.rs @@ -50,15 +50,8 @@ pub enum Extend { Reflect, } -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum Space { - Object, - Global, -} - #[derive(Clone, Debug)] pub struct LinearGradient { - pub space: Space, pub start: Point, pub end: Point, pub stops: StopVec, @@ -67,7 +60,6 @@ pub struct LinearGradient { #[derive(Clone, Debug)] pub struct RadialGradient { - pub space: Space, pub center0: Point, pub radius0: f32, pub center1: Point, @@ -78,7 +70,6 @@ pub struct RadialGradient { #[derive(Clone, Debug)] pub struct SweepGradient { - pub space: Space, pub center: Point, pub start_angle: f32, pub end_angle: f32, diff --git a/piet-scene/src/glyph/mod.rs b/piet-scene/src/glyph/mod.rs new file mode 100644 index 0000000..0e13211 --- /dev/null +++ b/piet-scene/src/glyph/mod.rs @@ -0,0 +1,263 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +pub use pinot; + +use crate::brush::{Brush, Color}; +use crate::path::Element; +use crate::scene::{build_fragment, Fill, Fragment}; +use moscato::{Context, Scaler}; +use pinot::{types::Tag, FontRef}; + +/// General context for creating scene fragments for glyph outlines. +pub struct GlyphContext { + ctx: Context, +} + +impl GlyphContext { + /// Creates a new context. + pub fn new() -> Self { + Self { + ctx: Context::new(), + } + } + + /// Creates a new provider for generating scene fragments for glyphs from + /// the specified font and settings. + pub fn new_provider<'a, V>( + &'a mut self, + font: &FontRef<'a>, + font_id: Option, + ppem: f32, + hint: bool, + variations: V, + ) -> GlyphProvider<'a> + where + V: IntoIterator, + V::Item: Into<(Tag, f32)>, + { + let scaler = if let Some(font_id) = font_id { + self.ctx + .new_scaler_with_id(font, font_id) + .size(ppem) + .hint(hint) + .variations(variations) + .build() + } else { + self.ctx + .new_scaler(font) + .size(ppem) + .hint(hint) + .variations(variations) + .build() + }; + GlyphProvider { scaler } + } +} + +/// Generator for scene fragments containing glyph outlines for a specific +/// font. +pub struct GlyphProvider<'a> { + scaler: Scaler<'a>, +} + +impl<'a> GlyphProvider<'a> { + /// Returns a scene fragment containing the commands to render the + /// specified glyph. + pub fn get(&mut self, gid: u16) -> Option { + let glyph = self.scaler.glyph(gid)?; + let path = glyph.path(0)?; + let mut fragment = Fragment::default(); + let mut builder = build_fragment(&mut fragment); + builder.fill( + Fill::NonZero, + &Brush::Solid(Color::rgb8(255, 255, 255)), + None, + convert_path(path.elements()), + ); + Some(fragment) + } + + /// Returns a scene fragment containing the commands and resources to + /// render the specified color glyph. + pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option { + use crate::geometry::*; + use moscato::Command; + let glyph = self.scaler.color_glyph(palette_index, gid)?; + let mut fragment = Fragment::default(); + let mut builder = build_fragment(&mut fragment); + for command in glyph.commands() { + match command { + Command::PushTransform(xform) => { + builder.push_transform(convert_transform(xform)); + } + Command::PopTransform => builder.pop_transform(), + Command::PushClip(path_index) => { + let path = glyph.path(*path_index)?; + builder.push_layer(Default::default(), convert_path(path.elements())); + } + Command::PopClip => builder.pop_layer(), + Command::PushLayer(bounds) => { + let rect = Rect { + min: Point::new(bounds.min.x, bounds.min.y), + max: Point::new(bounds.max.x, bounds.max.y), + }; + builder.push_layer(Default::default(), rect.elements()); + } + Command::PopLayer => builder.pop_layer(), + Command::BeginBlend(bounds, mode) => { + let rect = Rect { + min: Point::new(bounds.min.x, bounds.min.y), + max: Point::new(bounds.max.x, bounds.max.y), + }; + builder.push_layer(convert_blend(*mode), rect.elements()) + } + Command::EndBlend => builder.pop_layer(), + Command::SimpleFill(path_index, brush, xform) => { + let path = glyph.path(*path_index)?; + let brush = convert_brush(brush); + let xform = xform.map(|xform| convert_transform(&xform)); + builder.fill(Fill::NonZero, &brush, xform, convert_path(path.elements())); + } + Command::Fill(brush, xform) => { + // TODO: this needs to compute a bounding box for + // the parent clips + } + } + } + Some(fragment) + } +} + +fn convert_path( + path: impl Iterator + Clone, +) -> impl Iterator + Clone { + use crate::geometry::Point; + path.map(|el| match el { + moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y)), + moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y)), + moscato::Element::QuadTo(p0, p1) => { + Element::QuadTo(Point::new(p0.x, p0.y), Point::new(p1.x, p1.y)) + } + moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo( + Point::new(p0.x, p0.y), + Point::new(p1.x, p1.y), + Point::new(p2.x, p2.y), + ), + moscato::Element::Close => Element::Close, + }) +} + +fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend { + use crate::scene::{Blend, Compose, Mix}; + use moscato::CompositeMode; + let mut mix = Mix::Normal; + let mut compose = Compose::SrcOver; + match mode { + CompositeMode::Clear => compose = Compose::Clear, + CompositeMode::Src => compose = Compose::Copy, + CompositeMode::Dest => compose = Compose::Dest, + CompositeMode::SrcOver => {} + CompositeMode::DestOver => compose = Compose::DestOver, + CompositeMode::SrcIn => compose = Compose::SrcIn, + CompositeMode::DestIn => compose = Compose::DestIn, + CompositeMode::SrcOut => compose = Compose::SrcOut, + CompositeMode::DestOut => compose = Compose::DestOut, + CompositeMode::SrcAtop => compose = Compose::SrcAtop, + CompositeMode::DestAtop => compose = Compose::DestAtop, + CompositeMode::Xor => compose = Compose::Xor, + CompositeMode::Plus => compose = Compose::Plus, + CompositeMode::Screen => mix = Mix::Screen, + CompositeMode::Overlay => mix = Mix::Overlay, + CompositeMode::Darken => mix = Mix::Darken, + CompositeMode::Lighten => mix = Mix::Lighten, + CompositeMode::ColorDodge => mix = Mix::ColorDodge, + CompositeMode::ColorBurn => mix = Mix::ColorBurn, + CompositeMode::HardLight => mix = Mix::HardLight, + CompositeMode::SoftLight => mix = Mix::SoftLight, + CompositeMode::Difference => mix = Mix::Difference, + CompositeMode::Exclusion => mix = Mix::Exclusion, + CompositeMode::Multiply => mix = Mix::Multiply, + CompositeMode::HslHue => mix = Mix::Hue, + CompositeMode::HslSaturation => mix = Mix::Saturation, + CompositeMode::HslColor => mix = Mix::Color, + CompositeMode::HslLuminosity => mix = Mix::Luminosity, + } + Blend { mix, compose } +} + +fn convert_transform(xform: &moscato::Transform) -> crate::geometry::Affine { + crate::geometry::Affine { + xx: xform.xx, + yx: xform.yx, + xy: xform.xy, + yy: xform.yy, + dx: xform.dx, + dy: xform.dy, + } +} + +fn convert_brush(brush: &moscato::Brush) -> crate::brush::Brush { + use crate::brush::*; + use crate::geometry::*; + match brush { + moscato::Brush::Solid(color) => Brush::Solid(Color { + r: color.r, + g: color.g, + b: color.b, + a: color.a, + }), + moscato::Brush::LinearGradient(grad) => Brush::LinearGradient(LinearGradient { + start: Point::new(grad.start.x, grad.start.y), + end: Point::new(grad.end.x, grad.end.y), + stops: convert_stops(&grad.stops), + extend: convert_extend(grad.extend), + }), + moscato::Brush::RadialGradient(grad) => Brush::RadialGradient(RadialGradient { + center0: Point::new(grad.center0.x, grad.center0.y), + center1: Point::new(grad.center1.x, grad.center1.y), + radius0: grad.radius0, + radius1: grad.radius1, + stops: convert_stops(&grad.stops), + extend: convert_extend(grad.extend), + }), + } +} + +fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::StopVec { + use crate::brush::Stop; + stops + .iter() + .map(|stop| Stop { + offset: stop.offset, + color: Color { + r: stop.color.r, + g: stop.color.g, + b: stop.color.b, + a: stop.color.a, + }, + }) + .collect() +} + +fn convert_extend(extend: moscato::ExtendMode) -> crate::brush::Extend { + use crate::brush::Extend::*; + match extend { + moscato::ExtendMode::Pad => Pad, + moscato::ExtendMode::Repeat => Repeat, + moscato::ExtendMode::Reflect => Reflect, + } +} diff --git a/piet-scene/src/lib.rs b/piet-scene/src/lib.rs index 6b23f6d..a72ff54 100644 --- a/piet-scene/src/lib.rs +++ b/piet-scene/src/lib.rs @@ -16,6 +16,7 @@ pub mod brush; pub mod geometry; +pub mod glyph; pub mod path; pub mod resource; pub mod scene; diff --git a/piet-scene/src/main.rs b/piet-scene/src/main.rs index ce62b43..ff4030f 100644 --- a/piet-scene/src/main.rs +++ b/piet-scene/src/main.rs @@ -308,7 +308,6 @@ fn test_scene1(scene: &mut Scene, rcx: &mut ResourceContext) { let mut fragment = Fragment::default(); let mut b = build_fragment(&mut fragment); let linear = Brush::LinearGradient(LinearGradient { - space: Space::Global, start: Point::new(0., 0.), end: Point::new(200., 100.), extend: Extend::Pad, @@ -329,7 +328,6 @@ fn test_scene1(scene: &mut Scene, rcx: &mut ResourceContext) { .into(), }); let radial = Brush::RadialGradient(RadialGradient { - space: Space::Global, center0: Point::new(50., 50.), center1: Point::new(50., 50.), radius0: 0., From c749addf6cfcb693397c50c3259c19a3f4addc8e Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 9 May 2022 22:39:59 -0400 Subject: [PATCH 06/13] rebase on timer query patch --- Cargo.lock | 1 + piet-gpu-hal/Cargo.toml | 1 + piet-gpu-hal/examples/collatz.rs | 8 +- piet-gpu-hal/src/backend.rs | 36 ++- piet-gpu-hal/src/dx12.rs | 35 ++- piet-gpu-hal/src/dx12/wrappers.rs | 3 - piet-gpu-hal/src/hub.rs | 91 +++++--- piet-gpu-hal/src/lib.rs | 24 +- piet-gpu-hal/src/metal.rs | 321 +++++++++++++++++++++++--- piet-gpu-hal/src/metal/timer.rs | 172 ++++++++++++++ piet-gpu-hal/src/mux.rs | 17 ++ piet-gpu-hal/src/vulkan.rs | 55 +++-- piet-gpu/bin/cli.rs | 8 +- piet-gpu/bin/winit.rs | 45 ++-- piet-gpu/shader/coarse.comp | 2 +- piet-gpu/shader/gen/binning.dxil | Bin 6336 -> 6336 bytes piet-gpu/shader/gen/coarse.dxil | Bin 11652 -> 11628 bytes piet-gpu/shader/gen/coarse.hlsl | 79 +++---- piet-gpu/shader/gen/coarse.msl | 61 +++-- piet-gpu/shader/gen/coarse.spv | Bin 58964 -> 58852 bytes piet-gpu/shader/gen/draw_leaf.dxil | Bin 6760 -> 6764 bytes piet-gpu/shader/gen/draw_reduce.dxil | Bin 4264 -> 4260 bytes piet-gpu/shader/gen/draw_root.dxil | Bin 4468 -> 4468 bytes piet-gpu/shader/gen/kernel4.dxil | Bin 15108 -> 15112 bytes piet-gpu/shader/gen/kernel4_gray.dxil | Bin 15016 -> 15016 bytes piet-gpu/shader/gen/tile_alloc.dxil | Bin 5136 -> 5132 bytes piet-gpu/src/encoder.rs | 156 ------------- piet-gpu/src/lib.rs | 98 ++++---- piet-gpu/src/render_ctx.rs | 6 +- piet-gpu/src/stages.rs | 10 +- piet-gpu/src/stages/clip.rs | 12 +- piet-gpu/src/stages/draw.rs | 14 +- piet-gpu/src/stages/path.rs | 16 +- piet-gpu/src/stages/transform.rs | 14 +- piet-gpu/src/test_scenes.rs | 7 +- tests/src/clear.rs | 19 +- tests/src/clip.rs | 6 +- tests/src/draw.rs | 6 +- tests/src/linkedlist.rs | 6 +- tests/src/message_passing.rs | 6 +- tests/src/path.rs | 6 +- tests/src/prefix.rs | 6 +- tests/src/prefix_tree.rs | 14 +- tests/src/runner.rs | 14 +- tests/src/transform.rs | 6 +- 45 files changed, 887 insertions(+), 494 deletions(-) create mode 100644 piet-gpu-hal/src/metal/timer.rs diff --git a/Cargo.lock b/Cargo.lock index 4e98c9e..cb6b76a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -925,6 +925,7 @@ dependencies = [ "block", "bytemuck", "cocoa-foundation", + "foreign-types", "metal", "objc", "raw-window-handle 0.3.4", diff --git a/piet-gpu-hal/Cargo.toml b/piet-gpu-hal/Cargo.toml index 29b51bd..f9b844a 100644 --- a/piet-gpu-hal/Cargo.toml +++ b/piet-gpu-hal/Cargo.toml @@ -28,3 +28,4 @@ metal = "0.22" objc = "0.2.5" block = "0.1.6" cocoa-foundation = "0.1" +foreign-types = "0.3.2" diff --git a/piet-gpu-hal/examples/collatz.rs b/piet-gpu-hal/examples/collatz.rs index dae5b31..7aff938 100644 --- a/piet-gpu-hal/examples/collatz.rs +++ b/piet-gpu-hal/examples/collatz.rs @@ -1,4 +1,4 @@ -use piet_gpu_hal::{include_shader, BindType}; +use piet_gpu_hal::{include_shader, BindType, ComputePassDescriptor}; use piet_gpu_hal::{BufferUsage, Instance, InstanceFlags, Session}; fn main() { @@ -20,9 +20,9 @@ fn main() { let mut cmd_buf = session.cmd_buf().unwrap(); cmd_buf.begin(); cmd_buf.reset_query_pool(&query_pool); - cmd_buf.write_timestamp(&query_pool, 0); - cmd_buf.dispatch(&pipeline, &descriptor_set, (256, 1, 1), (1, 1, 1)); - cmd_buf.write_timestamp(&query_pool, 1); + let mut pass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::timer(&query_pool, 0, 1)); + pass.dispatch(&pipeline, &descriptor_set, (256, 1, 1), (1, 1, 1)); + pass.end(); cmd_buf.finish_timestamps(&query_pool); cmd_buf.host_barrier(); cmd_buf.finish(); diff --git a/piet-gpu-hal/src/backend.rs b/piet-gpu-hal/src/backend.rs index 02ac7cb..f2c67a1 100644 --- a/piet-gpu-hal/src/backend.rs +++ b/piet-gpu-hal/src/backend.rs @@ -17,7 +17,8 @@ //! The generic trait for backends to implement. use crate::{ - BindType, BufferUsage, Error, GpuInfo, ImageFormat, ImageLayout, MapMode, SamplerParams, + BindType, BufferUsage, ComputePassDescriptor, Error, GpuInfo, ImageFormat, ImageLayout, + MapMode, SamplerParams, }; pub trait Device: Sized { @@ -159,14 +160,32 @@ pub trait Device: Sized { unsafe fn create_sampler(&self, params: SamplerParams) -> Result; } +/// The trait implemented by backend command buffer implementations. +/// +/// Valid encoding is represented by a state machine (currently not validated +/// but it is easy to imagine there might be at least debug validation). Most +/// methods are only valid in a particular state, and some move it to another +/// state. pub trait CmdBuf { + /// Begin encoding. + /// + /// State: init -> ready unsafe fn begin(&mut self); + /// State: ready -> finished unsafe fn finish(&mut self); /// Return true if the command buffer is suitable for reuse. unsafe fn reset(&mut self) -> bool; + /// Begin a compute pass. + /// + /// State: ready -> in_compute_pass + unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor); + + /// Dispatch + /// + /// State: in_compute_pass unsafe fn dispatch( &mut self, pipeline: &D::Pipeline, @@ -175,6 +194,9 @@ pub trait CmdBuf { workgroup_size: (u32, u32, u32), ); + /// State: in_compute_pass -> ready + unsafe fn end_compute_pass(&mut self); + /// Insert an execution and memory barrier. /// /// Compute kernels (and other actions) after this barrier may read from buffers @@ -202,16 +224,16 @@ 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, size: Option); + unsafe fn clear_buffer(&mut self, buffer: &D::Buffer, size: Option); - unsafe fn copy_buffer(&self, src: &D::Buffer, dst: &D::Buffer); + unsafe fn copy_buffer(&mut self, src: &D::Buffer, dst: &D::Buffer); - unsafe fn copy_image_to_buffer(&self, src: &D::Image, dst: &D::Buffer); + unsafe fn copy_image_to_buffer(&mut self, src: &D::Image, dst: &D::Buffer); - unsafe fn copy_buffer_to_image(&self, src: &D::Buffer, dst: &D::Image); + unsafe fn copy_buffer_to_image(&mut self, src: &D::Buffer, dst: &D::Image); // low portability, dx12 doesn't support it natively - unsafe fn blit_image(&self, src: &D::Image, dst: &D::Image); + unsafe fn blit_image(&mut self, src: &D::Image, dst: &D::Image); /// Reset the query pool. /// @@ -227,7 +249,7 @@ pub trait CmdBuf { unsafe fn finish_timestamps(&mut self, _pool: &D::QueryPool) {} /// Begin a labeled section for debugging and profiling purposes. - unsafe fn begin_debug_label(&mut self, label: &str) {} + unsafe fn begin_debug_label(&mut self, _label: &str) {} /// End a section opened by `begin_debug_label`. unsafe fn end_debug_label(&mut self) {} diff --git a/piet-gpu-hal/src/dx12.rs b/piet-gpu-hal/src/dx12.rs index 78ad449..c5e1e04 100644 --- a/piet-gpu-hal/src/dx12.rs +++ b/piet-gpu-hal/src/dx12.rs @@ -21,7 +21,7 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use smallvec::SmallVec; -use crate::{BindType, BufferUsage, Error, GpuInfo, ImageLayout, MapMode, WorkgroupLimits, ImageFormat}; +use crate::{BindType, BufferUsage, Error, GpuInfo, ImageLayout, MapMode, WorkgroupLimits, ImageFormat, ComputePassDescriptor}; use self::{ descriptor::{CpuHeapRefOwned, DescriptorPool, GpuHeapRefOwned}, @@ -76,6 +76,7 @@ pub struct CmdBuf { c: wrappers::GraphicsCommandList, allocator: CommandAllocator, needs_reset: bool, + end_query: Option<(wrappers::QueryHeap, u32)>, } pub struct Pipeline { @@ -360,6 +361,7 @@ impl crate::backend::Device for Dx12Device { c, allocator, needs_reset: false, + end_query: None, }) } } @@ -388,11 +390,10 @@ impl crate::backend::Device for Dx12Device { let mapped = self.map_buffer(&pool.buf, 0, size as u64, MapMode::Read)?; std::ptr::copy_nonoverlapping(mapped, buf.as_mut_ptr() as *mut u8, size); self.unmap_buffer(&pool.buf, 0, size as u64, MapMode::Read)?; - let ts0 = buf[0]; let tsp = (self.ts_freq as f64).recip(); - let result = buf[1..] + let result = buf .iter() - .map(|ts| ts.wrapping_sub(ts0) as f64 * tsp) + .map(|ts| *ts as f64 * tsp) .collect(); Ok(result) } @@ -610,6 +611,16 @@ impl crate::backend::CmdBuf for CmdBuf { self.allocator.reset().is_ok() && self.c.reset(&self.allocator, None).is_ok() } + unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor) { + if let Some((pool, start, end)) = &desc.timer_queries { + #[allow(irrefutable_let_patterns)] + if let crate::hub::QueryPool::Dx12(pool) = pool { + self.write_timestamp(pool, *start); + self.end_query = Some((pool.heap.clone(), *end)); + } + } + } + unsafe fn dispatch( &mut self, pipeline: &Pipeline, @@ -628,6 +639,12 @@ impl crate::backend::CmdBuf for CmdBuf { .dispatch(workgroup_count.0, workgroup_count.1, workgroup_count.2); } + unsafe fn end_compute_pass(&mut self) { + if let Some((heap, end)) = self.end_query.take() { + self.c.end_timing_query(&heap, end); + } + } + unsafe fn memory_barrier(&mut self) { // See comments in CommandBuffer::pipeline_barrier in gfx-hal dx12 backend. // The "proper" way to do this would be to name the actual buffers participating @@ -666,7 +683,7 @@ impl crate::backend::CmdBuf for CmdBuf { self.memory_barrier(); } - unsafe fn clear_buffer(&self, buffer: &Buffer, size: Option) { + unsafe fn clear_buffer(&mut self, buffer: &Buffer, size: Option) { let cpu_ref = buffer.cpu_ref.as_ref().unwrap(); let (gpu_ref, heap) = buffer .gpu_ref @@ -684,23 +701,23 @@ impl crate::backend::CmdBuf for CmdBuf { ); } - unsafe fn copy_buffer(&self, src: &Buffer, dst: &Buffer) { + unsafe fn copy_buffer(&mut self, src: &Buffer, dst: &Buffer) { // TODO: consider using copy_resource here (if sizes match) let size = src.size.min(dst.size); self.c.copy_buffer(&dst.resource, 0, &src.resource, 0, size); } - unsafe fn copy_image_to_buffer(&self, src: &Image, dst: &Buffer) { + unsafe fn copy_image_to_buffer(&mut self, src: &Image, dst: &Buffer) { self.c .copy_texture_to_buffer(&src.resource, &dst.resource, src.size.0, src.size.1); } - unsafe fn copy_buffer_to_image(&self, src: &Buffer, dst: &Image) { + unsafe fn copy_buffer_to_image(&mut self, src: &Buffer, dst: &Image) { self.c .copy_buffer_to_texture(&src.resource, &dst.resource, dst.size.0, dst.size.1); } - unsafe fn blit_image(&self, src: &Image, dst: &Image) { + unsafe fn blit_image(&mut self, src: &Image, dst: &Image) { self.c.copy_resource(&src.resource, &dst.resource); } diff --git a/piet-gpu-hal/src/dx12/wrappers.rs b/piet-gpu-hal/src/dx12/wrappers.rs index 4bbb86c..9a3fb90 100644 --- a/piet-gpu-hal/src/dx12/wrappers.rs +++ b/piet-gpu-hal/src/dx12/wrappers.rs @@ -79,7 +79,6 @@ pub struct Blob(pub ComPtr); #[derive(Clone)] pub struct ShaderByteCode { pub bytecode: d3d12::D3D12_SHADER_BYTECODE, - blob: Option, } #[derive(Clone)] @@ -741,7 +740,6 @@ impl ShaderByteCode { BytecodeLength: blob.0.GetBufferSize(), pShaderBytecode: blob.0.GetBufferPointer(), }, - blob: Some(blob), } } @@ -810,7 +808,6 @@ impl ShaderByteCode { BytecodeLength: bytecode.len(), pShaderBytecode: bytecode.as_ptr() as *const _, }, - blob: None, } } } diff --git a/piet-gpu-hal/src/hub.rs b/piet-gpu-hal/src/hub.rs index cc09832..ea17754 100644 --- a/piet-gpu-hal/src/hub.rs +++ b/piet-gpu-hal/src/hub.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex, Weak}; use bytemuck::Pod; use smallvec::SmallVec; -use crate::{mux, BackendType, BufWrite, ImageFormat, MapMode}; +use crate::{mux, BackendType, BufWrite, ComputePassDescriptor, ImageFormat, MapMode}; use crate::{BindType, BufferUsage, Error, GpuInfo, ImageLayout, SamplerParams}; @@ -135,6 +135,11 @@ pub struct BufReadGuard<'a> { size: u64, } +/// A sub-object of a command buffer for a sequence of compute dispatches. +pub struct ComputePass<'a> { + cmd_buf: &'a mut CmdBuf, +} + impl Session { /// Create a new session, choosing the best backend. pub fn new(device: mux::Device) -> Session { @@ -370,8 +375,17 @@ impl Session { /// /// This should be called after waiting on the command buffer that wrote the /// timer queries. + /// + /// The returned vector is one shorter than the number of timer queries in the + /// pool; the first value is subtracted off. It would likely be better to return + /// the raw timestamps, but that change should be made consistently. pub unsafe fn fetch_query_pool(&self, pool: &QueryPool) -> Result, Error> { - self.0.device.fetch_query_pool(pool) + let result = self.0.device.fetch_query_pool(pool)?; + // Subtract off first timestamp. + Ok(result[1..] + .iter() + .map(|ts| *ts as f64 - result[0]) + .collect()) } #[doc(hidden)] @@ -471,23 +485,10 @@ impl CmdBuf { self.cmd_buf().finish(); } - /// Dispatch a compute shader. - /// - /// Request a compute shader to be run, using the pipeline to specify the - /// code, and the descriptor set to address the resources read and written. - /// - /// Both the workgroup count (number of workgroups) and the workgroup size - /// (number of threads in a workgroup) must be specified here, though not - /// all back-ends require the latter info. - pub unsafe fn dispatch( - &mut self, - pipeline: &Pipeline, - descriptor_set: &DescriptorSet, - workgroup_count: (u32, u32, u32), - workgroup_size: (u32, u32, u32), - ) { - self.cmd_buf() - .dispatch(pipeline, descriptor_set, workgroup_count, workgroup_size); + /// Begin a compute pass. + pub unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor) -> ComputePass { + self.cmd_buf().begin_compute_pass(desc); + ComputePass { cmd_buf: self } } /// Insert an execution and memory barrier. @@ -582,13 +583,6 @@ impl CmdBuf { self.cmd_buf().reset_query_pool(pool); } - /// Write a timestamp. - /// - /// The query index must be less than the size of the query pool on creation. - pub unsafe fn write_timestamp(&mut self, pool: &QueryPool, query: u32) { - self.cmd_buf().write_timestamp(pool, query); - } - /// Prepare the timestamps for reading. This isn't required on Vulkan but /// is required on (at least) DX12. /// @@ -692,6 +686,51 @@ impl Drop for SubmittedCmdBuf { } } +impl<'a> ComputePass<'a> { + /// Dispatch a compute shader. + /// + /// Request a compute shader to be run, using the pipeline to specify the + /// code, and the descriptor set to address the resources read and written. + /// + /// Both the workgroup count (number of workgroups) and the workgroup size + /// (number of threads in a workgroup) must be specified here, though not + /// all back-ends require the latter info. + pub unsafe fn dispatch( + &mut self, + pipeline: &Pipeline, + descriptor_set: &DescriptorSet, + workgroup_count: (u32, u32, u32), + workgroup_size: (u32, u32, u32), + ) { + self.cmd_buf + .cmd_buf() + .dispatch(pipeline, descriptor_set, workgroup_count, workgroup_size); + } + + /// Add a memory barrier. + /// + /// Inserts a memory barrier in the compute encoder. This is a convenience + /// function for calling the same function on the underlying command buffer, + /// avoiding borrow check issues. + pub unsafe fn memory_barrier(&mut self) { + self.cmd_buf.memory_barrier(); + } + + /// Begin a labeled section for debugging and profiling purposes. + pub unsafe fn begin_debug_label(&mut self, label: &str) { + self.cmd_buf.begin_debug_label(label); + } + + /// End a section opened by `begin_debug_label`. + pub unsafe fn end_debug_label(&mut self) { + self.cmd_buf.end_debug_label(); + } + + pub unsafe fn end(self) { + self.cmd_buf.cmd_buf().end_compute_pass(); + } +} + impl Drop for BufferInner { fn drop(&mut self) { if let Some(session) = Weak::upgrade(&self.session) { diff --git a/piet-gpu-hal/src/lib.rs b/piet-gpu-hal/src/lib.rs index fab7d65..a1073f4 100644 --- a/piet-gpu-hal/src/lib.rs +++ b/piet-gpu-hal/src/lib.rs @@ -21,8 +21,8 @@ pub use crate::mux::{ }; pub use bufwrite::BufWrite; pub use hub::{ - BufReadGuard, BufWriteGuard, Buffer, CmdBuf, DescriptorSetBuilder, Image, RetainResource, - Session, SubmittedCmdBuf, + BufReadGuard, BufWriteGuard, Buffer, CmdBuf, ComputePass, DescriptorSetBuilder, Image, + RetainResource, Session, SubmittedCmdBuf, }; // TODO: because these are conditionally included, "cargo fmt" does not @@ -189,3 +189,23 @@ pub struct WorkgroupLimits { /// dimension. pub max_invocations: u32, } + +/// Options for creating a compute pass. +#[derive(Default)] +pub struct ComputePassDescriptor<'a> { + // Maybe label should go here? It does in wgpu and wgpu_hal. + /// Timer query parameters. + /// + /// To record timer queries for a compute pass, set the query pool, start + /// query index, and end query index here. The indices must be less than + /// the size of the query pool. + timer_queries: Option<(&'a QueryPool, u32, u32)>, +} + +impl<'a> ComputePassDescriptor<'a> { + pub fn timer(pool: &'a QueryPool, start_query: u32, end_query: u32) -> ComputePassDescriptor { + ComputePassDescriptor { + timer_queries: Some((pool, start_query, end_query)), + } + } +} diff --git a/piet-gpu-hal/src/metal.rs b/piet-gpu-hal/src/metal.rs index e3157d4..307def8 100644 --- a/piet-gpu-hal/src/metal.rs +++ b/piet-gpu-hal/src/metal.rs @@ -15,25 +15,32 @@ // Also licensed under MIT license, at your choice. mod clear; +mod timer; mod util; use std::mem; use std::sync::{Arc, Mutex}; +use block::Block; use cocoa_foundation::base::id; use cocoa_foundation::foundation::{NSInteger, NSUInteger}; +use foreign_types::ForeignType; use objc::rc::autoreleasepool; use objc::runtime::{Object, BOOL, YES}; use objc::{class, msg_send, sel, sel_impl}; -use metal::{CGFloat, MTLFeatureSet}; +use metal::{CGFloat, CommandBufferRef, MTLFeatureSet}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use crate::{BufferUsage, Error, GpuInfo, ImageFormat, MapMode, WorkgroupLimits}; +use crate::{ + BufferUsage, ComputePassDescriptor, Error, GpuInfo, ImageFormat, MapMode, WorkgroupLimits, +}; use util::*; +use self::timer::{CounterSampleBuffer, CounterSet, TimeCalibration}; + pub struct MtlInstance; pub struct MtlDevice { @@ -41,6 +48,18 @@ pub struct MtlDevice { cmd_queue: Arc>, gpu_info: GpuInfo, helpers: Arc, + timer_set: Option, + counter_style: CounterStyle, +} + +/// Type of counter sampling. +/// +/// See https://developer.apple.com/documentation/metal/counter_sampling/sampling_gpu_data_into_counter_sample_buffers +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum CounterStyle { + None, + Stage, + Command, } pub struct MtlSurface { @@ -81,9 +100,22 @@ pub struct Semaphore; pub struct CmdBuf { cmd_buf: metal::CommandBuffer, helpers: Arc, + cur_encoder: Encoder, + time_calibration: Arc>, + counter_style: CounterStyle, } -pub struct QueryPool; +enum Encoder { + None, + Compute(metal::ComputeCommandEncoder, Option<(id, u32)>), + Blit(metal::BlitCommandEncoder), +} + +#[derive(Default)] +pub struct QueryPool { + counter_sample_buf: Option, + calibration: Arc>>>>, +} pub struct Pipeline(metal::ComputePipelineState); @@ -209,18 +241,43 @@ impl MtlDevice { let helpers = Arc::new(Helpers { clear_pipeline: clear::make_clear_pipeline(&device), }); + // Timer stuff + let timer_set = CounterSet::get_timer_counter_set(&device); + let counter_style = if timer_set.is_some() { + if device.supports_counter_sampling(metal::MTLCounterSamplingPoint::AtStageBoundary) { + CounterStyle::Stage + } else if device + .supports_counter_sampling(metal::MTLCounterSamplingPoint::AtDispatchBoundary) + { + CounterStyle::Command + } else { + CounterStyle::None + } + } else { + CounterStyle::None + }; MtlDevice { device, cmd_queue: Arc::new(Mutex::new(cmd_queue)), gpu_info, helpers, + timer_set, + counter_style, } } pub fn cmd_buf_from_raw_mtl(&self, raw_cmd_buf: metal::CommandBuffer) -> CmdBuf { let cmd_buf = raw_cmd_buf; let helpers = self.helpers.clone(); - CmdBuf { cmd_buf, helpers } + let cur_encoder = Encoder::None; + let time_calibration = Default::default(); + CmdBuf { + cmd_buf, + helpers, + cur_encoder, + time_calibration, + counter_style: self.counter_style, + } } pub fn image_from_raw_mtl(&self, texture: metal::Texture, width: u32, height: u32) -> Image { @@ -330,11 +387,35 @@ impl crate::backend::Device for MtlDevice { fn create_cmd_buf(&self) -> Result { let cmd_queue = self.cmd_queue.lock().unwrap(); + // A discussion about autorelease pools. + // + // Autorelease pools are a sore point in Rust/Objective-C interop. Basically, + // you can have any two of correctness, ergonomics, and performance. Here we've + // chosen the first two, using the pattern of a fine grained autorelease pool + // to give the Obj-C object Rust-like lifetime semantics whenever objects are + // created as autorelease (by convention, this is any object creation with an + // Obj-C method name that doesn't begin with "new" or "alloc"). + // + // To gain back some of the performance, we'd need a way to wrap an autorelease + // pool over a chunk of work - that could be one frame of rendering, but for + // tests that iterate a number of command buffer submissions, it would need to + // be around that. On non-mac platforms, it would be a no-op. + // + // In any case, this way, the caller doesn't need to worry, and the performance + // hit might not be so bad (perhaps we should measure). + // consider new_command_buffer_with_unretained_references for performance - let cmd_buf = cmd_queue.new_command_buffer(); - let cmd_buf = autoreleasepool(|| cmd_buf.to_owned()); + let cmd_buf = autoreleasepool(|| cmd_queue.new_command_buffer().to_owned()); let helpers = self.helpers.clone(); - Ok(CmdBuf { cmd_buf, helpers }) + let cur_encoder = Encoder::None; + let time_calibration = Default::default(); + Ok(CmdBuf { + cmd_buf, + helpers, + cur_encoder, + time_calibration, + counter_style: self.counter_style, + }) } unsafe fn destroy_cmd_buf(&self, _cmd_buf: Self::CmdBuf) -> Result<(), Error> { @@ -342,12 +423,31 @@ impl crate::backend::Device for MtlDevice { } fn create_query_pool(&self, n_queries: u32) -> Result { - // TODO - Ok(QueryPool) + if let Some(timer_set) = &self.timer_set { + let pool = CounterSampleBuffer::new(&self.device, n_queries as u64, timer_set) + .ok_or("error creating timer query pool")?; + return Ok(QueryPool { + counter_sample_buf: Some(pool), + calibration: Default::default(), + }); + } + Ok(QueryPool::default()) } unsafe fn fetch_query_pool(&self, pool: &Self::QueryPool) -> Result, Error> { - // TODO + if let Some(raw) = &pool.counter_sample_buf { + let resolved = raw.resolve(); + let calibration = pool.calibration.lock().unwrap(); + if let Some(calibration) = &*calibration { + let calibration = calibration.lock().unwrap(); + let result = resolved + .iter() + .map(|time_ns| calibration.correlate(*time_ns)) + .collect(); + return Ok(result); + } + } + // Maybe should return None indicating it wasn't successful? But that might break. Ok(Vec::new()) } @@ -358,7 +458,37 @@ impl crate::backend::Device for MtlDevice { _signal_semaphores: &[&Self::Semaphore], fence: Option<&mut Self::Fence>, ) -> Result<(), Error> { + unsafe fn add_scheduled_handler( + cmd_buf: &metal::CommandBufferRef, + block: &Block<(&CommandBufferRef,), ()>, + ) { + msg_send![cmd_buf, addScheduledHandler: block] + } for cmd_buf in cmd_bufs { + let time_calibration = cmd_buf.time_calibration.clone(); + let start_block = block::ConcreteBlock::new(move |buffer: &metal::CommandBufferRef| { + let device: id = msg_send![buffer, device]; + let mut time_calibration = time_calibration.lock().unwrap(); + let cpu_ts_ptr = &mut time_calibration.cpu_start_ts as *mut _; + let gpu_ts_ptr = &mut time_calibration.gpu_start_ts as *mut _; + // TODO: only do this if supported. + let () = msg_send![device, sampleTimestamps: cpu_ts_ptr gpuTimestamp: gpu_ts_ptr]; + }) + .copy(); + add_scheduled_handler(&cmd_buf.cmd_buf, &start_block); + let time_calibration = cmd_buf.time_calibration.clone(); + let completed_block = + block::ConcreteBlock::new(move |buffer: &metal::CommandBufferRef| { + let device: id = msg_send![buffer, device]; + let mut time_calibration = time_calibration.lock().unwrap(); + let cpu_ts_ptr = &mut time_calibration.cpu_end_ts as *mut _; + let gpu_ts_ptr = &mut time_calibration.gpu_end_ts as *mut _; + // TODO: only do this if supported. + let () = + msg_send![device, sampleTimestamps: cpu_ts_ptr gpuTimestamp: gpu_ts_ptr]; + }) + .copy(); + cmd_buf.cmd_buf.add_completed_handler(&completed_block); cmd_buf.cmd_buf.commit(); } if let Some(last_cmd_buf) = cmd_bufs.last() { @@ -439,12 +569,70 @@ impl crate::backend::Device for MtlDevice { impl crate::backend::CmdBuf for CmdBuf { unsafe fn begin(&mut self) {} - unsafe fn finish(&mut self) {} + unsafe fn finish(&mut self) { + self.flush_encoder(); + } unsafe fn reset(&mut self) -> bool { false } + unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor) { + // TODO: we might want to get better about validation but the following + // assert is likely to trigger, and also a case can be made that + // validation should be done at the hub level, for consistency. + //debug_assert!(matches!(self.cur_encoder, Encoder::None)); + self.flush_encoder(); + autoreleasepool(|| { + let (encoder, end_query) = match (&desc.timer_queries, self.counter_style) { + (Some(queries), CounterStyle::Stage) => { + let descriptor: id = + msg_send![class!(MTLComputePassDescriptor), computePassDescriptor]; + let attachments: id = msg_send![descriptor, sampleBufferAttachments]; + let index: NSUInteger = 0; + let attachment: id = msg_send![attachments, objectAtIndexedSubscript: index]; + // Here we break the hub/mux separation a bit, for expedience + #[allow(irrefutable_let_patterns)] + if let crate::hub::QueryPool::Mtl(query_pool) = queries.0 { + if let Some(sample_buf) = &query_pool.counter_sample_buf { + let () = msg_send![attachment, setSampleBuffer: sample_buf.id()]; + } + } + let start_index = queries.1 as NSUInteger; + let end_index = queries.2 as NSInteger; + let () = msg_send![attachment, setStartOfEncoderSampleIndex: start_index]; + let () = msg_send![attachment, setEndOfEncoderSampleIndex: end_index]; + ( + msg_send![ + self.cmd_buf, + computeCommandEncoderWithDescriptor: descriptor + ], + None, + ) + } + (Some(queries), CounterStyle::Command) => { + let encoder = self.cmd_buf.new_compute_command_encoder(); + #[allow(irrefutable_let_patterns)] + let end_query = if let crate::hub::QueryPool::Mtl(query_pool) = queries.0 { + if let Some(sample_buf) = &query_pool.counter_sample_buf { + let sample_index = queries.1 as NSUInteger; + let sample_buf = sample_buf.id(); + let () = msg_send![encoder, sampleCountersInBuffer: sample_buf atSampleIndex: sample_index withBarrier: true]; + Some((sample_buf, queries.2)) + } else { + None + } + } else { + None + }; + (encoder, end_query) + } + _ => (self.cmd_buf.new_compute_command_encoder(), None), + }; + self.cur_encoder = Encoder::Compute(encoder.to_owned(), end_query); + }); + } + unsafe fn dispatch( &mut self, pipeline: &Pipeline, @@ -452,7 +640,7 @@ impl crate::backend::CmdBuf for CmdBuf { workgroup_count: (u32, u32, u32), workgroup_size: (u32, u32, u32), ) { - let encoder = self.cmd_buf.new_compute_command_encoder(); + let encoder = self.compute_command_encoder(); encoder.set_compute_pipeline_state(&pipeline.0); let mut buf_ix = 0; for buffer in &descriptor_set.buffers { @@ -475,7 +663,11 @@ impl crate::backend::CmdBuf for CmdBuf { depth: workgroup_size.2 as u64, }; encoder.dispatch_thread_groups(workgroup_count, workgroup_size); - encoder.end_encoding(); + } + + unsafe fn end_compute_pass(&mut self) { + // TODO: might validate that we are in a compute encoder state + self.flush_encoder(); } unsafe fn memory_barrier(&mut self) { @@ -494,22 +686,23 @@ impl crate::backend::CmdBuf for CmdBuf { // I think these are being tracked. } - unsafe fn clear_buffer(&self, buffer: &Buffer, size: Option) { + unsafe fn clear_buffer(&mut self, buffer: &Buffer, size: Option) { let size = size.unwrap_or(buffer.size); - let encoder = self.cmd_buf.new_compute_command_encoder(); - clear::encode_clear(&encoder, &self.helpers.clear_pipeline, &buffer.buffer, size); - encoder.end_encoding() + let _ = self.compute_command_encoder(); + // Getting this directly is a workaround for a borrow checker issue. + if let Encoder::Compute(e, _) = &self.cur_encoder { + clear::encode_clear(e, &self.helpers.clear_pipeline, &buffer.buffer, size); + } } - unsafe fn copy_buffer(&self, src: &Buffer, dst: &Buffer) { - let encoder = self.cmd_buf.new_blit_command_encoder(); + unsafe fn copy_buffer(&mut self, src: &Buffer, dst: &Buffer) { + let encoder = self.blit_command_encoder(); let size = src.size.min(dst.size); encoder.copy_from_buffer(&src.buffer, 0, &dst.buffer, 0, size); - encoder.end_encoding(); } - unsafe fn copy_image_to_buffer(&self, src: &Image, dst: &Buffer) { - let encoder = self.cmd_buf.new_blit_command_encoder(); + unsafe fn copy_image_to_buffer(&mut self, src: &Image, dst: &Buffer) { + let encoder = self.blit_command_encoder(); assert_eq!(dst.size, (src.width as u64) * (src.height as u64) * 4); let bytes_per_row = (src.width * 4) as NSUInteger; let src_size = metal::MTLSize { @@ -530,11 +723,10 @@ impl crate::backend::CmdBuf for CmdBuf { bytes_per_row * src.height as NSUInteger, metal::MTLBlitOption::empty(), ); - encoder.end_encoding(); } - unsafe fn copy_buffer_to_image(&self, src: &Buffer, dst: &Image) { - let encoder = self.cmd_buf.new_blit_command_encoder(); + unsafe fn copy_buffer_to_image(&mut self, src: &Buffer, dst: &Image) { + let encoder = self.blit_command_encoder(); assert_eq!(src.size, (dst.width as u64) * (dst.height as u64) * 4); let bytes_per_row = (dst.width * 4) as NSUInteger; let src_size = metal::MTLSize { @@ -555,11 +747,10 @@ impl crate::backend::CmdBuf for CmdBuf { origin, metal::MTLBlitOption::empty(), ); - encoder.end_encoding(); } - unsafe fn blit_image(&self, src: &Image, dst: &Image) { - let encoder = self.cmd_buf.new_blit_command_encoder(); + unsafe fn blit_image(&mut self, src: &Image, dst: &Image) { + let encoder = self.blit_command_encoder(); let src_size = metal::MTLSize { width: src.width.min(dst.width) as NSUInteger, height: src.width.min(dst.height) as NSUInteger, @@ -577,15 +768,79 @@ impl crate::backend::CmdBuf for CmdBuf { 0, origin, ); - encoder.end_encoding(); } - unsafe fn reset_query_pool(&mut self, pool: &QueryPool) {} + unsafe fn reset_query_pool(&mut self, pool: &QueryPool) { + let mut calibration = pool.calibration.lock().unwrap(); + *calibration = Some(self.time_calibration.clone()); + } unsafe fn write_timestamp(&mut self, pool: &QueryPool, query: u32) { - // TODO - // This really a PITA because it's pretty different than Vulkan. - // See https://developer.apple.com/documentation/metal/counter_sampling + if let Some(buf) = &pool.counter_sample_buf { + if matches!(self.cur_encoder, Encoder::None) { + self.cur_encoder = + Encoder::Compute(self.cmd_buf.new_compute_command_encoder().to_owned(), None); + } + let sample_index = query as NSUInteger; + if self.counter_style == CounterStyle::Command { + match &self.cur_encoder { + Encoder::Compute(e, _) => { + let () = msg_send![e.as_ptr(), sampleCountersInBuffer: buf.id() atSampleIndex: sample_index withBarrier: true]; + } + Encoder::None => unreachable!(), + _ => todo!(), + } + } else if self.counter_style == CounterStyle::Stage { + match &self.cur_encoder { + Encoder::Compute(_e, _) => { + println!("write_timestamp is not supported for stage-style encoders"); + } + _ => (), + } + } + } + } +} + +impl CmdBuf { + fn compute_command_encoder(&mut self) -> &metal::ComputeCommandEncoder { + if !matches!(self.cur_encoder, Encoder::Compute(..)) { + self.flush_encoder(); + self.cur_encoder = + Encoder::Compute(self.cmd_buf.new_compute_command_encoder().to_owned(), None); + } + if let Encoder::Compute(e, _) = &self.cur_encoder { + e + } else { + unreachable!() + } + } + + fn blit_command_encoder(&mut self) -> &metal::BlitCommandEncoder { + if !matches!(self.cur_encoder, Encoder::Blit(_)) { + self.flush_encoder(); + self.cur_encoder = Encoder::Blit(self.cmd_buf.new_blit_command_encoder().to_owned()); + } + if let Encoder::Blit(e) = &self.cur_encoder { + e + } else { + unreachable!() + } + } + + fn flush_encoder(&mut self) { + match std::mem::replace(&mut self.cur_encoder, Encoder::None) { + Encoder::Compute(e, Some((sample_buf, end_query))) => { + let sample_index = end_query as NSUInteger; + unsafe { + let () = msg_send![e.as_ptr(), sampleCountersInBuffer: sample_buf atSampleIndex: sample_index withBarrier: true]; + } + e.end_encoding(); + } + Encoder::Compute(e, None) => e.end_encoding(), + Encoder::Blit(e) => e.end_encoding(), + Encoder::None => (), + } } } diff --git a/piet-gpu-hal/src/metal/timer.rs b/piet-gpu-hal/src/metal/timer.rs new file mode 100644 index 0000000..65c8026 --- /dev/null +++ b/piet-gpu-hal/src/metal/timer.rs @@ -0,0 +1,172 @@ +// Copyright 2021 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +//! Support for timer queries. +//! +//! Likely some of this should be upstreamed into metal-rs. + +use std::{ffi::CStr, ptr::null_mut}; + +use cocoa_foundation::{ + base::id, + foundation::{NSRange, NSUInteger}, +}; +use metal::{DeviceRef, MTLStorageMode}; +use objc::{class, msg_send, sel, sel_impl}; + +pub struct CounterSampleBuffer { + id: id, + count: u64, +} + +pub struct CounterSet { + id: id, +} + +#[derive(Default)] +pub struct TimeCalibration { + pub cpu_start_ts: u64, + pub gpu_start_ts: u64, + pub cpu_end_ts: u64, + pub gpu_end_ts: u64, +} + +impl Drop for CounterSampleBuffer { + fn drop(&mut self) { + unsafe { msg_send![self.id, release] } + } +} + +impl Clone for CounterSampleBuffer { + fn clone(&self) -> CounterSampleBuffer { + unsafe { + CounterSampleBuffer { + id: msg_send![self.id, retain], + count: self.count, + } + } + } +} + +impl CounterSampleBuffer { + pub fn id(&self) -> id { + self.id + } +} + +impl Drop for CounterSet { + fn drop(&mut self) { + unsafe { msg_send![self.id, release] } + } +} + +impl CounterSet { + pub fn get_timer_counter_set(device: &DeviceRef) -> Option { + unsafe { + // TODO: version check + let sets: id = msg_send!(device, counterSets); + let count: NSUInteger = msg_send![sets, count]; + for i in 0..count { + let set: id = msg_send![sets, objectAtIndex: i]; + let name: id = msg_send![set, name]; + let name_cstr = CStr::from_ptr(msg_send![name, UTF8String]); + if name_cstr.to_bytes() == b"timestamp" { + return Some(CounterSet { id: set }); + } + } + None + } + } +} + +// copied from metal-rs; should be in common utilities maybe? +fn nsstring_as_str(nsstr: &objc::runtime::Object) -> &str { + let bytes = unsafe { + let bytes: *const std::os::raw::c_char = msg_send![nsstr, UTF8String]; + bytes as *const u8 + }; + let len: NSUInteger = unsafe { msg_send![nsstr, length] }; + unsafe { + let bytes = std::slice::from_raw_parts(bytes, len as usize); + std::str::from_utf8(bytes).unwrap() + } +} + +impl CounterSampleBuffer { + pub fn new( + device: &DeviceRef, + count: u64, + counter_set: &CounterSet, + ) -> Option { + unsafe { + let desc_cls = class!(MTLCounterSampleBufferDescriptor); + let descriptor: id = msg_send![desc_cls, alloc]; + let _: id = msg_send![descriptor, init]; + let count = count as NSUInteger; + let () = msg_send![descriptor, setSampleCount: count]; + let () = msg_send![descriptor, setCounterSet: counter_set.id]; + let () = msg_send![ + descriptor, + setStorageMode: MTLStorageMode::Shared as NSUInteger + ]; + let mut error: id = null_mut(); + let buf: id = msg_send![device, newCounterSampleBufferWithDescriptor: descriptor error: &mut error]; + let () = msg_send![descriptor, release]; + if !error.is_null() { + let description = msg_send![error, localizedDescription]; + println!( + "error allocating sample buffer, code = {}", + nsstring_as_str(description) + ); + let () = msg_send![error, release]; + return None; + } + Some(CounterSampleBuffer { id: buf, count }) + } + } + + // Read the timestamps. + // + // Safety: the lifetime of the returned slice is wrong, it's actually autoreleased. + pub unsafe fn resolve(&self) -> &[u64] { + let range = NSRange::new(0, self.count); + let data: id = msg_send![self.id, resolveCounterRange: range]; + if data.is_null() { + &[] + } else { + let bytes: *const u64 = msg_send![data, bytes]; + std::slice::from_raw_parts(bytes, self.count as usize) + } + } +} + +impl TimeCalibration { + /// Convert GPU timestamp into CPU time base. + /// + /// See https://developer.apple.com/documentation/metal/performance_tuning/correlating_cpu_and_gpu_timestamps + pub fn correlate(&self, raw_ts: u64) -> f64 { + let delta_cpu = self.cpu_end_ts - self.cpu_start_ts; + let delta_gpu = self.gpu_end_ts - self.gpu_start_ts; + let adj_ts = if delta_gpu > 0 { + let scale = delta_cpu as f64 / delta_gpu as f64; + self.cpu_start_ts as f64 + (raw_ts as f64 - self.gpu_start_ts as f64) * scale + } else { + // Default is ns on Apple Silicon; on other hardware this will be wrong + raw_ts as f64 + }; + adj_ts * 1e-9 + } +} diff --git a/piet-gpu-hal/src/mux.rs b/piet-gpu-hal/src/mux.rs index af1702d..9795193 100644 --- a/piet-gpu-hal/src/mux.rs +++ b/piet-gpu-hal/src/mux.rs @@ -35,6 +35,7 @@ use crate::backend::DescriptorSetBuilder as DescriptorSetBuilderTrait; use crate::backend::Device as DeviceTrait; use crate::BackendType; use crate::BindType; +use crate::ComputePassDescriptor; use crate::ImageFormat; use crate::MapMode; use crate::{BufferUsage, Error, GpuInfo, ImageLayout, InstanceFlags}; @@ -658,6 +659,14 @@ impl CmdBuf { } } + pub unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor) { + mux_match! { self; + CmdBuf::Vk(c) => c.begin_compute_pass(desc), + CmdBuf::Dx12(c) => c.begin_compute_pass(desc), + CmdBuf::Mtl(c) => c.begin_compute_pass(desc), + } + } + /// Dispatch a compute shader. /// /// Note that both the number of workgroups (`workgroup_count`) and the number of @@ -680,6 +689,14 @@ impl CmdBuf { } } + pub unsafe fn end_compute_pass(&mut self) { + mux_match! { self; + CmdBuf::Vk(c) => c.end_compute_pass(), + CmdBuf::Dx12(c) => c.end_compute_pass(), + CmdBuf::Mtl(c) => c.end_compute_pass(), + } + } + pub unsafe fn memory_barrier(&mut self) { mux_match! { self; CmdBuf::Vk(c) => c.memory_barrier(), diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 8392899..504d947 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -15,7 +15,7 @@ use smallvec::SmallVec; use crate::backend::Device as DeviceTrait; use crate::{ BindType, BufferUsage, Error, GpuInfo, ImageFormat, ImageLayout, MapMode, SamplerParams, SubgroupSize, - WorkgroupLimits, + WorkgroupLimits, ComputePassDescriptor, }; pub struct VkInstance { @@ -92,6 +92,7 @@ pub struct CmdBuf { cmd_buf: vk::CommandBuffer, cmd_pool: vk::CommandPool, device: Arc, + end_query: Option<(vk::QueryPool, u32)>, } pub struct QueryPool { @@ -738,6 +739,7 @@ impl crate::backend::Device for VkDevice { cmd_buf, cmd_pool, device: self.device.clone(), + end_query: None, }) } } @@ -770,11 +772,10 @@ impl crate::backend::Device for VkDevice { // results (Windows 10, AMD 5700 XT). let flags = vk::QueryResultFlags::TYPE_64 | vk::QueryResultFlags::WAIT; device.get_query_pool_results(pool.pool, 0, pool.n_queries, &mut buf, flags)?; - let ts0 = buf[0]; let tsp = self.timestamp_period as f64 * 1e-9; - let result = buf[1..] + let result = buf .iter() - .map(|ts| ts.wrapping_sub(ts0) as f64 * tsp) + .map(|ts| *ts as f64 * tsp) .collect(); Ok(result) } @@ -902,6 +903,16 @@ impl crate::backend::CmdBuf for CmdBuf { true } + unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor) { + if let Some((pool, start, end)) = &desc.timer_queries { + #[allow(irrefutable_let_patterns)] + if let crate::hub::QueryPool::Vk(pool) = pool { + self.write_timestamp_raw(pool.pool, *start); + self.end_query = Some((pool.pool, *end)); + } + } + } + unsafe fn dispatch( &mut self, pipeline: &Pipeline, @@ -931,6 +942,12 @@ impl crate::backend::CmdBuf for CmdBuf { ); } + unsafe fn end_compute_pass(&mut self) { + if let Some((pool, end)) = self.end_query.take() { + self.write_timestamp_raw(pool, end); + } + } + /// Insert a pipeline barrier for all memory accesses. unsafe fn memory_barrier(&mut self) { let device = &self.device.device; @@ -995,13 +1012,13 @@ impl crate::backend::CmdBuf for CmdBuf { ); } - unsafe fn clear_buffer(&self, buffer: &Buffer, size: Option) { + unsafe fn clear_buffer(&mut self, buffer: &Buffer, size: Option) { let device = &self.device.device; 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) { + unsafe fn copy_buffer(&mut self, src: &Buffer, dst: &Buffer) { let device = &self.device.device; let size = src.size.min(dst.size); device.cmd_copy_buffer( @@ -1012,7 +1029,7 @@ impl crate::backend::CmdBuf for CmdBuf { ); } - unsafe fn copy_image_to_buffer(&self, src: &Image, dst: &Buffer) { + unsafe fn copy_image_to_buffer(&mut self, src: &Image, dst: &Buffer) { let device = &self.device.device; device.cmd_copy_image_to_buffer( self.cmd_buf, @@ -1035,7 +1052,7 @@ impl crate::backend::CmdBuf for CmdBuf { ); } - unsafe fn copy_buffer_to_image(&self, src: &Buffer, dst: &Image) { + unsafe fn copy_buffer_to_image(&mut self, src: &Buffer, dst: &Image) { let device = &self.device.device; device.cmd_copy_buffer_to_image( self.cmd_buf, @@ -1058,7 +1075,7 @@ impl crate::backend::CmdBuf for CmdBuf { ); } - unsafe fn blit_image(&self, src: &Image, dst: &Image) { + unsafe fn blit_image(&mut self, src: &Image, dst: &Image) { let device = &self.device.device; device.cmd_blit_image( self.cmd_buf, @@ -1106,13 +1123,7 @@ impl crate::backend::CmdBuf for CmdBuf { } unsafe fn write_timestamp(&mut self, pool: &QueryPool, query: u32) { - let device = &self.device.device; - device.cmd_write_timestamp( - self.cmd_buf, - vk::PipelineStageFlags::COMPUTE_SHADER, - pool.pool, - query, - ); + self.write_timestamp_raw(pool.pool, query); } unsafe fn begin_debug_label(&mut self, label: &str) { @@ -1130,6 +1141,18 @@ impl crate::backend::CmdBuf for CmdBuf { } } +impl CmdBuf { + unsafe fn write_timestamp_raw(&mut self, pool: vk::QueryPool, query: u32) { + let device = &self.device.device; + device.cmd_write_timestamp( + self.cmd_buf, + vk::PipelineStageFlags::COMPUTE_SHADER, + pool, + query, + ); + } +} + impl crate::backend::DescriptorSetBuilder for DescriptorSetBuilder { fn add_buffers(&mut self, buffers: &[&Buffer]) { self.buffers.extend(buffers.iter().map(|b| b.buffer)); diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index 70023af..abe6ae1 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -6,7 +6,7 @@ use clap::{App, Arg}; use piet_gpu_hal::{BufferUsage, Error, Instance, InstanceFlags, Session}; -use piet_gpu::{test_scenes, PietGpuRenderContext, Renderer}; +use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, Renderer}; const WIDTH: usize = 2048; const HEIGHT: usize = 1536; @@ -243,7 +243,11 @@ fn main() -> Result<(), Error> { if matches.is_present("flip") { scale = -scale; } - test_scenes::render_svg(&mut ctx, input, scale); + let xml_str = std::fs::read_to_string(input).unwrap(); + let start = std::time::Instant::now(); + let svg = PicoSvg::load(&xml_str, scale).unwrap(); + println!("parsing time: {:?}", start.elapsed()); + test_scenes::render_svg(&mut ctx, &svg); } else { test_scenes::render_scene(&mut ctx); } diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 3ca0742..1642026 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -2,7 +2,7 @@ use piet::kurbo::Point; use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; use piet_gpu_hal::{CmdBuf, Error, ImageLayout, Instance, Session, SubmittedCmdBuf}; -use piet_gpu::{test_scenes, PietGpuRenderContext, Renderer}; +use piet_gpu::{test_scenes, PicoSvg, PietGpuRenderContext, Renderer}; use clap::{App, Arg}; @@ -29,6 +29,25 @@ fn main() -> Result<(), Error> { ) .get_matches(); + // Collect SVG if input + let svg = match matches.value_of("INPUT") { + Some(file) => { + let mut scale = matches + .value_of("scale") + .map(|scale| scale.parse().unwrap()) + .unwrap_or(8.0); + if matches.is_present("flip") { + scale = -scale; + } + let xml_str = std::fs::read_to_string(file).unwrap(); + let start = std::time::Instant::now(); + let svg = PicoSvg::load(&xml_str, scale).unwrap(); + println!("parsing time: {:?}", start.elapsed()); + Some(svg) + } + None => None, + }; + let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_inner_size(winit::dpi::LogicalSize { @@ -51,7 +70,7 @@ fn main() -> Result<(), Error> { .map(|_| session.create_semaphore()) .collect::, Error>>()?; let query_pools = (0..NUM_FRAMES) - .map(|_| session.create_query_pool(8)) + .map(|_| session.create_query_pool(12)) .collect::, Error>>()?; let mut cmd_bufs: [Option; NUM_FRAMES] = Default::default(); let mut submitted: [Option; NUM_FRAMES] = Default::default(); @@ -93,29 +112,23 @@ fn main() -> Result<(), Error> { if !ts.is_empty() { info_string = format!( "{:.3}ms :: e:{:.3}ms|alloc:{:.3}ms|cp:{:.3}ms|bd:{:.3}ms|bin:{:.3}ms|cr:{:.3}ms|r:{:.3}ms", - ts[6] * 1e3, + ts[10] * 1e3, ts[0] * 1e3, (ts[1] - ts[0]) * 1e3, (ts[2] - ts[1]) * 1e3, - (ts[3] - ts[2]) * 1e3, (ts[4] - ts[3]) * 1e3, - (ts[5] - ts[4]) * 1e3, (ts[6] - ts[5]) * 1e3, + (ts[8] - ts[7]) * 1e3, + (ts[10] - ts[9]) * 1e3, ); } } let mut ctx = PietGpuRenderContext::new(); - 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; - } - test_scenes::render_svg(&mut ctx, input, scale); - } else { + let test_blend = false; + if let Some(svg) = &svg { + test_scenes::render_svg(&mut ctx, svg); + } else if test_blend { use piet_gpu::{Blend, BlendMode::*, CompositionMode::*}; let blends = [ Blend::new(Normal, SrcOver), @@ -151,6 +164,8 @@ fn main() -> Result<(), Error> { let blend = blends[mode % blends.len()]; test_scenes::render_blend_test(&mut ctx, current_frame, blend); info_string = format!("{:?}", blend); + } else { + test_scenes::render_anim_frame(&mut ctx, current_frame); } render_info_string(&mut ctx, &info_string); if let Err(e) = renderer.upload_render_ctx(&mut ctx, frame_idx) { diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index adbedfd..3abb2e0 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -306,7 +306,7 @@ void main() { is_blend = (blend != BlendComp_default); } include_tile = tile.tile.offset != 0 || (tile.backdrop == 0) == is_clip - || (is_clip && is_blend); + || is_blend; } if (include_tile) { uint el_slice = el_ix / 32; diff --git a/piet-gpu/shader/gen/binning.dxil b/piet-gpu/shader/gen/binning.dxil index 4a4f0734c4735cdf4f37ec0ade7e7e7cacccabbc..3050aa83bdb31b0655c4e6a2c8d80987840b6532 100644 GIT binary patch delta 2232 zcmYjSeNYo;8h6cK2&oPwm@CLW3vkQ1*X;+ahX zX{u1dt!){wy&zcW6njA}jvYFaK(6MhT*cBGu0vb6<8FFa&jP16(>boaU5WJepWWyA z{hsG}pWpku@A`B6InvVF;*z3!w?2Q$@9B+keEFa~aC&WAI*H;&bHVg;<~M#gcw6;w zn5oFVwusjAZ2z+Fyv1i;jZm#6fXzp{ypslK*rY-Xfbg*4_Z)_J`mjN(d-?Da1C*#x zlnx`nRV2m%6t=K-F$s8KgIF``0Twp2w&htGkYS4oeF{PPu;KSaVE+##AwPzB1Z=iw zmZ($pOXEb9o^E04vme1Fyb22T5+7g0BRyU;EfGq?|7--I3lMn%q1|yne2B~4P5|&z z_8rf!^ca~!tjwlFf@CUs$S3M2!JMnpB)1X-ZywK;z`2X5=!FT3`mHV!K^!Dif;W>1 zLXe80hKt!*GtCYU@+TM?O-1SVEq<4UZ>ZfjG33pl?iGyOoLy}{+P2HtF`U^p%#SOH zVh-lM_DAH_UPSj$cl|1_I7KSGu3kw+kJ#vD;)7UhJ7y;6fXjdd0aot*@Wb(DB1B?- zYTAS}07L>Weoo?tOT;}pFYMw@GEa1gDer?bf^)8MnaVQhh=QV+I#Hk^f!@}3Jc0r?;8NDb& zQLI`tR1LMtDz#R2)KG+?qApQ04#qB0*yzYO<;YZHF*v_FYTwJ*=4hzK8@V6K^t&M* zO%gOzFj-8%KJ5Sw%6zO=AoBl`on3wSL zVSnDzrvE!S<`h1{)Qhui--1XVh~U@za z=}r|D1dtwQM=#}}UFP@|QH9b`16P53@%eK_o9$Pwmt?_Y4%@yWx51F@zwA9ji})}w zXn}Kp6xO2^)?78%+Q>vmqHQ9vDc{=4NnBm)hCl5g5?HnDN%DWwCz6l|w)MsC-#vqW z;SNBov;{xkE$-BY>2>|E`iZ#fox9eHpPme|DkSilgqMMAvU4vk(OU0vnXP)c))Hf= z!t-`Z^2TFsfO>wV-)-Qt8EtNRT?pO*=c^ZjRSTt0U*I*#GNB|SnypMCjRwg-@a0aA zq_9u&ZEKY8mv`W9B9~hrG1^t-EAdIDs_zG@gU+1~$lr+ge6~e;c`Dfv1#}szq_h>%8)u!TjTi>t0GK-W8oCvP#yUKo2;FwBF@47pqm*XYhUEXDdk=f%-7s(VAgk@%(n9Ohg>>fz9a zfaPOL^4KF`&Rl$phzN6TBIcriTlrY!^4~%VNVm``rLiHEEvA7M+xQQE50^oBnG}`kFGO8+|e%%jK`!H+nVhv!m{_^sNoCvaD-c zzBe4GgJMg+)7^H5mz*b*_hnF+2tFaSJaXvjZfz^{pJ@QlC6{A*j4Z)5hqB?`crNNd5}T9X#F0mPIP%0}az001c}!m6$fSxRr~ZLs dmz;qce3!I+Ud+91Ubk4zWz-3S?4Kh1{0|^9yNdt- delta 2206 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 bf_I0a-~kj&4?w|p=c3@hamheYpZ@*_0bBw> delta 6967 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 diff --git a/piet-gpu/shader/gen/coarse.hlsl b/piet-gpu/shader/gen/coarse.hlsl index 1e610ec..04529bb 100644 --- a/piet-gpu/shader/gen/coarse.hlsl +++ b/piet-gpu/shader/gen/coarse.hlsl @@ -931,23 +931,14 @@ void comp_main() { _1701 = _1692; } - bool _1708; - if (!_1701) - { - _1708 = is_clip && is_blend; - } - else - { - _1708 = _1701; - } - include_tile = _1708; + include_tile = _1701 || is_blend; } if (include_tile) { uint el_slice = el_ix / 32u; uint el_mask = 1u << (el_ix & 31u); - uint _1728; - InterlockedOr(sh_bitmaps[el_slice][(y * 16u) + x], el_mask, _1728); + uint _1723; + InterlockedOr(sh_bitmaps[el_slice][(y * 16u) + x], el_mask, _1723); } } GroupMemoryBarrierWithGroupSync(); @@ -976,9 +967,9 @@ void comp_main() { uint param_25 = element_ref_ix; bool param_26 = mem_ok; - TileRef _1805 = { sh_tile_base[element_ref_ix] + (((sh_tile_stride[element_ref_ix] * tile_y) + tile_x) * 8u) }; + TileRef _1800 = { sh_tile_base[element_ref_ix] + (((sh_tile_stride[element_ref_ix] * tile_y) + tile_x) * 8u) }; Alloc param_27 = read_tile_alloc(param_25, param_26); - TileRef param_28 = _1805; + TileRef param_28 = _1800; Tile tile_1 = Tile_read(param_27, param_28); uint drawmonoid_base_2 = drawmonoid_start + (4u * element_ix_2); uint scene_offset_1 = _260.Load((drawmonoid_base_2 + 2u) * 4 + 8); @@ -993,11 +984,11 @@ void comp_main() Alloc param_29 = cmd_alloc; CmdRef param_30 = cmd_ref; uint param_31 = cmd_limit; - bool _1853 = alloc_cmd(param_29, param_30, param_31); + bool _1848 = alloc_cmd(param_29, param_30, param_31); cmd_alloc = param_29; cmd_ref = param_30; cmd_limit = param_31; - if (!_1853) + if (!_1848) { break; } @@ -1008,10 +999,10 @@ void comp_main() write_fill(param_32, param_33, param_34, param_35); cmd_ref = param_33; uint rgba = _1372.Load(dd_1 * 4 + 0); - CmdColor _1876 = { rgba }; + CmdColor _1871 = { rgba }; Alloc param_36 = cmd_alloc; CmdRef param_37 = cmd_ref; - CmdColor param_38 = _1876; + CmdColor param_38 = _1871; Cmd_Color_write(param_36, param_37, param_38); cmd_ref.offset += 8u; break; @@ -1021,11 +1012,11 @@ void comp_main() Alloc param_39 = cmd_alloc; CmdRef param_40 = cmd_ref; uint param_41 = cmd_limit; - bool _1894 = alloc_cmd(param_39, param_40, param_41); + bool _1889 = alloc_cmd(param_39, param_40, param_41); cmd_alloc = param_39; cmd_ref = param_40; cmd_limit = param_41; - if (!_1894) + if (!_1889) { break; } @@ -1052,11 +1043,11 @@ void comp_main() Alloc param_49 = cmd_alloc; CmdRef param_50 = cmd_ref; uint param_51 = cmd_limit; - bool _1958 = alloc_cmd(param_49, param_50, param_51); + bool _1953 = alloc_cmd(param_49, param_50, param_51); cmd_alloc = param_49; cmd_ref = param_50; cmd_limit = param_51; - if (!_1958) + if (!_1953) { break; } @@ -1086,11 +1077,11 @@ void comp_main() Alloc param_59 = cmd_alloc; CmdRef param_60 = cmd_ref; uint param_61 = cmd_limit; - bool _2064 = alloc_cmd(param_59, param_60, param_61); + bool _2059 = alloc_cmd(param_59, param_60, param_61); cmd_alloc = param_59; cmd_ref = param_60; cmd_limit = param_61; - if (!_2064) + if (!_2059) { break; } @@ -1103,27 +1094,27 @@ void comp_main() uint index = _1372.Load(dd_1 * 4 + 0); uint raw1 = _1372.Load((dd_1 + 1u) * 4 + 0); int2 offset_1 = int2(int(raw1 << uint(16)) >> 16, int(raw1) >> 16); - CmdImage _2103 = { index, offset_1 }; + CmdImage _2098 = { index, offset_1 }; Alloc param_66 = cmd_alloc; CmdRef param_67 = cmd_ref; - CmdImage param_68 = _2103; + CmdImage param_68 = _2098; Cmd_Image_write(param_66, param_67, param_68); cmd_ref.offset += 12u; break; } case 5u: { - bool _2117 = tile_1.tile.offset == 0u; - bool _2123; - if (_2117) + bool _2112 = tile_1.tile.offset == 0u; + bool _2118; + if (_2112) { - _2123 = tile_1.backdrop == 0; + _2118 = tile_1.backdrop == 0; } else { - _2123 = _2117; + _2118 = _2112; } - if (_2123) + if (_2118) { clip_zero_depth = clip_depth + 1u; } @@ -1132,11 +1123,11 @@ void comp_main() Alloc param_69 = cmd_alloc; CmdRef param_70 = cmd_ref; uint param_71 = cmd_limit; - bool _2135 = alloc_cmd(param_69, param_70, param_71); + bool _2130 = alloc_cmd(param_69, param_70, param_71); cmd_alloc = param_69; cmd_ref = param_70; cmd_limit = param_71; - if (!_2135) + if (!_2130) { break; } @@ -1154,11 +1145,11 @@ void comp_main() Alloc param_74 = cmd_alloc; CmdRef param_75 = cmd_ref; uint param_76 = cmd_limit; - bool _2163 = alloc_cmd(param_74, param_75, param_76); + bool _2158 = alloc_cmd(param_74, param_75, param_76); cmd_alloc = param_74; cmd_ref = param_75; cmd_limit = param_76; - if (!_2163) + if (!_2158) { break; } @@ -1169,10 +1160,10 @@ void comp_main() write_fill(param_77, param_78, param_79, param_80); cmd_ref = param_78; uint blend_1 = _1372.Load(dd_1 * 4 + 0); - CmdEndClip _2186 = { blend_1 }; + CmdEndClip _2181 = { blend_1 }; Alloc param_81 = cmd_alloc; CmdRef param_82 = cmd_ref; - CmdEndClip param_83 = _2186; + CmdEndClip param_83 = _2181; Cmd_EndClip_write(param_81, param_82, param_83); cmd_ref.offset += 8u; break; @@ -1207,17 +1198,17 @@ void comp_main() break; } } - bool _2233 = (bin_tile_x + tile_x) < _1005.Load(8); - bool _2242; - if (_2233) + bool _2228 = (bin_tile_x + tile_x) < _1005.Load(8); + bool _2237; + if (_2228) { - _2242 = (bin_tile_y + tile_y) < _1005.Load(12); + _2237 = (bin_tile_y + tile_y) < _1005.Load(12); } else { - _2242 = _2233; + _2237 = _2228; } - if (_2242) + if (_2237) { Alloc param_84 = cmd_alloc; CmdRef param_85 = cmd_ref; diff --git a/piet-gpu/shader/gen/coarse.msl b/piet-gpu/shader/gen/coarse.msl index abd636b..55812d4 100644 --- a/piet-gpu/shader/gen/coarse.msl +++ b/piet-gpu/shader/gen/coarse.msl @@ -954,22 +954,13 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M { _1701 = _1692; } - bool _1708; - if (!_1701) - { - _1708 = is_clip && is_blend; - } - else - { - _1708 = _1701; - } - include_tile = _1708; + include_tile = _1701 || is_blend; } if (include_tile) { uint el_slice = el_ix / 32u; uint el_mask = 1u << (el_ix & 31u); - uint _1728 = atomic_fetch_or_explicit((threadgroup atomic_uint*)&sh_bitmaps[el_slice][(y * 16u) + x], el_mask, memory_order_relaxed); + uint _1723 = atomic_fetch_or_explicit((threadgroup atomic_uint*)&sh_bitmaps[el_slice][(y * 16u) + x], el_mask, memory_order_relaxed); } } threadgroup_barrier(mem_flags::mem_threadgroup); @@ -1014,11 +1005,11 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M Alloc param_29 = cmd_alloc; CmdRef param_30 = cmd_ref; uint param_31 = cmd_limit; - bool _1853 = alloc_cmd(param_29, param_30, param_31, v_260, v_260BufferSize); + bool _1848 = alloc_cmd(param_29, param_30, param_31, v_260, v_260BufferSize); cmd_alloc = param_29; cmd_ref = param_30; cmd_limit = param_31; - if (!_1853) + if (!_1848) { break; } @@ -1041,11 +1032,11 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M Alloc param_39 = cmd_alloc; CmdRef param_40 = cmd_ref; uint param_41 = cmd_limit; - bool _1894 = alloc_cmd(param_39, param_40, param_41, v_260, v_260BufferSize); + bool _1889 = alloc_cmd(param_39, param_40, param_41, v_260, v_260BufferSize); cmd_alloc = param_39; cmd_ref = param_40; cmd_limit = param_41; - if (!_1894) + if (!_1889) { break; } @@ -1072,11 +1063,11 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M Alloc param_49 = cmd_alloc; CmdRef param_50 = cmd_ref; uint param_51 = cmd_limit; - bool _1958 = alloc_cmd(param_49, param_50, param_51, v_260, v_260BufferSize); + bool _1953 = alloc_cmd(param_49, param_50, param_51, v_260, v_260BufferSize); cmd_alloc = param_49; cmd_ref = param_50; cmd_limit = param_51; - if (!_1958) + if (!_1953) { break; } @@ -1106,11 +1097,11 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M Alloc param_59 = cmd_alloc; CmdRef param_60 = cmd_ref; uint param_61 = cmd_limit; - bool _2064 = alloc_cmd(param_59, param_60, param_61, v_260, v_260BufferSize); + bool _2059 = alloc_cmd(param_59, param_60, param_61, v_260, v_260BufferSize); cmd_alloc = param_59; cmd_ref = param_60; cmd_limit = param_61; - if (!_2064) + if (!_2059) { break; } @@ -1132,17 +1123,17 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M } case 5u: { - bool _2117 = tile_1.tile.offset == 0u; - bool _2123; - if (_2117) + bool _2112 = tile_1.tile.offset == 0u; + bool _2118; + if (_2112) { - _2123 = tile_1.backdrop == 0; + _2118 = tile_1.backdrop == 0; } else { - _2123 = _2117; + _2118 = _2112; } - if (_2123) + if (_2118) { clip_zero_depth = clip_depth + 1u; } @@ -1151,11 +1142,11 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M Alloc param_69 = cmd_alloc; CmdRef param_70 = cmd_ref; uint param_71 = cmd_limit; - bool _2135 = alloc_cmd(param_69, param_70, param_71, v_260, v_260BufferSize); + bool _2130 = alloc_cmd(param_69, param_70, param_71, v_260, v_260BufferSize); cmd_alloc = param_69; cmd_ref = param_70; cmd_limit = param_71; - if (!_2135) + if (!_2130) { break; } @@ -1173,11 +1164,11 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M Alloc param_74 = cmd_alloc; CmdRef param_75 = cmd_ref; uint param_76 = cmd_limit; - bool _2163 = alloc_cmd(param_74, param_75, param_76, v_260, v_260BufferSize); + bool _2158 = alloc_cmd(param_74, param_75, param_76, v_260, v_260BufferSize); cmd_alloc = param_74; cmd_ref = param_75; cmd_limit = param_76; - if (!_2163) + if (!_2158) { break; } @@ -1225,17 +1216,17 @@ kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device M break; } } - bool _2233 = (bin_tile_x + tile_x) < _1005.conf.width_in_tiles; - bool _2242; - if (_2233) + bool _2228 = (bin_tile_x + tile_x) < _1005.conf.width_in_tiles; + bool _2237; + if (_2228) { - _2242 = (bin_tile_y + tile_y) < _1005.conf.height_in_tiles; + _2237 = (bin_tile_y + tile_y) < _1005.conf.height_in_tiles; } else { - _2242 = _2233; + _2237 = _2228; } - if (_2242) + if (_2237) { Alloc param_84 = cmd_alloc; CmdRef param_85 = cmd_ref; diff --git a/piet-gpu/shader/gen/coarse.spv b/piet-gpu/shader/gen/coarse.spv index fdc10a08e6089784d240cac387f52eef71f95253..6d33ee70c7b65cf2d8b8fef43e5b5ea63b20cbdf 100644 GIT binary patch delta 12338 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 delta 12561 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 delta 2961 zcmbtWdsGu=7N5!FF?qox;V}W!37{KAA#Xqt5`v0RXj%nBQ9D3EYyl%~Ij)C2c@QB; zQc?nl*1GXnyH=?i)OMxY^#lP)DXpm}B1^lVtn0RP7ipJ*b@%K963^M5vw!XUG5PK{ z_wl>GdvCsPTJ~7R&(dY8fuB7fA(waYe+Q?&lao3e(K^n)%zm)l_~U`svvgaxTR8xL z0~DZ`3;=!^Ak-1ULQm{xsy$x|SO8FP1*FqRj1m!G zUR9-;0k3GO6hF2Ngd^?lT|1YFTjdMdc{C5)q=A;ZjcjZfNVfQU=tstwz4Fz+B`a%~)hY>KfpM?7HHg3Cd6b`xpm;0F_8Ne%N6&pK>cmqTe~noID#wG7&@A9A3?DPP{4c!Sg`Nix z61(2S00ch#zXh=fK*-S4%8AKk^=$Y})K~-+NBaT$EPlY3alodbqpW1|0zh@H<}wXj z-YgQ@%XYiCxac!ujH*9#>u$;v5J!dmRqyG`7|jH@XX&8UlIpR7BqN6Eqyla?V(Tl< z1n!tZM8!sbH?`-ql9R}*#3vEp+&pAks#_aR_=Ks_`0KV7lxRVib_yj5UhQoZYr$;V zJA_~`){Hia#}Qf@NWH?+a9d++q;C@ViIpe2frU7KIc-2qe)@(cS6Iw)7K#cX}dHmHk%W_%8#@IoijDC`+T+#L-K2gTa;q885 z_mU9NeiDDU=CO-({~99~+RTaq$UVTY{SMNE%N=)h_Pfd^19yAy!=Ko9(I%rD8Nt~j z*-cT;_aEBtVoff&W2D?ZqHT)R1ogjVj|iJQa^#+#eNWe+zPv?&__HM2H&u9*8oIRq}z%KK+kKrLuEc(6>Iy=$iN+WzvkV+fk zNVn1JE#?n|qXqT9U@!!ss95Rm;HHd`5*caE?}G!8iMeqOJ(7@r$tJt~otp85@99|U8q=lrC%PoxvETyHJ}9qo+ID30?re`x`PN7I9tBPK(~ z;HQ4yxi8KkrMI+maA_0!4!)1?5(?>ALA5A>f%7%2d6m$+K;i_jGPmSTC6mJVMCiEI zjjH^TZCOdJb#1#Sc_VdmS5M`$BlS4b{uFgY9YmiEOZ?74p^}C zrN3VHyIVWRf#vy64K{K_pQ*Nt{+@}k$>9}5m4TYcZ-gnXan}*(TN1u4+&i_#OF%34 z(`d`7v80=q^N+l{~bpHFiF57t# z$O&Y-HkI!D3GVYJdkp3Uk~Lh+^=4(5CC{_fc*%jv-ge3snw2?zwo$1mP`0O*oUHhA zC&6|~=6znP{2XhlWE3)@C~-SW(!9 zrI($ms~y5<3%XR+Y*)2ts>G7&9UzO0%@j&)1q{vWaL+)B#M*rDRb<7*%kv%KzJW{I zh_>@z2z*1fYhhWc4y13uYD|y7X2sxJ&Flt4EsGvGKfVDdJS>pz>iN`TRYAm|fUkE- z9&kH{+>(SVlE>A{-11W_8!97CZ}6rxL7k}j3|==ZS$8(^9#}{j3>u{mOCE7QvCT-5 zzL!iBMsK)+MpQ*oU7~6T7W1Uyhlzg$3mZRMGBTieF;DspcgQA^635W0L95LQ zX>X$Hb6CDZDvwM0Em&y(V=z?N4(Z>y<2JoC`H=L7YM%R_)}T?nA{k0jeF3LBrKu%J zTfo8>pC|R2Q##9?wz;Jnu1M#r!`<>OmLZsxVuLzKbqn4&EZulEX&+da7XXOjRm0Lb z?laqrG~;{eGs!kR`>L=^a-y+xRoe;egJIVHC%qDI6IS7{g(QQ4`W;` HaR2@fw5$C> diff --git a/piet-gpu/shader/gen/draw_reduce.dxil b/piet-gpu/shader/gen/draw_reduce.dxil index c101fc8c7f1997dba7d8584f3ec61030fb5e0059..be69aad2781f64f94fa4a1d109e7b980e3bf0fdb 100644 GIT binary patch delta 860 zcmZ8gU1$_n6h5<=nZ22vB-_N%yIF-9B$|YfY}Sq2+Er(gRHA5DVGSD*ytA6LlpuMq zSZzvX|Kbv}vKlp7O*_GeTj;}T0?lqzCMqH*Ws~}_C^msoA3~v(zWC6G&eV#~`*1kl zch1N8&b{2MG%dM0!|ffbTfW}^Q&UR#}L`s%8> zLvZfg_P8^=d+!AVK!AZd8-RtuY-pGqd6IwHJ>I~b zmiWH?bYBoLU2;LrrlB}V>47p?Wlb%d#HM(Bk=v)b*@xmLH1P?ELBL(z-kVYhGIQB| zUg<@jnt8k!s7d_(jR=eM)c0GPI%l0r#@?Ul0hFzvLzF>JiCweF8~J@s-{&#ts-ekq z&%lFRRP?4_1P@{I#rQgAGeyRCGE&Wf4c@?n*S{?v$o}Z_7xE2?eP-D)W*gR`8wYV) zBm*L1yKx|P^T0;79Aac8Mftw`t}Q~W!XsDxaw~q*Apd`Alh+^BLTSlXZw~1m;aF%E{EdVWQiD zS_{sCbH!ou$Kjs-Ei`80?p7I`biBUmC(-eY_#_x{+X5)=ke}u32NSiQ2`4n+hiK1h zS*QxVmOrQ}ZQ(rl78ul5Toxk}5|32jtBOU;Qqg2YLanB)1pir;;m; zVKOHL8Smvjag-z%)~Z58H3 zkcz~*h4goHsbCMZ9A;?yPV#G&29hzvmwyfDHWr delta 906 zcmZ9LQA}G^7{|YRxxKx;_qG(T0 z|8xF1-*-;VtUYhH_J{g>@V`M|&R?_M*FJuK__^ZMq4$1Ce)Z1K3-#wJ{h_1JW*Gnx zG~gfsY7BV05ho<<_xVcS+j)R}a~;gDgs#)|#NO;a{oRSlHujv|a68cWRDezgBx;sR zZ8ETJg|Cl*Iflb;BLLszI_r;{sOonvLD%7(cakOwHdQ0%<9$9bQNpImbG}}zZIpIe z7_Lx+>R`sdjMxt-Sw*?fD*6H=!hQ}MjK+K6(L<{va_*Rk5~l^&P!cvpPtA;ME98o_ za=E-5`*!Dpf#A-7yM_5oeNe1?cxUy}&7xn|mvIcgOGtmjvnPN49| zL-%Z9>(-Re=2L#!f4uvT;6Zn=+uc(58HcBe>vw+i-n>*?U-j#HVQz~XBjc$=^Bbru zs)E4K6R5ZR>9J;72eMclBRbED;y6OU$cY5aqOo218vP6!T-(kNq$HsJn5*Iau4yA! zQbf?>(Y$oJnPza>c$N5s+B($-yqkL2NbiyIGy3Zp|7B0z73p4B7IUOgTjk{h8<${N zKzT4%Il3LR6>NFP&vWO=b~S11@PLs@K3)Hfz-@?~c2I0`FO%dv8zt?D)ZSB;HzmuJ zMDUu&q96R-)6h`qXIF zV*0_JVQ(n%XF806=-otP#-hBKPs@xe1IgNSk)cOV=NM*G6+g=1KB+RVB-oO0SFAO? z3voN9-D)2;4b50^SxzKH>Y^rEtVnALfx3mmQQ&P_gs<}ppy&8yBkw-@u%@(+47UAM z9!H({nU+$9d6o4#%|(?f7FdgPYhu@*G8A)c!r8Qt;}%M6OQrm;KWkVhuuHAZ1B(T2 eagFVo|8L>CVX?##j~%*E;%-#gIf}ub2;grG!X&-` diff --git a/piet-gpu/shader/gen/draw_root.dxil b/piet-gpu/shader/gen/draw_root.dxil index 873fa29da1ffca54fc149ad5b31f42a47e79f5ba..4ea23f7699a63ef9a4dcac871e1f234a5ea5b0ef 100644 GIT binary patch delta 855 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 diff --git a/piet-gpu/shader/gen/kernel4_gray.dxil b/piet-gpu/shader/gen/kernel4_gray.dxil index a594d502fa09859dac2cb640e38b2553cc692204..046045f51c54cb050580f10b12ea5046b1b56a4f 100644 GIT binary patch delta 10526 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 DTaNA( diff --git a/piet-gpu/shader/gen/tile_alloc.dxil b/piet-gpu/shader/gen/tile_alloc.dxil index 7b130e013427d747d2d4199badc1dcd7fb8e8297..7759910ca527b4ff12fbe9ba3ee03fdb6a428ca7 100644 GIT binary patch delta 1180 zcmYLJe@q)?82;{hcfE2QTuVx?EsQG^>|EWhY$A2h^a@T;HXDOk`C%-DDH=e=HnZs> zZGm#j8ZBP8q0?E0b4^yv_%R%bGi`wdHh&aEDN0DeEkRA@Vy2skAzrhh-(T-P;vA z^KO}~qOzF)zz$IG6AVD|4g@$UFsg*Sr8K;(l>#s~62TYYTLhWpQiF4_Q{p!Inuuo)o}v9vF$;nc*xEvk*2m4HZLCEfB7!C9 zueKgKw*2GLTw;0c`qG;DS}b~#+H?MLWay+zdR0mfhMZOjinickyW5H_cvc9JG0q%m zpfL?rM2n34N9aQ^JiuQ=X+_{y-^#due0Js*d-eSI?ALoFB>;@%V%0Ej&#-iu<^h`z z3d}PUguCh6v&_5<2LNgUARm(ex7~M{eKY{6xd_L?PMT_LwJTn;V-1YM2WT8y)YN&@ zAxPz=D1f^o2gEV;e<7*yQnZkTlwt?d714ML5%Z>x{bZPflAYQP+p(v!c`d2z_{7z| z7-_Nqf01~f1t>zUcODQI#H|2*i}`>ABS_SY(#T&x`WSMI#3nXHHC?R4^-YnqjBz-V zN|y97R)O^=!7uz7IZ2w)Hv)=zC?(-^+M6--Lb9mT)uNj>Bn&3R^<=0cD$K6)H5UYg zopE93@RVEcXYZs9NT4=$`1{F?F8hd2eOSaGED}a%YJSH4P#GCOBBEQ$b@~dR8&=Iv z=3|)9Ai#C%5hZ9Oa;9II3v>lmFVeWzS6T4F6&WtZfs#wP_$P6%7%A@jadw+4JPheegsZ+ZS zI#)c2oUr{Aw8g!G)s%>tzWwEW0q)=xStklurq8;X-LBPH9J<|kl(8gf22n7{D$5-^ zwcue&90a+%ORmILbD}HR&+upEG<>Ot56jhvq*vd=FUz%1^u5fjFD7zZPnp$0&{e)S zkVXd&w@C&|oX0PQ)5kogow80$7bj#QNP%0~=hiq@BshGewKp?gHab-^`c&;` zcSTiw7PCasjm!;eb;Jkw E0q+okRR910 delta 1185 zcmZ8hYfM{Z82(N>)fywCeQ zKR&-Ypw1|_n#|yT4`Fk-knujR?Bc+0q_X1Oo&r1B_DjAp7&TJ| zxS%%;HEd}W{9+^Y4$&wwrdKV8VI&6GIH*@e!f=g^F~*(GYYo=H`EwCd6=5V+OX}!; za?4Pj4r-y_UEXLN{|TKW7Wo`Y9R%kh^TG(`iIzCcUCrcrSCwn(mp^~l8Nr@ZHtbY# zb^Xuff7f0(E4(^O^tSFg+8xQh8nwFJ46!tyj@A=41=cd({4{DK^n}BLwOIOwR_9jp zhGy;#Icwl)WyG~mL`|Ih&@~~3b;JjA;M2Ny+T;Rv2cd{B2vM7m@PER z-K?_#jI}El;{%T^a2!NWE)t^KkYM(USJ0nfv9 zao1%3hN+o;se-K{5^l4WWHSBvs6qJIFr){N_@>To3w9!ll0!*m&~4vj+D%|MhudHR z=^^*kXTTr1?@_0qwAYjAGxFWv>xgo#os84oSKl=zW0Tr(2;TaG`2-r0yMyU5B*8Z- z=!qqN8E;BdjVtm)zS$VJ8P0@Ro~A%YUN5VU$Lgb3OzL6do?wcPbQ%}e>BA)r_!j|; z(XdC;)DBG5A1H1DMax6OYq=Q4!h!>4H7WtExIEgnOalWXhR;j;wmD?&t)#!6S>X1h zOjAXog5GUUcFa>2-!#A=ta9z|op^tlUz!y+Cn@zh zMG~<60Pfb>JL3k_zPj%Tr{rSXxPpzyF*KT(d9sIJc{u_0rhW^hdKGm~q~Z9IC`5d$ zez!9vg89S*sr(ywAN135+BSj|cphM9v|R0&qiQV3bKMkqlYvn?W(jAVSE@{QRb`H} zxD-G%3C{}~B~|tkB`Ad4&~`TUkywI-0Q$e;6+ zZRl*AN y({e>iD`%D}8khR2PrpvZ(xj@}wmU)Gn9-5(u~O|l8mUGd^A372$o?Jwy?+3ac!wAO diff --git a/piet-gpu/src/encoder.rs b/piet-gpu/src/encoder.rs index 8f21485..2f4b85e 100644 --- a/piet-gpu/src/encoder.rs +++ b/piet-gpu/src/encoder.rs @@ -37,147 +37,6 @@ pub struct Encoder { n_clip: u32, } -#[derive(Copy, Clone, Debug)] -pub struct EncodedSceneRef<'a, T: Copy + Pod> { - pub transform_stream: &'a [T], - pub tag_stream: &'a [u8], - pub pathseg_stream: &'a [u8], - pub linewidth_stream: &'a [f32], - pub drawtag_stream: &'a [u32], - pub drawdata_stream: &'a [u8], - pub n_path: u32, - pub n_pathseg: u32, - pub n_clip: u32, - pub ramp_data: &'a [u32], -} - -impl<'a, T: Copy + Pod> EncodedSceneRef<'a, T> { - /// Return a config for the element processing pipeline. - /// - /// This does not include further pipeline processing. Also returns the - /// beginning of free memory. - pub fn stage_config(&self) -> (Config, usize) { - // Layout of scene buffer - let drawtag_offset = 0; - let n_drawobj = self.n_drawobj(); - let n_drawobj_padded = align_up(n_drawobj, DRAW_PART_SIZE as usize); - let drawdata_offset = drawtag_offset + n_drawobj_padded * DRAWTAG_SIZE; - let trans_offset = drawdata_offset + self.drawdata_stream.len(); - let n_trans = self.transform_stream.len(); - let n_trans_padded = align_up(n_trans, TRANSFORM_PART_SIZE as usize); - let linewidth_offset = trans_offset + n_trans_padded * TRANSFORM_SIZE; - let n_linewidth = self.linewidth_stream.len(); - let pathtag_offset = linewidth_offset + n_linewidth * LINEWIDTH_SIZE; - let n_pathtag = self.tag_stream.len(); - let n_pathtag_padded = align_up(n_pathtag, PATHSEG_PART_SIZE as usize); - let pathseg_offset = pathtag_offset + n_pathtag_padded; - - // Layout of memory - let mut alloc = 0; - let trans_alloc = alloc; - alloc += trans_alloc + n_trans_padded * TRANSFORM_SIZE; - let pathseg_alloc = alloc; - alloc += pathseg_alloc + self.n_pathseg as usize * PATHSEG_SIZE; - let path_bbox_alloc = alloc; - let n_path = self.n_path as usize; - alloc += path_bbox_alloc + n_path * PATH_BBOX_SIZE; - let drawmonoid_alloc = alloc; - alloc += n_drawobj_padded * DRAWMONOID_SIZE; - let anno_alloc = alloc; - alloc += n_drawobj * ANNOTATED_SIZE; - let clip_alloc = alloc; - let n_clip = self.n_clip as usize; - const CLIP_SIZE: usize = 4; - alloc += n_clip * CLIP_SIZE; - let clip_bic_alloc = alloc; - const CLIP_BIC_SIZE: usize = 8; - // This can round down, as we only reduce the prefix - alloc += (n_clip / CLIP_PART_SIZE as usize) * CLIP_BIC_SIZE; - let clip_stack_alloc = alloc; - const CLIP_EL_SIZE: usize = 20; - alloc += n_clip * CLIP_EL_SIZE; - let clip_bbox_alloc = alloc; - const CLIP_BBOX_SIZE: usize = 16; - alloc += align_up(n_clip as usize, CLIP_PART_SIZE as usize) * CLIP_BBOX_SIZE; - let draw_bbox_alloc = alloc; - alloc += n_drawobj * DRAW_BBOX_SIZE; - let drawinfo_alloc = alloc; - // TODO: not optimized; it can be accumulated during encoding or summed from drawtags - const MAX_DRAWINFO_SIZE: usize = 44; - alloc += n_drawobj * MAX_DRAWINFO_SIZE; - - let config = Config { - n_elements: n_drawobj as u32, - n_pathseg: self.n_pathseg, - pathseg_alloc: pathseg_alloc as u32, - anno_alloc: anno_alloc as u32, - trans_alloc: trans_alloc as u32, - path_bbox_alloc: path_bbox_alloc as u32, - drawmonoid_alloc: drawmonoid_alloc as u32, - clip_alloc: clip_alloc as u32, - clip_bic_alloc: clip_bic_alloc as u32, - clip_stack_alloc: clip_stack_alloc as u32, - clip_bbox_alloc: clip_bbox_alloc as u32, - draw_bbox_alloc: draw_bbox_alloc as u32, - drawinfo_alloc: drawinfo_alloc as u32, - n_trans: n_trans as u32, - n_path: self.n_path, - n_clip: self.n_clip, - trans_offset: trans_offset as u32, - linewidth_offset: linewidth_offset as u32, - pathtag_offset: pathtag_offset as u32, - pathseg_offset: pathseg_offset as u32, - drawtag_offset: drawtag_offset as u32, - drawdata_offset: drawdata_offset as u32, - ..Default::default() - }; - (config, alloc) - } - - pub fn write_scene(&self, buf: &mut BufWrite) { - buf.extend_slice(&self.drawtag_stream); - let n_drawobj = self.drawtag_stream.len(); - buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE); - buf.extend_slice(&self.drawdata_stream); - buf.extend_slice(&self.transform_stream); - let n_trans = self.transform_stream.len(); - buf.fill_zero(padding(n_trans, TRANSFORM_PART_SIZE as usize) * TRANSFORM_SIZE); - buf.extend_slice(&self.linewidth_stream); - buf.extend_slice(&self.tag_stream); - let n_pathtag = self.tag_stream.len(); - buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize)); - buf.extend_slice(&self.pathseg_stream); - } - - /// The number of draw objects in the draw object stream. - pub(crate) fn n_drawobj(&self) -> usize { - self.drawtag_stream.len() - } - - /// The number of paths. - pub(crate) fn n_path(&self) -> u32 { - self.n_path - } - - /// The number of path segments. - pub(crate) fn n_pathseg(&self) -> u32 { - self.n_pathseg - } - - pub(crate) fn n_transform(&self) -> usize { - self.transform_stream.len() - } - - /// The number of tags in the path stream. - pub(crate) fn n_pathtag(&self) -> usize { - self.tag_stream.len() - } - - pub(crate) fn n_clip(&self) -> u32 { - self.n_clip - } -} - /// A scene fragment encoding a glyph. /// /// This is a reduced version of the full encoder. @@ -471,21 +330,6 @@ impl Encoder { self.n_path += glyph.n_path; self.n_pathseg += glyph.n_pathseg; } - - pub(crate) fn scene_ref(&self) -> EncodedSceneRef { - EncodedSceneRef { - transform_stream: &self.transform_stream, - tag_stream: &self.tag_stream, - pathseg_stream: &self.pathseg_stream, - linewidth_stream: &self.linewidth_stream, - drawtag_stream: &self.drawtag_stream, - drawdata_stream: &self.drawdata_stream, - n_path: self.n_path, - n_pathseg: self.n_pathseg, - n_clip: self.n_clip, - ramp_data: &[], - } - } } fn align_up(x: usize, align: usize) -> usize { diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index b3ead90..773007d 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -10,10 +10,7 @@ mod text; use std::convert::TryInto; -use bytemuck::Pod; - pub use blend::{Blend, BlendMode, CompositionMode}; -pub use encoder::EncodedSceneRef; pub use render_ctx::PietGpuRenderContext; pub use gradient::Colrv1RadialGradient; @@ -21,11 +18,11 @@ use piet::kurbo::Vec2; use piet::{ImageFormat, RenderContext}; use piet_gpu_hal::{ - include_shader, BindType, Buffer, BufferUsage, CmdBuf, DescriptorSet, Error, Image, - ImageLayout, Pipeline, QueryPool, Session, + include_shader, BindType, Buffer, BufferUsage, CmdBuf, ComputePassDescriptor, DescriptorSet, + Error, Image, ImageLayout, Pipeline, QueryPool, Session, }; -use pico_svg::PicoSvg; +pub use pico_svg::PicoSvg; use stages::{ClipBinding, ElementBinding, ElementCode}; use crate::stages::{ClipCode, Config, ElementStage}; @@ -358,27 +355,16 @@ impl Renderer { render_ctx: &mut PietGpuRenderContext, buf_ix: usize, ) -> Result<(), Error> { - let mut scene = render_ctx.encoded_scene(); - let ramp_data = render_ctx.get_ramp_data(); - scene.ramp_data = &ramp_data; - self.upload_scene(&scene, buf_ix) - } - - pub fn upload_scene( - &mut self, - scene: &EncodedSceneRef, - buf_ix: usize, - ) -> Result<(), Error> { - let (mut config, mut alloc) = scene.stage_config(); - let n_drawobj = scene.n_drawobj(); + let (mut config, mut alloc) = render_ctx.stage_config(); + let n_drawobj = render_ctx.n_drawobj(); // TODO: be more consistent in size types - let n_path = scene.n_path() as usize; + let n_path = render_ctx.n_path() as usize; self.n_paths = n_path; - self.n_transform = scene.n_transform(); - self.n_drawobj = scene.n_drawobj(); - self.n_pathseg = scene.n_pathseg() as usize; - self.n_pathtag = scene.n_pathtag(); - self.n_clip = scene.n_clip(); + self.n_transform = render_ctx.n_transform(); + self.n_drawobj = render_ctx.n_drawobj(); + self.n_pathseg = render_ctx.n_pathseg() as usize; + self.n_pathtag = render_ctx.n_pathtag(); + self.n_clip = render_ctx.n_clip(); // These constants depend on encoding and may need to be updated. // Perhaps we can plumb these from piet-gpu-derive? @@ -402,18 +388,19 @@ impl Renderer { // TODO: reallocate scene buffer if size is inadequate { let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?; - scene.write_scene(&mut mapped_scene); + render_ctx.write_scene(&mut mapped_scene); } self.config_bufs[buf_ix].write(&[config])?; self.memory_buf_host[buf_ix].write(&[alloc as u32, 0 /* Overflow flag */])?; // Upload gradient data. - if !scene.ramp_data.is_empty() { + let ramp_data = render_ctx.get_ramp_data(); + if !ramp_data.is_empty() { assert!( self.gradient_bufs[buf_ix].size() as usize - >= std::mem::size_of_val(&*scene.ramp_data) + >= std::mem::size_of_val(&*ramp_data) ); - self.gradient_bufs[buf_ix].write(scene.ramp_data)?; + self.gradient_bufs[buf_ix].write(&ramp_data)?; } } Ok(()) @@ -437,10 +424,10 @@ impl Renderer { cmd_buf.copy_buffer_to_image(&self.gradient_bufs[buf_ix], &self.gradients); cmd_buf.image_barrier(&self.gradients, ImageLayout::BlitDst, ImageLayout::General); cmd_buf.reset_query_pool(&query_pool); - cmd_buf.write_timestamp(&query_pool, 0); cmd_buf.begin_debug_label("Element bounding box calculation"); + let mut pass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::timer(&query_pool, 0, 1)); self.element_stage.record( - cmd_buf, + &mut pass, &self.element_code, &self.element_bindings[buf_ix], self.n_transform as u64, @@ -448,56 +435,59 @@ impl Renderer { self.n_pathtag as u32, self.n_drawobj as u64, ); + pass.end(); cmd_buf.end_debug_label(); - cmd_buf.write_timestamp(&query_pool, 1); cmd_buf.memory_barrier(); - cmd_buf.begin_debug_label("Clip bounding box calculation"); + let mut pass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::timer(&query_pool, 2, 3)); + pass.begin_debug_label("Clip bounding box calculation"); self.clip_binding - .record(cmd_buf, &self.clip_code, self.n_clip as u32); - cmd_buf.end_debug_label(); - cmd_buf.begin_debug_label("Element binning"); - cmd_buf.dispatch( + .record(&mut pass, &self.clip_code, self.n_clip as u32); + pass.end_debug_label(); + pass.begin_debug_label("Element binning"); + pass.dispatch( &self.bin_pipeline, &self.bin_ds, (((self.n_paths + 255) / 256) as u32, 1, 1), (256, 1, 1), ); - cmd_buf.end_debug_label(); - cmd_buf.memory_barrier(); - cmd_buf.begin_debug_label("Tile allocation"); - cmd_buf.dispatch( + pass.end_debug_label(); + pass.memory_barrier(); + pass.begin_debug_label("Tile allocation"); + pass.dispatch( &self.tile_pipeline, &self.tile_ds[buf_ix], (((self.n_paths + 255) / 256) as u32, 1, 1), (256, 1, 1), ); - cmd_buf.end_debug_label(); - cmd_buf.write_timestamp(&query_pool, 2); - cmd_buf.memory_barrier(); + pass.end_debug_label(); + pass.end(); cmd_buf.begin_debug_label("Path flattening"); - cmd_buf.dispatch( + cmd_buf.memory_barrier(); + let mut pass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::timer(&query_pool, 4, 5)); + pass.dispatch( &self.path_pipeline, &self.path_ds, (((self.n_pathseg + 31) / 32) as u32, 1, 1), (32, 1, 1), ); + pass.end(); cmd_buf.end_debug_label(); - cmd_buf.write_timestamp(&query_pool, 3); cmd_buf.memory_barrier(); cmd_buf.begin_debug_label("Backdrop propagation"); - cmd_buf.dispatch( + let mut pass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::timer(&query_pool, 6, 7)); + pass.dispatch( &self.backdrop_pipeline, &self.backdrop_ds, (((self.n_paths + 255) / 256) as u32, 1, 1), (256, self.backdrop_y, 1), ); + pass.end(); cmd_buf.end_debug_label(); - cmd_buf.write_timestamp(&query_pool, 4); // TODO: redo query accounting - cmd_buf.write_timestamp(&query_pool, 5); cmd_buf.memory_barrier(); cmd_buf.begin_debug_label("Coarse raster"); - cmd_buf.dispatch( + let mut pass = cmd_buf.begin_compute_pass(&ComputePassDescriptor::timer(&query_pool, 8, 9)); + pass.dispatch( &self.coarse_pipeline, &self.coarse_ds[buf_ix], ( @@ -507,11 +497,13 @@ impl Renderer { ), (256, 1, 1), ); + pass.end(); cmd_buf.end_debug_label(); - cmd_buf.write_timestamp(&query_pool, 6); cmd_buf.memory_barrier(); cmd_buf.begin_debug_label("Fine raster"); - cmd_buf.dispatch( + let mut pass = + cmd_buf.begin_compute_pass(&ComputePassDescriptor::timer(&query_pool, 10, 11)); + pass.dispatch( &self.k4_pipeline, &self.k4_ds, ( @@ -521,8 +513,8 @@ impl Renderer { ), (8, 4, 1), ); + pass.end(); cmd_buf.end_debug_label(); - cmd_buf.write_timestamp(&query_pool, 7); cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc); } diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index ad608ca..dca03eb 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::encoder::{EncodedSceneRef, GlyphEncoder}; +use crate::encoder::GlyphEncoder; use crate::stages::{Config, Transform}; use crate::MAX_BLEND_STACK; use piet::kurbo::{Affine, Insets, PathEl, Point, Rect, Shape}; @@ -97,10 +97,6 @@ impl PietGpuRenderContext { self.new_encoder.stage_config() } - pub fn encoded_scene(&self) -> EncodedSceneRef { - self.new_encoder.scene_ref() - } - /// Number of draw objects. /// /// This is for the new element processing pipeline. It's not necessarily the diff --git a/piet-gpu/src/stages.rs b/piet-gpu/src/stages.rs index 52b8bf1..5442ba3 100644 --- a/piet-gpu/src/stages.rs +++ b/piet-gpu/src/stages.rs @@ -26,7 +26,7 @@ use bytemuck::{Pod, Zeroable}; pub use clip::{ClipBinding, ClipCode, CLIP_PART_SIZE}; pub use draw::{DrawBinding, DrawCode, DrawMonoid, DrawStage, DRAW_PART_SIZE}; pub use path::{PathBinding, PathCode, PathEncoder, PathStage, PATHSEG_PART_SIZE}; -use piet_gpu_hal::{Buffer, CmdBuf, Session}; +use piet_gpu_hal::{Buffer, ComputePass, Session}; pub use transform::{ Transform, TransformBinding, TransformCode, TransformStage, TRANSFORM_PART_SIZE, }; @@ -140,7 +140,7 @@ impl ElementStage { pub unsafe fn record( &self, - cmd_buf: &mut CmdBuf, + pass: &mut ComputePass, code: &ElementCode, binding: &ElementBinding, n_transform: u64, @@ -149,14 +149,14 @@ impl ElementStage { n_drawobj: u64, ) { self.transform_stage.record( - cmd_buf, + pass, &code.transform_code, &binding.transform_binding, n_transform, ); // No memory barrier needed here; path has at least one before pathseg self.path_stage.record( - cmd_buf, + pass, &code.path_code, &binding.path_binding, n_paths, @@ -164,6 +164,6 @@ impl ElementStage { ); // No memory barrier needed here; draw has at least one before draw_leaf self.draw_stage - .record(cmd_buf, &code.draw_code, &binding.draw_binding, n_drawobj); + .record(pass, &code.draw_code, &binding.draw_binding, n_drawobj); } } diff --git a/piet-gpu/src/stages/clip.rs b/piet-gpu/src/stages/clip.rs index e4bc3db..2fd195b 100644 --- a/piet-gpu/src/stages/clip.rs +++ b/piet-gpu/src/stages/clip.rs @@ -16,7 +16,7 @@ //! The clip processing stage (includes substages). -use piet_gpu_hal::{include_shader, BindType, Buffer, CmdBuf, DescriptorSet, Pipeline, Session}; +use piet_gpu_hal::{include_shader, BindType, Buffer, ComputePass, DescriptorSet, Pipeline, Session}; // Note that this isn't the code/stage/binding pattern of most of the other stages // in the new element processing pipeline. We want to move those temporary buffers @@ -69,26 +69,26 @@ impl ClipBinding { /// Record the clip dispatches. /// /// Assumes memory barrier on entry. Provides memory barrier on exit. - pub unsafe fn record(&self, cmd_buf: &mut CmdBuf, code: &ClipCode, n_clip: u32) { + pub unsafe fn record(&self, pass: &mut ComputePass, code: &ClipCode, n_clip: u32) { let n_wg_reduce = n_clip.saturating_sub(1) / CLIP_PART_SIZE; if n_wg_reduce > 0 { - cmd_buf.dispatch( + pass.dispatch( &code.reduce_pipeline, &self.reduce_ds, (n_wg_reduce, 1, 1), (CLIP_PART_SIZE, 1, 1), ); - cmd_buf.memory_barrier(); + pass.memory_barrier(); } let n_wg = (n_clip + CLIP_PART_SIZE - 1) / CLIP_PART_SIZE; if n_wg > 0 { - cmd_buf.dispatch( + pass.dispatch( &code.leaf_pipeline, &self.leaf_ds, (n_wg, 1, 1), (CLIP_PART_SIZE, 1, 1), ); - cmd_buf.memory_barrier(); + pass.memory_barrier(); } } } diff --git a/piet-gpu/src/stages/draw.rs b/piet-gpu/src/stages/draw.rs index 21312a4..f0ee2b6 100644 --- a/piet-gpu/src/stages/draw.rs +++ b/piet-gpu/src/stages/draw.rs @@ -19,7 +19,7 @@ use bytemuck::{Pod, Zeroable}; use piet_gpu_hal::{ - include_shader, BindType, Buffer, BufferUsage, CmdBuf, DescriptorSet, Pipeline, Session, + include_shader, BindType, Buffer, BufferUsage, ComputePass, DescriptorSet, Pipeline, Session, }; /// The output element of the draw object stage. @@ -130,7 +130,7 @@ impl DrawStage { pub unsafe fn record( &self, - cmd_buf: &mut CmdBuf, + pass: &mut ComputePass, code: &DrawCode, binding: &DrawBinding, size: u64, @@ -140,22 +140,22 @@ impl DrawStage { } let n_workgroups = (size + DRAW_PART_SIZE - 1) / DRAW_PART_SIZE; if n_workgroups > 1 { - cmd_buf.dispatch( + pass.dispatch( &code.reduce_pipeline, &binding.reduce_ds, (n_workgroups as u32, 1, 1), (DRAW_WG as u32, 1, 1), ); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( + pass.memory_barrier(); + pass.dispatch( &code.root_pipeline, &self.root_ds, (1, 1, 1), (DRAW_WG as u32, 1, 1), ); } - cmd_buf.memory_barrier(); - cmd_buf.dispatch( + pass.memory_barrier(); + pass.dispatch( &code.leaf_pipeline, &binding.leaf_ds, (n_workgroups as u32, 1, 1), diff --git a/piet-gpu/src/stages/path.rs b/piet-gpu/src/stages/path.rs index 6c524a2..be33041 100644 --- a/piet-gpu/src/stages/path.rs +++ b/piet-gpu/src/stages/path.rs @@ -17,7 +17,7 @@ //! The path stage (includes substages). use piet_gpu_hal::{ - include_shader, BindType, Buffer, BufferUsage, CmdBuf, DescriptorSet, Pipeline, Session, + include_shader, BindType, Buffer, BufferUsage, ComputePass, DescriptorSet, Pipeline, Session, }; pub struct PathCode { @@ -148,7 +148,7 @@ impl PathStage { /// those are consumed. Result is written without barrier. pub unsafe fn record( &self, - cmd_buf: &mut CmdBuf, + pass: &mut ComputePass, code: &PathCode, binding: &PathBinding, n_paths: u32, @@ -166,15 +166,15 @@ impl PathStage { let reduce_part_tags = REDUCE_PART_SIZE * 4; let n_wg_tag_reduce = (n_tags + reduce_part_tags - 1) / reduce_part_tags; if n_wg_tag_reduce > 1 { - cmd_buf.dispatch( + pass.dispatch( &code.reduce_pipeline, &binding.reduce_ds, (n_wg_tag_reduce, 1, 1), (REDUCE_WG, 1, 1), ); // I think we can skip root if n_wg_tag_reduce == 2 - cmd_buf.memory_barrier(); - cmd_buf.dispatch( + pass.memory_barrier(); + pass.dispatch( &code.tag_root_pipeline, &self.tag_root_ds, (1, 1, 1), @@ -183,15 +183,15 @@ impl PathStage { // No barrier needed here; clear doesn't depend on path tags } let n_wg_clear = (n_paths + CLEAR_WG - 1) / CLEAR_WG; - cmd_buf.dispatch( + pass.dispatch( &code.clear_pipeline, &binding.clear_ds, (n_wg_clear, 1, 1), (CLEAR_WG, 1, 1), ); - cmd_buf.memory_barrier(); + pass.memory_barrier(); let n_wg_pathseg = (n_tags + SCAN_PART_SIZE - 1) / SCAN_PART_SIZE; - cmd_buf.dispatch( + pass.dispatch( &code.pathseg_pipeline, &binding.path_ds, (n_wg_pathseg, 1, 1), diff --git a/piet-gpu/src/stages/transform.rs b/piet-gpu/src/stages/transform.rs index b21712f..8de7cee 100644 --- a/piet-gpu/src/stages/transform.rs +++ b/piet-gpu/src/stages/transform.rs @@ -20,7 +20,7 @@ use bytemuck::{Pod, Zeroable}; use piet::kurbo::Affine; use piet_gpu_hal::{ - include_shader, BindType, Buffer, BufferUsage, CmdBuf, DescriptorSet, Pipeline, Session, + include_shader, BindType, Buffer, BufferUsage, ComputePass, DescriptorSet, Pipeline, Session, }; /// An affine transform. @@ -132,7 +132,7 @@ impl TransformStage { pub unsafe fn record( &self, - cmd_buf: &mut CmdBuf, + pass: &mut ComputePass, code: &TransformCode, binding: &TransformBinding, size: u64, @@ -142,22 +142,22 @@ impl TransformStage { } let n_workgroups = (size + TRANSFORM_PART_SIZE - 1) / TRANSFORM_PART_SIZE; if n_workgroups > 1 { - cmd_buf.dispatch( + pass.dispatch( &code.reduce_pipeline, &binding.reduce_ds, (n_workgroups as u32, 1, 1), (TRANSFORM_WG as u32, 1, 1), ); - cmd_buf.memory_barrier(); - cmd_buf.dispatch( + pass.memory_barrier(); + pass.dispatch( &code.root_pipeline, &self.root_ds, (1, 1, 1), (TRANSFORM_WG as u32, 1, 1), ); - cmd_buf.memory_barrier(); + pass.memory_barrier(); } - cmd_buf.dispatch( + pass.dispatch( &code.leaf_pipeline, &binding.leaf_ds, (n_workgroups as u32, 1, 1), diff --git a/piet-gpu/src/test_scenes.rs b/piet-gpu/src/test_scenes.rs index cf5a50d..bfd2af2 100644 --- a/piet-gpu/src/test_scenes.rs +++ b/piet-gpu/src/test_scenes.rs @@ -21,12 +21,7 @@ pub fn render_blend_test(rc: &mut PietGpuRenderContext, i: usize, blend: Blend) rc.restore().unwrap(); } -pub fn render_svg(rc: &mut impl RenderContext, filename: &str, scale: f64) { - let xml_str = std::fs::read_to_string(filename).unwrap(); - let start = std::time::Instant::now(); - let svg = PicoSvg::load(&xml_str, scale).unwrap(); - println!("parsing time: {:?}", start.elapsed()); - +pub fn render_svg(rc: &mut impl RenderContext, svg: &PicoSvg) { let start = std::time::Instant::now(); svg.render(rc); println!("flattening and encoding time: {:?}", start.elapsed()); diff --git a/tests/src/clear.rs b/tests/src/clear.rs index fc6f063..af4b8ea 100644 --- a/tests/src/clear.rs +++ b/tests/src/clear.rs @@ -16,11 +16,11 @@ //! Utilities (and a benchmark) for clearing buffers with compute shaders. -use piet_gpu_hal::{include_shader, BindType, BufferUsage, DescriptorSet}; +use piet_gpu_hal::{include_shader, BindType, BufferUsage, ComputePass, DescriptorSet}; use piet_gpu_hal::{Buffer, Pipeline}; use crate::config::Config; -use crate::runner::{Commands, Runner}; +use crate::runner::Runner; use crate::test_result::TestResult; const WG_SIZE: u64 = 256; @@ -52,9 +52,9 @@ pub unsafe fn run_clear_test(runner: &mut Runner, config: &Config) -> TestResult let mut total_elapsed = 0.0; for i in 0..n_iter { let mut commands = runner.commands(); - commands.write_timestamp(0); - stage.record(&mut commands, &code, &binding); - commands.write_timestamp(1); + let mut pass = commands.compute_pass(0, 1); + stage.record(&mut pass, &code, &binding); + pass.end(); if i == 0 || config.verify_all { commands.cmd_buf.memory_barrier(); commands.download(&out_buf); @@ -108,17 +108,12 @@ impl ClearStage { ClearBinding { descriptor_set } } - pub unsafe fn record( - &self, - commands: &mut Commands, - code: &ClearCode, - bindings: &ClearBinding, - ) { + pub unsafe fn record(&self, pass: &mut ComputePass, code: &ClearCode, bindings: &ClearBinding) { let n_workgroups = (self.n_elements + WG_SIZE - 1) / WG_SIZE; // An issue: for clearing large buffers (>16M), we need to check the // number of workgroups against the (dynamically detected) limit, and // potentially issue multiple dispatches. - commands.cmd_buf.dispatch( + pass.dispatch( &code.pipeline, &bindings.descriptor_set, (n_workgroups as u32, 1, 1), diff --git a/tests/src/clip.rs b/tests/src/clip.rs index 4a38949..b1f8613 100644 --- a/tests/src/clip.rs +++ b/tests/src/clip.rs @@ -58,11 +58,11 @@ pub unsafe fn clip_test(runner: &mut Runner, config: &Config) -> TestResult { let binding = ClipBinding::new(&runner.session, &code, &config_buf, &memory.dev_buf); let mut commands = runner.commands(); - commands.write_timestamp(0); commands.upload(&memory); - binding.record(&mut commands.cmd_buf, &code, n_clip as u32); + let mut pass = commands.compute_pass(0, 1); + binding.record(&mut pass, &code, n_clip as u32); + pass.end(); commands.download(&memory); - commands.write_timestamp(1); runner.submit(commands); let dst = memory.map_read(..); if let Some(failure) = data.verify(&dst) { diff --git a/tests/src/draw.rs b/tests/src/draw.rs index 4372da4..dc82572 100644 --- a/tests/src/draw.rs +++ b/tests/src/draw.rs @@ -77,9 +77,9 @@ pub unsafe fn draw_test(runner: &mut Runner, config: &Config) -> TestResult { let n_iter = config.n_iter; for i in 0..n_iter { let mut commands = runner.commands(); - commands.write_timestamp(0); - stage.record(&mut commands.cmd_buf, &code, &binding, n_tag); - commands.write_timestamp(1); + let mut pass = commands.compute_pass(0, 1); + stage.record(&mut pass, &code, &binding, n_tag); + pass.end(); if i == 0 || config.verify_all { commands.cmd_buf.memory_barrier(); commands.download(&memory); diff --git a/tests/src/linkedlist.rs b/tests/src/linkedlist.rs index 5767806..e24adcb 100644 --- a/tests/src/linkedlist.rs +++ b/tests/src/linkedlist.rs @@ -45,9 +45,7 @@ pub unsafe fn run_linkedlist_test(runner: &mut Runner, config: &Config) -> TestR for i in 0..n_iter { let mut commands = runner.commands(); // Might clear only buckets to save time. - commands.write_timestamp(0); stage.record(&mut commands, &code, &binding, &mem_buf.dev_buf); - commands.write_timestamp(1); if i == 0 || config.verify_all { commands.cmd_buf.memory_barrier(); commands.download(&mem_buf); @@ -107,12 +105,14 @@ impl LinkedListStage { commands.cmd_buf.clear_buffer(out_buf, None); commands.cmd_buf.memory_barrier(); let n_workgroups = N_BUCKETS / WG_SIZE; - commands.cmd_buf.dispatch( + let mut pass = commands.compute_pass(0, 1); + pass.dispatch( &code.pipeline, &bindings.descriptor_set, (n_workgroups as u32, 1, 1), (WG_SIZE as u32, 1, 1), ); + pass.end(); } } diff --git a/tests/src/message_passing.rs b/tests/src/message_passing.rs index c5d989b..39e71dc 100644 --- a/tests/src/message_passing.rs +++ b/tests/src/message_passing.rs @@ -59,9 +59,7 @@ pub unsafe fn run_message_passing_test( let mut failures = 0; for _ in 0..n_iter { let mut commands = runner.commands(); - commands.write_timestamp(0); stage.record(&mut commands, &code, &binding, &out_buf.dev_buf); - commands.write_timestamp(1); commands.cmd_buf.memory_barrier(); commands.download(&out_buf); total_elapsed += runner.submit(commands); @@ -128,11 +126,13 @@ impl MessagePassingStage { commands.cmd_buf.clear_buffer(&self.data_buf, None); commands.cmd_buf.clear_buffer(out_buf, None); commands.cmd_buf.memory_barrier(); - commands.cmd_buf.dispatch( + let mut pass = commands.compute_pass(0, 1); + pass.dispatch( &code.pipeline, &bindings.descriptor_set, (256, 1, 1), (256, 1, 1), ); + pass.end(); } } diff --git a/tests/src/path.rs b/tests/src/path.rs index bf72c68..9d794e1 100644 --- a/tests/src/path.rs +++ b/tests/src/path.rs @@ -105,15 +105,15 @@ pub unsafe fn path_test(runner: &mut Runner, config: &Config) -> TestResult { let mut commands = runner.commands(); commands.cmd_buf.copy_buffer(&memory_init, &memory.dev_buf); commands.cmd_buf.memory_barrier(); - commands.write_timestamp(0); + let mut pass = commands.compute_pass(0, 1); stage.record( - &mut commands.cmd_buf, + &mut pass, &code, &binding, path_data.n_path, path_data.tags.len() as u32, ); - commands.write_timestamp(1); + pass.end(); if i == 0 || config.verify_all { commands.cmd_buf.memory_barrier(); commands.download(&memory); diff --git a/tests/src/prefix.rs b/tests/src/prefix.rs index 4174d8d..dbaf256 100644 --- a/tests/src/prefix.rs +++ b/tests/src/prefix.rs @@ -85,9 +85,7 @@ pub unsafe fn run_prefix_test( let mut total_elapsed = 0.0; for i in 0..n_iter { let mut commands = runner.commands(); - commands.write_timestamp(0); stage.record(&mut commands, &code, &binding); - commands.write_timestamp(1); if i == 0 || config.verify_all { commands.cmd_buf.memory_barrier(); commands.download(&out_buf); @@ -159,12 +157,14 @@ impl PrefixStage { let n_workgroups = (self.n_elements + ELEMENTS_PER_WG - 1) / ELEMENTS_PER_WG; commands.cmd_buf.clear_buffer(&self.state_buf, None); commands.cmd_buf.memory_barrier(); - commands.cmd_buf.dispatch( + let mut pass = commands.compute_pass(0, 1); + pass.dispatch( &code.pipeline, &bindings.descriptor_set, (n_workgroups as u32, 1, 1), (WG_SIZE as u32, 1, 1), ); + pass.end(); // One thing that's missing here is registering the buffers so // they can be safely dropped by Rust code before the execution // of the command buffer completes. diff --git a/tests/src/prefix_tree.rs b/tests/src/prefix_tree.rs index 24be2af..3c9c813 100644 --- a/tests/src/prefix_tree.rs +++ b/tests/src/prefix_tree.rs @@ -66,9 +66,7 @@ pub unsafe fn run_prefix_test(runner: &mut Runner, config: &Config) -> TestResul let mut commands = runner.commands(); commands.cmd_buf.copy_buffer(&data_buf, &out_buf.dev_buf); commands.cmd_buf.memory_barrier(); - commands.write_timestamp(0); stage.record(&mut commands, &code, &binding); - commands.write_timestamp(1); if i == 0 || config.verify_all { commands.cmd_buf.memory_barrier(); commands.download(&out_buf); @@ -175,33 +173,35 @@ impl PrefixTreeStage { code: &PrefixTreeCode, bindings: &PrefixTreeBinding, ) { + let mut pass = commands.compute_pass(0, 1); let n = self.tmp_bufs.len(); for i in 0..n { let n_workgroups = self.sizes[i + 1]; - commands.cmd_buf.dispatch( + pass.dispatch( &code.reduce_pipeline, &bindings.descriptor_sets[i], (n_workgroups as u32, 1, 1), (WG_SIZE as u32, 1, 1), ); - commands.cmd_buf.memory_barrier(); + pass.memory_barrier(); } - commands.cmd_buf.dispatch( + pass.dispatch( &code.root_pipeline, &bindings.descriptor_sets[n], (1, 1, 1), (WG_SIZE as u32, 1, 1), ); for i in (0..n).rev() { - commands.cmd_buf.memory_barrier(); + pass.memory_barrier(); let n_workgroups = self.sizes[i + 1]; - commands.cmd_buf.dispatch( + pass.dispatch( &code.scan_pipeline, &bindings.descriptor_sets[2 * n - i], (n_workgroups as u32, 1, 1), (WG_SIZE as u32, 1, 1), ); } + pass.end(); } } diff --git a/tests/src/runner.rs b/tests/src/runner.rs index 1fd6774..3ba8223 100644 --- a/tests/src/runner.rs +++ b/tests/src/runner.rs @@ -20,8 +20,8 @@ use std::ops::RangeBounds; use bytemuck::Pod; use piet_gpu_hal::{ - BackendType, BufReadGuard, BufWriteGuard, Buffer, BufferUsage, CmdBuf, Instance, InstanceFlags, - QueryPool, Session, + BackendType, BufReadGuard, BufWriteGuard, Buffer, BufferUsage, CmdBuf, ComputePass, + ComputePassDescriptor, Instance, InstanceFlags, QueryPool, Session, }; pub struct Runner { @@ -118,8 +118,14 @@ impl Runner { } impl Commands { - pub unsafe fn write_timestamp(&mut self, query: u32) { - self.cmd_buf.write_timestamp(&self.query_pool, query); + /// Start a compute pass with timer queries. + pub unsafe fn compute_pass(&mut self, start_query: u32, end_query: u32) -> ComputePass { + self.cmd_buf + .begin_compute_pass(&ComputePassDescriptor::timer( + &self.query_pool, + start_query, + end_query, + )) } pub unsafe fn upload(&mut self, buf: &BufStage) { diff --git a/tests/src/transform.rs b/tests/src/transform.rs index 6edcc3f..43bfc67 100644 --- a/tests/src/transform.rs +++ b/tests/src/transform.rs @@ -61,9 +61,9 @@ pub unsafe fn transform_test(runner: &mut Runner, config: &Config) -> TestResult let n_iter = config.n_iter; for i in 0..n_iter { let mut commands = runner.commands(); - commands.write_timestamp(0); - stage.record(&mut commands.cmd_buf, &code, &binding, n_elements); - commands.write_timestamp(1); + let mut pass = commands.compute_pass(0, 1); + stage.record(&mut pass, &code, &binding, n_elements); + pass.end(); if i == 0 || config.verify_all { commands.cmd_buf.memory_barrier(); commands.download(&memory); From e12b063cd36bc8057519a08967c1bacaec88b257 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 10 May 2022 00:53:48 -0400 Subject: [PATCH 07/13] Add Renderer::upload_scene() This allows rendering from raw stream data and may be temporary depending on the crate structure after the traditional piet api is removed. --- piet-gpu/src/encoder.rs | 141 ++++++++++++++++++++++++++++++++++++++++ piet-gpu/src/lib.rs | 57 ++++++++++++++++ piet-scene/src/main.rs | 60 ++--------------- 3 files changed, 205 insertions(+), 53 deletions(-) diff --git a/piet-gpu/src/encoder.rs b/piet-gpu/src/encoder.rs index 2f4b85e..a24ddbc 100644 --- a/piet-gpu/src/encoder.rs +++ b/piet-gpu/src/encoder.rs @@ -37,6 +37,147 @@ pub struct Encoder { n_clip: u32, } +#[derive(Copy, Clone, Debug)] +pub struct EncodedSceneRef<'a, T: Copy + Pod> { + pub transform_stream: &'a [T], + pub tag_stream: &'a [u8], + pub pathseg_stream: &'a [u8], + pub linewidth_stream: &'a [f32], + pub drawtag_stream: &'a [u32], + pub drawdata_stream: &'a [u8], + pub n_path: u32, + pub n_pathseg: u32, + pub n_clip: u32, + pub ramp_data: &'a [u32], +} + +impl<'a, T: Copy + Pod> EncodedSceneRef<'a, T> { + /// Return a config for the element processing pipeline. + /// + /// This does not include further pipeline processing. Also returns the + /// beginning of free memory. + pub fn stage_config(&self) -> (Config, usize) { + // Layout of scene buffer + let drawtag_offset = 0; + let n_drawobj = self.n_drawobj(); + let n_drawobj_padded = align_up(n_drawobj, DRAW_PART_SIZE as usize); + let drawdata_offset = drawtag_offset + n_drawobj_padded * DRAWTAG_SIZE; + let trans_offset = drawdata_offset + self.drawdata_stream.len(); + let n_trans = self.transform_stream.len(); + let n_trans_padded = align_up(n_trans, TRANSFORM_PART_SIZE as usize); + let linewidth_offset = trans_offset + n_trans_padded * TRANSFORM_SIZE; + let n_linewidth = self.linewidth_stream.len(); + let pathtag_offset = linewidth_offset + n_linewidth * LINEWIDTH_SIZE; + let n_pathtag = self.tag_stream.len(); + let n_pathtag_padded = align_up(n_pathtag, PATHSEG_PART_SIZE as usize); + let pathseg_offset = pathtag_offset + n_pathtag_padded; + + // Layout of memory + let mut alloc = 0; + let trans_alloc = alloc; + alloc += trans_alloc + n_trans_padded * TRANSFORM_SIZE; + let pathseg_alloc = alloc; + alloc += pathseg_alloc + self.n_pathseg as usize * PATHSEG_SIZE; + let path_bbox_alloc = alloc; + let n_path = self.n_path as usize; + alloc += path_bbox_alloc + n_path * PATH_BBOX_SIZE; + let drawmonoid_alloc = alloc; + alloc += n_drawobj_padded * DRAWMONOID_SIZE; + let anno_alloc = alloc; + alloc += n_drawobj * ANNOTATED_SIZE; + let clip_alloc = alloc; + let n_clip = self.n_clip as usize; + const CLIP_SIZE: usize = 4; + alloc += n_clip * CLIP_SIZE; + let clip_bic_alloc = alloc; + const CLIP_BIC_SIZE: usize = 8; + // This can round down, as we only reduce the prefix + alloc += (n_clip / CLIP_PART_SIZE as usize) * CLIP_BIC_SIZE; + let clip_stack_alloc = alloc; + const CLIP_EL_SIZE: usize = 20; + alloc += n_clip * CLIP_EL_SIZE; + let clip_bbox_alloc = alloc; + const CLIP_BBOX_SIZE: usize = 16; + alloc += align_up(n_clip as usize, CLIP_PART_SIZE as usize) * CLIP_BBOX_SIZE; + let draw_bbox_alloc = alloc; + alloc += n_drawobj * DRAW_BBOX_SIZE; + let drawinfo_alloc = alloc; + // TODO: not optimized; it can be accumulated during encoding or summed from drawtags + const MAX_DRAWINFO_SIZE: usize = 44; + alloc += n_drawobj * MAX_DRAWINFO_SIZE; + + let config = Config { + n_elements: n_drawobj as u32, + n_pathseg: self.n_pathseg, + pathseg_alloc: pathseg_alloc as u32, + anno_alloc: anno_alloc as u32, + trans_alloc: trans_alloc as u32, + path_bbox_alloc: path_bbox_alloc as u32, + drawmonoid_alloc: drawmonoid_alloc as u32, + clip_alloc: clip_alloc as u32, + clip_bic_alloc: clip_bic_alloc as u32, + clip_stack_alloc: clip_stack_alloc as u32, + clip_bbox_alloc: clip_bbox_alloc as u32, + draw_bbox_alloc: draw_bbox_alloc as u32, + drawinfo_alloc: drawinfo_alloc as u32, + n_trans: n_trans as u32, + n_path: self.n_path, + n_clip: self.n_clip, + trans_offset: trans_offset as u32, + linewidth_offset: linewidth_offset as u32, + pathtag_offset: pathtag_offset as u32, + pathseg_offset: pathseg_offset as u32, + drawtag_offset: drawtag_offset as u32, + drawdata_offset: drawdata_offset as u32, + ..Default::default() + }; + (config, alloc) + } + + pub fn write_scene(&self, buf: &mut BufWrite) { + buf.extend_slice(&self.drawtag_stream); + let n_drawobj = self.drawtag_stream.len(); + buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE); + buf.extend_slice(&self.drawdata_stream); + buf.extend_slice(&self.transform_stream); + let n_trans = self.transform_stream.len(); + buf.fill_zero(padding(n_trans, TRANSFORM_PART_SIZE as usize) * TRANSFORM_SIZE); + buf.extend_slice(&self.linewidth_stream); + buf.extend_slice(&self.tag_stream); + let n_pathtag = self.tag_stream.len(); + buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize)); + buf.extend_slice(&self.pathseg_stream); + } + + /// The number of draw objects in the draw object stream. + pub(crate) fn n_drawobj(&self) -> usize { + self.drawtag_stream.len() + } + + /// The number of paths. + pub(crate) fn n_path(&self) -> u32 { + self.n_path + } + + /// The number of path segments. + pub(crate) fn n_pathseg(&self) -> u32 { + self.n_pathseg + } + + pub(crate) fn n_transform(&self) -> usize { + self.transform_stream.len() + } + + /// The number of tags in the path stream. + pub(crate) fn n_pathtag(&self) -> usize { + self.tag_stream.len() + } + + pub(crate) fn n_clip(&self) -> u32 { + self.n_clip + } +} + /// A scene fragment encoding a glyph. /// /// This is a reduced version of the full encoder. diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 773007d..d32a9c5 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -8,9 +8,11 @@ pub mod stages; pub mod test_scenes; mod text; +use bytemuck::Pod; use std::convert::TryInto; pub use blend::{Blend, BlendMode, CompositionMode}; +pub use encoder::EncodedSceneRef; pub use render_ctx::PietGpuRenderContext; pub use gradient::Colrv1RadialGradient; @@ -406,6 +408,61 @@ impl Renderer { Ok(()) } + pub fn upload_scene( + &mut self, + scene: &EncodedSceneRef, + buf_ix: usize, + ) -> Result<(), Error> { + let (mut config, mut alloc) = scene.stage_config(); + let n_drawobj = scene.n_drawobj(); + // TODO: be more consistent in size types + let n_path = scene.n_path() as usize; + self.n_paths = n_path; + self.n_transform = scene.n_transform(); + self.n_drawobj = scene.n_drawobj(); + self.n_pathseg = scene.n_pathseg() as usize; + self.n_pathtag = scene.n_pathtag(); + self.n_clip = scene.n_clip(); + + // These constants depend on encoding and may need to be updated. + // Perhaps we can plumb these from piet-gpu-derive? + const PATH_SIZE: usize = 12; + const BIN_SIZE: usize = 8; + let width_in_tiles = self.width / TILE_W; + let height_in_tiles = self.height / TILE_H; + let tile_base = alloc; + alloc += ((n_path + 3) & !3) * PATH_SIZE; + let bin_base = alloc; + alloc += ((n_drawobj + 255) & !255) * BIN_SIZE; + let ptcl_base = alloc; + alloc += width_in_tiles * height_in_tiles * PTCL_INITIAL_ALLOC; + + config.width_in_tiles = width_in_tiles as u32; + config.height_in_tiles = height_in_tiles as u32; + config.tile_alloc = tile_base as u32; + config.bin_alloc = bin_base as u32; + config.ptcl_alloc = ptcl_base as u32; + unsafe { + // TODO: reallocate scene buffer if size is inadequate + { + let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?; + scene.write_scene(&mut mapped_scene); + } + self.config_bufs[buf_ix].write(&[config])?; + self.memory_buf_host[buf_ix].write(&[alloc as u32, 0 /* Overflow flag */])?; + + // Upload gradient data. + if !scene.ramp_data.is_empty() { + assert!( + self.gradient_bufs[buf_ix].size() as usize + >= std::mem::size_of_val(&*scene.ramp_data) + ); + self.gradient_bufs[buf_ix].write(scene.ramp_data)?; + } + } + Ok(()) + } + pub unsafe fn record(&self, cmd_buf: &mut CmdBuf, query_pool: &QueryPool, buf_ix: usize) { cmd_buf.copy_buffer(&self.config_bufs[buf_ix], &self.config_buf); cmd_buf.copy_buffer(&self.memory_buf_host[buf_ix], &self.memory_buf_dev); diff --git a/piet-scene/src/main.rs b/piet-scene/src/main.rs index ff4030f..b1b777b 100644 --- a/piet-scene/src/main.rs +++ b/piet-scene/src/main.rs @@ -57,7 +57,7 @@ fn main() -> Result<(), Error> { .map(|_| session.create_semaphore()) .collect::, Error>>()?; let query_pools = (0..NUM_FRAMES) - .map(|_| session.create_query_pool(8)) + .map(|_| session.create_query_pool(12)) .collect::, Error>>()?; let mut cmd_bufs: [Option; NUM_FRAMES] = Default::default(); let mut submitted: [Option; NUM_FRAMES] = Default::default(); @@ -99,72 +99,26 @@ fn main() -> Result<(), Error> { if !ts.is_empty() { info_string = format!( "{:.3}ms :: e:{:.3}ms|alloc:{:.3}ms|cp:{:.3}ms|bd:{:.3}ms|bin:{:.3}ms|cr:{:.3}ms|r:{:.3}ms", - ts[6] * 1e3, + ts[10] * 1e3, ts[0] * 1e3, (ts[1] - ts[0]) * 1e3, (ts[2] - ts[1]) * 1e3, - (ts[3] - ts[2]) * 1e3, (ts[4] - ts[3]) * 1e3, - (ts[5] - ts[4]) * 1e3, (ts[6] - ts[5]) * 1e3, + (ts[8] - ts[7]) * 1e3, + (ts[10] - ts[9]) * 1e3, ); } } let mut ctx = PietGpuRenderContext::new(); - 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; - } - test_scenes::render_svg(&mut ctx, input, scale); - } else { - use piet_gpu::{Blend, BlendMode::*, CompositionMode::*}; - let blends = [ - Blend::new(Normal, SrcOver), - Blend::new(Multiply, SrcOver), - Blend::new(Screen, SrcOver), - Blend::new(Overlay, SrcOver), - Blend::new(Darken, SrcOver), - Blend::new(Lighten, SrcOver), - Blend::new(ColorDodge, SrcOver), - Blend::new(ColorBurn, SrcOver), - Blend::new(HardLight, SrcOver), - Blend::new(SoftLight, SrcOver), - Blend::new(Difference, SrcOver), - Blend::new(Exclusion, SrcOver), - Blend::new(Hue, SrcOver), - Blend::new(Saturation, SrcOver), - Blend::new(Color, SrcOver), - Blend::new(Luminosity, SrcOver), - Blend::new(Normal, Clear), - Blend::new(Normal, Copy), - Blend::new(Normal, Dest), - Blend::new(Normal, SrcOver), - Blend::new(Normal, DestOver), - Blend::new(Normal, SrcIn), - Blend::new(Normal, DestIn), - Blend::new(Normal, SrcOut), - Blend::new(Normal, DestOut), - Blend::new(Normal, SrcAtop), - Blend::new(Normal, DestAtop), - Blend::new(Normal, Xor), - Blend::new(Normal, Plus), - ]; - let blend = blends[mode % blends.len()]; - test_scenes::render_blend_test(&mut ctx, current_frame, blend); - info_string = format!("{:?}", blend); - } render_info_string(&mut ctx, &info_string); ctx = PietGpuRenderContext::new(); test_scene1_old(&mut ctx); - let mut encoded_scene_old = ctx.encoded_scene(); - let ramp_data = ctx.get_ramp_data(); - encoded_scene_old.ramp_data = &ramp_data; + //let mut encoded_scene_old = ctx.encoded_scene(); + // let ramp_data = ctx.get_ramp_data(); + //encoded_scene_old.ramp_data = &ramp_data; test_scene1(&mut scene, &mut rcx); let encoded_scene = scene_to_encoded_scene(&scene, &rcx); // println!("{:?}\n============\n{:?}", encoded_scene_old, encoded_scene); From 532b6ee808b223081dc6f356a6084179fbef22ef Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 10 May 2022 03:56:06 -0400 Subject: [PATCH 08/13] Add C api for glyph rendering First cut at a public C api that supports glyph rendering on Metal targets. --- Cargo.lock | 132 ++++++++++++++++++++ Cargo.toml | 1 + pgpu-render/Cargo.toml | 21 ++++ pgpu-render/build.rs | 29 +++++ pgpu-render/pgpu.h | 140 ++++++++++++++++++++++ pgpu-render/src/lib.rs | 233 ++++++++++++++++++++++++++++++++++++ pgpu-render/src/render.rs | 222 ++++++++++++++++++++++++++++++++++ piet-scene/src/geometry.rs | 4 +- piet-scene/src/glyph/mod.rs | 74 ++++++++++-- piet-scene/src/scene/mod.rs | 9 +- 10 files changed, 853 insertions(+), 12 deletions(-) create mode 100644 pgpu-render/Cargo.toml create mode 100644 pgpu-render/build.rs create mode 100644 pgpu-render/pgpu.h create mode 100644 pgpu-render/src/lib.rs create mode 100644 pgpu-render/src/render.rs diff --git a/Cargo.lock b/Cargo.lock index cb6b76a..938c91f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,25 @@ dependencies = [ "nix 0.18.0", ] +[[package]] +name = "cbindgen" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + [[package]] name = "cc" version = "1.0.73" @@ -452,6 +471,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + [[package]] name = "fnv" version = "1.0.7" @@ -501,6 +529,21 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -516,6 +559,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -525,6 +578,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[package]] name = "jni-sys" version = "0.3.0" @@ -876,6 +935,19 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pgpu-render" +version = "0.1.0" +dependencies = [ + "cbindgen", + "cocoa", + "metal", + "objc", + "piet-gpu", + "piet-gpu-hal", + "piet-scene", +] + [[package]] name = "piet" version = "0.2.0" @@ -1131,6 +1203,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "roxmltree" version = "0.13.1" @@ -1150,6 +1231,12 @@ dependencies = [ "owned_ttf_parser", ] +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + [[package]] name = "same-file" version = "1.0.6" @@ -1176,6 +1263,31 @@ name = "serde" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] [[package]] name = "smallvec" @@ -1235,6 +1347,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -1330,6 +1456,12 @@ dependencies = [ "unic-common", ] +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + [[package]] name = "unicode-width" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index b94b82b..88d9611 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ + "pgpu-render", "piet-gpu", "piet-gpu-derive", "piet-gpu-hal", diff --git a/pgpu-render/Cargo.toml b/pgpu-render/Cargo.toml new file mode 100644 index 0000000..8581d82 --- /dev/null +++ b/pgpu-render/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "pgpu-render" +version = "0.1.0" +description = "C interface for glyph rendering using piet-gpu." +license = "MIT/Apache-2.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +piet-gpu = { path = "../piet-gpu" } +piet-gpu-hal = { path = "../piet-gpu-hal" } +piet-scene = { path = "../piet-scene" } + +metal = "0.22" +cocoa = "0.24.0" +objc = "0.2.5" + +[build-dependencies] +cbindgen = "0.20.0" diff --git a/pgpu-render/build.rs b/pgpu-render/build.rs new file mode 100644 index 0000000..182c72c --- /dev/null +++ b/pgpu-render/build.rs @@ -0,0 +1,29 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +extern crate cbindgen; + +use std::env; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + cbindgen::Builder::new() + .with_crate(crate_dir) + .generate() + .expect("Unable to generate bindings") + .write_to_file("pgpu.h"); +} diff --git a/pgpu-render/pgpu.h b/pgpu-render/pgpu.h new file mode 100644 index 0000000..7f93625 --- /dev/null +++ b/pgpu-render/pgpu.h @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include + +/// Encoded (possibly color) outline for a glyph. +struct PgpuGlyph; + +/// Context for loading and scaling glyphs. +struct PgpuGlyphContext; + +/// Context for loading a scaling glyphs from a specific font. +struct PgpuGlyphProvider; + +/// State and resources for rendering a scene. +struct PgpuRenderer; + +/// Encoded streams and resources describing a vector graphics scene. +struct PgpuScene; + +/// Builder for constructing an encoded scene. +struct PgpuSceneBuilder; + +/// Tag and value for a font variation axis. +struct PgpuFontVariation { + /// Tag that specifies the axis. + uint32_t tag; + /// Requested setting for the axis. + float value; +}; + +/// Description of a font. +struct PgpuFontDesc { + /// Pointer to the context of the font file. + const uint8_t *data; + /// Size of the font file data in bytes. + uintptr_t data_len; + /// Index of the requested font in the font file. + uint32_t index; + /// Unique identifier for the font. + uint64_t unique_id; + /// Requested size in pixels per em unit. Set to 0.0 for + /// unscaled outlines. + float ppem; + /// Pointer to array of font variation settings. + const PgpuFontVariation *variations; + /// Number of font variation settings. + uintptr_t variations_len; +}; + +/// Rectangle defined by minimum and maximum points. +struct PgpuRect { + float x0; + float y0; + float x1; + float y1; +}; + +extern "C" { + +/// Creates a new piet-gpu renderer for the specified Metal device and +/// command queue. +/// +/// device: MTLDevice* +/// queue: MTLCommandQueue* +PgpuRenderer *pgpu_renderer_new(void *device, void *queue); + +/// Renders a prepared scene into a texture target. Commands for rendering are +/// recorded into the specified command buffer. Returns an id representing +/// resources that may have been allocated during this process. After the +/// command buffer has been retired, call `pgpu_renderer_release` with this id +/// to drop any associated resources. +/// +/// target: MTLTexture* +/// cmdbuf: MTLCommandBuffer* +uint32_t pgpu_renderer_render(PgpuRenderer *renderer, + const PgpuScene *scene, + void *target, + void *cmdbuf); + +/// Releases the internal resources associated with the specified id from a +/// previous render operation. +void pgpu_renderer_release(PgpuRenderer *renderer, uint32_t id); + +/// Destroys the piet-gpu renderer. +void pgpu_renderer_destroy(PgpuRenderer *renderer); + +/// Creates a new, empty piet-gpu scene. +PgpuScene *pgpu_scene_new(); + +/// Destroys the piet-gpu scene. +void pgpu_scene_destroy(PgpuScene *scene); + +/// Creates a new builder for filling a piet-gpu scene. The specified scene +/// should not be accessed while the builder is live. +PgpuSceneBuilder *pgpu_scene_builder_new(PgpuScene *scene); + +/// Adds a glyph with the specified transform to the underlying scene. +void pgpu_scene_builder_add_glyph(PgpuSceneBuilder *builder, + const PgpuGlyph *glyph, + const float (*transform)[6]); + +/// Finalizes the scene builder, making the underlying scene ready for +/// rendering. This takes ownership and consumes the builder. +void pgpu_scene_builder_finish(PgpuSceneBuilder *builder); + +/// Creates a new context for loading glyph outlines. +PgpuGlyphContext *pgpu_glyph_context_new(); + +/// Destroys the glyph context. +void pgpu_glyph_context_destroy(PgpuGlyphContext *gcx); + +/// Creates a new glyph provider for the specified glyph context and font +/// descriptor. May return nullptr if the font data is invalid. Only one glyph +/// provider may be live for a glyph context. +PgpuGlyphProvider *pgpu_glyph_provider_new(PgpuGlyphContext *gcx, const PgpuFontDesc *font); + +/// Returns an encoded outline for the specified glyph provider and glyph id. +/// May return nullptr if the requested glyph is not available. +const PgpuGlyph *pgpu_glyph_provider_get(PgpuGlyphProvider *provider, uint16_t gid); + +/// Returns an encoded color outline for the specified glyph provider, color +/// palette index and glyph id. May return nullptr if the requested glyph is +/// not available. +PgpuGlyph *pgpu_glyph_provider_get_color(PgpuGlyphProvider *provider, + uint16_t palette_index, + uint16_t gid); + +/// Destroys the glyph provider. +void pgpu_glyph_provider_destroy(PgpuGlyphProvider *provider); + +/// Computes the bounding box for the glyph after applying the specified +/// transform. +PgpuRect pgpu_glyph_bbox(const PgpuGlyph *glyph, const float (*transform)[6]); + +/// Destroys the glyph. +void pgpu_glyph_destroy(PgpuGlyph *glyph); + +} // extern "C" diff --git a/pgpu-render/src/lib.rs b/pgpu-render/src/lib.rs new file mode 100644 index 0000000..2036f57 --- /dev/null +++ b/pgpu-render/src/lib.rs @@ -0,0 +1,233 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +mod render; + +use render::*; +use std::ffi::c_void; +use std::mem::transmute; + +/// Creates a new piet-gpu renderer for the specified Metal device and +/// command queue. +/// +/// device: MTLDevice* +/// queue: MTLCommandQueue* +#[no_mangle] +pub unsafe extern "C" fn pgpu_renderer_new( + device: *mut c_void, + queue: *mut c_void, +) -> *mut PgpuRenderer { + let device: &metal::DeviceRef = transmute(device); + let queue: &metal::CommandQueueRef = transmute(queue); + Box::into_raw(Box::new(PgpuRenderer::new(device, queue))) +} + +/// Renders a prepared scene into a texture target. Commands for rendering are +/// recorded into the specified command buffer. Returns an id representing +/// resources that may have been allocated during this process. After the +/// command buffer has been retired, call `pgpu_renderer_release` with this id +/// to drop any associated resources. +/// +/// target: MTLTexture* +/// cmdbuf: MTLCommandBuffer* +#[no_mangle] +pub unsafe extern "C" fn pgpu_renderer_render( + renderer: *mut PgpuRenderer, + scene: *const PgpuScene, + target: *mut c_void, + cmdbuf: *mut c_void, +) -> u32 { + let cmdbuf: &metal::CommandBufferRef = transmute(cmdbuf); + let target: &metal::TextureRef = transmute(target); + (*renderer).render(&*scene, cmdbuf, target) +} + +/// Releases the internal resources associated with the specified id from a +/// previous render operation. +#[no_mangle] +pub unsafe extern "C" fn pgpu_renderer_release(renderer: *mut PgpuRenderer, id: u32) { + (*renderer).release(id); +} + +/// Destroys the piet-gpu renderer. +#[no_mangle] +pub unsafe extern "C" fn pgpu_renderer_destroy(renderer: *mut PgpuRenderer) { + Box::from_raw(renderer); +} + +/// Creates a new, empty piet-gpu scene. +#[no_mangle] +pub unsafe extern "C" fn pgpu_scene_new() -> *mut PgpuScene { + Box::into_raw(Box::new(PgpuScene::new())) +} + +/// Destroys the piet-gpu scene. +#[no_mangle] +pub unsafe extern "C" fn pgpu_scene_destroy(scene: *mut PgpuScene) { + Box::from_raw(scene); +} + +/// Creates a new builder for filling a piet-gpu scene. The specified scene +/// should not be accessed while the builder is live. +#[no_mangle] +pub unsafe extern "C" fn pgpu_scene_builder_new( + scene: *mut PgpuScene, +) -> *mut PgpuSceneBuilder<'static> { + Box::into_raw(Box::new((*scene).build())) +} + +/// Adds a glyph with the specified transform to the underlying scene. +#[no_mangle] +pub unsafe extern "C" fn pgpu_scene_builder_add_glyph( + builder: *mut PgpuSceneBuilder<'static>, + glyph: *const PgpuGlyph, + transform: &[f32; 6], +) { + let transform = piet_scene::geometry::Affine::new(transform); + (*builder).add_glyph(&*glyph, &transform); +} + +/// Finalizes the scene builder, making the underlying scene ready for +/// rendering. This takes ownership and consumes the builder. +#[no_mangle] +pub unsafe extern "C" fn pgpu_scene_builder_finish(builder: *mut PgpuSceneBuilder<'static>) { + let builder = Box::from_raw(builder); + builder.finish(); +} + +/// Creates a new context for loading glyph outlines. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_context_new() -> *mut PgpuGlyphContext { + Box::into_raw(Box::new(PgpuGlyphContext::new())) +} + +/// Destroys the glyph context. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_context_destroy(gcx: *mut PgpuGlyphContext) { + Box::from_raw(gcx); +} + +/// Description of a font. +#[derive(Copy, Clone)] +#[repr(C)] +pub struct PgpuFontDesc { + /// Pointer to the context of the font file. + data: *const u8, + /// Size of the font file data in bytes. + data_len: usize, + /// Index of the requested font in the font file. + index: u32, + /// Unique identifier for the font. + unique_id: u64, + /// Requested size in pixels per em unit. Set to 0.0 for + /// unscaled outlines. + ppem: f32, + /// Pointer to array of font variation settings. + variations: *const PgpuFontVariation, + /// Number of font variation settings. + variations_len: usize, +} + +/// Creates a new glyph provider for the specified glyph context and font +/// descriptor. May return nullptr if the font data is invalid. Only one glyph +/// provider may be live for a glyph context. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_provider_new( + gcx: *mut PgpuGlyphContext, + font: *const PgpuFontDesc, +) -> *mut PgpuGlyphProvider<'static> { + let font = &*font; + let font_data = std::slice::from_raw_parts(font.data, font.data_len); + let variations = std::slice::from_raw_parts(font.variations, font.variations_len); + if let Some(provider) = (*gcx).new_provider( + font_data, + font.index, + font.unique_id, + font.ppem, + false, + variations, + ) { + Box::into_raw(Box::new(provider)) + } else { + std::ptr::null_mut() + } +} + +/// Returns an encoded outline for the specified glyph provider and glyph id. +/// May return nullptr if the requested glyph is not available. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_provider_get( + provider: *mut PgpuGlyphProvider, + gid: u16, +) -> *const PgpuGlyph { + if let Some(glyph) = (*provider).get(gid) { + Box::into_raw(Box::new(glyph)) + } else { + std::ptr::null() + } +} + +/// Returns an encoded color outline for the specified glyph provider, color +/// palette index and glyph id. May return nullptr if the requested glyph is +/// not available. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_provider_get_color( + provider: *mut PgpuGlyphProvider, + palette_index: u16, + gid: u16, +) -> *mut PgpuGlyph { + if let Some(glyph) = (*provider).get_color(palette_index, gid) { + Box::into_raw(Box::new(glyph)) + } else { + std::ptr::null_mut() + } +} + +/// Destroys the glyph provider. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_provider_destroy(provider: *mut PgpuGlyphProvider) { + Box::from_raw(provider); +} + +/// Rectangle defined by minimum and maximum points. +#[derive(Copy, Clone, Default)] +#[repr(C)] +pub struct PgpuRect { + pub x0: f32, + pub y0: f32, + pub x1: f32, + pub y1: f32, +} + +/// Computes the bounding box for the glyph after applying the specified +/// transform. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_bbox(glyph: *const PgpuGlyph, transform: &[f32; 6]) -> PgpuRect { + let transform = piet_scene::geometry::Affine::new(transform); + let rect = (*glyph).bbox(Some(transform)); + PgpuRect { + x0: rect.min.x, + y0: rect.min.y, + x1: rect.max.x, + y1: rect.max.y, + } +} + +/// Destroys the glyph. +#[no_mangle] +pub unsafe extern "C" fn pgpu_glyph_destroy(glyph: *mut PgpuGlyph) { + Box::from_raw(glyph); +} diff --git a/pgpu-render/src/render.rs b/pgpu-render/src/render.rs new file mode 100644 index 0000000..361ef42 --- /dev/null +++ b/pgpu-render/src/render.rs @@ -0,0 +1,222 @@ +// Copyright 2022 The piet-gpu authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Also licensed under MIT license, at your choice. + +use piet_gpu::{EncodedSceneRef, PixelFormat, RenderConfig}; +use piet_gpu_hal::{QueryPool, Session}; +use piet_scene::glyph::pinot::{types::Tag, FontDataRef}; +use piet_scene::geometry::{Affine, Rect}; +use piet_scene::glyph::{GlyphContext, GlyphProvider}; +use piet_scene::resource::ResourceContext; +use piet_scene::scene::{Fragment, Scene}; + +/// State and resources for rendering a scene. +pub struct PgpuRenderer { + session: Session, + pgpu_renderer: Option, + query_pool: QueryPool, + width: u32, + height: u32, + is_color: bool, +} + +impl PgpuRenderer { + pub fn new(device: &metal::DeviceRef, queue: &metal::CommandQueueRef) -> Self { + let piet_device = piet_gpu_hal::Device::new_from_raw_mtl(device, &queue); + let session = Session::new(piet_device); + let query_pool = session.create_query_pool(12).unwrap(); + Self { + session, + pgpu_renderer: None, + query_pool, + width: 0, + height: 0, + is_color: false, + } + } + + pub fn render( + &mut self, + scene: &PgpuScene, + cmdbuf: &metal::CommandBufferRef, + target: &metal::TextureRef, + ) -> u32 { + let is_color = target.pixel_format() != metal::MTLPixelFormat::A8Unorm; + let width = target.width() as u32; + let height = target.height() as u32; + if self.pgpu_renderer.is_none() + || self.width != width + || self.height != height + || self.is_color != is_color + { + self.width = width; + self.height = height; + self.is_color = is_color; + let format = if is_color { + PixelFormat::Rgba8 + } else { + PixelFormat::A8 + }; + let config = RenderConfig::new(width as usize, height as usize).pixel_format(format); + unsafe { + self.pgpu_renderer = + piet_gpu::Renderer::new_from_config(&self.session, config, 1).ok(); + } + } + unsafe { + let mut cmd_buf = self.session.cmd_buf_from_raw_mtl(cmdbuf); + let dst_image = self + .session + .image_from_raw_mtl(target, self.width, self.height); + if let Some(renderer) = &mut self.pgpu_renderer { + renderer.upload_scene(&scene.encoded_scene(), 0).unwrap(); + renderer.record(&mut cmd_buf, &self.query_pool, 0); + // TODO later: we can bind the destination image and avoid the copy. + cmd_buf.blit_image(&renderer.image_dev, &dst_image); + } + } + 0 + } + + pub fn release(&mut self, _id: u32) { + // TODO: worry about freeing resources / managing overlapping submits + } +} + +/// Encoded streams and resources describing a vector graphics scene. +pub struct PgpuScene { + scene: Scene, + rcx: ResourceContext, +} + +impl PgpuScene { + pub fn new() -> Self { + Self { + scene: Scene::default(), + rcx: ResourceContext::new(), + } + } + + pub fn build(&mut self) -> PgpuSceneBuilder { + self.rcx.advance(); + PgpuSceneBuilder(piet_scene::scene::build_scene( + &mut self.scene, + &mut self.rcx, + )) + } + + fn encoded_scene<'a>(&'a self) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> { + let d = self.scene.data(); + EncodedSceneRef { + transform_stream: &d.transform_stream, + tag_stream: &d.tag_stream, + pathseg_stream: &d.pathseg_stream, + linewidth_stream: &d.linewidth_stream, + drawtag_stream: &d.drawtag_stream, + drawdata_stream: &d.drawdata_stream, + n_path: d.n_path, + n_pathseg: d.n_pathseg, + n_clip: d.n_clip, + ramp_data: self.rcx.ramp_data(), + } + } +} + +/// Builder for constructing an encoded scene. +pub struct PgpuSceneBuilder<'a>(piet_scene::scene::Builder<'a>); + +impl<'a> PgpuSceneBuilder<'a> { + pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) { + self.0.push_transform(*transform); + self.0.append(&glyph.fragment); + self.0.pop_transform(); + } + + pub fn finish(self) { + self.0.finish(); + } +} + +/// Tag and value for a font variation axis. +#[derive(Copy, Clone)] +#[repr(C)] +pub struct PgpuFontVariation { + /// Tag that specifies the axis. + pub tag: u32, + /// Requested setting for the axis. + pub value: f32, +} + +/// Context for loading and scaling glyphs. +pub struct PgpuGlyphContext(GlyphContext); + +impl PgpuGlyphContext { + pub fn new() -> Self { + Self(GlyphContext::new()) + } + + pub fn new_provider<'a>( + &'a mut self, + font_data: &'a [u8], + font_index: u32, + font_id: u64, + ppem: f32, + hint: bool, + variations: &[PgpuFontVariation], + ) -> Option { + let font = FontDataRef::new(font_data).and_then(|f| f.get(font_index))?; + Some(PgpuGlyphProvider( + self.0.new_provider( + &font, + Some(font_id), + ppem, + hint, + variations + .iter() + .map(|variation| (Tag(variation.tag), variation.value)), + ), + )) + } +} + +/// Context for loading a scaling glyphs from a specific font. +pub struct PgpuGlyphProvider<'a>(GlyphProvider<'a>); + +impl<'a> PgpuGlyphProvider<'a> { + pub fn get(&mut self, gid: u16) -> Option { + let fragment = self.0.get(gid)?; + Some(PgpuGlyph { fragment }) + } + + pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option { + let fragment = self.0.get_color(palette_index, gid)?; + Some(PgpuGlyph { fragment }) + } +} + +/// Encoded (possibly color) outline for a glyph. +pub struct PgpuGlyph { + fragment: Fragment, +} + +impl PgpuGlyph { + pub fn bbox(&self, transform: Option) -> Rect { + if let Some(transform) = &transform { + Rect::from_points(self.fragment.points().iter().map(|p| p.transform(transform))) + } else { + Rect::from_points(self.fragment.points()) + } + } +} diff --git a/piet-scene/src/geometry.rs b/piet-scene/src/geometry.rs index fbc8765..d844013 100644 --- a/piet-scene/src/geometry.rs +++ b/piet-scene/src/geometry.rs @@ -19,7 +19,7 @@ use core::borrow::Borrow; use core::hash::{Hash, Hasher}; /// Two dimensional point. -#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug)] +#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug, Pod, Zeroable)] #[repr(C)] pub struct Point { pub x: f32, @@ -168,7 +168,7 @@ impl std::ops::Mul for Affine { } /// Axis-aligned rectangle represented as minimum and maximum points. -#[derive(Copy, Clone, Default, Debug)] +#[derive(Copy, Clone, Default, Debug, Pod, Zeroable)] #[repr(C)] pub struct Rect { pub min: Point, diff --git a/piet-scene/src/glyph/mod.rs b/piet-scene/src/glyph/mod.rs index 0e13211..24e458b 100644 --- a/piet-scene/src/glyph/mod.rs +++ b/piet-scene/src/glyph/mod.rs @@ -17,11 +17,15 @@ pub use pinot; use crate::brush::{Brush, Color}; +use crate::geometry::Affine; use crate::path::Element; use crate::scene::{build_fragment, Fill, Fragment}; + use moscato::{Context, Scaler}; use pinot::{types::Tag, FontRef}; +use smallvec::SmallVec; + /// General context for creating scene fragments for glyph outlines. pub struct GlyphContext { ctx: Context, @@ -99,40 +103,70 @@ impl<'a> GlyphProvider<'a> { let glyph = self.scaler.color_glyph(palette_index, gid)?; let mut fragment = Fragment::default(); let mut builder = build_fragment(&mut fragment); + let mut xform_stack: SmallVec<[Affine; 8]> = SmallVec::new(); for command in glyph.commands() { match command { Command::PushTransform(xform) => { - builder.push_transform(convert_transform(xform)); + xform_stack.push(convert_transform(xform)); } - Command::PopTransform => builder.pop_transform(), + Command::PopTransform => { xform_stack.pop(); }, Command::PushClip(path_index) => { let path = glyph.path(*path_index)?; - builder.push_layer(Default::default(), convert_path(path.elements())); + if let Some(xform) = xform_stack.last() { + builder.push_layer( + Default::default(), + convert_transformed_path(path.elements(), xform), + ); + } else { + builder.push_layer(Default::default(), convert_path(path.elements())); + } } Command::PopClip => builder.pop_layer(), Command::PushLayer(bounds) => { - let rect = Rect { + let mut rect = Rect { min: Point::new(bounds.min.x, bounds.min.y), max: Point::new(bounds.max.x, bounds.max.y), }; + if let Some(xform) = xform_stack.last() { + rect.min = rect.min.transform(xform); + rect.max = rect.max.transform(xform); + } builder.push_layer(Default::default(), rect.elements()); } Command::PopLayer => builder.pop_layer(), Command::BeginBlend(bounds, mode) => { - let rect = Rect { + let mut rect = Rect { min: Point::new(bounds.min.x, bounds.min.y), max: Point::new(bounds.max.x, bounds.max.y), }; + if let Some(xform) = xform_stack.last() { + rect.min = rect.min.transform(xform); + rect.max = rect.max.transform(xform); + } builder.push_layer(convert_blend(*mode), rect.elements()) } Command::EndBlend => builder.pop_layer(), - Command::SimpleFill(path_index, brush, xform) => { + Command::SimpleFill(path_index, brush, brush_xform) => { let path = glyph.path(*path_index)?; let brush = convert_brush(brush); - let xform = xform.map(|xform| convert_transform(&xform)); - builder.fill(Fill::NonZero, &brush, xform, convert_path(path.elements())); + let brush_xform = brush_xform.map(|xform| convert_transform(&xform)); + if let Some(xform) = xform_stack.last() { + builder.fill( + Fill::NonZero, + &brush, + brush_xform, + convert_transformed_path(path.elements(), xform), + ); + } else { + builder.fill( + Fill::NonZero, + &brush, + brush_xform, + convert_path(path.elements()), + ); + } } - Command::Fill(brush, xform) => { + Command::Fill(brush, brush_xform) => { // TODO: this needs to compute a bounding box for // the parent clips } @@ -161,6 +195,28 @@ fn convert_path( }) } +fn convert_transformed_path( + path: impl Iterator + Clone, + xform: &Affine, +) -> impl Iterator + Clone { + use crate::geometry::Point; + let xform = *xform; + path.map(move |el| match el { + moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y).transform(&xform)), + moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y).transform(&xform)), + moscato::Element::QuadTo(p0, p1) => Element::QuadTo( + Point::new(p0.x, p0.y).transform(&xform), + Point::new(p1.x, p1.y).transform(&xform), + ), + moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo( + Point::new(p0.x, p0.y).transform(&xform), + Point::new(p1.x, p1.y).transform(&xform), + Point::new(p2.x, p2.y).transform(&xform), + ), + moscato::Element::Close => Element::Close, + }) +} + fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend { use crate::scene::{Blend, Compose, Mix}; use moscato::CompositeMode; diff --git a/piet-scene/src/scene/mod.rs b/piet-scene/src/scene/mod.rs index ba0b069..9f7be2f 100644 --- a/piet-scene/src/scene/mod.rs +++ b/piet-scene/src/scene/mod.rs @@ -23,8 +23,9 @@ pub use builder::{build_fragment, build_scene, Builder}; pub use style::*; use super::brush::*; -use super::geometry::{Affine, Rect}; +use super::geometry::{Affine, Point, Rect}; use super::path::Element; + use core::ops::Range; #[derive(Default)] @@ -94,6 +95,12 @@ pub struct Fragment { resources: FragmentResources, } +impl Fragment { + pub fn points(&self) -> &[Point] { + bytemuck::cast_slice(&self.data.pathseg_stream) + } +} + #[derive(Default)] struct FragmentResources { patches: Vec, From 3ff87c88a7bd3293046e40849a33293782ef85f4 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 10 May 2022 03:59:31 -0400 Subject: [PATCH 09/13] Make return value mutable Changes the return mutability for pgpu_glyph_provider_get() --- pgpu-render/pgpu.h | 2 +- pgpu-render/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pgpu-render/pgpu.h b/pgpu-render/pgpu.h index 7f93625..93d3d5c 100644 --- a/pgpu-render/pgpu.h +++ b/pgpu-render/pgpu.h @@ -118,7 +118,7 @@ PgpuGlyphProvider *pgpu_glyph_provider_new(PgpuGlyphContext *gcx, const PgpuFont /// Returns an encoded outline for the specified glyph provider and glyph id. /// May return nullptr if the requested glyph is not available. -const PgpuGlyph *pgpu_glyph_provider_get(PgpuGlyphProvider *provider, uint16_t gid); +PgpuGlyph *pgpu_glyph_provider_get(PgpuGlyphProvider *provider, uint16_t gid); /// Returns an encoded color outline for the specified glyph provider, color /// palette index and glyph id. May return nullptr if the requested glyph is diff --git a/pgpu-render/src/lib.rs b/pgpu-render/src/lib.rs index 2036f57..7d4c60b 100644 --- a/pgpu-render/src/lib.rs +++ b/pgpu-render/src/lib.rs @@ -172,11 +172,11 @@ pub unsafe extern "C" fn pgpu_glyph_provider_new( pub unsafe extern "C" fn pgpu_glyph_provider_get( provider: *mut PgpuGlyphProvider, gid: u16, -) -> *const PgpuGlyph { +) -> *mut PgpuGlyph { if let Some(glyph) = (*provider).get(gid) { Box::into_raw(Box::new(glyph)) } else { - std::ptr::null() + std::ptr::null_mut() } } From c95887b4df7a898e1eec9392984fd470f349ca73 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 10 May 2022 04:09:02 -0400 Subject: [PATCH 10/13] Change metal format to BGRA8Unorm This should fix channel swizzling on M1 and restore functionality on Intel --- piet-gpu-hal/src/metal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/piet-gpu-hal/src/metal.rs b/piet-gpu-hal/src/metal.rs index 307def8..03a23b0 100644 --- a/piet-gpu-hal/src/metal.rs +++ b/piet-gpu-hal/src/metal.rs @@ -350,7 +350,7 @@ impl crate::backend::Device for MtlDevice { //desc.set_mipmap_level_count(1); let mtl_format = match format { ImageFormat::A8 => metal::MTLPixelFormat::R8Unorm, - ImageFormat::Rgba8 => metal::MTLPixelFormat::RGBA8Unorm, + ImageFormat::Rgba8 => metal::MTLPixelFormat::BGRA8Unorm, }; desc.set_pixel_format(mtl_format); desc.set_usage(metal::MTLTextureUsage::ShaderRead | metal::MTLTextureUsage::ShaderWrite); From a9356cc50b8b43c2e8964a4b2e692adbe1569f72 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 10 May 2022 04:19:36 -0400 Subject: [PATCH 11/13] Change color packing to match target format --- piet-scene/src/brush/color.rs | 4 ---- piet-scene/src/main.rs | 32 +---------------------------- piet-scene/src/resource/gradient.rs | 2 +- 3 files changed, 2 insertions(+), 36 deletions(-) diff --git a/piet-scene/src/brush/color.rs b/piet-scene/src/brush/color.rs index b9cbe52..ef519ec 100644 --- a/piet-scene/src/brush/color.rs +++ b/piet-scene/src/brush/color.rs @@ -48,10 +48,6 @@ impl Color { ) } - pub fn pack(self) -> u32 { - (self.b as u32) << 24 | (self.g as u32) << 16 | (self.r as u32) << 8 | self.a as u32 - } - pub fn to_premul_u32(self) -> u32 { let a = self.a as f64 / 255.0; let r = (self.r as f64 * a) as u32; diff --git a/piet-scene/src/main.rs b/piet-scene/src/main.rs index b1b777b..f0f6e30 100644 --- a/piet-scene/src/main.rs +++ b/piet-scene/src/main.rs @@ -306,7 +306,7 @@ fn test_scene1(scene: &mut Scene, rcx: &mut ResourceContext) { //b.push_transform(Affine::translate(200., 200.) * Affine::rotate(45f32.to_radians())); b.fill( Fill::NonZero, - // &Brush::Solid(Color::rgba8(0, 255, 0, 255)), + // &Brush::Solid(Color::rgba8(255, 0, 0, 255)), &radial, None, //Some(Affine::rotate(90f32.to_radians())), Rect { @@ -354,33 +354,3 @@ fn render_info_string(rc: &mut impl RenderContext, info: &str) { rc.draw_text(&layout, Point::new(110.0, 50.0)); } -// use piet_scene::geometry::*; -// use piet_scene::path::*; -// use piet_scene::scene::*; -// use piet_scene::{geometry::*, path::*, resource::ResourceContext, scene::*}; - -// fn main() { -// let mut scene = Scene::default(); -// let mut rcx = ResourceContext::new(); -// let mut sb = build_scene(&mut scene, &mut rcx); - -// sb.push_layer(Blend::default(), Rect::default().elements()); - -// // let mut path = Path::new(); -// // let mut b = PathBuilder::new(&mut path); -// // b.move_to(100., 100.); -// // b.line_to(200., 200.); -// // b.close_path(); -// // b.move_to(50., 50.); -// // b.line_to(600., 150.); -// // b.move_to(4., 2.); -// // b.quad_to(8., 8., 9., 9.); -// // b.close_path(); -// // println!("{:?}", path); -// // for el in path.elements() { -// // println!("{:?}", el); -// // } -// //sb.push_layer(path.elements(), BlendMode::default()); - -// sb.push_layer(Blend::default(), [Element::MoveTo((0., 0.).into())]); -// } diff --git a/piet-scene/src/resource/gradient.rs b/piet-scene/src/resource/gradient.rs index bd2491c..39b7f40 100644 --- a/piet-scene/src/resource/gradient.rs +++ b/piet-scene/src/resource/gradient.rs @@ -133,6 +133,6 @@ impl ColorF64 { let g = ((self.0[1] * a).min(1.0).max(0.0) * 255.0) as u32; let b = ((self.0[2] * a).min(1.0).max(0.0) * 255.0) as u32; let a = (a * 255.0) as u32; - r | (g << 8) | (b << 16) | (a << 24) + b | (g << 8) | (r << 16) | (a << 24) } } From 6f9e53459ad570f365f6d1b8454c393abb52aa8f Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 10 May 2022 20:07:41 -0400 Subject: [PATCH 12/13] Address review feedback * Change pgpu-render header file generator to add a comment noting that the file is auto-generated * Remove bin target and associated dependencies from piet-scene crate * Remove FP constructors from the Color type * Better codegen and rounding in Color::to_premul_u32() * Add kurbo attribution for piet_scene::geometry module --- Cargo.lock | 8 - pgpu-render/build.rs | 10 +- pgpu-render/pgpu.h | 2 + piet-scene/Cargo.toml | 10 - piet-scene/src/brush/color.rs | 25 +-- piet-scene/src/geometry.rs | 2 + piet-scene/src/main.rs | 356 ---------------------------------- 7 files changed, 13 insertions(+), 400 deletions(-) delete mode 100644 piet-scene/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 938c91f..1f80fa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1031,17 +1031,9 @@ name = "piet-scene" version = "0.1.0" dependencies = [ "bytemuck", - "clap", "moscato", - "piet", - "piet-gpu", - "piet-gpu-hal", "pinot", - "png", - "rand", - "roxmltree", "smallvec", - "winit", ] [[package]] diff --git a/pgpu-render/build.rs b/pgpu-render/build.rs index 182c72c..f565804 100644 --- a/pgpu-render/build.rs +++ b/pgpu-render/build.rs @@ -20,10 +20,10 @@ use std::env; fn main() { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - cbindgen::Builder::new() - .with_crate(crate_dir) - .generate() - .expect("Unable to generate bindings") - .write_to_file("pgpu.h"); + .with_crate(crate_dir) + .with_header("/** Automatically generated from pgpu-render/src/lib.rs with cbindgen. **/") + .generate() + .expect("Unable to generate bindings") + .write_to_file("pgpu.h"); } diff --git a/pgpu-render/pgpu.h b/pgpu-render/pgpu.h index 93d3d5c..a72a161 100644 --- a/pgpu-render/pgpu.h +++ b/pgpu-render/pgpu.h @@ -1,3 +1,5 @@ +/** Automatically generated from pgpu-render/src/lib.rs with cbindgen. **/ + #include #include #include diff --git a/piet-scene/Cargo.toml b/piet-scene/Cargo.toml index 0e9ca25..df66483 100644 --- a/piet-scene/Cargo.toml +++ b/piet-scene/Cargo.toml @@ -9,13 +9,3 @@ bytemuck = { version = "1.7.2", features = ["derive"] } smallvec = "1.8.0" pinot = "0.1.5" moscato = "0.1.2" - -# remove these and move demo to another directory -piet-gpu = { path = "../piet-gpu" } -piet-gpu-hal = { path = "../piet-gpu-hal" } -winit = "0.25" -piet = "0.2.0" -png = "0.16.2" -rand = "0.7.3" -roxmltree = "0.13" -clap = "2.33" diff --git a/piet-scene/src/brush/color.rs b/piet-scene/src/brush/color.rs index ef519ec..a377888 100644 --- a/piet-scene/src/brush/color.rs +++ b/piet-scene/src/brush/color.rs @@ -31,28 +31,11 @@ impl Color { Self { r, g, b, a } } - pub fn rgb>(r: C, g: C, b: C) -> Self { - Self::rgb8( - (r.into() / 255.0) as u8, - (g.into() / 255.0) as u8, - (b.into() / 255.0) as u8, - ) - } - - pub fn rgba>(r: C, g: C, b: C, a: C) -> Self { - Self::rgba8( - (r.into() / 255.0) as u8, - (g.into() / 255.0) as u8, - (b.into() / 255.0) as u8, - (a.into() / 255.0) as u8, - ) - } - pub fn to_premul_u32(self) -> u32 { - let a = self.a as f64 / 255.0; - let r = (self.r as f64 * a) as u32; - let g = (self.g as f64 * a) as u32; - let b = (self.b as f64 * a) as u32; + let a = self.a as f64 * (1.0 / 255.0); + let r = (self.r as f64 * a).round() as u32; + let g = (self.g as f64 * a).round() as u32; + let b = (self.b as f64 * a).round() as u32; (r << 24) | (g << 16) | (b << 8) | self.a as u32 } } diff --git a/piet-scene/src/geometry.rs b/piet-scene/src/geometry.rs index d844013..1ea8f33 100644 --- a/piet-scene/src/geometry.rs +++ b/piet-scene/src/geometry.rs @@ -14,6 +14,8 @@ // // Also licensed under MIT license, at your choice. +// This module is based in part on kurbo (https://github.com/linebender/kurbo) + use bytemuck::{Pod, Zeroable}; use core::borrow::Borrow; use core::hash::{Hash, Hasher}; diff --git a/piet-scene/src/main.rs b/piet-scene/src/main.rs deleted file mode 100644 index f0f6e30..0000000 --- a/piet-scene/src/main.rs +++ /dev/null @@ -1,356 +0,0 @@ -use piet::kurbo::Point; -use piet::{RenderContext, Text, TextAttribute, TextLayoutBuilder}; -use piet_gpu_hal::{CmdBuf, Error, ImageLayout, Instance, Session, SubmittedCmdBuf}; - -use piet_gpu::{test_scenes, EncodedSceneRef, PietGpuRenderContext, Renderer}; - -use piet_scene::resource::ResourceContext; -use piet_scene::scene::{build_scene, Scene}; - -use clap::{App, Arg}; - -use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; - -const NUM_FRAMES: usize = 2; - -const WIDTH: usize = 2048; -const HEIGHT: usize = 1536; - -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 { - width: (WIDTH / 2) as f64, - height: (HEIGHT / 2) as f64, - }) - .with_resizable(false) // currently not supported - .build(&event_loop)?; - - let (instance, surface) = Instance::new(Some(&window), Default::default())?; - let mut info_string = "info".to_string(); - - let mut scene = Scene::default(); - let mut rcx = ResourceContext::new(); - unsafe { - let device = instance.device(surface.as_ref())?; - let mut swapchain = - instance.swapchain(WIDTH / 2, HEIGHT / 2, &device, surface.as_ref().unwrap())?; - let session = Session::new(device); - - let mut current_frame = 0; - let present_semaphores = (0..NUM_FRAMES) - .map(|_| session.create_semaphore()) - .collect::, Error>>()?; - let query_pools = (0..NUM_FRAMES) - .map(|_| session.create_query_pool(12)) - .collect::, Error>>()?; - let mut cmd_bufs: [Option; NUM_FRAMES] = Default::default(); - let mut submitted: [Option; NUM_FRAMES] = Default::default(); - - let mut renderer = Renderer::new(&session, WIDTH, HEIGHT, NUM_FRAMES)?; - let mut mode = 0usize; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Poll; // `ControlFlow::Wait` if only re-render on event - - match event { - Event::WindowEvent { event, window_id } if window_id == window.id() => { - use winit::event::{ElementState, VirtualKeyCode}; - match event { - WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - } - WindowEvent::KeyboardInput { input, .. } => { - if input.state == ElementState::Pressed { - match input.virtual_keycode { - Some(VirtualKeyCode::Left) => mode = mode.wrapping_sub(1), - Some(VirtualKeyCode::Right) => mode = mode.wrapping_add(1), - _ => {} - } - } - } - _ => (), - } - } - Event::MainEventsCleared => { - window.request_redraw(); - } - Event::RedrawRequested(window_id) if window_id == window.id() => { - let frame_idx = current_frame % NUM_FRAMES; - - if let Some(submitted) = submitted[frame_idx].take() { - cmd_bufs[frame_idx] = submitted.wait().unwrap(); - let ts = session.fetch_query_pool(&query_pools[frame_idx]).unwrap(); - if !ts.is_empty() { - info_string = format!( - "{:.3}ms :: e:{:.3}ms|alloc:{:.3}ms|cp:{:.3}ms|bd:{:.3}ms|bin:{:.3}ms|cr:{:.3}ms|r:{:.3}ms", - ts[10] * 1e3, - ts[0] * 1e3, - (ts[1] - ts[0]) * 1e3, - (ts[2] - ts[1]) * 1e3, - (ts[4] - ts[3]) * 1e3, - (ts[6] - ts[5]) * 1e3, - (ts[8] - ts[7]) * 1e3, - (ts[10] - ts[9]) * 1e3, - ); - } - } - - let mut ctx = PietGpuRenderContext::new(); - render_info_string(&mut ctx, &info_string); - - ctx = PietGpuRenderContext::new(); - test_scene1_old(&mut ctx); - //let mut encoded_scene_old = ctx.encoded_scene(); - // let ramp_data = ctx.get_ramp_data(); - //encoded_scene_old.ramp_data = &ramp_data; - test_scene1(&mut scene, &mut rcx); - let encoded_scene = scene_to_encoded_scene(&scene, &rcx); - // println!("{:?}\n============\n{:?}", encoded_scene_old, encoded_scene); - // panic!(); - let res = if mode & 1 == 0 { - render_info_string(&mut ctx, &info_string); - renderer.upload_render_ctx(&mut ctx, frame_idx) - } else { - renderer.upload_scene(&encoded_scene, frame_idx) - }; - if let Err(e) = res { - println!("error in uploading: {}", e); - } - - let (image_idx, acquisition_semaphore) = swapchain.next().unwrap(); - let swap_image = swapchain.image(image_idx); - let query_pool = &query_pools[frame_idx]; - let mut cmd_buf = cmd_bufs[frame_idx].take().unwrap_or_else(|| session.cmd_buf().unwrap()); - cmd_buf.begin(); - renderer.record(&mut cmd_buf, &query_pool, frame_idx); - - // Image -> Swapchain - cmd_buf.image_barrier( - &swap_image, - ImageLayout::Undefined, - ImageLayout::BlitDst, - ); - cmd_buf.blit_image(&renderer.image_dev, &swap_image); - cmd_buf.image_barrier(&swap_image, ImageLayout::BlitDst, ImageLayout::Present); - cmd_buf.finish(); - - submitted[frame_idx] = Some(session - .run_cmd_buf( - cmd_buf, - &[&acquisition_semaphore], - &[&present_semaphores[frame_idx]], - ) - .unwrap()); - - swapchain - .present(image_idx, &[&present_semaphores[frame_idx]]) - .unwrap(); - - current_frame += 1; - } - Event::LoopDestroyed => { - for cmd_buf in &mut submitted { - // Wait for command list submission, otherwise dropping of renderer may - // cause validation errors (and possibly crashes). - if let Some(cmd_buf) = cmd_buf.take() { - cmd_buf.wait().unwrap(); - } - } - } - _ => (), - } - }) - } -} - -fn test_scene1_old(ctx: &mut PietGpuRenderContext) { - use piet::kurbo::{Affine, Rect, Vec2}; - use piet::{Color, GradientStop}; - ctx.transform(Affine::translate(Vec2::new(200., 200.)) * Affine::rotate(45f64.to_radians())); - let linear = ctx - .gradient(piet::FixedGradient::Linear(piet::FixedLinearGradient { - start: Point::new(0., 0.), - end: Point::new(200., 100.), - stops: vec![ - GradientStop { - pos: 0.0, - color: Color::rgb8(0, 0, 255), - }, - GradientStop { - pos: 0.5, - color: Color::rgb8(0, 255, 0), - }, - GradientStop { - pos: 1.0, - color: Color::rgb8(255, 0, 0), - }, - ], - })) - .unwrap(); - let radial = ctx - .gradient(piet::FixedGradient::Radial(piet::FixedRadialGradient { - center: Point::new(50., 50.), - origin_offset: Vec2::new(0., 0.), - radius: 240., - stops: vec![ - GradientStop { - pos: 0.0, - color: Color::rgb8(0, 0, 255), - }, - GradientStop { - pos: 0.5, - color: Color::rgb8(0, 255, 0), - }, - GradientStop { - pos: 1.0, - color: Color::rgb8(255, 0, 0), - }, - ], - })) - .unwrap(); - ctx.fill_transform( - Rect { - x0: 0., - y0: 0., - x1: 200., - y1: 100., - }, - // &piet::PaintBrush::Color(piet::Color::rgb8(0, 255, 0)), - &radial, - // &piet::FixedGradient::Linear(piet::FixedLinearGradient { - // start: Point::new(0., 0.), - // end: Point::new(200., 100.), - // stops: vec![ - // GradientStop { - // pos: 0.0, - // color: Color::rgb8(0, 0, 255) - // }, - // GradientStop { - // pos: 0.5, - // color: Color::rgb8(0, 255, 0) - // }, - // GradientStop { - // pos: 1.0, - // color: Color::rgb8(255, 0, 0) - // }, - // ], - // }), - Affine::default(), // rotate(90f64.to_radians()), - ); -} - -fn test_scene1(scene: &mut Scene, rcx: &mut ResourceContext) { - use piet_scene::brush::*; - use piet_scene::geometry::{Affine, Point, Rect}; - use piet_scene::scene::*; - let mut fragment = Fragment::default(); - let mut b = build_fragment(&mut fragment); - let linear = Brush::LinearGradient(LinearGradient { - start: Point::new(0., 0.), - end: Point::new(200., 100.), - extend: Extend::Pad, - stops: (&[ - Stop { - offset: 0., - color: Color::rgb8(0, 0, 255), - }, - Stop { - offset: 0.5, - color: Color::rgb8(0, 255, 0), - }, - Stop { - offset: 1., - color: Color::rgb8(255, 0, 0), - }, - ][..]) - .into(), - }); - let radial = Brush::RadialGradient(RadialGradient { - center0: Point::new(50., 50.), - center1: Point::new(50., 50.), - radius0: 0., - radius1: 240., - extend: Extend::Pad, - stops: (&[ - Stop { - offset: 0., - color: Color::rgb8(0, 0, 255), - }, - Stop { - offset: 0.5, - color: Color::rgb8(0, 255, 0), - }, - Stop { - offset: 1., - color: Color::rgb8(255, 0, 0), - }, - ][..]) - .into(), - }); - //b.push_transform(Affine::translate(200., 200.) * Affine::rotate(45f32.to_radians())); - b.fill( - Fill::NonZero, - // &Brush::Solid(Color::rgba8(255, 0, 0, 255)), - &radial, - None, //Some(Affine::rotate(90f32.to_radians())), - Rect { - min: Point::new(0., 0.), - max: Point::new(200., 100.), - } - .elements(), - ); - b.finish(); - let mut b = build_scene(scene, rcx); - b.push_transform(Affine::translate(200., 200.) * Affine::rotate(45f32.to_radians())); - b.append(&fragment); - b.pop_transform(); - b.push_transform(Affine::translate(400., 600.)); - b.append(&fragment); - b.finish(); -} - -fn scene_to_encoded_scene<'a>( - scene: &'a Scene, - rcx: &'a ResourceContext, -) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> { - let d = scene.data(); - EncodedSceneRef { - transform_stream: &d.transform_stream, - tag_stream: &d.tag_stream, - pathseg_stream: &d.pathseg_stream, - linewidth_stream: &d.linewidth_stream, - drawtag_stream: &d.drawtag_stream, - drawdata_stream: &d.drawdata_stream, - n_path: d.n_path, - n_pathseg: d.n_pathseg, - n_clip: d.n_clip, - ramp_data: rcx.ramp_data(), - } -} - -fn render_info_string(rc: &mut impl RenderContext, info: &str) { - let layout = rc - .text() - .new_text_layout(info.to_string()) - .default_attribute(TextAttribute::FontSize(40.0)) - .build() - .unwrap(); - rc.draw_text(&layout, Point::new(110.0, 50.0)); -} - From 3afe2eaac2105544038201e97469deebdcda75e5 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 11 May 2022 14:39:09 -0400 Subject: [PATCH 13/13] Fix transform stack bugs in color glyph loader --- piet-scene/src/glyph/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/piet-scene/src/glyph/mod.rs b/piet-scene/src/glyph/mod.rs index 24e458b..3bfa36c 100644 --- a/piet-scene/src/glyph/mod.rs +++ b/piet-scene/src/glyph/mod.rs @@ -107,7 +107,12 @@ impl<'a> GlyphProvider<'a> { for command in glyph.commands() { match command { Command::PushTransform(xform) => { - xform_stack.push(convert_transform(xform)); + let xform = if let Some(parent) = xform_stack.last() { + convert_transform(xform) * *parent + } else { + convert_transform(xform) + }; + xform_stack.push(xform); } Command::PopTransform => { xform_stack.pop(); }, Command::PushClip(path_index) => { @@ -128,8 +133,7 @@ impl<'a> GlyphProvider<'a> { max: Point::new(bounds.max.x, bounds.max.y), }; if let Some(xform) = xform_stack.last() { - rect.min = rect.min.transform(xform); - rect.max = rect.max.transform(xform); + rect = rect.transform(xform); } builder.push_layer(Default::default(), rect.elements()); } @@ -140,8 +144,7 @@ impl<'a> GlyphProvider<'a> { max: Point::new(bounds.max.x, bounds.max.y), }; if let Some(xform) = xform_stack.last() { - rect.min = rect.min.transform(xform); - rect.max = rect.max.transform(xform); + rect = rect.transform(xform); } builder.push_layer(convert_blend(*mode), rect.elements()) } @@ -154,7 +157,7 @@ impl<'a> GlyphProvider<'a> { builder.fill( Fill::NonZero, &brush, - brush_xform, + brush_xform.map(|x| x * *xform), convert_transformed_path(path.elements(), xform), ); } else {