-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Apply codebase changes in preparation for StandardMaterial
transmission
#8704
Conversation
@@ -237,4 +237,4 @@ macro_rules! impl_exclusive_system_function { | |||
} | |||
// Note that we rely on the highest impl to be <= the highest order of the tuple impls | |||
// of `SystemParam` created. | |||
all_tuples!(impl_exclusive_system_function, 0, 16, F); | |||
all_tuples!(impl_exclusive_system_function, 0, 17, F); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seems out of place here and wasn't mentioned in the change log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was also divided on wether or not to include this one. One of the systems I update in the other PR to add transmission ends up needing more than 16, but since it's technically a change unrelated to transmission I wasn't sure it would make sense to keep it there or here.
I didn't include it in the change log because it was minor and mostly internal/not user-facing, but I can also add a note about it. It's probably very obscure though, and users might be confused on why 17
, and not, say, 32
(My understanding is that compile-time memory and CPU usage grows non-linearly with it, so we want to keep it low)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO that system should just use a custom (derived) SystemParam
instead. That's the idiomatic way to handle the problem, so we should use it internally instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, I haven't used that yet; I'll try looking into it. I agree just bumping the limit arbitrarily is probably not a good solution, especially since next time when we add another feature we'll just have to bump it again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you can also nest your parameters in tuples
fn my_system(
(first_param, second_param): (Res<First>, ResMut<Second>),
third_param: Query<((&Also, &Works), (&WithQuery, &Parameters))>,
// ...
) {
// ...
}
SystemParam
is better, but if you don't want to bother with them, you can use tuples. Increasing this number generates a n² amount of code and slows down compile time disproportionately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooo, I really like this tuple-based solution, if y'all are okay with me using it instead of SystemParams
, I'd very much prefer it, since SystemParams
feels a bit verbose for this scenario.
for item in self | ||
.items | ||
.iter() | ||
.skip(range.start) | ||
.take(range.end - range.start) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
item
is Vec
here, so this should work. The difference being this skips when range is partially outside of items
. (say items.len() == 3
and range = 2..5
)
for item in self | |
.items | |
.iter() | |
.skip(range.start) | |
.take(range.end - range.start) | |
for item in self.items.get(range).iter().flatten() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't get that to work verbatim because of the nested reference in the type, so I ended up adding a .expect()
instead, which will panic if you use an out-of-bounds range, which might also be less confusing to debug than just silently ignoring the range if it's out of bounds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only blocker for me is incrementing the size of trait impl tuple generation, it should stay at 16. Otherwise, all the other changes looks high quality (with some non-blocking reservations)
/// 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered using "WhiteFallbackImage" and "TransparentBlackFallbackImage"? Though I understand why you'd want to keep the current names over the ones I'm suggesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... I tried out a bunch of different names/orders, but they all felt weird. I didn't want to rename the existing FallbackImage
needlessly since that would introduce a breaking change for no good reason, and since there was already FallbackImageCubemap
(with the “qualifier” at the end) I just went with the same pattern for consistency.
I can rename all of them if there's significant demand, but since we might want to be able to (eventually) create arbitrary-color fallbacks, maybe we can save the breaking change for then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine as-is for now
crates/bevy_pbr/src/render/fog.wgsl
Outdated
@@ -6,6 +6,7 @@ | |||
// https://iquilezles.org/articles/fog/ (Atmospheric Fog and Scattering) | |||
|
|||
fn scattering_adjusted_fog_color( | |||
fog: Fog, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the global fog
uniform be renamed, so that in future changes to this code, we don't accidentally end up using the uniform instead of the argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up renaming the argument in the fog functions instead, to fog_params
. Right now other uniforms have a 1:1 match between their binding name and type name, with only a difference in case, (e.g. point_lights: PointLights
and cluster_offsets_and_counts: ClusterOffsetsAndCounts
) so ideally I want to preserve that consistency.
crates/bevy_render/src/view/mod.rs
Outdated
sampled: Option<Texture>, | ||
sampled_view: Option<TextureView>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This invites possibly invalid state. (one is Some, the other None). I'd suggest defining a TextureAndView
then you'd have
struct TextureAndView { texture: Texture, view: TextureView }
struct MainTargetTextures {
a: TextureAndView,
b: TextureAndView,
sampled: Option<TextureAndView>,
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ended up reusing CachedTexture
which is pretty much that, and in fact it's what the methods we use to set up the texture return us, that way the setup code is also shorter/cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, in my DLSS fork which needs the Texture, I have the same changes of making this hold a CachedTexture.
if self.main_texture.load(Ordering::SeqCst) == 0 { | ||
&self.main_textures.b | ||
} else { | ||
&self.main_textures.a | ||
} | ||
} | ||
|
||
/// The "main" unsampled texture. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this should explain the difference with main_texture
. It's surprising as a user to see two seemingly identical functions with the same documentation that returns two different types. What should I pick? I think a view
is a subset of the texture right? Why would I use the Texture
version over this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's really necessary. It's just a wgpu thing - texture views are for binding, textures are for copy operations mostly and not much else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually don't fully understand the distinction either, except that some APIs require Texture
and others require TextureView
. I guess it probably becomes more useful when you have an atlas or would like to "slice" portions of your texture for something, but I don't understand why most calls can't take a TextureView
directly instead of a Texture
. copy_texture_to_texture()
is especially weird given that it takes a texture and a region, which could be better served by a TextureView
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TextureView lets you do things like bind specific mip levels of a texture separately. They also correspond to different lower-level DX/VK/Metal objects, which I imagine is the real reason why WebGPU has both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just return CachedTexture
and let users pick what they need at the callsite?
@alice-i-cecile @nicopap Reverted the tuple size bump, and applied the feedback from the reviews. |
if self.main_texture.load(Ordering::SeqCst) == 0 { | ||
&self.main_textures.b | ||
} else { | ||
&self.main_textures.a | ||
} | ||
} | ||
|
||
/// The "main" unsampled texture. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's really necessary. It's just a wgpu thing - texture views are for binding, textures are for copy operations mostly and not much else.
crates/bevy_render/src/view/mod.rs
Outdated
sampled: Option<Texture>, | ||
sampled_view: Option<TextureView>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, in my DLSS fork which needs the Texture, I have the same changes of making this hold a CachedTexture.
/// 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine as-is for now
@@ -89,6 +89,29 @@ impl<I: PhaseItem> RenderPhase<I> { | |||
draw_function.draw(world, render_pass, view, item); | |||
} | |||
} | |||
|
|||
/// Renders a range of the [`PhaseItem`]s using their corresponding draw functions. | |||
pub fn render_range<'w>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we want some validation here to check that the range is valid.
Also, remind me why render_range() is useful for transmission?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we want some validation here to check that the range is valid.
The updated code with .get(range)
now has validation.
Also, remind me why render_range() is useful for transmission?
We use it to implement transmissive "steps", to achieve more than one layer of transparency for transmission. The items are sorted far-to-near, then split into roughly equal ranges and drawn parceled out, with a texture copy between each draw. This is kinda expensive so we default to a single step (equivalent to not using render_range()
) but you can configure it via Camera3d
if higher fidelity is required.
Note that there are probably better ways to do this range division, including by determining which items' bounds overlap in screen space, making the steps exponentially spaced (so that near objects get more steps) or prioritizing based on roughness, transmission, and screen space coverage, however to start I just went for the most naive approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
A bunch of nice and simple improvements.
Objective
StandardMaterial
Light Transmission #8015 easier to review;Solution
StandardMaterial
Light Transmission #8015, in easier-to-review, one-change-per-commit form.Changelog
Fixed
0.0
instead of1.0
, to avoid TAA artifacts on transparent objects against the background;Added
E
mathematical constant is now available for use in shaders, exposed underbevy_pbr::utils
;TAA
shader def is now available, for conditionally enabling shader logic via#ifdef
when TAA is enabled; (e.g. for jittering texture samples)FallbackImageZero
resource is introduced, for when a fallback image filled with zeroes is required;RenderPhase<I>::render_range()
method is introduced, for render phases that need to render their items in multiple parceled out “steps”;Changed
MainTargetTextures
struct now holds bothTexture
andTextureViews
for the main textures;bevy_pbr::fog
now take the aFog
structure as their first argument, instead of relying on the globalfog
uniform;Migration Guide
ViewTarget::main_texture()
andViewTarget::main_texture_other()
now return&Texture
instead of&TextureView
. If you were relying on these methods, replace your usage withViewTarget::main_texture_view()
andViewTarget::main_texture_other_view()
, respectively;ViewTarget::sampled_main_texture()
now returnsOption<&Texture>
instead of aOption<&TextureView>
. If you were relying on this method, replace your usage withViewTarget::sampled_main_texture_view()
;apply_fog()
,linear_fog()
,exponential_fog()
,exponential_squared_fog()
andatmospheric_fog()
functions now take a configurableFog
struct. If you were relying on them, update your usage by adding the globalfog
uniform as their first argument;