เผยแพร่: 20 ก.ค. 2023, อัปเดตล่าสุด: 27 ส.ค. 2025
สำหรับนักพัฒนาเว็บ WebGPU คือ API กราฟิกของเว็บที่ให้การเข้าถึง GPU ที่รวดเร็วและเป็นหนึ่งเดียว WebGPU แสดงความสามารถของฮาร์ดแวร์ที่ทันสมัยและอนุญาตให้ดำเนินการแสดงผล และการคำนวณใน GPU ซึ่งคล้ายกับ Direct3D 12, Metal และ Vulkan
แม้จะเป็นเรื่องจริง แต่ก็เป็นเรื่องราวที่ไม่สมบูรณ์ WebGPU เป็นผลมาจากการทำงานร่วมกัน ซึ่งรวมถึงบริษัทชั้นนำอย่าง Apple, Google, Intel, Mozilla และ Microsoft ในบรรดาผู้ที่เข้าร่วม มีบางคนตระหนักว่า WebGPU ไม่ได้เป็นเพียง JavaScript API แต่เป็นกราฟิก API แบบข้ามแพลตฟอร์มสำหรับนักพัฒนาซอฟต์แวร์ในระบบนิเวศต่างๆ นอกเหนือจากเว็บ
เราจึงเปิดตัว JavaScript API ใน Chrome 113 เพื่อตอบสนอง Use Case หลัก อย่างไรก็ตาม เราได้พัฒนาโปรเจ็กต์สำคัญอีกโปรเจ็กต์หนึ่งควบคู่กันไป นั่นก็คือ C API ของ webgpu.h ไฟล์ส่วนหัว C นี้แสดงรายการขั้นตอนและโครงสร้างข้อมูลทั้งหมดที่พร้อมใช้งาน ของ WebGPU โดยทำหน้าที่เป็นเลเยอร์การแยกฮาร์ดแวร์ที่ไม่ขึ้นกับแพลตฟอร์ม ซึ่งช่วยให้คุณสร้างแอปพลิเคชันเฉพาะแพลตฟอร์มได้ด้วยการมอบอินเทอร์เฟซที่สอดคล้องกันในแพลตฟอร์มต่างๆ
ในเอกสารนี้ คุณจะได้เรียนรู้วิธีเขียนแอป C++ ขนาดเล็กโดยใช้ WebGPU ซึ่งทำงานได้ทั้งบนเว็บและแพลตฟอร์มที่เฉพาะเจาะจง เราขอสปอยล์ว่าคุณจะเห็นสามเหลี่ยมสีแดงแบบเดียวกับที่ปรากฏในหน้าต่างเบราว์เซอร์และหน้าต่างเดสก์ท็อปโดยมีการปรับโค้ดเบสเพียงเล็กน้อย

