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 <fstream>
10#include <iostream>
11#include <iterator>
12#include <map>
13#include <sstream>
14#include <string>
15#include <webgpu/webgpu_cpp.h>
16
17// auto generated by tools/embed_shaders.py
18#include <embedded_shaders.h>
19
20class GPU {
21 private:
22 wgpu::Instance instance;
23 wgpu::Adapter adapter;
24 wgpu::Device device;
25 wgpu::Queue queue;
26
27 bool adapter_ready = false;
28 bool device_ready = false;
29 bool gpu_initialized = false;
30
31 GPU() = default;
32
33 bool validate_device() {
34 if (!device)
35 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)
116 return;
117
118 for (uint32_t i = 0; i < info->messageCount; ++i) {
119 const auto& msg = info->messages[i];
120 std::cerr << "Shader Error ["
121 << (msg.type == wgpu::CompilationMessageType::Error ? "ERR" : "WARN")
122 << "]"
123 << " Line " << msg.lineNum << ":" << msg.linePos << " - "
124 << msg.message.data // .data for StringView
125 << std::endl;
126 }
127 }
128 );
129 };
130
131 void init_gpu() {
132 if (gpu_initialized)
133 return;
134
135 wgpu::InstanceDescriptor instanceDesc = {};
136 instance = wgpu::CreateInstance(&instanceDesc);
137
138 if (!instance) {
139 std::cerr << "Fatal: WebGPU instance creation failed." << std::endl;
140 return;
141 }
142
143 // ---------------------------------------------------------
144 // 1. Get Adapter
145 // ---------------------------------------------------------
146 std::cout << "Requesting Adapter..." << std::endl;
147 adapter_ready = false;
148
149 instance.RequestAdapter(
150 nullptr,
151 wgpu::CallbackMode::AllowProcessEvents, // <--- ALLOW EVENTS
152 [this](wgpu::RequestAdapterStatus status, wgpu::Adapter a, wgpu::StringView msg) {
153 if (status == wgpu::RequestAdapterStatus::Success) {
154 adapter = std::move(a);
155 std::cout << "Adapter Acquired" << std::endl;
156 } else {
157 std::cerr << "Adapter Failed: "
158 << std::string_view(msg.data ? msg.data : "", msg.length)
159 << std::endl;
160 }
161 adapter_ready = true; // Unblock the loop
162 }
163 );
164
165 // WAIT LOOP: Yield to browser so it can actually find the adapter
166 while (!adapter_ready) {
167 instance.ProcessEvents();
168#if defined(__EMSCRIPTEN__)
169 emscripten_sleep(10); // Sleep 10ms
170#endif
171 }
172
173 if (!adapter) {
174 std::cerr << "Fatal: Could not get WebGPU Adapter." << std::endl;
175 return;
176 }
177
178 // ---------------------------------------------------------
179 // 2. Get Device
180 // ---------------------------------------------------------
181 std::cout << "Requesting Device..." << std::endl;
182 device_ready = false;
183
184 wgpu::DeviceDescriptor deviceDesc = {};
185 deviceDesc.SetUncapturedErrorCallback([](const wgpu::Device&, wgpu::ErrorType type,
186 wgpu::StringView msg) {
187 // 1. Safely extract the string using the provided length
188 std::string err_str = (msg.data && msg.length > 0) ? std::string(msg.data, msg.length)
189 : "Unknown Error (Null message)";
190
191 // 2. Print it safely
192 std::cerr << "\n[WEBGPU FATAL ERROR] Type: " << static_cast<uint32_t>(type)
193 << " | Msg: " << err_str << "\n"
194 << std::endl;
195 });
196 deviceDesc.SetDeviceLostCallback(
197 wgpu::CallbackMode::AllowProcessEvents,
198 [](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView msg) {
199 std::string err_msg = (msg.data && msg.length > 0)
200 ? std::string(msg.data, msg.length)
201 : "Unknown device lost reason";
202 std::cerr << "[DEVICE LOST] Reason: " << static_cast<int>(reason)
203 << " Msg: " << err_msg << std::endl;
204 }
205 );
206
207 // Set maximum possible device hardware memory
208 wgpu::Limits supportedLimits;
209 wgpu::Limits requiredLimits;
210 if (adapter.GetLimits(&supportedLimits)) {
211 // nominally the device Buffer limit is 256MB
212
213 // Copy the adapter's physical limits over to your requested limits
214 requiredLimits = supportedLimits;
215
216 std::cout << "maxBufferSize: " << requiredLimits.maxBufferSize << std::endl;
217 std::cout << "maxStorageBufferBindingSize: "
218 << requiredLimits.maxStorageBufferBindingSize << std::endl;
219
220 deviceDesc.requiredLimits = &requiredLimits;
221 }
222
223 adapter.RequestDevice(
224 &deviceDesc,
225 wgpu::CallbackMode::AllowProcessEvents, // <--- ALLOW EVENTS
226 [this](wgpu::RequestDeviceStatus status, wgpu::Device d, wgpu::StringView msg) {
227 if (status == wgpu::RequestDeviceStatus::Success) {
228 device = std::move(d);
229 std::cout << "Device Acquired" << std::endl;
230 } else {
231 std::cerr << "Device Failed: "
232 << (msg.data && msg.length > 0 ? std::string(msg.data, msg.length)
233 : "Unknown error")
234 << std::endl;
235 }
236 device_ready = true; // Unblock the loop
237 }
238 );
239
240 // WAIT LOOP
241 while (!device_ready) {
242 instance.ProcessEvents();
243#if defined(__EMSCRIPTEN__)
244 emscripten_sleep(10);
245#endif
246 }
247
248 if (!device) {
249 std::cerr << "Fatal: Could not get WebGPU Device." << std::endl;
250 return;
251 }
252
253 if (!validate_device()) {
254 std::cerr << "Fatal: Could not get WebGPU Device." << std::endl;
255 return;
256 }
257
258 queue = device.GetQueue();
259 gpu_initialized = true;
260 std::cout << "GPU Fully Initialized." << std::endl;
261 };
262
263 ~GPU() {
264 device = nullptr;
265 adapter = nullptr;
266 queue = nullptr;
267 instance = nullptr;
268 gpu_initialized = false;
269 };
270};
271
272#endif
Definition gpu.h:20