plusui-native-core 0.1.104 → 0.1.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/Core/.claude/settings.local.json +7 -0
  2. package/Core/CMakeLists.txt +1 -1
  3. package/Core/Features/API/Connect_API.ts +20 -52
  4. package/Core/Features/API/app-api.ts +28 -28
  5. package/Core/Features/API/browser-api.ts +38 -38
  6. package/Core/Features/API/clipboard-api.ts +21 -21
  7. package/Core/Features/API/display-api.ts +33 -33
  8. package/Core/Features/API/keyboard-api.ts +33 -33
  9. package/Core/Features/API/menu-api.ts +39 -39
  10. package/Core/Features/API/router-api.ts +23 -23
  11. package/Core/Features/API/tray-api.ts +22 -22
  12. package/Core/Features/API/webgpu-api.ts +55 -55
  13. package/Core/Features/App/app.cpp +128 -102
  14. package/Core/Features/Browser/browser.cpp +227 -227
  15. package/Core/Features/Browser/browser.ts +161 -161
  16. package/Core/Features/Clipboard/clipboard.cpp +235 -235
  17. package/Core/Features/Display/display.cpp +212 -212
  18. package/Core/Features/FileDrop/filedrop.cpp +448 -324
  19. package/Core/Features/FileDrop/filedrop.css +421 -421
  20. package/Core/Features/FileDrop/filedrop.ts +5 -9
  21. package/Core/Features/Keyboard/keyboard_linux.cpp +4 -0
  22. package/Core/Features/Router/router.cpp +62 -62
  23. package/Core/Features/Router/router.ts +113 -113
  24. package/Core/Features/Tray/tray.cpp +328 -324
  25. package/Core/Features/WebGPU/webgpu.cpp +948 -948
  26. package/Core/Features/Window/webview.cpp +1026 -1014
  27. package/Core/Features/Window/webview.ts +516 -516
  28. package/Core/Features/Window/window.cpp +2265 -1988
  29. package/Core/include/plusui/api.hpp +237 -237
  30. package/Core/include/plusui/app.hpp +33 -33
  31. package/Core/include/plusui/browser.hpp +67 -67
  32. package/Core/include/plusui/clipboard.hpp +41 -41
  33. package/Core/include/plusui/connect.hpp +340 -340
  34. package/Core/include/plusui/connection.hpp +3 -3
  35. package/Core/include/plusui/display.hpp +90 -90
  36. package/Core/include/plusui/filedrop.hpp +92 -77
  37. package/Core/include/plusui/keyboard.hpp +112 -112
  38. package/Core/include/plusui/menu.hpp +153 -153
  39. package/Core/include/plusui/plusui +18 -18
  40. package/Core/include/plusui/router.hpp +42 -42
  41. package/Core/include/plusui/tray.hpp +94 -94
  42. package/Core/include/plusui/webgpu.hpp +434 -434
  43. package/Core/include/plusui/window.hpp +180 -177
  44. package/Core/scripts/generate-umbrella-header.mjs +77 -77
  45. package/Core/vendor/WebView2EnvironmentOptions.h +406 -406
  46. package/Core/vendor/webview.h +487 -487
  47. package/Core/vendor/webview2.h +52079 -52079
  48. package/README.md +19 -19
  49. package/package.json +1 -1
