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 d39c06e

Browse files
Add simple code.
1 parent b1c7a3a commit d39c06e

File tree

4 files changed

+233
-0
lines changed

4 files changed

+233
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[submodule "dawn"]
2+
path = dawn
3+
url = https://dawn.googlesource.com/dawn
4+
branch = chromium/7204 # Chrome 138
5+
shallow = true
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
cmake_minimum_required(VERSION 3.13) # CMake version check
2+
project(app) # Create project "app"
3+
set(CMAKE_CXX_STANDARD 20) # Enable C++20 standard
4+
5+
add_executable(app "main.cpp")
6+
7+
set(DAWN_FETCH_DEPENDENCIES ON)
8+
add_subdirectory("dawn" EXCLUDE_FROM_ALL)
9+
10+
if(EMSCRIPTEN)
11+
set_target_properties(app PROPERTIES SUFFIX ".html")
12+
target_link_libraries(app PRIVATE emdawnwebgpu_cpp webgpu_glfw)
13+
target_link_options(app PRIVATE "-sASYNCIFY=1" "-sUSE_GLFW=3")
14+
else()
15+
target_link_libraries(app PRIVATE dawn::webgpu_dawn glfw webgpu_glfw)
16+
endif()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
![image](https://github.com/beaufortfrancois/webgpu-cross-platform-app/assets/634478/81579516-7390-4198-bb18-68e7f4cb34c3)
2+
3+
# WebGPU cross-platform app with CMake/Emscripten
4+
5+
This app is a <em>minimalistic</em> C++ example that shows how to use [WebGPU](https://gpuweb.github.io/gpuweb/) to build desktop and web apps from a single codebase. Under the hood, it uses WebGPU's [webgpu.h](https://github.com/webgpu-native/webgpu-headers/blob/main/webgpu.h) as a platform-agnostic hardware abstraction layer through a C++ wrapper called [webgpu_cpp.h](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/include/webgpu/webgpu_cpp.h).
6+
7+
On the web, the app is built against [emdawnwebgpu](https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/) (Emscripten Dawn WebGPU), which has bindings implementing webgpu.h on top of the JavaScript API. It uses [Emscripten](https://emscripten.org/), a tool for compiling C/C++ programs to WebAssembly. On specific platforms such as macOS or Windows, this project can be built against [Dawn](https://dawn.googlesource.com/dawn/), Chromium's cross-platform WebGPU implementation.
8+
9+
<p align="center" width="100%">
10+
:warning: The webgpu.h and webgpu_cpp.h APIs are not yet stabilized. :warning:
11+
</p>
12+
13+
## Setup
14+
15+
```sh
16+
# Clone repository and initialize submodules.
17+
git clone https://github.com/beaufortfrancois/webgpu-cross-platform-app.git
18+
cd webgpu-cross-platform-app/
19+
git submodule update --init
20+
```
21+
22+
## Requirements
23+
24+
<i>Instructions are for macOS; they will need to be adapted to work on Linux and Windows.</i>
25+
26+
```sh
27+
# Make sure CMake and Emscripten are installed.
28+
brew install cmake emscripten
29+
```
30+
31+
## Specific platform build
32+
33+
```sh
34+
# Build the app with CMake.
35+
cmake -B build && cmake --build build -j4
36+
37+
# Run the app.
38+
./build/app
39+
```
40+
41+
## Web build
42+
43+
```sh
44+
# Build the app with Emscripten.
45+
emcmake cmake -B build-web && cmake --build build-web -j4
46+
47+
# Run a server.
48+
npx http-server
49+
```
50+
51+
```sh
52+
# Open the web app.
53+
open http://127.0.0.1:8080/build-web/app.html
54+
```
55+
56+
### Debugging WebAssembly
57+
58+
When building the app, compile it with DWARF debug information included thanks to `emcmake cmake -DCMAKE_BUILD_TYPE=Debug -B build-web`. And make sure to install the [C/C++ DevTools Support (DWARF) Chrome extension](https://goo.gle/wasm-debugging-extension) to enable WebAssembly debugging in DevTools.
59+
60+
<img width="1112" alt="image" src="https://github.com/beaufortfrancois/webgpu-cross-platform-app/assets/634478/e82f2494-6b1a-4534-b9e3-0c04caeca96d">
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include <iostream>
2+
3+
#include <GLFW/glfw3.h>
4+
#if defined(__EMSCRIPTEN__)
5+
#include <emscripten/emscripten.h>
6+
#endif
7+
#include <dawn/webgpu_cpp_print.h>
8+
#include <webgpu/webgpu_cpp.h>
9+
#include <webgpu/webgpu_glfw.h>
10+
11+
wgpu::Instance instance;
12+
wgpu::Adapter adapter;
13+
wgpu::Device device;
14+
wgpu::RenderPipeline pipeline;
15+
16+
wgpu::Surface surface;
17+
wgpu::TextureFormat format;
18+
const uint32_t kWidth = 512;
19+
const uint32_t kHeight = 512;
20+
21+
void ConfigureSurface() {
22+
wgpu::SurfaceCapabilities capabilities;
23+
surface.GetCapabilities(adapter, &capabilities);
24+
format = capabilities.formats[0];
25+
26+
wgpu::SurfaceConfiguration config{.device = device,
27+
.format = format,
28+
.width = kWidth,
29+
.height = kHeight,
30+
.presentMode = wgpu::PresentMode::Fifo};
31+
surface.Configure(&config);
32+
}
33+
34+
void Init() {
35+
wgpu::InstanceDescriptor instanceDesc{
36+
.capabilities = {.timedWaitAnyEnable = true}};
37+
instance = wgpu::CreateInstance(&instanceDesc);
38+
39+
wgpu::Future f1 = instance.RequestAdapter(
40+
nullptr, wgpu::CallbackMode::WaitAnyOnly,
41+
[](wgpu::RequestAdapterStatus status, wgpu::Adapter a,
42+
wgpu::StringView message) {
43+
if (status != wgpu::RequestAdapterStatus::Success) {
44+
std::cout << "RequestAdapter: " << message << "\n";
45+
exit(0);
46+
}
47+
adapter = std::move(a);
48+
});
49+
instance.WaitAny(f1, UINT64_MAX);
50+
51+
wgpu::DeviceDescriptor desc{};
52+
desc.SetUncapturedErrorCallback([](const wgpu::Device&,
53+
wgpu::ErrorType errorType,
54+
wgpu::StringView message) {
55+
std::cout << "Error: " << errorType << " - message: " << message << "\n";
56+
});
57+
58+
wgpu::Future f2 = adapter.RequestDevice(
59+
&desc, wgpu::CallbackMode::WaitAnyOnly,
60+
[](wgpu::RequestDeviceStatus status, wgpu::Device d,
61+
wgpu::StringView message) {
62+
if (status != wgpu::RequestDeviceStatus::Success) {
63+
std::cout << "RequestDevice: " << message << "\n";
64+
exit(0);
65+
}
66+
device = std::move(d);
67+
});
68+
instance.WaitAny(f2, UINT64_MAX);
69+
}
70+
71+
const char shaderCode[] = R"(
72+
@vertex fn vertexMain(@builtin(vertex_index) i : u32) ->
73+
@builtin(position) vec4f {
74+
const pos = array(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1));
75+
return vec4f(pos[i], 0, 1);
76+
}
77+
@fragment fn fragmentMain() -> @location(0) vec4f {
78+
return vec4f(1, 0, 0, 1);
79+
}
80+
)";
81+
82+
void CreateRenderPipeline() {
83+
wgpu::ShaderSourceWGSL wgsl{{.code = shaderCode}};
84+
85+
wgpu::ShaderModuleDescriptor shaderModuleDescriptor{.nextInChain = &wgsl};
86+
wgpu::ShaderModule shaderModule =
87+
device.CreateShaderModule(&shaderModuleDescriptor);
88+
89+
wgpu::ColorTargetState colorTargetState{.format = format};
90+
91+
wgpu::FragmentState fragmentState{
92+
.module = shaderModule, .targetCount = 1, .targets = &colorTargetState};
93+
94+
wgpu::RenderPipelineDescriptor descriptor{.vertex = {.module = shaderModule},
95+
.fragment = &fragmentState};
96+
pipeline = device.CreateRenderPipeline(&descriptor);
97+
}
98+
99+
void Render() {
100+
wgpu::SurfaceTexture surfaceTexture;
101+
surface.GetCurrentTexture(&surfaceTexture);
102+
103+
wgpu::RenderPassColorAttachment attachment{
104+
.view = surfaceTexture.texture.CreateView(),
105+
.loadOp = wgpu::LoadOp::Clear,
106+
.storeOp = wgpu::StoreOp::Store};
107+
108+
wgpu::RenderPassDescriptor renderpass{.colorAttachmentCount = 1,
109+
.colorAttachments = &attachment};
110+
111+
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
112+
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderpass);
113+
pass.SetPipeline(pipeline);
114+
pass.Draw(3);
115+
pass.End();
116+
wgpu::CommandBuffer commands = encoder.Finish();
117+
device.GetQueue().Submit(1, &commands);
118+
}
119+
120+
void InitGraphics() {
121+
ConfigureSurface();
122+
CreateRenderPipeline();
123+
}
124+
125+
void Start() {
126+
if (!glfwInit()) {
127+
return;
128+
}
129+
130+
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
131+
GLFWwindow* window =
132+
glfwCreateWindow(kWidth, kHeight, "WebGPU window", nullptr, nullptr);
133+
surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
134+
135+
InitGraphics();
136+
137+
#if defined(__EMSCRIPTEN__)
138+
emscripten_set_main_loop(Render, 0, false);
139+
#else
140+
while (!glfwWindowShouldClose(window)) {
141+
glfwPollEvents();
142+
Render();
143+
surface.Present();
144+
instance.ProcessEvents();
145+
}
146+
#endif
147+
}
148+
149+
int main() {
150+
Init();
151+
Start();
152+
}

0 commit comments

Comments
(0)

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