From 07e07c7544736467baf63ca8760cbd2fd585b36c Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Mon, 15 Mar 2021 12:28:04 +0100 Subject: [PATCH] ensure consistent path segment transformation As described in #62, the non-deterministic scene monoid may result in slightly different transformations for path segments in an otherwise closed path. This change ensures consistent transformation across paths in three steps. First, absolute transformations computed by the scene monoid is stored along with path segments and annotated elements. Second, elements.comp no longer transforms path segments. Instead, each segment is stored untransformed along with a reference to its absolute transformation. Finally, path_coarse performs the transformation of path segments. Because all segments in a path share a single transformation reference, the inconsistency in #62 is avoided. Fixes #62 Signed-off-by: Elias Naur --- piet-gpu-types/src/pathseg.rs | 3 +++ piet-gpu-types/src/state.rs | 1 + piet-gpu-types/src/tile.rs | 4 +++ piet-gpu/bin/cli.rs | 3 ++- piet-gpu/bin/winit.rs | 3 ++- piet-gpu/shader/backdrop.spv | Bin 11040 -> 11088 bytes piet-gpu/shader/binning.spv | Bin 15720 -> 15768 bytes piet-gpu/shader/coarse.spv | Bin 52252 -> 52300 bytes piet-gpu/shader/elements.comp | 44 ++++++++++++++++++------------- piet-gpu/shader/elements.spv | Bin 63752 -> 64920 bytes piet-gpu/shader/kernel4.spv | Bin 36872 -> 36920 bytes piet-gpu/shader/path_coarse.comp | 11 ++++++++ piet-gpu/shader/path_coarse.spv | Bin 37768 -> 42252 bytes piet-gpu/shader/pathseg.h | 20 +++++++++----- piet-gpu/shader/setup.h | 1 + piet-gpu/shader/state.h | 6 ++++- piet-gpu/shader/tile.h | 39 +++++++++++++++++++++++++++ piet-gpu/shader/tile_alloc.spv | Bin 14668 -> 14716 bytes piet-gpu/src/lib.rs | 14 ++++++---- piet-gpu/src/render_ctx.rs | 9 +++++++ 20 files changed, 125 insertions(+), 33 deletions(-) diff --git a/piet-gpu-types/src/pathseg.rs b/piet-gpu-types/src/pathseg.rs index 080344d..a679e44 100644 --- a/piet-gpu-types/src/pathseg.rs +++ b/piet-gpu-types/src/pathseg.rs @@ -9,6 +9,8 @@ piet_gpu! { p2: [f32; 2], p3: [f32; 2], path_ix: u32, + // trans_ix is 1-based, 0 means no transformation. + trans_ix: u32, // A note: the layout of this struct is shared with // PathStrokeCubic. In that case, we actually write // [0.0, 0.0] as the stroke field, to minimize divergence. @@ -19,6 +21,7 @@ piet_gpu! { p2: [f32; 2], p3: [f32; 2], path_ix: u32, + trans_ix: u32, // halfwidth in both x and y for binning stroke: [f32; 2], } diff --git a/piet-gpu-types/src/state.rs b/piet-gpu-types/src/state.rs index 602fab9..6e2b581 100644 --- a/piet-gpu-types/src/state.rs +++ b/piet-gpu-types/src/state.rs @@ -11,6 +11,7 @@ piet_gpu! { flags: u32, path_count: u32, pathseg_count: u32, + trans_count: u32, } } } diff --git a/piet-gpu-types/src/tile.rs b/piet-gpu-types/src/tile.rs index 38ee93b..27e87f4 100644 --- a/piet-gpu-types/src/tile.rs +++ b/piet-gpu-types/src/tile.rs @@ -18,5 +18,9 @@ piet_gpu! { y_edge: f32, next: Ref, } + struct TransformSeg { + mat: [f32; 4], + translate: [f32; 2], + } } } diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index dfed520..646a505 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -248,10 +248,11 @@ fn main() -> Result<(), Error> { } let n_paths = ctx.path_count(); let n_pathseg = ctx.pathseg_count(); + let n_trans = ctx.trans_count(); let scene = ctx.get_scene_buf(); //dump_scene(&scene); - let renderer = Renderer::new(&session, scene, n_paths, n_pathseg)?; + let renderer = Renderer::new(&session, scene, n_paths, n_pathseg, n_trans)?; let image_buf = session.create_buffer((WIDTH * HEIGHT * 4) as u64, MemFlags::host_coherent())?; diff --git a/piet-gpu/bin/winit.rs b/piet-gpu/bin/winit.rs index 786c78c..db501e9 100644 --- a/piet-gpu/bin/winit.rs +++ b/piet-gpu/bin/winit.rs @@ -40,9 +40,10 @@ fn main() -> Result<(), Error> { render_scene(&mut ctx); let n_paths = ctx.path_count(); let n_pathseg = ctx.pathseg_count(); + let n_trans = ctx.pathseg_count(); let scene = ctx.get_scene_buf(); - let renderer = Renderer::new(&session, scene, n_paths, n_pathseg)?; + let renderer = Renderer::new(&session, scene, n_paths, n_pathseg, n_trans)?; let mut submitted: Option = None; let mut last_frame_idx = 0; diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv index 81d26c0a2f0d8ca51e177557603c4d52f00282ca..48fcb48a01da2d8cc342d45f4b04b41e2ce931d3 100644 GIT binary patch delta 68 zcmZ1wb|Gv-4VyR{0~^Cy1_lOBATB9N%qxyh%*n}5-dxSLpP8Q(E~N~VR@rRICCbdm OGufM0b8`Xj23Y`4)eq?a delta 28 kcmcZ*wjgXn4cq1^Z2Oour*R1|GjdNZ=hfW2gm;ZB0HOX03;+NC diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index a9a05f5db3e27349173b482ec0610889d76d1d15..be8662d160948c341821178d72acf015a71001cf 100644 GIT binary patch delta 68 zcmaD+HKTgN8V+$b1~!J@3=9mMKwMIkm{%O1n3I#Aym>XpY8HN0xRf$bT4i$}?^I?+ Pp2^PzG&l1I#^?Y5oh=c_ delta 28 kcmbPH{i15a8jj6JI99Q2ZsMK5%*Z`iSWt7bgJ6UX0JPBxPyhe` diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 974e400ac3e80ab54ef2d6aaec0765a9b78b518f..23eb96295eaa1b2ecb42c21a75a0af2a600e3e27 100644 GIT binary patch delta 70 zcmbO;gZa!1<_!zv#n~9x7#1)yFmN(3Fq9M}<`u^$=H%ojZ=Nszo132%E~N~VR@ppN T`y~e>&*XX&&CN?pDy9PfsGSpM delta 30 ocmV+(0O9}4m;;=c1F)bUv$!An3$w;6> 1; c.path_count = a.path_count + b.path_count; c.pathseg_count = a.pathseg_count + b.pathseg_count; + c.trans_count = a.trans_count + b.trans_count; return c; } @@ -106,6 +108,7 @@ State map_element(ElementRef ref) { c.flags = 0; c.path_count = 0; c.pathseg_count = 0; + c.trans_count = 0; switch (tag) { case Element_FillLine: case Element_StrokeLine: @@ -146,6 +149,7 @@ State map_element(ElementRef ref) { Transform t = Element_Transform_read(ref); c.mat = t.mat; c.translate = t.translate; + c.trans_count = 1; break; } return c; @@ -205,6 +209,7 @@ void main() { exclusive.flags = 0; exclusive.path_count = 0; exclusive.pathseg_count = 0; + exclusive.trans_count = 0; // Publish aggregate for this partition if (gl_LocalInvocationID.x == WG_SIZE - 1) { @@ -290,14 +295,13 @@ void main() { case Element_FillLine: case Element_StrokeLine: LineSeg line = Element_StrokeLine_read(this_ref); - vec2 p0 = st.mat.xy * line.p0.x + st.mat.zw * line.p0.y + st.translate; - vec2 p1 = st.mat.xy * line.p1.x + st.mat.zw * line.p1.y + st.translate; PathStrokeCubic path_cubic; - path_cubic.p0 = p0; - path_cubic.p1 = mix(p0, p1, 1.0 / 3.0); - path_cubic.p2 = mix(p1, p0, 1.0 / 3.0); - path_cubic.p3 = p1; + path_cubic.p0 = line.p0; + path_cubic.p1 = mix(line.p0, line.p1, 1.0 / 3.0); + path_cubic.p2 = mix(line.p1, line.p0, 1.0 / 3.0); + path_cubic.p3 = line.p1; path_cubic.path_ix = st.path_count; + path_cubic.trans_ix = st.trans_count; if (tag == Element_StrokeLine) { path_cubic.stroke = get_linewidth(st); } else { @@ -313,15 +317,12 @@ void main() { case Element_FillQuad: case Element_StrokeQuad: QuadSeg quad = Element_StrokeQuad_read(this_ref); - p0 = st.mat.xy * quad.p0.x + st.mat.zw * quad.p0.y + st.translate; - p1 = st.mat.xy * quad.p1.x + st.mat.zw * quad.p1.y + st.translate; - vec2 p2 = st.mat.xy * quad.p2.x + st.mat.zw * quad.p2.y + st.translate; - path_cubic; - path_cubic.p0 = p0; - path_cubic.p1 = mix(p1, p0, 1.0 / 3.0); - path_cubic.p2 = mix(p1, p2, 1.0 / 3.0); - path_cubic.p3 = p2; + path_cubic.p0 = quad.p0; + path_cubic.p1 = mix(quad.p1, quad.p0, 1.0 / 3.0); + path_cubic.p2 = mix(quad.p1, quad.p2, 1.0 / 3.0); + path_cubic.p3 = quad.p2; path_cubic.path_ix = st.path_count; + path_cubic.trans_ix = st.trans_count; if (tag == Element_StrokeQuad) { path_cubic.stroke = get_linewidth(st); } else { @@ -337,12 +338,12 @@ void main() { case Element_FillCubic: case Element_StrokeCubic: CubicSeg cubic = Element_StrokeCubic_read(this_ref); - path_cubic; - path_cubic.p0 = st.mat.xy * cubic.p0.x + st.mat.zw * cubic.p0.y + st.translate; - path_cubic.p1 = st.mat.xy * cubic.p1.x + st.mat.zw * cubic.p1.y + st.translate; - path_cubic.p2 = st.mat.xy * cubic.p2.x + st.mat.zw * cubic.p2.y + st.translate; - path_cubic.p3 = st.mat.xy * cubic.p3.x + st.mat.zw * cubic.p3.y + st.translate; + path_cubic.p0 = cubic.p0; + path_cubic.p1 = cubic.p1; + path_cubic.p2 = cubic.p2; + path_cubic.p3 = cubic.p3; path_cubic.path_ix = st.path_count; + path_cubic.trans_ix = st.trans_count; if (tag == Element_StrokeCubic) { path_cubic.stroke = get_linewidth(st); } else { @@ -388,6 +389,11 @@ void main() { out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); Annotated_EndClip_write(conf.anno_alloc, out_ref, anno_end_clip); break; + case Element_Transform: + TransformSeg transform = TransformSeg(st.mat, st.translate); + TransformSegRef trans_ref = TransformSegRef(conf.trans_alloc.offset + (st.trans_count - 1) * TransformSeg_size); + TransformSeg_write(conf.trans_alloc, trans_ref, transform); + break; } } } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 7857b93afcace676f40e1c112178bde68866ed72..70db1aeffb3cda0577af9789e16b5dfa61a5de5d 100644 GIT binary patch literal 64920 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 KpJOO@4F4Z|7CDdr literal 63752 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

