9#if defined(__EMSCRIPTEN__)
10#include <emscripten/emscripten.h>
20graphics::webgpu::Graphics *requireWebGpuGraphics() {
21 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>(
"Graphics");
22 if (!gfx) gfx = eve::graphics::Graphics::create();
23 auto *wgg =
dynamic_cast<graphics::webgpu::Graphics *
>(gfx);
24 if (!wgg)
throw Exception(
"Gpgpu: requires WebGPU Graphics backend");
25 if (!wgg->getDevice())
26 throw Exception(
"Gpgpu: Graphics device not initialized (create a window first)");
30wgpu::Device &gpuDevice() {
return requireWebGpuGraphics()->getDevice(); }
31wgpu::Queue &gpuQueue() {
return requireWebGpuGraphics()->getQueue(); }
32wgpu::Instance &gpuInstance() {
return requireWebGpuGraphics()->getInstance(); }
35WGPUStringView sv(
const char *
s) {
return WGPUStringView{
s,
s ? std::strlen(
s) : 0}; }
41 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>(
"Graphics");
42 if (!gfx)
return false;
44 if (!wgg)
return false;
45 return bool(wgg->getDevice());
55 bindings_[size_t(binding)] = buffer;
59 if (binding < 0 || binding >=
kMaxBindings)
return nullptr;
60 return bindings_[size_t(binding)];
69 if (index < 0 || index >=
kMaxFloats)
return 0.f;
70 return push_[size_t(index)];
74 bindings_.fill(
nullptr);
78 auto &
device = gpuDevice();
84 entries[size_t(i)].binding = uint32_t(i);
85 entries[size_t(i)].visibility = WGPUShaderStage_Compute;
86 entries[size_t(i)].buffer.type = WGPUBufferBindingType_Storage;
87 entries[size_t(i)].buffer.hasDynamicOffset =
false;
88 entries[size_t(i)].buffer.minBindingSize = 0;
96 WGPUBindGroupLayoutDescriptor bglDesc{};
97 bglDesc.label = sv(
"eve_compute");
99 bglDesc.entries = entries;
100 shader->setLayout =
device.CreateBindGroupLayout(
reinterpret_cast<const wgpu::BindGroupLayoutDescriptor*
>(&bglDesc));
102 WGPUBindGroupLayout bgl =
shader->setLayout.Get();
103 WGPUPipelineLayoutDescriptor plDesc{};
104 plDesc.label = sv(
"eve_compute_layout");
105 plDesc.bindGroupLayoutCount = 1;
106 plDesc.bindGroupLayouts = &bgl;
107 shader->pipelineLayout =
device.CreatePipelineLayout(
reinterpret_cast<const wgpu::PipelineLayoutDescriptor*
>(&plDesc));
109 WGPUShaderSourceWGSL wd{};
110 wd.chain.sType = WGPUSType_ShaderSourceWGSL;
111 wd.code = sv(wgsl.c_str());
112 WGPUShaderModuleDescriptor md{};
113 md.label = sv(
"eve_compute_module");
114 md.nextInChain = &wd.chain;
115 wgpu::ShaderModule
module = device.CreateShaderModule(reinterpret_cast<const wgpu::ShaderModuleDescriptor*>(&md));
117 WGPUComputePipelineDescriptor pd{};
118 pd.label = sv(
"eve_compute_pipeline");
119 pd.layout =
shader->pipelineLayout.Get();
120 pd.compute.module =
module.Get();
121 pd.compute.entryPoint = sv(
"main");
122 shader->pipeline =
device.CreateComputePipeline(
reinterpret_cast<const wgpu::ComputePipelineDescriptor*
>(&pd));
125 WGPUBufferDescriptor bd{};
126 bd.label = sv(
"eve_compute_push");
128 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
129 bd.mappedAtCreation =
false;
130 shader->pushUbo =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
137 throw Exception(
"Gpgpu.newShaderFromSpirv: SPIR-V compute shaders are not supported on the "
138 "WebGPU backend; use WGSL source instead.");
142 if (byteSize <= 0)
throw Exception(
"Gpgpu.newBuffer: byteSize must be > 0");
143 auto &
device = gpuDevice();
144 bool staging = usage ==
"staging";
145 WGPUBufferUsage bufUsage = staging ? (WGPUBufferUsage_CopySrc | WGPUBufferUsage_MapRead)
146 : (WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst |
147 WGPUBufferUsage_CopySrc | WGPUBufferUsage_Vertex);
148 WGPUBufferDescriptor bd{};
149 bd.label = sv(
"eve_compute_buffer");
150 bd.size = uint64_t(byteSize);
152 bd.mappedAtCreation =
false;
154 b->buffer =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
155 b->size_ = uint64_t(byteSize);
162bool readBufferToCpu(wgpu::Buffer src, uint64_t srcOffset,
void *dst, uint64_t nbytes) {
163 auto &
device = gpuDevice();
164 auto &queue = gpuQueue();
165 if (!src || nbytes == 0)
return false;
167 WGPUBufferDescriptor bd{};
168 bd.label = sv(
"eve_compute_readback");
170 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead;
171 bd.mappedAtCreation =
false;
172 wgpu::Buffer staging =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
174 wgpu::CommandEncoder enc =
device.CreateCommandEncoder();
175 enc.CopyBufferToBuffer(src, srcOffset, staging, 0, nbytes);
176 wgpu::CommandBuffer cmd = enc.Finish();
177 queue.Submit(1, &cmd);
180 WGPUBufferMapCallbackInfo cbInfo{};
181 cbInfo.mode = WGPUCallbackMode_AllowProcessEvents;
182 cbInfo.callback = [](WGPUMapAsyncStatus
status, WGPUStringView ,
void *userdata1,
184 bool *
ok =
static_cast<bool *
>(userdata1);
185 *
ok = (
status == WGPUMapAsyncStatus_Success);
187 cbInfo.userdata1 = &mapped;
188 wgpuBufferMapAsync(staging.Get(), WGPUMapMode_Read, 0, nbytes, cbInfo);
191 while (!mapped && guard < 2000) {
192#if defined(__EMSCRIPTEN__)
195 wgpuInstanceProcessEvents(gpuInstance().Get());
198 if (!mapped)
return false;
199 const uint8_t *
data =
static_cast<const uint8_t *
>(staging.GetConstMappedRange(0, nbytes));
200 if (!data)
return false;
201 std::memcpy(dst, data,
size_t(nbytes));
210 if (!ws || !ws->ready || !ws->pipeline)
return;
211 if (groupsX <= 0) groupsX = 1;
212 if (groupsY <= 0) groupsY = 1;
213 if (groupsZ <= 0) groupsZ = 1;
215 auto &
device = gpuDevice();
216 auto &queue = gpuQueue();
226 entries[size_t(i)].binding = uint32_t(i);
227 entries[size_t(i)].buffer = (vb && vb->buffer) ? vb->buffer.Get() : dummy->
buffer.Get();
228 entries[size_t(i)].size = (vb && vb->buffer) ? vb->size_ : 4;
233 WGPUBindGroupDescriptor bgDesc{};
234 bgDesc.layout = ws->setLayout.Get();
236 bgDesc.entries = entries;
237 wgpu::BindGroup bg =
device.CreateBindGroup(
reinterpret_cast<const wgpu::BindGroupDescriptor*
>(&bgDesc));
240 wgpu::CommandEncoder enc =
device.CreateCommandEncoder();
241 WGPUComputePassDescriptor cpDesc{};
242 wgpu::ComputePassEncoder pass = enc.BeginComputePass(
reinterpret_cast<const wgpu::ComputePassDescriptor*
>(&cpDesc));
243 pass.SetPipeline(ws->pipeline);
244 uint32_t offsets[1] = {0};
245 pass.SetBindGroup(0, bg, 1, offsets);
246 pass.DispatchWorkgroups(uint32_t(groupsX), uint32_t(groupsY), uint32_t(groupsZ));
248 wgpu::CommandBuffer cmd = enc.Finish();
249 queue.Submit(1, &cmd);
259 if (!
buffer || !src || nbytes == 0)
return;
260 if (dstOffset + nbytes >
size_)
261 throw Exception(
"GpuBuffer.write: out of range");
262 gpuQueue().WriteBuffer(
buffer, dstOffset, src, nbytes);
266 if (!
buffer || !dst || nbytes == 0)
return;
267 if (srcOffset + nbytes >
size_)
268 throw Exception(
"GpuBuffer.read: out of range");
269 readBufferToCpu(
buffer, srcOffset, dst, nbytes);
278 const int off = srcOffset < 0 ? 0 : srcOffset;
280 if (nbytes < 0) nbytes = int(
size_) - off;
283 downloadBytes(out->getData(), uint64_t(nbytes), uint64_t(off));
288 if (floatIndex < 0)
return;
293 if (floatIndex < 0)
return 0.f;
295 downloadBytes(&
v,
sizeof(
float), uint64_t(floatIndex) *
sizeof(
float));
300 if (!
data || count <= 0 || startIndex < 0)
return;
301 uploadBytes(
data, uint64_t(count) *
sizeof(
float), uint64_t(startIndex) *
sizeof(
float));
305 if (!out || count <= 0 || startIndex < 0)
return;
306 downloadBytes(out, uint64_t(count) *
sizeof(
float), uint64_t(startIndex) *
sizeof(
float));
310 if (
size_ <
sizeof(
float))
return;
311 const size_t count = size_t(
size_ /
sizeof(
float));
312 std::vector<float> tmp(count,
value);
In-memory byte buffer implementing eve::Data (ref-counted).
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...
Compute program for the WebGPU backend. Accepts WGSL source; GLSL/SPIR-V input is rejected (browsers ...
float getFloat(int index) const override
void bindBuffer(int binding, GpuBuffer *buffer) override
Bind a storage buffer to set=0 binding. binding in [0, kMaxBindings).
void setFloat(int index, float value) override
void clearBindings() override
GpuBuffer * getBoundBuffer(int binding) const override
~WebGpuComputeShader() override
Storage / staging buffer for the WebGPU backend.
float readFloat32(int floatIndex) override
void downloadBytes(void *dst, uint64_t nbytes, uint64_t srcOffset=0) const override
data::ByteData * readData(int srcOffset=0, int size=-1) override
~WebGpuGpuBuffer() override
void writeFloat32(int floatIndex, float value) override
void readFloat32s(float *out, int count, int startIndex=0) const override
void uploadBytes(const void *src, uint64_t nbytes, uint64_t dstOffset=0) override
void writeFloat32s(const float *data, int count, int startIndex=0) override
Bulk float upload/download (one transfer). startIndex is in floats.
void writeData(data::ByteData *data, int dstOffset=0) override
void fillFloat32(float value) override
void webgpuDispatch(ComputeShader *shader, int groupsX, int groupsY, int groupsZ)
WebGpuGpuBuffer * webgpuNewBuffer(int byteSize, const std::string &usage)
WebGpuComputeShader * webgpuNewShaderFromSpirv(const std::vector< uint32_t > &spv)
WebGpuComputeShader * webgpuNewShaderFromWgsl(const std::string &wgsl)