Class: WGPU::Device

Inherits:
Object
  • Object
show all
Includes:
NativeResource
Defined in:
lib/wgpu/core/device.rb,
sig/wgpu.rbs

Constant Summary collapse

LIMIT_FIELDS =
Native::Limits.members.freeze

Constants included from NativeResource

NativeResource::GUARDED_METHOD_EXEMPTIONS

Instance Attribute Summary collapse

Attributes included from NativeResource

#label

Class Method Summary collapse

Instance Method Summary collapse

Methods included from NativeResource

included, #inspect, #released?, #use

Constructor Details

#initialize(handle, adapter: nil, label: nil, callback_state: nil, callback_lifetime: nil) ⇒ Device

Wraps a native device and obtains its default queue.

Parameters:

  • handle (FFI::Pointer)

    native device handle

  • adapter (Adapter, nil) (defaults to: nil)

    adapter that created the device

  • label (String, nil) (defaults to: nil)

    optional debug label

  • callback_lifetime (DeviceCallbackLifetime, nil) (defaults to: nil)

    shared native callback lifetime



205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/wgpu/core/device.rb', line 205

def initialize(handle, adapter: nil, label: nil, callback_state: nil, callback_lifetime: nil)
  @handle = handle
  @adapter = adapter
  @label = label
  @device_callback_lifetime = callback_lifetime
  @device_callback_state = callback_state || {
    mutex: Mutex.new,
    uncaptured_error: nil,
    device_lost: nil
  }
  @device_callback_tokens = []
  @queue = Queue.new(Native.wgpuDeviceGetQueue(@handle), device: self)
end

Instance Attribute Details

#adapterAdapter? (readonly)

Returns the value of attribute adapter.

Returns:



5
6
7
# File 'lib/wgpu/core/device.rb', line 5

def adapter
  @adapter
end

#handleObject (readonly)

Returns the value of attribute handle.

Returns:

  • (Object)


5
6
7
# File 'lib/wgpu/core/device.rb', line 5

def handle
  @handle
end

#queueQueue (readonly)

Returns the value of attribute queue.

Returns:



5
6
7
# File 'lib/wgpu/core/device.rb', line 5

def queue
  @queue
end

Class Method Details

.request(arg0, label:, required_features:, required_limits:, timeout:) ⇒ Device .request(arg0, arg1) ⇒ Device

Requests a logical device and waits for the native callback.

Overloads:

  • .request(arg0, label:, required_features:, required_limits:, timeout:) ⇒ Device

    Parameters:

    • arg0 (Adapter)
    • label: (String, nil)
    • required_features: (Array[enum_value])
    • required_limits: (Hash[Symbol, Integer], nil)
    • timeout: (Numeric, nil)

    Returns:

  • .request(arg0, arg1) ⇒ Device

    Parameters:

    Returns:

Parameters:

  • adapter (Adapter)

    adapter that will create the device

  • label (String, nil) (defaults to: nil)

    optional debugging label

  • required_features (Array<Symbol, Integer>) (defaults to: [])

    features the device must enable

  • required_limits (Hash, nil) (defaults to: nil)

    minimum required limits

  • timeout (Numeric, nil) (defaults to: nil)

    maximum wait time in seconds

Returns:

  • (Device)

    requested device

Raises:

  • (DeviceError)

    if device creation fails

  • (TimeoutError)

    if the request exceeds timeout

  • (ArgumentError)

    if timeout is used without an instance-backed adapter



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/wgpu/core/device.rb', line 20

