wgpu 1.1.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -0
  3. data/README.md +44 -5
  4. data/docs/README.md +22 -0
  5. data/docs/api_coverage.md +140 -0
  6. data/docs/async.md +41 -0
  7. data/docs/bind_groups.md +25 -0
  8. data/docs/buffer_data.md +37 -0
  9. data/docs/command_encoding.md +49 -0
  10. data/docs/errors.md +40 -0
  11. data/docs/getting_started_compute.md +62 -0
  12. data/docs/getting_started_rendering.md +62 -0
  13. data/docs/installation.md +94 -0
  14. data/docs/pipeline_descriptors.md +43 -0
  15. data/docs/releasing.md +32 -0
  16. data/docs/resource_lifetime.md +108 -0
  17. data/docs/shaders.md +32 -0
  18. data/docs/texture_readback.md +46 -0
  19. data/docs/troubleshooting.md +53 -0
  20. data/docs/upgrading_wgpu_native.md +37 -0
  21. data/ext/wgpu/extconf.rb +10 -142
  22. data/lib/wgpu/async_task.rb +19 -0
  23. data/lib/wgpu/commands/command_buffer.rb +25 -1
  24. data/lib/wgpu/commands/command_encoder.rb +123 -9
  25. data/lib/wgpu/commands/compute_pass.rb +53 -0
  26. data/lib/wgpu/commands/render_bundle.rb +9 -1
  27. data/lib/wgpu/commands/render_bundle_encoder.rb +65 -4
  28. data/lib/wgpu/commands/render_pass.rb +136 -8
  29. data/lib/wgpu/core/adapter.rb +123 -19
  30. data/lib/wgpu/core/async_waiter.rb +47 -4
  31. data/lib/wgpu/core/canvas_context.rb +32 -2
  32. data/lib/wgpu/core/device.rb +399 -53
  33. data/lib/wgpu/core/instance.rb +28 -4
  34. data/lib/wgpu/core/queue.rb +197 -51
  35. data/lib/wgpu/core/surface.rb +64 -17
  36. data/lib/wgpu/data_types.rb +83 -0
  37. data/lib/wgpu/descriptor_helpers.rb +104 -0
  38. data/lib/wgpu/error.rb +70 -0
  39. data/lib/wgpu/logging.rb +63 -0
  40. data/lib/wgpu/native/abi_verifier.rb +143 -0
  41. data/lib/wgpu/native/callbacks.rb +10 -1
  42. data/lib/wgpu/native/capabilities.rb +32 -2
  43. data/lib/wgpu/native/distribution.rb +176 -0
  44. data/lib/wgpu/native/enum_helper.rb +80 -0
  45. data/lib/wgpu/native/enums.rb +68 -8
  46. data/lib/wgpu/native/fixtures/webgpu-v27.0.4.0-enums.h +848 -0
  47. data/lib/wgpu/native/functions.rb +21 -10
  48. data/lib/wgpu/native/installer.rb +223 -0
  49. data/lib/wgpu/native/loader.rb +61 -21
  50. data/lib/wgpu/native/structs.rb +18 -1
  51. data/lib/wgpu/native_resource.rb +342 -0
  52. data/lib/wgpu/pipeline/bind_group.rb +21 -0
  53. data/lib/wgpu/pipeline/bind_group_layout.rb +108 -41
  54. data/lib/wgpu/pipeline/compute_pipeline.rb +40 -50
  55. data/lib/wgpu/pipeline/pipeline_layout.rb +8 -0
  56. data/lib/wgpu/pipeline/render_pipeline.rb +207 -110
  57. data/lib/wgpu/pipeline/shader_module.rb +95 -28
  58. data/lib/wgpu/resources/buffer.rb +387 -85
  59. data/lib/wgpu/resources/query_set.rb +26 -12
  60. data/lib/wgpu/resources/sampler.rb +43 -20
  61. data/lib/wgpu/resources/texture.rb +89 -48
  62. data/lib/wgpu/resources/texture_view.rb +35 -7
  63. data/lib/wgpu/texture_format.rb +96 -0
  64. data/lib/wgpu/version.rb +1 -1
  65. data/lib/wgpu/window.rb +34 -1
  66. data/lib/wgpu.rb +32 -0
  67. data/sig/wgpu.rbs +460 -0
  68. metadata +33 -16
@@ -4,33 +4,31 @@ module WGPU
4
4
  class Sampler
