mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-21 22:33:49 +00:00
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:
parent
dd6febe309
commit
9114283707
555
Cargo.lock
generated
555
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,7 @@ vulkan-portability = ["hal/vulkan"]
|
||||
[dependencies]
|
||||
arrayvec = "0.7"
|
||||
bitflags = "1.0"
|
||||
bit-vec = "0.6"
|
||||
codespan-reporting = "0.11"
|
||||
copyless = "0.1"
|
||||
fxhash = "0.2"
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
device::{DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT},
|
||||
error::{ErrorFormatter, PrettyError},
|
||||
hub::Resource,
|
||||
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid},
|
||||
hub::{HalApi, Resource},
|
||||
id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureId, TextureViewId, Valid},
|
||||
init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction},
|
||||
track::{TrackerSet, UsageConflict, DUMMY_SELECTOR},
|
||||
track::{BindGroupStates, UsageConflict},
|
||||
validation::{MissingBufferUsageError, MissingTextureUsageError},
|
||||
FastHashMap, Label, LifeGuard, MultiRefCount, Stored,
|
||||
};
|
||||
@ -16,10 +16,7 @@ use serde::Deserialize;
|
||||
#[cfg(feature = "trace")]
|
||||
use serde::Serialize;
|
||||
|
||||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
ops::Range,
|
||||
};
|
||||
use std::{borrow::Cow, ops::Range};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
@ -63,6 +60,8 @@ pub enum CreateBindGroupError {
|
||||
InvalidBuffer(BufferId),
|
||||
#[error("texture view {0:?} is invalid")]
|
||||
InvalidTextureView(TextureViewId),
|
||||
#[error("texture {0:?} is invalid")]
|
||||
InvalidTexture(TextureId),
|
||||
#[error("sampler {0:?} is invalid")]
|
||||
InvalidSampler(SamplerId),
|
||||
#[error(
|
||||
@ -709,13 +708,12 @@ pub(crate) fn buffer_binding_type_alignment(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BindGroup<A: hal::Api> {
|
||||
pub struct BindGroup<A: HalApi> {
|
||||
pub(crate) raw: A::BindGroup,
|
||||
pub(crate) device_id: Stored<DeviceId>,
|
||||
pub(crate) layout_id: Valid<BindGroupLayoutId>,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
pub(crate) used: TrackerSet,
|
||||
pub(crate) used: BindGroupStates<A>,
|
||||
pub(crate) used_buffer_ranges: Vec<BufferInitTrackerAction>,
|
||||
pub(crate) used_texture_ranges: Vec<TextureInitTrackerAction>,
|
||||
pub(crate) dynamic_binding_info: Vec<BindGroupDynamicBindingData>,
|
||||
@ -724,7 +722,7 @@ pub struct BindGroup<A: hal::Api> {
|
||||
pub(crate) late_buffer_binding_sizes: Vec<wgt::BufferSize>,
|
||||
}
|
||||
|
||||
impl<A: hal::Api> BindGroup<A> {
|
||||
impl<A: HalApi> BindGroup<A> {
|
||||
pub(crate) fn validate_dynamic_bindings(
|
||||
&self,
|
||||
offsets: &[wgt::DynamicOffset],
|
||||
@ -766,13 +764,7 @@ impl<A: hal::Api> BindGroup<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Borrow<()> for BindGroup<A> {
|
||||
fn borrow(&self) -> &() {
|
||||
&DUMMY_SELECTOR
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Resource for BindGroup<A> {
|
||||
impl<A: HalApi> Resource for BindGroup<A> {
|
||||
const TYPE: &'static str = "BindGroup";
|
||||
|
||||
fn life_guard(&self) -> &LifeGuard {
|
||||
|
@ -34,7 +34,7 @@ invalidations or index format changes.
|
||||
#![allow(clippy::reversed_empty_ranges)]
|
||||
|
||||
use crate::{
|
||||
binding_model::buffer_binding_type_alignment,
|
||||
binding_model::{self, buffer_binding_type_alignment},
|
||||
command::{
|
||||
BasePass, BindGroupStateChange, DrawError, MapPassErr, PassErrorScope, RenderCommand,
|
||||
RenderCommandError, StateChange,
|
||||
@ -48,8 +48,9 @@ use crate::{
|
||||
hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Resource, Storage, Token},
|
||||
id,
|
||||
init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},
|
||||
pipeline::PipelineFlags,
|
||||
track::{TrackerSet, UsageConflict},
|
||||
pipeline::{self, PipelineFlags},
|
||||
resource,
|
||||
track::RenderBundleScope,
|
||||
validation::check_buffer_usage,
|
||||
Label, LabelHelpers, LifeGuard, Stored,
|
||||
};
|
||||
@ -117,7 +118,7 @@ impl RenderBundleEncoder {
|
||||
},
|
||||
sample_count: {
|
||||
let sc = desc.sample_count;
|
||||
if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) {
|
||||
if sc == 0 || sc > 32 || !conv::is_power_of_two_u32(sc) {
|
||||
return Err(CreateRenderBundleError::InvalidSampleCount(sc));
|
||||
}
|
||||
sc
|
||||
@ -177,20 +178,28 @@ impl RenderBundleEncoder {
|
||||
/// and accumulate buffer and texture initialization actions.
|
||||
///
|
||||
/// [`ExecuteBundle`]: RenderCommand::ExecuteBundle
|
||||
pub(crate) fn finish<A: hal::Api, G: GlobalIdentityHandlerFactory>(
|
||||
pub(crate) fn finish<A: HalApi, G: GlobalIdentityHandlerFactory>(
|
||||
self,
|
||||
desc: &RenderBundleDescriptor,
|
||||
device: &Device<A>,
|
||||
hub: &Hub<A, G>,
|
||||
token: &mut Token<Device<A>>,
|
||||
) -> Result<RenderBundle, RenderBundleError> {
|
||||
) -> Result<RenderBundle<A>, RenderBundleError> {
|
||||
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(token);
|
||||
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
|
||||
let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token);
|
||||
let (buffer_guard, _) = hub.buffers.read(&mut token);
|
||||
let (query_set_guard, mut token) = hub.query_sets.read(&mut token);
|
||||
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
|
||||
let (texture_guard, _) = hub.textures.read(&mut token);
|
||||
|
||||
let mut state = State {
|
||||
trackers: TrackerSet::new(self.parent_id.backend()),
|
||||
trackers: RenderBundleScope::new(
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
&*bind_group_guard,
|
||||
&*pipeline_guard,
|
||||
&*query_set_guard,
|
||||
),
|
||||
index: IndexState::new(),
|
||||
vertex: (0..hal::MAX_VERTEX_BUFFERS)
|
||||
.map(|_| VertexState::new())
|
||||
@ -234,11 +243,11 @@ impl RenderBundleEncoder {
|
||||
next_dynamic_offset = offsets_range.end;
|
||||
let offsets = &base.dynamic_offsets[offsets_range.clone()];
|
||||
|
||||
let bind_group = state
|
||||
let bind_group: &binding_model::BindGroup<A> = state
|
||||
.trackers
|
||||
.bind_groups
|
||||
.use_extend(&*bind_group_guard, bind_group_id, (), ())
|
||||
.map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id))
|
||||
.add_single(&*bind_group_guard, bind_group_id)
|
||||
.ok_or(RenderCommandError::InvalidBindGroup(bind_group_id))
|
||||
.map_pass_err(scope)?;
|
||||
if bind_group.dynamic_binding_info.len() != offsets.len() {
|
||||
return Err(RenderCommandError::InvalidDynamicOffsetCount {
|
||||
@ -268,10 +277,12 @@ impl RenderBundleEncoder {
|
||||
texture_memory_init_actions.extend_from_slice(&bind_group.used_texture_ranges);
|
||||
|
||||
state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets_range);
|
||||
state
|
||||
.trackers
|
||||
.merge_extend_stateful(&bind_group.used)
|
||||
.map_pass_err(scope)?;
|
||||
unsafe {
|
||||
state
|
||||
.trackers
|
||||
.merge_bind_group(&*texture_guard, &bind_group.used)
|
||||
.map_pass_err(scope)?
|
||||
};
|
||||
//Note: stateless trackers are not merged: the lifetime reference
|
||||
// is held to the bind group itself.
|
||||
}
|
||||
@ -280,11 +291,11 @@ impl RenderBundleEncoder {
|
||||
|
||||
state.pipeline = Some(pipeline_id);
|
||||
|
||||
let pipeline = state
|
||||
let pipeline: &pipeline::RenderPipeline<A> = state
|
||||
.trackers
|
||||
.render_pipes
|
||||
.use_extend(&*pipeline_guard, pipeline_id, (), ())
|
||||
.map_err(|_| RenderCommandError::InvalidPipeline(pipeline_id))
|
||||
.render_pipelines
|
||||
.add_single(&*pipeline_guard, pipeline_id)
|
||||
.ok_or(RenderCommandError::InvalidPipeline(pipeline_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
self.context
|
||||
@ -320,11 +331,11 @@ impl RenderBundleEncoder {
|
||||
size,
|
||||
} => {
|
||||
let scope = PassErrorScope::SetIndexBuffer(buffer_id);
|
||||
let buffer = state
|
||||
let buffer: &resource::Buffer<A> = state
|
||||
.trackers
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDEX)
|
||||
.unwrap();
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDEX)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
@ -347,11 +358,11 @@ impl RenderBundleEncoder {
|
||||
size,
|
||||
} => {
|
||||
let scope = PassErrorScope::SetVertexBuffer(buffer_id);
|
||||
let buffer = state
|
||||
let buffer: &resource::Buffer<A> = state
|
||||
.trackers
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::VERTEX)
|
||||
.unwrap();
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(buffer.usage, wgt::BufferUsages::VERTEX)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
@ -472,11 +483,11 @@ impl RenderBundleEncoder {
|
||||
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let buffer = state
|
||||
let buffer: &resource::Buffer<A> = state
|
||||
.trackers
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
|
||||
.unwrap();
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
@ -505,11 +516,10 @@ impl RenderBundleEncoder {
|
||||
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let buffer = state
|
||||
let buffer: &resource::Buffer<A> = state
|
||||
.trackers
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
|
||||
.map_err(|err| RenderCommandError::Buffer(buffer_id, err))
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(buffer.usage, wgt::BufferUsages::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
@ -612,24 +622,23 @@ pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
|
||||
//Note: here, `RenderBundle` is just wrapping a raw stream of render commands.
|
||||
// The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle,
|
||||
// or Metal indirect command buffer.
|
||||
#[derive(Debug)]
|
||||
pub struct RenderBundle {
|
||||
pub struct RenderBundle<A: HalApi> {
|
||||
// Normalized command stream. It can be executed verbatim,
|
||||
// without re-binding anything on the pipeline change.
|
||||
base: BasePass<RenderCommand>,
|
||||
pub(super) is_ds_read_only: bool,
|
||||
pub(crate) device_id: Stored<id::DeviceId>,
|
||||
pub(crate) used: TrackerSet,
|
||||
pub(crate) used: RenderBundleScope<A>,
|
||||
pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
|
||||
pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
|
||||
pub(super) context: RenderPassContext,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
}
|
||||
|
||||
unsafe impl Send for RenderBundle {}
|
||||
unsafe impl Sync for RenderBundle {}
|
||||
unsafe impl<A: HalApi> Send for RenderBundle<A> {}
|
||||
unsafe impl<A: HalApi> Sync for RenderBundle<A> {}
|
||||
|
||||
impl RenderBundle {
|
||||
impl<A: HalApi> RenderBundle<A> {
|
||||
/// Actually encode the contents into a native command buffer.
|
||||
///
|
||||
/// This is partially duplicating the logic of `command_encoder_run_render_pass`.
|
||||
@ -639,7 +648,7 @@ impl RenderBundle {
|
||||
/// Note that the function isn't expected to fail, generally.
|
||||
/// All the validation has already been done by this point.
|
||||
/// The only failure condition is if some of the used buffers are destroyed.
|
||||
pub(super) unsafe fn execute<A: HalApi>(
|
||||
pub(super) unsafe fn execute(
|
||||
&self,
|
||||
raw: &mut A::CommandEncoder,
|
||||
pipeline_layout_guard: &Storage<
|
||||
@ -828,7 +837,7 @@ impl RenderBundle {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for RenderBundle {
|
||||
impl<A: HalApi> Resource for RenderBundle<A> {
|
||||
const TYPE: &'static str = "RenderBundle";
|
||||
|
||||
fn life_guard(&self) -> &LifeGuard {
|
||||
@ -1027,10 +1036,9 @@ struct VertexLimitState {
|
||||
/// [`SetIndexBuffer`] to the simulated state stored here, and then
|
||||
/// calls the `flush_foo` methods before draw calls to produce the
|
||||
/// update commands we actually need.
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
struct State<A: HalApi> {
|
||||
/// Resources used by this bundle. This will become [`RenderBundle::used`].
|
||||
trackers: TrackerSet,
|
||||
trackers: RenderBundleScope<A>,
|
||||
|
||||
/// The current index buffer. We flush this state before indexed
|
||||
/// draw commands.
|
||||
@ -1056,7 +1064,7 @@ struct State {
|
||||
pipeline: Option<id::RenderPipelineId>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
impl<A: HalApi> State<A> {
|
||||
fn vertex_limits(&self) -> VertexLimitState {
|
||||
let mut vert_state = VertexLimitState {
|
||||
vertex_limit: u32::MAX,
|
||||
@ -1235,8 +1243,6 @@ pub(super) enum RenderBundleErrorInner {
|
||||
#[error(transparent)]
|
||||
RenderCommand(RenderCommandError),
|
||||
#[error(transparent)]
|
||||
ResourceUsageConflict(#[from] UsageConflict),
|
||||
#[error(transparent)]
|
||||
Draw(#[from] DrawError),
|
||||
#[error(transparent)]
|
||||
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
|
||||
|
@ -4,13 +4,12 @@ use std::{num::NonZeroU32, ops::Range};
|
||||
use crate::device::trace::Command as TraceCommand;
|
||||
use crate::{
|
||||
command::CommandBuffer,
|
||||
device::Device,
|
||||
get_lowest_common_denom,
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token},
|
||||
hub::{self, Global, GlobalIdentityHandlerFactory, HalApi, Token},
|
||||
id::{BufferId, CommandEncoderId, DeviceId, TextureId, Valid},
|
||||
init_tracker::{MemoryInitKind, TextureInitRange},
|
||||
resource::{Texture, TextureClearMode},
|
||||
track::{ResourceTracker, TextureSelector, TextureState},
|
||||
track::{TextureSelector, TextureTracker},
|
||||
};
|
||||
|
||||
use hal::{auxil::align_to, CommandEncoder as _};
|
||||
@ -90,8 +89,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (dst_buffer, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, dst, (), hal::BufferUses::COPY_DST)
|
||||
.map_err(ClearError::InvalidBuffer)?;
|
||||
.set_single(&*buffer_guard, dst, hal::BufferUses::COPY_DST)
|
||||
.ok_or(ClearError::InvalidBuffer(dst))?;
|
||||
let dst_raw = dst_buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
@ -139,7 +138,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
|
||||
let cmd_buf_raw = cmd_buf.encoder.open();
|
||||
unsafe {
|
||||
cmd_buf_raw.transition_buffers(dst_barrier);
|
||||
cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
|
||||
cmd_buf_raw.clear_buffer(dst_raw, offset..end);
|
||||
}
|
||||
Ok(())
|
||||
@ -191,13 +190,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
// Check if subresource level range is valid
|
||||
let subresource_level_end = match subresource_range.mip_level_count {
|
||||
Some(count) => subresource_range.base_mip_level + count.get(),
|
||||
None => dst_texture.full_range.levels.end,
|
||||
None => dst_texture.full_range.mips.end,
|
||||
};
|
||||
if dst_texture.full_range.levels.start > subresource_range.base_mip_level
|
||||
|| dst_texture.full_range.levels.end < subresource_level_end
|
||||
if dst_texture.full_range.mips.start > subresource_range.base_mip_level
|
||||
|| dst_texture.full_range.mips.end < subresource_level_end
|
||||
{
|
||||
return Err(ClearError::InvalidTextureLevelRange {
|
||||
texture_level_range: dst_texture.full_range.levels.clone(),
|
||||
texture_level_range: dst_texture.full_range.mips.clone(),
|
||||
subresource_base_mip_level: subresource_range.base_mip_level,
|
||||
subresource_mip_level_count: subresource_range.mip_level_count,
|
||||
});
|
||||
@ -217,48 +216,34 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
});
|
||||
}
|
||||
|
||||
let device = &device_guard[cmd_buf.device_id.value];
|
||||
|
||||
clear_texture(
|
||||
&*texture_guard,
|
||||
Valid(dst),
|
||||
dst_texture,
|
||||
TextureInitRange {
|
||||
mip_range: subresource_range.base_mip_level..subresource_level_end,
|
||||
layer_range: subresource_range.base_array_layer..subresource_layer_end,
|
||||
},
|
||||
cmd_buf.encoder.open(),
|
||||
&mut cmd_buf.trackers.textures,
|
||||
&device_guard[cmd_buf.device_id.value],
|
||||
&device.alignments,
|
||||
&device.zero_buffer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clear_texture<A: hal::Api>(
|
||||
pub(crate) fn clear_texture<A: HalApi>(
|
||||
storage: &hub::Storage<Texture<A>, TextureId>,
|
||||
dst_texture_id: Valid<TextureId>,
|
||||
dst_texture: &Texture<A>,
|
||||
range: TextureInitRange,
|
||||
encoder: &mut A::CommandEncoder,
|
||||
texture_tracker: &mut ResourceTracker<TextureState>,
|
||||
device: &Device<A>,
|
||||
) -> Result<(), ClearError> {
|
||||
clear_texture_no_device(
|
||||
dst_texture_id,
|
||||
dst_texture,
|
||||
range,
|
||||
encoder,
|
||||
texture_tracker,
|
||||
&device.alignments,
|
||||
&device.zero_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn clear_texture_no_device<A: hal::Api>(
|
||||
dst_texture_id: Valid<TextureId>,
|
||||
dst_texture: &Texture<A>,
|
||||
range: TextureInitRange,
|
||||
encoder: &mut A::CommandEncoder,
|
||||
texture_tracker: &mut ResourceTracker<TextureState>,
|
||||
texture_tracker: &mut TextureTracker<A>,
|
||||
alignments: &hal::Alignments,
|
||||
zero_buffer: &A::Buffer,
|
||||
) -> Result<(), ClearError> {
|
||||
let dst_texture = &storage[dst_texture_id];
|
||||
|
||||
let dst_raw = dst_texture
|
||||
.inner
|
||||
.as_raw()
|
||||
@ -277,7 +262,7 @@ pub(crate) fn clear_texture_no_device<A: hal::Api>(
|
||||
};
|
||||
|
||||
let selector = TextureSelector {
|
||||
levels: range.mip_range.clone(),
|
||||
mips: range.mip_range.clone(),
|
||||
layers: range.layer_range.clone(),
|
||||
};
|
||||
|
||||
@ -287,14 +272,13 @@ pub(crate) fn clear_texture_no_device<A: hal::Api>(
|
||||
// On the other hand, when coming via command_encoder_clear_texture, the life_guard is still there since in order to call it a texture object is needed.
|
||||
//
|
||||
// We could in theory distinguish these two scenarios in the internal clear_texture api in order to remove this check and call the cheaper change_replace_tracked whenever possible.
|
||||
let dst_barrier = if let Some(ref_count) = dst_texture.life_guard().ref_count.as_ref() {
|
||||
texture_tracker.change_replace(dst_texture_id, ref_count, selector, clear_usage)
|
||||
} else {
|
||||
texture_tracker.change_replace_tracked(dst_texture_id, selector, clear_usage)
|
||||
}
|
||||
.map(|pending| pending.into_hal(dst_texture));
|
||||
let dst_barrier = texture_tracker
|
||||
.set_single(storage, dst_texture_id.0, selector, clear_usage)
|
||||
.unwrap()
|
||||
.1
|
||||
.map(|pending| pending.into_hal(dst_texture));
|
||||
unsafe {
|
||||
encoder.transition_textures(dst_barrier);
|
||||
encoder.transition_textures(dst_barrier.into_iter());
|
||||
}
|
||||
|
||||
// Record actual clearing
|
||||
|
@ -14,8 +14,9 @@ use crate::{
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
|
||||
id,
|
||||
init_tracker::MemoryInitKind,
|
||||
resource::{Buffer, Texture},
|
||||
track::{StatefulTrackerSubset, TrackerSet, UsageConflict, UseExtendError},
|
||||
pipeline,
|
||||
resource::{self, Buffer, Texture},
|
||||
track::{Tracker, UsageConflict, UsageScope},
|
||||
validation::{check_buffer_usage, MissingBufferUsageError},
|
||||
Label,
|
||||
};
|
||||
@ -228,15 +229,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
struct State<A: HalApi> {
|
||||
binder: Binder,
|
||||
pipeline: Option<id::ComputePipelineId>,
|
||||
trackers: StatefulTrackerSubset,
|
||||
scope: UsageScope<A>,
|
||||
debug_scope_depth: u32,
|
||||
}
|
||||
|
||||
impl State {
|
||||
impl<A: HalApi> State<A> {
|
||||
fn is_ready(&self) -> Result<(), DispatchError> {
|
||||
let bind_mask = self.binder.invalid_mask();
|
||||
if bind_mask != 0 {
|
||||
@ -253,32 +253,36 @@ impl State {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_states<A: HalApi>(
|
||||
fn flush_states(
|
||||
&mut self,
|
||||
raw_encoder: &mut A::CommandEncoder,
|
||||
base_trackers: &mut TrackerSet,
|
||||
base_trackers: &mut Tracker<A>,
|
||||
bind_group_guard: &Storage<BindGroup<A>, id::BindGroupId>,
|
||||
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
|
||||
texture_guard: &Storage<Texture<A>, id::TextureId>,
|
||||
) -> Result<(), UsageConflict> {
|
||||
for id in self.binder.list_active() {
|
||||
self.trackers.merge_extend(&bind_group_guard[id].used)?;
|
||||
//Note: stateless trackers are not merged: the lifetime reference
|
||||
unsafe {
|
||||
self.scope
|
||||
.merge_bind_group(texture_guard, &bind_group_guard[id].used)?
|
||||
};
|
||||
// Note: stateless trackers are not merged: the lifetime reference
|
||||
// is held to the bind group itself.
|
||||
}
|
||||
|
||||
for id in self.binder.list_active() {
|
||||
unsafe {
|
||||
base_trackers.set_and_remove_from_usage_scope_sparse(
|
||||
texture_guard,
|
||||
&mut self.scope,
|
||||
&bind_group_guard[id].used,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("Encoding dispatch barriers");
|
||||
|
||||
CommandBuffer::insert_barriers(
|
||||
raw_encoder,
|
||||
base_trackers,
|
||||
&self.trackers.buffers,
|
||||
&self.trackers.textures,
|
||||
buffer_guard,
|
||||
texture_guard,
|
||||
);
|
||||
|
||||
self.trackers.clear();
|
||||
CommandBuffer::drain_barriers(raw_encoder, base_trackers, buffer_guard, texture_guard);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -338,7 +342,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let mut state = State {
|
||||
binder: Binder::new(),
|
||||
pipeline: None,
|
||||
trackers: StatefulTrackerSubset::new(A::VARIANT),
|
||||
scope: UsageScope::new(&*buffer_guard, &*texture_guard),
|
||||
debug_scope_depth: 0,
|
||||
};
|
||||
let mut temp_offsets = Vec::new();
|
||||
@ -346,6 +350,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let mut string_offset = 0;
|
||||
let mut active_query = None;
|
||||
|
||||
cmd_buf.trackers.set_size(
|
||||
Some(&*buffer_guard),
|
||||
Some(&*texture_guard),
|
||||
None,
|
||||
None,
|
||||
Some(&*bind_group_guard),
|
||||
Some(&*pipeline_guard),
|
||||
None,
|
||||
None,
|
||||
Some(&*query_set_guard),
|
||||
);
|
||||
|
||||
let hal_desc = hal::ComputePassDescriptor { label: base.label };
|
||||
unsafe {
|
||||
raw.begin_compute_pass(&hal_desc);
|
||||
@ -379,11 +395,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
);
|
||||
dynamic_offset_count += num_dynamic_offsets as usize;
|
||||
|
||||
let bind_group = cmd_buf
|
||||
let bind_group: &BindGroup<A> = cmd_buf
|
||||
.trackers
|
||||
.bind_groups
|
||||
.use_extend(&*bind_group_guard, bind_group_id, (), ())
|
||||
.map_err(|_| ComputePassErrorInner::InvalidBindGroup(bind_group_id))
|
||||
.add_single(&*bind_group_guard, bind_group_id)
|
||||
.ok_or(ComputePassErrorInner::InvalidBindGroup(bind_group_id))
|
||||
.map_pass_err(scope)?;
|
||||
bind_group
|
||||
.validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits)
|
||||
@ -434,11 +450,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
state.pipeline = Some(pipeline_id);
|
||||
|
||||
let pipeline = cmd_buf
|
||||
let pipeline: &pipeline::ComputePipeline<A> = cmd_buf
|
||||
.trackers
|
||||
.compute_pipes
|
||||
.use_extend(&*pipeline_guard, pipeline_id, (), ())
|
||||
.map_err(|_| ComputePassErrorInner::InvalidPipeline(pipeline_id))
|
||||
.compute_pipelines
|
||||
.add_single(&*pipeline_guard, pipeline_id)
|
||||
.ok_or(ComputePassErrorInner::InvalidPipeline(pipeline_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
unsafe {
|
||||
@ -587,11 +603,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let indirect_buffer = state
|
||||
.trackers
|
||||
let indirect_buffer: &Buffer<A> = state
|
||||
.scope
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
|
||||
.map_err(|_| ComputePassErrorInner::InvalidIndirectBuffer(buffer_id))
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(indirect_buffer.usage, wgt::BufferUsages::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
@ -670,16 +685,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
} => {
|
||||
let scope = PassErrorScope::WriteTimestamp;
|
||||
|
||||
let query_set = cmd_buf
|
||||
let query_set: &resource::QuerySet<A> = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => {
|
||||
ComputePassErrorInner::InvalidQuerySet(query_set_id)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.add_single(&*query_set_guard, query_set_id)
|
||||
.ok_or(ComputePassErrorInner::InvalidQuerySet(query_set_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
query_set
|
||||
@ -692,16 +702,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
} => {
|
||||
let scope = PassErrorScope::BeginPipelineStatisticsQuery;
|
||||
|
||||
let query_set = cmd_buf
|
||||
let query_set: &resource::QuerySet<A> = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => {
|
||||
ComputePassErrorInner::InvalidQuerySet(query_set_id)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.add_single(&*query_set_guard, query_set_id)
|
||||
.ok_or(ComputePassErrorInner::InvalidQuerySet(query_set_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
query_set
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError},
|
||||
error::ErrorFormatter,
|
||||
id,
|
||||
track::UseExtendError,
|
||||
track::UsageConflict,
|
||||
validation::{MissingBufferUsageError, MissingTextureUsageError},
|
||||
};
|
||||
use wgt::{BufferAddress, BufferSize, Color};
|
||||
@ -13,8 +13,6 @@ use wgt::{BufferAddress, BufferSize, Color};
|
||||
use std::num::NonZeroU32;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type BufferError = UseExtendError<hal::BufferUses>;
|
||||
|
||||
/// Error validating a draw call.
|
||||
#[derive(Clone, Debug, Error, PartialEq)]
|
||||
pub enum DrawError {
|
||||
@ -79,8 +77,8 @@ pub enum RenderCommandError {
|
||||
IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError),
|
||||
#[error("pipeline writes to depth/stencil, while the pass has read-only depth/stencil")]
|
||||
IncompatiblePipelineRods,
|
||||
#[error("buffer {0:?} is in error {1:?}")]
|
||||
Buffer(id::BufferId, BufferError),
|
||||
#[error(transparent)]
|
||||
UsageConflict(#[from] UsageConflict),
|
||||
#[error("buffer {0:?} is destroyed")]
|
||||
DestroyedBuffer(id::BufferId),
|
||||
#[error(transparent)]
|
||||
@ -106,7 +104,11 @@ impl crate::error::PrettyError for RenderCommandError {
|
||||
Self::InvalidPipeline(id) => {
|
||||
fmt.render_pipeline_label(&id);
|
||||
}
|
||||
Self::Buffer(id, ..) | Self::DestroyedBuffer(id) => {
|
||||
Self::UsageConflict(UsageConflict::TextureInvalid { id }) => {
|
||||
fmt.texture_label(&id);
|
||||
}
|
||||
Self::UsageConflict(UsageConflict::BufferInvalid { id })
|
||||
| Self::DestroyedBuffer(id) => {
|
||||
fmt.buffer_label(&id);
|
||||
}
|
||||
_ => {}
|
||||
|
@ -4,11 +4,11 @@ use hal::CommandEncoder;
|
||||
|
||||
use crate::{
|
||||
device::Device,
|
||||
hub::Storage,
|
||||
hub::{HalApi, Storage},
|
||||
id::{self, TextureId},
|
||||
init_tracker::*,
|
||||
resource::{Buffer, Texture},
|
||||
track::{ResourceTracker, TextureState, TrackerSet},
|
||||
track::{TextureTracker, Tracker},
|
||||
FastHashMap,
|
||||
};
|
||||
|
||||
@ -121,36 +121,37 @@ impl CommandBufferTextureMemoryActions {
|
||||
// Utility function that takes discarded surfaces from (several calls to) register_init_action and initializes them on the spot.
|
||||
// Takes care of barriers as well!
|
||||
pub(crate) fn fixup_discarded_surfaces<
|
||||
A: hal::Api,
|
||||
A: HalApi,
|
||||
InitIter: Iterator<Item = TextureSurfaceDiscard>,
|
||||
>(
|
||||
inits: InitIter,
|
||||
encoder: &mut A::CommandEncoder,
|
||||
texture_guard: &Storage<Texture<A>, TextureId>,
|
||||
texture_tracker: &mut ResourceTracker<TextureState>,
|
||||
texture_tracker: &mut TextureTracker<A>,
|
||||
device: &Device<A>,
|
||||
) {
|
||||
for init in inits {
|
||||
clear_texture(
|
||||
texture_guard,
|
||||
id::Valid(init.texture),
|
||||
texture_guard.get(init.texture).unwrap(),
|
||||
TextureInitRange {
|
||||
mip_range: init.mip_level..(init.mip_level + 1),
|
||||
layer_range: init.layer..(init.layer + 1),
|
||||
},
|
||||
encoder,
|
||||
texture_tracker,
|
||||
device,
|
||||
&device.alignments,
|
||||
&device.zero_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> BakedCommands<A> {
|
||||
impl<A: HalApi> BakedCommands<A> {
|
||||
// inserts all buffer initializations that are going to be needed for executing the commands and updates resource init states accordingly
|
||||
pub(crate) fn initialize_buffer_memory(
|
||||
&mut self,
|
||||
device_tracker: &mut TrackerSet,
|
||||
device_tracker: &mut Tracker<A>,
|
||||
buffer_guard: &mut Storage<Buffer<A>, id::BufferId>,
|
||||
) -> Result<(), DestroyedBufferError> {
|
||||
// Gather init ranges for each buffer so we can collapse them.
|
||||
@ -202,11 +203,11 @@ impl<A: hal::Api> BakedCommands<A> {
|
||||
|
||||
// Don't do use_replace since the buffer may already no longer have a ref_count.
|
||||
// However, we *know* that it is currently in use, so the tracker must already know about it.
|
||||
let transition = device_tracker.buffers.change_replace_tracked(
|
||||
id::Valid(buffer_id),
|
||||
(),
|
||||
hal::BufferUses::COPY_DST,
|
||||
);
|
||||
let transition = device_tracker
|
||||
.buffers
|
||||
.set_single(buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
|
||||
.unwrap()
|
||||
.1;
|
||||
|
||||
let buffer = buffer_guard
|
||||
.get_mut(buffer_id)
|
||||
@ -214,8 +215,11 @@ impl<A: hal::Api> BakedCommands<A> {
|
||||
let raw_buf = buffer.raw.as_ref().ok_or(DestroyedBufferError(buffer_id))?;
|
||||
|
||||
unsafe {
|
||||
self.encoder
|
||||
.transition_buffers(transition.map(|pending| pending.into_hal(buffer)));
|
||||
self.encoder.transition_buffers(
|
||||
transition
|
||||
.map(|pending| pending.into_hal(buffer))
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
for range in ranges.iter() {
|
||||
@ -234,7 +238,7 @@ impl<A: hal::Api> BakedCommands<A> {
|
||||
// any textures that are left discarded by this command buffer will be marked as uninitialized
|
||||
pub(crate) fn initialize_texture_memory(
|
||||
&mut self,
|
||||
device_tracker: &mut TrackerSet,
|
||||
device_tracker: &mut Tracker<A>,
|
||||
texture_guard: &mut Storage<Texture<A>, TextureId>,
|
||||
device: &Device<A>,
|
||||
) -> Result<(), DestroyedTextureError> {
|
||||
@ -274,12 +278,13 @@ impl<A: hal::Api> BakedCommands<A> {
|
||||
// TODO: Could we attempt some range collapsing here?
|
||||
for range in ranges.drain(..) {
|
||||
clear_texture(
|
||||
texture_guard,
|
||||
id::Valid(texture_use.id),
|
||||
&*texture,
|
||||
range,
|
||||
&mut self.encoder,
|
||||
&mut device_tracker.textures,
|
||||
device,
|
||||
&device.alignments,
|
||||
&device.zero_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ mod transfer;
|
||||
|
||||
use std::slice;
|
||||
|
||||
pub(crate) use self::clear::clear_texture_no_device;
|
||||
pub(crate) use self::clear::clear_texture;
|
||||
pub use self::{
|
||||
bundle::*, clear::ClearError, compute::*, draw::*, query::*, render::*, transfer::*,
|
||||
};
|
||||
@ -19,11 +19,11 @@ use self::memory_init::CommandBufferTextureMemoryActions;
|
||||
|
||||
use crate::error::{ErrorFormatter, PrettyError};
|
||||
use crate::init_tracker::BufferInitTrackerAction;
|
||||
use crate::track::{Tracker, UsageScope};
|
||||
use crate::{
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
|
||||
id,
|
||||
resource::{Buffer, Texture},
|
||||
track::{BufferState, ResourceTracker, TextureState, TrackerSet},
|
||||
Label, Stored,
|
||||
};
|
||||
|
||||
@ -81,10 +81,10 @@ impl<A: hal::Api> CommandEncoder<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BakedCommands<A: hal::Api> {
|
||||
pub struct BakedCommands<A: HalApi> {
|
||||
pub(crate) encoder: A::CommandEncoder,
|
||||
pub(crate) list: Vec<A::CommandBuffer>,
|
||||
pub(crate) trackers: TrackerSet,
|
||||
pub(crate) trackers: Tracker<A>,
|
||||
buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
|
||||
texture_memory_actions: CommandBufferTextureMemoryActions,
|
||||
}
|
||||
@ -92,11 +92,11 @@ pub struct BakedCommands<A: hal::Api> {
|
||||
pub(crate) struct DestroyedBufferError(pub id::BufferId);
|
||||
pub(crate) struct DestroyedTextureError(pub id::TextureId);
|
||||
|
||||
pub struct CommandBuffer<A: hal::Api> {
|
||||
pub struct CommandBuffer<A: HalApi> {
|
||||
encoder: CommandEncoder<A>,
|
||||
status: CommandEncoderStatus,
|
||||
pub(crate) device_id: Stored<id::DeviceId>,
|
||||
pub(crate) trackers: TrackerSet,
|
||||
pub(crate) trackers: Tracker<A>,
|
||||
buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
|
||||
texture_memory_actions: CommandBufferTextureMemoryActions,
|
||||
limits: wgt::Limits,
|
||||
@ -124,7 +124,7 @@ impl<A: HalApi> CommandBuffer<A> {
|
||||
},
|
||||
status: CommandEncoderStatus::Recording,
|
||||
device_id,
|
||||
trackers: TrackerSet::new(A::VARIANT),
|
||||
trackers: Tracker::new(),
|
||||
buffer_memory_init_actions: Default::default(),
|
||||
texture_memory_actions: Default::default(),
|
||||
limits,
|
||||
@ -138,23 +138,52 @@ impl<A: HalApi> CommandBuffer<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert_barriers(
|
||||
pub(crate) fn insert_barriers_from_tracker(
|
||||
raw: &mut A::CommandEncoder,
|
||||
base: &mut TrackerSet,
|
||||
head_buffers: &ResourceTracker<BufferState>,
|
||||
head_textures: &ResourceTracker<TextureState>,
|
||||
base: &mut Tracker<A>,
|
||||
head: &Tracker<A>,
|
||||
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
|
||||
texture_guard: &Storage<Texture<A>, id::TextureId>,
|
||||
) {
|
||||
profiling::scope!("insert_barriers");
|
||||
debug_assert_eq!(A::VARIANT, base.backend());
|
||||
|
||||
let buffer_barriers = base.buffers.merge_replace(head_buffers).map(|pending| {
|
||||
let buf = &buffer_guard[pending.id];
|
||||
base.buffers.set_from_tracker(&head.buffers);
|
||||
base.textures
|
||||
.set_from_tracker(&*texture_guard, &head.textures);
|
||||
|
||||
Self::drain_barriers(raw, base, buffer_guard, texture_guard);
|
||||
}
|
||||
|
||||
pub(crate) fn insert_barriers_from_scope(
|
||||
raw: &mut A::CommandEncoder,
|
||||
base: &mut Tracker<A>,
|
||||
head: &UsageScope<A>,
|
||||
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
|
||||
texture_guard: &Storage<Texture<A>, id::TextureId>,
|
||||
) {
|
||||
profiling::scope!("insert_barriers");
|
||||
|
||||
base.buffers.set_from_usage_scope(&head.buffers);
|
||||
base.textures
|
||||
.set_from_usage_scope(&*texture_guard, &head.textures);
|
||||
|
||||
Self::drain_barriers(raw, base, buffer_guard, texture_guard);
|
||||
}
|
||||
|
||||
pub(crate) fn drain_barriers(
|
||||
raw: &mut A::CommandEncoder,
|
||||
base: &mut Tracker<A>,
|
||||
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
|
||||
texture_guard: &Storage<Texture<A>, id::TextureId>,
|
||||
) {
|
||||
profiling::scope!("drain_barriers");
|
||||
|
||||
let buffer_barriers = base.buffers.drain().map(|pending| {
|
||||
let buf = unsafe { &buffer_guard.get_unchecked(pending.id) };
|
||||
pending.into_hal(buf)
|
||||
});
|
||||
let texture_barriers = base.textures.merge_replace(head_textures).map(|pending| {
|
||||
let tex = &texture_guard[pending.id];
|
||||
let texture_barriers = base.textures.drain().map(|pending| {
|
||||
let tex = unsafe { texture_guard.get_unchecked(pending.id) };
|
||||
pending.into_hal(tex)
|
||||
});
|
||||
|
||||
@ -165,7 +194,7 @@ impl<A: HalApi> CommandBuffer<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> CommandBuffer<A> {
|
||||
impl<A: HalApi> CommandBuffer<A> {
|
||||
fn get_encoder_mut(
|
||||
storage: &mut Storage<Self, id::CommandEncoderId>,
|
||||
id: id::CommandEncoderId,
|
||||
@ -198,7 +227,7 @@ impl<A: hal::Api> CommandBuffer<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> crate::hub::Resource for CommandBuffer<A> {
|
||||
impl<A: HalApi> crate::hub::Resource for CommandBuffer<A> {
|
||||
const TYPE: &'static str = "CommandBuffer";
|
||||
|
||||
fn life_guard(&self) -> &crate::LifeGuard {
|
||||
@ -321,7 +350,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
cmd_buf.status = CommandEncoderStatus::Finished;
|
||||
//Note: if we want to stop tracking the swapchain texture view,
|
||||
// this is the place to do it.
|
||||
log::trace!("Command buffer {:?} {:#?}", encoder_id, cmd_buf.trackers);
|
||||
log::trace!("Command buffer {:?}", encoder_id);
|
||||
None
|
||||
}
|
||||
CommandEncoderStatus::Finished => Some(CommandEncoderError::NotRecording),
|
||||
|
@ -8,7 +8,6 @@ use crate::{
|
||||
id::{self, Id, TypedId},
|
||||
init_tracker::MemoryInitKind,
|
||||
resource::QuerySet,
|
||||
track::UseExtendError,
|
||||
Epoch, FastHashMap, Index,
|
||||
};
|
||||
use std::{iter, marker::PhantomData};
|
||||
@ -300,11 +299,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let query_set = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => QueryError::InvalidQuerySet(query_set_id),
|
||||
_ => unreachable!(),
|
||||
})?;
|
||||
.add_single(&*query_set_guard, query_set_id)
|
||||
.ok_or(QueryError::InvalidQuerySet(query_set_id))?;
|
||||
|
||||
query_set.validate_and_write_timestamp(raw_encoder, query_set_id, query_index, None)?;
|
||||
|
||||
@ -348,17 +344,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let query_set = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => QueryError::InvalidQuerySet(query_set_id),
|
||||
_ => unreachable!(),
|
||||
})?;
|
||||
.add_single(&*query_set_guard, query_set_id)
|
||||
.ok_or(QueryError::InvalidQuerySet(query_set_id))?;
|
||||
|
||||
let (dst_buffer, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, destination, (), hal::BufferUses::COPY_DST)
|
||||
.map_err(QueryError::InvalidBuffer)?;
|
||||
.set_single(&*buffer_guard, destination, hal::BufferUses::COPY_DST)
|
||||
.ok_or(QueryError::InvalidBuffer(destination))?;
|
||||
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
|
||||
|
||||
if !dst_buffer.usage.contains(wgt::BufferUsages::COPY_DST) {
|
||||
@ -407,7 +400,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
));
|
||||
|
||||
unsafe {
|
||||
raw_encoder.transition_buffers(dst_barrier);
|
||||
raw_encoder.transition_buffers(dst_barrier.into_iter());
|
||||
raw_encoder.copy_query_results(
|
||||
&query_set.raw,
|
||||
start_query..end_query,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
binding_model::BindError,
|
||||
command::{
|
||||
self,
|
||||
bind::Binder,
|
||||
end_pipeline_statistics_query,
|
||||
memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
|
||||
@ -17,8 +18,8 @@ use crate::{
|
||||
id,
|
||||
init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
|
||||
pipeline::PipelineFlags,
|
||||
resource::{Texture, TextureView},
|
||||
track::{StatefulTrackerSubset, TextureSelector, UsageConflict},
|
||||
resource::{self, Buffer, Texture, TextureView},
|
||||
track::{TextureSelector, UsageConflict, UsageScope},
|
||||
validation::{
|
||||
check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError,
|
||||
},
|
||||
@ -38,7 +39,6 @@ use serde::Deserialize;
|
||||
#[cfg(any(feature = "serial-pass", feature = "trace"))]
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::track::UseExtendError;
|
||||
use std::{borrow::Cow, fmt, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, str};
|
||||
|
||||
use super::{memory_init::TextureSurfaceDiscard, CommandBufferTextureMemoryActions};
|
||||
@ -561,9 +561,9 @@ impl<A: hal::Api> TextureView<A> {
|
||||
const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_TARGETS + hal::MAX_COLOR_TARGETS + 1;
|
||||
type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
|
||||
|
||||
struct RenderPassInfo<'a, A: hal::Api> {
|
||||
struct RenderPassInfo<'a, A: HalApi> {
|
||||
context: RenderPassContext,
|
||||
trackers: StatefulTrackerSubset,
|
||||
usage_scope: UsageScope<A>,
|
||||
render_attachments: AttachmentDataVec<RenderAttachment<'a>>, // All render attachments, including depth/stencil
|
||||
is_ds_read_only: bool,
|
||||
extent: wgt::Extent3d,
|
||||
@ -605,7 +605,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
// but recording the discard right away be alright since the texture can't be used during the pass anyways
|
||||
texture_memory_actions.discard(TextureSurfaceDiscard {
|
||||
texture: view.parent_id.value.0,
|
||||
mip_level: view.selector.levels.start,
|
||||
mip_level: view.selector.mips.start,
|
||||
layer: view.selector.layers.start,
|
||||
});
|
||||
}
|
||||
@ -618,6 +618,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
|
||||
cmd_buf: &mut CommandBuffer<A>,
|
||||
view_guard: &'a Storage<TextureView<A>, id::TextureViewId>,
|
||||
buffer_guard: &'a Storage<Buffer<A>, id::BufferId>,
|
||||
texture_guard: &'a Storage<Texture<A>, id::TextureId>,
|
||||
) -> Result<Self, RenderPassErrorInner> {
|
||||
profiling::scope!("start", "RenderPassInfo");
|
||||
@ -699,8 +700,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
let view = cmd_buf
|
||||
.trackers
|
||||
.views
|
||||
.use_extend(&*view_guard, at.view, (), ())
|
||||
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?;
|
||||
.add_single(&*view_guard, at.view)
|
||||
.ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
|
||||
check_multiview(view)?;
|
||||
add_view(view, "depth")?;
|
||||
|
||||
@ -779,7 +780,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
// Both are discarded using the regular path.
|
||||
discarded_surfaces.push(TextureSurfaceDiscard {
|
||||
texture: view.parent_id.value.0,
|
||||
mip_level: view.selector.levels.start,
|
||||
mip_level: view.selector.mips.start,
|
||||
layer: view.selector.layers.start,
|
||||
});
|
||||
}
|
||||
@ -808,8 +809,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
let color_view = cmd_buf
|
||||
.trackers
|
||||
.views
|
||||
.use_extend(&*view_guard, at.view, (), ())
|
||||
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?;
|
||||
.add_single(&*view_guard, at.view)
|
||||
.ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
|
||||
check_multiview(color_view)?;
|
||||
add_view(color_view, "color")?;
|
||||
|
||||
@ -838,8 +839,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
let resolve_view = cmd_buf
|
||||
.trackers
|
||||
.views
|
||||
.use_extend(&*view_guard, resolve_target, (), ())
|
||||
.map_err(|_| RenderPassErrorInner::InvalidAttachment(resolve_target))?;
|
||||
.add_single(&*view_guard, resolve_target)
|
||||
.ok_or(RenderPassErrorInner::InvalidAttachment(resolve_target))?;
|
||||
|
||||
check_multiview(resolve_view)?;
|
||||
if color_view.extent != resolve_view.extent {
|
||||
@ -934,7 +935,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
|
||||
Ok(Self {
|
||||
context,
|
||||
trackers: StatefulTrackerSubset::new(A::VARIANT),
|
||||
usage_scope: UsageScope::new(buffer_guard, texture_guard),
|
||||
render_attachments,
|
||||
is_ds_read_only,
|
||||
extent,
|
||||
@ -949,7 +950,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
mut self,
|
||||
raw: &mut A::CommandEncoder,
|
||||
texture_guard: &Storage<Texture<A>, id::TextureId>,
|
||||
) -> Result<(StatefulTrackerSubset, SurfacesInDiscardState), RenderPassErrorInner> {
|
||||
) -> Result<(UsageScope<A>, SurfacesInDiscardState), RenderPassErrorInner> {
|
||||
profiling::scope!("finish", "RenderPassInfo");
|
||||
unsafe {
|
||||
raw.end_render_pass();
|
||||
@ -963,15 +964,18 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
check_texture_usage(texture.desc.usage, TextureUsages::RENDER_ATTACHMENT)?;
|
||||
|
||||
// the tracker set of the pass is always in "extend" mode
|
||||
self.trackers
|
||||
.textures
|
||||
.change_extend(
|
||||
ra.texture_id.value,
|
||||
&ra.texture_id.ref_count,
|
||||
ra.selector.clone(),
|
||||
ra.usage,
|
||||
)
|
||||
.map_err(UsageConflict::from)?;
|
||||
unsafe {
|
||||
self.usage_scope
|
||||
.textures
|
||||
.merge_single(
|
||||
&*texture_guard,
|
||||
ra.texture_id.value,
|
||||
Some(ra.selector.clone()),
|
||||
&ra.texture_id.ref_count,
|
||||
ra.usage,
|
||||
)
|
||||
.map_err(UsageConflict::from)?
|
||||
};
|
||||
}
|
||||
|
||||
// If either only stencil or depth was discarded, we put in a special clear pass to keep the init status of the aspects in sync.
|
||||
@ -1012,7 +1016,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok((self.trackers, self.pending_discard_init_fixups))
|
||||
Ok((self.usage_scope, self.pending_discard_init_fixups))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1047,7 +1051,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let mut token = Token::root();
|
||||
let (device_guard, mut token) = hub.devices.read(&mut token);
|
||||
|
||||
let (trackers, query_reset_state, pending_discard_init_fixups) = {
|
||||
let (scope, query_reset_state, pending_discard_init_fixups) = {
|
||||
let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
|
||||
// Spell out the type, to placate rust-analyzer.
|
||||
@ -1075,7 +1079,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (bundle_guard, mut token) = hub.render_bundles.read(&mut token);
|
||||
let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token);
|
||||
let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
|
||||
let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token);
|
||||
let (render_pipeline_guard, mut token) = hub.render_pipelines.read(&mut token);
|
||||
let (query_set_guard, mut token) = hub.query_sets.read(&mut token);
|
||||
let (buffer_guard, mut token) = hub.buffers.read(&mut token);
|
||||
let (texture_guard, mut token) = hub.textures.read(&mut token);
|
||||
@ -1093,10 +1097,23 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
depth_stencil_attachment,
|
||||
cmd_buf,
|
||||
&*view_guard,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
)
|
||||
.map_pass_err(init_scope)?;
|
||||
|
||||
cmd_buf.trackers.set_size(
|
||||
Some(&*buffer_guard),
|
||||
Some(&*texture_guard),
|
||||
Some(&*view_guard),
|
||||
None,
|
||||
Some(&*bind_group_guard),
|
||||
None,
|
||||
Some(&*render_pipeline_guard),
|
||||
Some(&*bundle_guard),
|
||||
Some(&*query_set_guard),
|
||||
);
|
||||
|
||||
let raw = &mut cmd_buf.encoder.raw;
|
||||
|
||||
let mut state = State {
|
||||
@ -1142,17 +1159,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let bind_group = cmd_buf
|
||||
.trackers
|
||||
.bind_groups
|
||||
.use_extend(&*bind_group_guard, bind_group_id, (), ())
|
||||
.map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id))
|
||||
.add_single(&*bind_group_guard, bind_group_id)
|
||||
.ok_or(RenderCommandError::InvalidBindGroup(bind_group_id))
|
||||
.map_pass_err(scope)?;
|
||||
bind_group
|
||||
.validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
// merge the resource tracker in
|
||||
info.trackers
|
||||
.merge_extend(&bind_group.used)
|
||||
.map_pass_err(scope)?;
|
||||
unsafe {
|
||||
info.usage_scope
|
||||
.merge_bind_group(&*texture_guard, &bind_group.used)
|
||||
.map_pass_err(scope)?;
|
||||
}
|
||||
//Note: stateless trackers are not merged: the lifetime reference
|
||||
// is held to the bind group itself.
|
||||
|
||||
@ -1203,9 +1222,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
let pipeline = cmd_buf
|
||||
.trackers
|
||||
.render_pipes
|
||||
.use_extend(&*pipeline_guard, pipeline_id, (), ())
|
||||
.map_err(|_| RenderCommandError::InvalidPipeline(pipeline_id))
|
||||
.render_pipelines
|
||||
.add_single(&*render_pipeline_guard, pipeline_id)
|
||||
.ok_or(RenderCommandError::InvalidPipeline(pipeline_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
info.context
|
||||
@ -1312,11 +1331,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
size,
|
||||
} => {
|
||||
let scope = PassErrorScope::SetIndexBuffer(buffer_id);
|
||||
let buffer = info
|
||||
.trackers
|
||||
let buffer: &Buffer<A> = info
|
||||
.usage_scope
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDEX)
|
||||
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(buffer.usage, BufferUsages::INDEX)
|
||||
.map_pass_err(scope)?;
|
||||
@ -1359,11 +1377,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
size,
|
||||
} => {
|
||||
let scope = PassErrorScope::SetVertexBuffer(buffer_id);
|
||||
let buffer = info
|
||||
.trackers
|
||||
let buffer: &Buffer<A> = info
|
||||
.usage_scope
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::VERTEX)
|
||||
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(buffer.usage, BufferUsages::VERTEX)
|
||||
.map_pass_err(scope)?;
|
||||
@ -1617,11 +1634,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let indirect_buffer = info
|
||||
.trackers
|
||||
let indirect_buffer: &Buffer<A> = info
|
||||
.usage_scope
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
|
||||
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
@ -1688,11 +1704,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let indirect_buffer = info
|
||||
.trackers
|
||||
let indirect_buffer: &Buffer<A> = info
|
||||
.usage_scope
|
||||
.buffers
|
||||
.use_extend(&*buffer_guard, buffer_id, (), hal::BufferUses::INDIRECT)
|
||||
.map_err(|e| RenderCommandError::Buffer(buffer_id, e))
|
||||
.merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(indirect_buffer.usage, BufferUsages::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
@ -1702,16 +1717,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let count_buffer = info
|
||||
.trackers
|
||||
let count_buffer: &Buffer<A> = info
|
||||
.usage_scope
|
||||
.buffers
|
||||
.use_extend(
|
||||
.merge_single(
|
||||
&*buffer_guard,
|
||||
count_buffer_id,
|
||||
(),
|
||||
hal::BufferUses::INDIRECT,
|
||||
)
|
||||
.map_err(|e| RenderCommandError::Buffer(count_buffer_id, e))
|
||||
.map_pass_err(scope)?;
|
||||
check_buffer_usage(count_buffer.usage, BufferUsages::INDIRECT)
|
||||
.map_pass_err(scope)?;
|
||||
@ -1814,16 +1827,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
} => {
|
||||
let scope = PassErrorScope::WriteTimestamp;
|
||||
|
||||
let query_set = cmd_buf
|
||||
let query_set: &resource::QuerySet<A> = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => {
|
||||
RenderCommandError::InvalidQuerySet(query_set_id)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.add_single(&*query_set_guard, query_set_id)
|
||||
.ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
query_set
|
||||
@ -1841,16 +1849,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
} => {
|
||||
let scope = PassErrorScope::BeginPipelineStatisticsQuery;
|
||||
|
||||
let query_set = cmd_buf
|
||||
let query_set: &resource::QuerySet<A> = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
.use_extend(&*query_set_guard, query_set_id, (), ())
|
||||
.map_err(|e| match e {
|
||||
UseExtendError::InvalidResource => {
|
||||
RenderCommandError::InvalidQuerySet(query_set_id)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.add_single(&*query_set_guard, query_set_id)
|
||||
.ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
query_set
|
||||
@ -1871,11 +1874,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
RenderCommand::ExecuteBundle(bundle_id) => {
|
||||
let scope = PassErrorScope::ExecuteBundle;
|
||||
let bundle = cmd_buf
|
||||
let bundle: &command::RenderBundle<A> = cmd_buf
|
||||
.trackers
|
||||
.bundles
|
||||
.use_extend(&*bundle_guard, bundle_id, (), ())
|
||||
.map_err(|_| RenderCommandError::InvalidRenderBundle(bundle_id))
|
||||
.add_single(&*bundle_guard, bundle_id)
|
||||
.ok_or(RenderCommandError::InvalidRenderBundle(bundle_id))
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
info.context
|
||||
@ -1913,7 +1916,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
raw,
|
||||
&*pipeline_layout_guard,
|
||||
&*bind_group_guard,
|
||||
&*pipeline_guard,
|
||||
&*render_pipeline_guard,
|
||||
&*buffer_guard,
|
||||
)
|
||||
}
|
||||
@ -1927,23 +1930,21 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
})
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
info.trackers
|
||||
.merge_extend(&bundle.used)
|
||||
.map_pass_err(scope)?;
|
||||
// Start tracking the bind groups specifically, as they are the only
|
||||
// compound resources, to make it easier to update submission indices
|
||||
// later at submission time.
|
||||
cmd_buf
|
||||
.trackers
|
||||
.bind_groups
|
||||
.merge_extend(&bundle.used.bind_groups)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
info.usage_scope
|
||||
.merge_render_bundle(&*texture_guard, &bundle.used)
|
||||
.map_pass_err(scope)?;
|
||||
cmd_buf
|
||||
.trackers
|
||||
.add_from_render_bundle(&bundle.used)
|
||||
.map_pass_err(scope)?;
|
||||
};
|
||||
state.reset_bundle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("Merging {:?} with the render pass", encoder_id);
|
||||
log::trace!("Merging renderpass into cmd_buf {:?}", encoder_id);
|
||||
let (trackers, pending_discard_init_fixups) =
|
||||
info.finish(raw, &*texture_guard).map_pass_err(init_scope)?;
|
||||
|
||||
@ -1977,11 +1978,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.map_err(RenderCommandError::InvalidQuerySet)
|
||||
.map_pass_err(PassErrorScope::QueryReset)?;
|
||||
|
||||
super::CommandBuffer::insert_barriers(
|
||||
super::CommandBuffer::insert_barriers_from_scope(
|
||||
transit,
|
||||
&mut cmd_buf.trackers,
|
||||
&trackers.buffers,
|
||||
&trackers.textures,
|
||||
&scope,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
);
|
||||
|
@ -1,12 +1,12 @@
|
||||
#[cfg(feature = "trace")]
|
||||
use crate::device::trace::Command as TraceCommand;
|
||||
use crate::{
|
||||
command::{CommandBuffer, CommandEncoderError},
|
||||
command::{clear_texture, CommandBuffer, CommandEncoderError},
|
||||
conv,
|
||||
device::{Device, MissingDownlevelFlags},
|
||||
error::{ErrorFormatter, PrettyError},
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
|
||||
id::{BufferId, CommandEncoderId, Id, TextureId, Valid},
|
||||
id::{BufferId, CommandEncoderId, TextureId, Valid},
|
||||
init_tracker::{
|
||||
has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,
|
||||
TextureInitTrackerAction,
|
||||
@ -15,14 +15,13 @@ use crate::{
|
||||
track::TextureSelector,
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use hal::CommandEncoder as _;
|
||||
use thiserror::Error;
|
||||
use wgt::{BufferAddress, BufferUsages, Extent3d, TextureUsages};
|
||||
|
||||
use std::iter;
|
||||
|
||||
use super::clear::clear_texture;
|
||||
|
||||
pub type ImageCopyBuffer = wgt::ImageCopyBuffer<BufferId>;
|
||||
pub type ImageCopyTexture = wgt::ImageCopyTexture<TextureId>;
|
||||
|
||||
@ -191,7 +190,7 @@ pub(crate) fn extract_texture_selector<A: hal::Api>(
|
||||
aspect: copy_aspect,
|
||||
};
|
||||
let selector = TextureSelector {
|
||||
levels: copy_texture.mip_level..copy_texture.mip_level + 1,
|
||||
mips: copy_texture.mip_level..copy_texture.mip_level + 1,
|
||||
layers,
|
||||
};
|
||||
|
||||
@ -381,14 +380,13 @@ pub(crate) fn validate_texture_copy_range(
|
||||
Ok((copy_extent, array_layer_count))
|
||||
}
|
||||
|
||||
fn handle_texture_init<A: hal::Api>(
|
||||
fn handle_texture_init<A: HalApi>(
|
||||
init_kind: MemoryInitKind,
|
||||
cmd_buf: &mut CommandBuffer<A>,
|
||||
device: &Device<A>,
|
||||
copy_texture: &ImageCopyTexture,
|
||||
copy_size: &Extent3d,
|
||||
texture_guard: &Storage<Texture<A>, Id<Texture<hal::api::Empty>>>,
|
||||
texture: &Texture<A>,
|
||||
texture_guard: &Storage<Texture<A>, TextureId>,
|
||||
) {
|
||||
let init_action = TextureInitTrackerAction {
|
||||
id: copy_texture.texture,
|
||||
@ -410,15 +408,16 @@ fn handle_texture_init<A: hal::Api>(
|
||||
let cmd_buf_raw = cmd_buf.encoder.open();
|
||||
for init in immediate_inits {
|
||||
clear_texture(
|
||||
texture_guard,
|
||||
Valid(init.texture),
|
||||
texture,
|
||||
TextureInitRange {
|
||||
mip_range: init.mip_level..(init.mip_level + 1),
|
||||
layer_range: init.layer..(init.layer + 1),
|
||||
},
|
||||
cmd_buf_raw,
|
||||
&mut cmd_buf.trackers.textures,
|
||||
device,
|
||||
&device.alignments,
|
||||
&device.zero_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
@ -426,14 +425,14 @@ fn handle_texture_init<A: hal::Api>(
|
||||
}
|
||||
|
||||
// Ensures the source texture of a transfer is in the right initialization state and records the state for after the transfer operation.
|
||||
fn handle_src_texture_init<A: hal::Api>(
|
||||
fn handle_src_texture_init<A: HalApi>(
|
||||
cmd_buf: &mut CommandBuffer<A>,
|
||||
device: &Device<A>,
|
||||
source: &ImageCopyTexture,
|
||||
copy_size: &Extent3d,
|
||||
texture_guard: &Storage<Texture<A>, TextureId>,
|
||||
) -> Result<(), TransferError> {
|
||||
let texture = texture_guard
|
||||
let _ = texture_guard
|
||||
.get(source.texture)
|
||||
.map_err(|_| TransferError::InvalidTexture(source.texture))?;
|
||||
|
||||
@ -444,13 +443,12 @@ fn handle_src_texture_init<A: hal::Api>(
|
||||
source,
|
||||
copy_size,
|
||||
texture_guard,
|
||||
texture,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Ensures the destination texture of a transfer is in the right initialization state and records the state for after the transfer operation.
|
||||
fn handle_dst_texture_init<A: hal::Api>(
|
||||
fn handle_dst_texture_init<A: HalApi>(
|
||||
cmd_buf: &mut CommandBuffer<A>,
|
||||
device: &Device<A>,
|
||||
destination: &ImageCopyTexture,
|
||||
@ -480,7 +478,6 @@ fn handle_dst_texture_init<A: hal::Api>(
|
||||
destination,
|
||||
copy_size,
|
||||
texture_guard,
|
||||
texture,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@ -521,8 +518,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (src_buffer, src_pending) = cmd_buf
|
||||
.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, source, (), hal::BufferUses::COPY_SRC)
|
||||
.map_err(TransferError::InvalidBuffer)?;
|
||||
.set_single(&*buffer_guard, source, hal::BufferUses::COPY_SRC)
|
||||
.ok_or(TransferError::InvalidBuffer(source))?;
|
||||
let src_raw = src_buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
@ -531,15 +528,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
return Err(TransferError::MissingCopySrcUsageFlag.into());
|
||||
}
|
||||
// expecting only a single barrier
|
||||
let src_barrier = src_pending
|
||||
.map(|pending| pending.into_hal(src_buffer))
|
||||
.next();
|
||||
let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer));
|
||||
|
||||
let (dst_buffer, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, destination, (), hal::BufferUses::COPY_DST)
|
||||
.map_err(TransferError::InvalidBuffer)?;
|
||||
.set_single(&*buffer_guard, destination, hal::BufferUses::COPY_DST)
|
||||
.ok_or(TransferError::InvalidBuffer(destination))?;
|
||||
let dst_raw = dst_buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
@ -547,9 +542,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
if !dst_buffer.usage.contains(BufferUsages::COPY_DST) {
|
||||
return Err(TransferError::MissingCopyDstUsageFlag(Some(destination), None).into());
|
||||
}
|
||||
let dst_barrier = dst_pending
|
||||
.map(|pending| pending.into_hal(dst_buffer))
|
||||
.next();
|
||||
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
|
||||
|
||||
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedCopySize(size).into());
|
||||
@ -659,8 +652,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (src_buffer, src_pending) = cmd_buf
|
||||
.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, source.buffer, (), hal::BufferUses::COPY_SRC)
|
||||
.map_err(TransferError::InvalidBuffer)?;
|
||||
.set_single(&*buffer_guard, source.buffer, hal::BufferUses::COPY_SRC)
|
||||
.ok_or(TransferError::InvalidBuffer(source.buffer))?;
|
||||
let src_raw = src_buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
@ -668,18 +661,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
if !src_buffer.usage.contains(BufferUsages::COPY_SRC) {
|
||||
return Err(TransferError::MissingCopySrcUsageFlag.into());
|
||||
}
|
||||
let src_barriers = src_pending.map(|pending| pending.into_hal(src_buffer));
|
||||
let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer));
|
||||
|
||||
let (dst_texture, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
.textures
|
||||
.use_replace(
|
||||
.set_single(
|
||||
&*texture_guard,
|
||||
destination.texture,
|
||||
dst_range,
|
||||
hal::TextureUses::COPY_DST,
|
||||
)
|
||||
.unwrap();
|
||||
.ok_or(TransferError::InvalidTexture(destination.texture))?;
|
||||
let dst_raw = dst_texture
|
||||
.inner
|
||||
.as_raw()
|
||||
@ -689,7 +682,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
|
||||
);
|
||||
}
|
||||
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture));
|
||||
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_texture));
|
||||
|
||||
let format_desc = dst_texture.desc.format.describe();
|
||||
let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
|
||||
@ -736,8 +729,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
let cmd_buf_raw = cmd_buf.encoder.open();
|
||||
unsafe {
|
||||
cmd_buf_raw.transition_textures(dst_barriers);
|
||||
cmd_buf_raw.transition_buffers(src_barriers);
|
||||
cmd_buf_raw.transition_textures(dst_barrier.into_iter());
|
||||
cmd_buf_raw.transition_buffers(src_barrier.into_iter());
|
||||
cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, regions);
|
||||
}
|
||||
Ok(())
|
||||
@ -786,13 +779,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (src_texture, src_pending) = cmd_buf
|
||||
.trackers
|
||||
.textures
|
||||
.use_replace(
|
||||
.set_single(
|
||||
&*texture_guard,
|
||||
source.texture,
|
||||
src_range,
|
||||
hal::TextureUses::COPY_SRC,
|
||||
)
|
||||
.unwrap();
|
||||
.ok_or(TransferError::InvalidTexture(source.texture))?;
|
||||
let src_raw = src_texture
|
||||
.inner
|
||||
.as_raw()
|
||||
@ -800,18 +793,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
if !src_texture.desc.usage.contains(TextureUsages::COPY_SRC) {
|
||||
return Err(TransferError::MissingCopySrcUsageFlag.into());
|
||||
}
|
||||
let src_barriers = src_pending.map(|pending| pending.into_hal(src_texture));
|
||||
let src_barrier = src_pending.map(|pending| pending.into_hal(src_texture));
|
||||
|
||||
let (dst_buffer, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
.buffers
|
||||
.use_replace(
|
||||
.set_single(
|
||||
&*buffer_guard,
|
||||
destination.buffer,
|
||||
(),
|
||||
hal::BufferUses::COPY_DST,
|
||||
)
|
||||
.map_err(TransferError::InvalidBuffer)?;
|
||||
.ok_or(TransferError::InvalidBuffer(destination.buffer))?;
|
||||
let dst_raw = dst_buffer
|
||||
.raw
|
||||
.as_ref()
|
||||
@ -821,7 +813,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
TransferError::MissingCopyDstUsageFlag(Some(destination.buffer), None).into(),
|
||||
);
|
||||
}
|
||||
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_buffer));
|
||||
let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
|
||||
|
||||
let format_desc = src_texture.desc.format.describe();
|
||||
let (hal_copy_size, array_layer_count) =
|
||||
@ -876,8 +868,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
});
|
||||
let cmd_buf_raw = cmd_buf.encoder.open();
|
||||
unsafe {
|
||||
cmd_buf_raw.transition_buffers(dst_barriers);
|
||||
cmd_buf_raw.transition_textures(src_barriers);
|
||||
cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
|
||||
cmd_buf_raw.transition_textures(src_barrier.into_iter());
|
||||
cmd_buf_raw.copy_texture_to_buffer(
|
||||
src_raw,
|
||||
hal::TextureUses::COPY_SRC,
|
||||
@ -937,13 +929,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (src_texture, src_pending) = cmd_buf
|
||||
.trackers
|
||||
.textures
|
||||
.use_replace(
|
||||
.set_single(
|
||||
&*texture_guard,
|
||||
source.texture,
|
||||
src_range,
|
||||
hal::TextureUses::COPY_SRC,
|
||||
)
|
||||
.unwrap();
|
||||
.ok_or(TransferError::InvalidTexture(source.texture))?;
|
||||
let src_raw = src_texture
|
||||
.inner
|
||||
.as_raw()
|
||||
@ -954,20 +946,21 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
//TODO: try to avoid this the collection. It's needed because both
|
||||
// `src_pending` and `dst_pending` try to hold `trackers.textures` mutably.
|
||||
let mut barriers = src_pending
|
||||
let mut barriers: ArrayVec<_, 2> = src_pending
|
||||
.map(|pending| pending.into_hal(src_texture))
|
||||
.collect::<Vec<_>>();
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let (dst_texture, dst_pending) = cmd_buf
|
||||
.trackers
|
||||
.textures
|
||||
.use_replace(
|
||||
.set_single(
|
||||
&*texture_guard,
|
||||
destination.texture,
|
||||
dst_range,
|
||||
hal::TextureUses::COPY_DST,
|
||||
)
|
||||
.unwrap();
|
||||
.ok_or(TransferError::InvalidTexture(destination.texture))?;
|
||||
let dst_raw = dst_texture
|
||||
.inner
|
||||
.as_raw()
|
||||
|
@ -1,6 +1,10 @@
|
||||
use crate::resource;
|
||||
|
||||
pub fn is_power_of_two(val: u32) -> bool {
|
||||
pub fn is_power_of_two_u16(val: u16) -> bool {
|
||||
val != 0 && (val & (val - 1)) == 0
|
||||
}
|
||||
|
||||
pub fn is_power_of_two_u32(val: u32) -> bool {
|
||||
val != 0 && (val & (val - 1)) == 0
|
||||
}
|
||||
|
||||
@ -53,7 +57,7 @@ pub fn map_buffer_usage(usage: wgt::BufferUsages) -> hal::BufferUses {
|
||||
usage.contains(wgt::BufferUsages::UNIFORM),
|
||||
);
|
||||
u.set(
|
||||
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE,
|
||||
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_READ_WRITE,
|
||||
usage.contains(wgt::BufferUsages::STORAGE),
|
||||
);
|
||||
u.set(
|
||||
@ -81,7 +85,7 @@ pub fn map_texture_usage(
|
||||
usage.contains(wgt::TextureUsages::TEXTURE_BINDING),
|
||||
);
|
||||
u.set(
|
||||
hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_WRITE,
|
||||
hal::TextureUses::STORAGE_READ | hal::TextureUses::STORAGE_READ_WRITE,
|
||||
usage.contains(wgt::TextureUsages::STORAGE_BINDING),
|
||||
);
|
||||
let is_color = aspect.contains(hal::FormatAspects::COLOR);
|
||||
@ -148,7 +152,7 @@ pub fn check_texture_dimension_size(
|
||||
return Err(Tde::LimitExceeded { dim, given, limit });
|
||||
}
|
||||
}
|
||||
if sample_size == 0 || sample_size > sample_limit || !is_power_of_two(sample_size) {
|
||||
if sample_size == 0 || sample_size > sample_limit || !is_power_of_two_u32(sample_size) {
|
||||
return Err(Tde::InvalidSampleCount(sample_size));
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
},
|
||||
hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Token},
|
||||
id, resource,
|
||||
track::TrackerSet,
|
||||
track::{BindGroupStates, RenderBundleScope, Tracker},
|
||||
RefCount, Stored, SubmissionIndex,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
@ -68,16 +68,20 @@ impl SuspectedResources {
|
||||
self.query_sets.extend_from_slice(&other.query_sets);
|
||||
}
|
||||
|
||||
pub(super) fn add_trackers(&mut self, trackers: &TrackerSet) {
|
||||
pub(super) fn add_render_bundle_scope<A: HalApi>(&mut self, trackers: &RenderBundleScope<A>) {
|
||||
self.buffers.extend(trackers.buffers.used());
|
||||
self.textures.extend(trackers.textures.used());
|
||||
self.bind_groups.extend(trackers.bind_groups.used());
|
||||
self.render_pipelines
|
||||
.extend(trackers.render_pipelines.used());
|
||||
self.query_sets.extend(trackers.query_sets.used());
|
||||
}
|
||||
|
||||
pub(super) fn add_bind_group_states<A: HalApi>(&mut self, trackers: &BindGroupStates<A>) {
|
||||
self.buffers.extend(trackers.buffers.used());
|
||||
self.textures.extend(trackers.textures.used());
|
||||
self.texture_views.extend(trackers.views.used());
|
||||
self.samplers.extend(trackers.samplers.used());
|
||||
self.bind_groups.extend(trackers.bind_groups.used());
|
||||
self.compute_pipelines.extend(trackers.compute_pipes.used());
|
||||
self.render_pipelines.extend(trackers.render_pipes.used());
|
||||
self.render_bundles.extend(trackers.bundles.used());
|
||||
self.query_sets.extend(trackers.query_sets.used());
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +277,8 @@ pub(super) struct LifetimeTracker<A: hal::Api> {
|
||||
/// Textures can be used in the upcoming submission by `write_texture`.
|
||||
pub future_suspected_textures: Vec<Stored<id::TextureId>>,
|
||||
|
||||
/// Resources that are suspected for destruction.
|
||||
/// Resources whose user handle has died (i.e. drop/destroy has been called)
|
||||
/// and will likely be ready for destruction soon.
|
||||
pub suspected_resources: SuspectedResources,
|
||||
|
||||
/// Resources used by queue submissions still in flight. One entry per
|
||||
@ -491,7 +496,7 @@ impl<A: HalApi> LifetimeTracker<A> {
|
||||
pub(super) fn triage_suspected<G: GlobalIdentityHandlerFactory>(
|
||||
&mut self,
|
||||
hub: &Hub<A, G>,
|
||||
trackers: &Mutex<TrackerSet>,
|
||||
trackers: &Mutex<Tracker<A>>,
|
||||
#[cfg(feature = "trace")] trace: Option<&Mutex<trace::Trace>>,
|
||||
token: &mut Token<super::Device<A>>,
|
||||
) {
|
||||
@ -510,7 +515,7 @@ impl<A: HalApi> LifetimeTracker<A> {
|
||||
}
|
||||
|
||||
if let Some(res) = hub.render_bundles.unregister_locked(id.0, &mut *guard) {
|
||||
self.suspected_resources.add_trackers(&res.used);
|
||||
self.suspected_resources.add_render_bundle_scope(&res.used);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -529,7 +534,7 @@ impl<A: HalApi> LifetimeTracker<A> {
|
||||
}
|
||||
|
||||
if let Some(res) = hub.bind_groups.unregister_locked(id.0, &mut *guard) {
|
||||
self.suspected_resources.add_trackers(&res.used);
|
||||
self.suspected_resources.add_bind_group_states(&res.used);
|
||||
|
||||
self.suspected_resources
|
||||
.bind_group_layouts
|
||||
@ -670,7 +675,7 @@ impl<A: HalApi> LifetimeTracker<A> {
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.compute_pipelines.drain(..) {
|
||||
if trackers.compute_pipes.remove_abandoned(id) {
|
||||
if trackers.compute_pipelines.remove_abandoned(id) {
|
||||
log::debug!("Compute pipeline {:?} will be destroyed", id);
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(t) = trace {
|
||||
@ -695,7 +700,7 @@ impl<A: HalApi> LifetimeTracker<A> {
|
||||
let mut trackers = trackers.lock();
|
||||
|
||||
for id in self.suspected_resources.render_pipelines.drain(..) {
|
||||
if trackers.render_pipes.remove_abandoned(id) {
|
||||
if trackers.render_pipelines.remove_abandoned(id) {
|
||||
log::debug!("Render pipeline {:?} will be destroyed", id);
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(t) = trace {
|
||||
@ -829,7 +834,7 @@ impl<A: HalApi> LifetimeTracker<A> {
|
||||
&mut self,
|
||||
hub: &Hub<A, G>,
|
||||
raw: &A::Device,
|
||||
trackers: &Mutex<TrackerSet>,
|
||||
trackers: &Mutex<Tracker<A>>,
|
||||
token: &mut Token<super::Device<A>>,
|
||||
) -> Vec<super::BufferMapPendingClosure> {
|
||||
if self.ready_to_map.is_empty() {
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
TextureInitTracker, TextureInitTrackerAction,
|
||||
},
|
||||
instance, pipeline, present, resource,
|
||||
track::{BufferState, TextureSelector, TextureState, TrackerSet, UsageConflict},
|
||||
track::{BindGroupStates, TextureSelector, Tracker},
|
||||
validation::{self, check_buffer_usage, check_texture_usage},
|
||||
FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, RefCount, Stored,
|
||||
SubmissionIndex, DOWNLEVEL_ERROR_MESSAGE,
|
||||
@ -22,7 +22,7 @@ use smallvec::SmallVec;
|
||||
use thiserror::Error;
|
||||
use wgt::{BufferAddress, TextureFormat, TextureViewDimension};
|
||||
|
||||
use std::{borrow::Cow, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, ptr};
|
||||
use std::{borrow::Cow, iter, mem, num::NonZeroU32, ops::Range, ptr};
|
||||
|
||||
mod life;
|
||||
pub mod queue;
|
||||
@ -256,7 +256,7 @@ impl<A: hal::Api> CommandAllocator<A> {
|
||||
/// 1. `life_tracker` is locked after `hub.devices`, enforced by the type system
|
||||
/// 1. `self.trackers` is locked last (unenforced)
|
||||
/// 1. `self.trace` is locked last (unenforced)
|
||||
pub struct Device<A: hal::Api> {
|
||||
pub struct Device<A: HalApi> {
|
||||
pub(crate) raw: A::Device,
|
||||
pub(crate) adapter_id: Stored<id::AdapterId>,
|
||||
pub(crate) queue: A::Queue,
|
||||
@ -281,7 +281,7 @@ pub struct Device<A: hal::Api> {
|
||||
/// All live resources allocated with this [`Device`].
|
||||
///
|
||||
/// Has to be locked temporarily only (locked last)
|
||||
pub(crate) trackers: Mutex<TrackerSet>,
|
||||
pub(crate) trackers: Mutex<Tracker<A>>,
|
||||
// Life tracker should be locked right after the device and before anything else.
|
||||
life_tracker: Mutex<life::LifetimeTracker<A>>,
|
||||
/// Temporary storage for resource management functions. Cleared at the end
|
||||
@ -306,7 +306,7 @@ pub enum CreateDeviceError {
|
||||
FailedToCreateZeroBuffer(#[from] DeviceError),
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Device<A> {
|
||||
impl<A: HalApi> Device<A> {
|
||||
pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> {
|
||||
if self.features.contains(feature) {
|
||||
Ok(())
|
||||
@ -328,7 +328,6 @@ impl<A: hal::Api> Device<A> {
|
||||
}
|
||||
|
||||
impl<A: HalApi> Device<A> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
open: hal::OpenDevice<A>,
|
||||
adapter_id: Stored<id::AdapterId>,
|
||||
@ -394,7 +393,7 @@ impl<A: HalApi> Device<A> {
|
||||
command_allocator: Mutex::new(com_alloc),
|
||||
active_submission_index: 0,
|
||||
fence,
|
||||
trackers: Mutex::new(TrackerSet::new(A::VARIANT)),
|
||||
trackers: Mutex::new(Tracker::new()),
|
||||
life_tracker: Mutex::new(life::LifetimeTracker::new()),
|
||||
temp_suspected: life::SuspectedResources::default(),
|
||||
#[cfg(feature = "trace")]
|
||||
@ -495,7 +494,7 @@ impl<A: HalApi> Device<A> {
|
||||
fn untrack<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>(
|
||||
&'this mut self,
|
||||
hub: &Hub<A, G>,
|
||||
trackers: &TrackerSet,
|
||||
trackers: &Tracker<A>,
|
||||
token: &mut Token<'token, Self>,
|
||||
) {
|
||||
self.temp_suspected.clear();
|
||||
@ -536,12 +535,12 @@ impl<A: HalApi> Device<A> {
|
||||
self.temp_suspected.samplers.push(id);
|
||||
}
|
||||
}
|
||||
for id in trackers.compute_pipes.used() {
|
||||
for id in trackers.compute_pipelines.used() {
|
||||
if compute_pipe_guard[id].life_guard.ref_count.is_none() {
|
||||
self.temp_suspected.compute_pipelines.push(id);
|
||||
}
|
||||
}
|
||||
for id in trackers.render_pipes.used() {
|
||||
for id in trackers.render_pipelines.used() {
|
||||
if render_pipe_guard[id].life_guard.ref_count.is_none() {
|
||||
self.temp_suspected.render_pipelines.push(id);
|
||||
}
|
||||
@ -655,7 +654,7 @@ impl<A: HalApi> Device<A> {
|
||||
desc.array_layer_count(),
|
||||
),
|
||||
full_range: TextureSelector {
|
||||
levels: 0..desc.mip_level_count,
|
||||
mips: 0..desc.mip_level_count,
|
||||
layers: 0..desc.array_layer_count(),
|
||||
},
|
||||
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
|
||||
@ -876,7 +875,7 @@ impl<A: HalApi> Device<A> {
|
||||
_ => texture.desc.array_layer_count(),
|
||||
},
|
||||
};
|
||||
let level_end = texture.full_range.levels.end;
|
||||
let level_end = texture.full_range.mips.end;
|
||||
let layer_end = texture.full_range.layers.end;
|
||||
if required_level_count > level_end {
|
||||
return Err(resource::CreateTextureViewError::TooManyMipLevels {
|
||||
@ -927,7 +926,7 @@ impl<A: HalApi> Device<A> {
|
||||
.array_layer_count
|
||||
.map_or(layer_end, |_| required_layer_count);
|
||||
let selector = TextureSelector {
|
||||
levels: desc.range.base_mip_level..end_level,
|
||||
mips: desc.range.base_mip_level..end_level,
|
||||
layers: desc.range.base_array_layer..end_layer,
|
||||
};
|
||||
|
||||
@ -972,11 +971,11 @@ impl<A: HalApi> Device<A> {
|
||||
wgt::TextureViewDimension::D3 => {
|
||||
hal::TextureUses::RESOURCE
|
||||
| hal::TextureUses::STORAGE_READ
|
||||
| hal::TextureUses::STORAGE_WRITE
|
||||
| hal::TextureUses::STORAGE_READ_WRITE
|
||||
}
|
||||
_ => hal::TextureUses::all(),
|
||||
};
|
||||
let mask_mip_level = if selector.levels.end - selector.levels.start != 1 {
|
||||
let mask_mip_level = if selector.mips.end - selector.mips.start != 1 {
|
||||
hal::TextureUses::RESOURCE
|
||||
} else {
|
||||
hal::TextureUses::all()
|
||||
@ -1058,7 +1057,8 @@ impl<A: HalApi> Device<A> {
|
||||
|
||||
let anisotropy_clamp = if let Some(clamp) = desc.anisotropy_clamp {
|
||||
let clamp = clamp.get();
|
||||
let valid_clamp = clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two(clamp as u32);
|
||||
let valid_clamp =
|
||||
clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two_u32(clamp as u32);
|
||||
if !valid_clamp {
|
||||
return Err(resource::CreateSamplerError::InvalidClamp(clamp));
|
||||
}
|
||||
@ -1486,7 +1486,6 @@ impl<A: HalApi> Device<A> {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_buffer_binding<'a>(
|
||||
bb: &binding_model::BufferBinding,
|
||||
binding: u32,
|
||||
@ -1494,7 +1493,7 @@ impl<A: HalApi> Device<A> {
|
||||
used_buffer_ranges: &mut Vec<BufferInitTrackerAction>,
|
||||
dynamic_binding_info: &mut Vec<binding_model::BindGroupDynamicBindingData>,
|
||||
late_buffer_binding_sizes: &mut FastHashMap<u32, wgt::BufferSize>,
|
||||
used: &mut TrackerSet,
|
||||
used: &mut BindGroupStates<A>,
|
||||
storage: &'a Storage<resource::Buffer<A>, id::BufferId>,
|
||||
limits: &wgt::Limits,
|
||||
) -> Result<hal::BufferBinding<'a, A>, binding_model::CreateBindGroupError> {
|
||||
@ -1525,7 +1524,7 @@ impl<A: HalApi> Device<A> {
|
||||
if read_only {
|
||||
hal::BufferUses::STORAGE_READ
|
||||
} else {
|
||||
hal::BufferUses::STORAGE_READ | hal::BufferUses::STORAGE_WRITE
|
||||
hal::BufferUses::STORAGE_READ_WRITE
|
||||
},
|
||||
limits.max_storage_buffer_binding_size,
|
||||
),
|
||||
@ -1543,8 +1542,8 @@ impl<A: HalApi> Device<A> {
|
||||
|
||||
let buffer = used
|
||||
.buffers
|
||||
.use_extend(storage, bb.buffer_id, (), internal_use)
|
||||
.map_err(|_| Error::InvalidBuffer(bb.buffer_id))?;
|
||||
.add_single(storage, bb.buffer_id, internal_use)
|
||||
.ok_or(Error::InvalidBuffer(bb.buffer_id))?;
|
||||
check_buffer_usage(buffer.usage, pub_usage)?;
|
||||
let raw_buffer = buffer
|
||||
.raw
|
||||
@ -1613,26 +1612,26 @@ impl<A: HalApi> Device<A> {
|
||||
|
||||
fn create_texture_binding(
|
||||
view: &resource::TextureView<A>,
|
||||
texture_guard: &parking_lot::lock_api::RwLockReadGuard<
|
||||
parking_lot::RawRwLock,
|
||||
Storage<resource::Texture<A>, id::Id<resource::Texture<hal::api::Empty>>>,
|
||||
>,
|
||||
texture_guard: &Storage<resource::Texture<A>, id::TextureId>,
|
||||
internal_use: hal::TextureUses,
|
||||
pub_usage: wgt::TextureUsages,
|
||||
used: &mut TrackerSet,
|
||||
used: &mut BindGroupStates<A>,
|
||||
used_texture_ranges: &mut Vec<TextureInitTrackerAction>,
|
||||
) -> Result<(), binding_model::CreateBindGroupError> {
|
||||
// Careful here: the texture may no longer have its own ref count,
|
||||
// if it was deleted by the user.
|
||||
let texture = &texture_guard[view.parent_id.value];
|
||||
used.textures
|
||||
.change_extend(
|
||||
view.parent_id.value,
|
||||
&view.parent_id.ref_count,
|
||||
view.selector.clone(),
|
||||
let texture = used
|
||||
.textures
|
||||
.add_single(
|
||||
texture_guard,
|
||||
view.parent_id.value.0,
|
||||
view.parent_id.ref_count.clone(),
|
||||
Some(view.selector.clone()),
|
||||
internal_use,
|
||||
)
|
||||
.map_err(UsageConflict::from)?;
|
||||
.ok_or(binding_model::CreateBindGroupError::InvalidTexture(
|
||||
view.parent_id.value.0,
|
||||
))?;
|
||||
check_texture_usage(texture.desc.usage, pub_usage)?;
|
||||
|
||||
used_texture_ranges.push(TextureInitTrackerAction {
|
||||
@ -1674,7 +1673,7 @@ impl<A: HalApi> Device<A> {
|
||||
// it needs to be in BGL iteration order, not BG entry order.
|
||||
let mut late_buffer_binding_sizes = FastHashMap::default();
|
||||
// fill out the descriptors
|
||||
let mut used = TrackerSet::new(A::VARIANT);
|
||||
let mut used = BindGroupStates::new();
|
||||
|
||||
let (buffer_guard, mut token) = hub.buffers.read(token);
|
||||
let (texture_guard, mut token) = hub.textures.read(&mut token); //skip token
|
||||
@ -1738,8 +1737,8 @@ impl<A: HalApi> Device<A> {
|
||||
wgt::BindingType::Sampler(ty) => {
|
||||
let sampler = used
|
||||
.samplers
|
||||
.use_extend(&*sampler_guard, id, (), ())
|
||||
.map_err(|_| Error::InvalidSampler(id))?;
|
||||
.add_single(&*sampler_guard, id)
|
||||
.ok_or(Error::InvalidSampler(id))?;
|
||||
|
||||
// Allowed sampler values for filtering and comparison
|
||||
let (allowed_filtering, allowed_comparison) = match ty {
|
||||
@ -1787,8 +1786,8 @@ impl<A: HalApi> Device<A> {
|
||||
for &id in bindings_array.iter() {
|
||||
let sampler = used
|
||||
.samplers
|
||||
.use_extend(&*sampler_guard, id, (), ())
|
||||
.map_err(|_| Error::InvalidSampler(id))?;
|
||||
.add_single(&*sampler_guard, id)
|
||||
.ok_or(Error::InvalidSampler(id))?;
|
||||
hal_samplers.push(&sampler.raw);
|
||||
}
|
||||
|
||||
@ -1797,8 +1796,8 @@ impl<A: HalApi> Device<A> {
|
||||
Br::TextureView(id) => {
|
||||
let view = used
|
||||
.views
|
||||
.use_extend(&*texture_view_guard, id, (), ())
|
||||
.map_err(|_| Error::InvalidTextureView(id))?;
|
||||
.add_single(&*texture_view_guard, id)
|
||||
.ok_or(Error::InvalidTextureView(id))?;
|
||||
let (pub_usage, internal_use) = Self::texture_use_parameters(
|
||||
binding,
|
||||
decl,
|
||||
@ -1828,8 +1827,8 @@ impl<A: HalApi> Device<A> {
|
||||
for &id in bindings_array.iter() {
|
||||
let view = used
|
||||
.views
|
||||
.use_extend(&*texture_view_guard, id, (), ())
|
||||
.map_err(|_| Error::InvalidTextureView(id))?;
|
||||
.add_single(&*texture_view_guard, id)
|
||||
.ok_or(Error::InvalidTextureView(id))?;
|
||||
let (pub_usage, internal_use) =
|
||||
Self::texture_use_parameters(binding, decl, view,
|
||||
"SampledTextureArray, ReadonlyStorageTextureArray or WriteonlyStorageTextureArray")?;
|
||||
@ -1858,6 +1857,8 @@ impl<A: HalApi> Device<A> {
|
||||
});
|
||||
}
|
||||
|
||||
used.optimize();
|
||||
|
||||
hal_entries.sort_by_key(|entry| entry.binding);
|
||||
for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) {
|
||||
if a.binding == b.binding {
|
||||
@ -2018,7 +2019,7 @@ impl<A: HalApi> Device<A> {
|
||||
});
|
||||
}
|
||||
|
||||
let mip_level_count = view.selector.levels.end - view.selector.levels.start;
|
||||
let mip_level_count = view.selector.mips.end - view.selector.mips.start;
|
||||
if mip_level_count != 1 {
|
||||
return Err(Error::InvalidStorageTextureMipLevelCount {
|
||||
binding,
|
||||
@ -2027,7 +2028,7 @@ impl<A: HalApi> Device<A> {
|
||||
}
|
||||
|
||||
let internal_use = match access {
|
||||
wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_WRITE,
|
||||
wgt::StorageTextureAccess::WriteOnly => hal::TextureUses::STORAGE_READ_WRITE,
|
||||
wgt::StorageTextureAccess::ReadOnly => {
|
||||
if !view
|
||||
.format_features
|
||||
@ -2047,7 +2048,7 @@ impl<A: HalApi> Device<A> {
|
||||
return Err(Error::StorageReadNotSupported(view.desc.format));
|
||||
}
|
||||
|
||||
hal::TextureUses::STORAGE_WRITE | hal::TextureUses::STORAGE_READ
|
||||
hal::TextureUses::STORAGE_READ_WRITE
|
||||
}
|
||||
};
|
||||
Ok((wgt::TextureUsages::STORAGE_BINDING, internal_use))
|
||||
@ -2551,7 +2552,7 @@ impl<A: HalApi> Device<A> {
|
||||
|
||||
let samples = {
|
||||
let sc = desc.multisample.count;
|
||||
if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) {
|
||||
if sc == 0 || sc > 32 || !conv::is_power_of_two_u32(sc) {
|
||||
return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc));
|
||||
}
|
||||
sc
|
||||
@ -2879,7 +2880,7 @@ impl<A: HalApi> Device<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Device<A> {
|
||||
impl<A: HalApi> Device<A> {
|
||||
pub(crate) fn destroy_buffer(&self, buffer: resource::Buffer<A>) {
|
||||
if let Some(raw) = buffer.raw {
|
||||
unsafe {
|
||||
@ -2925,7 +2926,7 @@ impl<A: hal::Api> Device<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> crate::hub::Resource for Device<A> {
|
||||
impl<A: HalApi> crate::hub::Resource for Device<A> {
|
||||
const TYPE: &'static str = "Device";
|
||||
|
||||
fn life_guard(&self) -> &LifeGuard {
|
||||
@ -2981,7 +2982,7 @@ pub struct ImplicitPipelineIds<'a, G: GlobalIdentityHandlerFactory> {
|
||||
}
|
||||
|
||||
impl<G: GlobalIdentityHandlerFactory> ImplicitPipelineIds<'_, G> {
|
||||
fn prepare<A: hal::Api>(self, hub: &Hub<A, G>) -> ImplicitPipelineContext {
|
||||
fn prepare<A: HalApi>(self, hub: &Hub<A, G>) -> ImplicitPipelineContext {
|
||||
ImplicitPipelineContext {
|
||||
root_id: hub.pipeline_layouts.prepare(self.root_id).into_id(),
|
||||
group_ids: self
|
||||
@ -3184,8 +3185,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.trackers
|
||||
.lock()
|
||||
.buffers
|
||||
.init(id, ref_count, BufferState::with_usage(buffer_use))
|
||||
.unwrap();
|
||||
.insert_single(id, ref_count, buffer_use);
|
||||
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -3483,19 +3484,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
Ok(texture) => texture,
|
||||
Err(error) => break error,
|
||||
};
|
||||
let num_levels = texture.full_range.levels.end;
|
||||
let num_layers = texture.full_range.layers.end;
|
||||
let ref_count = texture.life_guard.add_ref();
|
||||
|
||||
let id = fid.assign(texture, &mut token);
|
||||
log::info!("Created texture {:?} with {:?}", id, desc);
|
||||
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.textures
|
||||
.init(id, ref_count, TextureState::new(num_levels, num_layers))
|
||||
.unwrap();
|
||||
device.trackers.lock().textures.insert_single(
|
||||
id.0,
|
||||
ref_count,
|
||||
hal::TextureUses::UNINITIALIZED,
|
||||
);
|
||||
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -3561,19 +3560,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
texture.initialization_status = TextureInitTracker::new(desc.mip_level_count, 0);
|
||||
|
||||
let num_levels = texture.full_range.levels.end;
|
||||
let num_layers = texture.full_range.layers.end;
|
||||
let ref_count = texture.life_guard.add_ref();
|
||||
|
||||
let id = fid.assign(texture, &mut token);
|
||||
log::info!("Created texture {:?} with {:?}", id, desc);
|
||||
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.textures
|
||||
.init(id, ref_count, TextureState::new(num_levels, num_layers))
|
||||
.unwrap();
|
||||
device.trackers.lock().textures.insert_single(
|
||||
id.0,
|
||||
ref_count,
|
||||
hal::TextureUses::UNINITIALIZED,
|
||||
);
|
||||
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -3731,12 +3728,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let ref_count = view.life_guard.add_ref();
|
||||
let id = fid.assign(view, &mut token);
|
||||
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.views
|
||||
.init(id, ref_count, PhantomData)
|
||||
.unwrap();
|
||||
device.trackers.lock().views.insert_single(id, ref_count);
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -3829,12 +3821,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let ref_count = sampler.life_guard.add_ref();
|
||||
let id = fid.assign(sampler, &mut token);
|
||||
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.samplers
|
||||
.init(id, ref_count, PhantomData)
|
||||
.unwrap();
|
||||
device.trackers.lock().samplers.insert_single(id, ref_count);
|
||||
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -4092,18 +4080,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let ref_count = bind_group.life_guard.add_ref();
|
||||
|
||||
let id = fid.assign(bind_group, &mut token);
|
||||
log::debug!(
|
||||
"Bind group {:?} {:#?}",
|
||||
id,
|
||||
hub.bind_groups.read(&mut token).0[id].used
|
||||
);
|
||||
log::debug!("Bind group {:?}", id,);
|
||||
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.bind_groups
|
||||
.init(id, ref_count, PhantomData)
|
||||
.unwrap();
|
||||
.insert_single(id, ref_count);
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -4406,16 +4389,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
Err(e) => break e,
|
||||
};
|
||||
|
||||
log::debug!("Render bundle {:#?}", render_bundle.used);
|
||||
log::debug!("Render bundle");
|
||||
let ref_count = render_bundle.life_guard.add_ref();
|
||||
let id = fid.assign(render_bundle, &mut token);
|
||||
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.bundles
|
||||
.init(id, ref_count, PhantomData)
|
||||
.unwrap();
|
||||
device.trackers.lock().bundles.insert_single(id, ref_count);
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -4494,8 +4472,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.trackers
|
||||
.lock()
|
||||
.query_sets
|
||||
.init(id, ref_count, PhantomData)
|
||||
.unwrap();
|
||||
.insert_single(id, ref_count);
|
||||
|
||||
return (id.0, None);
|
||||
};
|
||||
@ -4589,9 +4566,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.render_pipes
|
||||
.init(id, ref_count, PhantomData)
|
||||
.unwrap();
|
||||
.render_pipelines
|
||||
.insert_single(id, ref_count);
|
||||
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -4730,9 +4707,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
device
|
||||
.trackers
|
||||
.lock()
|
||||
.compute_pipes
|
||||
.init(id, ref_count, PhantomData)
|
||||
.unwrap();
|
||||
.compute_pipelines
|
||||
.insert_single(id, ref_count);
|
||||
return (id.0, None);
|
||||
};
|
||||
|
||||
@ -5204,16 +5180,20 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
};
|
||||
log::debug!("Buffer {:?} map state -> Waiting", buffer_id);
|
||||
|
||||
(buffer.device_id.value, buffer.life_guard.add_ref())
|
||||
let device = &device_guard[buffer.device_id.value];
|
||||
|
||||
let ret = (buffer.device_id.value, buffer.life_guard.add_ref());
|
||||
|
||||
let mut trackers = device.trackers.lock();
|
||||
trackers
|
||||
.buffers
|
||||
.set_single(&*buffer_guard, buffer_id, internal_use);
|
||||
trackers.buffers.drain();
|
||||
|
||||
ret
|
||||
};
|
||||
|
||||
let device = &device_guard[device_id];
|
||||
device.trackers.lock().buffers.change_replace(
|
||||
id::Valid(buffer_id),
|
||||
&ref_count,
|
||||
(),
|
||||
internal_use,
|
||||
);
|
||||
|
||||
device
|
||||
.lock_life(&mut token)
|
||||
|
@ -192,7 +192,7 @@ impl<A: hal::Api> PendingWrites<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> super::Device<A> {
|
||||
impl<A: HalApi> super::Device<A> {
|
||||
fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result<StagingData<A>, DeviceError> {
|
||||
profiling::scope!("prepare_stage");
|
||||
let stage_desc = hal::BufferDescriptor {
|
||||
@ -286,8 +286,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let mut trackers = device.trackers.lock();
|
||||
let (dst, transition) = trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, buffer_id, (), hal::BufferUses::COPY_DST)
|
||||
.map_err(TransferError::InvalidBuffer)?;
|
||||
.set_single(&*buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
|
||||
.ok_or(TransferError::InvalidBuffer(buffer_id))?;
|
||||
let dst_raw = dst
|
||||
.raw
|
||||
.as_ref()
|
||||
@ -451,9 +451,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.drain(init_layer_range)
|
||||
.collect::<Vec<std::ops::Range<u32>>>()
|
||||
{
|
||||
crate::command::clear_texture_no_device(
|
||||
crate::command::clear_texture(
|
||||
&*texture_guard,
|
||||
id::Valid(destination.texture),
|
||||
&*dst,
|
||||
TextureInitRange {
|
||||
mip_range: destination.mip_level..(destination.mip_level + 1),
|
||||
layer_range,
|
||||
@ -473,13 +473,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
let (dst, transition) = trackers
|
||||
.textures
|
||||
.use_replace(
|
||||
.set_single(
|
||||
&*texture_guard,
|
||||
destination.texture,
|
||||
selector,
|
||||
hal::TextureUses::COPY_DST,
|
||||
)
|
||||
.unwrap();
|
||||
.ok_or(TransferError::InvalidTexture(destination.texture))?;
|
||||
|
||||
let (hal_copy_size, array_layer_count) =
|
||||
validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?;
|
||||
@ -561,7 +561,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.ok_or(TransferError::InvalidTexture(destination.texture))?;
|
||||
|
||||
unsafe {
|
||||
encoder.transition_textures(transition.map(|pending| pending.into_hal(dst)));
|
||||
encoder
|
||||
.transition_textures(transition.map(|pending| pending.into_hal(dst)).into_iter());
|
||||
encoder.transition_buffers(iter::once(barrier));
|
||||
encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, regions);
|
||||
}
|
||||
@ -594,7 +595,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
device.active_submission_index += 1;
|
||||
let submit_index = device.active_submission_index;
|
||||
let mut active_executions = Vec::new();
|
||||
let mut used_surface_textures = track::ResourceTracker::new(A::VARIANT);
|
||||
let mut used_surface_textures = track::TextureUsageScope::new();
|
||||
|
||||
{
|
||||
let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token);
|
||||
@ -616,12 +617,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
//Note: locking the trackers has to be done after the storages
|
||||
let mut trackers = device.trackers.lock();
|
||||
|
||||
used_surface_textures.set_size(texture_guard.len());
|
||||
|
||||
//TODO: if multiple command buffers are submitted, we can re-use the last
|
||||
// native command buffer of the previous chain instead of always creating
|
||||
// a temporary one, since the chains are not finished.
|
||||
|
||||
// finish all the command buffers first
|
||||
for &cmb_id in command_buffer_ids {
|
||||
#[allow(unused_mut)]
|
||||
let mut cmdbuf = match hub
|
||||
.command_buffers
|
||||
.unregister_locked(cmb_id, &mut *command_buffer_guard)
|
||||
@ -642,7 +646,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
|
||||
// optimize the tracked states
|
||||
cmdbuf.trackers.optimize();
|
||||
// cmdbuf.trackers.optimize();
|
||||
|
||||
// update submission IDs
|
||||
for id in cmdbuf.trackers.buffers.used() {
|
||||
@ -669,33 +673,35 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
for id in cmdbuf.trackers.textures.used() {
|
||||
let texture = &mut texture_guard[id];
|
||||
match texture.inner {
|
||||
let should_extend = match texture.inner {
|
||||
TextureInner::Native { raw: None } => {
|
||||
return Err(QueueSubmitError::DestroyedTexture(id.0));
|
||||
}
|
||||
TextureInner::Native { raw: Some(_) } => {}
|
||||
TextureInner::Native { raw: Some(_) } => false,
|
||||
TextureInner::Surface {
|
||||
ref mut has_work, ..
|
||||
} => {
|
||||
use track::ResourceState as _;
|
||||
|
||||
*has_work = true;
|
||||
let ref_count = cmdbuf.trackers.textures.get_ref_count(id);
|
||||
//TODO: better error handling here?
|
||||
// register it in the temporary tracker.
|
||||
let mut ts = track::TextureState::default();
|
||||
let _ = ts.change(
|
||||
id,
|
||||
texture.full_range.clone(),
|
||||
hal::TextureUses::empty(), //present
|
||||
None,
|
||||
);
|
||||
let _ = used_surface_textures.init(id, ref_count.clone(), ts);
|
||||
true
|
||||
}
|
||||
}
|
||||
};
|
||||
if !texture.life_guard.use_at(submit_index) {
|
||||
device.temp_suspected.textures.push(id);
|
||||
}
|
||||
if should_extend {
|
||||
unsafe {
|
||||
let ref_count = cmdbuf.trackers.textures.get_ref_count(id);
|
||||
used_surface_textures
|
||||
.merge_single(
|
||||
&*texture_guard,
|
||||
id,
|
||||
None,
|
||||
ref_count,
|
||||
hal::TextureUses::PRESENT,
|
||||
)
|
||||
.unwrap();
|
||||
};
|
||||
}
|
||||
}
|
||||
for id in cmdbuf.trackers.views.used() {
|
||||
if !texture_view_guard[id].life_guard.use_at(submit_index) {
|
||||
@ -717,13 +723,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
sampler_guard[sub_id].life_guard.use_at(submit_index);
|
||||
}
|
||||
}
|
||||
assert!(cmdbuf.trackers.samplers.is_empty());
|
||||
for id in cmdbuf.trackers.compute_pipes.used() {
|
||||
// assert!(cmdbuf.trackers.samplers.is_empty());
|
||||
for id in cmdbuf.trackers.compute_pipelines.used() {
|
||||
if !compute_pipe_guard[id].life_guard.use_at(submit_index) {
|
||||
device.temp_suspected.compute_pipelines.push(id);
|
||||
}
|
||||
}
|
||||
for id in cmdbuf.trackers.render_pipes.used() {
|
||||
for id in cmdbuf.trackers.render_pipelines.used() {
|
||||
if !render_pipe_guard[id].life_guard.use_at(submit_index) {
|
||||
device.temp_suspected.render_pipelines.push(id);
|
||||
}
|
||||
@ -741,12 +747,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
// We need to update the submission indices for the contained
|
||||
// state-less (!) resources as well, excluding the bind groups.
|
||||
// They don't get deleted too early if the bundle goes out of scope.
|
||||
for sub_id in bundle.used.compute_pipes.used() {
|
||||
compute_pipe_guard[sub_id].life_guard.use_at(submit_index);
|
||||
}
|
||||
for sub_id in bundle.used.render_pipes.used() {
|
||||
for sub_id in bundle.used.render_pipelines.used() {
|
||||
render_pipe_guard[sub_id].life_guard.use_at(submit_index);
|
||||
}
|
||||
for sub_id in bundle.used.query_sets.used() {
|
||||
query_set_guard[sub_id].life_guard.use_at(submit_index);
|
||||
}
|
||||
}
|
||||
|
||||
let mut baked = cmdbuf.into_baked();
|
||||
@ -766,11 +772,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?;
|
||||
//Note: stateless trackers are not merged:
|
||||
// device already knows these resources exist.
|
||||
CommandBuffer::insert_barriers(
|
||||
CommandBuffer::insert_barriers_from_tracker(
|
||||
&mut baked.encoder,
|
||||
&mut *trackers,
|
||||
&baked.trackers.buffers,
|
||||
&baked.trackers.textures,
|
||||
&baked.trackers,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
);
|
||||
@ -788,19 +793,19 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.begin_encoding(Some("(wgpu internal) Present"))
|
||||
.map_err(DeviceError::from)?
|
||||
};
|
||||
let texture_barriers = trackers
|
||||
trackers
|
||||
.textures
|
||||
.merge_replace(&used_surface_textures)
|
||||
.map(|pending| {
|
||||
let tex = &texture_guard[pending.id];
|
||||
pending.into_hal(tex)
|
||||
});
|
||||
.set_from_usage_scope(&*texture_guard, &used_surface_textures);
|
||||
let texture_barriers = trackers.textures.drain().map(|pending| {
|
||||
let tex = unsafe { texture_guard.get_unchecked(pending.id) };
|
||||
pending.into_hal(tex)
|
||||
});
|
||||
let present = unsafe {
|
||||
baked.encoder.transition_textures(texture_barriers);
|
||||
baked.encoder.end_encoding().unwrap()
|
||||
};
|
||||
baked.list.push(present);
|
||||
used_surface_textures.clear();
|
||||
used_surface_textures = track::TextureUsageScope::new();
|
||||
}
|
||||
|
||||
// done
|
||||
@ -810,7 +815,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
});
|
||||
}
|
||||
|
||||
log::trace!("Device after submission {}: {:#?}", submit_index, trackers);
|
||||
log::trace!("Device after submission {}", submit_index);
|
||||
}
|
||||
|
||||
let super::Device {
|
||||
@ -830,6 +835,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (_, mut token) = hub.buffers.read(&mut token); // skip token
|
||||
let (mut texture_guard, _) = hub.textures.write(&mut token);
|
||||
|
||||
used_surface_textures.set_size(texture_guard.len());
|
||||
|
||||
for &id in pending_writes.dst_textures.iter() {
|
||||
let texture = texture_guard.get_mut(id).unwrap();
|
||||
match texture.inner {
|
||||
@ -840,39 +847,39 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
TextureInner::Surface {
|
||||
ref mut has_work, ..
|
||||
} => {
|
||||
use track::ResourceState as _;
|
||||
|
||||
*has_work = true;
|
||||
let ref_count = texture.life_guard.add_ref();
|
||||
//TODO: better error handling here?
|
||||
// register it in the temporary tracker.
|
||||
let mut ts = track::TextureState::default();
|
||||
let _ = ts.change(
|
||||
id::Valid(id),
|
||||
texture.full_range.clone(),
|
||||
hal::TextureUses::empty(), //present
|
||||
None,
|
||||
);
|
||||
let _ = used_surface_textures.init(id::Valid(id), ref_count, ts);
|
||||
unsafe {
|
||||
used_surface_textures
|
||||
.merge_single(
|
||||
&*texture_guard,
|
||||
id::Valid(id),
|
||||
None,
|
||||
&ref_count,
|
||||
hal::TextureUses::PRESENT,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !used_surface_textures.is_empty() {
|
||||
let mut trackers = device.trackers.lock();
|
||||
let texture_barriers = trackers
|
||||
|
||||
trackers
|
||||
.textures
|
||||
.merge_replace(&used_surface_textures)
|
||||
.map(|pending| {
|
||||
let tex = &texture_guard[pending.id];
|
||||
pending.into_hal(tex)
|
||||
});
|
||||
.set_from_usage_scope(&*texture_guard, &used_surface_textures);
|
||||
let texture_barriers = trackers.textures.drain().map(|pending| {
|
||||
let tex = unsafe { texture_guard.get_unchecked(pending.id) };
|
||||
pending.into_hal(tex)
|
||||
});
|
||||
|
||||
unsafe {
|
||||
pending_writes
|
||||
.command_encoder
|
||||
.transition_textures(texture_barriers);
|
||||
};
|
||||
used_surface_textures.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,6 +194,14 @@ impl<T, I: id::TypedId> Storage<T, I> {
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_unchecked(&self, id: u32) -> &T {
|
||||
match self.map[id as usize] {
|
||||
Element::Occupied(ref v, _) => v,
|
||||
Element::Vacant => panic!("{}[{}] does not exist", self.kind, id),
|
||||
Element::Error(_, _) => panic!(""),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn label_for_invalid_id(&self, id: I) -> &str {
|
||||
let (index, _, _) = id.unzip();
|
||||
match self.map.get(index as usize) {
|
||||
@ -266,6 +274,10 @@ impl<T, I: id::TypedId> Storage<T, I> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
fn generate_report(&self) -> StorageReport {
|
||||
let mut report = StorageReport {
|
||||
element_size: mem::size_of::<T>(),
|
||||
@ -298,56 +310,56 @@ pub enum Root {}
|
||||
impl Access<Instance> for Root {}
|
||||
impl Access<Surface> for Root {}
|
||||
impl Access<Surface> for Instance {}
|
||||
impl<A: hal::Api> Access<Adapter<A>> for Root {}
|
||||
impl<A: hal::Api> Access<Adapter<A>> for Surface {}
|
||||
impl<A: hal::Api> Access<Device<A>> for Root {}
|
||||
impl<A: hal::Api> Access<Device<A>> for Surface {}
|
||||
impl<A: hal::Api> Access<Device<A>> for Adapter<A> {}
|
||||
impl<A: hal::Api> Access<PipelineLayout<A>> for Root {}
|
||||
impl<A: hal::Api> Access<PipelineLayout<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<PipelineLayout<A>> for RenderBundle {}
|
||||
impl<A: hal::Api> Access<BindGroupLayout<A>> for Root {}
|
||||
impl<A: hal::Api> Access<BindGroupLayout<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<BindGroupLayout<A>> for PipelineLayout<A> {}
|
||||
impl<A: hal::Api> Access<BindGroup<A>> for Root {}
|
||||
impl<A: hal::Api> Access<BindGroup<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<BindGroup<A>> for BindGroupLayout<A> {}
|
||||
impl<A: hal::Api> Access<BindGroup<A>> for PipelineLayout<A> {}
|
||||
impl<A: hal::Api> Access<BindGroup<A>> for CommandBuffer<A> {}
|
||||
impl<A: hal::Api> Access<CommandBuffer<A>> for Root {}
|
||||
impl<A: hal::Api> Access<CommandBuffer<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<RenderBundle> for Device<A> {}
|
||||
impl<A: hal::Api> Access<RenderBundle> for CommandBuffer<A> {}
|
||||
impl<A: hal::Api> Access<ComputePipeline<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<ComputePipeline<A>> for BindGroup<A> {}
|
||||
impl<A: hal::Api> Access<RenderPipeline<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<RenderPipeline<A>> for BindGroup<A> {}
|
||||
impl<A: hal::Api> Access<RenderPipeline<A>> for ComputePipeline<A> {}
|
||||
impl<A: hal::Api> Access<QuerySet<A>> for Root {}
|
||||
impl<A: hal::Api> Access<QuerySet<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<QuerySet<A>> for CommandBuffer<A> {}
|
||||
impl<A: hal::Api> Access<QuerySet<A>> for RenderPipeline<A> {}
|
||||
impl<A: hal::Api> Access<QuerySet<A>> for ComputePipeline<A> {}
|
||||
impl<A: hal::Api> Access<QuerySet<A>> for Sampler<A> {}
|
||||
impl<A: hal::Api> Access<ShaderModule<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<ShaderModule<A>> for BindGroupLayout<A> {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for Root {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for BindGroupLayout<A> {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for BindGroup<A> {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for CommandBuffer<A> {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for ComputePipeline<A> {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for RenderPipeline<A> {}
|
||||
impl<A: hal::Api> Access<Buffer<A>> for QuerySet<A> {}
|
||||
impl<A: hal::Api> Access<Texture<A>> for Root {}
|
||||
impl<A: hal::Api> Access<Texture<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<Texture<A>> for Buffer<A> {}
|
||||
impl<A: hal::Api> Access<TextureView<A>> for Root {}
|
||||
impl<A: hal::Api> Access<TextureView<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<TextureView<A>> for Texture<A> {}
|
||||
impl<A: hal::Api> Access<Sampler<A>> for Root {}
|
||||
impl<A: hal::Api> Access<Sampler<A>> for Device<A> {}
|
||||
impl<A: hal::Api> Access<Sampler<A>> for TextureView<A> {}
|
||||
impl<A: HalApi> Access<Adapter<A>> for Root {}
|
||||
impl<A: HalApi> Access<Adapter<A>> for Surface {}
|
||||
impl<A: HalApi> Access<Device<A>> for Root {}
|
||||
impl<A: HalApi> Access<Device<A>> for Surface {}
|
||||
impl<A: HalApi> Access<Device<A>> for Adapter<A> {}
|
||||
impl<A: HalApi> Access<PipelineLayout<A>> for Root {}
|
||||
impl<A: HalApi> Access<PipelineLayout<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<PipelineLayout<A>> for RenderBundle<A> {}
|
||||
impl<A: HalApi> Access<BindGroupLayout<A>> for Root {}
|
||||
impl<A: HalApi> Access<BindGroupLayout<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<BindGroupLayout<A>> for PipelineLayout<A> {}
|
||||
impl<A: HalApi> Access<BindGroup<A>> for Root {}
|
||||
impl<A: HalApi> Access<BindGroup<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<BindGroup<A>> for BindGroupLayout<A> {}
|
||||
impl<A: HalApi> Access<BindGroup<A>> for PipelineLayout<A> {}
|
||||
impl<A: HalApi> Access<BindGroup<A>> for CommandBuffer<A> {}
|
||||
impl<A: HalApi> Access<CommandBuffer<A>> for Root {}
|
||||
impl<A: HalApi> Access<CommandBuffer<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<RenderBundle<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<RenderBundle<A>> for CommandBuffer<A> {}
|
||||
impl<A: HalApi> Access<ComputePipeline<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<ComputePipeline<A>> for BindGroup<A> {}
|
||||
impl<A: HalApi> Access<RenderPipeline<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<RenderPipeline<A>> for BindGroup<A> {}
|
||||
impl<A: HalApi> Access<RenderPipeline<A>> for ComputePipeline<A> {}
|
||||
impl<A: HalApi> Access<QuerySet<A>> for Root {}
|
||||
impl<A: HalApi> Access<QuerySet<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<QuerySet<A>> for CommandBuffer<A> {}
|
||||
impl<A: HalApi> Access<QuerySet<A>> for RenderPipeline<A> {}
|
||||
impl<A: HalApi> Access<QuerySet<A>> for ComputePipeline<A> {}
|
||||
impl<A: HalApi> Access<QuerySet<A>> for Sampler<A> {}
|
||||
impl<A: HalApi> Access<ShaderModule<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<ShaderModule<A>> for BindGroupLayout<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for Root {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for BindGroupLayout<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for BindGroup<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for CommandBuffer<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for ComputePipeline<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for RenderPipeline<A> {}
|
||||
impl<A: HalApi> Access<Buffer<A>> for QuerySet<A> {}
|
||||
impl<A: HalApi> Access<Texture<A>> for Root {}
|
||||
impl<A: HalApi> Access<Texture<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<Texture<A>> for Buffer<A> {}
|
||||
impl<A: HalApi> Access<TextureView<A>> for Root {}
|
||||
impl<A: HalApi> Access<TextureView<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<TextureView<A>> for Texture<A> {}
|
||||
impl<A: HalApi> Access<Sampler<A>> for Root {}
|
||||
impl<A: HalApi> Access<Sampler<A>> for Device<A> {}
|
||||
impl<A: HalApi> Access<Sampler<A>> for TextureView<A> {}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
thread_local! {
|
||||
@ -614,7 +626,7 @@ impl HubReport {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Hub<A: hal::Api, F: GlobalIdentityHandlerFactory> {
|
||||
pub struct Hub<A: HalApi, F: GlobalIdentityHandlerFactory> {
|
||||
pub adapters: Registry<Adapter<A>, id::AdapterId, F>,
|
||||
pub devices: Registry<Device<A>, id::DeviceId, F>,
|
||||
pub pipeline_layouts: Registry<PipelineLayout<A>, id::PipelineLayoutId, F>,
|
||||
@ -622,7 +634,7 @@ pub struct Hub<A: hal::Api, F: GlobalIdentityHandlerFactory> {
|
||||
pub bind_group_layouts: Registry<BindGroupLayout<A>, id::BindGroupLayoutId, F>,
|
||||
pub bind_groups: Registry<BindGroup<A>, id::BindGroupId, F>,
|
||||
pub command_buffers: Registry<CommandBuffer<A>, id::CommandBufferId, F>,
|
||||
pub render_bundles: Registry<RenderBundle, id::RenderBundleId, F>,
|
||||
pub render_bundles: Registry<RenderBundle<A>, id::RenderBundleId, F>,
|
||||
pub render_pipelines: Registry<RenderPipeline<A>, id::RenderPipelineId, F>,
|
||||
pub compute_pipelines: Registry<ComputePipeline<A>, id::ComputePipelineId, F>,
|
||||
pub query_sets: Registry<QuerySet<A>, id::QuerySetId, F>,
|
||||
@ -1008,6 +1020,25 @@ pub trait HalApi: hal::Api {
|
||||
fn get_surface_mut(surface: &mut Surface) -> &mut HalSurface<Self>;
|
||||
}
|
||||
|
||||
impl HalApi for hal::api::Empty {
|
||||
const VARIANT: Backend = Backend::Empty;
|
||||
fn create_instance_from_hal(_: &str, _: Self::Instance) -> Instance {
|
||||
unimplemented!("called empty api")
|
||||
}
|
||||
fn instance_as_hal(_: &Instance) -> Option<&Self::Instance> {
|
||||
unimplemented!("called empty api")
|
||||
}
|
||||
fn hub<G: GlobalIdentityHandlerFactory>(_: &Global<G>) -> &Hub<Self, G> {
|
||||
unimplemented!("called empty api")
|
||||
}
|
||||
fn get_surface(_: &Surface) -> &HalSurface<Self> {
|
||||
unimplemented!("called empty api")
|
||||
}
|
||||
fn get_surface_mut(_: &mut Surface) -> &mut HalSurface<Self> {
|
||||
unimplemented!("called empty api")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(vulkan)]
|
||||
impl HalApi for hal::api::Vulkan {
|
||||
const VARIANT: Backend = Backend::Vulkan;
|
||||
|
@ -64,9 +64,9 @@ impl<T> From<SerialId> for Id<T> {
|
||||
}
|
||||
|
||||
impl<T> Id<T> {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn dummy() -> Valid<Self> {
|
||||
Valid(Id(NonZeroId::new(1).unwrap(), PhantomData))
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn dummy(index: u32) -> Valid<Self> {
|
||||
Valid(Id::zip(index, 1, Backend::Empty))
|
||||
}
|
||||
|
||||
pub fn backend(self) -> Backend {
|
||||
@ -135,7 +135,7 @@ pub(crate) struct Valid<I>(pub I);
|
||||
/// Most `wgpu-core` clients should not use this trait. Unusual clients that
|
||||
/// need to construct `Id` values directly, or access their components, like the
|
||||
/// WGPU recording player, may use this trait to do so.
|
||||
pub trait TypedId {
|
||||
pub trait TypedId: Copy {
|
||||
fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self;
|
||||
fn unzip(self) -> (Index, Epoch, Backend);
|
||||
}
|
||||
@ -184,7 +184,7 @@ pub type CommandBufferId = Id<crate::command::CommandBuffer<Dummy>>;
|
||||
pub type RenderPassEncoderId = *mut crate::command::RenderPass;
|
||||
pub type ComputePassEncoderId = *mut crate::command::ComputePass;
|
||||
pub type RenderBundleEncoderId = *mut crate::command::RenderBundleEncoder;
|
||||
pub type RenderBundleId = Id<crate::command::RenderBundle>;
|
||||
pub type RenderBundleId = Id<crate::command::RenderBundle<Dummy>>;
|
||||
pub type QuerySetId = Id<crate::resource::QuerySet<Dummy>>;
|
||||
|
||||
#[test]
|
||||
|
@ -26,7 +26,7 @@ pub(crate) fn has_copy_partial_init_tracker_coverage(
|
||||
impl From<TextureSelector> for TextureInitRange {
|
||||
fn from(selector: TextureSelector) -> Self {
|
||||
TextureInitRange {
|
||||
mip_range: selector.levels,
|
||||
mip_range: selector.mips,
|
||||
layer_range: selector.layers,
|
||||
}
|
||||
}
|
||||
|
@ -494,6 +494,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
|
||||
#[cfg(dx12)]
|
||||
/// # Safety
|
||||
///
|
||||
/// The visual must be valid and able to be used to make a swapchain with.
|
||||
pub unsafe fn instance_create_surface_from_visual(
|
||||
&self,
|
||||
visual: *mut std::ffi::c_void,
|
||||
|
@ -4,6 +4,8 @@
|
||||
*/
|
||||
|
||||
#![allow(
|
||||
// It is much clearer to assert negative conditions with eq! false
|
||||
clippy::bool_assert_comparison,
|
||||
// We use loops for getting early-out of scope without closures.
|
||||
clippy::never_loop,
|
||||
// We don't use syntax sugar where it's not necessary.
|
||||
@ -16,6 +18,8 @@
|
||||
clippy::new_without_default,
|
||||
// Needless updates are more scaleable, easier to play with features.
|
||||
clippy::needless_update,
|
||||
// Need many arguments for some core functions to be able to re-use code in many situations.
|
||||
clippy::too_many_arguments,
|
||||
// For some reason `rustc` can warn about these in const generics even
|
||||
// though they are required.
|
||||
unused_braces,
|
||||
|
@ -174,7 +174,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
initialization_status: TextureInitTracker::new(1, 1),
|
||||
full_range: track::TextureSelector {
|
||||
layers: 0..1,
|
||||
levels: 0..1,
|
||||
mips: 0..1,
|
||||
},
|
||||
life_guard: LifeGuard::new("<Surface>"),
|
||||
clear_mode: resource::TextureClearMode::RenderPass {
|
||||
@ -187,20 +187,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let id = fid.assign(texture, &mut token);
|
||||
|
||||
{
|
||||
use track::ResourceState as _;
|
||||
// register it in the device tracker as uninitialized
|
||||
let mut trackers = device.trackers.lock();
|
||||
let mut ts = track::TextureState::default();
|
||||
let _ = ts.change(
|
||||
id,
|
||||
track::TextureSelector {
|
||||
layers: 0..1,
|
||||
levels: 0..1,
|
||||
},
|
||||
trackers.textures.insert_single(
|
||||
id.0,
|
||||
ref_count.clone(),
|
||||
hal::TextureUses::UNINITIALIZED,
|
||||
None,
|
||||
);
|
||||
let _ = trackers.textures.init(id, ref_count.clone(), ts);
|
||||
}
|
||||
|
||||
if present.acquired_texture.is_some() {
|
||||
@ -273,6 +266,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
// The texture ID got added to the device tracker by `submit()`,
|
||||
// and now we are moving it away.
|
||||
log::debug!(
|
||||
"Removing swapchain texture {:?} from the device tracker",
|
||||
texture_id.value
|
||||
);
|
||||
device.trackers.lock().textures.remove(texture_id.value);
|
||||
|
||||
let (texture, _) = hub.textures.unregister(texture_id.value.0, &mut token);
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Resource, Token},
|
||||
id::{AdapterId, DeviceId, SurfaceId, TextureId, Valid},
|
||||
init_tracker::{BufferInitTracker, TextureInitTracker},
|
||||
track::{TextureSelector, DUMMY_SELECTOR},
|
||||
track::TextureSelector,
|
||||
validation::MissingBufferUsageError,
|
||||
Label, LifeGuard, RefCount, Stored,
|
||||
};
|
||||
@ -149,12 +149,6 @@ impl<A: hal::Api> Resource for Buffer<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Borrow<()> for Buffer<A> {
|
||||
fn borrow(&self) -> &() {
|
||||
&DUMMY_SELECTOR
|
||||
}
|
||||
}
|
||||
|
||||
pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -448,12 +442,6 @@ impl<A: hal::Api> Resource for TextureView<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Borrow<()> for TextureView<A> {
|
||||
fn borrow(&self) -> &() {
|
||||
&DUMMY_SELECTOR
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a [`Sampler`]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
|
||||
@ -530,11 +518,6 @@ impl<A: hal::Api> Resource for Sampler<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Borrow<()> for Sampler<A> {
|
||||
fn borrow(&self) -> &() {
|
||||
&DUMMY_SELECTOR
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum CreateQuerySetError {
|
||||
#[error(transparent)]
|
||||
@ -565,12 +548,6 @@ impl<A: hal::Api> Resource for QuerySet<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Borrow<()> for QuerySet<A> {
|
||||
fn borrow(&self) -> &() {
|
||||
&DUMMY_SELECTOR
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum DestroyError {
|
||||
#[error("resource is invalid")]
|
||||
|
@ -1,236 +1,778 @@
|
||||
use super::{PendingTransition, ResourceState, Unit};
|
||||
use crate::id::{BufferId, Valid};
|
||||
/*! Buffer Trackers
|
||||
*
|
||||
* Buffers are represented by a single state for the whole resource,
|
||||
* a 16 bit bitflag of buffer usages. Because there is only ever
|
||||
* one subresource, they have no selector.
|
||||
!*/
|
||||
|
||||
use std::{borrow::Cow, marker::PhantomData, vec::Drain};
|
||||
|
||||
use super::PendingTransition;
|
||||
use crate::{
|
||||
hub,
|
||||
id::{BufferId, TypedId, Valid},
|
||||
resource::Buffer,
|
||||
track::{
|
||||
invalid_resource_state, iterate_bitvec_indices, skip_barrier, ResourceMetadata,
|
||||
ResourceMetadataProvider, ResourceUses, UsageConflict,
|
||||
},
|
||||
LifeGuard, RefCount,
|
||||
};
|
||||
use hal::BufferUses;
|
||||
|
||||
pub(crate) type BufferState = Unit<BufferUses>;
|
||||
impl ResourceUses for BufferUses {
|
||||
const EXCLUSIVE: Self = Self::EXCLUSIVE;
|
||||
|
||||
impl PendingTransition<BufferState> {
|
||||
fn collapse(self) -> Result<BufferUses, Self> {
|
||||
if self.usage.start.is_empty()
|
||||
|| self.usage.start == self.usage.end
|
||||
|| !BufferUses::EXCLUSIVE.intersects(self.usage.start | self.usage.end)
|
||||
{
|
||||
Ok(self.usage.start | self.usage.end)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BufferState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
first: None,
|
||||
last: BufferUses::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferState {
|
||||
pub fn with_usage(usage: BufferUses) -> Self {
|
||||
Unit::new(usage)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceState for BufferState {
|
||||
type Id = BufferId;
|
||||
type Selector = ();
|
||||
type Usage = BufferUses;
|
||||
|
||||
fn query(&self, _selector: Self::Selector) -> Option<Self::Usage> {
|
||||
Some(self.last)
|
||||
fn bits(self) -> u16 {
|
||||
Self::bits(&self)
|
||||
}
|
||||
|
||||
fn change(
|
||||
&mut self,
|
||||
id: Valid<Self::Id>,
|
||||
_selector: Self::Selector,
|
||||
usage: Self::Usage,
|
||||
output: Option<&mut Vec<PendingTransition<Self>>>,
|
||||
) -> Result<(), PendingTransition<Self>> {
|
||||
let old = self.last;
|
||||
if old != usage || !BufferUses::ORDERED.contains(usage) {
|
||||
let pending = PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: old..usage,
|
||||
};
|
||||
*self = match output {
|
||||
None => {
|
||||
assert_eq!(
|
||||
self.first, None,
|
||||
"extending a state that is already a transition"
|
||||
);
|
||||
Unit::new(pending.collapse()?)
|
||||
}
|
||||
Some(transitions) => {
|
||||
transitions.push(pending);
|
||||
Unit {
|
||||
first: self.first.or(Some(old)),
|
||||
last: usage,
|
||||
}
|
||||
}
|
||||
};
|
||||
fn all_ordered(self) -> bool {
|
||||
Self::ORDERED.contains(self)
|
||||
}
|
||||
|
||||
fn any_exclusive(self) -> bool {
|
||||
self.intersects(Self::EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores all the buffers that a bind group stores.
|
||||
pub(crate) struct BufferBindGroupState<A: hub::HalApi> {
|
||||
buffers: Vec<(Valid<BufferId>, RefCount, BufferUses)>,
|
||||
|
||||
_phantom: PhantomData<A>,
|
||||
}
|
||||
impl<A: hub::HalApi> BufferBindGroupState<A> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buffers: Vec::new(),
|
||||
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimize the buffer bind group state by sorting it by ID.
|
||||
///
|
||||
/// When this list of states is merged into a tracker, the memory
|
||||
/// accesses will be in a constant assending order.
|
||||
pub(crate) fn optimize(&mut self) {
|
||||
self.buffers
|
||||
.sort_unstable_by_key(|&(id, _, _)| id.0.unzip().0);
|
||||
}
|
||||
|
||||
/// Returns a list of all buffers tracked. May contain duplicates.
|
||||
pub fn used(&self) -> impl Iterator<Item = Valid<BufferId>> + '_ {
|
||||
self.buffers.iter().map(|&(id, _, _)| id)
|
||||
}
|
||||
|
||||
/// Adds the given resource with the given state.
|
||||
pub fn add_single<'a>(
|
||||
&mut self,
|
||||
storage: &'a hub::Storage<Buffer<A>, BufferId>,
|
||||
id: BufferId,
|
||||
state: BufferUses,
|
||||
) -> Option<&'a Buffer<A>> {
|
||||
let buffer = storage.get(id).ok()?;
|
||||
|
||||
self.buffers
|
||||
.push((Valid(id), buffer.life_guard.add_ref(), state));
|
||||
|
||||
Some(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores all buffer state within a single usage scope.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct BufferUsageScope<A: hub::HalApi> {
|
||||
state: Vec<BufferUses>,
|
||||
|
||||
metadata: ResourceMetadata<A>,
|
||||
}
|
||||
|
||||
impl<A: hub::HalApi> BufferUsageScope<A> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: Vec::new(),
|
||||
|
||||
metadata: ResourceMetadata::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_assert_in_bounds(&self, index: usize) {
|
||||
debug_assert!(index < self.state.len());
|
||||
self.metadata.debug_assert_in_bounds(index);
|
||||
}
|
||||
|
||||
/// Sets the size of all the vectors inside the tracker.
|
||||
///
|
||||
/// Must be called with the highest possible Buffer ID before
|
||||
/// all unsafe functions are called.
|
||||
pub fn set_size(&mut self, size: usize) {
|
||||
self.state.resize(size, BufferUses::empty());
|
||||
self.metadata.set_size(size);
|
||||
}
|
||||
|
||||
/// Extend the vectors to let the given index be valid.
|
||||
fn allow_index(&mut self, index: usize) {
|
||||
if index >= self.state.len() {
|
||||
self.set_size(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of all buffers tracked.
|
||||
pub fn used(&self) -> impl Iterator<Item = Valid<BufferId>> + '_ {
|
||||
self.metadata.used()
|
||||
}
|
||||
|
||||
/// Merge the list of buffer states in the given bind group into this usage scope.
|
||||
///
|
||||
/// If any of the resulting states is invalid, stops the merge and returns a usage
|
||||
/// conflict with the details of the invalid state.
|
||||
///
|
||||
/// Because bind groups do not check if the union of all their states is valid,
|
||||
/// this method is allowed to return Err on the first bind group bound.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// [`Self::set_size`] must be called with the maximum possible Buffer ID before this
|
||||
/// method is called.
|
||||
pub unsafe fn merge_bind_group(
|
||||
&mut self,
|
||||
bind_group: &BufferBindGroupState<A>,
|
||||
) -> Result<(), UsageConflict> {
|
||||
for &(id, ref ref_count, state) in &bind_group.buffers {
|
||||
let (index32, epoch, _) = id.0.unzip();
|
||||
let index = index32 as usize;
|
||||
|
||||
insert_or_merge(
|
||||
None,
|
||||
None,
|
||||
&mut self.state,
|
||||
&mut self.metadata,
|
||||
index32,
|
||||
index,
|
||||
BufferStateProvider::Direct { state },
|
||||
ResourceMetadataProvider::Direct {
|
||||
epoch,
|
||||
ref_count: Cow::Borrowed(ref_count),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn merge(
|
||||
&mut self,
|
||||
id: Valid<Self::Id>,
|
||||
other: &Self,
|
||||
output: Option<&mut Vec<PendingTransition<Self>>>,
|
||||
) -> Result<(), PendingTransition<Self>> {
|
||||
let old = self.last;
|
||||
let new = other.port();
|
||||
if old == new && BufferUses::ORDERED.contains(new) {
|
||||
if output.is_some() && self.first.is_none() {
|
||||
*self = Unit {
|
||||
first: Some(old),
|
||||
last: other.last,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
let pending = PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: old..new,
|
||||
};
|
||||
*self = match output {
|
||||
None => {
|
||||
assert_eq!(
|
||||
self.first, None,
|
||||
"extending a state that is already a transition"
|
||||
);
|
||||
Unit::new(pending.collapse()?)
|
||||
}
|
||||
Some(transitions) => {
|
||||
transitions.push(pending);
|
||||
Unit {
|
||||
first: self.first.or(Some(old)),
|
||||
last: other.last,
|
||||
}
|
||||
}
|
||||
/// Merge the list of buffer states in the given usage scope into this UsageScope.
|
||||
///
|
||||
/// If any of the resulting states is invalid, stops the merge and returns a usage
|
||||
/// conflict with the details of the invalid state.
|
||||
///
|
||||
/// If the given tracker uses IDs higher than the length of internal vectors,
|
||||
/// the vectors will be extended. A call to set_size is not needed.
|
||||
pub fn merge_usage_scope(&mut self, scope: &Self) -> Result<(), UsageConflict> {
|
||||
let incoming_size = scope.state.len();
|
||||
if incoming_size > self.state.len() {
|
||||
self.set_size(incoming_size);
|
||||
}
|
||||
|
||||
for index in iterate_bitvec_indices(&scope.metadata.owned) {
|
||||
self.debug_assert_in_bounds(index);
|
||||
scope.debug_assert_in_bounds(index);
|
||||
|
||||
unsafe {
|
||||
insert_or_merge(
|
||||
None,
|
||||
None,
|
||||
&mut self.state,
|
||||
&mut self.metadata,
|
||||
index as u32,
|
||||
index,
|
||||
BufferStateProvider::Indirect {
|
||||
state: &scope.state,
|
||||
},
|
||||
ResourceMetadataProvider::Indirect {
|
||||
metadata: &scope.metadata,
|
||||
},
|
||||
)?;
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn optimize(&mut self) {}
|
||||
}
|
||||
/// Merge a single state into the UsageScope.
|
||||
///
|
||||
/// If the resulting state is invalid, returns a usage
|
||||
/// conflict with the details of the invalid state.
|
||||
///
|
||||
/// If the ID is higher than the length of internal vectors,
|
||||
/// the vectors will be extended. A call to set_size is not needed.
|
||||
pub fn merge_single<'a>(
|
||||
&mut self,
|
||||
storage: &'a hub::Storage<Buffer<A>, BufferId>,
|
||||
id: BufferId,
|
||||
new_state: BufferUses,
|
||||
) -> Result<&'a Buffer<A>, UsageConflict> {
|
||||
let buffer = storage
|
||||
.get(id)
|
||||
.map_err(|_| UsageConflict::BufferInvalid { id })?;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::id::Id;
|
||||
let (index32, epoch, _) = id.unzip();
|
||||
let index = index32 as usize;
|
||||
|
||||
#[test]
|
||||
fn change_extend() {
|
||||
let mut bs = Unit {
|
||||
first: None,
|
||||
last: BufferUses::INDEX,
|
||||
};
|
||||
let id = Id::dummy();
|
||||
assert_eq!(
|
||||
bs.change(id, (), BufferUses::STORAGE_WRITE, None),
|
||||
Err(PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: BufferUses::INDEX..BufferUses::STORAGE_WRITE,
|
||||
}),
|
||||
);
|
||||
bs.change(id, (), BufferUses::VERTEX, None).unwrap();
|
||||
bs.change(id, (), BufferUses::INDEX, None).unwrap();
|
||||
assert_eq!(bs, Unit::new(BufferUses::VERTEX | BufferUses::INDEX));
|
||||
}
|
||||
self.allow_index(index);
|
||||
|
||||
#[test]
|
||||
fn change_replace() {
|
||||
let mut bs = Unit {
|
||||
first: None,
|
||||
last: BufferUses::STORAGE_WRITE,
|
||||
};
|
||||
let id = Id::dummy();
|
||||
let mut list = Vec::new();
|
||||
bs.change(id, (), BufferUses::VERTEX, Some(&mut list))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
&list,
|
||||
&[PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: BufferUses::STORAGE_WRITE..BufferUses::VERTEX,
|
||||
}],
|
||||
);
|
||||
assert_eq!(
|
||||
bs,
|
||||
Unit {
|
||||
first: Some(BufferUses::STORAGE_WRITE),
|
||||
last: BufferUses::VERTEX,
|
||||
}
|
||||
);
|
||||
self.debug_assert_in_bounds(index);
|
||||
|
||||
list.clear();
|
||||
bs.change(id, (), BufferUses::STORAGE_WRITE, Some(&mut list))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
&list,
|
||||
&[PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: BufferUses::VERTEX..BufferUses::STORAGE_WRITE,
|
||||
}],
|
||||
);
|
||||
assert_eq!(
|
||||
bs,
|
||||
Unit {
|
||||
first: Some(BufferUses::STORAGE_WRITE),
|
||||
last: BufferUses::STORAGE_WRITE,
|
||||
}
|
||||
);
|
||||
}
|
||||
unsafe {
|
||||
insert_or_merge(
|
||||
Some(&buffer.life_guard),
|
||||
None,
|
||||
&mut self.state,
|
||||
&mut self.metadata,
|
||||
index32,
|
||||
index,
|
||||
BufferStateProvider::Direct { state: new_state },
|
||||
ResourceMetadataProvider::Resource { epoch },
|
||||
)?;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_replace() {
|
||||
let mut bs = Unit {
|
||||
first: None,
|
||||
last: BufferUses::empty(),
|
||||
};
|
||||
let other_smooth = Unit {
|
||||
first: Some(BufferUses::empty()),
|
||||
last: BufferUses::COPY_DST,
|
||||
};
|
||||
let id = Id::dummy();
|
||||
let mut list = Vec::new();
|
||||
bs.merge(id, &other_smooth, Some(&mut list)).unwrap();
|
||||
assert!(list.is_empty());
|
||||
assert_eq!(
|
||||
bs,
|
||||
Unit {
|
||||
first: Some(BufferUses::empty()),
|
||||
last: BufferUses::COPY_DST,
|
||||
}
|
||||
);
|
||||
|
||||
let other_rough = Unit {
|
||||
first: Some(BufferUses::empty()),
|
||||
last: BufferUses::UNIFORM,
|
||||
};
|
||||
bs.merge(id, &other_rough, Some(&mut list)).unwrap();
|
||||
assert_eq!(
|
||||
&list,
|
||||
&[PendingTransition {
|
||||
id,
|
||||
selector: (),
|
||||
usage: BufferUses::COPY_DST..BufferUses::empty(),
|
||||
}],
|
||||
);
|
||||
assert_eq!(
|
||||
bs,
|
||||
Unit {
|
||||
first: Some(BufferUses::empty()),
|
||||
last: BufferUses::UNIFORM,
|
||||
}
|
||||
);
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores all buffer state within a command buffer or device.
|
||||
pub(crate) struct BufferTracker<A: hub::HalApi> {
|
||||
start: Vec<BufferUses>,
|
||||
end: Vec<BufferUses>,
|
||||
|
||||
metadata: ResourceMetadata<A>,
|
||||
|
||||
temp: Vec<PendingTransition<BufferUses>>,
|
||||
}
|
||||
impl<A: hub::HalApi> BufferTracker<A> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
start: Vec::new(),
|
||||
end: Vec::new(),
|
||||
|
||||
metadata: ResourceMetadata::new(),
|
||||
|
||||
temp: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_assert_in_bounds(&self, index: usize) {
|
||||
debug_assert!(index < self.start.len());
|
||||
debug_assert!(index < self.end.len());
|
||||
self.metadata.debug_assert_in_bounds(index);
|
||||
}
|
||||
|
||||
/// Sets the size of all the vectors inside the tracker.
|
||||
///
|
||||
/// Must be called with the highest possible Buffer ID before
|
||||
/// all unsafe functions are called.
|
||||
pub fn set_size(&mut self, size: usize) {
|
||||
self.start.resize(size, BufferUses::empty());
|
||||
self.end.resize(size, BufferUses::empty());
|
||||
|
||||
self.metadata.set_size(size);
|
||||
}
|
||||
|
||||
/// Extend the vectors to let the given index be valid.
|
||||
fn allow_index(&mut self, index: usize) {
|
||||
if index >= self.start.len() {
|
||||
self.set_size(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of all buffers tracked.
|
||||
pub fn used(&self) -> impl Iterator<Item = Valid<BufferId>> + '_ {
|
||||
self.metadata.used()
|
||||
}
|
||||
|
||||
/// Drains all currently pending transitions.
|
||||
pub fn drain(&mut self) -> Drain<'_, PendingTransition<BufferUses>> {
|
||||
self.temp.drain(..)
|
||||
}
|
||||
|
||||
/// Inserts a single buffer and its state into the resource tracker.
|
||||
///
|
||||
/// If the resource already exists in the tracker, this will panic.
|
||||
///
|
||||
/// If the ID is higher than the length of internal vectors,
|
||||
/// the vectors will be extended. A call to set_size is not needed.
|
||||
pub fn insert_single(&mut self, id: Valid<BufferId>, ref_count: RefCount, state: BufferUses) {
|
||||
let (index32, epoch, _) = id.0.unzip();
|
||||
let index = index32 as usize;
|
||||
|
||||
self.allow_index(index);
|
||||
|
||||
self.debug_assert_in_bounds(index);
|
||||
|
||||
unsafe {
|
||||
let currently_owned = self.metadata.owned.get(index).unwrap_unchecked();
|
||||
|
||||
if currently_owned {
|
||||
panic!("Tried to insert buffer already tracked");
|
||||
}
|
||||
|
||||
insert(
|
||||
None,
|
||||
Some(&mut self.start),
|
||||
&mut self.end,
|
||||
&mut self.metadata,
|
||||
index,
|
||||
BufferStateProvider::Direct { state },
|
||||
None,
|
||||
ResourceMetadataProvider::Direct {
|
||||
epoch,
|
||||
ref_count: Cow::Owned(ref_count),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the state of a single buffer.
|
||||
///
|
||||
/// If a transition is needed to get the buffer into the given state, that transition
|
||||
/// is returned. No more than one transition is needed.
|
||||
///
|
||||
/// If the ID is higher than the length of internal vectors,
|
||||
/// the vectors will be extended. A call to set_size is not needed.
|
||||
pub fn set_single<'a>(
|
||||
&mut self,
|
||||
storage: &'a hub::Storage<Buffer<A>, BufferId>,
|
||||
id: BufferId,
|
||||
state: BufferUses,
|
||||
) -> Option<(&'a Buffer<A>, Option<PendingTransition<BufferUses>>)> {
|
||||
let value = storage.get(id).ok()?;
|
||||
|
||||
let (index32, epoch, _) = id.unzip();
|
||||
let index = index32 as usize;
|
||||
|
||||
self.allow_index(index);
|
||||
|
||||
self.debug_assert_in_bounds(index);
|
||||
|
||||
unsafe {
|
||||
insert_or_barrier_update(
|
||||
Some(&value.life_guard),
|
||||
Some(&mut self.start),
|
||||
&mut self.end,
|
||||
&mut self.metadata,
|
||||
index32,
|
||||
index,
|
||||
BufferStateProvider::Direct { state },
|
||||
None,
|
||||
ResourceMetadataProvider::Resource { epoch },
|
||||
&mut self.temp,
|
||||
)
|
||||
};
|
||||
|
||||
debug_assert!(self.temp.len() <= 1);
|
||||
|
||||
Some((value, self.temp.pop()))
|
||||
}
|
||||
|
||||
/// Sets the given state for all buffers in the given tracker.
|
||||
///
|
||||
/// If a transition is needed to get the buffers into the needed state,
|
||||
/// those transitions are stored within the tracker. A subsequent
|
||||
/// call to [`Self::drain`] is needed to get those transitions.
|
||||
///
|
||||
/// If the ID is higher than the length of internal vectors,
|
||||
/// the vectors will be extended. A call to set_size is not needed.
|
||||
pub fn set_from_tracker(&mut self, tracker: &Self) {
|
||||
let incoming_size = tracker.start.len();
|
||||
if incoming_size > self.start.len() {
|
||||
self.set_size(incoming_size);
|
||||
}
|
||||
|
||||
for index in iterate_bitvec_indices(&tracker.metadata.owned) {
|
||||
self.debug_assert_in_bounds(index);
|
||||
tracker.debug_assert_in_bounds(index);
|
||||
unsafe {
|
||||
insert_or_barrier_update(
|
||||
None,
|
||||
Some(&mut self.start),
|
||||
&mut self.end,
|
||||
&mut self.metadata,
|
||||
index as u32,
|
||||
index,
|
||||
BufferStateProvider::Indirect {
|
||||
state: &tracker.start,
|
||||
},
|
||||
Some(BufferStateProvider::Indirect {
|
||||
state: &tracker.end,
|
||||
}),
|
||||
ResourceMetadataProvider::Indirect {
|
||||
metadata: &tracker.metadata,
|
||||
},
|
||||
&mut self.temp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the given state for all buffers in the given UsageScope.
|
||||
///
|
||||
/// If a transition is needed to get the buffers into the needed state,
|
||||
/// those transitions are stored within the tracker. A subsequent
|
||||
/// call to [`Self::drain`] is needed to get those transitions.
|
||||
///
|
||||
/// If the ID is higher than the length of internal vectors,
|
||||
/// the vectors will be extended. A call to set_size is not needed.
|
||||
pub fn set_from_usage_scope(&mut self, scope: &BufferUsageScope<A>) {
|
||||
let incoming_size = scope.state.len();
|
||||
if incoming_size > self.start.len() {
|
||||
self.set_size(incoming_size);
|
||||
}
|
||||
|
||||
for index in iterate_bitvec_indices(&scope.metadata.owned) {
|
||||
self.debug_assert_in_bounds(index);
|
||||
scope.debug_assert_in_bounds(index);
|
||||
unsafe {
|
||||
insert_or_barrier_update(
|
||||
None,
|
||||
Some(&mut self.start),
|
||||
&mut self.end,
|
||||
&mut self.metadata,
|
||||
index as u32,
|
||||
index,
|
||||
BufferStateProvider::Indirect {
|
||||
state: &scope.state,
|
||||
},
|
||||
None,
|
||||
ResourceMetadataProvider::Indirect {
|
||||
metadata: &scope.metadata,
|
||||
},
|
||||
&mut self.temp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates through all buffers in the given bind group and adopts
|
||||
/// the state given for those buffers in the UsageScope. It also
|
||||
/// removes all touched buffers from the usage scope.
|
||||
///
|
||||
/// If a transition is needed to get the buffers into the needed state,
|
||||
/// those transitions are stored within the tracker. A subsequent
|
||||
/// call to [`Self::drain`] is needed to get those transitions.
|
||||
///
|
||||
/// This is a really funky method used by Compute Passes to generate
|
||||
/// barriers after a call to dispatch without needing to iterate
|
||||
/// over all elements in the usage scope. We use each the
|
||||
/// bind group as a source of which IDs to look at. The bind groups
|
||||
/// must have first been added to the usage scope.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// [`Self::set_size`] must be called with the maximum possible Buffer ID before this
|
||||
/// method is called.
|
||||
pub unsafe fn set_and_remove_from_usage_scope_sparse(
|
||||
&mut self,
|
||||
scope: &mut BufferUsageScope<A>,
|
||||
bind_group_state: &BufferBindGroupState<A>,
|
||||
) {
|
||||
let incoming_size = scope.state.len();
|
||||
if incoming_size > self.start.len() {
|
||||
self.set_size(incoming_size);
|
||||
}
|
||||
|
||||
for &(id, ref ref_count, _) in bind_group_state.buffers.iter() {
|
||||
let (index32, epoch, _) = id.0.unzip();
|
||||
let index = index32 as usize;
|
||||
|
||||
scope.debug_assert_in_bounds(index);
|
||||
|
||||
if !scope.metadata.owned.get(index).unwrap_unchecked() {
|
||||
continue;
|
||||
}
|
||||
insert_or_barrier_update(
|
||||
None,
|
||||
Some(&mut self.start),
|
||||
&mut self.end,
|
||||
&mut self.metadata,
|
||||
index as u32,
|
||||
index,
|
||||
BufferStateProvider::Indirect {
|
||||
state: &scope.state,
|
||||
},
|
||||
None,
|
||||
ResourceMetadataProvider::Direct {
|
||||
epoch,
|
||||
ref_count: Cow::Borrowed(ref_count),
|
||||
},
|
||||
&mut self.temp,
|
||||
);
|
||||
|
||||
scope.metadata.reset(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the given resource from the tracker iff we have the last reference to the
|
||||
/// resource and the epoch matches.
|
||||
///
|
||||
/// Returns true if the resource was removed.
|
||||
///
|
||||
/// If the ID is higher than the length of internal vectors,
|
||||
/// false will be returned.
|
||||
pub fn remove_abandoned(&mut self, id: Valid<BufferId>) -> bool {
|
||||
let (index32, epoch, _) = id.0.unzip();
|
||||
let index = index32 as usize;
|
||||
|
||||
if index > self.metadata.owned.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.debug_assert_in_bounds(index);
|
||||
|
||||
unsafe {
|
||||
if self.metadata.owned.get(index).unwrap_unchecked() {
|
||||
let existing_epoch = self.metadata.epochs.get_unchecked_mut(index);
|
||||
let existing_ref_count = self.metadata.ref_counts.get_unchecked_mut(index);
|
||||
|
||||
if *existing_epoch == epoch
|
||||
&& existing_ref_count.as_mut().unwrap_unchecked().load() == 1
|
||||
{
|
||||
self.metadata.reset(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Source of Buffer State.
|
||||
#[derive(Debug, Clone)]
|
||||
enum BufferStateProvider<'a> {
|
||||
/// Get a state that was provided directly.
|
||||
Direct { state: BufferUses },
|
||||
/// Get a state from an an array of states.
|
||||
Indirect { state: &'a [BufferUses] },
|
||||
}
|
||||
impl BufferStateProvider<'_> {
|
||||
/// Gets the state from the provider, given a resource ID index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Index must be in bounds for the indirect source iff this is in the indirect state.
|
||||
#[inline(always)]
|
||||
unsafe fn get_state(&self, index: usize) -> BufferUses {
|
||||
match *self {
|
||||
BufferStateProvider::Direct { state } => state,
|
||||
BufferStateProvider::Indirect { state } => {
|
||||
debug_assert!(index < state.len());
|
||||
*state.get_unchecked(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Does an insertion operation if the index isn't tracked
|
||||
/// in the current metadata, otherwise merges the given state
|
||||
/// with the current state. If the merging would cause
|
||||
/// a conflict, returns that usage conflict.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Indexes must be valid indexes into all arrays passed in
|
||||
/// to this function, either directly or via metadata or provider structs.
|
||||
#[inline(always)]
|
||||
unsafe fn insert_or_merge<A: hub::HalApi>(
|
||||
life_guard: Option<&LifeGuard>,
|
||||
start_states: Option<&mut [BufferUses]>,
|
||||
current_states: &mut [BufferUses],
|
||||
resource_metadata: &mut ResourceMetadata<A>,
|
||||
index32: u32,
|
||||
index: usize,
|
||||
state_provider: BufferStateProvider<'_>,
|
||||
metadata_provider: ResourceMetadataProvider<'_, A>,
|
||||
) -> Result<(), UsageConflict> {
|
||||
let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked();
|
||||
|
||||
if !currently_owned {
|
||||
insert(
|
||||
life_guard,
|
||||
start_states,
|
||||
current_states,
|
||||
resource_metadata,
|
||||
index,
|
||||
state_provider,
|
||||
None,
|
||||
metadata_provider,
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
merge(
|
||||
current_states,
|
||||
index32,
|
||||
index,
|
||||
state_provider,
|
||||
metadata_provider,
|
||||
)
|
||||
}
|
||||
|
||||
/// If the resource isn't tracked
|
||||
/// - Inserts the given resource.
|
||||
/// - Uses the `start_state_provider` to populate `start_states`
|
||||
/// - Uses either `end_state_provider` or `start_state_provider`
|
||||
/// to populate `current_states`.
|
||||
/// If the resource is tracked
|
||||
/// - Inserts barriers from the state in `current_states`
|
||||
/// to the state provided by `start_state_provider`.
|
||||
/// - Updates the `current_states` with either the state from
|
||||
/// `end_state_provider` or `start_state_provider`.
|
||||
///
|
||||
/// Any barriers are added to the barrier vector.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Indexes must be valid indexes into all arrays passed in
|
||||
/// to this function, either directly or via metadata or provider structs.
|
||||
#[inline(always)]
|
||||
unsafe fn insert_or_barrier_update<A: hub::HalApi>(
|
||||
life_guard: Option<&LifeGuard>,
|
||||
start_states: Option<&mut [BufferUses]>,
|
||||
current_states: &mut [BufferUses],
|
||||
resource_metadata: &mut ResourceMetadata<A>,
|
||||
index32: u32,
|
||||
index: usize,
|
||||
start_state_provider: BufferStateProvider<'_>,
|
||||
end_state_provider: Option<BufferStateProvider<'_>>,
|
||||
metadata_provider: ResourceMetadataProvider<'_, A>,
|
||||
barriers: &mut Vec<PendingTransition<BufferUses>>,
|
||||
) {
|
||||
let currently_owned = resource_metadata.owned.get(index).unwrap_unchecked();
|
||||
|
||||
if !currently_owned {
|
||||
insert(
|
||||
life_guard,
|
||||
start_states,
|
||||
current_states,
|
||||
resource_metadata,
|
||||
index,
|
||||
start_state_provider,
|
||||
end_state_provider,
|
||||
metadata_provider,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let update_state_provider = end_state_provider.unwrap_or_else(|| start_state_provider.clone());
|
||||
barrier(
|
||||
current_states,
|
||||
index32,
|
||||
index,
|
||||
start_state_provider,
|
||||
barriers,
|
||||
);
|
||||
|
||||
update(current_states, index, update_state_provider);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn insert<A: hub::HalApi>(
|
||||
life_guard: Option<&LifeGuard>,
|
||||
start_states: Option<&mut [BufferUses]>,
|
||||
current_states: &mut [BufferUses],
|
||||
resource_metadata: &mut ResourceMetadata<A>,
|
||||
index: usize,
|
||||
start_state_provider: BufferStateProvider<'_>,
|
||||
end_state_provider: Option<BufferStateProvider<'_>>,
|
||||
metadata_provider: ResourceMetadataProvider<'_, A>,
|
||||
) {
|
||||
let new_start_state = start_state_provider.get_state(index);
|
||||
let new_end_state = end_state_provider.map_or(new_start_state, |p| p.get_state(index));
|
||||
|
||||
// This should only ever happen with a wgpu bug, but let's just double
|
||||
// check that resource states don't have any conflicts.
|
||||
debug_assert_eq!(invalid_resource_state(new_start_state), false);
|
||||
debug_assert_eq!(invalid_resource_state(new_end_state), false);
|
||||
|
||||
log::trace!("\tbuf {index}: insert {new_start_state:?}..{new_end_state:?}");
|
||||
|
||||
if let Some(&mut ref mut start_state) = start_states {
|
||||
*start_state.get_unchecked_mut(index) = new_start_state;
|
||||
}
|
||||
*current_states.get_unchecked_mut(index) = new_end_state;
|
||||
|
||||
let (epoch, ref_count) = metadata_provider.get_own(life_guard, index);
|
||||
|
||||
resource_metadata.owned.set(index, true);
|
||||
*resource_metadata.epochs.get_unchecked_mut(index) = epoch;
|
||||
*resource_metadata.ref_counts.get_unchecked_mut(index) = Some(ref_count);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn merge<A: hub::HalApi>(
|
||||
current_states: &mut [BufferUses],
|
||||
index32: u32,
|
||||
index: usize,
|
||||
state_provider: BufferStateProvider<'_>,
|
||||
metadata_provider: ResourceMetadataProvider<'_, A>,
|
||||
) -> Result<(), UsageConflict> {
|
||||
let current_state = current_states.get_unchecked_mut(index);
|
||||
let new_state = state_provider.get_state(index);
|
||||
|
||||
let merged_state = *current_state | new_state;
|
||||
|
||||
if invalid_resource_state(merged_state) {
|
||||
return Err(UsageConflict::from_buffer(
|
||||
BufferId::zip(index32, metadata_provider.get_epoch(index), A::VARIANT),
|
||||
*current_state,
|
||||
new_state,
|
||||
));
|
||||
}
|
||||
|
||||
log::trace!("\tbuf {index32}: merge {current_state:?} + {new_state:?}");
|
||||
|
||||
*current_state = merged_state;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn barrier(
|
||||
current_states: &mut [BufferUses],
|
||||
index32: u32,
|
||||
index: usize,
|
||||
state_provider: BufferStateProvider<'_>,
|
||||
barriers: &mut Vec<PendingTransition<BufferUses>>,
|
||||
) {
|
||||
let current_state = *current_states.get_unchecked(index);
|
||||
let new_state = state_provider.get_state(index);
|
||||
|
||||
if skip_barrier(current_state, new_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
barriers.push(PendingTransition {
|
||||
id: index32,
|
||||
selector: (),
|
||||
usage: current_state..new_state,
|
||||
});
|
||||
|
||||
log::trace!("\tbuf {index32}: transition {current_state:?} -> {new_state:?}");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn update(
|
||||
current_states: &mut [BufferUses],
|
||||
index: usize,
|
||||
state_provider: BufferStateProvider<'_>,
|
||||
) {
|
||||
let current_state = current_states.get_unchecked_mut(index);
|
||||
let new_state = state_provider.get_state(index);
|
||||
|
||||
*current_state = new_state;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,25 +2,19 @@
|
||||
//TODO: consider getting rid of it.
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use std::{cmp::Ordering, fmt::Debug, iter, ops::Range, slice::Iter};
|
||||
use std::{fmt::Debug, iter, ops::Range};
|
||||
|
||||
/// Structure that keeps track of a I -> T mapping,
|
||||
/// optimized for a case where keys of the same values
|
||||
/// are often grouped together linearly.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RangedStates<I, T> {
|
||||
pub(crate) struct RangedStates<I, T> {
|
||||
/// List of ranges, each associated with a singe value.
|
||||
/// Ranges of keys have to be non-intersecting and ordered.
|
||||
ranges: SmallVec<[(Range<I>, T); 1]>,
|
||||
}
|
||||
|
||||
impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
ranges: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Copy + Ord, T: Copy + PartialEq> RangedStates<I, T> {
|
||||
pub fn from_range(range: Range<I>, value: T) -> Self {
|
||||
Self {
|
||||
ranges: iter::once((range, value)).collect(),
|
||||
@ -35,20 +29,12 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all the ranges.
|
||||
pub fn clear(&mut self) {
|
||||
self.ranges.clear();
|
||||
pub fn iter(&self) -> impl Iterator<Item = &(Range<I>, T)> + Clone {
|
||||
self.ranges.iter()
|
||||
}
|
||||
|
||||
/// Append a range.
|
||||
///
|
||||
/// Assumes that the object is being constructed from a set of
|
||||
/// ranges, and they are given in the ascending order of their keys.
|
||||
pub fn append(&mut self, index: Range<I>, value: T) {
|
||||
if let Some(last) = self.ranges.last() {
|
||||
debug_assert!(last.0.end <= index.start);
|
||||
}
|
||||
self.ranges.push((index, value));
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (Range<I>, T)> {
|
||||
self.ranges.iter_mut()
|
||||
}
|
||||
|
||||
/// Check that all the ranges are non-intersecting and ordered.
|
||||
@ -64,7 +50,6 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
|
||||
}
|
||||
|
||||
/// Merge the neighboring ranges together, where possible.
|
||||
#[allow(clippy::suspicious_operation_groupings)]
|
||||
pub fn coalesce(&mut self) {
|
||||
let mut num_removed = 0;
|
||||
let mut iter = self.ranges.iter_mut();
|
||||
@ -86,25 +71,18 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if all intersecting ranges have the same value, which is returned.
|
||||
///
|
||||
/// Returns `None` if no intersections are detected.
|
||||
/// Returns `Some(Err)` if the intersected values are inconsistent.
|
||||
pub fn query<U: PartialEq>(
|
||||
&self,
|
||||
index: &Range<I>,
|
||||
fun: impl Fn(&T) -> U,
|
||||
) -> Option<Result<U, ()>> {
|
||||
let mut result = None;
|
||||
for &(ref range, ref value) in self.ranges.iter() {
|
||||
if range.end > index.start && range.start < index.end {
|
||||
let old = result.replace(fun(value));
|
||||
if old.is_some() && old != result {
|
||||
return Some(Err(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
result.map(Ok)
|
||||
pub fn iter_filter<'a>(
|
||||
&'a self,
|
||||
range: &'a Range<I>,
|
||||
) -> impl Iterator<Item = (Range<I>, &T)> + 'a {
|
||||
self.ranges
|
||||
.iter()
|
||||
.filter(move |&&(ref inner, ..)| inner.end > range.start && inner.start < range.end)
|
||||
.map(move |&(ref inner, ref v)| {
|
||||
let new_range = inner.start.max(range.start)..inner.end.min(range.end);
|
||||
|
||||
(new_range, v)
|
||||
})
|
||||
}
|
||||
|
||||
/// Split the storage ranges in such a way that there is a linear subset of
|
||||
@ -176,112 +154,12 @@ impl<I: Copy + PartialOrd, T: Copy + PartialEq> RangedStates<I, T> {
|
||||
clone.check_sanity();
|
||||
result
|
||||
}
|
||||
|
||||
/// Produce an iterator that merges two instances together.
|
||||
///
|
||||
/// Each range in the returned iterator is a subset of a range in either
|
||||
/// `self` or `other`, and the value returned as a `Range` from `self` to `other`.
|
||||
pub fn merge<'a>(&'a self, other: &'a Self, base: I) -> Merge<'a, I, T> {
|
||||
Merge {
|
||||
base,
|
||||
sa: self.ranges.iter().peekable(),
|
||||
sb: other.ranges.iter().peekable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A custom iterator that goes through two `RangedStates` and process a merge.
|
||||
#[derive(Debug)]
|
||||
pub struct Merge<'a, I, T> {
|
||||
base: I,
|
||||
sa: iter::Peekable<Iter<'a, (Range<I>, T)>>,
|
||||
sb: iter::Peekable<Iter<'a, (Range<I>, T)>>,
|
||||
}
|
||||
|
||||
impl<'a, I: Copy + Debug + Ord, T: Copy + Debug> Iterator for Merge<'a, I, T> {
|
||||
type Item = (Range<I>, Range<Option<T>>);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match (self.sa.peek(), self.sb.peek()) {
|
||||
// we have both streams
|
||||
(Some(&&(ref ra, va)), Some(&&(ref rb, vb))) => {
|
||||
let (range, usage) = if ra.start < self.base {
|
||||
// in the middle of the left stream
|
||||
let (end, end_value) = if self.base == rb.start {
|
||||
// right stream is starting
|
||||
debug_assert!(self.base < ra.end);
|
||||
(rb.end, Some(vb))
|
||||
} else {
|
||||
// right hasn't started yet
|
||||
debug_assert!(self.base < rb.start);
|
||||
(rb.start, None)
|
||||
};
|
||||
(self.base..ra.end.min(end), Some(va)..end_value)
|
||||
} else if rb.start < self.base {
|
||||
// in the middle of the right stream
|
||||
let (end, start_value) = if self.base == ra.start {
|
||||
// left stream is starting
|
||||
debug_assert!(self.base < rb.end);
|
||||
(ra.end, Some(va))
|
||||
} else {
|
||||
// left hasn't started yet
|
||||
debug_assert!(self.base < ra.start);
|
||||
(ra.start, None)
|
||||
};
|
||||
(self.base..rb.end.min(end), start_value..Some(vb))
|
||||
} else {
|
||||
// no active streams
|
||||
match ra.start.cmp(&rb.start) {
|
||||
// both are starting
|
||||
Ordering::Equal => (ra.start..ra.end.min(rb.end), Some(va)..Some(vb)),
|
||||
// only left is starting
|
||||
Ordering::Less => (ra.start..rb.start.min(ra.end), Some(va)..None),
|
||||
// only right is starting
|
||||
Ordering::Greater => (rb.start..ra.start.min(rb.end), None..Some(vb)),
|
||||
}
|
||||
};
|
||||
self.base = range.end;
|
||||
if ra.end == range.end {
|
||||
let _ = self.sa.next();
|
||||
}
|
||||
if rb.end == range.end {
|
||||
let _ = self.sb.next();
|
||||
}
|
||||
Some((range, usage))
|
||||
}
|
||||
// only right stream
|
||||
(None, Some(&&(ref rb, vb))) => {
|
||||
let range = self.base.max(rb.start)..rb.end;
|
||||
self.base = rb.end;
|
||||
let _ = self.sb.next();
|
||||
Some((range, None..Some(vb)))
|
||||
}
|
||||
// only left stream
|
||||
(Some(&&(ref ra, va)), None) => {
|
||||
let range = self.base.max(ra.start)..ra.end;
|
||||
self.base = ra.end;
|
||||
let _ = self.sa.next();
|
||||
Some((range, Some(va)..None))
|
||||
}
|
||||
// done
|
||||
(None, None) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
//TODO: randomized/fuzzy testing
|
||||
use super::RangedStates;
|
||||
use std::{fmt::Debug, ops::Range};
|
||||
|
||||
fn easy_merge<T: PartialEq + Copy + Debug>(
|
||||
ra: &[(Range<usize>, T)],
|
||||
rb: &[(Range<usize>, T)],
|
||||
) -> Vec<(Range<usize>, Range<Option<T>>)> {
|
||||
RangedStates::from_slice(ra)
|
||||
.merge(&RangedStates::from_slice(rb), 0)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sane_good() {
|
||||
@ -311,14 +189,6 @@ mod test {
|
||||
assert_eq!(rs.ranges.as_slice(), &[(1..5, 9), (5..7, 1), (8..9, 1),]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query() {
|
||||
let rs = RangedStates::from_slice(&[(1..4, 1u8), (5..7, 2)]);
|
||||
assert_eq!(rs.query(&(0..1), |v| *v), None);
|
||||
assert_eq!(rs.query(&(1..3), |v| *v), Some(Ok(1)));
|
||||
assert_eq!(rs.query(&(1..6), |v| *v), Some(Err(())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isolate() {
|
||||
let rs = RangedStates::from_slice(&[(1..4, 9u8), (4..5, 9), (5..7, 1), (8..9, 1)]);
|
||||
@ -333,104 +203,4 @@ mod test {
|
||||
&[(6..7, 1), (7..8, 0), (8..9, 1),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_same() {
|
||||
assert_eq!(
|
||||
&easy_merge(&[(1..4, 0u8),], &[(1..4, 2u8),],),
|
||||
&[(1..4, Some(0)..Some(2)),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_empty() {
|
||||
assert_eq!(
|
||||
&easy_merge(&[(1..2, 0u8),], &[],),
|
||||
&[(1..2, Some(0)..None),]
|
||||
);
|
||||
assert_eq!(
|
||||
&easy_merge(&[], &[(3..4, 1u8),],),
|
||||
&[(3..4, None..Some(1)),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_separate() {
|
||||
assert_eq!(
|
||||
&easy_merge(&[(1..2, 0u8), (5..6, 1u8),], &[(2..4, 2u8),],),
|
||||
&[
|
||||
(1..2, Some(0)..None),
|
||||
(2..4, None..Some(2)),
|
||||
(5..6, Some(1)..None),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_subset() {
|
||||
assert_eq!(
|
||||
&easy_merge(&[(1..6, 0u8),], &[(2..4, 2u8),],),
|
||||
&[
|
||||
(1..2, Some(0)..None),
|
||||
(2..4, Some(0)..Some(2)),
|
||||
(4..6, Some(0)..None),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
&easy_merge(&[(2..4, 0u8),], &[(1..4, 2u8),],),
|
||||
&[(1..2, None..Some(2)), (2..4, Some(0)..Some(2)),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_all() {
|
||||
assert_eq!(
|
||||
&easy_merge(&[(1..4, 0u8), (5..8, 1u8),], &[(2..6, 2u8), (7..9, 3u8),],),
|
||||
&[
|
||||
(1..2, Some(0)..None),
|
||||
(2..4, Some(0)..Some(2)),
|
||||
(4..5, None..Some(2)),
|
||||
(5..6, Some(1)..Some(2)),
|
||||
(6..7, Some(1)..None),
|
||||
(7..8, Some(1)..Some(3)),
|
||||
(8..9, None..Some(3)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_complex() {
|
||||
assert_eq!(
|
||||
&easy_merge(
|
||||
&[
|
||||
(0..8, 0u8),
|
||||
(8..9, 1),
|
||||
(9..16, 2),
|
||||
(16..17, 3),
|
||||
(17..118, 4),
|
||||
(118..119, 5),
|
||||
(119..124, 6),
|
||||
(124..125, 7),
|
||||
(125..512, 8),
|
||||
],
|
||||
&[(15..16, 10u8), (51..52, 11), (126..127, 12),],
|
||||
),
|
||||
&[
|
||||
(0..8, Some(0)..None),
|
||||
(8..9, Some(1)..None),
|
||||
(9..15, Some(2)..None),
|
||||
(15..16, Some(2)..Some(10)),
|
||||
(16..17, Some(3)..None),
|
||||
(17..51, Some(4)..None),
|
||||
(51..52, Some(4)..Some(11)),
|
||||
(52..118, Some(4)..None),
|
||||
(118..119, Some(5)..None),
|
||||
(119..124, Some(6)..None),
|
||||
(124..125, Some(7)..None),
|
||||
(125..126, Some(8)..None),
|
||||
(126..127, Some(8)..Some(12)),
|
||||
(127..512, Some(8)..None),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
209
wgpu-core/src/track/stateless.rs
Normal file
209
wgpu-core/src/track/stateless.rs
Normal 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
@ -685,7 +685,7 @@ impl<A: hal::Api> Example<A> {
|
||||
let target_barrier1 = hal::TextureBarrier {
|
||||
texture: surface_tex.borrow(),
|
||||
range: wgt::ImageSubresourceRange::default(),
|
||||
usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::empty(),
|
||||
usage: hal::TextureUses::COLOR_TARGET..hal::TextureUses::PRESENT,
|
||||
};
|
||||
unsafe {
|
||||
ctx.encoder.end_render_pass();
|
||||
|
@ -287,7 +287,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
StateAfter: s1,
|
||||
};
|
||||
self.temp.barriers.push(raw);
|
||||
} else if barrier.usage.start == crate::BufferUses::STORAGE_WRITE {
|
||||
} else if barrier.usage.start == crate::BufferUses::STORAGE_READ_WRITE {
|
||||
let mut raw = d3d12::D3D12_RESOURCE_BARRIER {
|
||||
Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
|
||||
Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
@ -382,7 +382,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if barrier.usage.start == crate::TextureUses::STORAGE_WRITE {
|
||||
} else if barrier.usage.start == crate::TextureUses::STORAGE_READ_WRITE {
|
||||
let mut raw = d3d12::D3D12_RESOURCE_BARRIER {
|
||||
Type: d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
|
||||
Flags: d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
|
@ -3,7 +3,7 @@ use winapi::um::{d3d12, d3dcommon};
|
||||
|
||||
pub fn map_buffer_usage_to_resource_flags(usage: crate::BufferUses) -> d3d12::D3D12_RESOURCE_FLAGS {
|
||||
let mut flags = 0;
|
||||
if usage.contains(crate::BufferUses::STORAGE_WRITE) {
|
||||
if usage.contains(crate::BufferUses::STORAGE_READ_WRITE) {
|
||||
flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
}
|
||||
flags
|
||||
@ -33,7 +33,7 @@ pub fn map_texture_usage_to_resource_flags(
|
||||
flags |= d3d12::D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
|
||||
}
|
||||
}
|
||||
if usage.contains(crate::TextureUses::STORAGE_WRITE) {
|
||||
if usage.contains(crate::TextureUses::STORAGE_READ_WRITE) {
|
||||
flags |= d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ pub fn map_buffer_usage_to_state(usage: crate::BufferUses) -> d3d12::D3D12_RESOU
|
||||
if usage.intersects(Bu::VERTEX | Bu::UNIFORM) {
|
||||
state |= d3d12::D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
|
||||
}
|
||||
if usage.intersects(Bu::STORAGE_WRITE) {
|
||||
if usage.intersects(Bu::STORAGE_READ_WRITE) {
|
||||
state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
} else if usage.intersects(Bu::STORAGE_READ) {
|
||||
state |= d3d12::D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
|
||||
@ -170,7 +170,7 @@ pub fn map_texture_usage_to_state(usage: crate::TextureUses) -> d3d12::D3D12_RES
|
||||
if usage.intersects(Tu::DEPTH_STENCIL_WRITE) {
|
||||
state |= d3d12::D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
}
|
||||
if usage.intersects(Tu::STORAGE_READ | Tu::STORAGE_WRITE) {
|
||||
if usage.intersects(Tu::STORAGE_READ | Tu::STORAGE_READ_WRITE) {
|
||||
state |= d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
}
|
||||
state
|
||||
|
@ -428,7 +428,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
|| !desc.usage.intersects(
|
||||
crate::TextureUses::RESOURCE
|
||||
| crate::TextureUses::STORAGE_READ
|
||||
| crate::TextureUses::STORAGE_WRITE,
|
||||
| crate::TextureUses::STORAGE_READ_WRITE,
|
||||
) {
|
||||
auxil::dxgi::conv::map_texture_format(desc.format)
|
||||
} else {
|
||||
@ -516,10 +516,9 @@ impl crate::Device<super::Api> for super::Device {
|
||||
} else {
|
||||
None
|
||||
},
|
||||
handle_uav: if desc
|
||||
.usage
|
||||
.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE)
|
||||
{
|
||||
handle_uav: if desc.usage.intersects(
|
||||
crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE,
|
||||
) {
|
||||
let raw_desc = view_desc.to_uav();
|
||||
let handle = self.srv_uav_pool.lock().alloc_handle();
|
||||
self.raw.CreateUnorderedAccessView(
|
||||
|
@ -230,7 +230,11 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
}
|
||||
for bar in barriers {
|
||||
// GLES only synchronizes storage -> anything explicitly
|
||||
if !bar.usage.start.contains(crate::BufferUses::STORAGE_WRITE) {
|
||||
if !bar
|
||||
.usage
|
||||
.start
|
||||
.contains(crate::BufferUses::STORAGE_READ_WRITE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
self.cmd_buffer
|
||||
@ -253,7 +257,11 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
let mut combined_usage = crate::TextureUses::empty();
|
||||
for bar in barriers {
|
||||
// GLES only synchronizes storage -> anything explicitly
|
||||
if !bar.usage.start.contains(crate::TextureUses::STORAGE_WRITE) {
|
||||
if !bar
|
||||
.usage
|
||||
.start
|
||||
.contains(crate::TextureUses::STORAGE_READ_WRITE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// unlike buffers, there is no need for a concrete texture
|
||||
|
@ -790,9 +790,9 @@ impl super::Queue {
|
||||
if usage.intersects(crate::BufferUses::MAP_READ | crate::BufferUses::MAP_WRITE) {
|
||||
flags |= glow::BUFFER_UPDATE_BARRIER_BIT;
|
||||
}
|
||||
if usage
|
||||
.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE)
|
||||
{
|
||||
if usage.intersects(
|
||||
crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE,
|
||||
) {
|
||||
flags |= glow::SHADER_STORAGE_BARRIER_BIT;
|
||||
}
|
||||
gl.memory_barrier(flags);
|
||||
@ -803,7 +803,7 @@ impl super::Queue {
|
||||
flags |= glow::TEXTURE_FETCH_BARRIER_BIT;
|
||||
}
|
||||
if usage.intersects(
|
||||
crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE,
|
||||
crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE,
|
||||
) {
|
||||
flags |= glow::SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
||||
}
|
||||
|
@ -628,53 +628,77 @@ bitflags!(
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Similar to `wgt::BufferUsages` but for internal use.
|
||||
pub struct BufferUses: u32 {
|
||||
pub struct BufferUses: u16 {
|
||||
/// The argument to a read-only mapping.
|
||||
const MAP_READ = 1 << 0;
|
||||
/// The argument to a write-only mapping.
|
||||
const MAP_WRITE = 1 << 1;
|
||||
/// The source of a hardware copy.
|
||||
const COPY_SRC = 1 << 2;
|
||||
/// The destination of a hardware copy.
|
||||
const COPY_DST = 1 << 3;
|
||||
/// The index buffer used for drawing.
|
||||
const INDEX = 1 << 4;
|
||||
/// A vertex buffer used for drawing.
|
||||
const VERTEX = 1 << 5;
|
||||
/// A uniform buffer bound in a bind group.
|
||||
const UNIFORM = 1 << 6;
|
||||
/// A read-only storage buffer used in a bind group.
|
||||
const STORAGE_READ = 1 << 7;
|
||||
const STORAGE_WRITE = 1 << 8;
|
||||
/// A read-write or write-only buffer used in a bind group.
|
||||
const STORAGE_READ_WRITE = 1 << 8;
|
||||
/// The indirect or count buffer in a indirect draw or dispatch.
|
||||
const INDIRECT = 1 << 9;
|
||||
/// The combination of usages that can be used together (read-only).
|
||||
/// The combination of states that a buffer may be in _at the same time_.
|
||||
const INCLUSIVE = Self::MAP_READ.bits | Self::COPY_SRC.bits |
|
||||
Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits |
|
||||
Self::STORAGE_READ.bits | Self::INDIRECT.bits;
|
||||
/// The combination of exclusive usages (write-only and read-write).
|
||||
/// These usages may still show up with others, but can't automatically be combined.
|
||||
const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_WRITE.bits;
|
||||
/// The combination of states that a buffer must exclusively be in.
|
||||
const EXCLUSIVE = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_READ_WRITE.bits;
|
||||
/// The combination of all usages that the are guaranteed to be be ordered by the hardware.
|
||||
/// If a usage is not ordered, then even if it doesn't change between draw calls, there
|
||||
/// still need to be pipeline barriers inserted for synchronization.
|
||||
/// If a usage is ordered, then if the buffer state doesn't change between draw calls, there
|
||||
/// are no barriers needed for synchronization.
|
||||
const ORDERED = Self::INCLUSIVE.bits | Self::MAP_WRITE.bits;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Similar to `wgt::TextureUsages` but for internal use.
|
||||
pub struct TextureUses: u32 {
|
||||
const COPY_SRC = 1 << 0;
|
||||
const COPY_DST = 1 << 1;
|
||||
const RESOURCE = 1 << 2;
|
||||
const COLOR_TARGET = 1 << 3;
|
||||
const DEPTH_STENCIL_READ = 1 << 4;
|
||||
const DEPTH_STENCIL_WRITE = 1 << 5;
|
||||
const STORAGE_READ = 1 << 6;
|
||||
const STORAGE_WRITE = 1 << 7;
|
||||
/// The combination of usages that can be used together (read-only).
|
||||
pub struct TextureUses: u16 {
|
||||
/// The texture is in unknown state.
|
||||
const UNINITIALIZED = 1 << 0;
|
||||
/// Ready to present image to the surface.
|
||||
const PRESENT = 1 << 1;
|
||||
/// The source of a hardware copy.
|
||||
const COPY_SRC = 1 << 2;
|
||||
/// The destination of a hardware copy.
|
||||
const COPY_DST = 1 << 3;
|
||||
/// Read-only sampled or fetched resource.
|
||||
const RESOURCE = 1 << 4;
|
||||
/// The color target of a renderpass.
|
||||
const COLOR_TARGET = 1 << 5;
|
||||
/// Read-only depth stencil usage.
|
||||
const DEPTH_STENCIL_READ = 1 << 6;
|
||||
/// Read-write depth stencil usage
|
||||
const DEPTH_STENCIL_WRITE = 1 << 7;
|
||||
/// Read-only storage buffer usage. Corresponds to a UAV in d3d, so is exclusive, despite being read only.
|
||||
const STORAGE_READ = 1 << 8;
|
||||
/// Read-write or write-only storage buffer usage.
|
||||
const STORAGE_READ_WRITE = 1 << 9;
|
||||
/// The combination of states that a texture may be in _at the same time_.
|
||||
const INCLUSIVE = Self::COPY_SRC.bits | Self::RESOURCE.bits | Self::DEPTH_STENCIL_READ.bits;
|
||||
/// The combination of exclusive usages (write-only and read-write).
|
||||
/// These usages may still show up with others, but can't automatically be combined.
|
||||
const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_WRITE.bits;
|
||||
/// The combination of states that a texture must exclusively be in.
|
||||
const EXCLUSIVE = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits | Self::STORAGE_READ_WRITE.bits | Self::PRESENT.bits;
|
||||
/// The combination of all usages that the are guaranteed to be be ordered by the hardware.
|
||||
/// If a usage is not ordered, then even if it doesn't change between draw calls, there
|
||||
/// still need to be pipeline barriers inserted for synchronization.
|
||||
/// If a usage is ordered, then if the texture state doesn't change between draw calls, there
|
||||
/// are no barriers needed for synchronization.
|
||||
const ORDERED = Self::INCLUSIVE.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_READ.bits;
|
||||
//TODO: remove this
|
||||
const UNINITIALIZED = 0xFFFF;
|
||||
|
||||
/// Flag used by the wgpu-core texture tracker to say a texture is in different states for every sub-resource
|
||||
const COMPLEX = 1 << 10;
|
||||
/// Flag used by the wgpu-core texture tracker to say that the tracker does not know the state of the sub-resource.
|
||||
/// This is different from UNINITIALIZED as that says the tracker does know, but the texture has not been initialized.
|
||||
const UNKNOWN = 1 << 11;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,13 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> mtl::MTLTextureUsage {
|
||||
);
|
||||
mtl_usage.set(
|
||||
mtl::MTLTextureUsage::ShaderRead,
|
||||
usage.intersects(Tu::RESOURCE | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ),
|
||||
usage.intersects(
|
||||
Tu::RESOURCE | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ | Tu::STORAGE_READ_WRITE,
|
||||
),
|
||||
);
|
||||
mtl_usage.set(
|
||||
mtl::MTLTextureUsage::ShaderWrite,
|
||||
usage.intersects(Tu::STORAGE_WRITE),
|
||||
usage.intersects(Tu::STORAGE_READ_WRITE),
|
||||
);
|
||||
|
||||
mtl_usage
|
||||
|
@ -200,7 +200,7 @@ pub fn derive_image_layout(
|
||||
vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
|
||||
}
|
||||
_ => {
|
||||
if usage.is_empty() {
|
||||
if usage == crate::TextureUses::PRESENT {
|
||||
vk::ImageLayout::PRESENT_SRC_KHR
|
||||
} else if is_color {
|
||||
vk::ImageLayout::GENERAL
|
||||
@ -230,7 +230,7 @@ pub fn map_texture_usage(usage: crate::TextureUses) -> vk::ImageUsageFlags {
|
||||
) {
|
||||
flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
|
||||
}
|
||||
if usage.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE) {
|
||||
if usage.intersects(crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE) {
|
||||
flags |= vk::ImageUsageFlags::STORAGE;
|
||||
}
|
||||
flags
|
||||
@ -276,12 +276,12 @@ pub fn map_texture_usage_to_barrier(
|
||||
stages |= shader_stages;
|
||||
access |= vk::AccessFlags::SHADER_READ;
|
||||
}
|
||||
if usage.contains(crate::TextureUses::STORAGE_WRITE) {
|
||||
if usage.contains(crate::TextureUses::STORAGE_READ_WRITE) {
|
||||
stages |= shader_stages;
|
||||
access |= vk::AccessFlags::SHADER_WRITE;
|
||||
access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE;
|
||||
}
|
||||
|
||||
if usage == crate::TextureUses::UNINITIALIZED || usage.is_empty() {
|
||||
if usage == crate::TextureUses::UNINITIALIZED || usage == crate::TextureUses::PRESENT {
|
||||
(
|
||||
vk::PipelineStageFlags::TOP_OF_PIPE,
|
||||
vk::AccessFlags::empty(),
|
||||
@ -309,7 +309,7 @@ pub fn map_vk_image_usage(usage: vk::ImageUsageFlags) -> crate::TextureUses {
|
||||
bits |= crate::TextureUses::DEPTH_STENCIL_READ | crate::TextureUses::DEPTH_STENCIL_WRITE;
|
||||
}
|
||||
if usage.contains(vk::ImageUsageFlags::STORAGE) {
|
||||
bits |= crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_WRITE;
|
||||
bits |= crate::TextureUses::STORAGE_READ | crate::TextureUses::STORAGE_READ_WRITE;
|
||||
}
|
||||
bits
|
||||
}
|
||||
@ -457,7 +457,7 @@ pub fn map_buffer_usage(usage: crate::BufferUses) -> vk::BufferUsageFlags {
|
||||
if usage.contains(crate::BufferUses::UNIFORM) {
|
||||
flags |= vk::BufferUsageFlags::UNIFORM_BUFFER;
|
||||
}
|
||||
if usage.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_WRITE) {
|
||||
if usage.intersects(crate::BufferUses::STORAGE_READ | crate::BufferUses::STORAGE_READ_WRITE) {
|
||||
flags |= vk::BufferUsageFlags::STORAGE_BUFFER;
|
||||
}
|
||||
if usage.contains(crate::BufferUses::INDEX) {
|
||||
@ -505,9 +505,9 @@ pub fn map_buffer_usage_to_barrier(
|
||||
stages |= shader_stages;
|
||||
access |= vk::AccessFlags::SHADER_READ;
|
||||
}
|
||||
if usage.intersects(crate::BufferUses::STORAGE_WRITE) {
|
||||
if usage.intersects(crate::BufferUses::STORAGE_READ_WRITE) {
|
||||
stages |= shader_stages;
|
||||
access |= vk::AccessFlags::SHADER_WRITE;
|
||||
access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE;
|
||||
}
|
||||
if usage.contains(crate::BufferUses::INDEX) {
|
||||
stages |= vk::PipelineStageFlags::VERTEX_INPUT;
|
||||
|
@ -1413,7 +1413,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
unsafe fn destroy_shader_module(&self, module: super::ShaderModule) {
|
||||
match module {
|
||||
super::ShaderModule::Raw(raw) => {
|
||||
let _ = self.shared.raw.destroy_shader_module(raw, None);
|
||||
self.shared.raw.destroy_shader_module(raw, None);
|
||||
}
|
||||
super::ShaderModule::Intermediate { .. } => {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user