Add support for occlusion queries (#3402)

Co-authored-by: Leo Kettmeir <crowlkats@toaxl.com>
Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
Valaphee The Meerkat 2023-08-02 21:05:59 +02:00 committed by GitHub
parent 5b4c6b8ae4
commit 494ae1a815
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 454 additions and 5 deletions

View File

@ -40,6 +40,21 @@ Bottom level categories:
## Unreleased
### Major changes
#### Occlusion Query Support
The `occlusion_query_set` value defines where the occlusion query results will be stored for this pass.
```diff
let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
// ...
+ occlusion_query_set: None,
});
```
By @Valaphee in [#3402](https://github.com/gfx-rs/wgpu/pull/3402)
### Changes
- Omit texture store bound checks since they are no-ops if out of bounds on all APIs. By @teoxoy in [#3975](https://github.com/gfx-rs/wgpu/pull/3975)

View File

@ -98,6 +98,7 @@ pub fn op_webgpu_command_encoder_begin_render_pass(
label: Option<String>,
color_attachments: Vec<Option<GpuRenderPassColorAttachment>>,
depth_stencil_attachment: Option<GpuRenderPassDepthStencilAttachment>,
occlusion_query_set: Option<ResourceId>,
) -> Result<WebGpuResult, AnyError> {
let command_encoder_resource = state
.resource_table
@ -171,10 +172,16 @@ pub fn op_webgpu_command_encoder_begin_render_pass(
});
}
let occlusion_query_set_resource = occlusion_query_set
.map(|rid| state.resource_table.get::<super::WebGpuQuerySet>(rid))
.transpose()?
.map(|query_set| query_set.1);
let descriptor = wgpu_core::command::RenderPassDescriptor {
label: label.map(Cow::from),
color_attachments: Cow::from(color_attachments),
depth_stencil_attachment: processed_depth_stencil_attachment.as_ref(),
occlusion_query_set: occlusion_query_set_resource,
};
let render_pass = wgpu_core::command::RenderPass::new(command_encoder_resource.1, &descriptor);

View File

@ -114,6 +114,40 @@ pub fn op_webgpu_render_pass_set_stencil_reference(
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_begin_occlusion_query(
state: &mut OpState,
render_pass_rid: ResourceId,
query_index: u32,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_begin_occlusion_query(
&mut render_pass_resource.0.borrow_mut(),
query_index,
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_end_occlusion_query(
state: &mut OpState,
render_pass_rid: ResourceId,
) -> Result<WebGpuResult, AnyError> {
let render_pass_resource = state
.resource_table
.get::<WebGpuRenderPass>(render_pass_rid)?;
wgpu_core::command::render_ffi::wgpu_render_pass_end_occlusion_query(
&mut render_pass_resource.0.borrow_mut(),
);
Ok(WebGpuResult::empty())
}
#[op]
pub fn op_webgpu_render_pass_begin_pipeline_statistics_query(
state: &mut OpState,

View File

@ -981,6 +981,7 @@ dictionary GPURenderPassDescriptor
: GPUObjectDescriptorBase {
required sequence<GPURenderPassColorAttachment?> colorAttachments;
GPURenderPassDepthStencilAttachment depthStencilAttachment;
GPUQuerySet occlusionQuerySet;
};
dictionary GPURenderPassColorAttachment {

View File

@ -283,6 +283,7 @@ impl wgpu_example::framework::Example for Example {
label: None,
color_attachments: &color_attachments,
depth_stencil_attachment: None,
occlusion_query_set: None,
};
// get command encoder

View File

@ -336,6 +336,7 @@ impl wgpu_example::framework::Example for Example {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&self.pipeline);
rpass.set_bind_group(0, &self.global_group, &[]);

View File

@ -104,6 +104,7 @@ async fn create_red_image_with_dimensions(
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
// Copy the data from the texture to the buffer

View File

@ -273,6 +273,7 @@ impl wgpu_example::framework::Example for Example {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&self.pipeline_triangle_conservative);
@ -292,6 +293,7 @@ impl wgpu_example::framework::Example for Example {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&self.pipeline_upscale);

View File

@ -379,6 +379,7 @@ impl wgpu_example::framework::Example for Example {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
rpass.push_debug_group("Prepare data for draw.");
rpass.set_pipeline(&self.pipeline);

View File

@ -122,6 +122,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&render_pipeline);
rpass.draw(0..3, 0..1);

View File

@ -135,6 +135,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Window, wgpu::Color)>) {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
}

View File

@ -167,6 +167,7 @@ impl Example {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
if let Some(ref query_sets) = query_sets {
rpass.write_timestamp(&query_sets.timestamp, timestamp_query_index_base);
@ -492,6 +493,7 @@ impl wgpu_example::framework::Example for Example {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&self.draw_pipeline);
rpass.set_bind_group(0, &self.bind_group, &[]);

View File

@ -300,6 +300,7 @@ impl wgpu_example::framework::Example for Example {
label: None,
color_attachments: &[Some(rpass_color_attachment)],
depth_stencil_attachment: None,
occlusion_query_set: None,
})
.execute_bundles(iter::once(&self.bundle));
}

View File

@ -777,6 +777,7 @@ impl wgpu_example::framework::Example for Example {
}),
stencil_ops: None,
}),
occlusion_query_set: None,
});
pass.set_pipeline(&self.shadow_pass.pipeline);
pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]);
@ -819,6 +820,7 @@ impl wgpu_example::framework::Example for Example {
}),
stencil_ops: None,
}),
occlusion_query_set: None,
});
pass.set_pipeline(&self.forward_pass.pipeline);
pass.set_bind_group(0, &self.forward_pass.bind_group, &[]);

