From fbf35e43a6425a9313a5c99e21f86103b4ad90aa Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 8 Jun 2020 00:54:06 -0400 Subject: [PATCH 1/3] Render bundles --- wgpu-core/src/command/bundle.rs | 364 +++++++++++++++++++++++++++++++ wgpu-core/src/command/compute.rs | 9 +- wgpu-core/src/command/mod.rs | 26 +-- wgpu-core/src/command/render.rs | 38 +++- wgpu-core/src/device/life.rs | 19 +- wgpu-core/src/device/mod.rs | 166 ++++++++------ wgpu-core/src/device/queue.rs | 2 +- wgpu-core/src/hub.rs | 10 +- wgpu-core/src/id.rs | 9 +- wgpu-types/src/lib.rs | 8 + 10 files changed, 538 insertions(+), 113 deletions(-) create mode 100644 wgpu-core/src/command/bundle.rs diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs new file mode 100644 index 0000000000..6a7eb4af9b --- /dev/null +++ b/wgpu-core/src/command/bundle.rs @@ -0,0 +1,364 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::{ + command::{RawPass, RenderCommand}, + conv, + device::RenderPassContext, + hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token}, + id, + resource::BufferUse, + track::TrackerSet, + LifeGuard, RefCount, +}; +use arrayvec::ArrayVec; +use peek_poke::Peek; + +#[derive(Debug)] +pub struct RenderBundleEncoderDescriptor<'a> { + pub color_formats: &'a [wgt::TextureFormat], + pub depth_stencil_format: Option, + pub sample_count: u32, +} + +#[derive(Debug)] +pub struct RenderBundleEncoder { + pub(crate) raw: RawPass, + pub(crate) context: RenderPassContext, + pub(crate) sample_count: u8, + pub(crate) backend: wgt::Backend, +} + +impl RenderBundleEncoder { + pub fn new( + desc: &RenderBundleEncoderDescriptor, + device_id: id::DeviceId, + backend: wgt::Backend, + ) -> Self { + RenderBundleEncoder { + raw: RawPass::from_vec::(Vec::with_capacity(1), device_id), + context: RenderPassContext { + colors: desc.color_formats.iter().cloned().collect(), + resolves: ArrayVec::new(), + depth_stencil: desc.depth_stencil_format, + }, + sample_count: { + let sc = desc.sample_count; + assert!(sc == 0 || sc > 32 || !conv::is_power_of_two(sc)); + sc as u8 + }, + backend, + } + } + + pub fn destroy(mut self) { + unsafe { self.raw.invalidate() }; + } +} + +//Note: here, `RenderBundle` is just wrapping a raw stream of render commands. +// The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle, +// or Metal indirect command buffer. +//Note: there is no API tracing support for `RenderBundle` yet. +// It's transparent with regards to the submitted render passes. +#[derive(Debug)] +pub struct RenderBundle { + pub(crate) device_ref_count: RefCount, + pub(crate) raw: RawPass, + pub(crate) trackers: TrackerSet, + pub(crate) context: RenderPassContext, + pub(crate) sample_count: u8, + pub(crate) life_guard: LifeGuard, +} + +impl Global { + pub fn render_bundle_encoder_finish( + &self, + bundle_encoder: RenderBundleEncoder, + _desc: &wgt::RenderBundleDescriptor, + id_in: Input, + ) -> id::RenderBundleId { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, mut token) = hub.devices.read(&mut token); + + let render_bundle = { + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); + let (buffer_guard, _) = hub.buffers.read(&mut token); + + let mut trackers = TrackerSet::new(bundle_encoder.backend); + + // populate the trackers and validate the commands + #[allow(trivial_casts)] // erroneous warning! + let mut peeker = bundle_encoder.raw.base as *const u8; + #[allow(trivial_casts)] // erroneous warning! + let raw_data_end = bundle_encoder.raw.data as *const _; + let mut command = RenderCommand::Draw { + vertex_count: 0, + instance_count: 0, + first_vertex: 0, + first_instance: 0, + }; + while peeker != raw_data_end { + peeker = unsafe { RenderCommand::peek_from(peeker, &mut command) }; + //TODO: find a safer way to enforce this without the `End` command + assert!(peeker <= raw_data_end); + match command { + RenderCommand::SetBindGroup { + index: _, + num_dynamic_offsets, + bind_group_id, + phantom_offsets, + } => { + let (new_peeker, offsets) = unsafe { + phantom_offsets.decode_unaligned( + peeker, + num_dynamic_offsets as usize, + raw_data_end, + ) + }; + peeker = new_peeker; + + if cfg!(debug_assertions) { + for off in offsets { + assert_eq!( + *off as wgt::BufferAddress % wgt::BIND_BUFFER_ALIGNMENT, + 0, + "Misaligned dynamic buffer offset: {} does not align with {}", + off, + wgt::BIND_BUFFER_ALIGNMENT + ); + } + } + + let bind_group = trackers + .bind_groups + .use_extend(&*bind_group_guard, bind_group_id, (), ()) + .unwrap(); + assert_eq!(bind_group.dynamic_count, offsets.len()); + + trackers.merge_extend(&bind_group.used); + } + RenderCommand::SetPipeline(pipeline_id) => { + let pipeline = trackers + .render_pipes + .use_extend(&*pipeline_guard, pipeline_id, (), ()) + .unwrap(); + + assert!( + bundle_encoder.context.compatible(&pipeline.pass_context), + "The render pipeline output formats do not match render pass attachment formats!" + ); + assert_eq!( + pipeline.sample_count, bundle_encoder.sample_count, + "The render pipeline and renderpass have mismatching sample_count" + ); + //TODO: check read-only depth + } + RenderCommand::SetIndexBuffer { buffer_id, .. } => { + let buffer = trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX) + .unwrap(); + assert!(buffer.usage.contains(wgt::BufferUsage::INDEX), "An invalid setIndexBuffer call has been made. The buffer usage is {:?} which does not contain required usage INDEX", buffer.usage); + } + RenderCommand::SetVertexBuffer { buffer_id, .. } => { + let buffer = trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX) + .unwrap(); + assert!(buffer.usage.contains(wgt::BufferUsage::VERTEX), "An invalid setVertexBuffer call has been made. The buffer usage is {:?} which does not contain required usage VERTEX", buffer.usage); + } + RenderCommand::Draw { .. } | RenderCommand::DrawIndexed { .. } => {} + RenderCommand::DrawIndirect { + buffer_id, + offset: _, + } + | RenderCommand::DrawIndexedIndirect { + buffer_id, + offset: _, + } => { + let buffer = trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .unwrap(); + assert!(buffer.usage.contains(wgt::BufferUsage::INDIRECT), "An invalid draw(Indexed)Indirect call has been made. The buffer usage is {:?} which does not contain required usage INDIRECT", buffer.usage); + } + RenderCommand::SetBlendColor(_) + | RenderCommand::SetStencilReference(_) + | RenderCommand::SetViewport { .. } + | RenderCommand::SetScissor(_) + | RenderCommand::End => unreachable!("not support by a render bundle"), + } + } + + log::debug!("Render bundle {:#?}", trackers); + //TODO: check if the device is still alive + let device = &device_guard[bundle_encoder.raw.parent]; + RenderBundle { + device_ref_count: device.life_guard.add_ref(), + raw: bundle_encoder.raw, + trackers, + context: bundle_encoder.context, + sample_count: bundle_encoder.sample_count, + life_guard: LifeGuard::new(), + } + }; + + hub.render_bundles + .register_identity(id_in, render_bundle, &mut token) + } +} + +pub mod bundle_ffi { + use super::{super::PhantomSlice, RenderBundleEncoder, RenderCommand}; + use crate::{id, RawString}; + use std::{convert::TryInto, slice}; + use wgt::{BufferAddress, BufferSize, DynamicOffset}; + + /// # Safety + /// + /// This function is unsafe as there is no guarantee that the given pointer is + /// valid for `offset_length` elements. + // TODO: There might be other safety issues, such as using the unsafe + // `RawPass::encode` and `RawPass::encode_slice`. + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_bundle_set_bind_group( + bundle_encoder: &mut RenderBundleEncoder, + index: u32, + bind_group_id: id::BindGroupId, + offsets: *const DynamicOffset, + offset_length: usize, + ) { + bundle_encoder.raw.encode(&RenderCommand::SetBindGroup { + index: index.try_into().unwrap(), + num_dynamic_offsets: offset_length.try_into().unwrap(), + bind_group_id, + phantom_offsets: PhantomSlice::default(), + }); + bundle_encoder + .raw + .encode_slice(slice::from_raw_parts(offsets, offset_length)); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_bundle_set_pipeline( + bundle_encoder: &mut RenderBundleEncoder, + pipeline_id: id::RenderPipelineId, + ) { + bundle_encoder + .raw + .encode(&RenderCommand::SetPipeline(pipeline_id)); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_bundle_set_index_buffer( + bundle_encoder: &mut RenderBundleEncoder, + buffer_id: id::BufferId, + offset: BufferAddress, + size: BufferSize, + ) { + bundle_encoder.raw.encode(&RenderCommand::SetIndexBuffer { + buffer_id, + offset, + size, + }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_bundle_set_vertex_buffer( + bundle_encoder: &mut RenderBundleEncoder, + slot: u32, + buffer_id: id::BufferId, + offset: BufferAddress, + size: BufferSize, + ) { + bundle_encoder.raw.encode(&RenderCommand::SetVertexBuffer { + slot, + buffer_id, + offset, + size, + }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_bundle_draw( + bundle_encoder: &mut RenderBundleEncoder, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) { + bundle_encoder.raw.encode(&RenderCommand::Draw { + vertex_count, + instance_count, + first_vertex, + first_instance, + }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_bundle_draw_indexed( + bundle_encoder: &mut RenderBundleEncoder, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + ) { + bundle_encoder.raw.encode(&RenderCommand::DrawIndexed { + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_bundle_draw_indirect( + bundle_encoder: &mut RenderBundleEncoder, + buffer_id: id::BufferId, + offset: BufferAddress, + ) { + bundle_encoder + .raw + .encode(&RenderCommand::DrawIndirect { buffer_id, offset }); + } + + #[no_mangle] + pub unsafe extern "C" fn wgpu_render_pass_bundle_indexed_indirect( + bundle_encoder: &mut RenderBundleEncoder, + buffer_id: id::BufferId, + offset: BufferAddress, + ) { + bundle_encoder + .raw + .encode(&RenderCommand::DrawIndexedIndirect { buffer_id, offset }); + } + + #[no_mangle] + pub extern "C" fn wgpu_render_bundle_push_debug_group( + _bundle_encoder: &mut RenderBundleEncoder, + _label: RawString, + ) { + //TODO + } + + #[no_mangle] + pub extern "C" fn wgpu_render_bundle_pop_debug_group( + _bundle_encoder: &mut RenderBundleEncoder, + ) { + //TODO + } + + #[no_mangle] + pub extern "C" fn wgpu_render_bundle_insert_debug_marker( + _bundle_encoder: &mut RenderBundleEncoder, + _label: RawString, + ) { + //TODO + } +} diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 57c3855942..1f742c6e67 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -51,7 +51,7 @@ impl Default for ComputeCommand { } } -impl super::RawPass { +impl super::RawPass { pub unsafe fn new_compute(parent: id::CommandEncoderId) -> Self { Self::from_vec(Vec::::with_capacity(1), parent) } @@ -291,14 +291,13 @@ impl Global { } pub mod compute_ffi { - use super::{ - super::{PhantomSlice, RawPass}, - ComputeCommand, - }; + use super::{super::PhantomSlice, ComputeCommand}; use crate::{id, RawString}; use std::{convert::TryInto, slice}; use wgt::{BufferAddress, DynamicOffset}; + type RawPass = super::super::RawPass; + /// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index ac6399b1de..27c76f1930 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -4,11 +4,13 @@ mod allocator; mod bind; +mod bundle; mod compute; mod render; mod transfer; pub(crate) use self::allocator::CommandAllocator; +pub use self::bundle::*; pub use self::compute::*; pub use self::render::*; pub use self::transfer::*; @@ -57,23 +59,25 @@ impl PhantomSlice { } #[repr(C)] -pub struct RawPass { +#[derive(Debug)] +pub struct RawPass

