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, RenderMesh},
83 render_asset::prepare_assets,
84 render_resource::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,
194}
195
196#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
198pub struct RenderStartup;
199
200#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
202pub struct Render;
203
204impl Render {
205 pub fn base_schedule() -> Schedule {
209 use RenderSystems::*;
210
211 let mut schedule = Schedule::new(Self);
212
213 schedule.configure_sets(
214 (
215 ExtractCommands,
216 PrepareMeshes,
217 ManageViews,
218 Queue,
219 PhaseSort,
220 Prepare,
221 Render,
222 Cleanup,
223 PostCleanup,
224 )
225 .chain(),
226 );
227
228 schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
229 schedule.configure_sets(
230 (QueueMeshes, QueueSweep)
231 .chain()
232 .in_set(Queue)
233 .after(prepare_assets::<RenderMesh>),
234 );
235 schedule.configure_sets(
236 (
237 PrepareResources,
238 PrepareResourcesCollectPhaseBuffers,
239 PrepareResourcesFlush,
240 PrepareBindGroups,
241 )
242 .chain()
243 .in_set(Prepare),
244 );
245
246 schedule
247 }
248}
249
250#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash, Default)]
258pub struct ExtractSchedule;
259
260#[derive(Resource, Default)]
266pub struct MainWorld(World);
267
268impl Deref for MainWorld {
269 type Target = World;
270
271 fn deref(&self) -> &Self::Target {
272 &self.0
273 }
274}
275
276impl DerefMut for MainWorld {
277 fn deref_mut(&mut self) -> &mut Self::Target {
278 &mut self.0
279 }
280}
281
282pub mod graph {
283 use crate::render_graph::RenderLabel;
284
285 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
286 pub struct CameraDriverLabel;
287}
288
289#[derive(Resource)]
290struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
291
292#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
294pub struct RenderApp;
295
296impl Plugin for RenderPlugin {
297 fn build(&self, app: &mut App) {
299 app.init_asset::<Shader>()
300 .init_asset_loader::<ShaderLoader>();
301
302 match &self.render_creation {
303 RenderCreation::Manual(resources) => {
304 let future_render_resources_wrapper = Arc::new(Mutex::new(Some(resources.clone())));
305 app.insert_resource(FutureRenderResources(
306 future_render_resources_wrapper.clone(),
307 ));
308 unsafe { initialize_render_app(app) };
310 }
311 RenderCreation::Automatic(render_creation) => {
312 if let Some(backends) = render_creation.backends {
313 let future_render_resources_wrapper = Arc::new(Mutex::new(None));
314 app.insert_resource(FutureRenderResources(
315 future_render_resources_wrapper.clone(),
316 ));
317
318 let primary_window = app
319 .world_mut()
320 .query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
321 .single(app.world())
322 .ok()
323 .cloned();
324
325 let settings = render_creation.clone();
326
327 #[cfg(feature = "raw_vulkan_init")]
328 let raw_vulkan_init_settings = app
329 .world_mut()
330 .get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
331 .cloned()
332 .unwrap_or_default();
333
334 let async_renderer = async move {
335 let render_resources = renderer::initialize_renderer(
336 backends,
337 primary_window,
338 &settings,
339 #[cfg(feature = "raw_vulkan_init")]
340 raw_vulkan_init_settings,
341 )
342 .await;
343
344 *future_render_resources_wrapper.lock().unwrap() = Some(render_resources);
345 };
346
347 #[cfg(target_arch = "wasm32")]
349 bevy_tasks::IoTaskPool::get()
350 .spawn_local(async_renderer)
351 .detach();
352 #[cfg(not(target_arch = "wasm32"))]
354 bevy_tasks::block_on(async_renderer);
355
356 unsafe { initialize_render_app(app) };
358 }
359 }
360 };
361
362 app.add_plugins((
363 WindowRenderPlugin,
364 CameraPlugin,
365 ViewPlugin,
366 MeshRenderAssetPlugin,
367 GlobalsPlugin,
368 #[cfg(feature = "morph")]
369 mesh::MorphPlugin,
370 TexturePlugin,
371 BatchingPlugin {
372 debug_flags: self.debug_flags,
373 },
374 SyncWorldPlugin,
375 StoragePlugin,
376 GpuReadbackPlugin::default(),
377 OcclusionCullingPlugin,
378 #[cfg(feature = "tracing-tracy")]
379 diagnostic::RenderDiagnosticsPlugin,
380 ));
381
382 app.init_resource::<RenderAssetBytesPerFrame>();
383 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
384 render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
385 render_app
386 .add_systems(ExtractSchedule, extract_render_asset_bytes_per_frame)
387 .add_systems(
388 Render,
389 reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
390 );
391 }
392 }
393
394 fn ready(&self, app: &App) -> bool {
395 app.world()
396 .get_resource::<FutureRenderResources>()
397 .and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
398 .unwrap_or(true)
399 }
400
401 fn finish(&self, app: &mut App) {
402 load_shader_library!(app, "maths.wgsl");
403 load_shader_library!(app, "color_operations.wgsl");
404 load_shader_library!(app, "bindless.wgsl");
405 if let Some(future_render_resources) =
406 app.world_mut().remove_resource::<FutureRenderResources>()
407 {
408 let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
409 let RenderResources(device, queue, adapter_info, render_adapter, instance, ..) =
410 render_resources;
411
412 let compressed_image_format_support = CompressedImageFormatSupport(
413 CompressedImageFormats::from_features(device.features()),
414 );
415
416 app.insert_resource(device.clone())
417 .insert_resource(queue.clone())
418 .insert_resource(adapter_info.clone())
419 .insert_resource(render_adapter.clone())
420 .insert_resource(compressed_image_format_support);
421
422 let render_app = app.sub_app_mut(RenderApp);
423
424 #[cfg(feature = "raw_vulkan_init")]
425 {
426 let additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures =
427 render_resources.5;
428 render_app.insert_resource(additional_vulkan_features);
429 }
430
431 render_app
432 .insert_resource(instance)
433 .insert_resource(PipelineCache::new(
434 device.clone(),
435 render_adapter.clone(),
436 self.synchronous_pipeline_compilation,
437 ))
438 .insert_resource(device)
439 .insert_resource(queue)
440 .insert_resource(render_adapter)
441 .insert_resource(adapter_info);
442 }
443 }
444}
445
446#[derive(Resource, Default)]
449struct ScratchMainWorld(World);
450
451fn extract(main_world: &mut World, render_world: &mut World) {
454 let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
456 let inserted_world = core::mem::replace(main_world, scratch_world.0);
457 render_world.insert_resource(MainWorld(inserted_world));
458 render_world.run_schedule(ExtractSchedule);
459
460 let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
462 let scratch_world = core::mem::replace(main_world, inserted_world.0);
463 main_world.insert_resource(ScratchMainWorld(scratch_world));
464}
465
466unsafe fn initialize_render_app(app: &mut App) {
469 app.init_resource::<ScratchMainWorld>();
470
471 let mut render_app = SubApp::new();
472 render_app.update_schedule = Some(Render.intern());
473
474 let mut extract_schedule = Schedule::new(ExtractSchedule);
475 extract_schedule.set_build_settings(ScheduleBuildSettings {
478 auto_insert_apply_deferred: false,
479 ..default()
480 });
481 extract_schedule.set_apply_final_deferred(false);
482
483 render_app
484 .add_schedule(extract_schedule)
485 .add_schedule(Render::base_schedule())
486 .init_resource::<render_graph::RenderGraph>()
487 .insert_resource(app.world().resource::<AssetServer>().clone())
488 .add_systems(ExtractSchedule, PipelineCache::extract_shaders)
489 .add_systems(
490 Render,
491 (
492 apply_extract_commands.in_set(RenderSystems::ExtractCommands),
495 (PipelineCache::process_pipeline_queue_system, render_system)
496 .chain()
497 .in_set(RenderSystems::Render),
498 despawn_temporary_render_entities.in_set(RenderSystems::PostCleanup),
499 ),
500 );
501
502 let mut should_run_startup = true;
506 render_app.set_extract(move |main_world, render_world| {
507 if should_run_startup {
508 render_world.run_schedule(RenderStartup);
512 should_run_startup = false;
513 }
514
515 {
516 #[cfg(feature = "trace")]
517 let _stage_span = tracing::info_span!("entity_sync").entered();
518 entity_sync_system(main_world, render_world);
519 }
520
521 extract(main_world, render_world);
523 });
524
525 let (sender, receiver) = bevy_time::create_time_channels();
526 render_app.insert_resource(sender);
527 app.insert_resource(receiver);
528 app.insert_sub_app(RenderApp, render_app);
529}
530
531fn apply_extract_commands(render_world: &mut World) {
535 render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
536 schedules
537 .get_mut(ExtractSchedule)
538 .unwrap()
539 .apply_deferred(render_world);
540 });
541}
542
543pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
547 if !cfg!(target_os = "android") {
548 return None;
549 }
550
551 let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
552
553 Some(
555 adreno_model
556 .chars()
557 .map_while(|c| c.to_digit(10))
558 .fold(0, |acc, digit| acc * 10 + digit),
559 )
560}
561
562pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
564 if !cfg!(target_os = "android") {
565 return None;
566 }
567
568 if !adapter_info.name.contains("Mali") {
569 return None;
570 }
571 let driver_info = &adapter_info.driver_info;
572 if let Some(start_pos) = driver_info.find("v1.r")
573 && let Some(end_pos) = driver_info[start_pos..].find('p')
574 {
575 let start_idx = start_pos + 4; let end_idx = start_pos + end_pos;
577
578 return driver_info[start_idx..end_idx].parse::<u32>().ok();
579 }
580
581 None
582}