1147: Update render pass and framebuffers logic to the latest gfx-hal r=cwfitzgerald a=kvark

**Connections**
Updates gfx-hal with the latest major changes of https://github.com/gfx-rs/gfx/pull/3571

**Description**
There is no need to track framebuffers at all any more! Since they don't contain images, we can keep them permanently.
There is no need to create and delete framebuffers for the swapchain either. Lots of non-trivial code is gone 🎉
The PR also removes one of the mutexes that the device holds. 

**Testing**
Will be tested on wgpu-rs

Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot]
2021-01-15 20:59:38 +00:00
committed by GitHub
11 changed files with 461 additions and 598 deletions

537
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ members = [
"wgpu-types",
]
#[patch."https://github.com/gfx-rs/gfx"]
[patch."https://github.com/gfx-rs/gfx"]
#hal = { package = "gfx-hal", path = "../gfx/src/hal" }
#gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan", features = ["naga"] }
#gfx-backend-metal = { path = "../gfx/src/backend/metal", features = ["naga"] }
@@ -15,5 +15,5 @@ members = [
#gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" }
#gfx-backend-empty = { path = "../gfx/src/backend/empty" }
#[patch."https://github.com/gfx-rs/naga"]
[patch."https://github.com/gfx-rs/naga"]
#naga = { path = "../naga" }

View File

@@ -15,12 +15,12 @@ publish = false
[features]
[dependencies]
env_logger = "0.7"
env_logger = "0.8"
log = "0.4"
raw-window-handle = "0.3"
renderdoc = { version = "0.8", optional = true, default_features = false }
renderdoc = { version = "0.10", optional = true, default_features = false }
ron = "0.6"
winit = { version = "0.22", optional = true }
winit = { version = "0.24", optional = true }
[dependencies.wgt]
path = "../wgpu-types"

View File

@@ -33,27 +33,27 @@ smallvec = "1"
tracing = { version = "0.1", default-features = false, features = ["std"] }
thiserror = "1"
gpu-alloc = { git = "https://github.com/zakarumych/gpu-alloc", rev = "29e761f24edc50e28d238e723503b146d55d222e", features = ["tracing"] }
gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "df74fd8c7bea03149058a41aab0e4fe04077b266", features = ["tracing"] }
gpu-alloc = { git = "https://github.com/zakarumych/gpu-alloc", rev = "15f4fe2cebbd0bf9ab446757a8e370e0adf4c502", features = ["tracing"] }
gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "aa092613889f03f8254d6f7278d08c655324c7c7", features = ["tracing"] }
hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4" }
gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4" }
hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e" }
gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e" }
[target.'cfg(all(not(target_arch = "wasm32"), all(unix, not(target_os = "ios"), not(target_os = "macos"))))'.dependencies]
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4", features = ["naga"] }
gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4", features = ["naga"] }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e", features = ["naga"] }
gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e", features = ["naga"] }
[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies]
gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4", features = ["naga"] }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4", optional = true }
gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e", features = ["naga"] }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e", optional = true }
[target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies]
gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4" }
gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4" }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4", features = ["naga"] }
gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e" }
gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e" }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e", features = ["naga"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "6b3a1e36939473f0062232baf11e1deacd6605f4", features = ["naga"] }
gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "7dc99d525cc7ad04161f227993181f36c68fe86e", features = ["naga"] }
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"

View File

