wgpu 1.2.0 → 1.2.2
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 +66 -0
- data/README.md +18 -2
- data/docs/README.md +2 -2
- data/docs/api_coverage.md +23 -15
- data/docs/async.md +13 -1
- data/docs/command_encoding.md +25 -0
- data/docs/errors.md +13 -0
- data/docs/getting_started_compute.md +1 -0
- data/docs/getting_started_rendering.md +5 -0
- data/docs/pipeline_descriptors.md +7 -2
- data/docs/releasing.md +11 -1
- data/docs/resource_lifetime.md +25 -2
- data/docs/texture_readback.md +27 -0
- data/docs/troubleshooting.md +5 -0
- data/docs/upgrading_wgpu_native.md +16 -5
- data/lib/wgpu/async_task.rb +19 -0
- data/lib/wgpu/commands/command_buffer.rb +14 -1
- data/lib/wgpu/commands/command_encoder.rb +82 -27
- data/lib/wgpu/commands/compute_pass.rb +49 -6
- data/lib/wgpu/commands/render_bundle.rb +9 -1
- data/lib/wgpu/commands/render_bundle_encoder.rb +63 -9
- data/lib/wgpu/commands/render_pass.rb +116 -14
- data/lib/wgpu/core/adapter.rb +95 -13
- data/lib/wgpu/core/async_waiter.rb +30 -4
- data/lib/wgpu/core/canvas_context.rb +37 -2
- data/lib/wgpu/core/device.rb +377 -70
- data/lib/wgpu/core/instance.rb +20 -0
- data/lib/wgpu/core/queue.rb +143 -43
- data/lib/wgpu/core/surface.rb +55 -7
- data/lib/wgpu/data_types.rb +16 -0
- data/lib/wgpu/descriptor_helpers.rb +87 -0
- data/lib/wgpu/error.rb +15 -0
- data/lib/wgpu/native/abi_verifier.rb +37 -3
- data/lib/wgpu/native/callbacks.rb +6 -0
- data/lib/wgpu/native/capabilities.rb +16 -2
- data/lib/wgpu/native/distribution.rb +63 -0
- data/lib/wgpu/native/enum_helper.rb +17 -0
- data/lib/wgpu/native/enums.rb +8 -0
- data/lib/wgpu/native/fixtures/webgpu-v27.0.4.0-enums.h +848 -0
- data/lib/wgpu/native/functions.rb +14 -0
- data/lib/wgpu/native/installer.rb +55 -18
- data/lib/wgpu/native/loader.rb +22 -0
- data/lib/wgpu/native/structs.rb +18 -1
- data/lib/wgpu/native_resource.rb +190 -3
- data/lib/wgpu/pipeline/bind_group.rb +16 -7
- data/lib/wgpu/pipeline/bind_group_layout.rb +20 -7
- data/lib/wgpu/pipeline/compute_pipeline.rb +25 -37
- data/lib/wgpu/pipeline/pipeline_layout.rb +12 -4
- data/lib/wgpu/pipeline/render_pipeline.rb +33 -48
- data/lib/wgpu/pipeline/shader_module.rb +57 -32
- data/lib/wgpu/resources/buffer.rb +330 -59
- data/lib/wgpu/resources/query_set.rb +13 -1
- data/lib/wgpu/resources/sampler.rb +10 -3
- data/lib/wgpu/resources/texture.rb +49 -12
- data/lib/wgpu/resources/texture_view.rb +25 -5
- data/lib/wgpu/texture_format.rb +14 -0
- data/lib/wgpu/version.rb +1 -1
- data/lib/wgpu/window.rb +26 -0
- data/sig/wgpu.rbs +85 -5
- metadata +12 -4
|
@@ -2,8 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
module WGPU
|
|
4
4
|
module DescriptorHelpers
|
|
5
|
+
SIZE_MAX = (1 << (FFI.type_size(:size_t) * 8)) - 1
|
|
6
|
+
|
|
5
7
|
module_function
|
|
6
8
|
|
|
9
|
+
# Normalizes an Array or Hash texture extent to a native descriptor.
|
|
10
|
+
# @param size [Array<Integer>, Hash] width, height, and depth/layer count
|
|
11
|
+
# @return [Native::Extent3D]
|
|
12
|
+
def extent_3d(size)
|
|
13
|
+
values = case size
|
|
14
|
+
when Array
|
|
15
|
+
[size.fetch(0), size[1] || 1, size[2] || 1]
|
|
16
|
+
when Hash
|
|
17
|
+
[size.fetch(:width), size[:height] || 1, size[:depth_or_array_layers] || 1]
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "texture size must be an Array or Hash"
|
|
20
|
+
end
|
|
21
|
+
extent = Native::Extent3D.new
|
|
22
|
+
[:width, :height, :depth_or_array_layers].zip(values).each do |field, value|
|
|
23
|
+
value = Integer(value)
|
|
24
|
+
raise ArgumentError, "#{field} must be between 0 and 4294967295" unless (0..0xFFFFFFFF).cover?(value)
|
|
25
|
+
|
|
26
|
+
extent[field] = value
|
|
27
|
+
end
|
|
28
|
+
extent
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Writes an optional Ruby label into a native descriptor.
|
|
32
|
+
#
|
|
33
|
+
# @param descriptor [FFI::Struct] descriptor with a +label+ member
|
|
34
|
+
# @param label [String, nil] label text
|
|
35
|
+
# @param keepalive [Array] receives allocated pointers that must remain alive
|
|
36
|
+
# @return [void]
|
|
7
37
|
def set_label(descriptor, label, keepalive:)
|
|
8
38
|
if label
|
|
9
39
|
pointer = FFI::MemoryPointer.from_string(label)
|
|
@@ -16,6 +46,11 @@ module WGPU
|
|
|
16
46
|
end
|
|
17
47
|
end
|
|
18
48
|
|
|
49
|
+
# Allocates and fills a native uint32 array.
|
|
50
|
+
#
|
|
51
|
+
# @param values [Array<Integer>] values to copy
|
|
52
|
+
# @param keepalive [Array] receives the allocated pointer
|
|
53
|
+
# @return [FFI::MemoryPointer, nil] pointer, or +nil+ for an empty array
|
|
19
54
|
def uint32_array(values, keepalive:)
|
|
20
55
|
return nil if values.empty?
|
|
21
56
|
|
|
@@ -25,6 +60,58 @@ module WGPU
|
|
|
25
60
|
pointer
|
|
26
61
|
end
|
|
27
62
|
|
|
63
|
+
# Writes a nullable string into a native string view.
|
|
64
|
+
#
|
|
65
|
+
# @param string_view [Native::StringView] destination view
|
|
66
|
+
# @param value [String, nil] string value; +nil+ uses the native null sentinel
|
|
67
|
+
# @param keepalive [Array] receives allocated pointers that must remain alive
|
|
68
|
+
# @return [void]
|
|
69
|
+
def set_nullable_string_view(string_view, value, keepalive:)
|
|
70
|
+
if value.nil?
|
|
71
|
+
string_view[:data] = nil
|
|
72
|
+
string_view[:length] = SIZE_MAX
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
string = value
|
|
77
|
+
pointer = FFI::MemoryPointer.from_string(string)
|
|
78
|
+
keepalive << pointer
|
|
79
|
+
string_view[:data] = pointer
|
|
80
|
+
string_view[:length] = string.bytesize
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Writes pipeline-overridable constants into a stage descriptor.
|
|
84
|
+
#
|
|
85
|
+
# @param stage_descriptor [FFI::Struct] programmable stage descriptor
|
|
86
|
+
# @param constants [Hash{#to_s => Numeric}, nil] constant names and values
|
|
87
|
+
# @param keepalive [Array] receives allocated pointers that must remain alive
|
|
88
|
+
# @return [void]
|
|
89
|
+
def set_constants(stage_descriptor, constants, keepalive:)
|
|
90
|
+
stage_descriptor[:constant_count] = 0
|
|
91
|
+
stage_descriptor[:constants] = nil
|
|
92
|
+
return if constants.nil? || constants.empty?
|
|
93
|
+
|
|
94
|
+
constants_pointer = FFI::MemoryPointer.new(Native::ConstantEntry, constants.size)
|
|
95
|
+
keepalive << constants_pointer
|
|
96
|
+
|
|
97
|
+
constants.each_with_index do |(key, value), index|
|
|
98
|
+
entry_pointer = constants_pointer + (index * Native::ConstantEntry.size)
|
|
99
|
+
entry = Native::ConstantEntry.new(entry_pointer)
|
|
100
|
+
entry[:next_in_chain] = nil
|
|
101
|
+
set_nullable_string_view(entry[:key], key.to_s, keepalive:)
|
|
102
|
+
entry[:value] = value.to_f
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
stage_descriptor[:constant_count] = constants.size
|
|
106
|
+
stage_descriptor[:constants] = constants_pointer
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Checks required keys and warns about unsupported descriptor keys.
|
|
110
|
+
# @param options [Hash] descriptor options
|
|
111
|
+
# @param allowed [Array<Symbol>] supported keys
|
|
112
|
+
# @param required [Array<Symbol>] mandatory keys
|
|
113
|
+
# @return [Hash] original options
|
|
114
|
+
# @raise [ArgumentError] when required keys are missing
|
|
28
115
|
def validate_keys!(options, allowed:, required: [], context: "descriptor")
|
|
29
116
|
return options unless options.is_a?(Hash)
|
|
30
117
|
|
data/lib/wgpu/error.rb
CHANGED
|
@@ -20,6 +20,9 @@ module WGPU
|
|
|
20
20
|
class SurfaceAcquisitionError < SurfaceError
|
|
21
21
|
attr_reader :status
|
|
22
22
|
|
|
23
|
+
# Creates an acquisition error for a native surface status.
|
|
24
|
+
# @param status [Symbol] native acquisition status
|
|
25
|
+
# @param message [String, nil] optional override message
|
|
23
26
|
def initialize(status, message = nil)
|
|
24
27
|
@status = status
|
|
25
28
|
super(message || "Failed to get current surface texture: #{status}")
|
|
@@ -28,12 +31,17 @@ module WGPU
|
|
|
28
31
|
class RenderBundleError < Error; end
|
|
29
32
|
|
|
30
33
|
GPUError = Data.define(:type, :message) do
|
|
34
|
+
# Converts a native error-scope result into a typed error.
|
|
35
|
+
# @param error [Hash, nil] native error result
|
|
36
|
+
# @return [GPUError, nil]
|
|
31
37
|
def self.from_hash(error)
|
|
32
38
|
return if error.nil? || error[:type].nil? || error[:type] == :no_error
|
|
33
39
|
|
|
34
40
|
new(type: error[:type], message: error[:message].to_s)
|
|
35
41
|
end
|
|
36
42
|
|
|
43
|
+
# Returns the Ruby exception class matching this GPU error type.
|
|
44
|
+
# @return [Class<Error>]
|
|
37
45
|
def exception_class
|
|
38
46
|
{
|
|
39
47
|
validation: ValidationError,
|
|
@@ -43,10 +51,15 @@ module WGPU
|
|
|
43
51
|
}.fetch(type, Error)
|
|
44
52
|
end
|
|
45
53
|
|
|
54
|
+
# Raises this error as its matching Ruby exception.
|
|
55
|
+
# @return [void]
|
|
56
|
+
# @raise [Error]
|
|
46
57
|
def raise!
|
|
47
58
|
raise exception_class, "GPU error (#{type}): #{message}"
|
|
48
59
|
end
|
|
49
60
|
|
|
61
|
+
# Returns a serializable representation of the error.
|
|
62
|
+
# @return [Hash]
|
|
50
63
|
def to_h
|
|
51
64
|
{ type:, message: }
|
|
52
65
|
end
|
|
@@ -63,6 +76,8 @@ module WGPU
|
|
|
63
76
|
alias line line_num
|
|
64
77
|
alias column line_pos
|
|
65
78
|
|
|
79
|
+
# Formats the diagnostic with source location, severity, and message.
|
|
80
|
+
# @return [String]
|
|
66
81
|
def to_s
|
|
67
82
|
location = line_num&.positive? ? "#{line_num}:#{line_pos}" : "unknown location"
|
|
68
83
|
"#{location}: #{type}: #{message}"
|
|
@@ -22,18 +22,29 @@ module WGPU
|
|
|
22
22
|
]
|
|
23
23
|
}.freeze
|
|
24
24
|
|
|
25
|
+
# Creates a verifier for a header and native binding.
|
|
26
|
+
# @param header_path [String, nil] header fixture to compare
|
|
27
|
+
# @param native [Module] native binding module
|
|
25
28
|
def initialize(header_path: nil, native: Native)
|
|
26
29
|
@header_path = header_path || self.class.default_header_path
|
|
27
30
|
@native = native
|
|
28
31
|
end
|
|
29
32
|
|
|
33
|
+
# Verifies the runtime version and Ruby enum ABI against the header.
|
|
34
|
+
# @return [true]
|
|
35
|
+
# @raise [WGPU::Error] when differences are detected
|
|
30
36
|
def verify!
|
|
31
37
|
differences = enum_differences
|
|
38
|
+
version_difference = native_version_difference
|
|
39
|
+
differences.unshift(version_difference) if version_difference
|
|
32
40
|
return true if differences.empty?
|
|
33
41
|
|
|
34
|
-
raise WGPU::Error, "wgpu-native ABI
|
|
42
|
+
raise WGPU::Error, "wgpu-native ABI differences:\n#{differences.join("\n")}"
|
|
35
43
|
end
|
|
36
44
|
|
|
45
|
+
# Compares Ruby enum values with the pinned native header.
|
|
46
|
+
#
|
|
47
|
+
# @return [Array<String>] human-readable differences
|
|
37
48
|
def enum_differences
|
|
38
49
|
header_enums = parse_header
|
|
39
50
|
ruby_enums.filter_map do |name, mapping|
|
|
@@ -44,10 +55,16 @@ module WGPU
|
|
|
44
55
|
end
|
|
45
56
|
end
|
|
46
57
|
|
|
58
|
+
# Locates the pinned header used for ABI verification.
|
|
59
|
+
# @return [String] absolute header path
|
|
60
|
+
# @raise [WGPU::Error] if no header is available
|
|
47
61
|
def self.default_header_path
|
|
48
62
|
override = ENV["WGPU_HEADER_PATH"]
|
|
49
63
|
return File.expand_path(override) if override && !override.empty?
|
|
50
64
|
|
|
65
|
+
fixture = fixture_header_path
|
|
66
|
+
return fixture if File.file?(fixture)
|
|
67
|
+
|
|
51
68
|
candidates = Distribution.cache_directories.map do |cache_dir|
|
|
52
69
|
File.join(cache_dir, "include", "webgpu", "webgpu.h")
|
|
53
70
|
end
|
|
@@ -56,13 +73,30 @@ module WGPU
|
|
|
56
73
|
|
|
57
74
|
raise WGPU::Error, <<~MSG.chomp
|
|
58
75
|
Pinned #{Distribution::VERSION} webgpu.h was not found.
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
Restore the repository ABI fixture, run `bundle exec rake wgpu:install`,
|
|
77
|
+
or set WGPU_HEADER_PATH.
|
|
78
|
+
Searched: #{([fixture] + candidates).join(", ")}
|
|
61
79
|
MSG
|
|
62
80
|
end
|
|
63
81
|
|
|
82
|
+
# Returns the repository's pinned enum fixture path.
|
|
83
|
+
#
|
|
84
|
+
# @return [String] absolute header fixture path
|
|
85
|
+
def self.fixture_header_path
|
|
86
|
+
File.expand_path("fixtures/webgpu-#{Distribution::VERSION}-enums.h", __dir__)
|
|
87
|
+
end
|
|
88
|
+
|
|
64
89
|
private
|
|
65
90
|
|
|
91
|
+
def native_version_difference
|
|
92
|
+
actual = @native.wgpuGetVersion
|
|
93
|
+
expected = Distribution::ENCODED_VERSION
|
|
94
|
+
return if actual == expected
|
|
95
|
+
|
|
96
|
+
"version: runtime #{Distribution.version_string(actual)} (0x#{format("%08x", actual)}) " \
|
|
97
|
+
"does not match pinned #{Distribution::VERSION} (0x#{format("%08x", expected)})"
|
|
98
|
+
end
|
|
99
|
+
|
|
66
100
|
def parse_header
|
|
67
101
|
source = File.read(@header_path)
|
|
68
102
|
source.scan(
|
|
@@ -8,6 +8,12 @@ module WGPU
|
|
|
8
8
|
callback :request_device_callback,
|
|
9
9
|
[RequestDeviceStatus, :pointer, StringView.by_value, :pointer, :pointer], :void
|
|
10
10
|
|
|
11
|
+
callback :create_compute_pipeline_async_callback,
|
|
12
|
+
[CreatePipelineAsyncStatus, :pointer, StringView.by_value, :pointer, :pointer], :void
|
|
13
|
+
|
|
14
|
+
callback :create_render_pipeline_async_callback,
|
|
15
|
+
[CreatePipelineAsyncStatus, :pointer, StringView.by_value, :pointer, :pointer], :void
|
|
16
|
+
|
|
11
17
|
callback :buffer_map_callback,
|
|
12
18
|
[MapAsyncStatus, StringView.by_value, :pointer, :pointer], :void
|
|
13
19
|
|
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
module WGPU
|
|
4
4
|
module Native
|
|
5
5
|
class << self
|
|
6
|
+
# Reports whether future-based callback waiting is available.
|
|
7
|
+
# @return [Boolean]
|
|
6
8
|
def future_api?
|
|
7
9
|
optional_function_available?(:wgpuInstanceWaitAny)
|
|
8
10
|
end
|
|
9
11
|
|
|
12
|
+
# Reports whether explicit device polling is available.
|
|
13
|
+
# @return [Boolean]
|
|
10
14
|
def device_poll_available?
|
|
11
15
|
optional_function_available?(:wgpuDevicePoll)
|
|
12
16
|
end
|
|
@@ -15,13 +19,23 @@ module WGPU
|
|
|
15
19
|
# implementation is a Rust panic stub. Calling it aborts the process, so
|
|
16
20
|
# symbol presence alone cannot be used as a capability check.
|
|
17
21
|
def compilation_info_available?
|
|
18
|
-
Distribution
|
|
22
|
+
Distribution.capability_implemented?(:compilation_info)
|
|
19
23
|
end
|
|
20
24
|
|
|
25
|
+
# Reports whether querying buffer map state is safe in the pinned runtime.
|
|
26
|
+
# @return [Boolean]
|
|
21
27
|
def buffer_map_state_available?
|
|
22
|
-
Distribution
|
|
28
|
+
Distribution.capability_implemented?(:buffer_map_state)
|
|
23
29
|
end
|
|
24
30
|
|
|
31
|
+
# Reports whether native asynchronous pipeline creation is implemented.
|
|
32
|
+
# @return [Boolean]
|
|
33
|
+
def pipeline_async_available?
|
|
34
|
+
Distribution.capability_implemented?(:pipeline_async)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Reports whether native logging callbacks and levels are available.
|
|
38
|
+
# @return [Boolean]
|
|
25
39
|
def logging_available?
|
|
26
40
|
optional_function_available?(:wgpuSetLogCallback) &&
|
|
27
41
|
optional_function_available?(:wgpuSetLogLevel)
|
|
@@ -7,6 +7,17 @@ module WGPU
|
|
|
7
7
|
module Distribution
|
|
8
8
|
VERSION = "v27.0.4.0"
|
|
9
9
|
RELEASE_BASE_URL = "https://github.com/gfx-rs/wgpu-native/releases/download/#{VERSION}"
|
|
10
|
+
VERSION_COMPONENTS = VERSION.delete_prefix("v").split(".").map { |part| Integer(part, 10) }.freeze
|
|
11
|
+
unless VERSION_COMPONENTS.length == 4 && VERSION_COMPONENTS.all? { |part| part.between?(0, 255) }
|
|
12
|
+
raise "wgpu-native VERSION must have four byte-sized components: #{VERSION}"
|
|
13
|
+
end
|
|
14
|
+
# wgpuGetVersion packs vMAJOR.MINOR.PATCH.BUILD into four big-endian bytes.
|
|
15
|
+
ENCODED_VERSION = VERSION_COMPONENTS.reduce(0) { |encoded, part| (encoded << 8) | part }
|
|
16
|
+
UNIMPLEMENTED_CAPABILITIES = {
|
|
17
|
+
compilation_info: [VERSION].freeze,
|
|
18
|
+
buffer_map_state: [VERSION].freeze,
|
|
19
|
+
pipeline_async: [VERSION].freeze
|
|
20
|
+
}.freeze
|
|
10
21
|
|
|
11
22
|
ARTIFACTS = [
|
|
12
23
|
{
|
|
@@ -43,6 +54,10 @@ module WGPU
|
|
|
43
54
|
|
|
44
55
|
module_function
|
|
45
56
|
|
|
57
|
+
# Selects the native release artifact for a Ruby platform.
|
|
58
|
+
# @param platform [String] Ruby platform identifier
|
|
59
|
+
# @return [Hash] artifact metadata
|
|
60
|
+
# @raise [LoadError] if the platform is unsupported
|
|
46
61
|
def artifact_for(platform = RUBY_PLATFORM)
|
|
47
62
|
raise LoadError, unsupported_platform_message(platform) if platform.include?("musl")
|
|
48
63
|
|
|
@@ -52,6 +67,12 @@ module WGPU
|
|
|
52
67
|
raise LoadError, unsupported_platform_message(platform)
|
|
53
68
|
end
|
|
54
69
|
|
|
70
|
+
# Returns the preferred versioned native-library cache directory.
|
|
71
|
+
#
|
|
72
|
+
# @param env [Hash] environment used for cache overrides
|
|
73
|
+
# @param home [String] user home directory
|
|
74
|
+
# @param host_os [String] host operating-system identifier
|
|
75
|
+
# @return [String] absolute cache directory
|
|
55
76
|
def primary_cache_dir(env: ENV, home: Dir.home, host_os: RbConfig::CONFIG["host_os"])
|
|
56
77
|
override = present_value(env["WGPU_CACHE_DIR"])
|
|
57
78
|
return File.join(File.expand_path(override), VERSION) if override
|
|
@@ -62,24 +83,66 @@ module WGPU
|
|
|
62
83
|
File.join(default_cache_base(env:, home:, host_os:), "wgpu-ruby", VERSION)
|
|
63
84
|
end
|
|
64
85
|
|
|
86
|
+
# Returns the legacy versioned cache directory.
|
|
87
|
+
#
|
|
88
|
+
# @param home [String] user home directory
|
|
89
|
+
# @return [String] absolute legacy cache directory
|
|
65
90
|
def legacy_cache_dir(home: Dir.home)
|
|
66
91
|
File.join(File.expand_path(home), ".cache", "wgpu-ruby", VERSION)
|
|
67
92
|
end
|
|
68
93
|
|
|
94
|
+
# Returns cache directories in lookup order.
|
|
95
|
+
#
|
|
96
|
+
# @param env [Hash] environment used for cache overrides
|
|
97
|
+
# @param home [String] user home directory
|
|
98
|
+
# @param host_os [String] host operating-system identifier
|
|
99
|
+
# @return [Array<String>] unique cache directories
|
|
69
100
|
def cache_directories(env: ENV, home: Dir.home, host_os: RbConfig::CONFIG["host_os"])
|
|
70
101
|
[primary_cache_dir(env:, home:, host_os:), legacy_cache_dir(home:)].uniq
|
|
71
102
|
end
|
|
72
103
|
|
|
104
|
+
# Returns candidate cached library paths for a platform.
|
|
105
|
+
#
|
|
106
|
+
# @param platform [String] Ruby platform identifier
|
|
107
|
+
# @param env [Hash] environment used for cache overrides
|
|
108
|
+
# @param home [String] user home directory
|
|
109
|
+
# @param host_os [String] host operating-system identifier
|
|
110
|
+
# @return [Array<String>] candidate shared-library paths
|
|
111
|
+
# @raise [LoadError] if the platform is unsupported
|
|
73
112
|
def library_paths(platform: RUBY_PLATFORM, env: ENV, home: Dir.home,
|
|
74
113
|
host_os: RbConfig::CONFIG["host_os"])
|
|
75
114
|
library = artifact_for(platform)[:library]
|
|
76
115
|
cache_directories(env:, home:, host_os:).map { |directory| File.join(directory, "lib", library) }
|
|
77
116
|
end
|
|
78
117
|
|
|
118
|
+
# Builds the download URL for an artifact entry.
|
|
119
|
+
#
|
|
120
|
+
# @param artifact [Hash] entry from {ARTIFACTS}
|
|
121
|
+
# @return [String] release archive URL
|
|
79
122
|
def release_url(artifact)
|
|
80
123
|
"#{RELEASE_BASE_URL}/#{artifact.fetch(:archive)}"
|
|
81
124
|
end
|
|
82
125
|
|
|
126
|
+
# Decodes the packed native version into a release tag.
|
|
127
|
+
#
|
|
128
|
+
# @param encoded_version [Integer] four-byte packed version
|
|
129
|
+
# @return [String] version in +vMAJOR.MINOR.PATCH.BUILD+ form
|
|
130
|
+
def version_string(encoded_version)
|
|
131
|
+
components = [24, 16, 8, 0].map { |shift| (encoded_version >> shift) & 0xFF }
|
|
132
|
+
"v#{components.join(".")}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Reports whether a native capability is implemented by the pinned release.
|
|
136
|
+
# @param name [Symbol] capability name
|
|
137
|
+
# @return [Boolean]
|
|
138
|
+
def capability_implemented?(name)
|
|
139
|
+
!UNIMPLEMENTED_CAPABILITIES.fetch(name, []).include?(VERSION)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Builds an actionable unsupported-platform message.
|
|
143
|
+
#
|
|
144
|
+
# @param platform [String] Ruby platform identifier
|
|
145
|
+
# @return [String] diagnostic message
|
|
83
146
|
def unsupported_platform_message(platform)
|
|
84
147
|
supported = ARTIFACTS.map { |artifact| artifact[:pattern].inspect }.join(", ")
|
|
85
148
|
<<~MESSAGE.chomp
|
|
@@ -5,6 +5,12 @@ module WGPU
|
|
|
5
5
|
module EnumHelper
|
|
6
6
|
module_function
|
|
7
7
|
|
|
8
|
+
# Converts a symbolic enum member to its integer value.
|
|
9
|
+
# @param enum [FFI::Enum, Hash] enum mapping
|
|
10
|
+
# @param value [Symbol, Integer] member or native value
|
|
11
|
+
# @param name [String] name used in validation errors
|
|
12
|
+
# @return [Integer]
|
|
13
|
+
# @raise [ArgumentError] if the value is invalid
|
|
8
14
|
def coerce(enum, value, name: "enum")
|
|
9
15
|
return value if value.is_a?(Integer)
|
|
10
16
|
raise ArgumentError, "#{name} must be a Symbol or Integer, got #{value.class}" unless value.is_a?(Symbol)
|
|
@@ -16,6 +22,13 @@ module WGPU
|
|
|
16
22
|
raise ArgumentError, unknown_value_message(name, value, mapping.keys)
|
|
17
23
|
end
|
|
18
24
|
|
|
25
|
+
# Converts symbolic flag names to their combined integer bitset.
|
|
26
|
+
#
|
|
27
|
+
# @param enum [FFI::Enum, Hash] flag mapping
|
|
28
|
+
# @param value [Symbol, Integer, Array<Symbol>] flags to convert
|
|
29
|
+
# @param name [String] name used in validation errors
|
|
30
|
+
# @return [Integer] combined bitset
|
|
31
|
+
# @raise [ArgumentError] if a flag is unknown or has an invalid type
|
|
19
32
|
def coerce_flags(enum, value, name: "flags")
|
|
20
33
|
return value if value.is_a?(Integer)
|
|
21
34
|
|
|
@@ -27,6 +40,10 @@ module WGPU
|
|
|
27
40
|
values.reduce(0) { |flags, item| flags | coerce(enum, item, name:) }
|
|
28
41
|
end
|
|
29
42
|
|
|
43
|
+
# Expands a bitset into its independent symbolic flags.
|
|
44
|
+
# @param enum [FFI::Enum, Hash] flag mapping
|
|
45
|
+
# @param value [Integer] combined bitset
|
|
46
|
+
# @return [Array<Symbol>]
|
|
30
47
|
def decompose_flags(enum, value)
|
|
31
48
|
raise ArgumentError, "flag value must be an Integer" unless value.is_a?(Integer)
|
|
32
49
|
|
data/lib/wgpu/native/enums.rb
CHANGED
|
@@ -61,6 +61,14 @@ module WGPU
|
|
|
61
61
|
:unknown, 0x00000004
|
|
62
62
|
)
|
|
63
63
|
|
|
64
|
+
CreatePipelineAsyncStatus = enum(
|
|
65
|
+
:success, 0x00000001,
|
|
66
|
+
:instance_dropped, 0x00000002,
|
|
67
|
+
:validation_error, 0x00000003,
|
|
68
|
+
:internal_error, 0x00000004,
|
|
69
|
+
:unknown, 0x00000005
|
|
70
|
+
)
|
|
71
|
+
|
|
64
72
|
DeviceLostReason = enum(
|
|
65
73
|
:unknown, 0x00000001,
|
|
66
74
|
:destroyed, 0x00000002,
|