def self.request(adapter, label: nil, required_features: [], required_limits: nil, timeout: nil)
  if timeout && !adapter.instance
    raise ArgumentError,
      "Device.request timeout requires an instance-backed adapter; " \
      "pass instance: to Adapter.from_handle"
  end

  device_ptr = FFI::MemoryPointer.new(:pointer)
  status_holder = {
    done: false,
    value: nil,
    message: nil,
    abandoned: false,
    cleanup_claimed: false,
    mutex: Mutex.new
  }
  device_callback_state = {
    mutex: Mutex.new,
    uncaptured_error: nil,
    device_lost: nil
  }

  callback_token = nil
  callback = FFI::Function.new(
    :void, [:uint32, :pointer, Native::StringView.by_value, :pointer, :pointer]
  ) do |status, device, message, _userdata1, _userdata2|
    cleanup_abandoned = false
    begin
      status_holder[:mutex].synchronize do
        status_holder[:value] = Native::RequestDeviceStatus[status]
        if message[:data] && !message[:data].null? && message[:length] > 0
          status_holder[:message] = message[:data].read_string(message[:length])
        end
        device_ptr.write_pointer(device)
        status_holder[:done] = true
        if status_holder[:abandoned] && !status_holder[:cleanup_claimed]
          status_holder[:cleanup_claimed] = true
          cleanup_abandoned = true
        end
      end
      if cleanup_abandoned
        if status_holder[:value] == :success && device && !device.null?
          release_abandoned_device(device, device_callback_state)
        else
          release_device_callback_keepalive(device_callback_state)
        end
      end
    ensure
      CallbackKeepalive.release(adapter, callback_token)
    end
  end

  queue_desc = Native::QueueDescriptor.new
  queue_desc[:next_in_chain] = nil
  queue_desc[:label][:data] = nil
  queue_desc[:label][:length] = 0

  device_lost_info = Native::DeviceLostCallbackInfo.new
  device_lost_info[:next_in_chain] = nil
  device_lost_info[:mode] = AsyncWaiter.callback_mode(instance: adapter.instance)
  device_lost_callback = build_device_lost_callback(device_callback_state)
  device_lost_info[:callback] = device_lost_callback
  device_lost_info[:userdata1] = nil
  device_lost_info[:userdata2] = nil

  error_info = Native::UncapturedErrorCallbackInfo.new
  error_info[:next_in_chain] = nil
  uncaptured_error_callback = build_uncaptured_error_callback(device_callback_state)
  error_info[:callback] = uncaptured_error_callback
  error_info[:userdata1] = nil
  error_info[:userdata2] = nil

  desc = Native::DeviceDescriptor.new
  desc[:next_in_chain] = nil
  if label
    label_ptr = FFI::MemoryPointer.from_string(label)
    desc[:label][:data] = label_ptr
    desc[:label][:length] = label.bytesize
  else
    desc[:label][:data] = nil
    desc[:label][:length] = 0
  end

  feature_values = normalize_required_features(required_features)
  if feature_values.empty?
    desc[:required_feature_count] = 0
    desc[:required_features] = nil
  else
    features_ptr = FFI::MemoryPointer.new(:uint32, feature_values.size)
    features_ptr.write_array_of_uint32(feature_values)
    desc[:required_feature_count] = feature_values.size
    desc[:required_features] = features_ptr
  end

  required_limits_struct = build_required_limits(adapter, required_limits)
  desc[:required_limits] = required_limits_struct ? required_limits_struct.to_ptr : nil
  desc[:default_queue] = queue_desc
  desc[:device_lost_callback_info] = device_lost_info
  desc[:uncaptured_error_callback_info] = error_info

  callback_info = Native::RequestDeviceCallbackInfo.new
  callback_info[:next_in_chain] = nil
  callback_info[:mode] = AsyncWaiter.callback_mode(instance: adapter.instance)
  callback_info[:callback] = callback
  callback_info[:userdata1] = nil
  callback_info[:userdata2] = nil

  device_lost_token = CallbackKeepalive.retain(adapter, device_lost_callback)
  uncaptured_error_token = CallbackKeepalive.retain(adapter, uncaptured_error_callback)
  configure_device_callback_keepalive(
    device_callback_state,
    owner: adapter,
    tokens: [device_lost_token, uncaptured_error_token]
  )
  callback_token = CallbackKeepalive.retain(adapter, callback)
  future =
    begin
      Native.wgpuAdapterRequestDevice(adapter.handle, desc, callback_info)
    rescue StandardError
      CallbackKeepalive.release(adapter, callback_token)
      release_device_callback_keepalive(device_callback_state)
      raise
    end

  begin
    AsyncWaiter.wait(
      status_holder: status_holder,
      instance: adapter.instance,
      future: future,
      timeout: timeout
    )
  rescue TimeoutError
    abandoned_device = nil
    cleanup_abandoned = status_holder[:mutex].synchronize do
      status_holder[:abandoned] = true
      next false unless status_holder[:done] && !status_holder[:cleanup_claimed]

      status_holder[:cleanup_claimed] = true
      abandoned_device = device_ptr.read_pointer
      true
    end
    if cleanup_abandoned
      if status_holder[:value] == :success && abandoned_device && !abandoned_device.null?
        release_abandoned_device(abandoned_device, device_callback_state)
      else
        release_device_callback_keepalive(device_callback_state)
      end
    end
    raise
  end

  handle = device_ptr.read_pointer
  if handle.null? || status_holder[:value] != :success
    release_device_callback_keepalive(device_callback_state)
    msg = status_holder[:message] || "Unknown error"
    raise DeviceError, "Failed to request device: #{msg}"
  end

  begin
    callback_lifetime = DeviceCallbackLifetime.new do
      release_device_callback_keepalive(device_callback_state)
    end
    device_callback_state[:mutex].synchronize do
      device_callback_state[:callback_lifetime] = callback_lifetime
    end
    device = new(
      handle,
      adapter: adapter,
      label: label,
      callback_state: device_callback_state,
      callback_lifetime: callback_lifetime
    )
    device.send(:adopt_device_callback_keepalive)
    device
  rescue StandardError
    device ? device.release : release_abandoned_device(handle, device_callback_state)
    raise
  end
