mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 21:53:07 +00:00
check sampler type in as_bind_group derives (#12637)
# Objective currently if we use an image with the wrong sampler type in a material, wgpu panics with an invalid texture format. turn this into a warning and fail more gracefully. ## Solution the expected sampler type is specified in the AsBindGroup derive, so we can just check the image sampler is what it should be. i am not totally sure about the mapping of image sampler type to #[sampler(type)], i assumed: ``` "filtering" => [ TextureSampleType::Float { filterable: true } ], "non_filtering" => [ TextureSampleType::Float { filterable: false }, TextureSampleType::Sint, TextureSampleType::Uint, ], "comparison" => [ TextureSampleType::Depth ], ```
This commit is contained in:
parent
f59a6a971a
commit
6e2f96f222
6 changed files with 60 additions and 3 deletions
|
@ -953,6 +953,7 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
|
||||||
Err(AsBindGroupError::RetryNextUpdate) => {
|
Err(AsBindGroupError::RetryNextUpdate) => {
|
||||||
Err(PrepareAssetError::RetryNextUpdate(material))
|
Err(PrepareAssetError::RetryNextUpdate(material))
|
||||||
}
|
}
|
||||||
|
Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -355,6 +355,20 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
|
||||||
|
|
||||||
let fallback_image = get_fallback_image(&render_path, *dimension);
|
let fallback_image = get_fallback_image(&render_path, *dimension);
|
||||||
|
|
||||||
|
let expected_samplers = match sampler_binding_type {
|
||||||
|
SamplerBindingType::Filtering => {
|
||||||
|
quote!( [#render_path::render_resource::TextureSampleType::Float { filterable: true }] )
|
||||||
|
}
|
||||||
|
SamplerBindingType::NonFiltering => quote!([
|
||||||
|
#render_path::render_resource::TextureSampleType::Float { filterable: false },
|
||||||
|
#render_path::render_resource::TextureSampleType::Sint,
|
||||||
|
#render_path::render_resource::TextureSampleType::Uint,
|
||||||
|
]),
|
||||||
|
SamplerBindingType::Comparison => {
|
||||||
|
quote!( [#render_path::render_resource::TextureSampleType::Depth] )
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// insert fallible texture-based entries at 0 so that if we fail here, we exit before allocating any buffers
|
// insert fallible texture-based entries at 0 so that if we fail here, we exit before allocating any buffers
|
||||||
binding_impls.insert(0, quote! {
|
binding_impls.insert(0, quote! {
|
||||||
(
|
(
|
||||||
|
@ -362,7 +376,26 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
|
||||||
#render_path::render_resource::OwnedBindingResource::Sampler({
|
#render_path::render_resource::OwnedBindingResource::Sampler({
|
||||||
let handle: Option<&#asset_path::Handle<#render_path::texture::Image>> = (&self.#field_name).into();
|
let handle: Option<&#asset_path::Handle<#render_path::texture::Image>> = (&self.#field_name).into();
|
||||||
if let Some(handle) = handle {
|
if let Some(handle) = handle {
|
||||||
images.get(handle).ok_or_else(|| #render_path::render_resource::AsBindGroupError::RetryNextUpdate)?.sampler.clone()
|
let image = images.get(handle).ok_or_else(|| #render_path::render_resource::AsBindGroupError::RetryNextUpdate)?;
|
||||||
|
|
||||||
|
let Some(sample_type) = image.texture_format.sample_type(None, None) else {
|
||||||
|
return Err(#render_path::render_resource::AsBindGroupError::InvalidSamplerType(
|
||||||
|
#binding_index,
|
||||||
|
"None".to_string(),
|
||||||
|
format!("{:?}", #expected_samplers),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let valid = #expected_samplers.contains(&sample_type);
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
return Err(#render_path::render_resource::AsBindGroupError::InvalidSamplerType(
|
||||||
|
#binding_index,
|
||||||
|
format!("{:?}", sample_type),
|
||||||
|
format!("{:?}", #expected_samplers),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
image.sampler.clone()
|
||||||
} else {
|
} else {
|
||||||
#fallback_image.sampler.clone()
|
#fallback_image.sampler.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::{ExtractSchedule, MainWorld, Render, RenderApp, RenderSet};
|
use crate::{
|
||||||
|
render_resource::AsBindGroupError, ExtractSchedule, MainWorld, Render, RenderApp, RenderSet,
|
||||||
|
};
|
||||||
use bevy_app::{App, Plugin, SubApp};
|
use bevy_app::{App, Plugin, SubApp};
|
||||||
use bevy_asset::{Asset, AssetEvent, AssetId, Assets};
|
use bevy_asset::{Asset, AssetEvent, AssetId, Assets};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
@ -9,7 +11,10 @@ use bevy_ecs::{
|
||||||
};
|
};
|
||||||
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
||||||
use bevy_render_macros::ExtractResource;
|
use bevy_render_macros::ExtractResource;
|
||||||
use bevy_utils::{tracing::debug, HashMap, HashSet};
|
use bevy_utils::{
|
||||||
|
tracing::{debug, error},
|
||||||
|
HashMap, HashSet,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -18,6 +23,8 @@ use thiserror::Error;
|
||||||
pub enum PrepareAssetError<E: Send + Sync + 'static> {
|
pub enum PrepareAssetError<E: Send + Sync + 'static> {
|
||||||
#[error("Failed to prepare asset")]
|
#[error("Failed to prepare asset")]
|
||||||
RetryNextUpdate(E),
|
RetryNextUpdate(E),
|
||||||
|
#[error("Failed to build bind group: {0}")]
|
||||||
|
AsBindGroupError(AsBindGroupError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes how an asset gets extracted and prepared for rendering.
|
/// Describes how an asset gets extracted and prepared for rendering.
|
||||||
|
@ -359,6 +366,12 @@ pub fn prepare_assets<A: RenderAsset>(
|
||||||
Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => {
|
Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => {
|
||||||
prepare_next_frame.assets.push((id, extracted_asset));
|
prepare_next_frame.assets.push((id, extracted_asset));
|
||||||
}
|
}
|
||||||
|
Err(PrepareAssetError::AsBindGroupError(e)) => {
|
||||||
|
error!(
|
||||||
|
"{} Bind group construction failed: {e}",
|
||||||
|
std::any::type_name::<A>()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,6 +404,12 @@ pub fn prepare_assets<A: RenderAsset>(
|
||||||
Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => {
|
Err(PrepareAssetError::RetryNextUpdate(extracted_asset)) => {
|
||||||
prepare_next_frame.assets.push((id, extracted_asset));
|
prepare_next_frame.assets.push((id, extracted_asset));
|
||||||
}
|
}
|
||||||
|
Err(PrepareAssetError::AsBindGroupError(e)) => {
|
||||||
|
error!(
|
||||||
|
"{} Bind group construction failed: {e}",
|
||||||
|
std::any::type_name::<A>()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -352,6 +352,8 @@ pub enum AsBindGroupError {
|
||||||
/// The bind group could not be generated. Try again next frame.
|
/// The bind group could not be generated. Try again next frame.
|
||||||
#[error("The bind group could not be generated")]
|
#[error("The bind group could not be generated")]
|
||||||
RetryNextUpdate,
|
RetryNextUpdate,
|
||||||
|
#[error("At binding index{0}, the provided image sampler `{1}` does not match the required sampler type(s) `{2}`.")]
|
||||||
|
InvalidSamplerType(u32, String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A prepared bind group returned as a result of [`AsBindGroup::as_bind_group`].
|
/// A prepared bind group returned as a result of [`AsBindGroup::as_bind_group`].
|
||||||
|
|
|
@ -615,6 +615,7 @@ impl<M: Material2d> RenderAsset for PreparedMaterial2d<M> {
|
||||||
Err(AsBindGroupError::RetryNextUpdate) => {
|
Err(AsBindGroupError::RetryNextUpdate) => {
|
||||||
Err(PrepareAssetError::RetryNextUpdate(material))
|
Err(PrepareAssetError::RetryNextUpdate(material))
|
||||||
}
|
}
|
||||||
|
Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -624,6 +624,7 @@ impl<M: UiMaterial> RenderAsset for PreparedUiMaterial<M> {
|
||||||
Err(AsBindGroupError::RetryNextUpdate) => {
|
Err(AsBindGroupError::RetryNextUpdate) => {
|
||||||
Err(PrepareAssetError::RetryNextUpdate(material))
|
Err(PrepareAssetError::RetryNextUpdate(material))
|
||||||
}
|
}
|
||||||
|
Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue