Мы работаем над веб-приложением визуализации, использующим силу d3 для рисования сети на холсте. Поскольку каждый узел содержит много информации, и поскольку он интенсивно перегружал процессор для каждого кадра, мы реализовали кеш, где каждый узел нарисован на его холсте (не связан с DOM) один раз для каждого уровня масштабирования. Затем эти холсты нарисованы на основном холсте (связанном с DOM) в позиции узла.
Мы довольны усилением скорости, даже если результат в настоящее время интенсивный (особенно на дисплеях hidpi (сетчатка), где плотность пикселей может составлять 2 или 3).
Но теперь у нас возникла проблема с браузерами на iOS, где процесс выходит из строя после нескольких взаимодействий с интерфейсом. К моему воспоминанию, это не проблема со старой версией (до iOS12), но у меня нет никакого обновленного устройства, чтобы подтвердить это.
Я думаю, что этот код суммирует проблему:
const { range } = require('d3-array')
// create a 1MB image
const createImage = () => {
const size = 512
const canvas = document.createElement('canvas')
canvas.height = size
canvas.width = size
const ctx = canvas.getContext('2d')
ctx.strokeRect(0, 0, size, size)
return canvas
}
const createImages = i => {
// create i * 1MB images
let ctxs = range(i).map(() => {
return createImage()
})
console.log('done for ${ctxs.length} MB')
ctxs = null
}
window.cis = createImages
Затем на iPad и в инспекторе:
> cis(256)
[Log] done for 256 MB (main-a9168dc888c2e24bbaf3.bundle.js, line 11317)
< undefined
> cis(1)
[Warning] Total canvas memory use exceeds the maximum limit (256 MB). (main-a9168dc888c2e24bbaf3.bundle.js, line 11307)
< TypeError: null is not an object (evaluating 'ctx.strokeRect')
Будучи, я создаю холст размером 256 х 1 МБ, все идет хорошо, но я создаю еще один, а canvas.getContext возвращает нулевой указатель. Тогда невозможно создать еще один холст.
Предел, похоже, связан с устройством, так как на iPad его 256 МБ, а на iPhone X - 288 МБ.
> cis(288)
[Log] done for 288 MB (main-a9168dc888c2e24bbaf3.bundle.js, line 11317)
< undefined
> cis(1)
[Warning] Total canvas memory use exceeds the maximum limit (288 MB). (main-a9168dc888c2e24bbaf3.bundle.js, line 11307)
< TypeError: null is not an object (evaluating 'ctx.strokeRect')
Поскольку это кеш, я должен удалять некоторые элементы, но Im not (поскольку установка ctxs или ctx в null должна запускать GC, но это не решает проблему).
Единственной релевантной страницей, которую я нашел по этой проблеме, является страница исходного кода webkit: HTMLCanvasElement.cpp.
Я подозреваю, что проблема может исходить от самого веб-кита, но Im хотел бы быть уверен, прежде чем отправлять на веб-сайт вопрос трекера.
Есть ли другой способ уничтожить контексты холста?
Заранее благодарю за любую идею, указатель,...
редактирует:
Чтобы добавить некоторую информацию, я попробовал другие браузеры. Safari 12 имеет ту же проблему на macOS, даже если предел выше (1/4 от памяти компьютера, как указано в источниках webkit). Я также пробовал с последней версией webkit (236590) без лишней удачи. Но код работает на Firefox 62 и Chrome 69.
Я уточнил тестовый код, поэтому его можно выполнить непосредственно с консоли отладчика. Было бы очень полезно, если бы кто-то мог проверить код на более раннем сафари (например, 11).
let counter = 0
// create a 1MB image
const createImage = () => {
const size = 512
const canvas = document.createElement('canvas')
canvas.height = size
canvas.width = size
const ctx = canvas.getContext('2d')
ctx.strokeRect(0, 0, size, size)
return canvas
}
const createImages = n => {
// create n * 1MB images
const ctxs = []
for( let i=0 ; i<n ; i++ ){
ctxs.push(createImage())
}
console.log('done for ${ctxs.length} MB')
}
const process = (frequency,size) => {
setInterval(()=>{
createImages(size)
counter+=size
console.log('total ${counter}')
},frequency)
}
process(2000,1000)