435: Implement PUSH_CONSTANTS feature r=kvark a=cwfitzgerald

The rust half of the push constant extensions, continuing from https://github.com/gfx-rs/wgpu/pull/777.

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
bors[bot]
2020-07-13 19:44:51 +00:00
committed by GitHub
19 changed files with 214 additions and 81 deletions

View File

@@ -27,14 +27,14 @@ vulkan = ["wgc/gfx-backend-vulkan"]
package = "wgpu-core"
version = "0.5"
git = "https://github.com/gfx-rs/wgpu"
rev = "d7ee89018bed6b2e050b8160b2bd837d89f598a6"
rev = "f67771fb87ce17a7f7e09f9a01149b4132196928"
features = ["raw-window-handle"]
[dependencies.wgt]
package = "wgpu-types"
version = "0.5"
git = "https://github.com/gfx-rs/wgpu"
rev = "d7ee89018bed6b2e050b8160b2bd837d89f598a6"
rev = "f67771fb87ce17a7f7e09f9a01149b4132196928"
[dependencies]
arrayvec = "0.5"

View File

@@ -5,7 +5,7 @@ For the simplest examples without using any helping code (see `framework.rs` her
- `hello-triangle` for graphics and presentation
- `hello-compute` for pure computing
Notably, `capture` example show rendering without a surface/window. It reads back the contents and saves them to a file.
Notably, `capture` example shows rendering without a surface/window. It reads back the contents and saves them to a file.
All framework-based examples render to the window.
@@ -32,6 +32,7 @@ All framework-based examples render to the window.
| compute passes | :star: | | | | | | | |
| optional extensions | | | | | | | :star: | |
| - binding indexing | | | | | | | :star: | |
| - push constants | | | | | | | :star: | |
| WGSL shaders | | | | | | | | |
## Hacking

View File

@@ -90,6 +90,7 @@ impl framework::Example for Example {
let compute_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&compute_bind_group_layout],
push_constant_ranges: &[],
});
// create render pipeline with empty bind group layout
@@ -97,6 +98,7 @@ impl framework::Example for Example {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {

View File

@@ -161,6 +161,7 @@ impl framework::Example for Example {
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
// Create the texture

View File

@@ -33,6 +33,9 @@ pub trait Example: 'static + Sized {
fn needed_features() -> wgpu::Features {
wgpu::Features::empty()
}
fn needed_limits() -> wgpu::Limits {
wgpu::Limits::default()
}
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
device: &wgpu::Device,
@@ -97,12 +100,14 @@ async fn setup<E: Example>(title: &str) -> Setup {
let adapter_features = adapter.features();
let needed_limits = E::needed_limits();
let trace_dir = std::env::var("WGPU_TRACE");
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
features: adapter_features & needed_features,
limits: wgpu::Limits::default(),
limits: needed_limits,
shader_validation: true,
},
trace_dir.ok().as_ref().map(std::path::Path::new),

View File

@@ -81,6 +81,7 @@ async fn execute_gpu(numbers: Vec<u32>) -> Vec<u32> {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {

View File

@@ -36,6 +36,7 @@ async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu::
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {

View File

@@ -1,5 +1,6 @@
/// This example shows how to describe the adapter in use.
async fn run() {
#[cfg_attr(target_arch = "wasm32", allow(unused_variables))]
let adapter = wgpu::Instance::new(wgpu::BackendBit::PRIMARY)
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,

View File

@@ -101,6 +101,7 @@ impl Example {
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
let vs_module = device.create_shader_module(wgpu::include_spirv!("blit.vert.spv"));
@@ -249,6 +250,7 @@ impl framework::Example for Example {
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
// Create the texture

View File

@@ -141,6 +141,7 @@ impl framework::Example for Example {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let multisampled_framebuffer =

View File

@@ -442,6 +442,7 @@ impl framework::Example for Example {
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout],
push_constant_ranges: &[],
});
let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
@@ -553,6 +554,7 @@ impl framework::Example for Example {
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout],
push_constant_ranges: &[],
});
let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32);

View File

@@ -84,6 +84,7 @@ impl framework::Example for Skybox {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
// Create the render pipeline

View File

@@ -23,12 +23,6 @@ struct Uniform {
unsafe impl Pod for Uniform {}
unsafe impl Zeroable for Uniform {}
struct UniformWorkaroundData {
bind_group_layout: wgpu::BindGroupLayout,
bind_group0: wgpu::BindGroup,
bind_group1: wgpu::BindGroup,
}
fn vertex(pos: [i8; 2], tc: [i8; 2], index: i8) -> Vertex {
Vertex {
_pos: [pos[0] as f32, pos[1] as f32],
@@ -81,7 +75,7 @@ struct Example {
bind_group: wgpu::BindGroup,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
uniform_workaround_data: Option<UniformWorkaroundData>,
uniform_workaround: bool,
}
impl framework::Example for Example {
@@ -90,6 +84,13 @@ impl framework::Example for Example {
| wgpu::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
| wgpu::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING
| wgpu::Features::SAMPLED_TEXTURE_BINDING_ARRAY
| wgpu::Features::PUSH_CONSTANTS
}
fn needed_limits() -> wgpu::Limits {
wgpu::Limits {
max_push_constant_size: 4,
..wgpu::Limits::default()
}
}
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
@@ -131,56 +132,6 @@ impl framework::Example for Example {
let index_buffer = device
.create_buffer_with_data(bytemuck::cast_slice(&index_data), wgpu::BufferUsage::INDEX);
let uniform_workaround_data = if uniform_workaround {
let buffer0 = device.create_buffer_with_data(
&bytemuck::cast_slice(&[Uniform { index: 0 }]),
wgpu::BufferUsage::UNIFORM,
);
let buffer1 = device.create_buffer_with_data(
&bytemuck::cast_slice(&[Uniform { index: 1 }]),
wgpu::BufferUsage::UNIFORM,
);
let bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[wgpu::BindGroupLayoutEntry::new(
0,
wgpu::ShaderStage::FRAGMENT,
wgpu::BindingType::UniformBuffer {
dynamic: false,
min_binding_size: None,
},
)],
label: Some("uniform workaround bind group layout"),
});
let bind_group0 = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(buffer0.slice(..)),
}],
label: Some("uniform workaround bind group 0"),
});
let bind_group1 = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(buffer1.slice(..)),
}],
label: Some("uniform workaround bind group 1"),
});
Some(UniformWorkaroundData {
bind_group_layout,
bind_group0,
bind_group1,
})
} else {
None
};
let red_texture_data = create_texture_data(Color::RED);
let green_texture_data = create_texture_data(Color::GREEN);
@@ -289,13 +240,18 @@ impl framework::Example for Example {
label: Some("bind group"),
});
let pipeline_layout = if let Some(ref workaround) = uniform_workaround_data {
let pipeline_layout = if uniform_workaround {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout, &workaround.bind_group_layout],
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[wgpu::PushConstantRange {
stages: wgpu::ShaderStage::FRAGMENT,
range: 0..4,
}],
})
} else {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
})
};
@@ -342,7 +298,7 @@ impl framework::Example for Example {
index_buffer,
bind_group,
pipeline,
uniform_workaround_data,
uniform_workaround,
}
}
fn resize(
@@ -378,14 +334,28 @@ impl framework::Example for Example {
}],
depth_stencil_attachment: None,
});
let uniform_workaround_data = if self.uniform_workaround {
Some([Uniform { index: 0 }, Uniform { index: 1 }])
} else {
None
};
rpass.set_pipeline(&self.pipeline);
rpass.set_bind_group(0, &self.bind_group, &[]);
rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
rpass.set_index_buffer(self.index_buffer.slice(..));
if let Some(ref workaround) = self.uniform_workaround_data {
rpass.set_bind_group(1, &workaround.bind_group0, &[]);
if let Some(ref data) = uniform_workaround_data {
rpass.set_push_constants(
wgpu::ShaderStage::FRAGMENT,
0,
bytemuck::cast_slice(&data[0..1]),
);
rpass.draw_indexed(0..6, 0, 0..1);
rpass.set_bind_group(1, &workaround.bind_group1, &[]);
rpass.set_push_constants(
wgpu::ShaderStage::FRAGMENT,
0,
bytemuck::cast_slice(&data[1..2]),
);
rpass.draw_indexed(6..12, 0, 0..1);
} else {
rpass.draw_indexed(0..12, 0, 0..1);

View File

@@ -6,7 +6,7 @@ layout(location = 0) out vec4 o_Color;
layout(set = 0, binding = 0) uniform texture2D u_Textures[2];
layout(set = 0, binding = 1) uniform sampler u_Sampler;
layout(set = 1, binding = 0) uniform Uniforms {
layout(push_constant) uniform Uniforms {
int u_Index; // dynamically uniform
};

View File

@@ -414,11 +414,13 @@ impl framework::Example for Example {
let water_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&water_bind_group_layout],
push_constant_ranges: &[],
});
let terrain_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&terrain_bind_group_layout],
push_constant_ranges: &[],
});
let water_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {

View File

@@ -33,6 +33,7 @@ pub type Context = wgc::hub::Global<wgc::hub::IdentityManagerFactory>;
mod pass_impl {
use super::Context;
use smallvec::SmallVec;
use std::convert::TryInto;
use std::ops::Range;
use wgc::command::{bundle_ffi::*, compute_ffi::*, render_ffi::*};
@@ -56,6 +57,18 @@ mod pass_impl {
)
}
}
fn set_push_constants(&mut self, offset: u32, data: &[u32]) {
unsafe {
wgpu_compute_pass_set_push_constant(
self,
offset,
(data.len() * std::mem::size_of::<u32>())
.try_into()
.unwrap(),
data.as_ptr(),
)
}
}
fn dispatch(&mut self, x: u32, y: u32, z: u32) {
wgpu_compute_pass_dispatch(self, x, y, z)
}
@@ -105,6 +118,19 @@ mod pass_impl {
) {
wgpu_render_pass_set_vertex_buffer(self, slot, *buffer, offset, size)
}
fn set_push_constants(&mut self, stages: wgt::ShaderStage, offset: u32, data: &[u32]) {
unsafe {
wgpu_render_pass_set_push_constants(
self,
stages,
offset,
(data.len() * std::mem::size_of::<u32>())
.try_into()
.unwrap(),
data.as_ptr(),
)
}
}
fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
wgpu_render_pass_draw(
self,
@@ -288,6 +314,20 @@ mod pass_impl {
) {
wgpu_render_bundle_set_vertex_buffer(self, slot, *buffer, offset, size)
}
fn set_push_constants(&mut self, stages: wgt::ShaderStage, offset: u32, data: &[u32]) {
unsafe {
wgpu_render_bundle_set_push_constants(
self,
stages,
offset,
(data.len() * std::mem::size_of::<u32>())
.try_into()
.unwrap(),
data.as_ptr(),
)
}
}
fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
wgpu_render_bundle_draw(
self,
@@ -583,17 +623,27 @@ impl crate::Context for Context {
desc: &PipelineLayoutDescriptor,
) -> Self::PipelineLayoutId {
wgc::span!(_guard, TRACE, "Device::create_pipeline_layout wrapper");
//TODO: avoid allocation here
// Limit is always less or equal to wgc::MAX_BIND_GROUPS, so this is always right
// Guards following ArrayVec
assert!(
desc.bind_group_layouts.len() <= wgc::MAX_BIND_GROUPS,
"Bind group layout count {} exceeds device bind group limit {}",
desc.bind_group_layouts.len(),
wgc::MAX_BIND_GROUPS
);
let temp_layouts = desc
.bind_group_layouts
.iter()
.map(|bgl| bgl.id)
.collect::<Vec<_>>();
.collect::<ArrayVec<[_; wgc::MAX_BIND_GROUPS]>>();
gfx_select!(*device => self.device_create_pipeline_layout(
*device,
&wgc::binding_model::PipelineLayoutDescriptor {
&wgt::PipelineLayoutDescriptor {
bind_group_layouts: &temp_layouts,
push_constant_ranges: &desc.push_constant_ranges,
},
PhantomData
))

View File

@@ -71,6 +71,9 @@ impl crate::ComputePassInner<Context> for ComputePass {
offsets.len() as u32,
);
}
fn set_push_constants(&mut self, _offset: u32, _data: &[u32]) {
panic!("PUSH_CONSTANTS feature must be enabled to call multi_draw_indexed_indirect")
}
fn dispatch(&mut self, x: u32, y: u32, z: u32) {
self.0.dispatch_with_y_and_z(x, y, z);
}
@@ -129,6 +132,9 @@ impl crate::RenderInner<Context> for RenderPass {
size.expect("TODO").get() as f64,
);
}
fn set_push_constants(&mut self, _stages: wgt::ShaderStage, _offset: u32, _data: &[u32]) {
panic!("PUSH_CONSTANTS feature must be enabled to call multi_draw_indexed_indirect")
}
fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
self.0
.draw_with_instance_count_and_first_vertex_and_first_instance(

View File

@@ -29,12 +29,13 @@ pub use wgt::{
ColorStateDescriptor, ColorWrite, CommandBufferDescriptor, CompareFunction, CullMode,
DepthStencilStateDescriptor, DeviceDescriptor, DynamicOffset, Extent3d, Features, FilterMode,
FrontFace, IndexFormat, InputStepMode, Limits, Origin3d, PowerPreference, PresentMode,
PrimitiveTopology, RasterizationStateDescriptor, RenderBundleEncoderDescriptor, ShaderLocation,
ShaderStage, StencilOperation, StencilStateFaceDescriptor, SwapChainDescriptor,
SwapChainStatus, TextureAspect, TextureComponentType, TextureDataLayout, TextureDimension,
TextureFormat, TextureUsage, TextureViewDimension, VertexAttributeDescriptor,
VertexBufferDescriptor, VertexFormat, VertexStateDescriptor, BIND_BUFFER_ALIGNMENT,
COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT,
PrimitiveTopology, PushConstantRange, RasterizationStateDescriptor,
RenderBundleEncoderDescriptor, ShaderLocation, ShaderStage, StencilOperation,
StencilStateFaceDescriptor, SwapChainDescriptor, SwapChainStatus, TextureAspect,
TextureComponentType, TextureDataLayout, TextureDimension, TextureFormat, TextureUsage,
TextureViewDimension, VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
VertexStateDescriptor, BIND_BUFFER_ALIGNMENT, COPY_BUFFER_ALIGNMENT,
COPY_BYTES_PER_ROW_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
};
use backend::Context as C;
@@ -47,6 +48,7 @@ trait ComputePassInner<Ctx: Context> {
bind_group: &Ctx::BindGroupId,
offsets: &[DynamicOffset],
);
fn set_push_constants(&mut self, offset: u32, data: &[u32]);
fn dispatch(&mut self, x: u32, y: u32, z: u32);
fn dispatch_indirect(
&mut self,
@@ -76,6 +78,7 @@ trait RenderInner<Ctx: Context> {
offset: BufferAddress,
size: Option<BufferSize>,
);
fn set_push_constants(&mut self, stages: wgt::ShaderStage, offset: u32, data: &[u32]);
fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>);
fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);
fn draw_indirect(&mut self, indirect_buffer: &Ctx::BufferId, indirect_offset: BufferAddress);
@@ -857,6 +860,10 @@ pub use wgt::TextureViewDescriptor as TextureViewDescriptorBase;
/// Describes a [`TextureView`].
pub type TextureViewDescriptor<'a> = TextureViewDescriptorBase<Option<&'a str>>;
pub use wgt::PipelineLayoutDescriptor as PipelineLayoutDescriptorBase;
/// Describes a [`PipelineLayout`].
pub type PipelineLayoutDescriptor<'a> = PipelineLayoutDescriptorBase<'a, &'a BindGroupLayout>;
pub use wgt::SamplerDescriptor as SamplerDescriptorBase;
/// Describes a [`Sampler`].
pub type SamplerDescriptor<'a> = SamplerDescriptorBase<Option<&'a str>>;
@@ -870,10 +877,6 @@ pub use wgt::BindGroupDescriptor as BindGroupDescriptorBase;
pub type BindGroupDescriptor<'a> =
BindGroupDescriptorBase<'a, &'a BindGroupLayout, BindGroupEntry<'a>>;
pub use wgt::PipelineLayoutDescriptor as PipelineLayoutDescriptorBase;
/// Describes a pipeline layout.
pub type PipelineLayoutDescriptor<'a> = PipelineLayoutDescriptorBase<'a, &'a BindGroupLayout>;
pub use wgt::ProgrammableStageDescriptor as ProgrammableStageDescriptorBase;
/// Describes a programmable pipeline stage.
pub type ProgrammableStageDescriptor<'a> = wgt::ProgrammableStageDescriptor<'a, &'a ShaderModule>;
@@ -1921,6 +1924,41 @@ impl<'a> RenderPass<'a> {
}
}
/// [`Features::PUSH_CONSTANTS`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> {
/// Set push constant data.
///
/// Offset is measured in bytes, but must be a multiple of [`PUSH_CONSTANT_ALIGNMENT`].
///
/// Data size must be a multiple of 4 and must be aligned to the 4s, so we take an array of u32.
/// For example, with an offset of 4 and an array of `[u32; 3]`, that will write to the range
/// of 4..16.
///
/// For each byte in the range of push constant data written, the union of the stages of all push constant
/// ranges that covers that byte must be exactly `stages`. There's no good way of explaining this simply,
/// so here are some examples:
///
/// ```text
/// For the given ranges:
/// - 0..4 Vertex
/// - 4..8 Fragment
/// ```
///
/// You would need to upload this in two set_push_constants calls. First for the `Vertex` range, second for the `Fragment` range.
///
/// ```text
/// For the given ranges:
/// - 0..8 Vertex
/// - 4..12 Fragment
/// ```
///
/// You would need to upload this in three set_push_constants calls. First for the `Vertex` only range 0..4, second
/// for the `Vertex | Fragment` range 4..8, third for the `Fragment` range 8..12.
pub fn set_push_constants(&mut self, stages: wgt::ShaderStage, offset: u32, data: &'a [u32]) {
self.id.set_push_constants(stages, offset, data);
}
}
impl<'a> Drop for RenderPass<'a> {
fn drop(&mut self) {
if !thread::panicking() {
@@ -1967,6 +2005,20 @@ impl<'a> ComputePass<'a> {
}
}
/// [`Features::PUSH_CONSTANTS`] must be enabled on the device in order to call these functions.
impl<'a> ComputePass<'a> {
/// Set push constant data.
///
/// Offset is measured in bytes, but must be a multiple of [`PUSH_CONSTANT_ALIGNMENT`].
///
/// Data size must be a multiple of 4 and must be aligned to the 4s, so we take an array of u32.
/// For example, with an offset of 4 and an array of `[u32; 3]`, that will write to the range
/// of 4..16.
pub fn set_push_constants(&mut self, offset: u32, data: &'a [u32]) {
self.id.set_push_constants(offset, data);
}
}
impl<'a> Drop for ComputePass<'a> {
fn drop(&mut self) {
if !thread::panicking() {
@@ -2101,6 +2153,41 @@ impl<'a> RenderBundleEncoder<'a> {
}
}
/// [`Features::PUSH_CONSTANTS`] must be enabled on the device in order to call these functions.
impl<'a> RenderBundleEncoder<'a> {
/// Set push constant data.
///
/// Offset is measured in bytes, but must be a multiple of [`PUSH_CONSTANT_ALIGNMENT`].
///
/// Data size must be a multiple of 4 and must be aligned to the 4s, so we take an array of u32.
/// For example, with an offset of 4 and an array of `[u32; 3]`, that will write to the range
/// of 4..16.
///
/// For each byte in the range of push constant data written, the union of the stages of all push constant
/// ranges that covers that byte must be exactly `stages`. There's no good way of explaining this simply,
/// so here are some examples:
///
/// ```text
/// For the given ranges:
/// - 0..4 Vertex
/// - 4..8 Fragment
/// ```
///
/// You would need to upload this in two set_push_constants calls. First for the `Vertex` range, second for the `Fragment` range.
///
/// ```text
/// For the given ranges:
/// - 0..8 Vertex
/// - 4..12 Fragment
/// ```
///
/// You would need to upload this in three set_push_constants calls. First for the `Vertex` only range 0..4, second
/// for the `Vertex | Fragment` range 4..8, third for the `Fragment` range 8..12.
pub fn set_push_constants(&mut self, stages: wgt::ShaderStage, offset: u32, data: &'a [u32]) {
self.id.set_push_constants(stages, offset, data);
}
}
impl Queue {
/// Schedule a data write into `buffer` starting at `offset`.
pub fn write_buffer(&self, buffer: &Buffer, offset: BufferAddress, data: &[u8]) {