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