Replace Handle::Weak with Handle::Uuid. (#19896)

# Objective

- Progress towards #19024.

## Solution

- Remove `Handle::Weak`!

If users were relying on `Handle::Weak` for some purpose, they can
almost certainly replace it with raw `AssetId` instead. If they cannot,
they can make their own enum that holds either a Handle or an AssetId.
In either case, we don't need weak handles!

Sadly we still need Uuid handles since we rely on them for "default"
assets and "invalid" assets, as well as anywhere where a component wants
to impl default with a non-defaulted asset handle. One step at a time
though!
This commit is contained in:
andriyDev 2025-07-02 07:40:35 -07:00 committed by GitHub
parent 1a410efd24
commit d05c435848
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 143 additions and 127 deletions

View File

@ -32,7 +32,7 @@
use bevy_app::{App, Plugin};
#[cfg(feature = "smaa_luts")]
use bevy_asset::load_internal_binary_asset;
use bevy_asset::{embedded_asset, load_embedded_asset, weak_handle, Handle};
use bevy_asset::{embedded_asset, load_embedded_asset, uuid_handle, Handle};
#[cfg(not(feature = "smaa_luts"))]
use bevy_core_pipeline::tonemapping::lut_placeholder;
use bevy_core_pipeline::{
@ -81,10 +81,10 @@ use bevy_utils::prelude::default;
/// The handle of the area LUT, a KTX2 format texture that SMAA uses internally.
const SMAA_AREA_LUT_TEXTURE_HANDLE: Handle<Image> =
weak_handle!("569c4d67-c7fa-4958-b1af-0836023603c0");
uuid_handle!("569c4d67-c7fa-4958-b1af-0836023603c0");
/// The handle of the search LUT, a KTX2 format texture that SMAA uses internally.
const SMAA_SEARCH_LUT_TEXTURE_HANDLE: Handle<Image> =
weak_handle!("43b97515-252e-4c8a-b9af-f2fc528a1c27");
uuid_handle!("43b97515-252e-4c8a-b9af-f2fc528a1c27");
/// Adds support for subpixel morphological antialiasing, or SMAA.
pub struct SmaaPlugin;

View File

@ -7,10 +7,12 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
use core::{
any::TypeId,
hash::{Hash, Hasher},
marker::PhantomData,
};
use crossbeam_channel::{Receiver, Sender};
use disqualified::ShortName;
use thiserror::Error;
use uuid::Uuid;
/// Provides [`Handle`] and [`UntypedHandle`] _for a specific asset type_.
/// This should _only_ be used for one specific asset type.
@ -117,7 +119,7 @@ impl core::fmt::Debug for StrongHandle {
/// avoiding the need to store multiple copies of the same data.
///
/// If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
/// alive until the [`Handle`] is dropped. If a [`Handle`] is [`Handle::Weak`], it does not necessarily reference a live [`Asset`],
/// alive until the [`Handle`] is dropped. If a [`Handle`] is [`Handle::Uuid`], it does not necessarily reference a live [`Asset`],
/// nor will it keep assets alive.
///
/// Modifying a *handle* will change which existing asset is referenced, but modifying the *asset*
@ -133,16 +135,16 @@ pub enum Handle<A: Asset> {
/// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
/// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata.
Strong(Arc<StrongHandle>),
/// A "weak" reference to an [`Asset`]. If a [`Handle`] is [`Handle::Weak`], it does not necessarily reference a live [`Asset`],
/// nor will it keep assets alive.
Weak(AssetId<A>),
/// A reference to an [`Asset`] using a stable-across-runs / const identifier. Dropping this
/// handle will not result in the asset being dropped.
Uuid(Uuid, #[reflect(ignore, clone)] PhantomData<fn() -> A>),
}
impl<T: Asset> Clone for Handle<T> {
fn clone(&self) -> Self {
match self {
Handle::Strong(handle) => Handle::Strong(handle.clone()),
Handle::Weak(id) => Handle::Weak(*id),
Handle::Uuid(uuid, ..) => Handle::Uuid(*uuid, PhantomData),
}
}
}
@ -153,7 +155,7 @@ impl<A: Asset> Handle<A> {
pub fn id(&self) -> AssetId<A> {
match self {
Handle::Strong(handle) => handle.id.typed_unchecked(),
Handle::Weak(id) => *id,
Handle::Uuid(uuid, ..) => AssetId::Uuid { uuid: *uuid },
}
}
@ -162,14 +164,14 @@ impl<A: Asset> Handle<A> {
pub fn path(&self) -> Option<&AssetPath<'static>> {
match self {
Handle::Strong(handle) => handle.path.as_ref(),
Handle::Weak(_) => None,
Handle::Uuid(..) => None,
}
}
/// Returns `true` if this is a weak handle.
/// Returns `true` if this is a uuid handle.
#[inline]
pub fn is_weak(&self) -> bool {
matches!(self, Handle::Weak(_))
pub fn is_uuid(&self) -> bool {
matches!(self, Handle::Uuid(..))
}
/// Returns `true` if this is a strong handle.
@ -178,18 +180,9 @@ impl<A: Asset> Handle<A> {
matches!(self, Handle::Strong(_))
}
/// Creates a [`Handle::Weak`] clone of this [`Handle`], which will not keep the referenced [`Asset`] alive.
#[inline]
pub fn clone_weak(&self) -> Self {
match self {
Handle::Strong(handle) => Handle::Weak(handle.id.typed_unchecked::<A>()),
Handle::Weak(id) => Handle::Weak(*id),
}
}
/// Converts this [`Handle`] to an "untyped" / "generic-less" [`UntypedHandle`], which stores the [`Asset`] type information
/// _inside_ [`UntypedHandle`]. This will return [`UntypedHandle::Strong`] for [`Handle::Strong`] and [`UntypedHandle::Weak`] for
/// [`Handle::Weak`].
/// _inside_ [`UntypedHandle`]. This will return [`UntypedHandle::Strong`] for [`Handle::Strong`] and [`UntypedHandle::Uuid`] for
/// [`Handle::Uuid`].
#[inline]
pub fn untyped(self) -> UntypedHandle {
self.into()
@ -198,7 +191,7 @@ impl<A: Asset> Handle<A> {
impl<A: Asset> Default for Handle<A> {
fn default() -> Self {
Handle::Weak(AssetId::default())
Handle::Uuid(AssetId::<A>::DEFAULT_UUID, PhantomData)
}
}
@ -214,7 +207,7 @@ impl<A: Asset> core::fmt::Debug for Handle<A> {
handle.path
)
}
Handle::Weak(id) => write!(f, "WeakHandle<{name}>({:?})", id.internal()),
Handle::Uuid(uuid, ..) => write!(f, "UuidHandle<{name}>({uuid:?})"),
}
}
}
@ -284,8 +277,13 @@ impl<A: Asset> From<&mut Handle<A>> for UntypedAssetId {
pub enum UntypedHandle {
/// A strong handle, which will keep the referenced [`Asset`] alive until all strong handles are dropped.
Strong(Arc<StrongHandle>),
/// A weak handle, which does not keep the referenced [`Asset`] alive.
Weak(UntypedAssetId),
/// A UUID handle, which does not keep the referenced [`Asset`] alive.
Uuid {
/// An identifier that records the underlying asset type.
type_id: TypeId,
/// The UUID provided during asset registration.
uuid: Uuid,
},
}
impl UntypedHandle {
@ -294,7 +292,10 @@ impl UntypedHandle {
pub fn id(&self) -> UntypedAssetId {
match self {
UntypedHandle::Strong(handle) => handle.id,
UntypedHandle::Weak(id) => *id,
UntypedHandle::Uuid { type_id, uuid } => UntypedAssetId::Uuid {
uuid: *uuid,
type_id: *type_id,
},
}
}
@ -303,16 +304,7 @@ impl UntypedHandle {
pub fn path(&self) -> Option<&AssetPath<'static>> {
match self {
UntypedHandle::Strong(handle) => handle.path.as_ref(),
UntypedHandle::Weak(_) => None,
}
}
/// Creates an [`UntypedHandle::Weak`] clone of this [`UntypedHandle`], which will not keep the referenced [`Asset`] alive.
#[inline]
pub fn clone_weak(&self) -> UntypedHandle {
match self {
UntypedHandle::Strong(handle) => UntypedHandle::Weak(handle.id),
UntypedHandle::Weak(id) => UntypedHandle::Weak(*id),
UntypedHandle::Uuid { .. } => None,
}
}
@ -321,7 +313,7 @@ impl UntypedHandle {
pub fn type_id(&self) -> TypeId {
match self {
UntypedHandle::Strong(handle) => handle.id.type_id(),
UntypedHandle::Weak(id) => id.type_id(),
UntypedHandle::Uuid { type_id, .. } => *type_id,
}
}
@ -330,7 +322,7 @@ impl UntypedHandle {
pub fn typed_unchecked<A: Asset>(self) -> Handle<A> {
match self {
UntypedHandle::Strong(handle) => Handle::Strong(handle),
UntypedHandle::Weak(id) => Handle::Weak(id.typed_unchecked::<A>()),
UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
}
}
@ -345,10 +337,7 @@ impl UntypedHandle {
TypeId::of::<A>(),
"The target Handle<A>'s TypeId does not match the TypeId of this UntypedHandle"
);
match self {
UntypedHandle::Strong(handle) => Handle::Strong(handle),
UntypedHandle::Weak(id) => Handle::Weak(id.typed_unchecked::<A>()),
}
self.typed_unchecked()
}
/// Converts to a typed Handle. This will panic if the internal [`TypeId`] does not match the given asset type `A`
@ -376,7 +365,7 @@ impl UntypedHandle {
pub fn meta_transform(&self) -> Option<&MetaTransform> {
match self {
UntypedHandle::Strong(handle) => handle.meta_transform.as_ref(),
UntypedHandle::Weak(_) => None,
UntypedHandle::Uuid { .. } => None,
}
}
}
@ -409,12 +398,9 @@ impl core::fmt::Debug for UntypedHandle {
handle.path
)
}
UntypedHandle::Weak(id) => write!(
f,
"WeakHandle{{ type_id: {:?}, id: {:?} }}",
id.type_id(),
id.internal()
),
UntypedHandle::Uuid { type_id, uuid } => {
write!(f, "UuidHandle{{ type_id: {type_id:?}, uuid: {uuid:?} }}",)
}
}
}
}
@ -474,7 +460,10 @@ impl<A: Asset> From<Handle<A>> for UntypedHandle {
fn from(value: Handle<A>) -> Self {
match value {
Handle::Strong(handle) => UntypedHandle::Strong(handle),
Handle::Weak(id) => UntypedHandle::Weak(id.into()),
Handle::Uuid(uuid, _) => UntypedHandle::Uuid {
type_id: TypeId::of::<A>(),
uuid,
},
}
}
}
@ -490,36 +479,37 @@ impl<A: Asset> TryFrom<UntypedHandle> for Handle<A> {
return Err(UntypedAssetConversionError::TypeIdMismatch { expected, found });
}
match value {
UntypedHandle::Strong(handle) => Ok(Handle::Strong(handle)),
UntypedHandle::Weak(id) => {
let Ok(id) = id.try_into() else {
return Err(UntypedAssetConversionError::TypeIdMismatch { expected, found });
};
Ok(Handle::Weak(id))
}
}
Ok(match value {
UntypedHandle::Strong(handle) => Handle::Strong(handle),
UntypedHandle::Uuid { uuid, .. } => Handle::Uuid(uuid, PhantomData),
})
}
}
/// Creates a weak [`Handle`] from a string literal containing a UUID.
/// Creates a [`Handle`] from a string literal containing a UUID.
///
/// # Examples
///
/// ```
/// # use bevy_asset::{Handle, weak_handle};
/// # use bevy_asset::{Handle, uuid_handle};
/// # type Shader = ();
/// const SHADER: Handle<Shader> = weak_handle!("1347c9b7-c46a-48e7-b7b8-023a354b7cac");
/// const SHADER: Handle<Shader> = uuid_handle!("1347c9b7-c46a-48e7-b7b8-023a354b7cac");
/// ```
#[macro_export]
macro_rules! weak_handle {
macro_rules! uuid_handle {
($uuid:expr) => {{
$crate::Handle::Weak($crate::AssetId::Uuid {
uuid: $crate::uuid::uuid!($uuid),
})
$crate::Handle::Uuid($crate::uuid::uuid!($uuid), core::marker::PhantomData)
}};
}
#[deprecated = "Use uuid_handle! instead"]
#[macro_export]
macro_rules! weak_handle {
($uuid:expr) => {
uuid_handle!($uuid)
};
}
/// Errors preventing the conversion of to/from an [`UntypedHandle`] and a [`Handle`].
#[derive(Error, Debug, PartialEq, Clone)]
#[non_exhaustive]
@ -559,15 +549,12 @@ mod tests {
/// Typed and Untyped `Handles` should be equivalent to each other and themselves
#[test]
fn equality() {
let typed = AssetId::<TestAsset>::Uuid { uuid: UUID_1 };
let untyped = UntypedAssetId::Uuid {
let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
let untyped = UntypedHandle::Uuid {
type_id: TypeId::of::<TestAsset>(),
uuid: UUID_1,
};
let typed = Handle::Weak(typed);
let untyped = UntypedHandle::Weak(untyped);
assert_eq!(
Ok(typed.clone()),
Handle::<TestAsset>::try_from(untyped.clone())
@ -585,22 +572,17 @@ mod tests {
fn ordering() {
assert!(UUID_1 < UUID_2);
let typed_1 = AssetId::<TestAsset>::Uuid { uuid: UUID_1 };
let typed_2 = AssetId::<TestAsset>::Uuid { uuid: UUID_2 };
let untyped_1 = UntypedAssetId::Uuid {
let typed_1 = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
let typed_2 = Handle::<TestAsset>::Uuid(UUID_2, PhantomData);
let untyped_1 = UntypedHandle::Uuid {
type_id: TypeId::of::<TestAsset>(),
uuid: UUID_1,
};
let untyped_2 = UntypedAssetId::Uuid {
let untyped_2 = UntypedHandle::Uuid {
type_id: TypeId::of::<TestAsset>(),
uuid: UUID_2,
};
let typed_1 = Handle::Weak(typed_1);
let typed_2 = Handle::Weak(typed_2);
let untyped_1 = UntypedHandle::Weak(untyped_1);
let untyped_2 = UntypedHandle::Weak(untyped_2);
assert!(typed_1 < typed_2);
assert!(untyped_1 < untyped_2);
@ -617,15 +599,12 @@ mod tests {
/// Typed and Untyped `Handles` should be equivalently hashable to each other and themselves
#[test]
fn hashing() {
let typed = AssetId::<TestAsset>::Uuid { uuid: UUID_1 };
let untyped = UntypedAssetId::Uuid {
let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
let untyped = UntypedHandle::Uuid {
type_id: TypeId::of::<TestAsset>(),
uuid: UUID_1,
};
let typed = Handle::Weak(typed);
let untyped = UntypedHandle::Weak(untyped);
assert_eq!(
hash(&typed),
hash(&Handle::<TestAsset>::try_from(untyped.clone()).unwrap())
@ -637,15 +616,12 @@ mod tests {
/// Typed and Untyped `Handles` should be interchangeable
#[test]
fn conversion() {
let typed = AssetId::<TestAsset>::Uuid { uuid: UUID_1 };
let untyped = UntypedAssetId::Uuid {
let typed = Handle::<TestAsset>::Uuid(UUID_1, PhantomData);
let untyped = UntypedHandle::Uuid {
type_id: TypeId::of::<TestAsset>(),
uuid: UUID_1,
};
let typed = Handle::Weak(typed);
let untyped = UntypedHandle::Weak(untyped);
assert_eq!(typed, Handle::try_from(untyped.clone()).unwrap());
assert_eq!(UntypedHandle::from(typed.clone()), untyped);
}

View File

@ -12,7 +12,7 @@ use crate::core_3d::{
prepare_core_3d_depth_textures,
};
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_asset::{load_internal_asset, uuid_handle, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::Component,
@ -51,7 +51,7 @@ use tracing::debug;
/// Identifies the `downsample_depth.wgsl` shader.
pub const DOWNSAMPLE_DEPTH_SHADER_HANDLE: Handle<Shader> =
weak_handle!("a09a149e-5922-4fa4-9170-3c1a13065364");
uuid_handle!("a09a149e-5922-4fa4-9170-3c1a13065364");
/// The maximum number of mip levels that we can produce.
///

View File

@ -3,7 +3,7 @@
//! Currently, this consists only of chromatic aberration.
use bevy_app::{App, Plugin};
use bevy_asset::{embedded_asset, load_embedded_asset, weak_handle, Assets, Handle};
use bevy_asset::{embedded_asset, load_embedded_asset, uuid_handle, Assets, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::Component,
@ -52,7 +52,7 @@ use crate::{
/// This is just a 3x1 image consisting of one red pixel, one green pixel, and
/// one blue pixel, in that order.
const DEFAULT_CHROMATIC_ABERRATION_LUT_HANDLE: Handle<Image> =
weak_handle!("dc3e3307-40a1-49bb-be6d-e0634e8836b2");
uuid_handle!("dc3e3307-40a1-49bb-be6d-e0634e8836b2");
/// The default chromatic aberration intensity amount, in a fraction of the
/// window size.

View File

@ -149,7 +149,7 @@ pub(crate) fn extract_linegizmos(
line_style: gizmo.line_config.style,
line_joints: gizmo.line_config.joints,
render_layers: render_layers.cloned().unwrap_or_default(),
handle: gizmo.handle.clone_weak(),
handle: gizmo.handle.clone(),
},
MainEntity::from(entity),
TemporaryRenderEntity,

View File

@ -3,7 +3,7 @@ use crate::{
MaterialPlugin, StandardMaterial,
};
use bevy_app::{App, Plugin};
use bevy_asset::{weak_handle, Asset, Assets, Handle};
use bevy_asset::{uuid_handle, Asset, Assets, Handle};
use bevy_ecs::component::Component;
use bevy_math::{prelude::Rectangle, Quat, Vec2, Vec3};
use bevy_reflect::{Reflect, TypePath};
@ -21,7 +21,7 @@ use bevy_render::{
};
const FORWARD_DECAL_MESH_HANDLE: Handle<Mesh> =
weak_handle!("afa817f9-1869-4e0c-ac0d-d8cd1552d38a");
uuid_handle!("afa817f9-1869-4e0c-ac0d-d8cd1552d38a");
/// Plugin to render [`ForwardDecal`]s.
pub struct ForwardDecalPlugin;

View File

@ -2,7 +2,7 @@
use core::array;
use bevy_asset::{load_embedded_asset, weak_handle, AssetId, Handle};
use bevy_asset::{load_embedded_asset, uuid_handle, AssetId, Handle};
use bevy_color::ColorToComponents as _;
use bevy_core_pipeline::{
core_3d::Camera3d,
@ -82,14 +82,14 @@ bitflags! {
///
/// This mesh is simply stretched to the size of the framebuffer, as when the
/// camera is inside a fog volume it's essentially a full-screen effect.
pub const PLANE_MESH: Handle<Mesh> = weak_handle!("92523617-c708-4fd0-b42f-ceb4300c930b");
pub const PLANE_MESH: Handle<Mesh> = uuid_handle!("92523617-c708-4fd0-b42f-ceb4300c930b");
/// The cube mesh, which is used to render a fog volume that the camera is
/// outside.
///
/// Note that only the front faces of this cuboid will be rasterized in
/// hardware. The back faces will be calculated in the shader via raytracing.
pub const CUBE_MESH: Handle<Mesh> = weak_handle!("4a1dd661-2d91-4377-a17a-a914e21e277e");
pub const CUBE_MESH: Handle<Mesh> = uuid_handle!("4a1dd661-2d91-4377-a17a-a914e21e277e");
/// The total number of bind group layouts.
///

View File

@ -21,7 +21,7 @@ use crate::{
render_asset::RenderAssetPlugin, renderer::RenderDevice, Render, RenderApp, RenderSystems,
};
use bevy_app::{App, Plugin};
use bevy_asset::{weak_handle, AssetApp, Assets, Handle};
use bevy_asset::{uuid_handle, AssetApp, Assets, Handle};
use bevy_ecs::prelude::*;
use tracing::warn;
@ -31,7 +31,7 @@ use tracing::warn;
/// While that handle points to an opaque white 1 x 1 image, this handle points to a transparent 1 x 1 white image.
// Number randomly selected by fair WolframAlpha query. Totally arbitrary.
pub const TRANSPARENT_IMAGE_HANDLE: Handle<Image> =
weak_handle!("d18ad97e-a322-4981-9505-44c59a4b5e46");
uuid_handle!("d18ad97e-a322-4981-9505-44c59a4b5e46");
// TODO: replace Texture names with Image names?
/// Adds the [`Image`] as an asset and makes sure that they are extracted and prepared for the GPU.

View File

@ -172,8 +172,8 @@ impl FontAtlasSet {
.get_glyph_index(cache_key)
.map(|location| GlyphAtlasInfo {
location,
texture_atlas: atlas.texture_atlas.clone_weak(),
texture: atlas.texture.clone_weak(),
texture_atlas: atlas.texture_atlas.id(),
texture: atlas.texture.id(),
})
})
})

View File

@ -1,6 +1,6 @@
//! This module exports types related to rendering glyphs.
use bevy_asset::Handle;
use bevy_asset::AssetId;
use bevy_image::prelude::*;
use bevy_math::{IVec2, Vec2};
use bevy_reflect::Reflect;
@ -38,14 +38,15 @@ pub struct PositionedGlyph {
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub struct GlyphAtlasInfo {
/// A handle to the [`Image`] data for the texture atlas this glyph was placed in.
/// An asset ID to the [`Image`] data for the texture atlas this glyph was placed in.
///
/// A (weak) clone of the handle held by the [`FontAtlas`](crate::FontAtlas).
pub texture: Handle<Image>,
/// A handle to the [`TextureAtlasLayout`] map for the texture atlas this glyph was placed in.
/// An asset ID of the handle held by the [`FontAtlas`](crate::FontAtlas).
pub texture: AssetId<Image>,
/// An asset ID to the [`TextureAtlasLayout`] map for the texture atlas this glyph was placed
/// in.
///
/// A (weak) clone of the handle held by the [`FontAtlas`](crate::FontAtlas).
pub texture_atlas: Handle<TextureAtlasLayout>,
/// An asset ID of the handle held by the [`FontAtlas`](crate::FontAtlas).
pub texture_atlas: AssetId<TextureAtlasLayout>,
/// Location and offset of a glyph within the texture atlas.
pub location: GlyphAtlasLocation,
}

View File

@ -340,7 +340,7 @@ impl TextPipeline {
)
})?;
let texture_atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap();
let texture_atlas = texture_atlases.get(atlas_info.texture_atlas).unwrap();
let location = atlas_info.location;
let glyph_rect = texture_atlas.textures[location.glyph_index];
let left = location.offset.x as f32;

View File

@ -213,7 +213,7 @@ pub fn extract_text2d_sprite(
current_span = *span_index;
}
let rect = texture_atlases
.get(&atlas_info.texture_atlas)
.get(atlas_info.texture_atlas)
.unwrap()
.textures[atlas_info.location.glyph_index]
.as_rect();
@ -232,7 +232,7 @@ pub fn extract_text2d_sprite(
render_entity,
transform,
color,
image_handle_id: atlas_info.texture.id(),
image_handle_id: atlas_info.texture,
flip_x: false,
flip_y: false,
kind: bevy_sprite::ExtractedSpriteKind::Slices {

View File

@ -865,7 +865,7 @@ pub fn extract_text_sections(
) in text_layout_info.glyphs.iter().enumerate()
{
let rect = texture_atlases
.get(&atlas_info.texture_atlas)
.get(atlas_info.texture_atlas)
.unwrap()
.textures[atlas_info.location.glyph_index]
.as_rect();
@ -891,7 +891,7 @@ pub fn extract_text_sections(
z_order: uinode.stack_index as f32 + stack_z_offsets::TEXT,
render_entity: commands.spawn(TemporaryRenderEntity).id(),
color,
image: atlas_info.texture.id(),
image: atlas_info.texture,
clip: clip.map(|clip| clip.clip),
extracted_camera_entity,
rect,
@ -956,7 +956,7 @@ pub fn extract_text_shadows(
) in text_layout_info.glyphs.iter().enumerate()
{
let rect = texture_atlases
.get(&atlas_info.texture_atlas)
.get(atlas_info.texture_atlas)
.unwrap()
.textures[atlas_info.location.glyph_index]
.as_rect();
@ -972,7 +972,7 @@ pub fn extract_text_shadows(
z_order: uinode.stack_index as f32 + stack_z_offsets::TEXT,
render_entity: commands.spawn(TemporaryRenderEntity).id(),
color: shadow.color.into(),
image: atlas_info.texture.id(),
image: atlas_info.texture,
clip: clip.map(|clip| clip.clip),
extracted_camera_entity,
rect,

View File

@ -6,7 +6,7 @@
//! [`Material2d`]: bevy::sprite::Material2d
use bevy::{
asset::weak_handle,
asset::uuid_handle,
color::palettes::basic::YELLOW,
core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT},
math::{ops, FloatOrd},
@ -287,7 +287,7 @@ pub struct ColoredMesh2dPlugin;
/// Handle to the custom shader with a unique random ID
pub const COLORED_MESH2D_SHADER_HANDLE: Handle<Shader> =
weak_handle!("f48b148f-7373-4638-9900-392b3b3ccc66");
uuid_handle!("f48b148f-7373-4638-9900-392b3b3ccc66");
/// Our custom pipeline needs its own instance storage
#[derive(Resource, Deref, DerefMut, Default)]

View File

@ -86,9 +86,9 @@ fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
// Switch texture to display every frame to show the one that was written to most recently.
fn switch_textures(images: Res<GameOfLifeImages>, mut sprite: Single<&mut Sprite>) {
if sprite.image == images.texture_a {
sprite.image = images.texture_b.clone_weak();
sprite.image = images.texture_b.clone();
} else {
sprite.image = images.texture_a.clone_weak();
sprite.image = images.texture_a.clone();
}
}

View File

@ -125,8 +125,7 @@ fn scene_load_check(
maybe_directional_light.is_some() || maybe_point_light.is_some()
});
scene_handle.instance_id =
Some(scene_spawner.spawn(gltf_scene_handle.clone_weak()));
scene_handle.instance_id = Some(scene_spawner.spawn(gltf_scene_handle.clone()));
info!("Spawning scene...");
}

View File

@ -0,0 +1,40 @@
---
title: `Handle::Weak` has been replaced by `Handle::Uuid`.
pull_requests: [19896]
---
`Handle::Weak` had some weird behavior. It allowed for a sprite to be given a handle that is dropped
**while the sprite is still using it**. This also resulted in more complexity in the asset system.
The primary remaining use for `Handle::Weak` is to store asset UUIDs initialized through the
`weak_handle!` macro. To address this, `Handle::Weak` has been replaced by `Handle::Uuid`!
Users using the `weak_handle!` macro should switch to the `uuid_handle!` macro.
```rust
// Before
const IMAGE: Handle<Image> = weak_handle!("b20988e9-b1b9-4176-b5f3-a6fa73aa617f");
// After
const IMAGE: Handle<Image> = uuid_handle!("b20988e9-b1b9-4176-b5f3-a6fa73aa617f");
```
Users using `Handle::clone_weak` can (most likely) just call `Handle::clone` instead.
```rust
// Somewhere in some startup system.
let my_sprite_image = asset_server.load("monster.png");
// In game code...
// This sprite could be unloaded even if the sprite is still using it!
commands.spawn(Sprite::from_image(my_sprite_image.clone_weak()));
// Just do this instead!
commands.spawn(Sprite::from_image(my_sprite_image.clone()));
```
Users using the `Handle::Weak` variant directly should consider replacing it with `AssetId` instead,
accessible through `Handle::id`. These situations are very case-by-case migrations.
P.S., for users of the `weak_handle!` macro: If you are using it for shaders, consider switching to
`load_shader_library`/`load_embedded_asset` instead (especially replacing `load_internal_asset`).
This enables hot reloading for your shaders - which Bevy internally has done this cycle!