หลักการทำงาน
หากต้องการดูแอปพลิเคชันที่เสร็จสมบูรณ์แล้ว โปรดดูที่ที่เก็บแอปข้ามแพลตฟอร์ม WebGPU
แอปนี้เป็นตัวอย่าง C++ ที่เรียบง่ายซึ่งแสดงวิธีใช้ WebGPU เพื่อสร้างแอปเดสก์ท็อปและเว็บจากฐานของโค้ดรายการเดียว โดยเบื้องหลังจะใช้ webgpu.h ของ WebGPU เป็นเลเยอร์การแยกฮาร์ดแวร์ที่ไม่ขึ้นกับแพลตฟอร์มผ่าน Wrapper C++ ที่ชื่อ webgpu_cpp.h
บนเว็บ แอปนี้สร้างขึ้นโดยใช้ emdawnwebgpu (Emscripten Dawn WebGPU) ซึ่งมีการเชื่อมโยงที่ใช้ webgpu.h อยู่เหนือ JavaScript API ในแพลตฟอร์มบางอย่าง เช่น macOS หรือ Windows โปรเจ็กต์นี้สามารถสร้างขึ้นกับ Dawn ซึ่งเป็นการติดตั้งใช้งาน WebGPU แบบข้ามแพลตฟอร์มของ Chromium นอกจากนี้ ยังมี wgpu-native ซึ่งเป็นการติดตั้งใช้งาน webgpu.h ใน Rust แต่ไม่ได้ใช้ในเอกสารนี้
เริ่มต้นใช้งาน
หากต้องการเริ่มต้นใช้งาน คุณต้องมีคอมไพเลอร์ C++ และ CMake เพื่อจัดการบิลด์ข้ามแพลตฟอร์มในลักษณะมาตรฐาน สร้างmain.cpp
ไฟล์ต้นฉบับและCMakeLists.txt
ไฟล์บิลด์ภายในโฟลเดอร์เฉพาะ
ขณะนี้ไฟล์ main.cpp
ควรมีฟังก์ชัน main()
ที่ว่างเปล่า
int main() {}
CMakeLists.txt
มีข้อมูลพื้นฐานเกี่ยวกับโปรเจ็กต์ บรรทัดสุดท้ายระบุว่าชื่อไฟล์ที่เรียกใช้งานได้คือ "app" และซอร์สโค้ดคือ main.cpp
cmake_minimum_required(VERSION 3.22) # CMake version check
project(app) # Create project "app"
set(CMAKE_CXX_STANDARD 20) # Enable C++20 standard
add_executable(app "main.cpp")
เรียกใช้ cmake -B build
เพื่อสร้างไฟล์บิลด์ในโฟลเดอร์ย่อย "build/" และ cmake --build build
เพื่อสร้างแอปจริงและสร้างไฟล์ที่เรียกใช้งานได้
# Build the app with CMake.
$ cmake -B build && cmake --build build
# Run the app.
$ ./build/app
แอปทำงานได้ แต่ยังไม่มีเอาต์พุตเนื่องจากคุณต้องมีวิธีวาดสิ่งต่างๆ บนหน้าจอ
รับ Dawn
หากต้องการวาดสามเหลี่ยม คุณสามารถใช้ประโยชน์จาก Dawn ซึ่งเป็นการติดตั้งใช้งาน WebGPU แบบข้ามแพลตฟอร์มของ Chromium ซึ่งรวมถึงไลบรารี C++ ของ GLFW สำหรับการวาดภาพบนหน้าจอ วิธีหนึ่งในการดาวน์โหลด Dawn คือการเพิ่มเป็น git submodule ในที่เก็บ คำสั่งต่อไปนี้จะดึงข้อมูลในโฟลเดอร์ย่อย "dawn/"
$ git init
$ git submodule add https://dawn.googlesource.com/dawn
จากนั้นให้เพิ่มลงในไฟล์ CMakeLists.txt
ดังนี้
- ตัวเลือก
DAWN_FETCH_DEPENDENCIES
ของ CMake จะดึงข้อมูลการขึ้นต่อกันทั้งหมดของ Dawn - ตัวเลือก CMake
DAWN_BUILD_MONOLITHIC_LIBRARY
จะรวมคอมโพเนนต์ Dawn ทั้งหมดไว้ในไลบรารีเดียว - โฟลเดอร์ย่อย
dawn/
จะรวมอยู่ในเป้าหมาย - แอปของคุณจะขึ้นอยู่กับเป้าหมาย
webgpu_dawn
,webgpu_glfw
และglfw
เพื่อให้คุณใช้เป้าหมายเหล่านั้นในไฟล์main.cpp
ได้ในภายหลัง
…
set(DAWN_FETCH_DEPENDENCIES ON)
set(DAWN_BUILD_MONOLITHIC_LIBRARY STATIC)
add_subdirectory("dawn" EXCLUDE_FROM_ALL)
target_link_libraries(app PRIVATE webgpu_dawn webgpu_glfw glfw)
เปิดหน้าต่าง
ตอนนี้ Dawn พร้อมใช้งานแล้ว ให้ใช้ GLFW เพื่อวาดสิ่งต่างๆ บนหน้าจอ ไลบรารีนี้รวมอยู่ใน webgpu_glfw
เพื่อความสะดวก ช่วยให้คุณเขียนโค้ดที่ไม่ขึ้นกับแพลตฟอร์มสำหรับการจัดการหน้าต่างได้
หากต้องการเปิดหน้าต่างชื่อ "หน้าต่าง WebGPU" ที่มีความละเอียด 512x512 ให้อัปเดตไฟล์ main.cpp
ดังนี้ โปรดทราบว่าเราใช้ glfwWindowHint()
ที่นี่เพื่อขอไม่ให้มีการเริ่มต้น API กราฟิกใดๆ
#include <GLFW/glfw3.h>
const uint32_t kWidth = 512;
const uint32_t kHeight = 512;
void Start() {
if (!glfwInit()) {
return;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window =
glfwCreateWindow(kWidth, kHeight, "WebGPU window", nullptr, nullptr);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// TODO: Render a triangle using WebGPU.
}
}
int main() {
Start();
}
การสร้างแอปใหม่และเรียกใช้แอปเหมือนเดิมจะทำให้เกิดหน้าต่างว่างเปล่า คุณคืบหน้าไปมากแล้ว

