dcp-client 4.4.10-0 → 4.4.11-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -63,218 +63,253 @@
63
63
  * @date May 2023
64
64
  */
65
65
  self.wrapScriptLoading({ scriptName: 'lift-webgpu' }, function liftWebGPU$$fn(protectedStorage, ring0PostMessage) {
66
- if ((typeof navigator === 'undefined') || !('gpu' in navigator))
67
- return;
68
-
69
- const TimeInterval = protectedStorage.TimeInterval;
70
- const globalTrackers = protectedStorage.bigBrother.globalTrackers;
71
- const webGPUTimer = globalTrackers.webGPUIntervals;
72
-
73
- /**
74
- * Factories to create wrappers for all webGPU function (except submit & onSubmittedWorkDone) to time them when they
75
- * are run, recording the duration of the function calls on the webGPU timer.
76
- */
77
- function webGPUAsyncTimingFactory(fn)
66
+ protectedStorage.webGPUInitialization = async function webGPUInitialization()
78
67
  {
79
- return function promiseWebGPUWrapper(...args)
68
+ // dcp-native lazy-loading for webgpu.
69
+ if (typeof initWebGPU === 'function')
80
70
  {
81
- const duration = new TimeInterval();
82
- const original = fn.apply(this, args);
83
- original.finally(() => webGPUTimer.push(duration.stop()));
84
- return original;
71
+ const webgpuReady = await initWebGPU();
72
+ if (!webgpuReady)
73
+ return;
85
74
  }
86
- }
87
75
 
88
- function webGPUSyncTimingFactory(fn)
89
- {
90
- return function syncWebGPUWrapper(...args)
76
+ if ((typeof navigator === 'undefined') || !('gpu' in navigator))
77
+ return;
78
+
79
+
80
+ const submitDescriptor = Object.getOwnPropertyDescriptor(GPUQueue.prototype, 'submit');
81
+ const navigatorDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'navigator');
82
+ const GPUDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'GPU');
83
+
84
+ // Fatal: globalThis.navigator OR globalThis.GPU are non-writable/configurable. This would prevent these scripts from being able
85
+ // to block access to webgpu for jobs that do not explicitly require it - allowing jobs to bypass scheduling decisions based
86
+ // on gpu availability must crash the sandbox, may want to stop the worker as well
87
+ if (!((GPUDescriptor.writable || GPUDescriptor.configurable )
88
+ && (navigatorDescriptor.writable || navigatorDescriptor.configurable)))
91
89
  {
92
- const duration = new TimeInterval();
93
- const ret = fn.apply(this, args);
94
- webGPUTimer.push(duration.stop());
95
- return ret;
90
+ postMessage({ request: 'unrecoverable-evaluator', message: 'webgpu exists but is not wrapable' });
91
+ close();
96
92
  }
97
- }
98
93
 
