Skip to content
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

Multithreaded render command encoding #9172

Merged
merged 64 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
11af143
Add RenderContext::add_async_command_buffer()
JMS55 Jul 16, 2023
a64583c
WIP
JMS55 Jul 16, 2023
66d2b83
More WIP
JMS55 Jul 16, 2023
8b391d2
Misc
JMS55 Jul 16, 2023
6bbbe41
Complete RenderContext::finish()
JMS55 Jul 16, 2023
a4f2ab6
Misc
JMS55 Jul 16, 2023
d5112d6
More lifetime WIP
JMS55 Jul 16, 2023
f002065
Fix lifetime issues
JMS55 Jul 16, 2023
ad04cd5
Misc lifetime rename
JMS55 Jul 16, 2023
6f2fbfa
Move trace into command gen task
JMS55 Jul 16, 2023
6e04259
Misc formatting
JMS55 Jul 16, 2023
2b447f4
Avoid unnecessary work when no command buffer generation tasks are qu…
JMS55 Jul 16, 2023
0c830b9
Revert rename
JMS55 Jul 16, 2023
ef4a411
Pass RenderDevice as task parameter
JMS55 Jul 16, 2023
5446d28
Use command buffer generation tasks in more nodes
JMS55 Jul 16, 2023
d52bcba
Use arcanized wgpu
JMS55 Jul 16, 2023
585ed5c
Fix bug with wrong color attachment
JMS55 Jul 17, 2023
175d1c8
Merge commit 'c6a1bf063b69a81e9af3de647cdebcefeba8b1d0' into multi-th…
JMS55 Aug 1, 2023
4cb683d
Update wgpu/naga/naga_oil
JMS55 Aug 1, 2023
53dbfd4
Merge remote-tracking branch 'bevy/main' into multi-threaded-command-…
JMS55 Aug 8, 2023
f406770
Merge commit 'ac8f36743e7cb7e6a85e563e3ff52e57ec1676a5' into multi-th…
JMS55 Aug 12, 2023
8004266
Merge commit 'cfa3303cf3c9dab5c285daab4e0e9086525836cc' into multi-th…
JMS55 Aug 16, 2023
533a6b5
Merge commit '8ace2ff9e361dd7ef1bc620b84674def0cb56454' into multi-th…
JMS55 Sep 24, 2023
821b2d6
wip, need to fix raw-window-handle 0.6 stuff
Elabajaba Dec 27, 2023
7770046
works
Elabajaba Jan 8, 2024
65abdb3
fix re-export
Elabajaba Jan 9, 2024
511e96a
bump wgpu
Elabajaba Jan 9, 2024
9b0d8d3
update + fmt
Elabajaba Jan 17, 2024
e3cb986
wgpu 0.19
Elabajaba Jan 18, 2024
83de50b
replace deprecated traits (#2)
mockersf Jan 19, 2024
82b889f
do the wrong thing to make webgpu work for now by making it a mutuall…
Elabajaba Jan 19, 2024
216b147
;
Elabajaba Jan 19, 2024
54f8642
wgpu 0.19.1
Elabajaba Jan 22, 2024
c5471fe
(WIP) have webgpu override webgl feature when targeting wasm
Elabajaba Jan 23, 2024
474ff2e
remove nice warning as it breaks webgpu
Elabajaba Jan 24, 2024
6c3da2c
remove instance_index workaround as it's no longer needed and now bre…
Elabajaba Jan 24, 2024
01a617b
remove unnecessary pub
Elabajaba Jan 24, 2024
3e68449
TextureDataOrder::LayerMajor -> default()
Elabajaba Jan 24, 2024
8d109b6
fix missed deprecated block_size -> block_copy_size
Elabajaba Jan 24, 2024
d2508cd
`webgpu` feature docs
Elabajaba Jan 24, 2024
0508445
fix weird linux timeout check
Elabajaba Jan 24, 2024
d03a634
example readme template fix
Elabajaba Jan 24, 2024
1ccbc90
update features
Elabajaba Jan 24, 2024
817a6f8
move safety comment to satisfy CI
Elabajaba Jan 24, 2024
76c4165
fix raw_handle docs
Elabajaba Jan 24, 2024
cde76a8
satisfy safety gods
Elabajaba Jan 24, 2024
934bb55
Merge remote-tracking branch 'elabajaba/wgpu-019' into multi-threaded…
JMS55 Jan 26, 2024
1447214
Fix
JMS55 Jan 26, 2024
1a1dad4
Merge commit '35ac1b152ee1b2c7b2930f67d93be0db6f395b47' into multi-th…
JMS55 Jan 26, 2024
818a176
WIP
JMS55 Jan 26, 2024
1888180
Misc
JMS55 Jan 26, 2024
601d660
Misc cleanup
JMS55 Jan 26, 2024
38e8151
WIP
JMS55 Jan 27, 2024
c738af2
Merge commit '8f25805b66f533d260a50a3fdcf12e89c8ffb493' into multi-th…
JMS55 Jan 27, 2024
7e837f5
Merge commit 'c227fc9fada2a327d174644a3ce7e5c210aecfb6' into multi-th…
JMS55 Jan 27, 2024
cb7df79
Merge commit '14f1a4f10e35375df512e50e26b3ffd3fe88dcb7' into multi-th…
JMS55 Jan 30, 2024
bdd9950
Merge commit 'e3126a494f4759d52f2492559d3f585a41a4ce29' into multi-th…
JMS55 Feb 1, 2024
b966ee8
HACK: Serial encoding for AMD + Windows + Vulkan
JMS55 Feb 1, 2024
d1c3cdc
Bypass unused
JMS55 Feb 1, 2024
8ed95d5
Fix unused import
JMS55 Feb 1, 2024
b66799f
Remove redundant code
JMS55 Feb 7, 2024
ecb810f
Merge commit 'ab16f5ed6afad717896c278381a0692531eedf44' into multi-th…
JMS55 Feb 7, 2024
14a2822
Merge commit '5313730534684408b6281fb2727144d6890524b0' into multi-th…
JMS55 Feb 9, 2024
7d1f6f7
Add trace to shadow pass
JMS55 Feb 9, 2024
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
101 changes: 59 additions & 42 deletions crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use bevy_ecs::{prelude::World, query::QueryItem};
use bevy_render::{
camera::ExtractedCamera,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::RenderPhase,
render_resource::{PipelineCache, RenderPassDescriptor, StoreOp},
render_phase::{RenderPhase, TrackedRenderPass},
render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget, ViewUniformOffset},
};
Expand All @@ -31,10 +31,10 @@ impl ViewNode for MainOpaquePass3dNode {
&'static ViewUniformOffset,
);

