69: Swapchain resize r=kvark a=kvark

Based on #67 

Here are the steps (as outlined on Gitter) that this PR follows:
  1. create a dummy frame in the swapchain (`SwapChain::outdated`). We return it when we aren't able to acquire a real frame. No synchronization is done atm, but shouldn't be anything critical there.
  2. handle the errors on acquire and present, use the dummy frame where needed. Presentation errors are just ignored, while acquiring errors are forcing the dummy frame. The idea is that the user would know about a swapchain resize from some kind of event loop / WSI, and thus they'd know when they should actually re-create it.
  3. associate surface with a swapchain. We merge the IDs since there can't be multiple swapchains on the same surface in the near future. Merging simplifies a lot of things in the implementation, but this is to be revised for sure once we get a better look on the browser integration.
  4. when the swapchain is re-created, consume the old one associated with a surface.


Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot] 2019-02-22 17:37:28 +00:00
commit 022087b0b8
9 changed files with 245 additions and 119 deletions

View File

@ -88,9 +88,22 @@ struct Cube {
index_buf: wgpu::Buffer,
index_count: usize,
bind_group: wgpu::BindGroup,
uniform_buf: wgpu::Buffer,
pipeline: wgpu::RenderPipeline,
}
impl Cube {
fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4<f32> {
let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 10.0);
let mx_view = cgmath::Matrix4::look_at(
cgmath::Point3::new(1.5f32, -5.0, 3.0),
cgmath::Point3::new(0f32, 0.0, 0.0),
cgmath::Vector3::unit_z(),
);
mx_projection * mx_view
}
}
impl framework::Example for Cube {
fn init(device: &mut wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self {
use std::mem;
@ -196,18 +209,9 @@ impl framework::Example for Cube {
size: 64,
usage: wgpu::BufferUsageFlags::UNIFORM | wgpu::BufferUsageFlags::TRANSFER_DST,
});
{
let aspect_ratio = sc_desc.width as f32 / sc_desc.height as f32;
let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 10.0);
let mx_view = cgmath::Matrix4::look_at(
cgmath::Point3::new(1.5f32, -5.0, 3.0),
cgmath::Point3::new(0f32, 0.0, 0.0),
cgmath::Vector3::unit_z(),
);
let mx_total = mx_projection * mx_view;
let mx_raw: &[f32; 16] = mx_total.as_ref();
uniform_buf.set_sub_data(0, framework::cast_slice(&mx_raw[..]));
}
let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32);
let mx_ref: &[f32; 16] = mx_total.as_ref();
uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..]));
// Create bind group
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
@ -294,11 +298,17 @@ impl framework::Example for Cube {
index_buf,
index_count: index_data.len(),
bind_group,
uniform_buf,
pipeline,
}
}
fn update(&mut self, _event: wgpu::winit::WindowEvent) {
fn update(&mut self, event: wgpu::winit::WindowEvent) {
if let wgpu::winit::WindowEvent::Resized(size) = event {
let mx_total = Self::generate_matrix(size.width as f32 / size.height as f32);
let mx_ref: &[f32; 16] = mx_total.as_ref();
self.uniform_buf.set_sub_data(0, framework::cast_slice(&mx_ref[..]));
}
}
fn render(&mut self, frame: &wgpu::SwapChainOutput, device: &mut wgpu::Device) {

View File

@ -70,7 +70,7 @@ pub fn run<E: Example>(title: &str) {
.to_physical(window.get_hidpi_factor());
let surface = instance.create_surface(&window);
let sc_desc = wgpu::SwapChainDescriptor {
let mut sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsageFlags::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::B8g8r8a8Unorm,
width: size.width as u32,
@ -91,7 +91,11 @@ pub fn run<E: Example>(title: &str) {
..
} => {
let physical = size.to_physical(window.get_hidpi_factor());
info!("Resized to {:?}", physical);
info!("Resizing to {:?}", physical);
sc_desc.width = physical.width as u32;
sc_desc.height = physical.height as u32;
swap_chain = device.create_swap_chain(&surface, &sc_desc);
example.update(WindowEvent::Resized(size));
}
Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {

View File

@ -435,10 +435,10 @@ typedef struct {
WGPUByteArray code;
} WGPUShaderModuleDescriptor;
typedef WGPUId WGPUSwapChainId;
typedef WGPUId WGPUSurfaceId;
typedef WGPUSurfaceId WGPUSwapChainId;
typedef uint32_t WGPUTextureUsageFlags;
typedef struct {

View File

@ -98,9 +98,8 @@ unsafe impl<B: hal::Backend> Send for DestroyedResources<B> {}
unsafe impl<B: hal::Backend> Sync for DestroyedResources<B> {}
impl<B: hal::Backend> DestroyedResources<B> {
fn add(&mut self, resource_id: ResourceId, life_guard: &LifeGuard) {
self.referenced
.push((resource_id, life_guard.ref_count.clone()));
fn add(&mut self, resource_id: ResourceId, ref_count: RefCount) {
self.referenced.push((resource_id, ref_count));
}
/// Returns the last submission index that is done.
@ -139,7 +138,11 @@ impl<B: hal::Backend> DestroyedResources<B> {
}
impl DestroyedResources<back::Backend> {
fn triage_referenced(&mut self) {
fn triage_referenced(
&mut self,
buffer_tracker: &mut BufferTracker,
texture_tracker: &mut TextureTracker,
) {
for i in (0..self.referenced.len()).rev() {
// one in resource itself, and one here in this list
let num_refs = self.referenced[i].1.load();
@ -148,11 +151,13 @@ impl DestroyedResources<back::Backend> {
let resource_id = self.referenced.swap_remove(i).0;
let (submit_index, resource) = match resource_id {
ResourceId::Buffer(id) => {
buffer_tracker.remove(id);
let buf = HUB.buffers.unregister(id);
let si = buf.life_guard.submission_index.load(Ordering::Acquire);
(si, Resource::Buffer(buf))
}
ResourceId::Texture(id) => {
texture_tracker.remove(id);
let tex = HUB.textures.unregister(id);
let si = tex.life_guard.submission_index.load(Ordering::Acquire);
(si, Resource::Texture(tex))
@ -369,7 +374,10 @@ pub extern "C" fn wgpu_buffer_destroy(buffer_id: BufferId) {
.get(buffer.device_id.value)
.destroyed
.lock()
.add(ResourceId::Buffer(buffer_id), &buffer.life_guard);
.add(
ResourceId::Buffer(buffer_id),
buffer.life_guard.ref_count.clone(),
);
}
@ -579,7 +587,10 @@ pub extern "C" fn wgpu_texture_destroy(texture_id: TextureId) {
.get(texture.device_id.value)
.destroyed
.lock()
.add(ResourceId::Texture(texture_id), &texture.life_guard);
.add(
ResourceId::Texture(texture_id),
texture.life_guard.ref_count.clone(),
);
}
#[no_mangle]
@ -595,7 +606,10 @@ pub extern "C" fn wgpu_texture_view_destroy(texture_view_id: TextureViewId) {
.get(device_id)
.destroyed
.lock()
.add(ResourceId::TextureView(texture_view_id), &view.life_guard);
.add(
ResourceId::TextureView(texture_view_id),
view.life_guard.ref_count.clone(),
);
}
@ -902,6 +916,8 @@ pub extern "C" fn wgpu_queue_submit(
.life_guard
.submission_index
.fetch_add(1, Ordering::Relaxed);
let mut buffer_tracker = device.buffer_tracker.lock();
let mut texture_tracker = device.texture_tracker.lock();
//TODO: if multiple command buffers are submitted, we can re-use the last
// native command buffer of the previous chain instead of always creating
@ -910,8 +926,6 @@ pub extern "C" fn wgpu_queue_submit(
let mut command_buffer_guard = HUB.command_buffers.write();
let buffer_guard = HUB.buffers.read();
let texture_guard = HUB.textures.read();
let mut buffer_tracker = device.buffer_tracker.lock();
let mut texture_tracker = device.texture_tracker.lock();
// finish all the command buffers first
for &cmb_id in command_buffer_ids {
@ -965,17 +979,20 @@ pub extern "C" fn wgpu_queue_submit(
let fence = device.raw.create_fence(false).unwrap();
{
let command_buffer_guard = HUB.command_buffers.read();
let swap_chain_guard = HUB.swap_chains.read();
let surface_guard = HUB.surfaces.read();
let wait_semaphores = swap_chain_links
.into_iter()
.map(|link| {
.flat_map(|link| {
//TODO: check the epoch
let sem = &swap_chain_guard
surface_guard
.get(link.swap_chain_id.0)
.frames[link.image_index as usize]
.sem_available;
(sem, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)
.swap_chain
.as_ref()
.map(|swap_chain| (
&swap_chain.frames[link.image_index as usize].sem_available,
hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT,
))
});
let submission =
@ -997,7 +1014,7 @@ pub extern "C" fn wgpu_queue_submit(
let last_done = {
let mut destroyed = device.destroyed.lock();
destroyed.triage_referenced();
destroyed.triage_referenced(&mut *buffer_tracker, &mut *texture_tracker);
let last_done = destroyed.cleanup(&device.raw);
destroyed.active.push(ActiveSubmission {
@ -1294,12 +1311,12 @@ pub extern "C" fn wgpu_device_create_compute_pipeline(
HUB.compute_pipelines.register(pipeline)
}
pub fn device_create_swap_chain(
device_id: DeviceId,
surface_id: SurfaceId,
desc: &swap_chain::SwapChainDescriptor,
) -> (swap_chain::SwapChain<back::Backend>, Vec<resource::Texture<back::Backend>>) {
outdated: swap_chain::OutdatedFrame,
) -> Vec<resource::Texture<back::Backend>> {
let device_guard = HUB.devices.read();
let device = device_guard.get(device_id);
let mut surface_guard = HUB.surfaces.write();
@ -1331,25 +1348,42 @@ pub fn device_create_swap_chain(
"Requested size {}x{} is outside of the supported range: {:?}",
desc.width, desc.height, caps.extents);
let (old_raw, sem_available, command_pool) = match surface.swap_chain.take() {
Some(mut old) => {
assert_eq!(old.device_id.value, device_id);
let mut destroyed = device.destroyed.lock();
destroyed.add(ResourceId::Texture(old.outdated.texture_id.value), old.outdated.texture_id.ref_count);
destroyed.add(ResourceId::TextureView(old.outdated.view_id.value), old.outdated.view_id.ref_count);
unsafe {
old.command_pool.reset()
};
(Some(old.raw), old.sem_available, old.command_pool)
}
_ => unsafe {
let sem_available = device.raw
.create_semaphore()
.unwrap();
let command_pool = device.raw
.create_command_pool_typed(
&device.queue_group,
hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL,
)
.unwrap();
(None, sem_available, command_pool)
}
};
let (raw, backbuffer) = unsafe {
device.raw
.create_swapchain(
&mut surface.raw,
config.with_image_usage(usage),
None,
old_raw,
)
.unwrap()
};
let command_pool = unsafe {
device.raw
.create_command_pool_typed(
&device.queue_group,
hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL,
)
.unwrap()
};
let swap_chain = swap_chain::SwapChain {
surface.swap_chain = Some(swap_chain::SwapChain {
raw,
device_id: Stored {
value: device_id,
@ -1357,16 +1391,17 @@ pub fn device_create_swap_chain(
},
frames: Vec::with_capacity(num_frames as usize),
acquired: Vec::with_capacity(1), //TODO: get it from gfx-hal?
sem_available: device.raw.create_semaphore().unwrap(),
sem_available,
outdated,
command_pool,
};
});
let images = match backbuffer {
hal::Backbuffer::Images(images) => images,
hal::Backbuffer::Framebuffer(_) => panic!("Deprecated API detected!"),
};
let textures = images
images
.into_iter()
.map(|raw| resource::Texture {
raw,
@ -1384,9 +1419,7 @@ pub fn device_create_swap_chain(
swap_chain_link: None,
life_guard: LifeGuard::new(),
})
.collect();
(swap_chain, textures)
.collect()
}
#[cfg(feature = "local")]
@ -1394,8 +1427,12 @@ fn swap_chain_populate_textures(
swap_chain_id: SwapChainId,
textures: Vec<resource::Texture<back::Backend>>,
) {
let mut swap_chain_guard = HUB.swap_chains.write();
let swap_chain = swap_chain_guard.get_mut(swap_chain_id);
let mut surface_guard = HUB.surfaces.write();
let swap_chain = surface_guard
.get_mut(swap_chain_id)
.swap_chain
.as_mut()
.unwrap();
let device_guard = HUB.devices.read();
let device = device_guard.get(swap_chain.device_id.value);
@ -1457,10 +1494,28 @@ pub extern "C" fn wgpu_device_create_swap_chain(
surface_id: SurfaceId,
desc: &swap_chain::SwapChainDescriptor,
) -> SwapChainId {
let (swap_chain, textures) = device_create_swap_chain(device_id, surface_id, desc);
let id = HUB.swap_chains.register(swap_chain);
swap_chain_populate_textures(id, textures);
id
let outdated = {
let outdated_texture = device_create_texture(device_id, &desc.to_texture_desc());
let texture_id = Stored {
ref_count: outdated_texture.life_guard.ref_count.clone(),
value: HUB.textures.register(outdated_texture),
};
device_track_texture(device_id, texture_id.value, texture_id.ref_count.clone());
let outdated_view = texture_create_default_view(texture_id.value);
let view_id = Stored {
ref_count: outdated_view.life_guard.ref_count.clone(),
value: HUB.texture_views.register(outdated_view),
};
swap_chain::OutdatedFrame {
texture_id,
view_id,
}
};
let textures = device_create_swap_chain(device_id, surface_id, desc, outdated);
swap_chain_populate_textures(surface_id, textures);
surface_id
}

View File

@ -4,7 +4,7 @@ use crate::{
RenderPassHandle, ComputePassHandle,
PipelineLayoutHandle, RenderPipelineHandle, ComputePipelineHandle, ShaderModuleHandle,
BufferHandle, SamplerHandle, TextureHandle, TextureViewHandle,
SurfaceHandle, SwapChainHandle,
SurfaceHandle,
};
use hal::backend::FastHashMap;
@ -126,7 +126,6 @@ pub struct Hub {
pub(crate) texture_views: Arc<Registry<TextureViewHandle>>,
pub(crate) samplers: Arc<Registry<SamplerHandle>>,
pub(crate) surfaces: Arc<Registry<SurfaceHandle>>,
pub(crate) swap_chains: Arc<Registry<SwapChainHandle>>,
}
lazy_static! {

View File

@ -54,10 +54,7 @@ pub extern "C" fn wgpu_instance_create_surface_from_winit(
.read()
.get(instance_id)
.create_surface(window);
let surface = SurfaceHandle {
raw,
};
let surface = SurfaceHandle::new(raw);
HUB.surfaces.register(surface)
}
@ -71,12 +68,11 @@ pub fn instance_create_surface_from_xlib(
unimplemented!();
#[cfg(all(unix, feature = "gfx-backend-vulkan"))]
SurfaceHandle {
raw: HUB.instances
.read()
.get(instance_id)
.create_surface_from_xlib(display, window),
}
SurfaceHandle::new(HUB.instances
.read()
.get(instance_id)
.create_surface_from_xlib(display, window)
)
}
#[cfg(feature = "local")]
@ -99,12 +95,11 @@ pub fn instance_create_surface_from_macos_layer(
unimplemented!();
#[cfg(feature = "gfx-backend-metal")]
SurfaceHandle {
raw: HUB.instances
.read()
.get(instance_id)
.create_surface_from_layer(layer as *mut _),
}
SurfaceHandle::new(HUB.instances
.read()
.get(instance_id)
.create_surface_from_layer(layer as *mut _)
)
}
#[cfg(feature = "local")]
@ -139,9 +134,7 @@ pub fn instance_create_surface_from_windows_hwnd(
.create_surface_from_hwnd(hinstance, hwnd);
#[cfg_attr(not(target_os = "windows"), allow(unreachable_code))]
SurfaceHandle {
raw,
}
SurfaceHandle::new(raw)
}
#[cfg(feature = "local")]

View File

@ -219,5 +219,4 @@ type ComputePassHandle = ComputePass<back::Backend>;
// Swap chain
pub type SurfaceId = hub::Id;
type SurfaceHandle = Surface<back::Backend>;
pub type SwapChainId = hub::Id;
type SwapChainHandle = SwapChain<back::Backend>;
pub type SwapChainId = SurfaceId;

View File

@ -1,4 +1,5 @@
use crate::{Stored, WeaklyStored,
use crate::{
Extent3d, Stored, WeaklyStored,
DeviceId, SwapChainId, TextureId, TextureViewId,
};
use crate::{conv, resource};
@ -8,7 +9,7 @@ use crate::track::{TrackPermit};
use hal;
use hal::{Device as _Device, Swapchain as _Swapchain};
use log::trace;
use log::{trace, warn};
use std::{iter, mem};
@ -23,6 +24,16 @@ pub(crate) struct SwapChainLink<E> {
pub struct Surface<B: hal::Backend> {
pub(crate) raw: B::Surface,
pub(crate) swap_chain: Option<SwapChain<B>>,
}
impl<B: hal::Backend> Surface<B> {
pub(crate) fn new(raw: B::Surface) -> Self {
Surface {
raw,
swap_chain: None,
}
}
}
pub(crate) struct Frame<B: hal::Backend> {
@ -34,6 +45,12 @@ pub(crate) struct Frame<B: hal::Backend> {
pub comb: hal::command::CommandBuffer<B, hal::General, hal::command::MultiShot>,
}
pub struct OutdatedFrame {
pub(crate) texture_id: Stored<TextureId>,
pub(crate) view_id: Stored<TextureViewId>,
}
const OUTDATED_IMAGE_INDEX: u32 = !0;
//TODO: does it need a ref-counted lifetime?
pub struct SwapChain<B: hal::Backend> {
@ -42,6 +59,7 @@ pub struct SwapChain<B: hal::Backend> {
pub(crate) frames: Vec<Frame<B>>,
pub(crate) acquired: Vec<hal::SwapImageIndex>,
pub(crate) sem_available: B::Semaphore,
pub(crate) outdated: OutdatedFrame,
#[cfg_attr(not(feature = "local"), allow(dead_code))] //TODO: remove
pub(crate) command_pool: hal::CommandPool<B, hal::General>,
}
@ -54,6 +72,22 @@ pub struct SwapChainDescriptor {
pub height: u32,
}
impl SwapChainDescriptor {
pub fn to_texture_desc(&self) -> resource::TextureDescriptor {
resource::TextureDescriptor {
size: Extent3d {
width: self.width,
height: self.height,
depth: 1,
},
array_size: 1,
dimension: resource::TextureDimension::D2,
format: self.format,
usage: self.usage,
}
}
}
#[repr(C)]
pub struct SwapChainOutput {
pub texture_id: TextureId,
@ -64,37 +98,51 @@ pub struct SwapChainOutput {
pub extern "C" fn wgpu_swap_chain_get_next_texture(
swap_chain_id: SwapChainId,
) -> SwapChainOutput {
let mut swap_chain_guard = HUB.swap_chains.write();
let swap_chain = swap_chain_guard.get_mut(swap_chain_id);
assert_ne!(swap_chain.acquired.len(), swap_chain.acquired.capacity(),
"Unable to acquire any more swap chain images before presenting");
let mut surface_guard = HUB.surfaces.write();
let swap_chain = surface_guard
.get_mut(swap_chain_id)
.swap_chain
.as_mut()
.unwrap();
let device_guard = HUB.devices.read();
let device = device_guard.get(swap_chain.device_id.value);
let image_index = unsafe {
assert_ne!(swap_chain.acquired.len(), swap_chain.acquired.capacity(),
"Unable to acquire any more swap chain images before presenting");
match {
let sync = hal::FrameSync::Semaphore(&swap_chain.sem_available);
swap_chain.raw.acquire_image(!0, sync).unwrap()
};
unsafe { swap_chain.raw.acquire_image(!0, sync) }
} {
Ok(image_index) => {
swap_chain.acquired.push(image_index);
let frame = &mut swap_chain.frames[image_index as usize];
unsafe {
device.raw.wait_for_fence(&frame.fence, !0).unwrap();
}
swap_chain.acquired.push(image_index);
let frame = &mut swap_chain.frames[image_index as usize];
unsafe {
device.raw.wait_for_fence(&frame.fence, !0).unwrap();
}
mem::swap(&mut frame.sem_available, &mut swap_chain.sem_available);
mem::swap(&mut frame.sem_available, &mut swap_chain.sem_available);
let texture_guard = HUB.textures.read();
let texture = texture_guard.get(frame.texture_id.value);
match texture.swap_chain_link {
Some(ref link) => *link.epoch.lock() += 1,
None => unreachable!(),
}
let texture_guard = HUB.textures.read();
let texture = texture_guard.get(frame.texture_id.value);
match texture.swap_chain_link {
Some(ref link) => *link.epoch.lock() += 1,
None => unreachable!(),
}
SwapChainOutput {
texture_id: frame.texture_id.value,
view_id: frame.view_id.value,
SwapChainOutput {
texture_id: frame.texture_id.value,
view_id: frame.view_id.value,
}
}
Err(e) => {
warn!("acquire_image failed: {:?}", e);
swap_chain.acquired.push(OUTDATED_IMAGE_INDEX);
SwapChainOutput {
texture_id: swap_chain.outdated.texture_id.value,
view_id: swap_chain.outdated.view_id.value,
}
}
}
}
@ -102,13 +150,24 @@ pub extern "C" fn wgpu_swap_chain_get_next_texture(
pub extern "C" fn wgpu_swap_chain_present(
swap_chain_id: SwapChainId,
) {
let mut swap_chain_guard = HUB.swap_chains.write();
let swap_chain = swap_chain_guard.get_mut(swap_chain_id);
let mut device_guard = HUB.devices.write();
let device = device_guard.get_mut(swap_chain.device_id.value);
let mut surface_guard = HUB.surfaces.write();
let swap_chain = surface_guard
.get_mut(swap_chain_id)
.swap_chain
.as_mut()
.unwrap();
let image_index = swap_chain.acquired.remove(0);
let frame = &mut swap_chain.frames[image_index as usize];
let frame = match swap_chain.frames.get_mut(image_index as usize) {
Some(frame) => frame,
None => {
assert_eq!(image_index, OUTDATED_IMAGE_INDEX);
return
}
};
let mut device_guard = HUB.devices.write();
let device = device_guard.get_mut(swap_chain.device_id.value);
let texture_guard = HUB.textures.read();
let texture = texture_guard.get(frame.texture_id.value);
@ -138,7 +197,7 @@ pub extern "C" fn wgpu_swap_chain_present(
range: texture.full_range.clone(),
});
unsafe {
let err = unsafe {
frame.comb.begin(false);
frame.comb.pipeline_barrier(
all_image_stages() .. hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT,
@ -157,11 +216,13 @@ pub extern "C" fn wgpu_swap_chain_present(
device.raw.reset_fence(&frame.fence).unwrap();
let queue = &mut device.queue_group.queues[0];
queue.submit(submission, Some(&frame.fence));
queue
.present(
iter::once((&swap_chain.raw, image_index)),
iter::once(&frame.sem_present),
)
.unwrap();
queue.present(
iter::once((&swap_chain.raw, image_index)),
iter::once(&frame.sem_present),
)
};
if let Err(e) = err {
warn!("present failed: {:?}", e);
}
}

View File

@ -87,6 +87,11 @@ impl<I: Clone + Hash + Eq, U: Copy + GenericUsage + BitOr<Output = U> + PartialE
}
}
/// Remove an id from the tracked map.
pub(crate) fn remove(&mut self, id: I) -> bool {
self.map.remove(&WeaklyStored(id)).is_some()
}
/// Get the last usage on a resource.
pub(crate) fn query(&mut self, stored: &Stored<I>, default: U) -> Query<U> {
match self.map.entry(WeaklyStored(stored.value.clone())) {