รับอุปกรณ์ GPU
ใน JavaScript navigator.gpu
คือจุดแรกเข้าสำหรับการเข้าถึง GPU ใน C++ คุณต้องสร้างตัวแปร wgpu::Instance
ด้วยตนเองเพื่อใช้ในวัตถุประสงค์เดียวกัน เพื่อความสะดวก ให้ประกาศ instance
ที่ด้านบนของไฟล์ main.cpp
และเรียกใช้ wgpu::CreateInstance()
ภายใน Init()
#include <webgpu/webgpu_cpp.h>
…
wgpu::Instance instance;
…
void Init() {
static const auto kTimedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
wgpu::InstanceDescriptor instanceDesc{.requiredFeatureCount = 1,
.requiredFeatures = &kTimedWaitAny};
instance = wgpu::CreateInstance(&instanceDesc);
}
int main() {
Init();
Start();
}
ประกาศตัวแปร wgpu::Adapter
และ wgpu::Device
ที่ด้านบนของไฟล์ main.cpp
อัปเดตฟังก์ชัน Init()
เพื่อเรียกใช้ instance.RequestAdapter()
และกำหนดฟังก์ชัน Callback ของผลลัพธ์ให้กับ adapter
จากนั้นเรียกใช้ adapter.RequestDevice()
และกำหนดฟังก์ชัน Callback ของผลลัพธ์ให้กับ device
#include <iostream>
#include <dawn/webgpu_cpp_print.h>
…
wgpu::Adapter adapter;
wgpu::Device device;
void Init() {
…
wgpu::Future f1 = instance.RequestAdapter(
nullptr, wgpu::CallbackMode::WaitAnyOnly,
[](wgpu::RequestAdapterStatus status, wgpu::Adapter a,
wgpu::StringView message) {
if (status != wgpu::RequestAdapterStatus::Success) {
std::cout << "RequestAdapter: " << message << "\n";
exit(0);
}
adapter = std::move(a);
});
instance.WaitAny(f1, UINT64_MAX);
wgpu::DeviceDescriptor desc{};
desc.SetUncapturedErrorCallback([](const wgpu::Device&,
wgpu::ErrorType errorType,
wgpu::StringView message) {
std::cout << "Error: " << errorType << " - message: " << message << "\n";
});
wgpu::Future f2 = adapter.RequestDevice(
&desc, wgpu::CallbackMode::WaitAnyOnly,
[](wgpu::RequestDeviceStatus status, wgpu::Device d,
wgpu::StringView message) {
if (status != wgpu::RequestDeviceStatus::Success) {
std::cout << "RequestDevice: " << message << "\n";
exit(0);
}
device = std::move(d);
});
instance.WaitAny(f2, UINT64_MAX);
}
วาดรูปสามเหลี่ยม
Swap Chain ไม่ได้แสดงใน JavaScript API เนื่องจากเบราว์เซอร์เป็นผู้จัดการ ใน C++ คุณต้องสร้างด้วยตนเอง อีกครั้งเพื่อความสะดวก ให้ประกาศตัวแปร wgpu::Surface
ที่ด้านบนของไฟล์ main.cpp
หลังจากสร้างหน้าต่าง GLFW ใน Start()
แล้ว ให้เรียกใช้ฟังก์ชัน wgpu::glfw::CreateSurfaceForWindow()
ที่สะดวกเพื่อสร้าง wgpu::Surface
(คล้ายกับ Canvas ของ HTML) และกำหนดค่าโดยเรียกใช้ฟังก์ชัน Helper ใหม่ ConfigureSurface()
ใน InitGraphics()
นอกจากนี้ คุณยังต้องเรียกใช้ surface.Present()
เพื่อแสดงพื้นผิวถัดไปในลูป while ด้วย การดำเนินการนี้จะไม่มีผลที่มองเห็นได้เนื่องจากยังไม่มีการแสดงผล
#include <webgpu/webgpu_glfw.h>
…
wgpu::Surface surface;
wgpu::TextureFormat format;
void ConfigureSurface() {
wgpu::SurfaceCapabilities capabilities;
surface.GetCapabilities(adapter, &capabilities);
format = capabilities.formats[0];
wgpu::SurfaceConfiguration config{.device = device,
.format = format,
.width = kWidth,
.height = kHeight,
.presentMode = wgpu::PresentMode::Fifo};
surface.Configure(&config);
}
void InitGraphics() {
ConfigureSurface();
}
void Render() {
// TODO: Render a triangle using WebGPU.
}
void Start() {
…
surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
InitGraphics();
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
Render();
surface.Present();
instance.ProcessEvents();
}
}
ตอนนี้เป็นเวลาที่เหมาะในการสร้างไปป์ไลน์การแสดงผลด้วยโค้ดด้านล่าง ประกาศตัวแปร wgpu::RenderPipeline
ที่ด้านบนของไฟล์ main.cpp
และเรียกใช้ฟังก์ชันตัวช่วย CreateRenderPipeline()
ใน InitGraphics()
เพื่อให้เข้าถึงได้ง่ายขึ้น
wgpu::RenderPipeline pipeline; … const char shaderCode[] = R"( @vertex fn vertexMain(@builtin(vertex_index) i : u32) -> @builtin(position) vec4f { const pos = array(vec2f(0, 1), vec2f(-1, -1), vec2f(1, -1)); return vec4f(pos[i], 0, 1); } @fragment fn fragmentMain() -> @location(0) vec4f { return vec4f(1, 0, 0, 1); } )"; void CreateRenderPipeline() { wgpu::ShaderSourceWGSL wgsl{{.code = shaderCode}}; wgpu::ShaderModuleDescriptor shaderModuleDescriptor{.nextInChain = &wgsl}; wgpu::ShaderModule shaderModule = device.CreateShaderModule(&shaderModuleDescriptor); wgpu::ColorTargetState colorTargetState{.format = format}; wgpu::FragmentState fragmentState{ .module = shaderModule, .targetCount = 1, .targets = &colorTargetState}; wgpu::RenderPipelineDescriptor descriptor{.vertex = {.module = shaderModule}, .fragment = &fragmentState}; pipeline = device.CreateRenderPipeline(&descriptor); } void InitGraphics() { … CreateRenderPipeline(); }
สุดท้าย ให้ส่งคำสั่งการแสดงผลไปยัง GPU ในฟังก์ชัน Render()
ที่เรียกใช้ในแต่ละเฟรม
void Render() {
wgpu::SurfaceTexture surfaceTexture;
surface.GetCurrentTexture(&surfaceTexture);
wgpu::RenderPassColorAttachment attachment{
.view = surfaceTexture.texture.CreateView(),
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store};
wgpu::RenderPassDescriptor renderpass{.colorAttachmentCount = 1,
.colorAttachments = &attachment};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderpass);
pass.SetPipeline(pipeline);
pass.Draw(3);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
device.GetQueue().Submit(1, &commands);
}
การสร้างแอปใหม่ด้วย CMake และการเรียกใช้แอปตอนนี้จะทำให้เกิดสามเหลี่ยมสีแดงที่รอคอยมานานในหน้าต่าง พักบ้างนะ คุณควรได้พัก

