[rs] Update web bindings

This commit is contained in:
Joshua Groves 2020-11-10 23:35:34 -03:30
parent 6f712d59bd
commit f79845e777
6 changed files with 180 additions and 175 deletions

View File

@ -97,6 +97,10 @@ test = true
#gfx-backend-dx12 = { version = "0.6", path = "../gfx/src/backend/dx12" }
#gfx-backend-dx11 = { version = "0.6", path = "../gfx/src/backend/dx11" }
#gfx-backend-metal = { version = "0.6", path = "../gfx/src/backend/metal" }
wasm-bindgen = { git = "https://github.com/rustwasm/wasm-bindgen", rev = "316c5a70fdc4a052fb65ef82bf02d52107b5671b" }
wasm-bindgen-futures = { git = "https://github.com/rustwasm/wasm-bindgen", rev = "316c5a70fdc4a052fb65ef82bf02d52107b5671b" }
web-sys = { git = "https://github.com/rustwasm/wasm-bindgen", rev = "316c5a70fdc4a052fb65ef82bf02d52107b5671b" }
js-sys = { git = "https://github.com/rustwasm/wasm-bindgen", rev = "316c5a70fdc4a052fb65ef82bf02d52107b5671b" }
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2.7"
@ -149,6 +153,7 @@ web-sys = { version = "=0.3.45", features = [
"GpuInputStepMode",
"GpuLimits",
"GpuLoadOp",
"GpuMapMode",
"GpuOrigin3dDict",
"GpuPipelineLayout",
"GpuPipelineLayoutDescriptor",

View File

@ -1216,7 +1216,7 @@ impl crate::Context for Context {
&self,
buffer: &Self::BufferId,
sub_range: Range<wgt::BufferAddress>,
) -> &[u8] {
) -> BufferMappedRange {
let size = sub_range.end - sub_range.start;
let global = &self.0;
match wgc::gfx_select!(buffer.id => global.buffer_get_mapped_range(
@ -1224,28 +1224,15 @@ impl crate::Context for Context {
sub_range.start,
wgt::BufferSize::new(size)
)) {
Ok(ptr) => unsafe { slice::from_raw_parts(ptr, size as usize) },
Ok(ptr) => BufferMappedRange {
ptr,
size: size as usize,
phantom: PhantomData,
},
Err(err) => self.handle_error_fatal(err, "Buffer::get_mapped_range"),
}
}
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 global = &self.0;
match wgc::gfx_select!(buffer.id => global.buffer_get_mapped_range(
buffer.id,
sub_range.start,
wgt::BufferSize::new(size)
)) {
Ok(ptr) => unsafe { slice::from_raw_parts_mut(ptr, size as usize) },
Err(err) => self.handle_error_fatal(err, "Buffer::get_mapped_range_mut"),
}
}
fn buffer_unmap(&self, buffer: &Self::BufferId) {
let global = &self.0;
match wgc::gfx_select!(buffer.id => global.buffer_unmap(buffer.id)) {
@ -1740,3 +1727,27 @@ fn default_error_handler(err: crate::Error) {
panic!("Handling wgpu errors as fatal by default");
}
#[derive(Debug)]
pub struct BufferMappedRange<'a> {
ptr: *mut u8,
size: usize,
phantom: PhantomData<&'a ()>,
}
impl<'a> crate::BufferMappedRangeSlice for BufferMappedRange<'a> {
fn slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.ptr, self.size) }
}
fn slice_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.ptr, self.size) }
}
}
impl<'a> Drop for BufferMappedRange<'a> {
fn drop(&mut self) {
// Intentionally left blank so that `BufferMappedRange` still
// implements `Drop`, to match the web backend
}
}

View File

@ -1,7 +1,7 @@
#[cfg(target_arch = "wasm32")]
mod web;
#[cfg(target_arch = "wasm32")]
pub(crate) use web::Context;
pub(crate) use web::{Context, BufferMappedRange};
#[cfg(not(target_arch = "wasm32"))]
mod direct;
@ -9,7 +9,7 @@ mod direct;
mod error;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) use direct::Context;
pub(crate) use direct::{Context, BufferMappedRange};
#[cfg(not(target_arch = "wasm32"))]
mod native_gpu_future;

View File

