Skip to content

headless_renderer: copy render target after RenderQueue has been submitted#24178

Open
rectalogic wants to merge 1 commit into
bevyengine:mainfrom
rectalogic:headless_rendergraph
Open

headless_renderer: copy render target after RenderQueue has been submitted#24178
rectalogic wants to merge 1 commit into
bevyengine:mainfrom
rectalogic:headless_rendergraph

Conversation

@rectalogic
Copy link
Copy Markdown
Contributor

Objective

Currently the headless_renderer example copies the render target in the RenderGraph schedule. The copy happens at the beginning of the schedule before the current frame has been submitted to the RenderQueue. So it is always copying the render results from the previous frame.

Solution

Schedule the copy after RenderGraphSystems::Submit

Testing

To test, temporarily modify headless_renderer to set single_image: false and pre_roll_frames to 0 and to stamp each render with the frame number - using the patch below.

Before this PR, the rendered frames were:

With this PR, the rendered frames are:

diff --git a/examples/app/headless_renderer.rs b/examples/app/headless_renderer.rs
index 82107edd7..fb977d12e 100644
--- a/examples/app/headless_renderer.rs
+++ b/examples/app/headless_renderer.rs
@@ -71,7 +71,7 @@ fn main() {
     let config = AppConfig {
         width: 1920,
         height: 1080,
-        single_image: true,
+        single_image: false,
     };
 
     // setup frame capture
@@ -165,10 +165,12 @@ fn setup(
         // 2. Few black box images
         // 3. Fully rendered scene images
         // Exact number depends on device speed, device load and scene size
-        40,
+        0,
         "main_scene".into(),
     );
 
+    commands.spawn(Text::new("initial"));
+
     // Scene example for non black box picture
     // circular base
     commands.spawn((
@@ -193,6 +195,7 @@ fn setup(
 
     commands.spawn((
         Camera3d::default(),
+        IsDefaultUiCamera,
         render_target,
         Tonemapping::None,
         Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
@@ -457,7 +460,12 @@ fn update(
     mut scene_controller: ResMut<SceneController>,
     mut app_exit_writer: MessageWriter<AppExit>,
     mut file_number: Local<u32>,
+    mut text: Single<&mut Text>,
+    mut frame_number: Local<u32>,
 ) {
+    text.0 = format!("frame {}", *frame_number);
+    *frame_number += 1;
+
     if let SceneState::Render(n) = scene_controller.state {
         if n < 1 {
             // We don't want to block the main world on this,

@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-Rendering Drawing game state to the screen C-Examples An addition or correction to our examples labels May 7, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in Rendering May 7, 2026
@alice-i-cecile alice-i-cecile added D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior C-Examples An addition or correction to our examples D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

3 participants