end

Instance Method Details

#adapter_infoHash?

Returns information about the adapter that created this device.

Returns:

  • (Hash, nil)


221
222
223
# File 'lib/wgpu/core/device.rb', line 221

def adapter_info
  @adapter&.info
end

#create_bind_group(label: nil, layout:, entries:) ⇒ BindGroup

Creates a bind group.

Parameters:

  • label: (String, nil) (defaults to: nil)
  • layout: (BindGroupLayout)
  • entries: (Array[Hash[Symbol, untyped]])

Returns:



258
259
260
# File 'lib/wgpu/core/device.rb', line 258

def create_bind_group(label: nil, layout:, entries:)
  BindGroup.new(self, label: label, layout: layout, entries: entries)
end

#create_bind_group_layout(label: nil, entries:) ⇒ BindGroupLayout

Creates a bind group layout.

Parameters:

  • label: (String, nil) (defaults to: nil)
  • entries: (Array[Hash[Symbol, untyped]])

Returns:



252
253
254
# File 'lib/wgpu/core/device.rb', line 252

def create_bind_group_layout(label: nil, entries:)
  BindGroupLayout.new(self, label: label, entries: entries)
end

#create_buffer(label: nil, size:, usage:, mapped_at_creation: false) ⇒ Buffer

Creates a GPU buffer.

Parameters:

  • label: (String, nil) (defaults to: nil)
  • size: (Integer)
  • usage: (flags)
  • mapped_at_creation: (Boolean) (defaults to: false)

Returns:



227
228
229
# File 'lib/wgpu/core/device.rb', line 227

def create_buffer(label: nil, size:, usage:, mapped_at_creation: false)
  Buffer.new(self, label: label, size: size, usage: usage, mapped_at_creation: mapped_at_creation)
end

#create_buffer_with_data(label: nil, data:, usage:, type: :f32) ⇒ Buffer

Creates a mapped buffer initialized from typed Ruby data.

Parameters:

  • data (Array, String, FFI::Pointer)

    source data

  • usage (Symbol, Array<Symbol>, Integer)

    buffer usage flags

  • type (Symbol) (defaults to: :f32)

    source element type

  • label: (String, nil) (defaults to: nil)
  • data: (data)
  • usage: (flags)
  • type: (Symbol) (defaults to: :f32)

Returns:



350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/wgpu/core/device.rb', line 350

def create_buffer_with_data(label: nil, data:, usage:, type: :f32)
  data_ptr, byte_size = DataTypes.to_pointer(data, type:)
  DataTypes.validate_alignment!(byte_size, 4, name: "buffer data size")
  buffer = create_buffer(
    label: label,
    size: byte_size,
    usage: usage,
    mapped_at_creation: true
  )
  buffer.mapped_range.write_bytes(data_ptr.read_bytes(byte_size))
  buffer.unmap
  buffer
end

#create_command_encoder(label: nil) ⇒ CommandEncoder

Creates a command encoder.

Parameters:

  • label: (String, nil) (defaults to: nil)

Returns:



246
247
248
# File 'lib/wgpu/core/device.rb', line 246

def create_command_encoder(label: nil)
  CommandEncoder.new(self, label: label)
end

#create_compute_pipeline(label: nil, layout:, compute:) ⇒ ComputePipeline

Creates a compute pipeline.

Parameters:

  • (Object)

Returns:



270
271
272
# File 'lib/wgpu/core/device.rb', line 270

def create_compute_pipeline(label: nil, layout:, compute:)
  ComputePipeline.new(self, label: label, layout: layout, compute: compute)
