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 diagnostic;
42pub mod erased_render_asset;
43pub mod error_handler;
44pub mod extract_component;
45pub mod extract_instances;
46mod extract_param;
47pub mod extract_plugin;
48pub mod extract_resource;
49pub mod globals;
50pub mod gpu_component_array_buffer;
51pub mod gpu_readback;
52pub mod mesh;
53pub mod occlusion_culling;
54#[cfg(not(target_arch = "wasm32"))]
55pub mod pipelined_rendering;
56pub mod render_asset;
57pub mod render_phase;
58pub mod render_resource;
59pub mod renderer;
60pub mod settings;
61pub mod slab_allocator;
62pub mod storage;
63pub mod sync_component;
64pub mod sync_world;
65pub mod texture;
66pub mod uniform;
67pub mod view;
68
69pub mod prelude {
73 #[doc(hidden)]
74 pub use crate::{
75 camera::NormalizedRenderTargetExt as _, renderer::RenderGraph, texture::ManualTextureViews,
76 view::Msaa, ExtractSchedule,
77 };
78}
79
80pub use extract_param::Extract;
81pub use extract_plugin::{ExtractSchedule, MainWorld};
82
83use crate::{
84 camera::CameraPlugin,
85 error_handler::{RenderErrorHandler, RenderState},
86 extract_plugin::ExtractPlugin,
87 gpu_readback::GpuReadbackPlugin,
88 mesh::{MeshRenderAssetPlugin, RenderMesh},
89 render_asset::prepare_assets,
90 render_resource::{PipelineCache, SparseBufferPlugin},
91 renderer::{render_system, RenderAdapterInfo, RenderGraph},
92 settings::{RenderCreation, WgpuLimits},
93 storage::StoragePlugin,
94 texture::TexturePlugin,
95 view::{ViewPlugin, WindowRenderPlugin},
96};
97use alloc::sync::Arc;
98use batching::gpu_preprocessing::BatchingPlugin;
99use bevy_app::{App, AppLabel, Plugin, SubApp};
100use bevy_asset::{AssetApp, AssetServer};
101use bevy_derive::Deref;
102use bevy_ecs::{
103 prelude::*,
104 schedule::{InternedScheduleLabel, ScheduleLabel},
105};
106use bevy_platform::time::Instant;
107use bevy_shader::{load_shader_library, Shader, ShaderLoader};
108use bevy_time::TimeSender;
109use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
110use bitflags::bitflags;
111use globals::GlobalsPlugin;
112use occlusion_culling::OcclusionCullingPlugin;
113use render_asset::{
114 extract_render_asset_bytes_per_frame, reset_render_asset_bytes_per_frame,
115 RenderAssetBytesPerFrame, RenderAssetBytesPerFrameLimiter,
116};
117use settings::RenderResources;
118use std::sync::{Mutex, OnceLock};
119
120#[derive(Default)]
129pub struct RenderPlugin {
130 pub render_creation: RenderCreation,
131 pub synchronous_pipeline_compilation: bool,
134 pub debug_flags: RenderDebugFlags,
136}
137
138bitflags! {
139 #[derive(Clone, Copy, PartialEq, Default, Debug)]
141 pub struct RenderDebugFlags: u8 {
142 const ALLOW_COPIES_FROM_INDIRECT_PARAMETERS = 1;
148 }
149}
150
151#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
155pub enum RenderSystems {
156 ExtractCommands,
158 PrepareAssets,
160 PrepareMeshes,
162 CreateViews,
164 Specialize,
166 PrepareViews,
168 Queue,
171 QueueMeshes,
173 QueueSweep,
176 PhaseSort,
181 Prepare,
184 PrepareResources,
186 PrepareResourcesBatchPhases,
188 PrepareResourcesWritePhaseBuffers,
191 PrepareResourcesCollectPhaseBuffers,
194 PrepareResourcesFlush,
196 PrepareBindGroups,
198 Render,
201 Cleanup,
203 PostCleanup,
208}
209
210#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
215pub struct RenderStartup;
216
217pub fn init_gpu_resource<R: Resource + FromWorld>(world: &mut World) {
219 let res = R::from_world(world);
220 world.insert_resource(res);
221}
222
223pub trait GpuResourceAppExt {
225 fn init_gpu_resource<R: Resource + FromWorld>(&mut self) -> &mut Self;
234}
235
236impl GpuResourceAppExt for SubApp {
237 fn init_gpu_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
238 self.add_systems(RenderStartup, init_gpu_resource::<R>.ambiguous_with_all())
239 }
240}
241
242#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone)]
245struct RenderRecovery;
246
247#[derive(Resource, Debug)]
251pub struct RenderScheduleOrder {
252 pub labels: Vec<InternedScheduleLabel>,
254}
255
256impl Default for RenderScheduleOrder {
257 fn default() -> Self {
258 Self {
259 labels: vec![Render.intern()],
260 }
261 }
262}
263
264impl RenderScheduleOrder {
265 pub fn insert_after(&mut self, after: impl ScheduleLabel, schedule: impl ScheduleLabel) {
267 let index = self
268 .labels
269 .iter()
270 .position(|current| (**current).eq(&after))
271 .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
272 self.labels.insert(index + 1, schedule.intern());
273 }
274
275 pub fn insert_before(&mut self, before: impl ScheduleLabel, schedule: impl ScheduleLabel) {
277 let index = self
278 .labels
279 .iter()
280 .position(|current| (**current).eq(&before))
281 .unwrap_or_else(|| panic!("Expected {before:?} to exist"));
282 self.labels.insert(index, schedule.intern());
283 }
284}
285
286#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
288pub struct Render;
289
290impl Render {
291 pub fn base_schedule() -> Schedule {
295 use RenderSystems::*;
296
297 let mut schedule = Schedule::new(Self);
298
299 schedule.configure_sets(
300 (
301 ExtractCommands,
302 PrepareMeshes,
303 CreateViews,
304 Specialize,
305 PrepareViews,
306 Queue,
307 PhaseSort,
308 Prepare,
309 Render,
310 Cleanup,
311 PostCleanup,
312 )
313 .chain(),
314 );
315 schedule.ignore_ambiguity(Specialize, Specialize);
316
317 schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
318 schedule.configure_sets(
319 (QueueMeshes, QueueSweep)
320 .chain()
321 .in_set(Queue)
322 .after(prepare_assets::<RenderMesh>),
323 );
324 schedule.configure_sets(
325 (
326 PrepareResources,
327 PrepareResourcesBatchPhases,
328 PrepareResourcesWritePhaseBuffers,
329 PrepareResourcesCollectPhaseBuffers,
330 PrepareResourcesFlush,
331 PrepareBindGroups,
332 )
333 .chain()
334 .in_set(Prepare),
335 );
336
337 schedule
338 }
339}
340
341#[derive(Resource, Default, Clone, Deref)]
342pub(crate) struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
343
344#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
346pub struct RenderApp;
347
348impl Plugin for RenderPlugin {
349 fn build(&self, app: &mut App) {
351 app.init_asset::<Shader>()
352 .init_asset_loader::<ShaderLoader>();
353 load_shader_library!(app, "maths.wgsl");
354 load_shader_library!(app, "color_operations.wgsl");
355 load_shader_library!(app, "bindless.wgsl");
356
357 if insert_future_resources(&self.render_creation, app.world_mut()) {
358 app.add_plugins(ExtractPlugin {
361 pre_extract: error_handler::update_state,
362 });
363 };
364
365 app.add_plugins((
366 WindowRenderPlugin,
367 CameraPlugin,
368 ViewPlugin,
369 MeshRenderAssetPlugin,
370 GlobalsPlugin,
371 TexturePlugin,
372 BatchingPlugin {
373 debug_flags: self.debug_flags,
374 },
375 StoragePlugin,
376 GpuReadbackPlugin::default(),
377 OcclusionCullingPlugin,
378 SparseBufferPlugin,
379 #[cfg(feature = "tracing-tracy")]
380 diagnostic::RenderDiagnosticsPlugin,
381 ));
382
383 let (sender, receiver) = bevy_time::create_time_channels();
384 app.insert_resource(receiver);
385
386 let asset_server = app.world().resource::<AssetServer>().clone();
387 app.init_resource::<RenderAssetBytesPerFrame>()
388 .init_resource::<RenderErrorHandler>();
389 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
390 render_app.init_resource::<RenderScheduleOrder>();
391 render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
392 render_app.init_gpu_resource::<renderer::PendingCommandBuffers>();
393 render_app.insert_resource(sender);
394 render_app.insert_resource(asset_server);
395 render_app.insert_resource(RenderState::Initializing);
396 render_app.add_systems(
397 ExtractSchedule,
398 (
399 extract_render_asset_bytes_per_frame,
400 PipelineCache::extract_shaders,
401 ),
402 );
403
404 #[cfg(not(feature = "reflect_auto_register"))]
405 render_app.init_resource::<AppTypeRegistry>();
406
407 #[cfg(feature = "reflect_auto_register")]
408 render_app.insert_resource(AppTypeRegistry::new_with_derived_types());
409
410 #[cfg(feature = "reflect_functions")]
411 render_app.init_resource::<AppFunctionRegistry>();
412
413 render_app.add_schedule(RenderGraph::base_schedule());
414
415 render_app.init_schedule(RenderStartup);
416 render_app
417 .get_schedule_mut(RenderStartup)
418 .unwrap()
419 .set_executor(bevy_ecs::schedule::SingleThreadedExecutor::new());
420 render_app.update_schedule = Some(RenderRecovery.intern());
421 render_app.add_systems(
422 RenderRecovery,
423 (run_render_schedule.run_if(renderer_is_ready), send_time).chain(),
424 );
425 render_app.add_systems(
426 Render,
427 (
428 (PipelineCache::process_pipeline_queue_system, render_system)
429 .chain()
430 .in_set(RenderSystems::Render),
431 reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup),
432 ),
433 );
434 }
435 }
436
437 fn ready(&self, app: &App) -> bool {
438 app.world()
447 .get_resource::<FutureRenderResources>()
448 .and_then(|frr| frr.try_lock().map(|locked| locked.is_some()).ok())
449 .unwrap_or(true)
450 }
451
452 fn finish(&self, app: &mut App) {
453 if let Some(future_render_resources) =
454 app.world_mut().remove_resource::<FutureRenderResources>()
455 {
456 let bevy_app::SubApps { main, sub_apps } = app.sub_apps_mut();
457 let render = sub_apps.get_mut(&RenderApp.intern()).unwrap();
458 let render_resources = future_render_resources.0.lock().unwrap().take().unwrap();
459
460 render_resources.unpack_into(
461 main.world_mut(),
462 render.world_mut(),
463 self.synchronous_pipeline_compilation,
464 );
465 }
466 }
467}
468
469fn renderer_is_ready(state: Res<RenderState>) -> bool {
470 matches!(*state, RenderState::Ready)
471}
472
473fn run_render_schedule(world: &mut World) {
474 world.resource_scope(|world, order: Mut<RenderScheduleOrder>| {
475 for &label in &order.labels {
476 let _ = world.try_run_schedule(label);
477 }
478 });
479}
480
481fn send_time(time_sender: Res<TimeSender>) {
482 if let Err(error) = time_sender.0.try_send(Instant::now()) {
484 match error {
485 bevy_time::TrySendError::Full(_) => {
486 panic!(
487 "The TimeSender channel should always be empty during render. \
488 You might need to add the bevy::core::time_system to your app."
489 );
490 }
491 bevy_time::TrySendError::Disconnected(_) => {
492 }
494 }
495 }
496}
497
498fn insert_future_resources(render_creation: &RenderCreation, main_world: &mut World) -> bool {
502 let primary_window = main_world
503 .query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
504 .single(main_world)
505 .ok()
506 .cloned();
507
508 #[cfg(feature = "raw_vulkan_init")]
509 let raw_vulkan_init_settings = main_world
510 .get_resource::<renderer::raw_vulkan_init::RawVulkanInitSettings>()
511 .cloned()
512 .unwrap_or_default();
513
514 let future_resources = FutureRenderResources::default();
515 let success = render_creation.create_render(
516 future_resources.clone(),
517 primary_window,
518 #[cfg(feature = "raw_vulkan_init")]
519 raw_vulkan_init_settings,
520 );
521 if success {
522 main_world.insert_resource(future_resources);
524 }
525 success
526}
527
528pub fn get_adreno_model(adapter_info: &RenderAdapterInfo) -> Option<u32> {
532 if !cfg!(target_os = "android") {
533 return None;
534 }
535
536 let adreno_model = adapter_info.name.strip_prefix("Adreno (TM) ")?;
537
538 Some(
540 adreno_model
541 .chars()
542 .map_while(|c| c.to_digit(10))
543 .fold(0, |acc, digit| acc * 10 + digit),
544 )
545}
546
547pub fn get_mali_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
549 if !cfg!(target_os = "android") {
550 return None;
551 }
552
553 if !adapter_info.name.contains("Mali") {
554 return None;
555 }
556 let driver_info = &adapter_info.driver_info;
557 if let Some(start_pos) = driver_info.find("v1.r")
558 && let Some(end_pos) = driver_info[start_pos..].find('p')
559 {
560 let start_idx = start_pos + 4; let end_idx = start_pos + end_pos;
562
563 return driver_info[start_idx..end_idx].parse::<u32>().ok();
564 }
565
566 None
567}
568
569pub fn get_pixel10_driver_version(adapter_info: &RenderAdapterInfo) -> Option<u32> {
570 if !cfg!(target_os = "android") {
571 return None;
572 }
573
574 if adapter_info.name != "PowerVR D-Series DXT-48-1536 MC1" {
575 return None;
576 }
577
578 let (_, driver_version) = adapter_info.driver_info.split_once('@')?;
579 driver_version.parse::<u32>().ok()
580}
581
582pub fn storage_buffers_are_unsupported(limits: &WgpuLimits) -> bool {
585 static STORAGE_BUFFERS_UNSUPPORTED: OnceLock<bool> = OnceLock::new();
586 *STORAGE_BUFFERS_UNSUPPORTED.get_or_init(|| limits.max_storage_buffers_per_shader_stage == 0)
587}