81: Buffer tracking and unmapping r=kvark a=swiftcoder

Adds preliminary transitioning of buffers to mapped state.
Adds buffer unmapping to the cube sample.
Modifies wgpu_queue_submit to not hold a write lock on the device during callbacks (this could definitely be cleaner, but I'm not sure which direction to take refactoring here).

Co-authored-by: Tristam MacDonald <tristam@trist.am>
This commit is contained in:
bors[bot] 2019-02-28 14:18:16 +00:00
commit 193eec694e
4 changed files with 156 additions and 129 deletions

View File

@ -102,10 +102,8 @@ fn main() {
println!("Times: {:?}", results); println!("Times: {:?}", results);
} }
staging_buffer.unmap();
}); });
device.get_queue().submit(&[encoder.finish()]); device.get_queue().submit(&[encoder.finish()]);
// TODO: why does calling unmap() inside the callback prevent the program from exiting?
staging_buffer.unmap();
} }

View File

@ -127,6 +127,8 @@ impl framework::Example for Example {
if let wgpu::BufferMapAsyncResult::Success(data) = result { if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(vertex_data.as_ptr() as *const u8, data.as_mut_ptr(), vertex_buffer_length) }; unsafe { std::ptr::copy_nonoverlapping(vertex_data.as_ptr() as *const u8, data.as_mut_ptr(), vertex_buffer_length) };
} }
vertex_buf.unmap();
}); });
let index_buf = device.create_buffer(&wgpu::BufferDescriptor { let index_buf = device.create_buffer(&wgpu::BufferDescriptor {
@ -138,6 +140,8 @@ impl framework::Example for Example {
if let wgpu::BufferMapAsyncResult::Success(data) = result { if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(index_data.as_ptr() as *const u8, data.as_mut_ptr(), index_buffer_length) }; unsafe { std::ptr::copy_nonoverlapping(index_data.as_ptr() as *const u8, data.as_mut_ptr(), index_buffer_length) };
} }
index_buf.unmap();
}); });
// Create pipeline layout // Create pipeline layout
@ -189,6 +193,8 @@ impl framework::Example for Example {
if let wgpu::BufferMapAsyncResult::Success(data) = result { if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(texels.as_ptr() as *const u8, data.as_mut_ptr(), texels.len()) }; unsafe { std::ptr::copy_nonoverlapping(texels.as_ptr() as *const u8, data.as_mut_ptr(), texels.len()) };
} }
temp_buf.unmap();
}); });
init_encoder.copy_buffer_to_texture( init_encoder.copy_buffer_to_texture(
wgpu::BufferCopyView { wgpu::BufferCopyView {
@ -235,6 +241,8 @@ impl framework::Example for Example {
if let wgpu::BufferMapAsyncResult::Success(data) = result { if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(mx_ref.as_ptr() as *const u8, data.as_mut_ptr(), 64) }; unsafe { std::ptr::copy_nonoverlapping(mx_ref.as_ptr() as *const u8, data.as_mut_ptr(), 64) };
} }
uniform_buf.unmap();
}); });
// Create bind group // Create bind group
@ -339,6 +347,8 @@ impl framework::Example for Example {
if let wgpu::BufferMapAsyncResult::Success(data) = result { if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(mx_ref.as_ptr() as *const u8, data.as_mut_ptr(), 64) }; unsafe { std::ptr::copy_nonoverlapping(mx_ref.as_ptr() as *const u8, data.as_mut_ptr(), 64) };
} }
self.uniform_buf.unmap();
}); });
} }

View File

