Port Mesh to RenderAsset, add Slab and FrameSlabMap garbage collection for Bind Groups

This commit is contained in:
Carter Anderson 2021-06-26 15:35:07 -07:00
parent 3ef951dcbc
commit 25de2d1819
16 changed files with 591 additions and 418 deletions

View File

@ -1,7 +1,8 @@
mod enum_variant_meta; mod enum_variant_meta;
pub use enum_variant_meta::*; pub mod slab;
pub use ahash::AHasher; pub use ahash::AHasher;
pub use enum_variant_meta::*;
pub use instant::{Duration, Instant}; pub use instant::{Duration, Instant};
pub use tracing; pub use tracing;
pub use uuid::Uuid; pub use uuid::Uuid;

View File

@ -0,0 +1,214 @@
use std::{
marker::PhantomData,
ops::{Index, IndexMut},
};
use crate::HashMap;
#[derive(Debug)]
pub struct SlabKey<V> {
index: usize,
marker: PhantomData<V>,
}
impl<V> Copy for SlabKey<V> {}
impl<V> Clone for SlabKey<V> {
fn clone(&self) -> Self {
Self {
index: self.index,
marker: PhantomData,
}
}
}
impl<V> SlabKey<V> {
#[inline]
pub fn index(&self) -> usize {
self.index
}
}
pub struct Slab<V> {
values: Vec<Option<V>>,
empty_indices: Vec<usize>,
}
impl<V> Default for Slab<V> {
fn default() -> Self {
Self {
values: Default::default(),
empty_indices: Default::default(),
}
}
}
impl<V> Slab<V> {
pub fn get(&self, key: SlabKey<V>) -> Option<&V> {
self.values[key.index].as_ref()
}
pub fn get_mut(&mut self, key: SlabKey<V>) -> Option<&mut V> {
self.values[key.index].as_mut()
}
pub fn add(&mut self, value: V) -> SlabKey<V> {
let index = if let Some(index) = self.empty_indices.pop() {
self.values[index] = Some(value);
index
} else {
let index = self.values.len();
self.values.push(Some(value));
index
};
SlabKey {
index,
marker: PhantomData,
}
}
pub fn remove(&mut self, key: SlabKey<V>) -> Option<V> {
if let Some(value) = self.values[key.index].take() {
self.empty_indices.push(key.index);
Some(value)
} else {
None
}
}
pub fn iter(&self) -> impl Iterator<Item = &V> {
self.values.iter().filter_map(|v| v.as_ref())
}
/// Retains any items matching the given predicate. Removed items will be dropped and their indices will be
/// made available for future items.
pub fn retain_in_place(&mut self, mut predicate: impl FnMut(&mut V) -> bool) {
for (i, value) in self.values.iter_mut().enumerate() {
if let Some(value) = value {
if predicate(value) {
continue;
}
} else {
continue;
}
*value = None;
self.empty_indices.push(i);
}
}
}
impl<V> Index<SlabKey<V>> for Slab<V> {
type Output = V;
#[inline]
fn index(&self, index: SlabKey<V>) -> &Self::Output {
self.get(index).unwrap()
}
}
impl<V> IndexMut<SlabKey<V>> for Slab<V> {
#[inline]
fn index_mut(&mut self, index: SlabKey<V>) -> &mut Self::Output {
self.get_mut(index).unwrap()
}
}
pub struct FrameSlabMapValue<K, V> {
value: V,
key: K,
frames_since_last_use: usize,
}
pub struct FrameSlabMap<K, V> {
slab: Slab<FrameSlabMapValue<K, V>>,
keys: HashMap<K, FrameSlabMapKey<K, V>>,
}
impl<K, V> Default for FrameSlabMap<K, V> {
fn default() -> Self {
Self {
slab: Default::default(),
keys: Default::default(),
}
}
}
pub type FrameSlabMapKey<K, V> = SlabKey<FrameSlabMapValue<K, V>>;
impl<K: std::hash::Hash + Eq + Clone, V> FrameSlabMap<K, V> {
pub fn get_value(&self, slab_key: FrameSlabMapKey<K, V>) -> Option<&V> {
let value = self.slab.get(slab_key)?;
Some(&value.value)
}
pub fn get_value_mut(&mut self, slab_key: FrameSlabMapKey<K, V>) -> Option<&mut V> {
let value = self.slab.get_mut(slab_key)?;
Some(&mut value.value)
}
pub fn get_or_insert_with(
&mut self,
key: K,
f: impl FnOnce() -> V,
) -> SlabKey<FrameSlabMapValue<K, V>> {
match self.keys.entry(key.clone()) {
std::collections::hash_map::Entry::Occupied(mut entry) => {
let slab_key = *entry.get();
match self.slab.get_mut(slab_key) {
Some(value) => {
value.frames_since_last_use = 0;
slab_key
}
None => {
let key = self.slab.add(FrameSlabMapValue {
frames_since_last_use: 0,
value: f(),
key,
});
entry.insert(key);
key
}
}
}
std::collections::hash_map::Entry::Vacant(entry) => {
let key = self.slab.add(FrameSlabMapValue {
frames_since_last_use: 0,
value: f(),
key,
});
entry.insert(key);
key
}
}
}
pub fn next_frame(&mut self) {
let keys = &mut self.keys;
self.slab.retain_in_place(|v| {
v.frames_since_last_use += 1;
if v.frames_since_last_use < 3 {
true
} else {
keys.remove(&v.key);
false
}
})
}
}
impl<K: std::hash::Hash + Eq + Clone, V> Index<FrameSlabMapKey<K, V>> for FrameSlabMap<K, V> {
type Output = V;
#[inline]
fn index(&self, index: FrameSlabMapKey<K, V>) -> &Self::Output {
self.get_value(index).unwrap()
}
}
impl<K: std::hash::Hash + Eq + Clone, V> IndexMut<FrameSlabMapKey<K, V>> for FrameSlabMap<K, V> {
#[inline]
fn index_mut(&mut self, index: FrameSlabMapKey<K, V>) -> &mut Self::Output {
self.get_value_mut(index).unwrap()
}
}

View File

