344: Use the new map-async r=kvark a=kvark

Depends on https://github.com/gfx-rs/wgpu/pull/675
It changes the API of mapping and removes `create_buffer_mapped`.
The new functionality is not implemented in Gecko yet, so this breaks the web target, for now.

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot]
2020-06-02 18:24:02 +00:00
committed by GitHub
8 changed files with 226 additions and 435 deletions

View File

@@ -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"

View File

@@ -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::<u32>() 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();
}
}

View File

@@ -51,20 +51,20 @@ async fn execute_gpu(numbers: Vec<u32>) -> Vec<u32> {
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<u32>) -> Vec<u32> {
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<u32>) -> Vec<u32> {
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<u32>) -> Vec<u32> {
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!")
}

View File

@@ -236,9 +236,10 @@ impl framework::Example for Example {
let entity_uniform_size = mem::size_of::<EntityUniforms>() 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::<LightRaw>()) 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::<ShadowUniforms>() 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

View File

@@ -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<Option<Self::AdapterId>>;
type RequestDeviceFuture =
Ready<Result<(Self::DeviceId, Self::QueueId), crate::RequestDeviceError>>;
type MapReadFuture =
native_gpu_future::GpuFuture<Result<BufferReadMappingDetail, crate::BufferAsyncError>>;
type MapWriteFuture =
native_gpu_future::GpuFuture<Result<BufferWriteMappingDetail, crate::BufferAsyncError>>;
type MapAsyncFuture = native_gpu_future::GpuFuture<Result<(), crate::BufferAsyncError>>;
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<wgt::BufferAddress>,
) -> 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<wgt::BufferAddress>,
) -> Self::MapWriteFuture {
let (future, completion) =
native_gpu_future::new_gpu_future(*buffer, range.end - range.start);
sub_range: Range<wgt::BufferAddress>,
) -> &[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<wgt::BufferAddress>,
) -> &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,

View File