5
5
  attr_reader :handle
6
6
 
7
+ # Creates a texture sampler with address, filter, and comparison settings.
8
+ # @param device [Device] owning device
9
+ # @raise [ResourceError] if native validation or creation fails
7
10
  def initialize(device, label: nil, address_mode_u: :clamp_to_edge, address_mode_v: :clamp_to_edge, address_mode_w: :clamp_to_edge, mag_filter: :nearest, min_filter: :nearest, mipmap_filter: :nearest, lod_min_clamp: 0.0, lod_max_clamp: 32.0, compare: nil, max_anisotropy: 1)
8
11
  @device = device
9
12
 
10
- desc = Native::SamplerDescriptor.new
11
- desc[:next_in_chain] = nil
12
- if label
13
- @label_ptr = FFI::MemoryPointer.from_string(label)
14
- desc[:label][:data] = @label_ptr
15
- desc[:label][:length] = label.bytesize
16
- else
17
- desc[:label][:data] = nil
18
- desc[:label][:length] = 0
19
- end
20
- desc[:address_mode_u] = address_mode_u
21
- desc[:address_mode_v] = address_mode_v
22
- desc[:address_mode_w] = address_mode_w
23
- desc[:mag_filter] = mag_filter
24
- desc[:min_filter] = min_filter
25
- desc[:mipmap_filter] = mipmap_filter
26
- desc[:lod_min_clamp] = lod_min_clamp
27
- desc[:lod_max_clamp] = lod_max_clamp
28
- desc[:compare] = compare || :undefined
29
- desc[:max_anisotropy] = max_anisotropy
13
+ desc, keepalive = build_descriptor(
14
+ label:,
15
+ address_mode_u:,
16
+ address_mode_v:,
17
+ address_mode_w:,
18
+ mag_filter:,
19
+ min_filter:,
20
+ mipmap_filter:,
21
+ lod_min_clamp:,
22
+ lod_max_clamp:,
23
+ compare:,
24
+ max_anisotropy:
25
+ )
26
+ @descriptor_keepalive = keepalive
30
27
 
31
28
  device.push_error_scope(:validation)
32
29
  @handle = Native.wgpuDeviceCreateSampler(device.handle, desc)
33
30
  error = device.pop_error_scope
31
+ @descriptor_keepalive = nil
34
32
 
35
33
  if @handle.null? || (error[:type] && error[:type] != :no_error)
36
34
  msg = error[:message] || "Failed to create sampler"
@@ -38,10 +36,35 @@ module WGPU
38
36
  end
39
37
  end
40
38
 
39
+ # Releases the native sampler handle.
40
+ #
41
+ # Calling this method more than once has no effect.
42
+ # @return [void]
41
43
  def release
42
44
  return if @handle.null?
43
45
  Native.wgpuSamplerRelease(@handle)
44
46
  @handle = FFI::Pointer::NULL
45
47
  end
48
+
49
+ private
50
+
51
+ def build_descriptor(label:, address_mode_u:, address_mode_v:, address_mode_w:, mag_filter:, min_filter:,
52
+ mipmap_filter:, lod_min_clamp:, lod_max_clamp:, compare:, max_anisotropy:)
53
+ keepalive = []
54
+ desc = Native::SamplerDescriptor.new
55
+ desc[:next_in_chain] = nil
56
+ DescriptorHelpers.set_label(desc, label, keepalive:)
57
+ desc[:address_mode_u] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_u, name: "address mode")
58
+ desc[:address_mode_v] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_v, name: "address mode")
59
+ desc[:address_mode_w] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_w, name: "address mode")
60
+ desc[:mag_filter] = Native::EnumHelper.coerce(Native::FilterMode, mag_filter, name: "mag filter")
61
+ desc[:min_filter] = Native::EnumHelper.coerce(Native::FilterMode, min_filter, name: "min filter")
62
+ desc[:mipmap_filter] = Native::EnumHelper.coerce(Native::MipmapFilterMode, mipmap_filter, name: "mipmap filter")
63
+ desc[:lod_min_clamp] = lod_min_clamp
64
+ desc[:lod_max_clamp] = lod_max_clamp
65
+ desc[:compare] = Native::EnumHelper.coerce(Native::CompareFunction, compare || :undefined, name: "compare function")
66
+ desc[:max_anisotropy] = max_anisotropy
67
+ [desc, keepalive]
68
+ end
46
69
  end