99
- /**
100
- * Wrap various webGPU functions such that their usage will be tracked
101
- *
102
- */
103
- function liftWebGPUPrototype(GPUClass)
104
- {
105
- // the standard dictates these functions will return promises
106
- const promiseReturningFunctions = new Set([
107
- 'requestDevice',
108
- 'requestAdapterInfo',
109
- 'createComputePipelineAsync',
110
- 'createRenderPipelineAsync',
111
- 'mapAsync', // this would overestimate in some cases, a potential discussion
112
- 'getCompilationInfo',
113
- 'onSubmittedWorkDone',
114
- 'popErrorScope',
115
- 'requestAdapter',
116
- ]);
117
-
118
- // TODO: consider what to do with 'destroy'
119
- // while they appear to be blocking, the meat of the work happens on the gpu driver thread
120
- const blockingFunctions = new Set([
121
- // GPU
122
- 'getPreferedCanvasFormat',
123
-
124
- // GPUDevice
125
- 'createBuffer',
126
- 'createTexture',
127
- 'createSampler',
128
- 'importExternalTexture',
129
- 'createBindGroupLayout',
130
- 'createPipelineLayout',
131
- 'createBindGroup',
132
- 'createShaderModule',
133
- 'createComputePipeline',
134
- 'createRenderPipeline',
135
- 'createCommandEncoder',
136
- 'createRenderBundleEncoder',
137
- 'createQuerySet',
138
-
139
- // GPUBuffer
140
- 'getMappedRange',
141
- 'unmap',
142
-
143
- // GPUTexture
144
- 'createView',
145
-
146
- // GPUPipelineBase
147
- 'getBindGroupLayout',
148
-
149
- // GPUDebugCommandsMixin
150
- 'pushDebugGroup',
151
- 'popDebugGroup',
152
- 'insertDebugWorker',
153
-
154
- // GPUCommandEncoder
155
- 'beginRenderPass',
156
- 'beginComputePass',
157
- 'copyBufferToBuffer',
158
- 'copyBufferToTexture',
159
- 'copyTextureToBuffer',
160
- 'copyTextureToTexture',
161
- 'clearBuffer',
162
- 'writeTimestamp',
163
- 'resolveQuerySet',
164
- 'finish',
165
-
166
- // GPUBindingsCommandMixin
167
- 'setBindGroup',
168
-
169
- // GPUComputePassEncoder
170
- 'setPipeline',
171
- 'dispatchWorkgroups',
172
- 'dispatchWorkgroupsIndirect',
173
- 'end',
174
-
175
- // GPURenderPassEncoder
176
- 'setViewPort',
177
- 'setScissorRect',
178
- 'setBlendConstant',
179
- 'setStencilReference',
180
- 'beginOcclusionQuery',
181
- 'endOcclusionQuery',
182
- 'executeBundles',
183
- 'end',
184
-
185
- // GPURenderCommandsMixin
186
- 'setPipeline',
187
- 'setIndexBuffer',
188
- 'draw',
189
- 'drawIndexed',
190
- 'drawIndirect',
191
- 'drawIndexedIndirect',
192
-
193
- // GPURenderBundleEncoder
194
- 'finish',
195
-
196
- // GPUCanvasContext
197
- 'configure',
198
- 'unconfigure',
199
-
200
- // GPUQueue
201
- 'writeBuffer',
202
- 'writeTexture',
203
- 'copyExternalImageToTexture',
204
-
205
- 'pushErrorScope',
206
- ]);
207
-
208
- // Iterating through all things 'GPU' on global object, some may not be classes. Skip those without a prototype.
209
- if (!self[GPUClass].prototype)
94
+ // Non-fatal: GPUQueue.prototype.submit is non-writable/configurable. This would prevent our gpu timing code from functioning
95
+ // properly, so we cannot use webGPU for this sandbox, however by writing over the navigator.gpu and globalThis.GPU symbols,
96
+ // we can fully block webGPU access, allowing the sandbox to live for CPU-compute purposes.
97
+ if (!(submitDescriptor.writable || submitDescriptor.configurable))
98
+ {
99
+ protectedStorage.forceDisableWebGPU = true;
210
100
  return;
101
+ }
102
+
103
+ const TimeInterval = protectedStorage.TimeInterval;
104
+ const globalTrackers = protectedStorage.bigBrother.globalTrackers;
105
+ const webGPUTimer = globalTrackers.webGPUIntervals;
211
106
 
212
- for (let prop of Object.keys(self[GPUClass].prototype))
107
+ /**
108
+ * Factories to create wrappers for all webGPU function (except submit & onSubmittedWorkDone) to time them when they
109
+ * are run, recording the duration of the function calls on the webGPU timer.
110
+ */
111
+ function webGPUAsyncTimingFactory(fn)
213
112
  {
214
- if (promiseReturningFunctions.has(prop))
113
+ return function promiseWebGPUWrapper(...args)
215
114
  {
216
- const fn = self[GPUClass].prototype[prop];
217
- self[GPUClass].prototype[prop] = webGPUAsyncTimingFactory(fn);
115
+ const duration = new TimeInterval();
116
+ const original = fn.apply(this, args);
117
+ original.finally(() => webGPUTimer.push(duration.stop()));
118
+ return original;
218
119
  }
219
- else if (blockingFunctions.has(prop))
120
+ }
121
+
122
+ function webGPUSyncTimingFactory(fn)
123
+ {
124
+ return function syncWebGPUWrapper(...args)
220
125
  {
221
- const fn = self[GPUClass].prototype[prop];
222
- self[GPUClass].prototype[prop] = webGPUSyncTimingFactory(fn);
126
+ const duration = new TimeInterval();
127
+ const ret = fn.apply(this, args);
128
+ webGPUTimer.push(duration.stop());
129
+ return ret;
223
130
  }
224
131
  }
225
- }
226
132
 
