Port bevy_core_pipeline to LinearRgba (#12116)

# Objective

- We should move towards a consistent use of the new `bevy_color` crate.
- As discussed in #12089, splitting this work up into small pieces makes
it easier to review.

## Solution

- Port all uses of `LegacyColor` in the `bevy_core_pipeline` to
`LinearRgba`
- `LinearRgba` is the correct type to use for internal rendering types
- Added `LinearRgba::BLACK` and `WHITE` (used during migration)
- Add `LinearRgba::grey` to more easily construct balanced grey colors
(used during migration)
- Add a conversion from `LinearRgba` to `wgpu::Color`. The converse was
not done at this time, as this is typically a user error.

I did not change the field type of the clear color on the cameras: as
this is user-facing, this should be done in concert with the other
configurable fields.

## Migration Guide

`ColorAttachment` now stores a `LinearRgba` color, rather than a Bevy
0.13 `Color`.
`set_blend_constant` now takes a `LinearRgba` argument, rather than a
Bevy 0.13 `Color`.

---------

Co-authored-by: Alice Cecile <alice.i.cecil@gmail.com>
This commit is contained in:
Alice Cecile 2024-02-26 07:25:11 -05:00 committed by GitHub
parent 43b859dfcf
commit 8ec65525ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 71 additions and 24 deletions

View file

@ -15,6 +15,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.14.0-dev", features = [
] } ] }
serde = "1.0" serde = "1.0"
thiserror = "1.0" thiserror = "1.0"
wgpu = { version = "0.19.1", default-features = false }
[lints] [lints]
workspace = true workspace = true

View file

@ -23,6 +23,30 @@ pub struct LinearRgba {
impl StandardColor for LinearRgba {} impl StandardColor for LinearRgba {}
impl LinearRgba { impl LinearRgba {
/// A fully black color with full alpha.
pub const BLACK: Self = Self {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 1.0,
};
/// A fully white color with full alpha.
pub const WHITE: Self = Self {
red: 1.0,
green: 1.0,
blue: 1.0,
alpha: 1.0,
};
/// A fully transparent color.
pub const NONE: Self = Self {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
};
/// Construct a new [`LinearRgba`] color from components. /// Construct a new [`LinearRgba`] color from components.
pub const fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self { pub const fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
Self { Self {
@ -49,6 +73,18 @@ impl LinearRgba {
} }
} }
/// Construct a new [`LinearRgba`] color with the same value for all channels and an alpha of 1.0.
///
/// A value of 0.0 is black, and a value of 1.0 is white.
pub const fn gray(value: f32) -> Self {
Self {
red: value,
green: value,
blue: value,
alpha: 1.0,
}
}
/// Return a copy of this color with the red channel set to the given value. /// Return a copy of this color with the red channel set to the given value.
pub const fn with_red(self, red: f32) -> Self { pub const fn with_red(self, red: f32) -> Self {
Self { red, ..self } Self { red, ..self }
@ -81,12 +117,7 @@ impl LinearRgba {
impl Default for LinearRgba { impl Default for LinearRgba {
/// Construct a new [`LinearRgba`] color with the default values (white with full alpha). /// Construct a new [`LinearRgba`] color with the default values (white with full alpha).
fn default() -> Self { fn default() -> Self {
Self { Self::WHITE
red: 1.,
green: 1.,
blue: 1.,
alpha: 1.,
}
} }
} }
@ -205,6 +236,17 @@ impl From<Hsla> for LinearRgba {
} }
} }
impl From<LinearRgba> for wgpu::Color {
fn from(color: LinearRgba) -> Self {
wgpu::Color {
r: color.red as f64,
g: color.green as f64,
b: color.blue as f64,
a: color.alpha as f64,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -24,6 +24,7 @@ tonemapping_luts = ["bevy_render/ktx2", "bevy_render/zstd"]
bevy_app = { path = "../bevy_app", version = "0.14.0-dev" } bevy_app = { path = "../bevy_app", version = "0.14.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.14.0-dev" } bevy_asset = { path = "../bevy_asset", version = "0.14.0-dev" }
bevy_core = { path = "../bevy_core", version = "0.14.0-dev" } bevy_core = { path = "../bevy_core", version = "0.14.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.14.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.14.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.14.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" }
bevy_log = { path = "../bevy_log", version = "0.14.0-dev" } bevy_log = { path = "../bevy_log", version = "0.14.0-dev" }

View file

@ -2,6 +2,7 @@ mod downsampling_pipeline;
mod settings; mod settings;
mod upsampling_pipeline; mod upsampling_pipeline;
use bevy_color::LinearRgba;
pub use settings::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings}; pub use settings::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings};
use crate::{ use crate::{
@ -17,7 +18,6 @@ use bevy_render::{
extract_component::{ extract_component::{
ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin, ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin,
}, },
prelude::LegacyColor,
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner}, render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
render_resource::*, render_resource::*,
renderer::{RenderContext, RenderDevice}, renderer::{RenderContext, RenderDevice},
@ -244,7 +244,7 @@ impl ViewNode for BloomNode {
mip as f32, mip as f32,
(bloom_texture.mip_count - 1) as f32, (bloom_texture.mip_count - 1) as f32,
); );
upsampling_pass.set_blend_constant(LegacyColor::rgb_linear(blend, blend, blend)); upsampling_pass.set_blend_constant(LinearRgba::gray(blend));
upsampling_pass.draw(0..3, 0..1); upsampling_pass.draw(0..3, 0..1);
} }
@ -271,7 +271,7 @@ impl ViewNode for BloomNode {
} }
let blend = let blend =
compute_blend_factor(bloom_settings, 0.0, (bloom_texture.mip_count - 1) as f32); compute_blend_factor(bloom_settings, 0.0, (bloom_texture.mip_count - 1) as f32);
upsampling_final_pass.set_blend_constant(LegacyColor::rgb_linear(blend, blend, blend)); upsampling_final_pass.set_blend_constant(LinearRgba::gray(blend));
upsampling_final_pass.draw(0..3, 0..1); upsampling_final_pass.draw(0..3, 0..1);
} }

