UNPKG

16.3 kBJavaScriptView Raw
1import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2import _typeof from "@babel/runtime/helpers/typeof";
3import _regeneratorRuntime from "@babel/runtime/regenerator";
4import _defineProperty from "@babel/runtime/helpers/defineProperty";
5import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
6import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
7import _createClass from "@babel/runtime/helpers/createClass";
8
9function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
10
11function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
12
13import { gl, isSafari } from '@antv/g-webgpu-core';
14import * as WebGPUConstants from '@webgpu/types/dist/constants';
15import isNil from 'lodash/isNil';
16import { extractUniforms } from '../utils/uniform';
17import { getColorStateDescriptors, getCullMode, getDepthStencilStateDescriptor, primitiveMap } from './constants';
18import WebGPUBuffer from './WebGPUBuffer';
19
20// @ts-ignore
21function concatenate(resultConstructor) {
22 var totalLength = 0;
23
24 for (var _len = arguments.length, arrays = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
25 arrays[_key - 1] = arguments[_key];
26 }
27
28 for (var _i = 0, _arrays = arrays; _i < _arrays.length; _i++) {
29 var arr = _arrays[_i];
30 totalLength += arr.length;
31 }
32
33 var result = new resultConstructor(totalLength);
34 var offset = 0;
35
36 for (var _i2 = 0, _arrays2 = arrays; _i2 < _arrays2.length; _i2++) {
37 var _arr = _arrays2[_i2];
38 result.set(_arr, offset);
39 offset += _arr.length;
40 }
41
42 return result;
43}
44
45var WebGPUModel = /*#__PURE__*/function () {
46 /**
47 * 用于后续渲染时动态更新
48 */
49
50 /**
51 * vertex
52 */
53
54 /**
55 * indices's buffer
56 */
57 function WebGPUModel(engine, options) {
58 _classCallCheck(this, WebGPUModel);
59
60 this.engine = engine;
61 this.options = options;
62 this.pipelineLayout = void 0;
63 this.renderPipeline = void 0;
64 this.uniformsBindGroupLayout = void 0;
65 this.uniformBindGroup = void 0;
66 this.uniformBuffer = void 0;
67 this.uniforms = {};
68 this.uniformGPUBufferLayout = [];
69 this.attributeCache = {};
70 this.indexBuffer = void 0;
71 this.indexCount = void 0;
72 }
73
74 _createClass(WebGPUModel, [{
75 key: "init",
76 value: function () {
77 var _init = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
78 var _this = this;
79
80 var _this$options, vs, fs, attributes, uniforms, primitive, count, elements, depth, blend, stencil, cull, instances, _yield$this$compilePi, vertexStage, fragmentStage, vertexState, descriptor;
81
82 return _regeneratorRuntime.wrap(function _callee$(_context) {
83 while (1) {
84 switch (_context.prev = _context.next) {
85 case 0:
86 _this$options = this.options, vs = _this$options.vs, fs = _this$options.fs, attributes = _this$options.attributes, uniforms = _this$options.uniforms, primitive = _this$options.primitive, count = _this$options.count, elements = _this$options.elements, depth = _this$options.depth, blend = _this$options.blend, stencil = _this$options.stencil, cull = _this$options.cull, instances = _this$options.instances; // build shaders first
87
88 _context.next = 3;
89 return this.compilePipelineStageDescriptor(vs, fs, null);
90
91 case 3:
92 _yield$this$compilePi = _context.sent;
93 vertexStage = _yield$this$compilePi.vertexStage;
94 fragmentStage = _yield$this$compilePi.fragmentStage;
95
96 if (uniforms) {
97 // create uniform bind groups & layout
98 this.buildUniformBindGroup(uniforms);
99 }
100
101 if (elements) {
102 this.indexBuffer = elements.get();
103 this.indexCount = elements.indexCount;
104 } // TODO: instanced array
105
106
107 vertexState = {
108 vertexBuffers: Object.keys(attributes).map(function (attributeName, i) {
109 var attribute = attributes[attributeName];
110
111 var _attribute$get = attribute.get(),
112 arrayStride = _attribute$get.arrayStride,
113 stepMode = _attribute$get.stepMode,
114 ats = _attribute$get.attributes;
115
116 _this.attributeCache[attributeName] = attribute;
117 return {
118 arrayStride: arrayStride,
119 stepMode: stepMode,
120 attributes: ats
121 };
122 })
123 };
124 descriptor = {
125 sampleCount: this.engine.mainPassSampleCount,
126 primitiveTopology: primitiveMap[primitive || gl.TRIANGLES],
127 rasterizationState: _objectSpread(_objectSpread({}, this.getDefaultRasterizationStateDescriptor()), {}, {
128 // TODO: support frontface
129 cullMode: getCullMode({
130 cull: cull
131 })
132 }),
133 depthStencilState: getDepthStencilStateDescriptor({
134 depth: depth,
135 stencil: stencil
136 }),
137 colorStates: getColorStateDescriptors({
138 blend: blend
139 }, this.engine.options.swapChainFormat),
140 layout: this.pipelineLayout,
141 vertexStage: vertexStage,
142 fragmentStage: fragmentStage,
143 vertexState: vertexState
144 }; // create pipeline
145
146 this.renderPipeline = this.engine.device.createRenderPipeline(descriptor);
147
148 case 11:
149 case "end":
150 return _context.stop();
151 }
152 }
153 }, _callee, this);
154 }));
155
156 function init() {
157 return _init.apply(this, arguments);
158 }
159
160 return init;
161 }()
162 }, {
163 key: "addUniforms",
164 value: function addUniforms(uniforms) {
165 this.uniforms = _objectSpread(_objectSpread({}, this.uniforms), extractUniforms(uniforms));
166 }
167 }, {
168 key: "draw",
169 value: function draw(options) {
170 var _this2 = this;
171
172 var renderPass = this.engine.getCurrentRenderPass();
173
174 var uniforms = _objectSpread(_objectSpread({}, this.uniforms), extractUniforms(options.uniforms || {}));
175
176 var bindGroupBindings = []; // TODO: uniform 发生修改
177
178 Object.keys(uniforms).forEach(function (uniformName) {
179 var type = _typeof(uniforms[uniformName]);
180
181 if (type === 'boolean' || type === 'number' || Array.isArray(uniforms[uniformName]) || // @ts-ignore
182 uniforms[uniformName].BYTES_PER_ELEMENT) {
183 var _this2$uniformGPUBuff;
184
185 var offset = (_this2$uniformGPUBuff = _this2.uniformGPUBufferLayout.find(function (_ref) {
186 var name = _ref.name;
187 return name === uniformName;
188 })) === null || _this2$uniformGPUBuff === void 0 ? void 0 : _this2$uniformGPUBuff.offset;
189
190 if (!isNil(offset)) {
191 _this2.uniformBuffer.subData({
192 data: uniforms[uniformName],
193 offset: offset
194 });
195 }
196 } else {
197 var _this2$uniformGPUBuff2;
198
199 var _offset = (_this2$uniformGPUBuff2 = _this2.uniformGPUBufferLayout.find(function (_ref2) {
200 var name = _ref2.name;
201 return name === uniformName;
202 })) === null || _this2$uniformGPUBuff2 === void 0 ? void 0 : _this2$uniformGPUBuff2.offset;
203
204 if (!isNil(_offset)) {
205 var textureOrFramebuffer = uniforms[uniformName].get();
206
207 var _ref3 = textureOrFramebuffer.color || textureOrFramebuffer,
208 texture = _ref3.texture,
209 sampler = _ref3.sampler;
210
211 if (sampler) {
212 bindGroupBindings.push({
213 binding: _offset,
214 resource: sampler
215 });
216 _offset++;
217 }
218
219 bindGroupBindings.push({
220 binding: _offset,
221 resource: texture.createView()
222 });
223 }
224 }
225 });
226
227 if (this.uniformBuffer) {
228 bindGroupBindings[0] = {
229 binding: 0,
230 resource: {
231 buffer: this.uniformBuffer.get() // 返回 GPUBuffer 原生对象
232
233 }
234 };
235 }
236
237 this.uniformBindGroup = this.engine.device.createBindGroup({
238 layout: this.uniformsBindGroupLayout,
239 entries: bindGroupBindings
240 });
241
242 if (this.renderPipeline) {
243 renderPass.setPipeline(this.renderPipeline);
244 }
245
246 renderPass.setBindGroup(0, this.uniformBindGroup);
247
248 if (this.indexBuffer) {
249 renderPass.setIndexBuffer(this.indexBuffer.get(), WebGPUConstants.IndexFormat.Uint32, 0);
250 }
251
252 Object.keys(this.attributeCache).forEach(function (attributeName, i) {
253 renderPass.setVertexBuffer(0 + i, _this2.attributeCache[attributeName].get().buffer, 0);
254 }); // renderPass.draw(verticesCount, instancesCount, verticesStart, 0);
255
256 if (this.indexBuffer) {
257 renderPass.drawIndexed(this.indexCount, this.options.instances || 1, 0, 0, 0);
258 } else {
259 renderPass.draw(this.options.count || 0, this.options.instances || 0, 0, 0);
260 }
261 }
262 }, {
263 key: "destroy",
264 value: function destroy() {
265 throw new Error('Method not implemented.');
266 }
267 }, {
268 key: "compilePipelineStageDescriptor",
269 value: function () {
270 var _compilePipelineStageDescriptor = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(vertexCode, fragmentCode, defines) {
271 var shaderVersion, vertexShader, fragmentShader;
272 return _regeneratorRuntime.wrap(function _callee2$(_context2) {
273 while (1) {
274 switch (_context2.prev = _context2.next) {
275 case 0:
276 shaderVersion = '#version 450\n';
277 vertexShader = vertexCode;
278 fragmentShader = fragmentCode;
279
280 if (this.engine.options.useWGSL) {
281 _context2.next = 10;
282 break;
283 }
284
285 _context2.next = 6;
286 return this.compileShaderToSpirV(vertexCode, 'vertex', shaderVersion);
287
288 case 6:
289 vertexShader = _context2.sent;
290 _context2.next = 9;
291 return this.compileShaderToSpirV(fragmentCode, 'fragment', shaderVersion);
292
293 case 9:
294 fragmentShader = _context2.sent;
295
296 case 10:
297 return _context2.abrupt("return", this.createPipelineStageDescriptor(vertexShader, fragmentShader));
298
299 case 11:
300 case "end":
301 return _context2.stop();
302 }
303 }
304 }, _callee2, this);
305 }));
306
307 function compilePipelineStageDescriptor(_x, _x2, _x3) {
308 return _compilePipelineStageDescriptor.apply(this, arguments);
309 }
310
311 return compilePipelineStageDescriptor;
312 }()
313 }, {
314 key: "compileShaderToSpirV",
315 value: function compileShaderToSpirV(source, type, shaderVersion) {
316 return this.compileRawShaderToSpirV(shaderVersion + source, type);
317 }
318 }, {
319 key: "compileRawShaderToSpirV",
320 value: function compileRawShaderToSpirV(source, type) {
321 return this.engine.glslang.compileGLSL(source, type);
322 }
323 }, {
324 key: "createPipelineStageDescriptor",
325 value: function createPipelineStageDescriptor(vertexShader, fragmentShader) {
326 return {
327 vertexStage: {
328 module: this.engine.device.createShaderModule({
329 code: vertexShader,
330 // @ts-ignore
331 isWHLSL: isSafari
332 }),
333 entryPoint: 'main'
334 },
335 fragmentStage: {
336 module: this.engine.device.createShaderModule({
337 code: fragmentShader,
338 // @ts-ignore
339 isWHLSL: isSafari
340 }),
341 entryPoint: 'main'
342 }
343 };
344 }
345 /**
346 * @see https://gpuweb.github.io/gpuweb/#rasterization-state
347 */
348
349 }, {
350 key: "getDefaultRasterizationStateDescriptor",
351 value: function getDefaultRasterizationStateDescriptor() {
352 return {
353 frontFace: WebGPUConstants.FrontFace.CCW,
354 cullMode: WebGPUConstants.CullMode.None,
355 depthBias: 0,
356 depthBiasSlopeScale: 0,
357 depthBiasClamp: 0
358 };
359 }
360 }, {
361 key: "buildUniformBindGroup",
362 value: function buildUniformBindGroup(uniforms) {
363 var _this3 = this;
364
365 var offset = 0; // FIXME: 所有 uniform 合并成一个 buffer,固定使用 Float32Array 存储,确实会造成一些内存的浪费
366
367 var mergedUniformData = concatenate.apply(void 0, [Float32Array].concat(_toConsumableArray(Object.keys(uniforms).map(function (uniformName) {
368 if (uniforms[uniformName]) {
369 _this3.uniformGPUBufferLayout.push({
370 name: uniformName,
371 offset: offset
372 }); // @ts-ignore
373
374
375 offset += (uniforms[uniformName].length || 1) * 4;
376 return uniforms[uniformName];
377 } else {
378 // texture & framebuffer
379 return [];
380 }
381 }))));
382 var entries = [];
383 var hasUniform = false;
384
385 if (mergedUniformData.length) {
386 hasUniform = true; // TODO: 所有 uniform 绑定到 slot 0,通过解析 Shader 代码判定可见性
387
388 entries.push({
389 // TODO: 暂时都绑定到 slot 0
390 binding: 0,
391 visibility: WebGPUConstants.ShaderStage.Fragment | WebGPUConstants.ShaderStage.Vertex,
392 // TODO: 暂时 VS 和 FS 都可见
393 type: WebGPUConstants.BindingType.UniformBuffer
394 });
395 } // 声明 texture & sampler
396
397
398 Object.keys(uniforms).filter(function (uniformName) {
399 return uniforms[uniformName] === null;
400 }).forEach(function (uniformName, i) {
401 _this3.uniformGPUBufferLayout.push({
402 name: uniformName,
403 offset: i * 2 + (hasUniform ? 1 : 0)
404 });
405
406 entries.push({
407 // Sampler
408 binding: i * 2 + (hasUniform ? 1 : 0),
409 visibility: WebGPUConstants.ShaderStage.Fragment,
410 type: WebGPUConstants.BindingType.Sampler
411 }, {
412 // Texture view
413 binding: i * 2 + (hasUniform ? 1 : 0) + 1,
414 visibility: WebGPUConstants.ShaderStage.Fragment,
415 type: WebGPUConstants.BindingType.SampledTexture
416 });
417 });
418 this.uniformsBindGroupLayout = this.engine.device.createBindGroupLayout({
419 // 最新 API 0.0.22 版本使用 entries。Chrome Canary 84.0.4110.0 已实现。
420 // 使用 bindings 会报 Warning: GPUBindGroupLayoutDescriptor.bindings is deprecated: renamed to entries
421 // @see https://github.com/antvis/GWebGPUEngine/issues/5
422 entries: entries
423 });
424 this.pipelineLayout = this.engine.device.createPipelineLayout({
425 bindGroupLayouts: [this.uniformsBindGroupLayout]
426 });
427
428 if (hasUniform) {
429 this.uniformBuffer = new WebGPUBuffer(this.engine, {
430 // TODO: 处理 Struct 和 boolean
431 // @ts-ignore
432 data: mergedUniformData instanceof Array ? // @ts-ignore
433 new Float32Array(mergedUniformData) : mergedUniformData,
434 usage: WebGPUConstants.BufferUsage.Uniform | WebGPUConstants.BufferUsage.CopyDst
435 });
436 }
437 }
438 }]);
439
440 return WebGPUModel;
441}();
442
443export { WebGPUModel as default };
444//# sourceMappingURL=WebGPUModel.js.map
\No newline at end of file