View File

@ -439,6 +439,7 @@ impl wgpu_example::framework::Example for Skybox {
}),
stencil_ops: None,
}),
occlusion_query_set: None,
});
rpass.set_bind_group(0, &self.bind_group, &[]);

View File

@ -211,6 +211,7 @@ impl wgpu_example::framework::Example for Triangles {
store: true,
}),
}),
occlusion_query_set: None,
});
rpass.set_stencil_reference(1);

View File

@ -383,6 +383,7 @@ impl wgpu_example::framework::Example for Example {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&self.pipeline);

View File

@ -752,6 +752,7 @@ impl wgpu_example::framework::Example for Example {
}),
stencil_ops: None,
}),
occlusion_query_set: None,
});
rpass.execute_bundles([&self.terrain_bundle]);
@ -777,6 +778,7 @@ impl wgpu_example::framework::Example for Example {
}),
stencil_ops: None,
}),
occlusion_query_set: None,
});
rpass.set_pipeline(&self.terrain_pipeline);
rpass.set_bind_group(0, &self.terrain_normal_bind_group, &[]);
@ -801,6 +803,7 @@ impl wgpu_example::framework::Example for Example {
depth_ops: None,
stencil_ops: None,
}),
occlusion_query_set: None,
});
rpass.set_pipeline(&self.water_pipeline);

View File

@ -129,12 +129,14 @@ impl GlobalPlay for wgc::global::Global<IdentityPassThroughFactory> {
base,
target_colors,
target_depth_stencil,
occlusion_query_set_id,
} => {
self.command_encoder_run_render_pass_impl::<A>(
encoder,
base.as_ref(),
&target_colors,
target_depth_stencil.as_ref(),
occlusion_query_set_id,
)
.unwrap();
}

View File

@ -0,0 +1,127 @@
use std::borrow::Cow;
use wgpu_test::{initialize_test, TestParameters};
#[test]
fn occlusion_query() {
initialize_test(TestParameters::default(), |ctx| {
// Create depth texture
let depth_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: Some("Depth texture"),
size: wgpu::Extent3d {
width: 64,
height: 64,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let depth_texture_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
// Setup pipeline using a simple shader with hardcoded vertices
let shader = ctx
.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader module"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
});
let pipeline = ctx
.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Pipeline"),
layout: None,
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
fragment: None,
primitive: wgpu::PrimitiveState::default(),
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
// Create occlusion query set
let query_set = ctx.device.create_query_set(&wgpu::QuerySetDescriptor {
label: Some("Query set"),
ty: wgpu::QueryType::Occlusion,
count: 3,
});
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render pass"),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth_texture_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
occlusion_query_set: Some(&query_set),
});
render_pass.set_pipeline(&pipeline);
// Not occluded (z = 1.0, nothing drawn yet)
render_pass.begin_occlusion_query(0);
render_pass.draw(4..7, 0..1);
render_pass.end_occlusion_query();
// Not occluded (z = 0.0)
render_pass.begin_occlusion_query(1);
render_pass.draw(0..3, 0..1);
render_pass.end_occlusion_query();
// Occluded (z = 1.0)
render_pass.begin_occlusion_query(2);
render_pass.draw(4..7, 0..1);
render_pass.end_occlusion_query();
}
// Resolve query set to buffer
let query_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Query buffer"),
size: std::mem::size_of::<u64>() as u64 * 3,
usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
encoder.resolve_query_set(&query_set, 0..3, &query_buffer, 0);
let mapping_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Mapping buffer"),
size: query_buffer.size(),
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
encoder.copy_buffer_to_buffer(&query_buffer, 0, &mapping_buffer, 0, query_buffer.size());
ctx.queue.submit(Some(encoder.finish()));
mapping_buffer
.slice(..)
.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
let query_buffer_view = mapping_buffer.slice(..).get_mapped_range();
let query_data: &[u64; 3] = bytemuck::from_bytes(&query_buffer_view);
// WebGPU only defines query results as zero/non-zero
assert_ne!(query_data[0], 0);
assert_ne!(query_data[1], 0);
assert_eq!(query_data[2], 0);
})
}

View File

@ -0,0 +1,7 @@
@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
let x = f32(i32(in_vertex_index & 3u) - 1);
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
return vec4<f32>(x, y, f32(in_vertex_index & 4u) / 8.0, 1.0);
}

View File

@ -144,6 +144,7 @@ fn pass_reset_vertex_buffer() {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
double_rpass.set_pipeline(&double_pipeline);
@ -177,6 +178,7 @@ fn pass_reset_vertex_buffer() {
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
single_rpass.set_pipeline(&single_pipeline);

View File

@ -13,6 +13,7 @@ mod encoder;
mod example_wgsl;
mod external_texture;
mod instance;
mod occlusion_query;
mod partially_bounded_arrays;
mod poll;
mod queue_transfer;

View File

@ -79,6 +79,7 @@ fn scissor_test_impl(ctx: &TestingContext, scissor_rect: Rect, expected_data: [u
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.set_scissor_rect(

View File

@ -176,6 +176,7 @@ fn pulling_common(
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
@ -185,7 +186,7 @@ fn pulling_common(
view: &color_view,
})],
depth_stencil_attachment: None,
label: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -130,13 +130,14 @@ fn reinterpret(
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
ops: wgpu::Operations::default(),
resolve_target: None,
view: &target_view,
})],
depth_stencil_attachment: None,
label: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);

View File

@ -108,13 +108,14 @@ fn pulling_common(
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
ops: wgpu::Operations::default(),
resolve_target: None,
view: &dummy,
})],
depth_stencil_attachment: None,
label: None,
occlusion_query_set: None,
});
rpass.set_pipeline(&pipeline);

View File

@ -154,6 +154,7 @@ impl<'ctx> TestCase<'ctx> {
store: true,
}),
}),
occlusion_query_set: None,
});
ctx.queue.submit([encoder.finish()]);
} else {
@ -237,6 +238,7 @@ impl<'ctx> TestCase<'ctx> {
}),
},
),
occlusion_query_set: None,
});
}
@ -260,6 +262,7 @@ impl<'ctx> TestCase<'ctx> {
}),
},
),
occlusion_query_set: None,
});
}
@ -283,6 +286,7 @@ impl<'ctx> TestCase<'ctx> {
}),
},
),
occlusion_query_set: None,
});
}

View File

