1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
import type { FrameRate } from "opencut-wasm";
import type { AnyBaseNode } from "./nodes/base-node";
import { createCanvasSurface } from "./canvas-utils";
import { buildFrameDescriptor } from "./compositor/frame-descriptor";
import { wasmCompositor } from "./compositor/wasm-compositor";
import { resolveRenderTree } from "./resolve";
import {
measureSpanAsync,
measureSpanSync,
onRenderPerfFrameComplete,
} from "@/diagnostics/render-perf";
export type CanvasRendererParams = {
width: number;
height: number;
fps: FrameRate;
};
export class CanvasRenderer {
canvas: OffscreenCanvas;
context: OffscreenCanvasRenderingContext2D;
width: number;
height: number;
fps: FrameRate;
constructor({ width, height, fps }: CanvasRendererParams) {
this.width = width;
this.height = height;
this.fps = fps;
const surface = createCanvasSurface({ width, height });
this.canvas = surface.canvas;
this.context = surface.context;
}
getOutputCanvas(): HTMLCanvasElement {
wasmCompositor.ensureInitialized({
width: this.width,
height: this.height,
});
return wasmCompositor.getCanvas();
}
setSize({ width, height }: { width: number; height: number }) {
this.width = width;
this.height = height;
const surface = createCanvasSurface({ width, height });
this.canvas = surface.canvas;
this.context = surface.context;
}
async render({ node, time }: { node: AnyBaseNode; time: number }) {
await measureSpanAsync({
name: "resolve",
fn: () => resolveRenderTree({ node, renderer: this, time }),
});
const { frame, textures } = await measureSpanAsync({
name: "buildFrame",
fn: () => buildFrameDescriptor({ node, renderer: this }),
});
wasmCompositor.ensureInitialized({
width: this.width,
height: this.height,
});
measureSpanSync({
name: "syncTextures",
fn: () => wasmCompositor.syncTextures(textures),
});
measureSpanSync({
name: "renderFrame",
fn: () => wasmCompositor.render(frame),
});
}
async renderToCanvas({
node,
time,
targetCanvas,
}: {
node: AnyBaseNode;
time: number;
targetCanvas: HTMLCanvasElement;
}) {
await this.render({ node, time });
const ctx = targetCanvas.getContext("2d");
if (!ctx) {
throw new Error("Failed to get target canvas context");
}
measureSpanSync({
name: "drawImage",
fn: () =>
ctx.drawImage(
wasmCompositor.getCanvas(),
0,
0,
targetCanvas.width,
targetCanvas.height,
),
});
onRenderPerfFrameComplete();
}
}
|