Class: WGPU::Buffer

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

Constant Summary

Constants included from NativeResource

NativeResource::GUARDED_METHOD_EXEMPTIONS

Instance Attribute Summary collapse

Attributes included from NativeResource

#label

Instance Method Summary collapse

Methods included from NativeResource

checked_handle, included, #inspect, #released?, #use

Constructor Details

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

Creates a GPU buffer with the requested size and usage.

Parameters:

  • device (Device)

    owning device

  • size (Integer)

    buffer size in bytes

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

    usage flags

  • mapped_at_creation (Boolean) (defaults to: false)

    whether to expose an initial mapped range

Raises:

  • (BufferError)

    if native validation or creation fails



15
16
17
18
19
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
# File 'lib/wgpu/resources/buffer.rb', line 15

def initialize(device, label: nil, size:, usage:, mapped_at_creation: false)
  @device = device
  @size = size
  @usage =
    begin
      normalize_usage(usage)
    rescue ArgumentError => e
      raise ArgumentError, buffer_error_message(e.message, label)
    end
  @mapped = mapped_at_creation
  @map_state = mapped_at_creation ? :mapped : :unmapped
  @map_generation = 0
  @map_state_mutex = Monitor.new
  @mapped_offset = 0
  @mapped_size = mapped_at_creation ? size : 0

  desc, keepalive = build_descriptor(
    label:,
    size:,
    usage: @usage,
    mapped_at_creation:
  )
  @descriptor_keepalive = keepalive

  error = device.send(:capture_error_scope) do
    @handle = Native.wgpuDeviceCreateBuffer(NativeResource.checked_handle(device, expected_class: Device), desc)
  end
  @descriptor_keepalive = nil

  if @handle.null? || (error[:type] && error[:type] != :no_error)
    msg = error[:message] || "Failed to create buffer"
    raise BufferError, buffer_error_message(msg, label)
  end
end

Instance Attribute Details

#handleObject (readonly)

Returns the value of attribute handle.

Returns:

  • (Object)


7
8
9
# File 'lib/wgpu/resources/buffer.rb', line 7

def handle
  @handle
end

#sizeInteger (readonly)

Returns the value of attribute size.

Returns:

  • (Integer)


7
8
9
# File 'lib/wgpu/resources/buffer.rb', line 7

def size
  @size
end

#usageInteger (readonly)

Returns the value of attribute usage.

Returns:

  • (Integer)


7
8
9
# File 'lib/wgpu/resources/buffer.rb', line 7

def usage
  @usage
end

Instance Method Details

#destroyvoid

This method returns an undefined value.

Destroys the buffer's storage.



239
240
241
242
243
244
# File 'lib/wgpu/resources/buffer.rb', line 239

def destroy
  map_state_mutex.synchronize do
    Native.wgpuBufferDestroy(@handle)
    invalidate_map_state
  end
end

#get_mapped_range(offset: 0, size: nil) ⇒ BufferMappedRange

Returns a writable view of a mapped byte range.

Parameters:

  • offset: (Integer) (defaults to: 0)
  • size: (Integer, nil) (defaults to: nil)

Returns:



80
81
82
# File 'lib/wgpu/resources/buffer.rb', line 80

def get_mapped_range(offset: 0, size: nil)
  mapped_range(offset: offset, size: size)
end

#map_async(mode, offset: 0, size: nil) ⇒ AsyncTask

Maps a range on a background task.

Parameters:

  • mode (Symbol, Integer)

    map access mode

  • (enum_value)
  • offset: (Integer) (defaults to: 0)
  • size: (Integer, nil) (defaults to: nil)

Returns:

  • (AsyncTask)

    task yielding true when mapped



109
110
111
112
113
114
115
116
# File 'lib/wgpu/resources/buffer.rb', line 109

def map_async(mode, offset: 0, size: nil)
  status_holder, _callback_token, future, generation =
    begin_map_request(mode, offset: offset, size: size)
  AsyncTask.new do
    wait_for_map(status_holder, future)
    finalize_map(status_holder, generation)
  end
end

#map_stateSymbol

Returns the current mapping state.

Returns:

  • (Symbol)


231
232
233
234
235
# File 'lib/wgpu/resources/buffer.rb', line 231

def map_state
  return map_state_mutex.synchronize { @map_state } unless Native.buffer_map_state_available?

  Native.wgpuBufferGetMapState(@handle) || map_state_mutex.synchronize { @map_state }
end

#map_sync(mode, offset: 0, size: nil, timeout: nil) ⇒ Boolean

Maps a range and waits for native completion.

Parameters:

  • mode (Symbol, Integer)

    map access mode

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

    maximum wait time in seconds

  • (enum_value)
  • offset: (Integer) (defaults to: 0)
  • size: (Integer, nil) (defaults to: nil)
  • timeout: (Numeric, nil) (defaults to: nil)

Returns:

  • (Boolean)

    true when mapped

Raises:



98
99
100
101
102
103
104
# File 'lib/wgpu/resources/buffer.rb', line 98

def map_sync(mode, offset: 0, size: nil, timeout: nil)
  timeout = AsyncWaiter.normalize_timeout(timeout)
  status_holder, _callback_token, future, generation =
    begin_map_request(mode, offset: offset, size: size)
  wait_for_map(status_holder, future, timeout:)
  finalize_map(status_holder, generation)
end

#mapped_range(offset: 0, size: nil) ⇒ BufferMappedRange

Returns a writable view of a mapped byte range.

Parameters:

  • offset: (Integer) (defaults to: 0)
  • size: (Integer, nil) (defaults to: nil)

Returns:

Raises:

  • (BufferError)

    if the buffer is not mapped or no range is available



65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/wgpu/resources/buffer.rb', line 65

def mapped_range(offset: 0, size: nil)
  map_state_mutex.synchronize do
    raise BufferError, "Buffer is not mapped" unless @mapped

    size ||= @size - offset
    offset, size = validate_mapped_access!(offset, size)
    ptr = Native.wgpuBufferGetMappedRange(@handle, offset, size)
    raise BufferError, "Failed to get mapped range" if ptr.null?

    BufferMappedRange.new(ptr, size, buffer: self, generation: current_map_generation)
  end
end

#read_mapped(offset: 0, size: nil, type: nil) ⇒ String, Array

Reads mapped bytes, optionally decoding typed values.

Parameters:

  • type (Symbol, nil) (defaults to: nil)

    element type, or nil for raw bytes

  • offset: (Integer) (defaults to: 0)
  • size: (Integer, nil) (defaults to: nil)
  • type: (Symbol, nil) (defaults to: nil)

Returns:

  • (String, Array)


137
138
139
140
# File 'lib/wgpu/resources/buffer.rb', line 137

def read_mapped(offset: 0, size: nil, type: nil)
  bytes = read_mapped_data(offset: offset, size: size)
  type ? DataTypes.unpack(bytes, type:) : bytes
end

#read_mapped_data(offset: 0, size: nil) ⇒ String

Copies bytes from a const mapped range.

Parameters:

  • offset: (Integer) (defaults to: 0)
  • size: (Integer, nil) (defaults to: nil)

Returns:

  • (String)

    mapped bytes

Raises:



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/wgpu/resources/buffer.rb', line 121

def read_mapped_data(offset: 0, size: nil)
  map_state_mutex.synchronize do
    raise BufferError, "Buffer is not mapped" unless @mapped

    size ||= @size - offset
    offset, size = validate_mapped_access!(offset, size)
    ptr = Native.wgpuBufferGetConstMappedRange(@handle, offset, size)
    raise BufferError, "Failed to get mapped range" if ptr.null?

    ptr.read_bytes(size)
  end
end

#read_mapped_float64s(offset: 0, count: nil) ⇒ Array<Float>

Reads mapped bytes as 64-bit floating-point values.

Parameters:

  • offset (Integer) (defaults to: 0)

    byte offset in the buffer

  • count (Integer, nil) (defaults to: nil)

    values to read; defaults to the remaining range

  • offset: (Integer) (defaults to: 0)
  • count: (Integer, nil) (defaults to: nil)

Returns:

  • (Array<Float>)

    decoded values

Raises:



195
196
197
# File 'lib/wgpu/resources/buffer.rb', line 195

def read_mapped_float64s(offset: 0, count: nil)
  read_mapped_values(type: :f64, offset:, count:)
end

#read_mapped_floats(offset: 0, count: nil) ⇒ Array<Float>

Reads mapped bytes as 32-bit floating-point values.

Parameters:

  • offset (Integer) (defaults to: 0)

    byte offset in the buffer

  • count (Integer, nil) (defaults to: nil)

    values to read; defaults to the remaining range

  • offset: (Integer) (defaults to: 0)
  • count: (Integer, nil) (defaults to: nil)

Returns:

  • (Array<Float>)

    decoded values

Raises:



165
166
167
# File 'lib/wgpu/resources/buffer.rb', line 165

def read_mapped_floats(offset: 0, count: nil)
  read_mapped_values(type: :f32, offset:, count:)
end

#read_mapped_int32s(offset: 0, count: nil) ⇒ Array<Integer>

Reads mapped bytes as signed 32-bit integers.

Parameters:

  • offset (Integer) (defaults to: 0)

    byte offset in the buffer

  • count (Integer, nil) (defaults to: nil)

    values to read; defaults to the remaining range

  • offset: (Integer) (defaults to: 0)
  • count: (Integer, nil) (defaults to: nil)

Returns:

  • (Array<Integer>)

    decoded values

Raises:



185
186
187
# File 'lib/wgpu/resources/buffer.rb', line 185

def read_mapped_int32s(offset: 0, count: nil)
  read_mapped_values(type: :i32, offset:, count:)
end

#read_mapped_uint16s(offset: 0, count: nil) ⇒ Array<Integer>

Reads mapped bytes as unsigned 16-bit integers.

Parameters:

  • offset (Integer) (defaults to: 0)

    byte offset in the buffer

  • count (Integer, nil) (defaults to: nil)

    values to read; defaults to the remaining range

  • offset: (Integer) (defaults to: 0)
  • count: (Integer, nil) (defaults to: nil)