@ -637,6 +637,8 @@ impl RenderBundleEncoder {
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
RenderCommand::PopDebugGroup => unimplemented!(),
RenderCommand::WriteTimestamp { .. } // Must check the TIMESTAMP_QUERY_INSIDE_PASSES feature
| RenderCommand::BeginOcclusionQuery { .. }
| RenderCommand::EndOcclusionQuery
| RenderCommand::BeginPipelineStatisticsQuery { .. }
| RenderCommand::EndPipelineStatisticsQuery => unimplemented!(),
RenderCommand::ExecuteBundle(_)
@ -950,6 +952,8 @@ impl<A: HalApi> RenderBundle<A> {
return Err(ExecutionError::Unimplemented("debug-markers"))
}
RenderCommand::WriteTimestamp { .. }
| RenderCommand::BeginOcclusionQuery { .. }
| RenderCommand::EndOcclusionQuery
| RenderCommand::BeginPipelineStatisticsQuery { .. }
| RenderCommand::EndPipelineStatisticsQuery => {
return Err(ExecutionError::Unimplemented("queries"))

View File

@ -243,6 +243,10 @@ pub enum RenderCommand {
query_set_id: id::QuerySetId,
query_index: u32,
},
BeginOcclusionQuery {
query_index: u32,
},
EndOcclusionQuery,
BeginPipelineStatisticsQuery {
query_set_id: id::QuerySetId,
query_index: u32,

View File

@ -592,6 +592,10 @@ pub enum PassErrorScope {
QueryReset,
#[error("In a write_timestamp command")]
WriteTimestamp,
#[error("In a begin_occlusion_query command")]
BeginOcclusionQuery,
#[error("In a end_occlusion_query command")]
EndOcclusionQuery,
#[error("In a begin_pipeline_statistics_query command")]
BeginPipelineStatisticsQuery,
#[error("In a end_pipeline_statistics_query command")]

View File

@ -240,6 +240,40 @@ impl<A: HalApi> QuerySet<A> {
Ok(())
}
pub(super) fn validate_and_begin_occlusion_query(
&self,
raw_encoder: &mut A::CommandEncoder,
query_set_id: id::QuerySetId,
query_index: u32,
reset_state: Option<&mut QueryResetMap<A>>,
active_query: &mut Option<(id::QuerySetId, u32)>,
) -> Result<(), QueryUseError> {
let needs_reset = reset_state.is_none();
let query_set = self.validate_query(
query_set_id,
SimplifiedQueryType::Occlusion,
query_index,
reset_state,
)?;
if let Some((_old_id, old_idx)) = active_query.replace((query_set_id, query_index)) {
return Err(QueryUseError::AlreadyStarted {
active_query_index: old_idx,
new_query_index: query_index,
});
}
unsafe {
// If we don't have a reset state tracker which can defer resets, we must reset now.
if needs_reset {
raw_encoder.reset_queries(&self.raw, query_index..(query_index + 1));
}
raw_encoder.begin_query(query_set, query_index);
}
Ok(())
}
pub(super) fn validate_and_begin_pipeline_statistics_query(
&self,
raw_encoder: &mut A::CommandEncoder,
@ -275,6 +309,23 @@ impl<A: HalApi> QuerySet<A> {
}
}
pub(super) fn end_occlusion_query<A: HalApi>(
raw_encoder: &mut A::CommandEncoder,
storage: &Storage<QuerySet<A>, id::QuerySetId>,
active_query: &mut Option<(id::QuerySetId, u32)>,
) -> Result<(), QueryUseError> {
if let Some((query_set_id, query_index)) = active_query.take() {
// We can unwrap here as the validity was validated when the active query was set
let query_set = storage.get(query_set_id).unwrap();
unsafe { raw_encoder.end_query(&query_set.raw, query_index) };
Ok(())
} else {
Err(QueryUseError::AlreadyStopped)
}
}
pub(super) fn end_pipeline_statistics_query<A: HalApi>(
raw_encoder: &mut A::CommandEncoder,
storage: &Storage<QuerySet<A>, id::QuerySetId>,

View File

@ -3,7 +3,7 @@ use crate::{
command::{
self,
bind::Binder,
end_pipeline_statistics_query,
end_occlusion_query, end_pipeline_statistics_query,
memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
BasePass, BasePassRef, BindGroupStateChange, CommandBuffer, CommandEncoderError,
CommandEncoderStatus, DrawError, ExecutionError, MapPassErr, PassErrorScope, QueryResetMap,
@ -187,6 +187,8 @@ pub struct RenderPassDescriptor<'a> {
pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment>]>,
/// The depth and stencil attachment of the render pass, if any.
pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachment>,
/// Defines where the occlusion query results will be stored for this pass.
pub occlusion_query_set: Option<id::QuerySetId>,
}
#[cfg_attr(feature = "serial-pass", derive(Deserialize, Serialize))]
@ -195,6 +197,7 @@ pub struct RenderPass {
parent_id: id::CommandEncoderId,
color_targets: ArrayVec<Option<RenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
depth_stencil_target: Option<RenderPassDepthStencilAttachment>,
occlusion_query_set_id: Option<id::QuerySetId>,
// Resource binding dedupe state.
#[cfg_attr(feature = "serial-pass", serde(skip))]
@ -210,6 +213,7 @@ impl RenderPass {
parent_id,
color_targets: desc.color_attachments.iter().cloned().collect(),
depth_stencil_target: desc.depth_stencil_attachment.cloned(),
occlusion_query_set_id: desc.occlusion_query_set,
current_bind_groups: BindGroupStateChange::new(),
current_pipeline: StateChange::new(),
@ -226,6 +230,7 @@ impl RenderPass {
base: self.base,
target_colors: self.color_targets.into_iter().collect(),
target_depth_stencil: self.depth_stencil_target,
occlusion_query_set_id: self.occlusion_query_set_id,
}
}
@ -589,6 +594,8 @@ pub enum RenderPassErrorInner {
"Multiview pass texture views with more than one array layer must have D2Array dimension"
)]
MultiViewDimensionMismatch,
#[error("missing occlusion query set")]
MissingOcclusionQuerySet,
}
impl PrettyError for RenderPassErrorInner {
@ -1201,6 +1208,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pass.base.as_ref(),
&pass.color_targets,
pass.depth_stencil_target.as_ref(),
pass.occlusion_query_set_id,
)
}
@ -1211,6 +1219,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
base: BasePassRef<RenderCommand>,
color_attachments: &[Option<RenderPassColorAttachment>],
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
occlusion_query_set_id: Option<id::QuerySetId>,
) -> Result<(), RenderPassError> {
profiling::scope!("CommandEncoder::run_render_pass");
let init_scope = PassErrorScope::Pass(encoder_id);
@ -1241,6 +1250,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
base: BasePass::from_ref(base),
target_colors: color_attachments.to_vec(),
target_depth_stencil: depth_stencil_attachment.cloned(),
occlusion_query_set_id,
});
}
@ -2027,6 +2037,35 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
)
.map_pass_err(scope)?;
}
RenderCommand::BeginOcclusionQuery { query_index } => {
let scope = PassErrorScope::BeginOcclusionQuery;
let query_set_id = occlusion_query_set_id
.ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)
.map_pass_err(scope)?;
let query_set: &resource::QuerySet<A> = cmd_buf
.trackers
.query_sets
.add_single(&*query_set_guard, query_set_id)
.ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
.map_pass_err(scope)?;
query_set
.validate_and_begin_occlusion_query(
raw,
query_set_id,
query_index,
Some(&mut query_reset_state),
&mut active_query,
)
.map_pass_err(scope)?;
}
RenderCommand::EndOcclusionQuery => {
let scope = PassErrorScope::EndOcclusionQuery;
end_occlusion_query(raw, &*query_set_guard, &mut active_query)
.map_pass_err(scope)?;
}
RenderCommand::BeginPipelineStatisticsQuery {
query_set_id,
query_index,
@ -2544,6 +2583,21 @@ pub mod render_ffi {
});
}
#[no_mangle]
pub extern "C" fn wgpu_render_pass_begin_occlusion_query(
pass: &mut RenderPass,
query_index: u32,
) {
pass.base
.commands
.push(RenderCommand::BeginOcclusionQuery { query_index });
}
#[no_mangle]
pub extern "C" fn wgpu_render_pass_end_occlusion_query(pass: &mut RenderPass) {
pass.base.commands.push(RenderCommand::EndOcclusionQuery);
}
#[no_mangle]
pub extern "C" fn wgpu_render_pass_begin_pipeline_statistics_query(
pass: &mut RenderPass,

View File

@ -95,6 +95,10 @@ pub fn map_buffer_usage(usage: wgt::BufferUsages) -> hal::BufferUses {
hal::BufferUses::INDIRECT,
usage.contains(wgt::BufferUsages::INDIRECT),
);
u.set(
hal::BufferUses::QUERY_RESOLVE,
usage.contains(wgt::BufferUsages::QUERY_RESOLVE),
);
u
}

