gdsdiff 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. gdsdiff/__init__.py +148 -0
  2. gdsdiff/__main__.py +7 -0
  3. gdsdiff/_environment.py +12 -0
  4. gdsdiff/_version.py +34 -0
  5. gdsdiff/acceptance_evidence.py +530 -0
  6. gdsdiff/api.py +2955 -0
  7. gdsdiff/backends/__init__.py +26 -0
  8. gdsdiff/backends/base.py +77 -0
  9. gdsdiff/backends/boundary_loop_ctypes.py +191 -0
  10. gdsdiff/backends/cpu.py +20 -0
  11. gdsdiff/backends/cpu_ctypes.py +255 -0
  12. gdsdiff/backends/cuda.py +40 -0
  13. gdsdiff/backends/cuda_ctypes.py +1297 -0
  14. gdsdiff/backends/exact.py +424 -0
  15. gdsdiff/backends/geometry_ctypes.py +252 -0
  16. gdsdiff/backends/identity.py +53 -0
  17. gdsdiff/backends/metal.py +198 -0
  18. gdsdiff/backends/metal_ctypes.py +866 -0
  19. gdsdiff/backends/polygon_extract_ctypes.py +233 -0
  20. gdsdiff/backends/structural_ctypes.py +130 -0
  21. gdsdiff/boundary_loops.py +616 -0
  22. gdsdiff/cache.py +544 -0
  23. gdsdiff/canonicalize.py +176 -0
  24. gdsdiff/cli.py +352 -0
  25. gdsdiff/config.py +257 -0
  26. gdsdiff/cuda_setup.py +174 -0
  27. gdsdiff/design_history.py +65 -0
  28. gdsdiff/diagnostics.py +42 -0
  29. gdsdiff/diff_gds.py +66 -0
  30. gdsdiff/exact_diff_markdown.py +979 -0
  31. gdsdiff/exact_oracle.py +649 -0
  32. gdsdiff/extract.py +376 -0
  33. gdsdiff/gds_metadata.py +43 -0
  34. gdsdiff/geometry.py +112 -0
  35. gdsdiff/grid.py +143 -0
  36. gdsdiff/hierarchy.py +215 -0
  37. gdsdiff/hierarchy_extract.py +263 -0
  38. gdsdiff/inventory.py +153 -0
  39. gdsdiff/multiprocessing_support.py +39 -0
  40. gdsdiff/native.py +135 -0
  41. gdsdiff/native_cache.py +265 -0
  42. gdsdiff/native_src/cpu_prefilter.cpp +349 -0
  43. gdsdiff/native_src/gds_boundary_loops.cpp +505 -0
  44. gdsdiff/native_src/gds_geometry_scan.cpp +601 -0
  45. gdsdiff/native_src/gds_polygon_extract.cpp +594 -0
  46. gdsdiff/native_src/gds_structural_scan.cpp +335 -0
  47. gdsdiff/native_src/gdsdiff/CMakeLists.txt +74 -0
  48. gdsdiff/native_src/gdsdiff/core.cpp +191 -0
  49. gdsdiff/native_src/gdsdiff/core.hpp +96 -0
  50. gdsdiff/native_src/gdsdiff/cuda_assignment.cu +304 -0
  51. gdsdiff/native_src/gdsdiff/cuda_assignment.cuh +33 -0
  52. gdsdiff/native_src/gdsdiff/cuda_candidates.cu +133 -0
  53. gdsdiff/native_src/gdsdiff/cuda_candidates.cuh +17 -0
  54. gdsdiff/native_src/gdsdiff/cuda_ctypes.cu +4528 -0
  55. gdsdiff/native_src/gdsdiff/cuda_memory.cu +194 -0
  56. gdsdiff/native_src/gdsdiff/cuda_memory.cuh +34 -0
  57. gdsdiff/native_src/gdsdiff/metal_ctypes.mm +2791 -0
  58. gdsdiff/native_src/gdsdiff/python_bindings.cpp +21 -0
  59. gdsdiff/native_src/gdsdiff/test_core.cpp +93 -0
  60. gdsdiff/native_src/gdsdiff/test_cuda_assignment.cu +109 -0
  61. gdsdiff/native_src/gdsdiff/test_cuda_candidates.cu +94 -0
  62. gdsdiff/native_src/gdsdiff/test_cuda_memory.cu +97 -0
  63. gdsdiff/policy.py +305 -0
  64. gdsdiff/polygon_components.py +860 -0
  65. gdsdiff/profiles.py +152 -0
  66. gdsdiff/py.typed +1 -0
  67. gdsdiff/rect_coverage.py +384 -0
  68. gdsdiff/report.py +354 -0
  69. gdsdiff/schemas/gdsdiff_report-v1.schema.json +92 -0
  70. gdsdiff/semantics.py +75 -0
  71. gdsdiff/source_edge_diff.py +1120 -0
  72. gdsdiff/spatial.py +71 -0
  73. gdsdiff/structural_guard.py +161 -0
  74. gdsdiff/surplus_coverage.py +255 -0
  75. gdsdiff/synthetic_suite.py +1643 -0
  76. gdsdiff/templates.py +709 -0
  77. gdsdiff/tiling.py +153 -0
  78. gdsdiff/windowed_exact.py +270 -0
  79. gdsdiff/windows.py +184 -0
  80. gdsdiff-0.1.0.dist-info/METADATA +135 -0
  81. gdsdiff-0.1.0.dist-info/RECORD +85 -0
  82. gdsdiff-0.1.0.dist-info/WHEEL +5 -0
  83. gdsdiff-0.1.0.dist-info/entry_points.txt +4 -0
  84. gdsdiff-0.1.0.dist-info/licenses/LICENSE +674 -0
  85. gdsdiff-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,4528 @@