47
70
  end
@@ -2,45 +2,33 @@
2
2
 
3
3
  module WGPU
4
4
  class Texture
5
- attr_reader :handle
6
-
5
+ attr_reader :handle, :surface_status
6
+
7
+ # Creates a GPU texture.
8
+ # @param device [Device] owning device
9
+ # @param size [Hash, Array] texture extent
10
+ # @param format [Symbol, Integer] texel format
11
+ # @param usage [Symbol, Array<Symbol>, Integer] usage flags
12
+ # @raise [ResourceError] if native validation or creation fails
7
13
  def initialize(device, label: nil, size:, format:, usage:, dimension: :d2, mip_level_count: 1, sample_count: 1, view_formats: [])
8
14
  @device = device
9
15
 
10
- desc = Native::TextureDescriptor.new
11
- desc[:next_in_chain] = nil
12
- if label
13
- @label_ptr = FFI::MemoryPointer.from_string(label)
14
- desc[:label][:data] = @label_ptr
15
- desc[:label][:length] = label.bytesize
16
- else
17
- desc[:label][:data] = nil
18
- desc[:label][:length] = 0
19
- end
20
- desc[:usage] = normalize_usage(usage)
21
- desc[:dimension] = dimension
22
- desc[:size][:width] = size[:width] || size[0]
23
- desc[:size][:height] = size[:height] || size[1] || 1
24
- desc[:size][:depth_or_array_layers] = size[:depth_or_array_layers] || size[2] || 1
25
- desc[:format] = format
26
- desc[:mip_level_count] = mip_level_count
27
- desc[:sample_count] = sample_count
28
- desc[:view_format_count] = view_formats.size
29
- if view_formats.empty?
30
- @view_formats_ptr = nil
31
- desc[:view_formats] = nil
32
- else
33
- format_values = view_formats.map do |vf|
34
- vf.is_a?(Integer) ? vf : Native::TextureFormat[vf]
35
- end
36
- @view_formats_ptr = FFI::MemoryPointer.new(:uint32, format_values.size)
37
- @view_formats_ptr.write_array_of_uint32(format_values)
38
- desc[:view_formats] = @view_formats_ptr
39
- end
16
+ desc, keepalive = build_descriptor(
17
+ label:,
18
+ size:,
19
+ format:,
20
+ usage:,
21
+ dimension:,
22
+ mip_level_count:,
23
+ sample_count:,
24
+ view_formats:
25
+ )
26
+ @descriptor_keepalive = keepalive
40
27
 
41
28
  device.push_error_scope(:validation)
42
29
  @handle = Native.wgpuDeviceCreateTexture(device.handle, desc)
43
30
  error = device.pop_error_scope
31
+ @descriptor_keepalive = nil
44
32
 
45
33
  if @handle.null? || (error[:type] && error[:type] != :no_error)
46
34
  msg = error[:message] || "Failed to create texture"
@@ -48,14 +36,23 @@ module WGPU
48
36
  end
49
37
  end
50
38
 
51
- def self.from_handle(handle)
52
- texture = allocate
53
- texture.instance_variable_set(:@handle, handle)
54
- texture.instance_variable_set(:@device, nil)
39
+ # Wraps a native texture handle, such as a surface-acquired texture.
40
+ #
41
+ # @param handle [FFI::Pointer] native texture handle
42
+ # @param surface_status [Symbol, nil] acquisition status associated with the texture
43
+ # @param device [Device, nil] device whose callbacks the texture may use
44
+ # @return [Texture] adopted wrapper
45
+ def self.from_handle(handle, surface_status: nil, device: nil)
46
+ texture = adopt_native_handle(handle)
47
+ texture.instance_variable_set(:@device, device)
48
+ texture.instance_variable_set(:@surface_status, surface_status)
49
+ texture.send(:attach_device_callback_lifetime, device)
55
50
  texture
56
51
  end
57
52
 
58
- def create_view(label: nil, format: nil, dimension: nil, base_mip_level: 0, mip_level_count: nil, base_array_layer: 0, array_layer_count: nil, aspect: :all)
53
+ # Creates a view into this texture.
54
+ # @return [TextureView]
55
+ def create_view(label: nil, format: nil, dimension: nil, base_mip_level: 0, mip_level_count: nil, base_array_layer: 0, array_layer_count: nil, aspect: :all, usage: nil)
59
56
  TextureView.new(self,
60
57
  label: label,
61
58
  format: format,
@@ -64,14 +61,19 @@ module WGPU
64
61
  mip_level_count: mip_level_count,
65
62
  base_array_layer: base_array_layer,
66
63
  array_layer_count: array_layer_count,
67
- aspect: aspect
64
+ aspect: aspect,
65
+ usage: usage
68
66
  )
