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.
- gdsdiff/__init__.py +148 -0
- gdsdiff/__main__.py +7 -0
- gdsdiff/_environment.py +12 -0
- gdsdiff/_version.py +34 -0
- gdsdiff/acceptance_evidence.py +530 -0
- gdsdiff/api.py +2955 -0
- gdsdiff/backends/__init__.py +26 -0
- gdsdiff/backends/base.py +77 -0
- gdsdiff/backends/boundary_loop_ctypes.py +191 -0
- gdsdiff/backends/cpu.py +20 -0
- gdsdiff/backends/cpu_ctypes.py +255 -0
- gdsdiff/backends/cuda.py +40 -0
- gdsdiff/backends/cuda_ctypes.py +1297 -0
- gdsdiff/backends/exact.py +424 -0
- gdsdiff/backends/geometry_ctypes.py +252 -0
- gdsdiff/backends/identity.py +53 -0
- gdsdiff/backends/metal.py +198 -0
- gdsdiff/backends/metal_ctypes.py +866 -0
- gdsdiff/backends/polygon_extract_ctypes.py +233 -0
- gdsdiff/backends/structural_ctypes.py +130 -0
- gdsdiff/boundary_loops.py +616 -0
- gdsdiff/cache.py +544 -0
- gdsdiff/canonicalize.py +176 -0
- gdsdiff/cli.py +352 -0
- gdsdiff/config.py +257 -0
- gdsdiff/cuda_setup.py +174 -0
- gdsdiff/design_history.py +65 -0
- gdsdiff/diagnostics.py +42 -0
- gdsdiff/diff_gds.py +66 -0
- gdsdiff/exact_diff_markdown.py +979 -0
- gdsdiff/exact_oracle.py +649 -0
- gdsdiff/extract.py +376 -0
- gdsdiff/gds_metadata.py +43 -0
- gdsdiff/geometry.py +112 -0
- gdsdiff/grid.py +143 -0
- gdsdiff/hierarchy.py +215 -0
- gdsdiff/hierarchy_extract.py +263 -0
- gdsdiff/inventory.py +153 -0
- gdsdiff/multiprocessing_support.py +39 -0
- gdsdiff/native.py +135 -0
- gdsdiff/native_cache.py +265 -0
- gdsdiff/native_src/cpu_prefilter.cpp +349 -0
- gdsdiff/native_src/gds_boundary_loops.cpp +505 -0
- gdsdiff/native_src/gds_geometry_scan.cpp +601 -0
- gdsdiff/native_src/gds_polygon_extract.cpp +594 -0
- gdsdiff/native_src/gds_structural_scan.cpp +335 -0
- gdsdiff/native_src/gdsdiff/CMakeLists.txt +74 -0
- gdsdiff/native_src/gdsdiff/core.cpp +191 -0
- gdsdiff/native_src/gdsdiff/core.hpp +96 -0
- gdsdiff/native_src/gdsdiff/cuda_assignment.cu +304 -0
- gdsdiff/native_src/gdsdiff/cuda_assignment.cuh +33 -0
- gdsdiff/native_src/gdsdiff/cuda_candidates.cu +133 -0
- gdsdiff/native_src/gdsdiff/cuda_candidates.cuh +17 -0
- gdsdiff/native_src/gdsdiff/cuda_ctypes.cu +4528 -0
- gdsdiff/native_src/gdsdiff/cuda_memory.cu +194 -0
- gdsdiff/native_src/gdsdiff/cuda_memory.cuh +34 -0
- gdsdiff/native_src/gdsdiff/metal_ctypes.mm +2791 -0
- gdsdiff/native_src/gdsdiff/python_bindings.cpp +21 -0
- gdsdiff/native_src/gdsdiff/test_core.cpp +93 -0
- gdsdiff/native_src/gdsdiff/test_cuda_assignment.cu +109 -0
- gdsdiff/native_src/gdsdiff/test_cuda_candidates.cu +94 -0
- gdsdiff/native_src/gdsdiff/test_cuda_memory.cu +97 -0
- gdsdiff/policy.py +305 -0
- gdsdiff/polygon_components.py +860 -0
- gdsdiff/profiles.py +152 -0
- gdsdiff/py.typed +1 -0
- gdsdiff/rect_coverage.py +384 -0
- gdsdiff/report.py +354 -0
- gdsdiff/schemas/gdsdiff_report-v1.schema.json +92 -0
- gdsdiff/semantics.py +75 -0
- gdsdiff/source_edge_diff.py +1120 -0
- gdsdiff/spatial.py +71 -0
- gdsdiff/structural_guard.py +161 -0
- gdsdiff/surplus_coverage.py +255 -0
- gdsdiff/synthetic_suite.py +1643 -0
- gdsdiff/templates.py +709 -0
- gdsdiff/tiling.py +153 -0
- gdsdiff/windowed_exact.py +270 -0
- gdsdiff/windows.py +184 -0
- gdsdiff-0.1.0.dist-info/METADATA +135 -0
- gdsdiff-0.1.0.dist-info/RECORD +85 -0
- gdsdiff-0.1.0.dist-info/WHEEL +5 -0
- gdsdiff-0.1.0.dist-info/entry_points.txt +4 -0
- gdsdiff-0.1.0.dist-info/licenses/LICENSE +674 -0
- gdsdiff-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2791 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <Metal/Metal.h>
|
|
3
|
+
|
|
4
|
+
#include <atomic>
|
|
5
|
+
#include <algorithm>
|
|
6
|
+
#include <cstdint>
|
|
7
|
+
#include <exception>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <cstdlib>
|
|
10
|
+
#include <fstream>
|
|
11
|
+
#include <mutex>
|
|
12
|
+
#include <limits>
|
|
13
|
+
#include <stdexcept>
|
|
14
|
+
#include <string>
|
|
15
|
+
#include <tuple>
|
|
16
|
+
#include <vector>
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
|
|
20
|
+
thread_local std::string g_last_error;
|
|
21
|
+
std::mutex g_mutex;
|
|
22
|
+
id<MTLDevice> g_device = nil;
|
|
23
|
+
id<MTLCommandQueue> g_queue = nil;
|
|
24
|
+
id<MTLLibrary> g_library = nil;
|
|
25
|
+
id<MTLComputePipelineState> g_byte_compare_pipeline = nil;
|
|
26
|
+
id<MTLComputePipelineState> g_tile_count_pipeline = nil;
|
|
27
|
+
id<MTLComputePipelineState> g_tile_emit_pipeline = nil;
|
|
28
|
+
id<MTLComputePipelineState> g_rect_set_pipeline = nil;
|
|
29
|
+
id<MTLComputePipelineState> g_record_delta_pipeline_old = nil;
|
|
30
|
+
id<MTLComputePipelineState> g_record_delta_pipeline_new = nil;
|
|
31
|
+
id<MTLComputePipelineState> g_record_key_from_hash_pipeline = nil;
|
|
32
|
+
id<MTLComputePipelineState> g_context_overlap_pipeline = nil;
|
|
33
|
+
id<MTLComputePipelineState> g_one_sided_boundary_pipeline = nil;
|
|
34
|
+
id<MTLComputePipelineState> g_mixed_boundary_pipeline = nil;
|
|
35
|
+
id<MTLComputePipelineState> g_boundary_loop_next_pipeline = nil;
|
|
36
|
+
id<MTLComputePipelineState> g_boundary_loop_trace_pipeline = nil;
|
|
37
|
+
|
|
38
|
+
constexpr const char* kMetalSource = R"METAL(
|
|
39
|
+
#include <metal_stdlib>
|
|
40
|
+
using namespace metal;
|
|
41
|
+
|
|
42
|
+
struct ByteCompareParams {
|
|
43
|
+
ulong byte_count;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
static inline long metal_floor_div(long value, long divisor) {
|
|
47
|
+
return static_cast<long>(floor(static_cast<float>(value) / static_cast<float>(divisor)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
kernel void byte_compare(
|
|
51
|
+
device const uchar* old_data [[buffer(0)]],
|
|
52
|
+
device const uchar* new_data [[buffer(1)]],
|
|
53
|
+
device atomic_uint* mismatch [[buffer(2)]],
|
|
54
|
+
constant ByteCompareParams& params [[buffer(3)]],
|
|
55
|
+
uint gid [[thread_position_in_grid]]
|
|
56
|
+
) {
|
|
57
|
+
ulong index = gid;
|
|
58
|
+
if (index >= params.byte_count) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (old_data[index] != new_data[index]) {
|
|
62
|
+
atomic_store_explicit(mismatch, 1u, memory_order_relaxed);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
struct TileCountParams {
|
|
67
|
+
ulong polygon_count;
|
|
68
|
+
long tile_size;
|
|
69
|
+
ulong max_tiles_per_polygon;
|
|
70
|
+
uint side;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
kernel void tile_count(
|
|
74
|
+
device const long* bboxes [[buffer(0)]],
|
|
75
|
+
device const int* layers [[buffer(1)]],
|
|
76
|
+
device const int* datatypes [[buffer(2)]],
|
|
77
|
+
device const ulong* canonical_ids [[buffer(3)]],
|
|
78
|
+
device ulong* out_counts [[buffer(4)]],
|
|
79
|
+
device uchar* out_oversized_flags [[buffer(5)]],
|
|
80
|
+
device int* out_oversized_layers [[buffer(6)]],
|
|
81
|
+
device int* out_oversized_datatypes [[buffer(7)]],
|
|
82
|
+
device ulong* out_oversized_canonical_ids [[buffer(8)]],
|
|
83
|
+
device long* out_oversized_tile_bboxes [[buffer(9)]],
|
|
84
|
+
device uint* out_errors [[buffer(10)]],
|
|
85
|
+
constant TileCountParams& params [[buffer(11)]],
|
|
86
|
+
uint gid [[thread_position_in_grid]]
|
|
87
|
+
) {
|
|
88
|
+
ulong index = gid;
|
|
89
|
+
if (index >= params.polygon_count) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
long x0 = bboxes[index * 4];
|
|
93
|
+
long y0 = bboxes[index * 4 + 1];
|
|
94
|
+
long x1 = bboxes[index * 4 + 2];
|
|
95
|
+
long y1 = bboxes[index * 4 + 3];
|
|
96
|
+
long tx0 = metal_floor_div(x0, params.tile_size);
|
|
97
|
+
long ty0 = metal_floor_div(y0, params.tile_size);
|
|
98
|
+
long tx1 = metal_floor_div(x1, params.tile_size);
|
|
99
|
+
long ty1 = metal_floor_div(y1, params.tile_size);
|
|
100
|
+
long span_x = tx1 - tx0 + 1;
|
|
101
|
+
long span_y = ty1 - ty0 + 1;
|
|
102
|
+
if (span_x <= 0 || span_y <= 0) {
|
|
103
|
+
out_errors[index] = 1;
|
|
104
|
+
out_counts[index] = 0;
|
|
105
|
+
out_oversized_flags[index] = 0;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
ulong span_x_u = static_cast<ulong>(span_x);
|
|
109
|
+
ulong span_y_u = static_cast<ulong>(span_y);
|
|
110
|
+
ulong max_ulong = ~static_cast<ulong>(0);
|
|
111
|
+
if (span_y_u != 0 && span_x_u > max_ulong / span_y_u) {
|
|
112
|
+
out_errors[index] = 2;
|
|
113
|
+
out_counts[index] = 0;
|
|
114
|
+
out_oversized_flags[index] = 0;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
ulong count = span_x_u * span_y_u;
|
|
118
|
+
if (count > params.max_tiles_per_polygon) {
|
|
119
|
+
out_counts[index] = 0;
|
|
120
|
+
out_oversized_flags[index] = 1;
|
|
121
|
+
out_oversized_layers[index] = layers[index];
|
|
122
|
+
out_oversized_datatypes[index] = datatypes[index];
|
|
123
|
+
out_oversized_canonical_ids[index] = canonical_ids[index];
|
|
124
|
+
out_oversized_tile_bboxes[index * 4] = tx0;
|
|
125
|
+
out_oversized_tile_bboxes[index * 4 + 1] = ty0;
|
|
126
|
+
out_oversized_tile_bboxes[index * 4 + 2] = tx1;
|
|
127
|
+
out_oversized_tile_bboxes[index * 4 + 3] = ty1;
|
|
128
|
+
} else {
|
|
129
|
+
out_counts[index] = count;
|
|
130
|
+
out_oversized_flags[index] = 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
struct TileEmitParams {
|
|
135
|
+
ulong polygon_count;
|
|
136
|
+
long tile_size;
|
|
137
|
+
uint side;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
kernel void tile_emit(
|
|
141
|
+
device const long* bboxes [[buffer(0)]],
|
|
142
|
+
device const int* layers [[buffer(1)]],
|
|
143
|
+
device const int* datatypes [[buffer(2)]],
|
|
144
|
+
device const ulong* canonical_ids [[buffer(3)]],
|
|
145
|
+
device const ulong* offsets [[buffer(4)]],
|
|
146
|
+
device const uchar* oversized_flags [[buffer(5)]],
|
|
147
|
+
device int* out_layers [[buffer(6)]],
|
|
148
|
+
device int* out_datatypes [[buffer(7)]],
|
|
149
|
+
device long* out_tx [[buffer(8)]],
|
|
150
|
+
device long* out_ty [[buffer(9)]],
|
|
151
|
+
device ulong* out_canonical_ids [[buffer(10)]],
|
|
152
|
+
device uchar* out_sides [[buffer(11)]],
|
|
153
|
+
constant TileEmitParams& params [[buffer(12)]],
|
|
154
|
+
uint gid [[thread_position_in_grid]]
|
|
155
|
+
) {
|
|
156
|
+
ulong index = gid;
|
|
157
|
+
if (index >= params.polygon_count || oversized_flags[index]) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
long tx0 = metal_floor_div(bboxes[index * 4], params.tile_size);
|
|
161
|
+
long ty0 = metal_floor_div(bboxes[index * 4 + 1], params.tile_size);
|
|
162
|
+
long tx1 = metal_floor_div(bboxes[index * 4 + 2], params.tile_size);
|
|
163
|
+
long ty1 = metal_floor_div(bboxes[index * 4 + 3], params.tile_size);
|
|
164
|
+
ulong out = offsets[index];
|
|
165
|
+
for (long tx = tx0; tx <= tx1; ++tx) {
|
|
166
|
+
for (long ty = ty0; ty <= ty1; ++ty) {
|
|
167
|
+
out_layers[out] = layers[index];
|
|
168
|
+
out_datatypes[out] = datatypes[index];
|
|
169
|
+
out_tx[out] = tx;
|
|
170
|
+
out_ty[out] = ty;
|
|
171
|
+
out_canonical_ids[out] = canonical_ids[index];
|
|
172
|
+
out_sides[out] = static_cast<uchar>(params.side);
|
|
173
|
+
out += 1;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
struct Params {
|
|
179
|
+
ulong task_count;
|
|
180
|
+
ushort max_rects;
|
|
181
|
+
ushort max_fragments;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
static inline void add_break(thread long* values, thread ushort& count, long value) {
|
|
185
|
+
values[count] = value;
|
|
186
|
+
count += 1;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static inline ushort sort_unique(thread long* values, ushort count) {
|
|
190
|
+
for (ushort i = 1; i < count; ++i) {
|
|
191
|
+
long item = values[i];
|
|
192
|
+
ushort j = i;
|
|
193
|
+
while (j > 0 && values[j - 1] > item) {
|
|
194
|
+
values[j] = values[j - 1];
|
|
195
|
+
j -= 1;
|
|
196
|
+
}
|
|
197
|
+
values[j] = item;
|
|
198
|
+
}
|
|
199
|
+
if (count == 0) {
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
ushort out = 1;
|
|
203
|
+
for (ushort i = 1; i < count; ++i) {
|
|
204
|
+
if (values[i] != values[out - 1]) {
|
|
205
|
+
values[out] = values[i];
|
|
206
|
+
out += 1;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static inline bool valid_rect(device const long* rects, ulong base) {
|
|
213
|
+
return rects[base] < rects[base + 2] && rects[base + 1] < rects[base + 3];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static inline bool cell_inside_any(
|
|
217
|
+
long x0,
|
|
218
|
+
long y0,
|
|
219
|
+
long x1,
|
|
220
|
+
long y1,
|
|
221
|
+
device const long* rects,
|
|
222
|
+
ushort count,
|
|
223
|
+
ushort max_rects,
|
|
224
|
+
ulong task
|
|
225
|
+
) {
|
|
226
|
+
for (ushort i = 0; i < count; ++i) {
|
|
227
|
+
ulong base = (task * max_rects + i) * 4;
|
|
228
|
+
if (!valid_rect(rects, base)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (x0 >= rects[base] && x1 <= rects[base + 2] && y0 >= rects[base + 1] && y1 <= rects[base + 3]) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
static inline void emit_cell(
|
|
239
|
+
long x0,
|
|
240
|
+
long y0,
|
|
241
|
+
long x1,
|
|
242
|
+
long y1,
|
|
243
|
+
ulong task,
|
|
244
|
+
ushort side,
|
|
245
|
+
ushort max_fragments,
|
|
246
|
+
device long* out_rects,
|
|
247
|
+
thread ushort& count,
|
|
248
|
+
thread long& twice_area,
|
|
249
|
+
device atomic_uint* error
|
|
250
|
+
) {
|
|
251
|
+
long area = (x1 - x0) * (y1 - y0) * 2;
|
|
252
|
+
twice_area += area;
|
|
253
|
+
if (count >= max_fragments) {
|
|
254
|
+
atomic_store_explicit(error, 1u, memory_order_relaxed);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
ulong base = ((task * 2 + side) * max_fragments + count) * 4;
|
|
258
|
+
out_rects[base] = x0;
|
|
259
|
+
out_rects[base + 1] = y0;
|
|
260
|
+
out_rects[base + 2] = x1;
|
|
261
|
+
out_rects[base + 3] = y1;
|
|
262
|
+
count += 1;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static inline void diff_side(
|
|
266
|
+
device const long* source_rects,
|
|
267
|
+
device const long* cutter_rects,
|
|
268
|
+
ushort source_count,
|
|
269
|
+
ushort cutter_count,
|
|
270
|
+
ushort max_rects,
|
|
271
|
+
ushort max_fragments,
|
|
272
|
+
ulong task,
|
|
273
|
+
ushort side,
|
|
274
|
+
device long* out_rects,
|
|
275
|
+
device ushort* out_counts,
|
|
276
|
+
device long* out_areas,
|
|
277
|
+
device atomic_uint* error
|
|
278
|
+
) {
|
|
279
|
+
thread long xs[64];
|
|
280
|
+
thread long ys[64];
|
|
281
|
+
ushort x_count = 0;
|
|
282
|
+
ushort y_count = 0;
|
|
283
|
+
for (ushort i = 0; i < source_count; ++i) {
|
|
284
|
+
ulong base = (task * max_rects + i) * 4;
|
|
285
|
+
if (!valid_rect(source_rects, base)) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
add_break(xs, x_count, source_rects[base]);
|
|
289
|
+
add_break(xs, x_count, source_rects[base + 2]);
|
|
290
|
+
add_break(ys, y_count, source_rects[base + 1]);
|
|
291
|
+
add_break(ys, y_count, source_rects[base + 3]);
|
|
292
|
+
}
|
|
293
|
+
for (ushort i = 0; i < cutter_count; ++i) {
|
|
294
|
+
ulong base = (task * max_rects + i) * 4;
|
|
295
|
+
if (!valid_rect(cutter_rects, base)) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
add_break(xs, x_count, cutter_rects[base]);
|
|
299
|
+
add_break(xs, x_count, cutter_rects[base + 2]);
|
|
300
|
+
add_break(ys, y_count, cutter_rects[base + 1]);
|
|
301
|
+
add_break(ys, y_count, cutter_rects[base + 3]);
|
|
302
|
+
}
|
|
303
|
+
x_count = sort_unique(xs, x_count);
|
|
304
|
+
y_count = sort_unique(ys, y_count);
|
|
305
|
+
ushort emitted = 0;
|
|
306
|
+
long twice_area = 0;
|
|
307
|
+
if (x_count >= 2 && y_count >= 2) {
|
|
308
|
+
for (ushort xi = 0; xi + 1 < x_count; ++xi) {
|
|
309
|
+
long x0 = xs[xi];
|
|
310
|
+
long x1 = xs[xi + 1];
|
|
311
|
+
if (x0 == x1) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
for (ushort yi = 0; yi + 1 < y_count; ++yi) {
|
|
315
|
+
long y0 = ys[yi];
|
|
316
|
+
long y1 = ys[yi + 1];
|
|
317
|
+
if (y0 == y1) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
bool in_source = cell_inside_any(x0, y0, x1, y1, source_rects, source_count, max_rects, task);
|
|
321
|
+
bool in_cutter = cell_inside_any(x0, y0, x1, y1, cutter_rects, cutter_count, max_rects, task);
|
|
322
|
+
if (in_source && !in_cutter) {
|
|
323
|
+
emit_cell(x0, y0, x1, y1, task, side, max_fragments, out_rects, emitted, twice_area, error);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
out_counts[task * 2 + side] = emitted;
|
|
329
|
+
out_areas[task * 3 + side] = twice_area;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
kernel void rect_set_exact(
|
|
333
|
+
device const long* old_rects [[buffer(0)]],
|
|
334
|
+
device const long* new_rects [[buffer(1)]],
|
|
335
|
+
device const uchar* old_counts [[buffer(2)]],
|
|
336
|
+
device const uchar* new_counts [[buffer(3)]],
|
|
337
|
+
device long* out_rects [[buffer(4)]],
|
|
338
|
+
device ushort* out_counts [[buffer(5)]],
|
|
339
|
+
device long* out_areas [[buffer(6)]],
|
|
340
|
+
device atomic_uint* error [[buffer(7)]],
|
|
341
|
+
constant Params& params [[buffer(8)]],
|
|
342
|
+
uint gid [[thread_position_in_grid]]
|
|
343
|
+
) {
|
|
344
|
+
ulong task = gid;
|
|
345
|
+
if (task >= params.task_count) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
ushort old_count = min(static_cast<ushort>(old_counts[task]), params.max_rects);
|
|
349
|
+
ushort new_count = min(static_cast<ushort>(new_counts[task]), params.max_rects);
|
|
350
|
+
diff_side(old_rects, new_rects, old_count, new_count, params.max_rects, params.max_fragments, task, 0, out_rects, out_counts, out_areas, error);
|
|
351
|
+
diff_side(new_rects, old_rects, new_count, old_count, params.max_rects, params.max_fragments, task, 1, out_rects, out_counts, out_areas, error);
|
|
352
|
+
out_areas[task * 3 + 2] = out_areas[task * 3] + out_areas[task * 3 + 1];
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
struct RecordParams {
|
|
356
|
+
ulong old_count;
|
|
357
|
+
ulong new_count;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
static inline ulong vertex_count(device const ulong* offsets, ulong index) {
|
|
361
|
+
return offsets[index + 1] - offsets[index];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
static inline bool record_key_equal(
|
|
365
|
+
device const ulong* left_offsets,
|
|
366
|
+
device const int* left_layers,
|
|
367
|
+
device const int* left_datatypes,
|
|
368
|
+
device const long* left_bboxes,
|
|
369
|
+
device const uint* left_flags,
|
|
370
|
+
device const ulong* left_hashes,
|
|
371
|
+
ulong left_index,
|
|
372
|
+
device const ulong* right_offsets,
|
|
373
|
+
device const int* right_layers,
|
|
374
|
+
device const int* right_datatypes,
|
|
375
|
+
device const long* right_bboxes,
|
|
376
|
+
device const uint* right_flags,
|
|
377
|
+
device const ulong* right_hashes,
|
|
378
|
+
ulong right_index
|
|
379
|
+
) {
|
|
380
|
+
if (left_layers[left_index] != right_layers[right_index]) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
if (left_datatypes[left_index] != right_datatypes[right_index]) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
if (left_flags[left_index] != right_flags[right_index]) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
if (vertex_count(left_offsets, left_index) != vertex_count(right_offsets, right_index)) {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
for (ushort offset = 0; offset < 4; ++offset) {
|
|
393
|
+
if (left_bboxes[left_index * 4 + offset] != right_bboxes[right_index * 4 + offset]) {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return left_hashes[left_index * 2] == right_hashes[right_index * 2]
|
|
398
|
+
&& left_hashes[left_index * 2 + 1] == right_hashes[right_index * 2 + 1];
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
static inline bool first_in_side(
|
|
402
|
+
device const ulong* offsets,
|
|
403
|
+
device const int* layers,
|
|
404
|
+
device const int* datatypes,
|
|
405
|
+
device const long* bboxes,
|
|
406
|
+
device const uint* flags,
|
|
407
|
+
device const ulong* hashes,
|
|
408
|
+
ulong index
|
|
409
|
+
) {
|
|
410
|
+
for (ulong other = 0; other < index; ++other) {
|
|
411
|
+
if (record_key_equal(offsets, layers, datatypes, bboxes, flags, hashes, index,
|
|
412
|
+
offsets, layers, datatypes, bboxes, flags, hashes, other)) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
static inline ulong count_same(
|
|
420
|
+
device const ulong* source_offsets,
|
|
421
|
+
device const int* source_layers,
|
|
422
|
+
device const int* source_datatypes,
|
|
423
|
+
device const long* source_bboxes,
|
|
424
|
+
device const uint* source_flags,
|
|
425
|
+
device const ulong* source_hashes,
|
|
426
|
+
ulong source_index,
|
|
427
|
+
device const ulong* target_offsets,
|
|
428
|
+
device const int* target_layers,
|
|
429
|
+
device const int* target_datatypes,
|
|
430
|
+
device const long* target_bboxes,
|
|
431
|
+
device const uint* target_flags,
|
|
432
|
+
device const ulong* target_hashes,
|
|
433
|
+
ulong target_count
|
|
434
|
+
) {
|
|
435
|
+
ulong total = 0;
|
|
436
|
+
for (ulong other = 0; other < target_count; ++other) {
|
|
437
|
+
if (record_key_equal(source_offsets, source_layers, source_datatypes, source_bboxes, source_flags, source_hashes, source_index,
|
|
438
|
+
target_offsets, target_layers, target_datatypes, target_bboxes, target_flags, target_hashes, other)) {
|
|
439
|
+
total += 1;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return total;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
static inline void emit_record_delta(
|
|
446
|
+
uchar direction,
|
|
447
|
+
ulong surplus,
|
|
448
|
+
ulong representative,
|
|
449
|
+
device const ulong* offsets,
|
|
450
|
+
device const int* layers,
|
|
451
|
+
device const int* datatypes,
|
|
452
|
+
device const long* bboxes,
|
|
453
|
+
device const uint* flags,
|
|
454
|
+
device const ulong* hashes,
|
|
455
|
+
device atomic_uint* record_counter,
|
|
456
|
+
device int* out_layers,
|
|
457
|
+
device int* out_datatypes,
|
|
458
|
+
device uchar* out_directions,
|
|
459
|
+
device ulong* out_counts,
|
|
460
|
+
device long* out_bboxes,
|
|
461
|
+
device ulong* out_vertex_counts,
|
|
462
|
+
device uint* out_flags,
|
|
463
|
+
device ulong* out_hashes,
|
|
464
|
+
device ulong* out_representative_indices
|
|
465
|
+
) {
|
|
466
|
+
uint row = atomic_fetch_add_explicit(record_counter, 1u, memory_order_relaxed);
|
|
467
|
+
out_layers[row] = layers[representative];
|
|
468
|
+
out_datatypes[row] = datatypes[representative];
|
|
469
|
+
out_directions[row] = direction;
|
|
470
|
+
out_counts[row] = surplus;
|
|
471
|
+
for (ushort offset = 0; offset < 4; ++offset) {
|
|
472
|
+
out_bboxes[row * 4 + offset] = bboxes[representative * 4 + offset];
|
|
473
|
+
}
|
|
474
|
+
out_vertex_counts[row] = vertex_count(offsets, representative);
|
|
475
|
+
out_flags[row] = flags[representative];
|
|
476
|
+
out_hashes[row * 2] = hashes[representative * 2];
|
|
477
|
+
out_hashes[row * 2 + 1] = hashes[representative * 2 + 1];
|
|
478
|
+
out_representative_indices[row] = representative;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
kernel void record_delta_old(
|
|
482
|
+
device const ulong* old_offsets [[buffer(0)]],
|
|
483
|
+
device const int* old_layers [[buffer(1)]],
|
|
484
|
+
device const int* old_datatypes [[buffer(2)]],
|
|
485
|
+
device const long* old_bboxes [[buffer(3)]],
|
|
486
|
+
device const uint* old_flags [[buffer(4)]],
|
|
487
|
+
device const ulong* old_hashes [[buffer(5)]],
|
|
488
|
+
device const ulong* new_offsets [[buffer(6)]],
|
|
489
|
+
device const int* new_layers [[buffer(7)]],
|
|
490
|
+
device const int* new_datatypes [[buffer(8)]],
|
|
491
|
+
device const long* new_bboxes [[buffer(9)]],
|
|
492
|
+
device const uint* new_flags [[buffer(10)]],
|
|
493
|
+
device const ulong* new_hashes [[buffer(11)]],
|
|
494
|
+
device int* out_layers [[buffer(12)]],
|
|
495
|
+
device int* out_datatypes [[buffer(13)]],
|
|
496
|
+
device uchar* out_directions [[buffer(14)]],
|
|
497
|
+
device ulong* out_counts [[buffer(15)]],
|
|
498
|
+
device long* out_bboxes [[buffer(16)]],
|
|
499
|
+
device ulong* out_vertex_counts [[buffer(17)]],
|
|
500
|
+
device uint* out_flags [[buffer(18)]],
|
|
501
|
+
device ulong* out_hashes [[buffer(19)]],
|
|
502
|
+
device ulong* out_representative_indices [[buffer(20)]],
|
|
503
|
+
device atomic_uint* record_counter [[buffer(21)]],
|
|
504
|
+
device atomic_uint* matched_counter [[buffer(22)]],
|
|
505
|
+
device atomic_uint* reduced_counter [[buffer(23)]],
|
|
506
|
+
constant RecordParams& params [[buffer(24)]],
|
|
507
|
+
uint gid [[thread_position_in_grid]]
|
|
508
|
+
) {
|
|
509
|
+
ulong index = gid;
|
|
510
|
+
if (index >= params.old_count) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (!first_in_side(old_offsets, old_layers, old_datatypes, old_bboxes, old_flags, old_hashes, index)) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
ulong old_total = count_same(old_offsets, old_layers, old_datatypes, old_bboxes, old_flags, old_hashes, index,
|
|
517
|
+
old_offsets, old_layers, old_datatypes, old_bboxes, old_flags, old_hashes, params.old_count);
|
|
518
|
+
ulong new_total = count_same(old_offsets, old_layers, old_datatypes, old_bboxes, old_flags, old_hashes, index,
|
|
519
|
+
new_offsets, new_layers, new_datatypes, new_bboxes, new_flags, new_hashes, params.new_count);
|
|
520
|
+
atomic_fetch_add_explicit(reduced_counter, 1u, memory_order_relaxed);
|
|
521
|
+
atomic_fetch_add_explicit(matched_counter, static_cast<uint>(min(old_total, new_total)), memory_order_relaxed);
|
|
522
|
+
if (old_total > new_total) {
|
|
523
|
+
emit_record_delta(1, old_total - new_total, index, old_offsets, old_layers, old_datatypes, old_bboxes, old_flags, old_hashes,
|
|
524
|
+
record_counter, out_layers, out_datatypes, out_directions, out_counts, out_bboxes,
|
|
525
|
+
out_vertex_counts, out_flags, out_hashes, out_representative_indices);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
kernel void record_delta_new(
|
|
530
|
+
device const ulong* old_offsets [[buffer(0)]],
|
|
531
|
+
device const int* old_layers [[buffer(1)]],
|
|
532
|
+
device const int* old_datatypes [[buffer(2)]],
|
|
533
|
+
device const long* old_bboxes [[buffer(3)]],
|
|
534
|
+
device const uint* old_flags [[buffer(4)]],
|
|
535
|
+
device const ulong* old_hashes [[buffer(5)]],
|
|
536
|
+
device const ulong* new_offsets [[buffer(6)]],
|
|
537
|
+
device const int* new_layers [[buffer(7)]],
|
|
538
|
+
device const int* new_datatypes [[buffer(8)]],
|
|
539
|
+
device const long* new_bboxes [[buffer(9)]],
|
|
540
|
+
device const uint* new_flags [[buffer(10)]],
|
|
541
|
+
device const ulong* new_hashes [[buffer(11)]],
|
|
542
|
+
device int* out_layers [[buffer(12)]],
|
|
543
|
+
device int* out_datatypes [[buffer(13)]],
|
|
544
|
+
device uchar* out_directions [[buffer(14)]],
|
|
545
|
+
device ulong* out_counts [[buffer(15)]],
|
|
546
|
+
device long* out_bboxes [[buffer(16)]],
|
|
547
|
+
device ulong* out_vertex_counts [[buffer(17)]],
|
|
548
|
+
device uint* out_flags [[buffer(18)]],
|
|
549
|
+
device ulong* out_hashes [[buffer(19)]],
|
|
550
|
+
device ulong* out_representative_indices [[buffer(20)]],
|
|
551
|
+
device atomic_uint* record_counter [[buffer(21)]],
|
|
552
|
+
device atomic_uint* matched_counter [[buffer(22)]],
|
|
553
|
+
device atomic_uint* reduced_counter [[buffer(23)]],
|
|
554
|
+
constant RecordParams& params [[buffer(24)]],
|
|
555
|
+
uint gid [[thread_position_in_grid]]
|
|
556
|
+
) {
|
|
557
|
+
ulong index = gid;
|
|
558
|
+
if (index >= params.new_count) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (!first_in_side(new_offsets, new_layers, new_datatypes, new_bboxes, new_flags, new_hashes, index)) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
ulong new_total = count_same(new_offsets, new_layers, new_datatypes, new_bboxes, new_flags, new_hashes, index,
|
|
565
|
+
new_offsets, new_layers, new_datatypes, new_bboxes, new_flags, new_hashes, params.new_count);
|
|
566
|
+
ulong old_total = count_same(new_offsets, new_layers, new_datatypes, new_bboxes, new_flags, new_hashes, index,
|
|
567
|
+
old_offsets, old_layers, old_datatypes, old_bboxes, old_flags, old_hashes, params.old_count);
|
|
568
|
+
if (old_total == 0) {
|
|
569
|
+
atomic_fetch_add_explicit(reduced_counter, 1u, memory_order_relaxed);
|
|
570
|
+
}
|
|
571
|
+
if (new_total > old_total) {
|
|
572
|
+
emit_record_delta(2, new_total - old_total, index, new_offsets, new_layers, new_datatypes, new_bboxes, new_flags, new_hashes,
|
|
573
|
+
record_counter, out_layers, out_datatypes, out_directions, out_counts, out_bboxes,
|
|
574
|
+
out_vertex_counts, out_flags, out_hashes, out_representative_indices);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
struct KeyParams {
|
|
579
|
+
ulong count;
|
|
580
|
+
ulong output_offset;
|
|
581
|
+
uint direction;
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
kernel void record_key_from_hash(
|
|
585
|
+
device const ulong* offsets [[buffer(0)]],
|
|
586
|
+
device const int* layers [[buffer(1)]],
|
|
587
|
+
device const int* datatypes [[buffer(2)]],
|
|
588
|
+
device const long* bboxes [[buffer(3)]],
|
|
589
|
+
device const uint* flags [[buffer(4)]],
|
|
590
|
+
device const ulong* hashes [[buffer(5)]],
|
|
591
|
+
device int* out_layers [[buffer(6)]],
|
|
592
|
+
device int* out_datatypes [[buffer(7)]],
|
|
593
|
+
device uchar* out_directions [[buffer(8)]],
|
|
594
|
+
device long* out_bboxes [[buffer(9)]],
|
|
595
|
+
device ulong* out_vertex_counts [[buffer(10)]],
|
|
596
|
+
device uint* out_flags [[buffer(11)]],
|
|
597
|
+
device ulong* out_hashes [[buffer(12)]],
|
|
598
|
+
device ulong* out_representative_indices [[buffer(13)]],
|
|
599
|
+
constant KeyParams& params [[buffer(14)]],
|
|
600
|
+
uint gid [[thread_position_in_grid]]
|
|
601
|
+
) {
|
|
602
|
+
ulong index = gid;
|
|
603
|
+
if (index >= params.count) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
ulong row = params.output_offset + index;
|
|
607
|
+
out_layers[row] = layers[index];
|
|
608
|
+
out_datatypes[row] = datatypes[index];
|
|
609
|
+
out_directions[row] = static_cast<uchar>(params.direction);
|
|
610
|
+
for (ushort offset = 0; offset < 4; ++offset) {
|
|
611
|
+
out_bboxes[row * 4 + offset] = bboxes[index * 4 + offset];
|
|
612
|
+
}
|
|
613
|
+
out_vertex_counts[row] = vertex_count(offsets, index);
|
|
614
|
+
out_flags[row] = flags[index];
|
|
615
|
+
out_hashes[row * 2] = hashes[index * 2];
|
|
616
|
+
out_hashes[row * 2 + 1] = hashes[index * 2 + 1];
|
|
617
|
+
out_representative_indices[row] = index;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
struct ContextParams {
|
|
621
|
+
ulong component_count;
|
|
622
|
+
ulong polygon_count;
|
|
623
|
+
ulong total_pairs;
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
kernel void context_overlap_flags(
|
|
627
|
+
device const long* component_bboxes [[buffer(0)]],
|
|
628
|
+
device const int* component_layers [[buffer(1)]],
|
|
629
|
+
device const int* component_datatypes [[buffer(2)]],
|
|
630
|
+
device const long* polygon_bboxes [[buffer(3)]],
|
|
631
|
+
device const int* polygon_layers [[buffer(4)]],
|
|
632
|
+
device const int* polygon_datatypes [[buffer(5)]],
|
|
633
|
+
device uchar* out_flags [[buffer(6)]],
|
|
634
|
+
constant ContextParams& params [[buffer(7)]],
|
|
635
|
+
uint gid [[thread_position_in_grid]]
|
|
636
|
+
) {
|
|
637
|
+
ulong linear = gid;
|
|
638
|
+
if (linear >= params.total_pairs) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
ulong component_index = linear / params.polygon_count;
|
|
642
|
+
ulong polygon_index = linear - component_index * params.polygon_count;
|
|
643
|
+
long cx0 = component_bboxes[component_index * 4];
|
|
644
|
+
long cy0 = component_bboxes[component_index * 4 + 1];
|
|
645
|
+
long cx1 = component_bboxes[component_index * 4 + 2];
|
|
646
|
+
long cy1 = component_bboxes[component_index * 4 + 3];
|
|
647
|
+
long px0 = polygon_bboxes[polygon_index * 4];
|
|
648
|
+
long py0 = polygon_bboxes[polygon_index * 4 + 1];
|
|
649
|
+
long px1 = polygon_bboxes[polygon_index * 4 + 2];
|
|
650
|
+
long py1 = polygon_bboxes[polygon_index * 4 + 3];
|
|
651
|
+
bool same_layer = component_layers[component_index] == polygon_layers[polygon_index]
|
|
652
|
+
&& component_datatypes[component_index] == polygon_datatypes[polygon_index];
|
|
653
|
+
bool overlaps = px1 > cx0 && cx1 > px0 && max(py0, cy0) < min(py1, cy1);
|
|
654
|
+
out_flags[linear] = same_layer && overlaps ? 1 : 0;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
struct BoundaryEdgeParams {
|
|
658
|
+
ulong vertex_count;
|
|
659
|
+
ulong polygon_count;
|
|
660
|
+
ulong segment_count;
|
|
661
|
+
uint direction;
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
static inline bool point_in_bbox_float(float x, float y, device const long* bbox) {
|
|
665
|
+
return x >= static_cast<float>(bbox[0]) && x <= static_cast<float>(bbox[2])
|
|
666
|
+
&& y >= static_cast<float>(bbox[1]) && y <= static_cast<float>(bbox[3]);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
static inline bool point_in_polygon_float(
|
|
670
|
+
float x,
|
|
671
|
+
float y,
|
|
672
|
+
device const long* vertices_xy,
|
|
673
|
+
device const ulong* polygon_offsets,
|
|
674
|
+
ulong polygon_index
|
|
675
|
+
) {
|
|
676
|
+
ulong start = polygon_offsets[polygon_index];
|
|
677
|
+
ulong stop = polygon_offsets[polygon_index + 1];
|
|
678
|
+
ulong vertex_count = stop - start;
|
|
679
|
+
if (vertex_count < 3) {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
bool inside = false;
|
|
683
|
+
for (ulong local = 0; local < vertex_count; ++local) {
|
|
684
|
+
ulong a = start + local;
|
|
685
|
+
ulong b = start + ((local + 1) % vertex_count);
|
|
686
|
+
float x0 = static_cast<float>(vertices_xy[a * 2]);
|
|
687
|
+
float y0 = static_cast<float>(vertices_xy[a * 2 + 1]);
|
|
688
|
+
float x1 = static_cast<float>(vertices_xy[b * 2]);
|
|
689
|
+
float y1 = static_cast<float>(vertices_xy[b * 2 + 1]);
|
|
690
|
+
if ((y0 > y) != (y1 > y)) {
|
|
691
|
+
float x_intersection = x0 + (y - y0) * (x1 - x0) / (y1 - y0);
|
|
692
|
+
if (x_intersection > x) {
|
|
693
|
+
inside = !inside;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return inside;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
static inline bool union_contains_float(
|
|
701
|
+
float x,
|
|
702
|
+
float y,
|
|
703
|
+
device const long* vertices_xy,
|
|
704
|
+
device const ulong* polygon_offsets,
|
|
705
|
+
device const long* bboxes,
|
|
706
|
+
ulong polygon_count
|
|
707
|
+
) {
|
|
708
|
+
for (ulong polygon_index = 0; polygon_index < polygon_count; ++polygon_index) {
|
|
709
|
+
device const long* bbox = bboxes + polygon_index * 4;
|
|
710
|
+
if (point_in_bbox_float(x, y, bbox)
|
|
711
|
+
&& point_in_polygon_float(x, y, vertices_xy, polygon_offsets, polygon_index)) {
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
static inline bool union_contains_side_float(
|
|
719
|
+
float x,
|
|
720
|
+
float y,
|
|
721
|
+
uchar side,
|
|
722
|
+
device const long* vertices_xy,
|
|
723
|
+
device const ulong* polygon_offsets,
|
|
724
|
+
device const long* bboxes,
|
|
725
|
+
device const uchar* polygon_sides,
|
|
726
|
+
ulong polygon_count
|
|
727
|
+
) {
|
|
728
|
+
for (ulong polygon_index = 0; polygon_index < polygon_count; ++polygon_index) {
|
|
729
|
+
if (polygon_sides[polygon_index] != side) {
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
device const long* bbox = bboxes + polygon_index * 4;
|
|
733
|
+
if (point_in_bbox_float(x, y, bbox)
|
|
734
|
+
&& point_in_polygon_float(x, y, vertices_xy, polygon_offsets, polygon_index)) {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
static inline bool directional_target_contains_float(
|
|
742
|
+
float x,
|
|
743
|
+
float y,
|
|
744
|
+
uchar direction,
|
|
745
|
+
device const long* vertices_xy,
|
|
746
|
+
device const ulong* polygon_offsets,
|
|
747
|
+
device const long* bboxes,
|
|
748
|
+
device const uchar* polygon_sides,
|
|
749
|
+
ulong polygon_count
|
|
750
|
+
) {
|
|
751
|
+
bool old_inside = union_contains_side_float(x, y, 1, vertices_xy, polygon_offsets, bboxes, polygon_sides, polygon_count);
|
|
752
|
+
bool new_inside = union_contains_side_float(x, y, 2, vertices_xy, polygon_offsets, bboxes, polygon_sides, polygon_count);
|
|
753
|
+
if (direction == 1) {
|
|
754
|
+
return old_inside && !new_inside;
|
|
755
|
+
}
|
|
756
|
+
return new_inside && !old_inside;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
static inline float boundary_sample_epsilon(float x, float y) {
|
|
760
|
+
return max(1e-3f, max(fabs(x), fabs(y)) * 1e-6f);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
kernel void one_sided_boundary_edges(
|
|
764
|
+
device const long* vertices_xy [[buffer(0)]],
|
|
765
|
+
device const ulong* polygon_offsets [[buffer(1)]],
|
|
766
|
+
device const long* bboxes [[buffer(2)]],
|
|
767
|
+
device const long* signed_twice_areas [[buffer(3)]],
|
|
768
|
+
device const float* segment_points [[buffer(4)]],
|
|
769
|
+
device const uint* segment_polygon_indices [[buffer(5)]],
|
|
770
|
+
device float* out_edges [[buffer(6)]],
|
|
771
|
+
device uchar* out_keep [[buffer(7)]],
|
|
772
|
+
constant BoundaryEdgeParams& params [[buffer(8)]],
|
|
773
|
+
uint gid [[thread_position_in_grid]]
|
|
774
|
+
) {
|
|
775
|
+
ulong index = gid;
|
|
776
|
+
if (index >= params.segment_count) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
out_keep[index] = 0;
|
|
780
|
+
ulong polygon_index = static_cast<ulong>(segment_polygon_indices[index]);
|
|
781
|
+
if (polygon_index >= params.polygon_count) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
long signed_area = signed_twice_areas[polygon_index];
|
|
785
|
+
if (signed_area == 0) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
float x0 = segment_points[index * 4];
|
|
789
|
+
float y0 = segment_points[index * 4 + 1];
|
|
790
|
+
float x1 = segment_points[index * 4 + 2];
|
|
791
|
+
float y1 = segment_points[index * 4 + 3];
|
|
792
|
+
float dx = x1 - x0;
|
|
793
|
+
float dy = y1 - y0;
|
|
794
|
+
float length = sqrt(dx * dx + dy * dy);
|
|
795
|
+
if (length <= 1e-12f) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
float normal_x = -dy / length;
|
|
799
|
+
float normal_y = dx / length;
|
|
800
|
+
float mid_x = (x0 + x1) * 0.5f;
|
|
801
|
+
float mid_y = (y0 + y1) * 0.5f;
|
|
802
|
+
float epsilon = boundary_sample_epsilon(mid_x, mid_y);
|
|
803
|
+
float sample_x = signed_area > 0 ? mid_x - normal_x * epsilon : mid_x + normal_x * epsilon;
|
|
804
|
+
float sample_y = signed_area > 0 ? mid_y - normal_y * epsilon : mid_y + normal_y * epsilon;
|
|
805
|
+
if (union_contains_float(sample_x, sample_y, vertices_xy, polygon_offsets, bboxes, params.polygon_count)) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (signed_area > 0) {
|
|
809
|
+
out_edges[index * 4] = x0;
|
|
810
|
+
out_edges[index * 4 + 1] = y0;
|
|
811
|
+
out_edges[index * 4 + 2] = x1;
|
|
812
|
+
out_edges[index * 4 + 3] = y1;
|
|
813
|
+
} else {
|
|
814
|
+
out_edges[index * 4] = x1;
|
|
815
|
+
out_edges[index * 4 + 1] = y1;
|
|
816
|
+
out_edges[index * 4 + 2] = x0;
|
|
817
|
+
out_edges[index * 4 + 3] = y0;
|
|
818
|
+
}
|
|
819
|
+
out_keep[index] = 1;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
kernel void mixed_boundary_edges(
|
|
823
|
+
device const long* vertices_xy [[buffer(0)]],
|
|
824
|
+
device const ulong* polygon_offsets [[buffer(1)]],
|
|
825
|
+
device const long* bboxes [[buffer(2)]],
|
|
826
|
+
device const uchar* polygon_sides [[buffer(3)]],
|
|
827
|
+
device const float* segment_points [[buffer(4)]],
|
|
828
|
+
device float* out_edges [[buffer(5)]],
|
|
829
|
+
device uchar* out_keep [[buffer(6)]],
|
|
830
|
+
constant BoundaryEdgeParams& params [[buffer(7)]],
|
|
831
|
+
uint gid [[thread_position_in_grid]]
|
|
832
|
+
) {
|
|
833
|
+
ulong index = gid;
|
|
834
|
+
if (index >= params.segment_count) {
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
out_keep[index] = 0;
|
|
838
|
+
float x0 = segment_points[index * 4];
|
|
839
|
+
float y0 = segment_points[index * 4 + 1];
|
|
840
|
+
float x1 = segment_points[index * 4 + 2];
|
|
841
|
+
float y1 = segment_points[index * 4 + 3];
|
|
842
|
+
float dx = x1 - x0;
|
|
843
|
+
float dy = y1 - y0;
|
|
844
|
+
float length = sqrt(dx * dx + dy * dy);
|
|
845
|
+
if (length <= 1e-12f) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
float normal_x = -dy / length;
|
|
849
|
+
float normal_y = dx / length;
|
|
850
|
+
float mid_x = (x0 + x1) * 0.5f;
|
|
851
|
+
float mid_y = (y0 + y1) * 0.5f;
|
|
852
|
+
float epsilon = boundary_sample_epsilon(mid_x, mid_y);
|
|
853
|
+
bool left_inside = directional_target_contains_float(
|
|
854
|
+
mid_x + normal_x * epsilon,
|
|
855
|
+
mid_y + normal_y * epsilon,
|
|
856
|
+
static_cast<uchar>(params.direction),
|
|
857
|
+
vertices_xy,
|
|
858
|
+
polygon_offsets,
|
|
859
|
+
bboxes,
|
|
860
|
+
polygon_sides,
|
|
861
|
+
params.polygon_count);
|
|
862
|
+
bool right_inside = directional_target_contains_float(
|
|
863
|
+
mid_x - normal_x * epsilon,
|
|
864
|
+
mid_y - normal_y * epsilon,
|
|
865
|
+
static_cast<uchar>(params.direction),
|
|
866
|
+
vertices_xy,
|
|
867
|
+
polygon_offsets,
|
|
868
|
+
bboxes,
|
|
869
|
+
polygon_sides,
|
|
870
|
+
params.polygon_count);
|
|
871
|
+
if (left_inside && !right_inside) {
|
|
872
|
+
out_edges[index * 4] = x0;
|
|
873
|
+
out_edges[index * 4 + 1] = y0;
|
|
874
|
+
out_edges[index * 4 + 2] = x1;
|
|
875
|
+
out_edges[index * 4 + 3] = y1;
|
|
876
|
+
out_keep[index] = 1;
|
|
877
|
+
} else if (right_inside && !left_inside) {
|
|
878
|
+
out_edges[index * 4] = x1;
|
|
879
|
+
out_edges[index * 4 + 1] = y1;
|
|
880
|
+
out_edges[index * 4 + 2] = x0;
|
|
881
|
+
out_edges[index * 4 + 3] = y0;
|
|
882
|
+
out_keep[index] = 1;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
struct DeviceLoopTraceStats {
|
|
887
|
+
ulong fragment_count;
|
|
888
|
+
ulong vertex_count;
|
|
889
|
+
ulong dropped_zero_area_loop_count;
|
|
890
|
+
uint error;
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
static inline bool loop_edge_less(device const long* edges, ulong left, ulong right) {
|
|
894
|
+
for (ushort axis = 0; axis < 4; ++axis) {
|
|
895
|
+
long left_value = edges[left * 4 + axis];
|
|
896
|
+
long right_value = edges[right * 4 + axis];
|
|
897
|
+
if (left_value != right_value) {
|
|
898
|
+
return left_value < right_value;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return false;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
static inline bool loop_point_less_xy(long ax, long ay, long bx, long by) {
|
|
905
|
+
return ax < bx || (ax == bx && ay < by);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
static inline bool loop_edge_start_matches(device const long* edges, ulong edge_index, long x, long y) {
|
|
909
|
+
return edges[edge_index * 4] == x && edges[edge_index * 4 + 1] == y;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
static inline bool loop_collinear(long ax, long ay, long bx, long by, long cx, long cy) {
|
|
913
|
+
long left = (bx - ax) * (cy - by);
|
|
914
|
+
long right = (by - ay) * (cx - bx);
|
|
915
|
+
return left == right;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
static inline void loop_turn_key(
|
|
919
|
+
long px,
|
|
920
|
+
long py,
|
|
921
|
+
long cx,
|
|
922
|
+
long cy,
|
|
923
|
+
long nx,
|
|
924
|
+
long ny,
|
|
925
|
+
thread int& turn_class,
|
|
926
|
+
thread ulong& abs_cross,
|
|
927
|
+
thread long& dot
|
|
928
|
+
) {
|
|
929
|
+
long in_x = cx - px;
|
|
930
|
+
long in_y = cy - py;
|
|
931
|
+
long out_x = nx - cx;
|
|
932
|
+
long out_y = ny - cy;
|
|
933
|
+
long cross = in_x * out_y - in_y * out_x;
|
|
934
|
+
dot = in_x * out_x + in_y * out_y;
|
|
935
|
+
if (cross < 0) {
|
|
936
|
+
turn_class = 0;
|
|
937
|
+
abs_cross = static_cast<ulong>(-cross);
|
|
938
|
+
} else if (cross == 0 && dot > 0) {
|
|
939
|
+
turn_class = 1;
|
|
940
|
+
abs_cross = 0;
|
|
941
|
+
} else if (cross > 0) {
|
|
942
|
+
turn_class = 2;
|
|
943
|
+
abs_cross = static_cast<ulong>(cross);
|
|
944
|
+
} else {
|
|
945
|
+
turn_class = 3;
|
|
946
|
+
abs_cross = 0;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
static inline bool loop_candidate_better(
|
|
951
|
+
long px,
|
|
952
|
+
long py,
|
|
953
|
+
long cx,
|
|
954
|
+
long cy,
|
|
955
|
+
long candidate_x,
|
|
956
|
+
long candidate_y,
|
|
957
|
+
long best_x,
|
|
958
|
+
long best_y
|
|
959
|
+
) {
|
|
960
|
+
int candidate_class = 0;
|
|
961
|
+
int best_class = 0;
|
|
962
|
+
ulong candidate_abs_cross = 0;
|
|
963
|
+
ulong best_abs_cross = 0;
|
|
964
|
+
long candidate_dot = 0;
|
|
965
|
+
long best_dot = 0;
|
|
966
|
+
loop_turn_key(px, py, cx, cy, candidate_x, candidate_y, candidate_class, candidate_abs_cross, candidate_dot);
|
|
967
|
+
loop_turn_key(px, py, cx, cy, best_x, best_y, best_class, best_abs_cross, best_dot);
|
|
968
|
+
if (candidate_class != best_class) {
|
|
969
|
+
return candidate_class < best_class;
|
|
970
|
+
}
|
|
971
|
+
if (candidate_abs_cross != best_abs_cross) {
|
|
972
|
+
return candidate_abs_cross > best_abs_cross;
|
|
973
|
+
}
|
|
974
|
+
if (candidate_dot != best_dot) {
|
|
975
|
+
return candidate_dot > best_dot;
|
|
976
|
+
}
|
|
977
|
+
return loop_point_less_xy(candidate_x, candidate_y, best_x, best_y);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
static inline ulong loop_clean_duplicates(device const long* input_points, ulong input_count, device long* output_points) {
|
|
981
|
+
ulong out_count = 0;
|
|
982
|
+
for (ulong index = 0; index < input_count; ++index) {
|
|
983
|
+
long x = input_points[index * 2];
|
|
984
|
+
long y = input_points[index * 2 + 1];
|
|
985
|
+
if (out_count == 0 || output_points[(out_count - 1) * 2] != x || output_points[(out_count - 1) * 2 + 1] != y) {
|
|
986
|
+
output_points[out_count * 2] = x;
|
|
987
|
+
output_points[out_count * 2 + 1] = y;
|
|
988
|
+
++out_count;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (out_count > 1 && output_points[0] == output_points[(out_count - 1) * 2] && output_points[1] == output_points[(out_count - 1) * 2 + 1]) {
|
|
992
|
+
--out_count;
|
|
993
|
+
}
|
|
994
|
+
return out_count;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
static inline ulong loop_clean_collinear_once(
|
|
998
|
+
device const long* input_points,
|
|
999
|
+
ulong input_count,
|
|
1000
|
+
device long* output_points,
|
|
1001
|
+
thread bool& changed
|
|
1002
|
+
) {
|
|
1003
|
+
changed = false;
|
|
1004
|
+
if (input_count < 3) {
|
|
1005
|
+
for (ulong index = 0; index < input_count; ++index) {
|
|
1006
|
+
output_points[index * 2] = input_points[index * 2];
|
|
1007
|
+
output_points[index * 2 + 1] = input_points[index * 2 + 1];
|
|
1008
|
+
}
|
|
1009
|
+
return input_count;
|
|
1010
|
+
}
|
|
1011
|
+
ulong out_count = 0;
|
|
1012
|
+
for (ulong index = 0; index < input_count; ++index) {
|
|
1013
|
+
ulong previous = (index + input_count - 1) % input_count;
|
|
1014
|
+
ulong following = (index + 1) % input_count;
|
|
1015
|
+
long ax = input_points[previous * 2];
|
|
1016
|
+
long ay = input_points[previous * 2 + 1];
|
|
1017
|
+
long bx = input_points[index * 2];
|
|
1018
|
+
long by = input_points[index * 2 + 1];
|
|
1019
|
+
long cx = input_points[following * 2];
|
|
1020
|
+
long cy = input_points[following * 2 + 1];
|
|
1021
|
+
if (loop_collinear(ax, ay, bx, by, cx, cy)) {
|
|
1022
|
+
changed = true;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
output_points[out_count * 2] = bx;
|
|
1026
|
+
output_points[out_count * 2 + 1] = by;
|
|
1027
|
+
++out_count;
|
|
1028
|
+
}
|
|
1029
|
+
return out_count;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
static inline long loop_twice_area(device const long* points, ulong count) {
|
|
1033
|
+
long area = 0;
|
|
1034
|
+
for (ulong index = 0; index < count; ++index) {
|
|
1035
|
+
ulong next = (index + 1) % count;
|
|
1036
|
+
area += points[index * 2] * points[next * 2 + 1] - points[next * 2] * points[index * 2 + 1];
|
|
1037
|
+
}
|
|
1038
|
+
return area;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
kernel void boundary_loop_next(
|
|
1042
|
+
device const long* edges [[buffer(0)]],
|
|
1043
|
+
device ulong* next_indices [[buffer(1)]],
|
|
1044
|
+
constant ulong& edge_count [[buffer(2)]],
|
|
1045
|
+
uint gid [[thread_position_in_grid]]
|
|
1046
|
+
) {
|
|
1047
|
+
ulong index = gid;
|
|
1048
|
+
if (index >= edge_count) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
long previous_x = edges[index * 4];
|
|
1052
|
+
long previous_y = edges[index * 4 + 1];
|
|
1053
|
+
long current_x = edges[index * 4 + 2];
|
|
1054
|
+
long current_y = edges[index * 4 + 3];
|
|
1055
|
+
bool have_best = false;
|
|
1056
|
+
ulong best_edge = ~static_cast<ulong>(0);
|
|
1057
|
+
long best_x = 0;
|
|
1058
|
+
long best_y = 0;
|
|
1059
|
+
for (ulong candidate = 0; candidate < edge_count; ++candidate) {
|
|
1060
|
+
if (!loop_edge_start_matches(edges, candidate, current_x, current_y)) {
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
long candidate_x = edges[candidate * 4 + 2];
|
|
1064
|
+
long candidate_y = edges[candidate * 4 + 3];
|
|
1065
|
+
if (!have_best || loop_candidate_better(previous_x, previous_y, current_x, current_y, candidate_x, candidate_y, best_x, best_y)) {
|
|
1066
|
+
best_edge = candidate;
|
|
1067
|
+
best_x = candidate_x;
|
|
1068
|
+
best_y = candidate_y;
|
|
1069
|
+
have_best = true;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
next_indices[index] = have_best ? best_edge : ~static_cast<ulong>(0);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
kernel void boundary_loop_trace(
|
|
1076
|
+
device const long* edges [[buffer(0)]],
|
|
1077
|
+
device const ulong* next_indices [[buffer(1)]],
|
|
1078
|
+
device uchar* remaining [[buffer(2)]],
|
|
1079
|
+
device long* scratch_a [[buffer(3)]],
|
|
1080
|
+
device long* scratch_b [[buffer(4)]],
|
|
1081
|
+
device long* out_vertices [[buffer(5)]],
|
|
1082
|
+
device ulong* out_offsets [[buffer(6)]],
|
|
1083
|
+
device long* out_bboxes [[buffer(7)]],
|
|
1084
|
+
device long* out_twice_areas [[buffer(8)]],
|
|
1085
|
+
device DeviceLoopTraceStats* stats [[buffer(9)]],
|
|
1086
|
+
constant ulong& edge_count [[buffer(10)]],
|
|
1087
|
+
uint gid [[thread_position_in_grid]]
|
|
1088
|
+
) {
|
|
1089
|
+
if (gid != 0) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
stats->fragment_count = 0;
|
|
1093
|
+
stats->vertex_count = 0;
|
|
1094
|
+
stats->dropped_zero_area_loop_count = 0;
|
|
1095
|
+
stats->error = 0;
|
|
1096
|
+
out_offsets[0] = 0;
|
|
1097
|
+
|
|
1098
|
+
while (true) {
|
|
1099
|
+
bool found_seed = false;
|
|
1100
|
+
ulong seed = 0;
|
|
1101
|
+
for (ulong index = 0; index < edge_count; ++index) {
|
|
1102
|
+
if (!remaining[index]) {
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
if (!found_seed || loop_edge_less(edges, index, seed)) {
|
|
1106
|
+
seed = index;
|
|
1107
|
+
found_seed = true;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
if (!found_seed) {
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
remaining[seed] = 0;
|
|
1115
|
+
long start_x = edges[seed * 4];
|
|
1116
|
+
long start_y = edges[seed * 4 + 1];
|
|
1117
|
+
long current_x = edges[seed * 4 + 2];
|
|
1118
|
+
long current_y = edges[seed * 4 + 3];
|
|
1119
|
+
ulong current_edge = seed;
|
|
1120
|
+
ulong raw_count = 0;
|
|
1121
|
+
scratch_a[raw_count * 2] = start_x;
|
|
1122
|
+
scratch_a[raw_count * 2 + 1] = start_y;
|
|
1123
|
+
++raw_count;
|
|
1124
|
+
scratch_a[raw_count * 2] = current_x;
|
|
1125
|
+
scratch_a[raw_count * 2 + 1] = current_y;
|
|
1126
|
+
++raw_count;
|
|
1127
|
+
|
|
1128
|
+
while (!(current_x == start_x && current_y == start_y)) {
|
|
1129
|
+
ulong best_edge = next_indices[current_edge];
|
|
1130
|
+
if (best_edge == ~static_cast<ulong>(0) || !remaining[best_edge]) {
|
|
1131
|
+
break;
|
|
1132
|
+
}
|
|
1133
|
+
long best_x = edges[best_edge * 4 + 2];
|
|
1134
|
+
long best_y = edges[best_edge * 4 + 3];
|
|
1135
|
+
remaining[best_edge] = 0;
|
|
1136
|
+
if (!(best_x == start_x && best_y == start_y)) {
|
|
1137
|
+
if (raw_count >= edge_count) {
|
|
1138
|
+
stats->error = 1;
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
scratch_a[raw_count * 2] = best_x;
|
|
1142
|
+
scratch_a[raw_count * 2 + 1] = best_y;
|
|
1143
|
+
++raw_count;
|
|
1144
|
+
}
|
|
1145
|
+
current_x = best_x;
|
|
1146
|
+
current_y = best_y;
|
|
1147
|
+
current_edge = best_edge;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
ulong clean_count = loop_clean_duplicates(scratch_a, raw_count, scratch_b);
|
|
1151
|
+
bool changed = true;
|
|
1152
|
+
bool scratch_b_current = true;
|
|
1153
|
+
while (changed && clean_count >= 3) {
|
|
1154
|
+
changed = false;
|
|
1155
|
+
if (scratch_b_current) {
|
|
1156
|
+
clean_count = loop_clean_collinear_once(scratch_b, clean_count, scratch_a, changed);
|
|
1157
|
+
scratch_b_current = false;
|
|
1158
|
+
} else {
|
|
1159
|
+
clean_count = loop_clean_collinear_once(scratch_a, clean_count, scratch_b, changed);
|
|
1160
|
+
scratch_b_current = true;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
device const long* clean_points = scratch_b_current ? scratch_b : scratch_a;
|
|
1164
|
+
if (clean_count < 3) {
|
|
1165
|
+
++stats->dropped_zero_area_loop_count;
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
long area = loop_twice_area(clean_points, clean_count);
|
|
1169
|
+
if (area == 0) {
|
|
1170
|
+
++stats->dropped_zero_area_loop_count;
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
ulong fragment_index = stats->fragment_count;
|
|
1174
|
+
if (fragment_index >= edge_count || stats->vertex_count + clean_count > edge_count) {
|
|
1175
|
+
stats->error = 2;
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
long x0 = clean_points[0];
|
|
1179
|
+
long y0 = clean_points[1];
|
|
1180
|
+
long x1 = clean_points[0];
|
|
1181
|
+
long y1 = clean_points[1];
|
|
1182
|
+
for (ulong index = 0; index < clean_count; ++index) {
|
|
1183
|
+
long x = clean_points[index * 2];
|
|
1184
|
+
long y = clean_points[index * 2 + 1];
|
|
1185
|
+
out_vertices[(stats->vertex_count + index) * 2] = x;
|
|
1186
|
+
out_vertices[(stats->vertex_count + index) * 2 + 1] = y;
|
|
1187
|
+
x0 = min(x0, x);
|
|
1188
|
+
y0 = min(y0, y);
|
|
1189
|
+
x1 = max(x1, x);
|
|
1190
|
+
y1 = max(y1, y);
|
|
1191
|
+
}
|
|
1192
|
+
out_bboxes[fragment_index * 4] = x0;
|
|
1193
|
+
out_bboxes[fragment_index * 4 + 1] = y0;
|
|
1194
|
+
out_bboxes[fragment_index * 4 + 2] = x1;
|
|
1195
|
+
out_bboxes[fragment_index * 4 + 3] = y1;
|
|
1196
|
+
out_twice_areas[fragment_index] = area;
|
|
1197
|
+
stats->vertex_count += clean_count;
|
|
1198
|
+
++stats->fragment_count;
|
|
1199
|
+
out_offsets[stats->fragment_count] = stats->vertex_count;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
)METAL";
|
|
1203
|
+
|
|
1204
|
+
struct Params {
|
|
1205
|
+
std::uint64_t task_count;
|
|
1206
|
+
std::uint16_t max_rects;
|
|
1207
|
+
std::uint16_t max_fragments;
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
struct ByteCompareParamsHost {
|
|
1211
|
+
std::uint64_t byte_count;
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
struct TileCountParamsHost {
|
|
1215
|
+
std::uint64_t polygon_count;
|
|
1216
|
+
std::int64_t tile_size;
|
|
1217
|
+
std::uint64_t max_tiles_per_polygon;
|
|
1218
|
+
std::uint32_t side;
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1221
|
+
struct TileEmitParamsHost {
|
|
1222
|
+
std::uint64_t polygon_count;
|
|
1223
|
+
std::int64_t tile_size;
|
|
1224
|
+
std::uint32_t side;
|
|
1225
|
+
};
|
|
1226
|
+
|
|
1227
|
+
struct RecordParamsHost {
|
|
1228
|
+
std::uint64_t old_count;
|
|
1229
|
+
std::uint64_t new_count;
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
struct KeyParamsHost {
|
|
1233
|
+
std::uint64_t count;
|
|
1234
|
+
std::uint64_t output_offset;
|
|
1235
|
+
std::uint32_t direction;
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
struct ContextParamsHost {
|
|
1239
|
+
std::uint64_t component_count;
|
|
1240
|
+
std::uint64_t polygon_count;
|
|
1241
|
+
std::uint64_t total_pairs;
|
|
1242
|
+
};
|
|
1243
|
+
|
|
1244
|
+
struct BoundaryEdgeParamsHost {
|
|
1245
|
+
std::uint64_t vertex_count;
|
|
1246
|
+
std::uint64_t polygon_count;
|
|
1247
|
+
std::uint64_t segment_count;
|
|
1248
|
+
std::uint32_t direction;
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
struct DeviceLoopTraceStatsHost {
|
|
1252
|
+
std::uint64_t fragment_count;
|
|
1253
|
+
std::uint64_t vertex_count;
|
|
1254
|
+
std::uint64_t dropped_zero_area_loop_count;
|
|
1255
|
+
std::uint32_t error;
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
struct HostRecordDelta {
|
|
1259
|
+
std::int32_t layer;
|
|
1260
|
+
std::int32_t datatype;
|
|
1261
|
+
std::uint8_t direction;
|
|
1262
|
+
std::uint64_t count;
|
|
1263
|
+
std::int64_t bbox[4];
|
|
1264
|
+
std::uint64_t vertex_count;
|
|
1265
|
+
std::uint32_t flags;
|
|
1266
|
+
std::uint64_t hash[2];
|
|
1267
|
+
std::uint64_t representative_index;
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
struct HostTileAssignment {
|
|
1271
|
+
std::int32_t layer;
|
|
1272
|
+
std::int32_t datatype;
|
|
1273
|
+
std::int64_t tx;
|
|
1274
|
+
std::int64_t ty;
|
|
1275
|
+
std::uint64_t canonical_id;
|
|
1276
|
+
std::uint8_t side;
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
struct HostCandidateTile {
|
|
1280
|
+
std::int32_t layer;
|
|
1281
|
+
std::int32_t datatype;
|
|
1282
|
+
std::int64_t tx;
|
|
1283
|
+
std::int64_t ty;
|
|
1284
|
+
};
|
|
1285
|
+
|
|
1286
|
+
struct HostOversizedTile {
|
|
1287
|
+
std::int32_t layer;
|
|
1288
|
+
std::int32_t datatype;
|
|
1289
|
+
std::uint64_t canonical_id;
|
|
1290
|
+
std::uint8_t side;
|
|
1291
|
+
std::int64_t tx0;
|
|
1292
|
+
std::int64_t ty0;
|
|
1293
|
+
std::int64_t tx1;
|
|
1294
|
+
std::int64_t ty1;
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
void set_error(const std::string& message) {
|
|
1298
|
+
g_last_error = message;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
std::vector<std::uint8_t> read_binary_file(const char* path) {
|
|
1302
|
+
std::ifstream stream(path, std::ios::binary | std::ios::ate);
|
|
1303
|
+
if (!stream) {
|
|
1304
|
+
throw std::runtime_error(std::string("could not open file: ") + path);
|
|
1305
|
+
}
|
|
1306
|
+
const auto size = stream.tellg();
|
|
1307
|
+
if (size < 0) {
|
|
1308
|
+
throw std::runtime_error(std::string("could not determine file size: ") + path);
|
|
1309
|
+
}
|
|
1310
|
+
std::vector<std::uint8_t> data(static_cast<std::size_t>(size));
|
|
1311
|
+
stream.seekg(0, std::ios::beg);
|
|
1312
|
+
if (!data.empty()) {
|
|
1313
|
+
stream.read(reinterpret_cast<char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
|
1314
|
+
if (!stream) {
|
|
1315
|
+
throw std::runtime_error(std::string("could not read file: ") + path);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return data;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
void ensure_library_locked() {
|
|
1322
|
+
if (g_device && g_queue && g_library) {
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
if (!g_device) {
|
|
1326
|
+
g_device = MTLCreateSystemDefaultDevice();
|
|
1327
|
+
if (!g_device) {
|
|
1328
|
+
NSArray<id<MTLDevice>>* devices = MTLCopyAllDevices();
|
|
1329
|
+
if ([devices count] > 0) {
|
|
1330
|
+
g_device = [devices objectAtIndex:0];
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
if (!g_device) {
|
|
1335
|
+
throw std::runtime_error("Metal device is not available");
|
|
1336
|
+
}
|
|
1337
|
+
if (!g_queue) {
|
|
1338
|
+
g_queue = [g_device newCommandQueue];
|
|
1339
|
+
}
|
|
1340
|
+
if (!g_queue) {
|
|
1341
|
+
throw std::runtime_error("Metal command queue could not be created");
|
|
1342
|
+
}
|
|
1343
|
+
if (g_library) {
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
NSError* error = nil;
|
|
1347
|
+
NSString* source = [NSString stringWithUTF8String:kMetalSource];
|
|
1348
|
+
g_library = [g_device newLibraryWithSource:source options:nil error:&error];
|
|
1349
|
+
if (!g_library) {
|
|
1350
|
+
NSString* message = error ? [error localizedDescription] : @"unknown Metal library compile failure";
|
|
1351
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
id<MTLComputePipelineState> make_named_pipeline_locked(const char* function_name, const char* label) {
|
|
1356
|
+
ensure_library_locked();
|
|
1357
|
+
NSError* error = nil;
|
|
1358
|
+
NSString* name = [NSString stringWithUTF8String:function_name];
|
|
1359
|
+
id<MTLFunction> function = [g_library newFunctionWithName:name];
|
|
1360
|
+
if (!function) {
|
|
1361
|
+
throw std::runtime_error(std::string("Metal function ") + function_name + " is unavailable");
|
|
1362
|
+
}
|
|
1363
|
+
id<MTLComputePipelineState> pipeline = [g_device newComputePipelineStateWithFunction:function error:&error];
|
|
1364
|
+
if (!pipeline) {
|
|
1365
|
+
NSString* message = error ? [error localizedDescription] : @"unknown Metal pipeline creation failure";
|
|
1366
|
+
throw std::runtime_error(std::string("unknown Metal ") + label + " pipeline creation failure: " + [[message description] UTF8String]);
|
|
1367
|
+
}
|
|
1368
|
+
return pipeline;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
void ensure_pipeline_locked() {
|
|
1372
|
+
if (g_byte_compare_pipeline && g_rect_set_pipeline && g_record_delta_pipeline_old && g_record_delta_pipeline_new && g_record_key_from_hash_pipeline && g_context_overlap_pipeline && g_one_sided_boundary_pipeline && g_mixed_boundary_pipeline && g_boundary_loop_next_pipeline && g_boundary_loop_trace_pipeline) {
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
if (!g_byte_compare_pipeline) g_byte_compare_pipeline = make_named_pipeline_locked("byte_compare", "byte-compare");
|
|
1376
|
+
if (!g_rect_set_pipeline) g_rect_set_pipeline = make_named_pipeline_locked("rect_set_exact", "rectangle exact");
|
|
1377
|
+
if (!g_record_delta_pipeline_old) g_record_delta_pipeline_old = make_named_pipeline_locked("record_delta_old", "old record-delta");
|
|
1378
|
+
if (!g_record_delta_pipeline_new) g_record_delta_pipeline_new = make_named_pipeline_locked("record_delta_new", "new record-delta");
|
|
1379
|
+
if (!g_record_key_from_hash_pipeline) g_record_key_from_hash_pipeline = make_named_pipeline_locked("record_key_from_hash", "record-key");
|
|
1380
|
+
if (!g_context_overlap_pipeline) g_context_overlap_pipeline = make_named_pipeline_locked("context_overlap_flags", "context-overlap");
|
|
1381
|
+
if (!g_one_sided_boundary_pipeline) g_one_sided_boundary_pipeline = make_named_pipeline_locked("one_sided_boundary_edges", "one-sided boundary");
|
|
1382
|
+
if (!g_mixed_boundary_pipeline) g_mixed_boundary_pipeline = make_named_pipeline_locked("mixed_boundary_edges", "mixed boundary");
|
|
1383
|
+
if (!g_boundary_loop_next_pipeline) g_boundary_loop_next_pipeline = make_named_pipeline_locked("boundary_loop_next", "boundary loop next");
|
|
1384
|
+
if (!g_boundary_loop_trace_pipeline) g_boundary_loop_trace_pipeline = make_named_pipeline_locked("boundary_loop_trace", "boundary loop trace");
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
void ensure_tile_pipelines_locked() {
|
|
1388
|
+
if (!g_tile_count_pipeline) g_tile_count_pipeline = make_named_pipeline_locked("tile_count", "tile-count");
|
|
1389
|
+
if (!g_tile_emit_pipeline) g_tile_emit_pipeline = make_named_pipeline_locked("tile_emit", "tile-emit");
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
id<MTLBuffer> make_input_buffer(const void* data, std::size_t byte_count) {
|
|
1393
|
+
static const std::uint8_t zero = 0;
|
|
1394
|
+
if (byte_count == 0) {
|
|
1395
|
+
byte_count = 1;
|
|
1396
|
+
data = &zero;
|
|
1397
|
+
}
|
|
1398
|
+
id<MTLBuffer> buffer = [g_device newBufferWithBytes:data length:byte_count options:MTLResourceStorageModeShared];
|
|
1399
|
+
if (!buffer) {
|
|
1400
|
+
throw std::runtime_error("Metal input buffer allocation failed");
|
|
1401
|
+
}
|
|
1402
|
+
return buffer;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
id<MTLBuffer> make_output_buffer(std::size_t byte_count) {
|
|
1406
|
+
if (byte_count == 0) {
|
|
1407
|
+
byte_count = 1;
|
|
1408
|
+
}
|
|
1409
|
+
id<MTLBuffer> buffer = [g_device newBufferWithLength:byte_count options:MTLResourceStorageModeShared];
|
|
1410
|
+
if (!buffer) {
|
|
1411
|
+
throw std::runtime_error("Metal output buffer allocation failed");
|
|
1412
|
+
}
|
|
1413
|
+
std::memset([buffer contents], 0, byte_count);
|
|
1414
|
+
return buffer;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
struct TileSideRun {
|
|
1418
|
+
std::vector<HostTileAssignment> assignments;
|
|
1419
|
+
std::vector<HostOversizedTile> oversized;
|
|
1420
|
+
std::uint64_t assignment_count = 0;
|
|
1421
|
+
std::uint64_t kernel_count = 0;
|
|
1422
|
+
};
|
|
1423
|
+
|
|
1424
|
+
TileSideRun run_tile_side(
|
|
1425
|
+
const std::int64_t* bboxes,
|
|
1426
|
+
const std::int32_t* layers,
|
|
1427
|
+
const std::int32_t* datatypes,
|
|
1428
|
+
const std::uint64_t* canonical_ids,
|
|
1429
|
+
std::uint64_t count,
|
|
1430
|
+
std::int64_t tile_size,
|
|
1431
|
+
std::uint64_t max_tiles_per_polygon,
|
|
1432
|
+
std::uint8_t side
|
|
1433
|
+
) {
|
|
1434
|
+
TileSideRun run;
|
|
1435
|
+
if (count == 0) {
|
|
1436
|
+
return run;
|
|
1437
|
+
}
|
|
1438
|
+
ensure_tile_pipelines_locked();
|
|
1439
|
+
const std::size_t count_size = static_cast<std::size_t>(count);
|
|
1440
|
+
id<MTLBuffer> bboxes_buffer = make_input_buffer(bboxes, count_size * 4 * sizeof(std::int64_t));
|
|
1441
|
+
id<MTLBuffer> layers_buffer = make_input_buffer(layers, count_size * sizeof(std::int32_t));
|
|
1442
|
+
id<MTLBuffer> datatypes_buffer = make_input_buffer(datatypes, count_size * sizeof(std::int32_t));
|
|
1443
|
+
id<MTLBuffer> canonical_ids_buffer = make_input_buffer(canonical_ids, count_size * sizeof(std::uint64_t));
|
|
1444
|
+
id<MTLBuffer> counts_buffer = make_output_buffer(count_size * sizeof(std::uint64_t));
|
|
1445
|
+
id<MTLBuffer> oversized_flags_buffer = make_output_buffer(count_size * sizeof(std::uint8_t));
|
|
1446
|
+
id<MTLBuffer> oversized_layers_buffer = make_output_buffer(count_size * sizeof(std::int32_t));
|
|
1447
|
+
id<MTLBuffer> oversized_datatypes_buffer = make_output_buffer(count_size * sizeof(std::int32_t));
|
|
1448
|
+
id<MTLBuffer> oversized_canonical_ids_buffer = make_output_buffer(count_size * sizeof(std::uint64_t));
|
|
1449
|
+
id<MTLBuffer> oversized_tile_bboxes_buffer = make_output_buffer(count_size * 4 * sizeof(std::int64_t));
|
|
1450
|
+
id<MTLBuffer> errors_buffer = make_output_buffer(count_size * sizeof(std::uint32_t));
|
|
1451
|
+
TileCountParamsHost count_params{count, tile_size, max_tiles_per_polygon, side};
|
|
1452
|
+
id<MTLBuffer> count_params_buffer = make_input_buffer(&count_params, sizeof(count_params));
|
|
1453
|
+
|
|
1454
|
+
id<MTLCommandBuffer> count_command = [g_queue commandBuffer];
|
|
1455
|
+
if (!count_command) {
|
|
1456
|
+
throw std::runtime_error("Metal tile count command buffer allocation failed");
|
|
1457
|
+
}
|
|
1458
|
+
id<MTLComputeCommandEncoder> count_encoder = [count_command computeCommandEncoder];
|
|
1459
|
+
if (!count_encoder) {
|
|
1460
|
+
throw std::runtime_error("Metal tile count encoder allocation failed");
|
|
1461
|
+
}
|
|
1462
|
+
[count_encoder setComputePipelineState:g_tile_count_pipeline];
|
|
1463
|
+
[count_encoder setBuffer:bboxes_buffer offset:0 atIndex:0];
|
|
1464
|
+
[count_encoder setBuffer:layers_buffer offset:0 atIndex:1];
|
|
1465
|
+
[count_encoder setBuffer:datatypes_buffer offset:0 atIndex:2];
|
|
1466
|
+
[count_encoder setBuffer:canonical_ids_buffer offset:0 atIndex:3];
|
|
1467
|
+
[count_encoder setBuffer:counts_buffer offset:0 atIndex:4];
|
|
1468
|
+
[count_encoder setBuffer:oversized_flags_buffer offset:0 atIndex:5];
|
|
1469
|
+
[count_encoder setBuffer:oversized_layers_buffer offset:0 atIndex:6];
|
|
1470
|
+
[count_encoder setBuffer:oversized_datatypes_buffer offset:0 atIndex:7];
|
|
1471
|
+
[count_encoder setBuffer:oversized_canonical_ids_buffer offset:0 atIndex:8];
|
|
1472
|
+
[count_encoder setBuffer:oversized_tile_bboxes_buffer offset:0 atIndex:9];
|
|
1473
|
+
[count_encoder setBuffer:errors_buffer offset:0 atIndex:10];
|
|
1474
|
+
[count_encoder setBuffer:count_params_buffer offset:0 atIndex:11];
|
|
1475
|
+
NSUInteger count_group_width = g_tile_count_pipeline.maxTotalThreadsPerThreadgroup;
|
|
1476
|
+
if (count_group_width > 256) {
|
|
1477
|
+
count_group_width = 256;
|
|
1478
|
+
}
|
|
1479
|
+
if (count_group_width == 0) {
|
|
1480
|
+
count_group_width = 1;
|
|
1481
|
+
}
|
|
1482
|
+
[count_encoder dispatchThreads:MTLSizeMake(static_cast<NSUInteger>(count), 1, 1) threadsPerThreadgroup:MTLSizeMake(count_group_width, 1, 1)];
|
|
1483
|
+
[count_encoder endEncoding];
|
|
1484
|
+
[count_command commit];
|
|
1485
|
+
[count_command waitUntilCompleted];
|
|
1486
|
+
if (count_command.status == MTLCommandBufferStatusError) {
|
|
1487
|
+
NSString* message = count_command.error ? [count_command.error localizedDescription] : @"Metal tile count command failed";
|
|
1488
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
1489
|
+
}
|
|
1490
|
+
run.kernel_count += 1;
|
|
1491
|
+
|
|
1492
|
+
const auto* errors = reinterpret_cast<const std::uint32_t*>([errors_buffer contents]);
|
|
1493
|
+
const auto* counts = reinterpret_cast<const std::uint64_t*>([counts_buffer contents]);
|
|
1494
|
+
const auto* oversized_flags = reinterpret_cast<const std::uint8_t*>([oversized_flags_buffer contents]);
|
|
1495
|
+
const auto* oversized_layers = reinterpret_cast<const std::int32_t*>([oversized_layers_buffer contents]);
|
|
1496
|
+
const auto* oversized_datatypes = reinterpret_cast<const std::int32_t*>([oversized_datatypes_buffer contents]);
|
|
1497
|
+
const auto* oversized_canonical_ids = reinterpret_cast<const std::uint64_t*>([oversized_canonical_ids_buffer contents]);
|
|
1498
|
+
const auto* oversized_tile_bboxes = reinterpret_cast<const std::int64_t*>([oversized_tile_bboxes_buffer contents]);
|
|
1499
|
+
std::vector<std::uint64_t> offsets(count_size);
|
|
1500
|
+
std::uint64_t total = 0;
|
|
1501
|
+
for (std::size_t index = 0; index < count_size; ++index) {
|
|
1502
|
+
if (errors[index]) {
|
|
1503
|
+
throw std::overflow_error("Metal assignment count overflow or invalid span");
|
|
1504
|
+
}
|
|
1505
|
+
offsets[index] = total;
|
|
1506
|
+
if (counts[index] > std::numeric_limits<std::uint64_t>::max() - total) {
|
|
1507
|
+
throw std::overflow_error("Metal total assignment count overflow");
|
|
1508
|
+
}
|
|
1509
|
+
total += counts[index];
|
|
1510
|
+
if (oversized_flags[index]) {
|
|
1511
|
+
run.oversized.push_back(HostOversizedTile{
|
|
1512
|
+
oversized_layers[index],
|
|
1513
|
+
oversized_datatypes[index],
|
|
1514
|
+
oversized_canonical_ids[index],
|
|
1515
|
+
side,
|
|
1516
|
+
oversized_tile_bboxes[index * 4],
|
|
1517
|
+
oversized_tile_bboxes[index * 4 + 1],
|
|
1518
|
+
oversized_tile_bboxes[index * 4 + 2],
|
|
1519
|
+
oversized_tile_bboxes[index * 4 + 3],
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
run.assignment_count = total;
|
|
1524
|
+
if (total == 0) {
|
|
1525
|
+
return run;
|
|
1526
|
+
}
|
|
1527
|
+
if (total > static_cast<std::uint64_t>(std::numeric_limits<std::size_t>::max())) {
|
|
1528
|
+
throw std::overflow_error("Metal total assignment count exceeds host addressable size");
|
|
1529
|
+
}
|
|
1530
|
+
id<MTLBuffer> offsets_buffer = make_input_buffer(offsets.data(), offsets.size() * sizeof(std::uint64_t));
|
|
1531
|
+
const std::size_t total_size = static_cast<std::size_t>(total);
|
|
1532
|
+
id<MTLBuffer> assignment_layers_buffer = make_output_buffer(total_size * sizeof(std::int32_t));
|
|
1533
|
+
id<MTLBuffer> assignment_datatypes_buffer = make_output_buffer(total_size * sizeof(std::int32_t));
|
|
1534
|
+
id<MTLBuffer> assignment_tx_buffer = make_output_buffer(total_size * sizeof(std::int64_t));
|
|
1535
|
+
id<MTLBuffer> assignment_ty_buffer = make_output_buffer(total_size * sizeof(std::int64_t));
|
|
1536
|
+
id<MTLBuffer> assignment_canonical_ids_buffer = make_output_buffer(total_size * sizeof(std::uint64_t));
|
|
1537
|
+
id<MTLBuffer> assignment_sides_buffer = make_output_buffer(total_size * sizeof(std::uint8_t));
|
|
1538
|
+
TileEmitParamsHost emit_params{count, tile_size, side};
|
|
1539
|
+
id<MTLBuffer> emit_params_buffer = make_input_buffer(&emit_params, sizeof(emit_params));
|
|
1540
|
+
|
|
1541
|
+
id<MTLCommandBuffer> emit_command = [g_queue commandBuffer];
|
|
1542
|
+
if (!emit_command) {
|
|
1543
|
+
throw std::runtime_error("Metal tile emit command buffer allocation failed");
|
|
1544
|
+
}
|
|
1545
|
+
id<MTLComputeCommandEncoder> emit_encoder = [emit_command computeCommandEncoder];
|
|
1546
|
+
if (!emit_encoder) {
|
|
1547
|
+
throw std::runtime_error("Metal tile emit encoder allocation failed");
|
|
1548
|
+
}
|
|
1549
|
+
[emit_encoder setComputePipelineState:g_tile_emit_pipeline];
|
|
1550
|
+
[emit_encoder setBuffer:bboxes_buffer offset:0 atIndex:0];
|
|
1551
|
+
[emit_encoder setBuffer:layers_buffer offset:0 atIndex:1];
|
|
1552
|
+
[emit_encoder setBuffer:datatypes_buffer offset:0 atIndex:2];
|
|
1553
|
+
[emit_encoder setBuffer:canonical_ids_buffer offset:0 atIndex:3];
|
|
1554
|
+
[emit_encoder setBuffer:offsets_buffer offset:0 atIndex:4];
|
|
1555
|
+
[emit_encoder setBuffer:oversized_flags_buffer offset:0 atIndex:5];
|
|
1556
|
+
[emit_encoder setBuffer:assignment_layers_buffer offset:0 atIndex:6];
|
|
1557
|
+
[emit_encoder setBuffer:assignment_datatypes_buffer offset:0 atIndex:7];
|
|
1558
|
+
[emit_encoder setBuffer:assignment_tx_buffer offset:0 atIndex:8];
|
|
1559
|
+
[emit_encoder setBuffer:assignment_ty_buffer offset:0 atIndex:9];
|
|
1560
|
+
[emit_encoder setBuffer:assignment_canonical_ids_buffer offset:0 atIndex:10];
|
|
1561
|
+
[emit_encoder setBuffer:assignment_sides_buffer offset:0 atIndex:11];
|
|
1562
|
+
[emit_encoder setBuffer:emit_params_buffer offset:0 atIndex:12];
|
|
1563
|
+
NSUInteger emit_group_width = g_tile_emit_pipeline.maxTotalThreadsPerThreadgroup;
|
|
1564
|
+
if (emit_group_width > 256) {
|
|
1565
|
+
emit_group_width = 256;
|
|
1566
|
+
}
|
|
1567
|
+
if (emit_group_width == 0) {
|
|
1568
|
+
emit_group_width = 1;
|
|
1569
|
+
}
|
|
1570
|
+
[emit_encoder dispatchThreads:MTLSizeMake(static_cast<NSUInteger>(count), 1, 1) threadsPerThreadgroup:MTLSizeMake(emit_group_width, 1, 1)];
|
|
1571
|
+
[emit_encoder endEncoding];
|
|
1572
|
+
[emit_command commit];
|
|
1573
|
+
[emit_command waitUntilCompleted];
|
|
1574
|
+
if (emit_command.status == MTLCommandBufferStatusError) {
|
|
1575
|
+
NSString* message = emit_command.error ? [emit_command.error localizedDescription] : @"Metal tile emit command failed";
|
|
1576
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
1577
|
+
}
|
|
1578
|
+
run.kernel_count += 1;
|
|
1579
|
+
|
|
1580
|
+
const auto* assignment_layers = reinterpret_cast<const std::int32_t*>([assignment_layers_buffer contents]);
|
|
1581
|
+
const auto* assignment_datatypes = reinterpret_cast<const std::int32_t*>([assignment_datatypes_buffer contents]);
|
|
1582
|
+
const auto* assignment_tx = reinterpret_cast<const std::int64_t*>([assignment_tx_buffer contents]);
|
|
1583
|
+
const auto* assignment_ty = reinterpret_cast<const std::int64_t*>([assignment_ty_buffer contents]);
|
|
1584
|
+
const auto* assignment_canonical_ids = reinterpret_cast<const std::uint64_t*>([assignment_canonical_ids_buffer contents]);
|
|
1585
|
+
const auto* assignment_sides = reinterpret_cast<const std::uint8_t*>([assignment_sides_buffer contents]);
|
|
1586
|
+
run.assignments.reserve(total_size);
|
|
1587
|
+
for (std::size_t index = 0; index < total_size; ++index) {
|
|
1588
|
+
run.assignments.push_back(HostTileAssignment{
|
|
1589
|
+
assignment_layers[index],
|
|
1590
|
+
assignment_datatypes[index],
|
|
1591
|
+
assignment_tx[index],
|
|
1592
|
+
assignment_ty[index],
|
|
1593
|
+
assignment_canonical_ids[index],
|
|
1594
|
+
assignment_sides[index],
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
return run;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
} // namespace
|
|
1601
|
+
|
|
1602
|
+
extern "C" {
|
|
1603
|
+
|
|
1604
|
+
struct GdsMetalPolygonRecordDeltaResult {
|
|
1605
|
+
std::uint64_t record_count;
|
|
1606
|
+
std::int32_t* layers;
|
|
1607
|
+
std::int32_t* datatypes;
|
|
1608
|
+
std::uint8_t* directions;
|
|
1609
|
+
std::uint64_t* counts;
|
|
1610
|
+
std::int64_t* bboxes;
|
|
1611
|
+
std::uint64_t* vertex_counts;
|
|
1612
|
+
std::uint32_t* flags;
|
|
1613
|
+
std::uint64_t* hashes;
|
|
1614
|
+
std::uint64_t* representative_indices;
|
|
1615
|
+
std::uint64_t old_input_count;
|
|
1616
|
+
std::uint64_t new_input_count;
|
|
1617
|
+
std::uint64_t matched_record_count;
|
|
1618
|
+
std::uint64_t reduced_key_count;
|
|
1619
|
+
std::uint64_t metal_kernel_count;
|
|
1620
|
+
float metal_event_elapsed_ms;
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1623
|
+
struct GdsMetalByteCompareResult {
|
|
1624
|
+
std::uint64_t old_size;
|
|
1625
|
+
std::uint64_t new_size;
|
|
1626
|
+
std::uint8_t same;
|
|
1627
|
+
};
|
|
1628
|
+
|
|
1629
|
+
struct GdsMetalBoundaryLoopResult {
|
|
1630
|
+
std::uint64_t fragment_count;
|
|
1631
|
+
std::uint64_t vertex_count;
|
|
1632
|
+
std::int64_t* vertices_xy;
|
|
1633
|
+
std::uint64_t* fragment_offsets;
|
|
1634
|
+
std::int64_t* bboxes;
|
|
1635
|
+
std::int64_t* twice_areas;
|
|
1636
|
+
std::uint64_t dropped_zero_area_loop_count;
|
|
1637
|
+
std::uint64_t metal_kernel_count;
|
|
1638
|
+
float metal_event_elapsed_ms;
|
|
1639
|
+
};
|
|
1640
|
+
|
|
1641
|
+
struct GdsMetalPrefilterResult {
|
|
1642
|
+
std::int64_t* candidate_tiles;
|
|
1643
|
+
std::uint64_t candidate_count;
|
|
1644
|
+
std::int64_t* oversized;
|
|
1645
|
+
std::uint64_t oversized_count;
|
|
1646
|
+
std::uint64_t assignment_count;
|
|
1647
|
+
std::uint64_t collapsed_pair_count;
|
|
1648
|
+
std::uint64_t oversized_matched_count;
|
|
1649
|
+
std::uint64_t metal_kernel_count;
|
|
1650
|
+
float metal_event_elapsed_ms;
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
struct GdsMetalContextOverlapPairsResult {
|
|
1654
|
+
std::uint64_t pair_count;
|
|
1655
|
+
std::uint64_t* component_indices;
|
|
1656
|
+
std::uint64_t* polygon_indices;
|
|
1657
|
+
std::uint64_t metal_kernel_count;
|
|
1658
|
+
float metal_event_elapsed_ms;
|
|
1659
|
+
};
|
|
1660
|
+
|
|
1661
|
+
const char* gds_metal_last_error() {
|
|
1662
|
+
return g_last_error.c_str();
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
void gds_metal_free_polygon_record_delta_result(GdsMetalPolygonRecordDeltaResult* result) {
|
|
1666
|
+
if (!result) {
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
std::free(result->layers);
|
|
1670
|
+
std::free(result->datatypes);
|
|
1671
|
+
std::free(result->directions);
|
|
1672
|
+
std::free(result->counts);
|
|
1673
|
+
std::free(result->bboxes);
|
|
1674
|
+
std::free(result->vertex_counts);
|
|
1675
|
+
std::free(result->flags);
|
|
1676
|
+
std::free(result->hashes);
|
|
1677
|
+
std::free(result->representative_indices);
|
|
1678
|
+
*result = GdsMetalPolygonRecordDeltaResult{};
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
void gds_metal_free_prefilter_result(GdsMetalPrefilterResult* result) {
|
|
1682
|
+
if (!result) {
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
std::free(result->candidate_tiles);
|
|
1686
|
+
std::free(result->oversized);
|
|
1687
|
+
*result = GdsMetalPrefilterResult{};
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
void gds_metal_free_boundary_loop_result(GdsMetalBoundaryLoopResult* result) {
|
|
1691
|
+
if (!result) {
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
std::free(result->vertices_xy);
|
|
1695
|
+
std::free(result->fragment_offsets);
|
|
1696
|
+
std::free(result->bboxes);
|
|
1697
|
+
std::free(result->twice_areas);
|
|
1698
|
+
*result = GdsMetalBoundaryLoopResult{};
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
void gds_metal_free_context_overlap_pairs_result(GdsMetalContextOverlapPairsResult* result) {
|
|
1702
|
+
if (!result) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
std::free(result->component_indices);
|
|
1706
|
+
std::free(result->polygon_indices);
|
|
1707
|
+
*result = GdsMetalContextOverlapPairsResult{};
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
int gds_metal_available() {
|
|
1711
|
+
@autoreleasepool {
|
|
1712
|
+
try {
|
|
1713
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
1714
|
+
ensure_pipeline_locked();
|
|
1715
|
+
return 1;
|
|
1716
|
+
} catch (const std::exception& exc) {
|
|
1717
|
+
set_error(exc.what());
|
|
1718
|
+
return 0;
|
|
1719
|
+
} catch (...) {
|
|
1720
|
+
set_error("unknown Metal availability probe failure");
|
|
1721
|
+
return 0;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
int gds_metal_release_device_memory() {
|
|
1727
|
+
@autoreleasepool {
|
|
1728
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
1729
|
+
g_byte_compare_pipeline = nil;
|
|
1730
|
+
g_tile_count_pipeline = nil;
|
|
1731
|
+
g_tile_emit_pipeline = nil;
|
|
1732
|
+
g_rect_set_pipeline = nil;
|
|
1733
|
+
g_record_delta_pipeline_old = nil;
|
|
1734
|
+
g_record_delta_pipeline_new = nil;
|
|
1735
|
+
g_record_key_from_hash_pipeline = nil;
|
|
1736
|
+
g_context_overlap_pipeline = nil;
|
|
1737
|
+
g_one_sided_boundary_pipeline = nil;
|
|
1738
|
+
g_mixed_boundary_pipeline = nil;
|
|
1739
|
+
g_boundary_loop_next_pipeline = nil;
|
|
1740
|
+
g_boundary_loop_trace_pipeline = nil;
|
|
1741
|
+
g_library = nil;
|
|
1742
|
+
g_queue = nil;
|
|
1743
|
+
g_device = nil;
|
|
1744
|
+
return 0;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
int gds_metal_compare_files(const char* old_path, const char* new_path, GdsMetalByteCompareResult* result) {
|
|
1749
|
+
@autoreleasepool {
|
|
1750
|
+
try {
|
|
1751
|
+
if (!old_path || !new_path || !result) {
|
|
1752
|
+
throw std::runtime_error("file compare received null pointer");
|
|
1753
|
+
}
|
|
1754
|
+
*result = GdsMetalByteCompareResult{};
|
|
1755
|
+
auto old_data = read_binary_file(old_path);
|
|
1756
|
+
auto new_data = read_binary_file(new_path);
|
|
1757
|
+
result->old_size = old_data.size();
|
|
1758
|
+
result->new_size = new_data.size();
|
|
1759
|
+
if (old_data.size() != new_data.size()) {
|
|
1760
|
+
result->same = 0;
|
|
1761
|
+
return 0;
|
|
1762
|
+
}
|
|
1763
|
+
if (old_data.empty()) {
|
|
1764
|
+
result->same = 1;
|
|
1765
|
+
return 0;
|
|
1766
|
+
}
|
|
1767
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
1768
|
+
ensure_pipeline_locked();
|
|
1769
|
+
id<MTLBuffer> old_buffer = make_input_buffer(old_data.data(), old_data.size());
|
|
1770
|
+
id<MTLBuffer> new_buffer = make_input_buffer(new_data.data(), new_data.size());
|
|
1771
|
+
std::uint32_t zero = 0;
|
|
1772
|
+
id<MTLBuffer> mismatch_buffer = make_input_buffer(&zero, sizeof(zero));
|
|
1773
|
+
ByteCompareParamsHost params{static_cast<std::uint64_t>(old_data.size())};
|
|
1774
|
+
id<MTLBuffer> params_buffer = make_input_buffer(¶ms, sizeof(params));
|
|
1775
|
+
|
|
1776
|
+
id<MTLCommandBuffer> command_buffer = [g_queue commandBuffer];
|
|
1777
|
+
if (!command_buffer) {
|
|
1778
|
+
throw std::runtime_error("Metal command buffer allocation failed");
|
|
1779
|
+
}
|
|
1780
|
+
id<MTLComputeCommandEncoder> encoder = [command_buffer computeCommandEncoder];
|
|
1781
|
+
if (!encoder) {
|
|
1782
|
+
throw std::runtime_error("Metal compute encoder allocation failed");
|
|
1783
|
+
}
|
|
1784
|
+
[encoder setComputePipelineState:g_byte_compare_pipeline];
|
|
1785
|
+
[encoder setBuffer:old_buffer offset:0 atIndex:0];
|
|
1786
|
+
[encoder setBuffer:new_buffer offset:0 atIndex:1];
|
|
1787
|
+
[encoder setBuffer:mismatch_buffer offset:0 atIndex:2];
|
|
1788
|
+
[encoder setBuffer:params_buffer offset:0 atIndex:3];
|
|
1789
|
+
NSUInteger group_width = g_byte_compare_pipeline.maxTotalThreadsPerThreadgroup;
|
|
1790
|
+
if (group_width > 256) {
|
|
1791
|
+
group_width = 256;
|
|
1792
|
+
}
|
|
1793
|
+
if (group_width == 0) {
|
|
1794
|
+
group_width = 1;
|
|
1795
|
+
}
|
|
1796
|
+
[encoder dispatchThreads:MTLSizeMake(static_cast<NSUInteger>(old_data.size()), 1, 1) threadsPerThreadgroup:MTLSizeMake(group_width, 1, 1)];
|
|
1797
|
+
[encoder endEncoding];
|
|
1798
|
+
[command_buffer commit];
|
|
1799
|
+
[command_buffer waitUntilCompleted];
|
|
1800
|
+
if (command_buffer.status == MTLCommandBufferStatusError) {
|
|
1801
|
+
NSString* message = command_buffer.error ? [command_buffer.error localizedDescription] : @"Metal command failed";
|
|
1802
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
1803
|
+
}
|
|
1804
|
+
const std::uint32_t mismatch = *reinterpret_cast<std::uint32_t*>([mismatch_buffer contents]);
|
|
1805
|
+
result->same = mismatch ? 0 : 1;
|
|
1806
|
+
return 0;
|
|
1807
|
+
} catch (const std::exception& exc) {
|
|
1808
|
+
set_error(exc.what());
|
|
1809
|
+
return 1;
|
|
1810
|
+
} catch (...) {
|
|
1811
|
+
set_error("unknown Metal byte compare failure");
|
|
1812
|
+
return 1;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
int gds_metal_prefilter(
|
|
1818
|
+
const std::int64_t* old_bboxes,
|
|
1819
|
+
const std::int32_t* old_layers,
|
|
1820
|
+
const std::int32_t* old_datatypes,
|
|
1821
|
+
const std::uint64_t* old_ids,
|
|
1822
|
+
std::uint64_t old_count,
|
|
1823
|
+
const std::int64_t* new_bboxes,
|
|
1824
|
+
const std::int32_t* new_layers,
|
|
1825
|
+
const std::int32_t* new_datatypes,
|
|
1826
|
+
const std::uint64_t* new_ids,
|
|
1827
|
+
std::uint64_t new_count,
|
|
1828
|
+
std::int64_t tile_size,
|
|
1829
|
+
std::uint64_t max_tiles_per_polygon,
|
|
1830
|
+
GdsMetalPrefilterResult* result
|
|
1831
|
+
) {
|
|
1832
|
+
@autoreleasepool {
|
|
1833
|
+
try {
|
|
1834
|
+
if (!result) {
|
|
1835
|
+
throw std::runtime_error("prefilter result pointer is null");
|
|
1836
|
+
}
|
|
1837
|
+
*result = GdsMetalPrefilterResult{};
|
|
1838
|
+
if (tile_size <= 0 || max_tiles_per_polygon == 0) {
|
|
1839
|
+
throw std::runtime_error("invalid tile config");
|
|
1840
|
+
}
|
|
1841
|
+
if (old_count && (!old_bboxes || !old_layers || !old_datatypes || !old_ids)) {
|
|
1842
|
+
throw std::runtime_error("old tile prefilter input contains null pointer");
|
|
1843
|
+
}
|
|
1844
|
+
if (new_count && (!new_bboxes || !new_layers || !new_datatypes || !new_ids)) {
|
|
1845
|
+
throw std::runtime_error("new tile prefilter input contains null pointer");
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
TileSideRun old_run;
|
|
1849
|
+
TileSideRun new_run;
|
|
1850
|
+
{
|
|
1851
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
1852
|
+
ensure_pipeline_locked();
|
|
1853
|
+
old_run = run_tile_side(old_bboxes, old_layers, old_datatypes, old_ids, old_count, tile_size, max_tiles_per_polygon, 1);
|
|
1854
|
+
new_run = run_tile_side(new_bboxes, new_layers, new_datatypes, new_ids, new_count, tile_size, max_tiles_per_polygon, 2);
|
|
1855
|
+
}
|
|
1856
|
+
result->assignment_count = old_run.assignment_count + new_run.assignment_count;
|
|
1857
|
+
result->metal_kernel_count = old_run.kernel_count + new_run.kernel_count;
|
|
1858
|
+
result->metal_event_elapsed_ms = 0.0f;
|
|
1859
|
+
|
|
1860
|
+
std::vector<HostTileAssignment> assignments;
|
|
1861
|
+
assignments.reserve(old_run.assignments.size() + new_run.assignments.size());
|
|
1862
|
+
assignments.insert(assignments.end(), old_run.assignments.begin(), old_run.assignments.end());
|
|
1863
|
+
assignments.insert(assignments.end(), new_run.assignments.begin(), new_run.assignments.end());
|
|
1864
|
+
std::sort(assignments.begin(), assignments.end(), [](const HostTileAssignment& left, const HostTileAssignment& right) {
|
|
1865
|
+
return std::make_tuple(left.layer, left.datatype, left.tx, left.ty, left.canonical_id)
|
|
1866
|
+
< std::make_tuple(right.layer, right.datatype, right.tx, right.ty, right.canonical_id);
|
|
1867
|
+
});
|
|
1868
|
+
std::vector<HostCandidateTile> candidate_tiles;
|
|
1869
|
+
std::size_t index = 0;
|
|
1870
|
+
while (index < assignments.size()) {
|
|
1871
|
+
const auto& key = assignments[index];
|
|
1872
|
+
std::uint8_t side = 0;
|
|
1873
|
+
std::size_t next = index;
|
|
1874
|
+
while (
|
|
1875
|
+
next < assignments.size()
|
|
1876
|
+
&& assignments[next].layer == key.layer
|
|
1877
|
+
&& assignments[next].datatype == key.datatype
|
|
1878
|
+
&& assignments[next].tx == key.tx
|
|
1879
|
+
&& assignments[next].ty == key.ty
|
|
1880
|
+
&& assignments[next].canonical_id == key.canonical_id
|
|
1881
|
+
) {
|
|
1882
|
+
side = static_cast<std::uint8_t>(side | assignments[next].side);
|
|
1883
|
+
next += 1;
|
|
1884
|
+
}
|
|
1885
|
+
result->collapsed_pair_count += 1;
|
|
1886
|
+
if (side != 3) {
|
|
1887
|
+
candidate_tiles.push_back(HostCandidateTile{key.layer, key.datatype, key.tx, key.ty});
|
|
1888
|
+
}
|
|
1889
|
+
index = next;
|
|
1890
|
+
}
|
|
1891
|
+
std::sort(candidate_tiles.begin(), candidate_tiles.end(), [](const HostCandidateTile& left, const HostCandidateTile& right) {
|
|
1892
|
+
return std::make_tuple(left.layer, left.datatype, left.tx, left.ty)
|
|
1893
|
+
< std::make_tuple(right.layer, right.datatype, right.tx, right.ty);
|
|
1894
|
+
});
|
|
1895
|
+
candidate_tiles.erase(
|
|
1896
|
+
std::unique(
|
|
1897
|
+
candidate_tiles.begin(),
|
|
1898
|
+
candidate_tiles.end(),
|
|
1899
|
+
[](const HostCandidateTile& left, const HostCandidateTile& right) {
|
|
1900
|
+
return left.layer == right.layer && left.datatype == right.datatype && left.tx == right.tx && left.ty == right.ty;
|
|
1901
|
+
}
|
|
1902
|
+
),
|
|
1903
|
+
candidate_tiles.end()
|
|
1904
|
+
);
|
|
1905
|
+
result->candidate_count = candidate_tiles.size();
|
|
1906
|
+
if (!candidate_tiles.empty()) {
|
|
1907
|
+
result->candidate_tiles = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * candidate_tiles.size() * 4));
|
|
1908
|
+
if (!result->candidate_tiles) {
|
|
1909
|
+
throw std::bad_alloc();
|
|
1910
|
+
}
|
|
1911
|
+
for (std::size_t row = 0; row < candidate_tiles.size(); ++row) {
|
|
1912
|
+
result->candidate_tiles[row * 4] = candidate_tiles[row].layer;
|
|
1913
|
+
result->candidate_tiles[row * 4 + 1] = candidate_tiles[row].datatype;
|
|
1914
|
+
result->candidate_tiles[row * 4 + 2] = candidate_tiles[row].tx;
|
|
1915
|
+
result->candidate_tiles[row * 4 + 3] = candidate_tiles[row].ty;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
std::vector<HostOversizedTile> oversized;
|
|
1920
|
+
oversized.reserve(old_run.oversized.size() + new_run.oversized.size());
|
|
1921
|
+
oversized.insert(oversized.end(), old_run.oversized.begin(), old_run.oversized.end());
|
|
1922
|
+
oversized.insert(oversized.end(), new_run.oversized.begin(), new_run.oversized.end());
|
|
1923
|
+
std::sort(oversized.begin(), oversized.end(), [](const HostOversizedTile& left, const HostOversizedTile& right) {
|
|
1924
|
+
return std::make_tuple(left.layer, left.datatype, left.canonical_id, left.tx0, left.ty0, left.tx1, left.ty1)
|
|
1925
|
+
< std::make_tuple(right.layer, right.datatype, right.canonical_id, right.tx0, right.ty0, right.tx1, right.ty1);
|
|
1926
|
+
});
|
|
1927
|
+
std::vector<HostOversizedTile> oversized_out;
|
|
1928
|
+
index = 0;
|
|
1929
|
+
while (index < oversized.size()) {
|
|
1930
|
+
HostOversizedTile item = oversized[index];
|
|
1931
|
+
std::uint8_t side = 0;
|
|
1932
|
+
std::size_t next = index;
|
|
1933
|
+
while (
|
|
1934
|
+
next < oversized.size()
|
|
1935
|
+
&& oversized[next].layer == item.layer
|
|
1936
|
+
&& oversized[next].datatype == item.datatype
|
|
1937
|
+
&& oversized[next].canonical_id == item.canonical_id
|
|
1938
|
+
&& oversized[next].tx0 == item.tx0
|
|
1939
|
+
&& oversized[next].ty0 == item.ty0
|
|
1940
|
+
&& oversized[next].tx1 == item.tx1
|
|
1941
|
+
&& oversized[next].ty1 == item.ty1
|
|
1942
|
+
) {
|
|
1943
|
+
side = static_cast<std::uint8_t>(side | oversized[next].side);
|
|
1944
|
+
next += 1;
|
|
1945
|
+
}
|
|
1946
|
+
if (side == 3) {
|
|
1947
|
+
result->oversized_matched_count += 1;
|
|
1948
|
+
} else {
|
|
1949
|
+
item.side = side;
|
|
1950
|
+
oversized_out.push_back(item);
|
|
1951
|
+
}
|
|
1952
|
+
index = next;
|
|
1953
|
+
}
|
|
1954
|
+
result->oversized_count = oversized_out.size();
|
|
1955
|
+
if (!oversized_out.empty()) {
|
|
1956
|
+
result->oversized = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * oversized_out.size() * 8));
|
|
1957
|
+
if (!result->oversized) {
|
|
1958
|
+
throw std::bad_alloc();
|
|
1959
|
+
}
|
|
1960
|
+
for (std::size_t row = 0; row < oversized_out.size(); ++row) {
|
|
1961
|
+
result->oversized[row * 8] = oversized_out[row].layer;
|
|
1962
|
+
result->oversized[row * 8 + 1] = oversized_out[row].datatype;
|
|
1963
|
+
result->oversized[row * 8 + 2] = static_cast<std::int64_t>(oversized_out[row].canonical_id);
|
|
1964
|
+
result->oversized[row * 8 + 3] = oversized_out[row].side;
|
|
1965
|
+
result->oversized[row * 8 + 4] = oversized_out[row].tx0;
|
|
1966
|
+
result->oversized[row * 8 + 5] = oversized_out[row].ty0;
|
|
1967
|
+
result->oversized[row * 8 + 6] = oversized_out[row].tx1;
|
|
1968
|
+
result->oversized[row * 8 + 7] = oversized_out[row].ty1;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
return 0;
|
|
1972
|
+
} catch (const std::exception& exc) {
|
|
1973
|
+
set_error(exc.what());
|
|
1974
|
+
if (result) {
|
|
1975
|
+
gds_metal_free_prefilter_result(result);
|
|
1976
|
+
}
|
|
1977
|
+
return 1;
|
|
1978
|
+
} catch (...) {
|
|
1979
|
+
set_error("unknown Metal tile prefilter failure");
|
|
1980
|
+
if (result) {
|
|
1981
|
+
gds_metal_free_prefilter_result(result);
|
|
1982
|
+
}
|
|
1983
|
+
return 1;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
int gds_metal_rect_set_exact_fragments(
|
|
1989
|
+
const std::int64_t* old_rects,
|
|
1990
|
+
const std::int64_t* new_rects,
|
|
1991
|
+
const std::uint8_t* old_counts,
|
|
1992
|
+
const std::uint8_t* new_counts,
|
|
1993
|
+
std::uint64_t task_count,
|
|
1994
|
+
std::uint16_t max_rects,
|
|
1995
|
+
std::uint16_t max_fragments,
|
|
1996
|
+
std::int64_t* out_rects,
|
|
1997
|
+
std::uint16_t* out_counts,
|
|
1998
|
+
std::int64_t* out_areas
|
|
1999
|
+
) {
|
|
2000
|
+
@autoreleasepool {
|
|
2001
|
+
try {
|
|
2002
|
+
if (!old_rects || !new_rects || !old_counts || !new_counts || !out_rects || !out_counts || !out_areas) {
|
|
2003
|
+
throw std::runtime_error("invalid null argument");
|
|
2004
|
+
}
|
|
2005
|
+
if (max_rects == 0 || max_rects > 16) {
|
|
2006
|
+
throw std::runtime_error("max_rects must be between 1 and 16");
|
|
2007
|
+
}
|
|
2008
|
+
if (max_fragments == 0 || max_fragments > 4096) {
|
|
2009
|
+
throw std::runtime_error("max_fragments must be between 1 and 4096");
|
|
2010
|
+
}
|
|
2011
|
+
if (task_count == 0) {
|
|
2012
|
+
return 0;
|
|
2013
|
+
}
|
|
2014
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
2015
|
+
ensure_pipeline_locked();
|
|
2016
|
+
const std::size_t rect_input_bytes = static_cast<std::size_t>(task_count) * max_rects * 4 * sizeof(std::int64_t);
|
|
2017
|
+
const std::size_t counts_input_bytes = static_cast<std::size_t>(task_count) * sizeof(std::uint8_t);
|
|
2018
|
+
const std::size_t out_rect_bytes = static_cast<std::size_t>(task_count) * 2 * max_fragments * 4 * sizeof(std::int64_t);
|
|
2019
|
+
const std::size_t out_count_bytes = static_cast<std::size_t>(task_count) * 2 * sizeof(std::uint16_t);
|
|
2020
|
+
const std::size_t out_area_bytes = static_cast<std::size_t>(task_count) * 3 * sizeof(std::int64_t);
|
|
2021
|
+
id<MTLBuffer> old_buffer = make_input_buffer(old_rects, rect_input_bytes);
|
|
2022
|
+
id<MTLBuffer> new_buffer = make_input_buffer(new_rects, rect_input_bytes);
|
|
2023
|
+
id<MTLBuffer> old_count_buffer = make_input_buffer(old_counts, counts_input_bytes);
|
|
2024
|
+
id<MTLBuffer> new_count_buffer = make_input_buffer(new_counts, counts_input_bytes);
|
|
2025
|
+
id<MTLBuffer> rect_output = make_output_buffer(out_rect_bytes);
|
|
2026
|
+
id<MTLBuffer> count_output = make_output_buffer(out_count_bytes);
|
|
2027
|
+
id<MTLBuffer> area_output = make_output_buffer(out_area_bytes);
|
|
2028
|
+
std::uint32_t zero = 0;
|
|
2029
|
+
id<MTLBuffer> error_buffer = make_input_buffer(&zero, sizeof(zero));
|
|
2030
|
+
Params params{task_count, max_rects, max_fragments};
|
|
2031
|
+
id<MTLBuffer> params_buffer = make_input_buffer(¶ms, sizeof(params));
|
|
2032
|
+
|
|
2033
|
+
id<MTLCommandBuffer> command_buffer = [g_queue commandBuffer];
|
|
2034
|
+
if (!command_buffer) {
|
|
2035
|
+
throw std::runtime_error("Metal command buffer allocation failed");
|
|
2036
|
+
}
|
|
2037
|
+
id<MTLComputeCommandEncoder> encoder = [command_buffer computeCommandEncoder];
|
|
2038
|
+
if (!encoder) {
|
|
2039
|
+
throw std::runtime_error("Metal compute encoder allocation failed");
|
|
2040
|
+
}
|
|
2041
|
+
[encoder setComputePipelineState:g_rect_set_pipeline];
|
|
2042
|
+
[encoder setBuffer:old_buffer offset:0 atIndex:0];
|
|
2043
|
+
[encoder setBuffer:new_buffer offset:0 atIndex:1];
|
|
2044
|
+
[encoder setBuffer:old_count_buffer offset:0 atIndex:2];
|
|
2045
|
+
[encoder setBuffer:new_count_buffer offset:0 atIndex:3];
|
|
2046
|
+
[encoder setBuffer:rect_output offset:0 atIndex:4];
|
|
2047
|
+
[encoder setBuffer:count_output offset:0 atIndex:5];
|
|
2048
|
+
[encoder setBuffer:area_output offset:0 atIndex:6];
|
|
2049
|
+
[encoder setBuffer:error_buffer offset:0 atIndex:7];
|
|
2050
|
+
[encoder setBuffer:params_buffer offset:0 atIndex:8];
|
|
2051
|
+
const NSUInteger width = static_cast<NSUInteger>(task_count);
|
|
2052
|
+
NSUInteger group_width = g_rect_set_pipeline.maxTotalThreadsPerThreadgroup;
|
|
2053
|
+
if (group_width > 256) {
|
|
2054
|
+
group_width = 256;
|
|
2055
|
+
}
|
|
2056
|
+
if (group_width == 0) {
|
|
2057
|
+
group_width = 1;
|
|
2058
|
+
}
|
|
2059
|
+
MTLSize grid = MTLSizeMake(width, 1, 1);
|
|
2060
|
+
MTLSize group = MTLSizeMake(group_width, 1, 1);
|
|
2061
|
+
[encoder dispatchThreads:grid threadsPerThreadgroup:group];
|
|
2062
|
+
[encoder endEncoding];
|
|
2063
|
+
[command_buffer commit];
|
|
2064
|
+
[command_buffer waitUntilCompleted];
|
|
2065
|
+
if (command_buffer.status == MTLCommandBufferStatusError) {
|
|
2066
|
+
NSString* message = command_buffer.error ? [command_buffer.error localizedDescription] : @"Metal command failed";
|
|
2067
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
2068
|
+
}
|
|
2069
|
+
std::uint32_t error_flag = *reinterpret_cast<std::uint32_t*>([error_buffer contents]);
|
|
2070
|
+
if (error_flag != 0) {
|
|
2071
|
+
throw std::runtime_error("Metal rectangle set output exceeded max_fragments");
|
|
2072
|
+
}
|
|
2073
|
+
std::memcpy(out_rects, [rect_output contents], out_rect_bytes);
|
|
2074
|
+
std::memcpy(out_counts, [count_output contents], out_count_bytes);
|
|
2075
|
+
std::memcpy(out_areas, [area_output contents], out_area_bytes);
|
|
2076
|
+
return 0;
|
|
2077
|
+
} catch (const std::exception& exc) {
|
|
2078
|
+
set_error(exc.what());
|
|
2079
|
+
return 1;
|
|
2080
|
+
} catch (...) {
|
|
2081
|
+
set_error("unknown Metal rectangle set exact failure");
|
|
2082
|
+
return 1;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
int gds_metal_polygon_record_delta_from_hashes(
|
|
2088
|
+
const std::uint64_t* old_offsets,
|
|
2089
|
+
const std::int32_t* old_layers,
|
|
2090
|
+
const std::int32_t* old_datatypes,
|
|
2091
|
+
const std::int64_t* old_bboxes,
|
|
2092
|
+
const std::uint32_t* old_flags,
|
|
2093
|
+
const std::uint64_t* old_hashes,
|
|
2094
|
+
std::uint64_t old_count,
|
|
2095
|
+
const std::uint64_t* new_offsets,
|
|
2096
|
+
const std::int32_t* new_layers,
|
|
2097
|
+
const std::int32_t* new_datatypes,
|
|
2098
|
+
const std::int64_t* new_bboxes,
|
|
2099
|
+
const std::uint32_t* new_flags,
|
|
2100
|
+
const std::uint64_t* new_hashes,
|
|
2101
|
+
std::uint64_t new_count,
|
|
2102
|
+
GdsMetalPolygonRecordDeltaResult* result
|
|
2103
|
+
) {
|
|
2104
|
+
@autoreleasepool {
|
|
2105
|
+
try {
|
|
2106
|
+
if (!result || !old_offsets || !new_offsets || (!old_layers && old_count) || (!old_datatypes && old_count)
|
|
2107
|
+
|| (!old_bboxes && old_count) || (!old_flags && old_count) || (!old_hashes && old_count)
|
|
2108
|
+
|| (!new_layers && new_count) || (!new_datatypes && new_count) || (!new_bboxes && new_count)
|
|
2109
|
+
|| (!new_flags && new_count) || (!new_hashes && new_count)) {
|
|
2110
|
+
throw std::runtime_error("invalid null argument");
|
|
2111
|
+
}
|
|
2112
|
+
*result = GdsMetalPolygonRecordDeltaResult{};
|
|
2113
|
+
const std::uint64_t max_records_u64 = old_count + new_count;
|
|
2114
|
+
if (max_records_u64 > static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max())) {
|
|
2115
|
+
throw std::runtime_error("Metal record delta exceeds uint32 record counter capacity");
|
|
2116
|
+
}
|
|
2117
|
+
if (max_records_u64 == 0) {
|
|
2118
|
+
result->old_input_count = old_count;
|
|
2119
|
+
result->new_input_count = new_count;
|
|
2120
|
+
return 0;
|
|
2121
|
+
}
|
|
2122
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
2123
|
+
ensure_pipeline_locked();
|
|
2124
|
+
const std::size_t old_count_size = static_cast<std::size_t>(old_count);
|
|
2125
|
+
const std::size_t new_count_size = static_cast<std::size_t>(new_count);
|
|
2126
|
+
const std::size_t max_records = static_cast<std::size_t>(max_records_u64);
|
|
2127
|
+
id<MTLBuffer> old_offsets_buffer = make_input_buffer(old_offsets, (old_count_size + 1) * sizeof(std::uint64_t));
|
|
2128
|
+
id<MTLBuffer> old_layers_buffer = make_input_buffer(old_layers, old_count_size * sizeof(std::int32_t));
|
|
2129
|
+
id<MTLBuffer> old_datatypes_buffer = make_input_buffer(old_datatypes, old_count_size * sizeof(std::int32_t));
|
|
2130
|
+
id<MTLBuffer> old_bboxes_buffer = make_input_buffer(old_bboxes, old_count_size * 4 * sizeof(std::int64_t));
|
|
2131
|
+
id<MTLBuffer> old_flags_buffer = make_input_buffer(old_flags, old_count_size * sizeof(std::uint32_t));
|
|
2132
|
+
id<MTLBuffer> old_hashes_buffer = make_input_buffer(old_hashes, old_count_size * 2 * sizeof(std::uint64_t));
|
|
2133
|
+
id<MTLBuffer> new_offsets_buffer = make_input_buffer(new_offsets, (new_count_size + 1) * sizeof(std::uint64_t));
|
|
2134
|
+
id<MTLBuffer> new_layers_buffer = make_input_buffer(new_layers, new_count_size * sizeof(std::int32_t));
|
|
2135
|
+
id<MTLBuffer> new_datatypes_buffer = make_input_buffer(new_datatypes, new_count_size * sizeof(std::int32_t));
|
|
2136
|
+
id<MTLBuffer> new_bboxes_buffer = make_input_buffer(new_bboxes, new_count_size * 4 * sizeof(std::int64_t));
|
|
2137
|
+
id<MTLBuffer> new_flags_buffer = make_input_buffer(new_flags, new_count_size * sizeof(std::uint32_t));
|
|
2138
|
+
id<MTLBuffer> new_hashes_buffer = make_input_buffer(new_hashes, new_count_size * 2 * sizeof(std::uint64_t));
|
|
2139
|
+
id<MTLBuffer> out_layers_buffer = make_output_buffer(max_records * sizeof(std::int32_t));
|
|
2140
|
+
id<MTLBuffer> out_datatypes_buffer = make_output_buffer(max_records * sizeof(std::int32_t));
|
|
2141
|
+
id<MTLBuffer> out_directions_buffer = make_output_buffer(max_records * sizeof(std::uint8_t));
|
|
2142
|
+
id<MTLBuffer> out_bboxes_buffer = make_output_buffer(max_records * 4 * sizeof(std::int64_t));
|
|
2143
|
+
id<MTLBuffer> out_vertex_counts_buffer = make_output_buffer(max_records * sizeof(std::uint64_t));
|
|
2144
|
+
id<MTLBuffer> out_flags_buffer = make_output_buffer(max_records * sizeof(std::uint32_t));
|
|
2145
|
+
id<MTLBuffer> out_hashes_buffer = make_output_buffer(max_records * 2 * sizeof(std::uint64_t));
|
|
2146
|
+
id<MTLBuffer> out_representative_indices_buffer = make_output_buffer(max_records * sizeof(std::uint64_t));
|
|
2147
|
+
KeyParamsHost old_params{old_count, 0, 1};
|
|
2148
|
+
KeyParamsHost new_params{new_count, old_count, 2};
|
|
2149
|
+
id<MTLBuffer> old_params_buffer = make_input_buffer(&old_params, sizeof(old_params));
|
|
2150
|
+
id<MTLBuffer> new_params_buffer = make_input_buffer(&new_params, sizeof(new_params));
|
|
2151
|
+
|
|
2152
|
+
id<MTLCommandBuffer> command_buffer = [g_queue commandBuffer];
|
|
2153
|
+
if (!command_buffer) {
|
|
2154
|
+
throw std::runtime_error("Metal command buffer allocation failed");
|
|
2155
|
+
}
|
|
2156
|
+
auto encode = [&](
|
|
2157
|
+
id<MTLBuffer> offsets_buffer,
|
|
2158
|
+
id<MTLBuffer> layers_buffer,
|
|
2159
|
+
id<MTLBuffer> datatypes_buffer,
|
|
2160
|
+
id<MTLBuffer> bboxes_buffer,
|
|
2161
|
+
id<MTLBuffer> flags_buffer,
|
|
2162
|
+
id<MTLBuffer> hashes_buffer,
|
|
2163
|
+
id<MTLBuffer> params_buffer,
|
|
2164
|
+
NSUInteger width
|
|
2165
|
+
) {
|
|
2166
|
+
if (width == 0) {
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
id<MTLComputeCommandEncoder> encoder = [command_buffer computeCommandEncoder];
|
|
2170
|
+
if (!encoder) {
|
|
2171
|
+
throw std::runtime_error("Metal compute encoder allocation failed");
|
|
2172
|
+
}
|
|
2173
|
+
[encoder setComputePipelineState:g_record_key_from_hash_pipeline];
|
|
2174
|
+
[encoder setBuffer:offsets_buffer offset:0 atIndex:0];
|
|
2175
|
+
[encoder setBuffer:layers_buffer offset:0 atIndex:1];
|
|
2176
|
+
[encoder setBuffer:datatypes_buffer offset:0 atIndex:2];
|
|
2177
|
+
[encoder setBuffer:bboxes_buffer offset:0 atIndex:3];
|
|
2178
|
+
[encoder setBuffer:flags_buffer offset:0 atIndex:4];
|
|
2179
|
+
[encoder setBuffer:hashes_buffer offset:0 atIndex:5];
|
|
2180
|
+
[encoder setBuffer:out_layers_buffer offset:0 atIndex:6];
|
|
2181
|
+
[encoder setBuffer:out_datatypes_buffer offset:0 atIndex:7];
|
|
2182
|
+
[encoder setBuffer:out_directions_buffer offset:0 atIndex:8];
|
|
2183
|
+
[encoder setBuffer:out_bboxes_buffer offset:0 atIndex:9];
|
|
2184
|
+
[encoder setBuffer:out_vertex_counts_buffer offset:0 atIndex:10];
|
|
2185
|
+
[encoder setBuffer:out_flags_buffer offset:0 atIndex:11];
|
|
2186
|
+
[encoder setBuffer:out_hashes_buffer offset:0 atIndex:12];
|
|
2187
|
+
[encoder setBuffer:out_representative_indices_buffer offset:0 atIndex:13];
|
|
2188
|
+
[encoder setBuffer:params_buffer offset:0 atIndex:14];
|
|
2189
|
+
NSUInteger group_width = g_record_key_from_hash_pipeline.maxTotalThreadsPerThreadgroup;
|
|
2190
|
+
if (group_width > 256) {
|
|
2191
|
+
group_width = 256;
|
|
2192
|
+
}
|
|
2193
|
+
if (group_width == 0) {
|
|
2194
|
+
group_width = 1;
|
|
2195
|
+
}
|
|
2196
|
+
[encoder dispatchThreads:MTLSizeMake(width, 1, 1) threadsPerThreadgroup:MTLSizeMake(group_width, 1, 1)];
|
|
2197
|
+
[encoder endEncoding];
|
|
2198
|
+
};
|
|
2199
|
+
encode(old_offsets_buffer, old_layers_buffer, old_datatypes_buffer, old_bboxes_buffer, old_flags_buffer, old_hashes_buffer, old_params_buffer, static_cast<NSUInteger>(old_count));
|
|
2200
|
+
encode(new_offsets_buffer, new_layers_buffer, new_datatypes_buffer, new_bboxes_buffer, new_flags_buffer, new_hashes_buffer, new_params_buffer, static_cast<NSUInteger>(new_count));
|
|
2201
|
+
[command_buffer commit];
|
|
2202
|
+
[command_buffer waitUntilCompleted];
|
|
2203
|
+
if (command_buffer.status == MTLCommandBufferStatusError) {
|
|
2204
|
+
NSString* message = command_buffer.error ? [command_buffer.error localizedDescription] : @"Metal command failed";
|
|
2205
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
const auto* out_layers = reinterpret_cast<const std::int32_t*>([out_layers_buffer contents]);
|
|
2209
|
+
const auto* out_datatypes = reinterpret_cast<const std::int32_t*>([out_datatypes_buffer contents]);
|
|
2210
|
+
const auto* out_directions = reinterpret_cast<const std::uint8_t*>([out_directions_buffer contents]);
|
|
2211
|
+
const auto* out_bboxes = reinterpret_cast<const std::int64_t*>([out_bboxes_buffer contents]);
|
|
2212
|
+
const auto* out_vertex_counts = reinterpret_cast<const std::uint64_t*>([out_vertex_counts_buffer contents]);
|
|
2213
|
+
const auto* out_flags = reinterpret_cast<const std::uint32_t*>([out_flags_buffer contents]);
|
|
2214
|
+
const auto* out_hashes = reinterpret_cast<const std::uint64_t*>([out_hashes_buffer contents]);
|
|
2215
|
+
const auto* out_reps = reinterpret_cast<const std::uint64_t*>([out_representative_indices_buffer contents]);
|
|
2216
|
+
std::vector<HostRecordDelta> source_records;
|
|
2217
|
+
source_records.reserve(max_records);
|
|
2218
|
+
for (std::size_t row = 0; row < max_records; ++row) {
|
|
2219
|
+
HostRecordDelta record{};
|
|
2220
|
+
record.layer = out_layers[row];
|
|
2221
|
+
record.datatype = out_datatypes[row];
|
|
2222
|
+
record.direction = out_directions[row];
|
|
2223
|
+
record.count = 1;
|
|
2224
|
+
for (int offset = 0; offset < 4; ++offset) {
|
|
2225
|
+
record.bbox[offset] = out_bboxes[row * 4 + offset];
|
|
2226
|
+
}
|
|
2227
|
+
record.vertex_count = out_vertex_counts[row];
|
|
2228
|
+
record.flags = out_flags[row];
|
|
2229
|
+
record.hash[0] = out_hashes[row * 2];
|
|
2230
|
+
record.hash[1] = out_hashes[row * 2 + 1];
|
|
2231
|
+
record.representative_index = out_reps[row];
|
|
2232
|
+
source_records.push_back(record);
|
|
2233
|
+
}
|
|
2234
|
+
auto key_less = [](const HostRecordDelta& left, const HostRecordDelta& right) {
|
|
2235
|
+
return std::make_tuple(left.layer, left.datatype, left.bbox[0], left.bbox[1], left.bbox[2], left.bbox[3], left.vertex_count, left.flags, left.hash[0], left.hash[1])
|
|
2236
|
+
< std::make_tuple(right.layer, right.datatype, right.bbox[0], right.bbox[1], right.bbox[2], right.bbox[3], right.vertex_count, right.flags, right.hash[0], right.hash[1]);
|
|
2237
|
+
};
|
|
2238
|
+
auto key_equal = [](const HostRecordDelta& left, const HostRecordDelta& right) {
|
|
2239
|
+
return left.layer == right.layer
|
|
2240
|
+
&& left.datatype == right.datatype
|
|
2241
|
+
&& left.bbox[0] == right.bbox[0]
|
|
2242
|
+
&& left.bbox[1] == right.bbox[1]
|
|
2243
|
+
&& left.bbox[2] == right.bbox[2]
|
|
2244
|
+
&& left.bbox[3] == right.bbox[3]
|
|
2245
|
+
&& left.vertex_count == right.vertex_count
|
|
2246
|
+
&& left.flags == right.flags
|
|
2247
|
+
&& left.hash[0] == right.hash[0]
|
|
2248
|
+
&& left.hash[1] == right.hash[1];
|
|
2249
|
+
};
|
|
2250
|
+
std::sort(source_records.begin(), source_records.end(), key_less);
|
|
2251
|
+
std::vector<HostRecordDelta> records;
|
|
2252
|
+
std::size_t index = 0;
|
|
2253
|
+
while (index < source_records.size()) {
|
|
2254
|
+
const auto& key_record = source_records[index];
|
|
2255
|
+
std::uint64_t old_total = 0;
|
|
2256
|
+
std::uint64_t new_total = 0;
|
|
2257
|
+
std::uint64_t old_rep = 0;
|
|
2258
|
+
std::uint64_t new_rep = 0;
|
|
2259
|
+
std::size_t next = index;
|
|
2260
|
+
while (next < source_records.size() && key_equal(key_record, source_records[next])) {
|
|
2261
|
+
if (source_records[next].direction == 1) {
|
|
2262
|
+
if (old_total == 0) {
|
|
2263
|
+
old_rep = source_records[next].representative_index;
|
|
2264
|
+
}
|
|
2265
|
+
old_total += 1;
|
|
2266
|
+
} else {
|
|
2267
|
+
if (new_total == 0) {
|
|
2268
|
+
new_rep = source_records[next].representative_index;
|
|
2269
|
+
}
|
|
2270
|
+
new_total += 1;
|
|
2271
|
+
}
|
|
2272
|
+
next += 1;
|
|
2273
|
+
}
|
|
2274
|
+
result->reduced_key_count += 1;
|
|
2275
|
+
result->matched_record_count += std::min(old_total, new_total);
|
|
2276
|
+
if (old_total > new_total) {
|
|
2277
|
+
HostRecordDelta delta = key_record;
|
|
2278
|
+
delta.direction = 1;
|
|
2279
|
+
delta.count = old_total - new_total;
|
|
2280
|
+
delta.representative_index = old_rep;
|
|
2281
|
+
records.push_back(delta);
|
|
2282
|
+
} else if (new_total > old_total) {
|
|
2283
|
+
HostRecordDelta delta = key_record;
|
|
2284
|
+
delta.direction = 2;
|
|
2285
|
+
delta.count = new_total - old_total;
|
|
2286
|
+
delta.representative_index = new_rep;
|
|
2287
|
+
records.push_back(delta);
|
|
2288
|
+
}
|
|
2289
|
+
index = next;
|
|
2290
|
+
}
|
|
2291
|
+
std::sort(records.begin(), records.end(), [](const HostRecordDelta& left, const HostRecordDelta& right) {
|
|
2292
|
+
return std::make_tuple(left.direction, left.layer, left.datatype, left.bbox[0], left.bbox[1], left.bbox[2], left.bbox[3], left.hash[0], left.hash[1])
|
|
2293
|
+
< std::make_tuple(right.direction, right.layer, right.datatype, right.bbox[0], right.bbox[1], right.bbox[2], right.bbox[3], right.hash[0], right.hash[1]);
|
|
2294
|
+
});
|
|
2295
|
+
|
|
2296
|
+
const std::size_t record_count = records.size();
|
|
2297
|
+
result->record_count = record_count;
|
|
2298
|
+
result->old_input_count = old_count;
|
|
2299
|
+
result->new_input_count = new_count;
|
|
2300
|
+
result->metal_kernel_count = (old_count ? 1 : 0) + (new_count ? 1 : 0);
|
|
2301
|
+
result->metal_event_elapsed_ms = 0.0f;
|
|
2302
|
+
if (record_count == 0) {
|
|
2303
|
+
return 0;
|
|
2304
|
+
}
|
|
2305
|
+
result->layers = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * record_count));
|
|
2306
|
+
result->datatypes = static_cast<std::int32_t*>(std::malloc(sizeof(std::int32_t) * record_count));
|
|
2307
|
+
result->directions = static_cast<std::uint8_t*>(std::malloc(sizeof(std::uint8_t) * record_count));
|
|
2308
|
+
result->counts = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * record_count));
|
|
2309
|
+
result->bboxes = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * record_count * 4));
|
|
2310
|
+
result->vertex_counts = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * record_count));
|
|
2311
|
+
result->flags = static_cast<std::uint32_t*>(std::malloc(sizeof(std::uint32_t) * record_count));
|
|
2312
|
+
result->hashes = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * record_count * 2));
|
|
2313
|
+
result->representative_indices = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * record_count));
|
|
2314
|
+
if (!result->layers || !result->datatypes || !result->directions || !result->counts || !result->bboxes
|
|
2315
|
+
|| !result->vertex_counts || !result->flags || !result->hashes || !result->representative_indices) {
|
|
2316
|
+
throw std::bad_alloc();
|
|
2317
|
+
}
|
|
2318
|
+
for (std::size_t row = 0; row < record_count; ++row) {
|
|
2319
|
+
const auto& record = records[row];
|
|
2320
|
+
result->layers[row] = record.layer;
|
|
2321
|
+
result->datatypes[row] = record.datatype;
|
|
2322
|
+
result->directions[row] = record.direction;
|
|
2323
|
+
result->counts[row] = record.count;
|
|
2324
|
+
for (int offset = 0; offset < 4; ++offset) {
|
|
2325
|
+
result->bboxes[row * 4 + offset] = record.bbox[offset];
|
|
2326
|
+
}
|
|
2327
|
+
result->vertex_counts[row] = record.vertex_count;
|
|
2328
|
+
result->flags[row] = record.flags;
|
|
2329
|
+
result->hashes[row * 2] = record.hash[0];
|
|
2330
|
+
result->hashes[row * 2 + 1] = record.hash[1];
|
|
2331
|
+
result->representative_indices[row] = record.representative_index;
|
|
2332
|
+
}
|
|
2333
|
+
return 0;
|
|
2334
|
+
} catch (const std::exception& exc) {
|
|
2335
|
+
set_error(exc.what());
|
|
2336
|
+
if (result) {
|
|
2337
|
+
gds_metal_free_polygon_record_delta_result(result);
|
|
2338
|
+
}
|
|
2339
|
+
return 1;
|
|
2340
|
+
} catch (...) {
|
|
2341
|
+
set_error("unknown Metal polygon record delta failure");
|
|
2342
|
+
if (result) {
|
|
2343
|
+
gds_metal_free_polygon_record_delta_result(result);
|
|
2344
|
+
}
|
|
2345
|
+
return 1;
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
int gds_metal_context_overlap_pairs(
|
|
2351
|
+
const std::int64_t* component_bboxes,
|
|
2352
|
+
const std::int32_t* component_layers,
|
|
2353
|
+
const std::int32_t* component_datatypes,
|
|
2354
|
+
std::uint64_t component_count,
|
|
2355
|
+
const std::int64_t* polygon_bboxes,
|
|
2356
|
+
const std::int32_t* polygon_layers,
|
|
2357
|
+
const std::int32_t* polygon_datatypes,
|
|
2358
|
+
std::uint64_t polygon_count,
|
|
2359
|
+
GdsMetalContextOverlapPairsResult* result
|
|
2360
|
+
) {
|
|
2361
|
+
@autoreleasepool {
|
|
2362
|
+
try {
|
|
2363
|
+
if (!result) {
|
|
2364
|
+
throw std::runtime_error("context overlap result pointer is null");
|
|
2365
|
+
}
|
|
2366
|
+
*result = GdsMetalContextOverlapPairsResult{};
|
|
2367
|
+
if (component_count == 0 || polygon_count == 0) {
|
|
2368
|
+
return 0;
|
|
2369
|
+
}
|
|
2370
|
+
if (!component_bboxes || !component_layers || !component_datatypes || !polygon_bboxes || !polygon_layers || !polygon_datatypes) {
|
|
2371
|
+
throw std::runtime_error("context overlap input contains null pointer");
|
|
2372
|
+
}
|
|
2373
|
+
if (component_count > std::numeric_limits<std::uint64_t>::max() / polygon_count) {
|
|
2374
|
+
throw std::overflow_error("context overlap pair search count overflow");
|
|
2375
|
+
}
|
|
2376
|
+
const std::uint64_t total_pairs = component_count * polygon_count;
|
|
2377
|
+
if (total_pairs > static_cast<std::uint64_t>(std::numeric_limits<std::size_t>::max())) {
|
|
2378
|
+
throw std::overflow_error("context overlap pair count exceeds host address space");
|
|
2379
|
+
}
|
|
2380
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
2381
|
+
ensure_pipeline_locked();
|
|
2382
|
+
const std::size_t component_count_size = static_cast<std::size_t>(component_count);
|
|
2383
|
+
const std::size_t polygon_count_size = static_cast<std::size_t>(polygon_count);
|
|
2384
|
+
const std::size_t total_pairs_size = static_cast<std::size_t>(total_pairs);
|
|
2385
|
+
id<MTLBuffer> component_bboxes_buffer = make_input_buffer(component_bboxes, component_count_size * 4 * sizeof(std::int64_t));
|
|
2386
|
+
id<MTLBuffer> component_layers_buffer = make_input_buffer(component_layers, component_count_size * sizeof(std::int32_t));
|
|
2387
|
+
id<MTLBuffer> component_datatypes_buffer = make_input_buffer(component_datatypes, component_count_size * sizeof(std::int32_t));
|
|
2388
|
+
id<MTLBuffer> polygon_bboxes_buffer = make_input_buffer(polygon_bboxes, polygon_count_size * 4 * sizeof(std::int64_t));
|
|
2389
|
+
id<MTLBuffer> polygon_layers_buffer = make_input_buffer(polygon_layers, polygon_count_size * sizeof(std::int32_t));
|
|
2390
|
+
id<MTLBuffer> polygon_datatypes_buffer = make_input_buffer(polygon_datatypes, polygon_count_size * sizeof(std::int32_t));
|
|
2391
|
+
id<MTLBuffer> flags_buffer = make_output_buffer(total_pairs_size * sizeof(std::uint8_t));
|
|
2392
|
+
ContextParamsHost params{component_count, polygon_count, total_pairs};
|
|
2393
|
+
id<MTLBuffer> params_buffer = make_input_buffer(¶ms, sizeof(params));
|
|
2394
|
+
|
|
2395
|
+
id<MTLCommandBuffer> command_buffer = [g_queue commandBuffer];
|
|
2396
|
+
if (!command_buffer) {
|
|
2397
|
+
throw std::runtime_error("Metal command buffer allocation failed");
|
|
2398
|
+
}
|
|
2399
|
+
id<MTLComputeCommandEncoder> encoder = [command_buffer computeCommandEncoder];
|
|
2400
|
+
if (!encoder) {
|
|
2401
|
+
throw std::runtime_error("Metal compute encoder allocation failed");
|
|
2402
|
+
}
|
|
2403
|
+
[encoder setComputePipelineState:g_context_overlap_pipeline];
|
|
2404
|
+
[encoder setBuffer:component_bboxes_buffer offset:0 atIndex:0];
|
|
2405
|
+
[encoder setBuffer:component_layers_buffer offset:0 atIndex:1];
|
|
2406
|
+
[encoder setBuffer:component_datatypes_buffer offset:0 atIndex:2];
|
|
2407
|
+
[encoder setBuffer:polygon_bboxes_buffer offset:0 atIndex:3];
|
|
2408
|
+
[encoder setBuffer:polygon_layers_buffer offset:0 atIndex:4];
|
|
2409
|
+
[encoder setBuffer:polygon_datatypes_buffer offset:0 atIndex:5];
|
|
2410
|
+
[encoder setBuffer:flags_buffer offset:0 atIndex:6];
|
|
2411
|
+
[encoder setBuffer:params_buffer offset:0 atIndex:7];
|
|
2412
|
+
NSUInteger group_width = g_context_overlap_pipeline.maxTotalThreadsPerThreadgroup;
|
|
2413
|
+
if (group_width > 256) {
|
|
2414
|
+
group_width = 256;
|
|
2415
|
+
}
|
|
2416
|
+
if (group_width == 0) {
|
|
2417
|
+
group_width = 1;
|
|
2418
|
+
}
|
|
2419
|
+
[encoder dispatchThreads:MTLSizeMake(static_cast<NSUInteger>(total_pairs), 1, 1) threadsPerThreadgroup:MTLSizeMake(group_width, 1, 1)];
|
|
2420
|
+
[encoder endEncoding];
|
|
2421
|
+
[command_buffer commit];
|
|
2422
|
+
[command_buffer waitUntilCompleted];
|
|
2423
|
+
if (command_buffer.status == MTLCommandBufferStatusError) {
|
|
2424
|
+
NSString* message = command_buffer.error ? [command_buffer.error localizedDescription] : @"Metal command failed";
|
|
2425
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
const auto* flags = reinterpret_cast<const std::uint8_t*>([flags_buffer contents]);
|
|
2429
|
+
std::uint64_t pair_count = 0;
|
|
2430
|
+
for (std::size_t linear = 0; linear < total_pairs_size; ++linear) {
|
|
2431
|
+
pair_count += flags[linear] ? 1 : 0;
|
|
2432
|
+
}
|
|
2433
|
+
result->pair_count = pair_count;
|
|
2434
|
+
result->metal_kernel_count = 1;
|
|
2435
|
+
result->metal_event_elapsed_ms = 0.0f;
|
|
2436
|
+
if (pair_count == 0) {
|
|
2437
|
+
return 0;
|
|
2438
|
+
}
|
|
2439
|
+
result->component_indices = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * pair_count));
|
|
2440
|
+
result->polygon_indices = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * pair_count));
|
|
2441
|
+
if (!result->component_indices || !result->polygon_indices) {
|
|
2442
|
+
throw std::bad_alloc();
|
|
2443
|
+
}
|
|
2444
|
+
std::uint64_t out_index = 0;
|
|
2445
|
+
for (std::uint64_t linear = 0; linear < total_pairs; ++linear) {
|
|
2446
|
+
if (!flags[static_cast<std::size_t>(linear)]) {
|
|
2447
|
+
continue;
|
|
2448
|
+
}
|
|
2449
|
+
const std::uint64_t component_index = linear / polygon_count;
|
|
2450
|
+
const std::uint64_t polygon_index = linear - component_index * polygon_count;
|
|
2451
|
+
result->component_indices[out_index] = component_index;
|
|
2452
|
+
result->polygon_indices[out_index] = polygon_index;
|
|
2453
|
+
out_index += 1;
|
|
2454
|
+
}
|
|
2455
|
+
return 0;
|
|
2456
|
+
} catch (const std::exception& exc) {
|
|
2457
|
+
set_error(exc.what());
|
|
2458
|
+
if (result) {
|
|
2459
|
+
gds_metal_free_context_overlap_pairs_result(result);
|
|
2460
|
+
}
|
|
2461
|
+
return 1;
|
|
2462
|
+
} catch (...) {
|
|
2463
|
+
set_error("unknown Metal context overlap failure");
|
|
2464
|
+
if (result) {
|
|
2465
|
+
gds_metal_free_context_overlap_pairs_result(result);
|
|
2466
|
+
}
|
|
2467
|
+
return 1;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
int gds_metal_boundary_loops_from_edges(
|
|
2473
|
+
const std::int64_t* edges,
|
|
2474
|
+
std::uint64_t edge_count,
|
|
2475
|
+
GdsMetalBoundaryLoopResult* result
|
|
2476
|
+
) {
|
|
2477
|
+
@autoreleasepool {
|
|
2478
|
+
try {
|
|
2479
|
+
if (!result) {
|
|
2480
|
+
throw std::runtime_error("boundary loop result pointer is null");
|
|
2481
|
+
}
|
|
2482
|
+
*result = GdsMetalBoundaryLoopResult{};
|
|
2483
|
+
if (edge_count == 0) {
|
|
2484
|
+
result->fragment_offsets = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t)));
|
|
2485
|
+
if (!result->fragment_offsets) {
|
|
2486
|
+
throw std::bad_alloc();
|
|
2487
|
+
}
|
|
2488
|
+
result->fragment_offsets[0] = 0;
|
|
2489
|
+
return 0;
|
|
2490
|
+
}
|
|
2491
|
+
if (!edges) {
|
|
2492
|
+
throw std::runtime_error("boundary loop edge pointer is null");
|
|
2493
|
+
}
|
|
2494
|
+
if (edge_count > static_cast<std::uint64_t>(std::numeric_limits<std::size_t>::max())) {
|
|
2495
|
+
throw std::overflow_error("boundary loop edge count exceeds host address space");
|
|
2496
|
+
}
|
|
2497
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
2498
|
+
ensure_pipeline_locked();
|
|
2499
|
+
const std::size_t edge_count_size = static_cast<std::size_t>(edge_count);
|
|
2500
|
+
id<MTLBuffer> edges_buffer = make_input_buffer(edges, edge_count_size * 4 * sizeof(std::int64_t));
|
|
2501
|
+
id<MTLBuffer> next_indices_buffer = make_output_buffer(edge_count_size * sizeof(std::uint64_t));
|
|
2502
|
+
std::vector<std::uint8_t> remaining(edge_count_size, 1);
|
|
2503
|
+
id<MTLBuffer> remaining_buffer = make_input_buffer(remaining.data(), remaining.size() * sizeof(std::uint8_t));
|
|
2504
|
+
id<MTLBuffer> scratch_a_buffer = make_output_buffer(edge_count_size * 2 * sizeof(std::int64_t));
|
|
2505
|
+
id<MTLBuffer> scratch_b_buffer = make_output_buffer(edge_count_size * 2 * sizeof(std::int64_t));
|
|
2506
|
+
id<MTLBuffer> vertices_buffer = make_output_buffer(edge_count_size * 2 * sizeof(std::int64_t));
|
|
2507
|
+
id<MTLBuffer> offsets_buffer = make_output_buffer((edge_count_size + 1) * sizeof(std::uint64_t));
|
|
2508
|
+
id<MTLBuffer> bboxes_buffer = make_output_buffer(edge_count_size * 4 * sizeof(std::int64_t));
|
|
2509
|
+
id<MTLBuffer> areas_buffer = make_output_buffer(edge_count_size * sizeof(std::int64_t));
|
|
2510
|
+
id<MTLBuffer> stats_buffer = make_output_buffer(sizeof(DeviceLoopTraceStatsHost));
|
|
2511
|
+
id<MTLBuffer> edge_count_buffer = make_input_buffer(&edge_count, sizeof(edge_count));
|
|
2512
|
+
|
|
2513
|
+
id<MTLCommandBuffer> command_buffer = [g_queue commandBuffer];
|
|
2514
|
+
if (!command_buffer) {
|
|
2515
|
+
throw std::runtime_error("Metal command buffer allocation failed");
|
|
2516
|
+
}
|
|
2517
|
+
id<MTLComputeCommandEncoder> next_encoder = [command_buffer computeCommandEncoder];
|
|
2518
|
+
if (!next_encoder) {
|
|
2519
|
+
throw std::runtime_error("Metal boundary loop next encoder allocation failed");
|
|
2520
|
+
}
|
|
2521
|
+
[next_encoder setComputePipelineState:g_boundary_loop_next_pipeline];
|
|
2522
|
+
[next_encoder setBuffer:edges_buffer offset:0 atIndex:0];
|
|
2523
|
+
[next_encoder setBuffer:next_indices_buffer offset:0 atIndex:1];
|
|
2524
|
+
[next_encoder setBuffer:edge_count_buffer offset:0 atIndex:2];
|
|
2525
|
+
NSUInteger next_group_width = g_boundary_loop_next_pipeline.maxTotalThreadsPerThreadgroup;
|
|
2526
|
+
if (next_group_width > 256) {
|
|
2527
|
+
next_group_width = 256;
|
|
2528
|
+
}
|
|
2529
|
+
if (next_group_width == 0) {
|
|
2530
|
+
next_group_width = 1;
|
|
2531
|
+
}
|
|
2532
|
+
[next_encoder dispatchThreads:MTLSizeMake(static_cast<NSUInteger>(edge_count), 1, 1) threadsPerThreadgroup:MTLSizeMake(next_group_width, 1, 1)];
|
|
2533
|
+
[next_encoder endEncoding];
|
|
2534
|
+
|
|
2535
|
+
id<MTLComputeCommandEncoder> trace_encoder = [command_buffer computeCommandEncoder];
|
|
2536
|
+
if (!trace_encoder) {
|
|
2537
|
+
throw std::runtime_error("Metal boundary loop trace encoder allocation failed");
|
|
2538
|
+
}
|
|
2539
|
+
[trace_encoder setComputePipelineState:g_boundary_loop_trace_pipeline];
|
|
2540
|
+
[trace_encoder setBuffer:edges_buffer offset:0 atIndex:0];
|
|
2541
|
+
[trace_encoder setBuffer:next_indices_buffer offset:0 atIndex:1];
|
|
2542
|
+
[trace_encoder setBuffer:remaining_buffer offset:0 atIndex:2];
|
|
2543
|
+
[trace_encoder setBuffer:scratch_a_buffer offset:0 atIndex:3];
|
|
2544
|
+
[trace_encoder setBuffer:scratch_b_buffer offset:0 atIndex:4];
|
|
2545
|
+
[trace_encoder setBuffer:vertices_buffer offset:0 atIndex:5];
|
|
2546
|
+
[trace_encoder setBuffer:offsets_buffer offset:0 atIndex:6];
|
|
2547
|
+
[trace_encoder setBuffer:bboxes_buffer offset:0 atIndex:7];
|
|
2548
|
+
[trace_encoder setBuffer:areas_buffer offset:0 atIndex:8];
|
|
2549
|
+
[trace_encoder setBuffer:stats_buffer offset:0 atIndex:9];
|
|
2550
|
+
[trace_encoder setBuffer:edge_count_buffer offset:0 atIndex:10];
|
|
2551
|
+
[trace_encoder dispatchThreads:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
|
|
2552
|
+
[trace_encoder endEncoding];
|
|
2553
|
+
[command_buffer commit];
|
|
2554
|
+
[command_buffer waitUntilCompleted];
|
|
2555
|
+
if (command_buffer.status == MTLCommandBufferStatusError) {
|
|
2556
|
+
NSString* message = command_buffer.error ? [command_buffer.error localizedDescription] : @"Metal boundary loop command failed";
|
|
2557
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
2558
|
+
}
|
|
2559
|
+
const auto* stats = reinterpret_cast<const DeviceLoopTraceStatsHost*>([stats_buffer contents]);
|
|
2560
|
+
if (stats->error) {
|
|
2561
|
+
throw std::runtime_error(stats->error == 1 ? "Metal boundary loop trace exceeded raw vertex limit" : "Metal boundary loop trace exceeded output limit");
|
|
2562
|
+
}
|
|
2563
|
+
result->fragment_count = stats->fragment_count;
|
|
2564
|
+
result->vertex_count = stats->vertex_count;
|
|
2565
|
+
result->dropped_zero_area_loop_count = stats->dropped_zero_area_loop_count;
|
|
2566
|
+
result->metal_kernel_count = 2;
|
|
2567
|
+
result->metal_event_elapsed_ms = 0.0f;
|
|
2568
|
+
if (result->fragment_count) {
|
|
2569
|
+
result->fragment_offsets = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t) * (result->fragment_count + 1)));
|
|
2570
|
+
result->vertices_xy = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * result->vertex_count * 2));
|
|
2571
|
+
result->bboxes = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * result->fragment_count * 4));
|
|
2572
|
+
result->twice_areas = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * result->fragment_count));
|
|
2573
|
+
if (!result->fragment_offsets || !result->vertices_xy || !result->bboxes || !result->twice_areas) {
|
|
2574
|
+
throw std::bad_alloc();
|
|
2575
|
+
}
|
|
2576
|
+
std::memcpy(result->fragment_offsets, [offsets_buffer contents], sizeof(std::uint64_t) * (result->fragment_count + 1));
|
|
2577
|
+
std::memcpy(result->vertices_xy, [vertices_buffer contents], sizeof(std::int64_t) * result->vertex_count * 2);
|
|
2578
|
+
std::memcpy(result->bboxes, [bboxes_buffer contents], sizeof(std::int64_t) * result->fragment_count * 4);
|
|
2579
|
+
std::memcpy(result->twice_areas, [areas_buffer contents], sizeof(std::int64_t) * result->fragment_count);
|
|
2580
|
+
} else {
|
|
2581
|
+
result->fragment_offsets = static_cast<std::uint64_t*>(std::malloc(sizeof(std::uint64_t)));
|
|
2582
|
+
if (!result->fragment_offsets) {
|
|
2583
|
+
throw std::bad_alloc();
|
|
2584
|
+
}
|
|
2585
|
+
result->fragment_offsets[0] = 0;
|
|
2586
|
+
}
|
|
2587
|
+
return 0;
|
|
2588
|
+
} catch (const std::exception& exc) {
|
|
2589
|
+
set_error(exc.what());
|
|
2590
|
+
if (result) {
|
|
2591
|
+
gds_metal_free_boundary_loop_result(result);
|
|
2592
|
+
}
|
|
2593
|
+
return 1;
|
|
2594
|
+
} catch (...) {
|
|
2595
|
+
set_error("unknown Metal boundary loop failure");
|
|
2596
|
+
if (result) {
|
|
2597
|
+
gds_metal_free_boundary_loop_result(result);
|
|
2598
|
+
}
|
|
2599
|
+
return 1;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
int gds_metal_one_sided_boundary_edges(
|
|
2605
|
+
const std::int64_t* vertices_xy,
|
|
2606
|
+
std::uint64_t vertex_count,
|
|
2607
|
+
const std::uint64_t* polygon_offsets,
|
|
2608
|
+
const std::int64_t* bboxes,
|
|
2609
|
+
const std::int64_t* signed_twice_areas,
|
|
2610
|
+
std::uint64_t polygon_count,
|
|
2611
|
+
const double* segment_points,
|
|
2612
|
+
const std::uint32_t* segment_polygon_indices,
|
|
2613
|
+
std::uint64_t segment_count,
|
|
2614
|
+
double* out_edges,
|
|
2615
|
+
std::uint8_t* out_keep
|
|
2616
|
+
) {
|
|
2617
|
+
@autoreleasepool {
|
|
2618
|
+
try {
|
|
2619
|
+
if (!out_edges || !out_keep) {
|
|
2620
|
+
throw std::runtime_error("one-sided boundary output pointer is null");
|
|
2621
|
+
}
|
|
2622
|
+
if (segment_count == 0) {
|
|
2623
|
+
return 0;
|
|
2624
|
+
}
|
|
2625
|
+
if (!polygon_offsets || !bboxes || !signed_twice_areas || !segment_points || !segment_polygon_indices || (vertex_count && !vertices_xy)) {
|
|
2626
|
+
throw std::runtime_error("one-sided boundary input contains null pointer");
|
|
2627
|
+
}
|
|
2628
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
2629
|
+
ensure_pipeline_locked();
|
|
2630
|
+
const std::size_t vertex_count_size = static_cast<std::size_t>(vertex_count);
|
|
2631
|
+
const std::size_t polygon_count_size = static_cast<std::size_t>(polygon_count);
|
|
2632
|
+
const std::size_t segment_count_size = static_cast<std::size_t>(segment_count);
|
|
2633
|
+
std::vector<float> segment_points_float(segment_count_size * 4);
|
|
2634
|
+
for (std::size_t index = 0; index < segment_points_float.size(); ++index) {
|
|
2635
|
+
segment_points_float[index] = static_cast<float>(segment_points[index]);
|
|
2636
|
+
}
|
|
2637
|
+
id<MTLBuffer> vertices_buffer = make_input_buffer(vertices_xy, vertex_count_size * 2 * sizeof(std::int64_t));
|
|
2638
|
+
id<MTLBuffer> offsets_buffer = make_input_buffer(polygon_offsets, (polygon_count_size + 1) * sizeof(std::uint64_t));
|
|
2639
|
+
id<MTLBuffer> bboxes_buffer = make_input_buffer(bboxes, polygon_count_size * 4 * sizeof(std::int64_t));
|
|
2640
|
+
id<MTLBuffer> signed_buffer = make_input_buffer(signed_twice_areas, polygon_count_size * sizeof(std::int64_t));
|
|
2641
|
+
id<MTLBuffer> segment_buffer = make_input_buffer(segment_points_float.data(), segment_points_float.size() * sizeof(float));
|
|
2642
|
+
id<MTLBuffer> index_buffer = make_input_buffer(segment_polygon_indices, segment_count_size * sizeof(std::uint32_t));
|
|
2643
|
+
id<MTLBuffer> out_edges_buffer = make_output_buffer(segment_count_size * 4 * sizeof(float));
|
|
2644
|
+
id<MTLBuffer> out_keep_buffer = make_output_buffer(segment_count_size * sizeof(std::uint8_t));
|
|
2645
|
+
BoundaryEdgeParamsHost params{vertex_count, polygon_count, segment_count, 0};
|
|
2646
|
+
id<MTLBuffer> params_buffer = make_input_buffer(¶ms, sizeof(params));
|
|
2647
|
+
|
|
2648
|
+
id<MTLCommandBuffer> command_buffer = [g_queue commandBuffer];
|
|
2649
|
+
if (!command_buffer) {
|
|
2650
|
+
throw std::runtime_error("Metal command buffer allocation failed");
|
|
2651
|
+
}
|
|
2652
|
+
id<MTLComputeCommandEncoder> encoder = [command_buffer computeCommandEncoder];
|
|
2653
|
+
if (!encoder) {
|
|
2654
|
+
throw std::runtime_error("Metal compute encoder allocation failed");
|
|
2655
|
+
}
|
|
2656
|
+
[encoder setComputePipelineState:g_one_sided_boundary_pipeline];
|
|
2657
|
+
[encoder setBuffer:vertices_buffer offset:0 atIndex:0];
|
|
2658
|
+
[encoder setBuffer:offsets_buffer offset:0 atIndex:1];
|
|
2659
|
+
[encoder setBuffer:bboxes_buffer offset:0 atIndex:2];
|
|
2660
|
+
[encoder setBuffer:signed_buffer offset:0 atIndex:3];
|
|
2661
|
+
[encoder setBuffer:segment_buffer offset:0 atIndex:4];
|
|
2662
|
+
[encoder setBuffer:index_buffer offset:0 atIndex:5];
|
|
2663
|
+
[encoder setBuffer:out_edges_buffer offset:0 atIndex:6];
|
|
2664
|
+
[encoder setBuffer:out_keep_buffer offset:0 atIndex:7];
|
|
2665
|
+
[encoder setBuffer:params_buffer offset:0 atIndex:8];
|
|
2666
|
+
NSUInteger group_width = g_one_sided_boundary_pipeline.maxTotalThreadsPerThreadgroup;
|
|
2667
|
+
if (group_width > 256) {
|
|
2668
|
+
group_width = 256;
|
|
2669
|
+
}
|
|
2670
|
+
if (group_width == 0) {
|
|
2671
|
+
group_width = 1;
|
|
2672
|
+
}
|
|
2673
|
+
[encoder dispatchThreads:MTLSizeMake(static_cast<NSUInteger>(segment_count), 1, 1) threadsPerThreadgroup:MTLSizeMake(group_width, 1, 1)];
|
|
2674
|
+
[encoder endEncoding];
|
|
2675
|
+
[command_buffer commit];
|
|
2676
|
+
[command_buffer waitUntilCompleted];
|
|
2677
|
+
if (command_buffer.status == MTLCommandBufferStatusError) {
|
|
2678
|
+
NSString* message = command_buffer.error ? [command_buffer.error localizedDescription] : @"Metal one-sided boundary command failed";
|
|
2679
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
2680
|
+
}
|
|
2681
|
+
const auto* out_edges_float = reinterpret_cast<const float*>([out_edges_buffer contents]);
|
|
2682
|
+
for (std::size_t index = 0; index < segment_count_size * 4; ++index) {
|
|
2683
|
+
out_edges[index] = static_cast<double>(out_edges_float[index]);
|
|
2684
|
+
}
|
|
2685
|
+
std::memcpy(out_keep, [out_keep_buffer contents], segment_count_size * sizeof(std::uint8_t));
|
|
2686
|
+
return 0;
|
|
2687
|
+
} catch (const std::exception& exc) {
|
|
2688
|
+
set_error(exc.what());
|
|
2689
|
+
return 1;
|
|
2690
|
+
} catch (...) {
|
|
2691
|
+
set_error("unknown Metal one-sided boundary edge failure");
|
|
2692
|
+
return 1;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
int gds_metal_mixed_boundary_edges(
|
|
2698
|
+
const std::int64_t* vertices_xy,
|
|
2699
|
+
std::uint64_t vertex_count,
|
|
2700
|
+
const std::uint64_t* polygon_offsets,
|
|
2701
|
+
const std::int64_t* bboxes,
|
|
2702
|
+
const std::uint8_t* polygon_sides,
|
|
2703
|
+
std::uint64_t polygon_count,
|
|
2704
|
+
const double* segment_points,
|
|
2705
|
+
std::uint64_t segment_count,
|
|
2706
|
+
std::uint8_t direction,
|
|
2707
|
+
double* out_edges,
|
|
2708
|
+
std::uint8_t* out_keep
|
|
2709
|
+
) {
|
|
2710
|
+
@autoreleasepool {
|
|
2711
|
+
try {
|
|
2712
|
+
if (!out_edges || !out_keep) {
|
|
2713
|
+
throw std::runtime_error("mixed boundary output pointer is null");
|
|
2714
|
+
}
|
|
2715
|
+
if (direction != 1 && direction != 2) {
|
|
2716
|
+
throw std::runtime_error("mixed boundary direction must be 1 or 2");
|
|
2717
|
+
}
|
|
2718
|
+
if (segment_count == 0) {
|
|
2719
|
+
return 0;
|
|
2720
|
+
}
|
|
2721
|
+
if (!polygon_offsets || !bboxes || !polygon_sides || !segment_points || (vertex_count && !vertices_xy)) {
|
|
2722
|
+
throw std::runtime_error("mixed boundary input contains null pointer");
|
|
2723
|
+
}
|
|
2724
|
+
std::lock_guard<std::mutex> lock(g_mutex);
|
|
2725
|
+
ensure_pipeline_locked();
|
|
2726
|
+
const std::size_t vertex_count_size = static_cast<std::size_t>(vertex_count);
|
|
2727
|
+
const std::size_t polygon_count_size = static_cast<std::size_t>(polygon_count);
|
|
2728
|
+
const std::size_t segment_count_size = static_cast<std::size_t>(segment_count);
|
|
2729
|
+
std::vector<float> segment_points_float(segment_count_size * 4);
|
|
2730
|
+
for (std::size_t index = 0; index < segment_points_float.size(); ++index) {
|
|
2731
|
+
segment_points_float[index] = static_cast<float>(segment_points[index]);
|
|
2732
|
+
}
|
|
2733
|
+
id<MTLBuffer> vertices_buffer = make_input_buffer(vertices_xy, vertex_count_size * 2 * sizeof(std::int64_t));
|
|
2734
|
+
id<MTLBuffer> offsets_buffer = make_input_buffer(polygon_offsets, (polygon_count_size + 1) * sizeof(std::uint64_t));
|
|
2735
|
+
id<MTLBuffer> bboxes_buffer = make_input_buffer(bboxes, polygon_count_size * 4 * sizeof(std::int64_t));
|
|
2736
|
+
id<MTLBuffer> sides_buffer = make_input_buffer(polygon_sides, polygon_count_size * sizeof(std::uint8_t));
|
|
2737
|
+
id<MTLBuffer> segment_buffer = make_input_buffer(segment_points_float.data(), segment_points_float.size() * sizeof(float));
|
|
2738
|
+
id<MTLBuffer> out_edges_buffer = make_output_buffer(segment_count_size * 4 * sizeof(float));
|
|
2739
|
+
id<MTLBuffer> out_keep_buffer = make_output_buffer(segment_count_size * sizeof(std::uint8_t));
|
|
2740
|
+
BoundaryEdgeParamsHost params{vertex_count, polygon_count, segment_count, direction};
|
|
2741
|
+
id<MTLBuffer> params_buffer = make_input_buffer(¶ms, sizeof(params));
|
|
2742
|
+
|
|
2743
|
+
id<MTLCommandBuffer> command_buffer = [g_queue commandBuffer];
|
|
2744
|
+
if (!command_buffer) {
|
|
2745
|
+
throw std::runtime_error("Metal command buffer allocation failed");
|
|
2746
|
+
}
|
|
2747
|
+
id<MTLComputeCommandEncoder> encoder = [command_buffer computeCommandEncoder];
|
|
2748
|
+
if (!encoder) {
|
|
2749
|
+
throw std::runtime_error("Metal compute encoder allocation failed");
|
|
2750
|
+
}
|
|
2751
|
+
[encoder setComputePipelineState:g_mixed_boundary_pipeline];
|
|
2752
|
+
[encoder setBuffer:vertices_buffer offset:0 atIndex:0];
|
|
2753
|
+
[encoder setBuffer:offsets_buffer offset:0 atIndex:1];
|
|
2754
|
+
[encoder setBuffer:bboxes_buffer offset:0 atIndex:2];
|
|
2755
|
+
[encoder setBuffer:sides_buffer offset:0 atIndex:3];
|
|
2756
|
+
[encoder setBuffer:segment_buffer offset:0 atIndex:4];
|
|
2757
|
+
[encoder setBuffer:out_edges_buffer offset:0 atIndex:5];
|
|
2758
|
+
[encoder setBuffer:out_keep_buffer offset:0 atIndex:6];
|
|
2759
|
+
[encoder setBuffer:params_buffer offset:0 atIndex:7];
|
|
2760
|
+
NSUInteger group_width = g_mixed_boundary_pipeline.maxTotalThreadsPerThreadgroup;
|
|
2761
|
+
if (group_width > 256) {
|
|
2762
|
+
group_width = 256;
|
|
2763
|
+
}
|
|
2764
|
+
if (group_width == 0) {
|
|
2765
|
+
group_width = 1;
|
|
2766
|
+
}
|
|
2767
|
+
[encoder dispatchThreads:MTLSizeMake(static_cast<NSUInteger>(segment_count), 1, 1) threadsPerThreadgroup:MTLSizeMake(group_width, 1, 1)];
|
|
2768
|
+
[encoder endEncoding];
|
|
2769
|
+
[command_buffer commit];
|
|
2770
|
+
[command_buffer waitUntilCompleted];
|
|
2771
|
+
if (command_buffer.status == MTLCommandBufferStatusError) {
|
|
2772
|
+
NSString* message = command_buffer.error ? [command_buffer.error localizedDescription] : @"Metal mixed boundary command failed";
|
|
2773
|
+
throw std::runtime_error([[message description] UTF8String]);
|
|
2774
|
+
}
|
|
2775
|
+
const auto* out_edges_float = reinterpret_cast<const float*>([out_edges_buffer contents]);
|
|
2776
|
+
for (std::size_t index = 0; index < segment_count_size * 4; ++index) {
|
|
2777
|
+
out_edges[index] = static_cast<double>(out_edges_float[index]);
|
|
2778
|
+
}
|
|
2779
|
+
std::memcpy(out_keep, [out_keep_buffer contents], segment_count_size * sizeof(std::uint8_t));
|
|
2780
|
+
return 0;
|
|
2781
|
+
} catch (const std::exception& exc) {
|
|
2782
|
+
set_error(exc.what());
|
|
2783
|
+
return 1;
|
|
2784
|
+
} catch (...) {
|
|
2785
|
+
set_error("unknown Metal mixed boundary edge failure");
|
|
2786
|
+
return 1;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
} // extern "C"
|