@ -213,20 +213,22 @@ impl DestroyedResources<back::Backend> {
let buffer_guard = HUB.buffers.read(); let buffer_guard = HUB.buffers.read();
for i in (0..self.mapped.len()).rev() { for i in (0..self.mapped.len()).rev() {
// one in resource itself, one here in this list, one the owner holds, and one more somewhere? let resource_id = self.mapped.swap_remove(i).value;
let num_refs = self.mapped[i].ref_count.load(); let buf = &buffer_guard[resource_id];
trace!("{} references remain", num_refs);
if num_refs <= 4 { let usage = match buf.pending_map_operation {
// assert_eq!(num_refs, 4); Some(BufferMapOperation::Read(..)) => resource::BufferUsageFlags::MAP_READ,
let resource_id = self.mapped.swap_remove(i).value; Some(BufferMapOperation::Write(..)) => resource::BufferUsageFlags::MAP_WRITE,
let buf = &buffer_guard[resource_id]; _ => unreachable!(),
let submit_index = buf.life_guard.submission_index.load(Ordering::Acquire); };
self.active trackers.buffers.get_with_replaced_usage(&buffer_guard, resource_id, usage).unwrap();
.iter_mut()
.find(|a| a.index == submit_index) let submit_index = buf.life_guard.submission_index.load(Ordering::Acquire);
.map_or(&mut self.ready_to_map, |a| &mut a.mapped) self.active
.push(resource_id); .iter_mut()
} .find(|a| a.index == submit_index)
.map_or(&mut self.ready_to_map, |a| &mut a.mapped)
.push(resource_id);
} }
} }
@ -269,34 +271,39 @@ impl DestroyedResources<back::Backend> {
} }
fn handle_mapping(&mut self, raw: &<back::Backend as hal::Backend>::Device) { fn handle_mapping(&mut self, raw: &<back::Backend as hal::Backend>::Device) {
let mut buffer_guard = HUB.buffers.write();
for buffer_id in self.ready_to_map.drain(..) { for buffer_id in self.ready_to_map.drain(..) {
let buffer = &mut buffer_guard[buffer_id];
let mut operation = None; let mut operation = None;
std::mem::swap(&mut operation, &mut buffer.pending_map_operation); let (result, ptr) = {
match operation { let mut buffer_guard = HUB.buffers.write();
Some(BufferMapOperation::Read(range, callback, userdata)) => { let buffer = &mut buffer_guard[buffer_id];
if let Ok(ptr) = unsafe { raw.map_memory(&buffer.memory, range.clone()) } { std::mem::swap(&mut operation, &mut buffer.pending_map_operation);
if !buffer.memory_properties.contains(hal::memory::Properties::COHERENT) { match operation.clone().unwrap() {
unsafe { raw.invalidate_mapped_memory_ranges(iter::once((&buffer.memory, range.clone()))).unwrap() }; // TODO BufferMapOperation::Read(range, ..) => {
if let Ok(ptr) = unsafe { raw.map_memory(&buffer.memory, range.clone()) } {
if !buffer.memory_properties.contains(hal::memory::Properties::COHERENT) {
unsafe { raw.invalidate_mapped_memory_ranges(iter::once((&buffer.memory, range.clone()))).unwrap() }; // TODO
}
(BufferMapAsyncStatus::Success, Some(ptr))
} else {
(BufferMapAsyncStatus::Error, None)
} }
callback(BufferMapAsyncStatus::Success, ptr, userdata); },
} else { BufferMapOperation::Write(range, ..) => {
callback(BufferMapAsyncStatus::Error, std::ptr::null(), userdata); if let Ok(ptr) = unsafe { raw.map_memory(&buffer.memory, range.clone()) } {
} if !buffer.memory_properties.contains(hal::memory::Properties::COHERENT) {
}, buffer.mapped_write_ranges.push(range.clone());
Some(BufferMapOperation::Write(range, callback, userdata)) => { }
if let Ok(ptr) = unsafe { raw.map_memory(&buffer.memory, range.clone()) } { (BufferMapAsyncStatus::Success, Some(ptr))
if !buffer.memory_properties.contains(hal::memory::Properties::COHERENT) { } else {
buffer.mapped_write_ranges.push(range); (BufferMapAsyncStatus::Error, None)
} }
callback(BufferMapAsyncStatus::Success, ptr, userdata); },
} else { }
callback(BufferMapAsyncStatus::Error, std::ptr::null_mut(), userdata); };
}
}, match operation.unwrap() {
_ => unreachable!(), BufferMapOperation::Read(_, callback, userdata) => callback(result, ptr.unwrap_or(std::ptr::null_mut()), userdata),
BufferMapOperation::Write(_, callback, userdata) => callback(result, ptr.unwrap_or(std::ptr::null_mut()), userdata),
}; };
} }
} }
@ -1022,108 +1029,119 @@ pub extern "C" fn wgpu_queue_submit(
command_buffer_ptr: *const CommandBufferId, command_buffer_ptr: *const CommandBufferId,
command_buffer_count: usize, command_buffer_count: usize,
) { ) {
let mut device_guard = HUB.devices.write();
let device = &mut device_guard[queue_id];
let mut swap_chain_links = Vec::new();
let command_buffer_ids = let command_buffer_ids =
unsafe { slice::from_raw_parts(command_buffer_ptr, command_buffer_count) }; unsafe { slice::from_raw_parts(command_buffer_ptr, command_buffer_count) };
let old_submit_index = device let (old_submit_index, fence) = {
.life_guard let mut device_guard = HUB.devices.write();
.submission_index let device = &mut device_guard[queue_id];
.fetch_add(1, Ordering::Relaxed);
let mut trackers = device.trackers.lock();
//TODO: if multiple command buffers are submitted, we can re-use the last let mut swap_chain_links = Vec::new();
// native command buffer of the previous chain instead of always creating
// a temporary one, since the chains are not finished.
{
let mut command_buffer_guard = HUB.command_buffers.write();
let buffer_guard = HUB.buffers.read();
let texture_guard = HUB.textures.read();
let texture_view_guard = HUB.texture_views.read();
// finish all the command buffers first let old_submit_index = device
for &cmb_id in command_buffer_ids { .life_guard
let comb = &mut command_buffer_guard[cmb_id]; .submission_index
swap_chain_links.extend(comb.swap_chain_links.drain(..)); .fetch_add(1, Ordering::Relaxed);
// update submission IDs let mut trackers = device.trackers.lock();
comb.life_guard.submission_index
.store(old_submit_index, Ordering::Release);
for id in comb.trackers.buffers.used() {
buffer_guard[id].life_guard.submission_index
.store(old_submit_index, Ordering::Release);
}
for id in comb.trackers.textures.used() {
texture_guard[id].life_guard.submission_index
.store(old_submit_index, Ordering::Release);
}
for id in comb.trackers.views.used() {
texture_view_guard[id].life_guard.submission_index
.store(old_submit_index, Ordering::Release);
}
// execute resource transitions //TODO: if multiple command buffers are submitted, we can re-use the last
let mut transit = device.com_allocator.extend(comb); // native command buffer of the previous chain instead of always creating
unsafe { // a temporary one, since the chains are not finished.
transit.begin( {
hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, let mut command_buffer_guard = HUB.command_buffers.write();
hal::command::CommandBufferInheritanceInfo::default(), let buffer_guard = HUB.buffers.read();
let texture_guard = HUB.textures.read();
let texture_view_guard = HUB.texture_views.read();
// finish all the command buffers first
for &cmb_id in command_buffer_ids {
let comb = &mut command_buffer_guard[cmb_id];
swap_chain_links.extend(comb.swap_chain_links.drain(..));
// update submission IDs
comb.life_guard.submission_index
.store(old_submit_index, Ordering::Release);
for id in comb.trackers.buffers.used() {
buffer_guard[id].life_guard.submission_index
.store(old_submit_index, Ordering::Release);
}
for id in comb.trackers.textures.used() {
texture_guard[id].life_guard.submission_index
.store(old_submit_index, Ordering::Release);
}
for id in comb.trackers.views.used() {
texture_view_guard[id].life_guard.submission_index
.store(old_submit_index, Ordering::Release);
}
// execute resource transitions
let mut transit = device.com_allocator.extend(comb);
unsafe {
transit.begin(
hal::command::CommandBufferFlags::ONE_TIME_SUBMIT,
hal::command::CommandBufferInheritanceInfo::default(),
);
}
command::CommandBuffer::insert_barriers(
&mut transit,
&mut *trackers,
&comb.trackers,
Stitch::Init,
&*buffer_guard,
&*texture_guard,
); );
} unsafe {
command::CommandBuffer::insert_barriers( transit.finish();
&mut transit, }
&mut *trackers, comb.raw.insert(0, transit);
&comb.trackers, unsafe {
Stitch::Init, comb.raw.last_mut().unwrap().finish();
&*buffer_guard, }
&*texture_guard,
);
unsafe {
transit.finish();
}
comb.raw.insert(0, transit);
unsafe {
comb.raw.last_mut().unwrap().finish();
} }
} }
}
// now prepare the GPU submission // now prepare the GPU submission
let fence = device.raw.create_fence(false).unwrap(); let fence = device.raw.create_fence(false).unwrap();
{ {
let command_buffer_guard = HUB.command_buffers.read(); let command_buffer_guard = HUB.command_buffers.read();
let surface_guard = HUB.surfaces.read(); let surface_guard = HUB.surfaces.read();
let wait_semaphores = swap_chain_links let wait_semaphores = swap_chain_links
.into_iter() .into_iter()
.flat_map(|link| { .flat_map(|link| {
//TODO: check the epoch //TODO: check the epoch
surface_guard[link.swap_chain_id].swap_chain surface_guard[link.swap_chain_id].swap_chain
.as_ref() .as_ref()
.map(|swap_chain| ( .map(|swap_chain| (
&swap_chain.frames[link.image_index as usize].sem_available, &swap_chain.frames[link.image_index as usize].sem_available,
hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT,
)) ))
}); });
let submission = let submission =
hal::queue::Submission::<_, _, &[<back::Backend as hal::Backend>::Semaphore]> { hal::queue::Submission::<_, _, &[<back::Backend as hal::Backend>::Semaphore]> {
//TODO: may `OneShot` be enough? //TODO: may `OneShot` be enough?
command_buffers: command_buffer_ids command_buffers: command_buffer_ids
.iter() .iter()
.flat_map(|&cmb_id| &command_buffer_guard[cmb_id].raw), .flat_map(|&cmb_id| &command_buffer_guard[cmb_id].raw),
wait_semaphores, wait_semaphores,
signal_semaphores: &[], //TODO: signal `sem_present`? signal_semaphores: &[], //TODO: signal `sem_present`?
}; };
unsafe { unsafe {
device.queue_group.queues[0] device.queue_group.queues[0]
.as_raw_mut() .as_raw_mut()
.submit(submission, Some(&fence)); .submit(submission, Some(&fence));
}
} }
}
(old_submit_index, fence)
};
// No need for write access to the device from here on out
let device_guard = HUB.devices.read();
let device = &device_guard[queue_id];
let mut trackers = device.trackers.lock();
let last_done = { let last_done = {
let mut destroyed = device.destroyed.lock(); let mut destroyed = device.destroyed.lock();

View File

@ -40,6 +40,7 @@ pub enum BufferMapAsyncStatus {
ContextLost, ContextLost,
} }
#[derive(Clone)]
pub(crate) enum BufferMapOperation { pub(crate) enum BufferMapOperation {
Read(std::ops::Range<u64>, BufferMapReadCallback, *mut u8), Read(std::ops::Range<u64>, BufferMapReadCallback, *mut u8),
Write(std::ops::Range<u64>, BufferMapWriteCallback, *mut u8), Write(std::ops::Range<u64>, BufferMapWriteCallback, *mut u8),