Renderpass take resource ownership (#5884)

* share timestamp write struct

* Make name of set_push_constants methods consistently plural

* remove lifetime bounds of resources passed into render pass

* first render pass resource ownership test

* introduce dynrenderpass & immediately create ArcCommands and take ownership of resources passed on pass creation

* Use of dynrenderpass in deno

* Separate active occlusion & pipeline statitics query

* resolve render/compute command is now behind `replay` feature

* add vertex & index buffer to ownership test

* test for pipeline statistics query

* add occlusion query set to pass resource test

* add tests for resource ownership of render pass query timestamps

* RenderPass can now be made 'static just like ComputePass. Add respective test

* Extend encoder_operations_fail_while_pass_alive test to also check encoder locking errors with render passes

* improve changelog entry on lifetime bounds
This commit is contained in:
Andreas Reich 2024-07-01 18:36:24 +02:00 committed by GitHub
parent c9a2d972ad
commit 0a76c0fa84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 2151 additions and 802 deletions

View File

@ -41,24 +41,38 @@ Bottom level categories:
### Major Changes ### Major Changes
#### Remove lifetime bounds on `wgpu::ComputePass` #### Lifetime bounds on `wgpu::RenderPass` & `wgpu::ComputePass`
TODO(wumpf): This is still work in progress. Should write a bit more about it. Also will very likely extend to `wgpu::RenderPass` before release. `wgpu::RenderPass` & `wgpu::ComputePass` recording methods (e.g. `wgpu::RenderPass:set_render_pipeline`) no longer impose a lifetime constraint to objects passed to a pass (like pipelines/buffers/bindgroups/query-sets etc.).
`wgpu::ComputePass` recording methods (e.g. `wgpu::ComputePass:set_render_pipeline`) no longer impose a lifetime constraint passed in resources. This means the following pattern works now as expected:
```rust
let mut pipelines: Vec<wgpu::RenderPipeline> = ...;
// ...
let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());
cpass.set_pipeline(&pipelines[123]);
// Change pipeline container - this requires mutable access to `pipelines` while one of the pipelines is in use.
pipelines.push(/* ... */);
// Continue pass recording.
cpass.set_bindgroup(...);
```
Previously, a set pipeline (or other resource) had to outlive pass recording which often affected wider systems,
meaning that users needed to prove to the borrow checker that `Vec<wgpu::RenderPipeline>` (or similar constructs)
aren't accessed mutably for the duration of pass recording.
Furthermore, you can now opt out of `wgpu::ComputePass`'s lifetime dependency on its parent `wgpu::CommandEncoder` using `wgpu::ComputePass::forget_lifetime`:
Furthermore, you can now opt out of `wgpu::RenderPass`/`wgpu::ComputePass`'s lifetime dependency on its parent `wgpu::CommandEncoder` using `wgpu::RenderPass::forget_lifetime`/`wgpu::ComputePass::forget_lifetime`:
```rust ```rust
fn independent_cpass<'enc>(encoder: &'enc mut wgpu::CommandEncoder) -> wgpu::ComputePass<'static> { fn independent_cpass<'enc>(encoder: &'enc mut wgpu::CommandEncoder) -> wgpu::ComputePass<'static> {
let cpass: wgpu::ComputePass<'enc> = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default()); let cpass: wgpu::ComputePass<'enc> = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());
cpass.forget_lifetime() cpass.forget_lifetime()
} }
``` ```
⚠️ As long as a `wgpu::ComputePass` is pending for a given `wgpu::CommandEncoder`, creation of a compute or render pass is an error and invalidates the `wgpu::CommandEncoder`. ⚠️ As long as a `wgpu::RenderPass`/`wgpu::ComputePass` is pending for a given `wgpu::CommandEncoder`, creation of a compute or render pass is an error and invalidates the `wgpu::CommandEncoder`.
This is very useful for library authors, but opens up an easy way for incorrect use, so use with care. `forget_lifetime` can be very useful for library authors, but opens up an easy way for incorrect use, so use with care.
`forget_lifetime` is zero overhead and has no side effects on pass recording. This method doesn't add any additional overhead and has no side effects on pass recording.
By @wumpf in [#5569](https://github.com/gfx-rs/wgpu/pull/5569), [#5575](https://github.com/gfx-rs/wgpu/pull/5575), [#5620](https://github.com/gfx-rs/wgpu/pull/5620), [#5768](https://github.com/gfx-rs/wgpu/pull/5768) (together with @kpreid), [#5671](https://github.com/gfx-rs/wgpu/pull/5671). By @wumpf in [#5569](https://github.com/gfx-rs/wgpu/pull/5569), [#5575](https://github.com/gfx-rs/wgpu/pull/5575), [#5620](https://github.com/gfx-rs/wgpu/pull/5620), [#5768](https://github.com/gfx-rs/wgpu/pull/5768) (together with @kpreid), [#5671](https://github.com/gfx-rs/wgpu/pull/5671), [#5794](https://github.com/gfx-rs/wgpu/pull/5794), [#5884](https://github.com/gfx-rs/wgpu/pull/5884).
#### Querying shader compilation errors #### Querying shader compilation errors

View File

@ -186,7 +186,7 @@ pub fn op_webgpu_command_encoder_begin_render_pass(
.get::<WebGpuQuerySet>(timestamp_writes.query_set)?; .get::<WebGpuQuerySet>(timestamp_writes.query_set)?;
let query_set = query_set_resource.1; let query_set = query_set_resource.1;
Some(wgpu_core::command::RenderPassTimestampWrites { Some(wgpu_core::command::PassTimestampWrites {
query_set, query_set,
beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index, beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index,
end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,
@ -200,6 +200,8 @@ pub fn op_webgpu_command_encoder_begin_render_pass(
.transpose()? .transpose()?
.map(|query_set| query_set.1); .map(|query_set| query_set.1);
let instance = state.borrow::<super::Instance>();
let command_encoder = &command_encoder_resource.1;
let descriptor = wgpu_core::command::RenderPassDescriptor { let descriptor = wgpu_core::command::RenderPassDescriptor {
label: Some(label), label: Some(label),
color_attachments: Cow::from(color_attachments), color_attachments: Cow::from(color_attachments),
@ -208,15 +210,14 @@ pub fn op_webgpu_command_encoder_begin_render_pass(
occlusion_query_set: occlusion_query_set_resource, occlusion_query_set: occlusion_query_set_resource,
}; };
let render_pass = wgpu_core::command::RenderPass::new(command_encoder_resource.1, &descriptor); let (render_pass, error) = gfx_select!(command_encoder => instance.command_encoder_create_render_pass_dyn(*command_encoder, &descriptor));
let rid = state let rid = state
.resource_table .resource_table
.add(super::render_pass::WebGpuRenderPass(RefCell::new( .add(super::render_pass::WebGpuRenderPass(RefCell::new(
render_pass, render_pass,
))); )));
Ok(WebGpuResult::rid(rid)) Ok(WebGpuResult::rid_err(rid, error))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -245,7 +246,7 @@ pub fn op_webgpu_command_encoder_begin_compute_pass(
.get::<WebGpuQuerySet>(timestamp_writes.query_set)?; .get::<WebGpuQuerySet>(timestamp_writes.query_set)?;
let query_set = query_set_resource.1; let query_set = query_set_resource.1;
Some(wgpu_core::command::ComputePassTimestampWrites { Some(wgpu_core::command::PassTimestampWrites {
query_set, query_set,
beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index, beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index,
end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,

View File

@ -9,11 +9,10 @@ use deno_core::ResourceId;
use serde::Deserialize; use serde::Deserialize;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use wgpu_core::global::Global;
use super::error::WebGpuResult; use super::error::WebGpuResult;
pub(crate) struct WebGpuRenderPass(pub(crate) RefCell<wgpu_core::command::RenderPass>); pub(crate) struct WebGpuRenderPass(pub(crate) RefCell<Box<dyn wgpu_core::command::DynRenderPass>>);
impl Resource for WebGpuRenderPass { impl Resource for WebGpuRenderPass {
fn name(&self) -> Cow<str> { fn name(&self) -> Cow<str> {
"webGPURenderPass".into() "webGPURenderPass".into()
@ -42,8 +41,8 @@ pub fn op_webgpu_render_pass_set_viewport(
.resource_table .resource_table
.get::<WebGpuRenderPass>(args.render_pass_rid)?; .get::<WebGpuRenderPass>(args.render_pass_rid)?;
state.borrow::<Global>().render_pass_set_viewport( render_pass_resource.0.borrow_mut().set_viewport(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
args.x, args.x,
args.y, args.y,
args.width, args.width,
@ -69,13 +68,10 @@ pub fn op_webgpu_render_pass_set_scissor_rect(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_set_scissor_rect( render_pass_resource
&mut render_pass_resource.0.borrow_mut(), .0
x, .borrow_mut()
y, .set_scissor_rect(state.borrow(), x, y, width, height)?;
width,
height,
)?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -91,9 +87,10 @@ pub fn op_webgpu_render_pass_set_blend_constant(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state render_pass_resource
.borrow::<Global>() .0
.render_pass_set_blend_constant(&mut render_pass_resource.0.borrow_mut(), &color)?; .borrow_mut()
.set_blend_constant(state.borrow(), color)?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -109,9 +106,10 @@ pub fn op_webgpu_render_pass_set_stencil_reference(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state render_pass_resource
.borrow::<Global>() .0
.render_pass_set_stencil_reference(&mut render_pass_resource.0.borrow_mut(), reference)?; .borrow_mut()
.set_stencil_reference(state.borrow(), reference)?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -127,9 +125,10 @@ pub fn op_webgpu_render_pass_begin_occlusion_query(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state render_pass_resource
.borrow::<Global>() .0
.render_pass_begin_occlusion_query(&mut render_pass_resource.0.borrow_mut(), query_index)?; .borrow_mut()
.begin_occlusion_query(state.borrow(), query_index)?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -144,9 +143,10 @@ pub fn op_webgpu_render_pass_end_occlusion_query(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state render_pass_resource
.borrow::<Global>() .0
.render_pass_end_occlusion_query(&mut render_pass_resource.0.borrow_mut())?; .borrow_mut()
.end_occlusion_query(state.borrow())?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -172,9 +172,10 @@ pub fn op_webgpu_render_pass_execute_bundles(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state render_pass_resource
.borrow::<Global>() .0
.render_pass_execute_bundles(&mut render_pass_resource.0.borrow_mut(), &bundles)?; .borrow_mut()
.execute_bundles(state.borrow(), &bundles)?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -189,12 +190,7 @@ pub fn op_webgpu_render_pass_end(
.resource_table .resource_table
.take::<WebGpuRenderPass>(render_pass_rid)?; .take::<WebGpuRenderPass>(render_pass_rid)?;
// TODO: Just like parent_id ComputePass, there's going to be DynComputePass soon which will eliminate the need of doing gfx_select here. render_pass_resource.0.borrow_mut().end(state.borrow())?;
let instance = state.borrow::<Global>();
let parent_id = render_pass_resource.0.borrow().parent_id();
gfx_select!(parent_id => instance.render_pass_end(
&mut render_pass_resource.0.borrow_mut()
))?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -226,8 +222,8 @@ pub fn op_webgpu_render_pass_set_bind_group(
let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len]; let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
state.borrow::<Global>().render_pass_set_bind_group( render_pass_resource.0.borrow_mut().set_bind_group(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
index, index,
bind_group_resource.1, bind_group_resource.1,
dynamic_offsets_data, dynamic_offsets_data,
@ -247,8 +243,8 @@ pub fn op_webgpu_render_pass_push_debug_group(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_push_debug_group( render_pass_resource.0.borrow_mut().push_debug_group(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
group_label, group_label,
0, // wgpu#975 0, // wgpu#975
)?; )?;
@ -266,9 +262,10 @@ pub fn op_webgpu_render_pass_pop_debug_group(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state render_pass_resource
.borrow::<Global>() .0
.render_pass_pop_debug_group(&mut render_pass_resource.0.borrow_mut())?; .borrow_mut()
.pop_debug_group(state.borrow())?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -284,8 +281,8 @@ pub fn op_webgpu_render_pass_insert_debug_marker(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_insert_debug_marker( render_pass_resource.0.borrow_mut().insert_debug_marker(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
marker_label, marker_label,
0, // wgpu#975 0, // wgpu#975
)?; )?;
@ -307,10 +304,10 @@ pub fn op_webgpu_render_pass_set_pipeline(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_set_pipeline( render_pass_resource
&mut render_pass_resource.0.borrow_mut(), .0
render_pipeline_resource.1, .borrow_mut()
)?; .set_pipeline(state.borrow(), render_pipeline_resource.1)?;
Ok(WebGpuResult::empty()) Ok(WebGpuResult::empty())
} }
@ -341,8 +338,8 @@ pub fn op_webgpu_render_pass_set_index_buffer(
None None
}; };
state.borrow::<Global>().render_pass_set_index_buffer( render_pass_resource.0.borrow_mut().set_index_buffer(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
buffer_resource.1, buffer_resource.1,
index_format, index_format,
offset, offset,
@ -378,8 +375,8 @@ pub fn op_webgpu_render_pass_set_vertex_buffer(
None None
}; };
state.borrow::<Global>().render_pass_set_vertex_buffer( render_pass_resource.0.borrow_mut().set_vertex_buffer(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
slot, slot,
buffer_resource.1, buffer_resource.1,
offset, offset,
@ -403,8 +400,8 @@ pub fn op_webgpu_render_pass_draw(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_draw( render_pass_resource.0.borrow_mut().draw(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
vertex_count, vertex_count,
instance_count, instance_count,
first_vertex, first_vertex,
@ -429,8 +426,8 @@ pub fn op_webgpu_render_pass_draw_indexed(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_draw_indexed( render_pass_resource.0.borrow_mut().draw_indexed(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
index_count, index_count,
instance_count, instance_count,
first_index, first_index,
@ -456,8 +453,8 @@ pub fn op_webgpu_render_pass_draw_indirect(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_draw_indirect( render_pass_resource.0.borrow_mut().draw_indirect(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
buffer_resource.1, buffer_resource.1,
indirect_offset, indirect_offset,
)?; )?;
@ -480,8 +477,8 @@ pub fn op_webgpu_render_pass_draw_indexed_indirect(
.resource_table .resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?; .get::<WebGpuRenderPass>(render_pass_rid)?;
state.borrow::<Global>().render_pass_draw_indexed_indirect( render_pass_resource.0.borrow_mut().draw_indexed_indirect(
&mut render_pass_resource.0.borrow_mut(), state.borrow(),
buffer_resource.1, buffer_resource.1,
indirect_offset, indirect_offset,
)?; )?;

View File

@ -111,14 +111,14 @@ async fn compute_pass_query_set_ownership_pipeline_statistics(ctx: TestingContex
} }
#[gpu_test] #[gpu_test]
static COMPUTE_PASS_QUERY_TIMESTAMPS: GpuTestConfiguration = static COMPUTE_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS: GpuTestConfiguration =
GpuTestConfiguration::new() GpuTestConfiguration::new()
.parameters(TestParameters::default().test_features_limits().features( .parameters(TestParameters::default().test_features_limits().features(
wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES, wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
)) ))
.run_async(compute_pass_query_timestamps); .run_async(compute_pass_query_set_ownership_timestamps);
async fn compute_pass_query_timestamps(ctx: TestingContext) { async fn compute_pass_query_set_ownership_timestamps(ctx: TestingContext) {
let ResourceSetup { let ResourceSetup {
gpu_buffer, gpu_buffer,
cpu_buffer, cpu_buffer,

View File

@ -77,18 +77,16 @@ static DROP_ENCODER_AFTER_ERROR: GpuTestConfiguration = GpuTestConfiguration::ne
drop(encoder); drop(encoder);
}); });
// TODO: This should also apply to render passes once the lifetime bound is lifted.
#[gpu_test] #[gpu_test]
static ENCODER_OPERATIONS_FAIL_WHILE_COMPUTE_PASS_ALIVE: GpuTestConfiguration = static ENCODER_OPERATIONS_FAIL_WHILE_PASS_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new()
GpuTestConfiguration::new()
.parameters(TestParameters::default().features( .parameters(TestParameters::default().features(
wgpu::Features::CLEAR_TEXTURE wgpu::Features::CLEAR_TEXTURE
| wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY
| wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, | wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS,
)) ))
.run_sync(encoder_operations_fail_while_compute_pass_alive); .run_sync(encoder_operations_fail_while_pass_alive);
fn encoder_operations_fail_while_compute_pass_alive(ctx: TestingContext) { fn encoder_operations_fail_while_pass_alive(ctx: TestingContext) {
let buffer_source = ctx let buffer_source = ctx
.device .device
.create_buffer_init(&wgpu::util::BufferInitDescriptor { .create_buffer_init(&wgpu::util::BufferInitDescriptor {
@ -129,6 +127,23 @@ fn encoder_operations_fail_while_compute_pass_alive(ctx: TestingContext) {
label: None, label: None,
}); });
let target_desc = wgpu::TextureDescriptor {
label: Some("target_tex"),
size: wgpu::Extent3d {
width: 4,
height: 4,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[wgpu::TextureFormat::Bgra8UnormSrgb],
};
let target_tex = ctx.device.create_texture(&target_desc);
let color_attachment_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
let recording_ops: Vec<(_, Box<dyn Fn(&mut CommandEncoder)>)> = vec![ let recording_ops: Vec<(_, Box<dyn Fn(&mut CommandEncoder)>)> = vec![
( (
@ -252,18 +267,45 @@ fn encoder_operations_fail_while_compute_pass_alive(ctx: TestingContext) {
), ),
]; ];
#[derive(Clone, Copy, Debug)]
enum PassType {
Compute,
Render,
}
let create_pass = |encoder: &mut wgpu::CommandEncoder, pass_type| -> Box<dyn std::any::Any> {
match pass_type {
PassType::Compute => Box::new(
encoder
.begin_compute_pass(&wgpu::ComputePassDescriptor::default())
.forget_lifetime(),
),
PassType::Render => Box::new(
encoder
.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &color_attachment_view,
resolve_target: None,
ops: wgpu::Operations::default(),
})],
..Default::default()
})
.forget_lifetime(),
),
}
};
for &pass_type in [PassType::Compute, PassType::Render].iter() {
for (op_name, op) in recording_ops.iter() { for (op_name, op) in recording_ops.iter() {
let mut encoder = ctx let mut encoder = ctx
.device .device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let pass = encoder let pass = create_pass(&mut encoder, pass_type);
.begin_compute_pass(&wgpu::ComputePassDescriptor::default())
.forget_lifetime();
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
log::info!("Testing operation {} on a locked command encoder", op_name); log::info!("Testing operation {op_name:?} on a locked command encoder while a {pass_type:?} pass is active");
fail( fail(
&ctx.device, &ctx.device,
|| op(&mut encoder), || op(&mut encoder),
@ -289,9 +331,7 @@ fn encoder_operations_fail_while_compute_pass_alive(ctx: TestingContext) {
let mut encoder = ctx let mut encoder = ctx
.device .device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let pass = encoder let pass = create_pass(&mut encoder, pass_type);
.begin_compute_pass(&wgpu::ComputePassDescriptor::default())
.forget_lifetime();
fail( fail(
&ctx.device, &ctx.device,
|| encoder.finish(), || encoder.finish(),
@ -304,3 +344,4 @@ fn encoder_operations_fail_while_compute_pass_alive(ctx: TestingContext) {
); );
} }
} }
}

View File

@ -0,0 +1,552 @@
//! Tests that render passes take ownership of resources that are associated with.
//! I.e. once a resource is passed in to a render pass, it can be dropped.
//!
//! TODO: Methods that take resources that weren't tested here:
//! * rpass.draw_indexed_indirect(indirect_buffer, indirect_offset)
//! * rpass.execute_bundles(render_bundles)
//! * rpass.multi_draw_indirect(indirect_buffer, indirect_offset, count)
//! * rpass.multi_draw_indexed_indirect(indirect_buffer, indirect_offset, count)
//! * rpass.multi_draw_indirect_count
//! * rpass.multi_draw_indexed_indirect_count
//!
use std::num::NonZeroU64;
use wgpu::util::DeviceExt as _;
use wgpu_test::{gpu_test, valid, GpuTestConfiguration, TestParameters, TestingContext};
// Minimal shader with buffer based side effect - only needed to check whether the render pass has executed at all.
const SHADER_SRC: &str = "
@group(0) @binding(0)
var<storage, read_write> buffer: array<vec4f>;
var<private> positions: array<vec2f, 3> = array<vec2f, 3>(
vec2f(-1.0, -3.0),
vec2f(-1.0, 1.0),
vec2f(3.0, 1.0)
);
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4<f32> {
return vec4f(positions[vertex_index], 0.0, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
buffer[0] *= 2.0;
return vec4<f32>(1.0, 0.0, 1.0, 1.0);
}";
#[gpu_test]
static RENDER_PASS_RESOURCE_OWNERSHIP: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().test_features_limits())
.run_async(render_pass_resource_ownership);
async fn render_pass_resource_ownership(ctx: TestingContext) {
let ResourceSetup {
gpu_buffer,
cpu_buffer,
buffer_size,
indirect_buffer,
vertex_buffer,
index_buffer,
bind_group,
pipeline,
color_attachment_view,
color_attachment_resolve_view,
depth_stencil_view,
occlusion_query_set,
} = resource_setup(&ctx);
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("render_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &color_attachment_view,
resolve_target: Some(&color_attachment_resolve_view),
ops: wgpu::Operations::default(),
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth_stencil_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: Some(&occlusion_query_set),
});
// Drop render pass attachments right away.
drop(color_attachment_view);
drop(color_attachment_resolve_view);
drop(depth_stencil_view);
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
rpass.begin_occlusion_query(0);
rpass.draw_indirect(&indirect_buffer, 0);
rpass.end_occlusion_query();
// Now drop all resources we set. Then do a device poll to make sure the resources are really not dropped too early, no matter what.
drop(pipeline);
drop(bind_group);
drop(indirect_buffer);
drop(vertex_buffer);
drop(index_buffer);
drop(occlusion_query_set);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
}
assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;
}
#[gpu_test]
static RENDER_PASS_QUERY_SET_OWNERSHIP_PIPELINE_STATISTICS: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.test_features_limits()
.features(wgpu::Features::PIPELINE_STATISTICS_QUERY),
)
.run_async(render_pass_query_set_ownership_pipeline_statistics);
async fn render_pass_query_set_ownership_pipeline_statistics(ctx: TestingContext) {
let ResourceSetup {
gpu_buffer,
cpu_buffer,
buffer_size,
vertex_buffer,
index_buffer,
bind_group,
pipeline,
color_attachment_view,
depth_stencil_view,
..
} = resource_setup(&ctx);
let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {
label: Some("query_set"),
ty: wgpu::QueryType::PipelineStatistics(
wgpu::PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS,
),
count: 1,
});
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &color_attachment_view,
resolve_target: None,
ops: wgpu::Operations::default(),
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth_stencil_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
rpass.begin_pipeline_statistics_query(&query_set, 0);
rpass.draw(0..3, 0..1);
rpass.end_pipeline_statistics_query();
// Drop the query set. Then do a device poll to make sure it's not dropped too early, no matter what.
drop(query_set);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
}
assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;
}
#[gpu_test]
static RENDER_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(TestParameters::default().test_features_limits().features(
wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
))
.run_async(render_pass_query_set_ownership_timestamps);
async fn render_pass_query_set_ownership_timestamps(ctx: TestingContext) {
let ResourceSetup {
gpu_buffer,
cpu_buffer,
buffer_size,
color_attachment_view,
depth_stencil_view,
pipeline,
bind_group,
vertex_buffer,
index_buffer,
..
} = resource_setup(&ctx);
let query_set_timestamp_writes = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {
label: Some("query_set_timestamp_writes"),
ty: wgpu::QueryType::Timestamp,
count: 2,
});
let query_set_write_timestamp = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {
label: Some("query_set_write_timestamp"),
ty: wgpu::QueryType::Timestamp,
count: 1,
});
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &color_attachment_view,
resolve_target: None,
ops: wgpu::Operations::default(),
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth_stencil_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: Some(wgpu::RenderPassTimestampWrites {
query_set: &query_set_timestamp_writes,
beginning_of_pass_write_index: Some(0),
end_of_pass_write_index: Some(1),
}),
..Default::default()
});
rpass.write_timestamp(&query_set_write_timestamp, 0);
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
rpass.draw(0..3, 0..1);
// Drop the query sets. Then do a device poll to make sure they're not dropped too early, no matter what.
drop(query_set_timestamp_writes);
drop(query_set_write_timestamp);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
}
assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await;
}
#[gpu_test]
static RENDER_PASS_KEEP_ENCODER_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().test_features_limits())
.run_async(render_pass_keep_encoder_alive);
async fn render_pass_keep_encoder_alive(ctx: TestingContext) {
let ResourceSetup {
bind_group,
vertex_buffer,
index_buffer,
pipeline,
color_attachment_view,
depth_stencil_view,
..
} = resource_setup(&ctx);
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &color_attachment_view,
resolve_target: None,
ops: wgpu::Operations::default(),
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth_stencil_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
// Now drop the encoder - it is kept alive by the compute pass.
// To do so, we have to make the compute pass forget the lifetime constraint first.
let mut rpass = rpass.forget_lifetime();
drop(encoder);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
// Record some a draw command.
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
rpass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
rpass.draw(0..3, 0..1);
// Dropping the pass will still execute the pass, even though there's no way to submit it.
// Ideally, this would log an error, but the encoder is not dropped until the compute pass is dropped,
// making this a valid operation.
// (If instead the encoder was explicitly destroyed or finished, this would be an error.)
valid(&ctx.device, || drop(rpass));
}
async fn assert_render_pass_executed_normally(
mut encoder: wgpu::CommandEncoder,
gpu_buffer: wgpu::Buffer,
cpu_buffer: wgpu::Buffer,
buffer_size: u64,
ctx: TestingContext,
) {
encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, buffer_size);
ctx.queue.submit([encoder.finish()]);
cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let data = cpu_buffer.slice(..).get_mapped_range();
let floats: &[f32] = bytemuck::cast_slice(&data);
assert!(floats[0] >= 2.0);
assert!(floats[1] >= 4.0);
assert!(floats[2] >= 6.0);
assert!(floats[3] >= 8.0);
}
// Setup ------------------------------------------------------------
struct ResourceSetup {
gpu_buffer: wgpu::Buffer,
cpu_buffer: wgpu::Buffer,
buffer_size: u64,
indirect_buffer: wgpu::Buffer,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup,
pipeline: wgpu::RenderPipeline,
color_attachment_view: wgpu::TextureView,
color_attachment_resolve_view: wgpu::TextureView,
depth_stencil_view: wgpu::TextureView,
occlusion_query_set: wgpu::QuerySet,
}
fn resource_setup(ctx: &TestingContext) -> ResourceSetup {
let sm = ctx
.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("shader"),
source: wgpu::ShaderSource::Wgsl(SHADER_SRC.into()),
});
let buffer_size = 4 * std::mem::size_of::<f32>() as u64;
let bgl = ctx
.device
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("bind_group_layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: NonZeroU64::new(buffer_size),
},
count: None,
}],
});
let gpu_buffer = ctx
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("gpu_buffer"),
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
contents: bytemuck::bytes_of(&[1.0_f32, 2.0, 3.0, 4.0]),
});
let cpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("cpu_buffer"),
size: buffer_size,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let vertex_count = 3;
let indirect_buffer = ctx
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("gpu_buffer"),
usage: wgpu::BufferUsages::INDIRECT,
contents: wgpu::util::DrawIndirectArgs {
vertex_count,
instance_count: 1,
first_vertex: 0,
first_instance: 0,
}
.as_bytes(),
});
let vertex_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("vertex_buffer"),
usage: wgpu::BufferUsages::VERTEX,
size: std::mem::size_of::<u32>() as u64 * vertex_count as u64,
mapped_at_creation: false,
});
let index_buffer = ctx
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("vertex_buffer"),
usage: wgpu::BufferUsages::INDEX,
contents: bytemuck::cast_slice(&[0_u32, 1, 2]),
});
let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("bind_group"),
layout: &bgl,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: gpu_buffer.as_entire_binding(),
}],
});
let pipeline_layout = ctx
.device
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("pipeline_layout"),
bind_group_layouts: &[&bgl],
push_constant_ranges: &[],
});
let target_size = wgpu::Extent3d {
width: 4,
height: 4,
depth_or_array_layers: 1,
};
let target_msaa = 4;
let target_format = wgpu::TextureFormat::Bgra8UnormSrgb;
let target_desc = wgpu::TextureDescriptor {
label: Some("target_tex"),
size: target_size,
mip_level_count: 1,
sample_count: target_msaa,
dimension: wgpu::TextureDimension::D2,
format: target_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[target_format],
};
let target_tex = ctx.device.create_texture(&target_desc);
let target_tex_resolve = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: Some("target_resolve"),
sample_count: 1,
..target_desc
});
let color_attachment_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default());
let color_attachment_resolve_view =
target_tex_resolve.create_view(&wgpu::TextureViewDescriptor::default());
let depth_stencil_format = wgpu::TextureFormat::Depth32Float;
let depth_stencil = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: Some("depth_stencil"),
format: depth_stencil_format,
view_formats: &[depth_stencil_format],
..target_desc
});
let depth_stencil_view = depth_stencil.create_view(&wgpu::TextureViewDescriptor::default());
let occlusion_query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {
label: Some("occ_query_set"),
ty: wgpu::QueryType::Occlusion,
count: 1,
});
let pipeline = ctx
.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &sm,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[wgpu::VertexBufferLayout {
array_stride: 4,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![0 => Uint32],
}],
},
fragment: Some(wgpu::FragmentState {
module: &sm,
entry_point: "fs_main",
compilation_options: Default::default(),
targets: &[Some(target_format.into())],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
strip_index_format: Some(wgpu::IndexFormat::Uint32),
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: depth_stencil_format,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::LessEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState {
count: target_msaa,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
});
ResourceSetup {
gpu_buffer,
cpu_buffer,
buffer_size,
indirect_buffer,
vertex_buffer,
index_buffer,
bind_group,
pipeline,
color_attachment_view,
color_attachment_resolve_view,
depth_stencil_view,
occlusion_query_set,
}
}

View File

@ -29,6 +29,7 @@ mod poll;
mod push_constants; mod push_constants;
mod query_set; mod query_set;
mod queue_transfer; mod queue_transfer;
mod render_pass_ownership;
mod resource_descriptor_accessor; mod resource_descriptor_accessor;
mod resource_error; mod resource_error;
mod scissor_tests; mod scissor_tests;

View File

@ -4,12 +4,12 @@ use crate::{
}, },
command::{ command::{
bind::Binder, bind::Binder,
compute_command::{ArcComputeCommand, ComputeCommand}, compute_command::ArcComputeCommand,
end_pipeline_statistics_query, end_pipeline_statistics_query,
memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState}, memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
validate_and_begin_pipeline_statistics_query, BasePass, BindGroupStateChange, validate_and_begin_pipeline_statistics_query, ArcPassTimestampWrites, BasePass,
CommandBuffer, CommandEncoderError, CommandEncoderStatus, MapPassErr, PassErrorScope, BindGroupStateChange, CommandBuffer, CommandEncoderError, CommandEncoderStatus, MapPassErr,
QueryUseError, StateChange, PassErrorScope, PassTimestampWrites, QueryUseError, StateChange,
}, },
device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures}, device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
error::{ErrorFormatter, PrettyError}, error::{ErrorFormatter, PrettyError},
@ -28,10 +28,6 @@ use crate::{
}; };
use hal::CommandEncoder as _; use hal::CommandEncoder as _;
#[cfg(feature = "serde")]
use serde::Deserialize;
#[cfg(feature = "serde")]
use serde::Serialize;
use thiserror::Error; use thiserror::Error;
use wgt::{BufferAddress, DynamicOffset}; use wgt::{BufferAddress, DynamicOffset};
@ -53,7 +49,7 @@ pub struct ComputePass<A: HalApi> {
/// If it is none, this pass is invalid and any operation on it will return an error. /// If it is none, this pass is invalid and any operation on it will return an error.
parent: Option<Arc<CommandBuffer<A>>>, parent: Option<Arc<CommandBuffer<A>>>,
timestamp_writes: Option<ArcComputePassTimestampWrites<A>>, timestamp_writes: Option<ArcPassTimestampWrites<A>>,
// Resource binding dedupe state. // Resource binding dedupe state.
current_bind_groups: BindGroupStateChange, current_bind_groups: BindGroupStateChange,
@ -103,39 +99,17 @@ impl<A: HalApi> fmt::Debug for ComputePass<A> {
} }
} }
/// Describes the writing of timestamp values in a compute pass.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ComputePassTimestampWrites {
/// The query set to write the timestamps to.
pub query_set: id::QuerySetId,
/// The index of the query set at which a start timestamp of this pass is written, if any.
pub beginning_of_pass_write_index: Option<u32>,
/// The index of the query set at which an end timestamp of this pass is written, if any.
pub end_of_pass_write_index: Option<u32>,
}
/// Describes the writing of timestamp values in a compute pass with the query set resolved.
struct ArcComputePassTimestampWrites<A: HalApi> {
/// The query set to write the timestamps to.
pub query_set: Arc<resource::QuerySet<A>>,
/// The index of the query set at which a start timestamp of this pass is written, if any.
pub beginning_of_pass_write_index: Option<u32>,
/// The index of the query set at which an end timestamp of this pass is written, if any.
pub end_of_pass_write_index: Option<u32>,
}
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct ComputePassDescriptor<'a> { pub struct ComputePassDescriptor<'a> {
pub label: Label<'a>, pub label: Label<'a>,
/// Defines where and when timestamp values will be written for this pass. /// Defines where and when timestamp values will be written for this pass.
pub timestamp_writes: Option<&'a ComputePassTimestampWrites>, pub timestamp_writes: Option<&'a PassTimestampWrites>,
} }
struct ArcComputePassDescriptor<'a, A: HalApi> { struct ArcComputePassDescriptor<'a, A: HalApi> {
pub label: &'a Label<'a>, pub label: &'a Label<'a>,
/// Defines where and when timestamp values will be written for this pass. /// Defines where and when timestamp values will be written for this pass.
pub timestamp_writes: Option<ArcComputePassTimestampWrites<A>>, pub timestamp_writes: Option<ArcPassTimestampWrites<A>>,
} }
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
@ -370,7 +344,9 @@ impl Global {
let Ok(query_set) = hub.query_sets.read().get_owned(tw.query_set) else { let Ok(query_set) = hub.query_sets.read().get_owned(tw.query_set) else {
return ( return (
ComputePass::new(None, arc_desc), ComputePass::new(None, arc_desc),
Some(CommandEncoderError::InvalidTimestampWritesQuerySetId), Some(CommandEncoderError::InvalidTimestampWritesQuerySetId(
tw.query_set,
)),
); );
}; };
@ -378,7 +354,7 @@ impl Global {
return (ComputePass::new(None, arc_desc), Some(e.into())); return (ComputePass::new(None, arc_desc), Some(e.into()));
} }
Some(ArcComputePassTimestampWrites { Some(ArcPassTimestampWrites {
query_set, query_set,
beginning_of_pass_write_index: tw.beginning_of_pass_write_index, beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
end_of_pass_write_index: tw.end_of_pass_write_index, end_of_pass_write_index: tw.end_of_pass_write_index,
@ -429,20 +405,22 @@ impl Global {
} }
#[doc(hidden)] #[doc(hidden)]
#[cfg(feature = "replay")]
pub fn compute_pass_end_with_unresolved_commands<A: HalApi>( pub fn compute_pass_end_with_unresolved_commands<A: HalApi>(
&self, &self,
encoder_id: id::CommandEncoderId, encoder_id: id::CommandEncoderId,
base: BasePass<ComputeCommand>, base: BasePass<super::ComputeCommand>,
timestamp_writes: Option<&ComputePassTimestampWrites>, timestamp_writes: Option<&PassTimestampWrites>,
) -> Result<(), ComputePassError> { ) -> Result<(), ComputePassError> {
let hub = A::hub(self); let hub = A::hub(self);
let scope = PassErrorScope::PassEncoder(encoder_id); let scope = PassErrorScope::PassEncoder(encoder_id);
let cmd_buf = CommandBuffer::get_encoder(hub, encoder_id).map_pass_err(scope)?; let cmd_buf = CommandBuffer::get_encoder(hub, encoder_id).map_pass_err(scope)?;
let commands = ComputeCommand::resolve_compute_command_ids(A::hub(self), &base.commands)?; let commands =
super::ComputeCommand::resolve_compute_command_ids(A::hub(self), &base.commands)?;
let timestamp_writes = if let Some(tw) = timestamp_writes { let timestamp_writes = if let Some(tw) = timestamp_writes {
Some(ArcComputePassTimestampWrites { Some(ArcPassTimestampWrites {
query_set: hub query_set: hub
.query_sets .query_sets
.read() .read()
@ -473,7 +451,7 @@ impl Global {
&self, &self,
cmd_buf: &CommandBuffer<A>, cmd_buf: &CommandBuffer<A>,
base: BasePass<ArcComputeCommand<A>>, base: BasePass<ArcComputeCommand<A>>,
mut timestamp_writes: Option<ArcComputePassTimestampWrites<A>>, mut timestamp_writes: Option<ArcPassTimestampWrites<A>>,
) -> Result<(), ComputePassError> { ) -> Result<(), ComputePassError> {
profiling::scope!("CommandEncoder::run_compute_pass"); profiling::scope!("CommandEncoder::run_compute_pass");
let pass_scope = PassErrorScope::Pass(Some(cmd_buf.as_info().id())); let pass_scope = PassErrorScope::Pass(Some(cmd_buf.as_info().id()));
@ -494,9 +472,7 @@ impl Global {
string_data: base.string_data.to_vec(), string_data: base.string_data.to_vec(),
push_constant_data: base.push_constant_data.to_vec(), push_constant_data: base.push_constant_data.to_vec(),
}, },
timestamp_writes: timestamp_writes timestamp_writes: timestamp_writes.as_ref().map(|tw| PassTimestampWrites {
.as_ref()
.map(|tw| ComputePassTimestampWrites {
query_set: tw.query_set.as_info().id(), query_set: tw.query_set.as_info().id(),
beginning_of_pass_write_index: tw.beginning_of_pass_write_index, beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
end_of_pass_write_index: tw.end_of_pass_write_index, end_of_pass_write_index: tw.end_of_pass_write_index,
@ -1104,7 +1080,7 @@ impl Global {
Ok(()) Ok(())
} }
pub fn compute_pass_set_push_constant<A: HalApi>( pub fn compute_pass_set_push_constants<A: HalApi>(
&self, &self,
pass: &mut ComputePass<A>, pass: &mut ComputePass<A>,
offset: u32, offset: u32,

View File

@ -8,8 +8,6 @@ use crate::{
resource::{Buffer, QuerySet}, resource::{Buffer, QuerySet},
}; };
use super::{ComputePassError, ComputePassErrorInner, PassErrorScope};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ComputeCommand { pub enum ComputeCommand {
@ -72,13 +70,13 @@ pub enum ComputeCommand {
impl ComputeCommand { impl ComputeCommand {
/// Resolves all ids in a list of commands into the corresponding resource Arc. /// Resolves all ids in a list of commands into the corresponding resource Arc.
// #[cfg(feature = "replay")]
// TODO: Once resolving is done on-the-fly during recording, this function should be only needed with the replay feature:
// #[cfg(feature = "replay")]
pub fn resolve_compute_command_ids<A: HalApi>( pub fn resolve_compute_command_ids<A: HalApi>(
hub: &crate::hub::Hub<A>, hub: &crate::hub::Hub<A>,
commands: &[ComputeCommand], commands: &[ComputeCommand],
) -> Result<Vec<ArcComputeCommand<A>>, ComputePassError> { ) -> Result<Vec<ArcComputeCommand<A>>, super::ComputePassError> {
use super::{ComputePassError, ComputePassErrorInner, PassErrorScope};
let buffers_guard = hub.buffers.read(); let buffers_guard = hub.buffers.read();
let bind_group_guard = hub.bind_groups.read(); let bind_group_guard = hub.bind_groups.read();
let query_set_guard = hub.query_sets.read(); let query_set_guard = hub.query_sets.read();

View File

@ -21,7 +21,7 @@ pub trait DynComputePass: std::fmt::Debug + WasmNotSendSync {
context: &global::Global, context: &global::Global,
pipeline_id: id::ComputePipelineId, pipeline_id: id::ComputePipelineId,
) -> Result<(), ComputePassError>; ) -> Result<(), ComputePassError>;
fn set_push_constant( fn set_push_constants(
&mut self, &mut self,
context: &global::Global, context: &global::Global,
offset: u32, offset: u32,
@ -93,13 +93,13 @@ impl<A: HalApi> DynComputePass for ComputePass<A> {
context.compute_pass_set_pipeline(self, pipeline_id) context.compute_pass_set_pipeline(self, pipeline_id)
} }
fn set_push_constant( fn set_push_constants(
&mut self, &mut self,
context: &global::Global, context: &global::Global,
offset: u32, offset: u32,
data: &[u8], data: &[u8],
) -> Result<(), ComputePassError> { ) -> Result<(), ComputePassError> {
context.compute_pass_set_push_constant(self, offset, data) context.compute_pass_set_push_constants(self, offset, data)
} }
fn dispatch_workgroups( fn dispatch_workgroups(

View File

@ -0,0 +1,458 @@
use wgt::WasmNotSendSync;
use crate::{global, hal_api::HalApi, id};
use super::{RenderPass, RenderPassError};
/// Trait for type erasing RenderPass.
// TODO(#5124): wgpu-core's RenderPass trait should not be hal type dependent.
// Practically speaking this allows us merge gfx_select with type erasure:
// The alternative would be to introduce RenderPassId which then first needs to be looked up and then dispatch via gfx_select.
pub trait DynRenderPass: std::fmt::Debug + WasmNotSendSync {
fn set_bind_group(
&mut self,
context: &global::Global,
index: u32,
bind_group_id: id::BindGroupId,
offsets: &[wgt::DynamicOffset],
) -> Result<(), RenderPassError>;
fn set_index_buffer(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
index_format: wgt::IndexFormat,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), RenderPassError>;
fn set_vertex_buffer(
&mut self,
context: &global::Global,
slot: u32,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), RenderPassError>;
fn set_pipeline(
&mut self,
context: &global::Global,
pipeline_id: id::RenderPipelineId,
) -> Result<(), RenderPassError>;
fn set_push_constants(
&mut self,
context: &global::Global,
stages: wgt::ShaderStages,
offset: u32,
data: &[u8],
) -> Result<(), RenderPassError>;
fn draw(
&mut self,
context: &global::Global,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), RenderPassError>;
fn draw_indexed(
&mut self,
context: &global::Global,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<(), RenderPassError>;
fn draw_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), RenderPassError>;
fn draw_indexed_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), RenderPassError>;
fn multi_draw_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count: u32,
) -> Result<(), RenderPassError>;
fn multi_draw_indexed_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count: u32,
) -> Result<(), RenderPassError>;
fn multi_draw_indirect_count(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count_buffer_id: id::BufferId,
count_buffer_offset: wgt::BufferAddress,
max_count: u32,
) -> Result<(), RenderPassError>;
fn multi_draw_indexed_indirect_count(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count_buffer_id: id::BufferId,
count_buffer_offset: wgt::BufferAddress,
max_count: u32,
) -> Result<(), RenderPassError>;
fn set_blend_constant(
&mut self,
context: &global::Global,
color: wgt::Color,
) -> Result<(), RenderPassError>;
fn set_scissor_rect(
&mut self,
context: &global::Global,
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), RenderPassError>;
fn set_viewport(
&mut self,
context: &global::Global,
x: f32,
y: f32,
width: f32,
height: f32,
min_depth: f32,
max_depth: f32,
) -> Result<(), RenderPassError>;
fn set_stencil_reference(
&mut self,
context: &global::Global,
reference: u32,
) -> Result<(), RenderPassError>;
fn push_debug_group(
&mut self,
context: &global::Global,
label: &str,
color: u32,
) -> Result<(), RenderPassError>;
fn pop_debug_group(&mut self, context: &global::Global) -> Result<(), RenderPassError>;
fn insert_debug_marker(
&mut self,
context: &global::Global,
label: &str,
color: u32,
) -> Result<(), RenderPassError>;
fn write_timestamp(
&mut self,
context: &global::Global,
query_set_id: id::QuerySetId,
query_index: u32,
) -> Result<(), RenderPassError>;
fn begin_occlusion_query(
&mut self,
context: &global::Global,
query_index: u32,
) -> Result<(), RenderPassError>;
fn end_occlusion_query(&mut self, context: &global::Global) -> Result<(), RenderPassError>;
fn begin_pipeline_statistics_query(
&mut self,
context: &global::Global,
query_set_id: id::QuerySetId,
query_index: u32,
) -> Result<(), RenderPassError>;
fn end_pipeline_statistics_query(
&mut self,
context: &global::Global,
) -> Result<(), RenderPassError>;
fn execute_bundles(
&mut self,
context: &global::Global,
bundles: &[id::RenderBundleId],
) -> Result<(), RenderPassError>;
fn end(&mut self, context: &global::Global) -> Result<(), RenderPassError>;
fn label(&self) -> Option<&str>;
}
impl<A: HalApi> DynRenderPass for RenderPass<A> {
fn set_index_buffer(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
index_format: wgt::IndexFormat,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), RenderPassError> {
context.render_pass_set_index_buffer(self, buffer_id, index_format, offset, size)
}
fn set_vertex_buffer(
&mut self,
context: &global::Global,
slot: u32,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>,
) -> Result<(), RenderPassError> {
context.render_pass_set_vertex_buffer(self, slot, buffer_id, offset, size)
}
fn set_bind_group(
&mut self,
context: &global::Global,
index: u32,
bind_group_id: id::BindGroupId,
offsets: &[wgt::DynamicOffset],
) -> Result<(), RenderPassError> {
context.render_pass_set_bind_group(self, index, bind_group_id, offsets)
}
fn set_pipeline(
&mut self,
context: &global::Global,
pipeline_id: id::RenderPipelineId,
) -> Result<(), RenderPassError> {
context.render_pass_set_pipeline(self, pipeline_id)
}
fn set_push_constants(
&mut self,
context: &global::Global,
stages: wgt::ShaderStages,
offset: u32,
data: &[u8],
) -> Result<(), RenderPassError> {
context.render_pass_set_push_constants(self, stages, offset, data)
}
fn draw(
&mut self,
context: &global::Global,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), RenderPassError> {
context.render_pass_draw(
self,
vertex_count,
instance_count,
first_vertex,
first_instance,
)
}
fn draw_indexed(
&mut self,
context: &global::Global,
index_count: u32,
instance_count: u32,
first_index: u32,
base_vertex: i32,
first_instance: u32,
) -> Result<(), RenderPassError> {
context.render_pass_draw_indexed(
self,
index_count,
instance_count,
first_index,
base_vertex,
first_instance,
)
}
fn draw_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), RenderPassError> {
context.render_pass_draw_indirect(self, buffer_id, offset)
}
fn draw_indexed_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
) -> Result<(), RenderPassError> {
context.render_pass_draw_indexed_indirect(self, buffer_id, offset)
}
fn multi_draw_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count: u32,
) -> Result<(), RenderPassError> {
context.render_pass_multi_draw_indirect(self, buffer_id, offset, count)
}
fn multi_draw_indexed_indirect(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count: u32,
) -> Result<(), RenderPassError> {
context.render_pass_multi_draw_indexed_indirect(self, buffer_id, offset, count)
}
fn multi_draw_indirect_count(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count_buffer_id: id::BufferId,
count_buffer_offset: wgt::BufferAddress,
max_count: u32,
) -> Result<(), RenderPassError> {
context.render_pass_multi_draw_indirect_count(
self,
buffer_id,
offset,
count_buffer_id,
count_buffer_offset,
max_count,
)
}
fn multi_draw_indexed_indirect_count(
&mut self,
context: &global::Global,
buffer_id: id::BufferId,
offset: wgt::BufferAddress,
count_buffer_id: id::BufferId,
count_buffer_offset: wgt::BufferAddress,
max_count: u32,
) -> Result<(), RenderPassError> {
context.render_pass_multi_draw_indexed_indirect_count(
self,
buffer_id,
offset,
count_buffer_id,
count_buffer_offset,
max_count,
)
}
fn set_blend_constant(
&mut self,
context: &global::Global,
color: wgt::Color,
) -> Result<(), RenderPassError> {
context.render_pass_set_blend_constant(self, color)
}
fn set_scissor_rect(
&mut self,
context: &global::Global,
x: u32,
y: u32,
width: u32,
height: u32,
) -> Result<(), RenderPassError> {
context.render_pass_set_scissor_rect(self, x, y, width, height)
}
fn set_viewport(
&mut self,
context: &global::Global,
x: f32,
y: f32,
width: f32,
height: f32,
min_depth: f32,
max_depth: f32,
) -> Result<(), RenderPassError> {
context.render_pass_set_viewport(self, x, y, width, height, min_depth, max_depth)
}
fn set_stencil_reference(
&mut self,
context: &global::Global,
reference: u32,
) -> Result<(), RenderPassError> {
context.render_pass_set_stencil_reference(self, reference)
}
fn push_debug_group(
&mut self,
context: &global::Global,
label: &str,
color: u32,
) -> Result<(), RenderPassError> {
context.render_pass_push_debug_group(self, label, color)
}
fn pop_debug_group(&mut self, context: &global::Global) -> Result<(), RenderPassError> {
context.render_pass_pop_debug_group(self)
}
fn insert_debug_marker(
&mut self,
context: &global::Global,
label: &str,
color: u32,
) -> Result<(), RenderPassError> {
context.render_pass_insert_debug_marker(self, label, color)
}
fn write_timestamp(
&mut self,
context: &global::Global,
query_set_id: id::QuerySetId,
query_index: u32,
) -> Result<(), RenderPassError> {
context.render_pass_write_timestamp(self, query_set_id, query_index)
}
fn begin_occlusion_query(
&mut self,
context: &global::Global,
query_index: u32,
) -> Result<(), RenderPassError> {
context.render_pass_begin_occlusion_query(self, query_index)
}
fn end_occlusion_query(&mut self, context: &global::Global) -> Result<(), RenderPassError> {
context.render_pass_end_occlusion_query(self)
}
fn begin_pipeline_statistics_query(
&mut self,
context: &global::Global,
query_set_id: id::QuerySetId,
query_index: u32,
) -> Result<(), RenderPassError> {
context.render_pass_begin_pipeline_statistics_query(self, query_set_id, query_index)
}
fn end_pipeline_statistics_query(
&mut self,
context: &global::Global,
) -> Result<(), RenderPassError> {
context.render_pass_end_pipeline_statistics_query(self)
}
fn execute_bundles(
&mut self,
context: &global::Global,
bundles: &[id::RenderBundleId],
) -> Result<(), RenderPassError> {
context.render_pass_execute_bundles(self, bundles)
}
fn end(&mut self, context: &global::Global) -> Result<(), RenderPassError> {
context.render_pass_end(self)
}
fn label(&self) -> Option<&str> {
self.label()
}
}

View File

@ -6,10 +6,12 @@ mod compute;
mod compute_command; mod compute_command;
mod draw; mod draw;
mod dyn_compute_pass; mod dyn_compute_pass;
mod dyn_render_pass;
mod memory_init; mod memory_init;
mod query; mod query;
mod render; mod render;
mod render_command; mod render_command;
mod timestamp_writes;
mod transfer; mod transfer;
use std::sync::Arc; use std::sync::Arc;
@ -17,11 +19,14 @@ use std::sync::Arc;
pub(crate) use self::clear::clear_texture; pub(crate) use self::clear::clear_texture;
pub use self::{ pub use self::{
bundle::*, clear::ClearError, compute::*, compute_command::ComputeCommand, draw::*, bundle::*, clear::ClearError, compute::*, compute_command::ComputeCommand, draw::*,
dyn_compute_pass::DynComputePass, query::*, render::*, render_command::RenderCommand, dyn_compute_pass::DynComputePass, dyn_render_pass::DynRenderPass, query::*, render::*,
transfer::*, render_command::RenderCommand, transfer::*,
}; };
pub(crate) use allocator::CommandAllocator; pub(crate) use allocator::CommandAllocator;
pub(crate) use timestamp_writes::ArcPassTimestampWrites;
pub use timestamp_writes::PassTimestampWrites;
use self::memory_init::CommandBufferTextureMemoryActions; use self::memory_init::CommandBufferTextureMemoryActions;
use crate::device::{Device, DeviceError}; use crate::device::{Device, DeviceError};
@ -604,8 +609,28 @@ pub enum CommandEncoderError {
Device(#[from] DeviceError), Device(#[from] DeviceError),
#[error("Command encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")] #[error("Command encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
Locked, Locked,
#[error("QuerySet provided for pass timestamp writes is invalid.")]
InvalidTimestampWritesQuerySetId, #[error("QuerySet {0:?} for pass timestamp writes is invalid.")]
InvalidTimestampWritesQuerySetId(id::QuerySetId),
#[error("Attachment texture view {0:?} is invalid")]
InvalidAttachment(id::TextureViewId),
#[error("Attachment texture view {0:?} for resolve is invalid")]
InvalidResolveTarget(id::TextureViewId),
#[error("Depth stencil attachment view {0:?} is invalid")]
InvalidDepthStencilAttachment(id::TextureViewId),
#[error("Occlusion query set {0:?} is invalid")]
InvalidOcclusionQuerySetId(id::QuerySetId),
}
impl PrettyError for CommandEncoderError {
fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
fmt.error(self);
if let Self::InvalidAttachment(id) = *self {
fmt.texture_view_label_with_key(&id, "attachment");
} else if let Self::InvalidResolveTarget(id) = *self {
fmt.texture_view_label_with_key(&id, "resolve target");
};
}
} }
impl Global { impl Global {
@ -860,10 +885,7 @@ pub enum PassErrorScope {
#[error("In a bundle parameter")] #[error("In a bundle parameter")]
Bundle, Bundle,
#[error("In a pass parameter")] #[error("In a pass parameter")]
// TODO: To be removed in favor of `Pass`. PassEncoder(id::CommandEncoderId), // Needed only for ending pass via tracing.
// ComputePass is already operating on command buffer instead,
// same should apply to RenderPass in the future.
PassEncoder(id::CommandEncoderId),
#[error("In a pass parameter")] #[error("In a pass parameter")]
Pass(Option<id::CommandBufferId>), Pass(Option<id::CommandBufferId>),
#[error("In a set_bind_group command")] #[error("In a set_bind_group command")]

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,7 @@ use wgt::{BufferAddress, BufferSize, Color};
use std::{num::NonZeroU32, sync::Arc}; use std::{num::NonZeroU32, sync::Arc};
use super::{ use super::{Rect, RenderBundle};
DrawKind, PassErrorScope, Rect, RenderBundle, RenderCommandError, RenderPassError,
RenderPassErrorInner,
};
#[doc(hidden)] #[doc(hidden)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -128,13 +125,15 @@ pub enum RenderCommand {
impl RenderCommand { impl RenderCommand {
/// Resolves all ids in a list of commands into the corresponding resource Arc. /// Resolves all ids in a list of commands into the corresponding resource Arc.
// #[cfg(feature = "replay")]
// TODO: Once resolving is done on-the-fly during recording, this function should be only needed with the replay feature:
// #[cfg(feature = "replay")]
pub fn resolve_render_command_ids<A: HalApi>( pub fn resolve_render_command_ids<A: HalApi>(
hub: &crate::hub::Hub<A>, hub: &crate::hub::Hub<A>,
commands: &[RenderCommand], commands: &[RenderCommand],
) -> Result<Vec<ArcRenderCommand<A>>, RenderPassError> { ) -> Result<Vec<ArcRenderCommand<A>>, super::RenderPassError> {
use super::{
DrawKind, PassErrorScope, RenderCommandError, RenderPassError, RenderPassErrorInner,
};
let buffers_guard = hub.buffers.read(); let buffers_guard = hub.buffers.read();
let bind_group_guard = hub.bind_groups.read(); let bind_group_guard = hub.bind_groups.read();
let query_set_guard = hub.query_sets.read(); let query_set_guard = hub.query_sets.read();

View File

@ -0,0 +1,25 @@
use std::sync::Arc;
use crate::{hal_api::HalApi, id};
/// Describes the writing of timestamp values in a render or compute pass.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PassTimestampWrites {
/// The query set to write the timestamps to.
pub query_set: id::QuerySetId,
/// The index of the query set at which a start timestamp of this pass is written, if any.
pub beginning_of_pass_write_index: Option<u32>,
/// The index of the query set at which an end timestamp of this pass is written, if any.
pub end_of_pass_write_index: Option<u32>,
}
/// Describes the writing of timestamp values in a render or compute pass with the query set resolved.
pub struct ArcPassTimestampWrites<A: HalApi> {
/// The query set to write the timestamps to.
pub query_set: Arc<crate::resource::QuerySet<A>>,
/// The index of the query set at which a start timestamp of this pass is written, if any.
pub beginning_of_pass_write_index: Option<u32>,
/// The index of the query set at which an end timestamp of this pass is written, if any.
pub end_of_pass_write_index: Option<u32>,
}

View File

@ -60,15 +60,6 @@ pub(crate) struct AttachmentData<T> {
pub depth_stencil: Option<T>, pub depth_stencil: Option<T>,
} }
impl<T: PartialEq> Eq for AttachmentData<T> {} impl<T: PartialEq> Eq for AttachmentData<T> {}
impl<T> AttachmentData<T> {
pub(crate) fn map<U, F: Fn(&T) -> U>(&self, fun: F) -> AttachmentData<U> {
AttachmentData {
colors: self.colors.iter().map(|c| c.as_ref().map(&fun)).collect(),
resolves: self.resolves.iter().map(&fun).collect(),
depth_stencil: self.depth_stencil.as_ref().map(&fun),
}
}
}
#[derive(Clone, Debug, Hash, PartialEq)] #[derive(Clone, Debug, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]

View File

@ -179,13 +179,13 @@ pub enum Command {
InsertDebugMarker(String), InsertDebugMarker(String),
RunComputePass { RunComputePass {
base: crate::command::BasePass<crate::command::ComputeCommand>, base: crate::command::BasePass<crate::command::ComputeCommand>,
timestamp_writes: Option<crate::command::ComputePassTimestampWrites>, timestamp_writes: Option<crate::command::PassTimestampWrites>,
}, },
RunRenderPass { RunRenderPass {
base: crate::command::BasePass<crate::command::RenderCommand>, base: crate::command::BasePass<crate::command::RenderCommand>,
target_colors: Vec<Option<crate::command::RenderPassColorAttachment>>, target_colors: Vec<Option<crate::command::RenderPassColorAttachment>>,
target_depth_stencil: Option<crate::command::RenderPassDepthStencilAttachment>, target_depth_stencil: Option<crate::command::RenderPassDepthStencilAttachment>,
timestamp_writes: Option<crate::command::RenderPassTimestampWrites>, timestamp_writes: Option<crate::command::PassTimestampWrites>,
occlusion_query_set_id: Option<id::QuerySetId>, occlusion_query_set_id: Option<id::QuerySetId>,
}, },
} }

View File

@ -2570,7 +2570,7 @@ impl crate::context::Context for ContextWebGpu {
&self, &self,
_encoder: &Self::CommandEncoderId, _encoder: &Self::CommandEncoderId,
encoder_data: &Self::CommandEncoderData, encoder_data: &Self::CommandEncoderData,
desc: &crate::RenderPassDescriptor<'_, '_>, desc: &crate::RenderPassDescriptor<'_>,
) -> (Self::RenderPassId, Self::RenderPassData) { ) -> (Self::RenderPassId, Self::RenderPassData) {
let mapped_color_attachments = desc let mapped_color_attachments = desc
.color_attachments .color_attachments

View File

@ -490,7 +490,7 @@ pub struct ComputePass {
#[derive(Debug)] #[derive(Debug)]
pub struct RenderPass { pub struct RenderPass {
pass: wgc::command::RenderPass, pass: Box<dyn wgc::command::DynRenderPass>,
error_sink: ErrorSink, error_sink: ErrorSink,
} }
@ -1913,7 +1913,7 @@ impl crate::Context for ContextWgpuCore {
let timestamp_writes = let timestamp_writes =
desc.timestamp_writes desc.timestamp_writes
.as_ref() .as_ref()
.map(|tw| wgc::command::ComputePassTimestampWrites { .map(|tw| wgc::command::PassTimestampWrites {
query_set: tw.query_set.id.into(), query_set: tw.query_set.id.into(),
beginning_of_pass_write_index: tw.beginning_of_pass_write_index, beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
end_of_pass_write_index: tw.end_of_pass_write_index, end_of_pass_write_index: tw.end_of_pass_write_index,
@ -1947,7 +1947,7 @@ impl crate::Context for ContextWgpuCore {
&self, &self,
encoder: &Self::CommandEncoderId, encoder: &Self::CommandEncoderId,
encoder_data: &Self::CommandEncoderData, encoder_data: &Self::CommandEncoderData,
desc: &crate::RenderPassDescriptor<'_, '_>, desc: &crate::RenderPassDescriptor<'_>,
) -> (Self::RenderPassId, Self::RenderPassData) { ) -> (Self::RenderPassId, Self::RenderPassData) {
if desc.color_attachments.len() > wgc::MAX_COLOR_ATTACHMENTS { if desc.color_attachments.len() > wgc::MAX_COLOR_ATTACHMENTS {
self.handle_error_fatal( self.handle_error_fatal(
@ -1982,27 +1982,34 @@ impl crate::Context for ContextWgpuCore {
let timestamp_writes = let timestamp_writes =
desc.timestamp_writes desc.timestamp_writes
.as_ref() .as_ref()
.map(|tw| wgc::command::RenderPassTimestampWrites { .map(|tw| wgc::command::PassTimestampWrites {
query_set: tw.query_set.id.into(), query_set: tw.query_set.id.into(),
beginning_of_pass_write_index: tw.beginning_of_pass_write_index, beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
end_of_pass_write_index: tw.end_of_pass_write_index, end_of_pass_write_index: tw.end_of_pass_write_index,
}); });
let (pass, err) = gfx_select!(encoder => self.0.command_encoder_create_render_pass_dyn(*encoder, &wgc::command::RenderPassDescriptor {
label: desc.label.map(Borrowed),
timestamp_writes: timestamp_writes.as_ref(),
color_attachments: std::borrow::Cow::Borrowed(&colors),
depth_stencil_attachment: depth_stencil.as_ref(),
occlusion_query_set: desc.occlusion_query_set.map(|query_set| query_set.id.into()),
}));
if let Some(cause) = err {
self.handle_error(
&encoder_data.error_sink,
cause,
LABEL,
desc.label,
"CommandEncoder::begin_compute_pass",
);
}
( (
Unused, Unused,
RenderPass { Self::RenderPassData {
pass: wgc::command::RenderPass::new( pass,
*encoder,
&wgc::command::RenderPassDescriptor {
label: desc.label.map(Borrowed),
color_attachments: Borrowed(&colors),
depth_stencil_attachment: depth_stencil.as_ref(),
timestamp_writes: timestamp_writes.as_ref(),
occlusion_query_set: desc
.occlusion_query_set
.map(|query_set| query_set.id.into()),
},
),
error_sink: encoder_data.error_sink.clone(), error_sink: encoder_data.error_sink.clone(),
}, },
) )
@ -2438,7 +2445,7 @@ impl crate::Context for ContextWgpuCore {
offset: u32, offset: u32,
data: &[u8], data: &[u8],
) { ) {
if let Err(cause) = pass_data.pass.set_push_constant(&self.0, offset, data) { if let Err(cause) = pass_data.pass.set_push_constants(&self.0, offset, data) {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -2810,10 +2817,7 @@ impl crate::Context for ContextWgpuCore {
pipeline: &Self::RenderPipelineId, pipeline: &Self::RenderPipelineId,
_pipeline_data: &Self::RenderPipelineData, _pipeline_data: &Self::RenderPipelineData,
) { ) {
if let Err(cause) = self if let Err(cause) = pass_data.pass.set_pipeline(&self.0, *pipeline) {
.0
.render_pass_set_pipeline(&mut pass_data.pass, *pipeline)
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -2833,9 +2837,9 @@ impl crate::Context for ContextWgpuCore {
_bind_group_data: &Self::BindGroupData, _bind_group_data: &Self::BindGroupData,
offsets: &[wgt::DynamicOffset], offsets: &[wgt::DynamicOffset],
) { ) {
if let Err(cause) = if let Err(cause) = pass_data
self.0 .pass
.render_pass_set_bind_group(&mut pass_data.pass, index, *bind_group, offsets) .set_bind_group(&self.0, index, *bind_group, offsets)
{ {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
@ -2857,13 +2861,11 @@ impl crate::Context for ContextWgpuCore {
offset: wgt::BufferAddress, offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>, size: Option<wgt::BufferSize>,
) { ) {
if let Err(cause) = self.0.render_pass_set_index_buffer( if let Err(cause) =
&mut pass_data.pass, pass_data
*buffer, .pass
index_format, .set_index_buffer(&self.0, *buffer, index_format, offset, size)
offset, {
size,
) {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -2884,9 +2886,9 @@ impl crate::Context for ContextWgpuCore {
offset: wgt::BufferAddress, offset: wgt::BufferAddress,
size: Option<wgt::BufferSize>, size: Option<wgt::BufferSize>,
) { ) {
if let Err(cause) = if let Err(cause) = pass_data
self.0 .pass
.render_pass_set_vertex_buffer(&mut pass_data.pass, slot, *buffer, offset, size) .set_vertex_buffer(&self.0, slot, *buffer, offset, size)
{ {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
@ -2906,9 +2908,9 @@ impl crate::Context for ContextWgpuCore {
offset: u32, offset: u32,
data: &[u8], data: &[u8],
) { ) {
if let Err(cause) = if let Err(cause) = pass_data
self.0 .pass
.render_pass_set_push_constants(&mut pass_data.pass, stages, offset, data) .set_push_constants(&self.0, stages, offset, data)
{ {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
@ -2927,8 +2929,8 @@ impl crate::Context for ContextWgpuCore {
vertices: Range<u32>, vertices: Range<u32>,
instances: Range<u32>, instances: Range<u32>,
) { ) {
if let Err(cause) = self.0.render_pass_draw( if let Err(cause) = pass_data.pass.draw(
&mut pass_data.pass, &self.0,
vertices.end - vertices.start, vertices.end - vertices.start,
instances.end - instances.start, instances.end - instances.start,
vertices.start, vertices.start,
@ -2952,8 +2954,8 @@ impl crate::Context for ContextWgpuCore {
base_vertex: i32, base_vertex: i32,
instances: Range<u32>, instances: Range<u32>,
) { ) {
if let Err(cause) = self.0.render_pass_draw_indexed( if let Err(cause) = pass_data.pass.draw_indexed(
&mut pass_data.pass, &self.0,
indices.end - indices.start, indices.end - indices.start,
instances.end - instances.start, instances.end - instances.start,
indices.start, indices.start,
@ -2978,9 +2980,9 @@ impl crate::Context for ContextWgpuCore {
_indirect_buffer_data: &Self::BufferData, _indirect_buffer_data: &Self::BufferData,
indirect_offset: wgt::BufferAddress, indirect_offset: wgt::BufferAddress,
) { ) {
if let Err(cause) = if let Err(cause) = pass_data
self.0 .pass
.render_pass_draw_indirect(&mut pass_data.pass, *indirect_buffer, indirect_offset) .draw_indirect(&self.0, *indirect_buffer, indirect_offset)
{ {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
@ -3000,11 +3002,11 @@ impl crate::Context for ContextWgpuCore {
_indirect_buffer_data: &Self::BufferData, _indirect_buffer_data: &Self::BufferData,
indirect_offset: wgt::BufferAddress, indirect_offset: wgt::BufferAddress,
) { ) {
if let Err(cause) = self.0.render_pass_draw_indexed_indirect( if let Err(cause) =
&mut pass_data.pass, pass_data
*indirect_buffer, .pass
indirect_offset, .draw_indexed_indirect(&self.0, *indirect_buffer, indirect_offset)
) { {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3024,12 +3026,11 @@ impl crate::Context for ContextWgpuCore {
indirect_offset: wgt::BufferAddress, indirect_offset: wgt::BufferAddress,
count: u32, count: u32,
) { ) {
if let Err(cause) = self.0.render_pass_multi_draw_indirect( if let Err(cause) =
&mut pass_data.pass, pass_data
*indirect_buffer, .pass
indirect_offset, .multi_draw_indirect(&self.0, *indirect_buffer, indirect_offset, count)
count, {
) {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3049,8 +3050,8 @@ impl crate::Context for ContextWgpuCore {
indirect_offset: wgt::BufferAddress, indirect_offset: wgt::BufferAddress,
count: u32, count: u32,
) { ) {
if let Err(cause) = self.0.render_pass_multi_draw_indexed_indirect( if let Err(cause) = pass_data.pass.multi_draw_indexed_indirect(
&mut pass_data.pass, &self.0,
*indirect_buffer, *indirect_buffer,
indirect_offset, indirect_offset,
count, count,
@ -3077,8 +3078,8 @@ impl crate::Context for ContextWgpuCore {
count_buffer_offset: wgt::BufferAddress, count_buffer_offset: wgt::BufferAddress,
max_count: u32, max_count: u32,
) { ) {
if let Err(cause) = self.0.render_pass_multi_draw_indirect_count( if let Err(cause) = pass_data.pass.multi_draw_indirect_count(
&mut pass_data.pass, &self.0,
*indirect_buffer, *indirect_buffer,
indirect_offset, indirect_offset,
*count_buffer, *count_buffer,
@ -3107,8 +3108,8 @@ impl crate::Context for ContextWgpuCore {
count_buffer_offset: wgt::BufferAddress, count_buffer_offset: wgt::BufferAddress,
max_count: u32, max_count: u32,
) { ) {
if let Err(cause) = self.0.render_pass_multi_draw_indexed_indirect_count( if let Err(cause) = pass_data.pass.multi_draw_indexed_indirect_count(
&mut pass_data.pass, &self.0,
*indirect_buffer, *indirect_buffer,
indirect_offset, indirect_offset,
*count_buffer, *count_buffer,
@ -3131,10 +3132,7 @@ impl crate::Context for ContextWgpuCore {
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
color: wgt::Color, color: wgt::Color,
) { ) {
if let Err(cause) = self if let Err(cause) = pass_data.pass.set_blend_constant(&self.0, color) {
.0
.render_pass_set_blend_constant(&mut pass_data.pass, &color)
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3154,9 +3152,9 @@ impl crate::Context for ContextWgpuCore {
width: u32, width: u32,
height: u32, height: u32,
) { ) {
if let Err(cause) = if let Err(cause) = pass_data
self.0 .pass
.render_pass_set_scissor_rect(&mut pass_data.pass, x, y, width, height) .set_scissor_rect(&self.0, x, y, width, height)
{ {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
@ -3179,15 +3177,10 @@ impl crate::Context for ContextWgpuCore {
min_depth: f32, min_depth: f32,
max_depth: f32, max_depth: f32,
) { ) {
if let Err(cause) = self.0.render_pass_set_viewport( if let Err(cause) = pass_data
&mut pass_data.pass, .pass
x, .set_viewport(&self.0, x, y, width, height, min_depth, max_depth)
y, {
width,
height,
min_depth,
max_depth,
) {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3204,10 +3197,7 @@ impl crate::Context for ContextWgpuCore {
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
reference: u32, reference: u32,
) { ) {
if let Err(cause) = self if let Err(cause) = pass_data.pass.set_stencil_reference(&self.0, reference) {
.0
.render_pass_set_stencil_reference(&mut pass_data.pass, reference)
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3224,10 +3214,7 @@ impl crate::Context for ContextWgpuCore {
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
label: &str, label: &str,
) { ) {
if let Err(cause) = self if let Err(cause) = pass_data.pass.insert_debug_marker(&self.0, label, 0) {
.0
.render_pass_insert_debug_marker(&mut pass_data.pass, label, 0)
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3244,10 +3231,7 @@ impl crate::Context for ContextWgpuCore {
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
group_label: &str, group_label: &str,
) { ) {
if let Err(cause) = self if let Err(cause) = pass_data.pass.push_debug_group(&self.0, group_label, 0) {
.0
.render_pass_push_debug_group(&mut pass_data.pass, group_label, 0)
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3263,7 +3247,7 @@ impl crate::Context for ContextWgpuCore {
_pass: &mut Self::RenderPassId, _pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
) { ) {
if let Err(cause) = self.0.render_pass_pop_debug_group(&mut pass_data.pass) { if let Err(cause) = pass_data.pass.pop_debug_group(&self.0) {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3282,9 +3266,9 @@ impl crate::Context for ContextWgpuCore {
_query_set_data: &Self::QuerySetData, _query_set_data: &Self::QuerySetData,
query_index: u32, query_index: u32,
) { ) {
if let Err(cause) = if let Err(cause) = pass_data
self.0 .pass
.render_pass_write_timestamp(&mut pass_data.pass, *query_set, query_index) .write_timestamp(&self.0, *query_set, query_index)
{ {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
@ -3302,10 +3286,7 @@ impl crate::Context for ContextWgpuCore {
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
query_index: u32, query_index: u32,
) { ) {
if let Err(cause) = self if let Err(cause) = pass_data.pass.begin_occlusion_query(&self.0, query_index) {
.0
.render_pass_begin_occlusion_query(&mut pass_data.pass, query_index)
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3321,7 +3302,7 @@ impl crate::Context for ContextWgpuCore {
_pass: &mut Self::RenderPassId, _pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
) { ) {
if let Err(cause) = self.0.render_pass_end_occlusion_query(&mut pass_data.pass) { if let Err(cause) = pass_data.pass.end_occlusion_query(&self.0) {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3340,11 +3321,11 @@ impl crate::Context for ContextWgpuCore {
_query_set_data: &Self::QuerySetData, _query_set_data: &Self::QuerySetData,
query_index: u32, query_index: u32,
) { ) {
if let Err(cause) = self.0.render_pass_begin_pipeline_statistics_query( if let Err(cause) =
&mut pass_data.pass, pass_data
*query_set, .pass
query_index, .begin_pipeline_statistics_query(&self.0, *query_set, query_index)
) { {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3360,10 +3341,7 @@ impl crate::Context for ContextWgpuCore {
_pass: &mut Self::RenderPassId, _pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
) { ) {
if let Err(cause) = self if let Err(cause) = pass_data.pass.end_pipeline_statistics_query(&self.0) {
.0
.render_pass_end_pipeline_statistics_query(&mut pass_data.pass)
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,
@ -3381,9 +3359,9 @@ impl crate::Context for ContextWgpuCore {
render_bundles: &mut dyn Iterator<Item = (Self::RenderBundleId, &Self::RenderBundleData)>, render_bundles: &mut dyn Iterator<Item = (Self::RenderBundleId, &Self::RenderBundleData)>,
) { ) {
let temp_render_bundles = render_bundles.map(|(i, _)| i).collect::<SmallVec<[_; 4]>>(); let temp_render_bundles = render_bundles.map(|(i, _)| i).collect::<SmallVec<[_; 4]>>();
if let Err(cause) = self if let Err(cause) = pass_data
.0 .pass
.render_pass_execute_bundles(&mut pass_data.pass, &temp_render_bundles) .execute_bundles(&self.0, &temp_render_bundles)
{ {
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
@ -3400,9 +3378,7 @@ impl crate::Context for ContextWgpuCore {
_pass: &mut Self::RenderPassId, _pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData, pass_data: &mut Self::RenderPassData,
) { ) {
let encoder = pass_data.pass.parent_id(); if let Err(cause) = pass_data.pass.end(&self.0) {
if let Err(cause) = wgc::gfx_select!(encoder => self.0.render_pass_end(&mut pass_data.pass))
{
self.handle_error( self.handle_error(
&pass_data.error_sink, &pass_data.error_sink,
cause, cause,

View File

@ -470,7 +470,7 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
&self, &self,
encoder: &Self::CommandEncoderId, encoder: &Self::CommandEncoderId,
encoder_data: &Self::CommandEncoderData, encoder_data: &Self::CommandEncoderData,
desc: &RenderPassDescriptor<'_, '_>, desc: &RenderPassDescriptor<'_>,
) -> (Self::RenderPassId, Self::RenderPassData); ) -> (Self::RenderPassId, Self::RenderPassData);
fn command_encoder_finish( fn command_encoder_finish(
&self, &self,
@ -1477,7 +1477,7 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
&self, &self,
encoder: &ObjectId, encoder: &ObjectId,
encoder_data: &crate::Data, encoder_data: &crate::Data,
desc: &RenderPassDescriptor<'_, '_>, desc: &RenderPassDescriptor<'_>,
) -> (ObjectId, Box<crate::Data>); ) -> (ObjectId, Box<crate::Data>);
fn command_encoder_finish( fn command_encoder_finish(
&self, &self,
@ -2799,7 +2799,7 @@ where
&self, &self,
encoder: &ObjectId, encoder: &ObjectId,
encoder_data: &crate::Data, encoder_data: &crate::Data,
desc: &RenderPassDescriptor<'_, '_>, desc: &RenderPassDescriptor<'_>,
) -> (ObjectId, Box<crate::Data>) { ) -> (ObjectId, Box<crate::Data>) {
let encoder = <T::CommandEncoderId>::from(*encoder); let encoder = <T::CommandEncoderId>::from(*encoder);
let encoder_data = downcast_ref(encoder_data); let encoder_data = downcast_ref(encoder_data);

View File

@ -1273,10 +1273,20 @@ impl Drop for CommandEncoder {
/// Corresponds to [WebGPU `GPURenderPassEncoder`]( /// Corresponds to [WebGPU `GPURenderPassEncoder`](
/// https://gpuweb.github.io/gpuweb/#render-pass-encoder). /// https://gpuweb.github.io/gpuweb/#render-pass-encoder).
#[derive(Debug)] #[derive(Debug)]
pub struct RenderPass<'a> { pub struct RenderPass<'encoder> {
/// The inner data of the render pass, separated out so it's easy to replace the lifetime with 'static if desired.
inner: RenderPassInner,
/// This lifetime is used to protect the [`CommandEncoder`] from being used
/// while the pass is alive.
encoder_guard: PhantomData<&'encoder ()>,
}
#[derive(Debug)]
struct RenderPassInner {
id: ObjectId, id: ObjectId,
data: Box<Data>, data: Box<Data>,
parent: &'a mut CommandEncoder, context: Arc<C>,
} }
/// In-progress recording of a compute pass. /// In-progress recording of a compute pass.
@ -1825,28 +1835,25 @@ static_assertions::assert_impl_all!(BindGroupDescriptor<'_>: Send, Sync);
/// ///
/// For use with [`CommandEncoder::begin_render_pass`]. /// For use with [`CommandEncoder::begin_render_pass`].
/// ///
/// Note: separate lifetimes are needed because the texture views (`'tex`)
/// have to live as long as the pass is recorded, while everything else (`'desc`) doesn't.
///
/// Corresponds to [WebGPU `GPURenderPassDescriptor`]( /// Corresponds to [WebGPU `GPURenderPassDescriptor`](
/// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderpassdescriptor). /// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderpassdescriptor).
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct RenderPassDescriptor<'tex, 'desc> { pub struct RenderPassDescriptor<'a> {
/// Debug label of the render pass. This will show up in graphics debuggers for easy identification. /// Debug label of the render pass. This will show up in graphics debuggers for easy identification.
pub label: Label<'desc>, pub label: Label<'a>,
/// The color attachments of the render pass. /// The color attachments of the render pass.
pub color_attachments: &'desc [Option<RenderPassColorAttachment<'tex>>], pub color_attachments: &'a [Option<RenderPassColorAttachment<'a>>],
/// The depth and stencil attachment of the render pass, if any. /// The depth and stencil attachment of the render pass, if any.
pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<'tex>>, pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<'a>>,
/// Defines which timestamp values will be written for this pass, and where to write them to. /// Defines which timestamp values will be written for this pass, and where to write them to.
/// ///
/// Requires [`Features::TIMESTAMP_QUERY`] to be enabled. /// Requires [`Features::TIMESTAMP_QUERY`] to be enabled.
pub timestamp_writes: Option<RenderPassTimestampWrites<'desc>>, pub timestamp_writes: Option<RenderPassTimestampWrites<'a>>,
/// Defines where the occlusion query results will be stored for this pass. /// Defines where the occlusion query results will be stored for this pass.
pub occlusion_query_set: Option<&'tex QuerySet>, pub occlusion_query_set: Option<&'a QuerySet>,
} }
#[cfg(send_sync)] #[cfg(send_sync)]
static_assertions::assert_impl_all!(RenderPassDescriptor<'_, '_>: Send, Sync); static_assertions::assert_impl_all!(RenderPassDescriptor<'_>: Send, Sync);
/// Describes how the vertex buffer is interpreted. /// Describes how the vertex buffer is interpreted.
/// ///
@ -3886,16 +3893,17 @@ impl CommandEncoder {
/// Begins recording of a render pass. /// Begins recording of a render pass.
/// ///
/// This function returns a [`RenderPass`] object which records a single render pass. /// This function returns a [`RenderPass`] object which records a single render pass.
// ///
// TODO(https://github.com/gfx-rs/wgpu/issues/1453): /// As long as the returned [`RenderPass`] has not ended,
// Just like with compute passes, we should have a way to opt out of the lifetime constraint. /// any mutating operation on this command encoder causes an error and invalidates it.
// See https://github.com/gfx-rs/wgpu/pull/5768 for details /// Note that the `'encoder` lifetime relationship protects against this,
// Once this is done, the documentation for `begin_render_pass` and `begin_compute_pass` should /// but it is possible to opt out of it by calling [`RenderPass::forget_lifetime`].
// be nearly identical. /// This can be useful for runtime handling of the encoder->pass
pub fn begin_render_pass<'pass>( /// dependency e.g. when pass and encoder are stored in the same data structure.
&'pass mut self, pub fn begin_render_pass<'encoder>(
desc: &RenderPassDescriptor<'pass, '_>, &'encoder mut self,
) -> RenderPass<'pass> { desc: &RenderPassDescriptor<'_>,
) -> RenderPass<'encoder> {
let id = self.id.as_ref().unwrap(); let id = self.id.as_ref().unwrap();
let (id, data) = DynContext::command_encoder_begin_render_pass( let (id, data) = DynContext::command_encoder_begin_render_pass(
&*self.context, &*self.context,
@ -3904,9 +3912,12 @@ impl CommandEncoder {
desc, desc,
); );
RenderPass { RenderPass {
inner: RenderPassInner {
id, id,
data, data,
parent: self, context: self.context.clone(),
},
encoder_guard: PhantomData,
} }
} }
@ -4177,7 +4188,26 @@ impl CommandEncoder {
} }
} }
impl<'a> RenderPass<'a> { impl<'encoder> RenderPass<'encoder> {
/// Drops the lifetime relationship to the parent command encoder, making usage of
/// the encoder while this pass is recorded a run-time error instead.
///
/// Attention: As long as the render pass has not been ended, any mutating operation on the parent
/// command encoder will cause a run-time error and invalidate it!
/// By default, the lifetime constraint prevents this, but it can be useful
/// to handle this at run time, such as when storing the pass and encoder in the same
/// data structure.
///
/// This operation has no effect on pass recording.
/// It's a safe operation, since [`CommandEncoder`] is in a locked state as long as the pass is active
/// regardless of the lifetime constraint or its absence.
pub fn forget_lifetime(self) -> RenderPass<'static> {
RenderPass {
inner: self.inner,
encoder_guard: PhantomData,
}
}
/// Sets the active bind group for a given bind group index. The bind group layout /// Sets the active bind group for a given bind group index. The bind group layout
/// in the active pipeline when any `draw_*()` method is called must match the layout of /// in the active pipeline when any `draw_*()` method is called must match the layout of
/// this bind group. /// this bind group.
@ -4190,13 +4220,13 @@ impl<'a> RenderPass<'a> {
pub fn set_bind_group( pub fn set_bind_group(
&mut self, &mut self,
index: u32, index: u32,
bind_group: &'a BindGroup, bind_group: &BindGroup,
offsets: &[DynamicOffset], offsets: &[DynamicOffset],
) { ) {
DynContext::render_pass_set_bind_group( DynContext::render_pass_set_bind_group(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
index, index,
&bind_group.id, &bind_group.id,
bind_group.data.as_ref(), bind_group.data.as_ref(),
@ -4207,11 +4237,11 @@ impl<'a> RenderPass<'a> {
/// Sets the active render pipeline. /// Sets the active render pipeline.
/// ///
/// Subsequent draw calls will exhibit the behavior defined by `pipeline`. /// Subsequent draw calls will exhibit the behavior defined by `pipeline`.
pub fn set_pipeline(&mut self, pipeline: &'a RenderPipeline) { pub fn set_pipeline(&mut self, pipeline: &RenderPipeline) {
DynContext::render_pass_set_pipeline( DynContext::render_pass_set_pipeline(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&pipeline.id, &pipeline.id,
pipeline.data.as_ref(), pipeline.data.as_ref(),
) )
@ -4224,9 +4254,9 @@ impl<'a> RenderPass<'a> {
/// (all components zero). /// (all components zero).
pub fn set_blend_constant(&mut self, color: Color) { pub fn set_blend_constant(&mut self, color: Color) {
DynContext::render_pass_set_blend_constant( DynContext::render_pass_set_blend_constant(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
color, color,
) )
} }
@ -4235,11 +4265,11 @@ impl<'a> RenderPass<'a> {
/// ///
/// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will /// Subsequent calls to [`draw_indexed`](RenderPass::draw_indexed) on this [`RenderPass`] will
/// use `buffer` as the source index buffer. /// use `buffer` as the source index buffer.
pub fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'a>, index_format: IndexFormat) { pub fn set_index_buffer(&mut self, buffer_slice: BufferSlice<'_>, index_format: IndexFormat) {
DynContext::render_pass_set_index_buffer( DynContext::render_pass_set_index_buffer(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&buffer_slice.buffer.id, &buffer_slice.buffer.id,
buffer_slice.buffer.data.as_ref(), buffer_slice.buffer.data.as_ref(),
index_format, index_format,
@ -4258,11 +4288,11 @@ impl<'a> RenderPass<'a> {
/// ///
/// [`draw`]: RenderPass::draw /// [`draw`]: RenderPass::draw
/// [`draw_indexed`]: RenderPass::draw_indexed /// [`draw_indexed`]: RenderPass::draw_indexed
pub fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'a>) { pub fn set_vertex_buffer(&mut self, slot: u32, buffer_slice: BufferSlice<'_>) {
DynContext::render_pass_set_vertex_buffer( DynContext::render_pass_set_vertex_buffer(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
slot, slot,
&buffer_slice.buffer.id, &buffer_slice.buffer.id,
buffer_slice.buffer.data.as_ref(), buffer_slice.buffer.data.as_ref(),
@ -4282,9 +4312,9 @@ impl<'a> RenderPass<'a> {
/// but it does not affect the coordinate system, only which fragments are discarded. /// but it does not affect the coordinate system, only which fragments are discarded.
pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) { pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {
DynContext::render_pass_set_scissor_rect( DynContext::render_pass_set_scissor_rect(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
x, x,
y, y,
width, width,
@ -4300,9 +4330,9 @@ impl<'a> RenderPass<'a> {
/// targets. /// targets.
pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) { pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) {
DynContext::render_pass_set_viewport( DynContext::render_pass_set_viewport(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
x, x,
y, y,
w, w,
@ -4318,9 +4348,9 @@ impl<'a> RenderPass<'a> {
/// If this method has not been called, the stencil reference value defaults to `0`. /// If this method has not been called, the stencil reference value defaults to `0`.
pub fn set_stencil_reference(&mut self, reference: u32) { pub fn set_stencil_reference(&mut self, reference: u32) {
DynContext::render_pass_set_stencil_reference( DynContext::render_pass_set_stencil_reference(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
reference, reference,
); );
} }
@ -4328,9 +4358,9 @@ impl<'a> RenderPass<'a> {
/// Inserts debug marker. /// Inserts debug marker.
pub fn insert_debug_marker(&mut self, label: &str) { pub fn insert_debug_marker(&mut self, label: &str) {
DynContext::render_pass_insert_debug_marker( DynContext::render_pass_insert_debug_marker(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
label, label,
); );
} }
@ -4338,9 +4368,9 @@ impl<'a> RenderPass<'a> {
/// Start record commands and group it into debug marker group. /// Start record commands and group it into debug marker group.
pub fn push_debug_group(&mut self, label: &str) { pub fn push_debug_group(&mut self, label: &str) {
DynContext::render_pass_push_debug_group( DynContext::render_pass_push_debug_group(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
label, label,
); );
} }
@ -4348,9 +4378,9 @@ impl<'a> RenderPass<'a> {
/// Stops command recording and creates debug group. /// Stops command recording and creates debug group.
pub fn pop_debug_group(&mut self) { pub fn pop_debug_group(&mut self) {
DynContext::render_pass_pop_debug_group( DynContext::render_pass_pop_debug_group(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
); );
} }
@ -4377,9 +4407,9 @@ impl<'a> RenderPass<'a> {
/// It is not affected by changes to the state that are performed after it is called. /// It is not affected by changes to the state that are performed after it is called.
pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) { pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
DynContext::render_pass_draw( DynContext::render_pass_draw(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
vertices, vertices,
instances, instances,
) )
@ -4411,9 +4441,9 @@ impl<'a> RenderPass<'a> {
/// It is not affected by changes to the state that are performed after it is called. /// It is not affected by changes to the state that are performed after it is called.
pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) { pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
DynContext::render_pass_draw_indexed( DynContext::render_pass_draw_indexed(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
indices, indices,
base_vertex, base_vertex,
instances, instances,
@ -4433,11 +4463,11 @@ impl<'a> RenderPass<'a> {
/// any use of `@builtin(vertex_index)` or `@builtin(instance_index)` in the vertex shader will have different values. /// any use of `@builtin(vertex_index)` or `@builtin(instance_index)` in the vertex shader will have different values.
/// ///
/// See details on the individual flags for more information. /// See details on the individual flags for more information.
pub fn draw_indirect(&mut self, indirect_buffer: &'a Buffer, indirect_offset: BufferAddress) { pub fn draw_indirect(&mut self, indirect_buffer: &Buffer, indirect_offset: BufferAddress) {
DynContext::render_pass_draw_indirect( DynContext::render_pass_draw_indirect(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&indirect_buffer.id, &indirect_buffer.id,
indirect_buffer.data.as_ref(), indirect_buffer.data.as_ref(),
indirect_offset, indirect_offset,
@ -4460,13 +4490,13 @@ impl<'a> RenderPass<'a> {
/// See details on the individual flags for more information. /// See details on the individual flags for more information.
pub fn draw_indexed_indirect( pub fn draw_indexed_indirect(
&mut self, &mut self,
indirect_buffer: &'a Buffer, indirect_buffer: &Buffer,
indirect_offset: BufferAddress, indirect_offset: BufferAddress,
) { ) {
DynContext::render_pass_draw_indexed_indirect( DynContext::render_pass_draw_indexed_indirect(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&indirect_buffer.id, &indirect_buffer.id,
indirect_buffer.data.as_ref(), indirect_buffer.data.as_ref(),
indirect_offset, indirect_offset,
@ -4478,22 +4508,25 @@ impl<'a> RenderPass<'a> {
/// ///
/// Commands in the bundle do not inherit this render pass's current render state, and after the /// Commands in the bundle do not inherit this render pass's current render state, and after the
/// bundle has executed, the state is **cleared** (reset to defaults, not the previous state). /// bundle has executed, the state is **cleared** (reset to defaults, not the previous state).
pub fn execute_bundles<I: IntoIterator<Item = &'a RenderBundle>>(&mut self, render_bundles: I) { pub fn execute_bundles<'a, I: IntoIterator<Item = &'a RenderBundle>>(
&mut self,
render_bundles: I,
) {
let mut render_bundles = render_bundles let mut render_bundles = render_bundles
.into_iter() .into_iter()
.map(|rb| (&rb.id, rb.data.as_ref())); .map(|rb| (&rb.id, rb.data.as_ref()));
DynContext::render_pass_execute_bundles( DynContext::render_pass_execute_bundles(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&mut render_bundles, &mut render_bundles,
) )
} }
} }
/// [`Features::MULTI_DRAW_INDIRECT`] must be enabled on the device in order to call these functions. /// [`Features::MULTI_DRAW_INDIRECT`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> { impl<'encoder> RenderPass<'encoder> {
/// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`. /// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`.
/// `count` draw calls are issued. /// `count` draw calls are issued.
/// ///
@ -4506,14 +4539,14 @@ impl<'a> RenderPass<'a> {
/// It is not affected by changes to the state that are performed after it is called. /// It is not affected by changes to the state that are performed after it is called.
pub fn multi_draw_indirect( pub fn multi_draw_indirect(
&mut self, &mut self,
indirect_buffer: &'a Buffer, indirect_buffer: &Buffer,
indirect_offset: BufferAddress, indirect_offset: BufferAddress,
count: u32, count: u32,
) { ) {
DynContext::render_pass_multi_draw_indirect( DynContext::render_pass_multi_draw_indirect(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&indirect_buffer.id, &indirect_buffer.id,
indirect_buffer.data.as_ref(), indirect_buffer.data.as_ref(),
indirect_offset, indirect_offset,
@ -4534,14 +4567,14 @@ impl<'a> RenderPass<'a> {
/// It is not affected by changes to the state that are performed after it is called. /// It is not affected by changes to the state that are performed after it is called.
pub fn multi_draw_indexed_indirect( pub fn multi_draw_indexed_indirect(
&mut self, &mut self,
indirect_buffer: &'a Buffer, indirect_buffer: &Buffer,
indirect_offset: BufferAddress, indirect_offset: BufferAddress,
count: u32, count: u32,
) { ) {
DynContext::render_pass_multi_draw_indexed_indirect( DynContext::render_pass_multi_draw_indexed_indirect(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&indirect_buffer.id, &indirect_buffer.id,
indirect_buffer.data.as_ref(), indirect_buffer.data.as_ref(),
indirect_offset, indirect_offset,
@ -4551,7 +4584,7 @@ impl<'a> RenderPass<'a> {
} }
/// [`Features::MULTI_DRAW_INDIRECT_COUNT`] must be enabled on the device in order to call these functions. /// [`Features::MULTI_DRAW_INDIRECT_COUNT`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> { impl<'encoder> RenderPass<'encoder> {
/// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`. /// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`.
/// The count buffer is read to determine how many draws to issue. /// The count buffer is read to determine how many draws to issue.
/// ///
@ -4576,16 +4609,16 @@ impl<'a> RenderPass<'a> {
/// It is not affected by changes to the state that are performed after it is called. /// It is not affected by changes to the state that are performed after it is called.
pub fn multi_draw_indirect_count( pub fn multi_draw_indirect_count(
&mut self, &mut self,
indirect_buffer: &'a Buffer, indirect_buffer: &Buffer,
indirect_offset: BufferAddress, indirect_offset: BufferAddress,
count_buffer: &'a Buffer, count_buffer: &Buffer,
count_offset: BufferAddress, count_offset: BufferAddress,
max_count: u32, max_count: u32,
) { ) {
DynContext::render_pass_multi_draw_indirect_count( DynContext::render_pass_multi_draw_indirect_count(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&indirect_buffer.id, &indirect_buffer.id,
indirect_buffer.data.as_ref(), indirect_buffer.data.as_ref(),
indirect_offset, indirect_offset,
@ -4623,16 +4656,16 @@ impl<'a> RenderPass<'a> {
/// It is not affected by changes to the state that are performed after it is called. /// It is not affected by changes to the state that are performed after it is called.
pub fn multi_draw_indexed_indirect_count( pub fn multi_draw_indexed_indirect_count(
&mut self, &mut self,
indirect_buffer: &'a Buffer, indirect_buffer: &Buffer,
indirect_offset: BufferAddress, indirect_offset: BufferAddress,
count_buffer: &'a Buffer, count_buffer: &Buffer,
count_offset: BufferAddress, count_offset: BufferAddress,
max_count: u32, max_count: u32,
) { ) {
DynContext::render_pass_multi_draw_indexed_indirect_count( DynContext::render_pass_multi_draw_indexed_indirect_count(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&indirect_buffer.id, &indirect_buffer.id,
indirect_buffer.data.as_ref(), indirect_buffer.data.as_ref(),
indirect_offset, indirect_offset,
@ -4645,7 +4678,7 @@ impl<'a> RenderPass<'a> {
} }
/// [`Features::PUSH_CONSTANTS`] must be enabled on the device in order to call these functions. /// [`Features::PUSH_CONSTANTS`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> { impl<'encoder> RenderPass<'encoder> {
/// Set push constant data for subsequent draw calls. /// Set push constant data for subsequent draw calls.
/// ///
/// Write the bytes in `data` at offset `offset` within push constant /// Write the bytes in `data` at offset `offset` within push constant
@ -4688,9 +4721,9 @@ impl<'a> RenderPass<'a> {
/// [`PushConstant`]: https://docs.rs/naga/latest/naga/enum.StorageClass.html#variant.PushConstant /// [`PushConstant`]: https://docs.rs/naga/latest/naga/enum.StorageClass.html#variant.PushConstant
pub fn set_push_constants(&mut self, stages: ShaderStages, offset: u32, data: &[u8]) { pub fn set_push_constants(&mut self, stages: ShaderStages, offset: u32, data: &[u8]) {
DynContext::render_pass_set_push_constants( DynContext::render_pass_set_push_constants(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
stages, stages,
offset, offset,
data, data,
@ -4699,7 +4732,7 @@ impl<'a> RenderPass<'a> {
} }
/// [`Features::TIMESTAMP_QUERY_INSIDE_PASSES`] must be enabled on the device in order to call these functions. /// [`Features::TIMESTAMP_QUERY_INSIDE_PASSES`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> { impl<'encoder> RenderPass<'encoder> {
/// Issue a timestamp command at this point in the queue. The /// Issue a timestamp command at this point in the queue. The
/// timestamp will be written to the specified query set, at the specified index. /// timestamp will be written to the specified query set, at the specified index.
/// ///
@ -4709,9 +4742,9 @@ impl<'a> RenderPass<'a> {
/// for a string of operations to complete. /// for a string of operations to complete.
pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) { pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {
DynContext::render_pass_write_timestamp( DynContext::render_pass_write_timestamp(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&query_set.id, &query_set.id,
query_set.data.as_ref(), query_set.data.as_ref(),
query_index, query_index,
@ -4719,14 +4752,14 @@ impl<'a> RenderPass<'a> {
} }
} }
impl<'a> RenderPass<'a> { impl<'encoder> RenderPass<'encoder> {
/// Start a occlusion query on this render pass. It can be ended with /// Start a occlusion query on this render pass. It can be ended with
/// `end_occlusion_query`. Occlusion queries may not be nested. /// `end_occlusion_query`. Occlusion queries may not be nested.
pub fn begin_occlusion_query(&mut self, query_index: u32) { pub fn begin_occlusion_query(&mut self, query_index: u32) {
DynContext::render_pass_begin_occlusion_query( DynContext::render_pass_begin_occlusion_query(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
query_index, query_index,
); );
} }
@ -4735,22 +4768,22 @@ impl<'a> RenderPass<'a> {
/// `begin_occlusion_query`. Occlusion queries may not be nested. /// `begin_occlusion_query`. Occlusion queries may not be nested.
pub fn end_occlusion_query(&mut self) { pub fn end_occlusion_query(&mut self) {
DynContext::render_pass_end_occlusion_query( DynContext::render_pass_end_occlusion_query(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
); );
} }
} }
/// [`Features::PIPELINE_STATISTICS_QUERY`] must be enabled on the device in order to call these functions. /// [`Features::PIPELINE_STATISTICS_QUERY`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> { impl<'encoder> RenderPass<'encoder> {
/// Start a pipeline statistics query on this render pass. It can be ended with /// Start a pipeline statistics query on this render pass. It can be ended with
/// `end_pipeline_statistics_query`. Pipeline statistics queries may not be nested. /// `end_pipeline_statistics_query`. Pipeline statistics queries may not be nested.
pub fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, query_index: u32) { pub fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, query_index: u32) {
DynContext::render_pass_begin_pipeline_statistics_query( DynContext::render_pass_begin_pipeline_statistics_query(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
&query_set.id, &query_set.id,
query_set.data.as_ref(), query_set.data.as_ref(),
query_index, query_index,
@ -4761,18 +4794,17 @@ impl<'a> RenderPass<'a> {
/// `begin_pipeline_statistics_query`. Pipeline statistics queries may not be nested. /// `begin_pipeline_statistics_query`. Pipeline statistics queries may not be nested.
pub fn end_pipeline_statistics_query(&mut self) { pub fn end_pipeline_statistics_query(&mut self) {
DynContext::render_pass_end_pipeline_statistics_query( DynContext::render_pass_end_pipeline_statistics_query(
&*self.parent.context, &*self.inner.context,
&mut self.id, &mut self.inner.id,
self.data.as_mut(), self.inner.data.as_mut(),
); );
} }
} }
impl<'a> Drop for RenderPass<'a> { impl Drop for RenderPassInner {
fn drop(&mut self) { fn drop(&mut self) {
if !thread::panicking() { if !thread::panicking() {
self.parent self.context
.context
.render_pass_end(&mut self.id, self.data.as_mut()); .render_pass_end(&mut self.id, self.data.as_mut());
} }
} }