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);
///////////////////////////////////////////////////////////////////////////////////