wgpu 0.1.0

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.
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WGPU
4
+ class CommandEncoder
5
+ attr_reader :handle
6
+
7
+ def initialize(device, label: nil)
8
+ @device = device
9
+ @finished = false
10
+
11
+ desc = Native::CommandEncoderDescriptor.new
12
+ desc[:next_in_chain] = nil
13
+ if label
14
+ label_ptr = FFI::MemoryPointer.from_string(label)
15
+ desc[:label][:data] = label_ptr
16
+ desc[:label][:length] = label.bytesize
17
+ else
18
+ desc[:label][:data] = nil
19
+ desc[:label][:length] = 0
20
+ end
21
+
22
+ @handle = Native.wgpuDeviceCreateCommandEncoder(device.handle, desc)
23
+ raise CommandError, "Failed to create command encoder" if @handle.null?
24
+ end
25
+
26
+ def begin_compute_pass(label: nil)
27
+ raise CommandError, "Encoder already finished" if @finished
28
+ ComputePass.new(self, label: label)
29
+ end
30
+
31
+ def begin_render_pass(color_attachments:, depth_stencil_attachment: nil, label: nil)
32
+ raise CommandError, "Encoder already finished" if @finished
33
+ RenderPass.new(self,
34
+ color_attachments: color_attachments,
35
+ depth_stencil_attachment: depth_stencil_attachment,
36
+ label: label
37
+ )
38
+ end
39
+
40
+ def copy_buffer_to_buffer(source:, source_offset: 0, destination:, destination_offset: 0, size:)
41
+ raise CommandError, "Encoder already finished" if @finished
42
+ Native.wgpuCommandEncoderCopyBufferToBuffer(
43
+ @handle,
44
+ source.handle, source_offset,
45
+ destination.handle, destination_offset,
46
+ size
47
+ )
48
+ end
49
+
50
+ def copy_buffer_to_texture(source:, destination:, copy_size:)
51
+ raise CommandError, "Encoder already finished" if @finished
52
+
53
+ size = Native::Extent3D.new
54
+ size[:width] = copy_size[:width] || copy_size[0]
55
+ size[:height] = copy_size[:height] || copy_size[1] || 1
56
+ size[:depth_or_array_layers] = copy_size[:depth_or_array_layers] || copy_size[2] || 1
57
+
58
+ src = Native::ImageCopyBuffer.new
59
+ src[:layout][:offset] = source[:offset] || 0
60
+ src[:layout][:bytes_per_row] = source[:bytes_per_row]
61
+ src[:layout][:rows_per_image] = source[:rows_per_image] || size[:height]
62
+ src[:buffer] = source[:buffer].handle
63
+
64
+ dst = Native::ImageCopyTexture.new
65
+ dst[:texture] = destination[:texture].handle
66
+ dst[:mip_level] = destination[:mip_level] || 0
67
+ dst[:origin][:x] = destination.dig(:origin, :x) || 0
68
+ dst[:origin][:y] = destination.dig(:origin, :y) || 0
69
+ dst[:origin][:z] = destination.dig(:origin, :z) || 0
70
+ dst[:aspect] = destination[:aspect] || :all
71
+
72
+ Native.wgpuCommandEncoderCopyBufferToTexture(@handle, src, dst, size)
73
+ end
74
+
75
+ def copy_texture_to_buffer(source:, destination:, copy_size:)
76
+ raise CommandError, "Encoder already finished" if @finished
77
+
78
+ size = Native::Extent3D.new
79
+ size[:width] = copy_size[:width] || copy_size[0]
80
+ size[:height] = copy_size[:height] || copy_size[1] || 1
81
+ size[:depth_or_array_layers] = copy_size[:depth_or_array_layers] || copy_size[2] || 1
82
+
83
+ src = Native::ImageCopyTexture.new
84
+ src[:texture] = source[:texture].handle
85
+ src[:mip_level] = source[:mip_level] || 0
86
+ src[:origin][:x] = source.dig(:origin, :x) || 0
87
+ src[:origin][:y] = source.dig(:origin, :y) || 0
88
+ src[:origin][:z] = source.dig(:origin, :z) || 0
89
+ src[:aspect] = source[:aspect] || :all
90
+
91
+ dst = Native::ImageCopyBuffer.new
92
+ dst[:layout][:offset] = destination[:offset] || 0
93
+ dst[:layout][:bytes_per_row] = destination[:bytes_per_row]
94
+ dst[:layout][:rows_per_image] = destination[:rows_per_image] || size[:height]
95
+ dst[:buffer] = destination[:buffer].handle
96
+
97
+ Native.wgpuCommandEncoderCopyTextureToBuffer(@handle, src, dst, size)
98
+ end
99
+
100
+ def copy_texture_to_texture(source:, destination:, copy_size:)
101
+ raise CommandError, "Encoder already finished" if @finished
102
+
103
+ src = Native::ImageCopyTexture.new
104
+ src[:texture] = source[:texture].handle
105
+ src[:mip_level] = source[:mip_level] || 0
106
+ src[:origin][:x] = source.dig(:origin, :x) || 0
107
+ src[:origin][:y] = source.dig(:origin, :y) || 0
108
+ src[:origin][:z] = source.dig(:origin, :z) || 0
109
+ src[:aspect] = source[:aspect] || :all
110
+
111
+ dst = Native::ImageCopyTexture.new
112
+ dst[:texture] = destination[:texture].handle
113
+ dst[:mip_level] = destination[:mip_level] || 0
114
+ dst[:origin][:x] = destination.dig(:origin, :x) || 0
115
+ dst[:origin][:y] = destination.dig(:origin, :y) || 0
116
+ dst[:origin][:z] = destination.dig(:origin, :z) || 0
117
+ dst[:aspect] = destination[:aspect] || :all
118
+
119
+ size = Native::Extent3D.new
120
+ size[:width] = copy_size[:width] || copy_size[0]
121
+ size[:height] = copy_size[:height] || copy_size[1] || 1
122
+ size[:depth_or_array_layers] = copy_size[:depth_or_array_layers] || copy_size[2] || 1
123
+
124
+ Native.wgpuCommandEncoderCopyTextureToTexture(@handle, src, dst, size)
125
+ end
126
+
127
+ def resolve_query_set(query_set:, first_query:, query_count:, destination:, destination_offset:)
128
+ raise CommandError, "Encoder already finished" if @finished
129
+ Native.wgpuCommandEncoderResolveQuerySet(
130
+ @handle,
131
+ query_set.handle,
132
+ first_query,
133
+ query_count,
134
+ destination.handle,
135
+ destination_offset
136
+ )
137
+ end
138
+
139
+ def clear_buffer(buffer, offset: 0, size: nil)
140
+ raise CommandError, "Encoder already finished" if @finished
141
+ size ||= buffer.size - offset
142
+ Native.wgpuCommandEncoderClearBuffer(@handle, buffer.handle, offset, size)
143
+ end
144
+
145
+ def write_timestamp(query_set, query_index)
146
+ raise CommandError, "Encoder already finished" if @finished
147
+ Native.wgpuCommandEncoderWriteTimestamp(@handle, query_set.handle, query_index)
148
+ end
149
+
150
+ def push_debug_group(label)
151
+ raise CommandError, "Encoder already finished" if @finished
152
+ label_view = Native::StringView.new
153
+ label_ptr = FFI::MemoryPointer.from_string(label)
154
+ label_view[:data] = label_ptr
155
+ label_view[:length] = label.bytesize
156
+ Native.wgpuCommandEncoderPushDebugGroup(@handle, label_view)
157
+ end
158
+
159
+ def pop_debug_group
160
+ raise CommandError, "Encoder already finished" if @finished
161
+ Native.wgpuCommandEncoderPopDebugGroup(@handle)
162
+ end
163
+
164
+ def insert_debug_marker(label)
165
+ raise CommandError, "Encoder already finished" if @finished
166
+ label_view = Native::StringView.new
167
+ label_ptr = FFI::MemoryPointer.from_string(label)
168
+ label_view[:data] = label_ptr
169
+ label_view[:length] = label.bytesize
170
+ Native.wgpuCommandEncoderInsertDebugMarker(@handle, label_view)
171
+ end
172
+
173
+ def finish(label: nil)
174
+ raise CommandError, "Encoder already finished" if @finished
175
+ @finished = true
176
+
177
+ buffer_handle = Native.wgpuCommandEncoderFinish(@handle, nil)
178
+ raise CommandError, "Failed to finish command encoder" if buffer_handle.null?
179
+
180
+ CommandBuffer.new(buffer_handle)
181
+ end
182
+
183
+ def release
184
+ return if @handle.null?
185
+ Native.wgpuCommandEncoderRelease(@handle)
186
+ @handle = FFI::Pointer::NULL
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WGPU
4
+ class ComputePass
5
+ attr_reader :handle
6
+
7
+ def initialize(encoder, label: nil)
8
+ desc = Native::ComputePassDescriptor.new
9
+ desc[:next_in_chain] = nil
10
+ if label
11
+ label_ptr = FFI::MemoryPointer.from_string(label)
12
+ desc[:label][:data] = label_ptr
13
+ desc[:label][:length] = label.bytesize
14
+ else
15
+ desc[:label][:data] = nil
16
+ desc[:label][:length] = 0
17
+ end
18
+ desc[:timestamp_writes] = nil
19
+
20
+ @handle = Native.wgpuCommandEncoderBeginComputePass(encoder.handle, desc)
21
+ raise CommandError, "Failed to begin compute pass" if @handle.null?
22
+ end
23
+
24
+ def set_pipeline(pipeline)
25
+ Native.wgpuComputePassEncoderSetPipeline(@handle, pipeline.handle)
26
+ end
27
+
28
+ def set_bind_group(index, bind_group, dynamic_offsets: [])
29
+ if dynamic_offsets.empty?
30
+ Native.wgpuComputePassEncoderSetBindGroup(@handle, index, bind_group.handle, 0, nil)
31
+ else
32
+ offsets_ptr = FFI::MemoryPointer.new(:uint32, dynamic_offsets.size)
33
+ offsets_ptr.write_array_of_uint32(dynamic_offsets)
34
+ Native.wgpuComputePassEncoderSetBindGroup(@handle, index, bind_group.handle, dynamic_offsets.size, offsets_ptr)
35
+ end
36
+ end
37
+
38
+ def dispatch_workgroups(x, y = 1, z = 1)
39
+ Native.wgpuComputePassEncoderDispatchWorkgroups(@handle, x, y, z)
40
+ end
41
+
42
+ def dispatch_workgroups_indirect(buffer, offset: 0)
43
+ Native.wgpuComputePassEncoderDispatchWorkgroupsIndirect(@handle, buffer.handle, offset)
44
+ end
45
+
46
+ def push_debug_group(label)
47
+ label_view = Native::StringView.new
48
+ label_ptr = FFI::MemoryPointer.from_string(label)
49
+ label_view[:data] = label_ptr
50
+ label_view[:length] = label.bytesize
51
+ Native.wgpuComputePassEncoderPushDebugGroup(@handle, label_view)
52
+ end
53
+
54
+ def pop_debug_group
55
+ Native.wgpuComputePassEncoderPopDebugGroup(@handle)
56
+ end
57
+
58
+ def insert_debug_marker(label)
59
+ label_view = Native::StringView.new
60
+ label_ptr = FFI::MemoryPointer.from_string(label)
61
+ label_view[:data] = label_ptr
62
+ label_view[:length] = label.bytesize
63
+ Native.wgpuComputePassEncoderInsertDebugMarker(@handle, label_view)
64
+ end
65
+
66
+ def end_pass
67
+ Native.wgpuComputePassEncoderEnd(@handle)
68
+ end
69
+
70
+ def release
71
+ return if @handle.null?
72
+ Native.wgpuComputePassEncoderRelease(@handle)
73
+ @handle = FFI::Pointer::NULL
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WGPU
4
+ class RenderBundle
5
+ attr_reader :handle
6
+
7
+ def initialize(handle)
8
+ @handle = handle
9
+ end
10
+
11
+ def release
12
+ return if @handle.null?
13
+
14
+ Native.wgpuRenderBundleRelease(@handle)
15
+ @handle = FFI::Pointer::NULL
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WGPU
4
+ class RenderBundleEncoder
5
+ attr_reader :handle
6
+
7
+ def initialize(device, color_formats:, depth_stencil_format: nil, sample_count: 1,
8
+ depth_read_only: false, stencil_read_only: false, label: nil)
9
+ @device = device
10
+ @finished = false
11
+
12
+ desc = Native::RenderBundleEncoderDescriptor.new
13
+ desc[:next_in_chain] = nil
14
+
15
+ if label
16
+ @label_ptr = FFI::MemoryPointer.from_string(label)
17
+ desc[:label][:data] = @label_ptr
18
+ desc[:label][:length] = label.bytesize
19
+ else
20
+ desc[:label][:data] = nil
21
+ desc[:label][:length] = 0
22
+ end
23
+
24
+ formats = Array(color_formats).map { |f| Native::TextureFormat[f] }
25
+ @formats_ptr = FFI::MemoryPointer.new(:uint32, formats.size)
26
+ @formats_ptr.write_array_of_uint32(formats)
27
+ desc[:color_format_count] = formats.size
28
+ desc[:color_formats] = @formats_ptr
29
+
30
+ desc[:depth_stencil_format] = depth_stencil_format || :undefined
31
+ desc[:sample_count] = sample_count
32
+ desc[:depth_read_only] = depth_read_only ? 1 : 0
33
+ desc[:stencil_read_only] = stencil_read_only ? 1 : 0
34
+
35
+ @handle = Native.wgpuDeviceCreateRenderBundleEncoder(device.handle, desc)
36
+ raise RenderBundleError, "Failed to create render bundle encoder" if @handle.null?
37
+ end
38
+
39
+ def set_pipeline(pipeline)
40
+ raise RenderBundleError, "Encoder already finished" if @finished
41
+
42
+ Native.wgpuRenderBundleEncoderSetPipeline(@handle, pipeline.handle)
43
+ end
44
+
45
+ def set_bind_group(index, bind_group, dynamic_offsets: nil)
46
+ raise RenderBundleError, "Encoder already finished" if @finished
47
+
48
+ if dynamic_offsets && !dynamic_offsets.empty?
49
+ offsets_ptr = FFI::MemoryPointer.new(:uint32, dynamic_offsets.size)
50
+ offsets_ptr.write_array_of_uint32(dynamic_offsets)
51
+ Native.wgpuRenderBundleEncoderSetBindGroup(@handle, index, bind_group.handle, dynamic_offsets.size, offsets_ptr)
52
+ else
53
+ Native.wgpuRenderBundleEncoderSetBindGroup(@handle, index, bind_group.handle, 0, nil)
54
+ end
55
+ end
56
+
57
+ def set_vertex_buffer(slot, buffer, offset: 0, size: nil)
58
+ raise RenderBundleError, "Encoder already finished" if @finished
59
+
60
+ size ||= buffer.size - offset
61
+ Native.wgpuRenderBundleEncoderSetVertexBuffer(@handle, slot, buffer.handle, offset, size)
62
+ end
63
+
64
+ def set_index_buffer(buffer, format: :uint32, offset: 0, size: nil)
65
+ raise RenderBundleError, "Encoder already finished" if @finished
66
+
67
+ size ||= buffer.size - offset
68
+ Native.wgpuRenderBundleEncoderSetIndexBuffer(@handle, buffer.handle, format, offset, size)
69
+ end
70
+
71
+ def draw(vertex_count, instance_count: 1, first_vertex: 0, first_instance: 0)
72
+ raise RenderBundleError, "Encoder already finished" if @finished
73
+
74
+ Native.wgpuRenderBundleEncoderDraw(@handle, vertex_count, instance_count, first_vertex, first_instance)
75
+ end
76
+
77
+ def draw_indexed(index_count, instance_count: 1, first_index: 0, base_vertex: 0, first_instance: 0)
78
+ raise RenderBundleError, "Encoder already finished" if @finished
79
+
80
+ Native.wgpuRenderBundleEncoderDrawIndexed(@handle, index_count, instance_count, first_index, base_vertex, first_instance)
81
+ end
82
+
83
+ def draw_indirect(buffer, offset: 0)
84
+ raise RenderBundleError, "Encoder already finished" if @finished
85
+
86
+ Native.wgpuRenderBundleEncoderDrawIndirect(@handle, buffer.handle, offset)
87
+ end
88
+
89
+ def draw_indexed_indirect(buffer, offset: 0)
90
+ raise RenderBundleError, "Encoder already finished" if @finished
91
+
92
+ Native.wgpuRenderBundleEncoderDrawIndexedIndirect(@handle, buffer.handle, offset)
93
+ end
94
+
95
+ def push_debug_group(label)
96
+ raise RenderBundleError, "Encoder already finished" if @finished
97
+
98
+ label_view = Native::StringView.new
99
+ label_ptr = FFI::MemoryPointer.from_string(label)
100
+ label_view[:data] = label_ptr
101
+ label_view[:length] = label.bytesize
102
+ Native.wgpuRenderBundleEncoderPushDebugGroup(@handle, label_view)
103
+ end
104
+
105
+ def pop_debug_group
106
+ raise RenderBundleError, "Encoder already finished" if @finished
107
+
108
+ Native.wgpuRenderBundleEncoderPopDebugGroup(@handle)
109
+ end
110
+
111
+ def insert_debug_marker(label)
112
+ raise RenderBundleError, "Encoder already finished" if @finished
113
+
114
+ label_view = Native::StringView.new
115
+ label_ptr = FFI::MemoryPointer.from_string(label)
116
+ label_view[:data] = label_ptr
117
+ label_view[:length] = label.bytesize
118
+ Native.wgpuRenderBundleEncoderInsertDebugMarker(@handle, label_view)
119
+ end
120
+
121
+ def finish(label: nil)
122
+ raise RenderBundleError, "Encoder already finished" if @finished
123
+
124
+ @finished = true
125
+
126
+ desc = nil
127
+ if label
128
+ desc = Native::RenderBundleDescriptor.new
129
+ desc[:next_in_chain] = nil
130
+ label_ptr = FFI::MemoryPointer.from_string(label)
131
+ desc[:label][:data] = label_ptr
132
+ desc[:label][:length] = label.bytesize
133
+ end
134
+
135
+ bundle_handle = Native.wgpuRenderBundleEncoderFinish(@handle, desc)
136
+ raise RenderBundleError, "Failed to finish render bundle encoder" if bundle_handle.null?
137
+
138
+ RenderBundle.new(bundle_handle)
139
+ end
140
+
141
+ def release
142
+ return if @handle.null?
143
+
144
+ Native.wgpuRenderBundleEncoderRelease(@handle)
145
+ @handle = FFI::Pointer::NULL
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WGPU
4
+ class RenderPass
5
+ attr_reader :handle
6
+
7
+ def initialize(encoder, label: nil, color_attachments:, depth_stencil_attachment: nil)
8
+ @encoder = encoder
9
+ @pointers = []
10
+
11
+ desc = Native::RenderPassDescriptor.new
12
+ desc[:next_in_chain] = nil
13
+ setup_label(desc, label)
14
+
15
+ color_attachments_ptr = setup_color_attachments(color_attachments)
16
+ desc[:color_attachment_count] = color_attachments.size
17
+ desc[:color_attachments] = color_attachments_ptr
18
+
19
+ if depth_stencil_attachment
20
+ ds_ptr = setup_depth_stencil_attachment(depth_stencil_attachment)
21
+ desc[:depth_stencil_attachment] = ds_ptr
22
+ else
23
+ desc[:depth_stencil_attachment] = nil
24
+ end
25
+
26
+ desc[:occlusion_query_set] = nil
27
+ desc[:timestamp_writes] = nil
28
+
29
+ @handle = Native.wgpuCommandEncoderBeginRenderPass(encoder.handle, desc)
30
+ raise CommandError, "Failed to begin render pass" if @handle.null?
31
+ end
32
+
33
+ def set_pipeline(pipeline)
34
+ Native.wgpuRenderPassEncoderSetPipeline(@handle, pipeline.handle)
35
+ end
36
+
37
+ def set_bind_group(index, bind_group, dynamic_offsets: [])
38
+ if dynamic_offsets.empty?
39
+ Native.wgpuRenderPassEncoderSetBindGroup(@handle, index, bind_group.handle, 0, nil)
40
+ else
41
+ offsets_ptr = FFI::MemoryPointer.new(:uint32, dynamic_offsets.size)
42
+ offsets_ptr.write_array_of_uint32(dynamic_offsets)
43
+ Native.wgpuRenderPassEncoderSetBindGroup(@handle, index, bind_group.handle, dynamic_offsets.size, offsets_ptr)
44
+ end
45
+ end
46
+
47
+ def set_vertex_buffer(slot, buffer, offset: 0, size: nil)
48
+ size ||= buffer.size - offset
49
+ Native.wgpuRenderPassEncoderSetVertexBuffer(@handle, slot, buffer.handle, offset, size)
50
+ end
51
+
52
+ def set_index_buffer(buffer, format, offset: 0, size: nil)
53
+ size ||= buffer.size - offset
54
+ Native.wgpuRenderPassEncoderSetIndexBuffer(@handle, buffer.handle, format, offset, size)
55
+ end
56
+
57
+ def draw(vertex_count, instance_count: 1, first_vertex: 0, first_instance: 0)
58
+ Native.wgpuRenderPassEncoderDraw(@handle, vertex_count, instance_count, first_vertex, first_instance)
59
+ end
60
+
61
+ def draw_indexed(index_count, instance_count: 1, first_index: 0, base_vertex: 0, first_instance: 0)
62
+ Native.wgpuRenderPassEncoderDrawIndexed(@handle, index_count, instance_count, first_index, base_vertex, first_instance)
63
+ end
64
+
65
+ def set_viewport(x, y, width, height, min_depth: 0.0, max_depth: 1.0)
66
+ Native.wgpuRenderPassEncoderSetViewport(@handle, x, y, width, height, min_depth, max_depth)
67
+ end
68
+
69
+ def set_scissor_rect(x, y, width, height)
70
+ Native.wgpuRenderPassEncoderSetScissorRect(@handle, x, y, width, height)
71
+ end
72
+
73
+ def set_blend_constant(r: 0.0, g: 0.0, b: 0.0, a: 1.0)
74
+ color = Native::Color.new
75
+ color[:r] = r
76
+ color[:g] = g
77
+ color[:b] = b
78
+ color[:a] = a
79
+ Native.wgpuRenderPassEncoderSetBlendConstant(@handle, color.to_ptr)
80
+ end
81
+
82
+ def set_stencil_reference(reference)
83
+ Native.wgpuRenderPassEncoderSetStencilReference(@handle, reference)
84
+ end
85
+
86
+ def draw_indirect(buffer, offset: 0)
87
+ Native.wgpuRenderPassEncoderDrawIndirect(@handle, buffer.handle, offset)
88
+ end
89
+
90
+ def draw_indexed_indirect(buffer, offset: 0)
91
+ Native.wgpuRenderPassEncoderDrawIndexedIndirect(@handle, buffer.handle, offset)
92
+ end
93
+
94
+ def execute_bundles(bundles)
95
+ bundle_handles = bundles.map(&:handle)
96
+ bundles_ptr = FFI::MemoryPointer.new(:pointer, bundle_handles.size)
97
+ bundles_ptr.write_array_of_pointer(bundle_handles)
98
+ Native.wgpuRenderPassEncoderExecuteBundles(@handle, bundle_handles.size, bundles_ptr)
99
+ end
100
+
101
+ def begin_occlusion_query(query_index)
102
+ Native.wgpuRenderPassEncoderBeginOcclusionQuery(@handle, query_index)
103
+ end
104
+
105
+ def end_occlusion_query
106
+ Native.wgpuRenderPassEncoderEndOcclusionQuery(@handle)
107
+ end
108
+
109
+ def push_debug_group(label)
110
+ label_view = Native::StringView.new
111
+ label_ptr = FFI::MemoryPointer.from_string(label)
112
+ label_view[:data] = label_ptr
113
+ label_view[:length] = label.bytesize
114
+ Native.wgpuRenderPassEncoderPushDebugGroup(@handle, label_view)
115
+ end
116
+
117
+ def pop_debug_group
118
+ Native.wgpuRenderPassEncoderPopDebugGroup(@handle)
119
+ end
120
+
121
+ def insert_debug_marker(label)
122
+ label_view = Native::StringView.new
123
+ label_ptr = FFI::MemoryPointer.from_string(label)
124
+ label_view[:data] = label_ptr
125
+ label_view[:length] = label.bytesize
126
+ Native.wgpuRenderPassEncoderInsertDebugMarker(@handle, label_view)
127
+ end
128
+
129
+ def end_pass
130
+ Native.wgpuRenderPassEncoderEnd(@handle)
131
+ end
132
+
133
+ def release
134
+ return if @handle.null?
135
+ Native.wgpuRenderPassEncoderRelease(@handle)
136
+ @handle = FFI::Pointer::NULL
137
+ end
138
+
139
+ private
140
+
141
+ def setup_label(desc, label)
142
+ if label
143
+ ptr = FFI::MemoryPointer.from_string(label)
144
+ @pointers << ptr
145
+ desc[:label][:data] = ptr
146
+ desc[:label][:length] = label.bytesize
147
+ else
148
+ desc[:label][:data] = nil
149
+ desc[:label][:length] = 0
150
+ end
151
+ end
152
+
153
+ def setup_color_attachments(attachments)
154
+ ptr = FFI::MemoryPointer.new(Native::RenderPassColorAttachment, attachments.size)
155
+ @pointers << ptr
156
+
157
+ attachments.each_with_index do |att, i|
158
+ ca = Native::RenderPassColorAttachment.new(ptr + i * Native::RenderPassColorAttachment.size)
159
+ ca[:next_in_chain] = nil
160
+ ca[:view] = att[:view].handle
161
+ ca[:depth_slice] = att[:depth_slice] || 0xFFFFFFFF
162
+ ca[:resolve_target] = att[:resolve_target]&.handle
163
+ ca[:load_op] = att[:load_op] || :clear
164
+ ca[:store_op] = att[:store_op] || :store
165
+
166
+ clear = att[:clear_value] || { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }
167
+ ca[:clear_value][:r] = clear[:r] || 0.0
168
+ ca[:clear_value][:g] = clear[:g] || 0.0
169
+ ca[:clear_value][:b] = clear[:b] || 0.0
170
+ ca[:clear_value][:a] = clear[:a] || 1.0
171
+ end
172
+
173
+ ptr
174
+ end
175
+
176
+ def setup_depth_stencil_attachment(att)
177
+ ds = Native::RenderPassDepthStencilAttachment.new
178
+ @pointers << ds
179
+
180
+ ds[:view] = att[:view].handle
181
+ ds[:depth_load_op] = att[:depth_load_op] || :clear
182
+ ds[:depth_store_op] = att[:depth_store_op] || :store
183
+ ds[:depth_clear_value] = att[:depth_clear_value] || 1.0
184
+ ds[:depth_read_only] = att[:depth_read_only] ? 1 : 0
185
+ ds[:stencil_load_op] = att[:stencil_load_op] || :clear
186
+ ds[:stencil_store_op] = att[:stencil_store_op] || :store
187
+ ds[:stencil_clear_value] = att[:stencil_clear_value] || 0
188
+ ds[:stencil_read_only] = att[:stencil_read_only] ? 1 : 0
189
+
190
+ ds.to_ptr
191
+ end
192
+ end
193
+ end