wgpu 1.1.0 → 1.2.1
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 +88 -0
- data/README.md +44 -5
- data/docs/README.md +22 -0
- data/docs/api_coverage.md +140 -0
- data/docs/async.md +41 -0
- data/docs/bind_groups.md +25 -0
- data/docs/buffer_data.md +37 -0
- data/docs/command_encoding.md +49 -0
- data/docs/errors.md +40 -0
- data/docs/getting_started_compute.md +62 -0
- data/docs/getting_started_rendering.md +62 -0
- data/docs/installation.md +94 -0
- data/docs/pipeline_descriptors.md +43 -0
- data/docs/releasing.md +32 -0
- data/docs/resource_lifetime.md +108 -0
- data/docs/shaders.md +32 -0
- data/docs/texture_readback.md +46 -0
- data/docs/troubleshooting.md +53 -0
- data/docs/upgrading_wgpu_native.md +37 -0
- data/ext/wgpu/extconf.rb +10 -142
- data/lib/wgpu/async_task.rb +19 -0
- data/lib/wgpu/commands/command_buffer.rb +25 -1
- data/lib/wgpu/commands/command_encoder.rb +123 -9
- data/lib/wgpu/commands/compute_pass.rb +53 -0
- data/lib/wgpu/commands/render_bundle.rb +9 -1
- data/lib/wgpu/commands/render_bundle_encoder.rb +65 -4
- data/lib/wgpu/commands/render_pass.rb +136 -8
- data/lib/wgpu/core/adapter.rb +123 -19
- data/lib/wgpu/core/async_waiter.rb +47 -4
- data/lib/wgpu/core/canvas_context.rb +32 -2
- data/lib/wgpu/core/device.rb +399 -53
- data/lib/wgpu/core/instance.rb +28 -4
- data/lib/wgpu/core/queue.rb +197 -51
- data/lib/wgpu/core/surface.rb +64 -17
- data/lib/wgpu/data_types.rb +83 -0
- data/lib/wgpu/descriptor_helpers.rb +104 -0
- data/lib/wgpu/error.rb +70 -0
- data/lib/wgpu/logging.rb +63 -0
- data/lib/wgpu/native/abi_verifier.rb +143 -0
- data/lib/wgpu/native/callbacks.rb +10 -1
- data/lib/wgpu/native/capabilities.rb +32 -2
- data/lib/wgpu/native/distribution.rb +176 -0
- data/lib/wgpu/native/enum_helper.rb +80 -0
- data/lib/wgpu/native/enums.rb +68 -8
- data/lib/wgpu/native/fixtures/webgpu-v27.0.4.0-enums.h +848 -0
- data/lib/wgpu/native/functions.rb +21 -10
- data/lib/wgpu/native/installer.rb +223 -0
- data/lib/wgpu/native/loader.rb +61 -21
- data/lib/wgpu/native/structs.rb +18 -1
- data/lib/wgpu/native_resource.rb +342 -0
- data/lib/wgpu/pipeline/bind_group.rb +21 -0
- data/lib/wgpu/pipeline/bind_group_layout.rb +108 -41
- data/lib/wgpu/pipeline/compute_pipeline.rb +40 -50
- data/lib/wgpu/pipeline/pipeline_layout.rb +8 -0
- data/lib/wgpu/pipeline/render_pipeline.rb +207 -110
- data/lib/wgpu/pipeline/shader_module.rb +95 -28
- data/lib/wgpu/resources/buffer.rb +387 -85
- data/lib/wgpu/resources/query_set.rb +26 -12
- data/lib/wgpu/resources/sampler.rb +43 -20
- data/lib/wgpu/resources/texture.rb +89 -48
- data/lib/wgpu/resources/texture_view.rb +35 -7
- data/lib/wgpu/texture_format.rb +96 -0
- data/lib/wgpu/version.rb +1 -1
- data/lib/wgpu/window.rb +34 -1
- data/lib/wgpu.rb +32 -0
- data/sig/wgpu.rbs +460 -0
- metadata +33 -16
|
@@ -4,33 +4,31 @@ module WGPU
|
|
|
4
4
|
class Sampler
|
|
5
5
|
attr_reader :handle
|
|
6
6
|
|
|
7
|
+
# Creates a texture sampler with address, filter, and comparison settings.
|
|
8
|
+
# @param device [Device] owning device
|
|
9
|
+
# @raise [ResourceError] if native validation or creation fails
|
|
7
10
|
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
11
|
@device = device
|
|
9
12
|
|
|
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
|
|
13
|
+
desc, keepalive = build_descriptor(
|
|
14
|
+
label:,
|
|
15
|
+
address_mode_u:,
|
|
16
|
+
address_mode_v:,
|
|
17
|
+
address_mode_w:,
|
|
18
|
+
mag_filter:,
|
|
19
|
+
min_filter:,
|
|
20
|
+
mipmap_filter:,
|
|
21
|
+
lod_min_clamp:,
|
|
22
|
+
lod_max_clamp:,
|
|
23
|
+
compare:,
|
|
24
|
+
max_anisotropy:
|
|
25
|
+
)
|
|
26
|
+
@descriptor_keepalive = keepalive
|
|
30
27
|
|
|
31
28
|
device.push_error_scope(:validation)
|
|
32
29
|
@handle = Native.wgpuDeviceCreateSampler(device.handle, desc)
|
|
33
30
|
error = device.pop_error_scope
|
|
31
|
+
@descriptor_keepalive = nil
|
|
34
32
|
|
|
35
33
|
if @handle.null? || (error[:type] && error[:type] != :no_error)
|
|
36
34
|
msg = error[:message] || "Failed to create sampler"
|
|
@@ -38,10 +36,35 @@ module WGPU
|
|
|
38
36
|
end
|
|
39
37
|
end
|
|
40
38
|
|
|
39
|
+
# Releases the native sampler handle.
|
|
40
|
+
#
|
|
41
|
+
# Calling this method more than once has no effect.
|
|
42
|
+
# @return [void]
|
|
41
43
|
def release
|
|
42
44
|
return if @handle.null?
|
|
43
45
|
Native.wgpuSamplerRelease(@handle)
|
|
44
46
|
@handle = FFI::Pointer::NULL
|
|
45
47
|
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def build_descriptor(label:, address_mode_u:, address_mode_v:, address_mode_w:, mag_filter:, min_filter:,
|
|
52
|
+
mipmap_filter:, lod_min_clamp:, lod_max_clamp:, compare:, max_anisotropy:)
|
|
53
|
+
keepalive = []
|
|
54
|
+
desc = Native::SamplerDescriptor.new
|
|
55
|
+
desc[:next_in_chain] = nil
|
|
56
|
+
DescriptorHelpers.set_label(desc, label, keepalive:)
|
|
57
|
+
desc[:address_mode_u] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_u, name: "address mode")
|
|
58
|
+
desc[:address_mode_v] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_v, name: "address mode")
|
|
59
|
+
desc[:address_mode_w] = Native::EnumHelper.coerce(Native::AddressMode, address_mode_w, name: "address mode")
|
|
60
|
+
desc[:mag_filter] = Native::EnumHelper.coerce(Native::FilterMode, mag_filter, name: "mag filter")
|
|
61
|
+
desc[:min_filter] = Native::EnumHelper.coerce(Native::FilterMode, min_filter, name: "min filter")
|
|
62
|
+
desc[:mipmap_filter] = Native::EnumHelper.coerce(Native::MipmapFilterMode, mipmap_filter, name: "mipmap filter")
|
|
63
|
+
desc[:lod_min_clamp] = lod_min_clamp
|
|
64
|
+
desc[:lod_max_clamp] = lod_max_clamp
|
|
65
|
+
desc[:compare] = Native::EnumHelper.coerce(Native::CompareFunction, compare || :undefined, name: "compare function")
|
|
66
|
+
desc[:max_anisotropy] = max_anisotropy
|
|
67
|
+
[desc, keepalive]
|
|
68
|
+
end
|
|
46
69
|
end
|
|
47
70
|
end
|
|
@@ -2,45 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
module WGPU
|
|
4
4
|
class Texture
|
|
5
|
-
attr_reader :handle
|
|
6
|
-
|
|
5
|
+
attr_reader :handle, :surface_status
|
|
6
|
+
|
|
7
|
+
# Creates a GPU texture.
|
|
8
|
+
# @param device [Device] owning device
|
|
9
|
+
# @param size [Hash, Array] texture extent
|
|
10
|
+
# @param format [Symbol, Integer] texel format
|
|
11
|
+
# @param usage [Symbol, Array<Symbol>, Integer] usage flags
|
|
12
|
+
# @raise [ResourceError] if native validation or creation fails
|
|
7
13
|
def initialize(device, label: nil, size:, format:, usage:, dimension: :d2, mip_level_count: 1, sample_count: 1, view_formats: [])
|
|
8
14
|
@device = device
|
|
9
15
|
|
|
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
|
|
16
|
+
desc, keepalive = build_descriptor(
|
|
17
|
+
label:,
|
|
18
|
+
size:,
|
|
19
|
+
format:,
|
|
20
|
+
usage:,
|
|
21
|
+
dimension:,
|
|
22
|
+
mip_level_count:,
|
|
23
|
+
sample_count:,
|
|
24
|
+
view_formats:
|
|
25
|
+
)
|
|
26
|
+
@descriptor_keepalive = keepalive
|
|
40
27
|
|
|
41
28
|
device.push_error_scope(:validation)
|
|
42
29
|
@handle = Native.wgpuDeviceCreateTexture(device.handle, desc)
|
|
43
30
|
error = device.pop_error_scope
|
|
31
|
+
@descriptor_keepalive = nil
|
|
44
32
|
|
|
45
33
|
if @handle.null? || (error[:type] && error[:type] != :no_error)
|
|
46
34
|
msg = error[:message] || "Failed to create texture"
|
|
@@ -48,14 +36,23 @@ module WGPU
|
|
|
48
36
|
end
|
|
49
37
|
end
|
|
50
38
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
# Wraps a native texture handle, such as a surface-acquired texture.
|
|
40
|
+
#
|
|
41
|
+
# @param handle [FFI::Pointer] native texture handle
|
|
42
|
+
# @param surface_status [Symbol, nil] acquisition status associated with the texture
|
|
43
|
+
# @param device [Device, nil] device whose callbacks the texture may use
|
|
44
|
+
# @return [Texture] adopted wrapper
|
|
45
|
+
def self.from_handle(handle, surface_status: nil, device: nil)
|
|
46
|
+
texture = adopt_native_handle(handle)
|
|
47
|
+
texture.instance_variable_set(:@device, device)
|
|
48
|
+
texture.instance_variable_set(:@surface_status, surface_status)
|
|
49
|
+
texture.send(:attach_device_callback_lifetime, device)
|
|
55
50
|
texture
|
|
56
51
|
end
|
|
57
52
|
|
|
58
|
-
|
|
53
|
+
# Creates a view into this texture.
|
|
54
|
+
# @return [TextureView]
|
|
55
|
+
def create_view(label: nil, format: nil, dimension: nil, base_mip_level: 0, mip_level_count: nil, base_array_layer: 0, array_layer_count: nil, aspect: :all, usage: nil)
|
|
59
56
|
TextureView.new(self,
|
|
60
57
|
label: label,
|
|
61
58
|
format: format,
|
|
@@ -64,14 +61,19 @@ module WGPU
|
|
|
64
61
|
mip_level_count: mip_level_count,
|
|
65
62
|
base_array_layer: base_array_layer,
|
|
66
63
|
array_layer_count: array_layer_count,
|
|
67
|
-
aspect: aspect
|
|
64
|
+
aspect: aspect,
|
|
65
|
+
usage: usage
|
|
68
66
|
)
|
|
69
67
|
end
|
|
70
68
|
|
|
69
|
+
# Returns the texture width in texels.
|
|
70
|
+
# @return [Integer]
|
|
71
71
|
def width
|
|
72
72
|
Native.wgpuTextureGetWidth(@handle)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
# Returns the texture extent.
|
|
76
|
+
# @return [Hash{Symbol => Integer}]
|
|
75
77
|
def size
|
|
76
78
|
{
|
|
77
79
|
width: width,
|
|
@@ -80,38 +82,58 @@ module WGPU
|
|
|
80
82
|
}
|
|
81
83
|
end
|
|
82
84
|
|
|
85
|
+
# Returns the texture height in texels.
|
|
86
|
+
# @return [Integer]
|
|
83
87
|
def height
|
|
84
88
|
Native.wgpuTextureGetHeight(@handle)
|
|
85
89
|
end
|
|
86
90
|
|
|
91
|
+
# Returns the texture depth or array layer count.
|
|
92
|
+
# @return [Integer]
|
|
87
93
|
def depth_or_array_layers
|
|
88
94
|
Native.wgpuTextureGetDepthOrArrayLayers(@handle)
|
|
89
95
|
end
|
|
90
96
|
|
|
97
|
+
# Returns the number of mip levels.
|
|
98
|
+
# @return [Integer]
|
|
91
99
|
def mip_level_count
|
|
92
100
|
Native.wgpuTextureGetMipLevelCount(@handle)
|
|
93
101
|
end
|
|
94
102
|
|
|
103
|
+
# Returns the multisample count.
|
|
104
|
+
# @return [Integer]
|
|
95
105
|
def sample_count
|
|
96
106
|
Native.wgpuTextureGetSampleCount(@handle)
|
|
97
107
|
end
|
|
98
108
|
|
|
109
|
+
# Returns the texture dimension.
|
|
110
|
+
# @return [Symbol, Integer]
|
|
99
111
|
def dimension
|
|
100
112
|
Native.wgpuTextureGetDimension(@handle)
|
|
101
113
|
end
|
|
102
114
|
|
|
115
|
+
# Returns the texture format.
|
|
116
|
+
# @return [Symbol, Integer]
|
|
103
117
|
def format
|
|
104
118
|
Native.wgpuTextureGetFormat(@handle)
|
|
105
119
|
end
|
|
106
120
|
|
|
121
|
+
# Returns the texture usage flags.
|
|
122
|
+
# @return [Integer]
|
|
107
123
|
def usage
|
|
108
124
|
Native.wgpuTextureGetUsage(@handle)
|
|
109
125
|
end
|
|
110
126
|
|
|
127
|
+
# Destroys the texture's storage.
|
|
128
|
+
# @return [void]
|
|
111
129
|
def destroy
|
|
112
130
|
Native.wgpuTextureDestroy(@handle)
|
|
113
131
|
end
|
|
114
132
|
|
|
133
|
+
# Releases the native texture handle.
|
|
134
|
+
#
|
|
135
|
+
# Calling this method more than once has no effect.
|
|
136
|
+
# @return [void]
|
|
115
137
|
def release
|
|
116
138
|
return if @handle.null?
|
|
117
139
|
Native.wgpuTextureRelease(@handle)
|
|
@@ -120,17 +142,36 @@ module WGPU
|
|
|
120
142
|
|
|
121
143
|
private
|
|
122
144
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
def build_descriptor(label:, size:, format:, usage:, dimension:, mip_level_count:, sample_count:, view_formats:)
|
|
146
|
+
keepalive = []
|
|
147
|
+
DescriptorHelpers.validate_keys!(
|
|
148
|
+
size,
|
|
149
|
+
allowed: [:width, :height, :depth_or_array_layers],
|
|
150
|
+
required: [:width],
|
|
151
|
+
context: "texture size"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
desc = Native::TextureDescriptor.new
|
|
155
|
+
desc[:next_in_chain] = nil
|
|
156
|
+
DescriptorHelpers.set_label(desc, label, keepalive:)
|
|
157
|
+
desc[:usage] = normalize_usage(usage)
|
|
158
|
+
desc[:dimension] = Native::EnumHelper.coerce(Native::TextureDimension, dimension, name: "texture dimension")
|
|
159
|
+
desc[:size][:width] = size[:width] || size[0]
|
|
160
|
+
desc[:size][:height] = size[:height] || size[1] || 1
|
|
161
|
+
desc[:size][:depth_or_array_layers] = size[:depth_or_array_layers] || size[2] || 1
|
|
162
|
+
desc[:format] = Native::EnumHelper.coerce(Native::TextureFormat, format, name: "texture format")
|
|
163
|
+
desc[:mip_level_count] = mip_level_count
|
|
164
|
+
desc[:sample_count] = sample_count
|
|
165
|
+
desc[:view_format_count] = view_formats.size
|
|
166
|
+
format_values = view_formats.map do |view_format|
|
|
167
|
+
Native::EnumHelper.coerce(Native::TextureFormat, view_format, name: "view format")
|
|
133
168
|
end
|
|
169
|
+
desc[:view_formats] = DescriptorHelpers.uint32_array(format_values, keepalive:)
|
|
170
|
+
[desc, keepalive]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def normalize_usage(usage)
|
|
174
|
+
Native::EnumHelper.coerce_flags(Native::TextureUsage, usage, name: "texture usage")
|
|
134
175
|
end
|
|
135
176
|
end
|
|
136
177
|
end
|
|
@@ -4,7 +4,10 @@ module WGPU
|
|
|
4
4
|
class TextureView
|
|
5
5
|
attr_reader :handle, :texture
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Creates a view selecting texture format, dimension, subresources, and aspect.
|
|
8
|
+
# @param texture [Texture] parent texture
|
|
9
|
+
# @raise [ResourceError] if native creation fails
|
|
10
|
+
def initialize(texture, label: nil, format: nil, dimension: nil, base_mip_level: 0, mip_level_count: nil, base_array_layer: 0, array_layer_count: nil, aspect: :all, usage: nil)
|
|
8
11
|
@texture = texture
|
|
9
12
|
|
|
10
13
|
desc = Native::TextureViewDescriptor.new
|
|
@@ -17,29 +20,54 @@ module WGPU
|
|
|
17
20
|
desc[:label][:data] = nil
|
|
18
21
|
desc[:label][:length] = 0
|
|
19
22
|
end
|
|
20
|
-
desc[:format] =
|
|
21
|
-
|
|
23
|
+
desc[:format] = Native::EnumHelper.coerce(
|
|
24
|
+
Native::TextureFormat,
|
|
25
|
+
format || :undefined,
|
|
26
|
+
name: "texture view format"
|
|
27
|
+
)
|
|
28
|
+
desc[:dimension] = Native::EnumHelper.coerce(
|
|
29
|
+
Native::TextureViewDimension,
|
|
30
|
+
dimension || :undefined,
|
|
31
|
+
name: "texture view dimension"
|
|
32
|
+
)
|
|
22
33
|
desc[:base_mip_level] = base_mip_level
|
|
23
34
|
desc[:mip_level_count] = mip_level_count || 0xFFFFFFFF
|
|
24
35
|
desc[:base_array_layer] = base_array_layer
|
|
25
36
|
desc[:array_layer_count] = array_layer_count || 0xFFFFFFFF
|
|
26
|
-
desc[:aspect] = aspect
|
|
37
|
+
desc[:aspect] = Native::EnumHelper.coerce(Native::TextureAspect, aspect, name: "texture aspect")
|
|
38
|
+
desc[:usage] = Native::EnumHelper.coerce_flags(
|
|
39
|
+
Native::TextureUsage,
|
|
40
|
+
usage || :none,
|
|
41
|
+
name: "texture view usage"
|
|
42
|
+
)
|
|
27
43
|
|
|
28
44
|
@handle = Native.wgpuTextureCreateView(texture.handle, desc)
|
|
29
45
|
raise ResourceError, "Failed to create texture view" if @handle.null?
|
|
30
46
|
end
|
|
31
47
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
# Wraps a native texture view handle owned by the caller.
|
|
49
|
+
#
|
|
50
|
+
# @param handle [FFI::Pointer] native texture view handle
|
|
51
|
+
# @param device [Device, nil] device whose callbacks the view may use
|
|
52
|
+
# @return [TextureView] adopted wrapper without a parent texture
|
|
53
|
+
def self.from_handle(handle, device: nil)
|
|
54
|
+
view = adopt_native_handle(handle)
|
|
35
55
|
view.instance_variable_set(:@texture, nil)
|
|
56
|
+
view.send(:attach_device_callback_lifetime, device)
|
|
36
57
|
view
|
|
37
58
|
end
|
|
38
59
|
|
|
60
|
+
# Returns the parent texture dimensions when known.
|
|
61
|
+
#
|
|
62
|
+
# @return [Hash, nil] texture extent, or +nil+ for an adopted standalone view
|
|
39
63
|
def size
|
|
40
64
|
@texture&.size
|
|
41
65
|
end
|
|
42
66
|
|
|
67
|
+
# Releases the native texture view handle.
|
|
68
|
+
#
|
|
69
|
+
# Calling this method more than once has no effect.
|
|
70
|
+
# @return [void]
|
|
43
71
|
def release
|
|
44
72
|
return if @handle.null?
|
|
45
73
|
Native.wgpuTextureViewRelease(@handle)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WGPU
|
|
4
|
+
module TextureFormat
|
|
5
|
+
COPY_ALIGNMENT = 256
|
|
6
|
+
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# Returns the byte size of one texel block.
|
|
10
|
+
# @return [Integer]
|
|
11
|
+
def block_size(format, aspect: :all)
|
|
12
|
+
block_info(format, aspect:).last
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns a format's texel block width and height.
|
|
16
|
+
# @return [Array(Integer, Integer)]
|
|
17
|
+
def block_dimensions(format)
|
|
18
|
+
block_info(format).first(2)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns the tightly packed bytes required for one texel row.
|
|
22
|
+
# @return [Integer]
|
|
23
|
+
def bytes_per_row(width, format, aspect: :all)
|
|
24
|
+
block_width, _, bytes = block_info(format, aspect:)
|
|
25
|
+
((Integer(width) + block_width - 1) / block_width) * bytes
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns row bytes rounded up to the requested copy alignment.
|
|
29
|
+
# @return [Integer]
|
|
30
|
+
def aligned_bytes_per_row(width, format, aspect: :all, alignment: COPY_ALIGNMENT)
|
|
31
|
+
row_bytes = bytes_per_row(width, format, aspect:)
|
|
32
|
+
((row_bytes + alignment - 1) / alignment) * alignment
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns the copy footprint of one texel block.
|
|
36
|
+
#
|
|
37
|
+
# @param format [Symbol, Integer] texture format
|
|
38
|
+
# @param aspect [Symbol] texture aspect for depth/stencil formats
|
|
39
|
+
# @return [Array(Integer, Integer, Integer)] block width, height, and byte size
|
|
40
|
+
# @raise [ArgumentError] if the format has no portable copy footprint
|
|
41
|
+
def block_info(format, aspect: :all)
|
|
42
|
+
name = normalize_format(format)
|
|
43
|
+
special = depth_stencil_block_info(name, aspect)
|
|
44
|
+
return special if special
|
|
45
|
+
|
|
46
|
+
case name.to_s
|
|
47
|
+
when /\Ar8_/
|
|
48
|
+
[1, 1, 1]
|
|
49
|
+
when /\A(?:r16_|rg8_)/
|
|
50
|
+
[1, 1, 2]
|
|
51
|
+
when /\A(?:r32_|rg16_|rgba8_|bgra8_|rgb10a2_|rg11b10_|rgb9e5_)/
|
|
52
|
+
[1, 1, 4]
|
|
53
|
+
when /\A(?:rg32_|rgba16_)/
|
|
54
|
+
[1, 1, 8]
|
|
55
|
+
when /\Argba32_/
|
|
56
|
+
[1, 1, 16]
|
|
57
|
+
when /\A(?:bc(?:1|4)|etc2_rgb8|etc2_rgb8a1|eac_r11)_/
|
|
58
|
+
[4, 4, 8]
|
|
59
|
+
when /\A(?:bc(?:2|3|5|6h|7)|etc2_rgba8|eac_rg11)_/
|
|
60
|
+
[4, 4, 16]
|
|
61
|
+
when /\Aastc_(\d+)x(\d+)_/
|
|
62
|
+
[Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, 16]
|
|
63
|
+
else
|
|
64
|
+
raise ArgumentError, "No texel block copy footprint for #{name.inspect}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def normalize_format(format)
|
|
69
|
+
return format if format.is_a?(Symbol) &&
|
|
70
|
+
Native::TextureFormat.to_h.key?(format)
|
|
71
|
+
|
|
72
|
+
value = Native::EnumHelper.coerce(Native::TextureFormat, format, name: "texture format")
|
|
73
|
+
Native::TextureFormat[value] || format
|
|
74
|
+
end
|
|
75
|
+
private_class_method :normalize_format
|
|
76
|
+
|
|
77
|
+
def depth_stencil_block_info(format, aspect)
|
|
78
|
+
case format
|
|
79
|
+
when :stencil8
|
|
80
|
+
[1, 1, 1]
|
|
81
|
+
when :depth16_unorm
|
|
82
|
+
[1, 1, 2]
|
|
83
|
+
when :depth32_float
|
|
84
|
+
[1, 1, 4]
|
|
85
|
+
when :depth32_float_stencil8
|
|
86
|
+
return [1, 1, 4] if aspect == :depth_only
|
|
87
|
+
return [1, 1, 1] if aspect == :stencil_only
|
|
88
|
+
|
|
89
|
+
raise ArgumentError, "depth32_float_stencil8 copies require :depth_only or :stencil_only aspect"
|
|
90
|
+
when :depth24_plus, :depth24_plus_stencil8
|
|
91
|
+
raise ArgumentError, "#{format} has no portable texel block copy footprint"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
private_class_method :depth_stencil_block_info
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/wgpu/version.rb
CHANGED
data/lib/wgpu/window.rb
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
begin
|
|
4
|
+
require "sdl3"
|
|
5
|
+
rescue LoadError => e
|
|
6
|
+
raise LoadError,
|
|
7
|
+
"wgpu/window requires the optional `sdl3` gem. " \
|
|
8
|
+
"Add `gem \"sdl3\", \"~> 1.0\"` and install the SDL3 system library. " \
|
|
9
|
+
"(original error: #{e.message})"
|
|
10
|
+
end
|
|
4
11
|
|
|
5
12
|
module WGPU
|
|
6
13
|
module Window
|
|
@@ -15,6 +22,11 @@ module WGPU
|
|
|
15
22
|
SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER = "SDL.window.wayland.display"
|
|
16
23
|
SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER = "SDL.window.wayland.surface"
|
|
17
24
|
|
|
25
|
+
# Creates an SDL window suitable for WebGPU presentation.
|
|
26
|
+
# @param title [String] window title
|
|
27
|
+
# @param width [Integer] initial logical width
|
|
28
|
+
# @param height [Integer] initial logical height
|
|
29
|
+
# @param resizable [Boolean] whether the user may resize the window
|
|
18
30
|
def initialize(title:, width: 800, height: 600, resizable: true)
|
|
19
31
|
@width = width
|
|
20
32
|
@height = height
|
|
@@ -28,6 +40,11 @@ module WGPU
|
|
|
28
40
|
@window = SDL3::Window.new(title, width, height, flags)
|
|
29
41
|
end
|
|
30
42
|
|
|
43
|
+
# Creates a presentation surface for this native window.
|
|
44
|
+
#
|
|
45
|
+
# @param instance [Instance] WebGPU instance that owns the surface
|
|
46
|
+
# @return [Surface] platform-specific presentation surface
|
|
47
|
+
# @raise [WindowError] if the platform or native window handle is unsupported
|
|
31
48
|
def create_surface(instance)
|
|
32
49
|
case platform
|
|
33
50
|
when :macos
|
|
@@ -43,6 +60,9 @@ module WGPU
|
|
|
43
60
|
end
|
|
44
61
|
end
|
|
45
62
|
|
|
63
|
+
# Collects currently pending SDL events.
|
|
64
|
+
#
|
|
65
|
+
# @return [Array] events in delivery order
|
|
46
66
|
def poll_events
|
|
47
67
|
events = []
|
|
48
68
|
SDL3::Event.each do |event|
|
|
@@ -51,10 +71,17 @@ module WGPU
|
|
|
51
71
|
events
|
|
52
72
|
end
|
|
53
73
|
|
|
74
|
+
# Reports whether a quit event is present.
|
|
75
|
+
# @param events [Array] SDL events to inspect
|
|
76
|
+
# @return [Boolean]
|
|
54
77
|
def should_close?(events)
|
|
55
78
|
events.any?(&:quit?)
|
|
56
79
|
end
|
|
57
80
|
|
|
81
|
+
# Reports whether a key-down event matches the requested key.
|
|
82
|
+
# @param events [Array] SDL events to inspect
|
|
83
|
+
# @param key [Symbol, Integer] common key name or SDL scancode
|
|
84
|
+
# @return [Boolean]
|
|
58
85
|
def key_pressed?(events, key)
|
|
59
86
|
scancode = case key
|
|
60
87
|
when :escape then SDL3::Raw::SDL_SCANCODE_ESCAPE
|
|
@@ -71,12 +98,18 @@ module WGPU
|
|
|
71
98
|
end
|
|
72
99
|
end
|
|
73
100
|
|
|
101
|
+
# Returns the drawable size in physical pixels.
|
|
102
|
+
#
|
|
103
|
+
# @return [Array(Integer, Integer)] width and height
|
|
74
104
|
def drawable_size
|
|
75
105
|
@window.size_in_pixels
|
|
76
106
|
rescue
|
|
77
107
|
[@width, @height]
|
|
78
108
|
end
|
|
79
109
|
|
|
110
|
+
# Destroys platform-specific resources and the SDL window.
|
|
111
|
+
#
|
|
112
|
+
# @return [void]
|
|
80
113
|
def close
|
|
81
114
|
if @metal_view && !@metal_view.null?
|
|
82
115
|
SDL3::Raw.SDL_Metal_DestroyView(@metal_view)
|
data/lib/wgpu.rb
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
require_relative "wgpu/version"
|
|
4
4
|
require_relative "wgpu/error"
|
|
5
5
|
require_relative "wgpu/native/loader"
|
|
6
|
+
require_relative "wgpu/native/enum_helper"
|
|
7
|
+
require_relative "wgpu/native/abi_verifier"
|
|
8
|
+
require_relative "wgpu/logging"
|
|
9
|
+
require_relative "wgpu/descriptor_helpers"
|
|
10
|
+
require_relative "wgpu/data_types"
|
|
11
|
+
require_relative "wgpu/texture_format"
|
|
12
|
+
require_relative "wgpu/native_resource"
|
|
6
13
|
require_relative "wgpu/async_task"
|
|
7
14
|
require_relative "wgpu/core/async_waiter"
|
|
8
15
|
|
|
@@ -34,4 +41,29 @@ require_relative "wgpu/core/surface"
|
|
|
34
41
|
require_relative "wgpu/core/canvas_context"
|
|
35
42
|
|
|
36
43
|
module WGPU
|
|
44
|
+
[
|
|
45
|
+
Instance,
|
|
46
|
+
Adapter,
|
|
47
|
+
Device,
|
|
48
|
+
Queue,
|
|
49
|
+
Surface,
|
|
50
|
+
CanvasContext,
|
|
51
|
+
Buffer,
|
|
52
|
+
Texture,
|
|
53
|
+
TextureView,
|
|
54
|
+
Sampler,
|
|
55
|
+
QuerySet,
|
|
56
|
+
ShaderModule,
|
|
57
|
+
BindGroupLayout,
|
|
58
|
+
BindGroup,
|
|
59
|
+
PipelineLayout,
|
|
60
|
+
ComputePipeline,
|
|
61
|
+
RenderPipeline,
|
|
62
|
+
CommandEncoder,
|
|
63
|
+
CommandBuffer,
|
|
64
|
+
ComputePass,
|
|
65
|
+
RenderPass,
|
|
66
|
+
RenderBundleEncoder,
|
|
67
|
+
RenderBundle
|
|
68
|
+
].each { |resource_class| resource_class.include(NativeResource) }
|
|
37
69
|
end
|