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
@@ -4,27 +4,62 @@ module WGPU
4
4
  class Adapter
5
5
  attr_reader :handle, :instance
6
6
 
7
+ # Wraps an existing native adapter handle.
8
+ # @param handle [FFI::Pointer] native adapter handle
9
+ # @param instance [Instance, nil] instance that owns the adapter
10
+ # @return [Adapter]
7
11
  def self.from_handle(handle, instance: nil)
8
- adapter = allocate
9
- adapter.instance_variable_set(:@handle, handle)
12
+ adapter = adopt_native_handle(handle)
10
13
  adapter.instance_variable_set(:@instance, instance)
11
14
  adapter
12
15
  end
13
16
 
17
+ # Requests an adapter and waits for the native callback.
18
+ #
19
+ # @param instance [Instance] instance used for discovery and callback progress
20
+ # @param power_preference [Symbol, Integer] preferred power profile
21
+ # @param backend [Symbol, Integer, nil] backend to restrict discovery to
22
+ # @param feature_level [Symbol, Integer] requested WebGPU feature level
23
+ # @param force_fallback_adapter [Boolean] whether to require a fallback adapter
24
+ # @param compatible_surface [Surface, nil] surface the adapter must support
25
+ # @param timeout [Numeric, nil] maximum wait time in seconds
26
+ # @return [Adapter] requested adapter
27
+ # @raise [AdapterError] if no adapter can be acquired
28
+ # @raise [TimeoutError] if the request exceeds +timeout+
14
29
  def self.request(instance, power_preference: :high_performance, backend: nil, feature_level: :core,
15
30
  force_fallback_adapter: false, compatible_surface: nil, timeout: nil)
16
31
  adapter_ptr = FFI::MemoryPointer.new(:pointer)
17
- status_holder = { value: nil, message: nil }
32
+ status_holder = {
33
+ done: false,
34
+ value: nil,
35
+ message: nil,
36
+ abandoned: false,
37
+ cleanup_claimed: false,
38
+ mutex: Mutex.new
39
+ }
18
40
 
41
+ callback_token = nil
19
42
  callback = FFI::Function.new(
20
43
  :void, [:uint32, :pointer, Native::StringView.by_value, :pointer, :pointer]
21
44
  ) do |status, adapter, message, _userdata1, _userdata2|
22
- status_holder[:value] = Native::RequestAdapterStatus[status]
23
- if message[:data] && !message[:data].null? && message[:length] > 0
24
- status_holder[:message] = message[:data].read_string(message[:length])
45
+ abandoned_adapter = nil
46
+ begin
47
+ status_holder[:mutex].synchronize do
48
+ status_holder[:value] = Native::RequestAdapterStatus[status]
49
+ if message[:data] && !message[:data].null? && message[:length] > 0
50
+ status_holder[:message] = message[:data].read_string(message[:length])
51
+ end
52
+ adapter_ptr.write_pointer(adapter)
53
+ status_holder[:done] = true
54
+ if status_holder[:abandoned] && !status_holder[:cleanup_claimed]
55
+ status_holder[:cleanup_claimed] = true
56
+ abandoned_adapter = adapter
57
+ end
58
+ end
59
+ Native.wgpuAdapterRelease(abandoned_adapter) if abandoned_adapter && !abandoned_adapter.null?
60
+ ensure
61
+ CallbackKeepalive.release(instance, callback_token)
25
62
  end
26
- adapter_ptr.write_pointer(adapter)
27
- status_holder[:done] = true
28
63
  end
29
64
 
30
65
  options = Native::RequestAdapterOptions.new
@@ -55,12 +90,26 @@ module WGPU
55
90
  callback_info[:userdata2] = nil
56
91
 
57
92
  callback_token = CallbackKeepalive.retain(instance, callback)
93
+ future =
94
+ begin
95
+ Native.wgpuInstanceRequestAdapter(instance.handle, options, callback_info)
96
+ rescue StandardError
97
+ CallbackKeepalive.release(instance, callback_token)
98
+ raise
99
+ end
100
+
58
101
  begin
59
- status_holder[:done] = false
60
- future = Native.wgpuInstanceRequestAdapter(instance.handle, options, callback_info)
61
102
  AsyncWaiter.wait(status_holder: status_holder, instance: instance, future: future, timeout: timeout)
62
- ensure
63
- CallbackKeepalive.release(instance, callback_token)
103
+ rescue TimeoutError
104
+ abandoned_adapter = status_holder[:mutex].synchronize do
105
+ status_holder[:abandoned] = true
106
+ next unless status_holder[:done] && !status_holder[:cleanup_claimed]
107
+
108
+ status_holder[:cleanup_claimed] = true
109
+ adapter_ptr.read_pointer
110
+ end
111
+ Native.wgpuAdapterRelease(abandoned_adapter) if abandoned_adapter && !abandoned_adapter.null?
112
+ raise
64
113
  end
