wgpu 1.2.0 → 1.2.1

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +18 -2
  4. data/docs/README.md +2 -2
  5. data/docs/api_coverage.md +23 -15
  6. data/docs/async.md +10 -0
  7. data/docs/command_encoding.md +25 -0
  8. data/docs/getting_started_compute.md +1 -0
  9. data/docs/pipeline_descriptors.md +7 -2
  10. data/docs/releasing.md +11 -1
  11. data/docs/resource_lifetime.md +16 -2
  12. data/docs/texture_readback.md +27 -0
  13. data/docs/troubleshooting.md +5 -0
  14. data/docs/upgrading_wgpu_native.md +16 -5
  15. data/lib/wgpu/async_task.rb +19 -0
  16. data/lib/wgpu/commands/command_buffer.rb +14 -1
  17. data/lib/wgpu/commands/command_encoder.rb +58 -1
  18. data/lib/wgpu/commands/compute_pass.rb +43 -0
  19. data/lib/wgpu/commands/render_bundle.rb +9 -1
  20. data/lib/wgpu/commands/render_bundle_encoder.rb +55 -1
  21. data/lib/wgpu/commands/render_pass.rb +102 -0
  22. data/lib/wgpu/core/adapter.rb +91 -12
  23. data/lib/wgpu/core/async_waiter.rb +15 -0
  24. data/lib/wgpu/core/canvas_context.rb +32 -0
  25. data/lib/wgpu/core/device.rb +296 -46
  26. data/lib/wgpu/core/instance.rb +20 -0
  27. data/lib/wgpu/core/queue.rb +139 -24
  28. data/lib/wgpu/core/surface.rb +49 -1
  29. data/lib/wgpu/data_types.rb +16 -0
  30. data/lib/wgpu/descriptor_helpers.rb +65 -0
  31. data/lib/wgpu/error.rb +15 -0
  32. data/lib/wgpu/native/abi_verifier.rb +37 -3
  33. data/lib/wgpu/native/callbacks.rb +6 -0
  34. data/lib/wgpu/native/capabilities.rb +16 -2
  35. data/lib/wgpu/native/distribution.rb +63 -0
  36. data/lib/wgpu/native/enum_helper.rb +17 -0
  37. data/lib/wgpu/native/enums.rb +8 -0
  38. data/lib/wgpu/native/fixtures/webgpu-v27.0.4.0-enums.h +848 -0
  39. data/lib/wgpu/native/functions.rb +11 -0
  40. data/lib/wgpu/native/installer.rb +48 -17
  41. data/lib/wgpu/native/loader.rb +22 -0
  42. data/lib/wgpu/native/structs.rb +18 -1
  43. data/lib/wgpu/native_resource.rb +174 -3
  44. data/lib/wgpu/pipeline/bind_group.rb +9 -0
  45. data/lib/wgpu/pipeline/bind_group_layout.rb +17 -4
  46. data/lib/wgpu/pipeline/compute_pipeline.rb +20 -32
  47. data/lib/wgpu/pipeline/pipeline_layout.rb +8 -0
  48. data/lib/wgpu/pipeline/render_pipeline.rb +27 -42
  49. data/lib/wgpu/pipeline/shader_module.rb +54 -29
  50. data/lib/wgpu/resources/buffer.rb +258 -31
  51. data/lib/wgpu/resources/query_set.rb +12 -0
  52. data/lib/wgpu/resources/sampler.rb +7 -0
  53. data/lib/wgpu/resources/texture.rb +45 -6
  54. data/lib/wgpu/resources/texture_view.rb +24 -4
  55. data/lib/wgpu/texture_format.rb +14 -0
  56. data/lib/wgpu/version.rb +1 -1
  57. data/lib/wgpu/window.rb +26 -0
  58. data/sig/wgpu.rbs +84 -5
  59. metadata +3 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65be5bedee12b2389a8bf8fd8ec2dcb883d739852178dda17cae6b682a26663e
4
- data.tar.gz: 584a76214478803361befac5011bb22df20034f4b9e18731be6685b69877eabd
3
+ metadata.gz: c118a2950771b2f34f06fcf4642634e57e8b1de07c26681b81aa94d6a1faaf93
4
+ data.tar.gz: b4f4cc4e97846d21e74144194f97ede7cb2704725cb5887a885345ceedd1c8f1
5
5
  SHA512:
6
- metadata.gz: cde23ae0101bf4e385adb161faca1221069ba15b275c066bcc1770aaab5f88e957bcaa7f8fcd06e1e711730fa75908fbee31e31140a06ba53c8acc55f4b634ed
7
- data.tar.gz: d2d974230e674a282ca23a790eb16155730f2b5d9e97d0bc4df3c08822e862e0b17874c9c41b80155a5297bbda427c53ac35e77a7b455050ddb8c2da02031498
6
+ metadata.gz: 5fad367a0475a31c846fa57151a01d6fda7aed9a5592f6167237117da8967df7269a7ac0233059a174be0803794dc844ee4660cd1383ce3fa858009620100036
7
+ data.tar.gz: 967d67b899a1ca01e5e805407677772aeaec152d72ede7b31746a8baef6d5fa04544863f7d8e70a4bb2804506ccf8fdd58036c5d5a99c49cfe338be73d667504
data/CHANGELOG.md CHANGED
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 1.2.1 - 2026-07-24
11
+
12
+ ### Added
13
+
14
+ - Add reusable staging buffers to `Queue#read_buffer` and
15
+ `Queue#read_texture`, together with a padded texture-readback example and
16
+ guide.
17
+ - Support WGSL override constants and omitted entry points across compute,
18
+ vertex, and fragment pipeline stages.
19
+ - Add a pinned wgpu-native v27.0.4.0 header fixture, ABI update tooling, and
20
+ cross-platform native artifact verification.
21
+ - Complete RBS and YARD coverage for the public API and expand acceptance
22
+ coverage for typed buffers, descriptors, errors, timeouts, resource
23
+ lifetimes, and GPU examples.
24
+
25
+ ### Changed
26
+
27
+ - Centralize native capability metadata and validate enum, callback, struct,
28
+ and function ABI expectations against the pinned wgpu-native release.
29
+ - Make compute and rendering examples verify their results, and enforce
30
+ documentation coverage, SDL3-free installation, release checks, and strict
31
+ lavapipe GPU coverage in CI.
32
+
33
+ ### Fixed
34
+
35
+ - Retain callbacks, user data, devices, and instances through dependent
36
+ resource and pending-operation lifetimes, preventing use-after-free crashes
37
+ after release, garbage collection, or timeout.
38
+ - Reject out-of-bounds mapped ranges and prevent late buffer, queue, shader,
39
+ and error-scope callbacks from corrupting completed operation state.
40
+ - Preserve row padding and clean up temporary resources during buffer and
41
+ texture readback, including repeated reads with caller-owned staging buffers.
42
+ - Recover rendering loops from timed-out, outdated, and lost surface frames,
43
+ report fatal acquisition states explicitly, and release every acquired
44
+ texture and view.
45
+ - Track adopted native handles in leak diagnostics and keep release operations
46
+ idempotent across wrapper types.
47
+ - Correct the isolated core-gem dependency path and make Windows native
48
+ artifact checks independent of shell quoting.
49
+
10
50
  ## 1.2.0 - 2026-07-23
11
51
 
12
52
  ### Added
data/README.md CHANGED
@@ -53,6 +53,7 @@ export WGPU_LIB_PATH=/path/to/libwgpu_native.so
53
53
 
54
54
  ### Basic Setup
55
55
 
56
+ <!-- wgpu-example: run; expect: Using: -->
56
57
  ```ruby
57
58
  require 'wgpu'
58
59
 
@@ -62,10 +63,15 @@ device = adapter.request_device
62
63
  queue = device.queue
63
64
 
64
65
  puts "Using: #{adapter.info[:device]} (#{adapter.info[:backend_type]})"
66
+
67
+ device.release
68
+ adapter.release
69
+ instance.release
65
70
  ```
66
71
 
67
72
  ### Compute Shader Example
68
73
 
74
+ <!-- wgpu-example: run; expect: [0.0, 2.0, 4.0 -->
69
75
  ```ruby
70
76
  require 'wgpu'
71
77
 
@@ -118,14 +124,23 @@ pass.set_pipeline(pipeline)
118
124
  pass.set_bind_group(0, bind_group)
119
125
  pass.dispatch_workgroups(input_data.size / 64)
120
126
  pass.end_pass
121
- queue.submit([encoder.finish])
127
+ command_buffer = encoder.finish
128
+ queue.submit(command_buffer)
122
129
 
123
130
  # Read results
124
- result = queue.read_buffer(buffer, device: device)
131
+ result = queue.read_buffer(buffer)
125
132
  output_data = result.unpack("f*")
126
133
 
127
134
  puts output_data[0, 10].inspect
128
135
  # => [0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0]
136
+
137
+ [
138
+ command_buffer, pass, encoder, pipeline, pipeline_layout,
139
+ bind_group, bind_group_layout, shader, buffer
140
+ ].each(&:release)
141
+ device.release
142
+ adapter.release
143
+ instance.release
129
144
  ```
130
145
 
131
146
  ## Examples
@@ -147,6 +162,7 @@ Headless validation examples:
147
162
  - `13_error_handling.rb` - Typed, labeled validation errors
148
163
  - `14_async_map.rb` - Async mapping through a forced GC cycle
149
164
  - `15_timestamp_query.rb` - Feature-gated timestamp query resolution
165
+ - `16_texture_readback.rb` - Aligned readback of a 65-pixel-wide texture with reusable staging
150
166
 
151
167
  Rendering examples (SDL3):
152
168
  - `07_triangle.rb` - Basic triangle rendering
data/docs/README.md CHANGED
@@ -17,6 +17,6 @@
17
17
  - [Upgrading wgpu-native](upgrading_wgpu_native.md)
18
18
  - [Releasing](releasing.md)
19
19
 
20
- Generated API documentation is built with `bundle exec yard doc`. Static API
21
- signatures live in `sig/wgpu.rbs` and validate with
20
+ The published [API reference](https://www.rubydoc.info/gems/wgpu) is generated
21
+ with `bundle exec yard doc`. Static API signatures live in `sig/wgpu.rbs` and validate with
22
22
  `bundle exec rbs -I sig validate`.
data/docs/api_coverage.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # WebGPU API coverage
2
2
 
3
- This page records the public Ruby API implemented by wgpu-ruby 1.1 and its
3
+ This page records the public Ruby API implemented by wgpu-ruby 1.2 and its
4
4
  relationship to the WebGPU object model. It is a baseline, not a claim that
5
5
  every WebGPU feature is implemented.
6
6
 
@@ -33,12 +33,12 @@ Legend:
33
33
  | `GPUCompilationMessage` | `WGPU::CompilationMessage` | ✅ | Message, type, line, column, offset, and length are typed fields. |
34
34
  | `GPUComputePassEncoder` | `WGPU::ComputePass` | ✅ | |
35
35
  | `GPUComputePipeline` | `WGPU::ComputePipeline` | ✅ | Auto layout and override constants are accepted. |
36
- | `GPUDevice` | `WGPU::Device` | ◐ | Resource creation, error scopes, polling, features, and limits are exposed; device-lost and uncaptured-error subscriptions are not. |
37
- | `GPUDeviceLostInfo` | | | |
38
- | `GPUError` | error hashes and `WGPU::Error` subclasses | | There is no common `GPUError` value object yet. |
36
+ | `GPUDevice` | `WGPU::Device` | ◐ | Resource creation, error scopes, polling, features, limits, device-lost notification, and uncaptured-error subscriptions are exposed. |
37
+ | `GPUDeviceLostInfo` | callback `reason` and `message` | | `Device#on_device_lost` exposes the native reason and message without a separate wrapper object. |
38
+ | `GPUError` | `WGPU::GPUError` | | Typed value object with `type`, `message`, `to_h`, and exception conversion. |
39
39
  | `GPUExternalTexture` | — | ❌ | Browser video/external-texture import is not exposed. |
40
- | `GPUInternalError` | | | |
41
- | `GPUOutOfMemoryError` | | | |
40
+ | `GPUInternalError` | `WGPU::InternalError` | | Represented as a Ruby exception converted from `GPUError`. |
41
+ | `GPUOutOfMemoryError` | `WGPU::OutOfMemoryError` | | Represented as a Ruby exception converted from `GPUError`. |
42
42
  | `GPUPipelineError` | `WGPU::PipelineError` | ◐ | Ruby exception exists; native pipeline error data is not a value object. |
43
43
  | `GPUPipelineLayout` | `WGPU::PipelineLayout` | ✅ | |
44
44
  | `GPUQuerySet` | `WGPU::QuerySet` | ✅ | |
@@ -53,10 +53,10 @@ Legend:
53
53
  | `GPUSupportedLimits` | `Hash` | ◐ | Represented as a Ruby hash. |
54
54
  | `GPUTexture` | `WGPU::Texture` | ✅ | |
55
55
  | `GPUTextureView` | `WGPU::TextureView` | ✅ | |
56
- | `GPUUncapturedErrorEvent` | | | |
57
- | `GPUValidationError` | class-specific Ruby exceptions | ◐ | Validation errors are translated during resource creation. |
56
+ | `GPUUncapturedErrorEvent` | `Device#on_uncaptured_error` | | The callback receives `WGPU::GPUError`; there is no DOM event wrapper. |
57
+ | `GPUValidationError` | `WGPU::ValidationError` | ◐ | Validation errors are translated during resource creation and typed error-scope handling. |
58
58
  | `WGSLLanguageFeatures` | — | ❌ | |
59
- | `GPUObjectBase` | `#handle`, labels on descriptors | | Common released-state behavior is not yet centralized. |
59
+ | `GPUObjectBase` | `WGPU::NativeResource` | | All native wrappers share `handle`, `label`, `released?`, `use`, `inspect`, idempotent release, and use-after-release guards. |
60
60
  | `GPUPipelineBase` | `#get_bind_group_layout` | ✅ | Implemented by compute and render pipelines. |
61
61
  | `GPUCommandsMixin` | `WGPU::CommandEncoder` | ✅ | |
62
62
  | `GPUDebugCommandsMixin` | encoder/pass debug methods | ✅ | |
@@ -76,19 +76,22 @@ and DOM canvas ownership) are not implemented.
76
76
 
77
77
  Constructors are omitted where objects are normally returned by another
78
78
  wrapper. `handle` readers are listed once here as a common escape hatch for
79
- native interoperation.
79
+ native interoperation. Every native wrapper also has the common
80
+ `label`, `released?`, `use`, `inspect`, and idempotent `release` methods from
81
+ `WGPU::NativeResource`.
80
82
 
81
83
  | Ruby class | Public class methods | Public instance methods |
82
84
  |---|---|---|
85
+ | `WGPU` | — | module methods `log_level`, `log_level=`, `on_log` |
83
86
  | `WGPU::Instance` | — | `request_adapter`, `request_adapter_async`, `enumerate_adapters`, `enumerate_adapters_async`, `process_events`, `get_canvas_context`, `release`, `handle` |
84
87
  | `WGPU::Adapter` | `request`, `from_handle` | `request_device`, `request_device_async`, `info`, `name`, `vendor`, `adapter_type`, `backend_type`, `features`, `has_feature?`, `limits`, `summary`, `release`, `handle`, `instance` |
85
- | `WGPU::Device` | `request` | `queue`, `adapter`, `adapter_info`, `features`, `has_feature?`, `limits`, all `create_*` methods, `push_error_scope`, `pop_error_scope`, `pop_error_scope_async`, `with_error_scope`, `poll`, `destroy`, `release`, `handle` |
88
+ | `WGPU::Device` | `request` | `queue`, `adapter`, `adapter_info`, `features`, `has_feature?`, `limits`, all `create_*` methods, `push_error_scope`, `pop_error_scope`, `pop_error_scope_async`, `pop_error_scope_typed`, `with_error_scope`, `on_uncaptured_error`, `on_device_lost`, `poll`, `destroy`, `release`, `handle` |
86
89
  | `WGPU::Queue` | — | `submit`, `write_buffer`, `write_texture`, `read_buffer`, `read_texture`, `on_submitted_work_done`, `on_submitted_work_done_async`, `release`, `handle` |
87
90
  | `WGPU::Surface` | `from_metal_layer`, `from_windows_hwnd`, `from_xlib_window`, `from_wayland_surface` | `configure`, `unconfigure`, `current_texture`, `get_current_texture`, `present`, `get_configuration`, `get_preferred_format`, `capabilities`, `release`, `handle` |
88
91
  | `WGPU::CanvasContext` | — | `configure`, `unconfigure`, `get_current_texture`, `present`, `get_configuration`, `get_preferred_format`, `set_physical_size`, `physical_size`, `release` |
89
- | `WGPU::Buffer` | — | `write`, `mapped_range`, `get_mapped_range`, `unmap`, `map_sync`, `map_async`, `read_mapped_data`, `read_mapped`, `write_mapped`, `read_mapped_floats`, `map_state`, `size`, `usage`, `destroy`, `release`, `handle` |
90
- | `WGPU::BufferMappedRange` | — | `read_bytes`, `write_bytes`, `read_floats`, `write_floats` |
91
- | `WGPU::Texture` | `from_handle` | `create_view`, `width`, `height`, `depth_or_array_layers`, `size`, `mip_level_count`, `sample_count`, `dimension`, `format`, `usage`, `destroy`, `release`, `handle` |
92
+ | `WGPU::Buffer` | — | `write`, `mapped_range`, `get_mapped_range`, `unmap`, `map_sync`, `map_async`, `read_mapped_data`, `read_mapped`, `write_mapped`, typed `read_mapped_*` methods, `read_mapped_values`, `map_state`, `size`, `usage`, `destroy`, `release`, `handle` |
93
+ | `WGPU::BufferMappedRange` | — | `read`, `write`, `read_bytes`, `write_bytes`, and typed `read_*` / `write_*` methods |
94
+ | `WGPU::Texture` | `from_handle` | `create_view`, `width`, `height`, `depth_or_array_layers`, `size`, `mip_level_count`, `sample_count`, `dimension`, `format`, `usage`, `surface_status`, `destroy`, `release`, `handle` |
92
95
  | `WGPU::TextureView` | `from_handle` | `texture`, `size`, `release`, `handle` |
93
96
  | `WGPU::Sampler` | — | `release`, `handle` |
94
97
  | `WGPU::QuerySet` | — | `count`, `type`, `destroy`, `release`, `handle` |
@@ -105,6 +108,10 @@ native interoperation.
105
108
  | `WGPU::RenderBundleEncoder` | — | pipeline/bind-group/buffer setup, draw/debug methods, `finish`, `release`, `handle` |
106
109
  | `WGPU::RenderBundle` | — | `release`, `handle` |
107
110
  | `WGPU::AsyncTask` | — | `wait`, `value`, `then`, `complete?`, `pending?`, `error` |
111
+ | `WGPU::GPUError` | `from_hash` | `type`, `message`, `exception_class`, `raise!`, `to_h` |
112
+ | `WGPU::CompilationMessage` | — | `type`, `message`, `line_num`/`line`, `line_pos`/`column`, `offset`, `length`, `to_s` |
113
+ | `WGPU::DataTypes` | `pack`, `unpack`, `byte_size`, `to_pointer`, `validate_alignment!` | — |
114
+ | `WGPU::TextureFormat` | `block_size`, `block_dimensions`, `bytes_per_row`, `aligned_bytes_per_row`, `block_info` | — |
108
115
  | `WGPU::Window::SDLWindow` | — | `create_surface`, event/key helpers, `drawable_size`, `close` |
109
116
 
110
117
  ## Examples as executable specifications
@@ -123,9 +130,10 @@ native interoperation.
123
130
  | `10_textured_quad.rb` | Texture sampling | texture upload/view, sampler, texture bind group, draw |
124
131
  | `11_rotating_cube.rb` | Depth-tested 3D rendering | vertex/index/uniform/depth resources, pipeline, per-frame uniform write, draw |
125
132
  | `12_headless_render.rb` | Offscreen triangle and readback | render-to-texture, block render pass, aligned texture readback, pixel assertions |
126
- | `13_error_handling.rb` | Typed and labeled validation error | `BufferError`, operation/label context |
133
+ | `13_error_handling.rb` | Typed and labeled validation error | `ArgumentError`, operation/label context |
127
134
  | `14_async_map.rb` | Async map and callback retention | `map_async`, `AsyncTask`, typed mapped reads, GC stress |
128
135
  | `15_timestamp_query.rb` | Feature-gated GPU timestamps | timestamp pass writes, query resolution, u64 readback |
136
+ | `16_texture_readback.rb` | Non-aligned-width texture readback | texture row alignment helpers, caller-owned reusable staging |
129
137
 
130
138
  When a public API changes, the corresponding example is part of the acceptance
131
139
  test surface. Compute and headless examples are automated in GPU CI; SDL
data/docs/async.md CHANGED
@@ -19,11 +19,21 @@ That release also exports `wgpuBufferGetMapState` as an unimplemented panic
19
19
  stub. `Buffer#map_state` is maintained by wgpu-ruby as `:unmapped`,
20
20
  `:pending`, or `:mapped` while map operations run.
21
21
 
22
+ The exported `wgpuDeviceCreateComputePipelineAsync` and
23
+ `wgpuDeviceCreateRenderPipelineAsync` functions are also panic stubs in
24
+ wgpu-native v27. Pipeline `*_async` methods therefore retain the safe Ruby
25
+ Thread implementation until those native entry points are implemented.
26
+
22
27
  Synchronous adapter/device requests, `Buffer#map_sync`,
23
28
  `Device#pop_error_scope`, and `Queue#on_submitted_work_done` accept
24
29
  `timeout:` in seconds. The default `nil` preserves the prior unbounded wait.
25
30
  Expiry raises `WGPU::TimeoutError`.
26
31
 
32
+ `Device.request` can time out only when its adapter is associated with an
33
+ `Instance`. When adopting a native adapter, pass `instance:` to
34
+ `Adapter.from_handle`; without an instance there is no safe event-progress
35
+ boundary for cleaning up a device that completes after timeout.
36
+
27
37
  `*_async` methods return `WGPU::AsyncTask`, a convenience wrapper backed by a
28
38
  Ruby Thread. Concurrent operations on the same WebGPU object are not
29
39
  guaranteed safe. Prefer synchronous operations or external serialization when
@@ -22,3 +22,28 @@ call `end_pass` (or `end`) and `release` yourself.
22
22
  Only one pass may be active on a command encoder. `CommandEncoder#finish`
23
23
  raises `WGPU::CommandError` while a pass is still active instead of handing
24
24
  invalid state to wgpu-native. Calling `end_pass` more than once is harmless.
25
+
26
+ ## Queries and debug markers
27
+
28
+ Pass descriptors accept `timestamp_writes:` with a query set and beginning/end
29
+ indices. Render passes additionally accept `occlusion_query_set:` and expose
30
+ `begin_occlusion_query` / `end_occlusion_query`. Copy completed query values
31
+ into a buffer with `CommandEncoder#resolve_query_set`; timestamp queries
32
+ require the adapter's `:timestamp_query` feature and a device requested with
33
+ that feature.
34
+
35
+ [`examples/15_timestamp_query.rb`](../examples/15_timestamp_query.rb) shows the
36
+ complete feature-gated timestamp flow, including query resolution and typed
37
+ `u64` readback.
38
+
39
+ Command encoders, compute/render passes, and render bundle encoders expose
40
+ `push_debug_group`, `pop_debug_group`, and `insert_debug_marker`. Labels are
41
+ forwarded directly to wgpu-native for capture/debugging tools:
42
+
43
+ ```ruby
44
+ encoder.push_debug_group("upload and dispatch")
45
+ encoder.insert_debug_marker("resources ready")
46
+ encoder.pop_debug_group
47
+ ```
48
+
49
+ Debug groups must be balanced before the encoder or pass is finished.
@@ -3,6 +3,7 @@
3
3
  Compute workloads need Ruby 3.2+, the `wgpu` gem, and a supported GPU driver.
4
4
  They do not need SDL3.
5
5
 
6
+ <!-- wgpu-example: run; expect: [2, 4, 6, 8] -->
6
7
  ```ruby
7
8
  require "wgpu"
8
9
 
@@ -5,13 +5,13 @@ WebGPU pipeline descriptors. Passing `layout: :auto`, `"auto"`, or `nil` request
5
5
  automatic layout inference. A layout object continues to select an explicit
6
6
  pipeline layout.
7
7
 
8
- ## Default render state
8
+ ## Default pipeline state
9
9
 
10
10
  The wrapper applies the following WebGPU defaults when a field is omitted:
11
11
 
12
12
  | State | Field | Default |
13
13
  |---|---|---|
14
- | Vertex | `entry_point` | `"main"` |
14
+ | Vertex/fragment/compute | `entry_point` | omitted (`nil`) |
15
15
  | Vertex buffer | `step_mode` | `:vertex` |
16
16
  | Primitive | `topology` | `:triangle_list` |
17
17
  | Primitive | `strip_index_format` | `:undefined` |
@@ -33,6 +33,11 @@ The wrapper applies the following WebGPU defaults when a field is omitted:
33
33
  depth/stencil state requires `format`. Unknown descriptor keys produce a warning
34
34
  so misspellings are visible while preserving v1.x compatibility.
35
35
 
36
+ When `entry_point` is omitted, the wrapper passes WebGPU's nullable string
37
+ sentinel to wgpu-native. Native pipeline creation then selects the sole matching
38
+ entry point in that shader stage. Specify `entry_point:` when a stage contains
39
+ multiple matching entry points.
40
+
36
41
  Both vertex/fragment and compute stages accept `constants:`, a Hash mapping WGSL
37
42
  override names to numeric values. The descriptor keeps all FFI strings and
38
43
  arrays alive until native pipeline creation completes.
data/docs/releasing.md CHANGED
@@ -3,7 +3,17 @@
3
3
  The release workflow uses RubyGems Trusted Publishing and runs for `v*` tags.
4
4
  Before the first automated release, configure a trusted publisher for the
5
5
  `ydah/wgpu-ruby` repository, workflow `release.yml`, and GitHub environment
6
- `release` on RubyGems.org.
6
+ `release` on RubyGems.org. The trusted publisher and protected environment are
7
+ repository/RubyGems.org settings, so they cannot be configured or verified from
8
+ this repository alone.
9
+
10
+ ## Dry run
11
+
12
+ Run the **Push gem** workflow manually from the GitHub Actions page before
13
+ tagging a release. A `workflow_dispatch` run executes the same native build,
14
+ ABI verification, CPU and GPU specs, examples, RuboCop, RBS, YARD, coverage,
15
+ and gem build checks as a tag run. It skips both the tag/version comparison and
16
+ the RubyGems publishing action, so it cannot publish a gem.
7
17
 
8
18
  Release checklist:
9
19
 
@@ -17,6 +17,10 @@ Ruby finalizers.
17
17
  - Every native wrapper supports `use { |resource| ... }`. It returns the block
18
18
  result and releases the wrapper in `ensure`, including when the block raises.
19
19
  This is Ruby convenience API rather than a WebGPU operation.
20
+ - Device callbacks share a lifetime across the device and every derived native
21
+ wrapper. Releasing a device before a child does not free callback closures
22
+ that wgpu-native may still reach; they are freed after the final child
23
+ wrapper and pending callback operation complete.
20
24
 
21
25
  For short-lived resources:
22
26
 
@@ -81,10 +85,20 @@ does not itself release the command buffer or its referenced Ruby wrappers.
81
85
  ## Internal staging resources
82
86
 
83
87
  `Queue#read_buffer` and `Queue#read_texture` create a temporary staging buffer,
84
- submit a copy, map and read it, then unmap and release it. In the baseline
85
- implementation cleanup runs from `ensure`, including exceptional paths.
88
+ submit a copy, map and read it, then unmap and release it. Cleanup independently
89
+ attempts to unmap and release the command buffer, encoder, and internal staging
90
+ buffer on every exit path, even when an earlier cleanup operation raises.
86
91
  Applications should not attempt to retain or release this internal buffer.
87
92
 
93
+ For repeated readbacks, pass a caller-owned `staging:` buffer created by the
94
+ same device. It must be unmapped, have `:map_read` and `:copy_dst` usage, and be
95
+ large enough for the requested byte range (`read_buffer`) or padded copy
96
+ footprint (`read_texture`). The queue unmaps it after each read but never
97
+ releases it, so it can be reused and the caller remains responsible for its
98
+ eventual release. A device-owned queue uses its owning device automatically;
99
+ only queues constructed without an owner require the explicit `device:`
100
+ keyword.
101
+
88
102
  ## Leak diagnostics
89
103
 
90
104
  Set `WGPU_DEBUG_LEAKS=1` before requiring the gem, or set
@@ -17,3 +17,30 @@ depth/stencil copies require an explicit `aspect:`.
17
17
  256-byte copy alignment and large enough for the requested width and format.
18
18
  Use `aligned_bytes_per_row` when the tight row is not already aligned. Automatic
19
19
  padding and mipmap generation are intentionally outside this low-level binding.
20
+
21
+ For example, an `rgba8_unorm` row that is 65 pixels wide contains 260 bytes,
22
+ but its smallest valid readback stride is 512 bytes:
23
+
24
+ ```ruby
25
+ width = 65
26
+ height = 2
27
+ bytes_per_row = WGPU::TextureFormat.aligned_bytes_per_row(width, :rgba8_unorm)
28
+ # => 512
29
+
30
+ pixels = queue.read_texture(
31
+ source: { texture: texture },
32
+ data_layout: { bytes_per_row: bytes_per_row, rows_per_image: height },
33
+ size: { width: width, height: height }
34
+ )
35
+ ```
36
+
37
+ The returned string includes row padding and has
38
+ `bytes_per_row * rows_per_image * depth_or_array_layers` bytes. Read only the
39
+ tight bytes of each row when processing pixels.
40
+
41
+ Both `Queue#read_texture` and `Queue#read_buffer` accept an optional `staging:`
42
+ buffer for repeated readbacks. It must be unmapped, belong to the same device,
43
+ be large enough for the full returned string, and include both `:map_read` and
44
+ `:copy_dst` usage. The helper unmaps caller-provided staging after each read but
45
+ does not release it, so the caller can reuse it and remains responsible for
46
+ releasing it. The queue uses its owning device when `device:` is omitted.
@@ -27,6 +27,11 @@ Pass `timeout:` to adapter/device requests, `Buffer#map_sync`,
27
27
  `WGPU::TimeoutError`. In an event loop, regularly call
28
28
  `Instance#process_events` or `Device#poll`.
29
29
 
30
+ For a native adapter wrapped with `Adapter.from_handle`, provide its
31
+ `instance:` before using `Device.request(timeout: ...)`. Timeout is rejected
32
+ without that instance because a late native completion cannot be cleaned up
33
+ safely.
34
+
30
35
  ## Texture readback rejects bytes_per_row
31
36
 
32
37
  Buffer/texture copy rows must be 256-byte aligned. Calculate the value with
@@ -9,11 +9,23 @@ Use this checklist for each pinned wgpu-native update.
9
9
  remains.
10
10
  4. Run `bundle exec rake wgpu:clean wgpu:install` on a disposable cache, then
11
11
  test an existing legacy cache separately.
12
- 5. Run `bundle exec rake wgpu:verify_abi`. Review every enum addition, removal,
13
- and value change against the checksum-pinned `webgpu.h`.
12
+ 5. Regenerate the repository enum fixture from the installed release header:
13
+
14
+ ```console
15
+ bundle exec ruby script/update_abi_fixture.rb \
16
+ "$WGPU_CACHE_DIR/<version>/include/webgpu/webgpu.h"
17
+ ```
18
+
19
+ The generated comment records the source SHA-256. For the current
20
+ `v27.0.4.0` release, `include/webgpu/webgpu.h` is
21
+ `a6fccf7f9f2fa674d1adfe4f6ea89784a876395b2307bfc2b06f2e77cf6cf356`.
22
+ Review every enum addition, removal, and value change, then run
23
+ `bundle exec rake wgpu:verify_abi`.
14
24
  6. Compare structs, field offsets, callback signatures, and exported functions.
15
- Add ABI specs for changed layouts. Mark functions optional only when the Ruby
16
- API has a valid capability fallback.
25
+ Add ABI specs for changed layouts. Check `include/webgpu/wgpu.h` for native
26
+ extensions as well. `wgpuGetVersion` encodes `vMAJOR.MINOR.PATCH.BUILD` in
27
+ four bytes; the verifier compares it with `Distribution::VERSION`. Mark
28
+ functions optional only when the Ruby API has a valid capability fallback.
17
29
  7. Run `bundle exec rspec --tag '~gpu'`, RuboCop, RBS validation, and YARD.
18
30
  8. Run every `:gpu` spec and `rake examples:ci` with lavapipe. Run rendering
19
31
  examples on at least one real surface backend.
@@ -23,4 +35,3 @@ Use this checklist for each pinned wgpu-native update.
23
35
 
24
36
  For a dry run, use a temporary `WGPU_CACHE_DIR` and restore the version/artifact
25
37
  edits afterward. Never replace the shared user cache as part of verification.
26
-
@@ -4,6 +4,8 @@ require "timeout"
4
4
 
5
5
  module WGPU
6
6
  class AsyncTask
7
+ # Starts a background task that evaluates the given block.
8
+ # @raise [ArgumentError] if no block is supplied
7
9
  def initialize(&block)
8
10
  raise ArgumentError, "block is required" unless block
9
11
 
@@ -18,12 +20,20 @@ module WGPU
18
20
  end
19
21
  end
20
22
 
23
+ # Waits until the task finishes.
24
+ # @param timeout [Numeric, nil] maximum number of seconds to wait
25
+ # @return [AsyncTask] this task
26
+ # @raise [Timeout::Error] if the task does not finish in time
21
27
  def wait(timeout: nil)
22
28
  joined = timeout ? @thread.join(timeout) : @thread.join
23
29
  raise Timeout::Error, "async task timeout" unless joined
24
30
  self
25
31
  end
26
32
 
33
+ # Waits for and returns the task result.
34
+ # @param timeout [Numeric, nil] maximum number of seconds to wait
35
+ # @return [Object] block result
36
+ # @raise [StandardError] re-raises an exception raised by the block
27
37
  def value(timeout: nil)
28
38
  wait(timeout: timeout)
29
39
  raise @error if @error
@@ -31,20 +41,29 @@ module WGPU
31
41
  @value
32
42
  end
33
43
 
44
+ # Creates a task that transforms this task's result.
45
+ # @yieldparam value [Object] completed task result
46
+ # @return [AsyncTask] chained task
34
47
  def then(&block)
35
48
  AsyncTask.new do
36
49
  block.call(value)
37
50
  end
38
51
  end
39
52
 
53
+ # Reports whether the task has finished.
54
+ # @return [Boolean]
40
55
  def complete?
41
56
  !@thread.alive?
42
57
  end
43
58
 
59
+ # Reports whether the task is still running.
60
+ # @return [Boolean]
44
61
  def pending?
45
62
  @thread.alive?
46
63
  end
47
64
 
65
+ # Returns the exception raised by the task, if any.
66
+ # @return [StandardError, nil]
48
67
  def error
49
68
  value
50
69
  nil
@@ -4,21 +4,34 @@ module WGPU
4
4
  class CommandBuffer
5
5
  attr_reader :handle
6
6
 
7
- def initialize(handle)
7
+ # Wraps an encoded native command buffer.
8
+ # @param handle [FFI::Pointer] native command buffer handle
9
+ # @param device [Device, nil] device whose callbacks the command buffer may use
10
+ def initialize(handle, device: nil)
8
11
  @handle = handle
12
+ @device = device
9
13
  @submitted = false
10
14
  end
11
15
 
16
+ # Reports whether this command buffer has been submitted.
17
+ # @return [Boolean]
12
18
  def submitted?
13
19
  @submitted
14
20
  end
15
21
 
22
+ # Marks this command buffer as submitted.
23
+ # @raise [CommandError] if it was already submitted
24
+ # @return [void]
16
25
  def mark_submitted!
17
26
  raise CommandError, "Command buffer has already been submitted" if @submitted
18
27
 
19
28
  @submitted = true
20
29
  end
21
30
 
31
+ # Releases the native command buffer handle.
32
+ #
33
+ # Calling this method more than once has no effect.
34
+ # @return [void]
22
35
  def release
23
36
  return if @handle.null?
24
37
  Native.wgpuCommandBufferRelease(@handle)