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.
- checksums.yaml +7 -0
- data/LICENSE-APACHE +190 -0
- data/LICENSE-MIT +21 -0
- data/README.md +213 -0
- data/ext/wgpu/Makefile +7 -0
- data/ext/wgpu/extconf.rb +161 -0
- data/lib/wgpu/commands/command_buffer.rb +17 -0
- data/lib/wgpu/commands/command_encoder.rb +189 -0
- data/lib/wgpu/commands/compute_pass.rb +76 -0
- data/lib/wgpu/commands/render_bundle.rb +18 -0
- data/lib/wgpu/commands/render_bundle_encoder.rb +148 -0
- data/lib/wgpu/commands/render_pass.rb +193 -0
- data/lib/wgpu/core/adapter.rb +170 -0
- data/lib/wgpu/core/device.rb +304 -0
- data/lib/wgpu/core/instance.rb +52 -0
- data/lib/wgpu/core/queue.rb +173 -0
- data/lib/wgpu/core/surface.rb +179 -0
- data/lib/wgpu/error.rb +16 -0
- data/lib/wgpu/native/callbacks.rb +26 -0
- data/lib/wgpu/native/enums.rb +524 -0
- data/lib/wgpu/native/functions.rb +413 -0
- data/lib/wgpu/native/loader.rb +61 -0
- data/lib/wgpu/native/structs.rb +609 -0
- data/lib/wgpu/pipeline/bind_group.rb +80 -0
- data/lib/wgpu/pipeline/bind_group_layout.rb +121 -0
- data/lib/wgpu/pipeline/compute_pipeline.rb +54 -0
- data/lib/wgpu/pipeline/pipeline_layout.rb +43 -0
- data/lib/wgpu/pipeline/render_pipeline.rb +248 -0
- data/lib/wgpu/pipeline/shader_module.rb +98 -0
- data/lib/wgpu/resources/buffer.rb +184 -0
- data/lib/wgpu/resources/query_set.rb +45 -0
- data/lib/wgpu/resources/sampler.rb +47 -0
- data/lib/wgpu/resources/texture.rb +118 -0
- data/lib/wgpu/resources/texture_view.rb +45 -0
- data/lib/wgpu/version.rb +5 -0
- data/lib/wgpu.rb +34 -0
- metadata +108 -0
|
@@ -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
|