वेब डेवलपर के लिए, WebGPU एक वेब ग्राफ़िक्स एपीआई है. यह जीपीयू का यूनिफ़ाइड और तेज़ी से ऐक्सेस देता है. WebGPU, हार्डवेयर की आधुनिक क्षमताओं को दिखाता है. साथ ही, Direct3D 12, मेटल, और Vulkan की तरह, जीपीयू पर रेंडर करने और कंप्यूटेशन के काम करने की सुविधा देता है.
हालांकि, यह सही है, लेकिन यह अधूरी है. WebGPU, साथ मिलकर किए गए काम का नतीजा है. इसमें Apple, Google, Intel, Mozilla, और Microsoft जैसी बड़ी कंपनियां शामिल हैं. इनमें से कुछ लोगों को यह एहसास हुआ कि WebGPU, JavaScript API से बेहतर हो सकता है. हालांकि, यह वेब के अलावा नेटवर्क पर मौजूद डेवलपर के लिए, एक क्रॉस-प्लैटफ़ॉर्म ग्राफ़िक एपीआई भी हो सकता है.
इस्तेमाल के मुख्य उदाहरण को पूरा करने के लिए, Chrome 113 में JavaScript API को पेश किया गया था. हालांकि, इसके साथ ही एक और अहम प्रोजेक्ट भी डेवलप किया गया है: webgpu.h C एपीआई. इस C हेडर फ़ाइल में WebGPU की सभी उपलब्ध प्रोसेस और डेटा स्ट्रक्चर की सूची होती है. यह प्लैटफ़ॉर्म-एग्नोस्टिक हार्डवेयर ऐब्स्ट्रैक्शन लेयर के तौर पर काम करता है. इससे आपको अलग-अलग प्लैटफ़ॉर्म पर एक जैसा इंटरफ़ेस देकर, प्लैटफ़ॉर्म के हिसाब से ऐप्लिकेशन बनाने में मदद मिलती है.
इस दस्तावेज़ में, WebGPU का इस्तेमाल करके एक छोटा C++ ऐप्लिकेशन लिखने का तरीका बताया गया है. यह ऐप्लिकेशन, वेब और खास प्लैटफ़ॉर्म, दोनों पर चलता है. स्पॉइलर अलर्ट, आपको वही लाल त्रिभुज दिखेगा जो आपके कोड बेस में कम से कम अडजस्टमेंट के साथ ब्राउज़र विंडो और डेस्कटॉप विंडो में दिखता है.
यह सुविधा कैसे काम करती है?
पूरा ऐप्लिकेशन देखने के लिए, WebGPU क्रॉस-प्लैटफ़ॉर्म ऐप्लिकेशन का डेटा स्टोर करने की जगह पर जाएं.
यह ऐप्लिकेशन, C++ का छोटा उदाहरण है. इससे यह पता चलता है कि एक ही कोड बेस से डेस्कटॉप और वेब ऐप्लिकेशन बनाने के लिए, WebGPU का इस्तेमाल कैसे किया जा सकता है. इसमें, WebGPU के webgpu.h का इस्तेमाल, प्लैटफ़ॉर्म-एग्नोस्टिक हार्डवेयर ऐब्स्ट्रैक्शन लेयर के तौर पर, एक C++ रैपर के ज़रिए किया जाता है. इसे webgpu_cpp.h कहा जाता है.
वेब पर, ऐप्लिकेशन को Emscripten के हिसाब से बनाया गया है. इसमें JavaScript API के सबसे ऊपर webgpu.h लागू करने के लिए बाइंडिंग हैं. macOS या Windows जैसे खास प्लैटफ़ॉर्म पर, यह प्रोजेक्ट Dawn, Chromium के क्रॉस-प्लैटफ़ॉर्म WebGPU को लागू करने के लिए बनाया जा सकता है. यह भी बताना ज़रूरी है कि 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.13) # CMake version check
project(app) # Create project "app"
set(CMAKE_CXX_STANDARD 20) # Enable C++20 standard
add_executable(app "main.cpp")
"build/" सब-फ़ोल्डर में बिल्ड फ़ाइलें बनाने के लिए, cmake -B build
चलाएं और cmake --build build
का इस्तेमाल करके, असल में ऐप्लिकेशन बनाएं और एक्ज़ीक्यूटेबल फ़ाइल जनरेट करें.
# Build the app with CMake.
$ cmake -B build && cmake --build build
# Run the app.
$ ./build/app
ऐप्लिकेशन चलता है, लेकिन अभी कोई आउटपुट नहीं है, क्योंकि आपको स्क्रीन पर चीज़ें बनाने के लिए एक तरीका चाहिए.
सुबह पाएं
त्रिकोण बनाने के लिए, Dawn की मदद से फ़ायदा उठाएं. यह Chromium के WebGPU क्रॉस-प्लैटफ़ॉर्म को लागू करता है. इसमें GLFW C++ लाइब्रेरी शामिल है, ताकि स्क्रीन पर ड्राइंग बनाई जा सके. Dawn को डाउनलोड करने का एक तरीका यह है कि आप इसे अपने रिपॉज़िटरी में git सबमॉड्यूल के तौर पर जोड़ें. ये निर्देश, इसे "dawn/" सब-फ़ोल्डर में फ़ेच करते हैं.
$ git init
$ git submodule add https://dawn.googlesource.com/dawn
इसके बाद, CMakeLists.txt
फ़ाइल में इस तरह जोड़ें:
- CMake
DAWN_FETCH_DEPENDENCIES
विकल्प सभी Dawn डिपेंडेंसी फ़ेच करता है. dawn/
सब-फ़ोल्डर को टारगेट में शामिल किया गया है.- आपका ऐप्लिकेशन
webgpu_cpp
,webgpu_dawn
,glfw
, औरwebgpu_glfw
टारगेट पर निर्भर करेगा, ताकि आप बाद मेंmain.cpp
फ़ाइल में उनका इस्तेमाल कर सकें.
…
set(DAWN_FETCH_DEPENDENCIES ON)
add_subdirectory("dawn" EXCLUDE_FROM_ALL)
target_link_libraries(app PRIVATE webgpu_cpp webgpu_dawn glfw webgpu_glfw)
विंडो खोलना
अब जब Dawn उपलब्ध है, स्क्रीन पर चीज़ें बनाने के लिए GLFW का इस्तेमाल करें. सुविधा के लिए, webgpu_glfw
में शामिल इस लाइब्रेरी से आपको ऐसा कोड लिखने की अनुमति मिलती है जो विंडो मैनेजमेंट के लिए प्लैटफ़ॉर्म-एग्नोस्टिक वाला हो.
512x512 रिज़ॉल्यूशन वाली "WebGPU विंडो" नाम की विंडो खोलने के लिए, 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
वैरिएबल बनाना होगा. इसका इस्तेमाल इसी काम के लिए किया जाएगा. सुविधा के लिए, main.cpp
फ़ाइल में सबसे ऊपर instance
की जानकारी दें और main()
में wgpu::CreateInstance()
को कॉल करें.
…
#include <webgpu/webgpu_cpp.h>
wgpu::Instance instance;
…
int main() {
instance = wgpu::CreateInstance();
Start();
}
JavaScript API के आकार की वजह से, जीपीयू को ऐक्सेस नहीं किया जा सकता. C++ में, GetAdapter()
और GetDevice()
नाम के दो हेल्पर फ़ंक्शन बनाएं. ये फ़ंक्शन wgpu::Adapter
और wgpu::Device
के साथ कॉलबैक फ़ंक्शन दिखाते हैं.
#include <iostream>
…
void GetAdapter(void (*callback)(wgpu::Adapter)) {
instance.RequestAdapter(
nullptr,
[](WGPURequestAdapterStatus status, WGPUAdapter cAdapter,
const char* message, void* userdata) {
if (status != WGPURequestAdapterStatus_Success) {
exit(0);
}
wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
reinterpret_cast<void (*)(wgpu::Adapter)>(userdata)(adapter);
}, reinterpret_cast<void*>(callback));
}
void GetDevice(void (*callback)(wgpu::Device)) {
adapter.RequestDevice(
nullptr,
[](WGPURequestDeviceStatus status, WGPUDevice cDevice,
const char* message, void* userdata) {
wgpu::Device device = wgpu::Device::Acquire(cDevice);
device.SetUncapturedErrorCallback(
[](WGPUErrorType type, const char* message, void* userdata) {
std::cout << "Error: " << type << " - message: " << message;
},
nullptr);
reinterpret_cast<void (*)(wgpu::Device)>(userdata)(device);
}, reinterpret_cast<void*>(callback));
}
आसानी से ऐक्सेस करने के लिए, main.cpp
फ़ाइल के सबसे ऊपर दो वैरिएबल wgpu::Adapter
और wgpu::Device
बताएं. GetAdapter()
को कॉल करने के लिए, main()
फ़ंक्शन अपडेट करें और इसके नतीजे के लिए कॉलबैक को adapter
को असाइन करें. इसके बाद, Start()
को कॉल करने से पहले, GetDevice()
को कॉल करें और इसके नतीजे के लिए कॉलबैक को device
को असाइन करें.
wgpu::Adapter adapter;
wgpu::Device device;
…
int main() {
instance = wgpu::CreateInstance();
GetAdapter([](wgpu::Adapter a) {
adapter = a;
GetDevice([](wgpu::Device d) {
device = d;
Start();
});
});
}
त्रिभुज बनाएं
स्वैप करने की चेन को JavaScript API में नहीं दिखाया जाता, क्योंकि इसे ब्राउज़र मैनेज करता है. C++ में, आपको इसे मैन्युअल तरीके से बनाना होगा. एक बार फिर से, सुविधा के लिए, main.cpp
फ़ाइल में सबसे ऊपर एक wgpu::Surface
वैरिएबल घोषित करें. Start()
में GLFW विंडो बनाने के ठीक बाद, wgpu::Surface
(एचटीएमएल कैनवस की तरह) बनाने के लिए, आसान wgpu::glfw::CreateSurfaceForWindow()
फ़ंक्शन को कॉल करें और InitGraphics()
में नए हेल्पर ConfigureSurface()
फ़ंक्शन को कॉल करके उसे कॉन्फ़िगर करें. लूप में अगली टेक्सचर को प्रज़ेंट करने के लिए, आपको surface.Present()
को कॉल करना होगा. इसका कोई असर नहीं दिख रहा है, क्योंकि अभी तक कोई रेंडरिंग नहीं हो रही है.
#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};
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();
}
}
अब नीचे दिए गए कोड की मदद से रेंडर पाइपलाइन बनाने का सही समय है. आसानी से ऐक्सेस करने के लिए, main.cpp
फ़ाइल में सबसे ऊपर wgpu::RenderPipeline
वैरिएबल का एलान करें. साथ ही, InitGraphics()
में हेल्पर फ़ंक्शन CreateRenderPipeline()
को कॉल करें.
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::ShaderModuleWGSLDescriptor wgslDesc{};
wgslDesc.code = shaderCode;
wgpu::ShaderModuleDescriptor shaderModuleDescriptor{
.nextInChain = &wgslDesc};
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 में कंपाइल करें
आइए, अब उन सबसे छोटे बदलावों पर नज़र डालते हैं जो ब्राउज़र विंडो में यह लाल त्रिभुज बनाने के लिए, आपके मौजूदा कोड बेस में बदलाव करने के लिए ज़रूरी हैं. फिर से, ऐप्लिकेशन को Emscripten की मदद से बनाया गया है. यह एक ऐसा टूल है जो WebAssembly में C/C++ प्रोग्राम को कंपाइल करता है. इसमें JavaScript API के सबसे ऊपर webgpu.h को लागू करने के लिए बाइंडिंग है.
C Maker की सेटिंग अपडेट करें
Emscripten इंस्टॉल हो जाने के बाद, CMakeLists.txt
बिल्ड फ़ाइल को इस तरह अपडेट करें.
आपको सिर्फ़ हाइलाइट किया गया कोड बदलना होगा.
- टारगेट फ़ाइल में "html" फ़ाइल एक्सटेंशन को अपने-आप जोड़ने के लिए,
set_target_properties
का इस्तेमाल किया जाता है. दूसरे शब्दों में, आप एक "app.html" फ़ाइल जनरेट करेंगे. - Emscripten में WebGPU सहायता चालू करने के लिए,
USE_WEBGPU
ऐप्लिकेशन लिंक का विकल्प ज़रूरी है. इसके बिना, आपकीmain.cpp
फ़ाइलwebgpu/webgpu_cpp.h
फ़ाइल को ऐक्सेस नहीं कर पाएगी. - यहां
USE_GLFW
ऐप्लिकेशन लिंक विकल्प भी ज़रूरी है, ताकि आप अपने जीएलएफ़डब्ल्यू कोड का फिर से इस्तेमाल कर सकें.
cmake_minimum_required(VERSION 3.13) # CMake version check
project(app) # Create project "app"
set(CMAKE_CXX_STANDARD 20) # Enable C++20 standard
add_executable(app "main.cpp")
if(EMSCRIPTEN)
set_target_properties(app PROPERTIES SUFFIX ".html")
target_link_options(app PRIVATE "-sUSE_WEBGPU=1" "-sUSE_GLFW=3")
else()
set(DAWN_FETCH_DEPENDENCIES ON)
add_subdirectory("dawn" EXCLUDE_FROM_ALL)
target_link_libraries(app PRIVATE webgpu_cpp webgpu_dawn glfw webgpu_glfw)
endif()
कोड अपडेट करें
Emscripten में, wgpu::surface
बनाने के लिए एचटीएमएल कैनवस एलिमेंट की ज़रूरत होती है. इसके लिए, instance.CreateSurface()
को कॉल करें और Emscripten से जनरेट किए गए एचटीएमएल पेज में सही एचटीएमएल कैनवस एलिमेंट से मिलान करने के लिए, #canvas
सिलेक्टर बताएं.
लूप में काम करने के बजाय, emscripten_set_main_loop(Render)
को कॉल करें. इससे यह पक्का हो सकेगा कि Render()
फ़ंक्शन को स्मूद रेट पर कॉल किया जाए, जो ब्राउज़र और मॉनिटर के साथ सही तरीके से अलाइन हो.
#include <GLFW/glfw3.h>
#include <webgpu/webgpu_cpp.h>
#include <iostream>
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#else
#include <webgpu/webgpu_glfw.h>
#endif
void Start() {
if (!glfwInit()) {
return;
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window =
glfwCreateWindow(kWidth, kHeight, "WebGPU window", nullptr, nullptr);
#if defined(__EMSCRIPTEN__)
wgpu::SurfaceDescriptorFromCanvasHTMLSelector canvasDesc{};
canvasDesc.selector = "#canvas";
wgpu::SurfaceDescriptor surfaceDesc{.nextInChain = &canvasDesc};
surface = instance.CreateSurface(&surfaceDesc);
#else
surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
#endif
InitGraphics();
#if defined(__EMSCRIPTEN__)
emscripten_set_main_loop(Render, 0, false);
#else
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
Render();
surface.Present();
instance.ProcessEvents();
}
#endif
}
Emscripten की मदद से ऐप्लिकेशन बनाएं
Emscripten की मदद से ऐप्लिकेशन बनाने के लिए, सिर्फ़ मैजिक emcmake
शेल स्क्रिप्ट के साथ cmake
कमांड जोड़ना होगा. इस बार, build-web
सब-फ़ोल्डर में ऐप्लिकेशन जनरेट करें और एचटीटीपी सर्वर चालू करें. आखिर में, अपना ब्राउज़र खोलें और 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 एपीआई के स्टेबलाइज़ेशन में सुधार.
- Android और iOS के लिए, Dawn की शुरुआती सुविधा.
इस बीच, कृपया सुझाव और सवालों के साथ, Emscripten के लिए WebGPU समस्याएं और डॉन से जुड़ी समस्याएं दर्ज करें.
संसाधन
बेझिझक इस ऐप्लिकेशन का सोर्स कोड एक्सप्लोर करें.
अगर आपको WebGPU की मदद से, C++ में स्थानीय 3D ऐप्लिकेशन बनाने के बारे में ज़्यादा जानकारी चाहिए, तो C++ दस्तावेज़ के लिए WebGPU जानें और Dawn Native WebGPU के उदाहरण देखें.
अगर आपकी दिलचस्पी Rust में है, तो आपके पास WebGPU पर आधारित wgpu ग्राफ़िक लाइब्रेरी को एक्सप्लोर करने का विकल्प है. उनका hello-त्रिकोण का डेमो देखें.
लोगों का आभार
इस लेख की समीक्षा कोरेंटिन वॉलेज़, काई निनोमिया, और रेचल एंड्रू ने की है.
Unsplash पर मार्क-ऑलिवर जोडोइन ने फ़ोटो ली है.