تاريخ النشر: 20 يوليو 2023، تاريخ آخر تعديل: 27 أغسطس 2025
بالنسبة إلى مطوّري الويب، فإنّ WebGPU هي واجهة برمجة تطبيقات لرسومات الويب توفّر وصولاً موحّدًا وسريعًا إلى وحدات معالجة الرسومات. تتيح WebGPU إمكانات الأجهزة الحديثة وتسمح بتنفيذ عمليات العرض والحوسبة على وحدة معالجة الرسومات (GPU)، على غرار Direct3D 12 وMetal وVulkan.
مع أنّ هذه القصة صحيحة، إلا أنّها غير مكتملة. إنّ WebGPU هي نتيجة جهد تعاوني شاركت فيه شركات كبرى، مثل Apple وGoogle وIntel وMozilla وMicrosoft. وقد أدرك بعضهم أنّ WebGPU يمكن أن تكون أكثر من مجرد واجهة برمجة تطبيقات Javascript، بل هي واجهة برمجة تطبيقات للرسومات متوافقة مع عدة منصات للمطوّرين في جميع الأنظمة المتكاملة، بخلاف الويب.
لتحقيق حالة الاستخدام الأساسية، تم إطلاق واجهة برمجة تطبيقات JavaScript في الإصدار 113 من Chrome. ومع ذلك، تم تطوير مشروع مهم آخر إلى جانب ذلك، وهو واجهة برمجة التطبيقات webgpu.h C. يسرد ملف عنوان C هذا جميع الإجراءات وبُنى البيانات المتاحة في WebGPU. وهي تعمل كطبقة تجريد للأجهزة مستقلة عن النظام الأساسي، ما يتيح لك إنشاء تطبيقات خاصة بنظام أساسي معيّن من خلال توفير واجهة متسقة على جميع الأنظمة الأساسية المختلفة.
في هذا المستند، ستتعرّف على كيفية كتابة تطبيق صغير بلغة C++ باستخدام WebGPU يعمل على الويب وعلى منصات معيّنة. تنبيه: سيظهر لك المثلث الأحمر نفسه الذي يظهر في نافذة المتصفّح ونافذة الكمبيوتر المكتبي مع إجراء تعديلات بسيطة على قاعدة الرموز البرمجية.

كيف تعمل هذه الميزة؟
للاطّلاع على التطبيق المكتمل، يمكنك الانتقال إلى مستودع التطبيق المتوافق مع منصات متعددة WebGPU.
التطبيق هو مثال بسيط بلغة C++ يوضّح كيفية استخدام WebGPU لإنشاء تطبيقات على أجهزة الكمبيوتر والويب من قاعدة رموز برمجية واحدة. في الخلفية، تستخدم WebGPU webgpu.h كطبقة تجريد للأجهزة مستقلة عن النظام الأساسي من خلال برنامج تضمين C++ يُسمى webgpu_cpp.h.
على الويب، تم إنشاء التطبيق باستخدام emdawnwebgpu (Emscripten Dawn WebGPU)، الذي يتضمّن روابط تنفّذ webgpu.h فوق واجهة برمجة تطبيقات JavaScript. على منصات معيّنة، مثل macOS أو Windows، يمكن إنشاء هذا المشروع باستخدام Dawn، وهي أداة تنفيذ WebGPU المتوافقة مع عدة أنظمة أساسية في Chromium. تجدر الإشارة إلى أنّ wgpu-native، وهو تطبيق Rust لملف webgpu.h، متاح أيضًا ولكنّه غير مستخدَم في هذا المستند.
البدء
للبدء، تحتاج إلى برنامج تجميع 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
يتم تشغيل التطبيق ولكن لا يوجد أي ناتج حتى الآن، لأنّك تحتاج إلى طريقة لرسم العناصر على الشاشة.
Get Dawn
لرسم المثلث، يمكنك الاستفادة من Dawn، وهي عملية تنفيذ WebGPU المتوافقة مع عدة أنظمة أساسية في Chromium. ويشمل ذلك مكتبة GLFW C++ للرسم على الشاشة. إحدى طرق تنزيل Dawn هي إضافتها كوحدة فرعية في git إلى المستودع. تستردّ الأوامر التالية الملف في مجلد فرعي باسم "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()
هنا لطلب عدم تهيئة أي واجهة برمجة تطبيقات رسومات معيّنة.
#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();
}
عند إعادة إنشاء التطبيق وتشغيله كما كان من قبل، تظهر الآن نافذة فارغة. أنت تحرز تقدّمًا جيدًا.

الحصول على جهاز وحدة معالجة الرسومات
في JavaScript، navigator.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()
وتعيين دالة ردّ الاتصال الخاصة بالنتيجة إلى adapter
، ثم استدعِ adapter.RequestDevice()
وعيِّن دالة ردّ الاتصال الخاصة بالنتيجة إلى 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);
}
رسم مثلث
لا يتم عرض سلسلة التبديل في JavaScript API لأنّ المتصفّح يتولّى ذلك. في C++، عليك إنشاء هذا الملف يدويًا. مرة أخرى، ولتسهيل الأمر، عليك تعريف المتغيّر wgpu::Surface
في أعلى ملف main.cpp
. بعد إنشاء نافذة GLFW في Start()
، استدعِ الدالة wgpu::glfw::CreateSurfaceForWindow()
المفيدة لإنشاء wgpu::Surface
(مشابهة لقماش HTML) وإعدادها من خلال استدعاء الدالة المساعدة الجديدة 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(); }
أخيرًا، أرسِل أوامر العرض إلى وحدة معالجة الرسومات في الدالة 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
خيار رابط التطبيق تفاعُل رمز C++ المتزامن مع JavaScript غير المتزامن. - يطلب خيار رابط التطبيق
USE_GLFW=3
من Emscripten استخدام تنفيذ JavaScript المضمّن لواجهة برمجة التطبيقات 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
إلى بداية الأوامر باستخدام نص برمجي سحري 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

الخطوات التالية
في ما يلي ما يمكن توقُّعه في المستقبل:
- تحسينات في ثبات واجهات برمجة التطبيقات webgpu.h وwebgpu_cpp.h
- توفير الدعم الأولي لـ Dawn على أجهزة Android وiOS
في الوقت الحالي، يُرجى الإبلاغ عن مشاكل WebGPU في Emscripten ومشاكل Dawn مع تقديم اقتراحات وطرح أسئلة.
الموارد
يمكنك استكشاف رمز المصدر لهذا التطبيق.
إذا كنت تريد التعرّف أكثر على إنشاء تطبيقات ثلاثية الأبعاد (3D) أصلية في C++ من البداية باستخدام WebGPU، يمكنك الاطّلاع على مستندات Learn WebGPU for C++ وأمثلة Dawn Native WebGPU.
إذا كنت مهتمًا بلغة Rust، يمكنك أيضًا استكشاف مكتبة الرسومات wgpu المستندة إلى WebGPU. يمكنك الاطّلاع على العرض التوضيحي hello-triangle.
كلمة شكر
راجع هذه المقالة كورنتين واليز وكاي نينوميا وراشيل أندرو.
صورة من مارك-أوليفر جودوان على Unsplash