From 121f29fef68fec89079132411586a0bbfe3ae00a Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Sat, 30 May 2020 08:35:26 -0700 Subject: [PATCH] Merge one segment at a time No parallelism yet, but seems to improve performance. --- piet-gpu-hal/src/vulkan.rs | 2 +- piet-gpu/bin/cli.rs | 4 +- piet-gpu/shader/binning.comp | 256 +++++++++++++++++------------------ piet-gpu/shader/binning.spv | Bin 16024 -> 15652 bytes piet-gpu/shader/coarse.comp | 73 ++-------- piet-gpu/shader/coarse.spv | Bin 49240 -> 46048 bytes piet-gpu/src/lib.rs | 13 +- 7 files changed, 144 insertions(+), 204 deletions(-) diff --git a/piet-gpu-hal/src/vulkan.rs b/piet-gpu-hal/src/vulkan.rs index 402b13d..2fac015 100644 --- a/piet-gpu-hal/src/vulkan.rs +++ b/piet-gpu-hal/src/vulkan.rs @@ -1016,7 +1016,7 @@ unsafe fn choose_compute_device( devices: &[vk::PhysicalDevice], surface: Option<&VkSurface>, ) -> Option<(vk::PhysicalDevice, u32)> { - for pdevice in &devices[1..] { + for pdevice in devices { let props = instance.get_physical_device_queue_family_properties(*pdevice); for (ix, info) in props.iter().enumerate() { // Check for surface presentation support diff --git a/piet-gpu/bin/cli.rs b/piet-gpu/bin/cli.rs index cc4cb44..5d7c09e 100644 --- a/piet-gpu/bin/cli.rs +++ b/piet-gpu/bin/cli.rs @@ -181,10 +181,12 @@ fn main() -> Result<(), Error> { println!("Coarse kernel time: {:.3}ms", (ts[2] - ts[1]) * 1e3); println!("Render kernel time: {:.3}ms", (ts[3] - ts[2]) * 1e3); + /* let mut data: Vec = Default::default(); - device.read_buffer(&renderer.bin_buf, &mut data).unwrap(); + device.read_buffer(&renderer.ptcl_buf, &mut data).unwrap(); piet_gpu::dump_k1_data(&data); //trace_ptcl(&data); + */ let mut img_data: Vec = Default::default(); // Note: because png can use a `&[u8]` slice, we could avoid an extra copy diff --git a/piet-gpu/shader/binning.comp b/piet-gpu/shader/binning.comp index d193dd2..d35c2d9 100644 --- a/piet-gpu/shader/binning.comp +++ b/piet-gpu/shader/binning.comp @@ -43,7 +43,6 @@ layout(set = 0, binding = 3) buffer BinsBuf { // Note: cudaraster has N_TILE + 1 to cut down on bank conflicts. shared uint bitmaps[N_SLICE][N_TILE]; shared uint count[N_SLICE][N_TILE]; -shared uint sh_my_tile; shared uint sh_chunk_start[N_TILE]; shared float sh_right_edge[N_TILE]; @@ -57,145 +56,138 @@ uint state_right_edge_index(uint partition_ix) { void main() { uint chunk_n = 0; uint my_n_elements = n_elements; - while (true) { - if (gl_LocalInvocationID.x == 0) { - sh_my_tile = atomicAdd(tile_ix, 1); + uint my_partition = gl_WorkGroupID.x; + + for (uint i = 0; i < N_SLICE; i++) { + bitmaps[i][gl_LocalInvocationID.x] = 0; + } + barrier(); + + // Read inputs and determine coverage of bins + uint element_ix = my_partition * N_TILE + gl_LocalInvocationID.x; + AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); + uint tag = Annotated_Nop; + if (element_ix < my_n_elements) { + tag = Annotated_tag(ref); + } + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + float my_right_edge = INFINITY; + bool crosses_edge = false; + switch (tag) { + case Annotated_FillLine: + case Annotated_StrokeLine: + AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); + x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x) * SX)); + y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y) * SY)); + x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x) * SX)); + y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y) * SY)); + crosses_edge = tag == Annotated_FillLine && ceil(line.p0.y * TSY) != ceil(line.p1.y * TSY); + break; + case Annotated_Fill: + case Annotated_Stroke: + // Note: we take advantage of the fact that fills and strokes + // have compatible layout. + AnnoFill fill = Annotated_Fill_read(ref); + x0 = int(floor(fill.bbox.x * SX)); + y0 = int(floor(fill.bbox.y * SY)); + x1 = int(ceil(fill.bbox.z * SX)); + y1 = int(ceil(fill.bbox.w * SY)); + // It probably makes more sense to track x1, to avoid having to redo + // the rounding to tile coords. + my_right_edge = fill.bbox.z; + break; + } + + // If the last element in this partition is a fill edge, then we need to do a + // look-forward to find the right edge of its corresponding fill. That data is + // recorded in aggregates computed in the element processing pass. + if (gl_LocalInvocationID.x == N_TILE - 1 && tag == Annotated_FillLine) { + uint aggregate_ix = (my_partition + 1) * ELEMENT_BINNING_RATIO; + // This is sequential but the expectation is that the amount of + // look-forward is small (performance may degrade in the case + // of massively complex paths). + do { + my_right_edge = uintBitsToFloat(state[state_right_edge_index(aggregate_ix)]); + aggregate_ix++; + } while (isinf(my_right_edge)); + } + + // Now propagate right_edge backward, from fill to segment. + for (uint i = 0; i < LG_N_TILE; i++) { + // Note: we could try to cut down on write bandwidth here if the value hasn't + // changed, but not sure it's worth the complexity to track. + sh_right_edge[gl_LocalInvocationID.x] = my_right_edge; + barrier(); + if (gl_LocalInvocationID.x + (1 << i) < N_TILE && isinf(my_right_edge)) { + my_right_edge = sh_right_edge[gl_LocalInvocationID.x + (1 << i)]; } barrier(); - uint my_tile = sh_my_tile; - if (my_tile * N_TILE >= my_n_elements) { - break; - } + } + if (crosses_edge) { + x1 = int(ceil(my_right_edge * SX)); + } - for (uint i = 0; i < N_SLICE; i++) { - bitmaps[i][gl_LocalInvocationID.x] = 0; + // At this point, we run an iterator over the coverage area, + // trying to keep divergence low. + // Right now, it's just a bbox, but we'll get finer with + // segments. + x0 = clamp(x0, 0, N_TILE_X); + x1 = clamp(x1, x0, N_TILE_X); + y0 = clamp(y0, 0, N_TILE_Y); + y1 = clamp(y1, y0, N_TILE_Y); + if (x0 == x1) y1 = y0; + int x = x0, y = y0; + uint my_slice = gl_LocalInvocationID.x / 32; + uint my_mask = 1 << (gl_LocalInvocationID.x & 31); + while (y < y1) { + atomicOr(bitmaps[my_slice][y * N_TILE_X + x], my_mask); + x++; + if (x == x1) { + x = x0; + y++; } - barrier(); + } - // Read inputs and determine coverage of bins - uint element_ix = my_tile * N_TILE + gl_LocalInvocationID.x; - AnnotatedRef ref = AnnotatedRef(element_ix * Annotated_size); - uint tag = Annotated_Nop; - if (element_ix < my_n_elements) { - tag = Annotated_tag(ref); - } - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; - float my_right_edge = INFINITY; - bool crosses_edge = false; - switch (tag) { - case Annotated_FillLine: - case Annotated_StrokeLine: - AnnoStrokeLineSeg line = Annotated_StrokeLine_read(ref); - x0 = int(floor((min(line.p0.x, line.p1.x) - line.stroke.x) * SX)); - y0 = int(floor((min(line.p0.y, line.p1.y) - line.stroke.y) * SY)); - x1 = int(ceil((max(line.p0.x, line.p1.x) + line.stroke.x) * SX)); - y1 = int(ceil((max(line.p0.y, line.p1.y) + line.stroke.y) * SY)); - crosses_edge = tag == Annotated_FillLine && ceil(line.p0.y * TSY) != ceil(line.p1.y * TSY); - break; - case Annotated_Fill: - case Annotated_Stroke: - // Note: we take advantage of the fact that fills and strokes - // have compatible layout. - AnnoFill fill = Annotated_Fill_read(ref); - x0 = int(floor(fill.bbox.x * SX)); - y0 = int(floor(fill.bbox.y * SY)); - x1 = int(ceil(fill.bbox.z * SX)); - y1 = int(ceil(fill.bbox.w * SY)); - // It probably makes more sense to track x1, to avoid having to redo - // the rounding to tile coords. - my_right_edge = fill.bbox.z; - break; - } + barrier(); + // Allocate output segments. + uint element_count = 0; + for (uint i = 0; i < N_SLICE; i++) { + element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); + count[i][gl_LocalInvocationID.x] = element_count; + } + // element_count is number of elements covering bin for this invocation. + uint chunk_start = 0; + if (element_count != 0) { + // TODO: aggregate atomic adds (subgroup is probably fastest) + chunk_start = atomicAdd(alloc, element_count * BinInstance_size); + sh_chunk_start[gl_LocalInvocationID.x] = chunk_start; + } + // Note: it might be more efficient for reading to do this in the + // other order (each bin is a contiguous sequence of partitions) + uint out_ix = (my_partition * N_TILE + gl_LocalInvocationID.x) * 2; + bins[out_ix] = element_count; + bins[out_ix + 1] = chunk_start; - // If the last element in this partition is a fill edge, then we need to do a - // look-forward to find the right edge of its corresponding fill. That data is - // recorded in aggregates computed in the element processing pass. - if (gl_LocalInvocationID.x == N_TILE - 1 && tag == Annotated_FillLine) { - uint aggregate_ix = (my_tile + 1) * ELEMENT_BINNING_RATIO; - // This is sequential but the expectation is that the amount of - // look-forward is small (performance may degrade in the case - // of massively complex paths). - do { - my_right_edge = uintBitsToFloat(state[state_right_edge_index(aggregate_ix)]); - aggregate_ix++; - } while (isinf(my_right_edge)); - } - - // Now propagate right_edge backward, from fill to segment. - for (uint i = 0; i < LG_N_TILE; i++) { - // Note: we could try to cut down on write bandwidth here if the value hasn't - // changed, but not sure it's worth the complexity to track. - sh_right_edge[gl_LocalInvocationID.x] = my_right_edge; - barrier(); - if (gl_LocalInvocationID.x + (1 << i) < N_TILE && isinf(my_right_edge)) { - my_right_edge = sh_right_edge[gl_LocalInvocationID.x + (1 << i)]; + barrier(); + // Use similar strategy as Laine & Karras paper; loop over bbox of bins + // touched by this element + x = x0; + y = y0; + while (y < y1) { + uint bin_ix = y * N_TILE_X + x; + uint out_mask = bitmaps[my_slice][bin_ix]; + if ((out_mask & my_mask) != 0) { + uint idx = bitCount(out_mask & (my_mask - 1)); + if (my_slice > 0) { + idx += count[my_slice - 1][bin_ix]; } - barrier(); + uint out_offset = sh_chunk_start[bin_ix] + idx * BinInstance_size; + BinInstance_write(BinInstanceRef(out_offset), BinInstance(element_ix, my_right_edge)); } - if (crosses_edge) { - x1 = int(ceil(my_right_edge * SX)); - } - - // At this point, we run an iterator over the coverage area, - // trying to keep divergence low. - // Right now, it's just a bbox, but we'll get finer with - // segments. - x0 = clamp(x0, 0, N_TILE_X); - x1 = clamp(x1, x0, N_TILE_X); - y0 = clamp(y0, 0, N_TILE_Y); - y1 = clamp(y1, y0, N_TILE_Y); - if (x0 == x1) y1 = y0; - int x = x0, y = y0; - uint my_slice = gl_LocalInvocationID.x / 32; - uint my_mask = 1 << (gl_LocalInvocationID.x & 31); - while (y < y1) { - atomicOr(bitmaps[my_slice][y * N_TILE_X + x], my_mask); - x++; - if (x == x1) { - x = x0; - y++; - } - } - - barrier(); - // Allocate output segments. - uint element_count = 0; - for (uint i = 0; i < N_SLICE; i++) { - element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); - count[i][gl_LocalInvocationID.x] = element_count; - } - // element_count is number of elements covering bin for this invocation. - uint chunk_start = 0; - if (element_count != 0) { - // TODO: aggregate atomic adds (subgroup is probably fastest) - chunk_start = atomicAdd(alloc, element_count * BinInstance_size); - sh_chunk_start[gl_LocalInvocationID.x] = chunk_start; - } - uint out_ix = (my_tile * N_TILE + gl_LocalInvocationID.x) * 2; - bins[out_ix] = element_count; - bins[out_ix + 1] = chunk_start; - - barrier(); - // Use similar strategy as Laine & Karras paper; loop over bbox of bins - // touched by this element - x = x0; - y = y0; - while (y < y1) { - uint bin_ix = y * N_TILE_X + x; - uint out_mask = bitmaps[my_slice][bin_ix]; - if ((out_mask & my_mask) != 0) { - uint idx = bitCount(out_mask & (my_mask - 1)); - 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, my_right_edge)); - } - x++; - if (x == x1) { - x = x0; - y++; - } + x++; + if (x == x1) { + x = x0; + y++; } } } diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 9f22a33b7a8812ba864f15fbee6d4b490ce04a31..6ea087746cc4641eb1746f46597823f20f338685 100644 GIT binary patch literal 15652 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 bcB|+2ukXUuzC+1eeGhyNMW1Udca8rKw{Co` literal 16024 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 0 ? - BinInstance_read(BinInstanceRef(start_chunk + BinChunk_size)).element_ix : ~0; - } if (th_ix < N_SLICE) { sh_bd_sign[th_ix] = 0; } @@ -138,47 +122,11 @@ void main() { sh_is_segment[th_ix] = 0; } - while (wr_ix - rd_ix <= N_TILE) { - // Choose segment with least element. - uint my_min; - if (th_ix < N_WG) { - if (th_ix == 0) { - sh_selected_n = 0; - sh_min_buf = ~0; - } - } - barrier(); - // Tempting to do this with subgroups, but atomic should be good enough. - if (th_ix < N_WG) { - my_min = sh_first_el[th_ix]; - atomicMin(sh_min_buf, my_min); - } - barrier(); - if (th_ix < N_WG) { - if (my_min == sh_min_buf && my_min != ~0) { - sh_elements_ref = sh_chunk[th_ix] + BinChunk_size; - uint selected_n = sh_chunk_n[th_ix]; - sh_selected_n = selected_n; - uint next_chunk = sh_chunk_next[th_ix]; - if (next_chunk == 0) { - sh_first_el[th_ix] = ~0; - } else { - sh_chunk[th_ix] = next_chunk; - BinChunk chunk = BinChunk_read(BinChunkRef(next_chunk)); - sh_chunk_n[th_ix] = chunk.n; - sh_chunk_next[th_ix] = chunk.next.offset; - sh_first_el[th_ix] = BinInstance_read( - BinInstanceRef(next_chunk + BinChunk_size)).element_ix; - } - } - } - barrier(); - uint chunk_n = sh_selected_n; - if (chunk_n == 0) { - // All chunks consumed - break; - } - BinInstanceRef inst_ref = BinInstanceRef(sh_elements_ref); + while (wr_ix - rd_ix <= N_TILE && partition_ix * N_TILE < my_n_elements) { + uint in_ix = (partition_ix * N_TILE + bin_ix) * 2; + uint chunk_n = bins[in_ix]; + uint elements_ref = bins[in_ix + 1]; + BinInstanceRef inst_ref = BinInstanceRef(elements_ref); if (th_ix < chunk_n) { BinInstance inst = BinInstance_read(BinInstance_index(inst_ref, th_ix)); uint wr_el_ix = (wr_ix + th_ix) % N_RINGBUF; @@ -186,6 +134,7 @@ void main() { sh_right_edge[wr_el_ix] = inst.right_edge; } wr_ix += chunk_n; + partition_ix++; } barrier(); diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index b58a3274efdbbcf459803bf752365656e4568355..a3dd59ec043b0771053ede1211b7504eb3bf7e93 100644 GIT binary patch literal 46048 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 literal 49240 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 vp9EG Renderer { let state_buf = device.create_buffer(1 * 1024 * 1024, dev)?; let anno_buf = device.create_buffer(64 * 1024 * 1024, dev)?; - let bin_buf = device.create_buffer(64 * 1024 * 1024, host)?; + let bin_buf = device.create_buffer(64 * 1024 * 1024, dev)?; let ptcl_buf = device.create_buffer(48 * 1024 * 1024, dev)?; let image_dev = device.create_image2d(WIDTH as u32, HEIGHT as u32, dev)?; @@ -192,12 +192,13 @@ impl Renderer { &[], )?; - let coarse_alloc_buf_host = device.create_buffer(4, host)?; - let coarse_alloc_buf_dev = device.create_buffer(4, dev)?; + let coarse_alloc_buf_host = device.create_buffer(8, host)?; + let coarse_alloc_buf_dev = device.create_buffer(8, dev)?; let coarse_alloc_start = WIDTH_IN_TILES * HEIGHT_IN_TILES * PTCL_INITIAL_ALLOC; device .write_buffer(&coarse_alloc_buf_host, &[ + n_elements as u32, coarse_alloc_start as u32, ]) ?; @@ -264,26 +265,22 @@ impl Renderer { cmd_buf.dispatch( &self.bin_pipeline, &self.bin_ds, - (N_WG, 1, 1), + (((self.n_elements + 255) / 256) as u32, 1, 1), ); cmd_buf.write_timestamp(&query_pool, 2); cmd_buf.memory_barrier(); - /* cmd_buf.dispatch( &self.coarse_pipeline, &self.coarse_ds, (WIDTH as u32 / 256, HEIGHT as u32 / 256, 1), ); - */ cmd_buf.write_timestamp(&query_pool, 3); cmd_buf.memory_barrier(); - /* cmd_buf.dispatch( &self.k4_pipeline, &self.k4_ds, ((WIDTH / TILE_W) as u32, (HEIGHT / TILE_H) as u32, 1), ); - */ cmd_buf.write_timestamp(&query_pool, 4); cmd_buf.memory_barrier(); cmd_buf.image_barrier(&self.image_dev, ImageLayout::General, ImageLayout::BlitSrc);