Consolidate Render(Ui)Materials(2d) into RenderAssets (#12827)

# Objective

- Replace `RenderMaterials` / `RenderMaterials2d` / `RenderUiMaterials`
with `RenderAssets` to enable implementing changes to one thing,
`RenderAssets`, that applies to all use cases rather than duplicating
changes everywhere for multiple things that should be one thing.
- Adopts #8149 

## Solution

- Make RenderAsset generic over the destination type rather than the
source type as in #8149
- Use `RenderAssets<PreparedMaterial<M>>` etc for render materials

---

## Changelog

- Changed:
- The `RenderAsset` trait is now implemented on the destination type.
Its `SourceAsset` associated type refers to the type of the source
asset.
- `RenderMaterials`, `RenderMaterials2d`, and `RenderUiMaterials` have
been replaced by `RenderAssets<PreparedMaterial<M>>` and similar.

## Migration Guide

- `RenderAsset` is now implemented for the destination type rather that
the source asset type. The source asset type is now the `RenderAsset`
trait's `SourceAsset` associated type.
This commit is contained in:
Robert Swain 2024-04-09 15:26:34 +02:00 committed by GitHub
parent 10af274d4e
commit ab7cbfa8fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 354 additions and 715 deletions

View file

@ -18,7 +18,7 @@ use bevy_render::{
*,
},
renderer::RenderDevice,
texture::{BevyDefault, Image},
texture::{BevyDefault, GpuImage, Image},
view::{ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniforms},
Render, RenderApp, RenderSet,
};
@ -236,7 +236,7 @@ fn prepare_skybox_bind_groups(
pipeline: Res<SkyboxPipeline>,
view_uniforms: Res<ViewUniforms>,
skybox_uniforms: Res<ComponentUniforms<SkyboxUniforms>>,
images: Res<RenderAssets<Image>>,
images: Res<RenderAssets<GpuImage>>,
render_device: Res<RenderDevice>,
views: Query<(Entity, &Skybox, &DynamicUniformIndex<SkyboxUniforms>)>,
) {

View file

@ -10,7 +10,7 @@ use bevy_render::render_resource::binding_types::{
sampler, texture_2d, texture_3d, uniform_buffer,
};
use bevy_render::renderer::RenderDevice;
use bevy_render::texture::{CompressedImageFormats, Image, ImageSampler, ImageType};
use bevy_render::texture::{CompressedImageFormats, GpuImage, Image, ImageSampler, ImageType};
use bevy_render::view::{ViewTarget, ViewUniform};
use bevy_render::{camera::Camera, texture::FallbackImage};
use bevy_render::{render_resource::*, Render, RenderApp, RenderSet};
@ -319,7 +319,7 @@ pub enum DebandDither {
}
pub fn get_lut_bindings<'a>(
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
tonemapping_luts: &'a TonemappingLuts,
tonemapping: &Tonemapping,
fallback_image: &'a FallbackImage,

View file

@ -11,7 +11,7 @@ use bevy_render::{
RenderPassColorAttachment, RenderPassDescriptor, StoreOp, TextureViewId,
},
renderer::RenderContext,
texture::{FallbackImage, Image},
texture::{FallbackImage, GpuImage},
view::{ViewTarget, ViewUniformOffset, ViewUniforms},
};
@ -42,7 +42,7 @@ impl ViewNode for TonemappingNode {
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();
let tonemapping_pipeline = world.resource::<TonemappingPipeline>();
let gpu_images = world.get_resource::<RenderAssets<Image>>().unwrap();
let gpu_images = world.get_resource::<RenderAssets<GpuImage>>().unwrap();
let fallback_image = world.resource::<FallbackImage>();
let view_uniforms_resource = world.resource::<ViewUniforms>();
let view_uniforms = &view_uniforms_resource.uniforms;

View file

@ -79,9 +79,7 @@ use bevy_math::Vec3;
use bevy_reflect::TypePath;
use bevy_render::{
extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
render_asset::{
PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssetUsages, RenderAssets,
},
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{
binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayout,
@ -129,7 +127,7 @@ impl Plugin for GizmoPlugin {
.register_type::<GizmoConfigStore>()
.add_plugins(UniformComponentPlugin::<LineGizmoUniform>::default())
.init_asset::<LineGizmo>()
.add_plugins(RenderAssetPlugin::<LineGizmo>::default())
.add_plugins(RenderAssetPlugin::<GpuLineGizmo>::default())
.init_resource::<LineGizmoHandles>()
// We insert the Resource GizmoConfigStore into the world implicitly here if it does not exist.
.init_gizmo_group::<DefaultGizmoConfigGroup>()
@ -377,26 +375,22 @@ struct GpuLineGizmo {
joints: GizmoLineJoint,
}
impl RenderAsset for LineGizmo {
type PreparedAsset = GpuLineGizmo;
impl RenderAsset for GpuLineGizmo {
type SourceAsset = LineGizmo;
type Param = SRes<RenderDevice>;
fn asset_usage(&self) -> RenderAssetUsages {
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD
}
fn prepare_asset(
self,
gizmo: Self::SourceAsset,
render_device: &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
let position_buffer_data = cast_slice(&self.positions);
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
let position_buffer_data = cast_slice(&gizmo.positions);
let position_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::VERTEX,
label: Some("LineGizmo Position Buffer"),
contents: position_buffer_data,
});
let color_buffer_data = cast_slice(&self.colors);
let color_buffer_data = cast_slice(&gizmo.colors);
let color_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::VERTEX,
label: Some("LineGizmo Color Buffer"),
@ -406,9 +400,9 @@ impl RenderAsset for LineGizmo {
Ok(GpuLineGizmo {
position_buffer,
color_buffer,
vertex_count: self.positions.len() as u32,
strip: self.strip,
joints: self.joints,
vertex_count: gizmo.positions.len() as u32,
strip: gizmo.strip,
joints: gizmo.joints,
})
}
}
@ -468,7 +462,7 @@ impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
struct DrawLineGizmo;
impl<P: PhaseItem> RenderCommand<P> for DrawLineGizmo {
type Param = SRes<RenderAssets<LineGizmo>>;
type Param = SRes<RenderAssets<GpuLineGizmo>>;
type ViewQuery = ();
type ItemQuery = Read<Handle<LineGizmo>>;
@ -514,7 +508,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawLineGizmo {
struct DrawLineJointGizmo;
impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo {
type Param = SRes<RenderAssets<LineGizmo>>;
type Param = SRes<RenderAssets<GpuLineGizmo>>;
type ViewQuery = ();
type ItemQuery = Read<Handle<LineGizmo>>;

View file

@ -1,8 +1,9 @@
use crate::{
config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig},
line_gizmo_vertex_buffer_layouts, line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo,
DrawLineJointGizmo, GizmoRenderSystem, LineGizmo, LineGizmoUniformBindgroupLayout,
SetLineGizmoBindGroup, LINE_JOINT_SHADER_HANDLE, LINE_SHADER_HANDLE,
DrawLineJointGizmo, GizmoRenderSystem, GpuLineGizmo, LineGizmo,
LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup, LINE_JOINT_SHADER_HANDLE,
LINE_SHADER_HANDLE,
};
use bevy_app::{App, Plugin};
use bevy_asset::Handle;
@ -52,7 +53,7 @@ impl Plugin for LineGizmo2dPlugin {
Render,
(queue_line_gizmos_2d, queue_line_joint_gizmos_2d)
.in_set(GizmoRenderSystem::QueueLineGizmos2d)
.after(prepare_assets::<LineGizmo>),
.after(prepare_assets::<GpuLineGizmo>),
);
}
@ -253,7 +254,7 @@ fn queue_line_gizmos_2d(
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut views: Query<(
&ExtractedView,
&mut SortedRenderPhase<Transparent2d>,
@ -306,7 +307,7 @@ fn queue_line_joint_gizmos_2d(
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut views: Query<(
&ExtractedView,
&mut SortedRenderPhase<Transparent2d>,

View file

@ -1,8 +1,9 @@
use crate::{
config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig},
line_gizmo_vertex_buffer_layouts, line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo,
DrawLineJointGizmo, GizmoRenderSystem, LineGizmo, LineGizmoUniformBindgroupLayout,
SetLineGizmoBindGroup, LINE_JOINT_SHADER_HANDLE, LINE_SHADER_HANDLE,
DrawLineJointGizmo, GizmoRenderSystem, GpuLineGizmo, LineGizmo,
LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup, LINE_JOINT_SHADER_HANDLE,
LINE_SHADER_HANDLE,
};
use bevy_app::{App, Plugin};
use bevy_asset::Handle;
@ -51,7 +52,7 @@ impl Plugin for LineGizmo3dPlugin {
Render,
(queue_line_gizmos_3d, queue_line_joint_gizmos_3d)
.in_set(GizmoRenderSystem::QueueLineGizmos3d)
.after(prepare_assets::<LineGizmo>),
.after(prepare_assets::<GpuLineGizmo>),
);
}
@ -278,7 +279,7 @@ fn queue_line_gizmos_3d(
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut views: Query<(
&ExtractedView,
&mut SortedRenderPhase<Transparent3d>,
@ -361,7 +362,7 @@ fn queue_line_joint_gizmos_3d(
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut views: Query<(
&ExtractedView,
&mut SortedRenderPhase<Transparent3d>,

View file

@ -8,7 +8,7 @@ use bevy_render::{
ShaderRef, SpecializedMeshPipelineError, UnpreparedBindGroup,
},
renderer::RenderDevice,
texture::{FallbackImage, Image},
texture::{FallbackImage, GpuImage},
};
use crate::{Material, MaterialPipeline, MaterialPipelineKey, MeshPipeline, MeshPipelineKey};
@ -139,7 +139,7 @@ impl<B: Material, E: MaterialExtension> AsBindGroup for ExtendedMaterial<B, E> {
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
images: &RenderAssets<Image>,
images: &RenderAssets<GpuImage>,
fallback_image: &FallbackImage,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> {
// add together the bindings of the base material and the user material

View file

@ -95,7 +95,7 @@ use bevy_render::{
render_asset::prepare_assets,
render_graph::RenderGraph,
render_resource::Shader,
texture::Image,
texture::{GpuImage, Image},
view::VisibilitySystems,
ExtractSchedule, Render, RenderApp, RenderSet,
};
@ -375,7 +375,7 @@ impl Plugin for PbrPlugin {
(
prepare_lights
.in_set(RenderSet::ManageViews)
.after(prepare_assets::<Image>),
.after(prepare_assets::<GpuImage>),
prepare_clusters.in_set(RenderSet::PrepareResources),
),
)

View file

@ -60,7 +60,7 @@ use bevy_render::{
TextureSampleType, TextureView,
},
renderer::RenderDevice,
texture::{FallbackImage, Image},
texture::{FallbackImage, GpuImage, Image},
};
use std::num::NonZeroU32;
@ -213,7 +213,7 @@ impl<'a> RenderViewEnvironmentMapBindGroupEntries<'a> {
/// specular binding arrays respectively, as well as the sampler.
pub(crate) fn get(
render_view_environment_maps: Option<&RenderViewLightProbes<EnvironmentMapLight>>,
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,
render_device: &RenderDevice,
) -> RenderViewEnvironmentMapBindGroupEntries<'a> {
@ -283,7 +283,7 @@ impl LightProbeComponent for EnvironmentMapLight {
// view.
type ViewLightProbeInfo = EnvironmentMapViewLightProbeInfo;
fn id(&self, image_assets: &RenderAssets<Image>) -> Option<Self::AssetId> {
fn id(&self, image_assets: &RenderAssets<GpuImage>) -> Option<Self::AssetId> {
if image_assets.get(&self.diffuse_map).is_none()
|| image_assets.get(&self.specular_map).is_none()
{
@ -302,7 +302,7 @@ impl LightProbeComponent for EnvironmentMapLight {
fn create_render_view_light_probes(
view_component: Option<&EnvironmentMapLight>,
image_assets: &RenderAssets<Image>,
image_assets: &RenderAssets<GpuImage>,
) -> RenderViewLightProbes<Self> {
let mut render_view_light_probes = RenderViewLightProbes::new();

View file

@ -140,7 +140,7 @@ use bevy_render::{
TextureSampleType, TextureView,
},
renderer::RenderDevice,
texture::{FallbackImage, Image},
texture::{FallbackImage, GpuImage, Image},
};
use std::{num::NonZeroU32, ops::Deref};
@ -212,7 +212,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
/// the view, as well as the sampler.
pub(crate) fn get(
render_view_irradiance_volumes: Option<&RenderViewLightProbes<IrradianceVolume>>,
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,
render_device: &RenderDevice,
) -> RenderViewIrradianceVolumeBindGroupEntries<'a> {
@ -236,7 +236,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
/// arrays are available on the current platform.
fn get_multiple(
render_view_irradiance_volumes: Option<&RenderViewLightProbes<IrradianceVolume>>,
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,
) -> RenderViewIrradianceVolumeBindGroupEntries<'a> {
let mut texture_views = vec![];
@ -269,7 +269,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
/// arrays aren't available on the current platform.
fn get_single(
render_view_irradiance_volumes: Option<&RenderViewLightProbes<IrradianceVolume>>,
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,
) -> RenderViewIrradianceVolumeBindGroupEntries<'a> {
if let Some(irradiance_volumes) = render_view_irradiance_volumes {
@ -322,7 +322,7 @@ impl LightProbeComponent for IrradianceVolume {
// here.
type ViewLightProbeInfo = ();
fn id(&self, image_assets: &RenderAssets<Image>) -> Option<Self::AssetId> {
fn id(&self, image_assets: &RenderAssets<GpuImage>) -> Option<Self::AssetId> {
if image_assets.get(&self.voxels).is_none() {
None
} else {
@ -336,7 +336,7 @@ impl LightProbeComponent for IrradianceVolume {
fn create_render_view_light_probes(
_: Option<&Self>,
_: &RenderAssets<Image>,
_: &RenderAssets<GpuImage>,
) -> RenderViewLightProbes<Self> {
RenderViewLightProbes::new()
}

View file

@ -21,7 +21,7 @@ use bevy_render::{
render_resource::{DynamicUniformBuffer, Sampler, Shader, ShaderType, TextureView},
renderer::{RenderDevice, RenderQueue},
settings::WgpuFeatures,
texture::{FallbackImage, Image},
texture::{FallbackImage, GpuImage, Image},
view::ExtractedView,
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
@ -270,7 +270,7 @@ pub trait LightProbeComponent: Send + Sync + Component + Sized {
/// Returns the asset ID or asset IDs of the texture or textures referenced
/// by this light probe.
fn id(&self, image_assets: &RenderAssets<Image>) -> Option<Self::AssetId>;
fn id(&self, image_assets: &RenderAssets<GpuImage>) -> Option<Self::AssetId>;
/// Returns the intensity of this light probe.
///
@ -284,7 +284,7 @@ pub trait LightProbeComponent: Send + Sync + Component + Sized {
/// This is called for every light probe in view every frame.
fn create_render_view_light_probes(
view_component: Option<&Self>,
image_assets: &RenderAssets<Image>,
image_assets: &RenderAssets<GpuImage>,
) -> RenderViewLightProbes<Self>;
}
@ -342,7 +342,7 @@ impl Plugin for LightProbePlugin {
/// Gathers up all light probes of a single type in the scene and assigns them
/// to views, performing frustum culling and distance sorting in the process.
fn gather_light_probes<C>(
image_assets: Res<RenderAssets<Image>>,
image_assets: Res<RenderAssets<GpuImage>>,
light_probe_query: Extract<Query<(&GlobalTransform, &C), With<LightProbe>>>,
view_query: Extract<Query<(Entity, &GlobalTransform, &Frustum, Option<&C>), With<Camera3d>>>,
mut reflection_probes: Local<Vec<LightProbeInfo<C>>>,
@ -505,7 +505,7 @@ where
/// every frame.
fn new(
(light_probe_transform, environment_map): (&GlobalTransform, &C),
image_assets: &RenderAssets<Image>,
image_assets: &RenderAssets<GpuImage>,
) -> Option<LightProbeInfo<C>> {
environment_map.id(image_assets).map(|id| LightProbeInfo {
affine_transform: light_probe_transform.affine(),
@ -634,7 +634,7 @@ pub(crate) fn add_cubemap_texture_view<'a>(
texture_views: &mut Vec<&'a <TextureView as Deref>::Target>,
sampler: &mut Option<&'a Sampler>,
image_id: AssetId<Image>,
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,
) {
match images.get(image_id) {

View file

@ -40,6 +40,8 @@ use bevy_ecs::{
};
use bevy_math::{uvec2, vec4, Rect, UVec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::mesh::GpuMesh;
use bevy_render::texture::GpuImage;
use bevy_render::{
mesh::Mesh, render_asset::RenderAssets, render_resource::Shader, texture::Image,
view::ViewVisibility, Extract, ExtractSchedule, RenderApp,
@ -143,8 +145,8 @@ fn extract_lightmaps(
mut render_lightmaps: ResMut<RenderLightmaps>,
lightmaps: Extract<Query<(Entity, &ViewVisibility, &Lightmap)>>,
render_mesh_instances: Res<RenderMeshInstances>,
images: Res<RenderAssets<Image>>,
meshes: Res<RenderAssets<Mesh>>,
images: Res<RenderAssets<GpuImage>>,
meshes: Res<RenderAssets<GpuMesh>>,
) {
// Clear out the old frame's data.
render_lightmaps.render_lightmaps.clear();

View file

@ -4,7 +4,7 @@ use crate::meshlet::{
MeshletGpuScene,
};
use crate::*;
use bevy_asset::{Asset, AssetEvent, AssetId, AssetServer};
use bevy_asset::{Asset, AssetId, AssetServer};
use bevy_core_pipeline::{
core_3d::{
AlphaMask3d, Camera3d, Opaque3d, Opaque3dBinKey, ScreenSpaceTransmissionQuality,
@ -25,16 +25,15 @@ use bevy_render::{
camera::TemporalJitter,
extract_instances::{ExtractInstancesPlugin, ExtractedInstances},
extract_resource::ExtractResource,
mesh::{Mesh, MeshVertexBufferLayoutRef},
render_asset::RenderAssets,
mesh::{GpuMesh, MeshVertexBufferLayoutRef},
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_phase::*,
render_resource::*,
renderer::RenderDevice,
texture::FallbackImage,
view::{ExtractedView, Msaa, VisibleEntities},
Extract,
};
use bevy_utils::{tracing::error, HashMap, HashSet};
use bevy_utils::tracing::error;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicU32, Ordering};
use std::{hash::Hash, num::NonZeroU32};
@ -43,7 +42,7 @@ use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight};
/// Materials are used alongside [`MaterialPlugin`] and [`MaterialMeshBundle`]
/// to spawn entities that are rendered with a specific [`Material`] type. They serve as an easy to use high level
/// way to render [`Mesh`] entities with custom shader logic.
/// way to render [`Mesh`](bevy_render::mesh::Mesh) entities with custom shader logic.
///
/// Materials must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders.
/// [`AsBindGroup`] can be derived, which makes generating bindings straightforward. See the [`AsBindGroup`] docs for details.
@ -250,8 +249,10 @@ where
M::Data: PartialEq + Eq + Hash + Clone,
{
fn build(&self, app: &mut App) {
app.init_asset::<M>()
.add_plugins(ExtractInstancesPlugin::<AssetId<M>>::extract_visible());
app.init_asset::<M>().add_plugins((
ExtractInstancesPlugin::<AssetId<M>>::extract_visible(),
RenderAssetPlugin::<PreparedMaterial<M>>::default(),
));
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
@ -261,28 +262,20 @@ where
.add_render_command::<Transparent3d, DrawMaterial<M>>()
.add_render_command::<Opaque3d, DrawMaterial<M>>()
.add_render_command::<AlphaMask3d, DrawMaterial<M>>()
.init_resource::<ExtractedMaterials<M>>()
.init_resource::<RenderMaterials<M>>()
.init_resource::<SpecializedMeshPipelines<MaterialPipeline<M>>>()
.add_systems(ExtractSchedule, extract_materials::<M>)
.add_systems(
Render,
(
prepare_materials::<M>
.in_set(RenderSet::PrepareAssets)
.after(prepare_assets::<Image>),
queue_material_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_materials::<M>),
),
queue_material_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_assets::<PreparedMaterial<M>>),
);
if self.shadows_enabled {
render_app.add_systems(
Render,
(queue_shadows::<M>
queue_shadows::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_materials::<M>),),
.after(prepare_assets::<PreparedMaterial<M>>),
);
}
@ -438,7 +431,10 @@ type DrawMaterial<M> = (
/// Sets the bind group for a given [`Material`] at the configured `I` index.
pub struct SetMaterialBindGroup<M: Material, const I: usize>(PhantomData<M>);
impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterialBindGroup<M, I> {
type Param = (SRes<RenderMaterials<M>>, SRes<RenderMaterialInstances<M>>);
type Param = (
SRes<RenderAssets<PreparedMaterial<M>>>,
SRes<RenderMaterialInstances<M>>,
);
type ViewQuery = ();
type ItemQuery = ();
@ -456,7 +452,7 @@ impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterial
let Some(material_asset_id) = material_instances.get(&item.entity()) else {
return RenderCommandResult::Failure;
};
let Some(material) = materials.get(material_asset_id) else {
let Some(material) = materials.get(*material_asset_id) else {
return RenderCommandResult::Failure;
};
pass.set_bind_group(I, &material.bind_group, &[]);
@ -522,8 +518,8 @@ pub fn queue_material_meshes<M: Material>(
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
render_materials: Res<RenderMaterials<M>>,
render_meshes: Res<RenderAssets<GpuMesh>>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_mesh_instances: Res<RenderMeshInstances>,
render_material_instances: Res<RenderMaterialInstances<M>>,
render_lightmaps: Res<RenderLightmaps>,
@ -657,7 +653,7 @@ pub fn queue_material_meshes<M: Material>(
let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
continue;
};
let Some(material) = render_materials.get(material_asset_id) else {
let Some(material) = render_materials.get(*material_asset_id) else {
continue;
};
@ -846,6 +842,61 @@ pub struct PreparedMaterial<T: Material> {
pub properties: MaterialProperties,
}
impl<M: Material> RenderAsset for PreparedMaterial<M> {
type SourceAsset = M;
type Param = (
SRes<RenderDevice>,
SRes<RenderAssets<GpuImage>>,
SRes<FallbackImage>,
SRes<MaterialPipeline<M>>,
SRes<DefaultOpaqueRendererMethod>,
);
fn prepare_asset(
material: Self::SourceAsset,
(render_device, images, fallback_image, pipeline, default_opaque_render_method): &mut SystemParamItem<Self::Param>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
match material.as_bind_group(
&pipeline.material_layout,
render_device,
images,
fallback_image,
) {
Ok(prepared) => {
let method = match material.opaque_render_method() {
OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
OpaqueRendererMethod::Auto => default_opaque_render_method.0,
};
let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
mesh_pipeline_key_bits.set(
MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
material.reads_view_transmission_texture(),
);
mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(material.alpha_mode()));
Ok(PreparedMaterial {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
properties: MaterialProperties {
alpha_mode: material.alpha_mode(),
depth_bias: material.depth_bias(),
reads_view_transmission_texture: mesh_pipeline_key_bits
.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE),
render_method: method,
mesh_pipeline_key_bits,
},
})
}
Err(AsBindGroupError::RetryNextUpdate) => {
Err(PrepareAssetError::RetryNextUpdate(material))
}
}
}
}
#[derive(Component, Clone, Copy, Default, PartialEq, Eq, Deref, DerefMut)]
pub struct MaterialBindGroupId(pub Option<BindGroupId>);
@ -894,181 +945,3 @@ impl<T: Material> PreparedMaterial<T> {
MaterialBindGroupId(Some(self.bind_group.id()))
}
}
#[derive(Resource)]
pub struct ExtractedMaterials<M: Material> {
extracted: Vec<(AssetId<M>, M)>,
removed: Vec<AssetId<M>>,
}
impl<M: Material> Default for ExtractedMaterials<M> {
fn default() -> Self {
Self {
extracted: Default::default(),
removed: Default::default(),
}
}
}
/// Stores all prepared representations of [`Material`] assets for as long as they exist.
#[derive(Resource, Deref, DerefMut)]
pub struct RenderMaterials<T: Material>(pub HashMap<AssetId<T>, PreparedMaterial<T>>);
impl<T: Material> Default for RenderMaterials<T> {
fn default() -> Self {
Self(Default::default())
}
}
/// This system extracts all created or modified assets of the corresponding [`Material`] type
/// into the "render world".
pub fn extract_materials<M: Material>(
mut commands: Commands,
mut events: Extract<EventReader<AssetEvent<M>>>,
assets: Extract<Res<Assets<M>>>,
) {
let mut changed_assets = HashSet::default();
let mut removed = Vec::new();
for event in events.read() {
#[allow(clippy::match_same_arms)]
match event {
AssetEvent::Added { id } | AssetEvent::Modified { id } => {
changed_assets.insert(*id);
}
AssetEvent::Removed { id } => {
changed_assets.remove(id);
removed.push(*id);
}
AssetEvent::Unused { .. } => {}
AssetEvent::LoadedWithDependencies { .. } => {
// TODO: handle this
}
}
}
let mut extracted_assets = Vec::new();
for id in changed_assets.drain() {
if let Some(asset) = assets.get(id) {
extracted_assets.push((id, asset.clone()));
}
}
commands.insert_resource(ExtractedMaterials {
extracted: extracted_assets,
removed,
});
}
/// All [`Material`] values of a given type that should be prepared next frame.
pub struct PrepareNextFrameMaterials<M: Material> {
assets: Vec<(AssetId<M>, M)>,
}
impl<M: Material> Default for PrepareNextFrameMaterials<M> {
fn default() -> Self {
Self {
assets: Default::default(),
}
}
}
/// This system prepares all assets of the corresponding [`Material`] type
/// which where extracted this frame for the GPU.
#[allow(clippy::too_many_arguments)]
pub fn prepare_materials<M: Material>(
mut prepare_next_frame: Local<PrepareNextFrameMaterials<M>>,
mut extracted_assets: ResMut<ExtractedMaterials<M>>,
mut render_materials: ResMut<RenderMaterials<M>>,
render_device: Res<RenderDevice>,
images: Res<RenderAssets<Image>>,
fallback_image: Res<FallbackImage>,
pipeline: Res<MaterialPipeline<M>>,
default_opaque_render_method: Res<DefaultOpaqueRendererMethod>,
) {
let queued_assets = std::mem::take(&mut prepare_next_frame.assets);
for (id, material) in queued_assets.into_iter() {
if extracted_assets.removed.contains(&id) {
continue;
}
match prepare_material(
&material,
&render_device,
&images,
&fallback_image,
&pipeline,
default_opaque_render_method.0,
) {
Ok(prepared_asset) => {
render_materials.insert(id, prepared_asset);
}
Err(AsBindGroupError::RetryNextUpdate) => {
prepare_next_frame.assets.push((id, material));
}
}
}
for removed in std::mem::take(&mut extracted_assets.removed) {
render_materials.remove(&removed);
}
for (id, material) in std::mem::take(&mut extracted_assets.extracted) {
match prepare_material(
&material,
&render_device,
&images,
&fallback_image,
&pipeline,
default_opaque_render_method.0,
) {
Ok(prepared_asset) => {
render_materials.insert(id, prepared_asset);
}
Err(AsBindGroupError::RetryNextUpdate) => {
prepare_next_frame.assets.push((id, material));
}
}
}
}
fn prepare_material<M: Material>(
material: &M,
render_device: &RenderDevice,
images: &RenderAssets<Image>,
fallback_image: &FallbackImage,
pipeline: &MaterialPipeline<M>,
default_opaque_render_method: OpaqueRendererMethod,
) -> Result<PreparedMaterial<M>, AsBindGroupError> {
let prepared = material.as_bind_group(
&pipeline.material_layout,
render_device,
images,
fallback_image,
)?;
let method = match material.opaque_render_method() {
OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
OpaqueRendererMethod::Auto => default_opaque_render_method,
};
let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
mesh_pipeline_key_bits.set(
MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
material.reads_view_transmission_texture(),
);
mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(material.alpha_mode()));
Ok(PreparedMaterial {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
properties: MaterialProperties {
alpha_mode: material.alpha_mode(),
depth_bias: material.depth_bias(),
reads_view_transmission_texture: mesh_pipeline_key_bits
.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE),
render_method: method,
mesh_pipeline_key_bits,
},
})
}

View file

@ -10,6 +10,7 @@ use bevy_derive::{Deref, DerefMut};
use bevy_render::{
camera::TemporalJitter,
mesh::{Mesh, MeshVertexBufferLayout, MeshVertexBufferLayoutRef, MeshVertexBufferLayouts},
render_asset::RenderAssets,
render_resource::*,
view::ExtractedView,
};
@ -29,7 +30,7 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass<M: Material>(
pipeline_cache: Res<PipelineCache>,
material_pipeline: Res<MaterialPipeline<M>>,
mesh_pipeline: Res<MeshPipeline>,
render_materials: Res<RenderMaterials<M>>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_material_instances: Res<RenderMaterialInstances<M>>,
asset_server: Res<AssetServer>,
mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,
@ -139,7 +140,7 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass<M: Material>(
view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList);
for material_id in render_material_instances.values() {
let Some(material) = render_materials.get(material_id) else {
let Some(material) = render_materials.get(*material_id) else {
continue;
};
@ -226,7 +227,7 @@ pub fn prepare_material_meshlet_meshes_prepass<M: Material>(
mut cache: Local<HashMap<MeshPipelineKey, CachedRenderPipelineId>>,
pipeline_cache: Res<PipelineCache>,
prepass_pipeline: Res<PrepassPipeline<M>>,
render_materials: Res<RenderMaterials<M>>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_material_instances: Res<RenderMaterialInstances<M>>,
mut mesh_vertex_buffer_layouts: ResMut<MeshVertexBufferLayouts>,
asset_server: Res<AssetServer>,
@ -264,7 +265,7 @@ pub fn prepare_material_meshlet_meshes_prepass<M: Material>(
view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList);
for material_id in render_material_instances.values() {
let Some(material) = render_materials.get(material_id) else {
let Some(material) = render_materials.get(*material_id) else {
continue;
};

View file

@ -651,7 +651,10 @@ pub struct StandardMaterialUniform {
}
impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
fn as_bind_group_shader_type(&self, images: &RenderAssets<Image>) -> StandardMaterialUniform {
fn as_bind_group_shader_type(
&self,
images: &RenderAssets<GpuImage>,
) -> StandardMaterialUniform {
let mut flags = StandardMaterialFlags::NONE;
if self.base_color_texture.is_some() {
flags |= StandardMaterialFlags::BASE_COLOR_TEXTURE;

View file

@ -1,7 +1,7 @@
mod prepass_bindings;
use bevy_render::batching::{batch_and_prepare_binned_render_phase, sort_binned_render_phase};
use bevy_render::mesh::MeshVertexBufferLayoutRef;
use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef};
use bevy_render::render_resource::binding_types::uniform_buffer;
pub use prepass_bindings::*;
@ -181,7 +181,7 @@ where
Render,
queue_prepass_material_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_materials::<M>)
.after(prepare_assets::<PreparedMaterial<M>>)
// queue_material_meshes only writes to `material_bind_group_id`, which `queue_prepass_material_meshes` doesn't read
.ambiguous_with(queue_material_meshes::<StandardMaterial>),
);
@ -714,9 +714,9 @@ pub fn queue_prepass_material_meshes<M: Material>(
mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
render_meshes: Res<RenderAssets<GpuMesh>>,
render_mesh_instances: Res<RenderMeshInstances>,
render_materials: Res<RenderMaterials<M>>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_material_instances: Res<RenderMaterialInstances<M>>,
render_lightmaps: Res<RenderLightmaps>,
mut views: Query<
@ -789,7 +789,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
let Some(mesh_instance) = render_mesh_instances.get(visible_entity) else {
continue;
};
let Some(material) = render_materials.get(material_asset_id) else {
let Some(material) = render_materials.get(*material_asset_id) else {
continue;
};
let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {

View file

@ -3,10 +3,11 @@ use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT};
use bevy_ecs::prelude::*;
use bevy_ecs::{entity::EntityHashMap, system::lifetimeless::Read};
use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_render::mesh::Mesh;
use bevy_render::{
camera::Camera,
diagnostic::RecordDiagnostics,
mesh::Mesh,
mesh::GpuMesh,
primitives::{CascadesFrusta, CubemapFrusta, Frustum, HalfSpace},
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext},
@ -1599,9 +1600,9 @@ pub fn prepare_clusters(
pub fn queue_shadows<M: Material>(
shadow_draw_functions: Res<DrawFunctions<Shadow>>,
prepass_pipeline: Res<PrepassPipeline<M>>,
render_meshes: Res<RenderAssets<Mesh>>,
render_meshes: Res<RenderAssets<GpuMesh>>,
render_mesh_instances: Res<RenderMeshInstances>,
render_materials: Res<RenderMaterials<M>>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_material_instances: Res<RenderMaterialInstances<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
@ -1659,7 +1660,7 @@ pub fn queue_shadows<M: Material>(
let Some(material_asset_id) = render_material_instances.get(&entity) else {
continue;
};
let Some(material) = render_materials.get(material_asset_id) else {
let Some(material) = render_materials.get(*material_asset_id) else {
continue;
};
let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {

View file

@ -22,7 +22,7 @@ use bevy_render::{
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
texture::{BevyDefault, DefaultImageSampler, GpuImage, ImageSampler, TextureFormatPixelInfo},
texture::{BevyDefault, DefaultImageSampler, ImageSampler, TextureFormatPixelInfo},
view::{ViewTarget, ViewUniformOffset, ViewVisibility},
Extract,
};
@ -427,7 +427,7 @@ impl FromWorld for MeshPipeline {
impl MeshPipeline {
pub fn get_image_texture<'a>(
&'a self,
gpu_images: &'a RenderAssets<Image>,
gpu_images: &'a RenderAssets<GpuImage>,
handle_option: &Option<Handle<Image>>,
) -> Option<(&'a TextureView, &'a Sampler)> {
if let Some(handle) = handle_option {
@ -1026,8 +1026,8 @@ impl MeshBindGroups {
#[allow(clippy::too_many_arguments)]
pub fn prepare_mesh_bind_group(
meshes: Res<RenderAssets<Mesh>>,
images: Res<RenderAssets<Image>>,
meshes: Res<RenderAssets<GpuMesh>>,
images: Res<RenderAssets<GpuImage>>,
mut groups: ResMut<MeshBindGroups>,
mesh_pipeline: Res<MeshPipeline>,
render_device: Res<RenderDevice>,
@ -1187,7 +1187,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
pub struct DrawMesh;
impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
type Param = (SRes<RenderAssets<Mesh>>, SRes<RenderMeshInstances>);
type Param = (SRes<RenderAssets<GpuMesh>>, SRes<RenderMeshInstances>);
type ViewQuery = ();
type ItemQuery = ();
#[inline]

View file

@ -17,7 +17,7 @@ use bevy_render::{
render_asset::RenderAssets,
render_resource::{binding_types::*, *},
renderer::RenderDevice,
texture::{BevyDefault, FallbackImage, FallbackImageMsaa, FallbackImageZero, Image},
texture::{BevyDefault, FallbackImage, FallbackImageMsaa, FallbackImageZero, GpuImage},
view::{Msaa, ViewUniform, ViewUniforms},
};
@ -370,7 +370,7 @@ pub fn prepare_mesh_view_bind_groups(
Option<&RenderViewLightProbes<IrradianceVolume>>,
)>,
(images, mut fallback_images, fallback_image, fallback_image_zero): (
Res<RenderAssets<Image>>,
Res<RenderAssets<GpuImage>>,
FallbackImageMsaa,
Res<FallbackImage>,
Res<FallbackImageZero>,

View file

@ -498,7 +498,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
&self,
layout: &#render_path::render_resource::BindGroupLayout,
render_device: &#render_path::renderer::RenderDevice,
images: &#render_path::render_asset::RenderAssets<#render_path::texture::Image>,
images: &#render_path::render_asset::RenderAssets<#render_path::texture::GpuImage>,
fallback_image: &#render_path::texture::FallbackImage,
) -> Result<#render_path::render_resource::UnpreparedBindGroup<Self::Data>, #render_path::render_resource::AsBindGroupError> {
let bindings = vec![#(#binding_impls,)*];

View file

@ -1,11 +1,11 @@
use crate::{
camera::CameraProjection,
camera::{ManualTextureViewHandle, ManualTextureViews},
camera::{CameraProjection, ManualTextureViewHandle, ManualTextureViews},
prelude::Image,
primitives::Frustum,
render_asset::RenderAssets,
render_graph::{InternedRenderSubGraph, RenderSubGraph},
render_resource::TextureView,
texture::GpuImage,
view::{ColorGrading, ExtractedView, ExtractedWindows, RenderLayers, VisibleEntities},
Extract,
};
@ -581,7 +581,7 @@ impl NormalizedRenderTarget {
pub fn get_texture_view<'a>(
&self,
windows: &'a ExtractedWindows,
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
manual_texture_views: &'a ManualTextureViews,
) -> Option<&'a TextureView> {
match self {
@ -601,7 +601,7 @@ impl NormalizedRenderTarget {
pub fn get_texture_format<'a>(
&self,
windows: &'a ExtractedWindows,
images: &'a RenderAssets<Image>,
images: &'a RenderAssets<GpuImage>,
manual_texture_views: &'a ManualTextureViews,
) -> Option<TextureFormat> {
match self {

View file

@ -61,10 +61,11 @@ use bevy_window::{PrimaryWindow, RawHandleWrapper};
use globals::GlobalsPlugin;
use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
use crate::mesh::GpuMesh;
use crate::renderer::WgpuWrapper;
use crate::{
camera::CameraPlugin,
mesh::{morph::MorphPlugin, Mesh, MeshPlugin},
mesh::{morph::MorphPlugin, MeshPlugin},
render_asset::prepare_assets,
render_resource::{PipelineCache, Shader, ShaderLoader},
renderer::{render_system, RenderInstance},
@ -111,7 +112,7 @@ pub enum RenderSet {
/// Queue drawable entities as phase items in render phases ready for
/// sorting (if necessary)
Queue,
/// A sub-set within [`Queue`](RenderSet::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<Mesh>` is completed.
/// A sub-set within [`Queue`](RenderSet::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<GpuMesh>` is completed.
QueueMeshes,
// TODO: This could probably be moved in favor of a system ordering
// abstraction in `Render` or `Queue`
@ -161,7 +162,7 @@ impl Render {
);
schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain());
schedule.configure_sets(QueueMeshes.in_set(Queue).after(prepare_assets::<Mesh>));
schedule.configure_sets(QueueMeshes.in_set(Queue).after(prepare_assets::<GpuMesh>));
schedule.configure_sets(
(PrepareResources, PrepareResourcesFlush, PrepareBindGroups)
.chain()

View file

@ -10,6 +10,7 @@ use crate::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages, RenderAssets},
render_resource::{Buffer, TextureView, VertexBufferLayout},
renderer::RenderDevice,
texture::GpuImage,
};
use bevy_asset::{Asset, Handle};
use bevy_derive::EnumVariantMeta;
@ -1463,68 +1464,69 @@ pub enum GpuBufferInfo {
NonIndexed,
}
impl RenderAsset for Mesh {
type PreparedAsset = GpuMesh;
impl RenderAsset for GpuMesh {
type SourceAsset = Mesh;
type Param = (
SRes<RenderDevice>,
SRes<RenderAssets<Image>>,
SRes<RenderAssets<GpuImage>>,
SResMut<MeshVertexBufferLayouts>,
);
fn asset_usage(&self) -> RenderAssetUsages {
self.asset_usage
#[inline]
fn asset_usage(mesh: &Self::SourceAsset) -> RenderAssetUsages {
mesh.asset_usage
}
/// Converts the extracted mesh a into [`GpuMesh`].
fn prepare_asset(
self,
mesh: Self::SourceAsset,
(render_device, images, ref mut mesh_vertex_buffer_layouts): &mut SystemParamItem<
Self::Param,
>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
let morph_targets = match self.morph_targets.as_ref() {
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
let morph_targets = match mesh.morph_targets.as_ref() {
Some(mt) => {
let Some(target_image) = images.get(mt) else {
return Err(PrepareAssetError::RetryNextUpdate(self));
return Err(PrepareAssetError::RetryNextUpdate(mesh));
};
Some(target_image.texture_view.clone())
}
None => None,
};
let vertex_buffer_data = self.get_vertex_buffer_data();
let vertex_buffer_data = mesh.get_vertex_buffer_data();
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::VERTEX,
label: Some("Mesh Vertex Buffer"),
contents: &vertex_buffer_data,
});
let buffer_info = if let Some(data) = self.get_index_buffer_bytes() {
let buffer_info = if let Some(data) = mesh.get_index_buffer_bytes() {
GpuBufferInfo::Indexed {
buffer: render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::INDEX,
contents: data,
label: Some("Mesh Index Buffer"),
}),
count: self.indices().unwrap().len() as u32,
index_format: self.indices().unwrap().into(),
count: mesh.indices().unwrap().len() as u32,
index_format: mesh.indices().unwrap().into(),
}
} else {
GpuBufferInfo::NonIndexed
};
let mesh_vertex_buffer_layout =
self.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
let mut key_bits = BaseMeshPipelineKey::from_primitive_topology(self.primitive_topology());
let mut key_bits = BaseMeshPipelineKey::from_primitive_topology(mesh.primitive_topology());
key_bits.set(
BaseMeshPipelineKey::MORPH_TARGETS,
self.morph_targets.is_some(),
mesh.morph_targets.is_some(),
);
Ok(GpuMesh {
vertex_buffer,
vertex_count: self.count_vertices() as u32,
vertex_count: mesh.count_vertices() as u32,
buffer_info,
key_bits,
layout: mesh_vertex_buffer_layout,

View file

@ -11,7 +11,7 @@ use std::{
sync::Arc,
};
use crate::{prelude::Image, render_asset::RenderAssetPlugin, RenderApp};
use crate::{render_asset::RenderAssetPlugin, texture::GpuImage, RenderApp};
use bevy_app::{App, Plugin};
use bevy_asset::AssetApp;
use bevy_ecs::{entity::Entity, system::Resource};
@ -27,7 +27,7 @@ impl Plugin for MeshPlugin {
.register_type::<skinning::SkinnedMesh>()
.register_type::<Vec<Entity>>()
// 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready
.add_plugins(RenderAssetPlugin::<Mesh, Image>::default());
.add_plugins(RenderAssetPlugin::<GpuMesh, GpuImage>::default());
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;

View file

@ -21,14 +21,14 @@ pub enum PrepareAssetError<E: Send + Sync + 'static> {
/// Describes how an asset gets extracted and prepared for rendering.
///
/// In the [`ExtractSchedule`] step the asset is transferred
/// In the [`ExtractSchedule`] step the [`RenderAsset::SourceAsset`] is transferred
/// from the "main world" into the "render world".
///
/// After that in the [`RenderSet::PrepareAssets`] step the extracted asset
/// is transformed into its GPU-representation of type [`RenderAsset::PreparedAsset`].
pub trait RenderAsset: Asset + Clone {
/// The GPU-representation of the asset.
type PreparedAsset: Send + Sync + 'static;
/// is transformed into its GPU-representation of type [`RenderAsset`].
pub trait RenderAsset: Send + Sync + 'static + Sized {
/// The representation of the asset in the "main world".
type SourceAsset: Asset + Clone;
/// Specifies all ECS data required by [`RenderAsset::prepare_asset`].
///
@ -36,15 +36,18 @@ pub trait RenderAsset: Asset + Clone {
type Param: SystemParam;
/// Whether or not to unload the asset after extracting it to the render world.
fn asset_usage(&self) -> RenderAssetUsages;
#[inline]
fn asset_usage(_source_asset: &Self::SourceAsset) -> RenderAssetUsages {
RenderAssetUsages::default()
}
/// Prepares the asset for the GPU by transforming it into a [`RenderAsset::PreparedAsset`].
/// Prepares the [`RenderAsset::SourceAsset`] for the GPU by transforming it into a [`RenderAsset`].
///
/// ECS data may be accessed via `param`.
fn prepare_asset(
self,
source_asset: Self::SourceAsset,
param: &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>>;
) -> Result<Self, PrepareAssetError<Self::SourceAsset>>;
}
bitflags::bitflags! {
@ -101,8 +104,8 @@ impl Default for RenderAssetUsages {
///
/// The `AFTER` generic parameter can be used to specify that `A::prepare_asset` should not be run until
/// `prepare_assets::<AFTER>` has completed. This allows the `prepare_asset` function to depend on another
/// prepared [`RenderAsset`], for example `Mesh::prepare_asset` relies on `RenderAssets::<Image>` for morph
/// targets, so the plugin is created as `RenderAssetPlugin::<Mesh, Image>::default()`.
/// prepared [`RenderAsset`], for example `Mesh::prepare_asset` relies on `RenderAssets::<GpuImage>` for morph
/// targets, so the plugin is created as `RenderAssetPlugin::<GpuMesh, GpuImage>::default()`.
pub struct RenderAssetPlugin<A: RenderAsset, AFTER: RenderAssetDependency + 'static = ()> {
phantom: PhantomData<fn() -> (A, AFTER)>,
}
@ -156,8 +159,8 @@ impl<A: RenderAsset> RenderAssetDependency for A {
/// Temporarily stores the extracted and removed assets of the current frame.
#[derive(Resource)]
pub struct ExtractedAssets<A: RenderAsset> {
extracted: Vec<(AssetId<A>, A)>,
removed: Vec<AssetId<A>>,
extracted: Vec<(AssetId<A::SourceAsset>, A::SourceAsset)>,
removed: Vec<AssetId<A::SourceAsset>>,
}
impl<A: RenderAsset> Default for ExtractedAssets<A> {
@ -169,10 +172,10 @@ impl<A: RenderAsset> Default for ExtractedAssets<A> {
}
}
/// Stores all GPU representations ([`RenderAsset::PreparedAssets`](RenderAsset::PreparedAsset))
/// of [`RenderAssets`](RenderAsset) as long as they exist.
/// Stores all GPU representations ([`RenderAsset`])
/// of [`RenderAsset::SourceAsset`] as long as they exist.
#[derive(Resource)]
pub struct RenderAssets<A: RenderAsset>(HashMap<AssetId<A>, A::PreparedAsset>);
pub struct RenderAssets<A: RenderAsset>(HashMap<AssetId<A::SourceAsset>, A>);
impl<A: RenderAsset> Default for RenderAssets<A> {
fn default() -> Self {
@ -181,31 +184,27 @@ impl<A: RenderAsset> Default for RenderAssets<A> {
}
impl<A: RenderAsset> RenderAssets<A> {
pub fn get(&self, id: impl Into<AssetId<A>>) -> Option<&A::PreparedAsset> {
pub fn get(&self, id: impl Into<AssetId<A::SourceAsset>>) -> Option<&A> {
self.0.get(&id.into())
}
pub fn get_mut(&mut self, id: impl Into<AssetId<A>>) -> Option<&mut A::PreparedAsset> {
pub fn get_mut(&mut self, id: impl Into<AssetId<A::SourceAsset>>) -> Option<&mut A> {
self.0.get_mut(&id.into())
}
pub fn insert(
&mut self,
id: impl Into<AssetId<A>>,
value: A::PreparedAsset,
) -> Option<A::PreparedAsset> {
pub fn insert(&mut self, id: impl Into<AssetId<A::SourceAsset>>, value: A) -> Option<A> {
self.0.insert(id.into(), value)
}
pub fn remove(&mut self, id: impl Into<AssetId<A>>) -> Option<A::PreparedAsset> {
pub fn remove(&mut self, id: impl Into<AssetId<A::SourceAsset>>) -> Option<A> {
self.0.remove(&id.into())
}
pub fn iter(&self) -> impl Iterator<Item = (AssetId<A>, &A::PreparedAsset)> {
pub fn iter(&self) -> impl Iterator<Item = (AssetId<A::SourceAsset>, &A)> {
self.0.iter().map(|(k, v)| (*k, v))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (AssetId<A>, &mut A::PreparedAsset)> {
pub fn iter_mut(&mut self) -> impl Iterator<Item = (AssetId<A::SourceAsset>, &mut A)> {
self.0.iter_mut().map(|(k, v)| (*k, v))
}
}
@ -213,8 +212,8 @@ impl<A: RenderAsset> RenderAssets<A> {
#[derive(Resource)]
struct CachedExtractRenderAssetSystemState<A: RenderAsset> {
state: SystemState<(
EventReader<'static, 'static, AssetEvent<A>>,
ResMut<'static, Assets<A>>,
EventReader<'static, 'static, AssetEvent<A::SourceAsset>>,
ResMut<'static, Assets<A::SourceAsset>>,
)>,
}
@ -226,7 +225,7 @@ impl<A: RenderAsset> FromWorld for CachedExtractRenderAssetSystemState<A> {
}
}
/// This system extracts all created or modified assets of the corresponding [`RenderAsset`] type
/// This system extracts all created or modified assets of the corresponding [`RenderAsset::SourceAsset`] type
/// into the "render world".
fn extract_render_asset<A: RenderAsset>(mut commands: Commands, mut main_world: ResMut<MainWorld>) {
main_world.resource_scope(
@ -256,7 +255,7 @@ fn extract_render_asset<A: RenderAsset>(mut commands: Commands, mut main_world:
let mut extracted_assets = Vec::new();
for id in changed_assets.drain() {
if let Some(asset) = assets.get(id) {
let asset_usage = asset.asset_usage();
let asset_usage = A::asset_usage(asset);
if asset_usage.contains(RenderAssetUsages::RENDER_WORLD) {
if asset_usage == RenderAssetUsages::RENDER_WORLD {
if let Some(asset) = assets.remove(id) {
@ -269,7 +268,7 @@ fn extract_render_asset<A: RenderAsset>(mut commands: Commands, mut main_world:
}
}
commands.insert_resource(ExtractedAssets {
commands.insert_resource(ExtractedAssets::<A> {
extracted: extracted_assets,
removed,
});
@ -282,7 +281,7 @@ fn extract_render_asset<A: RenderAsset>(mut commands: Commands, mut main_world:
/// All assets that should be prepared next frame.
#[derive(Resource)]
pub struct PrepareNextFrameAssets<A: RenderAsset> {
assets: Vec<(AssetId<A>, A)>,
assets: Vec<(AssetId<A::SourceAsset>, A::SourceAsset)>,
}
impl<A: RenderAsset> Default for PrepareNextFrameAssets<A> {
@ -293,7 +292,7 @@ impl<A: RenderAsset> Default for PrepareNextFrameAssets<A> {
}
}
/// This system prepares all assets of the corresponding [`RenderAsset`] type
/// This system prepares all assets of the corresponding [`RenderAsset::SourceAsset`] type
/// which where extracted this frame for the GPU.
pub fn prepare_assets<A: RenderAsset>(
mut extracted_assets: ResMut<ExtractedAssets<A>>,
@ -308,7 +307,7 @@ pub fn prepare_assets<A: RenderAsset>(
continue;
}
match extracted_asset.prepare_asset(&mut param) {
match A::prepare_asset(extracted_asset, &mut param) {
Ok(prepared_asset) => {
render_assets.insert(id, prepared_asset);
}
@ -323,7 +322,7 @@ pub fn prepare_assets<A: RenderAsset>(
}
for (id, extracted_asset) in extracted_assets.extracted.drain(..) {
match extracted_asset.prepare_asset(&mut param) {
match A::prepare_asset(extracted_asset, &mut param) {
Ok(prepared_asset) => {
render_assets.insert(id, prepared_asset);
}

View file

@ -1,10 +1,9 @@
use crate::{
define_atomic_id,
prelude::Image,
render_asset::RenderAssets,
render_resource::{resource_macros::*, BindGroupLayout, Buffer, Sampler, TextureView},
renderer::RenderDevice,
texture::FallbackImage,
texture::{FallbackImage, GpuImage},
};
pub use bevy_render_macros::AsBindGroup;
use encase::ShaderType;
@ -58,14 +57,14 @@ impl Deref for BindGroup {
///
/// This is an opinionated trait that is intended to make it easy to generically
/// convert a type into a [`BindGroup`]. It provides access to specific render resources,
/// such as [`RenderAssets<Image>`] and [`FallbackImage`]. If a type has a [`Handle<Image>`](bevy_asset::Handle),
/// such as [`RenderAssets<GpuImage>`] and [`FallbackImage`]. If a type has a [`Handle<Image>`](bevy_asset::Handle),
/// these can be used to retrieve the corresponding [`Texture`](crate::render_resource::Texture) resource.
///
/// [`AsBindGroup::as_bind_group`] is intended to be called once, then the result cached somewhere. It is generally
/// ok to do "expensive" work here, such as creating a [`Buffer`] for a uniform.
///
/// If for some reason a [`BindGroup`] cannot be created yet (for example, the [`Texture`](crate::render_resource::Texture)
/// for an [`Image`] hasn't loaded yet), just return [`AsBindGroupError::RetryNextUpdate`], which signals that the caller
/// for an [`Image`](crate::texture::Image) hasn't loaded yet), just return [`AsBindGroupError::RetryNextUpdate`], which signals that the caller
/// should retry again later.
///
/// # Deriving
@ -117,7 +116,7 @@ impl Deref for BindGroup {
/// GPU resource, which will be bound as a texture in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,
/// most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
/// [`None`], the [`FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `sampler` binding attribute
/// (with a different binding index) if a binding of the sampler for the [`Image`] is also required.
/// (with a different binding index) if a binding of the sampler for the [`Image`](crate::texture::Image) is also required.
///
/// | Arguments | Values | Default |
/// |-----------------------|-------------------------------------------------------------------------|----------------------|
@ -145,7 +144,7 @@ impl Deref for BindGroup {
/// resource, which will be bound as a sampler in shaders. The field will be assumed to implement [`Into<Option<Handle<Image>>>`]. In practice,
/// most fields should be a [`Handle<Image>`](bevy_asset::Handle) or [`Option<Handle<Image>>`]. If the value of an [`Option<Handle<Image>>`] is
/// [`None`], the [`FallbackImage`] resource will be used instead. This attribute can be used in conjunction with a `texture` binding attribute
/// (with a different binding index) if a binding of the texture for the [`Image`] is also required.
/// (with a different binding index) if a binding of the texture for the [`Image`](crate::texture::Image) is also required.
///
/// | Arguments | Values | Default |
/// |------------------------|-------------------------------------------------------------------------|------------------------|
@ -220,7 +219,7 @@ impl Deref for BindGroup {
/// much like the field-level `uniform` attribute. The difference is that the entire [`AsBindGroup`] value is converted to `ConvertedShaderType`,
/// which must implement [`ShaderType`], instead of a specific field implementing [`ShaderType`]. This is useful if more complicated conversion
/// logic is required. The conversion is done using the [`AsBindGroupShaderType<ConvertedShaderType>`] trait, which is automatically implemented
/// if `&Self` implements [`Into<ConvertedShaderType>`]. Only use [`AsBindGroupShaderType`] if access to resources like [`RenderAssets<Image>`] is
/// if `&Self` implements [`Into<ConvertedShaderType>`]. Only use [`AsBindGroupShaderType`] if access to resources like [`RenderAssets<GpuImage>`] is
/// required.
/// * `bind_group_data(DataType)`
/// * The [`AsBindGroup`] type will be converted to some `DataType` using [`Into<DataType>`] and stored
@ -295,7 +294,7 @@ pub trait AsBindGroup {
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
images: &RenderAssets<Image>,
images: &RenderAssets<GpuImage>,
fallback_image: &FallbackImage,
) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> {
let UnpreparedBindGroup { bindings, data } =
@ -326,7 +325,7 @@ pub trait AsBindGroup {
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
images: &RenderAssets<Image>,
images: &RenderAssets<GpuImage>,
fallback_image: &FallbackImage,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError>;
@ -396,7 +395,7 @@ impl OwnedBindingResource {
pub trait AsBindGroupShaderType<T: ShaderType> {
/// Return the `T` [`ShaderType`] for `self`. When used in [`AsBindGroup`]
/// derives, it is safe to assume that all images in `self` exist.
fn as_bind_group_shader_type(&self, images: &RenderAssets<Image>) -> T;
fn as_bind_group_shader_type(&self, images: &RenderAssets<GpuImage>) -> T;
}
impl<T, U: ShaderType> AsBindGroupShaderType<U> for T
@ -404,7 +403,7 @@ where
for<'a> &'a T: Into<U>,
{
#[inline]
fn as_bind_group_shader_type(&self, _images: &RenderAssets<Image>) -> U {
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U {
self.into()
}
}
@ -412,7 +411,7 @@ where
#[cfg(test)]
mod test {
use super::*;
use crate as bevy_render;
use crate::{self as bevy_render, prelude::Image};
use bevy_asset::Handle;
#[test]

View file

@ -826,39 +826,41 @@ pub struct GpuImage {
pub mip_level_count: u32,
}
impl RenderAsset for Image {
type PreparedAsset = GpuImage;
impl RenderAsset for GpuImage {
type SourceAsset = Image;
type Param = (
SRes<RenderDevice>,
SRes<RenderQueue>,
SRes<DefaultImageSampler>,
);
fn asset_usage(&self) -> RenderAssetUsages {
self.asset_usage
#[inline]
fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {
image.asset_usage
}
/// Converts the extracted image into a [`GpuImage`].
fn prepare_asset(
self,
image: Self::SourceAsset,
(render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
let texture = render_device.create_texture_with_data(
render_queue,
&self.texture_descriptor,
&image.texture_descriptor,
// TODO: Is this correct? Do we need to use `MipMajor` if it's a ktx2 file?
wgpu::util::TextureDataOrder::default(),
&self.data,
&image.data,
);
let size = self.size();
let size = image.size();
let texture_view = texture.create_view(
self.texture_view_descriptor
image
.texture_view_descriptor
.or_else(|| Some(TextureViewDescriptor::default()))
.as_ref()
.unwrap(),
);
let sampler = match self.sampler {
let sampler = match image.sampler {
ImageSampler::Default => (***default_sampler).clone(),
ImageSampler::Descriptor(descriptor) => {
render_device.create_sampler(&descriptor.as_wgpu())
@ -868,10 +870,10 @@ impl RenderAsset for Image {
Ok(GpuImage {
texture,
texture_view,
texture_format: self.texture_descriptor.format,
texture_format: image.texture_descriptor.format,
sampler,
size,
mip_level_count: self.texture_descriptor.mip_level_count,
mip_level_count: image.texture_descriptor.mip_level_count,
})
}
}

View file

@ -84,7 +84,7 @@ impl Plugin for ImagePlugin {
app.init_asset_loader::<HdrTextureLoader>();
}
app.add_plugins(RenderAssetPlugin::<Image>::default())
app.add_plugins(RenderAssetPlugin::<GpuImage>::default())
.register_type::<Image>()
.init_asset::<Image>()
.register_asset_reflect::<Image>();

View file

@ -11,13 +11,15 @@ use crate::{
ManualTextureViews, MipBias, TemporalJitter,
},
extract_resource::{ExtractResource, ExtractResourcePlugin},
prelude::{Image, Shader},
prelude::Shader,
primitives::Frustum,
render_asset::RenderAssets,
render_phase::ViewRangefinder3d,
render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView},
renderer::{RenderDevice, RenderQueue},
texture::{BevyDefault, CachedTexture, ColorAttachment, DepthAttachment, TextureCache},
texture::{
BevyDefault, CachedTexture, ColorAttachment, DepthAttachment, GpuImage, TextureCache,
},
Render, RenderApp, RenderSet,
};
use bevy_app::{App, Plugin};
@ -62,7 +64,7 @@ impl Plugin for ViewPlugin {
prepare_view_targets
.in_set(RenderSet::ManageViews)
.after(prepare_windows)
.after(crate::render_asset::prepare_assets::<Image>)
.after(crate::render_asset::prepare_assets::<GpuImage>)
.ambiguous_with(crate::camera::sort_cameras), // doesn't use `sorted_camera_index_for_target`
prepare_view_uniforms.in_set(RenderSet::PrepareResources),
),
@ -460,7 +462,7 @@ struct MainTargetTextures {
pub fn prepare_view_targets(
mut commands: Commands,
windows: Res<ExtractedWindows>,
images: Res<RenderAssets<Image>>,
images: Res<RenderAssets<GpuImage>>,
msaa: Res<Msaa>,
clear_color_global: Res<ClearColor>,
render_device: Res<RenderDevice>,

View file

@ -3,7 +3,7 @@ use bevy_asset::{Assets, Handle};
use bevy_math::{URect, UVec2};
use bevy_render::{
render_asset::{RenderAsset, RenderAssetUsages},
texture::{Image, TextureFormatPixelInfo},
texture::{GpuImage, Image, TextureFormatPixelInfo},
};
use guillotiere::{size2, Allocation, AtlasAllocator};
@ -56,8 +56,7 @@ impl DynamicTextureAtlasBuilder {
if let Some(allocation) = allocation {
let atlas_texture = textures.get_mut(atlas_texture_handle).unwrap();
assert!(
atlas_texture
.asset_usage()
<GpuImage as RenderAsset>::asset_usage(atlas_texture)
.contains(RenderAssetUsages::MAIN_WORLD),
"The asset at atlas_texture_handle must have the RenderAssetUsages::MAIN_WORLD usage flag set"
);

View file

@ -4,7 +4,11 @@ use bevy_asset::{load_internal_asset, Asset, AssetApp, Assets, Handle};
use bevy_color::{Color, LinearRgba};
use bevy_math::Vec4;
use bevy_reflect::prelude::*;
use bevy_render::{render_asset::RenderAssets, render_resource::*, texture::Image};
use bevy_render::{
render_asset::RenderAssets,
render_resource::*,
texture::{GpuImage, Image},
};
pub const COLOR_MATERIAL_SHADER_HANDLE: Handle<Shader> =
Handle::weak_from_u128(3253086872234592509);
@ -92,7 +96,7 @@ pub struct ColorMaterialUniform {
}
impl AsBindGroupShaderType<ColorMaterialUniform> for ColorMaterial {
fn as_bind_group_shader_type(&self, _images: &RenderAssets<Image>) -> ColorMaterialUniform {
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> ColorMaterialUniform {
let mut flags = ColorMaterialFlags::NONE;
if self.texture.is_some() {
flags |= ColorMaterialFlags::TEXTURE;

View file

@ -1,5 +1,5 @@
use bevy_app::{App, Plugin};
use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Handle};
use bevy_asset::{Asset, AssetApp, AssetId, AssetServer, Handle};
use bevy_core_pipeline::{
core_2d::Transparent2d,
tonemapping::{DebandDither, Tonemapping},
@ -12,9 +12,10 @@ use bevy_ecs::{
};
use bevy_math::FloatOrd;
use bevy_render::{
mesh::{Mesh, MeshVertexBufferLayoutRef},
prelude::Image,
render_asset::{prepare_assets, RenderAssets},
mesh::{GpuMesh, MeshVertexBufferLayoutRef},
render_asset::{
prepare_assets, PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets,
},
render_phase::{
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
SetItemPipeline, SortedRenderPhase, TrackedRenderPass,
@ -25,13 +26,12 @@ use bevy_render::{
SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines,
},
renderer::RenderDevice,
texture::FallbackImage,
texture::{FallbackImage, GpuImage},
view::{ExtractedView, InheritedVisibility, Msaa, ViewVisibility, Visibility, VisibleEntities},
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
use bevy_transform::components::{GlobalTransform, Transform};
use bevy_utils::tracing::error;
use bevy_utils::{HashMap, HashSet};
use std::hash::Hash;
use std::marker::PhantomData;
@ -148,29 +148,20 @@ where
M::Data: PartialEq + Eq + Hash + Clone,
{
fn build(&self, app: &mut App) {
app.init_asset::<M>();
app.init_asset::<M>()
.add_plugins(RenderAssetPlugin::<PreparedMaterial2d<M>>::default());
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.add_render_command::<Transparent2d, DrawMaterial2d<M>>()
.init_resource::<ExtractedMaterials2d<M>>()
.init_resource::<RenderMaterials2d<M>>()
.init_resource::<RenderMaterial2dInstances<M>>()
.init_resource::<SpecializedMeshPipelines<Material2dPipeline<M>>>()
.add_systems(
ExtractSchedule,
(extract_materials_2d::<M>, extract_material_meshes_2d::<M>),
)
.add_systems(ExtractSchedule, extract_material_meshes_2d::<M>)
.add_systems(
Render,
(
prepare_materials_2d::<M>
.in_set(RenderSet::PrepareAssets)
.after(prepare_assets::<Image>),
queue_material2d_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_materials_2d::<M>),
),
queue_material2d_meshes::<M>
.in_set(RenderSet::QueueMeshes)
.after(prepare_assets::<PreparedMaterial2d<M>>),
);
}
}
@ -330,7 +321,7 @@ impl<P: PhaseItem, M: Material2d, const I: usize> RenderCommand<P>
for SetMaterial2dBindGroup<M, I>
{
type Param = (
SRes<RenderMaterials2d<M>>,
SRes<RenderAssets<PreparedMaterial2d<M>>>,
SRes<RenderMaterial2dInstances<M>>,
);
type ViewQuery = ();
@ -349,7 +340,7 @@ impl<P: PhaseItem, M: Material2d, const I: usize> RenderCommand<P>
let Some(material_instance) = material_instances.get(&item.entity()) else {
return RenderCommandResult::Failure;
};
let Some(material2d) = materials.get(material_instance) else {
let Some(material2d) = materials.get(*material_instance) else {
return RenderCommandResult::Failure;
};
pass.set_bind_group(I, &material2d.bind_group, &[]);
@ -379,8 +370,8 @@ pub fn queue_material2d_meshes<M: Material2d>(
mut pipelines: ResMut<SpecializedMeshPipelines<Material2dPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
render_materials: Res<RenderMaterials2d<M>>,
render_meshes: Res<RenderAssets<GpuMesh>>,
render_materials: Res<RenderAssets<PreparedMaterial2d<M>>>,
mut render_mesh_instances: ResMut<RenderMesh2dInstances>,
render_material_instances: Res<RenderMaterial2dInstances<M>>,
mut views: Query<(
@ -419,7 +410,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
let Some(mesh_instance) = render_mesh_instances.get_mut(visible_entity) else {
continue;
};
let Some(material2d) = render_materials.get(material_asset_id) else {
let Some(material2d) = render_materials.get(*material_asset_id) else {
continue;
};
let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else {
@ -483,157 +474,37 @@ impl<T: Material2d> PreparedMaterial2d<T> {
}
}
#[derive(Resource)]
pub struct ExtractedMaterials2d<M: Material2d> {
extracted: Vec<(AssetId<M>, M)>,
removed: Vec<AssetId<M>>,
}
impl<M: Material2d> RenderAsset for PreparedMaterial2d<M> {
type SourceAsset = M;
impl<M: Material2d> Default for ExtractedMaterials2d<M> {
fn default() -> Self {
Self {
extracted: Default::default(),
removed: Default::default(),
}
}
}
type Param = (
SRes<RenderDevice>,
SRes<RenderAssets<GpuImage>>,
SRes<FallbackImage>,
SRes<Material2dPipeline<M>>,
);
/// Stores all prepared representations of [`Material2d`] assets for as long as they exist.
#[derive(Resource, Deref, DerefMut)]
pub struct RenderMaterials2d<T: Material2d>(HashMap<AssetId<T>, PreparedMaterial2d<T>>);
impl<T: Material2d> Default for RenderMaterials2d<T> {
fn default() -> Self {
Self(Default::default())
}
}
/// This system extracts all created or modified assets of the corresponding [`Material2d`] type
/// into the "render world".
pub fn extract_materials_2d<M: Material2d>(
mut commands: Commands,
mut events: Extract<EventReader<AssetEvent<M>>>,
assets: Extract<Res<Assets<M>>>,
) {
let mut changed_assets = HashSet::default();
let mut removed = Vec::new();
for event in events.read() {
#[allow(clippy::match_same_arms)]
match event {
AssetEvent::Added { id } | AssetEvent::Modified { id } => {
changed_assets.insert(*id);
}
AssetEvent::Removed { id } => {
changed_assets.remove(id);
removed.push(*id);
}
AssetEvent::Unused { .. } => {}
AssetEvent::LoadedWithDependencies { .. } => {
// TODO: handle this
}
}
}
let mut extracted_assets = Vec::new();
for id in changed_assets.drain() {
if let Some(asset) = assets.get(id) {
extracted_assets.push((id, asset.clone()));
}
}
commands.insert_resource(ExtractedMaterials2d {
extracted: extracted_assets,
removed,
});
}
/// All [`Material2d`] values of a given type that should be prepared next frame.
pub struct PrepareNextFrameMaterials<M: Material2d> {
assets: Vec<(AssetId<M>, M)>,
}
impl<M: Material2d> Default for PrepareNextFrameMaterials<M> {
fn default() -> Self {
Self {
assets: Default::default(),
}
}
}
/// This system prepares all assets of the corresponding [`Material2d`] type
/// which where extracted this frame for the GPU.
pub fn prepare_materials_2d<M: Material2d>(
mut prepare_next_frame: Local<PrepareNextFrameMaterials<M>>,
mut extracted_assets: ResMut<ExtractedMaterials2d<M>>,
mut render_materials: ResMut<RenderMaterials2d<M>>,
render_device: Res<RenderDevice>,
images: Res<RenderAssets<Image>>,
fallback_image: Res<FallbackImage>,
pipeline: Res<Material2dPipeline<M>>,
) {
let queued_assets = std::mem::take(&mut prepare_next_frame.assets);
for (id, material) in queued_assets {
if extracted_assets.removed.contains(&id) {
continue;
}
match prepare_material2d(
&material,
&render_device,
&images,
&fallback_image,
&pipeline,
fn prepare_asset(
material: Self::SourceAsset,
(render_device, images, fallback_image, pipeline): &mut SystemParamItem<Self::Param>,
) -> Result<Self, bevy_render::render_asset::PrepareAssetError<Self::SourceAsset>> {
match material.as_bind_group(
&pipeline.material2d_layout,
render_device,
images,
fallback_image,
) {
Ok(prepared_asset) => {
render_materials.insert(id, prepared_asset);
}
Ok(prepared) => Ok(PreparedMaterial2d {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
depth_bias: material.depth_bias(),
}),
Err(AsBindGroupError::RetryNextUpdate) => {
prepare_next_frame.assets.push((id, material));
Err(PrepareAssetError::RetryNextUpdate(material))
}
}
}
for removed in std::mem::take(&mut extracted_assets.removed) {
render_materials.remove(&removed);
}
for (asset_id, material) in std::mem::take(&mut extracted_assets.extracted) {
match prepare_material2d(
&material,
&render_device,
&images,
&fallback_image,
&pipeline,
) {
Ok(prepared_asset) => {
render_materials.insert(asset_id, prepared_asset);
}
Err(AsBindGroupError::RetryNextUpdate) => {
prepare_next_frame.assets.push((asset_id, material));
}
}
}
}
fn prepare_material2d<M: Material2d>(
material: &M,
render_device: &RenderDevice,
images: &RenderAssets<Image>,
fallback_image: &FallbackImage,
pipeline: &Material2dPipeline<M>,
) -> Result<PreparedMaterial2d<M>, AsBindGroupError> {
let prepared = material.as_bind_group(
&pipeline.material2d_layout,
render_device,
images,
fallback_image,
)?;
Ok(PreparedMaterial2d {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
depth_bias: material.depth_bias(),
})
}
/// A component bundle for entities with a [`Mesh2dHandle`] and a [`Material2d`].

View file

@ -11,7 +11,7 @@ use bevy_ecs::{
};
use bevy_math::{Affine3, Vec4};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::mesh::MeshVertexBufferLayoutRef;
use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef};
use bevy_render::{
batching::{
batch_and_prepare_sorted_render_phase, write_batched_instance_buffer, GetBatchData,
@ -323,7 +323,7 @@ impl FromWorld for Mesh2dPipeline {
impl Mesh2dPipeline {
pub fn get_image_texture<'a>(
&'a self,
gpu_images: &'a RenderAssets<Image>,
gpu_images: &'a RenderAssets<GpuImage>,
handle_option: &Option<Handle<Image>>,
) -> Option<(&'a TextureView, &'a Sampler)> {
if let Some(handle) = handle_option {
@ -666,7 +666,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMesh2dBindGroup<I> {
pub struct DrawMesh2d;
impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
type Param = (SRes<RenderAssets<Mesh>>, SRes<RenderMesh2dInstances>);
type Param = (SRes<RenderAssets<GpuMesh>>, SRes<RenderMesh2dInstances>);
type ViewQuery = ();
type ItemQuery = ();

View file

@ -528,7 +528,7 @@ pub fn prepare_sprites(
view_uniforms: Res<ViewUniforms>,
sprite_pipeline: Res<SpritePipeline>,
mut image_bind_groups: ResMut<ImageBindGroups>,
gpu_images: Res<RenderAssets<Image>>,
gpu_images: Res<RenderAssets<GpuImage>>,
extracted_sprites: Res<ExtractedSprites>,
mut phases: Query<&mut SortedRenderPhase<Transparent2d>>,
events: Res<SpriteAssetEvents>,

View file

@ -7,6 +7,7 @@ use bevy_core_pipeline::core_2d::graph::{Core2d, Node2d};
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
use bevy_hierarchy::Parent;
use bevy_render::texture::GpuImage;
use bevy_render::{render_phase::PhaseItem, view::ViewVisibility, ExtractSchedule, Render};
use bevy_sprite::{SpriteAssetEvents, TextureAtlas};
pub use pipeline::*;
@ -920,7 +921,7 @@ pub fn prepare_uinodes(
view_uniforms: Res<ViewUniforms>,
ui_pipeline: Res<UiPipeline>,
mut image_bind_groups: ResMut<UiImageBindGroups>,
gpu_images: Res<RenderAssets<Image>>,
gpu_images: Res<RenderAssets<GpuImage>>,
mut phases: Query<&mut SortedRenderPhase<TransparentUi>>,
events: Res<SpriteAssetEvents>,
mut previous_len: Local<usize>,

View file

@ -1,7 +1,6 @@
use std::{hash::Hash, marker::PhantomData, ops::Range};
use bevy_asset::*;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
prelude::Component,
query::ROQueryItem,
@ -13,16 +12,15 @@ use bevy_math::{FloatOrd, Mat4, Rect, Vec2, Vec4Swizzles};
use bevy_render::{
extract_component::ExtractComponentPlugin,
globals::{GlobalsBuffer, GlobalsUniform},
render_asset::RenderAssets,
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_phase::*,
render_resource::{binding_types::uniform_buffer, *},
renderer::{RenderDevice, RenderQueue},
texture::{BevyDefault, FallbackImage, Image},
texture::{BevyDefault, FallbackImage, GpuImage},
view::*,
Extract, ExtractSchedule, Render, RenderSet,
};
use bevy_transform::prelude::GlobalTransform;
use bevy_utils::{HashMap, HashSet};
use bevy_window::{PrimaryWindow, Window};
use bytemuck::{Pod, Zeroable};
@ -59,28 +57,24 @@ where
"ui_material.wgsl",
Shader::from_wgsl
);
app.init_asset::<M>()
.add_plugins(ExtractComponentPlugin::<Handle<M>>::extract_visible());
app.init_asset::<M>().add_plugins((
ExtractComponentPlugin::<Handle<M>>::extract_visible(),
RenderAssetPlugin::<PreparedUiMaterial<M>>::default(),
));
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.add_render_command::<TransparentUi, DrawUiMaterial<M>>()
.init_resource::<ExtractedUiMaterials<M>>()
.init_resource::<ExtractedUiMaterialNodes<M>>()
.init_resource::<RenderUiMaterials<M>>()
.init_resource::<UiMaterialMeta<M>>()
.init_resource::<SpecializedRenderPipelines<UiMaterialPipeline<M>>>()
.add_systems(
ExtractSchedule,
(
extract_ui_materials::<M>,
extract_ui_material_nodes::<M>.in_set(RenderUiSystem::ExtractBackgrounds),
),
extract_ui_material_nodes::<M>.in_set(RenderUiSystem::ExtractBackgrounds),
)
.add_systems(
Render,
(
prepare_ui_materials::<M>.in_set(RenderSet::PrepareAssets),
queue_ui_material_nodes::<M>.in_set(RenderSet::Queue),
prepare_uimaterial_nodes::<M>.in_set(RenderSet::PrepareBindGroups),
),
@ -286,7 +280,7 @@ pub struct SetUiMaterialBindGroup<M: UiMaterial, const I: usize>(PhantomData<M>)
impl<P: PhaseItem, M: UiMaterial, const I: usize> RenderCommand<P>
for SetUiMaterialBindGroup<M, I>
{
type Param = SRes<RenderUiMaterials<M>>;
type Param = SRes<RenderAssets<PreparedUiMaterial<M>>>;
type ViewQuery = ();
type ItemQuery = Read<UiMaterialBatch<M>>;
@ -300,7 +294,7 @@ impl<P: PhaseItem, M: UiMaterial, const I: usize> RenderCommand<P>
let Some(material_handle) = material_handle else {
return RenderCommandResult::Failure;
};
let Some(material) = materials.into_inner().get(&material_handle.material) else {
let Some(material) = materials.into_inner().get(material_handle.material) else {
return RenderCommandResult::Failure;
};
pass.set_bind_group(I, &material.bind_group, &[]);
@ -601,152 +595,37 @@ pub fn prepare_uimaterial_nodes<M: UiMaterial>(
extracted_uinodes.uinodes.clear();
}
#[derive(Resource, Deref, DerefMut)]
pub struct RenderUiMaterials<T: UiMaterial>(HashMap<AssetId<T>, PreparedUiMaterial<T>>);
impl<T: UiMaterial> Default for RenderUiMaterials<T> {
fn default() -> Self {
Self(Default::default())
}
}
pub struct PreparedUiMaterial<T: UiMaterial> {
pub bindings: Vec<(u32, OwnedBindingResource)>,
pub bind_group: BindGroup,
pub key: T::Data,
}
#[derive(Resource)]
pub struct ExtractedUiMaterials<M: UiMaterial> {
extracted: Vec<(AssetId<M>, M)>,
removed: Vec<AssetId<M>>,
}
impl<M: UiMaterial> RenderAsset for PreparedUiMaterial<M> {
type SourceAsset = M;
impl<M: UiMaterial> Default for ExtractedUiMaterials<M> {
fn default() -> Self {
Self {
extracted: Default::default(),
removed: Default::default(),
}
}
}
type Param = (
SRes<RenderDevice>,
SRes<RenderAssets<GpuImage>>,
SRes<FallbackImage>,
SRes<UiMaterialPipeline<M>>,
);
pub fn extract_ui_materials<M: UiMaterial>(
mut commands: Commands,
mut events: Extract<EventReader<AssetEvent<M>>>,
assets: Extract<Res<Assets<M>>>,
) {
let mut changed_assets = HashSet::default();
let mut removed = Vec::new();
for event in events.read() {
#[allow(clippy::match_same_arms)]
match event {
AssetEvent::Added { id } | AssetEvent::Modified { id } => {
changed_assets.insert(*id);
}
AssetEvent::Removed { id } => {
changed_assets.remove(id);
removed.push(*id);
}
AssetEvent::Unused { .. } => {}
AssetEvent::LoadedWithDependencies { .. } => {
// TODO: handle this
}
}
}
let mut extracted_assets = Vec::new();
for id in changed_assets.drain() {
if let Some(asset) = assets.get(id) {
extracted_assets.push((id, asset.clone()));
}
}
commands.insert_resource(ExtractedUiMaterials {
extracted: extracted_assets,
removed,
});
}
pub struct PrepareNextFrameMaterials<M: UiMaterial> {
assets: Vec<(AssetId<M>, M)>,
}
impl<M: UiMaterial> Default for PrepareNextFrameMaterials<M> {
fn default() -> Self {
Self {
assets: Default::default(),
}
}
}
pub fn prepare_ui_materials<M: UiMaterial>(
mut prepare_next_frame: Local<PrepareNextFrameMaterials<M>>,
mut extracted_assets: ResMut<ExtractedUiMaterials<M>>,
mut render_materials: ResMut<RenderUiMaterials<M>>,
render_device: Res<RenderDevice>,
images: Res<RenderAssets<Image>>,
fallback_image: Res<FallbackImage>,
pipeline: Res<UiMaterialPipeline<M>>,
) {
let queued_assets = std::mem::take(&mut prepare_next_frame.assets);
for (id, material) in queued_assets {
if extracted_assets.removed.contains(&id) {
continue;
}
match prepare_ui_material(
&material,
&render_device,
&images,
&fallback_image,
&pipeline,
) {
Ok(prepared_asset) => {
render_materials.insert(id, prepared_asset);
}
fn prepare_asset(
material: Self::SourceAsset,
(render_device, images, fallback_image, pipeline): &mut SystemParamItem<Self::Param>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
match material.as_bind_group(&pipeline.ui_layout, render_device, images, fallback_image) {
Ok(prepared) => Ok(PreparedUiMaterial {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
}),
Err(AsBindGroupError::RetryNextUpdate) => {
prepare_next_frame.assets.push((id, material));
Err(PrepareAssetError::RetryNextUpdate(material))
}
}
}
for removed in std::mem::take(&mut extracted_assets.removed) {
render_materials.remove(&removed);
}
for (handle, material) in std::mem::take(&mut extracted_assets.extracted) {
match prepare_ui_material(
&material,
&render_device,
&images,
&fallback_image,
&pipeline,
) {
Ok(prepared_asset) => {
render_materials.insert(handle, prepared_asset);
}
Err(AsBindGroupError::RetryNextUpdate) => {
prepare_next_frame.assets.push((handle, material));
}
}
}
}
fn prepare_ui_material<M: UiMaterial>(
material: &M,
render_device: &RenderDevice,
images: &RenderAssets<Image>,
fallback_image: &Res<FallbackImage>,
pipeline: &UiMaterialPipeline<M>,
) -> Result<PreparedUiMaterial<M>, AsBindGroupError> {
let prepared =
material.as_bind_group(&pipeline.ui_layout, render_device, images, fallback_image)?;
Ok(PreparedUiMaterial {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
})
}
#[allow(clippy::too_many_arguments)]
@ -756,7 +635,7 @@ pub fn queue_ui_material_nodes<M: UiMaterial>(
ui_material_pipeline: Res<UiMaterialPipeline<M>>,
mut pipelines: ResMut<SpecializedRenderPipelines<UiMaterialPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
render_materials: Res<RenderUiMaterials<M>>,
render_materials: Res<RenderAssets<PreparedUiMaterial<M>>>,
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<TransparentUi>)>,
) where
M::Data: PartialEq + Eq + Hash + Clone,
@ -764,7 +643,7 @@ pub fn queue_ui_material_nodes<M: UiMaterial>(
let draw_function = draw_functions.read().id::<DrawUiMaterial<M>>();
for (entity, extracted_uinode) in extracted_uinodes.uinodes.iter() {
let Some(material) = render_materials.get(&extracted_uinode.material) else {
let Some(material) = render_materials.get(extracted_uinode.material) else {
continue;
};
let Ok((view, mut transparent_phase)) = views.get_mut(extracted_uinode.camera_entity)

View file

@ -11,7 +11,7 @@ use bevy::{
math::FloatOrd,
prelude::*,
render::{
mesh::{Indices, MeshVertexAttribute},
mesh::{GpuMesh, Indices, MeshVertexAttribute},
render_asset::{RenderAssetUsages, RenderAssets},
render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase},
render_resource::{
@ -356,7 +356,7 @@ pub fn queue_colored_mesh2d(
mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
render_meshes: Res<RenderAssets<GpuMesh>>,
render_mesh_instances: Res<RenderMesh2dInstances>,
mut views: Query<(
&VisibleEntities,

View file

@ -11,6 +11,7 @@ use bevy::{
render_graph::{self, RenderGraph, RenderLabel},
render_resource::{binding_types::texture_storage_2d, *},
renderer::{RenderContext, RenderDevice},
texture::GpuImage,
Render, RenderApp, RenderSet,
},
};
@ -129,7 +130,7 @@ struct GameOfLifeImageBindGroups([BindGroup; 2]);
fn prepare_bind_group(
mut commands: Commands,
pipeline: Res<GameOfLifePipeline>,
gpu_images: Res<RenderAssets<Image>>,
gpu_images: Res<RenderAssets<GpuImage>>,
game_of_life_images: Res<GameOfLifeImages>,
render_device: Res<RenderDevice>,
) {

View file

@ -12,7 +12,7 @@ use bevy::{
prelude::*,
render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
mesh::{GpuBufferInfo, MeshVertexBufferLayoutRef},
mesh::{GpuBufferInfo, GpuMesh, MeshVertexBufferLayoutRef},
render_asset::RenderAssets,
render_phase::{
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
@ -114,7 +114,7 @@ fn queue_custom(
msaa: Res<Msaa>,
mut pipelines: ResMut<SpecializedMeshPipelines<CustomPipeline>>,
pipeline_cache: Res<PipelineCache>,
meshes: Res<RenderAssets<Mesh>>,
meshes: Res<RenderAssets<GpuMesh>>,
render_mesh_instances: Res<RenderMeshInstances>,
material_meshes: Query<Entity, With<InstanceMaterialData>>,
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<Transparent3d>)>,
@ -234,7 +234,7 @@ type DrawCustom = (
struct DrawMeshInstanced;
impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
type Param = (SRes<RenderAssets<Mesh>>, SRes<RenderMeshInstances>);
type Param = (SRes<RenderAssets<GpuMesh>>, SRes<RenderMeshInstances>);
type ViewQuery = ();
type ItemQuery = Read<InstanceBuffer>;

View file

@ -5,8 +5,11 @@ use bevy::{
prelude::*,
reflect::TypePath,
render::{
render_asset::RenderAssets, render_resource::*, renderer::RenderDevice,
texture::FallbackImage, RenderApp,
render_asset::RenderAssets,
render_resource::*,
renderer::RenderDevice,
texture::{FallbackImage, GpuImage},
RenderApp,
},
};
use std::{num::NonZeroU32, process::exit};
@ -92,7 +95,7 @@ impl AsBindGroup for BindlessMaterial {
&self,
layout: &BindGroupLayout,
render_device: &RenderDevice,
image_assets: &RenderAssets<Image>,
image_assets: &RenderAssets<GpuImage>,
fallback_image: &FallbackImage,
) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> {
// retrieve the render resources from handles
@ -133,7 +136,7 @@ impl AsBindGroup for BindlessMaterial {
&self,
_: &BindGroupLayout,
_: &RenderDevice,
_: &RenderAssets<Image>,
_: &RenderAssets<GpuImage>,
_: &FallbackImage,
) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> {
// we implement as_bind_group directly because