1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
|
#![allow(unsafe_code)]
use crate::error::*;
use std::mem::size_of;
use std::ptr::copy_nonoverlapping;
use vulkanalia::{ Device, Instance };
use vulkanalia::vk::{ self, Handle, HasBuilder, InstanceV1_0, DeviceV1_0 };
#[allow(unsafe_code)]
pub fn init_buffer<T>(instance: &Instance, device: &Device, queue: &vk::Queue,
command_pool: &vk::CommandPool,
usage: vk::BufferUsageFlags, contents: &[T])
-> Result<(vk::Buffer, vk::DeviceMemory)>
{
let (staging_buffer, staging_memory, size)
= stage_in_buffer(instance, device, contents)?;
let final_usage = vk::BufferUsageFlags::TRANSFER_DST | usage;
let final_memory_flags = vk::MemoryPropertyFlags::DEVICE_LOCAL;
let (final_buffer, device_memory)
= allocate_buffer(instance, device, size as vk::DeviceSize,
final_usage, final_memory_flags)?;
copy_buffer(device, queue, command_pool, &staging_buffer, &final_buffer,
size as vk::DeviceSize)?;
unsafe { device.destroy_buffer(staging_buffer, None) };
unsafe { device.free_memory(staging_memory, None) };
Ok((final_buffer, device_memory))
}
pub fn stage_in_buffer<T>(instance: &Instance, device: &Device,
contents: &[T])
-> Result<(vk::Buffer, vk::DeviceMemory, usize)>
{
let size = size_of::<T>() * contents.len();
let staging_usage = vk::BufferUsageFlags::TRANSFER_SRC;
let staging_memory_flags = vk::MemoryPropertyFlags::HOST_COHERENT
| vk::MemoryPropertyFlags::HOST_VISIBLE;
let (staging_buffer, staging_memory)
= allocate_buffer(instance, device, size as vk::DeviceSize,
staging_usage, staging_memory_flags)?;
let host_memory = unsafe {
device.map_memory(staging_memory, 0, size as vk::DeviceSize,
vk::MemoryMapFlags::empty())
}?;
unsafe {
copy_nonoverlapping(contents.as_ptr(), host_memory.cast(), contents.len())
};
unsafe { device.unmap_memory(staging_memory) };
Ok((staging_buffer, staging_memory, size))
}
#[allow(unsafe_code)]
pub fn allocate_buffer(instance: &Instance, device: &Device,
size: vk::DeviceSize, usage: vk::BufferUsageFlags,
memory_flags: vk::MemoryPropertyFlags)
-> Result<(vk::Buffer, vk::DeviceMemory)>
{
let physical_device = device.physical_device();
let buffer_info = vk::BufferCreateInfo::builder()
.size(size)
.usage(usage)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let buffer = unsafe { device.create_buffer(&buffer_info, None) }?;
// The requirements are mostly what you'd think: size and alignment. The
// bits field is something special; see pick_memory_type() for the
// explanation. Despite the simplicity of this data, Vulkan wants to be the
// one to tell us about it, and we let it.
let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
let type_index = pick_memory_type(instance, &physical_device,
&memory_flags, &requirements)?;
let memory_info = vk::MemoryAllocateInfo::builder()
.allocation_size(requirements.size)
.memory_type_index(type_index);
let device_memory = unsafe { device.allocate_memory(&memory_info, None) }?;
unsafe { device.bind_buffer_memory(buffer, device_memory, 0) }?;
Ok((buffer, device_memory))
}
#[allow(unsafe_code)]
pub fn copy_buffer(device: &Device, queue: &vk::Queue,
command_pool: &vk::CommandPool, source: &vk::Buffer,
destination: &vk::Buffer, size: vk::DeviceSize)
-> Result<()>
{
let command_buffer = begin_transient_commands(device, command_pool)?;
let copy_info = vk::BufferCopy::builder().size(size);
unsafe {
device.cmd_copy_buffer(command_buffer, *source, *destination,
&[copy_info])
};
end_transient_commands(command_buffer, device, queue, command_pool)?;
Ok(())
}
#[allow(unsafe_code)]
pub fn pick_memory_type(instance: &Instance,
physical_device: &vk::PhysicalDevice,
properties: &vk::MemoryPropertyFlags,
requirements: &vk::MemoryRequirements)
-> Result<u32>
{
let memory_map = unsafe {
instance.get_physical_device_memory_properties(*physical_device)
};
// So. The memory_type_bits field is a map of which indices are suitable,
// based on the buffer our caller passed to
// get_buffer_memory_requirements(). Yes, that means there's a hard cap on
// how many memory types there can be, based on the size of the bitfield.
for index in 0 .. memory_map.memory_type_count {
if requirements.memory_type_bits & (1 << index) == 0 {
continue;
}
let memory_type = memory_map.memory_types[index as usize];
if memory_type.property_flags.contains(*properties) {
return Ok(index);
}
}
Err(Error {
message: "The system has no suitable memory for a buffer.".to_string()
})
}
#[allow(unsafe_code)]
pub fn begin_transient_commands(device: &Device,
command_pool: &vk::CommandPool)
-> Result<vk::CommandBuffer>
{
let command_buffer_allocation_info
= vk::CommandBufferAllocateInfo::builder()
.command_pool(*command_pool)
.level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(1);
let command_buffer = unsafe {
device.allocate_command_buffers(&command_buffer_allocation_info)
}?[0];
let command_buffer_begin_info = vk::CommandBufferBeginInfo::builder()
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
unsafe {
device.begin_command_buffer(command_buffer, &command_buffer_begin_info)
}?;
Ok(command_buffer)
}
#[allow(unsafe_code)]
pub fn end_transient_commands(command_buffer: vk::CommandBuffer,
device: &Device, queue: &vk::Queue,
command_pool: &vk::CommandPool)
-> Result<()>
{
unsafe { device.end_command_buffer(command_buffer) }?;
let command_buffers = [command_buffer];
let submit_info = vk::SubmitInfo::builder()
.command_buffers(&command_buffers);
unsafe { device.queue_submit(*queue, &[submit_info], vk::Fence::null()) }?;
unsafe { device.queue_wait_idle(*queue) }?;
unsafe { device.free_command_buffers(*command_pool, &command_buffers) };
Ok(())
}
|