buffers are now zero initialized for mappings

queue_write_buffer marks as initialized
This commit is contained in:
Andreas Reich
2021-01-18 21:49:24 +01:00
parent 1668bb2451
commit 875cfb5961
5 changed files with 81 additions and 3 deletions

1
Cargo.lock generated
View File

@@ -1807,6 +1807,7 @@ dependencies = [
"loom",
"naga",
"parking_lot",
"range-alloc",
"raw-window-handle",
"ron",
"serde",

View File

@@ -38,6 +38,7 @@ gpu-descriptor = { version = "0.1", features = ["tracing"] }
hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "2fd74dbe1562a3eef05b11dcd300c1c9c9bc12a8" }
gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "2fd74dbe1562a3eef05b11dcd300c1c9c9bc12a8" }
range-alloc = {git = "https://github.com/gfx-rs/gfx", rev = "2fd74dbe1562a3eef05b11dcd300c1c9c9bc12a8"}
[target.'cfg(all(not(target_arch = "wasm32"), all(unix, not(target_os = "ios"), not(target_os = "macos"))))'.dependencies]
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "2fd74dbe1562a3eef05b11dcd300c1c9c9bc12a8", features = ["naga"] }

View File

@@ -196,6 +196,30 @@ fn map_buffer<B: hal::Backend>(
}),
_ => None,
};
let writes_are_synced = buffer.sync_mapped_writes.is_some() || block.is_coherent();
// Zero out uninitialized parts of the mapping. (Spec dictates all resources behave as if they were initialized with zero)
// Note that going through a `fill_buffer` command may be faster but we don't have the chance of interacting with the queue
// between map intent (map_buffer_async or immediately) and this call.
let uninitialized_ranges: Vec<Range<BufferAddress>> =
buffer.uninitialized_ranges_in_range(Range {
start: offset,
end: offset + size,
});
for range in uninitialized_ranges {
unsafe {
ptr::write_bytes(
ptr.as_ptr().offset(range.start as isize),
0,
(range.end - range.start) as usize,
)
};
// (technically the buffer is only initialized when we're done unmapping but we know it can't be used otherwise meanwhile)
if writes_are_synced {
buffer.mark_initialized(range);
}
}
Ok(ptr)
}
@@ -534,6 +558,13 @@ impl<B: GfxBackend> Device<B> {
.allocate(&self.raw, requirements, mem_usage)?;
block.bind_buffer(&self.raw, &mut buffer)?;
let mut uninitialized_ranges = range_alloc::RangeAllocator::new(Range {
start: 0,
end: desc.size,
});
// Mark buffer as uninitialized
let _ = uninitialized_ranges.allocate_range(desc.size);
Ok(resource::Buffer {
raw: Some((buffer, block)),
device_id: Stored {
@@ -542,7 +573,7 @@ impl<B: GfxBackend> Device<B> {
},
usage: desc.usage,
size: desc.size,
full_range: (),
uninitialized_ranges,
sync_mapped_writes: None,
map_state: resource::BufferMapState::Idle,
life_guard: LifeGuard::new(desc.label.borrow_or_default()),

View File

@@ -191,7 +191,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let device = device_guard
.get_mut(queue_id)
.map_err(|_| DeviceError::Invalid)?;
let (buffer_guard, _) = hub.buffers.read(&mut token);
let (mut buffer_guard, _) = hub.buffers.write(&mut token);
#[cfg(feature = "trace")]
if let Some(ref trace) = device.trace {
@@ -271,6 +271,21 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device.pending_writes.consume(stage);
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.
{
let dst = buffer_guard.get_mut(buffer_id).unwrap();
for range in dst
.uninitialized_ranges_in_range::<Vec<std::ops::Range<wgt::BufferAddress>>>(
std::ops::Range {
start: buffer_offset,
end: buffer_offset + data_size,
},
)
{
dst.mark_initialized(range);
}
}
Ok(())
}

View File

@@ -160,12 +160,42 @@ pub struct Buffer<B: hal::Backend> {
pub(crate) device_id: Stored<DeviceId>,
pub(crate) usage: wgt::BufferUsage,
pub(crate) size: wgt::BufferAddress,
pub(crate) full_range: (),
// An allocated range in this allocator means that the range in question is NOT yet initialized.
pub(crate) uninitialized_ranges: range_alloc::RangeAllocator<wgt::BufferAddress>,
pub(crate) sync_mapped_writes: Option<hal::memory::Segment>,
pub(crate) life_guard: LifeGuard,
pub(crate) map_state: BufferMapState<B>,
}
impl<B: hal::Backend> Buffer<B> {
pub(crate) fn uninitialized_ranges_in_range<
'a,
R: std::iter::FromIterator<Range<wgt::BufferAddress>>,
>(
&self,
range: Range<wgt::BufferAddress>,
) -> R {
self.uninitialized_ranges
.allocated_ranges()
.filter_map(|r: Range<wgt::BufferAddress>| {
if r.end > range.start && r.start < range.end {
Some(Range {
start: range.start.max(r.start),
end: range.end.min(r.end),
})
} else {
None
}
})
.collect::<R>()
}
// Range must be continuous previously uninitialized section.
pub(crate) fn mark_initialized(&mut self, range: Range<wgt::BufferAddress>) {
self.uninitialized_ranges.free_range(range);
}
}
#[derive(Clone, Debug, Error)]
pub enum CreateBufferError {
#[error(transparent)]