diff --git a/codegen/apipatcher.py b/codegen/apipatcher.py index 115b8462..94d1d219 100644 --- a/codegen/apipatcher.py +++ b/codegen/apipatcher.py @@ -286,15 +286,13 @@ def get_class_def(self, classname): bases = sorted(cls.bases or [], key=lambda n: n.count("GPUObjectBase")) bases = [b for b in bases if b not in ignore] # Cover some special cases - if not bases and classname.lower().endswith("error"): - if "memory" in classname: - bases = "(MemoryError)" - else: - bases = "(Exception)" - elif not bases: - bases = "" - else: - bases = f"({', '.join(bases)})" + if classname.lower().endswith("error"): + if "memory" in classname.lower(): + bases.append("MemoryError") + elif not bases: + bases.append("Exception") + + bases = "" if not bases else f"({', '.join(bases)})" return f"class {classname}{bases}:" def get_method_def(self, classname, methodname): @@ -437,10 +435,15 @@ def class_is_known(self, classname): def get_class_def(self, classname): line, _ = self.classes[classname] - if "):" in line: - return line.replace("(", f"(base.{classname}, ") - else: + + if "):" not in line: return line.replace(":", f"(base.{classname}):") + else: + i = line.find("(") + bases = line[i:].strip("():").replace(",", " ").split() + bases = [b for b in bases if b.startswith("GPU")] + bases.insert(0, f"base.{classname}") + return line[:i] + "(" + ", ".join(bases) + "):" def get_method_def(self, classname, methodname): _, methods = self.classes[classname] diff --git a/codegen/idlparser.py b/codegen/idlparser.py index 33b25496..5063c91d 100644 --- a/codegen/idlparser.py +++ b/codegen/idlparser.py @@ -201,6 +201,7 @@ def resolve_type(self, typename): "ImageData": "memoryview", "VideoFrame": "memoryview", "GPUPipelineConstantValue": "float", + "GPUExternalTexture": "object", } name = pythonmap.get(name, name) @@ -403,6 +404,8 @@ def _post_process(self): "GPUSupportedLimits", "GPUSupportedFeatures", "WGSLLanguageFeatures", + "GPUUncapturedErrorEvent", + "GPUExternalTexture", ]: self._interfaces.pop(name, None) diff --git a/docs/guide.rst b/docs/guide.rst index c197cefb..bb27aa92 100644 --- a/docs/guide.rst +++ b/docs/guide.rst @@ -16,7 +16,7 @@ Selecting the backend To use ``wgpu``, you must select a backend. Eventually there may be multiple backends, but at the moment -there is only one backend, which is based on the Rust libary +there is only one, which is based on the Rust libary `wgpu-native `__. You select the backend by importing it: @@ -203,7 +203,7 @@ object. These objects provide a quite versatile view on ndarray data: .. code-block:: py # One could, for instance read the content of a buffer - m = buffer.read_data() + m = device.queue.read_buffer(buffer) # Cast it to float32 m = m.cast("f") # Index it diff --git a/docs/start.rst b/docs/start.rst index 16146496..58ea7913 100644 --- a/docs/start.rst +++ b/docs/start.rst @@ -14,7 +14,7 @@ Python 3.7 or higher is required. Pypy is supported. Only depends on ``cffi`` (i pip install wgpu -Since most users will want to render something to screen, we recommend installing GLGW as well: +Since most users will want to render something to screen, we recommend installing GLFW as well: .. code-block:: bash @@ -54,8 +54,8 @@ is selected automatically, but can be overridden by setting the Windows +++++++ -On Windows 10+, things should just work. On older Windows versions you -may need to install the Vulkan drivers. +On Windows 10+, things should just work. If your machine has a dedicated GPU, +you may want to update to the latest (Nvidia or AMD) drivers. MacOS +++++ diff --git a/docs/utils.rst b/docs/utils.rst index 378f1fdf..71a65fa1 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -1,7 +1,7 @@ Utils ===== -The wgpu library provides a few utilities. Note that the functions below need to be explictly imported. +The wgpu library provides a few utilities. Note that most functions below need to be explictly imported. Get default device @@ -13,6 +13,10 @@ Get default device Compute with buffers -------------------- +.. code-block:: py + + from wgpu.utils.compute import compute_with_buffers + .. autofunction:: wgpu.utils.compute_with_buffers diff --git a/docs/wgpu.rst b/docs/wgpu.rst index ef685d49..aa9f7839 100644 --- a/docs/wgpu.rst +++ b/docs/wgpu.rst @@ -13,7 +13,8 @@ Graphics Processing Unit. The WebGPU API is still being developed and occasionally there are backwards incompatible changes. Since we mostly follow the WebGPU API, there may be backwards incompatible changes to wgpu-py too. This will be so until - the WebGPU API settles as a standard. + the WebGPU API settles as a standard. In the mean time, keep an eye on the + `CHANGELOG.md `_. How to read this API @@ -21,7 +22,7 @@ How to read this API The classes in this API all have a name staring with "GPU", this helps discern them from flags and enums. These classes are never instantiated -directly; new objects are returned by certain methods. +directly; new objects are returned by special methods (mostly from the device). Most methods in this API have no positional arguments; each argument must be referenced by name. Some argument values must be a :doc:`dict `, these @@ -44,7 +45,7 @@ here accept the fields in that struct as keyword arguments. Each backend may also implement minor differences (usually additions) -from the base API. For the ``rs`` backend check ``print(wgpu.backends.rs.apidiff.__doc__)``. +from the base API. The ``rs`` backend only adds ``GPUAdapter.request_device_tracing()``. Overview @@ -89,8 +90,6 @@ for it, that can be attached to a shader. To let a shader sample from a texture, you also need a :class:`GPUSampler` that defines the filtering and sampling behavior beyond the edges. -WebGPU also defines the :class:`GPUExternalTexture`, but this is not (yet?) used in wgpu-py. - Bind groups +++++++++++ @@ -144,16 +143,16 @@ implememted in wgpu-py. Error handling ++++++++++++++ -Errors are caught and logged using the ``wgpu`` logger. +Errors in wgpu-native are raised as Python errors where possible. Uncaught errors +and warnings are logged using the ``wgpu`` logger. -Todo: document the role of these classes: -:class:`GPUUncapturedErrorEvent` -:class:`GPUError` -:class:`GPUValidationError` -:class:`GPUOutOfMemoryError` -:class:`GPUInternalError` -:class:`GPUPipelineError` -:class:`GPUDeviceLostInfo` +There are specific exceptions that can be raised: +* :class:`GPUError` is the generic (base) error class. +* :class:`GPUValidationError` is for wgpu validation errors. Shader errors also fall into this category. +* :class:`GPUOutOfMemoryError` is a wgpu `MemoryError`. +* :class:`GPUInternalError` when wgpu reaches a internal error state. +* :class:`GPUPipelineError` for errors related to the pipeline. +* :class:`GPUDeviceLostInfo` when the device is lost. TODO ++++ @@ -202,7 +201,6 @@ List of GPU classes ~GPUDevice ~GPUDeviceLostInfo ~GPUError - ~GPUExternalTexture ~GPUInternalError ~GPUObjectBase ~GPUOutOfMemoryError @@ -220,5 +218,4 @@ List of GPU classes ~GPUShaderModule ~GPUTexture ~GPUTextureView - ~GPUUncapturedErrorEvent ~GPUValidationError diff --git a/docs/wgpu_enums.rst b/docs/wgpu_enums.rst index 67962fae..b4532ff1 100644 --- a/docs/wgpu_enums.rst +++ b/docs/wgpu_enums.rst @@ -4,3 +4,4 @@ Enums .. automodule:: wgpu.enums :members: :undoc-members: + :exclude-members: Enum diff --git a/docs/wgpu_flags.rst b/docs/wgpu_flags.rst index 4efe4bb9..8c41c6c7 100644 --- a/docs/wgpu_flags.rst +++ b/docs/wgpu_flags.rst @@ -4,3 +4,4 @@ Flags .. automodule:: wgpu.flags :members: :undoc-members: + :exclude-members: Flags diff --git a/docs/wgpu_structs.rst b/docs/wgpu_structs.rst index ef469948..3feb6068 100644 --- a/docs/wgpu_structs.rst +++ b/docs/wgpu_structs.rst @@ -4,3 +4,4 @@ Structs .. automodule:: wgpu.structs :members: :undoc-members: + :exclude-members: Struct diff --git a/wgpu/backends/rs.py b/wgpu/backends/rs.py index a0b116c8..a2a3e23f 100644 --- a/wgpu/backends/rs.py +++ b/wgpu/backends/rs.py @@ -2688,20 +2688,17 @@ def _destroy(self): libf.wgpuRenderBundleRelease(internal) -class GPUDeviceLostInfo(base.GPUDeviceLostInfo): - pass - - -class GPUError(base.GPUError, Exception): +class GPUQuerySet(base.GPUQuerySet, GPUObjectBase): pass - -class GPUOutOfMemoryError(base.GPUOutOfMemoryError, GPUError): - pass + def destroy(self): + if self._internal is not None and lib is not None: + self._internal, internal = None, self._internal + # H: void f(WGPUQuerySet querySet) + libf.wgpuQuerySetRelease(internal) -class GPUValidationError(base.GPUValidationError, GPUError): - pass +# %% Subclasses that don't need anything else class GPUCompilationMessage(base.GPUCompilationMessage): @@ -2712,32 +2709,28 @@ class GPUCompilationInfo(base.GPUCompilationInfo): pass -class GPUQuerySet(base.GPUQuerySet, GPUObjectBase): +class GPUDeviceLostInfo(base.GPUDeviceLostInfo): pass - def destroy(self): - if self._internal is not None and lib is not None: - self._internal, internal = None, self._internal - # H: void f(WGPUQuerySet querySet) - libf.wgpuQuerySetRelease(internal) + +class GPUError(base.GPUError): + pass -class GPUExternalTexture(base.GPUExternalTexture, GPUObjectBase): +class GPUOutOfMemoryError(base.GPUOutOfMemoryError, GPUError): pass -class GPUUncapturedErrorEvent(base.GPUUncapturedErrorEvent): +class GPUValidationError(base.GPUValidationError, GPUError): pass -class GPUPipelineError(base.GPUPipelineError, Exception): - def __init__(self, message="", options=None): - super().__init__(message, options) +class GPUPipelineError(base.GPUPipelineError): + pass class GPUInternalError(base.GPUInternalError, GPUError): - def __init__(self, message): - super().__init__(message) + pass # %% diff --git a/wgpu/base.py b/wgpu/base.py index b4fc46a5..aaa2650e 100644 --- a/wgpu/base.py +++ b/wgpu/base.py @@ -25,7 +25,6 @@ "GPUBuffer", "GPUTexture", "GPUTextureView", - "GPUExternalTexture", "GPUSampler", "GPUBindGroupLayout", "GPUBindGroup", @@ -55,7 +54,6 @@ "GPUValidationError", "GPUOutOfMemoryError", "GPUInternalError", - "GPUUncapturedErrorEvent", ] logger = logging.getLogger("wgpu") @@ -103,7 +101,7 @@ async def request_adapter_async( ) # no-cover # IDL: GPUTextureFormat getPreferredCanvasFormat(); - @apidiff.change("Disabled because we put it on the canvas context.") + @apidiff.change("Disabled because we put it on the canvas context") def get_preferred_canvas_format(self): """Not implemented in wgpu-py; use `GPUCanvasContext.get_preferred_format()` instead. The WebGPU spec defines this function, but in wgpu there are different @@ -419,12 +417,17 @@ def adapter(self): return self._adapter # IDL: readonly attribute Promise lost; + @apidiff.hide("Not a Pythonic API") @property def lost(self): """Provides information about why the device is lost.""" + # In JS you can device.lost.then ... to handle lost devices. + # We may want to eventually support something similar async-like? + # at some point raise NotImplementedError() # IDL: attribute EventHandler onuncapturederror; + @apidiff.hide("Specific to browsers") @property def onuncapturederror(self): """Method called when an error is capured?""" @@ -908,7 +911,7 @@ def pop_error_scope(self): raise NotImplementedError() # IDL: GPUExternalTexture importExternalTexture(GPUExternalTextureDescriptor descriptor); - @apidiff.hide("Specific to browsers.") + @apidiff.hide("Specific to browsers") def import_external_texture( self, *, @@ -1843,7 +1846,7 @@ def on_submitted_work_done(self): raise NotImplementedError() # IDL: undefined copyExternalImageToTexture( GPUImageCopyExternalImage source, GPUImageCopyTextureTagged destination, GPUExtent3D copySize); - @apidiff.hide("Specific to browsers.") + @apidiff.hide("Specific to browsers") def copy_external_image_to_texture(self, source, destination, copy_size): raise NotImplementedError() @@ -1854,6 +1857,8 @@ def copy_external_image_to_texture(self, source, destination, copy_size): class GPUDeviceLostInfo: """An object that contains information about the device being lost.""" + # Not used at the moment, see device.lost prop + def __init__(self, reason, message): self._reason = reason self._message = message @@ -1884,7 +1889,7 @@ def message(self): return self.args[0] -class GPUOutOfMemoryError(GPUError): +class GPUOutOfMemoryError(GPUError, MemoryError): """An error raised when the GPU is out of memory.""" # IDL: constructor(DOMString message); @@ -2001,24 +2006,9 @@ def count(self): raise NotImplementedError() -class GPUExternalTexture(GPUObjectBase): - """Ignore this - specific to browsers.""" - - -class GPUUncapturedErrorEvent: - """TODO""" - - # IDL: [SameObject] readonly attribute GPUError error; - @property - def error(self): - """The error object.""" - raise NotImplementedError() - - # IDL: constructor( DOMString type, GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict ); - def __init__(self, type, gpu_uncaptured_error_event_init_dict): - pass - - # %%%%% Post processing +# Note that some toplevel classes are already filtered out by the codegen, +# like GPUExternalTexture and GPUUncapturedErrorEvent, and more. + apidiff.remove_hidden_methods(globals()) diff --git a/wgpu/resources/codegen_report.md b/wgpu/resources/codegen_report.md index 0f442d71..4c7666fb 100644 --- a/wgpu/resources/codegen_report.md +++ b/wgpu/resources/codegen_report.md @@ -1,6 +1,6 @@ # Code generatation report ## Preparing -* The webgpu.idl defines 39 classes with 78 functions +* The webgpu.idl defines 37 classes with 77 functions * The webgpu.idl defines 5 flags, 33 enums, 59 structs * The wgpu.h defines 198 functions * The wgpu.h defines 6 flags, 49 enums, 88 structs @@ -11,15 +11,15 @@ ### Patching API for base.py * Diffs for GPU: add print_report, change get_preferred_canvas_format, change request_adapter, change request_adapter_async * Diffs for GPUCanvasContext: add get_preferred_format, add present -* Diffs for GPUDevice: add adapter, add create_buffer_with_data, hide import_external_texture, hide pop_error_scope, hide push_error_scope +* Diffs for GPUDevice: add adapter, add create_buffer_with_data, hide import_external_texture, hide lost, hide onuncapturederror, hide pop_error_scope, hide push_error_scope * Diffs for GPUBuffer: add map_read, add map_write, add read_mapped, add write_mapped, hide get_mapped_range * Diffs for GPUTexture: add size * Diffs for GPUTextureView: add size, add texture * Diffs for GPUQueue: add read_buffer, add read_texture, hide copy_external_image_to_texture -* Validated 39 classes, 114 methods, 44 properties +* Validated 37 classes, 113 methods, 43 properties ### Patching API for backends/rs.py * Diffs for GPUAdapter: add request_device_tracing -* Validated 39 classes, 103 methods, 0 properties +* Validated 37 classes, 101 methods, 0 properties ## Validating rs.py * Enum field TextureFormat.rgb10a2uint missing in wgpu.h * Enum PipelineErrorReason missing in wgpu.h diff --git a/wgpu/structs.py b/wgpu/structs.py index f116cb2e..fcd21d02 100644 --- a/wgpu/structs.py +++ b/wgpu/structs.py @@ -211,11 +211,11 @@ def __repr__(self): ) #: * binding :: int -#: * resource :: Union[:class:`GPUExternalTexture `, :class:`GPUSampler `, :class:`GPUTextureView `, :obj:`structs.BufferBinding `] +#: * resource :: Union[:class:`GPUSampler `, :class:`GPUTextureView `, object, :obj:`structs.BufferBinding `] BindGroupEntry = Struct( "BindGroupEntry", binding="int", - resource="Union[GPUExternalTexture, GPUSampler, GPUTextureView, structs.BufferBinding]", + resource="Union[GPUSampler, GPUTextureView, object, structs.BufferBinding]", ) #: * buffer :: :class:`GPUBuffer `