WebGPU की मदद से ऐप्लिकेशन बनाएं

François Beaufort
François Beaufort

पब्लिश होने की तारीख: 20 जुलाई, 2023, पिछली बार अपडेट किए जाने की तारीख: 27 अगस्त, 2025

वेब डेवलपर के लिए WebGPU, एक वेब ग्राफ़िक्स एपीआई है. यह जीपीयू को एक साथ और तेज़ी से ऐक्सेस करने की सुविधा देता है. WebGPU, आधुनिक हार्डवेयर की क्षमताओं को दिखाता है. साथ ही, Direct3D 12, Metal, और Vulkan की तरह, GPU पर रेंडरिंग और कंप्यूटेशन ऑपरेशन की अनुमति देता है.

यह बात सही है, लेकिन यह कहानी अधूरी है. WebGPU को मिलकर बनाया गया है. इसमें Apple, Google, Intel, Mozilla, और Microsoft जैसी बड़ी कंपनियां शामिल हैं. इनमें से कुछ लोगों को समझ आया कि WebGPU, सिर्फ़ एक JavaScript API नहीं है. यह वेब के अलावा, अलग-अलग इकोसिस्टम के डेवलपर के लिए क्रॉस-प्लैटफ़ॉर्म ग्राफ़िक्स एपीआई है.

इस्तेमाल के मुख्य मामले को पूरा करने के लिए, Chrome 113 में एक JavaScript API पेश किया गया था. हालांकि, इसके साथ-साथ एक और अहम प्रोजेक्ट भी तैयार किया गया है: webgpu.h C API. इस C हेडर फ़ाइल में, WebGPU की सभी उपलब्ध प्रोसेस और डेटा स्ट्रक्चर की सूची दी गई है. यह प्लैटफ़ॉर्म से अलग हार्डवेयर ऐब्स्ट्रैक्शन लेयर के तौर पर काम करता है. इससे आपको अलग-अलग प्लैटफ़ॉर्म पर एक जैसा इंटरफ़ेस मिलता है. साथ ही, आपको प्लैटफ़ॉर्म के हिसाब से ऐप्लिकेशन बनाने की सुविधा मिलती है.

इस दस्तावेज़ में, WebGPU का इस्तेमाल करके एक छोटा C++ ऐप्लिकेशन लिखने का तरीका बताया गया है. यह ऐप्लिकेशन, वेब और कुछ खास प्लैटफ़ॉर्म, दोनों पर काम करता है. स्पॉइलर अलर्ट! आपको वही लाल रंग का त्रिकोण मिलेगा जो ब्राउज़र विंडो और डेस्कटॉप विंडो में दिखता है. इसके लिए, आपको अपने कोडबेस में बहुत कम बदलाव करने होंगे.

macOS पर, ब्राउज़र विंडो और डेस्कटॉप विंडो में WebGPU की मदद से रेंडर किए गए लाल रंग के ट्राएंगल का स्क्रीनशॉट.
ब्राउज़र विंडो और डेस्कटॉप विंडो में WebGPU की मदद से बनाया गया एक ही ट्रायंगल.

यह कैसे काम करता है?

पूरा किया गया ऐप्लिकेशन देखने के लिए, WebGPU क्रॉस-प्लैटफ़ॉर्म ऐप्लिकेशन रिपॉज़िटरी देखें.

यह ऐप्लिकेशन, C++ का एक सामान्य उदाहरण है. इसमें बताया गया है कि एक ही कोडबेस से डेस्कटॉप और वेब ऐप्लिकेशन बनाने के लिए, WebGPU का इस्तेमाल कैसे किया जाता है. यह प्लैटफ़ॉर्म, हार्डवेयर के हिसाब से काम करने वाली एब्स्ट्रैक्शन लेयर के तौर पर WebGPU के webgpu.h का इस्तेमाल करता है. यह C++ रैपर के ज़रिए काम करता है, जिसे webgpu_cpp.h कहा जाता है.

वेब पर, ऐप्लिकेशन को emdawnwebgpu (Emscripten Dawn WebGPU) के हिसाब से बनाया गया है. इसमें JavaScript API के ऊपर webgpu.h को लागू करने वाले बाइंडिंग हैं. macOS या Windows जैसे कुछ प्लैटफ़ॉर्म पर, इस प्रोजेक्ट को Dawn के साथ बनाया जा सकता है. यह Chromium का क्रॉस-प्लैटफ़ॉर्म WebGPU इंप्लीमेंटेशन है. यह ध्यान देने वाली बात है कि webgpu.h का Rust वर्शन, wgpu-native भी मौजूद है. हालांकि, इस दस्तावेज़ में इसका इस्तेमाल नहीं किया गया है.

अपनी प्रोफ़ाइल बनाना शुरू करें

शुरू करने के लिए, आपको 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")

"build/" सब फ़ोल्डर में बिल्ड फ़ाइलें बनाने के लिए, cmake -B build चलाएं. साथ ही, ऐप्लिकेशन को बनाने और एक्ज़ीक्यूटेबल फ़ाइल जनरेट करने के लिए, cmake --build build चलाएं.

# Build the app with CMake.
$ cmake -B build && cmake --build build

# Run the app.
$ ./build/app

ऐप्लिकेशन चल रहा है, लेकिन अभी तक कोई आउटपुट नहीं मिला है. ऐसा इसलिए, क्योंकि आपको स्क्रीन पर चीज़ें बनाने का तरीका चाहिए.

Get Dawn

अपना ट्रायंगल बनाने के लिए, 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 की सभी डिपेंडेंसी फ़ेच करता है.
  • 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();
}

ऐप्लिकेशन को फिर से बनाने और पहले की तरह चलाने पर, अब एक खाली विंडो दिखती है. आपकी प्रोग्रेस दिख रही है!

