wgpu 0.19.0__py3-none-win_arm64.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.
Files changed (48) hide show
  1. wgpu/__init__.py +19 -0
  2. wgpu/__pyinstaller/__init__.py +12 -0
  3. wgpu/__pyinstaller/conftest.py +1 -0
  4. wgpu/__pyinstaller/hook-wgpu.py +30 -0
  5. wgpu/__pyinstaller/test_wgpu.py +30 -0
  6. wgpu/_classes.py +2628 -0
  7. wgpu/_coreutils.py +226 -0
  8. wgpu/_diagnostics.py +524 -0
  9. wgpu/_version.py +113 -0
  10. wgpu/backends/__init__.py +37 -0
  11. wgpu/backends/auto.py +27 -0
  12. wgpu/backends/js_webgpu/__init__.py +31 -0
  13. wgpu/backends/wgpu_native/__init__.py +23 -0
  14. wgpu/backends/wgpu_native/_api.py +3646 -0
  15. wgpu/backends/wgpu_native/_ffi.py +208 -0
  16. wgpu/backends/wgpu_native/_helpers.py +513 -0
  17. wgpu/backends/wgpu_native/_mappings.py +491 -0
  18. wgpu/backends/wgpu_native/extras.py +121 -0
  19. wgpu/classes.py +8 -0
  20. wgpu/enums.py +411 -0
  21. wgpu/flags.py +70 -0
  22. wgpu/gui/__init__.py +12 -0
  23. wgpu/gui/_gui_utils.py +160 -0
  24. wgpu/gui/auto.py +191 -0
  25. wgpu/gui/base.py +389 -0
  26. wgpu/gui/glfw.py +628 -0
  27. wgpu/gui/jupyter.py +126 -0
  28. wgpu/gui/offscreen.py +99 -0
  29. wgpu/gui/qt.py +546 -0
  30. wgpu/gui/wx.py +519 -0
  31. wgpu/resources/__init__.py +1 -0
  32. wgpu/resources/codegen_report.md +40 -0
  33. wgpu/resources/webgpu.h +2091 -0
  34. wgpu/resources/webgpu.idl +1331 -0
  35. wgpu/resources/wgpu.h +301 -0
  36. wgpu/resources/wgpu_native-release.dll +0 -0
  37. wgpu/structs.py +757 -0
  38. wgpu/utils/__init__.py +21 -0
  39. wgpu/utils/compute.py +200 -0
  40. wgpu/utils/device.py +17 -0
  41. wgpu/utils/imgui/__init__.py +2 -0
  42. wgpu/utils/imgui/imgui_backend.py +501 -0
  43. wgpu/utils/imgui/imgui_renderer.py +205 -0
  44. wgpu-0.19.0.dist-info/METADATA +260 -0
  45. wgpu-0.19.0.dist-info/RECORD +48 -0
  46. wgpu-0.19.0.dist-info/WHEEL +4 -0
  47. wgpu-0.19.0.dist-info/entry_points.txt +3 -0
  48. wgpu-0.19.0.dist-info/licenses/LICENSE +25 -0
