WebGPU in C++


First Steps


C++ Configuration


We need to create a structure for the project, we used the help of CMake to link libraries
We also make use of GLFW for a window


Basic concepts



Buffers


Buffers are a way that allows for the CPU and GPU to share data by allocatin memory and mapping it to make sure that there are no issues when multithreading.

C++


We will use the C++ language to test the API

Shaders


Shaders are compiled in the GPU and the allow us to edit and manipulate our test objects. The CPU provides constant data for the Shader to manipulate.

Pipelines


The pipeline is the process of creating the graphics that will end up displaying on the screen. From creating the vertices and their data to rasterization and depth-test to end up with a 3D image.

Uniforms


Uniforms are the constants that are passed to the shader with which they can do a number of things such as map textures or add vertices to the model.

Bindings


The bindings in WebGPU is similar to how the ID works when creating uniforms in OpenGL. To send a uniform to a shader you need to bind the layout.


GPU Instance


First thing to do is to get an instance. This is our access point to the GPU.
It should be noted that we are going to work within this function.

          
 
/////////////////////////////////////////////////////////////////////////
WGPUInstanceDescriptor desc = {};
desc.nextInChain = nullptr;

WGPUInstance instance = wgpuCreateInstance(&desc);
/////////////////////////////////////////////////////////////////////////
            
        


Hardware Features and Resources, Adapter


To create the context we need to know the capacities of the hardware and we also need an entry point to the resources of it. We need an Adapter

          
//adapter
///////////////////////////////////////////////////////////////////////////////////
WGPUSurface surface = glfwGetWGPUSurface(instance, window);
    
WGPURequestAdapterOptions adapterOpts = {};
adapterOpts.nextInChain = nullptr;
adapterOpts.compatibleSurface = surface;
WGPUAdapter adapter = requestAdapter(instance, &adapterOpts);
///////////////////////////////////////////////////////////////////////////////////
          
        


Buffers and Pipeline, Device


We now need access to the resources. We obtain them through a device.
Here lives every object of the context.

          
//device
///////////////////////////////////////////////////////////////////////////////////
WGPUDeviceDescriptor deviceDesc = {};
deviceDesc.nextInChain = nullptr;
deviceDesc.label = "My Device";
deviceDesc.requiredFeaturesCount = 0;
deviceDesc.requiredLimits = nullptr;
deviceDesc.defaultQueue.nextInChain = nullptr;
deviceDesc.defaultQueue.label = "The default queue";
WGPUDevice device = requestDevice(adapter, &deviceDesc);
    
WGPUQueue queue = wgpuDeviceGetQueue(device);    
///////////////////////////////////////////////////////////////////////////////////
          
        


Rendered images, Swap Chain


The images that are being presented and the ones that are about to be live in a Swap Chain.

          
//swap chain
///////////////////////////////////////////////////////////////////////////////////
WGPUSwapChainDescriptor swapChainDesc = {};
swapChainDesc.width = 640;
swapChainDesc.height = 480;
    
swapChainDesc.usage = WGPUTextureUsage_RenderAttachment;
    
WGPUTextureFormat swapChainFormat = wgpuSurfaceGetPreferredFormat(surface, adapter);
swapChainDesc.format = swapChainFormat;
    
swapChainDesc.presentMode = WGPUPresentMode_Fifo;
    
WGPUSwapChain swapChain = wgpuDeviceCreateSwapChain(device, surface, &swapChainDesc);
///////////////////////////////////////////////////////////////////////////////////
          
        


Encoder & Command QUeue


Here the capabilites of WebGPU start to shine. WebGPU makes work more efficient between the CPU and GPU by providing to the GPU a queue of commands for it to run. This gets rid of idle time that the CPU would normally have by waiting for the GPU to do its thing.

          
//encoder
///////////////////////////////////////////////////////////////////////////////////
WGPUCommandEncoderDescriptor commandEncoderDesc = {};
commandEncoderDesc.nextInChain = nullptr;
commandEncoderDesc.label = "Command Encoder";
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &commandEncoderDesc);
///////////////////////////////////////////////////////////////////////////////////
          
//Command queue
///////////////////////////////////////////////////////////////////////////////////
WGPURenderPassDescriptor renderPassDesc = {};
        
WGPURenderPassColorAttachment renderPassColorAttachment = {};
renderPassColorAttachment.view = nextTexture;
renderPassColorAttachment.resolveTarget = nullptr;
renderPassColorAttachment.loadOp = WGPULoadOp_Clear;
renderPassColorAttachment.storeOp = WGPUStoreOp_Store;
renderPassColorAttachment.clearValue = WGPUColor{ 0.9, 0.1, 0.2, 1.0 };
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &renderPassColorAttachment;
        
renderPassDesc.depthStencilAttachment = nullptr;
        
renderPassDesc.timestampWriteCount = 0;
renderPassDesc.timestampWrites = nullptr;
        
renderPassDesc.nextInChain = nullptr;
        
WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderEnd(renderPass);
        
wgpuTextureViewDrop(nextTexture);
        
WGPUCommandBufferDescriptor cmdBufferDescriptor = {};
cmdBufferDescriptor.nextInChain = nullptr;
cmdBufferDescriptor.label = "Command buffer";
WGPUCommandBuffer command = wgpuCommandEncoderFinish(encoder, &cmdBufferDescriptor);
wgpuQueueSubmit(queue, 1, &command);
        
wgpuSwapChainPresent(swapChain);
///////////////////////////////////////////////////////////////////////////////////
          
        


Colored Screen


Full Example code. To use it make sure you have the gl-Matrix library.