69
67
  end
70
68
 
69
+ # Returns the texture width in texels.
70
+ # @return [Integer]
71
71
  def width
72
72
  Native.wgpuTextureGetWidth(@handle)
73
73
  end
74
74
 
75
+ # Returns the texture extent.
76
+ # @return [Hash{Symbol => Integer}]
75
77
  def size
76
78
  {
77
79
  width: width,
@@ -80,38 +82,58 @@ module WGPU
80
82
  }
81
83
  end
82
84
 
85
+ # Returns the texture height in texels.
86
+ # @return [Integer]
83
87
  def height
84
88
  Native.wgpuTextureGetHeight(@handle)
85
89
  end
86
90
 
91
+ # Returns the texture depth or array layer count.
92
+ # @return [Integer]
87
93
  def depth_or_array_layers
88
94
  Native.wgpuTextureGetDepthOrArrayLayers(@handle)
89
95
  end
90
96
 
97
+ # Returns the number of mip levels.
98
+ # @return [Integer]
91
99
  def mip_level_count
92
100
  Native.wgpuTextureGetMipLevelCount(@handle)
93
101
  end
94
102
 
103
+ # Returns the multisample count.
104
+ # @return [Integer]
95
105
  def sample_count
96
106
  Native.wgpuTextureGetSampleCount(@handle)
97
107
  end
98
108
 
109
+ # Returns the texture dimension.
110
+ # @return [Symbol, Integer]
99
111
  def dimension
100
112
  Native.wgpuTextureGetDimension(@handle)
101
113
  end
102
114
 
115
+ # Returns the texture format.
116
+ # @return [Symbol, Integer]
103
117
  def format
104
118
  Native.wgpuTextureGetFormat(@handle)
105
119
  end
106
120
 
121
+ # Returns the texture usage flags.
122
+ # @return [Integer]
107
123
  def usage
108
124
  Native.wgpuTextureGetUsage(@handle)
109
125
  end
110
126
 
127
+ # Destroys the texture's storage.
128
+ # @return [void]
111
129
  def destroy
112
130
  Native.wgpuTextureDestroy(@handle)
113
131
  end
114
132
 
133
+ # Releases the native texture handle.
134
+ #
135
+ # Calling this method more than once has no effect.
136
+ # @return [void]
115
137
  def release
116
138
  return if @handle.null?
117
139
  Native.wgpuTextureRelease(@handle)
@@ -120,17 +142,36 @@ module WGPU
120
142
 
121
143
  private
122
144
 
123
- def normalize_usage(usage)
124
- case usage
125
- when Integer
126
- usage
127
- when Symbol
128
- Native::TextureUsage[usage]
129
- when Array
130
- usage.reduce(0) { |acc, u| acc | Native::TextureUsage[u] }
131
- else
132
- raise ArgumentError, "Invalid usage: #{usage}"
145
+ def build_descriptor(label:, size:, format:, usage:, dimension:, mip_level_count:, sample_count:, view_formats:)
146
+ keepalive = []
147
+ DescriptorHelpers.validate_keys!(
148
+ size,
149
+ allowed: [:width, :height, :depth_or_array_layers],
150
+ required: [:width],
151
+ context: "texture size"
152
+ )
153
+
154
+ desc = Native::TextureDescriptor.new
155
+ desc[:next_in_chain] = nil
156
+ DescriptorHelpers.set_label(desc, label, keepalive:)
157
+ desc[:usage] = normalize_usage(usage)
158
+ desc[:dimension] = Native::EnumHelper.coerce(Native::TextureDimension, dimension, name: "texture dimension")
159
+ desc[:size][:width] = size[:width] || size[0]
160
+ desc[:size][:height] = size[:height] || size[1] || 1
161
+ desc[:size][:depth_or_array_layers] = size[:depth_or_array_layers] || size[2] || 1
162
+ desc[:format] = Native::EnumHelper.coerce(Native::TextureFormat, format, name: "texture format")
163
+ desc[:mip_level_count] = mip_level_count
164
+ desc[:sample_count] = sample_count
165
+ desc[:view_format_count] = view_formats.size
166
+ format_values = view_formats.map do |view_format|
167
+ Native::EnumHelper.coerce(Native::TextureFormat, view_format, name: "view format")
133
168
  end
