Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions crates/bevy_camera/src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,20 @@ impl Viewport {
}
}

pub fn with_override(
&self,
pub fn from_viewport_and_override(
viewport: Option<&Self>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a bit weird, but I don't have a suggestion for it right now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I'm still also bothered by this since "main pass" isn't intrinsically meaningful in the context of Camera, but didn't have any other ideas for now.

main_pass_resolution_override: Option<&MainPassResolutionOverride>,
) -> Self {
let mut viewport = self.clone();
) -> Option<Self> {
let mut viewport = viewport.cloned();

if let Some(override_size) = main_pass_resolution_override {
viewport.physical_size = **override_size;
if viewport.is_none() {
viewport = Some(Viewport::default());
}

viewport.as_mut().unwrap().physical_size = **override_size;
}

viewport
}
}
Expand All @@ -101,7 +107,8 @@ impl Viewport {
/// * Insert this component on a 3d camera entity in the render world.
/// * The resolution override must be smaller than the camera's viewport size.
/// * The resolution override is specified in physical pixels.
#[derive(Component, Reflect, Deref)]
/// * In shaders, use `View::main_pass_viewport` instead of `View::viewport`.
#[derive(Component, Reflect, Deref, Debug)]
#[reflect(Component)]
pub struct MainPassResolutionOverride(pub UVec2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
core_3d::Opaque3d,
skybox::{SkyboxBindGroup, SkyboxPipelineId},
};
use bevy_camera::Viewport;
use bevy_ecs::{prelude::World, query::QueryItem};
use bevy_render::{
camera::{ExtractedCamera, MainPassResolutionOverride},
Expand Down Expand Up @@ -91,8 +92,10 @@ impl ViewNode for MainOpaquePass3dNode {
let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
let pass_span = diagnostics.pass_span(&mut render_pass, "main_opaque_pass_3d");

if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

// Opaque draws
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{Camera3d, ViewTransmissionTexture};
use crate::core_3d::Transmissive3d;
use bevy_camera::Viewport;
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_image::ToExtents;
use bevy_render::{
Expand Down Expand Up @@ -110,8 +111,11 @@ impl ViewNode for MainTransmissivePass3dNode {
let mut render_pass =
render_context.begin_tracked_render_pass(render_pass_descriptor);

if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) = Viewport::from_viewport_and_override(
camera.viewport.as_ref(),
resolution_override,
) {
render_pass.set_camera_viewport(&viewport);
}

if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::core_3d::Transparent3d;
use bevy_camera::Viewport;
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_render::{
camera::{ExtractedCamera, MainPassResolutionOverride},
Expand Down Expand Up @@ -69,8 +70,10 @@ impl ViewNode for MainTransparentPass3dNode {

let pass_span = diagnostics.pass_span(&mut render_pass, "main_transparent_pass_3d");

if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) {
Expand Down
7 changes: 5 additions & 2 deletions crates/bevy_core_pipeline/src/deferred/node.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bevy_camera::Viewport;
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_render::camera::MainPassResolutionOverride;
use bevy_render::experimental::occlusion_culling::OcclusionCulling;
Expand Down Expand Up @@ -222,8 +223,10 @@ fn run_deferred_prepass<'w>(
occlusion_query_set: None,
});
let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

// Opaque draws
Expand Down
7 changes: 5 additions & 2 deletions crates/bevy_core_pipeline/src/oit/resolve/node.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bevy_camera::Viewport;
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_render::{
camera::{ExtractedCamera, MainPassResolutionOverride},
Expand Down Expand Up @@ -63,8 +64,10 @@ impl ViewNode for OitResolveNode {
occlusion_query_set: None,
});

if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

render_pass.set_render_pipeline(pipeline);
Expand Down
7 changes: 5 additions & 2 deletions crates/bevy_core_pipeline/src/prepass/node.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bevy_camera::Viewport;
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_render::{
camera::{ExtractedCamera, MainPassResolutionOverride},
Expand Down Expand Up @@ -186,8 +187,10 @@ fn run_prepass<'w>(
let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
let pass_span = diagnostics.pass_span(&mut render_pass, label);

if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

// Opaque draws
Expand Down
19 changes: 13 additions & 6 deletions crates/bevy_pbr/src/meshlet/material_shade_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
MeshViewBindGroup, PrepassViewBindGroup, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset,
ViewLightProbesUniformOffset, ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset,
};
use bevy_camera::Viewport;
use bevy_core_pipeline::prepass::{
MotionVectorPrepass, PreviousViewUniformOffset, ViewPrepassTextures,
};
Expand Down Expand Up @@ -102,8 +103,10 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
timestamp_writes: None,
occlusion_query_set: None,
});
if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

render_pass.set_bind_group(
Expand Down Expand Up @@ -223,8 +226,10 @@ impl ViewNode for MeshletPrepassNode {
timestamp_writes: None,
occlusion_query_set: None,
});
if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

if view_has_motion_vector_prepass {
Expand Down Expand Up @@ -354,8 +359,10 @@ impl ViewNode for MeshletDeferredGBufferPrepassNode {
timestamp_writes: None,
occlusion_query_set: None,
});
if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
if let Some(viewport) =
Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
{
render_pass.set_camera_viewport(&viewport);
}

if view_has_motion_vector_prepass {
Expand Down
23 changes: 21 additions & 2 deletions crates/bevy_render/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod window;

use bevy_camera::{
primitives::Frustum, CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure,
MainPassResolutionOverride,
};
use bevy_diagnostic::FrameCount;
pub use visibility::*;
Expand Down Expand Up @@ -568,6 +569,7 @@ pub struct ViewUniform {
pub exposure: f32,
// viewport(x_origin, y_origin, width, height)
pub viewport: Vec4,
pub main_pass_viewport: Vec4,
/// 6 world-space half spaces (normal: vec3, distance: f32) ordered left, right, top, bottom, near, far.
/// The normal vectors point towards the interior of the frustum.
/// A half space contains `p` if `normal.dot(p) + distance > 0.`
Expand Down Expand Up @@ -901,6 +903,7 @@ pub fn prepare_view_uniforms(
Option<&Frustum>,
Option<&TemporalJitter>,
Option<&MipBias>,
Option<&MainPassResolutionOverride>,
)>,
frame_count: Res<FrameCount>,
) {
Expand All @@ -913,13 +916,28 @@ pub fn prepare_view_uniforms(
else {
return;
};
for (entity, extracted_camera, extracted_view, frustum, temporal_jitter, mip_bias) in &views {
for (
entity,
extracted_camera,
extracted_view,
frustum,
temporal_jitter,
mip_bias,
resolution_override,
) in &views
{
let viewport = extracted_view.viewport.as_vec4();
let mut main_pass_viewport = viewport;
if let Some(resolution_override) = resolution_override {
main_pass_viewport.z = resolution_override.0.x as f32;
main_pass_viewport.w = resolution_override.0.y as f32;
}

let unjittered_projection = extracted_view.clip_from_view;
let mut clip_from_view = unjittered_projection;

if let Some(temporal_jitter) = temporal_jitter {
temporal_jitter.jitter_projection(&mut clip_from_view, viewport.zw());
temporal_jitter.jitter_projection(&mut clip_from_view, main_pass_viewport.zw());
}

let view_from_clip = clip_from_view.inverse();
Expand Down Expand Up @@ -953,6 +971,7 @@ pub fn prepare_view_uniforms(
.map(|c| c.exposure)
.unwrap_or_else(|| Exposure::default().exposure()),
viewport,
main_pass_viewport,
frustum,
color_grading: extracted_view.color_grading.clone().into(),
mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0,
Expand Down
9 changes: 5 additions & 4 deletions crates/bevy_render/src/view/view.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,15 @@ struct View {
// `clip_from_view[3][3] == 1.0` is the standard way to check if a projection is orthographic
//
// Wgsl matrices are column major, so for example getting the near plane of a perspective projection is `clip_from_view[3][2]`
//
//
// Custom projections are also possible however.
clip_from_view: mat4x4<f32>,
view_from_clip: mat4x4<f32>,
world_position: vec3<f32>,
exposure: f32,
// viewport(x_origin, y_origin, width, height)
viewport: vec4<f32>,
main_pass_viewport: vec4<f32>,
// 6 world-space half spaces (normal: vec3, distance: f32) ordered left, right, top, bottom, near, far.
// The normal vectors point towards the interior of the frustum.
// A half space contains `p` if `normal.dot(p) + distance > 0.`
Expand All @@ -78,7 +79,7 @@ struct View {
/// https://www.w3.org/TR/webgpu/#coordinate-systems
/// (-1.0, -1.0) in NDC is located at the bottom-left corner of NDC
/// (1.0, 1.0) in NDC is located at the top-right corner of NDC
/// Z is depth where:
/// Z is depth where:
/// 1.0 is near clipping plane
/// Perspective projection: 0.0 is inf far away
/// Orthographic projection: 0.0 is far clipping plane
Expand Down Expand Up @@ -209,7 +210,7 @@ fn perspective_camera_near(clip_from_view: mat4x4<f32>) -> f32 {
return clip_from_view[3][2];
}

/// Convert ndc depth to linear view z.
/// Convert ndc depth to linear view z.
/// Note: Depth values in front of the camera will be negative as -z is forward
fn depth_ndc_to_view_z(ndc_depth: f32, clip_from_view: mat4x4<f32>, view_from_clip: mat4x4<f32>) -> f32 {
#ifdef VIEW_PROJECTION_PERSPECTIVE
Expand All @@ -222,7 +223,7 @@ fn depth_ndc_to_view_z(ndc_depth: f32, clip_from_view: mat4x4<f32>, view_from_cl
#endif
}

/// Convert linear view z to ndc depth.
/// Convert linear view z to ndc depth.
/// Note: View z input should be negative for values in front of the camera as -z is forward
fn view_z_to_depth_ndc(view_z: f32, clip_from_view: mat4x4<f32>) -> f32 {
#ifdef VIEW_PROJECTION_PERSPECTIVE
Expand Down
20 changes: 9 additions & 11 deletions crates/bevy_solari/src/realtime/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use bevy_ecs::{
};
use bevy_image::ToExtents;
use bevy_render::{
camera::ExtractedCamera,
diagnostic::RecordDiagnostics,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_resource::{
Expand Down Expand Up @@ -50,7 +49,6 @@ impl ViewNode for SolariLightingNode {
type ViewQuery = (
&'static SolariLighting,
&'static SolariLightingResources,
&'static ExtractedCamera,
&'static ViewTarget,
&'static ViewPrepassTextures,
&'static ViewUniformOffset,
Expand All @@ -64,7 +62,6 @@ impl ViewNode for SolariLightingNode {
(
solari_lighting,
solari_lighting_resources,
camera,
view_target,
view_prepass_textures,
view_uniform_offset,
Expand All @@ -84,7 +81,6 @@ impl ViewNode for SolariLightingNode {
Some(gi_initial_and_temporal_pipeline),
Some(gi_spatial_and_shade_pipeline),
Some(scene_bindings),
Some(viewport),
Some(gbuffer),
Some(depth_buffer),
Some(motion_vectors),
Expand All @@ -97,7 +93,6 @@ impl ViewNode for SolariLightingNode {
pipeline_cache.get_compute_pipeline(self.gi_initial_and_temporal_pipeline),
pipeline_cache.get_compute_pipeline(self.gi_spatial_and_shade_pipeline),
&scene_bindings.bind_group,
camera.physical_viewport_size,
view_prepass_textures.deferred_view(),
view_prepass_textures.depth_view(),
view_prepass_textures.motion_vectors_view(),
Expand Down Expand Up @@ -153,6 +148,9 @@ impl ViewNode for SolariLightingNode {
});
let pass_span = diagnostics.pass_span(&mut pass, "solari_lighting");

let dx = solari_lighting_resources.view_size.x.div_ceil(8);
let dy = solari_lighting_resources.view_size.y.div_ceil(8);

pass.set_bind_group(0, scene_bindings, &[]);
pass.set_bind_group(
1,
Expand All @@ -171,16 +169,16 @@ impl ViewNode for SolariLightingNode {
pass.dispatch_workgroups(LIGHT_TILE_BLOCKS as u32, 1, 1);

pass.set_pipeline(di_initial_and_temporal_pipeline);
pass.dispatch_workgroups(viewport.x.div_ceil(8), viewport.y.div_ceil(8), 1);
pass.dispatch_workgroups(dx, dy, 1);

pass.set_pipeline(di_spatial_and_shade_pipeline);
pass.dispatch_workgroups(viewport.x.div_ceil(8), viewport.y.div_ceil(8), 1);
pass.dispatch_workgroups(dx, dy, 1);

pass.set_pipeline(gi_initial_and_temporal_pipeline);
pass.dispatch_workgroups(viewport.x.div_ceil(8), viewport.y.div_ceil(8), 1);
pass.dispatch_workgroups(dx, dy, 1);

pass.set_pipeline(gi_spatial_and_shade_pipeline);
pass.dispatch_workgroups(viewport.x.div_ceil(8), viewport.y.div_ceil(8), 1);
pass.dispatch_workgroups(dx, dy, 1);

pass_span.end(&mut pass);
drop(pass);
Expand All @@ -195,7 +193,7 @@ impl ViewNode for SolariLightingNode {
.texture
.as_image_copy(),
solari_lighting_resources.previous_gbuffer.0.as_image_copy(),
viewport.to_extents(),
solari_lighting_resources.view_size.to_extents(),
);
command_encoder.copy_texture_to_texture(
view_prepass_textures
Expand All @@ -206,7 +204,7 @@ impl ViewNode for SolariLightingNode {
.texture
.as_image_copy(),
solari_lighting_resources.previous_depth.0.as_image_copy(),
viewport.to_extents(),
solari_lighting_resources.view_size.to_extents(),
);

Ok(())
Expand Down
Loading
Loading