@ -242,7 +242,7 @@ impl Default for CountTimer {
fn counter_system( fn counter_system(
mut timer: Local<CountTimer>, mut timer: Local<CountTimer>,
diagnostics: Res<Diagnostics>, _diagnostics: Res<Diagnostics>,
time: Res<Time>, time: Res<Time>,
counter: Res<BevyCounter>, counter: Res<BevyCounter>,
) { ) {
@ -255,7 +255,7 @@ fn counter_system(
/// ///
/// Because there is no `Mul<Color> for Color` instead `[f32; 3]` is /// Because there is no `Mul<Color> for Color` instead `[f32; 3]` is
/// used. /// used.
fn gen_color(rng: &mut impl Rng) -> [f32; 3] { fn _gen_color(rng: &mut impl Rng) -> [f32; 3] {
let r = rng.gen_range(0.2..1.0); let r = rng.gen_range(0.2..1.0);
let g = rng.gen_range(0.2..1.0); let g = rng.gen_range(0.2..1.0);
let b = rng.gen_range(0.2..1.0); let b = rng.gen_range(0.2..1.0);

View File

@ -48,7 +48,6 @@ impl Plugin for PbrPlugin {
) )
.init_resource::<PbrShaders>() .init_resource::<PbrShaders>()
.init_resource::<ShadowShaders>() .init_resource::<ShadowShaders>()
.init_resource::<MaterialMeta>()
.init_resource::<MeshMeta>() .init_resource::<MeshMeta>()
.init_resource::<LightMeta>(); .init_resource::<LightMeta>();

View File

@ -1,9 +1,13 @@
use bevy_app::{App, CoreStage, EventReader, Plugin}; use bevy_app::{App, CoreStage, EventReader, Plugin};
use bevy_asset::{AddAsset, AssetEvent, Assets, Handle}; use bevy_asset::{AddAsset, AssetEvent, Assets};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::Vec4; use bevy_math::Vec4;
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_render2::{color::Color, render_resource::{Buffer, BufferId, BufferInitDescriptor, BufferUsage}, renderer::{RenderDevice, RenderQueue}}; use bevy_render2::{
color::Color,
render_resource::{Buffer, BufferInitDescriptor, BufferUsage},
renderer::RenderDevice,
};
use bevy_utils::HashSet; use bevy_utils::HashSet;
use crevice::std140::{AsStd140, Std140}; use crevice::std140::{AsStd140, Std140};
@ -103,7 +107,6 @@ impl Plugin for StandardMaterialPlugin {
pub fn standard_material_resource_system( pub fn standard_material_resource_system(
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
mut material_events: EventReader<AssetEvent<StandardMaterial>>, mut material_events: EventReader<AssetEvent<StandardMaterial>>,
) { ) {
@ -144,8 +147,6 @@ pub fn standard_material_resource_system(
}; };
let value_std140 = value.as_std140(); let value_std140 = value.as_std140();
let size = StandardMaterialUniformData::std140_size_static();
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
label: None, label: None,
usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST, usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,

View File

@ -4,6 +4,8 @@ use bevy_math::{Mat4, Vec3, Vec4};
use bevy_render2::{ use bevy_render2::{
color::Color, color::Color,
core_pipeline::Transparent3dPhase, core_pipeline::Transparent3dPhase,
mesh::Mesh,
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass}, render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass},
render_resource::*, render_resource::*,
@ -82,10 +84,7 @@ impl FromWorld for ShadowShaders {
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor { let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None, label: None,
push_constant_ranges: &[], push_constant_ranges: &[],
bind_group_layouts: &[ bind_group_layouts: &[&view_layout, &pbr_shaders.mesh_layout],
&view_layout,
&pbr_shaders.mesh_layout,
],
}); });
let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor { let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
@ -378,6 +377,7 @@ type DrawShadowMeshParams<'s, 'w> = (
Res<'w, ExtractedMeshes>, Res<'w, ExtractedMeshes>,
Res<'w, LightMeta>, Res<'w, LightMeta>,
Res<'w, MeshMeta>, Res<'w, MeshMeta>,
Res<'w, RenderAssets<Mesh>>,
Query<'w, 's, &'w ViewUniformOffset>, Query<'w, 's, &'w ViewUniformOffset>,
); );
pub struct DrawShadowMesh { pub struct DrawShadowMesh {
@ -401,7 +401,7 @@ impl Draw for DrawShadowMesh {
draw_key: usize, draw_key: usize,
_sort_key: usize, _sort_key: usize,
) { ) {
let (shadow_shaders, extracted_meshes, light_meta, mesh_meta, views) = let (shadow_shaders, extracted_meshes, light_meta, mesh_meta, meshes, views) =
self.params.get(world); self.params.get(world);
let view_uniform_offset = views.get(view).unwrap(); let view_uniform_offset = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key]; let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
@ -425,8 +425,10 @@ impl Draw for DrawShadowMesh {
.unwrap(), .unwrap(),
&[extracted_mesh.transform_binding_offset], &[extracted_mesh.transform_binding_offset],
); );
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &extracted_mesh.index_info { let gpu_mesh = meshes.into_inner().get(&extracted_mesh.mesh).unwrap();
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &gpu_mesh.index_info {
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32); pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
pass.draw_indexed(0..index_info.count, 0, 0..1); pass.draw_indexed(0..index_info.count, 0, 0..1);
} else { } else {

View File

@ -7,6 +7,7 @@ use bevy_math::Mat4;
use bevy_render2::{ use bevy_render2::{
core_pipeline::Transparent3dPhase, core_pipeline::Transparent3dPhase,
mesh::Mesh, mesh::Mesh,
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext}, render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass}, render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
render_resource::*, render_resource::*,
@ -16,7 +17,7 @@ use bevy_render2::{
view::{ViewMeta, ViewUniform, ViewUniformOffset}, view::{ViewMeta, ViewUniform, ViewUniformOffset},
}; };
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap; use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
use crevice::std140::AsStd140; use crevice::std140::AsStd140;
use std::borrow::Cow; use std::borrow::Cow;
@ -233,15 +234,9 @@ impl FromWorld for PbrShaders {
struct ExtractedMesh { struct ExtractedMesh {
transform: Mat4, transform: Mat4,
vertex_buffer: Buffer, mesh: Handle<Mesh>,
index_info: Option<IndexInfo>,
transform_binding_offset: u32,
material_buffer: Buffer, material_buffer: Buffer,
} transform_binding_offset: u32,
struct IndexInfo {
buffer: Buffer,
count: u32,
} }
pub struct ExtractedMeshes { pub struct ExtractedMeshes {
@ -256,22 +251,17 @@ pub fn extract_meshes(
) { ) {
let mut extracted_meshes = Vec::new(); let mut extracted_meshes = Vec::new();
for (transform, mesh_handle, material_handle) in query.iter() { for (transform, mesh_handle, material_handle) in query.iter() {
if let Some(mesh) = meshes.get(mesh_handle) { if !meshes.contains(mesh_handle) {
if let Some(mesh_gpu_data) = &mesh.gpu_data() { continue;
if let Some(material) = materials.get(material_handle) { }
if let Some(material_gpu_data) = &material.gpu_data() { if let Some(material) = materials.get(material_handle) {
extracted_meshes.push(ExtractedMesh { if let Some(material_gpu_data) = &material.gpu_data() {
transform: transform.compute_matrix(), extracted_meshes.push(ExtractedMesh {
vertex_buffer: mesh_gpu_data.vertex_buffer.clone(), transform: transform.compute_matrix(),
index_info: mesh_gpu_data.index_buffer.as_ref().map(|i| IndexInfo { mesh: mesh_handle.clone_weak(),
buffer: i.clone(), material_buffer: material_gpu_data.buffer.clone(),
count: mesh.indices().unwrap().len() as u32, transform_binding_offset: 0,
}), });
transform_binding_offset: 0,
material_buffer: material_gpu_data.buffer.clone(),
});
}
}
} }
} }
} }
@ -281,10 +271,17 @@ pub fn extract_meshes(
}); });
} }
struct MeshDrawInfo {
// TODO: compare cost of doing this vs cloning the BindGroup?
material_bind_group_key: FrameSlabMapKey<BufferId, BindGroup>,
}
#[derive(Default)] #[derive(Default)]
pub struct MeshMeta { pub struct MeshMeta {
transform_uniforms: DynamicUniformVec<Mat4>, transform_uniforms: DynamicUniformVec<Mat4>,
material_bind_groups: FrameSlabMap<BufferId, BindGroup>,
mesh_transform_bind_group: Option<BindGroup>, mesh_transform_bind_group: Option<BindGroup>,
mesh_draw_info: Vec<MeshDrawInfo>,
} }
pub fn prepare_meshes( pub fn prepare_meshes(
@ -304,11 +301,6 @@ pub fn prepare_meshes(
.transform_uniforms .transform_uniforms
.write_to_staging_buffer(&render_device); .write_to_staging_buffer(&render_device);
} }
#[derive(Default)]
pub struct MaterialMeta {
material_bind_groups: Vec<BindGroup>,
material_bind_group_indices: HashMap<BufferId, usize>,
}
pub struct MeshViewBindGroups { pub struct MeshViewBindGroups {
view: BindGroup, view: BindGroup,
@ -321,7 +313,6 @@ pub fn queue_meshes(
pbr_shaders: Res<PbrShaders>, pbr_shaders: Res<PbrShaders>,
shadow_shaders: Res<ShadowShaders>, shadow_shaders: Res<ShadowShaders>,
mesh_meta: ResMut<MeshMeta>, mesh_meta: ResMut<MeshMeta>,
material_meta: ResMut<MaterialMeta>,
mut light_meta: ResMut<LightMeta>, mut light_meta: ResMut<LightMeta>,
view_meta: Res<ViewMeta>, view_meta: Res<ViewMeta>,
mut extracted_meshes: ResMut<ExtractedMeshes>, mut extracted_meshes: ResMut<ExtractedMeshes>,
@ -329,7 +320,6 @@ pub fn queue_meshes(
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>, mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
) { ) {
let mesh_meta = mesh_meta.into_inner(); let mesh_meta = mesh_meta.into_inner();
let material_meta = material_meta.into_inner();
light_meta.shadow_view_bind_group.get_or_insert_with(|| { light_meta.shadow_view_bind_group.get_or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor { render_device.create_bind_group(&BindGroupDescriptor {
@ -385,34 +375,34 @@ pub fn queue_meshes(
view: view_bind_group, view: view_bind_group,
}); });
// TODO: free old bind groups after a few frames without use?
let draw_pbr = draw_functions.read().get_id::<DrawPbr>().unwrap(); let draw_pbr = draw_functions.read().get_id::<DrawPbr>().unwrap();
let material_bind_groups = &mut material_meta.material_bind_groups; mesh_meta.mesh_draw_info.clear();
mesh_meta.material_bind_groups.next_frame();
for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() { for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() {
let material_bind_group_index = *material_meta let material_bind_group_key = mesh_meta.material_bind_groups.get_or_insert_with(
.material_bind_group_indices mesh.material_buffer.id(),
.entry(mesh.material_buffer.id()) || {
.or_insert_with(|| { render_device.create_bind_group(&BindGroupDescriptor {
let index = material_bind_groups.len(); entries: &[BindGroupEntry {
let material_bind_group = binding: 0,
render_device.create_bind_group(&BindGroupDescriptor { resource: mesh.material_buffer.as_entire_binding(),
entries: &[BindGroupEntry { }],
binding: 0, label: None,
resource: mesh.material_buffer.as_entire_binding(), layout: &pbr_shaders.material_layout,
}], })
label: None, },
layout: &pbr_shaders.material_layout, );
});
material_bind_groups.push(material_bind_group); mesh_meta.mesh_draw_info.push(MeshDrawInfo {
index material_bind_group_key,
}); });
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material // TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
transparent_phase.add(Drawable { transparent_phase.add(Drawable {
draw_function: draw_pbr, draw_function: draw_pbr,
draw_key: i, draw_key: i,
sort_key: material_bind_group_index, // TODO: sort back-to-front, sorting by material for now sort_key: material_bind_group_key.index(), // TODO: sort back-to-front, sorting by material for now
}); });
} }
@ -456,9 +446,9 @@ impl Node for PbrNode {
type DrawPbrParams<'s, 'w> = ( type DrawPbrParams<'s, 'w> = (
Res<'w, PbrShaders>, Res<'w, PbrShaders>,
Res<'w, MaterialMeta>,
Res<'w, MeshMeta>, Res<'w, MeshMeta>,
Res<'w, ExtractedMeshes>, Res<'w, ExtractedMeshes>,
Res<'w, RenderAssets<Mesh>>,
Query< Query<
'w, 'w,
's, 's,
@ -489,12 +479,12 @@ impl Draw for DrawPbr {
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
view: Entity, view: Entity,
draw_key: usize, draw_key: usize,
sort_key: usize, _sort_key: usize,
) { ) {
let (pbr_shaders, material_meta, mesh_meta, extracted_meshes, views) = let (pbr_shaders, mesh_meta, extracted_meshes, meshes, views) = self.params.get(world);
self.params.get(world);
let (view_uniforms, view_lights, mesh_view_bind_groups) = views.get(view).unwrap(); let (view_uniforms, view_lights, mesh_view_bind_groups) = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key]; let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
let mesh_meta = mesh_meta.into_inner();
pass.set_render_pipeline(&pbr_shaders.into_inner().pipeline); pass.set_render_pipeline(&pbr_shaders.into_inner().pipeline);
pass.set_bind_group( pass.set_bind_group(
0, 0,
@ -503,20 +493,19 @@ impl Draw for DrawPbr {
); );
pass.set_bind_group( pass.set_bind_group(
1, 1,
mesh_meta mesh_meta.mesh_transform_bind_group.as_ref().unwrap(),
.into_inner()
.mesh_transform_bind_group
.as_ref()
.unwrap(),
&[extracted_mesh.transform_binding_offset], &[extracted_mesh.transform_binding_offset],
); );
let mesh_draw_info = &mesh_meta.mesh_draw_info[draw_key];
pass.set_bind_group( pass.set_bind_group(
2, 2,
&material_meta.into_inner().material_bind_groups[sort_key], &mesh_meta.material_bind_groups[mesh_draw_info.material_bind_group_key],
&[], &[],
); );
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &extracted_mesh.index_info { let gpu_mesh = meshes.into_inner().get(&extracted_mesh.mesh).unwrap();
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &gpu_mesh.index_info {
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32); pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
pass.draw_indexed(0..index_info.count, 0, 0..1); pass.draw_indexed(0..index_info.count, 0, 0..1);
} else { } else {

View File

@ -5,13 +5,12 @@ mod main_pass_driver;
pub use main_pass_2d::*; pub use main_pass_2d::*;
pub use main_pass_3d::*; pub use main_pass_3d::*;
pub use main_pass_driver::*; pub use main_pass_driver::*;
use wgpu::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage};
use crate::{ use crate::{
camera::{ActiveCameras, CameraPlugin}, camera::{ActiveCameras, CameraPlugin},
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType}, render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_phase::{sort_phase_system, RenderPhase}, render_phase::{sort_phase_system, RenderPhase},
render_resource::{Texture, TextureId, TextureView, TextureViewId}, render_resource::{Texture, TextureView},
renderer::RenderDevice, renderer::RenderDevice,
texture::TextureCache, texture::TextureCache,
view::{ExtractedView, ViewPlugin}, view::{ExtractedView, ViewPlugin},
@ -19,6 +18,7 @@ use crate::{
}; };
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use wgpu::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage};
// Plugins that contribute to the RenderGraph should use the following label conventions: // Plugins that contribute to the RenderGraph should use the following label conventions:
// 1. Graph modules should have a NAME, input module, and node module (where relevant) // 1. Graph modules should have a NAME, input module, and node module (where relevant)

View File

@ -1,69 +0,0 @@
use crate::{
mesh::{Mesh, MeshGpuData},
render_resource::Buffer,
renderer::RenderDevice,
};
use bevy_asset::{AssetEvent, Assets};
use bevy_ecs::prelude::*;
use bevy_utils::HashSet;
use wgpu::{util::BufferInitDescriptor, BufferUsage};
pub fn mesh_resource_provider_system(
render_device: Res<RenderDevice>,
mut meshes: ResMut<Assets<Mesh>>,
mut mesh_events: EventReader<AssetEvent<Mesh>>,
) {
let mut changed_meshes = HashSet::default();
for event in mesh_events.iter() {
match event {
AssetEvent::Created { ref handle } => {
changed_meshes.insert(handle.clone_weak());
}
AssetEvent::Modified { ref handle } => {
changed_meshes.insert(handle.clone_weak());
// TODO: uncomment this to support mutated meshes
// remove_current_mesh_resources(render_resource_context, handle, &mut meshes);
}
AssetEvent::Removed { ref handle } => {
// if mesh was modified and removed in the same update, ignore the modification
// events are ordered so future modification events are ok
changed_meshes.remove(handle);
}
}
}
// update changed mesh data
for changed_mesh_handle in changed_meshes.iter() {
if let Some(mesh) = meshes.get_mut(changed_mesh_handle) {
// TODO: this avoids creating new meshes each frame because storing gpu data in the mesh flags it as
// modified. this prevents hot reloading and therefore can't be used in an actual impl.
if mesh.gpu_data.is_some() {
continue;
}
let vertex_buffer_data = mesh.get_vertex_buffer_data();
let vertex_buffer = Buffer::from(render_device.create_buffer_with_data(
&BufferInitDescriptor {
usage: BufferUsage::VERTEX,
label: None,
contents: &vertex_buffer_data,
},
));
let index_buffer = mesh.get_index_buffer_bytes().map(|data| {
Buffer::from(
render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsage::INDEX,
contents: &data,
label: None,
}),
)
});
mesh.gpu_data = Some(MeshGpuData {
vertex_buffer,
index_buffer,
});
}
}
}

View File

@ -1,229 +1,20 @@
mod conversions; mod conversions;
mod mesh_resource_provider;
pub use mesh_resource_provider::*;
use wgpu::{IndexFormat, PrimitiveTopology, VertexFormat};
use crate::{ use crate::{
render_asset::RenderAsset,
render_resource::Buffer, render_resource::Buffer,
renderer::{RenderDevice, RenderQueue},
}; };
use bevy_core::cast_slice; use bevy_core::cast_slice;
use bevy_math::*; use bevy_math::*;
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_utils::EnumVariantMeta; use bevy_utils::EnumVariantMeta;
use std::{borrow::Cow, collections::BTreeMap, sync::Arc}; use std::{borrow::Cow, collections::BTreeMap};
use wgpu::{util::BufferInitDescriptor, BufferUsage, IndexFormat, PrimitiveTopology, VertexFormat};
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0; pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
/// An array where each entry describes a property of a single vertex.
#[derive(Clone, Debug, EnumVariantMeta)]
pub enum VertexAttributeValues {
Float32(Vec<f32>),
Sint32(Vec<i32>),
Uint32(Vec<u32>),
Float32x2(Vec<[f32; 2]>),
Sint32x2(Vec<[i32; 2]>),
Uint32x2(Vec<[u32; 2]>),
Float32x3(Vec<[f32; 3]>),
Sint32x3(Vec<[i32; 3]>),
Uint32x3(Vec<[u32; 3]>),
Float32x4(Vec<[f32; 4]>),
Sint32x4(Vec<[i32; 4]>),
Uint32x4(Vec<[u32; 4]>),
Sint16x2(Vec<[i16; 2]>),
Snorm16x2(Vec<[i16; 2]>),
Uint16x2(Vec<[u16; 2]>),
Unorm16x2(Vec<[u16; 2]>),
Sint16x4(Vec<[i16; 4]>),
Snorm16x4(Vec<[i16; 4]>),
Uint16x4(Vec<[u16; 4]>),
Unorm16x4(Vec<[u16; 4]>),
Sint8x2(Vec<[i8; 2]>),
Snorm8x2(Vec<[i8; 2]>),
Uint8x2(Vec<[u8; 2]>),
Unorm8x2(Vec<[u8; 2]>),
Sint8x4(Vec<[i8; 4]>),
Snorm8x4(Vec<[i8; 4]>),
Uint8x4(Vec<[u8; 4]>),
Unorm8x4(Vec<[u8; 4]>),
}
impl VertexAttributeValues {
/// Returns the number of vertices in this VertexAttribute. For a single
/// mesh, all of the VertexAttributeValues must have the same length.
pub fn len(&self) -> usize {
match *self {
VertexAttributeValues::Float32(ref values) => values.len(),
VertexAttributeValues::Sint32(ref values) => values.len(),
VertexAttributeValues::Uint32(ref values) => values.len(),
VertexAttributeValues::Float32x2(ref values) => values.len(),
VertexAttributeValues::Sint32x2(ref values) => values.len(),
VertexAttributeValues::Uint32x2(ref values) => values.len(),
VertexAttributeValues::Float32x3(ref values) => values.len(),
VertexAttributeValues::Sint32x3(ref values) => values.len(),
VertexAttributeValues::Uint32x3(ref values) => values.len(),
VertexAttributeValues::Float32x4(ref values) => values.len(),
VertexAttributeValues::Sint32x4(ref values) => values.len(),
VertexAttributeValues::Uint32x4(ref values) => values.len(),
VertexAttributeValues::Sint16x2(ref values) => values.len(),
VertexAttributeValues::Snorm16x2(ref values) => values.len(),
VertexAttributeValues::Uint16x2(ref values) => values.len(),
VertexAttributeValues::Unorm16x2(ref values) => values.len(),
VertexAttributeValues::Sint16x4(ref values) => values.len(),
VertexAttributeValues::Snorm16x4(ref values) => values.len(),
VertexAttributeValues::Uint16x4(ref values) => values.len(),
VertexAttributeValues::Unorm16x4(ref values) => values.len(),
VertexAttributeValues::Sint8x2(ref values) => values.len(),
VertexAttributeValues::Snorm8x2(ref values) => values.len(),
VertexAttributeValues::Uint8x2(ref values) => values.len(),
VertexAttributeValues::Unorm8x2(ref values) => values.len(),
VertexAttributeValues::Sint8x4(ref values) => values.len(),
VertexAttributeValues::Snorm8x4(ref values) => values.len(),
VertexAttributeValues::Uint8x4(ref values) => values.len(),
VertexAttributeValues::Unorm8x4(ref values) => values.len(),
}
}
/// Returns `true` if there are no vertices in this VertexAttributeValue
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn as_float3(&self) -> Option<&[[f32; 3]]> {
match self {
VertexAttributeValues::Float32x3(values) => Some(values),
_ => None,
}
}
// TODO: add vertex format as parameter here and perform type conversions
/// Flattens the VertexAttributeArray into a sequence of bytes. This is
/// useful for serialization and sending to the GPU.
pub fn get_bytes(&self) -> &[u8] {
match self {
VertexAttributeValues::Float32(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32(values) => cast_slice(&values[..]),
VertexAttributeValues::Float32x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Float32x3(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32x3(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32x3(values) => cast_slice(&values[..]),
VertexAttributeValues::Float32x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint8x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm8x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint8x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm8x4(values) => cast_slice(&values[..]),
}
}
}
impl From<&VertexAttributeValues> for VertexFormat {
fn from(values: &VertexAttributeValues) -> Self {
match values {
VertexAttributeValues::Float32(_) => VertexFormat::Float32,
VertexAttributeValues::Sint32(_) => VertexFormat::Sint32,
VertexAttributeValues::Uint32(_) => VertexFormat::Uint32,
VertexAttributeValues::Float32x2(_) => VertexFormat::Float32x2,
VertexAttributeValues::Sint32x2(_) => VertexFormat::Sint32x2,
VertexAttributeValues::Uint32x2(_) => VertexFormat::Uint32x2,
VertexAttributeValues::Float32x3(_) => VertexFormat::Float32x3,
VertexAttributeValues::Sint32x3(_) => VertexFormat::Sint32x3,
VertexAttributeValues::Uint32x3(_) => VertexFormat::Uint32x3,
VertexAttributeValues::Float32x4(_) => VertexFormat::Float32x4,
VertexAttributeValues::Sint32x4(_) => VertexFormat::Sint32x4,
VertexAttributeValues::Uint32x4(_) => VertexFormat::Uint32x4,
VertexAttributeValues::Sint16x2(_) => VertexFormat::Sint16x2,
VertexAttributeValues::Snorm16x2(_) => VertexFormat::Snorm16x2,
VertexAttributeValues::Uint16x2(_) => VertexFormat::Uint16x2,
VertexAttributeValues::Unorm16x2(_) => VertexFormat::Unorm16x2,
VertexAttributeValues::Sint16x4(_) => VertexFormat::Sint16x4,
VertexAttributeValues::Snorm16x4(_) => VertexFormat::Snorm16x4,
VertexAttributeValues::Uint16x4(_) => VertexFormat::Uint16x4,
VertexAttributeValues::Unorm16x4(_) => VertexFormat::Unorm16x4,
VertexAttributeValues::Sint8x2(_) => VertexFormat::Sint8x2,
VertexAttributeValues::Snorm8x2(_) => VertexFormat::Snorm8x2,
VertexAttributeValues::Uint8x2(_) => VertexFormat::Uint8x2,
VertexAttributeValues::Unorm8x2(_) => VertexFormat::Unorm8x2,
VertexAttributeValues::Sint8x4(_) => VertexFormat::Sint8x4,
VertexAttributeValues::Snorm8x4(_) => VertexFormat::Snorm8x4,
VertexAttributeValues::Uint8x4(_) => VertexFormat::Uint8x4,
VertexAttributeValues::Unorm8x4(_) => VertexFormat::Unorm8x4,
}
}
}
/// An array of indices into the VertexAttributeValues for a mesh.
///
/// It describes the order in which the vertex attributes should be joined into faces.
#[derive(Debug, Clone)]
pub enum Indices {
U16(Vec<u16>),
U32(Vec<u32>),
}
impl Indices {
fn iter(&self) -> impl Iterator<Item = usize> + '_ {
match self {
Indices::U16(vec) => IndicesIter::U16(vec.iter()),
Indices::U32(vec) => IndicesIter::U32(vec.iter()),
}
}
pub fn len(&self) -> usize {
match self {
Indices::U16(vec) => vec.len(),
Indices::U32(vec) => vec.len(),
}
}
}
enum IndicesIter<'a> {
U16(std::slice::Iter<'a, u16>),
U32(std::slice::Iter<'a, u32>),
}
impl Iterator for IndicesIter<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
match self {
IndicesIter::U16(iter) => iter.next().map(|val| *val as usize),
IndicesIter::U32(iter) => iter.next().map(|val| *val as usize),
}
}
}
impl From<&Indices> for IndexFormat {
fn from(indices: &Indices) -> Self {
match indices {
Indices::U16(_) => IndexFormat::Uint16,
Indices::U32(_) => IndexFormat::Uint32,
}
}
}
// TODO: this shouldn't live in the Mesh type
#[derive(Debug, Clone)]
pub struct MeshGpuData {
pub vertex_buffer: Buffer,
pub index_buffer: Option<Buffer>,
}
// TODO: allow values to be unloaded after been submitting to the GPU to conserve memory // TODO: allow values to be unloaded after been submitting to the GPU to conserve memory
#[derive(Debug, TypeUuid, Clone)] #[derive(Debug, TypeUuid, Clone)]
#[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"] #[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"]
@ -235,7 +26,6 @@ pub struct Mesh {
/// which allows easy stable VertexBuffers (i.e. same buffer order) /// which allows easy stable VertexBuffers (i.e. same buffer order)
attributes: BTreeMap<Cow<'static, str>, VertexAttributeValues>, attributes: BTreeMap<Cow<'static, str>, VertexAttributeValues>,
indices: Option<Indices>, indices: Option<Indices>,
gpu_data: Option<MeshGpuData>,
} }
/// Contains geometry in the form of a mesh. /// Contains geometry in the form of a mesh.
@ -282,7 +72,6 @@ impl Mesh {
primitive_topology, primitive_topology,
attributes: Default::default(), attributes: Default::default(),
indices: None, indices: None,
gpu_data: None,
} }
} }
@ -290,10 +79,6 @@ impl Mesh {
self.primitive_topology self.primitive_topology
} }
pub fn gpu_data(&self) -> Option<&MeshGpuData> {
self.gpu_data.as_ref()
}
/// Sets the data for a vertex attribute (position, normal etc.). The name will /// Sets the data for a vertex attribute (position, normal etc.). The name will
/// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`] /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`]
pub fn set_attribute( pub fn set_attribute(
@ -530,4 +315,257 @@ impl VertexFormatSize for wgpu::VertexFormat {
VertexFormat::Float64x4 => 8 * 4, VertexFormat::Float64x4 => 8 * 4,
} }
} }
} }
/// An array where each entry describes a property of a single vertex.
#[derive(Clone, Debug, EnumVariantMeta)]
pub enum VertexAttributeValues {
Float32(Vec<f32>),
Sint32(Vec<i32>),
Uint32(Vec<u32>),
Float32x2(Vec<[f32; 2]>),
Sint32x2(Vec<[i32; 2]>),
Uint32x2(Vec<[u32; 2]>),
Float32x3(Vec<[f32; 3]>),
Sint32x3(Vec<[i32; 3]>),
Uint32x3(Vec<[u32; 3]>),
Float32x4(Vec<[f32; 4]>),
Sint32x4(Vec<[i32; 4]>),
Uint32x4(Vec<[u32; 4]>),
Sint16x2(Vec<[i16; 2]>),
Snorm16x2(Vec<[i16; 2]>),
Uint16x2(Vec<[u16; 2]>),
Unorm16x2(Vec<[u16; 2]>),
Sint16x4(Vec<[i16; 4]>),
Snorm16x4(Vec<[i16; 4]>),
Uint16x4(Vec<[u16; 4]>),
Unorm16x4(Vec<[u16; 4]>),
Sint8x2(Vec<[i8; 2]>),
Snorm8x2(Vec<[i8; 2]>),
Uint8x2(Vec<[u8; 2]>),
Unorm8x2(Vec<[u8; 2]>),
Sint8x4(Vec<[i8; 4]>),
Snorm8x4(Vec<[i8; 4]>),
Uint8x4(Vec<[u8; 4]>),
Unorm8x4(Vec<[u8; 4]>),
}
impl VertexAttributeValues {
/// Returns the number of vertices in this VertexAttribute. For a single
/// mesh, all of the VertexAttributeValues must have the same length.
pub fn len(&self) -> usize {
match *self {
VertexAttributeValues::Float32(ref values) => values.len(),
VertexAttributeValues::Sint32(ref values) => values.len(),
VertexAttributeValues::Uint32(ref values) => values.len(),
VertexAttributeValues::Float32x2(ref values) => values.len(),
VertexAttributeValues::Sint32x2(ref values) => values.len(),
VertexAttributeValues::Uint32x2(ref values) => values.len(),
VertexAttributeValues::Float32x3(ref values) => values.len(),
VertexAttributeValues::Sint32x3(ref values) => values.len(),
VertexAttributeValues::Uint32x3(ref values) => values.len(),
VertexAttributeValues::Float32x4(ref values) => values.len(),
VertexAttributeValues::Sint32x4(ref values) => values.len(),
VertexAttributeValues::Uint32x4(ref values) => values.len(),
VertexAttributeValues::Sint16x2(ref values) => values.len(),
VertexAttributeValues::Snorm16x2(ref values) => values.len(),
VertexAttributeValues::Uint16x2(ref values) => values.len(),
VertexAttributeValues::Unorm16x2(ref values) => values.len(),
VertexAttributeValues::Sint16x4(ref values) => values.len(),
VertexAttributeValues::Snorm16x4(ref values) => values.len(),
VertexAttributeValues::Uint16x4(ref values) => values.len(),
VertexAttributeValues::Unorm16x4(ref values) => values.len(),
VertexAttributeValues::Sint8x2(ref values) => values.len(),
VertexAttributeValues::Snorm8x2(ref values) => values.len(),
VertexAttributeValues::Uint8x2(ref values) => values.len(),
VertexAttributeValues::Unorm8x2(ref values) => values.len(),
VertexAttributeValues::Sint8x4(ref values) => values.len(),
VertexAttributeValues::Snorm8x4(ref values) => values.len(),
VertexAttributeValues::Uint8x4(ref values) => values.len(),
VertexAttributeValues::Unorm8x4(ref values) => values.len(),
}
}
/// Returns `true` if there are no vertices in this VertexAttributeValue
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn as_float3(&self) -> Option<&[[f32; 3]]> {
match self {
VertexAttributeValues::Float32x3(values) => Some(values),
_ => None,
}
}
// TODO: add vertex format as parameter here and perform type conversions
/// Flattens the VertexAttributeArray into a sequence of bytes. This is
/// useful for serialization and sending to the GPU.
pub fn get_bytes(&self) -> &[u8] {
match self {
VertexAttributeValues::Float32(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32(values) => cast_slice(&values[..]),
VertexAttributeValues::Float32x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Float32x3(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32x3(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32x3(values) => cast_slice(&values[..]),
VertexAttributeValues::Float32x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint32x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint32x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm16x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm16x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm8x2(values) => cast_slice(&values[..]),
VertexAttributeValues::Sint8x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Snorm8x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Uint8x4(values) => cast_slice(&values[..]),
VertexAttributeValues::Unorm8x4(values) => cast_slice(&values[..]),
}
}
}
impl From<&VertexAttributeValues> for VertexFormat {
fn from(values: &VertexAttributeValues) -> Self {
match values {
VertexAttributeValues::Float32(_) => VertexFormat::Float32,
VertexAttributeValues::Sint32(_) => VertexFormat::Sint32,
VertexAttributeValues::Uint32(_) => VertexFormat::Uint32,
VertexAttributeValues::Float32x2(_) => VertexFormat::Float32x2,
VertexAttributeValues::Sint32x2(_) => VertexFormat::Sint32x2,
VertexAttributeValues::Uint32x2(_) => VertexFormat::Uint32x2,
VertexAttributeValues::Float32x3(_) => VertexFormat::Float32x3,
VertexAttributeValues::Sint32x3(_) => VertexFormat::Sint32x3,
VertexAttributeValues::Uint32x3(_) => VertexFormat::Uint32x3,
VertexAttributeValues::Float32x4(_) => VertexFormat::Float32x4,
VertexAttributeValues::Sint32x4(_) => VertexFormat::Sint32x4,
VertexAttributeValues::Uint32x4(_) => VertexFormat::Uint32x4,
VertexAttributeValues::Sint16x2(_) => VertexFormat::Sint16x2,
VertexAttributeValues::Snorm16x2(_) => VertexFormat::Snorm16x2,
VertexAttributeValues::Uint16x2(_) => VertexFormat::Uint16x2,
VertexAttributeValues::Unorm16x2(_) => VertexFormat::Unorm16x2,
VertexAttributeValues::Sint16x4(_) => VertexFormat::Sint16x4,
VertexAttributeValues::Snorm16x4(_) => VertexFormat::Snorm16x4,
VertexAttributeValues::Uint16x4(_) => VertexFormat::Uint16x4,
VertexAttributeValues::Unorm16x4(_) => VertexFormat::Unorm16x4,
VertexAttributeValues::Sint8x2(_) => VertexFormat::Sint8x2,
VertexAttributeValues::Snorm8x2(_) => VertexFormat::Snorm8x2,
VertexAttributeValues::Uint8x2(_) => VertexFormat::Uint8x2,
VertexAttributeValues::Unorm8x2(_) => VertexFormat::Unorm8x2,
VertexAttributeValues::Sint8x4(_) => VertexFormat::Sint8x4,
VertexAttributeValues::Snorm8x4(_) => VertexFormat::Snorm8x4,
VertexAttributeValues::Uint8x4(_) => VertexFormat::Uint8x4,
VertexAttributeValues::Unorm8x4(_) => VertexFormat::Unorm8x4,
}
}
}
/// An array of indices into the VertexAttributeValues for a mesh.
///
/// It describes the order in which the vertex attributes should be joined into faces.
#[derive(Debug, Clone)]
pub enum Indices {
U16(Vec<u16>),
U32(Vec<u32>),
}
impl Indices {
fn iter(&self) -> impl Iterator<Item = usize> + '_ {
match self {
Indices::U16(vec) => IndicesIter::U16(vec.iter()),
Indices::U32(vec) => IndicesIter::U32(vec.iter()),
}
}
pub fn len(&self) -> usize {
match self {
Indices::U16(vec) => vec.len(),
Indices::U32(vec) => vec.len(),
}
}
}
enum IndicesIter<'a> {
U16(std::slice::Iter<'a, u16>),
U32(std::slice::Iter<'a, u32>),
}
impl Iterator for IndicesIter<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
match self {
IndicesIter::U16(iter) => iter.next().map(|val| *val as usize),
IndicesIter::U32(iter) => iter.next().map(|val| *val as usize),
}
}
}
impl From<&Indices> for IndexFormat {
fn from(indices: &Indices) -> Self {
match indices {
Indices::U16(_) => IndexFormat::Uint16,
Indices::U32(_) => IndexFormat::Uint32,
}
}
}
#[derive(Debug, Clone)]
pub struct GpuMesh {
pub vertex_buffer: Buffer,
pub index_info: Option<GpuIndexInfo>,
}
#[derive(Debug, Clone)]
pub struct GpuIndexInfo {
pub buffer: Buffer,
pub count: u32,
}
impl RenderAsset for Mesh {
type ExtractedAsset = Mesh;
type PreparedAsset = GpuMesh;
fn extract_asset(&self) -> Self::ExtractedAsset {
self.clone()
}
fn prepare_asset(
mesh: Self::ExtractedAsset,
render_device: &RenderDevice,
_render_queue: &RenderQueue,
) -> Self::PreparedAsset {
let vertex_buffer_data = mesh.get_vertex_buffer_data();
let vertex_buffer = Buffer::from(render_device.create_buffer_with_data(
&BufferInitDescriptor {
usage: BufferUsage::VERTEX,
label: None,
contents: &vertex_buffer_data,
},
));
let index_info = mesh.get_index_buffer_bytes().map(|data| GpuIndexInfo {
buffer: Buffer::from(render_device.create_buffer_with_data(
&BufferInitDescriptor {
usage: BufferUsage::INDEX,
contents: &data,
label: None,
},
)),
count: mesh.indices().unwrap().len() as u32,
});
GpuMesh {
vertex_buffer,
index_info,
}
}
}

View File

@ -3,19 +3,17 @@ mod mesh;
/// Generation for some primitive shape meshes. /// Generation for some primitive shape meshes.
pub mod shape; pub mod shape;
use bevy_asset::AddAsset;
pub use mesh::*; pub use mesh::*;
use bevy_app::{App, CoreStage, Plugin}; use crate::render_asset::RenderAssetPlugin;
use bevy_ecs::system::IntoSystem; use bevy_app::{App, Plugin};
use bevy_asset::AddAsset;
pub struct MeshPlugin; pub struct MeshPlugin;
impl Plugin for MeshPlugin { impl Plugin for MeshPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_asset::<Mesh>().add_system_to_stage( app.add_asset::<Mesh>()
CoreStage::PostUpdate, .add_plugin(RenderAssetPlugin::<Mesh>::default());
mesh_resource_provider_system.system(),
);
} }
} }

View File

@ -21,9 +21,14 @@ pub trait RenderAsset: Asset {
} }
/// Extracts assets into gpu-usable data /// Extracts assets into gpu-usable data
#[derive(Default)]
pub struct RenderAssetPlugin<A: RenderAsset>(PhantomData<fn() -> A>); pub struct RenderAssetPlugin<A: RenderAsset>(PhantomData<fn() -> A>);
impl<A: RenderAsset> Default for RenderAssetPlugin<A> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> { impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
let render_app = app.sub_app_mut(0); let render_app = app.sub_app_mut(0);
@ -49,7 +54,7 @@ impl<A: RenderAsset> Default for ExtractedAssets<A> {
} }
} }
pub type RenderAssets<A: RenderAsset> = HashMap<Handle<A>, A::PreparedAsset>; pub type RenderAssets<A> = HashMap<Handle<A>, <A as RenderAsset>::PreparedAsset>;
fn extract_render_asset<A: RenderAsset>( fn extract_render_asset<A: RenderAsset>(
mut commands: Commands, mut commands: Commands,

View File

@ -357,7 +357,6 @@ impl RenderAsset for Image {
let texture = render_device.create_texture(&image.texture_descriptor); let texture = render_device.create_texture(&image.texture_descriptor);
let sampler = render_device.create_sampler(&image.sampler_descriptor); let sampler = render_device.create_sampler(&image.sampler_descriptor);
let width = image.texture_descriptor.size.width as usize;
let format_size = image.texture_descriptor.format.pixel_size(); let format_size = image.texture_descriptor.format.pixel_size();
render_queue.write_texture( render_queue.write_texture(
ImageCopyTexture { ImageCopyTexture {

View File

@ -1,8 +1,8 @@
use crate::{ use crate::{
render_resource::{Texture, TextureView, TextureViewId}, render_resource::{Texture, TextureView},
renderer::RenderDevice, renderer::RenderDevice,
}; };
use bevy_ecs::prelude::{Res, ResMut}; use bevy_ecs::prelude::ResMut;
use bevy_utils::HashMap; use bevy_utils::HashMap;
use wgpu::{TextureDescriptor, TextureViewDescriptor}; use wgpu::{TextureDescriptor, TextureViewDescriptor};
@ -72,7 +72,7 @@ impl TextureCache {
} }
} }
pub fn update(&mut self, device: &RenderDevice) { pub fn update(&mut self) {
for textures in self.textures.values_mut() { for textures in self.textures.values_mut() {
for texture in textures.iter_mut() { for texture in textures.iter_mut() {
texture.frames_since_last_use += 1; texture.frames_since_last_use += 1;
@ -84,9 +84,6 @@ impl TextureCache {
} }
} }
pub fn update_texture_cache_system( pub fn update_texture_cache_system(mut texture_cache: ResMut<TextureCache>) {
mut texture_cache: ResMut<TextureCache>, texture_cache.update();
render_device: Res<RenderDevice>,
) {
texture_cache.update(&render_device);
} }

View File

@ -1,5 +1,3 @@
use std::ops::{Deref, DerefMut};
use crate::{ use crate::{
render_resource::TextureView, render_resource::TextureView,
renderer::{RenderDevice, RenderInstance}, renderer::{RenderDevice, RenderInstance},
@ -10,7 +8,8 @@ use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_utils::HashMap; use bevy_utils::HashMap;
use bevy_window::{RawWindowHandleWrapper, WindowId, Windows}; use bevy_window::{RawWindowHandleWrapper, WindowId, Windows};
use wgpu::{SwapChainFrame, TextureFormat}; use std::ops::{Deref, DerefMut};
use wgpu::TextureFormat;
pub struct WindowRenderPlugin; pub struct WindowRenderPlugin;
@ -77,10 +76,6 @@ pub struct WindowSurfaces {
swap_chains: HashMap<WindowId, wgpu::SwapChain>, swap_chains: HashMap<WindowId, wgpu::SwapChain>,
} }
pub struct WindowSwapChain {
value: TextureView,
}
pub fn prepare_windows( pub fn prepare_windows(
mut windows: ResMut<ExtractedWindows>, mut windows: ResMut<ExtractedWindows>,
mut window_surfaces: ResMut<WindowSurfaces>, mut window_surfaces: ResMut<WindowSurfaces>,

View File

@ -15,7 +15,7 @@ use bevy_render2::{
view::{ViewMeta, ViewUniform, ViewUniformOffset}, view::{ViewMeta, ViewUniform, ViewUniformOffset},
}; };
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap; use bevy_utils::slab::{FrameSlabMap, FrameSlabMapKey};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::borrow::Cow; use std::borrow::Cow;
@ -56,7 +56,6 @@ impl FromWorld for SpriteShaders {
ty: BindingType::Buffer { ty: BindingType::Buffer {
ty: BufferBindingType::Uniform, ty: BufferBindingType::Uniform,
has_dynamic_offset: true, has_dynamic_offset: true,
// TODO: verify this is correct
min_binding_size: BufferSize::new(std::mem::size_of::<ViewUniform>() as u64), min_binding_size: BufferSize::new(std::mem::size_of::<ViewUniform>() as u64),
}, },
count: None, count: None,
@ -204,9 +203,8 @@ pub struct SpriteMeta {
indices: BufferVec<u32>, indices: BufferVec<u32>,
quad: Mesh, quad: Mesh,
view_bind_group: Option<BindGroup>, view_bind_group: Option<BindGroup>,
// TODO: these should be garbage collected if unused across X frames texture_bind_group_keys: Vec<FrameSlabMapKey<Handle<Image>, BindGroup>>,
texture_bind_groups: Vec<BindGroup>, texture_bind_groups: FrameSlabMap<Handle<Image>, BindGroup>,
texture_bind_group_indices: HashMap<Handle<Image>, usize>,
} }
impl Default for SpriteMeta { impl Default for SpriteMeta {
@ -214,8 +212,8 @@ impl Default for SpriteMeta {
Self { Self {
vertices: BufferVec::new(BufferUsage::VERTEX), vertices: BufferVec::new(BufferUsage::VERTEX),
indices: BufferVec::new(BufferUsage::INDEX), indices: BufferVec::new(BufferUsage::INDEX),
texture_bind_groups: Vec::new(), texture_bind_groups: Default::default(),
texture_bind_group_indices: HashMap::default(), texture_bind_group_keys: Default::default(),
view_bind_group: None, view_bind_group: None,
quad: Quad { quad: Quad {
size: Vec2::new(1.0, 1.0), size: Vec2::new(1.0, 1.0),
@ -320,16 +318,15 @@ pub fn queue_sprites(
}); });
let sprite_meta = &mut *sprite_meta; let sprite_meta = &mut *sprite_meta;
let draw_sprite_function = draw_functions.read().get_id::<DrawSprite>().unwrap(); let draw_sprite_function = draw_functions.read().get_id::<DrawSprite>().unwrap();
sprite_meta.texture_bind_groups.next_frame();
sprite_meta.texture_bind_group_keys.clear();
for mut transparent_phase in views.iter_mut() { for mut transparent_phase in views.iter_mut() {
let texture_bind_groups = &mut sprite_meta.texture_bind_groups;
for (i, sprite) in extracted_sprites.sprites.iter().enumerate() { for (i, sprite) in extracted_sprites.sprites.iter().enumerate() {
let bind_group_index = *sprite_meta let texture_bind_group_key = sprite_meta.texture_bind_groups.get_or_insert_with(
.texture_bind_group_indices sprite.handle.clone_weak(),
.entry(sprite.handle.clone_weak()) || {
.or_insert_with(|| {
let gpu_image = gpu_images.get(&sprite.handle).unwrap(); let gpu_image = gpu_images.get(&sprite.handle).unwrap();
let index = texture_bind_groups.len(); render_device.create_bind_group(&BindGroupDescriptor {
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[ entries: &[
BindGroupEntry { BindGroupEntry {
binding: 0, binding: 0,
@ -342,14 +339,17 @@ pub fn queue_sprites(
], ],
label: None, label: None,
layout: &sprite_shaders.material_layout, layout: &sprite_shaders.material_layout,
}); })
texture_bind_groups.push(bind_group); },
index );
}); sprite_meta
.texture_bind_group_keys
.push(texture_bind_group_key);
transparent_phase.add(Drawable { transparent_phase.add(Drawable {
draw_function: draw_sprite_function, draw_function: draw_sprite_function,
draw_key: i, draw_key: i,
sort_key: bind_group_index, sort_key: texture_bind_group_key.index(),
}); });
} }
} }
@ -400,7 +400,7 @@ impl Draw for DrawSprite {
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
view: Entity, view: Entity,
draw_key: usize, draw_key: usize,
sort_key: usize, _sort_key: usize,
) { ) {
const INDICES: usize = 6; const INDICES: usize = 6;
let (sprite_shaders, sprite_meta, views) = self.params.get(world); let (sprite_shaders, sprite_meta, views) = self.params.get(world);
@ -418,7 +418,11 @@ impl Draw for DrawSprite {
sprite_meta.view_bind_group.as_ref().unwrap(), sprite_meta.view_bind_group.as_ref().unwrap(),
&[view_uniform.offset], &[view_uniform.offset],
); );
pass.set_bind_group(1, &sprite_meta.texture_bind_groups[sort_key], &[]); pass.set_bind_group(
1,
&sprite_meta.texture_bind_groups[sprite_meta.texture_bind_group_keys[draw_key]],
&[],
);
pass.draw_indexed( pass.draw_indexed(
(draw_key * INDICES) as u32..(draw_key * INDICES + INDICES) as u32, (draw_key * INDICES) as u32..(draw_key * INDICES + INDICES) as u32,