// // SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // "use client" import React, { useEffect, useRef, useState, useCallback } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Switch } from '@/components/ui/switch' import { Alert, AlertDescription } from '@/components/ui/alert' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Loader2, Cpu, Monitor, RotateCcw } from 'lucide-react' import { useToast } from '@/hooks/use-toast' import { WebGPUClusteringEngine } from '@/utils/webgpu-clustering' import { ForceGraphWrapper } from './force-graph-wrapper' interface PerformanceMetrics { renderingTime: number clusteringTime?: number totalNodes: number totalLinks: number memoryUsage?: number } interface WebGPU3DViewerProps { graphData: { nodes: any[] links: any[] } | null remoteServiceUrl?: string enableClustering?: boolean onClusteringUpdate?: (metrics: PerformanceMetrics) => void onError?: (error: string) => void } interface RenderingMode { id: 'local' | 'hybrid' name: string description: string available: boolean recommended?: boolean } export function WebGPU3DViewer({ graphData, remoteServiceUrl = 'http://localhost:8083', enableClustering = true, onClusteringUpdate, onError }: WebGPU3DViewerProps) { const [activeMode, setActiveMode] = useState('local') const [isInitializing, setIsInitializing] = useState(true) const [renderingModes, setRenderingModes] = useState([]) const [clusteringEngine, setClusteringEngine] = useState(null) const { toast } = useToast() // Initialize rendering modes and capabilities useEffect(() => { const initializeCapabilities = async () => { try { setIsInitializing(true) // Initialize local WebGPU clustering engine const engine = new WebGPUClusteringEngine([32, 18, 24]) await new Promise(resolve => setTimeout(resolve, 200)) // Give time to initialize setClusteringEngine(engine) // Determine available rendering modes const modes: RenderingMode[] = [ { id: 'local', name: 'Local WebGPU', description: 'Client-side WebGPU clustering and Three.js rendering', available: Boolean(engine.isAvailable()), recommended: true }, { id: 'hybrid', name: 'CPU Fallback', description: 'CPU-based rendering fallback', available: true, recommended: !engine.isAvailable() } ] setRenderingModes(modes) // Auto-select best available mode const recommendedMode = modes.find(m => m.recommended && m.available) const fallbackMode = modes.find(m => m.available) if (recommendedMode) { setActiveMode(recommendedMode.id) toast({ title: "Rendering Mode Selected", description: `Using ${recommendedMode.name} for optimal performance`, }) } else if (fallbackMode) { setActiveMode(fallbackMode.id) toast({ title: "Fallback Mode", description: `Using ${fallbackMode.name} as fallback`, variant: "destructive" }) } else { onError?.('No rendering modes available') } } catch (error) { console.error('Failed to initialize 3D viewer capabilities:', error) onError?.(`Initialization failed: ${error}`) } finally { setIsInitializing(false) } } initializeCapabilities() return () => { if (clusteringEngine) { clusteringEngine.dispose() } if (remoteClient) { remoteClient.dispose() } } }, [remoteServiceUrl]) // Handle clustering updates and performance metrics useEffect(() => { if (graphData && onClusteringUpdate) { const startTime = performance.now() // Simulate clustering time for performance metrics // In a real implementation, this would come from the actual clustering engine const nodeCount = graphData.nodes?.length || 0 const linkCount = graphData.links?.length || 0 if (nodeCount > 0) { // Simulate clustering processing time based on node count const clusteringTime = enableClustering ? Math.max(10, nodeCount * 0.01) : 0 const renderingTime = performance.now() - startTime setTimeout(() => { onClusteringUpdate({ renderingTime, clusteringTime, totalNodes: nodeCount, totalLinks: linkCount, }) }, clusteringTime) } } }, [graphData, enableClustering, onClusteringUpdate]) // Handle mode change const handleModeChange = useCallback((mode: string) => { const selectedMode = renderingModes.find(m => m.id === mode) if (selectedMode && selectedMode.available) { setActiveMode(mode) toast({ title: "Rendering Mode Changed", description: `Switched to ${selectedMode.name}`, }) } }, [renderingModes]) if (isInitializing) { return ( Initializing 3D GPU Viewer

Detecting WebGPU capabilities and remote services...

) } if (renderingModes.length === 0 || !renderingModes.some(m => m.available)) { return ( No Rendering Options Available Neither local WebGPU nor remote GPU services are available. Please ensure WebGPU is supported in your browser or that the remote service is running. ) } return (
{/* Rendering Mode Tabs */} {renderingModes.map((mode) => ( {mode.id === 'local' && } {mode.id === 'hybrid' && } {mode.id === 'webrtc' && } {mode.name} ))} {/* Local WebGPU Mode */} Local WebGPU Clustering + Three.js Rendering Uses your browser's WebGPU for clustering and Three.js for 3D rendering {clusteringEngine && !clusteringEngine.isUsingRemote() ? (
Local WebGPU is available. This provides the best performance with no network latency. {/* Clustering Controls */}
{ // Handle clustering toggle if (onClusteringUpdate && graphData) { const nodeCount = graphData.nodes?.length || 0 const linkCount = graphData.links?.length || 0 onClusteringUpdate({ renderingTime: performance.now() % 100, clusteringTime: checked ? Math.max(10, nodeCount * 0.01) : 0, totalNodes: nodeCount, totalLinks: linkCount, }) } }} />
{enableClustering ? "Enabled" : "Disabled"}
{/* Standard 3D Force Graph */}
{graphData ? ( onError?.(err.message)} /> ) : (

No graph data available

Load graph data to see WebGPU clustering
)}
) : ( Local WebGPU is not available in this browser or environment. )}
{/* Hybrid Mode */} Hybrid GPU/CPU Rendering Server performs GPU clustering, client handles CPU-based Three.js rendering {remoteClient ? (
Remote GPU clustering is available. Clustering will be performed on the server GPU, with results sent to your browser for 3D rendering. {/* Clustering Controls */}
{ // Handle clustering toggle if (onClusteringUpdate && graphData) { const nodeCount = graphData.nodes?.length || 0 const linkCount = graphData.links?.length || 0 onClusteringUpdate({ renderingTime: performance.now() % 100, clusteringTime: checked ? Math.max(15, nodeCount * 0.02) : 0, totalNodes: nodeCount, totalLinks: linkCount, }) } }} />
{enableClustering ? "Server GPU" : "Disabled"}
{/* Enhanced ForceGraphWrapper with remote GPU clustering */}
{graphData ? ( onError?.(err.message)} /> ) : (

No graph data available

Load graph data to see hybrid GPU clustering
)}
) : ( Remote GPU clustering service is not available. )}
{/* Service Status */} {clusteringEngine && ( Service Status

Local WebGPU

{clusteringEngine?.isAvailable() ? 'Available' : 'Not Available'}

Clustering

{clusteringEngine?.isAvailable() ? 'GPU Accelerated' : 'CPU Fallback'}
)}
) }