1#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
15#![expect(unsafe_code, reason = "Unsafe code is used to improve performance.")]
16#![cfg_attr(
17 any(docsrs, docsrs_dep),
18 expect(
19 internal_features,
20 reason = "rustdoc_internals is needed for fake_variadic"
21 )
22)]
23#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_cfg, rustdoc_internals))]
24#![doc(
25 html_logo_url = "https://bevy.org/assets/icon.png",
26 html_favicon_url = "https://bevy.org/assets/icon.png"
27)]
28
29#[cfg(target_pointer_width = "16")]
30compile_error!("bevy_render cannot compile for a 16-bit platform.");
31
32extern crate alloc;
33extern crate core;
34
35extern crate self as bevy_render;
37
38pub mod alpha;
39pub mod batching;
40pub mod camera;
41pub mod diagnostic;
42pub mod erased_render_asset;
43pub mod experimental;
44pub mod extract_component;
45pub mod extract_instances;
46mod extract_param;
47pub mod extract_resource;
48pub mod globals;
49pub mod gpu_component_array_buffer;
50pub mod gpu_readback;
51pub mod mesh;
52#[cfg(not(target_arch = "wasm32"))]
53pub mod pipelined_rendering;
54pub mod render_asset;
55pub mod render_graph;
56pub mod render_phase;
57pub mod render_resource;
58pub mod renderer;
59pub mod settings;
60pub mod storage;
61pub mod sync_component;
62pub mod sync_world;
63pub mod texture;
64pub mod view;
65
66pub mod prelude {
70 #[doc(hidden)]
71 pub use crate::{
72 alpha::AlphaMode, camera::NormalizedRenderTargetExt as _, texture::ManualTextureViews,
73 view::Msaa, ExtractSchedule,
74 };
75}
76
77pub use extract_param::Extract;
78
79use crate::{
80 camera::CameraPlugin,
81 gpu_readback::GpuReadbackPlugin,
82 mesh::{MeshRenderAssetPlugin, MorphPlugin, RenderMesh},
83 render_asset::prepare_assets,
84 render_resource::{init_empty_bind_group_layout, PipelineCache},
85 renderer::{render_system, RenderAdapterInfo},
86 settings::RenderCreation,
87 storage::StoragePlugin,
88 texture::TexturePlugin,
89 view::{ViewPlugin, WindowRenderPlugin},
90};
91use alloc::sync::Arc;
92use batching::gpu_preprocessing::BatchingPlugin;
93use bevy_app::{App, AppLabel, Plugin, SubApp};
94use bevy_asset::{AssetApp, AssetServer};
95use bevy_ecs::{
96 prelude::*,
97 schedule::{ScheduleBuildSettings, ScheduleLabel},
98};
99use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats};
100use bevy_shader::{load_shader_library, Shader, ShaderLoader};
101use bevy_utils::prelude::default;
102use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
103use bitflags::bitflags;
104use core::ops::{Deref, DerefMut};
105use experimental::occlusion_culling::OcclusionCullingPlugin;
106use globals::GlobalsPlugin;
107use render_asset::{
108 extract_render_asset_bytes_per_frame, reset_render_asset_bytes_per_frame,
109 RenderAssetBytesPerFrame, RenderAssetBytesPerFrameLimiter,
110};
111use settings::RenderResources;
112use std::sync::Mutex;
113use sync_world::{despawn_temporary_render_entities, entity_sync_system, SyncWorldPlugin};
114
115#[derive(Default)]
124pub struct RenderPlugin {
125 pub render_creation: RenderCreation,
126 pub synchronous_pipeline_compilation: bool,
129 pub debug_flags: RenderDebugFlags,
131}
132
133bitflags! {
134 #[derive(Clone, Copy, PartialEq, Default, Debug)]
136 pub struct RenderDebugFlags: u8 {
137 const ALLOW_COPIES_FROM_INDIRECT_PARAMETERS = 1;
143 }
144}
145
146#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
150pub enum RenderSystems {
151 ExtractCommands,
153 PrepareAssets,
155 PrepareMeshes,
157 ManageViews,
159 Queue,
162 QueueMeshes,
164 QueueSweep,
167 PhaseSort,
172 Prepare,
175 PrepareResources,
177 PrepareResourcesCollectPhaseBuffers,
180 PrepareResourcesFlush,
182 PrepareBindGroups,
184 Render,
187 Cleanup,
189 PostCleanup,
193}
194
195#[deprecated(since = "0.17.0", note = "Renamed to `RenderSystems`.")]
197pub type RenderSet = RenderSystems;
198
199#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
201pub struct RenderStartup;
202
203#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
205pub struct Render;
206
207impl Render {
208 pub fn base_schedule() -> Schedule {
212 use RenderSystems::*;
213
214 let mut schedule = Schedule::new(Self);
215
216 schedule.configure_sets(
217 (
218 ExtractCommands,
219 PrepareMeshes,
220 ManageViews,
221 Queue,
222 PhaseSort,
223 Prepare,
224 Render,
225 Cleanup,
226 PostCleanup,
227 )
228 .chain(),
229 );
230
231 schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
232 schedule.configure_sets(
233 (QueueMeshes, QueueSweep)
234 .chain()
235 .in_set(Queue)
236 .after(prepare_assets::<RenderMesh>),
237 );
238 schedule.configure_sets(
239 (
240 PrepareResources,
241 PrepareResourcesCollectPhaseBuffers,
242 PrepareResourcesFlush,
243 PrepareBindGroups,
244 )
245 .chain()
246 .in_set(Prepare),
247 );
248
249 schedule
250 }
251}
252
253#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash, Default)]
261pub struct ExtractSchedule;
262
263#[derive(Resource, Default)]
269pub struct MainWorld(World);
270
271impl Deref for MainWorld {
272 type Target = World;
273
274 fn deref(&self) -> &Self::Target {
275 &self.0
276 }
277}
278
279impl DerefMut for MainWorld {
280 fn deref_mut(&mut self) -> &mut Self::Target {
281 &mut self.0
282 }
283}
284
285pub mod graph {
286 use crate::render_graph::RenderLabel;
287
288 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
289 pub struct CameraDriverLabel;
290}
291
292#[derive(Resource)]
293struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
294
295#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
297pub struct RenderApp;
298
299impl Plugin for RenderPlugin {
300 fn build(&self, app: &mut App) {
302 app.init_asset::<Shader>()
303 .init_asset_loader::<ShaderLoader>();
304
305 match &self.render_creation {
306 RenderCreation::Manual(resources) => {
307 let future_render_resources_wrapper = Arc::new(Mutex::new(Some(resources.clone())));
308 app.insert_resource(FutureRenderResources(
309 future_render_resources_wrapper.clone(),
310 ));
311 unsafe { initialize_render_app(app) };
313 }
314 RenderCreation::Automatic(render_creation) => {
315 if let Some(backends) = render_creation.backends {
316 let future_render_resources_wrapper = Arc::new(Mutex::new(None));
317 app.insert_resource(FutureRenderResources(
318 future_render_resources_wrapper.clone(),
319 ));
320
321 let primary_window = app
322 .world_mut()
323 .query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
324 .single(app.world())
325 .ok()
326 .cloned();
327
328 let settings = render_creation.clone();
329
330 #[cfg(feature = "raw_vulkan_init")]
331 let raw_vulkan_init_settings = app
332 .world_mut()
333 .get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
334 .cloned()
335 .unwrap_or_default();
336
337 let async_renderer = async move {
338 let render_resources = renderer::initialize_renderer(
339 backends,
340 primary_window,
341 &settings,
342 #[cfg(feature = "raw_vulkan_init")]
343 raw_vulkan_init_settings,
344 )
345 .await;
346
347 *future_render_resources_wrapper.lock().unwrap() = Some(render_resources);
348 };
349
350 #[cfg(target_arch = "wasm32")]
352 bevy_tasks::IoTaskPool::get()
353 .spawn_local(async_renderer)
354 .detach();
355 #[cfg(not(target_arch = "wasm32"))]
357 bevy_tasks::block_on(async_renderer);
358
359 unsafe { initialize_render_app(app) };
361 }
362 }
363 };
364
365 app.add_plugins((
366 WindowRenderPlugin,
367 CameraPlugin,
368 ViewPlugin,
369 MeshRenderAssetPlugin,
370 GlobalsPlugin,
371 MorphPlugin,
372 TexturePlugin,
373 BatchingPlugin {
374 debug_flags: self.debug_flags,
375 },
376 SyncWorldPlugin,
377 StoragePlugin,
378 GpuReadbackPlugin::default(),
379 OcclusionCullingPlugin,
380 #[cfg(feature = "tracing-tracy")]
381 diagnostic::RenderDiagnosticsPlugin,
382 ));
383
384 app.init_resource::<RenderAssetBytesPerFrame>();
385 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
386 render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
387 render_app
388 .add_systems(ExtractSchedule, extract_render_asset_bytes_per_frame)
389 .add_systems(
390 Render,
391 reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
392 );
393
394 render_app.add_systems(RenderStartup, init_empty_bind_group_layout);
395 }
396 }
397
398 fn ready(&self, app: &App) -> bool {
399 app.world()
400 .get_resource::<FutureRenderResources>()
401 .and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
402 .unwrap_or(true)
403 }
404
405 fn finish(&self, app: &mut App) {
406 load_shader_library!(app, "maths.wgsl");
407 load_shader_library!(app, "color_operations.wgsl");
408 load_shader_library!(app, "bindless.wgsl");
409 if let Some(future_render_resources) =
410 app.world_mut().remove_resource::<FutureRenderResources>()
411 {
412 let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
413 let RenderResources(device, queue, adapter_info, render_adapter, instance, ..) =
414 render_resources;
415
416 let compressed_image_format_support = CompressedImageFormatSupport(
417 CompressedImageFormats::from_features(device.features()),
418 );
419
420 app.insert_resource(device.clone())
421 .insert_resource(queue.clone())
422 .insert_resource(adapter_info.clone())
423 .insert_resource(render_adapter.clone())
424 .insert_resource(compressed_image_format_support);
425
426 let render_app = app.sub_app_mut(RenderApp);
427
428 #[cfg(feature = "raw_vulkan_init")]
429 {
430 let additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures =
431 render_resources.5;
432 render_app.insert_resource(additional_vulkan_features);
433 }
434
435 render_app
436 .insert_resource(instance)
437 .insert_resource(PipelineCache::new(
438 device.clone(),
439 render_adapter.clone(),
440 self.synchronous_pipeline_compilation,
441 ))
442 .insert_resource(device)
443 .insert_resource(queue)
444 .insert_resource(render_adapter)
445 .insert_resource(adapter_info);
446 }
447 }
448}
449
450#[derive(Resource, Default)]
453struct ScratchMainWorld(World);
454
455fn extract(main_world: &mut World, render_world: &mut World) {
458 let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
460 let inserted_world = core::mem::replace(main_world, scratch_world.0);
461 render_world.insert_resource(MainWorld(inserted_world));
462 render_world.run_schedule(ExtractSchedule);
463
464 let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
466 let scratch_world = core::mem::replace(main_world, inserted_world.0);
467 main_world.insert_resource(ScratchMainWorld(scratch_world));
468}
469
470unsafe fn initialize_render_app(app: &mut App) {
473 app.init_resource::<ScratchMainWorld>();
474
475 let mut render_app = SubApp::new();
476 render_app.update_schedule = Some(Render.intern());
477
478 let mut extract_schedule = Schedule::new(ExtractSchedule);
479 extract_schedule.set_build_settings(ScheduleBuildSettings {
482 auto_insert_apply_deferred: false,
483 ..default()
484 });
485 extract_schedule.set_apply_final_deferred(false);
486
487 render_app
488 .add_schedule(extract_schedule)
489 .add_schedule(Render::base_schedule())
490 .init_resource::<render_graph::RenderGraph>()
491 .insert_resource(app.world().resource::<AssetServer>().clone())
492 .add_systems(ExtractSchedule, PipelineCache::extract_shaders)
493 .add_systems(
494 Render,
495 (
496 apply_extract_commands.in_set(RenderSystems::ExtractCommands),
499 (PipelineCache::process_pipeline_queue_system, render_system)
500 .chain()
501 .in_set(RenderSystems::Render),
502 despawn_temporary_render_entities.in_set(RenderSystems::PostCleanup),
503 ),
504 );
505
506 let mut should_run_startup = true;
510 render_app.set_extract(move |main_world, render_world| {
511 if should_run_startup {
512 render_world.run_schedule(RenderStartup);
516 should_run_startup = false;
517 }
518
519 {
520 #[cfg(feature = "trace")]
521 let _stage_span = tracing::info_span!("entity_sync").entered();
522 entity_sync_system(main_world, render_world);
523 }
524
525 extract(main_world, render_world);
527 });
528
529 let (sender, receiver) = bevy_time::create_time_channels();
530 render_app.insert_resource(sender);
531 app.insert_resource(receiver);
532 app.insert_sub_app(RenderApp, render_app);
533}
534
535fn apply_extract_commands(render_world: &mut World) {
539 render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
540 schedules
541 .get_mut(ExtractSchedule)
542 .unwrap()
543 .apply_deferred(render_world);
544 });
545}
546
547pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
551 if !cfg!(target_os = "android") {
552 return None;
553 }
554
555 let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
556
557 Some(
559 adreno_model
560 .chars()
561 .map_while(|c| c.to_digit(10))
562 .fold(0, |acc, digit| acc * 10 + digit),
563 )
564}
565
566pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
568 if !cfg!(target_os = "android") {
569 return None;
570 }
571
572 if !adapter_info.name.contains("Mali") {
573 return None;
574 }
575 let driver_info = &adapter_info.driver_info;
576 if let Some(start_pos) = driver_info.find("v1.r")
577 && let Some(end_pos) = driver_info[start_pos..].find('p')
578 {
579 let start_idx = start_pos + 4; let end_idx = start_pos + end_pos;
581
582 return driver_info[start_idx..end_idx].parse::<u32>().ok();
583 }
584
585 None
586}