Optional explicit compressed image format support (#19190)

# Objective

- Allow compressed image formats to be used with `ImagePlugin` and
`GltfPlugin` in cases where there is no `RenderDevice` resource. (For
example, when using a custom render backend)

## Solution

- Define a `CompressedImageFormatSupport` component that allows the user
to explicitly determine which formats are supported.

~~Not sure if this is the best solution. Alternatively, I considered
initializing CompressedImageFormatSupport from render device features
separately, it would need to run after the render device is initialized
but before `ImagePlugin` and `GltfPlugin` finish. Not sure where the
best place for that to happen would be.~~

Update: decided on going with @greeble-dev solution: defining the
`CompressedImageFormatSupport` resource in `bevy_image`, but letting
`bevy_render` register the resource value.
This commit is contained in:
Griffin 2025-05-26 11:00:33 -07:00 committed by GitHub
parent 5117e6fd49
commit d79efada3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 40 additions and 12 deletions

View File

@ -99,15 +99,15 @@ extern crate alloc;
use alloc::sync::Arc;
use std::sync::Mutex;
use tracing::warn;
use bevy_platform::collections::HashMap;
use bevy_app::prelude::*;
use bevy_asset::AssetApp;
use bevy_ecs::prelude::Resource;
use bevy_image::{CompressedImageFormats, ImageSamplerDescriptor};
use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats, ImageSamplerDescriptor};
use bevy_mesh::MeshVertexAttribute;
use bevy_render::renderer::RenderDevice;
/// The glTF prelude.
///
@ -205,10 +205,16 @@ impl Plugin for GltfPlugin {
}
fn finish(&self, app: &mut App) {
let supported_compressed_formats = match app.world().get_resource::<RenderDevice>() {
Some(render_device) => CompressedImageFormats::from_features(render_device.features()),
None => CompressedImageFormats::NONE,
let supported_compressed_formats = if let Some(resource) =
app.world().get_resource::<CompressedImageFormatSupport>()
{
resource.0
} else {
warn!("CompressedImageFormatSupport resource not found. It should either be initialized in finish() of \
RenderPlugin, or manually if not using the RenderPlugin or the WGPU backend.");
CompressedImageFormats::NONE
};
let default_sampler_resource = DefaultGltfImageSampler::new(&self.default_sampler);
let default_sampler = default_sampler_resource.get_internal();
app.insert_resource(default_sampler_resource);

View File

@ -46,6 +46,7 @@ bevy_color = { path = "../bevy_color", version = "0.16.0-dev", features = [
"serialize",
"wgpu-types",
] }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
bevy_math = { path = "../bevy_math", version = "0.16.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }

View File

@ -11,6 +11,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_asset::{Asset, RenderAssetUsages};
use bevy_color::{Color, ColorToComponents, Gray, LinearRgba, Srgba, Xyza};
use bevy_ecs::resource::Resource;
use bevy_math::{AspectRatio, UVec2, UVec3, Vec2};
use core::hash::Hash;
use serde::{Deserialize, Serialize};
@ -1648,6 +1649,12 @@ impl CompressedImageFormats {
}
}
/// For defining which compressed image formats are supported. This will be initialized from available device features
/// in `finish()` of the bevy `RenderPlugin`, but is left for the user to specify if not using the `RenderPlugin`, or
/// the WGPU backend.
#[derive(Resource)]
pub struct CompressedImageFormatSupport(pub CompressedImageFormats);
#[cfg(test)]
mod test {
use super::*;

View File

@ -79,6 +79,7 @@ pub mod _macro {
}
use bevy_ecs::schedule::ScheduleBuildSettings;
use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats};
use bevy_utils::prelude::default;
pub use extract_param::Extract;
@ -473,10 +474,15 @@ impl Plugin for RenderPlugin {
let RenderResources(device, queue, adapter_info, render_adapter, instance) =
future_render_resources.0.lock().unwrap().take().unwrap();
let compressed_image_format_support = CompressedImageFormatSupport(
CompressedImageFormats::from_features(device.features()),
);
app.insert_resource(device.clone())
.insert_resource(queue.clone())
.insert_resource(adapter_info.clone())
.insert_resource(render_adapter.clone());
.insert_resource(render_adapter.clone())
.insert_resource(compressed_image_format_support);
let render_app = app.sub_app_mut(RenderApp);

View File

@ -8,7 +8,10 @@ pub use crate::render_resource::DefaultImageSampler;
use bevy_image::CompressedImageSaver;
#[cfg(feature = "hdr")]
use bevy_image::HdrTextureLoader;
use bevy_image::{CompressedImageFormats, Image, ImageLoader, ImageSamplerDescriptor};
use bevy_image::{
CompressedImageFormatSupport, CompressedImageFormats, Image, ImageLoader,
ImageSamplerDescriptor,
};
pub use fallback_image::*;
pub use gpu_image::*;
pub use texture_attachment::*;
@ -20,6 +23,7 @@ use crate::{
use bevy_app::{App, Plugin};
use bevy_asset::{weak_handle, AssetApp, Assets, Handle};
use bevy_ecs::prelude::*;
use tracing::warn;
/// A handle to a 1 x 1 transparent white image.
///
@ -111,12 +115,16 @@ impl Plugin for ImagePlugin {
fn finish(&self, app: &mut App) {
if !ImageLoader::SUPPORTED_FORMATS.is_empty() {
let supported_compressed_formats = match app.world().get_resource::<RenderDevice>() {
Some(render_device) => {
CompressedImageFormats::from_features(render_device.features())
}
None => CompressedImageFormats::NONE,
let supported_compressed_formats = if let Some(resource) =
app.world().get_resource::<CompressedImageFormatSupport>()
{
resource.0
} else {
warn!("CompressedImageFormatSupport resource not found. It should either be initialized in finish() of \
RenderPlugin, or manually if not using the RenderPlugin or the WGPU backend.");
CompressedImageFormats::NONE
};
app.register_asset_loader(ImageLoader::new(supported_compressed_formats));
}