Returns:

  • (Array<Integer>)

    decoded values

Raises:



205
206
207
# File 'lib/wgpu/resources/buffer.rb', line 205

def read_mapped_uint16s(offset: 0, count: nil)
  read_mapped_values(type: :u16, offset:, count:)
end

#read_mapped_uint32s(offset: 0, count: nil) ⇒ Array<Integer>

Reads mapped bytes as unsigned 32-bit integers.

Parameters:

  • offset (Integer) (defaults to: 0)

    byte offset in the buffer

  • count (Integer, nil) (defaults to: nil)

    values to read; defaults to the remaining range

  • offset: (Integer) (defaults to: 0)
  • count: (Integer, nil) (defaults to: nil)

Returns:

  • (Array<Integer>)

    decoded values

Raises:



175
176
177
# File 'lib/wgpu/resources/buffer.rb', line 175

def read_mapped_uint32s(offset: 0, count: nil)
  read_mapped_values(type: :u32, offset:, count:)
end

#read_mapped_uint8s(offset: 0, count: nil) ⇒ Array<Integer>

Reads mapped bytes as unsigned 8-bit integers.

Parameters:

  • offset (Integer) (defaults to: 0)

    byte offset in the buffer

  • count (Integer, nil) (defaults to: nil)

    values to read; defaults to the remaining range

  • offset: (Integer) (defaults to: 0)
  • count: (Integer, nil) (defaults to: nil)

Returns:

  • (Array<Integer>)

    decoded values

Raises:



215
216
217
# File 'lib/wgpu/resources/buffer.rb', line 215

def read_mapped_uint8s(offset: 0, count: nil)
  read_mapped_values(type: :u8, offset:, count:)
end

#read_mapped_values(type: :f32, offset: 0, count: nil) ⇒ Array

Reads mapped values of the requested element type.

Parameters:

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

    element type

  • count (Integer, nil) (defaults to: nil)

    number of values

  • type: (Symbol) (defaults to: :f32)
  • offset: (Integer) (defaults to: 0)
  • count: (Integer, nil) (defaults to: nil)

Returns:

  • (Array)


223
224
225
226
227
# File 'lib/wgpu/resources/buffer.rb', line 223

def read_mapped_values(type: :f32, offset: 0, count: nil)
  element_size = DataTypes.byte_size(type)
  size = count ? count * element_size : @size - offset
  DataTypes.unpack(read_mapped_data(offset:, size:), type:)
end

#releasevoid

This method returns an undefined value.

Releases the native buffer handle.

Calling this method more than once has no effect.



250
251
252
253
254
255
256
257
# File 'lib/wgpu/resources/buffer.rb', line 250

def release
  map_state_mutex.synchronize do
    return if @handle.null?
    Native.wgpuBufferRelease(@handle)
    @handle = FFI::Pointer::NULL
    invalidate_map_state
  end
end

#unmapvoid

This method returns an undefined value.

Unmaps the buffer and invalidates its mapped ranges.



86
87
88
89
90
91
# File 'lib/wgpu/resources/buffer.rb', line 86

def unmap
  map_state_mutex.synchronize do
    Native.wgpuBufferUnmap(@handle)
    invalidate_map_state
  end
end

#write(data, offset: 0, type: :f32) ⇒ void

This method returns an undefined value.

Writes typed data through the device's default queue.

Parameters:

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

    source data

  • offset (Integer) (defaults to: 0)

    destination byte offset

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

    source element type

  • (data)
  • offset: (Integer) (defaults to: 0)
  • type: (Symbol) (defaults to: :f32)


55
56
57
58
59
60
# File 'lib/wgpu/resources/buffer.rb', line 55

def write(data, offset: 0, type: :f32)
  ptr, byte_size = DataTypes.to_pointer(data, type:)
  DataTypes.validate_alignment!(offset, 4, name: "offset")
  DataTypes.validate_alignment!(byte_size, 4, name: "data size")
  Native.wgpuQueueWriteBuffer(NativeResource.checked_handle(@device.queue, expected_class: Queue), @handle, offset, ptr, byte_size)
end

#write_mapped(data, offset: 0, type: :f32) ⇒ void

This method returns an undefined value.

Writes typed data into a mapped range.

Parameters:

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

    source data

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

    source element type

  • (data)
  • offset: (Integer) (defaults to: 0)
  • type: (Symbol) (defaults to: :f32)


146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/wgpu/resources/buffer.rb', line 146

def write_mapped(data, offset: 0, type: :f32)
  map_state_mutex.synchronize do
    raise BufferError, "Buffer is not mapped" unless @mapped

    ptr, byte_size = DataTypes.to_pointer(data, type:)
    offset, byte_size = validate_mapped_access!(offset, byte_size)
    target = Native.wgpuBufferGetMappedRange(@handle, offset, byte_size)
    raise BufferError, "Failed to get mapped range" if target.null?

    target.put_bytes(0, ptr.read_bytes(byte_size))
  end
end