diff --git a/README.md b/README.md index 4306d72..8f5f1e9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # xgpu -`xgpu` is a Python 3.7+ binding of [webgpu-native](https://github.com/gfx-rs/wgpu-native) which is auto-generated at build-time and tracks the upstream releases as closely as possible. It is currently in beta, and until it is more mature most users should probably use [wgpu-py](https://github.com/pygfx/wgpu-py). +`xgpu` is an agressively typed, red-squiggle-free Python binding +of [wgpu-native](https://github.com/gfx-rs/wgpu-native), autogenerated from +the C headers to always track the latest upstream release. + +Not 'production ready'. ### Install @@ -9,16 +13,30 @@ Wheels are built for Mac (x86 only), Windows, and Linux for Python 3.7+: pip install xgpu ``` -### Conventions +### Motivation + +The motivation for this project compared to the other, currently-existing, +semi-mature wgpu_native binding [wgpu-py](https://github.com/pygfx/wgpu-py) +is very simple: I would like to enable typechecking and not get red squiggles +everywhere. Also, I would like the Python bindings to update as soon as possible +after upstream wgpu_native has a new release. `xgpu` addresses the first by +being fully type-hinted and red-squiggle-free, and the second by being +automatically generated from the raw upstream C headers. -`xgpu` is a mostly 1-to-1 binding of `webgpu.h` and `wgpu.h` from `wgpu_native`. +### Conventions/Philosophy + +`xgpu` is a mostly 1-to-1 binding of `webgpu.h` (+`wgpu.h` from `wgpu_native`). #### General name conventions +`xgpu` largely tries to maintain the names from `webgpu.h` rather than localizing +them into Python's conventions. + * Names keep their formatting from `webgpu.h` but lose `WGPU` prefixes: `WGPUTextureSampleType` -> `TextureSampleType` -* Field names, functions, etc. maintain same camel-case: `WGPUAdapterProperties.vendorName` -> `AdapterProperties.vendorName` -* Enum variants lose prefix: `WGPUTextureUsage_CopySrc` -> `TextureUsage.CopySrc` - - Names invalid in Python are prefixed with "_": `WGPUBufferUsage_None` -> `BufferUsage._None` +* Fields: `WGPUAdapterProperties.vendorName` -> `AdapterProperties.vendorName` +* Member functions: `wgpuDeviceHasFeature` -> `Device.hasFeature` +* Enum values: `WGPUTextureUsage_CopySrc` -> `TextureUsage.CopySrc` + - Names invalid in Python are prefixed with "_": `WGPUBufferUsage_None` -> `BufferUsage._None`, `WGPUTextureDimension_2D` -> `TextureDimension._2D` #### Struct constructors @@ -35,7 +53,7 @@ extents.height = 100 extents.depthOrArrayLayers = 1 ``` -#### "Member" function calls +#### Member functions As a C API, `webgpu.h` follows typical C convention for member functions, which is to define them like: @@ -47,11 +65,11 @@ uint32_t wgpuTextureGetHeight(WGPUTexture texture) In `xgpu` these become genuine member functions, e.g., ```python -class TextureView: +class Texture: def getHeight(self) -> int ``` -#### Array passing conventions +#### Array arguments / fields Some `webgpu.h` functions and structs take arrays using the convention of passing first the array item count, and then the array pointer, e.g., @@ -77,7 +95,7 @@ def pipelineLayoutDescriptor(*, bindGroupLayouts: List["BindGroupLayout"]) #### Enums and Flags -Enums are translated into `IntEnum`s and inherit all their conveniences: +Enums are translated into `IntEnum`s: ```python mode = xgpu.AddressMode.MirrorRepeat @@ -117,8 +135,7 @@ Callbacks must be explicitly wrapped in the appropriate callback type: ```python def my_adapter_cb(status: xgpu.RequestAdapterStatus, gotten: xgpu.Adapter, msg: str): - print("Got adapter with msg:", msg, ", status:", status.name) - adapter[0] = gotten + print(f"Got adapter with msg:'{msg}', status: {status.name}") cb = xgpu.RequestAdapterCallback(my_adapter_cb) ``` @@ -167,6 +184,18 @@ my_array = np.ones(100, dtype=np.float32) wrapped = xgpu.DataPtr.wrap(my_array) ``` -### Codegen +### Codegen/Local Build -The code generation was written in Typescript and runs in `bun`. Python users shouldn’t have to touch this. +You will need [bun](https://bun.sh/) to run the codegen. Deno *might* +work but just go ahead and install bun. You will also need to have +ruff and cffi installed in python (`pip install ruff cffi`). + +Then: +``` +python codegen/fetch_wgpu_bins.py +bun codegen/generate.ts +cd xgpu +python _build_ext.py +cd .. +pip install . +``` diff --git a/examples/triangle_offscreen.py b/examples/triangle_offscreen.py index e70184e..4b3d860 100644 --- a/examples/triangle_offscreen.py +++ b/examples/triangle_offscreen.py @@ -75,9 +75,7 @@ def _main(device: xg.Device): layout = device.createPipelineLayout(bindGroupLayouts=[]) color_tex = device.createTexture( - usage=xg.TextureUsageFlags( - [xg.TextureUsage.RenderAttachment, xg.TextureUsage.CopySrc] - ), + usage=xg.TextureUsage.RenderAttachment | xg.TextureUsage.CopySrc, size=xg.extent3D(width=WIDTH, height=HEIGHT, depthOrArrayLayers=1), format=xg.TextureFormat.RGBA8Unorm, viewFormats=[xg.TextureFormat.RGBA8Unorm], @@ -90,7 +88,7 @@ def _main(device: xg.Device): vertex = xg.vertexState(module=shader, entryPoint="vs_main", constants=[], buffers=[]) color_target = xg.colorTargetState( format=xg.TextureFormat.RGBA8Unorm, - writeMask=xg.ColorWriteMaskFlags([xg.ColorWriteMask.All]), + writeMask=xg.ColorWriteMask.All, ) multisample = xg.multisampleState() fragment = xg.fragmentState( @@ -109,8 +107,7 @@ def _main(device: xg.Device): format=xg.TextureFormat.RGBA8Unorm, dimension=xg.TextureViewDimension._2D, mipLevelCount=1, - arrayLayerCount=1, - aspect=xg.TextureAspect.All, + arrayLayerCount=1 ) color_attachment = xg.renderPassColorAttachment( diff --git a/examples/triangle_windowed.py b/examples/triangle_windowed.py index c02da2f..af13d73 100644 --- a/examples/triangle_windowed.py +++ b/examples/triangle_windowed.py @@ -80,8 +80,6 @@ def main(): primitive = xg.primitiveState( topology=xg.PrimitiveTopology.TriangleList, stripIndexFormat=xg.IndexFormat.Undefined, - frontFace=xg.FrontFace.CCW, - cullMode=xg.CullMode._None, ) vertex = xg.vertexState(module=shader, entryPoint="vs_main", constants=[], buffers=[]) color_target = xg.colorTargetState(