end

#create_compute_pipeline_async(label: nil, layout:, compute:) ⇒ AsyncTask

Creates a compute pipeline on a background task.

Parameters:

  • (Object)

Returns:



276
277
278
279
280
# File 'lib/wgpu/core/device.rb', line 276

def create_compute_pipeline_async(label: nil, layout:, compute:)
  AsyncTask.new do
    create_compute_pipeline(label: label, layout: layout, compute: compute)
  end
end

#create_pipeline_layout(label: nil, bind_group_layouts:) ⇒ PipelineLayout

Creates a pipeline layout.

Parameters:

  • label: (String, nil) (defaults to: nil)
  • bind_group_layouts: (Array[BindGroupLayout])

Returns:



264
265
266
# File 'lib/wgpu/core/device.rb', line 264

def create_pipeline_layout(label: nil, bind_group_layouts:)
  PipelineLayout.new(self, label: label, bind_group_layouts: bind_group_layouts)
end

#create_query_set(label: nil, type:, count:) ⇒ QuerySet

Creates a GPU query set.

Parameters:

  • label: (String, nil) (defaults to: nil)
  • type: (enum_value)
  • count: (Integer)

Returns:



366
367
368
# File 'lib/wgpu/core/device.rb', line 366

def create_query_set(label: nil, type:, count:)
  QuerySet.new(self, label: label, type: type, count: count)
end

#create_render_bundle_encoder(color_formats:, depth_stencil_format: nil, sample_count: 1, depth_read_only: false, stencil_read_only: false, label: nil) ⇒ RenderBundleEncoder

Creates an encoder for reusable render commands.

Parameters:

  • (Object)

Returns:



372
373
374
375
376
377
378
379
380
381
382
# File 'lib/wgpu/core/device.rb', line 372

def create_render_bundle_encoder(color_formats:, depth_stencil_format: nil, sample_count: 1,
                                 depth_read_only: false, stencil_read_only: false, label: nil)
  RenderBundleEncoder.new(self,
    color_formats: color_formats,
    depth_stencil_format: depth_stencil_format,
    sample_count: sample_count,
    depth_read_only: depth_read_only,
    stencil_read_only: stencil_read_only,
    label: label
  )
end

#create_render_pipeline(label: nil, layout:, vertex:, primitive: {}, depth_stencil: nil, multisample: {}, fragment: nil) ⇒ RenderPipeline

Creates a render pipeline.

Parameters:

  • (Object)

Returns:



284
285
286
287
288
289
290
291
292
293
294
# File 'lib/wgpu/core/device.rb', line 284

def create_render_pipeline(label: nil, layout:, vertex:, primitive: {}, depth_stencil: nil, multisample: {}, fragment: nil)
  RenderPipeline.new(self,
    label: label,
    layout: layout,
    vertex: vertex,
    primitive: primitive,
    depth_stencil: depth_stencil,
    multisample: multisample,
    fragment: fragment
  )
end

#create_render_pipeline_async(label: nil, layout:, vertex:, primitive: {}, depth_stencil: nil, multisample: {}, fragment: nil) ⇒ AsyncTask

Creates a render pipeline on a background task.

Parameters:

  • (Object)

Returns:



298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/wgpu/core/device.rb', line 298

def create_render_pipeline_async(label: nil, layout:, vertex:, primitive: {}, depth_stencil: nil, multisample: {}, fragment: nil)
  AsyncTask.new do
    create_render_pipeline(
      label: label,
      layout: layout,
      vertex: vertex,
      primitive: primitive,
      depth_stencil: depth_stencil,
      multisample: multisample,
      fragment: fragment
    )
  end
end

#create_sampler(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) ⇒ Sampler

Creates a texture sampler.

Parameters:

  • (Object)

Returns:



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/wgpu/core/device.rb', line 329

def create_sampler(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)
  Sampler.new(self,
    label: label,
    address_mode_u: address_mode_u,
    address_mode_v: address_mode_v,
    address_mode_w: address_mode_w,
    mag_filter: mag_filter,
    min_filter: min_filter,
    mipmap_filter: mipmap_filter,
    lod_min_clamp: lod_min_clamp,
    lod_max_clamp: lod_max_clamp,
    compare: compare,
    max_anisotropy: max_anisotropy
  )
end

#create_shader_module(label: nil, code: nil, spirv: nil, compilation_hints: [], validate: false) ⇒ ShaderModule

Creates a shader module from WGSL or SPIR-V source.

Parameters:

  • (Object)

Returns:



233
234
235
236
237
238
239
240
241
242
# File 'lib/wgpu/core/device.rb', line 233

def create_shader_module(label: nil, code: nil, spirv: nil, compilation_hints: [], validate: false)
  ShaderModule.new(
    self,
    label: label,
    code: code,
    spirv: spirv,
    compilation_hints: compilation_hints,
    validate: validate
  )
end

#create_texture(label: nil, size:, format:, usage:, dimension: :d2, mip_level_count: 1, sample_count: 1, view_formats: []) ⇒ Texture

Creates a texture.

Parameters:

  • (Object)

Returns:



314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/wgpu/core/device.rb', line 314

def create_texture(label: nil, size:, format:, usage:, dimension: :d2, mip_level_count: 1, sample_count: 1, view_formats: [])
  Texture.new(self,
    label: label,
    size: size,
    format: format,
    usage: usage,
    dimension: dimension,
    mip_level_count: mip_level_count,
    sample_count: sample_count,
    view_formats: view_formats
  )
end

#destroyvoid

This method returns an undefined value.

Destroys device-owned native resources.



538
539
540
541
# File 'lib/wgpu/core/device.rb', line 538

def destroy
  return if @handle.null?
  Native.wgpuDeviceDestroy(@handle)
end

#featuresArray<Symbol>

Lists optional features enabled on the device.

Returns:

  • (Array<Symbol>)


386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/wgpu/core/device.rb', line 386

def features
  supported = Native::SupportedFeatures.new
  Native.wgpuDeviceGetFeatures(@handle, supported)

  result = []
  if supported[:feature_count] > 0 && !supported[:features].null?
    supported[:features].read_array_of_uint32(supported[:feature_count]).each do |f|
      result << Native::FeatureName[f]
    end
  end
  result
end

#has_feature?(feature) ⇒ Boolean

Reports whether a feature is enabled on the device.

Parameters:

  • feature (Symbol)

    feature name

  • (Symbol)

Returns:

  • (Boolean)


402
403
404
# File 'lib/wgpu/core/device.rb', line 402

def has_feature?(feature)
  features.include?(feature)
end

#limitsHash{Symbol => Integer}

Returns resource limits supported by the device.

Returns:

  • (Hash{Symbol => Integer})


408
409
410
411
412
413
# File 'lib/wgpu/core/device.rb', line 408

def limits
  supported = Native::SupportedLimits.new
  supported[:next_in_chain] = nil
  Native.wgpuDeviceGetLimits(@handle, supported)
  limits_to_hash(supported[:limits])
end

#on_device_lost {|reason, message, arg0, arg1| ... } ⇒ Device

Registers the handler invoked when the device is lost.

Yields:

Yield Parameters:

  • reason (Symbol)

    native device-lost reason

  • message (String)

    native diagnostic message

  • arg0 (Symbol)
  • arg1 (String)

Yield Returns:

  • (void)

Returns:

Raises:

  • (ArgumentError)

    if no block is provided



529
530
531
532
533
534
# File 'lib/wgpu/core/device.rb', line 529

def on_device_lost(&handler)
  raise ArgumentError, "on_device_lost requires a block" unless handler

  set_device_callback(:device_lost, handler)
  self
end

#on_uncaptured_error {|error, arg0| ... } ⇒ Device

Registers the handler for uncaptured GPU errors.

Yields:

Yield Parameters:

Yield Returns:

  • (void)

Returns:

Raises:

  • (ArgumentError)

    if no block is provided



517
518
519
520
521
522
# File 'lib/wgpu/core/device.rb', line 517

def on_uncaptured_error(&handler)
  raise ArgumentError, "on_uncaptured_error requires a block" unless handler

  set_device_callback(:uncaptured_error, handler)
  self
end

#poll(wait: false) ⇒ Integer

Processes device work, optionally waiting for queue progress.

Parameters:

  • wait (Boolean) (defaults to: false)

    whether to wait for submitted work

  • wait: (Boolean) (defaults to: false)

Returns:

  • (Integer)

    native poll status



418
419
420
421
422
423
424
425
# File 'lib/wgpu/core/device.rb', line 418

def poll(wait: false)
  if Native.device_poll_available?
    Native.wgpuDevicePoll(@handle, wait ? 1 : 0, nil)
  else
    @adapter&.instance&.process_events
    0
  end
