mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 00:03:29 +00:00
Add infrastructure for counting and reporting internal resources (#5708)
* Add an optional system for counting and reporting internal resources and events * Count API objects in wgpu-hal * Expose internal counters in wgpu-core and wgpu.
This commit is contained in:
parent
afc8e38fc1
commit
1de04926b1
@ -32,6 +32,11 @@ ignored = ["cfg_aliases"]
|
||||
[lib]
|
||||
|
||||
[features]
|
||||
## Internally count resources and events for debugging purposes. If the counters
|
||||
## feature is disabled, the counting infrastructure is removed from the build and
|
||||
## the exposed counters always return 0.
|
||||
counters = ["wgt/counters"]
|
||||
|
||||
## Log all API entry points at info instead of trace level.
|
||||
api_log_info = []
|
||||
|
||||
|
@ -2403,6 +2403,21 @@ impl Global {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn device_get_internal_counters<A: HalApi>(
|
||||
&self,
|
||||
device_id: DeviceId,
|
||||
) -> wgt::InternalCounters {
|
||||
let hub = A::hub(self);
|
||||
if let Ok(device) = hub.devices.get(device_id) {
|
||||
wgt::InternalCounters {
|
||||
hal: device.get_hal_counters(),
|
||||
core: wgt::CoreCounters {},
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_drop<A: HalApi>(&self, queue_id: QueueId) {
|
||||
profiling::scope!("Queue::drop");
|
||||
api_log!("Queue::drop {queue_id:?}");
|
||||
|
@ -404,6 +404,7 @@ impl<A: HalApi> Device<A> {
|
||||
snatch_guard: SnatchGuard,
|
||||
) -> Result<(UserClosures, bool), WaitIdleError> {
|
||||
profiling::scope!("Device::maintain");
|
||||
|
||||
let fence = fence_guard.as_ref().unwrap();
|
||||
let last_done_index = if maintain.is_wait() {
|
||||
let index_to_wait_for = match maintain {
|
||||
@ -3641,6 +3642,13 @@ impl<A: HalApi> Device<A> {
|
||||
pub(crate) fn new_usage_scope(&self) -> UsageScope<'_, A> {
|
||||
UsageScope::new_pooled(&self.usage_scopes, &self.tracker_indices)
|
||||
}
|
||||
|
||||
pub fn get_hal_counters(&self) -> wgt::HalCounters {
|
||||
self.raw
|
||||
.as_ref()
|
||||
.map(|raw| raw.get_internal_counters())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: HalApi> Device<A> {
|
||||
|
@ -181,6 +181,7 @@ impl super::Device {
|
||||
null_rtv_handle,
|
||||
mem_allocator,
|
||||
dxc_container,
|
||||
counters: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -377,6 +378,8 @@ impl crate::Device for super::Device {
|
||||
unsafe { resource.SetName(cwstr.as_ptr()) };
|
||||
}
|
||||
|
||||
self.counters.buffers.add(1);
|
||||
|
||||
Ok(super::Buffer {
|
||||
resource,
|
||||
size,
|
||||
@ -388,11 +391,14 @@ impl crate::Device for super::Device {
|
||||
// Only happens when it's using the windows_rs feature and there's an allocation
|
||||
if let Some(alloc) = buffer.allocation.take() {
|
||||
super::suballocation::free_buffer_allocation(
|
||||
self,
|
||||
alloc,
|
||||
// SAFETY: for allocations to exist, the allocator must exist
|
||||
unsafe { self.mem_allocator.as_ref().unwrap_unchecked() },
|
||||
);
|
||||
}
|
||||
|
||||
self.counters.buffers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn map_buffer(
|
||||
@ -459,6 +465,8 @@ impl crate::Device for super::Device {
|
||||
unsafe { resource.SetName(cwstr.as_ptr()) };
|
||||
}
|
||||
|
||||
self.counters.textures.add(1);
|
||||
|
||||
Ok(super::Texture {
|
||||
resource,
|
||||
format: desc.format,
|
||||
@ -473,11 +481,14 @@ impl crate::Device for super::Device {
|
||||
unsafe fn destroy_texture(&self, mut texture: super::Texture) {
|
||||
if let Some(alloc) = texture.allocation.take() {
|
||||
super::suballocation::free_texture_allocation(
|
||||
self,
|
||||
alloc,
|
||||
// SAFETY: for allocations to exist, the allocator must exist
|
||||
unsafe { self.mem_allocator.as_ref().unwrap_unchecked() },
|
||||
);
|
||||
}
|
||||
|
||||
self.counters.textures.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_texture_view(
|
||||
@ -487,6 +498,8 @@ impl crate::Device for super::Device {
|
||||
) -> Result<super::TextureView, DeviceError> {
|
||||
let view_desc = desc.to_internal(texture);
|
||||
|
||||
self.counters.texture_views.add(1);
|
||||
|
||||
Ok(super::TextureView {
|
||||
raw_format: view_desc.rtv_dsv_format,
|
||||
aspects: view_desc.aspects,
|
||||
@ -583,6 +596,7 @@ impl crate::Device for super::Device {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_texture_view(&self, view: super::TextureView) {
|
||||
if view.handle_srv.is_some() || view.handle_uav.is_some() {
|
||||
let mut pool = self.srv_uav_pool.lock();
|
||||
@ -605,6 +619,8 @@ impl crate::Device for super::Device {
|
||||
pool.free_handle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
self.counters.texture_views.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_sampler(
|
||||
@ -643,10 +659,14 @@ impl crate::Device for super::Device {
|
||||
desc.lod_clamp.clone(),
|
||||
);
|
||||
|
||||
self.counters.samplers.add(1);
|
||||
|
||||
Ok(super::Sampler { handle })
|
||||
}
|
||||
|
||||
unsafe fn destroy_sampler(&self, sampler: super::Sampler) {
|
||||
self.sampler_pool.lock().free_handle(sampler.handle);
|
||||
self.counters.samplers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_command_encoder(
|
||||
@ -663,6 +683,8 @@ impl crate::Device for super::Device {
|
||||
unsafe { allocator.SetName(cwstr.as_ptr()) };
|
||||
}
|
||||
|
||||
self.counters.command_encoders.add(1);
|
||||
|
||||
Ok(super::CommandEncoder {
|
||||
allocator,
|
||||
device: self.raw.clone(),
|
||||
@ -675,7 +697,10 @@ impl crate::Device for super::Device {
|
||||
end_of_pass_timer_query: None,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {}
|
||||
|
||||
unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {
|
||||
self.counters.command_encoders.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group_layout(
|
||||
&self,
|
||||
@ -698,6 +723,8 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
}
|
||||
|
||||
self.counters.bind_group_layouts.add(1);
|
||||
|
||||
let num_views = num_buffer_views + num_texture_views;
|
||||
Ok(super::BindGroupLayout {
|
||||
entries: desc.entries.to_vec(),
|
||||
@ -724,7 +751,10 @@ impl crate::Device for super::Device {
|
||||
copy_counts: vec![1; num_views.max(num_samplers) as usize],
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {}
|
||||
|
||||
unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {
|
||||
self.counters.bind_group_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_layout(
|
||||
&self,
|
||||
@ -1063,6 +1093,8 @@ impl crate::Device for super::Device {
|
||||
unsafe { raw.SetName(cwstr.as_ptr()) };
|
||||
}
|
||||
|
||||
self.counters.pipeline_layouts.add(1);
|
||||
|
||||
Ok(super::PipelineLayout {
|
||||
shared: super::PipelineLayoutShared {
|
||||
signature: raw,
|
||||
@ -1081,7 +1113,10 @@ impl crate::Device for super::Device {
|
||||
},
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {}
|
||||
|
||||
unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {
|
||||
self.counters.pipeline_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group(
|
||||
&self,
|
||||
@ -1253,12 +1288,15 @@ impl crate::Device for super::Device {
|
||||
None => None,
|
||||
};
|
||||
|
||||
self.counters.bind_groups.add(1);
|
||||
|
||||
Ok(super::BindGroup {
|
||||
handle_views,
|
||||
handle_samplers,
|
||||
dynamic_buffers,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_bind_group(&self, group: super::BindGroup) {
|
||||
if let Some(dual) = group.handle_views {
|
||||
self.shared.heap_views.free_slice(dual);
|
||||
@ -1266,6 +1304,8 @@ impl crate::Device for super::Device {
|
||||
if let Some(dual) = group.handle_samplers {
|
||||
self.shared.heap_samplers.free_slice(dual);
|
||||
}
|
||||
|
||||
self.counters.bind_groups.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_shader_module(
|
||||
@ -1273,6 +1313,8 @@ impl crate::Device for super::Device {
|
||||
desc: &crate::ShaderModuleDescriptor,
|
||||
shader: crate::ShaderInput,
|
||||
) -> Result<super::ShaderModule, crate::ShaderError> {
|
||||
self.counters.shader_modules.add(1);
|
||||
|
||||
let raw_name = desc.label.and_then(|label| ffi::CString::new(label).ok());
|
||||
match shader {
|
||||
crate::ShaderInput::Naga(naga) => Ok(super::ShaderModule { naga, raw_name }),
|
||||
@ -1282,6 +1324,7 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
}
|
||||
unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {
|
||||
self.counters.shader_modules.sub(1);
|
||||
// just drop
|
||||
}
|
||||
|
||||
@ -1463,6 +1506,8 @@ impl crate::Device for super::Device {
|
||||
unsafe { raw.SetName(cwstr.as_ptr()) };
|
||||
}
|
||||
|
||||
self.counters.render_pipelines.add(1);
|
||||
|
||||
Ok(super::RenderPipeline {
|
||||
raw,
|
||||
layout: desc.layout.shared.clone(),
|
||||
@ -1470,7 +1515,9 @@ impl crate::Device for super::Device {
|
||||
vertex_strides,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_render_pipeline(&self, _pipeline: super::RenderPipeline) {}
|
||||
unsafe fn destroy_render_pipeline(&self, _pipeline: super::RenderPipeline) {
|
||||
self.counters.render_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_compute_pipeline(
|
||||
&self,
|
||||
@ -1502,12 +1549,17 @@ impl crate::Device for super::Device {
|
||||
unsafe { raw.SetName(cwstr.as_ptr()) };
|
||||
}
|
||||
|
||||
self.counters.compute_pipelines.add(1);
|
||||
|
||||
Ok(super::ComputePipeline {
|
||||
raw,
|
||||
layout: desc.layout.shared.clone(),
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_compute_pipeline(&self, _pipeline: super::ComputePipeline) {}
|
||||
|
||||
unsafe fn destroy_compute_pipeline(&self, _pipeline: super::ComputePipeline) {
|
||||
self.counters.compute_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_cache(
|
||||
&self,
|
||||
@ -1548,9 +1600,14 @@ impl crate::Device for super::Device {
|
||||
unsafe { raw.SetName(cwstr.as_ptr()) };
|
||||
}
|
||||
|
||||
self.counters.query_sets.add(1);
|
||||
|
||||
Ok(super::QuerySet { raw, raw_ty })
|
||||
}
|
||||
unsafe fn destroy_query_set(&self, _set: super::QuerySet) {}
|
||||
|
||||
unsafe fn destroy_query_set(&self, _set: super::QuerySet) {
|
||||
self.counters.query_sets.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_fence(&self) -> Result<super::Fence, DeviceError> {
|
||||
let mut raw = d3d12::Fence::null();
|
||||
@ -1565,9 +1622,14 @@ impl crate::Device for super::Device {
|
||||
hr.into_device_result("Fence creation")?;
|
||||
null_comptr_check(&raw)?;
|
||||
|
||||
self.counters.fences.add(1);
|
||||
|
||||
Ok(super::Fence { raw })
|
||||
}
|
||||
unsafe fn destroy_fence(&self, _fence: super::Fence) {}
|
||||
unsafe fn destroy_fence(&self, _fence: super::Fence) {
|
||||
self.counters.fences.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn get_fence_value(
|
||||
&self,
|
||||
fence: &super::Fence,
|
||||
@ -1711,4 +1773,8 @@ impl crate::Device for super::Device {
|
||||
// Destroy a D3D12 resource as per-usual.
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_internal_counters(&self) -> wgt::HalCounters {
|
||||
self.counters.clone()
|
||||
}
|
||||
}
|
||||
|
@ -261,6 +261,7 @@ pub struct Device {
|
||||
null_rtv_handle: descriptor::Handle,
|
||||
mem_allocator: Option<Mutex<suballocation::GpuAllocatorWrapper>>,
|
||||
dxc_container: Option<Arc<shader_compilation::DxcContainer>>,
|
||||
counters: wgt::HalCounters,
|
||||
}
|
||||
|
||||
unsafe impl Send for Device {}
|
||||
|
@ -118,6 +118,11 @@ mod placed {
|
||||
|
||||
null_comptr_check(resource)?;
|
||||
|
||||
device
|
||||
.counters
|
||||
.buffer_memory
|
||||
.add(allocation.size() as isize);
|
||||
|
||||
Ok((hr, Some(AllocationWrapper { allocation })))
|
||||
}
|
||||
|
||||
@ -167,13 +172,23 @@ mod placed {
|
||||
|
||||
null_comptr_check(resource)?;
|
||||
|
||||
device
|
||||
.counters
|
||||
.texture_memory
|
||||
.add(allocation.size() as isize);
|
||||
|
||||
Ok((hr, Some(AllocationWrapper { allocation })))
|
||||
}
|
||||
|
||||
pub(crate) fn free_buffer_allocation(
|
||||
device: &crate::dx12::Device,
|
||||
allocation: AllocationWrapper,
|
||||
allocator: &Mutex<GpuAllocatorWrapper>,
|
||||
) {
|
||||
device
|
||||
.counters
|
||||
.buffer_memory
|
||||
.sub(allocation.allocation.size() as isize);
|
||||
match allocator.lock().allocator.free(allocation.allocation) {
|
||||
Ok(_) => (),
|
||||
// TODO: Don't panic here
|
||||
@ -182,9 +197,14 @@ mod placed {
|
||||
}
|
||||
|
||||
pub(crate) fn free_texture_allocation(
|
||||
device: &crate::dx12::Device,
|
||||
allocation: AllocationWrapper,
|
||||
allocator: &Mutex<GpuAllocatorWrapper>,
|
||||
) {
|
||||
device
|
||||
.counters
|
||||
.texture_memory
|
||||
.sub(allocation.allocation.size() as isize);
|
||||
match allocator.lock().allocator.free(allocation.allocation) {
|
||||
Ok(_) => (),
|
||||
// TODO: Don't panic here
|
||||
@ -352,6 +372,7 @@ mod committed {
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn free_buffer_allocation(
|
||||
_device: &crate::dx12::Device,
|
||||
_allocation: AllocationWrapper,
|
||||
_allocator: &Mutex<GpuAllocatorWrapper>,
|
||||
) {
|
||||
@ -360,6 +381,7 @@ mod committed {
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn free_texture_allocation(
|
||||
_device: &crate::dx12::Device,
|
||||
_allocation: AllocationWrapper,
|
||||
_allocator: &Mutex<GpuAllocatorWrapper>,
|
||||
) {
|
||||
|
@ -276,6 +276,10 @@ impl crate::Device for Context {
|
||||
Default::default()
|
||||
}
|
||||
unsafe fn destroy_acceleration_structure(&self, _acceleration_structure: Resource) {}
|
||||
|
||||
fn get_internal_counters(&self) -> wgt::HalCounters {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CommandEncoder for Encoder {
|
||||
|
@ -967,6 +967,7 @@ impl crate::Adapter for super::Adapter {
|
||||
main_vao,
|
||||
#[cfg(all(native, feature = "renderdoc"))]
|
||||
render_doc: Default::default(),
|
||||
counters: Default::default(),
|
||||
},
|
||||
queue: super::Queue {
|
||||
shared: Arc::clone(&self.shared),
|
||||
|
@ -632,6 +632,8 @@ impl crate::Device for super::Device {
|
||||
None
|
||||
};
|
||||
|
||||
self.counters.buffers.add(1);
|
||||
|
||||
Ok(super::Buffer {
|
||||
raw,
|
||||
target,
|
||||
@ -640,11 +642,14 @@ impl crate::Device for super::Device {
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_buffer(&self, buffer: super::Buffer) {
|
||||
if let Some(raw) = buffer.raw {
|
||||
let gl = &self.shared.context.lock();
|
||||
unsafe { gl.delete_buffer(raw) };
|
||||
}
|
||||
|
||||
self.counters.buffers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn map_buffer(
|
||||
@ -941,6 +946,8 @@ impl crate::Device for super::Device {
|
||||
super::TextureInner::Texture { raw, target }
|
||||
};
|
||||
|
||||
self.counters.textures.add(1);
|
||||
|
||||
Ok(super::Texture {
|
||||
inner,
|
||||
drop_guard: None,
|
||||
@ -951,6 +958,7 @@ impl crate::Device for super::Device {
|
||||
copy_size: desc.copy_extent(),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_texture(&self, texture: super::Texture) {
|
||||
if texture.drop_guard.is_none() {
|
||||
let gl = &self.shared.context.lock();
|
||||
@ -970,6 +978,8 @@ impl crate::Device for super::Device {
|
||||
// For clarity, we explicitly drop the drop guard. Although this has no real semantic effect as the
|
||||
// end of the scope will drop the drop guard since this function takes ownership of the texture.
|
||||
drop(texture.drop_guard);
|
||||
|
||||
self.counters.textures.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_texture_view(
|
||||
@ -977,6 +987,7 @@ impl crate::Device for super::Device {
|
||||
texture: &super::Texture,
|
||||
desc: &crate::TextureViewDescriptor,
|
||||
) -> Result<super::TextureView, crate::DeviceError> {
|
||||
self.counters.texture_views.add(1);
|
||||
Ok(super::TextureView {
|
||||
//TODO: use `conv::map_view_dimension(desc.dimension)`?
|
||||
inner: texture.inner.clone(),
|
||||
@ -986,7 +997,10 @@ impl crate::Device for super::Device {
|
||||
format: texture.format,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_texture_view(&self, _view: super::TextureView) {}
|
||||
|
||||
unsafe fn destroy_texture_view(&self, _view: super::TextureView) {
|
||||
self.counters.texture_views.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_sampler(
|
||||
&self,
|
||||
@ -1080,34 +1094,47 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
}
|
||||
|
||||
self.counters.samplers.add(1);
|
||||
|
||||
Ok(super::Sampler { raw })
|
||||
}
|
||||
|
||||
unsafe fn destroy_sampler(&self, sampler: super::Sampler) {
|
||||
let gl = &self.shared.context.lock();
|
||||
unsafe { gl.delete_sampler(sampler.raw) };
|
||||
self.counters.samplers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_command_encoder(
|
||||
&self,
|
||||
_desc: &crate::CommandEncoderDescriptor<super::Api>,
|
||||
) -> Result<super::CommandEncoder, crate::DeviceError> {
|
||||
self.counters.command_encoders.add(1);
|
||||
|
||||
Ok(super::CommandEncoder {
|
||||
cmd_buffer: super::CommandBuffer::default(),
|
||||
state: Default::default(),
|
||||
private_caps: self.shared.private_caps,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {}
|
||||
|
||||
unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {
|
||||
self.counters.command_encoders.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group_layout(
|
||||
&self,
|
||||
desc: &crate::BindGroupLayoutDescriptor,
|
||||
) -> Result<super::BindGroupLayout, crate::DeviceError> {
|
||||
self.counters.bind_group_layouts.add(1);
|
||||
Ok(super::BindGroupLayout {
|
||||
entries: Arc::from(desc.entries),
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {}
|
||||
|
||||
unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {
|
||||
self.counters.bind_group_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_layout(
|
||||
&self,
|
||||
@ -1184,6 +1211,8 @@ impl crate::Device for super::Device {
|
||||
});
|
||||
}
|
||||
|
||||
self.counters.pipeline_layouts.add(1);
|
||||
|
||||
Ok(super::PipelineLayout {
|
||||
group_infos: group_infos.into_boxed_slice(),
|
||||
naga_options: glsl::Options {
|
||||
@ -1194,7 +1223,10 @@ impl crate::Device for super::Device {
|
||||
},
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {}
|
||||
|
||||
unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {
|
||||
self.counters.pipeline_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group(
|
||||
&self,
|
||||
@ -1270,17 +1302,24 @@ impl crate::Device for super::Device {
|
||||
contents.push(binding);
|
||||
}
|
||||
|
||||
self.counters.bind_groups.add(1);
|
||||
|
||||
Ok(super::BindGroup {
|
||||
contents: contents.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_bind_group(&self, _group: super::BindGroup) {}
|
||||
|
||||
unsafe fn destroy_bind_group(&self, _group: super::BindGroup) {
|
||||
self.counters.bind_groups.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_shader_module(
|
||||
&self,
|
||||
desc: &crate::ShaderModuleDescriptor,
|
||||
shader: crate::ShaderInput,
|
||||
) -> Result<super::ShaderModule, crate::ShaderError> {
|
||||
self.counters.shader_modules.add(1);
|
||||
|
||||
Ok(super::ShaderModule {
|
||||
naga: match shader {
|
||||
crate::ShaderInput::SpirV(_) => {
|
||||
@ -1292,7 +1331,10 @@ impl crate::Device for super::Device {
|
||||
id: self.shared.next_shader_id.fetch_add(1, Ordering::Relaxed),
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {}
|
||||
|
||||
unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {
|
||||
self.counters.shader_modules.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_render_pipeline(
|
||||
&self,
|
||||
@ -1341,6 +1383,8 @@ impl crate::Device for super::Device {
|
||||
targets.into_boxed_slice()
|
||||
};
|
||||
|
||||
self.counters.render_pipelines.add(1);
|
||||
|
||||
Ok(super::RenderPipeline {
|
||||
inner,
|
||||
primitive: desc.primitive,
|
||||
@ -1363,6 +1407,7 @@ impl crate::Device for super::Device {
|
||||
alpha_to_coverage_enabled: desc.multisample.alpha_to_coverage_enabled,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) {
|
||||
let mut program_cache = self.shared.program_cache.lock();
|
||||
// If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache`
|
||||
@ -1377,6 +1422,8 @@ impl crate::Device for super::Device {
|
||||
let gl = &self.shared.context.lock();
|
||||
unsafe { gl.delete_program(pipeline.inner.program) };
|
||||
}
|
||||
|
||||
self.counters.render_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_compute_pipeline(
|
||||
@ -1388,8 +1435,11 @@ impl crate::Device for super::Device {
|
||||
shaders.push((naga::ShaderStage::Compute, &desc.stage));
|
||||
let inner = unsafe { self.create_pipeline(gl, shaders, desc.layout, desc.label, None) }?;
|
||||
|
||||
self.counters.compute_pipelines.add(1);
|
||||
|
||||
Ok(super::ComputePipeline { inner })
|
||||
}
|
||||
|
||||
unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) {
|
||||
let mut program_cache = self.shared.program_cache.lock();
|
||||
// If the pipeline only has 2 strong references remaining, they're `pipeline` and `program_cache``
|
||||
@ -1404,6 +1454,8 @@ impl crate::Device for super::Device {
|
||||
let gl = &self.shared.context.lock();
|
||||
unsafe { gl.delete_program(pipeline.inner.program) };
|
||||
}
|
||||
|
||||
self.counters.compute_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_cache(
|
||||
@ -1437,6 +1489,8 @@ impl crate::Device for super::Device {
|
||||
queries.push(query);
|
||||
}
|
||||
|
||||
self.counters.query_sets.add(1);
|
||||
|
||||
Ok(super::QuerySet {
|
||||
queries: queries.into_boxed_slice(),
|
||||
target: match desc.ty {
|
||||
@ -1446,24 +1500,31 @@ impl crate::Device for super::Device {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_query_set(&self, set: super::QuerySet) {
|
||||
let gl = &self.shared.context.lock();
|
||||
for &query in set.queries.iter() {
|
||||
unsafe { gl.delete_query(query) };
|
||||
}
|
||||
self.counters.query_sets.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_fence(&self) -> Result<super::Fence, crate::DeviceError> {
|
||||
self.counters.fences.add(1);
|
||||
Ok(super::Fence {
|
||||
last_completed: 0,
|
||||
pending: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_fence(&self, fence: super::Fence) {
|
||||
let gl = &self.shared.context.lock();
|
||||
for (_, sync) in fence.pending {
|
||||
unsafe { gl.delete_sync(sync) };
|
||||
}
|
||||
self.counters.fences.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn get_fence_value(
|
||||
&self,
|
||||
fence: &super::Fence,
|
||||
@ -1542,6 +1603,10 @@ impl crate::Device for super::Device {
|
||||
unimplemented!()
|
||||
}
|
||||
unsafe fn destroy_acceleration_structure(&self, _acceleration_structure: ()) {}
|
||||
|
||||
fn get_internal_counters(&self) -> wgt::HalCounters {
|
||||
self.counters.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(send_sync)]
|
||||
|
@ -268,6 +268,7 @@ pub struct Device {
|
||||
main_vao: glow::VertexArray,
|
||||
#[cfg(all(native, feature = "renderdoc"))]
|
||||
render_doc: crate::auxil::renderdoc::RenderDoc,
|
||||
counters: wgt::HalCounters,
|
||||
}
|
||||
|
||||
pub struct ShaderClearProgram {
|
||||
|
@ -884,6 +884,8 @@ pub trait Device: WasmNotSendSync {
|
||||
&self,
|
||||
acceleration_structure: <Self::A as Api>::AccelerationStructure,
|
||||
);
|
||||
|
||||
fn get_internal_counters(&self) -> wgt::HalCounters;
|
||||
}
|
||||
|
||||
pub trait Queue: WasmNotSendSync {
|
||||
|
@ -62,6 +62,7 @@ impl crate::Adapter for super::Adapter {
|
||||
device: super::Device {
|
||||
shared: Arc::clone(&self.shared),
|
||||
features,
|
||||
counters: Default::default(),
|
||||
},
|
||||
queue: super::Queue {
|
||||
raw: Arc::new(Mutex::new(queue)),
|
||||
|
@ -305,6 +305,7 @@ impl super::Device {
|
||||
super::Device {
|
||||
shared: Arc::new(super::AdapterShared::new(raw)),
|
||||
features,
|
||||
counters: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,13 +346,16 @@ impl crate::Device for super::Device {
|
||||
if let Some(label) = desc.label {
|
||||
raw.set_label(label);
|
||||
}
|
||||
self.counters.buffers.add(1);
|
||||
Ok(super::Buffer {
|
||||
raw,
|
||||
size: desc.size,
|
||||
})
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_buffer(&self, _buffer: super::Buffer) {}
|
||||
unsafe fn destroy_buffer(&self, _buffer: super::Buffer) {
|
||||
self.counters.buffers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn map_buffer(
|
||||
&self,
|
||||
@ -418,6 +422,8 @@ impl crate::Device for super::Device {
|
||||
raw.set_label(label);
|
||||
}
|
||||
|
||||
self.counters.textures.add(1);
|
||||
|
||||
Ok(super::Texture {
|
||||
raw,
|
||||
format: desc.format,
|
||||
@ -429,7 +435,9 @@ impl crate::Device for super::Device {
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn destroy_texture(&self, _texture: super::Texture) {}
|
||||
unsafe fn destroy_texture(&self, _texture: super::Texture) {
|
||||
self.counters.textures.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_texture_view(
|
||||
&self,
|
||||
@ -489,9 +497,14 @@ impl crate::Device for super::Device {
|
||||
})
|
||||
};
|
||||
|
||||
self.counters.texture_views.add(1);
|
||||
|
||||
Ok(super::TextureView { raw, aspects })
|
||||
}
|
||||
unsafe fn destroy_texture_view(&self, _view: super::TextureView) {}
|
||||
|
||||
unsafe fn destroy_texture_view(&self, _view: super::TextureView) {
|
||||
self.counters.texture_views.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_sampler(
|
||||
&self,
|
||||
@ -548,15 +561,20 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
let raw = self.shared.device.lock().new_sampler(&descriptor);
|
||||
|
||||
self.counters.samplers.add(1);
|
||||
|
||||
Ok(super::Sampler { raw })
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_sampler(&self, _sampler: super::Sampler) {}
|
||||
unsafe fn destroy_sampler(&self, _sampler: super::Sampler) {
|
||||
self.counters.samplers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_command_encoder(
|
||||
&self,
|
||||
desc: &crate::CommandEncoderDescriptor<super::Api>,
|
||||
) -> Result<super::CommandEncoder, crate::DeviceError> {
|
||||
self.counters.command_encoders.add(1);
|
||||
Ok(super::CommandEncoder {
|
||||
shared: Arc::clone(&self.shared),
|
||||
raw_queue: Arc::clone(&desc.queue.raw),
|
||||
@ -565,17 +583,25 @@ impl crate::Device for super::Device {
|
||||
temp: super::Temp::default(),
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {}
|
||||
|
||||
unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {
|
||||
self.counters.command_encoders.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group_layout(
|
||||
&self,
|
||||
desc: &crate::BindGroupLayoutDescriptor,
|
||||
) -> DeviceResult<super::BindGroupLayout> {
|
||||
self.counters.bind_group_layouts.add(1);
|
||||
|
||||
Ok(super::BindGroupLayout {
|
||||
entries: Arc::from(desc.entries),
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {}
|
||||
|
||||
unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {
|
||||
self.counters.bind_group_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_layout(
|
||||
&self,
|
||||
@ -736,6 +762,8 @@ impl crate::Device for super::Device {
|
||||
resources: info.resources,
|
||||
});
|
||||
|
||||
self.counters.pipeline_layouts.add(1);
|
||||
|
||||
Ok(super::PipelineLayout {
|
||||
bind_group_infos,
|
||||
push_constants_infos,
|
||||
@ -744,7 +772,10 @@ impl crate::Device for super::Device {
|
||||
per_stage_map,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {}
|
||||
|
||||
unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {
|
||||
self.counters.pipeline_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group(
|
||||
&self,
|
||||
@ -831,16 +862,22 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
}
|
||||
|
||||
self.counters.bind_groups.add(1);
|
||||
|
||||
Ok(bg)
|
||||
}
|
||||
|
||||
unsafe fn destroy_bind_group(&self, _group: super::BindGroup) {}
|
||||
unsafe fn destroy_bind_group(&self, _group: super::BindGroup) {
|
||||
self.counters.bind_groups.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_shader_module(
|
||||
&self,
|
||||
desc: &crate::ShaderModuleDescriptor,
|
||||
shader: crate::ShaderInput,
|
||||
) -> Result<super::ShaderModule, crate::ShaderError> {
|
||||
self.counters.shader_modules.add(1);
|
||||
|
||||
match shader {
|
||||
crate::ShaderInput::Naga(naga) => Ok(super::ShaderModule {
|
||||
naga,
|
||||
@ -851,7 +888,10 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {}
|
||||
|
||||
unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {
|
||||
self.counters.shader_modules.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_render_pipeline(
|
||||
&self,
|
||||
@ -1094,6 +1134,8 @@ impl crate::Device for super::Device {
|
||||
)
|
||||
})?;
|
||||
|
||||
self.counters.render_pipelines.add(1);
|
||||
|
||||
Ok(super::RenderPipeline {
|
||||
raw,
|
||||
vs_lib,
|
||||
@ -1117,7 +1159,10 @@ impl crate::Device for super::Device {
|
||||
})
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_render_pipeline(&self, _pipeline: super::RenderPipeline) {}
|
||||
|
||||
unsafe fn destroy_render_pipeline(&self, _pipeline: super::RenderPipeline) {
|
||||
self.counters.render_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_compute_pipeline(
|
||||
&self,
|
||||
@ -1165,6 +1210,8 @@ impl crate::Device for super::Device {
|
||||
)
|
||||
})?;
|
||||
|
||||
self.counters.compute_pipelines.add(1);
|
||||
|
||||
Ok(super::ComputePipeline {
|
||||
raw,
|
||||
cs_info,
|
||||
@ -1174,7 +1221,10 @@ impl crate::Device for super::Device {
|
||||
})
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_compute_pipeline(&self, _pipeline: super::ComputePipeline) {}
|
||||
|
||||
unsafe fn destroy_compute_pipeline(&self, _pipeline: super::ComputePipeline) {
|
||||
self.counters.compute_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_cache(
|
||||
&self,
|
||||
@ -1237,6 +1287,8 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
};
|
||||
|
||||
self.counters.query_sets.add(1);
|
||||
|
||||
Ok(super::QuerySet {
|
||||
raw_buffer: destination_buffer,
|
||||
counter_sample_buffer: Some(counter_sample_buffer),
|
||||
@ -1249,15 +1301,23 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_query_set(&self, _set: super::QuerySet) {}
|
||||
|
||||
unsafe fn destroy_query_set(&self, _set: super::QuerySet) {
|
||||
self.counters.query_sets.add(1);
|
||||
}
|
||||
|
||||
unsafe fn create_fence(&self) -> DeviceResult<super::Fence> {
|
||||
self.counters.fences.add(1);
|
||||
Ok(super::Fence {
|
||||
completed_value: Arc::new(atomic::AtomicU64::new(0)),
|
||||
pending_command_buffers: Vec::new(),
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_fence(&self, _fence: super::Fence) {}
|
||||
|
||||
unsafe fn destroy_fence(&self, _fence: super::Fence) {
|
||||
self.counters.fences.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn get_fence_value(&self, fence: &super::Fence) -> DeviceResult<crate::FenceValue> {
|
||||
let mut max_value = fence.completed_value.load(atomic::Ordering::Acquire);
|
||||
for &(value, ref cmd_buf) in fence.pending_command_buffers.iter() {
|
||||
@ -1348,4 +1408,8 @@ impl crate::Device for super::Device {
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_internal_counters(&self) -> wgt::HalCounters {
|
||||
self.counters.clone()
|
||||
}
|
||||
}
|
||||
|
@ -339,6 +339,7 @@ impl Queue {
|
||||
pub struct Device {
|
||||
shared: Arc<AdapterShared>,
|
||||
features: wgt::Features,
|
||||
counters: wgt::HalCounters,
|
||||
}
|
||||
|
||||
pub struct Surface {
|
||||
|
@ -1819,6 +1819,7 @@ impl super::Adapter {
|
||||
workarounds: self.workarounds,
|
||||
render_passes: Mutex::new(Default::default()),
|
||||
framebuffers: Mutex::new(Default::default()),
|
||||
memory_allocations_counter: Default::default(),
|
||||
});
|
||||
|
||||
let relay_semaphores = super::RelaySemaphores::new(&shared)?;
|
||||
@ -1881,6 +1882,7 @@ impl super::Adapter {
|
||||
naga_options,
|
||||
#[cfg(feature = "renderdoc")]
|
||||
render_doc: Default::default(),
|
||||
counters: Default::default(),
|
||||
};
|
||||
|
||||
Ok(crate::OpenDevice { device, queue })
|
||||
|
@ -312,7 +312,10 @@ impl gpu_alloc::MemoryDevice<vk::DeviceMemory> for super::DeviceShared {
|
||||
}
|
||||
|
||||
match unsafe { self.raw.allocate_memory(&info, None) } {
|
||||
Ok(memory) => Ok(memory),
|
||||
Ok(memory) => {
|
||||
self.memory_allocations_counter.add(1);
|
||||
Ok(memory)
|
||||
}
|
||||
Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => {
|
||||
Err(gpu_alloc::OutOfMemory::OutOfDeviceMemory)
|
||||
}
|
||||
@ -325,6 +328,8 @@ impl gpu_alloc::MemoryDevice<vk::DeviceMemory> for super::DeviceShared {
|
||||
}
|
||||
|
||||
unsafe fn deallocate_memory(&self, memory: vk::DeviceMemory) {
|
||||
self.memory_allocations_counter.sub(1);
|
||||
|
||||
unsafe { self.raw.free_memory(memory, None) };
|
||||
}
|
||||
|
||||
@ -910,6 +915,9 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.set_object_name(raw, label) };
|
||||
}
|
||||
|
||||
self.counters.buffer_memory.add(block.size() as isize);
|
||||
self.counters.buffers.add(1);
|
||||
|
||||
Ok(super::Buffer {
|
||||
raw,
|
||||
block: Some(Mutex::new(block)),
|
||||
@ -918,12 +926,12 @@ impl crate::Device for super::Device {
|
||||
unsafe fn destroy_buffer(&self, buffer: super::Buffer) {
|
||||
unsafe { self.shared.raw.destroy_buffer(buffer.raw, None) };
|
||||
if let Some(block) = buffer.block {
|
||||
unsafe {
|
||||
self.mem_allocator
|
||||
.lock()
|
||||
.dealloc(&*self.shared, block.into_inner())
|
||||
};
|
||||
let block = block.into_inner();
|
||||
self.counters.buffer_memory.sub(block.size() as isize);
|
||||
unsafe { self.mem_allocator.lock().dealloc(&*self.shared, block) };
|
||||
}
|
||||
|
||||
self.counters.buffers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn map_buffer(
|
||||
@ -1049,6 +1057,8 @@ impl crate::Device for super::Device {
|
||||
)?
|
||||
};
|
||||
|
||||
self.counters.texture_memory.add(block.size() as isize);
|
||||
|
||||
unsafe {
|
||||
self.shared
|
||||
.raw
|
||||
@ -1059,6 +1069,8 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.set_object_name(raw, label) };
|
||||
}
|
||||
|
||||
self.counters.textures.add(1);
|
||||
|
||||
Ok(super::Texture {
|
||||
raw,
|
||||
drop_guard: None,
|
||||
@ -1075,8 +1087,12 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.raw.destroy_image(texture.raw, None) };
|
||||
}
|
||||
if let Some(block) = texture.block {
|
||||
self.counters.texture_memory.sub(block.size() as isize);
|
||||
|
||||
unsafe { self.mem_allocator.lock().dealloc(&*self.shared, block) };
|
||||
}
|
||||
|
||||
self.counters.textures.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_texture_view(
|
||||
@ -1126,6 +1142,8 @@ impl crate::Device for super::Device {
|
||||
.collect(),
|
||||
};
|
||||
|
||||
self.counters.texture_views.add(1);
|
||||
|
||||
Ok(super::TextureView {
|
||||
raw,
|
||||
layers,
|
||||
@ -1143,6 +1161,8 @@ impl crate::Device for super::Device {
|
||||
fbuf_lock.retain(|key, _| !key.attachments.iter().any(|at| at.raw == view.raw));
|
||||
}
|
||||
unsafe { self.shared.raw.destroy_image_view(view.raw, None) };
|
||||
|
||||
self.counters.texture_views.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_sampler(
|
||||
@ -1184,10 +1204,14 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.set_object_name(raw, label) };
|
||||
}
|
||||
|
||||
self.counters.samplers.add(1);
|
||||
|
||||
Ok(super::Sampler { raw })
|
||||
}
|
||||
unsafe fn destroy_sampler(&self, sampler: super::Sampler) {
|
||||
unsafe { self.shared.raw.destroy_sampler(sampler.raw, None) };
|
||||
|
||||
self.counters.samplers.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_command_encoder(
|
||||
@ -1199,6 +1223,8 @@ impl crate::Device for super::Device {
|
||||
.flags(vk::CommandPoolCreateFlags::TRANSIENT);
|
||||
let raw = unsafe { self.shared.raw.create_command_pool(&vk_info, None)? };
|
||||
|
||||
self.counters.command_encoders.add(1);
|
||||
|
||||
Ok(super::CommandEncoder {
|
||||
raw,
|
||||
device: Arc::clone(&self.shared),
|
||||
@ -1219,6 +1245,8 @@ impl crate::Device for super::Device {
|
||||
// fields.
|
||||
self.shared.raw.destroy_command_pool(cmd_encoder.raw, None);
|
||||
}
|
||||
|
||||
self.counters.command_encoders.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group_layout(
|
||||
@ -1339,6 +1367,8 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.set_object_name(raw, label) };
|
||||
}
|
||||
|
||||
self.counters.bind_group_layouts.add(1);
|
||||
|
||||
Ok(super::BindGroupLayout {
|
||||
raw,
|
||||
desc_count,
|
||||
@ -1352,6 +1382,8 @@ impl crate::Device for super::Device {
|
||||
.raw
|
||||
.destroy_descriptor_set_layout(bg_layout.raw, None)
|
||||
};
|
||||
|
||||
self.counters.bind_group_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_layout(
|
||||
@ -1403,6 +1435,8 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
}
|
||||
|
||||
self.counters.pipeline_layouts.add(1);
|
||||
|
||||
Ok(super::PipelineLayout {
|
||||
raw,
|
||||
binding_arrays,
|
||||
@ -1414,6 +1448,8 @@ impl crate::Device for super::Device {
|
||||
.raw
|
||||
.destroy_pipeline_layout(pipeline_layout.raw, None)
|
||||
};
|
||||
|
||||
self.counters.pipeline_layouts.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_bind_group(
|
||||
@ -1596,14 +1632,20 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
|
||||
unsafe { self.shared.raw.update_descriptor_sets(&writes, &[]) };
|
||||
|
||||
self.counters.bind_groups.add(1);
|
||||
|
||||
Ok(super::BindGroup { set })
|
||||
}
|
||||
|
||||
unsafe fn destroy_bind_group(&self, group: super::BindGroup) {
|
||||
unsafe {
|
||||
self.desc_allocator
|
||||
.lock()
|
||||
.free(&*self.shared, Some(group.set))
|
||||
};
|
||||
|
||||
self.counters.bind_groups.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_shader_module(
|
||||
@ -1661,8 +1703,11 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.set_object_name(raw, label) };
|
||||
}
|
||||
|
||||
self.counters.shader_modules.add(1);
|
||||
|
||||
Ok(super::ShaderModule::Raw(raw))
|
||||
}
|
||||
|
||||
unsafe fn destroy_shader_module(&self, module: super::ShaderModule) {
|
||||
match module {
|
||||
super::ShaderModule::Raw(raw) => {
|
||||
@ -1670,6 +1715,8 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
super::ShaderModule::Intermediate { .. } => {}
|
||||
}
|
||||
|
||||
self.counters.shader_modules.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_render_pipeline(
|
||||
@ -1900,10 +1947,14 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
|
||||
}
|
||||
|
||||
self.counters.render_pipelines.add(1);
|
||||
|
||||
Ok(super::RenderPipeline { raw })
|
||||
}
|
||||
unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) {
|
||||
unsafe { self.shared.raw.destroy_pipeline(pipeline.raw, None) };
|
||||
|
||||
self.counters.render_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_compute_pipeline(
|
||||
@ -1946,10 +1997,15 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
|
||||
}
|
||||
|
||||
self.counters.compute_pipelines.add(1);
|
||||
|
||||
Ok(super::ComputePipeline { raw })
|
||||
}
|
||||
|
||||
unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) {
|
||||
unsafe { self.shared.raw.destroy_pipeline(pipeline.raw, None) };
|
||||
|
||||
self.counters.compute_pipelines.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_pipeline_cache(
|
||||
@ -2001,18 +2057,26 @@ impl crate::Device for super::Device {
|
||||
unsafe { self.shared.set_object_name(raw, label) };
|
||||
}
|
||||
|
||||
self.counters.query_sets.add(1);
|
||||
|
||||
Ok(super::QuerySet { raw })
|
||||
}
|
||||
|
||||
unsafe fn destroy_query_set(&self, set: super::QuerySet) {
|
||||
unsafe { self.shared.raw.destroy_query_pool(set.raw, None) };
|
||||
|
||||
self.counters.query_sets.sub(1);
|
||||
}
|
||||
|
||||
unsafe fn create_fence(&self) -> Result<super::Fence, crate::DeviceError> {
|
||||
self.counters.fences.add(1);
|
||||
|
||||
Ok(if self.shared.private_caps.timeline_semaphores {
|
||||
let mut sem_type_info =
|
||||
vk::SemaphoreTypeCreateInfo::default().semaphore_type(vk::SemaphoreType::TIMELINE);
|
||||
let vk_info = vk::SemaphoreCreateInfo::default().push_next(&mut sem_type_info);
|
||||
let raw = unsafe { self.shared.raw.create_semaphore(&vk_info, None) }?;
|
||||
|
||||
super::Fence::TimelineSemaphore(raw)
|
||||
} else {
|
||||
super::Fence::FencePool {
|
||||
@ -2040,6 +2104,8 @@ impl crate::Device for super::Device {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.counters.fences.sub(1);
|
||||
}
|
||||
unsafe fn get_fence_value(
|
||||
&self,
|
||||
@ -2320,6 +2386,14 @@ impl crate::Device for super::Device {
|
||||
.dealloc(&*self.shared, acceleration_structure.block.into_inner());
|
||||
}
|
||||
}
|
||||
|
||||
fn get_internal_counters(&self) -> wgt::HalCounters {
|
||||
self.counters
|
||||
.memory_allocations
|
||||
.set(self.shared.memory_allocations_counter.read());
|
||||
|
||||
self.counters.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::DeviceShared {
|
||||
|
@ -43,6 +43,7 @@ use std::{
|
||||
use arrayvec::ArrayVec;
|
||||
use ash::{ext, khr, vk};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use wgt::InternalCounter;
|
||||
|
||||
const MILLIS_TO_NANOS: u64 = 1_000_000;
|
||||
const MAX_TOTAL_ATTACHMENTS: usize = crate::MAX_COLOR_ATTACHMENTS * 2 + 1;
|
||||
@ -527,6 +528,7 @@ struct DeviceShared {
|
||||
features: wgt::Features,
|
||||
render_passes: Mutex<rustc_hash::FxHashMap<RenderPassKey, vk::RenderPass>>,
|
||||
framebuffers: Mutex<rustc_hash::FxHashMap<FramebufferKey, vk::Framebuffer>>,
|
||||
memory_allocations_counter: InternalCounter,
|
||||
}
|
||||
|
||||
pub struct Device {
|
||||
@ -538,6 +540,7 @@ pub struct Device {
|
||||
naga_options: naga::back::spv::Options<'static>,
|
||||
#[cfg(feature = "renderdoc")]
|
||||
render_doc: crate::auxil::renderdoc::RenderDoc,
|
||||
counters: wgt::HalCounters,
|
||||
}
|
||||
|
||||
/// Semaphores for forcing queue submissions to run in order.
|
||||
|
@ -30,6 +30,8 @@ targets = [
|
||||
[features]
|
||||
strict_asserts = []
|
||||
fragile-send-sync-non-atomic-wasm = []
|
||||
# Enables some internal instrumentation for debugging purposes.
|
||||
counters = []
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2"
|
||||
|
153
wgpu-types/src/counters.rs
Normal file
153
wgpu-types/src/counters.rs
Normal file
@ -0,0 +1,153 @@
|
||||
#[cfg(feature = "counters")]
|
||||
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||
|
||||
/// An internal counter for debugging purposes
|
||||
///
|
||||
/// Internally represented as an atomic isize if the `counters` feature is enabled,
|
||||
/// or compiles to nothing otherwise.
|
||||
pub struct InternalCounter {
|
||||
#[cfg(feature = "counters")]
|
||||
value: AtomicIsize,
|
||||
}
|
||||
|
||||
impl InternalCounter {
|
||||
/// Creates a counter with value 0.
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
InternalCounter {
|
||||
#[cfg(feature = "counters")]
|
||||
value: AtomicIsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the counter's value.
|
||||
#[cfg(feature = "counters")]
|
||||
#[inline]
|
||||
pub fn read(&self) -> isize {
|
||||
self.value.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Get the counter's value.
|
||||
///
|
||||
/// Always returns 0 if the `counters` feature is not enabled.
|
||||
#[cfg(not(feature = "counters"))]
|
||||
#[inline]
|
||||
pub fn read(&self) -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
/// Get and reset the counter's value.
|
||||
///
|
||||
/// Always returns 0 if the `counters` feature is not enabled.
|
||||
#[cfg(feature = "counters")]
|
||||
#[inline]
|
||||
pub fn take(&self) -> isize {
|
||||
self.value.swap(0, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Get and reset the counter's value.
|
||||
///
|
||||
/// Always returns 0 if the `counters` feature is not enabled.
|
||||
#[cfg(not(feature = "counters"))]
|
||||
#[inline]
|
||||
pub fn take(&self) -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
/// Increment the counter by the provided amount.
|
||||
#[inline]
|
||||
pub fn add(&self, _val: isize) {
|
||||
#[cfg(feature = "counters")]
|
||||
self.value.fetch_add(_val, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Decrement the counter by the provided amount.
|
||||
#[inline]
|
||||
pub fn sub(&self, _val: isize) {
|
||||
#[cfg(feature = "counters")]
|
||||
self.value.fetch_add(-_val, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Sets the counter to the provided value.
|
||||
#[inline]
|
||||
pub fn set(&self, _val: isize) {
|
||||
#[cfg(feature = "counters")]
|
||||
self.value.store(_val, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for InternalCounter {
|
||||
fn clone(&self) -> Self {
|
||||
InternalCounter {
|
||||
#[cfg(feature = "counters")]
|
||||
value: AtomicIsize::new(self.read()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InternalCounter {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for InternalCounter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.read().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// `wgpu-hal`'s internal counters.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct HalCounters {
|
||||
// API objects
|
||||
///
|
||||
pub buffers: InternalCounter,
|
||||
///
|
||||
pub textures: InternalCounter,
|
||||
///
|
||||
pub texture_views: InternalCounter,
|
||||
///
|
||||
pub bind_groups: InternalCounter,
|
||||
///
|
||||
pub bind_group_layouts: InternalCounter,
|
||||
///
|
||||
pub render_pipelines: InternalCounter,
|
||||
///
|
||||
pub compute_pipelines: InternalCounter,
|
||||
///
|
||||
pub pipeline_layouts: InternalCounter,
|
||||
///
|
||||
pub samplers: InternalCounter,
|
||||
///
|
||||
pub command_encoders: InternalCounter,
|
||||
///
|
||||
pub shader_modules: InternalCounter,
|
||||
///
|
||||
pub query_sets: InternalCounter,
|
||||
///
|
||||
pub fences: InternalCounter,
|
||||
|
||||
// Resources
|
||||
/// Amount of allocated gpu memory attributed to buffers, in bytes.
|
||||
pub buffer_memory: InternalCounter,
|
||||
/// Amount of allocated gpu memory attributed to textures, in bytes.
|
||||
pub texture_memory: InternalCounter,
|
||||
/// Number of gpu memory allocations.
|
||||
pub memory_allocations: InternalCounter,
|
||||
}
|
||||
|
||||
/// `wgpu-core`'s internal counters.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct CoreCounters {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// All internal counters, exposed for debugging purposes.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct InternalCounters {
|
||||
/// `wgpu-core` counters.
|
||||
pub core: CoreCounters,
|
||||
/// `wgpu-hal` counters.
|
||||
pub hal: HalCounters,
|
||||
}
|
@ -18,8 +18,11 @@ use std::path::PathBuf;
|
||||
use std::{num::NonZeroU32, ops::Range};
|
||||
|
||||
pub mod assertions;
|
||||
mod counters;
|
||||
pub mod math;
|
||||
|
||||
pub use counters::*;
|
||||
|
||||
// Use this macro instead of the one provided by the bitflags_serde_shim crate
|
||||
// because the latter produces an error when deserializing bits that are not
|
||||
// specified in the bitflags, while we want deserialization to succeed and
|
||||
|
@ -97,6 +97,11 @@ replay = ["serde", "wgc/replay"]
|
||||
#! ### Other
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
## Internally count resources and events for debugging purposes. If the counters
|
||||
## feature is disabled, the counting infrastructure is removed from the build and
|
||||
## the exposed counters always return 0.
|
||||
counters = ["wgc/counters"]
|
||||
|
||||
## Implement `Send` and `Sync` on Wasm, but only if atomics are not enabled.
|
||||
##
|
||||
## WebGL/WebGPU objects can not be shared between threads.
|
||||
|
@ -2978,6 +2978,14 @@ impl crate::context::Context for ContextWebGpu {
|
||||
fn device_start_capture(&self, _device: &Self::DeviceId, _device_data: &Self::DeviceData) {}
|
||||
fn device_stop_capture(&self, _device: &Self::DeviceId, _device_data: &Self::DeviceData) {}
|
||||
|
||||
fn device_get_internal_counters(
|
||||
&self,
|
||||
_device: &Self::DeviceId,
|
||||
_device_data: &Self::DeviceData,
|
||||
) -> wgt::InternalCounters {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn pipeline_cache_get_data(
|
||||
&self,
|
||||
_: &Self::PipelineCacheId,
|
||||
|
@ -2370,6 +2370,14 @@ impl crate::Context for ContextWgpuCore {
|
||||
wgc::gfx_select!(device => self.0.device_stop_capture(*device));
|
||||
}
|
||||
|
||||
fn device_get_internal_counters(
|
||||
&self,
|
||||
device: &Self::DeviceId,
|
||||
_device_data: &Self::DeviceData,
|
||||
) -> wgt::InternalCounters {
|
||||
wgc::gfx_select!(device => self.0.device_get_internal_counters(*device))
|
||||
}
|
||||
|
||||
fn pipeline_cache_get_data(
|
||||
&self,
|
||||
cache: &Self::PipelineCacheId,
|
||||
|
@ -611,6 +611,13 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
|
||||
|
||||
fn device_start_capture(&self, device: &Self::DeviceId, device_data: &Self::DeviceData);
|
||||
fn device_stop_capture(&self, device: &Self::DeviceId, device_data: &Self::DeviceData);
|
||||
|
||||
fn device_get_internal_counters(
|
||||
&self,
|
||||
device: &Self::DeviceId,
|
||||
_device_data: &Self::DeviceData,
|
||||
) -> wgt::InternalCounters;
|
||||
|
||||
fn pipeline_cache_get_data(
|
||||
&self,
|
||||
cache: &Self::PipelineCacheId,
|
||||
@ -1604,6 +1611,12 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
|
||||
fn device_start_capture(&self, device: &ObjectId, data: &crate::Data);
|
||||
fn device_stop_capture(&self, device: &ObjectId, data: &crate::Data);
|
||||
|
||||
fn device_get_internal_counters(
|
||||
&self,
|
||||
device: &ObjectId,
|
||||
device_data: &crate::Data,
|
||||
) -> wgt::InternalCounters;
|
||||
|
||||
fn pipeline_cache_get_data(
|
||||
&self,
|
||||
cache: &ObjectId,
|
||||
@ -3078,6 +3091,16 @@ where
|
||||
Context::device_stop_capture(self, &device, device_data)
|
||||
}
|
||||
|
||||
fn device_get_internal_counters(
|
||||
&self,
|
||||
device: &ObjectId,
|
||||
device_data: &crate::Data,
|
||||
) -> wgt::InternalCounters {
|
||||
let device = <T::DeviceId>::from(*device);
|
||||
let device_data = downcast_ref(device_data);
|
||||
Context::device_get_internal_counters(self, &device, device_data)
|
||||
}
|
||||
|
||||
fn pipeline_cache_get_data(
|
||||
&self,
|
||||
cache: &ObjectId,
|
||||
|
@ -3167,6 +3167,16 @@ impl Device {
|
||||
DynContext::device_stop_capture(&*self.context, &self.id, self.data.as_ref())
|
||||
}
|
||||
|
||||
/// Query internal counters from the native backend for debugging purposes.
|
||||
///
|
||||
/// Some backends may not set all counters, or may not set any counter at all.
|
||||
/// The `counters` cargo feature must be enabled for any counter to be set.
|
||||
///
|
||||
/// If a counter is not set, its contains its default value (zero).
|
||||
pub fn get_internal_counters(&self) -> wgt::InternalCounters {
|
||||
DynContext::device_get_internal_counters(&*self.context, &self.id, self.data.as_ref())
|
||||
}
|
||||
|
||||
/// Apply a callback to this `Device`'s underlying backend device.
|
||||
///
|
||||
/// If this `Device` is implemented by the backend API given by `A` (Vulkan,
|
||||
|
Loading…
Reference in New Issue
Block a user