65
114
 
66
115
  handle = adapter_ptr.read_pointer
@@ -72,11 +121,16 @@ module WGPU
72
121
  new(handle, instance: instance)
73
122
  end
74
123
 
124
+ # Wraps a native adapter handle.
125
+ # @param handle [FFI::Pointer] native adapter handle
126
+ # @param instance [Instance, nil] instance that owns the adapter
75
127
  def initialize(handle, instance: nil)
76
128
  @handle = handle
77
129
  @instance = instance
78
130
  end
79
131
 
132
+ # Requests a logical device from this adapter.
133
+ # @return [Device]
80
134
  def request_device(label: nil, required_features: [], required_limits: nil, timeout: nil)
81
135
  Device.request(
82
136
  self,
@@ -87,6 +141,8 @@ module WGPU
87
141
  )
88
142
  end
89
143
 
144
+ # Requests a logical device on a background task.
145
+ # @return [AsyncTask] task yielding a {Device}
90
146
  def request_device_async(label: nil, required_features: [], required_limits: nil, timeout: nil)
91
147
  AsyncTask.new do
92
148
  request_device(
@@ -98,6 +154,8 @@ module WGPU
98
154
  end
99
155
  end
100
156
 
157
+ # Returns identifying and backend information for the adapter.
158
+ # @return [Hash]
101
159
  def info
102
160
  info_struct = Native::AdapterInfo.new
103
161
  Native.wgpuAdapterGetInfo(@handle, info_struct)
@@ -117,22 +175,32 @@ module WGPU
117
175
  result
118
176
  end
119
177
 
178
+ # Returns the adapter's device name.
179
+ # @return [String]
120
180
  def name
121
181
  info[:device]
122
182
  end
123
183
 
184
+ # Returns the adapter vendor name.
185
+ # @return [String]
124
186
  def vendor
125
187
  info[:vendor]
126
188
  end
127
189
 
190
+ # Returns the backend used by the adapter.
191
+ # @return [Symbol, Integer]
128
192
  def backend_type
129
193
  info[:backend_type]
130
194
  end
131
195
 
196
+ # Returns the adapter's device classification.
197
+ # @return [Symbol, Integer]
132
198
  def adapter_type
133
199
  info[:adapter_type]
134
200
  end
135
201
 
202
+ # Lists optional features supported by the adapter.
203
+ # @return [Array<Symbol>]
136
204
  def features
137
205
  supported = Native::SupportedFeatures.new
138
206
  Native.wgpuAdapterGetFeatures(@handle, supported)
@@ -146,10 +214,15 @@ module WGPU
146
214
  result
147
215
  end
148
216
 
217
+ # Reports whether the adapter supports a feature.
218
+ # @param feature [Symbol] feature name
219
+ # @return [Boolean]
149
220
  def has_feature?(feature)
150
221
  features.include?(feature)
151
222
  end
152
223
 
224
+ # Returns the adapter resource limits.
225
+ # @return [Hash{Symbol => Integer}]
153
226
  def limits
154
227
  supported = Native::SupportedLimits.new
155
228
  supported[:next_in_chain] = nil
@@ -157,11 +230,17 @@ module WGPU
157
230
  limits_to_hash(supported[:limits])
158
231
  end
159
232
 
233
+ # Returns a concise human-readable adapter description.
234
+ # @return [String]
160
235
  def summary
161
236
  info_hash = info
162
237
  "#{info_hash[:device]} (#{info_hash[:adapter_type]}) via #{info_hash[:backend_type]}"
163
238
  end
164
239
 
240
+ # Releases the native adapter handle.
241
+ #
242
+ # Calling this method more than once has no effect.
243
+ # @return [void]
165
244
  def release
166
245
  return if @handle.null?
167
246
  Native.wgpuAdapterRelease(@handle)
@@ -10,10 +10,16 @@ module WGPU
10
10
 
11
11
  module_function
12
12
 
13
+ # Returns the delay between callback progress polls.
14
+ # @return [Float] seconds
13
15
  def poll_interval
14
16
  @poll_interval ||= POLL_INTERVAL_SECONDS
15
17
  end
16
18
 
19
+ # Sets the delay between callback progress polls.
20
+ # @param seconds [Numeric] positive delay in seconds
21
+ # @return [Float]
22
+ # @raise [ArgumentError] if the delay is not positive
17
23
  def poll_interval=(seconds)
18
24
  value = Float(seconds)
19
25
  raise ArgumentError, "poll interval must be positive" unless value.positive?
@@ -21,6 +27,10 @@ module WGPU
21
27
  @poll_interval = value
22
28
  end
23
29
 
30
+ # Selects the native callback delivery mode for an operation.
31
+ #
32
+ # @param instance [Instance, nil] instance capable of processing events
33
+ # @return [Integer] native {Native::CallbackMode} value
24
34
  def callback_mode(instance:)
25
35
  if instance
26
36
  callback_mode_value(:allow_process_events)
@@ -29,6 +39,11 @@ module WGPU
29
39
  end
30
40
  end
31
41
 
42
+ # Drives native event processing until a callback completes.
43
+ # @param status_holder [Hash] state whose +:done+ flag is set by the callback
44
+ # @param timeout [Numeric, nil] maximum wait time in seconds
45
+ # @return [void]
46
+ # @raise [TimeoutError] if completion exceeds the timeout
32
47
  def wait(status_holder:, instance: nil, device: nil, future: nil, timeout: nil)
33
48
  timeout = Float(timeout) if timeout
34
49
  raise ArgumentError, "timeout must be non-negative" if timeout&.negative?
@@ -4,6 +4,9 @@ module WGPU
4
4
  class CanvasContext
5
5
  attr_reader :physical_size
6
6
 
7
+ # Creates a canvas context for platform presentation information.
8
+ # @param instance [Instance] owning WebGPU instance
9
+ # @param present_info [Hash] platform handles or an existing surface
7
10
  def initialize(instance, present_info = {})
8
11
  @instance = instance
9
12
  @present_info = present_info || {}
@@ -12,21 +15,38 @@ module WGPU
12
15
  @config = nil
13
16
  end
14
17
 
18
+ # Updates the drawable's physical pixel size.
19
+ # @param width [Integer] width in pixels
20
+ # @param height [Integer] height in pixels
21
+ # @return [Array<Integer>] stored width and height
22
+ # @raise [ArgumentError] if either dimension is negative
15
23
  def set_physical_size(width, height)
16
24
  raise ArgumentError, "width and height must be non-negative" if width.to_i.negative? || height.to_i.negative?
17
25
 
18
26
  @physical_size = [width.to_i, height.to_i]
19
27
  end
20
28
 
29
+ # Returns the surface format preferred by an adapter.
30
+ #
31
+ # @param adapter [Adapter] adapter used to query surface capabilities
32
+ # @return [Symbol] preferred texture format
21
33
  def get_preferred_format(adapter)
22
34
  ensure_surface
23
35
  @surface.get_preferred_format(adapter)
24
36
  end
25
37
 
38
+ # Returns the most recent canvas configuration.
39
+ #
40
+ # @return [Hash, nil] configuration options, or +nil+ when unconfigured
26
41
  def get_configuration
27
42
  @config
28
43
  end
29
44
 
45
+ # Configures the backing surface for presentation.
46
+ # @param device [Device] device used to render frames
47
+ # @param format [Symbol, nil] surface texture format
48
+ # @return [Hash] resolved canvas configuration
49
+ # @raise [SurfaceError] if no surface exists or dimensions are not positive
30
50
  def configure(device:, format: nil, usage: :render_attachment, view_formats: [], color_space: "srgb", tone_mapping: nil, alpha_mode: :opaque, width: nil, height: nil, present_mode: :fifo)
31
51
  ensure_surface
32
52
 
@@ -59,21 +79,33 @@ module WGPU
59
79
  }
60
80
  end
61
81
 
82
+ # Removes the current surface configuration.
83
+ # @return [void]
62
84
  def unconfigure
63
85
  @surface&.unconfigure
64
86
  @config = nil
65
87
  end
66
88
 
89
+ # Acquires the texture for the current presentation frame.
90
+ #
91
+ # @return [Texture] acquired surface texture
92
+ # @raise [SurfaceError] if the context has not been configured
93
+ # @raise [SurfaceAcquisitionError] if acquisition fails
67
94
  def get_current_texture
68
95
  raise SurfaceError, "Canvas context must be configured before get_current_texture" unless @config
69
96
 
70
97
  @surface.current_texture
71
98
  end
72
99
 
100
+ # Presents the current surface texture.
101
+ # @return [void]
73
102
  def present
74
103
  @surface&.present
75
104
  end
76
105
 
106
+ # Releases the backing surface and clears the configuration.
107
+ #
108
+ # @return [void]
77
109
  def release
78
110
  @surface&.release
79
111
  @surface = nil