From 6a4e26ef2ac6eae52f392d44a65e0e24b31cf0ca Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 24 Dec 2020 12:00:53 +0100 Subject: [PATCH] all: add optional memory checks Defining MEM_DEBUG in mem.h will add a size field to Alloc and enable bounds and alignment checks for every memory read and write. Notes: - Deriving an Alloc from Path.tiles is unsound, but it's more trouble to convert Path.tiles from TileRef to a variable sized Alloc. - elements.comp note that "We should be able to use an array of structs but the NV shader compiler doesn't seem to like it". If that's still relevant, does the shared arrays of Allocs work? Signed-off-by: Elias Naur --- piet-gpu-derive/src/glsl.rs | 160 +++++++++++++++------ piet-gpu/shader/annotated.h | 120 ++++++++-------- piet-gpu/shader/backdrop.comp | 26 ++-- piet-gpu/shader/backdrop.spv | Bin 7980 -> 11040 bytes piet-gpu/shader/binning.comp | 32 +++-- piet-gpu/shader/binning.spv | Bin 12232 -> 15720 bytes piet-gpu/shader/bins.h | 8 +- piet-gpu/shader/coarse.comp | 108 ++++++++------ piet-gpu/shader/coarse.spv | Bin 40060 -> 52300 bytes piet-gpu/shader/elements.comp | 38 ++--- piet-gpu/shader/elements.spv | Bin 59452 -> 66568 bytes piet-gpu/shader/kernel4.comp | 62 +++++---- piet-gpu/shader/kernel4.spv | Bin 34252 -> 41884 bytes piet-gpu/shader/mem.h | 111 +++++++++++++-- piet-gpu/shader/path_coarse.comp | 30 ++-- piet-gpu/shader/path_coarse.spv | Bin 32368 -> 37768 bytes piet-gpu/shader/pathseg.h | 192 ++++++++++++------------- piet-gpu/shader/ptcl.h | 232 +++++++++++++++---------------- piet-gpu/shader/setup.h | 10 +- piet-gpu/shader/tile.h | 56 ++++---- piet-gpu/shader/tile_alloc.comp | 32 +++-- piet-gpu/shader/tile_alloc.spv | Bin 10620 -> 14668 bytes 22 files changed, 710 insertions(+), 507 deletions(-) diff --git a/piet-gpu-derive/src/glsl.rs b/piet-gpu-derive/src/glsl.rs index 2409637..ce862b0 100644 --- a/piet-gpu-derive/src/glsl.rs +++ b/piet-gpu-derive/src/glsl.rs @@ -31,22 +31,18 @@ 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; - } + let is_mem = !module.name.eq(&"state") && !module.name.eq(&"scene"); match def { (_size, LayoutTypeDef::Struct(fields)) => { - gen_struct_read(&mut r, buf_name, &name, fields); + gen_struct_read(&mut r, &module.name, &name, is_mem, fields); if module.gpu_write { - gen_struct_write(&mut r, buf_name, &name, fields); + gen_struct_write(&mut r, &module.name, &name, is_mem, fields); } } (_size, LayoutTypeDef::Enum(en)) => { - gen_enum_read(&mut r, buf_name, &name, en); + gen_enum_read(&mut r, &module.name, &name, is_mem, en); if module.gpu_write { - gen_enum_write(&mut r, buf_name, &name, en); + gen_enum_write(&mut r, &module.name, &name, is_mem, en); } } } @@ -96,14 +92,23 @@ fn gen_struct_read( r: &mut String, bufname: &str, name: &str, + is_mem: bool, fields: &[(String, usize, LayoutType)], ) { - writeln!(r, "{} {}_read({}Ref ref) {{", name, name, name).unwrap(); + write!(r, "{} {}_read(", name, name).unwrap(); + if is_mem { + write!(r, "Alloc a, ").unwrap(); + } + writeln!(r, "{}Ref ref) {{", name).unwrap(); writeln!(r, " uint ix = ref.offset >> 2;").unwrap(); let coverage = crate::layout::struct_coverage(fields, false); for (i, fields) in coverage.iter().enumerate() { if !fields.is_empty() { - writeln!(r, " uint raw{} = {}[ix + {}];", i, bufname, i).unwrap(); + if is_mem { + writeln!(r, " uint raw{} = read_mem(a, ix + {});", i, i).unwrap(); + } else { + writeln!(r, " uint raw{} = {}[ix + {}];", i, bufname, i).unwrap(); + } } } writeln!(r, " {} s;", name).unwrap(); @@ -130,26 +135,47 @@ fn gen_enum_read( r: &mut String, bufname: &str, name: &str, + is_mem: bool, variants: &[(String, Vec<(usize, LayoutType)>)], ) { - writeln!(r, "uint {}_tag({}Ref ref) {{", name, name).unwrap(); - writeln!(r, " return {}[ref.offset >> 2];", bufname).unwrap(); + if is_mem { + writeln!(r, "uint {}_tag(Alloc a, {}Ref ref) {{", name, name).unwrap(); + writeln!(r, " return read_mem(a, ref.offset >> 2);").unwrap(); + } else { + writeln!(r, "uint {}_tag({}Ref ref) {{", name, name).unwrap(); + writeln!(r, " return {}[ref.offset >> 2];", bufname).unwrap(); + } writeln!(r, "}}\n").unwrap(); for (var_name, payload) in variants { if payload.len() == 1 { if let GpuType::InlineStruct(structname) = &payload[0].1.ty { - writeln!( - r, - "{} {}_{}_read({}Ref ref) {{", - structname, name, var_name, name - ) - .unwrap(); - writeln!( - r, - " return {}_read({}Ref(ref.offset + {}));", - structname, structname, payload[0].0 - ) - .unwrap(); + if is_mem { + writeln!( + r, + "{} {}_{}_read(Alloc a, {}Ref ref) {{", + structname, name, var_name, name + ) + .unwrap(); + writeln!( + r, + " return {}_read(a, {}Ref(ref.offset + {}));", + structname, structname, payload[0].0 + ) + .unwrap(); + } else { + writeln!( + r, + "{} {}_{}_read({}Ref ref) {{", + structname, name, var_name, name + ) + .unwrap(); + writeln!( + r, + " return {}_read({}Ref(ref.offset + {}));", + structname, structname, payload[0].0 + ) + .unwrap(); + } writeln!(r, "}}\n").unwrap(); } } @@ -303,9 +329,14 @@ fn gen_struct_write( r: &mut String, bufname: &str, name: &str, + is_mem: bool, fields: &[(String, usize, LayoutType)], ) { - writeln!(r, "void {}_write({}Ref ref, {} s) {{", name, name, name).unwrap(); + write!(r, "void {}_write(", name).unwrap(); + if is_mem { + write!(r, "Alloc a, ").unwrap(); + } + writeln!(r, "{}Ref ref, {} s) {{", name, name).unwrap(); writeln!(r, " uint ix = ref.offset >> 2;").unwrap(); let coverage = crate::layout::struct_coverage(fields, true); @@ -381,13 +412,20 @@ fn gen_struct_write( } if !pieces.is_empty() { - write!(r, " {}[ix + {}] = ", bufname, i).unwrap(); + if is_mem { + write!(r, " write_mem(a, ix + {}, ", i).unwrap(); + } else { + write!(r, " {}[ix + {}] = ", bufname, i).unwrap(); + } for (j, piece) in pieces.iter().enumerate() { if j != 0 { write!(r, " | ").unwrap(); } write!(r, "{}", piece).unwrap(); } + if is_mem { + write!(r, ")").unwrap(); + } writeln!(r, ";").unwrap(); } } @@ -429,38 +467,70 @@ fn gen_enum_write( r: &mut String, bufname: &str, name: &str, + is_mem: bool, variants: &[(String, Vec<(usize, LayoutType)>)], ) { for (var_name, payload) in variants { if payload.is_empty() { - writeln!(r, "void {}_{}_write({}Ref ref) {{", name, var_name, name).unwrap(); - writeln!( - r, - " {}[ref.offset >> 2] = {}_{};", - bufname, name, var_name - ) - .unwrap(); - writeln!(r, "}}\n").unwrap(); - } else if payload.len() == 1 { - if let GpuType::InlineStruct(structname) = &payload[0].1.ty { + if is_mem { + writeln!(r, "void {}_{}_write(Alloc a, {}Ref ref) {{", name, var_name, name).unwrap(); writeln!( r, - "void {}_{}_write({}Ref ref, {} s) {{", - name, var_name, name, structname + " write_mem(a, ref.offset >> 2, {}_{});", + name, var_name ) .unwrap(); + } else { + writeln!(r, "void {}_{}_write({}Ref ref) {{", name, var_name, name).unwrap(); writeln!( r, " {}[ref.offset >> 2] = {}_{};", bufname, name, var_name ) .unwrap(); - writeln!( - r, - " {}_write({}Ref(ref.offset + {}), s);", - structname, structname, payload[0].0 - ) - .unwrap(); + } + writeln!(r, "}}\n").unwrap(); + } else if payload.len() == 1 { + if let GpuType::InlineStruct(structname) = &payload[0].1.ty { + if is_mem { + writeln!( + r, + "void {}_{}_write(Alloc a, {}Ref ref, {} s) {{", + name, var_name, name, structname + ) + .unwrap(); + writeln!( + r, + " write_mem(a, ref.offset >> 2, {}_{});", + name, var_name + ) + .unwrap(); + writeln!( + r, + " {}_write(a, {}Ref(ref.offset + {}), s);", + structname, structname, payload[0].0 + ) + .unwrap(); + } else { + writeln!( + r, + "void {}_{}_write(Alloc a, {}Ref ref, {} s) {{", + name, var_name, name, structname + ) + .unwrap(); + writeln!( + r, + " {}[ref.offset >> 2] = {}_{};", + bufname, name, var_name + ) + .unwrap(); + writeln!( + r, + " {}_write({}Ref(ref.offset + {}), s);", + structname, structname, payload[0].0 + ) + .unwrap(); + } writeln!(r, "}}\n").unwrap(); } } diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index 8a757ef..291496f 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -62,36 +62,36 @@ AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { return AnnotatedRef(ref.offset + index * Annotated_size); } -AnnoFill AnnoFill_read(AnnoFillRef ref) { +AnnoFill AnnoFill_read(Alloc a, AnnoFillRef ref) { uint ix = ref.offset >> 2; - 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 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); AnnoFill s; s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.rgba_color = raw4; return s; } -void AnnoFill_write(AnnoFillRef ref, AnnoFill s) { +void AnnoFill_write(Alloc a, AnnoFillRef ref, AnnoFill s) { uint ix = ref.offset >> 2; - 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; + write_mem(a, ix + 0, floatBitsToUint(s.bbox.x)); + write_mem(a, ix + 1, floatBitsToUint(s.bbox.y)); + write_mem(a, ix + 2, floatBitsToUint(s.bbox.z)); + write_mem(a, ix + 3, floatBitsToUint(s.bbox.w)); + write_mem(a, ix + 4, s.rgba_color); } -AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { +AnnoStroke AnnoStroke_read(Alloc a, AnnoStrokeRef ref) { uint ix = ref.offset >> 2; - 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 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); AnnoStroke s; s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); s.rgba_color = raw4; @@ -99,76 +99,76 @@ AnnoStroke AnnoStroke_read(AnnoStrokeRef ref) { return s; } -void AnnoStroke_write(AnnoStrokeRef ref, AnnoStroke s) { +void AnnoStroke_write(Alloc a, AnnoStrokeRef ref, AnnoStroke s) { uint ix = ref.offset >> 2; - 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); + write_mem(a, ix + 0, floatBitsToUint(s.bbox.x)); + write_mem(a, ix + 1, floatBitsToUint(s.bbox.y)); + write_mem(a, ix + 2, floatBitsToUint(s.bbox.z)); + write_mem(a, ix + 3, floatBitsToUint(s.bbox.w)); + write_mem(a, ix + 4, s.rgba_color); + write_mem(a, ix + 5, floatBitsToUint(s.linewidth)); } -AnnoClip AnnoClip_read(AnnoClipRef ref) { +AnnoClip AnnoClip_read(Alloc a, AnnoClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; - uint raw2 = memory[ix + 2]; - uint raw3 = memory[ix + 3]; + 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); AnnoClip s; s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); return s; } -void AnnoClip_write(AnnoClipRef ref, AnnoClip s) { +void AnnoClip_write(Alloc a, AnnoClipRef ref, AnnoClip s) { uint ix = ref.offset >> 2; - 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); + write_mem(a, ix + 0, floatBitsToUint(s.bbox.x)); + write_mem(a, ix + 1, floatBitsToUint(s.bbox.y)); + write_mem(a, ix + 2, floatBitsToUint(s.bbox.z)); + write_mem(a, ix + 3, floatBitsToUint(s.bbox.w)); } -uint Annotated_tag(AnnotatedRef ref) { - return memory[ref.offset >> 2]; +uint Annotated_tag(Alloc a, AnnotatedRef ref) { + return read_mem(a, ref.offset >> 2); } -AnnoStroke Annotated_Stroke_read(AnnotatedRef ref) { - return AnnoStroke_read(AnnoStrokeRef(ref.offset + 4)); +AnnoStroke Annotated_Stroke_read(Alloc a, AnnotatedRef ref) { + return AnnoStroke_read(a, AnnoStrokeRef(ref.offset + 4)); } -AnnoFill Annotated_Fill_read(AnnotatedRef ref) { - return AnnoFill_read(AnnoFillRef(ref.offset + 4)); +AnnoFill Annotated_Fill_read(Alloc a, AnnotatedRef ref) { + return AnnoFill_read(a, AnnoFillRef(ref.offset + 4)); } -AnnoClip Annotated_BeginClip_read(AnnotatedRef ref) { - return AnnoClip_read(AnnoClipRef(ref.offset + 4)); +AnnoClip Annotated_BeginClip_read(Alloc a, AnnotatedRef ref) { + return AnnoClip_read(a, AnnoClipRef(ref.offset + 4)); } -AnnoClip Annotated_EndClip_read(AnnotatedRef ref) { - return AnnoClip_read(AnnoClipRef(ref.offset + 4)); +AnnoClip Annotated_EndClip_read(Alloc a, AnnotatedRef ref) { + return AnnoClip_read(a, AnnoClipRef(ref.offset + 4)); } -void Annotated_Nop_write(AnnotatedRef ref) { - memory[ref.offset >> 2] = Annotated_Nop; +void Annotated_Nop_write(Alloc a, AnnotatedRef ref) { + write_mem(a, ref.offset >> 2, Annotated_Nop); } -void Annotated_Stroke_write(AnnotatedRef ref, AnnoStroke s) { - memory[ref.offset >> 2] = Annotated_Stroke; - AnnoStroke_write(AnnoStrokeRef(ref.offset + 4), s); +void Annotated_Stroke_write(Alloc a, AnnotatedRef ref, AnnoStroke s) { + write_mem(a, ref.offset >> 2, Annotated_Stroke); + AnnoStroke_write(a, AnnoStrokeRef(ref.offset + 4), s); } -void Annotated_Fill_write(AnnotatedRef ref, AnnoFill s) { - memory[ref.offset >> 2] = Annotated_Fill; - AnnoFill_write(AnnoFillRef(ref.offset + 4), s); +void Annotated_Fill_write(Alloc a, AnnotatedRef ref, AnnoFill s) { + write_mem(a, ref.offset >> 2, Annotated_Fill); + AnnoFill_write(a, AnnoFillRef(ref.offset + 4), s); } -void Annotated_BeginClip_write(AnnotatedRef ref, AnnoClip s) { - memory[ref.offset >> 2] = Annotated_BeginClip; - AnnoClip_write(AnnoClipRef(ref.offset + 4), s); +void Annotated_BeginClip_write(Alloc a, AnnotatedRef ref, AnnoClip s) { + write_mem(a, ref.offset >> 2, Annotated_BeginClip); + AnnoClip_write(a, AnnoClipRef(ref.offset + 4), s); } -void Annotated_EndClip_write(AnnotatedRef ref, AnnoClip s) { - memory[ref.offset >> 2] = Annotated_EndClip; - AnnoClip_write(AnnoClipRef(ref.offset + 4), s); +void Annotated_EndClip_write(Alloc a, AnnotatedRef ref, AnnoClip s) { + write_mem(a, ref.offset >> 2, Annotated_EndClip); + AnnoClip_write(a, AnnoClipRef(ref.offset + 4), s); } diff --git a/piet-gpu/shader/backdrop.comp b/piet-gpu/shader/backdrop.comp index 6828ac1..85e54e8 100644 --- a/piet-gpu/shader/backdrop.comp +++ b/piet-gpu/shader/backdrop.comp @@ -15,8 +15,8 @@ #version 450 #extension GL_GOOGLE_include_directive : enable -#include "setup.h" #include "mem.h" +#include "setup.h" #define LG_BACKDROP_WG (7 + LG_WG_FACTOR) #define BACKDROP_WG (1 << LG_BACKDROP_WG) @@ -31,27 +31,27 @@ layout(set = 0, binding = 1) readonly buffer ConfigBuf { #include "tile.h" shared uint sh_row_count[BACKDROP_WG]; -shared uint sh_row_base[BACKDROP_WG]; +shared Alloc sh_row_alloc[BACKDROP_WG]; shared uint sh_row_width[BACKDROP_WG]; void main() { - if (mem_overflow) { + if (mem_error != NO_ERROR) { return; } uint th_ix = gl_LocalInvocationID.x; uint element_ix = gl_GlobalInvocationID.x; - AnnotatedRef ref = AnnotatedRef(conf.anno_base + element_ix * Annotated_size); + AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); // Work assignment: 1 thread : 1 path element uint row_count = 0; if (element_ix < conf.n_elements) { - uint tag = Annotated_tag(ref); + uint tag = Annotated_tag(conf.anno_alloc, ref); switch (tag) { case Annotated_Fill: case Annotated_BeginClip: - PathRef path_ref = PathRef(conf.tile_base + element_ix * Path_size); - Path path = Path_read(path_ref); + PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size); + Path path = Path_read(conf.tile_alloc, path_ref); sh_row_width[th_ix] = path.bbox.z - path.bbox.x; row_count = path.bbox.w - path.bbox.y; // Paths that don't cross tile top edges don't have backdrops. @@ -62,7 +62,8 @@ void main() { // long as it doesn't cross the left edge. row_count = 0; } - sh_row_base[th_ix] = (path.tiles.offset >> 2) + 1; + Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size); + sh_row_alloc[th_ix] = path_alloc; } } @@ -92,13 +93,14 @@ void main() { if (width > 0) { // Process one row sequentially // Read backdrop value per tile and prefix sum it + Alloc tiles_alloc = sh_row_alloc[el_ix]; uint seq_ix = row - (el_ix > 0 ? sh_row_count[el_ix - 1] : 0); - uint tile_el_ix = sh_row_base[el_ix] + seq_ix * 2 * width; - uint sum = memory[tile_el_ix]; + uint tile_el_ix = (tiles_alloc.offset >> 2) + 1 + seq_ix * 2 * width; + uint sum = read_mem(tiles_alloc, tile_el_ix); for (uint x = 1; x < width; x++) { tile_el_ix += 2; - sum += memory[tile_el_ix]; - memory[tile_el_ix] = sum; + sum += read_mem(tiles_alloc, tile_el_ix); + write_mem(tiles_alloc, tile_el_ix, sum); } } } diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv index dc1cbc476990d468c314ef76f181347ec746fd55..81d26c0a2f0d8ca51e177557603c4d52f00282ca 100644 GIT binary patch literal 11040 zcmZ{o2b^406^CEg*+PgA2vs0%C=v(>gr*UQ1SFft0-=al$I0#_8J*opc4rf+bOf;= z3L=PZ1SJY$?+vhE?_yW%y?3#F-`jhVyW@`s=Xd`9bIv{Y+;i`J^Rivr?cZAzGm4o- zU-8BnMfvPpY=@;Nb|`vEJurCI;G*&N@S>xRKHPvEi>@-xn4OB=q8nX7Mrw^w9k(M} zklBpazB6JXYm5#xCx-KYZX)s;W$0ml_3(E-?PApED%;WmY{N=bx zu?u=z4LcNj!8eUH+Vw}wGgXwicJIiw$Fy8s#h&nuwPso0T<}=EHvEWb@tboWbY9=S z@NM<=6#ejJqob{Mtz92Jv%Use_EWphw%FOlf$*)d)pl*||Mw*tm1QjZMJ-*${*137 z;selQOlOS;fmhYq>zv-bl>2O(yHXqiKfBSaM~vJjk68dWk9PCOeO9k-ou>|)=d?|( zadbB){xG!Tsy6fy*OV%pS`Op7iiM1e^(+EUt*5J4ywzSQjsvf+jnzibu61b#Uk*RD zKGGUH-}-tAxeuqV8fy!?b!)GWjkU(WuE%)q8}>4Xb*+H=INxdLeEwC_cvo>Qd}E4T zJ(wJI=Hkt_{|4FPyYR>Py&GJ<8qn^E+$Z+W{=6A}QfqWgW9`&F%Y8Uh%xAwYvW9>SY5YEU~!Dsl?i*VaK-Pl(X z9q)&T@4>#lJ=6?G%+afTs;Y2pt+@|hjawfM`+5(ysWqQCu?B2!wD}Ct3$?v`tANKb z??aEZHdTjO6QjJwOaHxS-ZtP$aUVE8&ks!F-NolhUf$)DzfwGyY^XBF*Gs)1+hI(Yq9YqdXU#@67m7}wH(mq#cq z#+VUoc4s;h6rWlTBvUL?#88b0b=DZr5ul<^7ysNklK6M8x#ZA+EmEz_Oep?5B zZ3o|+@d0W*nRR(iJ=-g&W;xo&-x!~;F|4%Zao!{2JhKDz)Unm zog?hd+n;RCF%N94Hsf7$#OpKupkyS1G?&^lLNsq38vO zwV1~q`GeSx%s>_-ek9oEVDh=nCCI+uxFd7PwH%xCDB7)U0BsNS9h>-yWQ*J@6E}V^ zVRPtLpM&@Wp8!W~`Y%SD!|_+3?S*TK_^ZMH{qGcaZPz01m+{x5jkhOJ&kbPn>%S4L z-+tNK&1mQJOzD3e+F0$bPkwu{>Axdk$Ao`Nvbi_jWqorwx!w_RF8Vvgara<1-Z|ca zcKzC%L+;(s7HfJNxG!<@zYFnhI{tpNvCb3mp9gz>qK97w&&v4s!7h9sq8pWev?E#$NNnZ+<3o9g6sF&Be>e{j-315 zk#oNza_+Z8&i#(ax!)2w_gf<8eoy4QmT~Lzdm{JyEs^sL9o%n=-0ycqaP#?X5nS!J zMb7=U2yVRJ7s1tjUj*0h_eF5E-xoRGocJlFH z&5t6H-?h8WyU`y@zBAIAKAvo-vd^B2PayXp*5z8{#+joZ{Yk{N>U$vZCCqEzK7|~J zI9{JI_oB5&uRjge=6XJh_Ia2+(=PY@^%=x>m)fx;Ob zf+H?Jw?D(K@A7nBehzl5cGvU^^e+*AN1fZ6Gnzo4l16bResBb6q9}#Wl^vwMk?c*KN_a{V~=S&>+`~@6!=k@#* zyS}?q{=b2>orC;?9Fa@?7k0rK{*E}8=h%4Xv6g?LeXPZpf0THt7ICaEavN`*>ZtQy z;0KY;J^nW~eG&JcEY5tY ztoFE@Jz(e77dF3zwENu^bIb%=%MFNYj&+^N0b zej6HRez`GXzi-?>bMFmyF7;ojbzk&c#7F;Ql3yKj_k+#x)^vX7fwkFV&!K!D#9ZbU zKZM=)hq50$a_tYE#1{MjxVb+@?jx`t2zH;eUqp@`@MFQ+?@99?1U8R$@5I4qA8XM! zAJJwl;+X3Y@Fa1;7r^tm4~09o_LzGiSbLnqMPT!2kKP{!w)grX_u*je!54$wulNl) z0iA2i9gk z;tXla&yf4wSzq+z3HY4fe~-i*$AYz+&u?sb_>Kp=9({gq%bjZ}+FI3~*Avjo5Fh;~ zCcipzodhm(5bybU65RRX{GJRpM!RQ5{zSyw&Ly^vd08f?E~okL*1 zSF}gWFj%|w)zI?r)xp~0_kImnn`g-$tONVl1AS`|ZLUKcd7cZ7I_7|%hs;K_8{_Xx zGuf@90hY)3QE zZ31hz)(vR6d*Hmz;auU1->dMcBhUF@$H)8^fL*J0$B&@3xqrQ^=OVDZ$$$GVhHJl{ z7~c`k2Wwjij&pMfTzmdqaw**W@x5^wSew6dQQzfo_djB;02`-0a$gBHzdmce5UtG| z?&}M{)~xSpv^?@(3pQTgHE4PKd+LSYxr}#=K4Y#zYZqUKc0Km}MPU1`_C59z^h*&R z{nsbII_7>E*c`!c0K3+Bue}^>jCRkF{Kbg5ol6{JUjcR<(UTj&+U%vZ>eFVtJ$WTK zdU6w39=Toxj-K2MmPb!+0oxPD=!-G8g6&Pr|7x%{C)0no-VWXiZ9d28xA*RY{ZdDsJHYln>U}*}TfEowY0LN4zRTnN zqLwd1n_nII-vB-iiTrnhwSAxZtX-cr$1g=Y{sgptt9d-)-xA#?zmg*XKC7=h}6hfVejE>)S#vv@b(WK>V#a3GL%u)P5qO%^Jn=cQ$P?=aa$C z8T=Hm>+;`IgXmL{6^QRjW35e_ars&Bz3RR5*&B)8?US%Q*%z^v$DpHMr-Ac+t?J`phGb{+$W7e~!@?{W}Y+&;C6H?PLG6pN(jK94!t>AvAlCR;bo6yK zIPdFl;&I2SaK{~qL=DdYYmeW_8d#fqZ;W?u2yt%L;2d(X`HheFa2@P_ z`7Q$I^F1G~?cx;Ue3v3IpL-xTm;E>#L6RrvV*Aq&0M~HwVjB)oS%-xmoFddSxRfY)8(E>l-%?SDG2j zM9!MTmYU^F3x=DGk-p}_G{M4=#fw)M-$vD0)*z+(9su4@Zfo?Gt>4NtPGZ-COataemTFT(%4w7Z#B;P^m&i?@;vS_Qzwu3aP$C`+oPkv zjq=ufezXPHi&43MY0F;HvfHwifY$livuA@jovDZ&m(SOxYH3})uUuVQ+k$Ei)N5GFYiK^F^+49$C{IlW%nIIyYJn|yHKYNaA8h1oNC zds8b7(f#2H;jm+K-o9)d=(p@zskp50`SxtC3~bs=dEtzF&amO7-tw?D1@Al7OLRf+ zg0gr1M=tg~O zsjog#<2B0tj8n$j18&b=3?3>s%7eMDBiokS`J5K{+OsPP4wbKYO>W1U&g?4eVJ4ex z@^-f3IFw_qNKd0o8Yb6n(st6`65(J$vt|eyWz0F?b&W{r5dx=mfefp zTHBu82OetFd%YK9_n@OF?GA=vxN<=;a~;_q6FWhM3z} z7q&4z_DHTR$L;z3rakh$?U84+C(8i#$lgukl%BJj)vg!MAjVp|5TAK?th2@W%a!G} zTsN`f3!l8P{$51vB(QOE2NOHF@YyqC?U}Lj@cH|oU(UQ!(Q@XPOKi;4ykeeQr^t5^ zZ-1=cd0A1|o-xie3*3cp{OoA_+Rlmou)PN#^UX!4=kCTf);*0^AA|UW?FT>d>tBwz z#(XEE&F7gS{#5W2Pwd-&HMZwBel6N~dlL1X1vbC_bI|&YpUy7lqwVo?(C+Ph8uwhZ zYvk>Vw(m*gd47Ri6aLEXq1Uom;5C6gC z9fbc9u(J~W%fY^TVP6UMAajU%J%?qlgLH zZ*p4C&Dd%Ew`fyO)PEN?*SpugAG{4YhDyB;-$mal_dNACM%fo7w(K{MK_IE1v`x}+oU4^}hu6C3AT*U8$@779s>^z)@$X`c{@6|I3 z&iDNxZsl2MpGS$;_e@02yAtnVD8+K_<2zPDeDBN=`@8@we+oIhSM7-J1NS0nd_T6l z@ny93Dk}CIFG763e1CkeVvj0VU)Y0S*Iq_lp0$q1)sSrkHw1Q_z73qw{#}5`ySFo@ zEq1=r8u@2PoXdip5Z!02aU&ZFJu9qOzV=d0I z_DRJ#E(T9PkIob zaN6@%V#{5QM9*IZcAY+ZegOK_h`hCLD{OP>do3cT&v#PWbNl=9I%GN`uitlAF6wzB z*m?6DbJ1@?jPZV!BN6vzu)c_U3pnD^JAW&@zDtX~zYXkKdFT0R^xF}?zwT{K+Qvoh zcYq`JHQ=b>onUw?5LnK>JwlGirG9{!vxW~N+Yx8Xc=xfEkD`67 z#h8yEa@Hb_^F{9PspGuYg3~*{4%@oVLcI5gyB@4>Cv|tCKZeN7Mq*ZPD0t5s_xo}1 z4#a(|N832h>bqpEYVCNB)v@meiLGNd+V@6$Z-L$OH(=w`*7Q5{?-3vUe?aS3M-6`jnLX|$`>yX{M9wpdBhRB?_m6wfwzmKyyD0^5p15g+XJxW(z~66 z?cM5&b(6vJp4q=EwC$n$x`%s(&ze%7F_C8q*!8jhRIq22cfEhZ$VL2gaC#pzuy>GO zK4K06%ZGh1*gWy?_(Q;Q%UKsOGqJs=h?#|LoP5mAY_PfYS(|@9$wiL2U^)N2(@m~< z;F;*zh`xwB6s*r2{>`N=_U|qGZm+t*_EGJ3Wj^{zh>!j!7k+i*JsfO~u#W(Hrufb* zz&1wSxzIigF}Hh(W9^Y(&k|=p3M^;O;_Pw}e>6Bf`!U#Yc6|}I5UelkMPPdn`y30F zGcNig7xBk|-6!nDVAn^_mw;nmW6dFFoPD#0o>d(=jtASfsNn>#Tzn(+$))#V&$Q!R zRcqfvf6T9r{3qh`-HrT9vE{BTdZ|y&_3<0$?~i{QFGu`;9i7+{5TEhLGR`#)y+7i9 z6VZO7=A!2n{2X-DaA?8rEOK>W%Q>6TyTicIyDo67oe$RcIQxjBrYYd*h|g5SnkJ#A zA=Wel9slp;aPZi~K5CSG+$ei#VxJ6NS=e#LBe2~kJ>vpw`8eZ|U^&m|99V-~oa<^r)@NROs~x^mz@FibVm3|#%bkiu%qp-k(c9C(a;uSu zIRk9Xk>^aXT$<;pMNH)B!Im@61}0=OdJS?K;vVKO#(Q6jej4H|c<-J?+xVzq9oX~v i{{;?2pN+`5{v7mr#P#uRYa8Qw=XpbpTeB{9*8c+> 2) + (my_partition * N_TILE + gl_LocalInvocationID.x) * 2; - memory[out_ix] = element_count; - memory[out_ix + 1] = chunk_alloc.offset; + uint out_ix = (conf.bin_alloc.offset >> 2) + (my_partition * N_TILE + gl_LocalInvocationID.x) * 2; + write_mem(conf.bin_alloc, out_ix, element_count); + write_mem(conf.bin_alloc, out_ix + 1, chunk_alloc.offset); barrier(); if (sh_alloc_failed) { @@ -137,8 +138,9 @@ void main() { if (my_slice > 0) { idx += count[my_slice - 1][bin_ix]; } - uint out_offset = sh_chunk_start[bin_ix] + idx * BinInstance_size; - BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix)); + Alloc out_alloc = sh_chunk_alloc[bin_ix]; + uint out_offset = out_alloc.offset + idx * BinInstance_size; + BinInstance_write(out_alloc, BinInstanceRef(out_offset), BinInstance(element_ix)); } x++; if (x == x1) { diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 7974dac05e70575047571fb057628c0f020a5ae7..a9a05f5db3e27349173b482ec0610889d76d1d15 100644 GIT binary patch literal 15720 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 z zdIO#M*n@9^?W;aw{R^z#`*!#-jJytQj`t;`I^Ty`;KAT@JkF!PPd1>}7QZ7kg4Ow0 z=MZEQ+`pGO2ga7`XAH+b1Z+NS`QOnJ+a_T5qc;DhCZ9o6L^h278HY5s{j^8SO~J;D z@offI=Qk(Dv<3X%jcGV~Z84@1V0At*rjc;}rWa$9>t~(*ZO?q+yEWMTn(l#7=+x`) z-~Qw=u5G}M%lO99Uj8=3J|2zDz3D#Q4(#JTR=+Jxo%>iEe}7YFf6v|>z;lcFy#y%_ z+Zgb?!geWA?tUMOG^WyT&yL9PG#~8~3cE65?F2SP@SWlF;c?&D1+I^J>z9wC8QWaq z=sOW?toU9`f~(W+ewqyK#2)*73S1xc5o0&7 z|KNMU^@;Dt-r!N#_0tx9)4ECqaVcQ3+F8IFSD~S>J)ah`2)W08hULU`Ii@@rQ=ilw+aaJq_t4q&{ zC2;kT<0!B-#{OOkR_8Z5>OLB7KW*kY5~(g?90OM8-z#U*?^v+9upbAWNZs1~_v+)} z>S7Ehg00ioCm`iw|6RdY;**i~i*s!mcn-1j*H63i82eLce9xs*z`mc3M}8_?UEHO# zsSE!~;U9cCycM7LJ+}?6k9yC_cBGGcP1_2ZI`^75;;aOx=c7DqRj}t!)VK<+E^5@K z&M`)8rF=ZnTuS-ag71j*oIa4|zbm?5y!-iVLOX!khamm`rU%{>Y^=?Z`M-f8=hF1E zR?o!wH2cIpTL9L_edb!p_0iv)N6@stNnCN%GaT%F&u0WJY8VN&hOLm+;J>LINwbFd zH_xL>{L~U(UT|yl-nNuxjF?Zk|DK@Tzonf>^WUf(LyYqzu(doxX2*RBO+U{D&l&BG z*IKkizY18LHtUjytqp8UZOf7JxSOv4yFT_=PO~=ez3nt>kNK7B8!=ac%@@21Hb=a_ ztHAoGKb)A3Wi`$5vPJUqb{=vKO}%G<{GElahKw`$v_c=}ZU@}>>U|fwkUqW(+B#|K zTu*VV$69c@9`f$O?s~k7W__;L3&s5R6#AHdrDJv<_0o*z9uu3-ykYN8?BdviTY$Hv z`HZ5u2c7e63Oocknr6K1kjA&p=Hp*cr4ghZ~cD^;AhkHi@kkL z!L8l*=iM~pxo7m1$6CJ!Y%|6<0AfEd7(X_c&Yyj>#r%B)tj+oR7}Cf2Q~yz# zI%^iYUzQbIedPT(*t{`kp8&_4X$!wgz}l?OammB>Nw7L|h+|Jq0Z*m*>_&@u-MzpQ zk$cdraZhCIyUW1oyk3!T--)XduHQ8!er>|_zb@hWUti)kCS3np6R!VlC4NW3^}j3O z`rlpR_at2Z`x37I{Rv+Wez4%4QQAL6bDmmhG3TEy^zr-l<#2WGb=Sx_R2TkNg4M-Y z{28!1_k=#a-&fOI8~Z!Qa`81t{oRkgE1#wLzUXreO&`}tedPNbIL-HYxVn=IAM<^I z7WrHwxv{QC>L2-T0Gm%AZTh&z>LcGx;56SYaCJ8qKIZ!(E%Ld>a$~u^`bWOo!RFIP zn?CLl^^xyO;56Tt;p*-ze9ZS1TI6$&$c=S3Qvb;JRj~Q=(WZ}kOnv108aU1Ob-22F z3m@}+gBJPRV{&7;zx0oM4}i_5k2ZbWqYuy`-#5Xoso&d4$cJd^!u~C=YY_HtgVlxo zVX$)+_V0kzh5fr=#~AkSfz>Tx$4^FnpJqH z_51{GKkJX0e+pI~@AJ>V>ekWpaeaSIGq+4X`1$JBq(j3=8$QaiKaExmr_{oCDxSoL9FCEvf;p*eu`3+c|`_|vXc18Y{ras2` zJFvAlwhc(R*nIjrPRHK$YyC&C`dI5ffz_>}IZkt`3;$=p>S7L`1*=;| vi#hu<_%NFNtx+zvKK-MfzknTQTF;Ac_4-@CJbu6ZD|j5un8wl`cc=dWi29B~ literal 12232 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 2; - uint raw0 = memory[ix + 0]; + uint raw0 = read_mem(a, ix + 0); BinInstance s; s.element_ix = raw0; return s; } -void BinInstance_write(BinInstanceRef ref, BinInstance s) { +void BinInstance_write(Alloc a, BinInstanceRef ref, BinInstance s) { uint ix = ref.offset >> 2; - memory[ix + 0] = s.element_ix; + write_mem(a, ix + 0, s.element_ix); } diff --git a/piet-gpu/shader/coarse.comp b/piet-gpu/shader/coarse.comp index 112a57d..3f4e460 100644 --- a/piet-gpu/shader/coarse.comp +++ b/piet-gpu/shader/coarse.comp @@ -13,8 +13,8 @@ #version 450 #extension GL_GOOGLE_include_directive : enable -#include "setup.h" #include "mem.h" +#include "setup.h" layout(local_size_x = N_TILE, local_size_y = 1) in; @@ -34,7 +34,7 @@ shared uint sh_elements[N_TILE]; // Number of elements in the partition; prefix sum. shared uint sh_part_count[N_PART_READ]; -shared uint sh_part_elements[N_PART_READ]; +shared Alloc sh_part_elements[N_PART_READ]; shared uint sh_bitmaps[N_SLICE][N_TILE]; @@ -48,24 +48,47 @@ shared uint sh_tile_y0[N_TILE]; 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. -bool alloc_cmd(inout CmdRef cmd_ref, inout uint cmd_limit) { +#ifdef MEM_DEBUG +// Store allocs only when MEM_DEBUG to save shared memory traffic. +shared Alloc sh_tile_alloc[N_TILE]; + +void write_tile_alloc(uint el_ix, Alloc a) { + sh_tile_alloc[el_ix] = a; +} + +Alloc read_tile_alloc(uint el_ix) { + return sh_tile_alloc[el_ix]; +} +#else +void write_tile_alloc(uint el_ix, Alloc a) { + // No-op +} + +Alloc read_tile_alloc(uint el_ix) { + // All memory. + return new_alloc(0, memory.length()*4); +} +#endif + +// Perhaps cmd_alloc should be a global? This is a style question. +bool alloc_cmd(inout Alloc cmd_alloc, inout CmdRef cmd_ref, inout uint cmd_limit) { if (cmd_ref.offset < cmd_limit) { return true; } - Alloc new_cmd = malloc(PTCL_INITIAL_ALLOC); + MallocResult 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; + CmdJump jump = CmdJump(new_cmd.alloc.offset); + Cmd_Jump_write(cmd_alloc, cmd_ref, jump); + cmd_alloc = new_cmd.alloc; + cmd_ref = CmdRef(cmd_alloc.offset); + cmd_limit = cmd_alloc.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; return true; } void main() { - if (mem_overflow) { + if (mem_error != NO_ERROR) { return; } @@ -85,7 +108,8 @@ 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) * conf.width_in_tiles + bin_tile_x + tile_x; - CmdRef cmd_ref = CmdRef(conf.ptcl_base + this_tile_ix * PTCL_INITIAL_ALLOC); + Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, this_tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC); + CmdRef cmd_ref = CmdRef(cmd_alloc.offset); uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - 2 * Cmd_size; // The nesting depth of the clip stack uint clip_depth = 0; @@ -117,9 +141,10 @@ 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 = (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]; + uint in_ix = (conf.bin_alloc.offset >> 2) + ((partition_ix + th_ix) * N_TILE + bin_ix) * 2; + count = read_mem(conf.bin_alloc, in_ix); + uint offset = read_mem(conf.bin_alloc, in_ix + 1); + sh_part_elements[th_ix] = new_alloc(offset, count*BinInstance_size); } // prefix sum of counts for (uint i = 0; i < LG_N_PART_READ; i++) { @@ -152,8 +177,9 @@ void main() { } } ix -= part_ix > 0 ? sh_part_count[part_ix - 1] : part_start_ix; - BinInstanceRef inst_ref = BinInstanceRef(sh_part_elements[part_ix]); - BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, ix)); + Alloc bin_alloc = sh_part_elements[part_ix]; + BinInstanceRef inst_ref = BinInstanceRef(bin_alloc.offset); + BinInstance inst = BinInstance_read(bin_alloc, BinInstance_index(inst_ref, ix)); sh_elements[th_ix] = inst.element_ix; } barrier(); @@ -169,8 +195,8 @@ void main() { AnnotatedRef ref; if (th_ix + rd_ix < wr_ix) { element_ix = sh_elements[th_ix]; - ref = AnnotatedRef(conf.anno_base + element_ix * Annotated_size); - tag = Annotated_tag(ref); + ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); + tag = Annotated_tag(conf.anno_alloc, ref); } // Bounding box of element in pixel coordinates. @@ -183,7 +209,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(conf.tile_base + path_ix * Path_size)); + Path path = Path_read(conf.tile_alloc, PathRef(conf.tile_alloc.offset + 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); @@ -199,6 +225,8 @@ void main() { // base relative to bin uint base = path.tiles.offset - uint(dy * stride + dx) * Tile_size; sh_tile_base[th_ix] = base; + Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size); + write_tile_alloc(th_ix, path_alloc); break; default: tile_count = 0; @@ -226,8 +254,8 @@ void main() { el_ix = probe; } } - AnnotatedRef ref = AnnotatedRef(conf.anno_base + sh_elements[el_ix] * Annotated_size); - uint tag = Annotated_tag(ref); + AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + sh_elements[el_ix] * Annotated_size); + uint tag = Annotated_tag(conf.anno_alloc, ref); uint seq_ix = ix - (el_ix > 0 ? sh_tile_count[el_ix - 1] : 0); uint width = sh_tile_width[el_ix]; uint x = sh_tile_x0[el_ix] + seq_ix % width; @@ -236,7 +264,7 @@ void main() { if (tag == Annotated_BeginClip || tag == Annotated_EndClip) { include_tile = true; } else { - Tile tile = Tile_read(TileRef(sh_tile_base[el_ix] + (sh_tile_stride[el_ix] * y + x) * Tile_size)); + Tile tile = Tile_read(read_tile_alloc(el_ix), TileRef(sh_tile_base[el_ix] + (sh_tile_stride[el_ix] * y + x) * Tile_size)); // Include the path in the tile if // - the tile contains at least a segment (tile offset non-zero) // - the tile is completely covered (backdrop non-zero) @@ -275,16 +303,16 @@ 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(conf.anno_base + element_ix * Annotated_size); - tag = Annotated_tag(ref); + ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); + tag = Annotated_tag(conf.anno_alloc, ref); if (clip_zero_depth == 0) { switch (tag) { case Annotated_Fill: - Tile tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + Tile tile = Tile_read(read_tile_alloc(element_ref_ix), 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); - if (!alloc_cmd(cmd_ref, cmd_limit)) { + AnnoFill fill = Annotated_Fill_read(conf.anno_alloc, ref); + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { break; } if (tile.tile.offset != 0) { @@ -292,32 +320,32 @@ void main() { cmd_fill.tile_ref = tile.tile.offset; cmd_fill.backdrop = tile.backdrop; cmd_fill.rgba_color = fill.rgba_color; - Cmd_Fill_write(cmd_ref, cmd_fill); + Cmd_Fill_write(cmd_alloc, cmd_ref, cmd_fill); } else { - Cmd_Solid_write(cmd_ref, CmdSolid(fill.rgba_color)); + Cmd_Solid_write(cmd_alloc, cmd_ref, CmdSolid(fill.rgba_color)); } cmd_ref.offset += Cmd_size; break; case Annotated_BeginClip: - tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + tile = Tile_read(read_tile_alloc(element_ref_ix), TileRef(sh_tile_base[element_ref_ix] + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); if (tile.tile.offset == 0 && tile.backdrop == 0) { clip_zero_depth = clip_depth + 1; } else if (tile.tile.offset == 0 && clip_depth < 32) { clip_one_mask |= (1 << clip_depth); } else { - if (!alloc_cmd(cmd_ref, cmd_limit)) { + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { break; } if (tile.tile.offset != 0) { CmdBeginClip cmd_begin_clip; cmd_begin_clip.tile_ref = tile.tile.offset; cmd_begin_clip.backdrop = tile.backdrop; - Cmd_BeginClip_write(cmd_ref, cmd_begin_clip); + Cmd_BeginClip_write(cmd_alloc, cmd_ref, cmd_begin_clip); } else { // TODO: here is where a bunch of optimization magic should happen float alpha = tile.backdrop == 0 ? 0.0 : 1.0; - Cmd_BeginSolidClip_write(cmd_ref, CmdBeginSolidClip(alpha)); + Cmd_BeginSolidClip_write(cmd_alloc, cmd_ref, CmdBeginSolidClip(alpha)); } cmd_ref.offset += Cmd_size; if (clip_depth < 32) { @@ -329,25 +357,25 @@ void main() { case Annotated_EndClip: clip_depth--; if (clip_depth >= 32 || (clip_one_mask & (1 << clip_depth)) == 0) { - if (!alloc_cmd(cmd_ref, cmd_limit)) { + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { break; } - Cmd_EndClip_write(cmd_ref, CmdEndClip(1.0)); + Cmd_EndClip_write(cmd_alloc, cmd_ref, CmdEndClip(1.0)); cmd_ref.offset += Cmd_size; } break; case Annotated_Stroke: - tile = Tile_read(TileRef(sh_tile_base[element_ref_ix] + tile = Tile_read(read_tile_alloc(element_ref_ix), TileRef(sh_tile_base[element_ref_ix] + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); - AnnoStroke stroke = Annotated_Stroke_read(ref); + AnnoStroke stroke = Annotated_Stroke_read(conf.anno_alloc, ref); CmdStroke cmd_stroke; cmd_stroke.tile_ref = tile.tile.offset; cmd_stroke.half_width = 0.5 * stroke.linewidth; cmd_stroke.rgba_color = stroke.rgba_color; - if (!alloc_cmd(cmd_ref, cmd_limit)) { + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { break; } - Cmd_Stroke_write(cmd_ref, cmd_stroke); + Cmd_Stroke_write(cmd_alloc, cmd_ref, cmd_stroke); cmd_ref.offset += Cmd_size; break; } @@ -372,6 +400,6 @@ void main() { if (rd_ix >= ready_ix && partition_ix >= n_partitions) break; } if (bin_tile_x + tile_x < conf.width_in_tiles && bin_tile_y + tile_y < conf.height_in_tiles) { - Cmd_End_write(cmd_ref); + Cmd_End_write(cmd_alloc, cmd_ref); } } diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 89910942d7736abbb0afa9c12cddafb2c30c7792..505c4f4073af8832db0a68569b0c614f7d0b183e 100644 GIT binary patch literal 52300 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 BMauvH literal 40060 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 diff --git a/piet-gpu/shader/elements.comp b/piet-gpu/shader/elements.comp index a0e5011..255dd13 100644 --- a/piet-gpu/shader/elements.comp +++ b/piet-gpu/shader/elements.comp @@ -9,8 +9,8 @@ #version 450 #extension GL_GOOGLE_include_directive : enable -#include "setup.h" #include "mem.h" +#include "setup.h" #define N_ROWS 4 #define WG_SIZE 32 @@ -172,7 +172,7 @@ shared uint sh_part_ix; shared State sh_prefix; void main() { - if (mem_overflow) { + if (mem_error != NO_ERROR) { return; } @@ -342,10 +342,10 @@ 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(conf.pathseg_base + (st.pathseg_count - 1) * PathSeg_size); + PathSegRef path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); uint out_tag = tag == Element_FillLine ? PathSeg_FillCubic : PathSeg_StrokeCubic; - memory[path_out_ref.offset >> 2] = out_tag; - PathStrokeCubic_write(PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); + write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); + PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; case Element_FillQuad: case Element_StrokeQuad: @@ -366,10 +366,10 @@ 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(conf.pathseg_base + (st.pathseg_count - 1) * PathSeg_size); + path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); out_tag = tag == Element_FillQuad ? PathSeg_FillCubic : PathSeg_StrokeCubic; - memory[path_out_ref.offset >> 2] = out_tag; - PathStrokeCubic_write(PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); + write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); + PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; case Element_FillCubic: case Element_StrokeCubic: @@ -387,10 +387,10 @@ 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(conf.pathseg_base + (st.pathseg_count - 1) * PathSeg_size); + path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); out_tag = tag == Element_FillCubic ? PathSeg_FillCubic : PathSeg_StrokeCubic; - memory[path_out_ref.offset >> 2] = out_tag; - PathStrokeCubic_write(PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); + write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); + PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); break; case Element_Stroke: Stroke stroke = Element_Stroke_read(this_ref); @@ -399,31 +399,31 @@ 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(conf.anno_base + (st.path_count - 1) * Annotated_size); - Annotated_Stroke_write(out_ref, anno_stroke); + AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_Stroke_write(conf.anno_alloc, out_ref, anno_stroke); break; case Element_Fill: Fill fill = Element_Fill_read(this_ref); AnnoFill anno_fill; anno_fill.rgba_color = fill.rgba_color; anno_fill.bbox = st.bbox; - out_ref = AnnotatedRef(conf.anno_base + (st.path_count - 1) * Annotated_size); - Annotated_Fill_write(out_ref, anno_fill); + out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_Fill_write(conf.anno_alloc, out_ref, anno_fill); break; case Element_BeginClip: Clip begin_clip = Element_BeginClip_read(this_ref); 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(conf.anno_base + (st.path_count - 1) * Annotated_size); - Annotated_BeginClip_write(out_ref, anno_begin_clip); + out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_BeginClip_write(conf.anno_alloc, 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(conf.anno_base + (st.path_count - 1) * Annotated_size); - Annotated_EndClip_write(out_ref, anno_end_clip); + 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; } } diff --git a/piet-gpu/shader/elements.spv b/piet-gpu/shader/elements.spv index 7475f040e7423e3ae6336f642d07c3e1f228a16a..287aa4e80402434e1fc83e376cea21dcde78d9a5 100644 GIT binary patch literal 66568 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 JL%DPK{{ZY|zHR^j literal 59452 zcmbWA1-M<+)wMU|-eAGq6WohSaSc{16o(LE1Y#sNxCJR%+@(<5in|mk6e!XHg|N6^;?lzZzUS<5v+ge6*YEjHyI5n4IoF(Xt-ZFKeQ%OpQ!YGJRZUS%TTNZvGfma{ z%v?>0QdQGcy<2_%EeCA5-1x2`%dNc1@;b~=^=kR)Gb28o^c}QOgGP+juww73nh9NP zW?EmGzP4WcZ%X<{ART1wzvaOGTW{Te%Z&#P9y@m6_^yLS4jnjh#OR>|hm06Eba2;* z{fFu|b>TN+^x%;bhLjGSc$E1&sfbBc8;=}1YUt>$?S>AkT2-p~88d9y_@P}DUVW-XR#+4NUaUz~jU%8r`+!?x=*>AJ{g%`| zVCeAfoN9h1=j^Cvh1Y(qRg(J|%j&vna@T6?3dWtTngd+>4;(ji(2$fkIajMhe0x>1 zYJQDEUNsaQdMtN$Czq@wqO6}vm2skl&6djQx z*sfatUe&_*x9jex76WfGV&q6%sHL;Tk&2o-swHY4_1^qX?q}Tay#@^&JZ9vWamJmt zS{lt*J!+_fm-&}+Y-`_jR?C71bd4Lc?@%jf?`<_dlh>`I`T@M{*RDbCr)Le*RUF3J zuSXT#Datz7l64)utCbx$w99R{WpizQSWL^@b=>djUGR2%(zkQWoqS9_cOV+&T1Waw=dZJqULARpe}gt z7I%Mj8FS>IE-p*d+NWBdzI$wsLc3M6x2tp?t%rZR#*S)3@P;EtjI})1LhfgBZ5`Fd zaDCMEk^9+euQ3POyN5QV*QZDNI&ir@{qbw(p03&woahZN&S6yvSo ze;?h>@A=x^Pcv4dit+#Bx%(eGqa9~tcbplj{faox3saAJ1JiKjry-D7@uI z?{!D+C)XW$^SWa!b?59~3esAO*~MLny?tqJ)g*`CMA#->j9%}>GYdGxMM ztK+xNVQ12=QQc2DmowmoZ~M0W)clllQESe{J)rI$D)&ogHCuHq+T^(ZIm_X|M(%*i^xvx%3vo&(M?JHO6T->O>2Ld{P(&kNz!qn|d9nVO%>vy*Qcm%`)spH=<^0q3|}R>$^~9@0K6tvb~GOs;p1>T0-kw0sA)7v(?eNu*{$-?NCs za~-dwPi+1a=8_F>m!tcq4z$ZPhR` znR0VAtQ>W(vXdK^T4;S9+)u9TwFj(&*2(wqN5pQXfrEw*A2)Qk2)`xT-;aA&4`UxY zXk6Ebt`TEK4`ed+xW~H3jm3S$f!%{z_TJT_^xrq`$?kF9z3Q*%CacFi@x9~buAW67 zJZ98hKAg7d?Q!R6?fCD#%dp;l`~N@Pi=HOOpjI8ds%Pl;YT@as=jlfc8ar@k`~5>Q z|Hngz=k#;Lu{-p*L@7kOl-5%SU6QI3k zdR23QQ_Fnd$+hr3zRkPiF4pGVahGoMI&Md`e4DS_&eKt?+UBby@7nO>9RN<=9ov5O z8gx{yYr51^Ez)wHKEO0UuRwOL)v_`)O8d*bzK5ZU6;50 z>biPW*A{*^6@Irh{O&3I9xVKR)$n_=@O!TCd$EUKNA+@>cdy6mZQi{eZ?}2(dc5D} z-RtpDn|B|FPvM#GG+g{L-|4~S@t>v5yYtT3=G}ScYx6p9uWBK9J8nm{CYWo+AisOK z<{LKn77ae2!FO%&!41A|gO6|ULmK?(20y96&+Nf_Rp-L9E*F9)uS;ijF}!u!cb}tE zwKVsW*U9#S`OtB4?;-lL9Rnji70;N-ru8*@H^%f0zZgHLMkcN_fU2LGhNzisgEdT`#c^FWch zb_EYky*yO#zNfXOr-3Ke*sD4d-uCOLt_AzOpgqry>bf4j9o2*I{dnUN`$G-;EAYX- z+im6Ts9x=1r#`-9j%1ZRw!Oe*9n-

T!K~*rsm%Y+?Awfm@ClJZR)*qxYxtL1VLx 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 D;UhOt diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index fe55ff9..395ac80 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -10,8 +10,8 @@ #extension GL_GOOGLE_include_directive : enable #extension GL_EXT_nonuniform_qualifier : enable -#include "setup.h" #include "mem.h" +#include "setup.h" #define CHUNK 8 #define CHUNK_DY (TILE_HEIGHT_PX / CHUNK) @@ -37,16 +37,16 @@ layout(set = 0, binding = 3) uniform sampler2D textures[]; #define CLIP_LINK_OFFSET (TILE_WIDTH_PX * TILE_HEIGHT_PX) #define CLIP_BUF_SIZE (CLIP_LINK_OFFSET + 1) -shared Alloc sh_clip_alloc; +shared MallocResult sh_clip_alloc; // Allocate a scratch buffer for clipping. -Alloc alloc_clip_buf(uint link) { +MallocResult alloc_clip_buf(uint link) { if (gl_LocalInvocationID.x == 0 && gl_LocalInvocationID.y == 0) { - Alloc alloc = malloc(CLIP_BUF_SIZE * 4); - if (!alloc.failed) { - memory[(alloc.offset >> 2) + CLIP_LINK_OFFSET] = link; + MallocResult m = malloc(CLIP_BUF_SIZE * 4); + if (!m.failed) { + write_mem(m.alloc, (m.alloc.offset >> 2) + CLIP_LINK_OFFSET, link); } - sh_clip_alloc = alloc; + sh_clip_alloc = m; } barrier(); return sh_clip_alloc; @@ -59,7 +59,7 @@ float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { for (uint k = 0; k < CHUNK; k++) area[k] = float(backdrop); TileSegRef tile_seg_ref = TileSegRef(tile_ref); do { - TileSeg seg = TileSeg_read(tile_seg_ref); + TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size), tile_seg_ref); for (uint k = 0; k < CHUNK; k++) { vec2 my_xy = vec2(xy.x, xy.y + float(k * CHUNK_DY)); vec2 start = seg.origin - my_xy; @@ -87,12 +87,13 @@ float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { } void main() { - if (mem_overflow) { + if (mem_error != NO_ERROR) { return; } uint tile_ix = gl_WorkGroupID.y * conf.width_in_tiles + gl_WorkGroupID.x; - CmdRef cmd_ref = CmdRef(conf.ptcl_base + tile_ix * PTCL_INITIAL_ALLOC); + Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC); + CmdRef cmd_ref = CmdRef(cmd_alloc.offset); uvec2 xy_uint = uvec2(gl_GlobalInvocationID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); vec2 xy = vec2(xy_uint); @@ -101,7 +102,7 @@ void main() { uint blend_stack[BLEND_STACK_SIZE][CHUNK]; uint blend_spill = 0; uint blend_sp = 0; - uint clip_tos = 0; + Alloc clip_tos = new_alloc(0, 0); for (uint i = 0; i < CHUNK; i++) { rgb[i] = vec3(0.5); if (xy_uint.x < 1024 && xy_uint.y < 1024) { @@ -111,13 +112,13 @@ void main() { } while (true) { - uint tag = Cmd_tag(cmd_ref); + uint tag = Cmd_tag(cmd_alloc, cmd_ref); if (tag == Cmd_End) { break; } switch (tag) { case Cmd_Circle: - CmdCircle circle = Cmd_Circle_read(cmd_ref); + CmdCircle circle = Cmd_Circle_read(cmd_alloc, cmd_ref); vec4 fg_rgba = unpackUnorm4x8(circle.rgba_color).wzyx; for (uint i = 0; i < CHUNK; i++) { float dy = float(i * CHUNK_DY); @@ -129,12 +130,12 @@ void main() { break; case Cmd_Stroke: // Calculate distance field from all the line segments in this tile. - CmdStroke stroke = Cmd_Stroke_read(cmd_ref); + CmdStroke stroke = Cmd_Stroke_read(cmd_alloc, cmd_ref); float df[CHUNK]; for (uint k = 0; k < CHUNK; k++) df[k] = 1e9; TileSegRef tile_seg_ref = TileSegRef(stroke.tile_ref); do { - TileSeg seg = TileSeg_read(tile_seg_ref); + TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size), tile_seg_ref); vec2 line_vec = seg.vector; for (uint k = 0; k < CHUNK; k++) { vec2 dpos = xy + vec2(0.5, 0.5) - seg.origin; @@ -151,7 +152,7 @@ void main() { } break; case Cmd_Fill: - CmdFill fill = Cmd_Fill_read(cmd_ref); + CmdFill fill = Cmd_Fill_read(cmd_alloc, cmd_ref); float area[CHUNK]; area = computeArea(xy, fill.backdrop, fill.tile_ref); fg_rgba = unpackUnorm4x8(fill.rgba_color).wzyx; @@ -164,25 +165,25 @@ void main() { uint blend_slot = blend_sp % BLEND_STACK_SIZE; if (blend_sp == blend_spill + BLEND_STACK_SIZE) { // spill to scratch buffer - Alloc alloc = alloc_clip_buf(clip_tos); - if (alloc.failed) { + MallocResult m = alloc_clip_buf(clip_tos.offset); + if (m.failed) { return; } - clip_tos = alloc.offset; - uint base_ix = (clip_tos >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; + clip_tos = m.alloc; + uint base_ix = (clip_tos.offset >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; for (uint k = 0; k < CHUNK; k++) { - memory[base_ix + k * TILE_WIDTH_PX * CHUNK_DY] = blend_stack[blend_slot][k]; + write_mem(clip_tos, base_ix + k * TILE_WIDTH_PX * CHUNK_DY, blend_stack[blend_slot][k]); } blend_spill++; } if (tag == Cmd_BeginClip) { - CmdBeginClip begin_clip = Cmd_BeginClip_read(cmd_ref); + CmdBeginClip begin_clip = Cmd_BeginClip_read(cmd_alloc, cmd_ref); area = computeArea(xy, begin_clip.backdrop, begin_clip.tile_ref); for (uint k = 0; k < CHUNK; k++) { blend_stack[blend_slot][k] = packUnorm4x8(vec4(rgb[k], clamp(abs(area[k]), 0.0, 1.0))); } } else { - CmdBeginSolidClip begin_solid_clip = Cmd_BeginSolidClip_read(cmd_ref); + CmdBeginSolidClip begin_solid_clip = Cmd_BeginSolidClip_read(cmd_alloc, cmd_ref); float solid_alpha = begin_solid_clip.alpha; for (uint k = 0; k < CHUNK; k++) { blend_stack[blend_slot][k] = packUnorm4x8(vec4(rgb[k], solid_alpha)); @@ -191,14 +192,14 @@ void main() { blend_sp++; break; case Cmd_EndClip: - CmdEndClip end_clip = Cmd_EndClip_read(cmd_ref); + CmdEndClip end_clip = Cmd_EndClip_read(cmd_alloc, cmd_ref); blend_slot = (blend_sp - 1) % BLEND_STACK_SIZE; if (blend_sp == blend_spill) { - uint base_ix = (clip_tos >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; + uint base_ix = (clip_tos.offset >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; for (uint k = 0; k < CHUNK; k++) { - blend_stack[blend_slot][k] = memory[base_ix + k * TILE_WIDTH_PX * CHUNK_DY]; + blend_stack[blend_slot][k] = read_mem(clip_tos, base_ix + k * TILE_WIDTH_PX * CHUNK_DY); } - clip_tos = memory[(clip_tos >> 2) + CLIP_LINK_OFFSET]; + clip_tos.offset = read_mem(clip_tos, (clip_tos.offset >> 2) + CLIP_LINK_OFFSET); blend_spill--; } blend_sp--; @@ -208,20 +209,21 @@ void main() { } break; case Cmd_Solid: - CmdSolid solid = Cmd_Solid_read(cmd_ref); + CmdSolid solid = Cmd_Solid_read(cmd_alloc, cmd_ref); fg_rgba = unpackUnorm4x8(solid.rgba_color).wzyx; for (uint k = 0; k < CHUNK; k++) { rgb[k] = mix(rgb[k], fg_rgba.rgb, mask[k] * fg_rgba.a); } break; case Cmd_SolidMask: - CmdSolidMask solid_mask = Cmd_SolidMask_read(cmd_ref); + CmdSolidMask solid_mask = Cmd_SolidMask_read(cmd_alloc, cmd_ref); for (uint k = 0; k < CHUNK; k++) { mask[k] = solid_mask.mask; } break; case Cmd_Jump: - cmd_ref = CmdRef(Cmd_Jump_read(cmd_ref).new_ref); + cmd_ref = CmdRef(Cmd_Jump_read(cmd_alloc, cmd_ref).new_ref); + cmd_alloc.offset = cmd_ref.offset; continue; } cmd_ref.offset += Cmd_size; diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index b3843402d26e2f425473d42baabf180350fed2c1..3ccf21d60fb5df3f2f875314aa7d8c406d19a292 100644 GIT binary patch literal 41884 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 a)?1reIfv$Jd%p$T4YohO6Nvpz@c#f(wA^k0 literal 34252 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 memory.length() * 4; - if (a.failed) { - mem_overflow = true; - } + a.offset = offset; +#ifdef MEM_DEBUG + a.size = size; +#endif return a; } + +// malloc allocates size bytes of memory. +MallocResult malloc(uint size) { + MallocResult r; + r.failed = false; + uint offset = atomicAdd(mem_offset, size); + r.alloc = new_alloc(offset, size); + if (offset + size > memory.length() * 4) { + r.failed = true; + atomicMax(mem_error, ERR_MALLOC_FAILED); + return r; + } +#ifdef MEM_DEBUG + if ((size & 3) != 0) { + r.failed = true; + atomicMax(mem_error, ERR_UNALIGNED_ACCESS); + return r; + } +#endif + return r; +} + +// touch_mem checks whether access to the memory word at offset is valid. +// If MEM_DEBUG is defined, touch_mem returns false if offset is out of bounds. +// Offset is in words. +bool touch_mem(Alloc alloc, uint offset) { +#ifdef MEM_DEBUG + if (offset < alloc.offset/4 || offset >= (alloc.offset + alloc.size)/4) { + atomicMax(mem_error, ERR_OUT_OF_BOUNDS); + return false; + } +#endif + return true; +} + +// write_mem writes val to memory at offset. +// Offset is in words. +void write_mem(Alloc alloc, uint offset, uint val) { + if (!touch_mem(alloc, offset)) { + return; + } + memory[offset] = val; +} + +// read_mem reads the value from memory at offset. +// Offset is in words. +uint read_mem(Alloc alloc, uint offset) { + if (!touch_mem(alloc, offset)) { + return 0; + } + uint v = memory[offset]; + return v; +} + +// slice_mem returns a sub-allocation inside another. Offset and size are in +// bytes, relative to a.offset. +Alloc slice_mem(Alloc a, uint offset, uint size) { +#ifdef MEM_DEBUG + if ((offset & 3) != 0 || (size & 3) != 0) { + atomicMax(mem_error, ERR_UNALIGNED_ACCESS); + return Alloc(0, 0); + } + if (offset + size > a.size) { + // slice_mem is sometimes used for slices outside bounds, + // but never written. + return Alloc(0, 0); + } +#endif + return new_alloc(a.offset + offset, size); +} diff --git a/piet-gpu/shader/path_coarse.comp b/piet-gpu/shader/path_coarse.comp index 20c3586..4f77ff9 100644 --- a/piet-gpu/shader/path_coarse.comp +++ b/piet-gpu/shader/path_coarse.comp @@ -7,8 +7,8 @@ #version 450 #extension GL_GOOGLE_include_directive : enable -#include "setup.h" #include "mem.h" +#include "setup.h" #define LG_COARSE_WG 5 #define COARSE_WG (1 << LG_COARSE_WG) @@ -87,21 +87,21 @@ SubdivResult estimate_subdiv(vec2 p0, vec2 p1, vec2 p2, float sqrt_tol) { } void main() { - if (mem_overflow) { + if (mem_error != NO_ERROR) { return; } uint element_ix = gl_GlobalInvocationID.x; - PathSegRef ref = PathSegRef(conf.pathseg_base + element_ix * PathSeg_size); + PathSegRef ref = PathSegRef(conf.pathseg_alloc.offset + element_ix * PathSeg_size); uint tag = PathSeg_Nop; if (element_ix < conf.n_pathseg) { - tag = PathSeg_tag(ref); + tag = PathSeg_tag(conf.pathseg_alloc, ref); } switch (tag) { case PathSeg_FillCubic: case PathSeg_StrokeCubic: - PathStrokeCubic cubic = PathSeg_StrokeCubic_read(ref); + PathStrokeCubic cubic = PathSeg_StrokeCubic_read(conf.pathseg_alloc, ref); 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. @@ -123,7 +123,8 @@ 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(conf.tile_base + path_ix * Path_size)); + Path path = Path_read(conf.tile_alloc, PathRef(conf.tile_alloc.offset + path_ix * Path_size)); + Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size); ivec4 bbox = ivec4(path.bbox); vec2 p0 = cubic.p0; qp0 = cubic.p0; @@ -182,11 +183,11 @@ 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. - Alloc tile_alloc = malloc(n_tile_alloc * TileSeg_size); + MallocResult tile_alloc = malloc(n_tile_alloc * TileSeg_size); if (tile_alloc.failed) { return; } - uint tile_offset = tile_alloc.offset; + uint tile_offset = tile_alloc.alloc.offset; TileSeg tile_seg; @@ -204,7 +205,9 @@ 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(memory[tile_el + 1], backdrop); + if (touch_mem(path_alloc, tile_el + 1)) { + atomicAdd(memory[tile_el + 1], backdrop); + } } // next_xray is the xray for the next scanline; the line segment intersects @@ -225,9 +228,12 @@ void main() { for (int x = xx0; x < xx1; x++) { float tile_x0 = float(x * TILE_WIDTH_PX); - TileRef tile_ref = Tile_index(path.tiles, uint(base + x)); + TileRef tile_ref = Tile_index(TileRef(path.tiles.offset), uint(base + x)); uint tile_el = tile_ref.offset >> 2; - uint old = atomicExchange(memory[tile_el], tile_offset); + uint old = 0; + if (touch_mem(path_alloc, tile_el)) { + old = atomicExchange(memory[tile_el], tile_offset); + } tile_seg.origin = p0; tile_seg.vector = p1 - p0; float y_edge = 0.0; @@ -254,7 +260,7 @@ void main() { } tile_seg.y_edge = y_edge; tile_seg.next.offset = old; - TileSeg_write(TileSegRef(tile_offset), tile_seg); + TileSeg_write(tile_alloc.alloc, TileSegRef(tile_offset), tile_seg); tile_offset += TileSeg_size; } xc += b; diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index 3e4392cf9c12347746b1ced7e5bb363ef54a9f07..185460464f9eac7aab4ded685ca146e57ce10d87 100644 GIT binary patch literal 37768 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 z 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 2; - 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 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); PathFillLine s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -101,24 +101,24 @@ PathFillLine PathFillLine_read(PathFillLineRef ref) { return s; } -void PathFillLine_write(PathFillLineRef ref, PathFillLine s) { +void PathFillLine_write(Alloc a, PathFillLineRef ref, PathFillLine s) { uint ix = ref.offset >> 2; - 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; + write_mem(a, ix + 0, floatBitsToUint(s.p0.x)); + write_mem(a, ix + 1, floatBitsToUint(s.p0.y)); + write_mem(a, ix + 2, floatBitsToUint(s.p1.x)); + write_mem(a, ix + 3, floatBitsToUint(s.p1.y)); + write_mem(a, ix + 4, s.path_ix); } -PathStrokeLine PathStrokeLine_read(PathStrokeLineRef ref) { +PathStrokeLine PathStrokeLine_read(Alloc a, PathStrokeLineRef ref) { uint ix = ref.offset >> 2; - 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 raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + uint raw6 = read_mem(a, ix + 6); PathStrokeLine s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -127,28 +127,28 @@ PathStrokeLine PathStrokeLine_read(PathStrokeLineRef ref) { return s; } -void PathStrokeLine_write(PathStrokeLineRef ref, PathStrokeLine s) { +void PathStrokeLine_write(Alloc a, PathStrokeLineRef ref, PathStrokeLine s) { uint ix = ref.offset >> 2; - 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); + write_mem(a, ix + 0, floatBitsToUint(s.p0.x)); + write_mem(a, ix + 1, floatBitsToUint(s.p0.y)); + write_mem(a, ix + 2, floatBitsToUint(s.p1.x)); + write_mem(a, ix + 3, floatBitsToUint(s.p1.y)); + write_mem(a, ix + 4, s.path_ix); + write_mem(a, ix + 5, floatBitsToUint(s.stroke.x)); + write_mem(a, ix + 6, floatBitsToUint(s.stroke.y)); } -PathFillCubic PathFillCubic_read(PathFillCubicRef ref) { +PathFillCubic PathFillCubic_read(Alloc a, PathFillCubicRef ref) { uint ix = ref.offset >> 2; - 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 raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + uint raw6 = read_mem(a, ix + 6); + uint raw7 = read_mem(a, ix + 7); + uint raw8 = read_mem(a, ix + 8); PathFillCubic s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -158,32 +158,32 @@ PathFillCubic PathFillCubic_read(PathFillCubicRef ref) { return s; } -void PathFillCubic_write(PathFillCubicRef ref, PathFillCubic s) { +void PathFillCubic_write(Alloc a, PathFillCubicRef ref, PathFillCubic s) { uint ix = ref.offset >> 2; - 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; + write_mem(a, ix + 0, floatBitsToUint(s.p0.x)); + write_mem(a, ix + 1, floatBitsToUint(s.p0.y)); + write_mem(a, ix + 2, floatBitsToUint(s.p1.x)); + write_mem(a, ix + 3, floatBitsToUint(s.p1.y)); + write_mem(a, ix + 4, floatBitsToUint(s.p2.x)); + write_mem(a, ix + 5, floatBitsToUint(s.p2.y)); + 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); } -PathStrokeCubic PathStrokeCubic_read(PathStrokeCubicRef ref) { +PathStrokeCubic PathStrokeCubic_read(Alloc a, PathStrokeCubicRef ref) { uint ix = ref.offset >> 2; - 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]; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + uint raw6 = read_mem(a, ix + 6); + uint raw7 = read_mem(a, ix + 7); + uint raw8 = read_mem(a, ix + 8); + uint raw9 = read_mem(a, ix + 9); + uint raw10 = read_mem(a, ix + 10); PathStrokeCubic s; s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -194,62 +194,62 @@ PathStrokeCubic PathStrokeCubic_read(PathStrokeCubicRef ref) { return s; } -void PathStrokeCubic_write(PathStrokeCubicRef ref, PathStrokeCubic s) { +void PathStrokeCubic_write(Alloc a, PathStrokeCubicRef ref, PathStrokeCubic s) { uint ix = ref.offset >> 2; - 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); + write_mem(a, ix + 0, floatBitsToUint(s.p0.x)); + write_mem(a, ix + 1, floatBitsToUint(s.p0.y)); + write_mem(a, ix + 2, floatBitsToUint(s.p1.x)); + write_mem(a, ix + 3, floatBitsToUint(s.p1.y)); + write_mem(a, ix + 4, floatBitsToUint(s.p2.x)); + write_mem(a, ix + 5, floatBitsToUint(s.p2.y)); + 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)); } -uint PathSeg_tag(PathSegRef ref) { - return memory[ref.offset >> 2]; +uint PathSeg_tag(Alloc a, PathSegRef ref) { + return read_mem(a, ref.offset >> 2); } -PathFillLine PathSeg_FillLine_read(PathSegRef ref) { - return PathFillLine_read(PathFillLineRef(ref.offset + 4)); +PathFillLine PathSeg_FillLine_read(Alloc a, PathSegRef ref) { + return PathFillLine_read(a, PathFillLineRef(ref.offset + 4)); } -PathStrokeLine PathSeg_StrokeLine_read(PathSegRef ref) { - return PathStrokeLine_read(PathStrokeLineRef(ref.offset + 4)); +PathStrokeLine PathSeg_StrokeLine_read(Alloc a, PathSegRef ref) { + return PathStrokeLine_read(a, PathStrokeLineRef(ref.offset + 4)); } -PathFillCubic PathSeg_FillCubic_read(PathSegRef ref) { - return PathFillCubic_read(PathFillCubicRef(ref.offset + 4)); +PathFillCubic PathSeg_FillCubic_read(Alloc a, PathSegRef ref) { + return PathFillCubic_read(a, PathFillCubicRef(ref.offset + 4)); } -PathStrokeCubic PathSeg_StrokeCubic_read(PathSegRef ref) { - return PathStrokeCubic_read(PathStrokeCubicRef(ref.offset + 4)); +PathStrokeCubic PathSeg_StrokeCubic_read(Alloc a, PathSegRef ref) { + return PathStrokeCubic_read(a, PathStrokeCubicRef(ref.offset + 4)); } -void PathSeg_Nop_write(PathSegRef ref) { - memory[ref.offset >> 2] = PathSeg_Nop; +void PathSeg_Nop_write(Alloc a, PathSegRef ref) { + write_mem(a, ref.offset >> 2, PathSeg_Nop); } -void PathSeg_FillLine_write(PathSegRef ref, PathFillLine s) { - memory[ref.offset >> 2] = PathSeg_FillLine; - PathFillLine_write(PathFillLineRef(ref.offset + 4), s); +void PathSeg_FillLine_write(Alloc a, PathSegRef ref, PathFillLine s) { + write_mem(a, ref.offset >> 2, PathSeg_FillLine); + PathFillLine_write(a, PathFillLineRef(ref.offset + 4), s); } -void PathSeg_StrokeLine_write(PathSegRef ref, PathStrokeLine s) { - memory[ref.offset >> 2] = PathSeg_StrokeLine; - PathStrokeLine_write(PathStrokeLineRef(ref.offset + 4), s); +void PathSeg_StrokeLine_write(Alloc a, PathSegRef ref, PathStrokeLine s) { + write_mem(a, ref.offset >> 2, PathSeg_StrokeLine); + PathStrokeLine_write(a, PathStrokeLineRef(ref.offset + 4), s); } -void PathSeg_FillCubic_write(PathSegRef ref, PathFillCubic s) { - memory[ref.offset >> 2] = PathSeg_FillCubic; - PathFillCubic_write(PathFillCubicRef(ref.offset + 4), s); +void PathSeg_FillCubic_write(Alloc a, PathSegRef ref, PathFillCubic s) { + write_mem(a, ref.offset >> 2, PathSeg_FillCubic); + PathFillCubic_write(a, PathFillCubicRef(ref.offset + 4), s); } -void PathSeg_StrokeCubic_write(PathSegRef ref, PathStrokeCubic s) { - memory[ref.offset >> 2] = PathSeg_StrokeCubic; - PathStrokeCubic_write(PathStrokeCubicRef(ref.offset + 4), s); +void PathSeg_StrokeCubic_write(Alloc a, PathSegRef ref, PathStrokeCubic s) { + write_mem(a, ref.offset >> 2, PathSeg_StrokeCubic); + PathStrokeCubic_write(a, PathStrokeCubicRef(ref.offset + 4), s); } diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index eb21eac..4587f8f 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -171,12 +171,12 @@ CmdRef Cmd_index(CmdRef ref, uint index) { return CmdRef(ref.offset + index * Cmd_size); } -CmdCircle CmdCircle_read(CmdCircleRef ref) { +CmdCircle CmdCircle_read(Alloc a, CmdCircleRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; - uint raw2 = memory[ix + 2]; - uint raw3 = memory[ix + 3]; + 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); CmdCircle s; s.center = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.radius = uintBitsToFloat(raw2); @@ -184,39 +184,39 @@ CmdCircle CmdCircle_read(CmdCircleRef ref) { return s; } -void CmdCircle_write(CmdCircleRef ref, CmdCircle s) { +void CmdCircle_write(Alloc a, CmdCircleRef ref, CmdCircle s) { uint ix = ref.offset >> 2; - 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; + write_mem(a, ix + 0, floatBitsToUint(s.center.x)); + write_mem(a, ix + 1, floatBitsToUint(s.center.y)); + write_mem(a, ix + 2, floatBitsToUint(s.radius)); + write_mem(a, ix + 3, s.rgba_color); } -CmdLine CmdLine_read(CmdLineRef ref) { +CmdLine CmdLine_read(Alloc a, CmdLineRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; - uint raw2 = memory[ix + 2]; - uint raw3 = memory[ix + 3]; + 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); CmdLine s; s.start = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.end = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); return s; } -void CmdLine_write(CmdLineRef ref, CmdLine s) { +void CmdLine_write(Alloc a, CmdLineRef ref, CmdLine s) { uint ix = ref.offset >> 2; - 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); + write_mem(a, ix + 0, floatBitsToUint(s.start.x)); + write_mem(a, ix + 1, floatBitsToUint(s.start.y)); + write_mem(a, ix + 2, floatBitsToUint(s.end.x)); + write_mem(a, ix + 3, floatBitsToUint(s.end.y)); } -CmdStroke CmdStroke_read(CmdStrokeRef ref) { +CmdStroke CmdStroke_read(Alloc a, CmdStrokeRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; - uint raw2 = memory[ix + 2]; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); CmdStroke s; s.tile_ref = raw0; s.half_width = uintBitsToFloat(raw1); @@ -224,18 +224,18 @@ CmdStroke CmdStroke_read(CmdStrokeRef ref) { return s; } -void CmdStroke_write(CmdStrokeRef ref, CmdStroke s) { +void CmdStroke_write(Alloc a, CmdStrokeRef ref, CmdStroke s) { uint ix = ref.offset >> 2; - memory[ix + 0] = s.tile_ref; - memory[ix + 1] = floatBitsToUint(s.half_width); - memory[ix + 2] = s.rgba_color; + write_mem(a, ix + 0, s.tile_ref); + write_mem(a, ix + 1, floatBitsToUint(s.half_width)); + write_mem(a, ix + 2, s.rgba_color); } -CmdFill CmdFill_read(CmdFillRef ref) { +CmdFill CmdFill_read(Alloc a, CmdFillRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; - uint raw2 = memory[ix + 2]; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); CmdFill s; s.tile_ref = raw0; s.backdrop = int(raw1); @@ -243,189 +243,189 @@ CmdFill CmdFill_read(CmdFillRef ref) { return s; } -void CmdFill_write(CmdFillRef ref, CmdFill s) { +void CmdFill_write(Alloc a, CmdFillRef ref, CmdFill s) { uint ix = ref.offset >> 2; - memory[ix + 0] = s.tile_ref; - memory[ix + 1] = uint(s.backdrop); - memory[ix + 2] = s.rgba_color; + write_mem(a, ix + 0, s.tile_ref); + write_mem(a, ix + 1, uint(s.backdrop)); + write_mem(a, ix + 2, s.rgba_color); } -CmdBeginClip CmdBeginClip_read(CmdBeginClipRef ref) { +CmdBeginClip CmdBeginClip_read(Alloc a, CmdBeginClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); CmdBeginClip s; s.tile_ref = raw0; s.backdrop = int(raw1); return s; } -void CmdBeginClip_write(CmdBeginClipRef ref, CmdBeginClip s) { +void CmdBeginClip_write(Alloc a, CmdBeginClipRef ref, CmdBeginClip s) { uint ix = ref.offset >> 2; - memory[ix + 0] = s.tile_ref; - memory[ix + 1] = uint(s.backdrop); + write_mem(a, ix + 0, s.tile_ref); + write_mem(a, ix + 1, uint(s.backdrop)); } -CmdBeginSolidClip CmdBeginSolidClip_read(CmdBeginSolidClipRef ref) { +CmdBeginSolidClip CmdBeginSolidClip_read(Alloc a, CmdBeginSolidClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; + uint raw0 = read_mem(a, ix + 0); CmdBeginSolidClip s; s.alpha = uintBitsToFloat(raw0); return s; } -void CmdBeginSolidClip_write(CmdBeginSolidClipRef ref, CmdBeginSolidClip s) { +void CmdBeginSolidClip_write(Alloc a, CmdBeginSolidClipRef ref, CmdBeginSolidClip s) { uint ix = ref.offset >> 2; - memory[ix + 0] = floatBitsToUint(s.alpha); + write_mem(a, ix + 0, floatBitsToUint(s.alpha)); } -CmdEndClip CmdEndClip_read(CmdEndClipRef ref) { +CmdEndClip CmdEndClip_read(Alloc a, CmdEndClipRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; + uint raw0 = read_mem(a, ix + 0); CmdEndClip s; s.alpha = uintBitsToFloat(raw0); return s; } -void CmdEndClip_write(CmdEndClipRef ref, CmdEndClip s) { +void CmdEndClip_write(Alloc a, CmdEndClipRef ref, CmdEndClip s) { uint ix = ref.offset >> 2; - memory[ix + 0] = floatBitsToUint(s.alpha); + write_mem(a, ix + 0, floatBitsToUint(s.alpha)); } -CmdSolid CmdSolid_read(CmdSolidRef ref) { +CmdSolid CmdSolid_read(Alloc a, CmdSolidRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; + uint raw0 = read_mem(a, ix + 0); CmdSolid s; s.rgba_color = raw0; return s; } -void CmdSolid_write(CmdSolidRef ref, CmdSolid s) { +void CmdSolid_write(Alloc a, CmdSolidRef ref, CmdSolid s) { uint ix = ref.offset >> 2; - memory[ix + 0] = s.rgba_color; + write_mem(a, ix + 0, s.rgba_color); } -CmdSolidMask CmdSolidMask_read(CmdSolidMaskRef ref) { +CmdSolidMask CmdSolidMask_read(Alloc a, CmdSolidMaskRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; + uint raw0 = read_mem(a, ix + 0); CmdSolidMask s; s.mask = uintBitsToFloat(raw0); return s; } -void CmdSolidMask_write(CmdSolidMaskRef ref, CmdSolidMask s) { +void CmdSolidMask_write(Alloc a, CmdSolidMaskRef ref, CmdSolidMask s) { uint ix = ref.offset >> 2; - memory[ix + 0] = floatBitsToUint(s.mask); + write_mem(a, ix + 0, floatBitsToUint(s.mask)); } -CmdJump CmdJump_read(CmdJumpRef ref) { +CmdJump CmdJump_read(Alloc a, CmdJumpRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; + uint raw0 = read_mem(a, ix + 0); CmdJump s; s.new_ref = raw0; return s; } -void CmdJump_write(CmdJumpRef ref, CmdJump s) { +void CmdJump_write(Alloc a, CmdJumpRef ref, CmdJump s) { uint ix = ref.offset >> 2; - memory[ix + 0] = s.new_ref; + write_mem(a, ix + 0, s.new_ref); } -uint Cmd_tag(CmdRef ref) { - return memory[ref.offset >> 2]; +uint Cmd_tag(Alloc a, CmdRef ref) { + return read_mem(a, ref.offset >> 2); } -CmdCircle Cmd_Circle_read(CmdRef ref) { - return CmdCircle_read(CmdCircleRef(ref.offset + 4)); +CmdCircle Cmd_Circle_read(Alloc a, CmdRef ref) { + return CmdCircle_read(a, CmdCircleRef(ref.offset + 4)); } -CmdLine Cmd_Line_read(CmdRef ref) { - return CmdLine_read(CmdLineRef(ref.offset + 4)); +CmdLine Cmd_Line_read(Alloc a, CmdRef ref) { + return CmdLine_read(a, CmdLineRef(ref.offset + 4)); } -CmdFill Cmd_Fill_read(CmdRef ref) { - return CmdFill_read(CmdFillRef(ref.offset + 4)); +CmdFill Cmd_Fill_read(Alloc a, CmdRef ref) { + return CmdFill_read(a, CmdFillRef(ref.offset + 4)); } -CmdBeginClip Cmd_BeginClip_read(CmdRef ref) { - return CmdBeginClip_read(CmdBeginClipRef(ref.offset + 4)); +CmdBeginClip Cmd_BeginClip_read(Alloc a, CmdRef ref) { + return CmdBeginClip_read(a, CmdBeginClipRef(ref.offset + 4)); } -CmdBeginSolidClip Cmd_BeginSolidClip_read(CmdRef ref) { - return CmdBeginSolidClip_read(CmdBeginSolidClipRef(ref.offset + 4)); +CmdBeginSolidClip Cmd_BeginSolidClip_read(Alloc a, CmdRef ref) { + return CmdBeginSolidClip_read(a, CmdBeginSolidClipRef(ref.offset + 4)); } -CmdEndClip Cmd_EndClip_read(CmdRef ref) { - return CmdEndClip_read(CmdEndClipRef(ref.offset + 4)); +CmdEndClip Cmd_EndClip_read(Alloc a, CmdRef ref) { + return CmdEndClip_read(a, CmdEndClipRef(ref.offset + 4)); } -CmdStroke Cmd_Stroke_read(CmdRef ref) { - return CmdStroke_read(CmdStrokeRef(ref.offset + 4)); +CmdStroke Cmd_Stroke_read(Alloc a, CmdRef ref) { + return CmdStroke_read(a, CmdStrokeRef(ref.offset + 4)); } -CmdSolid Cmd_Solid_read(CmdRef ref) { - return CmdSolid_read(CmdSolidRef(ref.offset + 4)); +CmdSolid Cmd_Solid_read(Alloc a, CmdRef ref) { + return CmdSolid_read(a, CmdSolidRef(ref.offset + 4)); } -CmdSolidMask Cmd_SolidMask_read(CmdRef ref) { - return CmdSolidMask_read(CmdSolidMaskRef(ref.offset + 4)); +CmdSolidMask Cmd_SolidMask_read(Alloc a, CmdRef ref) { + return CmdSolidMask_read(a, CmdSolidMaskRef(ref.offset + 4)); } -CmdJump Cmd_Jump_read(CmdRef ref) { - return CmdJump_read(CmdJumpRef(ref.offset + 4)); +CmdJump Cmd_Jump_read(Alloc a, CmdRef ref) { + return CmdJump_read(a, CmdJumpRef(ref.offset + 4)); } -void Cmd_End_write(CmdRef ref) { - memory[ref.offset >> 2] = Cmd_End; +void Cmd_End_write(Alloc a, CmdRef ref) { + write_mem(a, ref.offset >> 2, Cmd_End); } -void Cmd_Circle_write(CmdRef ref, CmdCircle s) { - memory[ref.offset >> 2] = Cmd_Circle; - CmdCircle_write(CmdCircleRef(ref.offset + 4), s); +void Cmd_Circle_write(Alloc a, CmdRef ref, CmdCircle s) { + write_mem(a, ref.offset >> 2, Cmd_Circle); + CmdCircle_write(a, CmdCircleRef(ref.offset + 4), s); } -void Cmd_Line_write(CmdRef ref, CmdLine s) { - memory[ref.offset >> 2] = Cmd_Line; - CmdLine_write(CmdLineRef(ref.offset + 4), s); +void Cmd_Line_write(Alloc a, CmdRef ref, CmdLine s) { + write_mem(a, ref.offset >> 2, Cmd_Line); + CmdLine_write(a, CmdLineRef(ref.offset + 4), s); } -void Cmd_Fill_write(CmdRef ref, CmdFill s) { - memory[ref.offset >> 2] = Cmd_Fill; - CmdFill_write(CmdFillRef(ref.offset + 4), s); +void Cmd_Fill_write(Alloc a, CmdRef ref, CmdFill s) { + write_mem(a, ref.offset >> 2, Cmd_Fill); + CmdFill_write(a, CmdFillRef(ref.offset + 4), s); } -void Cmd_BeginClip_write(CmdRef ref, CmdBeginClip s) { - memory[ref.offset >> 2] = Cmd_BeginClip; - CmdBeginClip_write(CmdBeginClipRef(ref.offset + 4), s); +void Cmd_BeginClip_write(Alloc a, CmdRef ref, CmdBeginClip s) { + write_mem(a, ref.offset >> 2, Cmd_BeginClip); + CmdBeginClip_write(a, CmdBeginClipRef(ref.offset + 4), s); } -void Cmd_BeginSolidClip_write(CmdRef ref, CmdBeginSolidClip s) { - memory[ref.offset >> 2] = Cmd_BeginSolidClip; - CmdBeginSolidClip_write(CmdBeginSolidClipRef(ref.offset + 4), s); +void Cmd_BeginSolidClip_write(Alloc a, CmdRef ref, CmdBeginSolidClip s) { + write_mem(a, ref.offset >> 2, Cmd_BeginSolidClip); + CmdBeginSolidClip_write(a, CmdBeginSolidClipRef(ref.offset + 4), s); } -void Cmd_EndClip_write(CmdRef ref, CmdEndClip s) { - memory[ref.offset >> 2] = Cmd_EndClip; - CmdEndClip_write(CmdEndClipRef(ref.offset + 4), s); +void Cmd_EndClip_write(Alloc a, CmdRef ref, CmdEndClip s) { + write_mem(a, ref.offset >> 2, Cmd_EndClip); + CmdEndClip_write(a, CmdEndClipRef(ref.offset + 4), s); } -void Cmd_Stroke_write(CmdRef ref, CmdStroke s) { - memory[ref.offset >> 2] = Cmd_Stroke; - CmdStroke_write(CmdStrokeRef(ref.offset + 4), s); +void Cmd_Stroke_write(Alloc a, CmdRef ref, CmdStroke s) { + write_mem(a, ref.offset >> 2, Cmd_Stroke); + CmdStroke_write(a, CmdStrokeRef(ref.offset + 4), s); } -void Cmd_Solid_write(CmdRef ref, CmdSolid s) { - memory[ref.offset >> 2] = Cmd_Solid; - CmdSolid_write(CmdSolidRef(ref.offset + 4), s); +void Cmd_Solid_write(Alloc a, CmdRef ref, CmdSolid s) { + write_mem(a, ref.offset >> 2, Cmd_Solid); + CmdSolid_write(a, CmdSolidRef(ref.offset + 4), s); } -void Cmd_SolidMask_write(CmdRef ref, CmdSolidMask s) { - memory[ref.offset >> 2] = Cmd_SolidMask; - CmdSolidMask_write(CmdSolidMaskRef(ref.offset + 4), s); +void Cmd_SolidMask_write(Alloc a, CmdRef ref, CmdSolidMask s) { + write_mem(a, ref.offset >> 2, Cmd_SolidMask); + CmdSolidMask_write(a, CmdSolidMaskRef(ref.offset + 4), s); } -void Cmd_Jump_write(CmdRef ref, CmdJump s) { - memory[ref.offset >> 2] = Cmd_Jump; - CmdJump_write(CmdJumpRef(ref.offset + 4), s); +void Cmd_Jump_write(Alloc a, CmdRef ref, CmdJump s) { + write_mem(a, ref.offset >> 2, Cmd_Jump); + CmdJump_write(a, CmdJumpRef(ref.offset + 4), s); } diff --git a/piet-gpu/shader/setup.h b/piet-gpu/shader/setup.h index bcfa510..5a4935c 100644 --- a/piet-gpu/shader/setup.h +++ b/piet-gpu/shader/setup.h @@ -30,9 +30,9 @@ struct Config { uint n_pathseg; uint width_in_tiles; uint height_in_tiles; - uint tile_base; - uint bin_base; - uint ptcl_base; - uint pathseg_base; - uint anno_base; + Alloc tile_alloc; + Alloc bin_alloc; + Alloc ptcl_alloc; + Alloc pathseg_alloc; + Alloc anno_alloc; }; diff --git a/piet-gpu/shader/tile.h b/piet-gpu/shader/tile.h index 133ff53..500277b 100644 --- a/piet-gpu/shader/tile.h +++ b/piet-gpu/shader/tile.h @@ -49,48 +49,48 @@ TileSegRef TileSeg_index(TileSegRef ref, uint index) { return TileSegRef(ref.offset + index * TileSeg_size); } -Path Path_read(PathRef ref) { +Path Path_read(Alloc a, PathRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; - uint raw2 = memory[ix + 2]; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); Path s; s.bbox = uvec4(raw0 & 0xffff, raw0 >> 16, raw1 & 0xffff, raw1 >> 16); s.tiles = TileRef(raw2); return s; } -void Path_write(PathRef ref, Path s) { +void Path_write(Alloc a, PathRef ref, Path s) { uint ix = ref.offset >> 2; - 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; + write_mem(a, ix + 0, s.bbox.x | (s.bbox.y << 16)); + write_mem(a, ix + 1, s.bbox.z | (s.bbox.w << 16)); + write_mem(a, ix + 2, s.tiles.offset); } -Tile Tile_read(TileRef ref) { +Tile Tile_read(Alloc a, TileRef ref) { uint ix = ref.offset >> 2; - uint raw0 = memory[ix + 0]; - uint raw1 = memory[ix + 1]; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); Tile s; s.tile = TileSegRef(raw0); s.backdrop = int(raw1); return s; } -void Tile_write(TileRef ref, Tile s) { +void Tile_write(Alloc a, TileRef ref, Tile s) { uint ix = ref.offset >> 2; - memory[ix + 0] = s.tile.offset; - memory[ix + 1] = uint(s.backdrop); + write_mem(a, ix + 0, s.tile.offset); + write_mem(a, ix + 1, uint(s.backdrop)); } -TileSeg TileSeg_read(TileSegRef ref) { +TileSeg TileSeg_read(Alloc a, TileSegRef ref) { uint ix = ref.offset >> 2; - 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 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); TileSeg s; s.origin = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); s.vector = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); @@ -99,13 +99,13 @@ TileSeg TileSeg_read(TileSegRef ref) { return s; } -void TileSeg_write(TileSegRef ref, TileSeg s) { +void TileSeg_write(Alloc a, TileSegRef ref, TileSeg s) { uint ix = ref.offset >> 2; - 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; + write_mem(a, ix + 0, floatBitsToUint(s.origin.x)); + write_mem(a, ix + 1, floatBitsToUint(s.origin.y)); + write_mem(a, ix + 2, floatBitsToUint(s.vector.x)); + write_mem(a, ix + 3, floatBitsToUint(s.vector.y)); + write_mem(a, ix + 4, floatBitsToUint(s.y_edge)); + write_mem(a, ix + 5, s.next.offset); } diff --git a/piet-gpu/shader/tile_alloc.comp b/piet-gpu/shader/tile_alloc.comp index 6588227..f0d42da 100644 --- a/piet-gpu/shader/tile_alloc.comp +++ b/piet-gpu/shader/tile_alloc.comp @@ -5,8 +5,8 @@ #version 450 #extension GL_GOOGLE_include_directive : enable -#include "setup.h" #include "mem.h" +#include "setup.h" #define LG_TILE_ALLOC_WG (7 + LG_WG_FACTOR) #define TILE_ALLOC_WG (1 << LG_TILE_ALLOC_WG) @@ -25,21 +25,21 @@ layout(set = 0, binding = 1) readonly buffer ConfigBuf { #define SY (1.0 / float(TILE_HEIGHT_PX)) shared uint sh_tile_count[TILE_ALLOC_WG]; -shared Alloc sh_tile_alloc; +shared MallocResult sh_tile_alloc; void main() { - if (mem_overflow) { + if (mem_error != NO_ERROR) { return; } uint th_ix = gl_LocalInvocationID.x; uint element_ix = gl_GlobalInvocationID.x; - PathRef path_ref = PathRef(conf.tile_base + element_ix * Path_size); - AnnotatedRef ref = AnnotatedRef(conf.anno_base + element_ix * Annotated_size); + PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size); + AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); uint tag = Annotated_Nop; if (element_ix < conf.n_elements) { - tag = Annotated_tag(ref); + tag = Annotated_tag(conf.anno_alloc, ref); } int x0 = 0, y0 = 0, x1 = 0, y1 = 0; switch (tag) { @@ -49,7 +49,7 @@ void main() { case Annotated_EndClip: // Note: we take advantage of the fact that fills, strokes, and // clips have compatible layout. - AnnoFill fill = Annotated_Fill_read(ref); + AnnoFill fill = Annotated_Fill_read(conf.anno_alloc, ref); x0 = int(floor(fill.bbox.x * SX)); y0 = int(floor(fill.bbox.y * SY)); x1 = int(ceil(fill.bbox.z * SX)); @@ -71,36 +71,38 @@ void main() { } sh_tile_count[th_ix] = tile_count; + uint total_tile_count = tile_count; // Prefix sum of sh_tile_count for (uint i = 0; i < LG_TILE_ALLOC_WG; i++) { barrier(); if (th_ix >= (1 << i)) { - tile_count += sh_tile_count[th_ix - (1 << i)]; + total_tile_count += sh_tile_count[th_ix - (1 << i)]; } barrier(); - sh_tile_count[th_ix] = tile_count; + sh_tile_count[th_ix] = total_tile_count; } if (th_ix == TILE_ALLOC_WG - 1) { - sh_tile_alloc = malloc(tile_count * Tile_size); + sh_tile_alloc = malloc(total_tile_count * Tile_size); } barrier(); - Alloc alloc_start = sh_tile_alloc; + MallocResult alloc_start = sh_tile_alloc; if (alloc_start.failed) { return; } if (element_ix < conf.n_elements) { uint tile_subix = th_ix > 0 ? sh_tile_count[th_ix - 1] : 0; - path.tiles = TileRef(alloc_start.offset + Tile_size * tile_subix); - Path_write(path_ref, path); + Alloc tiles_alloc = slice_mem(alloc_start.alloc, Tile_size * tile_subix, Tile_size * tile_count); + path.tiles = TileRef(tiles_alloc.offset); + Path_write(conf.tile_alloc, 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.offset >> 2; + uint start_ix = alloc_start.alloc.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. - memory[start_ix + i] = 0; + write_mem(alloc_start.alloc, start_ix + i, 0); } } diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv index 7a80ad75eee4ebfd3ea232d2f16dd4cf2de5df42..b2563928f58bd73eb5a8497a9b716df985184391 100644 GIT binary patch literal 14668 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 literal 10620 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