fn run(
fn run<'w>(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
render_context: &mut RenderContext<'w>,
(
camera,
opaque_phase,
Expand All @@ -44,52 +44,69 @@ impl ViewNode for MainOpaquePass3dNode {
skybox_pipeline,
skybox_bind_group,
view_uniform_offset,
): QueryItem<Self::ViewQuery>,
world: &World,
): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
// Run the opaque pass, sorted front-to-back
// NOTE: Scoped to drop the mutable borrow of render_context
#[cfg(feature = "trace")]
let _main_opaque_pass_3d_span = info_span!("main_opaque_pass_3d").entered();
let color_attachments = [Some(target.get_color_attachment())];
let depth_stencil_attachment = Some(depth.get_attachment(StoreOp::Store));

// Setup render pass
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_opaque_pass_3d"),
color_attachments: &[Some(target.get_color_attachment())],
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
timestamp_writes: None,
occlusion_query_set: None,
});
let view_entity = graph.view_entity();
render_context.add_command_buffer_generation_task(move |render_device| {
#[cfg(feature = "trace")]
let _main_opaque_pass_3d_span = info_span!("main_opaque_pass_3d").entered();

if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(viewport);
}
// Command encoder setup
let mut command_encoder =
render_device.create_command_encoder(&CommandEncoderDescriptor {
label: Some("main_opaque_pass_3d_command_encoder"),
});

let view_entity = graph.view_entity();
// Render pass setup
let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("main_opaque_pass_3d"),
color_attachments: &color_attachments,
depth_stencil_attachment,
timestamp_writes: None,
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);
}

// Opaque draws
opaque_phase.render(&mut render_pass, world, view_entity);
// Opaque draws
if !opaque_phase.items.is_empty() {
#[cfg(feature = "trace")]
let _opaque_main_pass_3d_span = info_span!("opaque_main_pass_3d").entered();
Copy link
Contributor

Choose a reason for hiding this comment

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

Smallest of all nits: I can see that this span has a much narrower scope than the one on line 56, but they have super similar names. Is there a good reason for them to be this similar? I assume it's to maintain compatibility with the previous version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I just copy pasted what the previous version did, yeah

opaque_phase.render(&mut render_pass, world, view_entity);
}

