wgpu 1.1.0 → 1.2.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 +4 -4
- data/CHANGELOG.md +48 -0
- data/README.md +26 -3
- data/docs/README.md +22 -0
- data/docs/api_coverage.md +132 -0
- data/docs/async.md +31 -0
- data/docs/bind_groups.md +25 -0
- data/docs/buffer_data.md +37 -0
- data/docs/command_encoding.md +24 -0
- data/docs/errors.md +40 -0
- data/docs/getting_started_compute.md +61 -0
- data/docs/getting_started_rendering.md +62 -0
- data/docs/installation.md +94 -0
- data/docs/pipeline_descriptors.md +38 -0
- data/docs/releasing.md +22 -0
- data/docs/resource_lifetime.md +94 -0
- data/docs/shaders.md +32 -0
- data/docs/texture_readback.md +19 -0
- data/docs/troubleshooting.md +48 -0
- data/docs/upgrading_wgpu_native.md +26 -0
- data/ext/wgpu/extconf.rb +10 -142
- data/lib/wgpu/commands/command_buffer.rb +11 -0
- data/lib/wgpu/commands/command_encoder.rb +65 -8
- data/lib/wgpu/commands/compute_pass.rb +10 -0
- data/lib/wgpu/commands/render_bundle_encoder.rb +10 -3
- data/lib/wgpu/commands/render_pass.rb +34 -8
- data/lib/wgpu/core/adapter.rb +36 -11
- data/lib/wgpu/core/async_waiter.rb +32 -4
- data/lib/wgpu/core/canvas_context.rb +0 -2
- data/lib/wgpu/core/device.rb +138 -42
- data/lib/wgpu/core/instance.rb +8 -4
- data/lib/wgpu/core/queue.rb +80 -49
- data/lib/wgpu/core/surface.rb +16 -17
- data/lib/wgpu/data_types.rb +67 -0
- data/lib/wgpu/descriptor_helpers.rb +39 -0
- data/lib/wgpu/error.rb +55 -0
- data/lib/wgpu/logging.rb +63 -0
- data/lib/wgpu/native/abi_verifier.rb +109 -0
- data/lib/wgpu/native/callbacks.rb +4 -1
- data/lib/wgpu/native/capabilities.rb +18 -2
- data/lib/wgpu/native/distribution.rb +113 -0
- data/lib/wgpu/native/enum_helper.rb +63 -0
- data/lib/wgpu/native/enums.rb +60 -8
- data/lib/wgpu/native/functions.rb +10 -10
- data/lib/wgpu/native/installer.rb +192 -0
- data/lib/wgpu/native/loader.rb +39 -21
- data/lib/wgpu/native_resource.rb +171 -0
- data/lib/wgpu/pipeline/bind_group.rb +12 -0
- data/lib/wgpu/pipeline/bind_group_layout.rb +91 -37
- data/lib/wgpu/pipeline/compute_pipeline.rb +28 -26
- data/lib/wgpu/pipeline/render_pipeline.rb +180 -68
- data/lib/wgpu/pipeline/shader_module.rb +50 -8
- data/lib/wgpu/resources/buffer.rb +148 -73
- data/lib/wgpu/resources/query_set.rb +14 -12
- data/lib/wgpu/resources/sampler.rb +36 -20
- data/lib/wgpu/resources/texture.rb +44 -42
- data/lib/wgpu/resources/texture_view.rb +11 -3
- data/lib/wgpu/texture_format.rb +82 -0
- data/lib/wgpu/version.rb +1 -1
- data/lib/wgpu/window.rb +8 -1
- data/lib/wgpu.rb +32 -0
- data/sig/wgpu.rbs +381 -0
- metadata +31 -16
|
@@ -6,32 +6,7 @@ module WGPU
|
|
|
6
6
|
|
|
7
7
|
def initialize(device, label: nil, layout:, compute:)
|
|
8
8
|
@device = device
|
|
9
|
-
@pointers =
|
|
10
|
-
|
|
11
|
-
entry_point = compute[:entry_point] || "main"
|
|
12
|
-
entry_point_ptr = FFI::MemoryPointer.from_string(entry_point)
|
|
13
|
-
@pointers << entry_point_ptr
|
|
14
|
-
|
|
15
|
-
desc = Native::ComputePipelineDescriptor.new
|
|
16
|
-
desc[:next_in_chain] = nil
|
|
17
|
-
if label
|
|
18
|
-
label_ptr = FFI::MemoryPointer.from_string(label)
|
|
19
|
-
@pointers << label_ptr
|
|
20
|
-
desc[:label][:data] = label_ptr
|
|
21
|
-
desc[:label][:length] = label.bytesize
|
|
22
|
-
else
|
|
23
|
-
desc[:label][:data] = nil
|
|
24
|
-
desc[:label][:length] = 0
|
|
25
|
-
end
|
|
26
|
-
desc[:layout] = normalize_layout(layout)
|
|
27
|
-
|
|
28
|
-
desc[:compute][:next_in_chain] = nil
|
|
29
|
-
desc[:compute][:module] = compute.fetch(:module).handle
|
|
30
|
-
desc[:compute][:entry_point][:data] = entry_point_ptr
|
|
31
|
-
desc[:compute][:entry_point][:length] = entry_point.bytesize
|
|
32
|
-
desc[:compute][:constant_count] = 0
|
|
33
|
-
desc[:compute][:constants] = nil
|
|
34
|
-
setup_constants(desc[:compute], compute[:constants])
|
|
9
|
+
desc, @pointers = build_descriptor(label:, layout:, compute:)
|
|
35
10
|
|
|
36
11
|
device.push_error_scope(:validation)
|
|
37
12
|
@handle = Native.wgpuDeviceCreateComputePipeline(device.handle, desc)
|
|
@@ -57,6 +32,33 @@ module WGPU
|
|
|
57
32
|
|
|
58
33
|
private
|
|
59
34
|
|
|
35
|
+
def build_descriptor(label:, layout:, compute:)
|
|
36
|
+
DescriptorHelpers.validate_keys!(
|
|
37
|
+
compute,
|
|
38
|
+
allowed: %i[module entry_point constants],
|
|
39
|
+
required: [:module],
|
|
40
|
+
context: "compute pipeline stage"
|
|
41
|
+
)
|
|
42
|
+
@pointers = []
|
|
43
|
+
entry_point = compute[:entry_point] || "main"
|
|
44
|
+
entry_point_ptr = FFI::MemoryPointer.from_string(entry_point)
|
|
45
|
+
@pointers << entry_point_ptr
|
|
46
|
+
|
|
47
|
+
desc = Native::ComputePipelineDescriptor.new
|
|
48
|
+
desc[:next_in_chain] = nil
|
|
49
|
+
DescriptorHelpers.set_label(desc, label, keepalive: @pointers)
|
|
50
|
+
desc[:layout] = normalize_layout(layout)
|
|
51
|
+
desc[:compute][:next_in_chain] = nil
|
|
52
|
+
desc[:compute][:module] = compute.fetch(:module).handle
|
|
53
|
+
desc[:compute][:entry_point][:data] = entry_point_ptr
|
|
54
|
+
desc[:compute][:entry_point][:length] = entry_point.bytesize
|
|
55
|
+
desc[:compute][:constant_count] = 0
|
|
56
|
+
desc[:compute][:constants] = nil
|
|
57
|
+
setup_constants(desc[:compute], compute[:constants])
|
|
58
|
+
|
|
59
|
+
[desc, @pointers]
|
|
60
|
+
end
|
|
61
|
+
|
|
60
62
|
def normalize_layout(layout)
|
|
61
63
|
return nil if layout.nil? || layout == :auto || layout == "auto"
|
|
62
64
|
layout.handle
|
|
@@ -6,30 +6,15 @@ module WGPU
|
|
|
6
6
|
|
|
7
7
|
def initialize(device, label: nil, layout:, vertex:, primitive: {}, depth_stencil: nil, multisample: {}, fragment: nil)
|
|
8
8
|
@device = device
|
|
9
|
-
@pointers =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
setup_multisample_state(desc[:multisample], multisample)
|
|
19
|
-
|
|
20
|
-
if depth_stencil
|
|
21
|
-
ds_ptr = setup_depth_stencil_state(depth_stencil)
|
|
22
|
-
desc[:depth_stencil] = ds_ptr
|
|
23
|
-
else
|
|
24
|
-
desc[:depth_stencil] = nil
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
if fragment
|
|
28
|
-
frag_ptr = setup_fragment_state(fragment)
|
|
29
|
-
desc[:fragment] = frag_ptr
|
|
30
|
-
else
|
|
31
|
-
desc[:fragment] = nil
|
|
32
|
-
end
|
|
9
|
+
desc, @pointers = build_descriptor(
|
|
10
|
+
label:,
|
|
11
|
+
layout:,
|
|
12
|
+
vertex:,
|
|
13
|
+
primitive:,
|
|
14
|
+
depth_stencil:,
|
|
15
|
+
multisample:,
|
|
16
|
+
fragment:
|
|
17
|
+
)
|
|
33
18
|
|
|
34
19
|
device.push_error_scope(:validation)
|
|
35
20
|
@handle = Native.wgpuDeviceCreateRenderPipeline(device.handle, desc)
|
|
@@ -55,19 +40,29 @@ module WGPU
|
|
|
55
40
|
|
|
56
41
|
private
|
|
57
42
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
43
|
+
def build_descriptor(label:, layout:, vertex:, primitive:, depth_stencil:, multisample:, fragment:)
|
|
44
|
+
@pointers = []
|
|
45
|
+
desc = Native::RenderPipelineDescriptor.new
|
|
46
|
+
desc[:next_in_chain] = nil
|
|
47
|
+
DescriptorHelpers.set_label(desc, label, keepalive: @pointers)
|
|
48
|
+
desc[:layout] = normalize_layout(layout)
|
|
49
|
+
|
|
50
|
+
setup_vertex_state(desc[:vertex], vertex)
|
|
51
|
+
setup_primitive_state(desc[:primitive], primitive)
|
|
52
|
+
setup_multisample_state(desc[:multisample], multisample)
|
|
53
|
+
desc[:depth_stencil] = depth_stencil ? setup_depth_stencil_state(depth_stencil) : nil
|
|
54
|
+
desc[:fragment] = fragment ? setup_fragment_state(fragment) : nil
|
|
55
|
+
|
|
56
|
+
[desc, @pointers]
|
|
68
57
|
end
|
|
69
58
|
|
|
70
59
|
def setup_vertex_state(vertex_state, vertex)
|
|
60
|
+
DescriptorHelpers.validate_keys!(
|
|
61
|
+
vertex,
|
|
62
|
+
allowed: %i[module entry_point constants buffers],
|
|
63
|
+
required: [:module],
|
|
64
|
+
context: "render pipeline vertex descriptor"
|
|
65
|
+
)
|
|
71
66
|
vertex_state[:next_in_chain] = nil
|
|
72
67
|
vertex_state[:module] = vertex[:module].handle
|
|
73
68
|
|
|
@@ -97,8 +92,18 @@ module WGPU
|
|
|
97
92
|
@pointers << layouts_ptr
|
|
98
93
|
|
|
99
94
|
buffers.each_with_index do |buf, i|
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
DescriptorHelpers.validate_keys!(
|
|
96
|
+
buf,
|
|
97
|
+
allowed: %i[array_stride step_mode attributes],
|
|
98
|
+
required: [:array_stride],
|
|
99
|
+
context: "vertex buffer layout"
|
|
100
|
+
)
|
|
101
|
+
layout = Native::VertexBufferLayout.new(layouts_ptr + (i * Native::VertexBufferLayout.size))
|
|
102
|
+
layout[:step_mode] = Native::EnumHelper.coerce(
|
|
103
|
+
Native::VertexStepMode,
|
|
104
|
+
buf[:step_mode] || :vertex,
|
|
105
|
+
name: "vertex step mode"
|
|
106
|
+
)
|
|
102
107
|
layout[:array_stride] = buf[:array_stride]
|
|
103
108
|
|
|
104
109
|
attrs = buf[:attributes] || []
|
|
@@ -109,8 +114,18 @@ module WGPU
|
|
|
109
114
|
attrs_ptr = FFI::MemoryPointer.new(Native::VertexAttribute, attrs.size)
|
|
110
115
|
@pointers << attrs_ptr
|
|
111
116
|
attrs.each_with_index do |attr, j|
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
DescriptorHelpers.validate_keys!(
|
|
118
|
+
attr,
|
|
119
|
+
allowed: %i[format offset shader_location],
|
|
120
|
+
required: %i[format offset shader_location],
|
|
121
|
+
context: "vertex attribute"
|
|
122
|
+
)
|
|
123
|
+
a = Native::VertexAttribute.new(attrs_ptr + (j * Native::VertexAttribute.size))
|
|
124
|
+
a[:format] = Native::EnumHelper.coerce(
|
|
125
|
+
Native::VertexFormat,
|
|
126
|
+
attr[:format],
|
|
127
|
+
name: "vertex format"
|
|
128
|
+
)
|
|
114
129
|
a[:offset] = attr[:offset]
|
|
115
130
|
a[:shader_location] = attr[:shader_location]
|
|
116
131
|
end
|
|
@@ -123,15 +138,41 @@ module WGPU
|
|
|
123
138
|
end
|
|
124
139
|
|
|
125
140
|
def setup_primitive_state(primitive_state, primitive)
|
|
141
|
+
DescriptorHelpers.validate_keys!(
|
|
142
|
+
primitive,
|
|
143
|
+
allowed: %i[topology strip_index_format front_face cull_mode unclipped_depth],
|
|
144
|
+
context: "primitive state"
|
|
145
|
+
)
|
|
126
146
|
primitive_state[:next_in_chain] = nil
|
|
127
|
-
primitive_state[:topology] =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
primitive_state[:topology] = Native::EnumHelper.coerce(
|
|
148
|
+
Native::PrimitiveTopology,
|
|
149
|
+
primitive[:topology] || :triangle_list,
|
|
150
|
+
name: "primitive topology"
|
|
151
|
+
)
|
|
152
|
+
primitive_state[:strip_index_format] = Native::EnumHelper.coerce(
|
|
153
|
+
Native::IndexFormat,
|
|
154
|
+
primitive[:strip_index_format] || :undefined,
|
|
155
|
+
name: "strip index format"
|
|
156
|
+
)
|
|
157
|
+
primitive_state[:front_face] = Native::EnumHelper.coerce(
|
|
158
|
+
Native::FrontFace,
|
|
159
|
+
primitive[:front_face] || :ccw,
|
|
160
|
+
name: "front face"
|
|
161
|
+
)
|
|
162
|
+
primitive_state[:cull_mode] = Native::EnumHelper.coerce(
|
|
163
|
+
Native::CullMode,
|
|
164
|
+
primitive[:cull_mode] || :none,
|
|
165
|
+
name: "cull mode"
|
|
166
|
+
)
|
|
131
167
|
primitive_state[:unclipped_depth] = primitive[:unclipped_depth] ? 1 : 0
|
|
132
168
|
end
|
|
133
169
|
|
|
134
170
|
def setup_multisample_state(multisample_state, multisample)
|
|
171
|
+
DescriptorHelpers.validate_keys!(
|
|
172
|
+
multisample,
|
|
173
|
+
allowed: %i[count mask alpha_to_coverage_enabled],
|
|
174
|
+
context: "multisample state"
|
|
175
|
+
)
|
|
135
176
|
multisample_state[:next_in_chain] = nil
|
|
136
177
|
multisample_state[:count] = multisample[:count] || 1
|
|
137
178
|
multisample_state[:mask] = multisample[:mask] || 0xFFFFFFFF
|
|
@@ -139,12 +180,29 @@ module WGPU
|
|
|
139
180
|
end
|
|
140
181
|
|
|
141
182
|
def setup_depth_stencil_state(depth_stencil)
|
|
183
|
+
DescriptorHelpers.validate_keys!(
|
|
184
|
+
depth_stencil,
|
|
185
|
+
allowed: %i[
|
|
186
|
+
format depth_write_enabled depth_compare stencil_front stencil_back
|
|
187
|
+
stencil_read_mask stencil_write_mask depth_bias depth_bias_slope_scale depth_bias_clamp
|
|
188
|
+
],
|
|
189
|
+
required: [:format],
|
|
190
|
+
context: "depth stencil state"
|
|
191
|
+
)
|
|
142
192
|
ds = Native::DepthStencilState.new
|
|
143
193
|
@pointers << ds
|
|
144
194
|
ds[:next_in_chain] = nil
|
|
145
|
-
ds[:format] =
|
|
195
|
+
ds[:format] = Native::EnumHelper.coerce(
|
|
196
|
+
Native::TextureFormat,
|
|
197
|
+
depth_stencil[:format],
|
|
198
|
+
name: "depth stencil format"
|
|
199
|
+
)
|
|
146
200
|
ds[:depth_write_enabled] = depth_stencil[:depth_write_enabled] ? 1 : 0
|
|
147
|
-
ds[:depth_compare] =
|
|
201
|
+
ds[:depth_compare] = Native::EnumHelper.coerce(
|
|
202
|
+
Native::CompareFunction,
|
|
203
|
+
depth_stencil[:depth_compare] || :always,
|
|
204
|
+
name: "depth compare function"
|
|
205
|
+
)
|
|
148
206
|
|
|
149
207
|
setup_stencil_face(ds[:stencil_front], depth_stencil[:stencil_front] || {})
|
|
150
208
|
setup_stencil_face(ds[:stencil_back], depth_stencil[:stencil_back] || {})
|
|
@@ -159,13 +217,40 @@ module WGPU
|
|
|
159
217
|
end
|
|
160
218
|
|
|
161
219
|
def setup_stencil_face(face, config)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
220
|
+
DescriptorHelpers.validate_keys!(
|
|
221
|
+
config,
|
|
222
|
+
allowed: %i[compare fail_op depth_fail_op pass_op],
|
|
223
|
+
context: "stencil face state"
|
|
224
|
+
)
|
|
225
|
+
face[:compare] = Native::EnumHelper.coerce(
|
|
226
|
+
Native::CompareFunction,
|
|
227
|
+
config[:compare] || :always,
|
|
228
|
+
name: "stencil compare function"
|
|
229
|
+
)
|
|
230
|
+
face[:fail_op] = Native::EnumHelper.coerce(
|
|
231
|
+
Native::StencilOperation,
|
|
232
|
+
config[:fail_op] || :keep,
|
|
233
|
+
name: "stencil fail operation"
|
|
234
|
+
)
|
|
235
|
+
face[:depth_fail_op] = Native::EnumHelper.coerce(
|
|
236
|
+
Native::StencilOperation,
|
|
237
|
+
config[:depth_fail_op] || :keep,
|
|
238
|
+
name: "stencil depth fail operation"
|
|
239
|
+
)
|
|
240
|
+
face[:pass_op] = Native::EnumHelper.coerce(
|
|
241
|
+
Native::StencilOperation,
|
|
242
|
+
config[:pass_op] || :keep,
|
|
243
|
+
name: "stencil pass operation"
|
|
244
|
+
)
|
|
166
245
|
end
|
|
167
246
|
|
|
168
247
|
def setup_fragment_state(fragment)
|
|
248
|
+
DescriptorHelpers.validate_keys!(
|
|
249
|
+
fragment,
|
|
250
|
+
allowed: %i[module entry_point constants targets],
|
|
251
|
+
required: [:module],
|
|
252
|
+
context: "render pipeline fragment descriptor"
|
|
253
|
+
)
|
|
169
254
|
frag = Native::FragmentState.new
|
|
170
255
|
@pointers << frag
|
|
171
256
|
frag[:next_in_chain] = nil
|
|
@@ -199,9 +284,19 @@ module WGPU
|
|
|
199
284
|
@pointers << targets_ptr
|
|
200
285
|
|
|
201
286
|
targets.each_with_index do |target, i|
|
|
202
|
-
|
|
287
|
+
DescriptorHelpers.validate_keys!(
|
|
288
|
+
target,
|
|
289
|
+
allowed: %i[format blend write_mask],
|
|
290
|
+
required: [:format],
|
|
291
|
+
context: "color target state"
|
|
292
|
+
)
|
|
293
|
+
ct = Native::ColorTargetState.new(targets_ptr + (i * Native::ColorTargetState.size))
|
|
203
294
|
ct[:next_in_chain] = nil
|
|
204
|
-
ct[:format] =
|
|
295
|
+
ct[:format] = Native::EnumHelper.coerce(
|
|
296
|
+
Native::TextureFormat,
|
|
297
|
+
target[:format],
|
|
298
|
+
name: "color target format"
|
|
299
|
+
)
|
|
205
300
|
ct[:write_mask] = normalize_write_mask(target[:write_mask])
|
|
206
301
|
|
|
207
302
|
if target[:blend]
|
|
@@ -216,35 +311,52 @@ module WGPU
|
|
|
216
311
|
end
|
|
217
312
|
|
|
218
313
|
def setup_blend_state(blend)
|
|
314
|
+
DescriptorHelpers.validate_keys!(
|
|
315
|
+
blend,
|
|
316
|
+
allowed: %i[color alpha],
|
|
317
|
+
context: "blend state"
|
|
318
|
+
)
|
|
219
319
|
bs = Native::BlendState.new
|
|
220
320
|
@pointers << bs
|
|
221
321
|
|
|
222
322
|
color = blend[:color] || {}
|
|
223
|
-
bs[:color]
|
|
224
|
-
bs[:color][:src_factor] = color[:src_factor] || :one
|
|
225
|
-
bs[:color][:dst_factor] = color[:dst_factor] || :zero
|
|
323
|
+
setup_blend_component(bs[:color], color, "color")
|
|
226
324
|
|
|
227
325
|
alpha = blend[:alpha] || {}
|
|
228
|
-
bs[:alpha]
|
|
229
|
-
bs[:alpha][:src_factor] = alpha[:src_factor] || :one
|
|
230
|
-
bs[:alpha][:dst_factor] = alpha[:dst_factor] || :zero
|
|
326
|
+
setup_blend_component(bs[:alpha], alpha, "alpha")
|
|
231
327
|
|
|
232
328
|
bs.to_ptr
|
|
233
329
|
end
|
|
234
330
|
|
|
331
|
+
def setup_blend_component(component, config, name)
|
|
332
|
+
DescriptorHelpers.validate_keys!(
|
|
333
|
+
config,
|
|
334
|
+
allowed: %i[operation src_factor dst_factor],
|
|
335
|
+
context: "#{name} blend component"
|
|
336
|
+
)
|
|
337
|
+
component[:operation] = Native::EnumHelper.coerce(
|
|
338
|
+
Native::BlendOperation,
|
|
339
|
+
config[:operation] || :add,
|
|
340
|
+
name: "#{name} blend operation"
|
|
341
|
+
)
|
|
342
|
+
component[:src_factor] = Native::EnumHelper.coerce(
|
|
343
|
+
Native::BlendFactor,
|
|
344
|
+
config[:src_factor] || :one,
|
|
345
|
+
name: "#{name} source blend factor"
|
|
346
|
+
)
|
|
347
|
+
component[:dst_factor] = Native::EnumHelper.coerce(
|
|
348
|
+
Native::BlendFactor,
|
|
349
|
+
config[:dst_factor] || :zero,
|
|
350
|
+
name: "#{name} destination blend factor"
|
|
351
|
+
)
|
|
352
|
+
end
|
|
353
|
+
|
|
235
354
|
def normalize_write_mask(mask)
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
when Symbol
|
|
242
|
-
Native::ColorWriteMask[mask]
|
|
243
|
-
when Array
|
|
244
|
-
mask.reduce(0) { |acc, m| acc | Native::ColorWriteMask[m] }
|
|
245
|
-
else
|
|
246
|
-
raise ArgumentError, "Invalid write_mask: #{mask}"
|
|
247
|
-
end
|
|
355
|
+
Native::EnumHelper.coerce_flags(
|
|
356
|
+
Native::ColorWriteMask,
|
|
357
|
+
mask || :all,
|
|
358
|
+
name: "color write mask"
|
|
359
|
+
)
|
|
248
360
|
end
|
|
249
361
|
|
|
250
362
|
def normalize_layout(layout)
|
|
@@ -4,12 +4,13 @@ module WGPU
|
|
|
4
4
|
class ShaderModule
|
|
5
5
|
attr_reader :handle
|
|
6
6
|
|
|
7
|
-
def initialize(device, label: nil, code
|
|
7
|
+
def initialize(device, label: nil, code: nil, spirv: nil, compilation_hints: [], validate: false)
|
|
8
8
|
@device = device
|
|
9
9
|
@pointers = []
|
|
10
|
-
compilation_hints
|
|
10
|
+
@compilation_hints = compilation_hints
|
|
11
|
+
source = select_source(code, spirv)
|
|
11
12
|
|
|
12
|
-
source_ptr = build_shader_source(
|
|
13
|
+
source_ptr = build_shader_source(source, label: label)
|
|
13
14
|
|
|
14
15
|
desc = Native::ShaderModuleDescriptor.new
|
|
15
16
|
desc[:next_in_chain] = source_ptr
|
|
@@ -29,11 +30,25 @@ module WGPU
|
|
|
29
30
|
|
|
30
31
|
if @handle.null? || (error[:type] && error[:type] != :no_error)
|
|
31
32
|
msg = error[:message] || "Failed to create shader module"
|
|
32
|
-
raise ShaderError, msg
|
|
33
|
+
raise ShaderError, shader_error_message(msg, label)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
return unless validate
|
|
37
|
+
|
|
38
|
+
begin
|
|
39
|
+
validate_compilation!(label)
|
|
40
|
+
rescue StandardError
|
|
41
|
+
release
|
|
42
|
+
raise
|
|
33
43
|
end
|
|
34
44
|
end
|
|
35
45
|
|
|
36
46
|
def get_compilation_info
|
|
47
|
+
unless Native.compilation_info_available?
|
|
48
|
+
raise ShaderError,
|
|
49
|
+
"Shader compilation info is not implemented by wgpu-native #{Native::Distribution::VERSION}"
|
|
50
|
+
end
|
|
51
|
+
|
|
37
52
|
result_holder = { done: false, status: nil, messages: [] }
|
|
38
53
|
instance = @device.adapter&.instance
|
|
39
54
|
|
|
@@ -55,14 +70,14 @@ module WGPU
|
|
|
55
70
|
else
|
|
56
71
|
""
|
|
57
72
|
end
|
|
58
|
-
result_holder[:messages] <<
|
|
73
|
+
result_holder[:messages] << CompilationMessage.new(
|
|
59
74
|
type: msg[:type],
|
|
60
75
|
message: message_text,
|
|
61
76
|
line_num: msg[:line_num],
|
|
62
77
|
line_pos: msg[:line_pos],
|
|
63
78
|
offset: msg[:offset],
|
|
64
79
|
length: msg[:length]
|
|
65
|
-
|
|
80
|
+
)
|
|
66
81
|
end
|
|
67
82
|
end
|
|
68
83
|
end
|
|
@@ -75,8 +90,13 @@ module WGPU
|
|
|
75
90
|
callback_info[:userdata1] = nil
|
|
76
91
|
callback_info[:userdata2] = nil
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
callback_token = CallbackKeepalive.retain(self, callback)
|
|
94
|
+
begin
|
|
95
|
+
future = Native.wgpuShaderModuleGetCompilationInfo(@handle, callback_info)
|
|
96
|
+
AsyncWaiter.wait(status_holder: result_holder, instance: instance, device: @device, future: future)
|
|
97
|
+
ensure
|
|
98
|
+
CallbackKeepalive.release(self, callback_token)
|
|
99
|
+
end
|
|
80
100
|
|
|
81
101
|
{
|
|
82
102
|
status: result_holder[:status],
|
|
@@ -96,6 +116,28 @@ module WGPU
|
|
|
96
116
|
|
|
97
117
|
private
|
|
98
118
|
|
|
119
|
+
def select_source(code, spirv)
|
|
120
|
+
sources = [code, spirv].reject(&:nil?)
|
|
121
|
+
raise ArgumentError, "provide exactly one of code: or spirv:" unless sources.one?
|
|
122
|
+
|
|
123
|
+
sources.first
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def validate_compilation!(label)
|
|
127
|
+
info = get_compilation_info
|
|
128
|
+
errors = info[:messages].select { |message| message.type == :error }
|
|
129
|
+
return if errors.empty?
|
|
130
|
+
|
|
131
|
+
details = errors.map(&:to_s).join("\n")
|
|
132
|
+
release
|
|
133
|
+
raise ShaderError, shader_error_message(details, label)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def shader_error_message(message, label)
|
|
137
|
+
context = label ? " for #{label.inspect}" : ""
|
|
138
|
+
"Shader compilation failed#{context}: #{message}"
|
|
139
|
+
end
|
|
140
|
+
|
|
99
141
|
def build_shader_source(code, label:)
|
|
100
142
|
if code.is_a?(String)
|
|
101
143
|
if spirv_binary?(code)
|