Skip to content

Commit 878c21b

Browse files
authored
Merge pull request #72 from obedojukwu16-jpg/feat/webgpu-accelerated-rendering-issue-59
feat: WebGPU accelerated network graph rendering for 10K+ node topolo…
2 parents 275a56e + ec5b656 commit 878c21b

10 files changed

Lines changed: 2862 additions & 13 deletions

File tree

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/network/MeshTopologyMap.tsx

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,32 @@ export function MeshTopologyMap({
423423
const dragViewport = useRef({ x: 0, y: 0 })
424424
const lastPinchDist = useRef(0)
425425

426+
// ---- Adaptive quality state ----
427+
const [qualityLevel, setQualityLevel] = useState(1)
428+
const frameTimesRef = useRef<number[]>([])
429+
430+
// ---- Adaptive Quality Helper (defined before use in render) ----
431+
const getAdaptiveQualityNodes = useCallback(
432+
(allNodes: NodePosition[], quality: number): NodePosition[] => {
433+
if (quality >= 1) return allNodes
434+
const currentEdges = data.edges
435+
const sorted = [...allNodes].sort((a, b) => {
436+
const aScore =
437+
(a.label ? 30 : 0) +
438+
(a.r ? a.r * 2 : 0) +
439+
(currentEdges.filter((e) => e.source === a.id || e.target === a.id).length * 5)
440+
const bScore =
441+
(b.label ? 30 : 0) +
442+
(b.r ? b.r * 2 : 0) +
443+
(currentEdges.filter((e) => e.source === b.id || e.target === b.id).length * 5)
444+
return bScore - aScore
445+
})
446+
const count = Math.max(1, Math.floor(allNodes.length * quality))
447+
return sorted.slice(0, count)
448+
},
449+
[data.edges],
450+
)
451+
426452
const layout = useMeshLayout(layoutConfig)
427453

428454
const nodes = useMemo(
@@ -490,16 +516,24 @@ export function MeshTopologyMap({
490516
const renderCallbackRef = useRef<() => void>(() => {})
491517

492518
const render = useCallback(() => {
519+
const frameStart = performance.now()
493520
const canvas = canvasRef.current
494521
if (!canvas) return
495522
const currentViewport = viewportRef.current
496523
const si = spatialIndexRef.current
497524
const visibleNodes = si ? si.queryViewport(currentViewport) : nodes
525+
526+
// Adaptive quality: reduce visible nodes when frame budget exceeded
527+
let renderNodes = visibleNodes
528+
if (cfg.adaptiveQualityEnabled) {
529+
renderNodes = getAdaptiveQualityNodes(visibleNodes, qualityLevel)
530+
}
531+
498532
const lod = getLodLevel(currentViewport.zoom, cfg)
499533

500534
const gl = glRef.current
501535
if (gl && renderModeRef.current === 'webgl2') {
502-
renderWebGL2(gl, visibleNodes, edges, currentViewport, cfg, lod, false)
536+
renderWebGL2(gl, renderNodes, edges, currentViewport, cfg, lod, false)
503537
} else {
504538
const ctx2d = canvas.getContext('2d')
505539
if (ctx2d) {
@@ -509,12 +543,32 @@ export function MeshTopologyMap({
509543
window.devicePixelRatio || 1,
510544
0, 0,
511545
)
512-
renderCanvas2D(ctx2d, visibleNodes, edges, currentViewport, cfg, lod, selectedNodeId)
546+
renderCanvas2D(ctx2d, renderNodes, edges, currentViewport, cfg, lod, selectedNodeId)
547+
}
548+
}
549+
550+
// Adaptive quality: track frame times and adjust quality
551+
if (cfg.adaptiveQualityEnabled) {
552+
const frameDuration = performance.now() - frameStart
553+
frameTimesRef.current.push(frameDuration)
554+
if (frameTimesRef.current.length > 30) frameTimesRef.current.shift()
555+
556+
const avgFrameTime =
557+
frameTimesRef.current.reduce((s, t) => s + t, 0) /
558+
frameTimesRef.current.length
559+
if (avgFrameTime > cfg.adaptiveQualityThreshold) {
560+
setQualityLevel((prev) =>
561+
Math.max(0.25, prev - cfg.adaptiveQualityReduceFraction),
562+
)
563+
} else if (avgFrameTime < cfg.adaptiveQualityThreshold * 0.5 && qualityLevel < 1) {
564+
setQualityLevel((prev) =>
565+
Math.min(1, prev + cfg.adaptiveQualityReduceFraction),
566+
)
513567
}
514568
}
515569

516570
animFrameRef.current = requestAnimationFrame(() => renderCallbackRef.current())
517-
}, [nodes, edges, cfg, selectedNodeId])
571+
}, [nodes, edges, cfg, selectedNodeId, qualityLevel, getAdaptiveQualityNodes])
518572

519573
useEffect(() => { renderCallbackRef.current = render }, [render])
520574

0 commit comments

Comments
 (0)