// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #extension GL_KHR_shader_subgroup_arithmetic : enable layout(local_size_x = 256, local_size_y = 1) in; layout(std430) buffer; #include layout(binding = 0) readonly buffer Cubics { uint count; CubicData data[]; } cubics; layout(binding = 1) buffer Quads { uint count; QuadData data[]; } quads; layout(binding = 2) buffer Lines { uint count; LineData data[]; } lines; layout(binding = 3) buffer Components { uint count; PathComponent data[]; } components; layout(binding = 4) buffer Polyline { uint count; vec2 data[]; } polyline; uniform Config { float cubic_accuracy; float quad_tolerance; } config; shared uvec2 cubic_ranges[512]; shared uvec2 quad_ranges[512]; shared uint scratch_count[512]; shared uint scratch_sum[512]; uint ComputePosition(uint index) { uint sum = scratch_sum[index]; for (uint position = gl_SubgroupSize - 1; position < index; position += gl_SubgroupSize) { sum += scratch_sum[position] + scratch_count[position]; } return sum; } void ProcessCubic(uint ident) { CubicData cubic; uint quad_count = 0; if (ident < cubics.count) { cubic = cubics.data[ident]; quad_count = EstimateQuadraticCount(cubic, config.cubic_accuracy); scratch_count[ident] = quad_count; } barrier(); uint offset = 0; if (quad_count > 0) { scratch_sum[ident] = subgroupExclusiveAdd(scratch_count[ident]); offset = ComputePosition(ident) + quads.count; } barrier(); if (quad_count > 0) { atomicAdd(quads.count, quad_count); cubic_ranges[ident] = uvec2(offset, quad_count); for (uint i = 0; i < quad_count; i++) { quads.data[offset + i] = GenerateQuadraticFromCubic(cubic, i, quad_count); } } } void ProcessQuad(uint ident) { QuadData quad; QuadDecomposition decomposition; if (ident < quads.count) { quad = quads.data[ident]; decomposition = DecomposeQuad(quad, config.quad_tolerance); scratch_count[ident] = decomposition.line_count; } barrier(); uint offset = 0; if (decomposition.line_count > 0) { scratch_sum[ident] = subgroupExclusiveAdd(scratch_count[ident]); offset = ComputePosition(ident) + lines.count; } barrier(); if (decomposition.line_count > 0) { atomicAdd(lines.count, decomposition.line_count); quad_ranges[ident] = uvec2(offset, decomposition.line_count); vec2 last_point = quad.p1; for (uint i = 1; i < decomposition.line_count; i++) { LineData line = LineData(last_point, GenerateLineFromQuad(quad, i, decomposition)); last_point = line.p2; lines.data[offset + i - 1] = line; } lines.data[offset + decomposition.line_count - 1] = LineData(last_point, quad.p2); } } void ProcessLine(uint ident) { if (ident == 0) { polyline.count = lines.count + 1; } PathComponent component; uvec2 range = uvec2(0, 0); if (ident < components.count) { component = components.data[ident]; if (component.count == 4) { // Determine location in quads uvec2 quad_range = cubic_ranges[component.index]; uvec2 end_range = quad_ranges[quad_range.x + quad_range.y - 1]; range.x = quad_ranges[quad_range.x].x; range.y = end_range.x + end_range.y - range.x; } else if (component.count == 3) { range = quad_ranges[component.index]; } else if (component.count == 2) { range = uvec2(component.index, 1); } scratch_count[ident] = range.y; } barrier(); if (ident < components.count) { scratch_sum[ident] = subgroupExclusiveAdd(scratch_count[ident]); uint offset = ComputePosition(ident); polyline.data[offset] = lines.data[range.x].p1; for (uint i = 0; i < range.y; i++) { polyline.data[offset + i + 1] = lines.data[range.x + i].p2; } } } void main() { uint ident = gl_GlobalInvocationID.x; // Turn each cubic into quads. ProcessCubic(ident); barrier(); // Turn each quad into lines. ProcessQuad(ident); barrier(); // Copy lines to the output buffer. ProcessLine(ident); }