1#[cfg(feature = "raw_vulkan_init")]
2pub mod raw_vulkan_init;
3mod render_context;
4mod render_device;
5mod wgpu_wrapper;
6
7pub use render_context::{
8 CurrentView, FlushCommands, PendingCommandBuffers, RenderContext, RenderContextState, ViewQuery,
9};
10pub use render_device::*;
11pub use wgpu_wrapper::WgpuWrapper;
12
13use crate::{
14 settings::{RenderResources, WgpuSettings, WgpuSettingsPriority},
15 view::{ExtractedWindows, ViewTarget},
16};
17use alloc::sync::Arc;
18use bevy_camera::NormalizedRenderTarget;
19use bevy_derive::{Deref, DerefMut};
20use bevy_ecs::schedule::ScheduleLabel;
21use bevy_ecs::{prelude::*, system::SystemState};
22#[cfg(feature = "trace")]
23use bevy_log::info_span;
24use bevy_log::{debug, info, warn};
25use bevy_render::camera::ExtractedCamera;
26use bevy_window::RawHandleWrapperHolder;
27use wgpu::{
28 Adapter, AdapterInfo, Backends, DeviceType, ForceShaderModelToken, Instance, Queue,
29 RequestAdapterOptions, Trace,
30};
31
32#[derive(ScheduleLabel, Debug, Clone, PartialEq, Eq, Hash, Default)]
35pub struct RenderGraph;
36
37impl RenderGraph {
38 pub fn base_schedule() -> Schedule {
39 let mut schedule = Schedule::new(Self);
40 schedule.configure_sets(
41 (
42 RenderGraphSystems::Begin,
43 RenderGraphSystems::Render,
44 RenderGraphSystems::Submit,
45 RenderGraphSystems::Finish,
46 )
47 .chain(),
48 );
49 schedule
50 }
51}
52
53#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
55pub enum RenderGraphSystems {
56 Begin,
58 Render,
60 Submit,
62 Finish,
64}
65
66pub fn render_system(
70 world: &mut World,
71 state: &mut SystemState<Query<(&ViewTarget, &ExtractedCamera)>>,
72) {
73 #[cfg(feature = "trace")]
74 let _span = info_span!("main_render_schedule").entered();
75
76 world.run_schedule(RenderGraph);
77
78 {
79 let render_device = world.resource::<RenderDevice>();
80 let render_queue = world.resource::<RenderQueue>();
81
82 let mut encoder =
83 render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
84
85 crate::view::screenshot::submit_screenshot_commands(world, &mut encoder);
86 crate::gpu_readback::submit_readback_commands(world, &mut encoder);
87
88 render_queue.submit([encoder.finish()]);
89 }
90
91 {
92 #[cfg(feature = "trace")]
93 let _span = info_span!("present_frames").entered();
94
95 world.resource_scope(|world, mut windows: Mut<ExtractedWindows>| {
96 let views = state.get(world).unwrap();
97 for window in windows.values_mut() {
98 let view_needs_present = views.iter().any(|(view_target, camera)| {
99 matches!(
100 camera.target,
101 Some(NormalizedRenderTarget::Window(w)) if w.entity() == window.entity
102 ) && view_target.needs_present()
103 });
104
105 if view_needs_present || window.needs_initial_present {
106 window.present();
107 window.needs_initial_present = false;
108 }
109 }
110 });
111
112 #[cfg(feature = "tracing-tracy")]
113 bevy_log::event!(
114 bevy_log::Level::INFO,
115 message = "finished frame",
116 tracy.frame_mark = true
117 );
118 }
119
120 crate::view::screenshot::collect_screenshots(world);
121}
122
123#[derive(Resource, Clone, Deref, DerefMut)]
125pub struct RenderQueue(pub Arc<WgpuWrapper<Queue>>);
126
127#[derive(Resource, Clone, Debug, Deref, DerefMut)]
130pub struct RenderAdapter(pub Arc<WgpuWrapper<Adapter>>);
131
132#[derive(Resource, Clone, Deref, DerefMut)]
135pub struct RenderInstance(pub Arc<WgpuWrapper<Instance>>);
136
137#[derive(Resource, Clone, Deref, DerefMut)]
139pub struct RenderAdapterInfo(pub WgpuWrapper<AdapterInfo>);
140
141const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") {
142 "Unable to find a GPU! Make sure you have installed required drivers! For extra information, see: https://github.com/bevyengine/bevy/blob/latest/docs/linux_dependencies.md"
143} else {
144 "Unable to find a GPU! Make sure you have installed required drivers!"
145};
146
147#[cfg(not(target_family = "wasm"))]
148async fn find_adapter_by_name(
149 instance: &Instance,
150 options: &WgpuSettings,
151 compatible_surface: Option<&wgpu::Surface<'_>>,
152 adapter_name: &str,
153) -> Option<Adapter> {
154 for adapter in instance
155 .enumerate_adapters(options.backends.expect(
156 "The `backends` field of `WgpuSettings` must be set to use a specific adapter.",
157 ))
158 .await
159 {
160 bevy_log::trace!("Checking adapter: {:?}", adapter.get_info());
161 let info = adapter.get_info();
162 if let Some(surface) = compatible_surface
163 && !adapter.is_surface_supported(surface)
164 {
165 continue;
166 }
167
168 if info
169 .name
170 .to_lowercase()
171 .contains(&adapter_name.to_lowercase())
172 {
173 return Some(adapter);
174 }
175 }
176 None
177}
178
179pub async fn initialize_renderer(
182 backends: Backends,
183 primary_window: Option<RawHandleWrapperHolder>,
184 options: &WgpuSettings,
185 #[cfg(feature = "raw_vulkan_init")]
186 raw_vulkan_init_settings: raw_vulkan_init::RawVulkanInitSettings,
187) -> RenderResources {
188 let instance_descriptor = wgpu::InstanceDescriptor {
189 backends,
190 flags: options.instance_flags,
191 memory_budget_thresholds: options.instance_memory_budget_thresholds,
192 display: None,
193 backend_options: wgpu::BackendOptions {
194 gl: wgpu::GlBackendOptions {
195 gles_minor_version: options.gles3_minor_version,
196 fence_behavior: wgpu::GlFenceBehavior::Normal,
197 debug_fns: wgpu::GlDebugFns::Auto,
198 },
199 dx12: wgpu::Dx12BackendOptions {
200 shader_compiler: options.dx12_shader_compiler.clone(),
201 presentation_system: wgpu::wgt::Dx12SwapchainKind::from_env().unwrap_or_default(),
202 latency_waitable_object: wgpu::wgt::Dx12UseFrameLatencyWaitableObject::from_env()
203 .unwrap_or_default(),
204 force_shader_model: ForceShaderModelToken::default(),
205 agility_sdk: None,
206 },
207 noop: wgpu::NoopBackendOptions { enable: false },
208 },
209 };
210
211 #[cfg(not(feature = "raw_vulkan_init"))]
212 let instance = Instance::new(instance_descriptor);
213 #[cfg(feature = "raw_vulkan_init")]
214 let mut additional_vulkan_features = raw_vulkan_init::AdditionalVulkanFeatures::default();
215 #[cfg(feature = "raw_vulkan_init")]
216 let instance = raw_vulkan_init::create_raw_vulkan_instance(
217 instance_descriptor,
218 &raw_vulkan_init_settings,
219 &mut additional_vulkan_features,
220 );
221
222 let surface = primary_window.and_then(|wrapper| {
223 let maybe_handle = wrapper
224 .0
225 .lock()
226 .expect("Couldn't get the window handle in time for renderer initialization");
227 if let Some(wrapper) = maybe_handle.as_ref() {
228 let handle = unsafe { wrapper.get_handle() };
230 Some(
231 instance
232 .create_surface(handle)
233 .expect("Failed to create wgpu surface"),
234 )
235 } else {
236 None
237 }
238 });
239
240 let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK_ADAPTER")
241 .map_or(options.force_fallback_adapter, |v| {
242 !(v.is_empty() || v == "0" || v == "false")
243 });
244
245 let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME")
246 .as_deref()
247 .map_or(options.adapter_name.clone(), |x| Some(x.to_lowercase()));
248
249 let request_adapter_options = RequestAdapterOptions {
250 power_preference: options.power_preference,
251 compatible_surface: surface.as_ref(),
252 force_fallback_adapter,
253 };
254
255 #[cfg(not(target_family = "wasm"))]
256 let mut selected_adapter = if let Some(adapter_name) = desired_adapter_name {
257 find_adapter_by_name(
258 &instance,
259 options,
260 request_adapter_options.compatible_surface,
261 &adapter_name,
262 )
263 .await
264 } else {
265 None
266 };
267 #[cfg(target_family = "wasm")]
268 let mut selected_adapter = None;
269
270 #[cfg(target_family = "wasm")]
271 if desired_adapter_name.is_some() {
272 warn!("Choosing an adapter is not supported on wasm.");
273 }
274
275 if selected_adapter.is_none() {
276 debug!(
277 "Searching for adapter with options: {:?}",
278 request_adapter_options
279 );
280 selected_adapter = instance
281 .request_adapter(&request_adapter_options)
282 .await
283 .ok();
284 }
285
286 let adapter = selected_adapter.expect(GPU_NOT_FOUND_ERROR_MESSAGE);
287 let adapter_info = adapter.get_info();
288 info!("{:?}", adapter_info);
289
290 if adapter_info.device_type == DeviceType::Cpu {
291 warn!(
292 "The selected adapter is using a driver that only supports software rendering. \
293 This is likely to be very slow. See https://bevy.org/learn/errors/b0006/"
294 );
295 }
296
297 let mut features = wgpu::Features::empty();
299 let mut limits = options.limits.clone();
300 if matches!(options.priority, WgpuSettingsPriority::Functionality) {
301 features = adapter.features();
302 if adapter_info.device_type == DeviceType::DiscreteGpu {
303 features.remove(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS);
308 }
309
310 limits = adapter.limits();
311 }
312
313 if let Some(disabled_features) = options.disabled_features {
315 features.remove(disabled_features);
316 }
317 features |= options.features;
319
320 if let Some(constrained_limits) = options.constrained_limits.as_ref() {
322 limits = limits.or_worse_values_from(constrained_limits);
328 }
329
330 let device_descriptor = wgpu::DeviceDescriptor {
331 label: options.device_label.as_ref().map(AsRef::as_ref),
332 required_features: features,
333 required_limits: limits,
334 experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
336 memory_hints: options.memory_hints.clone(),
337 trace: Trace::Off,
339 };
340
341 #[cfg(not(feature = "raw_vulkan_init"))]
342 let (device, queue) = adapter.request_device(&device_descriptor).await.unwrap();
343
344 #[cfg(feature = "raw_vulkan_init")]
345 let (device, queue) = raw_vulkan_init::create_raw_device(
346 &adapter,
347 &device_descriptor,
348 &raw_vulkan_init_settings,
349 &mut additional_vulkan_features,
350 )
351 .await
352 .unwrap();
353
354 debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
355 debug!("Configured wgpu adapter Features: {:#?}", device.features());
356
357 RenderResources(
358 RenderDevice::from(device),
359 RenderQueue(Arc::new(WgpuWrapper::new(queue))),
360 RenderAdapterInfo(WgpuWrapper::new(adapter_info)),
361 RenderAdapter(Arc::new(WgpuWrapper::new(adapter))),
362 RenderInstance(Arc::new(WgpuWrapper::new(instance))),
363 #[cfg(feature = "raw_vulkan_init")]
364 additional_vulkan_features,
365 )
366}