View File

@ -181,6 +181,7 @@ pub enum Command {
base: crate::command::BasePass<crate::command::RenderCommand>,
target_colors: Vec<Option<crate::command::RenderPassColorAttachment>>,
target_depth_stencil: Option<crate::command::RenderPassDepthStencilAttachment>,
occlusion_query_set_id: Option<id::QuerySetId>,
},
}

View File

@ -527,6 +527,10 @@ impl crate::Device<super::Api> for super::Device {
map_flags |= glow::MAP_COHERENT_BIT;
}
}
// TODO: may also be required for other calls involving `buffer_sub_data_u8_slice` (e.g. copy buffer to buffer and clear buffer)
if desc.usage.intersects(crate::BufferUses::QUERY_RESOLVE) {
map_flags |= glow::DYNAMIC_STORAGE_BIT;
}
unsafe { gl.buffer_storage(target, raw_size, None, map_flags) };
} else {
assert!(!is_coherent);
@ -1238,7 +1242,7 @@ impl crate::Device<super::Api> for super::Device {
Ok(super::QuerySet {
queries: queries.into_boxed_slice(),
target: match desc.ty {
wgt::QueryType::Occlusion => glow::ANY_SAMPLES_PASSED,
wgt::QueryType::Occlusion => glow::ANY_SAMPLES_PASSED_CONSERVATIVE,
_ => unimplemented!(),
},
})

View File

@ -718,6 +718,8 @@ bitflags::bitflags! {
const STORAGE_READ_WRITE = 1 << 8;
/// The indirect or count buffer in a indirect draw or dispatch.
const INDIRECT = 1 << 9;
/// A buffer used to store query results.
const QUERY_RESOLVE = 1 << 10;
/// The combination of states that a buffer may be in _at the same time_.
const INCLUSIVE = Self::MAP_READ.bits() | Self::COPY_SRC.bits() |
Self::INDEX.bits() | Self::VERTEX.bits() | Self::UNIFORM.bits() |

View File

@ -1926,6 +1926,9 @@ impl crate::Context for Context {
label: desc.label.map(Borrowed),
color_attachments: Borrowed(&colors),
depth_stencil_attachment: depth_stencil.as_ref(),
occlusion_query_set: desc
.occlusion_query_set
.map(|query_set| query_set.id.into()),
},
),
)
@ -2940,6 +2943,23 @@ impl crate::Context for Context {
wgpu_render_pass_write_timestamp(pass_data, *query_set, query_index)
}
fn render_pass_begin_occlusion_query(
&self,
_pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData,
query_index: u32,
) {
wgpu_render_pass_begin_occlusion_query(pass_data, query_index)
}
fn render_pass_end_occlusion_query(
&self,
_pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData,
) {
wgpu_render_pass_end_occlusion_query(pass_data)
}
fn render_pass_begin_pipeline_statistics_query(
&self,
_pass: &mut Self::RenderPassId,

View File

@ -3217,6 +3217,23 @@ impl crate::context::Context for Context {
panic!("TIMESTAMP_QUERY_INSIDE_PASSES feature must be enabled to call write_timestamp in a compute pass")
}
fn render_pass_begin_occlusion_query(
&self,
_pass: &mut Self::RenderPassId,
_pass_data: &mut Self::RenderPassData,
_query_index: u32,
) {
// Not available in gecko yet
}
fn render_pass_end_occlusion_query(
&self,
_pass: &mut Self::RenderPassId,
_pass_data: &mut Self::RenderPassData,
) {
// Not available in gecko yet
}
fn render_pass_begin_pipeline_statistics_query(
&self,
_pass: &mut Self::RenderPassId,

View File

@ -970,6 +970,17 @@ pub trait Context: Debug + WasmNotSend + WasmNotSync + Sized {
query_set_data: &Self::QuerySetData,
query_index: u32,
);
fn render_pass_begin_occlusion_query(
&self,
pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData,
query_index: u32,
);
fn render_pass_end_occlusion_query(
&self,
pass: &mut Self::RenderPassId,
pass_data: &mut Self::RenderPassData,
);
fn render_pass_begin_pipeline_statistics_query(
&self,
pass: &mut Self::RenderPassId,
@ -1986,6 +1997,13 @@ pub(crate) trait DynContext: Debug + WasmNotSend + WasmNotSync {
query_set_data: &crate::Data,
query_index: u32,
);
fn render_pass_begin_occlusion_query(
&self,
pass: &mut ObjectId,
pass_data: &mut crate::Data,
query_index: u32,
);
fn render_pass_end_occlusion_query(&self, pass: &mut ObjectId, pass_data: &mut crate::Data);
fn render_pass_begin_pipeline_statistics_query(
&self,
pass: &mut ObjectId,
@ -3911,6 +3929,23 @@ where
)
}
fn render_pass_begin_occlusion_query(
&self,
pass: &mut ObjectId,
pass_data: &mut crate::Data,
query_index: u32,
) {
let mut pass = <T::RenderPassId>::from(*pass);
let pass_data = downcast_mut::<T::RenderPassData>(pass_data);
Context::render_pass_begin_occlusion_query(self, &mut pass, pass_data, query_index)
}
fn render_pass_end_occlusion_query(&self, pass: &mut ObjectId, pass_data: &mut crate::Data) {
let mut pass = <T::RenderPassId>::from(*pass);
let pass_data = downcast_mut::<T::RenderPassData>(pass_data);
Context::render_pass_end_occlusion_query(self, &mut pass, pass_data)
}
fn render_pass_begin_pipeline_statistics_query(
&self,
pass: &mut ObjectId,

View File

@ -851,6 +851,7 @@ impl Drop for RenderBundle {
/// It can be created with [`Device::create_query_set`].
///
/// Corresponds to [WebGPU `GPUQuerySet`](https://gpuweb.github.io/gpuweb/#queryset).
#[derive(Debug)]
pub struct QuerySet {
context: Arc<C>,
id: ObjectId,
@ -1336,6 +1337,8 @@ pub struct RenderPassDescriptor<'tex, 'desc> {
pub color_attachments: &'desc [Option<RenderPassColorAttachment<'tex>>],
/// The depth and stencil attachment of the render pass, if any.
pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<'tex>>,
/// Defines where the occlusion query results will be stored for this pass.
pub occlusion_query_set: Option<&'tex QuerySet>,
}
#[cfg(any(
not(target_arch = "wasm32"),
@ -3818,6 +3821,29 @@ impl<'a> RenderPass<'a> {
}
}
impl<'a> RenderPass<'a> {
/// Start a occlusion query on this render pass. It can be ended with
/// `end_occlusion_query`. Occlusion queries may not be nested.
pub fn begin_occlusion_query(&mut self, query_index: u32) {
DynContext::render_pass_begin_occlusion_query(
&*self.parent.context,
&mut self.id,
self.data.as_mut(),
query_index,
);
}
/// End the occlusion query on this render pass. It can be started with
/// `begin_occlusion_query`. Occlusion queries may not be nested.
pub fn end_occlusion_query(&mut self) {
DynContext::render_pass_end_occlusion_query(
&*self.parent.context,
&mut self.id,
self.data.as_mut(),
);
}
}
/// [`Features::PIPELINE_STATISTICS_QUERY`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> {
/// Start a pipeline statistics query on this render pass. It can be ended with