WebGL의 다음 버전인 WebGPU에 관하여..
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에 적용되어 있다. 확실히 코드양은 증가했지만, 뭔가 더 최적화할 수 있는 부분은 많아졌다.