载入中...
搜索中...
未找到
Gpgpu.cpp
浏览该文件的文档.
1#include "gpgpu/Gpgpu.h"
5#include "gpgpu/GpuBuffer.h"
6#include "gpgpu/Sequence.h"
8
9#include "common/Exception.h"
10#include "common/Capability.h"
11#include "common/Module.h"
14#include "graphics/Graphics.h"
15#include "graphics/Material.h"
16#include "graphics/Mesh.h"
17
18#ifdef EVENGINE_WEBGPU
20#else
23#endif
24
25#include <simplesquirrel/simplesquirrel.hpp>
26
27#include <functional>
28#include <limits>
29#include <memory>
30#include <vector>
31
32namespace eve::gpgpu {
33namespace {
34
35std::string currentGraphicsBackend() {
36 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
37 if (!gfx) gfx = eve::graphics::Graphics::create();
38 if (!gfx) return {};
39 return gfx->getBackendName();
40}
41
42eve::graphics::Graphics *currentGraphics() {
43 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
44 if (!gfx) gfx = eve::graphics::Graphics::create();
45 return gfx;
46}
47
48std::string residentSubmitStatusName(eve::graphics::GpuResidentSubmitStatus status) {
50 switch (status) {
51 case Status::Submitted: return "submitted";
52 case Status::Unsupported: return "unsupported";
53 case Status::InvalidArgument: return "invalid_argument";
54 case Status::BackendMismatch: return "backend_mismatch";
55 case Status::ResourceUnavailable: return "resource_unavailable";
56 case Status::CapacityExceeded: return "capacity_exceeded";
57 }
58 return "unsupported";
59}
60
61int scriptGpuDrivenSlot(uint32_t slot) {
62 if (slot == eve::graphics::kInvalidGpuDrivenSlot || slot > static_cast<uint32_t>(std::numeric_limits<int>::max()))
63 return -1;
64 return static_cast<int>(slot);
65}
66
67std::vector<eve::graphics::GpuResidentInstanceBucket> scriptResidentBuckets(ssq::Array buckets) {
68 std::vector<eve::graphics::GpuResidentInstanceBucket> result;
69 result.reserve(buckets.size());
70 for (std::size_t i = 0; i < buckets.size(); ++i) {
71 ssq::Table bucket = buckets.get<ssq::Table>(i);
72 const int first = bucket.get<int>("firstInstance");
73 const int count = bucket.get<int>("instanceCount");
74 const int mesh = bucket.get<int>("meshId");
75 const int material = bucket.get<int>("materialId");
76 if (first < 0 || count <= 0 || mesh < 0 || material < 0)
77 throw Exception("Gpgpu.submitResidentInstances: bucket %d has invalid fields", static_cast<int>(i));
78 result.push_back({static_cast<uint32_t>(first), static_cast<uint32_t>(count), static_cast<uint32_t>(mesh),
79 static_cast<uint32_t>(material)});
80 }
81 return result;
82}
83
84std::string submitResidentInstances(GpuBuffer *buffer, ssq::Array buckets, int instanceCount, int offsetBytes) {
85 if (!buffer || instanceCount <= 0 || offsetBytes < 0 ||
86 offsetBytes % static_cast<int>(eve::kGpuResidentStorageOffsetAlignment) != 0)
87 return "invalid_argument";
88 auto *gfx = currentGraphics();
89 if (!gfx) return "resource_unavailable";
90 const auto nativeBuckets = scriptResidentBuckets(buckets);
92 batch.buffer = buffer->residentView();
93 batch.buffer.offsetBytes = static_cast<uint64_t>(offsetBytes);
95 batch.buckets = nativeBuckets.data();
96 batch.bucketCount = static_cast<uint32_t>(nativeBuckets.size());
97 batch.instanceCount = static_cast<uint32_t>(instanceCount);
98 return residentSubmitStatusName(gfx->gpuDrivenSubmitResident(batch));
99}
100
101void writeGpuDrivenInstance(GpuBuffer *buffer, int instanceIndex, ssq::Array model, int meshId, int materialId,
102 int flags, int lodGroupId) {
103 if (!buffer || instanceIndex < 0 || model.size() != 16 || meshId < 0 || materialId < 0 || flags < 0 ||
104 lodGroupId < -1)
105 throw Exception("Gpgpu.writeGpuDrivenInstance: invalid argument");
107 float *matrix = &instance.model[0][0];
108 for (std::size_t i = 0; i < 16; ++i) matrix[i] = model.get<float>(i);
109 instance.meshId = static_cast<uint32_t>(meshId);
110 instance.materialId = static_cast<uint32_t>(materialId);
111 instance.flags = static_cast<uint32_t>(flags);
112 instance.lodGroupId = lodGroupId < 0 ? eve::graphics::kInvalidGpuDrivenSlot : static_cast<uint32_t>(lodGroupId);
113 const uint64_t offset = static_cast<uint64_t>(instanceIndex) * sizeof(instance);
114 if (offset + sizeof(instance) > static_cast<uint64_t>(buffer->getSize()))
115 throw Exception("Gpgpu.writeGpuDrivenInstance: instance exceeds buffer capacity");
116 buffer->uploadBytes(&instance, sizeof(instance), offset);
117}
118
119void setGpuDrivenEnabledScript(Gpgpu *gpgpu, bool enabled) {
120 (void)gpgpu;
121 if (auto *gfx = currentGraphics()) gfx->gpuDrivenSetEnabled(enabled);
122}
123
124bool isGpuDrivenEnabledScript(Gpgpu *gpgpu) {
125 (void)gpgpu;
126 auto *gfx = currentGraphics();
127 return gfx && gfx->gpuDrivenEnabled();
128}
129
130int gpuDrivenMeshRecordScript(Gpgpu *gpgpu, eve::graphics::Mesh *mesh) {
131 (void)gpgpu;
132 auto *gfx = currentGraphics();
133 return scriptGpuDrivenSlot(gfx ? gfx->gpuDrivenMeshRecord(mesh) : eve::graphics::kInvalidGpuDrivenSlot);
134}
135
136int gpuDrivenMaterialRecordScript(Gpgpu *gpgpu, eve::graphics::Material *material) {
137 (void)gpgpu;
138 auto *gfx = currentGraphics();
139 return scriptGpuDrivenSlot(gfx ? gfx->gpuDrivenMaterialRecord(material) : eve::graphics::kInvalidGpuDrivenSlot);
140}
141
142bool gpuDrivenMaterialUsableScript(Gpgpu *gpgpu, eve::graphics::Material *material) {
143 (void)gpgpu;
144 auto *gfx = currentGraphics();
145 return gfx && gfx->gpuDrivenMaterialUsable(material);
146}
147
148int getGpuDrivenInstanceStrideScript(Gpgpu *gpgpu) {
149 (void)gpgpu;
150 return sizeof(eve::graphics::GpuInstance);
151}
152
153int getGpuResidentOffsetAlignmentScript(Gpgpu *gpgpu) {
154 (void)gpgpu;
156}
157
158std::string submitSequenceAsyncScript(Sequence *sequence) {
159 if (!sequence) return "failed";
160 (void)sequence->submitAsync();
161 return sequence->getStatusName();
162}
163
164std::string pollSequenceScript(Sequence *sequence) {
165 if (!sequence) return "failed";
166 (void)sequence->poll();
167 return sequence->getStatusName();
168}
169
170std::string waitSequenceScript(Sequence *sequence) {
171 if (!sequence) return "failed";
172 (void)sequence->wait();
173 return sequence->getStatusName();
174}
175
176void setShaderScript(ShaderSystem *system, ComputeShader *shader) {
177 if (system) system->setShader(shader, false);
178}
179
180void dispatchShaderSystemScript(ShaderSystem *system, int entityCount, float deltaTime) {
181 if (system) system->dispatch(entityCount, deltaTime);
182}
183
184void writeGpuDrivenInstanceScript(Gpgpu *gpgpu, GpuBuffer *buffer, int instanceIndex, ssq::Array model, int meshId,
185 int materialId, int flags, int lodGroupId) {
186 (void)gpgpu;
187 writeGpuDrivenInstance(buffer, instanceIndex, model, meshId, materialId, flags, lodGroupId);
188}
189
190std::string submitResidentInstancesScript(Gpgpu *gpgpu, GpuBuffer *buffer, ssq::Array buckets, int instanceCount,
191 int offsetBytes) {
192 (void)gpgpu;
193 return submitResidentInstances(buffer, buckets, instanceCount, offsetBytes);
194}
195
196} // namespace
197
199
200Gpgpu::Gpgpu() { eve::cap::provide<IMeshDeformationCompute>(this); }
201
202Gpgpu::~Gpgpu() { eve::cap::revoke<IMeshDeformationCompute>(this); }
203
205 const std::size_t count = request.positions.size() / 3u;
206 if (!isAvailable())
207 return Result<std::vector<float>>::failure(
208 Diagnostic::error(DiagnosticCode::Unsupported, "mesh deformation compute backend is unavailable"));
209 if (count == 0 || request.positions.size() != count * 3u || request.normals.size() != count * 3u ||
210 (!request.baseline.empty() && request.baseline.size() != count * 3u) ||
211 (!request.targets.empty() && request.targets.size() != count * 3u) || count > 4'000'000u)
212 return Result<std::vector<float>>::failure(
213 Diagnostic::error(DiagnosticCode::InvalidArgument, "invalid mesh deformation compute buffers"));
214
215 std::vector<float> positions(count * 4u), normals(count * 4u), baseline(count * 4u), targets(count * 4u);
216 for (std::size_t i = 0; i < count; ++i) {
217 for (std::size_t c = 0; c < 3u; ++c) {
218 positions[i * 4u + c] = request.positions[i * 3u + c];
219 normals[i * 4u + c] = request.normals[i * 3u + c];
220 baseline[i * 4u + c] = request.baseline.empty() ? positions[i * 4u + c] : request.baseline[i * 3u + c];
221 targets[i * 4u + c] = request.targets.empty() ? positions[i * 4u + c] : request.targets[i * 3u + c];
222 }
223 }
224 static constexpr const char* glsl = R"(#version 450
225layout(local_size_x=64) in;
226layout(set=0,binding=0) buffer P{vec4 v[];} p;
227layout(set=0,binding=1) readonly buffer N{vec4 v[];} n;
228layout(set=0,binding=2) readonly buffer B{vec4 v[];} b;
229layout(set=0,binding=3) readonly buffer T{vec4 v[];} t;
230layout(push_constant) uniform PC{float d[32];} pc;
231void main(){uint i=gl_GlobalInvocationID.x; if(i>=uint(pc.d[0]))return;
232 vec3 q=p.v[i].xyz,c=vec3(pc.d[2],pc.d[3],pc.d[4]); float dist=distance(q,c);
233 if(dist>=pc.d[5])return; float w=pow(1.0-dist/pc.d[5],pc.d[7])*pc.d[6]; int op=int(pc.d[1]);
234 if(op==4){p.v[i].xyz=mix(q,t.v[i].xyz,clamp(abs(w),0.0,1.0));return;}
235 if(op==2){p.v[i].y=mix(q.y,pc.d[3],clamp(abs(w),0.0,1.0));return;}
236 vec3 dir=(op==3||op==5)?normalize(vec3(pc.d[8],pc.d[9],pc.d[10])):n.v[i].xyz;
237 if(op==1)w=-w; vec3 outp=q+dir*w;
238 if(op==5){vec3 delta=outp-b.v[i].xyz;float len=length(delta);if(len>pc.d[11])delta*=pc.d[11]/len;outp=b.v[i].xyz+delta;}
239 p.v[i].xyz=outp;}
240)";
241 static constexpr const char* wgsl = R"(
242struct V{v:array<vec4f>}; struct Push{d:array<vec4f,8>};
243@group(0) @binding(0) var<storage,read_write> p:V; @group(0) @binding(1) var<storage,read> n:V;
244@group(0) @binding(2) var<storage,read> b:V; @group(0) @binding(3) var<storage,read> t:V;
245@group(0) @binding(8) var<uniform> pc:Push;
246fn f(i:u32)->f32{return pc.d[i/4u][i%4u];}
247@compute @workgroup_size(64) fn main(@builtin(global_invocation_id) gid:vec3u){let i=gid.x;if(i>=u32(f(0u))){return;}
248 let q=p.v[i].xyz;let c=vec3f(f(2u),f(3u),f(4u));let dist=distance(q,c);if(dist>=f(5u)){return;}
249 var w=pow(1.0-dist/f(5u),f(7u))*f(6u);let op=i32(f(1u));
250 if(op==4){p.v[i]=vec4f(mix(q,t.v[i].xyz,clamp(abs(w),0.0,1.0)),0.0);return;}
251 if(op==2){p.v[i]=vec4f(q.x,mix(q.y,f(3u),clamp(abs(w),0.0,1.0)),q.z,0.0);return;}
252 var dir=n.v[i].xyz;if(op==3||op==5){dir=normalize(vec3f(f(8u),f(9u),f(10u)));}if(op==1){w=-w;}
253 var outp=q+dir*w;if(op==5){var delta=outp-b.v[i].xyz;let len=length(delta);if(len>f(11u)){delta*=f(11u)/len;}outp=b.v[i].xyz+delta;}
254 p.v[i]=vec4f(outp,0.0);}
255)";
256 try {
257 std::unique_ptr<GpuBuffer> p(newBuffer(static_cast<int>(positions.size() * sizeof(float)), "storage"));
258 std::unique_ptr<GpuBuffer> n(newBuffer(static_cast<int>(normals.size() * sizeof(float)), "storage"));
259 std::unique_ptr<GpuBuffer> b(newBuffer(static_cast<int>(baseline.size() * sizeof(float)), "storage"));
260 std::unique_ptr<GpuBuffer> t(newBuffer(static_cast<int>(targets.size() * sizeof(float)), "storage"));
261 p->writeFloat32s(positions.data(), static_cast<int>(positions.size()));
262 n->writeFloat32s(normals.data(), static_cast<int>(normals.size()));
263 b->writeFloat32s(baseline.data(), static_cast<int>(baseline.size()));
264 t->writeFloat32s(targets.data(), static_cast<int>(targets.size()));
265 std::unique_ptr<ComputeShader> shader(newShader(currentGraphicsBackend() == "webgpu" ? wgsl : glsl));
266 shader->bindBuffer(0, p.get()); shader->bindBuffer(1, n.get()); shader->bindBuffer(2, b.get()); shader->bindBuffer(3, t.get());
267 shader->setFloat(0, static_cast<float>(count));
268 shader->setFloat(1, static_cast<float>(static_cast<int>(request.operation)));
269 shader->setFloat(2, request.centerX); shader->setFloat(3, request.centerY); shader->setFloat(4, request.centerZ);
270 shader->setFloat(5, request.radius); shader->setFloat(6, request.strength); shader->setFloat(7, request.falloff);
271 shader->setFloat(8, request.directionX); shader->setFloat(9, request.directionY); shader->setFloat(10, request.directionZ);
272 shader->setFloat(11, request.maxDisplacement);
273 dispatch(shader.get(), static_cast<int>((count + 63u) / 64u), 1, 1);
274 p->readFloat32s(positions.data(), static_cast<int>(positions.size()));
275 std::vector<float> result(count * 3u);
276 for (std::size_t i = 0; i < count; ++i)
277 for (std::size_t c = 0; c < 3u; ++c) result[i * 3u + c] = positions[i * 4u + c];
278 return Result<std::vector<float>>::success(std::move(result));
279 } catch (const std::exception& error) {
281 }
282}
283
284bool Gpgpu::isAvailable() const {
285#ifdef EVENGINE_WEBGPU
286 if (currentGraphicsBackend() != "webgpu") return false;
287 return webgpuGpgpuReady();
288#else
289 if (currentGraphicsBackend() != "vulkan") return false;
290 return vulkanGpgpuReady();
291#endif
292}
293
295#ifdef EVENGINE_WEBGPU
296 return Result<std::vector<uint32_t>>::failure(
297 Diagnostic::error(DiagnosticCode::Unsupported, "GLSL compilation requires Vulkan"));
298#else
299 try {
301 } catch (const std::exception &e) {
303 }
304#endif
305}
306
307Result<std::unique_ptr<ComputeShader>> createComputeShader(const std::vector<uint32_t> &words) {
308#ifdef EVENGINE_WEBGPU
310 Diagnostic::error(DiagnosticCode::Unsupported, "SPIR-V pipelines require Vulkan"));
311#else
312 try {
313 if (words.size() < 5 || words.front() != 0x07230203)
315 Diagnostic::error(DiagnosticCode::InvalidArgument, "Invalid SPIR-V header"));
317 std::unique_ptr<ComputeShader>(vulkanNewShaderFromSpirv(words)));
318 } catch (const std::exception &e) {
320 }
321#endif
322}
323
325#ifdef EVENGINE_WEBGPU
326 if (currentGraphicsBackend() != "webgpu")
327 throw Exception("Gpgpu.newShader: requires webgpu Graphics backend");
328 // Source is WGSL on the WebGPU backend (browsers cannot compile GLSL).
330#else
331 if (currentGraphicsBackend() != "vulkan")
332 throw Exception("Gpgpu.newShader: requires vulkan Graphics backend");
333 auto compiled = compileComputeSpirv(source);
334 if (!compiled.ok()) throw Exception("%s", compiled.error()->message().c_str());
335 auto shader = createComputeShader(compiled.value());
336 if (!shader.ok()) throw Exception("%s", shader.error()->message().c_str());
337 return shader.value().release();
338#endif
339}
340
342#ifdef EVENGINE_WEBGPU
343 if (currentGraphicsBackend() != "webgpu")
344 throw Exception("Gpgpu.newShaderFromBytecode: requires webgpu Graphics backend");
345 // Load WGSL text from the given path.
346 auto *fs = eve::filesystem::Filesystem::create();
347 if (!fs) throw Exception("Gpgpu.newShaderFromBytecode: no filesystem");
348 std::unique_ptr<eve::filesystem::FileData> file(fs->read(path));
349 if (!file) throw Exception("Gpgpu.newShaderFromBytecode: cannot open '%s'", path.c_str());
350 std::string src(reinterpret_cast<const char *>(file->getData()), file->getSize());
351 return webgpuNewShaderFromWgsl(src);
352#else
353 if (currentGraphicsBackend() != "vulkan")
354 throw Exception("Gpgpu.newShaderFromBytecode: requires vulkan Graphics backend");
356#endif
357}
358
360#ifdef EVENGINE_WEBGPU
361 if (currentGraphicsBackend() != "webgpu")
362 throw Exception("Gpgpu.newShaderFromSpvFile: requires webgpu Graphics backend");
363 throw Exception("Gpgpu.newShaderFromSpvFile: SPIR-V is only supported on vulkan; "
364 "use newShaderFromBytecode with a .wgsl file on the WebGPU backend");
365#else
366 if (currentGraphicsBackend() != "vulkan")
367 throw Exception("Gpgpu.newShaderFromSpvFile: SPIR-V is only supported on vulkan");
369#endif
370}
371
372GpuBuffer *Gpgpu::newBuffer(int byteSize, const std::string &usage) {
373#ifdef EVENGINE_WEBGPU
374 if (currentGraphicsBackend() != "webgpu")
375 throw Exception("Gpgpu.newBuffer: requires webgpu Graphics backend");
376 return webgpuNewBuffer(byteSize, usage);
377#else
378 if (currentGraphicsBackend() != "vulkan")
379 throw Exception("Gpgpu.newBuffer: requires vulkan Graphics backend");
380 return vulkanNewBuffer(byteSize, usage);
381#endif
382}
383
385#ifdef EVENGINE_WEBGPU
386 if (currentGraphicsBackend() != "webgpu")
387 throw Exception("Gpgpu.newSequence: requires webgpu Graphics backend");
388 return new Sequence();
389#else
390 if (currentGraphicsBackend() != "vulkan")
391 throw Exception("Gpgpu.newSequence: requires vulkan Graphics backend");
392 return new Sequence();
393#endif
394}
395
396void Gpgpu::dispatch(ComputeShader *shader, int groupsX, int groupsY, int groupsZ) {
397 if (!shader) return;
398#ifdef EVENGINE_WEBGPU
399 if (currentGraphicsBackend() != "webgpu")
400 throw Exception("Gpgpu.dispatch: requires webgpu Graphics backend");
401 webgpuDispatch(shader, groupsX, groupsY, groupsZ);
402#else
403 if (currentGraphicsBackend() != "vulkan")
404 throw Exception("Gpgpu.dispatch: requires vulkan Graphics backend");
405 vulkanDispatch(shader, groupsX, groupsY, groupsZ);
406#endif
407}
408
409void Gpgpu::expose(ssq::Table &table) {
410 auto cls = table.addClass(name, Gpgpu::create, false);
411 expose(cls);
412
413 auto shader = table.addClass<ComputeShader>(
414 "ComputeShader",
415 std::function<ComputeShader *()>([]() -> ComputeShader * { return nullptr; }), true);
416 shader.addFunc("bindBuffer", &ComputeShader::bindBuffer);
417 shader.addFunc("getBoundBuffer", &ComputeShader::getBoundBuffer);
418 shader.addFunc("setFloat", &ComputeShader::setFloat);
419 shader.addFunc("getFloat", &ComputeShader::getFloat);
420 shader.addFunc("clearBindings", &ComputeShader::clearBindings);
421
422 auto buf = table.addClass<GpuBuffer>(
423 "GpuBuffer", std::function<GpuBuffer *()>([]() -> GpuBuffer * { return nullptr; }), true);
424 buf.addFunc("getSize", &GpuBuffer::getSize);
425 buf.addFunc("getUsage", &GpuBuffer::getUsage);
426 buf.addFunc("writeData", &GpuBuffer::writeData);
427 buf.addFunc("readData", &GpuBuffer::readData);
428 buf.addFunc("writeFloat32", &GpuBuffer::writeFloat32);
429 buf.addFunc("readFloat32", &GpuBuffer::readFloat32);
430 buf.addFunc("fillFloat32", &GpuBuffer::fillFloat32);
431
432 auto seq = table.addClass<Sequence>(
433 "GpuSequence",
434 std::function<Sequence *()>([]() -> Sequence * { return new Sequence(); }), true);
435 seq.addFunc("isAvailable", &Sequence::isAvailable);
436 seq.addFunc("begin", &Sequence::begin);
437 seq.addFunc("recordUpload", &Sequence::recordUpload);
438 seq.addFunc("recordDownload", &Sequence::recordDownload);
439 seq.addFunc("recordDispatch", &Sequence::recordDispatch);
440 seq.addFunc("submit", &Sequence::submit);
441 seq.addFunc("submitAsync", submitSequenceAsyncScript);
442 seq.addFunc("poll", pollSequenceScript);
443 seq.addFunc("wait", waitSequenceScript);
444 seq.addFunc("getStatus", &Sequence::getStatusName);
445
446 // Native ECS↔GPU helper (used by eve.ShaderSystem script class).
447 auto ecsSys = table.addClass<ShaderSystem>(
448 "EcsShaderSystem",
449 std::function<ShaderSystem *()>([]() -> ShaderSystem * { return new ShaderSystem(); }),
450 true);
451 ecsSys.addFunc("setGpgpu", &ShaderSystem::setGpgpu);
452 ecsSys.addFunc("getGpgpu", &ShaderSystem::getGpgpu);
453 ecsSys.addFunc("setShaderSource", &ShaderSystem::setShaderSource);
454 ecsSys.addFunc("setShader", setShaderScript);
455 ecsSys.addFunc("getShader", &ShaderSystem::getShader);
456 ecsSys.addFunc("setLocalSize", &ShaderSystem::setLocalSize);
457 ecsSys.addFunc("getLocalSize", &ShaderSystem::getLocalSize);
458 ecsSys.addFunc("ensureBuffer", &ShaderSystem::ensureBuffer);
459 ecsSys.addFunc("getBuffer", &ShaderSystem::getBuffer);
460 ecsSys.addFunc("attachBuffer", &ShaderSystem::attachBuffer);
461 ecsSys.addFunc("setFloat", &ShaderSystem::setFloat);
462 ecsSys.addFunc("getFloat", &ShaderSystem::getFloat);
463 ecsSys.addFunc("getUploadCount", &ShaderSystem::getUploadCount);
464 ecsSys.addFunc("getDownloadCount", &ShaderSystem::getDownloadCount);
465 ecsSys.addFunc("getDispatchCount", &ShaderSystem::getDispatchCount);
466 ecsSys.addFunc("resetStatistics", &ShaderSystem::resetStatistics);
467 ecsSys.addFunc("dispatch", dispatchShaderSystemScript);
468 ecsSys.addFunc("recordDispatch", &ShaderSystem::recordDispatch);
469 ecsSys.addFunc("clearBuffers", &ShaderSystem::clearBuffers);
470
471 table.addFunc("packEcsFloats", packScriptEntityFloats);
472 table.addFunc("packEcsFloatsRange", packScriptEntityFloatsRange);
473 table.addFunc("unpackEcsFloats", unpackScriptEntityFloats);
474 table.addFunc("unpackEcsFloatsRange", unpackScriptEntityFloatsRange);
475}
476
477void Gpgpu::expose(ssq::Class &cls) {
478 cls.addFunc("getName", &Gpgpu::getName);
479 cls.addFunc("isAvailable", &Gpgpu::isAvailable);
480 cls.addFunc("newShader", &Gpgpu::newShader);
481 cls.addFunc("newShaderFromBytecode", &Gpgpu::newShaderFromBytecode);
482 cls.addFunc("newShaderFromSpvFile", &Gpgpu::newShaderFromSpvFile);
483 cls.addFunc("newBuffer", &Gpgpu::newBuffer);
484 cls.addFunc("newSequence", &Gpgpu::newSequence);
485 cls.addFunc("dispatch", &Gpgpu::dispatch);
486 cls.addFunc("setGpuDrivenEnabled", setGpuDrivenEnabledScript);
487 cls.addFunc("isGpuDrivenEnabled", isGpuDrivenEnabledScript);
488 cls.addFunc("gpuDrivenMeshRecord", gpuDrivenMeshRecordScript);
489 cls.addFunc("gpuDrivenMaterialRecord", gpuDrivenMaterialRecordScript);
490 cls.addFunc("gpuDrivenMaterialUsable", gpuDrivenMaterialUsableScript);
491 cls.addFunc("getGpuDrivenInstanceStride", getGpuDrivenInstanceStrideScript);
492 cls.addFunc("getGpuResidentOffsetAlignment", getGpuResidentOffsetAlignmentScript);
493 cls.addFunc("writeGpuDrivenInstance", writeGpuDrivenInstanceScript);
494 cls.addFunc("submitResidentInstances", submitResidentInstancesScript);
495}
496
497} // namespace eve::gpgpu
std::string usage
glm::vec4 p[6]
HSQOBJECT cls
Definition ECS.cpp:21
filesystem::File * file
const GltfImportRequest & request
std::uint32_t instanceCount
wgpu::PopErrorScopeStatus status
glm::vec3 n
Definition Grass.cpp:63
std::vector< float > normals
std::vector< float > positions
std::int32_t c
std::int32_t first
size_t offset
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::unique_ptr< gpgpu::Sequence > sequence
Definition OnnxGpgpu.cpp:43
std::string error
Definition Package.cpp:60
std::string path
Definition PlayHost.cpp:110
float t
Mesh * mesh
Shader * shader
glm::mat4 model
Material * material
std::uint32_t count
const UnitySourceAsset & source
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
virtual std::string getName() const =0
Returns the name.
Move-only operation result carrying either a value or Status.
Definition Result.h:155
Backend-agnostic compute program. Bind storage buffers then dispatch via Gpgpu::dispatch....
virtual void setFloat(int index, float value)=0
Sets the float.
virtual GpuBuffer * getBoundBuffer(int binding) const =0
Returns the bound buffer.
virtual void clearBindings()=0
Clears bindings.
virtual float getFloat(int index) const =0
Returns the float.
virtual void bindBuffer(int binding, GpuBuffer *buffer)=0
Bind a storage buffer to set=0 binding. binding in [0, kMaxBindings).
GPGPU module — compute shaders + storage buffers via the active Graphics backend. Uses the graphics q...
Definition Gpgpu.h:23
ComputeShader * newShaderFromSpvFile(const std::string &path)
Vulkan SPIR-V compatibility wrapper → newShaderFromBytecode.
Definition Gpgpu.cpp:359
ComputeShader * newShader(const std::string &source)
Compatibility-only raw-owning shader factory (Vulkan: GLSL; WebGPU: WGSL). Vulkan delegates to the ch...
Definition Gpgpu.cpp:324
Result< std::vector< float > > deform(MeshDeformationComputeRequest request) override
Execute mesh deformation through the active compute backend.
Definition Gpgpu.cpp:204
~Gpgpu() override
Gpgpu.
Definition Gpgpu.cpp:202
bool isAvailable() const
True when the active Graphics backend can run compute (device initialized).
Definition Gpgpu.cpp:284
ComputeShader * newShaderFromBytecode(const std::string &path)
Load precompiled compute bytecode from Filesystem path (Vulkan: SPIR-V).
Definition Gpgpu.cpp:341
void dispatch(ComputeShader *shader, int groupsX, int groupsY=1, int groupsZ=1)
Record + submit a compute dispatch and wait for completion (sync). groups*: workgroup counts (not thr...
Definition Gpgpu.cpp:396
GpuBuffer * newBuffer(int byteSize, const std::string &usage="storage")
Allocate a GPU buffer. usage: "storage" (SSBO, device-local) | "staging" (host-visible transfer).
Definition Gpgpu.cpp:372
Sequence * newSequence()
Create a Kompute-style command Sequence: record buffer transfers and compute dispatches into one comm...
Definition Gpgpu.cpp:384
Backend-agnostic GPU buffer for compute (storage) or CPU staging transfers. Squirrel-owned; derived c...
Definition GpuBuffer.h:18
virtual void writeData(data::ByteData *data, int dstOffset=0)=0
Writes data.
virtual int getSize() const =0
Byte length of the owned buffer.
virtual std::string getUsage() const =0
Returns the usage.
virtual void fillFloat32(float value)=0
Fill float 32.
virtual float readFloat32(int floatIndex)=0
Reads float 32.
virtual void writeFloat32(int floatIndex, float value)=0
Writes float 32.
virtual data::ByteData * readData(int srcOffset=0, int size=-1)=0
Reads data.
EVENGINE_API_WORLD public API.
Definition Sequence.h:41
void recordUpload(GpuBuffer *dst, const void *src, uint64_t nbytes, uint64_t dstOffset=0)
Record upload.
Definition Sequence.cpp:77
void recordDownload(GpuBuffer *src, GpuBuffer *staging, uint64_t nbytes, uint64_t srcOffset=0)
Record download.
Definition Sequence.cpp:82
void submit()
Submit.
Definition Sequence.cpp:92
void recordDispatch(ComputeShader *shader, int groupsX, int groupsY=1, int groupsZ=1)
Record dispatch.
Definition Sequence.cpp:87
std::string getStatusName() const
Stable script/debug name for getStatus().
Definition Sequence.cpp:105
void begin()
Begins begin.
Definition Sequence.cpp:75
bool isAvailable() const
True when available.
Definition Sequence.cpp:66
Gpgpu * getGpgpu() const
Returns the gpgpu.
uint64_t getDispatchCount() const
Number of compute dispatches issued through this system.
GpuBuffer * getBuffer(int binding) const
Returns the buffer.
GpuBuffer * ensureBuffer(int binding, int floatCount)
Ensure buffer.
int getLocalSize() const
Returns the local size.
void clearBuffers()
Clears buffers.
void setFloat(int index, float value)
Sets the float.
float getFloat(int index) const
Returns the float.
void attachBuffer(int binding, GpuBuffer *buffer)
Attach a non-owning resident buffer produced by another GPU system.
void setShaderSource(const std::string &glsl)
Sets the shader source.
void setLocalSize(int localSize)
Sets the local size.
void setGpgpu(Gpgpu *gpu)
Sets the gpgpu.
ComputeShader * getShader() const
Returns the shader.
void resetStatistics()
Reset transfer and dispatch counters used for profiling.
uint64_t getDownloadCount() const
Number of device-to-host downloads issued through this system.
uint64_t getUploadCount() const
Number of host-to-device uploads issued through this system.
void recordDispatch(Sequence *sequence, int entityCount, float dt=0.f)
Record this system into a caller-owned sequence without submitting or waiting. The sequence and all a...
Packages shading method + surface parameters into one attachable asset.
Definition Material.h:35
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
eve::StatusCode Status
Fluids module — interactive surface fluid simulation.
Definition Fluids.h:34
bool vulkanGpgpuReady()
Vulkan 后端是否就绪(设备/队列可用)。
bool webgpuGpgpuReady()
Webgpu gpgpu ready.
ComputeShader * vulkanNewShaderFromSpirv(const std::vector< uint32_t > &spv)
从 SPIR-V 字节码创建计算着色器。
void webgpuDispatch(ComputeShader *shader, int groupsX, int groupsY, int groupsZ)
Webgpu dispatch.
EVENGINE_API_WORLD Result< std::unique_ptr< ComputeShader > > createComputeShader(const std::vector< uint32_t > &words)
Create an owning compute pipeline from valid SPIR-V produced by compileComputeSpirv.
Definition Gpgpu.cpp:307
std::vector< uint32_t > loadSpirvFile(const std::string &path)
Loads spirv file.
EVENGINE_API_WORLD Result< std::vector< uint32_t > > compileComputeSpirv(const std::string &source)
Compile GLSL compute source to owning SPIR-V without accessing Graphics or a GPU.
Definition Gpgpu.cpp:294
GpuBuffer * vulkanNewBuffer(int byteSize, const std::string &usage)
创建 GPU 存储/传输缓冲区;usage 为 "storage" | "vertex" 等。
WebGpuGpuBuffer * webgpuNewBuffer(int byteSize, const std::string &usage)
Webgpu new buffer.
std::vector< uint32_t > compileComputeGlsl(const std::string &glsl)
Compiles compute glsl.
int unpackScriptEntityFloatsRange(ssq::Object entitiesObj, const std::string &slot, ssq::Object fieldsObj, GpuBuffer *buf, int firstEntity, int entityCount)
Unpack a contiguous buffer range into the matching stable ECS view range.
int packScriptEntityFloats(ssq::Object entitiesObj, const std::string &slot, ssq::Object fieldsObj, GpuBuffer *buf)
Pack script ECS entities' component number fields into a GpuBuffer (AoS). entities: Squirrel array of...
int unpackScriptEntityFloats(ssq::Object entitiesObj, const std::string &slot, ssq::Object fieldsObj, GpuBuffer *buf, int entityCount)
Inverse of packScriptEntityFloats; entityCount should match pack result.
int packScriptEntityFloatsRange(ssq::Object entitiesObj, const std::string &slot, ssq::Object fieldsObj, GpuBuffer *buf, int firstEntity, int entityCount)
Pack a contiguous entity range into the matching range of an existing buffer.
WebGpuComputeShader * webgpuNewShaderFromWgsl(const std::string &wgsl)
Webgpu new shader from wgsl.
void vulkanDispatch(ComputeShader *shader, int groupsX, int groupsY, int groupsZ)
派发计算着色器(groupsX/Y/Z 为线程组数)。
constexpr uint32_t kInvalidGpuDrivenSlot
GPU-driven rendering shared constants + std430 GPU layouts.
GpuResidentSubmitStatus
Structured result for direct resident-instance submission.
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
constexpr uint64_t kGpuResidentStorageOffsetAlignment
Portable alignment required for resident storage-buffer slice offsets.
bool enabled
Owning, backend-neutral input for one synchronous GPU mesh deformation.
Per-instance GPU record (std430). Mirrors GLSL GpuInstance.
Direct-render description for a GPU-authored array of GpuInstance records. @ownership buckets and buf...
const GpuResidentInstanceBucket * buckets
uint32_t bucket