Tracking Optimization and Rewrite (#2662)

* Texture state full is dead code

* temp

* temp2 - next is lifetime only

* temp3 - all trackers in place

* temp4 - continue muddling along

* temp5 - doodle doodle

* temp6 - continuous progress is continuous

* temp7 - pro-gress

* temp8 - Add refcounts to trackers

* temp9 - Soundness, generics, refcounts, and epochs

* temp10 - Compiling?!1?!1?!1

* temp11 - Add bind group state optimization

* temp12 - Safety and adding size setting

* temp13 - unsafe

* temp14 - Abstract all the state transitions

* temp15 - It verks! kinda...

* temp16 - it more verks!

* temp17 - debugging and unit tests

* temp18 - fixing unfixed things

* temp19 - hmmm

* temp20 - curious about line deltas

* temp21 - working texture trackers

* temp22 - write merge/update/barrier for buffers

* temp23 - cleanup and buffers

* temp24 - clippy cleanup

* temp25 - Add inline markers on buffer functions

* temp26 - Fix buffer trackers

* temp27 - fixed texture insert to handle both sides

* temp28 - document tracker and usages

* temp29 - document track/mod.rs

* temp30 - convert STORAGE_WRITE to STORAGE_READ_WRITE

* temp31 - Add some debug asserts to make sure we can't insert invalid states

* temp32 - clippy is opinionated sometimes

* temp33 - renaming and documentation

* temp34 - logging
This commit is contained in:
Connor Fitzgerald 2022-05-24 11:36:13 -04:00 committed by GitHub
parent dd6febe309
commit 9114283707
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 4115 additions and 2369 deletions

555
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ vulkan-portability = ["hal/vulkan"]
[dependencies]
arrayvec = "0.7"
bitflags = "1.0"
bit-vec = "0.6"
codespan-reporting = "0.11"
copyless = "0.1"
fxhash = "0.2"

View File

@ -1,10 +1,10 @@
use crate::{
device::{DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT},
error::{ErrorFormatter, PrettyError},
hub::Resource,
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid},
hub::{HalApi, Resource},
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureId, TextureViewId, Valid},
init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
track::{TrackerSet, UsageConflict, DUMMY_SELECTOR},
track::{BindGroupStates, UsageConflict},
validation::{MissingBufferUsageError, MissingTextureUsageError},
FastHashMap, Label, LifeGuard, MultiRefCount, Stored,
};
@ -16,10 +16,7 @@ use serde::Deserialize;
#[cfg(feature = "trace")]
use serde::Serialize;
use std::{
borrow::{Borrow, Cow},
ops::Range,
};
use std::{borrow::Cow, ops::Range};
use thiserror::Error;
@ -63,6 +60,8 @@ pub enum CreateBindGroupError {
InvalidBuffer(BufferId),
#[error("texture view {0:?} is invalid")]
InvalidTextureView(TextureViewId),
#[error("texture {0:?} is invalid")]
InvalidTexture(TextureId),
#[error("sampler {0:?} is invalid")]
InvalidSampler(SamplerId),
#[error(
@ -709,13 +708,12 @@ pub(crate) fn buffer_binding_type_alignment(
}
}
#[derive(Debug)]
pub struct BindGroup<A: hal::Api> {
pub struct BindGroup<A: HalApi> {
pub(crate) raw: A::BindGroup,
pub(crate) device_id: Stored<DeviceId>,
pub(crate) layout_id: Valid<BindGroupLayoutId>,
pub(crate) life_guard: LifeGuard,
pub(crate) used: TrackerSet,
pub(crate) used: BindGroupStates<A>,
pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,
pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
@ -724,7 +722,7 @@ pub struct BindGroup<A: hal::Api> {
pub(crate) late_buffer_binding_sizes: Vec<wgt::BufferSize>,
}
impl<A: hal::Api> BindGroup<A> {
impl<A: HalApi> BindGroup<A> {
pub(crate) fn validate_dynamic_bindings(
&self,
offsets: &[wgt::DynamicOffset],
@ -766,13 +764,7 @@ impl<A: hal::Api> BindGroup<A> {
}
}
impl<A: hal::Api> Borrow<()> for BindGroup<A> {
fn borrow(&self) -> &() {
&DUMMY_SELECTOR
}
}
impl<A: hal::Api> Resource for BindGroup<A> {
impl<A: HalApi> Resource for BindGroup<A> {
const TYPE: &'static str = "BindGroup";
fn life_guard(&self) -> &LifeGuard {

View File

@ -34,7 +34,7 @@ invalidations or index format changes.
#![allow(clippy::reversed_empty_ranges)]
use crate::{
binding_model::buffer_binding_type_alignment,
binding_model::{self, buffer_binding_type_alignment},
command::{
BasePass, BindGroupStateChange, DrawError, MapPassErr, PassErrorScope, RenderCommand,
RenderCommandError, StateChange,
@ -48,8 +48,9 @@ use crate::{
hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Resource, Storage, Token},
id,
init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},
pipeline::PipelineFlags,
track::{TrackerSet, UsageConflict},
pipeline::{self, PipelineFlags},
resource,
track::RenderBundleScope,
validation::check_buffer_usage,
Label, LabelHelpers, LifeGuard, Stored,
};
@ -117,7 +118,7 @@ impl RenderBundleEncoder {
},
sample_count: {
let sc = desc.sample_count;
if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) {
if sc == 0 || sc > 32 || !conv::is_power_of_two_u32(sc) {
return Err(CreateRenderBundleError::InvalidSampleCount(sc));
}
sc
@ -177,20 +178,28 @@ impl RenderBundleEncoder {
/// and accumulate buffer and texture initialization actions.
///
/// [`ExecuteBundle`]: RenderCommand::ExecuteBundle
pub(crate) fn finish<A: hal::Api, G: GlobalIdentityHandlerFactory>(
pub(crate) fn finish<A: HalApi, G: GlobalIdentityHandlerFactory>(
self,
desc: &RenderBundleDescriptor,
device: &Device<A>,
hub: &Hub<A, G>,
token: &mut Token<Device<A>>,
) -> Result<RenderBundle, RenderBundleError> {
) -> Result<RenderBundle<A>, RenderBundleError> {
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(token);
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token);
let (buffer_guard, _) = hub.buffers.read(&mut token);
let (query_set_guard, mut token) = hub.query_sets.read(&mut token);
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
let (texture_guard, _) = hub.textures.read(&mut token);
let mut state = State {
trackers: TrackerSet::new(self.parent_id.backend()),
trackers: RenderBundleScope::new(
&*buffer_guard,
&*texture_guard,
&*bind_group_guard,
&*pipeline_guard,
&*query_set_guard,
),
index: IndexState::new(),
vertex: (0..hal::MAX_VERTEX_BUFFERS)
.map(|_| VertexState::new())
@ -234,11 +243,11 @@ impl RenderBundleEncoder {
next_dynamic_offset = offsets_range.end;
let offsets = &base.dynamic_offsets[offsets_range.clone()];
let bind_group = state
let bind_group: &binding_model::BindGroup<A> = state
.trackers
.bind_groups
.use_extend(&*bind_group_guard, bind_group_id, (), ())
.map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id))
.add_single(&*bind_group_guard, bind_group_id)
.ok_or(RenderCommandError::InvalidBindGroup(bind_group_id))
.map_pass_err(scope)?;
if bind_group.dynamic_binding_info.len() != offsets.len() {
return Err(RenderCommandError::InvalidDynamicOffsetCount {
@ -268,10 +277,12 @@ impl RenderBundleEncoder {
texture_memory_init_actions.extend_from_slice(&bind_group.used_texture_ranges);
state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets_range);
state
.trackers
.merge_extend_stateful(&bind_group.used)
.map_pass_err(scope)?;
unsafe {
state
.trackers
.merge_bind_group(&*texture_guard, &bind_group.used)
.map_pass_err(scope)?
};
//Note: stateless trackers are not merged: the lifetime reference
// is held to the bind group itself.
}
@ -280,11 +291,11 @@ impl RenderBundleEncoder {
state.pipeline = Some(pipeline_id);
let pipeline = state
let pipeline: &pipeline::RenderPipeline<A> = state
.trackers
.render_pipes
.use_extend(&*pipeline_guard, pipeline_id, (), ())
.map_err(|_| RenderCommandError::InvalidPipeline(pipeline_id))
.render_pipelines
.add_single(&*pipeline_guard, pipeline_id)
.ok_or(RenderCommandError::InvalidPipeline(pipeline_id))
.map_pass_err(scope)?;
self.context
@ -320,11 +331,11 @@ impl RenderBundleEncoder {
size,
} => {
let scope = PassErrorScope::SetIndexBuffer(buffer_id);
let buffer = state
let buffer: &resource::Buffer<A> = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDEX)
.unwrap();
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX)
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDEX)
.map_pass_err(scope)?;
@ -347,11 +358,11 @@ impl RenderBundleEncoder {
size,
} => {
let scope = PassErrorScope::SetVertexBuffer(buffer_id);
let buffer = state
let buffer: &resource::Buffer<A> = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::VERTEX)
.unwrap();
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX)
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, wgt::BufferUsages::VERTEX)
.map_pass_err(scope)?;
@ -472,11 +483,11 @@ impl RenderBundleEncoder {
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
.map_pass_err(scope)?;
let buffer = state
let buffer: &resource::Buffer<A> = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
.unwrap();
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
.map_pass_err(scope)?;
@ -505,11 +516,10 @@ impl RenderBundleEncoder {
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
.map_pass_err(scope)?;
let buffer = state
let buffer: &resource::Buffer<A> = state
.trackers
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
.map_err(|err| RenderCommandError::Buffer(buffer_id, err))
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
.map_pass_err(scope)?;
@ -612,24 +622,23 @@ pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
//Note: here, `RenderBundle` is just wrapping a raw stream of render commands.
// The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle,
// or Metal indirect command buffer.
#[derive(Debug)]
pub struct RenderBundle {
pub struct RenderBundle<A: HalApi> {
// Normalized command stream. It can be executed verbatim,
// without re-binding anything on the pipeline change.
base: BasePass<RenderCommand>,
pub(super) is_ds_read_only: bool,
pub(crate) device_id: Stored<id::DeviceId>,
pub(crate) used: TrackerSet,
pub(crate) used: RenderBundleScope<A>,
pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
pub(super) context: RenderPassContext,
pub(crate) life_guard: LifeGuard,
}
unsafe impl Send for RenderBundle {}
unsafe impl Sync for RenderBundle {}
unsafe impl<A: HalApi> Send for RenderBundle<A> {}
unsafe impl<A: HalApi> Sync for RenderBundle<A> {}
impl RenderBundle {
impl<A: HalApi> RenderBundle<A> {
/// Actually encode the contents into a native command buffer.
///
/// This is partially duplicating the logic of `command_encoder_run_render_pass`.
@ -639,7 +648,7 @@ impl RenderBundle {
/// Note that the function isn't expected to fail, generally.
/// All the validation has already been done by this point.
/// The only failure condition is if some of the used buffers are destroyed.
pub(super) unsafe fn execute<A: HalApi>(
pub(super) unsafe fn execute(
&self,
raw: &mut A::CommandEncoder,
pipeline_layout_guard: &Storage<
@ -828,7 +837,7 @@ impl RenderBundle {
}
}
impl Resource for RenderBundle {
impl<A: HalApi> Resource for RenderBundle<A> {
const TYPE: &'static str = "RenderBundle";
fn life_guard(&self) -> &LifeGuard {
@ -1027,10 +1036,9 @@ struct VertexLimitState {
/// [`SetIndexBuffer`] to the simulated state stored here, and then
/// calls the `flush_foo` methods before draw calls to produce the
/// update commands we actually need.
#[derive(Debug)]
struct State {
struct State<A: HalApi> {
/// Resources used by this bundle. This will become [`RenderBundle::used`].
trackers: TrackerSet,
trackers: RenderBundleScope<A>,
/// The current index buffer. We flush this state before indexed
/// draw commands.
@ -1056,7 +1064,7 @@ struct State {
pipeline: Option<id::RenderPipelineId>,
}
impl State {
impl<A: HalApi> State<A> {
fn vertex_limits(&self) -> VertexLimitState {
let mut vert_state = VertexLimitState {
vertex_limit: u32::MAX,
@ -1235,8 +1243,6 @@ pub(super) enum RenderBundleErrorInner {
#[error(transparent)]
RenderCommand(RenderCommandError),
#[error(transparent)]
ResourceUsageConflict(#[from] UsageConflict),
#[error(transparent)]
Draw(#[from] DrawError),
#[error(transparent)]
MissingDownlevelFlags(#[from] MissingDownlevelFlags),

View File

@ -4,13 +4,12 @@ use std::{num::NonZeroU32, ops::Range};
use crate::device::trace::Command as TraceCommand;
use crate::{
command::CommandBuffer,
device::Device,
get_lowest_common_denom,
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token},
hub::{self, Global, GlobalIdentityHandlerFactory, HalApi, Token},
id::{BufferId, CommandEncoderId, DeviceId, TextureId, Valid},
init_tracker::{MemoryInitKind, TextureInitRange},
resource::{Texture, TextureClearMode},
track::{ResourceTracker, TextureSelector, TextureState},
track::{TextureSelector, TextureTracker},
};
use hal::{auxil::align_to, CommandEncoder as _};
@ -90,8 +89,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (dst_buffer, dst_pending) = cmd_buf
.trackers
.buffers
.use_replace(&*buffer_guard, dst, (), hal::BufferUses::COPY_DST)
.map_err(ClearError::InvalidBuffer)?;
.set_single(&*buffer_guard, dst, hal::BufferUses::COPY_DST)
.ok_or(ClearError::InvalidBuffer(dst))?;
let dst_raw = dst_buffer
.raw
.as_ref()
@ -139,7 +138,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
let cmd_buf_raw = cmd_buf.encoder.open();
unsafe {
cmd_buf_raw.transition_buffers(dst_barrier);
cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
cmd_buf_raw.clear_buffer(dst_raw, offset..end);
}
Ok(())
@ -191,13 +190,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// Check if subresource level range is valid
let subresource_level_end = match subresource_range.mip_level_count {
Some(count) => subresource_range.base_mip_level + count.get(),
None => dst_texture.full_range.levels.end,
None => dst_texture.full_range.mips.end,
};
if dst_texture.full_range.levels.start > subresource_range.base_mip_level
|| dst_texture.full_range.levels.end < subresource_level_end
if dst_texture.full_range.mips.start > subresource_range.base_mip_level
|| dst_texture.full_range.mips.end < subresource_level_end
{
return Err(ClearError::InvalidTextureLevelRange {
texture_level_range: dst_texture.full_range.levels.clone(),
texture_level_range: dst_texture.full_range.mips.clone(),
subresource_base_mip_level: subresource_range.base_mip_level,
subresource_mip_level_count: subresource_range.mip_level_count,
});
@ -217,48 +216,34 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
});
}
let device = &device_guard[cmd_buf.device_id.value];
clear_texture(
&*texture_guard,
Valid(dst),
dst_texture,
TextureInitRange {
mip_range: subresource_range.base_mip_level..subresource_level_end,
layer_range: subresource_range.base_array_layer..subresource_layer_end,
},
cmd_buf.encoder.open(),
&mut cmd_buf.trackers.textures,
&device_guard[cmd_buf.device_id.value],
&device.alignments,
&device.zero_buffer,
)
}
}
pub(crate) fn clear_texture<A: hal::Api>(
pub(crate) fn clear_texture<A: HalApi>(
storage: &hub::Storage<Texture<A>, TextureId>,
dst_texture_id: Valid<TextureId>,
dst_texture: &Texture<A>,
range: TextureInitRange,
encoder: &mut A::CommandEncoder,
texture_tracker: &mut ResourceTracker<TextureState>,
device: &Device<A>,
) -> Result<(), ClearError> {
clear_texture_no_device(
dst_texture_id,
dst_texture,
range,
encoder,
texture_tracker,
&device.alignments,
&device.zero_buffer,
)
}
pub(crate) fn clear_texture_no_device<A: hal::Api>(
dst_texture_id: Valid<TextureId>,
dst_texture: &Texture<A>,
range: TextureInitRange,
encoder: &mut A::CommandEncoder,
texture_tracker: &mut ResourceTracker<TextureState>,
texture_tracker: &mut TextureTracker<A>,
alignments: &hal::Alignments,
zero_buffer: &A::Buffer,
) -> Result<(), ClearError> {
let dst_texture = &storage[dst_texture_id];
let dst_raw = dst_texture
.inner
.as_raw()
@ -277,7 +262,7 @@ pub(crate) fn clear_texture_no_device<A: hal::Api>(
};
let selector = TextureSelector {
levels: range.mip_range.clone(),
mips: range.mip_range.clone(),
layers: range.layer_range.clone(),
};
@ -287,14 +272,13 @@ pub(crate) fn clear_texture_no_device<A: hal::Api>(
// On the other hand, when coming via command_encoder_clear_texture, the life_guard is still there since in order to call it a texture object is needed.
//
// We could in theory distinguish these two scenarios in the internal clear_texture api in order to remove this check and call the cheaper change_replace_tracked whenever possible.
let dst_barrier = if let Some(ref_count) = dst_texture.life_guard().ref_count.as_ref() {
texture_tracker.change_replace(dst_texture_id, ref_count, selector, clear_usage)
} else {
texture_tracker.change_replace_tracked(dst_texture_id, selector, clear_usage)
}
.map(|pending| pending.into_hal(dst_texture));
let dst_barrier = texture_tracker
.set_single(storage, dst_texture_id.0, selector, clear_usage)
.unwrap()
.1
.map(|pending| pending.into_hal(dst_texture));
unsafe {
encoder.transition_textures(dst_barrier);
encoder.transition_textures(dst_barrier.into_iter());
}
// Record actual clearing

View File

@ -14,8 +14,9 @@ use crate::{
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
init_tracker::MemoryInitKind,
resource::{Buffer, Texture},
track::{StatefulTrackerSubset, TrackerSet, UsageConflict, UseExtendError},
pipeline,
resource::{self, Buffer, Texture},
track::{Tracker, UsageConflict, UsageScope},
validation::{check_buffer_usage, MissingBufferUsageError},
Label,
};
@ -228,15 +229,14 @@ where
}
}
#[derive(Debug)]
struct State {
struct State<A: HalApi> {
binder: Binder,
pipeline: Option<id::ComputePipelineId>,
trackers: StatefulTrackerSubset,
scope: UsageScope<A>,
debug_scope_depth: u32,
}
impl State {
impl<A: HalApi> State<A> {
fn is_ready(&self) -> Result<(), DispatchError> {
let bind_mask = self.binder.invalid_mask();
if bind_mask != 0 {
@ -253,32 +253,36 @@ impl State {
Ok(())
}
fn flush_states<A: HalApi>(
fn flush_states(
&mut self,
raw_encoder: &mut A::CommandEncoder,
base_trackers: &mut TrackerSet,
base_trackers: &mut Tracker<A>,
bind_group_guard: &Storage<BindGroup<A>, id::BindGroupId>,
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
texture_guard: &Storage<Texture<A>, id::TextureId>,
) -> Result<(), UsageConflict> {
for id in self.binder.list_active() {
self.trackers.merge_extend(&bind_group_guard[id].used)?;
//Note: stateless trackers are not merged: the lifetime reference
unsafe {
self.scope
.merge_bind_group(texture_guard, &bind_group_guard[id].used)?
};
// Note: stateless trackers are not merged: the lifetime reference
// is held to the bind group itself.
}
for id in self.binder.list_active() {
unsafe {
base_trackers.set_and_remove_from_usage_scope_sparse(
texture_guard,
&mut self.scope,
&bind_group_guard[id].used,
)
}
}
log::trace!("Encoding dispatch barriers");
CommandBuffer::insert_barriers(
raw_encoder,
base_trackers,
&self.trackers.buffers,
&self.trackers.textures,
buffer_guard,
texture_guard,
);
self.trackers.clear();
CommandBuffer::drain_barriers(raw_encoder, base_trackers, buffer_guard, texture_guard);
Ok(())
}
}
@ -338,7 +342,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut state = State {
binder: Binder::new(),
pipeline: None,
trackers: StatefulTrackerSubset::new(A::VARIANT),
scope: UsageScope::new(&*buffer_guard, &*texture_guard),
debug_scope_depth: 0,
};
let mut temp_offsets = Vec::new();
@ -346,6 +350,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut string_offset = 0;
let mut active_query = None;
cmd_buf.trackers.set_size(
Some(&*buffer_guard),
Some(&*texture_guard),
None,
None,
Some(&*bind_group_guard),
Some(&*pipeline_guard),
None,
None,
Some(&*query_set_guard),
);
let hal_desc = hal::ComputePassDescriptor { label: base.label };
unsafe {
raw.begin_compute_pass(&hal_desc);
@ -379,11 +395,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
);
dynamic_offset_count += num_dynamic_offsets as usize;
let bind_group = cmd_buf
let bind_group: &BindGroup<A> = cmd_buf
.trackers
.bind_groups
.use_extend(&*bind_group_guard, bind_group_id, (), ())
.map_err(|_| ComputePassErrorInner::InvalidBindGroup(bind_group_id))
.add_single(&*bind_group_guard, bind_group_id)
.ok_or(ComputePassErrorInner::InvalidBindGroup(bind_group_id))
.map_pass_err(scope)?;
bind_group
.validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits)
@ -434,11 +450,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
state.pipeline = Some(pipeline_id);
let pipeline = cmd_buf
let pipeline: &pipeline::ComputePipeline<A> = cmd_buf
.trackers
.compute_pipes
.use_extend(&*pipeline_guard, pipeline_id, (), ())
.map_err(|_| ComputePassErrorInner::InvalidPipeline(pipeline_id))
.compute_pipelines
.add_single(&*pipeline_guard, pipeline_id)
.ok_or(ComputePassErrorInner::InvalidPipeline(pipeline_id))
.map_pass_err(scope)?;
unsafe {
@ -587,11 +603,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
.map_pass_err(scope)?;
let indirect_buffer = state
.trackers
let indirect_buffer: &Buffer<A> = state
.scope
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
.map_err(|_| ComputePassErrorInner::InvalidIndirectBuffer(buffer_id))
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
check_buffer_usage(indirect_buffer.usage, wgt::BufferUsages::INDIRECT)
.map_pass_err(scope)?;
@ -670,16 +685,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} => {
let scope = PassErrorScope::WriteTimestamp;
let query_set = cmd_buf
let query_set: &resource::QuerySet<A> = cmd_buf
.trackers
.query_sets
.use_extend(&*query_set_guard, query_set_id, (), ())
.map_err(|e| match e {
UseExtendError::InvalidResource => {
ComputePassErrorInner::InvalidQuerySet(query_set_id)
}
_ => unreachable!(),
})
.add_single(&*query_set_guard, query_set_id)
.ok_or(ComputePassErrorInner::InvalidQuerySet(query_set_id))
.map_pass_err(scope)?;
query_set
@ -692,16 +702,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} => {
let scope = PassErrorScope::BeginPipelineStatisticsQuery;
let query_set = cmd_buf
let query_set: &resource::QuerySet<A> = cmd_buf
.trackers
.query_sets
.use_extend(&*query_set_guard, query_set_id, (), ())
.map_err(|e| match e {
UseExtendError::InvalidResource => {
ComputePassErrorInner::InvalidQuerySet(query_set_id)
}
_ => unreachable!(),
})
.add_single(&*query_set_guard, query_set_id)
.ok_or(ComputePassErrorInner::InvalidQuerySet(query_set_id))
.map_pass_err(scope)?;
query_set

View File

@ -5,7 +5,7 @@ use crate::{
binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError},
error::ErrorFormatter,
id,
track::UseExtendError,
track::UsageConflict,
validation::{MissingBufferUsageError, MissingTextureUsageError},
};
use wgt::{BufferAddress, BufferSize, Color};
@ -13,8 +13,6 @@ use wgt::{BufferAddress, BufferSize, Color};
use std::num::NonZeroU32;
use thiserror::Error;
pub type BufferError = UseExtendError<hal::BufferUses>;
/// Error validating a draw call.
#[derive(Clone, Debug, Error, PartialEq)]
pub enum DrawError {
@ -79,8 +77,8 @@ pub enum RenderCommandError {
IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError),
#[error("pipeline writes to depth/stencil, while the pass has read-only depth/stencil")]
IncompatiblePipelineRods,
#[error("buffer {0:?} is in error {1:?}")]
Buffer(id::BufferId, BufferError),
#[error(transparent)]
UsageConflict(#[from] UsageConflict),
#[error("buffer {0:?} is destroyed")]
DestroyedBuffer(id::BufferId),
#[error(transparent)]
@ -106,7 +104,11 @@ impl crate::error::PrettyError for RenderCommandError {
Self::InvalidPipeline(id) => {
fmt.render_pipeline_label(&id);
}
Self::Buffer(id, ..) | Self::DestroyedBuffer(id) => {
Self::UsageConflict(UsageConflict::TextureInvalid { id }) => {
fmt.texture_label(&id);
}
Self::UsageConflict(UsageConflict::BufferInvalid { id })
| Self::DestroyedBuffer(id) => {
fmt.buffer_label(&id);
}
_ => {}

View File

@ -4,11 +4,11 @@ use hal::CommandEncoder;
use crate::{
device::Device,
hub::Storage,
hub::{HalApi, Storage},
id::{self, TextureId},
init_tracker::*,
resource::{Buffer, Texture},
track::{ResourceTracker, TextureState, TrackerSet},
track::{TextureTracker, Tracker},
FastHashMap,
};
@ -121,36 +121,37 @@ impl CommandBufferTextureMemoryActions {
// Utility function that takes discarded surfaces from (several calls to) register_init_action and initializes them on the spot.
// Takes care of barriers as well!
pub(crate) fn fixup_discarded_surfaces<
A: hal::Api,
A: HalApi,
InitIter: Iterator<Item = TextureSurfaceDiscard>,
>(
inits: InitIter,
encoder: &mut A::CommandEncoder,
texture_guard: &Storage<Texture<A>, TextureId>,
texture_tracker: &mut ResourceTracker<TextureState>,
texture_tracker: &mut TextureTracker<A>,
device: &Device<A>,
) {
for init in inits {
clear_texture(
texture_guard,
id::Valid(init.texture),
texture_guard.get(init.texture).unwrap(),
TextureInitRange {
mip_range: init.mip_level..(init.mip_level + 1),
layer_range: init.layer..(init.layer + 1),
},
encoder,
texture_tracker,
device,
&device.alignments,
&device.zero_buffer,
)
.unwrap();
}
}
impl<A: hal::Api> BakedCommands<A> {
impl<A: HalApi> BakedCommands<A> {
// inserts all buffer initializations that are going to be needed for executing the commands and updates resource init states accordingly
pub(crate) fn initialize_buffer_memory(
&mut self,
device_tracker: &mut TrackerSet,
device_tracker: &mut Tracker<A>,
buffer_guard: &mut Storage<Buffer<A>, id::BufferId>,
) -> Result<(), DestroyedBufferError> {
// Gather init ranges for each buffer so we can collapse them.
@ -202,11 +203,11 @@ impl<A: hal::Api> BakedCommands<A> {
// Don't do use_replace since the buffer may already no longer have a ref_count.
// However, we *know* that it is currently in use, so the tracker must already know about it.
let transition = device_tracker.buffers.change_replace_tracked(
id::Valid(buffer_id),
(),
hal::BufferUses::COPY_DST,
);
let transition = device_tracker
.buffers
.set_single(buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
.unwrap()
.1;
let buffer = buffer_guard
.get_mut(buffer_id)
@ -214,8 +215,11 @@ impl<A: hal::Api> BakedCommands<A> {
let raw_buf = buffer.raw.as_ref().ok_or(DestroyedBufferError(buffer_id))?;
unsafe {
self.encoder
.transition_buffers(transition.map(|pending| pending.into_hal(buffer)));
self.encoder.transition_buffers(
transition
.map(|pending| pending.into_hal(buffer))
.into_iter(),
);
}
for range in ranges.iter() {
@ -234,7 +238,7 @@ impl<A: hal::Api> BakedCommands<A> {
// any textures that are left discarded by this command buffer will be marked as uninitialized
pub(crate) fn initialize_texture_memory(
&mut self,
device_tracker: &mut TrackerSet,
device_tracker: &mut Tracker<A>,
texture_guard: &mut Storage<Texture<A>, TextureId>,
device: &Device<A>,
) -> Result<(), DestroyedTextureError> {
@ -274,12 +278,13 @@ impl<A: hal::Api> BakedCommands<A> {
// TODO: Could we attempt some range collapsing here?
for range in ranges.drain(..) {
clear_texture(
texture_guard,
id::Valid(texture_use.id),
&*texture,
range,
&mut self.encoder,
&mut device_tracker.textures,
device,
&device.alignments,
&device.zero_buffer,
)
.unwrap();
}

View File

@ -10,7 +10,7 @@ mod transfer;
use std::slice;
pub(crate) use self::clear::clear_texture_no_device;
pub(crate) use self::clear::clear_texture;
pub use self::{
bundle::*, clear::ClearError, compute::*, draw::*, query::*, render::*, transfer::*,
};
@ -19,11 +19,11 @@ use self::memory_init::CommandBufferTextureMemoryActions;
use crate::error::{ErrorFormatter, PrettyError};
use crate::init_tracker::BufferInitTrackerAction;
use crate::track::{Tracker, UsageScope};
use crate::{
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id,
resource::{Buffer, Texture},
track::{BufferState, ResourceTracker, TextureState, TrackerSet},
Label, Stored,
};
@ -81,10 +81,10 @@ impl<A: hal::Api> CommandEncoder<A> {
}
}
pub struct BakedCommands<A: hal::Api> {
pub struct BakedCommands<A: HalApi> {
pub(crate) encoder: A::CommandEncoder,
pub(crate) list: Vec<A::CommandBuffer>,
pub(crate) trackers: TrackerSet,
pub(crate) trackers: Tracker<A>,
buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
texture_memory_actions: CommandBufferTextureMemoryActions,
}
@ -92,11 +92,11 @@ pub struct BakedCommands<A: hal::Api> {
pub(crate) struct DestroyedBufferError(pub id::BufferId);
pub(crate) struct DestroyedTextureError(pub id::TextureId);
pub struct CommandBuffer<A: hal::Api> {
pub struct CommandBuffer<A: HalApi> {
encoder: CommandEncoder<A>,
status: CommandEncoderStatus,
pub(crate) device_id: Stored<id::DeviceId>,
pub(crate) trackers: TrackerSet,
pub(crate) trackers: Tracker<A>,
buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
texture_memory_actions: CommandBufferTextureMemoryActions,
limits: wgt::Limits,
@ -124,7 +124,7 @@ impl<A: HalApi> CommandBuffer<A> {
},
status: CommandEncoderStatus::Recording,
device_id,
trackers: TrackerSet::new(A::VARIANT),
trackers: Tracker::new(),
buffer_memory_init_actions: Default::default(),
texture_memory_actions: Default::default(),
limits,
@ -138,23 +138,52 @@ impl<A: HalApi> CommandBuffer<A> {
}
}
pub(crate) fn insert_barriers(
pub(crate) fn insert_barriers_from_tracker(
raw: &mut A::CommandEncoder,
base: &mut TrackerSet,
head_buffers: &ResourceTracker<BufferState>,
head_textures: &ResourceTracker<TextureState>,
base: &mut Tracker<A>,
head: &Tracker<A>,
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
texture_guard: &Storage<Texture<A>, id::TextureId>,
) {
profiling::scope!("insert_barriers");
debug_assert_eq!(A::VARIANT, base.backend());
let buffer_barriers = base.buffers.merge_replace(head_buffers).map(|pending| {
let buf = &buffer_guard[pending.id];
base.buffers.set_from_tracker(&head.buffers);
base.textures
.set_from_tracker(&*texture_guard, &head.textures);
Self::drain_barriers(raw, base, buffer_guard, texture_guard);
}
pub(crate) fn insert_barriers_from_scope(
raw: &mut A::CommandEncoder,
base: &mut Tracker<A>,
head: &UsageScope<A>,
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
texture_guard: &Storage<Texture<A>, id::TextureId>,
) {
profiling::scope!("insert_barriers");
base.buffers.set_from_usage_scope(&head.buffers);
base.textures
.set_from_usage_scope(&*texture_guard, &head.textures);
Self::drain_barriers(raw, base, buffer_guard, texture_guard);
}
pub(crate) fn drain_barriers(
raw: &mut A::CommandEncoder,
base: &mut Tracker<A>,
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
texture_guard: &Storage<Texture<A>, id::TextureId>,
) {
profiling::scope!("drain_barriers");
let buffer_barriers = base.buffers.drain().map(|pending| {
let buf = unsafe { &buffer_guard.get_unchecked(pending.id) };
pending.into_hal(buf)
});
let texture_barriers = base.textures.merge_replace(head_textures).map(|pending| {
let tex = &texture_guard[pending.id];
let texture_barriers = base.textures.drain().map(|pending| {
let tex = unsafe { texture_guard.get_unchecked(pending.id) };
pending.into_hal(tex)
});
@ -165,7 +194,7 @@ impl<A: HalApi> CommandBuffer<A> {
}
}
impl<A: hal::Api> CommandBuffer<A> {
impl<A: HalApi> CommandBuffer<A> {
fn get_encoder_mut(
storage: &mut Storage<Self, id::CommandEncoderId>,
id: id::CommandEncoderId,
@ -198,7 +227,7 @@ impl<A: hal::Api> CommandBuffer<A> {
}
}
impl<A: hal::Api> crate::hub::Resource for CommandBuffer<A> {
impl<A: HalApi> crate::hub::Resource for CommandBuffer<A> {
const TYPE: &'static str = "CommandBuffer";
fn life_guard(&self) -> &crate::LifeGuard {
@ -321,7 +350,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
cmd_buf.status = CommandEncoderStatus::Finished;
//Note: if we want to stop tracking the swapchain texture view,
// this is the place to do it.
log::trace!("Command buffer {:?} {:#?}", encoder_id, cmd_buf.trackers);
log::trace!("Command buffer {:?}", encoder_id);
None
}
CommandEncoderStatus::Finished => Some(CommandEncoderError::NotRecording),

View File

@ -8,7 +8,6 @@ use crate::{
id::{self, Id, TypedId},
init_tracker::MemoryInitKind,
resource::QuerySet,
track::UseExtendError,
Epoch, FastHashMap, Index,
};
use std::{iter, marker::PhantomData};
@ -300,11 +299,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let query_set = cmd_buf
.trackers
.query_sets
.use_extend(&*query_set_guard, query_set_id, (), ())
.map_err(|e| match e {
UseExtendError::InvalidResource => QueryError::InvalidQuerySet(query_set_id),
_ => unreachable!(),
})?;
.add_single(&*query_set_guard, query_set_id)
.ok_or(QueryError::InvalidQuerySet(query_set_id))?;
query_set.validate_and_write_timestamp(raw_encoder, query_set_id, query_index, None)?;
@ -348,17 +344,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let query_set = cmd_buf
.trackers
.query_sets
.use_extend(&*query_set_guard, query_set_id, (), ())
.map_err(|e| match e {
UseExtendError::InvalidResource => QueryError::InvalidQuerySet(query_set_id),
_ => unreachable!(),
})?;
.add_single(&*query_set_guard, query_set_id)
.ok_or(QueryError::InvalidQuerySet(query_set_id))?;
let (dst_buffer, dst_pending) = cmd_buf
.trackers
.buffers
.use_replace(&*buffer_guard, destination, (), hal::BufferUses::COPY_DST)
.map_err(QueryError::InvalidBuffer)?;
.set_single(&*buffer_guard, destination, hal::BufferUses::COPY_DST)
.ok_or(QueryError::InvalidBuffer(destination))?;
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
if !dst_buffer.usage.contains(wgt::BufferUsages::COPY_DST) {
@ -407,7 +400,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
));
unsafe {
raw_encoder.transition_buffers(dst_barrier);
raw_encoder.transition_buffers(dst_barrier.into_iter());
raw_encoder.copy_query_results(
&query_set.raw,
start_query..end_query,

View File

@ -1,6 +1,7 @@
use crate::{
binding_model::BindError,
command::{
self,
bind::Binder,
end_pipeline_statistics_query,
memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
@ -17,8 +18,8 @@ use crate::{
id,
init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
pipeline::PipelineFlags,
resource::{Texture, TextureView},
track::{StatefulTrackerSubset, TextureSelector, UsageConflict},
resource::{self, Buffer, Texture, TextureView},
track::{TextureSelector, UsageConflict, UsageScope},
validation::{
check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError,
},
@ -38,7 +39,6 @@ use serde::Deserialize;
#[cfg(any(feature = "serial-pass", feature = "trace"))]
use serde::Serialize;
use crate::track::UseExtendError;
use std::{borrow::Cow, fmt, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, str};
use super::{memory_init::TextureSurfaceDiscard, CommandBufferTextureMemoryActions};
@ -561,9 +561,9 @@ impl<A: hal::Api> TextureView<A> {
const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_TARGETS + hal::MAX_COLOR_TARGETS + 1;
type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
struct RenderPassInfo<'a, A: hal::Api> {
struct RenderPassInfo<'a, A: HalApi> {
context: RenderPassContext,
trackers: StatefulTrackerSubset,
usage_scope: UsageScope<A>,
render_attachments: AttachmentDataVec<RenderAttachment<'a>>, // All render attachments, including depth/stencil
is_ds_read_only: bool,
extent: wgt::Extent3d,
@ -605,7 +605,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
// but recording the discard right away be alright since the texture can't be used during the pass anyways
texture_memory_actions.discard(TextureSurfaceDiscard {
texture: view.parent_id.value.0,
mip_level: view.selector.levels.start,
mip_level: view.selector.mips.start,
layer: view.selector.layers.start,
});
}
@ -618,6 +618,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
cmd_buf: &mut CommandBuffer<A>,
view_guard: &'a Storage<TextureView<A>, id::TextureViewId>,
buffer_guard: &'a Storage<Buffer<A>, id::BufferId>,
texture_guard: &'a Storage<Texture<A>, id::TextureId>,
) -> Result<Self, RenderPassErrorInner> {
profiling::scope!("start", "RenderPassInfo");
@ -699,8 +700,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
let view = cmd_buf
.trackers
.views
.use_extend(&*view_guard, at.view, (), ())
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?;
.add_single(&*view_guard, at.view)
.ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
check_multiview(view)?;
add_view(view, "depth")?;
@ -779,7 +780,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
// Both are discarded using the regular path.
discarded_surfaces.push(TextureSurfaceDiscard {
texture: view.parent_id.value.0,
mip_level: view.selector.levels.start,
mip_level: view.selector.mips.start,
layer: view.selector.layers.start,
});
}
@ -808,8 +809,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
let color_view = cmd_buf
.trackers
.views
.use_extend(&*view_guard, at.view, (), ())
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?;
.add_single(&*view_guard, at.view)
.ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
check_multiview(color_view)?;
add_view(color_view, "color")?;
@ -838,8 +839,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
let resolve_view = cmd_buf
.trackers
.views
.use_extend(&*view_guard, resolve_target, (), ())
.map_err(|_| RenderPassErrorInner::InvalidAttachment(resolve_target))?;
.add_single(&*view_guard, resolve_target)
.ok_or(RenderPassErrorInner::InvalidAttachment(resolve_target))?;
check_multiview(resolve_view)?;
if color_view.extent != resolve_view.extent {
@ -934,7 +935,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
Ok(Self {
context,
trackers: StatefulTrackerSubset::new(A::VARIANT),
usage_scope: UsageScope::new(buffer_guard, texture_guard),
render_attachments,
is_ds_read_only,
extent,
@ -949,7 +950,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
mut self,
raw: &mut A::CommandEncoder,
texture_guard: &Storage<Texture<A>, id::TextureId>,
) -> Result<(StatefulTrackerSubset, SurfacesInDiscardState), RenderPassErrorInner> {
) -> Result<(UsageScope<A>, SurfacesInDiscardState), RenderPassErrorInner> {
profiling::scope!("finish", "RenderPassInfo");
unsafe {
raw.end_render_pass();
@ -963,15 +964,18 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
check_texture_usage(texture.desc.usage, TextureUsages::RENDER_ATTACHMENT)?;
// the tracker set of the pass is always in "extend" mode
self.trackers
.textures
.change_extend(
ra.texture_id.value,
&ra.texture_id.ref_count,
ra.selector.clone(),
ra.usage,
)
.map_err(UsageConflict::from)?;
unsafe {
self.usage_scope
.textures
.merge_single(
&*texture_guard,
ra.texture_id.value,
Some(ra.selector.clone()),
&ra.texture_id.ref_count,
ra.usage,
)
.map_err(UsageConflict::from)?
};
}
// If either only stencil or depth was discarded, we put in a special clear pass to keep the init status of the aspects in sync.
@ -1012,7 +1016,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
}
}
Ok((self.trackers, self.pending_discard_init_fixups))
Ok((self.usage_scope, self.pending_discard_init_fixups))
}
}
@ -1047,7 +1051,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut token = Token::root();
let (device_guard, mut token) = hub.devices.read(&mut token);
let (trackers, query_reset_state, pending_discard_init_fixups) = {
let (scope, query_reset_state, pending_discard_init_fixups) = {
let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token);
// Spell out the type, to placate rust-analyzer.
@ -1075,7 +1079,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (bundle_guard, mut token) = hub.render_bundles.read(&mut token);
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token);
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token);
let (render_pipeline_guard, mut token) = hub.render_pipelines.read(&mut token);
let (query_set_guard, mut token) = hub.query_sets.read(&mut token);
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
let (texture_guard, mut token) = hub.textures.read(&mut token);
@ -1093,10 +1097,23 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
depth_stencil_attachment,
cmd_buf,
&*view_guard,
&*buffer_guard,
&*texture_guard,
)
.map_pass_err(init_scope)?;
cmd_buf.trackers.set_size(
Some(&*buffer_guard),
Some(&*texture_guard),
Some(&*view_guard),
None,
Some(&*bind_group_guard),
None,
Some(&*render_pipeline_guard),
Some(&*bundle_guard),
Some(&*query_set_guard),
);
let raw = &mut cmd_buf.encoder.raw;
let mut state = State {
@ -1142,17 +1159,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let bind_group = cmd_buf
.trackers
.bind_groups
.use_extend(&*bind_group_guard, bind_group_id, (), ())
.map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id))
.add_single(&*bind_group_guard, bind_group_id)
.ok_or(RenderCommandError::InvalidBindGroup(bind_group_id))
.map_pass_err(scope)?;
bind_group
.validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits)
.map_pass_err(scope)?;
// merge the resource tracker in
info.trackers
.merge_extend(&bind_group.used)
.map_pass_err(scope)?;
unsafe {
info.usage_scope
.merge_bind_group(&*texture_guard, &bind_group.used)
.map_pass_err(scope)?;
}
//Note: stateless trackers are not merged: the lifetime reference
// is held to the bind group itself.
@ -1203,9 +1222,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let pipeline = cmd_buf
.trackers
.render_pipes
.use_extend(&*pipeline_guard, pipeline_id, (), ())
.map_err(|_| RenderCommandError::InvalidPipeline(pipeline_id))
.render_pipelines
.add_single(&*render_pipeline_guard, pipeline_id)
.ok_or(RenderCommandError::InvalidPipeline(pipeline_id))
.map_pass_err(scope)?;
info.context
@ -1312,11 +1331,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
size,
} => {
let scope = PassErrorScope::SetIndexBuffer(buffer_id);
let buffer = info
.trackers
let buffer: &Buffer<A> = info
.usage_scope
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDEX)
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX)
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, BufferUsages::INDEX)
.map_pass_err(scope)?;
@ -1359,11 +1377,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
size,
} => {
let scope = PassErrorScope::SetVertexBuffer(buffer_id);
let buffer = info
.trackers
let buffer: &Buffer<A> = info
.usage_scope
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::VERTEX)
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX)
.map_pass_err(scope)?;
check_buffer_usage(buffer.usage, BufferUsages::VERTEX)
.map_pass_err(scope)?;
@ -1617,11 +1634,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
.map_pass_err(scope)?;
let indirect_buffer = info
.trackers
let indirect_buffer: &Buffer<A> = info
.usage_scope
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT)
.map_pass_err(scope)?;
@ -1688,11 +1704,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
.map_pass_err(scope)?;
let indirect_buffer = info
.trackers
let indirect_buffer: &Buffer<A> = info
.usage_scope
.buffers
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
.map_pass_err(scope)?;
check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT)
.map_pass_err(scope)?;
@ -1702,16 +1717,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
.map_pass_err(scope)?;
let count_buffer = info
.trackers
let count_buffer: &Buffer<A> = info
.usage_scope
.buffers
.use_extend(
.merge_single(
&*buffer_guard,
count_buffer_id,
(),
hal::BufferUses::INDIRECT,
)
.map_err(|e| RenderCommandError::Buffer(count_buffer_id, e))
.map_pass_err(scope)?;
check_buffer_usage(count_buffer.usage, BufferUsages::INDIRECT)
.map_pass_err(scope)?;
@ -1814,16 +1827,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} => {
let scope = PassErrorScope::WriteTimestamp;
let query_set = cmd_buf
let query_set: &resource::QuerySet<A> = cmd_buf
.trackers
.query_sets
.use_extend(&*query_set_guard, query_set_id, (), ())
.map_err(|e| match e {
UseExtendError::InvalidResource => {
RenderCommandError::InvalidQuerySet(query_set_id)
}
_ => unreachable!(),
})
.add_single(&*query_set_guard, query_set_id)
.ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
.map_pass_err(scope)?;
query_set
@ -1841,16 +1849,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} => {
let scope = PassErrorScope::BeginPipelineStatisticsQuery;
let query_set = cmd_buf
let query_set: &resource::QuerySet<A> = cmd_buf
.trackers
.query_sets
.use_extend(&*query_set_guard, query_set_id, (), ())
.map_err(|e| match e {
UseExtendError::InvalidResource => {
RenderCommandError::InvalidQuerySet(query_set_id)
}
_ => unreachable!(),
})
.add_single(&*query_set_guard, query_set_id)
.ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
.map_pass_err(scope)?;
query_set
@ -1871,11 +1874,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
RenderCommand::ExecuteBundle(bundle_id) => {
let scope = PassErrorScope::ExecuteBundle;
let bundle = cmd_buf
let bundle: &command::RenderBundle<A> = cmd_buf
.trackers
.bundles
.use_extend(&*bundle_guard, bundle_id, (), ())
.map_err(|_| RenderCommandError::InvalidRenderBundle(bundle_id))
.add_single(&*bundle_guard, bundle_id)
.ok_or(RenderCommandError::InvalidRenderBundle(bundle_id))
.map_pass_err(scope)?;
info.context
@ -1913,7 +1916,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
raw,
&*pipeline_layout_guard,
&*bind_group_guard,
&*pipeline_guard,
&*render_pipeline_guard,
&*buffer_guard,
)
}
@ -1927,23 +1930,21 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
})
.map_pass_err(scope)?;
info.trackers
.merge_extend(&bundle.used)
.map_pass_err(scope)?;
// Start tracking the bind groups specifically, as they are the only
// compound resources, to make it easier to update submission indices
// later at submission time.
cmd_buf
.trackers
.bind_groups
.merge_extend(&bundle.used.bind_groups)
.unwrap();
unsafe {
info.usage_scope
.merge_render_bundle(&*texture_guard, &bundle.used)
.map_pass_err(scope)?;
cmd_buf
.trackers
.add_from_render_bundle(&bundle.used)
.map_pass_err(scope)?;
};
state.reset_bundle();
}
}
}
log::trace!("Merging {:?} with the render pass", encoder_id);
log::trace!("Merging renderpass into cmd_buf {:?}", encoder_id);
let (trackers, pending_discard_init_fixups) =
info.finish(raw, &*texture_guard).map_pass_err(init_scope)?;
@ -1977,11 +1978,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_err(RenderCommandError::InvalidQuerySet)
.map_pass_err(PassErrorScope::QueryReset)?;
super::CommandBuffer::insert_barriers(
super::CommandBuffer::insert_barriers_from_scope(
transit,
&mut cmd_buf.trackers,
&trackers.buffers,
&trackers.textures,
&scope,
&*buffer_guard,
&*texture_guard,
);

View File

@ -1,12 +1,12 @@
#[cfg(feature = "trace")]
use crate::device::trace::Command as TraceCommand;
use crate::{
command::{CommandBuffer, CommandEncoderError},
command::{clear_texture, CommandBuffer, CommandEncoderError},
conv,
device::{Device, MissingDownlevelFlags},
error::{ErrorFormatter, PrettyError},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
id::{BufferId, CommandEncoderId, Id, TextureId, Valid},
id::{BufferId, CommandEncoderId, TextureId, Valid},
init_tracker::{
has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,
TextureInitTrackerAction,
@ -15,14 +15,13 @@ use crate::{
track::TextureSelector,
};
use arrayvec::ArrayVec;
use hal::CommandEncoder as _;
use thiserror::Error;
use wgt::{BufferAddress, BufferUsages, Extent3d, TextureUsages};
use std::iter;
use super::clear::clear_texture;
pub type ImageCopyBuffer = wgt::ImageCopyBuffer<BufferId>;
pub type ImageCopyTexture = wgt::ImageCopyTexture<TextureId>;
@ -191,7 +190,7 @@ pub(crate) fn extract_texture_selector<A: hal::Api>(
aspect: copy_aspect,
};
let selector = TextureSelector {
levels: copy_texture.mip_level..copy_texture.mip_level + 1,
mips: copy_texture.mip_level..copy_texture.mip_level + 1,
layers,
};
@ -381,14 +380,13 @@ pub(crate) fn validate_texture_copy_range(
Ok((copy_extent, array_layer_count))
}
fn handle_texture_init<A: hal::Api>(
fn handle_texture_init<A: HalApi>(
init_kind: MemoryInitKind,
cmd_buf: &mut CommandBuffer<A>,
device: &Device<A>,
copy_texture: &ImageCopyTexture,
copy_size: &Extent3d,
texture_guard: &Storage<Texture<A>, Id<Texture<hal::api::Empty>>>,
texture: &Texture<A>,
texture_guard: &Storage<Texture<A>, TextureId>,
) {
let init_action = TextureInitTrackerAction {
id: copy_texture.texture,
@ -410,15 +408,16 @@ fn handle_texture_init<A: hal::Api>(
let cmd_buf_raw = cmd_buf.encoder.open();
for init in immediate_inits {
clear_texture(
texture_guard,
Valid(init.texture),
texture,
TextureInitRange {
mip_range: init.mip_level..(init.mip_level + 1),
layer_range: init.layer..(init.layer + 1),
},
cmd_buf_raw,
&mut cmd_buf.trackers.textures,
device,
&device.alignments,
&device.zero_buffer,
)
.unwrap();
}
@ -426,14 +425,14 @@ fn handle_texture_init<A: hal::Api>(
}
// Ensures the source texture of a transfer is in the right initialization state and records the state for after the transfer operation.
fn handle_src_texture_init<A: hal::Api>(
fn handle_src_texture_init<A: HalApi>(
cmd_buf: &mut CommandBuffer<A>,
device: &Device<A>,
source: &ImageCopyTexture,
copy_size: &Extent3d,
texture_guard: &Storage<Texture<A>, TextureId>,
) -> Result<(), TransferError> {
let texture = texture_guard
let _ = texture_guard
.get(source.texture)
.map_err(|_| TransferError::InvalidTexture(source.texture))?;
@ -444,13 +443,12 @@ fn handle_src_texture_init<A: hal::Api>(
source,
copy_size,
texture_guard,
texture,
);
Ok(())
}
// Ensures the destination texture of a transfer is in the right initialization state and records the state for after the transfer operation.
fn handle_dst_texture_init<A: hal::Api>(
fn handle_dst_texture_init<A: HalApi>(
cmd_buf: &mut CommandBuffer<A>,
device: &Device<A>,
destination: &ImageCopyTexture,
@ -480,7 +478,6 @@ fn handle_dst_texture_init<A: hal::Api>(
destination,
copy_size,
texture_guard,
texture,
);
Ok(())
}
@ -521,8 +518,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (src_buffer, src_pending) = cmd_buf
.trackers
.buffers
.use_replace(&*buffer_guard, source, (), hal::BufferUses::COPY_SRC)
.map_err(TransferError::InvalidBuffer)?;
.set_single(&*buffer_guard, source, hal::BufferUses::COPY_SRC)
.ok_or(TransferError::InvalidBuffer(source))?;
let src_raw = src_buffer
.raw
.as_ref()
@ -531,15 +528,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
return Err(TransferError::MissingCopySrcUsageFlag.into());
}
// expecting only a single barrier
let src_barrier = src_pending
.map(|pending| pending.into_hal(src_buffer))
.next();
let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer));
let (dst_buffer, dst_pending) = cmd_buf
.trackers
.buffers
.use_replace(&*buffer_guard, destination, (), hal::BufferUses::COPY_DST)
.map_err(TransferError::InvalidBuffer)?;
.set_single(&*buffer_guard, destination, hal::BufferUses::COPY_DST)
.ok_or(TransferError::InvalidBuffer(destination))?;
let dst_raw = dst_buffer
.raw
.as_ref()
@ -547,9 +542,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if !dst_buffer.usage.contains(BufferUsages::COPY_DST) {
return Err(TransferError::MissingCopyDstUsageFlag(Some(destination), None).into());
}
let dst_barrier = dst_pending
.map(|pending| pending.into_hal(dst_buffer))
.next();
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
return Err(TransferError::UnalignedCopySize(size).into());
@ -659,8 +652,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (src_buffer, src_pending) = cmd_buf
.trackers
.buffers
.use_replace(&*buffer_guard, source.buffer, (), hal::BufferUses::COPY_SRC)
.map_err(TransferError::InvalidBuffer)?;
.set_single(&*buffer_guard, source.buffer, hal::BufferUses::COPY_SRC)
.ok_or(TransferError::InvalidBuffer(source.buffer))?;
let src_raw = src_buffer
.raw
.as_ref()
@ -668,18 +661,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if !src_buffer.usage.contains(BufferUsages::COPY_SRC) {
return Err(TransferError::MissingCopySrcUsageFlag.into());
}
let src_barriers = src_pending.map(|pending| pending.into_hal(src_buffer));
let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer));
let (dst_texture, dst_pending) = cmd_buf
.trackers
.textures
.use_replace(
.set_single(
&*texture_guard,
destination.texture,
dst_range,
hal::TextureUses::COPY_DST,
)
.unwrap();
.ok_or(TransferError::InvalidTexture(destination.texture))?;
let dst_raw = dst_texture
.inner
.as_raw()
@ -689,7 +682,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
);
}
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture));
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_texture));
let format_desc = dst_texture.desc.format.describe();
let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
@ -736,8 +729,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let cmd_buf_raw = cmd_buf.encoder.open();
unsafe {
cmd_buf_raw.transition_textures(dst_barriers);
cmd_buf_raw.transition_buffers(src_barriers);
cmd_buf_raw.transition_textures(dst_barrier.into_iter());
cmd_buf_raw.transition_buffers(src_barrier.into_iter());
cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, regions);
}
Ok(())
@ -786,13 +779,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (src_texture, src_pending) = cmd_buf
.trackers
.textures
.use_replace(
.set_single(
&*texture_guard,
source.texture,
src_range,
hal::TextureUses::COPY_SRC,
)
.unwrap();
.ok_or(TransferError::InvalidTexture(source.texture))?;
let src_raw = src_texture
.inner
.as_raw()
@ -800,18 +793,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
if !src_texture.desc.usage.contains(TextureUsages::COPY_SRC) {
return Err(TransferError::MissingCopySrcUsageFlag.into());
}
let src_barriers = src_pending.map(|pending| pending.into_hal(src_texture));
let src_barrier = src_pending.map(|pending| pending.into_hal(src_texture));
let (dst_buffer, dst_pending) = cmd_buf
.trackers
.buffers
.use_replace(
.set_single(
&*buffer_guard,
destination.buffer,
(),
hal::BufferUses::COPY_DST,
)
.map_err(TransferError::InvalidBuffer)?;
.ok_or(TransferError::InvalidBuffer(destination.buffer))?;
let dst_raw = dst_buffer
.raw
.as_ref()
@ -821,7 +813,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
TransferError::MissingCopyDstUsageFlag(Some(destination.buffer), None).into(),
);
}
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_buffer));
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
let format_desc = src_texture.desc.format.describe();
let (hal_copy_size, array_layer_count) =
@ -876,8 +868,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
});
let cmd_buf_raw = cmd_buf.encoder.open();
unsafe {
cmd_buf_raw.transition_buffers(dst_barriers);
cmd_buf_raw.transition_textures(src_barriers);
cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
cmd_buf_raw.transition_textures(src_barrier.into_iter());
cmd_buf_raw.copy_texture_to_buffer(
src_raw,
hal::TextureUses::COPY_SRC,
@ -937,13 +929,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (src_texture, src_pending) = cmd_buf
.trackers
.textures
.use_replace(
.set_single(
&*texture_guard,
source.texture,
src_range,
hal::TextureUses::COPY_SRC,
)
.unwrap();
.ok_or(TransferError::InvalidTexture(source.texture))?;
let src_raw = src_texture
.inner
.as_raw()
@ -954,20 +946,21 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
//TODO: try to avoid this the collection. It's needed because both
// `src_pending` and `dst_pending` try to hold `trackers.textures` mutably.
let mut barriers = src_pending
let mut barriers: ArrayVec<_, 2> = src_pending
.map(|pending| pending.into_hal(src_texture))
.collect::<Vec<_>>();
.into_iter()
.collect();
let (dst_texture, dst_pending) = cmd_buf
.trackers
.textures
.use_replace(
.set_single(
&*texture_guard,
destination.texture,
dst_range,
hal::TextureUses::COPY_DST,
)
.unwrap();
.ok_or(TransferError::InvalidTexture(destination.texture))?;
let dst_raw = dst_texture
.inner
.as_raw()

View File

@ -1,6 +1,10 @@
use crate::resource;
pub fn is_power_of_two(val: u32) -> bool {
pub fn is_power_of_two_u16(val: u16) -> bool {
val != 0 && (val & (val - 1)) == 0
}
pub fn is_power_of_two_u32(val: u32) -> bool {
val != 0 && (val & (val - 1)) == 0
}
@ -53,7 +57,7 @@ pub fn map_buffer_usage(usage: wgt::BufferUsages) -> hal::BufferUses {
usage.contains(wgt::BufferUsages::UNIFORM),
);
u.set(
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE,
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_READ_WRITE,
usage.contains(wgt::BufferUsages::STORAGE),
);
u.set(
@ -81,7 +85,7 @@ pub fn map_texture_usage(
usage.contains(wgt::TextureUsages::TEXTURE_BINDING),
);
u.set(
hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_WRITE,
hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_READ_WRITE,
usage.contains(wgt::TextureUsages::STORAGE_BINDING),
);
let is_color = aspect.contains(hal::FormatAspects::COLOR);
@ -148,7 +152,7 @@ pub fn check_texture_dimension_size(
return Err(Tde::LimitExceeded { dim, given, limit });
}
}
if sample_size == 0 || sample_size > sample_limit || !is_power_of_two(sample_size) {
if sample_size == 0 || sample_size > sample_limit || !is_power_of_two_u32(sample_size) {
return Err(Tde::InvalidSampleCount(sample_size));
}

View File

@ -7,7 +7,7 @@ use crate::{
},
hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Token},
id, resource,
track::TrackerSet,
track::{BindGroupStates, RenderBundleScope, Tracker},
RefCount, Stored, SubmissionIndex,
};
use smallvec::SmallVec;
@ -68,16 +68,20 @@ impl SuspectedResources {
self.query_sets.extend_from_slice(&other.query_sets);
}
pub(super) fn add_trackers(&mut self, trackers: &TrackerSet) {
pub(super) fn add_render_bundle_scope<A: HalApi>(&mut self, trackers: &RenderBundleScope<A>) {
self.buffers.extend(trackers.buffers.used());
self.textures.extend(trackers.textures.used());
self.bind_groups.extend(trackers.bind_groups.used());
self.render_pipelines
.extend(trackers.render_pipelines.used());
self.query_sets.extend(trackers.query_sets.used());
}
pub(super) fn add_bind_group_states<A: HalApi>(&mut self, trackers: &BindGroupStates<A>) {
self.buffers.extend(trackers.buffers.used());
self.textures.extend(trackers.textures.used());
self.texture_views.extend(trackers.views.used());
self.samplers.extend(trackers.samplers.used());
self.bind_groups.extend(trackers.bind_groups.used());
self.compute_pipelines.extend(trackers.compute_pipes.used());
self.render_pipelines.extend(trackers.render_pipes.used());
self.render_bundles.extend(trackers.bundles.used());
self.query_sets.extend(trackers.query_sets.used());
}
}
@ -273,7 +277,8 @@ pub(super) struct LifetimeTracker<A: hal::Api> {
/// Textures can be used in the upcoming submission by `write_texture`.
pub future_suspected_textures: Vec<Stored<id::TextureId>>,
/// Resources that are suspected for destruction.
/// Resources whose user handle has died (i.e. drop/destroy has been called)
/// and will likely be ready for destruction soon.
pub suspected_resources: SuspectedResources,
/// Resources used by queue submissions still in flight. One entry per
@ -491,7 +496,7 @@ impl<A: HalApi> LifetimeTracker<A> {
pub(super) fn triage_suspected<G: GlobalIdentityHandlerFactory>(
&mut self,
hub: &Hub<A, G>,
trackers: &Mutex<TrackerSet>,
trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: Option<&Mutex<trace::Trace>>,
token: &mut Token<super::Device<A>>,
) {
@ -510,7 +515,7 @@ impl<A: HalApi> LifetimeTracker<A> {
}
if let Some(res) = hub.render_bundles.unregister_locked(id.0, &mut *guard) {
self.suspected_resources.add_trackers(&res.used);
self.suspected_resources.add_render_bundle_scope(&res.used);
}
}
}
@ -529,7 +534,7 @@ impl<A: HalApi> LifetimeTracker<A> {
}
if let Some(res) = hub.bind_groups.unregister_locked(id.0, &mut *guard) {
self.suspected_resources.add_trackers(&res.used);
self.suspected_resources.add_bind_group_states(&res.used);
self.suspected_resources
.bind_group_layouts
@ -670,7 +675,7 @@ impl<A: HalApi> LifetimeTracker<A> {
let mut trackers = trackers.lock();
for id in self.suspected_resources.compute_pipelines.drain(..) {
if trackers.compute_pipes.remove_abandoned(id) {
if trackers.compute_pipelines.remove_abandoned(id) {
log::debug!("Compute pipeline {:?} will be destroyed", id);
#[cfg(feature = "trace")]
if let Some(t) = trace {
@ -695,7 +700,7 @@ impl<A: HalApi> LifetimeTracker<A> {
let mut trackers = trackers.lock();
for id in self.suspected_resources.render_pipelines.drain(..) {
if trackers.render_pipes.remove_abandoned(id) {
if trackers.render_pipelines.remove_abandoned(id) {
log::debug!("Render pipeline {:?} will be destroyed", id);
#[cfg(feature = "trace")]
if let Some(t) = trace {
@ -829,7 +834,7 @@ impl<A: HalApi> LifetimeTracker<A> {
&mut self,
hub: &Hub<A, G>,
raw: &A::Device,
trackers: &Mutex<TrackerSet>,
trackers: &Mutex<Tracker<A>>,
token: &mut Token<super::Device<A>>,
) -> Vec<super::BufferMapPendingClosure> {
if self.ready_to_map.is_empty() {

View File

@ -8,7 +8,7 @@ use crate::{
TextureInitTracker, TextureInitTrackerAction,
},
instance, pipeline, present, resource,
track::{BufferState, TextureSelector, TextureState, TrackerSet, UsageConflict},
track::{BindGroupStates, TextureSelector, Tracker},
validation::{self, check_buffer_usage, check_texture_usage},
FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, RefCount, Stored,
SubmissionIndex, DOWNLEVEL_ERROR_MESSAGE,
@ -22,7 +22,7 @@ use smallvec::SmallVec;
use thiserror::Error;
use wgt::{BufferAddress, TextureFormat, TextureViewDimension};
use std::{borrow::Cow, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, ptr};
use std::{borrow::Cow, iter, mem, num::NonZeroU32, ops::Range, ptr};
mod life;
pub mod queue;
@ -256,7 +256,7 @@ impl<A: hal::Api> CommandAllocator<A> {
/// 1. `life_tracker` is locked after `hub.devices`, enforced by the type system
/// 1. `self.trackers` is locked last (unenforced)
/// 1. `self.trace` is locked last (unenforced)
pub struct Device<A: hal::Api> {
pub struct Device<A: HalApi> {
pub(crate) raw: A::Device,
pub(crate) adapter_id: Stored<id::AdapterId>,
pub(crate) queue: A::Queue,
@ -281,7 +281,7 @@ pub struct Device<A: hal::Api> {
/// All live resources allocated with this [`Device`].
///
/// Has to be locked temporarily only (locked last)
pub(crate) trackers: Mutex<TrackerSet>,
pub(crate) trackers: Mutex<Tracker<A>>,
// Life tracker should be locked right after the device and before anything else.
life_tracker: Mutex<life::LifetimeTracker<A>>,
/// Temporary storage for resource management functions. Cleared at the end
@ -306,7 +306,7 @@ pub enum CreateDeviceError {
FailedToCreateZeroBuffer(#[from] DeviceError),
}
impl<A: hal::Api> Device<A> {
impl<A: HalApi> Device<A> {
pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
if self.features.contains(feature) {
Ok(())
@ -328,7 +328,6 @@ impl<A: hal::Api> Device<A> {
}
impl<A: HalApi> Device<A> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
open: hal::OpenDevice<A>,
adapter_id: Stored<id::AdapterId>,
@ -394,7 +393,7 @@ impl<A: HalApi> Device<A> {
command_allocator: Mutex::new(com_alloc),
active_submission_index: 0,
fence,
trackers: Mutex::new(TrackerSet::new(A::VARIANT)),
trackers: Mutex::new(Tracker::new()),
life_tracker: Mutex::new(life::LifetimeTracker::new()),
temp_suspected: life::SuspectedResources::default(),
#[cfg(feature = "trace")]
@ -495,7 +494,7 @@ impl<A: HalApi> Device<A> {
fn untrack<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>(
&'this mut self,
hub: &Hub<A, G>,
trackers: &TrackerSet,
trackers: &Tracker<A>,
token: &mut Token<'token, Self>,
) {
self.temp_suspected.clear();
@ -536,12 +535,12 @@ impl<A: HalApi> Device<A> {
self.temp_suspected.samplers.push(id);
}
}
for id in trackers.compute_pipes.used() {
for id in trackers.compute_pipelines.used() {
if compute_pipe_guard[id].life_guard.ref_count.is_none() {
self.temp_suspected.compute_pipelines.push(id);
}
}
for id in trackers.render_pipes.used() {
for id in trackers.render_pipelines.used() {
if render_pipe_guard[id].life_guard.ref_count.is_none() {
self.temp_suspected.render_pipelines.push(id);
}
@ -655,7 +654,7 @@ impl<A: HalApi> Device<A> {
desc.array_layer_count(),
),
full_range: TextureSelector {
levels: 0..desc.mip_level_count,
mips: 0..desc.mip_level_count,
layers: 0..desc.array_layer_count(),
},
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
@ -876,7 +875,7 @@ impl<A: HalApi> Device<A> {
_ => texture.desc.array_layer_count(),
},
};
let level_end = texture.full_range.levels.end;
let level_end = texture.full_range.mips.end;
let layer_end = texture.full_range.layers.end;
if required_level_count > level_end {
return Err(resource::CreateTextureViewError::TooManyMipLevels {
@ -927,7 +926,7 @@ impl<A: HalApi> Device<A> {
.array_layer_count
.map_or(layer_end, |_| required_layer_count);
let selector = TextureSelector {
levels: desc.range.base_mip_level..end_level,
mips: desc.range.base_mip_level..end_level,
layers: desc.range.base_array_layer..end_layer,
};
@ -972,11 +971,11 @@ impl<A: HalApi> Device<A> {
wgt::TextureViewDimension::D3 => {
hal::TextureUses::RESOURCE
| hal::TextureUses::STORAGE_READ
| hal::TextureUses::STORAGE_WRITE
| hal::TextureUses::STORAGE_READ_WRITE
}
_ => hal::TextureUses::all(),
};
let mask_mip_level = if selector.levels.end - selector.levels.start != 1 {
let mask_mip_level = if selector.mips.end - selector.mips.start != 1 {
hal::TextureUses::RESOURCE
} else {
hal::TextureUses::all()
@ -1058,7 +1057,8 @@ impl<A: HalApi> Device<A> {
let anisotropy_clamp = if let Some(clamp) = desc.anisotropy_clamp {
let clamp = clamp.get();
let valid_clamp = clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two(clamp as u32);
let valid_clamp =
clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two_u32(clamp as u32);
if !valid_clamp {
return Err(resource::CreateSamplerError::InvalidClamp(clamp));
}
@ -1486,7 +1486,6 @@ impl<A: HalApi> Device<A> {
})
}
#[allow(clippy::too_many_arguments)]
fn create_buffer_binding<'a>(
bb: &binding_model::BufferBinding,
binding: u32,
@ -1494,7 +1493,7 @@ impl<A: HalApi> Device<A> {
used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,
dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
used: &mut TrackerSet,
used: &mut BindGroupStates<A>,
storage: &'a Storage<resource::Buffer<A>, id::BufferId>,
limits: &wgt::Limits,
) -> Result<hal::BufferBinding<'a, A>, binding_model::CreateBindGroupError> {
@ -1525,7 +1524,7 @@ impl<A: HalApi> Device<A> {
if read_only {
hal::BufferUses::STORAGE_READ
} else {
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE
hal::BufferUses::STORAGE_READ_WRITE
},
limits.max_storage_buffer_binding_size,
),
@ -1543,8 +1542,8 @@ impl<A: HalApi> Device<A> {
let buffer = used
.buffers
.use_extend(storage, bb.buffer_id, (), internal_use)
.map_err(|_| Error::InvalidBuffer(bb.buffer_id))?;
.add_single(storage, bb.buffer_id, internal_use)
.ok_or(Error::InvalidBuffer(bb.buffer_id))?;
check_buffer_usage(buffer.usage, pub_usage)?;
let raw_buffer = buffer
.raw
@ -1613,26 +1612,26 @@ impl<A: HalApi> Device<A> {
fn create_texture_binding(
view: &resource::TextureView<A>,
texture_guard: &parking_lot::lock_api::RwLockReadGuard<
parking_lot::RawRwLock,
Storage<resource::Texture<A>, id::Id<resource::Texture<hal::api::Empty>>>,
>,
texture_guard: &Storage<resource::Texture<A>, id::TextureId>,
internal_use: hal::TextureUses,
pub_usage: wgt::TextureUsages,
used: &mut TrackerSet,
used: &mut BindGroupStates<A>,
used_texture_ranges: &mut Vec<TextureInitTrackerAction>,
) -> Result<(), binding_model::CreateBindGroupError> {
// Careful here: the texture may no longer have its own ref count,
// if it was deleted by the user.
let texture = &texture_guard[view.parent_id.value];
used.textures
.change_extend(
view.parent_id.value,
&view.parent_id.ref_count,
view.selector.clone(),
let texture = used
.textures
.add_single(
texture_guard,
view.parent_id.value.0,
view.parent_id.ref_count.clone(),
Some(view.selector.clone()),
internal_use,
)
.map_err(UsageConflict::from)?;
.ok_or(binding_model::CreateBindGroupError::InvalidTexture(
view.parent_id.value.0,
))?;
check_texture_usage(texture.desc.usage, pub_usage)?;
used_texture_ranges.push(TextureInitTrackerAction {
@ -1674,7 +1673,7 @@ impl<A: HalApi> Device<A> {
// it needs to be in BGL iteration order, not BG entry order.
let mut late_buffer_binding_sizes = FastHashMap::default();
// fill out the descriptors
let mut used = TrackerSet::new(A::VARIANT);
let mut used = BindGroupStates::new();
let (buffer_guard, mut token) = hub.buffers.read(token);
let (texture_guard, mut token) = hub.textures.read(&mut token); //skip token
@ -1738,8 +1737,8 @@ impl<A: HalApi> Device<A> {
wgt::BindingType::Sampler(ty) => {
let sampler = used
.samplers
.use_extend(&*sampler_guard, id, (), ())
.map_err(|_| Error::InvalidSampler(id))?;
.add_single(&*sampler_guard, id)
.ok_or(Error::InvalidSampler(id))?;
// Allowed sampler values for filtering and comparison
let (allowed_filtering, allowed_comparison) = match ty {
@ -1787,8 +1786,8 @@ impl<A: HalApi> Device<A> {
for &id in bindings_array.iter() {
let sampler = used
.samplers
.use_extend(&*sampler_guard, id, (), ())
.map_err(|_| Error::InvalidSampler(id))?;
.add_single(&*sampler_guard, id)
.ok_or(Error::InvalidSampler(id))?;
hal_samplers.push(&sampler.raw);
}
@ -1797,8 +1796,8 @@ impl<A: HalApi> Device<A> {
Br::TextureView(id) => {
let view = used
.views
.use_extend(&*texture_view_guard, id, (), ())
.map_err(|_| Error::InvalidTextureView(id))?;
.add_single(&*texture_view_guard, id)
.ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) = Self::texture_use_parameters(
binding,
decl,
@ -1828,8 +1827,8 @@ impl<A: HalApi> Device<A> {
for &id in bindings_array.iter() {
let view = used
.views
.use_extend(&*texture_view_guard, id, (), ())
.map_err(|_| Error::InvalidTextureView(id))?;
.add_single(&*texture_view_guard, id)
.ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) =
Self::texture_use_parameters(binding, decl, view,
"SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?;
@ -1858,6 +1857,8 @@ impl<A: HalApi> Device<A> {
});
}
used.optimize();
hal_entries.sort_by_key(|entry| entry.binding);
for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
if a.binding == b.binding {
@ -2018,7 +2019,7 @@ impl<A: HalApi> Device<A> {
});
}
let mip_level_count = view.selector.levels.end - view.selector.levels.start;
let mip_level_count = view.selector.mips.end - view.selector.mips.start;
if mip_level_count != 1 {
return Err(Error::InvalidStorageTextureMipLevelCount {
binding,
@ -2027,7 +2028,7 @@ impl<A: HalApi> Device<A> {
}
let internal_use = match access {
wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_WRITE,
wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_READ_WRITE,
wgt::StorageTextureAccess::ReadOnly => {
if !view
.format_features
@ -2047,7 +2048,7 @@ impl<A: HalApi> Device<A> {
return Err(Error::StorageReadNotSupported(view.desc.format));
}
hal::TextureUses::STORAGE_WRITE | hal::TextureUses::STORAGE_READ
hal::TextureUses::STORAGE_READ_WRITE
}
};
Ok((wgt::TextureUsages::STORAGE_BINDING, internal_use))
@ -2551,7 +2552,7 @@ impl<A: HalApi> Device<A> {
let samples = {
let sc = desc.multisample.count;
if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) {
if sc == 0 || sc > 32 || !conv::is_power_of_two_u32(sc) {
return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
}
sc
@ -2879,7 +2880,7 @@ impl<A: HalApi> Device<A> {
}
}
impl<A: hal::Api> Device<A> {
impl<A: HalApi> Device<A> {
pub(crate) fn destroy_buffer(&self, buffer: resource::Buffer<A>) {
if let Some(raw) = buffer.raw {
unsafe {
@ -2925,7 +2926,7 @@ impl<A: hal::Api> Device<A> {
}
}
impl<A: hal::Api> crate::hub::Resource for Device<A> {
impl<A: HalApi> crate::hub::Resource for Device<A> {
const TYPE: &'static str = "Device";
fn life_guard(&self) -> &LifeGuard {
@ -2981,7 +2982,7 @@ pub struct ImplicitPipelineIds<'a, G: GlobalIdentityHandlerFactory> {
}
impl<G: GlobalIdentityHandlerFactory> ImplicitPipelineIds<'_, G> {
fn prepare<A: hal::Api>(self, hub: &Hub<A, G>) -> ImplicitPipelineContext {
fn prepare<A: HalApi>(self, hub: &Hub<A, G>) -> ImplicitPipelineContext {
ImplicitPipelineContext {
root_id: hub.pipeline_layouts.prepare(self.root_id).into_id(),
group_ids: self
@ -3184,8 +3185,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.trackers
.lock()
.buffers
.init(id, ref_count, BufferState::with_usage(buffer_use))
.unwrap();
.insert_single(id, ref_count, buffer_use);
return (id.0, None);
};
@ -3483,19 +3484,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Ok(texture) => texture,
Err(error) => break error,
};
let num_levels = texture.full_range.levels.end;
let num_layers = texture.full_range.layers.end;
let ref_count = texture.life_guard.add_ref();
let id = fid.assign(texture, &mut token);
log::info!("Created texture {:?} with {:?}", id, desc);
device
.trackers
.lock()
.textures
.init(id, ref_count, TextureState::new(num_levels, num_layers))
.unwrap();
device.trackers.lock().textures.insert_single(
id.0,
ref_count,
hal::TextureUses::UNINITIALIZED,
);
return (id.0, None);
};
@ -3561,19 +3560,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
texture.initialization_status = TextureInitTracker::new(desc.mip_level_count, 0);
let num_levels = texture.full_range.levels.end;
let num_layers = texture.full_range.layers.end;
let ref_count = texture.life_guard.add_ref();
let id = fid.assign(texture, &mut token);
log::info!("Created texture {:?} with {:?}", id, desc);
device
.trackers
.lock()
.textures
.init(id, ref_count, TextureState::new(num_levels, num_layers))
.unwrap();
device.trackers.lock().textures.insert_single(
id.0,
ref_count,
hal::TextureUses::UNINITIALIZED,
);
return (id.0, None);
};
@ -3731,12 +3728,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let ref_count = view.life_guard.add_ref();
let id = fid.assign(view, &mut token);
device
.trackers
.lock()
.views
.init(id, ref_count, PhantomData)
.unwrap();
device.trackers.lock().views.insert_single(id, ref_count);
return (id.0, None);
};
@ -3829,12 +3821,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let ref_count = sampler.life_guard.add_ref();
let id = fid.assign(sampler, &mut token);
device
.trackers
.lock()
.samplers
.init(id, ref_count, PhantomData)
.unwrap();
device.trackers.lock().samplers.insert_single(id, ref_count);
return (id.0, None);
};
@ -4092,18 +4080,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let ref_count = bind_group.life_guard.add_ref();
let id = fid.assign(bind_group, &mut token);
log::debug!(
"Bind group {:?} {:#?}",
id,
hub.bind_groups.read(&mut token).0[id].used
);
log::debug!("Bind group {:?}", id,);
device
.trackers
.lock()
.bind_groups
.init(id, ref_count, PhantomData)
.unwrap();
.insert_single(id, ref_count);
return (id.0, None);
};
@ -4406,16 +4389,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Err(e) => break e,
};
log::debug!("Render bundle {:#?}", render_bundle.used);
log::debug!("Render bundle");
let ref_count = render_bundle.life_guard.add_ref();
let id = fid.assign(render_bundle, &mut token);
device
.trackers
.lock()
.bundles
.init(id, ref_count, PhantomData)
.unwrap();
device.trackers.lock().bundles.insert_single(id, ref_count);
return (id.0, None);
};
@ -4494,8 +4472,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.trackers
.lock()
.query_sets
.init(id, ref_count, PhantomData)
.unwrap();
.insert_single(id, ref_count);
return (id.0, None);
};
@ -4589,9 +4566,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device
.trackers
.lock()
.render_pipes
.init(id, ref_count, PhantomData)
.unwrap();
.render_pipelines
.insert_single(id, ref_count);
return (id.0, None);
};
@ -4730,9 +4707,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device
.trackers
.lock()
.compute_pipes
.init(id, ref_count, PhantomData)
.unwrap();
.compute_pipelines
.insert_single(id, ref_count);
return (id.0, None);
};
@ -5204,16 +5180,20 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
log::debug!("Buffer {:?} map state -> Waiting", buffer_id);
(buffer.device_id.value, buffer.life_guard.add_ref())
let device = &device_guard[buffer.device_id.value];
let ret = (buffer.device_id.value, buffer.life_guard.add_ref());
let mut trackers = device.trackers.lock();
trackers
.buffers
.set_single(&*buffer_guard, buffer_id, internal_use);
trackers.buffers.drain();
ret
};
let device = &device_guard[device_id];
device.trackers.lock().buffers.change_replace(
id::Valid(buffer_id),
&ref_count,
(),
internal_use,
);
device
.lock_life(&mut token)

View File

@ -192,7 +192,7 @@ impl<A: hal::Api> PendingWrites<A> {
}
}
impl<A: hal::Api> super::Device<A> {
impl<A: HalApi> super::Device<A> {
fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result<StagingData<A>, DeviceError> {
profiling::scope!("prepare_stage");
let stage_desc = hal::BufferDescriptor {
@ -286,8 +286,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut trackers = device.trackers.lock();
let (dst, transition) = trackers
.buffers
.use_replace(&*buffer_guard, buffer_id, (), hal::BufferUses::COPY_DST)
.map_err(TransferError::InvalidBuffer)?;
.set_single(&*buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
.ok_or(TransferError::InvalidBuffer(buffer_id))?;
let dst_raw = dst
.raw
.as_ref()
@ -451,9 +451,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.drain(init_layer_range)
.collect::<Vec<std::ops::Range<u32>>>()
{
crate::command::clear_texture_no_device(
crate::command::clear_texture(
&*texture_guard,
id::Valid(destination.texture),
&*dst,
TextureInitRange {
mip_range: destination.mip_level..(destination.mip_level + 1),
layer_range,
@ -473,13 +473,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (dst, transition) = trackers
.textures
.use_replace(
.set_single(
&*texture_guard,
destination.texture,
selector,
hal::TextureUses::COPY_DST,
)
.unwrap();
.ok_or(TransferError::InvalidTexture(destination.texture))?;
let (hal_copy_size, array_layer_count) =
validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?;
@ -561,7 +561,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.ok_or(TransferError::InvalidTexture(destination.texture))?;
unsafe {
encoder.transition_textures(transition.map(|pending| pending.into_hal(dst)));
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);
}
@ -594,7 +595,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device.active_submission_index += 1;
let submit_index = device.active_submission_index;
let mut active_executions = Vec::new();
let mut used_surface_textures = track::ResourceTracker::new(A::VARIANT);
let mut used_surface_textures = track::TextureUsageScope::new();
{
let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token);
@ -616,12 +617,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
//Note: locking the trackers has to be done after the storages
let mut trackers = device.trackers.lock();
used_surface_textures.set_size(texture_guard.len());
//TODO: if multiple command buffers are submitted, we can re-use the last
// native command buffer of the previous chain instead of always creating
// a temporary one, since the chains are not finished.
// finish all the command buffers first
for &cmb_id in command_buffer_ids {
#[allow(unused_mut)]
let mut cmdbuf = match hub
.command_buffers
.unregister_locked(cmb_id, &mut *command_buffer_guard)
@ -642,7 +646,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
// optimize the tracked states
cmdbuf.trackers.optimize();
// cmdbuf.trackers.optimize();
// update submission IDs
for id in cmdbuf.trackers.buffers.used() {
@ -669,33 +673,35 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
for id in cmdbuf.trackers.textures.used() {
let texture = &mut texture_guard[id];
match texture.inner {
let should_extend = match texture.inner {
TextureInner::Native { raw: None } => {
return Err(QueueSubmitError::DestroyedTexture(id.0));
}
TextureInner::Native { raw: Some(_) } => {}
TextureInner::Native { raw: Some(_) } => false,
TextureInner::Surface {
ref mut has_work, ..
} => {
use track::ResourceState as _;
*has_work = true;
let ref_count = cmdbuf.trackers.textures.get_ref_count(id);
//TODO: better error handling here?
// register it in the temporary tracker.
let mut ts = track::TextureState::default();
let _ = ts.change(
id,
texture.full_range.clone(),
hal::TextureUses::empty(), //present
None,
);
let _ = used_surface_textures.init(id, ref_count.clone(), ts);
true
}
}
};
if !texture.life_guard.use_at(submit_index) {
device.temp_suspected.textures.push(id);
}
if should_extend {
unsafe {
let ref_count = cmdbuf.trackers.textures.get_ref_count(id);
used_surface_textures
.merge_single(
&*texture_guard,
id,
None,
ref_count,
hal::TextureUses::PRESENT,
)
.unwrap();
};
}
}
for id in cmdbuf.trackers.views.used() {
if !texture_view_guard[id].life_guard.use_at(submit_index) {
@ -717,13 +723,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
sampler_guard[sub_id].life_guard.use_at(submit_index);
}
}
assert!(cmdbuf.trackers.samplers.is_empty());
for id in cmdbuf.trackers.compute_pipes.used() {
// assert!(cmdbuf.trackers.samplers.is_empty());
for id in cmdbuf.trackers.compute_pipelines.used() {
if !compute_pipe_guard[id].life_guard.use_at(submit_index) {
device.temp_suspected.compute_pipelines.push(id);
}
}
for id in cmdbuf.trackers.render_pipes.used() {
for id in cmdbuf.trackers.render_pipelines.used() {
if !render_pipe_guard[id].life_guard.use_at(submit_index) {
device.temp_suspected.render_pipelines.push(id);
}
@ -741,12 +747,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// We need to update the submission indices for the contained
// state-less (!) resources as well, excluding the bind groups.
// They don't get deleted too early if the bundle goes out of scope.
for sub_id in bundle.used.compute_pipes.used() {
compute_pipe_guard[sub_id].life_guard.use_at(submit_index);
}
for sub_id in bundle.used.render_pipes.used() {
for sub_id in bundle.used.render_pipelines.used() {
render_pipe_guard[sub_id].life_guard.use_at(submit_index);
}
for sub_id in bundle.used.query_sets.used() {
query_set_guard[sub_id].life_guard.use_at(submit_index);
}
}
let mut baked = cmdbuf.into_baked();
@ -766,11 +772,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?;
//Note: stateless trackers are not merged:
// device already knows these resources exist.
CommandBuffer::insert_barriers(
CommandBuffer::insert_barriers_from_tracker(
&mut baked.encoder,
&mut *trackers,
&baked.trackers.buffers,
&baked.trackers.textures,
&baked.trackers,
&*buffer_guard,
&*texture_guard,
);
@ -788,19 +793,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.begin_encoding(Some("(wgpu internal) Present"))
.map_err(DeviceError::from)?
};
let texture_barriers = trackers
trackers
.textures
.merge_replace(&used_surface_textures)
.map(|pending| {
let tex = &texture_guard[pending.id];
pending.into_hal(tex)
});
.set_from_usage_scope(&*texture_guard, &used_surface_textures);
let texture_barriers = trackers.textures.drain().map(|pending| {
let tex = unsafe { texture_guard.get_unchecked(pending.id) };
pending.into_hal(tex)
});
let present = unsafe {
baked.encoder.transition_textures(texture_barriers);
baked.encoder.end_encoding().unwrap()
};
baked.list.push(present);
used_surface_textures.clear();
used_surface_textures = track::TextureUsageScope::new();
}
// done
@ -810,7 +815,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
});
}
log::trace!("Device after submission {}: {:#?}", submit_index, trackers);
log::trace!("Device after submission {}", submit_index);
}
let super::Device {
@ -830,6 +835,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (_, mut token) = hub.buffers.read(&mut token); // skip token
let (mut texture_guard, _) = hub.textures.write(&mut token);
used_surface_textures.set_size(texture_guard.len());
for &id in pending_writes.dst_textures.iter() {
let texture = texture_guard.get_mut(id).unwrap();
match texture.inner {
@ -840,39 +847,39 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
TextureInner::Surface {
ref mut has_work, ..
} => {
use track::ResourceState as _;
*has_work = true;
let ref_count = texture.life_guard.add_ref();
//TODO: better error handling here?
// register it in the temporary tracker.
let mut ts = track::TextureState::default();
let _ = ts.change(
id::Valid(id),
texture.full_range.clone(),
hal::TextureUses::empty(), //present
None,
);
let _ = used_surface_textures.init(id::Valid(id), ref_count, ts);
unsafe {
used_surface_textures
.merge_single(
&*texture_guard,
id::Valid(id),
None,
&ref_count,
hal::TextureUses::PRESENT,
)
.unwrap()
};
}
}
}
if !used_surface_textures.is_empty() {
let mut trackers = device.trackers.lock();
let texture_barriers = trackers
trackers
.textures
.merge_replace(&used_surface_textures)
.map(|pending| {
let tex = &texture_guard[pending.id];
pending.into_hal(tex)
});
.set_from_usage_scope(&*texture_guard, &used_surface_textures);
let texture_barriers = trackers.textures.drain().map(|pending| {
let tex = unsafe { texture_guard.get_unchecked(pending.id) };
pending.into_hal(tex)
});
unsafe {
pending_writes
.command_encoder
.transition_textures(texture_barriers);
};
used_surface_textures.clear();
}
}

View File

@ -194,6 +194,14 @@ impl<T, I: id::TypedId> Storage<T, I> {
result
}
pub(crate) unsafe fn get_unchecked(&self, id: u32) -> &T {
match self.map[id as usize] {
Element::Occupied(ref v, _) => v,
Element::Vacant => panic!("{}[{}] does not exist", self.kind, id),
Element::Error(_, _) => panic!(""),
}
}
pub(crate) fn label_for_invalid_id(&self, id: I) -> &str {
let (index, _, _) = id.unzip();
match self.map.get(index as usize) {
@ -266,6 +274,10 @@ impl<T, I: id::TypedId> Storage<T, I> {
})
}
pub(crate) fn len(&self) -> usize {
self.map.len()
}
fn generate_report(&self) -> StorageReport {
let mut report = StorageReport {
element_size: mem::size_of::<T>(),
@ -298,56 +310,56 @@ pub enum Root {}
impl Access<Instance> for Root {}
impl Access<Surface> for Root {}
impl Access<Surface> for Instance {}
impl<A: hal::Api> Access<Adapter<A>> for Root {}
impl<A: hal::Api> Access<Adapter<A>> for Surface {}
impl<A: hal::Api> Access<Device<A>> for Root {}
impl<A: hal::Api> Access<Device<A>> for Surface {}
impl<A: hal::Api> Access<Device<A>> for Adapter<A> {}
impl<A: hal::Api> Access<PipelineLayout<A>> for Root {}
impl<A: hal::Api> Access<PipelineLayout<A>> for Device<A> {}
impl<A: hal::Api> Access<PipelineLayout<A>> for RenderBundle {}
impl<A: hal::Api> Access<BindGroupLayout<A>> for Root {}
impl<A: hal::Api> Access<BindGroupLayout<A>> for Device<A> {}
impl<A: hal::Api> Access<BindGroupLayout<A>> for PipelineLayout<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for Root {}
impl<A: hal::Api> Access<BindGroup<A>> for Device<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for BindGroupLayout<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for PipelineLayout<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for CommandBuffer<A> {}
impl<A: hal::Api> Access<CommandBuffer<A>> for Root {}
impl<A: hal::Api> Access<CommandBuffer<A>> for Device<A> {}
impl<A: hal::Api> Access<RenderBundle> for Device<A> {}
impl<A: hal::Api> Access<RenderBundle> for CommandBuffer<A> {}
impl<A: hal::Api> Access<ComputePipeline<A>> for Device<A> {}
impl<A: hal::Api> Access<ComputePipeline<A>> for BindGroup<A> {}
impl<A: hal::Api> Access<RenderPipeline<A>> for Device<A> {}
impl<A: hal::Api> Access<RenderPipeline<A>> for BindGroup<A> {}
impl<A: hal::Api> Access<RenderPipeline<A>> for ComputePipeline<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for Root {}
impl<A: hal::Api> Access<QuerySet<A>> for Device<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for CommandBuffer<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for RenderPipeline<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for ComputePipeline<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for Sampler<A> {}
impl<A: hal::Api> Access<ShaderModule<A>> for Device<A> {}
impl<A: hal::Api> Access<ShaderModule<A>> for BindGroupLayout<A> {}
impl<A: hal::Api> Access<Buffer<A>> for Root {}
impl<A: hal::Api> Access<Buffer<A>> for Device<A> {}
impl<A: hal::Api> Access<Buffer<A>> for BindGroupLayout<A> {}
impl<A: hal::Api> Access<Buffer<A>> for BindGroup<A> {}
impl<A: hal::Api> Access<Buffer<A>> for CommandBuffer<A> {}
impl<A: hal::Api> Access<Buffer<A>> for ComputePipeline<A> {}
impl<A: hal::Api> Access<Buffer<A>> for RenderPipeline<A> {}
impl<A: hal::Api> Access<Buffer<A>> for QuerySet<A> {}
impl<A: hal::Api> Access<Texture<A>> for Root {}
impl<A: hal::Api> Access<Texture<A>> for Device<A> {}
impl<A: hal::Api> Access<Texture<A>> for Buffer<A> {}
impl<A: hal::Api> Access<TextureView<A>> for Root {}
impl<A: hal::Api> Access<TextureView<A>> for Device<A> {}
impl<A: hal::Api> Access<TextureView<A>> for Texture<A> {}
impl<A: hal::Api> Access<Sampler<A>> for Root {}
impl<A: hal::Api> Access<Sampler<A>> for Device<A> {}
impl<A: hal::Api> Access<Sampler<A>> for TextureView<A> {}
impl<A: HalApi> Access<Adapter<A>> for Root {}
impl<A: HalApi> Access<Adapter<A>> for Surface {}
impl<A: HalApi> Access<Device<A>> for Root {}
impl<A: HalApi> Access<Device<A>> for Surface {}
impl<A: HalApi> Access<Device<A>> for Adapter<A> {}
impl<A: HalApi> Access<PipelineLayout<A>> for Root {}
impl<A: HalApi> Access<PipelineLayout<A>> for Device<A> {}
impl<A: HalApi> Access<PipelineLayout<A>> for RenderBundle<A> {}
impl<A: HalApi> Access<BindGroupLayout<A>> for Root {}
impl<A: HalApi> Access<BindGroupLayout<A>> for Device<A> {}
impl<A: HalApi> Access<BindGroupLayout<A>> for PipelineLayout<A> {}
impl<A: HalApi> Access<BindGroup<A>> for Root {}
impl<A: HalApi> Access<BindGroup<A>> for Device<A> {}
impl<A: HalApi> Access<BindGroup<A>> for BindGroupLayout<A> {}
impl<A: HalApi> Access<BindGroup<A>> for PipelineLayout<A> {}
impl<A: HalApi> Access<BindGroup<A>> for CommandBuffer<A> {}
impl<A: HalApi> Access<CommandBuffer<A>> for Root {}
impl<A: HalApi> Access<CommandBuffer<A>> for Device<A> {}
impl<A: HalApi> Access<RenderBundle<A>> for Device<A> {}
impl<A: HalApi> Access<RenderBundle<A>> for CommandBuffer<A> {}
impl<A: HalApi> Access<ComputePipeline<A>> for Device<A> {}
impl<A: HalApi> Access<ComputePipeline<A>> for BindGroup<A> {}
impl<A: HalApi> Access<RenderPipeline<A>> for Device<A> {}
impl<A: HalApi> Access<RenderPipeline<A>> for BindGroup<A> {}
impl<A: HalApi> Access<RenderPipeline<A>> for ComputePipeline<A> {}
impl<A: HalApi> Access<QuerySet<A>> for Root {}
impl<A: HalApi> Access<QuerySet<A>> for Device<A> {}
impl<A: HalApi> Access<QuerySet<A>> for CommandBuffer<A> {}
impl<A: HalApi> Access<QuerySet<A>> for RenderPipeline<A> {}
impl<A: HalApi> Access<QuerySet<A>> for ComputePipeline<A> {}
impl<A: HalApi> Access<QuerySet<A>> for Sampler<A> {}
impl<A: HalApi> Access<ShaderModule<A>> for Device<A> {}
impl<A: HalApi> Access<ShaderModule<A>> for BindGroupLayout<A> {}
impl<A: HalApi> Access<Buffer<A>> for Root {}
impl<A: HalApi> Access<Buffer<A>> for Device<A> {}
impl<A: HalApi> Access<Buffer<A>> for BindGroupLayout<A> {}
impl<A: HalApi> Access<Buffer<A>> for BindGroup<A> {}
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<Texture<A>> for Root {}
impl<A: HalApi> Access<Texture<A>> for Device<A> {}
impl<A: HalApi> Access<Texture<A>> for Buffer<A> {}
impl<A: HalApi> Access<TextureView<A>> for Root {}
impl<A: HalApi> Access<TextureView<A>> for Device<A> {}
impl<A: HalApi> Access<TextureView<A>> for Texture<A> {}
impl<A: HalApi> Access<Sampler<A>> for Root {}
impl<A: HalApi> Access<Sampler<A>> for Device<A> {}
impl<A: HalApi> Access<Sampler<A>> for TextureView<A> {}
#[cfg(debug_assertions)]
thread_local! {
@ -614,7 +626,7 @@ impl HubReport {
}
}
pub struct Hub<A: hal::Api, F: GlobalIdentityHandlerFactory> {
pub struct Hub<A: HalApi, F: GlobalIdentityHandlerFactory> {
pub adapters: Registry<Adapter<A>, id::AdapterId, F>,
pub devices: Registry<Device<A>, id::DeviceId, F>,
pub pipeline_layouts: Registry<PipelineLayout<A>, id::PipelineLayoutId, F>,
@ -622,7 +634,7 @@ pub struct Hub<A: hal::Api, F: GlobalIdentityHandlerFactory> {
pub bind_group_layouts: Registry<BindGroupLayout<A>, id::BindGroupLayoutId, F>,
pub bind_groups: Registry<BindGroup<A>, id::BindGroupId, F>,
pub command_buffers: Registry<CommandBuffer<A>, id::CommandBufferId, F>,
pub render_bundles: Registry<RenderBundle, id::RenderBundleId, F>,
pub render_bundles: Registry<RenderBundle<A>, id::RenderBundleId, F>,
pub render_pipelines: Registry<RenderPipeline<A>, id::RenderPipelineId, F>,
pub compute_pipelines: Registry<ComputePipeline<A>, id::ComputePipelineId, F>,
pub query_sets: Registry<QuerySet<A>, id::QuerySetId, F>,
@ -1008,6 +1020,25 @@ pub trait HalApi: hal::Api {
fn get_surface_mut(surface: &mut Surface) -> &mut HalSurface<Self>;
}
impl HalApi for hal::api::Empty {
const VARIANT: Backend = Backend::Empty;
fn create_instance_from_hal(_: &str, _: Self::Instance) -> Instance {
unimplemented!("called empty api")
}
fn instance_as_hal(_: &Instance) -> Option<&Self::Instance> {
unimplemented!("called empty api")
}
fn hub<G: GlobalIdentityHandlerFactory>(_: &Global<G>) -> &Hub<Self, G> {
unimplemented!("called empty api")
}
fn get_surface(_: &Surface) -> &HalSurface<Self> {
unimplemented!("called empty api")
}
fn get_surface_mut(_: &mut Surface) -> &mut HalSurface<Self> {
unimplemented!("called empty api")
}
}
#[cfg(vulkan)]
impl HalApi for hal::api::Vulkan {
const VARIANT: Backend = Backend::Vulkan;

View File

@ -64,9 +64,9 @@ impl<T> From<SerialId> for Id<T> {
}
impl<T> Id<T> {
#[cfg(test)]
pub(crate) fn dummy() -> Valid<Self> {
Valid(Id(NonZeroId::new(1).unwrap(), PhantomData))
#[allow(dead_code)]
pub(crate) fn dummy(index: u32) -> Valid<Self> {
Valid(Id::zip(index, 1, Backend::Empty))
}
pub fn backend(self) -> Backend {
@ -135,7 +135,7 @@ pub(crate) struct Valid<I>(pub I);
/// Most `wgpu-core` clients should not use this trait. Unusual clients that
/// need to construct `Id` values directly, or access their components, like the
/// WGPU recording player, may use this trait to do so.
pub trait TypedId {
pub trait TypedId: Copy {
fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self;
fn unzip(self) -> (Index, Epoch, Backend);
}
@ -184,7 +184,7 @@ pub type CommandBufferId = Id<crate::command::CommandBuffer<Dummy>>;
pub type RenderPassEncoderId = *mut crate::command::RenderPass;
pub type ComputePassEncoderId = *mut crate::command::ComputePass;
pub type RenderBundleEncoderId = *mut crate::command::RenderBundleEncoder;
pub type RenderBundleId = Id<crate::command::RenderBundle>;
pub type RenderBundleId = Id<crate::command::RenderBundle<Dummy>>;
pub type QuerySetId = Id<crate::resource::QuerySet<Dummy>>;
#[test]

View File

@ -26,7 +26,7 @@ pub(crate) fn has_copy_partial_init_tracker_coverage(
impl From<TextureSelector> for TextureInitRange {
fn from(selector: TextureSelector) -> Self {
TextureInitRange {
mip_range: selector.levels,
mip_range: selector.mips,
layer_range: selector.layers,
}
}

View File

@ -494,6 +494,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
#[cfg(dx12)]
/// # Safety
///
/// The visual must be valid and able to be used to make a swapchain with.
pub unsafe fn instance_create_surface_from_visual(
&self,
visual: *mut std::ffi::c_void,

View File

@ -4,6 +4,8 @@
*/
#![allow(
// It is much clearer to assert negative conditions with eq! false
clippy::bool_assert_comparison,
// We use loops for getting early-out of scope without closures.
clippy::never_loop,
// We don't use syntax sugar where it's not necessary.
@ -16,6 +18,8 @@
clippy::new_without_default,
// Needless updates are more scaleable, easier to play with features.
clippy::needless_update,
// Need many arguments for some core functions to be able to re-use code in many situations.
clippy::too_many_arguments,
// For some reason `rustc` can warn about these in const generics even
// though they are required.
unused_braces,

View File

@ -174,7 +174,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
initialization_status: TextureInitTracker::new(1, 1),
full_range: track::TextureSelector {
layers: 0..1,
levels: 0..1,
mips: 0..1,
},
life_guard: LifeGuard::new("<Surface>"),
clear_mode: resource::TextureClearMode::RenderPass {
@ -187,20 +187,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let id = fid.assign(texture, &mut token);
{
use track::ResourceState as _;
// register it in the device tracker as uninitialized
let mut trackers = device.trackers.lock();
let mut ts = track::TextureState::default();
let _ = ts.change(
id,
track::TextureSelector {
layers: 0..1,
levels: 0..1,
},
trackers.textures.insert_single(
id.0,
ref_count.clone(),
hal::TextureUses::UNINITIALIZED,
None,
);
let _ = trackers.textures.init(id, ref_count.clone(), ts);
}
if present.acquired_texture.is_some() {
@ -273,6 +266,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// The texture ID got added to the device tracker by `submit()`,
// and now we are moving it away.
log::debug!(
"Removing swapchain texture {:?} from the device tracker",
texture_id.value
);
device.trackers.lock().textures.remove(texture_id.value);
let (texture, _) = hub.textures.unregister(texture_id.value.0, &mut token);

View File

@ -3,7 +3,7 @@ use crate::{
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token},
id::{AdapterId, DeviceId, SurfaceId, TextureId, Valid},
init_tracker::{BufferInitTracker, TextureInitTracker},
track::{TextureSelector, DUMMY_SELECTOR},
track::TextureSelector,
validation::MissingBufferUsageError,
Label, LifeGuard, RefCount, Stored,
};
@ -149,12 +149,6 @@ impl<A: hal::Api> Resource for Buffer<A> {
}
}
impl<A: hal::Api> Borrow<()> for Buffer<A> {
fn borrow(&self) -> &() {
&DUMMY_SELECTOR
}
}
pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>>;
#[derive(Debug)]
@ -448,12 +442,6 @@ impl<A: hal::Api> Resource for TextureView<A> {
}
}
impl<A: hal::Api> Borrow<()> for TextureView<A> {
fn borrow(&self) -> &() {
&DUMMY_SELECTOR
}
}
/// Describes a [`Sampler`]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
@ -530,11 +518,6 @@ impl<A: hal::Api> Resource for Sampler<A> {
}
}
impl<A: hal::Api> Borrow<()> for Sampler<A> {
fn borrow(&self) -> &() {
&DUMMY_SELECTOR
}
}
#[derive(Clone, Debug, Error)]
pub enum CreateQuerySetError {
#[error(transparent)]
@ -565,12 +548,6 @@ impl<A: hal::Api> Resource for QuerySet<A> {
}
}
impl<A: hal::Api> Borrow<()> for QuerySet<A> {
fn borrow(&self) -> &() {
&DUMMY_SELECTOR
}
}
#[derive(Clone, Debug, Error)]
pub enum DestroyError {
#[error("resource is invalid")]

View File

@ -1,236 +1,778 @@
use super::{PendingTransition, ResourceState, Unit};
use crate::id::{BufferId, Valid};
/*! Buffer Trackers
*
* Buffers are represented by a single state for the whole resource,
* a 16 bit bitflag of buffer usages. Because there is only ever
* one subresource, they have no selector.
!*/
use std::{borrow::Cow, marker::PhantomData, vec::Drain};
use super::PendingTransition;
use crate::{
hub,
id::{BufferId, TypedId, Valid},
resource::Buffer,
track::{
invalid_resource_state, iterate_bitvec_indices, skip_barrier, ResourceMetadata,
ResourceMetadataProvider, ResourceUses, UsageConflict,
},
LifeGuard, RefCount,
};
use hal::BufferUses;
pub(crate) type BufferState = Unit<BufferUses>;
impl ResourceUses for BufferUses {
const EXCLUSIVE: Self = Self::EXCLUSIVE;
impl PendingTransition<BufferState> {
fn collapse(self) -> Result<BufferUses, Self> {
if self.usage.start.is_empty()
|| self.usage.start == self.usage.end
|| !BufferUses::EXCLUSIVE.intersects(self.usage.start | self.usage.end)
{
Ok(self.usage.start | self.usage.end)
} else {
Err(self)
}
}
}
impl Default for BufferState {
fn default() -> Self {
Self {
first: None,
last: BufferUses::empty(),
}
}
}
impl BufferState {
pub fn with_usage(usage: BufferUses) -> Self {
Unit::new(usage)
}
}
impl ResourceState for BufferState {
type Id = BufferId;
type Selector = ();
type Usage = BufferUses;
fn query(&self, _selector: Self::Selector) -> Option<Self::Usage> {
Some(self.last)
fn bits(self) -> u16 {
Self::bits(&self)
}
fn change(
&mut self,
id: Valid<Self::Id>,
_selector: Self::Selector,
usage: Self::Usage,
output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>> {
let old = self.last;
if old != usage || !BufferUses::ORDERED.contains(usage) {
let pending = PendingTransition {
id,
selector: (),
usage: old..usage,
};
*self = match output {
None => {
assert_eq!(
self.first, None,
"extending a state that is already a transition"
);
Unit::new(pending.collapse()?)
}
Some(transitions) => {
transitions.push(pending);
Unit {
first: self.first.or(Some(old)),
last: usage,
}
}
};
fn all_ordered(self) -> bool {
Self::ORDERED.contains(self)
}
fn any_exclusive(self) -> bool {
self.intersects(Self::EXCLUSIVE)
}
}
/// Stores all the buffers that a bind group stores.
pub(crate) struct BufferBindGroupState<A: hub::HalApi> {
buffers: Vec<(Valid<BufferId>, RefCount, BufferUses)>,
_phantom: PhantomData<A>,
}
impl<A: hub::HalApi> BufferBindGroupState<A> {
pub fn new() -> Self {
Self {
buffers: Vec::new(),
_phantom: PhantomData,
}
}
/// Optimize the buffer bind group state by sorting it by ID.
///
/// When this list of states is merged into a tracker, the memory
/// accesses will be in a constant assending order.
pub(crate) fn optimize(&mut self) {
self.buffers
.sort_unstable_by_key(|&(id, _, _)| id.0.unzip().0);
}
/// Returns a list of all buffers tracked. May contain duplicates.
pub fn used(&self) -> impl Iterator<Item = Valid<BufferId>> + '_ {
self.buffers.iter().map(|&(id, _, _)| id)
}
/// Adds the given resource with the given state.
pub fn add_single<'a>(
&mut self,
storage: &'a hub::Storage<Buffer<A>, BufferId>,
id: BufferId,
state: BufferUses,
) -> Option<&'a Buffer<A>> {
let buffer = storage.get(id).ok()?;
self.buffers
.push((Valid(id), buffer.life_guard.add_ref(), state));
Some(buffer)
}
}
/// Stores all buffer state within a single usage scope.
#[derive(Debug)]
pub(crate) struct BufferUsageScope<A: hub::HalApi> {
state: Vec<BufferUses>,
metadata: ResourceMetadata<A>,
}
impl<A: hub::HalApi> BufferUsageScope<A> {
pub fn new() -> Self {
Self {
state: Vec::new(),
metadata: ResourceMetadata::new(),
}
}
fn debug_assert_in_bounds(&self, index: usize) {
debug_assert!(index < self.state.len());
self.metadata.debug_assert_in_bounds(index);
}
/// Sets the size of all the vectors inside the tracker.
///
/// Must be called with the highest possible Buffer ID before
/// all unsafe functions are called.
pub fn set_size(&mut self, size: usize) {
self.state.resize(size, BufferUses::empty());
self.metadata.set_size(size);
}
/// Extend the vectors to let the given index be valid.
fn allow_index(&mut self, index: usize) {
if index >= self.state.len() {
self.set_size(index + 1);
}
}
/// Returns a list of all buffers tracked.
pub fn used(&self) -> impl Iterator<Item = Valid<BufferId>> + '_ {
self.metadata.used()
}
/// Merge the list of buffer states in the given bind group into this usage scope.
///
/// If any of the resulting states is invalid, stops the merge and returns a usage
/// conflict with the details of the invalid state.
///
/// Because bind groups do not check if the union of all their states is valid,
/// this method is allowed to return Err on the first bind group bound.
///
/// # Safety
///
/// [`Self::set_size`] must be called with the maximum possible Buffer ID before this
/// method is called.
pub unsafe fn merge_bind_group(
&mut self,
bind_group: &BufferBindGroupState<A>,
) -> Result<(), UsageConflict> {
for &(id, ref ref_count, state) in &bind_group.buffers {
let (index32, epoch, _) = id.0.unzip();
let index = index32 as usize;
insert_or_merge(
None,
None,
&mut self.state,
&mut self.metadata,
index32,
index,
BufferStateProvider::Direct { state },
ResourceMetadataProvider::Direct {
epoch,
ref_count: Cow::Borrowed(ref_count),
},
)?;
}
Ok(())
}
fn merge(
&mut self,
id: Valid<Self::Id>,
other: &Self,
output: Option<&mut Vec<PendingTransition<Self>>>,
) -> Result<(), PendingTransition<Self>> {
let old = self.last;
let new = other.port();
if old == new && BufferUses::ORDERED.contains(new) {
if output.is_some() && self.first.is_none() {
*self = Unit {
first: Some(old),
last: other.last,
};
}
} else {
let pending = PendingTransition {
id,
selector: (),
usage: old..new,
};
*self = match output {
None => {
assert_eq!(
self.first, None,
"extending a state that is already a transition"
);
Unit::new(pending.collapse()?)
}
Some(transitions) => {
transitions.push(pending);
Unit {
first: self.first.or(Some(old)),
last: other.last,
}
}
/// Merge the list of buffer states in the given usage scope into this UsageScope.
///
/// If any of the resulting states is invalid, stops the merge and returns a usage
/// conflict with the details of the invalid state.
///
/// If the given tracker uses IDs higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn merge_usage_scope(&mut self, scope: &Self) -> Result<(), UsageConflict> {
let incoming_size = scope.state.len();
if incoming_size > self.state.len() {
self.set_size(incoming_size);
}
for index in iterate_bitvec_indices(&scope.metadata.owned) {
self.debug_assert_in_bounds(index);
scope.debug_assert_in_bounds(index);
unsafe {
insert_or_merge(
None,
None,
&mut self.state,
&mut self.metadata,
index as u32,
index,
BufferStateProvider::Indirect {
state: &scope.state,
},
ResourceMetadataProvider::Indirect {
metadata: &scope.metadata,
},
)?;
};
}
Ok(())
}
fn optimize(&mut self) {}
}
/// Merge a single state into the UsageScope.
///
/// If the resulting state is invalid, returns a usage
/// conflict with the details of the invalid state.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn merge_single<'a>(
&mut self,
storage: &'a hub::Storage<Buffer<A>, BufferId>,
id: BufferId,
new_state: BufferUses,
) -> Result<&'a Buffer<A>, UsageConflict> {
let buffer = storage
.get(id)
.map_err(|_| UsageConflict::BufferInvalid { id })?;
#[cfg(test)]
mod test {
use super::*;
use crate::id::Id;
let (index32, epoch, _) = id.unzip();
let index = index32 as usize;
#[test]
fn change_extend() {
let mut bs = Unit {
first: None,
last: BufferUses::INDEX,
};
let id = Id::dummy();
assert_eq!(
bs.change(id, (), BufferUses::STORAGE_WRITE, None),
Err(PendingTransition {
id,
selector: (),
usage: BufferUses::INDEX..BufferUses::STORAGE_WRITE,
}),
);
bs.change(id, (), BufferUses::VERTEX, None).unwrap();
bs.change(id, (), BufferUses::INDEX, None).unwrap();
assert_eq!(bs, Unit::new(BufferUses::VERTEX | BufferUses::INDEX));
}
self.allow_index(index);
#[test]
fn change_replace() {
let mut bs = Unit {
first: None,
last: BufferUses::STORAGE_WRITE,
};
let id = Id::dummy();
let mut list = Vec::new();
bs.change(id, (), BufferUses::VERTEX, Some(&mut list))
.unwrap();
assert_eq!(
&list,
&[PendingTransition {
id,
selector: (),
usage: BufferUses::STORAGE_WRITE..BufferUses::VERTEX,
}],
);
assert_eq!(
bs,
Unit {
first: Some(BufferUses::STORAGE_WRITE),
last: BufferUses::VERTEX,
}
);
self.debug_assert_in_bounds(index);
list.clear();
bs.change(id, (), BufferUses::STORAGE_WRITE, Some(&mut list))
.unwrap();
assert_eq!(
&list,
&[PendingTransition {
id,
selector: (),
usage: BufferUses::VERTEX..BufferUses::STORAGE_WRITE,
}],
);
assert_eq!(
bs,
Unit {
first: Some(BufferUses::STORAGE_WRITE),
last: BufferUses::STORAGE_WRITE,
}
);
}
unsafe {
insert_or_merge(
Some(&buffer.life_guard),
None,
&mut self.state,
&mut self.metadata,
index32,
index,
BufferStateProvider::Direct { state: new_state },
ResourceMetadataProvider::Resource { epoch },
)?;
}
#[test]
fn merge_replace() {
let mut bs = Unit {
first: None,
last: BufferUses::empty(),
};
let other_smooth = Unit {
first: Some(BufferUses::empty()),
last: BufferUses::COPY_DST,
};
let id = Id::dummy();
let mut list = Vec::new();
bs.merge(id, &other_smooth, Some(&mut list)).unwrap();
assert!(list.is_empty());
assert_eq!(
bs,
Unit {
first: Some(BufferUses::empty()),
last: BufferUses::COPY_DST,
}
);
let other_rough = Unit {
first: Some(BufferUses::empty()),
last: BufferUses::UNIFORM,
};
bs.merge(id, &other_rough, Some(&mut list)).unwrap();
assert_eq!(
&list,
&[PendingTransition {
id,
selector: (),
usage: BufferUses::COPY_DST..BufferUses::empty(),
}],
);
assert_eq!(
bs,
Unit {
first: Some(BufferUses::empty()),
last: BufferUses::UNIFORM,
}
);
Ok(buffer)
}
}
/// Stores all buffer state within a command buffer or device.
pub(crate) struct BufferTracker<A: hub::HalApi> {
start: Vec<BufferUses>,
end: Vec<BufferUses>,
metadata: ResourceMetadata<A>,
temp: Vec<PendingTransition<BufferUses>>,
}
impl<A: hub::HalApi> BufferTracker<A> {
pub fn new() -> Self {
Self {
start: Vec::new(),
end: Vec::new(),
metadata: ResourceMetadata::new(),
temp: Vec::new(),
}
}
fn debug_assert_in_bounds(&self, index: usize) {
debug_assert!(index < self.start.len());
debug_assert!(index < self.end.len());
self.metadata.debug_assert_in_bounds(index);
}
/// Sets the size of all the vectors inside the tracker.
///
/// Must be called with the highest possible Buffer ID before
/// all unsafe functions are called.
pub fn set_size(&mut self, size: usize) {
self.start.resize(size, BufferUses::empty());
self.end.resize(size, BufferUses::empty());
self.metadata.set_size(size);
}
/// Extend the vectors to let the given index be valid.
fn allow_index(&mut self, index: usize) {
if index >= self.start.len() {
self.set_size(index + 1);
}
}
/// Returns a list of all buffers tracked.
pub fn used(&self) -> impl Iterator<Item = Valid<BufferId>> + '_ {
self.metadata.used()
}
/// Drains all currently pending transitions.
pub fn drain(&mut self) -> Drain<'_, PendingTransition<BufferUses>> {
self.temp.drain(..)
}
/// Inserts a single buffer and its state into the resource tracker.
///
/// If the resource already exists in the tracker, this will panic.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn insert_single(&mut self, id: Valid<BufferId>, ref_count: RefCount, state: BufferUses) {
let (index32, epoch, _) = id.0.unzip();
let index = index32 as usize;
self.allow_index(index);
self.debug_assert_in_bounds(index);
unsafe {
let currently_owned = self.metadata.owned.get(index).unwrap_unchecked();
if currently_owned {
panic!("Tried to insert buffer already tracked");
}
insert(
None,
Some(&mut self.start),
&mut self.end,
&mut self.metadata,
index,
BufferStateProvider::Direct { state },
None,
ResourceMetadataProvider::Direct {
epoch,
ref_count: Cow::Owned(ref_count),
},
)
}
}
/// Sets the state of a single buffer.
///
/// If a transition is needed to get the buffer into the given state, that transition
/// is returned. No more than one transition is needed.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn set_single<'a>(
&mut self,
storage: &'a hub::Storage<Buffer<A>, BufferId>,
id: BufferId,
state: BufferUses,
) -> Option<(&'a Buffer<A>, Option<PendingTransition<BufferUses>>)> {
let value = storage.get(id).ok()?;
let (index32, epoch, _) = id.unzip();
let index = index32 as usize;
self.allow_index(index);
self.debug_assert_in_bounds(index);
unsafe {
insert_or_barrier_update(
Some(&value.life_guard),
Some(&mut self.start),
&mut self.end,
&mut self.metadata,
index32,
index,
BufferStateProvider::Direct { state },
None,
ResourceMetadataProvider::Resource { epoch },
&mut self.temp,
)
};
debug_assert!(self.temp.len() <= 1);
Some((value, self.temp.pop()))
}
/// Sets the given state for all buffers in the given tracker.
///
/// If a transition is needed to get the buffers into the needed state,
/// those transitions are stored within the tracker. A subsequent
/// call to [`Self::drain`] is needed to get those transitions.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn set_from_tracker(&mut self, tracker: &Self) {
let incoming_size = tracker.start.len();
if incoming_size > self.start.len() {
self.set_size(incoming_size);
}
for index in iterate_bitvec_indices(&tracker.metadata.owned) {
self.debug_assert_in_bounds(index);
tracker.debug_assert_in_bounds(index);
unsafe {
insert_or_barrier_update(
None,
Some(&mut self.start),
&mut self.end,
&mut self.metadata,
index as u32,
index,
BufferStateProvider::Indirect {
state: &tracker.start,
},
Some(BufferStateProvider::Indirect {
state: &tracker.end,
}),
ResourceMetadataProvider::Indirect {
metadata: &tracker.metadata,
},
&mut self.temp,
)
}
}
}
/// Sets the given state for all buffers in the given UsageScope.
///
/// If a transition is needed to get the buffers into the needed state,
/// those transitions are stored within the tracker. A subsequent
/// call to [`Self::drain`] is needed to get those transitions.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn set_from_usage_scope(&mut self, scope: &BufferUsageScope<A>) {
let incoming_size = scope.state.len();
if incoming_size > self.start.len() {
self.set_size(incoming_size);
}
for index in iterate_bitvec_indices(&scope.metadata.owned) {
self.debug_assert_in_bounds(index);
scope.debug_assert_in_bounds(index);
unsafe {
insert_or_barrier_update(
None,
Some(&mut self.start),
&mut self.end,
&mut self.metadata,
index as u32,
index,
BufferStateProvider::Indirect {
state: &scope.state,
},
None,
ResourceMetadataProvider::Indirect {
metadata: &scope.metadata,
},
&mut self.temp,
)
}
}
}
/// Iterates through all buffers in the given bind group and adopts
/// the state given for those buffers in the UsageScope. It also
/// removes all touched buffers from the usage scope.
///
/// If a transition is needed to get the buffers into the needed state,
/// those transitions are stored within the tracker. A subsequent
/// call to [`Self::drain`] is needed to get those transitions.
///
/// This is a really funky method used by Compute Passes to generate
/// barriers after a call to dispatch without needing to iterate
/// over all elements in the usage scope. We use each the
/// bind group as a source of which IDs to look at. The bind groups
/// must have first been added to the usage scope.
///
/// # Safety
///
/// [`Self::set_size`] must be called with the maximum possible Buffer ID before this
/// method is called.
pub unsafe fn set_and_remove_from_usage_scope_sparse(
&mut self,
scope: &mut BufferUsageScope<A>,
bind_group_state: &BufferBindGroupState<A>,
) {
let incoming_size = scope.state.len();
if incoming_size > self.start.len() {
self.set_size(incoming_size);
}
for &(id, ref ref_count, _) in bind_group_state.buffers.iter() {
let (index32, epoch, _) = id.0.unzip();
let index = index32 as usize;
scope.debug_assert_in_bounds(index);
if !scope.metadata.owned.get(index).unwrap_unchecked() {
continue;
}
insert_or_barrier_update(
None,
Some(&mut self.start),
&mut self.end,
&mut self.metadata,
index as u32,
index,
BufferStateProvider::Indirect {
state: &scope.state,
},
None,
ResourceMetadataProvider::Direct {
epoch,
ref_count: Cow::Borrowed(ref_count),
},
&mut self.temp,
);
scope.metadata.reset(index);
}
}
/// Removes the given resource from the tracker iff we have the last reference to the
/// resource and the epoch matches.
///
/// Returns true if the resource was removed.
///
/// If the ID is higher than the length of internal vectors,
/// false will be returned.
pub fn remove_abandoned(&mut self, id: Valid<BufferId>) -> bool {
let (index32, epoch, _) = id.0.unzip();
let index = index32 as usize;
if index > self.metadata.owned.len() {
return false;
}
self.debug_assert_in_bounds(index);
unsafe {
if self.metadata.owned.get(index).unwrap_unchecked() {
let existing_epoch = self.metadata.epochs.get_unchecked_mut(index);
let existing_ref_count = self.metadata.ref_counts.get_unchecked_mut(index);
if *existing_epoch == epoch
&& existing_ref_count.as_mut().unwrap_unchecked().load() == 1
{
self.metadata.reset(index);
return true;
}
}
}
false
}
}
/// Source of Buffer State.
#[derive(Debug, Clone)]
enum BufferStateProvider<'a> {
/// Get a state that was provided directly.
Direct { state: BufferUses },
/// Get a state from an an array of states.
Indirect { state: &'a [BufferUses] },
}
impl BufferStateProvider<'_> {
/// Gets the state from the provider, given a resource ID index.
///
/// # Safety
///
/// Index must be in bounds for the indirect source iff this is in the indirect state.
#[inline(always)]
unsafe fn get_state(&self, index: usize) -> BufferUses {
match *self {
BufferStateProvider::Direct { state } => state,
BufferStateProvider::Indirect { state } => {
debug_assert!(index < state.len());
*state.get_unchecked(index)
}
}
}
}
/// Does an insertion operation if the index isn't tracked
/// in the current metadata, otherwise merges the given state
/// with the current state. If the merging would cause
/// a conflict, returns that usage conflict.
///
/// # Safety
///
/// Indexes must be valid indexes into all arrays passed in
/// to this function, either directly or via metadata or provider structs.
#[inline(always)]
unsafe fn insert_or_merge<A: hub::HalApi>(
life_guard: Option<&LifeGuard>,
start_states: Option<&mut [BufferUses]>,
current_states: &mut [BufferUses],
resource_metadata: &mut ResourceMetadata<A>,
index32: u32,
index: usize,
state_provider: BufferStateProvider<'_>,
metadata_provider: ResourceMetadataProvider<'_, A>,
) -> Result<(), UsageConflict> {
let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked();
if !currently_owned {
insert(
life_guard,
start_states,
current_states,
resource_metadata,
index,
state_provider,
None,
metadata_provider,
);
return Ok(());
}
merge(
current_states,
index32,
index,
state_provider,
metadata_provider,
)
}
/// If the resource isn't tracked
/// - Inserts the given resource.
/// - Uses the `start_state_provider` to populate `start_states`
/// - Uses either `end_state_provider` or `start_state_provider`
/// to populate `current_states`.
/// If the resource is tracked
/// - Inserts barriers from the state in `current_states`
/// to the state provided by `start_state_provider`.
/// - Updates the `current_states` with either the state from
/// `end_state_provider` or `start_state_provider`.
///
/// Any barriers are added to the barrier vector.
///
/// # Safety
///
/// Indexes must be valid indexes into all arrays passed in
/// to this function, either directly or via metadata or provider structs.
#[inline(always)]
unsafe fn insert_or_barrier_update<A: hub::HalApi>(
life_guard: Option<&LifeGuard>,
start_states: Option<&mut [BufferUses]>,
current_states: &mut [BufferUses],
resource_metadata: &mut ResourceMetadata<A>,
index32: u32,
index: usize,
start_state_provider: BufferStateProvider<'_>,
end_state_provider: Option<BufferStateProvider<'_>>,
metadata_provider: ResourceMetadataProvider<'_, A>,
barriers: &mut Vec<PendingTransition<BufferUses>>,
) {
let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked();
if !currently_owned {
insert(
life_guard,
start_states,
current_states,
resource_metadata,
index,
start_state_provider,
end_state_provider,
metadata_provider,
);
return;
}
let update_state_provider = end_state_provider.unwrap_or_else(|| start_state_provider.clone());
barrier(
current_states,
index32,
index,
start_state_provider,
barriers,
);
update(current_states, index, update_state_provider);
}
#[inline(always)]
unsafe fn insert<A: hub::HalApi>(
life_guard: Option<&LifeGuard>,
start_states: Option<&mut [BufferUses]>,
current_states: &mut [BufferUses],
resource_metadata: &mut ResourceMetadata<A>,
index: usize,
start_state_provider: BufferStateProvider<'_>,
end_state_provider: Option<BufferStateProvider<'_>>,
metadata_provider: ResourceMetadataProvider<'_, A>,
) {
let new_start_state = start_state_provider.get_state(index);
let new_end_state = end_state_provider.map_or(new_start_state, |p| p.get_state(index));
// This should only ever happen with a wgpu bug, but let's just double
// check that resource states don't have any conflicts.
debug_assert_eq!(invalid_resource_state(new_start_state), false);
debug_assert_eq!(invalid_resource_state(new_end_state), false);
log::trace!("\tbuf {index}: insert {new_start_state:?}..{new_end_state:?}");
if let Some(&mut ref mut start_state) = start_states {
*start_state.get_unchecked_mut(index) = new_start_state;
}
*current_states.get_unchecked_mut(index) = new_end_state;
let (epoch, ref_count) = metadata_provider.get_own(life_guard, index);
resource_metadata.owned.set(index, true);
*resource_metadata.epochs.get_unchecked_mut(index) = epoch;
*resource_metadata.ref_counts.get_unchecked_mut(index) = Some(ref_count);
}
#[inline(always)]
unsafe fn merge<A: hub::HalApi>(
current_states: &mut [BufferUses],
index32: u32,
index: usize,
state_provider: BufferStateProvider<'_>,
metadata_provider: ResourceMetadataProvider<'_, A>,
) -> Result<(), UsageConflict> {
let current_state = current_states.get_unchecked_mut(index);
let new_state = state_provider.get_state(index);
let merged_state = *current_state | new_state;
if invalid_resource_state(merged_state) {
return Err(UsageConflict::from_buffer(
BufferId::zip(index32, metadata_provider.get_epoch(index), A::VARIANT),
*current_state,
new_state,
));
}
log::trace!("\tbuf {index32}: merge {current_state:?} + {new_state:?}");
*current_state = merged_state;
Ok(())
}
#[inline(always)]
unsafe fn barrier(
current_states: &mut [BufferUses],
index32: u32,
index: usize,
state_provider: BufferStateProvider<'_>,
barriers: &mut Vec<PendingTransition<BufferUses>>,
) {
let current_state = *current_states.get_unchecked(index);
let new_state = state_provider.get_state(index);
if skip_barrier(current_state, new_state) {
return;
}
barriers.push(PendingTransition {
id: index32,
selector: (),
usage: current_state..new_state,
});
log::trace!("\tbuf {index32}: transition {current_state:?} -> {new_state:?}");
}
#[inline(always)]
unsafe fn update(
current_states: &mut [BufferUses],
index: usize,
state_provider: BufferStateProvider<'_>,
) {
let current_state = current_states.get_unchecked_mut(index);
let new_state = state_provider.get_state(index);
*current_state = new_state;
}

File diff suppressed because it is too large Load Diff

View File

@ -2,25 +2,19 @@
//TODO: consider getting rid of it.
use smallvec::SmallVec;
use std::{cmp::Ordering, fmt::Debug, iter, ops::Range, slice::Iter};
use std::{fmt::Debug, iter, ops::Range};
/// Structure that keeps track of a I -> T mapping,
/// optimized for a case where keys of the same values
/// are often grouped together linearly.
#[derive(Clone, Debug, PartialEq)]
pub struct RangedStates<I, T> {
pub(crate) struct RangedStates<I, T> {
/// List of ranges, each associated with a singe value.
/// Ranges of keys have to be non-intersecting and ordered.
ranges: SmallVec<[(Range<I>, T); 1]>,
}
impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
pub fn empty() -> Self {
Self {
ranges: SmallVec::new(),
}
}
impl<I: Copy + Ord, T: Copy + PartialEq> RangedStates<I, T> {
pub fn from_range(range: Range<I>, value: T) -> Self {
Self {
ranges: iter::once((range, value)).collect(),
@ -35,20 +29,12 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
}
}
/// Clear all the ranges.
pub fn clear(&mut self) {
self.ranges.clear();
pub fn iter(&self) -> impl Iterator<Item = &(Range<I>, T)> + Clone {
self.ranges.iter()
}
/// Append a range.
///
/// Assumes that the object is being constructed from a set of
/// ranges, and they are given in the ascending order of their keys.
pub fn append(&mut self, index: Range<I>, value: T) {
if let Some(last) = self.ranges.last() {
debug_assert!(last.0.end <= index.start);
}
self.ranges.push((index, value));
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (Range<I>, T)> {
self.ranges.iter_mut()
}
/// Check that all the ranges are non-intersecting and ordered.
@ -64,7 +50,6 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
}
/// Merge the neighboring ranges together, where possible.
#[allow(clippy::suspicious_operation_groupings)]
pub fn coalesce(&mut self) {
let mut num_removed = 0;
let mut iter = self.ranges.iter_mut();
@ -86,25 +71,18 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
}
}
/// Check if all intersecting ranges have the same value, which is returned.
///
/// Returns `None` if no intersections are detected.
/// Returns `Some(Err)` if the intersected values are inconsistent.
pub fn query<U: PartialEq>(
&self,
index: &Range<I>,
fun: impl Fn(&T) -> U,
) -> Option<Result<U, ()>> {
let mut result = None;
for &(ref range, ref value) in self.ranges.iter() {
if range.end > index.start && range.start < index.end {
let old = result.replace(fun(value));
if old.is_some() && old != result {
return Some(Err(()));
}
}
}
result.map(Ok)
pub fn iter_filter<'a>(
&'a self,
range: &'a Range<I>,
) -> impl Iterator<Item = (Range<I>, &T)> + 'a {
self.ranges
.iter()
.filter(move |&&(ref inner, ..)| inner.end > range.start && inner.start < range.end)
.map(move |&(ref inner, ref v)| {
let new_range = inner.start.max(range.start)..inner.end.min(range.end);
(new_range, v)
})
}
/// Split the storage ranges in such a way that there is a linear subset of
@ -176,112 +154,12 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
clone.check_sanity();
result
}
/// Produce an iterator that merges two instances together.
///
/// Each range in the returned iterator is a subset of a range in either
/// `self` or `other`, and the value returned as a `Range` from `self` to `other`.
pub fn merge<'a>(&'a self, other: &'a Self, base: I) -> Merge<'a, I, T> {
Merge {
base,
sa: self.ranges.iter().peekable(),
sb: other.ranges.iter().peekable(),
}
}
}
/// A custom iterator that goes through two `RangedStates` and process a merge.
#[derive(Debug)]
pub struct Merge<'a, I, T> {
base: I,
sa: iter::Peekable<Iter<'a, (Range<I>, T)>>,
sb: iter::Peekable<Iter<'a, (Range<I>, T)>>,
}
impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> {
type Item = (Range<I>, Range<Option<T>>);
fn next(&mut self) -> Option<Self::Item> {
match (self.sa.peek(), self.sb.peek()) {
// we have both streams
(Some(&&(ref ra, va)), Some(&&(ref rb, vb))) => {
let (range, usage) = if ra.start < self.base {
// in the middle of the left stream
let (end, end_value) = if self.base == rb.start {
// right stream is starting
debug_assert!(self.base < ra.end);
(rb.end, Some(vb))
} else {
// right hasn't started yet
debug_assert!(self.base < rb.start);
(rb.start, None)
};
(self.base..ra.end.min(end), Some(va)..end_value)
} else if rb.start < self.base {
// in the middle of the right stream
let (end, start_value) = if self.base == ra.start {
// left stream is starting
debug_assert!(self.base < rb.end);
(ra.end, Some(va))
} else {
// left hasn't started yet
debug_assert!(self.base < ra.start);
(ra.start, None)
};
(self.base..rb.end.min(end), start_value..Some(vb))
} else {
// no active streams
match ra.start.cmp(&rb.start) {
// both are starting
Ordering::Equal => (ra.start..ra.end.min(rb.end), Some(va)..Some(vb)),
// only left is starting
Ordering::Less => (ra.start..rb.start.min(ra.end), Some(va)..None),
// only right is starting
Ordering::Greater => (rb.start..ra.start.min(rb.end), None..Some(vb)),
}
};
self.base = range.end;
if ra.end == range.end {
let _ = self.sa.next();
}
if rb.end == range.end {
let _ = self.sb.next();
}
Some((range, usage))
}
// only right stream
(None, Some(&&(ref rb, vb))) => {
let range = self.base.max(rb.start)..rb.end;
self.base = rb.end;
let _ = self.sb.next();
Some((range, None..Some(vb)))
}
// only left stream
(Some(&&(ref ra, va)), None) => {
let range = self.base.max(ra.start)..ra.end;
self.base = ra.end;
let _ = self.sa.next();
Some((range, Some(va)..None))
}
// done
(None, None) => None,
}
}
}
#[cfg(test)]
mod test {
//TODO: randomized/fuzzy testing
use super::RangedStates;
use std::{fmt::Debug, ops::Range};
fn easy_merge<T: PartialEq + Copy + Debug>(
ra: &[(Range<usize>, T)],
rb: &[(Range<usize>, T)],
) -> Vec<(Range<usize>, Range<Option<T>>)> {
RangedStates::from_slice(ra)
.merge(&RangedStates::from_slice(rb), 0)
.collect()
}
#[test]
fn sane_good() {
@ -311,14 +189,6 @@ mod test {
assert_eq!(rs.ranges.as_slice(), &[(1..5, 9), (5..7, 1), (8..9, 1),]);
}
#[test]
fn query() {
let rs = RangedStates::from_slice(&[(1..4, 1u8), (5..7, 2)]);
assert_eq!(rs.query(&(0..1), |v| *v), None);
assert_eq!(rs.query(&(1..3), |v| *v), Some(Ok(1)));
assert_eq!(rs.query(&(1..6), |v| *v), Some(Err(())));
}
#[test]
fn isolate() {
let rs = RangedStates::from_slice(&[(1..4, 9u8), (4..5, 9), (5..7, 1), (8..9, 1)]);
@ -333,104 +203,4 @@ mod test {
&[(6..7, 1), (7..8, 0), (8..9, 1),]
);
}
#[test]
fn merge_same() {
assert_eq!(
&easy_merge(&[(1..4, 0u8),], &[(1..4, 2u8),],),
&[(1..4, Some(0)..Some(2)),]
);
}
#[test]
fn merge_empty() {
assert_eq!(
&easy_merge(&[(1..2, 0u8),], &[],),
&[(1..2, Some(0)..None),]
);
assert_eq!(
&easy_merge(&[], &[(3..4, 1u8),],),
&[(3..4, None..Some(1)),]
);
}
#[test]
fn merge_separate() {
assert_eq!(
&easy_merge(&[(1..2, 0u8), (5..6, 1u8),], &[(2..4, 2u8),],),
&[
(1..2, Some(0)..None),
(2..4, None..Some(2)),
(5..6, Some(1)..None),
]
);
}
#[test]
fn merge_subset() {
assert_eq!(
&easy_merge(&[(1..6, 0u8),], &[(2..4, 2u8),],),
&[
(1..2, Some(0)..None),
(2..4, Some(0)..Some(2)),
(4..6, Some(0)..None),
]
);
assert_eq!(
&easy_merge(&[(2..4, 0u8),], &[(1..4, 2u8),],),
&[(1..2, None..Some(2)), (2..4, Some(0)..Some(2)),]
);
}
#[test]
fn merge_all() {
assert_eq!(
&easy_merge(&[(1..4, 0u8), (5..8, 1u8),], &[(2..6, 2u8), (7..9, 3u8),],),
&[
(1..2, Some(0)..None),
(2..4, Some(0)..Some(2)),
(4..5, None..Some(2)),
(5..6, Some(1)..Some(2)),
(6..7, Some(1)..None),
(7..8, Some(1)..Some(3)),
(8..9, None..Some(3)),
]
);
}
#[test]
fn merge_complex() {
assert_eq!(
&easy_merge(
&[
(0..8, 0u8),
(8..9, 1),
(9..16, 2),
(16..17, 3),
(17..118, 4),
(118..119, 5),
(119..124, 6),
(124..125, 7),
(125..512, 8),
],
&[(15..16, 10u8), (51..52, 11), (126..127, 12),],
),
&[
(0..8, Some(0)..None),
(8..9, Some(1)..None),
(9..15, Some(2)..None),
(15..16, Some(2)..Some(10)),
(16..17, Some(3)..None),
(17..51, Some(4)..None),
(51..52, Some(4)..Some(11)),
(52..118, Some(4)..None),
(118..119, Some(5)..None),
(119..124, Some(6)..None),
(124..125, Some(7)..None),
(125..126, Some(8)..None),
(126..127, Some(8)..Some(12)),
(127..512, Some(8)..None),
]
);
}
}

View File

@ -0,0 +1,209 @@
/*! Stateless Trackers
*
* Stateless trackers don't have any state, so make no
* distinction between a usage scope and a full tracker.
!*/
use std::marker::PhantomData;
use crate::{
hub,
id::{TypedId, Valid},
track::{iterate_bitvec_indices, ResourceMetadata},
RefCount,
};
/// Stores all the resources that a bind group stores.
pub(crate) struct StatelessBindGroupSate<T, Id: TypedId> {
resources: Vec<(Valid<Id>, RefCount)>,
_phantom: PhantomData<T>,
}
impl<T: hub::Resource, Id: TypedId> StatelessBindGroupSate<T, Id> {
pub fn new() -> Self {
Self {
resources: Vec::new(),
_phantom: PhantomData,
}
}
/// Optimize the buffer bind group state by sorting it by ID.
///
/// When this list of states is merged into a tracker, the memory
/// accesses will be in a constant assending order.
pub(crate) fn optimize(&mut self) {
self.resources
.sort_unstable_by_key(|&(id, _)| id.0.unzip().0);
}
/// Returns a list of all resources tracked. May contain duplicates.
pub fn used(&self) -> impl Iterator<Item = Valid<Id>> + '_ {
self.resources.iter().map(|&(id, _)| id)
}
/// Adds the given resource.
pub fn add_single<'a>(&mut self, storage: &'a hub::Storage<T, Id>, id: Id) -> Option<&'a T> {
let resource = storage.get(id).ok()?;
self.resources
.push((Valid(id), resource.life_guard().add_ref()));
Some(resource)
}
}
/// Stores all resource state within a command buffer or device.
pub(crate) struct StatelessTracker<A: hub::HalApi, T, Id: TypedId> {
metadata: ResourceMetadata<A>,
_phantom: PhantomData<(T, Id)>,
}
impl<A: hub::HalApi, T: hub::Resource, Id: TypedId> StatelessTracker<A, T, Id> {
pub fn new() -> Self {
Self {
metadata: ResourceMetadata::new(),
_phantom: PhantomData,
}
}
fn debug_assert_in_bounds(&self, index: usize) {
self.metadata.debug_assert_in_bounds(index);
}
/// Sets the size of all the vectors inside the tracker.
///
/// Must be called with the highest possible Resource ID of this type
/// before all unsafe functions are called.
pub fn set_size(&mut self, size: usize) {
self.metadata.set_size(size);
}
/// Extend the vectors to let the given index be valid.
fn allow_index(&mut self, index: usize) {
if index >= self.metadata.owned.len() {
self.set_size(index + 1);
}
}
/// Returns a list of all resources tracked.
pub fn used(&self) -> impl Iterator<Item = Valid<Id>> + '_ {
self.metadata.used()
}
/// Inserts a single resource into the resource tracker.
///
/// If the resource already exists in the tracker, it will be overwritten.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn insert_single(&mut self, id: Valid<Id>, ref_count: RefCount) {
let (index32, epoch, _) = id.0.unzip();
let index = index32 as usize;
self.allow_index(index);
self.debug_assert_in_bounds(index);
unsafe {
*self.metadata.epochs.get_unchecked_mut(index) = epoch;
*self.metadata.ref_counts.get_unchecked_mut(index) = Some(ref_count);
self.metadata.owned.set(index, true);
}
}
/// Adds the given resource to the tracker.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn add_single<'a>(&mut self, storage: &'a hub::Storage<T, Id>, id: Id) -> Option<&'a T> {
let item = storage.get(id).ok()?;
let (index32, epoch, _) = id.unzip();
let index = index32 as usize;
self.allow_index(index);
self.debug_assert_in_bounds(index);
unsafe {
*self.metadata.epochs.get_unchecked_mut(index) = epoch;
*self.metadata.ref_counts.get_unchecked_mut(index) = Some(item.life_guard().add_ref());
self.metadata.owned.set(index, true);
}
Some(item)
}
/// Adds the given resources from the given tracker.
///
/// If the ID is higher than the length of internal vectors,
/// the vectors will be extended. A call to set_size is not needed.
pub fn add_from_tracker(&mut self, other: &Self) {
let incoming_size = other.metadata.owned.len();
if incoming_size > self.metadata.owned.len() {
self.set_size(incoming_size);
}
for index in iterate_bitvec_indices(&other.metadata.owned) {
self.debug_assert_in_bounds(index);
other.debug_assert_in_bounds(index);
unsafe {
let previously_owned = self.metadata.owned.get(index).unwrap_unchecked();
if !previously_owned {
self.metadata.owned.set(index, true);
let other_ref_count = other
.metadata
.ref_counts
.get_unchecked(index)
.clone()
.unwrap_unchecked();
*self.metadata.ref_counts.get_unchecked_mut(index) = Some(other_ref_count);
let epoch = *other.metadata.epochs.get_unchecked(index);
*self.metadata.epochs.get_unchecked_mut(index) = epoch;
}
}
}
}
/// Removes the given resource from the tracker iff we have the last reference to the
/// resource and the epoch matches.
///
/// Returns true if the resource was removed.
///
/// If the ID is higher than the length of internal vectors,
/// false will be returned.
pub fn remove_abandoned(&mut self, id: Valid<Id>) -> bool {
let (index32, epoch, _) = id.0.unzip();
let index = index32 as usize;
if index > self.metadata.owned.len() {
return false;
}
self.debug_assert_in_bounds(index);
unsafe {
if self.metadata.owned.get(index).unwrap_unchecked() {
let existing_epoch = self.metadata.epochs.get_unchecked_mut(index);
let existing_ref_count = self.metadata.ref_counts.get_unchecked_mut(index);
if *existing_epoch == epoch
&& existing_ref_count.as_mut().unwrap_unchecked().load() == 1
{
self.metadata.reset(index);
return true;
}
}
}
false
}
}

File diff suppressed because it is too large Load Diff

View File

@ -685,7 +685,7 @@ impl<A: hal::Api> Example<A> {
let target_barrier1 = hal::TextureBarrier {
texture: surface_tex.borrow(),
range: wgt::ImageSubresourceRange::default(),
usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::empty(),
usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::PRESENT,
};
unsafe {
ctx.encoder.end_render_pass();

View File

@ -287,7 +287,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
StateAfter: s1,
};
self.temp.barriers.push(raw);
} else if barrier.usage.start == crate::BufferUses::STORAGE_WRITE {
} else if barrier.usage.start == crate::BufferUses::STORAGE_READ_WRITE {
let mut raw = d3d12::D3D12_RESOURCE_BARRIER {
Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
@ -382,7 +382,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
}
}
}
} else if barrier.usage.start == crate::TextureUses::STORAGE_WRITE {
} else if barrier.usage.start == crate::TextureUses::STORAGE_READ_WRITE {
let mut raw = d3d12::D3D12_RESOURCE_BARRIER {
Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE,

View File

@ -3,7 +3,7 @@ use winapi::um::{d3d12, d3dcommon};
pub fn map_buffer_usage_to_resource_flags(usage: crate::BufferUses) -> d3d12::D3D12_RESOURCE_FLAGS {
let mut flags = 0;
if usage.contains(crate::BufferUses::STORAGE_WRITE) {
if usage.contains(crate::BufferUses::STORAGE_READ_WRITE) {
flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
flags
@ -33,7 +33,7 @@ pub fn map_texture_usage_to_resource_flags(
flags |= d3d12::D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
}
}
if usage.contains(crate::TextureUses::STORAGE_WRITE) {
if usage.contains(crate::TextureUses::STORAGE_READ_WRITE) {
flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
@ -130,7 +130,7 @@ pub fn map_buffer_usage_to_state(usage: crate::BufferUses) -> d3d12::D3D12_RESOU
if usage.intersects(Bu::VERTEX | Bu::UNIFORM) {
state |= d3d12::D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
}
if usage.intersects(Bu::STORAGE_WRITE) {
if usage.intersects(Bu::STORAGE_READ_WRITE) {
state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
} else if usage.intersects(Bu::STORAGE_READ) {
state |= d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
@ -170,7 +170,7 @@ pub fn map_texture_usage_to_state(usage: crate::TextureUses) -> d3d12::D3D12_RES
if usage.intersects(Tu::DEPTH_STENCIL_WRITE) {
state |= d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE;
}
if usage.intersects(Tu::STORAGE_READ | Tu::STORAGE_WRITE) {
if usage.intersects(Tu::STORAGE_READ | Tu::STORAGE_READ_WRITE) {
state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
}
state

View File

@ -428,7 +428,7 @@ impl crate::Device<super::Api> for super::Device {
|| !desc.usage.intersects(
crate::TextureUses::RESOURCE
| crate::TextureUses::STORAGE_READ
| crate::TextureUses::STORAGE_WRITE,
| crate::TextureUses::STORAGE_READ_WRITE,
) {
auxil::dxgi::conv::map_texture_format(desc.format)
} else {
@ -516,10 +516,9 @@ impl crate::Device<super::Api> for super::Device {
} else {
None
},
handle_uav: if desc
.usage
.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE)
{
handle_uav: if desc.usage.intersects(
crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE,
) {
let raw_desc = view_desc.to_uav();
let handle = self.srv_uav_pool.lock().alloc_handle();
self.raw.CreateUnorderedAccessView(

View File

@ -230,7 +230,11 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
}
for bar in barriers {
// GLES only synchronizes storage -> anything explicitly
if !bar.usage.start.contains(crate::BufferUses::STORAGE_WRITE) {
if !bar
.usage
.start
.contains(crate::BufferUses::STORAGE_READ_WRITE)
{
continue;
}
self.cmd_buffer
@ -253,7 +257,11 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
let mut combined_usage = crate::TextureUses::empty();
for bar in barriers {
// GLES only synchronizes storage -> anything explicitly
if !bar.usage.start.contains(crate::TextureUses::STORAGE_WRITE) {
if !bar
.usage
.start
.contains(crate::TextureUses::STORAGE_READ_WRITE)
{
continue;
}
// unlike buffers, there is no need for a concrete texture

View File

@ -790,9 +790,9 @@ impl super::Queue {
if usage.intersects(crate::BufferUses::MAP_READ | crate::BufferUses::MAP_WRITE) {
flags |= glow::BUFFER_UPDATE_BARRIER_BIT;
}
if usage
.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE)
{
if usage.intersects(
crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE,
) {
flags |= glow::SHADER_STORAGE_BARRIER_BIT;
}
gl.memory_barrier(flags);
@ -803,7 +803,7 @@ impl super::Queue {
flags |= glow::TEXTURE_FETCH_BARRIER_BIT;
}
if usage.intersects(
crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE,
crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE,
) {
flags |= glow::SHADER_IMAGE_ACCESS_BARRIER_BIT;
}

View File

@ -628,53 +628,77 @@ bitflags!(
bitflags::bitflags! {
/// Similar to `wgt::BufferUsages` but for internal use.
pub struct BufferUses: u32 {
pub struct BufferUses: u16 {
/// The argument to a read-only mapping.
const MAP_READ = 1 << 0;
/// The argument to a write-only mapping.
const MAP_WRITE = 1 << 1;
/// The source of a hardware copy.
const COPY_SRC = 1 << 2;
/// The destination of a hardware copy.
const COPY_DST = 1 << 3;
/// The index buffer used for drawing.
const INDEX = 1 << 4;
/// A vertex buffer used for drawing.
const VERTEX = 1 << 5;
/// A uniform buffer bound in a bind group.
const UNIFORM = 1 << 6;
/// A read-only storage buffer used in a bind group.
const STORAGE_READ = 1 << 7;
const STORAGE_WRITE = 1 << 8;
/// A read-write or write-only buffer used in a bind group.
const STORAGE_READ_WRITE = 1 << 8;
/// The indirect or count buffer in a indirect draw or dispatch.
const INDIRECT = 1 << 9;
/// The combination of usages that can be used together (read-only).
/// The combination of states that a buffer may be in _at the same time_.
const INCLUSIVE = Self::MAP_READ.bits | Self::COPY_SRC.bits |
Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits |
Self::STORAGE_READ.bits | Self::INDIRECT.bits;
/// The combination of exclusive usages (write-only and read-write).
/// These usages may still show up with others, but can't automatically be combined.
const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_WRITE.bits;
/// The combination of states that a buffer must exclusively be in.
const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_READ_WRITE.bits;
/// The combination of all usages that the are guaranteed to be be ordered by the hardware.
/// If a usage is not ordered, then even if it doesn't change between draw calls, there
/// still need to be pipeline barriers inserted for synchronization.
/// If a usage is ordered, then if the buffer state doesn't change between draw calls, there
/// are no barriers needed for synchronization.
const ORDERED = Self::INCLUSIVE.bits | Self::MAP_WRITE.bits;
}
}
bitflags::bitflags! {
/// Similar to `wgt::TextureUsages` but for internal use.
pub struct TextureUses: u32 {
const COPY_SRC = 1 << 0;
const COPY_DST = 1 << 1;
const RESOURCE = 1 << 2;
const COLOR_TARGET = 1 << 3;
const DEPTH_STENCIL_READ = 1 << 4;
const DEPTH_STENCIL_WRITE = 1 << 5;
const STORAGE_READ = 1 << 6;
const STORAGE_WRITE = 1 << 7;
/// The combination of usages that can be used together (read-only).
pub struct TextureUses: u16 {
/// The texture is in unknown state.
const UNINITIALIZED = 1 << 0;
/// Ready to present image to the surface.
const PRESENT = 1 << 1;
/// The source of a hardware copy.
const COPY_SRC = 1 << 2;
/// The destination of a hardware copy.
const COPY_DST = 1 << 3;
/// Read-only sampled or fetched resource.
const RESOURCE = 1 << 4;
/// The color target of a renderpass.
const COLOR_TARGET = 1 << 5;
/// Read-only depth stencil usage.
const DEPTH_STENCIL_READ = 1 << 6;
/// Read-write depth stencil usage
const DEPTH_STENCIL_WRITE = 1 << 7;
/// Read-only storage buffer usage. Corresponds to a UAV in d3d, so is exclusive, despite being read only.
const STORAGE_READ = 1 << 8;
/// Read-write or write-only storage buffer usage.
const STORAGE_READ_WRITE = 1 << 9;
/// The combination of states that a texture may be in _at the same time_.
const INCLUSIVE = Self::COPY_SRC.bits | Self::RESOURCE.bits | Self::DEPTH_STENCIL_READ.bits;
/// The combination of exclusive usages (write-only and read-write).
/// These usages may still show up with others, but can't automatically be combined.
const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_WRITE.bits;
/// The combination of states that a texture must exclusively be in.
const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_READ_WRITE.bits | Self::PRESENT.bits;
/// The combination of all usages that the are guaranteed to be be ordered by the hardware.
/// If a usage is not ordered, then even if it doesn't change between draw calls, there
/// still need to be pipeline barriers inserted for synchronization.
/// If a usage is ordered, then if the texture state doesn't change between draw calls, there
/// are no barriers needed for synchronization.
const ORDERED = Self::INCLUSIVE.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits;
//TODO: remove this
const UNINITIALIZED = 0xFFFF;
/// Flag used by the wgpu-core texture tracker to say a texture is in different states for every sub-resource
const COMPLEX = 1 << 10;
/// Flag used by the wgpu-core texture tracker to say that the tracker does not know the state of the sub-resource.
/// This is different from UNINITIALIZED as that says the tracker does know, but the texture has not been initialized.
const UNKNOWN = 1 << 11;
}
}

View File

@ -9,11 +9,13 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> mtl::MTLTextureUsage {
);
mtl_usage.set(
mtl::MTLTextureUsage::ShaderRead,
usage.intersects(Tu::RESOURCE | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ),
usage.intersects(
Tu::RESOURCE | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ | Tu::STORAGE_READ_WRITE,
),
);
mtl_usage.set(
mtl::MTLTextureUsage::ShaderWrite,
usage.intersects(Tu::STORAGE_WRITE),
usage.intersects(Tu::STORAGE_READ_WRITE),
);
mtl_usage

View File

@ -200,7 +200,7 @@ pub fn derive_image_layout(
vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
}
_ => {
if usage.is_empty() {
if usage == crate::TextureUses::PRESENT {
vk::ImageLayout::PRESENT_SRC_KHR
} else if is_color {
vk::ImageLayout::GENERAL
@ -230,7 +230,7 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> vk::ImageUsageFlags {
) {
flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
}
if usage.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE) {
if usage.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE) {
flags |= vk::ImageUsageFlags::STORAGE;
}
flags
@ -276,12 +276,12 @@ pub fn map_texture_usage_to_barrier(
stages |= shader_stages;
access |= vk::AccessFlags::SHADER_READ;
}
if usage.contains(crate::TextureUses::STORAGE_WRITE) {
if usage.contains(crate::TextureUses::STORAGE_READ_WRITE) {
stages |= shader_stages;
access |= vk::AccessFlags::SHADER_WRITE;
access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE;
}
if usage == crate::TextureUses::UNINITIALIZED || usage.is_empty() {
if usage == crate::TextureUses::UNINITIALIZED || usage == crate::TextureUses::PRESENT {
(
vk::PipelineStageFlags::TOP_OF_PIPE,
vk::AccessFlags::empty(),
@ -309,7 +309,7 @@ pub fn map_vk_image_usage(usage: vk::ImageUsageFlags) -> crate::TextureUses {
bits |= crate::TextureUses::DEPTH_STENCIL_READ | crate::TextureUses::DEPTH_STENCIL_WRITE;
}
if usage.contains(vk::ImageUsageFlags::STORAGE) {
bits |= crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE;
bits |= crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE;
}
bits
}
@ -457,7 +457,7 @@ pub fn map_buffer_usage(usage: crate::BufferUses) -> vk::BufferUsageFlags {
if usage.contains(crate::BufferUses::UNIFORM) {
flags |= vk::BufferUsageFlags::UNIFORM_BUFFER;
}
if usage.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE) {
if usage.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE) {
flags |= vk::BufferUsageFlags::STORAGE_BUFFER;
}
if usage.contains(crate::BufferUses::INDEX) {
@ -505,9 +505,9 @@ pub fn map_buffer_usage_to_barrier(
stages |= shader_stages;
access |= vk::AccessFlags::SHADER_READ;
}
if usage.intersects(crate::BufferUses::STORAGE_WRITE) {
if usage.intersects(crate::BufferUses::STORAGE_READ_WRITE) {
stages |= shader_stages;
access |= vk::AccessFlags::SHADER_WRITE;
access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE;
}
if usage.contains(crate::BufferUses::INDEX) {
stages |= vk::PipelineStageFlags::VERTEX_INPUT;

View File

@ -1413,7 +1413,7 @@ impl crate::Device<super::Api> for super::Device {
unsafe fn destroy_shader_module(&self, module: super::ShaderModule) {
match module {
super::ShaderModule::Raw(raw) => {
let _ = self.shared.raw.destroy_shader_module(raw, None);
self.shared.raw.destroy_shader_module(raw, None);
}
super::ShaderModule::Intermediate { .. } => {}
}