Pack multiple vertex and index arrays together into growable buffers. (#14257)
This commit uses the [`offset-allocator`] crate to combine vertex and index arrays from different meshes into single buffers. Since the primary source of `wgpu` overhead is from validation and synchronization when switching buffers, this significantly improves Bevy's rendering performance on many scenes. This patch is a more flexible version of #13218, which also used slabs. Unlike #13218, which used slabs of a fixed size, this commit implements slabs that start small and can grow. In addition to reducing memory usage, supporting slab growth reduces the number of vertex and index buffer switches that need to happen during rendering, leading to improved performance. To prevent pathological fragmentation behavior, slabs are capped to a maximum size, and mesh arrays that are too large get their own dedicated slabs. As an additional improvement over #13218, this commit allows the application to customize all allocator heuristics. The `MeshAllocatorSettings` resource contains values that adjust the minimum and maximum slab sizes, the cutoff point at which meshes get their own dedicated slabs, and the rate at which slabs grow. Hopefully-sensible defaults have been chosen for each value. Unfortunately, WebGL 2 doesn't support the *base vertex* feature, which is necessary to pack vertex arrays from different meshes into the same buffer. `wgpu` represents this restriction as the downlevel flag `BASE_VERTEX`. This patch detects that bit and ensures that all vertex buffers get dedicated slabs on that platform. Even on WebGL 2, though, we can combine all *index* arrays into single buffers to reduce buffer changes, and we do so. The following measurements are on Bistro: Overall frame time improves from 8.74 ms to 5.53 ms (1.58x speedup):  Render system time improves from 6.57 ms to 3.54 ms (1.86x speedup):  Opaque pass time improves from 4.64 ms to 2.33 ms (1.99x speedup):  ## Migration Guide ### Changed * Vertex and index buffers for meshes may now be packed alongside other buffers, for performance. * `GpuMesh` has been renamed to `RenderMesh`, to reflect the fact that it no longer directly stores handles to GPU objects. * Because meshes no longer have their own vertex and index buffers, the responsibility for the buffers has moved from `GpuMesh` (now called `RenderMesh`) to the `MeshAllocator` resource. To access the vertex data for a mesh, use `MeshAllocator::mesh_vertex_slice`. To access the index data for a mesh, use `MeshAllocator::mesh_index_slice`. [`offset-allocator`]: https://github.com/pcwalton/offset-allocator
This commit is contained in:
parent
293e91564b
commit
bc34216929
@ -40,7 +40,7 @@ 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::mesh::RenderMesh;
|
||||
use bevy_render::texture::GpuImage;
|
||||
use bevy_render::{
|
||||
mesh::Mesh, render_asset::RenderAssets, render_resource::Shader, texture::Image,
|
||||
@ -145,7 +145,7 @@ fn extract_lightmaps(
|
||||
lightmaps: Extract<Query<(Entity, &ViewVisibility, &Lightmap)>>,
|
||||
render_mesh_instances: Res<RenderMeshInstances>,
|
||||
images: Res<RenderAssets<GpuImage>>,
|
||||
meshes: Res<RenderAssets<GpuMesh>>,
|
||||
meshes: Res<RenderAssets<RenderMesh>>,
|
||||
) {
|
||||
// Clear out the old frame's data.
|
||||
render_lightmaps.render_lightmaps.clear();
|
||||
|
@ -25,7 +25,7 @@ use bevy_render::{
|
||||
camera::TemporalJitter,
|
||||
extract_instances::{ExtractInstancesPlugin, ExtractedInstances},
|
||||
extract_resource::ExtractResource,
|
||||
mesh::{GpuMesh, MeshVertexBufferLayoutRef},
|
||||
mesh::{MeshVertexBufferLayoutRef, RenderMesh},
|
||||
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
|
||||
render_phase::*,
|
||||
render_resource::*,
|
||||
@ -537,7 +537,7 @@ pub fn queue_material_meshes<M: Material>(
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
msaa: Res<Msaa>,
|
||||
render_meshes: Res<RenderAssets<GpuMesh>>,
|
||||
render_meshes: Res<RenderAssets<RenderMesh>>,
|
||||
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
|
||||
render_mesh_instances: Res<RenderMeshInstances>,
|
||||
render_material_instances: Res<RenderMaterialInstances<M>>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
mod prepass_bindings;
|
||||
|
||||
use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef};
|
||||
use bevy_render::mesh::{MeshVertexBufferLayoutRef, RenderMesh};
|
||||
use bevy_render::render_resource::binding_types::uniform_buffer;
|
||||
use bevy_render::view::WithMesh;
|
||||
pub use prepass_bindings::*;
|
||||
@ -680,7 +680,7 @@ 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<GpuMesh>>,
|
||||
render_meshes: Res<RenderAssets<RenderMesh>>,
|
||||
render_mesh_instances: Res<RenderMeshInstances>,
|
||||
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
|
||||
render_material_instances: Res<RenderMaterialInstances<M>>,
|
||||
|
@ -7,7 +7,7 @@ use bevy_ecs::{entity::EntityHashMap, system::lifetimeless::Read};
|
||||
use bevy_math::{Mat4, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
|
||||
use bevy_render::{
|
||||
diagnostic::RecordDiagnostics,
|
||||
mesh::GpuMesh,
|
||||
mesh::RenderMesh,
|
||||
primitives::{CascadesFrusta, CubemapFrusta, Frustum, HalfSpace},
|
||||
render_asset::RenderAssets,
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||
@ -1162,7 +1162,7 @@ pub fn prepare_lights(
|
||||
pub fn queue_shadows<M: Material>(
|
||||
shadow_draw_functions: Res<DrawFunctions<Shadow>>,
|
||||
prepass_pipeline: Res<PrepassPipeline<M>>,
|
||||
render_meshes: Res<RenderAssets<GpuMesh>>,
|
||||
render_meshes: Res<RenderAssets<RenderMesh>>,
|
||||
render_mesh_instances: Res<RenderMeshInstances>,
|
||||
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
|
||||
render_material_instances: Res<RenderMaterialInstances<M>>,
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::mem;
|
||||
|
||||
use allocator::MeshAllocator;
|
||||
use bevy_asset::{load_internal_asset, AssetId};
|
||||
use bevy_core_pipeline::{
|
||||
core_3d::{AlphaMask3d, Opaque3d, Transmissive3d, Transparent3d, CORE_3D_DEPTH_FORMAT},
|
||||
@ -1209,7 +1210,8 @@ impl GetBatchData for MeshPipeline {
|
||||
type Param = (
|
||||
SRes<RenderMeshInstances>,
|
||||
SRes<RenderLightmaps>,
|
||||
SRes<RenderAssets<GpuMesh>>,
|
||||
SRes<RenderAssets<RenderMesh>>,
|
||||
SRes<MeshAllocator>,
|
||||
);
|
||||
// The material bind group ID, the mesh ID, and the lightmap ID,
|
||||
// respectively.
|
||||
@ -1218,7 +1220,7 @@ impl GetBatchData for MeshPipeline {
|
||||
type BufferData = MeshUniform;
|
||||
|
||||
fn get_batch_data(
|
||||
(mesh_instances, lightmaps, _): &SystemParamItem<Self::Param>,
|
||||
(mesh_instances, lightmaps, _, _): &SystemParamItem<Self::Param>,
|
||||
entity: Entity,
|
||||
) -> Option<(Self::BufferData, Option<Self::CompareData>)> {
|
||||
let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
|
||||
@ -1249,7 +1251,7 @@ impl GetFullBatchData for MeshPipeline {
|
||||
type BufferInputData = MeshInputUniform;
|
||||
|
||||
fn get_index_and_compare_data(
|
||||
(mesh_instances, lightmaps, _): &SystemParamItem<Self::Param>,
|
||||
(mesh_instances, lightmaps, _, _): &SystemParamItem<Self::Param>,
|
||||
entity: Entity,
|
||||
) -> Option<(NonMaxU32, Option<Self::CompareData>)> {
|
||||
// This should only be called during GPU building.
|
||||
@ -1275,7 +1277,7 @@ impl GetFullBatchData for MeshPipeline {
|
||||
}
|
||||
|
||||
fn get_binned_batch_data(
|
||||
(mesh_instances, lightmaps, _): &SystemParamItem<Self::Param>,
|
||||
(mesh_instances, lightmaps, _, _): &SystemParamItem<Self::Param>,
|
||||
entity: Entity,
|
||||
) -> Option<Self::BufferData> {
|
||||
let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else {
|
||||
@ -1294,7 +1296,7 @@ impl GetFullBatchData for MeshPipeline {
|
||||
}
|
||||
|
||||
fn get_binned_index(
|
||||
(mesh_instances, _, _): &SystemParamItem<Self::Param>,
|
||||
(mesh_instances, _, _, _): &SystemParamItem<Self::Param>,
|
||||
entity: Entity,
|
||||
) -> Option<NonMaxU32> {
|
||||
// This should only be called during GPU building.
|
||||
@ -1312,7 +1314,7 @@ impl GetFullBatchData for MeshPipeline {
|
||||
}
|
||||
|
||||
fn get_batch_indirect_parameters_index(
|
||||
(mesh_instances, _, meshes): &SystemParamItem<Self::Param>,
|
||||
(mesh_instances, _, meshes, mesh_allocator): &SystemParamItem<Self::Param>,
|
||||
indirect_parameters_buffer: &mut IndirectParametersBuffer,
|
||||
entity: Entity,
|
||||
instance_index: u32,
|
||||
@ -1320,6 +1322,7 @@ impl GetFullBatchData for MeshPipeline {
|
||||
get_batch_indirect_parameters_index(
|
||||
mesh_instances,
|
||||
meshes,
|
||||
mesh_allocator,
|
||||
indirect_parameters_buffer,
|
||||
entity,
|
||||
instance_index,
|
||||
@ -1332,7 +1335,8 @@ impl GetFullBatchData for MeshPipeline {
|
||||
/// parameters.
|
||||
fn get_batch_indirect_parameters_index(
|
||||
mesh_instances: &RenderMeshInstances,
|
||||
meshes: &RenderAssets<GpuMesh>,
|
||||
meshes: &RenderAssets<RenderMesh>,
|
||||
mesh_allocator: &MeshAllocator,
|
||||
indirect_parameters_buffer: &mut IndirectParametersBuffer,
|
||||
entity: Entity,
|
||||
instance_index: u32,
|
||||
@ -1348,24 +1352,29 @@ fn get_batch_indirect_parameters_index(
|
||||
|
||||
let mesh_instance = mesh_instances.get(&entity)?;
|
||||
let mesh = meshes.get(mesh_instance.mesh_asset_id)?;
|
||||
let vertex_buffer_slice = mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id)?;
|
||||
|
||||
// Note that `IndirectParameters` covers both of these structures, even
|
||||
// though they actually have distinct layouts. See the comment above that
|
||||
// type for more information.
|
||||
let indirect_parameters = match mesh.buffer_info {
|
||||
GpuBufferInfo::Indexed {
|
||||
RenderMeshBufferInfo::Indexed {
|
||||
count: index_count, ..
|
||||
} => IndirectParameters {
|
||||
vertex_or_index_count: index_count,
|
||||
instance_count: 0,
|
||||
first_vertex: 0,
|
||||
base_vertex_or_first_instance: 0,
|
||||
first_instance: instance_index,
|
||||
},
|
||||
GpuBufferInfo::NonIndexed => IndirectParameters {
|
||||
} => {
|
||||
let index_buffer_slice =
|
||||
mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id)?;
|
||||
IndirectParameters {
|
||||
vertex_or_index_count: index_count,
|
||||
instance_count: 0,
|
||||
first_vertex_or_first_index: index_buffer_slice.range.start,
|
||||
base_vertex_or_first_instance: vertex_buffer_slice.range.start,
|
||||
first_instance: instance_index,
|
||||
}
|
||||
}
|
||||
RenderMeshBufferInfo::NonIndexed => IndirectParameters {
|
||||
vertex_or_index_count: mesh.vertex_count,
|
||||
instance_count: 0,
|
||||
first_vertex: 0,
|
||||
first_vertex_or_first_index: vertex_buffer_slice.range.start,
|
||||
base_vertex_or_first_instance: instance_index,
|
||||
first_instance: instance_index,
|
||||
},
|
||||
@ -1945,7 +1954,7 @@ impl MeshBindGroups {
|
||||
self.morph_targets.clear();
|
||||
self.lightmaps.clear();
|
||||
}
|
||||
/// Get the `BindGroup` for `GpuMesh` with given `handle_id` and lightmap
|
||||
/// Get the `BindGroup` for `RenderMesh` with given `handle_id` and lightmap
|
||||
/// key `lightmap`.
|
||||
pub fn get(
|
||||
&self,
|
||||
@ -1982,7 +1991,7 @@ impl MeshBindGroupPair {
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn prepare_mesh_bind_group(
|
||||
meshes: Res<RenderAssets<GpuMesh>>,
|
||||
meshes: Res<RenderAssets<RenderMesh>>,
|
||||
images: Res<RenderAssets<GpuImage>>,
|
||||
mut groups: ResMut<MeshBindGroups>,
|
||||
mesh_pipeline: Res<MeshPipeline>,
|
||||
@ -2238,10 +2247,11 @@ 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<GpuMesh>>,
|
||||
SRes<RenderAssets<RenderMesh>>,
|
||||
SRes<RenderMeshInstances>,
|
||||
SRes<IndirectParametersBuffer>,
|
||||
SRes<PipelineCache>,
|
||||
SRes<MeshAllocator>,
|
||||
Option<SRes<PreprocessPipelines>>,
|
||||
);
|
||||
type ViewQuery = Has<PreprocessBindGroup>;
|
||||
@ -2251,7 +2261,14 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
|
||||
item: &P,
|
||||
has_preprocess_bind_group: ROQueryItem<Self::ViewQuery>,
|
||||
_item_query: Option<()>,
|
||||
(meshes, mesh_instances, indirect_parameters_buffer, pipeline_cache, preprocess_pipelines): SystemParamItem<'w, '_, Self::Param>,
|
||||
(
|
||||
meshes,
|
||||
mesh_instances,
|
||||
indirect_parameters_buffer,
|
||||
pipeline_cache,
|
||||
mesh_allocator,
|
||||
preprocess_pipelines,
|
||||
): SystemParamItem<'w, '_, Self::Param>,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
// If we're using GPU preprocessing, then we're dependent on that
|
||||
@ -2268,6 +2285,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
|
||||
let meshes = meshes.into_inner();
|
||||
let mesh_instances = mesh_instances.into_inner();
|
||||
let indirect_parameters_buffer = indirect_parameters_buffer.into_inner();
|
||||
let mesh_allocator = mesh_allocator.into_inner();
|
||||
|
||||
let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.entity()) else {
|
||||
return RenderCommandResult::Failure;
|
||||
@ -2275,6 +2293,9 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
|
||||
let Some(gpu_mesh) = meshes.get(mesh_asset_id) else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_asset_id) else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
|
||||
// Calculate the indirect offset, and look up the buffer.
|
||||
let indirect_parameters = match item.extra_index().as_indirect_parameters_index() {
|
||||
@ -2291,21 +2312,31 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
|
||||
},
|
||||
};
|
||||
|
||||
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
||||
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
|
||||
|
||||
let batch_range = item.batch_range();
|
||||
|
||||
// Draw either directly or indirectly, as appropriate.
|
||||
match &gpu_mesh.buffer_info {
|
||||
GpuBufferInfo::Indexed {
|
||||
buffer,
|
||||
RenderMeshBufferInfo::Indexed {
|
||||
index_format,
|
||||
count,
|
||||
} => {
|
||||
pass.set_index_buffer(buffer.slice(..), 0, *index_format);
|
||||
let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_asset_id)
|
||||
else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
|
||||
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);
|
||||
|
||||
match indirect_parameters {
|
||||
None => {
|
||||
pass.draw_indexed(0..*count, 0, batch_range.clone());
|
||||
pass.draw_indexed(
|
||||
index_buffer_slice.range.start
|
||||
..(index_buffer_slice.range.start + *count),
|
||||
vertex_buffer_slice.range.start as i32,
|
||||
batch_range.clone(),
|
||||
);
|
||||
}
|
||||
Some((indirect_parameters_offset, indirect_parameters_buffer)) => pass
|
||||
.draw_indexed_indirect(
|
||||
@ -2314,7 +2345,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
|
||||
),
|
||||
}
|
||||
}
|
||||
GpuBufferInfo::NonIndexed => match indirect_parameters {
|
||||
RenderMeshBufferInfo::NonIndexed => match indirect_parameters {
|
||||
None => {
|
||||
pass.draw(0..gpu_mesh.vertex_count, batch_range.clone());
|
||||
}
|
||||
|
@ -18,7 +18,9 @@ use bevy_ecs::{
|
||||
};
|
||||
use bevy_math::{vec4, Mat3A, Mat4, Vec3, Vec3A, Vec4, Vec4Swizzles as _};
|
||||
use bevy_render::{
|
||||
mesh::{GpuBufferInfo, GpuMesh, Mesh, MeshVertexBufferLayoutRef},
|
||||
mesh::{
|
||||
allocator::MeshAllocator, Mesh, MeshVertexBufferLayoutRef, RenderMesh, RenderMeshBufferInfo,
|
||||
},
|
||||
render_asset::RenderAssets,
|
||||
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
|
||||
render_resource::{
|
||||
@ -329,6 +331,7 @@ impl ViewNode for VolumetricFogNode {
|
||||
let volumetric_lighting_uniform_buffers = world.resource::<VolumetricFogUniformBuffer>();
|
||||
let image_assets = world.resource::<RenderAssets<GpuImage>>();
|
||||
let msaa = world.resource::<Msaa>();
|
||||
let mesh_allocator = world.resource::<MeshAllocator>();
|
||||
|
||||
// Fetch the uniform buffer and binding.
|
||||
let (
|
||||
@ -344,7 +347,7 @@ impl ViewNode for VolumetricFogNode {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let gpu_meshes = world.resource::<RenderAssets<GpuMesh>>();
|
||||
let render_meshes = world.resource::<RenderAssets<RenderMesh>>();
|
||||
|
||||
for view_fog_volume in view_fog_volumes.iter() {
|
||||
// If the camera is outside the fog volume, pick the cube mesh;
|
||||
@ -356,6 +359,11 @@ impl ViewNode for VolumetricFogNode {
|
||||
PLANE_MESH.clone()
|
||||
};
|
||||
|
||||
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_handle.id())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let density_image = view_fog_volume
|
||||
.density_texture
|
||||
.and_then(|density_texture| image_assets.get(density_texture));
|
||||
@ -370,7 +378,7 @@ impl ViewNode for VolumetricFogNode {
|
||||
|
||||
// This should always succeed, but if the asset was unloaded don't
|
||||
// panic.
|
||||
let Some(gpu_mesh) = gpu_meshes.get(&mesh_handle) else {
|
||||
let Some(render_mesh) = render_meshes.get(&mesh_handle) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@ -426,7 +434,7 @@ impl ViewNode for VolumetricFogNode {
|
||||
.command_encoder()
|
||||
.begin_render_pass(&render_pass_descriptor);
|
||||
|
||||
render_pass.set_vertex_buffer(0, *gpu_mesh.vertex_buffer.slice(..));
|
||||
render_pass.set_vertex_buffer(0, *vertex_buffer_slice.buffer.slice(..));
|
||||
render_pass.set_pipeline(pipeline);
|
||||
render_pass.set_bind_group(
|
||||
0,
|
||||
@ -446,17 +454,23 @@ impl ViewNode for VolumetricFogNode {
|
||||
);
|
||||
|
||||
// Draw elements or arrays, as appropriate.
|
||||
match &gpu_mesh.buffer_info {
|
||||
GpuBufferInfo::Indexed {
|
||||
buffer,
|
||||
match &render_mesh.buffer_info {
|
||||
RenderMeshBufferInfo::Indexed {
|
||||
index_format,
|
||||
count,
|
||||
} => {
|
||||
render_pass.set_index_buffer(*buffer.slice(..), *index_format);
|
||||
let Some(index_buffer_slice) =
|
||||
mesh_allocator.mesh_index_slice(&mesh_handle.id())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
render_pass
|
||||
.set_index_buffer(*index_buffer_slice.buffer.slice(..), *index_format);
|
||||
render_pass.draw_indexed(0..*count, 0, 0..1);
|
||||
}
|
||||
GpuBufferInfo::NonIndexed => {
|
||||
render_pass.draw(0..gpu_mesh.vertex_count, 0..1);
|
||||
RenderMeshBufferInfo::NonIndexed => {
|
||||
render_pass.draw(0..render_mesh.vertex_count, 0..1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -584,7 +598,7 @@ pub fn prepare_volumetric_fog_pipelines(
|
||||
With<VolumetricFogSettings>,
|
||||
>,
|
||||
msaa: Res<Msaa>,
|
||||
meshes: Res<RenderAssets<GpuMesh>>,
|
||||
meshes: Res<RenderAssets<RenderMesh>>,
|
||||
) {
|
||||
let plane_mesh = meshes.get(&PLANE_MESH).expect("Plane mesh not found!");
|
||||
|
||||
|
@ -101,6 +101,7 @@ profiling = { version = "1", features = [
|
||||
async-channel = "2.2.0"
|
||||
nonmax = "0.5"
|
||||
smallvec = { version = "1.11", features = ["const_new"] }
|
||||
offset-allocator = "0.2"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
# Omit the `glsl` feature in non-WebAssembly by default.
|
||||
|
@ -185,8 +185,9 @@ pub struct IndirectParameters {
|
||||
/// This field is in the same place in both structures.
|
||||
pub instance_count: u32,
|
||||
|
||||
/// The index of the first vertex we're to draw.
|
||||
pub first_vertex: u32,
|
||||
/// For `ArrayIndirectParameters`, `first_vertex`; for
|
||||
/// `ElementIndirectParameters`, `first_index`.
|
||||
pub first_vertex_or_first_index: u32,
|
||||
|
||||
/// For `ArrayIndirectParameters`, `first_instance`; for
|
||||
/// `ElementIndirectParameters`, `base_vertex`.
|
||||
|
@ -64,7 +64,7 @@ use globals::GlobalsPlugin;
|
||||
use render_asset::RenderAssetBytesPerFrame;
|
||||
use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
|
||||
|
||||
use crate::mesh::GpuMesh;
|
||||
use crate::mesh::RenderMesh;
|
||||
use crate::renderer::WgpuWrapper;
|
||||
use crate::{
|
||||
camera::CameraPlugin,
|
||||
@ -115,7 +115,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::<GpuMesh>` is completed.
|
||||
/// A sub-set within [`Queue`](RenderSet::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<RenderMesh>` is completed.
|
||||
QueueMeshes,
|
||||
// TODO: This could probably be moved in favor of a system ordering
|
||||
// abstraction in `Render` or `Queue`
|
||||
@ -165,7 +165,11 @@ impl Render {
|
||||
);
|
||||
|
||||
schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain());
|
||||
schedule.configure_sets(QueueMeshes.in_set(Queue).after(prepare_assets::<GpuMesh>));
|
||||
schedule.configure_sets(
|
||||
QueueMeshes
|
||||
.in_set(Queue)
|
||||
.after(prepare_assets::<RenderMesh>),
|
||||
);
|
||||
schedule.configure_sets(
|
||||
(PrepareResources, PrepareResourcesFlush, PrepareBindGroups)
|
||||
.chain()
|
||||
|
1025
crates/bevy_render/src/mesh/allocator.rs
Normal file
1025
crates/bevy_render/src/mesh/allocator.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,7 @@ use crate::{
|
||||
prelude::Image,
|
||||
primitives::Aabb,
|
||||
render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages, RenderAssets},
|
||||
render_resource::{Buffer, TextureView, VertexBufferLayout},
|
||||
renderer::RenderDevice,
|
||||
render_resource::{TextureView, VertexBufferLayout},
|
||||
texture::GpuImage,
|
||||
};
|
||||
use bevy_asset::{Asset, Handle};
|
||||
@ -24,10 +23,7 @@ use bevy_utils::tracing::{error, warn};
|
||||
use bytemuck::cast_slice;
|
||||
use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator};
|
||||
use thiserror::Error;
|
||||
use wgpu::{
|
||||
util::BufferInitDescriptor, BufferUsages, IndexFormat, VertexAttribute, VertexFormat,
|
||||
VertexStepMode,
|
||||
};
|
||||
use wgpu::{IndexFormat, VertexAttribute, VertexFormat, VertexStepMode};
|
||||
|
||||
use super::{MeshVertexBufferLayoutRef, MeshVertexBufferLayouts};
|
||||
|
||||
@ -1660,42 +1656,51 @@ impl BaseMeshPipelineKey {
|
||||
}
|
||||
}
|
||||
|
||||
/// The GPU-representation of a [`Mesh`].
|
||||
/// Consists of a vertex data buffer and an optional index data buffer.
|
||||
/// The render world representation of a [`Mesh`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GpuMesh {
|
||||
/// Contains all attribute data for each vertex.
|
||||
pub vertex_buffer: Buffer,
|
||||
pub struct RenderMesh {
|
||||
/// The number of vertices in the mesh.
|
||||
pub vertex_count: u32,
|
||||
|
||||
/// Morph targets for the mesh, if present.
|
||||
pub morph_targets: Option<TextureView>,
|
||||
pub buffer_info: GpuBufferInfo,
|
||||
|
||||
/// Information about the mesh data buffers, including whether the mesh uses
|
||||
/// indices or not.
|
||||
pub buffer_info: RenderMeshBufferInfo,
|
||||
|
||||
/// Precomputed pipeline key bits for this mesh.
|
||||
pub key_bits: BaseMeshPipelineKey,
|
||||
|
||||
/// A reference to the vertex buffer layout.
|
||||
///
|
||||
/// Combined with [`RenderMesh::buffer_info`], this specifies the complete
|
||||
/// layout of the buffers associated with this mesh.
|
||||
pub layout: MeshVertexBufferLayoutRef,
|
||||
}
|
||||
|
||||
impl GpuMesh {
|
||||
impl RenderMesh {
|
||||
/// Returns the primitive topology of this mesh (triangles, triangle strips,
|
||||
/// etc.)
|
||||
#[inline]
|
||||
pub fn primitive_topology(&self) -> PrimitiveTopology {
|
||||
self.key_bits.primitive_topology()
|
||||
}
|
||||
}
|
||||
|
||||
/// The index/vertex buffer info of a [`GpuMesh`].
|
||||
/// The index/vertex buffer info of a [`RenderMesh`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GpuBufferInfo {
|
||||
pub enum RenderMeshBufferInfo {
|
||||
Indexed {
|
||||
/// Contains all index data of a mesh.
|
||||
buffer: Buffer,
|
||||
count: u32,
|
||||
index_format: IndexFormat,
|
||||
},
|
||||
NonIndexed,
|
||||
}
|
||||
|
||||
impl RenderAsset for GpuMesh {
|
||||
impl RenderAsset for RenderMesh {
|
||||
type SourceAsset = Mesh;
|
||||
type Param = (
|
||||
SRes<RenderDevice>,
|
||||
SRes<RenderAssets<GpuImage>>,
|
||||
SResMut<MeshVertexBufferLayouts>,
|
||||
);
|
||||
@ -1717,12 +1722,10 @@ impl RenderAsset for GpuMesh {
|
||||
Some(vertex_size * vertex_count + index_bytes)
|
||||
}
|
||||
|
||||
/// Converts the extracted mesh a into [`GpuMesh`].
|
||||
/// Converts the extracted mesh into a [`RenderMesh`].
|
||||
fn prepare_asset(
|
||||
mesh: Self::SourceAsset,
|
||||
(render_device, images, ref mut mesh_vertex_buffer_layouts): &mut SystemParamItem<
|
||||
Self::Param,
|
||||
>,
|
||||
(images, ref mut mesh_vertex_buffer_layouts): &mut SystemParamItem<Self::Param>,
|
||||
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
|
||||
let morph_targets = match mesh.morph_targets.as_ref() {
|
||||
Some(mt) => {
|
||||
@ -1734,25 +1737,12 @@ impl RenderAsset for GpuMesh {
|
||||
None => None,
|
||||
};
|
||||
|
||||
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) = 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: mesh.indices().unwrap().len() as u32,
|
||||
index_format: mesh.indices().unwrap().into(),
|
||||
}
|
||||
} else {
|
||||
GpuBufferInfo::NonIndexed
|
||||
let buffer_info = match mesh.indices() {
|
||||
Some(indices) => RenderMeshBufferInfo::Indexed {
|
||||
count: indices.len() as u32,
|
||||
index_format: indices.into(),
|
||||
},
|
||||
None => RenderMeshBufferInfo::NonIndexed,
|
||||
};
|
||||
|
||||
let mesh_vertex_buffer_layout =
|
||||
@ -1764,8 +1754,7 @@ impl RenderAsset for GpuMesh {
|
||||
mesh.morph_targets.is_some(),
|
||||
);
|
||||
|
||||
Ok(GpuMesh {
|
||||
vertex_buffer,
|
||||
Ok(RenderMesh {
|
||||
vertex_count: mesh.count_vertices() as u32,
|
||||
buffer_info,
|
||||
key_bits,
|
||||
|
@ -1,8 +1,11 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
mod mesh;
|
||||
|
||||
pub mod allocator;
|
||||
pub mod morph;
|
||||
pub mod primitives;
|
||||
|
||||
use allocator::MeshAllocatorPlugin;
|
||||
use bevy_utils::HashSet;
|
||||
pub use mesh::*;
|
||||
pub use primitives::*;
|
||||
@ -27,7 +30,8 @@ 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::<GpuMesh, GpuImage>::default());
|
||||
.add_plugins(RenderAssetPlugin::<RenderMesh, GpuImage>::default())
|
||||
.add_plugins(MeshAllocatorPlugin);
|
||||
|
||||
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||
return;
|
||||
|
@ -114,7 +114,7 @@ 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::<GpuImage>` for morph
|
||||
/// targets, so the plugin is created as `RenderAssetPlugin::<GpuMesh, GpuImage>::default()`.
|
||||
/// targets, so the plugin is created as `RenderAssetPlugin::<RenderMesh, GpuImage>::default()`.
|
||||
pub struct RenderAssetPlugin<A: RenderAsset, AFTER: RenderAssetDependency + 'static = ()> {
|
||||
phantom: PhantomData<fn() -> (A, AFTER)>,
|
||||
}
|
||||
@ -168,9 +168,16 @@ 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::SourceAsset>, A::SourceAsset)>,
|
||||
removed: HashSet<AssetId<A::SourceAsset>>,
|
||||
added: HashSet<AssetId<A::SourceAsset>>,
|
||||
/// The assets extracted this frame.
|
||||
pub extracted: Vec<(AssetId<A::SourceAsset>, A::SourceAsset)>,
|
||||
|
||||
/// IDs of the assets removed this frame.
|
||||
///
|
||||
/// These assets will not be present in [`ExtractedAssets::extracted`].
|
||||
pub removed: HashSet<AssetId<A::SourceAsset>>,
|
||||
|
||||
/// IDs of the assets added this frame.
|
||||
pub added: HashSet<AssetId<A::SourceAsset>>,
|
||||
}
|
||||
|
||||
impl<A: RenderAsset> Default for ExtractedAssets<A> {
|
||||
@ -238,7 +245,10 @@ impl<A: RenderAsset> FromWorld for CachedExtractRenderAssetSystemState<A> {
|
||||
|
||||
/// 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>) {
|
||||
pub(crate) fn extract_render_asset<A: RenderAsset>(
|
||||
mut commands: Commands,
|
||||
mut main_world: ResMut<MainWorld>,
|
||||
) {
|
||||
main_world.resource_scope(
|
||||
|world, mut cached_state: Mut<CachedExtractRenderAssetSystemState<A>>| {
|
||||
let (mut events, mut assets) = cached_state.state.get_mut(world);
|
||||
|
@ -12,7 +12,7 @@ use bevy_ecs::{
|
||||
};
|
||||
use bevy_math::FloatOrd;
|
||||
use bevy_render::{
|
||||
mesh::{GpuMesh, MeshVertexBufferLayoutRef},
|
||||
mesh::{MeshVertexBufferLayoutRef, RenderMesh},
|
||||
render_asset::{
|
||||
prepare_assets, PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets,
|
||||
},
|
||||
@ -370,7 +370,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<Material2dPipeline<M>>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
msaa: Res<Msaa>,
|
||||
render_meshes: Res<RenderAssets<GpuMesh>>,
|
||||
render_meshes: Res<RenderAssets<RenderMesh>>,
|
||||
render_materials: Res<RenderAssets<PreparedMaterial2d<M>>>,
|
||||
mut render_mesh_instances: ResMut<RenderMesh2dInstances>,
|
||||
render_material_instances: Res<RenderMaterial2dInstances<M>>,
|
||||
|
@ -18,12 +18,13 @@ use bevy_render::batching::no_gpu_preprocessing::{
|
||||
self, batch_and_prepare_sorted_render_phase, write_batched_instance_buffer,
|
||||
BatchedInstanceBuffer,
|
||||
};
|
||||
use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef};
|
||||
use bevy_render::mesh::allocator::MeshAllocator;
|
||||
use bevy_render::mesh::{MeshVertexBufferLayoutRef, RenderMesh};
|
||||
use bevy_render::texture::FallbackImage;
|
||||
use bevy_render::{
|
||||
batching::{GetBatchData, NoAutomaticBatching},
|
||||
globals::{GlobalsBuffer, GlobalsUniform},
|
||||
mesh::{GpuBufferInfo, Mesh},
|
||||
mesh::{Mesh, RenderMeshBufferInfo},
|
||||
render_asset::RenderAssets,
|
||||
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::{binding_types::uniform_buffer, *},
|
||||
@ -694,7 +695,11 @@ 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<GpuMesh>>, SRes<RenderMesh2dInstances>);
|
||||
type Param = (
|
||||
SRes<RenderAssets<RenderMesh>>,
|
||||
SRes<RenderMesh2dInstances>,
|
||||
SRes<MeshAllocator>,
|
||||
);
|
||||
type ViewQuery = ();
|
||||
type ItemQuery = ();
|
||||
|
||||
@ -703,11 +708,12 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
|
||||
item: &P,
|
||||
_view: (),
|
||||
_item_query: Option<()>,
|
||||
(meshes, render_mesh2d_instances): SystemParamItem<'w, '_, Self::Param>,
|
||||
(meshes, render_mesh2d_instances, mesh_allocator): SystemParamItem<'w, '_, Self::Param>,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
let meshes = meshes.into_inner();
|
||||
let render_mesh2d_instances = render_mesh2d_instances.into_inner();
|
||||
let mesh_allocator = mesh_allocator.into_inner();
|
||||
|
||||
let Some(RenderMesh2dInstance { mesh_asset_id, .. }) =
|
||||
render_mesh2d_instances.get(&item.entity())
|
||||
@ -717,20 +723,32 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
|
||||
let Some(gpu_mesh) = meshes.get(*mesh_asset_id) else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(mesh_asset_id) else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
|
||||
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
||||
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
|
||||
|
||||
let batch_range = item.batch_range();
|
||||
match &gpu_mesh.buffer_info {
|
||||
GpuBufferInfo::Indexed {
|
||||
buffer,
|
||||
RenderMeshBufferInfo::Indexed {
|
||||
index_format,
|
||||
count,
|
||||
} => {
|
||||
pass.set_index_buffer(buffer.slice(..), 0, *index_format);
|
||||
pass.draw_indexed(0..*count, 0, batch_range.clone());
|
||||
let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(mesh_asset_id)
|
||||
else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
|
||||
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);
|
||||
|
||||
pass.draw_indexed(
|
||||
index_buffer_slice.range.start..(index_buffer_slice.range.start + count),
|
||||
vertex_buffer_slice.range.start as i32,
|
||||
batch_range.clone(),
|
||||
);
|
||||
}
|
||||
GpuBufferInfo::NonIndexed => {
|
||||
RenderMeshBufferInfo::NonIndexed => {
|
||||
pass.draw(0..gpu_mesh.vertex_count, batch_range.clone());
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use bevy::{
|
||||
math::FloatOrd,
|
||||
prelude::*,
|
||||
render::{
|
||||
mesh::{GpuMesh, Indices, MeshVertexAttribute},
|
||||
mesh::{Indices, MeshVertexAttribute, RenderMesh},
|
||||
render_asset::{RenderAssetUsages, RenderAssets},
|
||||
render_phase::{
|
||||
AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline,
|
||||
@ -352,7 +352,7 @@ pub fn queue_colored_mesh2d(
|
||||
mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
msaa: Res<Msaa>,
|
||||
render_meshes: Res<RenderAssets<GpuMesh>>,
|
||||
render_meshes: Res<RenderAssets<RenderMesh>>,
|
||||
render_mesh_instances: Res<RenderColoredMesh2dInstances>,
|
||||
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
|
||||
mut views: Query<(Entity, &VisibleEntities, &ExtractedView)>,
|
||||
|
@ -12,7 +12,9 @@ use bevy::{
|
||||
prelude::*,
|
||||
render::{
|
||||
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
||||
mesh::{GpuBufferInfo, GpuMesh, MeshVertexBufferLayoutRef},
|
||||
mesh::{
|
||||
allocator::MeshAllocator, MeshVertexBufferLayoutRef, RenderMesh, RenderMeshBufferInfo,
|
||||
},
|
||||
render_asset::RenderAssets,
|
||||
render_phase::{
|
||||
AddRenderCommand, DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand,
|
||||
@ -117,7 +119,7 @@ fn queue_custom(
|
||||
msaa: Res<Msaa>,
|
||||
mut pipelines: ResMut<SpecializedMeshPipelines<CustomPipeline>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
meshes: Res<RenderAssets<GpuMesh>>,
|
||||
meshes: Res<RenderAssets<RenderMesh>>,
|
||||
render_mesh_instances: Res<RenderMeshInstances>,
|
||||
material_meshes: Query<Entity, With<InstanceMaterialData>>,
|
||||
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
|
||||
@ -241,7 +243,11 @@ type DrawCustom = (
|
||||
struct DrawMeshInstanced;
|
||||
|
||||
impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
|
||||
type Param = (SRes<RenderAssets<GpuMesh>>, SRes<RenderMeshInstances>);
|
||||
type Param = (
|
||||
SRes<RenderAssets<RenderMesh>>,
|
||||
SRes<RenderMeshInstances>,
|
||||
SRes<MeshAllocator>,
|
||||
);
|
||||
type ViewQuery = ();
|
||||
type ItemQuery = Read<InstanceBuffer>;
|
||||
|
||||
@ -250,9 +256,12 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
|
||||
item: &P,
|
||||
_view: (),
|
||||
instance_buffer: Option<&'w InstanceBuffer>,
|
||||
(meshes, render_mesh_instances): SystemParamItem<'w, '_, Self::Param>,
|
||||
(meshes, render_mesh_instances, mesh_allocator): SystemParamItem<'w, '_, Self::Param>,
|
||||
pass: &mut TrackedRenderPass<'w>,
|
||||
) -> RenderCommandResult {
|
||||
// A borrow check workaround.
|
||||
let mesh_allocator = mesh_allocator.into_inner();
|
||||
|
||||
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.entity())
|
||||
else {
|
||||
return RenderCommandResult::Failure;
|
||||
@ -263,20 +272,34 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
|
||||
let Some(instance_buffer) = instance_buffer else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
let Some(vertex_buffer_slice) =
|
||||
mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id)
|
||||
else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
|
||||
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
||||
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
|
||||
pass.set_vertex_buffer(1, instance_buffer.buffer.slice(..));
|
||||
|
||||
match &gpu_mesh.buffer_info {
|
||||
GpuBufferInfo::Indexed {
|
||||
buffer,
|
||||
RenderMeshBufferInfo::Indexed {
|
||||
index_format,
|
||||
count,
|
||||
} => {
|
||||
pass.set_index_buffer(buffer.slice(..), 0, *index_format);
|
||||
pass.draw_indexed(0..*count, 0, 0..instance_buffer.length as u32);
|
||||
let Some(index_buffer_slice) =
|
||||
mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id)
|
||||
else {
|
||||
return RenderCommandResult::Failure;
|
||||
};
|
||||
|
||||
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);
|
||||
pass.draw_indexed(
|
||||
index_buffer_slice.range.start..(index_buffer_slice.range.start + count),
|
||||
vertex_buffer_slice.range.start as i32,
|
||||
0..instance_buffer.length as u32,
|
||||
);
|
||||
}
|
||||
GpuBufferInfo::NonIndexed => {
|
||||
RenderMeshBufferInfo::NonIndexed => {
|
||||
pass.draw(0..gpu_mesh.vertex_count, 0..instance_buffer.length as u32);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user