UNPKG

16.8 kBJavaScriptView Raw
1import _regeneratorRuntime from "@babel/runtime/regenerator";
2import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
4import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
5import _createClass from "@babel/runtime/helpers/createClass";
6import _isNumber from "lodash/isNumber";
7import { AST_TOKEN_TYPES, isSafari, STORAGE_CLASS, createEntity } from '@antv/g-webgpu-core';
8import * as WebGPUConstants from '@webgpu/types/dist/constants';
9import isNil from 'lodash/isNil';
10import WebGPUBuffer from './WebGPUBuffer';
11
12var WebGPUComputeModel = /*#__PURE__*/function () {
13 /**
14 * 用于后续渲染时动态更新
15 */
16 function WebGPUComputeModel(engine, context) {
17 _classCallCheck(this, WebGPUComputeModel);
18
19 this.engine = engine;
20 this.context = context;
21 this.entity = createEntity();
22 this.uniformGPUBufferLayout = [];
23 this.uniformBuffer = void 0;
24 this.vertexBuffers = {};
25 this.outputBuffer = void 0;
26 this.bindGroupEntries = void 0;
27 this.bindGroup = void 0;
28 this.computePipeline = void 0;
29 }
30
31 _createClass(WebGPUComputeModel, [{
32 key: "init",
33 value: function () {
34 var _init = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
35 var _this = this;
36
37 var _yield$this$compileCo, computeStage, buffers, uniforms, bufferBindingIndex, offset, mergedUniformData;
38
39 return _regeneratorRuntime.wrap(function _callee$(_context) {
40 while (1) {
41 switch (_context.prev = _context.next) {
42 case 0:
43 _context.next = 2;
44 return this.compileComputePipelineStageDescriptor(this.context.shader);
45
46 case 2:
47 _yield$this$compileCo = _context.sent;
48 computeStage = _yield$this$compileCo.computeStage;
49 buffers = this.context.uniforms.filter(function (uniform) {
50 return uniform.storageClass === STORAGE_CLASS.StorageBuffer;
51 });
52 uniforms = this.context.uniforms.filter(function (uniform) {
53 return uniform.storageClass === STORAGE_CLASS.Uniform;
54 });
55 bufferBindingIndex = uniforms.length ? 1 : 0;
56 this.bindGroupEntries = [];
57
58 if (bufferBindingIndex) {
59 offset = 0; // FIXME: 所有 uniform 合并成一个 buffer,固定使用 Float32Array 存储,确实会造成一些内存的浪费
60 // we use std140 layout @see https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)
61
62 mergedUniformData = [];
63 uniforms.forEach(function (uniform) {
64 if (_isNumber(uniform.data)) {
65 _this.uniformGPUBufferLayout.push({
66 name: uniform.name,
67 offset: offset
68 });
69
70 offset += 4;
71 mergedUniformData.push(uniform.data);
72 } else {
73 var _uniform$data;
74
75 var originDataLength = ((_uniform$data = uniform.data) === null || _uniform$data === void 0 ? void 0 : _uniform$data.length) || 1;
76
77 if (originDataLength === 3) {
78 // vec3 -> vec4
79 // @see http://ptgmedia.pearsoncmg.com/images/9780321552624/downloads/0321552628_AppL.pdf
80 originDataLength = 4;
81 uniform.data.push(0);
82 } // 4 elements per block/line
83
84
85 var padding = offset / 4 % 4;
86
87 if (padding > 0) {
88 var space = 4 - padding;
89
90 if (originDataLength > 1 && originDataLength <= space) {
91 if (originDataLength === 2) {
92 if (space === 3) {
93 offset += 4;
94 mergedUniformData.push(0);
95 }
96
97 mergedUniformData.push.apply(mergedUniformData, _toConsumableArray(uniform.data));
98
99 _this.uniformGPUBufferLayout.push({
100 name: uniform.name,
101 offset: offset
102 });
103 }
104 } else {
105 for (var i = 0; i < space; i++) {
106 offset += 4;
107 mergedUniformData.push(0);
108 }
109
110 mergedUniformData.push.apply(mergedUniformData, _toConsumableArray(uniform.data));
111
112 _this.uniformGPUBufferLayout.push({
113 name: uniform.name,
114 offset: offset
115 });
116 }
117 }
118
119 offset += 4 * originDataLength;
120 }
121 });
122 this.uniformBuffer = new WebGPUBuffer(this.engine, {
123 // TODO: 处理 Struct 和 boolean
124 // @ts-ignore
125 data: mergedUniformData instanceof Array ? // @ts-ignore
126 new Float32Array(mergedUniformData) : mergedUniformData,
127 usage: WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst
128 });
129 this.bindGroupEntries.push({
130 binding: 0,
131 resource: {
132 buffer: this.uniformBuffer.get()
133 }
134 });
135 } // create GPUBuffers for storeage buffers
136
137
138 buffers.forEach(function (buffer) {
139 if (!isNil(buffer.data)) {
140 if (buffer.type === AST_TOKEN_TYPES.Vector4FloatArray || buffer.type === AST_TOKEN_TYPES.FloatArray) {
141 var gpuBuffer;
142
143 if (buffer.name === _this.context.output.name) {
144 gpuBuffer = new WebGPUBuffer(_this.engine, {
145 // @ts-ignore
146 data: isFinite(Number(buffer.data)) ? [buffer.data] : buffer.data,
147 usage: WebGPUConstants.BufferUsage.Storage | WebGPUConstants.BufferUsage.CopyDst | WebGPUConstants.BufferUsage.CopySrc
148 });
149 _this.outputBuffer = gpuBuffer;
150 _this.context.output = {
151 name: buffer.name,
152 // @ts-ignore
153 length: isFinite(Number(buffer.data)) ? 1 : buffer.data.length,
154 typedArrayConstructor: Float32Array,
155 gpuBuffer: gpuBuffer.get()
156 };
157 } else {
158 if (buffer.isReferer) {
159 // @ts-ignore
160 if (buffer.data.model && buffer.data.model.outputBuffer) {
161 // @ts-ignore
162 gpuBuffer = buffer.data.model.outputBuffer;
163 } else {// referred kernel haven't been executed
164 }
165 } else {
166 gpuBuffer = new WebGPUBuffer(_this.engine, {
167 // @ts-ignore
168 data: isFinite(Number(buffer.data)) ? [buffer.data] : buffer.data,
169 usage: WebGPUConstants.BufferUsage.Storage | WebGPUConstants.BufferUsage.CopyDst | WebGPUConstants.BufferUsage.CopySrc
170 });
171 }
172 }
173
174 _this.vertexBuffers[buffer.name] = gpuBuffer;
175
176 _this.bindGroupEntries.push({
177 binding: bufferBindingIndex,
178 resource: {
179 name: buffer.name,
180 refer: gpuBuffer ? undefined : buffer.data,
181 buffer: gpuBuffer ? gpuBuffer.get() : undefined
182 }
183 });
184
185 bufferBindingIndex++;
186 }
187 }
188 }); // create compute pipeline layout
189
190 this.computePipeline = this.engine.device.createComputePipeline({
191 computeStage: computeStage
192 });
193 console.log(this.bindGroupEntries);
194 this.bindGroup = this.engine.device.createBindGroup({
195 layout: this.computePipeline.getBindGroupLayout(0),
196 entries: this.bindGroupEntries
197 });
198
199 case 13:
200 case "end":
201 return _context.stop();
202 }
203 }
204 }, _callee, this);
205 }));
206
207 function init() {
208 return _init.apply(this, arguments);
209 }
210
211 return init;
212 }()
213 }, {
214 key: "destroy",
215 value: function destroy() {
216 var _this2 = this;
217
218 if (this.uniformBuffer) {
219 this.uniformBuffer.destroy();
220 }
221
222 Object.keys(this.vertexBuffers).forEach(function (bufferName) {
223 return _this2.vertexBuffers[bufferName].destroy();
224 });
225 }
226 }, {
227 key: "readData",
228 value: function () {
229 var _readData = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
230 var output, length, typedArrayConstructor, gpuBuffer, byteCount, gpuReadBuffer, encoder, queue, arraybuffer, typedArray;
231 return _regeneratorRuntime.wrap(function _callee2$(_context2) {
232 while (1) {
233 switch (_context2.prev = _context2.next) {
234 case 0:
235 output = this.context.output;
236
237 if (!output) {
238 _context2.next = 16;
239 break;
240 }
241
242 length = output.length, typedArrayConstructor = output.typedArrayConstructor, gpuBuffer = output.gpuBuffer;
243
244 if (!gpuBuffer) {
245 _context2.next = 16;
246 break;
247 }
248
249 // await gpuBuffer.mapAsync(WebGPUConstants.MapMode.Read);
250 // const arraybuffer = gpuBuffer.getMappedRange();
251 // let arraybuffer;
252 // if (isSafari) {
253 // arraybuffer = await gpuBuffer.mapReadAsync();
254 // } else {
255 byteCount = length * typedArrayConstructor.BYTES_PER_ELEMENT; // @see https://developers.google.com/web/updates/2019/08/get-started-with-gpu-compute-on-the-web
256
257 gpuReadBuffer = this.engine.device.createBuffer({
258 size: byteCount,
259 usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
260 });
261 encoder = this.engine.device.createCommandEncoder();
262 encoder.copyBufferToBuffer(gpuBuffer, 0, gpuReadBuffer, 0, byteCount);
263 queue = isSafari ? // @ts-ignore
264 this.engine.device.getQueue() : this.engine.device.defaultQueue;
265 queue.submit([encoder.finish()]);
266 _context2.next = 12;
267 return gpuReadBuffer.mapAsync(WebGPUConstants.MapMode.Read);
268
269 case 12:
270 arraybuffer = gpuReadBuffer.getMappedRange();
271 typedArray = new typedArrayConstructor(arraybuffer.slice(0));
272 gpuReadBuffer.unmap();
273 return _context2.abrupt("return", typedArray);
274
275 case 16:
276 return _context2.abrupt("return", new Float32Array());
277
278 case 17:
279 case "end":
280 return _context2.stop();
281 }
282 }
283 }, _callee2, this);
284 }));
285
286 function readData() {
287 return _readData.apply(this, arguments);
288 }
289
290 return readData;
291 }()
292 }, {
293 key: "run",
294 value: function run() {
295 if (this.engine.currentComputePass) {
296 var _this$engine$currentC;
297
298 this.engine.currentComputePass.setPipeline(this.computePipeline); // this.bindGroupEntries.forEach((entry) => {
299 // if (!entry.resource.buffer) {
300 // // get referred kernel's output
301 // const gpuBuffer = (entry.resource.refer.model as WebGPUComputeModel)
302 // .outputBuffer;
303 // this.vertexBuffers[entry.resource.name] = gpuBuffer;
304 // entry.resource.buffer = gpuBuffer.get();
305 // }
306 // });
307 // const bindGroup = this.engine.device.createBindGroup({
308 // layout: this.computePipeline.getBindGroupLayout(0),
309 // entries: this.bindGroupEntries,
310 // });
311
312 this.engine.currentComputePass.setBindGroup(0, this.bindGroup);
313
314 (_this$engine$currentC = this.engine.currentComputePass).dispatch.apply(_this$engine$currentC, _toConsumableArray(this.context.dispatch));
315 }
316 }
317 }, {
318 key: "updateBuffer",
319 value: function updateBuffer(bufferName, data) {
320 var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
321 var buffer = this.vertexBuffers[bufferName];
322
323 if (buffer) {
324 buffer.subData({
325 data: data,
326 offset: offset
327 });
328 }
329 }
330 }, {
331 key: "updateUniform",
332 value: function updateUniform(uniformName, data) {
333 var layout = this.uniformGPUBufferLayout.find(function (l) {
334 return l.name === uniformName;
335 });
336
337 if (layout) {
338 this.uniformBuffer.subData({
339 data: Number.isFinite(data) ? new Float32Array([data]) : new Float32Array(data),
340 offset: layout.offset
341 });
342 }
343 }
344 }, {
345 key: "confirmInput",
346 value: function confirmInput(model, inputName) {
347 // copy output GPUBuffer of kernel
348 var inputBuffer = this.vertexBuffers[inputName];
349 var outputBuffer = model.outputBuffer;
350
351 if (inputBuffer && outputBuffer && inputBuffer !== outputBuffer) {
352 var encoder = this.engine.device.createCommandEncoder();
353 var _context$output = model.context.output,
354 length = _context$output.length,
355 typedArrayConstructor = _context$output.typedArrayConstructor;
356 var byteCount = length * typedArrayConstructor.BYTES_PER_ELEMENT;
357 encoder.copyBufferToBuffer(outputBuffer.get(), 0, inputBuffer.get(), 0, byteCount);
358 var queue = isSafari ? // @ts-ignore
359 this.engine.device.getQueue() : this.engine.device.defaultQueue;
360 queue.submit([encoder.finish()]);
361 }
362 }
363 }, {
364 key: "compileShaderToSpirV",
365 value: function compileShaderToSpirV(source, type, shaderVersion) {
366 return this.compileRawShaderToSpirV(shaderVersion + source, type);
367 }
368 }, {
369 key: "compileRawShaderToSpirV",
370 value: function compileRawShaderToSpirV(source, type) {
371 return this.engine.glslang.compileGLSL(source, type);
372 }
373 }, {
374 key: "compileComputePipelineStageDescriptor",
375 value: function () {
376 var _compileComputePipelineStageDescriptor = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(computeCode) {
377 var computeShader, shaderVersion;
378 return _regeneratorRuntime.wrap(function _callee3$(_context3) {
379 while (1) {
380 switch (_context3.prev = _context3.next) {
381 case 0:
382 computeShader = computeCode;
383 shaderVersion = '#version 450\n';
384
385 if (this.engine.options.useWGSL) {
386 _context3.next = 6;
387 break;
388 }
389
390 _context3.next = 5;
391 return this.compileShaderToSpirV(computeCode, 'compute', shaderVersion);
392
393 case 5:
394 computeShader = _context3.sent;
395
396 case 6:
397 return _context3.abrupt("return", {
398 computeStage: {
399 module: this.engine.device.createShaderModule({
400 code: computeShader,
401 // @ts-ignore
402 isWHLSL: isSafari
403 }),
404 entryPoint: 'main'
405 }
406 });
407
408 case 7:
409 case "end":
410 return _context3.stop();
411 }
412 }
413 }, _callee3, this);
414 }));
415
416 function compileComputePipelineStageDescriptor(_x) {
417 return _compileComputePipelineStageDescriptor.apply(this, arguments);
418 }
419
420 return compileComputePipelineStageDescriptor;
421 }()
422 }]);
423
424 return WebGPUComputeModel;
425}();
426
427export { WebGPUComputeModel as default };
428//# sourceMappingURL=WebGPUComputeModel.js.map
\No newline at end of file