end

#pop_error_scope(timeout: nil) ⇒ Hash

Pops the latest error scope and waits for its result.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    maximum wait time in seconds

  • timeout: (Numeric, nil) (defaults to: nil)

Returns:

  • (Hash)

    native status, error type, and message



438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/wgpu/core/device.rb', line 438

def pop_error_scope(timeout: nil)
  error_holder = { done: false, status: nil, type: nil, message: nil }

  callback_lifetime_release = device_callback_lifetime_lease
  callback_token = nil
  callback = FFI::Function.new(
    :void, [:uint32, :uint32, Native::StringView.by_value, :pointer, :pointer]
  ) do |status, error_type, message, _userdata1, _userdata2|
    begin
      error_holder[:status] = Native::PopErrorScopeStatus[status]
      error_holder[:type] = Native::ErrorType[error_type]
      if message[:data] && !message[:data].null? && message[:length] > 0
        error_holder[:message] = message[:data].read_string(message[:length])
      end
      error_holder[:done] = true
    ensure
      CallbackKeepalive.release(self, callback_token)
      callback_lifetime_release.call
    end
  end

  callback_info = Native::PopErrorScopeCallbackInfo.new
  callback_info[:next_in_chain] = nil
  callback_info[:mode] = AsyncWaiter.callback_mode(instance: @adapter&.instance)
  callback_info[:callback] = callback
  callback_info[:userdata1] = nil
  callback_info[:userdata2] = nil

  callback_token = CallbackKeepalive.retain(self, callback)
  future =
    begin
      Native.wgpuDevicePopErrorScope(@handle, callback_info)
    rescue StandardError
      CallbackKeepalive.release(self, callback_token)
      callback_lifetime_release.call
      raise
    end
  AsyncWaiter.wait(
    status_holder: error_holder,
    instance: @adapter&.instance,
    device: self,
    future: future,
    timeout: timeout
  )

  error_holder
end

#pop_error_scope_async(timeout: nil) ⇒ AsyncTask

Pops the latest error scope on a background thread.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    maximum wait time in seconds

  • timeout: (Numeric, nil) (defaults to: nil)

Returns:

  • (AsyncTask)

    task whose value is the error hash



490
491
492
# File 'lib/wgpu/core/device.rb', line 490

def pop_error_scope_async(timeout: nil)
  AsyncTask.new { pop_error_scope(timeout: timeout) }
end

#pop_error_scope_typed(timeout: nil) ⇒ GPUError?

Pops the latest error scope as a typed error.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    maximum wait time in seconds

  • timeout: (Numeric, nil) (defaults to: nil)

Returns:



497
498
499
# File 'lib/wgpu/core/device.rb', line 497

def pop_error_scope_typed(timeout: nil)
  GPUError.from_hash(pop_error_scope(timeout: timeout))
end

#push_error_scope(filter = :validation) ⇒ void

This method returns an undefined value.

Pushes a scope that captures matching GPU errors.

Parameters:

  • filter (Symbol, Integer) (defaults to: :validation)

    error filter

  • (enum_value)


430
431
432
433
# File 'lib/wgpu/core/device.rb', line 430

def push_error_scope(filter = :validation)
  filter_value = Native::EnumHelper.coerce(Native::ErrorFilter, filter, name: "error filter")
  Native.wgpuDevicePushErrorScope(@handle, filter_value)
end

#releasevoid

This method returns an undefined value.

Releases the default queue and native device handle.

Device callbacks remain alive until all derived wrappers and pending callback operations have also completed their native releases.

Calling this method more than once has no effect.



550
551
552
553
554
555
556
# File 'lib/wgpu/core/device.rb', line 550

def release
  @queue&.release
  return if @handle.null?
  Native.wgpuDeviceRelease(@handle)
  @handle = FFI::Pointer::NULL
  @device_callback_tokens = nil
end

#with_error_scope(filter = :validation) ⇒ Object

Runs a block inside an error scope and raises captured GPU errors.

Parameters:

  • filter (Symbol, Integer) (defaults to: :validation)

    error filter

Yield Returns:

  • (Object)

    block result

Returns:

  • (Object)

    block result



505
506
507
508
509
510
511
# File 'lib/wgpu/core/device.rb', line 505

def with_error_scope(filter = :validation)
  push_error_scope(filter)
  result = yield
  error = GPUError.from_hash(pop_error_scope)
  error&.raise!
  result
end