1#![allow(missing_docs)]
3#![allow(unsafe_code)]
4#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5#![doc(
6 html_logo_url = "https://bevyengine.org/assets/icon.png",
7 html_favicon_url = "https://bevyengine.org/assets/icon.png"
8)]
9
10#[cfg(target_pointer_width = "16")]
11compile_error!("bevy_render cannot compile for a 16-bit platform.");
12
13extern crate core;
14
15pub mod alpha;
16pub mod batching;
17pub mod camera;
18pub mod diagnostic;
19pub mod extract_component;
20pub mod extract_instances;
21mod extract_param;
22pub mod extract_resource;
23pub mod globals;
24pub mod gpu_component_array_buffer;
25pub mod mesh;
26#[cfg(not(target_arch = "wasm32"))]
27pub mod pipelined_rendering;
28pub mod primitives;
29pub mod render_asset;
30pub mod render_graph;
31pub mod render_phase;
32pub mod render_resource;
33pub mod renderer;
34pub mod settings;
35mod spatial_bundle;
36pub mod texture;
37pub mod view;
38pub mod prelude {
39 #[doc(hidden)]
40 pub use crate::{
41 alpha::AlphaMode,
42 camera::{
43 Camera, ClearColor, ClearColorConfig, OrthographicProjection, PerspectiveProjection,
44 Projection,
45 },
46 mesh::{morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh},
47 render_resource::Shader,
48 spatial_bundle::SpatialBundle,
49 texture::{image_texture_conversion::IntoDynamicImageError, Image, ImagePlugin},
50 view::{InheritedVisibility, Msaa, ViewVisibility, Visibility, VisibilityBundle},
51 ExtractSchedule,
52 };
53}
54
55use batching::gpu_preprocessing::BatchingPlugin;
56use bevy_ecs::schedule::ScheduleBuildSettings;
57use bevy_utils::prelude::default;
58pub use extract_param::Extract;
59
60use bevy_hierarchy::ValidParentCheckPlugin;
61use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
62use extract_resource::ExtractResourcePlugin;
63use globals::GlobalsPlugin;
64use render_asset::RenderAssetBytesPerFrame;
65use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
66
67use crate::mesh::GpuMesh;
68use crate::renderer::WgpuWrapper;
69use crate::{
70 camera::CameraPlugin,
71 mesh::{morph::MorphPlugin, MeshPlugin},
72 render_asset::prepare_assets,
73 render_resource::{PipelineCache, Shader, ShaderLoader},
74 renderer::{render_system, RenderInstance},
75 settings::RenderCreation,
76 view::{ViewPlugin, WindowRenderPlugin},
77};
78use bevy_app::{App, AppLabel, Plugin, SubApp};
79use bevy_asset::{load_internal_asset, AssetApp, AssetServer, Handle};
80use bevy_ecs::{prelude::*, schedule::ScheduleLabel, system::SystemState};
81use bevy_utils::tracing::debug;
82use std::{
83 ops::{Deref, DerefMut},
84 sync::{Arc, Mutex},
85};
86
87#[derive(Default)]
96pub struct RenderPlugin {
97 pub render_creation: RenderCreation,
98 pub synchronous_pipeline_compilation: bool,
101}
102
103#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
108pub enum RenderSet {
109 ExtractCommands,
111 PrepareAssets,
113 ManageViews,
115 Queue,
118 QueueMeshes,
120 PhaseSort,
125 Prepare,
128 PrepareResources,
130 PrepareResourcesFlush,
132 PrepareBindGroups,
134 Render,
137 Cleanup,
139}
140
141#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
143pub struct Render;
144
145impl Render {
146 pub fn base_schedule() -> Schedule {
150 use RenderSet::*;
151
152 let mut schedule = Schedule::new(Self);
153
154 schedule.configure_sets(
155 (
156 ExtractCommands,
157 ManageViews,
158 Queue,
159 PhaseSort,
160 Prepare,
161 Render,
162 Cleanup,
163 )
164 .chain(),
165 );
166
167 schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain());
168 schedule.configure_sets(QueueMeshes.in_set(Queue).after(prepare_assets::<GpuMesh>));
169 schedule.configure_sets(
170 (PrepareResources, PrepareResourcesFlush, PrepareBindGroups)
171 .chain()
172 .in_set(Prepare),
173 );
174
175 schedule
176 }
177}
178
179#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash)]
187pub struct ExtractSchedule;
188
189#[derive(Resource, Default)]
194pub struct MainWorld(World);
195
196impl Deref for MainWorld {
197 type Target = World;
198
199 fn deref(&self) -> &Self::Target {
200 &self.0
201 }
202}
203
204impl DerefMut for MainWorld {
205 fn deref_mut(&mut self) -> &mut Self::Target {
206 &mut self.0
207 }
208}
209
210pub mod graph {
211 use crate::render_graph::RenderLabel;
212
213 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
214 pub struct CameraDriverLabel;
215}
216
217#[derive(Resource)]
218struct FutureRendererResources(
219 Arc<
220 Mutex<
221 Option<(
222 RenderDevice,
223 RenderQueue,
224 RenderAdapterInfo,
225 RenderAdapter,
226 RenderInstance,
227 )>,
228 >,
229 >,
230);
231
232#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
234pub struct RenderApp;
235
236pub const INSTANCE_INDEX_SHADER_HANDLE: Handle<Shader> =
237 Handle::weak_from_u128(10313207077636615845);
238pub const MATHS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(10665356303104593376);
239pub const COLOR_OPERATIONS_SHADER_HANDLE: Handle<Shader> =
240 Handle::weak_from_u128(1844674407370955161);
241
242impl Plugin for RenderPlugin {
243 fn build(&self, app: &mut App) {
245 app.init_asset::<Shader>()
246 .init_asset_loader::<ShaderLoader>();
247
248 match &self.render_creation {
249 RenderCreation::Manual(device, queue, adapter_info, adapter, instance) => {
250 let future_renderer_resources_wrapper = Arc::new(Mutex::new(Some((
251 device.clone(),
252 queue.clone(),
253 adapter_info.clone(),
254 adapter.clone(),
255 instance.clone(),
256 ))));
257 app.insert_resource(FutureRendererResources(
258 future_renderer_resources_wrapper.clone(),
259 ));
260 unsafe { initialize_render_app(app) };
262 }
263 RenderCreation::Automatic(render_creation) => {
264 if let Some(backends) = render_creation.backends {
265 let future_renderer_resources_wrapper = Arc::new(Mutex::new(None));
266 app.insert_resource(FutureRendererResources(
267 future_renderer_resources_wrapper.clone(),
268 ));
269
270 let mut system_state: SystemState<
271 Query<&RawHandleWrapperHolder, With<PrimaryWindow>>,
272 > = SystemState::new(app.world_mut());
273 let primary_window = system_state.get(app.world()).get_single().ok().cloned();
274 let settings = render_creation.clone();
275 let async_renderer = async move {
276 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
277 backends,
278 dx12_shader_compiler: settings.dx12_shader_compiler.clone(),
279 flags: settings.instance_flags,
280 gles_minor_version: settings.gles3_minor_version,
281 });
282
283 let surface = primary_window.and_then(|wrapper| unsafe {
285 let maybe_handle = wrapper.0.lock().expect(
286 "Couldn't get the window handle in time for renderer initialization",
287 );
288 if let Some(wrapper) = maybe_handle.as_ref() {
289 let handle = wrapper.get_handle();
290 Some(
291 instance
292 .create_surface(handle)
293 .expect("Failed to create wgpu surface"),
294 )
295 } else {
296 None
297 }
298 });
299
300 let request_adapter_options = wgpu::RequestAdapterOptions {
301 power_preference: settings.power_preference,
302 compatible_surface: surface.as_ref(),
303 ..Default::default()
304 };
305
306 let (device, queue, adapter_info, render_adapter) =
307 renderer::initialize_renderer(
308 &instance,
309 &settings,
310 &request_adapter_options,
311 )
312 .await;
313 debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
314 debug!("Configured wgpu adapter Features: {:#?}", device.features());
315 let mut future_renderer_resources_inner =
316 future_renderer_resources_wrapper.lock().unwrap();
317 *future_renderer_resources_inner = Some((
318 device,
319 queue,
320 adapter_info,
321 render_adapter,
322 RenderInstance(Arc::new(WgpuWrapper::new(instance))),
323 ));
324 };
325 #[cfg(target_arch = "wasm32")]
327 bevy_tasks::IoTaskPool::get()
328 .spawn_local(async_renderer)
329 .detach();
330 #[cfg(not(target_arch = "wasm32"))]
332 futures_lite::future::block_on(async_renderer);
333
334 unsafe { initialize_render_app(app) };
336 }
337 }
338 };
339
340 app.add_plugins((
341 ValidParentCheckPlugin::<view::InheritedVisibility>::default(),
342 WindowRenderPlugin,
343 CameraPlugin,
344 ViewPlugin,
345 MeshPlugin,
346 GlobalsPlugin,
347 MorphPlugin,
348 BatchingPlugin,
349 ));
350
351 app.init_resource::<RenderAssetBytesPerFrame>()
352 .add_plugins(ExtractResourcePlugin::<RenderAssetBytesPerFrame>::default());
353
354 app.register_type::<alpha::AlphaMode>()
355 .register_type::<bevy_color::Color>()
357 .register_type::<primitives::Aabb>()
358 .register_type::<primitives::CascadesFrusta>()
359 .register_type::<primitives::CubemapFrusta>()
360 .register_type::<primitives::Frustum>();
361 }
362
363 fn ready(&self, app: &App) -> bool {
364 app.world()
365 .get_resource::<FutureRendererResources>()
366 .and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
367 .unwrap_or(true)
368 }
369
370 fn finish(&self, app: &mut App) {
371 load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
372 load_internal_asset!(
373 app,
374 COLOR_OPERATIONS_SHADER_HANDLE,
375 "color_operations.wgsl",
376 Shader::from_wgsl
377 );
378 if let Some(future_renderer_resources) =
379 app.world_mut().remove_resource::<FutureRendererResources>()
380 {
381 let (device, queue, adapter_info, render_adapter, instance) =
382 future_renderer_resources.0.lock().unwrap().take().unwrap();
383
384 app.insert_resource(device.clone())
385 .insert_resource(queue.clone())
386 .insert_resource(adapter_info.clone())
387 .insert_resource(render_adapter.clone());
388
389 let render_app = app.sub_app_mut(RenderApp);
390
391 render_app
392 .insert_resource(instance)
393 .insert_resource(PipelineCache::new(
394 device.clone(),
395 render_adapter.clone(),
396 self.synchronous_pipeline_compilation,
397 ))
398 .insert_resource(device)
399 .insert_resource(queue)
400 .insert_resource(render_adapter)
401 .insert_resource(adapter_info)
402 .add_systems(
403 Render,
404 (|mut bpf: ResMut<RenderAssetBytesPerFrame>| {
405 bpf.reset();
406 })
407 .in_set(RenderSet::Cleanup),
408 );
409 }
410 }
411}
412
413#[derive(Resource, Default)]
416struct ScratchMainWorld(World);
417
418fn extract(main_world: &mut World, render_world: &mut World) {
421 let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
423 let inserted_world = std::mem::replace(main_world, scratch_world.0);
424 render_world.insert_resource(MainWorld(inserted_world));
425 render_world.run_schedule(ExtractSchedule);
426
427 let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
429 let scratch_world = std::mem::replace(main_world, inserted_world.0);
430 main_world.insert_resource(ScratchMainWorld(scratch_world));
431}
432
433unsafe fn initialize_render_app(app: &mut App) {
435 app.init_resource::<ScratchMainWorld>();
436
437 let mut render_app = SubApp::new();
438 render_app.update_schedule = Some(Render.intern());
439
440 let mut extract_schedule = Schedule::new(ExtractSchedule);
441 extract_schedule.set_build_settings(ScheduleBuildSettings {
444 auto_insert_apply_deferred: false,
445 ..default()
446 });
447 extract_schedule.set_apply_final_deferred(false);
448
449 render_app
450 .add_schedule(extract_schedule)
451 .add_schedule(Render::base_schedule())
452 .init_resource::<render_graph::RenderGraph>()
453 .insert_resource(app.world().resource::<AssetServer>().clone())
454 .add_systems(ExtractSchedule, PipelineCache::extract_shaders)
455 .add_systems(
456 Render,
457 (
458 apply_extract_commands.in_set(RenderSet::ExtractCommands),
461 (
462 PipelineCache::process_pipeline_queue_system.before(render_system),
463 render_system,
464 )
465 .in_set(RenderSet::Render),
466 World::clear_entities.in_set(RenderSet::Cleanup),
467 ),
468 );
469
470 render_app.set_extract(|main_world, render_world| {
471 #[cfg(feature = "trace")]
472 let _render_span = bevy_utils::tracing::info_span!("extract main app to render subapp").entered();
473 {
474 #[cfg(feature = "trace")]
475 let _stage_span =
476 bevy_utils::tracing::info_span!("reserve_and_flush")
477 .entered();
478
479 let total_count = main_world.entities().total_count();
482
483 assert_eq!(
484 render_world.entities().len(),
485 0,
486 "An entity was spawned after the entity list was cleared last frame and before the extract schedule began. This is not supported",
487 );
488
489 unsafe {
491 render_world
492 .entities_mut()
493 .flush_and_reserve_invalid_assuming_no_entities(total_count);
494 }
495 }
496
497 extract(main_world, render_world);
499 });
500
501 let (sender, receiver) = bevy_time::create_time_channels();
502 render_app.insert_resource(sender);
503 app.insert_resource(receiver);
504 app.insert_sub_app(RenderApp, render_app);
505}
506
507fn apply_extract_commands(render_world: &mut World) {
511 render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
512 schedules
513 .get_mut(ExtractSchedule)
514 .unwrap()
515 .apply_deferred(render_world);
516 });
517}