227
- // Want to use the submit/onSubmittedWorkDone original functions for timing.
228
- const underlyingOnSubmittedWorkDone = GPUQueue.prototype.onSubmittedWorkDone;
229
- const underlyingSubmit = GPUQueue.prototype.submit;
230
-
231
- // some of them will get re-wrapped, that's fine, we always refer to the original function
232
- const requiredWrappingGPUClasses = [
233
- 'GPU',
234
- 'GPUAdapter',
235
- 'GPUDevice',
236
- 'GPUBuffer',
237
- 'GPUTexture',
238
- 'GPUShaderModule',
239
- 'GPUComputePipeline',
240
- 'GPURenderPipeline',
241
- 'GPUCommandEncoder',
242
- 'GPUComputePassEncoder',
243
- 'GPURenderPassEncoder',
244
- 'GPURenderBundleEncoder',
245
- 'GPUQueue',
246
- 'GPUQuerySet',
247
- 'GPUCanvasContext',
248
- ];
249
-
250
- requiredWrappingGPUClasses.forEach(liftWebGPUPrototype);
251
-
252
- let locked = false;
253
- const submittedDonePromises = [];
254
- protectedStorage.webGPU = {
255
- lock: () => { locked = true; },
256
- unlock: () => { locked = false; },
257
- waitAllCommandToFinish: () => { return Promise.allSettled(submittedDonePromises); },
258
- };
259
-
260
- // our submit keeps a global tracker of all submissions, so we can track the time of each submission
261
- GPUQueue.prototype.submit = function submit(commandBuffers)
262
- {
263
- if (locked)
264
- throw new Error('Attempted to submit webGPU queue after work function resolved');
265
- underlyingSubmit.call(this, commandBuffers);
266
-
267
- const submitTime = performance.now();
268
- const submitDonePromise = underlyingOnSubmittedWorkDone.call(this).then(() => {
269
- const idx = submittedDonePromises.indexOf(submitDonePromise);
270
- submittedDonePromises.splice(idx);
271
-
272
- const completedAt = performance.now();
273
- const duration = new TimeInterval();
274
- duration.overrideInterval(submitTime, completedAt);
275
- webGPUTimer.push(duration);
276
- });
277
- submittedDonePromises.push(submitDonePromise);
278
- }
133
+ /**
134
+ * Wrap various webGPU functions such that their usage will be tracked
135
+ *
136
+ */
137
+ function liftWebGPUPrototype(GPUClass)
138
+ {
139
+ // the standard dictates these functions will return promises
140
+ const promiseReturningFunctions = new Set([
141
+ 'requestDevice',
142
+ 'requestAdapterInfo',
143
+ 'createComputePipelineAsync',
144
+ 'createRenderPipelineAsync',
145
+ 'mapAsync', // this would overestimate in some cases, a potential discussion
146
+ 'getCompilationInfo',
147
+ 'onSubmittedWorkDone',
148
+ 'popErrorScope',
149
+ 'requestAdapter',
150
+ ]);
151
+
152
+ // TODO: consider what to do with 'destroy'
153
+ // while they appear to be blocking, the meat of the work happens on the gpu driver thread
154
+ const blockingFunctions = new Set([
155
+ // GPU
156
+ 'getPreferedCanvasFormat',
157
+
158
+ // GPUDevice
159
+ 'createBuffer',
160
+ 'createTexture',
161
+ 'createSampler',
162
+ 'importExternalTexture',
163
+ 'createBindGroupLayout',
164
+ 'createPipelineLayout',
165
+ 'createBindGroup',
166
+ 'createShaderModule',
167
+ 'createComputePipeline',
168
+ 'createRenderPipeline',
169
+ 'createCommandEncoder',
170
+ 'createRenderBundleEncoder',
171
+ 'createQuerySet',
172
+
173
+ // GPUBuffer
174
+ 'getMappedRange',
175
+ 'unmap',
176
+
177
+ // GPUTexture
178
+ 'createView',
179
+
180
+ // GPUPipelineBase
181
+ 'getBindGroupLayout',
182
+
183
+ // GPUDebugCommandsMixin
184
+ 'pushDebugGroup',
185
+ 'popDebugGroup',
186
+ 'insertDebugWorker',
279
187
 
188
+ // GPUCommandEncoder
189
+ 'beginRenderPass',
190
+ 'beginComputePass',
191
+ 'copyBufferToBuffer',
192
+ 'copyBufferToTexture',
193
+ 'copyTextureToBuffer',
194
+ 'copyTextureToTexture',
195
+ 'clearBuffer',
196
+ 'writeTimestamp',
197
+ 'resolveQuerySet',
198
+ 'finish',
199
+
200
+ // GPUBindingsCommandMixin
201
+ 'setBindGroup',
202
+
203
+ // GPUComputePassEncoder
204
+ 'setPipeline',
205
+ 'dispatchWorkgroups',
206
+ 'dispatchWorkgroupsIndirect',
207
+ 'end',
208
+
209
+ // GPURenderPassEncoder
210
+ 'setViewPort',
211
+ 'setScissorRect',
212
+ 'setBlendConstant',
213
+ 'setStencilReference',
214
+ 'beginOcclusionQuery',
215
+ 'endOcclusionQuery',
216
+ 'executeBundles',
217
+ 'end',
218
+
219
+ // GPURenderCommandsMixin
220
+ 'setPipeline',
221
+ 'setIndexBuffer',
222
+ 'draw',
223
+ 'drawIndexed',
224
+ 'drawIndirect',
225
+ 'drawIndexedIndirect',
226
+
227
+ // GPURenderBundleEncoder
228
+ 'finish',
229
+
230
+ // GPUCanvasContext
231
+ 'configure',
232
+ 'unconfigure',
233
+
234
+ // GPUQueue
235
+ 'writeBuffer',
236
+ 'writeTexture',
237
+ 'copyExternalImageToTexture',
238
+
239
+ 'pushErrorScope',
240
+ ]);
241
+
242
+ // Iterating through all things 'GPU' on global object, some may not be classes. Skip those without a prototype.
243
+ if (!self[GPUClass].prototype)
244
+ return;
245
+
246
+ for (let prop of Object.keys(self[GPUClass].prototype))
247
+ {
248
+ if (promiseReturningFunctions.has(prop))
249
+ {
250
+ const fn = self[GPUClass].prototype[prop];
251
+ self[GPUClass].prototype[prop] = webGPUAsyncTimingFactory(fn);
252
+ }
253
+ else if (blockingFunctions.has(prop))
254
+ {
255
+ const fn = self[GPUClass].prototype[prop];
256
+ self[GPUClass].prototype[prop] = webGPUSyncTimingFactory(fn);
257
+ }
258
+ }
259
+ }
260
+
261
+ // Want to use the submit/onSubmittedWorkDone original functions for timing.
262
+ const underlyingOnSubmittedWorkDone = GPUQueue.prototype.onSubmittedWorkDone;
263
+ const underlyingSubmit = GPUQueue.prototype.submit;
264
+
265
+ // some of them will get re-wrapped, that's fine, we always refer to the original function
266
+ const requiredWrappingGPUClasses = [
267
+ 'GPU',
268
+ 'GPUAdapter',
269
+ 'GPUDevice',
270
+ 'GPUBuffer',
271
+ 'GPUTexture',
272
+ 'GPUShaderModule',
273
+ 'GPUComputePipeline',
274
+ 'GPURenderPipeline',
275
+ 'GPUCommandEncoder',
276
+ 'GPUComputePassEncoder',
277
+ 'GPURenderPassEncoder',
278
+ 'GPURenderBundleEncoder',
279
+ 'GPUQueue',
280
+ 'GPUQuerySet',
281
+ 'GPUCanvasContext',
282
+ ];
283
+
284
+ requiredWrappingGPUClasses.forEach(liftWebGPUPrototype);
285
+
286
+ let locked = false;
287
+ const submittedDonePromises = [];
288
+ protectedStorage.webGPU = {
289
+ lock: () => { locked = true; },
290
+ unlock: () => { locked = false; },
291
+ waitAllCommandToFinish: () => { return Promise.allSettled(submittedDonePromises); },
292
+ };
293
+
294
+ // our submit keeps a global tracker of all submissions, so we can track the time of each submission
295
+ GPUQueue.prototype.submit = function submit(commandBuffers)
296
+ {
297
+ if (locked)
298
+ throw new Error('Attempted to submit webGPU queue after work function resolved');
299
+ underlyingSubmit.call(this, commandBuffers);
300
+
301
+ const submitTime = performance.now();
302
+ const submitDonePromise = underlyingOnSubmittedWorkDone.call(this).then(() => {
303
+ const idx = submittedDonePromises.indexOf(submitDonePromise);
304
+ submittedDonePromises.splice(idx);
305
+
306
+ const completedAt = performance.now();
307
+ const duration = new TimeInterval();
308
+ duration.overrideInterval(submitTime, completedAt);
309
+ webGPUTimer.push(duration);
310
+ });
311
+ submittedDonePromises.push(submitDonePromise);
312
+ }
313
+
314
+ }
280
315
  });
