perf: speed up world viewer loading
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-05-14 11:46:16 +08:00
parent 89ec01cfb8
commit dee8019efd
6 changed files with 105 additions and 15 deletions

View File

@ -4,6 +4,8 @@ import { buildThreeAssetUrl } from "@/lib/canvas/three-asset-url";
export const runtime = "nodejs";
export const maxDuration = 60;
const THREE_ASSET_BROWSER_CACHE_CONTROL =
"private, max-age=86400, stale-while-revalidate=604800";
function isAllowedModelHost(hostname: string) {
const normalized = hostname.toLowerCase();
@ -144,7 +146,13 @@ export async function GET(request: Request) {
const acceptRanges = upstream.headers.get("accept-ranges");
if (acceptRanges) responseHeaders.set("accept-ranges", acceptRanges);
responseHeaders.set("cache-control", "private, no-store");
const etag = upstream.headers.get("etag");
if (etag) responseHeaders.set("etag", etag);
const lastModified = upstream.headers.get("last-modified");
if (lastModified) responseHeaders.set("last-modified", lastModified);
responseHeaders.set("cache-control", THREE_ASSET_BROWSER_CACHE_CONTROL);
if (shouldRewriteGltf) {
const rewrittenPayload = rewriteGltfPayload(await upstream.text(), targetUrl);

View File

@ -91,6 +91,7 @@ import { cn, fetcher } from "@/lib/utils";
import {
getSortedWorldLabsSplatEntries,
pickBestArchivedWorldLabsSplatUrl,
pickPreferredInlineWorldLabsSplatUrl,
} from "@/lib/worldlabs/splat-utils";
import {
DEFAULT_PUBLIC_THREED_MODEL_ALIAS_CONFIG,
@ -2605,7 +2606,10 @@ export default function CanvasModel3DPage() {
? buildWorldShareUrl(currentTask.shareToken)
: null;
const currentSpzEntries = getSortedWorldLabsSplatEntries(currentWorldAsset?.spzUrls);
const currentPrimarySpzUrl = currentSpzEntries[0]?.[1] || null;
const currentPrimarySpzUrl =
pickPreferredInlineWorldLabsSplatUrl(currentWorldAsset?.spzUrls) ||
currentSpzEntries[0]?.[1] ||
null;
const currentWorldImageUrl =
currentWorldAsset?.panoUrl ||
currentWorldAsset?.thumbnailUrl ||

View File

@ -3,7 +3,7 @@ import { notFound } from "next/navigation";
import { WorldSharePage } from "@/components/canvas/world3d/WorldSharePage";
import { db } from "@/lib/db";
import { model3dAsset } from "@/lib/db/schema";
import { pickBestArchivedWorldLabsSplatUrl } from "@/lib/worldlabs/splat-utils";
import { pickPreferredInlineWorldLabsSplatUrl } from "@/lib/worldlabs/splat-utils";
export const dynamic = "force-dynamic";
@ -86,7 +86,7 @@ export default async function SharedWorldPage({
previewImageUrl: asset.previewImageUrl || assetPayload?.thumbnailUrl || null,
prompt: asset.prompt || null,
semanticsMetadata: assetPayload?.semanticsMetadata || null,
splatUrl: pickBestArchivedWorldLabsSplatUrl(assetPayload?.spzUrls),
splatUrl: pickPreferredInlineWorldLabsSplatUrl(assetPayload?.spzUrls),
title: assetPayload?.displayName || asset.prompt || `World ${asset.taskId.slice(0, 8)}`,
worldUrl: assetPayload?.worldUrl || null,
};

View File

@ -35,6 +35,7 @@ import {
import {
isArchivedWorldLabsAssetUrl,
pickBestArchivedWorldLabsSplatUrl,
pickPreferredInlineWorldLabsSplatUrl,
} from "@/lib/worldlabs/splat-utils";
import { getNodeContentScale } from "@/lib/canvas/node-content-scale";
import {
@ -201,9 +202,17 @@ export function World3DNode({
const activeView = useMemo(() => resolveActiveView(data), [data]);
const availableViews = useMemo(() => resolveAvailableViews(data), [data]);
const renderableSplatUrl = isArchivedWorldLabsAssetUrl(data.splatUrl)
? data.splatUrl
: undefined;
const renderableSplatUrl = useMemo(() => {
const assetPayloadSplatUrl = pickPreferredInlineWorldLabsSplatUrl(
metadataAssetPayload.spzUrls as Record<string, string> | null | undefined
);
if (isArchivedWorldLabsAssetUrl(assetPayloadSplatUrl)) {
return assetPayloadSplatUrl;
}
return isArchivedWorldLabsAssetUrl(data.splatUrl) ? data.splatUrl : undefined;
}, [data.splatUrl, metadataAssetPayload.spzUrls]);
const renderableMeshUrl = isArchivedWorldLabsAssetUrl(data.meshUrl)
? data.meshUrl
: undefined;

View File

@ -166,8 +166,10 @@ function lerpSparkRevealProfile(
};
}
const SPLAT_REVEAL_DURATION_MS = 2600;
const SPLAT_PARTICLE_SAMPLE_LIMIT = 1800;
const SPLAT_REVEAL_DURATION_MS = 900;
const SPLAT_PARTICLE_SAMPLE_LIMIT = 720;
const SPLAT_WORLD_BOUNDS_SAMPLE_LIMIT = 1200;
const SPLAT_FAST_LOAD_SKIP_REVEAL_MS = 1200;
const KEY_TO_MOVEMENT: Partial<Record<string, MovementCommand>> = {
KeyW: "forward",
@ -307,7 +309,7 @@ function getRobustSplatWorldBounds(splatMesh: SparkSplatMesh) {
}
splatMesh.updateMatrixWorld(true);
const targetCount = Math.min(totalSplats, 3200);
const targetCount = Math.min(totalSplats, SPLAT_WORLD_BOUNDS_SAMPLE_LIMIT);
const sampleStep = Math.max(Math.floor(totalSplats / targetCount), 1);
const point = new THREE.Vector3();
const xs: number[] = [];
@ -1063,16 +1065,16 @@ function WorldSplatScene({
onLoadStateChange?.("ready");
const buildParticleCloud = () => {
if (cancelled) return;
if (cancelled || revealPhaseRef.current === "hidden") return;
setParticleCloudData(buildSplatParticleCloudData(splatMesh));
};
if ("requestIdleCallback" in window) {
idleCallbackId = window.requestIdleCallback(buildParticleCloud, {
timeout: 900,
timeout: 250,
});
} else {
timeoutId = globalThis.setTimeout(buildParticleCloud, 120);
timeoutId = globalThis.setTimeout(buildParticleCloud, 160);
}
});
})
@ -1364,15 +1366,20 @@ function WorldSplatViewer({
const [loadState, setLoadState] = useState<"loading" | "ready" | "error">("loading");
const [overlayPhase, setOverlayPhase] = useState<SplatLoadingPhase>("loading");
const resolvedSplatUrl = useMemo(() => buildThreeAssetUrl(splatUrl), [splatUrl]);
const loadStartedAtRef = useRef(0);
useEffect(() => {
loadStartedAtRef.current = performance.now();
setLoadState("loading");
setOverlayPhase("loading");
}, [resolvedSplatUrl]);
useEffect(() => {
if (loadState === "ready") {
setOverlayPhase("revealing");
const elapsed = performance.now() - loadStartedAtRef.current;
setOverlayPhase(
elapsed <= SPLAT_FAST_LOAD_SKIP_REVEAL_MS ? "hidden" : "revealing"
);
}
if (loadState === "loading") {
@ -1421,8 +1428,13 @@ function WorldSplatViewer({
<Canvas
camera={{ position: [0, 1.6, 4], fov: 55 }}
className="h-full w-full bg-[radial-gradient(circle_at_top,_rgba(15,23,42,0.95),_rgba(1,1,1,1)_68%)]"
dpr={[1, 1.25]}
frameloop="always"
gl={{ antialias: false, preserveDrawingBuffer: true }}
gl={{
antialias: false,
powerPreference: "high-performance",
preserveDrawingBuffer: true,
}}
>
<WorldSplatCaptureBridge onCaptureReady={onCaptureReady} />
<WorldSplatScene

View File

@ -59,6 +59,40 @@ export function pickBestWorldLabsSplatUrl(
return pickBestWorldLabsSplatEntry(spzUrls)?.[1] || null;
}
const INLINE_WORLDLABS_SPLAT_TARGET_SCORE = 1_500_000;
function pickClosestResolutionEntry(
entries: Array<[string, string]>,
targetScore: number
) {
if (!entries.length) return null;
const scoredEntries = entries
.map((entry) => ({
entry,
score: parseResolutionScore(entry[0]),
}))
.filter(
(item) =>
Number.isFinite(item.score) &&
item.score > 0 &&
item.score !== Number.MAX_SAFE_INTEGER
);
if (!scoredEntries.length) {
return entries[0] || null;
}
return scoredEntries
.slice()
.sort((left, right) => {
const distanceDelta =
Math.abs(left.score - targetScore) - Math.abs(right.score - targetScore);
if (distanceDelta !== 0) return distanceDelta;
return left.score - right.score;
})[0]?.entry || null;
}
const KNOWN_ARCHIVED_ASSET_HOSTS = [
"blob.vercel-storage.com",
"s3.1080p.hk",
@ -129,3 +163,26 @@ export function pickBestArchivedWorldLabsSplatUrl(
) {
return getArchivedWorldLabsSplatEntries(spzUrls)[0]?.[1] || null;
}
export function pickPreferredInlineWorldLabsSplatEntry(
spzUrls?: Record<string, string> | null
) {
const archivedEntries = getArchivedWorldLabsSplatEntries(spzUrls);
if (archivedEntries.length > 0) {
return pickClosestResolutionEntry(
archivedEntries,
INLINE_WORLDLABS_SPLAT_TARGET_SCORE
);
}
return pickClosestResolutionEntry(
getSortedWorldLabsSplatEntries(spzUrls),
INLINE_WORLDLABS_SPLAT_TARGET_SCORE
);
}
export function pickPreferredInlineWorldLabsSplatUrl(
spzUrls?: Record<string, string> | null
) {
return pickPreferredInlineWorldLabsSplatEntry(spzUrls)?.[1] || null;
}