Skip to content

Commit 6832998

Browse files
committed
progress
1 parent bb9a53d commit 6832998

4 files changed

Lines changed: 176 additions & 120 deletions

File tree

packages/app/src/routes/app/+page.svelte

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import RangeInput from '$lib/components/RangeInput.svelte';
88
import { page } from '$app/state';
99
import { goto } from '$app/navigation';
10-
import { edgePathBundlingGPUFloydWarshall } from '@bachelor/core/edge-path-bundling/floyd-warshall/gpu';
10+
import { EdgePathBundlingGPUFloydWarshall } from '@bachelor/core/edge-path-bundling/floyd-warshall/gpu';
11+
import { onMount } from 'svelte';
1112
1213
const { device } = getWebGPUState();
1314
const { canvas, context } = getCanvasState();
@@ -18,26 +19,42 @@
1819
});
1920
2021
let maxDistortion = $state<number>(2);
21-
let edgeWeightFactor = $state<number>(1);
22+
let edgeWeightFactor = $state<number>(2);
23+
let epb: EdgePathBundlingGPUFloydWarshall;
2224
2325
canvas.onResize = () => runGPU();
2426
27+
// $effect(() => {
28+
// console.log({ maxDistortion, edgeWeightFactor });
29+
// runGPU();
30+
// });
31+
2532
$effect(() => {
26-
console.log({ maxDistortion, edgeWeightFactor });
27-
runGPU();
33+
console.log({ edgeWeightFactor });
34+
if (!epb) return;
35+
epb.setEdgeWeightFactor(edgeWeightFactor).then(() => runGPU());
2836
});
2937
30-
async function runGPU() {
38+
onMount(async () => {
3139
const graph = await loadGraph(selectedGraph);
3240
const spanner = await loadSpanner(selectedGraph);
3341
34-
console.time('EPB');
35-
const { bundeledEdges } = await edgePathBundlingGPUFloydWarshall(graph, {
36-
device,
42+
epb = new EdgePathBundlingGPUFloydWarshall({
43+
graph,
3744
spanner,
3845
maxDistortion,
3946
edgeWeightFactor,
47+
device,
4048
});
49+
50+
runGPU();
51+
});
52+
53+
async function runGPU() {
54+
if (!epb) return;
55+
56+
console.time('EPB');
57+
const { bundeledEdges, spanner } = await epb.bundle();
4158
console.timeEnd('EPB');
4259
4360
drawGraphAndBundledEdges({ ctx: context, graph: spanner, bundeledEdges });
@@ -69,6 +86,4 @@
6986
<button onclick={runGPU}>Run GPU</button>
7087

7188
<a href="/">back</a>
72-
<a href="/app?graph=migration">old</a>
73-
<a href="/app/new?graph=migration">new</a>
7489
</ControlPanel>

packages/core/src/edge-path-bundling/floyd-warshall/gpu.ts

Lines changed: 81 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,79 +4,98 @@ import { FloydWarshall } from '../../shortest-path/floyd-warshall/FloydWarshall'
44
import { greedySpanner } from '../../spanner/greedy';
55

66
export type EdgePathBundlingGPUFloydWarshallParams = {
7-
device: GPUDevice;
7+
graph: Graph;
88
spanner: Graph;
99
maxDistortion?: number;
1010
edgeWeightFactor?: number;
11+
device: GPUDevice;
1112
};
1213

13-
export async function edgePathBundlingGPUFloydWarshall(
14-
graph: Graph,
15-
{
16-
device,
14+
export class EdgePathBundlingGPUFloydWarshall {
15+
#graph: Graph;
16+
#device: GPUDevice;
17+
#spanner: Graph;
18+
#maxDistortion: number;
19+
#floydWarshall: FloydWarshall;
20+
21+
initialized = false;
22+
23+
constructor({
24+
graph,
1725
spanner,
1826
maxDistortion = 2,
1927
edgeWeightFactor = 1,
20-
}: EdgePathBundlingGPUFloydWarshallParams
21-
) {
22-
// if (!spanner) {
23-
// spanner = greedySpanner(graph, maxDistortion);
24-
// }
25-
26-
console.time('Difference');
27-
const difference: Edge[] = [];
28-
graph.edges.forEach((edge, key) => {
29-
if (!spanner.edges.has(key)) {
30-
difference.push(edge);
31-
}
32-
});
33-
console.timeEnd('Difference');
34-
35-
console.time('Floyd Warshall');
36-
const floydWarshall = new FloydWarshall({ graph: spanner, device, edgeWeightFactor });
37-
console.timeEnd('Floyd Warshall');
38-
39-
console.time('Floyd Warshall Init');
40-
await floydWarshall.init();
41-
console.timeEnd('Floyd Warshall Init');
42-
43-
console.time('Floyd Warshall Compute');
44-
await floydWarshall.compute();
45-
console.timeEnd('Floyd Warshall Compute');
46-
47-
console.time('Floyd Warshall Shortest Paths');
48-
const shortestPaths = await floydWarshall.shortestPaths(difference);
49-
console.timeEnd('Floyd Warshall Shortest Paths');
50-
51-
console.time('Bundeling');
52-
const bundeledEdges: {
53-
edge: Edge;
54-
controlPoints: { x: number; y: number }[];
55-
}[] = [];
56-
57-
let i = 0;
58-
for (const shortestPath of shortestPaths) {
59-
const edge = difference[i];
60-
if (!edge) throw new Error('Edge not found');
61-
62-
if (shortestPath === null) {
63-
throw new Error('Shortest path is null');
64-
}
28+
device,
29+
}: EdgePathBundlingGPUFloydWarshallParams) {
30+
// if (!spanner) {
31+
// this.#spanner = greedySpanner(graph, maxDistortion);
32+
// }
33+
this.#graph = graph;
34+
this.#device = device;
35+
this.#spanner = spanner;
36+
this.#maxDistortion = maxDistortion;
37+
38+
this.#floydWarshall = new FloydWarshall({
39+
graph: spanner,
40+
device,
41+
edgeWeightFactor,
42+
});
43+
}
6544

66-
if (shortestPath.length <= maxDistortion * edge.weight) {
67-
bundeledEdges.push({
68-
edge,
69-
controlPoints: shortestPath.nodes.slice(1, -1).map((nodeIndex) => {
70-
const node = graph.nodes.get(nodeIndex);
71-
if (!node) throw new Error('Node not found');
72-
return { x: node.x, y: node.y };
73-
}),
74-
});
45+
async init() {
46+
if (this.initialized) return;
47+
await this.#floydWarshall.init();
48+
await this.#floydWarshall.compute();
49+
this.initialized = true;
50+
}
51+
52+
async bundle() {
53+
if (!this.initialized) await this.init();
54+
55+
const difference: Edge[] = [];
56+
this.#graph.edges.forEach((edge, key) => {
57+
if (!this.#spanner.edges.has(key)) {
58+
difference.push(edge);
59+
}
60+
});
61+
62+
const shortestPaths = await this.#floydWarshall.shortestPaths(difference);
63+
64+
const bundeledEdges: {
65+
edge: Edge;
66+
controlPoints: { x: number; y: number }[];
67+
}[] = [];
68+
69+
let i = 0;
70+
for (const shortestPath of shortestPaths) {
71+
const edge = difference[i];
72+
if (!edge) throw new Error('Edge not found');
73+
74+
if (shortestPath === null) {
75+
throw new Error('Shortest path is null');
76+
}
77+
78+
if (shortestPath.length <= this.#maxDistortion * edge.weight) {
79+
bundeledEdges.push({
80+
edge,
81+
controlPoints: shortestPath.nodes.slice(1, -1).map((nodeIndex) => {
82+
const node = this.#graph.nodes.get(nodeIndex);
83+
if (!node) throw new Error('Node not found');
84+
return { x: node.x, y: node.y };
85+
}),
86+
});
87+
}
88+
89+
i++;
7590
}
7691

77-
i++;
92+
return { bundeledEdges, spanner: this.#spanner };
7893
}
79-
console.timeEnd('Bundeling');
8094

81-
return { bundeledEdges, spanner };
95+
async setEdgeWeightFactor(value: number) {
96+
this.#floydWarshall.edgeWeightFactor = value;
97+
console.time('compute');
98+
await this.#floydWarshall.compute();
99+
console.timeEnd('compute');
100+
}
82101
}

packages/core/src/shortest-path/floyd-warshall/FloydWarshall.ts

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { AdjacencyMatrix } from '../../AdjacencyMatrix';
22
import { BufferData } from '../../BufferData';
3-
import type { Edge, Graph, Node } from '../../AdjacencyList';
3+
import type { Edge, Graph } from '../../AdjacencyList';
44
import type { Path } from '../../path';
55
import { mapAndReadBuffer } from '../../utils';
66
import shader from './shader.wgsl?raw';
7-
import { writeGPUBuffer } from '../../GPUBuffer';
7+
import { createGPUBuffer, writeGPUBuffer } from '../../GPUBuffer';
88

99
export type FloydWarshallParams = {
1010
graph: Graph;
@@ -31,6 +31,9 @@ export class FloydWarshall {
3131
#uniformsBufferData: BufferData<{ k: 'uint'; edge_weight_factor: 'float' }>;
3232
#uniformsBuffer: GPUBuffer | undefined;
3333

34+
#pathsBufferData: BufferData<{ start: 'uint'; end: 'uint' }> | undefined;
35+
#pathsBuffer: GPUBuffer | undefined;
36+
3437
constructor({ graph, device, edgeWeightFactor = 1 }: FloydWarshallParams) {
3538
this.graph = graph;
3639
this.#device = device;
@@ -131,7 +134,7 @@ export class FloydWarshall {
131134
});
132135
}
133136

134-
async compute() {
137+
async compute(readBack = false) {
135138
for (let k = 0; k < this.distanceMatrix.size; ++k) {
136139
this.#uniformsBufferData.set({ k });
137140

@@ -157,59 +160,63 @@ export class FloydWarshall {
157160
this.#device.queue.submit([commandBuffer]);
158161
}
159162

160-
const encoder = this.#device.createCommandEncoder({ label: 'compute builtin encoder' });
161-
encoder.copyBufferToBuffer(
162-
this.#distanceMatrixBuffer!,
163-
0,
164-
this.#distanceMatrixReadBuffer!,
165-
0,
166-
this.distanceMatrix.buffer.byteLength
167-
);
163+
if (readBack) {
164+
const encoder = this.#device.createCommandEncoder({ label: 'compute builtin encoder' });
165+
encoder.copyBufferToBuffer(
166+
this.#distanceMatrixBuffer!,
167+
0,
168+
this.#distanceMatrixReadBuffer!,
169+
0,
170+
this.distanceMatrix.buffer.byteLength
171+
);
168172

169-
encoder.copyBufferToBuffer(
170-
this.#nextMatrixBuffer!,
171-
0,
172-
this.#nextMatrixReadBuffer!,
173-
0,
174-
this.nextMatrix.buffer.byteLength
175-
);
173+
encoder.copyBufferToBuffer(
174+
this.#nextMatrixBuffer!,
175+
0,
176+
this.#nextMatrixReadBuffer!,
177+
0,
178+
this.nextMatrix.buffer.byteLength
179+
);
176180

177-
const commandBuffer = encoder.finish();
178-
this.#device.queue.submit([commandBuffer]);
181+
const commandBuffer = encoder.finish();
182+
this.#device.queue.submit([commandBuffer]);
179183

180-
await this.#distanceMatrixReadBuffer!.mapAsync(GPUMapMode.READ);
181-
const distances = new Float32Array(await this.#distanceMatrixReadBuffer!.getMappedRange());
184+
await this.#distanceMatrixReadBuffer!.mapAsync(GPUMapMode.READ);
185+
const distances = new Float32Array(await this.#distanceMatrixReadBuffer!.getMappedRange());
182186

183-
await this.#nextMatrixReadBuffer!.mapAsync(GPUMapMode.READ);
184-
const next = new Uint32Array(await this.#nextMatrixReadBuffer!.getMappedRange());
185-
this.distanceMatrix.values = distances;
186-
this.nextMatrix.values = next;
187+
await this.#nextMatrixReadBuffer!.mapAsync(GPUMapMode.READ);
188+
const next = new Uint32Array(await this.#nextMatrixReadBuffer!.getMappedRange());
189+
this.distanceMatrix.values = distances;
190+
this.nextMatrix.values = next;
191+
}
187192
}
188193

189194
async shortestPaths(paths: { start: number; end: number }[]): Promise<(Path | null)[]> {
190195
console.time('Shortest Paths Buffer Data');
191-
const pathsBufferData = new BufferData(
192-
{
193-
start: 'uint',
194-
end: 'uint',
195-
},
196-
paths.length
197-
);
196+
if (!this.#pathsBufferData) {
197+
this.#pathsBufferData = new BufferData(
198+
{
199+
start: 'uint',
200+
end: 'uint',
201+
},
202+
paths.length
203+
);
198204

199-
for (let i = 0; i < paths.length; i++) {
200-
const { start, end } = paths[i]!;
201-
pathsBufferData.set({ start, end }, i);
205+
for (let i = 0; i < paths.length; i++) {
206+
const { start, end } = paths[i]!;
207+
this.#pathsBufferData.set({ start, end }, i);
208+
}
202209
}
203210
console.timeEnd('Shortest Paths Buffer Data');
204211

205212
console.time('Shortest Paths Buffer Compute');
206-
const pathsBuffer = this.#device.createBuffer({
207-
label: 'Paths Buffer',
208-
size: pathsBufferData.buffer.byteLength,
209-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
210-
});
211-
212-
this.#device.queue.writeBuffer(pathsBuffer, 0, pathsBufferData.buffer);
213+
if (!this.#pathsBuffer) {
214+
this.#pathsBuffer = createGPUBuffer({
215+
device: this.#device,
216+
data: this.#pathsBufferData,
217+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
218+
});
219+
}
213220

214221
const shortestPathsDistancesBuffer = this.#device.createBuffer({
215222
size: paths.length * 4,
@@ -249,7 +256,7 @@ export class FloydWarshall {
249256
entries: [
250257
{ binding: 0, resource: { buffer: this.#distanceMatrixBuffer! } },
251258
{ binding: 1, resource: { buffer: this.#nextMatrixBuffer! } },
252-
{ binding: 3, resource: { buffer: pathsBuffer } },
259+
{ binding: 3, resource: { buffer: this.#pathsBuffer! } },
253260
{ binding: 4, resource: { buffer: shortestPathsDistancesBuffer } },
254261
{ binding: 5, resource: { buffer: shortestPathsNodesBuffer } },
255262
],
@@ -296,7 +303,7 @@ export class FloydWarshall {
296303
const ret: (Path | null)[] = [];
297304

298305
for (let i = 0; i < paths.length; i++) {
299-
const endIndex = pathsBufferData.get('end', i)[0]!;
306+
const endIndex = this.#pathsBufferData.get('end', i)[0]!;
300307

301308
const nodes: number[] = [];
302309

@@ -316,4 +323,13 @@ export class FloydWarshall {
316323

317324
return ret;
318325
}
326+
327+
set edgeWeightFactor(value: number) {
328+
this.#uniformsBufferData.set({ edge_weight_factor: value });
329+
writeGPUBuffer({
330+
device: this.#device,
331+
buffer: this.#uniformsBuffer!,
332+
data: this.#uniformsBufferData,
333+
});
334+
}
319335
}

0 commit comments

Comments
 (0)