1
+ #include <cuda_runtime.h>
2
+ #include <thrust/copy.h>
3
+ #include <thrust/device_vector.h>
4
+ #include <thrust/execution_policy.h>
5
+ #include <thrust/functional.h>
6
+ #include <thrust/host_vector.h>
7
+ #include <thrust/iterator/constant_iterator.h>
8
+ #include <thrust/remove.h>
9
+ #include <thrust/reduce.h>
10
+ #include <thrust/scan.h>
11
+ #include <thrust/sort.h>
12
+ #include <thrust/transform.h>
13
+ #include <thrust/tuple.h>
14
+ #include <thrust/unique.h>
15
+
16
+ #include <algorithm>
17
+ #include <cmath>
18
+ #include <cstdint>
19
+ #include <cstdlib>
20
+ #include <cstring>
21
+ #include <exception>
22
+ #include <fstream>
23
+ #include <limits>
24
+ #include <stdexcept>
25
+ #include <string>
26
+ #include <tuple>
27
+ #include <vector>
28
+
29
+ namespace {
30
+
31
+ constexpr std::uint8_t kOldSide = 1;
32
+ constexpr std::uint8_t kNewSide = 2;
33
+ constexpr std::uint8_t kBothSides = kOldSide | kNewSide;
34
+ constexpr int kMaxRectSetRects = 16;
35
+ constexpr int kMaxRectSetCoords = kMaxRectSetRects * 4;
36
+ constexpr int kMaxPolygonComponentEdges = 384;
37
+ constexpr int kMaxPolygonComponentBreaks = 2048;
38
+ constexpr int kMaxPolygonComponentIntervals = 384;
39
+
40
+ thread_local std::string g_last_error;
41
+
42
+ struct DevicePolygon {
43
+ std::int64_t x0;
44
+ std::int64_t y0;
45
+ std::int64_t x1;
46
+ std::int64_t y1;
47
+ std::int32_t layer;
48
+ std::int32_t datatype;
49
+ std::uint64_t canonical_id;
50
+ };
51
+
52
+ struct DeviceAssignment {
53
+ std::int32_t layer;
54
+ std::int32_t datatype;
55
+ std::int64_t tx;
56
+ std::int64_t ty;
57
+ std::uint64_t canonical_id;
58
+ std::uint8_t side;
59
+ };
60
+
61
+ struct DeviceOversized {
62
+ std::int32_t layer;
63
+ std::int32_t datatype;
64
+ std::uint64_t canonical_id;
65
+ std::uint8_t side;
66
+ std::int64_t tx0;
67
+ std::int64_t ty0;
68
+ std::int64_t tx1;
69
+ std::int64_t ty1;
70
+ };
71
+
72
+ struct AssignmentKey {
73
+ std::int32_t layer;
74
+ std::int32_t datatype;
75
+ std::int64_t tx;
76
+ std::int64_t ty;
77
+ std::uint64_t canonical_id;
78
+
79
+ __host__ __device__ bool operator<(const AssignmentKey& other) const {
80
+ return thrust::make_tuple(layer, datatype, tx, ty, canonical_id)
81
+ < thrust::make_tuple(other.layer, other.datatype, other.tx, other.ty, other.canonical_id);
82
+ }
83
+
84
+ __host__ __device__ bool operator==(const AssignmentKey& other) const {
85
+ return layer == other.layer && datatype == other.datatype && tx == other.tx && ty == other.ty
86
+ && canonical_id == other.canonical_id;
87
+ }
88
+ };
89
+
90
+ struct AssignmentKeyEqual {
91
+ __host__ __device__ bool operator()(const AssignmentKey& left, const AssignmentKey& right) const {
92
+ return left == right;
93
+ }
94
+ };
95
+
96
+ struct SideOr {
97
+ __host__ __device__ std::uint8_t operator()(std::uint8_t left, std::uint8_t right) const {
98
+ return static_cast<std::uint8_t>(left | right);
99
+ }
100
+ };
101
+
102
+ struct DeviceAssignmentToKey {
103
+ __host__ __device__ AssignmentKey operator()(const DeviceAssignment& item) const {
104
+ return AssignmentKey{item.layer, item.datatype, item.tx, item.ty, item.canonical_id};
105
+ }
106
+ };
107
+
108
+ struct DeviceAssignmentToSide {
109
+ __host__ __device__ std::uint8_t operator()(const DeviceAssignment& item) const {
110
+ return item.side;
111
+ }
112
+ };
113
+
114
+ struct CandidateTile {
115
+ std::int32_t layer;
116
+ std::int32_t datatype;
117
+ std::int64_t tx;
118
+ std::int64_t ty;
119
+
120
+ bool operator<(const CandidateTile& other) const {
121
+ return std::tie(layer, datatype, tx, ty) < std::tie(other.layer, other.datatype, other.tx, other.ty);
122
+ }
123
+
124
+ bool operator==(const CandidateTile& other) const {
125
+ return layer == other.layer && datatype == other.datatype && tx == other.tx && ty == other.ty;
126
+ }
127
+ };
128
+
129
+ struct OversizedKey {
130
+ std::int32_t layer;
131
+ std::int32_t datatype;
132
+ std::uint64_t canonical_id;
133
+ std::int64_t tx0;
134
+ std::int64_t ty0;
135
+ std::int64_t tx1;
136
+ std::int64_t ty1;
137
+
138
+ bool operator<(const OversizedKey& other) const {
139
+ return std::tie(layer, datatype, canonical_id, tx0, ty0, tx1, ty1)
140
+ < std::tie(other.layer, other.datatype, other.canonical_id, other.tx0, other.ty0, other.tx1, other.ty1);
141
+ }
142
+ };
143
+
144
+ struct OversizedItem {
145
+ OversizedKey key;
146
+ std::uint8_t side;
147
+ };
148
+
149
+ struct SideAssignments {
150
+ thrust::device_vector<DeviceAssignment> assignments;
151
+ std::vector<OversizedItem> oversized_items;
152
+ std::uint64_t assignment_count = 0;
153
+ };
154
+
155
+ struct AxisRawEdge {
156
+ std::int64_t x0 = 0;
157
+ std::int64_t y0 = 0;
158
+ std::int64_t x1 = 0;
159
+ std::int64_t y1 = 0;
160
+ std::int64_t line = 0;
161
+ std::int64_t p0 = 0;
162
+ std::int64_t p1 = 0;
163
+ std::uint8_t vertical = 0;
164
+ std::uint8_t valid = 0;
165
+ };
166
+
167
+ struct AxisEndpointKey {
168
+ std::uint8_t vertical = 0;
169
+ std::int64_t line = 0;
170
+ std::int64_t projection = 0;
171
+
172
+ __host__ __device__ bool operator<(const AxisEndpointKey& other) const {
173
+ return thrust::make_tuple(vertical, line, projection)
174
+ < thrust::make_tuple(other.vertical, other.line, other.projection);
175
+ }
176
+
177
+ __host__ __device__ bool operator==(const AxisEndpointKey& other) const {
178
+ return vertical == other.vertical && line == other.line && projection == other.projection;
179
+ }
180
+ };
181
+
182
+ struct AxisEndpointInvalid {
183
+ __host__ __device__ bool operator()(const AxisEndpointKey& key) const {
184
+ return key.vertical > 1;
185
+ }
186
+ };
187
+
188
+ struct EdgeKey {
189
+ std::int64_t x0 = 0;
190
+ std::int64_t y0 = 0;
191
+ std::int64_t x1 = 0;
192
+ std::int64_t y1 = 0;
193
+
194
+ __host__ __device__ bool operator<(const EdgeKey& other) const {
195
+ return thrust::make_tuple(x0, y0, x1, y1) < thrust::make_tuple(other.x0, other.y0, other.x1, other.y1);
196
+ }
197
+
198
+ __host__ __device__ bool operator==(const EdgeKey& other) const {
199
+ return x0 == other.x0 && y0 == other.y0 && x1 == other.x1 && y1 == other.y1;
200
+ }
201
+ };
202
+
203
+ struct EdgeInvalid {
204
+ __host__ __device__ bool operator()(const EdgeKey& edge) const {
205
+ return edge.x0 == edge.x1 && edge.y0 == edge.y1;
206
+ }
207
+ };
208
+
209
+ template <class T>
210
+ class DeviceBuffer {
211
+ public:
212
+ explicit DeviceBuffer(std::size_t count) : count_(count) {
213
+ if (count_) {
214
+ auto status = cudaMalloc(&ptr_, sizeof(T) * count_);
215
+ if (status != cudaSuccess) {
216
+ throw std::runtime_error(std::string("cudaMalloc: ") + cudaGetErrorString(status));
217
+ }
218
+ }
219
+ }
220
+
221
+ ~DeviceBuffer() {
222
+ if (ptr_) {
223
+ (void)cudaFree(ptr_);
224
+ }
225
+ }
226
+
227
+ DeviceBuffer(const DeviceBuffer&) = delete;
228
+ DeviceBuffer& operator=(const DeviceBuffer&) = delete;
229
+
230
+ T* get() { return ptr_; }
231
+
232
+ private:
233
+ T* ptr_ = nullptr;
234
+ std::size_t count_ = 0;
235
+ };
236
+
237
+ __device__ std::int64_t device_floor_div(std::int64_t value, std::int64_t divisor) {
238
+ std::int64_t quotient = value / divisor;
239
+ const std::int64_t remainder = value % divisor;
240
+ if (remainder != 0 && ((remainder < 0) != (divisor < 0))) {
241
+ --quotient;
242
+ }
243
+ return quotient;
244
+ }
245
+
246
+ __global__ void count_kernel(
247
+ const DevicePolygon* polygons,
248
+ std::uint64_t n,
249
+ std::int64_t tile_size,
250
+ std::uint64_t max_tiles_per_polygon,
251
+ std::uint64_t* counts,
252
+ std::uint8_t* oversized_flags,
253
+ DeviceOversized* oversized,
254
+ std::uint8_t side,
255
+ std::uint32_t* error_flags) {
256
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
257
+ if (index >= n) {
258
+ return;
259
+ }
260
+ const DevicePolygon polygon = polygons[index];
261
+ const std::int64_t tx0 = device_floor_div(polygon.x0, tile_size);
262
+ const std::int64_t ty0 = device_floor_div(polygon.y0, tile_size);
263
+ const std::int64_t tx1 = device_floor_div(polygon.x1, tile_size);
264
+ const std::int64_t ty1 = device_floor_div(polygon.y1, tile_size);
265
+ const std::int64_t span_x = tx1 - tx0 + 1;
266
+ const std::int64_t span_y = ty1 - ty0 + 1;
267
+ if (span_x <= 0 || span_y <= 0) {
268
+ error_flags[index] = 1;
269
+ counts[index] = 0;
270
+ oversized_flags[index] = 0;
271
+ return;
272
+ }
273
+ const auto span_x_u = static_cast<std::uint64_t>(span_x);
274
+ const auto span_y_u = static_cast<std::uint64_t>(span_y);
275
+ if (span_y_u != 0 && span_x_u > UINT64_MAX / span_y_u) {
276
+ error_flags[index] = 2;
277
+ counts[index] = 0;
278
+ oversized_flags[index] = 0;
279
+ return;
280
+ }
281
+ const std::uint64_t count = span_x_u * span_y_u;
282
+ if (count > max_tiles_per_polygon) {
283
+ counts[index] = 0;
284
+ oversized_flags[index] = 1;
285
+ oversized[index] = DeviceOversized{polygon.layer, polygon.datatype, polygon.canonical_id, side, tx0, ty0, tx1, ty1};
286
+ } else {
287
+ counts[index] = count;
288
+ oversized_flags[index] = 0;
289
+ }
290
+ }
291
+
292
+ __global__ void emit_kernel(
293
+ const DevicePolygon* polygons,
294
+ std::uint64_t n,
295
+ std::int64_t tile_size,
296
+ const std::uint64_t* offsets,
297
+ const std::uint8_t* oversized_flags,
298
+ DeviceAssignment* assignments,
299
+ std::uint8_t side) {
300
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
301
+ if (index >= n || oversized_flags[index]) {
302
+ return;
303
+ }
304
+ const DevicePolygon polygon = polygons[index];
305
+ const std::int64_t tx0 = device_floor_div(polygon.x0, tile_size);
306
+ const std::int64_t ty0 = device_floor_div(polygon.y0, tile_size);
307
+ const std::int64_t tx1 = device_floor_div(polygon.x1, tile_size);
308
+ const std::int64_t ty1 = device_floor_div(polygon.y1, tile_size);
309
+ std::uint64_t out = offsets[index];
310
+ for (std::int64_t tx = tx0; tx <= tx1; ++tx) {
311
+ for (std::int64_t ty = ty0; ty <= ty1; ++ty) {
312
+ assignments[out++] = DeviceAssignment{polygon.layer, polygon.datatype, tx, ty, polygon.canonical_id, side};
313
+ }
314
+ }
315
+ }
316
+
317
+ __global__ void byte_compare_kernel(const std::uint8_t* old_data, const std::uint8_t* new_data, std::uint64_t n, std::uint32_t* mismatch) {
318
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
319
+ if (index >= n || *mismatch) {
320
+ return;
321
+ }
322
+ if (old_data[index] != new_data[index]) {
323
+ atomicExch(mismatch, 1u);
324
+ }
325
+ }
326
+
327
+ struct PolygonRecordKey {
328
+ std::int32_t layer;
329
+ std::int32_t datatype;
330
+ std::uint32_t flags;
331
+ std::uint64_t vertex_count;
332
+ std::int64_t x0;
333
+ std::int64_t y0;
334
+ std::int64_t x1;
335
+ std::int64_t y1;
336
+ std::uint64_t hash0;
337
+ std::uint64_t hash1;
338
+
339
+ __host__ __device__ bool operator<(const PolygonRecordKey& other) const {
340
+ if (layer != other.layer) return layer < other.layer;
341
+ if (datatype != other.datatype) return datatype < other.datatype;
342
+ if (flags != other.flags) return flags < other.flags;
343
+ if (vertex_count != other.vertex_count) return vertex_count < other.vertex_count;
344
+ if (x0 != other.x0) return x0 < other.x0;
345
+ if (y0 != other.y0) return y0 < other.y0;
346
+ if (x1 != other.x1) return x1 < other.x1;
347
+ if (y1 != other.y1) return y1 < other.y1;
348
+ if (hash0 != other.hash0) return hash0 < other.hash0;
349
+ return hash1 < other.hash1;
350
+ }
351
+
352
+ __host__ __device__ bool operator==(const PolygonRecordKey& other) const {
353
+ return layer == other.layer && datatype == other.datatype && flags == other.flags
354
+ && vertex_count == other.vertex_count && x0 == other.x0 && y0 == other.y0 && x1 == other.x1
355
+ && y1 == other.y1 && hash0 == other.hash0 && hash1 == other.hash1;
356
+ }
357
+ };
358
+
359
+ struct PolygonSideRecord {
360
+ PolygonRecordKey key;
361
+ std::uint8_t side;
362
+ std::uint64_t index;
363
+ };
364
+
365
+ struct PolygonCountPair {
366
+ std::uint64_t old_count;
367
+ std::uint64_t new_count;
368
+ std::uint64_t old_index;
369
+ std::uint64_t new_index;
370
+ };
371
+
372
+ struct PolygonRecordToKey {
373
+ __host__ __device__ PolygonRecordKey operator()(const PolygonSideRecord& item) const {
374
+ return item.key;
375
+ }
376
+ };
377
+
378
+ struct PolygonSideRecordLess {
379
+ __host__ __device__ bool operator()(const PolygonSideRecord& left, const PolygonSideRecord& right) const {
380
+ return left.key < right.key;
381
+ }
382
+ };
383
+
384
+ struct PolygonRecordToCountPair {
385
+ __host__ __device__ PolygonCountPair operator()(const PolygonSideRecord& item) const {
386
+ constexpr std::uint64_t none = UINT64_MAX;
387
+ return item.side == kOldSide ? PolygonCountPair{1, 0, item.index, none} : PolygonCountPair{0, 1, none, item.index};
388
+ }
389
+ };
390
+
391
+ struct PolygonKeyEqual {
392
+ __host__ __device__ bool operator()(const PolygonRecordKey& left, const PolygonRecordKey& right) const {
393
+ return left == right;
394
+ }
395
+ };
396
+
397
+ struct PolygonCountPairAdd {
398
+ __host__ __device__ PolygonCountPair operator()(const PolygonCountPair& left, const PolygonCountPair& right) const {
399
+ return PolygonCountPair{
400
+ left.old_count + right.old_count,
401
+ left.new_count + right.new_count,
402
+ min(left.old_index, right.old_index),
403
+ min(left.new_index, right.new_index),
404
+ };
405
+ }
406
+ };
407
+
408
+ __device__ std::uint64_t fnv1a_u64_device(std::uint64_t hash, std::uint64_t value) {
409
+ constexpr std::uint64_t prime = 1099511628211ull;
410
+ for (int i = 0; i < 8; ++i) {
411
+ hash ^= (value >> (i * 8)) & 0xffu;
412
+ hash *= prime;
413
+ }
414
+ return hash;
415
+ }
416
+
417
+ __device__ bool point_less_device(
418
+ const std::int64_t* vertices_xy,
419
+ std::uint64_t left_index,
420
+ std::uint64_t right_index) {
421
+ const std::int64_t lx = vertices_xy[left_index * 2 + 0];
422
+ const std::int64_t ly = vertices_xy[left_index * 2 + 1];
423
+ const std::int64_t rx = vertices_xy[right_index * 2 + 0];
424
+ const std::int64_t ry = vertices_xy[right_index * 2 + 1];
425
+ return lx < rx || (lx == rx && ly < ry);
426
+ }
427
+
428
+ __device__ std::uint64_t sequence_index_device(
429
+ std::uint64_t start,
430
+ std::uint64_t count,
431
+ std::uint64_t candidate,
432
+ std::uint64_t step,
433
+ bool forward) {
434
+ if (forward) {
435
+ return start + ((candidate + step) % count);
436
+ }
437
+ return start + ((candidate + count - (step % count)) % count);
438
+ }
439
+
440
+ __device__ bool sequence_less_device(
441
+ const std::int64_t* vertices_xy,
442
+ std::uint64_t start,
443
+ std::uint64_t count,
444
+ std::uint64_t left_candidate,
445
+ bool left_forward,
446
+ std::uint64_t right_candidate,
447
+ bool right_forward) {
448
+ for (std::uint64_t step = 0; step < count; ++step) {
449
+ const std::uint64_t left_index = sequence_index_device(start, count, left_candidate, step, left_forward);
450
+ const std::uint64_t right_index = sequence_index_device(start, count, right_candidate, step, right_forward);
451
+ if (point_less_device(vertices_xy, left_index, right_index)) {
452
+ return true;
453
+ }
454
+ if (point_less_device(vertices_xy, right_index, left_index)) {
455
+ return false;
456
+ }
457
+ }
458
+ return false;
459
+ }
460
+
461
+ __device__ bool is_axis_aligned_bbox_rectangle_device(
462
+ const std::int64_t* vertices_xy,
463
+ std::uint64_t start,
464
+ std::uint64_t count,
465
+ std::int64_t x0,
466
+ std::int64_t y0,
467
+ std::int64_t x1,
468
+ std::int64_t y1) {
469
+ if (count != 4 || x0 >= x1 || y0 >= y1) {
470
+ return false;
471
+ }
472
+ bool corners[4] = {false, false, false, false};
473
+ for (std::uint64_t offset = 0; offset < count; ++offset) {
474
+ const auto x = vertices_xy[(start + offset) * 2 + 0];
475
+ const auto y = vertices_xy[(start + offset) * 2 + 1];
476
+ int corner = -1;
477
+ if (x == x0 && y == y0) corner = 0;
478
+ if (x == x0 && y == y1) corner = 1;
479
+ if (x == x1 && y == y0) corner = 2;
480
+ if (x == x1 && y == y1) corner = 3;
481
+ if (corner < 0 || corners[corner]) {
482
+ return false;
483
+ }
484
+ corners[corner] = true;
485
+ }
486
+ return corners[0] && corners[1] && corners[2] && corners[3];
487
+ }
488
+
489
+ __device__ void canonical_polygon_hash_device(
490
+ const std::int64_t* vertices_xy,
491
+ std::uint64_t start,
492
+ std::uint64_t stop,
493
+ std::uint32_t flags,
494
+ std::int64_t x0,
495
+ std::int64_t y0,
496
+ std::int64_t x1,
497
+ std::int64_t y1,
498
+ std::uint64_t* hash0,
499
+ std::uint64_t* hash1) {
500
+ const std::uint64_t count = stop - start;
501
+ std::uint64_t h0 = 1469598103934665603ull;
502
+ std::uint64_t h1 = 1099511628211ull;
503
+ if (flags == 0 && is_axis_aligned_bbox_rectangle_device(vertices_xy, start, count, x0, y0, x1, y1)) {
504
+ h0 = fnv1a_u64_device(fnv1a_u64_device(h0, 2), 0);
505
+ h0 = fnv1a_u64_device(fnv1a_u64_device(h0, static_cast<std::uint64_t>(x0)), static_cast<std::uint64_t>(y0));
506
+ h0 = fnv1a_u64_device(fnv1a_u64_device(h0, static_cast<std::uint64_t>(x1)), static_cast<std::uint64_t>(y1));
507
+ h1 = fnv1a_u64_device(fnv1a_u64_device(h1, static_cast<std::uint64_t>(y1)), static_cast<std::uint64_t>(x1));
508
+ h1 = fnv1a_u64_device(fnv1a_u64_device(h1, static_cast<std::uint64_t>(y0)), static_cast<std::uint64_t>(x0));
509
+ *hash0 = h0;
510
+ *hash1 = h1;
511
+ return;
512
+ }
513
+ std::uint64_t best_candidate = 0;
514
+ bool best_forward = true;
515
+ if (flags == 0 && count > 0) {
516
+ for (std::uint64_t candidate = 1; candidate < count; ++candidate) {
517
+ if (sequence_less_device(vertices_xy, start, count, candidate, true, best_candidate, best_forward)) {
518
+ best_candidate = candidate;
519
+ best_forward = true;
520
+ }
521
+ }
522
+ for (std::uint64_t candidate = 0; candidate < count; ++candidate) {
523
+ if (sequence_less_device(vertices_xy, start, count, candidate, false, best_candidate, best_forward)) {
524
+ best_candidate = candidate;
525
+ best_forward = false;
526
+ }
527
+ }
528
+ }
529
+ for (std::uint64_t step = 0; step < count; ++step) {
530
+ const std::uint64_t vertex = flags == 0
531
+ ? sequence_index_device(start, count, best_candidate, step, best_forward)
532
+ : start + step;
533
+ const auto x = static_cast<std::uint64_t>(vertices_xy[vertex * 2 + 0]);
534
+ const auto y = static_cast<std::uint64_t>(vertices_xy[vertex * 2 + 1]);
535
+ h0 = fnv1a_u64_device(fnv1a_u64_device(h0, x), y);
536
+ h1 = fnv1a_u64_device(fnv1a_u64_device(h1, y), x);
537
+ }
538
+ *hash0 = h0;
539
+ *hash1 = h1;
540
+ }
541
+
542
+ __global__ void polygon_record_key_kernel(
543
+ const std::int64_t* vertices_xy,
544
+ const std::uint64_t* polygon_offsets,
545
+ const std::int32_t* layers,
546
+ const std::int32_t* datatypes,
547
+ const std::int64_t* bboxes,
548
+ const std::uint32_t* flags,
549
+ std::uint64_t count,
550
+ std::uint8_t side,
551
+ std::uint64_t out_offset,
552
+ PolygonSideRecord* out_records) {
553
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
554
+ if (index >= count) {
555
+ return;
556
+ }
557
+ const std::uint64_t start = polygon_offsets[index];
558
+ const std::uint64_t stop = polygon_offsets[index + 1];
559
+ std::uint64_t hash0 = 0;
560
+ std::uint64_t hash1 = 0;
561
+ canonical_polygon_hash_device(
562
+ vertices_xy,
563
+ start,
564
+ stop,
565
+ flags[index],
566
+ bboxes[index * 4 + 0],
567
+ bboxes[index * 4 + 1],
568
+ bboxes[index * 4 + 2],
569
+ bboxes[index * 4 + 3],
570
+ &hash0,
571
+ &hash1);
572
+ out_records[out_offset + index] = PolygonSideRecord{
573
+ PolygonRecordKey{
574
+ layers[index],
575
+ datatypes[index],
576
+ flags[index],
577
+ stop - start,
578
+ bboxes[index * 4 + 0],
579
+ bboxes[index * 4 + 1],
580
+ bboxes[index * 4 + 2],
581
+ bboxes[index * 4 + 3],
582
+ hash0,
583
+ hash1,
584
+ },
585
+ side,
586
+ index,
587
+ };
588
+ }
589
+
590
+ __global__ void polygon_record_key_from_hash_kernel(
591
+ const std::uint64_t* polygon_offsets,
592
+ const std::int32_t* layers,
593
+ const std::int32_t* datatypes,
594
+ const std::int64_t* bboxes,
595
+ const std::uint32_t* flags,
596
+ const std::uint64_t* hashes,
597
+ std::uint64_t count,
598
+ std::uint8_t side,
599
+ std::uint64_t out_offset,
600
+ PolygonSideRecord* out_records) {
601
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
602
+ if (index >= count) {
603
+ return;
604
+ }
605
+ const std::uint64_t start = polygon_offsets[index];
606
+ const std::uint64_t stop = polygon_offsets[index + 1];
607
+ out_records[out_offset + index] = PolygonSideRecord{
608
+ PolygonRecordKey{
609
+ layers[index],
610
+ datatypes[index],
611
+ flags[index],
612
+ stop - start,
613
+ bboxes[index * 4 + 0],
614
+ bboxes[index * 4 + 1],
615
+ bboxes[index * 4 + 2],
616
+ bboxes[index * 4 + 3],
617
+ hashes[index * 2 + 0],
618
+ hashes[index * 2 + 1],
619
+ },
620
+ side,
621
+ index,
622
+ };
623
+ }
624
+
625
+ struct PolygonSweepEdge {
626
+ double x0;
627
+ double y0;
628
+ double x1;
629
+ double y1;
630
+ std::uint8_t side;
631
+ int polygon_slot;
632
+ };
633
+
634
+ struct PolygonSweepCrossing {
635
+ double y;
636
+ int edge_index;
637
+ };
638
+
639
+ struct PolygonSweepInterval {
640
+ double low_y;
641
+ double high_y;
642
+ int low_edge_index;
643
+ int high_edge_index;
644
+ };
645
+
646
+ __device__ double edge_y_at(const PolygonSweepEdge& edge, double x) {
647
+ const double dx = edge.x1 - edge.x0;
648
+ if (fabs(dx) < 1e-12) {
649
+ return edge.y0;
650
+ }
651
+ const double t = (x - edge.x0) / dx;
652
+ return edge.y0 + t * (edge.y1 - edge.y0);
653
+ }
654
+
655
+ __device__ bool add_breakpoint(double* breaks, int* break_count, double value) {
656
+ if (*break_count >= kMaxPolygonComponentBreaks || !isfinite(value)) {
657
+ return false;
658
+ }
659
+ for (int i = 0; i < *break_count; ++i) {
660
+ if (fabs(breaks[i] - value) <= 1e-7) {
661
+ return true;
662
+ }
663
+ }
664
+ breaks[(*break_count)++] = value;
665
+ return true;
666
+ }
667
+
668
+ __device__ void sort_breakpoints(double* breaks, int count) {
669
+ for (int i = 1; i < count; ++i) {
670
+ const double value = breaks[i];
671
+ int j = i - 1;
672
+ while (j >= 0 && breaks[j] > value) {
673
+ breaks[j + 1] = breaks[j];
674
+ --j;
675
+ }
676
+ breaks[j + 1] = value;
677
+ }
678
+ }
679
+
680
+ __device__ void sort_crossings(PolygonSweepCrossing* crossings, int count) {
681
+ for (int i = 1; i < count; ++i) {
682
+ const auto value = crossings[i];
683
+ int j = i - 1;
684
+ while (j >= 0 && crossings[j].y > value.y) {
685
+ crossings[j + 1] = crossings[j];
686
+ --j;
687
+ }
688
+ crossings[j + 1] = value;
689
+ }
690
+ }
691
+
692
+ __device__ void sort_intervals(PolygonSweepInterval* intervals, int count) {
693
+ for (int i = 1; i < count; ++i) {
694
+ const auto value = intervals[i];
695
+ int j = i - 1;
696
+ while (j >= 0 && intervals[j].low_y > value.low_y) {
697
+ intervals[j + 1] = intervals[j];
698
+ --j;
699
+ }
700
+ intervals[j + 1] = value;
701
+ }
702
+ }
703
+
704
+ __device__ bool edge_intersection_x(const PolygonSweepEdge& a, const PolygonSweepEdge& b, double* out_x) {
705
+ const double ax = a.x1 - a.x0;
706
+ const double ay = a.y1 - a.y0;
707
+ const double bx = b.x1 - b.x0;
708
+ const double by = b.y1 - b.y0;
709
+ const double denom = ax * by - ay * bx;
710
+ if (fabs(denom) < 1e-12) {
711
+ return false;
712
+ }
713
+ const double cx = b.x0 - a.x0;
714
+ const double cy = b.y0 - a.y0;
715
+ const double ta = (cx * by - cy * bx) / denom;
716
+ const double tb = (cx * ay - cy * ax) / denom;
717
+ if (ta < -1e-10 || ta > 1.0 + 1e-10 || tb < -1e-10 || tb > 1.0 + 1e-10) {
718
+ return false;
719
+ }
720
+ *out_x = a.x0 + ta * ax;
721
+ return true;
722
+ }
723
+
724
+ __device__ bool build_union_intervals(
725
+ const PolygonSweepEdge* edges,
726
+ int edge_count,
727
+ int polygon_count,
728
+ std::uint8_t side,
729
+ double x_mid,
730
+ PolygonSweepCrossing* crossings,
731
+ PolygonSweepInterval* intervals,
732
+ int* interval_count) {
733
+ int raw_interval_count = 0;
734
+ for (int polygon_slot = 0; polygon_slot < polygon_count; ++polygon_slot) {
735
+ int crossing_count = 0;
736
+ for (int edge_index = 0; edge_index < edge_count; ++edge_index) {
737
+ const auto& edge = edges[edge_index];
738
+ if (edge.side != side || edge.polygon_slot != polygon_slot) {
739
+ continue;
740
+ }
741
+ const double min_x = fmin(edge.x0, edge.x1);
742
+ const double max_x = fmax(edge.x0, edge.x1);
743
+ if (x_mid <= min_x + 1e-9 || x_mid >= max_x - 1e-9) {
744
+ continue;
745
+ }
746
+ if (crossing_count >= kMaxPolygonComponentIntervals) {
747
+ return false;
748
+ }
749
+ crossings[crossing_count++] = PolygonSweepCrossing{edge_y_at(edge, x_mid), edge_index};
750
+ }
751
+ sort_crossings(crossings, crossing_count);
752
+ for (int i = 0; i + 1 < crossing_count; i += 2) {
753
+ PolygonSweepInterval next{
754
+ crossings[i].y,
755
+ crossings[i + 1].y,
756
+ crossings[i].edge_index,
757
+ crossings[i + 1].edge_index,
758
+ };
759
+ if (next.high_y <= next.low_y + 1e-9) {
760
+ continue;
761
+ }
762
+ if (raw_interval_count >= kMaxPolygonComponentIntervals) {
763
+ return false;
764
+ }
765
+ intervals[raw_interval_count++] = next;
766
+ }
767
+ }
768
+ sort_intervals(intervals, raw_interval_count);
769
+ *interval_count = 0;
770
+ for (int i = 0; i < raw_interval_count; ++i) {
771
+ const auto next = intervals[i];
772
+ if (*interval_count == 0 || next.low_y > intervals[*interval_count - 1].high_y + 1e-9) {
773
+ intervals[(*interval_count)++] = next;
774
+ } else if (next.high_y > intervals[*interval_count - 1].high_y) {
775
+ intervals[*interval_count - 1].high_y = next.high_y;
776
+ intervals[*interval_count - 1].high_edge_index = next.high_edge_index;
777
+ }
778
+ }
779
+ return true;
780
+ }
781
+
782
+ __device__ double interval_area(
783
+ const PolygonSweepEdge* edges,
784
+ const PolygonSweepInterval& interval,
785
+ double x_left,
786
+ double x_right) {
787
+ const double low_left = edge_y_at(edges[interval.low_edge_index], x_left);
788
+ const double low_right = edge_y_at(edges[interval.low_edge_index], x_right);
789
+ const double high_left = edge_y_at(edges[interval.high_edge_index], x_left);
790
+ const double high_right = edge_y_at(edges[interval.high_edge_index], x_right);
791
+ const double height_sum = (high_left - low_left) + (high_right - low_right);
792
+ const double area = (x_right - x_left) * height_sum * 0.5;
793
+ return area > 0.0 ? area : 0.0;
794
+ }
795
+
796
+ __device__ double interval_difference_area(
797
+ const PolygonSweepEdge* edges,
798
+ const PolygonSweepInterval* keep,
799
+ int keep_count,
800
+ const PolygonSweepInterval* cut,
801
+ int cut_count,
802
+ double x_left,
803
+ double x_right,
804
+ double x_mid) {
805
+ double area = 0.0;
806
+ for (int i = 0; i < keep_count; ++i) {
807
+ double cursor_y = keep[i].low_y;
808
+ int cursor_edge = keep[i].low_edge_index;
809
+ for (int j = 0; j < cut_count; ++j) {
810
+ if (cut[j].high_y <= cursor_y + 1e-9 || cut[j].low_y >= keep[i].high_y - 1e-9) {
811
+ continue;
812
+ }
813
+ if (cut[j].low_y > cursor_y + 1e-9) {
814
+ PolygonSweepInterval piece{cursor_y, fmin(cut[j].low_y, keep[i].high_y), cursor_edge, cut[j].low_edge_index};
815
+ area += interval_area(edges, piece, x_left, x_right);
816
+ }
817
+ if (cut[j].high_y > cursor_y) {
818
+ cursor_y = fmax(cursor_y, cut[j].high_y);
819
+ cursor_edge = cut[j].high_edge_index;
820
+ }
821
+ if (cursor_y >= keep[i].high_y - 1e-9) {
822
+ break;
823
+ }
824
+ }
825
+ if (cursor_y < keep[i].high_y - 1e-9) {
826
+ PolygonSweepInterval piece{cursor_y, keep[i].high_y, cursor_edge, keep[i].high_edge_index};
827
+ area += interval_area(edges, piece, x_left, x_right);
828
+ }
829
+ }
830
+ return area;
831
+ }
832
+
833
+ __device__ bool emit_interval_fragment(
834
+ const PolygonSweepEdge* edges,
835
+ const PolygonSweepInterval& interval,
836
+ double x_left,
837
+ double x_right,
838
+ std::int64_t* out_quads,
839
+ std::int64_t* out_fragment_twice_areas,
840
+ std::uint16_t* out_count,
841
+ std::int64_t* out_twice_area,
842
+ std::uint16_t max_fragments) {
843
+ const double low_left = edge_y_at(edges[interval.low_edge_index], x_left);
844
+ const double low_right = edge_y_at(edges[interval.low_edge_index], x_right);
845
+ const double high_left = edge_y_at(edges[interval.high_edge_index], x_left);
846
+ const double high_right = edge_y_at(edges[interval.high_edge_index], x_right);
847
+ const double height_sum = (high_left - low_left) + (high_right - low_right);
848
+ const double area = (x_right - x_left) * height_sum * 0.5;
849
+ if (area <= 1e-9) {
850
+ return true;
851
+ }
852
+ if (*out_count >= max_fragments) {
853
+ return false;
854
+ }
855
+ const std::uint16_t index = (*out_count)++;
856
+ const auto fragment_twice_area = static_cast<std::int64_t>(llround(area * 2.0));
857
+ std::int64_t* quad = out_quads + static_cast<std::uint64_t>(index) * 8;
858
+ quad[0] = static_cast<std::int64_t>(llround(x_left));
859
+ quad[1] = static_cast<std::int64_t>(llround(low_left));
860
+ quad[2] = static_cast<std::int64_t>(llround(x_right));
861
+ quad[3] = static_cast<std::int64_t>(llround(low_right));
862
+ quad[4] = static_cast<std::int64_t>(llround(x_right));
863
+ quad[5] = static_cast<std::int64_t>(llround(high_right));
864
+ quad[6] = static_cast<std::int64_t>(llround(x_left));
865
+ quad[7] = static_cast<std::int64_t>(llround(high_left));
866
+ out_fragment_twice_areas[index] = fragment_twice_area;
867
+ *out_twice_area += fragment_twice_area;
868
+ return true;
869
+ }
870
+
871
+ __device__ bool emit_interval_difference_fragments(
872
+ const PolygonSweepEdge* edges,
873
+ const PolygonSweepInterval* keep,
874
+ int keep_count,
875
+ const PolygonSweepInterval* cut,
876
+ int cut_count,
877
+ double x_left,
878
+ double x_right,
879
+ std::int64_t* out_quads,
880
+ std::int64_t* out_fragment_twice_areas,
881
+ std::uint16_t* out_count,
882
+ std::int64_t* out_twice_area,
883
+ std::uint16_t max_fragments) {
884
+ for (int i = 0; i < keep_count; ++i) {
885
+ double cursor_y = keep[i].low_y;
886
+ int cursor_edge = keep[i].low_edge_index;
887
+ for (int j = 0; j < cut_count; ++j) {
888
+ if (cut[j].high_y <= cursor_y + 1e-9 || cut[j].low_y >= keep[i].high_y - 1e-9) {
889
+ continue;
890
+ }
891
+ if (cut[j].low_y > cursor_y + 1e-9) {
892
+ PolygonSweepInterval piece{cursor_y, fmin(cut[j].low_y, keep[i].high_y), cursor_edge, cut[j].low_edge_index};
893
+ if (!emit_interval_fragment(edges, piece, x_left, x_right, out_quads, out_fragment_twice_areas, out_count, out_twice_area, max_fragments)) {
894
+ return false;
895
+ }
896
+ }
897
+ if (cut[j].high_y > cursor_y) {
898
+ cursor_y = fmax(cursor_y, cut[j].high_y);
899
+ cursor_edge = cut[j].high_edge_index;
900
+ }
901
+ if (cursor_y >= keep[i].high_y - 1e-9) {
902
+ break;
903
+ }
904
+ }
905
+ if (cursor_y < keep[i].high_y - 1e-9) {
906
+ PolygonSweepInterval piece{cursor_y, keep[i].high_y, cursor_edge, keep[i].high_edge_index};
907
+ if (!emit_interval_fragment(edges, piece, x_left, x_right, out_quads, out_fragment_twice_areas, out_count, out_twice_area, max_fragments)) {
908
+ return false;
909
+ }
910
+ }
911
+ }
912
+ return true;
913
+ }
914
+
915
+ __global__ void polygon_component_area_kernel(
916
+ const std::int64_t* vertices_xy,
917
+ const std::uint64_t* polygon_offsets,
918
+ const std::uint8_t* polygon_sides,
919
+ const std::uint64_t* component_offsets,
920
+ std::uint64_t component_count,
921
+ std::int64_t* out_twice_areas,
922
+ std::uint32_t* out_errors) {
923
+ const std::uint64_t component_index = blockIdx.x * blockDim.x + threadIdx.x;
924
+ if (component_index >= component_count) {
925
+ return;
926
+ }
927
+ PolygonSweepEdge edges[kMaxPolygonComponentEdges];
928
+ double breaks[kMaxPolygonComponentBreaks];
929
+ PolygonSweepCrossing crossings[kMaxPolygonComponentIntervals];
930
+ PolygonSweepInterval old_intervals[kMaxPolygonComponentIntervals];
931
+ PolygonSweepInterval new_intervals[kMaxPolygonComponentIntervals];
932
+ int edge_count = 0;
933
+ int break_count = 0;
934
+
935
+ const std::uint64_t polygon_start = component_offsets[component_index];
936
+ const std::uint64_t polygon_stop = component_offsets[component_index + 1];
937
+ for (std::uint64_t polygon_index = polygon_start; polygon_index < polygon_stop; ++polygon_index) {
938
+ const int polygon_slot = static_cast<int>(polygon_index - polygon_start);
939
+ const std::uint64_t vertex_start = polygon_offsets[polygon_index];
940
+ const std::uint64_t vertex_stop = polygon_offsets[polygon_index + 1];
941
+ const std::uint64_t vertex_count = vertex_stop - vertex_start;
942
+ if (vertex_count < 3) {
943
+ continue;
944
+ }
945
+ for (std::uint64_t local = 0; local < vertex_count; ++local) {
946
+ const std::uint64_t a = vertex_start + local;
947
+ const std::uint64_t b = vertex_start + ((local + 1) % vertex_count);
948
+ const double x0 = static_cast<double>(vertices_xy[a * 2 + 0]);
949
+ const double y0 = static_cast<double>(vertices_xy[a * 2 + 1]);
950
+ const double x1 = static_cast<double>(vertices_xy[b * 2 + 0]);
951
+ const double y1 = static_cast<double>(vertices_xy[b * 2 + 1]);
952
+ if (!add_breakpoint(breaks, &break_count, x0) || !add_breakpoint(breaks, &break_count, x1)) {
953
+ out_errors[component_index] = 1;
954
+ return;
955
+ }
956
+ if (fabs(x1 - x0) <= 1e-12) {
957
+ continue;
958
+ }
959
+ if (edge_count >= kMaxPolygonComponentEdges) {
960
+ out_errors[component_index] = 2;
961
+ return;
962
+ }
963
+ edges[edge_count++] = PolygonSweepEdge{x0, y0, x1, y1, polygon_sides[polygon_index], polygon_slot};
964
+ }
965
+ }
966
+ for (int i = 0; i < edge_count; ++i) {
967
+ for (int j = i + 1; j < edge_count; ++j) {
968
+ double x = 0.0;
969
+ if (edge_intersection_x(edges[i], edges[j], &x)) {
970
+ const double min_x = fmax(fmin(edges[i].x0, edges[i].x1), fmin(edges[j].x0, edges[j].x1));
971
+ const double max_x = fmin(fmax(edges[i].x0, edges[i].x1), fmax(edges[j].x0, edges[j].x1));
972
+ if (x > min_x + 1e-7 && x < max_x - 1e-7 && !add_breakpoint(breaks, &break_count, x)) {
973
+ out_errors[component_index] = 1;
974
+ return;
975
+ }
976
+ }
977
+ }
978
+ }
979
+ sort_breakpoints(breaks, break_count);
980
+ double old_minus_new = 0.0;
981
+ double new_minus_old = 0.0;
982
+ for (int slab = 0; slab + 1 < break_count; ++slab) {
983
+ const double x_left = breaks[slab];
984
+ const double x_right = breaks[slab + 1];
985
+ if (x_right <= x_left + 1e-9) {
986
+ continue;
987
+ }
988
+ const double x_mid = (x_left + x_right) * 0.5;
989
+ int old_count = 0;
990
+ int new_count = 0;
991
+ const int component_polygon_count = static_cast<int>(polygon_stop - polygon_start);
992
+ if (!build_union_intervals(edges, edge_count, component_polygon_count, kOldSide, x_mid, crossings, old_intervals, &old_count)
993
+ || !build_union_intervals(edges, edge_count, component_polygon_count, kNewSide, x_mid, crossings, new_intervals, &new_count)) {
994
+ out_errors[component_index] = 3;
995
+ return;
996
+ }
997
+ old_minus_new += interval_difference_area(edges, old_intervals, old_count, new_intervals, new_count, x_left, x_right, x_mid);
998
+ new_minus_old += interval_difference_area(edges, new_intervals, new_count, old_intervals, old_count, x_left, x_right, x_mid);
999
+ }
1000
+ const auto old_twice = static_cast<std::int64_t>(llround(old_minus_new * 2.0));
1001
+ const auto new_twice = static_cast<std::int64_t>(llround(new_minus_old * 2.0));
1002
+ out_twice_areas[component_index * 3 + 0] = old_twice;
1003
+ out_twice_areas[component_index * 3 + 1] = new_twice;
1004
+ out_twice_areas[component_index * 3 + 2] = old_twice + new_twice;
1005
+ out_errors[component_index] = 0;
1006
+ }
1007
+
1008
+ __global__ void polygon_component_fragment_kernel(
1009
+ const std::int64_t* vertices_xy,
1010
+ const std::uint64_t* polygon_offsets,
1011
+ const std::uint8_t* polygon_sides,
1012
+ const std::uint64_t* component_offsets,
1013
+ std::uint64_t component_count,
1014
+ std::uint16_t max_fragments,
1015
+ std::int64_t* out_quads,
1016
+ std::int64_t* out_fragment_twice_areas,
1017
+ std::uint16_t* out_counts,
1018
+ std::int64_t* out_twice_areas,
1019
+ std::uint32_t* out_errors) {
1020
+ const std::uint64_t component_index = blockIdx.x * blockDim.x + threadIdx.x;
1021
+ if (component_index >= component_count) {
1022
+ return;
1023
+ }
1024
+ PolygonSweepEdge edges[kMaxPolygonComponentEdges];
1025
+ double breaks[kMaxPolygonComponentBreaks];
1026
+ PolygonSweepCrossing crossings[kMaxPolygonComponentIntervals];
1027
+ PolygonSweepInterval old_intervals[kMaxPolygonComponentIntervals];
1028
+ PolygonSweepInterval new_intervals[kMaxPolygonComponentIntervals];
1029
+ int edge_count = 0;
1030
+ int break_count = 0;
1031
+
1032
+ const std::uint64_t polygon_start = component_offsets[component_index];
1033
+ const std::uint64_t polygon_stop = component_offsets[component_index + 1];
1034
+ for (std::uint64_t polygon_index = polygon_start; polygon_index < polygon_stop; ++polygon_index) {
1035
+ const int polygon_slot = static_cast<int>(polygon_index - polygon_start);
1036
+ const std::uint64_t vertex_start = polygon_offsets[polygon_index];
1037
+ const std::uint64_t vertex_stop = polygon_offsets[polygon_index + 1];
1038
+ const std::uint64_t vertex_count = vertex_stop - vertex_start;
1039
+ if (vertex_count < 3) {
1040
+ continue;
1041
+ }
1042
+ for (std::uint64_t local = 0; local < vertex_count; ++local) {
1043
+ const std::uint64_t a = vertex_start + local;
1044
+ const std::uint64_t b = vertex_start + ((local + 1) % vertex_count);
1045
+ const double x0 = static_cast<double>(vertices_xy[a * 2 + 0]);
1046
+ const double y0 = static_cast<double>(vertices_xy[a * 2 + 1]);
1047
+ const double x1 = static_cast<double>(vertices_xy[b * 2 + 0]);
1048
+ const double y1 = static_cast<double>(vertices_xy[b * 2 + 1]);
1049
+ if (!add_breakpoint(breaks, &break_count, x0) || !add_breakpoint(breaks, &break_count, x1)) {
1050
+ out_errors[component_index] = 1;
1051
+ return;
1052
+ }
1053
+ if (fabs(x1 - x0) <= 1e-12) {
1054
+ continue;
1055
+ }
1056
+ if (edge_count >= kMaxPolygonComponentEdges) {
1057
+ out_errors[component_index] = 2;
1058
+ return;
1059
+ }
1060
+ edges[edge_count++] = PolygonSweepEdge{x0, y0, x1, y1, polygon_sides[polygon_index], polygon_slot};
1061
+ }
1062
+ }
1063
+ for (int i = 0; i < edge_count; ++i) {
1064
+ for (int j = i + 1; j < edge_count; ++j) {
1065
+ double x = 0.0;
1066
+ if (edge_intersection_x(edges[i], edges[j], &x)) {
1067
+ const double min_x = fmax(fmin(edges[i].x0, edges[i].x1), fmin(edges[j].x0, edges[j].x1));
1068
+ const double max_x = fmin(fmax(edges[i].x0, edges[i].x1), fmax(edges[j].x0, edges[j].x1));
1069
+ if (x > min_x + 1e-7 && x < max_x - 1e-7 && !add_breakpoint(breaks, &break_count, x)) {
1070
+ out_errors[component_index] = 1;
1071
+ return;
1072
+ }
1073
+ }
1074
+ }
1075
+ }
1076
+ sort_breakpoints(breaks, break_count);
1077
+ std::uint16_t old_count_out = 0;
1078
+ std::uint16_t new_count_out = 0;
1079
+ std::int64_t old_twice = 0;
1080
+ std::int64_t new_twice = 0;
1081
+ double old_area_total = 0.0;
1082
+ double new_area_total = 0.0;
1083
+ std::int64_t* old_quads = out_quads + component_index * 2 * static_cast<std::uint64_t>(max_fragments) * 8;
1084
+ std::int64_t* new_quads = old_quads + static_cast<std::uint64_t>(max_fragments) * 8;
1085
+ std::int64_t* old_fragment_areas = out_fragment_twice_areas + component_index * 2 * static_cast<std::uint64_t>(max_fragments);
1086
+ std::int64_t* new_fragment_areas = old_fragment_areas + static_cast<std::uint64_t>(max_fragments);
1087
+ for (int slab = 0; slab + 1 < break_count; ++slab) {
1088
+ const double x_left = breaks[slab];
1089
+ const double x_right = breaks[slab + 1];
1090
+ if (x_right <= x_left + 1e-9) {
1091
+ continue;
1092
+ }
1093
+ const double x_mid = (x_left + x_right) * 0.5;
1094
+ int old_count = 0;
1095
+ int new_count = 0;
1096
+ const int component_polygon_count = static_cast<int>(polygon_stop - polygon_start);
1097
+ if (!build_union_intervals(edges, edge_count, component_polygon_count, kOldSide, x_mid, crossings, old_intervals, &old_count)
1098
+ || !build_union_intervals(edges, edge_count, component_polygon_count, kNewSide, x_mid, crossings, new_intervals, &new_count)) {
1099
+ out_errors[component_index] = 3;
1100
+ return;
1101
+ }
1102
+ old_area_total += interval_difference_area(edges, old_intervals, old_count, new_intervals, new_count, x_left, x_right, x_mid);
1103
+ new_area_total += interval_difference_area(edges, new_intervals, new_count, old_intervals, old_count, x_left, x_right, x_mid);
1104
+ if (!emit_interval_difference_fragments(
1105
+ edges,
1106
+ old_intervals,
1107
+ old_count,
1108
+ new_intervals,
1109
+ new_count,
1110
+ x_left,
1111
+ x_right,
1112
+ old_quads,
1113
+ old_fragment_areas,
1114
+ &old_count_out,
1115
+ &old_twice,
1116
+ max_fragments)
1117
+ || !emit_interval_difference_fragments(
1118
+ edges,
1119
+ new_intervals,
1120
+ new_count,
1121
+ old_intervals,
1122
+ old_count,
1123
+ x_left,
1124
+ x_right,
1125
+ new_quads,
1126
+ new_fragment_areas,
1127
+ &new_count_out,
1128
+ &new_twice,
1129
+ max_fragments)) {
1130
+ out_errors[component_index] = 4;
1131
+ return;
1132
+ }
1133
+ }
1134
+ old_twice = static_cast<std::int64_t>(llround(old_area_total * 2.0));
1135
+ new_twice = static_cast<std::int64_t>(llround(new_area_total * 2.0));
1136
+ out_counts[component_index * 2 + 0] = old_count_out;
1137
+ out_counts[component_index * 2 + 1] = new_count_out;
1138
+ out_twice_areas[component_index * 3 + 0] = old_twice;
1139
+ out_twice_areas[component_index * 3 + 1] = new_twice;
1140
+ out_twice_areas[component_index * 3 + 2] = old_twice + new_twice;
1141
+ out_errors[component_index] = 0;
1142
+ }
1143
+
1144
+ __device__ bool point_in_bbox_double(double x, double y, const std::int64_t* bbox) {
1145
+ return x >= static_cast<double>(bbox[0]) && x <= static_cast<double>(bbox[2])
1146
+ && y >= static_cast<double>(bbox[1]) && y <= static_cast<double>(bbox[3]);
1147
+ }
1148
+
1149
+ __device__ bool point_in_polygon_double(
1150
+ double x,
1151
+ double y,
1152
+ const std::int64_t* vertices_xy,
1153
+ const std::uint64_t* polygon_offsets,
1154
+ std::uint64_t polygon_index) {
1155
+ const std::uint64_t start = polygon_offsets[polygon_index];
1156
+ const std::uint64_t stop = polygon_offsets[polygon_index + 1];
1157
+ const std::uint64_t vertex_count = stop - start;
1158
+ if (vertex_count < 3) {
1159
+ return false;
1160
+ }
1161
+ bool inside = false;
1162
+ for (std::uint64_t local = 0; local < vertex_count; ++local) {
1163
+ const std::uint64_t a = start + local;
1164
+ const std::uint64_t b = start + ((local + 1) % vertex_count);
1165
+ const double x0 = static_cast<double>(vertices_xy[a * 2 + 0]);
1166
+ const double y0 = static_cast<double>(vertices_xy[a * 2 + 1]);
1167
+ const double x1 = static_cast<double>(vertices_xy[b * 2 + 0]);
1168
+ const double y1 = static_cast<double>(vertices_xy[b * 2 + 1]);
1169
+ if ((y0 > y) != (y1 > y)) {
1170
+ const double x_intersection = x0 + (y - y0) * (x1 - x0) / (y1 - y0);
1171
+ if (x_intersection > x) {
1172
+ inside = !inside;
1173
+ }
1174
+ }
1175
+ }
1176
+ return inside;
1177
+ }
1178
+
1179
+ __device__ bool union_contains_double(
1180
+ double x,
1181
+ double y,
1182
+ const std::int64_t* vertices_xy,
1183
+ const std::uint64_t* polygon_offsets,
1184
+ const std::int64_t* bboxes,
1185
+ std::uint64_t polygon_count) {
1186
+ for (std::uint64_t polygon_index = 0; polygon_index < polygon_count; ++polygon_index) {
1187
+ const std::int64_t* bbox = bboxes + polygon_index * 4;
1188
+ if (point_in_bbox_double(x, y, bbox)
1189
+ && point_in_polygon_double(x, y, vertices_xy, polygon_offsets, polygon_index)) {
1190
+ return true;
1191
+ }
1192
+ }
1193
+ return false;
1194
+ }
1195
+
1196
+ __device__ bool union_contains_side_double(
1197
+ double x,
1198
+ double y,
1199
+ std::uint8_t side,
1200
+ const std::int64_t* vertices_xy,
1201
+ const std::uint64_t* polygon_offsets,
1202
+ const std::int64_t* bboxes,
1203
+ const std::uint8_t* polygon_sides,
1204
+ std::uint64_t polygon_count) {
1205
+ for (std::uint64_t polygon_index = 0; polygon_index < polygon_count; ++polygon_index) {
1206
+ if (polygon_sides[polygon_index] != side) {
1207
+ continue;
1208
+ }
1209
+ const std::int64_t* bbox = bboxes + polygon_index * 4;
1210
+ if (point_in_bbox_double(x, y, bbox)
1211
+ && point_in_polygon_double(x, y, vertices_xy, polygon_offsets, polygon_index)) {
1212
+ return true;
1213
+ }
1214
+ }
1215
+ return false;
1216
+ }
1217
+
1218
+ __device__ bool directional_target_contains_double(
1219
+ double x,
1220
+ double y,
1221
+ std::uint8_t direction,
1222
+ const std::int64_t* vertices_xy,
1223
+ const std::uint64_t* polygon_offsets,
1224
+ const std::int64_t* bboxes,
1225
+ const std::uint8_t* polygon_sides,
1226
+ std::uint64_t polygon_count) {
1227
+ const bool old_inside = union_contains_side_double(x, y, kOldSide, vertices_xy, polygon_offsets, bboxes, polygon_sides, polygon_count);
1228
+ const bool new_inside = union_contains_side_double(x, y, kNewSide, vertices_xy, polygon_offsets, bboxes, polygon_sides, polygon_count);
1229
+ if (direction == kOldSide) {
1230
+ return old_inside && !new_inside;
1231
+ }
1232
+ return new_inside && !old_inside;
1233
+ }
1234
+
1235
+ __global__ void mixed_boundary_edge_kernel(
1236
+ const std::int64_t* vertices_xy,
1237
+ const std::uint64_t* polygon_offsets,
1238
+ const std::int64_t* bboxes,
1239
+ const std::uint8_t* polygon_sides,
1240
+ std::uint64_t polygon_count,
1241
+ const double* segment_points,
1242
+ std::uint64_t segment_count,
1243
+ std::uint8_t direction,
1244
+ double* out_edges,
1245
+ std::uint8_t* out_keep) {
1246
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
1247
+ if (index >= segment_count) {
1248
+ return;
1249
+ }
1250
+ out_keep[index] = 0;
1251
+ const double x0 = segment_points[index * 4 + 0];
1252
+ const double y0 = segment_points[index * 4 + 1];
1253
+ const double x1 = segment_points[index * 4 + 2];
1254
+ const double y1 = segment_points[index * 4 + 3];
1255
+ const double dx = x1 - x0;
1256
+ const double dy = y1 - y0;
1257
+ const double length = sqrt(dx * dx + dy * dy);
1258
+ if (length <= 1e-12) {
1259
+ return;
1260
+ }
1261
+ const double normal_x = -dy / length;
1262
+ const double normal_y = dx / length;
1263
+ const double mid_x = (x0 + x1) * 0.5;
1264
+ const double mid_y = (y0 + y1) * 0.5;
1265
+ const bool left_inside = directional_target_contains_double(
1266
+ mid_x + normal_x * 1e-5,
1267
+ mid_y + normal_y * 1e-5,
1268
+ direction,
1269
+ vertices_xy,
1270
+ polygon_offsets,
1271
+ bboxes,
1272
+ polygon_sides,
1273
+ polygon_count);
1274
+ const bool right_inside = directional_target_contains_double(
1275
+ mid_x - normal_x * 1e-5,
1276
+ mid_y - normal_y * 1e-5,
1277
+ direction,
1278
+ vertices_xy,
1279
+ polygon_offsets,
1280
+ bboxes,
1281
+ polygon_sides,
1282
+ polygon_count);
1283
+ if (left_inside && !right_inside) {
1284
+ out_edges[index * 4 + 0] = x0;
1285
+ out_edges[index * 4 + 1] = y0;
1286
+ out_edges[index * 4 + 2] = x1;
1287
+ out_edges[index * 4 + 3] = y1;
1288
+ out_keep[index] = 1;
1289
+ } else if (right_inside && !left_inside) {
1290
+ out_edges[index * 4 + 0] = x1;
1291
+ out_edges[index * 4 + 1] = y1;
1292
+ out_edges[index * 4 + 2] = x0;
1293
+ out_edges[index * 4 + 3] = y0;
1294
+ out_keep[index] = 1;
1295
+ }
1296
+ }
1297
+
1298
+ __global__ void one_sided_boundary_edge_kernel(
1299
+ const std::int64_t* vertices_xy,
1300
+ const std::uint64_t* polygon_offsets,
1301
+ const std::int64_t* bboxes,
1302
+ const std::int64_t* signed_twice_areas,
1303
+ std::uint64_t polygon_count,
1304
+ const double* segment_points,
1305
+ const std::uint32_t* segment_polygon_indices,
1306
+ std::uint64_t segment_count,
1307
+ double* out_edges,
1308
+ std::uint8_t* out_keep) {
1309
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
1310
+ if (index >= segment_count) {
1311
+ return;
1312
+ }
1313
+ out_keep[index] = 0;
1314
+ const auto polygon_index = static_cast<std::uint64_t>(segment_polygon_indices[index]);
1315
+ if (polygon_index >= polygon_count) {
1316
+ return;
1317
+ }
1318
+ const std::int64_t signed_area = signed_twice_areas[polygon_index];
1319
+ if (signed_area == 0) {
1320
+ return;
1321
+ }
1322
+ const double x0 = segment_points[index * 4 + 0];
1323
+ const double y0 = segment_points[index * 4 + 1];
1324
+ const double x1 = segment_points[index * 4 + 2];
1325
+ const double y1 = segment_points[index * 4 + 3];
1326
+ const double dx = x1 - x0;
1327
+ const double dy = y1 - y0;
1328
+ const double length = sqrt(dx * dx + dy * dy);
1329
+ if (length <= 1e-12) {
1330
+ return;
1331
+ }
1332
+ const double normal_x = -dy / length;
1333
+ const double normal_y = dx / length;
1334
+ const double mid_x = (x0 + x1) * 0.5;
1335
+ const double mid_y = (y0 + y1) * 0.5;
1336
+ const double sample_x = signed_area > 0 ? mid_x - normal_x * 1e-5 : mid_x + normal_x * 1e-5;
1337
+ const double sample_y = signed_area > 0 ? mid_y - normal_y * 1e-5 : mid_y + normal_y * 1e-5;
1338
+ if (union_contains_double(sample_x, sample_y, vertices_xy, polygon_offsets, bboxes, polygon_count)) {
1339
+ return;
1340
+ }
1341
+ if (signed_area > 0) {
1342
+ out_edges[index * 4 + 0] = x0;
1343
+ out_edges[index * 4 + 1] = y0;
1344
+ out_edges[index * 4 + 2] = x1;
1345
+ out_edges[index * 4 + 3] = y1;
1346
+ } else {
1347
+ out_edges[index * 4 + 0] = x1;
1348
+ out_edges[index * 4 + 1] = y1;
1349
+ out_edges[index * 4 + 2] = x0;
1350
+ out_edges[index * 4 + 3] = y0;
1351
+ }
1352
+ out_keep[index] = 1;
1353
+ }
1354
+
1355
+ __device__ bool nearly_equal_double(double left, double right) {
1356
+ return fabs(left - right) <= 1e-9;
1357
+ }
1358
+
1359
+ __device__ bool point_equal_double(double ax, double ay, double bx, double by) {
1360
+ return nearly_equal_double(ax, bx) && nearly_equal_double(ay, by);
1361
+ }
1362
+
1363
+ __device__ bool strict_between_zero_one(double value) {
1364
+ return value > 1e-9 && value < 1.0 - 1e-9;
1365
+ }
1366
+
1367
+ __device__ bool segment_pairs_require_split(const double* left, const double* right) {
1368
+ const double ax0 = left[0];
1369
+ const double ay0 = left[1];
1370
+ const double ax1 = left[2];
1371
+ const double ay1 = left[3];
1372
+ const double bx0 = right[0];
1373
+ const double by0 = right[1];
1374
+ const double bx1 = right[2];
1375
+ const double by1 = right[3];
1376
+ if (fmax(fmin(ax0, ax1), fmin(bx0, bx1)) > fmin(fmax(ax0, ax1), fmax(bx0, bx1)) + 1e-9
1377
+ || fmax(fmin(ay0, ay1), fmin(by0, by1)) > fmin(fmax(ay0, ay1), fmax(by0, by1)) + 1e-9) {
1378
+ return false;
1379
+ }
1380
+ const double adx = ax1 - ax0;
1381
+ const double ady = ay1 - ay0;
1382
+ const double bdx = bx1 - bx0;
1383
+ const double bdy = by1 - by0;
1384
+ const double denom = adx * bdy - ady * bdx;
1385
+ const double cx = bx0 - ax0;
1386
+ const double cy = by0 - ay0;
1387
+ if (fabs(denom) <= 1e-12) {
1388
+ if (fabs(cx * ady - cy * adx) > 1e-6) {
1389
+ return false;
1390
+ }
1391
+ const double alen2 = adx * adx + ady * ady;
1392
+ const double blen2 = bdx * bdx + bdy * bdy;
1393
+ if (alen2 <= 1e-12 || blen2 <= 1e-12) {
1394
+ return false;
1395
+ }
1396
+ const double bt0 = ((bx0 - ax0) * adx + (by0 - ay0) * ady) / alen2;
1397
+ const double bt1 = ((bx1 - ax0) * adx + (by1 - ay0) * ady) / alen2;
1398
+ const double at0 = ((ax0 - bx0) * bdx + (ay0 - by0) * bdy) / blen2;
1399
+ const double at1 = ((ax1 - bx0) * bdx + (ay1 - by0) * bdy) / blen2;
1400
+ return strict_between_zero_one(bt0) || strict_between_zero_one(bt1)
1401
+ || strict_between_zero_one(at0) || strict_between_zero_one(at1);
1402
+ }
1403
+ const double t = (cx * bdy - cy * bdx) / denom;
1404
+ const double u = (cx * ady - cy * adx) / denom;
1405
+ if (t < -1e-9 || t > 1.0 + 1e-9 || u < -1e-9 || u > 1.0 + 1e-9) {
1406
+ return false;
1407
+ }
1408
+ const bool a_interior = strict_between_zero_one(t);
1409
+ const bool b_interior = strict_between_zero_one(u);
1410
+ if (a_interior || b_interior) {
1411
+ return true;
1412
+ }
1413
+ const double ix_a = ax0 + t * adx;
1414
+ const double iy_a = ay0 + t * ady;
1415
+ return !(point_equal_double(ix_a, iy_a, ax0, ay0)
1416
+ || point_equal_double(ix_a, iy_a, ax1, ay1))
1417
+ || !(point_equal_double(ix_a, iy_a, bx0, by0)
1418
+ || point_equal_double(ix_a, iy_a, bx1, by1));
1419
+ }
1420
+
1421
+ __global__ void one_sided_split_required_kernel(
1422
+ const double* segment_points,
1423
+ std::uint64_t segment_count,
1424
+ std::uint32_t* split_required) {
1425
+ const std::uint64_t left_index = blockIdx.x * blockDim.x + threadIdx.x;
1426
+ const std::uint64_t right_index = blockIdx.y * blockDim.y + threadIdx.y;
1427
+ if (left_index >= segment_count || right_index >= segment_count || right_index <= left_index || *split_required) {
1428
+ return;
1429
+ }
1430
+ if (segment_pairs_require_split(segment_points + left_index * 4, segment_points + right_index * 4)) {
1431
+ atomicExch(split_required, 1u);
1432
+ }
1433
+ }
1434
+
1435
+ __device__ std::int64_t rect_area_twice(std::int64_t x0, std::int64_t y0, std::int64_t x1, std::int64_t y1) {
1436
+ const std::int64_t width = x1 - x0;
1437
+ const std::int64_t height = y1 - y0;
1438
+ if (width <= 0 || height <= 0) {
1439
+ return 0;
1440
+ }
1441
+ return width * height * 2;
1442
+ }
1443
+
1444
+ __device__ std::int64_t rect_minus_rect_twice_area(const std::int64_t* rect, const std::int64_t* cutter, bool has_cutter) {
1445
+ if (!has_cutter) {
1446
+ return rect_area_twice(rect[0], rect[1], rect[2], rect[3]);
1447
+ }
1448
+ const std::int64_t ix0 = max(rect[0], cutter[0]);
1449
+ const std::int64_t iy0 = max(rect[1], cutter[1]);
1450
+ const std::int64_t ix1 = min(rect[2], cutter[2]);
1451
+ const std::int64_t iy1 = min(rect[3], cutter[3]);
1452
+ if (ix0 >= ix1 || iy0 >= iy1) {
1453
+ return rect_area_twice(rect[0], rect[1], rect[2], rect[3]);
1454
+ }
1455
+ return rect_area_twice(rect[0], rect[1], ix0, rect[3])
1456
+ + rect_area_twice(ix1, rect[1], rect[2], rect[3])
1457
+ + rect_area_twice(ix0, rect[1], ix1, iy0)
1458
+ + rect_area_twice(ix0, iy1, ix1, rect[3]);
1459
+ }
1460
+
1461
+ __device__ std::uint8_t emit_rect_minus_rect(
1462
+ const std::int64_t* rect,
1463
+ const std::int64_t* cutter,
1464
+ bool has_cutter,
1465
+ std::int64_t* out_rects) {
1466
+ if (!has_cutter) {
1467
+ out_rects[0] = rect[0];
1468
+ out_rects[1] = rect[1];
1469
+ out_rects[2] = rect[2];
1470
+ out_rects[3] = rect[3];
1471
+ return rect_area_twice(rect[0], rect[1], rect[2], rect[3]) ? 1 : 0;
1472
+ }
1473
+ const std::int64_t ix0 = max(rect[0], cutter[0]);
1474
+ const std::int64_t iy0 = max(rect[1], cutter[1]);
1475
+ const std::int64_t ix1 = min(rect[2], cutter[2]);
1476
+ const std::int64_t iy1 = min(rect[3], cutter[3]);
1477
+ if (ix0 >= ix1 || iy0 >= iy1) {
1478
+ out_rects[0] = rect[0];
1479
+ out_rects[1] = rect[1];
1480
+ out_rects[2] = rect[2];
1481
+ out_rects[3] = rect[3];
1482
+ return rect_area_twice(rect[0], rect[1], rect[2], rect[3]) ? 1 : 0;
1483
+ }
1484
+ const std::int64_t pieces[4][4] = {
1485
+ {rect[0], rect[1], ix0, rect[3]},
1486
+ {ix1, rect[1], rect[2], rect[3]},
1487
+ {ix0, rect[1], ix1, iy0},
1488
+ {ix0, iy1, ix1, rect[3]},
1489
+ };
1490
+ std::uint8_t count = 0;
1491
+ for (int i = 0; i < 4; ++i) {
1492
+ if (rect_area_twice(pieces[i][0], pieces[i][1], pieces[i][2], pieces[i][3])) {
1493
+ out_rects[count * 4 + 0] = pieces[i][0];
1494
+ out_rects[count * 4 + 1] = pieces[i][1];
1495
+ out_rects[count * 4 + 2] = pieces[i][2];
1496
+ out_rects[count * 4 + 3] = pieces[i][3];
1497
+ ++count;
1498
+ }
1499
+ }
1500
+ return count;
1501
+ }
1502
+
1503
+ __global__ void rect_exact_area_kernel(
1504
+ const std::int64_t* old_rects,
1505
+ const std::int64_t* new_rects,
1506
+ const std::uint8_t* old_present,
1507
+ const std::uint8_t* new_present,
1508
+ std::uint64_t n,
1509
+ std::int64_t* out_twice_areas) {
1510
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
1511
+ if (index >= n) {
1512
+ return;
1513
+ }
1514
+ const bool has_old = old_present[index] != 0;
1515
+ const bool has_new = new_present[index] != 0;
1516
+ const std::int64_t* old_rect = old_rects + index * 4;
1517
+ const std::int64_t* new_rect = new_rects + index * 4;
1518
+ const std::int64_t old_minus = has_old ? rect_minus_rect_twice_area(old_rect, new_rect, has_new) : 0;
1519
+ const std::int64_t new_minus = has_new ? rect_minus_rect_twice_area(new_rect, old_rect, has_old) : 0;
1520
+ out_twice_areas[index * 3 + 0] = old_minus;
1521
+ out_twice_areas[index * 3 + 1] = new_minus;
1522
+ out_twice_areas[index * 3 + 2] = old_minus + new_minus;
1523
+ }
1524
+
1525
+ __global__ void rect_exact_fragments_kernel(
1526
+ const std::int64_t* old_rects,
1527
+ const std::int64_t* new_rects,
1528
+ const std::uint8_t* old_present,
1529
+ const std::uint8_t* new_present,
1530
+ std::uint64_t n,
1531
+ std::int64_t* out_rects,
1532
+ std::uint8_t* out_counts,
1533
+ std::int64_t* out_twice_areas) {
1534
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
1535
+ if (index >= n) {
1536
+ return;
1537
+ }
1538
+ const bool has_old = old_present[index] != 0;
1539
+ const bool has_new = new_present[index] != 0;
1540
+ const std::int64_t* old_rect = old_rects + index * 4;
1541
+ const std::int64_t* new_rect = new_rects + index * 4;
1542
+ std::int64_t* old_out = out_rects + index * 8 * 4;
1543
+ std::int64_t* new_out = old_out + 4 * 4;
1544
+ std::uint8_t old_count = 0;
1545
+ std::uint8_t new_count = 0;
1546
+ if (has_old) {
1547
+ old_count = emit_rect_minus_rect(old_rect, new_rect, has_new, old_out);
1548
+ }
1549
+ if (has_new) {
1550
+ new_count = emit_rect_minus_rect(new_rect, old_rect, has_old, new_out);
1551
+ }
1552
+ out_counts[index * 2 + 0] = old_count;
1553
+ out_counts[index * 2 + 1] = new_count;
1554
+ std::int64_t old_area = 0;
1555
+ std::int64_t new_area = 0;
1556
+ for (std::uint8_t i = 0; i < old_count; ++i) {
1557
+ old_area += rect_area_twice(old_out[i * 4 + 0], old_out[i * 4 + 1], old_out[i * 4 + 2], old_out[i * 4 + 3]);
1558
+ }
1559
+ for (std::uint8_t i = 0; i < new_count; ++i) {
1560
+ new_area += rect_area_twice(new_out[i * 4 + 0], new_out[i * 4 + 1], new_out[i * 4 + 2], new_out[i * 4 + 3]);
1561
+ }
1562
+ out_twice_areas[index * 3 + 0] = old_area;
1563
+ out_twice_areas[index * 3 + 1] = new_area;
1564
+ out_twice_areas[index * 3 + 2] = old_area + new_area;
1565
+ }
1566
+
1567
+ __device__ void insert_coord_sorted(std::int64_t value, std::int64_t* coords, std::uint8_t* count) {
1568
+ for (std::uint8_t i = 0; i < *count; ++i) {
1569
+ if (coords[i] == value) {
1570
+ return;
1571
+ }
1572
+ }
1573
+ std::uint8_t pos = *count;
1574
+ while (pos > 0 && coords[pos - 1] > value) {
1575
+ coords[pos] = coords[pos - 1];
1576
+ --pos;
1577
+ }
1578
+ coords[pos] = value;
1579
+ ++(*count);
1580
+ }
1581
+
1582
+ __device__ bool rect_contains_cell_device(const std::int64_t* rect, std::int64_t x0, std::int64_t y0, std::int64_t x1, std::int64_t y1) {
1583
+ return rect[0] <= x0 && x1 <= rect[2] && rect[1] <= y0 && y1 <= rect[3];
1584
+ }
1585
+
1586
+ __device__ bool any_rect_contains_cell_device(
1587
+ const std::int64_t* rects,
1588
+ std::uint8_t count,
1589
+ std::int64_t x0,
1590
+ std::int64_t y0,
1591
+ std::int64_t x1,
1592
+ std::int64_t y1) {
1593
+ for (std::uint8_t i = 0; i < count; ++i) {
1594
+ if (rect_contains_cell_device(rects + i * 4, x0, y0, x1, y1)) {
1595
+ return true;
1596
+ }
1597
+ }
1598
+ return false;
1599
+ }
1600
+
1601
+ __device__ std::uint16_t emit_rect_set_minus_rect_set_device(
1602
+ const std::int64_t* source,
1603
+ std::uint8_t source_count,
1604
+ const std::int64_t* cutters,
1605
+ std::uint8_t cutter_count,
1606
+ std::uint16_t max_fragments,
1607
+ std::int64_t* out_rects,
1608
+ std::int64_t* out_twice_area,
1609
+ std::uint32_t* error_flag) {
1610
+ std::int64_t xs[kMaxRectSetCoords];
1611
+ std::int64_t ys[kMaxRectSetCoords];
1612
+ std::uint8_t x_count = 0;
1613
+ std::uint8_t y_count = 0;
1614
+ for (std::uint8_t i = 0; i < source_count; ++i) {
1615
+ const std::int64_t* rect = source + i * 4;
1616
+ insert_coord_sorted(rect[0], xs, &x_count);
1617
+ insert_coord_sorted(rect[2], xs, &x_count);
1618
+ insert_coord_sorted(rect[1], ys, &y_count);
1619
+ insert_coord_sorted(rect[3], ys, &y_count);
1620
+ }
1621
+ for (std::uint8_t i = 0; i < cutter_count; ++i) {
1622
+ const std::int64_t* rect = cutters + i * 4;
1623
+ insert_coord_sorted(rect[0], xs, &x_count);
1624
+ insert_coord_sorted(rect[2], xs, &x_count);
1625
+ insert_coord_sorted(rect[1], ys, &y_count);
1626
+ insert_coord_sorted(rect[3], ys, &y_count);
1627
+ }
1628
+ std::uint16_t out_count = 0;
1629
+ std::int64_t area = 0;
1630
+ for (std::uint8_t xi = 0; xi + 1 < x_count; ++xi) {
1631
+ const std::int64_t x0 = xs[xi];
1632
+ const std::int64_t x1 = xs[xi + 1];
1633
+ if (x0 == x1) {
1634
+ continue;
1635
+ }
1636
+ for (std::uint8_t yi = 0; yi + 1 < y_count; ++yi) {
1637
+ const std::int64_t y0 = ys[yi];
1638
+ const std::int64_t y1 = ys[yi + 1];
1639
+ if (y0 == y1) {
1640
+ continue;
1641
+ }
1642
+ if (!any_rect_contains_cell_device(source, source_count, x0, y0, x1, y1)) {
1643
+ continue;
1644
+ }
1645
+ if (any_rect_contains_cell_device(cutters, cutter_count, x0, y0, x1, y1)) {
1646
+ continue;
1647
+ }
1648
+ if (out_count >= max_fragments) {
1649
+ *error_flag = 2;
1650
+ *out_twice_area = area;
1651
+ return out_count;
1652
+ }
1653
+ std::int64_t* out = out_rects + out_count * 4;
1654
+ out[0] = x0;
1655
+ out[1] = y0;
1656
+ out[2] = x1;
1657
+ out[3] = y1;
1658
+ area += rect_area_twice(x0, y0, x1, y1);
1659
+ ++out_count;
1660
+ }
1661
+ }
1662
+ *out_twice_area = area;
1663
+ return out_count;
1664
+ }
1665
+
1666
+ __global__ void rect_set_exact_fragments_kernel(
1667
+ const std::int64_t* old_rects,
1668
+ const std::int64_t* new_rects,
1669
+ const std::uint8_t* old_counts,
1670
+ const std::uint8_t* new_counts,
1671
+ std::uint64_t task_count,
1672
+ std::uint16_t max_rects,
1673
+ std::uint16_t max_fragments,
1674
+ std::int64_t* out_rects,
1675
+ std::uint16_t* out_counts,
1676
+ std::int64_t* out_twice_areas,
1677
+ std::uint32_t* error_flags) {
1678
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
1679
+ if (index >= task_count) {
1680
+ return;
1681
+ }
1682
+ const std::uint8_t old_count = old_counts[index];
1683
+ const std::uint8_t new_count = new_counts[index];
1684
+ if (old_count > max_rects || new_count > max_rects || max_rects > kMaxRectSetRects) {
1685
+ error_flags[index] = 1;
1686
+ return;
1687
+ }
1688
+ const std::int64_t* old_task = old_rects + index * max_rects * 4;
1689
+ const std::int64_t* new_task = new_rects + index * max_rects * 4;
1690
+ std::int64_t* old_out = out_rects + index * 2 * max_fragments * 4;
1691
+ std::int64_t* new_out = old_out + max_fragments * 4;
1692
+ std::int64_t old_area = 0;
1693
+ std::int64_t new_area = 0;
1694
+ const std::uint16_t old_out_count = emit_rect_set_minus_rect_set_device(
1695
+ old_task,
1696
+ old_count,
1697
+ new_task,
1698
+ new_count,
1699
+ max_fragments,
1700
+ old_out,
1701
+ &old_area,
1702
+ error_flags + index);
1703
+ const std::uint16_t new_out_count = emit_rect_set_minus_rect_set_device(
1704
+ new_task,
1705
+ new_count,
1706
+ old_task,
1707
+ old_count,
1708
+ max_fragments,
1709
+ new_out,
1710
+ &new_area,
1711
+ error_flags + index);
1712
+ out_counts[index * 2 + 0] = old_out_count;
1713
+ out_counts[index * 2 + 1] = new_out_count;
1714
+ out_twice_areas[index * 3 + 0] = old_area;
1715
+ out_twice_areas[index * 3 + 1] = new_area;
1716
+ out_twice_areas[index * 3 + 2] = old_area + new_area;
1717
+ }
1718
+
1719
+ struct DeviceLoopTraceStats {
1720
+ std::uint64_t fragment_count;
1721
+ std::uint64_t vertex_count;
1722
+ std::uint64_t dropped_zero_area_loop_count;
1723
+ std::uint32_t error;
1724
+ };
1725
+
1726
+ __device__ bool loop_point_equal(const std::int64_t* points, std::uint64_t left, std::uint64_t right) {
1727
+ return points[left * 2] == points[right * 2] && points[left * 2 + 1] == points[right * 2 + 1];
1728
+ }
1729
+
1730
+ __device__ bool loop_edge_less(const std::int64_t* edges, std::uint64_t left, std::uint64_t right) {
1731
+ for (int axis = 0; axis < 4; ++axis) {
1732
+ const auto left_value = edges[left * 4 + axis];
1733
+ const auto right_value = edges[right * 4 + axis];
1734
+ if (left_value != right_value) {
1735
+ return left_value < right_value;
1736
+ }
1737
+ }
1738
+ return false;
1739
+ }
1740
+
1741
+ __device__ bool loop_point_less_xy(std::int64_t ax, std::int64_t ay, std::int64_t bx, std::int64_t by) {
1742
+ return ax < bx || (ax == bx && ay < by);
1743
+ }
1744
+
1745
+ __device__ bool loop_edge_start_matches(const std::int64_t* edges, std::uint64_t edge_index, std::int64_t x, std::int64_t y) {
1746
+ return edges[edge_index * 4] == x && edges[edge_index * 4 + 1] == y;
1747
+ }
1748
+
1749
+ __device__ bool loop_edge_stop_matches(const std::int64_t* edges, std::uint64_t edge_index, std::int64_t x, std::int64_t y) {
1750
+ return edges[edge_index * 4 + 2] == x && edges[edge_index * 4 + 3] == y;
1751
+ }
1752
+
1753
+ __device__ bool loop_collinear(std::int64_t ax, std::int64_t ay, std::int64_t bx, std::int64_t by, std::int64_t cx, std::int64_t cy) {
1754
+ const long long left = static_cast<long long>(bx - ax) * static_cast<long long>(cy - by);
1755
+ const long long right = static_cast<long long>(by - ay) * static_cast<long long>(cx - bx);
1756
+ return left == right;
1757
+ }
1758
+
1759
+ __device__ void loop_turn_key(
1760
+ std::int64_t px,
1761
+ std::int64_t py,
1762
+ std::int64_t cx,
1763
+ std::int64_t cy,
1764
+ std::int64_t nx,
1765
+ std::int64_t ny,
1766
+ int* turn_class,
1767
+ unsigned long long* abs_cross,
1768
+ long long* dot) {
1769
+ const long long in_x = static_cast<long long>(cx - px);
1770
+ const long long in_y = static_cast<long long>(cy - py);
1771
+ const long long out_x = static_cast<long long>(nx - cx);
1772
+ const long long out_y = static_cast<long long>(ny - cy);
1773
+ const long long cross = in_x * out_y - in_y * out_x;
1774
+ *dot = in_x * out_x + in_y * out_y;
1775
+ if (cross < 0) {
1776
+ *turn_class = 0;
1777
+ *abs_cross = static_cast<unsigned long long>(-cross);
1778
+ } else if (cross == 0 && *dot > 0) {
1779
+ *turn_class = 1;
1780
+ *abs_cross = 0;
1781
+ } else if (cross > 0) {
1782
+ *turn_class = 2;
1783
+ *abs_cross = static_cast<unsigned long long>(cross);
1784
+ } else {
1785
+ *turn_class = 3;
1786
+ *abs_cross = 0;
1787
+ }
1788
+ }
1789
+
1790
+ __device__ bool loop_candidate_better(
1791
+ std::int64_t px,
1792
+ std::int64_t py,
1793
+ std::int64_t cx,
1794
+ std::int64_t cy,
1795
+ std::int64_t candidate_x,
1796
+ std::int64_t candidate_y,
1797
+ std::int64_t best_x,
1798
+ std::int64_t best_y) {
1799
+ int candidate_class = 0;
1800
+ int best_class = 0;
1801
+ unsigned long long candidate_abs_cross = 0;
1802
+ unsigned long long best_abs_cross = 0;
1803
+ long long candidate_dot = 0;
1804
+ long long best_dot = 0;
1805
+ loop_turn_key(px, py, cx, cy, candidate_x, candidate_y, &candidate_class, &candidate_abs_cross, &candidate_dot);
1806
+ loop_turn_key(px, py, cx, cy, best_x, best_y, &best_class, &best_abs_cross, &best_dot);
1807
+ if (candidate_class != best_class) {
1808
+ return candidate_class < best_class;
1809
+ }
1810
+ if (candidate_abs_cross != best_abs_cross) {
1811
+ return candidate_abs_cross > best_abs_cross;
1812
+ }
1813
+ if (candidate_dot != best_dot) {
1814
+ return candidate_dot > best_dot;
1815
+ }
1816
+ return loop_point_less_xy(candidate_x, candidate_y, best_x, best_y);
1817
+ }
1818
+
1819
+ __device__ std::uint64_t loop_clean_duplicates(
1820
+ const std::int64_t* input_points,
1821
+ std::uint64_t input_count,
1822
+ std::int64_t* output_points) {
1823
+ std::uint64_t out_count = 0;
1824
+ for (std::uint64_t index = 0; index < input_count; ++index) {
1825
+ const auto x = input_points[index * 2];
1826
+ const auto y = input_points[index * 2 + 1];
1827
+ if (out_count == 0 || output_points[(out_count - 1) * 2] != x || output_points[(out_count - 1) * 2 + 1] != y) {
1828
+ output_points[out_count * 2] = x;
1829
+ output_points[out_count * 2 + 1] = y;
1830
+ ++out_count;
1831
+ }
1832
+ }
1833
+ if (out_count > 1 && output_points[0] == output_points[(out_count - 1) * 2] && output_points[1] == output_points[(out_count - 1) * 2 + 1]) {
1834
+ --out_count;
1835
+ }
1836
+ return out_count;
1837
+ }
1838
+
1839
+ __device__ std::uint64_t loop_clean_collinear_once(
1840
+ const std::int64_t* input_points,
1841
+ std::uint64_t input_count,
1842
+ std::int64_t* output_points,
1843
+ bool* changed) {
1844
+ *changed = false;
1845
+ if (input_count < 3) {
1846
+ for (std::uint64_t index = 0; index < input_count; ++index) {
1847
+ output_points[index * 2] = input_points[index * 2];
1848
+ output_points[index * 2 + 1] = input_points[index * 2 + 1];
1849
+ }
1850
+ return input_count;
1851
+ }
1852
+ std::uint64_t out_count = 0;
1853
+ for (std::uint64_t index = 0; index < input_count; ++index) {
1854
+ const std::uint64_t previous = (index + input_count - 1) % input_count;
1855
+ const std::uint64_t following = (index + 1) % input_count;
1856
+ const auto ax = input_points[previous * 2];
1857
+ const auto ay = input_points[previous * 2 + 1];
1858
+ const auto bx = input_points[index * 2];
1859
+ const auto by = input_points[index * 2 + 1];
1860
+ const auto cx = input_points[following * 2];
1861
+ const auto cy = input_points[following * 2 + 1];
1862
+ if (loop_collinear(ax, ay, bx, by, cx, cy)) {
1863
+ *changed = true;
1864
+ continue;
1865
+ }
1866
+ output_points[out_count * 2] = bx;
1867
+ output_points[out_count * 2 + 1] = by;
1868
+ ++out_count;
1869
+ }
1870
+ return out_count;
1871
+ }
1872
+
1873
+ __device__ long long loop_twice_area(const std::int64_t* points, std::uint64_t count) {
1874
+ long long area = 0;
1875
+ for (std::uint64_t index = 0; index < count; ++index) {
1876
+ const auto next = (index + 1) % count;
1877
+ area += static_cast<long long>(points[index * 2]) * static_cast<long long>(points[next * 2 + 1])
1878
+ - static_cast<long long>(points[next * 2]) * static_cast<long long>(points[index * 2 + 1]);
1879
+ }
1880
+ return area;
1881
+ }
1882
+
1883
+ __global__ void boundary_loop_next_kernel(
1884
+ const std::int64_t* edges,
1885
+ std::uint64_t edge_count,
1886
+ std::uint64_t* next_indices) {
1887
+ const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
1888
+ if (index >= edge_count) {
1889
+ return;
1890
+ }
1891
+ const auto previous_x = edges[index * 4];
1892
+ const auto previous_y = edges[index * 4 + 1];
1893
+ const auto current_x = edges[index * 4 + 2];
1894
+ const auto current_y = edges[index * 4 + 3];
1895
+ bool have_best = false;
1896
+ std::uint64_t best_edge = UINT64_MAX;
1897
+ std::int64_t best_x = 0;
1898
+ std::int64_t best_y = 0;
1899
+ for (std::uint64_t candidate = 0; candidate < edge_count; ++candidate) {
1900
+ if (!loop_edge_start_matches(edges, candidate, current_x, current_y)) {
1901
+ continue;
1902
+ }
1903
+ const auto candidate_x = edges[candidate * 4 + 2];
1904
+ const auto candidate_y = edges[candidate * 4 + 3];
1905
+ if (!have_best || loop_candidate_better(previous_x, previous_y, current_x, current_y, candidate_x, candidate_y, best_x, best_y)) {
1906
+ best_edge = candidate;
1907
+ best_x = candidate_x;
1908
+ best_y = candidate_y;
1909
+ have_best = true;
1910
+ }
1911
+ }
1912
+ next_indices[index] = have_best ? best_edge : UINT64_MAX;
1913
+ }
1914
+
1915
+ __global__ void boundary_loop_trace_kernel(
1916
+ const std::int64_t* edges,
1917
+ std::uint64_t edge_count,
1918
+ const std::uint64_t* next_indices,
1919
+ std::uint8_t* remaining,
1920
+ std::int64_t* scratch_a,
1921
+ std::int64_t* scratch_b,
1922
+ std::int64_t* out_vertices,
1923
+ std::uint64_t* out_offsets,
1924
+ std::int64_t* out_bboxes,
1925
+ std::int64_t* out_twice_areas,
1926
+ DeviceLoopTraceStats* stats) {
1927
+ if (blockIdx.x != 0 || threadIdx.x != 0) {
1928
+ return;
1929
+ }
1930
+ stats->fragment_count = 0;
1931
+ stats->vertex_count = 0;
1932
+ stats->dropped_zero_area_loop_count = 0;
1933
+ stats->error = 0;
1934
+ out_offsets[0] = 0;
1935
+
1936
+ while (true) {
1937
+ bool found_seed = false;
1938
+ std::uint64_t seed = 0;
1939
+ for (std::uint64_t index = 0; index < edge_count; ++index) {
1940
+ if (!remaining[index]) {
1941
+ continue;
1942
+ }
1943
+ if (!found_seed || loop_edge_less(edges, index, seed)) {
1944
+ seed = index;
1945
+ found_seed = true;
1946
+ }
1947
+ }
1948
+ if (!found_seed) {
1949
+ break;
1950
+ }
1951
+
1952
+ remaining[seed] = 0;
1953
+ const auto start_x = edges[seed * 4];
1954
+ const auto start_y = edges[seed * 4 + 1];
1955
+ std::int64_t previous_x = start_x;
1956
+ std::int64_t previous_y = start_y;
1957
+ std::int64_t current_x = edges[seed * 4 + 2];
1958
+ std::int64_t current_y = edges[seed * 4 + 3];
1959
+ std::uint64_t current_edge = seed;
1960
+ std::uint64_t raw_count = 0;
1961
+ scratch_a[raw_count * 2] = start_x;
1962
+ scratch_a[raw_count * 2 + 1] = start_y;
1963
+ ++raw_count;
1964
+ scratch_a[raw_count * 2] = current_x;
1965
+ scratch_a[raw_count * 2 + 1] = current_y;
1966
+ ++raw_count;
1967
+
1968
+ while (!(current_x == start_x && current_y == start_y)) {
1969
+ const std::uint64_t best_edge = next_indices[current_edge];
1970
+ if (best_edge == UINT64_MAX || !remaining[best_edge]) {
1971
+ break;
1972
+ }
1973
+ const auto best_x = edges[best_edge * 4 + 2];
1974
+ const auto best_y = edges[best_edge * 4 + 3];
1975
+ remaining[best_edge] = 0;
1976
+ if (!(best_x == start_x && best_y == start_y)) {
1977
+ if (raw_count >= edge_count) {
1978
+ stats->error = 1;
1979
+ return;
1980
+ }
1981
+ scratch_a[raw_count * 2] = best_x;
1982
+ scratch_a[raw_count * 2 + 1] = best_y;
1983
+ ++raw_count;
1984
+ }
1985
+ previous_x = current_x;
1986
+ previous_y = current_y;
1987
+ current_x = best_x;
1988
+ current_y = best_y;
1989
+ current_edge = best_edge;
1990
+ }
1991
+
1992
+ std::uint64_t clean_count = loop_clean_duplicates(scratch_a, raw_count, scratch_b);
1993
+ bool changed = true;
1994
+ bool scratch_b_current = true;
1995
+ while (changed && clean_count >= 3) {
1996
+ changed = false;
1997
+ if (scratch_b_current) {
1998
+ clean_count = loop_clean_collinear_once(scratch_b, clean_count, scratch_a, &changed);
1999
+ scratch_b_current = false;
2000
+ } else {
2001
+ clean_count = loop_clean_collinear_once(scratch_a, clean_count, scratch_b, &changed);
2002
+ scratch_b_current = true;
2003
+ }
2004
+ }
2005
+ const std::int64_t* clean_points = scratch_b_current ? scratch_b : scratch_a;
2006
+ if (clean_count < 3) {
2007
+ ++stats->dropped_zero_area_loop_count;
2008
+ continue;
2009
+ }
2010
+ const auto area = loop_twice_area(clean_points, clean_count);
2011
+ if (area == 0) {
2012
+ ++stats->dropped_zero_area_loop_count;
2013
+ continue;
2014
+ }
2015
+ const std::uint64_t fragment_index = stats->fragment_count;
2016
+ if (fragment_index >= edge_count || stats->vertex_count + clean_count > edge_count) {
2017
+ stats->error = 2;
2018
+ return;
2019
+ }
2020
+ std::int64_t x0 = clean_points[0];
2021
+ std::int64_t y0 = clean_points[1];
2022
+ std::int64_t x1 = clean_points[0];
2023
+ std::int64_t y1 = clean_points[1];
2024
+ for (std::uint64_t index = 0; index < clean_count; ++index) {
2025
+ const auto x = clean_points[index * 2];
2026
+ const auto y = clean_points[index * 2 + 1];
2027
+ out_vertices[(stats->vertex_count + index) * 2] = x;
2028
+ out_vertices[(stats->vertex_count + index) * 2 + 1] = y;
2029
+ x0 = min(x0, x);
2030
+ y0 = min(y0, y);
2031
+ x1 = max(x1, x);
2032
+ y1 = max(y1, y);
2033
+ }
2034
+ out_bboxes[fragment_index * 4] = x0;
2035
+ out_bboxes[fragment_index * 4 + 1] = y0;
2036
+ out_bboxes[fragment_index * 4 + 2] = x1;
2037
+ out_bboxes[fragment_index * 4 + 3] = y1;
2038
+ out_twice_areas[fragment_index] = area;
2039
+ stats->vertex_count += clean_count;
2040
+ ++stats->fragment_count;
2041
+ out_offsets[stats->fragment_count] = stats->vertex_count;
2042
+ }
2043
+ }
2044
+
2045
+ __global__ void axis_raw_edge_kernel(
2046
+ const std::int64_t* vertices_xy,
2047
+ const std::uint64_t* fragment_offsets,
2048
+ std::uint64_t fragment_count,
2049
+ AxisRawEdge* raw_edges,
2050
+ AxisEndpointKey* endpoints,
2051
+ std::uint32_t* error) {
2052
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2053
+ for (std::uint64_t fragment_index = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2054
+ fragment_index < fragment_count;
2055
+ fragment_index += stride) {
2056
+ const std::uint64_t start = fragment_offsets[fragment_index];
2057
+ const std::uint64_t stop = fragment_offsets[fragment_index + 1];
2058
+ if (stop <= start + 2) {
2059
+ continue;
2060
+ }
2061
+ for (std::uint64_t vertex_index = start; vertex_index < stop; ++vertex_index) {
2062
+ const std::uint64_t next_index = vertex_index + 1 == stop ? start : vertex_index + 1;
2063
+ const auto x0 = vertices_xy[vertex_index * 2];
2064
+ const auto y0 = vertices_xy[vertex_index * 2 + 1];
2065
+ const auto x1 = vertices_xy[next_index * 2];
2066
+ const auto y1 = vertices_xy[next_index * 2 + 1];
2067
+ AxisRawEdge edge{};
2068
+ AxisEndpointKey first_endpoint{2, 0, 0};
2069
+ AxisEndpointKey second_endpoint{2, 0, 0};
2070
+ if (x0 == x1 && y0 == y1) {
2071
+ raw_edges[vertex_index] = edge;
2072
+ endpoints[vertex_index * 2] = first_endpoint;
2073
+ endpoints[vertex_index * 2 + 1] = second_endpoint;
2074
+ continue;
2075
+ }
2076
+ if (x0 == x1) {
2077
+ edge = AxisRawEdge{x0, y0, x1, y1, x0, y0, y1, 1, 1};
2078
+ first_endpoint = AxisEndpointKey{1, x0, y0};
2079
+ second_endpoint = AxisEndpointKey{1, x0, y1};
2080
+ } else if (y0 == y1) {
2081
+ edge = AxisRawEdge{x0, y0, x1, y1, y0, x0, x1, 0, 1};
2082
+ first_endpoint = AxisEndpointKey{0, y0, x0};
2083
+ second_endpoint = AxisEndpointKey{0, y0, x1};
2084
+ } else {
2085
+ atomicOr(error, 1u);
2086
+ }
2087
+ raw_edges[vertex_index] = edge;
2088
+ endpoints[vertex_index * 2] = first_endpoint;
2089
+ endpoints[vertex_index * 2 + 1] = second_endpoint;
2090
+ }
2091
+ }
2092
+ }
2093
+
2094
+ __device__ bool axis_endpoint_in_edge(const AxisEndpointKey& endpoint, const AxisRawEdge& edge, std::int64_t low, std::int64_t high) {
2095
+ return endpoint.vertical == edge.vertical
2096
+ && endpoint.line == edge.line
2097
+ && low <= endpoint.projection
2098
+ && endpoint.projection <= high;
2099
+ }
2100
+
2101
+ __global__ void axis_split_count_kernel(
2102
+ const AxisRawEdge* raw_edges,
2103
+ std::uint64_t raw_edge_count,
2104
+ const AxisEndpointKey* endpoints,
2105
+ std::uint64_t endpoint_count,
2106
+ std::uint64_t* split_counts) {
2107
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2108
+ for (std::uint64_t edge_index = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2109
+ edge_index < raw_edge_count;
2110
+ edge_index += stride) {
2111
+ const auto edge = raw_edges[edge_index];
2112
+ if (!edge.valid) {
2113
+ split_counts[edge_index] = 0;
2114
+ continue;
2115
+ }
2116
+ const auto low = min(edge.p0, edge.p1);
2117
+ const auto high = max(edge.p0, edge.p1);
2118
+ std::uint64_t breakpoint_count = 0;
2119
+ for (std::uint64_t endpoint_index = 0; endpoint_index < endpoint_count; ++endpoint_index) {
2120
+ if (axis_endpoint_in_edge(endpoints[endpoint_index], edge, low, high)) {
2121
+ ++breakpoint_count;
2122
+ }
2123
+ }
2124
+ split_counts[edge_index] = breakpoint_count > 1 ? breakpoint_count - 1 : 0;
2125
+ }
2126
+ }
2127
+
2128
+ __device__ EdgeKey axis_edge_from_line_piece(std::uint8_t vertical, std::int64_t line, std::int64_t a, std::int64_t b, bool reverse) {
2129
+ if (vertical) {
2130
+ return reverse ? EdgeKey{line, b, line, a} : EdgeKey{line, a, line, b};
2131
+ }
2132
+ return reverse ? EdgeKey{b, line, a, line} : EdgeKey{a, line, b, line};
2133
+ }
2134
+
2135
+ __global__ void axis_split_emit_kernel(
2136
+ const AxisRawEdge* raw_edges,
2137
+ std::uint64_t raw_edge_count,
2138
+ const AxisEndpointKey* endpoints,
2139
+ std::uint64_t endpoint_count,
2140
+ const std::uint64_t* split_offsets,
2141
+ EdgeKey* split_edges) {
2142
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2143
+ for (std::uint64_t edge_index = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2144
+ edge_index < raw_edge_count;
2145
+ edge_index += stride) {
2146
+ const auto edge = raw_edges[edge_index];
2147
+ if (!edge.valid) {
2148
+ continue;
2149
+ }
2150
+ const auto low = min(edge.p0, edge.p1);
2151
+ const auto high = max(edge.p0, edge.p1);
2152
+ const bool reverse = edge.p0 > edge.p1;
2153
+ bool have_previous = false;
2154
+ std::int64_t previous = 0;
2155
+ std::uint64_t out_index = split_offsets[edge_index];
2156
+ for (std::uint64_t endpoint_index = 0; endpoint_index < endpoint_count; ++endpoint_index) {
2157
+ const auto endpoint = endpoints[endpoint_index];
2158
+ if (!axis_endpoint_in_edge(endpoint, edge, low, high)) {
2159
+ continue;
2160
+ }
2161
+ if (!have_previous) {
2162
+ previous = endpoint.projection;
2163
+ have_previous = true;
2164
+ continue;
2165
+ }
2166
+ if (previous != endpoint.projection) {
2167
+ split_edges[out_index++] = axis_edge_from_line_piece(edge.vertical, edge.line, previous, endpoint.projection, reverse);
2168
+ }
2169
+ previous = endpoint.projection;
2170
+ }
2171
+ }
2172
+ }
2173
+
2174
+ __device__ EdgeKey edge_reverse_key(const EdgeKey& edge) {
2175
+ return EdgeKey{edge.x1, edge.y1, edge.x0, edge.y0};
2176
+ }
2177
+
2178
+ __device__ bool edge_less_device(const EdgeKey& left, const EdgeKey& right) {
2179
+ if (left.x0 != right.x0) return left.x0 < right.x0;
2180
+ if (left.y0 != right.y0) return left.y0 < right.y0;
2181
+ if (left.x1 != right.x1) return left.x1 < right.x1;
2182
+ return left.y1 < right.y1;
2183
+ }
2184
+
2185
+ __device__ bool edge_equal_device(const EdgeKey& left, const EdgeKey& right) {
2186
+ return left.x0 == right.x0 && left.y0 == right.y0 && left.x1 == right.x1 && left.y1 == right.y1;
2187
+ }
2188
+
2189
+ __device__ std::uint64_t edge_lower_bound_device(const EdgeKey* edges, std::uint64_t count, const EdgeKey& key) {
2190
+ std::uint64_t left = 0;
2191
+ std::uint64_t right = count;
2192
+ while (left < right) {
2193
+ const std::uint64_t middle = left + (right - left) / 2;
2194
+ if (edge_less_device(edges[middle], key)) {
2195
+ left = middle + 1;
2196
+ } else {
2197
+ right = middle;
2198
+ }
2199
+ }
2200
+ return left;
2201
+ }
2202
+
2203
+ __global__ void edge_surplus_count_kernel(
2204
+ const EdgeKey* unique_edges,
2205
+ const std::uint64_t* counts,
2206
+ std::uint64_t unique_count,
2207
+ std::uint64_t* surplus_counts) {
2208
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2209
+ for (std::uint64_t index = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2210
+ index < unique_count;
2211
+ index += stride) {
2212
+ const auto reverse = edge_reverse_key(unique_edges[index]);
2213
+ const auto reverse_index = edge_lower_bound_device(unique_edges, unique_count, reverse);
2214
+ std::uint64_t reverse_count = 0;
2215
+ if (reverse_index < unique_count && edge_equal_device(unique_edges[reverse_index], reverse)) {
2216
+ reverse_count = counts[reverse_index];
2217
+ }
2218
+ surplus_counts[index] = counts[index] > reverse_count ? counts[index] - reverse_count : 0;
2219
+ }
2220
+ }
2221
+
2222
+ __global__ void edge_surplus_emit_kernel(
2223
+ const EdgeKey* unique_edges,
2224
+ const std::uint64_t* surplus_counts,
2225
+ const std::uint64_t* surplus_offsets,
2226
+ std::uint64_t unique_count,
2227
+ std::int64_t* out_edges) {
2228
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2229
+ for (std::uint64_t index = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2230
+ index < unique_count;
2231
+ index += stride) {
2232
+ const auto edge = unique_edges[index];
2233
+ const auto count = surplus_counts[index];
2234
+ const auto start = surplus_offsets[index];
2235
+ for (std::uint64_t repeat = 0; repeat < count; ++repeat) {
2236
+ const std::uint64_t out_index = start + repeat;
2237
+ out_edges[out_index * 4] = edge.x0;
2238
+ out_edges[out_index * 4 + 1] = edge.y0;
2239
+ out_edges[out_index * 4 + 2] = edge.x1;
2240
+ out_edges[out_index * 4 + 3] = edge.y1;
2241
+ }
2242
+ }
2243
+ }
2244
+
2245
+ __global__ void split_free_raw_edge_kernel(
2246
+ const std::int64_t* vertices_xy,
2247
+ const std::uint64_t* fragment_offsets,
2248
+ std::uint64_t fragment_count,
2249
+ EdgeKey* raw_edges) {
2250
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2251
+ for (std::uint64_t fragment_index = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2252
+ fragment_index < fragment_count;
2253
+ fragment_index += stride) {
2254
+ const std::uint64_t start = fragment_offsets[fragment_index];
2255
+ const std::uint64_t stop = fragment_offsets[fragment_index + 1];
2256
+ if (stop <= start + 2) {
2257
+ continue;
2258
+ }
2259
+ for (std::uint64_t vertex_index = start; vertex_index < stop; ++vertex_index) {
2260
+ const std::uint64_t next_index = vertex_index + 1 == stop ? start : vertex_index + 1;
2261
+ raw_edges[vertex_index] = EdgeKey{
2262
+ vertices_xy[vertex_index * 2],
2263
+ vertices_xy[vertex_index * 2 + 1],
2264
+ vertices_xy[next_index * 2],
2265
+ vertices_xy[next_index * 2 + 1],
2266
+ };
2267
+ }
2268
+ }
2269
+ }
2270
+
2271
+ __device__ long long edge_cross_64(long long ax, long long ay, long long bx, long long by) {
2272
+ return ax * by - ay * bx;
2273
+ }
2274
+
2275
+ __device__ bool edge_same_undirected(const EdgeKey& left, const EdgeKey& right) {
2276
+ return (left.x0 == right.x0 && left.y0 == right.y0 && left.x1 == right.x1 && left.y1 == right.y1)
2277
+ || (left.x0 == right.x1 && left.y0 == right.y1 && left.x1 == right.x0 && left.y1 == right.y0);
2278
+ }
2279
+
2280
+ __device__ bool edge_collinear(const EdgeKey& left, const EdgeKey& right) {
2281
+ const long long ldx = static_cast<long long>(left.x1 - left.x0);
2282
+ const long long ldy = static_cast<long long>(left.y1 - left.y0);
2283
+ const long long rdx = static_cast<long long>(right.x1 - right.x0);
2284
+ const long long rdy = static_cast<long long>(right.y1 - right.y0);
2285
+ if (edge_cross_64(ldx, ldy, rdx, rdy) != 0) {
2286
+ return false;
2287
+ }
2288
+ const long long cx = static_cast<long long>(right.x0 - left.x0);
2289
+ const long long cy = static_cast<long long>(right.y0 - left.y0);
2290
+ return edge_cross_64(cx, cy, ldx, ldy) == 0;
2291
+ }
2292
+
2293
+ __device__ bool edge_positive_projection_overlap(const EdgeKey& left, const EdgeKey& right) {
2294
+ const auto ldx = left.x1 >= left.x0 ? left.x1 - left.x0 : left.x0 - left.x1;
2295
+ const auto ldy = left.y1 >= left.y0 ? left.y1 - left.y0 : left.y0 - left.y1;
2296
+ std::int64_t left_a = 0;
2297
+ std::int64_t left_b = 0;
2298
+ std::int64_t right_a = 0;
2299
+ std::int64_t right_b = 0;
2300
+ if (ldx >= ldy) {
2301
+ left_a = min(left.x0, left.x1);
2302
+ left_b = max(left.x0, left.x1);
2303
+ right_a = min(right.x0, right.x1);
2304
+ right_b = max(right.x0, right.x1);
2305
+ } else {
2306
+ left_a = min(left.y0, left.y1);
2307
+ left_b = max(left.y0, left.y1);
2308
+ right_a = min(right.y0, right.y1);
2309
+ right_b = max(right.y0, right.y1);
2310
+ }
2311
+ return max(left_a, right_a) < min(left_b, right_b);
2312
+ }
2313
+
2314
+ __global__ void split_free_required_kernel(
2315
+ const EdgeKey* raw_edges,
2316
+ std::uint64_t edge_count,
2317
+ std::uint32_t* split_required) {
2318
+ const std::uint64_t total_pairs = edge_count * edge_count;
2319
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2320
+ for (std::uint64_t linear = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2321
+ linear < total_pairs;
2322
+ linear += stride) {
2323
+ if (*split_required) {
2324
+ return;
2325
+ }
2326
+ const std::uint64_t left_index = linear / edge_count;
2327
+ const std::uint64_t right_index = linear - left_index * edge_count;
2328
+ if (right_index <= left_index) {
2329
+ continue;
2330
+ }
2331
+ const auto left = raw_edges[left_index];
2332
+ const auto right = raw_edges[right_index];
2333
+ if (EdgeInvalid{}(left) || EdgeInvalid{}(right)) {
2334
+ continue;
2335
+ }
2336
+ if (!edge_collinear(left, right) || !edge_positive_projection_overlap(left, right)) {
2337
+ continue;
2338
+ }
2339
+ if (!edge_same_undirected(left, right)) {
2340
+ atomicOr(split_required, 1u);
2341
+ return;
2342
+ }
2343
+ }
2344
+ }
2345
+
2346
+ __global__ void context_overlap_flag_kernel(
2347
+ const std::int64_t* component_bboxes,
2348
+ const std::int32_t* component_layers,
2349
+ const std::int32_t* component_datatypes,
2350
+ std::uint64_t component_count,
2351
+ const std::int64_t* polygon_bboxes,
2352
+ const std::int32_t* polygon_layers,
2353
+ const std::int32_t* polygon_datatypes,
2354
+ std::uint64_t polygon_count,
2355
+ std::uint64_t total_pairs,
2356
+ std::uint64_t* flags) {
2357
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2358
+ for (std::uint64_t linear = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2359
+ linear < total_pairs;
2360
+ linear += stride) {
2361
+ const std::uint64_t component_index = linear / polygon_count;
2362
+ const std::uint64_t polygon_index = linear - component_index * polygon_count;
2363
+ const auto cx0 = component_bboxes[component_index * 4];
2364
+ const auto cy0 = component_bboxes[component_index * 4 + 1];
2365
+ const auto cx1 = component_bboxes[component_index * 4 + 2];
2366
+ const auto cy1 = component_bboxes[component_index * 4 + 3];
2367
+ const auto px0 = polygon_bboxes[polygon_index * 4];
2368
+ const auto py0 = polygon_bboxes[polygon_index * 4 + 1];
2369
+ const auto px1 = polygon_bboxes[polygon_index * 4 + 2];
2370
+ const auto py1 = polygon_bboxes[polygon_index * 4 + 3];
2371
+ const bool same_layer = component_layers[component_index] == polygon_layers[polygon_index]
2372
+ && component_datatypes[component_index] == polygon_datatypes[polygon_index];
2373
+ const bool overlaps = px1 > cx0 && cx1 > px0 && max(py0, cy0) < min(py1, cy1);
2374
+ flags[linear] = same_layer && overlaps ? 1 : 0;
2375
+ }
2376
+ }
2377
+
2378
+ __global__ void context_overlap_emit_kernel(
2379
+ const std::uint64_t* flags,
2380
+ const std::uint64_t* offsets,
2381
+ std::uint64_t polygon_count,
2382
+ std::uint64_t total_pairs,
2383
+ std::uint64_t* out_component_indices,
2384
+ std::uint64_t* out_polygon_indices) {
2385
+ const std::uint64_t stride = static_cast<std::uint64_t>(blockDim.x) * static_cast<std::uint64_t>(gridDim.x);
2386
+ for (std::uint64_t linear = static_cast<std::uint64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
2387
+ linear < total_pairs;
2388
+ linear += stride) {
2389
+ if (!flags[linear]) {
2390
+ continue;
2391
+ }
2392
+ const std::uint64_t component_index = linear / polygon_count;
2393
+ const std::uint64_t polygon_index = linear - component_index * polygon_count;
2394
+ const std::uint64_t out_index = offsets[linear];
2395
+ out_component_indices[out_index] = component_index;
2396
+ out_polygon_indices[out_index] = polygon_index;
2397
+ }
2398
+ }
2399
+
2400
+ void check_cuda(cudaError_t status, const char* action) {
2401
+ if (status != cudaSuccess) {
2402
+ throw std::runtime_error(std::string(action) + ": " + cudaGetErrorString(status));
2403
+ }
2404
+ }
2405
+
2406
+ std::vector<DevicePolygon> pack_polygons(
2407
+ const std::int64_t* bboxes,
2408
+ const std::int32_t* layers,
2409
+ const std::int32_t* datatypes,
2410
+ const std::uint64_t* canonical_ids,
2411
+ std::uint64_t count) {
2412
+ std::vector<DevicePolygon> out;
2413
+ out.reserve(count);
2414
+ for (std::uint64_t i = 0; i < count; ++i) {
2415
+ out.push_back(DevicePolygon{
2416
+ bboxes[i * 4 + 0],
2417
+ bboxes[i * 4 + 1],
2418
+ bboxes[i * 4 + 2],
2419
+ bboxes[i * 4 + 3],
2420
+ layers[i],
2421
+ datatypes[i],
2422
+ canonical_ids[i],
2423
+ });
2424
+ }
2425
+ return out;
2426
+ }
2427
+
2428
+ std::vector<std::uint8_t> read_binary_file(const char* path) {
2429
+ std::ifstream input(path, std::ios::binary | std::ios::ate);
2430
+ if (!input) {
2431
+ throw std::runtime_error(std::string("unable to open file: ") + path);
2432
+ }
2433
+ const auto end = input.tellg();
2434
+ if (end < 0) {
2435
+ throw std::runtime_error(std::string("unable to determine file size: ") + path);
2436
+ }
2437
+ std::vector<std::uint8_t> data(static_cast<std::size_t>(end));
2438
+ input.seekg(0, std::ios::beg);
2439
+ if (!data.empty()) {
2440
+ input.read(reinterpret_cast<char*>(data.data()), static_cast<std::streamsize>(data.size()));
2441
+ if (!input) {
2442
+ throw std::runtime_error(std::string("unable to read file: ") + path);
2443
+ }
2444
+ }
2445
+ return data;
2446
+ }
2447
+
2448
+ SideAssignments build_side_assignments(
2449
+ const std::vector<DevicePolygon>& polygons,
2450
+ std::int64_t tile_size,
2451
+ std::uint64_t max_tiles_per_polygon,
2452
+ std::uint8_t side) {
2453
+ SideAssignments result;
2454
+ if (polygons.empty()) {
2455
+ return result;
2456
+ }
2457
+ const std::uint64_t n = polygons.size();
2458
+ thrust::device_vector<DevicePolygon> d_polygons(polygons.begin(), polygons.end());
2459
+ thrust::device_vector<std::uint64_t> d_counts(n);
2460
+ thrust::device_vector<std::uint8_t> d_oversized_flags(n);
2461
+ thrust::device_vector<DeviceOversized> d_oversized(n);
2462
+ thrust::device_vector<std::uint32_t> d_errors(n, 0);
2463
+
2464
+ auto* d_polygons_ptr = thrust::raw_pointer_cast(d_polygons.data());
2465
+ auto* d_counts_ptr = thrust::raw_pointer_cast(d_counts.data());
2466
+ auto* d_oversized_flags_ptr = thrust::raw_pointer_cast(d_oversized_flags.data());
2467
+ auto* d_oversized_ptr = thrust::raw_pointer_cast(d_oversized.data());
2468
+ auto* d_errors_ptr = thrust::raw_pointer_cast(d_errors.data());
2469
+
2470
+ const int threads = 256;
2471
+ const int blocks = static_cast<int>((n + threads - 1) / threads);
2472
+ count_kernel<<<blocks, threads>>>(
2473
+ d_polygons_ptr,
2474
+ n,
2475
+ tile_size,
2476
+ max_tiles_per_polygon,
2477
+ d_counts_ptr,
2478
+ d_oversized_flags_ptr,
2479
+ d_oversized_ptr,
2480
+ side,
2481
+ d_errors_ptr);
2482
+ check_cuda(cudaGetLastError(), "launch count kernel");
2483
+ check_cuda(cudaDeviceSynchronize(), "run count kernel");
2484
+
2485
+ const auto max_error = thrust::reduce(d_errors.begin(), d_errors.end(), std::uint32_t{0}, thrust::maximum<std::uint32_t>{});
2486
+ if (max_error) {
2487
+ throw std::overflow_error("CUDA assignment count overflow or invalid span");
2488
+ }
2489
+ std::vector<std::uint8_t> oversized_flags(n);
2490
+ std::vector<DeviceOversized> oversized(n);
2491
+ check_cuda(
2492
+ cudaMemcpy(oversized_flags.data(), d_oversized_flags_ptr, sizeof(std::uint8_t) * n, cudaMemcpyDeviceToHost),
2493
+ "copy oversized flags");
2494
+ check_cuda(cudaMemcpy(oversized.data(), d_oversized_ptr, sizeof(DeviceOversized) * n, cudaMemcpyDeviceToHost), "copy oversized");
2495
+
2496
+ thrust::device_vector<std::uint64_t> d_offsets(n);
2497
+ thrust::exclusive_scan(d_counts.begin(), d_counts.end(), d_offsets.begin());
2498
+
2499
+ std::uint64_t last_offset = 0;
2500
+ std::uint64_t last_count = 0;
2501
+ check_cuda(
2502
+ cudaMemcpy(&last_offset, thrust::raw_pointer_cast(d_offsets.data()) + (n - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost),
2503
+ "copy last offset");
2504
+ check_cuda(cudaMemcpy(&last_count, d_counts_ptr + (n - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy last count");
2505
+ if (last_count > std::numeric_limits<std::uint64_t>::max() - last_offset) {
2506
+ throw std::overflow_error("total assignment count overflow");
2507
+ }
2508
+ const std::uint64_t total = last_offset + last_count;
2509
+ result.assignment_count = total;
2510
+ for (std::size_t i = 0; i < oversized_flags.size(); ++i) {
2511
+ if (oversized_flags[i]) {
2512
+ const DeviceOversized& item = oversized[i];
2513
+ result.oversized_items.push_back(OversizedItem{
2514
+ OversizedKey{item.layer, item.datatype, item.canonical_id, item.tx0, item.ty0, item.tx1, item.ty1},
2515
+ item.side,
2516
+ });
2517
+ }
2518
+ }
2519
+ if (total == 0) {
2520
+ return result;
2521
+ }
2522
+ result.assignments.resize(total);
2523
+ emit_kernel<<<blocks, threads>>>(
2524
+ d_polygons_ptr,
2525
+ n,
2526
+ tile_size,
2527
+ thrust::raw_pointer_cast(d_offsets.data()),
2528
+ d_oversized_flags_ptr,
2529
+ thrust::raw_pointer_cast(result.assignments.data()),
2530
+ side);
2531
+ check_cuda(cudaGetLastError(), "launch emit kernel");
2532
+ check_cuda(cudaDeviceSynchronize(), "run emit kernel");
2533
+ return result;
2534
+ }
2535
+
2536
+ } // namespace
2537
+
2538
+ extern "C" {
2539
+
2540
+ struct GdsCudaPrefilterResult {
2541
+ std::int64_t* candidate_tiles;
2542
+ std::uint64_t candidate_count;
2543
+ std::int64_t* oversized;
2544
+ std::uint64_t oversized_count;
2545
+ std::uint64_t assignment_count;
2546
+ std::uint64_t collapsed_pair_count;
2547
+ std::uint64_t oversized_matched_count;
2548
+ };
2549
+
2550
+ struct GdsCudaByteCompareResult {
2551
+ std::uint64_t old_size;
2552
+ std::uint64_t new_size;
2553
+ std::uint8_t same;
2554
+ };
2555
+
2556
+ struct GdsCudaPolygonRecordDeltaResult {
2557
+ std::uint64_t record_count;
2558
+ std::int32_t* layers;
2559
+ std::int32_t* datatypes;
2560
+ std::uint8_t* directions;
2561
+ std::uint64_t* counts;
2562
+ std::int64_t* bboxes;
2563
+ std::uint64_t* vertex_counts;
2564
+ std::uint32_t* flags;
2565
+ std::uint64_t* hashes;
2566
+ std::uint64_t* representative_indices;
2567
+ std::uint64_t old_input_count;
2568
+ std::uint64_t new_input_count;
2569
+ std::uint64_t matched_record_count;
2570
+ std::uint64_t reduced_key_count;
2571
+ std::uint64_t cuda_kernel_count;
2572
+ float cuda_event_elapsed_ms;
2573
+ };
2574
+
2575
+ struct GdsCudaBoundaryLoopResult {
2576
+ std::uint64_t fragment_count;
2577
+ std::uint64_t vertex_count;
2578
+ std::int64_t* vertices_xy;
2579
+ std::uint64_t* fragment_offsets;
2580
+ std::int64_t* bboxes;
2581
+ std::int64_t* twice_areas;
2582
+ std::uint64_t dropped_zero_area_loop_count;
2583
+ std::uint64_t cuda_kernel_count;
2584
+ float cuda_event_elapsed_ms;
2585
+ };
2586
+
2587
+ struct GdsCudaBoundaryEdgesResult {
2588
+ std::uint64_t edge_count;
2589
+ std::int64_t* edges;
2590
+ std::uint64_t cuda_kernel_count;
2591
+ float cuda_event_elapsed_ms;
2592
+ };
2593
+
2594
+ struct GdsCudaContextOverlapPairsResult {
2595
+ std::uint64_t pair_count;
2596
+ std::uint64_t* component_indices;
2597
+ std::uint64_t* polygon_indices;
2598
+ std::uint64_t cuda_kernel_count;
2599
+ float cuda_event_elapsed_ms;
2600
+ };
2601
+
2602
+ const char* gds_cuda_last_error() {
2603
+ return g_last_error.c_str();
2604
+ }
2605
+
2606
+ int gds_cuda_release_device_memory() {
2607
+ const auto sync_status = cudaDeviceSynchronize();
2608
+ if (sync_status != cudaSuccess && sync_status != cudaErrorCudartUnloading) {
2609
+ g_last_error = std::string("cudaDeviceSynchronize before release: ") + cudaGetErrorString(sync_status);
2610
+ return 1;
2611
+ }
2612
+ #if CUDART_VERSION >= 11020
2613
+ cudaMemPool_t pool = nullptr;
2614
+ const auto pool_status = cudaDeviceGetDefaultMemPool(&pool, 0);
2615
+ if (pool_status == cudaSuccess && pool) {
2616
+ const auto trim_status = cudaMemPoolTrimTo(pool, 0);
2617
+ if (trim_status != cudaSuccess && trim_status != cudaErrorCudartUnloading) {
2618
+ g_last_error = std::string("cudaMemPoolTrimTo: ") + cudaGetErrorString(trim_status);
2619
+ return 1;
2620
+ }
2621
+ } else if (pool_status != cudaErrorNotSupported && pool_status != cudaErrorCudartUnloading) {
2622
+ g_last_error = std::string("cudaDeviceGetDefaultMemPool: ") + cudaGetErrorString(pool_status);
2623
+ return 1;
2624
+ }
2625
+ #endif
2626
+ g_last_error.clear();
2627
+ return 0;
2628
+ }
2629
+
2630
+ void gds_cuda_free_result(GdsCudaPrefilterResult* result) {
2631
+ if (!result) {
2632
+ return;
2633
+ }
2634
+ std::free(result->candidate_tiles);
2635
+ std::free(result->oversized);
2636
+ result->candidate_tiles = nullptr;
2637
+ result->oversized = nullptr;
2638
+ result->candidate_count = 0;
2639
+ result->oversized_count = 0;
2640
+ }
2641
+
2642
+ void gds_cuda_free_polygon_record_delta_result(GdsCudaPolygonRecordDeltaResult* result) {
2643
+ if (!result) {
2644
+ return;
2645
+ }
2646
+ std::free(result->layers);
2647
+ std::free(result->datatypes);
2648
+ std::free(result->directions);
2649
+ std::free(result->counts);
2650
+ std::free(result->bboxes);
2651
+ std::free(result->vertex_counts);
2652
+ std::free(result->flags);
2653
+ std::free(result->hashes);
2654
+ std::free(result->representative_indices);
2655
+ *result = GdsCudaPolygonRecordDeltaResult{};
2656
+ }
2657
+
2658
+ void gds_cuda_free_boundary_loop_result(GdsCudaBoundaryLoopResult* result) {
2659
+ if (!result) {
2660
+ return;
2661
+ }
2662
+ std::free(result->vertices_xy);
2663
+ std::free(result->fragment_offsets);
2664
+ std::free(result->bboxes);
2665
+ std::free(result->twice_areas);
2666
+ *result = GdsCudaBoundaryLoopResult{};
2667
+ }
2668
+
2669
+ void gds_cuda_free_boundary_edges_result(GdsCudaBoundaryEdgesResult* result) {
2670
+ if (!result) {
2671
+ return;
2672
+ }
2673
+ std::free(result->edges);
2674
+ *result = GdsCudaBoundaryEdgesResult{};
2675
+ }
2676
+
2677
+ void gds_cuda_free_context_overlap_pairs_result(GdsCudaContextOverlapPairsResult* result) {
2678
+ if (!result) {
2679
+ return;
2680
+ }
2681
+ std::free(result->component_indices);
2682
+ std::free(result->polygon_indices);
2683
+ *result = GdsCudaContextOverlapPairsResult{};
2684
+ }
2685
+
2686
+ int gds_cuda_compare_files(const char* old_path, const char* new_path, GdsCudaByteCompareResult* result) {
2687
+ try {
2688
+ if (!old_path || !new_path || !result) {
2689
+ throw std::invalid_argument("file compare received null pointer");
2690
+ }
2691
+ *result = GdsCudaByteCompareResult{};
2692
+ auto old_data = read_binary_file(old_path);
2693
+ auto new_data = read_binary_file(new_path);
2694
+ result->old_size = old_data.size();
2695
+ result->new_size = new_data.size();
2696
+ if (old_data.size() != new_data.size()) {
2697
+ result->same = 0;
2698
+ g_last_error.clear();
2699
+ return 0;
2700
+ }
2701
+ if (old_data.empty()) {
2702
+ result->same = 1;
2703
+ g_last_error.clear();
2704
+ return 0;
2705
+ }
2706
+ DeviceBuffer<std::uint8_t> d_old(old_data.size());
2707
+ DeviceBuffer<std::uint8_t> d_new(new_data.size());
2708
+ DeviceBuffer<std::uint32_t> d_mismatch(1);
2709
+ check_cuda(cudaMemcpy(d_old.get(), old_data.data(), old_data.size(), cudaMemcpyHostToDevice), "copy old file bytes");
2710
+ check_cuda(cudaMemcpy(d_new.get(), new_data.data(), new_data.size(), cudaMemcpyHostToDevice), "copy new file bytes");
2711
+ check_cuda(cudaMemset(d_mismatch.get(), 0, sizeof(std::uint32_t)), "clear byte compare mismatch");
2712
+ const int threads = 256;
2713
+ const int blocks = static_cast<int>((old_data.size() + threads - 1) / threads);
2714
+ byte_compare_kernel<<<blocks, threads>>>(d_old.get(), d_new.get(), old_data.size(), d_mismatch.get());
2715
+ check_cuda(cudaGetLastError(), "launch byte compare kernel");
2716
+ check_cuda(cudaDeviceSynchronize(), "run byte compare kernel");
2717
+ std::uint32_t mismatch = 0;
2718
+ check_cuda(cudaMemcpy(&mismatch, d_mismatch.get(), sizeof(std::uint32_t), cudaMemcpyDeviceToHost), "copy byte compare mismatch");
2719
+ result->same = mismatch ? 0 : 1;
2720
+ g_last_error.clear();
2721
+ return 0;
2722
+ } catch (const std::exception& exc) {
2723
+ g_last_error = exc.what();
2724
+ return 1;
2725
+ } catch (...) {
2726
+ g_last_error = "unknown CUDA byte compare error";
2727
+ return 1;
2728
+ }
2729
+ }
2730
+
2731
+ int gds_cuda_polygon_record_delta(
2732
+ const std::int64_t* old_vertices_xy,
2733
+ std::uint64_t old_vertex_count,
2734
+ const std::uint64_t* old_offsets,
2735
+ const std::int32_t* old_layers,
2736
+ const std::int32_t* old_datatypes,
2737
+ const std::int64_t* old_bboxes,
2738
+ const std::uint32_t* old_flags,
2739
+ std::uint64_t old_count,
2740
+ const std::int64_t* new_vertices_xy,
2741
+ std::uint64_t new_vertex_count,
2742
+ const std::uint64_t* new_offsets,
2743
+ const std::int32_t* new_layers,
2744
+ const std::int32_t* new_datatypes,
2745
+ const std::int64_t* new_bboxes,
2746
+ const std::uint32_t* new_flags,
2747
+ std::uint64_t new_count,
2748
+ GdsCudaPolygonRecordDeltaResult* result) {
2749
+ cudaEvent_t event_start = nullptr;
2750
+ cudaEvent_t event_stop = nullptr;
2751
+ try {
2752
+ if (!result) {
2753
+ throw std::invalid_argument("polygon record delta result pointer is null");
2754
+ }
2755
+ *result = GdsCudaPolygonRecordDeltaResult{};
2756
+ result->old_input_count = old_count;
2757
+ result->new_input_count = new_count;
2758
+ const std::uint64_t total_count = old_count + new_count;
2759
+ if (total_count == 0) {
2760
+ g_last_error.clear();
2761
+ return 0;
2762
+ }
2763
+ if (total_count < old_count) {
2764
+ throw std::overflow_error("polygon record count overflow");
2765
+ }
2766
+ if (old_count && (!old_offsets || !old_layers || !old_datatypes || !old_bboxes || !old_flags || (old_vertex_count && !old_vertices_xy))) {
2767
+ throw std::invalid_argument("old polygon record delta input contains null pointer");
2768
+ }
2769
+ if (new_count && (!new_offsets || !new_layers || !new_datatypes || !new_bboxes || !new_flags || (new_vertex_count && !new_vertices_xy))) {
2770
+ throw std::invalid_argument("new polygon record delta input contains null pointer");
2771
+ }
2772
+
2773
+ check_cuda(cudaEventCreate(&event_start), "create polygon delta start event");
2774
+ check_cuda(cudaEventCreate(&event_stop), "create polygon delta stop event");
2775
+ check_cuda(cudaEventRecord(event_start), "record polygon delta start event");
2776
+
2777
+ DeviceBuffer<std::int64_t> d_old_vertices(old_vertex_count * 2);
2778
+ DeviceBuffer<std::uint64_t> d_old_offsets(old_count ? old_count + 1 : 0);
2779
+ DeviceBuffer<std::int32_t> d_old_layers(old_count);
2780
+ DeviceBuffer<std::int32_t> d_old_datatypes(old_count);
2781
+ DeviceBuffer<std::int64_t> d_old_bboxes(old_count * 4);
2782
+ DeviceBuffer<std::uint32_t> d_old_flags(old_count);
2783
+ DeviceBuffer<std::int64_t> d_new_vertices(new_vertex_count * 2);
2784
+ DeviceBuffer<std::uint64_t> d_new_offsets(new_count ? new_count + 1 : 0);
2785
+ DeviceBuffer<std::int32_t> d_new_layers(new_count);
2786
+ DeviceBuffer<std::int32_t> d_new_datatypes(new_count);
2787
+ DeviceBuffer<std::int64_t> d_new_bboxes(new_count * 4);
2788
+ DeviceBuffer<std::uint32_t> d_new_flags(new_count);
2789
+
2790
+ if (old_count) {
2791
+ if (old_vertex_count) {
2792
+ check_cuda(cudaMemcpy(d_old_vertices.get(), old_vertices_xy, sizeof(std::int64_t) * old_vertex_count * 2, cudaMemcpyHostToDevice), "copy old polygon vertices");
2793
+ }
2794
+ check_cuda(cudaMemcpy(d_old_offsets.get(), old_offsets, sizeof(std::uint64_t) * (old_count + 1), cudaMemcpyHostToDevice), "copy old polygon offsets");
2795
+ check_cuda(cudaMemcpy(d_old_layers.get(), old_layers, sizeof(std::int32_t) * old_count, cudaMemcpyHostToDevice), "copy old polygon layers");
2796
+ check_cuda(cudaMemcpy(d_old_datatypes.get(), old_datatypes, sizeof(std::int32_t) * old_count, cudaMemcpyHostToDevice), "copy old polygon datatypes");
2797
+ check_cuda(cudaMemcpy(d_old_bboxes.get(), old_bboxes, sizeof(std::int64_t) * old_count * 4, cudaMemcpyHostToDevice), "copy old polygon bboxes");
2798
+ check_cuda(cudaMemcpy(d_old_flags.get(), old_flags, sizeof(std::uint32_t) * old_count, cudaMemcpyHostToDevice), "copy old polygon flags");
2799
+ }
2800
+ if (new_count) {
2801
+ if (new_vertex_count) {
2802
+ check_cuda(cudaMemcpy(d_new_vertices.get(), new_vertices_xy, sizeof(std::int64_t) * new_vertex_count * 2, cudaMemcpyHostToDevice), "copy new polygon vertices");
2803
+ }
2804
+ check_cuda(cudaMemcpy(d_new_offsets.get(), new_offsets, sizeof(std::uint64_t) * (new_count + 1), cudaMemcpyHostToDevice), "copy new polygon offsets");
2805
+ check_cuda(cudaMemcpy(d_new_layers.get(), new_layers, sizeof(std::int32_t) * new_count, cudaMemcpyHostToDevice), "copy new polygon layers");
2806
+ check_cuda(cudaMemcpy(d_new_datatypes.get(), new_datatypes, sizeof(std::int32_t) * new_count, cudaMemcpyHostToDevice), "copy new polygon datatypes");
2807
+ check_cuda(cudaMemcpy(d_new_bboxes.get(), new_bboxes, sizeof(std::int64_t) * new_count * 4, cudaMemcpyHostToDevice), "copy new polygon bboxes");
2808
+ check_cuda(cudaMemcpy(d_new_flags.get(), new_flags, sizeof(std::uint32_t) * new_count, cudaMemcpyHostToDevice), "copy new polygon flags");
2809
+ }
2810
+
2811
+ thrust::device_vector<PolygonSideRecord> records(total_count);
2812
+ const int threads = 256;
2813
+ if (old_count) {
2814
+ const int blocks = static_cast<int>((old_count + threads - 1) / threads);
2815
+ polygon_record_key_kernel<<<blocks, threads>>>(
2816
+ d_old_vertices.get(),
2817
+ d_old_offsets.get(),
2818
+ d_old_layers.get(),
2819
+ d_old_datatypes.get(),
2820
+ d_old_bboxes.get(),
2821
+ d_old_flags.get(),
2822
+ old_count,
2823
+ kOldSide,
2824
+ 0,
2825
+ thrust::raw_pointer_cast(records.data()));
2826
+ check_cuda(cudaGetLastError(), "launch old polygon record key kernel");
2827
+ }
2828
+ if (new_count) {
2829
+ const int blocks = static_cast<int>((new_count + threads - 1) / threads);
2830
+ polygon_record_key_kernel<<<blocks, threads>>>(
2831
+ d_new_vertices.get(),
2832
+ d_new_offsets.get(),
2833
+ d_new_layers.get(),
2834
+ d_new_datatypes.get(),
2835
+ d_new_bboxes.get(),
2836
+ d_new_flags.get(),
2837
+ new_count,
2838
+ kNewSide,
2839
+ old_count,
2840
+ thrust::raw_pointer_cast(records.data()));
2841
+ check_cuda(cudaGetLastError(), "launch new polygon record key kernel");
2842
+ }
2843
+ check_cuda(cudaDeviceSynchronize(), "run polygon record key kernels");
2844
+ result->cuda_kernel_count = (old_count ? 1 : 0) + (new_count ? 1 : 0);
2845
+
2846
+ thrust::sort(records.begin(), records.end(), PolygonSideRecordLess{});
2847
+ thrust::device_vector<PolygonRecordKey> keys(total_count);
2848
+ thrust::device_vector<PolygonCountPair> pairs(total_count);
2849
+ thrust::transform(records.begin(), records.end(), keys.begin(), PolygonRecordToKey{});
2850
+ thrust::transform(records.begin(), records.end(), pairs.begin(), PolygonRecordToCountPair{});
2851
+ thrust::device_vector<PolygonRecordKey> reduced_keys(total_count);
2852
+ thrust::device_vector<PolygonCountPair> reduced_pairs(total_count);
2853
+ auto ends = thrust::reduce_by_key(
2854
+ keys.begin(),
2855
+ keys.end(),
2856
+ pairs.begin(),
2857
+ reduced_keys.begin(),
2858
+ reduced_pairs.begin(),
2859
+ PolygonKeyEqual{},
2860
+ PolygonCountPairAdd{});
2861
+ const auto reduced_count = static_cast<std::size_t>(ends.first - reduced_keys.begin());
2862
+ reduced_keys.resize(reduced_count);
2863
+ reduced_pairs.resize(reduced_count);
2864
+ result->reduced_key_count = reduced_count;
2865
+
2866
+ check_cuda(cudaEventRecord(event_stop), "record polygon delta stop event");
2867
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize polygon delta stop event");
2868
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure polygon delta elapsed time");
2869
+
2870
+ thrust::host_vector<PolygonRecordKey> host_keys = reduced_keys;
2871
+ thrust::host_vector<PolygonCountPair> host_pairs = reduced_pairs;
2872
+ struct HostDelta {
2873
+ PolygonRecordKey key;
2874
+ std::uint8_t direction;
2875
+ std::uint64_t count;
2876
+ std::uint64_t representative_index;
2877
+ };
2878
+ std::vector<HostDelta> deltas;
2879
+ for (std::size_t i = 0; i < host_keys.size(); ++i) {
2880
+ const auto old_side_count = host_pairs[i].old_count;
2881
+ const auto new_side_count = host_pairs[i].new_count;
2882
+ result->matched_record_count += std::min(old_side_count, new_side_count);
2883
+ if (old_side_count > new_side_count) {
2884
+ deltas.push_back(HostDelta{host_keys[i], kOldSide, old_side_count - new_side_count, host_pairs[i].old_index});
2885
+ } else if (new_side_count > old_side_count) {
2886
+ deltas.push_back(HostDelta{host_keys[i], kNewSide, new_side_count - old_side_count, host_pairs[i].new_index});
2887
+ }
2888
+ }
2889
+ result->record_count = deltas.size();
2890
+ if (!deltas.empty()) {
2891
+ result->layers = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * deltas.size()));
2892
+ result->datatypes = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * deltas.size()));
2893
+ result->directions = static_cast<std::uint8_t*>(std::malloc(sizeof(std::uint8_t) * deltas.size()));
2894
+ result->counts = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size()));
2895
+ result->bboxes = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * deltas.size() * 4));
2896
+ result->vertex_counts = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size()));
2897
+ result->flags = static_cast<std::uint32_t*>(std::malloc(sizeof(std::uint32_t) * deltas.size()));
2898
+ result->hashes = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size() * 2));
2899
+ result->representative_indices = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size()));
2900
+ if (!result->layers || !result->datatypes || !result->directions || !result->counts || !result->bboxes
2901
+ || !result->vertex_counts || !result->flags || !result->hashes || !result->representative_indices) {
2902
+ throw std::bad_alloc();
2903
+ }
2904
+ for (std::size_t i = 0; i < deltas.size(); ++i) {
2905
+ const auto& delta = deltas[i];
2906
+ result->layers[i] = delta.key.layer;
2907
+ result->datatypes[i] = delta.key.datatype;
2908
+ result->directions[i] = delta.direction;
2909
+ result->counts[i] = delta.count;
2910
+ result->bboxes[i * 4 + 0] = delta.key.x0;
2911
+ result->bboxes[i * 4 + 1] = delta.key.y0;
2912
+ result->bboxes[i * 4 + 2] = delta.key.x1;
2913
+ result->bboxes[i * 4 + 3] = delta.key.y1;
2914
+ result->vertex_counts[i] = delta.key.vertex_count;
2915
+ result->flags[i] = delta.key.flags;
2916
+ result->hashes[i * 2 + 0] = delta.key.hash0;
2917
+ result->hashes[i * 2 + 1] = delta.key.hash1;
2918
+ result->representative_indices[i] = delta.representative_index;
2919
+ }
2920
+ }
2921
+ cudaEventDestroy(event_start);
2922
+ cudaEventDestroy(event_stop);
2923
+ g_last_error.clear();
2924
+ return 0;
2925
+ } catch (const std::exception& exc) {
2926
+ if (event_start) {
2927
+ cudaEventDestroy(event_start);
2928
+ }
2929
+ if (event_stop) {
2930
+ cudaEventDestroy(event_stop);
2931
+ }
2932
+ g_last_error = exc.what();
2933
+ if (result) {
2934
+ gds_cuda_free_polygon_record_delta_result(result);
2935
+ }
2936
+ return 1;
2937
+ } catch (...) {
2938
+ if (event_start) {
2939
+ cudaEventDestroy(event_start);
2940
+ }
2941
+ if (event_stop) {
2942
+ cudaEventDestroy(event_stop);
2943
+ }
2944
+ g_last_error = "unknown CUDA polygon record delta error";
2945
+ if (result) {
2946
+ gds_cuda_free_polygon_record_delta_result(result);
2947
+ }
2948
+ return 1;
2949
+ }
2950
+ }
2951
+
2952
+ int gds_cuda_polygon_record_delta_from_hashes(
2953
+ const std::uint64_t* old_offsets,
2954
+ const std::int32_t* old_layers,
2955
+ const std::int32_t* old_datatypes,
2956
+ const std::int64_t* old_bboxes,
2957
+ const std::uint32_t* old_flags,
2958
+ const std::uint64_t* old_hashes,
2959
+ std::uint64_t old_count,
2960
+ const std::uint64_t* new_offsets,
2961
+ const std::int32_t* new_layers,
2962
+ const std::int32_t* new_datatypes,
2963
+ const std::int64_t* new_bboxes,
2964
+ const std::uint32_t* new_flags,
2965
+ const std::uint64_t* new_hashes,
2966
+ std::uint64_t new_count,
2967
+ GdsCudaPolygonRecordDeltaResult* result) {
2968
+ cudaEvent_t event_start = nullptr;
2969
+ cudaEvent_t event_stop = nullptr;
2970
+ try {
2971
+ if (!result) {
2972
+ throw std::invalid_argument("polygon record delta result pointer is null");
2973
+ }
2974
+ *result = GdsCudaPolygonRecordDeltaResult{};
2975
+ result->old_input_count = old_count;
2976
+ result->new_input_count = new_count;
2977
+ const std::uint64_t total_count = old_count + new_count;
2978
+ if (total_count == 0) {
2979
+ g_last_error.clear();
2980
+ return 0;
2981
+ }
2982
+ if (total_count < old_count) {
2983
+ throw std::overflow_error("polygon record count overflow");
2984
+ }
2985
+ if (old_count && (!old_offsets || !old_layers || !old_datatypes || !old_bboxes || !old_flags || !old_hashes)) {
2986
+ throw std::invalid_argument("old polygon hash delta input contains null pointer");
2987
+ }
2988
+ if (new_count && (!new_offsets || !new_layers || !new_datatypes || !new_bboxes || !new_flags || !new_hashes)) {
2989
+ throw std::invalid_argument("new polygon hash delta input contains null pointer");
2990
+ }
2991
+
2992
+ check_cuda(cudaEventCreate(&event_start), "create polygon hash delta start event");
2993
+ check_cuda(cudaEventCreate(&event_stop), "create polygon hash delta stop event");
2994
+ check_cuda(cudaEventRecord(event_start), "record polygon hash delta start event");
2995
+
2996
+ DeviceBuffer<std::uint64_t> d_old_offsets(old_count ? old_count + 1 : 0);
2997
+ DeviceBuffer<std::int32_t> d_old_layers(old_count);
2998
+ DeviceBuffer<std::int32_t> d_old_datatypes(old_count);
2999
+ DeviceBuffer<std::int64_t> d_old_bboxes(old_count * 4);
3000
+ DeviceBuffer<std::uint32_t> d_old_flags(old_count);
3001
+ DeviceBuffer<std::uint64_t> d_old_hashes(old_count * 2);
3002
+ DeviceBuffer<std::uint64_t> d_new_offsets(new_count ? new_count + 1 : 0);
3003
+ DeviceBuffer<std::int32_t> d_new_layers(new_count);
3004
+ DeviceBuffer<std::int32_t> d_new_datatypes(new_count);
3005
+ DeviceBuffer<std::int64_t> d_new_bboxes(new_count * 4);
3006
+ DeviceBuffer<std::uint32_t> d_new_flags(new_count);
3007
+ DeviceBuffer<std::uint64_t> d_new_hashes(new_count * 2);
3008
+
3009
+ if (old_count) {
3010
+ check_cuda(cudaMemcpy(d_old_offsets.get(), old_offsets, sizeof(std::uint64_t) * (old_count + 1), cudaMemcpyHostToDevice), "copy old polygon offsets");
3011
+ check_cuda(cudaMemcpy(d_old_layers.get(), old_layers, sizeof(std::int32_t) * old_count, cudaMemcpyHostToDevice), "copy old polygon layers");
3012
+ check_cuda(cudaMemcpy(d_old_datatypes.get(), old_datatypes, sizeof(std::int32_t) * old_count, cudaMemcpyHostToDevice), "copy old polygon datatypes");
3013
+ check_cuda(cudaMemcpy(d_old_bboxes.get(), old_bboxes, sizeof(std::int64_t) * old_count * 4, cudaMemcpyHostToDevice), "copy old polygon bboxes");
3014
+ check_cuda(cudaMemcpy(d_old_flags.get(), old_flags, sizeof(std::uint32_t) * old_count, cudaMemcpyHostToDevice), "copy old polygon flags");
3015
+ check_cuda(cudaMemcpy(d_old_hashes.get(), old_hashes, sizeof(std::uint64_t) * old_count * 2, cudaMemcpyHostToDevice), "copy old polygon hashes");
3016
+ }
3017
+ if (new_count) {
3018
+ check_cuda(cudaMemcpy(d_new_offsets.get(), new_offsets, sizeof(std::uint64_t) * (new_count + 1), cudaMemcpyHostToDevice), "copy new polygon offsets");
3019
+ check_cuda(cudaMemcpy(d_new_layers.get(), new_layers, sizeof(std::int32_t) * new_count, cudaMemcpyHostToDevice), "copy new polygon layers");
3020
+ check_cuda(cudaMemcpy(d_new_datatypes.get(), new_datatypes, sizeof(std::int32_t) * new_count, cudaMemcpyHostToDevice), "copy new polygon datatypes");
3021
+ check_cuda(cudaMemcpy(d_new_bboxes.get(), new_bboxes, sizeof(std::int64_t) * new_count * 4, cudaMemcpyHostToDevice), "copy new polygon bboxes");
3022
+ check_cuda(cudaMemcpy(d_new_flags.get(), new_flags, sizeof(std::uint32_t) * new_count, cudaMemcpyHostToDevice), "copy new polygon flags");
3023
+ check_cuda(cudaMemcpy(d_new_hashes.get(), new_hashes, sizeof(std::uint64_t) * new_count * 2, cudaMemcpyHostToDevice), "copy new polygon hashes");
3024
+ }
3025
+
3026
+ thrust::device_vector<PolygonSideRecord> records(total_count);
3027
+ const int threads = 256;
3028
+ if (old_count) {
3029
+ const int blocks = static_cast<int>((old_count + threads - 1) / threads);
3030
+ polygon_record_key_from_hash_kernel<<<blocks, threads>>>(
3031
+ d_old_offsets.get(),
3032
+ d_old_layers.get(),
3033
+ d_old_datatypes.get(),
3034
+ d_old_bboxes.get(),
3035
+ d_old_flags.get(),
3036
+ d_old_hashes.get(),
3037
+ old_count,
3038
+ kOldSide,
3039
+ 0,
3040
+ thrust::raw_pointer_cast(records.data()));
3041
+ check_cuda(cudaGetLastError(), "launch old polygon hash record key kernel");
3042
+ }
3043
+ if (new_count) {
3044
+ const int blocks = static_cast<int>((new_count + threads - 1) / threads);
3045
+ polygon_record_key_from_hash_kernel<<<blocks, threads>>>(
3046
+ d_new_offsets.get(),
3047
+ d_new_layers.get(),
3048
+ d_new_datatypes.get(),
3049
+ d_new_bboxes.get(),
3050
+ d_new_flags.get(),
3051
+ d_new_hashes.get(),
3052
+ new_count,
3053
+ kNewSide,
3054
+ old_count,
3055
+ thrust::raw_pointer_cast(records.data()));
3056
+ check_cuda(cudaGetLastError(), "launch new polygon hash record key kernel");
3057
+ }
3058
+ check_cuda(cudaDeviceSynchronize(), "run polygon hash record key kernels");
3059
+ result->cuda_kernel_count = (old_count ? 1 : 0) + (new_count ? 1 : 0);
3060
+
3061
+ thrust::sort(records.begin(), records.end(), PolygonSideRecordLess{});
3062
+ thrust::device_vector<PolygonRecordKey> keys(total_count);
3063
+ thrust::device_vector<PolygonCountPair> pairs(total_count);
3064
+ thrust::transform(records.begin(), records.end(), keys.begin(), PolygonRecordToKey{});
3065
+ thrust::transform(records.begin(), records.end(), pairs.begin(), PolygonRecordToCountPair{});
3066
+ thrust::device_vector<PolygonRecordKey> reduced_keys(total_count);
3067
+ thrust::device_vector<PolygonCountPair> reduced_pairs(total_count);
3068
+ auto ends = thrust::reduce_by_key(
3069
+ keys.begin(),
3070
+ keys.end(),
3071
+ pairs.begin(),
3072
+ reduced_keys.begin(),
3073
+ reduced_pairs.begin(),
3074
+ PolygonKeyEqual{},
3075
+ PolygonCountPairAdd{});
3076
+ const auto reduced_count = static_cast<std::size_t>(ends.first - reduced_keys.begin());
3077
+ reduced_keys.resize(reduced_count);
3078
+ reduced_pairs.resize(reduced_count);
3079
+ result->reduced_key_count = reduced_count;
3080
+
3081
+ check_cuda(cudaEventRecord(event_stop), "record polygon hash delta stop event");
3082
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize polygon hash delta stop event");
3083
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure polygon hash delta elapsed time");
3084
+
3085
+ thrust::host_vector<PolygonRecordKey> host_keys = reduced_keys;
3086
+ thrust::host_vector<PolygonCountPair> host_pairs = reduced_pairs;
3087
+ struct HostDelta {
3088
+ PolygonRecordKey key;
3089
+ std::uint8_t direction;
3090
+ std::uint64_t count;
3091
+ std::uint64_t representative_index;
3092
+ };
3093
+ std::vector<HostDelta> deltas;
3094
+ for (std::size_t i = 0; i < host_keys.size(); ++i) {
3095
+ const auto old_side_count = host_pairs[i].old_count;
3096
+ const auto new_side_count = host_pairs[i].new_count;
3097
+ result->matched_record_count += std::min(old_side_count, new_side_count);
3098
+ if (old_side_count > new_side_count) {
3099
+ deltas.push_back(HostDelta{host_keys[i], kOldSide, old_side_count - new_side_count, host_pairs[i].old_index});
3100
+ } else if (new_side_count > old_side_count) {
3101
+ deltas.push_back(HostDelta{host_keys[i], kNewSide, new_side_count - old_side_count, host_pairs[i].new_index});
3102
+ }
3103
+ }
3104
+ result->record_count = deltas.size();
3105
+ if (!deltas.empty()) {
3106
+ result->layers = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * deltas.size()));
3107
+ result->datatypes = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * deltas.size()));
3108
+ result->directions = static_cast<std::uint8_t*>(std::malloc(sizeof(std::uint8_t) * deltas.size()));
3109
+ result->counts = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size()));
3110
+ result->bboxes = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * deltas.size() * 4));
3111
+ result->vertex_counts = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size()));
3112
+ result->flags = static_cast<std::uint32_t*>(std::malloc(sizeof(std::uint32_t) * deltas.size()));
3113
+ result->hashes = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size() * 2));
3114
+ result->representative_indices = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * deltas.size()));
3115
+ if (!result->layers || !result->datatypes || !result->directions || !result->counts || !result->bboxes
3116
+ || !result->vertex_counts || !result->flags || !result->hashes || !result->representative_indices) {
3117
+ throw std::bad_alloc();
3118
+ }
3119
+ for (std::size_t i = 0; i < deltas.size(); ++i) {
3120
+ const auto& delta = deltas[i];
3121
+ result->layers[i] = delta.key.layer;
3122
+ result->datatypes[i] = delta.key.datatype;
3123
+ result->directions[i] = delta.direction;
3124
+ result->counts[i] = delta.count;
3125
+ result->bboxes[i * 4 + 0] = delta.key.x0;
3126
+ result->bboxes[i * 4 + 1] = delta.key.y0;
3127
+ result->bboxes[i * 4 + 2] = delta.key.x1;
3128
+ result->bboxes[i * 4 + 3] = delta.key.y1;
3129
+ result->vertex_counts[i] = delta.key.vertex_count;
3130
+ result->flags[i] = delta.key.flags;
3131
+ result->hashes[i * 2 + 0] = delta.key.hash0;
3132
+ result->hashes[i * 2 + 1] = delta.key.hash1;
3133
+ result->representative_indices[i] = delta.representative_index;
3134
+ }
3135
+ }
3136
+ cudaEventDestroy(event_start);
3137
+ cudaEventDestroy(event_stop);
3138
+ g_last_error.clear();
3139
+ return 0;
3140
+ } catch (const std::exception& exc) {
3141
+ if (event_start) {
3142
+ cudaEventDestroy(event_start);
3143
+ }
3144
+ if (event_stop) {
3145
+ cudaEventDestroy(event_stop);
3146
+ }
3147
+ g_last_error = exc.what();
3148
+ if (result) {
3149
+ gds_cuda_free_polygon_record_delta_result(result);
3150
+ }
3151
+ return 1;
3152
+ } catch (...) {
3153
+ if (event_start) {
3154
+ cudaEventDestroy(event_start);
3155
+ }
3156
+ if (event_stop) {
3157
+ cudaEventDestroy(event_stop);
3158
+ }
3159
+ g_last_error = "unknown CUDA polygon hash record delta error";
3160
+ if (result) {
3161
+ gds_cuda_free_polygon_record_delta_result(result);
3162
+ }
3163
+ return 1;
3164
+ }
3165
+ }
3166
+
3167
+ int gds_cuda_prefilter(
3168
+ const std::int64_t* old_bboxes,
3169
+ const std::int32_t* old_layers,
3170
+ const std::int32_t* old_datatypes,
3171
+ const std::uint64_t* old_ids,
3172
+ std::uint64_t old_count,
3173
+ const std::int64_t* new_bboxes,
3174
+ const std::int32_t* new_layers,
3175
+ const std::int32_t* new_datatypes,
3176
+ const std::uint64_t* new_ids,
3177
+ std::uint64_t new_count,
3178
+ std::int64_t tile_size,
3179
+ std::uint64_t max_tiles_per_polygon,
3180
+ GdsCudaPrefilterResult* result) {
3181
+ try {
3182
+ if (!result) {
3183
+ throw std::invalid_argument("result pointer is null");
3184
+ }
3185
+ *result = GdsCudaPrefilterResult{};
3186
+ if (tile_size <= 0 || max_tiles_per_polygon == 0) {
3187
+ throw std::invalid_argument("invalid tile config");
3188
+ }
3189
+ std::vector<OversizedItem> oversized_items;
3190
+ auto old_polygons = pack_polygons(old_bboxes, old_layers, old_datatypes, old_ids, old_count);
3191
+ auto new_polygons = pack_polygons(new_bboxes, new_layers, new_datatypes, new_ids, new_count);
3192
+ auto old_assignments = build_side_assignments(old_polygons, tile_size, max_tiles_per_polygon, kOldSide);
3193
+ auto new_assignments = build_side_assignments(new_polygons, tile_size, max_tiles_per_polygon, kNewSide);
3194
+ result->assignment_count = old_assignments.assignment_count + new_assignments.assignment_count;
3195
+ oversized_items.reserve(old_assignments.oversized_items.size() + new_assignments.oversized_items.size());
3196
+ oversized_items.insert(oversized_items.end(), old_assignments.oversized_items.begin(), old_assignments.oversized_items.end());
3197
+ oversized_items.insert(oversized_items.end(), new_assignments.oversized_items.begin(), new_assignments.oversized_items.end());
3198
+
3199
+ std::vector<CandidateTile> candidate_tiles;
3200
+ if (result->assignment_count != 0) {
3201
+ thrust::device_vector<DeviceAssignment> assignments;
3202
+ assignments.reserve(result->assignment_count);
3203
+ assignments.insert(assignments.end(), old_assignments.assignments.begin(), old_assignments.assignments.end());
3204
+ assignments.insert(assignments.end(), new_assignments.assignments.begin(), new_assignments.assignments.end());
3205
+
3206
+ thrust::device_vector<AssignmentKey> keys(assignments.size());
3207
+ thrust::device_vector<std::uint8_t> sides(assignments.size());
3208
+ thrust::transform(assignments.begin(), assignments.end(), keys.begin(), DeviceAssignmentToKey{});
3209
+ thrust::transform(assignments.begin(), assignments.end(), sides.begin(), DeviceAssignmentToSide{});
3210
+ thrust::sort_by_key(keys.begin(), keys.end(), sides.begin());
3211
+
3212
+ thrust::device_vector<AssignmentKey> reduced_keys(keys.size());
3213
+ thrust::device_vector<std::uint8_t> reduced_sides(sides.size());
3214
+ auto ends = thrust::reduce_by_key(
3215
+ keys.begin(),
3216
+ keys.end(),
3217
+ sides.begin(),
3218
+ reduced_keys.begin(),
3219
+ reduced_sides.begin(),
3220
+ AssignmentKeyEqual{},
3221
+ SideOr{});
3222
+ const auto reduced_count = static_cast<std::size_t>(ends.first - reduced_keys.begin());
3223
+ result->collapsed_pair_count = reduced_count;
3224
+ reduced_keys.resize(reduced_count);
3225
+ reduced_sides.resize(reduced_count);
3226
+
3227
+ thrust::host_vector<AssignmentKey> final_keys = reduced_keys;
3228
+ thrust::host_vector<std::uint8_t> final_sides = reduced_sides;
3229
+ for (std::size_t i = 0; i < reduced_count; ++i) {
3230
+ if (final_sides[i] != kBothSides) {
3231
+ const AssignmentKey key = final_keys[i];
3232
+ candidate_tiles.push_back(CandidateTile{key.layer, key.datatype, key.tx, key.ty});
3233
+ }
3234
+ }
3235
+ }
3236
+ std::sort(candidate_tiles.begin(), candidate_tiles.end());
3237
+ candidate_tiles.erase(std::unique(candidate_tiles.begin(), candidate_tiles.end()), candidate_tiles.end());
3238
+ result->candidate_count = candidate_tiles.size();
3239
+ if (!candidate_tiles.empty()) {
3240
+ result->candidate_tiles = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * candidate_tiles.size() * 4));
3241
+ if (!result->candidate_tiles) {
3242
+ throw std::bad_alloc();
3243
+ }
3244
+ for (std::size_t i = 0; i < candidate_tiles.size(); ++i) {
3245
+ result->candidate_tiles[i * 4 + 0] = candidate_tiles[i].layer;
3246
+ result->candidate_tiles[i * 4 + 1] = candidate_tiles[i].datatype;
3247
+ result->candidate_tiles[i * 4 + 2] = candidate_tiles[i].tx;
3248
+ result->candidate_tiles[i * 4 + 3] = candidate_tiles[i].ty;
3249
+ }
3250
+ }
3251
+
3252
+ std::sort(oversized_items.begin(), oversized_items.end(), [](const OversizedItem& left, const OversizedItem& right) {
3253
+ return left.key < right.key;
3254
+ });
3255
+ std::vector<OversizedItem> oversized_out;
3256
+ for (std::size_t i = 0; i < oversized_items.size();) {
3257
+ const OversizedKey key = oversized_items[i].key;
3258
+ std::uint8_t side = 0;
3259
+ do {
3260
+ side = static_cast<std::uint8_t>(side | oversized_items[i].side);
3261
+ ++i;
3262
+ } while (
3263
+ i < oversized_items.size()
3264
+ && !(key < oversized_items[i].key)
3265
+ && !(oversized_items[i].key < key));
3266
+ if (side == kBothSides) {
3267
+ ++result->oversized_matched_count;
3268
+ } else {
3269
+ oversized_out.push_back(OversizedItem{key, side});
3270
+ }
3271
+ }
3272
+ result->oversized_count = oversized_out.size();
3273
+ if (!oversized_out.empty()) {
3274
+ result->oversized = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * oversized_out.size() * 8));
3275
+ if (!result->oversized) {
3276
+ throw std::bad_alloc();
3277
+ }
3278
+ for (std::size_t i = 0; i < oversized_out.size(); ++i) {
3279
+ const OversizedItem& item = oversized_out[i];
3280
+ result->oversized[i * 8 + 0] = item.key.layer;
3281
+ result->oversized[i * 8 + 1] = item.key.datatype;
3282
+ result->oversized[i * 8 + 2] = static_cast<std::int64_t>(item.key.canonical_id);
3283
+ result->oversized[i * 8 + 3] = item.side;
3284
+ result->oversized[i * 8 + 4] = item.key.tx0;
3285
+ result->oversized[i * 8 + 5] = item.key.ty0;
3286
+ result->oversized[i * 8 + 6] = item.key.tx1;
3287
+ result->oversized[i * 8 + 7] = item.key.ty1;
3288
+ }
3289
+ }
3290
+ g_last_error.clear();
3291
+ return 0;
3292
+ } catch (const std::exception& exc) {
3293
+ g_last_error = exc.what();
3294
+ if (result) {
3295
+ gds_cuda_free_result(result);
3296
+ }
3297
+ return 1;
3298
+ } catch (...) {
3299
+ g_last_error = "unknown CUDA prefilter error";
3300
+ if (result) {
3301
+ gds_cuda_free_result(result);
3302
+ }
3303
+ return 1;
3304
+ }
3305
+ }
3306
+
3307
+ int gds_cuda_rect_exact_areas(
3308
+ const std::int64_t* old_rects,
3309
+ const std::int64_t* new_rects,
3310
+ const std::uint8_t* old_present,
3311
+ const std::uint8_t* new_present,
3312
+ std::uint64_t count,
3313
+ std::int64_t* out_twice_areas) {
3314
+ try {
3315
+ if (!out_twice_areas) {
3316
+ throw std::invalid_argument("rect exact output pointer is null");
3317
+ }
3318
+ if (count == 0) {
3319
+ g_last_error.clear();
3320
+ return 0;
3321
+ }
3322
+ if (!old_rects || !new_rects || !old_present || !new_present) {
3323
+ throw std::invalid_argument("rect exact received null input pointer");
3324
+ }
3325
+ DeviceBuffer<std::int64_t> d_old_rects(count * 4);
3326
+ DeviceBuffer<std::int64_t> d_new_rects(count * 4);
3327
+ DeviceBuffer<std::uint8_t> d_old_present(count);
3328
+ DeviceBuffer<std::uint8_t> d_new_present(count);
3329
+ DeviceBuffer<std::int64_t> d_out(count * 3);
3330
+ check_cuda(cudaMemcpy(d_old_rects.get(), old_rects, sizeof(std::int64_t) * count * 4, cudaMemcpyHostToDevice), "copy old rects");
3331
+ check_cuda(cudaMemcpy(d_new_rects.get(), new_rects, sizeof(std::int64_t) * count * 4, cudaMemcpyHostToDevice), "copy new rects");
3332
+ check_cuda(cudaMemcpy(d_old_present.get(), old_present, sizeof(std::uint8_t) * count, cudaMemcpyHostToDevice), "copy old present");
3333
+ check_cuda(cudaMemcpy(d_new_present.get(), new_present, sizeof(std::uint8_t) * count, cudaMemcpyHostToDevice), "copy new present");
3334
+ const int threads = 256;
3335
+ const int blocks = static_cast<int>((count + threads - 1) / threads);
3336
+ rect_exact_area_kernel<<<blocks, threads>>>(
3337
+ d_old_rects.get(),
3338
+ d_new_rects.get(),
3339
+ d_old_present.get(),
3340
+ d_new_present.get(),
3341
+ count,
3342
+ d_out.get());
3343
+ check_cuda(cudaGetLastError(), "launch rect exact area kernel");
3344
+ check_cuda(cudaDeviceSynchronize(), "run rect exact area kernel");
3345
+ check_cuda(cudaMemcpy(out_twice_areas, d_out.get(), sizeof(std::int64_t) * count * 3, cudaMemcpyDeviceToHost), "copy rect exact areas");
3346
+ g_last_error.clear();
3347
+ return 0;
3348
+ } catch (const std::exception& exc) {
3349
+ g_last_error = exc.what();
3350
+ return 1;
3351
+ } catch (...) {
3352
+ g_last_error = "unknown CUDA rect exact error";
3353
+ return 1;
3354
+ }
3355
+ }
3356
+
3357
+ int gds_cuda_rect_exact_fragments(
3358
+ const std::int64_t* old_rects,
3359
+ const std::int64_t* new_rects,
3360
+ const std::uint8_t* old_present,
3361
+ const std::uint8_t* new_present,
3362
+ std::uint64_t count,
3363
+ std::int64_t* out_rects,
3364
+ std::uint8_t* out_counts,
3365
+ std::int64_t* out_twice_areas) {
3366
+ try {
3367
+ if (!out_rects || !out_counts || !out_twice_areas) {
3368
+ throw std::invalid_argument("rect exact fragment output pointer is null");
3369
+ }
3370
+ if (count == 0) {
3371
+ g_last_error.clear();
3372
+ return 0;
3373
+ }
3374
+ if (!old_rects || !new_rects || !old_present || !new_present) {
3375
+ throw std::invalid_argument("rect exact fragments received null input pointer");
3376
+ }
3377
+ DeviceBuffer<std::int64_t> d_old_rects(count * 4);
3378
+ DeviceBuffer<std::int64_t> d_new_rects(count * 4);
3379
+ DeviceBuffer<std::uint8_t> d_old_present(count);
3380
+ DeviceBuffer<std::uint8_t> d_new_present(count);
3381
+ DeviceBuffer<std::int64_t> d_out_rects(count * 8 * 4);
3382
+ DeviceBuffer<std::uint8_t> d_out_counts(count * 2);
3383
+ DeviceBuffer<std::int64_t> d_out_areas(count * 3);
3384
+ check_cuda(cudaMemcpy(d_old_rects.get(), old_rects, sizeof(std::int64_t) * count * 4, cudaMemcpyHostToDevice), "copy old rects");
3385
+ check_cuda(cudaMemcpy(d_new_rects.get(), new_rects, sizeof(std::int64_t) * count * 4, cudaMemcpyHostToDevice), "copy new rects");
3386
+ check_cuda(cudaMemcpy(d_old_present.get(), old_present, sizeof(std::uint8_t) * count, cudaMemcpyHostToDevice), "copy old present");
3387
+ check_cuda(cudaMemcpy(d_new_present.get(), new_present, sizeof(std::uint8_t) * count, cudaMemcpyHostToDevice), "copy new present");
3388
+ check_cuda(cudaMemset(d_out_rects.get(), 0, sizeof(std::int64_t) * count * 8 * 4), "clear rect fragment output");
3389
+ check_cuda(cudaMemset(d_out_counts.get(), 0, sizeof(std::uint8_t) * count * 2), "clear rect fragment counts");
3390
+ check_cuda(cudaMemset(d_out_areas.get(), 0, sizeof(std::int64_t) * count * 3), "clear rect fragment areas");
3391
+ const int threads = 256;
3392
+ const int blocks = static_cast<int>((count + threads - 1) / threads);
3393
+ rect_exact_fragments_kernel<<<blocks, threads>>>(
3394
+ d_old_rects.get(),
3395
+ d_new_rects.get(),
3396
+ d_old_present.get(),
3397
+ d_new_present.get(),
3398
+ count,
3399
+ d_out_rects.get(),
3400
+ d_out_counts.get(),
3401
+ d_out_areas.get());
3402
+ check_cuda(cudaGetLastError(), "launch rect exact fragment kernel");
3403
+ check_cuda(cudaDeviceSynchronize(), "run rect exact fragment kernel");
3404
+ check_cuda(cudaMemcpy(out_rects, d_out_rects.get(), sizeof(std::int64_t) * count * 8 * 4, cudaMemcpyDeviceToHost), "copy rect exact fragments");
3405
+ check_cuda(cudaMemcpy(out_counts, d_out_counts.get(), sizeof(std::uint8_t) * count * 2, cudaMemcpyDeviceToHost), "copy rect exact fragment counts");
3406
+ check_cuda(cudaMemcpy(out_twice_areas, d_out_areas.get(), sizeof(std::int64_t) * count * 3, cudaMemcpyDeviceToHost), "copy rect exact fragment areas");
3407
+ g_last_error.clear();
3408
+ return 0;
3409
+ } catch (const std::exception& exc) {
3410
+ g_last_error = exc.what();
3411
+ return 1;
3412
+ } catch (...) {
3413
+ g_last_error = "unknown CUDA rect exact fragment error";
3414
+ return 1;
3415
+ }
3416
+ }
3417
+
3418
+ int gds_cuda_polygon_component_areas(
3419
+ const std::int64_t* vertices_xy,
3420
+ std::uint64_t vertex_count,
3421
+ const std::uint64_t* polygon_offsets,
3422
+ const std::uint8_t* polygon_sides,
3423
+ std::uint64_t polygon_count,
3424
+ const std::uint64_t* component_offsets,
3425
+ std::uint64_t component_count,
3426
+ std::int64_t* out_twice_areas) {
3427
+ try {
3428
+ if (!out_twice_areas) {
3429
+ throw std::invalid_argument("polygon component area output pointer is null");
3430
+ }
3431
+ if (component_count == 0) {
3432
+ g_last_error.clear();
3433
+ return 0;
3434
+ }
3435
+ if (!polygon_offsets || !polygon_sides || !component_offsets || (vertex_count && !vertices_xy)) {
3436
+ throw std::invalid_argument("polygon component area received null input pointer");
3437
+ }
3438
+ DeviceBuffer<std::int64_t> d_vertices(vertex_count * 2);
3439
+ DeviceBuffer<std::uint64_t> d_polygon_offsets(polygon_count + 1);
3440
+ DeviceBuffer<std::uint8_t> d_polygon_sides(polygon_count);
3441
+ DeviceBuffer<std::uint64_t> d_component_offsets(component_count + 1);
3442
+ DeviceBuffer<std::int64_t> d_out(component_count * 3);
3443
+ DeviceBuffer<std::uint32_t> d_errors(component_count);
3444
+ if (vertex_count) {
3445
+ check_cuda(cudaMemcpy(d_vertices.get(), vertices_xy, sizeof(std::int64_t) * vertex_count * 2, cudaMemcpyHostToDevice), "copy polygon component vertices");
3446
+ }
3447
+ check_cuda(cudaMemcpy(d_polygon_offsets.get(), polygon_offsets, sizeof(std::uint64_t) * (polygon_count + 1), cudaMemcpyHostToDevice), "copy polygon component offsets");
3448
+ check_cuda(cudaMemcpy(d_polygon_sides.get(), polygon_sides, sizeof(std::uint8_t) * polygon_count, cudaMemcpyHostToDevice), "copy polygon component sides");
3449
+ check_cuda(cudaMemcpy(d_component_offsets.get(), component_offsets, sizeof(std::uint64_t) * (component_count + 1), cudaMemcpyHostToDevice), "copy polygon component offsets");
3450
+ check_cuda(cudaMemset(d_out.get(), 0, sizeof(std::int64_t) * component_count * 3), "clear polygon component area output");
3451
+ check_cuda(cudaMemset(d_errors.get(), 0, sizeof(std::uint32_t) * component_count), "clear polygon component errors");
3452
+ const int threads = 64;
3453
+ const int blocks = static_cast<int>((component_count + threads - 1) / threads);
3454
+ polygon_component_area_kernel<<<blocks, threads>>>(
3455
+ d_vertices.get(),
3456
+ d_polygon_offsets.get(),
3457
+ d_polygon_sides.get(),
3458
+ d_component_offsets.get(),
3459
+ component_count,
3460
+ d_out.get(),
3461
+ d_errors.get());
3462
+ check_cuda(cudaGetLastError(), "launch polygon component area kernel");
3463
+ check_cuda(cudaDeviceSynchronize(), "run polygon component area kernel");
3464
+ const auto max_error = thrust::reduce(
3465
+ thrust::device_pointer_cast(d_errors.get()),
3466
+ thrust::device_pointer_cast(d_errors.get()) + component_count,
3467
+ std::uint32_t{0},
3468
+ thrust::maximum<std::uint32_t>{});
3469
+ if (max_error) {
3470
+ throw std::runtime_error("polygon component area task exceeds supported limits");
3471
+ }
3472
+ check_cuda(cudaMemcpy(out_twice_areas, d_out.get(), sizeof(std::int64_t) * component_count * 3, cudaMemcpyDeviceToHost), "copy polygon component areas");
3473
+ g_last_error.clear();
3474
+ return 0;
3475
+ } catch (const std::exception& exc) {
3476
+ g_last_error = exc.what();
3477
+ return 1;
3478
+ } catch (...) {
3479
+ g_last_error = "unknown CUDA polygon component area error";
3480
+ return 1;
3481
+ }
3482
+ }
3483
+
3484
+ int gds_cuda_polygon_component_fragments(
3485
+ const std::int64_t* vertices_xy,
3486
+ std::uint64_t vertex_count,
3487
+ const std::uint64_t* polygon_offsets,
3488
+ const std::uint8_t* polygon_sides,
3489
+ std::uint64_t polygon_count,
3490
+ const std::uint64_t* component_offsets,
3491
+ std::uint64_t component_count,
3492
+ std::uint16_t max_fragments,
3493
+ std::int64_t* out_quads,
3494
+ std::int64_t* out_fragment_twice_areas,
3495
+ std::uint16_t* out_counts,
3496
+ std::int64_t* out_twice_areas) {
3497
+ try {
3498
+ if (!out_quads || !out_fragment_twice_areas || !out_counts || !out_twice_areas) {
3499
+ throw std::invalid_argument("polygon component fragment output pointer is null");
3500
+ }
3501
+ if (component_count == 0) {
3502
+ g_last_error.clear();
3503
+ return 0;
3504
+ }
3505
+ if (!polygon_offsets || !polygon_sides || !component_offsets || (vertex_count && !vertices_xy)) {
3506
+ throw std::invalid_argument("polygon component fragments received null input pointer");
3507
+ }
3508
+ if (max_fragments == 0) {
3509
+ throw std::invalid_argument("polygon component max_fragments must be positive");
3510
+ }
3511
+ DeviceBuffer<std::int64_t> d_vertices(vertex_count * 2);
3512
+ DeviceBuffer<std::uint64_t> d_polygon_offsets(polygon_count + 1);
3513
+ DeviceBuffer<std::uint8_t> d_polygon_sides(polygon_count);
3514
+ DeviceBuffer<std::uint64_t> d_component_offsets(component_count + 1);
3515
+ DeviceBuffer<std::int64_t> d_out_quads(component_count * 2 * max_fragments * 8);
3516
+ DeviceBuffer<std::int64_t> d_out_fragment_areas(component_count * 2 * max_fragments);
3517
+ DeviceBuffer<std::uint16_t> d_out_counts(component_count * 2);
3518
+ DeviceBuffer<std::int64_t> d_out_areas(component_count * 3);
3519
+ DeviceBuffer<std::uint32_t> d_errors(component_count);
3520
+ if (vertex_count) {
3521
+ check_cuda(cudaMemcpy(d_vertices.get(), vertices_xy, sizeof(std::int64_t) * vertex_count * 2, cudaMemcpyHostToDevice), "copy polygon component fragment vertices");
3522
+ }
3523
+ check_cuda(cudaMemcpy(d_polygon_offsets.get(), polygon_offsets, sizeof(std::uint64_t) * (polygon_count + 1), cudaMemcpyHostToDevice), "copy polygon component fragment offsets");
3524
+ check_cuda(cudaMemcpy(d_polygon_sides.get(), polygon_sides, sizeof(std::uint8_t) * polygon_count, cudaMemcpyHostToDevice), "copy polygon component fragment sides");
3525
+ check_cuda(cudaMemcpy(d_component_offsets.get(), component_offsets, sizeof(std::uint64_t) * (component_count + 1), cudaMemcpyHostToDevice), "copy polygon component fragment component offsets");
3526
+ check_cuda(cudaMemset(d_out_quads.get(), 0, sizeof(std::int64_t) * component_count * 2 * max_fragments * 8), "clear polygon component fragments");
3527
+ check_cuda(cudaMemset(d_out_fragment_areas.get(), 0, sizeof(std::int64_t) * component_count * 2 * max_fragments), "clear polygon component fragment areas");
3528
+ check_cuda(cudaMemset(d_out_counts.get(), 0, sizeof(std::uint16_t) * component_count * 2), "clear polygon component fragment counts");
3529
+ check_cuda(cudaMemset(d_out_areas.get(), 0, sizeof(std::int64_t) * component_count * 3), "clear polygon component fragment areas");
3530
+ check_cuda(cudaMemset(d_errors.get(), 0, sizeof(std::uint32_t) * component_count), "clear polygon component fragment errors");
3531
+ const int threads = 64;
3532
+ const int blocks = static_cast<int>((component_count + threads - 1) / threads);
3533
+ polygon_component_fragment_kernel<<<blocks, threads>>>(
3534
+ d_vertices.get(),
3535
+ d_polygon_offsets.get(),
3536
+ d_polygon_sides.get(),
3537
+ d_component_offsets.get(),
3538
+ component_count,
3539
+ max_fragments,
3540
+ d_out_quads.get(),
3541
+ d_out_fragment_areas.get(),
3542
+ d_out_counts.get(),
3543
+ d_out_areas.get(),
3544
+ d_errors.get());
3545
+ check_cuda(cudaGetLastError(), "launch polygon component fragment kernel");
3546
+ check_cuda(cudaDeviceSynchronize(), "run polygon component fragment kernel");
3547
+ const auto max_error = thrust::reduce(
3548
+ thrust::device_pointer_cast(d_errors.get()),
3549
+ thrust::device_pointer_cast(d_errors.get()) + component_count,
3550
+ std::uint32_t{0},
3551
+ thrust::maximum<std::uint32_t>{});
3552
+ if (max_error) {
3553
+ throw std::runtime_error(max_error == 4 ? "polygon component fragment task exceeds fragment limit" : "polygon component fragment task exceeds supported limits");
3554
+ }
3555
+ check_cuda(cudaMemcpy(out_quads, d_out_quads.get(), sizeof(std::int64_t) * component_count * 2 * max_fragments * 8, cudaMemcpyDeviceToHost), "copy polygon component fragments");
3556
+ check_cuda(cudaMemcpy(out_fragment_twice_areas, d_out_fragment_areas.get(), sizeof(std::int64_t) * component_count * 2 * max_fragments, cudaMemcpyDeviceToHost), "copy polygon component fragment areas");
3557
+ check_cuda(cudaMemcpy(out_counts, d_out_counts.get(), sizeof(std::uint16_t) * component_count * 2, cudaMemcpyDeviceToHost), "copy polygon component fragment counts");
3558
+ check_cuda(cudaMemcpy(out_twice_areas, d_out_areas.get(), sizeof(std::int64_t) * component_count * 3, cudaMemcpyDeviceToHost), "copy polygon component fragment areas");
3559
+ g_last_error.clear();
3560
+ return 0;
3561
+ } catch (const std::exception& exc) {
3562
+ g_last_error = exc.what();
3563
+ return 1;
3564
+ } catch (...) {
3565
+ g_last_error = "unknown CUDA polygon component fragment error";
3566
+ return 1;
3567
+ }
3568
+ }
3569
+
3570
+ int gds_cuda_one_sided_boundary_edges(
3571
+ const std::int64_t* vertices_xy,
3572
+ std::uint64_t vertex_count,
3573
+ const std::uint64_t* polygon_offsets,
3574
+ const std::int64_t* bboxes,
3575
+ const std::int64_t* signed_twice_areas,
3576
+ std::uint64_t polygon_count,
3577
+ const double* segment_points,
3578
+ const std::uint32_t* segment_polygon_indices,
3579
+ std::uint64_t segment_count,
3580
+ double* out_edges,
3581
+ std::uint8_t* out_keep) {
3582
+ try {
3583
+ if (!out_edges || !out_keep) {
3584
+ throw std::invalid_argument("one-sided boundary edge output pointer is null");
3585
+ }
3586
+ if (segment_count == 0) {
3587
+ g_last_error.clear();
3588
+ return 0;
3589
+ }
3590
+ if (!polygon_offsets || !bboxes || !signed_twice_areas || !segment_points || !segment_polygon_indices || (vertex_count && !vertices_xy)) {
3591
+ throw std::invalid_argument("one-sided boundary edge received null input pointer");
3592
+ }
3593
+ DeviceBuffer<std::int64_t> d_vertices(vertex_count * 2);
3594
+ DeviceBuffer<std::uint64_t> d_polygon_offsets(polygon_count + 1);
3595
+ DeviceBuffer<std::int64_t> d_bboxes(polygon_count * 4);
3596
+ DeviceBuffer<std::int64_t> d_signed_twice_areas(polygon_count);
3597
+ DeviceBuffer<double> d_segment_points(segment_count * 4);
3598
+ DeviceBuffer<std::uint32_t> d_segment_polygon_indices(segment_count);
3599
+ DeviceBuffer<double> d_out_edges(segment_count * 4);
3600
+ DeviceBuffer<std::uint8_t> d_out_keep(segment_count);
3601
+ if (vertex_count) {
3602
+ check_cuda(cudaMemcpy(d_vertices.get(), vertices_xy, sizeof(std::int64_t) * vertex_count * 2, cudaMemcpyHostToDevice), "copy one-sided boundary vertices");
3603
+ }
3604
+ check_cuda(cudaMemcpy(d_polygon_offsets.get(), polygon_offsets, sizeof(std::uint64_t) * (polygon_count + 1), cudaMemcpyHostToDevice), "copy one-sided boundary offsets");
3605
+ check_cuda(cudaMemcpy(d_bboxes.get(), bboxes, sizeof(std::int64_t) * polygon_count * 4, cudaMemcpyHostToDevice), "copy one-sided boundary bboxes");
3606
+ check_cuda(cudaMemcpy(d_signed_twice_areas.get(), signed_twice_areas, sizeof(std::int64_t) * polygon_count, cudaMemcpyHostToDevice), "copy one-sided boundary signed areas");
3607
+ check_cuda(cudaMemcpy(d_segment_points.get(), segment_points, sizeof(double) * segment_count * 4, cudaMemcpyHostToDevice), "copy one-sided boundary segment points");
3608
+ check_cuda(cudaMemcpy(d_segment_polygon_indices.get(), segment_polygon_indices, sizeof(std::uint32_t) * segment_count, cudaMemcpyHostToDevice), "copy one-sided boundary segment polygon indices");
3609
+ check_cuda(cudaMemset(d_out_edges.get(), 0, sizeof(double) * segment_count * 4), "clear one-sided boundary edges");
3610
+ check_cuda(cudaMemset(d_out_keep.get(), 0, sizeof(std::uint8_t) * segment_count), "clear one-sided boundary keep flags");
3611
+ const int threads = 256;
3612
+ const int blocks = static_cast<int>((segment_count + threads - 1) / threads);
3613
+ one_sided_boundary_edge_kernel<<<blocks, threads>>>(
3614
+ d_vertices.get(),
3615
+ d_polygon_offsets.get(),
3616
+ d_bboxes.get(),
3617
+ d_signed_twice_areas.get(),
3618
+ polygon_count,
3619
+ d_segment_points.get(),
3620
+ d_segment_polygon_indices.get(),
3621
+ segment_count,
3622
+ d_out_edges.get(),
3623
+ d_out_keep.get());
3624
+ check_cuda(cudaGetLastError(), "launch one-sided boundary edge kernel");
3625
+ check_cuda(cudaDeviceSynchronize(), "run one-sided boundary edge kernel");
3626
+ check_cuda(cudaMemcpy(out_edges, d_out_edges.get(), sizeof(double) * segment_count * 4, cudaMemcpyDeviceToHost), "copy one-sided boundary edges");
3627
+ check_cuda(cudaMemcpy(out_keep, d_out_keep.get(), sizeof(std::uint8_t) * segment_count, cudaMemcpyDeviceToHost), "copy one-sided boundary keep flags");
3628
+ g_last_error.clear();
3629
+ return 0;
3630
+ } catch (const std::exception& exc) {
3631
+ g_last_error = exc.what();
3632
+ return 1;
3633
+ } catch (...) {
3634
+ g_last_error = "unknown CUDA one-sided boundary edge error";
3635
+ return 1;
3636
+ }
3637
+ }
3638
+
3639
+ int gds_cuda_mixed_boundary_edges(
3640
+ const std::int64_t* vertices_xy,
3641
+ std::uint64_t vertex_count,
3642
+ const std::uint64_t* polygon_offsets,
3643
+ const std::int64_t* bboxes,
3644
+ const std::uint8_t* polygon_sides,
3645
+ std::uint64_t polygon_count,
3646
+ const double* segment_points,
3647
+ std::uint64_t segment_count,
3648
+ std::uint8_t direction,
3649
+ double* out_edges,
3650
+ std::uint8_t* out_keep) {
3651
+ try {
3652
+ if (!out_edges || !out_keep) {
3653
+ throw std::invalid_argument("mixed boundary edge output pointer is null");
3654
+ }
3655
+ if (segment_count == 0) {
3656
+ g_last_error.clear();
3657
+ return 0;
3658
+ }
3659
+ if (!polygon_offsets || !bboxes || !polygon_sides || !segment_points || (vertex_count && !vertices_xy)) {
3660
+ throw std::invalid_argument("mixed boundary edge received null input pointer");
3661
+ }
3662
+ if (direction != kOldSide && direction != kNewSide) {
3663
+ throw std::invalid_argument("mixed boundary edge direction must be old or new side");
3664
+ }
3665
+ DeviceBuffer<std::int64_t> d_vertices(vertex_count * 2);
3666
+ DeviceBuffer<std::uint64_t> d_polygon_offsets(polygon_count + 1);
3667
+ DeviceBuffer<std::int64_t> d_bboxes(polygon_count * 4);
3668
+ DeviceBuffer<std::uint8_t> d_polygon_sides(polygon_count);
3669
+ DeviceBuffer<double> d_segment_points(segment_count * 4);
3670
+ DeviceBuffer<double> d_out_edges(segment_count * 4);
3671
+ DeviceBuffer<std::uint8_t> d_out_keep(segment_count);
3672
+ if (vertex_count) {
3673
+ check_cuda(cudaMemcpy(d_vertices.get(), vertices_xy, sizeof(std::int64_t) * vertex_count * 2, cudaMemcpyHostToDevice), "copy mixed boundary vertices");
3674
+ }
3675
+ check_cuda(cudaMemcpy(d_polygon_offsets.get(), polygon_offsets, sizeof(std::uint64_t) * (polygon_count + 1), cudaMemcpyHostToDevice), "copy mixed boundary offsets");
3676
+ check_cuda(cudaMemcpy(d_bboxes.get(), bboxes, sizeof(std::int64_t) * polygon_count * 4, cudaMemcpyHostToDevice), "copy mixed boundary bboxes");
3677
+ check_cuda(cudaMemcpy(d_polygon_sides.get(), polygon_sides, sizeof(std::uint8_t) * polygon_count, cudaMemcpyHostToDevice), "copy mixed boundary sides");
3678
+ check_cuda(cudaMemcpy(d_segment_points.get(), segment_points, sizeof(double) * segment_count * 4, cudaMemcpyHostToDevice), "copy mixed boundary segment points");
3679
+ check_cuda(cudaMemset(d_out_edges.get(), 0, sizeof(double) * segment_count * 4), "clear mixed boundary edges");
3680
+ check_cuda(cudaMemset(d_out_keep.get(), 0, sizeof(std::uint8_t) * segment_count), "clear mixed boundary keep flags");
3681
+ const int threads = 256;
3682
+ const int blocks = static_cast<int>((segment_count + threads - 1) / threads);
3683
+ mixed_boundary_edge_kernel<<<blocks, threads>>>(
3684
+ d_vertices.get(),
3685
+ d_polygon_offsets.get(),
3686
+ d_bboxes.get(),
3687
+ d_polygon_sides.get(),
3688
+ polygon_count,
3689
+ d_segment_points.get(),
3690
+ segment_count,
3691
+ direction,
3692
+ d_out_edges.get(),
3693
+ d_out_keep.get());
3694
+ check_cuda(cudaGetLastError(), "launch mixed boundary edge kernel");
3695
+ check_cuda(cudaDeviceSynchronize(), "run mixed boundary edge kernel");
3696
+ check_cuda(cudaMemcpy(out_edges, d_out_edges.get(), sizeof(double) * segment_count * 4, cudaMemcpyDeviceToHost), "copy mixed boundary edges");
3697
+ check_cuda(cudaMemcpy(out_keep, d_out_keep.get(), sizeof(std::uint8_t) * segment_count, cudaMemcpyDeviceToHost), "copy mixed boundary keep flags");
3698
+ g_last_error.clear();
3699
+ return 0;
3700
+ } catch (const std::exception& exc) {
3701
+ g_last_error = exc.what();
3702
+ return 1;
3703
+ } catch (...) {
3704
+ g_last_error = "unknown CUDA mixed boundary edge error";
3705
+ return 1;
3706
+ }
3707
+ }
3708
+
3709
+ int gds_cuda_one_sided_boundary_edges_unsplit_if_safe(
3710
+ const std::int64_t* vertices_xy,
3711
+ std::uint64_t vertex_count,
3712
+ const std::uint64_t* polygon_offsets,
3713
+ const std::int64_t* bboxes,
3714
+ const std::int64_t* signed_twice_areas,
3715
+ std::uint64_t polygon_count,
3716
+ const double* segment_points,
3717
+ const std::uint32_t* segment_polygon_indices,
3718
+ std::uint64_t segment_count,
3719
+ double* out_edges,
3720
+ std::uint8_t* out_keep,
3721
+ std::uint8_t* out_safe) {
3722
+ try {
3723
+ if (!out_edges || !out_keep || !out_safe) {
3724
+ throw std::invalid_argument("one-sided unsplit boundary output pointer is null");
3725
+ }
3726
+ *out_safe = 0;
3727
+ if (segment_count == 0) {
3728
+ *out_safe = 1;
3729
+ g_last_error.clear();
3730
+ return 0;
3731
+ }
3732
+ if (!polygon_offsets || !bboxes || !signed_twice_areas || !segment_points || !segment_polygon_indices || (vertex_count && !vertices_xy)) {
3733
+ throw std::invalid_argument("one-sided unsplit boundary received null input pointer");
3734
+ }
3735
+ DeviceBuffer<std::int64_t> d_vertices(vertex_count * 2);
3736
+ DeviceBuffer<std::uint64_t> d_polygon_offsets(polygon_count + 1);
3737
+ DeviceBuffer<std::int64_t> d_bboxes(polygon_count * 4);
3738
+ DeviceBuffer<std::int64_t> d_signed_twice_areas(polygon_count);
3739
+ DeviceBuffer<double> d_segment_points(segment_count * 4);
3740
+ DeviceBuffer<std::uint32_t> d_segment_polygon_indices(segment_count);
3741
+ DeviceBuffer<double> d_out_edges(segment_count * 4);
3742
+ DeviceBuffer<std::uint8_t> d_out_keep(segment_count);
3743
+ DeviceBuffer<std::uint32_t> d_split_required(1);
3744
+ if (vertex_count) {
3745
+ check_cuda(cudaMemcpy(d_vertices.get(), vertices_xy, sizeof(std::int64_t) * vertex_count * 2, cudaMemcpyHostToDevice), "copy one-sided unsplit boundary vertices");
3746
+ }
3747
+ check_cuda(cudaMemcpy(d_polygon_offsets.get(), polygon_offsets, sizeof(std::uint64_t) * (polygon_count + 1), cudaMemcpyHostToDevice), "copy one-sided unsplit boundary offsets");
3748
+ check_cuda(cudaMemcpy(d_bboxes.get(), bboxes, sizeof(std::int64_t) * polygon_count * 4, cudaMemcpyHostToDevice), "copy one-sided unsplit boundary bboxes");
3749
+ check_cuda(cudaMemcpy(d_signed_twice_areas.get(), signed_twice_areas, sizeof(std::int64_t) * polygon_count, cudaMemcpyHostToDevice), "copy one-sided unsplit boundary signed areas");
3750
+ check_cuda(cudaMemcpy(d_segment_points.get(), segment_points, sizeof(double) * segment_count * 4, cudaMemcpyHostToDevice), "copy one-sided unsplit boundary segment points");
3751
+ check_cuda(cudaMemcpy(d_segment_polygon_indices.get(), segment_polygon_indices, sizeof(std::uint32_t) * segment_count, cudaMemcpyHostToDevice), "copy one-sided unsplit boundary segment polygon indices");
3752
+ check_cuda(cudaMemset(d_out_edges.get(), 0, sizeof(double) * segment_count * 4), "clear one-sided unsplit boundary edges");
3753
+ check_cuda(cudaMemset(d_out_keep.get(), 0, sizeof(std::uint8_t) * segment_count), "clear one-sided unsplit boundary keep flags");
3754
+ check_cuda(cudaMemset(d_split_required.get(), 0, sizeof(std::uint32_t)), "clear one-sided unsplit split flag");
3755
+ dim3 threads2(16, 16);
3756
+ dim3 blocks2(
3757
+ static_cast<unsigned int>((segment_count + threads2.x - 1) / threads2.x),
3758
+ static_cast<unsigned int>((segment_count + threads2.y - 1) / threads2.y));
3759
+ one_sided_split_required_kernel<<<blocks2, threads2>>>(
3760
+ d_segment_points.get(),
3761
+ segment_count,
3762
+ d_split_required.get());
3763
+ check_cuda(cudaGetLastError(), "launch one-sided split-required kernel");
3764
+ check_cuda(cudaDeviceSynchronize(), "run one-sided split-required kernel");
3765
+ std::uint32_t split_required = 0;
3766
+ check_cuda(cudaMemcpy(&split_required, d_split_required.get(), sizeof(std::uint32_t), cudaMemcpyDeviceToHost), "copy one-sided split-required flag");
3767
+ if (split_required) {
3768
+ check_cuda(cudaMemset(d_out_keep.get(), 0, sizeof(std::uint8_t) * segment_count), "clear one-sided fallback keep flags");
3769
+ check_cuda(cudaMemcpy(out_keep, d_out_keep.get(), sizeof(std::uint8_t) * segment_count, cudaMemcpyDeviceToHost), "copy one-sided fallback keep flags");
3770
+ *out_safe = 0;
3771
+ g_last_error.clear();
3772
+ return 0;
3773
+ }
3774
+ const int threads = 256;
3775
+ const int blocks = static_cast<int>((segment_count + threads - 1) / threads);
3776
+ one_sided_boundary_edge_kernel<<<blocks, threads>>>(
3777
+ d_vertices.get(),
3778
+ d_polygon_offsets.get(),
3779
+ d_bboxes.get(),
3780
+ d_signed_twice_areas.get(),
3781
+ polygon_count,
3782
+ d_segment_points.get(),
3783
+ d_segment_polygon_indices.get(),
3784
+ segment_count,
3785
+ d_out_edges.get(),
3786
+ d_out_keep.get());
3787
+ check_cuda(cudaGetLastError(), "launch one-sided unsplit boundary edge kernel");
3788
+ check_cuda(cudaDeviceSynchronize(), "run one-sided unsplit boundary edge kernel");
3789
+ check_cuda(cudaMemcpy(out_edges, d_out_edges.get(), sizeof(double) * segment_count * 4, cudaMemcpyDeviceToHost), "copy one-sided unsplit boundary edges");
3790
+ check_cuda(cudaMemcpy(out_keep, d_out_keep.get(), sizeof(std::uint8_t) * segment_count, cudaMemcpyDeviceToHost), "copy one-sided unsplit boundary keep flags");
3791
+ *out_safe = 1;
3792
+ g_last_error.clear();
3793
+ return 0;
3794
+ } catch (const std::exception& exc) {
3795
+ g_last_error = exc.what();
3796
+ return 1;
3797
+ } catch (...) {
3798
+ g_last_error = "unknown CUDA one-sided unsplit boundary edge error";
3799
+ return 1;
3800
+ }
3801
+ }
3802
+
3803
+ int gds_cuda_boundary_loops_from_edges(
3804
+ const std::int64_t* edges,
3805
+ std::uint64_t edge_count,
3806
+ GdsCudaBoundaryLoopResult* result) {
3807
+ cudaEvent_t event_start = nullptr;
3808
+ cudaEvent_t event_stop = nullptr;
3809
+ try {
3810
+ if (!result) {
3811
+ throw std::invalid_argument("CUDA boundary loop output pointer is null");
3812
+ }
3813
+ *result = GdsCudaBoundaryLoopResult{};
3814
+ if (edge_count == 0) {
3815
+ g_last_error.clear();
3816
+ return 0;
3817
+ }
3818
+ if (!edges) {
3819
+ throw std::invalid_argument("CUDA boundary loop edge pointer is null");
3820
+ }
3821
+ DeviceBuffer<std::int64_t> d_edges(edge_count * 4);
3822
+ DeviceBuffer<std::uint64_t> d_next_indices(edge_count);
3823
+ DeviceBuffer<std::uint8_t> d_remaining(edge_count);
3824
+ DeviceBuffer<std::int64_t> d_scratch_a(edge_count * 2);
3825
+ DeviceBuffer<std::int64_t> d_scratch_b(edge_count * 2);
3826
+ DeviceBuffer<std::int64_t> d_vertices(edge_count * 2);
3827
+ DeviceBuffer<std::uint64_t> d_offsets(edge_count + 1);
3828
+ DeviceBuffer<std::int64_t> d_bboxes(edge_count * 4);
3829
+ DeviceBuffer<std::int64_t> d_areas(edge_count);
3830
+ DeviceBuffer<DeviceLoopTraceStats> d_stats(1);
3831
+ check_cuda(cudaMemcpy(d_edges.get(), edges, sizeof(std::int64_t) * edge_count * 4, cudaMemcpyHostToDevice), "copy boundary loop edges");
3832
+ check_cuda(cudaMemset(d_next_indices.get(), 0xff, sizeof(std::uint64_t) * edge_count), "clear boundary loop next indices");
3833
+ check_cuda(cudaMemset(d_remaining.get(), 1, sizeof(std::uint8_t) * edge_count), "initialize boundary loop remaining flags");
3834
+ check_cuda(cudaMemset(d_vertices.get(), 0, sizeof(std::int64_t) * edge_count * 2), "clear boundary loop vertices");
3835
+ check_cuda(cudaMemset(d_offsets.get(), 0, sizeof(std::uint64_t) * (edge_count + 1)), "clear boundary loop offsets");
3836
+ check_cuda(cudaMemset(d_bboxes.get(), 0, sizeof(std::int64_t) * edge_count * 4), "clear boundary loop bboxes");
3837
+ check_cuda(cudaMemset(d_areas.get(), 0, sizeof(std::int64_t) * edge_count), "clear boundary loop areas");
3838
+ check_cuda(cudaMemset(d_stats.get(), 0, sizeof(DeviceLoopTraceStats)), "clear boundary loop stats");
3839
+ check_cuda(cudaEventCreate(&event_start), "create boundary loop start event");
3840
+ check_cuda(cudaEventCreate(&event_stop), "create boundary loop stop event");
3841
+ check_cuda(cudaEventRecord(event_start), "record boundary loop start event");
3842
+ {
3843
+ const int threads = 256;
3844
+ const int blocks = static_cast<int>((edge_count + threads - 1) / threads);
3845
+ boundary_loop_next_kernel<<<blocks, threads>>>(d_edges.get(), edge_count, d_next_indices.get());
3846
+ check_cuda(cudaGetLastError(), "launch boundary loop next kernel");
3847
+ }
3848
+ boundary_loop_trace_kernel<<<1, 1>>>(
3849
+ d_edges.get(),
3850
+ edge_count,
3851
+ d_next_indices.get(),
3852
+ d_remaining.get(),
3853
+ d_scratch_a.get(),
3854
+ d_scratch_b.get(),
3855
+ d_vertices.get(),
3856
+ d_offsets.get(),
3857
+ d_bboxes.get(),
3858
+ d_areas.get(),
3859
+ d_stats.get());
3860
+ check_cuda(cudaGetLastError(), "launch boundary loop trace kernel");
3861
+ check_cuda(cudaEventRecord(event_stop), "record boundary loop stop event");
3862
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize boundary loop stop event");
3863
+ DeviceLoopTraceStats stats{};
3864
+ check_cuda(cudaMemcpy(&stats, d_stats.get(), sizeof(DeviceLoopTraceStats), cudaMemcpyDeviceToHost), "copy boundary loop stats");
3865
+ if (stats.error) {
3866
+ throw std::runtime_error(stats.error == 1 ? "CUDA boundary loop trace exceeded raw vertex limit" : "CUDA boundary loop trace exceeded output limit");
3867
+ }
3868
+ result->fragment_count = stats.fragment_count;
3869
+ result->vertex_count = stats.vertex_count;
3870
+ result->dropped_zero_area_loop_count = stats.dropped_zero_area_loop_count;
3871
+ result->cuda_kernel_count = 1;
3872
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure boundary loop elapsed time");
3873
+ if (stats.fragment_count) {
3874
+ result->fragment_offsets = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * (stats.fragment_count + 1)));
3875
+ result->vertices_xy = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * stats.vertex_count * 2));
3876
+ result->bboxes = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * stats.fragment_count * 4));
3877
+ result->twice_areas = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * stats.fragment_count));
3878
+ if (!result->fragment_offsets || !result->vertices_xy || !result->bboxes || !result->twice_areas) {
3879
+ throw std::runtime_error("CUDA boundary loop host allocation failed");
3880
+ }
3881
+ check_cuda(cudaMemcpy(result->fragment_offsets, d_offsets.get(), sizeof(std::uint64_t) * (stats.fragment_count + 1), cudaMemcpyDeviceToHost), "copy boundary loop offsets");
3882
+ check_cuda(cudaMemcpy(result->vertices_xy, d_vertices.get(), sizeof(std::int64_t) * stats.vertex_count * 2, cudaMemcpyDeviceToHost), "copy boundary loop vertices");
3883
+ check_cuda(cudaMemcpy(result->bboxes, d_bboxes.get(), sizeof(std::int64_t) * stats.fragment_count * 4, cudaMemcpyDeviceToHost), "copy boundary loop bboxes");
3884
+ check_cuda(cudaMemcpy(result->twice_areas, d_areas.get(), sizeof(std::int64_t) * stats.fragment_count, cudaMemcpyDeviceToHost), "copy boundary loop areas");
3885
+ } else {
3886
+ result->fragment_offsets = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t)));
3887
+ if (!result->fragment_offsets) {
3888
+ throw std::runtime_error("CUDA boundary loop empty offset allocation failed");
3889
+ }
3890
+ result->fragment_offsets[0] = 0;
3891
+ }
3892
+ cudaEventDestroy(event_start);
3893
+ cudaEventDestroy(event_stop);
3894
+ g_last_error.clear();
3895
+ return 0;
3896
+ } catch (const std::exception& exc) {
3897
+ if (event_start) {
3898
+ cudaEventDestroy(event_start);
3899
+ }
3900
+ if (event_stop) {
3901
+ cudaEventDestroy(event_stop);
3902
+ }
3903
+ g_last_error = exc.what();
3904
+ if (result) {
3905
+ gds_cuda_free_boundary_loop_result(result);
3906
+ }
3907
+ return 1;
3908
+ } catch (...) {
3909
+ if (event_start) {
3910
+ cudaEventDestroy(event_start);
3911
+ }
3912
+ if (event_stop) {
3913
+ cudaEventDestroy(event_stop);
3914
+ }
3915
+ g_last_error = "unknown CUDA boundary loop error";
3916
+ if (result) {
3917
+ gds_cuda_free_boundary_loop_result(result);
3918
+ }
3919
+ return 1;
3920
+ }
3921
+ }
3922
+
3923
+ int gds_cuda_axis_aligned_boundary_edges_from_polygons(
3924
+ const std::int64_t* vertices_xy,
3925
+ const std::uint64_t* fragment_offsets,
3926
+ std::uint64_t fragment_count,
3927
+ GdsCudaBoundaryEdgesResult* result) {
3928
+ cudaEvent_t event_start = nullptr;
3929
+ cudaEvent_t event_stop = nullptr;
3930
+ try {
3931
+ if (!result) {
3932
+ throw std::invalid_argument("axis-aligned boundary edge result pointer is null");
3933
+ }
3934
+ *result = GdsCudaBoundaryEdgesResult{};
3935
+ if (fragment_count == 0) {
3936
+ g_last_error.clear();
3937
+ return 0;
3938
+ }
3939
+ if (!vertices_xy || !fragment_offsets) {
3940
+ throw std::invalid_argument("axis-aligned boundary edge input contains null pointer");
3941
+ }
3942
+ const std::uint64_t vertex_count = fragment_offsets[fragment_count];
3943
+ if (vertex_count == 0) {
3944
+ g_last_error.clear();
3945
+ return 0;
3946
+ }
3947
+
3948
+ check_cuda(cudaEventCreate(&event_start), "create axis boundary edge start event");
3949
+ check_cuda(cudaEventCreate(&event_stop), "create axis boundary edge stop event");
3950
+ check_cuda(cudaEventRecord(event_start), "record axis boundary edge start event");
3951
+
3952
+ DeviceBuffer<std::int64_t> d_vertices(vertex_count * 2);
3953
+ DeviceBuffer<std::uint64_t> d_offsets(fragment_count + 1);
3954
+ DeviceBuffer<AxisRawEdge> d_raw_edges(vertex_count);
3955
+ DeviceBuffer<AxisEndpointKey> d_endpoint_storage(vertex_count * 2);
3956
+ DeviceBuffer<std::uint32_t> d_error(1);
3957
+ check_cuda(cudaMemcpy(d_vertices.get(), vertices_xy, sizeof(std::int64_t) * vertex_count * 2, cudaMemcpyHostToDevice), "copy axis boundary vertices");
3958
+ check_cuda(cudaMemcpy(d_offsets.get(), fragment_offsets, sizeof(std::uint64_t) * (fragment_count + 1), cudaMemcpyHostToDevice), "copy axis boundary offsets");
3959
+ check_cuda(cudaMemset(d_raw_edges.get(), 0, sizeof(AxisRawEdge) * vertex_count), "clear axis raw edges");
3960
+ check_cuda(cudaMemset(d_error.get(), 0, sizeof(std::uint32_t)), "clear axis boundary error");
3961
+
3962
+ const int threads = 256;
3963
+ const int fragment_blocks = static_cast<int>(std::min<std::uint64_t>((fragment_count + threads - 1) / threads, 65535));
3964
+ axis_raw_edge_kernel<<<fragment_blocks, threads>>>(
3965
+ d_vertices.get(),
3966
+ d_offsets.get(),
3967
+ fragment_count,
3968
+ d_raw_edges.get(),
3969
+ d_endpoint_storage.get(),
3970
+ d_error.get());
3971
+ check_cuda(cudaGetLastError(), "launch axis raw edge kernel");
3972
+
3973
+ std::uint32_t error = 0;
3974
+ check_cuda(cudaMemcpy(&error, d_error.get(), sizeof(std::uint32_t), cudaMemcpyDeviceToHost), "copy axis raw edge error");
3975
+ if (error) {
3976
+ throw std::runtime_error("axis-aligned boundary edge CUDA path received non-axis-aligned edge");
3977
+ }
3978
+
3979
+ thrust::device_vector<AxisEndpointKey> d_endpoints(vertex_count * 2);
3980
+ check_cuda(cudaMemcpy(thrust::raw_pointer_cast(d_endpoints.data()), d_endpoint_storage.get(), sizeof(AxisEndpointKey) * vertex_count * 2, cudaMemcpyDeviceToDevice), "copy axis endpoints to thrust vector");
3981
+ auto valid_end = thrust::remove_if(d_endpoints.begin(), d_endpoints.end(), AxisEndpointInvalid{});
3982
+ d_endpoints.erase(valid_end, d_endpoints.end());
3983
+ if (d_endpoints.empty()) {
3984
+ check_cuda(cudaEventRecord(event_stop), "record empty axis boundary edge stop event");
3985
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize empty axis boundary edge stop event");
3986
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure empty axis boundary edge elapsed time");
3987
+ result->cuda_kernel_count = 1;
3988
+ cudaEventDestroy(event_start);
3989
+ cudaEventDestroy(event_stop);
3990
+ g_last_error.clear();
3991
+ return 0;
3992
+ }
3993
+ thrust::sort(d_endpoints.begin(), d_endpoints.end());
3994
+ auto unique_end = thrust::unique(d_endpoints.begin(), d_endpoints.end());
3995
+ d_endpoints.erase(unique_end, d_endpoints.end());
3996
+ const std::uint64_t endpoint_count = d_endpoints.size();
3997
+
3998
+ DeviceBuffer<std::uint64_t> d_split_counts(vertex_count);
3999
+ DeviceBuffer<std::uint64_t> d_split_offsets(vertex_count);
4000
+ const int edge_blocks = static_cast<int>(std::min<std::uint64_t>((vertex_count + threads - 1) / threads, 65535));
4001
+ axis_split_count_kernel<<<edge_blocks, threads>>>(
4002
+ d_raw_edges.get(),
4003
+ vertex_count,
4004
+ thrust::raw_pointer_cast(d_endpoints.data()),
4005
+ endpoint_count,
4006
+ d_split_counts.get());
4007
+ check_cuda(cudaGetLastError(), "launch axis split count kernel");
4008
+ thrust::device_ptr<std::uint64_t> split_counts_begin(d_split_counts.get());
4009
+ thrust::device_ptr<std::uint64_t> split_counts_end(d_split_counts.get() + vertex_count);
4010
+ thrust::device_ptr<std::uint64_t> split_offsets_begin(d_split_offsets.get());
4011
+ thrust::exclusive_scan(thrust::device, split_counts_begin, split_counts_end, split_offsets_begin);
4012
+
4013
+ std::uint64_t last_offset = 0;
4014
+ std::uint64_t last_count = 0;
4015
+ check_cuda(cudaMemcpy(&last_offset, d_split_offsets.get() + (vertex_count - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy axis split last offset");
4016
+ check_cuda(cudaMemcpy(&last_count, d_split_counts.get() + (vertex_count - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy axis split last count");
4017
+ if (last_count > std::numeric_limits<std::uint64_t>::max() - last_offset) {
4018
+ throw std::overflow_error("axis split edge count overflow");
4019
+ }
4020
+ const std::uint64_t split_edge_count = last_offset + last_count;
4021
+ result->cuda_kernel_count = 4;
4022
+ if (split_edge_count == 0) {
4023
+ check_cuda(cudaEventRecord(event_stop), "record no-split axis boundary edge stop event");
4024
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize no-split axis boundary edge stop event");
4025
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure no-split axis boundary edge elapsed time");
4026
+ cudaEventDestroy(event_start);
4027
+ cudaEventDestroy(event_stop);
4028
+ g_last_error.clear();
4029
+ return 0;
4030
+ }
4031
+
4032
+ DeviceBuffer<EdgeKey> d_split_edges(split_edge_count);
4033
+ axis_split_emit_kernel<<<edge_blocks, threads>>>(
4034
+ d_raw_edges.get(),
4035
+ vertex_count,
4036
+ thrust::raw_pointer_cast(d_endpoints.data()),
4037
+ endpoint_count,
4038
+ d_split_offsets.get(),
4039
+ d_split_edges.get());
4040
+ check_cuda(cudaGetLastError(), "launch axis split emit kernel");
4041
+
4042
+ thrust::device_ptr<EdgeKey> split_begin(d_split_edges.get());
4043
+ thrust::device_ptr<EdgeKey> split_end(d_split_edges.get() + split_edge_count);
4044
+ thrust::sort(thrust::device, split_begin, split_end);
4045
+ thrust::device_vector<EdgeKey> d_unique_edges(split_edge_count);
4046
+ thrust::device_vector<std::uint64_t> d_unique_counts(split_edge_count);
4047
+ auto reduce_end = thrust::reduce_by_key(
4048
+ thrust::device,
4049
+ split_begin,
4050
+ split_end,
4051
+ thrust::make_constant_iterator<std::uint64_t>(1),
4052
+ d_unique_edges.begin(),
4053
+ d_unique_counts.begin());
4054
+ const std::uint64_t unique_count = reduce_end.first - d_unique_edges.begin();
4055
+ if (unique_count == 0) {
4056
+ check_cuda(cudaEventRecord(event_stop), "record no-unique axis boundary edge stop event");
4057
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize no-unique axis boundary edge stop event");
4058
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure no-unique axis boundary edge elapsed time");
4059
+ cudaEventDestroy(event_start);
4060
+ cudaEventDestroy(event_stop);
4061
+ g_last_error.clear();
4062
+ return 0;
4063
+ }
4064
+ d_unique_edges.resize(unique_count);
4065
+ d_unique_counts.resize(unique_count);
4066
+
4067
+ thrust::device_vector<std::uint64_t> d_surplus_counts(unique_count);
4068
+ const int unique_blocks = static_cast<int>(std::min<std::uint64_t>((unique_count + threads - 1) / threads, 65535));
4069
+ edge_surplus_count_kernel<<<unique_blocks, threads>>>(
4070
+ thrust::raw_pointer_cast(d_unique_edges.data()),
4071
+ thrust::raw_pointer_cast(d_unique_counts.data()),
4072
+ unique_count,
4073
+ thrust::raw_pointer_cast(d_surplus_counts.data()));
4074
+ check_cuda(cudaGetLastError(), "launch axis edge surplus count kernel");
4075
+ thrust::device_vector<std::uint64_t> d_surplus_offsets(unique_count);
4076
+ thrust::exclusive_scan(d_surplus_counts.begin(), d_surplus_counts.end(), d_surplus_offsets.begin());
4077
+ check_cuda(cudaMemcpy(&last_offset, thrust::raw_pointer_cast(d_surplus_offsets.data()) + (unique_count - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy axis surplus last offset");
4078
+ check_cuda(cudaMemcpy(&last_count, thrust::raw_pointer_cast(d_surplus_counts.data()) + (unique_count - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy axis surplus last count");
4079
+ if (last_count > std::numeric_limits<std::uint64_t>::max() - last_offset) {
4080
+ throw std::overflow_error("axis surplus edge count overflow");
4081
+ }
4082
+ result->edge_count = last_offset + last_count;
4083
+ if (result->edge_count) {
4084
+ DeviceBuffer<std::int64_t> d_out_edges(result->edge_count * 4);
4085
+ edge_surplus_emit_kernel<<<unique_blocks, threads>>>(
4086
+ thrust::raw_pointer_cast(d_unique_edges.data()),
4087
+ thrust::raw_pointer_cast(d_surplus_counts.data()),
4088
+ thrust::raw_pointer_cast(d_surplus_offsets.data()),
4089
+ unique_count,
4090
+ d_out_edges.get());
4091
+ check_cuda(cudaGetLastError(), "launch axis edge surplus emit kernel");
4092
+ check_cuda(cudaEventRecord(event_stop), "record axis boundary edge stop event");
4093
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize axis boundary edge stop event");
4094
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure axis boundary edge elapsed time");
4095
+ result->edges = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * result->edge_count * 4));
4096
+ if (!result->edges) {
4097
+ throw std::runtime_error("axis boundary edge output allocation failed");
4098
+ }
4099
+ check_cuda(cudaMemcpy(result->edges, d_out_edges.get(), sizeof(std::int64_t) * result->edge_count * 4, cudaMemcpyDeviceToHost), "copy axis boundary edges");
4100
+ } else {
4101
+ check_cuda(cudaEventRecord(event_stop), "record empty-surplus axis boundary edge stop event");
4102
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize empty-surplus axis boundary edge stop event");
4103
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure empty-surplus axis boundary edge elapsed time");
4104
+ }
4105
+ result->cuda_kernel_count = 6;
4106
+ cudaEventDestroy(event_start);
4107
+ cudaEventDestroy(event_stop);
4108
+ g_last_error.clear();
4109
+ return 0;
4110
+ } catch (const std::exception& exc) {
4111
+ if (event_start) {
4112
+ cudaEventDestroy(event_start);
4113
+ }
4114
+ if (event_stop) {
4115
+ cudaEventDestroy(event_stop);
4116
+ }
4117
+ g_last_error = exc.what();
4118
+ if (result) {
4119
+ gds_cuda_free_boundary_edges_result(result);
4120
+ }
4121
+ return 1;
4122
+ } catch (...) {
4123
+ if (event_start) {
4124
+ cudaEventDestroy(event_start);
4125
+ }
4126
+ if (event_stop) {
4127
+ cudaEventDestroy(event_stop);
4128
+ }
4129
+ g_last_error = "unknown CUDA axis boundary edge error";
4130
+ if (result) {
4131
+ gds_cuda_free_boundary_edges_result(result);
4132
+ }
4133
+ return 1;
4134
+ }
4135
+ }
4136
+
4137
+ int gds_cuda_split_free_boundary_edges_from_polygons(
4138
+ const std::int64_t* vertices_xy,
4139
+ const std::uint64_t* fragment_offsets,
4140
+ std::uint64_t fragment_count,
4141
+ GdsCudaBoundaryEdgesResult* result) {
4142
+ cudaEvent_t event_start = nullptr;
4143
+ cudaEvent_t event_stop = nullptr;
4144
+ try {
4145
+ if (!result) {
4146
+ throw std::invalid_argument("split-free boundary edge result pointer is null");
4147
+ }
4148
+ *result = GdsCudaBoundaryEdgesResult{};
4149
+ if (fragment_count == 0) {
4150
+ g_last_error.clear();
4151
+ return 0;
4152
+ }
4153
+ if (!vertices_xy || !fragment_offsets) {
4154
+ throw std::invalid_argument("split-free boundary edge input contains null pointer");
4155
+ }
4156
+ const std::uint64_t vertex_count = fragment_offsets[fragment_count];
4157
+ if (vertex_count == 0) {
4158
+ g_last_error.clear();
4159
+ return 0;
4160
+ }
4161
+ if (vertex_count > 100000) {
4162
+ throw std::overflow_error("split-free boundary edge input is too large for quadratic safety check");
4163
+ }
4164
+ if (vertex_count > std::numeric_limits<std::uint64_t>::max() / vertex_count) {
4165
+ throw std::overflow_error("split-free boundary edge pair count overflow");
4166
+ }
4167
+
4168
+ check_cuda(cudaEventCreate(&event_start), "create split-free boundary edge start event");
4169
+ check_cuda(cudaEventCreate(&event_stop), "create split-free boundary edge stop event");
4170
+ check_cuda(cudaEventRecord(event_start), "record split-free boundary edge start event");
4171
+
4172
+ DeviceBuffer<std::int64_t> d_vertices(vertex_count * 2);
4173
+ DeviceBuffer<std::uint64_t> d_offsets(fragment_count + 1);
4174
+ DeviceBuffer<EdgeKey> d_raw_edges(vertex_count);
4175
+ DeviceBuffer<std::uint32_t> d_split_required(1);
4176
+ check_cuda(cudaMemcpy(d_vertices.get(), vertices_xy, sizeof(std::int64_t) * vertex_count * 2, cudaMemcpyHostToDevice), "copy split-free boundary vertices");
4177
+ check_cuda(cudaMemcpy(d_offsets.get(), fragment_offsets, sizeof(std::uint64_t) * (fragment_count + 1), cudaMemcpyHostToDevice), "copy split-free boundary offsets");
4178
+ check_cuda(cudaMemset(d_raw_edges.get(), 0, sizeof(EdgeKey) * vertex_count), "clear split-free raw edges");
4179
+ check_cuda(cudaMemset(d_split_required.get(), 0, sizeof(std::uint32_t)), "clear split-free required flag");
4180
+
4181
+ const int threads = 256;
4182
+ const int fragment_blocks = static_cast<int>(std::min<std::uint64_t>((fragment_count + threads - 1) / threads, 65535));
4183
+ split_free_raw_edge_kernel<<<fragment_blocks, threads>>>(
4184
+ d_vertices.get(),
4185
+ d_offsets.get(),
4186
+ fragment_count,
4187
+ d_raw_edges.get());
4188
+ check_cuda(cudaGetLastError(), "launch split-free raw edge kernel");
4189
+
4190
+ const std::uint64_t total_pairs = vertex_count * vertex_count;
4191
+ const int pair_blocks = static_cast<int>(std::min<std::uint64_t>((total_pairs + threads - 1) / threads, 65535));
4192
+ split_free_required_kernel<<<pair_blocks, threads>>>(
4193
+ d_raw_edges.get(),
4194
+ vertex_count,
4195
+ d_split_required.get());
4196
+ check_cuda(cudaGetLastError(), "launch split-free required kernel");
4197
+ std::uint32_t split_required = 0;
4198
+ check_cuda(cudaMemcpy(&split_required, d_split_required.get(), sizeof(std::uint32_t), cudaMemcpyDeviceToHost), "copy split-free required flag");
4199
+ if (split_required) {
4200
+ throw std::runtime_error("split-free boundary edge CUDA path requires collinear splitting");
4201
+ }
4202
+
4203
+ thrust::device_vector<EdgeKey> d_edges(vertex_count);
4204
+ check_cuda(cudaMemcpy(thrust::raw_pointer_cast(d_edges.data()), d_raw_edges.get(), sizeof(EdgeKey) * vertex_count, cudaMemcpyDeviceToDevice), "copy split-free raw edges to thrust vector");
4205
+ auto valid_end = thrust::remove_if(d_edges.begin(), d_edges.end(), EdgeInvalid{});
4206
+ d_edges.erase(valid_end, d_edges.end());
4207
+ if (d_edges.empty()) {
4208
+ check_cuda(cudaEventRecord(event_stop), "record empty split-free boundary edge stop event");
4209
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize empty split-free boundary edge stop event");
4210
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure empty split-free boundary edge elapsed time");
4211
+ result->cuda_kernel_count = 2;
4212
+ cudaEventDestroy(event_start);
4213
+ cudaEventDestroy(event_stop);
4214
+ g_last_error.clear();
4215
+ return 0;
4216
+ }
4217
+ thrust::sort(d_edges.begin(), d_edges.end());
4218
+ const std::uint64_t edge_count = d_edges.size();
4219
+ thrust::device_vector<EdgeKey> d_unique_edges(edge_count);
4220
+ thrust::device_vector<std::uint64_t> d_unique_counts(edge_count);
4221
+ auto reduce_end = thrust::reduce_by_key(
4222
+ thrust::device,
4223
+ d_edges.begin(),
4224
+ d_edges.end(),
4225
+ thrust::make_constant_iterator<std::uint64_t>(1),
4226
+ d_unique_edges.begin(),
4227
+ d_unique_counts.begin());
4228
+ const std::uint64_t unique_count = reduce_end.first - d_unique_edges.begin();
4229
+ if (unique_count == 0) {
4230
+ check_cuda(cudaEventRecord(event_stop), "record no-unique split-free boundary edge stop event");
4231
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize no-unique split-free boundary edge stop event");
4232
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure no-unique split-free boundary edge elapsed time");
4233
+ result->cuda_kernel_count = 2;
4234
+ cudaEventDestroy(event_start);
4235
+ cudaEventDestroy(event_stop);
4236
+ g_last_error.clear();
4237
+ return 0;
4238
+ }
4239
+ d_unique_edges.resize(unique_count);
4240
+ d_unique_counts.resize(unique_count);
4241
+
4242
+ thrust::device_vector<std::uint64_t> d_surplus_counts(unique_count);
4243
+ const int unique_blocks = static_cast<int>(std::min<std::uint64_t>((unique_count + threads - 1) / threads, 65535));
4244
+ edge_surplus_count_kernel<<<unique_blocks, threads>>>(
4245
+ thrust::raw_pointer_cast(d_unique_edges.data()),
4246
+ thrust::raw_pointer_cast(d_unique_counts.data()),
4247
+ unique_count,
4248
+ thrust::raw_pointer_cast(d_surplus_counts.data()));
4249
+ check_cuda(cudaGetLastError(), "launch split-free edge surplus count kernel");
4250
+ thrust::device_vector<std::uint64_t> d_surplus_offsets(unique_count);
4251
+ thrust::exclusive_scan(d_surplus_counts.begin(), d_surplus_counts.end(), d_surplus_offsets.begin());
4252
+ std::uint64_t last_offset = 0;
4253
+ std::uint64_t last_count = 0;
4254
+ check_cuda(cudaMemcpy(&last_offset, thrust::raw_pointer_cast(d_surplus_offsets.data()) + (unique_count - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy split-free surplus last offset");
4255
+ check_cuda(cudaMemcpy(&last_count, thrust::raw_pointer_cast(d_surplus_counts.data()) + (unique_count - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy split-free surplus last count");
4256
+ if (last_count > std::numeric_limits<std::uint64_t>::max() - last_offset) {
4257
+ throw std::overflow_error("split-free surplus edge count overflow");
4258
+ }
4259
+ result->edge_count = last_offset + last_count;
4260
+ if (result->edge_count) {
4261
+ DeviceBuffer<std::int64_t> d_out_edges(result->edge_count * 4);
4262
+ edge_surplus_emit_kernel<<<unique_blocks, threads>>>(
4263
+ thrust::raw_pointer_cast(d_unique_edges.data()),
4264
+ thrust::raw_pointer_cast(d_surplus_counts.data()),
4265
+ thrust::raw_pointer_cast(d_surplus_offsets.data()),
4266
+ unique_count,
4267
+ d_out_edges.get());
4268
+ check_cuda(cudaGetLastError(), "launch split-free edge surplus emit kernel");
4269
+ check_cuda(cudaEventRecord(event_stop), "record split-free boundary edge stop event");
4270
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize split-free boundary edge stop event");
4271
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure split-free boundary edge elapsed time");
4272
+ result->edges = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * result->edge_count * 4));
4273
+ if (!result->edges) {
4274
+ throw std::runtime_error("split-free boundary edge output allocation failed");
4275
+ }
4276
+ check_cuda(cudaMemcpy(result->edges, d_out_edges.get(), sizeof(std::int64_t) * result->edge_count * 4, cudaMemcpyDeviceToHost), "copy split-free boundary edges");
4277
+ } else {
4278
+ check_cuda(cudaEventRecord(event_stop), "record empty-surplus split-free boundary edge stop event");
4279
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize empty-surplus split-free boundary edge stop event");
4280
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure empty-surplus split-free boundary edge elapsed time");
4281
+ }
4282
+ result->cuda_kernel_count = 5;
4283
+ cudaEventDestroy(event_start);
4284
+ cudaEventDestroy(event_stop);
4285
+ g_last_error.clear();
4286
+ return 0;
4287
+ } catch (const std::exception& exc) {
4288
+ if (event_start) {
4289
+ cudaEventDestroy(event_start);
4290
+ }
4291
+ if (event_stop) {
4292
+ cudaEventDestroy(event_stop);
4293
+ }
4294
+ g_last_error = exc.what();
4295
+ if (result) {
4296
+ gds_cuda_free_boundary_edges_result(result);
4297
+ }
4298
+ return 1;
4299
+ } catch (...) {
4300
+ if (event_start) {
4301
+ cudaEventDestroy(event_start);
4302
+ }
4303
+ if (event_stop) {
4304
+ cudaEventDestroy(event_stop);
4305
+ }
4306
+ g_last_error = "unknown CUDA split-free boundary edge error";
4307
+ if (result) {
4308
+ gds_cuda_free_boundary_edges_result(result);
4309
+ }
4310
+ return 1;
4311
+ }
4312
+ }
4313
+
4314
+ int gds_cuda_context_overlap_pairs(
4315
+ const std::int64_t* component_bboxes,
4316
+ const std::int32_t* component_layers,
4317
+ const std::int32_t* component_datatypes,
4318
+ std::uint64_t component_count,
4319
+ const std::int64_t* polygon_bboxes,
4320
+ const std::int32_t* polygon_layers,
4321
+ const std::int32_t* polygon_datatypes,
4322
+ std::uint64_t polygon_count,
4323
+ GdsCudaContextOverlapPairsResult* result) {
4324
+ cudaEvent_t event_start = nullptr;
4325
+ cudaEvent_t event_stop = nullptr;
4326
+ try {
4327
+ if (!result) {
4328
+ throw std::invalid_argument("context overlap result pointer is null");
4329
+ }
4330
+ *result = GdsCudaContextOverlapPairsResult{};
4331
+ if (component_count == 0 || polygon_count == 0) {
4332
+ g_last_error.clear();
4333
+ return 0;
4334
+ }
4335
+ if (!component_bboxes || !component_layers || !component_datatypes || !polygon_bboxes || !polygon_layers || !polygon_datatypes) {
4336
+ throw std::invalid_argument("context overlap input contains null pointer");
4337
+ }
4338
+ if (component_count > std::numeric_limits<std::uint64_t>::max() / polygon_count) {
4339
+ throw std::overflow_error("context overlap pair search count overflow");
4340
+ }
4341
+ const std::uint64_t total_pairs = component_count * polygon_count;
4342
+ check_cuda(cudaEventCreate(&event_start), "create context overlap start event");
4343
+ check_cuda(cudaEventCreate(&event_stop), "create context overlap stop event");
4344
+ check_cuda(cudaEventRecord(event_start), "record context overlap start event");
4345
+
4346
+ DeviceBuffer<std::int64_t> d_component_bboxes(component_count * 4);
4347
+ DeviceBuffer<std::int32_t> d_component_layers(component_count);
4348
+ DeviceBuffer<std::int32_t> d_component_datatypes(component_count);
4349
+ DeviceBuffer<std::int64_t> d_polygon_bboxes(polygon_count * 4);
4350
+ DeviceBuffer<std::int32_t> d_polygon_layers(polygon_count);
4351
+ DeviceBuffer<std::int32_t> d_polygon_datatypes(polygon_count);
4352
+ DeviceBuffer<std::uint64_t> d_flags(total_pairs);
4353
+
4354
+ check_cuda(cudaMemcpy(d_component_bboxes.get(), component_bboxes, sizeof(std::int64_t) * component_count * 4, cudaMemcpyHostToDevice), "copy context component bboxes");
4355
+ check_cuda(cudaMemcpy(d_component_layers.get(), component_layers, sizeof(std::int32_t) * component_count, cudaMemcpyHostToDevice), "copy context component layers");
4356
+ check_cuda(cudaMemcpy(d_component_datatypes.get(), component_datatypes, sizeof(std::int32_t) * component_count, cudaMemcpyHostToDevice), "copy context component datatypes");
4357
+ check_cuda(cudaMemcpy(d_polygon_bboxes.get(), polygon_bboxes, sizeof(std::int64_t) * polygon_count * 4, cudaMemcpyHostToDevice), "copy context polygon bboxes");
4358
+ check_cuda(cudaMemcpy(d_polygon_layers.get(), polygon_layers, sizeof(std::int32_t) * polygon_count, cudaMemcpyHostToDevice), "copy context polygon layers");
4359
+ check_cuda(cudaMemcpy(d_polygon_datatypes.get(), polygon_datatypes, sizeof(std::int32_t) * polygon_count, cudaMemcpyHostToDevice), "copy context polygon datatypes");
4360
+
4361
+ const int threads = 256;
4362
+ const int blocks = static_cast<int>(std::min<std::uint64_t>((total_pairs + threads - 1) / threads, 65535));
4363
+ context_overlap_flag_kernel<<<blocks, threads>>>(
4364
+ d_component_bboxes.get(),
4365
+ d_component_layers.get(),
4366
+ d_component_datatypes.get(),
4367
+ component_count,
4368
+ d_polygon_bboxes.get(),
4369
+ d_polygon_layers.get(),
4370
+ d_polygon_datatypes.get(),
4371
+ polygon_count,
4372
+ total_pairs,
4373
+ d_flags.get());
4374
+ check_cuda(cudaGetLastError(), "launch context overlap flag kernel");
4375
+
4376
+ thrust::device_ptr<std::uint64_t> flags_begin(d_flags.get());
4377
+ thrust::device_ptr<std::uint64_t> flags_end(d_flags.get() + total_pairs);
4378
+ thrust::device_vector<std::uint64_t> d_offsets(total_pairs);
4379
+ thrust::exclusive_scan(thrust::device, flags_begin, flags_end, d_offsets.begin());
4380
+
4381
+ std::uint64_t last_offset = 0;
4382
+ std::uint64_t last_flag = 0;
4383
+ check_cuda(cudaMemcpy(&last_offset, thrust::raw_pointer_cast(d_offsets.data()) + (total_pairs - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy context overlap last offset");
4384
+ check_cuda(cudaMemcpy(&last_flag, d_flags.get() + (total_pairs - 1), sizeof(std::uint64_t), cudaMemcpyDeviceToHost), "copy context overlap last flag");
4385
+ if (last_flag > std::numeric_limits<std::uint64_t>::max() - last_offset) {
4386
+ throw std::overflow_error("context overlap pair count overflow");
4387
+ }
4388
+ result->pair_count = last_offset + last_flag;
4389
+ result->cuda_kernel_count = 3;
4390
+
4391
+ if (result->pair_count) {
4392
+ DeviceBuffer<std::uint64_t> d_component_indices(result->pair_count);
4393
+ DeviceBuffer<std::uint64_t> d_polygon_indices(result->pair_count);
4394
+ context_overlap_emit_kernel<<<blocks, threads>>>(
4395
+ d_flags.get(),
4396
+ thrust::raw_pointer_cast(d_offsets.data()),
4397
+ polygon_count,
4398
+ total_pairs,
4399
+ d_component_indices.get(),
4400
+ d_polygon_indices.get());
4401
+ check_cuda(cudaGetLastError(), "launch context overlap emit kernel");
4402
+ check_cuda(cudaEventRecord(event_stop), "record context overlap stop event");
4403
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize context overlap stop event");
4404
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure context overlap elapsed time");
4405
+
4406
+ result->component_indices = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * result->pair_count));
4407
+ result->polygon_indices = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * result->pair_count));
4408
+ if (!result->component_indices || !result->polygon_indices) {
4409
+ throw std::runtime_error("context overlap output allocation failed");
4410
+ }
4411
+ check_cuda(cudaMemcpy(result->component_indices, d_component_indices.get(), sizeof(std::uint64_t) * result->pair_count, cudaMemcpyDeviceToHost), "copy context overlap component indices");
4412
+ check_cuda(cudaMemcpy(result->polygon_indices, d_polygon_indices.get(), sizeof(std::uint64_t) * result->pair_count, cudaMemcpyDeviceToHost), "copy context overlap polygon indices");
4413
+ } else {
4414
+ check_cuda(cudaEventRecord(event_stop), "record empty context overlap stop event");
4415
+ check_cuda(cudaEventSynchronize(event_stop), "synchronize empty context overlap stop event");
4416
+ check_cuda(cudaEventElapsedTime(&result->cuda_event_elapsed_ms, event_start, event_stop), "measure empty context overlap elapsed time");
4417
+ }
4418
+ cudaEventDestroy(event_start);
4419
+ cudaEventDestroy(event_stop);
4420
+ g_last_error.clear();
4421
+ return 0;
4422
+ } catch (const std::exception& exc) {
4423
+ if (event_start) {
4424
+ cudaEventDestroy(event_start);
4425
+ }
4426
+ if (event_stop) {
4427
+ cudaEventDestroy(event_stop);
4428
+ }
4429
+ g_last_error = exc.what();
4430
+ if (result) {
4431
+ gds_cuda_free_context_overlap_pairs_result(result);
4432
+ }
4433
+ return 1;
4434
+ } catch (...) {
4435
+ if (event_start) {
4436
+ cudaEventDestroy(event_start);
4437
+ }
4438
+ if (event_stop) {
4439
+ cudaEventDestroy(event_stop);
4440
+ }
4441
+ g_last_error = "unknown CUDA context overlap error";
4442
+ if (result) {
4443
+ gds_cuda_free_context_overlap_pairs_result(result);
4444
+ }
4445
+ return 1;
4446
+ }
4447
+ }
4448
+
4449
+ int gds_cuda_rect_set_exact_fragments(
4450
+ const std::int64_t* old_rects,
4451
+ const std::int64_t* new_rects,
4452
+ const std::uint8_t* old_counts,
4453
+ const std::uint8_t* new_counts,
4454
+ std::uint64_t task_count,
4455
+ std::uint16_t max_rects,
4456
+ std::uint16_t max_fragments,
4457
+ std::int64_t* out_rects,
4458
+ std::uint16_t* out_counts,
4459
+ std::int64_t* out_twice_areas) {
4460
+ try {
4461
+ if (!out_rects || !out_counts || !out_twice_areas) {
4462
+ throw std::invalid_argument("rect set exact fragment output pointer is null");
4463
+ }
4464
+ if (task_count == 0) {
4465
+ g_last_error.clear();
4466
+ return 0;
4467
+ }
4468
+ if (!old_rects || !new_rects || !old_counts || !new_counts) {
4469
+ throw std::invalid_argument("rect set exact fragments received null input pointer");
4470
+ }
4471
+ if (max_rects == 0 || max_rects > kMaxRectSetRects || max_fragments == 0) {
4472
+ throw std::invalid_argument("invalid rect set exact limits");
4473
+ }
4474
+ DeviceBuffer<std::int64_t> d_old_rects(task_count * max_rects * 4);
4475
+ DeviceBuffer<std::int64_t> d_new_rects(task_count * max_rects * 4);
4476
+ DeviceBuffer<std::uint8_t> d_old_counts(task_count);
4477
+ DeviceBuffer<std::uint8_t> d_new_counts(task_count);
4478
+ DeviceBuffer<std::int64_t> d_out_rects(task_count * 2 * max_fragments * 4);
4479
+ DeviceBuffer<std::uint16_t> d_out_counts(task_count * 2);
4480
+ DeviceBuffer<std::int64_t> d_out_areas(task_count * 3);
4481
+ DeviceBuffer<std::uint32_t> d_errors(task_count);
4482
+ check_cuda(cudaMemcpy(d_old_rects.get(), old_rects, sizeof(std::int64_t) * task_count * max_rects * 4, cudaMemcpyHostToDevice), "copy old rect set rects");
4483
+ check_cuda(cudaMemcpy(d_new_rects.get(), new_rects, sizeof(std::int64_t) * task_count * max_rects * 4, cudaMemcpyHostToDevice), "copy new rect set rects");
4484
+ check_cuda(cudaMemcpy(d_old_counts.get(), old_counts, sizeof(std::uint8_t) * task_count, cudaMemcpyHostToDevice), "copy old rect set counts");
4485
+ check_cuda(cudaMemcpy(d_new_counts.get(), new_counts, sizeof(std::uint8_t) * task_count, cudaMemcpyHostToDevice), "copy new rect set counts");
4486
+ check_cuda(cudaMemset(d_out_rects.get(), 0, sizeof(std::int64_t) * task_count * 2 * max_fragments * 4), "clear rect set fragments");
4487
+ check_cuda(cudaMemset(d_out_counts.get(), 0, sizeof(std::uint16_t) * task_count * 2), "clear rect set counts");
4488
+ check_cuda(cudaMemset(d_out_areas.get(), 0, sizeof(std::int64_t) * task_count * 3), "clear rect set areas");
4489
+ check_cuda(cudaMemset(d_errors.get(), 0, sizeof(std::uint32_t) * task_count), "clear rect set errors");
4490
+ const int threads = 128;
4491
+ const int blocks = static_cast<int>((task_count + threads - 1) / threads);
4492
+ rect_set_exact_fragments_kernel<<<blocks, threads>>>(
4493
+ d_old_rects.get(),
4494
+ d_new_rects.get(),
4495
+ d_old_counts.get(),
4496
+ d_new_counts.get(),
4497
+ task_count,
4498
+ max_rects,
4499
+ max_fragments,
4500
+ d_out_rects.get(),
4501
+ d_out_counts.get(),
4502
+ d_out_areas.get(),
4503
+ d_errors.get());
4504
+ check_cuda(cudaGetLastError(), "launch rect set exact fragment kernel");
4505
+ check_cuda(cudaDeviceSynchronize(), "run rect set exact fragment kernel");
4506
+ const auto max_error = thrust::reduce(
4507
+ thrust::device_pointer_cast(d_errors.get()),
4508
+ thrust::device_pointer_cast(d_errors.get()) + task_count,
4509
+ std::uint32_t{0},
4510
+ thrust::maximum<std::uint32_t>{});
4511
+ if (max_error) {
4512
+ throw std::runtime_error(max_error == 1 ? "rect set exact task exceeds supported rectangle limit" : "rect set exact task exceeds fragment limit");
4513
+ }
4514
+ check_cuda(cudaMemcpy(out_rects, d_out_rects.get(), sizeof(std::int64_t) * task_count * 2 * max_fragments * 4, cudaMemcpyDeviceToHost), "copy rect set exact fragments");
4515
+ check_cuda(cudaMemcpy(out_counts, d_out_counts.get(), sizeof(std::uint16_t) * task_count * 2, cudaMemcpyDeviceToHost), "copy rect set exact counts");
4516
+ check_cuda(cudaMemcpy(out_twice_areas, d_out_areas.get(), sizeof(std::int64_t) * task_count * 3, cudaMemcpyDeviceToHost), "copy rect set exact areas");
4517
+ g_last_error.clear();
4518
+ return 0;
4519
+ } catch (const std::exception& exc) {
4520
+ g_last_error = exc.what();
4521
+ return 1;
4522
+ } catch (...) {
4523
+ g_last_error = "unknown CUDA rect set exact fragment error";
4524
+ return 1;
4525
+ }
4526
+ }
4527
+
4528
+ } // extern "C"