View file

@ -41,6 +41,7 @@ pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
use std::ops::Range; use std::ops::Range;
use bevy_asset::AssetId; use bevy_asset::AssetId;
use bevy_color::LinearRgba;
pub use camera_3d::*; pub use camera_3d::*;
pub use main_opaque_pass_3d_node::*; pub use main_opaque_pass_3d_node::*;
pub use main_transparent_pass_3d_node::*; pub use main_transparent_pass_3d_node::*;
@ -49,7 +50,6 @@ use bevy_app::{App, Plugin, PostUpdate};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_render::{ use bevy_render::{
camera::{Camera, ExtractedCamera}, camera::{Camera, ExtractedCamera},
color::LegacyColor,
extract_component::ExtractComponentPlugin, extract_component::ExtractComponentPlugin,
mesh::Mesh, mesh::Mesh,
prelude::Msaa, prelude::Msaa,
@ -836,18 +836,18 @@ pub fn prepare_prepass_textures(
commands.entity(entity).insert(ViewPrepassTextures { commands.entity(entity).insert(ViewPrepassTextures {
depth: cached_depth_texture depth: cached_depth_texture
.map(|t| ColorAttachment::new(t, None, Some(LegacyColor::BLACK))), .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
normal: cached_normals_texture normal: cached_normals_texture
.map(|t| ColorAttachment::new(t, None, Some(LegacyColor::BLACK))), .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
// Red and Green channels are X and Y components of the motion vectors // Red and Green channels are X and Y components of the motion vectors
// Blue channel doesn't matter, but set to 0.0 for possible faster clear // Blue channel doesn't matter, but set to 0.0 for possible faster clear
// https://gpuopen.com/performance/#clears // https://gpuopen.com/performance/#clears
motion_vectors: cached_motion_vectors_texture motion_vectors: cached_motion_vectors_texture
.map(|t| ColorAttachment::new(t, None, Some(LegacyColor::BLACK))), .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
deferred: cached_deferred_texture deferred: cached_deferred_texture
.map(|t| ColorAttachment::new(t, None, Some(LegacyColor::BLACK))), .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
deferred_lighting_pass_id: cached_deferred_lighting_pass_id_texture deferred_lighting_pass_id: cached_deferred_lighting_pass_id_texture
.map(|t| ColorAttachment::new(t, None, Some(LegacyColor::BLACK))), .map(|t| ColorAttachment::new(t, None, Some(LinearRgba::BLACK))),
size, size,
}); });
} }

View file

@ -4,10 +4,10 @@ use crate::{
core_3d::graph::{Core3d, Node3d}, core_3d::graph::{Core3d, Node3d},
}; };
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_color::LinearRgba;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_render::{ use bevy_render::{
camera::ExtractedCamera, camera::ExtractedCamera,
color::LegacyColor,
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext}, render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
renderer::RenderContext, renderer::RenderContext,
view::{Msaa, ViewTarget}, view::{Msaa, ViewTarget},
@ -92,7 +92,7 @@ impl Node for MsaaWritebackNode {
view: target.sampled_main_texture_view().unwrap(), view: target.sampled_main_texture_view().unwrap(),
resolve_target: Some(post_process.destination), resolve_target: Some(post_process.destination),
ops: Operations { ops: Operations {
load: LoadOp::Clear(LegacyColor::BLACK.into()), load: LoadOp::Clear(LinearRgba::BLACK.into()),
store: StoreOp::Store, store: StoreOp::Store,
}, },
})], })],

