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,6 +4,10 @@ module WGPU
4
4
  class PipelineLayout
5
5
  attr_reader :handle
6
6
 
7
+ # Creates a pipeline layout from ordered bind group layouts.
8
+ # @param device [Device] owning device
9
+ # @param bind_group_layouts [Array<BindGroupLayout>] layouts by group index
10
+ # @raise [PipelineError] if native validation or creation fails
7
11
  def initialize(device, label: nil, bind_group_layouts:)
8
12
  @device = device
9
13
 
@@ -34,6 +38,10 @@ module WGPU
34
38
  end
35
39
  end
36
40
 
41
+ # Releases the native pipeline layout handle.
42
+ #
43
+ # Calling this method more than once has no effect.
44
+ # @return [void]
37
45
  def release
38
46
  return if @handle.null?
39
47
  Native.wgpuPipelineLayoutRelease(@handle)
@@ -4,6 +4,12 @@ module WGPU
4
4
  class RenderPipeline
5
5
  attr_reader :handle
6
6
 
7
+ # Creates a render pipeline.
8
+ # @param device [Device] owning device
9
+ # @param layout [PipelineLayout, :auto, nil] pipeline layout
10
+ # @param vertex [Hash] programmable vertex stage descriptor
11
+ # @param fragment [Hash, nil] optional fragment stage descriptor
12
+ # @raise [PipelineError] if native validation or creation fails
7
13
  def initialize(device, label: nil, layout:, vertex:, primitive: {}, depth_stencil: nil, multisample: {}, fragment: nil)
8
14
  @device = device
