Img2Num C++ (Internal Developer Docs) dev
API Documentation
Loading...
Searching...
No Matches
gpu.h
1#ifndef GPU_H
2#define GPU_H
3
4#if defined(__EMSCRIPTEN__)
5#include <emscripten/emscripten.h>
6#include <emscripten/html5.h>
7#endif
8
9#include <webgpu/webgpu_cpp.h>
10
11#include <fstream>
12#include <iostream>
13#include <iterator>
14#include <map>
15#include <sstream>
16#include <string>
17
18// auto generated by tools/embed_shaders.py
19#include <embedded_shaders.h>
20
21class GPU {
22 private:
23 wgpu::Instance instance;
24 wgpu::Adapter adapter;
25 wgpu::Device device;
26 wgpu::Queue queue;
27
28 bool adapter_ready = false;
29 bool device_ready = false;
30 bool gpu_initialized = false;
31
32 GPU() = default;
33
34 bool validate_device() {
35 if (!device) return false;
36
37 // Try creating a trivial buffer to ensure the device is usable
38 wgpu::BufferDescriptor desc = {};
39 desc.size = 4;
40 desc.usage = wgpu::BufferUsage::CopyDst;
41
42 wgpu::Buffer test = device.CreateBuffer(&desc);
43 return test != nullptr;
44 }
45
46 public:
47 // makes a single global instance that other files can reference
48 static GPU& getClassInstance() {
49 static GPU gpuInstance;
50 return gpuInstance;
51 }
52
53 const wgpu::Device& get_device() {
54 return device;
55 }
56
57 const wgpu::Instance& get_instance() {
58 return instance;
59 }
60
61 const wgpu::Queue& get_queue() {
62 return queue;
63 }
64
65 bool is_initialized() {
66 return gpu_initialized;
67 }
68
69 // Delete copy constructor and assignment operator to prevent duplication
70 GPU(const GPU&) = delete;
71 GPU& operator=(const GPU&) = delete;
72 GPU(GPU&&) = delete;
73 GPU& operator=(GPU&&) = delete;
74
75 std::string readWGSLFile(std::string_view shader_id) {
76 // A simple linear search over 9 items is blazingly fast in C++
77 for (const auto& entry : embedded_shaders::shaders) {
78 if (entry.id == shader_id) {
79 // Safely convert the string_view back to a std::string for Dawn
80 return std::string(entry.source);
81 }
82 }
83
84 // Always good to handle the "not found" case gracefully!
85 return "";
86 }
87
88 wgpu::ComputePipeline createPipeline(const std::string& filename, const std::string& label) {
89 wgpu::ShaderSourceWGSL wgsl;
90 std::string shaderCode = readWGSLFile(filename);
91 wgsl.code = shaderCode.c_str();
92 wgpu::ShaderModuleDescriptor md = {};
93 md.nextInChain = &wgsl;
94 md.label = label.c_str();
95 wgpu::ShaderModule sm = device.CreateShaderModule(&md);
96
97 wgpu::ComputePipelineDescriptor cpd = {};
98 cpd.compute.module = sm;
99 cpd.compute.entryPoint = "main";
100 return device.CreateComputePipeline(&cpd);
101 };
102
103 static uint32_t getAlignedBytesPerRow(uint32_t width, uint32_t bytesPerPixel = 4) {
104 uint32_t unaligned = width * bytesPerPixel;
105 uint32_t align = 256;
106 return (unaligned + align - 1) & ~(align - 1);
107 };
108
109 static void printShaderError(wgpu::ShaderModule shaderModule) {
110 shaderModule.GetCompilationInfo(
111 // Callback Mode (New API Requirement)
112 wgpu::CallbackMode::AllowProcessEvents,
113 // Callback Lambda
114 [](wgpu::CompilationInfoRequestStatus status, const wgpu::CompilationInfo* info) {
115 if (status != wgpu::CompilationInfoRequestStatus::Success || !info) return;
116
117 for (uint32_t i = 0; i < info->messageCount; ++i) {
118 const auto& msg = info->messages[i];
119 std::cerr << "Shader Error ["
120 << (msg.type == wgpu::CompilationMessageType::Error ? "ERR" : "WARN")
121 << "]"
122 << " Line " << msg.lineNum << ":" << msg.linePos << " - "
123 << msg.message.data // .data for StringView
124 << std::endl;
125 }
126 });
127 };
128
129 void init_gpu() { // wgpu::Instance& instance, wgpu::Adapter& adapter, wgpu::Device& device,
130 // wgpu::Queue& queue) {
131 if (gpu_initialized) return;
132
133 wgpu::InstanceDescriptor instanceDesc = {};
134 instance = wgpu::CreateInstance(&instanceDesc);
135
136 if (!instance) {
137 std::cerr << "Fatal: WebGPU instance creation failed." << std::endl;
138 return;
139 }
140
141 // ---------------------------------------------------------
142 // 1. Get Adapter
143 // ---------------------------------------------------------
144 std::cout << "Requesting Adapter..." << std::endl;
145 adapter_ready = false;
146
147 instance.RequestAdapter(
148 nullptr,
149 wgpu::CallbackMode::AllowProcessEvents, // <--- ALLOW EVENTS
150 [this](wgpu::RequestAdapterStatus status, wgpu::Adapter a, wgpu::StringView msg) {
151 if (status == wgpu::RequestAdapterStatus::Success) {
152 adapter = std::move(a);
153 std::cout << "Adapter Acquired" << std::endl;
154 } else {
155 std::cerr << "Adapter Failed: "
156 << std::string_view(msg.data ? msg.data : "", msg.length)
157 << std::endl;
158 }
159 adapter_ready = true; // Unblock the loop
160 });
161
162 // WAIT LOOP: Yield to browser so it can actually find the adapter
163 while (!adapter_ready) {
164 instance.ProcessEvents();
165#if defined(__EMSCRIPTEN__)
166 emscripten_sleep(10); // Sleep 10ms
167#endif
168 }
169
170 if (!adapter) {
171 std::cerr << "Fatal: Could not get WebGPU Adapter." << std::endl;
172 return;
173 }
174
175 // ---------------------------------------------------------
176 // 2. Get Device
177 // ---------------------------------------------------------
178 std::cout << "Requesting Device..." << std::endl;
179 device_ready = false;
180
181 wgpu::DeviceDescriptor deviceDesc = {};
182 deviceDesc.SetUncapturedErrorCallback([](const wgpu::Device&, wgpu::ErrorType type,
183 wgpu::StringView msg) {
184 // 1. Safely extract the string using the provided length
185 std::string err_str = (msg.data && msg.length > 0) ? std::string(msg.data, msg.length)
186 : "Unknown Error (Null message)";
187
188 // 2. Print it safely
189 std::cerr << "\n[WEBGPU FATAL ERROR] Type: " << static_cast<uint32_t>(type)
190 << " | Msg: " << err_str << "\n"
191 << std::endl;
192 });
193 deviceDesc.SetDeviceLostCallback(
194 wgpu::CallbackMode::AllowProcessEvents,
195 [](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView msg) {
196 std::string err_msg = (msg.data && msg.length > 0)
197 ? std::string(msg.data, msg.length)
198 : "Unknown device lost reason";
199 std::cerr << "[DEVICE LOST] Reason: " << static_cast<int>(reason)
200 << " Msg: " << err_msg << std::endl;
201 });
202
203 // Set maximum possible device hardware memory
204 wgpu::Limits supportedLimits;
205 wgpu::Limits requiredLimits;
206 if (adapter.GetLimits(&supportedLimits)) {
207 // nominally the device Buffer limit is 256MB
208
209 // Copy the adapter's physical limits over to your requested limits
210 requiredLimits = supportedLimits;
211
212 std::cout << "maxBufferSize: " << requiredLimits.maxBufferSize << std::endl;
213 std::cout << "maxStorageBufferBindingSize: "
214 << requiredLimits.maxStorageBufferBindingSize << std::endl;
215
216 deviceDesc.requiredLimits = &requiredLimits;
217 }
218
219 adapter.RequestDevice(
220 &deviceDesc,
221 wgpu::CallbackMode::AllowProcessEvents, // <--- ALLOW EVENTS
222 [this](wgpu::RequestDeviceStatus status, wgpu::Device d, wgpu::StringView msg) {
223 if (status == wgpu::RequestDeviceStatus::Success) {
224 device = std::move(d);
225 std::cout << "Device Acquired" << std::endl;
226 } else {
227 std::cerr << "Device Failed: " << msg.data << std::endl;
228 }
229 device_ready = true; // Unblock the loop
230 });
231
232 // WAIT LOOP
233 while (!device_ready) {
234 instance.ProcessEvents();
235#if defined(__EMSCRIPTEN__)
236 emscripten_sleep(10);
237#endif
238 }
239
240 if (!device) {
241 std::cerr << "Fatal: Could not get WebGPU Device." << std::endl;
242 return;
243 }
244
245 if (!validate_device()) {
246 std::cerr << "Fatal: Could not get WebGPU Device." << std::endl;
247 return;
248 }
249
250 queue = device.GetQueue();
251 gpu_initialized = true;
252 std::cout << "GPU Fully Initialized." << std::endl;
253 };
254
255 ~GPU() {
256 device = nullptr;
257 adapter = nullptr;
258 queue = nullptr;
259 instance = nullptr;
260 };
261};
262
263#endif
Definition gpu.h:21