mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
[rs] Merge #715
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:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
178
wgpu/src/lib.rs
178
wgpu/src/lib.rs
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user