Skip to content

Commit c2da780

Browse files
Daviercart
andcommitted
Add 2d meshes and materials (#3460)
# Objective The current 2d rendering is specialized to render sprites, we need a generic way to render 2d items, using meshes and materials like we have for 3d. ## Solution I cloned a good part of `bevy_pbr` into `bevy_sprite/src/mesh2d`, removed lighting and pbr itself, adapted it to 2d rendering, added a `ColorMaterial`, and modified the sprite rendering to break batches around 2d meshes. ~~The PR is a bit crude; I tried to change as little as I could in both the parts copied from 3d and the current sprite rendering to make reviewing easier. In the future, I expect we could make the sprite rendering a normal 2d material, cleanly integrated with the rest.~~ _edit: see <https://github.com/bevyengine/bevy/pull/3460#issuecomment-1003605194>_ ## Remaining work - ~~don't require mesh normals~~ _out of scope_ - ~~add an example~~ _done_ - support 2d meshes & materials in the UI? - bikeshed names (I didn't think hard about naming, please check if it's fine) ## Remaining questions - ~~should we add a depth buffer to 2d now that there are 2d meshes?~~ _let's revisit that when we have an opaque render phase_ - ~~should we add MSAA support to the sprites, or remove it from the 2d meshes?~~ _I added MSAA to sprites since it's really needed for 2d meshes_ - ~~how to customize vertex attributes?~~ _#3120_ Co-authored-by: Carter Anderson <[email protected]>
1 parent 32f7997 commit c2da780

File tree

29 files changed

+2257
-355
lines changed

29 files changed

+2257
-355
lines changed

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ path = "examples/2d/contributors.rs"
119119
name = "many_sprites"
120120
path = "examples/2d/many_sprites.rs"
121121

122+
[[example]]
123+
name = "mesh2d"
124+
path = "examples/2d/mesh2d.rs"
125+
126+
[[example]]
127+
name = "mesh2d_manual"
128+
path = "examples/2d/mesh2d_manual.rs"
129+
122130
[[example]]
123131
name = "rect"
124132
path = "examples/2d/rect.rs"

crates/bevy_core_pipeline/src/lib.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub use main_pass_2d::*;
1515
pub use main_pass_3d::*;
1616
pub use main_pass_driver::*;
1717

18+
use std::ops::Range;
19+
1820
use bevy_app::{App, Plugin};
1921
use bevy_core::FloatOrd;
2022
use bevy_ecs::prelude::*;
@@ -23,8 +25,8 @@ use bevy_render::{
2325
color::Color,
2426
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
2527
render_phase::{
26-
sort_phase_system, CachedPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem,
27-
PhaseItem, RenderPhase,
28+
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedPipelinePhaseItem,
29+
DrawFunctionId, DrawFunctions, EntityPhaseItem, PhaseItem, RenderPhase,
2830
},
2931
render_resource::*,
3032
renderer::RenderDevice,
@@ -84,6 +86,11 @@ pub mod clear_graph {
8486
#[derive(Default)]
8587
pub struct CorePipelinePlugin;
8688

89+
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
90+
pub enum CorePipelineRenderSystems {
91+
SortTransparent2d,
92+
}
93+
8794
impl Plugin for CorePipelinePlugin {
8895
fn build(&self, app: &mut App) {
8996
app.init_resource::<ClearColor>();
@@ -97,7 +104,16 @@ impl Plugin for CorePipelinePlugin {
97104
.add_system_to_stage(RenderStage::Extract, extract_clear_color)
98105
.add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases)
99106
.add_system_to_stage(RenderStage::Prepare, prepare_core_views_system)
100-
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent2d>)
107+
.add_system_to_stage(
108+
RenderStage::PhaseSort,
109+
sort_phase_system::<Transparent2d>
110+
.label(CorePipelineRenderSystems::SortTransparent2d),
111+
)
112+
.add_system_to_stage(
113+
RenderStage::PhaseSort,
114+
batch_phase_system::<Transparent2d>
115+
.after(CorePipelineRenderSystems::SortTransparent2d),
116+
)
101117
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Opaque3d>)
102118
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<AlphaMask3d>)
103119
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent3d>);
@@ -160,6 +176,8 @@ pub struct Transparent2d {
160176
pub entity: Entity,
161177
pub pipeline: CachedPipelineId,
162178
pub draw_function: DrawFunctionId,
179+
/// Range in the vertex buffer of this item
180+
pub batch_range: Option<Range<u32>>,
163181
}
164182

165183
impl PhaseItem for Transparent2d {
@@ -176,6 +194,30 @@ impl PhaseItem for Transparent2d {
176194
}
177195
}
178196

197+
impl EntityPhaseItem for Transparent2d {
198+
#[inline]
199+
fn entity(&self) -> Entity {
200+
self.entity
201+
}
202+
}
203+
204+
impl CachedPipelinePhaseItem for Transparent2d {
205+
#[inline]
206+
fn cached_pipeline(&self) -> CachedPipelineId {
207+
self.pipeline
208+
}
209+
}
210+
211+
impl BatchedPhaseItem for Transparent2d {
212+
fn batch_range(&self) -> &Option<Range<u32>> {
213+
&self.batch_range
214+
}
215+
216+
fn batch_range_mut(&mut self) -> &mut Option<Range<u32>> {
217+
&mut self.batch_range
218+
}
219+
}
220+
179221
pub struct Opaque3d {
180222
pub distance: f32,
181223
pub pipeline: CachedPipelineId,

crates/bevy_core_pipeline/src/main_pass_2d.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use bevy_ecs::prelude::*;
33
use bevy_render::{
44
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
55
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
6-
render_resource::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor},
6+
render_resource::{LoadOp, Operations, RenderPassDescriptor},
77
renderer::RenderContext,
88
view::{ExtractedView, ViewTarget},
99
};
@@ -46,14 +46,10 @@ impl Node for MainPass2dNode {
4646

4747
let pass_descriptor = RenderPassDescriptor {
4848
label: Some("main_pass_2d"),
49-
color_attachments: &[RenderPassColorAttachment {
50-
view: &target.view,
51-
resolve_target: None,
52-
ops: Operations {
53-
load: LoadOp::Load,
54-
store: true,
55-
},
56-
}],
49+
color_attachments: &[target.get_color_attachment(Operations {
50+
load: LoadOp::Load,
51+
store: true,
52+
})],
5753
depth_stencil_attachment: None,
5854
};
5955

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use bevy_ecs::{
88
prelude::*,
99
system::{lifetimeless::*, SystemParamItem},
1010
};
11-
use bevy_math::Mat4;
11+
use bevy_math::{Mat4, Size};
1212
use bevy_reflect::TypeUuid;
1313
use bevy_render::{
1414
mesh::{GpuBufferInfo, Mesh},
@@ -328,6 +328,10 @@ impl FromWorld for MeshPipeline {
328328
texture,
329329
texture_view,
330330
sampler,
331+
size: Size::new(
332+
image.texture_descriptor.size.width as f32,
333+
image.texture_descriptor.size.height as f32,
334+
),
331335
}
332336
};
333337
MeshPipeline {

crates/bevy_render/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ hex = "0.4.2"
5353
hexasphere = "6.0.0"
5454
parking_lot = "0.11.0"
5555
regex = "1.5"
56+
copyless = "0.1.5"

crates/bevy_render/src/render_phase/draw.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bevy_ecs::{
1313
};
1414
use bevy_utils::HashMap;
1515
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
16-
use std::{any::TypeId, fmt::Debug, hash::Hash};
16+
use std::{any::TypeId, fmt::Debug, hash::Hash, ops::Range};
1717

1818
/// A draw function which is used to draw a specific [`PhaseItem`].
1919
///
@@ -166,6 +166,49 @@ pub trait CachedPipelinePhaseItem: PhaseItem {
166166
fn cached_pipeline(&self) -> CachedPipelineId;
167167
}
168168

169+
/// A [`PhaseItem`] that can be batched dynamically.
170+
///
171+
/// Batching is an optimization that regroups multiple items in the same vertex buffer
172+
/// to render them in a single draw call.
173+
pub trait BatchedPhaseItem: EntityPhaseItem {
174+
/// Range in the vertex buffer of this item
175+
fn batch_range(&self) -> &Option<Range<u32>>;
176+
177+
/// Range in the vertex buffer of this item
178+
fn batch_range_mut(&mut self) -> &mut Option<Range<u32>>;
179+
180+
/// Batches another item within this item if they are compatible.
181+
/// Items can be batched together if they have the same entity, and consecutive ranges.
182+
/// If batching is successful, the `other` item should be discarded from the render pass.
183+
#[inline]
184+
fn add_to_batch(&mut self, other: &Self) -> BatchResult {
185+
let self_entity = self.entity();
186+
if let (Some(self_batch_range), Some(other_batch_range)) = (
187+
self.batch_range_mut().as_mut(),
188+
other.batch_range().as_ref(),
189+
) {
190+
// If the items are compatible, join their range into `self`
191+
if self_entity == other.entity() {
192+
if self_batch_range.end == other_batch_range.start {
193+
self_batch_range.end = other_batch_range.end;
194+
return BatchResult::Success;
195+
} else if self_batch_range.start == other_batch_range.end {
196+
self_batch_range.start = other_batch_range.start;
197+
return BatchResult::Success;
198+
}
199+
}
200+
}
201+
BatchResult::IncompatibleItems
202+
}
203+
}
204+
205+
pub enum BatchResult {
206+
/// The `other` item was batched into `self`
207+
Success,
208+
/// `self` and `other` cannot be batched together
209+
IncompatibleItems,
210+
}
211+
169212
impl<P: EntityPhaseItem, E: EntityRenderCommand> RenderCommand<P> for E {
170213
type Param = E::Param;
171214

0 commit comments

Comments
 (0)