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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +88 -0
- data/README.md +44 -5
- data/docs/README.md +22 -0
- data/docs/api_coverage.md +140 -0
- data/docs/async.md +41 -0
- data/docs/bind_groups.md +25 -0
- data/docs/buffer_data.md +37 -0
- data/docs/command_encoding.md +49 -0
- data/docs/errors.md +40 -0
- data/docs/getting_started_compute.md +62 -0
- data/docs/getting_started_rendering.md +62 -0
- data/docs/installation.md +94 -0
- data/docs/pipeline_descriptors.md +43 -0
- data/docs/releasing.md +32 -0
- data/docs/resource_lifetime.md +108 -0
- data/docs/shaders.md +32 -0
- data/docs/texture_readback.md +46 -0
- data/docs/troubleshooting.md +53 -0
- data/docs/upgrading_wgpu_native.md +37 -0
- data/ext/wgpu/extconf.rb +10 -142
- data/lib/wgpu/async_task.rb +19 -0
- data/lib/wgpu/commands/command_buffer.rb +25 -1
- data/lib/wgpu/commands/command_encoder.rb +123 -9
- data/lib/wgpu/commands/compute_pass.rb +53 -0
- data/lib/wgpu/commands/render_bundle.rb +9 -1
- data/lib/wgpu/commands/render_bundle_encoder.rb +65 -4
- data/lib/wgpu/commands/render_pass.rb +136 -8
- data/lib/wgpu/core/adapter.rb +123 -19
- data/lib/wgpu/core/async_waiter.rb +47 -4
- data/lib/wgpu/core/canvas_context.rb +32 -2
- data/lib/wgpu/core/device.rb +399 -53
- data/lib/wgpu/core/instance.rb +28 -4
- data/lib/wgpu/core/queue.rb +197 -51
- data/lib/wgpu/core/surface.rb +64 -17
- data/lib/wgpu/data_types.rb +83 -0
- data/lib/wgpu/descriptor_helpers.rb +104 -0
- data/lib/wgpu/error.rb +70 -0
- data/lib/wgpu/logging.rb +63 -0
- data/lib/wgpu/native/abi_verifier.rb +143 -0
- data/lib/wgpu/native/callbacks.rb +10 -1
- data/lib/wgpu/native/capabilities.rb +32 -2
- data/lib/wgpu/native/distribution.rb +176 -0
- data/lib/wgpu/native/enum_helper.rb +80 -0
- data/lib/wgpu/native/enums.rb +68 -8
- data/lib/wgpu/native/fixtures/webgpu-v27.0.4.0-enums.h +848 -0
- data/lib/wgpu/native/functions.rb +21 -10
- data/lib/wgpu/native/installer.rb +223 -0
- data/lib/wgpu/native/loader.rb +61 -21
- data/lib/wgpu/native/structs.rb +18 -1
- data/lib/wgpu/native_resource.rb +342 -0
- data/lib/wgpu/pipeline/bind_group.rb +21 -0
- data/lib/wgpu/pipeline/bind_group_layout.rb +108 -41
- data/lib/wgpu/pipeline/compute_pipeline.rb +40 -50
- data/lib/wgpu/pipeline/pipeline_layout.rb +8 -0
- data/lib/wgpu/pipeline/render_pipeline.rb +207 -110
- data/lib/wgpu/pipeline/shader_module.rb +95 -28
- data/lib/wgpu/resources/buffer.rb +387 -85
- data/lib/wgpu/resources/query_set.rb +26 -12
- data/lib/wgpu/resources/sampler.rb +43 -20
- data/lib/wgpu/resources/texture.rb +89 -48
- data/lib/wgpu/resources/texture_view.rb +35 -7
- data/lib/wgpu/texture_format.rb +96 -0
- data/lib/wgpu/version.rb +1 -1
- data/lib/wgpu/window.rb +34 -1
- data/lib/wgpu.rb +32 -0
- data/sig/wgpu.rbs +460 -0
- metadata +33 -16
data/lib/wgpu/async_task.rb
CHANGED
|
@@ -4,6 +4,8 @@ require "timeout"
|
|
|
4
4
|
|
|
5
5
|
module WGPU
|
|
6
6
|
class AsyncTask
|
|
7
|
+
# Starts a background task that evaluates the given block.
|
|
8
|
+
# @raise [ArgumentError] if no block is supplied
|
|
7
9
|
def initialize(&block)
|
|
8
10
|
raise ArgumentError, "block is required" unless block
|
|
9
11
|
|
|
@@ -18,12 +20,20 @@ module WGPU
|
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
# Waits until the task finishes.
|
|
24
|
+
# @param timeout [Numeric, nil] maximum number of seconds to wait
|
|
25
|
+
# @return [AsyncTask] this task
|
|
26
|
+
# @raise [Timeout::Error] if the task does not finish in time
|
|
21
27
|
def wait(timeout: nil)
|
|
22
28
|
joined = timeout ? @thread.join(timeout) : @thread.join
|
|
23
29
|
raise Timeout::Error, "async task timeout" unless joined
|
|
24
30
|
self
|
|
25
31
|
end
|
|
26
32
|
|
|
33
|
+
# Waits for and returns the task result.
|
|
34
|
+
# @param timeout [Numeric, nil] maximum number of seconds to wait
|
|
35
|
+
# @return [Object] block result
|
|
36
|
+
# @raise [StandardError] re-raises an exception raised by the block
|
|
27
37
|
def value(timeout: nil)
|
|
28
38
|
wait(timeout: timeout)
|
|
29
39
|
raise @error if @error
|
|
@@ -31,20 +41,29 @@ module WGPU
|
|
|
31
41
|
@value
|
|
32
42
|
end
|
|
33
43
|
|
|
44
|
+
# Creates a task that transforms this task's result.
|
|
45
|
+
# @yieldparam value [Object] completed task result
|
|
46
|
+
# @return [AsyncTask] chained task
|
|
34
47
|
def then(&block)
|
|
35
48
|
AsyncTask.new do
|
|
36
49
|
block.call(value)
|
|
37
50
|
end
|
|
38
51
|
end
|
|
39
52
|
|
|
53
|
+
# Reports whether the task has finished.
|
|
54
|
+
# @return [Boolean]
|
|
40
55
|
def complete?
|
|
41
56
|
!@thread.alive?
|
|
42
57
|
end
|
|
43
58
|
|
|
59
|
+
# Reports whether the task is still running.
|
|
60
|
+
# @return [Boolean]
|
|
44
61
|
def pending?
|
|
45
62
|
@thread.alive?
|
|
46
63
|
end
|
|
47
64
|
|
|
65
|
+
# Returns the exception raised by the task, if any.
|
|
66
|
+
# @return [StandardError, nil]
|
|
48
67
|
def error
|
|
49
68
|
value
|
|
50
69
|
nil
|
|
@@ -4,10 +4,34 @@ module WGPU
|
|
|
4
4
|
class CommandBuffer
|
|
5
5
|
attr_reader :handle
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Wraps an encoded native command buffer.
|
|
8
|
+
# @param handle [FFI::Pointer] native command buffer handle
|
|
9
|
+
# @param device [Device, nil] device whose callbacks the command buffer may use
|
|
10
|
+
def initialize(handle, device: nil)
|
|
8
11
|
@handle = handle
|
|
12
|
+
@device = device
|
|
13
|
+
@submitted = false
|
|
9
14
|
end
|
|
10
15
|
|
|
16
|
+
# Reports whether this command buffer has been submitted.
|
|
17
|
+
# @return [Boolean]
|
|
18
|
+
def submitted?
|
|
19
|
+
@submitted
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Marks this command buffer as submitted.
|
|
23
|
+
# @raise [CommandError] if it was already submitted
|
|
24
|
+
# @return [void]
|
|
25
|
+
def mark_submitted!
|
|
26
|
+
raise CommandError, "Command buffer has already been submitted" if @submitted
|
|
27
|
+
|
|
28
|
+
@submitted = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Releases the native command buffer handle.
|
|
32
|
+
#
|
|
33
|
+
# Calling this method more than once has no effect.
|
|
34
|
+
# @return [void]
|
|
11
35
|
def release
|
|
12
36
|
return if @handle.null?
|
|
13
37
|
Native.wgpuCommandBufferRelease(@handle)
|
|
@@ -4,9 +4,14 @@ module WGPU
|
|
|
4
4
|
class CommandEncoder
|
|
5
5
|
attr_reader :handle
|
|
6
6
|
|
|
7
|
+
# Creates an encoder for commands submitted to a device queue.
|
|
8
|
+
# @param device [Device] owning device
|
|
9
|
+
# @param label [String, nil] optional debug label
|
|
10
|
+
# @raise [CommandError] if the native encoder cannot be created
|
|
7
11
|
def initialize(device, label: nil)
|
|
8
12
|
@device = device
|
|
9
13
|
@finished = false
|
|
14
|
+
@active_pass = nil
|
|
10
15
|
|
|
11
16
|
desc = Native::CommandEncoderDescriptor.new
|
|
12
17
|
desc[:next_in_chain] = nil
|
|
@@ -23,14 +28,36 @@ module WGPU
|
|
|
23
28
|
raise CommandError, "Failed to create command encoder" if @handle.null?
|
|
24
29
|
end
|
|
25
30
|
|
|
31
|
+
# Begins a compute pass, optionally yielding it for scoped recording.
|
|
32
|
+
# @param label [String, nil] optional debug label
|
|
33
|
+
# @param timestamp_writes [Hash, nil] timestamp query settings
|
|
34
|
+
# @yieldparam pass [ComputePass] newly created pass
|
|
35
|
+
# @return [ComputePass, Object] pass without a block, otherwise the block result
|
|
26
36
|
def begin_compute_pass(label: nil, timestamp_writes: nil)
|
|
27
|
-
|
|
28
|
-
ComputePass.new(self, label: label, timestamp_writes: timestamp_writes)
|
|
37
|
+
ensure_can_begin_pass!
|
|
38
|
+
pass = ComputePass.new(self, label: label, timestamp_writes: timestamp_writes)
|
|
39
|
+
@active_pass = pass
|
|
40
|
+
return pass unless block_given?
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
yield pass
|
|
44
|
+
ensure
|
|
45
|
+
begin
|
|
46
|
+
pass.end_pass unless pass.ended?
|
|
47
|
+
ensure
|
|
48
|
+
pass.release
|
|
49
|
+
pass_ended(pass)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
29
52
|
end
|
|
30
53
|
|
|
54
|
+
# Begins a render pass, optionally yielding it for scoped recording.
|
|
55
|
+
# @param color_attachments [Array<Hash>] color attachment descriptors
|
|
56
|
+
# @yieldparam pass [RenderPass] newly created pass
|
|
57
|
+
# @return [RenderPass, Object] pass without a block, otherwise the block result
|
|
31
58
|
def begin_render_pass(color_attachments:, depth_stencil_attachment: nil, occlusion_query_set: nil, timestamp_writes: nil, max_draw_count: nil, label: nil)
|
|
32
|
-
|
|
33
|
-
RenderPass.new(self,
|
|
59
|
+
ensure_can_begin_pass!
|
|
60
|
+
pass = RenderPass.new(self,
|
|
34
61
|
color_attachments: color_attachments,
|
|
35
62
|
depth_stencil_attachment: depth_stencil_attachment,
|
|
36
63
|
occlusion_query_set: occlusion_query_set,
|
|
@@ -38,8 +65,23 @@ module WGPU
|
|
|
38
65
|
max_draw_count: max_draw_count,
|
|
39
66
|
label: label
|
|
40
67
|
)
|
|
68
|
+
@active_pass = pass
|
|
69
|
+
return pass unless block_given?
|
|
70
|
+
|
|
71
|
+
begin
|
|
72
|
+
yield pass
|
|
73
|
+
ensure
|
|
74
|
+
begin
|
|
75
|
+
pass.end_pass unless pass.ended?
|
|
76
|
+
ensure
|
|
77
|
+
pass.release
|
|
78
|
+
pass_ended(pass)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
41
81
|
end
|
|
42
82
|
|
|
83
|
+
# Copies bytes between buffers.
|
|
84
|
+
# @return [void]
|
|
43
85
|
def copy_buffer_to_buffer(source:, source_offset: 0, destination:, destination_offset: 0, size:)
|
|
44
86
|
raise CommandError, "Encoder already finished" if @finished
|
|
45
87
|
Native.wgpuCommandEncoderCopyBufferToBuffer(
|
|
@@ -50,6 +92,11 @@ module WGPU
|
|
|
50
92
|
)
|
|
51
93
|
end
|
|
52
94
|
|
|
95
|
+
# Copies buffer data into a texture region.
|
|
96
|
+
# @param source [Hash] buffer and layout descriptor
|
|
97
|
+
# @param destination [Hash] texture and origin descriptor
|
|
98
|
+
# @param copy_size [Hash, Array] extent to copy
|
|
99
|
+
# @return [void]
|
|
53
100
|
def copy_buffer_to_texture(source:, destination:, copy_size:)
|
|
54
101
|
raise CommandError, "Encoder already finished" if @finished
|
|
55
102
|
|
|
@@ -70,11 +117,20 @@ module WGPU
|
|
|
70
117
|
dst[:origin][:x] = destination.dig(:origin, :x) || 0
|
|
71
118
|
dst[:origin][:y] = destination.dig(:origin, :y) || 0
|
|
72
119
|
dst[:origin][:z] = destination.dig(:origin, :z) || 0
|
|
73
|
-
dst[:aspect] =
|
|
120
|
+
dst[:aspect] = Native::EnumHelper.coerce(
|
|
121
|
+
Native::TextureAspect,
|
|
122
|
+
destination[:aspect] || :all,
|
|
123
|
+
name: "texture aspect"
|
|
124
|
+
)
|
|
74
125
|
|
|
75
126
|
Native.wgpuCommandEncoderCopyBufferToTexture(@handle, src, dst, size)
|
|
76
127
|
end
|
|
77
128
|
|
|
129
|
+
# Copies a texture region into a buffer.
|
|
130
|
+
# @param source [Hash] texture and origin descriptor
|
|
131
|
+
# @param destination [Hash] buffer and layout descriptor
|
|
132
|
+
# @param copy_size [Hash, Array] extent to copy
|
|
133
|
+
# @return [void]
|
|
78
134
|
def copy_texture_to_buffer(source:, destination:, copy_size:)
|
|
79
135
|
raise CommandError, "Encoder already finished" if @finished
|
|
80
136
|
|
|
@@ -89,7 +145,11 @@ module WGPU
|
|
|
89
145
|
src[:origin][:x] = source.dig(:origin, :x) || 0
|
|
90
146
|
src[:origin][:y] = source.dig(:origin, :y) || 0
|
|
91
147
|
src[:origin][:z] = source.dig(:origin, :z) || 0
|
|
92
|
-
src[:aspect] =
|
|
148
|
+
src[:aspect] = Native::EnumHelper.coerce(
|
|
149
|
+
Native::TextureAspect,
|
|
150
|
+
source[:aspect] || :all,
|
|
151
|
+
name: "texture aspect"
|
|
152
|
+
)
|
|
93
153
|
|
|
94
154
|
dst = Native::ImageCopyBuffer.new
|
|
95
155
|
dst[:layout][:offset] = destination[:offset] || 0
|
|
@@ -100,6 +160,11 @@ module WGPU
|
|
|
100
160
|
Native.wgpuCommandEncoderCopyTextureToBuffer(@handle, src, dst, size)
|
|
101
161
|
end
|
|
102
162
|
|
|
163
|
+
# Copies one texture region into another texture.
|
|
164
|
+
# @param source [Hash] source texture and origin descriptor
|
|
165
|
+
# @param destination [Hash] destination texture and origin descriptor
|
|
166
|
+
# @param copy_size [Hash, Array] extent to copy
|
|
167
|
+
# @return [void]
|
|
103
168
|
def copy_texture_to_texture(source:, destination:, copy_size:)
|
|
104
169
|
raise CommandError, "Encoder already finished" if @finished
|
|
105
170
|
|
|
@@ -109,7 +174,11 @@ module WGPU
|
|
|
109
174
|
src[:origin][:x] = source.dig(:origin, :x) || 0
|
|
110
175
|
src[:origin][:y] = source.dig(:origin, :y) || 0
|
|
111
176
|
src[:origin][:z] = source.dig(:origin, :z) || 0
|
|
112
|
-
src[:aspect] =
|
|
177
|
+
src[:aspect] = Native::EnumHelper.coerce(
|
|
178
|
+
Native::TextureAspect,
|
|
179
|
+
source[:aspect] || :all,
|
|
180
|
+
name: "source texture aspect"
|
|
181
|
+
)
|
|
113
182
|
|
|
114
183
|
dst = Native::ImageCopyTexture.new
|
|
115
184
|
dst[:texture] = destination[:texture].handle
|
|
@@ -117,7 +186,11 @@ module WGPU
|
|
|
117
186
|
dst[:origin][:x] = destination.dig(:origin, :x) || 0
|
|
118
187
|
dst[:origin][:y] = destination.dig(:origin, :y) || 0
|
|
119
188
|
dst[:origin][:z] = destination.dig(:origin, :z) || 0
|
|
120
|
-
dst[:aspect] =
|
|
189
|
+
dst[:aspect] = Native::EnumHelper.coerce(
|
|
190
|
+
Native::TextureAspect,
|
|
191
|
+
destination[:aspect] || :all,
|
|
192
|
+
name: "destination texture aspect"
|
|
193
|
+
)
|
|
121
194
|
|
|
122
195
|
size = Native::Extent3D.new
|
|
123
196
|
size[:width] = copy_size[:width] || copy_size[0]
|
|
@@ -127,6 +200,8 @@ module WGPU
|
|
|
127
200
|
Native.wgpuCommandEncoderCopyTextureToTexture(@handle, src, dst, size)
|
|
128
201
|
end
|
|
129
202
|
|
|
203
|
+
# Resolves query results into a destination buffer.
|
|
204
|
+
# @return [void]
|
|
130
205
|
def resolve_query_set(query_set:, first_query:, query_count:, destination:, destination_offset:)
|
|
131
206
|
raise CommandError, "Encoder already finished" if @finished
|
|
132
207
|
Native.wgpuCommandEncoderResolveQuerySet(
|
|
@@ -139,17 +214,29 @@ module WGPU
|
|
|
139
214
|
)
|
|
140
215
|
end
|
|
141
216
|
|
|
217
|
+
# Clears a byte range in a buffer to zero.
|
|
218
|
+
# @param buffer [Buffer] buffer to clear
|
|
219
|
+
# @param offset [Integer] first byte to clear
|
|
220
|
+
# @param size [Integer, nil] number of bytes to clear
|
|
221
|
+
# @return [void]
|
|
142
222
|
def clear_buffer(buffer, offset: 0, size: nil)
|
|
143
223
|
raise CommandError, "Encoder already finished" if @finished
|
|
144
224
|
size ||= buffer.size - offset
|
|
145
225
|
Native.wgpuCommandEncoderClearBuffer(@handle, buffer.handle, offset, size)
|
|
146
226
|
end
|
|
147
227
|
|
|
228
|
+
# Writes a GPU timestamp to a query set.
|
|
229
|
+
# @param query_set [QuerySet] timestamp query set
|
|
230
|
+
# @param query_index [Integer] destination query index
|
|
231
|
+
# @return [void]
|
|
148
232
|
def write_timestamp(query_set, query_index)
|
|
149
233
|
raise CommandError, "Encoder already finished" if @finished
|
|
150
234
|
Native.wgpuCommandEncoderWriteTimestamp(@handle, query_set.handle, query_index)
|
|
151
235
|
end
|
|
152
236
|
|
|
237
|
+
# Starts a labeled group in GPU debugging tools.
|
|
238
|
+
# @param label [String] group label
|
|
239
|
+
# @return [void]
|
|
153
240
|
def push_debug_group(label)
|
|
154
241
|
raise CommandError, "Encoder already finished" if @finished
|
|
155
242
|
label_view = Native::StringView.new
|
|
@@ -159,11 +246,16 @@ module WGPU
|
|
|
159
246
|
Native.wgpuCommandEncoderPushDebugGroup(@handle, label_view)
|
|
160
247
|
end
|
|
161
248
|
|
|
249
|
+
# Ends the most recently pushed debug group.
|
|
250
|
+
# @return [void]
|
|
162
251
|
def pop_debug_group
|
|
163
252
|
raise CommandError, "Encoder already finished" if @finished
|
|
164
253
|
Native.wgpuCommandEncoderPopDebugGroup(@handle)
|
|
165
254
|
end
|
|
166
255
|
|
|
256
|
+
# Inserts a labeled point in GPU debugging tools.
|
|
257
|
+
# @param label [String] marker label
|
|
258
|
+
# @return [void]
|
|
167
259
|
def insert_debug_marker(label)
|
|
168
260
|
raise CommandError, "Encoder already finished" if @finished
|
|
169
261
|
label_view = Native::StringView.new
|
|
@@ -173,8 +265,15 @@ module WGPU
|
|
|
173
265
|
Native.wgpuCommandEncoderInsertDebugMarker(@handle, label_view)
|
|
174
266
|
end
|
|
175
267
|
|
|
268
|
+
# Finishes recording and returns a command buffer.
|
|
269
|
+
# @param label [String, nil] optional command buffer label
|
|
270
|
+
# @return [CommandBuffer]
|
|
271
|
+
# @raise [CommandError] if the encoder is finished, has an active pass, or native creation fails
|
|
176
272
|
def finish(label: nil)
|
|
177
273
|
raise CommandError, "Encoder already finished" if @finished
|
|
274
|
+
if @active_pass && !@active_pass.ended?
|
|
275
|
+
raise CommandError, "Cannot finish command encoder while a pass is active"
|
|
276
|
+
end
|
|
178
277
|
@finished = true
|
|
179
278
|
|
|
180
279
|
desc = nil
|
|
@@ -189,13 +288,28 @@ module WGPU
|
|
|
189
288
|
buffer_handle = Native.wgpuCommandEncoderFinish(@handle, desc)
|
|
190
289
|
raise CommandError, "Failed to finish command encoder" if buffer_handle.null?
|
|
191
290
|
|
|
192
|
-
CommandBuffer.new(buffer_handle)
|
|
291
|
+
CommandBuffer.new(buffer_handle, device: @device)
|
|
193
292
|
end
|
|
194
293
|
|
|
294
|
+
# Releases the native command encoder handle.
|
|
295
|
+
#
|
|
296
|
+
# Calling this method more than once has no effect.
|
|
297
|
+
# @return [void]
|
|
195
298
|
def release
|
|
196
299
|
return if @handle.null?
|
|
197
300
|
Native.wgpuCommandEncoderRelease(@handle)
|
|
198
301
|
@handle = FFI::Pointer::NULL
|
|
199
302
|
end
|
|
303
|
+
|
|
304
|
+
private
|
|
305
|
+
|
|
306
|
+
def ensure_can_begin_pass!
|
|
307
|
+
raise CommandError, "Encoder already finished" if @finished
|
|
308
|
+
raise CommandError, "A command pass is already active" if @active_pass && !@active_pass.ended?
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def pass_ended(pass)
|
|
312
|
+
@active_pass = nil if @active_pass.equal?(pass)
|
|
313
|
+
end
|
|
200
314
|
end
|
|
201
315
|
end
|
|
@@ -4,7 +4,14 @@ module WGPU
|
|
|
4
4
|
class ComputePass
|
|
5
5
|
attr_reader :handle
|
|
6
6
|
|
|
7
|
+
# Begins a compute pass owned by the command encoder.
|
|
8
|
+
# @param encoder [CommandEncoder] owning encoder
|
|
9
|
+
# @param label [String, nil] optional debug label
|
|
10
|
+
# @param timestamp_writes [Hash, nil] beginning and ending timestamp query settings
|
|
11
|
+
# @raise [CommandError] if the native pass cannot be created
|
|
7
12
|
def initialize(encoder, label: nil, timestamp_writes: nil)
|
|
13
|
+
@encoder = encoder
|
|
14
|
+
@ended = false
|
|
8
15
|
desc = Native::ComputePassDescriptor.new
|
|
9
16
|
desc[:next_in_chain] = nil
|
|
10
17
|
if label
|
|
@@ -30,10 +37,18 @@ module WGPU
|
|
|
30
37
|
raise CommandError, "Failed to begin compute pass" if @handle.null?
|
|
31
38
|
end
|
|
32
39
|
|
|
40
|
+
# Selects the compute pipeline used by subsequent dispatches.
|
|
41
|
+
# @param pipeline [ComputePipeline] pipeline to bind
|
|
42
|
+
# @return [void]
|
|
33
43
|
def set_pipeline(pipeline)
|
|
34
44
|
Native.wgpuComputePassEncoderSetPipeline(@handle, pipeline.handle)
|
|
35
45
|
end
|
|
36
46
|
|
|
47
|
+
# Binds a resource group for subsequent dispatches.
|
|
48
|
+
# @param index [Integer] bind group index
|
|
49
|
+
# @param bind_group [BindGroup] group to bind
|
|
50
|
+
# @param dynamic_offsets [Array<Integer>] dynamic buffer offsets
|
|
51
|
+
# @return [void]
|
|
37
52
|
def set_bind_group(index, bind_group, dynamic_offsets: [])
|
|
38
53
|
if dynamic_offsets.empty?
|
|
39
54
|
Native.wgpuComputePassEncoderSetBindGroup(@handle, index, bind_group.handle, 0, nil)
|
|
@@ -44,14 +59,27 @@ module WGPU
|
|
|
44
59
|
end
|
|
45
60
|
end
|
|
46
61
|
|
|
62
|
+
# Dispatches a three-dimensional compute workgroup grid.
|
|
63
|
+
# @param x [Integer] workgroup count on the x axis
|
|
64
|
+
# @param y [Integer] workgroup count on the y axis
|
|
65
|
+
# @param z [Integer] workgroup count on the z axis
|
|
66
|
+
# @return [void]
|
|
47
67
|
def dispatch_workgroups(x, y = 1, z = 1)
|
|
48
68
|
Native.wgpuComputePassEncoderDispatchWorkgroups(@handle, x, y, z)
|
|
49
69
|
end
|
|
50
70
|
|
|
71
|
+
# Dispatches workgroups using arguments read from a buffer.
|
|
72
|
+
# @param buffer [Buffer] indirect argument buffer
|
|
73
|
+
# @param offset [Integer] byte offset of the arguments
|
|
74
|
+
# @return [void]
|
|
51
75
|
def dispatch_workgroups_indirect(buffer, offset: 0)
|
|
52
76
|
Native.wgpuComputePassEncoderDispatchWorkgroupsIndirect(@handle, buffer.handle, offset)
|
|
53
77
|
end
|
|
54
78
|
|
|
79
|
+
# Starts a labeled group in GPU debugging tools.
|
|
80
|
+
#
|
|
81
|
+
# @param label [String] group label
|
|
82
|
+
# @return [void]
|
|
55
83
|
def push_debug_group(label)
|
|
56
84
|
label_view = Native::StringView.new
|
|
57
85
|
label_ptr = FFI::MemoryPointer.from_string(label)
|
|
@@ -60,10 +88,17 @@ module WGPU
|
|
|
60
88
|
Native.wgpuComputePassEncoderPushDebugGroup(@handle, label_view)
|
|
61
89
|
end
|
|
62
90
|
|
|
91
|
+
# Ends the most recently pushed debug group.
|
|
92
|
+
#
|
|
93
|
+
# @return [void]
|
|
63
94
|
def pop_debug_group
|
|
64
95
|
Native.wgpuComputePassEncoderPopDebugGroup(@handle)
|
|
65
96
|
end
|
|
66
97
|
|
|
98
|
+
# Inserts a labeled point in GPU debugging tools.
|
|
99
|
+
#
|
|
100
|
+
# @param label [String] marker label
|
|
101
|
+
# @return [void]
|
|
67
102
|
def insert_debug_marker(label)
|
|
68
103
|
label_view = Native::StringView.new
|
|
69
104
|
label_ptr = FFI::MemoryPointer.from_string(label)
|
|
@@ -72,14 +107,32 @@ module WGPU
|
|
|
72
107
|
Native.wgpuComputePassEncoderInsertDebugMarker(@handle, label_view)
|
|
73
108
|
end
|
|
74
109
|
|
|
110
|
+
# Ends the pass if it has not already ended.
|
|
111
|
+
# @return [void]
|
|
75
112
|
def end_pass
|
|
113
|
+
return if @ended
|
|
114
|
+
|
|
76
115
|
Native.wgpuComputePassEncoderEnd(@handle)
|
|
116
|
+
@ended = true
|
|
117
|
+
@encoder.send(:pass_ended, self)
|
|
77
118
|
end
|
|
78
119
|
|
|
120
|
+
# Ends the pass.
|
|
121
|
+
# @return [void]
|
|
79
122
|
def end
|
|
80
123
|
end_pass
|
|
81
124
|
end
|
|
82
125
|
|
|
126
|
+
# Reports whether the pass has ended.
|
|
127
|
+
# @return [Boolean]
|
|
128
|
+
def ended?
|
|
129
|
+
@ended
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Releases the native compute pass encoder handle.
|
|
133
|
+
#
|
|
134
|
+
# Calling this method more than once has no effect.
|
|
135
|
+
# @return [void]
|
|
83
136
|
def release
|
|
84
137
|
return if @handle.null?
|
|
85
138
|
Native.wgpuComputePassEncoderRelease(@handle)
|
|
@@ -4,10 +4,18 @@ module WGPU
|
|
|
4
4
|
class RenderBundle
|
|
5
5
|
attr_reader :handle
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Wraps a reusable native render bundle.
|
|
8
|
+
# @param handle [FFI::Pointer] native render bundle handle
|
|
9
|
+
# @param device [Device, nil] device whose callbacks the render bundle may use
|
|
10
|
+
def initialize(handle, device: nil)
|
|
8
11
|
@handle = handle
|
|
12
|
+
@device = device
|
|
9
13
|
end
|
|
10
14
|
|
|
15
|
+
# Releases the native render bundle handle.
|
|
16
|
+
#
|
|
17
|
+
# Calling this method more than once has no effect.
|
|
18
|
+
# @return [void]
|
|
11
19
|
def release
|
|
12
20
|
return if @handle.null?
|
|
13
21
|
|
|
@@ -4,6 +4,15 @@ module WGPU
|
|
|
4
4
|
class RenderBundleEncoder
|
|
5
5
|
attr_reader :handle
|
|
6
6
|
|
|
7
|
+
# Creates an encoder for reusable render commands.
|
|
8
|
+
# @param device [Device] owning device
|
|
9
|
+
# @param color_formats [Array<Symbol, Integer>] color attachment formats
|
|
10
|
+
# @param depth_stencil_format [Symbol, Integer, nil] optional depth/stencil format
|
|
11
|
+
# @param sample_count [Integer] multisample count
|
|
12
|
+
# @param depth_read_only [Boolean] whether depth writes are disabled
|
|
13
|
+
# @param stencil_read_only [Boolean] whether stencil writes are disabled
|
|
14
|
+
# @param label [String, nil] optional debug label
|
|
15
|
+
# @raise [RenderBundleError] if the native encoder cannot be created
|
|
7
16
|
def initialize(device, color_formats:, depth_stencil_format: nil, sample_count: 1,
|
|
8
17
|
depth_read_only: false, stencil_read_only: false, label: nil)
|
|
9
18
|
@device = device
|
|
@@ -21,13 +30,19 @@ module WGPU
|
|
|
21
30
|
desc[:label][:length] = 0
|
|
22
31
|
end
|
|
23
32
|
|
|
24
|
-
formats = Array(color_formats).map
|
|
33
|
+
formats = Array(color_formats).map do |format|
|
|
34
|
+
Native::EnumHelper.coerce(Native::TextureFormat, format, name: "color format")
|
|
35
|
+
end
|
|
25
36
|
@formats_ptr = FFI::MemoryPointer.new(:uint32, formats.size)
|
|
26
37
|
@formats_ptr.write_array_of_uint32(formats)
|
|
27
38
|
desc[:color_format_count] = formats.size
|
|
28
39
|
desc[:color_formats] = @formats_ptr
|
|
29
40
|
|
|
30
|
-
desc[:depth_stencil_format] =
|
|
41
|
+
desc[:depth_stencil_format] = Native::EnumHelper.coerce(
|
|
42
|
+
Native::TextureFormat,
|
|
43
|
+
depth_stencil_format || :undefined,
|
|
44
|
+
name: "depth stencil format"
|
|
45
|
+
)
|
|
31
46
|
desc[:sample_count] = sample_count
|
|
32
47
|
desc[:depth_read_only] = depth_read_only ? 1 : 0
|
|
33
48
|
desc[:stencil_read_only] = stencil_read_only ? 1 : 0
|
|
@@ -36,12 +51,21 @@ module WGPU
|
|
|
36
51
|
raise RenderBundleError, "Failed to create render bundle encoder" if @handle.null?
|
|
37
52
|
end
|
|
38
53
|
|
|
54
|
+
# Selects the pipeline used by subsequent bundle draws.
|
|
55
|
+
# @param pipeline [RenderPipeline] pipeline to bind
|
|
56
|
+
# @raise [RenderBundleError] if the encoder is finished
|
|
57
|
+
# @return [void]
|
|
39
58
|
def set_pipeline(pipeline)
|
|
40
59
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
41
60
|
|
|
42
61
|
Native.wgpuRenderBundleEncoderSetPipeline(@handle, pipeline.handle)
|
|
43
62
|
end
|
|
44
63
|
|
|
64
|
+
# Binds a resource group for subsequent bundle draws.
|
|
65
|
+
# @param index [Integer] bind group index
|
|
66
|
+
# @param bind_group [BindGroup] group to bind
|
|
67
|
+
# @param dynamic_offsets [Array<Integer>, nil] dynamic buffer offsets
|
|
68
|
+
# @return [void]
|
|
45
69
|
def set_bind_group(index, bind_group, dynamic_offsets: nil)
|
|
46
70
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
47
71
|
|
|
@@ -54,6 +78,10 @@ module WGPU
|
|
|
54
78
|
end
|
|
55
79
|
end
|
|
56
80
|
|
|
81
|
+
# Binds a vertex buffer to a slot.
|
|
82
|
+
# @param slot [Integer] vertex buffer slot
|
|
83
|
+
# @param buffer [Buffer] vertex data buffer
|
|
84
|
+
# @return [void]
|
|
57
85
|
def set_vertex_buffer(slot, buffer, offset: 0, size: nil)
|
|
58
86
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
59
87
|
|
|
@@ -61,37 +89,57 @@ module WGPU
|
|
|
61
89
|
Native.wgpuRenderBundleEncoderSetVertexBuffer(@handle, slot, buffer.handle, offset, size)
|
|
62
90
|
end
|
|
63
91
|
|
|
92
|
+
# Binds an index buffer for indexed bundle draws.
|
|
93
|
+
# @param buffer [Buffer] index data buffer
|
|
94
|
+
# @param format [Symbol, Integer] index element format
|
|
95
|
+
# @return [void]
|
|
64
96
|
def set_index_buffer(buffer, format: :uint32, offset: 0, size: nil)
|
|
65
97
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
66
98
|
|
|
67
99
|
size ||= buffer.size - offset
|
|
68
|
-
Native.
|
|
100
|
+
format_value = Native::EnumHelper.coerce(Native::IndexFormat, format, name: "index format")
|
|
101
|
+
Native.wgpuRenderBundleEncoderSetIndexBuffer(@handle, buffer.handle, format_value, offset, size)
|
|
69
102
|
end
|
|
70
103
|
|
|
104
|
+
# Records a non-indexed draw in the bundle.
|
|
105
|
+
# @return [void]
|
|
71
106
|
def draw(vertex_count, instance_count: 1, first_vertex: 0, first_instance: 0)
|
|
72
107
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
73
108
|
|
|
74
109
|
Native.wgpuRenderBundleEncoderDraw(@handle, vertex_count, instance_count, first_vertex, first_instance)
|
|
75
110
|
end
|
|
76
111
|
|
|
112
|
+
# Records an indexed draw in the bundle.
|
|
113
|
+
# @return [void]
|
|
77
114
|
def draw_indexed(index_count, instance_count: 1, first_index: 0, base_vertex: 0, first_instance: 0)
|
|
78
115
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
79
116
|
|
|
80
117
|
Native.wgpuRenderBundleEncoderDrawIndexed(@handle, index_count, instance_count, first_index, base_vertex, first_instance)
|
|
81
118
|
end
|
|
82
119
|
|
|
120
|
+
# Records a non-indexed draw using buffer arguments.
|
|
121
|
+
# @param buffer [Buffer] indirect argument buffer
|
|
122
|
+
# @param offset [Integer] byte offset of the arguments
|
|
123
|
+
# @return [void]
|
|
83
124
|
def draw_indirect(buffer, offset: 0)
|
|
84
125
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
85
126
|
|
|
86
127
|
Native.wgpuRenderBundleEncoderDrawIndirect(@handle, buffer.handle, offset)
|
|
87
128
|
end
|
|
88
129
|
|
|
130
|
+
# Records an indexed draw using buffer arguments.
|
|
131
|
+
# @param buffer [Buffer] indirect argument buffer
|
|
132
|
+
# @param offset [Integer] byte offset of the arguments
|
|
133
|
+
# @return [void]
|
|
89
134
|
def draw_indexed_indirect(buffer, offset: 0)
|
|
90
135
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
91
136
|
|
|
92
137
|
Native.wgpuRenderBundleEncoderDrawIndexedIndirect(@handle, buffer.handle, offset)
|
|
93
138
|
end
|
|
94
139
|
|
|
140
|
+
# Starts a labeled group in GPU debugging tools.
|
|
141
|
+
# @param label [String] group label
|
|
142
|
+
# @return [void]
|
|
95
143
|
def push_debug_group(label)
|
|
96
144
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
97
145
|
|
|
@@ -102,12 +150,17 @@ module WGPU
|
|
|
102
150
|
Native.wgpuRenderBundleEncoderPushDebugGroup(@handle, label_view)
|
|
103
151
|
end
|
|
104
152
|
|
|
153
|
+
# Ends the most recently pushed debug group.
|
|
154
|
+
# @return [void]
|
|
105
155
|
def pop_debug_group
|
|
106
156
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
107
157
|
|
|
108
158
|
Native.wgpuRenderBundleEncoderPopDebugGroup(@handle)
|
|
109
159
|
end
|
|
110
160
|
|
|
161
|
+
# Inserts a labeled point in GPU debugging tools.
|
|
162
|
+
# @param label [String] marker label
|
|
163
|
+
# @return [void]
|
|
111
164
|
def insert_debug_marker(label)
|
|
112
165
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
113
166
|
|
|
@@ -118,6 +171,10 @@ module WGPU
|
|
|
118
171
|
Native.wgpuRenderBundleEncoderInsertDebugMarker(@handle, label_view)
|
|
119
172
|
end
|
|
120
173
|
|
|
174
|
+
# Finishes recording and creates an immutable render bundle.
|
|
175
|
+
# @param label [String, nil] optional bundle label
|
|
176
|
+
# @return [RenderBundle]
|
|
177
|
+
# @raise [RenderBundleError] if already finished or native creation fails
|
|
121
178
|
def finish(label: nil)
|
|
122
179
|
raise RenderBundleError, "Encoder already finished" if @finished
|
|
123
180
|
|
|
@@ -135,9 +192,13 @@ module WGPU
|
|
|
135
192
|
bundle_handle = Native.wgpuRenderBundleEncoderFinish(@handle, desc)
|
|
136
193
|
raise RenderBundleError, "Failed to finish render bundle encoder" if bundle_handle.null?
|
|
137
194
|
|
|
138
|
-
RenderBundle.new(bundle_handle)
|
|
195
|
+
RenderBundle.new(bundle_handle, device: @device)
|
|
139
196
|
end
|
|
140
197
|
|
|
198
|
+
# Releases the native render bundle encoder handle.
|
|
199
|
+
#
|
|
200
|
+
# Calling this method more than once has no effect.
|
|
201
|
+
# @return [void]
|
|
141
202
|
def release
|
|
142
203
|
return if @handle.null?
|
|
143
204
|
|