1#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
2#![expect(unsafe_code, reason = "Unsafe code is used to improve performance.")]
3#![cfg_attr(
4 any(docsrs, docsrs_dep),
5 expect(
6 internal_features,
7 reason = "rustdoc_internals is needed for fake_variadic"
8 )
9)]
10#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
11#![doc(
12 html_logo_url = "https://bevyengine.org/assets/icon.png",
13 html_favicon_url = "https://bevyengine.org/assets/icon.png"
14)]
15
16#[cfg(target_pointer_width = "16")]
17compile_error!("bevy_render cannot compile for a 16-bit platform.");
18
19extern crate alloc;
20extern crate core;
21
22extern crate self as bevy_render;
24
25pub mod alpha;
26pub mod batching;
27pub mod camera;
28pub mod diagnostic;
29pub mod experimental;
30pub mod extract_component;
31pub mod extract_instances;
32mod extract_param;
33pub mod extract_resource;
34pub mod globals;
35pub mod gpu_component_array_buffer;
36pub mod gpu_readback;
37pub mod mesh;
38#[cfg(not(target_arch = "wasm32"))]
39pub mod pipelined_rendering;
40pub mod primitives;
41pub mod render_asset;
42pub mod render_graph;
43pub mod render_phase;
44pub mod render_resource;
45pub mod renderer;
46pub mod settings;
47pub mod storage;
48pub mod sync_component;
49pub mod sync_world;
50pub mod texture;
51pub mod view;
52
53pub mod prelude {
57 #[doc(hidden)]
58 pub use crate::{
59 alpha::AlphaMode,
60 camera::{
61 Camera, ClearColor, ClearColorConfig, OrthographicProjection, PerspectiveProjection,
62 Projection,
63 },
64 mesh::{
65 morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh, Mesh2d,
66 Mesh3d,
67 },
68 render_resource::Shader,
69 texture::ImagePlugin,
70 view::{InheritedVisibility, Msaa, ViewVisibility, Visibility},
71 ExtractSchedule,
72 };
73}
74use batching::gpu_preprocessing::BatchingPlugin;
75use bevy_ecs::schedule::ScheduleBuildSettings;
76use bevy_utils::prelude::default;
77pub use extract_param::Extract;
78
79use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
80use experimental::occlusion_culling::OcclusionCullingPlugin;
81use globals::GlobalsPlugin;
82use render_asset::{
83 extract_render_asset_bytes_per_frame, reset_render_asset_bytes_per_frame,
84 RenderAssetBytesPerFrame, RenderAssetBytesPerFrameLimiter,
85};
86use renderer::{RenderAdapter, RenderDevice, RenderQueue};
87use settings::RenderResources;
88use sync_world::{
89 despawn_temporary_render_entities, entity_sync_system, SyncToRenderWorld, SyncWorldPlugin,
90};
91
92use crate::gpu_readback::GpuReadbackPlugin;
93use crate::{
94 camera::CameraPlugin,
95 mesh::{MeshPlugin, MorphPlugin, RenderMesh},
96 render_asset::prepare_assets,
97 render_resource::{PipelineCache, Shader, ShaderLoader},
98 renderer::{render_system, RenderInstance, WgpuWrapper},
99 settings::RenderCreation,
100 storage::StoragePlugin,
101 view::{ViewPlugin, WindowRenderPlugin},
102};
103use alloc::sync::Arc;
104use bevy_app::{App, AppLabel, Plugin, SubApp};
105use bevy_asset::{load_internal_asset, weak_handle, AssetApp, AssetServer, Handle};
106use bevy_ecs::{prelude::*, schedule::ScheduleLabel};
107use bitflags::bitflags;
108use core::ops::{Deref, DerefMut};
109use std::sync::Mutex;
110use tracing::debug;
111
112#[derive(Default)]
121pub struct RenderPlugin {
122 pub render_creation: RenderCreation,
123 pub synchronous_pipeline_compilation: bool,
126 pub debug_flags: RenderDebugFlags,
128}
129
130bitflags! {
131 #[derive(Clone, Copy, PartialEq, Default, Debug)]
133 pub struct RenderDebugFlags: u8 {
134 const ALLOW_COPIES_FROM_INDIRECT_PARAMETERS = 1;
140 }
141}
142
143#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
147pub enum RenderSet {
148 ExtractCommands,
150 PrepareAssets,
152 PrepareMeshes,
154 ManageViews,
156 Queue,
159 QueueMeshes,
161 QueueSweep,
164 PhaseSort,
169 Prepare,
172 PrepareResources,
174 PrepareResourcesCollectPhaseBuffers,
177 PrepareResourcesFlush,
179 PrepareBindGroups,
181 Render,
184 Cleanup,
186 PostCleanup,
190}
191
192#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
194pub struct Render;
195
196impl Render {
197 pub fn base_schedule() -> Schedule {
201 use RenderSet::*;
202
203 let mut schedule = Schedule::new(Self);
204
205 schedule.configure_sets(
206 (
207 ExtractCommands,
208 PrepareMeshes,
209 ManageViews,
210 Queue,
211 PhaseSort,
212 Prepare,
213 Render,
214 Cleanup,
215 PostCleanup,
216 )
217 .chain(),
218 );
219
220 schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
221 schedule.configure_sets(
222 (QueueMeshes, QueueSweep)
223 .chain()
224 .in_set(Queue)
225 .after(prepare_assets::<RenderMesh>),
226 );
227 schedule.configure_sets(
228 (
229 PrepareResources,
230 PrepareResourcesCollectPhaseBuffers,
231 PrepareResourcesFlush,
232 PrepareBindGroups,
233 )
234 .chain()
235 .in_set(Prepare),
236 );
237
238 schedule
239 }
240}
241
242#[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash, Default)]
250pub struct ExtractSchedule;
251
252#[derive(Resource, Default)]
258pub struct MainWorld(World);
259
260impl Deref for MainWorld {
261 type Target = World;
262
263 fn deref(&self) -> &Self::Target {
264 &self.0
265 }
266}
267
268impl DerefMut for MainWorld {
269 fn deref_mut(&mut self) -> &mut Self::Target {
270 &mut self.0
271 }
272}
273
274pub mod graph {
275 use crate::render_graph::RenderLabel;
276
277 #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
278 pub struct CameraDriverLabel;
279}
280
281#[derive(Resource)]
282struct FutureRenderResources(Arc<Mutex<Option<RenderResources>>>);
283
284#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
286pub struct RenderApp;
287
288pub const INSTANCE_INDEX_SHADER_HANDLE: Handle<Shader> =
289 weak_handle!("475c76aa-4afd-4a6b-9878-1fc1e2f41216");
290pub const MATHS_SHADER_HANDLE: Handle<Shader> =
291 weak_handle!("d94d70d4-746d-49c4-bfc3-27d63f2acda0");
292pub const COLOR_OPERATIONS_SHADER_HANDLE: Handle<Shader> =
293 weak_handle!("33a80b2f-aaf7-4c86-b828-e7ae83b72f1a");
294pub const BINDLESS_SHADER_HANDLE: Handle<Shader> =
295 weak_handle!("13f1baaa-41bf-448e-929e-258f9307a522");
296
297impl Plugin for RenderPlugin {
298 fn build(&self, app: &mut App) {
300 app.init_asset::<Shader>()
301 .init_asset_loader::<ShaderLoader>();
302
303 match &self.render_creation {
304 RenderCreation::Manual(resources) => {
305 let future_render_resources_wrapper = Arc::new(Mutex::new(Some(resources.clone())));
306 app.insert_resource(FutureRenderResources(
307 future_render_resources_wrapper.clone(),
308 ));
309 unsafe { initialize_render_app(app) };
311 }
312 RenderCreation::Automatic(render_creation) => {
313 if let Some(backends) = render_creation.backends {
314 let future_render_resources_wrapper = Arc::new(Mutex::new(None));
315 app.insert_resource(FutureRenderResources(
316 future_render_resources_wrapper.clone(),
317 ));
318
319 let primary_window = app
320 .world_mut()
321 .query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
322 .single(app.world())
323 .ok()
324 .cloned();
325 let settings = render_creation.clone();
326 let async_renderer = async move {
327 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
328 backends,
329 flags: settings.instance_flags,
330 backend_options: wgpu::BackendOptions {
331 gl: wgpu::GlBackendOptions {
332 gles_minor_version: settings.gles3_minor_version,
333 },
334 dx12: wgpu::Dx12BackendOptions {
335 shader_compiler: settings.dx12_shader_compiler.clone(),
336 },
337 },
338 });
339
340 let surface = primary_window.and_then(|wrapper| {
341 let maybe_handle = wrapper.0.lock().expect(
342 "Couldn't get the window handle in time for renderer initialization",
343 );
344 if let Some(wrapper) = maybe_handle.as_ref() {
345 let handle = unsafe { wrapper.get_handle() };
347 Some(
348 instance
349 .create_surface(handle)
350 .expect("Failed to create wgpu surface"),
351 )
352 } else {
353 None
354 }
355 });
356
357 let request_adapter_options = wgpu::RequestAdapterOptions {
358 power_preference: settings.power_preference,
359 compatible_surface: surface.as_ref(),
360 ..Default::default()
361 };
362
363 let (device, queue, adapter_info, render_adapter) =
364 renderer::initialize_renderer(
365 &instance,
366 &settings,
367 &request_adapter_options,
368 )
369 .await;
370 debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
371 debug!("Configured wgpu adapter Features: {:#?}", device.features());
372 let mut future_render_resources_inner =
373 future_render_resources_wrapper.lock().unwrap();
374 *future_render_resources_inner = Some(RenderResources(
375 device,
376 queue,
377 adapter_info,
378 render_adapter,
379 RenderInstance(Arc::new(WgpuWrapper::new(instance))),
380 ));
381 };
382 #[cfg(target_arch = "wasm32")]
384 bevy_tasks::IoTaskPool::get()
385 .spawn_local(async_renderer)
386 .detach();
387 #[cfg(not(target_arch = "wasm32"))]
389 futures_lite::future::block_on(async_renderer);
390
391 unsafe { initialize_render_app(app) };
393 }
394 }
395 };
396
397 app.add_plugins((
398 WindowRenderPlugin,
399 CameraPlugin,
400 ViewPlugin,
401 MeshPlugin,
402 GlobalsPlugin,
403 MorphPlugin,
404 BatchingPlugin {
405 debug_flags: self.debug_flags,
406 },
407 SyncWorldPlugin,
408 StoragePlugin,
409 GpuReadbackPlugin::default(),
410 OcclusionCullingPlugin,
411 #[cfg(feature = "tracing-tracy")]
412 diagnostic::RenderDiagnosticsPlugin,
413 ));
414
415 app.init_resource::<RenderAssetBytesPerFrame>();
416 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
417 render_app.init_resource::<RenderAssetBytesPerFrameLimiter>();
418 render_app
419 .add_systems(ExtractSchedule, extract_render_asset_bytes_per_frame)
420 .add_systems(
421 Render,
422 reset_render_asset_bytes_per_frame.in_set(RenderSet::Cleanup),
423 );
424 }
425
426 app.register_type::<alpha::AlphaMode>()
427 .register_type::<bevy_color::Color>()
429 .register_type::<primitives::Aabb>()
430 .register_type::<primitives::CascadesFrusta>()
431 .register_type::<primitives::CubemapFrusta>()
432 .register_type::<primitives::Frustum>()
433 .register_type::<SyncToRenderWorld>();
434 }
435
436 fn ready(&self, app: &App) -> bool {
437 app.world()
438 .get_resource::<FutureRenderResources>()
439 .and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
440 .unwrap_or(true)
441 }
442
443 fn finish(&self, app: &mut App) {
444 load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
445 load_internal_asset!(
446 app,
447 COLOR_OPERATIONS_SHADER_HANDLE,
448 "color_operations.wgsl",
449 Shader::from_wgsl
450 );
451 load_internal_asset!(
452 app,
453 BINDLESS_SHADER_HANDLE,
454 "bindless.wgsl",
455 Shader::from_wgsl
456 );
457 if let Some(future_render_resources) =
458 app.world_mut().remove_resource::<FutureRenderResources>()
459 {
460 let RenderResources(device, queue, adapter_info, render_adapter, instance) =
461 future_render_resources.0.lock().unwrap().take().unwrap();
462
463 app.insert_resource(device.clone())
464 .insert_resource(queue.clone())
465 .insert_resource(adapter_info.clone())
466 .insert_resource(render_adapter.clone());
467
468 let render_app = app.sub_app_mut(RenderApp);
469
470 render_app
471 .insert_resource(instance)
472 .insert_resource(PipelineCache::new(
473 device.clone(),
474 render_adapter.clone(),
475 self.synchronous_pipeline_compilation,
476 ))
477 .insert_resource(device)
478 .insert_resource(queue)
479 .insert_resource(render_adapter)
480 .insert_resource(adapter_info);
481 }
482 }
483}
484
485#[derive(Resource, Default)]
488struct ScratchMainWorld(World);
489
490fn extract(main_world: &mut World, render_world: &mut World) {
493 let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
495 let inserted_world = core::mem::replace(main_world, scratch_world.0);
496 render_world.insert_resource(MainWorld(inserted_world));
497 render_world.run_schedule(ExtractSchedule);
498
499 let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
501 let scratch_world = core::mem::replace(main_world, inserted_world.0);
502 main_world.insert_resource(ScratchMainWorld(scratch_world));
503}
504
505unsafe fn initialize_render_app(app: &mut App) {
508 app.init_resource::<ScratchMainWorld>();
509
510 let mut render_app = SubApp::new();
511 render_app.update_schedule = Some(Render.intern());
512
513 let mut extract_schedule = Schedule::new(ExtractSchedule);
514 extract_schedule.set_build_settings(ScheduleBuildSettings {
517 auto_insert_apply_deferred: false,
518 ..default()
519 });
520 extract_schedule.set_apply_final_deferred(false);
521
522 render_app
523 .add_schedule(extract_schedule)
524 .add_schedule(Render::base_schedule())
525 .init_resource::<render_graph::RenderGraph>()
526 .insert_resource(app.world().resource::<AssetServer>().clone())
527 .add_systems(ExtractSchedule, PipelineCache::extract_shaders)
528 .add_systems(
529 Render,
530 (
531 apply_extract_commands.in_set(RenderSet::ExtractCommands),
534 (PipelineCache::process_pipeline_queue_system, render_system)
535 .chain()
536 .in_set(RenderSet::Render),
537 despawn_temporary_render_entities.in_set(RenderSet::PostCleanup),
538 ),
539 );
540
541 render_app.set_extract(|main_world, render_world| {
542 {
543 #[cfg(feature = "trace")]
544 let _stage_span = tracing::info_span!("entity_sync").entered();
545 entity_sync_system(main_world, render_world);
546 }
547
548 extract(main_world, render_world);
550 });
551
552 let (sender, receiver) = bevy_time::create_time_channels();
553 render_app.insert_resource(sender);
554 app.insert_resource(receiver);
555 app.insert_sub_app(RenderApp, render_app);
556}
557
558fn apply_extract_commands(render_world: &mut World) {
562 render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
563 schedules
564 .get_mut(ExtractSchedule)
565 .unwrap()
566 .apply_deferred(render_world);
567 });
568}
569
570pub fn get_adreno_model(adapter: &RenderAdapter) -> Option<u32> {
574 if !cfg!(target_os = "android") {
575 return None;
576 }
577
578 let adapter_name = adapter.get_info().name;
579 let adreno_model = adapter_name.strip_prefix("Adreno (TM) ")?;
580
581 Some(
583 adreno_model
584 .chars()
585 .map_while(|c| c.to_digit(10))
586 .fold(0, |acc, digit| acc * 10 + digit),
587 )
588}
589
590pub fn get_mali_driver_version(adapter: &RenderAdapter) -> Option<u32> {
592 if !cfg!(target_os = "android") {
593 return None;
594 }
595
596 let driver_name = adapter.get_info().name;
597 if !driver_name.contains("Mali") {
598 return None;
599 }
600 let driver_info = adapter.get_info().driver_info;
601 if let Some(start_pos) = driver_info.find("v1.r") {
602 if let Some(end_pos) = driver_info[start_pos..].find('p') {
603 let start_idx = start_pos + 4; let end_idx = start_pos + end_pos;
605
606 return driver_info[start_idx..end_idx].parse::<u32>().ok();
607 }
608 }
609
610 None
611}