@ -42,29 +42,6 @@ impl fmt::Debug for Context {
}
}
impl Context {
pub fn create_buffer_init_polyfill(
device: &Sendable<web_sys::GpuDevice>,
desc: &BufferDescriptor<'_>,
contents: &[u8],
) -> Sendable<web_sys::GpuBuffer> {
// Emulate buffer mapping with the old API. This is a temporary
// polyfill until the new buffer mapping API is available on gecko.
let mut buffer_desc =
web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits());
if let Some(label) = desc.label {
buffer_desc.label(label);
}
let buffer = device.0.create_buffer(&buffer_desc);
let data = js_sys::Uint8Array::from(contents).buffer();
device
.0
.default_queue()
.write_buffer_with_u32(&buffer, 0, &data);
Sendable(buffer)
}
}
#[derive(Debug)]
pub(crate) struct ComputePass(web_sys::GpuComputePassEncoder);
#[derive(Debug)]
@ -331,7 +308,7 @@ fn map_texture_format(texture_format: wgt::TextureFormat) -> web_sys::GpuTexture
TextureFormat::Bgra8Unorm => tf::Bgra8unorm,
TextureFormat::Bgra8UnormSrgb => tf::Bgra8unormSrgb,
TextureFormat::Rgb10a2Unorm => tf::Rgb10a2unorm,
TextureFormat::Rg11b10Float => tf::Rg11b10float,
TextureFormat::Rg11b10Float => tf::Rg11b10ufloat,
TextureFormat::Rg32Uint => tf::Rg32uint,
TextureFormat::Rg32Sint => tf::Rg32sint,
TextureFormat::Rg32Float => tf::Rg32float,
@ -355,9 +332,7 @@ fn map_texture_component_type(
wgt::TextureComponentType::Float => web_sys::GpuTextureComponentType::Float,
wgt::TextureComponentType::Sint => web_sys::GpuTextureComponentType::Sint,
wgt::TextureComponentType::Uint => web_sys::GpuTextureComponentType::Uint,
wgt::TextureComponentType::DepthComparison => {
panic!("depth-comparison is not supported here yet")
}
wgt::TextureComponentType::DepthComparison => web_sys::GpuTextureComponentType::DepthComparison,
}
}
@ -583,7 +558,11 @@ fn map_vertex_state_descriptor(
}
fn map_extent_3d(extent: wgt::Extent3d) -> web_sys::GpuExtent3dDict {
web_sys::GpuExtent3dDict::new(extent.depth, extent.height, extent.width)
let mut mapped = web_sys::GpuExtent3dDict::new();
mapped.depth(extent.depth);
mapped.height(extent.height);
mapped.width(extent.height);
mapped
}
fn map_origin_3d(origin: wgt::Origin3d) -> web_sys::GpuOrigin3dDict {
@ -617,7 +596,8 @@ fn map_texture_view_dimension(
}
fn map_buffer_copy_view(view: crate::BufferCopyView) -> web_sys::GpuBufferCopyView {
let mut mapped = web_sys::GpuBufferCopyView::new(view.layout.bytes_per_row, &view.buffer.id.0);
let mut mapped = web_sys::GpuBufferCopyView::new(&view.buffer.id.0);
mapped.bytes_per_row(view.layout.bytes_per_row);
mapped.rows_per_image(view.layout.rows_per_image);
mapped.offset(view.layout.offset as f64);
mapped
@ -666,6 +646,13 @@ fn map_store_op(store: bool) -> web_sys::GpuStoreOp {
}
}
fn map_map_mode(mode: crate::MapMode) -> u32 {
match mode {
crate::MapMode::Read => web_sys::GpuMapMode::READ,
crate::MapMode::Write => web_sys::GpuMapMode::WRITE,
}
}
type JsFutureResult = Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>;
type FutureMap<T> = futures::future::Map<wasm_bindgen_futures::JsFuture, fn(JsFutureResult) -> T>;
@ -688,35 +675,13 @@ fn future_request_device(
.map_err(|_| crate::RequestDeviceError)
}
pub(crate) struct MapFuture<T> {
child: wasm_bindgen_futures::JsFuture,
buffer: Option<web_sys::GpuBuffer>,
marker: PhantomData<T>,
}
impl<T> Unpin for MapFuture<T> {}
//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,
) -> std::task::Poll<Self::Output> {
std::future::Future::poll(
std::pin::Pin::new(&mut self.as_mut().get_mut().child),
context,
)
.map(|result| {
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);
() //TODO
})
.map_err(|_| crate::BufferAsyncError)
})
}
fn future_map_async(
result: JsFutureResult,
) -> Result<(), crate::BufferAsyncError>
{
result
.map(|_| ())
.map_err(|_| crate::BufferAsyncError)
}
impl crate::Context for Context {
@ -748,7 +713,7 @@ impl crate::Context for Context {
type RequestDeviceFuture = MakeSendFuture<
FutureMap<Result<(Self::DeviceId, Self::QueueId), crate::RequestDeviceError>>,
>;
type MapAsyncFuture = MakeSendFuture<MapFuture<()>>;
type MapAsyncFuture = MakeSendFuture<FutureMap<Result<(), crate::BufferAsyncError>>>;
fn init(_backends: wgt::BackendBit) -> Self {
Context(web_sys::window().unwrap().navigator().gpu())
@ -893,6 +858,7 @@ impl crate::Context for Context {
BindingType::StorageBuffer { readonly: true, .. } => bt::ReadonlyStorageBuffer,
BindingType::Sampler { comparison: false } => bt::Sampler,
BindingType::Sampler { .. } => bt::ComparisonSampler,
BindingType::SampledTexture { multisampled: true, .. } => bt::MultisampledTexture,
BindingType::SampledTexture { .. } => bt::SampledTexture,
BindingType::StorageTexture { readonly: true, .. } => {
bt::ReadonlyStorageTexture
@ -921,12 +887,10 @@ impl crate::Context for Context {
if let BindingType::SampledTexture {
component_type,
multisampled,
..
} = bind.ty
{
mapped_entry.texture_component_type(map_texture_component_type(component_type));
mapped_entry.multisampled(multisampled);
}
match bind.ty {
@ -1096,6 +1060,7 @@ impl crate::Context for Context {
) -> Self::BufferId {
let mut mapped_desc =
web_sys::GpuBufferDescriptor::new(desc.size as f64, desc.usage.bits());
mapped_desc.mapped_at_creation(desc.mapped_at_creation);
if let Some(ref label) = desc.label {
mapped_desc.label(label);
}
@ -1182,32 +1147,37 @@ impl crate::Context for Context {
fn buffer_map_async(
&self,
_buffer: &Self::BufferId,
_mode: crate::MapMode,
_range: Range<wgt::BufferAddress>,
buffer: &Self::BufferId,
mode: crate::MapMode,
range: Range<wgt::BufferAddress>,
) -> Self::MapAsyncFuture {
unimplemented!()
//MakeSendFuture(MapFuture {
// child: wasm_bindgen_futures::JsFuture::from(buffer.0.map_async()),
// buffer: Some(buffer.0.clone()),
// marker: PhantomData,
//})
let map_promise = buffer.0.map_async_with_f64_and_f64(
map_map_mode(mode),
range.start as f64,
(range.end - range.start) as f64,
);
MakeSendFuture(
wasm_bindgen_futures::JsFuture::from(map_promise).map(future_map_async),
)
}
fn buffer_get_mapped_range(
&self,
_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!()
buffer: &Self::BufferId,
sub_range: Range<wgt::BufferAddress>,
) -> BufferMappedRange {
let array_buffer = buffer.0.get_mapped_range_with_f64_and_f64(
sub_range.start as f64,
(sub_range.end - sub_range.start) as f64,
);
let actual_mapping = js_sys::Uint8Array::new(&array_buffer);
let temporary_mapping = actual_mapping.to_vec();
BufferMappedRange {
actual_mapping,
temporary_mapping,
phantom: PhantomData,
}
}
fn buffer_unmap(&self, buffer: &Self::BufferId) {
@ -1499,13 +1469,13 @@ impl crate::Context for Context {
Sendable(encoder.finish_with_descriptor(&mapped_desc))
}
fn command_encoder_insert_debug_marker(&self, encoder: &Self::CommandEncoderId, label: &str) {
fn command_encoder_insert_debug_marker(&self, _encoder: &Self::CommandEncoderId, _label: &str) {
// TODO
}
fn command_encoder_push_debug_group(&self, encoder: &Self::CommandEncoderId, label: &str) {
fn command_encoder_push_debug_group(&self, _encoder: &Self::CommandEncoderId, _label: &str) {
// TODO
}
fn command_encoder_pop_debug_group(&self, encoder: &Self::CommandEncoderId) {
fn command_encoder_pop_debug_group(&self, _encoder: &Self::CommandEncoderId) {
// TODO
}
@ -1524,7 +1494,16 @@ impl crate::Context for Context {
offset: wgt::BufferAddress,
data: &[u8],
) {
queue.0.write_buffer_with_f64_and_f64_and_f64(
/* Skip the copy once gecko allows BufferSource instead of ArrayBuffer
queue.0.write_buffer_with_f64_and_u8_array_and_f64_and_f64(
&buffer.0,
offset as f64,
data,
0f64,
data.len() as f64,
);
*/
queue.0.write_buffer_with_f64_and_buffer_source_and_f64_and_f64(
&buffer.0,
offset as f64,
&js_sys::Uint8Array::from(data).buffer(),
@ -1541,11 +1520,20 @@ impl crate::Context for Context {
data_layout: wgt::TextureDataLayout,
size: wgt::Extent3d,
) {
let mut mapped_data_layout = web_sys::GpuTextureDataLayout::new(data_layout.bytes_per_row);
let mut mapped_data_layout = web_sys::GpuTextureDataLayout::new();
mapped_data_layout.bytes_per_row(data_layout.bytes_per_row);
mapped_data_layout.rows_per_image(data_layout.rows_per_image);
mapped_data_layout.offset(data_layout.offset as f64);
queue.0.write_texture_with_gpu_extent_3d_dict(
/* Skip the copy once gecko allows BufferSource instead of ArrayBuffer
queue.0.write_texture_with_u8_array_and_gpu_extent_3d_dict(
&map_texture_copy_view(texture),
data,
&mapped_data_layout,
&map_extent_3d(size),
);
*/
queue.0.write_texture_with_buffer_source_and_gpu_extent_3d_dict(
&map_texture_copy_view(texture),
&js_sys::Uint8Array::from(data).buffer(),
&mapped_data_layout,
@ -1565,3 +1553,33 @@ impl crate::Context for Context {
}
pub(crate) type SwapChainOutputDetail = ();
#[derive(Debug)]
pub struct BufferMappedRange<'a> {
actual_mapping: js_sys::Uint8Array,
temporary_mapping: Vec<u8>,
phantom: PhantomData<&'a ()>,
}
impl<'a> crate::BufferMappedRangeSlice for BufferMappedRange<'a> {
fn slice(&self) -> &[u8] {
&self.temporary_mapping
}
fn slice_mut(&mut self) -> &mut [u8] {
&mut self.temporary_mapping
}
}
impl<'a> Drop for BufferMappedRange<'a> {
fn drop(&mut self) {
// Copy from the temporary mapping back into the array buffer that was
// originally provided by the browser
let temporary_mapping_slice = self.temporary_mapping.as_slice();
unsafe {
// Note: no allocations can happen between `view` and `set`, or this
// will break
self.actual_mapping.set(&js_sys::Uint8Array::view(temporary_mapping_slice), 0);
}
}
}

View File

@ -45,7 +45,7 @@ pub use wgt::{
COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
};
use backend::Context as C;
use backend::{Context as C, BufferMappedRange};
trait ComputePassInner<Ctx: Context> {
fn set_pipeline(&mut self, pipeline: &Ctx::ComputePipelineId);
@ -274,18 +274,11 @@ trait Context: Debug + Send + Sized + Sync {
mode: MapMode,
range: Range<BufferAddress>,
) -> 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,
sub_range: Range<BufferAddress>,
) -> &[u8];
fn buffer_get_mapped_range_mut(
&self,
buffer: &Self::BufferId,
sub_range: Range<BufferAddress>,
) -> &mut [u8];
) -> BufferMappedRange;
fn buffer_unmap(&self, buffer: &Self::BufferId);
fn swap_chain_get_current_texture_view(
&self,
@ -466,9 +459,8 @@ 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.
/// 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,
@ -1602,18 +1594,23 @@ fn range_to_offset_size<S: RangeBounds<BufferAddress>>(
(offset, size)
}
trait BufferMappedRangeSlice {
fn slice(&self) -> &[u8];
fn slice_mut(&mut self) -> &mut [u8];
}
/// Read only view into a mapped buffer.
#[derive(Debug)]
pub struct BufferView<'a> {
slice: BufferSlice<'a>,
data: &'a [u8],
data: BufferMappedRange<'a>,
}
/// Write only view into mapped buffer.
#[derive(Debug)]
pub struct BufferViewMut<'a> {
slice: BufferSlice<'a>,
data: &'a mut [u8],
data: BufferMappedRange<'a>,
readable: bool,
}
@ -1621,7 +1618,7 @@ impl std::ops::Deref for BufferView<'_> {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.data
self.data.slice()
}
}
@ -1634,25 +1631,25 @@ impl std::ops::Deref for BufferViewMut<'_> {
"Attempting to read a write-only mapping for buffer {:?}",
self.slice.buffer.id
);
self.data
self.data.slice()
}
}
impl std::ops::DerefMut for BufferViewMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.data
self.data.slice_mut()
}
}
impl AsRef<[u8]> for BufferView<'_> {
fn as_ref(&self) -> &[u8] {
self.data
self.data.slice()
}
}
impl AsMut<[u8]> for BufferViewMut<'_> {
fn as_mut(&mut self) -> &mut [u8] {
self.data
self.data.slice_mut()
}
}
@ -1724,21 +1721,19 @@ impl<'a> BufferSlice<'a> {
&self,
mode: MapMode,
) -> impl Future<Output = Result<(), BufferAsyncError>> + Send {
let end = {
let mut mc = self.buffer.map_context.lock();
assert_eq!(
mc.initial_range,
0..0,
"Buffer {:?} is already mapped",
self.buffer.id
);
let end = match self.size {
Some(s) => self.offset + s.get(),
None => mc.total_size,
};
mc.initial_range = self.offset..end;
end
let mut mc = self.buffer.map_context.lock();
assert_eq!(
mc.initial_range,
0..0,
"Buffer {:?} is already mapped",
self.buffer.id
);
let end = match self.size {
Some(s) => self.offset + s.get(),
None => mc.total_size,
};
mc.initial_range = self.offset..end;
Context::buffer_map_async(
&*self.buffer.context,
&self.buffer.id,
@ -1763,7 +1758,7 @@ impl<'a> BufferSlice<'a> {
/// through [`BufferDescriptor::mapped_at_creation`] or [`BufferSlice::map_async`], will panic.
pub fn get_mapped_range_mut(&self) -> BufferViewMut<'a> {
let end = self.buffer.map_context.lock().add(self.offset, self.size);
let data = Context::buffer_get_mapped_range_mut(
let data = Context::buffer_get_mapped_range(
&*self.buffer.context,
&self.buffer.id,
self.offset..end,

View File

@ -9,7 +9,6 @@ use std::{
};
pub use belt::StagingBelt;
use std::sync::Arc;
/// Treat the given byte slice as a SPIR-V module.
///
@ -75,39 +74,16 @@ impl DeviceExt for crate::Device {
map_context.initial_range = 0..padded_size;
#[cfg(target_arch = "wasm32")]
let buffer = crate::Buffer {
context: Arc::clone(&self.context),
id: crate::backend::Context::create_buffer_init_polyfill(
&self.id,
&wgt_descriptor,
descriptor.contents,
),
map_context: parking_lot::Mutex::new(map_context),
usage: descriptor.usage,
};
#[cfg(not(target_arch = "wasm32"))]
let buffer = {
let buffer = crate::Buffer {
context: Arc::clone(&self.context),
id: crate::Context::device_create_buffer(&*self.context, &self.id, &wgt_descriptor),
map_context: parking_lot::Mutex::new(map_context),
usage: descriptor.usage,
};
let range = crate::Context::buffer_get_mapped_range_mut(
&*self.context,
&buffer.id,
0..padded_size,
);
range[0..unpadded_size as usize].copy_from_slice(descriptor.contents);
let buffer = self.create_buffer(&wgt_descriptor);
{
let mut slice = buffer.slice(..).get_mapped_range_mut();
slice[0..unpadded_size as usize].copy_from_slice(descriptor.contents);
for i in unpadded_size..padded_size {
range[i as usize] = 0;
slice[i as usize] = 0;
}
buffer.unmap();
buffer
};
}
buffer.unmap();
buffer
}
}