Graphics에 해당하는 글 1

WebGL의 다음 버전인 WebGPU에 관하여..

Graphics|2021. 5. 13. 13:59

WebGL은 웹에서 3D 그래픽을 표현하기 위해 사용하는 API로  OpenGL/ES를 기반으로 만들어져있다. 현재 버전 3.0까지 나와있고 각 브라우저 개발자들은 다음 버전인  WebGPU를 만들고 있다. WebGPU의 탄생은 OpenGL의 다음 버전인 Vulkan이나  Metal과 연관이 되어 있다. OpenGL은 보편적인 3D 그래픽스 API로 게임 부터 CAD까지 쓰일 수 있다. 하지만, Vulkan이나 Metal은 개발자가 GPU에 더 접근하고 최적화할 수 있도록 저수준의 API제공하며  CPU오버헤드를 줄일 수 있도록 디자인되어있다. 그렇다보니, GPU에 대한 더 많은 지식을 요구하고 코드량도 증가해서 배우기가 더 어려워졌다. 예전에 OpenGL driver에 제공되는 기능을 이제는 Vulkan 개발자는 본인이 직접해야 한다.
또한 OpenGL은 에러 처리를 위한 많은 코드가 드라이버에 있지만,  Vulkan은 없다. 그래서 단순하고 코드만 잘 만들면 더 좋은 성능을 얻을 수 있다.
이런 API를 과연 웹 개발자에게도 노출할 필요가 있을까? 초기 애플이  WebKit에 WebGPU를 구현하고 이를 제안할 때, 반대고 있었다. 하지만 지금은 힘을 보아 개발을 진행하고 있다.

Apple에서 만든 WebGPU 데모 코드를 한번 보자.

let gpu;
let commandQueue;
let renderPassDescriptor;
let renderPipelineState;

window.addEventListener("load", init, false);

function init() {

    if (!checkForWebMetal()) {
        return;
    }

    let canvas = document.querySelector("canvas");
    let canvasSize = canvas.getBoundingClientRect();
    canvas.width = canvasSize.width;
    canvas.height = canvasSize.height;

    gpu = canvas.getContext("webmetal");
    commandQueue = gpu.createCommandQueue();

    let library = gpu.createLibrary(document.getElementById("library").text);
    let vertexFunction = library.functionWithName("vertex_main");
    let fragmentFunction = library.functionWithName("fragment_main");

    if (!library || !fragmentFunction || !vertexFunction) {
        return;
    }

    let pipelineDescriptor = new WebMetalRenderPipelineDescriptor();
    pipelineDescriptor.vertexFunction = vertexFunction;
    pipelineDescriptor.fragmentFunction = fragmentFunction;
    // NOTE: Our API proposal has these values as enums, not constant numbers.
    // We haven't got around to implementing the enums yet.
    pipelineDescriptor.colorAttachments[0].pixelFormat = gpu.PixelFormatBGRA8Unorm;

    renderPipelineState = gpu.createRenderPipelineState(pipelineDescriptor);

    renderPassDescriptor = new WebMetalRenderPassDescriptor();
    // NOTE: Our API proposal has some of these values as enums, not constant numbers.
    // We haven't got around to implementing the enums yet.
    renderPassDescriptor.colorAttachments[0].loadAction = gpu.LoadActionClear;
    renderPassDescriptor.colorAttachments[0].storeAction = gpu.StoreActionStore;
    renderPassDescriptor.colorAttachments[0].clearColor = [0.35, 0.65, 0.85, 1.0];

    render();
}

function render() {

    let commandBuffer = commandQueue.createCommandBuffer();
    let drawable = gpu.nextDrawable();
    renderPassDescriptor.colorAttachments[0].texture = drawable.texture;

    let commandEncoder = commandBuffer.createRenderCommandEncoderWithDescriptor(renderPassDescriptor);
    commandEncoder.setRenderPipelineState(renderPipelineState);

    // NOTE: We didn't attach any buffers. We create the geometry in the vertex shader using
    // the vertex ID.

    // NOTE: Our API proposal uses the enum value "triangle" here. We haven't got around to implementing the enums yet.
    commandEncoder.drawPrimitives(gpu.PrimitiveTypeTriangle, 0, 3);

    commandEncoder.endEncoding();
    commandBuffer.presentDrawable(drawable);
    commandBuffer.commit();
}

Vulkan 코드를 만져본 분은 익숙한 코드일 수 있다. commandQueue, Pipeline, CommnadBuffer등 Vulkan에서 사용되는 개념들이 똑같이 WebGPU에 적용되어 있다. 확실히 코드양은 증가했지만, 뭔가 더 최적화할 수 있는 부분은 많아졌다. 

댓글()