Skip to content

Commit a0b90cd

Browse files
tychedeliaJMS55atlv24
authored
Resolution override (#19817)
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com> Co-authored-by: atlv <email@atlasdostal.com>
1 parent 410ca48 commit a0b90cd

File tree

10 files changed

+67
-24
lines changed

10 files changed

+67
-24
lines changed

crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
};
55
use bevy_ecs::{prelude::World, query::QueryItem};
66
use bevy_render::{
7-
camera::ExtractedCamera,
7+
camera::{ExtractedCamera, MainPassResolutionOverride},
88
diagnostic::RecordDiagnostics,
99
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
1010
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
@@ -31,6 +31,7 @@ impl ViewNode for MainOpaquePass3dNode {
3131
Option<&'static SkyboxPipelineId>,
3232
Option<&'static SkyboxBindGroup>,
3333
&'static ViewUniformOffset,
34+
Option<&'static MainPassResolutionOverride>,
3435
);
3536

3637
fn run<'w>(
@@ -45,6 +46,7 @@ impl ViewNode for MainOpaquePass3dNode {
4546
skybox_pipeline,
4647
skybox_bind_group,
4748
view_uniform_offset,
49+
resolution_override,
4850
): QueryItem<'w, '_, Self::ViewQuery>,
4951
world: &'w World,
5052
) -> Result<(), NodeRunError> {
@@ -90,7 +92,7 @@ impl ViewNode for MainOpaquePass3dNode {
9092
let pass_span = diagnostics.pass_span(&mut render_pass, "main_opaque_pass_3d");
9193

9294
if let Some(viewport) = camera.viewport.as_ref() {
93-
render_pass.set_camera_viewport(viewport);
95+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
9496
}
9597

9698
// Opaque draws

crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::core_3d::Transmissive3d;
33
use bevy_ecs::{prelude::*, query::QueryItem};
44
use bevy_image::ToExtents;
55
use bevy_render::{
6-
camera::ExtractedCamera,
6+
camera::{ExtractedCamera, MainPassResolutionOverride},
77
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
88
render_phase::ViewSortedRenderPhases,
99
render_resource::{RenderPassDescriptor, StoreOp},
@@ -28,13 +28,16 @@ impl ViewNode for MainTransmissivePass3dNode {
2828
&'static ViewTarget,
2929
Option<&'static ViewTransmissionTexture>,
3030
&'static ViewDepthTexture,
31+
Option<&'static MainPassResolutionOverride>,
3132
);
3233

3334
fn run(
3435
&self,
3536
graph: &mut RenderGraphContext,
3637
render_context: &mut RenderContext,
37-
(camera, view, camera_3d, target, transmission, depth): QueryItem<Self::ViewQuery>,
38+
(camera, view, camera_3d, target, transmission, depth, resolution_override): QueryItem<
39+
Self::ViewQuery,
40+
>,
3841
world: &World,
3942
) -> Result<(), NodeRunError> {
4043
let view_entity = graph.view_entity();
@@ -108,7 +111,7 @@ impl ViewNode for MainTransmissivePass3dNode {
108111
render_context.begin_tracked_render_pass(render_pass_descriptor);
109112

110113
if let Some(viewport) = camera.viewport.as_ref() {
111-
render_pass.set_camera_viewport(viewport);
114+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
112115
}
113116

114117
if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) {

crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::core_3d::Transparent3d;
22
use bevy_ecs::{prelude::*, query::QueryItem};
33
use bevy_render::{
4-
camera::ExtractedCamera,
4+
camera::{ExtractedCamera, MainPassResolutionOverride},
55
diagnostic::RecordDiagnostics,
66
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
77
render_phase::ViewSortedRenderPhases,
@@ -24,12 +24,13 @@ impl ViewNode for MainTransparentPass3dNode {
2424
&'static ExtractedView,
2525
&'static ViewTarget,
2626
&'static ViewDepthTexture,
27+
Option<&'static MainPassResolutionOverride>,
2728
);
2829
fn run(
2930
&self,
3031
graph: &mut RenderGraphContext,
3132
render_context: &mut RenderContext,
32-
(camera, view, target, depth): QueryItem<Self::ViewQuery>,
33+
(camera, view, target, depth, resolution_override): QueryItem<Self::ViewQuery>,
3334
world: &World,
3435
) -> Result<(), NodeRunError> {
3536
let view_entity = graph.view_entity();
@@ -69,7 +70,7 @@ impl ViewNode for MainTransparentPass3dNode {
6970
let pass_span = diagnostics.pass_span(&mut render_pass, "main_transparent_pass_3d");
7071

7172
if let Some(viewport) = camera.viewport.as_ref() {
72-
render_pass.set_camera_viewport(viewport);
73+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
7374
}
7475

7576
if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) {

crates/bevy_core_pipeline/src/deferred/node.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use bevy_ecs::{prelude::*, query::QueryItem};
2+
use bevy_render::camera::MainPassResolutionOverride;
23
use bevy_render::experimental::occlusion_culling::OcclusionCulling;
34
use bevy_render::render_graph::ViewNode;
45

@@ -66,6 +67,7 @@ impl ViewNode for LateDeferredGBufferPrepassNode {
6667
&'static ExtractedView,
6768
&'static ViewDepthTexture,
6869
&'static ViewPrepassTextures,
70+
Option<&'static MainPassResolutionOverride>,
6971
Has<OcclusionCulling>,
7072
Has<NoIndirectDrawing>,
7173
);
@@ -77,7 +79,7 @@ impl ViewNode for LateDeferredGBufferPrepassNode {
7779
view_query: QueryItem<'w, '_, Self::ViewQuery>,
7880
world: &'w World,
7981
) -> Result<(), NodeRunError> {
80-
let (_, _, _, _, occlusion_culling, no_indirect_drawing) = view_query;
82+
let (.., occlusion_culling, no_indirect_drawing) = view_query;
8183
if !occlusion_culling || no_indirect_drawing {
8284
return Ok(());
8385
}
@@ -105,7 +107,7 @@ impl ViewNode for LateDeferredGBufferPrepassNode {
105107
fn run_deferred_prepass<'w>(
106108
graph: &mut RenderGraphContext,
107109
render_context: &mut RenderContext<'w>,
108-
(camera, extracted_view, view_depth_texture, view_prepass_textures, _, _): QueryItem<
110+
(camera, extracted_view, view_depth_texture, view_prepass_textures, resolution_override, _, _): QueryItem<
109111
'w,
110112
'_,
111113
<LateDeferredGBufferPrepassNode as ViewNode>::ViewQuery,
@@ -220,7 +222,7 @@ fn run_deferred_prepass<'w>(
220222
});
221223
let mut render_pass = TrackedRenderPass::new(&render_device, render_pass);
222224
if let Some(viewport) = camera.viewport.as_ref() {
223-
render_pass.set_camera_viewport(viewport);
225+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
224226
}
225227

226228
// Opaque draws

crates/bevy_core_pipeline/src/oit/resolve/node.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bevy_ecs::{prelude::*, query::QueryItem};
22
use bevy_render::{
3-
camera::ExtractedCamera,
3+
camera::{ExtractedCamera, MainPassResolutionOverride},
44
render_graph::{NodeRunError, RenderGraphContext, RenderLabel, ViewNode},
55
render_resource::{BindGroupEntries, PipelineCache, RenderPassDescriptor},
66
renderer::RenderContext,
@@ -23,13 +23,14 @@ impl ViewNode for OitResolveNode {
2323
&'static ViewUniformOffset,
2424
&'static OitResolvePipelineId,
2525
&'static ViewDepthTexture,
26+
Option<&'static MainPassResolutionOverride>,
2627
);
2728

2829
fn run(
2930
&self,
3031
_graph: &mut RenderGraphContext,
3132
render_context: &mut RenderContext,
32-
(camera, view_target, view_uniform, oit_resolve_pipeline_id, depth): QueryItem<
33+
(camera, view_target, view_uniform, oit_resolve_pipeline_id, depth, resolution_override): QueryItem<
3334
Self::ViewQuery,
3435
>,
3536
world: &World,
@@ -63,7 +64,7 @@ impl ViewNode for OitResolveNode {
6364
});
6465

6566
if let Some(viewport) = camera.viewport.as_ref() {
66-
render_pass.set_camera_viewport(viewport);
67+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
6768
}
6869

6970
render_pass.set_render_pipeline(pipeline);

crates/bevy_core_pipeline/src/prepass/node.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bevy_ecs::{prelude::*, query::QueryItem};
22
use bevy_render::{
3-
camera::ExtractedCamera,
3+
camera::{ExtractedCamera, MainPassResolutionOverride},
44
diagnostic::RecordDiagnostics,
55
experimental::occlusion_culling::OcclusionCulling,
66
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
@@ -64,6 +64,7 @@ impl ViewNode for LatePrepassNode {
6464
Option<&'static RenderSkyboxPrepassPipeline>,
6565
Option<&'static SkyboxPrepassBindGroup>,
6666
Option<&'static PreviousViewUniformOffset>,
67+
Option<&'static MainPassResolutionOverride>,
6768
Has<OcclusionCulling>,
6869
Has<NoIndirectDrawing>,
6970
Has<DeferredPrepass>,
@@ -78,7 +79,7 @@ impl ViewNode for LatePrepassNode {
7879
) -> Result<(), NodeRunError> {
7980
// We only need a late prepass if we have occlusion culling and indirect
8081
// drawing.
81-
let (_, _, _, _, _, _, _, _, _, occlusion_culling, no_indirect_drawing, _) = query;
82+
let (.., occlusion_culling, no_indirect_drawing, _) = query;
8283
if !occlusion_culling || no_indirect_drawing {
8384
return Ok(());
8485
}
@@ -109,6 +110,7 @@ fn run_prepass<'w>(
109110
skybox_prepass_pipeline,
110111
skybox_prepass_bind_group,
111112
view_prev_uniform_offset,
113+
resolution_override,
112114
_,
113115
_,
114116
has_deferred,
@@ -183,7 +185,7 @@ fn run_prepass<'w>(
183185
let pass_span = diagnostics.pass_span(&mut render_pass, label);
184186

185187
if let Some(viewport) = camera.viewport.as_ref() {
186-
render_pass.set_camera_viewport(viewport);
188+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
187189
}
188190

189191
// Opaque draws

crates/bevy_pbr/src/meshlet/material_shade_nodes.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use bevy_ecs::{
1818
world::World,
1919
};
2020
use bevy_render::{
21-
camera::ExtractedCamera,
21+
camera::{ExtractedCamera, MainPassResolutionOverride},
2222
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
2323
render_resource::{
2424
LoadOp, Operations, PipelineCache, RenderPassDepthStencilAttachment, RenderPassDescriptor,
@@ -42,6 +42,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
4242
&'static ViewLightProbesUniformOffset,
4343
&'static ViewScreenSpaceReflectionsUniformOffset,
4444
&'static ViewEnvironmentMapUniformOffset,
45+
Option<&'static MainPassResolutionOverride>,
4546
&'static MeshletViewMaterialsMainOpaquePass,
4647
&'static MeshletViewBindGroups,
4748
&'static MeshletViewResources,
@@ -61,6 +62,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
6162
view_light_probes_offset,
6263
view_ssr_offset,
6364
view_environment_map_offset,
65+
resolution_override,
6466
meshlet_view_materials,
6567
meshlet_view_bind_groups,
6668
meshlet_view_resources,
@@ -101,7 +103,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode {
101103
occlusion_query_set: None,
102104
});
103105
if let Some(viewport) = camera.viewport.as_ref() {
104-
render_pass.set_camera_viewport(viewport);
106+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
105107
}
106108

107109
render_pass.set_bind_group(
@@ -147,6 +149,7 @@ impl ViewNode for MeshletPrepassNode {
147149
&'static ViewPrepassTextures,
148150
&'static ViewUniformOffset,
149151
&'static PreviousViewUniformOffset,
152+
Option<&'static MainPassResolutionOverride>,
150153
Has<MotionVectorPrepass>,
151154
&'static MeshletViewMaterialsPrepass,
152155
&'static MeshletViewBindGroups,
@@ -162,6 +165,7 @@ impl ViewNode for MeshletPrepassNode {
162165
view_prepass_textures,
163166
view_uniform_offset,
164167
previous_view_uniform_offset,
168+
resolution_override,
165169
view_has_motion_vector_prepass,
166170
meshlet_view_materials,
167171
meshlet_view_bind_groups,
@@ -219,7 +223,7 @@ impl ViewNode for MeshletPrepassNode {
219223
occlusion_query_set: None,
220224
});
221225
if let Some(viewport) = camera.viewport.as_ref() {
222-
render_pass.set_camera_viewport(viewport);
226+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
223227
}
224228

225229
if view_has_motion_vector_prepass {
@@ -270,6 +274,7 @@ impl ViewNode for MeshletDeferredGBufferPrepassNode {
270274
&'static ViewPrepassTextures,
271275
&'static ViewUniformOffset,
272276
&'static PreviousViewUniformOffset,
277+
Option<&'static MainPassResolutionOverride>,
273278
Has<MotionVectorPrepass>,
274279
&'static MeshletViewMaterialsDeferredGBufferPrepass,
275280
&'static MeshletViewBindGroups,
@@ -285,6 +290,7 @@ impl ViewNode for MeshletDeferredGBufferPrepassNode {
285290
view_prepass_textures,
286291
view_uniform_offset,
287292
previous_view_uniform_offset,
293+
resolution_override,
288294
view_has_motion_vector_prepass,
289295
meshlet_view_materials,
290296
meshlet_view_bind_groups,
@@ -347,7 +353,7 @@ impl ViewNode for MeshletDeferredGBufferPrepassNode {
347353
occlusion_query_set: None,
348354
});
349355
if let Some(viewport) = camera.viewport.as_ref() {
350-
render_pass.set_camera_viewport(viewport);
356+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
351357
}
352358

353359
if view_has_motion_vector_prepass {

crates/bevy_render/src/camera/camera.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ impl Viewport {
111111
}
112112
}
113113
}
114+
115+
pub fn with_override(
116+
&self,
117+
main_pass_resolution_override: Option<&MainPassResolutionOverride>,
118+
) -> Self {
119+
let mut viewport = self.clone();
120+
if let Some(override_size) = main_pass_resolution_override {
121+
viewport.physical_size = **override_size;
122+
}
123+
viewport
124+
}
114125
}
115126

116127
/// Settings to define a camera sub view.
@@ -1366,6 +1377,19 @@ impl TemporalJitter {
13661377
#[reflect(Default, Component)]
13671378
pub struct MipBias(pub f32);
13681379

1380+
/// Override the resolution a 3d camera's main pass is rendered at.
1381+
///
1382+
/// Does not affect post processing.
1383+
///
1384+
/// ## Usage
1385+
///
1386+
/// * Insert this component on a 3d camera entity in the render world.
1387+
/// * The resolution override must be smaller than the camera's viewport size.
1388+
/// * The resolution override is specified in physical pixels.
1389+
#[derive(Component, Reflect, Deref)]
1390+
#[reflect(Component)]
1391+
pub struct MainPassResolutionOverride(pub UVec2);
1392+
13691393
impl Default for MipBias {
13701394
fn default() -> Self {
13711395
Self(-1.0)

crates/bevy_render/src/camera/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl Plugin for CameraPlugin {
2929
.register_type::<Exposure>()
3030
.register_type::<TemporalJitter>()
3131
.register_type::<MipBias>()
32+
.register_type::<MainPassResolutionOverride>()
3233
.init_resource::<ManualTextureViews>()
3334
.init_resource::<ClearColor>()
3435
.add_plugins((

examples/shader/custom_render_phase.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use bevy::{
3434
},
3535
GetBatchData, GetFullBatchData,
3636
},
37-
camera::ExtractedCamera,
37+
camera::{ExtractedCamera, MainPassResolutionOverride},
3838
extract_component::{ExtractComponent, ExtractComponentPlugin},
3939
mesh::{allocator::MeshAllocator, MeshVertexBufferLayoutRef, RenderMesh},
4040
render_asset::RenderAssets,
@@ -589,13 +589,14 @@ impl ViewNode for CustomDrawNode {
589589
&'static ExtractedCamera,
590590
&'static ExtractedView,
591591
&'static ViewTarget,
592+
Option<&'static MainPassResolutionOverride>,
592593
);
593594

594595
fn run<'w>(
595596
&self,
596597
graph: &mut RenderGraphContext,
597598
render_context: &mut RenderContext<'w>,
598-
(camera, view, target): QueryItem<'w, '_, Self::ViewQuery>,
599+
(camera, view, target, resolution_override): QueryItem<'w, '_, Self::ViewQuery>,
599600
world: &'w World,
600601
) -> Result<(), NodeRunError> {
601602
// First, we need to get our phases resource
@@ -625,7 +626,7 @@ impl ViewNode for CustomDrawNode {
625626
});
626627

627628
if let Some(viewport) = camera.viewport.as_ref() {
628-
render_pass.set_camera_viewport(viewport);
629+
render_pass.set_camera_viewport(&viewport.with_override(resolution_override));
629630
}
630631

631632
// Render the phase

0 commit comments

Comments
 (0)