载入中...
搜索中...
未找到
PointCompute.cpp
浏览该文件的文档.
2
3#include "common/Module.h"
4#include "common/config.h"
6#include "gpgpu/Gpgpu.h"
7#include "gpgpu/GpuBuffer.h"
8#include "gpgpu/Sequence.h"
9
10#include <algorithm>
11#include <cmath>
12#include <cstdlib>
13#include <exception>
14#include <limits>
15#include <memory>
16#include <string>
17#include <vector>
18
19namespace eve::procgen {
20namespace {
21
22constexpr int kPointFloats = 11;
23constexpr int kWorkgroupSize = 64;
24constexpr int kMaxTransforms = 4;
25
26bool forcedFailure(const char* stage) {
27 const char* requested = std::getenv("EVENGINE_POINT_COMPUTE_FAIL");
28 return requested && std::string(requested) == stage;
29}
30
31const char* transformKernel() {
32#ifdef EVENGINE_WEBGPU
33 return R"(
34struct Data { values: array<f32> };
35struct Push { data: array<vec4f, 8> };
36@group(0) @binding(0) var<storage, read_write> points: Data;
37@group(0) @binding(8) var<uniform> push: Push;
38fn parameter(index: u32) -> f32 {
39 let value = push.data[index / 4u];
40 switch index % 4u {
41 case 0u: { return value.x; }
42 case 1u: { return value.y; }
43 case 2u: { return value.z; }
44 default: { return value.w; }
45 }
46}
47@compute @workgroup_size(64)
48fn main(@builtin(global_invocation_id) gid: vec3u) {
49 if (gid.x >= u32(parameter(0u))) { return; }
50 let base = gid.x * 11u;
51 for (var operation = 0u; operation < min(u32(parameter(1u)), 4u); operation++) {
52 let offset = 2u + operation * 7u;
53 let sx = parameter(offset + 3u);
54 let sy = parameter(offset + 4u);
55 let sz = parameter(offset + 5u);
56 let yaw = parameter(offset + 6u);
57 let radians = yaw * 0.017453292519943295;
58 let c = cos(radians);
59 let s = sin(radians);
60 let x = points.values[base] * sx;
61 let z = points.values[base + 2u] * sz;
62 points.values[base] = x * c - z * s + parameter(offset);
63 points.values[base + 1u] = points.values[base + 1u] * sy + parameter(offset + 1u);
64 points.values[base + 2u] = x * s + z * c + parameter(offset + 2u);
65 var nx = points.values[base + 3u] / select(1.0, sx, abs(sx) > 0.000001);
66 var ny = points.values[base + 4u] / select(1.0, sy, abs(sy) > 0.000001);
67 var nz = points.values[base + 5u] / select(1.0, sz, abs(sz) > 0.000001);
68 let rotated = vec3f(nx * c - nz * s, ny, nx * s + nz * c);
69 let normal = select(rotated, normalize(rotated), dot(rotated, rotated) > 0.0);
70 points.values[base + 3u] = normal.x;
71 points.values[base + 4u] = normal.y;
72 points.values[base + 5u] = normal.z;
73 points.values[base + 6u] += yaw;
74 points.values[base + 7u] *= sx;
75 points.values[base + 8u] *= sy;
76 points.values[base + 9u] *= sz;
77 }
78}
79)";
80#else
81 return R"(#version 450
82layout(local_size_x = 64) in;
83layout(set = 0, binding = 0) buffer Data { float values[]; } points;
84layout(push_constant) uniform Push { float data[32]; } push;
85void main() {
86 uint i = gl_GlobalInvocationID.x;
87 if (i >= uint(push.data[0])) return;
88 uint base = i * 11u;
89 for (int operation = 0; operation < min(int(push.data[1]), 4); ++operation) {
90 int offset = 2 + operation * 7;
91 float sx = push.data[offset + 3], sy = push.data[offset + 4], sz = push.data[offset + 5];
92 float yaw = push.data[offset + 6];
93 float radians = yaw * 0.017453292519943295;
94 float c = cos(radians), s = sin(radians);
95 float x = points.values[base] * sx;
96 float z = points.values[base + 2u] * sz;
97 points.values[base] = x * c - z * s + push.data[offset];
98 points.values[base + 1u] = points.values[base + 1u] * sy + push.data[offset + 1];
99 points.values[base + 2u] = x * s + z * c + push.data[offset + 2];
100 float nx = points.values[base + 3u] / (abs(sx) > 0.000001 ? sx : 1.0);
101 float ny = points.values[base + 4u] / (abs(sy) > 0.000001 ? sy : 1.0);
102 float nz = points.values[base + 5u] / (abs(sz) > 0.000001 ? sz : 1.0);
103 vec3 normal = vec3(nx * c - nz * s, ny, nx * s + nz * c);
104 float normalLength = length(normal);
105 if (normalLength > 0.0) normal /= normalLength;
106 points.values[base + 3u] = normal.x;
107 points.values[base + 4u] = normal.y;
108 points.values[base + 5u] = normal.z;
109 points.values[base + 6u] += yaw;
110 points.values[base + 7u] *= sx;
111 points.values[base + 8u] *= sy;
112 points.values[base + 9u] *= sz;
113 }
114}
115)";
116#endif
117}
118
119} // namespace
120
122 std::vector<float> packed;
123 std::unique_ptr<eve::gpgpu::GpuBuffer> storage;
124 std::unique_ptr<eve::gpgpu::GpuBuffer> staging;
125 std::unique_ptr<eve::gpgpu::ComputeShader> shader;
126 std::unique_ptr<eve::gpgpu::Sequence> sequence;
128 uint64_t uploadCount = 0;
129 uint64_t dispatchCount = 0;
130 uint64_t readbackCount = 0;
131 uint64_t bufferReuseCount = 0;
132 uint64_t peakBufferBytes = 0;
134
135 void reset() {
136 sequence.reset();
137 shader.reset();
138 staging.reset();
139 storage.reset();
140 capacityBytes = 0;
141 }
142};
143
144PointCompute::PointCompute() : impl_(std::make_unique<Impl>()) {}
146
147bool PointCompute::transform(const PointSet& input, PointSet& output, float translateX, float translateY,
148 float translateZ, float yawDegrees, float scaleX, float scaleY, float scaleZ) {
149 return transformChain(input, output, {{translateX, translateY, translateZ, yawDegrees, scaleX, scaleY, scaleZ}});
150}
151
152bool PointCompute::transformChain(const PointSet& input, PointSet& output, const std::vector<Transform>& transforms) {
153 error_.clear();
154 if (transforms.empty() || transforms.size() > kMaxTransforms) {
155 error_ = "transform chain must contain one to four operations";
156 return false;
157 }
158 if (input.empty()) {
159 output = input;
160 return true;
161 }
162 constexpr size_t bytesPerPoint = size_t(kPointFloats) * sizeof(float);
163 if (size_t(input.getCount()) > size_t(std::numeric_limits<int>::max()) / bytesPerPoint) {
164 error_ = "point data exceeds GPU buffer address range";
165 return false;
166 }
167 try {
168 if (forcedFailure("compile")) {
169 error_ = "forced shader compilation failure";
170 return false;
171 }
172 auto* gpu = eve::ModuleManager::getInstance<eve::gpgpu::Gpgpu>("Gpgpu");
173 if (!gpu) gpu = eve::gpgpu::Gpgpu::create();
174 if (!gpu || !gpu->isAvailable()) {
175 error_ = "compute device unavailable";
176 return false;
177 }
178
179 impl_->packed.resize(size_t(input.getCount()) * kPointFloats);
180 std::vector<float>& packed = impl_->packed;
181 for (int index = 0; index < input.getCount(); ++index) {
182 const ProcgenPoint& point = input.points()[size_t(index)];
183 float* values = packed.data() + size_t(index) * kPointFloats;
184 values[0] = point.x;
185 values[1] = point.y;
186 values[2] = point.z;
187 values[3] = point.normalX;
188 values[4] = point.normalY;
189 values[5] = point.normalZ;
190 values[6] = point.yaw;
191 values[7] = point.scaleX;
192 values[8] = point.scaleY;
193 values[9] = point.scaleZ;
194 values[10] = point.density;
195 }
196 const int byteSize = int(packed.size() * sizeof(float));
197 if (!impl_->shader) impl_->shader.reset(gpu->newShader(transformKernel()));
198 if (!impl_->sequence) impl_->sequence.reset(gpu->newSequence());
199 const bool reusedBuffers = byteSize <= impl_->capacityBytes;
200 if (byteSize > impl_->capacityBytes) {
201 if (forcedFailure("allocation")) {
202 error_ = "forced GPU allocation failure";
203 return false;
204 }
205 int capacity = std::max(byteSize, impl_->capacityBytes + impl_->capacityBytes / 2);
206 impl_->storage.reset(gpu->newBuffer(capacity, "storage"));
207 impl_->staging.reset(gpu->newBuffer(capacity, "staging"));
208 impl_->capacityBytes = capacity;
209 impl_->peakBufferBytes = std::max(impl_->peakBufferBytes, uint64_t(capacity));
210 }
211 if (!impl_->storage || !impl_->staging || !impl_->shader || !impl_->sequence ||
212 !impl_->sequence->isAvailable()) {
213 error_ = "compute resources unavailable";
214 return false;
215 }
216 impl_->shader->bindBuffer(0, impl_->storage.get());
217 impl_->shader->setFloat(0, float(input.getCount()));
218 impl_->shader->setFloat(1, float(transforms.size()));
219 for (size_t index = 0; index < transforms.size(); ++index) {
220 const Transform& transform = transforms[index];
221 const int offset = 2 + int(index) * 7;
222 impl_->shader->setFloat(offset, transform.x);
223 impl_->shader->setFloat(offset + 1, transform.y);
224 impl_->shader->setFloat(offset + 2, transform.z);
225 impl_->shader->setFloat(offset + 3, transform.scaleX);
226 impl_->shader->setFloat(offset + 4, transform.scaleY);
227 impl_->shader->setFloat(offset + 5, transform.scaleZ);
228 impl_->shader->setFloat(offset + 6, transform.yaw);
229 }
230 impl_->sequence->begin();
231 impl_->sequence->recordUpload(impl_->storage.get(), packed.data(), uint64_t(byteSize));
232 impl_->sequence->recordDispatch(impl_->shader.get(), (input.getCount() + kWorkgroupSize - 1) / kWorkgroupSize);
233 impl_->sequence->recordDownload(impl_->storage.get(), impl_->staging.get(), uint64_t(byteSize));
234 if (forcedFailure("submit")) {
235 error_ = "forced GPU submission failure";
236 impl_->reset();
237 return false;
238 }
239 impl_->sequence->submit();
240 if (forcedFailure("readback")) {
241 error_ = "forced GPU readback failure";
242 impl_->reset();
243 return false;
244 }
245 impl_->staging->downloadBytes(packed.data(), uint64_t(byteSize));
246 ++impl_->uploadCount;
247 ++impl_->dispatchCount;
248 ++impl_->readbackCount;
249 if (reusedBuffers) ++impl_->bufferReuseCount;
250 impl_->lastFusedTransformCount = int(transforms.size());
251
252 output = input;
253 for (int index = 0; index < output.getCount(); ++index) {
254 ProcgenPoint& point = output.mutablePoint(size_t(index));
255 const float* values = packed.data() + size_t(index) * kPointFloats;
256 point.x = values[0];
257 point.y = values[1];
258 point.z = values[2];
259 point.normalX = values[3];
260 point.normalY = values[4];
261 point.normalZ = values[5];
262 point.yaw = values[6];
263 point.scaleX = values[7];
264 point.scaleY = values[8];
265 point.scaleZ = values[9];
266 point.density = values[10];
267 }
268 return true;
269 } catch (const std::exception& exception) {
270 error_ = exception.what();
271 impl_->reset();
272 } catch (...) {
273 error_ = "unknown compute failure";
274 impl_->reset();
275 }
276 return false;
277}
278
279uint64_t PointCompute::getUploadCount() const { return impl_->uploadCount; }
280uint64_t PointCompute::getDispatchCount() const { return impl_->dispatchCount; }
281uint64_t PointCompute::getReadbackCount() const { return impl_->readbackCount; }
282uint64_t PointCompute::getBufferReuseCount() const { return impl_->bufferReuseCount; }
283uint64_t PointCompute::getPeakBufferBytes() const { return impl_->peakBufferBytes; }
284int PointCompute::getLastFusedTransformCount() const { return impl_->lastFusedTransformCount; }
285
286} // namespace eve::procgen
std::string output
std::map< std::string, Var > values
EvpackChunkInput input
Definition Evpack.cpp:170
std::uint32_t capacity
size_t offset
uint32_t index
glm::vec3 point
uint64_t getReadbackCount() const
Return successful GPU-to-host readback count for this executor.
bool transformChain(const PointSet &input, PointSet &output, const std::vector< Transform > &transforms)
Execute one to four transforms in one upload, dispatch and readback.
int getLastFusedTransformCount() const
Return logical transform count in the most recent successful dispatch.
uint64_t getBufferReuseCount() const
Return executions that reused existing GPU buffers.
uint64_t getDispatchCount() const
Return successful compute dispatch count for this executor.
uint64_t getPeakBufferBytes() const
Return peak bytes reserved in each reusable GPU point buffer.
bool transform(const PointSet &input, PointSet &output, float translateX, float translateY, float translateZ, float yawDegrees, float scaleX, float scaleY, float scaleZ)
Transform attributed points through the active Vulkan or WebGPU compute backend.
uint64_t getUploadCount() const
Return successful host-to-GPU upload count for this executor.
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
std::unique_ptr< eve::gpgpu::Sequence > sequence
std::unique_ptr< eve::gpgpu::ComputeShader > shader
std::unique_ptr< eve::gpgpu::GpuBuffer > storage
std::unique_ptr< eve::gpgpu::GpuBuffer > staging
One transform operation in a fused point-compute dispatch.
One deterministic sample used by script-first procedural pipelines.
Definition PointSet.h:17