View file

@ -1,12 +1,12 @@
use crate::{ use crate::{
camera::Viewport, camera::Viewport,
prelude::LegacyColor,
render_resource::{ render_resource::{
BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId, BindGroup, BindGroupId, Buffer, BufferId, BufferSlice, RenderPipeline, RenderPipelineId,
ShaderStages, ShaderStages,
}, },
renderer::RenderDevice, renderer::RenderDevice,
}; };
use bevy_color::LinearRgba;
use bevy_utils::{default, detailed_trace}; use bevy_utils::{default, detailed_trace};
use std::ops::Range; use std::ops::Range;
use wgpu::{IndexFormat, RenderPass}; use wgpu::{IndexFormat, RenderPass};
@ -598,7 +598,7 @@ impl<'a> TrackedRenderPass<'a> {
/// Sets the blend color as used by some of the blending modes. /// Sets the blend color as used by some of the blending modes.
/// ///
/// Subsequent blending tests will test against this value. /// Subsequent blending tests will test against this value.
pub fn set_blend_constant(&mut self, color: LegacyColor) { pub fn set_blend_constant(&mut self, color: LinearRgba) {
detailed_trace!("set blend constant: {:?}", color); detailed_trace!("set blend constant: {:?}", color);
self.pass.set_blend_constant(wgpu::Color::from(color)); self.pass.set_blend_constant(wgpu::Color::from(color));
} }

View file

@ -1,5 +1,6 @@
use super::CachedTexture; use super::CachedTexture;
use crate::{prelude::LegacyColor, render_resource::TextureView}; use crate::render_resource::TextureView;
use bevy_color::LinearRgba;
use std::sync::{ use std::sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc,
@ -13,7 +14,7 @@ use wgpu::{
pub struct ColorAttachment { pub struct ColorAttachment {
pub texture: CachedTexture, pub texture: CachedTexture,
pub resolve_target: Option<CachedTexture>, pub resolve_target: Option<CachedTexture>,
clear_color: Option<LegacyColor>, clear_color: Option<LinearRgba>,
is_first_call: Arc<AtomicBool>, is_first_call: Arc<AtomicBool>,
} }
@ -21,7 +22,7 @@ impl ColorAttachment {
pub fn new( pub fn new(
texture: CachedTexture, texture: CachedTexture,
resolve_target: Option<CachedTexture>, resolve_target: Option<CachedTexture>,
clear_color: Option<LegacyColor>, clear_color: Option<LinearRgba>,
) -> Self { ) -> Self {
Self { Self {
texture, texture,

View file

@ -551,9 +551,11 @@ pub fn prepare_view_targets(
(a, b, sampled, main_texture) (a, b, sampled, main_texture)
}); });
let converted_clear_color = clear_color.map(|color| color.into());
let main_textures = MainTargetTextures { let main_textures = MainTargetTextures {
a: ColorAttachment::new(a.clone(), sampled.clone(), clear_color), a: ColorAttachment::new(a.clone(), sampled.clone(), converted_clear_color),
b: ColorAttachment::new(b.clone(), sampled.clone(), clear_color), b: ColorAttachment::new(b.clone(), sampled.clone(), converted_clear_color),
main_texture: main_texture.clone(), main_texture: main_texture.clone(),
}; };