คอมไพล์เป็น WebAssembly
มาดูการเปลี่ยนแปลงขั้นต่ำที่จำเป็นในการปรับฐานโค้ดที่มีอยู่เพื่อวาดสามเหลี่ยมสีแดงนี้ในหน้าต่างเบราว์เซอร์กัน อีกครั้งที่แอปสร้างขึ้นจาก emdawnwebgpu (Emscripten Dawn WebGPU) ซึ่งมีการเชื่อมโยงที่ใช้ webgpu.h อยู่เหนือ JavaScript API โดยใช้ Emscripten ซึ่งเป็นเครื่องมือสำหรับคอมไพล์โปรแกรม C/C++ เป็น WebAssembly
อัปเดตการตั้งค่า CMake
เมื่อติดตั้ง Emscripten แล้ว ให้อัปเดตCMakeLists.txt
ไฟล์บิลด์ดังนี้
คุณต้องเปลี่ยนเฉพาะโค้ดที่ไฮไลต์เท่านั้น
set_target_properties
ใช้เพื่อเพิ่มนามสกุลไฟล์ "html" ลงในไฟล์เป้าหมายโดยอัตโนมัติ กล่าวคือ คุณจะสร้างไฟล์ "app.html"emdawnwebgpu_cpp
ไลบรารีลิงก์เป้าหมายช่วยให้รองรับ WebGPU ใน Emscripten ได้ หากไม่มีไฟล์นี้ ไฟล์main.cpp
จะเข้าถึงไฟล์webgpu/webgpu_cpp.h
ไม่ได้ASYNCIFY=1
ตัวเลือก App Link ช่วยให้โค้ด C++ แบบซิงโครนัสโต้ตอบกับ JavaScript แบบอะซิงโครนัสได้USE_GLFW=3
ตัวเลือก App Link จะบอกให้ Emscripten ใช้การติดตั้งใช้งาน JavaScript ในตัวของ API GLFW 3
cmake_minimum_required(VERSION 3.22) # CMake version check
project(app) # Create project "app"
set(CMAKE_CXX_STANDARD 20) # Enable C++20 standard
add_executable(app "main.cpp")
set(DAWN_FETCH_DEPENDENCIES ON)
set(DAWN_BUILD_MONOLITHIC_LIBRARY STATIC)
add_subdirectory("dawn" EXCLUDE_FROM_ALL)
if(EMSCRIPTEN)
set_target_properties(app PROPERTIES SUFFIX ".html")
target_link_libraries(app PRIVATE emdawnwebgpu_cpp webgpu_glfw)
target_link_options(app PRIVATE "-sASYNCIFY=1" "-sUSE_GLFW=3")
else()
target_link_libraries(app PRIVATE webgpu_dawn webgpu_glfw glfw)
endif()
อัปเดตโค้ด
แทนที่จะใช้ลูป while ให้เรียกใช้ emscripten_set_main_loop(Render)
เพื่อให้แน่ใจว่าฟังก์ชัน Render()
จะได้รับการเรียกใช้ในอัตราที่เหมาะสมและราบรื่นซึ่งสอดคล้องกับเบราว์เซอร์และจอภาพอย่างถูกต้อง
#include <iostream>
#include <GLFW/glfw3.h>
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#endif
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <webgpu/webgpu_glfw.h>
void Start() {
…
#if defined(__EMSCRIPTEN__)
emscripten_set_main_loop(Render, 0, false);
#else
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
Render();
surface.Present();
instance.ProcessEvents();
}
#endif
}
สร้างแอปด้วย Emscripten
การเปลี่ยนแปลงเพียงอย่างเดียวที่จำเป็นในการสร้างแอปด้วย Emscripten คือการนำหน้าคำสั่ง cmake
ด้วยสคริปต์เชลล์ magical emcmake
คราวนี้ให้สร้างแอปในโฟลเดอร์ย่อย build-web
แล้วเริ่มเซิร์ฟเวอร์ HTTP สุดท้าย ให้เปิดเบราว์เซอร์และไปที่ build-web/app.html
# Build the app with Emscripten.
$ emcmake cmake -B build-web && cmake --build build-web
# Start a HTTP server.
$ npx http-server