@@ -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<T> {
Result(T),
}
type GpuFutureData<T> = Mutex<Option<WakerOrResult<T>>>;
/// A Future that can poll the wgpu::Device
pub struct GpuFuture<T> {
data: Arc<Data<T>>,
data: Arc<GpuFutureData<T>>,
}
pub enum OpaqueData {}
struct Data<T> {
buffer_id: wgc::id::BufferId,
size: BufferAddress,
waker_or_result: Mutex<Option<WakerOrResult<T>>>,
}
//TODO: merge this with `GpuFuture` and avoid `Arc` on the data.
/// A completion handle to set the result on a GpuFuture
pub struct GpuFutureCompletion<T> {
data: Arc<Data<T>>,
data: Arc<GpuFutureData<T>>,
}
impl<T> Future for GpuFuture<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll<Self::Output> {
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<T> Future for GpuFuture<T> {
impl<T> GpuFutureCompletion<T> {
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<T> GpuFutureCompletion<T> {
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<T>(
buffer_id: wgc::id::BufferId,
size: BufferAddress,
) -> (GpuFuture<T>, GpuFutureCompletion<T>) {
let data = Arc::new(Data {
buffer_id,
size,
waker_or_result: Mutex::new(None),
});
pub(crate) fn new_gpu_future<T>() -> (GpuFuture<T>, GpuFutureCompletion<T>) {
let data = Arc::new(Mutex::new(None));
(
GpuFuture {
data: Arc::clone(&data),

View File

@@ -574,25 +574,10 @@ pub(crate) struct MapFuture<T> {
marker: PhantomData<T>,
}
impl<T> Unpin for MapFuture<T> {}
type MapData = (web_sys::GpuBuffer, Vec<u8>);
impl From<MapData> for BufferReadMappingDetail {
fn from((buffer_id, mapped): MapData) -> Self {
BufferReadMappingDetail {
buffer_id: Sendable(buffer_id),
mapped,
}
}
}
impl From<MapData> for BufferWriteMappingDetail {
fn from((buffer_id, mapped): MapData) -> Self {
BufferWriteMappingDetail {
buffer_id: Sendable(buffer_id),
mapped,
}
}
}
impl<T: From<MapData>> std::future::Future for MapFuture<T> {
type Output = Result<T, crate::BufferAsyncError>;
//type MapData = (web_sys::GpuBuffer, Vec<u8>);
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<T: From<MapData>> std::future::Future for MapFuture<T> {
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<web_sys::GpuSwapChain>;
type RenderPassId = RenderPass;
type CreateBufferMappedDetail = CreateBufferMappedDetail;
type BufferReadMappingDetail = BufferReadMappingDetail;
type BufferWriteMappingDetail = BufferWriteMappingDetail;
type SwapChainOutputDetail = SwapChainOutputDetail;
type RequestAdapterFuture = MakeSendFuture<FutureMap<Option<Self::AdapterId>>>;
type RequestDeviceFuture = MakeSendFuture<
FutureMap<Result<(Self::DeviceId, Self::QueueId), crate::RequestDeviceError>>,
>;
type MapReadFuture = MakeSendFuture<MapFuture<BufferReadMappingDetail>>;
type MapWriteFuture = MakeSendFuture<MapFuture<BufferWriteMappingDetail>>;
type MapAsyncFuture = MakeSendFuture<MapFuture<()>>;
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<wgt::BufferAddress>,
) -> 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<wgt::BufferAddress>,
) -> 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<wgt::BufferAddress>,
) -> &[u8] {
unimplemented!()
}
fn buffer_get_mapped_range_mut(
&self,
_buffer: &Self::BufferId,
_sub_range: Range<wgt::BufferAddress>,
) -> &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<u8> = 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<web_sys::GpuBuffer>,
mapped: Vec<u8>,
}
impl BufferReadMappingDetail {
pub(crate) fn as_slice(&self) -> &[u8] {
&self.mapped[..]
}
}
pub(crate) struct BufferWriteMappingDetail {
pub(crate) buffer_id: Sendable<web_sys::GpuBuffer>,
mapped: Vec<u8>,
}
impl BufferWriteMappingDetail {
pub(crate) fn as_slice(&mut self) -> &mut [u8] {
&mut self.mapped[..]
}
}
pub(crate) type SwapChainOutputDetail = ();

View File

@@ -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<Self>;
type CreateBufferMappedDetail: Send;
type BufferReadMappingDetail: Send;
type BufferWriteMappingDetail: Send;
type SwapChainOutputDetail: Send;
type RequestAdapterFuture: Future<Output = Option<Self::AdapterId>> + Send;
type RequestDeviceFuture: Future<Output = Result<(Self::DeviceId, Self::QueueId), RequestDeviceError>>
+ Send;
type MapReadFuture: Future<Output = Result<Self::BufferReadMappingDetail, BufferAsyncError>>
+ Send;
type MapWriteFuture: Future<Output = Result<Self::BufferWriteMappingDetail, BufferAsyncError>>
+ Send;
type MapAsyncFuture: Future<Output = Result<(), BufferAsyncError>> + 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<BufferAddress>,
) -> 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<BufferAddress>,
) -> Self::MapWriteFuture;
sub_range: Range<BufferAddress>,
) -> &[u8];
fn buffer_get_mapped_range_mut(
&self,
buffer: &Self::BufferId,
sub_range: Range<BufferAddress>,
) -> &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<BufferAddress>,
sub_ranges: Vec<Range<BufferAddress>>,
}
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<C>,
id: <C as Context>::BufferId,
size: BufferAddress,
map_context: Mutex<MapContext>,
}
/// 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<C>,
id: <C as Context>::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: <C as Context>::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 {
<C as Context>::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<C>,
detail: <C as Context>::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<C>,
detail: <C as Context>::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<Output = Result<BufferReadMapping, BufferAsyncError>> + Send {
let context = Arc::clone(&self.context);
let end = if size == BufferSize::WHOLE {
self.size
} else {
start + size.0
) -> impl Future<Output = Result<(), BufferAsyncError>> + 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<Output = Result<BufferWriteMapping, BufferAsyncError>> + 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);
}
}