Skip to content

Commit

Permalink
Use bitbybit to encode the MeshPipelineKey
Browse files Browse the repository at this point in the history
This removes all the maths around bit shifting and masking, it also
makes reading key flags easier, and setting key flags easier.
  • Loading branch information
nicopap committed Oct 27, 2023
1 parent b22db47 commit a8120b0
Show file tree
Hide file tree
Showing 16 changed files with 397 additions and 564 deletions.
2 changes: 2 additions & 0 deletions crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ bevy_transform = { path = "../bevy_transform", version = "0.12.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.12.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }

arbitrary-int = "1.2.6"
bitbybit = "1.2.2"
serde = { version = "1", features = ["derive"] }
bitflags = "2.3"
radsort = "0.1"
86 changes: 37 additions & 49 deletions crates/bevy_core_pipeline/src/tonemapping/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,37 +114,36 @@ pub struct TonemappingPipeline {
}

/// Optionally enables a tonemapping shader that attempts to map linear input stimulus into a perceptually uniform image for a given [`Camera`] entity.
#[derive(
Component, Debug, Hash, Clone, Copy, Reflect, Default, ExtractComponent, PartialEq, Eq,
)]
#[derive(Component, Debug, Hash, Reflect, Default, ExtractComponent, PartialEq, Eq)]
#[extract_component_filter(With<Camera>)]
#[reflect(Component)]
#[bitbybit::bitenum(u3, exhaustive: true)]
pub enum Tonemapping {
/// Bypass tonemapping.
None,
None = 0,
/// Suffers from lots hue shifting, brights don't desaturate naturally.
/// Bright primaries and secondaries don't desaturate at all.
Reinhard,
Reinhard = 1,
/// Suffers from hue shifting. Brights don't desaturate much at all across the spectrum.
ReinhardLuminance,
ReinhardLuminance = 2,
/// Same base implementation that Godot 4.0 uses for Tonemap ACES.
/// <https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl>
/// Not neutral, has a very specific aesthetic, intentional and dramatic hue shifting.
/// Bright greens and reds turn orange. Bright blues turn magenta.
/// Significantly increased contrast. Brights desaturate across the spectrum.
AcesFitted,
AcesFitted = 3,
/// By Troy Sobotka
/// <https://github.com/sobotka/AgX>
/// Very neutral. Image is somewhat desaturated when compared to other tonemappers.
/// Little to no hue shifting. Subtle [Abney shifting](https://en.wikipedia.org/wiki/Abney_effect).
/// NOTE: Requires the `tonemapping_luts` cargo feature.
AgX,
AgX = 4,
/// By Tomasz Stachowiak
/// Has little hue shifting in the darks and mids, but lots in the brights. Brights desaturate across the spectrum.
/// Is sort of between Reinhard and ReinhardLuminance. Conceptually similar to reinhard-jodie.
/// Designed as a compromise if you want e.g. decent skin tones in low light, but can't afford to re-do your
/// VFX to look good without hue shifting.
SomewhatBoringDisplayTransform,
SomewhatBoringDisplayTransform = 5,
/// Current Bevy default.
/// By Tomasz Stachowiak
/// <https://github.com/h3r2tic/tony-mc-mapface>
Expand All @@ -158,17 +157,42 @@ pub enum Tonemapping {
/// To avoid posterization, selective desaturation is employed, with care to avoid the [Abney effect](https://en.wikipedia.org/wiki/Abney_effect).
/// NOTE: Requires the `tonemapping_luts` cargo feature.
#[default]
TonyMcMapface,
TonyMcMapface = 6,
/// Default Filmic Display Transform from blender.
/// Somewhat neutral. Suffers from hue shifting. Brights desaturate across the spectrum.
/// NOTE: Requires the `tonemapping_luts` cargo feature.
BlenderFilmic,
BlenderFilmic = 7,
}

impl Tonemapping {
pub fn is_enabled(&self) -> bool {
*self != Tonemapping::None
}
pub const fn define(self) -> &'static str {
use Tonemapping::SomewhatBoringDisplayTransform;

match self {
Self::None => "TONEMAP_METHOD_NONE",
Self::Reinhard => "TONEMAP_METHOD_REINHARD",
Self::ReinhardLuminance => "TONEMAP_METHOD_REINHARD_LUMINANCE",
Self::AcesFitted => "TONEMAP_METHOD_ACES_FITTED",
Self::AgX => "TONEMAP_METHOD_AGX",
SomewhatBoringDisplayTransform => "TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM",
Self::TonyMcMapface => "TONEMAP_METHOD_TONY_MC_MAPFACE",
Self::BlenderFilmic => "TONEMAP_METHOD_BLENDER_FILMIC",
}
}

pub fn log_feature_error(self) {
#[cfg(not(feature = "tonemapping_luts"))]
if matches!(self, Self::AgX | Self::TonyMcMapface | Self::BlenderFilmic) {
bevy_log::error!(
"{self:?} tonemapping requires the `tonemapping_luts` feature. Either enable the \
`tonemapping_luts` feature for bevy in `Cargo.toml` (recommended), or use a \
different `Tonemapping` method in your `Camera2dBundle`/`Camera3dBundle`."
);
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
Expand All @@ -185,45 +209,9 @@ impl SpecializedRenderPipeline for TonemappingPipeline {
if let DebandDither::Enabled = key.deband_dither {
shader_defs.push("DEBAND_DITHER".into());
}
key.tonemapping.log_feature_error();
shader_defs.push(key.tonemapping.define().into());

match key.tonemapping {
Tonemapping::None => shader_defs.push("TONEMAP_METHOD_NONE".into()),
Tonemapping::Reinhard => shader_defs.push("TONEMAP_METHOD_REINHARD".into()),
Tonemapping::ReinhardLuminance => {
shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
}
Tonemapping::AcesFitted => shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into()),
Tonemapping::AgX => {
#[cfg(not(feature = "tonemapping_luts"))]
bevy_log::error!(
"AgX tonemapping requires the `tonemapping_luts` feature.
Either enable the `tonemapping_luts` feature for bevy in `Cargo.toml` (recommended),
or use a different `Tonemapping` method in your `Camera2dBundle`/`Camera3dBundle`."
);
shader_defs.push("TONEMAP_METHOD_AGX".into());
}
Tonemapping::SomewhatBoringDisplayTransform => {
shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
}
Tonemapping::TonyMcMapface => {
#[cfg(not(feature = "tonemapping_luts"))]
bevy_log::error!(
"TonyMcMapFace tonemapping requires the `tonemapping_luts` feature.
Either enable the `tonemapping_luts` feature for bevy in `Cargo.toml` (recommended),
or use a different `Tonemapping` method in your `Camera2dBundle`/`Camera3dBundle`."
);
shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
}
Tonemapping::BlenderFilmic => {
#[cfg(not(feature = "tonemapping_luts"))]
bevy_log::error!(
"BlenderFilmic tonemapping requires the `tonemapping_luts` feature.
Either enable the `tonemapping_luts` feature for bevy in `Cargo.toml` (recommended),
or use a different `Tonemapping` method in your `Camera2dBundle`/`Camera3dBundle`."
);
shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
}
}
RenderPipelineDescriptor {
label: Some("tonemapping pipeline".into()),
layout: vec![self.texture_bind_group.clone()],
Expand Down
5 changes: 2 additions & 3 deletions crates/bevy_gizmos/src/pipeline_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
shader_defs.push("PERSPECTIVE".into());
}

let format = if key.mesh_key.contains(MeshPipelineKey::HDR) {
let format = if key.mesh_key.hdr() {
ViewTarget::TEXTURE_FORMAT_HDR
} else {
TextureFormat::bevy_default()
Expand Down Expand Up @@ -168,8 +168,7 @@ fn queue_line_gizmos_3d(
continue;
}

let mesh_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
| MeshPipelineKey::from_hdr(view.hdr);
let mesh_key = MeshPipelineKey::DEFAULT.with_msaa(*msaa).with_hdr(view.hdr);

for (entity, handle) in &line_gizmos {
let Some(line_gizmo) = line_gizmo_assets.get(handle) else {
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ bevy_window = { path = "../bevy_window", version = "0.12.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }

# other
arbitrary-int = "1.2.6"
bitbybit = "1.2.2"
bitflags = "2.3"
fixedbitset = "0.4"
# direct dependency required for derive macro
Expand Down
56 changes: 56 additions & 0 deletions crates/bevy_pbr/src/alpha.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
use bevy_render::render_resource::BlendState;
use bitbybit::bitenum;

// TODO: add discussion about performance.
/// Sets how a material's base color alpha channel is used for transparency.
Expand Down Expand Up @@ -47,5 +49,59 @@ pub enum AlphaMode {
/// Useful for effects like stained glass, window tint film and some colored liquids.
Multiply,
}
impl AlphaMode {
pub fn may_discard(self) -> bool {
matches!(self, Self::Mask(_))
}
}

#[bitenum(u2, exhaustive: true)]
#[derive(PartialEq)]
pub enum BlendMode {
Opaque = 0,
PremultipliedAlpha = 1,
Multiply = 2,
Alpha = 3,
}
impl From<AlphaMode> for BlendMode {
fn from(value: AlphaMode) -> Self {
match value {
AlphaMode::Premultiplied | AlphaMode::Add => BlendMode::PremultipliedAlpha,
AlphaMode::Blend => BlendMode::Alpha,
AlphaMode::Multiply => BlendMode::Multiply,
_ => BlendMode::Opaque,
}
}
}
impl BlendMode {
pub fn is_opaque(self) -> bool {
matches!(self, Self::Opaque)
}
pub fn state(self) -> Option<BlendState> {
use bevy_render::render_resource::*;
match self {
BlendMode::PremultipliedAlpha => Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING),
BlendMode::Multiply => Some(BlendState {
color: BlendComponent {
src_factor: BlendFactor::Dst,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent::OVER,
}),
BlendMode::Alpha => Some(BlendState::ALPHA_BLENDING),
BlendMode::Opaque => None,
}
}
pub const fn defines(self) -> Option<[&'static str; 2]> {
match self {
BlendMode::Alpha | BlendMode::Opaque => None,
BlendMode::PremultipliedAlpha => {
Some(["PREMULTIPLY_ALPHA", "BLEND_PREMULTIPLIED_ALPHA"])
}
BlendMode::Multiply => Some(["PREMULTIPLY_ALPHA", "BLEND_MULTIPLY"]),
}
}
}

impl Eq for AlphaMode {}
Loading

0 comments on commit a8120b0

Please sign in to comment.