@@ -52,7 +52,7 @@ self.wrapScriptLoading({ scriptName: 'native-event-loop' }, function nativeEvent
52
52
  {
53
53
  sortTimers();
54
54
  let timer = timers.shift();
55
- let now = performance.now();
55
+ let now = Date.now();
56
56
  if (!timer)
57
57
  throw new Error('Logic error: trying to run timer when no timer exists') /* should be impossible */
58
58
  if (timer.when > now) /* should be impossible, but at least we can handle this */
@@ -65,7 +65,7 @@ self.wrapScriptLoading({ scriptName: 'native-event-loop' }, function nativeEvent
65
65
 
66
66
  if (timer.recur)
67
67
  {
68
- timer.when = performance.now() + timer.recur;
68
+ timer.when = Date.now() + timer.recur;
69
69
  timers.push(timer);
70
70
  }
71
71
 
@@ -101,7 +101,7 @@ self.wrapScriptLoading({ scriptName: 'native-event-loop' }, function nativeEvent
101
101
  timers.serial = +timers.serial + 1;
102
102
  timer = {
103
103
  fn: callback,
104
- when: performance.now() + (+timeout || 0),
104
+ when: Date.now() + (+timeout || 0),
105
105
  serial: timers.serial,
106
106
  valueOf: function () { return this.serial; }
107
107
  }
@@ -69,6 +69,12 @@ try {
69
69
  send({type: 'workerMessage', message });
70
70
  }
71
71
 
72
+ self.close = function close()
73
+ {
74
+ writeln('DIE: worker close called');
75
+ die();
76
+ }
77
+
72
78
  self.addEventListener = function workerControl$$Worker$addEventListener (type, listener) {
73
79
  if (typeof eventListeners[type] === 'undefined') { eventListeners[type] = [] }
74
80
  eventListeners[type].push(listener)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcp-client",
3
- "version": "4.4.10-0",
3
+ "version": "4.4.11-0",
4
4
  "description": "Core libraries for accessing DCP network",
5
5
  "keywords": [
6
6
  "dcp"
@@ -43,8 +43,8 @@
43
43
  "polyfill-crypto.getrandomvalues": "^1.0.0",
44
44
  "regedit": "^3.0.3",
45
45
  "semver": "^7.3.5",
46
- "webpack": "5.92.0",
47
46
  "source-map-support": "0.5.21",
47
+ "webpack": "5.92.0",
48
48
  "webpack-cli": "^4.7.2",
49
49
  "yargs": "16.2.0"
50
50
  },
@@ -52,7 +52,7 @@
52
52
  "@kingsds/eslint-config": "1.0.1",
53
53
  "eslint": "7.30.0",
54
54
  "express": "^4.18.2",
55
- "peter": "2.4.5"
55
+ "peter": "2.4.7"
56
56
  },
57
57
  "engines": {
58
58
  "node": ">=18",