lambda_platform/gfx/
buffer.rs

1use gfx_hal::{
2  memory::{
3    Segment,
4    SparseFlags,
5  },
6  prelude::Device,
7  Backend,
8};
9
10use super::gpu::Gpu;
11
12// Reuse gfx-hal buffer usage & properties for now.
13pub type Usage = gfx_hal::buffer::Usage;
14pub type Properties = gfx_hal::memory::Properties;
15
16/// The type of buffers that can be allocated on the GPU.
17#[derive(Debug, Clone, Copy)]
18pub enum BufferType {
19  Vertex,
20  Index,
21  Uniform,
22  Storage,
23}
24
25/// A buffer is a block of memory that can be used to store data that can be
26/// accessed by the GPU.
27#[derive(Debug, Clone, Copy)]
28pub struct Buffer<RenderBackend: Backend> {
29  buffer: RenderBackend::Buffer,
30  memory: RenderBackend::Memory,
31  stride: usize,
32  buffer_type: BufferType,
33}
34
35impl<RenderBackend: Backend> Buffer<RenderBackend> {
36  /// Destroy the buffer and all it's resources with the GPU that
37  /// created it.
38  pub fn destroy(self, gpu: &Gpu<RenderBackend>) {
39    unsafe {
40      gpu.internal_logical_device().free_memory(self.memory);
41      gpu.internal_logical_device().destroy_buffer(self.buffer);
42    }
43  }
44
45  /// Size of the buffer in bytes.
46  pub fn stride(&self) -> usize {
47    return self.stride;
48  }
49}
50
51impl<RenderBackend: Backend> Buffer<RenderBackend> {
52  /// Retrieve a reference to the internal buffer.
53  pub(super) fn internal_buffer(&self) -> &RenderBackend::Buffer {
54    return &self.buffer;
55  }
56}
57
58pub struct BufferBuilder {
59  buffer_length: usize,
60  usage: Usage,
61  properties: Properties,
62  buffer_type: BufferType,
63}
64
65impl BufferBuilder {
66  pub fn new() -> Self {
67    return Self {
68      buffer_length: 0,
69      usage: Usage::empty(),
70      properties: Properties::empty(),
71      buffer_type: BufferType::Vertex,
72    };
73  }
74
75  pub fn with_length(&mut self, length: usize) -> &mut Self {
76    self.buffer_length = length;
77    return self;
78  }
79
80  pub fn with_usage(&mut self, usage: Usage) -> &mut Self {
81    self.usage = usage;
82    return self;
83  }
84
85  pub fn with_properties(&mut self, properties: Properties) -> &mut Self {
86    self.properties = properties;
87    return self;
88  }
89
90  pub fn with_buffer_type(&mut self, buffer_type: BufferType) -> &mut Self {
91    self.buffer_type = buffer_type;
92    return self;
93  }
94
95  /// Builds & binds a buffer of memory to the GPU. If the buffer cannot be
96  /// bound to the GPU, the buffer memory is freed before the error is returned.
97  /// Data must represent the data that will be stored in the buffer, meaning
98  /// it must repr C and be the same size as the buffer length.
99  pub fn build<RenderBackend: Backend, Data: Sized>(
100    &self,
101    gpu: &mut Gpu<RenderBackend>,
102    data: Vec<Data>,
103  ) -> Result<Buffer<RenderBackend>, &'static str> {
104    use gfx_hal::{
105      adapter::PhysicalDevice,
106      MemoryTypeId,
107    };
108    let logical_device = gpu.internal_logical_device();
109    let physical_device = gpu.internal_physical_device();
110
111    // TODO(vmarcella): Add the ability for the user to specify the memory
112    // properties (I.E. SparseFlags::SPARSE_MEMORY).
113    logging::debug!(
114      "[DEBUG] Creating buffer of length: {}",
115      self.buffer_length
116    );
117    let buffer_result = unsafe {
118      logical_device.create_buffer(
119        self.buffer_length as u64,
120        self.usage,
121        SparseFlags::empty(),
122      )
123    };
124
125    if buffer_result.is_err() {
126      logging::error!("Failed to create buffer for allocating memory.");
127      return Err("Failed to create buffer for allocating memory.");
128    }
129
130    let mut buffer = buffer_result.unwrap();
131
132    let requirements =
133      unsafe { logical_device.get_buffer_requirements(&buffer) };
134    let memory_types = physical_device.memory_properties().memory_types;
135
136    logging::debug!("Buffer requirements: {:?}", requirements);
137    // Find a memory type that supports the requirements of the buffer.
138    let memory_type = memory_types
139      .iter()
140      .enumerate()
141      .find(|(id, memory_type)| {
142        let type_supported = requirements.type_mask & (1 << id) != 0;
143        type_supported && memory_type.properties.contains(self.properties)
144      })
145      .map(|(id, _)| MemoryTypeId(id))
146      .unwrap();
147
148    logging::debug!("Allocating memory for buffer.");
149    // Allocates the memory on the GPU for the buffer.
150    let buffer_memory_allocation =
151      unsafe { logical_device.allocate_memory(memory_type, requirements.size) };
152
153    if buffer_memory_allocation.is_err() {
154      logging::error!("Failed to allocate memory for buffer.");
155      return Err("Failed to allocate memory for buffer.");
156    }
157
158    let mut buffer_memory = buffer_memory_allocation.unwrap();
159
160    // Bind the buffer to the GPU memory
161    let buffer_binding = unsafe {
162      logical_device.bind_buffer_memory(&buffer_memory, 0, &mut buffer)
163    };
164
165    // Destroy the buffer if we failed to bind it to memory.
166    if buffer_binding.is_err() {
167      unsafe { logical_device.destroy_buffer(buffer) };
168      logging::error!("Failed to bind buffer memory.");
169      return Err("Failed to bind buffer memory.");
170    }
171
172    // Get address of the buffer memory on the GPU so that we can write to it.
173    let get_mapping_to_memory =
174      unsafe { logical_device.map_memory(&mut buffer_memory, Segment::ALL) };
175
176    if get_mapping_to_memory.is_err() {
177      unsafe { logical_device.destroy_buffer(buffer) };
178      logging::error!("Failed to map memory.");
179      return Err("Failed to map memory.");
180    }
181    let mapped_memory = get_mapping_to_memory.unwrap();
182
183    // Copy the data to the GPU memory.
184    unsafe {
185      std::ptr::copy_nonoverlapping(
186        data.as_ptr() as *const u8,
187        mapped_memory,
188        self.buffer_length,
189      );
190    };
191
192    // Flush the data to ensure it is written to the GPU memory.
193    let memory_flush = unsafe {
194      logical_device
195        .flush_mapped_memory_ranges(std::iter::once((
196          &buffer_memory,
197          Segment::ALL,
198        )))
199        .map_err(|_| "Failed to flush memory.")
200    };
201
202    if memory_flush.is_err() {
203      unsafe { logical_device.destroy_buffer(buffer) };
204      logging::error!("Failed to flush memory.");
205      return Err("No memory available on the GPU.");
206    }
207
208    // Unmap the memory now that it's no longer needed by the CPU.
209    unsafe { logical_device.unmap_memory(&mut buffer_memory) };
210
211    return Ok(Buffer {
212      buffer,
213      memory: buffer_memory,
214      stride: std::mem::size_of::<Data>(),
215      buffer_type: self.buffer_type,
216    });
217  }
218}