dcp-client 4.2.31 → 4.2.32

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.
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @file unique-timing.js
3
+ * Copyright (c) 2022, Distributive, Ltd.
4
+ * All Rights Reserved. Licensed under the terms of the MIT License.
5
+ *
6
+ * This file adds wrappers various classes/functions that may have different requirements in order to accurately time them.
7
+ * Includes:
8
+ * - timer for webGL functions
9
+ * - timer for webGPU functions
10
+ * - wrapper to webGPU and WebAssembly functions that may cause the event loop to start from
11
+ * a different thread (ie after WebAssembly compiling) to ensure our CPU timing can pick up
12
+ * and continue proper measurement.
13
+ *
14
+ * @author Ryan Saweczko, ryansaweczko@kingsds.network
15
+ * @date Aug 2022
16
+ */
17
+
18
+ /* global WebGPUWindow GPU */
19
+ // @ts-nocheck
20
+
21
+ self.wrapScriptLoading({ scriptName: 'gpu-timers' }, async function gpuTimers$fn(protectedStorage, ring2PostMessage)
22
+ {
23
+ const webGLTimer = protectedStorage.timers.webGL;
24
+ const webGPUTimer = protectedStorage.timers.webGPU;
25
+
26
+ // WebAssembly doesn't have a prototype, can't use the factory the same way. But WebAssembly's spec is finalized so we don't need to be as general as for webGPU
27
+ for (const prop of Object.keys(WebAssembly))
28
+ {
29
+ const fn = WebAssembly[prop];
30
+ WebAssembly[prop] = function timerWrapper(...args)
31
+ {
32
+ var unwrappedReturn = fn.bind(this)(...args);
33
+ if (unwrappedReturn instanceof Promise)
34
+ return new Promise((resolve, reject) => {
35
+ unwrappedReturn.then(
36
+ (res) => setImmediate(() => resolve(res)),
37
+ (rej) => setImmediate(() => reject(rej)));
38
+ });
39
+ return unwrappedReturn;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Given a class, wrap all functions in that class so.
45
+ * The wrapper will not change the function if the function returns a value.
46
+ * If the function returns a promise, wrap the return value with another promise, so that
47
+ * when the original promise resolves or rejects, setImmediate will be called to resolve the
48
+ * wrapping promise. This causes the promise resolution to spend a moment on the event loop,
49
+ * allowing timing code to know the promise resolution occurred.
50
+ */
51
+ function wrapPrototypeFunctions(GPUClass)
52
+ {
53
+ // Iterating through all things 'GPU' on global object, some may not be classes. Skip those without a prototype.
54
+ if (!self[GPUClass].prototype)
55
+ return;
56
+
57
+ for (let prop of Object.keys(self[GPUClass].prototype))
58
+ {
59
+ let originalFn;
60
+ try
61
+ {
62
+ originalFn = self[GPUClass].prototype[prop];
63
+ if (originalFn instanceof Promise)
64
+ {
65
+ originalFn.catch(() => {/* accessing properties from class constructors can be dangerous in weird ways */})
66
+ continue;
67
+ }
68
+ if (typeof originalFn !== 'function')
69
+ continue;
70
+ }
71
+ catch(e)
72
+ {
73
+ // The property can't be invoked, so must be a property (like 'name'). Don't need to wrap it.
74
+ continue;
75
+ }
76
+
77
+ // If the function returns a promise, wrap it with setImmediate. Triggers restart of CPU measurement.
78
+ self[GPUClass].prototype[prop] = function timerWrapper(...args)
79
+ {
80
+ const fn = originalFn.bind(this);
81
+ const returnValue = fn(...args);
82
+ if (returnValue instanceof Promise)
83
+ return new Promise((resolve, reject) => {
84
+ returnValue
85
+ .then((res) => setImmediate(() => resolve(res)))
86
+ .catch((rej) => setImmediate(() => reject(rej)));
87
+ });
88
+ return returnValue;
89
+ }
90
+ }
91
+ }
92
+
93
+ if (self.OffscreenCanvas && new OffscreenCanvas(1,1))
94
+ {
95
+ /**
96
+ * Wrap webGL function for a given context. The wrapper will add a time interval to the
97
+ * webGLTimer that measures the execution time of the function run.
98
+ *
99
+ * @param {obj} context - the OffscreenCanvas context for which a function needs to be wrapped
100
+ * @param {string} prop - property of the context to be wrapped
101
+ */
102
+ function timeWebGLFunction(context, prop)
103
+ {
104
+ const originalFn = context[prop].bind(context);
105
+
106
+ context[prop] = function wrappedWebGLFunction(...args)
107
+ {
108
+ let returnValue;
109
+ const interval = new protectedStorage.TimeInterval();
110
+ webGLTimer.push(interval);
111
+ try
112
+ {
113
+ returnValue = originalFn(...args);
114
+ interval.stop();
115
+ }
116
+ catch(e)
117
+ {
118
+ interval.stop();
119
+ throw e;
120
+ }
121
+ return returnValue;
122
+ }
123
+ }
124
+
125
+ /* Update all functions on the OffscreenCanvas getContext prototype to have timers */
126
+ const oldGetContext = OffscreenCanvas.prototype.getContext;
127
+ OffscreenCanvas.prototype.getContext = function(type, options)
128
+ {
129
+ const context = oldGetContext.bind(this)(type, options);
130
+ for (const key of Object.getOwnPropertyNames(context.__proto__))
131
+ if (typeof context[key] === 'function')
132
+ timeWebGLFunction(context, key);
133
+ return context;
134
+ };
135
+ }
136
+
137
+ if (!navigator.gpu)
138
+ return
139
+
140
+ // Want to use the wrapped versions of these after all gpu functions are wrapped.
141
+ const originalSubmit = GPUQueue.prototype.submit;
142
+ const originalSubmitDone = GPUQueue.prototype.onSubmittedWorkDone;
143
+
144
+ for (const key of Object.getOwnPropertyNames(self))
145
+ {
146
+ if (key.startsWith('GPU'))
147
+ wrapPrototypeFunctions(key);
148
+ }
149
+
150
+ GPUQueue.prototype.submit = function submit(...args)
151
+ {
152
+ const fn = originalSubmit.bind(this);
153
+ fn(...args);
154
+
155
+ const queueP = originalSubmitDone.bind(this)();
156
+ const interval = new protectedStorage.TimeInterval();
157
+ webGPUTimer.push({ interval, queueP });
158
+
159
+ queueP.then(() => {
160
+ interval.stop();
161
+ });
162
+ }
163
+ });
@@ -12,8 +12,10 @@ self.wrapScriptLoading({ scriptName: 'webgpu-evaluator' }, function webGpuWorker
12
12
  {
13
13
  if (typeof GPU !== 'undefined') {
14
14
  try {
15
- if (typeof self.navigator === 'undefined') {
15
+ if (typeof self.navigator === 'undefined')
16
+ {
16
17
  self.navigator = {};
18
+ protectedStorage.createdNewNavigator = true;
17
19
  }
18
20
  self.navigator.gpu = GPU;
19
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcp-client",
3
- "version": "4.2.31",
3
+ "version": "4.2.32",
4
4
  "description": "Core libraries for accessing DCP network",
5
5
  "keywords": [
6
6
  "dcp"
@@ -1,84 +0,0 @@
1
- /**
2
- * @file gpu-timers.js
3
- * Copyright (c) 2022, Distributive, Ltd.
4
- * All Rights Reserved. Licensed under the terms of the MIT License.
5
- *
6
- * This file adds wrappers for webGL and webGPU functions so we can measure their GPU time
7
- *
8
- * @author Ryan Saweczko, ryansaweczko@kingsds.network
9
- * @date July 2022
10
- */
11
-
12
- /* global WebGPUWindow GPU */
13
- // @ts-nocheck
14
-
15
- self.wrapScriptLoading({ scriptName: 'gpu-timers' }, function gpuTimers$fn(protectedStorage, ring2PostMessage)
16
- {
17
- /* Default if we don't have webGL */
18
- protectedStorage.getAndResetWebGLTimer = function defaultTimer()
19
- {
20
- return 0;
21
- }
22
-
23
- /**
24
- * @returns {boolean}
25
- */
26
- protectedStorage.hasWebglSupport = function webglSupport() {
27
- try
28
- {
29
- const canvas = new OffscreenCanvas(1,1);
30
- return Boolean(canvas.getContext('webgl') || canvas.getContext('webgl2'));
31
- }
32
- catch
33
- {
34
- return false;
35
- }
36
- };
37
-
38
- if (protectedStorage.hasWebglSupport())
39
- {
40
- let time = 0;
41
- function getAndResetWebGLTimer()
42
- {
43
- const tmp = time;
44
- time = 0;
45
- return tmp;
46
- }
47
- protectedStorage.getAndResetWebGLTimer = getAndResetWebGLTimer;
48
-
49
- /* Factory to wrap a function from a context with a timer */
50
- function timeWebGLFactory(context, prop)
51
- {
52
- let originalFn = context[prop].bind(context);
53
-
54
- context[prop] = function wrappedWebGLFunction(...args)
55
- {
56
- var returnValue;
57
- const start = performance.now();
58
- try
59
- {
60
- returnValue = originalFn(...args);
61
- time += performance.now() - start;
62
- }
63
- catch(e)
64
- {
65
- time += performance.now() - start;
66
- throw e;
67
- }
68
- return returnValue;
69
- }
70
- }
71
-
72
- /* Update all functions on the OffscreenCanvas getContext prototype to have timers */
73
- let oldGetContext = OffscreenCanvas.prototype.getContext;
74
- OffscreenCanvas.prototype.getContext = function(type, options)
75
- {
76
- let context = oldGetContext.bind(this)(type, options);
77
- for (let key of Object.getOwnPropertyNames(context.__proto__))
78
- if (typeof context[key] === 'function')
79
- timeWebGLFactory(context, key);
80
- return context;
81
- };
82
- }
83
-
84
- });