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