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] [dependencies]
arrayvec = "0.7" arrayvec = "0.7"
bitflags = "1.0" bitflags = "1.0"
bit-vec = "0.6"
codespan-reporting = "0.11" codespan-reporting = "0.11"
copyless = "0.1" copyless = "0.1"
fxhash = "0.2" fxhash = "0.2"

View File

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

View File

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

View File

@ -4,13 +4,12 @@ use std::{num::NonZeroU32, ops::Range};
use crate::device::trace::Command as TraceCommand; use crate::device::trace::Command as TraceCommand;
use crate::{ use crate::{
command::CommandBuffer, command::CommandBuffer,
device::Device,
get_lowest_common_denom, get_lowest_common_denom,
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token}, hub::{self, Global, GlobalIdentityHandlerFactory, HalApi, Token},
id::{BufferId, CommandEncoderId, DeviceId, TextureId, Valid}, id::{BufferId, CommandEncoderId, DeviceId, TextureId, Valid},
init_tracker::{MemoryInitKind, TextureInitRange}, init_tracker::{MemoryInitKind, TextureInitRange},
resource::{Texture, TextureClearMode}, resource::{Texture, TextureClearMode},
track::{ResourceTracker, TextureSelector, TextureState}, track::{TextureSelector, TextureTracker},
}; };
use hal::{auxil::align_to, CommandEncoder as _}; use hal::{auxil::align_to, CommandEncoder as _};
@ -90,8 +89,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (dst_buffer, dst_pending) = cmd_buf let (dst_buffer, dst_pending) = cmd_buf
.trackers .trackers
.buffers .buffers
.use_replace(&*buffer_guard, dst, (), hal::BufferUses::COPY_DST) .set_single(&*buffer_guard, dst, hal::BufferUses::COPY_DST)
.map_err(ClearError::InvalidBuffer)?; .ok_or(ClearError::InvalidBuffer(dst))?;
let dst_raw = dst_buffer let dst_raw = dst_buffer
.raw .raw
.as_ref() .as_ref()
@ -139,7 +138,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
let cmd_buf_raw = cmd_buf.encoder.open(); let cmd_buf_raw = cmd_buf.encoder.open();
unsafe { 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); cmd_buf_raw.clear_buffer(dst_raw, offset..end);
} }
Ok(()) Ok(())
@ -191,13 +190,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
// Check if subresource level range is valid // Check if subresource level range is valid
let subresource_level_end = match subresource_range.mip_level_count { let subresource_level_end = match subresource_range.mip_level_count {
Some(count) => subresource_range.base_mip_level + count.get(), 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 if dst_texture.full_range.mips.start > subresource_range.base_mip_level
|| dst_texture.full_range.levels.end < subresource_level_end || dst_texture.full_range.mips.end < subresource_level_end
{ {
return Err(ClearError::InvalidTextureLevelRange { 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_base_mip_level: subresource_range.base_mip_level,
subresource_mip_level_count: subresource_range.mip_level_count, 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( clear_texture(
&*texture_guard,
Valid(dst), Valid(dst),
dst_texture,
TextureInitRange { TextureInitRange {
mip_range: subresource_range.base_mip_level..subresource_level_end, mip_range: subresource_range.base_mip_level..subresource_level_end,
layer_range: subresource_range.base_array_layer..subresource_layer_end, layer_range: subresource_range.base_array_layer..subresource_layer_end,
}, },
cmd_buf.encoder.open(), cmd_buf.encoder.open(),
&mut cmd_buf.trackers.textures, &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_id: Valid<TextureId>,
dst_texture: &Texture<A>,
range: TextureInitRange, range: TextureInitRange,
encoder: &mut A::CommandEncoder, encoder: &mut A::CommandEncoder,
texture_tracker: &mut ResourceTracker<TextureState>, texture_tracker: &mut TextureTracker<A>,
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>,
alignments: &hal::Alignments, alignments: &hal::Alignments,
zero_buffer: &A::Buffer, zero_buffer: &A::Buffer,
) -> Result<(), ClearError> { ) -> Result<(), ClearError> {
let dst_texture = &storage[dst_texture_id];
let dst_raw = dst_texture let dst_raw = dst_texture
.inner .inner
.as_raw() .as_raw()
@ -277,7 +262,7 @@ pub(crate) fn clear_texture_no_device<A: hal::Api>(
}; };
let selector = TextureSelector { let selector = TextureSelector {
levels: range.mip_range.clone(), mips: range.mip_range.clone(),
layers: range.layer_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. // 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. // 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() { let dst_barrier = texture_tracker
texture_tracker.change_replace(dst_texture_id, ref_count, selector, clear_usage) .set_single(storage, dst_texture_id.0, selector, clear_usage)
} else { .unwrap()
texture_tracker.change_replace_tracked(dst_texture_id, selector, clear_usage) .1
} .map(|pending| pending.into_hal(dst_texture));
.map(|pending| pending.into_hal(dst_texture));
unsafe { unsafe {
encoder.transition_textures(dst_barrier); encoder.transition_textures(dst_barrier.into_iter());
} }
// Record actual clearing // Record actual clearing

View File

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

View File

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

View File

@ -4,11 +4,11 @@ use hal::CommandEncoder;
use crate::{ use crate::{
device::Device, device::Device,
hub::Storage, hub::{HalApi, Storage},
id::{self, TextureId}, id::{self, TextureId},
init_tracker::*, init_tracker::*,
resource::{Buffer, Texture}, resource::{Buffer, Texture},
track::{ResourceTracker, TextureState, TrackerSet}, track::{TextureTracker, Tracker},
FastHashMap, 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. // 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! // Takes care of barriers as well!
pub(crate) fn fixup_discarded_surfaces< pub(crate) fn fixup_discarded_surfaces<
A: hal::Api, A: HalApi,
InitIter: Iterator<Item = TextureSurfaceDiscard>, InitIter: Iterator<Item = TextureSurfaceDiscard>,
>( >(
inits: InitIter, inits: InitIter,
encoder: &mut A::CommandEncoder, encoder: &mut A::CommandEncoder,
texture_guard: &Storage<Texture<A>, TextureId>, texture_guard: &Storage<Texture<A>, TextureId>,
texture_tracker: &mut ResourceTracker<TextureState>, texture_tracker: &mut TextureTracker<A>,
device: &Device<A>, device: &Device<A>,
) { ) {
for init in inits { for init in inits {
clear_texture( clear_texture(
texture_guard,
id::Valid(init.texture), id::Valid(init.texture),
texture_guard.get(init.texture).unwrap(),
TextureInitRange { TextureInitRange {
mip_range: init.mip_level..(init.mip_level + 1), mip_range: init.mip_level..(init.mip_level + 1),
layer_range: init.layer..(init.layer + 1), layer_range: init.layer..(init.layer + 1),
}, },
encoder, encoder,
texture_tracker, texture_tracker,
device, &device.alignments,
&device.zero_buffer,
) )
.unwrap(); .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 // 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( pub(crate) fn initialize_buffer_memory(
&mut self, &mut self,
device_tracker: &mut TrackerSet, device_tracker: &mut Tracker<A>,
buffer_guard: &mut Storage<Buffer<A>, id::BufferId>, buffer_guard: &mut Storage<Buffer<A>, id::BufferId>,
) -> Result<(), DestroyedBufferError> { ) -> Result<(), DestroyedBufferError> {
// Gather init ranges for each buffer so we can collapse them. // 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. // 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. // 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( let transition = device_tracker
id::Valid(buffer_id), .buffers
(), .set_single(buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
hal::BufferUses::COPY_DST, .unwrap()
); .1;
let buffer = buffer_guard let buffer = buffer_guard
.get_mut(buffer_id) .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))?; let raw_buf = buffer.raw.as_ref().ok_or(DestroyedBufferError(buffer_id))?;
unsafe { unsafe {
self.encoder self.encoder.transition_buffers(
.transition_buffers(transition.map(|pending| pending.into_hal(buffer))); transition
.map(|pending| pending.into_hal(buffer))
.into_iter(),
);
} }
for range in ranges.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 // any textures that are left discarded by this command buffer will be marked as uninitialized
pub(crate) fn initialize_texture_memory( pub(crate) fn initialize_texture_memory(
&mut self, &mut self,
device_tracker: &mut TrackerSet, device_tracker: &mut Tracker<A>,
texture_guard: &mut Storage<Texture<A>, TextureId>, texture_guard: &mut Storage<Texture<A>, TextureId>,
device: &Device<A>, device: &Device<A>,
) -> Result<(), DestroyedTextureError> { ) -> Result<(), DestroyedTextureError> {
@ -274,12 +278,13 @@ impl<A: hal::Api> BakedCommands<A> {
// TODO: Could we attempt some range collapsing here? // TODO: Could we attempt some range collapsing here?
for range in ranges.drain(..) { for range in ranges.drain(..) {
clear_texture( clear_texture(
texture_guard,
id::Valid(texture_use.id), id::Valid(texture_use.id),
&*texture,
range, range,
&mut self.encoder, &mut self.encoder,
&mut device_tracker.textures, &mut device_tracker.textures,
device, &device.alignments,
&device.zero_buffer,
) )
.unwrap(); .unwrap();
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,10 @@
use crate::resource; 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 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), usage.contains(wgt::BufferUsages::UNIFORM),
); );
u.set( u.set(
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE, hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_READ_WRITE,
usage.contains(wgt::BufferUsages::STORAGE), usage.contains(wgt::BufferUsages::STORAGE),
); );
u.set( u.set(
@ -81,7 +85,7 @@ pub fn map_texture_usage(
usage.contains(wgt::TextureUsages::TEXTURE_BINDING), usage.contains(wgt::TextureUsages::TEXTURE_BINDING),
); );
u.set( 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), usage.contains(wgt::TextureUsages::STORAGE_BINDING),
); );
let is_color = aspect.contains(hal::FormatAspects::COLOR); 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 }); 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)); return Err(Tde::InvalidSampleCount(sample_size));
} }

View File

@ -7,7 +7,7 @@ use crate::{
}, },
hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Token}, hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Token},
id, resource, id, resource,
track::TrackerSet, track::{BindGroupStates, RenderBundleScope, Tracker},
RefCount, Stored, SubmissionIndex, RefCount, Stored, SubmissionIndex,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
@ -68,16 +68,20 @@ impl SuspectedResources {
self.query_sets.extend_from_slice(&other.query_sets); 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.buffers.extend(trackers.buffers.used());
self.textures.extend(trackers.textures.used()); self.textures.extend(trackers.textures.used());
self.texture_views.extend(trackers.views.used()); self.texture_views.extend(trackers.views.used());
self.samplers.extend(trackers.samplers.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`. /// Textures can be used in the upcoming submission by `write_texture`.
pub future_suspected_textures: Vec<Stored<id::TextureId>>, 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, pub suspected_resources: SuspectedResources,
/// Resources used by queue submissions still in flight. One entry per /// 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>( pub(super) fn triage_suspected<G: GlobalIdentityHandlerFactory>(
&mut self, &mut self,
hub: &Hub<A, G>, hub: &Hub<A, G>,
trackers: &Mutex<TrackerSet>, trackers: &Mutex<Tracker<A>>,
#[cfg(feature = "trace")] trace: Option<&Mutex<trace::Trace>>, #[cfg(feature = "trace")] trace: Option<&Mutex<trace::Trace>>,
token: &mut Token<super::Device<A>>, 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) { 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) { 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 self.suspected_resources
.bind_group_layouts .bind_group_layouts
@ -670,7 +675,7 @@ impl<A: HalApi> LifetimeTracker<A> {
let mut trackers = trackers.lock(); let mut trackers = trackers.lock();
for id in self.suspected_resources.compute_pipelines.drain(..) { 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); log::debug!("Compute pipeline {:?} will be destroyed", id);
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
if let Some(t) = trace { if let Some(t) = trace {
@ -695,7 +700,7 @@ impl<A: HalApi> LifetimeTracker<A> {
let mut trackers = trackers.lock(); let mut trackers = trackers.lock();
for id in self.suspected_resources.render_pipelines.drain(..) { 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); log::debug!("Render pipeline {:?} will be destroyed", id);
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
if let Some(t) = trace { if let Some(t) = trace {
@ -829,7 +834,7 @@ impl<A: HalApi> LifetimeTracker<A> {
&mut self, &mut self,
hub: &Hub<A, G>, hub: &Hub<A, G>,
raw: &A::Device, raw: &A::Device,
trackers: &Mutex<TrackerSet>, trackers: &Mutex<Tracker<A>>,
token: &mut Token<super::Device<A>>, token: &mut Token<super::Device<A>>,
) -> Vec<super::BufferMapPendingClosure> { ) -> Vec<super::BufferMapPendingClosure> {
if self.ready_to_map.is_empty() { if self.ready_to_map.is_empty() {

View File

@ -8,7 +8,7 @@ use crate::{
TextureInitTracker, TextureInitTrackerAction, TextureInitTracker, TextureInitTrackerAction,
}, },
instance, pipeline, present, resource, instance, pipeline, present, resource,
track::{BufferState, TextureSelector, TextureState, TrackerSet, UsageConflict}, track::{BindGroupStates, TextureSelector, Tracker},
validation::{self, check_buffer_usage, check_texture_usage}, validation::{self, check_buffer_usage, check_texture_usage},
FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, RefCount, Stored, FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, RefCount, Stored,
SubmissionIndex, DOWNLEVEL_ERROR_MESSAGE, SubmissionIndex, DOWNLEVEL_ERROR_MESSAGE,
@ -22,7 +22,7 @@ use smallvec::SmallVec;
use thiserror::Error; use thiserror::Error;
use wgt::{BufferAddress, TextureFormat, TextureViewDimension}; 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; mod life;
pub mod queue; 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. `life_tracker` is locked after `hub.devices`, enforced by the type system
/// 1. `self.trackers` is locked last (unenforced) /// 1. `self.trackers` is locked last (unenforced)
/// 1. `self.trace` 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) raw: A::Device,
pub(crate) adapter_id: Stored<id::AdapterId>, pub(crate) adapter_id: Stored<id::AdapterId>,
pub(crate) queue: A::Queue, pub(crate) queue: A::Queue,
@ -281,7 +281,7 @@ pub struct Device<A: hal::Api> {
/// All live resources allocated with this [`Device`]. /// All live resources allocated with this [`Device`].
/// ///
/// Has to be locked temporarily only (locked last) /// 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 should be locked right after the device and before anything else.
life_tracker: Mutex<life::LifetimeTracker<A>>, life_tracker: Mutex<life::LifetimeTracker<A>>,
/// Temporary storage for resource management functions. Cleared at the end /// Temporary storage for resource management functions. Cleared at the end
@ -306,7 +306,7 @@ pub enum CreateDeviceError {
FailedToCreateZeroBuffer(#[from] DeviceError), 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> { pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
if self.features.contains(feature) { if self.features.contains(feature) {
Ok(()) Ok(())
@ -328,7 +328,6 @@ impl<A: hal::Api> Device<A> {
} }
impl<A: HalApi> Device<A> { impl<A: HalApi> Device<A> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new( pub(crate) fn new(
open: hal::OpenDevice<A>, open: hal::OpenDevice<A>,
adapter_id: Stored<id::AdapterId>, adapter_id: Stored<id::AdapterId>,
@ -394,7 +393,7 @@ impl<A: HalApi> Device<A> {
command_allocator: Mutex::new(com_alloc), command_allocator: Mutex::new(com_alloc),
active_submission_index: 0, active_submission_index: 0,
fence, fence,
trackers: Mutex::new(TrackerSet::new(A::VARIANT)), trackers: Mutex::new(Tracker::new()),
life_tracker: Mutex::new(life::LifetimeTracker::new()), life_tracker: Mutex::new(life::LifetimeTracker::new()),
temp_suspected: life::SuspectedResources::default(), temp_suspected: life::SuspectedResources::default(),
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
@ -495,7 +494,7 @@ impl<A: HalApi> Device<A> {
fn untrack<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>( fn untrack<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>(
&'this mut self, &'this mut self,
hub: &Hub<A, G>, hub: &Hub<A, G>,
trackers: &TrackerSet, trackers: &Tracker<A>,
token: &mut Token<'token, Self>, token: &mut Token<'token, Self>,
) { ) {
self.temp_suspected.clear(); self.temp_suspected.clear();
@ -536,12 +535,12 @@ impl<A: HalApi> Device<A> {
self.temp_suspected.samplers.push(id); 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() { if compute_pipe_guard[id].life_guard.ref_count.is_none() {
self.temp_suspected.compute_pipelines.push(id); 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() { if render_pipe_guard[id].life_guard.ref_count.is_none() {
self.temp_suspected.render_pipelines.push(id); self.temp_suspected.render_pipelines.push(id);
} }
@ -655,7 +654,7 @@ impl<A: HalApi> Device<A> {
desc.array_layer_count(), desc.array_layer_count(),
), ),
full_range: TextureSelector { full_range: TextureSelector {
levels: 0..desc.mip_level_count, mips: 0..desc.mip_level_count,
layers: 0..desc.array_layer_count(), layers: 0..desc.array_layer_count(),
}, },
life_guard: LifeGuard::new(desc.label.borrow_or_default()), life_guard: LifeGuard::new(desc.label.borrow_or_default()),
@ -876,7 +875,7 @@ impl<A: HalApi> Device<A> {
_ => texture.desc.array_layer_count(), _ => 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; let layer_end = texture.full_range.layers.end;
if required_level_count > level_end { if required_level_count > level_end {
return Err(resource::CreateTextureViewError::TooManyMipLevels { return Err(resource::CreateTextureViewError::TooManyMipLevels {
@ -927,7 +926,7 @@ impl<A: HalApi> Device<A> {
.array_layer_count .array_layer_count
.map_or(layer_end, |_| required_layer_count); .map_or(layer_end, |_| required_layer_count);
let selector = TextureSelector { 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, layers: desc.range.base_array_layer..end_layer,
}; };
@ -972,11 +971,11 @@ impl<A: HalApi> Device<A> {
wgt::TextureViewDimension::D3 => { wgt::TextureViewDimension::D3 => {
hal::TextureUses::RESOURCE hal::TextureUses::RESOURCE
| hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_READ
| hal::TextureUses::STORAGE_WRITE | hal::TextureUses::STORAGE_READ_WRITE
} }
_ => hal::TextureUses::all(), _ => 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 hal::TextureUses::RESOURCE
} else { } else {
hal::TextureUses::all() hal::TextureUses::all()
@ -1058,7 +1057,8 @@ impl<A: HalApi> Device<A> {
let anisotropy_clamp = if let Some(clamp) = desc.anisotropy_clamp { let anisotropy_clamp = if let Some(clamp) = desc.anisotropy_clamp {
let clamp = clamp.get(); 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 { if !valid_clamp {
return Err(resource::CreateSamplerError::InvalidClamp(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>( fn create_buffer_binding<'a>(
bb: &binding_model::BufferBinding, bb: &binding_model::BufferBinding,
binding: u32, binding: u32,
@ -1494,7 +1493,7 @@ impl<A: HalApi> Device<A> {
used_buffer_ranges: &mut Vec<BufferInitTrackerAction>, used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,
dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>, dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>, late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
used: &mut TrackerSet, used: &mut BindGroupStates<A>,
storage: &'a Storage<resource::Buffer<A>, id::BufferId>, storage: &'a Storage<resource::Buffer<A>, id::BufferId>,
limits: &wgt::Limits, limits: &wgt::Limits,
) -> Result<hal::BufferBinding<'a, A>, binding_model::CreateBindGroupError> { ) -> Result<hal::BufferBinding<'a, A>, binding_model::CreateBindGroupError> {
@ -1525,7 +1524,7 @@ impl<A: HalApi> Device<A> {
if read_only { if read_only {
hal::BufferUses::STORAGE_READ hal::BufferUses::STORAGE_READ
} else { } else {
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE hal::BufferUses::STORAGE_READ_WRITE
}, },
limits.max_storage_buffer_binding_size, limits.max_storage_buffer_binding_size,
), ),
@ -1543,8 +1542,8 @@ impl<A: HalApi> Device<A> {
let buffer = used let buffer = used
.buffers .buffers
.use_extend(storage, bb.buffer_id, (), internal_use) .add_single(storage, bb.buffer_id, internal_use)
.map_err(|_| Error::InvalidBuffer(bb.buffer_id))?; .ok_or(Error::InvalidBuffer(bb.buffer_id))?;
check_buffer_usage(buffer.usage, pub_usage)?; check_buffer_usage(buffer.usage, pub_usage)?;
let raw_buffer = buffer let raw_buffer = buffer
.raw .raw
@ -1613,26 +1612,26 @@ impl<A: HalApi> Device<A> {
fn create_texture_binding( fn create_texture_binding(
view: &resource::TextureView<A>, view: &resource::TextureView<A>,
texture_guard: &parking_lot::lock_api::RwLockReadGuard< texture_guard: &Storage<resource::Texture<A>, id::TextureId>,
parking_lot::RawRwLock,
Storage<resource::Texture<A>, id::Id<resource::Texture<hal::api::Empty>>>,
>,
internal_use: hal::TextureUses, internal_use: hal::TextureUses,
pub_usage: wgt::TextureUsages, pub_usage: wgt::TextureUsages,
used: &mut TrackerSet, used: &mut BindGroupStates<A>,
used_texture_ranges: &mut Vec<TextureInitTrackerAction>, used_texture_ranges: &mut Vec<TextureInitTrackerAction>,
) -> Result<(), binding_model::CreateBindGroupError> { ) -> Result<(), binding_model::CreateBindGroupError> {
// Careful here: the texture may no longer have its own ref count, // Careful here: the texture may no longer have its own ref count,
// if it was deleted by the user. // if it was deleted by the user.
let texture = &texture_guard[view.parent_id.value]; let texture = used
used.textures .textures
.change_extend( .add_single(
view.parent_id.value, texture_guard,
&view.parent_id.ref_count, view.parent_id.value.0,
view.selector.clone(), view.parent_id.ref_count.clone(),
Some(view.selector.clone()),
internal_use, 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)?; check_texture_usage(texture.desc.usage, pub_usage)?;
used_texture_ranges.push(TextureInitTrackerAction { 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. // it needs to be in BGL iteration order, not BG entry order.
let mut late_buffer_binding_sizes = FastHashMap::default(); let mut late_buffer_binding_sizes = FastHashMap::default();
// fill out the descriptors // 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 (buffer_guard, mut token) = hub.buffers.read(token);
let (texture_guard, mut token) = hub.textures.read(&mut token); //skip 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) => { wgt::BindingType::Sampler(ty) => {
let sampler = used let sampler = used
.samplers .samplers
.use_extend(&*sampler_guard, id, (), ()) .add_single(&*sampler_guard, id)
.map_err(|_| Error::InvalidSampler(id))?; .ok_or(Error::InvalidSampler(id))?;
// Allowed sampler values for filtering and comparison // Allowed sampler values for filtering and comparison
let (allowed_filtering, allowed_comparison) = match ty { let (allowed_filtering, allowed_comparison) = match ty {
@ -1787,8 +1786,8 @@ impl<A: HalApi> Device<A> {
for &id in bindings_array.iter() { for &id in bindings_array.iter() {
let sampler = used let sampler = used
.samplers .samplers
.use_extend(&*sampler_guard, id, (), ()) .add_single(&*sampler_guard, id)
.map_err(|_| Error::InvalidSampler(id))?; .ok_or(Error::InvalidSampler(id))?;
hal_samplers.push(&sampler.raw); hal_samplers.push(&sampler.raw);
} }
@ -1797,8 +1796,8 @@ impl<A: HalApi> Device<A> {
Br::TextureView(id) => { Br::TextureView(id) => {
let view = used let view = used
.views .views
.use_extend(&*texture_view_guard, id, (), ()) .add_single(&*texture_view_guard, id)
.map_err(|_| Error::InvalidTextureView(id))?; .ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) = Self::texture_use_parameters( let (pub_usage, internal_use) = Self::texture_use_parameters(
binding, binding,
decl, decl,
@ -1828,8 +1827,8 @@ impl<A: HalApi> Device<A> {
for &id in bindings_array.iter() { for &id in bindings_array.iter() {
let view = used let view = used
.views .views
.use_extend(&*texture_view_guard, id, (), ()) .add_single(&*texture_view_guard, id)
.map_err(|_| Error::InvalidTextureView(id))?; .ok_or(Error::InvalidTextureView(id))?;
let (pub_usage, internal_use) = let (pub_usage, internal_use) =
Self::texture_use_parameters(binding, decl, view, Self::texture_use_parameters(binding, decl, view,
"SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?; "SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?;
@ -1858,6 +1857,8 @@ impl<A: HalApi> Device<A> {
}); });
} }
used.optimize();
hal_entries.sort_by_key(|entry| entry.binding); hal_entries.sort_by_key(|entry| entry.binding);
for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) { for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
if a.binding == b.binding { 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 { if mip_level_count != 1 {
return Err(Error::InvalidStorageTextureMipLevelCount { return Err(Error::InvalidStorageTextureMipLevelCount {
binding, binding,
@ -2027,7 +2028,7 @@ impl<A: HalApi> Device<A> {
} }
let internal_use = match access { let internal_use = match access {
wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_WRITE, wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_READ_WRITE,
wgt::StorageTextureAccess::ReadOnly => { wgt::StorageTextureAccess::ReadOnly => {
if !view if !view
.format_features .format_features
@ -2047,7 +2048,7 @@ impl<A: HalApi> Device<A> {
return Err(Error::StorageReadNotSupported(view.desc.format)); 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)) Ok((wgt::TextureUsages::STORAGE_BINDING, internal_use))
@ -2551,7 +2552,7 @@ impl<A: HalApi> Device<A> {
let samples = { let samples = {
let sc = desc.multisample.count; 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)); return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
} }
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>) { pub(crate) fn destroy_buffer(&self, buffer: resource::Buffer<A>) {
if let Some(raw) = buffer.raw { if let Some(raw) = buffer.raw {
unsafe { 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"; const TYPE: &'static str = "Device";
fn life_guard(&self) -> &LifeGuard { fn life_guard(&self) -> &LifeGuard {
@ -2981,7 +2982,7 @@ pub struct ImplicitPipelineIds<'a, G: GlobalIdentityHandlerFactory> {
} }
impl<G: GlobalIdentityHandlerFactory> ImplicitPipelineIds<'_, G> { 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 { ImplicitPipelineContext {
root_id: hub.pipeline_layouts.prepare(self.root_id).into_id(), root_id: hub.pipeline_layouts.prepare(self.root_id).into_id(),
group_ids: self group_ids: self
@ -3184,8 +3185,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.trackers .trackers
.lock() .lock()
.buffers .buffers
.init(id, ref_count, BufferState::with_usage(buffer_use)) .insert_single(id, ref_count, buffer_use);
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -3483,19 +3484,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Ok(texture) => texture, Ok(texture) => texture,
Err(error) => break error, 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 ref_count = texture.life_guard.add_ref();
let id = fid.assign(texture, &mut token); let id = fid.assign(texture, &mut token);
log::info!("Created texture {:?} with {:?}", id, desc); log::info!("Created texture {:?} with {:?}", id, desc);
device device.trackers.lock().textures.insert_single(
.trackers id.0,
.lock() ref_count,
.textures hal::TextureUses::UNINITIALIZED,
.init(id, ref_count, TextureState::new(num_levels, num_layers)) );
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -3561,19 +3560,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
texture.initialization_status = TextureInitTracker::new(desc.mip_level_count, 0); 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 ref_count = texture.life_guard.add_ref();
let id = fid.assign(texture, &mut token); let id = fid.assign(texture, &mut token);
log::info!("Created texture {:?} with {:?}", id, desc); log::info!("Created texture {:?} with {:?}", id, desc);
device device.trackers.lock().textures.insert_single(
.trackers id.0,
.lock() ref_count,
.textures hal::TextureUses::UNINITIALIZED,
.init(id, ref_count, TextureState::new(num_levels, num_layers)) );
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -3731,12 +3728,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let ref_count = view.life_guard.add_ref(); let ref_count = view.life_guard.add_ref();
let id = fid.assign(view, &mut token); let id = fid.assign(view, &mut token);
device device.trackers.lock().views.insert_single(id, ref_count);
.trackers
.lock()
.views
.init(id, ref_count, PhantomData)
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -3829,12 +3821,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let ref_count = sampler.life_guard.add_ref(); let ref_count = sampler.life_guard.add_ref();
let id = fid.assign(sampler, &mut token); let id = fid.assign(sampler, &mut token);
device device.trackers.lock().samplers.insert_single(id, ref_count);
.trackers
.lock()
.samplers
.init(id, ref_count, PhantomData)
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -4092,18 +4080,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let ref_count = bind_group.life_guard.add_ref(); let ref_count = bind_group.life_guard.add_ref();
let id = fid.assign(bind_group, &mut token); let id = fid.assign(bind_group, &mut token);
log::debug!( log::debug!("Bind group {:?}", id,);
"Bind group {:?} {:#?}",
id,
hub.bind_groups.read(&mut token).0[id].used
);
device device
.trackers .trackers
.lock() .lock()
.bind_groups .bind_groups
.init(id, ref_count, PhantomData) .insert_single(id, ref_count);
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -4406,16 +4389,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Err(e) => break e, 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 ref_count = render_bundle.life_guard.add_ref();
let id = fid.assign(render_bundle, &mut token); let id = fid.assign(render_bundle, &mut token);
device device.trackers.lock().bundles.insert_single(id, ref_count);
.trackers
.lock()
.bundles
.init(id, ref_count, PhantomData)
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -4494,8 +4472,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.trackers .trackers
.lock() .lock()
.query_sets .query_sets
.init(id, ref_count, PhantomData) .insert_single(id, ref_count);
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -4589,9 +4566,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device device
.trackers .trackers
.lock() .lock()
.render_pipes .render_pipelines
.init(id, ref_count, PhantomData) .insert_single(id, ref_count);
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -4730,9 +4707,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device device
.trackers .trackers
.lock() .lock()
.compute_pipes .compute_pipelines
.init(id, ref_count, PhantomData) .insert_single(id, ref_count);
.unwrap();
return (id.0, None); return (id.0, None);
}; };
@ -5204,16 +5180,20 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}; };
log::debug!("Buffer {:?} map state -> Waiting", buffer_id); 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]; let device = &device_guard[device_id];
device.trackers.lock().buffers.change_replace(
id::Valid(buffer_id),
&ref_count,
(),
internal_use,
);
device device
.lock_life(&mut token) .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> { fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result<StagingData<A>, DeviceError> {
profiling::scope!("prepare_stage"); profiling::scope!("prepare_stage");
let stage_desc = hal::BufferDescriptor { let stage_desc = hal::BufferDescriptor {
@ -286,8 +286,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let mut trackers = device.trackers.lock(); let mut trackers = device.trackers.lock();
let (dst, transition) = trackers let (dst, transition) = trackers
.buffers .buffers
.use_replace(&*buffer_guard, buffer_id, (), hal::BufferUses::COPY_DST) .set_single(&*buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
.map_err(TransferError::InvalidBuffer)?; .ok_or(TransferError::InvalidBuffer(buffer_id))?;
let dst_raw = dst let dst_raw = dst
.raw .raw
.as_ref() .as_ref()
@ -451,9 +451,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.drain(init_layer_range) .drain(init_layer_range)
.collect::<Vec<std::ops::Range<u32>>>() .collect::<Vec<std::ops::Range<u32>>>()
{ {
crate::command::clear_texture_no_device( crate::command::clear_texture(
&*texture_guard,
id::Valid(destination.texture), id::Valid(destination.texture),
&*dst,
TextureInitRange { TextureInitRange {
mip_range: destination.mip_level..(destination.mip_level + 1), mip_range: destination.mip_level..(destination.mip_level + 1),
layer_range, layer_range,
@ -473,13 +473,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (dst, transition) = trackers let (dst, transition) = trackers
.textures .textures
.use_replace( .set_single(
&*texture_guard, &*texture_guard,
destination.texture, destination.texture,
selector, selector,
hal::TextureUses::COPY_DST, hal::TextureUses::COPY_DST,
) )
.unwrap(); .ok_or(TransferError::InvalidTexture(destination.texture))?;
let (hal_copy_size, array_layer_count) = let (hal_copy_size, array_layer_count) =
validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?; 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))?; .ok_or(TransferError::InvalidTexture(destination.texture))?;
unsafe { 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.transition_buffers(iter::once(barrier));
encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, regions); encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, regions);
} }
@ -594,7 +595,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device.active_submission_index += 1; device.active_submission_index += 1;
let submit_index = device.active_submission_index; let submit_index = device.active_submission_index;
let mut active_executions = Vec::new(); 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); 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 //Note: locking the trackers has to be done after the storages
let mut trackers = device.trackers.lock(); 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 //TODO: if multiple command buffers are submitted, we can re-use the last
// native command buffer of the previous chain instead of always creating // native command buffer of the previous chain instead of always creating
// a temporary one, since the chains are not finished. // a temporary one, since the chains are not finished.
// finish all the command buffers first // finish all the command buffers first
for &cmb_id in command_buffer_ids { for &cmb_id in command_buffer_ids {
#[allow(unused_mut)]
let mut cmdbuf = match hub let mut cmdbuf = match hub
.command_buffers .command_buffers
.unregister_locked(cmb_id, &mut *command_buffer_guard) .unregister_locked(cmb_id, &mut *command_buffer_guard)
@ -642,7 +646,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} }
// optimize the tracked states // optimize the tracked states
cmdbuf.trackers.optimize(); // cmdbuf.trackers.optimize();
// update submission IDs // update submission IDs
for id in cmdbuf.trackers.buffers.used() { for id in cmdbuf.trackers.buffers.used() {
@ -669,33 +673,35 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} }
for id in cmdbuf.trackers.textures.used() { for id in cmdbuf.trackers.textures.used() {
let texture = &mut texture_guard[id]; let texture = &mut texture_guard[id];
match texture.inner { let should_extend = match texture.inner {
TextureInner::Native { raw: None } => { TextureInner::Native { raw: None } => {
return Err(QueueSubmitError::DestroyedTexture(id.0)); return Err(QueueSubmitError::DestroyedTexture(id.0));
} }
TextureInner::Native { raw: Some(_) } => {} TextureInner::Native { raw: Some(_) } => false,
TextureInner::Surface { TextureInner::Surface {
ref mut has_work, .. ref mut has_work, ..
} => { } => {
use track::ResourceState as _;
*has_work = true; *has_work = true;
let ref_count = cmdbuf.trackers.textures.get_ref_count(id); true
//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);
} }
} };
if !texture.life_guard.use_at(submit_index) { if !texture.life_guard.use_at(submit_index) {
device.temp_suspected.textures.push(id); 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() { for id in cmdbuf.trackers.views.used() {
if !texture_view_guard[id].life_guard.use_at(submit_index) { 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); sampler_guard[sub_id].life_guard.use_at(submit_index);
} }
} }
assert!(cmdbuf.trackers.samplers.is_empty()); // assert!(cmdbuf.trackers.samplers.is_empty());
for id in cmdbuf.trackers.compute_pipes.used() { for id in cmdbuf.trackers.compute_pipelines.used() {
if !compute_pipe_guard[id].life_guard.use_at(submit_index) { if !compute_pipe_guard[id].life_guard.use_at(submit_index) {
device.temp_suspected.compute_pipelines.push(id); 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) { if !render_pipe_guard[id].life_guard.use_at(submit_index) {
device.temp_suspected.render_pipelines.push(id); 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 // We need to update the submission indices for the contained
// state-less (!) resources as well, excluding the bind groups. // state-less (!) resources as well, excluding the bind groups.
// They don't get deleted too early if the bundle goes out of scope. // They don't get deleted too early if the bundle goes out of scope.
for sub_id in bundle.used.compute_pipes.used() { for sub_id in bundle.used.render_pipelines.used() {
compute_pipe_guard[sub_id].life_guard.use_at(submit_index);
}
for sub_id in bundle.used.render_pipes.used() {
render_pipe_guard[sub_id].life_guard.use_at(submit_index); 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(); let mut baked = cmdbuf.into_baked();
@ -766,11 +772,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?; .map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?;
//Note: stateless trackers are not merged: //Note: stateless trackers are not merged:
// device already knows these resources exist. // device already knows these resources exist.
CommandBuffer::insert_barriers( CommandBuffer::insert_barriers_from_tracker(
&mut baked.encoder, &mut baked.encoder,
&mut *trackers, &mut *trackers,
&baked.trackers.buffers, &baked.trackers,
&baked.trackers.textures,
&*buffer_guard, &*buffer_guard,
&*texture_guard, &*texture_guard,
); );
@ -788,19 +793,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.begin_encoding(Some("(wgpu internal) Present")) .begin_encoding(Some("(wgpu internal) Present"))
.map_err(DeviceError::from)? .map_err(DeviceError::from)?
}; };
let texture_barriers = trackers trackers
.textures .textures
.merge_replace(&used_surface_textures) .set_from_usage_scope(&*texture_guard, &used_surface_textures);
.map(|pending| { let texture_barriers = trackers.textures.drain().map(|pending| {
let tex = &texture_guard[pending.id]; let tex = unsafe { texture_guard.get_unchecked(pending.id) };
pending.into_hal(tex) pending.into_hal(tex)
}); });
let present = unsafe { let present = unsafe {
baked.encoder.transition_textures(texture_barriers); baked.encoder.transition_textures(texture_barriers);
baked.encoder.end_encoding().unwrap() baked.encoder.end_encoding().unwrap()
}; };
baked.list.push(present); baked.list.push(present);
used_surface_textures.clear(); used_surface_textures = track::TextureUsageScope::new();
} }
// done // 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 { let super::Device {
@ -830,6 +835,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (_, mut token) = hub.buffers.read(&mut token); // skip token let (_, mut token) = hub.buffers.read(&mut token); // skip token
let (mut texture_guard, _) = hub.textures.write(&mut 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() { for &id in pending_writes.dst_textures.iter() {
let texture = texture_guard.get_mut(id).unwrap(); let texture = texture_guard.get_mut(id).unwrap();
match texture.inner { match texture.inner {
@ -840,39 +847,39 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
TextureInner::Surface { TextureInner::Surface {
ref mut has_work, .. ref mut has_work, ..
} => { } => {
use track::ResourceState as _;
*has_work = true; *has_work = true;
let ref_count = texture.life_guard.add_ref(); let ref_count = texture.life_guard.add_ref();
//TODO: better error handling here? unsafe {
// register it in the temporary tracker. used_surface_textures
let mut ts = track::TextureState::default(); .merge_single(
let _ = ts.change( &*texture_guard,
id::Valid(id), id::Valid(id),
texture.full_range.clone(), None,
hal::TextureUses::empty(), //present &ref_count,
None, hal::TextureUses::PRESENT,
); )
let _ = used_surface_textures.init(id::Valid(id), ref_count, ts); .unwrap()
};
} }
} }
} }
if !used_surface_textures.is_empty() { if !used_surface_textures.is_empty() {
let mut trackers = device.trackers.lock(); let mut trackers = device.trackers.lock();
let texture_barriers = trackers
trackers
.textures .textures
.merge_replace(&used_surface_textures) .set_from_usage_scope(&*texture_guard, &used_surface_textures);
.map(|pending| { let texture_barriers = trackers.textures.drain().map(|pending| {
let tex = &texture_guard[pending.id]; let tex = unsafe { texture_guard.get_unchecked(pending.id) };
pending.into_hal(tex) pending.into_hal(tex)
}); });
unsafe { unsafe {
pending_writes pending_writes
.command_encoder .command_encoder
.transition_textures(texture_barriers); .transition_textures(texture_barriers);
}; };
used_surface_textures.clear();
} }
} }

View File

@ -194,6 +194,14 @@ impl<T, I: id::TypedId> Storage<T, I> {
result 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 { pub(crate) fn label_for_invalid_id(&self, id: I) -> &str {
let (index, _, _) = id.unzip(); let (index, _, _) = id.unzip();
match self.map.get(index as usize) { 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 { fn generate_report(&self) -> StorageReport {
let mut report = StorageReport { let mut report = StorageReport {
element_size: mem::size_of::<T>(), element_size: mem::size_of::<T>(),
@ -298,56 +310,56 @@ pub enum Root {}
impl Access<Instance> for Root {} impl Access<Instance> for Root {}
impl Access<Surface> for Root {} impl Access<Surface> for Root {}
impl Access<Surface> for Instance {} impl Access<Surface> for Instance {}
impl<A: hal::Api> Access<Adapter<A>> for Root {} impl<A: HalApi> Access<Adapter<A>> for Root {}
impl<A: hal::Api> Access<Adapter<A>> for Surface {} impl<A: HalApi> Access<Adapter<A>> for Surface {}
impl<A: hal::Api> Access<Device<A>> for Root {} impl<A: HalApi> Access<Device<A>> for Root {}
impl<A: hal::Api> Access<Device<A>> for Surface {} impl<A: HalApi> Access<Device<A>> for Surface {}
impl<A: hal::Api> Access<Device<A>> for Adapter<A> {} impl<A: HalApi> Access<Device<A>> for Adapter<A> {}
impl<A: hal::Api> Access<PipelineLayout<A>> for Root {} impl<A: HalApi> Access<PipelineLayout<A>> for Root {}
impl<A: hal::Api> Access<PipelineLayout<A>> for Device<A> {} impl<A: HalApi> Access<PipelineLayout<A>> for Device<A> {}
impl<A: hal::Api> Access<PipelineLayout<A>> for RenderBundle {} impl<A: HalApi> Access<PipelineLayout<A>> for RenderBundle<A> {}
impl<A: hal::Api> Access<BindGroupLayout<A>> for Root {} impl<A: HalApi> Access<BindGroupLayout<A>> for Root {}
impl<A: hal::Api> Access<BindGroupLayout<A>> for Device<A> {} impl<A: HalApi> Access<BindGroupLayout<A>> for Device<A> {}
impl<A: hal::Api> Access<BindGroupLayout<A>> for PipelineLayout<A> {} impl<A: HalApi> Access<BindGroupLayout<A>> for PipelineLayout<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for Root {} impl<A: HalApi> Access<BindGroup<A>> for Root {}
impl<A: hal::Api> Access<BindGroup<A>> for Device<A> {} impl<A: HalApi> Access<BindGroup<A>> for Device<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for BindGroupLayout<A> {} impl<A: HalApi> Access<BindGroup<A>> for BindGroupLayout<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for PipelineLayout<A> {} impl<A: HalApi> Access<BindGroup<A>> for PipelineLayout<A> {}
impl<A: hal::Api> Access<BindGroup<A>> for CommandBuffer<A> {} impl<A: HalApi> Access<BindGroup<A>> for CommandBuffer<A> {}
impl<A: hal::Api> Access<CommandBuffer<A>> for Root {} impl<A: HalApi> Access<CommandBuffer<A>> for Root {}
impl<A: hal::Api> Access<CommandBuffer<A>> for Device<A> {} impl<A: HalApi> Access<CommandBuffer<A>> for Device<A> {}
impl<A: hal::Api> Access<RenderBundle> for Device<A> {} impl<A: HalApi> Access<RenderBundle<A>> for Device<A> {}
impl<A: hal::Api> Access<RenderBundle> for CommandBuffer<A> {} impl<A: HalApi> Access<RenderBundle<A>> for CommandBuffer<A> {}
impl<A: hal::Api> Access<ComputePipeline<A>> for Device<A> {} impl<A: HalApi> Access<ComputePipeline<A>> for Device<A> {}
impl<A: hal::Api> Access<ComputePipeline<A>> for BindGroup<A> {} impl<A: HalApi> Access<ComputePipeline<A>> for BindGroup<A> {}
impl<A: hal::Api> Access<RenderPipeline<A>> for Device<A> {} impl<A: HalApi> Access<RenderPipeline<A>> for Device<A> {}
impl<A: hal::Api> Access<RenderPipeline<A>> for BindGroup<A> {} impl<A: HalApi> Access<RenderPipeline<A>> for BindGroup<A> {}
impl<A: hal::Api> Access<RenderPipeline<A>> for ComputePipeline<A> {} impl<A: HalApi> Access<RenderPipeline<A>> for ComputePipeline<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for Root {} impl<A: HalApi> Access<QuerySet<A>> for Root {}
impl<A: hal::Api> Access<QuerySet<A>> for Device<A> {} impl<A: HalApi> Access<QuerySet<A>> for Device<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for CommandBuffer<A> {} impl<A: HalApi> Access<QuerySet<A>> for CommandBuffer<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for RenderPipeline<A> {} impl<A: HalApi> Access<QuerySet<A>> for RenderPipeline<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for ComputePipeline<A> {} impl<A: HalApi> Access<QuerySet<A>> for ComputePipeline<A> {}
impl<A: hal::Api> Access<QuerySet<A>> for Sampler<A> {} impl<A: HalApi> Access<QuerySet<A>> for Sampler<A> {}
impl<A: hal::Api> Access<ShaderModule<A>> for Device<A> {} impl<A: HalApi> Access<ShaderModule<A>> for Device<A> {}
impl<A: hal::Api> Access<ShaderModule<A>> for BindGroupLayout<A> {} impl<A: HalApi> Access<ShaderModule<A>> for BindGroupLayout<A> {}
impl<A: hal::Api> Access<Buffer<A>> for Root {} impl<A: HalApi> Access<Buffer<A>> for Root {}
impl<A: hal::Api> Access<Buffer<A>> for Device<A> {} impl<A: HalApi> Access<Buffer<A>> for Device<A> {}
impl<A: hal::Api> Access<Buffer<A>> for BindGroupLayout<A> {} impl<A: HalApi> Access<Buffer<A>> for BindGroupLayout<A> {}
impl<A: hal::Api> Access<Buffer<A>> for BindGroup<A> {} impl<A: HalApi> Access<Buffer<A>> for BindGroup<A> {}
impl<A: hal::Api> Access<Buffer<A>> for CommandBuffer<A> {} impl<A: HalApi> Access<Buffer<A>> for CommandBuffer<A> {}
impl<A: hal::Api> Access<Buffer<A>> for ComputePipeline<A> {} impl<A: HalApi> Access<Buffer<A>> for ComputePipeline<A> {}
impl<A: hal::Api> Access<Buffer<A>> for RenderPipeline<A> {} impl<A: HalApi> Access<Buffer<A>> for RenderPipeline<A> {}
impl<A: hal::Api> Access<Buffer<A>> for QuerySet<A> {} impl<A: HalApi> Access<Buffer<A>> for QuerySet<A> {}
impl<A: hal::Api> Access<Texture<A>> for Root {} impl<A: HalApi> Access<Texture<A>> for Root {}
impl<A: hal::Api> Access<Texture<A>> for Device<A> {} impl<A: HalApi> Access<Texture<A>> for Device<A> {}
impl<A: hal::Api> Access<Texture<A>> for Buffer<A> {} impl<A: HalApi> Access<Texture<A>> for Buffer<A> {}
impl<A: hal::Api> Access<TextureView<A>> for Root {} impl<A: HalApi> Access<TextureView<A>> for Root {}
impl<A: hal::Api> Access<TextureView<A>> for Device<A> {} impl<A: HalApi> Access<TextureView<A>> for Device<A> {}
impl<A: hal::Api> Access<TextureView<A>> for Texture<A> {} impl<A: HalApi> Access<TextureView<A>> for Texture<A> {}
impl<A: hal::Api> Access<Sampler<A>> for Root {} impl<A: HalApi> Access<Sampler<A>> for Root {}
impl<A: hal::Api> Access<Sampler<A>> for Device<A> {} impl<A: HalApi> Access<Sampler<A>> for Device<A> {}
impl<A: hal::Api> Access<Sampler<A>> for TextureView<A> {} impl<A: HalApi> Access<Sampler<A>> for TextureView<A> {}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
thread_local! { 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 adapters: Registry<Adapter<A>, id::AdapterId, F>,
pub devices: Registry<Device<A>, id::DeviceId, F>, pub devices: Registry<Device<A>, id::DeviceId, F>,
pub pipeline_layouts: Registry<PipelineLayout<A>, id::PipelineLayoutId, 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_group_layouts: Registry<BindGroupLayout<A>, id::BindGroupLayoutId, F>,
pub bind_groups: Registry<BindGroup<A>, id::BindGroupId, F>, pub bind_groups: Registry<BindGroup<A>, id::BindGroupId, F>,
pub command_buffers: Registry<CommandBuffer<A>, id::CommandBufferId, 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 render_pipelines: Registry<RenderPipeline<A>, id::RenderPipelineId, F>,
pub compute_pipelines: Registry<ComputePipeline<A>, id::ComputePipelineId, F>, pub compute_pipelines: Registry<ComputePipeline<A>, id::ComputePipelineId, F>,
pub query_sets: Registry<QuerySet<A>, id::QuerySetId, 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>; 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)] #[cfg(vulkan)]
impl HalApi for hal::api::Vulkan { impl HalApi for hal::api::Vulkan {
const VARIANT: Backend = Backend::Vulkan; const VARIANT: Backend = Backend::Vulkan;

View File

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

View File

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

View File

@ -494,6 +494,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} }
#[cfg(dx12)] #[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( pub unsafe fn instance_create_surface_from_visual(
&self, &self,
visual: *mut std::ffi::c_void, visual: *mut std::ffi::c_void,

View File

@ -4,6 +4,8 @@
*/ */
#![allow( #![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. // We use loops for getting early-out of scope without closures.
clippy::never_loop, clippy::never_loop,
// We don't use syntax sugar where it's not necessary. // We don't use syntax sugar where it's not necessary.
@ -16,6 +18,8 @@
clippy::new_without_default, clippy::new_without_default,
// Needless updates are more scaleable, easier to play with features. // Needless updates are more scaleable, easier to play with features.
clippy::needless_update, 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 // For some reason `rustc` can warn about these in const generics even
// though they are required. // though they are required.
unused_braces, unused_braces,

View File

@ -174,7 +174,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
initialization_status: TextureInitTracker::new(1, 1), initialization_status: TextureInitTracker::new(1, 1),
full_range: track::TextureSelector { full_range: track::TextureSelector {
layers: 0..1, layers: 0..1,
levels: 0..1, mips: 0..1,
}, },
life_guard: LifeGuard::new("<Surface>"), life_guard: LifeGuard::new("<Surface>"),
clear_mode: resource::TextureClearMode::RenderPass { clear_mode: resource::TextureClearMode::RenderPass {
@ -187,20 +187,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let id = fid.assign(texture, &mut token); let id = fid.assign(texture, &mut token);
{ {
use track::ResourceState as _;
// register it in the device tracker as uninitialized // register it in the device tracker as uninitialized
let mut trackers = device.trackers.lock(); let mut trackers = device.trackers.lock();
let mut ts = track::TextureState::default(); trackers.textures.insert_single(
let _ = ts.change( id.0,
id, ref_count.clone(),
track::TextureSelector {
layers: 0..1,
levels: 0..1,
},
hal::TextureUses::UNINITIALIZED, hal::TextureUses::UNINITIALIZED,
None,
); );
let _ = trackers.textures.init(id, ref_count.clone(), ts);
} }
if present.acquired_texture.is_some() { 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()`, // The texture ID got added to the device tracker by `submit()`,
// and now we are moving it away. // 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); device.trackers.lock().textures.remove(texture_id.value);
let (texture, _) = hub.textures.unregister(texture_id.value.0, &mut token); 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}, hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token},
id::{AdapterId, DeviceId, SurfaceId, TextureId, Valid}, id::{AdapterId, DeviceId, SurfaceId, TextureId, Valid},
init_tracker::{BufferInitTracker, TextureInitTracker}, init_tracker::{BufferInitTracker, TextureInitTracker},
track::{TextureSelector, DUMMY_SELECTOR}, track::TextureSelector,
validation::MissingBufferUsageError, validation::MissingBufferUsageError,
Label, LifeGuard, RefCount, Stored, 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>>; pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>>;
#[derive(Debug)] #[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`] /// Describes a [`Sampler`]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))] #[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)] #[derive(Clone, Debug, Error)]
pub enum CreateQuerySetError { pub enum CreateQuerySetError {
#[error(transparent)] #[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)] #[derive(Clone, Debug, Error)]
pub enum DestroyError { pub enum DestroyError {
#[error("resource is invalid")] #[error("resource is invalid")]

View File

@ -1,236 +1,778 @@
use super::{PendingTransition, ResourceState, Unit}; /*! Buffer Trackers
use crate::id::{BufferId, Valid}; *
* 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; 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 Id = BufferId;
type Selector = (); type Selector = ();
type Usage = BufferUses;
fn query(&self, _selector: Self::Selector) -> Option<Self::Usage> { fn bits(self) -> u16 {
Some(self.last) Self::bits(&self)
} }
fn change( fn all_ordered(self) -> bool {
&mut self, Self::ORDERED.contains(self)
id: Valid<Self::Id>, }
_selector: Self::Selector,
usage: Self::Usage, fn any_exclusive(self) -> bool {
output: Option<&mut Vec<PendingTransition<Self>>>, self.intersects(Self::EXCLUSIVE)
) -> Result<(), PendingTransition<Self>> { }
let old = self.last; }
if old != usage || !BufferUses::ORDERED.contains(usage) {
let pending = PendingTransition { /// Stores all the buffers that a bind group stores.
id, pub(crate) struct BufferBindGroupState<A: hub::HalApi> {
selector: (), buffers: Vec<(Valid<BufferId>, RefCount, BufferUses)>,
usage: old..usage,
}; _phantom: PhantomData<A>,
*self = match output { }
None => { impl<A: hub::HalApi> BufferBindGroupState<A> {
assert_eq!( pub fn new() -> Self {
self.first, None, Self {
"extending a state that is already a transition" buffers: Vec::new(),
);
Unit::new(pending.collapse()?) _phantom: PhantomData,
}
Some(transitions) => {
transitions.push(pending);
Unit {
first: self.first.or(Some(old)),
last: usage,
}
}
};
} }
}
/// 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(()) Ok(())
} }
fn merge( /// Merge the list of buffer states in the given usage scope into this UsageScope.
&mut self, ///
id: Valid<Self::Id>, /// If any of the resulting states is invalid, stops the merge and returns a usage
other: &Self, /// conflict with the details of the invalid state.
output: Option<&mut Vec<PendingTransition<Self>>>, ///
) -> Result<(), PendingTransition<Self>> { /// If the given tracker uses IDs higher than the length of internal vectors,
let old = self.last; /// the vectors will be extended. A call to set_size is not needed.
let new = other.port(); pub fn merge_usage_scope(&mut self, scope: &Self) -> Result<(), UsageConflict> {
if old == new && BufferUses::ORDERED.contains(new) { let incoming_size = scope.state.len();
if output.is_some() && self.first.is_none() { if incoming_size > self.state.len() {
*self = Unit { self.set_size(incoming_size);
first: Some(old), }
last: other.last,
}; for index in iterate_bitvec_indices(&scope.metadata.owned) {
} self.debug_assert_in_bounds(index);
} else { scope.debug_assert_in_bounds(index);
let pending = PendingTransition {
id, unsafe {
selector: (), insert_or_merge(
usage: old..new, None,
}; None,
*self = match output { &mut self.state,
None => { &mut self.metadata,
assert_eq!( index as u32,
self.first, None, index,
"extending a state that is already a transition" BufferStateProvider::Indirect {
); state: &scope.state,
Unit::new(pending.collapse()?) },
} ResourceMetadataProvider::Indirect {
Some(transitions) => { metadata: &scope.metadata,
transitions.push(pending); },
Unit { )?;
first: self.first.or(Some(old)),
last: other.last,
}
}
}; };
} }
Ok(()) 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)] let (index32, epoch, _) = id.unzip();
mod test { let index = index32 as usize;
use super::*;
use crate::id::Id;
#[test] self.allow_index(index);
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));
}
#[test] self.debug_assert_in_bounds(index);
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,
}
);
list.clear(); unsafe {
bs.change(id, (), BufferUses::STORAGE_WRITE, Some(&mut list)) insert_or_merge(
.unwrap(); Some(&buffer.life_guard),
assert_eq!( None,
&list, &mut self.state,
&[PendingTransition { &mut self.metadata,
id, index32,
selector: (), index,
usage: BufferUses::VERTEX..BufferUses::STORAGE_WRITE, BufferStateProvider::Direct { state: new_state },
}], ResourceMetadataProvider::Resource { epoch },
); )?;
assert_eq!( }
bs,
Unit {
first: Some(BufferUses::STORAGE_WRITE),
last: BufferUses::STORAGE_WRITE,
}
);
}
#[test] Ok(buffer)
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,
}
);
} }
} }
/// 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. //TODO: consider getting rid of it.
use smallvec::SmallVec; 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, /// Structure that keeps track of a I -> T mapping,
/// optimized for a case where keys of the same values /// optimized for a case where keys of the same values
/// are often grouped together linearly. /// are often grouped together linearly.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct RangedStates<I, T> { pub(crate) struct RangedStates<I, T> {
/// List of ranges, each associated with a singe value. /// List of ranges, each associated with a singe value.
/// Ranges of keys have to be non-intersecting and ordered. /// Ranges of keys have to be non-intersecting and ordered.
ranges: SmallVec<[(Range<I>, T); 1]>, ranges: SmallVec<[(Range<I>, T); 1]>,
} }
impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> { impl<I: Copy + Ord, T: Copy + PartialEq> RangedStates<I, T> {
pub fn empty() -> Self {
Self {
ranges: SmallVec::new(),
}
}
pub fn from_range(range: Range<I>, value: T) -> Self { pub fn from_range(range: Range<I>, value: T) -> Self {
Self { Self {
ranges: iter::once((range, value)).collect(), 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 iter(&self) -> impl Iterator<Item = &(Range<I>, T)> + Clone {
pub fn clear(&mut self) { self.ranges.iter()
self.ranges.clear();
} }
/// Append a range. pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (Range<I>, T)> {
/// self.ranges.iter_mut()
/// 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));
} }
/// Check that all the ranges are non-intersecting and ordered. /// 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. /// Merge the neighboring ranges together, where possible.
#[allow(clippy::suspicious_operation_groupings)]
pub fn coalesce(&mut self) { pub fn coalesce(&mut self) {
let mut num_removed = 0; let mut num_removed = 0;
let mut iter = self.ranges.iter_mut(); 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. pub fn iter_filter<'a>(
/// &'a self,
/// Returns `None` if no intersections are detected. range: &'a Range<I>,
/// Returns `Some(Err)` if the intersected values are inconsistent. ) -> impl Iterator<Item = (Range<I>, &T)> + 'a {
pub fn query<U: PartialEq>( self.ranges
&self, .iter()
index: &Range<I>, .filter(move |&&(ref inner, ..)| inner.end > range.start && inner.start < range.end)
fun: impl Fn(&T) -> U, .map(move |&(ref inner, ref v)| {
) -> Option<Result<U, ()>> { let new_range = inner.start.max(range.start)..inner.end.min(range.end);
let mut result = None;
for &(ref range, ref value) in self.ranges.iter() { (new_range, v)
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)
} }
/// Split the storage ranges in such a way that there is a linear subset of /// 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(); clone.check_sanity();
result 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)] #[cfg(test)]
mod test { mod test {
//TODO: randomized/fuzzy testing //TODO: randomized/fuzzy testing
use super::RangedStates; 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] #[test]
fn sane_good() { fn sane_good() {
@ -311,14 +189,6 @@ mod test {
assert_eq!(rs.ranges.as_slice(), &[(1..5, 9), (5..7, 1), (8..9, 1),]); 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] #[test]
fn isolate() { fn isolate() {
let rs = RangedStates::from_slice(&[(1..4, 9u8), (4..5, 9), (5..7, 1), (8..9, 1)]); 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),] &[(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 { let target_barrier1 = hal::TextureBarrier {
texture: surface_tex.borrow(), texture: surface_tex.borrow(),
range: wgt::ImageSubresourceRange::default(), range: wgt::ImageSubresourceRange::default(),
usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::empty(), usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::PRESENT,
}; };
unsafe { unsafe {
ctx.encoder.end_render_pass(); ctx.encoder.end_render_pass();

View File

@ -287,7 +287,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
StateAfter: s1, StateAfter: s1,
}; };
self.temp.barriers.push(raw); 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 { let mut raw = d3d12::D3D12_RESOURCE_BARRIER {
Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, 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 { let mut raw = d3d12::D3D12_RESOURCE_BARRIER {
Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE, 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 { pub fn map_buffer_usage_to_resource_flags(usage: crate::BufferUses) -> d3d12::D3D12_RESOURCE_FLAGS {
let mut flags = 0; 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 |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
} }
flags flags
@ -33,7 +33,7 @@ pub fn map_texture_usage_to_resource_flags(
flags |= d3d12::D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; 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; 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) { if usage.intersects(Bu::VERTEX | Bu::UNIFORM) {
state |= d3d12::D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; 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; state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
} else if usage.intersects(Bu::STORAGE_READ) { } else if usage.intersects(Bu::STORAGE_READ) {
state |= d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE 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) { if usage.intersects(Tu::DEPTH_STENCIL_WRITE) {
state |= d3d12::D3D12_RESOURCE_STATE_DEPTH_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 |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
} }
state state

View File

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

View File

@ -230,7 +230,11 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
} }
for bar in barriers { for bar in barriers {
// GLES only synchronizes storage -> anything explicitly // 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; continue;
} }
self.cmd_buffer self.cmd_buffer
@ -253,7 +257,11 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
let mut combined_usage = crate::TextureUses::empty(); let mut combined_usage = crate::TextureUses::empty();
for bar in barriers { for bar in barriers {
// GLES only synchronizes storage -> anything explicitly // 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; continue;
} }
// unlike buffers, there is no need for a concrete texture // 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) { if usage.intersects(crate::BufferUses::MAP_READ | crate::BufferUses::MAP_WRITE) {
flags |= glow::BUFFER_UPDATE_BARRIER_BIT; flags |= glow::BUFFER_UPDATE_BARRIER_BIT;
} }
if usage if usage.intersects(
.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE) crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE,
{ ) {
flags |= glow::SHADER_STORAGE_BARRIER_BIT; flags |= glow::SHADER_STORAGE_BARRIER_BIT;
} }
gl.memory_barrier(flags); gl.memory_barrier(flags);
@ -803,7 +803,7 @@ impl super::Queue {
flags |= glow::TEXTURE_FETCH_BARRIER_BIT; flags |= glow::TEXTURE_FETCH_BARRIER_BIT;
} }
if usage.intersects( 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; flags |= glow::SHADER_IMAGE_ACCESS_BARRIER_BIT;
} }

View File

@ -628,53 +628,77 @@ bitflags!(
bitflags::bitflags! { bitflags::bitflags! {
/// Similar to `wgt::BufferUsages` but for internal use. /// 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; const MAP_READ = 1 << 0;
/// The argument to a write-only mapping.
const MAP_WRITE = 1 << 1; const MAP_WRITE = 1 << 1;
/// The source of a hardware copy.
const COPY_SRC = 1 << 2; const COPY_SRC = 1 << 2;
/// The destination of a hardware copy.
const COPY_DST = 1 << 3; const COPY_DST = 1 << 3;
/// The index buffer used for drawing.
const INDEX = 1 << 4; const INDEX = 1 << 4;
/// A vertex buffer used for drawing.
const VERTEX = 1 << 5; const VERTEX = 1 << 5;
/// A uniform buffer bound in a bind group.
const UNIFORM = 1 << 6; const UNIFORM = 1 << 6;
/// A read-only storage buffer used in a bind group.
const STORAGE_READ = 1 << 7; 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; 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 | const INCLUSIVE = Self::MAP_READ.bits | Self::COPY_SRC.bits |
Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits | Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits |
Self::STORAGE_READ.bits | Self::INDIRECT.bits; Self::STORAGE_READ.bits | Self::INDIRECT.bits;
/// The combination of exclusive usages (write-only and read-write). /// The combination of states that a buffer must exclusively be in.
/// 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_READ_WRITE.bits;
const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_WRITE.bits;
/// The combination of all usages that the are guaranteed to be be ordered by the hardware. /// 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 /// If a usage is ordered, then if the buffer state doesn't change between draw calls, there
/// still need to be pipeline barriers inserted for synchronization. /// are no barriers needed for synchronization.
const ORDERED = Self::INCLUSIVE.bits | Self::MAP_WRITE.bits; const ORDERED = Self::INCLUSIVE.bits | Self::MAP_WRITE.bits;
} }
} }
bitflags::bitflags! { bitflags::bitflags! {
/// Similar to `wgt::TextureUsages` but for internal use. /// Similar to `wgt::TextureUsages` but for internal use.
pub struct TextureUses: u32 { pub struct TextureUses: u16 {
const COPY_SRC = 1 << 0; /// The texture is in unknown state.
const COPY_DST = 1 << 1; const UNINITIALIZED = 1 << 0;
const RESOURCE = 1 << 2; /// Ready to present image to the surface.
const COLOR_TARGET = 1 << 3; const PRESENT = 1 << 1;
const DEPTH_STENCIL_READ = 1 << 4; /// The source of a hardware copy.
const DEPTH_STENCIL_WRITE = 1 << 5; const COPY_SRC = 1 << 2;
const STORAGE_READ = 1 << 6; /// The destination of a hardware copy.
const STORAGE_WRITE = 1 << 7; const COPY_DST = 1 << 3;
/// The combination of usages that can be used together (read-only). /// 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; const INCLUSIVE = Self::COPY_SRC.bits | Self::RESOURCE.bits | Self::DEPTH_STENCIL_READ.bits;
/// The combination of exclusive usages (write-only and read-write). /// The combination of states that a texture must exclusively be in.
/// 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_READ_WRITE.bits | Self::PRESENT.bits;
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 all usages that the are guaranteed to be be ordered by the hardware. /// 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 /// If a usage is ordered, then if the texture state doesn't change between draw calls, there
/// still need to be pipeline barriers inserted for synchronization. /// are no barriers needed for synchronization.
const ORDERED = Self::INCLUSIVE.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits; 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_usage.set(
mtl::MTLTextureUsage::ShaderRead, 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_usage.set(
mtl::MTLTextureUsage::ShaderWrite, mtl::MTLTextureUsage::ShaderWrite,
usage.intersects(Tu::STORAGE_WRITE), usage.intersects(Tu::STORAGE_READ_WRITE),
); );
mtl_usage mtl_usage

View File

@ -200,7 +200,7 @@ pub fn derive_image_layout(
vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
} }
_ => { _ => {
if usage.is_empty() { if usage == crate::TextureUses::PRESENT {
vk::ImageLayout::PRESENT_SRC_KHR vk::ImageLayout::PRESENT_SRC_KHR
} else if is_color { } else if is_color {
vk::ImageLayout::GENERAL vk::ImageLayout::GENERAL
@ -230,7 +230,7 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> vk::ImageUsageFlags {
) { ) {
flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT; 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 |= vk::ImageUsageFlags::STORAGE;
} }
flags flags
@ -276,12 +276,12 @@ pub fn map_texture_usage_to_barrier(
stages |= shader_stages; stages |= shader_stages;
access |= vk::AccessFlags::SHADER_READ; access |= vk::AccessFlags::SHADER_READ;
} }
if usage.contains(crate::TextureUses::STORAGE_WRITE) { if usage.contains(crate::TextureUses::STORAGE_READ_WRITE) {
stages |= shader_stages; 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::PipelineStageFlags::TOP_OF_PIPE,
vk::AccessFlags::empty(), 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; bits |= crate::TextureUses::DEPTH_STENCIL_READ | crate::TextureUses::DEPTH_STENCIL_WRITE;
} }
if usage.contains(vk::ImageUsageFlags::STORAGE) { 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 bits
} }
@ -457,7 +457,7 @@ pub fn map_buffer_usage(usage: crate::BufferUses) -> vk::BufferUsageFlags {
if usage.contains(crate::BufferUses::UNIFORM) { if usage.contains(crate::BufferUses::UNIFORM) {
flags |= vk::BufferUsageFlags::UNIFORM_BUFFER; 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; flags |= vk::BufferUsageFlags::STORAGE_BUFFER;
} }
if usage.contains(crate::BufferUses::INDEX) { if usage.contains(crate::BufferUses::INDEX) {
@ -505,9 +505,9 @@ pub fn map_buffer_usage_to_barrier(
stages |= shader_stages; stages |= shader_stages;
access |= vk::AccessFlags::SHADER_READ; access |= vk::AccessFlags::SHADER_READ;
} }
if usage.intersects(crate::BufferUses::STORAGE_WRITE) { if usage.intersects(crate::BufferUses::STORAGE_READ_WRITE) {
stages |= shader_stages; stages |= shader_stages;
access |= vk::AccessFlags::SHADER_WRITE; access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE;
} }
if usage.contains(crate::BufferUses::INDEX) { if usage.contains(crate::BufferUses::INDEX) {
stages |= vk::PipelineStageFlags::VERTEX_INPUT; 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) { unsafe fn destroy_shader_module(&self, module: super::ShaderModule) {
match module { match module {
super::ShaderModule::Raw(raw) => { super::ShaderModule::Raw(raw) => {
let _ = self.shared.raw.destroy_shader_module(raw, None); self.shared.raw.destroy_shader_module(raw, None);
} }
super::ShaderModule::Intermediate { .. } => {} super::ShaderModule::Intermediate { .. } => {}
} }