Govern task context and advance execution pointer
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-20 06:02:43 -04:00
parent 380cbed4ff
commit 10640aeb3c
984 changed files with 543475 additions and 327 deletions

View File

@@ -9,6 +9,7 @@ import type { OffscreenViewportRequest, OffscreenViewportResponse } from "./offs
import { PBR_PROFILE, PBR_SHADOW_PROFILE, PBR_TONE_MAPPING } from "./pbr";
import { resolveViewportPixelMetrics, viewportNDC } from "../../../protocol/viewport-dpr";
import { observePointerEvent } from "../../../protocol/pointer-contract";
import { createInputModalState, reduceInputModal, type InputModalState } from "../../../protocol/input-modal";
import type { NonMeshElementKind } from "./nonmesh";
import type { GreasePencilPointPreview, GreasePencilPointRef } from "./grease-pencil";
import type { CurveGizmoFrameIR, CurveGizmoHandleIR } from "../../../protocol/nonmesh-interaction";
@@ -88,6 +89,8 @@ export class OffscreenViewportRenderer implements ViewportBackend {
private readonly onGreasePencilPointSelect?: (point: GreasePencilPointRef, additive: boolean, baseSelectionRevision: number) => void;
private readonly onGreasePencilMarqueeSelect?: (result: GreasePencilMarqueeResultIR, additive: boolean) => void;
private pointer: { id: number; x: number; y: number; moved: boolean } | null = null;
private inputModal: InputModalState = createInputModalState();
private touchNavigationActive = false;
private lastSnapshot: SceneSnapshotIR | null = null;
private lastGeometryBuffers: MeshGeometryBuffer[] | null = null;
private lastNonMeshGeometryBuffers: NonMeshGeometryChunk[] | null = null;
@@ -134,6 +137,7 @@ export class OffscreenViewportRenderer implements ViewportBackend {
canvas.dataset.pbrProfile = PBR_PROFILE;
canvas.dataset.toneMapping = PBR_TONE_MAPPING;
canvas.dataset.shadowMap = PBR_SHADOW_PROFILE;
this.publishInputModal();
}
setSnapshot(snapshot: SceneSnapshotIR, geometryBuffers: MeshGeometryBuffer[] = [], nonMeshGeometryBuffers: NonMeshGeometryChunk[] = []): void {
@@ -252,8 +256,8 @@ export class OffscreenViewportRenderer implements ViewportBackend {
}
private pointerDown = (event: PointerEvent): void => {
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event)); }
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
const observation = this.recordInputModalEvent(event);
if (observation?.pointerType === "touch" && this.inputModal.activePointerIds.length >= 2) this.touchNavigationActive = true;
this.pointer = { id: event.pointerId, x: event.clientX, y: event.clientY, moved: false };
try {
this.canvas.setPointerCapture(event.pointerId);
@@ -274,8 +278,17 @@ export class OffscreenViewportRenderer implements ViewportBackend {
};
private pointerUp = (event: PointerEvent): void => {
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event)); }
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
const observation = this.recordInputModalEvent(event);
const wasTouchNavigation = this.touchNavigationActive;
if (observation?.pointerType === "touch" && this.inputModal.activePointerIds.length === 0) this.touchNavigationActive = false;
if (!observation) {
this.pointer = null;
return;
}
if (event.type === "pointercancel" || observation.pointerType === "pen" || wasTouchNavigation) {
this.pointer = null;
return;
}
if (!this.pointer || this.pointer.id !== event.pointerId) return;
if (!this.pointer.moved) {
const bounds = this.canvas.getBoundingClientRect();
@@ -285,6 +298,37 @@ export class OffscreenViewportRenderer implements ViewportBackend {
this.pointer = null;
};
private recordInputModalEvent(event: PointerEvent): ReturnType<typeof observePointerEvent> | null {
try {
const observation = observePointerEvent(event);
this.canvas.dataset.lastPointer = JSON.stringify(observation);
if (observation.pointerType === "touch" || observation.pointerType === "pen") {
this.inputModal = reduceInputModal(this.inputModal, {
type: event.type as "pointerdown" | "pointerup" | "pointercancel",
pointerType: observation.pointerType,
pointerId: observation.pointerId,
});
this.publishInputModal();
}
return observation;
}
catch {
this.canvas.dataset.lastPointer = "BLOCKED";
this.canvas.dataset.inputModalError = "INPUT_MODAL_EVENT_BLOCKED";
return null;
}
}
private publishInputModal(): void {
this.canvas.dataset.inputModalSchemaVersion = String(this.inputModal.schemaVersion);
this.canvas.dataset.inputModalKind = this.inputModal.kind;
this.canvas.dataset.inputModalActivePointerIds = this.inputModal.activePointerIds.join(",");
this.canvas.dataset.inputModalCancelled = this.inputModal.cancelled ? "1" : "0";
this.canvas.dataset.inputModalNavigationRevision = String(this.inputModal.navigationRevision);
this.canvas.dataset.inputModalMainCommitCount = String(this.inputModal.mainCommitCount);
this.canvas.dataset.inputModalError = "";
}
private wheel = (event: WheelEvent): void => {
event.preventDefault();
this.worker.postMessage({ type: "orbit", deltaX: 0, deltaY: 0, zoom: event.deltaY } satisfies OffscreenViewportRequest);

View File

@@ -71,6 +71,7 @@ import { validatePaintDepthVisibilityRequest, type PaintDepthVisibilityRequestIR
import { samplePaintDepthVisibilityGPU } from "./paint-depth-visibility";
import { resolveViewportPixelMetrics, viewportNDC } from "../../../protocol/viewport-dpr";
import { observePointerEvent } from "../../../protocol/pointer-contract";
import { createInputModalState, reduceInputModal, type InputModalState } from "../../../protocol/input-modal";
export function collectMeshInstanceGroups(snapshot: SceneSnapshotIR, minimumSize = 2): Map<string, string[]> {
const groups = new Map<string, string[]>();
@@ -104,6 +105,7 @@ export class ViewportRenderer {
private readonly textureStore = new GPUTextureStore("THREE_WEBGL2");
private readonly raycaster = new Raycaster();
private readonly pointer = new Vector2();
private inputModal: InputModalState = createInputModalState();
private readonly onSelect?: (objectId: string, additive: boolean) => void;
private readonly onElementSelect?: (meshId: string, mode: MeshElementMode, index: number, additive: boolean, nonMeshKind?: NonMeshElementKind) => void;
private readonly onGreasePencilPointSelect?: (point: GreasePencilPointRef, additive: boolean, baseSelectionRevision: number) => void;
@@ -173,12 +175,14 @@ export class ViewportRenderer {
this.resizeObserver.observe(canvas);
this.canvas.addEventListener("click", this.handleClick);
this.canvas.addEventListener("pointerdown", this.handlePointerObservation);
this.canvas.addEventListener("pointerup", this.handlePointerObservation);
this.canvas.addEventListener("pointercancel", this.handlePointerObservation);
this.canvas.addEventListener("webglcontextlost", this.handleContextLost);
this.canvas.addEventListener("webglcontextrestored", this.handleContextRestored);
this.resize();
this.controls.update();
this.publishCameraState();
this.publishInputModal();
this.renderLoop();
}
@@ -821,6 +825,8 @@ export class ViewportRenderer {
if ("pointerType" in event) {
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event as MouseEvent & { pointerType?: string; pointerId?: number; pressure?: number; tiltX?: number; tiltY?: number; buttons?: number; type?: string })); }
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
const pointerType = (event as MouseEvent & { pointerType?: string }).pointerType;
if ((pointerType === "touch" || pointerType === "pen") && this.inputModal.cancelled) return;
}
const bounds = this.canvas.getBoundingClientRect();
if (bounds.width <= 0 || bounds.height <= 0) return;
@@ -897,10 +903,34 @@ export class ViewportRenderer {
};
private handlePointerObservation = (event: PointerEvent): void => {
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event)); }
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
try {
const observation = observePointerEvent(event);
this.canvas.dataset.lastPointer = JSON.stringify(observation);
if (observation.pointerType === "touch" || observation.pointerType === "pen") {
this.inputModal = reduceInputModal(this.inputModal, {
type: event.type as "pointerdown" | "pointerup" | "pointercancel",
pointerType: observation.pointerType,
pointerId: observation.pointerId,
});
this.publishInputModal();
}
}
catch {
this.canvas.dataset.lastPointer = "BLOCKED";
this.canvas.dataset.inputModalError = "INPUT_MODAL_EVENT_BLOCKED";
}
};
private publishInputModal(): void {
this.canvas.dataset.inputModalSchemaVersion = String(this.inputModal.schemaVersion);
this.canvas.dataset.inputModalKind = this.inputModal.kind;
this.canvas.dataset.inputModalActivePointerIds = this.inputModal.activePointerIds.join(",");
this.canvas.dataset.inputModalCancelled = this.inputModal.cancelled ? "1" : "0";
this.canvas.dataset.inputModalNavigationRevision = String(this.inputModal.navigationRevision);
this.canvas.dataset.inputModalMainCommitCount = String(this.inputModal.mainCommitCount);
this.canvas.dataset.inputModalError = "";
}
private renderLoop = (): void => {
if (this.disposed) return;
if (!this.contextLost) {
@@ -965,6 +995,7 @@ export class ViewportRenderer {
this.resizeObserver.disconnect();
this.canvas.removeEventListener("click", this.handleClick);
this.canvas.removeEventListener("pointerdown", this.handlePointerObservation);
this.canvas.removeEventListener("pointerup", this.handlePointerObservation);
this.canvas.removeEventListener("pointercancel", this.handlePointerObservation);
this.canvas.removeEventListener("webglcontextlost", this.handleContextLost);
this.canvas.removeEventListener("webglcontextrestored", this.handleContextRestored);