Skip to content

Commit

Permalink
Add 2d meshes and materials (#3460)
Browse files Browse the repository at this point in the history
# 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]>
  • Loading branch information
Davier and cart committed Jan 8, 2022
1 parent 32f7997 commit c2da780
Show file tree
Hide file tree
Showing 29 changed files with 2,257 additions and 355 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ path = "examples/2d/contributors.rs"
name = "many_sprites"
path = "examples/2d/many_sprites.rs"

[[example]]
name = "mesh2d"
path = "examples/2d/mesh2d.rs"

[[example]]
name = "mesh2d_manual"
path = "examples/2d/mesh2d_manual.rs"

[[example]]
name = "rect"
path = "examples/2d/rect.rs"
Expand Down
48 changes: 45 additions & 3 deletions crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub use main_pass_2d::*;
pub use main_pass_3d::*;
pub use main_pass_driver::*;

use std::ops::Range;

use bevy_app::{App, Plugin};
use bevy_core::FloatOrd;
use bevy_ecs::prelude::*;
Expand All @@ -23,8 +25,8 @@ use bevy_render::{
color::Color,
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_phase::{
sort_phase_system, CachedPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem,
PhaseItem, RenderPhase,
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedPipelinePhaseItem,
DrawFunctionId, DrawFunctions, EntityPhaseItem, PhaseItem, RenderPhase,
},
render_resource::*,
renderer::RenderDevice,
Expand Down Expand Up @@ -84,6 +86,11 @@ pub mod clear_graph {
#[derive(Default)]
pub struct CorePipelinePlugin;

#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
pub enum CorePipelineRenderSystems {
SortTransparent2d,
}

impl Plugin for CorePipelinePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ClearColor>();
Expand All @@ -97,7 +104,16 @@ impl Plugin for CorePipelinePlugin {
.add_system_to_stage(RenderStage::Extract, extract_clear_color)
.add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases)
.add_system_to_stage(RenderStage::Prepare, prepare_core_views_system)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent2d>)
.add_system_to_stage(
RenderStage::PhaseSort,
sort_phase_system::<Transparent2d>
.label(CorePipelineRenderSystems::SortTransparent2d),
)
.add_system_to_stage(
RenderStage::PhaseSort,
batch_phase_system::<Transparent2d>
.after(CorePipelineRenderSystems::SortTransparent2d),
)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Opaque3d>)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<AlphaMask3d>)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent3d>);
Expand Down Expand Up @@ -160,6 +176,8 @@ pub struct Transparent2d {
pub entity: Entity,
pub pipeline: CachedPipelineId,
pub draw_function: DrawFunctionId,
/// Range in the vertex buffer of this item
pub batch_range: Option<Range<u32>>,
}

impl PhaseItem for Transparent2d {
Expand All @@ -176,6 +194,30 @@ impl PhaseItem for Transparent2d {
}
}

impl EntityPhaseItem for Transparent2d {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedPipelinePhaseItem for Transparent2d {
#[inline]
fn cached_pipeline(&self) -> CachedPipelineId {
self.pipeline
}
}

impl BatchedPhaseItem for Transparent2d {
fn batch_range(&self) -> &Option<Range<u32>> {
&self.batch_range
}

fn batch_range_mut(&mut self) -> &mut Option<Range<u32>> {
&mut self.batch_range
}
}

pub struct Opaque3d {
pub distance: f32,
pub pipeline: CachedPipelineId,
Expand Down
14 changes: 5 additions & 9 deletions crates/bevy_core_pipeline/src/main_pass_2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use bevy_ecs::prelude::*;
use bevy_render::{
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
render_resource::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor},
render_resource::{LoadOp, Operations, RenderPassDescriptor},
renderer::RenderContext,
view::{ExtractedView, ViewTarget},
};
Expand Down Expand Up @@ -46,14 +46,10 @@ impl Node for MainPass2dNode {

let pass_descriptor = RenderPassDescriptor {
label: Some("main_pass_2d"),
color_attachments: &[RenderPassColorAttachment {
view: &target.view,
resolve_target: None,
ops: Operations {
load: LoadOp::Load,
store: true,
},
}],
color_attachments: &[target.get_color_attachment(Operations {
load: LoadOp::Load,
store: true,
})],
depth_stencil_attachment: None,
};

Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bevy_ecs::{
prelude::*,
system::{lifetimeless::*, SystemParamItem},
};
use bevy_math::Mat4;
use bevy_math::{Mat4, Size};
use bevy_reflect::TypeUuid;
use bevy_render::{
mesh::{GpuBufferInfo, Mesh},
Expand Down Expand Up @@ -328,6 +328,10 @@ impl FromWorld for MeshPipeline {
texture,
texture_view,
sampler,
size: Size::new(
image.texture_descriptor.size.width as f32,
image.texture_descriptor.size.height as f32,
),
}
};
MeshPipeline {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ hex = "0.4.2"
hexasphere = "6.0.0"
parking_lot = "0.11.0"
regex = "1.5"
copyless = "0.1.5"
45 changes: 44 additions & 1 deletion crates/bevy_render/src/render_phase/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use bevy_ecs::{
};
use bevy_utils::HashMap;
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::{any::TypeId, fmt::Debug, hash::Hash};
use std::{any::TypeId, fmt::Debug, hash::Hash, ops::Range};

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

/// A [`PhaseItem`] that can be batched dynamically.
///
/// Batching is an optimization that regroups multiple items in the same vertex buffer
/// to render them in a single draw call.
pub trait BatchedPhaseItem: EntityPhaseItem {
/// Range in the vertex buffer of this item
fn batch_range(&self) -> &Option<Range<u32>>;

/// Range in the vertex buffer of this item
fn batch_range_mut(&mut self) -> &mut Option<Range<u32>>;

/// Batches another item within this item if they are compatible.
/// Items can be batched together if they have the same entity, and consecutive ranges.
/// If batching is successful, the `other` item should be discarded from the render pass.
#[inline]
fn add_to_batch(&mut self, other: &Self) -> BatchResult {
let self_entity = self.entity();
if let (Some(self_batch_range), Some(other_batch_range)) = (
self.batch_range_mut().as_mut(),
other.batch_range().as_ref(),
) {
// If the items are compatible, join their range into `self`
if self_entity == other.entity() {
if self_batch_range.end == other_batch_range.start {
self_batch_range.end = other_batch_range.end;
return BatchResult::Success;
} else if self_batch_range.start == other_batch_range.end {
self_batch_range.start = other_batch_range.start;
return BatchResult::Success;
}
}
}
BatchResult::IncompatibleItems
}
}

pub enum BatchResult {
/// The `other` item was batched into `self`
Success,
/// `self` and `other` cannot be batched together
IncompatibleItems,
}

impl<P: EntityPhaseItem, E: EntityRenderCommand> RenderCommand<P> for E {
type Param = E::Param;

Expand Down
Loading

0 comments on commit c2da780

Please sign in to comment.