169
+ desc[:view_formats] = DescriptorHelpers.uint32_array(format_values, keepalive:)
170
+ [desc, keepalive]
171
+ end
172
+
173
+ def normalize_usage(usage)
174
+ Native::EnumHelper.coerce_flags(Native::TextureUsage, usage, name: "texture usage")
134
175
  end
135
176
  end
136
177
  end
@@ -4,7 +4,10 @@ module WGPU
4
4
  class TextureView
5
5
  attr_reader :handle, :texture
6
6
 
7
- def initialize(texture, label: nil, format: nil, dimension: nil, base_mip_level: 0, mip_level_count: nil, base_array_layer: 0, array_layer_count: nil, aspect: :all)
7
+ # Creates a view selecting texture format, dimension, subresources, and aspect.
8
+ # @param texture [Texture] parent texture
9
+ # @raise [ResourceError] if native creation fails
10
+ def initialize(texture, label: nil, format: nil, dimension: nil, base_mip_level: 0, mip_level_count: nil, base_array_layer: 0, array_layer_count: nil, aspect: :all, usage: nil)
8
11
  @texture = texture
9
12
 
10
13
  desc = Native::TextureViewDescriptor.new
@@ -17,29 +20,54 @@ module WGPU
17
20
  desc[:label][:data] = nil
18
21
  desc[:label][:length] = 0
19
22
  end
20
- desc[:format] = format || :undefined
21
- desc[:dimension] = dimension || :undefined
23
+ desc[:format] = Native::EnumHelper.coerce(
24
+ Native::TextureFormat,
25
+ format || :undefined,
26
+ name: "texture view format"
27
+ )
28
+ desc[:dimension] = Native::EnumHelper.coerce(
29
+ Native::TextureViewDimension,
30
+ dimension || :undefined,
31
+ name: "texture view dimension"
32
+ )
22
33
  desc[:base_mip_level] = base_mip_level
23
34
  desc[:mip_level_count] = mip_level_count || 0xFFFFFFFF
24
35
  desc[:base_array_layer] = base_array_layer
25
36
  desc[:array_layer_count] = array_layer_count || 0xFFFFFFFF
26
- desc[:aspect] = aspect
37
+ desc[:aspect] = Native::EnumHelper.coerce(Native::TextureAspect, aspect, name: "texture aspect")
38
+ desc[:usage] = Native::EnumHelper.coerce_flags(
39
+ Native::TextureUsage,
40
+ usage || :none,
41
+ name: "texture view usage"
42
+ )
27
43
 
28
44
  @handle = Native.wgpuTextureCreateView(texture.handle, desc)
29
45
  raise ResourceError, "Failed to create texture view" if @handle.null?
30
46
  end
31
47
 
32
- def self.from_handle(handle)
33
- view = allocate
34
- view.instance_variable_set(:@handle, handle)
48
+ # Wraps a native texture view handle owned by the caller.
49
+ #
50
+ # @param handle [FFI::Pointer] native texture view handle
51
+ # @param device [Device, nil] device whose callbacks the view may use
52
+ # @return [TextureView] adopted wrapper without a parent texture
53
+ def self.from_handle(handle, device: nil)
54
+ view = adopt_native_handle(handle)
35
55
  view.instance_variable_set(:@texture, nil)
56
+ view.send(:attach_device_callback_lifetime, device)
36
57
  view
37
58
  end
38
59
 
60
+ # Returns the parent texture dimensions when known.
61
+ #
62
+ # @return [Hash, nil] texture extent, or +nil+ for an adopted standalone view
39
63
  def size
40
64
  @texture&.size
41
65
  end
42
66
 
67
+ # Releases the native texture view handle.
68
+ #
69
+ # Calling this method more than once has no effect.
70
+ # @return [void]
43
71
  def release
44
72
  return if @handle.null?
