webgpu 0.0.1.dev0__py3-none-any.whl

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.
webgpu/link/link.js ADDED
@@ -0,0 +1,431 @@
1
+ /* eslint-disable */
2
+
3
+ function serializeEvent(event) {
4
+ event.preventDefault();
5
+ const keys = [
6
+ 'button',
7
+ 'altKey',
8
+ 'metaKey',
9
+ 'ctrlKey',
10
+ 'shiftKey',
11
+ 'x',
12
+ 'y',
13
+ 'deltaX',
14
+ 'deltaY',
15
+ 'deltaMode',
16
+ 'movementX',
17
+ 'movementY',
18
+ ];
19
+ return Object.fromEntries(keys.map((k) => [k, event[k]]));
20
+ }
21
+
22
+ function decodeB64(data) {
23
+ return Uint8Array.from(atob(data), (c) => c.charCodeAt(0)).buffer;
24
+ }
25
+
26
+ function encodeB64(buffer) {
27
+ return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));
28
+ }
29
+
30
+ function isPrimitive(value) {
31
+ return (
32
+ value === null || (typeof value !== 'object' && typeof value !== 'function')
33
+ );
34
+ }
35
+
36
+ function isPrimitiveDict(obj) {
37
+ if (obj.$children) return false;
38
+ if (obj === window) return false;
39
+ if (obj === navigator) return false;
40
+
41
+ if (typeof obj !== 'object' || obj === null) return false;
42
+ if (Array.isArray(obj)) {
43
+ return obj.every(isPrimitiveDict);
44
+ }
45
+
46
+ for (let key in obj) {
47
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
48
+ const value = obj[key];
49
+
50
+ if (!isPrimitive(value) && !isPrimitiveDict(value)) {
51
+ return false;
52
+ }
53
+ }
54
+ }
55
+
56
+ if (obj.constructor !== Object) return false;
57
+
58
+ return true;
59
+ }
60
+
61
+ function createProxy(link, id, parent_id) {
62
+ const target = function (...args) {
63
+ return link.call(id, args, parent_id);
64
+ };
65
+ target.handleEvent = async (eventType, event) => {
66
+ return link.call(id, [eventType, event], parent_id, '_handle');
67
+ };
68
+ target.callFunction = (self, arg) => {
69
+ return link.call(id, [arg], parent_id);
70
+ };
71
+ target.callMethod = async (prop, arg) => {
72
+ return await link.callMethod(id, prop, [arg]);
73
+ };
74
+ target.callMethodIgnoreResult = (prop, arg) => {
75
+ link.callMethodIgnoreResult(id, prop, [arg]);
76
+ };
77
+ const handler = {
78
+ get: function (obj, prop) {
79
+ if (prop === '_parent_id') return parent_id;
80
+ if (prop === '_id') return id;
81
+ if (prop === '_link') return link;
82
+ if (prop === 'handleEvent') return target.handleEvent;
83
+ if (prop === 'call') {
84
+ return target.callFunction;
85
+ }
86
+ if (prop === 'then') return Reflect.get(...arguments);
87
+ if (prop.startsWith('__')) return Reflect.get(...arguments);
88
+ if (prop === 'callMethodIgnoreResult') return Reflect.get(...arguments);
89
+ if (prop === 'callMethod') return Reflect.get(...arguments);
90
+
91
+ return link.getProp(id, prop);
92
+ },
93
+ set: function (obj, prop, value) {
94
+ link.setProp(id, prop, value);
95
+ },
96
+
97
+ apply: function (obj, thisArg, args) {
98
+ return link.call(id, args, parent_id);
99
+ },
100
+ };
101
+ return new Proxy(target, handler);
102
+ }
103
+
104
+ class CrossLink {
105
+ constructor(connection) {
106
+ this.requestCounter = 1;
107
+ this.requests = {};
108
+
109
+ this.counter = 1;
110
+ this.objects = {};
111
+
112
+ this.connection = connection;
113
+ this.connection.onMessage((data) => this.onMessage(data));
114
+ this.connected = new Promise((resolve) => {
115
+ this.connection.onOpen(() => {
116
+ console.log('connection open');
117
+ resolve();
118
+ });
119
+ });
120
+ }
121
+
122
+ async _sendRequestAwaitResponse(data) {
123
+ //console.log('sending request', data);
124
+ const request_id = this.requestCounter++;
125
+ data.request_id = request_id;
126
+ try {
127
+ const result = await new Promise((resolve, reject) => {
128
+ this.requests[request_id] = resolve;
129
+ this.connection.send(JSON.stringify(data));
130
+ setTimeout(() => {
131
+ reject(
132
+ new Error(
133
+ `Timeout, request ${request_id}, data: ${JSON.stringify(data)}`
134
+ )
135
+ );
136
+ }, 10000);
137
+ });
138
+ // const t = Date.now() - requestData.sent;
139
+ return result;
140
+ } finally {
141
+ delete this.requests[request_id];
142
+ }
143
+ }
144
+
145
+ async getProp(id, prop) {
146
+ return await this._sendRequestAwaitResponse({ type: 'get', id, prop });
147
+ }
148
+
149
+ async getItem(id, key) {
150
+ return await this._sendRequestAwaitResponse({ type: 'get', id, key });
151
+ }
152
+
153
+ async setProp(id, prop, value) {
154
+ this.connection.send({
155
+ type: 'set',
156
+ id,
157
+ prop,
158
+ value: await this._dumpData(value),
159
+ });
160
+ }
161
+
162
+ async setItem(id, key, value) {
163
+ this.connection.send({
164
+ type: 'set',
165
+ id,
166
+ key,
167
+ value: await this._dumpData(value),
168
+ });
169
+ }
170
+
171
+ async call(id, args = [], parent_id = undefined, prop = undefined) {
172
+ return await this._sendRequestAwaitResponse({
173
+ type: 'call',
174
+ id,
175
+ parent_id,
176
+ args: await this._dumpData(args),
177
+ prop,
178
+ });
179
+ }
180
+
181
+ async callMethod(id, prop, args = []) {
182
+ return await this._sendRequestAwaitResponse({
183
+ type: 'call',
184
+ id,
185
+ args: await this._dumpData(args),
186
+ prop,
187
+ });
188
+ }
189
+
190
+ async callMethodIgnoreResult(id, prop, args = []) {
191
+ return await this.connection.send(
192
+ JSON.stringify({
193
+ type: 'call',
194
+ id,
195
+ args: await this._dumpData(args),
196
+ prop,
197
+ })
198
+ );
199
+ }
200
+
201
+ expose(name, obj) {
202
+ this.objects[name] = obj;
203
+ }
204
+
205
+ async _dumpData(data) {
206
+ //console.log("dumping data", data);
207
+ if (data === null) return undefined;
208
+
209
+ if (isPrimitive(data)) return data;
210
+
211
+ if (data instanceof MouseEvent) return serializeEvent(data);
212
+ if (data instanceof Event) return serializeEvent(data);
213
+ if (data instanceof InputEvent) return serializeEvent(data);
214
+
215
+ if (data instanceof ArrayBuffer)
216
+ return {
217
+ __is_crosslink_type__: true,
218
+ type: 'bytes',
219
+ value: encodeB64(data),
220
+ };
221
+
222
+ if (data.constructor === Array) {
223
+ const result = [];
224
+ for (let item of data) result.push(await this._dumpData(item));
225
+ return result;
226
+ }
227
+
228
+ if (data.__is_crosslink_type__) return data;
229
+
230
+ if (isPrimitiveDict(data)) {
231
+ return data;
232
+ }
233
+ if (data.constructor === Object) {
234
+ const result = {};
235
+ Object.keys(data).map(async (key) => {
236
+ result[key] = await this._dumpData(data[key]);
237
+ });
238
+ return result;
239
+ }
240
+
241
+ // complex type - store it in objects only send its id
242
+ const id = this.counter++;
243
+ this.objects[id] = data;
244
+ return {
245
+ __is_crosslink_type__: true,
246
+ type: 'proxy',
247
+ id,
248
+ };
249
+ }
250
+
251
+ _loadValue(value) {
252
+ if (value === null || value === undefined) return undefined;
253
+ if (!value.__is_crosslink_type__) return value;
254
+
255
+ if (value.type == 'bytes') return decodeB64(value.value);
256
+ if (value.type == 'object') return this.objects[value.id];
257
+ if (value.type == 'proxy')
258
+ return createProxy(this, value.id, value.parent_id);
259
+
260
+ console.error('Cannot load value, unknown value type:', value);
261
+ }
262
+
263
+ _loadData(data) {
264
+ if (
265
+ data === undefined ||
266
+ data === null ||
267
+ typeof data !== 'object' ||
268
+ data.__is_crosslink_type__
269
+ )
270
+ return this._loadValue(data);
271
+
272
+ Object.keys(data).map((key) => {
273
+ data[key] = this._loadData(data[key]);
274
+ });
275
+ return data;
276
+ }
277
+
278
+ async sendResponse(data, request_id, parent_id) {
279
+ if (request_id === undefined) {
280
+ return;
281
+ }
282
+
283
+ const value = await this._dumpData(await Promise.resolve(data));
284
+ //console.log('encoded data', value);
285
+
286
+ this.connection.send(
287
+ JSON.stringify({
288
+ type: 'response',
289
+ request_id,
290
+ value,
291
+ })
292
+ );
293
+ }
294
+
295
+ async onMessage(event) {
296
+ const data = JSON.parse(event.data);
297
+ const request_id = data.request_id;
298
+
299
+ let obj = data.id ? this.objects[data.id] : window;
300
+ // console.log('onMessage', data, obj);
301
+
302
+ let response = null;
303
+
304
+ switch (data.type) {
305
+ case 'call':
306
+ const args = this._loadData(data.args);
307
+ let self = null;
308
+ if (data.prop) {
309
+ self = this.objects[data.id];
310
+ obj = self[data.prop];
311
+ } else {
312
+ self = this.objects[data.parent_id];
313
+ }
314
+
315
+ // console.log('calling', 'data', data, 'obj', obj, 'self', self, 'args', args);
316
+
317
+ response = obj.apply(self, args);
318
+ break;
319
+ case 'get_keys':
320
+ response = Object.keys(obj);
321
+ break;
322
+ case 'get':
323
+ if (data.prop) response = obj[data.prop];
324
+ else if (data.key) response = obj[data.key];
325
+ else response = obj;
326
+ if (response && (data.prop || data.key)) {
327
+ response = await this._dumpData(response);
328
+ if (typeof response === 'object') response.parent_id = data.id;
329
+ }
330
+ break;
331
+ case 'set':
332
+ const value = this._loadData(data.value);
333
+ if (data.prop) obj[data.prop] = value;
334
+ if (data.key) obj[data.key] = value;
335
+ break;
336
+ case 'delete':
337
+ this.objects[data.id] = undefined;
338
+ break;
339
+ case 'response':
340
+ this.requests[request_id](this._loadData(data.value));
341
+ return;
342
+ default:
343
+ console.error('Unknown message type:', data, data.type);
344
+ }
345
+
346
+ if (request_id !== undefined && data.type !== 'response') {
347
+ response = await response;
348
+ this.sendResponse(response, request_id);
349
+ }
350
+ }
351
+ }
352
+
353
+ export function WebsocketLink(url) {
354
+ const socket = new WebSocket(url);
355
+ return new CrossLink({
356
+ send: (data) => socket.send(data),
357
+ onMessage: (callback) => (socket.onmessage = callback),
358
+ onOpen: (callback) => (socket.onopen = callback),
359
+ });
360
+ }
361
+
362
+ export function WebworkerLink(worker) {
363
+ // wait for first message, which means the worker has loaded and is ready
364
+ const workerReady = new Promise((resolve) => {
365
+ worker.addEventListener(
366
+ 'message',
367
+ (event) => {
368
+ resolve();
369
+ },
370
+ { once: true }
371
+ );
372
+ });
373
+ return new CrossLink({
374
+ send: (data) => worker.postMessage(data),
375
+ onMessage: async (callback) => {
376
+ await workerReady;
377
+ worker.addEventListener('message', callback);
378
+ },
379
+ onOpen: async (callback) => {
380
+ await workerReady;
381
+ callback();
382
+ },
383
+ });
384
+ }
385
+
386
+ window.createLilGUI = async (args) => {
387
+ if (window.lil === undefined) {
388
+ const url = 'https://cdn.jsdelivr.net/npm/lil-gui@0.20';
389
+ if (window.define === undefined) {
390
+ await import(url);
391
+ } else {
392
+ await new Promise(async (resolve) => {
393
+ require([url], (module) => {
394
+ window.lil = module;
395
+ resolve();
396
+ });
397
+ });
398
+ }
399
+ }
400
+ return new window.lil.GUI(args);
401
+ };
402
+
403
+ window.importPackage=async (url) => {
404
+ if (window.define === undefined) {
405
+ await import(url);
406
+ } else {
407
+ await new Promise(async (resolve) => {
408
+ require([url], (module) => {
409
+ resolve(module);
410
+ });
411
+ });
412
+ }
413
+ }
414
+
415
+ window.patchedRequestAnimationFrame = (device, context, target) => {
416
+ // context.getCurrentTexture() is only guaranteed to be valid during the requestAnimationFrame callback
417
+ // Thus, in order to render from python asynchroniously, we are always rendering into a separate texture
418
+ // The actual callback here only copies the rendered image from the separate render target texture to the current texture
419
+ requestAnimationFrame((t) => {
420
+ const current = context.getCurrentTexture();
421
+ const encoder = device.createCommandEncoder();
422
+
423
+ encoder.copyTextureToTexture(
424
+ { texture: target },
425
+ { texture: current },
426
+ { width: current.width, height: current.height }
427
+ );
428
+
429
+ device.queue.submit([encoder.finish()]);
430
+ });
431
+ };
webgpu/link/proxy.py ADDED
@@ -0,0 +1,81 @@
1
+ from .base import LinkBase
2
+
3
+
4
+ class _ProxyIterator:
5
+ def __init__(self, proxy):
6
+ self._proxy = proxy
7
+ self._keys = proxy._get_keys()
8
+ self._index = 0
9
+
10
+ def __next__(self):
11
+ if self._index < len(self._keys):
12
+ key = self._keys[self._index]
13
+ self._index += 1
14
+ return key
15
+ else:
16
+ raise StopIteration
17
+
18
+ def __iter__(self):
19
+ return self
20
+
21
+
22
+ class Proxy:
23
+ _link: LinkBase
24
+ _parent_id: int | None
25
+ _id: int | None
26
+
27
+ def __init__(self, link, parent_id=None, id=None):
28
+ self._link = link
29
+ self._id = id
30
+ self._parent_id = parent_id
31
+
32
+ def __getitem__(self, key):
33
+ return self.__getattr__(key)
34
+
35
+ def __getattr__(self, key):
36
+ if (
37
+ key
38
+ in [
39
+ "_id",
40
+ "_parent_id",
41
+ "_link",
42
+ "_call",
43
+ "_call_method_ignore_return",
44
+ "_call_method",
45
+ ]
46
+ or isinstance(key, str)
47
+ and key.startswith("__")
48
+ ):
49
+ return super().__getattr__(key)
50
+ return self._link.get(self._id, key)
51
+
52
+ def __setattr__(self, key, value):
53
+ if key in ["_id", "_parent_id", "_link"]:
54
+ return super().__setattr__(key, value)
55
+
56
+ return self._link.set(self._id, key, value)
57
+
58
+ def __call__(self, *args, _ignore_result=False):
59
+ return self._link.call(self._id, list(args), self._parent_id, _ignore_result)
60
+
61
+ def _call_method(self, prop, args=[], ignore_result=False):
62
+ return self._link.call_method(self._id, prop, args, ignore_result)
63
+
64
+ def _call_method_ignore_return(self, prop, args=[]):
65
+ return self._link.call_method_ignore_return(self._id, prop, args)
66
+
67
+ def _new(self, *args):
68
+ return self._link.call_new(self._id, args=list(args))
69
+
70
+ def _to_js(self):
71
+ return {
72
+ "type": "proxy",
73
+ "id": self._id,
74
+ "parent_id": self._parent_id,
75
+ }
76
+
77
+ def _get_keys(self):
78
+ return self._link.get_keys(self._id)
79
+
80
+ def __iter__(self):
81
+ return _ProxyIterator(self)
@@ -0,0 +1,115 @@
1
+ import asyncio
2
+ import json
3
+ import threading
4
+
5
+ import websockets
6
+ import websockets.asyncio.client
7
+
8
+ from .base import LinkBaseAsync
9
+
10
+
11
+ class WebsocketLinkBase(LinkBaseAsync):
12
+ _websocket_thread: threading.Thread
13
+ _connection: websockets.asyncio.client.ClientConnection
14
+ _event_is_connected: threading.Event
15
+ _start_handling_messages: threading.Event
16
+
17
+ def __init__(self):
18
+ super().__init__()
19
+ self._event_is_connected = threading.Event()
20
+ self._start_handling_messages = threading.Event()
21
+ self._send_loop = asyncio.new_event_loop()
22
+
23
+ self._websocket_thread = threading.Thread(target=self._connect)
24
+ self._websocket_thread.start()
25
+
26
+ def wait_for_connection(self):
27
+ self._event_is_connected.wait()
28
+
29
+ async def _send_async(self, message):
30
+ if self._connection:
31
+ await self._connection.send(message)
32
+ else:
33
+ raise Exception("Websocket not connected")
34
+
35
+ def _connect(self):
36
+ raise NotImplementedError
37
+
38
+
39
+ class WebsocketLinkClient(WebsocketLinkBase):
40
+ _url: str
41
+
42
+ def __init__(self, url):
43
+ super().__init__()
44
+ self._url = url
45
+
46
+ def _connect(self):
47
+ async def start_websocket():
48
+ async for websocket in websockets.connect(self._url):
49
+ try:
50
+ # print("client connected")
51
+ self._connection = websocket
52
+ self._event_is_connected.set()
53
+ self._start_handling_messages.wait()
54
+ async for message in websocket:
55
+ self._on_message(message)
56
+ except websockets.exceptions.ConnectionClosed:
57
+ continue
58
+ except Exception:
59
+ break
60
+
61
+ try:
62
+ asyncio.set_event_loop(self._send_loop)
63
+ self._send_loop.run_until_complete(start_websocket())
64
+ except Exception:
65
+ print("closing connection")
66
+
67
+
68
+ class WebsocketLinkServer(WebsocketLinkBase):
69
+ _stop: asyncio.Future
70
+ _port: int = 0
71
+
72
+ def __init__(self):
73
+ super().__init__()
74
+ self._stop = self._send_loop.create_future()
75
+ self._port = 8700
76
+
77
+ @property
78
+ def port(self):
79
+ return self._port
80
+
81
+ async def _websocket_handler(self, websocket, path=""):
82
+ try:
83
+ # print("client connected")
84
+ self._connection = websocket
85
+ self._event_is_connected.set()
86
+ async for message in websocket:
87
+ thread = threading.Thread(target=self._on_message, args=(message,))
88
+ thread.start()
89
+ finally:
90
+ self._connection = None
91
+
92
+ def _connect(self):
93
+ async def start_websocket():
94
+ while True:
95
+ try:
96
+ async with websockets.serve(
97
+ self._websocket_handler, "", self._port, max_queue=128
98
+ ):
99
+ # print("server running on port", self._port)
100
+ await self._stop
101
+ break
102
+ except OSError as e:
103
+ self._port += 1
104
+ except Exception as e:
105
+ print("error in websocket server", e)
106
+
107
+ try:
108
+ asyncio.set_event_loop(self._send_loop)
109
+ self._send_loop.run_until_complete(start_websocket())
110
+ except Exception as e:
111
+ print("exception in _start_websocket_server", e)
112
+ print("stopped websocket")
113
+
114
+ def stop(self):
115
+ self._send_loop.call_soon_threadsafe(self._stop.set_result, None)