// Alpha draws
if !alpha_mask_phase.items.is_empty() {
alpha_mask_phase.render(&mut render_pass, world, view_entity);
}
// Alpha draws
if !alpha_mask_phase.items.is_empty() {
#[cfg(feature = "trace")]
let _alpha_mask_main_pass_3d_span = info_span!("alpha_mask_main_pass_3d").entered();
alpha_mask_phase.render(&mut render_pass, world, view_entity);
}

// Draw the skybox using a fullscreen triangle
if let (Some(skybox_pipeline), Some(SkyboxBindGroup(skybox_bind_group))) =
(skybox_pipeline, skybox_bind_group)
{
let pipeline_cache = world.resource::<PipelineCache>();
if let Some(pipeline) = pipeline_cache.get_render_pipeline(skybox_pipeline.0) {
render_pass.set_render_pipeline(pipeline);
render_pass.set_bind_group(
0,
&skybox_bind_group.0,
&[view_uniform_offset.offset, skybox_bind_group.1],
);
render_pass.draw(0..3, 0..1);
// Skybox draw using a fullscreen triangle
if let (Some(skybox_pipeline), Some(SkyboxBindGroup(skybox_bind_group))) =
(skybox_pipeline, skybox_bind_group)
{
let pipeline_cache = world.resource::<PipelineCache>();
if let Some(pipeline) = pipeline_cache.get_render_pipeline(skybox_pipeline.0) {
render_pass.set_render_pipeline(pipeline);
render_pass.set_bind_group(
0,
&skybox_bind_group.0,
&[view_uniform_offset.offset, skybox_bind_group.1],
);
render_pass.draw(0..3, 0..1);
}
}
}

drop(render_pass);
command_encoder.finish()
});

Ok(())
}
Expand Down
66 changes: 40 additions & 26 deletions crates/bevy_core_pipeline/src/deferred/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryItem;
use bevy_render::render_graph::ViewNode;

use bevy_render::render_resource::StoreOp;
use bevy_render::render_phase::TrackedRenderPass;
use bevy_render::render_resource::{CommandEncoderDescriptor, StoreOp};
use bevy_render::{
camera::ExtractedCamera,
render_graph::{NodeRunError, RenderGraphContext},
Expand Down Expand Up @@ -33,21 +34,19 @@ impl ViewNode for DeferredGBufferPrepassNode {
&'static ViewPrepassTextures,
);

fn run(
fn run<'w>(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
render_context: &mut RenderContext<'w>,
(
camera,
opaque_deferred_phase,
alpha_mask_deferred_phase,
view_depth_texture,
view_prepass_textures,
): QueryItem<Self::ViewQuery>,
world: &World,
): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();

let mut color_attachments = vec![];
color_attachments.push(
view_prepass_textures
Expand Down Expand Up @@ -107,49 +106,64 @@ impl ViewNode for DeferredGBufferPrepassNode {
.map(|deferred_lighting_pass_id| deferred_lighting_pass_id.get_attachment()),
);

// If all color attachments are none: clear the color attachment list so that no fragment shader is required
if color_attachments.iter().all(Option::is_none) {
// All attachments are none: clear the attachment list so that no fragment shader is required.
color_attachments.clear();
}

{
// Set up the pass descriptor with the depth attachment and optional color attachments.
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));

let view_entity = graph.view_entity();
render_context.add_command_buffer_generation_task(move |render_device| {
#[cfg(feature = "trace")]
let _deferred_span = info_span!("deferred").entered();

// Command encoder setup
let mut command_encoder =
render_device.create_command_encoder(&CommandEncoderDescriptor {
label: Some("deferred_command_encoder"),
});

// Render pass setup
let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("deferred"),
color_attachments: &color_attachments,
depth_stencil_attachment: Some(view_depth_texture.get_attachment(StoreOp::Store)),
depth_stencil_attachment,
timestamp_writes: None,
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);
}

// Always run deferred pass to ensure the deferred gbuffer and deferred_lighting_pass_id are cleared.
{
// Run the prepass, sorted front-to-back.
// Opaque draws
if !opaque_deferred_phase.items.is_empty() {
#[cfg(feature = "trace")]
let _opaque_prepass_span = info_span!("opaque_deferred").entered();
opaque_deferred_phase.render(&mut render_pass, world, view_entity);
}

// Alpha masked draws
if !alpha_mask_deferred_phase.items.is_empty() {
// Run the deferred, sorted front-to-back.
#[cfg(feature = "trace")]
let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred").entered();
alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity);
}
}