@@ -1,948 +1,948 @@
1
- #include <plusui/webgpu.hpp>
2
-
3
- #ifdef _WIN32
4
- #pragma warning(push)
5
- #pragma warning(disable: 4100) // Suppress unreferenced parameter warnings - placeholder implementations
6
- #endif
7
-
8
- #ifdef PLUSUI_WEBGPU_ENABLED
9
- #include <dawn/webgpu_cpp.h>
10
- #include <dawn_native/DawnNative.h>
11
- #include <dawn_proc/dawn_proc.h>
12
- #include <nlohmann/json.hpp>
13
- #endif
14
-
15
- #include <memory>
16
- #include <sstream>
17
- #include <iostream>
18
- #include <iomanip>
19
-
20
- #ifdef PLUSUI_WEBGPU_ENABLED
21
- using json = nlohmann::json;
22
- #endif
23
-
24
- namespace plusui {
25
-
26
- /**
27
- * @brief Implementation details for WebGPU using Pimpl idiom
28
- */
29
- struct WebGPU::Impl {
30
- #ifdef PLUSUI_WEBGPU_ENABLED
31
- wgpu::Instance instance;
32
- std::unordered_map<std::string, wgpu::Adapter> adapters;
33
- std::unordered_map<std::string, wgpu::Device> devices;
34
- std::unordered_map<std::string, wgpu::Buffer> buffers;
35
- std::unordered_map<std::string, wgpu::Texture> textures;
36
- std::unordered_map<std::string, wgpu::TextureView> textureViews;
37
- std::unordered_map<std::string, wgpu::Sampler> samplers;
38
- std::unordered_map<std::string, wgpu::ShaderModule> shaderModules;
39
- std::unordered_map<std::string, wgpu::RenderPipeline> renderPipelines;
40
- std::unordered_map<std::string, wgpu::ComputePipeline> computePipelines;
41
- std::unordered_map<std::string, wgpu::BindGroupLayout> bindGroupLayouts;
42
- std::unordered_map<std::string, wgpu::BindGroup> bindGroups;
43
- std::unordered_map<std::string, wgpu::CommandEncoder> commandEncoders;
44
- std::unordered_map<std::string, wgpu::RenderPassEncoder> renderPasses;
45
- std::unordered_map<std::string, wgpu::CommandBuffer> commandBuffers;
46
- std::unordered_map<std::string, wgpu::Surface> surfaces;
47
- std::unordered_map<std::string, wgpu::SwapChain> swapChains;
48
-
49
- // Resource counter for generating unique IDs
50
- uint64_t resourceCounter = 0;
51
- #endif
52
-
53
- WebGPUConfig config;
54
- bool initialized = false;
55
-
56
- #ifdef _WIN32
57
- // Windows-specific members can be added here
58
- #elif defined(__APPLE__)
59
- // macOS-specific members can be added here
60
- #else
61
- // Linux-specific members can be added here
62
- #endif
63
-
64
- Impl() = default;
65
- ~Impl() = default;
66
- };
67
-
68
- // ============================================================================
69
- // WebGPU Implementation
70
- // ============================================================================
71
-
72
- WebGPU::WebGPU() : pImpl(std::make_shared<Impl>()) {}
73
-
74
- WebGPU::~WebGPU() {
75
- if (pImpl && pImpl->initialized) {
76
- destroy();
77
- }
78
- }
79
-
80
- WebGPU::WebGPU(WebGPU&&) noexcept = default;
81
- WebGPU& WebGPU::operator=(WebGPU&&) noexcept = default;
82
-
83
- WebGPU WebGPU::create(const WebGPUConfig& config) {
84
- WebGPU instance;
85
- instance.pImpl->config = config;
86
- return instance;
87
- }
88
-
89
- bool WebGPU::initialize() {
90
- #ifndef PLUSUI_WEBGPU_ENABLED
91
- if (pImpl->config.verbose) {
92
- std::cerr << "WebGPU: Support not compiled (PLUSUI_WEBGPU_ENABLED not defined)\n";
93
- }
94
- return false;
95
- #else
96
- if (pImpl->initialized) {
97
- if (pImpl->config.verbose) {
98
- std::cout << "WebGPU: Already initialized\n";
99
- }
100
- return true;
101
- }
102
-
103
- if (!pImpl->config.enabled) {
104
- if (pImpl->config.verbose) {
105
- std::cout << "WebGPU: Disabled in configuration\n";
106
- }
107
- return false;
108
- }
109
-
110
- try {
111
- // Initialize the Dawn proc table
112
- dawnProcSetProcs(dawn_native::GetProcs());
113
-
114
- // Create the instance
115
- WGPUInstanceDescriptor instanceDesc{};
116
- pImpl->instance = wgpu::Instance(wgpuCreateInstance(&instanceDesc));
117
-
118
- if (!pImpl->instance) {
119
- std::cerr << "WebGPU: Failed to create instance\n";
120
- return false;
121
- }
122
-
123
- pImpl->initialized = true;
124
-
125
- if (pImpl->config.verbose) {
126
- std::cout << "WebGPU: Initialization successful\n";
127
- }
128
-
129
- return true;
130
- } catch (const std::exception& e) {
131
- std::cerr << "WebGPU: Initialization error: " << e.what() << "\n";
132
- return false;
133
- }
134
- #endif
135
- }
136
-
137
- void WebGPU::destroy() {
138
- if (!pImpl->initialized) {
139
- return;
140
- }
141
-
142
- #ifdef PLUSUI_WEBGPU_ENABLED
143
- // Destroy all resources in reverse order of dependency
144
- pImpl->renderPasses.clear();
145
- pImpl->commandEncoders.clear();
146
- pImpl->commandBuffers.clear();
147
- pImpl->renderPipelines.clear();
148
- pImpl->computePipelines.clear();
149
- pImpl->bindGroups.clear();
150
- pImpl->bindGroupLayouts.clear();
151
- pImpl->shaderModules.clear();
152
- pImpl->samplers.clear();
153
- pImpl->textureViews.clear();
154
- pImpl->textures.clear();
155
- pImpl->buffers.clear();
156
- pImpl->swapChains.clear();
157
- pImpl->surfaces.clear();
158
- pImpl->devices.clear();
159
- pImpl->adapters.clear();
160
- pImpl->instance = nullptr;
161
- #endif
162
-
163
- pImpl->initialized = false;
164
-
165
- if (pImpl->config.verbose) {
166
- std::cout << "WebGPU: Shutdown complete\n";
167
- }
168
- }
169
-
170
- bool WebGPU::isAvailable() const {
171
- return pImpl->initialized;
172
- }
173
-
174
- // ============================================================================
175
- // Utility Functions
176
- // ============================================================================
177
-
178
- namespace {
179
- std::string generateHandle(const std::string& prefix, uint64_t& counter) {
180
- std::ostringstream oss;
181
- oss << prefix << "_" << std::hex << ++counter;
182
- return oss.str();
183
- }
184
- }
185
-
186
- // ============================================================================
187
- // Adapter API
188
- // ============================================================================
189
-
190
- std::string WebGPU::requestAdapter(const std::string& options) {
191
- #ifndef PLUSUI_WEBGPU_ENABLED
192
- return "";
193
- #else
194
- if (!pImpl->initialized) {
195
- if (pImpl->config.verbose) {
196
- std::cerr << "WebGPU: Not initialized\n";
197
- }
198
- return "";
199
- }
200
-
201
- try {
202
- WGPURequestAdapterOptions wgpuOptions{};
203
-
204
- // Parse options from JSON if provided
205
- if (!options.empty() && options != "{}") {
206
- try {
207
- json opts = json::parse(options);
208
- // Parse power preference if provided
209
- if (opts.contains("powerPreference")) {
210
- std::string powerPref = opts["powerPreference"];
211
- if (powerPref == "low-power") {
212
- wgpuOptions.powerPreference = WGPUPowerPreference_LowPower;
213
- } else if (powerPref == "high-performance") {
214
- wgpuOptions.powerPreference = WGPUPowerPreference_HighPerformance;
215
- }
216
- }
217
- } catch (const std::exception& e) {
218
- std::cerr << "WebGPU: Failed to parse options: " << e.what() << "\n";
219
- }
220
- }
221
-
222
- WGPUAdapter wgpuAdapter = wgpuInstanceRequestAdapter(pImpl->instance.Get(), &wgpuOptions);
223
-
224
- if (!wgpuAdapter) {
225
- if (pImpl->config.verbose) {
226
- std::cerr << "WebGPU: No suitable adapter found\n";
227
- }
228
- return "";
229
- }
230
-
231
- // Store the adapter
232
- std::string adapterId = generateHandle("adapter", pImpl->resourceCounter);
233
- pImpl->adapters[adapterId] = wgpu::Adapter(wgpuAdapter);
234
-
235
- // Build response JSON
236
- json response;
237
- response["id"] = adapterId;
238
- response["type"] = "adapter";
239
- response["features"] = json::array();
240
- response["limits"] = json::object();
241
- response["limits"]["maxBindGroups"] = 8;
242
- response["limits"]["maxDynamicUniformBuffersPerPipelineLayout"] = 8;
243
- response["limits"]["maxDynamicStorageBuffersPerPipelineLayout"] = 4;
244
-
245
- if (pImpl->config.verbose) {
246
- std::cout << "WebGPU: Adapter created: " << adapterId << "\n";
247
- }
248
-
249
- return response.dump();
250
- } catch (const std::exception& e) {
251
- std::cerr << "WebGPU: Error in requestAdapter: " << e.what() << "\n";
252
- return "";
253
- }
254
- #endif
255
- }
256
-
257
- std::string WebGPU::getAdapters() {
258
- #ifndef PLUSUI_WEBGPU_ENABLED
259
- return "[]";
260
- #else
261
- json adaptersJson = json::array();
262
- for (const auto& [id, _] : pImpl->adapters) {
263
- adaptersJson.push_back(id);
264
- }
265
- return adaptersJson.dump();
266
- #endif
267
- }
268
-
269
- // ============================================================================
270
- // Device API
271
- // ============================================================================
272
-
273
- std::string WebGPU::requestDevice(const std::string& adapterId, const std::string& descriptor) {
274
- #ifndef PLUSUI_WEBGPU_ENABLED
275
- return "";
276
- #else
277
- if (!pImpl->initialized) {
278
- return "";
279
- }
280
-
281
- // Find the adapter
282
- auto adapterIt = pImpl->adapters.find(adapterId);
283
- if (adapterIt == pImpl->adapters.end()) {
284
- if (pImpl->config.verbose) {
285
- std::cerr << "WebGPU: Adapter not found: " << adapterId << "\n";
286
- }
287
- return "";
288
- }
289
-
290
- try {
291
- WGPUDeviceDescriptor wgpuDescriptor{};
292
- wgpuDescriptor.label = "Device";
293
-
294
- WGPUDevice wgpuDevice = wgpuAdapterRequestDevice(adapterIt->second.Get(), &wgpuDescriptor);
295
-
296
- if (!wgpuDevice) {
297
- if (pImpl->config.verbose) {
298
- std::cerr << "WebGPU: Failed to create device\n";
299
- }
300
- return "";
301
- }
302
-
303
- std::string deviceId = generateHandle("device", pImpl->resourceCounter);
304
- pImpl->devices[deviceId] = wgpu::Device(wgpuDevice);
305
-
306
- // Set error callback
307
- auto errorCallback = [](WGPUErrorType type, const char* message, void* userdata) {
308
- std::cerr << "WebGPU Error: " << message << "\n";
309
- };
310
- wgpuDeviceSetUncapturedErrorCallback(wgpuDevice, errorCallback, nullptr);
311
-
312
- json response;
313
- response["id"] = deviceId;
314
- response["type"] = "device";
315
- response["features"] = json::array();
316
- response["limits"] = json::object();
317
-
318
- if (pImpl->config.verbose) {
319
- std::cout << "WebGPU: Device created: " << deviceId << "\n";
320
- }
321
-
322
- return response.dump();
323
- } catch (const std::exception& e) {
324
- std::cerr << "WebGPU: Error in requestDevice: " << e.what() << "\n";
325
- return "";
326
- }
327
- #endif
328
- }
329
-
330
- std::string WebGPU::getDeviceInfo(const std::string& deviceId) {
331
- #ifndef PLUSUI_WEBGPU_ENABLED
332
- return "{}";
333
- #else
334
- auto deviceIt = pImpl->devices.find(deviceId);
335
- if (deviceIt == pImpl->devices.end()) {
336
- return "{}";
337
- }
338
-
339
- json info;
340
- info["id"] = deviceId;
341
- info["features"] = json::array();
342
- info["limits"] = json::object();
343
- return info.dump();
344
- #endif
345
- }
346
-
347
- // ============================================================================
348
- // Buffer API
349
- // ============================================================================
350
-
351
- std::string WebGPU::createBuffer(const std::string& deviceId, const std::string& descriptor) {
352
- #ifndef PLUSUI_WEBGPU_ENABLED
353
- return "";
354
- #else
355
- auto deviceIt = pImpl->devices.find(deviceId);
356
- if (deviceIt == pImpl->devices.end()) {
357
- return "";
358
- }
359
-
360
- try {
361
- json desc = json::parse(descriptor);
362
-
363
- WGPUBufferDescriptor wgpuDesc{};
364
- wgpuDesc.size = desc.value("size", 256UL);
365
- wgpuDesc.usage = desc.value("usage", WGPUBufferUsage_CopyDst | WGPUBufferUsage_CopySrc);
366
- wgpuDesc.mappedAtCreation = desc.value("mappedAtCreation", false);
367
-
368
- WGPUBuffer wgpuBuffer = wgpuDeviceCreateBuffer(deviceIt->second.Get(), &wgpuDesc);
369
-
370
- if (!wgpuBuffer) {
371
- if (pImpl->config.verbose) {
372
- std::cerr << "WebGPU: Failed to create buffer\n";
373
- }
374
- return "";
375
- }
376
-
377
- std::string bufferId = generateHandle("buffer", pImpl->resourceCounter);
378
- pImpl->buffers[bufferId] = wgpu::Buffer(wgpuBuffer);
379
-
380
- json response;
381
- response["handle"] = bufferId;
382
- response["size"] = wgpuDesc.size;
383
-
384
- return response.dump();
385
- } catch (const std::exception& e) {
386
- std::cerr << "WebGPU: Error in createBuffer: " << e.what() << "\n";
387
- return "";
388
- }
389
- #endif
390
- }
391
-
392
- bool WebGPU::writeBuffer(const std::string& bufferId, uint32_t offset, const std::string& data) {
393
- #ifndef PLUSUI_WEBGPU_ENABLED
394
- return false;
395
- #else
396
- auto bufferIt = pImpl->buffers.find(bufferId);
397
- if (bufferIt == pImpl->buffers.end()) {
398
- return false;
399
- }
400
-
401
- // TODO: Implement data decoding (base64 or hex)
402
- // For now, just mark as successful
403
- return true;
404
- #endif
405
- }
406
-
407
- bool WebGPU::mapBuffer(const std::string& bufferId, const std::string& mode) {
408
- #ifndef PLUSUI_WEBGPU_ENABLED
409
- return false;
410
- #else
411
- auto bufferIt = pImpl->buffers.find(bufferId);
412
- if (bufferIt == pImpl->buffers.end()) {
413
- return false;
414
- }
415
-
416
- WGPUMapMode mapMode = (mode == "read") ? WGPUMapMode_Read : WGPUMapMode_Write;
417
- auto mapAsyncCallback = [](WGPUBufferMapAsyncStatus status, void* userdata) {
418
- if (status != WGPUBufferMapAsyncStatus_Success) {
419
- std::cerr << "WebGPU: Buffer mapping failed\n";
420
- }
421
- };
422
-
423
- wgpuBufferMapAsync(bufferIt->second.Get(), mapMode, 0, bufferIt->second.GetSize(), mapAsyncCallback, nullptr);
424
- return true;
425
- #endif
426
- }
427
-
428
- bool WebGPU::unmapBuffer(const std::string& bufferId) {
429
- #ifndef PLUSUI_WEBGPU_ENABLED
430
- return false;
431
- #else
432
- auto bufferIt = pImpl->buffers.find(bufferId);
433
- if (bufferIt == pImpl->buffers.end()) {
434
- return false;
435
- }
436
-
437
- wgpuBufferUnmap(bufferIt->second.Get());
438
- return true;
439
- #endif
440
- }
441
-
442
- std::string WebGPU::getMappedBufferData(const std::string& bufferId) {
443
- #ifndef PLUSUI_WEBGPU_ENABLED
444
- return "";
445
- #else
446
- auto bufferIt = pImpl->buffers.find(bufferId);
447
- if (bufferIt == pImpl->buffers.end()) {
448
- return "";
449
- }
450
-
451
- // TODO: Implement data retrieval and encoding
452
- return "";
453
- #endif
454
- }
455
-
456
- // ============================================================================
457
- // Texture API
458
- // ============================================================================
459
-
460
- std::string WebGPU::createTexture(const std::string& deviceId, const std::string& descriptor) {
461
- #ifndef PLUSUI_WEBGPU_ENABLED
462
- return "";
463
- #else
464
- auto deviceIt = pImpl->devices.find(deviceId);
465
- if (deviceIt == pImpl->devices.end()) {
466
- return "";
467
- }
468
-
469
- try {
470
- json desc = json::parse(descriptor);
471
-
472
- WGPUTextureDescriptor wgpuDesc{};
473
- wgpuDesc.size = {
474
- .width = desc.value("width", 256U),
475
- .height = desc.value("height", 256U),
476
- .depthOrArrayLayers = desc.value("depthOrArrayLayers", 1U)
477
- };
478
- wgpuDesc.mipLevelCount = desc.value("mipLevelCount", 1U);
479
- wgpuDesc.sampleCount = desc.value("sampleCount", 1U);
480
- wgpuDesc.dimension = WGPUTextureDimension_2D;
481
- wgpuDesc.format = WGPUTextureFormat_BGRA8Unorm; // Default format
482
- wgpuDesc.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopyDst;
483
-
484
- WGPUTexture wgpuTexture = wgpuDeviceCreateTexture(deviceIt->second.Get(), &wgpuDesc);
485
-
486
- if (!wgpuTexture) {
487
- if (pImpl->config.verbose) {
488
- std::cerr << "WebGPU: Failed to create texture\n";
489
- }
490
- return "";
491
- }
492
-
493
- std::string textureId = generateHandle("texture", pImpl->resourceCounter);
494
- pImpl->textures[textureId] = wgpu::Texture(wgpuTexture);
495
-
496
- json response;
497
- response["handle"] = textureId;
498
- response["width"] = wgpuDesc.size.width;
499
- response["height"] = wgpuDesc.size.height;
500
-
501
- return response.dump();
502
- } catch (const std::exception& e) {
503
- std::cerr << "WebGPU: Error in createTexture: " << e.what() << "\n";
504
- return "";
505
- }
506
- #endif
507
- }
508
-
509
- bool WebGPU::writeTexture(const std::string& textureId, const std::string& descriptor, const std::string& data) {
510
- #ifndef PLUSUI_WEBGPU_ENABLED
511
- return false;
512
- #else
513
- auto textureIt = pImpl->textures.find(textureId);
514
- if (textureIt == pImpl->textures.end()) {
515
- return false;
516
- }
517
-
518
- // TODO: Implement texture data upload
519
- return true;
520
- #endif
521
- }
522
-
523
- std::string WebGPU::createTextureView(const std::string& textureId, const std::string& descriptor) {
524
- #ifndef PLUSUI_WEBGPU_ENABLED
525
- return "";
526
- #else
527
- auto textureIt = pImpl->textures.find(textureId);
528
- if (textureIt == pImpl->textures.end()) {
529
- return "";
530
- }
531
-
532
- try {
533
- WGPUTextureViewDescriptor wgpuDesc{};
534
-
535
- WGPUTextureView wgpuView = wgpuTextureCreateView(textureIt->second.Get(), &wgpuDesc);
536
-
537
- if (!wgpuView) {
538
- return "";
539
- }
540
-
541
- std::string viewId = generateHandle("textureview", pImpl->resourceCounter);
542
- pImpl->textureViews[viewId] = wgpu::TextureView(wgpuView);
543
-
544
- return viewId;
545
- } catch (const std::exception& e) {
546
- std::cerr << "WebGPU: Error in createTextureView: " << e.what() << "\n";
547
- return "";
548
- }
549
- #endif
550
- }
551
-
552
- // ============================================================================
553
- // Sampler API
554
- // ============================================================================
555
-
556
- std::string WebGPU::createSampler(const std::string& deviceId, const std::string& descriptor) {
557
- #ifndef PLUSUI_WEBGPU_ENABLED
558
- return "";
559
- #else
560
- auto deviceIt = pImpl->devices.find(deviceId);
561
- if (deviceIt == pImpl->devices.end()) {
562
- return "";
563
- }
564
-
565
- try {
566
- WGPUSamplerDescriptor wgpuDesc{};
567
- wgpuDesc.magFilter = WGPUFilterMode_Linear;
568
- wgpuDesc.minFilter = WGPUFilterMode_Linear;
569
- wgpuDesc.mipmapFilter = WGPUFilterMode_Linear;
570
- wgpuDesc.addressModeU = WGPUAddressMode_ClampToEdge;
571
- wgpuDesc.addressModeV = WGPUAddressMode_ClampToEdge;
572
- wgpuDesc.addressModeW = WGPUAddressMode_ClampToEdge;
573
-
574
- WGPUSampler wgpuSampler = wgpuDeviceCreateSampler(deviceIt->second.Get(), &wgpuDesc);
575
-
576
- if (!wgpuSampler) {
577
- return "";
578
- }
579
-
580
- std::string samplerId = generateHandle("sampler", pImpl->resourceCounter);
581
- pImpl->samplers[samplerId] = wgpu::Sampler(wgpuSampler);
582
-
583
- return samplerId;
584
- } catch (const std::exception& e) {
585
- std::cerr << "WebGPU: Error in createSampler: " << e.what() << "\n";
586
- return "";
587
- }
588
- #endif
589
- }
590
-
591
- // ============================================================================
592
- // Shader API
593
- // ============================================================================
594
-
595
- std::string WebGPU::createShaderModule(const std::string& deviceId, const std::string& wgslSource) {
596
- #ifndef PLUSUI_WEBGPU_ENABLED
597
- return "";
598
- #else
599
- auto deviceIt = pImpl->devices.find(deviceId);
600
- if (deviceIt == pImpl->devices.end()) {
601
- return "";
602
- }
603
-
604
- try {
605
- WGPUShaderModuleDescriptor wgpuDesc{};
606
- WGPUShaderModuleWGSLDescriptor wgslDesc{};
607
- wgslDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
608
- wgslDesc.code = wgslSource.c_str();
609
- wgpuDesc.nextInChain = &wgslDesc.chain;
610
-
611
- WGPUShaderModule wgpuShader = wgpuDeviceCreateShaderModule(deviceIt->second.Get(), &wgpuDesc);
612
-
613
- if (!wgpuShader) {
614
- if (pImpl->config.verbose) {
615
- std::cerr << "WebGPU: Failed to create shader module\n";
616
- }
617
- return "";
618
- }
619
-
620
- std::string shaderId = generateHandle("shader", pImpl->resourceCounter);
621
- pImpl->shaderModules[shaderId] = wgpu::ShaderModule(wgpuShader);
622
-
623
- return shaderId;
624
- } catch (const std::exception& e) {
625
- std::cerr << "WebGPU: Error in createShaderModule: " << e.what() << "\n";
626
- return "";
627
- }
628
- #endif
629
- }
630
-
631
- std::string WebGPU::getShaderErrors(const std::string& shaderId) {
632
- #ifndef PLUSUI_WEBGPU_ENABLED
633
- return "{}";
634
- #else
635
- // TODO: Implement shader error reporting
636
- return "{}";
637
- #endif
638
- }
639
-
640
- // ============================================================================
641
- // Pipeline API
642
- // ============================================================================
643
-
644
- std::string WebGPU::createRenderPipeline(const std::string& deviceId, const std::string& descriptor) {
645
- #ifndef PLUSUI_WEBGPU_ENABLED
646
- return "";
647
- #else
648
- auto deviceIt = pImpl->devices.find(deviceId);
649
- if (deviceIt == pImpl->devices.end()) {
650
- return "";
651
- }
652
-
653
- // TODO: Implement full pipeline creation
654
- std::string pipelineId = generateHandle("pipeline", pImpl->resourceCounter);
655
- return pipelineId;
656
- #endif
657
- }
658
-
659
- std::string WebGPU::createComputePipeline(const std::string& deviceId, const std::string& descriptor) {
660
- #ifndef PLUSUI_WEBGPU_ENABLED
661
- return "";
662
- #else
663
- auto deviceIt = pImpl->devices.find(deviceId);
664
- if (deviceIt == pImpl->devices.end()) {
665
- return "";
666
- }
667
-
668
- // TODO: Implement compute pipeline creation
669
- std::string pipelineId = generateHandle("pipeline", pImpl->resourceCounter);
670
- return pipelineId;
671
- #endif
672
- }
673
-
674
- std::string WebGPU::getPipelineInfo(const std::string& pipelineId) {
675
- #ifndef PLUSUI_WEBGPU_ENABLED
676
- return "{}";
677
- #else
678
- json info;
679
- info["id"] = pipelineId;
680
- return info.dump();
681
- #endif
682
- }
683
-
684
- // ============================================================================
685
- // Bind Group API
686
- // ============================================================================
687
-
688
- std::string WebGPU::createBindGroupLayout(const std::string& deviceId, const std::string& descriptor) {
689
- #ifndef PLUSUI_WEBGPU_ENABLED
690
- return "";
691
- #else
692
- auto deviceIt = pImpl->devices.find(deviceId);
693
- if (deviceIt == pImpl->devices.end()) {
694
- return "";
695
- }
696
-
697
- std::string layoutId = generateHandle("bindgrouplayout", pImpl->resourceCounter);
698
- return layoutId;
699
- #endif
700
- }
701
-
702
- std::string WebGPU::createBindGroup(const std::string& deviceId, const std::string& descriptor) {
703
- #ifndef PLUSUI_WEBGPU_ENABLED
704
- return "";
705
- #else
706
- auto deviceIt = pImpl->devices.find(deviceId);
707
- if (deviceIt == pImpl->devices.end()) {
708
- return "";
709
- }
710
-
711
- std::string bindGroupId = generateHandle("bindgroup", pImpl->resourceCounter);
712
- return bindGroupId;
713
- #endif
714
- }
715
-
716
- // ============================================================================
717
- // Surface API
718
- // ============================================================================
719
-
720
- std::string WebGPU::createSurface(void* windowHandle, uint32_t width, uint32_t height) {
721
- #ifndef PLUSUI_WEBGPU_ENABLED
722
- return "";
723
- #else
724
- if (!pImpl->initialized) {
725
- return "";
726
- }
727
-
728
- std::string surfaceId = generateHandle("surface", pImpl->resourceCounter);
729
-
730
- if (pImpl->config.verbose) {
731
- std::cout << "WebGPU: Surface created: " << surfaceId << "\n";
732
- }
733
-
734
- return surfaceId;
735
- #endif
736
- }
737
-
738
- bool WebGPU::configureSurface(const std::string& surfaceId, const std::string& descriptor) {
739
- #ifndef PLUSUI_WEBGPU_ENABLED
740
- return false;
741
- #else
742
- return true;
743
- #endif
744
- }
745
-
746
- bool WebGPU::resizeSurface(const std::string& surfaceId, uint32_t width, uint32_t height) {
747
- #ifndef PLUSUI_WEBGPU_ENABLED
748
- return false;
749
- #else
750
- return true;
751
- #endif
752
- }
753
-
754
- std::string WebGPU::getNextTexture(const std::string& surfaceId) {
755
- #ifndef PLUSUI_WEBGPU_ENABLED
756
- return "";
757
- #else
758
- std::string textureId = generateHandle("texture", pImpl->resourceCounter);
759
- return textureId;
760
- #endif
761
- }
762
-
763
- // ============================================================================
764
- // Command Encoding API
765
- // ============================================================================
766
-
767
- std::string WebGPU::createCommandEncoder(const std::string& deviceId) {
768
- #ifndef PLUSUI_WEBGPU_ENABLED
769
- return "";
770
- #else
771
- auto deviceIt = pImpl->devices.find(deviceId);
772
- if (deviceIt == pImpl->devices.end()) {
773
- return "";
774
- }
775
-
776
- std::string encoderId = generateHandle("encoder", pImpl->resourceCounter);
777
- return encoderId;
778
- #endif
779
- }
780
-
781
- std::string WebGPU::beginRenderPass(const std::string& encoderId, const std::string& descriptor) {
782
- #ifndef PLUSUI_WEBGPU_ENABLED
783
- return "";
784
- #else
785
- std::string passId = generateHandle("renderpass", pImpl->resourceCounter);
786
- return passId;
787
- #endif
788
- }
789
-
790
- bool WebGPU::endRenderPass(const std::string& passId) {
791
- #ifndef PLUSUI_WEBGPU_ENABLED
792
- return false;
793
- #else
794
- return true;
795
- #endif
796
- }
797
-
798
- bool WebGPU::setRenderPipeline(const std::string& passId, const std::string& pipelineId) {
799
- #ifndef PLUSUI_WEBGPU_ENABLED
800
- return false;
801
- #else
802
- return true;
803
- #endif
804
- }
805
-
806
- bool WebGPU::setBindGroup(const std::string& passId, uint32_t groupIndex, const std::string& bindGroupId) {
807
- #ifndef PLUSUI_WEBGPU_ENABLED
808
- return false;
809
- #else
810
- return true;
811
- #endif
812
- }
813
-
814
- bool WebGPU::setVertexBuffer(const std::string& passId, uint32_t slot, const std::string& bufferId, uint64_t offset) {
815
- #ifndef PLUSUI_WEBGPU_ENABLED
816
- return false;
817
- #else
818
- return true;
819
- #endif
820
- }
821
-
822
- bool WebGPU::setIndexBuffer(const std::string& passId, const std::string& bufferId, const std::string& format, uint64_t offset) {
823
- #ifndef PLUSUI_WEBGPU_ENABLED
824
- return false;
825
- #else
826
- return true;
827
- #endif
828
- }
829
-
830
- bool WebGPU::draw(const std::string& passId, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) {
831
- #ifndef PLUSUI_WEBGPU_ENABLED
832
- return false;
833
- #else
834
- return true;
835
- #endif
836
- }
837
-
838
- bool WebGPU::drawIndexed(const std::string& passId, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) {
839
- #ifndef PLUSUI_WEBGPU_ENABLED
840
- return false;
841
- #else
842
- return true;
843
- #endif
844
- }
845
-
846
- std::string WebGPU::finish(const std::string& encoderId) {
847
- #ifndef PLUSUI_WEBGPU_ENABLED
848
- return "";
849
- #else
850
- std::string bufferId = generateHandle("cmdbuffer", pImpl->resourceCounter);
851
- return bufferId;
852
- #endif
853
- }
854
-
855
- // ============================================================================
856
- // Queue API
857
- // ============================================================================
858
-
859
- bool WebGPU::submitCommands(const std::string& deviceId, const std::string& commandBufferIds) {
860
- #ifndef PLUSUI_WEBGPU_ENABLED
861
- return false;
862
- #else
863
- auto deviceIt = pImpl->devices.find(deviceId);
864
- if (deviceIt == pImpl->devices.end()) {
865
- return false;
866
- }
867
-
868
- return true;
869
- #endif
870
- }
871
-
872
- bool WebGPU::presentSurface(const std::string& surfaceId) {
873
- #ifndef PLUSUI_WEBGPU_ENABLED
874
- return false;
875
- #else
876
- return true;
877
- #endif
878
- }
879
-
880
- // ============================================================================
881
- // Resource Management
882
- // ============================================================================
883
-
884
- bool WebGPU::destroyResource(const std::string& handle) {
885
- #ifndef PLUSUI_WEBGPU_ENABLED
886
- (void)handle;
887
- return false;
888
- #else
889
- // Try to find and erase from all resource maps
890
- pImpl->buffers.erase(handle);
891
- pImpl->textures.erase(handle);
892
- pImpl->textureViews.erase(handle);
893
- pImpl->samplers.erase(handle);
894
- pImpl->shaderModules.erase(handle);
895
- pImpl->renderPipelines.erase(handle);
896
- pImpl->computePipelines.erase(handle);
897
- pImpl->bindGroups.erase(handle);
898
- pImpl->bindGroupLayouts.erase(handle);
899
- pImpl->surfaces.erase(handle);
900
- pImpl->swapChains.erase(handle);
901
- pImpl->commandBuffers.erase(handle);
902
- pImpl->adapters.erase(handle);
903
-
904
- return true;
905
- #endif
906
- }
907
-
908
- bool WebGPU::destroyDevice(const std::string& deviceId) {
909
- #ifndef PLUSUI_WEBGPU_ENABLED
910
- (void)deviceId;
911
- return false;
912
- #else
913
- // Destroy all resources associated with device
914
- pImpl->buffers.clear();
915
- pImpl->textures.clear();
916
- pImpl->textureViews.clear();
917
- pImpl->samplers.clear();
918
- pImpl->shaderModules.clear();
919
- pImpl->renderPipelines.clear();
920
- pImpl->computePipelines.clear();
921
- pImpl->bindGroups.clear();
922
-
923
- pImpl->devices.erase(deviceId);
924
-
925
- return true;
926
- #endif
927
- }
928
-
929
- std::string WebGPU::getMemoryStats() {
930
- #ifndef PLUSUI_WEBGPU_ENABLED
931
- return "{}";
932
- #else
933
- json stats;
934
- stats["buffers"] = pImpl->buffers.size();
935
- stats["textures"] = pImpl->textures.size();
936
- stats["samplers"] = pImpl->samplers.size();
937
- stats["shaders"] = pImpl->shaderModules.size();
938
- stats["pipelines"] = pImpl->renderPipelines.size() + pImpl->computePipelines.size();
939
- stats["devices"] = pImpl->devices.size();
940
- return stats.dump();
941
- #endif
942
- }
943
-
944
- } // namespace plusui
945
-
946
- #ifdef _WIN32
947
- #pragma warning(pop)
948
- #endif
1
+ #include <plusui/webgpu.hpp>
2
+
3
+ #ifdef _WIN32
4
+ #pragma warning(push)
5
+ #pragma warning(disable: 4100) // Suppress unreferenced parameter warnings - placeholder implementations
6
+ #endif
7
+
8
+ #ifdef PLUSUI_WEBGPU_ENABLED
9
+ #include <dawn/webgpu_cpp.h>
10
+ #include <dawn_native/DawnNative.h>
11
+ #include <dawn_proc/dawn_proc.h>
12
+ #include <nlohmann/json.hpp>
13
+ #endif
14
+
15
+ #include <memory>
16
+ #include <sstream>
17
+ #include <iostream>
18
+ #include <iomanip>
19
+
20
+ #ifdef PLUSUI_WEBGPU_ENABLED
21
+ using json = nlohmann::json;
22
+ #endif
23
+
24
+ namespace plusui {
25
+
26
+ /**
27
+ * @brief Implementation details for WebGPU using Pimpl idiom
28
+ */
29
+ struct WebGPU::Impl {
30
+ #ifdef PLUSUI_WEBGPU_ENABLED
31
+ wgpu::Instance instance;
32
+ std::unordered_map<std::string, wgpu::Adapter> adapters;
33
+ std::unordered_map<std::string, wgpu::Device> devices;
34
+ std::unordered_map<std::string, wgpu::Buffer> buffers;
35
+ std::unordered_map<std::string, wgpu::Texture> textures;
36
+ std::unordered_map<std::string, wgpu::TextureView> textureViews;
37
+ std::unordered_map<std::string, wgpu::Sampler> samplers;
38
+ std::unordered_map<std::string, wgpu::ShaderModule> shaderModules;
39
+ std::unordered_map<std::string, wgpu::RenderPipeline> renderPipelines;
40
+ std::unordered_map<std::string, wgpu::ComputePipeline> computePipelines;
41
+ std::unordered_map<std::string, wgpu::BindGroupLayout> bindGroupLayouts;
42
+ std::unordered_map<std::string, wgpu::BindGroup> bindGroups;
43
+ std::unordered_map<std::string, wgpu::CommandEncoder> commandEncoders;
44
+ std::unordered_map<std::string, wgpu::RenderPassEncoder> renderPasses;
45
+ std::unordered_map<std::string, wgpu::CommandBuffer> commandBuffers;
46
+ std::unordered_map<std::string, wgpu::Surface> surfaces;
47
+ std::unordered_map<std::string, wgpu::SwapChain> swapChains;
48
+
49
+ // Resource counter for generating unique IDs
50
+ uint64_t resourceCounter = 0;
51
+ #endif
52
+
53
+ WebGPUConfig config;
54
+ bool initialized = false;
55
+
56
+ #ifdef _WIN32
57
+ // Windows-specific members can be added here
58
+ #elif defined(__APPLE__)
59
+ // macOS-specific members can be added here
60
+ #else
61
+ // Linux-specific members can be added here
62
+ #endif
63
+
64
+ Impl() = default;
65
+ ~Impl() = default;
66
+ };
67
+
68
+ // ============================================================================
69
+ // WebGPU Implementation
70
+ // ============================================================================
71
+
72
+ WebGPU::WebGPU() : pImpl(std::make_shared<Impl>()) {}
73
+
74
+ WebGPU::~WebGPU() {
75
+ if (pImpl && pImpl->initialized) {
76
+ destroy();
77
+ }
78
+ }
79
+
80
+ WebGPU::WebGPU(WebGPU&&) noexcept = default;
81
+ WebGPU& WebGPU::operator=(WebGPU&&) noexcept = default;
82
+
83
+ WebGPU WebGPU::create(const WebGPUConfig& config) {
84
+ WebGPU instance;
85
+ instance.pImpl->config = config;
86
+ return instance;
87
+ }
88
+
89
+ bool WebGPU::initialize() {
90
+ #ifndef PLUSUI_WEBGPU_ENABLED
91
+ if (pImpl->config.verbose) {
92
+ std::cerr << "WebGPU: Support not compiled (PLUSUI_WEBGPU_ENABLED not defined)\n";
93
+ }
94
+ return false;
95
+ #else
96
+ if (pImpl->initialized) {
97
+ if (pImpl->config.verbose) {
98
+ std::cout << "WebGPU: Already initialized\n";
99
+ }
100
+ return true;
101
+ }
102
+
103
+ if (!pImpl->config.enabled) {
104
+ if (pImpl->config.verbose) {
105
+ std::cout << "WebGPU: Disabled in configuration\n";
106
+ }
107
+ return false;
108
+ }
109
+
110
+ try {
111
+ // Initialize the Dawn proc table
112
+ dawnProcSetProcs(dawn_native::GetProcs());
113
+
114
+ // Create the instance
115
+ WGPUInstanceDescriptor instanceDesc{};
116
+ pImpl->instance = wgpu::Instance(wgpuCreateInstance(&instanceDesc));
117
+
118
+ if (!pImpl->instance) {
119
+ std::cerr << "WebGPU: Failed to create instance\n";
120
+ return false;
121
+ }
122
+
123
+ pImpl->initialized = true;
124
+
125
+ if (pImpl->config.verbose) {
126
+ std::cout << "WebGPU: Initialization successful\n";
127
+ }
128
+
129
+ return true;
130
+ } catch (const std::exception& e) {
131
+ std::cerr << "WebGPU: Initialization error: " << e.what() << "\n";
132
+ return false;
133
+ }
134
+ #endif
135
+ }
136
+
137
+ void WebGPU::destroy() {
138
+ if (!pImpl->initialized) {
139
+ return;
140
+ }
141
+
142
+ #ifdef PLUSUI_WEBGPU_ENABLED
143
+ // Destroy all resources in reverse order of dependency
144
+ pImpl->renderPasses.clear();
145
+ pImpl->commandEncoders.clear();
146
+ pImpl->commandBuffers.clear();
147
+ pImpl->renderPipelines.clear();
148
+ pImpl->computePipelines.clear();
149
+ pImpl->bindGroups.clear();
150
+ pImpl->bindGroupLayouts.clear();
151
+ pImpl->shaderModules.clear();
152
+ pImpl->samplers.clear();
153
+ pImpl->textureViews.clear();
154
+ pImpl->textures.clear();
155
+ pImpl->buffers.clear();
156
+ pImpl->swapChains.clear();
157
+ pImpl->surfaces.clear();
158
+ pImpl->devices.clear();
159
+ pImpl->adapters.clear();
160
+ pImpl->instance = nullptr;
161
+ #endif
162
+
163
+ pImpl->initialized = false;
164
+
165
+ if (pImpl->config.verbose) {
166
+ std::cout << "WebGPU: Shutdown complete\n";
167
+ }
168
+ }
169
+
170
+ bool WebGPU::isAvailable() const {
171
+ return pImpl->initialized;
172
+ }
173
+
174
+ // ============================================================================
175
+ // Utility Functions
176
+ // ============================================================================
177
+
178
+ namespace {
179
+ std::string generateHandle(const std::string& prefix, uint64_t& counter) {
180
+ std::ostringstream oss;
181
+ oss << prefix << "_" << std::hex << ++counter;
182
+ return oss.str();
183
+ }
184
+ }
185
+
186
+ // ============================================================================
187
+ // Adapter API
188
+ // ============================================================================
189
+
190
+ std::string WebGPU::requestAdapter(const std::string& options) {
191
+ #ifndef PLUSUI_WEBGPU_ENABLED
192
+ return "";
193
+ #else
194
+ if (!pImpl->initialized) {
195
+ if (pImpl->config.verbose) {
196
+ std::cerr << "WebGPU: Not initialized\n";
197
+ }
198
+ return "";
199
+ }
200
+
201
+ try {
202
+ WGPURequestAdapterOptions wgpuOptions{};
203
+
204
+ // Parse options from JSON if provided
205
+ if (!options.empty() && options != "{}") {
206
+ try {
207
+ json opts = json::parse(options);
208
+ // Parse power preference if provided
209
+ if (opts.contains("powerPreference")) {
210
+ std::string powerPref = opts["powerPreference"];
211
+ if (powerPref == "low-power") {
212
+ wgpuOptions.powerPreference = WGPUPowerPreference_LowPower;
213
+ } else if (powerPref == "high-performance") {
214
+ wgpuOptions.powerPreference = WGPUPowerPreference_HighPerformance;
215
+ }
216
+ }
217
+ } catch (const std::exception& e) {
218
+ std::cerr << "WebGPU: Failed to parse options: " << e.what() << "\n";
219
+ }
220
+ }
221
+
222
+ WGPUAdapter wgpuAdapter = wgpuInstanceRequestAdapter(pImpl->instance.Get(), &wgpuOptions);
223
+
224
+ if (!wgpuAdapter) {
225
+ if (pImpl->config.verbose) {
226
+ std::cerr << "WebGPU: No suitable adapter found\n";
227
+ }
228
+ return "";
229
+ }
230
+
231
+ // Store the adapter
232
+ std::string adapterId = generateHandle("adapter", pImpl->resourceCounter);
233
+ pImpl->adapters[adapterId] = wgpu::Adapter(wgpuAdapter);
234
+
235
+ // Build response JSON
236
+ json response;
237
+ response["id"] = adapterId;
238
+ response["type"] = "adapter";
239
+ response["features"] = json::array();
240
+ response["limits"] = json::object();
241
+ response["limits"]["maxBindGroups"] = 8;
242
+ response["limits"]["maxDynamicUniformBuffersPerPipelineLayout"] = 8;
243
+ response["limits"]["maxDynamicStorageBuffersPerPipelineLayout"] = 4;
244
+
245
+ if (pImpl->config.verbose) {
246
+ std::cout << "WebGPU: Adapter created: " << adapterId << "\n";
247
+ }
248
+
249
+ return response.dump();
250
+ } catch (const std::exception& e) {
251
+ std::cerr << "WebGPU: Error in requestAdapter: " << e.what() << "\n";
252
+ return "";
253
+ }
254
+ #endif
255
+ }
256
+
257
+ std::string WebGPU::getAdapters() {
258
+ #ifndef PLUSUI_WEBGPU_ENABLED
259
+ return "[]";
260
+ #else
261
+ json adaptersJson = json::array();
262
+ for (const auto& [id, _] : pImpl->adapters) {
263
+ adaptersJson.push_back(id);
264
+ }
265
+ return adaptersJson.dump();
266
+ #endif
267
+ }
268
+
269
+ // ============================================================================
270
+ // Device API
271
+ // ============================================================================
272
+
273
+ std::string WebGPU::requestDevice(const std::string& adapterId, const std::string& descriptor) {
274
+ #ifndef PLUSUI_WEBGPU_ENABLED
275
+ return "";
276
+ #else
277
+ if (!pImpl->initialized) {
278
+ return "";
279
+ }
280
+
281
+ // Find the adapter
282
+ auto adapterIt = pImpl->adapters.find(adapterId);
283
+ if (adapterIt == pImpl->adapters.end()) {
284
+ if (pImpl->config.verbose) {
285
+ std::cerr << "WebGPU: Adapter not found: " << adapterId << "\n";
286
+ }
287
+ return "";
288
+ }
289
+
290
+ try {
291
+ WGPUDeviceDescriptor wgpuDescriptor{};
292
+ wgpuDescriptor.label = "Device";
293
+
294
+ WGPUDevice wgpuDevice = wgpuAdapterRequestDevice(adapterIt->second.Get(), &wgpuDescriptor);
295
+
296
+ if (!wgpuDevice) {
297
+ if (pImpl->config.verbose) {
298
+ std::cerr << "WebGPU: Failed to create device\n";
299
+ }
300
+ return "";
301
+ }
302
+
303
+ std::string deviceId = generateHandle("device", pImpl->resourceCounter);
304
+ pImpl->devices[deviceId] = wgpu::Device(wgpuDevice);
305
+
306
+ // Set error callback
307
+ auto errorCallback = [](WGPUErrorType type, const char* message, void* userdata) {
308
+ std::cerr << "WebGPU Error: " << message << "\n";
309
+ };
310
+ wgpuDeviceSetUncapturedErrorCallback(wgpuDevice, errorCallback, nullptr);
311
+
312
+ json response;
313
+ response["id"] = deviceId;
314
+ response["type"] = "device";
315
+ response["features"] = json::array();
316
+ response["limits"] = json::object();
317
+
318
+ if (pImpl->config.verbose) {
319
+ std::cout << "WebGPU: Device created: " << deviceId << "\n";
320
+ }
321
+
322
+ return response.dump();
323
+ } catch (const std::exception& e) {
324
+ std::cerr << "WebGPU: Error in requestDevice: " << e.what() << "\n";
325
+ return "";
326
+ }
327
+ #endif
328
+ }
329
+
330
+ std::string WebGPU::getDeviceInfo(const std::string& deviceId) {
331
+ #ifndef PLUSUI_WEBGPU_ENABLED
332
+ return "{}";
333
+ #else
334
+ auto deviceIt = pImpl->devices.find(deviceId);
335
+ if (deviceIt == pImpl->devices.end()) {
336
+ return "{}";
337
+ }
338
+
339
+ json info;
340
+ info["id"] = deviceId;
341
+ info["features"] = json::array();
342
+ info["limits"] = json::object();
343
+ return info.dump();
344
+ #endif
345
+ }
346
+
347
+ // ============================================================================
348
+ // Buffer API
349
+ // ============================================================================
350
+
351
+ std::string WebGPU::createBuffer(const std::string& deviceId, const std::string& descriptor) {
352
+ #ifndef PLUSUI_WEBGPU_ENABLED
353
+ return "";
354
+ #else
355
+ auto deviceIt = pImpl->devices.find(deviceId);
356
+ if (deviceIt == pImpl->devices.end()) {
357
+ return "";
358
+ }
359
+
360
+ try {
361
+ json desc = json::parse(descriptor);
362
+
363
+ WGPUBufferDescriptor wgpuDesc{};
364
+ wgpuDesc.size = desc.value("size", 256UL);
365
+ wgpuDesc.usage = desc.value("usage", WGPUBufferUsage_CopyDst | WGPUBufferUsage_CopySrc);
366
+ wgpuDesc.mappedAtCreation = desc.value("mappedAtCreation", false);
367
+
368
+ WGPUBuffer wgpuBuffer = wgpuDeviceCreateBuffer(deviceIt->second.Get(), &wgpuDesc);
369
+
370
+ if (!wgpuBuffer) {
371
+ if (pImpl->config.verbose) {
372
+ std::cerr << "WebGPU: Failed to create buffer\n";
373
+ }
374
+ return "";
375
+ }
376
+
377
+ std::string bufferId = generateHandle("buffer", pImpl->resourceCounter);
378
+ pImpl->buffers[bufferId] = wgpu::Buffer(wgpuBuffer);
379
+
380
+ json response;
381
+ response["handle"] = bufferId;
382
+ response["size"] = wgpuDesc.size;
383
+
384
+ return response.dump();
385
+ } catch (const std::exception& e) {
386
+ std::cerr << "WebGPU: Error in createBuffer: " << e.what() << "\n";
387
+ return "";
388
+ }
389
+ #endif
390
+ }
391
+
392
+ bool WebGPU::writeBuffer(const std::string& bufferId, uint32_t offset, const std::string& data) {
393
+ #ifndef PLUSUI_WEBGPU_ENABLED
394
+ return false;
395
+ #else
396
+ auto bufferIt = pImpl->buffers.find(bufferId);
397
+ if (bufferIt == pImpl->buffers.end()) {
398
+ return false;
399
+ }
400
+
401
+ // TODO: Implement data decoding (base64 or hex)
402
+ // For now, just mark as successful
403
+ return true;
404
+ #endif
405
+ }
406
+
407
+ bool WebGPU::mapBuffer(const std::string& bufferId, const std::string& mode) {
408
+ #ifndef PLUSUI_WEBGPU_ENABLED
409
+ return false;
410
+ #else
411
+ auto bufferIt = pImpl->buffers.find(bufferId);
412
+ if (bufferIt == pImpl->buffers.end()) {
413
+ return false;
414
+ }
415
+
416
+ WGPUMapMode mapMode = (mode == "read") ? WGPUMapMode_Read : WGPUMapMode_Write;
417
+ auto mapAsyncCallback = [](WGPUBufferMapAsyncStatus status, void* userdata) {
418
+ if (status != WGPUBufferMapAsyncStatus_Success) {
419
+ std::cerr << "WebGPU: Buffer mapping failed\n";
420
+ }
421
+ };
422
+
423
+ wgpuBufferMapAsync(bufferIt->second.Get(), mapMode, 0, bufferIt->second.GetSize(), mapAsyncCallback, nullptr);
424
+ return true;
425
+ #endif
426
+ }
427
+
428
+ bool WebGPU::unmapBuffer(const std::string& bufferId) {
429
+ #ifndef PLUSUI_WEBGPU_ENABLED
430
+ return false;
431
+ #else
432
+ auto bufferIt = pImpl->buffers.find(bufferId);
433
+ if (bufferIt == pImpl->buffers.end()) {
434
+ return false;
435
+ }
436
+
437
+ wgpuBufferUnmap(bufferIt->second.Get());
438
+ return true;
439
+ #endif
440
+ }
441
+
442
+ std::string WebGPU::getMappedBufferData(const std::string& bufferId) {
443
+ #ifndef PLUSUI_WEBGPU_ENABLED
444
+ return "";
445
+ #else
446
+ auto bufferIt = pImpl->buffers.find(bufferId);
447
+ if (bufferIt == pImpl->buffers.end()) {
448
+ return "";
449
+ }
450
+
451
+ // TODO: Implement data retrieval and encoding
452
+ return "";
453
+ #endif
454
+ }
455
+
456
+ // ============================================================================
457
+ // Texture API
458
+ // ============================================================================
459
+
460
+ std::string WebGPU::createTexture(const std::string& deviceId, const std::string& descriptor) {
461
+ #ifndef PLUSUI_WEBGPU_ENABLED
462
+ return "";
463
+ #else
464
+ auto deviceIt = pImpl->devices.find(deviceId);
465
+ if (deviceIt == pImpl->devices.end()) {
466
+ return "";
467
+ }
468
+
469
+ try {
470
+ json desc = json::parse(descriptor);
471
+
472
+ WGPUTextureDescriptor wgpuDesc{};
473
+ wgpuDesc.size = {
474
+ .width = desc.value("width", 256U),
475
+ .height = desc.value("height", 256U),
476
+ .depthOrArrayLayers = desc.value("depthOrArrayLayers", 1U)
477
+ };
478
+ wgpuDesc.mipLevelCount = desc.value("mipLevelCount", 1U);
479
+ wgpuDesc.sampleCount = desc.value("sampleCount", 1U);
480
+ wgpuDesc.dimension = WGPUTextureDimension_2D;
481
+ wgpuDesc.format = WGPUTextureFormat_BGRA8Unorm; // Default format
482
+ wgpuDesc.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopyDst;
483
+
484
+ WGPUTexture wgpuTexture = wgpuDeviceCreateTexture(deviceIt->second.Get(), &wgpuDesc);
485
+
486
+ if (!wgpuTexture) {
487
+ if (pImpl->config.verbose) {
488
+ std::cerr << "WebGPU: Failed to create texture\n";
489
+ }
490
+ return "";
491
+ }
492
+
493
+ std::string textureId = generateHandle("texture", pImpl->resourceCounter);
494
+ pImpl->textures[textureId] = wgpu::Texture(wgpuTexture);
495
+
496
+ json response;
497
+ response["handle"] = textureId;
498
+ response["width"] = wgpuDesc.size.width;
499
+ response["height"] = wgpuDesc.size.height;
500
+
501
+ return response.dump();
502
+ } catch (const std::exception& e) {
503
+ std::cerr << "WebGPU: Error in createTexture: " << e.what() << "\n";
504
+ return "";
505
+ }
506
+ #endif
507
+ }
508
+
509
+ bool WebGPU::writeTexture(const std::string& textureId, const std::string& descriptor, const std::string& data) {
510
+ #ifndef PLUSUI_WEBGPU_ENABLED
511
+ return false;
512
+ #else
513
+ auto textureIt = pImpl->textures.find(textureId);
514
+ if (textureIt == pImpl->textures.end()) {
515
+ return false;
516
+ }
517
+
518
+ // TODO: Implement texture data upload
519
+ return true;
520
+ #endif
521
+ }
522
+
523
+ std::string WebGPU::createTextureView(const std::string& textureId, const std::string& descriptor) {
524
+ #ifndef PLUSUI_WEBGPU_ENABLED
525
+ return "";
526
+ #else
527
+ auto textureIt = pImpl->textures.find(textureId);
528
+ if (textureIt == pImpl->textures.end()) {
529
+ return "";
530
+ }
531
+
532
+ try {
533
+ WGPUTextureViewDescriptor wgpuDesc{};
534
+
535
+ WGPUTextureView wgpuView = wgpuTextureCreateView(textureIt->second.Get(), &wgpuDesc);
536
+
537
+ if (!wgpuView) {
538
+ return "";
539
+ }
540
+
541
+ std::string viewId = generateHandle("textureview", pImpl->resourceCounter);
542
+ pImpl->textureViews[viewId] = wgpu::TextureView(wgpuView);
543
+
544
+ return viewId;
545
+ } catch (const std::exception& e) {
546
+ std::cerr << "WebGPU: Error in createTextureView: " << e.what() << "\n";
547
+ return "";
548
+ }
549
+ #endif
550
+ }
551
+
552
+ // ============================================================================
553
+ // Sampler API
554
+ // ============================================================================
555
+
556
+ std::string WebGPU::createSampler(const std::string& deviceId, const std::string& descriptor) {
557
+ #ifndef PLUSUI_WEBGPU_ENABLED
558
+ return "";
559
+ #else
560
+ auto deviceIt = pImpl->devices.find(deviceId);
561
+ if (deviceIt == pImpl->devices.end()) {
562
+ return "";
563
+ }
564
+
565
+ try {
566
+ WGPUSamplerDescriptor wgpuDesc{};
567
+ wgpuDesc.magFilter = WGPUFilterMode_Linear;
568
+ wgpuDesc.minFilter = WGPUFilterMode_Linear;
569
+ wgpuDesc.mipmapFilter = WGPUFilterMode_Linear;
570
+ wgpuDesc.addressModeU = WGPUAddressMode_ClampToEdge;
571
+ wgpuDesc.addressModeV = WGPUAddressMode_ClampToEdge;
572
+ wgpuDesc.addressModeW = WGPUAddressMode_ClampToEdge;
573
+
574
+ WGPUSampler wgpuSampler = wgpuDeviceCreateSampler(deviceIt->second.Get(), &wgpuDesc);
575
+
576
+ if (!wgpuSampler) {
577
+ return "";
578
+ }
579
+
580
+ std::string samplerId = generateHandle("sampler", pImpl->resourceCounter);
581
+ pImpl->samplers[samplerId] = wgpu::Sampler(wgpuSampler);
582
+
583
+ return samplerId;
584
+ } catch (const std::exception& e) {
585
+ std::cerr << "WebGPU: Error in createSampler: " << e.what() << "\n";
586
+ return "";
587
+ }
588
+ #endif
589
+ }
590
+
591
+ // ============================================================================
592
+ // Shader API
593
+ // ============================================================================
594
+
595
+ std::string WebGPU::createShaderModule(const std::string& deviceId, const std::string& wgslSource) {
596
+ #ifndef PLUSUI_WEBGPU_ENABLED
597
+ return "";
598
+ #else
599
+ auto deviceIt = pImpl->devices.find(deviceId);
600
+ if (deviceIt == pImpl->devices.end()) {
601
+ return "";
602
+ }
603
+
604
+ try {
605
+ WGPUShaderModuleDescriptor wgpuDesc{};
606
+ WGPUShaderModuleWGSLDescriptor wgslDesc{};
607
+ wgslDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
608
+ wgslDesc.code = wgslSource.c_str();
609
+ wgpuDesc.nextInChain = &wgslDesc.chain;
610
+
611
+ WGPUShaderModule wgpuShader = wgpuDeviceCreateShaderModule(deviceIt->second.Get(), &wgpuDesc);
612
+
613
+ if (!wgpuShader) {
614
+ if (pImpl->config.verbose) {
615
+ std::cerr << "WebGPU: Failed to create shader module\n";
616
+ }
617
+ return "";
618
+ }
619
+
620
+ std::string shaderId = generateHandle("shader", pImpl->resourceCounter);
621
+ pImpl->shaderModules[shaderId] = wgpu::ShaderModule(wgpuShader);
622
+
623
+ return shaderId;
624
+ } catch (const std::exception& e) {
625
+ std::cerr << "WebGPU: Error in createShaderModule: " << e.what() << "\n";
626
+ return "";
627
+ }
628
+ #endif
629
+ }
630
+
631
+ std::string WebGPU::getShaderErrors(const std::string& shaderId) {
632
+ #ifndef PLUSUI_WEBGPU_ENABLED
633
+ return "{}";
634
+ #else
635
+ // TODO: Implement shader error reporting
636
+ return "{}";
637
+ #endif
638
+ }
639
+
640
+ // ============================================================================
641
+ // Pipeline API
642
+ // ============================================================================
643
+
644
+ std::string WebGPU::createRenderPipeline(const std::string& deviceId, const std::string& descriptor) {
645
+ #ifndef PLUSUI_WEBGPU_ENABLED
646
+ return "";
647
+ #else
648
+ auto deviceIt = pImpl->devices.find(deviceId);
649
+ if (deviceIt == pImpl->devices.end()) {
650
+ return "";
651
+ }
652
+
653
+ // TODO: Implement full pipeline creation
654
+ std::string pipelineId = generateHandle("pipeline", pImpl->resourceCounter);
655
+ return pipelineId;
656
+ #endif
657
+ }
658
+
659
+ std::string WebGPU::createComputePipeline(const std::string& deviceId, const std::string& descriptor) {
660
+ #ifndef PLUSUI_WEBGPU_ENABLED
661
+ return "";
662
+ #else
663
+ auto deviceIt = pImpl->devices.find(deviceId);
664
+ if (deviceIt == pImpl->devices.end()) {
665
+ return "";
666
+ }
667
+
668
+ // TODO: Implement compute pipeline creation
669
+ std::string pipelineId = generateHandle("pipeline", pImpl->resourceCounter);
670
+ return pipelineId;
671
+ #endif
672
+ }
673
+
674
+ std::string WebGPU::getPipelineInfo(const std::string& pipelineId) {
675
+ #ifndef PLUSUI_WEBGPU_ENABLED
676
+ return "{}";
677
+ #else
678
+ json info;
679
+ info["id"] = pipelineId;
680
+ return info.dump();
681
+ #endif
682
+ }
683
+
684
+ // ============================================================================
685
+ // Bind Group API
686
+ // ============================================================================
687
+
688
+ std::string WebGPU::createBindGroupLayout(const std::string& deviceId, const std::string& descriptor) {
689
+ #ifndef PLUSUI_WEBGPU_ENABLED
690
+ return "";
691
+ #else
692
+ auto deviceIt = pImpl->devices.find(deviceId);
693
+ if (deviceIt == pImpl->devices.end()) {
694
+ return "";
695
+ }
696
+
697
+ std::string layoutId = generateHandle("bindgrouplayout", pImpl->resourceCounter);
698
+ return layoutId;
699
+ #endif
700
+ }
701
+
702
+ std::string WebGPU::createBindGroup(const std::string& deviceId, const std::string& descriptor) {
703
+ #ifndef PLUSUI_WEBGPU_ENABLED
704
+ return "";
705
+ #else
706
+ auto deviceIt = pImpl->devices.find(deviceId);
707
+ if (deviceIt == pImpl->devices.end()) {
708
+ return "";
709
+ }
710
+
711
+ std::string bindGroupId = generateHandle("bindgroup", pImpl->resourceCounter);
712
+ return bindGroupId;
713
+ #endif
714
+ }
715
+
716
+ // ============================================================================
717
+ // Surface API
718
+ // ============================================================================
719
+
720
+ std::string WebGPU::createSurface(void* windowHandle, uint32_t width, uint32_t height) {
721
+ #ifndef PLUSUI_WEBGPU_ENABLED
722
+ return "";
723
+ #else
724
+ if (!pImpl->initialized) {
725
+ return "";
726
+ }
727
+
728
+ std::string surfaceId = generateHandle("surface", pImpl->resourceCounter);
729
+
730
+ if (pImpl->config.verbose) {
731
+ std::cout << "WebGPU: Surface created: " << surfaceId << "\n";
732
+ }
733
+
734
+ return surfaceId;
735
+ #endif
736
+ }
737
+
738
+ bool WebGPU::configureSurface(const std::string& surfaceId, const std::string& descriptor) {
739
+ #ifndef PLUSUI_WEBGPU_ENABLED
740
+ return false;
741
+ #else
742
+ return true;
743
+ #endif
744
+ }
745
+
746
+ bool WebGPU::resizeSurface(const std::string& surfaceId, uint32_t width, uint32_t height) {
747
+ #ifndef PLUSUI_WEBGPU_ENABLED
748
+ return false;
749
+ #else
750
+ return true;
751
+ #endif
752
+ }
753
+
754
+ std::string WebGPU::getNextTexture(const std::string& surfaceId) {
755
+ #ifndef PLUSUI_WEBGPU_ENABLED
756
+ return "";
757
+ #else
758
+ std::string textureId = generateHandle("texture", pImpl->resourceCounter);
759
+ return textureId;
760
+ #endif
761
+ }
762
+
763
+ // ============================================================================
764
+ // Command Encoding API
765
+ // ============================================================================
766
+
767
+ std::string WebGPU::createCommandEncoder(const std::string& deviceId) {
768
+ #ifndef PLUSUI_WEBGPU_ENABLED
769
+ return "";
770
+ #else
771
+ auto deviceIt = pImpl->devices.find(deviceId);
772
+ if (deviceIt == pImpl->devices.end()) {
773
+ return "";
774
+ }
775
+
776
+ std::string encoderId = generateHandle("encoder", pImpl->resourceCounter);
777
+ return encoderId;
778
+ #endif
779
+ }
780
+
781
+ std::string WebGPU::beginRenderPass(const std::string& encoderId, const std::string& descriptor) {
782
+ #ifndef PLUSUI_WEBGPU_ENABLED
783
+ return "";
784
+ #else
785
+ std::string passId = generateHandle("renderpass", pImpl->resourceCounter);
786
+ return passId;
787
+ #endif
788
+ }
789
+
790
+ bool WebGPU::endRenderPass(const std::string& passId) {
791
+ #ifndef PLUSUI_WEBGPU_ENABLED
792
+ return false;
793
+ #else
794
+ return true;
795
+ #endif
796
+ }
797
+
798
+ bool WebGPU::setRenderPipeline(const std::string& passId, const std::string& pipelineId) {
799
+ #ifndef PLUSUI_WEBGPU_ENABLED
800
+ return false;
801
+ #else
802
+ return true;
803
+ #endif
804
+ }
805
+
806
+ bool WebGPU::setBindGroup(const std::string& passId, uint32_t groupIndex, const std::string& bindGroupId) {
807
+ #ifndef PLUSUI_WEBGPU_ENABLED
808
+ return false;
809
+ #else
810
+ return true;
811
+ #endif
812
+ }
813
+
814
+ bool WebGPU::setVertexBuffer(const std::string& passId, uint32_t slot, const std::string& bufferId, uint64_t offset) {
815
+ #ifndef PLUSUI_WEBGPU_ENABLED
816
+ return false;
817
+ #else
818
+ return true;
819
+ #endif
820
+ }
821
+
822
+ bool WebGPU::setIndexBuffer(const std::string& passId, const std::string& bufferId, const std::string& format, uint64_t offset) {
823
+ #ifndef PLUSUI_WEBGPU_ENABLED
824
+ return false;
825
+ #else
826
+ return true;
827
+ #endif
828
+ }
829
+
830
+ bool WebGPU::draw(const std::string& passId, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) {
831
+ #ifndef PLUSUI_WEBGPU_ENABLED
832
+ return false;
833
+ #else
834
+ return true;
835
+ #endif
836
+ }
837
+
838
+ bool WebGPU::drawIndexed(const std::string& passId, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) {
839
+ #ifndef PLUSUI_WEBGPU_ENABLED
840
+ return false;
841
+ #else
842
+ return true;
843
+ #endif
844
+ }
845
+
846
+ std::string WebGPU::finish(const std::string& encoderId) {
847
+ #ifndef PLUSUI_WEBGPU_ENABLED
848
+ return "";
849
+ #else
850
+ std::string bufferId = generateHandle("cmdbuffer", pImpl->resourceCounter);
851
+ return bufferId;
852
+ #endif
853
+ }
854
+
855
+ // ============================================================================
856
+ // Queue API
857
+ // ============================================================================
858
+
859
+ bool WebGPU::submitCommands(const std::string& deviceId, const std::string& commandBufferIds) {
860
+ #ifndef PLUSUI_WEBGPU_ENABLED
861
+ return false;
862
+ #else
863
+ auto deviceIt = pImpl->devices.find(deviceId);
864
+ if (deviceIt == pImpl->devices.end()) {
865
+ return false;
866
+ }
867
+
868
+ return true;
869
+ #endif
870
+ }
871
+
872
+ bool WebGPU::presentSurface(const std::string& surfaceId) {
873
+ #ifndef PLUSUI_WEBGPU_ENABLED
874
+ return false;
875
+ #else
876
+ return true;
877
+ #endif
878
+ }
879
+
880
+ // ============================================================================
881
+ // Resource Management
882
+ // ============================================================================
883
+
884
+ bool WebGPU::destroyResource(const std::string& handle) {
885
+ #ifndef PLUSUI_WEBGPU_ENABLED
886
+ (void)handle;
887
+ return false;
888
+ #else
889
+ // Try to find and erase from all resource maps
890
+ pImpl->buffers.erase(handle);
891
+ pImpl->textures.erase(handle);
892
+ pImpl->textureViews.erase(handle);
893
+ pImpl->samplers.erase(handle);
894
+ pImpl->shaderModules.erase(handle);
895
+ pImpl->renderPipelines.erase(handle);
896
+ pImpl->computePipelines.erase(handle);
897
+ pImpl->bindGroups.erase(handle);
898
+ pImpl->bindGroupLayouts.erase(handle);
899
+ pImpl->surfaces.erase(handle);
900
+ pImpl->swapChains.erase(handle);
901
+ pImpl->commandBuffers.erase(handle);
902
+ pImpl->adapters.erase(handle);
903
+
904
+ return true;
905
+ #endif
906
+ }
907
+
908
+ bool WebGPU::destroyDevice(const std::string& deviceId) {
909
+ #ifndef PLUSUI_WEBGPU_ENABLED
910
+ (void)deviceId;
911
+ return false;
912
+ #else
913
+ // Destroy all resources associated with device
914
+ pImpl->buffers.clear();
915
+ pImpl->textures.clear();
916
+ pImpl->textureViews.clear();
917
+ pImpl->samplers.clear();
918
+ pImpl->shaderModules.clear();
919
+ pImpl->renderPipelines.clear();
920
+ pImpl->computePipelines.clear();
921
+ pImpl->bindGroups.clear();
922
+
923
+ pImpl->devices.erase(deviceId);
924
+
925
+ return true;
926
+ #endif
927
+ }
928
+
929
+ std::string WebGPU::getMemoryStats() {
930
+ #ifndef PLUSUI_WEBGPU_ENABLED
931
+ return "{}";
932
+ #else
933
+ json stats;
934
+ stats["buffers"] = pImpl->buffers.size();
935
+ stats["textures"] = pImpl->textures.size();
936
+ stats["samplers"] = pImpl->samplers.size();
937
+ stats["shaders"] = pImpl->shaderModules.size();
938
+ stats["pipelines"] = pImpl->renderPipelines.size() + pImpl->computePipelines.size();
939
+ stats["devices"] = pImpl->devices.size();
940
+ return stats.dump();
941
+ #endif
942
+ }
943
+
944
+ } // namespace plusui
945
+
946
+ #ifdef _WIN32
947
+ #pragma warning(pop)
948
+ #endif