forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improve compile time by type-erasing wgpu structs (bevyengine#5950)
# Objective structs containing wgpu types take a long time to compile. this is particularly bad for generics containing the wgpu structs (like the depth pipeline builder with `#[derive(SystemParam)]` i've been working on). we can avoid that by boxing and type-erasing in the bevy `render_resource` wrappers. type system magic is not a strength of mine so i guess there will be a cleaner way to achieve this, happy to take feedback or for it to be taken as a proof of concept if someone else wants to do a better job. ## Solution - add macros to box and type-erase in debug mode - leave current impl for release mode timings: <html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 15"> <link id=Main-File rel=Main-File href="file:///C:/Users/robfm/AppData/Local/Temp/msohtmlclip1/01/clip.htm"> <link rel=File-List href="file:///C:/Users/robfm/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml"> <!--table {mso-displayed-decimal-separator:"\."; mso-displayed-thousand-separator:"\,";} @page {margin:.75in .7in .75in .7in; mso-header-margin:.3in; mso-footer-margin:.3in;} tr {mso-height-source:auto;} col {mso-width-source:auto;} br {mso-data-placement:same-cell;} td {padding-top:1px; padding-right:1px; padding-left:1px; mso-ignore:padding; color:black; font-size:11.0pt; font-weight:400; font-style:normal; text-decoration:none; font-family:Calibri, sans-serif; mso-font-charset:0; mso-number-format:General; text-align:general; vertical-align:bottom; border:none; mso-background-source:auto; mso-pattern:auto; mso-protection:locked visible; white-space:nowrap; mso-rotate:0;} .xl65 {mso-number-format:0%;} .xl66 {vertical-align:middle; white-space:normal;} .xl67 {vertical-align:middle;} --> </head> <body link="#0563C1" vlink="#954F72"> current | | | -- | -- | -- | -- | Total time: | 64.9s | | bevy_pbr v0.9.0-dev | 19.2s | | bevy_render v0.9.0-dev | 17.0s | | bevy_sprite v0.9.0-dev | 15.1s | | DepthPipelineBuilder | 18.7s | | | | with type-erasing | | | diff | Total time: | 49.0s | -24% | bevy_render v0.9.0-dev | 12.0s | -38% | bevy_pbr v0.9.0-dev | 8.7s | -49% | bevy_sprite v0.9.0-dev | 6.1s | -60% | DepthPipelineBuilder | 1.2s | -94% </body> </html> the depth pipeline builder is a binary with body: ```rust use std::{marker::PhantomData, hash::Hash}; use bevy::{prelude::*, ecs::system::SystemParam, pbr::{RenderMaterials, MaterialPipeline, ShadowPipeline}, render::{renderer::RenderDevice, render_resource::{SpecializedMeshPipelines, PipelineCache}, render_asset::RenderAssets}}; fn main() { println!("Hello, world p!\n"); } #[derive(SystemParam)] pub struct DepthPipelineBuilder<'w, 's, M: Material> where M::Data: Eq + Hash + Clone, { render_device: Res<'w, RenderDevice>, material_pipeline: Res<'w, MaterialPipeline<M>>, material_pipelines: ResMut<'w, SpecializedMeshPipelines<MaterialPipeline<M>>>, shadow_pipeline: Res<'w, ShadowPipeline>, pipeline_cache: ResMut<'w, PipelineCache>, render_meshes: Res<'w, RenderAssets<Mesh>>, render_materials: Res<'w, RenderMaterials<M>>, msaa: Res<'w, Msaa>, #[system_param(ignore)] _p: PhantomData<&'s M>, } ```
- Loading branch information
Showing
10 changed files
with
202 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
crates/bevy_render/src/render_resource/resource_macros.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// structs containing wgpu types take a long time to compile. this is particularly bad for generic | ||
// structs containing wgpu structs. we avoid that in debug builds (and for cargo check and rust analyzer) | ||
// by boxing and type-erasing with the `render_resource_wrapper` macro. | ||
// analysis from https://github.com/bevyengine/bevy/pull/5950#issuecomment-1243473071 indicates this is | ||
// due to `evaluate_obligations`. we should check if this can be removed after a fix lands for | ||
// https://github.com/rust-lang/rust/issues/99188 (and after other `evaluate_obligations`-related changes). | ||
#[cfg(debug_assertions)] | ||
#[macro_export] | ||
macro_rules! render_resource_wrapper { | ||
($wrapper_type:ident, $wgpu_type:ty) => { | ||
#[derive(Clone, Debug)] | ||
pub struct $wrapper_type(Option<std::sync::Arc<Box<()>>>); | ||
|
||
impl $wrapper_type { | ||
pub fn new(value: $wgpu_type) -> Self { | ||
unsafe { | ||
Self(Some(std::sync::Arc::new(std::mem::transmute(Box::new( | ||
value, | ||
))))) | ||
} | ||
} | ||
|
||
pub fn try_unwrap(mut self) -> Option<$wgpu_type> { | ||
let inner = self.0.take(); | ||
if let Some(inner) = inner { | ||
match std::sync::Arc::try_unwrap(inner) { | ||
Ok(untyped_box) => { | ||
let typed_box = unsafe { | ||
std::mem::transmute::<Box<()>, Box<$wgpu_type>>(untyped_box) | ||
}; | ||
Some(*typed_box) | ||
} | ||
Err(inner) => { | ||
let _ = unsafe { | ||
std::mem::transmute::< | ||
std::sync::Arc<Box<()>>, | ||
std::sync::Arc<Box<$wgpu_type>>, | ||
>(inner) | ||
}; | ||
None | ||
} | ||
} | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
impl std::ops::Deref for $wrapper_type { | ||
type Target = $wgpu_type; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
let untyped_box = self | ||
.0 | ||
.as_ref() | ||
.expect("render_resource_wrapper inner value has already been taken (via drop or try_unwrap") | ||
.as_ref(); | ||
|
||
let typed_box = | ||
unsafe { std::mem::transmute::<&Box<()>, &Box<$wgpu_type>>(untyped_box) }; | ||
typed_box.as_ref() | ||
} | ||
} | ||
|
||
impl Drop for $wrapper_type { | ||
fn drop(&mut self) { | ||
let inner = self.0.take(); | ||
if let Some(inner) = inner { | ||
let _ = unsafe { | ||
std::mem::transmute::< | ||
std::sync::Arc<Box<()>>, | ||
std::sync::Arc<Box<$wgpu_type>>, | ||
>(inner) | ||
}; | ||
} | ||
} | ||
} | ||
|
||
// Arc<Box<()>> and Arc<()> will be Sync and Send even when $wgpu_type is not Sync or Send. | ||
// We ensure correctness by checking that $wgpu_type does implement Send and Sync. | ||
// If in future there is a case where a wrapper is required for a non-send/sync type | ||
// we can implement a macro variant that also does `impl !Send for $wrapper_type {}` and | ||
// `impl !Sync for $wrapper_type {}` | ||
const _: () = { | ||
trait AssertSendSyncBound: Send + Sync {} | ||
impl AssertSendSyncBound for $wgpu_type {} | ||
}; | ||
}; | ||
} | ||
|
||
#[cfg(not(debug_assertions))] | ||
#[macro_export] | ||
macro_rules! render_resource_wrapper { | ||
($wrapper_type:ident, $wgpu_type:ty) => { | ||
#[derive(Clone, Debug)] | ||
pub struct $wrapper_type(std::sync::Arc<$wgpu_type>); | ||
|
||
impl $wrapper_type { | ||
pub fn new(value: $wgpu_type) -> Self { | ||
Self(std::sync::Arc::new(value)) | ||
} | ||
|
||
pub fn try_unwrap(self) -> Option<$wgpu_type> { | ||
std::sync::Arc::try_unwrap(self.0).ok() | ||
} | ||
} | ||
|
||
impl std::ops::Deref for $wrapper_type { | ||
type Target = $wgpu_type; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
self.0.as_ref() | ||
} | ||
} | ||
|
||
const _: () = { | ||
trait AssertSendSyncBound: Send + Sync {} | ||
impl AssertSendSyncBound for $wgpu_type {} | ||
}; | ||
}; | ||
} | ||
|
||
pub use render_resource_wrapper; |
Oops, something went wrong.