mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 16:24:24 +00:00
Implement Queue::write_buffer_with (#2777)
* implement Queue::write_buffer_with * address comments * update doc * Fix copy span location Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
parent
0eb6845b6a
commit
5eb09f6bd7
@ -8,10 +8,10 @@ use crate::{
|
||||
conv,
|
||||
device::{DeviceError, WaitIdleError},
|
||||
get_lowest_common_denom,
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Token},
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Input, Token},
|
||||
id,
|
||||
init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
|
||||
resource::{BufferAccessError, BufferMapState, TextureInner},
|
||||
resource::{BufferAccessError, BufferMapState, StagingBuffer, TextureInner},
|
||||
track, FastHashSet, SubmissionIndex,
|
||||
};
|
||||
|
||||
@ -86,28 +86,6 @@ pub struct WrappedSubmissionIndex {
|
||||
pub index: SubmissionIndex,
|
||||
}
|
||||
|
||||
struct StagingData<A: hal::Api> {
|
||||
buffer: A::Buffer,
|
||||
}
|
||||
|
||||
impl<A: hal::Api> StagingData<A> {
|
||||
unsafe fn write(
|
||||
&self,
|
||||
device: &A::Device,
|
||||
offset: wgt::BufferAddress,
|
||||
data: &[u8],
|
||||
) -> Result<(), hal::DeviceError> {
|
||||
let mapping = device.map_buffer(&self.buffer, offset..offset + data.len() as u64)?;
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len());
|
||||
if !mapping.is_coherent {
|
||||
device
|
||||
.flush_mapped_ranges(&self.buffer, iter::once(offset..offset + data.len() as u64));
|
||||
}
|
||||
device.unmap_buffer(&self.buffer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TempResource<A: hal::Api> {
|
||||
Buffer(A::Buffer),
|
||||
@ -178,8 +156,8 @@ impl<A: hal::Api> PendingWrites<A> {
|
||||
self.temp_resources.push(resource);
|
||||
}
|
||||
|
||||
fn consume(&mut self, stage: StagingData<A>) {
|
||||
self.temp_resources.push(TempResource::Buffer(stage.buffer));
|
||||
fn consume(&mut self, buffer: StagingBuffer<A>) {
|
||||
self.temp_resources.push(TempResource::Buffer(buffer.raw));
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -240,16 +218,38 @@ impl<A: hal::Api> PendingWrites<A> {
|
||||
}
|
||||
|
||||
impl<A: HalApi> super::Device<A> {
|
||||
fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result<StagingData<A>, DeviceError> {
|
||||
profiling::scope!("prepare_stage");
|
||||
fn prepare_staging_buffer(
|
||||
&mut self,
|
||||
size: wgt::BufferAddress,
|
||||
) -> Result<(StagingBuffer<A>, *mut u8), DeviceError> {
|
||||
profiling::scope!("prepare_staging_buffer");
|
||||
let stage_desc = hal::BufferDescriptor {
|
||||
label: Some("(wgpu internal) Staging"),
|
||||
size,
|
||||
usage: hal::BufferUses::MAP_WRITE | hal::BufferUses::COPY_SRC,
|
||||
memory_flags: hal::MemoryFlags::TRANSIENT,
|
||||
};
|
||||
|
||||
let buffer = unsafe { self.raw.create_buffer(&stage_desc)? };
|
||||
Ok(StagingData { buffer })
|
||||
let mapping = unsafe { self.raw.map_buffer(&buffer, 0..size) }?;
|
||||
|
||||
let staging_buffer = StagingBuffer {
|
||||
raw: buffer,
|
||||
size,
|
||||
is_coherent: mapping.is_coherent,
|
||||
};
|
||||
|
||||
Ok((staging_buffer, mapping.ptr.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> StagingBuffer<A> {
|
||||
unsafe fn flush(&self, device: &A::Device) -> Result<(), DeviceError> {
|
||||
if !self.is_coherent {
|
||||
device.flush_mapped_ranges(&self.raw, iter::once(0..self.size));
|
||||
}
|
||||
device.unmap_buffer(&self.raw)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,12 +298,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
profiling::scope!("write_buffer", "Queue");
|
||||
|
||||
let hub = A::hub(self);
|
||||
let mut token = Token::root();
|
||||
let (mut device_guard, mut token) = hub.devices.write(&mut token);
|
||||
let root_token = &mut Token::root();
|
||||
|
||||
let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
|
||||
let device = device_guard
|
||||
.get_mut(queue_id)
|
||||
.map_err(|_| DeviceError::Invalid)?;
|
||||
let (buffer_guard, _) = hub.buffers.read(&mut token);
|
||||
|
||||
let data_size = data.len() as wgt::BufferAddress;
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref trace) = device.trace {
|
||||
@ -312,81 +314,197 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
trace.add(Action::WriteBuffer {
|
||||
id: buffer_id,
|
||||
data: data_path,
|
||||
range: buffer_offset..buffer_offset + data.len() as wgt::BufferAddress,
|
||||
range: buffer_offset..buffer_offset + data_size,
|
||||
queued: true,
|
||||
});
|
||||
}
|
||||
|
||||
let data_size = data.len() as wgt::BufferAddress;
|
||||
if data_size == 0 {
|
||||
log::trace!("Ignoring write_buffer of size 0");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let stage = device.prepare_stage(data_size)?;
|
||||
let (staging_buffer, staging_buffer_ptr) = device.prepare_staging_buffer(data_size)?;
|
||||
|
||||
unsafe {
|
||||
profiling::scope!("copy");
|
||||
stage.write(&device.raw, 0, data)
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), staging_buffer_ptr, data.len());
|
||||
staging_buffer.flush(&device.raw)?;
|
||||
};
|
||||
|
||||
self.queue_write_staging_buffer_impl(
|
||||
device,
|
||||
device_token,
|
||||
staging_buffer,
|
||||
buffer_id,
|
||||
buffer_offset,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn queue_create_staging_buffer<A: HalApi>(
|
||||
&self,
|
||||
queue_id: id::QueueId,
|
||||
buffer_size: wgt::BufferSize,
|
||||
id_in: Input<G, id::StagingBufferId>,
|
||||
) -> Result<(id::StagingBufferId, *mut u8), QueueWriteError> {
|
||||
let hub = A::hub(self);
|
||||
let root_token = &mut Token::root();
|
||||
|
||||
let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
|
||||
let device = device_guard
|
||||
.get_mut(queue_id)
|
||||
.map_err(|_| DeviceError::Invalid)?;
|
||||
|
||||
let (staging_buffer, staging_buffer_ptr) =
|
||||
device.prepare_staging_buffer(buffer_size.get())?;
|
||||
|
||||
let fid = hub.staging_buffers.prepare(id_in);
|
||||
let id = fid.assign(staging_buffer, device_token);
|
||||
|
||||
Ok((id.0, staging_buffer_ptr))
|
||||
}
|
||||
|
||||
pub fn queue_write_staging_buffer<A: HalApi>(
|
||||
&self,
|
||||
queue_id: id::QueueId,
|
||||
buffer_id: id::BufferId,
|
||||
buffer_offset: wgt::BufferAddress,
|
||||
staging_buffer_id: id::StagingBufferId,
|
||||
) -> Result<(), QueueWriteError> {
|
||||
profiling::scope!("write_buffer_with", "Queue");
|
||||
|
||||
let hub = A::hub(self);
|
||||
let root_token = &mut Token::root();
|
||||
|
||||
let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
|
||||
let device = device_guard
|
||||
.get_mut(queue_id)
|
||||
.map_err(|_| DeviceError::Invalid)?;
|
||||
|
||||
let staging_buffer = hub
|
||||
.staging_buffers
|
||||
.unregister(staging_buffer_id, device_token)
|
||||
.0
|
||||
.ok_or(TransferError::InvalidBuffer(buffer_id))?;
|
||||
|
||||
unsafe { staging_buffer.flush(&device.raw)? };
|
||||
|
||||
self.queue_write_staging_buffer_impl(
|
||||
device,
|
||||
device_token,
|
||||
staging_buffer,
|
||||
buffer_id,
|
||||
buffer_offset,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn queue_validate_write_buffer<A: HalApi>(
|
||||
&self,
|
||||
_queue_id: id::QueueId,
|
||||
buffer_id: id::BufferId,
|
||||
buffer_offset: u64,
|
||||
buffer_size: u64,
|
||||
) -> Result<(), QueueWriteError> {
|
||||
let hub = A::hub(self);
|
||||
let root_token = &mut Token::root();
|
||||
|
||||
let (_, ref mut device_token) = hub.devices.read(root_token);
|
||||
|
||||
let buffer_guard = hub.buffers.read(device_token).0;
|
||||
let buffer = buffer_guard
|
||||
.get(buffer_id)
|
||||
.map_err(|_| TransferError::InvalidBuffer(buffer_id))?;
|
||||
|
||||
self.queue_validate_write_buffer_impl(buffer, buffer_id, buffer_offset, buffer_size)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue_validate_write_buffer_impl<A: HalApi>(
|
||||
&self,
|
||||
buffer: &super::resource::Buffer<A>,
|
||||
buffer_id: id::BufferId,
|
||||
buffer_offset: u64,
|
||||
buffer_size: u64,
|
||||
) -> Result<(), TransferError> {
|
||||
if !buffer.usage.contains(wgt::BufferUsages::COPY_DST) {
|
||||
return Err(TransferError::MissingCopyDstUsageFlag(
|
||||
Some(buffer_id),
|
||||
None,
|
||||
));
|
||||
}
|
||||
.map_err(DeviceError::from)?;
|
||||
if buffer_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedCopySize(buffer_size));
|
||||
}
|
||||
if buffer_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedBufferOffset(buffer_offset));
|
||||
}
|
||||
if buffer_offset + buffer_size > buffer.size {
|
||||
return Err(TransferError::BufferOverrun {
|
||||
start_offset: buffer_offset,
|
||||
end_offset: buffer_offset + buffer_size,
|
||||
buffer_size: buffer.size,
|
||||
side: CopySide::Destination,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue_write_staging_buffer_impl<A: HalApi>(
|
||||
&self,
|
||||
device: &mut super::Device<A>,
|
||||
device_token: &mut Token<super::Device<A>>,
|
||||
staging_buffer: StagingBuffer<A>,
|
||||
buffer_id: id::BufferId,
|
||||
buffer_offset: u64,
|
||||
) -> Result<(), QueueWriteError> {
|
||||
let hub = A::hub(self);
|
||||
|
||||
let buffer_guard = hub.buffers.read(device_token).0;
|
||||
|
||||
let mut trackers = device.trackers.lock();
|
||||
let (dst, transition) = trackers
|
||||
.buffers
|
||||
.set_single(&*buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
|
||||
.set_single(&buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
|
||||
.ok_or(TransferError::InvalidBuffer(buffer_id))?;
|
||||
let dst_raw = dst
|
||||
.raw
|
||||
.as_ref()
|
||||
.ok_or(TransferError::InvalidBuffer(buffer_id))?;
|
||||
if !dst.usage.contains(wgt::BufferUsages::COPY_DST) {
|
||||
return Err(TransferError::MissingCopyDstUsageFlag(Some(buffer_id), None).into());
|
||||
}
|
||||
|
||||
let src_buffer_size = staging_buffer.size;
|
||||
self.queue_validate_write_buffer_impl(dst, buffer_id, buffer_offset, src_buffer_size)?;
|
||||
|
||||
dst.life_guard.use_at(device.active_submission_index + 1);
|
||||
|
||||
if data_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedCopySize(data_size).into());
|
||||
}
|
||||
if buffer_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedBufferOffset(buffer_offset).into());
|
||||
}
|
||||
if buffer_offset + data_size > dst.size {
|
||||
return Err(TransferError::BufferOverrun {
|
||||
start_offset: buffer_offset,
|
||||
end_offset: buffer_offset + data_size,
|
||||
buffer_size: dst.size,
|
||||
side: CopySide::Destination,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let region = wgt::BufferSize::new(data.len() as u64).map(|size| hal::BufferCopy {
|
||||
let region = wgt::BufferSize::new(src_buffer_size).map(|size| hal::BufferCopy {
|
||||
src_offset: 0,
|
||||
dst_offset: buffer_offset,
|
||||
size,
|
||||
});
|
||||
let barriers = iter::once(hal::BufferBarrier {
|
||||
buffer: &stage.buffer,
|
||||
buffer: &staging_buffer.raw,
|
||||
usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC,
|
||||
})
|
||||
.chain(transition.map(|pending| pending.into_hal(dst)));
|
||||
let encoder = device.pending_writes.activate();
|
||||
unsafe {
|
||||
encoder.transition_buffers(barriers);
|
||||
encoder.copy_buffer_to_buffer(&stage.buffer, dst_raw, region.into_iter());
|
||||
encoder.copy_buffer_to_buffer(&staging_buffer.raw, dst_raw, region.into_iter());
|
||||
}
|
||||
|
||||
device.pending_writes.consume(stage);
|
||||
device.pending_writes.consume(staging_buffer);
|
||||
device.pending_writes.dst_buffers.insert(buffer_id);
|
||||
|
||||
// Ensure the overwritten bytes are marked as initialized so they don't need to be nulled prior to mapping or binding.
|
||||
{
|
||||
drop(buffer_guard);
|
||||
let (mut buffer_guard, _) = hub.buffers.write(&mut token);
|
||||
let mut buffer_guard = hub.buffers.write(device_token).0;
|
||||
|
||||
let dst = buffer_guard.get_mut(buffer_id).unwrap();
|
||||
dst.initialization_status
|
||||
.drain(buffer_offset..(buffer_offset + data_size));
|
||||
.drain(buffer_offset..(buffer_offset + src_buffer_size));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -469,7 +587,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let block_rows_in_copy =
|
||||
(size.depth_or_array_layers - 1) * block_rows_per_image + height_blocks;
|
||||
let stage_size = stage_bytes_per_row as u64 * block_rows_in_copy as u64;
|
||||
let stage = device.prepare_stage(stage_size)?;
|
||||
let (staging_buffer, staging_buffer_ptr) = device.prepare_staging_buffer(stage_size)?;
|
||||
|
||||
let dst = texture_guard.get_mut(destination.texture).unwrap();
|
||||
if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) {
|
||||
@ -538,30 +656,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
width_blocks * format_desc.block_size as u32
|
||||
};
|
||||
|
||||
let mapping = unsafe { device.raw.map_buffer(&stage.buffer, 0..stage_size) }
|
||||
.map_err(DeviceError::from)?;
|
||||
unsafe {
|
||||
if stage_bytes_per_row == bytes_per_row {
|
||||
profiling::scope!("copy aligned");
|
||||
// Fast path if the data is already being aligned optimally.
|
||||
if stage_bytes_per_row == bytes_per_row {
|
||||
profiling::scope!("copy aligned");
|
||||
// Fast path if the data is already being aligned optimally.
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
data.as_ptr().offset(data_layout.offset as isize),
|
||||
mapping.ptr.as_ptr(),
|
||||
staging_buffer_ptr,
|
||||
stage_size as usize,
|
||||
);
|
||||
} else {
|
||||
profiling::scope!("copy chunked");
|
||||
// Copy row by row into the optimal alignment.
|
||||
let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize;
|
||||
for layer in 0..size.depth_or_array_layers {
|
||||
let rows_offset = layer * block_rows_per_image;
|
||||
for row in 0..height_blocks {
|
||||
}
|
||||
} else {
|
||||
profiling::scope!("copy chunked");
|
||||
// Copy row by row into the optimal alignment.
|
||||
let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize;
|
||||
for layer in 0..size.depth_or_array_layers {
|
||||
let rows_offset = layer * block_rows_per_image;
|
||||
for row in 0..height_blocks {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
data.as_ptr().offset(
|
||||
data_layout.offset as isize
|
||||
+ (rows_offset + row) as isize * bytes_per_row as isize,
|
||||
),
|
||||
mapping.ptr.as_ptr().offset(
|
||||
staging_buffer_ptr.offset(
|
||||
(rows_offset + row) as isize * stage_bytes_per_row as isize,
|
||||
),
|
||||
copy_bytes_per_row,
|
||||
@ -570,17 +688,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
if !mapping.is_coherent {
|
||||
device
|
||||
.raw
|
||||
.flush_mapped_ranges(&stage.buffer, iter::once(0..stage_size));
|
||||
}
|
||||
device
|
||||
.raw
|
||||
.unmap_buffer(&stage.buffer)
|
||||
.map_err(DeviceError::from)?;
|
||||
}
|
||||
|
||||
unsafe { staging_buffer.flush(&device.raw) }?;
|
||||
|
||||
let regions = (0..array_layer_count).map(|rel_array_layer| {
|
||||
let mut texture_base = dst_base.clone();
|
||||
@ -598,7 +707,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
});
|
||||
let barrier = hal::BufferBarrier {
|
||||
buffer: &stage.buffer,
|
||||
buffer: &staging_buffer.raw,
|
||||
usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC,
|
||||
};
|
||||
|
||||
@ -611,10 +720,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
encoder
|
||||
.transition_textures(transition.map(|pending| pending.into_hal(dst)).into_iter());
|
||||
encoder.transition_buffers(iter::once(barrier));
|
||||
encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, regions);
|
||||
encoder.copy_buffer_to_texture(&staging_buffer.raw, dst_raw, regions);
|
||||
}
|
||||
|
||||
device.pending_writes.consume(stage);
|
||||
device.pending_writes.consume(staging_buffer);
|
||||
device
|
||||
.pending_writes
|
||||
.dst_textures
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
id,
|
||||
instance::{Adapter, HalSurface, Instance, Surface},
|
||||
pipeline::{ComputePipeline, RenderPipeline, ShaderModule},
|
||||
resource::{Buffer, QuerySet, Sampler, Texture, TextureClearMode, TextureView},
|
||||
resource::{Buffer, QuerySet, Sampler, StagingBuffer, Texture, TextureClearMode, TextureView},
|
||||
Epoch, Index,
|
||||
};
|
||||
|
||||
@ -351,6 +351,7 @@ impl<A: HalApi> Access<Buffer<A>> for CommandBuffer<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for ComputePipeline<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for RenderPipeline<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for QuerySet<A> {}
|
||||
impl<A: HalApi> Access<StagingBuffer<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<Texture<A>> for Root {}
|
||||
impl<A: HalApi> Access<Texture<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<Texture<A>> for Buffer<A> {}
|
||||
@ -452,6 +453,7 @@ pub trait GlobalIdentityHandlerFactory:
|
||||
+ IdentityHandlerFactory<id::ComputePipelineId>
|
||||
+ IdentityHandlerFactory<id::QuerySetId>
|
||||
+ IdentityHandlerFactory<id::BufferId>
|
||||
+ IdentityHandlerFactory<id::StagingBufferId>
|
||||
+ IdentityHandlerFactory<id::TextureId>
|
||||
+ IdentityHandlerFactory<id::TextureViewId>
|
||||
+ IdentityHandlerFactory<id::SamplerId>
|
||||
@ -639,6 +641,7 @@ pub struct Hub<A: HalApi, F: GlobalIdentityHandlerFactory> {
|
||||
pub compute_pipelines: Registry<ComputePipeline<A>, id::ComputePipelineId, F>,
|
||||
pub query_sets: Registry<QuerySet<A>, id::QuerySetId, F>,
|
||||
pub buffers: Registry<Buffer<A>, id::BufferId, F>,
|
||||
pub staging_buffers: Registry<StagingBuffer<A>, id::StagingBufferId, F>,
|
||||
pub textures: Registry<Texture<A>, id::TextureId, F>,
|
||||
pub texture_views: Registry<TextureView<A>, id::TextureViewId, F>,
|
||||
pub samplers: Registry<Sampler<A>, id::SamplerId, F>,
|
||||
@ -659,6 +662,7 @@ impl<A: HalApi, F: GlobalIdentityHandlerFactory> Hub<A, F> {
|
||||
compute_pipelines: Registry::new(A::VARIANT, factory),
|
||||
query_sets: Registry::new(A::VARIANT, factory),
|
||||
buffers: Registry::new(A::VARIANT, factory),
|
||||
staging_buffers: Registry::new(A::VARIANT, factory),
|
||||
textures: Registry::new(A::VARIANT, factory),
|
||||
texture_views: Registry::new(A::VARIANT, factory),
|
||||
samplers: Registry::new(A::VARIANT, factory),
|
||||
|
@ -196,6 +196,7 @@ pub type DeviceId = Id<crate::device::Device<Dummy>>;
|
||||
pub type QueueId = DeviceId;
|
||||
// Resource
|
||||
pub type BufferId = Id<crate::resource::Buffer<Dummy>>;
|
||||
pub type StagingBufferId = Id<crate::resource::StagingBuffer<Dummy>>;
|
||||
pub type TextureViewId = Id<crate::resource::TextureView<Dummy>>;
|
||||
pub type TextureId = Id<crate::resource::Texture<Dummy>>;
|
||||
pub type SamplerId = Id<crate::resource::Sampler<Dummy>>;
|
||||
|
@ -186,6 +186,24 @@ impl<A: hal::Api> Resource for Buffer<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StagingBuffer<A: hal::Api> {
|
||||
pub(crate) raw: A::Buffer,
|
||||
pub(crate) size: wgt::BufferAddress,
|
||||
pub(crate) is_coherent: bool,
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Resource for StagingBuffer<A> {
|
||||
const TYPE: &'static str = "StagingBuffer";
|
||||
|
||||
fn life_guard(&self) -> &LifeGuard {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn label(&self) -> &str {
|
||||
"<StagingBuffer>"
|
||||
}
|
||||
}
|
||||
|
||||
pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -2192,6 +2192,58 @@ impl crate::Context for Context {
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_validate_write_buffer(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
buffer: &Self::BufferId,
|
||||
offset: wgt::BufferAddress,
|
||||
size: wgt::BufferSize,
|
||||
) {
|
||||
let global = &self.0;
|
||||
match wgc::gfx_select!(
|
||||
*queue => global.queue_validate_write_buffer(*queue, buffer.id, offset, size.get())
|
||||
) {
|
||||
Ok(()) => (),
|
||||
Err(err) => self.handle_error_fatal(err, "Queue::write_buffer_with"),
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_create_staging_buffer(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
size: wgt::BufferSize,
|
||||
) -> QueueWriteBuffer {
|
||||
let global = &self.0;
|
||||
match wgc::gfx_select!(
|
||||
*queue => global.queue_create_staging_buffer(*queue, size, PhantomData)
|
||||
) {
|
||||
Ok((buffer_id, ptr)) => QueueWriteBuffer {
|
||||
buffer_id,
|
||||
mapping: BufferMappedRange {
|
||||
ptr,
|
||||
size: size.get() as usize,
|
||||
},
|
||||
},
|
||||
Err(err) => self.handle_error_fatal(err, "Queue::write_buffer_with"),
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_write_staging_buffer(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
buffer: &Self::BufferId,
|
||||
offset: wgt::BufferAddress,
|
||||
staging_buffer: &QueueWriteBuffer,
|
||||
) {
|
||||
let global = &self.0;
|
||||
match wgc::gfx_select!(
|
||||
*queue => global.queue_write_staging_buffer(*queue, buffer.id, offset, staging_buffer.buffer_id)
|
||||
) {
|
||||
Ok(()) => (),
|
||||
Err(err) => self.handle_error_fatal(err, "Queue::write_buffer_with"),
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_write_texture(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
@ -2324,6 +2376,27 @@ fn default_error_handler(err: crate::Error) {
|
||||
panic!("wgpu error: {}\n", err);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QueueWriteBuffer {
|
||||
buffer_id: wgc::id::StagingBufferId,
|
||||
mapping: BufferMappedRange,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for QueueWriteBuffer {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
panic!("QueueWriteBuffer is write-only!");
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for QueueWriteBuffer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
use crate::BufferMappedRangeSlice;
|
||||
self.mapping.slice_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BufferMappedRange {
|
||||
ptr: *mut u8,
|
||||
|
@ -1,9 +1,9 @@
|
||||
#[cfg(all(target_arch = "wasm32", not(feature = "webgl")))]
|
||||
mod web;
|
||||
#[cfg(all(target_arch = "wasm32", not(feature = "webgl")))]
|
||||
pub(crate) use web::{BufferMappedRange, Context};
|
||||
pub(crate) use web::{BufferMappedRange, Context, QueueWriteBuffer};
|
||||
|
||||
#[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))]
|
||||
mod direct;
|
||||
#[cfg(any(not(target_arch = "wasm32"), feature = "webgl"))]
|
||||
pub(crate) use direct::{BufferMappedRange, Context};
|
||||
pub(crate) use direct::{BufferMappedRange, Context, QueueWriteBuffer};
|
||||
|
@ -2197,6 +2197,34 @@ impl crate::Context for Context {
|
||||
);
|
||||
}
|
||||
|
||||
fn queue_validate_write_buffer(
|
||||
&self,
|
||||
_queue: &Self::QueueId,
|
||||
_buffer: &Self::BufferId,
|
||||
_offset: wgt::BufferAddress,
|
||||
_size: wgt::BufferSize,
|
||||
) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn queue_create_staging_buffer(
|
||||
&self,
|
||||
_queue: &Self::QueueId,
|
||||
size: wgt::BufferSize,
|
||||
) -> QueueWriteBuffer {
|
||||
QueueWriteBuffer(vec![0; size.get() as usize].into_boxed_slice())
|
||||
}
|
||||
|
||||
fn queue_write_staging_buffer(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
buffer: &Self::BufferId,
|
||||
offset: wgt::BufferAddress,
|
||||
staging_buffer: &QueueWriteBuffer,
|
||||
) {
|
||||
self.queue_write_buffer(queue, buffer, offset, staging_buffer)
|
||||
}
|
||||
|
||||
fn queue_write_texture(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
@ -2262,6 +2290,23 @@ impl crate::Context for Context {
|
||||
|
||||
pub(crate) type SurfaceOutputDetail = ();
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QueueWriteBuffer(Box<[u8]>);
|
||||
|
||||
impl std::ops::Deref for QueueWriteBuffer {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
panic!("QueueWriteBuffer is write-only!");
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for QueueWriteBuffer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BufferMappedRange {
|
||||
actual_mapping: js_sys::Uint8Array,
|
||||
|
@ -43,7 +43,7 @@ pub use wgt::{
|
||||
QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT,
|
||||
};
|
||||
|
||||
use backend::{BufferMappedRange, Context as C};
|
||||
use backend::{BufferMappedRange, Context as C, QueueWriteBuffer};
|
||||
|
||||
/// Filter for error scopes.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||
@ -482,6 +482,25 @@ trait Context: Debug + Send + Sized + Sync {
|
||||
offset: BufferAddress,
|
||||
data: &[u8],
|
||||
);
|
||||
fn queue_validate_write_buffer(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
buffer: &Self::BufferId,
|
||||
offset: wgt::BufferAddress,
|
||||
size: wgt::BufferSize,
|
||||
);
|
||||
fn queue_create_staging_buffer(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
size: BufferSize,
|
||||
) -> QueueWriteBuffer;
|
||||
fn queue_write_staging_buffer(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
buffer: &Self::BufferId,
|
||||
offset: BufferAddress,
|
||||
staging_buffer: &QueueWriteBuffer,
|
||||
);
|
||||
fn queue_write_texture(
|
||||
&self,
|
||||
queue: &Self::QueueId,
|
||||
@ -3376,6 +3395,40 @@ impl<'a> RenderBundleEncoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A write-only view into a staging buffer
|
||||
pub struct QueueWriteBufferView<'a> {
|
||||
queue: &'a Queue,
|
||||
buffer: &'a Buffer,
|
||||
offset: BufferAddress,
|
||||
inner: QueueWriteBuffer,
|
||||
}
|
||||
|
||||
impl<'a> std::ops::Deref for QueueWriteBufferView<'a> {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
panic!("QueueWriteBufferView is write-only!");
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::DerefMut for QueueWriteBufferView<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for QueueWriteBufferView<'a> {
|
||||
fn drop(&mut self) {
|
||||
Context::queue_write_staging_buffer(
|
||||
&*self.queue.context,
|
||||
&self.queue.id,
|
||||
&self.buffer.id,
|
||||
self.offset,
|
||||
&self.inner,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
/// Schedule a data write into `buffer` starting at `offset`.
|
||||
///
|
||||
@ -3388,6 +3441,32 @@ impl Queue {
|
||||
Context::queue_write_buffer(&*self.context, &self.id, &buffer.id, offset, data)
|
||||
}
|
||||
|
||||
/// Schedule a data write into `buffer` starting at `offset` via the returned [QueueWriteBufferView].
|
||||
///
|
||||
/// The returned value can be dereferenced to a `&mut [u8]`; dereferencing it to a `&[u8]` panics!
|
||||
///
|
||||
/// This method is intended to have low performance costs.
|
||||
/// As such, the write is not immediately submitted, and instead enqueued
|
||||
/// internally to happen at the start of the next `submit()` call.
|
||||
///
|
||||
/// This method fails if `size` is greater than the size of `buffer` starting at `offset`.
|
||||
#[must_use]
|
||||
pub fn write_buffer_with<'a>(
|
||||
&'a self,
|
||||
buffer: &'a Buffer,
|
||||
offset: BufferAddress,
|
||||
size: BufferSize,
|
||||
) -> QueueWriteBufferView<'a> {
|
||||
Context::queue_validate_write_buffer(&*self.context, &self.id, &buffer.id, offset, size);
|
||||
let staging_buffer = Context::queue_create_staging_buffer(&*self.context, &self.id, size);
|
||||
QueueWriteBufferView {
|
||||
queue: self,
|
||||
buffer,
|
||||
offset,
|
||||
inner: staging_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule a data write into `texture`.
|
||||
///
|
||||
/// This method is intended to have low performance costs.
|
||||
|
Loading…
Reference in New Issue
Block a user