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
|
@@ -7,36 +7,38 @@ module WGPU
|
|
|
7
7
|
def initialize(device, label: nil, size:, usage:, mapped_at_creation: false)
|
|
8
8
|
@device = device
|
|
9
9
|
@size = size
|
|
10
|
-
@usage =
|
|
10
|
+
@usage =
|
|
11
|
+
begin
|
|
12
|
+
normalize_usage(usage)
|
|
13
|
+
rescue ArgumentError => e
|
|
14
|
+
raise BufferError, buffer_error_message(e.message, label)
|
|
15
|
+
end
|
|
11
16
|
@mapped = mapped_at_creation
|
|
12
|
-
@
|
|
17
|
+
@map_state = mapped_at_creation ? :mapped : :unmapped
|
|
13
18
|
|
|
14
|
-
desc =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
desc[:label][:data] = nil
|
|
22
|
-
desc[:label][:length] = 0
|
|
23
|
-
end
|
|
24
|
-
desc[:usage] = @usage
|
|
25
|
-
desc[:size] = size
|
|
26
|
-
desc[:mapped_at_creation] = mapped_at_creation ? 1 : 0
|
|
19
|
+
desc, keepalive = build_descriptor(
|
|
20
|
+
label:,
|
|
21
|
+
size:,
|
|
22
|
+
usage: @usage,
|
|
23
|
+
mapped_at_creation:
|
|
24
|
+
)
|
|
25
|
+
@descriptor_keepalive = keepalive
|
|
27
26
|
|
|
28
27
|
device.push_error_scope(:validation)
|
|
29
28
|
@handle = Native.wgpuDeviceCreateBuffer(device.handle, desc)
|
|
30
29
|
error = device.pop_error_scope
|
|
30
|
+
@descriptor_keepalive = nil
|
|
31
31
|
|
|
32
32
|
if @handle.null? || (error[:type] && error[:type] != :no_error)
|
|
33
33
|
msg = error[:message] || "Failed to create buffer"
|
|
34
|
-
raise BufferError, msg
|
|
34
|
+
raise BufferError, buffer_error_message(msg, label)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def write(data, offset: 0)
|
|
39
|
-
ptr, byte_size =
|
|
38
|
+
def write(data, offset: 0, type: :f32)
|
|
39
|
+
ptr, byte_size = DataTypes.to_pointer(data, type:)
|
|
40
|
+
DataTypes.validate_alignment!(offset, 4, name: "offset")
|
|
41
|
+
DataTypes.validate_alignment!(byte_size, 4, name: "data size")
|
|
40
42
|
Native.wgpuQueueWriteBuffer(@device.queue.handle, @handle, offset, ptr, byte_size)
|
|
41
43
|
end
|
|
42
44
|
|
|
@@ -44,6 +46,7 @@ module WGPU
|
|
|
44
46
|
raise BufferError, "Buffer is not mapped" unless @mapped
|
|
45
47
|
|
|
46
48
|
size ||= @size - offset
|
|
49
|
+
validate_map_range!(offset, size)
|
|
47
50
|
ptr = Native.wgpuBufferGetMappedRange(@handle, offset, size)
|
|
48
51
|
raise BufferError, "Failed to get mapped range" if ptr.null?
|
|
49
52
|
|
|
@@ -57,23 +60,24 @@ module WGPU
|
|
|
57
60
|
def unmap
|
|
58
61
|
Native.wgpuBufferUnmap(@handle)
|
|
59
62
|
@mapped = false
|
|
63
|
+
@map_state = :unmapped
|
|
60
64
|
end
|
|
61
65
|
|
|
62
|
-
def map_sync(mode, offset: 0, size: nil)
|
|
63
|
-
status_holder,
|
|
64
|
-
wait_for_map(status_holder, future)
|
|
66
|
+
def map_sync(mode, offset: 0, size: nil, timeout: nil)
|
|
67
|
+
status_holder, callback_token, future = begin_map_request(mode, offset: offset, size: size)
|
|
68
|
+
wait_for_map(status_holder, future, timeout:)
|
|
65
69
|
finalize_map(status_holder)
|
|
66
70
|
ensure
|
|
67
|
-
|
|
71
|
+
CallbackKeepalive.release(self, callback_token)
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
def map_async(mode, offset: 0, size: nil)
|
|
71
|
-
status_holder,
|
|
75
|
+
status_holder, callback_token, future = begin_map_request(mode, offset: offset, size: size)
|
|
72
76
|
AsyncTask.new do
|
|
73
77
|
wait_for_map(status_holder, future)
|
|
74
78
|
finalize_map(status_holder)
|
|
75
79
|
ensure
|
|
76
|
-
|
|
80
|
+
CallbackKeepalive.release(self, callback_token)
|
|
77
81
|
end
|
|
78
82
|
end
|
|
79
83
|
|
|
@@ -81,20 +85,23 @@ module WGPU
|
|
|
81
85
|
raise BufferError, "Buffer is not mapped" unless @mapped
|
|
82
86
|
|
|
83
87
|
size ||= @size - offset
|
|
88
|
+
validate_map_range!(offset, size)
|
|
84
89
|
ptr = Native.wgpuBufferGetConstMappedRange(@handle, offset, size)
|
|
85
90
|
raise BufferError, "Failed to get mapped range" if ptr.null?
|
|
86
91
|
|
|
87
92
|
ptr.read_bytes(size)
|
|
88
93
|
end
|
|
89
94
|
|
|
90
|
-
def read_mapped(offset: 0, size: nil)
|
|
91
|
-
read_mapped_data(offset: offset, size: size)
|
|
95
|
+
def read_mapped(offset: 0, size: nil, type: nil)
|
|
96
|
+
bytes = read_mapped_data(offset: offset, size: size)
|
|
97
|
+
type ? DataTypes.unpack(bytes, type:) : bytes
|
|
92
98
|
end
|
|
93
99
|
|
|
94
|
-
def write_mapped(data, offset: 0)
|
|
100
|
+
def write_mapped(data, offset: 0, type: :f32)
|
|
95
101
|
raise BufferError, "Buffer is not mapped" unless @mapped
|
|
96
102
|
|
|
97
|
-
ptr, byte_size =
|
|
103
|
+
ptr, byte_size = DataTypes.to_pointer(data, type:)
|
|
104
|
+
validate_map_range!(offset, byte_size)
|
|
98
105
|
target = Native.wgpuBufferGetMappedRange(@handle, offset, byte_size)
|
|
99
106
|
raise BufferError, "Failed to get mapped range" if target.null?
|
|
100
107
|
|
|
@@ -102,18 +109,39 @@ module WGPU
|
|
|
102
109
|
end
|
|
103
110
|
|
|
104
111
|
def read_mapped_floats(offset: 0, count: nil)
|
|
105
|
-
|
|
112
|
+
read_mapped_values(type: :f32, offset:, count:)
|
|
113
|
+
end
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
def read_mapped_uint32s(offset: 0, count: nil)
|
|
116
|
+
read_mapped_values(type: :u32, offset:, count:)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def read_mapped_int32s(offset: 0, count: nil)
|
|
120
|
+
read_mapped_values(type: :i32, offset:, count:)
|
|
121
|
+
end
|
|
110
122
|
|
|
111
|
-
|
|
123
|
+
def read_mapped_float64s(offset: 0, count: nil)
|
|
124
|
+
read_mapped_values(type: :f64, offset:, count:)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def read_mapped_uint16s(offset: 0, count: nil)
|
|
128
|
+
read_mapped_values(type: :u16, offset:, count:)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def read_mapped_uint8s(offset: 0, count: nil)
|
|
132
|
+
read_mapped_values(type: :u8, offset:, count:)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def read_mapped_values(type: :f32, offset: 0, count: nil)
|
|
136
|
+
element_size = DataTypes.byte_size(type)
|
|
137
|
+
size = count ? count * element_size : @size - offset
|
|
138
|
+
DataTypes.unpack(read_mapped_data(offset:, size:), type:)
|
|
112
139
|
end
|
|
113
140
|
|
|
114
141
|
def map_state
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
return @map_state unless Native.buffer_map_state_available?
|
|
143
|
+
|
|
144
|
+
Native.wgpuBufferGetMapState(@handle) || @map_state
|
|
117
145
|
end
|
|
118
146
|
|
|
119
147
|
def destroy
|
|
@@ -128,14 +156,21 @@ module WGPU
|
|
|
128
156
|
|
|
129
157
|
private
|
|
130
158
|
|
|
159
|
+
def build_descriptor(label:, size:, usage:, mapped_at_creation:)
|
|
160
|
+
keepalive = []
|
|
161
|
+
desc = Native::BufferDescriptor.new
|
|
162
|
+
desc[:next_in_chain] = nil
|
|
163
|
+
DescriptorHelpers.set_label(desc, label, keepalive:)
|
|
164
|
+
desc[:usage] = usage
|
|
165
|
+
desc[:size] = size
|
|
166
|
+
desc[:mapped_at_creation] = mapped_at_creation ? 1 : 0
|
|
167
|
+
[desc, keepalive]
|
|
168
|
+
end
|
|
169
|
+
|
|
131
170
|
def begin_map_request(mode, offset:, size:)
|
|
132
171
|
size ||= @size - offset
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
when :write then Native::MapMode[:write]
|
|
136
|
-
when Integer then mode
|
|
137
|
-
else raise ArgumentError, "Invalid map mode: #{mode}"
|
|
138
|
-
end
|
|
172
|
+
validate_map_range!(offset, size)
|
|
173
|
+
mode_flag = Native::EnumHelper.coerce(Native::MapMode, mode, name: "map mode")
|
|
139
174
|
|
|
140
175
|
status_holder = { done: false, status: nil, message: nil }
|
|
141
176
|
callback = FFI::Function.new(:void, [:uint32, Native::StringView.by_value, :pointer, :pointer]) do |status, message, _userdata1, _userdata2|
|
|
@@ -145,7 +180,7 @@ module WGPU
|
|
|
145
180
|
status_holder[:message] = message[:data].read_string(message[:length])
|
|
146
181
|
end
|
|
147
182
|
end
|
|
148
|
-
|
|
183
|
+
callback_token = CallbackKeepalive.retain(self, callback)
|
|
149
184
|
|
|
150
185
|
callback_info = Native::BufferMapCallbackInfo.new
|
|
151
186
|
callback_info[:next_in_chain] = nil
|
|
@@ -155,24 +190,28 @@ module WGPU
|
|
|
155
190
|
callback_info[:userdata2] = nil
|
|
156
191
|
|
|
157
192
|
future = Native.wgpuBufferMapAsync(@handle, mode_flag, offset, size, callback_info)
|
|
193
|
+
@map_state = :pending
|
|
158
194
|
|
|
159
|
-
[status_holder,
|
|
195
|
+
[status_holder, callback_token, future]
|
|
160
196
|
end
|
|
161
197
|
|
|
162
|
-
def wait_for_map(status_holder, future)
|
|
198
|
+
def wait_for_map(status_holder, future, timeout: nil)
|
|
163
199
|
AsyncWaiter.wait(
|
|
164
200
|
status_holder: status_holder,
|
|
165
201
|
instance: @device.adapter&.instance,
|
|
166
202
|
device: @device,
|
|
167
|
-
future: future
|
|
203
|
+
future: future,
|
|
204
|
+
timeout: timeout
|
|
168
205
|
)
|
|
169
206
|
end
|
|
170
207
|
|
|
171
208
|
def finalize_map(status_holder)
|
|
172
209
|
if status_holder[:status] == :success
|
|
173
210
|
@mapped = true
|
|
211
|
+
@map_state = :mapped
|
|
174
212
|
true
|
|
175
213
|
else
|
|
214
|
+
@map_state = :unmapped
|
|
176
215
|
detail = status_holder[:message]
|
|
177
216
|
base = "Failed to map buffer: #{status_holder[:status]}"
|
|
178
217
|
raise BufferError, detail && !detail.empty? ? "#{base} (#{detail})" : base
|
|
@@ -180,33 +219,17 @@ module WGPU
|
|
|
180
219
|
end
|
|
181
220
|
|
|
182
221
|
def normalize_usage(usage)
|
|
183
|
-
|
|
184
|
-
when Integer
|
|
185
|
-
usage
|
|
186
|
-
when Symbol
|
|
187
|
-
Native::BufferUsage[usage]
|
|
188
|
-
when Array
|
|
189
|
-
usage.reduce(0) { |acc, u| acc | Native::BufferUsage[u] }
|
|
190
|
-
else
|
|
191
|
-
raise ArgumentError, "Invalid usage type: #{usage.class}"
|
|
192
|
-
end
|
|
222
|
+
Native::EnumHelper.coerce_flags(Native::BufferUsage, usage, name: "buffer usage")
|
|
193
223
|
end
|
|
194
224
|
|
|
195
|
-
def
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
ptr.write_array_of_float(data)
|
|
204
|
-
[ptr, data.size * 4]
|
|
205
|
-
when FFI::Pointer
|
|
206
|
-
[data, data.size]
|
|
207
|
-
else
|
|
208
|
-
raise ArgumentError, "Unsupported data type: #{data.class}"
|
|
209
|
-
end
|
|
225
|
+
def buffer_error_message(message, label)
|
|
226
|
+
context = label ? " #{label.inspect}" : ""
|
|
227
|
+
"create buffer#{context}: #{message}"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def validate_map_range!(offset, size)
|
|
231
|
+
DataTypes.validate_alignment!(offset, 8, name: "map offset")
|
|
232
|
+
DataTypes.validate_alignment!(size, 4, name: "map size")
|
|
210
233
|
end
|
|
211
234
|
end
|
|
212
235
|
|
|
@@ -217,12 +240,64 @@ module WGPU
|
|
|
217
240
|
end
|
|
218
241
|
|
|
219
242
|
def read_floats(count = nil)
|
|
220
|
-
|
|
221
|
-
@pointer.read_array_of_float(count)
|
|
243
|
+
read(type: :f32, count:)
|
|
222
244
|
end
|
|
223
245
|
|
|
224
246
|
def write_floats(data)
|
|
225
|
-
|
|
247
|
+
write(data, type: :f32)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def read_uint32s(count = nil)
|
|
251
|
+
read(type: :u32, count:)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def write_uint32s(data)
|
|
255
|
+
write(data, type: :u32)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def read_int32s(count = nil)
|
|
259
|
+
read(type: :i32, count:)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def write_int32s(data)
|
|
263
|
+
write(data, type: :i32)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def read_float64s(count = nil)
|
|
267
|
+
read(type: :f64, count:)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def write_float64s(data)
|
|
271
|
+
write(data, type: :f64)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def read_uint16s(count = nil)
|
|
275
|
+
read(type: :u16, count:)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def write_uint16s(data)
|
|
279
|
+
write(data, type: :u16)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def read_uint8s(count = nil)
|
|
283
|
+
read(type: :u8, count:)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def write_uint8s(data)
|
|
287
|
+
write(data, type: :u8)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def read(type: :f32, count: nil)
|
|
291
|
+
byte_size = DataTypes.byte_size(type)
|
|
292
|
+
count ||= @size / byte_size
|
|
293
|
+
DataTypes.unpack(@pointer.read_bytes(count * byte_size), type:)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def write(data, type: :f32)
|
|
297
|
+
bytes = data.is_a?(String) ? data : DataTypes.pack(data, type:)
|
|
298
|
+
raise ArgumentError, "data exceeds mapped range" if bytes.bytesize > @size
|
|
299
|
+
|
|
300
|
+
@pointer.put_bytes(0, bytes)
|
|
226
301
|
end
|
|
227
302
|
|
|
228
303
|
def read_bytes
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module WGPU
|
|
4
4
|
class QuerySet
|
|
5
|
-
attr_reader :handle
|
|
5
|
+
attr_reader :handle, :count, :type
|
|
6
6
|
|
|
7
7
|
def initialize(device, label: nil, type:, count:)
|
|
8
8
|
@device = device
|
|
9
|
+
@count = Integer(count)
|
|
10
|
+
type_value = Native::EnumHelper.coerce(Native::QueryType, type, name: "query type")
|
|
11
|
+
@type = Native::QueryType[type_value]
|
|
12
|
+
@destroyed = false
|
|
9
13
|
|
|
10
14
|
desc = Native::QuerySetDescriptor.new
|
|
11
15
|
desc[:next_in_chain] = nil
|
|
@@ -17,28 +21,26 @@ module WGPU
|
|
|
17
21
|
desc[:label][:data] = nil
|
|
18
22
|
desc[:label][:length] = 0
|
|
19
23
|
end
|
|
20
|
-
desc[:type] =
|
|
21
|
-
desc[:count] = count
|
|
24
|
+
desc[:type] = type_value
|
|
25
|
+
desc[:count] = @count
|
|
22
26
|
|
|
23
27
|
@handle = Native.wgpuDeviceCreateQuerySet(device.handle, desc)
|
|
24
28
|
raise ResourceError, "Failed to create query set" if @handle.null?
|
|
25
29
|
end
|
|
26
30
|
|
|
27
|
-
def count
|
|
28
|
-
Native.wgpuQuerySetGetCount(@handle)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def type
|
|
32
|
-
Native.wgpuQuerySetGetType(@handle)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
31
|
def destroy
|
|
32
|
+
return if @destroyed
|
|
33
|
+
|
|
36
34
|
Native.wgpuQuerySetDestroy(@handle)
|
|
35
|
+
@destroyed = true
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
def release
|
|
40
39
|
return if @handle.null?
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
# Pinned wgpu-native v27 removes a destroyed query set from its storage;
|
|
42
|
+
# calling Release afterward aborts inside Rust instead of being harmless.
|
|
43
|
+
Native.wgpuQuerySetRelease(@handle) unless @destroyed
|
|
42
44
|
@handle = FFI::Pointer::NULL
|
|
43
45
|
end
|
|
44
46
|
end
|
|
@@ -7,30 +7,25 @@ module WGPU
|
|
|
7
7
|
def initialize(device, label: nil, address_mode_u: :clamp_to_edge, address_mode_v: :clamp_to_edge, address_mode_w: :clamp_to_edge, mag_filter: :nearest, min_filter: :nearest, mipmap_filter: :nearest, lod_min_clamp: 0.0, lod_max_clamp: 32.0, compare: nil, max_anisotropy: 1)
|
|
8
8
|
@device = device
|
|
9
9
|
|
|
10
|
-
desc =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
desc[:min_filter] = min_filter
|
|
25
|
-
desc[:mipmap_filter] = mipmap_filter
|
|
26
|
-
desc[:lod_min_clamp] = lod_min_clamp
|
|
27
|
-
desc[:lod_max_clamp] = lod_max_clamp
|
|
28
|
-
desc[:compare] = compare || :undefined
|
|
29
|
-
desc[:max_anisotropy] = max_anisotropy
|
|
10
|
+
desc, keepalive = build_descriptor(
|
|
11
|
+
label:,
|
|
12
|
+
address_mode_u:,
|
|
13
|
+
address_mode_v:,
|
|
14
|
+
address_mode_w:,
|
|
15
|
+
mag_filter:,
|
|
16
|
+
min_filter:,
|
|
17
|
+
mipmap_filter:,
|
|
18
|
+
lod_min_clamp:,
|
|
19
|
+
lod_max_clamp:,
|
|
20
|
+
compare:,
|
|
21
|
+
max_anisotropy:
|
|
22
|
+
)
|
|
23
|
+
@descriptor_keepalive = keepalive
|
|
30
24
|
|
|
31
25
|
device.push_error_scope(:validation)
|
|
32
26
|
@handle = Native.wgpuDeviceCreateSampler(device.handle, desc)
|
|
33
27
|
error = device.pop_error_scope
|
|
28
|
+
@descriptor_keepalive = nil
|
|
34
29
|
|
|
35
30
|
if @handle.null? || (error[:type] && error[:type] != :no_error)
|
|
36
31
|
msg = error[:message] || "Failed to create sampler"
|
|
@@ -43,5 +38,26 @@ module WGPU
|
|
|
43
38
|
Native.wgpuSamplerRelease(@handle)
|
|
44
39
|
@handle = FFI::Pointer::NULL
|
|
45
40
|
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def build_descriptor(label:, address_mode_u:, address_mode_v:, address_mode_w:, mag_filter:, min_filter:,
|
|
45
|
+
mipmap_filter:, lod_min_clamp:, lod_max_clamp:, compare:, max_anisotropy:)
|
|
46
|
+
keepalive = []
|
|
47
|
+
desc = Native::SamplerDescriptor.new
|
|
48
|
+
desc[:next_in_chain] = nil
|
|
49
|
+
DescriptorHelpers.set_label(desc, label, keepalive:)
|
|
50
|
+
desc[:address_mode_u] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_u, name: "address mode")
|
|
51
|
+
desc[:address_mode_v] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_v, name: "address mode")
|
|
52
|
+
desc[:address_mode_w] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_w, name: "address mode")
|
|
53
|
+
desc[:mag_filter] = Native::EnumHelper.coerce(Native::FilterMode, mag_filter, name: "mag filter")
|
|
54
|
+
desc[:min_filter] = Native::EnumHelper.coerce(Native::FilterMode, min_filter, name: "min filter")
|
|
55
|
+
desc[:mipmap_filter] = Native::EnumHelper.coerce(Native::MipmapFilterMode, mipmap_filter, name: "mipmap filter")
|
|
56
|
+
desc[:lod_min_clamp] = lod_min_clamp
|
|
57
|
+
desc[:lod_max_clamp] = lod_max_clamp
|
|
58
|
+
desc[:compare] = Native::EnumHelper.coerce(Native::CompareFunction, compare || :undefined, name: "compare function")
|
|
59
|
+
desc[:max_anisotropy] = max_anisotropy
|
|
60
|
+
[desc, keepalive]
|
|
61
|
+
end
|
|
46
62
|
end
|
|
47
63
|
end
|
|
@@ -2,45 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
module WGPU
|
|
4
4
|
class Texture
|
|
5
|
-
attr_reader :handle
|
|
5
|
+
attr_reader :handle, :surface_status
|
|
6
6
|
|
|
7
7
|
def initialize(device, label: nil, size:, format:, usage:, dimension: :d2, mip_level_count: 1, sample_count: 1, view_formats: [])
|
|
8
8
|
@device = device
|
|
9
9
|
|
|
10
|
-
desc =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
desc[:dimension] = dimension
|
|
22
|
-
desc[:size][:width] = size[:width] || size[0]
|
|
23
|
-
desc[:size][:height] = size[:height] || size[1] || 1
|
|
24
|
-
desc[:size][:depth_or_array_layers] = size[:depth_or_array_layers] || size[2] || 1
|
|
25
|
-
desc[:format] = format
|
|
26
|
-
desc[:mip_level_count] = mip_level_count
|
|
27
|
-
desc[:sample_count] = sample_count
|
|
28
|
-
desc[:view_format_count] = view_formats.size
|
|
29
|
-
if view_formats.empty?
|
|
30
|
-
@view_formats_ptr = nil
|
|
31
|
-
desc[:view_formats] = nil
|
|
32
|
-
else
|
|
33
|
-
format_values = view_formats.map do |vf|
|
|
34
|
-
vf.is_a?(Integer) ? vf : Native::TextureFormat[vf]
|
|
35
|
-
end
|
|
36
|
-
@view_formats_ptr = FFI::MemoryPointer.new(:uint32, format_values.size)
|
|
37
|
-
@view_formats_ptr.write_array_of_uint32(format_values)
|
|
38
|
-
desc[:view_formats] = @view_formats_ptr
|
|
39
|
-
end
|
|
10
|
+
desc, keepalive = build_descriptor(
|
|
11
|
+
label:,
|
|
12
|
+
size:,
|
|
13
|
+
format:,
|
|
14
|
+
usage:,
|
|
15
|
+
dimension:,
|
|
16
|
+
mip_level_count:,
|
|
17
|
+
sample_count:,
|
|
18
|
+
view_formats:
|
|
19
|
+
)
|
|
20
|
+
@descriptor_keepalive = keepalive
|
|
40
21
|
|
|
41
22
|
device.push_error_scope(:validation)
|
|
42
23
|
@handle = Native.wgpuDeviceCreateTexture(device.handle, desc)
|
|
43
24
|
error = device.pop_error_scope
|
|
25
|
+
@descriptor_keepalive = nil
|
|
44
26
|
|
|
45
27
|
if @handle.null? || (error[:type] && error[:type] != :no_error)
|
|
46
28
|
msg = error[:message] || "Failed to create texture"
|
|
@@ -48,10 +30,11 @@ module WGPU
|
|
|
48
30
|
end
|
|
49
31
|
end
|
|
50
32
|
|
|
51
|
-
def self.from_handle(handle)
|
|
33
|
+
def self.from_handle(handle, surface_status: nil)
|
|
52
34
|
texture = allocate
|
|
53
35
|
texture.instance_variable_set(:@handle, handle)
|
|
54
36
|
texture.instance_variable_set(:@device, nil)
|
|
37
|
+
texture.instance_variable_set(:@surface_status, surface_status)
|
|
55
38
|
texture
|
|
56
39
|
end
|
|
57
40
|
|
|
@@ -120,17 +103,36 @@ module WGPU
|
|
|
120
103
|
|
|
121
104
|
private
|
|
122
105
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
106
|
+
def build_descriptor(label:, size:, format:, usage:, dimension:, mip_level_count:, sample_count:, view_formats:)
|
|
107
|
+
keepalive = []
|
|
108
|
+
DescriptorHelpers.validate_keys!(
|
|
109
|
+
size,
|
|
110
|
+
allowed: [:width, :height, :depth_or_array_layers],
|
|
111
|
+
required: [:width],
|
|
112
|
+
context: "texture size"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
desc = Native::TextureDescriptor.new
|
|
116
|
+
desc[:next_in_chain] = nil
|
|
117
|
+
DescriptorHelpers.set_label(desc, label, keepalive:)
|
|
118
|
+
desc[:usage] = normalize_usage(usage)
|
|
119
|
+
desc[:dimension] = Native::EnumHelper.coerce(Native::TextureDimension, dimension, name: "texture dimension")
|
|
120
|
+
desc[:size][:width] = size[:width] || size[0]
|
|
121
|
+
desc[:size][:height] = size[:height] || size[1] || 1
|
|
122
|
+
desc[:size][:depth_or_array_layers] = size[:depth_or_array_layers] || size[2] || 1
|
|
123
|
+
desc[:format] = Native::EnumHelper.coerce(Native::TextureFormat, format, name: "texture format")
|
|
124
|
+
desc[:mip_level_count] = mip_level_count
|
|
125
|
+
desc[:sample_count] = sample_count
|
|
126
|
+
desc[:view_format_count] = view_formats.size
|
|
127
|
+
format_values = view_formats.map do |view_format|
|
|
128
|
+
Native::EnumHelper.coerce(Native::TextureFormat, view_format, name: "view format")
|
|
133
129
|
end
|
|
130
|
+
desc[:view_formats] = DescriptorHelpers.uint32_array(format_values, keepalive:)
|
|
131
|
+
[desc, keepalive]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def normalize_usage(usage)
|
|
135
|
+
Native::EnumHelper.coerce_flags(Native::TextureUsage, usage, name: "texture usage")
|
|
134
136
|
end
|
|
135
137
|
end
|
|
136
138
|
end
|
|
@@ -17,13 +17,21 @@ module WGPU
|
|
|
17
17
|
desc[:label][:data] = nil
|
|
18
18
|
desc[:label][:length] = 0
|
|
19
19
|
end
|
|
20
|
-
desc[:format] =
|
|
21
|
-
|
|
20
|
+
desc[:format] = Native::EnumHelper.coerce(
|
|
21
|
+
Native::TextureFormat,
|
|
22
|
+
format || :undefined,
|
|
23
|
+
name: "texture view format"
|
|
24
|
+
)
|
|
25
|
+
desc[:dimension] = Native::EnumHelper.coerce(
|
|
26
|
+
Native::TextureViewDimension,
|
|
27
|
+
dimension || :undefined,
|
|
28
|
+
name: "texture view dimension"
|
|
29
|
+
)
|
|
22
30
|
desc[:base_mip_level] = base_mip_level
|
|
23
31
|
desc[:mip_level_count] = mip_level_count || 0xFFFFFFFF
|
|
24
32
|
desc[:base_array_layer] = base_array_layer
|
|
25
33
|
desc[:array_layer_count] = array_layer_count || 0xFFFFFFFF
|
|
26
|
-
desc[:aspect] = aspect
|
|
34
|
+
desc[:aspect] = Native::EnumHelper.coerce(Native::TextureAspect, aspect, name: "texture aspect")
|
|
27
35
|
|
|
28
36
|
@handle = Native.wgpuTextureCreateView(texture.handle, desc)
|
|
29
37
|
raise ResourceError, "Failed to create texture view" if @handle.null?
|