9
15
  desc, @pointers = build_descriptor(
@@ -26,12 +32,20 @@ module WGPU
26
32
  end
27
33
  end
28
34
 
35
+ # Returns the bind group layout inferred or assigned at an index.
36
+ # @param index [Integer] bind group index
37
+ # @return [BindGroupLayout]
38
+ # @raise [PipelineError] if no native layout is returned
29
39
  def get_bind_group_layout(index)
30
40
  handle = Native.wgpuRenderPipelineGetBindGroupLayout(@handle, index)
31
41
  raise PipelineError, "Failed to get bind group layout at index #{index}" if handle.null?
32
- BindGroupLayout.from_handle(handle)
42
+ BindGroupLayout.from_handle(handle, device: @device)
33
43
  end
34
44
 
45
+ # Releases the native render pipeline handle.
46
+ #
47
+ # Calling this method more than once has no effect.
48
+ # @return [void]
35
49
  def release
36
50
  return if @handle.null?
37
51
  Native.wgpuRenderPipelineRelease(@handle)
@@ -66,15 +80,12 @@ module WGPU
66
80
  vertex_state[:next_in_chain] = nil
67
81
  vertex_state[:module] = vertex[:module].handle
68
82
 
69
- entry_point = vertex[:entry_point] || "main"
70
- entry_ptr = FFI::MemoryPointer.from_string(entry_point)
71
- @pointers << entry_ptr
72
- vertex_state[:entry_point][:data] = entry_ptr
73
- vertex_state[:entry_point][:length] = entry_point.bytesize
74
-
75
- vertex_state[:constant_count] = 0
76
- vertex_state[:constants] = nil
77
- setup_constants(vertex_state, vertex[:constants])
83
+ DescriptorHelpers.set_nullable_string_view(
84
+ vertex_state[:entry_point],
85
+ vertex[:entry_point],
86
+ keepalive: @pointers
87
+ )
88
+ DescriptorHelpers.set_constants(vertex_state, vertex[:constants], keepalive: @pointers)
78
89
 
79
90
  buffers = vertex[:buffers] || []
80
91
  if buffers.empty?
@@ -256,15 +267,12 @@ module WGPU
256
267
  frag[:next_in_chain] = nil
257
268
  frag[:module] = fragment[:module].handle
258
269
 
259
- entry_point = fragment[:entry_point] || "main"
260
- entry_ptr = FFI::MemoryPointer.from_string(entry_point)
261
- @pointers << entry_ptr
262
- frag[:entry_point][:data] = entry_ptr
263
- frag[:entry_point][:length] = entry_point.bytesize
264
-
265
- frag[:constant_count] = 0
266
- frag[:constants] = nil
267
- setup_constants(frag, fragment[:constants])
270
+ DescriptorHelpers.set_nullable_string_view(
271
+ frag[:entry_point],
272
+ fragment[:entry_point],
273
+ keepalive: @pointers
274
+ )
275
+ DescriptorHelpers.set_constants(frag, fragment[:constants], keepalive: @pointers)
268
276
 
269
277
  targets = fragment[:targets] || []
270
278
  if targets.empty?
@@ -363,28 +371,5 @@ module WGPU
363
371
  return nil if layout.nil? || layout == :auto || layout == "auto"
364
372
  layout.handle
365
373
  end
366
-
367
- def setup_constants(stage_state, constants)
368
- return if constants.nil? || constants.empty?
369
-
370
- constants_ptr = FFI::MemoryPointer.new(Native::ConstantEntry, constants.size)
371
- @pointers << constants_ptr
372
-
373
- constants.each_with_index do |(key, value), i|
374
- entry_ptr = constants_ptr + (i * Native::ConstantEntry.size)
375
- entry = Native::ConstantEntry.new(entry_ptr)
376
- entry[:next_in_chain] = nil
377
-
378
- key_str = key.to_s
379
- key_ptr = FFI::MemoryPointer.from_string(key_str)
380
- @pointers << key_ptr
381
- entry[:key][:data] = key_ptr
382
- entry[:key][:length] = key_str.bytesize
383
- entry[:value] = value.to_f
384
- end
385
-
386
- stage_state[:constant_count] = constants.size
387
- stage_state[:constants] = constants_ptr
388
- end
389
374
  end
390
375
  end
@@ -4,6 +4,12 @@ module WGPU
4
4
  class ShaderModule
5
5
  attr_reader :handle
6
6
 
7
+ # Creates a shader module from WGSL, GLSL, or SPIR-V input.
8
+ # @param device [Device] owning device
9
+ # @param code [String, Array<Integer>, nil] shader source or binary
10
+ # @param spirv [String, Array<Integer>, nil] explicit SPIR-V input
11
+ # @param validate [Boolean] whether to fetch and raise compilation errors
12
+ # @raise [ShaderError] if native validation, creation, or compilation fails
7
13
  def initialize(device, label: nil, code: nil, spirv: nil, compilation_hints: [], validate: false)
8
14
  @device = device
9
15
  @pointers = []
@@ -43,6 +49,9 @@ module WGPU
43
49
  end
44
50
  end
45
51
 
52
+ # Fetches compiler diagnostics for the shader.
53
+ # @return [Hash] request status and compilation messages
54
+ # @raise [ShaderError] if the pinned native library cannot provide diagnostics
46
55
  def get_compilation_info
47
56
  unless Native.compilation_info_available?
48
57
  raise ShaderError,
@@ -52,34 +61,41 @@ module WGPU
52
61
  result_holder = { done: false, status: nil, messages: [] }
53
62
  instance = @device.adapter&.instance
54
63
 
64
+ callback_lifetime_release = device_callback_lifetime_lease
65
+ callback_token = nil
55
66
  callback = FFI::Function.new(
56
67
  :void, [:uint32, :pointer, :pointer, :pointer]
57
68
  ) do |status, compilation_info_ptr, _userdata1, _userdata2|
58
- result_holder[:done] = true
59
- result_holder[:status] = Native::CompilationInfoRequestStatus[status]
60
-
61
- unless compilation_info_ptr.null?
62
- info = Native::CompilationInfo.new(compilation_info_ptr)
63
- count = info[:message_count]
64
- if count > 0 && !info[:messages].null?
65
- count.times do |i|
66
- msg_ptr = info[:messages] + (i * Native::CompilationMessage.size)
67
- msg = Native::CompilationMessage.new(msg_ptr)
68
- message_text = if msg[:message][:data] && !msg[:message][:data].null? && msg[:message][:length] > 0
69
- msg[:message][:data].read_string(msg[:message][:length])
70
- else
71
- ""
72
- end
73
- result_holder[:messages] << CompilationMessage.new(
74
- type: msg[:type],
75
- message: message_text,
76
- line_num: msg[:line_num],
77
- line_pos: msg[:line_pos],
78
- offset: msg[:offset],
79
- length: msg[:length]
80
- )
69
+ begin
70
+ result_holder[:status] = Native::CompilationInfoRequestStatus[status]
71
+
72
+ unless compilation_info_ptr.null?
73
+ info = Native::CompilationInfo.new(compilation_info_ptr)
74
+ count = info[:message_count]
75
+ if count > 0 && !info[:messages].null?
76
+ count.times do |i|
77
+ msg_ptr = info[:messages] + (i * Native::CompilationMessage.size)
78
+ msg = Native::CompilationMessage.new(msg_ptr)
79
+ message_text = if msg[:message][:data] && !msg[:message][:data].null? && msg[:message][:length] > 0
80
+ msg[:message][:data].read_string(msg[:message][:length])
81
+ else
82
+ ""
83
+ end
84
+ result_holder[:messages] << CompilationMessage.new(
85
+ type: msg[:type],
86
+ message: message_text,
87
+ line_num: msg[:line_num],
88
+ line_pos: msg[:line_pos],
89
+ offset: msg[:offset],
90
+ length: msg[:length]
91
+ )
92
+ end
81
93
  end
82
94
  end
95
+ result_holder[:done] = true
96
+ ensure
97
+ CallbackKeepalive.release(self, callback_token)
98
+ callback_lifetime_release.call
83
99
  end
84
100
  end
85
101
 
@@ -91,12 +107,15 @@ module WGPU
91
107
  callback_info[:userdata2] = nil
92
108
 
93
109
  callback_token = CallbackKeepalive.retain(self, callback)
94
- begin
95
- future = Native.wgpuShaderModuleGetCompilationInfo(@handle, callback_info)
96
- AsyncWaiter.wait(status_holder: result_holder, instance: instance, device: @device, future: future)
97
- ensure
98
- CallbackKeepalive.release(self, callback_token)
99
- end
110
+ future =
111
+ begin
112
+ Native.wgpuShaderModuleGetCompilationInfo(@handle, callback_info)
113
+ rescue StandardError
114
+ CallbackKeepalive.release(self, callback_token)
115
+ callback_lifetime_release.call
116
+ raise
117
+ end
118
+ AsyncWaiter.wait(status_holder: result_holder, instance: instance, device: @device, future: future)
100
119
 
101
120
  {
102
121
  status: result_holder[:status],
@@ -104,10 +123,16 @@ module WGPU
104
123
  }
105
124
  end
106
125
 
126
+ # Fetches compiler diagnostics on a background task.
127
+ # @return [AsyncTask] task yielding compilation information
107
128
  def get_compilation_info_async
108
129
  AsyncTask.new { get_compilation_info }
109
130
  end
110
131
 
132
+ # Releases the native shader module handle.
133
+ #
134
+ # Calling this method more than once has no effect.
135
+ # @return [void]
111
136
  def release
112
137
  return if @handle.null?
113
138
  Native.wgpuShaderModuleRelease(@handle)