diff --git a/Cargo.toml b/Cargo.toml index 0685325fa0..9549c28a42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,14 +28,14 @@ vulkan = ["wgc/gfx-backend-vulkan"] package = "wgpu-core" version = "0.5" git = "https://github.com/gfx-rs/wgpu" -rev = "fbc2c87de61b0e7bab2583ddf305742e3cbf85e8" +rev = "9120f0399ce8d8cc48b0b87c26d58c71b2e925be" features = ["raw-window-handle"] [dependencies.wgt] package = "wgpu-types" version = "0.5" git = "https://github.com/gfx-rs/wgpu" -rev = "fbc2c87de61b0e7bab2583ddf305742e3cbf85e8" +rev = "9120f0399ce8d8cc48b0b87c26d58c71b2e925be" [dependencies] arrayvec = "0.5" diff --git a/examples/capture/main.rs b/examples/capture/main.rs index cde29eb4a9..6f06591d54 100644 --- a/examples/capture/main.rs +++ b/examples/capture/main.rs @@ -34,9 +34,10 @@ async fn run() { // The output buffer lets us retrieve the data as an array let output_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, size: (size * size) as u64 * size_of::() as u64, usage: wgpu::BufferUsage::MAP_READ | wgpu::BufferUsage::COPY_DST, - label: None, + mapped_at_creation: false, }); let texture_extent = wgpu::Extent3d { @@ -95,7 +96,7 @@ async fn run() { queue.submit(Some(command_buffer)); // Note that we're not calling `.await` here. - let buffer_future = output_buffer.map_read(0, wgt::BufferSize::WHOLE); + let buffer_future = output_buffer.map_async(wgpu::MapMode::Read, 0, wgt::BufferSize::WHOLE); // Poll the device in a blocking manner so that our future resolves. // In an actual application, `device.poll(...)` should @@ -108,15 +109,17 @@ async fn run() { return; } - if let Ok(mapping) = buffer_future.await { + if let Ok(()) = buffer_future.await { + let data = output_buffer.get_mapped_range(0, wgt::BufferSize::WHOLE); let mut png_encoder = png::Encoder::new(File::create("red.png").unwrap(), size, size); png_encoder.set_depth(png::BitDepth::Eight); png_encoder.set_color(png::ColorType::RGBA); png_encoder .write_header() .unwrap() - .write_image_data(mapping.as_slice()) + .write_image_data(data) .unwrap(); + output_buffer.unmap(); } } diff --git a/examples/hello-compute/main.rs b/examples/hello-compute/main.rs index 893dfb7bbf..f2f2cdf2c8 100644 --- a/examples/hello-compute/main.rs +++ b/examples/hello-compute/main.rs @@ -51,20 +51,20 @@ async fn execute_gpu(numbers: Vec) -> Vec { let cs_module = device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(&cs[..])).unwrap()); - let staging_buffer = device.create_buffer_with_data( - bytemuck::cast_slice(&numbers), - wgpu::BufferUsage::MAP_READ | wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::COPY_SRC, - ); - - let storage_buffer = device.create_buffer(&wgpu::BufferDescriptor { - size, - usage: wgpu::BufferUsage::STORAGE - | wgpu::BufferUsage::COPY_DST - | wgpu::BufferUsage::COPY_SRC, + let staging_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: None, + size, + usage: wgpu::BufferUsage::MAP_READ | wgpu::BufferUsage::COPY_DST, + mapped_at_creation: false, }); + let storage_buffer = device.create_buffer_with_data( + bytemuck::cast_slice(&numbers), + wgpu::BufferUsage::STORAGE | wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::COPY_SRC, + ); + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, bindings: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStage::COMPUTE, @@ -73,16 +73,15 @@ async fn execute_gpu(numbers: Vec) -> Vec { readonly: false, }, }], - label: None, }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, layout: &bind_group_layout, bindings: &[wgpu::Binding { binding: 0, resource: wgpu::BindingResource::Buffer(storage_buffer.slice(..)), }], - label: None, }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { @@ -99,7 +98,6 @@ async fn execute_gpu(numbers: Vec) -> Vec { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - encoder.copy_buffer_to_buffer(&staging_buffer, 0, &storage_buffer, 0, size); { let mut cpass = encoder.begin_compute_pass(); cpass.set_pipeline(&compute_pipeline); @@ -111,19 +109,21 @@ async fn execute_gpu(numbers: Vec) -> Vec { queue.submit(Some(encoder.finish())); // Note that we're not calling `.await` here. - let buffer_future = staging_buffer.map_read(0, wgt::BufferSize::WHOLE); + let buffer_future = staging_buffer.map_async(wgpu::MapMode::Read, 0, wgt::BufferSize::WHOLE); // Poll the device in a blocking manner so that our future resolves. // In an actual application, `device.poll(...)` should // be called in an event loop or on another thread. device.poll(wgpu::Maintain::Wait); - if let Ok(mapping) = buffer_future.await { - mapping - .as_slice() + if let Ok(()) = buffer_future.await { + let data = staging_buffer.get_mapped_range(0, wgt::BufferSize::WHOLE); + let result = data .chunks_exact(4) .map(|b| u32::from_ne_bytes(b.try_into().unwrap())) - .collect() + .collect(); + staging_buffer.unmap(); + result } else { panic!("failed to run compute on gpu!") } diff --git a/examples/shadow/main.rs b/examples/shadow/main.rs index 76df23c431..49c20d6a18 100644 --- a/examples/shadow/main.rs +++ b/examples/shadow/main.rs @@ -236,9 +236,10 @@ impl framework::Example for Example { let entity_uniform_size = mem::size_of::() as wgpu::BufferAddress; let plane_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, size: entity_uniform_size, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - label: None, + mapped_at_creation: false, }); let local_bind_group_layout = @@ -316,9 +317,10 @@ impl framework::Example for Example { scale: cube.scale, }; let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, size: entity_uniform_size, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - label: None, + mapped_at_creation: false, }); entities.push(Entity { mx_world: cgmath::Matrix4::from(transform), @@ -408,11 +410,12 @@ impl framework::Example for Example { let light_uniform_size = (Self::MAX_LIGHTS * mem::size_of::()) as wgpu::BufferAddress; let light_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, size: light_uniform_size, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::COPY_DST, - label: None, + mapped_at_creation: false, }); let vb_desc = wgpu::VertexBufferDescriptor { @@ -438,9 +441,10 @@ impl framework::Example for Example { let uniform_size = mem::size_of::() as wgpu::BufferAddress; let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, size: uniform_size, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - label: None, + mapped_at_creation: false, }); // Create bind group diff --git a/src/backend/direct.rs b/src/backend/direct.rs index d00b1bd781..ffbd37fb03 100644 --- a/src/backend/direct.rs +++ b/src/backend/direct.rs @@ -1,8 +1,8 @@ use crate::{ backend::native_gpu_future, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BindingType, BufferDescriptor, CommandEncoderDescriptor, ComputePipelineDescriptor, Extensions, - Limits, PipelineLayoutDescriptor, RenderPipelineDescriptor, SamplerDescriptor, SwapChainStatus, - TextureDescriptor, TextureViewDescriptor, TextureViewDimension, + Limits, MapMode, PipelineLayoutDescriptor, RenderPipelineDescriptor, SamplerDescriptor, + SwapChainStatus, TextureDescriptor, TextureViewDescriptor, TextureViewDimension, }; use arrayvec::ArrayVec; @@ -202,18 +202,12 @@ impl crate::Context for Context { type SwapChainId = wgc::id::SwapChainId; type RenderPassId = wgc::command::RawPass; - type CreateBufferMappedDetail = CreateBufferMappedDetail; - type BufferReadMappingDetail = BufferReadMappingDetail; - type BufferWriteMappingDetail = BufferWriteMappingDetail; type SwapChainOutputDetail = SwapChainOutputDetail; type RequestAdapterFuture = Ready>; type RequestDeviceFuture = Ready>; - type MapReadFuture = - native_gpu_future::GpuFuture>; - type MapWriteFuture = - native_gpu_future::GpuFuture>; + type MapAsyncFuture = native_gpu_future::GpuFuture>; fn init() -> Self { wgc::hub::Global::new("wgpu", wgc::hub::IdentityManagerFactory) @@ -513,23 +507,6 @@ impl crate::Context for Context { )) } - fn device_create_buffer_mapped<'a>( - &self, - device: &Self::DeviceId, - desc: &BufferDescriptor, - ) -> (Self::BufferId, &'a mut [u8], Self::CreateBufferMappedDetail) { - let owned_label = OwnedLabel::new(desc.label.as_deref()); - unsafe { - let (id, ptr) = gfx_select!(*device => self.device_create_buffer_mapped( - *device, - &desc.map_label(|_| owned_label.as_ptr()), - PhantomData - )); - let mapped_data = std::slice::from_raw_parts_mut(ptr, desc.size as usize); - (id, mapped_data, CreateBufferMappedDetail) - } - } - fn device_create_buffer( &self, device: &Self::DeviceId, @@ -603,78 +580,65 @@ impl crate::Context for Context { )); } - fn buffer_map_read( + fn buffer_map_async( &self, buffer: &Self::BufferId, + mode: MapMode, range: Range, - ) -> Self::MapReadFuture { - let (future, completion) = - native_gpu_future::new_gpu_future(*buffer, range.end - range.start); + ) -> Self::MapAsyncFuture { + let (future, completion) = native_gpu_future::new_gpu_future(); - extern "C" fn buffer_map_read_future_wrapper( + extern "C" fn buffer_map_future_wrapper( status: wgc::resource::BufferMapAsyncStatus, - data: *const u8, user_data: *mut u8, ) { let completion = unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; - let (buffer_id, size) = completion.get_buffer_info(); - - if let wgc::resource::BufferMapAsyncStatus::Success = status { - completion.complete(Ok(BufferReadMappingDetail { - data, - size: size as usize, - buffer_id, - })); - } else { - completion.complete(Err(crate::BufferAsyncError)); - } + completion.complete(match status { + wgc::resource::BufferMapAsyncStatus::Success => Ok(()), + _ => Err(crate::BufferAsyncError), + }) } - let operation = wgc::resource::BufferMapOperation::Read { - callback: buffer_map_read_future_wrapper, - userdata: completion.to_raw() as _, + let operation = wgc::resource::BufferMapOperation { + host: match mode { + MapMode::Read => wgc::device::HostMap::Read, + MapMode::Write => wgc::device::HostMap::Write, + }, + callback: buffer_map_future_wrapper, + user_data: completion.to_raw() as _, }; gfx_select!(*buffer => self.buffer_map_async(*buffer, range, operation)); future } - fn buffer_map_write( + fn buffer_get_mapped_range( &self, buffer: &Self::BufferId, - range: Range, - ) -> Self::MapWriteFuture { - let (future, completion) = - native_gpu_future::new_gpu_future(*buffer, range.end - range.start); + sub_range: Range, + ) -> &[u8] { + let size = sub_range.end - sub_range.start; + let ptr = gfx_select!(*buffer => self.buffer_get_mapped_range( + *buffer, + sub_range.start, + wgt::BufferSize(size) + )); + unsafe { slice::from_raw_parts(ptr, size as usize) } + } - extern "C" fn buffer_map_write_future_wrapper( - status: wgc::resource::BufferMapAsyncStatus, - data: *mut u8, - user_data: *mut u8, - ) { - let completion = - unsafe { native_gpu_future::GpuFutureCompletion::from_raw(user_data as _) }; - let (buffer_id, size) = completion.get_buffer_info(); - - if let wgc::resource::BufferMapAsyncStatus::Success = status { - completion.complete(Ok(BufferWriteMappingDetail { - data, - size: size as usize, - buffer_id, - })); - } else { - completion.complete(Err(crate::BufferAsyncError)); - } - } - - let operation = wgc::resource::BufferMapOperation::Write { - callback: buffer_map_write_future_wrapper, - userdata: completion.to_raw() as _, - }; - gfx_select!(*buffer => self.buffer_map_async(*buffer, range, operation)); - - future + fn buffer_get_mapped_range_mut( + &self, + buffer: &Self::BufferId, + sub_range: Range, + ) -> &mut [u8] { + let size = sub_range.end - sub_range.start; + let ptr = gfx_select!(*buffer => self.buffer_get_mapped_range( + *buffer, + sub_range.start, + wgt::BufferSize(size) + )); + unsafe { slice::from_raw_parts_mut(ptr, size as usize) } } fn buffer_unmap(&self, buffer: &Self::BufferId) { @@ -749,8 +713,6 @@ impl crate::Context for Context { gfx_select!(*pipeline => self.render_pipeline_destroy(*pipeline)) } - fn flush_mapped_data(_data: &mut [u8], _detail: CreateBufferMappedDetail) {} - fn encoder_copy_buffer_to_buffer( &self, encoder: &Self::CommandEncoderId, @@ -933,38 +895,6 @@ impl crate::Context for Context { } } -pub(crate) struct CreateBufferMappedDetail; - -pub(crate) struct BufferReadMappingDetail { - pub(crate) buffer_id: wgc::id::BufferId, - data: *const u8, - size: usize, -} - -impl BufferReadMappingDetail { - pub(crate) fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.data as *const u8, self.size) } - } -} - -pub(crate) struct BufferWriteMappingDetail { - pub(crate) buffer_id: wgc::id::BufferId, - data: *mut u8, - size: usize, -} - -// SAFETY: It is safe to implement Send for `BufferReadMappingDetail` and `BufferWriteMappingDetail` -// because the only !Send field is `data`, and it is used similarly to `&[u8]` or `&mut [u8]`. - -unsafe impl Send for BufferReadMappingDetail {} -unsafe impl Send for BufferWriteMappingDetail {} - -impl BufferWriteMappingDetail { - pub(crate) fn as_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.data as *mut u8, self.size) } - } -} - #[derive(Debug)] pub(crate) struct SwapChainOutputDetail { swap_chain_id: wgc::id::SwapChainId, diff --git a/src/backend/native_gpu_future.rs b/src/backend/native_gpu_future.rs index 1c93ac17b7..5c50c49664 100644 --- a/src/backend/native_gpu_future.rs +++ b/src/backend/native_gpu_future.rs @@ -1,4 +1,3 @@ -use crate::BufferAddress; use parking_lot::Mutex; use std::future::Future; use std::pin::Pin; @@ -10,29 +9,26 @@ enum WakerOrResult { Result(T), } +type GpuFutureData = Mutex>>; + /// A Future that can poll the wgpu::Device pub struct GpuFuture { - data: Arc>, + data: Arc>, } pub enum OpaqueData {} -struct Data { - buffer_id: wgc::id::BufferId, - size: BufferAddress, - waker_or_result: Mutex>>, -} - +//TODO: merge this with `GpuFuture` and avoid `Arc` on the data. /// A completion handle to set the result on a GpuFuture pub struct GpuFutureCompletion { - data: Arc>, + data: Arc>, } impl Future for GpuFuture { type Output = T; fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll { - let mut waker_or_result = self.into_ref().get_ref().data.waker_or_result.lock(); + let mut waker_or_result = self.into_ref().get_ref().data.lock(); match waker_or_result.take() { Some(WakerOrResult::Result(res)) => Poll::Ready(res), @@ -46,7 +42,7 @@ impl Future for GpuFuture { impl GpuFutureCompletion { pub fn complete(self, value: T) { - let mut waker_or_result = self.data.waker_or_result.lock(); + let mut waker_or_result = self.data.lock(); match waker_or_result.replace(WakerOrResult::Result(value)) { Some(WakerOrResult::Waker(waker)) => waker.wake(), @@ -68,22 +64,10 @@ impl GpuFutureCompletion { data: Arc::from_raw(this as _), } } - - pub(crate) fn get_buffer_info(&self) -> (wgc::id::BufferId, BufferAddress) { - (self.data.buffer_id, self.data.size) - } } -pub(crate) fn new_gpu_future( - buffer_id: wgc::id::BufferId, - size: BufferAddress, -) -> (GpuFuture, GpuFutureCompletion) { - let data = Arc::new(Data { - buffer_id, - size, - waker_or_result: Mutex::new(None), - }); - +pub(crate) fn new_gpu_future() -> (GpuFuture, GpuFutureCompletion) { + let data = Arc::new(Mutex::new(None)); ( GpuFuture { data: Arc::clone(&data), diff --git a/src/backend/web.rs b/src/backend/web.rs index c5fb4b05c2..17814aaa65 100644 --- a/src/backend/web.rs +++ b/src/backend/web.rs @@ -574,25 +574,10 @@ pub(crate) struct MapFuture { marker: PhantomData, } impl Unpin for MapFuture {} -type MapData = (web_sys::GpuBuffer, Vec); -impl From for BufferReadMappingDetail { - fn from((buffer_id, mapped): MapData) -> Self { - BufferReadMappingDetail { - buffer_id: Sendable(buffer_id), - mapped, - } - } -} -impl From for BufferWriteMappingDetail { - fn from((buffer_id, mapped): MapData) -> Self { - BufferWriteMappingDetail { - buffer_id: Sendable(buffer_id), - mapped, - } - } -} -impl> std::future::Future for MapFuture { - type Output = Result; +//type MapData = (web_sys::GpuBuffer, Vec); + +impl std::future::Future for MapFuture<()> { + type Output = Result<(), crate::BufferAsyncError>; fn poll( mut self: std::pin::Pin<&mut Self>, context: &mut std::task::Context, @@ -602,12 +587,12 @@ impl> std::future::Future for MapFuture { context, ) .map(|result| { - let buffer = self.buffer.take().unwrap(); + let _buffer = self.buffer.take().unwrap(); result .map(|js_value| { let array_buffer = js_sys::ArrayBuffer::from(js_value); - let view = js_sys::Uint8Array::new(&array_buffer); - T::from((buffer, view.to_vec())) + let _view = js_sys::Uint8Array::new(&array_buffer); + () //TODO }) .map_err(|_| crate::BufferAsyncError) }) @@ -635,17 +620,13 @@ impl crate::Context for Context { type SwapChainId = Sendable; type RenderPassId = RenderPass; - type CreateBufferMappedDetail = CreateBufferMappedDetail; - type BufferReadMappingDetail = BufferReadMappingDetail; - type BufferWriteMappingDetail = BufferWriteMappingDetail; type SwapChainOutputDetail = SwapChainOutputDetail; type RequestAdapterFuture = MakeSendFuture>>; type RequestDeviceFuture = MakeSendFuture< FutureMap>, >; - type MapReadFuture = MakeSendFuture>; - type MapWriteFuture = MakeSendFuture>; + type MapAsyncFuture = MakeSendFuture>; fn init() -> Self { Sendable(web_sys::window().unwrap().navigator().gpu()) @@ -720,26 +701,26 @@ impl crate::Context for Context { ) } - fn adapter_extensions(&self, adapter: &Self::AdapterId) -> wgt::Extensions { + fn adapter_extensions(&self, _adapter: &Self::AdapterId) -> wgt::Extensions { // TODO: web-sys has no way of getting extensions on adapters wgt::Extensions { anisotropic_filtering: false, } } - fn adapter_limits(&self, adapter: &Self::AdapterId) -> wgt::Limits { + fn adapter_limits(&self, _adapter: &Self::AdapterId) -> wgt::Limits { // TODO: web-sys has no way of getting limits on adapters wgt::Limits::default() } - fn device_extensions(&self, device: &Self::DeviceId) -> wgt::Extensions { + fn device_extensions(&self, _device: &Self::DeviceId) -> wgt::Extensions { // TODO: web-sys has no way of getting extensions on devices wgt::Extensions { anisotropic_filtering: false, } } - fn device_limits(&self, device: &Self::DeviceId) -> wgt::Limits { + fn device_limits(&self, _device: &Self::DeviceId) -> wgt::Limits { // TODO: web-sys has a method for getting limits on devices, but it returns Object not GpuLimit wgt::Limits::default() } @@ -968,34 +949,6 @@ impl crate::Context for Context { Sendable(device.0.create_compute_pipeline(&mapped_desc)) } - fn device_create_buffer_mapped<'a>( - &self, - device: &Self::DeviceId, - desc: &BufferDescriptor, - ) -> (Self::BufferId, &'a mut [u8], Self::CreateBufferMappedDetail) { - let mut mapped_desc = - web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits()); - if let Some(label) = desc.label { - mapped_desc.label(label); - } - unsafe { - let pair = device.0.create_buffer_mapped(&mapped_desc); - let id = pair.get(0).into(); - let array_buffer = pair.get(1).into(); - // TODO: Use `Vec::from_raw_parts` once it's stable - let memory = vec![0; desc.size as usize].into_boxed_slice(); - let mapped_data = std::slice::from_raw_parts_mut( - Box::into_raw(memory) as *mut u8, - desc.size as usize, - ); - ( - Sendable(id), - mapped_data, - CreateBufferMappedDetail { array_buffer }, - ) - } - } - fn device_create_buffer( &self, device: &Self::DeviceId, @@ -1071,28 +1024,34 @@ impl crate::Context for Context { // Device is polled automatically } - fn buffer_map_read( + fn buffer_map_async( &self, - buffer: &Self::BufferId, + _buffer: &Self::BufferId, + _mode: crate::MapMode, _range: Range, - ) -> Self::MapReadFuture { - MakeSendFuture(MapFuture { - child: wasm_bindgen_futures::JsFuture::from(buffer.0.map_read_async()), - buffer: Some(buffer.0.clone()), - marker: PhantomData, - }) + ) -> Self::MapAsyncFuture { + unimplemented!() + //MakeSendFuture(MapFuture { + // child: wasm_bindgen_futures::JsFuture::from(buffer.0.map_async()), + // buffer: Some(buffer.0.clone()), + // marker: PhantomData, + //}) } - fn buffer_map_write( + fn buffer_get_mapped_range( &self, - buffer: &Self::BufferId, - _range: Range, - ) -> Self::MapWriteFuture { - MakeSendFuture(MapFuture { - child: wasm_bindgen_futures::JsFuture::from(buffer.0.map_write_async()), - buffer: Some(buffer.0.clone()), - marker: PhantomData, - }) + _buffer: &Self::BufferId, + _sub_range: Range, + ) -> &[u8] { + unimplemented!() + } + + fn buffer_get_mapped_range_mut( + &self, + _buffer: &Self::BufferId, + _sub_range: Range, + ) -> &mut [u8] { + unimplemented!() } fn buffer_unmap(&self, buffer: &Self::BufferId) { @@ -1180,26 +1139,6 @@ impl crate::Context for Context { // Buffer is dropped automatically } - fn flush_mapped_data(data: &mut [u8], detail: CreateBufferMappedDetail) { - unsafe { - // Convert the `mapped_data` slice back into a `Vec`. This should be - // safe because `mapped_data` is no longer accessible beyond this - // function. - let memory: Vec = Box::<[u8]>::from_raw(data).into(); - - // Create a view into the mapped `ArrayBuffer` that was provided by the - // browser - let mapped = js_sys::Uint8Array::new(&detail.array_buffer); - - // Convert `memory` into a temporary `Uint8Array` view. This should be - // safe as long as the backing wasm memory is not resized. - let memory_view = js_sys::Uint8Array::view(&memory[..]); - - // Finally copy into `mapped` and let `memory` drop - mapped.set(&memory_view, 0); - } - } - fn encoder_copy_buffer_to_buffer( &self, encoder: &Self::CommandEncoderId, @@ -1382,39 +1321,4 @@ impl crate::Context for Context { } } -pub(crate) struct CreateBufferMappedDetail { - /// On wasm we need to allocate our own temporary storage for `data`. Later - /// we copy this temporary storage into the `Uint8Array` which was returned - /// by the browser originally. - array_buffer: js_sys::ArrayBuffer, -} - -// `CreateBufferMappedDetail` must be `Send` to match native. -// -// SAFETY: This is safe on wasm32 *for now*, but similarly to the unsafe Send impls for the handle -// type wrappers, the full story for threading on wasm32 is still unfolding. -unsafe impl Send for CreateBufferMappedDetail {} - -pub(crate) struct BufferReadMappingDetail { - pub(crate) buffer_id: Sendable, - mapped: Vec, -} - -impl BufferReadMappingDetail { - pub(crate) fn as_slice(&self) -> &[u8] { - &self.mapped[..] - } -} - -pub(crate) struct BufferWriteMappingDetail { - pub(crate) buffer_id: Sendable, - mapped: Vec, -} - -impl BufferWriteMappingDetail { - pub(crate) fn as_slice(&mut self) -> &mut [u8] { - &mut self.mapped[..] - } -} - pub(crate) type SwapChainOutputDetail = (); diff --git a/src/lib.rs b/src/lib.rs index f1370e84e0..8f72729c8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,6 @@ mod backend; #[macro_use] mod macros; -use futures::FutureExt as _; use std::{ future::Future, marker::PhantomData, @@ -14,6 +13,9 @@ use std::{ thread, }; +use futures::FutureExt as _; +use parking_lot::Mutex; + #[cfg(not(target_arch = "wasm32"))] pub use wgc::instance::{AdapterInfo, DeviceType}; pub use wgt::{ @@ -105,18 +107,12 @@ trait Context: Sized { type SwapChainId: Send + Sync; type RenderPassId: RenderPassInner; - type CreateBufferMappedDetail: Send; - type BufferReadMappingDetail: Send; - type BufferWriteMappingDetail: Send; type SwapChainOutputDetail: Send; type RequestAdapterFuture: Future> + Send; type RequestDeviceFuture: Future> + Send; - type MapReadFuture: Future> - + Send; - type MapWriteFuture: Future> - + Send; + type MapAsyncFuture: Future> + Send; fn init() -> Self; fn instance_create_surface( @@ -175,11 +171,6 @@ trait Context: Sized { device: &Self::DeviceId, desc: &ComputePipelineDescriptor, ) -> Self::ComputePipelineId; - fn device_create_buffer_mapped<'a>( - &self, - device: &Self::DeviceId, - desc: &BufferDescriptor, - ) -> (Self::BufferId, &'a mut [u8], Self::CreateBufferMappedDetail); fn device_create_buffer( &self, device: &Self::DeviceId, @@ -203,16 +194,24 @@ trait Context: Sized { fn device_drop(&self, device: &Self::DeviceId); fn device_poll(&self, device: &Self::DeviceId, maintain: Maintain); - fn buffer_map_read( + fn buffer_map_async( &self, buffer: &Self::BufferId, + mode: MapMode, range: Range, - ) -> Self::MapReadFuture; - fn buffer_map_write( + ) -> Self::MapAsyncFuture; + //TODO: we might be able to merge these, depending on how Web backend + // turns out to be implemented. + fn buffer_get_mapped_range( &self, buffer: &Self::BufferId, - range: Range, - ) -> Self::MapWriteFuture; + sub_range: Range, + ) -> &[u8]; + fn buffer_get_mapped_range_mut( + &self, + buffer: &Self::BufferId, + sub_range: Range, + ) -> &mut [u8]; fn buffer_unmap(&self, buffer: &Self::BufferId); fn swap_chain_get_next_texture( &self, @@ -271,7 +270,6 @@ trait Context: Sized { copy_size: Extent3d, ); - fn flush_mapped_data(data: &mut [u8], detail: Self::CreateBufferMappedDetail); fn encoder_begin_compute_pass(&self, encoder: &Self::CommandEncoderId) -> Self::ComputePassId; fn encoder_end_compute_pass( &self, @@ -354,11 +352,54 @@ pub enum Maintain { Poll, } +/// The main purpose of this struct is to resolve mapped ranges +/// (convert sizes to end points), and to ensure that the sub-ranges +/// don't intersect. +#[derive(Debug)] +struct MapContext { + total_size: BufferAddress, + initial_range: Range, + sub_ranges: Vec>, +} + +impl MapContext { + fn new(total_size: BufferAddress) -> Self { + MapContext { + total_size, + initial_range: 0..0, + sub_ranges: Vec::new(), + } + } + + fn reset(&mut self) { + self.initial_range = 0..0; + self.sub_ranges.clear(); + } + + fn add(&mut self, offset: BufferAddress, size: BufferSize) -> BufferAddress { + let end = if size == BufferSize::WHOLE { + self.initial_range.end + } else { + offset + size.0 + }; + assert!(self.initial_range.start <= offset && end <= self.initial_range.end); + for sub in self.sub_ranges.iter() { + assert!( + end <= sub.start || offset >= sub.end, + "Intersecting map range with {:?}", + sub + ); + } + self.sub_ranges.push(offset..end); + end + } +} + /// A handle to a GPU-accessible buffer. pub struct Buffer { context: Arc, id: ::BufferId, - size: BufferAddress, + map_context: Mutex, } /// A description of what portion of a buffer to use @@ -871,35 +912,6 @@ pub struct TextureCopyView<'a> { pub origin: Origin3d, } -/// A buffer being created, mapped in host memory. -pub struct CreateBufferMapped<'a> { - context: Arc, - id: ::BufferId, - /// The backing field for `data()`. This isn't `pub` because users shouldn't - /// be able to replace it to point somewhere else. We rely on it pointing to - /// to the correct memory later during `unmap()`. - mapped_data: &'a mut [u8], - detail: ::CreateBufferMappedDetail, -} - -impl CreateBufferMapped<'_> { - /// The mapped data. - pub fn data(&mut self) -> &mut [u8] { - self.mapped_data - } - - /// Unmaps the buffer from host memory and returns a [`Buffer`]. - pub fn finish(self) -> Buffer { - ::flush_mapped_data(self.mapped_data, self.detail); - Context::buffer_unmap(&*self.context, &self.id); - Buffer { - context: self.context, - id: self.id, - size: self.mapped_data.len() as BufferAddress, - } - } -} - impl Instance { /// Create an new instance. pub fn new() -> Self { @@ -1098,36 +1110,24 @@ impl Device { Buffer { context: Arc::clone(&self.context), id: Context::device_create_buffer(&*self.context, &self.id, desc), - size: desc.size, - } - } - - /// Creates a new buffer and maps it into host-visible memory. - /// - /// This returns a [`CreateBufferMapped`], which exposes a `&mut [u8]`. The actual [`Buffer`] - /// will not be created until calling [`CreateBufferMapped::finish`]. - pub fn create_buffer_mapped(&self, desc: &BufferDescriptor) -> CreateBufferMapped<'_> { - assert_ne!(desc.size, 0); - let (id, mapped_data, detail) = - Context::device_create_buffer_mapped(&*self.context, &self.id, desc); - CreateBufferMapped { - context: Arc::clone(&self.context), - id, - mapped_data, - detail, + map_context: Mutex::new(MapContext::new(desc.size)), } } /// Creates a new buffer, maps it into host-visible memory, copies data from the given slice, /// and finally unmaps it, returning a [`Buffer`]. pub fn create_buffer_with_data(&self, data: &[u8], usage: BufferUsage) -> Buffer { - let mut mapped = self.create_buffer_mapped(&BufferDescriptor { - size: data.len() as u64, - usage, + let size = data.len() as u64; + let buffer = self.create_buffer(&BufferDescriptor { label: None, + size, + usage, + mapped_at_creation: true, }); - mapped.data().copy_from_slice(data); - mapped.finish() + Context::buffer_get_mapped_range_mut(&*self.context, &buffer.id, 0..size) + .copy_from_slice(data); + buffer.unmap(); + buffer } /// Creates a new [`Texture`]. @@ -1172,44 +1172,10 @@ pub struct RequestDeviceError; #[derive(Clone, PartialEq, Eq, Debug)] pub struct BufferAsyncError; -pub struct BufferReadMapping { - context: Arc, - detail: ::BufferReadMappingDetail, -} - -unsafe impl Send for BufferReadMapping {} -unsafe impl Sync for BufferReadMapping {} - -impl BufferReadMapping { - pub fn as_slice(&self) -> &[u8] { - self.detail.as_slice() - } -} - -impl Drop for BufferReadMapping { - fn drop(&mut self) { - Context::buffer_unmap(&*self.context, &self.detail.buffer_id); - } -} - -pub struct BufferWriteMapping { - context: Arc, - detail: ::BufferWriteMappingDetail, -} - -unsafe impl Send for BufferWriteMapping {} -unsafe impl Sync for BufferWriteMapping {} - -impl BufferWriteMapping { - pub fn as_slice(&mut self) -> &mut [u8] { - self.detail.as_slice() - } -} - -impl Drop for BufferWriteMapping { - fn drop(&mut self) { - Context::buffer_unmap(&*self.context, &self.detail.buffer_id); - } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum MapMode { + Read, + Write, } impl Buffer { @@ -1233,7 +1199,7 @@ impl Buffer { } } - /// Map the buffer for reading. The result is returned in a future. + /// Map the buffer. Buffer is ready to map once the future is resolved. /// /// For the future to complete, `device.poll(...)` must be called elsewhere in the runtime, possibly integrated /// into an event loop, run on a separate thread, or continually polled in the same task runtime that this @@ -1241,44 +1207,44 @@ impl Buffer { /// /// It's expected that wgpu will eventually supply its own event loop infrastructure that will be easy to integrate /// into other event loops, like winit's. - pub fn map_read( + pub fn map_async( &self, - start: BufferAddress, + mode: MapMode, + offset: BufferAddress, size: BufferSize, - ) -> impl Future> + Send { - let context = Arc::clone(&self.context); - let end = if size == BufferSize::WHOLE { - self.size - } else { - start + size.0 + ) -> impl Future> + Send { + let end = { + let mut mc = self.map_context.lock(); + assert_eq!( + mc.initial_range, + 0..0, + "Buffer {:?} is already mapped", + self.id + ); + let end = if size == BufferSize::WHOLE { + mc.total_size + } else { + offset + size.0 + }; + mc.initial_range = offset..end; + end }; - self.context - .buffer_map_read(&self.id, start..end) - .map(|result| result.map(|detail| BufferReadMapping { context, detail })) + Context::buffer_map_async(&*self.context, &self.id, mode, offset..end) } - /// Map the buffer for writing. The result is returned in a future. - /// - /// See the documentation of (map_read)[#method.map_read] for more information about - /// how to run this future. - pub fn map_write( - &self, - start: BufferAddress, - size: BufferSize, - ) -> impl Future> + Send { - let context = Arc::clone(&self.context); - let end = if size == BufferSize::WHOLE { - self.size - } else { - start + size.0 - }; - self.context - .buffer_map_write(&self.id, start..end) - .map(|result| result.map(|detail| BufferWriteMapping { context, detail })) + pub fn get_mapped_range(&self, offset: BufferAddress, size: BufferSize) -> &[u8] { + let end = self.map_context.lock().add(offset, size); + Context::buffer_get_mapped_range(&*self.context, &self.id, offset..end) + } + + pub fn get_mapped_range_mut(&self, offset: BufferAddress, size: BufferSize) -> &mut [u8] { + let end = self.map_context.lock().add(offset, size); + Context::buffer_get_mapped_range_mut(&*self.context, &self.id, offset..end) } /// Flushes any pending write operations and unmaps the buffer from host memory. pub fn unmap(&self) { + self.map_context.lock().reset(); Context::buffer_unmap(&*self.context, &self.id); } }