Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b483435

Browse files
Add commented main cpp file.
1 parent b6b0160 commit b483435

File tree

2 files changed

+308
-1
lines changed

2 files changed

+308
-1
lines changed
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
# WebGPU Cross Platform App
22

3-
In this video, we go over the code from Google's WebGPU Cross Platform App tutorial written by [Francois Beaufort](https://github.com/beaufortfrancois). I also go over some interesting tidbits I learned (about C++ and WebGPU) along the way.
3+
In this video, we go over the code from Google's WebGPU Cross Platform App tutorial written by [Francois Beaufort](https://github.com/beaufortfrancois). We also go over some interesting tidbits I learned (about C++ and WebGPU) along the way.
4+
5+
Note: The code example worked on the first try, but I had a lot of questions. So I asked ChatGPT and added the comments to this example code. Wanted to make this video largely for my own future reference.
46

57
Tutorial: https://developer.chrome.com/docs/web-platform/webgpu/build-app
68

79
Code: https://github.com/beaufortfrancois/webgpu-cross-platform-app
10+
11+
## Learning Approach
12+
13+
Here's my learning approach for anyone else going down the same path.
14+
15+
I cloned the repository, followed the steps to run it on my machine (using the commands specified in the repo, which worked on the first run). I asked ChatGPT a lot of questions about WebGPU and copied some info to the .cpp file with the comments. I also formatted the file to make it more readable (for me) using the a .clang-format file.
16+
17+
Basically, the code is the same, but it has comments that I find helpful.
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
// Reference
2+
// Francois: https://github.com/beaufortfrancois
3+
// GitHub: https://github.com/beaufortfrancois/webgpu-cross-platform-app
4+
// Reference: https://github.com/beaufortfrancois/webgpu-cross-platform-app/blob/main/main.cpp
5+
6+
#include <iostream>
7+
8+
/**
9+
* GLFW: cross-platform window and input library.
10+
* Used here mainly to create a window and get a native handle for WebGPU.
11+
*/
12+
#include <GLFW/glfw3.h>
13+
14+
/**
15+
* Emscripten/WebAssembly support.
16+
* Only needed when building for browser/WebAssembly targets.
17+
*/
18+
#if defined(__EMSCRIPTEN__)
19+
#include <emscripten/emscripten.h>
20+
#endif
21+
22+
/**
23+
* Optional Dawn utility header.
24+
* Provides functions to print human-readable info for WebGPU objects.
25+
* Useful for debugging or logging GPU info.
26+
*/
27+
#include <dawn/webgpu_cpp_print.h>
28+
29+
/**
30+
* WebGPU C++ RAII wrapper.
31+
* Simplifies using the low-level C API in a safe, idiomatic C++ style.
32+
*/
33+
#include <webgpu/webgpu_cpp.h>
34+
35+
/**
36+
* Helper to integrate WebGPU with GLFW windows.
37+
* Abstracts platform-specific surface creation.
38+
*/
39+
#include <webgpu/webgpu_glfw.h>
40+
41+
/**
42+
* Core WebGPU objects.
43+
*/
44+
45+
/**
46+
* Entry point to the WebGPU API.
47+
* - Manages adapters, surfaces, and asynchronous GPU events.
48+
* - All GPU operations (device creation, surface setup, etc.) start from the Instance.
49+
*/
50+
wgpu::Instance instance;
51+
52+
/**
53+
* Represents a physical GPU in the system.
54+
* - Could be a discrete GPU (high-performance) or integrated GPU (low-power).
55+
* - Provides information about supported features, limits, and formats.
56+
* - Used as a basis to create a logical GPU device.
57+
*/
58+
wgpu::Adapter adapter;
59+
60+
/**
61+
* Logical GPU object used to issue rendering commands.
62+
* - Created from the Adapter.
63+
* - Responsible for creating GPU resources like buffers, textures, and pipelines.
64+
* - Handles command submission, validation, and error reporting.
65+
* - Central object for recording and executing GPU work.
66+
*/
67+
wgpu::Device device;
68+
69+
/**
70+
* Describes the GPU rendering pipeline.
71+
* - Combines vertex and fragment shaders, input layouts, rasterization, and blending state.
72+
* - Defines how vertex data is processed and how fragments are shaded.
73+
* - Can be reused for multiple draw calls.
74+
*/
75+
wgpu::RenderPipeline pipeline;
76+
77+
/**
78+
* Surface for rendering, typically linked to a window or canvas.
79+
* - Represents the target for presenting rendered frames.
80+
* - Format specifies the color texture layout (e.g., RGBA8Unorm).
81+
* - Used to acquire textures for the swap chain.
82+
*/
83+
wgpu::Surface surface;
84+
wgpu::TextureFormat format;
85+
86+
// Window dimensions.
87+
const uint32_t kWidth = 512;
88+
const uint32_t kHeight = 512;
89+
90+
/**
91+
* Configure the rendering surface (swap chain) based on GPU capabilities.
92+
* Uses double/triple buffering to avoid flicker and tearing.
93+
*/
94+
void ConfigureSurface() {
95+
wgpu::SurfaceCapabilities capabilities;
96+
surface.GetCapabilities(adapter, &capabilities);
97+
format = capabilities.formats[0];
98+
wgpu::SurfaceConfiguration config{
99+
.device = device,
100+
.format = format,
101+
.width = kWidth,
102+
.height = kHeight,
103+
.presentMode = wgpu::PresentMode::Fifo, // V-sync
104+
};
105+
surface.Configure(&config);
106+
}
107+
108+
/**
109+
* Initialize core WebGPU objects: Instance, Adapter, and Device.
110+
*
111+
* - Instance: the entry point to the WebGPU API. Manages adapters, surfaces, and async events.
112+
* - Adapter: represents a physical GPU (discrete or integrated) and its capabilities.
113+
* - Device: logical GPU used to create resources, pipelines, and issue rendering commands.
114+
*/
115+
void Init() {
116+
/**
117+
* Create a WebGPU Instance.
118+
*
119+
* - `timedWaitAnyEnable = true` allows synchronous waiting on multiple asynchronous operations.
120+
* - Instance is required before requesting an Adapter or creating a Surface.
121+
*/
122+
wgpu::InstanceDescriptor instanceDesc{
123+
.capabilities = {
124+
.timedWaitAnyEnable = true,
125+
},
126+
};
127+
instance = wgpu::CreateInstance(&instanceDesc);
128+
129+
/**
130+
* Request a GPU adapter asynchronously but wait synchronously.
131+
*
132+
* - Adapter represents the actual physical GPU the device will use.
133+
* - We check for success and exit if no adapter is found.
134+
*/
135+
wgpu::Future f1 = instance.RequestAdapter(
136+
nullptr,
137+
wgpu::CallbackMode::WaitAnyOnly,
138+
[](wgpu::RequestAdapterStatus status, wgpu::Adapter a, wgpu::StringView message) {
139+
if (status != wgpu::RequestAdapterStatus::Success) {
140+
std::cout << "RequestAdapter: " << message << "\n";
141+
exit(0);
142+
}
143+
adapter = std::move(a);
144+
});
145+
instance.WaitAny(f1, UINT64_MAX);
146+
147+
/**
148+
* Request a logical GPU device from the adapter.
149+
*
150+
* - Device is required to create GPU resources and pipelines.
151+
* - Set an uncaptured error callback to log any runtime GPU errors.
152+
* - Wait synchronously for device creation to complete.
153+
*/
154+
wgpu::DeviceDescriptor desc{};
155+
desc.SetUncapturedErrorCallback(
156+
[](const wgpu::Device&, wgpu::ErrorType errorType, wgpu::StringView message) {
157+
std::cout << "Error: " << errorType << " - message: " << message << "\n";
158+
});
159+
wgpu::Future f2 = adapter.RequestDevice(
160+
&desc,
161+
wgpu::CallbackMode::WaitAnyOnly,
162+
[](wgpu::RequestDeviceStatus status, wgpu::Device d, wgpu::StringView message) {
163+
if (status != wgpu::RequestDeviceStatus::Success) {
164+
std::cout << "RequestDevice: " << message << "\n";
165+
exit(0);
166+
}
167+
device = std::move(d);
168+
});
169+
instance.WaitAny(f2, UINT64_MAX);
170+
}
171+
172+
/**
173+
* Embedded WGSL shader code as a raw string.
174+
*
175+
* - Vertex shader (`vertexMain`): outputs positions of a single triangle.
176+
* - Fragment shader (`fragmentMain`): outputs a solid magenta color.
177+
*
178+
* Notes:
179+
* - WGSL is the shading language used in WebGPU.
180+
* - Raw string literal (R"( ... )") avoids escaping quotes or newlines.
181+
*/
182+
const char shaderCode[] = R"(
183+
@vertex fn vertexMain(@builtin(vertex_index) i : u32) -> @builtin(position) vec4f {
184+
const pos = array(vec2f(0,1), vec2f(-1,-1), vec2f(1,-1));
185+
return vec4f(pos[i],0,1);
186+
}
187+
@fragment fn fragmentMain() -> @location(0) vec4f {
188+
return vec4f(1,0,0,1);
189+
}
190+
)";
191+
192+
/**
193+
* Create a render pipeline: encapsulates all GPU state needed to draw.
194+
*
195+
* A render pipeline in WebGPU defines:
196+
* - Vertex processing: how vertices are transformed and passed to the fragment stage.
197+
* - Fragment processing: how pixel colors are computed and written to the framebuffer.
198+
* - Output formats, blending, rasterization, and other GPU state.
199+
*
200+
* This function sets up a simple pipeline using the embedded WGSL shader.
201+
*/
202+
void CreateRenderPipeline() {
203+
wgpu::ShaderSourceWGSL wgsl{{
204+
.code = shaderCode,
205+
}};
206+
wgpu::ShaderModuleDescriptor shaderModuleDescriptor{
207+
.nextInChain = &wgsl,
208+
};
209+
wgpu::ShaderModule shaderModule = device.CreateShaderModule(&shaderModuleDescriptor);
210+
wgpu::ColorTargetState colorTargetState{
211+
.format = format,
212+
};
213+
wgpu::FragmentState fragmentState{
214+
.module = shaderModule,
215+
.targetCount = 1,
216+
.targets = &colorTargetState,
217+
};
218+
wgpu::RenderPipelineDescriptor descriptor{
219+
.vertex = {
220+
.module = shaderModule,
221+
},
222+
.fragment = &fragmentState,
223+
};
224+
pipeline = device.CreateRenderPipeline(&descriptor);
225+
}
226+
227+
/**
228+
* Render a single frame.
229+
*
230+
* Steps:
231+
* 1. Acquire the next available texture from the surface to render into.
232+
* 2. Set up a render pass describing how the GPU should render to that texture.
233+
* 3. Record drawing commands (vertex/fragment pipeline operations) into a command encoder.
234+
* 4. Submit the recorded commands to the GPU queue for execution.
235+
*
236+
* This function is typically called every frame in the main loop.
237+
*/
238+
void Render() {
239+
wgpu::SurfaceTexture surfaceTexture;
240+
surface.GetCurrentTexture(&surfaceTexture);
241+
242+
wgpu::RenderPassColorAttachment attachment{
243+
.view = surfaceTexture.texture.CreateView(),
244+
.loadOp = wgpu::LoadOp::Clear,
245+
.storeOp = wgpu::StoreOp::Store,
246+
};
247+
wgpu::RenderPassDescriptor renderpass{
248+
.colorAttachmentCount = 1,
249+
.colorAttachments = &attachment,
250+
};
251+
252+
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
253+
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderpass);
254+
pass.SetPipeline(pipeline);
255+
pass.Draw(3); // Single triangle
256+
pass.End();
257+
258+
wgpu::CommandBuffer commands = encoder.Finish();
259+
device.GetQueue().Submit(1, &commands);
260+
}
261+
262+
/** Setup graphics objects: configure surface and create pipeline */
263+
void InitGraphics() {
264+
ConfigureSurface();
265+
CreateRenderPipeline();
266+
}
267+
268+
/**
269+
* Start application: initialize GLFW, create window and surface, enter render loop.
270+
*/
271+
void Start() {
272+
if (!glfwInit())
273+
return;
274+
275+
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // WebGPU only
276+
GLFWwindow* window = glfwCreateWindow(kWidth, kHeight, "WebGPU window", nullptr, nullptr);
277+
278+
surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
279+
InitGraphics();
280+
281+
#if defined(__EMSCRIPTEN__)
282+
emscripten_set_main_loop(Render, 0, false);
283+
#else
284+
while (!glfwWindowShouldClose(window)) {
285+
glfwPollEvents();
286+
Render();
287+
surface.Present();
288+
instance.ProcessEvents();
289+
}
290+
#endif
291+
}
292+
293+
/** Program entry point */
294+
int main() {
295+
Init();
296+
Start();
297+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /