diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs
index bd3a7b8f5a0..2f077b67d62 100644
--- a/wgpu-core/src/command/bundle.rs
+++ b/wgpu-core/src/command/bundle.rs
@@ -255,16 +255,11 @@ impl RenderBundleEncoder {
&*pipeline_guard,
&*query_set_guard,
),
- index: None,
- vertex: (0..hal::MAX_VERTEX_BUFFERS)
- .map(|_| VertexState::new())
- .collect(),
+ pipeline: None,
bind: (0..hal::MAX_BIND_GROUPS).map(|_| None).collect(),
- push_constant_ranges: PushConstantState::new(),
+ vertex: (0..hal::MAX_VERTEX_BUFFERS).map(|_| None).collect(),
+ index: None,
flat_dynamic_offsets: Vec::new(),
- used_bind_groups: 0,
- pipeline: None,
- pipeline_layout: None,
};
let mut commands = Vec::new();
let mut buffer_memory_init_actions = Vec::new();
@@ -347,8 +342,6 @@ impl RenderBundleEncoder {
RenderCommand::SetPipeline(pipeline_id) => {
let scope = PassErrorScope::SetPipelineRender(pipeline_id);
- state.pipeline = Some(pipeline_id);
-
let pipeline: &pipeline::RenderPipeline = state
.trackers
.render_pipelines
@@ -373,22 +366,17 @@ impl RenderBundleEncoder {
}
let layout = &pipeline_layout_guard[pipeline.layout_id.value];
- state.pipeline_layout = Some(pipeline.layout_id.value);
+ let pipeline_state = PipelineState::new(pipeline_id, pipeline, layout);
- state.set_pipeline(
- &pipeline.vertex_steps,
- &layout.bind_group_layout_ids,
- &layout.push_constant_ranges,
- );
commands.push(command);
- // If this pipeline's push constant ranges aren't the same
- // as the ones we were using previously (or if this is the
- // first pipeline to use push constants at all), then emit
- // commands to zero out the push constant values it will use.
- if let Some(iter) = state.flush_push_constants() {
+ // If this pipeline uses push constants, zero out their values.
+ if let Some(iter) = pipeline_state.zero_push_constants() {
commands.extend(iter)
}
+
+ state.invalidate_bind_groups(&pipeline_state, layout);
+ state.pipeline = Some(pipeline_state);
}
RenderCommand::SetIndexBuffer {
buffer_id,
@@ -444,7 +432,7 @@ impl RenderBundleEncoder {
offset..end,
MemoryInitKind::NeedsInitializedMemory,
));
- state.vertex[slot as usize].set_buffer(buffer_id, offset..end);
+ state.vertex[slot as usize] = Some(VertexState::new(buffer_id, offset..end));
}
RenderCommand::SetPushConstant {
stages,
@@ -455,11 +443,8 @@ impl RenderBundleEncoder {
let scope = PassErrorScope::SetPushConstant;
let end_offset = offset + size_bytes;
- let pipeline_layout_id = state
- .pipeline_layout
- .ok_or(DrawError::MissingPipeline)
- .map_pass_err(scope)?;
- let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id];
+ let pipeline = state.pipeline(scope)?;
+ let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id];
pipeline_layout
.validate_push_constant_ranges(stages, offset, end_offset)
@@ -476,9 +461,11 @@ impl RenderBundleEncoder {
let scope = PassErrorScope::Draw {
indexed: false,
indirect: false,
- pipeline: state.pipeline,
+ pipeline: state.pipeline_id(),
};
- let vertex_limits = state.vertex_limits();
+ let pipeline = state.pipeline(scope)?;
+ let used_bind_groups = pipeline.used_bind_groups;
+ let vertex_limits = state.vertex_limits(pipeline);
let last_vertex = first_vertex + vertex_count;
if last_vertex > vertex_limits.vertex_limit {
return Err(DrawError::VertexBeyondLimit {
@@ -498,7 +485,7 @@ impl RenderBundleEncoder {
.map_pass_err(scope);
}
commands.extend(state.flush_vertices());
- commands.extend(state.flush_binds(base.dynamic_offsets));
+ commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
commands.push(command);
}
RenderCommand::DrawIndexed {
@@ -511,14 +498,16 @@ impl RenderBundleEncoder {
let scope = PassErrorScope::Draw {
indexed: true,
indirect: false,
- pipeline: state.pipeline,
+ pipeline: state.pipeline_id(),
};
+ let pipeline = state.pipeline(scope)?;
+ let used_bind_groups = pipeline.used_bind_groups;
let index = match state.index {
Some(ref index) => index,
None => return Err(DrawError::MissingIndexBuffer).map_pass_err(scope),
};
//TODO: validate that base_vertex + max_index() is within the provided range
- let vertex_limits = state.vertex_limits();
+ let vertex_limits = state.vertex_limits(pipeline);
let index_limit = index.limit();
let last_index = first_index + index_count;
if last_index > index_limit {
@@ -539,7 +528,7 @@ impl RenderBundleEncoder {
}
commands.extend(state.flush_index());
commands.extend(state.flush_vertices());
- commands.extend(state.flush_binds(base.dynamic_offsets));
+ commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
commands.push(command);
}
RenderCommand::MultiDrawIndirect {
@@ -551,12 +540,15 @@ impl RenderBundleEncoder {
let scope = PassErrorScope::Draw {
indexed: false,
indirect: true,
- pipeline: state.pipeline,
+ pipeline: state.pipeline_id(),
};
device
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
.map_pass_err(scope)?;
+ let pipeline = state.pipeline(scope)?;
+ let used_bind_groups = pipeline.used_bind_groups;
+
let buffer: &resource::Buffer = state
.trackers
.buffers
@@ -574,7 +566,7 @@ impl RenderBundleEncoder {
));
commands.extend(state.flush_vertices());
- commands.extend(state.flush_binds(base.dynamic_offsets));
+ commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
commands.push(command);
}
RenderCommand::MultiDrawIndirect {
@@ -586,12 +578,15 @@ impl RenderBundleEncoder {
let scope = PassErrorScope::Draw {
indexed: true,
indirect: true,
- pipeline: state.pipeline,
+ pipeline: state.pipeline_id(),
};
device
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
.map_pass_err(scope)?;
+ let pipeline = state.pipeline(scope)?;
+ let used_bind_groups = pipeline.used_bind_groups;
+
let buffer: &resource::Buffer = state
.trackers
.buffers
@@ -615,7 +610,7 @@ impl RenderBundleEncoder {
commands.extend(index.flush());
commands.extend(state.flush_vertices());
- commands.extend(state.flush_binds(base.dynamic_offsets));
+ commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
commands.push(command);
}
RenderCommand::MultiDrawIndirect { .. }
@@ -994,31 +989,20 @@ impl IndexState {
/// [`flush`]: IndexState::flush
#[derive(Debug)]
struct VertexState {
- buffer: Option,
+ buffer: id::BufferId,
range: Range,
- step: pipeline::VertexStep,
is_dirty: bool,
}
impl VertexState {
- /// Construct a fresh `VertexState`: no buffer has been set for
- /// this slot.
- fn new() -> Self {
+ fn new(buffer: id::BufferId, range: Range) -> Self {
Self {
- buffer: None,
- range: 0..0,
- step: pipeline::VertexStep::default(),
- is_dirty: false,
+ buffer,
+ range,
+ is_dirty: true,
}
}
- /// Set this slot's vertex buffer.
- fn set_buffer(&mut self, buffer_id: id::BufferId, range: Range) {
- self.buffer = Some(buffer_id);
- self.range = range;
- self.is_dirty = true;
- }
-
/// Generate a `SetVertexBuffer` command for this slot, if necessary.
///
/// `slot` is the index of the vertex buffer slot that `self` tracks.
@@ -1027,7 +1011,7 @@ impl VertexState {
self.is_dirty = false;
Some(RenderCommand::SetVertexBuffer {
slot,
- buffer_id: self.buffer.unwrap(),
+ buffer_id: self.buffer,
offset: self.range.start,
size: wgt::BufferSize::new(self.range.end - self.range.start),
})
@@ -1055,38 +1039,6 @@ struct BindState {
is_dirty: bool,
}
-#[derive(Debug)]
-struct PushConstantState {
- /// Push constant ranges used by the most recently set pipeline.
- ///
- /// Before any pipeline has been set, this is empty.
- ranges: ArrayVec,
-
- /// True if this bundle has ever set a pipeline that uses push constants.
- ///
- /// If this is true, then every time we set a pipeline, we will emit
- /// `SetPushConstant` commands to clear the push constants it uses.
- is_dirty: bool,
-}
-impl PushConstantState {
- fn new() -> Self {
- Self {
- ranges: ArrayVec::new(),
- is_dirty: false,
- }
- }
-
- fn set_push_constants(&mut self, new_ranges: &[wgt::PushConstantRange]) -> bool {
- if &*self.ranges != new_ranges {
- self.ranges = new_ranges.iter().cloned().collect();
- self.is_dirty = true;
- true
- } else {
- false
- }
- }
-}
-
#[derive(Debug)]
struct VertexLimitState {
/// Length of the shortest vertex rate vertex buffer
@@ -1099,6 +1051,64 @@ struct VertexLimitState {
instance_limit_slot: u32,
}
+/// The bundle's current pipeline, and some cached information needed for validation.
+struct PipelineState {
+ /// The pipeline's id.
+ id: id::RenderPipelineId,
+
+ /// The id of the pipeline's layout.
+ layout_id: id::Valid,
+
+ /// How this pipeline's vertex shader traverses each vertex buffer, indexed
+ /// by vertex buffer slot number.
+ steps: Vec,
+
+ /// Ranges of push constants this pipeline uses, copied from the pipeline
+ /// layout.
+ push_constant_ranges: ArrayVec,
+
+ /// The number of bind groups this pipeline uses.
+ used_bind_groups: usize,
+}
+
+impl PipelineState {
+ fn new(
+ pipeline_id: id::RenderPipelineId,
+ pipeline: &pipeline::RenderPipeline,
+ layout: &binding_model::PipelineLayout,
+ ) -> Self {
+ Self {
+ id: pipeline_id,
+ layout_id: pipeline.layout_id.value,
+ steps: pipeline.vertex_steps.to_vec(),
+ push_constant_ranges: layout.push_constant_ranges.iter().cloned().collect(),
+ used_bind_groups: layout.bind_group_layout_ids.len(),
+ }
+ }
+
+ /// Return a sequence of commands to zero the push constant ranges this
+ /// pipeline uses. If no initialization is necessary, return `None`.
+ fn zero_push_constants(&self) -> Option> {
+ if !self.push_constant_ranges.is_empty() {
+ let nonoverlapping_ranges =
+ super::bind::compute_nonoverlapping_ranges(&self.push_constant_ranges);
+
+ Some(
+ nonoverlapping_ranges
+ .into_iter()
+ .map(|range| RenderCommand::SetPushConstant {
+ stages: range.stages,
+ offset: range.range.start,
+ size_bytes: range.range.end - range.range.start,
+ values_offset: None, // write zeros
+ }),
+ )
+ } else {
+ None
+ }
+ }
+}
+
/// State for analyzing and cleaning up bundle command streams.
///
/// To minimize state updates, [`RenderBundleEncoder::finish`]
@@ -1113,17 +1123,18 @@ struct State {
/// Resources used by this bundle. This will become [`RenderBundle::used`].
trackers: RenderBundleScope,
- /// The current index buffer, if one has been set. We flush this state
- /// before indexed draw commands.
- index: Option,
-
- /// The state of each vertex buffer slot.
- vertex: ArrayVec,
+ /// The currently set pipeline, if any.
+ pipeline: Option,
/// The bind group set at each index, if any.
bind: ArrayVec