Compute Shader через ComputeShader API (WebGPU)

Babylon.js предоставляет интеграцию с современными графическими API, включая WebGPU, что позволяет использовать Compute Shaders для высокопроизводительных вычислений на GPU. Compute Shader — это специальный тип шейдера, предназначенный для параллельной обработки данных, не привязанный к визуализации сцены напрямую, но способный существенно ускорять вычисления.


Инициализация Compute Shader

Для работы с Compute Shader необходимо использовать объект ComputeShader:

const computeShader = new BABYLON.ComputeShader("computeShader", scene, {
    computeSource: `
        @group(0) @binding(0) var<storage, read_write> dat a: array<f32>;

        @compute @workgroup_size(64)
        fn main(@builtin(global_invocation_id) id: vec3<u32>) {
            let idx = id.x;
            data[idx] = data[idx] * 2.0;
        }
    `
});

Ключевые моменты:

  • computeSource — GLSL или WGSL код Compute Shader.
  • @workgroup_size(64) — размер группы потоков, оптимальный для параллельного исполнения.
  • @builtin(global_invocation_id) — уникальный идентификатор потока для индексирования данных.

Создание и управление буферами

Compute Shader работает с буферами (GPUBuffer), которые передают данные между CPU и GPU. В Babylon.js используется RawTexture или специализированный StorageBuffer:

const dataArray = new Float32Array(1024);
for (let i = 0; i < dataArray.length; i++) {
    dataArray[i] = i;
}

const storageBuffer = new BABYLON.StorageBuffer(engine, dataArray, true);
computeShader.setStorageBuffer("data", storageBuffer);

Особенности:

  • Буфер должен иметь правильное выравнивание по размеру и типу данных.
  • true в конструкторе буфера указывает на возможность чтения и записи на GPU.

Запуск Compute Shader

Для выполнения шейдера используется метод dispatch:

const numThreads = 1024;
const workgroupSize = 64;
computeShader.dispatch(Math.ceil(numThreads / workgroupSize));

Пояснения:

  • dispatch указывает количество рабочих групп.
  • Общее число потоков = workgroupSize * количество групп.
  • Math.ceil гарантирует обработку всех элементов массива.

Чтение результатов с GPU

После завершения вычислений данные необходимо считывать обратно на CPU:

storageBuffer.readData().then((result) => {
    console.log("Результат:", result);
});

Особенности работы с асинхронностью:

  • Чтение данных происходит асинхронно.
  • Для больших массивов желательно использовать mapping или double buffering, чтобы избежать блокировок CPU.

Взаимодействие с другими шейдерами

Compute Shader может быть частью сложного пайплайна:

  • Post-processing: вычисление физических эффектов, генерация текстур.
  • Simulation: частицы, жидкости, мягкие тела.
  • Procedural generation: генерация высотных карт, процедурных текстур.
const texture = new BABYLON.RawTexture2DArray(
    storageBuffer.getData(),
    32,
    32,
    1,
    scene,
    false,
    false,
    BABYLON.Texture.NEAREST_SAMPLINGMODE,
    BABYLON.Engine.TEXTUREFORMAT_R32F
);

Буфер можно напрямую использовать для создания текстур или атрибутов вершин, избегая лишнего копирования.


Оптимизация работы Compute Shader

  • Размер рабочей группы: экспериментировать с @workgroup_size для максимальной загрузки GPU.
  • Буферизация: использовать несколько буферов для параллельной генерации данных и чтения на CPU.
  • Минимизация синхронизации: избегать частых операций чтения/записи между CPU и GPU.

Примеры практического использования

  1. Обработка частиц:
@compute @workgroup_size(128)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    positions[id.x] += velocities[id.x];
}
  1. Фильтрация изображения:
@compute @workgroup_size(16, 16)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
    let x = id.x;
    let y = id.y;
    output[y * width + x] = input[y * width + x] * 0.5;
}

Интеграция с рендерингом Babylon.js

Compute Shader в Babylon.js легко интегрируется с материалами и текстурами:

material.albedoTexture = new BABYLON.RawTexture(
    storageBuffer.getData(),
    width,
    height,
    BABYLON.Engine.TEXTUREFORMAT_R32G32B32A32,
    scene,
    false
);

Такой подход позволяет GPU генерировать данные, которые сразу используются для визуализации без промежуточного копирования на CPU.


ComputeShader API Babylon.js открывает широкие возможности для производительных вычислений на GPU, позволяя реализовать сложные симуляции, процедурную генерацию и высокопроизводительный пост-обработочный рендеринг прямо в браузере через WebGPU.