if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
// Copy depth buffer to texture.
render_context.command_encoder().copy_texture_to_texture(
view_depth_texture.texture.as_image_copy(),
prepass_depth_texture.texture.texture.as_image_copy(),
view_prepass_textures.size,
);
}
drop(render_pass);

// Copy prepass depth to the main depth texture
if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
command_encoder.copy_texture_to_texture(
view_depth_texture.texture.as_image_copy(),
prepass_depth_texture.texture.texture.as_image_copy(),
view_prepass_textures.size,
);
}

command_encoder.finish()
});

Ok(())
}
Expand Down
77 changes: 45 additions & 32 deletions crates/bevy_core_pipeline/src/prepass/node.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryItem;
use bevy_render::render_graph::ViewNode;
use bevy_render::render_resource::StoreOp;
use bevy_render::{
camera::ExtractedCamera,
render_graph::{NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::RenderPassDescriptor,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::{RenderPhase, TrackedRenderPass},
render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::ViewDepthTexture,
};
Expand All @@ -31,22 +29,20 @@ impl ViewNode for PrepassNode {
Option<&'static DeferredPrepass>,
);

fn run(
fn run<'w>(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
render_context: &mut RenderContext<'w>,
(
camera,
opaque_prepass_phase,
alpha_mask_prepass_phase,
view_depth_texture,
view_prepass_textures,
deferred_prepass,
): QueryItem<Self::ViewQuery>,
world: &World,
): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();

let mut color_attachments = vec![
view_prepass_textures
.normal
Expand All @@ -56,55 +52,72 @@ impl ViewNode for PrepassNode {
.motion_vectors
.as_ref()
.map(|motion_vectors_texture| motion_vectors_texture.get_attachment()),
// Use None in place of Deferred attachments
// Use None in place of deferred attachments
None,
None,
];

// If all color attachments are none: clear the color attachment list so that no fragment shader is required
if color_attachments.iter().all(Option::is_none) {
// all attachments are none: clear the attachment list so that no fragment shader is required
color_attachments.clear();
}

{
// Set up the pass descriptor with the depth attachment and optional color attachments
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
let depth_stencil_attachment = Some(view_depth_texture.get_attachment(StoreOp::Store));

let view_entity = graph.view_entity();
render_context.add_command_buffer_generation_task(move |render_device| {
#[cfg(feature = "trace")]
let _prepass_span = info_span!("prepass").entered();

// Command encoder setup
let mut command_encoder =
render_device.create_command_encoder(&CommandEncoderDescriptor {
label: Some("prepass_command_encoder"),
});

// Render pass setup
let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("prepass"),
color_attachments: &color_attachments,
depth_stencil_attachment: Some(view_depth_texture.get_attachment(StoreOp::Store)),
depth_stencil_attachment,
timestamp_writes: None,
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);
}

// Always run opaque pass to ensure screen is cleared
{
// Run the prepass, sorted front-to-back
// Opaque draws
if !opaque_prepass_phase.items.is_empty() {
#[cfg(feature = "trace")]
let _opaque_prepass_span = info_span!("opaque_prepass").entered();
opaque_prepass_phase.render(&mut render_pass, world, view_entity);
}

// Alpha masked draws
if !alpha_mask_prepass_phase.items.is_empty() {
// Run the prepass, sorted front-to-back
#[cfg(feature = "trace")]
let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered();
alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity);
}
}
if deferred_prepass.is_none() {
// Copy if deferred isn't going to
if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
// Copy depth buffer to texture
render_context.command_encoder().copy_texture_to_texture(
view_depth_texture.texture.as_image_copy(),
prepass_depth_texture.texture.texture.as_image_copy(),
view_prepass_textures.size,
);

drop(render_pass);

// Copy prepass depth to the main depth texture if deferred isn't going to
if deferred_prepass.is_none() {
if let Some(prepass_depth_texture) = &view_prepass_textures.depth {
command_encoder.copy_texture_to_texture(
view_depth_texture.texture.as_image_copy(),
prepass_depth_texture.texture.texture.as_image_copy(),
view_prepass_textures.size,
);
}
}
}

command_encoder.finish()
});

Ok(())
}
}
Loading