Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use GpuArrayBuffer for MeshUniform #9254

Merged
merged 15 commits into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions assets/shaders/custom_vertex_attribute.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct CustomMaterial {
var<uniform> material: CustomMaterial;

struct Vertex {
@builtin(instance_index) instance_index: u32,
@location(0) position: vec3<f32>,
@location(1) blend_color: vec4<f32>,
};
Expand All @@ -21,8 +22,8 @@ struct VertexOutput {
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(
mesh.model,
vec4<f32>(vertex.position, 1.0)
mesh[vertex.instance_index].model,
vec4<f32>(vertex.position, 1.0),
);
out.blend_color = vertex.blend_color;
return out;
Expand Down
6 changes: 5 additions & 1 deletion assets/shaders/instancing.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ struct VertexOutput {
fn vertex(vertex: Vertex) -> VertexOutput {
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
var out: VertexOutput;
// NOTE: The 0 index into the Mesh array is a hack for this example as the
// instance_index builtin would map to the wrong index in the Mesh array.
// This index could be passed in via another uniform instead but it's
// unnecessary for the example.
out.clip_position = mesh_position_local_to_clip(
mesh.model,
mesh[0].model,
vec4<f32>(position, 1.0)
);
out.color = vertex.i_color;
Expand Down
30 changes: 24 additions & 6 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,18 @@ impl Plugin for Core3dPlugin {

pub struct Opaque3d {
pub distance: f32,
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
// each batch of per-object data starts at the same dynamic offset.
pub per_object_binding_dynamic_offset: u32,
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
}

impl PhaseItem for Opaque3d {
// NOTE: (dynamic offset, -distance)
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
type SortKey = Reverse<FloatOrd>;
type SortKey = (u32, Reverse<FloatOrd>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not for this pr but just wondering, is there any possibility for / value in some kind of spatial sorting before putting the mesh data in the gpu array? i think currently they are entity iterator sorted, but it seems like (at least for static meshes) there'd be some benefit to having a batch be as proximally local as possible and then to sort batches based on the nearest member, or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will happen when the render set reordering and batching is implemented. The reason for reordering the render sets to extract, prepare assets, queue, sort, prepare+batch, render is to allow the order of draws to be known when preparing the data so that the order can be taken into account.


#[inline]
fn entity(&self) -> Entity {
Expand All @@ -152,7 +156,10 @@ impl PhaseItem for Opaque3d {

#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
}

#[inline]
Expand All @@ -163,7 +170,9 @@ impl PhaseItem for Opaque3d {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| -item.distance);
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
}
}

Expand All @@ -176,14 +185,18 @@ impl CachedRenderPipelinePhaseItem for Opaque3d {

pub struct AlphaMask3d {
pub distance: f32,
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
// each batch of per-object data starts at the same dynamic offset.
pub per_object_binding_dynamic_offset: u32,
superdump marked this conversation as resolved.
Show resolved Hide resolved
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
}

impl PhaseItem for AlphaMask3d {
// NOTE: (dynamic offset, -distance)
// NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort.
type SortKey = Reverse<FloatOrd>;
type SortKey = (u32, Reverse<FloatOrd>);

#[inline]
fn entity(&self) -> Entity {
Expand All @@ -192,7 +205,10 @@ impl PhaseItem for AlphaMask3d {

#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
}

#[inline]
Expand All @@ -203,7 +219,9 @@ impl PhaseItem for AlphaMask3d {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| -item.distance);
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
}
}

Expand Down
32 changes: 25 additions & 7 deletions crates/bevy_core_pipeline/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,18 @@ pub struct ViewPrepassTextures {
/// Used to render all 3D meshes with materials that have no transparency.
pub struct Opaque3dPrepass {
pub distance: f32,
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
// each batch of per-object data starts at the same dynamic offset.
pub per_object_binding_dynamic_offset: u32,
pub entity: Entity,
pub pipeline_id: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
}

impl PhaseItem for Opaque3dPrepass {
// NOTE: (dynamic offset, -distance)
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
type SortKey = Reverse<FloatOrd>;
type SortKey = (u32, Reverse<FloatOrd>);

#[inline]
fn entity(&self) -> Entity {
Expand All @@ -96,7 +100,10 @@ impl PhaseItem for Opaque3dPrepass {

#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
}

#[inline]
Expand All @@ -107,7 +114,9 @@ impl PhaseItem for Opaque3dPrepass {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| -item.distance);
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
}
}

Expand All @@ -125,14 +134,18 @@ impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
/// Used to render all meshes with a material with an alpha mask.
pub struct AlphaMask3dPrepass {
pub distance: f32,
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
// each batch of per-object data starts at the same dynamic offset.
pub per_object_binding_dynamic_offset: u32,
pub entity: Entity,
pub pipeline_id: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
}

impl PhaseItem for AlphaMask3dPrepass {
// NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort.
type SortKey = Reverse<FloatOrd>;
// NOTE: (dynamic offset, -distance)
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
type SortKey = (u32, Reverse<FloatOrd>);

#[inline]
fn entity(&self) -> Entity {
Expand All @@ -141,7 +154,10 @@ impl PhaseItem for AlphaMask3dPrepass {

#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
}

#[inline]
Expand All @@ -152,7 +168,9 @@ impl PhaseItem for AlphaMask3dPrepass {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| -item.distance);
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }
bitflags = "2.3"
# direct dependency required for derive macro
bytemuck = { version = "1", features = ["derive"] }
radsort = "0.1"
naga_oil = "0.8"
radsort = "0.1"
smallvec = "1.6"
21 changes: 16 additions & 5 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ use bevy_render::{
RenderPhase, SetItemPipeline, TrackedRenderPass,
},
render_resource::{
AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, OwnedBindingResource,
PipelineCache, RenderPipelineDescriptor, Shader, ShaderRef, SpecializedMeshPipeline,
SpecializedMeshPipelineError, SpecializedMeshPipelines,
AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, GpuArrayBufferIndex,
OwnedBindingResource, PipelineCache, RenderPipelineDescriptor, Shader, ShaderRef,
SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines,
},
renderer::RenderDevice,
texture::FallbackImage,
Expand Down Expand Up @@ -379,7 +379,12 @@ pub fn queue_material_meshes<M: Material>(
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
render_materials: Res<RenderMaterials<M>>,
material_meshes: Query<(&Handle<M>, &Handle<Mesh>, &MeshUniform)>,
material_meshes: Query<(
&Handle<M>,
&Handle<Mesh>,
&MeshUniform,
&GpuArrayBufferIndex<MeshUniform>,
)>,
images: Res<RenderAssets<Image>>,
mut views: Query<(
&ExtractedView,
Expand Down Expand Up @@ -463,7 +468,7 @@ pub fn queue_material_meshes<M: Material>(

let rangefinder = view.rangefinder3d();
for visible_entity in &visible_entities.entities {
if let Ok((material_handle, mesh_handle, mesh_uniform)) =
if let Ok((material_handle, mesh_handle, mesh_uniform, batch_indices)) =
material_meshes.get(*visible_entity)
{
if let (Some(mesh), Some(material)) = (
Expand Down Expand Up @@ -520,6 +525,9 @@ pub fn queue_material_meshes<M: Material>(
draw_function: draw_opaque_pbr,
pipeline: pipeline_id,
distance,
per_object_binding_dynamic_offset: batch_indices
.dynamic_offset
.unwrap_or_default(),
});
}
AlphaMode::Mask(_) => {
Expand All @@ -528,6 +536,9 @@ pub fn queue_material_meshes<M: Material>(
draw_function: draw_alpha_mask_pbr,
pipeline: pipeline_id,
distance,
per_object_binding_dynamic_offset: batch_indices
.dynamic_offset
.unwrap_or_default(),
});
}
AlphaMode::Blend
Expand Down
21 changes: 16 additions & 5 deletions crates/bevy_pbr/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ use bevy_render::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType,
ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
DynamicUniformBuffer, FragmentState, FrontFace, MultisampleState, PipelineCache,
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderRef, ShaderStages,
ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
DynamicUniformBuffer, FragmentState, FrontFace, GpuArrayBufferIndex, MultisampleState,
PipelineCache, PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderRef,
ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
SpecializedMeshPipelines, StencilFaceState, StencilState, TextureSampleType,
TextureViewDimension, VertexState,
},
Expand Down Expand Up @@ -751,7 +751,12 @@ pub fn queue_prepass_material_meshes<M: Material>(
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
render_materials: Res<RenderMaterials<M>>,
material_meshes: Query<(&Handle<M>, &Handle<Mesh>, &MeshUniform)>,
material_meshes: Query<(
&Handle<M>,
&Handle<Mesh>,
&MeshUniform,
&GpuArrayBufferIndex<MeshUniform>,
)>,
mut views: Query<(
&ExtractedView,
&VisibleEntities,
Expand Down Expand Up @@ -796,7 +801,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
let rangefinder = view.rangefinder3d();

for visible_entity in &visible_entities.entities {
let Ok((material_handle, mesh_handle, mesh_uniform)) = material_meshes.get(*visible_entity) else {
let Ok((material_handle, mesh_handle, mesh_uniform, batch_indices)) = material_meshes.get(*visible_entity) else {
continue;
};

Expand Down Expand Up @@ -848,6 +853,9 @@ pub fn queue_prepass_material_meshes<M: Material>(
draw_function: opaque_draw_prepass,
pipeline_id,
distance,
per_object_binding_dynamic_offset: batch_indices
.dynamic_offset
.unwrap_or_default(),
});
}
AlphaMode::Mask(_) => {
Expand All @@ -856,6 +864,9 @@ pub fn queue_prepass_material_meshes<M: Material>(
draw_function: alpha_mask_draw_prepass,
pipeline_id,
distance,
per_object_binding_dynamic_offset: batch_indices
.dynamic_offset
.unwrap_or_default(),
});
}
AlphaMode::Blend
Expand Down
27 changes: 23 additions & 4 deletions crates/bevy_pbr/src/prepass/prepass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// Most of these attributes are not used in the default prepass fragment shader, but they are still needed so we can
// pass them to custom prepass shaders like pbr_prepass.wgsl.
struct Vertex {
@builtin(instance_index) instance_index: u32,
@location(0) position: vec3<f32>,

#ifdef VERTEX_UVS
Expand Down Expand Up @@ -88,7 +89,9 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef SKINNED
var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
#else // SKINNED
var model = mesh.model;
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416
var model = mesh[vertex_no_morph.instance_index].model;
#endif // SKINNED

out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0));
Expand All @@ -105,17 +108,33 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef SKINNED
out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
#else // SKINNED
out.world_normal = bevy_pbr::mesh_functions::mesh_normal_local_to_world(vertex.normal);
out.world_normal = bevy_pbr::mesh_functions::mesh_normal_local_to_world(
vertex.normal,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416
vertex_no_morph.instance_index
);
#endif // SKINNED

#ifdef VERTEX_TANGENTS
out.world_tangent = bevy_pbr::mesh_functions::mesh_tangent_local_to_world(model, vertex.tangent);
out.world_tangent = bevy_pbr::mesh_functions::mesh_tangent_local_to_world(
model,
vertex.tangent,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416
vertex_no_morph.instance_index
);
#endif // VERTEX_TANGENTS
#endif // NORMAL_PREPASS

#ifdef MOTION_VECTOR_PREPASS
out.world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(mesh.previous_model, vec4<f32>(vertex.position, 1.0));
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416
out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(
mesh[vertex_no_morph.instance_index].previous_model,
vec4<f32>(vertex.position, 1.0)
);
#endif // MOTION_VECTOR_PREPASS

return out;
Expand Down
3 changes: 1 addition & 2 deletions crates/bevy_pbr/src/prepass/prepass_bindings.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ var<uniform> previous_view_proj: mat4x4<f32>;

// Material bindings will be in @group(1)

@group(2) @binding(0)
var<uniform> mesh: bevy_pbr::mesh_types::Mesh;
#import bevy_pbr::mesh_bindings mesh
Loading
Loading