45
73
  Native.wgpuTextureViewRelease(@handle)
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WGPU
4
+ module TextureFormat
5
+ COPY_ALIGNMENT = 256
6
+
7
+ module_function
8
+
9
+ # Returns the byte size of one texel block.
10
+ # @return [Integer]
11
+ def block_size(format, aspect: :all)
12
+ block_info(format, aspect:).last
13
+ end
14
+
15
+ # Returns a format's texel block width and height.
16
+ # @return [Array(Integer, Integer)]
17
+ def block_dimensions(format)
18
+ block_info(format).first(2)
19
+ end
20
+
21
+ # Returns the tightly packed bytes required for one texel row.
22
+ # @return [Integer]
23
+ def bytes_per_row(width, format, aspect: :all)
24
+ block_width, _, bytes = block_info(format, aspect:)
25
+ ((Integer(width) + block_width - 1) / block_width) * bytes
26
+ end
27
+
28
+ # Returns row bytes rounded up to the requested copy alignment.
29
+ # @return [Integer]
30
+ def aligned_bytes_per_row(width, format, aspect: :all, alignment: COPY_ALIGNMENT)
31
+ row_bytes = bytes_per_row(width, format, aspect:)
32
+ ((row_bytes + alignment - 1) / alignment) * alignment
33
+ end
34
+
35
+ # Returns the copy footprint of one texel block.
36
+ #
37
+ # @param format [Symbol, Integer] texture format
38
+ # @param aspect [Symbol] texture aspect for depth/stencil formats
39
+ # @return [Array(Integer, Integer, Integer)] block width, height, and byte size
40
+ # @raise [ArgumentError] if the format has no portable copy footprint
41
+ def block_info(format, aspect: :all)
42
+ name = normalize_format(format)
43
+ special = depth_stencil_block_info(name, aspect)
44
+ return special if special
45
+
46
+ case name.to_s
47
+ when /\Ar8_/
48
+ [1, 1, 1]
49
+ when /\A(?:r16_|rg8_)/
50
+ [1, 1, 2]
51
+ when /\A(?:r32_|rg16_|rgba8_|bgra8_|rgb10a2_|rg11b10_|rgb9e5_)/
52
+ [1, 1, 4]
53
+ when /\A(?:rg32_|rgba16_)/
54
+ [1, 1, 8]
55
+ when /\Argba32_/
56
+ [1, 1, 16]
57
+ when /\A(?:bc(?:1|4)|etc2_rgb8|etc2_rgb8a1|eac_r11)_/
58
+ [4, 4, 8]
59
+ when /\A(?:bc(?:2|3|5|6h|7)|etc2_rgba8|eac_rg11)_/
60
+ [4, 4, 16]
61
+ when /\Aastc_(\d+)x(\d+)_/
62
+ [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, 16]
63
+ else
64
+ raise ArgumentError, "No texel block copy footprint for #{name.inspect}"
65
+ end
66
+ end
67
+
68
+ def normalize_format(format)
69
+ return format if format.is_a?(Symbol) &&
70
+ Native::TextureFormat.to_h.key?(format)
71
+
72
+ value = Native::EnumHelper.coerce(Native::TextureFormat, format, name: "texture format")
73
+ Native::TextureFormat[value] || format
74
+ end
75
+ private_class_method :normalize_format
76
+
77
+ def depth_stencil_block_info(format, aspect)
78
+ case format
79
+ when :stencil8
80
+ [1, 1, 1]
81
+ when :depth16_unorm
82
+ [1, 1, 2]
83
+ when :depth32_float
84
+ [1, 1, 4]
85
+ when :depth32_float_stencil8
86
+ return [1, 1, 4] if aspect == :depth_only
87
+ return [1, 1, 1] if aspect == :stencil_only
88
+
89
+ raise ArgumentError, "depth32_float_stencil8 copies require :depth_only or :stencil_only aspect"
90
+ when :depth24_plus, :depth24_plus_stencil8
91
+ raise ArgumentError, "#{format} has no portable texel block copy footprint"
92
+ end
93
+ end
94
+ private_class_method :depth_stencil_block_info
95
+ end
96
+ end
data/lib/wgpu/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WGPU
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.1"
5
5
  end
data/lib/wgpu/window.rb CHANGED
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "sdl3"
3
+ begin
4
+ require "sdl3"
5
+ rescue LoadError => e
6
+ raise LoadError,
7
+ "wgpu/window requires the optional `sdl3` gem. " \
8
+ "Add `gem \"sdl3\", \"~> 1.0\"` and install the SDL3 system library. " \
9
+ "(original error: #{e.message})"
10
+ end
4
11
 
5
12
  module WGPU
6
13
  module Window
@@ -15,6 +22,11 @@ module WGPU
15
22
  SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER = "SDL.window.wayland.display"
