Remove WebGPU backend

This commit is contained in:
Danila Poyarkov 2026-03-27 22:01:47 +03:00
parent 25788dae36
commit d52d38cb3b
4 changed files with 9 additions and 108 deletions

View file

@ -213,7 +213,6 @@ OpenPencil is the alternative: open source (MIT), reads .fig files natively, eve
- Grid child positioning UI — column/row span controls, grid overlay on canvas
- Skewing and OkHCL color support
- Windows code signing (Azure Authenticode certificates)
- Experimental WebGPU/Graphite rendering backend
## Contributing

View file

@ -4,67 +4,21 @@ import { IS_BROWSER } from './constants'
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 (!IS_BROWSER) 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 =
'CanvasKitInit' in globalThis
? ((globalThis as typeof globalThis & { CanvasKitInit: unknown })
.CanvasKitInit as (opts?: {
locateFile?: (file: string) => string
}) => Promise<CanvasKit>)
: null
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 (IS_BROWSER) 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
})
}
instance = await CanvasKitInit({
locateFile: options?.locateFile ?? defaultLocate
})
return instance
}

View file

@ -133,7 +133,7 @@ export {
export type { FrameCapture, NodeProfile } from './profiler'
export { computeLayout, computeAllLayouts, setTextMeasurer } from './layout'
export type { TextMeasurer } from './layout'
export { getCanvasKit, getGpuBackend, type CanvasKitOptions, type GpuBackend } from './canvaskit'
export { getCanvasKit, type CanvasKitOptions } from './canvaskit'
export {
FONT_WEIGHT_NAMES,
collectFontKeys,

View file

@ -1,44 +1,13 @@
import { useRafFn, useResizeObserver } from '@vueuse/core'
import { onMounted, onScopeDispose, type Ref } from 'vue'
import { getCanvasKit, getGpuBackend, SkiaRenderer } from '@open-pencil/core'
import { getCanvasKit, SkiaRenderer } from '@open-pencil/core'
import { useViewportKind } from '../viewport/useViewportKind'
import type { Editor } from '@open-pencil/core/editor'
import type { CanvasKit } from 'canvaskit-wasm'
interface WebGPUContext {
device: GPUDevice
deviceContext: unknown
}
interface CanvasKitWebGPU {
MakeGPUDeviceContext(device: GPUDevice): unknown
MakeGPUCanvasContext(ctx: unknown, canvas: HTMLCanvasElement, opts?: unknown): unknown
MakeGPUCanvasSurface(
ctx: unknown,
colorSpace?: unknown,
width?: number,
height?: number
): ReturnType<CanvasKit['MakeSurface']>
}
function asWebGPU(ck: CanvasKit): CanvasKitWebGPU {
return ck as unknown as CanvasKitWebGPU
}
async function initWebGPU(ck: CanvasKit): Promise<WebGPUContext | null> {
if (!('gpu' in navigator)) return null
const adapter = await navigator.gpu.requestAdapter()
if (!adapter) return null
const device = await adapter.requestDevice()
// oxlint-disable-next-line typescript/no-unnecessary-condition -- WebGPU CanvasKit API may not exist at runtime
const deviceContext = asWebGPU(ck).MakeGPUDeviceContext?.(device)
if (!deviceContext) return null
return { device, deviceContext }
}
/**
* Options for {@link useCanvas}.
*/
@ -76,7 +45,6 @@ export function useCanvas(
) {
let renderer: SkiaRenderer | null = null
let ck: CanvasKit | null = null
let gpuCtx: WebGPUContext | null = null
let glContext: ReturnType<CanvasKit['MakeGrContext']> | null = null
let destroyed = false
let dirty = true
@ -88,17 +56,8 @@ export function useCanvas(
if (!canvas || destroyed) return
ck = await getCanvasKit()
// oxlint-disable-next-line typescript/no-unnecessary-condition -- async race: destroyed may change during await
if (destroyed) return
if (getGpuBackend() === 'webgpu') {
gpuCtx = await initWebGPU(ck)
if (!gpuCtx) {
console.warn('WebGPU init failed, reload without ?gpu=webgpu to use WebGL')
return
}
}
await new Promise((r) => requestAnimationFrame(r))
createSurface(canvas)
await renderer?.loadFonts()
@ -133,21 +92,10 @@ export function useCanvas(
sizeCanvas(canvas)
let surface
if (getGpuBackend() === 'webgpu' && gpuCtx) {
const gpu = asWebGPU(ck)
const canvasCtx = gpu.MakeGPUCanvasContext(gpuCtx.deviceContext, canvas)
surface = gpu.MakeGPUCanvasSurface(canvasCtx, ck.ColorSpace.SRGB, canvas.width, canvas.height)
if (!surface) {
console.error('Failed to create WebGPU surface')
return
}
} else {
surface = makeGLSurface(canvas)
if (!surface) {
console.error('Failed to create WebGL surface')
return
}
const surface = makeGLSurface(canvas)
if (!surface) {
console.error('Failed to create WebGL surface')
return
}
const glCtx = canvas.getContext('webgl2') ?? null