Implement map read/write async

This commit is contained in:
Tristam MacDonald 2019-02-12 09:36:29 -08:00 committed by Tristam MacDonald
parent b2f58d0975
commit 8431da80ed
6 changed files with 268 additions and 29 deletions

2
Cargo.lock generated
View File

@ -1,3 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.6.9"

View File

@ -95,7 +95,17 @@ fn main() {
}
encoder.copy_buffer_to_buffer(&storage_buffer, 0, &staging_buffer, 0, size);
// TODO: read the results back out of the staging buffer
staging_buffer.map_read_async(0, size, |result: wgpu::BufferMapAsyncResult<&[u8]>| {
if let wgpu::BufferMapAsyncResult::Success(data) = result {
let results = unsafe { ::std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / std::mem::size_of::<u32>()) };
println!("Times: {:?}", results);
}
});
device.get_queue().submit(&[encoder.finish()]);
// TODO: why does calling unmap() inside the callback prevent the program from exiting?
staging_buffer.unmap();
}

View File

@ -115,16 +115,30 @@ impl framework::Example for Example {
// Create the vertex and index buffers
let vertex_size = mem::size_of::<Vertex>();
let (vertex_data, index_data) = create_vertices();
let vertex_buffer_length = vertex_data.len() * vertex_size;
let index_buffer_length = index_data.len() * mem::size_of::<u16>();
let vertex_buf = device.create_buffer(&wgpu::BufferDescriptor {
size: (vertex_data.len() * vertex_size) as u32,
usage: wgpu::BufferUsageFlags::VERTEX | wgpu::BufferUsageFlags::TRANSFER_DST,
size: vertex_buffer_length as u32,
usage: wgpu::BufferUsageFlags::VERTEX | wgpu::BufferUsageFlags::TRANSFER_DST | wgpu::BufferUsageFlags::MAP_WRITE,
});
vertex_buf.set_sub_data(0, framework::cast_slice(&vertex_data));
//vertex_buf.set_sub_data(0, framework::cast_slice(&vertex_data));
vertex_buf.map_write_async(0, vertex_buffer_length as u32, |result: wgpu::BufferMapAsyncResult<&mut [u8]>| {
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) };
}
});
let index_buf = device.create_buffer(&wgpu::BufferDescriptor {
size: (index_data.len() * 2) as u32,
usage: wgpu::BufferUsageFlags::INDEX | wgpu::BufferUsageFlags::TRANSFER_DST,
size: index_buffer_length as u32,
usage: wgpu::BufferUsageFlags::INDEX | wgpu::BufferUsageFlags::TRANSFER_DST | wgpu::BufferUsageFlags::MAP_WRITE,
});
// index_buf.set_sub_data(0, framework::cast_slice(&index_data));
index_buf.map_write_async(0, index_buffer_length as u32, |result: wgpu::BufferMapAsyncResult<&mut [u8]>| {
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) };
}
});
index_buf.set_sub_data(0, framework::cast_slice(&index_data));
// Create pipeline layout
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -163,14 +177,19 @@ impl framework::Example for Example {
array_size: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::R8g8b8a8Unorm,
usage: wgpu::TextureUsageFlags::SAMPLED | wgpu::TextureUsageFlags::TRANSFER_DST
usage: wgpu::TextureUsageFlags::SAMPLED | wgpu::TextureUsageFlags::TRANSFER_DST,
});
let texture_view = texture.create_default_view();
let temp_buf = device.create_buffer(&wgpu::BufferDescriptor {
size: texels.len() as u32,
usage: wgpu::BufferUsageFlags::TRANSFER_SRC | wgpu::BufferUsageFlags::TRANSFER_DST
usage: wgpu::BufferUsageFlags::TRANSFER_SRC | wgpu::BufferUsageFlags::TRANSFER_DST | wgpu::BufferUsageFlags::MAP_WRITE,
});
// temp_buf.set_sub_data(0, &texels);
temp_buf.map_write_async(0, texels.len() as u32, |result: wgpu::BufferMapAsyncResult<&mut [u8]>| {
if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(texels.as_ptr() as *const u8, data.as_mut_ptr(), texels.len()) };
}
});
temp_buf.set_sub_data(0, &texels);
init_encoder.copy_buffer_to_texture(
wgpu::BufferCopyView {
buffer: &temp_buf,
@ -207,11 +226,16 @@ impl framework::Example for Example {
});
let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
size: 64,
usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST,
usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST | wgpu::BufferUsageFlags::MAP_WRITE,
});
let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32);
let mx_ref: &[f32; 16] = mx_total.as_ref();
uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..]));
// uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..]));
uniform_buf.map_write_async(0, 64, |result: wgpu::BufferMapAsyncResult<&mut [u8]>| {
if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(mx_ref.as_ptr() as *const u8, data.as_mut_ptr(), 64) };
}
});
// Create bind group
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
@ -310,7 +334,12 @@ impl framework::Example for Example {
fn resize(&mut self, sc_desc: &wgpu::SwapChainDescriptor, _device: &mut wgpu::Device) {
let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32);
let mx_ref: &[f32; 16] = mx_total.as_ref();
self.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..]));
// self.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..]));
self.uniform_buf.map_write_async(0, 64, |result: wgpu::BufferMapAsyncResult<&mut [u8]>| {
if let wgpu::BufferMapAsyncResult::Success(data) = result {
unsafe { std::ptr::copy_nonoverlapping(mx_ref.as_ptr() as *const u8, data.as_mut_ptr(), 64) };
}
});
}
fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device) {

View File

@ -3,6 +3,7 @@ use crate::hub::HUB;
use crate::track::{TrackerSet, TrackPermit};
use crate::{
LifeGuard, RefCount, Stored, SubmissionIndex, WeaklyStored,
BufferMapAsyncStatus, BufferMapOperation,
};
use crate::{
BufferId, CommandBufferId, AdapterId, DeviceId, QueueId,
@ -11,7 +12,7 @@ use crate::{
#[cfg(feature = "local")]
use crate::{
BindGroupId, BindGroupLayoutId, PipelineLayoutId, SamplerId, SwapChainId,
ShaderModuleId, CommandEncoderId, RenderPipelineId, ComputePipelineId,
ShaderModuleId, CommandEncoderId, RenderPipelineId, ComputePipelineId,
};
use back;
@ -83,9 +84,12 @@ struct ActiveSubmission<B: hal::Backend> {
index: SubmissionIndex,
fence: B::Fence,
resources: Vec<Resource<B>>,
mapped: Vec<BufferId>,
}
struct DestroyedResources<B: hal::Backend> {
/// Resources that the user has requested be mapped, but are still in use.
mapped: Vec<Stored<BufferId>>,
/// Resources that are destroyed by the user but still referenced by
/// other objects or command buffers.
referenced: Vec<(ResourceId, RefCount)>,
@ -95,17 +99,22 @@ struct DestroyedResources<B: hal::Backend> {
/// Resources that are neither referenced or used, just pending
/// actual deletion.
free: Vec<Resource<B>>,
ready_to_map: Vec<BufferId>,
}
unsafe impl<B: hal::Backend> Send for DestroyedResources<B> {}
unsafe impl<B: hal::Backend> Sync for DestroyedResources<B> {}
impl<B: hal::Backend> DestroyedResources<B> {
fn add(&mut self, resource_id: ResourceId, ref_count: RefCount) {
fn destroy(&mut self, resource_id: ResourceId, ref_count: RefCount) {
debug_assert!(!self.referenced.iter().any(|r| r.0 == resource_id));
self.referenced.push((resource_id, ref_count));
}
fn map(&mut self, buffer: BufferId, ref_count: RefCount) {
self.mapped.push(Stored{value: buffer, ref_count});
}
/// Returns the last submission index that is done.
fn cleanup(&mut self, raw: &B::Device) -> SubmissionIndex {
let mut last_done = 0;
@ -185,10 +194,64 @@ impl DestroyedResources<back::Backend> {
}
}
}
let buffer_guard = HUB.buffers.read();
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 num_refs = self.mapped[i].ref_count.load();
trace!("{} references remain", num_refs);
if num_refs <= 4 {
// assert_eq!(num_refs, 4);
let resource_id = self.mapped.swap_remove(i).value;
let buf = buffer_guard.get(resource_id);
let submit_index = buf.life_guard.submission_index.load(Ordering::Acquire);
match self
.active
.iter_mut()
.find(|a| a.index == submit_index)
{
Some(a) => a.mapped.push(resource_id),
None => self.ready_to_map.push(resource_id),
}
}
}
}
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(..) {
let mut buffer = buffer_guard.get_mut(buffer_id);
let mut operation = None;
std::mem::swap(&mut operation, &mut buffer.pending_map_operation);
match operation {
Some(BufferMapOperation::Read(range, callback, userdata)) => {
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
}
callback(BufferMapAsyncStatus::Success, ptr, userdata);
} else {
callback(BufferMapAsyncStatus::Error, std::ptr::null(), userdata);
}
},
Some(BufferMapOperation::Write(range, callback, 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);
}
callback(BufferMapAsyncStatus::Success, ptr, userdata);
} else {
callback(BufferMapAsyncStatus::Error, std::ptr::null_mut(), userdata);
}
},
_ => unreachable!(),
};
}
}
}
pub struct Device<B: hal::Backend> {
pub(crate) raw: B::Device,
adapter_id: WeaklyStored<AdapterId>,
@ -276,9 +339,11 @@ impl<B: hal::Backend> Device<B> {
framebuffers: Mutex::new(FastHashMap::default()),
desc_pool,
destroyed: Mutex::new(DestroyedResources {
mapped: Vec::new(),
referenced: Vec::new(),
active: Vec::new(),
free: Vec::new(),
ready_to_map: Vec::new(),
}),
}
}
@ -296,7 +361,7 @@ pub fn device_create_buffer(
) -> resource::Buffer<back::Backend> {
let device_guard = HUB.devices.read();
let device = &device_guard.get(device_id);
let (usage, _) = conv::map_buffer_usage(desc.usage);
let (usage, memory_properties) = conv::map_buffer_usage(desc.usage);
let mut buffer = unsafe {
device.raw.create_buffer(desc.size as u64, usage).unwrap()
@ -310,11 +375,10 @@ pub fn device_create_buffer(
.iter()
.enumerate()
.position(|(id, memory_type)| {
// TODO
requirements.type_mask & (1 << id) != 0
&& memory_type
.properties
.contains(hal::memory::Properties::DEVICE_LOCAL)
.contains(memory_properties)
})
.unwrap()
.into();
@ -336,6 +400,10 @@ pub fn device_create_buffer(
value: device_id,
ref_count: device.life_guard.ref_count.clone(),
},
memory_properties,
memory,
mapped_write_ranges: Vec::new(),
pending_map_operation: None,
life_guard: LifeGuard::new(),
}
}
@ -377,13 +445,12 @@ pub extern "C" fn wgpu_buffer_destroy(buffer_id: BufferId) {
.get(buffer.device_id.value)
.destroyed
.lock()
.add(
.destroy(
ResourceId::Buffer(buffer_id),
buffer.life_guard.ref_count.clone(),
);
}
pub fn device_create_texture(
device_id: DeviceId,
desc: &resource::TextureDescriptor,
@ -618,7 +685,7 @@ pub extern "C" fn wgpu_texture_destroy(texture_id: TextureId) {
.get(texture.device_id.value)
.destroyed
.lock()
.add(
.destroy(
ResourceId::Texture(texture_id),
texture.life_guard.ref_count.clone(),
);
@ -637,7 +704,7 @@ pub extern "C" fn wgpu_texture_view_destroy(texture_view_id: TextureViewId) {
.get(device_id)
.destroyed
.lock()
.add(
.destroy(
ResourceId::TextureView(texture_view_id),
view.life_guard.ref_count.clone(),
);
@ -1047,11 +1114,13 @@ pub extern "C" fn wgpu_queue_submit(
let mut destroyed = device.destroyed.lock();
destroyed.triage_referenced(&mut *trackers);
let last_done = destroyed.cleanup(&device.raw);
destroyed.handle_mapping(&device.raw);
destroyed.active.push(ActiveSubmission {
index: old_submit_index + 1,
fence,
resources: Vec::new(),
mapped: Vec::new(),
});
last_done
@ -1386,8 +1455,8 @@ pub fn device_create_swap_chain(
let mut destroyed = device.destroyed.lock();
assert_eq!(old.device_id.value, device_id);
for frame in old.frames {
destroyed.add(ResourceId::Texture(frame.texture_id.value), frame.texture_id.ref_count);
destroyed.add(ResourceId::TextureView(frame.view_id.value), frame.view_id.ref_count);
destroyed.destroy(ResourceId::Texture(frame.texture_id.value), frame.texture_id.ref_count);
destroyed.destroy(ResourceId::TextureView(frame.view_id.value), frame.view_id.ref_count);
}
unsafe {
old.command_pool.reset()
@ -1610,4 +1679,62 @@ pub extern "C" fn wgpu_buffer_set_sub_data(
#[no_mangle]
pub extern "C" fn wgpu_device_destroy(device_id: BufferId) {
HUB.devices.unregister(device_id);
}
pub type BufferMapReadCallback = extern "C" fn(status: BufferMapAsyncStatus, data: *const u8, userdata: *mut u8);
pub type BufferMapWriteCallback = extern "C" fn(status: BufferMapAsyncStatus, data: *mut u8, userdata: *mut u8);
#[no_mangle]
pub extern "C" fn wgpu_buffer_map_read_async(
buffer_id: BufferId,
start: u32, size: u32, callback: BufferMapReadCallback, userdata: *mut u8,
) {
let mut buffer_guard = HUB.buffers.write();
let buffer = buffer_guard.get_mut(buffer_id);
let device_guard = HUB.devices.read();
let device = device_guard.get(buffer.device_id.value);
let range = start as u64..(start + size) as u64;
buffer.pending_map_operation = Some(BufferMapOperation::Read(range, callback, userdata));
device
.destroyed
.lock()
.map(buffer_id, buffer.life_guard.ref_count.clone());
}
#[no_mangle]
pub extern "C" fn wgpu_buffer_map_write_async(
buffer_id: BufferId,
start: u32, size: u32, callback: BufferMapWriteCallback, userdata: *mut u8,
) {
let mut buffer_guard = HUB.buffers.write();
let buffer = buffer_guard.get_mut(buffer_id);
let device_guard = HUB.devices.read();
let device = device_guard.get(buffer.device_id.value);
let range = start as u64..(start + size) as u64;
buffer.pending_map_operation = Some(BufferMapOperation::Write(range, callback, userdata));
device
.destroyed
.lock()
.map(buffer_id, buffer.life_guard.ref_count.clone());
}
#[no_mangle]
pub extern "C" fn wgpu_buffer_unmap(
buffer_id: BufferId,
) {
let mut buffer_guard = HUB.buffers.write();
let buffer = buffer_guard.get_mut(buffer_id);
let mut device_guard = HUB.devices.write();
let device = device_guard.get_mut(buffer.device_id.value);
if !buffer.mapped_write_ranges.is_empty() {
unsafe { device.raw.flush_mapped_memory_ranges( buffer.mapped_write_ranges.iter().map(|r| {(&buffer.memory, r.clone())}) ).unwrap() }; // TODO
buffer.mapped_write_ranges.clear();
}
unsafe { device.raw.unmap_memory(&buffer.memory) };
}

View File

@ -1,6 +1,7 @@
use crate::{
Extent3d, LifeGuard, RefCount, Stored,
DeviceId, TextureId,
BufferMapReadCallback, BufferMapWriteCallback,
};
use crate::swap_chain::{SwapChainLink, SwapImageEpoch};
@ -10,7 +11,6 @@ use parking_lot::Mutex;
use std::borrow::Borrow;
bitflags! {
#[repr(transparent)]
pub struct BufferUsageFlags: u32 {
@ -33,12 +33,29 @@ pub struct BufferDescriptor {
pub usage: BufferUsageFlags,
}
pub enum BufferMapAsyncStatus {
Success,
Error,
Unknown,
ContextLost,
}
pub(crate) enum BufferMapOperation {
Read(std::ops::Range<u64>, BufferMapReadCallback, *mut u8),
Write(std::ops::Range<u64>, BufferMapWriteCallback, *mut u8),
}
unsafe impl Send for BufferMapOperation {}
unsafe impl Sync for BufferMapOperation {}
pub struct Buffer<B: hal::Backend> {
pub(crate) raw: B::Buffer,
pub(crate) device_id: Stored<DeviceId>,
//pub memory_properties: hal::memory::Properties,
pub(crate) memory_properties: hal::memory::Properties,
pub(crate) memory: B::Memory,
pub(crate) mapped_write_ranges: Vec<std::ops::Range<u64>>,
pub(crate) pending_map_operation: Option<BufferMapOperation>,
pub(crate) life_guard: LifeGuard,
// TODO: mapping, unmap()
}
impl<B: hal::Backend> Borrow<RefCount> for Buffer<B> {

View File

@ -6,12 +6,13 @@ use arrayvec::ArrayVec;
use std::ffi::CString;
use std::ops::Range;
use std::ptr;
use std::slice;
pub use wgn::winit;
pub use wgn::{
MAX_DEPTH_BIAS_CLAMP,
AdapterDescriptor, BindGroupLayoutBinding, BindingType,
BlendDescriptor, BlendOperation, BlendFactor, ColorWriteFlags,
BlendDescriptor, BlendOperation, BlendFactor, BufferMapAsyncStatus, ColorWriteFlags,
RasterizationStateDescriptor, CullMode, FrontFace,
BufferDescriptor, BufferUsageFlags,
IndexFormat, InputStepMode, ShaderAttributeIndex, VertexAttributeDescriptor, VertexFormat,
@ -454,10 +455,63 @@ impl Drop for Device {
}
}
pub enum BufferMapAsyncResult<T> {
Success(T),
Error,
}
struct BufferMapReadAsyncUserData<F: FnOnce(BufferMapAsyncResult<&[u8]>)> {
size: u32,
callback: F,
}
struct BufferMapWriteAsyncUserData<F: FnOnce(BufferMapAsyncResult<&mut [u8]>)> {
size: u32,
callback: F,
}
impl Buffer {
pub fn set_sub_data(&self, offset: u32, data: &[u8]) {
wgn::wgpu_buffer_set_sub_data(self.id, offset, data.len() as u32, data.as_ptr());
}
pub fn map_read_async<F>(&self, start: u32, size: u32, callback: F)
where F: FnOnce(BufferMapAsyncResult<&[u8]>) {
extern "C" fn buffer_map_read_callback_wrapper<F>(status: wgn::BufferMapAsyncStatus, data: *const u8, userdata: *mut u8)
where F: FnOnce(BufferMapAsyncResult<&[u8]>) {
let userdata = unsafe { Box::from_raw(userdata as *mut BufferMapReadAsyncUserData<F>) };
let data = unsafe { slice::from_raw_parts(data, userdata.size as usize) };
if let wgn::BufferMapAsyncStatus::Success = status {
(userdata.callback)(BufferMapAsyncResult::Success(data));
} else {
(userdata.callback)(BufferMapAsyncResult::Error);
}
}
let userdata = Box::new(BufferMapReadAsyncUserData{size, callback});
wgn::wgpu_buffer_map_read_async(self.id, start, size, buffer_map_read_callback_wrapper::<F>, Box::into_raw(userdata) as *mut u8);
}
pub fn map_write_async<F>(&self, start: u32, size: u32, callback: F)
where F: FnOnce(BufferMapAsyncResult<&mut [u8]>) {
extern "C" fn buffer_map_write_callback_wrapper<F>(status: wgn::BufferMapAsyncStatus, data: *mut u8, userdata: *mut u8)
where F: FnOnce(BufferMapAsyncResult<&mut [u8]>) {
let userdata = unsafe { Box::from_raw(userdata as *mut BufferMapWriteAsyncUserData<F>) };
let data = unsafe { slice::from_raw_parts_mut(data, userdata.size as usize) };
if let wgn::BufferMapAsyncStatus::Success = status {
(userdata.callback)(BufferMapAsyncResult::Success(data));
} else {
(userdata.callback)(BufferMapAsyncResult::Error);
}
}
let userdata = Box::new(BufferMapWriteAsyncUserData{size, callback});
wgn::wgpu_buffer_map_write_async(self.id, start, size, buffer_map_write_callback_wrapper::<F>, Box::into_raw(userdata) as *mut u8);
}
pub fn unmap(&self) {
wgn::wgpu_buffer_unmap(self.id);
}
}
impl Drop for Buffer {