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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +18 -2
- data/docs/README.md +2 -2
- data/docs/api_coverage.md +23 -15
- data/docs/async.md +10 -0
- data/docs/command_encoding.md +25 -0
- data/docs/getting_started_compute.md +1 -0
- data/docs/pipeline_descriptors.md +7 -2
- data/docs/releasing.md +11 -1
- data/docs/resource_lifetime.md +16 -2
- data/docs/texture_readback.md +27 -0
- data/docs/troubleshooting.md +5 -0
- data/docs/upgrading_wgpu_native.md +16 -5
- data/lib/wgpu/async_task.rb +19 -0
- data/lib/wgpu/commands/command_buffer.rb +14 -1
- data/lib/wgpu/commands/command_encoder.rb +58 -1
- data/lib/wgpu/commands/compute_pass.rb +43 -0
- data/lib/wgpu/commands/render_bundle.rb +9 -1
- data/lib/wgpu/commands/render_bundle_encoder.rb +55 -1
- data/lib/wgpu/commands/render_pass.rb +102 -0
- data/lib/wgpu/core/adapter.rb +91 -12
- data/lib/wgpu/core/async_waiter.rb +15 -0
- data/lib/wgpu/core/canvas_context.rb +32 -0
- data/lib/wgpu/core/device.rb +296 -46
- data/lib/wgpu/core/instance.rb +20 -0
- data/lib/wgpu/core/queue.rb +139 -24
- data/lib/wgpu/core/surface.rb +49 -1
- data/lib/wgpu/data_types.rb +16 -0
- data/lib/wgpu/descriptor_helpers.rb +65 -0
- data/lib/wgpu/error.rb +15 -0
- data/lib/wgpu/native/abi_verifier.rb +37 -3
- data/lib/wgpu/native/callbacks.rb +6 -0
- data/lib/wgpu/native/capabilities.rb +16 -2
- data/lib/wgpu/native/distribution.rb +63 -0
- data/lib/wgpu/native/enum_helper.rb +17 -0
- data/lib/wgpu/native/enums.rb +8 -0
- data/lib/wgpu/native/fixtures/webgpu-v27.0.4.0-enums.h +848 -0
- data/lib/wgpu/native/functions.rb +11 -0
- data/lib/wgpu/native/installer.rb +48 -17
- data/lib/wgpu/native/loader.rb +22 -0
- data/lib/wgpu/native/structs.rb +18 -1
- data/lib/wgpu/native_resource.rb +174 -3
- data/lib/wgpu/pipeline/bind_group.rb +9 -0
- data/lib/wgpu/pipeline/bind_group_layout.rb +17 -4
- data/lib/wgpu/pipeline/compute_pipeline.rb +20 -32
- data/lib/wgpu/pipeline/pipeline_layout.rb +8 -0
- data/lib/wgpu/pipeline/render_pipeline.rb +27 -42
- data/lib/wgpu/pipeline/shader_module.rb +54 -29
- data/lib/wgpu/resources/buffer.rb +258 -31
- data/lib/wgpu/resources/query_set.rb +12 -0
- data/lib/wgpu/resources/sampler.rb +7 -0
- data/lib/wgpu/resources/texture.rb +45 -6
- data/lib/wgpu/resources/texture_view.rb +24 -4
- data/lib/wgpu/texture_format.rb +14 -0
- data/lib/wgpu/version.rb +1 -1
- data/lib/wgpu/window.rb +26 -0
- data/sig/wgpu.rbs +84 -5
- metadata +3 -1
|
@@ -4,6 +4,12 @@ module WGPU
|
|
|
4
4
|
class Buffer
|
|
5
5
|
attr_reader :handle, :size, :usage
|
|
6
6
|
|
|
7
|
+
# Creates a GPU buffer with the requested size and usage.
|
|
8
|
+
# @param device [Device] owning device
|
|
9
|
+
# @param size [Integer] buffer size in bytes
|
|
10
|
+
# @param usage [Symbol, Array<Symbol>, Integer] usage flags
|
|
11
|
+
# @param mapped_at_creation [Boolean] whether to expose an initial mapped range
|
|
12
|
+
# @raise [BufferError] if native validation or creation fails
|
|
7
13
|
def initialize(device, label: nil, size:, usage:, mapped_at_creation: false)
|
|
8
14
|
@device = device
|
|
9
15
|
@size = size
|
|
@@ -11,10 +17,12 @@ module WGPU
|
|
|
11
17
|
begin
|
|
12
18
|
normalize_usage(usage)
|
|
13
19
|
rescue ArgumentError => e
|
|
14
|
-
raise
|
|
20
|
+
raise ArgumentError, buffer_error_message(e.message, label)
|
|
15
21
|
end
|
|
16
22
|
@mapped = mapped_at_creation
|
|
17
23
|
@map_state = mapped_at_creation ? :mapped : :unmapped
|
|
24
|
+
@map_generation = 0
|
|
25
|
+
@map_state_mutex = Mutex.new
|
|
18
26
|
|
|
19
27
|
desc, keepalive = build_descriptor(
|
|
20
28
|
label:,
|
|
@@ -35,6 +43,11 @@ module WGPU
|
|
|
35
43
|
end
|
|
36
44
|
end
|
|
37
45
|
|
|
46
|
+
# Writes typed data through the device's default queue.
|
|
47
|
+
# @param data [Array, String, FFI::Pointer] source data
|
|
48
|
+
# @param offset [Integer] destination byte offset
|
|
49
|
+
# @param type [Symbol] source element type
|
|
50
|
+
# @return [void]
|
|
38
51
|
def write(data, offset: 0, type: :f32)
|
|
39
52
|
ptr, byte_size = DataTypes.to_pointer(data, type:)
|
|
40
53
|
DataTypes.validate_alignment!(offset, 4, name: "offset")
|
|
@@ -42,116 +55,192 @@ module WGPU
|
|
|
42
55
|
Native.wgpuQueueWriteBuffer(@device.queue.handle, @handle, offset, ptr, byte_size)
|
|
43
56
|
end
|
|
44
57
|
|
|
58
|
+
# Returns a writable view of a mapped byte range.
|
|
59
|
+
# @return [BufferMappedRange]
|
|
60
|
+
# @raise [BufferError] if the buffer is not mapped or no range is available
|
|
45
61
|
def mapped_range(offset: 0, size: nil)
|
|
46
62
|
raise BufferError, "Buffer is not mapped" unless @mapped
|
|
47
63
|
|
|
48
64
|
size ||= @size - offset
|
|
49
|
-
validate_map_range!(offset, size)
|
|
65
|
+
offset, size = validate_map_range!(offset, size)
|
|
50
66
|
ptr = Native.wgpuBufferGetMappedRange(@handle, offset, size)
|
|
51
67
|
raise BufferError, "Failed to get mapped range" if ptr.null?
|
|
52
68
|
|
|
53
69
|
BufferMappedRange.new(ptr, size)
|
|
54
70
|
end
|
|
55
71
|
|
|
72
|
+
# Returns a writable view of a mapped byte range.
|
|
73
|
+
# @return [BufferMappedRange]
|
|
56
74
|
def get_mapped_range(offset: 0, size: nil)
|
|
57
75
|
mapped_range(offset: offset, size: size)
|
|
58
76
|
end
|
|
59
77
|
|
|
78
|
+
# Unmaps the buffer and invalidates its mapped ranges.
|
|
79
|
+
# @return [void]
|
|
60
80
|
def unmap
|
|
61
81
|
Native.wgpuBufferUnmap(@handle)
|
|
62
|
-
|
|
63
|
-
|
|
82
|
+
map_state_mutex.synchronize do
|
|
83
|
+
@map_generation = current_map_generation + 1
|
|
84
|
+
@mapped = false
|
|
85
|
+
@map_state = :unmapped
|
|
86
|
+
end
|
|
64
87
|
end
|
|
65
88
|
|
|
89
|
+
# Maps a range and waits for native completion.
|
|
90
|
+
# @param mode [Symbol, Integer] map access mode
|
|
91
|
+
# @param timeout [Numeric, nil] maximum wait time in seconds
|
|
92
|
+
# @return [Boolean] true when mapped
|
|
93
|
+
# @raise [BufferError] if mapping fails
|
|
66
94
|
def map_sync(mode, offset: 0, size: nil, timeout: nil)
|
|
67
|
-
status_holder,
|
|
95
|
+
status_holder, _callback_token, future, generation =
|
|
96
|
+
begin_map_request(mode, offset: offset, size: size)
|
|
68
97
|
wait_for_map(status_holder, future, timeout:)
|
|
69
|
-
finalize_map(status_holder)
|
|
70
|
-
ensure
|
|
71
|
-
CallbackKeepalive.release(self, callback_token)
|
|
98
|
+
finalize_map(status_holder, generation)
|
|
72
99
|
end
|
|
73
100
|
|
|
101
|
+
# Maps a range on a background task.
|
|
102
|
+
# @param mode [Symbol, Integer] map access mode
|
|
103
|
+
# @return [AsyncTask] task yielding true when mapped
|
|
74
104
|
def map_async(mode, offset: 0, size: nil)
|
|
75
|
-
status_holder,
|
|
105
|
+
status_holder, _callback_token, future, generation =
|
|
106
|
+
begin_map_request(mode, offset: offset, size: size)
|
|
76
107
|
AsyncTask.new do
|
|
77
108
|
wait_for_map(status_holder, future)
|
|
78
|
-
finalize_map(status_holder)
|
|
79
|
-
ensure
|
|
80
|
-
CallbackKeepalive.release(self, callback_token)
|
|
109
|
+
finalize_map(status_holder, generation)
|
|
81
110
|
end
|
|
82
111
|
end
|
|
83
112
|
|
|
113
|
+
# Copies bytes from a const mapped range.
|
|
114
|
+
# @return [String] mapped bytes
|
|
115
|
+
# @raise [BufferError] if the buffer is not mapped
|
|
84
116
|
def read_mapped_data(offset: 0, size: nil)
|
|
85
117
|
raise BufferError, "Buffer is not mapped" unless @mapped
|
|
86
118
|
|
|
87
119
|
size ||= @size - offset
|
|
88
|
-
validate_map_range!(offset, size)
|
|
120
|
+
offset, size = validate_map_range!(offset, size)
|
|
89
121
|
ptr = Native.wgpuBufferGetConstMappedRange(@handle, offset, size)
|
|
90
122
|
raise BufferError, "Failed to get mapped range" if ptr.null?
|
|
91
123
|
|
|
92
124
|
ptr.read_bytes(size)
|
|
93
125
|
end
|
|
94
126
|
|
|
127
|
+
# Reads mapped bytes, optionally decoding typed values.
|
|
128
|
+
# @param type [Symbol, nil] element type, or +nil+ for raw bytes
|
|
129
|
+
# @return [String, Array]
|
|
95
130
|
def read_mapped(offset: 0, size: nil, type: nil)
|
|
96
131
|
bytes = read_mapped_data(offset: offset, size: size)
|
|
97
132
|
type ? DataTypes.unpack(bytes, type:) : bytes
|
|
98
133
|
end
|
|
99
134
|
|
|
135
|
+
# Writes typed data into a mapped range.
|
|
136
|
+
# @param data [Array, String, FFI::Pointer] source data
|
|
137
|
+
# @param type [Symbol] source element type
|
|
138
|
+
# @return [void]
|
|
100
139
|
def write_mapped(data, offset: 0, type: :f32)
|
|
101
140
|
raise BufferError, "Buffer is not mapped" unless @mapped
|
|
102
141
|
|
|
103
142
|
ptr, byte_size = DataTypes.to_pointer(data, type:)
|
|
104
|
-
validate_map_range!(offset, byte_size)
|
|
143
|
+
offset, byte_size = validate_map_range!(offset, byte_size)
|
|
105
144
|
target = Native.wgpuBufferGetMappedRange(@handle, offset, byte_size)
|
|
106
145
|
raise BufferError, "Failed to get mapped range" if target.null?
|
|
107
146
|
|
|
108
147
|
target.put_bytes(0, ptr.read_bytes(byte_size))
|
|
109
148
|
end
|
|
110
149
|
|
|
150
|
+
# Reads mapped bytes as 32-bit floating-point values.
|
|
151
|
+
#
|
|
152
|
+
# @param offset [Integer] byte offset in the buffer
|
|
153
|
+
# @param count [Integer, nil] values to read; defaults to the remaining range
|
|
154
|
+
# @return [Array<Float>] decoded values
|
|
155
|
+
# @raise [BufferError] if the buffer is not mapped
|
|
111
156
|
def read_mapped_floats(offset: 0, count: nil)
|
|
112
157
|
read_mapped_values(type: :f32, offset:, count:)
|
|
113
158
|
end
|
|
114
159
|
|
|
160
|
+
# Reads mapped bytes as unsigned 32-bit integers.
|
|
161
|
+
#
|
|
162
|
+
# @param offset [Integer] byte offset in the buffer
|
|
163
|
+
# @param count [Integer, nil] values to read; defaults to the remaining range
|
|
164
|
+
# @return [Array<Integer>] decoded values
|
|
165
|
+
# @raise [BufferError] if the buffer is not mapped
|
|
115
166
|
def read_mapped_uint32s(offset: 0, count: nil)
|
|
116
167
|
read_mapped_values(type: :u32, offset:, count:)
|
|
117
168
|
end
|
|
118
169
|
|
|
170
|
+
# Reads mapped bytes as signed 32-bit integers.
|
|
171
|
+
#
|
|
172
|
+
# @param offset [Integer] byte offset in the buffer
|
|
173
|
+
# @param count [Integer, nil] values to read; defaults to the remaining range
|
|
174
|
+
# @return [Array<Integer>] decoded values
|
|
175
|
+
# @raise [BufferError] if the buffer is not mapped
|
|
119
176
|
def read_mapped_int32s(offset: 0, count: nil)
|
|
120
177
|
read_mapped_values(type: :i32, offset:, count:)
|
|
121
178
|
end
|
|
122
179
|
|
|
180
|
+
# Reads mapped bytes as 64-bit floating-point values.
|
|
181
|
+
#
|
|
182
|
+
# @param offset [Integer] byte offset in the buffer
|
|
183
|
+
# @param count [Integer, nil] values to read; defaults to the remaining range
|
|
184
|
+
# @return [Array<Float>] decoded values
|
|
185
|
+
# @raise [BufferError] if the buffer is not mapped
|
|
123
186
|
def read_mapped_float64s(offset: 0, count: nil)
|
|
124
187
|
read_mapped_values(type: :f64, offset:, count:)
|
|
125
188
|
end
|
|
126
189
|
|
|
190
|
+
# Reads mapped bytes as unsigned 16-bit integers.
|
|
191
|
+
#
|
|
192
|
+
# @param offset [Integer] byte offset in the buffer
|
|
193
|
+
# @param count [Integer, nil] values to read; defaults to the remaining range
|
|
194
|
+
# @return [Array<Integer>] decoded values
|
|
195
|
+
# @raise [BufferError] if the buffer is not mapped
|
|
127
196
|
def read_mapped_uint16s(offset: 0, count: nil)
|
|
128
197
|
read_mapped_values(type: :u16, offset:, count:)
|
|
129
198
|
end
|
|
130
199
|
|
|
200
|
+
# Reads mapped bytes as unsigned 8-bit integers.
|
|
201
|
+
#
|
|
202
|
+
# @param offset [Integer] byte offset in the buffer
|
|
203
|
+
# @param count [Integer, nil] values to read; defaults to the remaining range
|
|
204
|
+
# @return [Array<Integer>] decoded values
|
|
205
|
+
# @raise [BufferError] if the buffer is not mapped
|
|
131
206
|
def read_mapped_uint8s(offset: 0, count: nil)
|
|
132
207
|
read_mapped_values(type: :u8, offset:, count:)
|
|
133
208
|
end
|
|
134
209
|
|
|
210
|
+
# Reads mapped values of the requested element type.
|
|
211
|
+
# @param type [Symbol] element type
|
|
212
|
+
# @param count [Integer, nil] number of values
|
|
213
|
+
# @return [Array]
|
|
135
214
|
def read_mapped_values(type: :f32, offset: 0, count: nil)
|
|
136
215
|
element_size = DataTypes.byte_size(type)
|
|
137
216
|
size = count ? count * element_size : @size - offset
|
|
138
217
|
DataTypes.unpack(read_mapped_data(offset:, size:), type:)
|
|
139
218
|
end
|
|
140
219
|
|
|
220
|
+
# Returns the current mapping state.
|
|
221
|
+
# @return [Symbol]
|
|
141
222
|
def map_state
|
|
142
|
-
return @map_state unless Native.buffer_map_state_available?
|
|
223
|
+
return map_state_mutex.synchronize { @map_state } unless Native.buffer_map_state_available?
|
|
143
224
|
|
|
144
|
-
Native.wgpuBufferGetMapState(@handle) || @map_state
|
|
225
|
+
Native.wgpuBufferGetMapState(@handle) || map_state_mutex.synchronize { @map_state }
|
|
145
226
|
end
|
|
146
227
|
|
|
228
|
+
# Destroys the buffer's storage.
|
|
229
|
+
# @return [void]
|
|
147
230
|
def destroy
|
|
148
231
|
Native.wgpuBufferDestroy(@handle)
|
|
232
|
+
invalidate_map_state
|
|
149
233
|
end
|
|
150
234
|
|
|
235
|
+
# Releases the native buffer handle.
|
|
236
|
+
#
|
|
237
|
+
# Calling this method more than once has no effect.
|
|
238
|
+
# @return [void]
|
|
151
239
|
def release
|
|
152
240
|
return if @handle.null?
|
|
153
241
|
Native.wgpuBufferRelease(@handle)
|
|
154
242
|
@handle = FFI::Pointer::NULL
|
|
243
|
+
invalidate_map_state
|
|
155
244
|
end
|
|
156
245
|
|
|
157
246
|
private
|
|
@@ -169,15 +258,33 @@ module WGPU
|
|
|
169
258
|
|
|
170
259
|
def begin_map_request(mode, offset:, size:)
|
|
171
260
|
size ||= @size - offset
|
|
172
|
-
validate_map_range!(offset, size)
|
|
261
|
+
offset, size = validate_map_range!(offset, size)
|
|
173
262
|
mode_flag = Native::EnumHelper.coerce(Native::MapMode, mode, name: "map mode")
|
|
174
263
|
|
|
175
264
|
status_holder = { done: false, status: nil, message: nil }
|
|
265
|
+
generation = map_state_mutex.synchronize do
|
|
266
|
+
@map_generation = current_map_generation + 1
|
|
267
|
+
@map_state = :pending
|
|
268
|
+
@map_generation
|
|
269
|
+
end
|
|
270
|
+
callback_lifetime_release = device_callback_lifetime_lease
|
|
271
|
+
callback_token = nil
|
|
176
272
|
callback = FFI::Function.new(:void, [:uint32, Native::StringView.by_value, :pointer, :pointer]) do |status, message, _userdata1, _userdata2|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
273
|
+
begin
|
|
274
|
+
status_holder[:status] = Native::MapAsyncStatus[status]
|
|
275
|
+
if message[:data] && !message[:data].null? && message[:length] > 0
|
|
276
|
+
status_holder[:message] = message[:data].read_string(message[:length])
|
|
277
|
+
end
|
|
278
|
+
map_state_mutex.synchronize do
|
|
279
|
+
if generation == current_map_generation && !@handle.null?
|
|
280
|
+
@mapped = status_holder[:status] == :success
|
|
281
|
+
@map_state = @mapped ? :mapped : :unmapped
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
status_holder[:done] = true
|
|
285
|
+
ensure
|
|
286
|
+
CallbackKeepalive.release(self, callback_token)
|
|
287
|
+
callback_lifetime_release.call
|
|
181
288
|
end
|
|
182
289
|
end
|
|
183
290
|
callback_token = CallbackKeepalive.retain(self, callback)
|
|
@@ -189,10 +296,17 @@ module WGPU
|
|
|
189
296
|
callback_info[:userdata1] = nil
|
|
190
297
|
callback_info[:userdata2] = nil
|
|
191
298
|
|
|
192
|
-
future =
|
|
193
|
-
|
|
299
|
+
future =
|
|
300
|
+
begin
|
|
301
|
+
Native.wgpuBufferMapAsync(@handle, mode_flag, offset, size, callback_info)
|
|
302
|
+
rescue StandardError
|
|
303
|
+
CallbackKeepalive.release(self, callback_token)
|
|
304
|
+
callback_lifetime_release.call
|
|
305
|
+
invalidate_map_state(generation)
|
|
306
|
+
raise
|
|
307
|
+
end
|
|
194
308
|
|
|
195
|
-
[status_holder, callback_token, future]
|
|
309
|
+
[status_holder, callback_token, future, generation]
|
|
196
310
|
end
|
|
197
311
|
|
|
198
312
|
def wait_for_map(status_holder, future, timeout: nil)
|
|
@@ -205,19 +319,41 @@ module WGPU
|
|
|
205
319
|
)
|
|
206
320
|
end
|
|
207
321
|
|
|
208
|
-
def finalize_map(status_holder)
|
|
322
|
+
def finalize_map(status_holder, generation)
|
|
209
323
|
if status_holder[:status] == :success
|
|
210
|
-
|
|
211
|
-
|
|
324
|
+
map_state_mutex.synchronize do
|
|
325
|
+
if generation == current_map_generation && !@handle.null?
|
|
326
|
+
@mapped = true
|
|
327
|
+
@map_state = :mapped
|
|
328
|
+
end
|
|
329
|
+
end
|
|
212
330
|
true
|
|
213
331
|
else
|
|
214
|
-
|
|
332
|
+
invalidate_map_state(generation)
|
|
215
333
|
detail = status_holder[:message]
|
|
216
334
|
base = "Failed to map buffer: #{status_holder[:status]}"
|
|
217
335
|
raise BufferError, detail && !detail.empty? ? "#{base} (#{detail})" : base
|
|
218
336
|
end
|
|
219
337
|
end
|
|
220
338
|
|
|
339
|
+
def invalidate_map_state(generation = nil)
|
|
340
|
+
map_state_mutex.synchronize do
|
|
341
|
+
return if generation && generation != current_map_generation
|
|
342
|
+
|
|
343
|
+
@map_generation = current_map_generation + 1
|
|
344
|
+
@mapped = false
|
|
345
|
+
@map_state = :unmapped
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def current_map_generation
|
|
350
|
+
@map_generation ||= 0
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def map_state_mutex
|
|
354
|
+
@map_state_mutex ||= Mutex.new
|
|
355
|
+
end
|
|
356
|
+
|
|
221
357
|
def normalize_usage(usage)
|
|
222
358
|
Native::EnumHelper.coerce_flags(Native::BufferUsage, usage, name: "buffer usage")
|
|
223
359
|
end
|
|
@@ -228,71 +364,148 @@ module WGPU
|
|
|
228
364
|
end
|
|
229
365
|
|
|
230
366
|
def validate_map_range!(offset, size)
|
|
231
|
-
DataTypes.validate_alignment!(offset, 8, name: "map offset")
|
|
232
|
-
DataTypes.validate_alignment!(size, 4, name: "map size")
|
|
367
|
+
offset = DataTypes.validate_alignment!(offset, 8, name: "map offset")
|
|
368
|
+
size = DataTypes.validate_alignment!(size, 4, name: "map size")
|
|
369
|
+
if offset > @size || size > @size - offset
|
|
370
|
+
raise ArgumentError,
|
|
371
|
+
"mapped range (offset #{offset}, size #{size}) exceeds buffer size #{@size}"
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
[offset, size]
|
|
233
375
|
end
|
|
234
376
|
end
|
|
235
377
|
|
|
236
378
|
class BufferMappedRange
|
|
379
|
+
# Wraps a native mapped memory range.
|
|
380
|
+
# @param pointer [FFI::Pointer] start of mapped memory
|
|
381
|
+
# @param size [Integer] range size in bytes
|
|
237
382
|
def initialize(pointer, size)
|
|
238
383
|
@pointer = pointer
|
|
239
384
|
@size = size
|
|
240
385
|
end
|
|
241
386
|
|
|
387
|
+
# Reads 32-bit floating-point values from the mapped range.
|
|
388
|
+
#
|
|
389
|
+
# @param count [Integer, nil] values to read; defaults to the full range
|
|
390
|
+
# @return [Array<Float>] decoded values
|
|
242
391
|
def read_floats(count = nil)
|
|
243
392
|
read(type: :f32, count:)
|
|
244
393
|
end
|
|
245
394
|
|
|
395
|
+
# Writes 32-bit floating-point values into the mapped range.
|
|
396
|
+
#
|
|
397
|
+
# @param data [Array<Numeric>] values to write
|
|
398
|
+
# @return [void]
|
|
399
|
+
# @raise [ArgumentError] if the data exceeds the mapped range
|
|
246
400
|
def write_floats(data)
|
|
247
401
|
write(data, type: :f32)
|
|
248
402
|
end
|
|
249
403
|
|
|
404
|
+
# Reads unsigned 32-bit integers from the mapped range.
|
|
405
|
+
#
|
|
406
|
+
# @param count [Integer, nil] values to read; defaults to the full range
|
|
407
|
+
# @return [Array<Integer>] decoded values
|
|
250
408
|
def read_uint32s(count = nil)
|
|
251
409
|
read(type: :u32, count:)
|
|
252
410
|
end
|
|
253
411
|
|
|
412
|
+
# Writes unsigned 32-bit integers into the mapped range.
|
|
413
|
+
#
|
|
414
|
+
# @param data [Array<Integer>] values to write
|
|
415
|
+
# @return [void]
|
|
416
|
+
# @raise [ArgumentError] if the data exceeds the mapped range
|
|
254
417
|
def write_uint32s(data)
|
|
255
418
|
write(data, type: :u32)
|
|
256
419
|
end
|
|
257
420
|
|
|
421
|
+
# Reads signed 32-bit integers from the mapped range.
|
|
422
|
+
#
|
|
423
|
+
# @param count [Integer, nil] values to read; defaults to the full range
|
|
424
|
+
# @return [Array<Integer>] decoded values
|
|
258
425
|
def read_int32s(count = nil)
|
|
259
426
|
read(type: :i32, count:)
|
|
260
427
|
end
|
|
261
428
|
|
|
429
|
+
# Writes signed 32-bit integers into the mapped range.
|
|
430
|
+
#
|
|
431
|
+
# @param data [Array<Integer>] values to write
|
|
432
|
+
# @return [void]
|
|
433
|
+
# @raise [ArgumentError] if the data exceeds the mapped range
|
|
262
434
|
def write_int32s(data)
|
|
263
435
|
write(data, type: :i32)
|
|
264
436
|
end
|
|
265
437
|
|
|
438
|
+
# Reads 64-bit floating-point values from the mapped range.
|
|
439
|
+
#
|
|
440
|
+
# @param count [Integer, nil] values to read; defaults to the full range
|
|
441
|
+
# @return [Array<Float>] decoded values
|
|
266
442
|
def read_float64s(count = nil)
|
|
267
443
|
read(type: :f64, count:)
|
|
268
444
|
end
|
|
269
445
|
|
|
446
|
+
# Writes 64-bit floating-point values into the mapped range.
|
|
447
|
+
#
|
|
448
|
+
# @param data [Array<Numeric>] values to write
|
|
449
|
+
# @return [void]
|
|
450
|
+
# @raise [ArgumentError] if the data exceeds the mapped range
|
|
270
451
|
def write_float64s(data)
|
|
271
452
|
write(data, type: :f64)
|
|
272
453
|
end
|
|
273
454
|
|
|
455
|
+
# Reads unsigned 16-bit integers from the mapped range.
|
|
456
|
+
#
|
|
457
|
+
# @param count [Integer, nil] values to read; defaults to the full range
|
|
458
|
+
# @return [Array<Integer>] decoded values
|
|
274
459
|
def read_uint16s(count = nil)
|
|
275
460
|
read(type: :u16, count:)
|
|
276
461
|
end
|
|
277
462
|
|
|
463
|
+
# Writes unsigned 16-bit integers into the mapped range.
|
|
464
|
+
#
|
|
465
|
+
# @param data [Array<Integer>] values to write
|
|
466
|
+
# @return [void]
|
|
467
|
+
# @raise [ArgumentError] if the data exceeds the mapped range
|
|
278
468
|
def write_uint16s(data)
|
|
279
469
|
write(data, type: :u16)
|
|
280
470
|
end
|
|
281
471
|
|
|
472
|
+
# Reads unsigned 8-bit integers from the mapped range.
|
|
473
|
+
#
|
|
474
|
+
# @param count [Integer, nil] values to read; defaults to the full range
|
|
475
|
+
# @return [Array<Integer>] decoded values
|
|
282
476
|
def read_uint8s(count = nil)
|
|
283
477
|
read(type: :u8, count:)
|
|
284
478
|
end
|
|
285
479
|
|
|
480
|
+
# Writes unsigned 8-bit integers into the mapped range.
|
|
481
|
+
#
|
|
482
|
+
# @param data [Array<Integer>] values to write
|
|
483
|
+
# @return [void]
|
|
484
|
+
# @raise [ArgumentError] if the data exceeds the mapped range
|
|
286
485
|
def write_uint8s(data)
|
|
287
486
|
write(data, type: :u8)
|
|
288
487
|
end
|
|
289
488
|
|
|
489
|
+
# Decodes typed values from the mapped range.
|
|
490
|
+
# @param type [Symbol] element type
|
|
491
|
+
# @param count [Integer, nil] number of values
|
|
492
|
+
# @return [Array]
|
|
290
493
|
def read(type: :f32, count: nil)
|
|
291
494
|
byte_size = DataTypes.byte_size(type)
|
|
292
495
|
count ||= @size / byte_size
|
|
293
|
-
|
|
496
|
+
count = Integer(count)
|
|
497
|
+
raise ArgumentError, "count must be non-negative" if count.negative?
|
|
498
|
+
|
|
499
|
+
bytes_to_read = count * byte_size
|
|
500
|
+
validate_byte_length!(bytes_to_read)
|
|
501
|
+
DataTypes.unpack(@pointer.read_bytes(bytes_to_read), type:)
|
|
294
502
|
end
|
|
295
503
|
|
|
504
|
+
# Encodes typed values into the mapped range.
|
|
505
|
+
# @param data [Array, String] values or bytes to write
|
|
506
|
+
# @param type [Symbol] element type
|
|
507
|
+
# @return [void]
|
|
508
|
+
# @raise [ArgumentError] if the encoded data exceeds the range
|
|
296
509
|
def write(data, type: :f32)
|
|
297
510
|
bytes = data.is_a?(String) ? data : DataTypes.pack(data, type:)
|
|
298
511
|
raise ArgumentError, "data exceeds mapped range" if bytes.bytesize > @size
|
|
@@ -300,12 +513,26 @@ module WGPU
|
|
|
300
513
|
@pointer.put_bytes(0, bytes)
|
|
301
514
|
end
|
|
302
515
|
|
|
516
|
+
# Returns all bytes in the mapped range.
|
|
517
|
+
# @return [String]
|
|
303
518
|
def read_bytes
|
|
304
519
|
@pointer.read_bytes(@size)
|
|
305
520
|
end
|
|
306
521
|
|
|
522
|
+
# Writes bytes at the start of the mapped range.
|
|
523
|
+
# @param data [String] bytes to write
|
|
524
|
+
# @return [void]
|
|
307
525
|
def write_bytes(data)
|
|
526
|
+
validate_byte_length!(data.bytesize)
|
|
308
527
|
@pointer.put_bytes(0, data)
|
|
309
528
|
end
|
|
529
|
+
|
|
530
|
+
private
|
|
531
|
+
|
|
532
|
+
def validate_byte_length!(byte_length)
|
|
533
|
+
return if byte_length <= @size
|
|
534
|
+
|
|
535
|
+
raise ArgumentError, "data exceeds mapped range"
|
|
536
|
+
end
|
|
310
537
|
end
|
|
311
538
|
end
|
|
@@ -4,6 +4,11 @@ module WGPU
|
|
|
4
4
|
class QuerySet
|
|
5
5
|
attr_reader :handle, :count, :type
|
|
6
6
|
|
|
7
|
+
# Creates a set of GPU queries.
|
|
8
|
+
# @param device [Device] owning device
|
|
9
|
+
# @param type [Symbol, Integer] query type
|
|
10
|
+
# @param count [Integer] number of queries
|
|
11
|
+
# @raise [ResourceError] if native creation fails
|
|
7
12
|
def initialize(device, label: nil, type:, count:)
|
|
8
13
|
@device = device
|
|
9
14
|
@count = Integer(count)
|
|
@@ -28,6 +33,8 @@ module WGPU
|
|
|
28
33
|
raise ResourceError, "Failed to create query set" if @handle.null?
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
# Destroys the query storage once.
|
|
37
|
+
# @return [void]
|
|
31
38
|
def destroy
|
|
32
39
|
return if @destroyed
|
|
33
40
|
|
|
@@ -35,6 +42,11 @@ module WGPU
|
|
|
35
42
|
@destroyed = true
|
|
36
43
|
end
|
|
37
44
|
|
|
45
|
+
# Releases the native query set handle.
|
|
46
|
+
#
|
|
47
|
+
# A query set destroyed through {#destroy} is only marked released because
|
|
48
|
+
# the pinned native implementation cannot safely release it afterward.
|
|
49
|
+
# @return [void]
|
|
38
50
|
def release
|
|
39
51
|
return if @handle.null?
|
|
40
52
|
|
|
@@ -4,6 +4,9 @@ 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
|
|
|
@@ -33,6 +36,10 @@ module WGPU
|
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
39
|
+
# Releases the native sampler handle.
|
|
40
|
+
#
|
|
41
|
+
# Calling this method more than once has no effect.
|
|
42
|
+
# @return [void]
|
|
36
43
|
def release
|
|
37
44
|
return if @handle.null?
|
|
38
45
|
Native.wgpuSamplerRelease(@handle)
|