715: Add pipeline statistics and timeline queries r=kvark a=cwfitzgerald

Blocked on https://github.com/gfx-rs/wgpu/pull/1128.

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
bors[bot]
2021-01-16 06:06:18 +00:00
committed by GitHub
15 changed files with 481 additions and 50 deletions

View File

@@ -26,20 +26,20 @@ webgl = ["wgc"]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "c788b1cef630fa5b5f0a86132fe84551d4149fb6"
rev = "9dce0afa746d624533dd086985889a2439750628"
features = ["raw-window-handle"]
[target.'cfg(target_arch = "wasm32")'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "c788b1cef630fa5b5f0a86132fe84551d4149fb6"
rev = "9dce0afa746d624533dd086985889a2439750628"
features = ["raw-window-handle"]
optional = true
[dependencies.wgt]
package = "wgpu-types"
git = "https://github.com/gfx-rs/wgpu"
rev = "c788b1cef630fa5b5f0a86132fe84551d4149fb6"
rev = "9dce0afa746d624533dd086985889a2439750628"
[dependencies]
arrayvec = "0.5"

View File

@@ -10,35 +10,36 @@ Notably, `capture` example shows rendering without a surface/window. It reads ba
All framework-based examples render to the window.
## Feature matrix
| Feature | boids | cube | mipmap | msaa-line | shadow | skybox | texture-arrays | water |
| ------------------------- | ------ | ------ | ------ | --------- | ------ | ------ | -------------- | ------ |
| vertex attributes | :star: | :star: | :star: | :star: | :star: | | :star: | :star: |
| instancing | :star: | | | | | | | |
| lines and points | | | | :star: | | | | |
| dynamic buffer offsets | | | | | :star: | | | |
| implicit layout | | | :star: | | | | | |
| sampled color textures | :star: | :star: | :star: | | | :star: | :star: | :star: |
| storage textures | :star: | | | | | | | |
| binding array | | | | | | | :star: | |
| comparison samplers | | | | | :star: | | | |
| subresource views | | | :star: | | :star: | | | |
| cubemaps | | | | | | :star: | | |
| multisampling | | | | :star: | | | | |
| off-screen rendering | | | | | :star: | | | :star: |
| stencil testing | | | | | | | | |
| depth testing | | | | | :star: | | | :star: |
| depth biasing | | | | | :star: | | | |
| read-only depth | | | | | | | | :star: |
| blending | | :star: | | | | | | :star: |
| render bundles | | | | :star: | | | | :star: |
| compute passes | :star: | | | | | | | |
| optional extensions | | | | | | | :star: | |
| - binding indexing | | | | | | | :star: | |
| - push constants | | | | | | | :star: | |
| - depth clamping | | | | | :star: | | | |
| - BCn compressed textures | | | | | | :star: | | |
| - polygon mode | | :star: | | | | | | |
| WGSL shaders | | | | | | | | |
| Feature | boids | cube | mipmap | msaa-line | shadow | skybox | texture-arrays | water |
| ---------------------- | ------ | ------ | ------ | --------- | ------ | ------ | -------------- | ------ |
| vertex attributes | :star: | :star: | :star: | :star: | :star: | | :star: | :star: |
| instancing | :star: | | | | | | | |
| lines and points | | | | :star: | | | | |
| dynamic buffer offsets | | | | | :star: | | | |
| implicit layout | | | :star: | | | | | |
| sampled color textures | :star: | :star: | :star: | | | :star: | :star: | :star: |
| storage textures | :star: | | | | | | | |
| binding array | | | | | | | :star: | |
| comparison samplers | | | | | :star: | | | |
| subresource views | | | :star: | | :star: | | | |
| cubemaps | | | | | | :star: | | |
| multisampling | | | | :star: | | | | |
| off-screen rendering | | | | | :star: | | | :star: |
| stencil testing | | | | | | | | |
| depth testing | | | | | :star: | | | :star: |
| depth biasing | | | | | :star: | | | |
| read-only depth | | | | | | | | :star: |
| blending | | :star: | | | | | | :star: |
| render bundles | | | | :star: | | | | :star: |
| compute passes | :star: | | | | | | | |
| optional extensions | | | | | | | :star: | |
| - binding indexing | | | | | | | :star: | |
| - push constants | | | | | | | :star: | |
| - depth clamping | | | | | :star: | | | |
| - compressed textures | | | | | | :star: | | |
| - polygon mode | | :star: | | | | | | |
| - queries | | | :star: | | | | | |
| WGSL shaders | | | | | | | | |
## Hacking

View File

@@ -29,6 +29,7 @@ impl framework::Example for Example {
/// constructs initial instance of Example struct
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
_queue: &wgpu::Queue,
) -> Self {

View File

@@ -118,6 +118,7 @@ impl framework::Example for Example {
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Self {

View File

@@ -41,6 +41,7 @@ pub trait Example: 'static + Sized {
}
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
adapter: &wgpu::Adapter,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Self;
@@ -209,7 +210,7 @@ fn start<E: Example>(
let mut swap_chain = device.create_swap_chain(&surface, &sc_desc);
log::info!("Initializing the example...");
let mut example = E::init(&sc_desc, &device, &queue);
let mut example = E::init(&sc_desc, &adapter, &device, &queue);
#[cfg(not(target_arch = "wasm32"))]
let mut last_update_inst = Instant::now();

View File

@@ -13,7 +13,7 @@ layout(set = 0, binding = 0) buffer PrimeIndices {
// This function returns how many times this recurrence needs to be applied to reach 1.
uint collatz_iterations(uint n) {
uint i = 0;
while(n != 1) {
while(n > 1) {
if (mod(n, 2) == 0) {
n = n / 2;
}

View File

@@ -2,10 +2,12 @@
mod framework;
use bytemuck::{Pod, Zeroable};
use std::num::NonZeroU32;
use std::{mem, num::NonZeroU32};
use wgpu::util::DeviceExt;
const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
const MIP_LEVEL_COUNT: u32 = 9;
const MIP_PASS_COUNT: u32 = MIP_LEVEL_COUNT - 1;
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
@@ -54,6 +56,27 @@ fn create_texels(size: usize, cx: f32, cy: f32) -> Vec<u8> {
.collect()
}
struct QuerySets {
timestamp: wgpu::QuerySet,
timestamp_period: f32,
pipeline_statistics: wgpu::QuerySet,
data_buffer: wgpu::Buffer,
}
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct TimestampData {
start: u64,
end: u64,
}
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
struct QueryData {
timestamps: [TimestampData; MIP_PASS_COUNT as usize],
pipeline_queries: [u64; MIP_PASS_COUNT as usize],
}
struct Example {
vertex_buf: wgpu::Buffer,
bind_group: wgpu::BindGroup,
@@ -77,6 +100,7 @@ impl Example {
encoder: &mut wgpu::CommandEncoder,
device: &wgpu::Device,
texture: &wgpu::Texture,
query_sets: &Option<QuerySets>,
mip_count: u32,
) {
let vs_module = device.create_shader_module(&wgpu::include_spirv!("blit.vert.spv"));
@@ -154,6 +178,9 @@ impl Example {
label: None,
});
let pipeline_query_index_base = target_mip as u32 - 1;
let timestamp_query_index_base = (target_mip as u32 - 1) * 2;
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
@@ -166,21 +193,51 @@ impl Example {
}],
depth_stencil_attachment: None,
});
if let Some(ref query_sets) = query_sets {
rpass.write_timestamp(&query_sets.timestamp, timestamp_query_index_base);
rpass.begin_pipeline_statistics_query(
&query_sets.pipeline_statistics,
pipeline_query_index_base,
);
}
rpass.set_pipeline(&pipeline);
rpass.set_bind_group(0, &bind_group, &[]);
rpass.draw(0..4, 0..1);
if let Some(ref query_sets) = query_sets {
rpass.write_timestamp(&query_sets.timestamp, timestamp_query_index_base + 1);
rpass.end_pipeline_statistics_query();
}
}
if let Some(ref query_sets) = query_sets {
let timestamp_query_count = MIP_PASS_COUNT * 2;
encoder.resolve_query_set(
&query_sets.timestamp,
0..timestamp_query_count,
&query_sets.data_buffer,
0,
);
encoder.resolve_query_set(
&query_sets.pipeline_statistics,
0..MIP_PASS_COUNT,
&query_sets.data_buffer,
(timestamp_query_count * mem::size_of::<u64>() as u32) as wgpu::BufferAddress,
);
}
}
}
impl framework::Example for Example {
fn optional_features() -> wgpu::Features {
wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::PIPELINE_STATISTICS_QUERY
}
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
adapter: &wgpu::Adapter,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Self {
use std::mem;
let mut init_encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
@@ -194,8 +251,7 @@ impl framework::Example for Example {
});
// Create the texture
let mip_level_count = 9;
let size = 1 << mip_level_count;
let size = 1 << MIP_LEVEL_COUNT;
let texels = create_texels(size as usize, -0.8, 0.156);
let texture_extent = wgpu::Extent3d {
width: size,
@@ -204,7 +260,7 @@ impl framework::Example for Example {
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
size: texture_extent,
mip_level_count,
mip_level_count: MIP_LEVEL_COUNT,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: TEXTURE_FORMAT,
@@ -314,9 +370,95 @@ impl framework::Example for Example {
label: None,
});
// Done
Self::generate_mipmaps(&mut init_encoder, &device, &texture, mip_level_count);
// If both kinds of query are supported, use queries
let query_sets = if device
.features()
.contains(wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::PIPELINE_STATISTICS_QUERY)
{
// For N total mips, it takes N - 1 passes to generate them, and we're measuring those.
let mip_passes = MIP_LEVEL_COUNT - 1;
// Create the timestamp query set. We need twice as many queries as we have passes,
// as we need a query at the beginning and at the end of the operation.
let timestamp = device.create_query_set(&wgpu::QuerySetDescriptor {
count: mip_passes * 2,
ty: wgpu::QueryType::Timestamp,
});
// Timestamp queries use an device-specific timestamp unit. We need to figure out how many
// nanoseconds go by for the timestamp to be incremented by one. The period is this value.
let timestamp_period = adapter.get_timestamp_period();
// We only need one pipeline statistics query per pass.
let pipeline_statistics = device.create_query_set(&wgpu::QuerySetDescriptor {
count: mip_passes,
ty: wgpu::QueryType::PipelineStatistics(
wgpu::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS,
),
});
// This databuffer has to store all of the query results, 2 * passes timestamp queries
// and 1 * passes statistics queries. Each query returns a u64 value.
let data_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("query buffer"),
size: mip_passes as wgpu::BufferAddress
* 3
* mem::size_of::<u64>() as wgpu::BufferAddress,
usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ,
mapped_at_creation: false,
});
Some(QuerySets {
timestamp,
timestamp_period,
pipeline_statistics,
data_buffer,
})
} else {
None
};
Self::generate_mipmaps(
&mut init_encoder,
&device,
&texture,
&query_sets,
MIP_LEVEL_COUNT,
);
queue.submit(Some(init_encoder.finish()));
if let Some(ref query_sets) = query_sets {
// We can ignore the future as we're about to wait for the device.
let _ = query_sets
.data_buffer
.slice(..)
.map_async(wgpu::MapMode::Read);
// Wait for device to be done rendering mipmaps
device.poll(wgpu::Maintain::Wait);
// This is guaranteed to be ready.
let view = query_sets.data_buffer.slice(..).get_mapped_range();
// Convert the raw data into a useful structure
let data: &QueryData = bytemuck::from_bytes(&*view);
// Iterate over the data
for (idx, (timestamp, pipeline)) in data
.timestamps
.iter()
.zip(data.pipeline_queries.iter())
.enumerate()
{
// Figure out the timestamp differences and multiply by the period to get nanoseconds
let nanoseconds =
(timestamp.end - timestamp.start) as f32 * query_sets.timestamp_period;
// Nanoseconds is a bit small, so lets use microseconds.
let microseconds = nanoseconds / 1000.0;
// Print the data!
println!(
"Generating mip level {} took {:.3} μs and called the fragment shader {} times",
idx + 1,
microseconds,
pipeline
);
}
}
Example {
vertex_buf,

View File

@@ -122,6 +122,7 @@ impl Example {
impl framework::Example for Example {
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
_queue: &wgpu::Queue,
) -> Self {

View File

@@ -199,6 +199,7 @@ impl framework::Example for Example {
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
_queue: &wgpu::Queue,
) -> Self {

View File

@@ -46,6 +46,7 @@ impl framework::Example for Skybox {
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Self {

View File

@@ -87,6 +87,7 @@ impl framework::Example for Example {
}
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Self {

View File

@@ -263,6 +263,7 @@ impl Example {
impl framework::Example for Example {
fn init(
sc_desc: &wgpu::SwapChainDescriptor,
_adapter: &wgpu::Adapter,
device: &wgpu::Device,
queue: &wgpu::Queue,
) -> Self {

View File

@@ -184,6 +184,25 @@ mod pass_impl {
fn pop_debug_group(&mut self) {
wgpu_compute_pass_pop_debug_group(self);
}
fn write_timestamp(&mut self, query_set: &wgc::id::QuerySetId, query_index: u32) {
unsafe { wgpu_compute_pass_write_timestamp(self, *query_set, query_index) }
}
fn begin_pipeline_statistics_query(
&mut self,
query_set: &wgc::id::QuerySetId,
query_index: u32,
) {
unsafe {
wgpu_compute_pass_begin_pipeline_statistics_query(self, *query_set, query_index)
}
}
fn end_pipeline_statistics_query(&mut self) {
unsafe { wgpu_compute_pass_end_pipeline_statistics_query(self) }
}
fn dispatch(&mut self, x: u32, y: u32, z: u32) {
wgpu_compute_pass_dispatch(self, x, y, z)
}
@@ -375,6 +394,24 @@ mod pass_impl {
wgpu_render_pass_pop_debug_group(self);
}
fn write_timestamp(&mut self, query_set: &wgc::id::QuerySetId, query_index: u32) {
unsafe { wgpu_render_pass_write_timestamp(self, *query_set, query_index) }
}
fn begin_pipeline_statistics_query(
&mut self,
query_set: &wgc::id::QuerySetId,
query_index: u32,
) {
unsafe {
wgpu_render_pass_begin_pipeline_statistics_query(self, *query_set, query_index)
}
}
fn end_pipeline_statistics_query(&mut self) {
unsafe { wgpu_render_pass_end_pipeline_statistics_query(self) }
}
fn execute_bundles<'a, I: Iterator<Item = &'a wgc::id::RenderBundleId>>(
&mut self,
render_bundles: I,
@@ -600,6 +637,7 @@ impl crate::Context for Context {
type BindGroupId = wgc::id::BindGroupId;
type TextureViewId = wgc::id::TextureViewId;
type SamplerId = wgc::id::SamplerId;
type QuerySetId = wgc::id::QuerySetId;
type BufferId = Buffer;
type TextureId = Texture;
type PipelineLayoutId = wgc::id::PipelineLayoutId;
@@ -702,6 +740,19 @@ impl crate::Context for Context {
}
}
fn adapter_get_timestamp_period(&self, adapter: &Self::AdapterId) -> f32 {
let global = &self.0;
let res = wgc::gfx_select!(adapter => global.adapter_get_timestamp_period(
*adapter
));
match res {
Ok(v) => v,
Err(cause) => {
self.handle_error_fatal(cause, "Adapter::get_timestamp_period");
}
}
}
fn adapter_get_info(&self, adapter: &wgc::id::AdapterId) -> AdapterInfo {
let global = &self.0;
match wgc::gfx_select!(*adapter => global.adapter_get_info(*adapter)) {
@@ -1123,6 +1174,23 @@ impl crate::Context for Context {
id
}
fn device_create_query_set(
&self,
device: &Self::DeviceId,
desc: &wgt::QuerySetDescriptor,
) -> Self::QuerySetId {
let global = &self.0;
let (id, error) = wgc::gfx_select!(device.id => global.device_create_query_set(
device.id,
&desc,
PhantomData
));
if let Some(cause) = error {
self.handle_error_nolabel(&device.error_sink, cause, "Device::create_query_set");
}
id
}
fn device_create_command_encoder(
&self,
device: &Self::DeviceId,
@@ -1382,6 +1450,10 @@ impl crate::Context for Context {
let global = &self.0;
wgc::gfx_select!(*sampler => global.sampler_drop(*sampler))
}
fn query_set_drop(&self, query_set: &Self::QuerySetId) {
let global = &self.0;
wgc::gfx_select!(*query_set => global.query_set_drop(*query_set))
}
fn bind_group_drop(&self, bind_group: &Self::BindGroupId) {
let global = &self.0;
wgc::gfx_select!(*bind_group => global.bind_group_drop(*bind_group))
@@ -1532,6 +1604,52 @@ impl crate::Context for Context {
}
}
fn command_encoder_write_timestamp(
&self,
encoder: &Self::CommandEncoderId,
query_set: &Self::QuerySetId,
query_index: u32,
) {
let global = &self.0;
if let Err(cause) = wgc::gfx_select!(encoder.id => global.command_encoder_write_timestamp(
encoder.id,
*query_set,
query_index
)) {
self.handle_error_nolabel(
&encoder.error_sink,
cause,
"CommandEncoder::write_timestamp",
);
}
}
fn command_encoder_resolve_query_set(
&self,
encoder: &Self::CommandEncoderId,
query_set: &Self::QuerySetId,
first_query: u32,
query_count: u32,
destination: &Self::BufferId,
destination_offset: wgt::BufferAddress,
) {
let global = &self.0;
if let Err(cause) = wgc::gfx_select!(encoder.id => global.command_encoder_resolve_query_set(
encoder.id,
*query_set,
first_query,
query_count,
destination.id,
destination_offset
)) {
self.handle_error_nolabel(
&encoder.error_sink,
cause,
"CommandEncoder::resolve_query_set",
);
}
}
fn command_encoder_begin_compute_pass(
&self,
encoder: &Self::CommandEncoderId,

View File

@@ -29,14 +29,15 @@ pub use wgt::{
BlendDescriptor, BlendFactor, BlendOperation, BufferAddress, BufferBindingType, BufferSize,
BufferUsage, Color, ColorStateDescriptor, ColorWrite, CommandBufferDescriptor, CompareFunction,
CullMode, DepthStencilStateDescriptor, DeviceType, DynamicOffset, Extent3d, Features,
FilterMode, FrontFace, IndexFormat, InputStepMode, Limits, Origin3d, PolygonMode,
PowerPreference, PresentMode, PrimitiveTopology, PushConstantRange,
RasterizationStateDescriptor, SamplerBorderColor, ShaderFlags, ShaderLocation, ShaderStage,
StencilOperation, StencilStateDescriptor, StencilStateFaceDescriptor, StorageTextureAccess,
SwapChainDescriptor, SwapChainStatus, TextureAspect, TextureDataLayout, TextureDimension,
TextureFormat, TextureSampleType, TextureUsage, TextureViewDimension,
VertexAttributeDescriptor, VertexFormat, BIND_BUFFER_ALIGNMENT, COPY_BUFFER_ALIGNMENT,
COPY_BYTES_PER_ROW_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
FilterMode, FrontFace, IndexFormat, InputStepMode, Limits, Origin3d, PipelineStatisticsTypes,
PolygonMode, PowerPreference, PresentMode, PrimitiveTopology, PushConstantRange,
QuerySetDescriptor, QueryType, RasterizationStateDescriptor, SamplerBorderColor, ShaderFlags,
ShaderLocation, ShaderStage, StencilOperation, StencilStateDescriptor,
StencilStateFaceDescriptor, StorageTextureAccess, SwapChainDescriptor, SwapChainStatus,
TextureAspect, TextureDataLayout, TextureDimension, TextureFormat, TextureSampleType,
TextureUsage, TextureViewDimension, VertexAttributeDescriptor, VertexFormat,
BIND_BUFFER_ALIGNMENT, COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT,
PUSH_CONSTANT_ALIGNMENT,
};
use backend::{BufferMappedRange, Context as C};
@@ -53,6 +54,9 @@ trait ComputePassInner<Ctx: Context> {
fn insert_debug_marker(&mut self, label: &str);
fn push_debug_group(&mut self, group_label: &str);
fn pop_debug_group(&mut self);
fn write_timestamp(&mut self, query_set: &Ctx::QuerySetId, query_index: u32);
fn begin_pipeline_statistics_query(&mut self, query_set: &Ctx::QuerySetId, query_index: u32);
fn end_pipeline_statistics_query(&mut self);
fn dispatch(&mut self, x: u32, y: u32, z: u32);
fn dispatch_indirect(
&mut self,
@@ -138,6 +142,9 @@ trait RenderPassInner<Ctx: Context>: RenderInner<Ctx> {
fn insert_debug_marker(&mut self, label: &str);
fn push_debug_group(&mut self, group_label: &str);
fn pop_debug_group(&mut self);
fn write_timestamp(&mut self, query_set: &Ctx::QuerySetId, query_index: u32);
fn begin_pipeline_statistics_query(&mut self, query_set: &Ctx::QuerySetId, query_index: u32);
fn end_pipeline_statistics_query(&mut self);
fn execute_bundles<'a, I: Iterator<Item = &'a Ctx::RenderBundleId>>(
&mut self,
render_bundles: I,
@@ -155,6 +162,7 @@ trait Context: Debug + Send + Sized + Sync {
type SamplerId: Debug + Send + Sync + 'static;
type BufferId: Debug + Send + Sync + 'static;
type TextureId: Debug + Send + Sync + 'static;
type QuerySetId: Debug + Send + Sync + 'static;
type PipelineLayoutId: Debug + Send + Sync + 'static;
type RenderPipelineId: Debug + Send + Sync + 'static;
type ComputePipelineId: Debug + Send + Sync + 'static;
@@ -196,6 +204,7 @@ trait Context: Debug + Send + Sized + Sync {
) -> TextureFormat;
fn adapter_features(&self, adapter: &Self::AdapterId) -> Features;
fn adapter_limits(&self, adapter: &Self::AdapterId) -> Limits;
fn adapter_get_timestamp_period(&self, adapter: &Self::AdapterId) -> f32;
fn adapter_get_info(&self, adapter: &Self::AdapterId) -> AdapterInfo;
fn adapter_get_texture_format_features(
&self,
@@ -256,6 +265,11 @@ trait Context: Debug + Send + Sized + Sync {
device: &Self::DeviceId,
desc: &SamplerDescriptor,
) -> Self::SamplerId;
fn device_create_query_set(
&self,
device: &Self::DeviceId,
desc: &QuerySetDescriptor,
) -> Self::QuerySetId;
fn device_create_command_encoder(
&self,
device: &Self::DeviceId,
@@ -309,6 +323,7 @@ trait Context: Debug + Send + Sized + Sync {
fn texture_drop(&self, texture: &Self::TextureId);
fn texture_view_drop(&self, texture_view: &Self::TextureViewId);
fn sampler_drop(&self, sampler: &Self::SamplerId);
fn query_set_drop(&self, query_set: &Self::QuerySetId);
fn bind_group_drop(&self, bind_group: &Self::BindGroupId);
fn bind_group_layout_drop(&self, bind_group_layout: &Self::BindGroupLayoutId);
fn pipeline_layout_drop(&self, pipeline_layout: &Self::PipelineLayoutId);
@@ -386,6 +401,22 @@ trait Context: Debug + Send + Sized + Sync {
fn command_encoder_push_debug_group(&self, encoder: &Self::CommandEncoderId, label: &str);
fn command_encoder_pop_debug_group(&self, encoder: &Self::CommandEncoderId);
fn command_encoder_write_timestamp(
&self,
encoder: &Self::CommandEncoderId,
query_set: &Self::QuerySetId,
query_index: u32,
);
fn command_encoder_resolve_query_set(
&self,
encoder: &Self::CommandEncoderId,
query_set: &Self::QuerySetId,
first_query: u32,
query_count: u32,
destination: &Self::BufferId,
destination_offset: BufferAddress,
);
fn render_bundle_encoder_finish(
&self,
encoder: Self::RenderBundleEncoderId,
@@ -868,6 +899,20 @@ impl Drop for RenderBundle {
}
}
/// Handle to a query set.
pub struct QuerySet {
context: Arc<C>,
id: <C as Context>::QuerySetId,
}
impl Drop for QuerySet {
fn drop(&mut self) {
if !thread::panicking() {
self.context.query_set_drop(&self.id);
}
}
}
/// Handle to a command queue on a device.
///
/// A `Queue` executes recorded [`CommandBuffer`] objects and provides convenience methods
@@ -1414,6 +1459,13 @@ impl Adapter {
Context::adapter_limits(&*self.context, &self.id)
}
/// Gets the amount of nanoseconds each tick of a timestamp query represents.
///
/// Returns zero if timestamp queries are unsupported.
pub fn get_timestamp_period(&self) -> f32 {
Context::adapter_get_timestamp_period(&*self.context, &self.id)
}
/// Get info about the adapter itself.
pub fn get_info(&self) -> AdapterInfo {
Context::adapter_get_info(&*self.context, &self.id)
@@ -1558,6 +1610,14 @@ impl Device {
}
}
/// Creates a new [`QuerySet`].
pub fn create_query_set(&self, desc: &QuerySetDescriptor) -> QuerySet {
QuerySet {
context: Arc::clone(&self.context),
id: Context::device_create_query_set(&*self.context, &self.id, desc),
}
}
/// Create a new [`SwapChain`] which targets `surface`.
///
/// # Panics
@@ -1991,6 +2051,49 @@ impl CommandEncoder {
}
}
/// [`Features::TIMESTAMP_QUERY`] must be enabled on the device in order to call these functions.
impl CommandEncoder {
/// Issue a timestamp command at this point in the queue.
/// The timestamp will be written to the specified query set, at the specified index.
///
/// Must be multiplied by [`Device::get_timestamp_period`] to get
/// the value in nanoseconds. Absolute values have no meaning,
/// but timestamps can be subtracted to get the time it takes
/// for a string of operations to complete.
pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {
Context::command_encoder_write_timestamp(
&*self.context,
&self.id,
&query_set.id,
query_index,
)
}
}
/// [`Features::TIMESTAMP_QUERY`] or [`Features::PIPELINE_STATISTICS_QUERY`] must be enabled on the device in order to call these functions.
impl CommandEncoder {
/// Resolve a query set, writing the results into the supplied destination buffer.
///
/// Queries may be between 8 and 40 bytes each. See [`PipelineStatisticsType`] for more information.
pub fn resolve_query_set(
&mut self,
query_set: &QuerySet,
query_range: Range<u32>,
destination: &Buffer,
destination_offset: BufferAddress,
) {
Context::command_encoder_resolve_query_set(
&*self.context,
&self.id,
&query_set.id,
query_range.start,
query_range.end - query_range.start,
&destination.id,
destination_offset,
)
}
}
impl<'a> RenderPass<'a> {
/// Sets the active bind group for a given bind group index. The bind group layout
/// in the active pipeline when any `draw()` function is called must match the layout of this bind group.
@@ -2354,6 +2457,36 @@ impl<'a> RenderPass<'a> {
}
}
/// [`Features::TIMESTAMP_QUERY`] must be enabled on the device in order to call these functions.
impl<'a> RenderPass<'a> {
/// Issue a timestamp command at this point in the queue. The
/// timestamp will be written to the specified query set, at the specified index.
///
/// Must be multiplied by [`Device::get_timestamp_period`] to get
/// the value in nanoseconds. Absolute values have no meaning,
/// but timestamps can be subtracted to get the time it takes
/// for a string of operations to complete.
pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {
self.id.write_timestamp(&query_set.id, query_index)
}
}
/// [`Features::PIPEILNE_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
/// `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) {
self.id
.begin_pipeline_statistics_query(&query_set.id, query_index);
}
/// End the pipeline statistics query on this render pass. It can be started with
/// `begin_pipeline_statistics_query`. Pipeline statistics queries may not be nested.
pub fn end_pipeline_statistics_query(&mut self) {
self.id.end_pipeline_statistics_query();
}
}
impl<'a> Drop for RenderPass<'a> {
fn drop(&mut self) {
if !thread::panicking() {
@@ -2430,6 +2563,35 @@ impl<'a> ComputePass<'a> {
}
}
/// [`Features::TIMESTAMP_QUERY`] must be enabled on the device in order to call these functions.
impl<'a> ComputePass<'a> {
/// Issue a timestamp command at this point in the queue. The timestamp will be written to the specified query set, at the specified index.
///
/// Must be multiplied by [`Device::get_timestamp_period`] to get
/// the value in nanoseconds. Absolute values have no meaning,
/// but timestamps can be subtracted to get the time it takes
/// for a string of operations to complete.
pub fn write_timestamp(&mut self, query_set: &QuerySet, query_index: u32) {
self.id.write_timestamp(&query_set.id, query_index)
}
}
/// [`Features::PIPEILNE_STATISTICS_QUERY`] must be enabled on the device in order to call these functions.
impl<'a> ComputePass<'a> {
/// 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.
pub fn begin_pipeline_statistics_query(&mut self, query_set: &QuerySet, query_index: u32) {
self.id
.begin_pipeline_statistics_query(&query_set.id, query_index);
}
/// End the pipeline statistics query on this render pass. It can be started with
/// `begin_pipeline_statistics_query`. Pipeline statistics queries may not be nested.
pub fn end_pipeline_statistics_query(&mut self) {
self.id.end_pipeline_statistics_query();
}
}
impl<'a> Drop for ComputePass<'a> {
fn drop(&mut self) {
if !thread::panicking() {