ขั้นตอนถัดไป
สิ่งที่คุณคาดหวังได้ในอนาคตมีดังนี้
- การปรับปรุงความเสถียรของ API webgpu.h และ webgpu_cpp.h
- เปิดตัวการรองรับ Dawn สำหรับ Android และ iOS
ในระหว่างนี้ โปรดแจ้งปัญหาเกี่ยวกับ WebGPU สำหรับ Emscripten และปัญหาเกี่ยวกับ Dawn พร้อมคำแนะนำและคำถาม
แหล่งข้อมูล
คุณสามารถสำรวจซอร์สโค้ดของแอปนี้ได้
หากต้องการเจาะลึกเรื่องการสร้างแอปพลิเคชัน 3 มิติแบบเนทีฟใน C++ ตั้งแต่ต้นด้วย WebGPU โปรดดูเอกสารประกอบ "เรียนรู้ WebGPU สำหรับ C++" และตัวอย่าง WebGPU แบบเนทีฟของ Dawn
หากสนใจ Rust คุณยังสำรวจไลบรารีกราฟิก wgpu ที่อิงตาม WebGPU ได้ด้วย ดูเดโม hello-triangle
คำขอบคุณ
บทความนี้ได้รับการตรวจสอบโดย Corentin Wallez, Kai Ninomiya และ Rachel Andrew
รูปภาพโดย Marc-Olivier Jodoin ใน Unsplash