Apply codebase changes in preparation for StandardMaterial transmission (#8704)
# Objective - Make #8015 easier to review; ## Solution - This commit contains changes not directly related to transmission required by #8015, in easier-to-review, one-change-per-commit form. --- ## Changelog ### Fixed - Clear motion vector prepass using `0.0` instead of `1.0`, to avoid TAA artifacts on transparent objects against the background; ### Added - The `E` mathematical constant is now available for use in shaders, exposed under `bevy_pbr::utils`; - A new `TAA` shader def is now available, for conditionally enabling shader logic via `#ifdef` when TAA is enabled; (e.g. for jittering texture samples) - A new `FallbackImageZero` resource is introduced, for when a fallback image filled with zeroes is required; - A new `RenderPhase<I>::render_range()` method is introduced, for render phases that need to render their items in multiple parceled out “steps”; ### Changed - The `MainTargetTextures` struct now holds both `Texture` and `TextureViews` for the main textures; - The fog shader functions under `bevy_pbr::fog` now take the a `Fog` structure as their first argument, instead of relying on the global `fog` uniform; - The main textures can now be used as copy sources; ## Migration Guide - `ViewTarget::main_texture()` and `ViewTarget::main_texture_other()` now return `&Texture` instead of `&TextureView`. If you were relying on these methods, replace your usage with `ViewTarget::main_texture_view()`and `ViewTarget::main_texture_other_view()`, respectively; - `ViewTarget::sampled_main_texture()` now returns `Option<&Texture>` instead of a `Option<&TextureView>`. If you were relying on this method, replace your usage with `ViewTarget::sampled_main_texture_view()`; - The `apply_fog()`, `linear_fog()`, `exponential_fog()`, `exponential_squared_fog()` and `atmospheric_fog()` functions now take a configurable `Fog` struct. If you were relying on them, update your usage by adding the global `fog` uniform as their first argument;
This commit is contained in:
parent
c8deedb0e1
commit
292e069bb5
@ -177,7 +177,9 @@ impl ViewNode for BloomNode {
|
|||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
// Read from main texture directly
|
// Read from main texture directly
|
||||||
resource: BindingResource::TextureView(view_target.main_texture()),
|
resource: BindingResource::TextureView(
|
||||||
|
view_target.main_texture_view(),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 1,
|
binding: 1,
|
||||||
|
|||||||
@ -67,9 +67,10 @@ impl ViewNode for PrepassNode {
|
|||||||
view: &view_motion_vectors_texture.default_view,
|
view: &view_motion_vectors_texture.default_view,
|
||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
ops: Operations {
|
ops: Operations {
|
||||||
// Blue channel doesn't matter, but set to 1.0 for possible faster clear
|
// 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
|
||||||
// https://gpuopen.com/performance/#clears
|
// https://gpuopen.com/performance/#clears
|
||||||
load: LoadOp::Clear(Color::rgb_linear(1.0, 1.0, 1.0).into()),
|
load: LoadOp::Clear(Color::rgb_linear(0.0, 0.0, 0.0).into()),
|
||||||
store: true,
|
store: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -47,7 +47,7 @@ impl ViewNode for UpscalingNode {
|
|||||||
LoadOp::Clear(Default::default())
|
LoadOp::Clear(Default::default())
|
||||||
};
|
};
|
||||||
|
|
||||||
let upscaled_texture = target.main_texture();
|
let upscaled_texture = target.main_texture_view();
|
||||||
|
|
||||||
let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
|
let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
|
||||||
let bind_group = match &mut *cached_bind_group {
|
let bind_group = match &mut *cached_bind_group {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use bevy_app::{App, Plugin};
|
|||||||
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
|
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
|
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
|
||||||
|
experimental::taa::TemporalAntiAliasSettings,
|
||||||
prepass::NormalPrepass,
|
prepass::NormalPrepass,
|
||||||
tonemapping::{DebandDither, Tonemapping},
|
tonemapping::{DebandDither, Tonemapping},
|
||||||
};
|
};
|
||||||
@ -387,6 +388,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||||||
Option<&DebandDither>,
|
Option<&DebandDither>,
|
||||||
Option<&EnvironmentMapLight>,
|
Option<&EnvironmentMapLight>,
|
||||||
Option<&NormalPrepass>,
|
Option<&NormalPrepass>,
|
||||||
|
Option<&TemporalAntiAliasSettings>,
|
||||||
&mut RenderPhase<Opaque3d>,
|
&mut RenderPhase<Opaque3d>,
|
||||||
&mut RenderPhase<AlphaMask3d>,
|
&mut RenderPhase<AlphaMask3d>,
|
||||||
&mut RenderPhase<Transparent3d>,
|
&mut RenderPhase<Transparent3d>,
|
||||||
@ -401,6 +403,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||||||
dither,
|
dither,
|
||||||
environment_map,
|
environment_map,
|
||||||
normal_prepass,
|
normal_prepass,
|
||||||
|
taa_settings,
|
||||||
mut opaque_phase,
|
mut opaque_phase,
|
||||||
mut alpha_mask_phase,
|
mut alpha_mask_phase,
|
||||||
mut transparent_phase,
|
mut transparent_phase,
|
||||||
@ -417,6 +420,10 @@ pub fn queue_material_meshes<M: Material>(
|
|||||||
view_key |= MeshPipelineKey::NORMAL_PREPASS;
|
view_key |= MeshPipelineKey::NORMAL_PREPASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if taa_settings.is_some() {
|
||||||
|
view_key |= MeshPipelineKey::TAA;
|
||||||
|
}
|
||||||
|
|
||||||
let environment_map_loaded = match environment_map {
|
let environment_map_loaded = match environment_map {
|
||||||
Some(environment_map) => environment_map.is_loaded(&images),
|
Some(environment_map) => environment_map.is_loaded(&images),
|
||||||
None => false,
|
None => false,
|
||||||
|
|||||||
@ -6,61 +6,66 @@
|
|||||||
// https://iquilezles.org/articles/fog/ (Atmospheric Fog and Scattering)
|
// https://iquilezles.org/articles/fog/ (Atmospheric Fog and Scattering)
|
||||||
|
|
||||||
fn scattering_adjusted_fog_color(
|
fn scattering_adjusted_fog_color(
|
||||||
|
fog_params: Fog,
|
||||||
scattering: vec3<f32>,
|
scattering: vec3<f32>,
|
||||||
) -> vec4<f32> {
|
) -> vec4<f32> {
|
||||||
if (fog.directional_light_color.a > 0.0) {
|
if (fog_params.directional_light_color.a > 0.0) {
|
||||||
return vec4<f32>(
|
return vec4<f32>(
|
||||||
fog.base_color.rgb
|
fog_params.base_color.rgb
|
||||||
+ scattering * fog.directional_light_color.rgb * fog.directional_light_color.a,
|
+ scattering * fog_params.directional_light_color.rgb * fog_params.directional_light_color.a,
|
||||||
fog.base_color.a,
|
fog_params.base_color.a,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return fog.base_color;
|
return fog_params.base_color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linear_fog(
|
fn linear_fog(
|
||||||
|
fog_params: Fog,
|
||||||
input_color: vec4<f32>,
|
input_color: vec4<f32>,
|
||||||
distance: f32,
|
distance: f32,
|
||||||
scattering: vec3<f32>,
|
scattering: vec3<f32>,
|
||||||
) -> vec4<f32> {
|
) -> vec4<f32> {
|
||||||
var fog_color = scattering_adjusted_fog_color(scattering);
|
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
|
||||||
let start = fog.be.x;
|
let start = fog_params.be.x;
|
||||||
let end = fog.be.y;
|
let end = fog_params.be.y;
|
||||||
fog_color.a *= 1.0 - clamp((end - distance) / (end - start), 0.0, 1.0);
|
fog_color.a *= 1.0 - clamp((end - distance) / (end - start), 0.0, 1.0);
|
||||||
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
|
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exponential_fog(
|
fn exponential_fog(
|
||||||
|
fog_params: Fog,
|
||||||
input_color: vec4<f32>,
|
input_color: vec4<f32>,
|
||||||
distance: f32,
|
distance: f32,
|
||||||
scattering: vec3<f32>,
|
scattering: vec3<f32>,
|
||||||
) -> vec4<f32> {
|
) -> vec4<f32> {
|
||||||
var fog_color = scattering_adjusted_fog_color(scattering);
|
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
|
||||||
let density = fog.be.x;
|
let density = fog_params.be.x;
|
||||||
fog_color.a *= 1.0 - 1.0 / exp(distance * density);
|
fog_color.a *= 1.0 - 1.0 / exp(distance * density);
|
||||||
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
|
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exponential_squared_fog(
|
fn exponential_squared_fog(
|
||||||
|
fog_params: Fog,
|
||||||
input_color: vec4<f32>,
|
input_color: vec4<f32>,
|
||||||
distance: f32,
|
distance: f32,
|
||||||
scattering: vec3<f32>,
|
scattering: vec3<f32>,
|
||||||
) -> vec4<f32> {
|
) -> vec4<f32> {
|
||||||
var fog_color = scattering_adjusted_fog_color(scattering);
|
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
|
||||||
let distance_times_density = distance * fog.be.x;
|
let distance_times_density = distance * fog_params.be.x;
|
||||||
fog_color.a *= 1.0 - 1.0 / exp(distance_times_density * distance_times_density);
|
fog_color.a *= 1.0 - 1.0 / exp(distance_times_density * distance_times_density);
|
||||||
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
|
return vec4<f32>(mix(input_color.rgb, fog_color.rgb, fog_color.a), input_color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn atmospheric_fog(
|
fn atmospheric_fog(
|
||||||
|
fog_params: Fog,
|
||||||
input_color: vec4<f32>,
|
input_color: vec4<f32>,
|
||||||
distance: f32,
|
distance: f32,
|
||||||
scattering: vec3<f32>,
|
scattering: vec3<f32>,
|
||||||
) -> vec4<f32> {
|
) -> vec4<f32> {
|
||||||
var fog_color = scattering_adjusted_fog_color(scattering);
|
var fog_color = scattering_adjusted_fog_color(fog_params, scattering);
|
||||||
let extinction_factor = 1.0 - 1.0 / exp(distance * fog.be);
|
let extinction_factor = 1.0 - 1.0 / exp(distance * fog_params.be);
|
||||||
let inscattering_factor = 1.0 - 1.0 / exp(distance * fog.bi);
|
let inscattering_factor = 1.0 - 1.0 / exp(distance * fog_params.bi);
|
||||||
return vec4<f32>(
|
return vec4<f32>(
|
||||||
input_color.rgb * (1.0 - extinction_factor * fog_color.a)
|
input_color.rgb * (1.0 - extinction_factor * fog_color.a)
|
||||||
+ fog_color.rgb * inscattering_factor * fog_color.a,
|
+ fog_color.rgb * inscattering_factor * fog_color.a,
|
||||||
|
|||||||
@ -586,6 +586,7 @@ bitflags::bitflags! {
|
|||||||
// See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
|
// See: https://www.khronos.org/opengl/wiki/Early_Fragment_Test
|
||||||
const ENVIRONMENT_MAP = (1 << 7);
|
const ENVIRONMENT_MAP = (1 << 7);
|
||||||
const DEPTH_CLAMP_ORTHO = (1 << 8);
|
const DEPTH_CLAMP_ORTHO = (1 << 8);
|
||||||
|
const TAA = (1 << 9);
|
||||||
const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state
|
const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state
|
||||||
const BLEND_OPAQUE = (0 << Self::BLEND_SHIFT_BITS); // ← Values are just sequential within the mask, and can range from 0 to 3
|
const BLEND_OPAQUE = (0 << Self::BLEND_SHIFT_BITS); // ← Values are just sequential within the mask, and can range from 0 to 3
|
||||||
const BLEND_PREMULTIPLIED_ALPHA = (1 << Self::BLEND_SHIFT_BITS); //
|
const BLEND_PREMULTIPLIED_ALPHA = (1 << Self::BLEND_SHIFT_BITS); //
|
||||||
@ -804,6 +805,10 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
|||||||
shader_defs.push("ENVIRONMENT_MAP".into());
|
shader_defs.push("ENVIRONMENT_MAP".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if key.contains(MeshPipelineKey::TAA) {
|
||||||
|
shader_defs.push("TAA".into());
|
||||||
|
}
|
||||||
|
|
||||||
let format = if key.contains(MeshPipelineKey::HDR) {
|
let format = if key.contains(MeshPipelineKey::HDR) {
|
||||||
ViewTarget::TEXTURE_FORMAT_HDR
|
ViewTarget::TEXTURE_FORMAT_HDR
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -133,7 +133,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
|
|
||||||
// fog
|
// fog
|
||||||
if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
|
if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) {
|
||||||
output_color = apply_fog(output_color, in.world_position.xyz, view.world_position.xyz);
|
output_color = apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TONEMAP_IN_SHADER
|
#ifdef TONEMAP_IN_SHADER
|
||||||
|
|||||||
@ -275,7 +275,7 @@ fn pbr(
|
|||||||
#endif // PREPASS_FRAGMENT
|
#endif // PREPASS_FRAGMENT
|
||||||
|
|
||||||
#ifndef PREPASS_FRAGMENT
|
#ifndef PREPASS_FRAGMENT
|
||||||
fn apply_fog(input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
|
fn apply_fog(fog_params: Fog, input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_world_position: vec3<f32>) -> vec4<f32> {
|
||||||
let view_to_world = fragment_world_position.xyz - view_world_position.xyz;
|
let view_to_world = fragment_world_position.xyz - view_world_position.xyz;
|
||||||
|
|
||||||
// `length()` is used here instead of just `view_to_world.z` since that produces more
|
// `length()` is used here instead of just `view_to_world.z` since that produces more
|
||||||
@ -285,7 +285,7 @@ fn apply_fog(input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_wo
|
|||||||
let distance = length(view_to_world);
|
let distance = length(view_to_world);
|
||||||
|
|
||||||
var scattering = vec3<f32>(0.0);
|
var scattering = vec3<f32>(0.0);
|
||||||
if fog.directional_light_color.a > 0.0 {
|
if fog_params.directional_light_color.a > 0.0 {
|
||||||
let view_to_world_normalized = view_to_world / distance;
|
let view_to_world_normalized = view_to_world / distance;
|
||||||
let n_directional_lights = lights.n_directional_lights;
|
let n_directional_lights = lights.n_directional_lights;
|
||||||
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
|
for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) {
|
||||||
@ -295,19 +295,19 @@ fn apply_fog(input_color: vec4<f32>, fragment_world_position: vec3<f32>, view_wo
|
|||||||
dot(view_to_world_normalized, light.direction_to_light),
|
dot(view_to_world_normalized, light.direction_to_light),
|
||||||
0.0
|
0.0
|
||||||
),
|
),
|
||||||
fog.directional_light_exponent
|
fog_params.directional_light_exponent
|
||||||
) * light.color.rgb;
|
) * light.color.rgb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fog.mode == FOG_MODE_LINEAR {
|
if fog_params.mode == FOG_MODE_LINEAR {
|
||||||
return linear_fog(input_color, distance, scattering);
|
return linear_fog(fog_params, input_color, distance, scattering);
|
||||||
} else if fog.mode == FOG_MODE_EXPONENTIAL {
|
} else if fog_params.mode == FOG_MODE_EXPONENTIAL {
|
||||||
return exponential_fog(input_color, distance, scattering);
|
return exponential_fog(fog_params, input_color, distance, scattering);
|
||||||
} else if fog.mode == FOG_MODE_EXPONENTIAL_SQUARED {
|
} else if fog_params.mode == FOG_MODE_EXPONENTIAL_SQUARED {
|
||||||
return exponential_squared_fog(input_color, distance, scattering);
|
return exponential_squared_fog(fog_params, input_color, distance, scattering);
|
||||||
} else if fog.mode == FOG_MODE_ATMOSPHERIC {
|
} else if fog_params.mode == FOG_MODE_ATMOSPHERIC {
|
||||||
return atmospheric_fog(input_color, distance, scattering);
|
return atmospheric_fog(fog_params, input_color, distance, scattering);
|
||||||
} else {
|
} else {
|
||||||
return input_color;
|
return input_color;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#define_import_path bevy_pbr::utils
|
#define_import_path bevy_pbr::utils
|
||||||
|
|
||||||
const PI: f32 = 3.141592653589793;
|
const PI: f32 = 3.141592653589793;
|
||||||
|
const E: f32 = 2.718281828459045;
|
||||||
|
|
||||||
fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3<f32> {
|
fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3<f32> {
|
||||||
let rgb = clamp(
|
let rgb = clamp(
|
||||||
|
|||||||
@ -89,6 +89,29 @@ impl<I: PhaseItem> RenderPhase<I> {
|
|||||||
draw_function.draw(world, render_pass, view, item);
|
draw_function.draw(world, render_pass, view, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions.
|
||||||
|
pub fn render_range<'w>(
|
||||||
|
&self,
|
||||||
|
render_pass: &mut TrackedRenderPass<'w>,
|
||||||
|
world: &'w World,
|
||||||
|
view: Entity,
|
||||||
|
range: Range<usize>,
|
||||||
|
) {
|
||||||
|
let draw_functions = world.resource::<DrawFunctions<I>>();
|
||||||
|
let mut draw_functions = draw_functions.write();
|
||||||
|
draw_functions.prepare(world);
|
||||||
|
|
||||||
|
for item in self
|
||||||
|
.items
|
||||||
|
.get(range)
|
||||||
|
.expect("`Range` provided to `render_range()` is out of bounds")
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
|
||||||
|
draw_function.draw(world, render_pass, view, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: BatchedPhaseItem> RenderPhase<I> {
|
impl<I: BatchedPhaseItem> RenderPhase<I> {
|
||||||
|
|||||||
@ -17,10 +17,21 @@ use crate::{
|
|||||||
/// A [`RenderApp`](crate::RenderApp) resource that contains the default "fallback image",
|
/// A [`RenderApp`](crate::RenderApp) resource that contains the default "fallback image",
|
||||||
/// which can be used in situations where an image was not explicitly defined. The most common
|
/// which can be used in situations where an image was not explicitly defined. The most common
|
||||||
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
|
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
|
||||||
/// [`FallbackImage`] defaults to a 1x1 fully white texture, making blending colors with it a no-op.
|
///
|
||||||
|
/// Defaults to a 1x1 fully opaque white texture, (1.0, 1.0, 1.0, 1.0) which makes multiplying
|
||||||
|
/// it with other colors a no-op.
|
||||||
#[derive(Resource, Deref)]
|
#[derive(Resource, Deref)]
|
||||||
pub struct FallbackImage(GpuImage);
|
pub struct FallbackImage(GpuImage);
|
||||||
|
|
||||||
|
/// A [`RenderApp`](crate::RenderApp) resource that contains a _zero-filled_ "fallback image",
|
||||||
|
/// which can be used in place of [`FallbackImage`], when a fully transparent or black fallback
|
||||||
|
/// is required instead of fully opaque white.
|
||||||
|
///
|
||||||
|
/// Defaults to a 1x1 fully transparent black texture, (0.0, 0.0, 0.0, 0.0) which makes adding
|
||||||
|
/// or alpha-blending it to other colors a no-op.
|
||||||
|
#[derive(Resource, Deref)]
|
||||||
|
pub struct FallbackImageZero(GpuImage);
|
||||||
|
|
||||||
/// A [`RenderApp`](crate::RenderApp) resource that contains a "cubemap fallback image",
|
/// A [`RenderApp`](crate::RenderApp) resource that contains a "cubemap fallback image",
|
||||||
/// which can be used in situations where an image was not explicitly defined. The most common
|
/// which can be used in situations where an image was not explicitly defined. The most common
|
||||||
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
|
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
|
||||||
@ -34,9 +45,10 @@ fn fallback_image_new(
|
|||||||
format: TextureFormat,
|
format: TextureFormat,
|
||||||
dimension: TextureViewDimension,
|
dimension: TextureViewDimension,
|
||||||
samples: u32,
|
samples: u32,
|
||||||
|
value: u8,
|
||||||
) -> GpuImage {
|
) -> GpuImage {
|
||||||
// TODO make this configurable
|
// TODO make this configurable per channel
|
||||||
let data = vec![255; format.pixel_size()];
|
let data = vec![value; format.pixel_size()];
|
||||||
|
|
||||||
let extents = Extent3d {
|
let extents = Extent3d {
|
||||||
width: 1,
|
width: 1,
|
||||||
@ -92,6 +104,24 @@ impl FromWorld for FallbackImage {
|
|||||||
TextureFormat::bevy_default(),
|
TextureFormat::bevy_default(),
|
||||||
TextureViewDimension::D2,
|
TextureViewDimension::D2,
|
||||||
1,
|
1,
|
||||||
|
255,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWorld for FallbackImageZero {
|
||||||
|
fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {
|
||||||
|
let render_device = world.resource::<RenderDevice>();
|
||||||
|
let render_queue = world.resource::<RenderQueue>();
|
||||||
|
let default_sampler = world.resource::<DefaultImageSampler>();
|
||||||
|
Self(fallback_image_new(
|
||||||
|
render_device,
|
||||||
|
render_queue,
|
||||||
|
default_sampler,
|
||||||
|
TextureFormat::bevy_default(),
|
||||||
|
TextureViewDimension::D2,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,6 +138,7 @@ impl FromWorld for FallbackImageCubemap {
|
|||||||
TextureFormat::bevy_default(),
|
TextureFormat::bevy_default(),
|
||||||
TextureViewDimension::Cube,
|
TextureViewDimension::Cube,
|
||||||
1,
|
1,
|
||||||
|
255,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +179,7 @@ impl<'w> FallbackImagesMsaa<'w> {
|
|||||||
TextureFormat::bevy_default(),
|
TextureFormat::bevy_default(),
|
||||||
TextureViewDimension::D2,
|
TextureViewDimension::D2,
|
||||||
sample_count,
|
sample_count,
|
||||||
|
255,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -171,6 +203,7 @@ impl<'w> FallbackImagesDepth<'w> {
|
|||||||
TextureFormat::Depth32Float,
|
TextureFormat::Depth32Float,
|
||||||
TextureViewDimension::D2,
|
TextureViewDimension::D2,
|
||||||
sample_count,
|
sample_count,
|
||||||
|
255,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -120,6 +120,7 @@ impl Plugin for ImagePlugin {
|
|||||||
render_app
|
render_app
|
||||||
.insert_resource(DefaultImageSampler(default_sampler))
|
.insert_resource(DefaultImageSampler(default_sampler))
|
||||||
.init_resource::<FallbackImage>()
|
.init_resource::<FallbackImage>()
|
||||||
|
.init_resource::<FallbackImageZero>()
|
||||||
.init_resource::<FallbackImageCubemap>()
|
.init_resource::<FallbackImageCubemap>()
|
||||||
.init_resource::<FallbackImageMsaaCache>()
|
.init_resource::<FallbackImageMsaaCache>()
|
||||||
.init_resource::<FallbackImageDepthCache>();
|
.init_resource::<FallbackImageDepthCache>();
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use crate::{
|
|||||||
render_phase::ViewRangefinder3d,
|
render_phase::ViewRangefinder3d,
|
||||||
render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView},
|
render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView},
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
texture::{BevyDefault, TextureCache},
|
texture::{BevyDefault, CachedTexture, TextureCache},
|
||||||
Render, RenderApp, RenderSet,
|
Render, RenderApp, RenderSet,
|
||||||
};
|
};
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
@ -202,13 +202,16 @@ pub struct PostProcessWrite<'a> {
|
|||||||
impl ViewTarget {
|
impl ViewTarget {
|
||||||
pub const TEXTURE_FORMAT_HDR: TextureFormat = TextureFormat::Rgba16Float;
|
pub const TEXTURE_FORMAT_HDR: TextureFormat = TextureFormat::Rgba16Float;
|
||||||
|
|
||||||
/// Retrieve this target's color attachment. This will use [`Self::sampled_main_texture`] and resolve to [`Self::main_texture`] if
|
/// Retrieve this target's color attachment. This will use [`Self::sampled_main_texture_view`] and resolve to [`Self::main_texture`] if
|
||||||
/// the target has sampling enabled. Otherwise it will use [`Self::main_texture`] directly.
|
/// the target has sampling enabled. Otherwise it will use [`Self::main_texture`] directly.
|
||||||
pub fn get_color_attachment(&self, ops: Operations<Color>) -> RenderPassColorAttachment {
|
pub fn get_color_attachment(&self, ops: Operations<Color>) -> RenderPassColorAttachment {
|
||||||
match &self.main_textures.sampled {
|
match &self.main_textures.sampled {
|
||||||
Some(sampled_texture) => RenderPassColorAttachment {
|
Some(CachedTexture {
|
||||||
view: sampled_texture,
|
default_view: sampled_texture_view,
|
||||||
resolve_target: Some(self.main_texture()),
|
..
|
||||||
|
}) => RenderPassColorAttachment {
|
||||||
|
view: sampled_texture_view,
|
||||||
|
resolve_target: Some(self.main_texture_view()),
|
||||||
ops,
|
ops,
|
||||||
},
|
},
|
||||||
None => self.get_unsampled_color_attachment(ops),
|
None => self.get_unsampled_color_attachment(ops),
|
||||||
@ -221,18 +224,18 @@ impl ViewTarget {
|
|||||||
ops: Operations<Color>,
|
ops: Operations<Color>,
|
||||||
) -> RenderPassColorAttachment {
|
) -> RenderPassColorAttachment {
|
||||||
RenderPassColorAttachment {
|
RenderPassColorAttachment {
|
||||||
view: self.main_texture(),
|
view: self.main_texture_view(),
|
||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
ops,
|
ops,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The "main" unsampled texture.
|
/// The "main" unsampled texture.
|
||||||
pub fn main_texture(&self) -> &TextureView {
|
pub fn main_texture(&self) -> &Texture {
|
||||||
if self.main_texture.load(Ordering::SeqCst) == 0 {
|
if self.main_texture.load(Ordering::SeqCst) == 0 {
|
||||||
&self.main_textures.a
|
&self.main_textures.a.texture
|
||||||
} else {
|
} else {
|
||||||
&self.main_textures.b
|
&self.main_textures.b.texture
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,17 +245,51 @@ impl ViewTarget {
|
|||||||
///
|
///
|
||||||
/// A use case for this is to be able to prepare a bind group for all main textures
|
/// A use case for this is to be able to prepare a bind group for all main textures
|
||||||
/// ahead of time.
|
/// ahead of time.
|
||||||
pub fn main_texture_other(&self) -> &TextureView {
|
pub fn main_texture_other(&self) -> &Texture {
|
||||||
if self.main_texture.load(Ordering::SeqCst) == 0 {
|
if self.main_texture.load(Ordering::SeqCst) == 0 {
|
||||||
&self.main_textures.b
|
&self.main_textures.b.texture
|
||||||
} else {
|
} else {
|
||||||
&self.main_textures.a
|
&self.main_textures.a.texture
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The "main" unsampled texture.
|
||||||
|
pub fn main_texture_view(&self) -> &TextureView {
|
||||||
|
if self.main_texture.load(Ordering::SeqCst) == 0 {
|
||||||
|
&self.main_textures.a.default_view
|
||||||
|
} else {
|
||||||
|
&self.main_textures.b.default_view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The _other_ "main" unsampled texture view.
|
||||||
|
/// In most cases you should use [`Self::main_texture_view`] instead and never this.
|
||||||
|
/// The textures will naturally be swapped when [`Self::post_process_write`] is called.
|
||||||
|
///
|
||||||
|
/// A use case for this is to be able to prepare a bind group for all main textures
|
||||||
|
/// ahead of time.
|
||||||
|
pub fn main_texture_other_view(&self) -> &TextureView {
|
||||||
|
if self.main_texture.load(Ordering::SeqCst) == 0 {
|
||||||
|
&self.main_textures.b.default_view
|
||||||
|
} else {
|
||||||
|
&self.main_textures.a.default_view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The "main" sampled texture.
|
/// The "main" sampled texture.
|
||||||
pub fn sampled_main_texture(&self) -> Option<&TextureView> {
|
pub fn sampled_main_texture(&self) -> Option<&Texture> {
|
||||||
self.main_textures.sampled.as_ref()
|
self.main_textures
|
||||||
|
.sampled
|
||||||
|
.as_ref()
|
||||||
|
.map(|sampled| &sampled.texture)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The "main" sampled texture view.
|
||||||
|
pub fn sampled_main_texture_view(&self) -> Option<&TextureView> {
|
||||||
|
self.main_textures
|
||||||
|
.sampled
|
||||||
|
.as_ref()
|
||||||
|
.map(|sampled| &sampled.default_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -290,13 +327,13 @@ impl ViewTarget {
|
|||||||
// if the old main texture is a, then the post processing must write from a to b
|
// if the old main texture is a, then the post processing must write from a to b
|
||||||
if old_is_a_main_texture == 0 {
|
if old_is_a_main_texture == 0 {
|
||||||
PostProcessWrite {
|
PostProcessWrite {
|
||||||
source: &self.main_textures.a,
|
source: &self.main_textures.a.default_view,
|
||||||
destination: &self.main_textures.b,
|
destination: &self.main_textures.b.default_view,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PostProcessWrite {
|
PostProcessWrite {
|
||||||
source: &self.main_textures.b,
|
source: &self.main_textures.b.default_view,
|
||||||
destination: &self.main_textures.a,
|
destination: &self.main_textures.a.default_view,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,9 +394,9 @@ pub fn prepare_view_uniforms(
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct MainTargetTextures {
|
struct MainTargetTextures {
|
||||||
a: TextureView,
|
a: CachedTexture,
|
||||||
b: TextureView,
|
b: CachedTexture,
|
||||||
sampled: Option<TextureView>,
|
sampled: Option<CachedTexture>,
|
||||||
/// 0 represents `main_textures.a`, 1 represents `main_textures.b`
|
/// 0 represents `main_textures.a`, 1 represents `main_textures.b`
|
||||||
/// This is shared across view targets with the same render target
|
/// This is shared across view targets with the same render target
|
||||||
main_texture: Arc<AtomicUsize>,
|
main_texture: Arc<AtomicUsize>,
|
||||||
@ -405,35 +442,30 @@ fn prepare_view_targets(
|
|||||||
dimension: TextureDimension::D2,
|
dimension: TextureDimension::D2,
|
||||||
format: main_texture_format,
|
format: main_texture_format,
|
||||||
usage: TextureUsages::RENDER_ATTACHMENT
|
usage: TextureUsages::RENDER_ATTACHMENT
|
||||||
| TextureUsages::TEXTURE_BINDING,
|
| TextureUsages::TEXTURE_BINDING
|
||||||
|
| TextureUsages::COPY_SRC,
|
||||||
view_formats: match main_texture_format {
|
view_formats: match main_texture_format {
|
||||||
TextureFormat::Bgra8Unorm => &[TextureFormat::Bgra8UnormSrgb],
|
TextureFormat::Bgra8Unorm => &[TextureFormat::Bgra8UnormSrgb],
|
||||||
TextureFormat::Rgba8Unorm => &[TextureFormat::Rgba8UnormSrgb],
|
TextureFormat::Rgba8Unorm => &[TextureFormat::Rgba8UnormSrgb],
|
||||||
_ => &[],
|
_ => &[],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
MainTargetTextures {
|
let a = texture_cache.get(
|
||||||
a: texture_cache
|
|
||||||
.get(
|
|
||||||
&render_device,
|
&render_device,
|
||||||
TextureDescriptor {
|
TextureDescriptor {
|
||||||
label: Some("main_texture_a"),
|
label: Some("main_texture_a"),
|
||||||
..descriptor
|
..descriptor
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
.default_view,
|
let b = texture_cache.get(
|
||||||
b: texture_cache
|
|
||||||
.get(
|
|
||||||
&render_device,
|
&render_device,
|
||||||
TextureDescriptor {
|
TextureDescriptor {
|
||||||
label: Some("main_texture_b"),
|
label: Some("main_texture_b"),
|
||||||
..descriptor
|
..descriptor
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
.default_view,
|
let sampled = if msaa.samples() > 1 {
|
||||||
sampled: (msaa.samples() > 1).then(|| {
|
let sampled = texture_cache.get(
|
||||||
texture_cache
|
|
||||||
.get(
|
|
||||||
&render_device,
|
&render_device,
|
||||||
TextureDescriptor {
|
TextureDescriptor {
|
||||||
label: Some("main_texture_sampled"),
|
label: Some("main_texture_sampled"),
|
||||||
@ -445,9 +477,15 @@ fn prepare_view_targets(
|
|||||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||||
view_formats: descriptor.view_formats,
|
view_formats: descriptor.view_formats,
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
.default_view
|
Some(sampled)
|
||||||
}),
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
MainTargetTextures {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
sampled,
|
||||||
main_texture: Arc::new(AtomicUsize::new(0)),
|
main_texture: Arc::new(AtomicUsize::new(0)),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user