download
raw
4.38 kB
import { defineWorkerModule, terminateWorker } from 'troika-worker-utils'
import createSDFGenerator from 'webgl-sdf-generator'
const now = () => (self.performance || Date).now()
const mainThreadGenerator = /*#__PURE__*/ createSDFGenerator()
let warned
/**
* Generate an SDF texture image for a single glyph path, placing the result into a webgl canvas at a
* given location and channel. Utilizes the webgl-sdf-generator external package for GPU-accelerated SDF
* generation when supported.
*/
export function generateSDF(width, height, path, viewBox, distance, exponent, canvas, x, y, channel, useWebGL = true) {
// Allow opt-out
if (!useWebGL) {
return generateSDF_JS_Worker(width, height, path, viewBox, distance, exponent, canvas, x, y, channel)
}
// Attempt GPU-accelerated generation first
return generateSDF_GL(width, height, path, viewBox, distance, exponent, canvas, x, y, channel).then(
null,
err => {
// WebGL failed either due to a hard error or unexpected results; fall back to JS in workers
if (!warned) {
console.warn(`WebGL SDF generation failed, falling back to JS`, err)
warned = true
}
return generateSDF_JS_Worker(width, height, path, viewBox, distance, exponent, canvas, x, y, channel)
}
)
}
const queue = []
const chunkTimeBudget = 5 // ms
let timer = 0
function nextChunk() {
const start = now()
while (queue.length && now() - start < chunkTimeBudget) {
queue.shift()()
}
timer = queue.length ? setTimeout(nextChunk, 0) : 0
}
/**
* WebGL-based implementation executed on the main thread. Requests are executed in time-bounded
* macrotask chunks to allow render frames to execute in between.
*/
const generateSDF_GL = (...args) => {
return new Promise((resolve, reject) => {
queue.push(() => {
const start = now()
try {
mainThreadGenerator.webgl.generateIntoCanvas(...args)
resolve({ timing: now() - start })
} catch (err) {
reject(err)
}
})
if (!timer) {
timer = setTimeout(nextChunk, 0)
}
})
}
const threadCount = 4 // how many workers to spawn
const idleTimeout = 2000 // workers will be terminated after being idle this many milliseconds
const threads = {}
let callNum = 0
/**
* Fallback JS-based implementation, fanned out to a number of worker threads for parallelism
*/
function generateSDF_JS_Worker(width, height, path, viewBox, distance, exponent, canvas, x, y, channel) {
const workerId = 'TroikaTextSDFGenerator_JS_' + ((callNum++) % threadCount)
let thread = threads[workerId]
if (!thread) {
thread = threads[workerId] = {
workerModule: defineWorkerModule({
name: workerId,
workerId,
dependencies: [
createSDFGenerator,
now
],
init(_createSDFGenerator, now) {
const generate = _createSDFGenerator().javascript.generate
return function (...args) {
const start = now()
const textureData = generate(...args)
return {
textureData,
timing: now() - start
}
}
},
getTransferables(result) {
return [result.textureData.buffer]
}
}),
requests: 0,
idleTimer: null
}
}
thread.requests++
clearTimeout(thread.idleTimer)
return thread.workerModule(width, height, path, viewBox, distance, exponent)
.then(({ textureData, timing }) => {
// copy result data into the canvas
const start = now()
// expand single-channel data into rgba
const imageData = new Uint8Array(textureData.length * 4)
for (let i = 0; i < textureData.length; i++) {
imageData[i * 4 + channel] = textureData[i]
}
mainThreadGenerator.webglUtils.renderImageData(canvas, imageData, x, y, width, height, 1 << (3 - channel))
timing += now() - start
// clean up workers after a while
if (--thread.requests === 0) {
thread.idleTimer = setTimeout(() => { terminateWorker(workerId) }, idleTimeout)
}
return { timing }
})
}
export function warmUpSDFCanvas(canvas) {
if (!canvas._warm) {
mainThreadGenerator.webgl.isSupported(canvas)
canvas._warm = true
}
}
export const resizeWebGLCanvasWithoutClearing = mainThreadGenerator.webglUtils.resizeWebGLCanvasWithoutClearing

Xet Storage Details

Size:
4.38 kB
·
Xet hash:
40b75ebd5f49898d8a88b645861ff0c793cf0cbb17e03bd42cdaa3527db4f507

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.