openpencil/packages/core/src/canvaskit.ts
Danila Poyarkov 8ced3201cf Cache effect ImageFilters and reuse layer paint
Eliminates per-frame WASM allocations for shadows and blurs:
- Reusable effectLayerPaint instead of new Paint() per effect
- ImageFilter cache keyed by params (drop shadow, blur, decal blur)
- Zero allocations during SkPicture recording for cached effects
2026-03-03 10:12:02 +03:00

66 lines
1.9 KiB
TypeScript

import CanvasKitInit, { type CanvasKit } from 'canvaskit-wasm'
let instance: CanvasKit | null = null
export type GpuBackend = 'webgl' | 'webgpu'
let activeBackend: GpuBackend = 'webgl'
export function getGpuBackend(): GpuBackend {
return activeBackend
}
export interface CanvasKitOptions {
locateFile?: (file: string) => string
backend?: GpuBackend
}
function detectBackend(): GpuBackend {
if (typeof window === 'undefined') return 'webgl'
const params = new URLSearchParams(window.location.search)
if (params.get('gpu') === 'webgpu' && 'gpu' in navigator) return 'webgpu'
return 'webgl'
}
function loadCanvasKitWebGPU(): Promise<(opts?: Record<string, unknown>) => Promise<CanvasKit>> {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = '/canvaskit-webgpu/canvaskit.js'
script.onload = () => {
const init = (globalThis as Record<string, unknown>).CanvasKitInit as
(opts?: Record<string, unknown>) => Promise<CanvasKit>
if (!init) return reject(new Error('CanvasKitInit not found after loading WebGPU build'))
resolve(init)
}
script.onerror = () => reject(new Error('Failed to load canvaskit-webgpu/canvaskit.js'))
document.head.appendChild(script)
})
}
export async function getCanvasKit(options?: CanvasKitOptions): Promise<CanvasKit> {
if (instance) return instance
const backend = options?.backend ?? detectBackend()
activeBackend = backend
const defaultLocate = (file: string) => {
if (typeof window !== 'undefined') return `/${file}`
return file
}
if (backend === 'webgpu') {
const init = await loadCanvasKitWebGPU()
instance = await init({
locateFile:
options?.locateFile ??
((file: string) => `/canvaskit-webgpu/${file}`)
})
} else {
instance = await CanvasKitInit({
locateFile: options?.locateFile ?? defaultLocate
})
}
return instance!
}