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(rustdoc_internals))]
24#![cfg_attr(docsrs, feature(doc_cfg))]
25#![doc(
26 html_logo_url = "https://bevy.org/assets/icon.png",
27 html_favicon_url = "https://bevy.org/assets/icon.png"
28)]
29
30#[cfg(target_pointer_width = "16")]
31compile_error!("bevy_render cannot compile for a 16-bit platform.");
32
33extern crate alloc;
34extern crate core;
35
36extern crate self as bevy_render;
38
39pub mod batching;
40pub mod camera;
41pub mod combined_bind_group;
42pub mod diagnostic;
43pub mod erased_render_asset;
44pub mod error_handler;
45pub mod extract_component {
46 pub type ExtractComponentPlugin<C, F = ()> =
47 bevy_extract::extract_component::ExtractComponentPlugin<C, crate::RenderApp, F>;
48
49 pub use crate::uniform::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin};
50
51 pub use bevy_extract::extract_component::ExtractComponent;
52}
53pub mod extract_plugin {
54 pub use bevy_extract::extract_plugin::ExtractPlugin;
55}
56pub mod extract_resource {
57 pub type ExtractResourcePlugin<R, F = ()> =
58 bevy_extract::extract_resource::ExtractResourcePlugin<R, crate::RenderApp, F>;
59
60 pub use bevy_extract::extract_resource::{extract_resource, ExtractResource};
61}
62pub mod globals;
63pub mod gpu_component_array_buffer;
64pub mod gpu_readback;
65pub mod material_bind_groups;
66pub mod mesh;
67pub mod occlusion_culling;
68#[cfg(not(target_arch = "wasm32"))]
69pub mod pipelined_rendering;
70pub mod render_asset;
71pub mod render_phase;
72pub mod render_resource;
73pub mod renderer;
74pub mod settings;
75pub mod slab_allocator;
76pub mod storage;
77pub mod sync_component {
78 pub type SyncComponentPlugin<C, F = ()> =
79 bevy_extract::sync_component::SyncComponentPlugin<C, crate::RenderApp, F>;
80
81 pub use bevy_extract::sync_component::SyncComponent;
82}
83pub mod sync_world {
84 pub type SyncToRenderWorld = bevy_extract::sync_world::SyncToSubWorld<crate::RenderApp>;
85
86 pub type RenderEntity = bevy_extract::sync_world::SubEntity<crate::RenderApp>;
87
88 pub type TemporaryRenderEntity = bevy_extract::sync_world::TemporaryEntity<crate::RenderApp>;
89
90 pub use bevy_extract::sync_world::{MainEntity, MainEntityHashMap, MainEntityHashSet};
91}
92#[cfg(test)]
93pub(crate) mod test_utils;
94pub mod texture;
95pub mod uniform;
96pub mod view;
97
98pub mod prelude {
102 #[doc(hidden)]
103 pub use crate::{
104 camera::NormalizedRenderTargetExt as _, renderer::RenderGraph, texture::ManualTextureViews,
105 view::Msaa, ExtractSchedule,
106 };
107}
108pub use bevy_extract::{
109 extract_param::Extract,
110 extract_plugin::{ExtractSchedule, MainWorld},
111};
112
113use crate::{
114 camera::CameraPlugin,
115 error_handler::{RenderErrorHandler, RenderState},
116 gpu_readback::GpuReadbackPlugin,
117 material_bind_groups::MaterialBindGroupPlugin,
118 mesh::{MeshRenderAssetPlugin, RenderMesh},
119 render_asset::prepare_assets,
120 render_resource::{PipelineCache, SparseBufferPlugin},
121 renderer::{render_system, RenderAdapterInfo, RenderGraph},
122 settings::{RenderCreation, WgpuLimits},
123 storage::StoragePlugin,
124 texture::TexturePlugin,
125 view::{ViewPlugin, WindowRenderPlugin},
126};
127use alloc::sync::Arc;
128use batching::gpu_preprocessing::BatchingPlugin;
129use bevy_app::{App, AppLabel, First, Plugin, SubApp};
130use bevy_asset::{AssetApp, AssetServer};
131use bevy_derive::Deref;
132use bevy_ecs::{
133 prelude::*,
134 schedule::{InternedScheduleLabel, ScheduleLabel},
135};
136use bevy_extract::ExtractPlugin;
137use bevy_platform::time::Instant;
138use bevy_shader::{load_shader_library, Shader, ShaderLoader};
139use bevy_time::TimeSender;
140use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
141use bitflags::bitflags;
142use globals::GlobalsPlugin;
143use occlusion_culling::OcclusionCullingPlugin;
144use render_asset::{
145 extract_render_asset_bytes_per_frame, reset_render_asset_bytes_per_frame,
146 RenderAssetBytesPerFrame, RenderAssetBytesPerFrameLimiter,
147};
148use settings::RenderResources;
149use std::sync::Mutex;
150
151#[derive(Default)]
160pub struct RenderPlugin {
161 pub render_creation: RenderCreation,
162 pub synchronous_pipeline_compilation: bool,
165 pub debug_flags: RenderDebugFlags,
167}
168
169bitflags! {
170 #[derive(Clone, Copy, PartialEq, Default, Debug)]
172 pub struct RenderDebugFlags: u8 {
173 const ALLOW_COPIES_FROM_INDIRECT_PARAMETERS = 1;
179 }
180}
181
182#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
186pub enum RenderSystems {
187 ExtractCommands,
189 PrepareAssets,
191 PrepareMeshes,
193 CreateViews,
195 Specialize,
197 PrepareViews,
199 Queue,
202 QueueMeshes,
204 QueueSweep,
207 PhaseSort,
212 Prepare,
215 PrepareResources,
217 PrepareResourcesBatchPhases,
219 PrepareResourcesWritePhaseBuffers,
222 PrepareResourcesCollectPhaseBuffers,
225 PrepareResourcesFlush,
227 PrepareBindGroups,
229 Render,
232 Cleanup,
234 PostCleanup,
239}
240
241#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
246pub struct RenderStartup;
247
248pub fn init_gpu_resource<R: Resource + FromWorld>(world: &mut World) {
250 let res = R::from_world(world);
251 world.insert_resource(res);
252}
253
254pub trait GpuResourceAppExt {
256 fn init_gpu_resource<R: Resource + FromWorld>(&mut self) -> &mut Self;
265}
266
267impl GpuResourceAppExt for SubApp {
268 fn init_gpu_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
269 self.add_systems(RenderStartup, init_gpu_resource::<R>.ambiguous_with_all())
270 }
271}
272
273#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
276struct RenderRecovery;
277
278#[derive(Resource, Debug)]
282pub struct RenderScheduleOrder {
283 pub labels: Vec<InternedScheduleLabel>,
285}
286
287impl Default for RenderScheduleOrder {
288 fn default() -> Self {
289 Self {
290 labels: vec![First.intern(), Render.intern()],
291 }
292 }
293}
294
295impl RenderScheduleOrder {
296 pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
298 let index = self
299 .labels
300 .iter()
301 .position(|current| (**current).eq(&after))
302 .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
303 self.labels.insert(index + 1, schedule.intern());
304 }
305
306 pub fn insert_before(&mut self, before: impl ScheduleLabel, schedule: impl ScheduleLabel) {
308 let index = self
309 .labels
310 .iter()
311 .position(|current| (**current).eq(&before))
312 .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
313 self.labels.insert(index, schedule.intern());
314 }
315}
316
317#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
321pub struct Render;
322
323impl Render {
324 pub fn base_schedule() -> Schedule {
328 use RenderSystems::*;
329
330 let mut schedule = Schedule::new(Self);
331
332 schedule.configure_sets(
333 (
334 ExtractCommands,
335 PrepareMeshes,
336 CreateViews,
337 Specialize,
338 PrepareViews,
339 Queue,
340 PhaseSort,
341 Prepare,
342 Render,
343 Cleanup,
344 PostCleanup,
345 )
346 .chain_weak(),
347 );
348 schedule.ignore_ambiguity(Specialize, Specialize);
349
350 schedule
351 .configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain_weak());
352 schedule.configure_sets(
353 (QueueMeshes, QueueSweep)
354 .chain_weak()
355 .in_set(Queue)
356 .after_weak(prepare_assets::<RenderMesh>),
357 );
358 schedule.configure_sets(
359 (
360 PrepareResources,
361 PrepareResourcesBatchPhases,
362 PrepareResourcesWritePhaseBuffers,
363 PrepareResourcesCollectPhaseBuffers,
364 PrepareResourcesFlush,
365 PrepareBindGroups,
366 )
367 .chain_weak()
368 .in_set(Prepare),
369 );
370
371 schedule
372 }
373}
374
375#[derive(Resource, Default, Clone, Deref)]
376pub(crate) struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
377
378#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel, Default)]
380pub struct RenderApp;
381
382impl Plugin for RenderPlugin {
383 fn build(&self, app: &mut App) {
385 app.init_asset::<Shader>()
386 .init_asset_loader::<ShaderLoader>();
387 load_shader_library!(app, "utils.wesl");
388 load_shader_library!(app, "view.wesl");
389 load_shader_library!(app, "maths.wesl");
390 load_shader_library!(app, "color_operations.wesl");
391 load_shader_library!(app, "bindless.wesl");
392
393 if insert_future_resources(&self.render_creation, app.world_mut()) {
394 app.add_plugins(ExtractPlugin::<RenderApp>::new(
397 error_handler::update_state,
398 Render::base_schedule,
399 Render.intern(),
400 RenderSystems::ExtractCommands.intern(),
401 RenderSystems::PostCleanup.intern(),
402 ));
403 };
404
405 app.add_plugins((
406 WindowRenderPlugin,
407 CameraPlugin,
408 ViewPlugin,
409 MeshRenderAssetPlugin,
410 GlobalsPlugin,
411 TexturePlugin,
412 BatchingPlugin {
413 debug_flags: self.debug_flags,
414 },
415 StoragePlugin,
416 GpuReadbackPlugin::default(),
417 OcclusionCullingPlugin,
418 SparseBufferPlugin,
419 MaterialBindGroupPlugin,
420 #[cfg(feature = "tracing-tracy")]
421 diagnostic::RenderDiagnosticsPlugin,
422 ));
423
424 let (sender, receiver) = bevy_time::create_time_channels();
425 app.insert_resource(receiver);
426
427 let asset_server = app.world().resource::<AssetServer>().clone();
428 app.init_resource::<RenderAssetBytesPerFrame>()
429 .init_resource::<RenderErrorHandler>();
430 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
431 render_app.init_resource::<RenderScheduleOrder>();
432 render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
433 render_app.init_gpu_resource::<renderer::PendingCommandBuffers>();
434 render_app.insert_resource(sender);
435 render_app.insert_resource(asset_server);
436 render_app.insert_resource(RenderState::Initializing);
437 render_app.add_systems(
438 ExtractSchedule,
439 (
440 extract_render_asset_bytes_per_frame,
441 PipelineCache::extract_shaders,
442 ),
443 );
444
445 #[cfg(not(feature = "reflect_auto_register"))]
446 render_app.init_resource::<AppTypeRegistry>();
447
448 #[cfg(feature = "reflect_auto_register")]
449 render_app.insert_resource(AppTypeRegistry::new_with_derived_types());
450
451 #[cfg(feature = "reflect_functions")]
452 render_app.init_resource::<AppFunctionRegistry>();
453
454 render_app.add_schedule(RenderGraph::base_schedule());
455
456 render_app.init_schedule(RenderStartup);
457 render_app
458 .get_schedule_mut(RenderStartup)
459 .unwrap()
460 .set_executor(bevy_ecs::schedule::SingleThreadedExecutor::new());
461 render_app.update_schedule = Some(RenderRecovery.intern());
462 render_app.add_systems(
463 RenderRecovery,
464 (run_render_schedule.run_if(renderer_is_ready), send_time).chain(),
465 );
466 render_app.add_systems(
467 Render,
468 (
469 (PipelineCache::process_pipeline_queue_system, render_system)
470 .chain()
471 .in_set(RenderSystems::Render),
472 reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
473 ),
474 );
475 }
476 }
477
478 fn ready(&self, app: &App) -> bool {
479 app.world()
488 .get_resource::<FutureRenderResources>()
489 .and_then(|frr| frr.try_lock().map(|locked| locked.is_some()).ok())
490 .unwrap_or(true)
491 }
492
493 fn finish(&self, app: &mut App) {
494 if let Some(future_render_resources) =
495 app.world_mut().remove_resource::<FutureRenderResources>()
496 {
497 let bevy_app::SubApps { main, sub_apps } = app.sub_apps_mut();
498 let render = sub_apps.get_mut(&RenderApp.intern()).unwrap();
499 let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
500
501 render_resources.unpack_into(
502 main.world_mut(),
503 render.world_mut(),
504 self.synchronous_pipeline_compilation,
505 );
506 }
507 }
508}
509
510fn renderer_is_ready(state: Res<RenderState>) -> bool {
511 matches!(*state, RenderState::Ready)
512}
513
514fn run_render_schedule(world: &mut World) {
515 world.resource_scope(|world, order: Mut<RenderScheduleOrder>| {
516 for &label in &order.labels {
517 let _ = world.try_run_schedule(label);
518 }
519 });
520}
521
522fn send_time(time_sender: Res<TimeSender>) {
523 if let Err(error) = time_sender.0.try_send(Instant::now()) {
525 match error {
526 bevy_time::TrySendError::Full(_) => {
527 panic!(
528 "The TimeSender channel should always be empty during render. \
529 You might need to add the bevy::core::time_system to your app."
530 );
531 }
532 bevy_time::TrySendError::Disconnected(_) => {
533 }
535 }
536 }
537}
538
539fn insert_future_resources(render_creation: &RenderCreation, main_world: &mut World) -> bool {
543 let primary_window = main_world
544 .query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
545 .single(main_world)
546 .ok()
547 .cloned();
548
549 #[cfg(feature = "raw_vulkan_init")]
550 let raw_vulkan_init_settings = main_world
551 .get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
552 .cloned()
553 .unwrap_or_default();
554
555 let future_resources = FutureRenderResources::default();
556 let success = render_creation.create_render(
557 future_resources.clone(),
558 primary_window,
559 #[cfg(feature = "raw_vulkan_init")]
560 raw_vulkan_init_settings,
561 );
562 if success {
563 main_world.insert_resource(future_resources);
565 }
566 success
567}
568
569pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
573 if !cfg!(target_os = "android") {
574 return None;
575 }
576
577 let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
578
579 Some(
581 adreno_model
582 .chars()
583 .map_while(|c| c.to_digit(10))
584 .fold(0, |acc, digit| acc * 10 + digit),
585 )
586}
587
588pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
590 if !cfg!(target_os = "android") {
591 return None;
592 }
593
594 if !adapter_info.name.contains("Mali") {
595 return None;
596 }
597 let driver_info = &adapter_info.driver_info;
598 if let Some(start_pos) = driver_info.find("v1.r")
599 && let Some(end_pos) = driver_info[start_pos..].find('p')
600 {
601 let start_idx = start_pos + 4; let end_idx = start_pos + end_pos;
603
604 return driver_info[start_idx..end_idx].parse::<u32>().ok();
605 }
606
607 None
608}
609
610pub fn get_pixel10_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
611 if !cfg!(target_os = "android") {
612 return None;
613 }
614
615 if adapter_info.name != "PowerVR D-Series DXT-48-1536 MC1" {
616 return None;
617 }
618
619 let (_, driver_version) = adapter_info.driver_info.split_once('@')?;
620 driver_version.parse::<u32>().ok()
621}
622
623pub fn storage_buffers_are_unsupported(limits: &WgpuLimits) -> bool {
626 limits.max_storage_buffers_per_shader_stage == 0
627}