{ data: *mut u8, base: *mut u8, capacity: usize, - parent: id::CommandEncoderId, + parent: P, } -impl RawPass { - fn from_vec(mut vec: Vec, encoder_id: id::CommandEncoderId) -> Self { +impl RawPass

{ + fn from_vec(mut vec: Vec, parent: P) -> Self { let ptr = vec.as_mut_ptr() as *mut u8; let capacity = vec.capacity() * mem::size_of::(); + assert_ne!(capacity, 0); mem::forget(vec); RawPass { data: ptr, base: ptr, capacity, - parent: encoder_id, + parent, } } @@ -94,15 +98,15 @@ impl RawPass { } /// Recover the data vector of the pass, consuming `self`. - unsafe fn into_vec(mut self) -> (Vec, id::CommandEncoderId) { - (self.invalidate(), self.parent) + unsafe fn into_vec(mut self) -> (Vec, P) { + self.invalidate() } /// Make pass contents invalid, return the contained data. /// /// Any following access to the pass will result in a crash /// for accessing address 0. - pub unsafe fn invalidate(&mut self) -> Vec { + pub unsafe fn invalidate(&mut self) -> (Vec, P) { let size = self.size(); assert!( size <= self.capacity, @@ -114,7 +118,7 @@ impl RawPass { self.data = ptr::null_mut(); self.base = ptr::null_mut(); self.capacity = 0; - vec + (vec, self.parent) } unsafe fn ensure_extra_size(&mut self, extra_size: usize) { @@ -147,10 +151,6 @@ impl RawPass { } } -pub struct RenderBundle { - _raw: B::CommandBuffer, -} - #[derive(Debug)] pub struct CommandBuffer { pub(crate) raw: Vec, diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index b0d77ac775..8c9ec1eb98 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -5,7 +5,7 @@ use crate::{ command::{ bind::{Binder, LayoutChange}, - PassComponent, PhantomSlice, RawRenderPassColorAttachmentDescriptor, + PassComponent, PhantomSlice, RawPass, RawRenderPassColorAttachmentDescriptor, RawRenderPassDepthStencilAttachmentDescriptor, RawRenderTargets, }, conv, @@ -126,7 +126,7 @@ impl Default for RenderCommand { } } -impl super::RawPass { +impl RawPass { pub unsafe fn new_render(parent_id: id::CommandEncoderId, desc: &RenderPassDescriptor) -> Self { let mut pass = Self::from_vec(Vec::::with_capacity(1), parent_id); @@ -1321,17 +1321,40 @@ impl Global { } cmb.raw.push(raw); } + + pub fn wgpu_render_pass_execute_bundles( + &mut self, + pass: &mut RawPass, + render_bundle_ids: &[id::RenderBundleId], + ) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (_, mut token) = hub.devices.read(&mut token); + let (bundle_guard, _) = hub.render_bundles.read(&mut token); + for &bundle_id in render_bundle_ids { + let bundle = &bundle_guard[bundle_id]; + //TODO: check the `bundle.context` + let size = bundle.raw.size(); + unsafe { + pass.ensure_extra_size(size); + std::ptr::copy_nonoverlapping(bundle.raw.base, pass.data, size); + } + } + } } pub mod render_ffi { use super::{ - super::{PhantomSlice, RawPass, Rect}, + super::{PhantomSlice, Rect}, RenderCommand, }; use crate::{id, RawString}; use std::{convert::TryInto, slice}; use wgt::{BufferAddress, BufferSize, Color, DynamicOffset}; + type RawPass = super::super::RawPass; + /// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is @@ -1486,15 +1509,6 @@ pub mod render_ffi { pass.encode(&RenderCommand::DrawIndexedIndirect { buffer_id, offset }); } - #[no_mangle] - pub extern "C" fn wgpu_render_pass_execute_bundles( - _pass: &mut RawPass, - _bundles: *const id::RenderBundleId, - _bundles_length: usize, - ) { - unimplemented!() - } - #[no_mangle] pub extern "C" fn wgpu_render_pass_push_debug_group(_pass: &mut RawPass, _label: RawString) { //TODO diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index 9d69abfceb..d8da38d923 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -5,7 +5,7 @@ #[cfg(feature = "trace")] use crate::device::trace; use crate::{ - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Token}, + hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Token}, id, resource, track::TrackerSet, FastHashMap, RefCount, Stored, SubmissionIndex, @@ -303,13 +303,11 @@ impl LifetimeTracker { impl LifetimeTracker { pub(crate) fn triage_suspected( &mut self, - global: &Global, + hub: &Hub, trackers: &Mutex, #[cfg(feature = "trace")] trace: Option<&Mutex>, token: &mut Token>, ) { - let hub = B::hub(global); - if !self.suspected_resources.bind_groups.is_empty() { let mut trackers = trackers.lock(); let (mut guard, _) = hub.bind_groups.write(token); @@ -528,13 +526,13 @@ impl LifetimeTracker { pub(crate) fn triage_mapped( &mut self, - global: &Global, + hub: &Hub, token: &mut Token>, ) { if self.mapped.is_empty() { return; } - let (buffer_guard, _) = B::hub(global).buffers.read(token); + let (buffer_guard, _) = hub.buffers.read(token); for stored in self.mapped.drain(..) { let resource_id = stored.value; @@ -558,11 +556,11 @@ impl LifetimeTracker { pub(crate) fn triage_framebuffers( &mut self, - global: &Global, + hub: &Hub, framebuffers: &mut FastHashMap, token: &mut Token>, ) { - let (texture_view_guard, _) = B::hub(global).texture_views.read(token); + let (texture_view_guard, _) = hub.texture_views.read(token); let remove_list = framebuffers .keys() .filter_map(|key| { @@ -624,7 +622,7 @@ impl LifetimeTracker { pub(crate) fn handle_mapping( &mut self, - global: &Global, + hub: &Hub, raw: &B::Device, trackers: &Mutex, token: &mut Token>, @@ -632,8 +630,7 @@ impl LifetimeTracker { if self.ready_to_map.is_empty() { return Vec::new(); } - let hub = B::hub(global); - let (mut buffer_guard, _) = B::hub(global).buffers.write(token); + let (mut buffer_guard, _) = hub.buffers.write(token); let mut pending_callbacks: Vec = Vec::with_capacity(self.ready_to_map.len()); let mut trackers = trackers.lock(); diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 84ad3ff351..9c11e7d62a 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -4,7 +4,7 @@ use crate::{ binding_model, command, conv, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token}, + hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Hub, Input, Token}, id, pipeline, resource, swap_chain, track::{BufferState, TextureState, TrackerSet}, validation, FastHashMap, LifeGuard, PrivateFeatures, Stored, MAX_BIND_GROUPS, @@ -172,7 +172,7 @@ pub struct Device { pub(crate) com_allocator: command::CommandAllocator, mem_allocator: Mutex>, desc_allocator: Mutex>, - life_guard: LifeGuard, + pub(crate) life_guard: LifeGuard, pub(crate) trackers: Mutex, pub(crate) render_passes: Mutex>, pub(crate) framebuffers: Mutex>, @@ -277,29 +277,89 @@ impl Device { fn maintain<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>( &'this self, - global: &Global, + hub: &Hub, force_wait: bool, token: &mut Token<'token, Self>, ) -> Vec { let mut life_tracker = self.lock_life(token); life_tracker.triage_suspected( - global, + hub, &self.trackers, #[cfg(feature = "trace")] self.trace.as_ref(), token, ); - life_tracker.triage_mapped(global, token); - life_tracker.triage_framebuffers(global, &mut *self.framebuffers.lock(), token); + life_tracker.triage_mapped(hub, token); + life_tracker.triage_framebuffers(hub, &mut *self.framebuffers.lock(), token); let last_done = life_tracker.triage_submissions(&self.raw, force_wait); - let callbacks = life_tracker.handle_mapping(global, &self.raw, &self.trackers, token); + let callbacks = life_tracker.handle_mapping(hub, &self.raw, &self.trackers, token); life_tracker.cleanup(&self.raw, &self.mem_allocator, &self.desc_allocator); self.com_allocator.maintain(&self.raw, last_done); callbacks } + fn untrack<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>( + &'this mut self, + hub: &Hub, + trackers: &TrackerSet, + mut token: &mut Token<'token, Self>, + ) { + self.temp_suspected.clear(); + // As the tracker is cleared/dropped, we need to consider all the resources + // that it references for destruction in the next GC pass. + { + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + let (compute_pipe_guard, mut token) = hub.compute_pipelines.read(&mut token); + let (render_pipe_guard, mut token) = hub.render_pipelines.read(&mut token); + let (buffer_guard, mut token) = hub.buffers.read(&mut token); + let (texture_guard, mut token) = hub.textures.read(&mut token); + let (texture_view_guard, mut token) = hub.texture_views.read(&mut token); + let (sampler_guard, _) = hub.samplers.read(&mut token); + + for id in trackers.buffers.used() { + if buffer_guard[id].life_guard.ref_count.is_none() { + self.temp_suspected.buffers.push(id); + } + } + for id in trackers.textures.used() { + if texture_guard[id].life_guard.ref_count.is_none() { + self.temp_suspected.textures.push(id); + } + } + for id in trackers.views.used() { + if texture_view_guard[id].life_guard.ref_count.is_none() { + self.temp_suspected.texture_views.push(id); + } + } + for id in trackers.bind_groups.used() { + if bind_group_guard[id].life_guard.ref_count.is_none() { + self.temp_suspected.bind_groups.push(id); + } + } + for id in trackers.samplers.used() { + if sampler_guard[id].life_guard.ref_count.is_none() { + self.temp_suspected.samplers.push(id); + } + } + for id in trackers.compute_pipes.used() { + if compute_pipe_guard[id].life_guard.ref_count.is_none() { + self.temp_suspected.compute_pipelines.push(id); + } + } + for id in trackers.render_pipes.used() { + if render_pipe_guard[id].life_guard.ref_count.is_none() { + self.temp_suspected.render_pipelines.push(id); + } + } + } + + self.lock_life(&mut token) + .suspected_resources + .extend(&self.temp_suspected); + } + fn create_buffer( &self, self_id: id::DeviceId, @@ -1776,66 +1836,14 @@ impl Global { let hub = B::hub(self); let mut token = Token::root(); + let (mut device_guard, mut token) = hub.devices.write(&mut token); let comb = { let (mut command_buffer_guard, _) = hub.command_buffers.write(&mut token); command_buffer_guard.remove(command_encoder_id).unwrap() }; - let (mut device_guard, mut token) = hub.devices.write(&mut token); let device = &mut device_guard[comb.device_id.value]; - device.temp_suspected.clear(); - // As the tracker is cleared/dropped, we need to consider all the resources - // that it references for destruction in the next GC pass. - { - let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); - let (compute_pipe_guard, mut token) = hub.compute_pipelines.read(&mut token); - let (render_pipe_guard, mut token) = hub.render_pipelines.read(&mut token); - let (buffer_guard, mut token) = hub.buffers.read(&mut token); - let (texture_guard, mut token) = hub.textures.read(&mut token); - let (texture_view_guard, mut token) = hub.texture_views.read(&mut token); - let (sampler_guard, _) = hub.samplers.read(&mut token); - - for id in comb.trackers.buffers.used() { - if buffer_guard[id].life_guard.ref_count.is_none() { - device.temp_suspected.buffers.push(id); - } - } - for id in comb.trackers.textures.used() { - if texture_guard[id].life_guard.ref_count.is_none() { - device.temp_suspected.textures.push(id); - } - } - for id in comb.trackers.views.used() { - if texture_view_guard[id].life_guard.ref_count.is_none() { - device.temp_suspected.texture_views.push(id); - } - } - for id in comb.trackers.bind_groups.used() { - if bind_group_guard[id].life_guard.ref_count.is_none() { - device.temp_suspected.bind_groups.push(id); - } - } - for id in comb.trackers.samplers.used() { - if sampler_guard[id].life_guard.ref_count.is_none() { - device.temp_suspected.samplers.push(id); - } - } - for id in comb.trackers.compute_pipes.used() { - if compute_pipe_guard[id].life_guard.ref_count.is_none() { - device.temp_suspected.compute_pipelines.push(id); - } - } - for id in comb.trackers.render_pipes.used() { - if render_pipe_guard[id].life_guard.ref_count.is_none() { - device.temp_suspected.render_pipelines.push(id); - } - } - } - - device - .lock_life(&mut token) - .suspected_resources - .extend(&device.temp_suspected); + device.untrack::(&hub, &comb.trackers, &mut token); device.com_allocator.discard(comb); } @@ -1843,6 +1851,36 @@ impl Global { self.command_encoder_destroy::(command_buffer_id) } + pub fn device_create_render_bundle_encoder( + &self, + device_id: id::DeviceId, + desc: &command::RenderBundleEncoderDescriptor, + ) -> id::RenderBundleEncoderId { + let encoder = command::RenderBundleEncoder::new(desc, device_id, B::VARIANT); + Box::into_raw(Box::new(encoder)) + } + + pub fn render_bundle_encoder_destroy( + &self, + render_bundle_encoder: command::RenderBundleEncoder, + ) { + render_bundle_encoder.destroy(); + } + + pub fn render_bundle_destroy(&self, render_bundle_id: id::RenderBundleId) { + let hub = B::hub(self); + let mut token = Token::root(); + + let (mut device_guard, mut token) = hub.devices.write(&mut token); + let mut bundle = { + let (mut render_bundle_guard, _) = hub.render_bundles.write(&mut token); + render_bundle_guard.remove(render_bundle_id).unwrap() + }; + + let (_, device_id) = unsafe { bundle.raw.invalidate() }; + device_guard[device_id].untrack(&hub, &bundle.trackers, &mut token); + } + pub fn device_create_render_pipeline( &self, device_id: id::DeviceId, @@ -2492,7 +2530,7 @@ impl Global { let (device_guard, mut token) = hub.devices.read(&mut token); let device = &device_guard[device_id]; device.lock_life(&mut token).triage_suspected( - self, + &hub, &device.trackers, #[cfg(feature = "trace")] None, @@ -2505,7 +2543,7 @@ impl Global { let mut token = Token::root(); let callbacks = { let (device_guard, mut token) = hub.devices.read(&mut token); - device_guard[device_id].maintain(self, force_wait, &mut token) + device_guard[device_id].maintain(&hub, force_wait, &mut token) }; fire_map_callbacks(callbacks); } @@ -2519,7 +2557,7 @@ impl Global { let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); for (_, device) in device_guard.iter(B::VARIANT) { - let cbs = device.maintain(self, force_wait, &mut token); + let cbs = device.maintain(&hub, force_wait, &mut token); callbacks.extend(cbs); } } diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 6024b8a081..e17d2cf27f 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -532,7 +532,7 @@ impl Global { .after_submit_internal(comb_raw, submit_index); } - let callbacks = device.maintain(self, false, &mut token); + let callbacks = device.maintain(&hub, false, &mut token); super::Device::lock_life_internal(&device.life_tracker, &mut token).track_submission( submit_index, fence, diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index 9f2c1714f4..a79337b76c 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -5,12 +5,12 @@ use crate::{ backend, binding_model::{BindGroup, BindGroupLayout, PipelineLayout}, - command::CommandBuffer, + command::{CommandBuffer, RenderBundle}, device::Device, id::{ AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, ComputePipelineId, - DeviceId, PipelineLayoutId, RenderPipelineId, SamplerId, ShaderModuleId, SurfaceId, - SwapChainId, TextureId, TextureViewId, TypedId, + DeviceId, PipelineLayoutId, RenderBundleId, RenderPipelineId, SamplerId, ShaderModuleId, + SurfaceId, SwapChainId, TextureId, TextureViewId, TypedId, }, instance::{Adapter, Instance, Surface}, pipeline::{ComputePipeline, RenderPipeline, ShaderModule}, @@ -186,6 +186,7 @@ impl Access> for CommandBuffer {} impl Access> for Root {} impl Access> for Device {} impl Access> for SwapChain {} +impl Access for Device {} impl Access> for Device {} impl Access> for BindGroup {} impl Access> for Device {} @@ -298,6 +299,7 @@ pub trait GlobalIdentityHandlerFactory: + IdentityHandlerFactory + IdentityHandlerFactory + IdentityHandlerFactory + + IdentityHandlerFactory + IdentityHandlerFactory + IdentityHandlerFactory + IdentityHandlerFactory @@ -405,6 +407,7 @@ pub struct Hub { pub bind_group_layouts: Registry, BindGroupLayoutId, F>, pub bind_groups: Registry, BindGroupId, F>, pub command_buffers: Registry, CommandBufferId, F>, + pub render_bundles: Registry, pub render_pipelines: Registry, RenderPipelineId, F>, pub compute_pipelines: Registry, ComputePipelineId, F>, pub buffers: Registry, BufferId, F>, @@ -424,6 +427,7 @@ impl Hub { bind_group_layouts: Registry::new(B::VARIANT, factory, "BindGroupLayout"), bind_groups: Registry::new(B::VARIANT, factory, "BindGroup"), command_buffers: Registry::new(B::VARIANT, factory, "CommandBuffer"), + render_bundles: Registry::new(B::VARIANT, factory, "RenderBundle"), render_pipelines: Registry::new(B::VARIANT, factory, "RenderPipeline"), compute_pipelines: Registry::new(B::VARIANT, factory, "ComputePipeline"), buffers: Registry::new(B::VARIANT, factory, "Buffer"), diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index ee4eafb62d..8f9af70041 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -164,11 +164,12 @@ pub type ShaderModuleId = Id>; pub type RenderPipelineId = Id>; pub type ComputePipelineId = Id>; // Command -pub type CommandBufferId = Id>; pub type CommandEncoderId = CommandBufferId; -pub type RenderPassId = *mut crate::command::RawPass; -pub type ComputePassId = *mut crate::command::RawPass; -pub type RenderBundleId = Id>; +pub type CommandBufferId = Id>; +pub type RenderPassId = *mut crate::command::RawPass; +pub type ComputePassId = *mut crate::command::RawPass; +pub type RenderBundleEncoderId = *mut crate::command::RenderBundleEncoder; +pub type RenderBundleId = Id; // Swap chain pub type SwapChainId = Id>; diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 3fd3fb7492..04bc25f4d4 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -1083,6 +1083,14 @@ pub struct CommandBufferDescriptor { pub todo: u32, } +#[repr(C)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "trace", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct RenderBundleDescriptor { + pub todo: u32, +} + #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "trace", derive(Serialize))] From afc4517db1f5588d2ce11c6e171a66291ba1c846 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 9 Jun 2020 09:35:39 -0400 Subject: [PATCH 2/3] Render bundle reset states, updated descriptors --- wgpu-core/src/command/bind.rs | 5 ++++ wgpu-core/src/command/bundle.rs | 47 +++++++++++++++++---------------- wgpu-core/src/command/render.rs | 33 +++++++++++++++++++++-- wgpu-core/src/device/mod.rs | 8 +++--- wgpu-core/src/hub.rs | 6 +++++ wgpu-types/src/lib.rs | 39 ++++++++++++++++++--------- 6 files changed, 97 insertions(+), 41 deletions(-) diff --git a/wgpu-core/src/command/bind.rs b/wgpu-core/src/command/bind.rs index 2ab9a88376..1c2d5724e1 100644 --- a/wgpu-core/src/command/bind.rs +++ b/wgpu-core/src/command/bind.rs @@ -145,6 +145,11 @@ impl Binder { } } + pub(crate) fn reset(&mut self) { + self.pipeline_layout_id = None; + self.entries.clear(); + } + pub(crate) fn reset_expectations(&mut self, length: usize) { for entry in self.entries[length..].iter_mut() { entry.expected_layout_id = None; diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 6a7eb4af9b..3b27ad4f86 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -5,7 +5,7 @@ use crate::{ command::{RawPass, RenderCommand}, conv, - device::RenderPassContext, + device::{Label, RenderPassContext}, hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token}, id, resource::BufferUse, @@ -13,29 +13,17 @@ use crate::{ LifeGuard, RefCount, }; use arrayvec::ArrayVec; -use peek_poke::Peek; - -#[derive(Debug)] -pub struct RenderBundleEncoderDescriptor<'a> { - pub color_formats: &'a [wgt::TextureFormat], - pub depth_stencil_format: Option, - pub sample_count: u32, -} +use peek_poke::{Peek, Poke}; #[derive(Debug)] pub struct RenderBundleEncoder { pub(crate) raw: RawPass, pub(crate) context: RenderPassContext, pub(crate) sample_count: u8, - pub(crate) backend: wgt::Backend, } impl RenderBundleEncoder { - pub fn new( - desc: &RenderBundleEncoderDescriptor, - device_id: id::DeviceId, - backend: wgt::Backend, - ) -> Self { + pub fn new(desc: &wgt::RenderBundleEncoderDescriptor, device_id: id::DeviceId) -> Self { RenderBundleEncoder { raw: RawPass::from_vec::(Vec::with_capacity(1), device_id), context: RenderPassContext { @@ -45,13 +33,16 @@ impl RenderBundleEncoder { }, sample_count: { let sc = desc.sample_count; - assert!(sc == 0 || sc > 32 || !conv::is_power_of_two(sc)); + assert!(sc != 0 && sc <= 32 && conv::is_power_of_two(sc)); sc as u8 }, - backend, } } + pub fn parent(&self) -> id::DeviceId { + self.raw.parent + } + pub fn destroy(mut self) { unsafe { self.raw.invalidate() }; } @@ -72,23 +63,28 @@ pub struct RenderBundle { pub(crate) life_guard: LifeGuard, } +unsafe impl Send for RenderBundle {} +unsafe impl Sync for RenderBundle {} + impl Global { pub fn render_bundle_encoder_finish( &self, - bundle_encoder: RenderBundleEncoder, - _desc: &wgt::RenderBundleDescriptor, + mut bundle_encoder: RenderBundleEncoder, + _desc: &wgt::RenderBundleDescriptor

{ } impl RawPass

{ - fn from_vec(mut vec: Vec, parent: P) -> Self { + fn new(parent: P) -> Self { + let mut vec = Vec::::with_capacity(1); let ptr = vec.as_mut_ptr() as *mut u8; - let capacity = vec.capacity() * mem::size_of::(); + let capacity = mem::size_of::(); assert_ne!(capacity, 0); mem::forget(vec); RawPass { @@ -135,13 +136,13 @@ impl RawPass

{ } #[inline] - pub unsafe fn encode(&mut self, command: &C) { + pub(crate) unsafe fn encode(&mut self, command: &C) { self.ensure_extra_size(C::max_size()); self.data = command.poke_into(self.data); } #[inline] - pub unsafe fn encode_slice(&mut self, data: &[T]) { + pub(crate) unsafe fn encode_slice(&mut self, data: &[T]) { let align_offset = self.data.align_offset(mem::align_of::()); let extra = align_offset + mem::size_of::() * data.len(); self.ensure_extra_size(extra); @@ -193,6 +194,7 @@ impl CommandBuffer { .merge_extend(&head.compute_pipes) .unwrap(); base.render_pipes.merge_extend(&head.render_pipes).unwrap(); + base.bundles.merge_extend(&head.bundles).unwrap(); let stages = all_buffer_stages() | all_image_stages(); unsafe { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 607e7b744f..6c7e32ecdd 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -10,7 +10,8 @@ use crate::{ }, conv, device::{ - FramebufferKey, RenderPassContext, RenderPassKey, MAX_COLOR_TARGETS, MAX_VERTEX_BUFFERS, + AttachmentData, FramebufferKey, RenderPassContext, RenderPassKey, MAX_COLOR_TARGETS, + MAX_VERTEX_BUFFERS, }, hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Token}, id, @@ -23,7 +24,6 @@ use crate::{ use arrayvec::ArrayVec; use hal::command::CommandBuffer as _; use peek_poke::{Peek, PeekPoke, Poke}; -use smallvec::SmallVec; use wgt::{ BufferAddress, BufferSize, BufferUsage, Color, DynamicOffset, IndexFormat, InputStepMode, LoadOp, RenderPassColorAttachmentDescriptorBase, @@ -116,9 +116,7 @@ pub enum RenderCommand { buffer_id: id::BufferId, offset: BufferAddress, }, - /// Resets all the state that can be set by a render bundle. - /// Also has to be the last command in a render bundle. - ResetBundleState, + ExecuteBundle(id::RenderBundleId), End, } @@ -131,7 +129,7 @@ impl Default for RenderCommand { impl RawPass { pub unsafe fn new_render(parent_id: id::CommandEncoderId, desc: &RenderPassDescriptor) -> Self { - let mut pass = Self::from_vec(Vec::::with_capacity(1), parent_id); + let mut pass = Self::new::(parent_id); let mut targets: RawRenderTargets = mem::zeroed(); if let Some(ds) = desc.depth_stencil_attachment { @@ -171,8 +169,28 @@ impl RawPass { pass.encode(&targets); pass } +} - pub unsafe fn finish_render(mut self) -> (Vec, id::CommandEncoderId) { +impl RawPass

{ + pub unsafe fn fill_render_commands( + &mut self, + commands: &[RenderCommand], + mut offsets: &[DynamicOffset], + ) { + for com in commands { + self.encode(com); + if let RenderCommand::SetBindGroup { + num_dynamic_offsets, + .. + } = *com + { + self.encode_slice(&offsets[..num_dynamic_offsets as usize]); + offsets = &offsets[num_dynamic_offsets as usize..]; + } + } + } + + pub unsafe fn finish_render(mut self) -> (Vec, P) { self.finish(RenderCommand::End); self.into_vec() } @@ -216,8 +234,8 @@ impl fmt::Debug for DrawError { } } -#[derive(Debug)] -pub struct IndexState { +#[derive(Debug, Default)] +struct IndexState { bound_buffer_view: Option<(id::BufferId, Range)>, format: IndexFormat, limit: u32, @@ -244,7 +262,7 @@ impl IndexState { } #[derive(Clone, Copy, Debug)] -pub struct VertexBufferState { +struct VertexBufferState { total_size: BufferAddress, stride: BufferAddress, rate: InputStepMode, @@ -258,9 +276,9 @@ impl VertexBufferState { }; } -#[derive(Debug)] -pub struct VertexState { - inputs: SmallVec<[VertexBufferState; MAX_VERTEX_BUFFERS]>, +#[derive(Debug, Default)] +struct VertexState { + inputs: ArrayVec<[VertexBufferState; MAX_VERTEX_BUFFERS]>, vertex_limit: u32, instance_limit: u32, } @@ -352,6 +370,7 @@ impl Global { raw.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT); } + let (bundle_guard, mut token) = hub.render_bundles.read(&mut token); let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); @@ -408,7 +427,7 @@ impl Global { // instead of the special read-only one, which would be `None`. let mut is_ds_read_only = false; - let (context, sample_count) = { + let context = { use hal::device::Device as _; let samples_count_limit = device.hal_limits.framebuffer_color_sample_counts; @@ -874,19 +893,22 @@ impl Global { ); } - let context = RenderPassContext { - colors: color_attachments - .iter() - .map(|at| view_guard[at.attachment].format) - .collect(), - resolves: color_attachments - .iter() - .filter_map(|at| at.resolve_target) - .map(|resolve| view_guard[resolve].format) - .collect(), - depth_stencil: depth_stencil_attachment.map(|at| view_guard[at.attachment].format), - }; - (context, sample_count) + RenderPassContext { + attachments: AttachmentData { + colors: color_attachments + .iter() + .map(|at| view_guard[at.attachment].format) + .collect(), + resolves: color_attachments + .iter() + .filter_map(|at| at.resolve_target) + .map(|resolve| view_guard[resolve].format) + .collect(), + depth_stencil: depth_stencil_attachment + .map(|at| view_guard[at.attachment].format), + }, + sample_count, + } }; let mut state = State { @@ -894,16 +916,8 @@ impl Global { blend_color: OptionalState::Unused, stencil_reference: OptionalState::Unused, pipeline: OptionalState::Required, - index: IndexState { - bound_buffer_view: None, - format: IndexFormat::Uint16, - limit: 0, - }, - vertex: VertexState { - inputs: SmallVec::new(), - vertex_limit: 0, - instance_limit: 0, - }, + index: IndexState::default(), + vertex: VertexState::default(), }; let mut command = RenderCommand::Draw { @@ -969,7 +983,7 @@ impl Global { ); unsafe { raw.bind_graphics_descriptor_sets( - &&pipeline_layout_guard[pipeline_layout_id].raw, + &pipeline_layout_guard[pipeline_layout_id].raw, index as usize, bind_groups, offsets @@ -989,11 +1003,7 @@ impl Global { assert!( context.compatible(&pipeline.pass_context), - "The render pipeline output formats do not match render pass attachment formats!" - ); - assert_eq!( - pipeline.sample_count, sample_count, - "The render pipeline and renderpass have mismatching sample_count" + "The render pipeline output formats and sample count do not match render pass attachment formats!" ); assert!( !is_ds_read_only || pipeline.flags.contains(PipelineFlags::DEPTH_STENCIL_READ_ONLY), @@ -1284,7 +1294,28 @@ impl Global { raw.draw_indexed_indirect(&buffer.raw, offset, 1, 0); } } - RenderCommand::ResetBundleState => { + RenderCommand::ExecuteBundle(bundle_id) => { + let bundle = trackers + .bundles + .use_extend(&*bundle_guard, bundle_id, (), ()) + .unwrap(); + + assert!( + context.compatible(&bundle.context), + "The render bundle output formats do not match render pass attachment formats!" + ); + + unsafe { + bundle.execute( + &mut raw, + &*pipeline_layout_guard, + &*bind_group_guard, + &*pipeline_guard, + &*buffer_guard, + ) + }; + + trackers.merge_extend(&bundle.used); state.reset_bundle(); } RenderCommand::End => break, @@ -1346,31 +1377,6 @@ impl Global { } cmb.raw.push(raw); } - - pub fn wgpu_render_pass_execute_bundles( - &self, - pass: &mut RawPass, - render_bundle_ids: &[id::RenderBundleId], - ) { - let hub = B::hub(self); - let mut token = Token::root(); - - unsafe { pass.encode(&RenderCommand::ResetBundleState) }; - - let (_, mut token) = hub.devices.read(&mut token); - let (bundle_guard, _) = hub.render_bundles.read(&mut token); - for &bundle_id in render_bundle_ids { - let bundle = &bundle_guard[bundle_id]; - //TODO: check the `bundle.context`? It will be checked - // when the render pass finishes. - let size = bundle.raw.size(); - unsafe { - pass.ensure_extra_size(size); - std::ptr::copy_nonoverlapping(bundle.raw.base, pass.data, size); - pass.data = pass.data.offset(size as isize); - } - } - } } pub mod render_ffi { @@ -1553,6 +1559,17 @@ pub mod render_ffi { //TODO } + #[no_mangle] + pub unsafe fn wgpu_render_pass_execute_bundles( + pass: &mut RawPass, + render_bundle_ids: *const id::RenderBundleId, + render_bundle_ids_length: usize, + ) { + for &bundle_id in slice::from_raw_parts(render_bundle_ids, render_bundle_ids_length) { + pass.encode(&RenderCommand::ExecuteBundle(bundle_id)); + } + } + #[no_mangle] pub unsafe extern "C" fn wgpu_render_pass_finish( pass: &mut RawPass, diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index d8da38d923..7d20209ae3 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -33,10 +33,11 @@ pub struct SuspectedResources { pub(crate) render_pipelines: Vec, pub(crate) bind_group_layouts: Vec>, pub(crate) pipeline_layouts: Vec>, + pub(crate) render_bundles: Vec, } impl SuspectedResources { - pub fn clear(&mut self) { + pub(crate) fn clear(&mut self) { self.buffers.clear(); self.textures.clear(); self.texture_views.clear(); @@ -46,9 +47,10 @@ impl SuspectedResources { self.render_pipelines.clear(); self.bind_group_layouts.clear(); self.pipeline_layouts.clear(); + self.render_bundles.clear(); } - pub fn extend(&mut self, other: &Self) { + pub(crate) fn extend(&mut self, other: &Self) { self.buffers.extend_from_slice(&other.buffers); self.textures.extend_from_slice(&other.textures); self.texture_views.extend_from_slice(&other.texture_views); @@ -62,6 +64,18 @@ impl SuspectedResources { .extend_from_slice(&other.bind_group_layouts); self.pipeline_layouts .extend_from_slice(&other.pipeline_layouts); + self.render_bundles.extend_from_slice(&other.render_bundles); + } + + pub(crate) fn add_trackers(&mut self, trackers: &TrackerSet) { + self.buffers.extend(trackers.buffers.used()); + self.textures.extend(trackers.textures.used()); + self.texture_views.extend(trackers.views.used()); + self.samplers.extend(trackers.samplers.used()); + self.bind_groups.extend(trackers.bind_groups.used()); + self.compute_pipelines.extend(trackers.compute_pipes.used()); + self.render_pipelines.extend(trackers.render_pipes.used()); + self.render_bundles.extend(trackers.bundles.used()); } } @@ -308,30 +322,33 @@ impl LifetimeTracker { #[cfg(feature = "trace")] trace: Option<&Mutex>, token: &mut Token>, ) { + if !self.suspected_resources.render_bundles.is_empty() { + let mut trackers = trackers.lock(); + let (mut guard, _) = hub.render_bundles.write(token); + + while let Some(id) = self.suspected_resources.render_bundles.pop() { + if trackers.bundles.remove_abandoned(id) { + #[cfg(feature = "trace")] + trace.map(|t| t.lock().add(trace::Action::DestroyRenderBundle(id))); + hub.render_bundles.free_id(id); + let res = guard.remove(id).unwrap(); + self.suspected_resources.add_trackers(&res.used); + } + } + } + if !self.suspected_resources.bind_groups.is_empty() { let mut trackers = trackers.lock(); let (mut guard, _) = hub.bind_groups.write(token); - for id in self.suspected_resources.bind_groups.drain(..) { + while let Some(id) = self.suspected_resources.bind_groups.pop() { if trackers.bind_groups.remove_abandoned(id) { #[cfg(feature = "trace")] trace.map(|t| t.lock().add(trace::Action::DestroyBindGroup(id))); hub.bind_groups.free_id(id); let res = guard.remove(id).unwrap(); - assert!(res.used.bind_groups.is_empty()); - self.suspected_resources - .buffers - .extend(res.used.buffers.used()); - self.suspected_resources - .textures - .extend(res.used.textures.used()); - self.suspected_resources - .texture_views - .extend(res.used.views.used()); - self.suspected_resources - .samplers - .extend(res.used.samplers.used()); + self.suspected_resources.add_trackers(&res.used); let submit_index = res.life_guard.submission_index.load(Ordering::Acquire); self.active diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index e0da94f687..8675da744e 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -101,17 +101,24 @@ impl AttachmentData { } } +pub(crate) type RenderPassKey = AttachmentData<(hal::pass::Attachment, hal::image::Layout)>; +pub(crate) type FramebufferKey = AttachmentData; + +#[derive(Clone, Debug, Hash, PartialEq)] +pub(crate) struct RenderPassContext { + pub attachments: AttachmentData, + pub sample_count: u8, +} + impl RenderPassContext { // Assumed the renderpass only contains one subpass pub(crate) fn compatible(&self, other: &RenderPassContext) -> bool { - self.colors == other.colors && self.depth_stencil == other.depth_stencil + self.attachments.colors == other.attachments.colors + && self.attachments.depth_stencil == other.attachments.depth_stencil + && self.sample_count == other.sample_count } } -pub(crate) type RenderPassKey = AttachmentData<(hal::pass::Attachment, hal::image::Layout)>; -pub(crate) type FramebufferKey = AttachmentData; -pub(crate) type RenderPassContext = AttachmentData; - type BufferMapResult = Result, hal::device::MapError>; type BufferMapPendingCallback = (resource::BufferMapOperation, resource::BufferMapAsyncStatus); @@ -1860,7 +1867,7 @@ impl Global { Box::into_raw(Box::new(encoder)) } - pub fn render_bundle_encoder_destroy( + pub fn render_bundle_encoder_destroy( &self, render_bundle_encoder: command::RenderBundleEncoder, ) { @@ -1871,14 +1878,19 @@ impl Global { let hub = B::hub(self); let mut token = Token::root(); - let (mut device_guard, mut token) = hub.devices.write(&mut token); - let mut bundle = { - let (mut render_bundle_guard, _) = hub.render_bundles.write(&mut token); - render_bundle_guard.remove(render_bundle_id).unwrap() + let (device_guard, mut token) = hub.devices.read(&mut token); + let device_id = { + let (mut bundle_guard, _) = hub.render_bundles.write(&mut token); + let bundle = &mut bundle_guard[render_bundle_id]; + bundle.life_guard.ref_count.take(); + bundle.device_id.value }; - let (_, device_id) = unsafe { bundle.raw.invalidate() }; - device_guard[device_id].untrack(&hub, &bundle.trackers, &mut token); + device_guard[device_id] + .lock_life(&mut token) + .suspected_resources + .render_bundles + .push(render_bundle_id); } pub fn device_create_render_pipeline( @@ -2177,9 +2189,12 @@ impl Global { }; let pass_context = RenderPassContext { - colors: color_states.iter().map(|state| state.format).collect(), - resolves: ArrayVec::new(), - depth_stencil: depth_stencil_state.map(|state| state.format), + attachments: AttachmentData { + colors: color_states.iter().map(|state| state.format).collect(), + resolves: ArrayVec::new(), + depth_stencil: depth_stencil_state.map(|state| state.format), + }, + sample_count: samples, }; let mut flags = pipeline::PipelineFlags::empty(); @@ -2211,7 +2226,6 @@ impl Global { flags, index_format: desc.vertex_state.index_format, vertex_strides, - sample_count: samples, life_guard: LifeGuard::new(), }; diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index e45202ae0e..0d2ece6e1e 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -2,10 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::{ - command::{BufferCopyView, TextureCopyView}, - id, -}; +use crate::id; #[cfg(feature = "trace")] use std::io::Write as _; use std::ops::Range; @@ -92,6 +89,28 @@ pub struct RenderPipelineDescriptor { pub alpha_to_coverage_enabled: bool, } +#[derive(Debug)] +#[cfg_attr(feature = "trace", derive(serde::Serialize))] +#[cfg_attr(feature = "replay", derive(serde::Deserialize))] +pub struct RenderBundleDescriptor { + pub label: String, + pub color_formats: Vec, + pub depth_stencil_format: Option, + pub sample_count: u32, +} + +#[cfg(feature = "trace")] +impl RenderBundleDescriptor { + pub(crate) fn new(label: super::Label, context: &super::RenderPassContext) -> Self { + RenderBundleDescriptor { + label: super::own_label(&label), + color_formats: context.attachments.colors.to_vec(), + depth_stencil_format: context.attachments.depth_stencil, + sample_count: context.sample_count as u32, + } + } +} + #[derive(Debug)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] #[cfg_attr(feature = "replay", derive(serde::Deserialize))] @@ -163,6 +182,13 @@ pub enum Action { desc: RenderPipelineDescriptor, }, DestroyRenderPipeline(id::RenderPipelineId), + CreateRenderBundle { + id: id::RenderBundleId, + desc: RenderBundleDescriptor, + commands: Vec, + dynamic_offsets: Vec, + }, + DestroyRenderBundle(id::RenderBundleId), WriteBuffer { id: id::BufferId, data: FileName, @@ -170,7 +196,7 @@ pub enum Action { queued: bool, }, WriteTexture { - to: TextureCopyView, + to: crate::command::TextureCopyView, data: FileName, layout: wgt::TextureDataLayout, size: wgt::Extent3d, @@ -190,18 +216,18 @@ pub enum Command { size: wgt::BufferAddress, }, CopyBufferToTexture { - src: BufferCopyView, - dst: TextureCopyView, + src: crate::command::BufferCopyView, + dst: crate::command::TextureCopyView, size: wgt::Extent3d, }, CopyTextureToBuffer { - src: TextureCopyView, - dst: BufferCopyView, + src: crate::command::TextureCopyView, + dst: crate::command::BufferCopyView, size: wgt::Extent3d, }, CopyTextureToTexture { - src: TextureCopyView, - dst: TextureCopyView, + src: crate::command::TextureCopyView, + dst: crate::command::TextureCopyView, size: wgt::Extent3d, }, RunComputePass { diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index 39e22ffe17..8ff49b97dc 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -174,7 +174,7 @@ impl Access> for Root {} impl Access> for Device {} impl Access> for Root {} impl Access> for Device {} -impl Access> for CommandBuffer {} +impl Access> for RenderBundle {} impl Access> for Root {} impl Access> for Device {} impl Access> for PipelineLayout {} @@ -187,6 +187,7 @@ impl Access> for Root {} impl Access> for Device {} impl Access> for SwapChain {} impl Access for Device {} +impl Access for CommandBuffer {} impl Access> for Device {} impl Access> for BindGroup {} impl Access> for Device {} diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index ca9170e033..726184cbcd 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -127,7 +127,6 @@ pub struct RenderPipeline { pub(crate) pass_context: RenderPassContext, pub(crate) flags: PipelineFlags, pub(crate) index_format: IndexFormat, - pub(crate) sample_count: u8, pub(crate) vertex_strides: Vec<(BufferAddress, InputStepMode)>, pub(crate) life_guard: LifeGuard, } diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index b3c67d9db5..95965590a1 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -237,11 +237,6 @@ impl ResourceTracker { self.map.clear(); } - /// Returns true if the tracker is empty. - pub fn is_empty(&self) -> bool { - self.map.is_empty() - } - /// Initialize a resource to be used. /// /// Returns false if the resource is already registered. @@ -444,6 +439,7 @@ pub(crate) struct TrackerSet { pub samplers: ResourceTracker>, pub compute_pipes: ResourceTracker>, pub render_pipes: ResourceTracker>, + pub bundles: ResourceTracker>, } impl TrackerSet { @@ -457,6 +453,7 @@ impl TrackerSet { samplers: ResourceTracker::new(backend), compute_pipes: ResourceTracker::new(backend), render_pipes: ResourceTracker::new(backend), + bundles: ResourceTracker::new(backend), } } @@ -469,6 +466,7 @@ impl TrackerSet { self.samplers.clear(); self.compute_pipes.clear(); self.render_pipes.clear(); + self.bundles.clear(); } /// Try to optimize the tracking representation. @@ -480,6 +478,7 @@ impl TrackerSet { self.samplers.optimize(); self.compute_pipes.optimize(); self.render_pipes.optimize(); + self.bundles.optimize(); } /// Merge all the trackers of another instance by extending @@ -494,6 +493,7 @@ impl TrackerSet { .merge_extend(&other.compute_pipes) .unwrap(); self.render_pipes.merge_extend(&other.render_pipes).unwrap(); + self.bundles.merge_extend(&other.bundles).unwrap(); } pub fn backend(&self) -> wgt::Backend { diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 516094c644..658c7feba7 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -10,15 +10,17 @@ use serde::Deserialize; use serde::Serialize; use std::{io, slice}; +pub type BufferAddress = u64; + /// Buffer-Texture copies on command encoders have to have the `bytes_per_row` /// aligned to this number. /// /// This doesn't apply to `Queue::write_texture`. pub const COPY_BYTES_PER_ROW_ALIGNMENT: u32 = 256; /// Bound uniform/storage buffer offsets must be aligned to this number. -pub const BIND_BUFFER_ALIGNMENT: u64 = 256; +pub const BIND_BUFFER_ALIGNMENT: BufferAddress = 256; /// Buffer to buffer copy offsets and sizes must be aligned to this number -pub const COPY_BUFFER_ALIGNMENT: u64 = 4; +pub const COPY_BUFFER_ALIGNMENT: BufferAddress = 4; #[repr(transparent)] #[derive(Clone, Copy, Debug, PartialEq)] @@ -33,7 +35,7 @@ pub const COPY_BUFFER_ALIGNMENT: u64 = 4; derive(serde::Deserialize), serde(from = "SerBufferSize") )] -pub struct BufferSize(pub u64); +pub struct BufferSize(pub BufferAddress); impl BufferSize { pub const WHOLE: BufferSize = BufferSize(!0); @@ -295,8 +297,6 @@ pub enum TextureViewDimension { D3, } -pub type BufferAddress = u64; - #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "trace", derive(Serialize))] @@ -545,6 +545,12 @@ pub enum IndexFormat { Uint32 = 1, } +impl Default for IndexFormat { + fn default() -> Self { + IndexFormat::Uint32 + } +} + #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "trace", derive(Serialize))]