16
23
  SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER = "SDL.window.wayland.surface"
17
24
 
25
+ # Creates an SDL window suitable for WebGPU presentation.
26
+ # @param title [String] window title
27
+ # @param width [Integer] initial logical width
28
+ # @param height [Integer] initial logical height
29
+ # @param resizable [Boolean] whether the user may resize the window
18
30
  def initialize(title:, width: 800, height: 600, resizable: true)
19
31
  @width = width
20
32
  @height = height
@@ -28,6 +40,11 @@ module WGPU
28
40
  @window = SDL3::Window.new(title, width, height, flags)
29
41
  end
30
42
 
43
+ # Creates a presentation surface for this native window.
44
+ #
45
+ # @param instance [Instance] WebGPU instance that owns the surface
46
+ # @return [Surface] platform-specific presentation surface
47
+ # @raise [WindowError] if the platform or native window handle is unsupported
31
48
  def create_surface(instance)
32
49
  case platform
33
50
  when :macos
@@ -43,6 +60,9 @@ module WGPU
43
60
  end
44
61
  end
45
62
 
63
+ # Collects currently pending SDL events.
64
+ #
65
+ # @return [Array] events in delivery order
46
66
  def poll_events
47
67
  events = []
48
68
  SDL3::Event.each do |event|
@@ -51,10 +71,17 @@ module WGPU
51
71
  events
52
72
  end
53
73
 
74
+ # Reports whether a quit event is present.
75
+ # @param events [Array] SDL events to inspect
76
+ # @return [Boolean]
54
77
  def should_close?(events)
55
78
  events.any?(&:quit?)
56
79
  end
57
80
 
81
+ # Reports whether a key-down event matches the requested key.
82
+ # @param events [Array] SDL events to inspect
83
+ # @param key [Symbol, Integer] common key name or SDL scancode
84
+ # @return [Boolean]
58
85
  def key_pressed?(events, key)
59
86
  scancode = case key
60
87
  when :escape then SDL3::Raw::SDL_SCANCODE_ESCAPE
@@ -71,12 +98,18 @@ module WGPU
71
98
  end
72
99
  end
73
100
 
101
+ # Returns the drawable size in physical pixels.
102
+ #
103
+ # @return [Array(Integer, Integer)] width and height
74
104
  def drawable_size
75
105
  @window.size_in_pixels
76
106
  rescue
77
107
  [@width, @height]
78
108
  end
79
109
 
110
+ # Destroys platform-specific resources and the SDL window.
111
+ #
112
+ # @return [void]
80
113
  def close
81
114
  if @metal_view && !@metal_view.null?
82
115
  SDL3::Raw.SDL_Metal_DestroyView(@metal_view)
data/lib/wgpu.rb CHANGED
@@ -3,6 +3,13 @@
3
3
  require_relative "wgpu/version"
4
4
  require_relative "wgpu/error"
5
5
  require_relative "wgpu/native/loader"
6
+ require_relative "wgpu/native/enum_helper"
7
+ require_relative "wgpu/native/abi_verifier"
8
+ require_relative "wgpu/logging"
9
+ require_relative "wgpu/descriptor_helpers"
10
+ require_relative "wgpu/data_types"
11
+ require_relative "wgpu/texture_format"
12
+ require_relative "wgpu/native_resource"
6
13
  require_relative "wgpu/async_task"
7
14
  require_relative "wgpu/core/async_waiter"
8
15
 
@@ -34,4 +41,29 @@ require_relative "wgpu/core/surface"
34
41
  require_relative "wgpu/core/canvas_context"
35
42
 
36
43
  module WGPU
44
+ [
45
+ Instance,
46
+ Adapter,
47
+ Device,
48
+ Queue,
49
+ Surface,
50
+ CanvasContext,
51
+ Buffer,
52
+ Texture,
53
+ TextureView,
54
+ Sampler,
55
+ QuerySet,
56
+ ShaderModule,
57
+ BindGroupLayout,
58
+ BindGroup,
59
+ PipelineLayout,
60
+ ComputePipeline,
61
+ RenderPipeline,
62
+ CommandEncoder,
63
+ CommandBuffer,
64
+ ComputePass,
65
+ RenderPass,
66
+ RenderBundleEncoder,
67
+ RenderBundle
68
+ ].each { |resource_class| resource_class.include(NativeResource) }
37
69
  end