mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Change default Image
FilterMode
to Linear
(#4465)
# Objective - Closes #4464 ## Solution - Specify default mag and min filter types for `Image` instead of using `wgpu`'s defaults. --- ## Changelog ### Changed - Default `Image` filtering changed from `Nearest` to `Linear`. Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
728d9696d7
commit
772d15238c
8 changed files with 127 additions and 19 deletions
|
@ -25,7 +25,7 @@ use bevy_render::{
|
|||
primitives::{Aabb, Frustum},
|
||||
render_resource::{AddressMode, Face, FilterMode, PrimitiveTopology, SamplerDescriptor},
|
||||
renderer::RenderDevice,
|
||||
texture::{CompressedImageFormats, Image, ImageType, TextureError},
|
||||
texture::{CompressedImageFormats, Image, ImageSampler, ImageType, TextureError},
|
||||
view::VisibleEntities,
|
||||
};
|
||||
use bevy_scene::Scene;
|
||||
|
@ -619,7 +619,7 @@ async fn load_texture<'a>(
|
|||
)?
|
||||
}
|
||||
};
|
||||
texture.sampler_descriptor = texture_sampler(&gltf_texture);
|
||||
texture.sampler_descriptor = ImageSampler::Descriptor(texture_sampler(&gltf_texture));
|
||||
|
||||
Ok((texture, texture_label(&gltf_texture)))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use bevy_app::Plugin;
|
|||
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
system::{lifetimeless::*, SystemParamItem},
|
||||
system::{lifetimeless::*, SystemParamItem, SystemState},
|
||||
};
|
||||
use bevy_math::{Mat4, Vec2};
|
||||
use bevy_reflect::TypeUuid;
|
||||
|
@ -21,7 +21,9 @@ use bevy_render::{
|
|||
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||
texture::{
|
||||
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
|
||||
},
|
||||
view::{ComputedVisibility, ViewUniform, ViewUniformOffset, ViewUniforms},
|
||||
RenderApp, RenderStage,
|
||||
};
|
||||
|
@ -275,7 +277,12 @@ pub struct MeshPipeline {
|
|||
|
||||
impl FromWorld for MeshPipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let render_device = world.resource::<RenderDevice>();
|
||||
let mut system_state: SystemState<(
|
||||
Res<RenderDevice>,
|
||||
Res<DefaultImageSampler>,
|
||||
Res<RenderQueue>,
|
||||
)> = SystemState::new(world);
|
||||
let (render_device, default_sampler, render_queue) = system_state.get_mut(world);
|
||||
let clustered_forward_buffer_binding_type = render_device
|
||||
.get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT);
|
||||
|
||||
|
@ -435,10 +442,12 @@ impl FromWorld for MeshPipeline {
|
|||
TextureFormat::bevy_default(),
|
||||
);
|
||||
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||
let sampler = render_device.create_sampler(&image.sampler_descriptor);
|
||||
let sampler = match image.sampler_descriptor {
|
||||
ImageSampler::Default => (**default_sampler).clone(),
|
||||
ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor),
|
||||
};
|
||||
|
||||
let format_size = image.texture_descriptor.format.pixel_size();
|
||||
let render_queue = world.resource_mut::<RenderQueue>();
|
||||
render_queue.write_texture(
|
||||
ImageCopyTexture {
|
||||
texture: &texture,
|
||||
|
|
|
@ -180,7 +180,8 @@ impl Plugin for RenderPlugin {
|
|||
.insert_resource(queue)
|
||||
.insert_resource(adapter_info)
|
||||
.insert_resource(pipeline_cache)
|
||||
.insert_resource(asset_server);
|
||||
.insert_resource(asset_server)
|
||||
.init_resource::<RenderGraph>();
|
||||
|
||||
app.add_sub_app(RenderApp, render_app, move |app_world, render_app| {
|
||||
#[cfg(feature = "trace")]
|
||||
|
|
|
@ -13,9 +13,11 @@ use crate::{
|
|||
texture::BevyDefault,
|
||||
};
|
||||
use bevy_asset::HandleUntyped;
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_reflect::TypeUuid;
|
||||
use std::hash::Hash;
|
||||
use thiserror::Error;
|
||||
use wgpu::{
|
||||
Extent3d, ImageCopyTexture, ImageDataLayout, Origin3d, TextureDimension, TextureFormat,
|
||||
|
@ -106,9 +108,49 @@ pub struct Image {
|
|||
pub data: Vec<u8>,
|
||||
// TODO: this nesting makes accessing Image metadata verbose. Either flatten out descriptor or add accessors
|
||||
pub texture_descriptor: wgpu::TextureDescriptor<'static>,
|
||||
pub sampler_descriptor: wgpu::SamplerDescriptor<'static>,
|
||||
pub sampler_descriptor: ImageSampler,
|
||||
}
|
||||
|
||||
/// Used in `Image`, this determines what image sampler to use when rendering. The default setting,
|
||||
/// [`ImageSampler::Default`], will result in reading the sampler set in the [`DefaultImageSampler`]
|
||||
/// resource - the global default sampler - at runtime. Setting this to [`ImageSampler::Descriptor`]
|
||||
/// will override the global default descriptor for this [`Image`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ImageSampler {
|
||||
Default,
|
||||
Descriptor(wgpu::SamplerDescriptor<'static>),
|
||||
}
|
||||
impl Default for ImageSampler {
|
||||
fn default() -> Self {
|
||||
Self::Default
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageSampler {
|
||||
/// Returns a sampler descriptor with `Linear` min and mag filters
|
||||
pub fn linear_descriptor() -> wgpu::SamplerDescriptor<'static> {
|
||||
wgpu::SamplerDescriptor {
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a sampler descriptor with `Nearest` min and mag filters
|
||||
pub fn nearest_descriptor() -> wgpu::SamplerDescriptor<'static> {
|
||||
wgpu::SamplerDescriptor {
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resource used as the global default image sampler for [`Image`]s with their `sampler_descriptor`
|
||||
/// set to [`ImageSampler::Default`].
|
||||
#[derive(Debug, Clone, Deref, DerefMut)]
|
||||
pub struct DefaultImageSampler(pub(crate) Sampler);
|
||||
|
||||
impl Default for Image {
|
||||
fn default() -> Self {
|
||||
let format = wgpu::TextureFormat::bevy_default();
|
||||
|
@ -128,7 +170,7 @@ impl Default for Image {
|
|||
sample_count: 1,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
},
|
||||
sampler_descriptor: wgpu::SamplerDescriptor::default(),
|
||||
sampler_descriptor: ImageSampler::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -540,7 +582,11 @@ pub struct GpuImage {
|
|||
impl RenderAsset for Image {
|
||||
type ExtractedAsset = Image;
|
||||
type PreparedAsset = GpuImage;
|
||||
type Param = (SRes<RenderDevice>, SRes<RenderQueue>);
|
||||
type Param = (
|
||||
SRes<RenderDevice>,
|
||||
SRes<RenderQueue>,
|
||||
SRes<DefaultImageSampler>,
|
||||
);
|
||||
|
||||
/// Clones the Image.
|
||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||
|
@ -550,7 +596,7 @@ impl RenderAsset for Image {
|
|||
/// Converts the extracted image into a [`GpuImage`].
|
||||
fn prepare_asset(
|
||||
image: Self::ExtractedAsset,
|
||||
(render_device, render_queue): &mut SystemParamItem<Self::Param>,
|
||||
(render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
|
||||
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
|
||||
let texture = if image.texture_descriptor.mip_level_count > 1 || image.is_compressed() {
|
||||
render_device.create_texture_with_data(
|
||||
|
@ -593,7 +639,11 @@ impl RenderAsset for Image {
|
|||
image.texture_descriptor.size.width as f32,
|
||||
image.texture_descriptor.size.height as f32,
|
||||
);
|
||||
let sampler = render_device.create_sampler(&image.sampler_descriptor);
|
||||
let sampler = match image.sampler_descriptor {
|
||||
ImageSampler::Default => (***default_sampler).clone(),
|
||||
ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor),
|
||||
};
|
||||
|
||||
Ok(GpuImage {
|
||||
texture,
|
||||
texture_view,
|
||||
|
|
|
@ -26,6 +26,7 @@ pub use texture_cache::*;
|
|||
|
||||
use crate::{
|
||||
render_asset::{PrepareAssetLabel, RenderAssetPlugin},
|
||||
renderer::RenderDevice,
|
||||
RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_app::{App, Plugin};
|
||||
|
@ -63,14 +64,52 @@ impl Plugin for ImagePlugin {
|
|||
.resource_mut::<Assets<Image>>()
|
||||
.set_untracked(DEFAULT_IMAGE_HANDLE, Image::default());
|
||||
|
||||
let default_sampler = app
|
||||
.world
|
||||
.get_resource_or_insert_with(ImageSettings::default)
|
||||
.default_sampler
|
||||
.clone();
|
||||
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
let default_sampler = {
|
||||
let device = render_app.world.resource::<RenderDevice>();
|
||||
device.create_sampler(&default_sampler)
|
||||
};
|
||||
render_app
|
||||
.insert_resource(DefaultImageSampler(default_sampler))
|
||||
.init_resource::<TextureCache>()
|
||||
.add_system_to_stage(RenderStage::Cleanup, update_texture_cache_system);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ImagePlugin`] settings.
|
||||
pub struct ImageSettings {
|
||||
/// The default image sampler to use when [`ImageSampler`] is set to `Default`.
|
||||
pub default_sampler: wgpu::SamplerDescriptor<'static>,
|
||||
}
|
||||
|
||||
impl Default for ImageSettings {
|
||||
fn default() -> Self {
|
||||
ImageSettings::default_linear()
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageSettings {
|
||||
/// Creates image settings with default linear sampling.
|
||||
pub fn default_linear() -> ImageSettings {
|
||||
ImageSettings {
|
||||
default_sampler: ImageSampler::linear_descriptor(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates image settings with default nearest sampling.
|
||||
pub fn default_nearest() -> ImageSettings {
|
||||
ImageSettings {
|
||||
default_sampler: ImageSampler::nearest_descriptor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BevyDefault {
|
||||
fn bevy_default() -> Self;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use bevy_app::Plugin;
|
|||
use bevy_asset::{load_internal_asset, Handle, HandleUntyped};
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
system::{lifetimeless::*, SystemParamItem},
|
||||
system::{lifetimeless::*, SystemParamItem, SystemState},
|
||||
};
|
||||
use bevy_math::{Mat4, Vec2};
|
||||
use bevy_reflect::{Reflect, TypeUuid};
|
||||
|
@ -13,7 +13,9 @@ use bevy_render::{
|
|||
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||
texture::{
|
||||
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
|
||||
},
|
||||
view::{ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms},
|
||||
RenderApp, RenderStage,
|
||||
};
|
||||
|
@ -140,7 +142,9 @@ pub struct Mesh2dPipeline {
|
|||
|
||||
impl FromWorld for Mesh2dPipeline {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
let render_device = world.resource::<RenderDevice>();
|
||||
let mut system_state: SystemState<(Res<RenderDevice>, Res<DefaultImageSampler>)> =
|
||||
SystemState::new(world);
|
||||
let (render_device, default_sampler) = system_state.get_mut(world);
|
||||
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
// View
|
||||
|
@ -180,7 +184,10 @@ impl FromWorld for Mesh2dPipeline {
|
|||
TextureFormat::bevy_default(),
|
||||
);
|
||||
let texture = render_device.create_texture(&image.texture_descriptor);
|
||||
let sampler = render_device.create_sampler(&image.sampler_descriptor);
|
||||
let sampler = match image.sampler_descriptor {
|
||||
ImageSampler::Default => (**default_sampler).clone(),
|
||||
ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor),
|
||||
};
|
||||
|
||||
let format_size = image.texture_descriptor.format.pixel_size();
|
||||
let render_queue = world.resource_mut::<RenderQueue>();
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! Renders an animated sprite by loading all animation frames from a single image (a sprite sheet)
|
||||
//! into a texture atlas, and changing the displayed image periodically.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy::{prelude::*, render::texture::ImageSettings};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(ImageSettings::default_nearest()) // prevents blurry sprites
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup)
|
||||
.add_system(animate_sprite)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
//! In this example we generate a new texture atlas (sprite sheet) from a folder containing
|
||||
//! individual sprites.
|
||||
|
||||
use bevy::{asset::LoadState, prelude::*};
|
||||
use bevy::{asset::LoadState, prelude::*, render::texture::ImageSettings};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.init_resource::<RpgSpriteHandles>()
|
||||
.insert_resource(ImageSettings::default_nearest()) // prevents blurry sprites
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_state(AppState::Setup)
|
||||
.add_system_set(SystemSet::on_enter(AppState::Setup).with_system(load_textures))
|
||||
|
|
Loading…
Reference in a new issue