@@ -43,7 +43,7 @@ pub struct CommandBuffer<B: hal::Backend> {
recorded_thread_id: ThreadId,
pub(crate) device_id: Stored<id::DeviceId>,
pub(crate) trackers: TrackerSet,
pub(crate) used_swap_chains: SmallVec<[(Stored<id::SwapChainId>, B::Framebuffer); 1]>,
pub(crate) used_swap_chains: SmallVec<[Stored<id::SwapChainId>; 1]>,
limits: wgt::Limits,
private_features: PrivateFeatures,
has_labels: bool,
@@ -217,7 +217,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
Ok(cmd_buf) => {
cmd_buf.is_recording = false;
// stop tracking the swapchain image, if used
for (ref sc_id, _) in cmd_buf.used_swap_chains.iter() {
for sc_id in cmd_buf.used_swap_chains.iter() {
let view_id = swap_chain_guard[sc_id.value]
.acquired_view_id
.as_ref()

View File

@@ -11,8 +11,8 @@ use crate::{
},
conv,
device::{
AttachmentData, AttachmentDataVec, Device, FramebufferKey, RenderPassCompatibilityError,
RenderPassContext, RenderPassKey, MAX_COLOR_TARGETS, MAX_VERTEX_BUFFERS,
AttachmentData, AttachmentDataVec, Device, RenderPassCompatibilityError, RenderPassContext,
RenderPassKey, RenderPassLock, MAX_COLOR_TARGETS, MAX_VERTEX_BUFFERS,
},
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token},
id,
@@ -42,6 +42,7 @@ use std::{
borrow::{Borrow, Cow},
collections::hash_map::Entry,
fmt, iter,
marker::PhantomData,
num::NonZeroU32,
ops::Range,
str,
@@ -389,15 +390,10 @@ pub enum RenderPassErrorInner {
InvalidAttachment(id::TextureViewId),
#[error("necessary attachments are missing")]
MissingAttachments,
#[error("color and/or depth attachments have differing sizes: a {mismatching_attachment_type_name} attachment of size {mismatching_dimensions:?} is not compatible with a {previous_attachment_type_name} attachment's size {previous_dimensions:?}",
)]
#[error("attachments have differing sizes: {previous:?} is followed by {mismatch:?}")]
AttachmentsDimensionMismatch {
/// depth or color
previous_attachment_type_name: &'static str,
/// depth or color
mismatching_attachment_type_name: &'static str,
previous_dimensions: (u32, u32),
mismatching_dimensions: (u32, u32),
previous: (&'static str, wgt::Extent3d),
mismatch: (&'static str, wgt::Extent3d),
},
#[error("attachment's sample count {0} is invalid")]
InvalidSampleCount(u8),
@@ -407,11 +403,6 @@ pub enum RenderPassErrorInner {
InvalidResolveTargetSampleCount,
#[error("not enough memory left")]
OutOfMemory,
#[error("extent state {state_extent:?} must match extent from view {view_extent:?}")]
ExtentStateMismatch {
state_extent: hal::image::Extent,
view_extent: hal::image::Extent,
},
#[error("attempted to use a swap chain image as a depth/stencil attachment")]
SwapChainImageAsDepthStencil,
#[error("unable to clear non-present/read-only depth")]
@@ -503,15 +494,14 @@ struct RenderAttachment<'a> {
new_use: TextureUse,
}
type UsedSwapChainInfo<F> = Option<(Stored<id::SwapChainId>, F)>;
struct RenderPassInfo<'a, B: hal::Backend> {
context: RenderPassContext,
trackers: TrackerSet,
render_attachments: AttachmentDataVec<RenderAttachment<'a>>,
used_swapchain_with_framebuffer: UsedSwapChainInfo<B::Framebuffer>,
used_swap_chain: Option<Stored<id::SwapChainId>>,
is_ds_read_only: bool,
extent: wgt::Extent3d,
_phantom: PhantomData<B>,
}
impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
@@ -532,25 +522,19 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
let mut render_attachments = AttachmentDataVec::<RenderAttachment>::new();
let mut attachment_width = None;
let mut attachment_height = None;
let mut attachment_type_name = "";
let mut mismatching_dimensions = None;
let mut extent = None;
let mut sample_count = 0;
let mut depth_stencil_aspects = hal::format::Aspects::empty();
let mut used_swap_chain = None::<Stored<id::SwapChainId>>;
let mut trackers = TrackerSet::new(B::VARIANT);
let mut used_swapchain_with_framebuffer = None;
let mut add_view = |view: &TextureView<B>| {
let mut add_view = |view: &TextureView<B>, type_name| {
if let Some(ex) = extent {
if ex != view.extent {
return Err(RenderPassErrorInner::ExtentStateMismatch {
state_extent: ex,
view_extent: view.extent,
return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
previous: (attachment_type_name, ex),
mismatch: (type_name, view.extent),
});
}
} else {
@@ -564,6 +548,7 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
expected: sample_count,
});
}
attachment_type_name = type_name;
Ok(())
};
@@ -574,25 +559,7 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
.views
.use_extend(&*view_guard, at.attachment, (), ())
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.attachment))?;
add_view(view)?;
// get the width and height set by another attachment, then check if it matches
let (previous_width, previous_height) = (
*attachment_width.get_or_insert(view.extent.width),
*attachment_height.get_or_insert(view.extent.height),
);
if previous_width != view.extent.width || previous_height != view.extent.height
{
mismatching_dimensions = Some((
"depth",
(view.extent.width, view.extent.height),
(previous_width, previous_height),
));
} else {
// this attachment's size was successfully inserted into `attachment_width` and `attachment_height`
attachment_type_name = "depth";
}
add_view(view, "depth")?;
depth_stencil_aspects = view.aspects;
@@ -650,24 +617,7 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
.views
.use_extend(&*view_guard, at.attachment, (), ())
.map_err(|_| RenderPassErrorInner::InvalidAttachment(at.attachment))?;
add_view(view)?;
// get the width and height set by another attachment, then check if it matches
let (previous_width, previous_height) = (
*attachment_width.get_or_insert(view.extent.width),
*attachment_height.get_or_insert(view.extent.height),
);
if previous_width != view.extent.width || previous_height != view.extent.height {
mismatching_dimensions = Some((
"color",
(view.extent.width, view.extent.height),
(previous_width, previous_height),
));
} else {
// this attachment's size was successfully inserted into `attachment_width` and `attachment_height`
attachment_type_name = "color";
}
add_view(view, "color")?;
let layouts = match view.inner {
TextureViewInner::Native { ref source_id, .. } => {
@@ -719,29 +669,15 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
colors.push((color_at, hal::image::Layout::ColorAttachmentOptimal));
}
if let Some((
mismatching_attachment_type_name,
previous_dimensions,
mismatching_dimensions,
)) = mismatching_dimensions
{
return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
previous_attachment_type_name: attachment_type_name,
mismatching_attachment_type_name,
previous_dimensions,
mismatching_dimensions,
});
}
for resolve_target in color_attachments.iter().flat_map(|at| at.resolve_target) {
let view = trackers
.views
.use_extend(&*view_guard, resolve_target, (), ())
.map_err(|_| RenderPassErrorInner::InvalidAttachment(resolve_target))?;
if extent != Some(view.extent) {
return Err(RenderPassErrorInner::ExtentStateMismatch {
state_extent: extent.unwrap_or_default(),
view_extent: view.extent,
return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
previous: (attachment_type_name, extent.unwrap_or_default()),
mismatch: ("resolve", view.extent),
});
}
if view.samples != 1 {
@@ -809,8 +745,11 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
return Err(RenderPassErrorInner::InvalidSampleCount(sample_count));
}
let mut render_pass_cache = device.render_passes.lock();
let render_pass = match render_pass_cache.entry(rp_key.clone()) {
let RenderPassLock {
ref mut render_passes,
ref mut framebuffers,
} = *device.render_passes.lock();
let render_pass = match render_passes.entry(rp_key.clone()) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(entry) => {
let color_ids: [hal::pass::AttachmentRef; MAX_COLOR_TARGETS] = [
@@ -871,147 +810,122 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
}
};
let mut framebuffer_cache;
let fb_key = FramebufferKey {
let view_data = AttachmentData {
colors: color_attachments
.iter()
.map(|at| id::Valid(at.attachment))
.map(|at| view_guard.get(at.attachment).unwrap())
.collect(),
resolves: color_attachments
.iter()
.filter_map(|at| at.resolve_target)
.map(id::Valid)
.map(|attachment| view_guard.get(attachment).unwrap())
.collect(),
depth_stencil: depth_stencil_attachment.map(|at| id::Valid(at.attachment)),
depth_stencil: depth_stencil_attachment
.map(|at| view_guard.get(at.attachment).unwrap()),
};
let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
let fb_key = (
view_data.map(|view| view.framebuffer_attachment.clone()),
extent,
);
let context = RenderPassContext {
attachments: AttachmentData {
colors: fb_key
.colors
.iter()
.map(|&at| view_guard[at].format)
.collect(),
resolves: fb_key
.resolves
.iter()
.map(|&at| view_guard[at].format)
.collect(),
depth_stencil: fb_key.depth_stencil.map(|at| view_guard[at].format),
},
attachments: view_data.map(|view| view.format),
sample_count,
};
let framebuffer = match used_swap_chain.take() {
Some(sc_id) => {
// Always create a new framebuffer and delete it after presentation.
let attachments = fb_key
.all()
.map(|&id| match view_guard[id].inner {
TextureViewInner::Native { ref raw, .. } => raw,
TextureViewInner::SwapChain { ref image, .. } => Borrow::borrow(image),
})
.collect::<AttachmentDataVec<_>>();
let framebuffer = unsafe {
// Cache framebuffers by the device.
let framebuffer = match framebuffers.entry(fb_key) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let fb = unsafe {
device
.raw
.create_framebuffer(&render_pass, attachments, extent.unwrap())
.create_framebuffer(
&render_pass,
e.key().0.all().map(|fat| fat.clone()),
conv::map_extent(&extent, wgt::TextureDimension::D3),
)
.or(Err(RenderPassErrorInner::OutOfMemory))?
};
used_swapchain_with_framebuffer = Some((sc_id, framebuffer));
&mut used_swapchain_with_framebuffer.as_mut().unwrap().1
}
None => {
// Cache framebuffers by the device.
framebuffer_cache = device.framebuffers.lock();
match framebuffer_cache.entry(fb_key) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let fb = {
let attachments = e
.key()
.all()
.map(|&id| match view_guard[id].inner {
TextureViewInner::Native { ref raw, .. } => raw,
TextureViewInner::SwapChain { ref image, .. } => {
Borrow::borrow(image)
}
})
.collect::<AttachmentDataVec<_>>();
unsafe {
device
.raw
.create_framebuffer(&render_pass, attachments, extent.unwrap())
.or(Err(RenderPassErrorInner::OutOfMemory))?
}
};
e.insert(fb)
}
}
e.insert(fb)
}
};
let rect = {
let ex = extent.unwrap();
hal::pso::Rect {
x: 0,
y: 0,
w: ex.width as _,
h: ex.height as _,
}
let rect = hal::pso::Rect {
x: 0,
y: 0,
w: extent.width as _,
h: extent.height as _,
};
let raw_views = view_data.map(|view| match view.inner {
TextureViewInner::Native { ref raw, .. } => raw,
TextureViewInner::SwapChain { ref image, .. } => Borrow::borrow(image),
});
let clear_values = color_attachments
let attachments = color_attachments
.iter()
.zip(&rp_key.colors)
.flat_map(|(at, (rat, _layout))| {
match at.channel.load_op {
LoadOp::Load => None,
LoadOp::Clear => {
use hal::format::ChannelType;
//TODO: validate sign/unsign and normalized ranges of the color values
let value = match rat.format.unwrap().base_format().1 {
ChannelType::Unorm
| ChannelType::Snorm
| ChannelType::Ufloat
| ChannelType::Sfloat
| ChannelType::Uscaled
| ChannelType::Sscaled
| ChannelType::Srgb => hal::command::ClearColor {
float32: conv::map_color_f32(&at.channel.clear_value),
},
ChannelType::Sint => hal::command::ClearColor {
sint32: conv::map_color_i32(&at.channel.clear_value),
},
ChannelType::Uint => hal::command::ClearColor {
uint32: conv::map_color_u32(&at.channel.clear_value),
},
};
Some(hal::command::ClearValue { color: value })
}
}
})
.chain(depth_stencil_attachment.and_then(|at| {
match (at.depth.load_op, at.stencil.load_op) {
(LoadOp::Load, LoadOp::Load) => None,
(LoadOp::Clear, _) | (_, LoadOp::Clear) => {
let value = hal::command::ClearDepthStencil {
depth: at.depth.clear_value,
stencil: at.stencil.clear_value,
};
Some(hal::command::ClearValue {
depth_stencil: value,
})
}
.zip(raw_views.colors)
.map(
|((at, (rat, _layout)), image_view)| hal::command::RenderAttachmentInfo {
image_view,
clear_value: match at.channel.load_op {
LoadOp::Load => Default::default(),
LoadOp::Clear => {
use hal::format::ChannelType;
//TODO: validate sign/unsign and normalized ranges of the color values
let value = match rat.format.unwrap().base_format().1 {
ChannelType::Unorm
| ChannelType::Snorm
| ChannelType::Ufloat
| ChannelType::Sfloat
| ChannelType::Uscaled
| ChannelType::Sscaled
| ChannelType::Srgb => hal::command::ClearColor {
float32: conv::map_color_f32(&at.channel.clear_value),
},
ChannelType::Sint => hal::command::ClearColor {
sint32: conv::map_color_i32(&at.channel.clear_value),
},
ChannelType::Uint => hal::command::ClearColor {
uint32: conv::map_color_u32(&at.channel.clear_value),
},
};
hal::command::ClearValue { color: value }
}
},
},
)
.chain(raw_views.resolves.into_iter().map(|image_view| {
hal::command::RenderAttachmentInfo {
image_view,
clear_value: Default::default(),
}
}))
.collect::<ArrayVec<[_; MAX_COLOR_TARGETS + 1]>>();
.chain(depth_stencil_attachment.zip(raw_views.depth_stencil).map(
|(at, image_view)| hal::command::RenderAttachmentInfo {
image_view,
clear_value: match (at.depth.load_op, at.stencil.load_op) {
(LoadOp::Load, LoadOp::Load) => Default::default(),
(LoadOp::Clear, _) | (_, LoadOp::Clear) => {
let value = hal::command::ClearDepthStencil {
depth: at.depth.clear_value,
stencil: at.stencil.clear_value,
};
hal::command::ClearValue {
depth_stencil: value,
}
}
},
},
));
unsafe {
raw.begin_render_pass(
render_pass,
framebuffer,
rect,
clear_values,
attachments,
hal::command::SubpassContents::Inline,
);
raw.set_scissors(0, iter::once(&rect));
@@ -1028,20 +942,17 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
context,
trackers,
render_attachments,
used_swapchain_with_framebuffer,
used_swap_chain,
is_ds_read_only,
extent: wgt::Extent3d {
width: attachment_width.ok_or(RenderPassErrorInner::MissingAttachments)?,
height: attachment_height.ok_or(RenderPassErrorInner::MissingAttachments)?,
depth: 1,
},
extent,
_phantom: PhantomData,
})
}
fn finish(
mut self,
texture_guard: &Storage<Texture<B>, id::TextureId>,
) -> Result<(TrackerSet, UsedSwapChainInfo<B::Framebuffer>), RenderPassErrorInner> {
) -> Result<(TrackerSet, Option<Stored<id::SwapChainId>>), RenderPassErrorInner> {
for ra in self.render_attachments {
let texture = &texture_guard[ra.texture_id.value];
check_texture_usage(texture.usage, TextureUsage::RENDER_ATTACHMENT)?;
@@ -1072,7 +983,7 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> {
.unwrap();
}
}
Ok((self.trackers, self.used_swapchain_with_framebuffer))
Ok((self.trackers, self.used_swap_chain))
}
}
@@ -1108,7 +1019,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (device_guard, mut token) = hub.devices.read(&mut token);
let (cmd_buf_raw, trackers, used_swapchain_with_framebuffer) = {
let (cmd_buf_raw, trackers, used_swapchain) = {
// read-only lock guard
let (cmb_guard, mut token) = hub.command_buffers.read(&mut token);
@@ -1866,9 +1777,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
raw.end_render_pass();
}
let (trackers, used_swapchain_with_framebuffer) =
info.finish(&*texture_guard).map_pass_err(scope)?;
(raw, trackers, used_swapchain_with_framebuffer)
let (trackers, used_swapchain) = info.finish(&*texture_guard).map_pass_err(scope)?;
(raw, trackers, used_swapchain)
};
let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token);
@@ -1877,9 +1787,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let cmd_buf =
CommandBuffer::get_encoder_mut(&mut *cmb_guard, encoder_id).map_pass_err(scope)?;
cmd_buf.has_labels |= base.label.is_some();
cmd_buf
.used_swap_chains
.extend(used_swapchain_with_framebuffer);
cmd_buf.used_swap_chains.extend(used_swapchain);
#[cfg(feature = "trace")]
if let Some(ref mut list) = cmd_buf.commands {

View File

@@ -14,7 +14,7 @@ use crate::{
hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Token},
id, resource,
track::TrackerSet,
FastHashMap, RefCount, Stored, SubmissionIndex,
RefCount, Stored, SubmissionIndex,
};
use copyless::VecHelper as _;
@@ -636,74 +636,6 @@ impl<B: GfxBackend> LifetimeTracker<B> {
}
}
pub(crate) fn triage_framebuffers<G: GlobalIdentityHandlerFactory>(
&mut self,
hub: &Hub<B, G>,
framebuffers: &mut FastHashMap<super::FramebufferKey, B::Framebuffer>,
token: &mut Token<super::Device<B>>,
) {
let (texture_view_guard, _) = hub.texture_views.read(token);
let remove_list = framebuffers
.keys()
.filter_map(|key| {
let mut last_submit = None;
let mut needs_cleanup = false;
// A framebuffer needs to be scheduled for cleanup, if there's at least one
// attachment is no longer valid.
for &at in key.all() {
// If this attachment is still registered, it's still valid
if texture_view_guard.contains(at.0) {
continue;
}
// This attachment is no longer registered, this framebuffer needs cleanup
needs_cleanup = true;
// Check if there's any active submissions that are still referring to this
// attachment, if there are we need to get the greatest submission index, as
// that's the last time this attachment is still valid
let mut attachment_last_submit = None;
for a in &self.active {
if a.last_resources.image_views.iter().any(|&(id, _)| id == at) {
let max = attachment_last_submit.unwrap_or(0).max(a.index);
attachment_last_submit = Some(max);
}
}
// Between all attachments, we need the smallest index, because that's the last
// time this framebuffer is still valid
if let Some(attachment_last_submit) = attachment_last_submit {
let min = last_submit
.unwrap_or(std::usize::MAX)
.min(attachment_last_submit);
last_submit = Some(min);
}
}
if needs_cleanup {
Some((key.clone(), last_submit.unwrap_or(0)))
} else {
None
}
})
.collect::<FastHashMap<_, _>>();
if !remove_list.is_empty() {
tracing::debug!("Free framebuffers {:?}", remove_list);
for (ref key, submit_index) in remove_list {
let framebuffer = framebuffers.remove(key).unwrap();
self.active
.iter_mut()
.find(|a| a.index == submit_index)
.map_or(&mut self.free_resources, |a| &mut a.last_resources)
.framebuffers
.push(framebuffer);
}
}
}
pub(crate) fn handle_mapping<G: GlobalIdentityHandlerFactory>(
&mut self,
hub: &Hub<B, G>,

View File

@@ -103,12 +103,23 @@ impl<T> AttachmentData<T> {
.chain(&self.resolves)
.chain(&self.depth_stencil)
}
pub(crate) fn map<U, F: Fn(&T) -> U>(&self, fun: F) -> AttachmentData<U> {
AttachmentData {
colors: self.colors.iter().map(&fun).collect(),
resolves: self.resolves.iter().map(&fun).collect(),
depth_stencil: self.depth_stencil.as_ref().map(&fun),
}
}
}
pub(crate) type AttachmentDataVec<T> = ArrayVec<[T; MAX_COLOR_TARGETS + MAX_COLOR_TARGETS + 1]>;
pub(crate) type RenderPassKey = AttachmentData<(hal::pass::Attachment, hal::image::Layout)>;
pub(crate) type FramebufferKey = AttachmentData<id::Valid<id::TextureViewId>>;
pub(crate) type FramebufferKey = (
AttachmentData<hal::image::FramebufferAttachment>,
wgt::Extent3d,
);
#[derive(Clone, Debug, Hash, PartialEq)]
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
@@ -211,11 +222,17 @@ fn fire_map_callbacks<I: IntoIterator<Item = BufferMapPendingCallback>>(callback
}
}
#[derive(Debug)]
pub(crate) struct RenderPassLock<B: hal::Backend> {
pub(crate) render_passes: FastHashMap<RenderPassKey, B::RenderPass>,
pub(crate) framebuffers: FastHashMap<FramebufferKey, B::Framebuffer>,
}
/// Structure describing a logical device. Some members are internally mutable,
/// stored behind mutexes.
/// TODO: establish clear order of locking for these:
/// `mem_allocator`, `desc_allocator`, `life_tracke`, `trackers`,
/// `render_passes`, `framebuffers`, `pending_writes`, `trace`.
/// `render_passes`, `pending_writes`, `trace`.
///
/// Currently, the rules are:
/// 1. `life_tracker` is locked after `hub.devices`, enforced by the type system
@@ -234,8 +251,7 @@ pub struct Device<B: hal::Backend> {
pub(crate) active_submission_index: SubmissionIndex,
/// Has to be locked temporarily only (locked last)
pub(crate) trackers: Mutex<TrackerSet>,
pub(crate) render_passes: Mutex<FastHashMap<RenderPassKey, B::RenderPass>>,
pub(crate) framebuffers: Mutex<FastHashMap<FramebufferKey, B::Framebuffer>>,
pub(crate) render_passes: Mutex<RenderPassLock<B>>,
// Life tracker should be locked right after the device and before anything else.
life_tracker: Mutex<life::LifetimeTracker<B>>,
temp_suspected: life::SuspectedResources,
@@ -297,8 +313,10 @@ impl<B: GfxBackend> Device<B> {
life_guard: LifeGuard::new("<device>"),
active_submission_index: 0,
trackers: Mutex::new(TrackerSet::new(B::VARIANT)),
render_passes: Mutex::new(FastHashMap::default()),
framebuffers: Mutex::new(FastHashMap::default()),
render_passes: Mutex::new(RenderPassLock {
render_passes: FastHashMap::default(),
framebuffers: FastHashMap::default(),
}),
life_tracker: Mutex::new(life::LifetimeTracker::new()),
temp_suspected: life::SuspectedResources::default(),
#[cfg(feature = "trace")]
@@ -359,7 +377,6 @@ impl<B: GfxBackend> Device<B> {
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(hub, &self.raw, &self.trackers, token);
life_tracker.cleanup(&self.raw, &self.mem_allocator, &self.desc_allocator);
@@ -591,12 +608,11 @@ impl<B: GfxBackend> Device<B> {
mip_level_count,
));
}
let mut view_capabilities = hal::image::ViewCapabilities::empty();
let mut view_caps = hal::image::ViewCapabilities::empty();
// 2D textures with array layer counts that are multiples of 6 could be cubemaps
// Following gpuweb/gpuweb#68 always add the hint in that case
if desc.dimension == TextureDimension::D2 && desc.size.depth % 6 == 0 {
view_capabilities |= hal::image::ViewCapabilities::KIND_CUBE;
view_caps |= hal::image::ViewCapabilities::KIND_CUBE;
};
// TODO: 2D arrays, cubemap arrays
@@ -610,7 +626,7 @@ impl<B: GfxBackend> Device<B> {
format,
hal::image::Tiling::Optimal,
usage,
view_capabilities,
view_caps,
)
.map_err(|err| match err {
hal::image::CreationError::OutOfMemory(_) => DeviceError::OutOfMemory,
@@ -642,6 +658,11 @@ impl<B: GfxBackend> Device<B> {
kind,
format: desc.format,
format_features,
framebuffer_attachment: hal::image::FramebufferAttachment {
usage,
format,
view_caps,
},
full_range: TextureSelector {
levels: 0..desc.mip_level_count as hal::image::Level,
layers: 0..kind.num_layers(),
@@ -775,6 +796,7 @@ impl<B: GfxBackend> Device<B> {
layer_start: desc.base_array_layer as _,
layer_count: desc.array_layer_count.map(|v| v.get() as _),
};
let hal_extent = texture.kind.extent().at_level(desc.base_mip_level as _);
let raw = unsafe {
self.raw
@@ -799,8 +821,13 @@ impl<B: GfxBackend> Device<B> {
aspects,
format: texture.format,
format_features: texture.format_features,
extent: texture.kind.extent().at_level(desc.base_mip_level as _),
extent: wgt::Extent3d {
width: hal_extent.width,
height: hal_extent.height,
depth: view_layer_count,
},
samples: texture.kind.num_samples(),
framebuffer_attachment: texture.framebuffer_attachment.clone(),
selector,
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
})
@@ -2172,7 +2199,7 @@ impl<B: GfxBackend> Device<B> {
.get(pipeline_layout_id)
.map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?;
let mut render_pass_cache = self.render_passes.lock();
let mut rp_lock = self.render_passes.lock();
let pipeline_desc = hal::pso::GraphicsPipelineDesc {
label: desc.label.as_ref().map(AsRef::as_ref),
primitive_assembler,
@@ -2185,7 +2212,7 @@ impl<B: GfxBackend> Device<B> {
layout: &layout.raw,
subpass: hal::pass::Subpass {
index: 0,
main_pass: match render_pass_cache.entry(rp_key) {
main_pass: match rp_lock.render_passes.entry(rp_key) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let pass = self
@@ -2312,10 +2339,11 @@ impl<B: hal::Backend> Device<B> {
unsafe {
desc_alloc.cleanup(&self.raw);
mem_alloc.clear(&self.raw);
for (_, rp) in self.render_passes.lock().drain() {
let rps = self.render_passes.into_inner();
for (_, rp) in rps.render_passes {
self.raw.destroy_render_pass(rp);
}
for (_, fbo) in self.framebuffers.lock().drain() {
for (_, fbo) in rps.framebuffers {
self.raw.destroy_framebuffer(fbo);
}
}
@@ -3987,6 +4015,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
validate_swap_chain_descriptor(&mut config, &caps)?;
let framebuffer_attachment = config.framebuffer_attachment();
unsafe {
B::get_surface_mut(surface)
@@ -4027,8 +4056,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.create_semaphore()
.or(Err(DeviceError::OutOfMemory))?,
acquired_view_id: None,
acquired_framebuffers: Vec::new(),
active_submission_index: 0,
framebuffer_attachment,
};
swap_chain_guard.insert(sc_id, swap_chain);
Ok(sc_id)

View File

@@ -514,19 +514,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
));
}
for (sc_id, fbo) in cmdbuf.used_swap_chains.drain(..) {
for sc_id in cmdbuf.used_swap_chains.drain(..) {
let sc = &mut swap_chain_guard[sc_id.value];
sc.active_submission_index = submit_index;
if sc.acquired_view_id.is_none() {
return Err(QueueSubmitError::SwapChainOutputDropped);
}
// For each swapchain, we only want to have at most 1 signaled semaphore.
if sc.acquired_framebuffers.is_empty() {
if sc.active_submission_index != submit_index {
sc.active_submission_index = submit_index;
// Only add a signal if this is the first time for this swapchain
// to be used in the submission.
signal_swapchain_semaphores.push(sc_id.value);
}
sc.acquired_framebuffers.push(fbo);
}
// optimize the tracked states

View File

@@ -206,6 +206,7 @@ pub struct Texture<B: hal::Backend> {
pub(crate) kind: hal::image::Kind,
pub(crate) format: wgt::TextureFormat,
pub(crate) format_features: wgt::TextureFormatFeatures,
pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
pub(crate) full_range: TextureSelector,
pub(crate) life_guard: LifeGuard,
}
@@ -307,8 +308,9 @@ pub struct TextureView<B: hal::Backend> {
pub(crate) aspects: hal::format::Aspects,
pub(crate) format: wgt::TextureFormat,
pub(crate) format_features: wgt::TextureFormatFeatures,
pub(crate) extent: hal::image::Extent,
pub(crate) extent: wgt::Extent3d,
pub(crate) samples: hal::image::NumSamples,
pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
pub(crate) selector: TextureSelector,
pub(crate) life_guard: LifeGuard,
}

View File

@@ -44,7 +44,7 @@ use crate::{
LifeGuard, PrivateFeatures, Stored, SubmissionIndex,
};
use hal::{self, device::Device as _, queue::CommandQueue as _, window::PresentationSurface as _};
use hal::{queue::CommandQueue as _, window::PresentationSurface as _};
use thiserror::Error;
use wgt::{SwapChainDescriptor, SwapChainStatus};
@@ -59,8 +59,8 @@ pub struct SwapChain<B: hal::Backend> {
pub(crate) num_frames: hal::window::SwapImageIndex,
pub(crate) semaphore: B::Semaphore,
pub(crate) acquired_view_id: Option<Stored<TextureViewId>>,
pub(crate) acquired_framebuffers: Vec<B::Framebuffer>,
pub(crate) active_submission_index: SubmissionIndex,
pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment,
}
impl<B: hal::Backend> crate::hub::Resource for SwapChain<B> {
@@ -188,12 +188,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
allowed_usages: wgt::TextureUsage::RENDER_ATTACHMENT,
flags: wgt::TextureFormatFeatureFlags::empty(),
},
extent: hal::image::Extent {
extent: wgt::Extent3d {
width: sc.desc.width,
height: sc.desc.height,
depth: 1,
},
samples: 1,
framebuffer_attachment: sc.framebuffer_attachment.clone(),
selector: TextureSelector {
layers: 0..1,
levels: 0..1,
@@ -282,12 +283,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
tracing::debug!(trace = true, "Presented. End of Frame");
for fbo in sc.acquired_framebuffers.drain(..) {
unsafe {
device.raw.destroy_framebuffer(fbo);
}
}
match result {
Ok(None) => Ok(SwapChainStatus::Good),
Ok(Some(_)) => Ok(SwapChainStatus::Suboptimal),