wgpu/_classes.py ADDED
@@ -0,0 +1,2628 @@
1
+ """
2
+ The classes representing the wgpu API. This module defines the classes,
3
+ properties, methods and documentation. The majority of methods are
4
+ implemented in backend modules.
5
+
6
+ This module is maintained using a combination of manual code and
7
+ automatically inserted code. Read the codegen/readme.md for more
8
+ information.
9
+ """
10
+
11
+ # Allow using class names in type annotations, before the class is defined. Py3.7+
12
+ from __future__ import annotations
13
+
14
+ import weakref
15
+ import logging
16
+ from typing import List, Dict, Union, Optional
17
+
18
+ from ._coreutils import ApiDiff, str_flag_to_int
19
+ from ._diagnostics import diagnostics, texture_format_to_bpp
20
+ from . import flags, enums, structs
21
+
22
+
23
+ __all__ = [
24
+ "GPUObjectBase",
25
+ "GPUAdapterInfo",
26
+ "GPU",
27
+ "GPUAdapter",
28
+ "GPUDevice",
29
+ "GPUBuffer",
30
+ "GPUTexture",
31
+ "GPUTextureView",
32
+ "GPUSampler",
33
+ "GPUBindGroupLayout",
34
+ "GPUBindGroup",
35
+ "GPUPipelineLayout",
36
+ "GPUShaderModule",
37
+ "GPUCompilationMessage",
38
+ "GPUCompilationInfo",
39
+ "GPUPipelineError",
40
+ "GPUPipelineBase",
41
+ "GPUComputePipeline",
42
+ "GPURenderPipeline",
43
+ "GPUCommandBuffer",
44
+ "GPUCommandsMixin",
45
+ "GPUCommandEncoder",
46
+ "GPUBindingCommandsMixin",
47
+ "GPUDebugCommandsMixin",
48
+ "GPUComputePassEncoder",
49
+ "GPURenderPassEncoder",
50
+ "GPURenderCommandsMixin",
51
+ "GPURenderBundle",
52
+ "GPURenderBundleEncoder",
53
+ "GPUQueue",
54
+ "GPUQuerySet",
55
+ "GPUCanvasContext",
56
+ "GPUDeviceLostInfo",
57
+ "GPUError",
58
+ "GPUValidationError",
59
+ "GPUOutOfMemoryError",
60
+ "GPUInternalError",
61
+ ]
62
+
63
+ logger = logging.getLogger("wgpu")
64
+
65
+
66
+ apidiff = ApiDiff()
67
+
68
+
69
+ # Obtain the object tracker. Note that we store a ref of
70
+ # the latter on all classes that refer to it. Otherwise, on a sys exit,
71
+ # the module attributes are None-ified, and the destructors would
72
+ # therefore fail and produce warnings.
73
+ object_tracker = diagnostics.object_counts.tracker
74
+
75
+ # The 'optional' value is used as the default value for optional arguments in the following two cases:
76
+ # * The method accepts a descriptor that is optional, so we make all arguments (i.e. descriptor fields) optional, and this one does not have a default value.
77
+ # * In wgpu-py we decided that this argument should be optional, even though it's currently not according to the WebGPU spec.
78
+ optional = None
79
+
80
+
81
+ class GPU:
82
+ """The entrypoint to the wgpu API.
83
+
84
+ The starting point of your wgpu-adventure is always to obtain an
85
+ adapter. This is the equivalent to browser's ``navigator.gpu``.
86
+ When a backend is loaded, the ``wgpu.gpu`` object is replaced with
87
+ a backend-specific implementation.
88
+ """
89
+
90
+ # IDL: Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {}); -> GPUPowerPreference powerPreference, boolean forceFallbackAdapter = false
91
+ @apidiff.change("arguments include canvas")
92
+ def request_adapter_sync(
93
+ self,
94
+ *,
95
+ power_preference: enums.PowerPreference = None,
96
+ force_fallback_adapter: bool = False,
97
+ canvas=None,
98
+ ):
99
+ """Sync version of `request_adapter_async()`.
100
+
101
+ Provided by wgpu-py, but not compatible with WebGPU.
102
+ """
103
+ # If this method gets called, no backend has been loaded yet, let's do that now!
104
+ from .backends.auto import gpu
105
+
106
+ return gpu.request_adapter_sync(
107
+ power_preference=power_preference,
108
+ force_fallback_adapter=force_fallback_adapter,
109
+ canvas=canvas,
110
+ )
111
+
112
+ # IDL: Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {}); -> GPUPowerPreference powerPreference, boolean forceFallbackAdapter = false
113
+ @apidiff.change("arguments include canvas")
114
+ async def request_adapter_async(
115
+ self,
116
+ *,
117
+ power_preference: enums.PowerPreference = None,
118
+ force_fallback_adapter: bool = False,
119
+ canvas=None,
120
+ ):
121
+ """Create a `GPUAdapter`, the object that represents an abstract wgpu
122
+ implementation, from which one can request a `GPUDevice`.
123
+
124
+ Arguments:
125
+ power_preference (PowerPreference): "high-performance" or "low-power".
126
+ force_fallback_adapter (bool): whether to use a (probably CPU-based)
127
+ fallback adapter.
128
+ canvas : The canvas that the adapter should be able to render to. This can typically
129
+ be left to None. If given, the object must implement ``WgpuCanvasInterface``.
130
+ """
131
+ # If this method gets called, no backend has been loaded yet, let's do that now!
132
+ from .backends.auto import gpu
133
+
134
+ return await gpu.request_adapter_async(
135
+ power_preference=power_preference,
136
+ force_fallback_adapter=force_fallback_adapter,
137
+ canvas=canvas,
138
+ )
139
+
140
+ @apidiff.add("Method useful for multi-gpu environments")
141
+ def enumerate_adapters_sync(self):
142
+ """Sync version of `enumerate_adapters_async()`.
143
+
144
+ Provided by wgpu-py, but not compatible with WebGPU.
145
+ """
146
+
147
+ # If this method gets called, no backend has been loaded yet, let's do that now!
148
+ from .backends.auto import gpu
149
+
150
+ return gpu.enumerate_adapters_sync()
151
+
152
+ @apidiff.add("Method useful for multi-gpu environments")
153
+ async def enumerate_adapters_async(self):
154
+ """Get a list of adapter objects available on the current system.
155
+
156
+ An adapter can then be selected (e.g. using it's summary), and a device
157
+ then created from it.
158
+
159
+ The order of the devices is such that Vulkan adapters go first, then
160
+ Metal, then D3D12, then OpenGL. Within each category, the order as
161
+ provided by the particular backend is maintained. Note that the same
162
+ device may be present via multiple backends (e.g. vulkan/opengl).
163
+
164
+ We cannot make guarantees about whether the order of the adapters
165
+ matches the order as reported by e.g. ``nvidia-smi``. We have found that
166
+ on a Linux multi-gpu cluster, the order does match, but we cannot
167
+ promise that this is always the case. If you want to make sure, do some
168
+ testing by allocating big buffers and checking memory usage using
169
+ ``nvidia-smi``.
170
+
171
+ See https://github.com/pygfx/wgpu-py/issues/482 for more details.
172
+ """
173
+ # Note that backends that cannot enumerate adapters, like WebGL,
174
+ # can at least request two adapters with different power preference,
175
+ # and then return both or one (if they represent the same adapter).
176
+
177
+ # If this method gets called, no backend has been loaded yet, let's do that now!
178
+ from .backends.auto import gpu
179
+
180
+ return await gpu.enumerate_adapters_async()
181
+
182
+ # IDL: GPUTextureFormat getPreferredCanvasFormat();
183
+ @apidiff.change("Disabled because we put it on the canvas context")
184
+ def get_preferred_canvas_format(self):
185
+ """Not implemented in wgpu-py; use `GPUCanvasContext.get_preferred_format()` instead.
186
+ The WebGPU spec defines this function, but in wgpu there are different
187
+ kinds of canvases which may each prefer/support a different format.
188
+ """
189
+ raise RuntimeError("Use canvas.get_preferred_format() instead.")
190
+
191
+ # IDL: [SameObject] readonly attribute WGSLLanguageFeatures wgslLanguageFeatures;
192
+ @property
193
+ def wgsl_language_features(self):
194
+ """A set of strings representing the WGSL language extensions supported by all adapters.
195
+ Returns an empty set for now."""
196
+ # Looks like at the time of writing there are no definitions for extensions yet
197
+ return set()
198
+
199
+
200
+ # Instantiate API entrypoint
201
+ gpu = GPU()
202
+
203
+
204
+ class GPUCanvasContext:
205
+ """Represents a context to configure a canvas and render to it.
206
+
207
+ Can be obtained via `gui.WgpuCanvasInterface.get_context()`.
208
+
209
+ The canvas-context plays a crucial role in connecting the wgpu API to the
210
+ GUI layer, in a way that allows the GUI to be agnostic about wgpu. It
211
+ combines (and checks) the user's preferences with the capabilities and
212
+ preferences of the canvas.
213
+ """
214
+
215
+ _ot = object_tracker
216
+
217
+ def __init__(self, canvas):
218
+ self._ot.increase(self.__class__.__name__)
219
+ self._canvas_ref = weakref.ref(canvas)
220
+
221
+ # Surface capabilities. Stored the first time it is obtained
222
+ self._capabilities = None
223
+
224
+ # Configuration dict from the user, set via self.configure()
225
+ self._config = None
226
+
227
+ # The last used texture
228
+ self._texture = None
229
+
230
+ # The configuration from the canvas, obtained with canvas.get_present_info()
231
+ self._present_info = canvas.get_present_info()
232
+ if self._present_info.get("method", None) not in ("screen", "image"):
233
+ raise RuntimeError(
234
+ "canvas.get_present_info() must produce a dict with a field 'method' that is either 'screen' or 'image'."
235
+ )
236
+
237
+ def _get_canvas(self):
238
+ """Getter method for internal use."""
239
+ return self._canvas_ref()
240
+
241
+ # IDL: readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;
242
+ @property
243
+ def canvas(self):
244
+ """The associated canvas object."""
245
+ return self._canvas_ref()
246
+
247
+ def _get_capabilities(self, adapter):
248
+ """Get dict of capabilities and cache the result."""
249
+ if self._capabilities is None:
250
+ self._capabilities = {}
251
+ if self._present_info["method"] == "screen":
252
+ # Query capabilities from the surface
253
+ self._capabilities.update(self._get_capabilities_screen(adapter))
254
+ else:
255
+ # Default image capabilities
256
+ self._capabilities = {
257
+ "formats": ["rgba8unorm-srgb", "rgba8unorm"],
258
+ "usages": 0xFF,
259
+ "alpha_modes": [enums.CanvasAlphaMode.opaque],
260
+ }
261
+ # If capabilities were provided via surface info, overload them!
262
+ for key in ["formats", "alpha_modes"]:
263
+ if key in self._present_info:
264
+ self._capabilities[key] = self._present_info[key]
265
+ # Derived defaults
266
+ if "view_formats" not in self._capabilities:
267
+ self._capabilities["view_formats"] = self._capabilities["formats"]
268
+
269
+ return self._capabilities
270
+
271
+ def _get_capabilities_screen(self, adapter):
272
+ """Get capabilities for a native surface."""
273
+ raise NotImplementedError()
274
+
275
+ @apidiff.add("Better place to define the preferred format")
276
+ def get_preferred_format(self, adapter):
277
+ """Get the preferred surface texture format."""
278
+ capabilities = self._get_capabilities(adapter)
279
+ formats = capabilities["formats"]
280
+ return formats[0] if formats else "bgra8-unorm"
281
+
282
+ # IDL: undefined configure(GPUCanvasConfiguration configuration); -> required GPUDevice device, required GPUTextureFormat format, GPUTextureUsageFlags usage = 0x10, sequence<GPUTextureFormat> viewFormats = [], PredefinedColorSpace colorSpace = "srgb", GPUCanvasToneMapping toneMapping = {}, GPUCanvasAlphaMode alphaMode = "opaque"
283
+ def configure(
284
+ self,
285
+ *,
286
+ device: GPUDevice,
287
+ format: enums.TextureFormat,
288
+ usage: flags.TextureUsage = 0x10,
289
+ view_formats: List[enums.TextureFormat] = [],
290
+ color_space: str = "srgb",
291
+ tone_mapping: structs.CanvasToneMapping = {},
292
+ alpha_mode: enums.CanvasAlphaMode = "opaque",
293
+ ):
294
+ """Configures the presentation context for the associated canvas.
295
+ Destroys any textures produced with a previous configuration.
296
+ This clears the drawing buffer to transparent black.
297
+
298
+ Arguments:
299
+ device (WgpuDevice): The GPU device object to create compatible textures for.
300
+ format (enums.TextureFormat): The format that textures returned by
301
+ ``get_current_texture()`` will have. Must be one of the supported context
302
+ formats. Can be ``None`` to use the canvas' preferred format.
303
+ usage (flags.TextureUsage): Default ``TextureUsage.OUTPUT_ATTACHMENT``.
304
+ view_formats (List[enums.TextureFormat]): The formats that views created
305
+ from textures returned by ``get_current_texture()`` may use.
306
+ color_space (PredefinedColorSpace): The color space that values written
307
+ into textures returned by ``get_current_texture()`` should be displayed with.
308
+ Default "srgb". Not yet supported.
309
+ tone_mapping (enums.CanvasToneMappingMode): Not yet supported.
310
+ alpha_mode (structs.CanvasAlphaMode): Determines the effect that alpha values
311
+ will have on the content of textures returned by ``get_current_texture()``
312
+ when read, displayed, or used as an image source. Default "opaque".
313
+ """
314
+
315
+ # Check types
316
+
317
+ if not isinstance(device, GPUDevice):
318
+ raise TypeError("Given device is not a device.")
319
+
320
+ if format is None:
321
+ format = self.get_preferred_format(device.adapter)
322
+ if format not in enums.TextureFormat:
323
+ raise ValueError(f"Configure: format {format} not in {enums.TextureFormat}")
324
+
325
+ if not isinstance(usage, int):
326
+ usage = str_flag_to_int(flags.TextureUsage, usage)
327
+
328
+ color_space # noqa - not really supported, just assume srgb for now
329
+ tone_mapping # noqa - not supported yet
330
+
331
+ if alpha_mode not in enums.CanvasAlphaMode:
332
+ raise ValueError(
333
+ f"Configure: alpha_mode {alpha_mode} not in {enums.CanvasAlphaMode}"
334
+ )
335
+
336
+ # Check against capabilities
337
+
338
+ capabilities = self._get_capabilities(device.adapter)
339
+
340
+ if format not in capabilities["formats"]:
341
+ raise ValueError(
342
+ f"Configure: unsupported texture format: {format} not in {capabilities['formats']}"
343
+ )
344
+
345
+ if not usage & capabilities["usages"]:
346
+ raise ValueError(
347
+ f"Configure: unsupported texture usage: {usage} not in {capabilities['usages']}"
348
+ )
349
+
350
+ for view_format in view_formats:
351
+ if view_format not in capabilities["view_formats"]:
352
+ raise ValueError(
353
+ f"Configure: unsupported view format: {view_format} not in {capabilities['view_formats']}"
354
+ )
355
+
356
+ if alpha_mode not in capabilities["alpha_modes"]:
357
+ raise ValueError(
358
+ f"Configure: unsupported alpha-mode: {alpha_mode} not in {capabilities['alpha_modes']}"
359
+ )
360
+
361
+ # Store
362
+
363
+ self._config = {
364
+ "device": device,
365
+ "format": format,
366
+ "usage": usage,
367
+ "view_formats": view_formats,
368
+ "color_space": color_space,
369
+ "tone_mapping": tone_mapping,
370
+ "alpha_mode": alpha_mode,
371
+ }
372
+
373
+ if self._present_info["method"] == "screen":
374
+ self._configure_screen(**self._config)
375
+
376
+ def _configure_screen(
377
+ self,
378
+ *,
379
+ device,
380
+ format,
381
+ usage,
382
+ view_formats,
383
+ color_space,
384
+ tone_mapping,
385
+ alpha_mode,
386
+ ):
387
+ raise NotImplementedError()
388
+
389
+ # IDL: undefined unconfigure();
390
+ def unconfigure(self):
391
+ """Removes the presentation context configuration.
392
+ Destroys any textures produced while configured.
393
+ """
394
+ if self._present_info["method"] == "screen":
395
+ self._unconfigure_screen()
396
+ self._config = None
397
+ self._drop_texture()
398
+
399
+ def _unconfigure_screen(self):
400
+ raise NotImplementedError()
401
+
402
+ # IDL: GPUTexture getCurrentTexture();
403
+ def get_current_texture(self):
404
+ """Get the `GPUTexture` that will be composited to the canvas next."""
405
+ if not self._config:
406
+ raise RuntimeError(
407
+ "Canvas context must be configured before calling get_current_texture()."
408
+ )
409
+
410
+ # When the texture is active right now, we could either:
411
+ # * return the existing texture
412
+ # * warn about it, and create a new one
413
+ # * raise an error
414
+ # Right now we return the existing texture, so user can retrieve it in different render passes that write to the same frame.
415
+
416
+ if self._texture is None:
417
+ if self._present_info["method"] == "screen":
418
+ self._texture = self._create_texture_screen()
419
+ else:
420
+ self._texture = self._create_texture_image()
421
+
422
+ return self._texture
423
+
424
+ def _create_texture_image(self):
425
+ canvas = self._get_canvas()
426
+ width, height = canvas.get_physical_size()
427
+ width, height = max(width, 1), max(height, 1)
428
+
429
+ device = self._config["device"]
430
+ self._texture = device.create_texture(
431
+ label="presentation-context",
432
+ size=(width, height, 1),
433
+ format=self._config["format"],
434
+ usage=self._config["usage"] | flags.TextureUsage.COPY_SRC,
435
+ )
436
+ return self._texture
437
+
438
+ def _create_texture_screen(self):
439
+ raise NotImplementedError()
440
+
441
+ def _drop_texture(self):
442
+ if self._texture:
443
+ self._texture._release() # not destroy, because it may be in use.
444
+ self._texture = None
445
+
446
+ @apidiff.add("Present method is exposed")
447
+ def present(self):
448
+ """Present what has been drawn to the current texture, by compositing it
449
+ to the canvas. Note that a canvas based on `gui.WgpuCanvasBase` will call this
450
+ method automatically at the end of each draw event.
451
+ """
452
+ # todo: can we remove this present() method?
453
+
454
+ if not self._texture:
455
+ # This can happen when a user somehow forgot to call
456
+ # get_current_texture(). But then what was this person rendering to
457
+ # then? The thing is that this also happens when there is an
458
+ # exception in the draw function before the call to
459
+ # get_current_texture(). In this scenario our warning may
460
+ # add confusion, so provide context and make it a debug level warning.
461
+ msg = "Warning in present(): No texture to present, missing call to get_current_texture()?"
462
+ logger.debug(msg)
463
+ return
464
+
465
+ if self._present_info["method"] == "screen":
466
+ self._present_screen()
467
+ else:
468
+ self._present_image()
469
+
470
+ self._drop_texture()
471
+
472
+ def _present_image(self):
473
+ texture = self._texture
474
+ device = texture._device
475
+
476
+ size = texture.size
477
+ format = texture.format
478
+ nchannels = 4 # we expect rgba or bgra
479
+ if not format.startswith(("rgba", "bgra")):
480
+ raise RuntimeError(f"Image present unsupported texture format {format}.")
481
+ if "8" in format:
482
+ bytes_per_pixel = nchannels
483
+ elif "16" in format:
484
+ bytes_per_pixel = nchannels * 2
485
+ elif "32" in format:
486
+ bytes_per_pixel = nchannels * 4
487
+ else:
488
+ raise RuntimeError(
489
+ f"Image present unsupported texture format bitdepth {format}."
490
+ )
491
+
492
+ data = device.queue.read_texture(
493
+ {
494
+ "texture": texture,
495
+ "mip_level": 0,
496
+ "origin": (0, 0, 0),
497
+ },
498
+ {
499
+ "offset": 0,
500
+ "bytes_per_row": bytes_per_pixel * size[0],
501
+ "rows_per_image": size[1],
502
+ },
503
+ size,
504
+ )
505
+
506
+ # Represent as memory object to avoid numpy dependency
507
+ # Equivalent: np.frombuffer(data, np.uint8).reshape(size[1], size[0], nchannels)
508
+ data = data.cast("B", (size[1], size[0], nchannels))
509
+
510
+ self._get_canvas().present_image(data, format=format)
511
+
512
+ def _present_screen(self):
513
+ raise NotImplementedError()
514
+
515
+ def __del__(self):
516
+ self._ot.decrease(self.__class__.__name__)
517
+ self._release()
518
+
519
+ def _release(self):
520
+ self._drop_texture()
521
+
522
+
523
+ class GPUAdapterInfo:
524
+ """Represents information about an adapter."""
525
+
526
+ def __init__(self, info):
527
+ self._info = info
528
+
529
+ # IDL: readonly attribute DOMString vendor;
530
+ @property
531
+ def vendor(self):
532
+ """The vendor that built this adaptor."""
533
+ return self._info["vendor"]
534
+
535
+ # IDL: readonly attribute DOMString architecture;
536
+ @property
537
+ def architecture(self):
538
+ """The adapters architecrure."""
539
+ return self._info["architecture"]
540
+
541
+ # IDL: readonly attribute DOMString device;
542
+ @property
543
+ def device(self):
544
+ """The kind of device that this adapter represents."""
545
+ return self._info["device"]
546
+
547
+ # IDL: readonly attribute DOMString description;
548
+ @property
549
+ def description(self):
550
+ """A textual description of the adapter."""
551
+ return self._info["description"]
552
+
553
+
554
+ class GPUAdapter:
555
+ """Represents an abstract wgpu implementation.
556
+
557
+ An adapter represents both an instance of a hardware accelerator
558
+ (e.g. GPU or CPU) and an implementation of WGPU on top of that
559
+ accelerator.
560
+
561
+ The adapter is used to request a device object. The adapter object
562
+ enumerates its capabilities (features) and limits.
563
+
564
+ If an adapter becomes unavailable, it becomes invalid.
565
+ Once invalid, it never becomes valid again.
566
+ """
567
+
568
+ _ot = object_tracker
569
+
570
+ def __init__(self, internal, features, limits, adapter_info):
571
+ self._ot.increase(self.__class__.__name__)
572
+ self._internal = internal
573
+
574
+ assert isinstance(features, set)
575
+ assert isinstance(limits, dict)
576
+ assert isinstance(adapter_info, dict)
577
+
578
+ self._features = features
579
+ self._limits = limits
580
+ self._adapter_info = adapter_info
581
+
582
+ # IDL: [SameObject] readonly attribute GPUSupportedFeatures features;
583
+ @property
584
+ def features(self):
585
+ """A set of feature names supported by the adapter."""
586
+ return self._features
587
+
588
+ # IDL: [SameObject] readonly attribute GPUSupportedLimits limits;
589
+ @property
590
+ def limits(self):
591
+ """A dict with limits for the adapter."""
592
+ return self._limits
593
+
594
+ # IDL: Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {}); -> USVString label = "", sequence<GPUFeatureName> requiredFeatures = [], record<DOMString, GPUSize64> requiredLimits = {}, GPUQueueDescriptor defaultQueue = {}
595
+ def request_device_sync(
596
+ self,
597
+ *,
598
+ label: str = "",
599
+ required_features: List[enums.FeatureName] = [],
600
+ required_limits: Dict[str, int] = {},
601
+ default_queue: structs.QueueDescriptor = {},
602
+ ):
603
+ """Sync version of `request_device_async()`.
604
+
605
+ Provided by wgpu-py, but not compatible with WebGPU.
606
+ """
607
+ raise NotImplementedError()
608
+
609
+ # IDL: Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {}); -> USVString label = "", sequence<GPUFeatureName> requiredFeatures = [], record<DOMString, GPUSize64> requiredLimits = {}, GPUQueueDescriptor defaultQueue = {}
610
+ async def request_device_async(
611
+ self,
612
+ *,
613
+ label: str = "",
614
+ required_features: List[enums.FeatureName] = [],
615
+ required_limits: Dict[str, int] = {},
616
+ default_queue: structs.QueueDescriptor = {},
617
+ ):
618
+ """Request a `GPUDevice` from the adapter.
619
+
620
+ Arguments:
621
+ label (str): A human-readable label. Optional.
622
+ required_features (list of str): the features (extensions) that you need. Default [].
623
+ required_limits (dict): the various limits that you need. Default {}.
624
+ default_queue (structs.QueueDescriptor): Descriptor for the default queue. Optional.
625
+ """
626
+ raise NotImplementedError()
627
+
628
+ def _release(self):
629
+ pass
630
+
631
+ def __del__(self):
632
+ self._ot.decrease(self.__class__.__name__)
633
+ self._release()
634
+
635
+ # IDL: readonly attribute boolean isFallbackAdapter;
636
+ @property
637
+ def is_fallback_adapter(self):
638
+ """Whether this adapter runs on software (rather than dedicated hardware)."""
639
+ return self._adapter_info.get("adapter_type", "").lower() in ("software", "cpu")
640
+
641
+ # IDL: [SameObject] readonly attribute GPUAdapterInfo info;
642
+ @property
643
+ def info(self):
644
+ """A dict with information about this adapter, such as the vendor and device name."""
645
+ # Note: returns a dict rather than an GPUAdapterInfo instance.
646
+ return self._adapter_info
647
+
648
+ @apidiff.add("Useful in multi-gpu environments")
649
+ @property
650
+ def summary(self):
651
+ """A one-line summary of the info of this adapter (device, adapter_type, backend_type)."""
652
+ d = self._adapter_info
653
+ return f"{d['device']} ({d['adapter_type']}) via {d['backend_type']}"
654
+
655
+
656
+ class GPUObjectBase:
657
+ """The base class for all GPU objects.
658
+
659
+ A GPU object is an object that can be thought of having a representation on
660
+ the GPU; the device and all objects belonging to a device.
661
+ """
662
+
663
+ _ot = object_tracker
664
+ _nbytes = 0
665
+
666
+ def __init__(self, label, internal, device):
667
+ self._ot.increase(self.__class__.__name__, self._nbytes)
668
+ self._label = label
669
+ self._internal = internal # The native/raw/real GPU object
670
+ self._device = device
671
+ logger.info(f"Creating {self.__class__.__name__} {label}")
672
+
673
+ # IDL: attribute USVString label;
674
+ @property
675
+ def label(self):
676
+ """A human-readable name identifying the GPU object."""
677
+ return self._label
678
+
679
+ def __str__(self):
680
+ if self._label:
681
+ return f'<{self.__class__.__name__} "{self._label}">'
682
+ else:
683
+ return f"<{self.__class__.__name__} {id(self)}>"
684
+
685
+ def _release(self):
686
+ """Subclasses can implement this to clean up."""
687
+ pass
688
+
689
+ def __del__(self):
690
+ self._ot.decrease(self.__class__.__name__, self._nbytes)
691
+ self._release()
692
+
693
+ # Public destroy() methods are implemented on classes as the WebGPU spec specifies.
694
+
695
+
696
+ class GPUDevice(GPUObjectBase):
697
+ """The top-level interface through which GPU objects are created.
698
+
699
+ A device is the logical instantiation of an adapter, through which
700
+ internal objects are created. It can be shared across threads.
701
+ A device is the exclusive owner of all internal objects created
702
+ from it: when the device is lost, all objects created from it become
703
+ invalid.
704
+
705
+ Create a device using `GPUAdapter.request_device_sync()` or
706
+ `GPUAdapter.request_device_async()`.
707
+ """
708
+
709
+ def __init__(self, label, internal, adapter, features, limits, queue):
710
+ super().__init__(label, internal, self)
711
+
712
+ assert isinstance(adapter, GPUAdapter)
713
+ assert isinstance(features, set)
714
+ assert isinstance(limits, dict)
715
+
716
+ self._adapter = adapter
717
+ self._features = features
718
+ self._limits = limits
719
+ self._queue = queue
720
+ queue._device = self # because it could not be set earlier
721
+
722
+ # IDL: [SameObject] readonly attribute GPUSupportedFeatures features;
723
+ @property
724
+ def features(self):
725
+ """A set of feature names supported by this device."""
726
+ return self._features
727
+
728
+ # IDL: [SameObject] readonly attribute GPUSupportedLimits limits;
729
+ @property
730
+ def limits(self):
731
+ """A dict with limits for this device."""
732
+ return self._limits
733
+
734
+ # IDL: [SameObject] readonly attribute GPUQueue queue;
735
+ @property
736
+ def queue(self):
737
+ """The default `GPUQueue` for this device."""
738
+ return self._queue
739
+
740
+ @apidiff.add("Too useful to not-have")
741
+ @property
742
+ def adapter(self):
743
+ """The adapter object corresponding to this device."""
744
+ return self._adapter
745
+
746
+ # IDL: readonly attribute Promise<GPUDeviceLostInfo> lost;
747
+ @apidiff.hide("Not a Pythonic API")
748
+ @property
749
+ def lost_sync(self):
750
+ """Sync version of `lost`.
751
+
752
+ Provided by wgpu-py, but not compatible with WebGPU.
753
+ """
754
+ return self._get_lost_sync()
755
+
756
+ # IDL: readonly attribute Promise<GPUDeviceLostInfo> lost;
757
+ @apidiff.hide("Not a Pythonic API")
758
+ @property
759
+ async def lost_async(self):
760
+ """Provides information about why the device is lost."""
761
+ # In JS you can device.lost.then ... to handle lost devices.
762
+ # We may want to eventually support something similar async-like?
763
+ # at some point
764
+
765
+ # Properties don't get repeated at _api.py, so we use a proxy method.
766
+ return await self._get_lost_async()
767
+
768
+ def _get_lost_sync(self):
769
+ raise NotImplementedError()
770
+
771
+ async def _get_lost_async(self):
772
+ raise NotImplementedError()
773
+
774
+ # IDL: attribute EventHandler onuncapturederror;
775
+ @apidiff.hide("Specific to browsers")
776
+ @property
777
+ def onuncapturederror(self):
778
+ """Event handler.
779
+
780
+ In JS you'd do ``gpuDevice.addEventListener('uncapturederror', ...)``. We'd need
781
+ to figure out how to do this in Python.
782
+ """
783
+ raise NotImplementedError()
784
+
785
+ # IDL: undefined destroy();
786
+ def destroy(self):
787
+ """Destroy this device.
788
+
789
+ This cleans up all its resources and puts it in an unusable state.
790
+ Note that all objects get cleaned up properly automatically; this
791
+ is only intended to support explicit destroying.
792
+
793
+ NOTE: not yet implemented; for the moment this does nothing.
794
+ """
795
+ raise NotImplementedError()
796
+
797
+ # IDL: GPUBuffer createBuffer(GPUBufferDescriptor descriptor); -> USVString label = "", required GPUSize64 size, required GPUBufferUsageFlags usage, boolean mappedAtCreation = false
798
+ def create_buffer(
799
+ self,
800
+ *,
801
+ label: str = "",
802
+ size: int,
803
+ usage: flags.BufferUsage,
804
+ mapped_at_creation: bool = False,
805
+ ):
806
+ """Create a `GPUBuffer` object.
807
+
808
+ Arguments:
809
+ label (str): A human-readable label. Optional.
810
+ size (int): The size of the buffer in bytes.
811
+ usage (flags.BufferUsage): The ways in which this buffer will be used.
812
+ mapped_at_creation (bool): Whether the buffer is initially mapped.
813
+
814
+ Alignment: the size must be a multiple of 4.
815
+
816
+ """
817
+ raise NotImplementedError()
818
+
819
+ @apidiff.add("Convenience function")
820
+ def create_buffer_with_data(self, *, label="", data, usage: "flags.BufferUsage"):
821
+ """Create a `GPUBuffer` object initialized with the given data.
822
+
823
+ This is a convenience function that creates a mapped buffer,
824
+ writes the given data to it, and then unmaps the buffer.
825
+
826
+ Arguments:
827
+ label (str): A human-readable label. Optional.
828
+ data: Any object supporting the Python buffer protocol (this
829
+ includes bytes, bytearray, ctypes arrays, numpy arrays, etc.).
830
+ usage (flags.BufferUsage): The ways in which this buffer will be used.
831
+
832
+ Alignment: if the size (in bytes) of data is not a multiple of 4, the buffer
833
+ size is rounded up to the nearest multiple of 4.
834
+
835
+ Also see `GPUBuffer.write_mapped()` and `GPUQueue.write_buffer()`.
836
+ """
837
+ # This function was originally created to support the workflow
838
+ # of initializing a buffer with data when we did not support
839
+ # buffer mapping. Now that we do have buffer mapping it is not
840
+ # strictly necessary, but it's still quite useful and feels
841
+ # more Pythonic than having to write the boilerplate code below.
842
+
843
+ # Create a view of known type
844
+ data = memoryview(data).cast("B")
845
+ size = (data.nbytes + 3) & ~3 # round up to a multiple of 4
846
+
847
+ # Create the buffer and write data
848
+ buf = self.create_buffer(
849
+ label=label, size=size, usage=usage, mapped_at_creation=True
850
+ )
851
+ buf.write_mapped(data)
852
+ buf.unmap()
853
+ return buf
854
+
855
+ # IDL: GPUTexture createTexture(GPUTextureDescriptor descriptor); -> USVString label = "", required GPUExtent3D size, GPUIntegerCoordinate mipLevelCount = 1, GPUSize32 sampleCount = 1, GPUTextureDimension dimension = "2d", required GPUTextureFormat format, required GPUTextureUsageFlags usage, sequence<GPUTextureFormat> viewFormats = []
856
+ def create_texture(
857
+ self,
858
+ *,
859
+ label: str = "",
860
+ size: Union[List[int], structs.Extent3D],
861
+ mip_level_count: int = 1,
862
+ sample_count: int = 1,
863
+ dimension: enums.TextureDimension = "2d",
864
+ format: enums.TextureFormat,
865
+ usage: flags.TextureUsage,
866
+ view_formats: List[enums.TextureFormat] = [],
867
+ ):
868
+ """Create a `GPUTexture` object.
869
+
870
+ Arguments:
871
+ label (str): A human-readable label. Optional.
872
+ size (tuple or dict): The texture size as a 3-tuple or a `structs.Extent3D`.
873
+ mip_level_count (int): The number of mip leveles. Default 1.
874
+ sample_count (int): The number of samples. Default 1.
875
+ dimension (enums.TextureDimension): The dimensionality of the texture. Default 2d.
876
+ format (TextureFormat): What channels it stores and how.
877
+ usage (flags.TextureUsage): The ways in which the texture will be used.
878
+ view_formats (optional): A list of formats that views are allowed to have
879
+ in addition to the texture's own view. Using these formats may have
880
+ a performance penalty.
881
+
882
+ See https://gpuweb.github.io/gpuweb/#texture-format-caps for a
883
+ list of available texture formats. Note that fewer formats are
884
+ available for storage usage.
885
+ """
886
+ raise NotImplementedError()
887
+
888
+ # IDL: GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {}); -> USVString label = "", GPUAddressMode addressModeU = "clamp-to-edge", GPUAddressMode addressModeV = "clamp-to-edge", GPUAddressMode addressModeW = "clamp-to-edge", GPUFilterMode magFilter = "nearest", GPUFilterMode minFilter = "nearest", GPUMipmapFilterMode mipmapFilter = "nearest", float lodMinClamp = 0, float lodMaxClamp = 32, GPUCompareFunction compare, [Clamp] unsigned short maxAnisotropy = 1
889
+ def create_sampler(
890
+ self,
891
+ *,
892
+ label: str = "",
893
+ address_mode_u: enums.AddressMode = "clamp-to-edge",
894
+ address_mode_v: enums.AddressMode = "clamp-to-edge",
895
+ address_mode_w: enums.AddressMode = "clamp-to-edge",
896
+ mag_filter: enums.FilterMode = "nearest",
897
+ min_filter: enums.FilterMode = "nearest",
898
+ mipmap_filter: enums.MipmapFilterMode = "nearest",
899
+ lod_min_clamp: float = 0,
900
+ lod_max_clamp: float = 32,
901
+ compare: enums.CompareFunction = optional,
902
+ max_anisotropy: int = 1,
903
+ ):
904
+ """Create a `GPUSampler` object. Samplers specify how a texture is sampled.
905
+
906
+ Arguments:
907
+ label (str): A human-readable label. Optional.
908
+ address_mode_u (enums.AddressMode): What happens when sampling beyond the x edge.
909
+ Default "clamp-to-edge".
910
+ address_mode_v (enums.AddressMode): What happens when sampling beyond the y edge.
911
+ Default "clamp-to-edge".
912
+ address_mode_w (enums.AddressMode): What happens when sampling beyond the z edge.
913
+ Default "clamp-to-edge".
914
+ mag_filter (enums.FilterMode): Interpolation when zoomed in. Default 'nearest'.
915
+ min_filter (enums.FilterMode): Interpolation when zoomed out. Default 'nearest'.
916
+ mipmap_filter: (enums.MipmapFilterMode): Interpolation between mip levels. Default 'nearest'.
917
+ lod_min_clamp (float): The minimum level of detail. Default 0.
918
+ lod_max_clamp (float): The maximum level of detail. Default 32.
919
+ compare (enums.CompareFunction): The sample compare operation for depth textures.
920
+ Only specify this for depth textures. Default None.
921
+ max_anisotropy (int): The maximum anisotropy value clamp used by the sample,
922
+ betweet 1 and 16, default 1.
923
+ """
924
+ raise NotImplementedError()
925
+
926
+ # IDL: GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor); -> USVString label = "", required sequence<GPUBindGroupLayoutEntry> entries
927
+ def create_bind_group_layout(
928
+ self, *, label: str = "", entries: List[structs.BindGroupLayoutEntry]
929
+ ):
930
+ """Create a `GPUBindGroupLayout` object. One or more
931
+ such objects are passed to `create_pipeline_layout()` to
932
+ specify the (abstract) pipeline layout for resources. See the
933
+ docs on bind groups for details.
934
+
935
+ Arguments:
936
+ label (str): A human-readable label. Optional.
937
+ entries (list): A list of `structs.BindGroupLayoutEntry` dicts.
938
+ Each contains either a `structs.BufferBindingLayout`,
939
+ `structs.SamplerBindingLayout`, `structs.TextureBindingLayout`,
940
+ or `structs.StorageTextureBindingLayout`.
941
+
942
+ Example with `structs.BufferBindingLayout`:
943
+
944
+ .. code-block:: py
945
+
946
+ {
947
+ "binding": 0,
948
+ "visibility": wgpu.ShaderStage.COMPUTE,
949
+ "buffer": {
950
+ "type": wgpu.BufferBindingType.storage_buffer,
951
+ "has_dynamic_offset": False, # optional
952
+ "min_binding_size": 0 # optional
953
+ }
954
+ },
955
+
956
+ Note on ``has_dynamic_offset``: For uniform-buffer, storage-buffer, and
957
+ readonly-storage-buffer bindings, it indicates whether the binding has a
958
+ dynamic offset. One offset must be passed to `pass.set_bind_group()`
959
+ for each dynamic binding in increasing order of binding number.
960
+ """
961
+ raise NotImplementedError()
962
+
963
+ # IDL: GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor); -> USVString label = "", required GPUBindGroupLayout layout, required sequence<GPUBindGroupEntry> entries
964
+ def create_bind_group(
965
+ self,
966
+ *,
967
+ label: str = "",
968
+ layout: GPUBindGroupLayout,
969
+ entries: List[structs.BindGroupEntry],
970
+ ):
971
+ """Create a `GPUBindGroup` object, which can be used in
972
+ `pass.set_bind_group()` to attach a group of resources.
973
+
974
+ Arguments:
975
+ label (str): A human-readable label. Optional.
976
+ layout (GPUBindGroupLayout): The layout (abstract representation)
977
+ for this bind group.
978
+ entries (list): A list of `structs.BindGroupEntry` dicts. The ``resource`` field
979
+ is either `GPUSampler`, `GPUTextureView` or `structs.BufferBinding`.
980
+
981
+ Example entry dicts:
982
+
983
+ .. code-block:: py
984
+
985
+ # For a sampler
986
+ {
987
+ "binding" : 0, # slot
988
+ "resource": a_sampler,
989
+ }
990
+ # For a texture view
991
+ {
992
+ "binding" : 0, # slot
993
+ "resource": a_texture_view,
994
+ }
995
+ # For a buffer
996
+ {
997
+ "binding" : 0, # slot
998
+ "resource": {
999
+ "buffer": a_buffer,
1000
+ "offset": 0,
1001
+ "size": 812,
1002
+ }
1003
+ }
1004
+ """
1005
+ raise NotImplementedError()
1006
+
1007
+ # IDL: GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor); -> USVString label = "", required sequence<GPUBindGroupLayout> bindGroupLayouts
1008
+ def create_pipeline_layout(
1009
+ self, *, label: str = "", bind_group_layouts: List[GPUBindGroupLayout]
1010
+ ):
1011
+ """Create a `GPUPipelineLayout` object, which can be
1012
+ used in `create_render_pipeline()` or `create_compute_pipeline()`.
1013
+
1014
+ Arguments:
1015
+ label (str): A human-readable label. Optional.
1016
+ bind_group_layouts (list): A list of `GPUBindGroupLayout` objects.
1017
+ """
1018
+ raise NotImplementedError()
1019
+
1020
+ # IDL: GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor); -> USVString label = "", required USVString code, object sourceMap, sequence<GPUShaderModuleCompilationHint> compilationHints = []
1021
+ def create_shader_module(
1022
+ self,
1023
+ *,
1024
+ label: str = "",
1025
+ code: str,
1026
+ source_map: dict = optional,
1027
+ compilation_hints: List[structs.ShaderModuleCompilationHint] = [],
1028
+ ):
1029
+ """Create a `GPUShaderModule` object from shader source.
1030
+
1031
+ The primary shader language is WGSL, though SpirV is also supported,
1032
+ as well as GLSL (experimental).
1033
+
1034
+ Arguments:
1035
+ label (str): A human-readable label. Optional.
1036
+ code (str | bytes): The shader code, as WGSL, GLSL or SpirV.
1037
+ For GLSL code, the label must be given and contain the word
1038
+ 'comp', 'vert' or 'frag'. For SpirV the code must be bytes.
1039
+ compilation_hints: currently unused.
1040
+ """
1041
+ raise NotImplementedError()
1042
+
1043
+ # IDL: GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor); -> USVString label = "", required (GPUPipelineLayout or GPUAutoLayoutMode) layout, required GPUProgrammableStage compute
1044
+ def create_compute_pipeline(
1045
+ self,
1046
+ *,
1047
+ label: str = "",
1048
+ layout: Union[GPUPipelineLayout, enums.AutoLayoutMode],
1049
+ compute: structs.ProgrammableStage,
1050
+ ):
1051
+ """Create a `GPUComputePipeline` object.
1052
+
1053
+ Arguments:
1054
+ label (str): A human-readable label. Optional.
1055
+ layout (GPUPipelineLayout): object created with `create_pipeline_layout()`.
1056
+ compute (structs.ProgrammableStage): Binds shader module and entrypoint.
1057
+ """
1058
+ raise NotImplementedError()
1059
+
1060
+ # IDL: Promise<GPUComputePipeline> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor); -> USVString label = "", required (GPUPipelineLayout or GPUAutoLayoutMode) layout, required GPUProgrammableStage compute
1061
+ async def create_compute_pipeline_async(
1062
+ self,
1063
+ *,
1064
+ label: str = "",
1065
+ layout: Union[GPUPipelineLayout, enums.AutoLayoutMode],
1066
+ compute: structs.ProgrammableStage,
1067
+ ):
1068
+ """Async version of `create_compute_pipeline()`.
1069
+
1070
+ Both versions are compatible with WebGPU."""
1071
+ raise NotImplementedError()
1072
+
1073
+ # IDL: GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor); -> USVString label = "", required (GPUPipelineLayout or GPUAutoLayoutMode) layout, required GPUVertexState vertex, GPUPrimitiveState primitive = {}, GPUDepthStencilState depthStencil, GPUMultisampleState multisample = {}, GPUFragmentState fragment
1074
+ def create_render_pipeline(
1075
+ self,
1076
+ *,
1077
+ label: str = "",
1078
+ layout: Union[GPUPipelineLayout, enums.AutoLayoutMode],
1079
+ vertex: structs.VertexState,
1080
+ primitive: structs.PrimitiveState = {},
1081
+ depth_stencil: structs.DepthStencilState = optional,
1082
+ multisample: structs.MultisampleState = {},
1083
+ fragment: structs.FragmentState = optional,
1084
+ ):
1085
+ """Create a `GPURenderPipeline` object.
1086
+
1087
+ Arguments:
1088
+ label (str): A human-readable label. Optional.
1089
+ layout (GPUPipelineLayout): The layout for the new pipeline.
1090
+ vertex (structs.VertexState): Describes the vertex shader entry point of the
1091
+ pipeline and its input buffer layouts.
1092
+ primitive (structs.PrimitiveState): Describes the primitive-related properties
1093
+ of the pipeline. If `strip_index_format` is present (which means the
1094
+ primitive topology is a strip), and the drawCall is indexed, the
1095
+ vertex index list is split into sub-lists using the maximum value of this
1096
+ index format as a separator. Example: a list with values
1097
+ `[1, 2, 65535, 4, 5, 6]` of type "uint16" will be split in sub-lists
1098
+ `[1, 2]` and `[4, 5, 6]`.
1099
+ depth_stencil (structs.DepthStencilState): Describes the optional depth-stencil
1100
+ properties, including the testing, operations, and bias. Optional.
1101
+ multisample (structs.MultisampleState): Describes the multi-sampling properties of the pipeline.
1102
+ fragment (structs.FragmentState): Describes the fragment shader
1103
+ entry point of the pipeline and its output colors. If it's
1104
+ None, the No-Color-Output mode is enabled: the pipeline
1105
+ does not produce any color attachment outputs. It still
1106
+ performs rasterization and produces depth values based on
1107
+ the vertex position output. The depth testing and stencil
1108
+ operations can still be used.
1109
+
1110
+ In the example dicts below, the values that are marked as optional,
1111
+ the shown value is the default.
1112
+
1113
+ Example vertex (structs.VertexState) dict:
1114
+
1115
+ .. code-block:: py
1116
+
1117
+ {
1118
+ "module": shader_module,
1119
+ "entry_point": "main",
1120
+ "buffers": [
1121
+ {
1122
+ "array_stride": 8,
1123
+ "step_mode": wgpu.VertexStepMode.vertex, # optional
1124
+ "attributes": [
1125
+ {
1126
+ "format": wgpu.VertexFormat.float2,
1127
+ "offset": 0,
1128
+ "shader_location": 0,
1129
+ },
1130
+ ...
1131
+ ],
1132
+ },
1133
+ ...
1134
+ ]
1135
+ }
1136
+
1137
+ Example primitive (structs.PrimitiveState) dict:
1138
+
1139
+ .. code-block:: py
1140
+
1141
+ {
1142
+ "topology": wgpu.PrimitiveTopology.triangle_list, # optional
1143
+ "strip_index_format": wgpu.IndexFormat.uint32, # see note
1144
+ "front_face": wgpu.FrontFace.ccw, # optional
1145
+ "cull_mode": wgpu.CullMode.none, # optional
1146
+ }
1147
+
1148
+ Example depth_stencil (structs.DepthStencilState) dict:
1149
+
1150
+ .. code-block:: py
1151
+
1152
+ {
1153
+ "format": wgpu.TextureFormat.depth24plus_stencil8,
1154
+ "depth_write_enabled": False, # optional
1155
+ "depth_compare": wgpu.CompareFunction.always, # optional
1156
+ "stencil_front": { # optional
1157
+ "compare": wgpu.CompareFunction.equal,
1158
+ "fail_op": wgpu.StencilOperation.keep,
1159
+ "depth_fail_op": wgpu.StencilOperation.keep,
1160
+ "pass_op": wgpu.StencilOperation.keep,
1161
+ },
1162
+ "stencil_back": { # optional
1163
+ "compare": wgpu.CompareFunction.equal,
1164
+ "fail_op": wgpu.StencilOperation.keep,
1165
+ "depth_fail_op": wgpu.StencilOperation.keep,
1166
+ "pass_op": wgpu.StencilOperation.keep,
1167
+ },
1168
+ "stencil_read_mask": 0xFFFFFFFF, # optional
1169
+ "stencil_write_mask": 0xFFFFFFFF, # optional
1170
+ "depth_bias": 0, # optional
1171
+ "depth_bias_slope_scale": 0.0, # optional
1172
+ "depth_bias_clamp": 0.0, # optional
1173
+ }
1174
+
1175
+ Example multisample (structs.MultisampleState) dict:
1176
+
1177
+ .. code-block:: py
1178
+
1179
+ {
1180
+ "count": 1, # optional
1181
+ "mask": 0xFFFFFFFF, # optional
1182
+ "alpha_to_coverage_enabled": False # optional
1183
+ }
1184
+
1185
+ Example fragment (structs.FragmentState) dict. The `blend` parameter can be None
1186
+ to disable blending (not all texture formats support blending).
1187
+
1188
+ .. code-block:: py
1189
+
1190
+ {
1191
+ "module": shader_module,
1192
+ "entry_point": "main",
1193
+ "targets": [
1194
+ {
1195
+ "format": wgpu.TextureFormat.bgra8unorm_srgb,
1196
+ "blend": {
1197
+ "color": {
1198
+ "src_target": wgpu.BlendFactor.one, # optional
1199
+ "dst_target": wgpu.BlendFactor.zero, # optional
1200
+ "operation": gpu.BlendOperation.add, # optional
1201
+ },
1202
+ "alpha": {
1203
+ "src_target": wgpu.BlendFactor.one, # optional
1204
+ "dst_target": wgpu.BlendFactor.zero, # optional
1205
+ "operation": wgpu.BlendOperation.add, # optional
1206
+ },
1207
+ }
1208
+ "write_mask": wgpu.ColorWrite.ALL # optional
1209
+ },
1210
+ ...
1211
+ ]
1212
+ }
1213
+
1214
+ """
1215
+ raise NotImplementedError()
1216
+
1217
+ # IDL: Promise<GPURenderPipeline> createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor); -> USVString label = "", required (GPUPipelineLayout or GPUAutoLayoutMode) layout, required GPUVertexState vertex, GPUPrimitiveState primitive = {}, GPUDepthStencilState depthStencil, GPUMultisampleState multisample = {}, GPUFragmentState fragment
1218
+ async def create_render_pipeline_async(
1219
+ self,
1220
+ *,
1221
+ label: str = "",
1222
+ layout: Union[GPUPipelineLayout, enums.AutoLayoutMode],
1223
+ vertex: structs.VertexState,
1224
+ primitive: structs.PrimitiveState = {},
1225
+ depth_stencil: structs.DepthStencilState = optional,
1226
+ multisample: structs.MultisampleState = {},
1227
+ fragment: structs.FragmentState = optional,
1228
+ ):
1229
+ """Async version of `create_render_pipeline()`.
1230
+
1231
+ Both versions are compatible with WebGPU."""
1232
+ raise NotImplementedError()
1233
+
1234
+ # IDL: GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {}); -> USVString label = ""
1235
+ def create_command_encoder(self, *, label: str = ""):
1236
+ """Create a `GPUCommandEncoder` object. A command
1237
+ encoder is used to record commands, which can then be submitted
1238
+ at once to the GPU.
1239
+
1240
+ Arguments:
1241
+ label (str): A human-readable label. Optional.
1242
+ """
1243
+ raise NotImplementedError()
1244
+
1245
+ # IDL: GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor); -> USVString label = "", required sequence<GPUTextureFormat?> colorFormats, GPUTextureFormat depthStencilFormat, GPUSize32 sampleCount = 1, boolean depthReadOnly = false, boolean stencilReadOnly = false
1246
+ def create_render_bundle_encoder(
1247
+ self,
1248
+ *,
1249
+ label: str = "",
1250
+ color_formats: List[enums.TextureFormat],
1251
+ depth_stencil_format: enums.TextureFormat = optional,
1252
+ sample_count: int = 1,
1253
+ depth_read_only: bool = False,
1254
+ stencil_read_only: bool = False,
1255
+ ):
1256
+ """Create a `GPURenderBundleEncoder` object.
1257
+
1258
+ Render bundles represent a pre-recorded bundle of commands. In cases where the same
1259
+ commands are issued across multiple views or frames, using a rander bundle can improve
1260
+ performance by removing the overhead of repeating the commands.
1261
+
1262
+ Arguments:
1263
+ label (str): A human-readable label. Optional.
1264
+ color_formats (list): A list of the `GPUTextureFormats` of the color attachments for this pass or bundle.
1265
+ depth_stencil_format (GPUTextureFormat): The format of the depth/stencil attachment for this pass or bundle.
1266
+ sample_count (int): The number of samples per pixel in the attachments for this pass or bundle. Default 1.
1267
+ depth_read_only (bool): If true, indicates that the render bundle does not modify the depth component of any depth-stencil attachments. Default False.
1268
+ stencil_read_only (bool): If true, indicates that the render bundle does not modify the stencil component of any depth-stencil attachments. Default False.
1269
+ """
1270
+ raise NotImplementedError()
1271
+
1272
+ # IDL: GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor); -> USVString label = "", required GPUQueryType type, required GPUSize32 count
1273
+ def create_query_set(self, *, label: str = "", type: enums.QueryType, count: int):
1274
+ """Create a `GPUQuerySet` object."""
1275
+ raise NotImplementedError()
1276
+
1277
+ # IDL: undefined pushErrorScope(GPUErrorFilter filter);
1278
+ @apidiff.hide
1279
+ def push_error_scope(self, filter: enums.ErrorFilter):
1280
+ """Pushes a new GPU error scope onto the stack."""
1281
+ raise NotImplementedError()
1282
+
1283
+ # IDL: Promise<GPUError?> popErrorScope();
1284
+ @apidiff.hide
1285
+ def pop_error_scope_sync(self):
1286
+ """Sync version of `pop_error_scope_async().
1287
+
1288
+ Provided by wgpu-py, but not compatible with WebGPU.
1289
+ """
1290
+ raise NotImplementedError()
1291
+
1292
+ # IDL: Promise<GPUError?> popErrorScope();
1293
+ @apidiff.hide
1294
+ async def pop_error_scope_async(self):
1295
+ """Pops a GPU error scope from the stack."""
1296
+ raise NotImplementedError()
1297
+
1298
+ # IDL: GPUExternalTexture importExternalTexture(GPUExternalTextureDescriptor descriptor); -> USVString label = "", required (HTMLVideoElement or VideoFrame) source, PredefinedColorSpace colorSpace = "srgb"
1299
+ @apidiff.hide("Specific to browsers")
1300
+ def import_external_texture(
1301
+ self,
1302
+ *,
1303
+ label: str = "",
1304
+ source: Union[memoryview, object],
1305
+ color_space: str = "srgb",
1306
+ ):
1307
+ """For browsers only."""
1308
+ raise NotImplementedError()
1309
+
1310
+
1311
+ class GPUBuffer(GPUObjectBase):
1312
+ """Represents a block of memory that can be used in GPU operations.
1313
+
1314
+ Data is stored in linear layout, meaning that each byte
1315
+ of the allocation can be addressed by its offset from the start of
1316
+ the buffer, subject to alignment restrictions depending on the
1317
+ operation.
1318
+
1319
+ Create a buffer using `GPUDevice.create_buffer()`.
1320
+
1321
+ One can sync data in a buffer by mapping it and then getting and setting data.
1322
+ Alternatively, one can tell the GPU (via the command encoder) to
1323
+ copy data between buffers and textures.
1324
+ """
1325
+
1326
+ def __init__(self, label, internal, device, size, usage, map_state):
1327
+ self._nbytes = size
1328
+ super().__init__(label, internal, device)
1329
+ self._size = size
1330
+ self._usage = usage
1331
+ self._map_state = map_state
1332
+
1333
+ # IDL: readonly attribute GPUSize64Out size;
1334
+ @property
1335
+ def size(self):
1336
+ """The length of the GPUBuffer allocation in bytes."""
1337
+ return self._size
1338
+
1339
+ # IDL: readonly attribute GPUFlagsConstant usage;
1340
+ @property
1341
+ def usage(self):
1342
+ """The allowed usages (int bitmap) for this GPUBuffer, specifying
1343
+ e.g. whether the buffer may be used as a vertex buffer, uniform buffer,
1344
+ target or source for copying data, etc.
1345
+ """
1346
+ return self._usage
1347
+
1348
+ # IDL: readonly attribute GPUBufferMapState mapState;
1349
+ @property
1350
+ def map_state(self):
1351
+ """The mapping state of the buffer, see `BufferMapState`."""
1352
+ return self._map_state
1353
+
1354
+ # WebGPU specifies an API to sync data with the buffer via mapping.
1355
+ # The idea is to (async) request mapped data, read from / write to
1356
+ # this memory (using getMappedRange), and then unmap. A buffer
1357
+ # must be unmapped before it can be used in a pipeline.
1358
+ #
1359
+ # This means that the mapped memory is reclaimed (i.e. invalid)
1360
+ # when unmap is called, and that whatever object we expose the
1361
+ # memory with to the user, must be set to a state where it can no
1362
+ # longer be used. There does not seem to be a good way to do this.
1363
+ #
1364
+ # In our Python API we do make use of the same map/unmap mechanism,
1365
+ # but reading and writing data goes via method calls instead of via
1366
+ # an array-like object that exposes the shared memory.
1367
+
1368
+ # IDL: Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
1369
+ def map_sync(
1370
+ self, mode: flags.MapMode, offset: int = 0, size: Optional[int] = None
1371
+ ):
1372
+ """Sync version of `map_async()`.
1373
+
1374
+ Provided by wgpu-py, but not compatible with WebGPU.
1375
+ """
1376
+ raise NotImplementedError()
1377
+
1378
+ # IDL: Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
1379
+ async def map_async(
1380
+ self, mode: flags.MapMode, offset: int = 0, size: Optional[int] = None
1381
+ ):
1382
+ """Maps the given range of the GPUBuffer.
1383
+
1384
+ When this call returns, the buffer content is ready to be
1385
+ accessed with ``read_mapped`` or ``write_mapped``. Don't forget
1386
+ to ``unmap()`` when done.
1387
+
1388
+ Arguments:
1389
+ mode (enum): The mapping mode, either wgpu.MapMode.READ or
1390
+ wgpu.MapMode.WRITE, can also be a string.
1391
+ offset (str): the buffer offset in bytes. Default 0.
1392
+ size (int): the size to read. Default until the end.
1393
+
1394
+ Alignment: the offset must be a multiple of 8, the size must be a multiple of 4.
1395
+ """
1396
+ raise NotImplementedError()
1397
+
1398
+ # IDL: undefined unmap();
1399
+ def unmap(self):
1400
+ """Unmaps the buffer.
1401
+
1402
+ Unmaps the mapped range of the GPUBuffer and makes its contents
1403
+ available for use by the GPU again.
1404
+ """
1405
+ raise NotImplementedError()
1406
+
1407
+ @apidiff.add("Replacement for get_mapped_range")
1408
+ def read_mapped(self, buffer_offset=None, size=None, *, copy=True):
1409
+ """Read mapped buffer data.
1410
+
1411
+ This method must only be called when the buffer is in a mapped state.
1412
+ This is the Python alternative to WebGPU's ``getMappedRange``.
1413
+ Returns a memoryview that is a copy of the mapped data (it won't
1414
+ become invalid when the buffer is ummapped).
1415
+
1416
+ Arguments:
1417
+ buffer_offset (int): the buffer offset in bytes. Must be at
1418
+ least as large as the offset specified in ``map()``. The default
1419
+ is the offset of the mapped range.
1420
+ size (int): the size to read (in bytes). The resulting range must fit into the range
1421
+ specified in ``map()``. The default is as large as the mapped range allows.
1422
+ copy (bool): whether a copy of the data is given. Default True.
1423
+ If False, the returned memoryview represents the mapped data
1424
+ directly, and is released when the buffer is unmapped.
1425
+ WARNING: views of the returned data (e.g. memoryview objects or
1426
+ numpy arrays) can still be used after the base memory is released,
1427
+ which can result in corrupted data and segfaults. Therefore, when
1428
+ setting copy to False, make *very* sure the memory is not accessed
1429
+ after the buffer is unmapped.
1430
+
1431
+ Alignment: the buffer offset must be a multiple of 8, the size must be a multiple of 4.
1432
+
1433
+ Also see `GPUBuffer.write_mapped()`, `GPUQueue.read_buffer()` and `GPUQueue.write_buffer()`.
1434
+ """
1435
+ raise NotImplementedError()
1436
+
1437
+ @apidiff.add("Replacement for get_mapped_range")
1438
+ def write_mapped(self, data, buffer_offset=None):
1439
+ """Write mapped buffer data.
1440
+
1441
+ This method must only be called when the buffer is in a mapped state.
1442
+ This is the Python alternative to WebGPU's ``getMappedRange``.
1443
+ Since the data can also be a view into a larger array, this method
1444
+ allows updating the buffer with minimal data copying.
1445
+
1446
+ Arguments:
1447
+ data (buffer-like): The data to write to the buffer.
1448
+ Must be an object that supports the buffer protocol,
1449
+ e.g. bytes, memoryview, numpy array, etc. Does not have to
1450
+ be contiguous, since the data is copied anyway.
1451
+ buffer_offset (int): the buffer offset in bytes. Must be at least
1452
+ as large as the offset specified in ``map()``. The default
1453
+ is the offset of the mapped range.
1454
+
1455
+ Alignment: the buffer offset must be a multiple of 8.
1456
+
1457
+
1458
+ Also see `GPUBuffer.read_mapped, `GPUQueue.read_buffer()` and `GPUQueue.write_buffer()`.
1459
+ """
1460
+ raise NotImplementedError()
1461
+
1462
+ # IDL: ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
1463
+ @apidiff.hide
1464
+ def get_mapped_range(self, offset: int = 0, size: Optional[int] = None):
1465
+ raise NotImplementedError("The Python API differs from WebGPU here")
1466
+
1467
+ # IDL: undefined destroy();
1468
+ def destroy(self):
1469
+ """Destroy the buffer.
1470
+
1471
+ Explicitly destroys the buffer, freeing its memory and putting
1472
+ the object in an unusable state. In general its easier (and
1473
+ safer) to just let the garbage collector do its thing.
1474
+ """
1475
+ raise NotImplementedError()
1476
+
1477
+
1478
+ class GPUTexture(GPUObjectBase):
1479
+ """Represents a 1D, 2D or 3D color image object.
1480
+
1481
+ A texture also can have mipmaps (different levels of varying
1482
+ detail), and arrays. The texture represents the "raw" data. A
1483
+ `GPUTextureView` is used to define how the texture data
1484
+ should be interpreted.
1485
+
1486
+ Create a texture using `GPUDevice.create_texture()`.
1487
+ """
1488
+
1489
+ def __init__(self, label, internal, device, tex_info):
1490
+ self._nbytes = self._estimate_nbytes(tex_info)
1491
+ super().__init__(label, internal, device)
1492
+ self._tex_info = tex_info
1493
+
1494
+ def _estimate_nbytes(self, tex_info):
1495
+ format = tex_info["format"]
1496
+ size = tex_info["size"]
1497
+ sample_count = tex_info["sample_count"] or 1
1498
+ mip_level_count = tex_info["mip_level_count"] or 1
1499
+
1500
+ bpp = texture_format_to_bpp.get(format, 0)
1501
+ npixels = size[0] * size[1] * size[2]
1502
+ nbytes_at_mip_level = sample_count * npixels * bpp / 8
1503
+
1504
+ nbytes = 0
1505
+ for i in range(mip_level_count):
1506
+ nbytes += nbytes_at_mip_level
1507
+ nbytes_at_mip_level /= 2
1508
+
1509
+ # Return rounded to nearest integer
1510
+ return int(nbytes + 0.5)
1511
+
1512
+ @apidiff.add("Too useful to not-have")
1513
+ @property
1514
+ def size(self):
1515
+ """The size of the texture in mipmap level 0, as a 3-tuple of ints."""
1516
+ return self._tex_info["size"]
1517
+
1518
+ # IDL: readonly attribute GPUIntegerCoordinateOut width;
1519
+ @property
1520
+ def width(self):
1521
+ """The texture's width. Also see ``.size``."""
1522
+ return self._tex_info["size"][0]
1523
+
1524
+ # IDL: readonly attribute GPUIntegerCoordinateOut height;
1525
+ @property
1526
+ def height(self):
1527
+ """The texture's height. Also see ``.size``."""
1528
+ return self._tex_info["size"][1]
1529
+
1530
+ # IDL: readonly attribute GPUIntegerCoordinateOut depthOrArrayLayers;
1531
+ @property
1532
+ def depth_or_array_layers(self):
1533
+ """The texture's depth or number of layers. Also see ``.size``."""
1534
+ return self._tex_info["size"][2]
1535
+
1536
+ # IDL: readonly attribute GPUIntegerCoordinateOut mipLevelCount;
1537
+ @property
1538
+ def mip_level_count(self):
1539
+ """The total number of the mipmap levels of the texture."""
1540
+ return self._tex_info["mip_level_count"]
1541
+
1542
+ # IDL: readonly attribute GPUSize32Out sampleCount;
1543
+ @property
1544
+ def sample_count(self):
1545
+ """The number of samples in each texel of the texture."""
1546
+ return self._tex_info["sample_count"]
1547
+
1548
+ # IDL: readonly attribute GPUTextureDimension dimension;
1549
+ @property
1550
+ def dimension(self):
1551
+ """The dimension of the texture."""
1552
+ return self._tex_info["dimension"]
1553
+
1554
+ # IDL: readonly attribute GPUTextureFormat format;
1555
+ @property
1556
+ def format(self):
1557
+ """The format of the texture."""
1558
+ return self._tex_info["format"]
1559
+
1560
+ # IDL: readonly attribute GPUFlagsConstant usage;
1561
+ @property
1562
+ def usage(self):
1563
+ """The allowed usages for this texture."""
1564
+ return self._tex_info["usage"]
1565
+
1566
+ # IDL: GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {}); -> USVString label = "", GPUTextureFormat format, GPUTextureViewDimension dimension, GPUTextureAspect aspect = "all", GPUIntegerCoordinate baseMipLevel = 0, GPUIntegerCoordinate mipLevelCount, GPUIntegerCoordinate baseArrayLayer = 0, GPUIntegerCoordinate arrayLayerCount
1567
+ def create_view(
1568
+ self,
1569
+ *,
1570
+ label: str = "",
1571
+ format: enums.TextureFormat = optional,
1572
+ dimension: enums.TextureViewDimension = optional,
1573
+ aspect: enums.TextureAspect = "all",
1574
+ base_mip_level: int = 0,
1575
+ mip_level_count: int = optional,
1576
+ base_array_layer: int = 0,
1577
+ array_layer_count: int = optional,
1578
+ ):
1579
+ """Create a `GPUTextureView` object.
1580
+
1581
+ If no arguments are given, a default view is given, with the
1582
+ same format and dimension as the texture.
1583
+
1584
+ Arguments:
1585
+ label (str): A human-readable label. Optional.
1586
+ format (enums.TextureFormat): What channels it stores and how.
1587
+ dimension (enums.TextureViewDimension): The dimensionality of the texture view.
1588
+ aspect (enums.TextureAspect): Whether this view is used for depth, stencil, or all.
1589
+ Default all.
1590
+ base_mip_level (int): The starting mip level. Default 0.
1591
+ mip_level_count (int): The number of mip levels. Default None.
1592
+ base_array_layer (int): The starting array layer. Default 0.
1593
+ array_layer_count (int): The number of array layers. Default None.
1594
+ """
1595
+ raise NotImplementedError()
1596
+
1597
+ # IDL: undefined destroy();
1598
+ def destroy(self):
1599
+ """Destroy the texture.
1600
+
1601
+ Explicitly destroys the texture, freeing its memory and putting
1602
+ the object in an unusable state. In general its easier (and
1603
+ safer) to just let the garbage collector do its thing.
1604
+ """
1605
+ raise NotImplementedError()
1606
+
1607
+
1608
+ class GPUTextureView(GPUObjectBase):
1609
+ """Represents a way to represent a `GPUTexture`.
1610
+
1611
+ Create a texture view using `GPUTexture.create_view()`.
1612
+ """
1613
+
1614
+ def __init__(self, label, internal, device, texture, size):
1615
+ super().__init__(label, internal, device)
1616
+ self._texture = texture
1617
+ self._size = size
1618
+
1619
+ @apidiff.add("Need to know size e.g. for texture view provided by canvas")
1620
+ @property
1621
+ def size(self):
1622
+ """The texture size (as a 3-tuple)."""
1623
+ return self._size
1624
+
1625
+ @apidiff.add("Too useful to not-have")
1626
+ @property
1627
+ def texture(self):
1628
+ """The texture object to which this is a view."""
1629
+ return self._texture
1630
+
1631
+
1632
+ class GPUSampler(GPUObjectBase):
1633
+ """Defines how a texture (view) must be sampled by the shader.
1634
+
1635
+ It defines the subsampling, sampling between mip levels, and sampling out
1636
+ of the image boundaries.
1637
+
1638
+ Create a sampler using `GPUDevice.create_sampler()`.
1639
+ """
1640
+
1641
+
1642
+ class GPUBindGroupLayout(GPUObjectBase):
1643
+ """Defines the interface between a set of resources bound in a `GPUBindGroup`.
1644
+
1645
+ It also defines their accessibility in shader stages.
1646
+
1647
+ Create a bind group layout using `GPUDevice.create_bind_group_layout()`.
1648
+ """
1649
+
1650
+ pass
1651
+
1652
+
1653
+ class GPUBindGroup(GPUObjectBase):
1654
+ """Represents a group of resource bindings (buffer, sampler, texture-view).
1655
+
1656
+ It holds the shader slot and a reference to the resource (sampler,
1657
+ texture-view, buffer).
1658
+
1659
+ Create a bind group using `GPUDevice.create_bind_group()`.
1660
+ """
1661
+
1662
+ pass
1663
+
1664
+
1665
+ class GPUPipelineLayout(GPUObjectBase):
1666
+ """Describes the layout of a pipeline, as a list of `GPUBindGroupLayout` objects.
1667
+
1668
+ Create a pipeline layout using `GPUDevice.create_pipeline_layout()`.
1669
+ """
1670
+
1671
+ pass
1672
+
1673
+
1674
+ class GPUShaderModule(GPUObjectBase):
1675
+ """Represents a programmable shader.
1676
+
1677
+ Create a shader module using `GPUDevice.create_shader_module()`.
1678
+ """
1679
+
1680
+ # IDL: Promise<GPUCompilationInfo> getCompilationInfo();
1681
+ def get_compilation_info_sync(self):
1682
+ """Sync version of `get_compilation_info_async()`.
1683
+
1684
+ Provided by wgpu-py, but not compatible with WebGPU.
1685
+ """
1686
+ raise NotImplementedError()
1687
+
1688
+ # IDL: Promise<GPUCompilationInfo> getCompilationInfo();
1689
+ async def get_compilation_info_async(self):
1690
+ """Get shader compilation info. Always returns empty list at the moment."""
1691
+ # How can this return shader errors if one cannot create a
1692
+ # shader module when the shader source has errors?
1693
+ raise NotImplementedError()
1694
+
1695
+
1696
+ class GPUPipelineBase:
1697
+ """A mixin class for render and compute pipelines."""
1698
+
1699
+ # IDL: [NewObject] GPUBindGroupLayout getBindGroupLayout(unsigned long index);
1700
+ def get_bind_group_layout(self, index: int):
1701
+ """Get the bind group layout at the given index."""
1702
+ raise NotImplementedError()
1703
+
1704
+
1705
+ class GPUComputePipeline(GPUPipelineBase, GPUObjectBase):
1706
+ """Represents a single pipeline for computations (no rendering).
1707
+
1708
+ Create a compute pipeline using `GPUDevice.create_compute_pipeline()`.
1709
+ """
1710
+
1711
+
1712
+ class GPURenderPipeline(GPUPipelineBase, GPUObjectBase):
1713
+ """Represents a single pipeline to draw something.
1714
+
1715
+ The rendering typically involves a vertex and fragment stage, though
1716
+ the latter is optional.
1717
+ The render target can come from a window on the screen or from an
1718
+ in-memory texture (off-screen rendering).
1719
+
1720
+ Create a render pipeline using `GPUDevice.create_render_pipeline()`.
1721
+ """
1722
+
1723
+
1724
+ class GPUCommandBuffer(GPUObjectBase):
1725
+ """Stores a series of commands generated by a `GPUCommandEncoder`.
1726
+
1727
+ The buffered commands can subsequently be submitted to a `GPUQueue`.
1728
+
1729
+ Command buffers are single use, you must only submit them once and
1730
+ submitting them destroys them. Use render bundles to re-use commands.
1731
+
1732
+ Create a command buffer using `GPUCommandEncoder.finish()`.
1733
+ """
1734
+
1735
+
1736
+ class GPUCommandsMixin:
1737
+ """Mixin for classes that encode commands."""
1738
+
1739
+ pass
1740
+
1741
+
1742
+ class GPUBindingCommandsMixin:
1743
+ """Mixin for classes that defines bindings."""
1744
+
1745
+ # IDL: undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup, Uint32Array dynamicOffsetsData, GPUSize64 dynamicOffsetsDataStart, GPUSize32 dynamicOffsetsDataLength);
1746
+ @apidiff.change(
1747
+ "In the WebGPU specification, this method has two different signatures."
1748
+ )
1749
+ def set_bind_group(
1750
+ self,
1751
+ index,
1752
+ bind_group,
1753
+ dynamic_offsets_data=[],
1754
+ dynamic_offsets_data_start=None,
1755
+ dynamic_offsets_data_length=None,
1756
+ ):
1757
+ """Associate the given bind group (i.e. group or resources) with the
1758
+ given slot/index.
1759
+
1760
+ Arguments:
1761
+ index (int): The slot to bind at.
1762
+ bind_group (GPUBindGroup): The bind group to bind.
1763
+ dynamic_offsets_data (list of int): A list of offsets (one for each entry in bind group marked as ``buffer.has_dynamic_offset``). Default ``[]``.
1764
+ dynamic_offsets_data_start (int): Offset in elements into dynamic_offsets_data where the buffer offset data begins. Default None.
1765
+ dynamic_offsets_data_length (int): Number of buffer offsets to read from dynamic_offsets_data. Default None.
1766
+ """
1767
+ raise NotImplementedError()
1768
+
1769
+
1770
+ class GPUDebugCommandsMixin:
1771
+ """Mixin for classes that support debug groups and markers."""
1772
+
1773
+ # IDL: undefined pushDebugGroup(USVString groupLabel);
1774
+ def push_debug_group(self, group_label: str):
1775
+ """Push a named debug group into the command stream."""
1776
+ raise NotImplementedError()
1777
+
1778
+ # IDL: undefined popDebugGroup();
1779
+ def pop_debug_group(self):
1780
+ """Pop the active debug group."""
1781
+ raise NotImplementedError()
1782
+
1783
+ # IDL: undefined insertDebugMarker(USVString markerLabel);
1784
+ def insert_debug_marker(self, marker_label: str):
1785
+ """Insert the given message into the debug message queue."""
1786
+ raise NotImplementedError()
1787
+
1788
+
1789
+ class GPURenderCommandsMixin:
1790
+ """Mixin for classes that provide rendering commands."""
1791
+
1792
+ # IDL: undefined setPipeline(GPURenderPipeline pipeline);
1793
+ def set_pipeline(self, pipeline: GPURenderPipeline):
1794
+ """Set the pipeline for this render pass.
1795
+
1796
+ Arguments:
1797
+ pipeline (GPURenderPipeline): The pipeline to use.
1798
+ """
1799
+ raise NotImplementedError()
1800
+
1801
+ # IDL: undefined setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size);
1802
+ def set_index_buffer(
1803
+ self,
1804
+ buffer: GPUBuffer,
1805
+ index_format: enums.IndexFormat,
1806
+ offset: int = 0,
1807
+ size: Optional[int] = None,
1808
+ ):
1809
+ """Set the index buffer for this render pass.
1810
+
1811
+ Arguments:
1812
+ buffer (GPUBuffer): The buffer that contains the indices.
1813
+ index_format (GPUIndexFormat): The format of the index data
1814
+ contained in buffer. If `strip_index_format` is given in the
1815
+ call to `GPUDevice.create_render_pipeline()`, it must match.
1816
+ offset (int): The byte offset in the buffer. Default 0.
1817
+ size (int): The number of bytes to use. If zero, the remaining size
1818
+ (after offset) of the buffer is used. Default 0.
1819
+ """
1820
+ raise NotImplementedError()
1821
+
1822
+ # IDL: undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer? buffer, optional GPUSize64 offset = 0, optional GPUSize64 size);
1823
+ def set_vertex_buffer(
1824
+ self, slot: int, buffer: GPUBuffer, offset: int = 0, size: Optional[int] = None
1825
+ ):
1826
+ """Associate a vertex buffer with a bind slot.
1827
+
1828
+ Arguments:
1829
+ slot (int): The binding slot for the vertex buffer.
1830
+ buffer (GPUBuffer): The buffer that contains the vertex data.
1831
+ offset (int): The byte offset in the buffer. Default 0.
1832
+ size (int): The number of bytes to use. If zero, the remaining size
1833
+ (after offset) of the buffer is used. Default 0.
1834
+
1835
+ Alignment: the offset must be a multiple of 4.
1836
+ """
1837
+ raise NotImplementedError()
1838
+
1839
+ # IDL: undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1, optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0);
1840
+ def draw(
1841
+ self,
1842
+ vertex_count: int,
1843
+ instance_count: int = 1,
1844
+ first_vertex: int = 0,
1845
+ first_instance: int = 0,
1846
+ ):
1847
+ """Run the render pipeline without an index buffer.
1848
+
1849
+ Arguments:
1850
+ vertex_count (int): The number of vertices to draw.
1851
+ instance_count (int): The number of instances to draw. Default 1.
1852
+ first_vertex (int): The vertex offset. Default 0.
1853
+ first_instance (int): The instance offset. Default 0.
1854
+ """
1855
+ raise NotImplementedError()
1856
+
1857
+ # IDL: undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
1858
+ def draw_indirect(self, indirect_buffer: GPUBuffer, indirect_offset: int):
1859
+ """Like `draw()`, but the function arguments are in a buffer.
1860
+
1861
+ Arguments:
1862
+ indirect_buffer (GPUBuffer): The buffer that contains the arguments.
1863
+ indirect_offset (int): The byte offset at which the arguments are.
1864
+
1865
+ Alignment: the indirect offset must be a multiple of 4.
1866
+ """
1867
+ raise NotImplementedError()
1868
+
1869
+ # IDL: undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1, optional GPUSize32 firstIndex = 0, optional GPUSignedOffset32 baseVertex = 0, optional GPUSize32 firstInstance = 0);
1870
+ def draw_indexed(
1871
+ self,
1872
+ index_count: int,
1873
+ instance_count: int = 1,
1874
+ first_index: int = 0,
1875
+ base_vertex: int = 0,
1876
+ first_instance: int = 0,
1877
+ ):
1878
+ """Run the render pipeline using an index buffer.
1879
+
1880
+ Arguments:
1881
+ index_count (int): The number of indices to draw.
1882
+ instance_count (int): The number of instances to draw. Default 1.
1883
+ first_index (int): The index offset. Default 0.
1884
+ base_vertex (int): A number added to each index in the index buffer. Default 0.
1885
+ first_instance (int): The instance offset. Default 0.
1886
+
1887
+ Alignment: the indirect offset must be a multiple of 4.
1888
+ """
1889
+ raise NotImplementedError()
1890
+
1891
+ # IDL: undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
1892
+ def draw_indexed_indirect(self, indirect_buffer: GPUBuffer, indirect_offset: int):
1893
+ """
1894
+ Like `draw_indexed()`, but the function arguments are in a buffer.
1895
+
1896
+ Arguments:
1897
+ indirect_buffer (GPUBuffer): The buffer that contains the arguments.
1898
+ indirect_offset (int): The byte offset at which the arguments are.
1899
+ """
1900
+ raise NotImplementedError()
1901
+
1902
+
1903
+ class GPUCommandEncoder(GPUCommandsMixin, GPUDebugCommandsMixin, GPUObjectBase):
1904
+ """Object to record a series of commands.
1905
+
1906
+ When done, call `finish()` to obtain a `GPUCommandBuffer` object.
1907
+
1908
+ Create a command encoder using `GPUDevice.create_command_encoder()`.
1909
+ """
1910
+
1911
+ # IDL: GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {}); -> USVString label = "", GPUComputePassTimestampWrites timestampWrites
1912
+ def begin_compute_pass(
1913
+ self,
1914
+ *,
1915
+ label: str = "",
1916
+ timestamp_writes: structs.ComputePassTimestampWrites = optional,
1917
+ ):
1918
+ """Record the beginning of a compute pass. Returns a
1919
+ `GPUComputePassEncoder` object.
1920
+
1921
+ Arguments:
1922
+ label (str): A human-readable label. Optional.
1923
+ timestamp_writes: unused
1924
+ """
1925
+ raise NotImplementedError()
1926
+
1927
+ # IDL: GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor); -> USVString label = "", required sequence<GPURenderPassColorAttachment?> colorAttachments, GPURenderPassDepthStencilAttachment depthStencilAttachment, GPUQuerySet occlusionQuerySet, GPURenderPassTimestampWrites timestampWrites, GPUSize64 maxDrawCount = 50000000
1928
+ def begin_render_pass(
1929
+ self,
1930
+ *,
1931
+ label: str = "",
1932
+ color_attachments: List[structs.RenderPassColorAttachment],
1933
+ depth_stencil_attachment: structs.RenderPassDepthStencilAttachment = optional,
1934
+ occlusion_query_set: GPUQuerySet = optional,
1935
+ timestamp_writes: structs.RenderPassTimestampWrites = optional,
1936
+ max_draw_count: int = 50000000,
1937
+ ):
1938
+ """Record the beginning of a render pass. Returns a
1939
+ `GPURenderPassEncoder` object.
1940
+
1941
+ Arguments:
1942
+ label (str): A human-readable label. Optional.
1943
+ color_attachments (list): List of `structs.RenderPassColorAttachment` dicts.
1944
+ depth_stencil_attachment (structs.RenderPassDepthStencilAttachment): Describes the depth stencil attachment. Default None.
1945
+ occlusion_query_set (GPUQuerySet): Default None.
1946
+ timestamp_writes: unused
1947
+ """
1948
+ raise NotImplementedError()
1949
+
1950
+ # IDL: undefined clearBuffer( GPUBuffer buffer, optional GPUSize64 offset = 0, optional GPUSize64 size);
1951
+ def clear_buffer(
1952
+ self, buffer: GPUBuffer, offset: int = 0, size: Optional[int] = None
1953
+ ):
1954
+ """Set (part of) the given buffer to zeros.
1955
+
1956
+ Arguments:
1957
+ buffer (GPUBuffer): The buffer to clear.
1958
+ offset (int): The byte offset.
1959
+ size (int, optional): The size to clear in bytes. If None, the effective size is the full size minus the offset.
1960
+
1961
+ Alignment: both offset and size must be a multiple of 4.
1962
+ """
1963
+ raise NotImplementedError()
1964
+
1965
+ # IDL: undefined copyBufferToBuffer( GPUBuffer source, GPUSize64 sourceOffset, GPUBuffer destination, GPUSize64 destinationOffset, GPUSize64 size);
1966
+ def copy_buffer_to_buffer(
1967
+ self,
1968
+ source: GPUBuffer,
1969
+ source_offset: int,
1970
+ destination: GPUBuffer,
1971
+ destination_offset: int,
1972
+ size: int,
1973
+ ):
1974
+ """Copy the contents of a buffer to another buffer.
1975
+
1976
+ Arguments:
1977
+ source (GPUBuffer): The source buffer.
1978
+ source_offset (int): The byte offset.
1979
+ destination (GPUBuffer): The target buffer.
1980
+ destination_offset (int): The byte offset in the destination buffer.
1981
+ size (int): The number of bytes to copy.
1982
+
1983
+ Alignment: the size, source offset, and destination offset must all be a multiple of 4.
1984
+ """
1985
+ raise NotImplementedError()
1986
+
1987
+ # IDL: undefined copyBufferToTexture( GPUImageCopyBuffer source, GPUImageCopyTexture destination, GPUExtent3D copySize);
1988
+ def copy_buffer_to_texture(
1989
+ self,
1990
+ source: structs.ImageCopyBuffer,
1991
+ destination: structs.ImageCopyTexture,
1992
+ copy_size: Union[List[int], structs.Extent3D],
1993
+ ):
1994
+ """Copy the contents of a buffer to a texture (view).
1995
+
1996
+ Arguments:
1997
+ source (GPUBuffer): A dict with fields: buffer, offset, bytes_per_row, rows_per_image.
1998
+ destination (GPUTexture): A dict with fields: texture, mip_level, origin.
1999
+ copy_size (int): The number of bytes to copy.
2000
+
2001
+ Alignment: the ``bytes_per_row`` must be a multiple of 256.
2002
+ """
2003
+ raise NotImplementedError()
2004
+
2005
+ # IDL: undefined copyTextureToBuffer( GPUImageCopyTexture source, GPUImageCopyBuffer destination, GPUExtent3D copySize);
2006
+ def copy_texture_to_buffer(
2007
+ self,
2008
+ source: structs.ImageCopyTexture,
2009
+ destination: structs.ImageCopyBuffer,
2010
+ copy_size: Union[List[int], structs.Extent3D],
2011
+ ):
2012
+ """Copy the contents of a texture (view) to a buffer.
2013
+
2014
+ Arguments:
2015
+ source (GPUTexture): A dict with fields: texture, mip_level, origin.
2016
+ destination (GPUBuffer): A dict with fields: buffer, offset, bytes_per_row, rows_per_image.
2017
+ copy_size (int): The number of bytes to copy.
2018
+
2019
+ Alignment: the ``bytes_per_row`` must be a multiple of 256.
2020
+ """
2021
+ raise NotImplementedError()
2022
+
2023
+ # IDL: undefined copyTextureToTexture( GPUImageCopyTexture source, GPUImageCopyTexture destination, GPUExtent3D copySize);
2024
+ def copy_texture_to_texture(
2025
+ self,
2026
+ source: structs.ImageCopyTexture,
2027
+ destination: structs.ImageCopyTexture,
2028
+ copy_size: Union[List[int], structs.Extent3D],
2029
+ ):
2030
+ """Copy the contents of a texture (view) to another texture (view).
2031
+
2032
+ Arguments:
2033
+ source (GPUTexture): A dict with fields: texture, mip_level, origin.
2034
+ destination (GPUTexture): A dict with fields: texture, mip_level, origin.
2035
+ copy_size (int): The number of bytes to copy.
2036
+ """
2037
+ raise NotImplementedError()
2038
+
2039
+ # IDL: GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {}); -> USVString label = ""
2040
+ def finish(self, *, label: str = ""):
2041
+ """Finish recording. Returns a `GPUCommandBuffer` to
2042
+ submit to a `GPUQueue`.
2043
+
2044
+ Arguments:
2045
+ label (str): A human-readable label. Optional.
2046
+ """
2047
+ raise NotImplementedError()
2048
+
2049
+ # IDL: undefined resolveQuerySet( GPUQuerySet querySet, GPUSize32 firstQuery, GPUSize32 queryCount, GPUBuffer destination, GPUSize64 destinationOffset);
2050
+ def resolve_query_set(
2051
+ self,
2052
+ query_set: GPUQuerySet,
2053
+ first_query: int,
2054
+ query_count: int,
2055
+ destination: GPUBuffer,
2056
+ destination_offset: int,
2057
+ ):
2058
+ """
2059
+ Resolves query results from a ``GPUQuerySet`` out into a range of a ``GPUBuffer``.
2060
+
2061
+ Arguments:
2062
+ query_set (GPUQuerySet): The source query set.
2063
+ first_query (int): The first query to resolve.
2064
+ query_count (int): The amount of queries to resolve.
2065
+ destination (GPUBuffer): The buffer to write the results to.
2066
+ destination_offset (int): The byte offset in the buffer.
2067
+
2068
+ Alignment: the destination offset must be a multiple of 256.
2069
+ """
2070
+ raise NotImplementedError()
2071
+
2072
+
2073
+ class GPUComputePassEncoder(
2074
+ GPUCommandsMixin, GPUDebugCommandsMixin, GPUBindingCommandsMixin, GPUObjectBase
2075
+ ):
2076
+ """Object to records commands for a compute pass.
2077
+
2078
+ Create a compute pass encoder using `GPUCommandEncoder.begin_compute_pass()`.
2079
+ """
2080
+
2081
+ # IDL: undefined setPipeline(GPUComputePipeline pipeline);
2082
+ def set_pipeline(self, pipeline: GPUComputePipeline):
2083
+ """Set the pipeline for this compute pass.
2084
+
2085
+ Arguments:
2086
+ pipeline (GPUComputePipeline): The pipeline to use.
2087
+ """
2088
+ raise NotImplementedError()
2089
+
2090
+ # IDL: undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1);
2091
+ def dispatch_workgroups(
2092
+ self,
2093
+ workgroup_count_x: int,
2094
+ workgroup_count_y: int = 1,
2095
+ workgroup_count_z: int = 1,
2096
+ ):
2097
+ """Run the compute shader.
2098
+
2099
+ Arguments:
2100
+ x (int): The number of cycles in index x.
2101
+ y (int): The number of cycles in index y. Default 1.
2102
+ z (int): The number of cycles in index z. Default 1.
2103
+ """
2104
+ raise NotImplementedError()
2105
+
2106
+ # IDL: undefined dispatchWorkgroupsIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
2107
+ def dispatch_workgroups_indirect(
2108
+ self, indirect_buffer: GPUBuffer, indirect_offset: int
2109
+ ):
2110
+ """Like `dispatch_workgroups()`, but the function arguments are in a buffer.
2111
+
2112
+ Arguments:
2113
+ indirect_buffer (GPUBuffer): The buffer that contains the arguments.
2114
+ indirect_offset (int): The byte offset at which the arguments are.
2115
+ """
2116
+ raise NotImplementedError()
2117
+
2118
+ # IDL: undefined end();
2119
+ def end(self):
2120
+ """Record the end of the compute pass."""
2121
+ raise NotImplementedError()
2122
+
2123
+
2124
+ class GPURenderPassEncoder(
2125
+ GPUCommandsMixin,
2126
+ GPUDebugCommandsMixin,
2127
+ GPUBindingCommandsMixin,
2128
+ GPURenderCommandsMixin,
2129
+ GPUObjectBase,
2130
+ ):
2131
+ """Object to records commands for a render pass.
2132
+
2133
+ Create a render pass encoder using `GPUCommandEncoder.begin_render_pass`.
2134
+ """
2135
+
2136
+ # IDL: undefined setViewport(float x, float y, float width, float height, float minDepth, float maxDepth);
2137
+ def set_viewport(
2138
+ self,
2139
+ x: float,
2140
+ y: float,
2141
+ width: float,
2142
+ height: float,
2143
+ min_depth: float,
2144
+ max_depth: float,
2145
+ ):
2146
+ """Set the viewport for this render pass. The whole scene is rendered
2147
+ to this sub-rectangle.
2148
+
2149
+ Arguments:
2150
+ x (int): Horizontal coordinate.
2151
+ y (int): Vertical coordinate.
2152
+ width (int): Horizontal size.
2153
+ height (int): Vertical size.
2154
+ min_depth (int): Clipping in depth.
2155
+ max_depth (int): Clipping in depth.
2156
+
2157
+ """
2158
+ raise NotImplementedError()
2159
+
2160
+ # IDL: undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y, GPUIntegerCoordinate width, GPUIntegerCoordinate height);
2161
+ def set_scissor_rect(self, x: int, y: int, width: int, height: int):
2162
+ """Set the scissor rectangle for this render pass. The scene
2163
+ is rendered as usual, but is only applied to this sub-rectangle.
2164
+
2165
+ Arguments:
2166
+ x (int): Horizontal coordinate.
2167
+ y (int): Vertical coordinate.
2168
+ width (int): Horizontal size.
2169
+ height (int): Vertical size.
2170
+ """
2171
+ raise NotImplementedError()
2172
+
2173
+ # IDL: undefined setBlendConstant(GPUColor color);
2174
+ def set_blend_constant(self, color: Union[List[float], structs.Color]):
2175
+ """Set the blend color for the render pass.
2176
+
2177
+ Arguments:
2178
+ color (tuple or dict): A color with fields (r, g, b, a).
2179
+ """
2180
+ raise NotImplementedError()
2181
+
2182
+ # IDL: undefined setStencilReference(GPUStencilValue reference);
2183
+ def set_stencil_reference(self, reference: int):
2184
+ """Set the reference stencil value for this render pass.
2185
+
2186
+ Arguments:
2187
+ reference (int): The reference value.
2188
+ """
2189
+ raise NotImplementedError()
2190
+
2191
+ # IDL: undefined executeBundles(sequence<GPURenderBundle> bundles);
2192
+ def execute_bundles(self, bundles: List[GPURenderBundle]):
2193
+ """Executes commands previously recorded into the render bundles
2194
+ as part of this render pass.
2195
+
2196
+ Arguments:
2197
+ bundles (Sequence[GPURenderBundle]): A sequence of Render Bundle objects.
2198
+ """
2199
+ raise NotImplementedError()
2200
+
2201
+ # IDL: undefined end();
2202
+ def end(self):
2203
+ """Record the end of the render pass."""
2204
+ raise NotImplementedError()
2205
+
2206
+ # IDL: undefined beginOcclusionQuery(GPUSize32 queryIndex);
2207
+ def begin_occlusion_query(self, query_index: int):
2208
+ """Begins an occlusion query.
2209
+
2210
+ Arguments:
2211
+ query_index (int): The index in the GPUQuerySet at which to write the
2212
+ result of the occlusion query. The Query Set is specified as the
2213
+ occlusion_query_set argument in begin_render_pass().
2214
+ """
2215
+
2216
+ raise NotImplementedError()
2217
+
2218
+ # IDL: undefined endOcclusionQuery();
2219
+ def end_occlusion_query(self):
2220
+ """Ends an occlusion query."""
2221
+ raise NotImplementedError()
2222
+
2223
+
2224
+ class GPURenderBundle(GPUObjectBase):
2225
+ """A reusable bundle of render commands."""
2226
+
2227
+ pass
2228
+
2229
+
2230
+ class GPURenderBundleEncoder(
2231
+ GPUCommandsMixin,
2232
+ GPUDebugCommandsMixin,
2233
+ GPUBindingCommandsMixin,
2234
+ GPURenderCommandsMixin,
2235
+ GPUObjectBase,
2236
+ ):
2237
+ """Encodes a series of render commands into a reusable render bundle."""
2238
+
2239
+ # IDL: GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {}); -> USVString label = ""
2240
+ def finish(self, *, label: str = ""):
2241
+ """Finish recording and return a `GPURenderBundle`.
2242
+
2243
+ Arguments:
2244
+ label (str): A human-readable label. Optional.
2245
+ """
2246
+ raise NotImplementedError()
2247
+
2248
+
2249
+ class GPUQueue(GPUObjectBase):
2250
+ """Object to submit command buffers to.
2251
+
2252
+ You can obtain a queue object via the :attr:`GPUDevice.queue` property.
2253
+ """
2254
+
2255
+ # IDL: undefined submit(sequence<GPUCommandBuffer> commandBuffers);
2256
+ def submit(self, command_buffers: List[GPUCommandBuffer]):
2257
+ """Submit a `GPUCommandBuffer` to the queue.
2258
+
2259
+ Arguments:
2260
+ command_buffers (list): The `GPUCommandBuffer` objects to add.
2261
+ """
2262
+ raise NotImplementedError()
2263
+
2264
+ # IDL: undefined writeBuffer( GPUBuffer buffer, GPUSize64 bufferOffset, AllowSharedBufferSource data, optional GPUSize64 dataOffset = 0, optional GPUSize64 size);
2265
+ def write_buffer(
2266
+ self,
2267
+ buffer: GPUBuffer,
2268
+ buffer_offset: int,
2269
+ data: memoryview,
2270
+ data_offset: int = 0,
2271
+ size: Optional[int] = None,
2272
+ ):
2273
+ """Takes the data contents and schedules a write operation to the buffer.
2274
+
2275
+ Changes to the data after this function is called don't affect
2276
+ the buffer contents.
2277
+
2278
+ Arguments:
2279
+ buffer: The `GPUBuffer` object to write to.
2280
+ buffer_offset (int): The offset in the buffer to start writing at.
2281
+ data: The data to write to the buffer. Must be an object that supports
2282
+ the buffer protocol, e.g. bytes, memoryview, numpy array, etc.
2283
+ Must be contiguous.
2284
+ data_offset: The offset in the data, in elements. Default 0.
2285
+ size: The number of bytes to write. Default all minus offset.
2286
+
2287
+ This maps the data to a temporary buffer and then copies that buffer
2288
+ to the given buffer. The given buffer's usage must include COPY_DST.
2289
+
2290
+ Alignment: the buffer offset must be a multiple of 4, the total size to write must be a multiple of 4 bytes.
2291
+
2292
+ Also see `GPUBuffer.map_sync()` and `GPUBuffer.map_async()`.
2293
+
2294
+ """
2295
+ raise NotImplementedError()
2296
+
2297
+ @apidiff.add("For symmetry with queue.write_buffer")
2298
+ def read_buffer(self, buffer, buffer_offset=0, size=None):
2299
+ """Takes the data contents of the buffer and return them as a memoryview.
2300
+
2301
+ Arguments:
2302
+ buffer: The `GPUBuffer` object to read from.
2303
+ buffer_offset (int): The offset in the buffer to start reading from.
2304
+ size: The number of bytes to read. Default all minus offset.
2305
+
2306
+ This copies the data in the given buffer to a temporary buffer
2307
+ and then maps that buffer to read the data. The given buffer's
2308
+ usage must include COPY_SRC.
2309
+
2310
+ Also see `GPUBuffer._sync()` and `GPUBuffer._async()`.
2311
+ """
2312
+ raise NotImplementedError()
2313
+
2314
+ # IDL: undefined writeTexture( GPUImageCopyTexture destination, AllowSharedBufferSource data, GPUImageDataLayout dataLayout, GPUExtent3D size);
2315
+ def write_texture(
2316
+ self,
2317
+ destination: structs.ImageCopyTexture,
2318
+ data: memoryview,
2319
+ data_layout: structs.ImageDataLayout,
2320
+ size: Union[List[int], structs.Extent3D],
2321
+ ):
2322
+ """Takes the data contents and schedules a write operation of
2323
+ these contents to the destination texture in the queue. A
2324
+ snapshot of the data is taken; any changes to the data after
2325
+ this function is called do not affect the texture contents.
2326
+
2327
+ Arguments:
2328
+ destination: A dict with fields: "texture" (a texture object),
2329
+ "origin" (a 3-tuple), "mip_level" (an int, default 0).
2330
+ data: The data to write to the texture. Must be an object that supports
2331
+ the buffer protocol, e.g. bytes, memoryview, numpy array, etc.
2332
+ Must be contiguous.
2333
+ data_layout: A dict with fields: "offset" (an int, default 0),
2334
+ "bytes_per_row" (an int), "rows_per_image" (an int, default 0).
2335
+ size: A 3-tuple of ints specifying the size to write.
2336
+
2337
+ Unlike `GPUCommandEncoder.copyBufferToTexture()`, there is
2338
+ no alignment requirement on `bytes_per_row`.
2339
+ """
2340
+ raise NotImplementedError()
2341
+
2342
+ @apidiff.add("For symmetry, and to help work around the bytes_per_row constraint")
2343
+ def read_texture(self, source, data_layout, size):
2344
+ """Reads the contents of the texture and return them as a memoryview.
2345
+
2346
+ Arguments:
2347
+ source: A dict with fields: "texture" (a texture object),
2348
+ "origin" (a 3-tuple), "mip_level" (an int, default 0).
2349
+ data_layout: A dict with fields: "offset" (an int, default 0),
2350
+ "bytes_per_row" (an int), "rows_per_image" (an int, default 0).
2351
+ size: A 3-tuple of ints specifying the size to write.
2352
+
2353
+ Unlike `GPUCommandEncoder.copyBufferToTexture()`, there is
2354
+ no alignment requirement on `bytes_per_row`, although in the
2355
+ current implementation there will be a performance penalty if
2356
+ ``bytes_per_row`` is not a multiple of 256 (because we'll be
2357
+ copying data row-by-row in Python).
2358
+ """
2359
+ raise NotImplementedError()
2360
+
2361
+ # IDL: undefined copyExternalImageToTexture( GPUImageCopyExternalImage source, GPUImageCopyTextureTagged destination, GPUExtent3D copySize);
2362
+ @apidiff.hide("Specific to browsers")
2363
+ def copy_external_image_to_texture(
2364
+ self,
2365
+ source: structs.ImageCopyExternalImage,
2366
+ destination: structs.ImageCopyTextureTagged,
2367
+ copy_size: Union[List[int], structs.Extent3D],
2368
+ ):
2369
+ raise NotImplementedError()
2370
+
2371
+ # IDL: Promise<undefined> onSubmittedWorkDone();
2372
+ def on_submitted_work_done_sync(self):
2373
+ """Sync version of `on_submitted_work_done_async()`.
2374
+
2375
+ Provided by wgpu-py, but not compatible with WebGPU.
2376
+ """
2377
+ raise NotImplementedError()
2378
+
2379
+ # IDL: Promise<undefined> onSubmittedWorkDone();
2380
+ async def on_submitted_work_done_async(self):
2381
+ """TODO"""
2382
+ raise NotImplementedError()
2383
+
2384
+
2385
+ # %% Further non-GPUObject classes
2386
+
2387
+
2388
+ class GPUDeviceLostInfo:
2389
+ """An object that contains information about the device being lost."""
2390
+
2391
+ # Not used at the moment, see device.lost prop
2392
+
2393
+ def __init__(self, reason, message):
2394
+ self._reason = reason
2395
+ self._message = message
2396
+
2397
+ # IDL: readonly attribute DOMString message;
2398
+ @property
2399
+ def message(self):
2400
+ """The error message specifying the reason for the device being lost."""
2401
+ return self._message
2402
+
2403
+ # IDL: readonly attribute GPUDeviceLostReason reason;
2404
+ @property
2405
+ def reason(self):
2406
+ """The reason (enums.GPUDeviceLostReason) for the device getting lost. Can be None."""
2407
+ return self._reason
2408
+
2409
+
2410
+ class GPUError(Exception):
2411
+ """A generic GPU error."""
2412
+
2413
+ def __init__(self, message):
2414
+ super().__init__(message)
2415
+
2416
+ # IDL: readonly attribute DOMString message;
2417
+ @property
2418
+ def message(self):
2419
+ """The error message."""
2420
+ return self.args[0]
2421
+
2422
+
2423
+ class GPUOutOfMemoryError(GPUError, MemoryError):
2424
+ """An error raised when the GPU is out of memory."""
2425
+
2426
+ # IDL: constructor(DOMString message);
2427
+ def __init__(self, message: str):
2428
+ super().__init__(message or "GPU is out of memory.")
2429
+
2430
+
2431
+ class GPUValidationError(GPUError):
2432
+ """An error raised when the pipeline could not be validated."""
2433
+
2434
+ # IDL: constructor(DOMString message);
2435
+ def __init__(self, message: str):
2436
+ super().__init__(message)
2437
+
2438
+
2439
+ class GPUPipelineError(Exception):
2440
+ """An error raised when a pipeline could not be created."""
2441
+
2442
+ # IDL: constructor(optional DOMString message = "", GPUPipelineErrorInit options);
2443
+ def __init__(self, message: str, options: structs.PipelineErrorInit):
2444
+ super().__init__(message or "")
2445
+ self._options = options
2446
+
2447
+ # IDL: readonly attribute GPUPipelineErrorReason reason;
2448
+ @property
2449
+ def reason(self):
2450
+ """The reason for the failure."""
2451
+ return self.args[0]
2452
+
2453
+
2454
+ class GPUInternalError(GPUError):
2455
+ """An error raised for implementation-specific reasons.
2456
+
2457
+ An operation failed for a system or implementation-specific
2458
+ reason even when all validation requirements have been satisfied.
2459
+ """
2460
+
2461
+ # IDL: constructor(DOMString message);
2462
+ def __init__(self, message: str):
2463
+ super().__init__(message)
2464
+
2465
+
2466
+ # %% Not implemented
2467
+
2468
+
2469
+ class GPUCompilationMessage:
2470
+ """An object that contains information about a problem with shader compilation."""
2471
+
2472
+ # IDL: readonly attribute DOMString message;
2473
+ @property
2474
+ def message(self):
2475
+ """The warning/error message."""
2476
+ raise NotImplementedError()
2477
+
2478
+ # IDL: readonly attribute GPUCompilationMessageType type;
2479
+ @property
2480
+ def type(self):
2481
+ """The type of warning/problem."""
2482
+ raise NotImplementedError()
2483
+
2484
+ # IDL: readonly attribute unsigned long long lineNum;
2485
+ @property
2486
+ def line_num(self):
2487
+ """The corresponding line number in the shader source."""
2488
+ raise NotImplementedError()
2489
+
2490
+ # IDL: readonly attribute unsigned long long linePos;
2491
+ @property
2492
+ def line_pos(self):
2493
+ """The position on the line in the shader source."""
2494
+ raise NotImplementedError()
2495
+
2496
+ # IDL: readonly attribute unsigned long long offset;
2497
+ @property
2498
+ def offset(self):
2499
+ """Offset of ..."""
2500
+ raise NotImplementedError()
2501
+
2502
+ # IDL: readonly attribute unsigned long long length;
2503
+ @property
2504
+ def length(self):
2505
+ """The length of the line?"""
2506
+ raise NotImplementedError()
2507
+
2508
+
2509
+ class GPUCompilationInfo:
2510
+ """TODO"""
2511
+
2512
+ # IDL: readonly attribute FrozenArray<GPUCompilationMessage> messages;
2513
+ @property
2514
+ def messages(self):
2515
+ """A list of `GPUCompilationMessage` objects."""
2516
+ raise NotImplementedError()
2517
+
2518
+
2519
+ class GPUQuerySet(GPUObjectBase):
2520
+ """An object to store the results of queries on passes.
2521
+
2522
+ You can obtain a query set object via :attr:`GPUDevice.create_query_set`.
2523
+ """
2524
+
2525
+ def __init__(self, label, internal, device, type, count):
2526
+ super().__init__(label, internal, device)
2527
+ self._type = type
2528
+ self._count = count
2529
+
2530
+ # IDL: readonly attribute GPUQueryType type;
2531
+ @property
2532
+ def type(self):
2533
+ """The type of the queries managed by this queryset."""
2534
+ return self._type
2535
+
2536
+ # IDL: readonly attribute GPUSize32Out count;
2537
+ @property
2538
+ def count(self):
2539
+ """The number of the queries managed by this queryset."""
2540
+ return self._count
2541
+
2542
+ # IDL: undefined destroy();
2543
+ def destroy(self):
2544
+ """Destroy the QuerySet.
2545
+
2546
+ This cleans up all its resources and puts it in an unusable state.
2547
+ Note that all objects get cleaned up properly automatically; this
2548
+ is only intended to support explicit destroying.
2549
+
2550
+ NOTE: not yet implemented; for the moment this does nothing.
2551
+ """
2552
+ raise NotImplementedError()
2553
+
2554
+
2555
+ # %%%%% Post processing
2556
+
2557
+ # Note that some toplevel classes are already filtered out by the codegen,
2558
+ # like GPUExternalTexture and GPUUncapturedErrorEvent, and more.
2559
+
2560
+ apidiff.remove_hidden_methods(globals())
2561
+
2562
+
2563
+ def _seed_object_counts():
2564
+ m = globals()
2565
+ for class_name in __all__:
2566
+ cls = m[class_name]
2567
+ if not class_name.endswith(("Base", "Mixin")):
2568
+ if hasattr(cls, "_ot"):
2569
+ object_tracker.counts[class_name] = 0
2570
+
2571
+
2572
+ def generic_repr(self):
2573
+ try:
2574
+ module_name = self.__module__
2575
+ if module_name.startswith("wgpu"):
2576
+ if module_name == "wgpu._classes":
2577
+ module_name = "wgpu"
2578
+ elif "backends." in module_name:
2579
+ backend_name = self.__module__.split("backends")[-1].split(".")[1]
2580
+ module_name = f"wgpu.backends.{backend_name}"
2581
+ object_str = "object"
2582
+ if isinstance(self, GPUObjectBase):
2583
+ object_str = f"object '{self.label}'"
2584
+ return (
2585
+ f"<{module_name}.{self.__class__.__name__} {object_str} at {hex(id(self))}>"
2586
+ )
2587
+ except Exception: # easy fallback
2588
+ return object.__repr__(self)
2589
+
2590
+
2591
+ def _set_repr_methods():
2592
+ m = globals()
2593
+ for class_name in __all__:
2594
+ cls = m[class_name]
2595
+ if len(cls.mro()) == 2: # class itself and object
2596
+ cls.__repr__ = generic_repr
2597
+
2598
+
2599
+ _async_warnings = {}
2600
+
2601
+
2602
+ def _set_compat_methods_for_async_methods():
2603
+ def create_new_method(name):
2604
+ def proxy_method(self, *args, **kwargs):
2605
+ warning = _async_warnings.pop(name, None)
2606
+ if warning:
2607
+ logger.warning(warning)
2608
+ return getattr(self, name)(*args, **kwargs)
2609
+
2610
+ proxy_method.__name__ = name + "_backwards_compat_proxy"
2611
+ proxy_method.__doc__ = f"Backwards compatible method for {name}()"
2612
+ return proxy_method
2613
+
2614
+ m = globals()
2615
+ for class_name in __all__:
2616
+ cls = m[class_name]
2617
+ for name, func in list(cls.__dict__.items()):
2618
+ if name.endswith("_sync") and callable(func):
2619
+ old_name = name[:-5]
2620
+ setattr(cls, old_name, create_new_method(name))
2621
+ _async_warnings[name] = (
2622
+ f"WGPU: {old_name}() is deprecated, use {name}() instead."
2623
+ )
2624
+
2625
+
2626
+ _seed_object_counts()
2627
+ _set_repr_methods()
2628
+ _set_compat_methods_for_async_methods()