Memory init tracker refactor (#1839)

* split out init_tracker module
with explicit buffer init tracking types

* Added shortcut methods for checked init action handling
Fixed a few unchecked sites in the process

* fix clippy warnings
This commit is contained in:
Wumpf
2021-08-23 04:46:13 +02:00
committed by GitHub
parent 7f395b39af
commit 9e1384dbb0
13 changed files with 220 additions and 245 deletions

View File

@@ -3,7 +3,7 @@ use crate::{
error::{ErrorFormatter, PrettyError},
hub::Resource,
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid},
memory_init_tracker::MemoryInitTrackerAction,
init_tracker::BufferInitTrackerAction,
track::{TrackerSet, UsageConflict, DUMMY_SELECTOR},
validation::{MissingBufferUsageError, MissingTextureUsageError},
FastHashMap, Label, LifeGuard, MultiRefCount, Stored,
@@ -671,7 +671,7 @@ pub struct BindGroup<A: hal::Api> {
pub(crate) layout_id: Valid<BindGroupLayoutId>,
pub(crate) life_guard: LifeGuard,
pub(crate) used: TrackerSet,
pub(crate) used_buffer_ranges: Vec<MemoryInitTrackerAction<BufferId>>,
pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
}

View File

@@ -46,7 +46,7 @@ use crate::{
error::{ErrorFormatter, PrettyError},
hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Resource, Storage, Token},
id,
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
init_tracker::{BufferInitTrackerAction, MemoryInitKind},
pipeline::PipelineFlags,
track::{TrackerSet, UsageConflict},
validation::check_buffer_usage,
@@ -287,11 +287,11 @@ impl RenderBundleEncoder {
Some(s) => offset + s.get(),
None => buffer.size,
};
buffer_memory_init_actions.push(MemoryInitTrackerAction {
id: buffer_id,
range: offset..end,
kind: MemoryInitKind::NeedsInitializedMemory,
});
buffer_memory_init_actions.extend(buffer.initialization_status.create_action(
buffer_id,
offset..end,
MemoryInitKind::NeedsInitializedMemory,
));
state.index.set_format(index_format);
state.index.set_buffer(buffer_id, offset..end);
}
@@ -314,11 +314,11 @@ impl RenderBundleEncoder {
Some(s) => offset + s.get(),
None => buffer.size,
};
buffer_memory_init_actions.push(MemoryInitTrackerAction {
id: buffer_id,
range: offset..end,
kind: MemoryInitKind::NeedsInitializedMemory,
});
buffer_memory_init_actions.extend(buffer.initialization_status.create_action(
buffer_id,
offset..end,
MemoryInitKind::NeedsInitializedMemory,
));
state.vertex[slot as usize].set_buffer(buffer_id, offset..end);
}
RenderCommand::SetPushConstant {
@@ -435,18 +435,11 @@ impl RenderBundleEncoder {
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
.map_pass_err(scope)?;
buffer_memory_init_actions.extend(
buffer
.initialization_status
.check(
offset..(offset + mem::size_of::<wgt::DrawIndirectArgs>() as u64),
)
.map(|range| MemoryInitTrackerAction {
id: buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
);
buffer_memory_init_actions.extend(buffer.initialization_status.create_action(
buffer_id,
offset..(offset + mem::size_of::<wgt::DrawIndirectArgs>() as u64),
MemoryInitKind::NeedsInitializedMemory,
));
commands.extend(state.flush_vertices());
commands.extend(state.flush_binds());
@@ -476,18 +469,11 @@ impl RenderBundleEncoder {
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
.map_pass_err(scope)?;
buffer_memory_init_actions.extend(
buffer
.initialization_status
.check(
offset..(offset + mem::size_of::<wgt::DrawIndirectArgs>() as u64),
)
.map(|range| MemoryInitTrackerAction {
id: buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
);
buffer_memory_init_actions.extend(buffer.initialization_status.create_action(
buffer_id,
offset..(offset + mem::size_of::<wgt::DrawIndirectArgs>() as u64),
MemoryInitKind::NeedsInitializedMemory,
));
commands.extend(state.index.flush());
commands.extend(state.flush_vertices());
@@ -586,7 +572,7 @@ pub struct RenderBundle {
pub(super) is_ds_read_only: bool,
pub(crate) device_id: Stored<id::DeviceId>,
pub(crate) used: TrackerSet,
pub(super) buffer_memory_init_actions: Vec<MemoryInitTrackerAction<id::BufferId>>,
pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
pub(super) context: RenderPassContext,
pub(crate) life_guard: LifeGuard,
}

View File

@@ -6,7 +6,7 @@ use crate::{
command::CommandBuffer,
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Token},
id::{BufferId, CommandEncoderId, TextureId},
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
init_tracker::MemoryInitKind,
track::TextureSelector,
};
@@ -127,17 +127,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
// Mark dest as initialized.
cmd_buf.buffer_memory_init_actions.extend(
dst_buffer
.initialization_status
.check(offset..end)
.map(|range| MemoryInitTrackerAction {
id: dst,
range,
kind: MemoryInitKind::ImplicitlyInitialized,
}),
);
cmd_buf
.buffer_memory_init_actions
.extend(dst_buffer.initialization_status.create_action(
dst,
offset..end,
MemoryInitKind::ImplicitlyInitialized,
));
// actual hal barrier & operation
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
let cmd_buf_raw = cmd_buf.encoder.open();

View File

@@ -9,7 +9,7 @@ use crate::{
error::{ErrorFormatter, PrettyError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
init_tracker::MemoryInitKind,
resource::{Buffer, Texture},
track::{StatefulTrackerSubset, TrackerSet, UsageConflict, UseExtendError},
validation::{check_buffer_usage, MissingBufferUsageError},
@@ -366,19 +366,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
cmd_buf.buffer_memory_init_actions.extend(
bind_group.used_buffer_ranges.iter().filter_map(
|action| match buffer_guard.get(action.id) {
Ok(buffer) => buffer
.initialization_status
.check(action.range.clone())
.map(|range| MemoryInitTrackerAction {
id: action.id,
range,
kind: action.kind,
}),
Ok(buffer) => buffer.initialization_status.check_action(action),
Err(_) => None,
},
),
);
let pipeline_layout_id = state.binder.pipeline_layout_id;
let entries = state.binder.assign_group(
index as usize,
@@ -565,14 +557,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let stride = 3 * 4; // 3 integers, x/y/z group size
cmd_buf.buffer_memory_init_actions.extend(
indirect_buffer
.initialization_status
.check(offset..(offset + stride))
.map(|range| MemoryInitTrackerAction {
id: buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
indirect_buffer.initialization_status.create_action(
buffer_id,
offset..(offset + stride),
MemoryInitKind::NeedsInitializedMemory,
),
);
state

View File

@@ -22,7 +22,7 @@ use crate::FastHashMap;
use crate::{
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
init_tracker::{BufferInitTrackerAction, MemoryInitKind},
resource::{Buffer, Texture},
track::{BufferState, ResourceTracker, TextureState, TrackerSet},
Label, Stored,
@@ -71,7 +71,7 @@ pub struct BakedCommands<A: hal::Api> {
pub(crate) encoder: A::CommandEncoder,
pub(crate) list: Vec<A::CommandBuffer>,
pub(crate) trackers: TrackerSet,
buffer_memory_init_actions: Vec<MemoryInitTrackerAction<id::BufferId>>,
buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
}
pub(crate) struct DestroyedBufferError(pub id::BufferId);
@@ -158,7 +158,7 @@ pub struct CommandBuffer<A: hal::Api> {
status: CommandEncoderStatus,
pub(crate) device_id: Stored<id::DeviceId>,
pub(crate) trackers: TrackerSet,
buffer_memory_init_actions: Vec<MemoryInitTrackerAction<id::BufferId>>,
buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
limits: wgt::Limits,
support_fill_buffer_texture: bool,
#[cfg(feature = "trace")]

View File

@@ -6,7 +6,7 @@ use crate::{
command::{CommandBuffer, CommandEncoderError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id::{self, Id, TypedId},
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
init_tracker::MemoryInitKind,
resource::QuerySet,
track::UseExtendError,
Epoch, FastHashMap, Index,
@@ -392,16 +392,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.into());
}
cmd_buf.buffer_memory_init_actions.extend(
dst_buffer
.initialization_status
.check(buffer_start_offset..buffer_end_offset)
.map(|range| MemoryInitTrackerAction {
id: destination,
range,
kind: MemoryInitKind::ImplicitlyInitialized,
}),
);
cmd_buf
.buffer_memory_init_actions
.extend(dst_buffer.initialization_status.create_action(
destination,
buffer_start_offset..buffer_end_offset,
MemoryInitKind::ImplicitlyInitialized,
));
unsafe {
raw_encoder.transition_buffers(dst_barrier);

View File

@@ -13,7 +13,7 @@ use crate::{
error::{ErrorFormatter, PrettyError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
init_tracker::MemoryInitKind,
pipeline::PipelineFlags,
resource::{Texture, TextureView},
track::{StatefulTrackerSubset, TextureSelector, UsageConflict},
@@ -889,14 +889,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
cmd_buf.buffer_memory_init_actions.extend(
bind_group.used_buffer_ranges.iter().filter_map(|action| {
match buffer_guard.get(action.id) {
Ok(buffer) => buffer
.initialization_status
.check(action.range.clone())
.map(|range| MemoryInitTrackerAction {
id: action.id,
range,
kind: action.kind,
}),
Ok(buffer) => buffer.initialization_status.check_action(action),
Err(_) => None,
}
}),
@@ -1067,14 +1060,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
state.index.update_limit();
cmd_buf.buffer_memory_init_actions.extend(
buffer
.initialization_status
.check(offset..end)
.map(|range| MemoryInitTrackerAction {
id: buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
buffer.initialization_status.create_action(
buffer_id,
offset..end,
MemoryInitKind::NeedsInitializedMemory,
),
);
let bb = hal::BufferBinding {
@@ -1122,14 +1112,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
vertex_state.bound = true;
cmd_buf.buffer_memory_init_actions.extend(
buffer
.initialization_status
.check(offset..(offset + vertex_state.total_size))
.map(|range| MemoryInitTrackerAction {
id: buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
buffer.initialization_status.create_action(
buffer_id,
offset..(offset + vertex_state.total_size),
MemoryInitKind::NeedsInitializedMemory,
),
);
let bb = hal::BufferBinding {
@@ -1382,14 +1369,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
cmd_buf.buffer_memory_init_actions.extend(
indirect_buffer
.initialization_status
.check(offset..end_offset)
.map(|range| MemoryInitTrackerAction {
id: buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
indirect_buffer.initialization_status.create_action(
buffer_id,
offset..end_offset,
MemoryInitKind::NeedsInitializedMemory,
),
);
match indexed {
@@ -1472,14 +1456,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_pass_err(scope);
}
cmd_buf.buffer_memory_init_actions.extend(
indirect_buffer
.initialization_status
.check(offset..end_offset)
.map(|range| MemoryInitTrackerAction {
id: buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
indirect_buffer.initialization_status.create_action(
buffer_id,
offset..end_offset,
MemoryInitKind::NeedsInitializedMemory,
),
);
let begin_count_offset = count_buffer_offset;
@@ -1493,14 +1474,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_pass_err(scope);
}
cmd_buf.buffer_memory_init_actions.extend(
count_buffer
.initialization_status
.check(count_buffer_offset..end_count_offset)
.map(|range| MemoryInitTrackerAction {
id: count_buffer_id,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
count_buffer.initialization_status.create_action(
count_buffer_id,
count_buffer_offset..end_count_offset,
MemoryInitKind::NeedsInitializedMemory,
),
);
match indexed {
@@ -1642,14 +1620,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.buffer_memory_init_actions
.iter()
.filter_map(|action| match buffer_guard.get(action.id) {
Ok(buffer) => buffer
.initialization_status
.check(action.range.clone())
.map(|range| MemoryInitTrackerAction {
id: action.id,
range,
kind: action.kind,
}),
Ok(buffer) => buffer.initialization_status.check_action(action),
Err(_) => None,
}),
);

View File

@@ -6,7 +6,7 @@ use crate::{
error::{ErrorFormatter, PrettyError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id::{BufferId, CommandEncoderId, TextureId},
memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction},
init_tracker::MemoryInitKind,
resource::{Texture, TextureErrorDimension},
track::TextureSelector,
};
@@ -462,26 +462,20 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
// Make sure source is initialized memory and mark dest as initialized.
cmd_buf.buffer_memory_init_actions.extend(
dst_buffer
.initialization_status
.check(destination_offset..(destination_offset + size))
.map(|range| MemoryInitTrackerAction {
id: destination,
range,
kind: MemoryInitKind::ImplicitlyInitialized,
}),
);
cmd_buf.buffer_memory_init_actions.extend(
src_buffer
.initialization_status
.check(source_offset..(source_offset + size))
.map(|range| MemoryInitTrackerAction {
id: source,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
);
cmd_buf
.buffer_memory_init_actions
.extend(dst_buffer.initialization_status.create_action(
destination,
destination_offset..(destination_offset + size),
MemoryInitKind::ImplicitlyInitialized,
));
cmd_buf
.buffer_memory_init_actions
.extend(src_buffer.initialization_status.create_action(
source,
source_offset..(source_offset + size),
MemoryInitKind::NeedsInitializedMemory,
));
let region = hal::BufferCopy {
src_offset: source_offset,
@@ -582,16 +576,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
true,
)?;
cmd_buf.buffer_memory_init_actions.extend(
src_buffer
.initialization_status
.check(source.layout.offset..(source.layout.offset + required_buffer_bytes_in_copy))
.map(|range| MemoryInitTrackerAction {
id: source.buffer,
range,
kind: MemoryInitKind::NeedsInitializedMemory,
}),
);
cmd_buf
.buffer_memory_init_actions
.extend(src_buffer.initialization_status.create_action(
source.buffer,
source.layout.offset..(source.layout.offset + required_buffer_bytes_in_copy),
MemoryInitKind::NeedsInitializedMemory,
));
if !conv::is_valid_copy_dst_texture_format(dst_texture.desc.format) {
return Err(
@@ -712,19 +703,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
);
}
cmd_buf.buffer_memory_init_actions.extend(
dst_buffer
.initialization_status
.check(
destination.layout.offset
..(destination.layout.offset + required_buffer_bytes_in_copy),
)
.map(|range| MemoryInitTrackerAction {
id: destination.buffer,
range,
kind: MemoryInitKind::ImplicitlyInitialized,
}),
);
cmd_buf
.buffer_memory_init_actions
.extend(dst_buffer.initialization_status.create_action(
destination.buffer,
destination.layout.offset
..(destination.layout.offset + required_buffer_bytes_in_copy),
MemoryInitKind::ImplicitlyInitialized,
));
let regions = (0..array_layer_count).map(|rel_array_layer| {
let mut texture_base = src_base.clone();

View File

@@ -2,9 +2,9 @@ use crate::{
binding_model, command, conv,
device::life::WaitIdleError,
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Hub, Input, InvalidId, Storage, Token},
id, instance,
memory_init_tracker::{MemoryInitKind, MemoryInitTracker, MemoryInitTrackerAction},
pipeline, present, resource,
id,
init_tracker::{BufferInitTracker, BufferInitTrackerAction, MemoryInitKind},
instance, pipeline, present, resource,
track::{BufferState, TextureSelector, TextureState, TrackerSet, UsageConflict},
validation::{self, check_buffer_usage, check_texture_usage},
FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, Stored, SubmissionIndex,
@@ -527,7 +527,7 @@ impl<A: HalApi> Device<A> {
},
usage: desc.usage,
size: desc.size,
initialization_status: MemoryInitTracker::new(desc.size),
initialization_status: BufferInitTracker::new(desc.size),
sync_mapped_writes: None,
map_state: resource::BufferMapState::Idle,
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
@@ -1201,7 +1201,7 @@ impl<A: HalApi> Device<A> {
bb: &binding_model::BufferBinding,
binding: u32,
decl: &wgt::BindGroupLayoutEntry,
used_buffer_ranges: &mut Vec<MemoryInitTrackerAction<id::BufferId>>,
used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,
dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
used: &mut TrackerSet,
storage: &'a Storage<resource::Buffer<A>, id::BufferId>,
@@ -1297,11 +1297,11 @@ impl<A: HalApi> Device<A> {
return Err(Error::BindingZeroSize(bb.buffer_id));
}
used_buffer_ranges.push(MemoryInitTrackerAction {
id: bb.buffer_id,
range: bb.offset..(bb.offset + bind_size),
kind: MemoryInitKind::NeedsInitializedMemory,
});
used_buffer_ranges.extend(buffer.initialization_status.create_action(
bb.buffer_id,
bb.offset..(bb.offset + bind_size),
MemoryInitKind::NeedsInitializedMemory,
));
Ok(hal::BufferBinding {
buffer: raw_buffer,

View File

@@ -0,0 +1,33 @@
use super::{InitTracker, MemoryInitKind};
use crate::id::BufferId;
use std::ops::Range;
#[derive(Debug, Clone)]
pub(crate) struct BufferInitTrackerAction {
pub id: BufferId,
pub range: Range<wgt::BufferAddress>,
pub kind: MemoryInitKind,
}
pub(crate) type BufferInitTracker = InitTracker<wgt::BufferAddress>;
impl BufferInitTracker {
/// Checks if an action has/requires any effect on the initialization status and shrinks its range if possible.
pub(crate) fn check_action(
&self,
action: &BufferInitTrackerAction,
) -> Option<BufferInitTrackerAction> {
self.create_action(action.id, action.range.clone(), action.kind)
}
/// Creates an action if it would have any effect on the initialization status and shrinks the range if possible.
pub(crate) fn create_action(
&self,
id: BufferId,
query_range: Range<wgt::BufferAddress>,
kind: MemoryInitKind,
) -> Option<BufferInitTrackerAction> {
self.check(query_range)
.map(|range| BufferInitTrackerAction { id, range, kind })
}
}

View File

@@ -1,4 +1,26 @@
use std::ops::Range;
// WebGPU specification requires all texture & buffer memory to be zero initialized on first read.
// To avoid unnecessary inits, we track the initialization status of every resource and perform inits lazily.
//
// The granularity is different for buffers and textures:
// * Buffer: Byte granularity to support usecases with large, partially bound buffers well.
// * Texture: Mip-level per layer. I.e. a 2D surface is either completely initialized or not, subrects are not tracked.
//
// Every use of a buffer/texture generates a InitTrackerAction which are recorded and later resolved at queue submit by merging them with the current state and each other in execution order.
// It is important to note that from the point of view of the memory init system there are two kind of writes:
// * Full writes:
// Any kind of memcpy operation. These cause a `MemoryInitKind.ImplicitlyInitialized` action.
// * (Potentially) partial writes:
// E.g. write use in a Shader. The system is not able to determine if a resource is fully initialized afterwards but is no longer allowed to perform any clears,
// therefore this leads to a `MemoryInitKind.ImplicitlyInitialized` action, exactly like a read would.
use smallvec::SmallVec;
use std::{iter, ops::Range};
mod buffer;
//mod texture;
pub(crate) use buffer::{BufferInitTracker, BufferInitTrackerAction};
//pub(crate) use texture::{TextureInitRange, TextureInitTracker, TextureInitTrackerAction};
#[derive(Debug, Clone, Copy)]
pub(crate) enum MemoryInitKind {
@@ -6,31 +28,36 @@ pub(crate) enum MemoryInitKind {
ImplicitlyInitialized,
// The memory range is going to be read, therefore needs to ensure prior initialization.
NeedsInitializedMemory,
// The memory going to be discarded and regarded therefore regarded uninitialized.
// TODO: This is tricky to implement: Discards needs to be resolved within AND between command buffers!
// Being able to do this would be quite nice because then we could mark any resource uninitialized at any point in time.
// Practically speaking however, discard can only ever happen for single rendertarget surfaces.
// Considering this, this could potentially be implemented differently since we don't really need to care about ranges of memory.
//DiscardMemory,
}
#[derive(Debug, Clone)]
pub(crate) struct MemoryInitTrackerAction<ResourceId> {
pub(crate) id: ResourceId,
pub(crate) range: Range<wgt::BufferAddress>,
pub(crate) kind: MemoryInitKind,
}
// Most of the time a resource is either fully uninitialized (one element) or initialized (zero elements).
type UninitializedRangeVec<Idx> = SmallVec<[Range<Idx>; 1]>;
/// Tracks initialization status of a linear range from 0..size
#[derive(Debug)]
pub(crate) struct MemoryInitTracker {
#[derive(Debug, Clone)]
pub(crate) struct InitTracker<Idx: Ord + Copy + Default> {
// Ordered, non overlapping list of all uninitialized ranges.
uninitialized_ranges: Vec<Range<wgt::BufferAddress>>,
uninitialized_ranges: UninitializedRangeVec<Idx>,
}
pub(crate) struct MemoryInitTrackerDrain<'a> {
uninitialized_ranges: &'a mut Vec<Range<wgt::BufferAddress>>,
drain_range: Range<wgt::BufferAddress>,
pub(crate) struct InitTrackerDrain<'a, Idx: Ord + Copy> {
uninitialized_ranges: &'a mut UninitializedRangeVec<Idx>,
drain_range: Range<Idx>,
first_index: usize,
next_index: usize,
}
impl<'a> Iterator for MemoryInitTrackerDrain<'a> {
type Item = Range<wgt::BufferAddress>;
impl<'a, Idx> Iterator for InitTrackerDrain<'a, Idx>
where
Idx: Ord + Copy,
{
type Item = Range<Idx>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(r) = self
@@ -89,15 +116,18 @@ impl<'a> Iterator for MemoryInitTrackerDrain<'a> {
}
}
impl MemoryInitTracker {
pub(crate) fn new(size: wgt::BufferAddress) -> Self {
impl<Idx> InitTracker<Idx>
where
Idx: Ord + Copy + Default,
{
pub(crate) fn new(size: Idx) -> Self {
Self {
uninitialized_ranges: vec![0..size],
uninitialized_ranges: iter::once(Idx::default()..size).collect(),
}
}
// Search smallest range.end which is bigger than bound in O(log n) (with n being number of uninitialized ranges)
fn lower_bound(&self, bound: wgt::BufferAddress) -> usize {
fn lower_bound(&self, bound: Idx) -> usize {
// This is equivalent to, except that it may return an out of bounds index instead of
//self.uninitialized_ranges.iter().position(|r| r.end > bound)
@@ -123,10 +153,7 @@ impl MemoryInitTracker {
// Checks if there's any uninitialized ranges within a query.
// If there are any, the range returned a the subrange of the query_range that contains all these uninitialized regions.
// Returned range may be larger than necessary (tradeoff for making this function O(log n))
pub(crate) fn check(
&self,
query_range: Range<wgt::BufferAddress>,
) -> Option<Range<wgt::BufferAddress>> {
pub(crate) fn check(&self, query_range: Range<Idx>) -> Option<Range<Idx>> {
let index = self.lower_bound(query_range.start);
self.uninitialized_ranges
.get(index)
@@ -153,12 +180,9 @@ impl MemoryInitTracker {
// Drains uninitialized ranges in a query range.
#[must_use]
pub(crate) fn drain(
&mut self,
drain_range: Range<wgt::BufferAddress>,
) -> MemoryInitTrackerDrain {
pub(crate) fn drain(&mut self, drain_range: Range<Idx>) -> InitTrackerDrain<Idx> {
let index = self.lower_bound(drain_range.start);
MemoryInitTrackerDrain {
InitTrackerDrain {
drain_range,
uninitialized_ranges: &mut self.uninitialized_ranges,
first_index: index,
@@ -167,19 +191,20 @@ impl MemoryInitTracker {
}
// Clears uninitialized ranges in a query range.
pub(crate) fn clear(&mut self, range: Range<wgt::BufferAddress>) {
pub(crate) fn clear(&mut self, range: Range<Idx>) {
self.drain(range).for_each(drop);
}
}
#[cfg(test)]
mod test {
use super::MemoryInitTracker;
use std::ops::Range;
type Tracker = super::InitTracker<usize>;
#[test]
fn check_for_newly_created_tracker() {
let tracker = MemoryInitTracker::new(10);
let tracker = Tracker::new(10);
assert_eq!(tracker.check(0..10), Some(0..10));
assert_eq!(tracker.check(0..3), Some(0..3));
assert_eq!(tracker.check(3..4), Some(3..4));
@@ -188,7 +213,7 @@ mod test {
#[test]
fn check_for_cleared_tracker() {
let mut tracker = MemoryInitTracker::new(10);
let mut tracker = Tracker::new(10);
tracker.clear(0..10);
assert_eq!(tracker.check(0..10), None);
assert_eq!(tracker.check(0..3), None);
@@ -198,7 +223,7 @@ mod test {
#[test]
fn check_for_partially_filled_tracker() {
let mut tracker = MemoryInitTracker::new(25);
let mut tracker = Tracker::new(25);
// Two regions of uninitialized memory
tracker.clear(0..5);
tracker.clear(10..15);
@@ -217,7 +242,7 @@ mod test {
#[test]
fn clear_already_cleared() {
let mut tracker = MemoryInitTracker::new(30);
let mut tracker = Tracker::new(30);
tracker.clear(10..20);
// Overlapping with non-cleared
@@ -233,11 +258,11 @@ mod test {
#[test]
fn drain_never_returns_ranges_twice_for_same_range() {
let mut tracker = MemoryInitTracker::new(19);
let mut tracker = Tracker::new(19);
assert_eq!(tracker.drain(0..19).count(), 1);
assert_eq!(tracker.drain(0..19).count(), 0);
let mut tracker = MemoryInitTracker::new(17);
let mut tracker = Tracker::new(17);
assert_eq!(tracker.drain(5..8).count(), 1);
assert_eq!(tracker.drain(5..8).count(), 0);
assert_eq!(tracker.drain(1..3).count(), 1);
@@ -248,31 +273,23 @@ mod test {
#[test]
fn drain_splits_ranges_correctly() {
let mut tracker = MemoryInitTracker::new(1337);
let mut tracker = Tracker::new(1337);
assert_eq!(
tracker
.drain(21..42)
.collect::<Vec<Range<wgt::BufferAddress>>>(),
tracker.drain(21..42).collect::<Vec<Range<usize>>>(),
vec![21..42]
);
assert_eq!(
tracker
.drain(900..1000)
.collect::<Vec<Range<wgt::BufferAddress>>>(),
tracker.drain(900..1000).collect::<Vec<Range<usize>>>(),
vec![900..1000]
);
// Splitted ranges.
assert_eq!(
tracker
.drain(5..1003)
.collect::<Vec<Range<wgt::BufferAddress>>>(),
tracker.drain(5..1003).collect::<Vec<Range<usize>>>(),
vec![5..21, 42..900, 1000..1003]
);
assert_eq!(
tracker
.drain(0..1337)
.collect::<Vec<Range<wgt::BufferAddress>>>(),
tracker.drain(0..1337).collect::<Vec<Range<usize>>>(),
vec![0..5, 1003..1337]
);
}

View File

@@ -37,8 +37,8 @@ pub mod device;
pub mod error;
pub mod hub;
pub mod id;
mod init_tracker;
pub mod instance;
mod memory_init_tracker;
pub mod pipeline;
pub mod present;
pub mod resource;

View File

@@ -2,7 +2,7 @@ use crate::{
device::{DeviceError, HostMap, MissingFeatures},
hub::Resource,
id::{DeviceId, SurfaceId, TextureId, Valid},
memory_init_tracker::MemoryInitTracker,
init_tracker::BufferInitTracker,
track::{TextureSelector, DUMMY_SELECTOR},
validation::MissingBufferUsageError,
Label, LifeGuard, RefCount, Stored,
@@ -120,7 +120,7 @@ pub struct Buffer<A: hal::Api> {
pub(crate) device_id: Stored<DeviceId>,
pub(crate) usage: wgt::BufferUsages,
pub(crate) size: wgt::BufferAddress,
pub(crate) initialization_status: MemoryInitTracker,
pub(crate) initialization_status: BufferInitTracker,
pub(crate) sync_mapped_writes: Option<hal::MemoryRange>,
pub(crate) life_guard: LifeGuard,
pub(crate) map_state: BufferMapState<A>,