Skip to content

Commit 10df253

Browse files
committed
[mlir-webgpu] add webgpu runtime wrappers
1 parent f346576 commit 10df253

File tree

5 files changed

+836
-0
lines changed

5 files changed

+836
-0
lines changed

.gitmodules

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@
44
ignore = dirty
55
shallow = true
66
branch = main
7+
[submodule "third_party/dawn"]
8+
path = third_party/dawn
9+
url = https://github.com/google/dawn.git
10+
fetchRecurseSubmodules = false
11+
shallow = true

projects/mlir-wgpu/CMakeLists.txt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2+
# See https://llvm.org/LICENSE.txt for license information.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
# Copyright (c) 2025.
5+
6+
cmake_minimum_required(VERSION 3.29)
7+
project(mlir-webgpu LANGUAGES CXX C)
8+
9+
set(CMAKE_CXX_STANDARD 20)
10+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
11+
set(CMAKE_CXX_EXTENSIONS OFF)
12+
13+
if(POLICY CMP0068)
14+
cmake_policy(SET CMP0068 NEW)
15+
set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON)
16+
endif()
17+
18+
if(POLICY CMP0075)
19+
cmake_policy(SET CMP0075 NEW)
20+
endif()
21+
22+
if(POLICY CMP0077)
23+
cmake_policy(SET CMP0077 NEW)
24+
endif()
25+
26+
if(POLICY CMP0091)
27+
cmake_policy(SET CMP0091 NEW)
28+
endif()
29+
30+
if(POLICY CMP0116)
31+
cmake_policy(SET CMP0116 NEW)
32+
endif()
33+
34+
if(POLICY CMP0135)
35+
cmake_policy(SET CMP0116 OLD)
36+
endif()
37+
38+
set(DAWN_FETCH_DEPENDENCIES ON)
39+
add_subdirectory(
40+
"${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/dawn"
41+
EXCLUDE_FROM_ALL
42+
${CMAKE_CURRENT_BINARY_DIR}/dawn
43+
)
44+
if(NOT Dawn_SOURCE_DIR OR NOT EXISTS ${Dawn_SOURCE_DIR})
45+
message(FATAL_ERROR "failed to configure Dawn dependency")
46+
endif()
47+
set(DAWN_SOURCE_DIR ${Dawn_SOURCE_DIR})
48+
49+
# https://github.com/kainino0x/webgpu-cross-platform-demo
50+
add_executable(dawn_app shader_test.cpp)
51+
target_compile_definitions(dawn_app PRIVATE DEMO_USE_ASYNCIFY=1)
52+
53+
if(EMSCRIPTEN)
54+
set_target_properties(dawn_app PROPERTIES SUFFIX ".html")
55+
target_link_libraries(dawn_app PRIVATE emdawnwebgpu_cpp webgpu_glfw)
56+
target_link_options(dawn_app PRIVATE
57+
"-sASYNCIFY=1"
58+
"-sUSE_GLFW=3"
59+
"-sASYNCIFY_STACK_SIZE=65536"
60+
"-sEXPORTED_RUNTIME_METHODS=ccall"
61+
)
62+
else()
63+
target_link_libraries(dawn_app PRIVATE webgpu_dawn webgpu_glfw glfw)
64+
endif()

projects/mlir-wgpu/dawn_main.cpp

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+
surface.Configure(&config);
31+
}
32+
33+
void Init() {
34+
static const auto kTimedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
35+
wgpu::InstanceDescriptor instanceDesc{.requiredFeatureCount = 1,
36+
.requiredFeatures = &kTimedWaitAny};
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)