wgpu 1.0.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 +7 -0
- data/LICENSE-APACHE +190 -0
- data/LICENSE-MIT +21 -0
- data/README.md +228 -0
- data/ext/wgpu/Makefile +7 -0
- data/ext/wgpu/extconf.rb +161 -0
- data/lib/wgpu/async_task.rb +55 -0
- data/lib/wgpu/commands/command_buffer.rb +17 -0
- data/lib/wgpu/commands/command_encoder.rb +201 -0
- data/lib/wgpu/commands/compute_pass.rb +89 -0
- data/lib/wgpu/commands/render_bundle.rb +18 -0
- data/lib/wgpu/commands/render_bundle_encoder.rb +148 -0
- data/lib/wgpu/commands/render_pass.rb +207 -0
- data/lib/wgpu/core/adapter.rb +186 -0
- data/lib/wgpu/core/canvas_context.rb +104 -0
- data/lib/wgpu/core/device.rb +397 -0
- data/lib/wgpu/core/instance.rb +81 -0
- data/lib/wgpu/core/queue.rb +197 -0
- data/lib/wgpu/core/surface.rb +221 -0
- data/lib/wgpu/error.rb +16 -0
- data/lib/wgpu/native/callbacks.rb +26 -0
- data/lib/wgpu/native/enums.rb +529 -0
- data/lib/wgpu/native/functions.rb +419 -0
- data/lib/wgpu/native/loader.rb +61 -0
- data/lib/wgpu/native/structs.rb +646 -0
- data/lib/wgpu/pipeline/bind_group.rb +80 -0
- data/lib/wgpu/pipeline/bind_group_layout.rb +121 -0
- data/lib/wgpu/pipeline/compute_pipeline.rb +88 -0
- data/lib/wgpu/pipeline/pipeline_layout.rb +43 -0
- data/lib/wgpu/pipeline/render_pipeline.rb +278 -0
- data/lib/wgpu/pipeline/shader_module.rb +202 -0
- data/lib/wgpu/resources/buffer.rb +228 -0
- data/lib/wgpu/resources/query_set.rb +45 -0
- data/lib/wgpu/resources/sampler.rb +47 -0
- data/lib/wgpu/resources/texture.rb +136 -0
- data/lib/wgpu/resources/texture_view.rb +49 -0
- data/lib/wgpu/version.rb +5 -0
- data/lib/wgpu/window.rb +177 -0
- data/lib/wgpu.rb +36 -0
- metadata +125 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WGPU
|
|
4
|
+
class Instance
|
|
5
|
+
attr_reader :handle
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
desc = Native::InstanceDescriptor.new
|
|
9
|
+
desc[:next_in_chain] = nil
|
|
10
|
+
desc[:features][:next_in_chain] = nil
|
|
11
|
+
desc[:features][:timed_wait_any_enable] = 0
|
|
12
|
+
desc[:features][:timed_wait_any_max_count] = 0
|
|
13
|
+
|
|
14
|
+
@handle = Native.wgpuCreateInstance(desc)
|
|
15
|
+
raise InitializationError, "Failed to create WebGPU instance" if @handle.null?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def request_adapter(power_preference: :high_performance, backend: nil, feature_level: :core, force_fallback_adapter: false, compatible_surface: nil)
|
|
19
|
+
Adapter.request(
|
|
20
|
+
self,
|
|
21
|
+
power_preference: power_preference,
|
|
22
|
+
backend: backend,
|
|
23
|
+
feature_level: feature_level,
|
|
24
|
+
force_fallback_adapter: force_fallback_adapter,
|
|
25
|
+
compatible_surface: compatible_surface
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def request_adapter_async(power_preference: :high_performance, backend: nil, feature_level: :core, force_fallback_adapter: false, compatible_surface: nil)
|
|
30
|
+
AsyncTask.new do
|
|
31
|
+
request_adapter(
|
|
32
|
+
power_preference: power_preference,
|
|
33
|
+
backend: backend,
|
|
34
|
+
feature_level: feature_level,
|
|
35
|
+
force_fallback_adapter: force_fallback_adapter,
|
|
36
|
+
compatible_surface: compatible_surface
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def enumerate_adapters(backends: nil)
|
|
42
|
+
options = nil
|
|
43
|
+
if backends
|
|
44
|
+
options = Native::InstanceEnumerateAdapterOptions.new
|
|
45
|
+
options[:next_in_chain] = nil
|
|
46
|
+
options[:backends] = backends
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
count = Native.wgpuInstanceEnumerateAdapters(@handle, options, nil)
|
|
50
|
+
return [] if count == 0
|
|
51
|
+
|
|
52
|
+
adapters_ptr = FFI::MemoryPointer.new(:pointer, count)
|
|
53
|
+
Native.wgpuInstanceEnumerateAdapters(@handle, options, adapters_ptr)
|
|
54
|
+
|
|
55
|
+
adapters_ptr.read_array_of_pointer(count).map do |ptr|
|
|
56
|
+
Adapter.from_handle(ptr)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def enumerate_adapters_async(backends: nil)
|
|
61
|
+
AsyncTask.new do
|
|
62
|
+
enumerate_adapters(backends: backends)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def get_canvas_context(present_info)
|
|
67
|
+
CanvasContext.new(self, present_info)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def process_events
|
|
71
|
+
Native.wgpuInstanceProcessEvents(@handle)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def release
|
|
75
|
+
return if @handle.null?
|
|
76
|
+
Native.wgpuInstanceRelease(@handle)
|
|
77
|
+
@handle = FFI::Pointer::NULL
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WGPU
|
|
4
|
+
class Queue
|
|
5
|
+
attr_reader :handle
|
|
6
|
+
|
|
7
|
+
def initialize(handle, device: nil)
|
|
8
|
+
@handle = handle
|
|
9
|
+
@device = device
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def submit(command_buffers)
|
|
13
|
+
buffers = Array(command_buffers)
|
|
14
|
+
return if buffers.empty?
|
|
15
|
+
|
|
16
|
+
handles = buffers.map(&:handle)
|
|
17
|
+
ptr = FFI::MemoryPointer.new(:pointer, handles.size)
|
|
18
|
+
ptr.write_array_of_pointer(handles)
|
|
19
|
+
|
|
20
|
+
Native.wgpuQueueSubmit(@handle, handles.size, ptr)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def write_buffer(buffer, buffer_offset, data, data_offset: 0, size: nil)
|
|
24
|
+
data_ptr, byte_size = data_to_pointer(data)
|
|
25
|
+
data_offset = Integer(data_offset)
|
|
26
|
+
raise ArgumentError, "data_offset must be non-negative" if data_offset.negative?
|
|
27
|
+
raise ArgumentError, "data_offset out of range" if data_offset > byte_size
|
|
28
|
+
|
|
29
|
+
write_size = size.nil? ? (byte_size - data_offset) : Integer(size)
|
|
30
|
+
raise ArgumentError, "size must be non-negative" if write_size.negative?
|
|
31
|
+
raise ArgumentError, "data_offset + size out of range" if data_offset + write_size > byte_size
|
|
32
|
+
|
|
33
|
+
Native.wgpuQueueWriteBuffer(
|
|
34
|
+
@handle,
|
|
35
|
+
buffer.handle,
|
|
36
|
+
buffer_offset,
|
|
37
|
+
data_ptr + data_offset,
|
|
38
|
+
write_size
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def write_texture(destination:, data:, data_layout:, size:)
|
|
43
|
+
data_ptr, byte_size = data_to_pointer(data)
|
|
44
|
+
|
|
45
|
+
dst = Native::ImageCopyTexture.new
|
|
46
|
+
dst[:texture] = destination[:texture].handle
|
|
47
|
+
dst[:mip_level] = destination[:mip_level] || 0
|
|
48
|
+
dst[:origin][:x] = destination.dig(:origin, :x) || 0
|
|
49
|
+
dst[:origin][:y] = destination.dig(:origin, :y) || 0
|
|
50
|
+
dst[:origin][:z] = destination.dig(:origin, :z) || 0
|
|
51
|
+
dst[:aspect] = destination[:aspect] || :all
|
|
52
|
+
|
|
53
|
+
extent = Native::Extent3D.new
|
|
54
|
+
if size.is_a?(Array)
|
|
55
|
+
extent[:width] = size[0]
|
|
56
|
+
extent[:height] = size[1] || 1
|
|
57
|
+
extent[:depth_or_array_layers] = size[2] || 1
|
|
58
|
+
else
|
|
59
|
+
extent[:width] = size[:width]
|
|
60
|
+
extent[:height] = size[:height] || 1
|
|
61
|
+
extent[:depth_or_array_layers] = size[:depth_or_array_layers] || 1
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
layout = Native::TextureDataLayout.new
|
|
65
|
+
layout[:offset] = data_layout[:offset] || 0
|
|
66
|
+
layout[:bytes_per_row] = data_layout[:bytes_per_row]
|
|
67
|
+
layout[:rows_per_image] = data_layout[:rows_per_image] || extent[:height]
|
|
68
|
+
|
|
69
|
+
Native.wgpuQueueWriteTexture(@handle, dst, data_ptr, byte_size, layout, extent)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def read_buffer(buffer, offset: 0, size: nil, device:)
|
|
73
|
+
size ||= buffer.size - offset
|
|
74
|
+
|
|
75
|
+
staging = Buffer.new(device,
|
|
76
|
+
size: size,
|
|
77
|
+
usage: [:map_read, :copy_dst]
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
encoder = CommandEncoder.new(device)
|
|
81
|
+
encoder.copy_buffer_to_buffer(
|
|
82
|
+
source: buffer,
|
|
83
|
+
source_offset: offset,
|
|
84
|
+
destination: staging,
|
|
85
|
+
destination_offset: 0,
|
|
86
|
+
size: size
|
|
87
|
+
)
|
|
88
|
+
command_buffer = encoder.finish
|
|
89
|
+
submit([command_buffer])
|
|
90
|
+
|
|
91
|
+
staging.map_sync(:read)
|
|
92
|
+
data = staging.read_mapped_data
|
|
93
|
+
staging.unmap
|
|
94
|
+
staging.release
|
|
95
|
+
|
|
96
|
+
data
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def read_texture(source:, data_layout:, size:, device:)
|
|
100
|
+
width = size[:width] || size[0]
|
|
101
|
+
height = size[:height] || size[1] || 1
|
|
102
|
+
depth = size[:depth_or_array_layers] || size[2] || 1
|
|
103
|
+
bytes_per_row = data_layout[:bytes_per_row]
|
|
104
|
+
rows_per_image = data_layout[:rows_per_image] || height
|
|
105
|
+
buffer_size = bytes_per_row * rows_per_image * depth
|
|
106
|
+
|
|
107
|
+
staging = Buffer.new(device,
|
|
108
|
+
size: buffer_size,
|
|
109
|
+
usage: [:map_read, :copy_dst]
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
encoder = CommandEncoder.new(device)
|
|
113
|
+
encoder.copy_texture_to_buffer(
|
|
114
|
+
source: source,
|
|
115
|
+
destination: {
|
|
116
|
+
buffer: staging,
|
|
117
|
+
offset: 0,
|
|
118
|
+
bytes_per_row: bytes_per_row,
|
|
119
|
+
rows_per_image: rows_per_image
|
|
120
|
+
},
|
|
121
|
+
copy_size: size
|
|
122
|
+
)
|
|
123
|
+
command_buffer = encoder.finish
|
|
124
|
+
submit([command_buffer])
|
|
125
|
+
|
|
126
|
+
staging.map_sync(:read)
|
|
127
|
+
data = staging.read_mapped_data
|
|
128
|
+
staging.unmap
|
|
129
|
+
staging.release
|
|
130
|
+
|
|
131
|
+
data
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def on_submitted_work_done(device: nil)
|
|
135
|
+
device ||= @device
|
|
136
|
+
status_holder = { done: false, status: nil }
|
|
137
|
+
|
|
138
|
+
callback = FFI::Function.new(
|
|
139
|
+
:void, [:uint32, :pointer, :pointer]
|
|
140
|
+
) do |status, _userdata1, _userdata2|
|
|
141
|
+
status_holder[:done] = true
|
|
142
|
+
status_holder[:status] = Native::QueueWorkDoneStatus[status]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
callback_info = Native::QueueWorkDoneCallbackInfo.new
|
|
146
|
+
callback_info[:next_in_chain] = nil
|
|
147
|
+
callback_info[:mode] = 1
|
|
148
|
+
callback_info[:callback] = callback
|
|
149
|
+
callback_info[:userdata1] = nil
|
|
150
|
+
callback_info[:userdata2] = nil
|
|
151
|
+
|
|
152
|
+
Native.wgpuQueueOnSubmittedWorkDone(@handle, callback_info)
|
|
153
|
+
|
|
154
|
+
if device
|
|
155
|
+
until status_holder[:done]
|
|
156
|
+
Native.wgpuDevicePoll(device.handle, 0, nil)
|
|
157
|
+
sleep(0.001)
|
|
158
|
+
end
|
|
159
|
+
else
|
|
160
|
+
sleep(0.001) until status_holder[:done]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
status_holder[:status]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def on_submitted_work_done_async(device: nil)
|
|
167
|
+
AsyncTask.new do
|
|
168
|
+
on_submitted_work_done(device: device)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def release
|
|
173
|
+
return if @handle.null?
|
|
174
|
+
Native.wgpuQueueRelease(@handle)
|
|
175
|
+
@handle = FFI::Pointer::NULL
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
private
|
|
179
|
+
|
|
180
|
+
def data_to_pointer(data)
|
|
181
|
+
case data
|
|
182
|
+
when String
|
|
183
|
+
ptr = FFI::MemoryPointer.new(:char, data.bytesize)
|
|
184
|
+
ptr.put_bytes(0, data)
|
|
185
|
+
[ptr, data.bytesize]
|
|
186
|
+
when Array
|
|
187
|
+
ptr = FFI::MemoryPointer.new(:float, data.size)
|
|
188
|
+
ptr.write_array_of_float(data)
|
|
189
|
+
[ptr, data.size * 4]
|
|
190
|
+
when FFI::Pointer
|
|
191
|
+
[data, data.size]
|
|
192
|
+
else
|
|
193
|
+
raise ArgumentError, "Unsupported data type: #{data.class}"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WGPU
|
|
4
|
+
class Surface
|
|
5
|
+
attr_reader :handle
|
|
6
|
+
|
|
7
|
+
def self.from_metal_layer(instance, layer)
|
|
8
|
+
source = Native::SurfaceSourceMetalLayer.new
|
|
9
|
+
source[:chain][:next] = nil
|
|
10
|
+
source[:chain][:s_type] = Native::SType[:surface_source_metal_layer]
|
|
11
|
+
source[:layer] = layer
|
|
12
|
+
|
|
13
|
+
desc = Native::SurfaceDescriptor.new
|
|
14
|
+
desc[:next_in_chain] = source.to_ptr
|
|
15
|
+
desc[:label][:data] = nil
|
|
16
|
+
desc[:label][:length] = 0
|
|
17
|
+
|
|
18
|
+
handle = Native.wgpuInstanceCreateSurface(instance.handle, desc)
|
|
19
|
+
raise SurfaceError, "Failed to create surface from Metal layer" if handle.null?
|
|
20
|
+
|
|
21
|
+
new(handle, instance)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.from_windows_hwnd(instance, hinstance, hwnd)
|
|
25
|
+
source = Native::SurfaceSourceWindowsHWND.new
|
|
26
|
+
source[:chain][:next] = nil
|
|
27
|
+
source[:chain][:s_type] = Native::SType[:surface_source_windows_hwnd]
|
|
28
|
+
source[:hinstance] = hinstance
|
|
29
|
+
source[:hwnd] = hwnd
|
|
30
|
+
|
|
31
|
+
desc = Native::SurfaceDescriptor.new
|
|
32
|
+
desc[:next_in_chain] = source.to_ptr
|
|
33
|
+
desc[:label][:data] = nil
|
|
34
|
+
desc[:label][:length] = 0
|
|
35
|
+
|
|
36
|
+
handle = Native.wgpuInstanceCreateSurface(instance.handle, desc)
|
|
37
|
+
raise SurfaceError, "Failed to create surface from Windows HWND" if handle.null?
|
|
38
|
+
|
|
39
|
+
new(handle, instance)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.from_xlib_window(instance, display, window)
|
|
43
|
+
source = Native::SurfaceSourceXlibWindow.new
|
|
44
|
+
source[:chain][:next] = nil
|
|
45
|
+
source[:chain][:s_type] = Native::SType[:surface_source_xlib_window]
|
|
46
|
+
source[:display] = display
|
|
47
|
+
source[:window] = window
|
|
48
|
+
|
|
49
|
+
desc = Native::SurfaceDescriptor.new
|
|
50
|
+
desc[:next_in_chain] = source.to_ptr
|
|
51
|
+
desc[:label][:data] = nil
|
|
52
|
+
desc[:label][:length] = 0
|
|
53
|
+
|
|
54
|
+
handle = Native.wgpuInstanceCreateSurface(instance.handle, desc)
|
|
55
|
+
raise SurfaceError, "Failed to create surface from Xlib window" if handle.null?
|
|
56
|
+
|
|
57
|
+
new(handle, instance)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.from_wayland_surface(instance, display, surface)
|
|
61
|
+
source = Native::SurfaceSourceWaylandSurface.new
|
|
62
|
+
source[:chain][:next] = nil
|
|
63
|
+
source[:chain][:s_type] = Native::SType[:surface_source_wayland_surface]
|
|
64
|
+
source[:display] = display
|
|
65
|
+
source[:surface] = surface
|
|
66
|
+
|
|
67
|
+
desc = Native::SurfaceDescriptor.new
|
|
68
|
+
desc[:next_in_chain] = source.to_ptr
|
|
69
|
+
desc[:label][:data] = nil
|
|
70
|
+
desc[:label][:length] = 0
|
|
71
|
+
|
|
72
|
+
handle = Native.wgpuInstanceCreateSurface(instance.handle, desc)
|
|
73
|
+
raise SurfaceError, "Failed to create surface from Wayland surface" if handle.null?
|
|
74
|
+
|
|
75
|
+
new(handle, instance)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def initialize(handle, instance)
|
|
79
|
+
@handle = handle
|
|
80
|
+
@instance = instance
|
|
81
|
+
@configured = false
|
|
82
|
+
@config = nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def configure(device:, format:, usage: :render_attachment, width:, height:, present_mode: :fifo, alpha_mode: :auto, view_formats: [])
|
|
86
|
+
config = Native::SurfaceConfiguration.new
|
|
87
|
+
config[:next_in_chain] = nil
|
|
88
|
+
config[:device] = device.handle
|
|
89
|
+
config[:format] = format
|
|
90
|
+
config[:usage] = normalize_usage(usage)
|
|
91
|
+
config[:width] = width
|
|
92
|
+
config[:height] = height
|
|
93
|
+
config[:view_format_count] = view_formats.size
|
|
94
|
+
if view_formats.empty?
|
|
95
|
+
@view_formats_ptr = nil
|
|
96
|
+
config[:view_formats] = nil
|
|
97
|
+
else
|
|
98
|
+
format_values = view_formats.map do |vf|
|
|
99
|
+
vf.is_a?(Integer) ? vf : Native::TextureFormat[vf]
|
|
100
|
+
end
|
|
101
|
+
@view_formats_ptr = FFI::MemoryPointer.new(:uint32, format_values.size)
|
|
102
|
+
@view_formats_ptr.write_array_of_uint32(format_values)
|
|
103
|
+
config[:view_formats] = @view_formats_ptr
|
|
104
|
+
end
|
|
105
|
+
config[:alpha_mode] = Native::CompositeAlphaMode[alpha_mode]
|
|
106
|
+
config[:present_mode] = Native::PresentMode[present_mode]
|
|
107
|
+
|
|
108
|
+
Native.wgpuSurfaceConfigure(@handle, config)
|
|
109
|
+
@configured = true
|
|
110
|
+
@device = device
|
|
111
|
+
@config = {
|
|
112
|
+
device: device,
|
|
113
|
+
format: format,
|
|
114
|
+
usage: usage,
|
|
115
|
+
width: width,
|
|
116
|
+
height: height,
|
|
117
|
+
present_mode: present_mode,
|
|
118
|
+
alpha_mode: alpha_mode,
|
|
119
|
+
view_formats: view_formats
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def unconfigure
|
|
124
|
+
Native.wgpuSurfaceUnconfigure(@handle)
|
|
125
|
+
@configured = false
|
|
126
|
+
@config = nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def current_texture
|
|
130
|
+
raise SurfaceError, "Surface is not configured" unless @configured
|
|
131
|
+
|
|
132
|
+
surface_texture = Native::SurfaceTexture.new
|
|
133
|
+
Native.wgpuSurfaceGetCurrentTexture(@handle, surface_texture)
|
|
134
|
+
|
|
135
|
+
status = Native::SurfaceGetCurrentTextureStatus[surface_texture[:status]]
|
|
136
|
+
unless status == :success_optimal || status == :success_suboptimal
|
|
137
|
+
raise SurfaceError, "Failed to get current texture: #{status}"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
texture_ptr = surface_texture[:texture]
|
|
141
|
+
if texture_ptr.nil? || texture_ptr.null?
|
|
142
|
+
raise SurfaceError, "Surface returned null texture"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
Texture.from_handle(texture_ptr)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def get_current_texture
|
|
149
|
+
current_texture
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def present
|
|
153
|
+
Native.wgpuSurfacePresent(@handle)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def get_configuration
|
|
157
|
+
@config
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def get_preferred_format(adapter)
|
|
161
|
+
caps = capabilities(adapter)
|
|
162
|
+
caps[:formats].first || :bgra8_unorm
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def capabilities(adapter)
|
|
166
|
+
caps = Native::SurfaceCapabilities.new
|
|
167
|
+
Native.wgpuSurfaceGetCapabilities(@handle, adapter.handle, caps)
|
|
168
|
+
|
|
169
|
+
formats = []
|
|
170
|
+
if caps[:format_count] > 0 && !caps[:formats].null?
|
|
171
|
+
formats = caps[:formats].read_array_of_uint32(caps[:format_count]).map do |f|
|
|
172
|
+
Native::TextureFormat[f]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
present_modes = []
|
|
177
|
+
if caps[:present_mode_count] > 0 && !caps[:present_modes].null?
|
|
178
|
+
present_modes = caps[:present_modes].read_array_of_uint32(caps[:present_mode_count]).map do |m|
|
|
179
|
+
Native::PresentMode[m]
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
alpha_modes = []
|
|
184
|
+
if caps[:alpha_mode_count] > 0 && !caps[:alpha_modes].null?
|
|
185
|
+
alpha_modes = caps[:alpha_modes].read_array_of_uint32(caps[:alpha_mode_count]).map do |a|
|
|
186
|
+
Native::CompositeAlphaMode[a]
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
{
|
|
191
|
+
formats: formats,
|
|
192
|
+
present_modes: present_modes,
|
|
193
|
+
alpha_modes: alpha_modes,
|
|
194
|
+
usages: caps[:usages]
|
|
195
|
+
}
|
|
196
|
+
ensure
|
|
197
|
+
Native.wgpuSurfaceCapabilitiesFreeMembers(caps) if caps
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def release
|
|
201
|
+
return if @handle.null?
|
|
202
|
+
Native.wgpuSurfaceRelease(@handle)
|
|
203
|
+
@handle = FFI::Pointer::NULL
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
private
|
|
207
|
+
|
|
208
|
+
def normalize_usage(usage)
|
|
209
|
+
case usage
|
|
210
|
+
when Integer
|
|
211
|
+
usage
|
|
212
|
+
when Symbol
|
|
213
|
+
Native::TextureUsage[usage]
|
|
214
|
+
when Array
|
|
215
|
+
usage.reduce(0) { |acc, u| acc | Native::TextureUsage[u] }
|
|
216
|
+
else
|
|
217
|
+
raise ArgumentError, "Invalid usage: #{usage}"
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
data/lib/wgpu/error.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WGPU
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
class InitializationError < Error; end
|
|
6
|
+
class AdapterError < Error; end
|
|
7
|
+
class DeviceError < Error; end
|
|
8
|
+
class BufferError < Error; end
|
|
9
|
+
class TextureError < Error; end
|
|
10
|
+
class ResourceError < Error; end
|
|
11
|
+
class ShaderError < Error; end
|
|
12
|
+
class PipelineError < Error; end
|
|
13
|
+
class CommandError < Error; end
|
|
14
|
+
class SurfaceError < Error; end
|
|
15
|
+
class RenderBundleError < Error; end
|
|
16
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WGPU
|
|
4
|
+
module Native
|
|
5
|
+
callback :request_adapter_callback,
|
|
6
|
+
[RequestAdapterStatus, :pointer, StringView.by_value, :pointer], :void
|
|
7
|
+
|
|
8
|
+
callback :request_device_callback,
|
|
9
|
+
[RequestDeviceStatus, :pointer, StringView.by_value, :pointer], :void
|
|
10
|
+
|
|
11
|
+
callback :buffer_map_callback,
|
|
12
|
+
[MapAsyncStatus, :pointer], :void
|
|
13
|
+
|
|
14
|
+
callback :error_callback,
|
|
15
|
+
[:uint32, StringView.by_value, :pointer], :void
|
|
16
|
+
|
|
17
|
+
callback :device_lost_callback,
|
|
18
|
+
[:pointer, :uint32, StringView.by_value, :pointer], :void
|
|
19
|
+
|
|
20
|
+
callback :pop_error_scope_callback,
|
|
21
|
+
[PopErrorScopeStatus, ErrorType, StringView.by_value, :pointer, :pointer], :void
|
|
22
|
+
|
|
23
|
+
callback :queue_work_done_callback,
|
|
24
|
+
[QueueWorkDoneStatus, :pointer, :pointer], :void
|
|
25
|
+
end
|
|
26
|
+
end
|