mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-02-16 17:02:32 +00:00
Merge #81
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:
commit
193eec694e
@ -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();
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user