wgpu 1.0.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 +49 -20
- data/lib/wgpu/core/async_waiter.rb +92 -0
- data/lib/wgpu/core/canvas_context.rb +0 -2
- data/lib/wgpu/core/device.rb +160 -52
- data/lib/wgpu/core/instance.rb +9 -5
- data/lib/wgpu/core/queue.rb +81 -57
- 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 +9 -6
- data/lib/wgpu/native/capabilities.rb +31 -0
- data/lib/wgpu/native/distribution.rb +113 -0
- data/lib/wgpu/native/enum_helper.rb +63 -0
- data/lib/wgpu/native/enums.rb +82 -13
- data/lib/wgpu/native/functions.rb +17 -8
- data/lib/wgpu/native/installer.rb +192 -0
- data/lib/wgpu/native/loader.rb +40 -21
- data/lib/wgpu/native/structs.rb +19 -5
- 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 +51 -12
- data/lib/wgpu/resources/buffer.rb +167 -84
- 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 +33 -0
- data/sig/wgpu.rbs +381 -0
- metadata +33 -16
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WGPU
|
|
4
|
+
class << self
|
|
5
|
+
attr_accessor :debug_leaks
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
self.debug_leaks = ENV["WGPU_DEBUG_LEAKS"] == "1"
|
|
9
|
+
|
|
10
|
+
module LeakTracker
|
|
11
|
+
@resources = {}
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def register(resource)
|
|
17
|
+
return unless WGPU.debug_leaks
|
|
18
|
+
|
|
19
|
+
object_id = resource.object_id
|
|
20
|
+
description = describe(resource)
|
|
21
|
+
@mutex.synchronize { @resources[object_id] = description }
|
|
22
|
+
ObjectSpace.define_finalizer(resource, finalizer(object_id))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def unregister(resource)
|
|
26
|
+
@mutex.synchronize { @resources.delete(resource.object_id) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def warn_remaining
|
|
30
|
+
resources = @mutex.synchronize do
|
|
31
|
+
remaining = @resources.values
|
|
32
|
+
@resources.clear
|
|
33
|
+
remaining
|
|
34
|
+
end
|
|
35
|
+
resources.each { |description| warn "WGPU resource leaked: #{description}" }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def describe(resource)
|
|
39
|
+
label = resource.label
|
|
40
|
+
label_text = label ? " label=#{label.inspect}" : ""
|
|
41
|
+
"#{resource.class}#{label_text}"
|
|
42
|
+
end
|
|
43
|
+
private_class_method :describe
|
|
44
|
+
|
|
45
|
+
def finalizer(object_id)
|
|
46
|
+
proc do
|
|
47
|
+
description = @mutex.synchronize { @resources.delete(object_id) }
|
|
48
|
+
warn "WGPU resource leaked: #{description}" if description
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
private_class_method :finalizer
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
at_exit { LeakTracker.warn_remaining if WGPU.debug_leaks }
|
|
55
|
+
|
|
56
|
+
module CallbackKeepalive
|
|
57
|
+
INITIALIZATION_MUTEX = Mutex.new
|
|
58
|
+
|
|
59
|
+
module_function
|
|
60
|
+
|
|
61
|
+
def retain(owner, callback)
|
|
62
|
+
mutex, callbacks = storage_for(owner)
|
|
63
|
+
token = Object.new
|
|
64
|
+
mutex.synchronize { callbacks[token] = callback }
|
|
65
|
+
token
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def release(owner, token)
|
|
69
|
+
return unless token
|
|
70
|
+
|
|
71
|
+
mutex, callbacks = storage_for(owner)
|
|
72
|
+
mutex.synchronize { callbacks.delete(token) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def count(owner)
|
|
76
|
+
mutex, callbacks = storage_for(owner)
|
|
77
|
+
mutex.synchronize { callbacks.length }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def storage_for(owner)
|
|
81
|
+
INITIALIZATION_MUTEX.synchronize do
|
|
82
|
+
mutex = owner.instance_variable_get(:@wgpu_callback_keepalive_mutex)
|
|
83
|
+
callbacks = owner.instance_variable_get(:@wgpu_callback_keepalive)
|
|
84
|
+
unless mutex && callbacks
|
|
85
|
+
mutex = Mutex.new
|
|
86
|
+
callbacks = {}
|
|
87
|
+
owner.instance_variable_set(:@wgpu_callback_keepalive_mutex, mutex)
|
|
88
|
+
owner.instance_variable_set(:@wgpu_callback_keepalive, callbacks)
|
|
89
|
+
end
|
|
90
|
+
[mutex, callbacks]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
private_class_method :storage_for
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
module NativeResource
|
|
97
|
+
GUARDED_METHOD_EXEMPTIONS = [:initialize, :release, :released?, :handle, :label, :inspect].freeze
|
|
98
|
+
|
|
99
|
+
module Lifecycle
|
|
100
|
+
def initialize(*args, **kwargs, &block)
|
|
101
|
+
super
|
|
102
|
+
@released = false
|
|
103
|
+
@label = kwargs[:label] if kwargs.key?(:label)
|
|
104
|
+
LeakTracker.register(self)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def release(...)
|
|
108
|
+
return if released?
|
|
109
|
+
|
|
110
|
+
result = super
|
|
111
|
+
@released = true
|
|
112
|
+
LeakTracker.unregister(self)
|
|
113
|
+
result
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.included(base)
|
|
118
|
+
guard = Module.new
|
|
119
|
+
base.public_instance_methods(false).each do |method_name|
|
|
120
|
+
next if GUARDED_METHOD_EXEMPTIONS.include?(method_name)
|
|
121
|
+
|
|
122
|
+
guard.define_method(method_name) do |*args, **kwargs, &block|
|
|
123
|
+
ensure_not_released!
|
|
124
|
+
super(*args, **kwargs, &block)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
base.prepend(guard)
|
|
128
|
+
base.prepend(Lifecycle)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
attr_reader :label
|
|
132
|
+
|
|
133
|
+
def released?
|
|
134
|
+
return true if @released
|
|
135
|
+
return false unless instance_variable_defined?(:@handle)
|
|
136
|
+
return true if @handle.nil?
|
|
137
|
+
|
|
138
|
+
@handle.respond_to?(:null?) && @handle.null?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Yields this wrapper and always releases it when the block exits.
|
|
142
|
+
#
|
|
143
|
+
# This is Ruby convenience API; WebGPU itself has no block-scoped resource
|
|
144
|
+
# primitive.
|
|
145
|
+
#
|
|
146
|
+
# @yieldparam resource [NativeResource] this resource
|
|
147
|
+
# @return the block result
|
|
148
|
+
def use
|
|
149
|
+
raise ArgumentError, "block is required" unless block_given?
|
|
150
|
+
|
|
151
|
+
ensure_not_released!
|
|
152
|
+
yield self
|
|
153
|
+
ensure
|
|
154
|
+
release if block_given? && !released?
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def inspect
|
|
158
|
+
label_text = @label ? " label=#{@label.inspect}" : ""
|
|
159
|
+
"#<#{self.class}#{label_text} released=#{released?}>"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
def ensure_not_released!
|
|
165
|
+
return unless released?
|
|
166
|
+
|
|
167
|
+
label_text = @label ? " (#{@label})" : ""
|
|
168
|
+
raise ResourceError, "#{self.class}#{label_text} has been released"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -47,6 +47,18 @@ module WGPU
|
|
|
47
47
|
private
|
|
48
48
|
|
|
49
49
|
def create_entry(entry_hash)
|
|
50
|
+
DescriptorHelpers.validate_keys!(
|
|
51
|
+
entry_hash,
|
|
52
|
+
allowed: %i[binding buffer offset size sampler texture_view],
|
|
53
|
+
required: [:binding],
|
|
54
|
+
context: "bind group entry"
|
|
55
|
+
)
|
|
56
|
+
resources = %i[buffer sampler texture_view].select { |key| entry_hash[key] }
|
|
57
|
+
unless resources.one?
|
|
58
|
+
raise ArgumentError,
|
|
59
|
+
"bind group entry must define exactly one resource (:buffer, :sampler, or :texture_view)"
|
|
60
|
+
end
|
|
61
|
+
|
|
50
62
|
entry = Native::BindGroupEntry.new
|
|
51
63
|
entry[:next_in_chain] = nil
|
|
52
64
|
entry[:binding] = entry_hash[:binding]
|
|
@@ -13,30 +13,12 @@ module WGPU
|
|
|
13
13
|
|
|
14
14
|
def initialize(device, label: nil, entries:)
|
|
15
15
|
@device = device
|
|
16
|
-
|
|
17
|
-
entries_array = entries.map { |e| create_entry(e) }
|
|
18
|
-
entries_ptr = FFI::MemoryPointer.new(Native::BindGroupLayoutEntry, entries_array.size)
|
|
19
|
-
entries_array.each_with_index do |entry, i|
|
|
20
|
-
offset = i * Native::BindGroupLayoutEntry.size
|
|
21
|
-
(entries_ptr + offset).put_bytes(0, entry.pointer.read_bytes(Native::BindGroupLayoutEntry.size))
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
desc = Native::BindGroupLayoutDescriptor.new
|
|
25
|
-
desc[:next_in_chain] = nil
|
|
26
|
-
if label
|
|
27
|
-
label_ptr = FFI::MemoryPointer.from_string(label)
|
|
28
|
-
desc[:label][:data] = label_ptr
|
|
29
|
-
desc[:label][:length] = label.bytesize
|
|
30
|
-
else
|
|
31
|
-
desc[:label][:data] = nil
|
|
32
|
-
desc[:label][:length] = 0
|
|
33
|
-
end
|
|
34
|
-
desc[:entry_count] = entries_array.size
|
|
35
|
-
desc[:entries] = entries_ptr
|
|
16
|
+
desc, @descriptor_keepalive = build_descriptor(label:, entries:)
|
|
36
17
|
|
|
37
18
|
device.push_error_scope(:validation)
|
|
38
19
|
@handle = Native.wgpuDeviceCreateBindGroupLayout(device.handle, desc)
|
|
39
20
|
error = device.pop_error_scope
|
|
21
|
+
@descriptor_keepalive = nil
|
|
40
22
|
|
|
41
23
|
if @handle.null? || (error[:type] && error[:type] != :no_error)
|
|
42
24
|
msg = error[:message] || "Failed to create bind group layout"
|
|
@@ -52,7 +34,39 @@ module WGPU
|
|
|
52
34
|
|
|
53
35
|
private
|
|
54
36
|
|
|
37
|
+
def build_descriptor(label:, entries:)
|
|
38
|
+
keepalive = []
|
|
39
|
+
entries_array = entries.map { |entry| create_entry(entry) }
|
|
40
|
+
entries_ptr = FFI::MemoryPointer.new(Native::BindGroupLayoutEntry, entries_array.size)
|
|
41
|
+
entries_array.each_with_index do |entry, index|
|
|
42
|
+
offset = index * Native::BindGroupLayoutEntry.size
|
|
43
|
+
(entries_ptr + offset).put_bytes(0, entry.pointer.read_bytes(Native::BindGroupLayoutEntry.size))
|
|
44
|
+
end
|
|
45
|
+
keepalive.concat(entries_array)
|
|
46
|
+
keepalive << entries_ptr
|
|
47
|
+
|
|
48
|
+
desc = Native::BindGroupLayoutDescriptor.new
|
|
49
|
+
desc[:next_in_chain] = nil
|
|
50
|
+
DescriptorHelpers.set_label(desc, label, keepalive:)
|
|
51
|
+
desc[:entry_count] = entries_array.size
|
|
52
|
+
desc[:entries] = entries_ptr
|
|
53
|
+
[desc, keepalive]
|
|
54
|
+
end
|
|
55
|
+
|
|
55
56
|
def create_entry(entry_hash)
|
|
57
|
+
DescriptorHelpers.validate_keys!(
|
|
58
|
+
entry_hash,
|
|
59
|
+
allowed: %i[binding visibility buffer sampler texture storage_texture],
|
|
60
|
+
required: %i[binding visibility],
|
|
61
|
+
context: "bind group layout entry"
|
|
62
|
+
)
|
|
63
|
+
variants = %i[buffer sampler texture storage_texture].select { |key| entry_hash[key] }
|
|
64
|
+
unless variants.one?
|
|
65
|
+
raise ArgumentError,
|
|
66
|
+
"bind group layout entry must define exactly one resource variant " \
|
|
67
|
+
"(:buffer, :sampler, :texture, or :storage_texture)"
|
|
68
|
+
end
|
|
69
|
+
|
|
56
70
|
entry = Native::BindGroupLayoutEntry.new
|
|
57
71
|
entry[:next_in_chain] = nil
|
|
58
72
|
entry[:binding] = entry_hash[:binding]
|
|
@@ -78,44 +92,84 @@ module WGPU
|
|
|
78
92
|
|
|
79
93
|
if entry_hash[:buffer]
|
|
80
94
|
buffer_info = entry_hash[:buffer]
|
|
81
|
-
|
|
95
|
+
DescriptorHelpers.validate_keys!(
|
|
96
|
+
buffer_info,
|
|
97
|
+
allowed: %i[type has_dynamic_offset min_binding_size],
|
|
98
|
+
context: "buffer binding layout"
|
|
99
|
+
)
|
|
100
|
+
entry[:buffer][:type] = Native::EnumHelper.coerce(
|
|
101
|
+
Native::BufferBindingType,
|
|
102
|
+
buffer_info[:type] || :storage,
|
|
103
|
+
name: "buffer binding type"
|
|
104
|
+
)
|
|
82
105
|
entry[:buffer][:has_dynamic_offset] = buffer_info[:has_dynamic_offset] ? 1 : 0
|
|
83
106
|
entry[:buffer][:min_binding_size] = buffer_info[:min_binding_size] || 0
|
|
84
107
|
end
|
|
85
108
|
|
|
86
109
|
if entry_hash[:sampler]
|
|
87
110
|
sampler_info = entry_hash[:sampler]
|
|
88
|
-
|
|
111
|
+
DescriptorHelpers.validate_keys!(
|
|
112
|
+
sampler_info,
|
|
113
|
+
allowed: [:type],
|
|
114
|
+
context: "sampler binding layout"
|
|
115
|
+
)
|
|
116
|
+
entry[:sampler][:type] = Native::EnumHelper.coerce(
|
|
117
|
+
Native::SamplerBindingType,
|
|
118
|
+
sampler_info[:type] || :filtering,
|
|
119
|
+
name: "sampler binding type"
|
|
120
|
+
)
|
|
89
121
|
end
|
|
90
122
|
|
|
91
123
|
if entry_hash[:texture]
|
|
92
124
|
texture_info = entry_hash[:texture]
|
|
93
|
-
|
|
94
|
-
|
|
125
|
+
DescriptorHelpers.validate_keys!(
|
|
126
|
+
texture_info,
|
|
127
|
+
allowed: %i[sample_type view_dimension multisampled],
|
|
128
|
+
context: "texture binding layout"
|
|
129
|
+
)
|
|
130
|
+
entry[:texture][:sample_type] = Native::EnumHelper.coerce(
|
|
131
|
+
Native::TextureSampleType,
|
|
132
|
+
texture_info[:sample_type] || :float,
|
|
133
|
+
name: "texture sample type"
|
|
134
|
+
)
|
|
135
|
+
entry[:texture][:view_dimension] = Native::EnumHelper.coerce(
|
|
136
|
+
Native::TextureViewDimension,
|
|
137
|
+
texture_info[:view_dimension] || :d2,
|
|
138
|
+
name: "texture view dimension"
|
|
139
|
+
)
|
|
95
140
|
entry[:texture][:multisampled] = texture_info[:multisampled] ? 1 : 0
|
|
96
141
|
end
|
|
97
142
|
|
|
98
143
|
if entry_hash[:storage_texture]
|
|
99
144
|
st_info = entry_hash[:storage_texture]
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
145
|
+
DescriptorHelpers.validate_keys!(
|
|
146
|
+
st_info,
|
|
147
|
+
allowed: %i[access format view_dimension],
|
|
148
|
+
required: [:format],
|
|
149
|
+
context: "storage texture binding layout"
|
|
150
|
+
)
|
|
151
|
+
entry[:storage_texture][:access] = Native::EnumHelper.coerce(
|
|
152
|
+
Native::StorageTextureAccess,
|
|
153
|
+
st_info[:access] || :write_only,
|
|
154
|
+
name: "storage texture access"
|
|
155
|
+
)
|
|
156
|
+
entry[:storage_texture][:format] = Native::EnumHelper.coerce(
|
|
157
|
+
Native::TextureFormat,
|
|
158
|
+
st_info.fetch(:format),
|
|
159
|
+
name: "storage texture format"
|
|
160
|
+
)
|
|
161
|
+
entry[:storage_texture][:view_dimension] = Native::EnumHelper.coerce(
|
|
162
|
+
Native::TextureViewDimension,
|
|
163
|
+
st_info[:view_dimension] || :d2,
|
|
164
|
+
name: "texture view dimension"
|
|
165
|
+
)
|
|
103
166
|
end
|
|
104
167
|
|
|
105
168
|
entry
|
|
106
169
|
end
|
|
107
170
|
|
|
108
171
|
def normalize_visibility(visibility)
|
|
109
|
-
|
|
110
|
-
when Integer
|
|
111
|
-
visibility
|
|
112
|
-
when Symbol
|
|
113
|
-
Native::ShaderStage[visibility]
|
|
114
|
-
when Array
|
|
115
|
-
visibility.reduce(0) { |acc, v| acc | Native::ShaderStage[v] }
|
|
116
|
-
else
|
|
117
|
-
raise ArgumentError, "Invalid visibility: #{visibility}"
|
|
118
|
-
end
|
|
172
|
+
Native::EnumHelper.coerce_flags(Native::ShaderStage, visibility, name: "shader visibility")
|
|
119
173
|
end
|
|
120
174
|
end
|
|
121
175
|
end
|
|
@@ -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
|