dAc~xS9JTH1*^@8LZ~n{QNovExF6jufL#cOYT#_ z#!)|zardJB)4vSOwH$k^U$KU+y&sW->=6r$E`@h&YK3_ejMlG)2vtW6iubv0{e5IbT z{X;uvp0DKk`T1f_;dd7iK0Z=2_}kSHWstLwUY>4X!?bGoS6RgN?5(&sT4N)t#FyXyvKpO>jB?Z^6}kHpuhU+i>;f z^VK_O+Kjsstvq9T7i=!apIq<3)$@GyK3J`MzWNYe&YOL=MEi(STb?^Ut~K{Ya(;sL z0cWmbxqjC7DXsZZ%jaNg*7g~#JkMAE0{eVrEc{Tr^H z=c{kPzcGEPM@!S0xwnDexz;j`uXZ-xX)L{ zvY+xyJ=JbB~9np4{Vu)jXS@UlX7uclr4> zA-cBYo(OCl^#d7qFY2EdY)#s%$Iq`xz}8bfUrmapo_>>o?W>-ClY?DH=fF9Z>!;m0 zo&ww)=agvb8Rt}BHBS#h(ZBspYicyd*?i`j23=dmIW5>Y>d8GFxS4x;H1*`30j%cP zoZ}hMlDnManb5T*_sn49sAsNb0b7%{%vCRNbB=qXsi)tpVEd}4-)vy#IG@RK{j@vB zeZb9e&W@&@an1o&^Ykd^cuq9O*?bP03td~rIXBoi>d8G1xS4xiH1*`353J_doa6b? zlDnMa1<k>!;m0UJTqE z=i+GU8RrsUHP7Z8FNx+j?`Go5d&N@d+A_{Rf{mk|+)IO-xtBpxPwr*GYM#wGUJfm} z%Q;>iU0ZUm05*<#=4wT-HEGLStpsk)@yclG>9>mhoa*VfD%d%84!l>$TYk(juv(4( z7`J?GTOCc^82YTie~gjmHn~3e{9F@UJ~yldSM&ELeQsL^?BR2pwzWAoESv>s%Ks&vV=QV4vI6Gqw%1Q&*nbzuI z-v+g4z8=f-Cad1~1j zT+aVCaJBQuljpW=;p)xjw(Zcg8P{hZdB)Ti+f=EaG%?ZWk2oZbKAu9Gmg(~`(Uf;ID3QTUSIzC?R~)`Y1PfM7p+?A{S#QN zyax7%tH@%auzl6j_cX9|n4|nGI~`qH{LU!+`V=|NMAw!aXMydjesG=l zY_PetO~`YoV?75<7)H*!!9qW`*5i}7=b<}4+B1jegPr5wh~d7H>z8}{Mc`b+7lIw5 zbL0Dni{aXhMN?hEf1HQ%v+`0j_1xnx1FMzy_$%P*mviR+awXXK+Puf#Nc^k7>ehJy zt=zfYjcfKAa9O7{dQZI;U0d!o*MZeMQup=nt2rH;vE}+1!@M_w&8O`KTDi9KvE2-I z{v<5H_SEwbxVcyU zhNhmq@-SGf+$)d3)yuu|DBSqkvR57ht2-A}dxJj@Yld<_J5gHp8l_coBiKFQ;*M^VEbi` zCm;2-cxbd}RpL_^b&pwf7 zpL`51>&!m+1YKM9$){j7kJSAc{3FiHfn2}rlYfD;Pd*3BwK@M^f}KBW`hr%TeexCf zA$*K&Kkel{nXK3+-(t%?`35Y{KKTxO7-n_zd`+vCHT(grmbG!8{0LXiKKTi(X8-SL z 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 X7}%WJk7ew=us;q~cYMyF+&TPzM*pEB diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index ede04e47f15021d19f8e1862a67069fe3eba757a..b48d66111b9770ba3ee9aaa659bfb7e3d30aebec 100644 GIT binary patch delta 70 zcmeBJz_eol(}owK;%p3T4BpHP44e!M3?)U0dByRGIXU^so1csRX5(jtODO}TRW{$1 S*~!kxGkLm-=H@*rKG^`BvJ%<= delta 29 lcmdn7fT?2v(}owKo5X&xZRV5R!p_J&`LK%S<_{_!*#Nk#3%mdT diff --git a/piet-gpu/shader/path_coarse.comp b/piet-gpu/shader/path_coarse.comp index 4f77ff9..70251bf 100644 --- a/piet-gpu/shader/path_coarse.comp +++ b/piet-gpu/shader/path_coarse.comp @@ -102,6 +102,17 @@ void main() { case PathSeg_FillCubic: case PathSeg_StrokeCubic: PathStrokeCubic cubic = PathSeg_StrokeCubic_read(conf.pathseg_alloc, ref); + + uint trans_ix = cubic.trans_ix; + if (trans_ix > 0) { + TransformSegRef trans_ref = TransformSegRef(conf.trans_alloc.offset + (trans_ix - 1) * TransformSeg_size); + TransformSeg trans = TransformSeg_read(conf.trans_alloc, trans_ref); + cubic.p0 = trans.mat.xy * cubic.p0.x + trans.mat.zw * cubic.p0.y + trans.translate; + cubic.p1 = trans.mat.xy * cubic.p1.x + trans.mat.zw * cubic.p1.y + trans.translate; + cubic.p2 = trans.mat.xy * cubic.p2.x + trans.mat.zw * cubic.p2.y + trans.translate; + cubic.p3 = trans.mat.xy * cubic.p3.x + trans.mat.zw * cubic.p3.y + trans.translate; + } + vec2 err_v = 3.0 * (cubic.p2 - cubic.p1) + cubic.p0 - cubic.p3; float err = err_v.x * err_v.x + err_v.y * err_v.y; // The number of quadratics. diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 0236541dcf987cefaa8d606494b19408b656940e..d84bd6da987bb7d0648cc84aa2f0874a08bbdab1 100644 GIT binary patch literal 42252 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 S9`!@8ZS>RTyOx^oTK@-tG(GtM diff --git a/piet-gpu/shader/pathseg.h b/piet-gpu/shader/pathseg.h index a170090..7c69b9d 100644 --- a/piet-gpu/shader/pathseg.h +++ b/piet-gpu/shader/pathseg.h @@ -20,9 +20,10 @@ struct PathFillCubic { vec2 p2; vec2 p3; uint path_ix; + uint trans_ix; }; -#define PathFillCubic_size 36 +#define PathFillCubic_size 40 PathFillCubicRef PathFillCubic_index(PathFillCubicRef ref, uint index) { return PathFillCubicRef(ref.offset + index * PathFillCubic_size); @@ -34,10 +35,11 @@ struct PathStrokeCubic { vec2 p2; vec2 p3; uint path_ix; + uint trans_ix; vec2 stroke; }; -#define PathStrokeCubic_size 44 +#define PathStrokeCubic_size 48 PathStrokeCubicRef PathStrokeCubic_index(PathStrokeCubicRef ref, uint index) { return PathStrokeCubicRef(ref.offset + index * PathStrokeCubic_size); @@ -46,7 +48,7 @@ PathStrokeCubicRef PathStrokeCubic_index(PathStrokeCubicRef ref, uint index) { #define PathSeg_Nop 0 #define PathSeg_FillCubic 1 #define PathSeg_StrokeCubic 2 -#define PathSeg_size 48 +#define PathSeg_size 52 PathSegRef PathSeg_index(PathSegRef ref, uint index) { return PathSegRef(ref.offset + index * PathSeg_size); @@ -63,12 +65,14 @@ PathFillCubic PathFillCubic_read(Alloc a, PathFillCubicRef ref) { 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); PathFillCubic s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7)); s.path_ix = raw8; + s.trans_ix = raw9; return s; } @@ -83,6 +87,7 @@ void PathFillCubic_write(Alloc a, PathFillCubicRef ref, PathFillCubic s) { write_mem(a, ix + 6, floatBitsToUint(s.p3.x)); write_mem(a, ix + 7, floatBitsToUint(s.p3.y)); write_mem(a, ix + 8, s.path_ix); + write_mem(a, ix + 9, s.trans_ix); } PathStrokeCubic PathStrokeCubic_read(Alloc a, PathStrokeCubicRef ref) { @@ -98,13 +103,15 @@ PathStrokeCubic PathStrokeCubic_read(Alloc a, PathStrokeCubicRef ref) { uint raw8 = read_mem(a, ix + 8); uint raw9 = read_mem(a, ix + 9); uint raw10 = read_mem(a, ix + 10); + uint raw11 = read_mem(a, ix + 11); PathStrokeCubic s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7)); s.path_ix = raw8; - s.stroke = vec2(uintBitsToFloat(raw9), uintBitsToFloat(raw10)); + s.trans_ix = raw9; + s.stroke = vec2(uintBitsToFloat(raw10), uintBitsToFloat(raw11)); return s; } @@ -119,8 +126,9 @@ void PathStrokeCubic_write(Alloc a, PathStrokeCubicRef ref, PathStrokeCubic s) { write_mem(a, ix + 6, floatBitsToUint(s.p3.x)); write_mem(a, ix + 7, floatBitsToUint(s.p3.y)); write_mem(a, ix + 8, s.path_ix); - write_mem(a, ix + 9, floatBitsToUint(s.stroke.x)); - write_mem(a, ix + 10, floatBitsToUint(s.stroke.y)); + write_mem(a, ix + 9, s.trans_ix); + write_mem(a, ix + 10, floatBitsToUint(s.stroke.x)); + write_mem(a, ix + 11, floatBitsToUint(s.stroke.y)); } uint PathSeg_tag(Alloc a, PathSegRef ref) { diff --git a/piet-gpu/shader/setup.h b/piet-gpu/shader/setup.h index 5a4935c..f2ca87c 100644 --- a/piet-gpu/shader/setup.h +++ b/piet-gpu/shader/setup.h @@ -35,4 +35,5 @@ struct Config { Alloc ptcl_alloc; Alloc pathseg_alloc; Alloc anno_alloc; + Alloc trans_alloc; }; diff --git a/piet-gpu/shader/state.h b/piet-gpu/shader/state.h index 8479dcf..d2df804 100644 --- a/piet-gpu/shader/state.h +++ b/piet-gpu/shader/state.h @@ -14,9 +14,10 @@ struct State { uint flags; uint path_count; uint pathseg_count; + uint trans_count; }; -#define State_size 56 +#define State_size 60 StateRef State_index(StateRef ref, uint index) { return StateRef(ref.offset + index * State_size); @@ -38,6 +39,7 @@ State State_read(StateRef ref) { uint raw11 = state[ix + 11]; uint raw12 = state[ix + 12]; uint raw13 = state[ix + 13]; + uint raw14 = state[ix + 14]; State s; s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); @@ -46,6 +48,7 @@ State State_read(StateRef ref) { s.flags = raw11; s.path_count = raw12; s.pathseg_count = raw13; + s.trans_count = raw14; return s; } @@ -65,5 +68,6 @@ void State_write(StateRef ref, State s) { state[ix + 11] = s.flags; state[ix + 12] = s.path_count; state[ix + 13] = s.pathseg_count; + state[ix + 14] = s.trans_count; } diff --git a/piet-gpu/shader/tile.h b/piet-gpu/shader/tile.h index 500277b..e11329c 100644 --- a/piet-gpu/shader/tile.h +++ b/piet-gpu/shader/tile.h @@ -14,6 +14,10 @@ struct TileSegRef { uint offset; }; +struct TransformSegRef { + uint offset; +}; + struct Path { uvec4 bbox; TileRef tiles; @@ -49,6 +53,17 @@ TileSegRef TileSeg_index(TileSegRef ref, uint index) { return TileSegRef(ref.offset + index * TileSeg_size); } +struct TransformSeg { + vec4 mat; + vec2 translate; +}; + +#define TransformSeg_size 24 + +TransformSegRef TransformSeg_index(TransformSegRef ref, uint index) { + return TransformSegRef(ref.offset + index * TransformSeg_size); +} + Path Path_read(Alloc a, PathRef ref) { uint ix = ref.offset >> 2; uint raw0 = read_mem(a, ix + 0); @@ -109,3 +124,27 @@ void TileSeg_write(Alloc a, TileSegRef ref, TileSeg s) { write_mem(a, ix + 5, s.next.offset); } +TransformSeg TransformSeg_read(Alloc a, TransformSegRef 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); + TransformSeg s; + s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + return s; +} + +void TransformSeg_write(Alloc a, TransformSegRef ref, TransformSeg s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.mat.x)); + write_mem(a, ix + 1, floatBitsToUint(s.mat.y)); + write_mem(a, ix + 2, floatBitsToUint(s.mat.z)); + write_mem(a, ix + 3, floatBitsToUint(s.mat.w)); + write_mem(a, ix + 4, floatBitsToUint(s.translate.x)); + write_mem(a, ix + 5, floatBitsToUint(s.translate.y)); +} + diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv index b2563928f58bd73eb5a8497a9b716df985184391..f97a7d578af86359d0223cb91a368f2bf0ddfef8 100644 GIT binary patch delta 68 zcmX?8^rvV82bVY-0~>=eBLf2`0|P@zQDR7y}Z0E Qj69Q13u Result { let host = MemFlags::host_coherent(); let dev = MemFlags::device_local(); let n_elements = scene.len() / piet_gpu_types::scene::Element::fixed_size(); println!( - "scene: {} elements, {} paths, {} path_segments", - n_elements, n_paths, n_pathseg + "scene: {} elements, {} paths, {} path_segments, {} transforms", + n_elements, n_paths, n_pathseg, n_trans ); let mut scene_buf_host = session @@ -222,15 +223,16 @@ impl Renderer { let state_buf = session.create_buffer(1 * 1024 * 1024, dev)?; let image_dev = session.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; - const CONFIG_SIZE: u64 = 9*4; // Size of Config in setup.h. + const CONFIG_SIZE: u64 = 10*4; // Size of Config in setup.h. let mut config_buf_host = session.create_buffer(CONFIG_SIZE, host)?; let config_buf_dev = session.create_buffer(CONFIG_SIZE, dev)?; // TODO: constants const PATH_SIZE: usize = 12; const BIN_SIZE: usize = 8; - const PATHSEG_SIZE: usize = 48; + const PATHSEG_SIZE: usize = 52; const ANNO_SIZE: usize = 28; + const TRANS_SIZE: usize = 24; let mut alloc = 0; let tile_base = alloc; alloc += ((n_paths + 3) & !3) * PATH_SIZE; @@ -242,7 +244,9 @@ impl Renderer { alloc += (n_pathseg * PATHSEG_SIZE + 3) & !3; let anno_base = alloc; alloc += (n_paths * ANNO_SIZE + 3) & !3; - config_buf_host.write(&[n_paths as u32, n_pathseg as u32, WIDTH_IN_TILES as u32, HEIGHT_IN_TILES as u32, tile_base as u32, bin_base as u32, ptcl_base as u32, pathseg_base as u32, anno_base as u32])?; + let trans_base = alloc; + alloc += (n_trans * TRANS_SIZE + 3) & !3; + config_buf_host.write(&[n_paths as u32, n_pathseg as u32, WIDTH_IN_TILES as u32, HEIGHT_IN_TILES as u32, tile_base as u32, bin_base as u32, ptcl_base as u32, pathseg_base as u32, anno_base as u32, trans_base as u32])?; let mut memory_buf_host = session.create_buffer(2*4, host)?; let memory_buf_dev = session.create_buffer(128 * 1024 * 1024, dev)?; diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 419c9ec..d05b712 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -38,6 +38,8 @@ pub struct PietGpuRenderContext { path_count: usize, /// The count of path segment elements. pathseg_count: usize, + /// The count of transform elements. + trans_count: usize, cur_transform: Affine, state_stack: Vec, @@ -82,6 +84,7 @@ impl PietGpuRenderContext { stroke_width, path_count: 0, pathseg_count: 0, + trans_count: 0, cur_transform: Affine::default(), state_stack: Vec::new(), clip_stack: Vec::new(), @@ -100,6 +103,10 @@ impl PietGpuRenderContext { pub fn pathseg_count(&self) -> usize { self.pathseg_count } + + pub fn trans_count(&self) -> usize { + self.trans_count + } } impl RenderContext for PietGpuRenderContext { @@ -207,6 +214,7 @@ impl RenderContext for PietGpuRenderContext { let a_inv = state.rel_transform.inverse(); self.elements .push(Element::Transform(to_scene_transform(a_inv))); + self.trans_count += 1; } self.cur_transform = state.transform; for _ in 0..state.n_clip { @@ -228,6 +236,7 @@ impl RenderContext for PietGpuRenderContext { fn transform(&mut self, transform: Affine) { self.elements .push(Element::Transform(to_scene_transform(transform))); + self.trans_count += 1; if let Some(tos) = self.state_stack.last_mut() { tos.rel_transform *= transform; }