载入中...
搜索中...
未找到
WebGpuGpgpu.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4#include "common/Module.h"
5#include "data/ByteData.h"
6#include "graphics/Graphics.h"
8
9#if defined(__EMSCRIPTEN__)
10#include <emscripten/emscripten.h>
11#endif
12
13#include <algorithm>
14#include <atomic>
15#include <cstring>
16#include <variant>
17#include <vector>
18
19namespace eve::gpgpu {
20
21namespace {
22
23// WebGPU permits at most 256-byte storage-offset alignment. Each unused writable
24// binding needs a disjoint range, even when the shader does not access it.
25constexpr uint64_t kDummyBindingStride = 256;
26
27graphics::webgpu::Graphics *requireWebGpuGraphics() {
28 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
29 if (!gfx) gfx = eve::graphics::Graphics::create();
30 auto *wgg = dynamic_cast<graphics::webgpu::Graphics *>(gfx);
31 if (!wgg) throw Exception("Gpgpu: requires WebGPU Graphics backend");
32 if (!wgg->getDevice())
33 throw Exception("Gpgpu: Graphics device not initialized (create a window first)");
34 return wgg;
35}
36
37wgpu::Device &gpuDevice() { return requireWebGpuGraphics()->getDevice(); }
38wgpu::Queue &gpuQueue() { return requireWebGpuGraphics()->getQueue(); }
39wgpu::Instance &gpuInstance() { return requireWebGpuGraphics()->getInstance(); }
40
42WGPUStringView sv(const char *s) { return WGPUStringView{s, s ? std::strlen(s) : 0}; }
43
44} // namespace
45
47 try {
48 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
49 if (!gfx) return false;
50 auto *wgg = dynamic_cast<graphics::webgpu::Graphics *>(gfx);
51 if (!wgg) return false;
52 return bool(wgg->getDevice());
53 } catch (...) {
54 return false;
55 }
56}
57
59
61 if (binding < 0 || binding >= kMaxBindings) return;
62 bindings_[size_t(binding)] = buffer;
63}
64
66 if (binding < 0 || binding >= kMaxBindings) return nullptr;
67 return bindings_[size_t(binding)];
68}
69
71 if (index < 0 || index >= kMaxFloats) return;
72 push_[size_t(index)] = value;
73}
74
76 if (index < 0 || index >= kMaxFloats) return 0.f;
77 return push_[size_t(index)];
78}
79
81 bindings_.fill(nullptr);
82}
83
85 auto &device = gpuDevice();
86 auto *shader = new WebGpuComputeShader();
87
88 // Bind group layout: 8 storage buffers (bindings 0..7) + push UBO (binding 8).
89 WGPUBindGroupLayoutEntry entries[ComputeShader::kMaxBindings + 1]{};
90 for (int i = 0; i < ComputeShader::kMaxBindings; ++i) {
91 entries[size_t(i)].binding = uint32_t(i);
92 entries[size_t(i)].visibility = WGPUShaderStage_Compute;
93 entries[size_t(i)].buffer.type = WGPUBufferBindingType_Storage;
94 entries[size_t(i)].buffer.hasDynamicOffset = false;
95 entries[size_t(i)].buffer.minBindingSize = 0;
96 }
98 entries[ComputeShader::kMaxBindings].visibility = WGPUShaderStage_Compute;
99 entries[ComputeShader::kMaxBindings].buffer.type = WGPUBufferBindingType_Uniform;
100 entries[ComputeShader::kMaxBindings].buffer.hasDynamicOffset = true;
102
103 WGPUBindGroupLayoutDescriptor bglDesc{};
104 bglDesc.label = sv("eve_compute");
105 bglDesc.entryCount = ComputeShader::kMaxBindings + 1;
106 bglDesc.entries = entries;
107 shader->setLayout =
108 device.CreateBindGroupLayout(reinterpret_cast<const wgpu::BindGroupLayoutDescriptor*>(&bglDesc));
109
110 WGPUBindGroupLayout bgl = shader->setLayout.Get();
111 WGPUPipelineLayoutDescriptor plDesc{};
112 plDesc.label = sv("eve_compute_layout");
113 plDesc.bindGroupLayoutCount = 1;
114 plDesc.bindGroupLayouts = &bgl;
115 shader->pipelineLayout =
116 device.CreatePipelineLayout(reinterpret_cast<const wgpu::PipelineLayoutDescriptor*>(&plDesc));
117
118 WGPUShaderSourceWGSL wd{};
119 wd.chain.sType = WGPUSType_ShaderSourceWGSL;
120 wd.code = sv(wgsl.c_str());
121 WGPUShaderModuleDescriptor md{};
122 md.label = sv("eve_compute_module");
123 md.nextInChain = &wd.chain;
124 wgpu::ShaderModule module = device.CreateShaderModule(reinterpret_cast<const wgpu::ShaderModuleDescriptor*>(&md));
125
126 WGPUComputePipelineDescriptor pd{};
127 pd.label = sv("eve_compute_pipeline");
128 pd.layout = shader->pipelineLayout.Get();
129 pd.compute.module = module.Get();
130 pd.compute.entryPoint = sv("main");
131 shader->pipeline = device.CreateComputePipeline(reinterpret_cast<const wgpu::ComputePipelineDescriptor*>(&pd));
132
133 // Push-constant UBO (128 bytes, 256-aligned for dynamic offsets).
134 WGPUBufferDescriptor bd{};
135 bd.label = sv("eve_compute_push");
137 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
138 bd.mappedAtCreation = false;
139 shader->pushUbo = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor*>(&bd));
140 shader->ready = true;
141 return shader;
142}
143
144WebGpuComputeShader *webgpuNewShaderFromSpirv(const std::vector<uint32_t> &spv) {
145 (void)spv;
146 throw Exception("Gpgpu.newShaderFromSpirv: SPIR-V compute shaders are not supported on the "
147 "WebGPU backend; use WGSL source instead.");
148}
149
150WebGpuGpuBuffer *webgpuNewBuffer(int byteSize, const std::string &usage) {
151 if (byteSize <= 0) throw Exception("Gpgpu.newBuffer: byteSize must be > 0");
152 auto &device = gpuDevice();
153 bool staging = usage == "staging";
154 WGPUBufferUsage bufUsage = staging ? (WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead)
155 : (WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst |
156 WGPUBufferUsage_CopySrc | WGPUBufferUsage_Vertex);
157 WGPUBufferDescriptor bd{};
158 bd.label = sv("eve_compute_buffer");
159 bd.size = uint64_t(byteSize);
160 bd.usage = bufUsage;
161 bd.mappedAtCreation = false;
162 auto *b = new WebGpuGpuBuffer();
163 b->buffer = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor*>(&bd));
164 b->size_ = uint64_t(byteSize);
165 b->usage_ = usage;
166 return b;
167}
168
169namespace {
170
171bool mapBufferToCpu(wgpu::Buffer buffer, uint64_t bufferSize, uint64_t srcOffset, void *dst,
172 uint64_t nbytes) {
173 if (!buffer || !dst || nbytes == 0 || srcOffset + nbytes > bufferSize) return false;
174 struct MapState {
175 bool done = false;
176 bool success = false;
177 } map;
178 WGPUBufferMapCallbackInfo cbInfo{};
179#if defined(__EMSCRIPTEN__)
180 cbInfo.mode = WGPUCallbackMode_AllowProcessEvents;
181#else
182 cbInfo.mode = WGPUCallbackMode_WaitAnyOnly;
183#endif
184 cbInfo.callback = [](WGPUMapAsyncStatus status, WGPUStringView, void *userdata1, void *) {
185 auto *state = static_cast<MapState *>(userdata1);
186 state->success = status == WGPUMapAsyncStatus_Success;
187 state->done = true;
188 };
189 cbInfo.userdata1 = &map;
190 WGPUFuture future = wgpuBufferMapAsync(buffer.Get(), WGPUMapMode_Read, 0, bufferSize, cbInfo);
191#if defined(__EMSCRIPTEN__)
192 int guard = 0;
193 while (!map.done && guard++ < 2000) {
194 emscripten_sleep(0);
195 wgpuInstanceProcessEvents(gpuInstance().Get());
196 }
197#else
198 WGPUFutureWaitInfo waitInfo{};
199 waitInfo.future = future;
200 (void)wgpuInstanceWaitAny(gpuInstance().Get(), 1, &waitInfo, UINT64_MAX);
201#endif
202 if (!map.success) return false;
203 const auto *data = static_cast<const uint8_t *>(buffer.GetConstMappedRange(0, bufferSize));
204 if (!data) return false;
205 std::memcpy(dst, data + srcOffset, size_t(nbytes));
206 buffer.Unmap();
207 return true;
208}
209
210bool readBufferToCpu(wgpu::Buffer src, uint64_t srcOffset, void *dst, uint64_t nbytes) {
211 auto &device = gpuDevice();
212 auto &queue = gpuQueue();
213 if (!src || nbytes == 0) return false;
214
215 WGPUBufferDescriptor bd{};
216 bd.label = sv("eve_compute_readback");
217 bd.size = nbytes;
218 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead;
219 bd.mappedAtCreation = false;
220 wgpu::Buffer staging = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor*>(&bd));
221
222 wgpu::CommandEncoder enc = device.CreateCommandEncoder();
223 enc.CopyBufferToBuffer(src, srcOffset, staging, 0, nbytes);
224 wgpu::CommandBuffer cmd = enc.Finish();
225 queue.Submit(1, &cmd);
226 return mapBufferToCpu(staging, nbytes, 0, dst, nbytes);
227}
228
229} // namespace
230
231void webgpuDispatch(ComputeShader *shader, int groupsX, int groupsY, int groupsZ) {
232 auto *ws = dynamic_cast<WebGpuComputeShader *>(shader);
233 if (!ws || !ws->ready || !ws->pipeline) return;
234 if (groupsX <= 0) groupsX = 1;
235 if (groupsY <= 0) groupsY = 1;
236 if (groupsZ <= 0) groupsZ = 1;
237
238 auto &device = gpuDevice();
239 auto &queue = gpuQueue();
240
241 // Push constants -> uniform buffer (dynamic offset 0).
242 queue.WriteBuffer(ws->pushUbo, 0, shader->pushConstantData(), ComputeShader::kPushConstantBytes);
243
244 // Build a bind group for the current storage-buffer bindings.
245 WGPUBindGroupEntry entries[ComputeShader::kMaxBindings + 1]{};
246 WebGpuGpuBuffer *dummy = webgpuNewBuffer(int(kDummyBindingStride * ComputeShader::kMaxBindings), "storage");
247 for (int i = 0; i < ComputeShader::kMaxBindings; ++i) {
248 auto *vb = dynamic_cast<WebGpuGpuBuffer *>(ws->getBoundBuffer(i));
249 entries[size_t(i)].binding = uint32_t(i);
250 entries[size_t(i)].buffer = (vb && vb->buffer) ? vb->buffer.Get() : dummy->buffer.Get();
251 entries[size_t(i)].size = (vb && vb->buffer) ? vb->size_ : 4;
252 entries[size_t(i)].offset = (vb && vb->buffer) ? 0 : uint64_t(i) * kDummyBindingStride;
253 }
255 entries[ComputeShader::kMaxBindings].buffer = ws->pushUbo.Get();
257 WGPUBindGroupDescriptor bgDesc{};
258 bgDesc.layout = ws->setLayout.Get();
259 bgDesc.entryCount = ComputeShader::kMaxBindings + 1;
260 bgDesc.entries = entries;
261 wgpu::BindGroup bg = device.CreateBindGroup(reinterpret_cast<const wgpu::BindGroupDescriptor*>(&bgDesc));
262 delete dummy;
263
264 wgpu::CommandEncoder enc = device.CreateCommandEncoder();
265 WGPUComputePassDescriptor cpDesc{};
266 wgpu::ComputePassEncoder pass = enc.BeginComputePass(reinterpret_cast<const wgpu::ComputePassDescriptor*>(&cpDesc));
267 pass.SetPipeline(ws->pipeline);
268 uint32_t offsets[1] = {0};
269 pass.SetBindGroup(0, bg, 1, offsets);
270 pass.DispatchWorkgroups(uint32_t(groupsX), uint32_t(groupsY), uint32_t(groupsZ));
271 pass.End();
272 wgpu::CommandBuffer cmd = enc.Finish();
273 queue.Submit(1, &cmd);
274}
275
276// ---------------------------------------------------------------------------
277// WebGpuGpuBuffer
278// ---------------------------------------------------------------------------
279
281
284 if (!buffer) return view;
285 const WGPUBuffer raw = buffer.Get();
286 static_assert(sizeof(raw) <= sizeof(view.nativeHandle));
287 std::memcpy(&view.nativeHandle, &raw, sizeof(raw));
289 view.sizeBytes = size_;
290 return view;
291}
292
293void WebGpuGpuBuffer::uploadBytes(const void *src, uint64_t nbytes, uint64_t dstOffset) {
294 if (!buffer || !src || nbytes == 0) return;
295 if (dstOffset + nbytes > size_)
296 throw Exception("GpuBuffer.write: out of range");
297 gpuQueue().WriteBuffer(buffer, dstOffset, src, nbytes);
298}
299
300void WebGpuGpuBuffer::downloadBytes(void *dst, uint64_t nbytes, uint64_t srcOffset) const {
301 if (!buffer || !dst || nbytes == 0) return;
302 if (srcOffset + nbytes > size_)
303 throw Exception("GpuBuffer.read: out of range");
304 if (usage_ == "staging")
305 mapBufferToCpu(buffer, size_, srcOffset, dst, nbytes);
306 else
307 readBufferToCpu(buffer, srcOffset, dst, nbytes);
308}
309
310void WebGpuGpuBuffer::writeData(data::ByteData *data, int dstOffset) {
311 if (!data) return;
312 uploadBytes(data->getData(), data->getSize(), uint64_t(dstOffset < 0 ? 0 : dstOffset));
313}
314
316 const int off = srcOffset < 0 ? 0 : srcOffset;
317 int nbytes = size;
318 if (nbytes < 0) nbytes = int(size_) - off;
319 if (nbytes <= 0) return new data::ByteData(size_t(0));
320 auto *out = new data::ByteData(size_t(nbytes));
321 downloadBytes(out->getData(), uint64_t(nbytes), uint64_t(off));
322 return out;
323}
324
325void WebGpuGpuBuffer::writeFloat32(int floatIndex, float value) {
326 if (floatIndex < 0) return;
327 uploadBytes(&value, sizeof(float), uint64_t(floatIndex) * sizeof(float));
328}
329
330float WebGpuGpuBuffer::readFloat32(int floatIndex) {
331 if (floatIndex < 0) return 0.f;
332 float v = 0.f;
333 downloadBytes(&v, sizeof(float), uint64_t(floatIndex) * sizeof(float));
334 return v;
335}
336
337void WebGpuGpuBuffer::writeFloat32s(const float *data, int count, int startIndex) {
338 if (!data || count <= 0 || startIndex < 0) return;
339 uploadBytes(data, uint64_t(count) * sizeof(float), uint64_t(startIndex) * sizeof(float));
340}
341
342void WebGpuGpuBuffer::readFloat32s(float *out, int count, int startIndex) const {
343 if (!out || count <= 0 || startIndex < 0) return;
344 downloadBytes(out, uint64_t(count) * sizeof(float), uint64_t(startIndex) * sizeof(float));
345}
346
348 if (size_ < sizeof(float)) return;
349 const size_t count = size_t(size_ / sizeof(float));
350 std::vector<float> tmp(count, value);
351 uploadBytes(tmp.data(), count * sizeof(float), 0);
352}
353
355public:
356 struct Upload {
358 std::vector<uint8_t> bytes;
359 uint64_t offset = 0;
360 };
361 struct Download {
364 uint64_t bytes = 0;
365 uint64_t srcOffset = 0;
366 };
367 struct Dispatch {
369 std::array<WebGpuGpuBuffer*, ComputeShader::kMaxBindings> bindings{};
370 std::array<float, ComputeShader::kMaxFloats> push{};
371 uint32_t groupsX = 1;
372 uint32_t groupsY = 1;
373 uint32_t groupsZ = 1;
374 };
375 using Op = std::variant<Upload, Download, Dispatch>;
376 std::vector<Op> ops;
377 std::atomic<SequenceStatus> status{SequenceStatus::Idle};
378 WGPUFuture completion{};
379};
380
383 if (!sequence) return;
385 delete sequence;
386}
388
390 if (!webgpuSequenceReady(sequence)) throw Exception("Gpgpu.Sequence: WebGPU is not ready");
391 if (sequence->status.load() == SequenceStatus::Submitted)
392 throw Exception("Gpgpu.Sequence.begin: previous async submission is still pending");
393 sequence->ops.clear();
394 sequence->status.store(SequenceStatus::Recording);
395}
396
397void webgpuSequenceRecordUpload(WebGpuSequence* sequence, GpuBuffer* dst, const void* src, uint64_t nbytes,
398 uint64_t dstOffset) {
399 auto* buffer = dynamic_cast<WebGpuGpuBuffer*>(dst);
400 if (!sequence || !buffer || !src || nbytes == 0 || dstOffset + nbytes > buffer->size_)
401 throw Exception("Gpgpu.Sequence.recordUpload: invalid WebGPU upload");
402 if ((nbytes & 3u) != 0 || (dstOffset & 3u) != 0)
403 throw Exception("Gpgpu.Sequence.recordUpload: byte count and offset must be 4-byte aligned");
405 op.dst = buffer;
406 op.bytes.resize(size_t(nbytes));
407 std::memcpy(op.bytes.data(), src, size_t(nbytes));
408 op.offset = dstOffset;
409 sequence->ops.emplace_back(std::move(op));
410}
411
413 uint64_t srcOffset) {
414 auto* source = dynamic_cast<WebGpuGpuBuffer*>(src);
415 auto* target = dynamic_cast<WebGpuGpuBuffer*>(staging);
416 if (!sequence || !source || !target || target->usage_ != "staging" || nbytes == 0 ||
417 srcOffset + nbytes > source->size_ || nbytes > target->size_)
418 throw Exception("Gpgpu.Sequence.recordDownload: invalid WebGPU download");
419 if ((nbytes & 3u) != 0 || (srcOffset & 3u) != 0)
420 throw Exception("Gpgpu.Sequence.recordDownload: byte count and offset must be 4-byte aligned");
421 sequence->ops.emplace_back(WebGpuSequence::Download{source, target, nbytes, srcOffset});
422}
423
425 int groupsZ) {
426 auto* compute = dynamic_cast<WebGpuComputeShader*>(shader);
427 if (!sequence || !compute || !compute->ready)
428 throw Exception("Gpgpu.Sequence.recordDispatch: invalid WebGPU shader");
430 op.shader = compute;
431 op.groupsX = uint32_t(std::max(1, groupsX));
432 op.groupsY = uint32_t(std::max(1, groupsY));
433 op.groupsZ = uint32_t(std::max(1, groupsZ));
434 for (int i = 0; i < ComputeShader::kMaxBindings; ++i)
435 op.bindings[size_t(i)] = dynamic_cast<WebGpuGpuBuffer*>(compute->getBoundBuffer(i));
436 std::memcpy(op.push.data(), compute->pushConstantData(), ComputeShader::kPushConstantBytes);
437 sequence->ops.emplace_back(std::move(op));
438}
439
441 if (!webgpuSequenceReady(sequence)) throw Exception("Gpgpu.Sequence.submit: WebGPU is not ready");
442 if (sequence->status.load() != SequenceStatus::Recording || sequence->ops.empty())
443 throw Exception("Gpgpu.Sequence.submit: nothing recorded");
444 auto& device = gpuDevice();
445 auto& queue = gpuQueue();
446 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
447 std::vector<wgpu::Buffer> retainedBuffers;
448 std::vector<wgpu::BindGroup> retainedBindGroups;
449
450 for (auto& op : sequence->ops) {
451 if (auto* upload = std::get_if<WebGpuSequence::Upload>(&op)) {
452 WGPUBufferDescriptor desc{};
453 desc.label = sv("eve_sequence_upload");
454 desc.size = upload->bytes.size();
455 desc.usage = WGPUBufferUsage_CopySrc;
456 desc.mappedAtCreation = true;
457 wgpu::Buffer staging = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor*>(&desc));
458 std::memcpy(staging.GetMappedRange(0, desc.size), upload->bytes.data(), desc.size);
459 staging.Unmap();
460 encoder.CopyBufferToBuffer(staging, 0, upload->dst->buffer, upload->offset, desc.size);
461 retainedBuffers.push_back(staging);
462 continue;
463 }
464 if (auto* download = std::get_if<WebGpuSequence::Download>(&op)) {
465 encoder.CopyBufferToBuffer(download->src->buffer, download->srcOffset, download->dst->buffer, 0,
466 download->bytes);
467 continue;
468 }
469 auto& dispatch = std::get<WebGpuSequence::Dispatch>(op);
470 WGPUBufferDescriptor pushDesc{};
471 pushDesc.label = sv("eve_sequence_push");
472 pushDesc.size = ComputeShader::kPushConstantBytes;
473 pushDesc.usage = WGPUBufferUsage_Uniform;
474 pushDesc.mappedAtCreation = true;
475 wgpu::Buffer push = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor*>(&pushDesc));
476 std::memcpy(push.GetMappedRange(0, pushDesc.size), dispatch.push.data(), pushDesc.size);
477 push.Unmap();
478
479 WGPUBufferDescriptor dummyDesc{};
480 dummyDesc.label = sv("eve_sequence_dummy");
481 dummyDesc.size = kDummyBindingStride * ComputeShader::kMaxBindings;
482 dummyDesc.usage = WGPUBufferUsage_Storage;
483 wgpu::Buffer dummy = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor*>(&dummyDesc));
484 WGPUBindGroupEntry entries[ComputeShader::kMaxBindings + 1]{};
485 for (int i = 0; i < ComputeShader::kMaxBindings; ++i) {
486 WebGpuGpuBuffer* bound = dispatch.bindings[size_t(i)];
487 entries[size_t(i)].binding = uint32_t(i);
488 entries[size_t(i)].buffer = bound ? bound->buffer.Get() : dummy.Get();
489 entries[size_t(i)].size = bound ? bound->size_ : 4;
490 entries[size_t(i)].offset = bound ? 0 : uint64_t(i) * kDummyBindingStride;
491 }
495 WGPUBindGroupDescriptor bindDesc{};
496 bindDesc.layout = dispatch.shader->setLayout.Get();
497 bindDesc.entryCount = ComputeShader::kMaxBindings + 1;
498 bindDesc.entries = entries;
499 wgpu::BindGroup bindGroup =
500 device.CreateBindGroup(reinterpret_cast<const wgpu::BindGroupDescriptor*>(&bindDesc));
501
502 WGPUComputePassDescriptor passDesc{};
503 wgpu::ComputePassEncoder pass =
504 encoder.BeginComputePass(reinterpret_cast<const wgpu::ComputePassDescriptor*>(&passDesc));
505 pass.SetPipeline(dispatch.shader->pipeline);
506 uint32_t offsets[1] = {0};
507 pass.SetBindGroup(0, bindGroup, 1, offsets);
508 pass.DispatchWorkgroups(dispatch.groupsX, dispatch.groupsY, dispatch.groupsZ);
509 pass.End();
510 retainedBuffers.push_back(push);
511 retainedBuffers.push_back(dummy);
512 retainedBindGroups.push_back(bindGroup);
513 }
514
515 wgpu::CommandBuffer command = encoder.Finish();
516 queue.Submit(1, &command);
517 sequence->status.store(SequenceStatus::Submitted);
518 WGPUQueueWorkDoneCallbackInfo callback{};
519#if defined(__EMSCRIPTEN__)
520 callback.mode = WGPUCallbackMode_AllowProcessEvents;
521#else
522 callback.mode = WGPUCallbackMode_WaitAnyOnly;
523#endif
524 callback.callback = [](WGPUQueueWorkDoneStatus status, WGPUStringView, void *userdata1, void *) {
525 auto *submitted = static_cast<WebGpuSequence *>(userdata1);
526 submitted->status.store(status == WGPUQueueWorkDoneStatus_Success ? SequenceStatus::Complete
528 };
529 callback.userdata1 = sequence;
530 sequence->completion = wgpuQueueOnSubmittedWorkDone(queue.Get(), callback);
532}
533
535 if (!sequence) return SequenceStatus::Failed;
536 if (sequence->status.load() != SequenceStatus::Submitted) return sequence->status.load();
537#if defined(__EMSCRIPTEN__)
538 wgpuInstanceProcessEvents(gpuInstance().Get());
539#else
540 WGPUFutureWaitInfo waitInfo{};
541 waitInfo.future = sequence->completion;
542 (void)wgpuInstanceWaitAny(gpuInstance().Get(), 1, &waitInfo, 0);
543#endif
544 return sequence->status.load();
545}
546
548 if (!sequence) return SequenceStatus::Failed;
549 if (sequence->status.load() != SequenceStatus::Submitted) return sequence->status.load();
550#if defined(__EMSCRIPTEN__)
551 while (sequence->status.load() == SequenceStatus::Submitted) {
552 emscripten_sleep(0);
553 wgpuInstanceProcessEvents(gpuInstance().Get());
554 }
555#else
556 WGPUFutureWaitInfo waitInfo{};
557 waitInfo.future = sequence->completion;
558 (void)wgpuInstanceWaitAny(gpuInstance().Get(), 1, &waitInfo, UINT64_MAX);
559#endif
560 return sequence->status.load();
561}
562
566
570 throw Exception("Gpgpu.Sequence.submit: GPU submission failed");
571}
572
573} // namespace eve::gpgpu
LogicalId target
double value
std::string usage
const std::string & s
std::unordered_map< std::string, QuestRuntime > entries
scene::NodeDesc desc
vkb::Device & device
wgpu::PopErrorScopeStatus status
float v
MeleePoint3 b
Definition MeleeHit.cpp:41
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::unique_ptr< gpgpu::Sequence > sequence
Definition OnnxGpgpu.cpp:43
OnnxCompute * compute
eve::action::ActionVfxBinding binding
Shader * shader
glm::mat4 view
std::uint32_t count
float size
Definition TreeMesh.cpp:156
uint32_t index
const UnitySourceAsset & source
ViewPreparation callback
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
In-memory byte buffer implementing eve::Data (ref-counted).
Definition ByteData.h:13
Backend-agnostic compute program. Bind storage buffers then dispatch via Gpgpu::dispatch....
static constexpr int kMaxBindings
static constexpr int kMaxFloats
std::array< float, kMaxFloats > push_
static constexpr uint32_t kPushConstantBytes
Backend-agnostic GPU buffer for compute (storage) or CPU staging transfers. Squirrel-owned; derived c...
Definition GpuBuffer.h:18
Compute program for the WebGPU backend. Accepts WGSL source; GLSL/SPIR-V input is rejected (browsers ...
Definition WebGpuGpgpu.h:22
float getFloat(int index) const override
Returns the float.
void bindBuffer(int binding, GpuBuffer *buffer) override
Binds buffer.
void setFloat(int index, float value) override
Sets the float.
void clearBindings() override
Clears bindings.
GpuBuffer * getBoundBuffer(int binding) const override
Returns the bound buffer.
~WebGpuComputeShader() override
Releases WebGpuComputeShader resources.
Storage / staging buffer for the WebGPU backend.
Definition WebGpuGpgpu.h:53
float readFloat32(int floatIndex) override
Reads float 32.
void downloadBytes(void *dst, uint64_t nbytes, uint64_t srcOffset=0) const override
Downloads bytes.
data::ByteData * readData(int srcOffset=0, int size=-1) override
Reads data.
~WebGpuGpuBuffer() override
Releases WebGpuGpuBuffer resources.
void writeFloat32(int floatIndex, float value) override
Writes float 32.
void readFloat32s(float *out, int count, int startIndex=0) const override
Reads float 32 s.
void uploadBytes(const void *src, uint64_t nbytes, uint64_t dstOffset=0) override
Uploads bytes.
void writeFloat32s(const float *data, int count, int startIndex=0) override
Writes float 32 s.
void writeData(data::ByteData *data, int dstOffset=0) override
Writes data.
void fillFloat32(float value) override
Fill float 32.
GpuResidentBufferView residentView() const override
Resident view.
std::variant< Upload, Download, Dispatch > Op
std::atomic< SequenceStatus > status
EVENGINE_API_BACKENDS public API.
Definition Graphics.h:191
Fluids module — interactive surface fluid simulation.
Definition Fluids.h:34
SequenceStatus
Observable lifecycle state of a reusable GPU command sequence.
Definition Sequence.h:14
void webgpuSequenceRecordDownload(WebGpuSequence *sequence, GpuBuffer *src, GpuBuffer *staging, uint64_t nbytes, uint64_t srcOffset)
Webgpu sequence record download.
bool webgpuSequenceReady(WebGpuSequence *sequence)
Webgpu sequence ready.
SequenceStatus webgpuSequencePoll(WebGpuSequence *sequence)
Webgpu sequence poll.
void webgpuSequenceBegin(WebGpuSequence *sequence)
Webgpu sequence begin.
bool webgpuGpgpuReady()
Webgpu gpgpu ready.
void webgpuDispatch(ComputeShader *shader, int groupsX, int groupsY, int groupsZ)
Webgpu dispatch.
void webgpuSequenceDestroy(WebGpuSequence *sequence)
Webgpu sequence destroy.
WebGpuGpuBuffer * webgpuNewBuffer(int byteSize, const std::string &usage)
Webgpu new buffer.
SequenceStatus webgpuSequenceWait(WebGpuSequence *sequence)
Webgpu sequence wait.
SequenceStatus webgpuSequenceStatus(const WebGpuSequence *sequence)
Webgpu sequence status.
WebGpuComputeShader * webgpuNewShaderFromSpirv(const std::vector< uint32_t > &spv)
Webgpu new shader from spirv.
WebGpuComputeShader * webgpuNewShaderFromWgsl(const std::string &wgsl)
Webgpu new shader from wgsl.
void webgpuSequenceRecordDispatch(WebGpuSequence *sequence, ComputeShader *shader, int groupsX, int groupsY, int groupsZ)
Webgpu sequence record dispatch.
WebGpuSequence * webgpuSequenceCreate()
Webgpu sequence create.
void webgpuSequenceRecordUpload(WebGpuSequence *sequence, GpuBuffer *dst, const void *src, uint64_t nbytes, uint64_t dstOffset)
Webgpu sequence record upload.
SequenceStatus webgpuSequenceSubmitAsync(WebGpuSequence *sequence)
Webgpu sequence submit async.
void webgpuSequenceSubmit(WebGpuSequence *sequence)
Webgpu sequence submit.
WGPUStringView sv(const char *s)
Build a WGPUStringView from a C string (null-safe, length auto-computed).
Definition wgpu_types.h:20
Transient backend buffer slice shared between sibling GPU modules. @ownership Non-owning; nativeHandl...
std::array< WebGpuGpuBuffer *, ComputeShader::kMaxBindings > bindings
std::array< float, ComputeShader::kMaxFloats > push
std::future< vkb::Instance > future
Definition Graphics.cpp:182