Unify set push constants.

This commit is contained in:
Vecvec 2025-06-22 19:11:00 +12:00 committed by Teodor Tanasoaia
parent acd8cb18bb
commit afa96af76f
5 changed files with 92 additions and 98 deletions

View File

@ -1315,7 +1315,7 @@ impl State {
fn pipeline(&self) -> Result<&PipelineState, RenderBundleErrorInner> {
self.pipeline
.as_ref()
.ok_or(DrawError::MissingPipeline.into())
.ok_or(DrawError::MissingPipeline(pass::MissingPipeline).into())
}
/// Mark all non-empty bind group table entries from `index` onwards as dirty.

View File

@ -114,7 +114,7 @@ type ArcComputePassDescriptor<'a> = ComputePassDescriptor<'a, ArcPassTimestampWr
#[non_exhaustive]
pub enum DispatchError {
#[error("Compute pipeline must be set")]
MissingPipeline,
MissingPipeline(pass::MissingPipeline),
#[error(transparent)]
IncompatibleBindGroup(#[from] Box<BinderError>),
#[error(
@ -176,6 +176,9 @@ pub enum ComputePassErrorInner {
InvalidResource(#[from] InvalidResourceError),
#[error(transparent)]
TimestampWrites(#[from] TimestampWritesError),
// This one is unreachable, but required for generic pass support
#[error(transparent)]
InvalidValuesOffset(#[from] pass::InvalidValuesOffset),
}
/// Error encountered when performing a compute pass, stored for later reporting
@ -188,6 +191,12 @@ pub struct ComputePassError {
pub(super) inner: ComputePassErrorInner,
}
impl From<pass::MissingPipeline> for ComputePassErrorInner {
fn from(value: pass::MissingPipeline) -> Self {
Self::Dispatch(DispatchError::MissingPipeline(value))
}
}
impl<E> MapPassErr<ComputePassError> for E
where
E: Into<ComputePassErrorInner>,
@ -223,7 +232,7 @@ impl<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder>
self.general.binder.check_late_buffer_bindings()?;
Ok(())
} else {
Err(DispatchError::MissingPipeline)
Err(DispatchError::MissingPipeline(pass::MissingPipeline))
}
}
@ -600,12 +609,21 @@ impl Global {
values_offset,
} => {
let scope = PassErrorScope::SetPushConstant;
set_push_constant(
&mut state,
pass::set_push_constant::<ComputePassErrorInner, _>(
&mut state.general,
&base.push_constant_data,
wgt::ShaderStages::COMPUTE,
offset,
size_bytes,
values_offset,
Some(values_offset),
|data_slice| {
let offset_in_elements =
(offset / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
let size_in_elements =
(size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
state.push_constants[offset_in_elements..][..size_in_elements]
.copy_from_slice(data_slice);
},
)
.map_pass_err(scope)?;
}
@ -735,7 +753,6 @@ fn set_pipeline(
pass::rebind_resources::<ComputePassErrorInner, _>(
&mut state.general,
&pipeline.layout,
wgt::ShaderStages::COMPUTE,
&pipeline.late_sized_buffer_groups,
|| {
// This only needs to be here for compute pipelines because they use push constants for
@ -757,48 +774,6 @@ fn set_pipeline(
)
}
fn set_push_constant(
state: &mut State,
push_constant_data: &[u32],
offset: u32,
size_bytes: u32,
values_offset: u32,
) -> Result<(), ComputePassErrorInner> {
let end_offset_bytes = offset + size_bytes;
let values_end_offset = (values_offset + size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
let data_slice = &push_constant_data[(values_offset as usize)..values_end_offset];
let pipeline_layout = state
.general
.binder
.pipeline_layout
.as_ref()
// TODO: don't error here, lazily update the push constants using `state.push_constants`
.ok_or(ComputePassErrorInner::Dispatch(
DispatchError::MissingPipeline,
))?;
pipeline_layout.validate_push_constant_ranges(
wgt::ShaderStages::COMPUTE,
offset,
end_offset_bytes,
)?;
let offset_in_elements = (offset / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
let size_in_elements = (size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
state.push_constants[offset_in_elements..][..size_in_elements].copy_from_slice(data_slice);
unsafe {
state.general.raw_encoder.set_push_constants(
pipeline_layout.raw(),
wgt::ShaderStages::COMPUTE,
offset,
data_slice,
);
}
Ok(())
}
fn dispatch(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorInner> {
state.is_ready()?;

View File

@ -20,7 +20,7 @@ pub enum DrawError {
#[error("Blend constant needs to be set")]
MissingBlendConstant,
#[error("Render pipeline must be set")]
MissingPipeline,
MissingPipeline(#[from] pass::MissingPipeline),
#[error("Currently set {pipeline} requires vertex buffer {index} to be set")]
MissingVertexBuffer {
pipeline: ResourceErrorIdent,

View File

@ -1,6 +1,6 @@
//! Generic pass functions that both compute and render passes need.
use crate::binding_model::{BindError, BindGroup};
use crate::binding_model::{BindError, BindGroup, PushConstantUploadError};
use crate::command::bind::Binder;
use crate::command::memory_init::{CommandBufferTextureMemoryActions, SurfacesInDiscardState};
use crate::command::CommandBuffer;
@ -26,6 +26,14 @@ pub struct BindGroupIndexOutOfRange {
pub max: u32,
}
#[derive(Clone, Debug, Error)]
#[error("Pipeline must be set")]
pub struct MissingPipeline;
#[derive(Clone, Debug, Error)]
#[error("Setting `values_offset` to be `None` is only for internal use in render bundles")]
pub struct InvalidValuesOffset;
pub(crate) struct BaseState<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> {
pub(crate) device: &'cmd_buf Arc<Device>,
@ -163,7 +171,6 @@ where
pub(crate) fn rebind_resources<E, F: FnOnce()>(
state: &mut BaseState,
pipeline_layout: &Arc<binding_model::PipelineLayout>,
stages: wgt::ShaderStages,
late_sized_buffer_groups: &[LateSizedBufferGroup],
f: F,
) -> Result<(), E>
@ -209,7 +216,7 @@ where
super::push_constant_clear(offset, size_bytes, |clear_offset, clear_data| unsafe {
state.raw_encoder.set_push_constants(
pipeline_layout.raw(),
stages,
range.stages,
clear_offset,
clear_data,
);
@ -218,3 +225,41 @@ where
}
Ok(())
}
pub(crate) fn set_push_constant<E, F: FnOnce(&[u32])>(
state: &mut BaseState,
push_constant_data: &[u32],
stages: wgt::ShaderStages,
offset: u32,
size_bytes: u32,
values_offset: Option<u32>,
f: F,
) -> Result<(), E>
where
E: From<PushConstantUploadError> + From<InvalidValuesOffset> + From<MissingPipeline>,
{
api_log!("Pass::set_push_constants");
let values_offset = values_offset.ok_or(InvalidValuesOffset)?;
let end_offset_bytes = offset + size_bytes;
let values_end_offset = (values_offset + size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
let data_slice = &push_constant_data[(values_offset as usize)..values_end_offset];
let pipeline_layout = state
.binder
.pipeline_layout
.as_ref()
.ok_or(MissingPipeline)?;
pipeline_layout.validate_push_constant_ranges(stages, offset, end_offset_bytes)?;
f(data_slice);
unsafe {
state
.raw_encoder
.set_push_constants(pipeline_layout.raw(), stages, offset, data_slice)
}
Ok(())
}

View File

@ -54,7 +54,7 @@ use super::{
};
use super::{DrawKind, Rect};
use crate::binding_model::BindError;
use crate::binding_model::{BindError, PushConstantUploadError};
pub use wgt::{LoadOp, StoreOp};
fn load_hal_ops<V>(load: LoadOp<V>) -> hal::AttachmentOps {
@ -559,7 +559,7 @@ impl<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder>
}
Ok(())
} else {
Err(DrawError::MissingPipeline)
Err(DrawError::MissingPipeline(pass::MissingPipeline))
}
}
@ -700,8 +700,8 @@ pub enum RenderPassErrorInner {
InvalidDepthOps,
#[error("Unable to clear non-present/read-only stencil")]
InvalidStencilOps,
#[error("Setting `values_offset` to be `None` is only for internal use in render bundles")]
InvalidValuesOffset,
#[error(transparent)]
InvalidValuesOffset(#[from] pass::InvalidValuesOffset),
#[error(transparent)]
MissingFeatures(#[from] MissingFeatures),
#[error(transparent)]
@ -789,6 +789,18 @@ impl From<pass::BindGroupIndexOutOfRange> for RenderPassErrorInner {
}
}
impl From<pass::MissingPipeline> for RenderPassErrorInner {
fn from(error: pass::MissingPipeline) -> Self {
Self::Draw(DrawError::MissingPipeline(error))
}
}
impl From<PushConstantUploadError> for RenderPassErrorInner {
fn from(error: PushConstantUploadError) -> Self {
Self::RenderCommand(error.into())
}
}
/// Error encountered when performing a render pass.
#[derive(Clone, Debug, Error)]
#[error("{scope}")]
@ -1915,13 +1927,14 @@ impl Global {
values_offset,
} => {
let scope = PassErrorScope::SetPushConstant;
set_push_constant(
&mut state,
pass::set_push_constant::<RenderPassErrorInner, _>(
&mut state.general,
&base.push_constant_data,
stages,
offset,
size_bytes,
values_offset,
|_| {},
)
.map_pass_err(scope)?;
}
@ -2238,7 +2251,6 @@ fn set_pipeline(
pass::rebind_resources::<RenderPassErrorInner, _>(
&mut state.general,
&pipeline.layout,
ShaderStages::VERTEX_FRAGMENT,
&pipeline.late_sized_buffer_groups,
|| {},
)?;
@ -2438,44 +2450,6 @@ fn set_viewport(
Ok(())
}
fn set_push_constant(
state: &mut State,
push_constant_data: &[u32],
stages: ShaderStages,
offset: u32,
size_bytes: u32,
values_offset: Option<u32>,
) -> Result<(), RenderPassErrorInner> {
api_log!("RenderPass::set_push_constants");
let values_offset = values_offset.ok_or(RenderPassErrorInner::InvalidValuesOffset)?;
let end_offset_bytes = offset + size_bytes;
let values_end_offset = (values_offset + size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
let data_slice = &push_constant_data[(values_offset as usize)..values_end_offset];
let pipeline_layout = state
.general
.binder
.pipeline_layout
.as_ref()
.ok_or(DrawError::MissingPipeline)?;
pipeline_layout
.validate_push_constant_ranges(stages, offset, end_offset_bytes)
.map_err(RenderCommandError::from)?;
unsafe {
state.general.raw_encoder.set_push_constants(
pipeline_layout.raw(),
stages,
offset,
data_slice,
)
}
Ok(())
}
fn set_scissor(state: &mut State, rect: Rect<u32>) -> Result<(), RenderPassErrorInner> {
api_log!("RenderPass::set_scissor_rect {rect:?}");