wgpu 1.2.1 → 1.2.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/docs/async.md +3 -1
- data/docs/errors.md +13 -0
- data/docs/getting_started_rendering.md +5 -0
- data/docs/resource_lifetime.md +9 -0
- data/lib/wgpu/commands/command_encoder.rb +24 -26
- data/lib/wgpu/commands/compute_pass.rb +6 -6
- data/lib/wgpu/commands/render_bundle_encoder.rb +8 -8
- data/lib/wgpu/commands/render_pass.rb +14 -14
- data/lib/wgpu/core/adapter.rb +6 -3
- data/lib/wgpu/core/async_waiter.rb +15 -4
- data/lib/wgpu/core/canvas_context.rb +5 -2
- data/lib/wgpu/core/device.rb +112 -55
- data/lib/wgpu/core/queue.rb +7 -22
- data/lib/wgpu/core/surface.rb +6 -6
- data/lib/wgpu/descriptor_helpers.rb +22 -0
- data/lib/wgpu/native/functions.rb +3 -0
- data/lib/wgpu/native/installer.rb +7 -1
- data/lib/wgpu/native_resource.rb +16 -0
- data/lib/wgpu/pipeline/bind_group.rb +7 -7
- data/lib/wgpu/pipeline/bind_group_layout.rb +3 -3
- data/lib/wgpu/pipeline/compute_pipeline.rb +5 -5
- data/lib/wgpu/pipeline/pipeline_layout.rb +4 -4
- data/lib/wgpu/pipeline/render_pipeline.rb +6 -6
- data/lib/wgpu/pipeline/shader_module.rb +3 -3
- data/lib/wgpu/resources/buffer.rb +83 -39
- data/lib/wgpu/resources/query_set.rb +1 -1
- data/lib/wgpu/resources/sampler.rb +3 -3
- data/lib/wgpu/resources/texture.rb +4 -6
- data/lib/wgpu/resources/texture_view.rb +1 -1
- data/lib/wgpu/version.rb +1 -1
- data/sig/wgpu.rbs +1 -0
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1ef9011b4b35861933cac88658c5f5a8c3dba6c3570a22f54109b0bb2b5f0c8
|
|
4
|
+
data.tar.gz: 33fef1fd859ad532a49e27d154f6e5fc303bf9ac29bd698a955e14a5b18c58ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f11393f0e2cb7cfa5b6f4e1eb2062f221654e45622c2e5f6ac45d9ffcd92e2e08dc101f81f73eb576cc21d7c42b832f0d418f3f5ccd3a502a8f66f24271d6117
|
|
7
|
+
data.tar.gz: a122894e157f437c429675f80819cc69f1198e54f6ba8188e7eaedb5ba32c431738893fd28ff911bd8f6917ef4fdb73eff4bc8541346150431c095494b4a11a2
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 1.2.2 - 2026-09-09
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Invalidate mapped buffer views after unmap, destroy, or release, and check
|
|
15
|
+
accesses against the actual mapped interval, including after remapping.
|
|
16
|
+
- Reject copies of native resource wrappers and validate resource argument
|
|
17
|
+
types and release state before passing handles to native code.
|
|
18
|
+
- Validate buffer clear ranges and skip zero-length clears that would panic
|
|
19
|
+
in the pinned native library.
|
|
20
|
+
- Reject empty error-scope pops, serialize nested scopes per device, start
|
|
21
|
+
asynchronous pops on the calling thread, and clean up scopes when blocks
|
|
22
|
+
raise without replacing the original exception.
|
|
23
|
+
- Release native handles after failed resource initialization and free native
|
|
24
|
+
feature arrays after converting them to Ruby values.
|
|
25
|
+
- Validate timeouts before starting native operations and reclaim adapter
|
|
26
|
+
and device handles when waiting fails.
|
|
27
|
+
- Accept Array texture extents consistently across creation, copies, writes,
|
|
28
|
+
and readback through shared descriptor normalization.
|
|
29
|
+
- Distinguish raw Wayland surface pointers from existing WGPU surfaces in
|
|
30
|
+
`CanvasContext`, with explicit `wl_surface:` and `wgpu_surface:` keys.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Allow rubyzip 3.x alongside 2.x.
|
|
35
|
+
|
|
10
36
|
## 1.2.1 - 2026-07-24
|
|
11
37
|
|
|
12
38
|
### Added
|
data/docs/async.md
CHANGED
|
@@ -27,7 +27,9 @@ Thread implementation until those native entry points are implemented.
|
|
|
27
27
|
Synchronous adapter/device requests, `Buffer#map_sync`,
|
|
28
28
|
`Device#pop_error_scope`, and `Queue#on_submitted_work_done` accept
|
|
29
29
|
`timeout:` in seconds. The default `nil` preserves the prior unbounded wait.
|
|
30
|
-
Expiry raises `WGPU::TimeoutError`.
|
|
30
|
+
Expiry raises `WGPU::TimeoutError`. Timeouts must be finite and non-negative;
|
|
31
|
+
invalid values are rejected before starting the native operation. Adapter and
|
|
32
|
+
device requests also reclaim their handles if waiting fails for another reason.
|
|
31
33
|
|
|
32
34
|
`Device.request` can time out only when its adapter is associated with an
|
|
33
35
|
`Instance`. When adopting a native adapter, pass `instance:` to
|
data/docs/errors.md
CHANGED
|
@@ -8,6 +8,19 @@
|
|
|
8
8
|
maps WebGPU error types to `ValidationError`, `OutOfMemoryError`,
|
|
9
9
|
`InternalError`, or `DeviceLostError`.
|
|
10
10
|
|
|
11
|
+
`with_error_scope { ... }` always pops its scope, including when the block
|
|
12
|
+
raises; the original block exception is preserved. Popping without a scope
|
|
13
|
+
owned by the current thread raises `WGPU::DeviceError` before calling native
|
|
14
|
+
code.
|
|
15
|
+
|
|
16
|
+
Scopes are nested and serialized per device from push through native pop.
|
|
17
|
+
Push and pop on the same thread, and finish the scope before waiting for
|
|
18
|
+
another thread's scoped work on that device. In particular, do not wait for
|
|
19
|
+
an asynchronous pipeline creation inside `with_error_scope`.
|
|
20
|
+
`pop_error_scope_async` starts the pop immediately on the calling thread and
|
|
21
|
+
waits for its result in the background, so a later push cannot change which
|
|
22
|
+
scope it pops.
|
|
23
|
+
|
|
11
24
|
Device-level callbacks can be installed after device creation:
|
|
12
25
|
|
|
13
26
|
```ruby
|
|
@@ -57,6 +57,11 @@ their window toolkit to `Surface.from_metal_layer`,
|
|
|
57
57
|
native window/display/layer alive longer than the surface; wgpu-ruby does not
|
|
58
58
|
take ownership of those platform objects.
|
|
59
59
|
|
|
60
|
+
For `CanvasContext`, pass an existing surface as `wgpu_surface: surface`.
|
|
61
|
+
Wayland platform descriptors use `platform: :wayland`, `display: wl_display`,
|
|
62
|
+
and `wl_surface: wl_surface_pointer`. The older `surface:` key remains
|
|
63
|
+
supported for existing WGPU surfaces and raw Wayland pointers.
|
|
64
|
+
|
|
60
65
|
[`examples/09_clear_color.rb`](../examples/09_clear_color.rb) is the smallest
|
|
61
66
|
resizable render loop. The other rendering examples cover vertex/index
|
|
62
67
|
buffers, textures, bind groups, depth testing, and uniform updates.
|
data/docs/resource_lifetime.md
CHANGED
|
@@ -33,6 +33,15 @@ bytes = device.create_buffer(
|
|
|
33
33
|
end
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
Native wrappers reject `dup` and `clone` with `TypeError`. Resource arguments
|
|
37
|
+
are checked for the expected wrapper type and release state before their
|
|
38
|
+
handles enter FFI. Failed constructors release any native handle they acquired.
|
|
39
|
+
|
|
40
|
+
Mapped views retain their buffer, but become invalid after `unmap`, `destroy`,
|
|
41
|
+
or `release`. Reading or writing an invalid view raises `WGPU::BufferError`,
|
|
42
|
+
even after the buffer is mapped again. Accesses must fit within the current
|
|
43
|
+
mapped interval, not just within the buffer's total size.
|
|
44
|
+
|
|
36
45
|
## Wrapper matrix
|
|
37
46
|
|
|
38
47
|
The table covers all 23 native wrapper classes. `BufferMappedRange`,
|
|
@@ -24,7 +24,7 @@ module WGPU
|
|
|
24
24
|
desc[:label][:length] = 0
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
@handle = Native.wgpuDeviceCreateCommandEncoder(device
|
|
27
|
+
@handle = Native.wgpuDeviceCreateCommandEncoder(NativeResource.checked_handle(device, expected_class: Device), desc)
|
|
28
28
|
raise CommandError, "Failed to create command encoder" if @handle.null?
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -86,8 +86,8 @@ module WGPU
|
|
|
86
86
|
raise CommandError, "Encoder already finished" if @finished
|
|
87
87
|
Native.wgpuCommandEncoderCopyBufferToBuffer(
|
|
88
88
|
@handle,
|
|
89
|
-
source
|
|
90
|
-
destination
|
|
89
|
+
NativeResource.checked_handle(source, expected_class: Buffer), source_offset,
|
|
90
|
+
NativeResource.checked_handle(destination, expected_class: Buffer), destination_offset,
|
|
91
91
|
size
|
|
92
92
|
)
|
|
93
93
|
end
|
|
@@ -100,19 +100,16 @@ module WGPU
|
|
|
100
100
|
def copy_buffer_to_texture(source:, destination:, copy_size:)
|
|
101
101
|
raise CommandError, "Encoder already finished" if @finished
|
|
102
102
|
|
|
103
|
-
size =
|
|
104
|
-
size[:width] = copy_size[:width] || copy_size[0]
|
|
105
|
-
size[:height] = copy_size[:height] || copy_size[1] || 1
|
|
106
|
-
size[:depth_or_array_layers] = copy_size[:depth_or_array_layers] || copy_size[2] || 1
|
|
103
|
+
size = DescriptorHelpers.extent_3d(copy_size)
|
|
107
104
|
|
|
108
105
|
src = Native::ImageCopyBuffer.new
|
|
109
106
|
src[:layout][:offset] = source[:offset] || 0
|
|
110
107
|
src[:layout][:bytes_per_row] = source[:bytes_per_row]
|
|
111
108
|
src[:layout][:rows_per_image] = source[:rows_per_image] || size[:height]
|
|
112
|
-
src[:buffer] = source[:buffer]
|
|
109
|
+
src[:buffer] = NativeResource.checked_handle(source[:buffer], expected_class: Buffer)
|
|
113
110
|
|
|
114
111
|
dst = Native::ImageCopyTexture.new
|
|
115
|
-
dst[:texture] = destination[:texture]
|
|
112
|
+
dst[:texture] = NativeResource.checked_handle(destination[:texture], expected_class: Texture)
|
|
116
113
|
dst[:mip_level] = destination[:mip_level] || 0
|
|
117
114
|
dst[:origin][:x] = destination.dig(:origin, :x) || 0
|
|
118
115
|
dst[:origin][:y] = destination.dig(:origin, :y) || 0
|
|
@@ -134,13 +131,10 @@ module WGPU
|
|
|
134
131
|
def copy_texture_to_buffer(source:, destination:, copy_size:)
|
|
135
132
|
raise CommandError, "Encoder already finished" if @finished
|
|
136
133
|
|
|
137
|
-
size =
|
|
138
|
-
size[:width] = copy_size[:width] || copy_size[0]
|
|
139
|
-
size[:height] = copy_size[:height] || copy_size[1] || 1
|
|
140
|
-
size[:depth_or_array_layers] = copy_size[:depth_or_array_layers] || copy_size[2] || 1
|
|
134
|
+
size = DescriptorHelpers.extent_3d(copy_size)
|
|
141
135
|
|
|
142
136
|
src = Native::ImageCopyTexture.new
|
|
143
|
-
src[:texture] = source[:texture]
|
|
137
|
+
src[:texture] = NativeResource.checked_handle(source[:texture], expected_class: Texture)
|
|
144
138
|
src[:mip_level] = source[:mip_level] || 0
|
|
145
139
|
src[:origin][:x] = source.dig(:origin, :x) || 0
|
|
146
140
|
src[:origin][:y] = source.dig(:origin, :y) || 0
|
|
@@ -155,7 +149,7 @@ module WGPU
|
|
|
155
149
|
dst[:layout][:offset] = destination[:offset] || 0
|
|
156
150
|
dst[:layout][:bytes_per_row] = destination[:bytes_per_row]
|
|
157
151
|
dst[:layout][:rows_per_image] = destination[:rows_per_image] || size[:height]
|
|
158
|
-
dst[:buffer] = destination[:buffer]
|
|
152
|
+
dst[:buffer] = NativeResource.checked_handle(destination[:buffer], expected_class: Buffer)
|
|
159
153
|
|
|
160
154
|
Native.wgpuCommandEncoderCopyTextureToBuffer(@handle, src, dst, size)
|
|
161
155
|
end
|
|
@@ -169,7 +163,7 @@ module WGPU
|
|
|
169
163
|
raise CommandError, "Encoder already finished" if @finished
|
|
170
164
|
|
|
171
165
|
src = Native::ImageCopyTexture.new
|
|
172
|
-
src[:texture] = source[:texture]
|
|
166
|
+
src[:texture] = NativeResource.checked_handle(source[:texture], expected_class: Texture)
|
|
173
167
|
src[:mip_level] = source[:mip_level] || 0
|
|
174
168
|
src[:origin][:x] = source.dig(:origin, :x) || 0
|
|
175
169
|
src[:origin][:y] = source.dig(:origin, :y) || 0
|
|
@@ -181,7 +175,7 @@ module WGPU
|
|
|
181
175
|
)
|
|
182
176
|
|
|
183
177
|
dst = Native::ImageCopyTexture.new
|
|
184
|
-
dst[:texture] = destination[:texture]
|
|
178
|
+
dst[:texture] = NativeResource.checked_handle(destination[:texture], expected_class: Texture)
|
|
185
179
|
dst[:mip_level] = destination[:mip_level] || 0
|
|
186
180
|
dst[:origin][:x] = destination.dig(:origin, :x) || 0
|
|
187
181
|
dst[:origin][:y] = destination.dig(:origin, :y) || 0
|
|
@@ -192,10 +186,7 @@ module WGPU
|
|
|
192
186
|
name: "destination texture aspect"
|
|
193
187
|
)
|
|
194
188
|
|
|
195
|
-
size =
|
|
196
|
-
size[:width] = copy_size[:width] || copy_size[0]
|
|
197
|
-
size[:height] = copy_size[:height] || copy_size[1] || 1
|
|
198
|
-
size[:depth_or_array_layers] = copy_size[:depth_or_array_layers] || copy_size[2] || 1
|
|
189
|
+
size = DescriptorHelpers.extent_3d(copy_size)
|
|
199
190
|
|
|
200
191
|
Native.wgpuCommandEncoderCopyTextureToTexture(@handle, src, dst, size)
|
|
201
192
|
end
|
|
@@ -206,10 +197,10 @@ module WGPU
|
|
|
206
197
|
raise CommandError, "Encoder already finished" if @finished
|
|
207
198
|
Native.wgpuCommandEncoderResolveQuerySet(
|
|
208
199
|
@handle,
|
|
209
|
-
query_set
|
|
200
|
+
NativeResource.checked_handle(query_set, expected_class: QuerySet),
|
|
210
201
|
first_query,
|
|
211
202
|
query_count,
|
|
212
|
-
destination
|
|
203
|
+
NativeResource.checked_handle(destination, expected_class: Buffer),
|
|
213
204
|
destination_offset
|
|
214
205
|
)
|
|
215
206
|
end
|
|
@@ -221,8 +212,15 @@ module WGPU
|
|
|
221
212
|
# @return [void]
|
|
222
213
|
def clear_buffer(buffer, offset: 0, size: nil)
|
|
223
214
|
raise CommandError, "Encoder already finished" if @finished
|
|
224
|
-
|
|
225
|
-
|
|
215
|
+
buffer_handle = NativeResource.checked_handle(buffer, expected_class: Buffer)
|
|
216
|
+
offset = DataTypes.validate_alignment!(offset, 4, name: "clear offset")
|
|
217
|
+
size = DataTypes.validate_alignment!(size || (buffer.size - offset), 4, name: "clear size")
|
|
218
|
+
if offset > buffer.size || size > buffer.size - offset
|
|
219
|
+
raise ArgumentError, "clear range exceeds buffer size"
|
|
220
|
+
end
|
|
221
|
+
return if size.zero?
|
|
222
|
+
|
|
223
|
+
Native.wgpuCommandEncoderClearBuffer(@handle, buffer_handle, offset, size)
|
|
226
224
|
end
|
|
227
225
|
|
|
228
226
|
# Writes a GPU timestamp to a query set.
|
|
@@ -231,7 +229,7 @@ module WGPU
|
|
|
231
229
|
# @return [void]
|
|
232
230
|
def write_timestamp(query_set, query_index)
|
|
233
231
|
raise CommandError, "Encoder already finished" if @finished
|
|
234
|
-
Native.wgpuCommandEncoderWriteTimestamp(@handle, query_set
|
|
232
|
+
Native.wgpuCommandEncoderWriteTimestamp(@handle, NativeResource.checked_handle(query_set, expected_class: QuerySet), query_index)
|
|
235
233
|
end
|
|
236
234
|
|
|
237
235
|
# Starts a labeled group in GPU debugging tools.
|
|
@@ -25,7 +25,7 @@ module WGPU
|
|
|
25
25
|
@timestamp_writes = nil
|
|
26
26
|
if timestamp_writes
|
|
27
27
|
@timestamp_writes = Native::ComputePassTimestampWrites.new
|
|
28
|
-
@timestamp_writes[:query_set] = timestamp_writes.fetch(:query_set)
|
|
28
|
+
@timestamp_writes[:query_set] = NativeResource.checked_handle(timestamp_writes.fetch(:query_set), expected_class: QuerySet)
|
|
29
29
|
@timestamp_writes[:beginning_of_pass_write_index] = timestamp_writes[:beginning_of_pass_write_index] || 0xFFFFFFFF
|
|
30
30
|
@timestamp_writes[:end_of_pass_write_index] = timestamp_writes[:end_of_pass_write_index] || 0xFFFFFFFF
|
|
31
31
|
desc[:timestamp_writes] = @timestamp_writes.to_ptr
|
|
@@ -33,7 +33,7 @@ module WGPU
|
|
|
33
33
|
desc[:timestamp_writes] = nil
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
@handle = Native.wgpuCommandEncoderBeginComputePass(encoder
|
|
36
|
+
@handle = Native.wgpuCommandEncoderBeginComputePass(NativeResource.checked_handle(encoder, expected_class: CommandEncoder), desc)
|
|
37
37
|
raise CommandError, "Failed to begin compute pass" if @handle.null?
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -41,7 +41,7 @@ module WGPU
|
|
|
41
41
|
# @param pipeline [ComputePipeline] pipeline to bind
|
|
42
42
|
# @return [void]
|
|
43
43
|
def set_pipeline(pipeline)
|
|
44
|
-
Native.wgpuComputePassEncoderSetPipeline(@handle, pipeline
|
|
44
|
+
Native.wgpuComputePassEncoderSetPipeline(@handle, NativeResource.checked_handle(pipeline, expected_class: ComputePipeline))
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# Binds a resource group for subsequent dispatches.
|
|
@@ -51,11 +51,11 @@ module WGPU
|
|
|
51
51
|
# @return [void]
|
|
52
52
|
def set_bind_group(index, bind_group, dynamic_offsets: [])
|
|
53
53
|
if dynamic_offsets.empty?
|
|
54
|
-
Native.wgpuComputePassEncoderSetBindGroup(@handle, index, bind_group
|
|
54
|
+
Native.wgpuComputePassEncoderSetBindGroup(@handle, index, NativeResource.checked_handle(bind_group, expected_class: BindGroup), 0, nil)
|
|
55
55
|
else
|
|
56
56
|
offsets_ptr = FFI::MemoryPointer.new(:uint32, dynamic_offsets.size)
|
|
57
57
|
offsets_ptr.write_array_of_uint32(dynamic_offsets)
|
|
58
|
-
Native.wgpuComputePassEncoderSetBindGroup(@handle, index, bind_group
|
|
58
|
+
Native.wgpuComputePassEncoderSetBindGroup(@handle, index, NativeResource.checked_handle(bind_group, expected_class: BindGroup), dynamic_offsets.size, offsets_ptr)
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -73,7 +73,7 @@ module WGPU
|
|
|
73
73
|
# @param offset [Integer] byte offset of the arguments
|
|
74
74
|
# @return [void]
|
|
75
75
|
def dispatch_workgroups_indirect(buffer, offset: 0)
|
|
76
|
-
Native.wgpuComputePassEncoderDispatchWorkgroupsIndirect(@handle, buffer
|
|
76
|
+
Native.wgpuComputePassEncoderDispatchWorkgroupsIndirect(@handle, NativeResource.checked_handle(buffer, expected_class: Buffer), offset)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
# Starts a labeled group in GPU debugging tools.
|
|
@@ -47,7 +47,7 @@ module WGPU
|
|
|
47
47
|
desc[:depth_read_only] = depth_read_only ? 1 : 0
|
|
48
48
|
desc[:stencil_read_only] = stencil_read_only ? 1 : 0
|
|
49
49
|
|
|
50
|
-
@handle = Native.wgpuDeviceCreateRenderBundleEncoder(device
|
|
50
|
+
@handle = Native.wgpuDeviceCreateRenderBundleEncoder(NativeResource.checked_handle(device, expected_class: Device), desc)
|
|
51
51
|
raise RenderBundleError, "Failed to create render bundle encoder" if @handle.null?
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -58,7 +58,7 @@ module WGPU
|
|
|
58
58
|
def set_pipeline(pipeline)
|
|
59
59
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
60
60
|
|
|
61
|
-
Native.wgpuRenderBundleEncoderSetPipeline(@handle, pipeline
|
|
61
|
+
Native.wgpuRenderBundleEncoderSetPipeline(@handle, NativeResource.checked_handle(pipeline, expected_class: RenderPipeline))
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# Binds a resource group for subsequent bundle draws.
|
|
@@ -72,9 +72,9 @@ module WGPU
|
|
|
72
72
|
if dynamic_offsets && !dynamic_offsets.empty?
|
|
73
73
|
offsets_ptr = FFI::MemoryPointer.new(:uint32, dynamic_offsets.size)
|
|
74
74
|
offsets_ptr.write_array_of_uint32(dynamic_offsets)
|
|
75
|
-
Native.wgpuRenderBundleEncoderSetBindGroup(@handle, index, bind_group
|
|
75
|
+
Native.wgpuRenderBundleEncoderSetBindGroup(@handle, index, NativeResource.checked_handle(bind_group, expected_class: BindGroup), dynamic_offsets.size, offsets_ptr)
|
|
76
76
|
else
|
|
77
|
-
Native.wgpuRenderBundleEncoderSetBindGroup(@handle, index, bind_group
|
|
77
|
+
Native.wgpuRenderBundleEncoderSetBindGroup(@handle, index, NativeResource.checked_handle(bind_group, expected_class: BindGroup), 0, nil)
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
|
|
@@ -86,7 +86,7 @@ module WGPU
|
|
|
86
86
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
87
87
|
|
|
88
88
|
size ||= buffer.size - offset
|
|
89
|
-
Native.wgpuRenderBundleEncoderSetVertexBuffer(@handle, slot, buffer
|
|
89
|
+
Native.wgpuRenderBundleEncoderSetVertexBuffer(@handle, slot, NativeResource.checked_handle(buffer, expected_class: Buffer), offset, size)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
# Binds an index buffer for indexed bundle draws.
|
|
@@ -98,7 +98,7 @@ module WGPU
|
|
|
98
98
|
|
|
99
99
|
size ||= buffer.size - offset
|
|
100
100
|
format_value = Native::EnumHelper.coerce(Native::IndexFormat, format, name: "index format")
|
|
101
|
-
Native.wgpuRenderBundleEncoderSetIndexBuffer(@handle, buffer
|
|
101
|
+
Native.wgpuRenderBundleEncoderSetIndexBuffer(@handle, NativeResource.checked_handle(buffer, expected_class: Buffer), format_value, offset, size)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
# Records a non-indexed draw in the bundle.
|
|
@@ -124,7 +124,7 @@ module WGPU
|
|
|
124
124
|
def draw_indirect(buffer, offset: 0)
|
|
125
125
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
126
126
|
|
|
127
|
-
Native.wgpuRenderBundleEncoderDrawIndirect(@handle, buffer
|
|
127
|
+
Native.wgpuRenderBundleEncoderDrawIndirect(@handle, NativeResource.checked_handle(buffer, expected_class: Buffer), offset)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
# Records an indexed draw using buffer arguments.
|
|
@@ -134,7 +134,7 @@ module WGPU
|
|
|
134
134
|
def draw_indexed_indirect(buffer, offset: 0)
|
|
135
135
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
136
136
|
|
|
137
|
-
Native.wgpuRenderBundleEncoderDrawIndexedIndirect(@handle, buffer
|
|
137
|
+
Native.wgpuRenderBundleEncoderDrawIndexedIndirect(@handle, NativeResource.checked_handle(buffer, expected_class: Buffer), offset)
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
# Starts a labeled group in GPU debugging tools.
|
|
@@ -34,10 +34,10 @@ module WGPU
|
|
|
34
34
|
desc[:depth_stencil_attachment] = nil
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
desc[:occlusion_query_set] = occlusion_query_set
|
|
37
|
+
desc[:occlusion_query_set] = (occlusion_query_set && NativeResource.checked_handle(occlusion_query_set, expected_class: QuerySet))
|
|
38
38
|
if timestamp_writes
|
|
39
39
|
ts = Native::RenderPassTimestampWrites.new
|
|
40
|
-
ts[:query_set] = timestamp_writes.fetch(:query_set)
|
|
40
|
+
ts[:query_set] = NativeResource.checked_handle(timestamp_writes.fetch(:query_set), expected_class: QuerySet)
|
|
41
41
|
ts[:beginning_of_pass_write_index] = timestamp_writes[:beginning_of_pass_write_index] || 0xFFFFFFFF
|
|
42
42
|
ts[:end_of_pass_write_index] = timestamp_writes[:end_of_pass_write_index] || 0xFFFFFFFF
|
|
43
43
|
@pointers << ts
|
|
@@ -46,7 +46,7 @@ module WGPU
|
|
|
46
46
|
desc[:timestamp_writes] = nil
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
@handle = Native.wgpuCommandEncoderBeginRenderPass(encoder
|
|
49
|
+
@handle = Native.wgpuCommandEncoderBeginRenderPass(NativeResource.checked_handle(encoder, expected_class: CommandEncoder), desc)
|
|
50
50
|
raise CommandError, "Failed to begin render pass" if @handle.null?
|
|
51
51
|
end
|
|
52
52
|
|
|
@@ -54,7 +54,7 @@ module WGPU
|
|
|
54
54
|
# @param pipeline [RenderPipeline] pipeline to bind
|
|
55
55
|
# @return [void]
|
|
56
56
|
def set_pipeline(pipeline)
|
|
57
|
-
Native.wgpuRenderPassEncoderSetPipeline(@handle, pipeline
|
|
57
|
+
Native.wgpuRenderPassEncoderSetPipeline(@handle, NativeResource.checked_handle(pipeline, expected_class: RenderPipeline))
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# Binds a resource group for subsequent draws.
|
|
@@ -64,11 +64,11 @@ module WGPU
|
|
|
64
64
|
# @return [void]
|
|
65
65
|
def set_bind_group(index, bind_group, dynamic_offsets: [])
|
|
66
66
|
if dynamic_offsets.empty?
|
|
67
|
-
Native.wgpuRenderPassEncoderSetBindGroup(@handle, index, bind_group
|
|
67
|
+
Native.wgpuRenderPassEncoderSetBindGroup(@handle, index, NativeResource.checked_handle(bind_group, expected_class: BindGroup), 0, nil)
|
|
68
68
|
else
|
|
69
69
|
offsets_ptr = FFI::MemoryPointer.new(:uint32, dynamic_offsets.size)
|
|
70
70
|
offsets_ptr.write_array_of_uint32(dynamic_offsets)
|
|
71
|
-
Native.wgpuRenderPassEncoderSetBindGroup(@handle, index, bind_group
|
|
71
|
+
Native.wgpuRenderPassEncoderSetBindGroup(@handle, index, NativeResource.checked_handle(bind_group, expected_class: BindGroup), dynamic_offsets.size, offsets_ptr)
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
@@ -80,7 +80,7 @@ module WGPU
|
|
|
80
80
|
# @return [void]
|
|
81
81
|
def set_vertex_buffer(slot, buffer, offset: 0, size: nil)
|
|
82
82
|
size ||= buffer.size - offset
|
|
83
|
-
Native.wgpuRenderPassEncoderSetVertexBuffer(@handle, slot, buffer
|
|
83
|
+
Native.wgpuRenderPassEncoderSetVertexBuffer(@handle, slot, NativeResource.checked_handle(buffer, expected_class: Buffer), offset, size)
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# Binds an index buffer for indexed draws.
|
|
@@ -92,7 +92,7 @@ module WGPU
|
|
|
92
92
|
def set_index_buffer(buffer, format, offset: 0, size: nil)
|
|
93
93
|
size ||= buffer.size - offset
|
|
94
94
|
format_value = Native::EnumHelper.coerce(Native::IndexFormat, format, name: "index format")
|
|
95
|
-
Native.wgpuRenderPassEncoderSetIndexBuffer(@handle, buffer
|
|
95
|
+
Native.wgpuRenderPassEncoderSetIndexBuffer(@handle, NativeResource.checked_handle(buffer, expected_class: Buffer), format_value, offset, size)
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
# Records a non-indexed draw.
|
|
@@ -161,7 +161,7 @@ module WGPU
|
|
|
161
161
|
# @param offset [Integer] byte offset of the arguments
|
|
162
162
|
# @return [void]
|
|
163
163
|
def draw_indirect(buffer, offset: 0)
|
|
164
|
-
Native.wgpuRenderPassEncoderDrawIndirect(@handle, buffer
|
|
164
|
+
Native.wgpuRenderPassEncoderDrawIndirect(@handle, NativeResource.checked_handle(buffer, expected_class: Buffer), offset)
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
# Draws using indexed arguments stored in a buffer.
|
|
@@ -170,7 +170,7 @@ module WGPU
|
|
|
170
170
|
# @param offset [Integer] byte offset of the arguments
|
|
171
171
|
# @return [void]
|
|
172
172
|
def draw_indexed_indirect(buffer, offset: 0)
|
|
173
|
-
Native.wgpuRenderPassEncoderDrawIndexedIndirect(@handle, buffer
|
|
173
|
+
Native.wgpuRenderPassEncoderDrawIndexedIndirect(@handle, NativeResource.checked_handle(buffer, expected_class: Buffer), offset)
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
# Executes pre-recorded render bundles.
|
|
@@ -178,7 +178,7 @@ module WGPU
|
|
|
178
178
|
# @param bundles [Array<RenderBundle>] bundles to execute in order
|
|
179
179
|
# @return [void]
|
|
180
180
|
def execute_bundles(bundles)
|
|
181
|
-
bundle_handles = bundles.map(
|
|
181
|
+
bundle_handles = bundles.map { |bundle| NativeResource.checked_handle(bundle, expected_class: RenderBundle) }
|
|
182
182
|
bundles_ptr = FFI::MemoryPointer.new(:pointer, bundle_handles.size)
|
|
183
183
|
bundles_ptr.write_array_of_pointer(bundle_handles)
|
|
184
184
|
Native.wgpuRenderPassEncoderExecuteBundles(@handle, bundle_handles.size, bundles_ptr)
|
|
@@ -283,9 +283,9 @@ module WGPU
|
|
|
283
283
|
attachments.each_with_index do |att, i|
|
|
284
284
|
ca = Native::RenderPassColorAttachment.new(ptr + (i * Native::RenderPassColorAttachment.size))
|
|
285
285
|
ca[:next_in_chain] = nil
|
|
286
|
-
ca[:view] = att[:view]
|
|
286
|
+
ca[:view] = NativeResource.checked_handle(att[:view], expected_class: TextureView)
|
|
287
287
|
ca[:depth_slice] = att[:depth_slice] || 0xFFFFFFFF
|
|
288
|
-
ca[:resolve_target] = att[:resolve_target]
|
|
288
|
+
ca[:resolve_target] = (att[:resolve_target] && NativeResource.checked_handle(att[:resolve_target], expected_class: TextureView))
|
|
289
289
|
ca[:load_op] = Native::EnumHelper.coerce(Native::LoadOp, att[:load_op] || :clear, name: "load op")
|
|
290
290
|
ca[:store_op] = Native::EnumHelper.coerce(Native::StoreOp, att[:store_op] || :store, name: "store op")
|
|
291
291
|
|
|
@@ -303,7 +303,7 @@ module WGPU
|
|
|
303
303
|
ds = Native::RenderPassDepthStencilAttachment.new
|
|
304
304
|
@pointers << ds
|
|
305
305
|
|
|
306
|
-
ds[:view] = att[:view]
|
|
306
|
+
ds[:view] = NativeResource.checked_handle(att[:view], expected_class: TextureView)
|
|
307
307
|
ds[:depth_load_op] = Native::EnumHelper.coerce(
|
|
308
308
|
Native::LoadOp,
|
|
309
309
|
att[:depth_load_op] || :clear,
|
data/lib/wgpu/core/adapter.rb
CHANGED
|
@@ -28,6 +28,7 @@ module WGPU
|
|
|
28
28
|
# @raise [TimeoutError] if the request exceeds +timeout+
|
|
29
29
|
def self.request(instance, power_preference: :high_performance, backend: nil, feature_level: :core,
|
|
30
30
|
force_fallback_adapter: false, compatible_surface: nil, timeout: nil)
|
|
31
|
+
timeout = AsyncWaiter.normalize_timeout(timeout)
|
|
31
32
|
adapter_ptr = FFI::MemoryPointer.new(:pointer)
|
|
32
33
|
status_holder = {
|
|
33
34
|
done: false,
|
|
@@ -80,7 +81,7 @@ module WGPU
|
|
|
80
81
|
backend || :undefined,
|
|
81
82
|
name: "backend type"
|
|
82
83
|
)
|
|
83
|
-
options[:compatible_surface] = compatible_surface
|
|
84
|
+
options[:compatible_surface] = (compatible_surface && NativeResource.checked_handle(compatible_surface, expected_class: Surface))
|
|
84
85
|
|
|
85
86
|
callback_info = Native::RequestAdapterCallbackInfo.new
|
|
86
87
|
callback_info[:next_in_chain] = nil
|
|
@@ -92,7 +93,7 @@ module WGPU
|
|
|
92
93
|
callback_token = CallbackKeepalive.retain(instance, callback)
|
|
93
94
|
future =
|
|
94
95
|
begin
|
|
95
|
-
Native.wgpuInstanceRequestAdapter(instance
|
|
96
|
+
Native.wgpuInstanceRequestAdapter(NativeResource.checked_handle(instance, expected_class: Instance), options, callback_info)
|
|
96
97
|
rescue StandardError
|
|
97
98
|
CallbackKeepalive.release(instance, callback_token)
|
|
98
99
|
raise
|
|
@@ -100,7 +101,7 @@ module WGPU
|
|
|
100
101
|
|
|
101
102
|
begin
|
|
102
103
|
AsyncWaiter.wait(status_holder: status_holder, instance: instance, future: future, timeout: timeout)
|
|
103
|
-
rescue
|
|
104
|
+
rescue StandardError
|
|
104
105
|
abandoned_adapter = status_holder[:mutex].synchronize do
|
|
105
106
|
status_holder[:abandoned] = true
|
|
106
107
|
next unless status_holder[:done] && !status_holder[:cleanup_claimed]
|
|
@@ -212,6 +213,8 @@ module WGPU
|
|
|
212
213
|
end
|
|
213
214
|
end
|
|
214
215
|
result
|
|
216
|
+
ensure
|
|
217
|
+
Native.wgpuSupportedFeaturesFreeMembers(supported) if supported
|
|
215
218
|
end
|
|
216
219
|
|
|
217
220
|
# Reports whether the adapter supports a feature.
|
|
@@ -45,8 +45,7 @@ module WGPU
|
|
|
45
45
|
# @return [void]
|
|
46
46
|
# @raise [TimeoutError] if completion exceeds the timeout
|
|
47
47
|
def wait(status_holder:, instance: nil, device: nil, future: nil, timeout: nil)
|
|
48
|
-
timeout =
|
|
49
|
-
raise ArgumentError, "timeout must be non-negative" if timeout&.negative?
|
|
48
|
+
timeout = normalize_timeout(timeout)
|
|
50
49
|
|
|
51
50
|
deadline = monotonic_time + timeout if timeout
|
|
52
51
|
wait_info = build_wait_info(future) if instance && Native.future_api?
|
|
@@ -60,12 +59,24 @@ module WGPU
|
|
|
60
59
|
elsif instance
|
|
61
60
|
instance.process_events
|
|
62
61
|
elsif device && Native.device_poll_available?
|
|
63
|
-
Native.wgpuDevicePoll(device
|
|
62
|
+
Native.wgpuDevicePoll(NativeResource.checked_handle(device, expected_class: Device), 0, nil)
|
|
64
63
|
end
|
|
65
64
|
sleep(poll_interval) unless status_holder[:done] || waited
|
|
66
65
|
end
|
|
67
66
|
end
|
|
68
67
|
|
|
68
|
+
# Validates a timeout before any native operation is started.
|
|
69
|
+
def normalize_timeout(timeout)
|
|
70
|
+
return nil if timeout.nil?
|
|
71
|
+
|
|
72
|
+
value = Float(timeout)
|
|
73
|
+
raise ArgumentError, "timeout must be finite and non-negative" unless value.finite? && value >= 0
|
|
74
|
+
|
|
75
|
+
value
|
|
76
|
+
rescue TypeError
|
|
77
|
+
raise ArgumentError, "timeout must be a number"
|
|
78
|
+
end
|
|
79
|
+
|
|
69
80
|
def callback_mode_value(name)
|
|
70
81
|
Native::CallbackMode[name] || CALLBACK_MODE_FALLBACK.fetch(name)
|
|
71
82
|
end
|
|
@@ -87,7 +98,7 @@ module WGPU
|
|
|
87
98
|
private_class_method :build_wait_info
|
|
88
99
|
|
|
89
100
|
def wait_with_wait_any(instance, wait_info)
|
|
90
|
-
status = Native.wgpuInstanceWaitAny(instance
|
|
101
|
+
status = Native.wgpuInstanceWaitAny(NativeResource.checked_handle(instance, expected_class: Instance), 1, wait_info.to_ptr, 0)
|
|
91
102
|
return false if [:success, :timed_out].include?(status)
|
|
92
103
|
|
|
93
104
|
raise Error, "wgpuInstanceWaitAny failed: #{status.inspect}"
|
|
@@ -10,7 +10,10 @@ module WGPU
|
|
|
10
10
|
def initialize(instance, present_info = {})
|
|
11
11
|
@instance = instance
|
|
12
12
|
@present_info = present_info || {}
|
|
13
|
-
@surface = @present_info[:
|
|
13
|
+
@surface = @present_info[:wgpu_surface]
|
|
14
|
+
if @present_info[:surface].is_a?(Surface) || ![:wayland, :linux_wayland].include?(@present_info[:platform]&.to_sym)
|
|
15
|
+
@surface ||= @present_info[:surface]
|
|
16
|
+
end
|
|
14
17
|
@physical_size = [0, 0]
|
|
15
18
|
@config = nil
|
|
16
19
|
end
|
|
@@ -125,7 +128,7 @@ module WGPU
|
|
|
125
128
|
when :x11, :linux_x11
|
|
126
129
|
Surface.from_xlib_window(@instance, @present_info.fetch(:display), @present_info.fetch(:window))
|
|
127
130
|
when :wayland, :linux_wayland
|
|
128
|
-
Surface.from_wayland_surface(@instance, @present_info.fetch(:display), @present_info.fetch(:surface))
|
|
131
|
+
Surface.from_wayland_surface(@instance, @present_info.fetch(:display), @present_info.fetch(:wl_surface) { @present_info.fetch(:surface) })
|
|
129
132
|
else
|
|
130
133
|
raise SurfaceError, "Cannot build surface from present_info: #{@present_info.inspect}"
|
|
131
134
|
end
|