Самый быстрый способ рендеринга Uint8Array в холст

#javascript #canvas #webgl

#javascript #холст #webgl

Вопрос:

Я получаю Uint8Array сообщение со значениями RGBA, а также некоторую информацию о том, какая часть изображения была обновлена. Каков самый быстрый способ рендеринга этой необработанной растровой информации обратно в холст? Я использовал контекст 2D-рендеринга, чтобы вручную перебирать массив и устанавливать информацию о пикселях, но это невероятно медленно:

 let imageData = new ImageData(1920, 1080);
for (var i=0;i < args.frameBuffer.length; i =4) {
    imageData.data[i]   = args.frameBuffer[i];
    imageData.data[i 1] = args.frameBuffer[i 1];
    imageData.data[i 2] = args.frameBuffer[i 2];
    imageData.data[i 3] = args.frameBuffer[i 3];
}
context.putImageData(imageData, 0, 0, args.dirtyX, args.dirtyY, args.dirtyWidth, args.dirtyHeight);
  

Какой был бы более эффективный способ сделать это? Будет ли WebGL более производительным, чем 2D контекст? Собственный или с помощью чего-то вроде Three.js ?

Комментарии:

1. Взгляните на текстуры . В частности, в методе texImage2D .

2. Для перемещения данных между типизированными массивами вы можете использовать imageDataTo.data.set(imageDataFrom.data) для копирования части данных (rect) использование set и subArray для перемещения строк. Для каждой строки (ImageData — это id) idTo.data.set(idFrom.data.subArray((yFrom*idFrom.width xFrom)*4,width),(yTo*idTo.width xTo)*4) Если вам нужно проверить каждый пиксель, вы можете создать Uint32Array для обработки пикселя за одно чтение / запись с data32 = new Uint32Array(imageData.data.buffer) учетом того, что в игру вступает маленький / большой порядковый номер. Также canvas — это изображение, поэтому вы можете рендерить context.drawImage(canvasFrom,... с холста на холст

Ответ №1:

Вы можете использовать gl.texSubImage2D для обновления текстуры, а затем выделения вашей текстуры целиком. Но у texSubImage2D есть проблемы с производительностью, потому что ему приходится ждать завершения предыдущего рендеринга, прежде чем обновлять текстуру для следующего кадра.

Лучшим методом могло бы быть использование бита preserveDrawingBuffer для webgl, а затем циклическое использование двух текстур с помощью texImage2D. Чтобы отобразить только грязную область, вам нужно настроить данные вершин для drawArrays на основе грязных координат.

WebGL все равно пришлось бы много копировать данных, но, возможно, эти пути копирования оптимизированы лучше, чем putImageData. Но существует серьезная опасность того, что webgl может работать медленнее, потому что драйверы часто оптимизируют для случая использования, когда изображения текстур статичны и используются для многократного рендеринга. Это ускоряет рендеринг, но замедляет загрузку текстурных данных.

Для решения проблемы дорогостоящей загрузки текстур в OpenGL есть несколько расширений, поддерживающих быструю загрузку изображений, но немного более медленный рендеринг. Жаль, что эти расширения непрактичны для webgl.

Но ваш цикл копирования очень похож на то, что вы получаете весь буфер кадра каждый раз, даже если он обновляется лишь частично.

Если бы вы могли сделать что-то вроде

 let imageData = new ImageData(args.frameBuffer, args.dirtyWidth, args.dirtyHeight);
context.putImageData(imageData, args.dirtyX, args.dirtyY);
  

Конечно, ваш текущий интерфейс можно было бы повторить с

 let imageData = new ImageData(args.frameBuffer, 1920, Math.floor((args.frameBuffer.length   4*1919)/(4*1920)));
context.putImageData(imageData, 0,0, args.dirtyX, args.dirtyY, args.dirtyWidth, args.dirtyHeight);
  

Комментарии:

1. Спасибо, что указали, что я использовал весь фреймбуфер целиком. Это уже значительно ускоряет процесс.