macOS की खाली विंडो का स्क्रीनशॉट.
खाली विंडो.

जीपीयू डिवाइस पाना

JavaScript में, navigator.gpu जीपीयू को ऐक्सेस करने का आपका एंट्रीपॉइंट है. C++ में, आपको मैन्युअल तरीके से एक wgpu::Instance वैरिएबल बनाना होगा, जिसका इस्तेमाल इसी मकसद के लिए किया जाता है. आसानी के लिए, main.cpp फ़ाइल के सबसे ऊपर instance को डिक्लेयर करें और Init() के अंदर wgpu::CreateInstance() को कॉल करें.

#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();
}

main.cpp फ़ाइल में सबसे ऊपर, दो वैरिएबल wgpu::Adapter और wgpu::Device का एलान करें. 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++ में, आपको इसे मैन्युअल तरीके से बनाना होगा. एक बार फिर, अपनी सुविधा के लिए main.cpp फ़ाइल में सबसे ऊपर wgpu::Surface वैरिएबल का एलान करें. Start() में GLFW विंडो बनाने के तुरंत बाद, wgpu::glfw::CreateSurfaceForWindow() फ़ंक्शन को कॉल करें. इससे wgpu::Surface (एचटीएमएल कैनवस की तरह) बन जाएगा. इसके बाद, InitGraphics() में नए हेल्पर ConfigureSurface() फ़ंक्शन को कॉल करके, इसे कॉन्फ़िगर करें. आपको while लूप में अगला टेक्सचर दिखाने के लिए, 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,
                                    .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();
  }
}

अब नीचे दिए गए कोड की मदद से, रेंडर पाइपलाइन बनाने का सही समय है. आसानी से ऐक्सेस करने के लिए, 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::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() फ़ंक्शन में, GPU को रेंडरिंग के निर्देश भेजें.

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 की मदद से ऐप्लिकेशन को फिर से बनाने और उसे चलाने पर, अब विंडो में लंबे समय से इंतज़ार किया जा रहा लाल रंग का त्रिकोण दिखता है! ब्रेक लें—यह आपका हक है.

macOS विंडो में लाल रंग के त्रिकोण का स्क्रीनशॉट.
डेस्कटॉप विंडो में लाल रंग का त्रिभुज.

WebAssembly में कंपाइल करना

अब हम देखेंगे कि ब्राउज़र विंडो में इस लाल रंग के त्रिकोण को बनाने के लिए, आपको अपने मौजूदा कोडबेस में कम से कम क्या बदलाव करने होंगे. यह ऐप्लिकेशन, emdawnwebgpu (Emscripten Dawn WebGPU) के हिसाब से बनाया गया है. इसमें JavaScript API के ऊपर webgpu.h को लागू करने वाले बाइंडिंग हैं. यह Emscripten का इस्तेमाल करता है. यह C/C++ प्रोग्राम को WebAssembly में कंपाइल करने का टूल है.

CMake की सेटिंग अपडेट करना

Emscripten इंस्टॉल होने के बाद, CMakeLists.txt बिल्ड फ़ाइल को इस तरह अपडेट करें. आपको सिर्फ़ हाइलाइट किए गए कोड में बदलाव करना है.

  • set_target_properties का इस्तेमाल, टारगेट फ़ाइल में "html" फ़ाइल एक्सटेंशन को अपने-आप जोड़ने के लिए किया जाता है. दूसरे शब्दों में, आपको "app.html" फ़ाइल जनरेट करनी होगी.
  • emdawnwebgpu_cpp टारगेट लिंक लाइब्रेरी, Emscripten में WebGPU की सुविधा चालू करती है. इसके बिना, आपकी main.cpp फ़ाइल, webgpu/webgpu_cpp.h फ़ाइल को ऐक्सेस नहीं कर सकती.
  • ASYNCIFY=1 ऐप्लिकेशन लिंक विकल्प की मदद से, सिंक्रोनस C++ कोड, असिंक्रोनस JavaScript के साथ इंटरैक्ट कर सकता है.
  • USE_GLFW=3 ऐप्लिकेशन लिंक विकल्प, Emscripten को GLFW 3 API के बिल्ट-इन JavaScript को लागू करने के लिए कहता है.
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 सब फ़ोल्डर में जनरेट करें और एचटीटीपी सर्वर शुरू करें. आखिर में, अपना ब्राउज़र खोलें और 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 API को ज़्यादा स्थिर बनाने के लिए सुधार किए गए हैं.
  • Android और iOS के लिए, Dawn थीम का शुरुआती वर्शन उपलब्ध कराया गया.

इस दौरान, कृपया सुझाव और सवालों के साथ Emscripten के लिए WebGPU से जुड़ी समस्याएं और Dawn से जुड़ी समस्याएं फ़ाइल करें.

संसाधन

इस ऐप्लिकेशन के सोर्स कोड को एक्सप्लोर करें.

अगर आपको WebGPU की मदद से, C++ में नेटिव 3D ऐप्लिकेशन बनाने के बारे में ज़्यादा जानना है, तो C++ के लिए WebGPU के बारे में जानकारी देने वाला दस्तावेज़ और Dawn Native WebGPU के उदाहरण देखें.

अगर आपकी दिलचस्पी Rust में है, तो WebGPU पर आधारित wgpu ग्राफ़िक्स लाइब्रेरी भी एक्सप्लोर की जा सकती है. उनके hello-triangle डेमो को देखें.

लोगों का आभार

इस लेख की समीक्षा कोरेंटिन वालेज़, काई निनोमिया, और राहेल ऐंड्रयू ने की है.

Unsplash पर Marc-Olivier Jodoin ने यह फ़ोटो ली है.