From 4de67d9081db59883838d5060c5d966462838775 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Fri, 11 Dec 2020 18:30:20 +0100 Subject: [PATCH] unify GPU memory management Merge all static and dynamic buffers to just one, "memory". Add a malloc function for dynamic allocations. Unify static allocation offsets into a "config" buffer containing scene setup (number of paths, number of path segments), as well as the memory offsets of the static allocations. Finally, set an overflow flag when an allocation fail, and make sure to exit shader execution as soon as that triggers. Add checks before beginning execution in case the client wants to run two or more shaders before checking the flag. The "state" buffer is left alone because it needs zero'ing and because it is accessed with the "volatile" keyword. Fixes #40 Signed-off-by: Elias Naur --- piet-gpu-derive/src/glsl.rs | 13 ++- piet-gpu/shader/annotated.h | 72 +++++++-------- piet-gpu/shader/backdrop.comp | 34 +++---- piet-gpu/shader/backdrop.spv | Bin 7248 -> 7632 bytes piet-gpu/shader/binning.comp | 45 +++++---- piet-gpu/shader/binning.spv | Bin 9856 -> 11600 bytes piet-gpu/shader/bins.h | 4 +- piet-gpu/shader/coarse.comp | 79 ++++++++-------- piet-gpu/shader/coarse.spv | Bin 37764 -> 39376 bytes piet-gpu/shader/elements.comp | 45 ++++----- piet-gpu/shader/elements.spv | Bin 58352 -> 59316 bytes piet-gpu/shader/kernel4.comp | 58 ++++++------ piet-gpu/shader/kernel4.spv | Bin 32608 -> 34160 bytes piet-gpu/shader/mem.h | 29 ++++++ piet-gpu/shader/path_coarse.comp | 36 ++++---- piet-gpu/shader/path_coarse.spv | Bin 30852 -> 32248 bytes piet-gpu/shader/pathseg.h | 140 ++++++++++++++-------------- piet-gpu/shader/ptcl.h | 108 +++++++++++----------- piet-gpu/shader/setup.h | 10 ++ piet-gpu/shader/tile.h | 44 ++++----- piet-gpu/shader/tile_alloc.comp | 42 ++++----- piet-gpu/shader/tile_alloc.spv | Bin 8916 -> 10308 bytes piet-gpu/src/lib.rs | 152 +++++++++++++------------------ 23 files changed, 463 insertions(+), 448 deletions(-) create mode 100644 piet-gpu/shader/mem.h diff --git a/piet-gpu-derive/src/glsl.rs b/piet-gpu-derive/src/glsl.rs index b55dda49..24096378 100644 --- a/piet-gpu-derive/src/glsl.rs +++ b/piet-gpu-derive/src/glsl.rs @@ -31,17 +31,22 @@ pub fn gen_glsl(module: &LayoutModule) -> String { for name in &module.def_names { let def = module.defs.get(name).unwrap(); + let mem = &"memory".to_owned(); + let mut buf_name = &module.name; + if !module.name.eq(&"state") && !module.name.eq(&"scene") { + buf_name = mem; + } match def { (_size, LayoutTypeDef::Struct(fields)) => { - gen_struct_read(&mut r, &module.name, &name, fields); + gen_struct_read(&mut r, buf_name, &name, fields); if module.gpu_write { - gen_struct_write(&mut r, &module.name, &name, fields); + gen_struct_write(&mut r, buf_name, &name, fields); } } (_size, LayoutTypeDef::Enum(en)) => { - gen_enum_read(&mut r, &module.name, &name, en); + gen_enum_read(&mut r, buf_name, &name, en); if module.gpu_write { - gen_enum_write(&mut r, &module.name, &name, en); + gen_enum_write(&mut r, buf_name, &name, en); } } } diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index 1e1ebe6a..8a757efa 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -64,11 +64,11 @@ AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { AnnoFill AnnoFill_read(AnnoFillRef ref) { uint ix = ref.offset >> 2; - uint raw0 = annotated[ix + 0]; - uint raw1 = annotated[ix + 1]; - uint raw2 = annotated[ix + 2]; - uint raw3 = annotated[ix + 3]; - uint raw4 = annotated[ix + 4]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; + uint raw4 = memory[ix + 4]; AnnoFill s; s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.rgba_color = raw4; @@ -77,21 +77,21 @@ AnnoFill AnnoFill_read(AnnoFillRef ref) { void AnnoFill_write(AnnoFillRef ref, AnnoFill s) { uint ix = ref.offset >> 2; - annotated[ix + 0] = floatBitsToUint(s.bbox.x); - annotated[ix + 1] = floatBitsToUint(s.bbox.y); - annotated[ix + 2] = floatBitsToUint(s.bbox.z); - annotated[ix + 3] = floatBitsToUint(s.bbox.w); - annotated[ix + 4] = s.rgba_color; + memory[ix + 0] = floatBitsToUint(s.bbox.x); + memory[ix + 1] = floatBitsToUint(s.bbox.y); + memory[ix + 2] = floatBitsToUint(s.bbox.z); + memory[ix + 3] = floatBitsToUint(s.bbox.w); + memory[ix + 4] = s.rgba_color; } AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { uint ix = ref.offset >> 2; - uint raw0 = annotated[ix + 0]; - uint raw1 = annotated[ix + 1]; - uint raw2 = annotated[ix + 2]; - uint raw3 = annotated[ix + 3]; - uint raw4 = annotated[ix + 4]; - uint raw5 = annotated[ix + 5]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; + uint raw4 = memory[ix + 4]; + uint raw5 = memory[ix + 5]; AnnoStroke s; s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.rgba_color = raw4; @@ -101,20 +101,20 @@ AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { void AnnoStroke_write(AnnoStrokeRef ref, AnnoStroke s) { uint ix = ref.offset >> 2; - annotated[ix + 0] = floatBitsToUint(s.bbox.x); - annotated[ix + 1] = floatBitsToUint(s.bbox.y); - annotated[ix + 2] = floatBitsToUint(s.bbox.z); - annotated[ix + 3] = floatBitsToUint(s.bbox.w); - annotated[ix + 4] = s.rgba_color; - annotated[ix + 5] = floatBitsToUint(s.linewidth); + memory[ix + 0] = floatBitsToUint(s.bbox.x); + memory[ix + 1] = floatBitsToUint(s.bbox.y); + memory[ix + 2] = floatBitsToUint(s.bbox.z); + memory[ix + 3] = floatBitsToUint(s.bbox.w); + memory[ix + 4] = s.rgba_color; + memory[ix + 5] = floatBitsToUint(s.linewidth); } AnnoClip AnnoClip_read(AnnoClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = annotated[ix + 0]; - uint raw1 = annotated[ix + 1]; - uint raw2 = annotated[ix + 2]; - uint raw3 = annotated[ix + 3]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; AnnoClip s; s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); return s; @@ -122,14 +122,14 @@ AnnoClip AnnoClip_read(AnnoClipRef ref) { void AnnoClip_write(AnnoClipRef ref, AnnoClip s) { uint ix = ref.offset >> 2; - annotated[ix + 0] = floatBitsToUint(s.bbox.x); - annotated[ix + 1] = floatBitsToUint(s.bbox.y); - annotated[ix + 2] = floatBitsToUint(s.bbox.z); - annotated[ix + 3] = floatBitsToUint(s.bbox.w); + memory[ix + 0] = floatBitsToUint(s.bbox.x); + memory[ix + 1] = floatBitsToUint(s.bbox.y); + memory[ix + 2] = floatBitsToUint(s.bbox.z); + memory[ix + 3] = floatBitsToUint(s.bbox.w); } uint Annotated_tag(AnnotatedRef ref) { - return annotated[ref.offset >> 2]; + return memory[ref.offset >> 2]; } AnnoStroke Annotated_Stroke_read(AnnotatedRef ref) { @@ -149,26 +149,26 @@ AnnoClip Annotated_EndClip_read(AnnotatedRef ref) { } void Annotated_Nop_write(AnnotatedRef ref) { - annotated[ref.offset >> 2] = Annotated_Nop; + memory[ref.offset >> 2] = Annotated_Nop; } void Annotated_Stroke_write(AnnotatedRef ref, AnnoStroke s) { - annotated[ref.offset >> 2] = Annotated_Stroke; + memory[ref.offset >> 2] = Annotated_Stroke; AnnoStroke_write(AnnoStrokeRef(ref.offset + 4), s); } void Annotated_Fill_write(AnnotatedRef ref, AnnoFill s) { - annotated[ref.offset >> 2] = Annotated_Fill; + memory[ref.offset >> 2] = Annotated_Fill; AnnoFill_write(AnnoFillRef(ref.offset + 4), s); } void Annotated_BeginClip_write(AnnotatedRef ref, AnnoClip s) { - annotated[ref.offset >> 2] = Annotated_BeginClip; + memory[ref.offset >> 2] = Annotated_BeginClip; AnnoClip_write(AnnoClipRef(ref.offset + 4), s); } void Annotated_EndClip_write(AnnotatedRef ref, AnnoClip s) { - annotated[ref.offset >> 2] = Annotated_EndClip; + memory[ref.offset >> 2] = Annotated_EndClip; AnnoClip_write(AnnoClipRef(ref.offset + 4), s); } diff --git a/piet-gpu/shader/backdrop.comp b/piet-gpu/shader/backdrop.comp index 42eec9c9..f57d6e08 100644 --- a/piet-gpu/shader/backdrop.comp +++ b/piet-gpu/shader/backdrop.comp @@ -16,27 +16,15 @@ #extension GL_GOOGLE_include_directive : enable #include "setup.h" +#include "mem.h" #define LG_BACKDROP_WG (7 + LG_WG_FACTOR) #define BACKDROP_WG (1 << LG_BACKDROP_WG) layout(local_size_x = BACKDROP_WG, local_size_y = 1) in; -layout(set = 0, binding = 0) buffer AnnotatedBuf { - uint[] annotated; -}; - -// This is really only used for n_elements; maybe we can handle that -// a different way, but it's convenient to have the same signature as -// tile allocation. -layout(set = 0, binding = 1) readonly buffer AllocBuf { - uint n_elements; // paths - uint n_pathseg; - uint alloc; -}; - -layout(set = 0, binding = 2) buffer TileBuf { - uint[] tile; +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; }; #include "annotated.h" @@ -47,18 +35,22 @@ shared uint sh_row_base[BACKDROP_WG]; shared uint sh_row_width[BACKDROP_WG]; void main() { + if (mem_overflow) { + return; + } + uint th_ix = gl_LocalInvocationID.x; uint element_ix = gl_GlobalInvocationID.x; - AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); + AnnotatedRef ref = AnnotatedRef(conf.anno_base + element_ix * Annotated_size); // Work assignment: 1 thread : 1 path element uint row_count = 0; - if (element_ix < n_elements) { + if (element_ix < conf.n_elements) { uint tag = Annotated_tag(ref); switch (tag) { case Annotated_Fill: case Annotated_BeginClip: - PathRef path_ref = PathRef(element_ix * Path_size); + PathRef path_ref = PathRef(conf.tile_base + element_ix * Path_size); Path path = Path_read(path_ref); sh_row_width[th_ix] = path.bbox.z - path.bbox.x; row_count = path.bbox.w - path.bbox.y; @@ -98,11 +90,11 @@ void main() { // Process one row sequentially // Read backdrop value per tile and prefix sum it uint tile_el_ix = sh_row_base[el_ix] + seq_ix * 2 * width; - uint sum = tile[tile_el_ix]; + uint sum = memory[tile_el_ix]; for (uint x = 1; x < width; x++) { tile_el_ix += 2; - sum += tile[tile_el_ix]; - tile[tile_el_ix] = sum; + sum += memory[tile_el_ix]; + memory[tile_el_ix] = sum; } } } diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv index 54bf7368785403a703313344a287918e052694c9..defe30e7d26b76850948dab2ceca8b9f7bbeb11e 100644 GIT binary patch literal 7632 zcmZ{o36xw_5r$tfOZGh>WE*A@4I!H(LI{Qc3E&VJNREJdaA|vHdctT=_b}6w0HOwr z3X0;2xNBSwV8jJ+Ujm9-P~0_9qek3!4G@I*eXn07@3e=4y7gX9Xd1m} zOp=UB#wTNw6{C{$nU;)(Ns&^8LkbD^wd`56Ra3nwQ8O5O;nvEL!@-yN#HHT+U0Ixer=y)`J3rQl3dbX zE=7#CPd>&znMd9{Y2U!z-PJ4eoIR|OYZ%=^B{R{kD-4y2y-{~oP*yT=P00-6qUPD) z3rhplp{v}hHPQANC=C>%COataeks4LG}KqFZa2>Q^m&i?(md`lM<d8p$z@8 z^|dWY5l||Z21=D$y2v%oBWpU#)$adWWUY+Y(cU;H=Ts|w{afq3Ne2h6QpmDWm_3uX zH_uNd-MBlW!b}ZbWJP1BJ&-N z7NV`8Ui-!o*LzQLK0TaC?RonJJgyl+4^_7pda5H8UWe4rSR}j!;O69N@L+MMIFR~U zk{zj?&RdqRIk`ULQ0bZ*QajePCO2UZGofs=y}!3UV>K zUAGrqqUq`I94%bdZg0prxzh%KW zuX28qg0mlT$ERHL(Rr-f$L7-*AJ6X0Hl?^Zy|?_?y+_Y(?e=0hz_Z(<8B}lm9jtb} zcos3%(2n@D;TeOjbN;d-<*h@A{JPHr}_Q!<~uFxFle+lJ5Yw|+VE zPDjg`V*#--D{a}FxK5F8C*J;8zjLrIvpr*+X)d@O;rPii{Mz2X{;<7&AM-6h=g-}N zZLE75uU?M$gzX18^6OuVxW;^Epv~u*BK|D!kt4_U--zw`jo*Ye-kwB#7l6&L|01-0 z<7cwVrD%KnY_xlOpT<20?HYOeqV4=gp66xQHR12hxHfLL53KL;#CzO|-#GWX9POFq z%%|-P%EdWe05)Irp#rw|VebU{?u30K*n;Pi?=?Wz%DLZb(aw{+`)T|Al+%B6hV%Q~ z0%w0BepeO~w%=6O$NqPMo$s*k(xx75=jc9!YdmT{0N#Ntr&9awJ7>Mlhq8tlwjb`{ zV=WKC_&u-&XYVq^`whF8v#rJNao#`FVEdaCe&ess*^|LLa<;R1ea_Z@L(aCI-3|8M z2HW40*w6TV4fg&9`(T58sKGwmVEa3h&u{0 zLF8{E#`otL8Mhq$Ah+@?w9jGU^*s}j^RC2?GL&LD_wijSAiiJbh<%<9mOqo6-m7-R z_kg>Re0(pqyzxb}_692U94|zCpL}0@k7AE9SYOx!VAt-XF3(y;nkJCW%5E5NSPXU`|0Ux~ur@mJqa{7EHwLQ1LBd4=S7rEaGj@-9^qlUME^+nv)Vs{?}K1D zXDa67LtuI9i=KWMybEz3>(MsOdYoTtRcpt)qmF%V16yC%w}b6TB2hT&@)#fOkHXyO7BB zX>k4=pTRcwJ@j)f`m=~Vm%oM_t?17o^1HM9|2)_{^4|X!(LUCq?+b{WwTNTyFM;!W ze;GT!_gAppTR!&Q1D20jy&G&E`RM&UV0*7G_WCMVKJ2f7y|0*&d%<$X#eB+n2T{BF z@%s36eEG9{13Nzt-^8{i!L*92|ebJNe z;B)``kl5q9V0rWHMQexedtlF_?|!tldwm~mt!n4>hv*+6KKg%*)~}9SKLMK~?4N?& zFXs1WU}NN+8SNh+<~EnuI`*M`Z^ZjE?4Cad8>hCWU!o5nKKg%!)~}8lehoH9)bJay zT)actzd+38T5+uXE!bMZ{vFs{cW3X+@4*is@-er60L#aH{t;{*dCzkYEf+ce1kU&D z&)CMq-2VkEw;G8%{wvr!))#sH29}R^_3vPF%loeW6YXQ)_5A~p^UUJN^DnUb$31A< z+lWB z()S25YE->0apZjzY@X=pV_><}Nc8jw*q-W(b^g0a-d-F=Yg@bfdUp2;pEc!u#zdac z#JN89Z^HJh@~%GwmW%inu=Pf5t=KnV%SX%@u)OQjzwdLtap3&=@z}dqFCXhCfaSxU z2)2&+n|Bhn+*-JZnT+jT5i^-Rc9vqLJgZSv5oB7p|cOKXrVb2G9ruaTA02?FkTxrin z%-9@xaf~u#6J=2 zK4C8gyFPk;A~^On)*N!i**AOWS=Eta3D~|x4JU!+;@#FKm%kT#rtR;B`P7kbDL&uz z$hQnz?#8Tl`s7?6zeWB|`0vKGi2om;6?+2WGY&b;c|(s!+;1Yj9x7SN3Yt!v33zy-(&0}j+&-{XCgk+5o?-^o`G1?EOh*TiN)YCIeTe? zeR6}nCTE`kUZ2@<#uKsKCx6Bz*z$44lfZJG(R;TBxj5G{uw2~B3b35tx9G`AaP&lf ztbGz#pLsotcKB9-^Ld|)Em!{r*S`g)U`Jm4vG!E3KJ(fW?eLuj&gVTHTh4bd>U|10 z^6HPZ>%jWVYj3r~cP7{~+?&nD*%(I0FnSwqaIU8{ga~R{jZ$du}aTdIH&!TO7)X)X?eEyqxD*8f1&h;0eHzTf( TcUjvQ*E`Q!Qe2;Pv9ta^#!n5> literal 7248 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 2) + (my_partition * N_TILE + gl_LocalInvocationID.x) * 2; + memory[out_ix] = element_count; + memory[out_ix + 1] = chunk_alloc.offset; barrier(); + if (sh_alloc_failed) { + return; + } + // Use similar strategy as Laine & Karras paper; loop over bbox of bins // touched by this element x = x0; diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index abe17d498eace42dc8b0730765bc9b88f9cd4345..da2df7626715da68de5a935589a93e42e6fd548b 100644 GIT binary patch literal 11600 zcmZ{p2bf%C6^3uJvzq`hq|!;U)IbOjN+=N|v?Uk}q6mUIPIhOLA+s~f?raKzM8w`v zQL)7W7(fBBB4`X&bQB(ibc}aI`gW;9-X!Y{2BKD~~f~`)oqijV>WWm0Cl` z^~h#q3ghLcA|^6k>Egff=*<`&+4COv+}$#rX0_wK6bwv^6r;-ocWWC_yYmFt1(N?9=U(LI@4NVkfcr`pNlJ>(!xZqgAf4vfbJAY%W|9 zTV69#YgJ=ziGzaq?26S*Jncz4=Y7c4o^w~WFL)G@_ob-hxRf1+KCL>`99h@iS8bo6 z>QFi6(8D_;?|$d}>gvc~y}8CX=d92717DuU8Vhyuh|ff~=U2+k0gqJHSK!CwIYsinm(Ou2dmKE-8;;8JJ%LX1&1O@*)Ew6f<~0EG&VpH|(R!`FxI;_X z+%`8<8C^w0PqqX*=F#6AYqW|yr^3gYoeSRT>(2VMSB^DSwU>oFrEC@4)}DH@1~%)~ z4�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 literal 9856 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 a^|W-HOX2GAzAgiA$YadmSZc<_z5YMV*!{i$ diff --git a/piet-gpu/shader/bins.h b/piet-gpu/shader/bins.h index bc32dda0..43642785 100644 --- a/piet-gpu/shader/bins.h +++ b/piet-gpu/shader/bins.h @@ -18,7 +18,7 @@ BinInstanceRef BinInstance_index(BinInstanceRef ref, uint index) { BinInstance BinInstance_read(BinInstanceRef ref) { uint ix = ref.offset >> 2; - uint raw0 = bins[ix + 0]; + uint raw0 = memory[ix + 0]; BinInstance s; s.element_ix = raw0; return s; @@ -26,6 +26,6 @@ BinInstance BinInstance_read(BinInstanceRef ref) { void BinInstance_write(BinInstanceRef ref, BinInstance s) { uint ix = ref.offset >> 2; - bins[ix + 0] = s.element_ix; + memory[ix + 0] = s.element_ix; } diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index a173608b..a70318ad 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -14,28 +14,12 @@ #extension GL_GOOGLE_include_directive : enable #include "setup.h" +#include "mem.h" layout(local_size_x = N_TILE, local_size_y = 1) in; -layout(set = 0, binding = 0) buffer AnnotatedBuf { - uint[] annotated; -}; - -layout(set = 0, binding = 1) buffer BinsBuf { - uint[] bins; -}; - -layout(set = 0, binding = 2) buffer TileBuf { - uint[] tile; -}; - -layout(set = 0, binding = 3) buffer AllocBuf { - uint n_elements; - uint alloc; -}; - -layout(set = 0, binding = 4) buffer PtclBuf { - uint[] ptcl; +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; }; #include "annotated.h" @@ -65,22 +49,31 @@ shared uint sh_tile_base[N_TILE]; shared uint sh_tile_stride[N_TILE]; // Perhaps cmd_limit should be a global? This is a style question. -void alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { - if (cmd_ref.offset > cmd_limit) { - uint new_cmd = atomicAdd(alloc, PTCL_INITIAL_ALLOC); - CmdJump jump = CmdJump(new_cmd); - Cmd_Jump_write(cmd_ref, jump); - cmd_ref = CmdRef(new_cmd); - cmd_limit = new_cmd + PTCL_INITIAL_ALLOC - 2 * Cmd_size; +bool alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { + if (cmd_ref.offset < cmd_limit) { + return true; } + Alloc new_cmd = malloc(PTCL_INITIAL_ALLOC); + if (new_cmd.failed) { + return false; + } + CmdJump jump = CmdJump(new_cmd.offset); + Cmd_Jump_write(cmd_ref, jump); + cmd_ref = CmdRef(new_cmd.offset); + cmd_limit = new_cmd.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; + return true; } void main() { + if (mem_overflow) { + return; + } + // Could use either linear or 2d layouts for both dispatch and // invocations within the workgroup. We'll use variables to abstract. uint bin_ix = N_TILE_X * gl_WorkGroupID.y + gl_WorkGroupID.x; uint partition_ix = 0; - uint n_partitions = (n_elements + N_TILE - 1) / N_TILE; + uint n_partitions = (conf.n_elements + N_TILE - 1) / N_TILE; uint th_ix = gl_LocalInvocationID.x; // Coordinates of top left of bin, in tiles. @@ -91,7 +84,7 @@ void main() { uint tile_x = gl_LocalInvocationID.x % N_TILE_X; uint tile_y = gl_LocalInvocationID.x / N_TILE_X; uint this_tile_ix = (bin_tile_y + tile_y) * WIDTH_IN_TILES + bin_tile_x + tile_x; - CmdRef cmd_ref = CmdRef(this_tile_ix * PTCL_INITIAL_ALLOC); + CmdRef cmd_ref = CmdRef(conf.ptcl_base + this_tile_ix * PTCL_INITIAL_ALLOC); uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; // The nesting depth of the clip stack uint clip_depth = 0; @@ -123,9 +116,9 @@ void main() { part_start_ix = ready_ix; uint count = 0; if (th_ix < N_PART_READ && partition_ix + th_ix < n_partitions) { - uint in_ix = ((partition_ix + th_ix) * N_TILE + bin_ix) * 2; - count = bins[in_ix]; - sh_part_elements[th_ix] = bins[in_ix + 1]; + uint in_ix = (conf.bin_base >> 2) + ((partition_ix + th_ix) * N_TILE + bin_ix) * 2; + count = memory[in_ix]; + sh_part_elements[th_ix] = memory[in_ix + 1]; } // prefix sum of counts for (uint i = 0; i < LG_N_PART_READ; i++) { @@ -175,7 +168,7 @@ void main() { AnnotatedRef ref; if (th_ix + rd_ix < wr_ix) { element_ix = sh_elements[th_ix]; - ref = AnnotatedRef(element_ix * Annotated_size); + ref = AnnotatedRef(conf.anno_base + element_ix * Annotated_size); tag = Annotated_tag(ref); } @@ -189,7 +182,7 @@ void main() { // We have one "path" for each element, even if the element isn't // actually a path (currently EndClip, but images etc in the future). uint path_ix = element_ix; - Path path = Path_read(PathRef(path_ix * Path_size)); + Path path = Path_read(PathRef(conf.tile_base + path_ix * Path_size)); uint stride = path.bbox.z - path.bbox.x; sh_tile_stride[th_ix] = stride; int dx = int(path.bbox.x) - int(bin_tile_x); @@ -232,7 +225,7 @@ void main() { el_ix = probe; } } - AnnotatedRef ref = AnnotatedRef(sh_elements[el_ix] * Annotated_size); + AnnotatedRef ref = AnnotatedRef(conf.anno_base + sh_elements[el_ix] * Annotated_size); uint tag = Annotated_tag(ref); uint seq_ix = ix - (el_ix > 0 ? sh_tile_count[el_ix - 1] : 0); uint width = sh_tile_width[el_ix]; @@ -281,7 +274,7 @@ void main() { // At this point, we read the element again from global memory. // If that turns out to be expensive, maybe we can pack it into // shared memory (or perhaps just the tag). - ref = AnnotatedRef(element_ix * Annotated_size); + ref = AnnotatedRef(conf.anno_base + element_ix * Annotated_size); tag = Annotated_tag(ref); if (clip_zero_depth == 0) { @@ -290,7 +283,9 @@ void main() { Tile tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); AnnoFill fill = Annotated_Fill_read(ref); - alloc_cmd(cmd_ref, cmd_limit); + if (!alloc_cmd(cmd_ref, cmd_limit)) { + break; + } if (tile.tile.offset != 0) { CmdFill cmd_fill; cmd_fill.tile_ref = tile.tile.offset; @@ -310,7 +305,9 @@ void main() { } else if (tile.tile.offset == 0 && clip_depth < 32) { clip_one_mask |= (1 << clip_depth); } else { - alloc_cmd(cmd_ref, cmd_limit); + if (!alloc_cmd(cmd_ref, cmd_limit)) { + break; + } if (tile.tile.offset != 0) { CmdBeginClip cmd_begin_clip; cmd_begin_clip.tile_ref = tile.tile.offset; @@ -331,7 +328,9 @@ void main() { case Annotated_EndClip: clip_depth--; if (clip_depth >= 32 || (clip_one_mask & (1 << clip_depth)) == 0) { - alloc_cmd(cmd_ref, cmd_limit); + if (!alloc_cmd(cmd_ref, cmd_limit)) { + break; + } Cmd_EndClip_write(cmd_ref, CmdEndClip(1.0)); cmd_ref.offset += Cmd_size; } @@ -344,7 +343,9 @@ void main() { cmd_stroke.tile_ref = tile.tile.offset; cmd_stroke.half_width = 0.5 * stroke.linewidth; cmd_stroke.rgba_color = stroke.rgba_color; - alloc_cmd(cmd_ref, cmd_limit); + if (!alloc_cmd(cmd_ref, cmd_limit)) { + break; + } Cmd_Stroke_write(cmd_ref, cmd_stroke); cmd_ref.offset += Cmd_size; break; diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 215a97ab70811dd84204d859cb0cc3f2d17b6c64..260db69611077342550fa9a7e16b49fd67f0cc46 100644 GIT binary patch literal 39376 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 W`g)Ep0;^w0Gp^@bE$7a6s{aQlSF$4j literal 37764 zcma)_2Y@9-^}QP=EIH?#7s*JL92aoOD4Ah4unf!wc4t{~Sh7gYK~QoA2@)kOSrHQo zD1w3r2nZ@!;P-v+^_i)C;Qw!HS?8R4>(;GX-TnIY%#Ll-E;*>Grm1GArmLp!tQyDc z)wC#8HDlG;=!Xp5Y3NE5`bMm@`Wma~FiX|e@Y843YEacd-%cCd-7`kRVbfOC_i3{+ zUVU~&ob|W0@gL09Ban7x9x}9R$adQe8M=Ab@Nwh1CiETBJF=^{XUxd15k2Ea4)5zZ zXrzAA6@EQqhWAb!Q95+sQRdJ=RxGUAc+8lwTlDny?l^K^vT16_)!2RaoiMVmLg`?g z1Af}{Q}Y-$Z0y1Io%~15{KtO)mrm3e|76_ zZ721N=-Ut6K{j*K55)b-3i?wk*Dx1jvW9u<@qOKWBS$osz-%3ry2rp8W~vs1J6FTE ztFL=hS*rx7C$d%x;NP5QmTJ+d#x~RXO157UW04}pl2gU-%=rIBXztk({V_VLrNNu@ zjM-|;gud=E!<(DtdeuD!);mkJJiPGj>KQX)J1aIHnw;v9T zJ(YV5tg*dX1HN-lZ=(>_LGDrdtO+-cx;}D`f%CShdEWLBYXW@-_l9-o9XD0tbwcI& zxHf*xoI9%Zz~-~l$Wf-#Al>!@7qbFk<}Jaq8x^F}-7_ z%1eP6%e*$iubEe8wJCVh(Ifh=Cun0{b&rAEI;zd#{k~vxQS&fA=Ji(5ubbn3-M#yE z)faaBz22w3U(H!!^k{=g6(Mu}(FQfpxZ5+rkO1Ut=wDkJdG+x9)>$RR8K4=csl_PmMGUM@ z)z$UXt8<^*svU@v_3a35uCKHDPBZ={BS-a&*|fK39QA6fQQc!;jh)plaB}JQ)o$+U z9<6ItZ(VB#cj-OR%Q#)Qj|T;Fun-o;!yjqU9jkwv0= zt!VccSm&T>A9(WXAA_d9HjnP!ar<@CcU1FLUSwbK|N2Gshx;!p>$h`q_OER?@w2uO z;OyO~=DeGaDHdgp>K+4Y>Zp3)1cTE^OZE~ z30ksLZ6;|=k7RsuJPO>*vAvqyjB7a?bIUyj@|&?b0nXfA?A2H1W?)^3W-X4#w>i)3 z)hYEj?=1gy;Q!+WWZozD$7`#;2X5v)XLUv$yZ^fWA13JcC1Y-L{nyE9#T=*i&oOs( zZaGK)752YQ)IVUVbNlRKu5>+%Oh{7d`e&sJTT_}(Y~m+<=4XN`GZUc|kkhY@OBJ^!?BJn@sF~UQzex&i4SY`QF=5#qHI@U}CMRB!8mG8;_sz zXPSJq*niXHt2cRD^&+5ot+Z8dgOlTXO}{$F_UeNs@1N(>Chwo;%O>xir=1UG@~Y=) zucmMEHE?UJVK#Vk-u7x`@VM^r-QL5^d9A54_| zcMAMrKW?vPV=oJ z9Mi%l!8woX@2H)P_eG<6yLK5n{(vFl$4(r#)n?AoSsmLP*Voh6Gq&m7)Q>|S)5Q!; zOMN}GSEr!!P0>@3@zXWlK@oT8*x}v1Ta7u0&Ok=j-cg-i=h1lnW{}$1rN+aG>pS1L ze&=fVv{x60H|(9&W%B)cCUiBN8q+t%U57r^{j=r+_s@>%HZ{IKbd4A}&Tq%A`}Syu zjvPO>HmKhbeJt)!mNcb=4tF ziMiXV2hn`KwH8ld&b+aA)-rIPnMG4|Y_q6?!vXNA+f*HREfqFw-7f zjP`01@W6S~R_z3D?pIs28@Rc*?bYu373a!c@ChRiYy@>(2fznv#OENK=^MFB0<#6+ z&gw*P-d0wS?AwTyHxSo4D&@!mv>_8)?SUD%CEiZYvBjO z2fqKeSI4y2k8R;sxA1FQ_{}Z+))xL~3x6EWJ%FDXH1mC8Dm(XXxa%r?em9j*d-Z$^ zf2oDP+`>CL2ljsmHM)y^M*>y62jugF-ebvTR}JC*qQ)%%+}?P;U!vdE$ywbb;phH8oFb5|KtZp_&I*_%J7TA(quz3~hr_vbF|!E`J@ zK0j?vR)#y{{fS(2opa;k8W*E^EP`baeN*$Na8-k=&4X=v`o(DTGHwQXxp^DBdh{JiuTKOflfYWli9ZR+xc=^Yzx z&CEq^tflD_YZ`o57(z(j5ZC;SR2r5S2xy% z^lHX%?56a|aWk-S)!d8CYn%HJpDn=pXtO3m=slMltIe20>Epi*SbsHRZd=>TCqCPO z_0eX`?dgr_SZ&7q4t@N;3)WxFHSSW|nrm;bcCGE|#@mhFc#hL%yuIlYuM2EEHEsLQ zH_z|(svE3L%~-?ejpZ0^`j4VdEFR?>{%YEK=o4%I+NNf#1L%$A7;XCVG1MRHK(KSG zX&YbIcr0(&)QshGxv?ChO@E(7lGmYNW2tHLU2ANI*ETg{9YJp_$7s`kGJX7y1shAv zbLu#H{nZ_RQo-FnwNnetJy1KV(5$7}`Gsap)GjHsEo$vYh30zoySmWKTkVEI8w_5a zd_7NoO!El81MC_WX03P8yH?kIH@)2J?jCx5)Yh%}y|rc>$J|fz(EkB?{ncZi0-mPe zPl5G`{aJ8Z!Jh+rk1*Z~^w!<`m;AMwd%w&&UI&-)XM}Xr{(1w} z>-O_j?mS!5o1>cRmiw8jrhR*`alQ8$C!fE@@pD$L-`@0&Q8SL*&)UQp12&HLy42%9 zv@dxXm-Y3*{j4zFVYR>aO8J>J_x>h7x902B{KA^M|K^#`b@TM|Tkg0k>CH*aJmr3d zt0k}HAmzTU1vmHDH-k@C@Ezdp(_BQ^8|;0{&v#?koOcAA|2A~zmHRobmbjxNoDks? z;ma2GgW=Z4&zM8N#`b(N{-JP|(a?=A_qjn$`{6Y%*L5PA4)OQ*d-TuzSHO>ke}h?E z*8{YLi1h@$aU3syl3vZaIM-A3p4aNemiyj8P5U!7PHjG_xiR#Y`_4iCaNjM+liRHD z=}1=Yp05n&RO;tL;YY(W-tYb8_)Fjm)&AyjJ=|xR`1`%QoZs)!CHMPt$^GtJa=#Oo z-0#Bj#rWS70G?0QH0!w*Z85a)8{vxi#@Z(yz@9s)}-_4cW z_i`mazlHlwuC)73F8o;X@LgQ^VE9cf-1l&$-FI*$_uX5`edktk-?^3it`_dQw$ko< zwvzi^t>nH}E4lB^O745IlKZZ#ngeLxk~PPtCIWfs^q?}D!K2hO71(WlKb8&-1_<6 zDts{9cUR%seSa0M-S<}|?{49~&noS{&nmg^w8Fh7`CcpB&s*PTh5Mbt_gUeC;l9r* zx$m+{?z^m#`yQ+0zQ-!L@2|pLukWtH&Bu3F;pXGJtMJD2Q^6hYyQ|obh5PO*d@$U1 zSK&U(`tB-R|HTWg-S<|d-S<}EKBxNLD%|`wF1Yc0Zx#Dscz3~fh5OzrcKv-{6|UX) zRpCD0`MxS#|6>Z?4flOjY4?3q_+ae5uL^g(@2kRnKKFf9xa;wKRk+WazOM?m-oCF2 z-vaLYs&M1`t}1*m+;>&s+I?3Q?s(r-g=_a+m3(79m3*E#g61>M6twmEjKW+US?kB) zBR>l6bCY_;9SwGz&+{2~3|#%_dfa5NW7Rj{)6ma8HL;(2jy0a&1CEFLEa&%*#5)15 zo_HsL)%?tzT+eYbSl#?jsI~9G)%9~OKU39``>9}`)5_dWgRATBXRbW{XMkJRdM28B zYIGLZ8mSw@&uO*9I0xKX=X2526XQIvG1QIWXS-TrTmWv(`$9DJlk2r#1XeSSF?=3S zON>jvtucOprk?s;3RX*ui@|Eymmh-7E&NAdeX>uNgY{9*+O7nv`#hX+SAnfl#$658 zPdz@@fL%j;t_AC-?)omHSBwAkV71J916XYoZTqVEdrdcj)gABikKFnU<{Y{itX7_f zx4_kt!>wR7W9B^kF<9O28^-lHN=@uDl;@h*=cizwoq~OC3ig>P*yp9-yKDSOHEned z*k?=o4_ViJ+Grlye_Gq^KgIq4{k=5(949uP2kDLPn1^bcy)hr5pF;D{{%CEtPrV-l z8{<~$w>SOcG_}oW&MSYIW-R9wXY9|wjt&1g*lTZN?mExV|AO{3O+RhstCoDt&6xIy z@hsSw*{ffI)m~=}S+82TUUQTGir#qkuKzdmzomI-|8;G*&wS5;-Iws+f!)9B>w5ei ztdDwpo(CUW@E5?x(Ne<~!TP9QQ^)xO*jU;=q7E<7dst&_f266Izc_RK32c6@-E&DU zewp4n#_tulwNDOz2CJP&bG}#UJ)BS5UubI1Cr+H#z~$Qh3O8={<8`n;>KXGlaJl|B z;QFas_snDe0iRx;XK&KX!?o&f4(9oH`sAt4TQoKE6lczNz-6BA!pl7W0oO-8_mcO( z>eeK2-Um;jWqtny>!+S^{{pK&MPkW5o9@VsF4o9p|^d|>0Kd;Kgx@8R6q z=BKGSw>Wbz2rlPd2wu*;FxWg z%YJ_Y>|xEdElX1~4{_ow2QF*AJluPF>?^>_x~vG-M?Ljj32c3hWv%4;nS186Z>>$n zuY%3-zAMbUtAf>y=Vy&Pwr_&H&!%>(!PQ*5{@T=>Kc8Rr8NUX$I}5%h+!`d_T5x^T zjpzFxwTxdItQNiw*w5#j|KEb^qaL4i!R46s;I1q8mG$BJsOR%}1F*5Qc^z#;pFP#K zAx+Ia6`SkE^xiAQo7C7kY+C!-d*9rgehZq1_93<1J~?j*Hb!#X3apmb%b3T=Cr(>^&yF^3vBa@f`&=Uif}s_dNOafYtOf zo;J1k9{_ee$-NisbBB6-MuXM;F1Ix690OJ}-v0D*eYG73R?{|)UYI+ML_(K+bKSI7egCC;S+=b7!w7!}U?mJ?U7mdOr7#2YYy)Ydemn=3L^$J^@_L zbt1gHAD;x*M?GUs2A9wG--91Ve#zw&xIXGv*Vp0q!N$_&x|552zIU7oHdkw*zj>O! zy5Af9&S?Jj*6}p-@_W|l@bWYK40yR`XTtSS&sxp`yZ+>UHe5e-?-%FNr;ggrp{bdN zIC0Jg=bZGp{sOq?n|gdM1go3-dGzwwE(WXRd(98PYM0Qw9_J;AOTp@nzldJ$cx^uf zt7*H8UhehebLo%33(~8nZffpB)?%MrE(cp%*MAkg$7lTHU;RoN%d4)S_uRJUzm2}V z?yZe}&h;*RzOxuhn`1K9HDK4R@7473*scSsr6xCk)vl*mlW#KLjbL@h-$XBW-ToZ^ z<_2#5+(!Gga5cZ9dEdAdtmb{g?-7~z$7tH(cU$3Co^Q9KYs+Td9zH2oYe*C%=03r@ae9`~VZ^ZCK=CijEYGVf2pr?Y9XKLA%VZ~Y$x zJ4W4k@1a-Aybpsj@06lWkDzNy+$mtS%=;+#>|)->;A)xoaj;|5o%bPnHOHIRlVEe1 ziQi_BKLz%j^D{!5`{wUtIQ9v+_Kf`**mxQHb8yCLE5|+!*PiwM0-W`h=f$(=+OpnX zg4MF#UxCXy{~E5A_5KFz81>}!TX6EyUgq@-Tzm5R9XNUUyE>Wo_vqS^*YjYt*k1sb z`|u)MEqVO`>=^ar^+#~>(q88E99(%I9nMIQ94xOnpW9Q%wVRoO{F@DIY!Ad}*-zmGf*FxSD?Mkv28=Ai3NBw>l2SR<31PczHg218z?G8DFlC@!fB0W1sw% z16zyaw>((QdgL5Zb9~Mr_q#Q})MQ0$&cAYT4zC1PH=e&2BG3G*fL)KamBDi7T9w{h z?LA*tqhFoop?!_oZl73df{hWr7TEc6{;mzyN8NKq{!N;(ol9&U{%(rbjrdzNcFuKc zKYMdppMC?HhxQF?yM1!l2yBexurXLI*N}WYnz78Gfr;(CV^cJ9NvzGl#>)Gf&A~&d zvwF_kEx_v8&mmyrsJos`=+zQ)OK@4Ut>F6Pyx$tEHidk$$KQs#$J!ESC|Et$>Na3w zt9z|(NAF?XwQWmNbIsz!*&git*$26`jjz4jx;xfBV|M^spR9dHuv*S#ZEEFSTKA0K z8En0?&hLOHQD61=d>5>q&xl>XYM0=b?@GJE)m_6*^m51hU1<-nnzr5P<@v6(C)n>w z>e_dsSIgXcgI6wi7ubCBzG)w@n&+-H9}f1g=GunQ_M=%hapH~u8z*}{60BD4`Mz-X zTwBJC0;`*AH@)22Ivpp*5G`4x%UBohyMcb#`NmiPo{5b&36YE!qt3cE}xq(Leq8w zar{~O#b7nBy=$3Q?Gm`U`*SJ1hx?=c1Dcw9Aof1!@0MN$c0TW;*23>qKZL7it{;Kb z%*VY<9Q(XuyBzG??yvsNk#j&>c@B6kIF~tJ0rosG=d0*F%vt?PnwmL_vsc%EJqL1K zTnARWmgbyS)2qe*2C)85ux3AdZvv~`NXs+o&0uSwEuXiyfYpt8J-s|JZv{I~_>aNm zId&Ud?O@gzpWET(Id%tJKXt!1`F`(Cu)jZJeC-+g6RG{pBIBevT7ojz_`9&b{w3u$uXL zzC8inNKMhvPygvi0dEao}XTX_P+s|og&MS7E zS&w?+J_|N(_%Fd_t$zi#*7|9S-><>iQfql^zX6vu`Yl{7HToSmxoUfkre>~Ub9H~@ z>hjdkKJ&Z)Huv01Uj(ap&Bo^sU~`#?Yr*@_AHlvW+KQ$v*X2uKZP|}MfsLo`8lR_E zOWaq$YI*kiGuUT8_4xb+tnT=i>E*G#3O27i!@LGo^V*BgUu&P-V_t`=>6^N%#s6<$ z=Se-@0IQYHns36@9e)w|${p{s=HJ0;+TH@o^Q`$c*k?_3?Wf_Vmbu>n-_CiG^YdM} zTK4=su!q-$wtvvx;CGVUV{ziV4>q>v^oL*%-{-5#nWFk9JucP1==pE;Z~n(U{ea$h z+7s_%aJf&Pz}xjJd_D!MXUu2da?F3=J`bqJ=X0=n_!r=dNiMbj60VPWeE6uC>UUVD zf$OK9anpj;%jbbMxH)Re^FTXT-M#R4_vLvW=mh&bpsxKR#;Tcz`#e23`|R)a%N^^# zmz)8-B)z(0{Tmi)j-8A?6WHI2)#kqekY|0ffN!d8{(AtqKTAcbW(E6q)f}TO@n!?7 z8PB-##G4(guQub#^Db}>u#2mZSF z^P_9?x*trO1;GAW2lFsbxjuOoS`ciU@>yshH1+r_3^qpY{c?RW$0A_&Au&3cYf-p* zd=>*6!+)P^9&&wB=Ow_|x5dG7ZT?&GrNEALoc}IEo_$*yd z>bbA43RYW*mT}(%XPooP^>ZHYIj+@vO=7N&t-L?10r#Gw9-lSA>fvjF%Q0)iy{D+h zXC1J5__x62J!M_EKI-vV4}3mr&idAe>!+T3$_8Nd@}9CG+#I##p0W{G-5P96FV8(? z6R`Ibb?u(hO|AJIV^j36yIwX!*Ot$P&B1E99*2NEydJe}K~uBlV)w}J6kCE@ucNKd z)DwGau$uet-X)HGzEgY~>^$zL{?6fN%{KHN=Aq9}+V(W_5U1YTf^RG8y&YU_3S+H{ zpMh%5Z%oGdz+YFV9nrOAEjxkLtW)OR8SdP9H~1a6n$J7={j=}F9jncC?Mm6w!Z0tv=x6ett!@b|A=NV}auzJSq3BH3dk1@u3(q3>i{j7yHHEUrW z>BapN#)L;9UP*V0mH>1Lw1(8!XTLU^v*%631vuyb)kE=Qgf9@kWC6)n;6I_GDl1 z*X_wDbZz-8*$=Fib9aBRhqcw#Lz_f%o#Mpr1%F-q(dgQ89~uKbfR@h^xjwlD#)4a~ zfpKW+@i`D|4EN5p%JoT&CV*4(@nE^OUGSd>cC6$2=;f*TLEtm+af~+i)b+bo*PQte z0k`IOD4KfC?Zd!ouJbz1oayP0gsUG;JA!5o2h*#YpFT%{)x2Nr13wz9mbFa=XKlxT z<;FRdUf-*Qo` zc^!WbuI6>@HT!+Ahv${HQ)p_|TAVnig3D|6G`Rb&{v>0)j!y@x=Ndi(tdEXZ(5f_k6>>S{MDz%iOP`_b_*TuB54%yVzVOW4{KR z>*8v#JnOg)ocrvx`q6ToTo3j>>lkf`cLP|>^TW9E#Jdr!uQub#Gwv2}^0*l+*Y8&P zn`oK$$KZ>YS08P;u5JV8y2^aFqp9cox&y4X5-sEI1ZSM{$@R0|sh53u4*Fd(bKHfk ze0IAV?(?2{eC`3Ohu;e>$J__^c~3n)_k-2Le+n+2_a1=jqaL3J!R7PbLva1n^St*k zSiOASdjxKd+VZ?N1*~pO9;KJ(dG9f>&wJ|Hy{D_0huC6JWVx{hK*Yf&F_M z>W+P!UM-)8KLdZ==i$%MwPoL)0Y6R4=b>C5?f#qXXTiqyJCAw!8SzWF`tkV4e+B*p zO+Dj&4K{A>A#(jP?{C1y)t!Ti@FTmyRXMYJl zt{CG3lRoO#a83L3ooUdFrOjHm(R)~HZPU`!thLzn-p4q(y8Ql{+sp5%xqjw0nRuP} z=GyV^CCI(U;Zh9(pFpqvId31=rulb&e1|p*&BMQev=;gK_pN58IsdHm`I|qtf(I4+ zCoTNp7XCyFf2M{1rr`b!j~8p6Yxg#|d6dtyx5L#RA$OKi-APl+ea#%zvS0Us)pD)> z6s+cX?ET$;xB4K>zxU=m+B5bcur|+q*D8!xbC4m zw%>xwxW9v|Jx5F2--8oZd&WKw)@EGK1$k_L09$jPP5oT_BTX&#m%!F8_CJBuVt*NI z4Pt)&&+d2P;4KNrnwa~}F!n{R^$75u#x{y_`> zw1t1!!u|inDC13EaR2^`|Gya8v&MJ8=4DM%hj+p1xmN!HR=a~{9n4uR{_lg;a;^Ro ztX5vD|AOC3a~|y(`){x||W*?odd2G z`z~*Q zf~`}okA=Yc=N=%}C;khAU4L>}1gz$GzdI~Ob9}BHxjv5fKCyUh_Z}k7J){%cf;5l$ zY2HKTqhEmLJ!B#J0)Hse|od2IWG%eV)?)ymKMUU=ea&)CsmZN{~>^4P|K zT|?fvjt8q9NQ=(|us*5nM6g;PEj|Z<%{g%PMr@0n}c&0 zLm&73F#5x3o(nlw<@zUwBfzdN@9T~Pt2usMVjl%|d_Du^`Z(V6`RLm2xh~GRz5%w4 zX&xKWyjK`+Bbw*>CiFSik7ZnWuAkV#zhCe(!DrPx=k{@MMHWBTl}KI1O{t2zEc`ip3e&%Vp`alCtb zacy_s#o701v29KB7(#R3H>2OO##_*DMe`i^Hhs>4ONrq*kUhN&?wE2$`WVCgP}47S-2g7O47IG`Ua(sB_&%^&xySdz&8M8_r~1&c_6NXOyD?n5ntqw)GQeaJ9_+C^&N))4A33%iNEF%efzitCf5F1l+mHJ$@2R zJ##$;&RoWDE;apH_jpfYdF)1WkH1U5dyRLc--G5J??sRJ1*!CAX8T)Ucnnd>*;a<1RP)yh47 z4(?p#9{&zaJ$w9nuv+GR9-O(2>D+4iwbp$ku|0;-ta}&z@EUj1kDyuied$y87l>2V z{YAKA%6j|(O+9u0BUmkUH-=hj@e)`qb^jAsEp=ZM{4&jaGLQGPS86@A_%mEBwfGA- zwJ@f&P}8rq7GsF#aRAL)>_^{QtMCi z!We3)#oxecsl^*$wXzm(!p$f1Sc|vN)KiPUgVj=tx524}F|CD~eyz2bL_Ci^nzcBP zeqxO$&>uvz76;R(7Vi+Jti`)<$0SE<@eefh)Z#s`T54eowbbH$uv%*IPq12QF_@VD zf}2k{&%e>sv-S_bS-UY@yPAHjYd@M;9!Jnz`=Rtl*7$JxqiC-E82YUJL*kTc{|N4w zGN+Hx)U)5<=V-59Q2O~2N)pFk{+ z<7lq^So-5>uJ=Uxe8zvtSoLyVTq^afZyGo= zx5l0hO+9-!JyUUa={Y*WbX8q2jw_bkc&P$xsPoMc}o;A)2H%3{%+0fKezuCcRW&P%Wr+(U9kD7Hz z>^b4BvFAclPyOZwtEGP5Vf;LBbz_>p-1_Z;ZGN!p)aF{`sow(NJ+V1PTWkH!#pZDq zE%iIQ#@+Pi(5&Bi^w!VM&LxSH`suS&&9lY@;l?QIw-B0o>bEdht*qZ7@YGMc>rtDL z>n*Vtg}26D3{5@tTO6#G`t40FOTg8QY5sESw-2_Z!LC!AYmujZ%Yc1;aE!Lr`dx(0 z<9u4`cR`K)JiU-+{Vt}retzDrLY&l3pH*v~H4cUwqpaVuXzHooH^6FT{g#KPe%f7+ z+H$nSUIE-1dqp($)NduQTIx57Tvmpw8`J#d)~^TKH^Ht`n`@D$eyf4UU~`PN*7{wB z&EpbU>i2^hZ$W=4&HDY2-ui7uzdmtNKYcc+dDgf(+!$s3)<9EF{niAlmGxU2p89Eb zJ!)&w5_=tRYwT~Ksi%JHg4I&LP03|FxVkaTUvB+2$F?EZb!u}h^3-o5@YdKIqph`m zS7P({5iRw*yv7sgub^4KtLUxYf%IDwC-u|k+cnP`H-;OdtluVR>Z#wRV70P-o5NE- z?XE{{Gg@MA0lvH?_7F7n)Nf0$TIx3r|E=Ka#x#Gq^&5|EDA;vsb1m}JZyT`Bd5+Q6 zTEFYCd0b6P{jRC;;q=$itl#zY*3ajpU5JzV>9cFiv&L=V#whEz9h!RTw>?;`tly6C z)K9zXQQLu**gJt+WABWnp89oY`cM7r#9CjPyKcWAC1j1 z+FI*(Gd7PKXsO?gH6BcV6V3YFLT~-lh7o5_&8=Tg!H!bb=>+GiY@b{0j?L$*DSFv+FfPG)^NZRY*x5(Pq`@z+xfbXY` z0$V%Rr7bz^4_5P?qIEt1tfsGPmg_qi+i0+B(&qZ**`Kjs{k2&OdG==<*mKx1+A{Zn x;LJT4`*^sTF`P#p-w9xSbN=*!)m+zP>fo9W0;^A?8N=GD`FmKYn|;oY{{u`R>)!wX diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index 5e8957f1..a0e50112 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -9,6 +9,9 @@ #version 450 #extension GL_GOOGLE_include_directive : enable +#include "setup.h" +#include "mem.h" + #define N_ROWS 4 #define WG_SIZE 32 #define LG_WG_SIZE 5 @@ -16,28 +19,22 @@ layout(local_size_x = WG_SIZE, local_size_y = 1) in; -layout(set = 0, binding = 0) readonly buffer SceneBuf { +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; +}; + +layout(set = 0, binding = 2) readonly buffer SceneBuf { uint[] scene; }; // It would be better to use the Vulkan memory model than // "volatile" but shooting for compatibility here rather // than doing things right. -layout(set = 0, binding = 1) volatile buffer StateBuf { +layout(set = 0, binding = 3) volatile buffer StateBuf { uint part_counter; uint[] state; }; -// The annotated results are stored here. -layout(set = 0, binding = 2) buffer AnnotatedBuf { - uint[] annotated; -}; - -// Path segments are stored here. -layout(set = 0, binding = 3) buffer PathSegBuf { - uint[] pathseg; -}; - #include "scene.h" #include "state.h" #include "annotated.h" @@ -175,6 +172,10 @@ shared uint sh_part_ix; shared State sh_prefix; void main() { + if (mem_overflow) { + return; + } + State th_state[N_ROWS]; // Determine partition to process by atomic counter (described in Section // 4.4 of prefix sum paper). @@ -341,9 +342,9 @@ void main() { } // We do encoding a bit by hand to minimize divergence. Another approach // would be to have a fill/stroke bool. - PathSegRef path_out_ref = PathSegRef((st.pathseg_count - 1) * PathSeg_size); + PathSegRef path_out_ref = PathSegRef(conf.pathseg_base + (st.pathseg_count - 1) * PathSeg_size); uint out_tag = tag == Element_FillLine ? PathSeg_FillCubic : PathSeg_StrokeCubic; - pathseg[path_out_ref.offset >> 2] = out_tag; + memory[path_out_ref.offset >> 2] = out_tag; PathStrokeCubic_write(PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; case Element_FillQuad: @@ -365,9 +366,9 @@ void main() { } // We do encoding a bit by hand to minimize divergence. Another approach // would be to have a fill/stroke bool. - path_out_ref = PathSegRef((st.pathseg_count - 1) * PathSeg_size); + path_out_ref = PathSegRef(conf.pathseg_base + (st.pathseg_count - 1) * PathSeg_size); out_tag = tag == Element_FillQuad ? PathSeg_FillCubic : PathSeg_StrokeCubic; - pathseg[path_out_ref.offset >> 2] = out_tag; + memory[path_out_ref.offset >> 2] = out_tag; PathStrokeCubic_write(PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; case Element_FillCubic: @@ -386,9 +387,9 @@ void main() { } // We do encoding a bit by hand to minimize divergence. Another approach // would be to have a fill/stroke bool. - path_out_ref = PathSegRef((st.pathseg_count - 1) * PathSeg_size); + path_out_ref = PathSegRef(conf.pathseg_base + (st.pathseg_count - 1) * PathSeg_size); out_tag = tag == Element_FillCubic ? PathSeg_FillCubic : PathSeg_StrokeCubic; - pathseg[path_out_ref.offset >> 2] = out_tag; + memory[path_out_ref.offset >> 2] = out_tag; PathStrokeCubic_write(PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; case Element_Stroke: @@ -398,7 +399,7 @@ void main() { vec2 lw = get_linewidth(st); anno_stroke.bbox = st.bbox + vec4(-lw, lw); anno_stroke.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); - AnnotatedRef out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + AnnotatedRef out_ref = AnnotatedRef(conf.anno_base + (st.path_count - 1) * Annotated_size); Annotated_Stroke_write(out_ref, anno_stroke); break; case Element_Fill: @@ -406,7 +407,7 @@ void main() { AnnoFill anno_fill; anno_fill.rgba_color = fill.rgba_color; anno_fill.bbox = st.bbox; - out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + out_ref = AnnotatedRef(conf.anno_base + (st.path_count - 1) * Annotated_size); Annotated_Fill_write(out_ref, anno_fill); break; case Element_BeginClip: @@ -414,14 +415,14 @@ void main() { AnnoClip anno_begin_clip = AnnoClip(begin_clip.bbox); // This is the absolute bbox, it's been transformed during encoding. anno_begin_clip.bbox = begin_clip.bbox; - out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + out_ref = AnnotatedRef(conf.anno_base + (st.path_count - 1) * Annotated_size); Annotated_BeginClip_write(out_ref, anno_begin_clip); break; case Element_EndClip: Clip end_clip = Element_EndClip_read(this_ref); // This bbox is expected to be the same as the begin one. AnnoClip anno_end_clip = AnnoClip(end_clip.bbox); - out_ref = AnnotatedRef((st.path_count - 1) * Annotated_size); + out_ref = AnnotatedRef(conf.anno_base + (st.path_count - 1) * Annotated_size); Annotated_EndClip_write(out_ref, anno_end_clip); break; } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index fd314c81fa191f058ce5ca4513123a11099ceaf3..95171f82fb84f69986c892679b14485db51a217e 100644 GIT binary patch literal 59316 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 literal 58352 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 2) + CLIP_LINK_OFFSET] = link; + } sh_clip_alloc = alloc; - clip_scratch[alloc + CLIP_LINK_OFFSET] = link; } barrier(); return sh_clip_alloc; @@ -95,8 +87,12 @@ float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { } void main() { + if (mem_overflow) { + return; + } + uint tile_ix = gl_WorkGroupID.y * WIDTH_IN_TILES + gl_WorkGroupID.x; - CmdRef cmd_ref = CmdRef(tile_ix * PTCL_INITIAL_ALLOC); + CmdRef cmd_ref = CmdRef(conf.ptcl_base + tile_ix * PTCL_INITIAL_ALLOC); uvec2 xy_uint = uvec2(gl_GlobalInvocationID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); vec2 xy = vec2(xy_uint); @@ -168,10 +164,14 @@ void main() { uint blend_slot = blend_sp % BLEND_STACK_SIZE; if (blend_sp == blend_spill + BLEND_STACK_SIZE) { // spill to scratch buffer - clip_tos = alloc_clip_buf(clip_tos); - uint base_ix = clip_tos + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; + Alloc alloc = alloc_clip_buf(clip_tos); + if (alloc.failed) { + return; + } + clip_tos = alloc.offset; + uint base_ix = (clip_tos >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; for (uint k = 0; k < CHUNK; k++) { - clip_scratch[base_ix + k * TILE_WIDTH_PX * CHUNK_DY] = blend_stack[blend_slot][k]; + memory[base_ix + k * TILE_WIDTH_PX * CHUNK_DY] = blend_stack[blend_slot][k]; } blend_spill++; } @@ -194,11 +194,11 @@ void main() { CmdEndClip end_clip = Cmd_EndClip_read(cmd_ref); blend_slot = (blend_sp - 1) % BLEND_STACK_SIZE; if (blend_sp == blend_spill) { - uint base_ix = clip_tos + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; + uint base_ix = (clip_tos >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; for (uint k = 0; k < CHUNK; k++) { - blend_stack[blend_slot][k] = clip_scratch[base_ix + k * TILE_WIDTH_PX * CHUNK_DY]; + blend_stack[blend_slot][k] = memory[base_ix + k * TILE_WIDTH_PX * CHUNK_DY]; } - clip_tos = clip_scratch[clip_tos + CLIP_LINK_OFFSET]; + clip_tos = memory[(clip_tos >> 2) + CLIP_LINK_OFFSET]; blend_spill--; } blend_sp--; diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index 33ed4f8d043b1edaa17d62b24c5a0fdbd317b483..f7acb7f2dbd1f53f027ba12fb957b0ac1a1c7af5 100644 GIT binary patch literal 34160 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 literal 32608 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 diff --git a/piet-gpu/shader/mem.h b/piet-gpu/shader/mem.h new file mode 100644 index 00000000..9373cbfa --- /dev/null +++ b/piet-gpu/shader/mem.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +layout(set = 0, binding = 0) buffer Memory { + // offset into memory of the next allocation, initialized by the user. + uint mem_offset; + bool mem_overflow; + uint[] memory; +}; + +// Alloc represents a memory allocation. +struct Alloc { + // offset in bytes into memory. + uint offset; + // failed is true if the allocation overflowed memory. + bool failed; +}; + +// malloc allocates size bytes of memory. +Alloc malloc(uint size) { + Alloc a; + // Round up to nearest 32-bit word. + size = (size + 3) & ~3; + a.offset = atomicAdd(mem_offset, size); + a.failed = a.offset + size > memory.length() * 4; + if (a.failed) { + mem_overflow = true; + } + return a; +} diff --git a/piet-gpu/shader/path_coarse.comp b/piet-gpu/shader/path_coarse.comp index cbca10fe..20c35866 100644 --- a/piet-gpu/shader/path_coarse.comp +++ b/piet-gpu/shader/path_coarse.comp @@ -8,24 +8,15 @@ #extension GL_GOOGLE_include_directive : enable #include "setup.h" +#include "mem.h" #define LG_COARSE_WG 5 #define COARSE_WG (1 << LG_COARSE_WG) layout(local_size_x = COARSE_WG, local_size_y = 1) in; -layout(set = 0, binding = 0) buffer PathSegBuf { - uint[] pathseg; -}; - -layout(set = 0, binding = 1) buffer AllocBuf { - uint n_paths; - uint n_pathseg; - uint alloc; -}; - -layout(set = 0, binding = 2) buffer TileBuf { - uint[] tile; +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; }; #include "pathseg.h" @@ -96,11 +87,15 @@ SubdivResult estimate_subdiv(vec2 p0, vec2 p1, vec2 p2, float sqrt_tol) { } void main() { + if (mem_overflow) { + return; + } + uint element_ix = gl_GlobalInvocationID.x; - PathSegRef ref = PathSegRef(element_ix * PathSeg_size); + PathSegRef ref = PathSegRef(conf.pathseg_base + element_ix * PathSeg_size); uint tag = PathSeg_Nop; - if (element_ix < n_pathseg) { + if (element_ix < conf.n_pathseg) { tag = PathSeg_tag(ref); } switch (tag) { @@ -128,7 +123,7 @@ void main() { uint n = max(uint(ceil(val * 0.5 / sqrt(REM_ACCURACY))), 1); uint path_ix = cubic.path_ix; - Path path = Path_read(PathRef(path_ix * Path_size)); + Path path = Path_read(PathRef(conf.tile_base + path_ix * Path_size)); ivec4 bbox = ivec4(path.bbox); vec2 p0 = cubic.p0; qp0 = cubic.p0; @@ -187,7 +182,12 @@ void main() { // TODO: can be tighter, use c to bound width uint n_tile_alloc = uint((x1 - x0) * (y1 - y0)); // Consider using subgroups to aggregate atomic add. - uint tile_offset = atomicAdd(alloc, n_tile_alloc * TileSeg_size); + Alloc tile_alloc = malloc(n_tile_alloc * TileSeg_size); + if (tile_alloc.failed) { + return; + } + uint tile_offset = tile_alloc.offset; + TileSeg tile_seg; int xray = int(floor(p0.x*SX)); @@ -204,7 +204,7 @@ void main() { int backdrop = p1.y < p0.y ? 1 : -1; TileRef tile_ref = Tile_index(path.tiles, uint(base + xbackdrop)); uint tile_el = tile_ref.offset >> 2; - atomicAdd(tile[tile_el + 1], backdrop); + atomicAdd(memory[tile_el + 1], backdrop); } // next_xray is the xray for the next scanline; the line segment intersects @@ -227,7 +227,7 @@ void main() { float tile_x0 = float(x * TILE_WIDTH_PX); TileRef tile_ref = Tile_index(path.tiles, uint(base + x)); uint tile_el = tile_ref.offset >> 2; - uint old = atomicExchange(tile[tile_el], tile_offset); + uint old = atomicExchange(memory[tile_el], tile_offset); tile_seg.origin = p0; tile_seg.vector = p1 - p0; float y_edge = 0.0; diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index bec287b0a915a1af830ffa29534edc47416a682a..6b2e3b34d62b2467b387cb4203e62762acd9ea86 100644 GIT binary patch literal 32248 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 B8Sel9 literal 30852 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 2; - uint raw0 = pathseg[ix + 0]; - uint raw1 = pathseg[ix + 1]; - uint raw2 = pathseg[ix + 2]; - uint raw3 = pathseg[ix + 3]; - uint raw4 = pathseg[ix + 4]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; + uint raw4 = memory[ix + 4]; PathFillLine s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -103,22 +103,22 @@ PathFillLine PathFillLine_read(PathFillLineRef ref) { void PathFillLine_write(PathFillLineRef ref, PathFillLine s) { uint ix = ref.offset >> 2; - pathseg[ix + 0] = floatBitsToUint(s.p0.x); - pathseg[ix + 1] = floatBitsToUint(s.p0.y); - pathseg[ix + 2] = floatBitsToUint(s.p1.x); - pathseg[ix + 3] = floatBitsToUint(s.p1.y); - pathseg[ix + 4] = s.path_ix; + memory[ix + 0] = floatBitsToUint(s.p0.x); + memory[ix + 1] = floatBitsToUint(s.p0.y); + memory[ix + 2] = floatBitsToUint(s.p1.x); + memory[ix + 3] = floatBitsToUint(s.p1.y); + memory[ix + 4] = s.path_ix; } PathStrokeLine PathStrokeLine_read(PathStrokeLineRef ref) { uint ix = ref.offset >> 2; - uint raw0 = pathseg[ix + 0]; - uint raw1 = pathseg[ix + 1]; - uint raw2 = pathseg[ix + 2]; - uint raw3 = pathseg[ix + 3]; - uint raw4 = pathseg[ix + 4]; - uint raw5 = pathseg[ix + 5]; - uint raw6 = pathseg[ix + 6]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; + uint raw4 = memory[ix + 4]; + uint raw5 = memory[ix + 5]; + uint raw6 = memory[ix + 6]; PathStrokeLine s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -129,26 +129,26 @@ PathStrokeLine PathStrokeLine_read(PathStrokeLineRef ref) { void PathStrokeLine_write(PathStrokeLineRef ref, PathStrokeLine s) { uint ix = ref.offset >> 2; - pathseg[ix + 0] = floatBitsToUint(s.p0.x); - pathseg[ix + 1] = floatBitsToUint(s.p0.y); - pathseg[ix + 2] = floatBitsToUint(s.p1.x); - pathseg[ix + 3] = floatBitsToUint(s.p1.y); - pathseg[ix + 4] = s.path_ix; - pathseg[ix + 5] = floatBitsToUint(s.stroke.x); - pathseg[ix + 6] = floatBitsToUint(s.stroke.y); + memory[ix + 0] = floatBitsToUint(s.p0.x); + memory[ix + 1] = floatBitsToUint(s.p0.y); + memory[ix + 2] = floatBitsToUint(s.p1.x); + memory[ix + 3] = floatBitsToUint(s.p1.y); + memory[ix + 4] = s.path_ix; + memory[ix + 5] = floatBitsToUint(s.stroke.x); + memory[ix + 6] = floatBitsToUint(s.stroke.y); } PathFillCubic PathFillCubic_read(PathFillCubicRef ref) { uint ix = ref.offset >> 2; - uint raw0 = pathseg[ix + 0]; - uint raw1 = pathseg[ix + 1]; - uint raw2 = pathseg[ix + 2]; - uint raw3 = pathseg[ix + 3]; - uint raw4 = pathseg[ix + 4]; - uint raw5 = pathseg[ix + 5]; - uint raw6 = pathseg[ix + 6]; - uint raw7 = pathseg[ix + 7]; - uint raw8 = pathseg[ix + 8]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; + uint raw4 = memory[ix + 4]; + uint raw5 = memory[ix + 5]; + uint raw6 = memory[ix + 6]; + uint raw7 = memory[ix + 7]; + uint raw8 = memory[ix + 8]; PathFillCubic s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -160,30 +160,30 @@ PathFillCubic PathFillCubic_read(PathFillCubicRef ref) { void PathFillCubic_write(PathFillCubicRef ref, PathFillCubic s) { uint ix = ref.offset >> 2; - pathseg[ix + 0] = floatBitsToUint(s.p0.x); - pathseg[ix + 1] = floatBitsToUint(s.p0.y); - pathseg[ix + 2] = floatBitsToUint(s.p1.x); - pathseg[ix + 3] = floatBitsToUint(s.p1.y); - pathseg[ix + 4] = floatBitsToUint(s.p2.x); - pathseg[ix + 5] = floatBitsToUint(s.p2.y); - pathseg[ix + 6] = floatBitsToUint(s.p3.x); - pathseg[ix + 7] = floatBitsToUint(s.p3.y); - pathseg[ix + 8] = s.path_ix; + memory[ix + 0] = floatBitsToUint(s.p0.x); + memory[ix + 1] = floatBitsToUint(s.p0.y); + memory[ix + 2] = floatBitsToUint(s.p1.x); + memory[ix + 3] = floatBitsToUint(s.p1.y); + memory[ix + 4] = floatBitsToUint(s.p2.x); + memory[ix + 5] = floatBitsToUint(s.p2.y); + memory[ix + 6] = floatBitsToUint(s.p3.x); + memory[ix + 7] = floatBitsToUint(s.p3.y); + memory[ix + 8] = s.path_ix; } PathStrokeCubic PathStrokeCubic_read(PathStrokeCubicRef ref) { uint ix = ref.offset >> 2; - uint raw0 = pathseg[ix + 0]; - uint raw1 = pathseg[ix + 1]; - uint raw2 = pathseg[ix + 2]; - uint raw3 = pathseg[ix + 3]; - uint raw4 = pathseg[ix + 4]; - uint raw5 = pathseg[ix + 5]; - uint raw6 = pathseg[ix + 6]; - uint raw7 = pathseg[ix + 7]; - uint raw8 = pathseg[ix + 8]; - uint raw9 = pathseg[ix + 9]; - uint raw10 = pathseg[ix + 10]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; + uint raw4 = memory[ix + 4]; + uint raw5 = memory[ix + 5]; + uint raw6 = memory[ix + 6]; + uint raw7 = memory[ix + 7]; + uint raw8 = memory[ix + 8]; + uint raw9 = memory[ix + 9]; + uint raw10 = memory[ix + 10]; PathStrokeCubic s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -196,21 +196,21 @@ PathStrokeCubic PathStrokeCubic_read(PathStrokeCubicRef ref) { void PathStrokeCubic_write(PathStrokeCubicRef ref, PathStrokeCubic s) { uint ix = ref.offset >> 2; - pathseg[ix + 0] = floatBitsToUint(s.p0.x); - pathseg[ix + 1] = floatBitsToUint(s.p0.y); - pathseg[ix + 2] = floatBitsToUint(s.p1.x); - pathseg[ix + 3] = floatBitsToUint(s.p1.y); - pathseg[ix + 4] = floatBitsToUint(s.p2.x); - pathseg[ix + 5] = floatBitsToUint(s.p2.y); - pathseg[ix + 6] = floatBitsToUint(s.p3.x); - pathseg[ix + 7] = floatBitsToUint(s.p3.y); - pathseg[ix + 8] = s.path_ix; - pathseg[ix + 9] = floatBitsToUint(s.stroke.x); - pathseg[ix + 10] = floatBitsToUint(s.stroke.y); + memory[ix + 0] = floatBitsToUint(s.p0.x); + memory[ix + 1] = floatBitsToUint(s.p0.y); + memory[ix + 2] = floatBitsToUint(s.p1.x); + memory[ix + 3] = floatBitsToUint(s.p1.y); + memory[ix + 4] = floatBitsToUint(s.p2.x); + memory[ix + 5] = floatBitsToUint(s.p2.y); + memory[ix + 6] = floatBitsToUint(s.p3.x); + memory[ix + 7] = floatBitsToUint(s.p3.y); + memory[ix + 8] = s.path_ix; + memory[ix + 9] = floatBitsToUint(s.stroke.x); + memory[ix + 10] = floatBitsToUint(s.stroke.y); } uint PathSeg_tag(PathSegRef ref) { - return pathseg[ref.offset >> 2]; + return memory[ref.offset >> 2]; } PathFillLine PathSeg_FillLine_read(PathSegRef ref) { @@ -230,26 +230,26 @@ PathStrokeCubic PathSeg_StrokeCubic_read(PathSegRef ref) { } void PathSeg_Nop_write(PathSegRef ref) { - pathseg[ref.offset >> 2] = PathSeg_Nop; + memory[ref.offset >> 2] = PathSeg_Nop; } void PathSeg_FillLine_write(PathSegRef ref, PathFillLine s) { - pathseg[ref.offset >> 2] = PathSeg_FillLine; + memory[ref.offset >> 2] = PathSeg_FillLine; PathFillLine_write(PathFillLineRef(ref.offset + 4), s); } void PathSeg_StrokeLine_write(PathSegRef ref, PathStrokeLine s) { - pathseg[ref.offset >> 2] = PathSeg_StrokeLine; + memory[ref.offset >> 2] = PathSeg_StrokeLine; PathStrokeLine_write(PathStrokeLineRef(ref.offset + 4), s); } void PathSeg_FillCubic_write(PathSegRef ref, PathFillCubic s) { - pathseg[ref.offset >> 2] = PathSeg_FillCubic; + memory[ref.offset >> 2] = PathSeg_FillCubic; PathFillCubic_write(PathFillCubicRef(ref.offset + 4), s); } void PathSeg_StrokeCubic_write(PathSegRef ref, PathStrokeCubic s) { - pathseg[ref.offset >> 2] = PathSeg_StrokeCubic; + memory[ref.offset >> 2] = PathSeg_StrokeCubic; PathStrokeCubic_write(PathStrokeCubicRef(ref.offset + 4), s); } diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index 20b362ed..eb21eac0 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -173,10 +173,10 @@ CmdRef Cmd_index(CmdRef ref, uint index) { CmdCircle CmdCircle_read(CmdCircleRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; - uint raw1 = ptcl[ix + 1]; - uint raw2 = ptcl[ix + 2]; - uint raw3 = ptcl[ix + 3]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; CmdCircle s; s.center = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.radius = uintBitsToFloat(raw2); @@ -186,18 +186,18 @@ CmdCircle CmdCircle_read(CmdCircleRef ref) { void CmdCircle_write(CmdCircleRef ref, CmdCircle s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = floatBitsToUint(s.center.x); - ptcl[ix + 1] = floatBitsToUint(s.center.y); - ptcl[ix + 2] = floatBitsToUint(s.radius); - ptcl[ix + 3] = s.rgba_color; + memory[ix + 0] = floatBitsToUint(s.center.x); + memory[ix + 1] = floatBitsToUint(s.center.y); + memory[ix + 2] = floatBitsToUint(s.radius); + memory[ix + 3] = s.rgba_color; } CmdLine CmdLine_read(CmdLineRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; - uint raw1 = ptcl[ix + 1]; - uint raw2 = ptcl[ix + 2]; - uint raw3 = ptcl[ix + 3]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; CmdLine s; s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -206,17 +206,17 @@ CmdLine CmdLine_read(CmdLineRef ref) { void CmdLine_write(CmdLineRef ref, CmdLine s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = floatBitsToUint(s.start.x); - ptcl[ix + 1] = floatBitsToUint(s.start.y); - ptcl[ix + 2] = floatBitsToUint(s.end.x); - ptcl[ix + 3] = floatBitsToUint(s.end.y); + memory[ix + 0] = floatBitsToUint(s.start.x); + memory[ix + 1] = floatBitsToUint(s.start.y); + memory[ix + 2] = floatBitsToUint(s.end.x); + memory[ix + 3] = floatBitsToUint(s.end.y); } CmdStroke CmdStroke_read(CmdStrokeRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; - uint raw1 = ptcl[ix + 1]; - uint raw2 = ptcl[ix + 2]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; CmdStroke s; s.tile_ref = raw0; s.half_width = uintBitsToFloat(raw1); @@ -226,16 +226,16 @@ CmdStroke CmdStroke_read(CmdStrokeRef ref) { void CmdStroke_write(CmdStrokeRef ref, CmdStroke s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.tile_ref; - ptcl[ix + 1] = floatBitsToUint(s.half_width); - ptcl[ix + 2] = s.rgba_color; + memory[ix + 0] = s.tile_ref; + memory[ix + 1] = floatBitsToUint(s.half_width); + memory[ix + 2] = s.rgba_color; } CmdFill CmdFill_read(CmdFillRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; - uint raw1 = ptcl[ix + 1]; - uint raw2 = ptcl[ix + 2]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; CmdFill s; s.tile_ref = raw0; s.backdrop = int(raw1); @@ -245,15 +245,15 @@ CmdFill CmdFill_read(CmdFillRef ref) { void CmdFill_write(CmdFillRef ref, CmdFill s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.tile_ref; - ptcl[ix + 1] = uint(s.backdrop); - ptcl[ix + 2] = s.rgba_color; + memory[ix + 0] = s.tile_ref; + memory[ix + 1] = uint(s.backdrop); + memory[ix + 2] = s.rgba_color; } CmdBeginClip CmdBeginClip_read(CmdBeginClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; - uint raw1 = ptcl[ix + 1]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; CmdBeginClip s; s.tile_ref = raw0; s.backdrop = int(raw1); @@ -262,13 +262,13 @@ CmdBeginClip CmdBeginClip_read(CmdBeginClipRef ref) { void CmdBeginClip_write(CmdBeginClipRef ref, CmdBeginClip s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.tile_ref; - ptcl[ix + 1] = uint(s.backdrop); + memory[ix + 0] = s.tile_ref; + memory[ix + 1] = uint(s.backdrop); } CmdBeginSolidClip CmdBeginSolidClip_read(CmdBeginSolidClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; + uint raw0 = memory[ix + 0]; CmdBeginSolidClip s; s.alpha = uintBitsToFloat(raw0); return s; @@ -276,12 +276,12 @@ CmdBeginSolidClip CmdBeginSolidClip_read(CmdBeginSolidClipRef ref) { void CmdBeginSolidClip_write(CmdBeginSolidClipRef ref, CmdBeginSolidClip s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = floatBitsToUint(s.alpha); + memory[ix + 0] = floatBitsToUint(s.alpha); } CmdEndClip CmdEndClip_read(CmdEndClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; + uint raw0 = memory[ix + 0]; CmdEndClip s; s.alpha = uintBitsToFloat(raw0); return s; @@ -289,12 +289,12 @@ CmdEndClip CmdEndClip_read(CmdEndClipRef ref) { void CmdEndClip_write(CmdEndClipRef ref, CmdEndClip s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = floatBitsToUint(s.alpha); + memory[ix + 0] = floatBitsToUint(s.alpha); } CmdSolid CmdSolid_read(CmdSolidRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; + uint raw0 = memory[ix + 0]; CmdSolid s; s.rgba_color = raw0; return s; @@ -302,12 +302,12 @@ CmdSolid CmdSolid_read(CmdSolidRef ref) { void CmdSolid_write(CmdSolidRef ref, CmdSolid s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.rgba_color; + memory[ix + 0] = s.rgba_color; } CmdSolidMask CmdSolidMask_read(CmdSolidMaskRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; + uint raw0 = memory[ix + 0]; CmdSolidMask s; s.mask = uintBitsToFloat(raw0); return s; @@ -315,12 +315,12 @@ CmdSolidMask CmdSolidMask_read(CmdSolidMaskRef ref) { void CmdSolidMask_write(CmdSolidMaskRef ref, CmdSolidMask s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = floatBitsToUint(s.mask); + memory[ix + 0] = floatBitsToUint(s.mask); } CmdJump CmdJump_read(CmdJumpRef ref) { uint ix = ref.offset >> 2; - uint raw0 = ptcl[ix + 0]; + uint raw0 = memory[ix + 0]; CmdJump s; s.new_ref = raw0; return s; @@ -328,11 +328,11 @@ CmdJump CmdJump_read(CmdJumpRef ref) { void CmdJump_write(CmdJumpRef ref, CmdJump s) { uint ix = ref.offset >> 2; - ptcl[ix + 0] = s.new_ref; + memory[ix + 0] = s.new_ref; } uint Cmd_tag(CmdRef ref) { - return ptcl[ref.offset >> 2]; + return memory[ref.offset >> 2]; } CmdCircle Cmd_Circle_read(CmdRef ref) { @@ -376,56 +376,56 @@ CmdJump Cmd_Jump_read(CmdRef ref) { } void Cmd_End_write(CmdRef ref) { - ptcl[ref.offset >> 2] = Cmd_End; + memory[ref.offset >> 2] = Cmd_End; } void Cmd_Circle_write(CmdRef ref, CmdCircle s) { - ptcl[ref.offset >> 2] = Cmd_Circle; + memory[ref.offset >> 2] = Cmd_Circle; CmdCircle_write(CmdCircleRef(ref.offset + 4), s); } void Cmd_Line_write(CmdRef ref, CmdLine s) { - ptcl[ref.offset >> 2] = Cmd_Line; + memory[ref.offset >> 2] = Cmd_Line; CmdLine_write(CmdLineRef(ref.offset + 4), s); } void Cmd_Fill_write(CmdRef ref, CmdFill s) { - ptcl[ref.offset >> 2] = Cmd_Fill; + memory[ref.offset >> 2] = Cmd_Fill; CmdFill_write(CmdFillRef(ref.offset + 4), s); } void Cmd_BeginClip_write(CmdRef ref, CmdBeginClip s) { - ptcl[ref.offset >> 2] = Cmd_BeginClip; + memory[ref.offset >> 2] = Cmd_BeginClip; CmdBeginClip_write(CmdBeginClipRef(ref.offset + 4), s); } void Cmd_BeginSolidClip_write(CmdRef ref, CmdBeginSolidClip s) { - ptcl[ref.offset >> 2] = Cmd_BeginSolidClip; + memory[ref.offset >> 2] = Cmd_BeginSolidClip; CmdBeginSolidClip_write(CmdBeginSolidClipRef(ref.offset + 4), s); } void Cmd_EndClip_write(CmdRef ref, CmdEndClip s) { - ptcl[ref.offset >> 2] = Cmd_EndClip; + memory[ref.offset >> 2] = Cmd_EndClip; CmdEndClip_write(CmdEndClipRef(ref.offset + 4), s); } void Cmd_Stroke_write(CmdRef ref, CmdStroke s) { - ptcl[ref.offset >> 2] = Cmd_Stroke; + memory[ref.offset >> 2] = Cmd_Stroke; CmdStroke_write(CmdStrokeRef(ref.offset + 4), s); } void Cmd_Solid_write(CmdRef ref, CmdSolid s) { - ptcl[ref.offset >> 2] = Cmd_Solid; + memory[ref.offset >> 2] = Cmd_Solid; CmdSolid_write(CmdSolidRef(ref.offset + 4), s); } void Cmd_SolidMask_write(CmdRef ref, CmdSolidMask s) { - ptcl[ref.offset >> 2] = Cmd_SolidMask; + memory[ref.offset >> 2] = Cmd_SolidMask; CmdSolidMask_write(CmdSolidMaskRef(ref.offset + 4), s); } void Cmd_Jump_write(CmdRef ref, CmdJump s) { - ptcl[ref.offset >> 2] = Cmd_Jump; + memory[ref.offset >> 2] = Cmd_Jump; CmdJump_write(CmdJumpRef(ref.offset + 4), s); } diff --git a/piet-gpu/shader/setup.h b/piet-gpu/shader/setup.h index 6998a16f..9a7d580d 100644 --- a/piet-gpu/shader/setup.h +++ b/piet-gpu/shader/setup.h @@ -28,3 +28,13 @@ #define N_TILE (N_TILE_X * N_TILE_Y) #define LG_N_TILE (7 + LG_WG_FACTOR) #define N_SLICE (N_TILE / 32) + +struct Config { + uint n_elements; // paths + uint n_pathseg; + uint tile_base; + uint bin_base; + uint ptcl_base; + uint pathseg_base; + uint anno_base; +}; diff --git a/piet-gpu/shader/tile.h b/piet-gpu/shader/tile.h index a33cb5ab..133ff53e 100644 --- a/piet-gpu/shader/tile.h +++ b/piet-gpu/shader/tile.h @@ -51,9 +51,9 @@ TileSegRef TileSeg_index(TileSegRef ref, uint index) { Path Path_read(PathRef ref) { uint ix = ref.offset >> 2; - uint raw0 = tile[ix + 0]; - uint raw1 = tile[ix + 1]; - uint raw2 = tile[ix + 2]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; Path s; s.bbox = uvec4(raw0 & 0xffff, raw0 >> 16, raw1 & 0xffff, raw1 >> 16); s.tiles = TileRef(raw2); @@ -62,15 +62,15 @@ Path Path_read(PathRef ref) { void Path_write(PathRef ref, Path s) { uint ix = ref.offset >> 2; - tile[ix + 0] = s.bbox.x | (s.bbox.y << 16); - tile[ix + 1] = s.bbox.z | (s.bbox.w << 16); - tile[ix + 2] = s.tiles.offset; + memory[ix + 0] = s.bbox.x | (s.bbox.y << 16); + memory[ix + 1] = s.bbox.z | (s.bbox.w << 16); + memory[ix + 2] = s.tiles.offset; } Tile Tile_read(TileRef ref) { uint ix = ref.offset >> 2; - uint raw0 = tile[ix + 0]; - uint raw1 = tile[ix + 1]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; Tile s; s.tile = TileSegRef(raw0); s.backdrop = int(raw1); @@ -79,18 +79,18 @@ Tile Tile_read(TileRef ref) { void Tile_write(TileRef ref, Tile s) { uint ix = ref.offset >> 2; - tile[ix + 0] = s.tile.offset; - tile[ix + 1] = uint(s.backdrop); + memory[ix + 0] = s.tile.offset; + memory[ix + 1] = uint(s.backdrop); } TileSeg TileSeg_read(TileSegRef ref) { uint ix = ref.offset >> 2; - uint raw0 = tile[ix + 0]; - uint raw1 = tile[ix + 1]; - uint raw2 = tile[ix + 2]; - uint raw3 = tile[ix + 3]; - uint raw4 = tile[ix + 4]; - uint raw5 = tile[ix + 5]; + uint raw0 = memory[ix + 0]; + uint raw1 = memory[ix + 1]; + uint raw2 = memory[ix + 2]; + uint raw3 = memory[ix + 3]; + uint raw4 = memory[ix + 4]; + uint raw5 = memory[ix + 5]; TileSeg s; s.origin = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.vector = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -101,11 +101,11 @@ TileSeg TileSeg_read(TileSegRef ref) { void TileSeg_write(TileSegRef ref, TileSeg s) { uint ix = ref.offset >> 2; - tile[ix + 0] = floatBitsToUint(s.origin.x); - tile[ix + 1] = floatBitsToUint(s.origin.y); - tile[ix + 2] = floatBitsToUint(s.vector.x); - tile[ix + 3] = floatBitsToUint(s.vector.y); - tile[ix + 4] = floatBitsToUint(s.y_edge); - tile[ix + 5] = s.next.offset; + memory[ix + 0] = floatBitsToUint(s.origin.x); + memory[ix + 1] = floatBitsToUint(s.origin.y); + memory[ix + 2] = floatBitsToUint(s.vector.x); + memory[ix + 3] = floatBitsToUint(s.vector.y); + memory[ix + 4] = floatBitsToUint(s.y_edge); + memory[ix + 5] = s.next.offset; } diff --git a/piet-gpu/shader/tile_alloc.comp b/piet-gpu/shader/tile_alloc.comp index 64529d1c..3280f7ff 100644 --- a/piet-gpu/shader/tile_alloc.comp +++ b/piet-gpu/shader/tile_alloc.comp @@ -6,24 +6,15 @@ #extension GL_GOOGLE_include_directive : enable #include "setup.h" +#include "mem.h" #define LG_TILE_ALLOC_WG (7 + LG_WG_FACTOR) #define TILE_ALLOC_WG (1 << LG_TILE_ALLOC_WG) layout(local_size_x = TILE_ALLOC_WG, local_size_y = 1) in; -layout(set = 0, binding = 0) buffer AnnotatedBuf { - uint[] annotated; -}; - -layout(set = 0, binding = 1) buffer AllocBuf { - uint n_elements; - uint n_pathseg; - uint alloc; -}; - -layout(set = 0, binding = 2) buffer TileBuf { - uint[] tile; +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; }; #include "annotated.h" @@ -34,16 +25,20 @@ layout(set = 0, binding = 2) buffer TileBuf { #define SY (1.0 / float(TILE_HEIGHT_PX)) shared uint sh_tile_count[TILE_ALLOC_WG]; -shared uint sh_tile_alloc; +shared Alloc sh_tile_alloc; void main() { + if (mem_overflow) { + return; + } + uint th_ix = gl_LocalInvocationID.x; uint element_ix = gl_GlobalInvocationID.x; - PathRef path_ref = PathRef(element_ix * Path_size); - AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); + PathRef path_ref = PathRef(conf.tile_base + element_ix * Path_size); + AnnotatedRef ref = AnnotatedRef(conf.anno_base + element_ix * Annotated_size); uint tag = Annotated_Nop; - if (element_ix < n_elements) { + if (element_ix < conf.n_elements) { tag = Annotated_tag(ref); } int x0 = 0, y0 = 0, x1 = 0, y1 = 0; @@ -86,23 +81,26 @@ void main() { sh_tile_count[th_ix] = tile_count; } if (th_ix == TILE_ALLOC_WG - 1) { - sh_tile_alloc = atomicAdd(alloc, tile_count * Tile_size); + sh_tile_alloc = malloc(tile_count * Tile_size); } barrier(); - uint alloc_start = sh_tile_alloc; + Alloc alloc_start = sh_tile_alloc; + if (alloc_start.failed) { + return; + } - if (element_ix < n_elements) { + if (element_ix < conf.n_elements) { uint tile_subix = th_ix > 0 ? sh_tile_count[th_ix - 1] : 0; - path.tiles = TileRef(alloc_start + Tile_size * tile_subix); + path.tiles = TileRef(alloc_start.offset + Tile_size * tile_subix); Path_write(path_ref, path); } // Zero out allocated tiles efficiently uint total_count = sh_tile_count[TILE_ALLOC_WG - 1] * (Tile_size / 4); - uint start_ix = alloc_start >> 2; + uint start_ix = alloc_start.offset >> 2; for (uint i = th_ix; i < total_count; i += TILE_ALLOC_WG) { // Note: this interleaving is faster than using Tile_write // by a significant amount. - tile[start_ix + i] = 0; + memory[start_ix + i] = 0; } } diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv index e901bad135fde888836ccb20256c400a5444927e..e407222c21568be5ae6ac2b9eb7e4f161d424dcd 100644 GIT binary patch literal 10308 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 diff --git a/piet-gpu/src/lib.rs b/piet-gpu/src/lib.rs index 18688886..02726648 100644 --- a/piet-gpu/src/lib.rs +++ b/piet-gpu/src/lib.rs @@ -156,15 +156,16 @@ pub fn dump_k1_data(k1_buf: &[u32]) { pub struct Renderer { pub image_dev: hub::Image, // resulting image - scene_buf: hub::Buffer, - scene_dev: hub::Buffer, + scene_buf_host: hub::Buffer, + scene_buf_dev: hub::Buffer, - pub state_buf: hub::Buffer, - pub anno_buf: hub::Buffer, - pub pathseg_buf: hub::Buffer, - pub tile_buf: hub::Buffer, - pub bin_buf: hub::Buffer, - pub ptcl_buf: hub::Buffer, + memory_buf_host: hub::Buffer, + memory_buf_dev: hub::Buffer, + + state_buf: hub::Buffer, + + config_buf_host: hub::Buffer, + config_buf_dev: hub::Buffer, el_pipeline: hub::Pipeline, el_ds: hub::DescriptorSet, @@ -178,23 +179,12 @@ pub struct Renderer { backdrop_pipeline: hub::Pipeline, backdrop_ds: hub::DescriptorSet, - tile_alloc_buf_host: hub::Buffer, - tile_alloc_buf_dev: hub::Buffer, - bin_pipeline: hub::Pipeline, bin_ds: hub::DescriptorSet, - bin_alloc_buf_host: hub::Buffer, - bin_alloc_buf_dev: hub::Buffer, - coarse_pipeline: hub::Pipeline, coarse_ds: hub::DescriptorSet, - coarse_alloc_buf_host: hub::Buffer, - coarse_alloc_buf_dev: hub::Buffer, - - clip_scratch_buf: hub::Buffer, - k4_pipeline: hub::Pipeline, k4_ds: hub::DescriptorSet, @@ -221,88 +211,83 @@ impl Renderer { n_elements, n_paths, n_pathseg ); - let mut scene_buf = session + let mut scene_buf_host = session .create_buffer(std::mem::size_of_val(&scene[..]) as u64, host) .unwrap(); - let scene_dev = session + let scene_buf_dev = session .create_buffer(std::mem::size_of_val(&scene[..]) as u64, dev) .unwrap(); - scene_buf.write(&scene)?; + scene_buf_host.write(&scene)?; let state_buf = session.create_buffer(1 * 1024 * 1024, dev)?; - let anno_buf = session.create_buffer(64 * 1024 * 1024, dev)?; - let pathseg_buf = session.create_buffer(64 * 1024 * 1024, dev)?; - let tile_buf = session.create_buffer(64 * 1024 * 1024, dev)?; - let bin_buf = session.create_buffer(64 * 1024 * 1024, dev)?; - let ptcl_buf = session.create_buffer(48 * 1024 * 1024, dev)?; let image_dev = session.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; + let mut config_buf_host = session.create_buffer(7*4, host)?; + let config_buf_dev = session.create_buffer(7*4, dev)?; + + // TODO: constants + const PATH_SIZE: usize = 12; + const BIN_SIZE: usize = 8; + const PATHSEG_SIZE: usize = 48; + const ANNO_SIZE: usize = 28; + let mut alloc = 0; + let tile_base = alloc; + alloc += ((n_paths + 3) & !3) * PATH_SIZE; + let bin_base = alloc; + alloc += ((n_paths + 255) & !255) * BIN_SIZE; + let ptcl_base = alloc; + alloc += WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; + let pathseg_base = alloc; + 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, tile_base as u32, bin_base as u32, ptcl_base as u32, pathseg_base as u32, anno_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)?; + memory_buf_host.write(&[alloc as u32, 0 /* Overflow flag */])?; + let el_code = include_bytes!("../shader/elements.spv"); let el_pipeline = session.create_simple_compute_pipeline(el_code, 4)?; let el_ds = session.create_simple_descriptor_set( &el_pipeline, - &[&scene_dev, &state_buf, &anno_buf, &pathseg_buf], + &[&memory_buf_dev, &config_buf_dev, &scene_buf_dev, &state_buf], )?; - let mut tile_alloc_buf_host = session.create_buffer(12, host)?; - let tile_alloc_buf_dev = session.create_buffer(12, dev)?; - - // TODO: constants - const PATH_SIZE: usize = 12; - let tile_alloc_start = ((n_paths + 31) & !31) * PATH_SIZE; - tile_alloc_buf_host.write(&[n_paths as u32, n_pathseg as u32, tile_alloc_start as u32])?; let tile_alloc_code = include_bytes!("../shader/tile_alloc.spv"); - let tile_pipeline = session.create_simple_compute_pipeline(tile_alloc_code, 3)?; + let tile_pipeline = session.create_simple_compute_pipeline(tile_alloc_code, 2)?; let tile_ds = session.create_simple_descriptor_set( &tile_pipeline, - &[&anno_buf, &tile_alloc_buf_dev, &tile_buf], + &[&memory_buf_dev, &config_buf_dev], )?; let path_alloc_code = include_bytes!("../shader/path_coarse.spv"); - let path_pipeline = session.create_simple_compute_pipeline(path_alloc_code, 3)?; + let path_pipeline = session.create_simple_compute_pipeline(path_alloc_code, 2)?; let path_ds = session.create_simple_descriptor_set( &path_pipeline, - &[&pathseg_buf, &tile_alloc_buf_dev, &tile_buf], + &[&memory_buf_dev, &config_buf_dev], )?; let backdrop_alloc_code = include_bytes!("../shader/backdrop.spv"); - let backdrop_pipeline = session.create_simple_compute_pipeline(backdrop_alloc_code, 3)?; + let backdrop_pipeline = session.create_simple_compute_pipeline(backdrop_alloc_code, 2)?; let backdrop_ds = session.create_simple_descriptor_set( &backdrop_pipeline, - &[&anno_buf, &tile_alloc_buf_dev, &tile_buf], + &[&memory_buf_dev, &config_buf_dev], )?; - let mut bin_alloc_buf_host = session.create_buffer(8, host)?; - let bin_alloc_buf_dev = session.create_buffer(8, dev)?; - // TODO: constants - let bin_alloc_start = ((n_paths + 255) & !255) * 8; - bin_alloc_buf_host.write(&[n_paths as u32, bin_alloc_start as u32])?; let bin_code = include_bytes!("../shader/binning.spv"); - let bin_pipeline = session.create_simple_compute_pipeline(bin_code, 3)?; + let bin_pipeline = session.create_simple_compute_pipeline(bin_code, 2)?; let bin_ds = session.create_simple_descriptor_set( &bin_pipeline, - &[&anno_buf, &bin_alloc_buf_dev, &bin_buf], + &[&memory_buf_dev, &config_buf_dev], )?; - let clip_scratch_buf = session.create_buffer(1024 * 1024, dev)?; - - let mut coarse_alloc_buf_host = session.create_buffer(8, host)?; - let coarse_alloc_buf_dev = session.create_buffer(8, dev)?; - - let coarse_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; - coarse_alloc_buf_host.write(&[n_paths as u32, coarse_alloc_start as u32])?; let coarse_code = include_bytes!("../shader/coarse.spv"); - let coarse_pipeline = session.create_simple_compute_pipeline(coarse_code, 5)?; + let coarse_pipeline = session.create_simple_compute_pipeline(coarse_code, 2)?; let coarse_ds = session.create_simple_descriptor_set( &coarse_pipeline, - &[ - &anno_buf, - &bin_buf, - &tile_buf, - &coarse_alloc_buf_dev, - &ptcl_buf, - ], + &[&memory_buf_dev, &config_buf_dev], )?; let bg_image = Self::make_test_bg_image(&session); @@ -318,20 +303,25 @@ impl Renderer { let sampler = session.create_sampler(SamplerParams::Linear)?; let k4_pipeline = session .pipeline_builder() - .add_buffers(3) + .add_buffers(2) .add_images(1) .add_textures(max_textures) .create_compute_pipeline(&session, k4_code)?; let k4_ds = session .descriptor_set_builder() - .add_buffers(&[&ptcl_buf, &tile_buf, &clip_scratch_buf]) + .add_buffers(&[&memory_buf_dev, &config_buf_dev]) .add_images(&[&image_dev]) .add_textures(&[&bg_image], &sampler) .build(&session, &k4_pipeline)?; Ok(Renderer { - scene_buf, - scene_dev, + scene_buf_host, + scene_buf_dev, + memory_buf_host, + memory_buf_dev, + state_buf, + config_buf_host, + config_buf_dev, image_dev, el_pipeline, el_ds, @@ -347,19 +337,6 @@ impl Renderer { coarse_ds, k4_pipeline, k4_ds, - state_buf, - anno_buf, - pathseg_buf, - tile_buf, - bin_buf, - ptcl_buf, - tile_alloc_buf_host, - tile_alloc_buf_dev, - bin_alloc_buf_host, - bin_alloc_buf_dev, - coarse_alloc_buf_host, - coarse_alloc_buf_dev, - clip_scratch_buf, n_elements, n_paths, n_pathseg, @@ -368,21 +345,16 @@ impl Renderer { } pub unsafe fn record(&self, cmd_buf: &mut hub::CmdBuf, query_pool: &hub::QueryPool) { - cmd_buf.copy_buffer(self.scene_buf.vk_buffer(), self.scene_dev.vk_buffer()); - cmd_buf.copy_buffer( - self.tile_alloc_buf_host.vk_buffer(), - self.tile_alloc_buf_dev.vk_buffer(), - ); + cmd_buf.copy_buffer(self.scene_buf_host.vk_buffer(), self.scene_buf_dev.vk_buffer()); cmd_buf.copy_buffer( - self.bin_alloc_buf_host.vk_buffer(), - self.bin_alloc_buf_dev.vk_buffer(), + self.config_buf_host.vk_buffer(), + self.config_buf_dev.vk_buffer(), ); cmd_buf.copy_buffer( - self.coarse_alloc_buf_host.vk_buffer(), - self.coarse_alloc_buf_dev.vk_buffer(), + self.memory_buf_host.vk_buffer(), + self.memory_buf_dev.vk_buffer(), ); cmd_buf.clear_buffer(self.state_buf.vk_buffer(), None); - cmd_buf.clear_buffer(self.clip_scratch_buf.vk_buffer(), Some(4)); cmd_buf.memory_barrier(); cmd_buf.image_barrier( self.image_dev.vk_image(),