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::*;
11
12pub(crate) use wgpu_wrapper::wgpu_wrapper;
13
14use crate::{
15 settings::{RenderResources, WgpuSettings, WgpuSettingsPriority},
16 sync_world::MainEntity,
17 view::{screenshot::SubmitScreenshotCommandsState, ExtractedWindow, ViewTarget},
18};
19use bevy_camera::NormalizedRenderTarget;
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)]
48pub struct RenderGraph;
49
50impl RenderGraph {
51 pub fn base_schedule() -> Schedule {
52 let mut schedule = Schedule::new(Self);
53 schedule.configure_sets(
54 (
55 RenderGraphSystems::Begin,
56 RenderGraphSystems::Render,
57 RenderGraphSystems::Submit,
58 RenderGraphSystems::Finish,
59 )
60 .chain_weak(),
61 );
62 schedule
63 }
64}
65
66#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
68pub enum RenderGraphSystems {
69 Begin,
71 Render,
73 Submit,
75 Finish,
77}
78
79pub fn render_system(
83 world: &mut World,
84 present_state: &mut SystemState<(
85 Query<(&ViewTarget, &ExtractedCamera)>,
86 Query<(MainEntity, &mut ExtractedWindow)>,
87 Res<RenderQueue>,
88 )>,
89 screenshot_state: &mut SystemState<SubmitScreenshotCommandsState>,
90) {
91 #[cfg(feature = "trace")]
92 let _span = info_span!("main_render_schedule").entered();
93
94 world.run_schedule(RenderGraph);
95
96 {
97 let render_device = world.resource::<RenderDevice>();
98 let render_queue = world.resource::<RenderQueue>();
99
100 let mut encoder =
101 render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
102
103 crate::view::screenshot::submit_screenshot_commands(world, screenshot_state, &mut encoder);
104 crate::gpu_readback::submit_readback_commands(world, &mut encoder);
105
106 render_queue.submit([encoder.finish()]);
107 }
108
109 {
110 #[cfg(feature = "trace")]
111 let _span = info_span!("present_frames").entered();
112
113 if let Ok((views, mut windows, render_queue)) = present_state.get_mut(world) {
114 for (window_entity, mut window) in &mut windows {
115 let view_needs_present = views.iter().any(|(view_target, camera)| {
116 matches!(
117 camera.target,
118 Some(NormalizedRenderTarget::Window(w)) if w.entity() == window_entity
119 ) && view_target.needs_present()
120 });
121
122 if view_needs_present || window.needs_initial_present {
123 window.present(&render_queue);
124 window.needs_initial_present = false;
125 }
126 }
127 }
128
129 #[cfg(feature = "tracing-tracy")]
130 bevy_log::event!(
131 bevy_log::Level::INFO,
132 message = "finished frame",
133 tracy.frame_mark = true
134 );
135 }
136
137 crate::view::screenshot::collect_screenshots(world);
138}
139
140wgpu_wrapper! {
141 #[derive(Resource, Clone)]
143 pub struct RenderQueue(Queue);
144
145 #[derive(Resource, Clone, Debug)]
148 pub struct RenderAdapter(Adapter);
149
150 #[derive(Resource, Clone)]
152 pub struct RenderInstance(Instance);
153
154 #[derive(Resource, Clone)]
156 pub struct RenderAdapterInfo(AdapterInfo);
157}
158
159const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") {
160 "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"
161} else {
162 "Unable to find a GPU! Make sure you have installed required drivers!"
163};
164
165#[cfg(not(target_family = "wasm"))]
166async fn find_adapter_by_name(
167 instance: &Instance,
168 options: &WgpuSettings,
169 compatible_surface: Option<&wgpu::Surface<'_>>,
170 adapter_name: &str,
171) -> Option<Adapter> {
172 for adapter in instance
173 .enumerate_adapters(options.backends.expect(
174 "The `backends` field of `WgpuSettings` must be set to use a specific adapter.",
175 ))
176 .await
177 {
178 bevy_log::trace!("Checking adapter: {:?}", adapter.get_info());
179 let info = adapter.get_info();
180 if let Some(surface) = compatible_surface
181 && !adapter.is_surface_supported(surface)
182 {
183 continue;
184 }
185
186 if info
187 .name
188 .to_lowercase()
189 .contains(&adapter_name.to_lowercase())
190 {
191 return Some(adapter);
192 }
193 }
194 None
195}
196
197pub async fn initialize_renderer(
200 backends: Backends,
201 primary_window: Option<RawHandleWrapperHolder>,
202 options: &WgpuSettings,
203 #[cfg(feature = "raw_vulkan_init")]
204 raw_vulkan_init_settings: raw_vulkan_init::RawVulkanInitSettings,
205) -> RenderResources {
206 let instance_descriptor = wgpu::InstanceDescriptor {
207 backends,
208 flags: options.instance_flags,
209 memory_budget_thresholds: options.instance_memory_budget_thresholds,
210 display: None,
211 backend_options: wgpu::BackendOptions {
212 gl: wgpu::GlBackendOptions {
213 gles_minor_version: options.gles3_minor_version,
214 fence_behavior: wgpu::GlFenceBehavior::Normal,
215 debug_fns: wgpu::GlDebugFns::Auto,
216 },
217 dx12: wgpu::Dx12BackendOptions {
218 shader_compiler: options.dx12_shader_compiler.clone(),
219 presentation_system: wgpu::wgt::Dx12SwapchainKind::from_env().unwrap_or_default(),
220 latency_waitable_object: wgpu::wgt::Dx12UseFrameLatencyWaitableObject::from_env()
221 .unwrap_or_default(),
222 force_shader_model: ForceShaderModelToken::default(),
223 agility_sdk: None,
224 },
225 noop: wgpu::NoopBackendOptions {
226 enable: false,
227 ..Default::default()
228 },
229 },
230 };
231
232 #[cfg(not(feature = "raw_vulkan_init"))]
233 let instance = Instance::new(instance_descriptor);
234 #[cfg(feature = "raw_vulkan_init")]
235 let mut additional_vulkan_features = raw_vulkan_init::AdditionalVulkanFeatures::default();
236 #[cfg(feature = "raw_vulkan_init")]
237 let instance = raw_vulkan_init::create_raw_vulkan_instance(
238 instance_descriptor,
239 &raw_vulkan_init_settings,
240 &mut additional_vulkan_features,
241 );
242
243 let surface = primary_window.and_then(|wrapper| {
244 let maybe_handle = wrapper
245 .0
246 .lock()
247 .expect("Couldn't get the window handle in time for renderer initialization");
248 if let Some(wrapper) = maybe_handle.as_ref() {
249 let handle = unsafe { wrapper.get_handle() };
251 Some(
252 instance
253 .create_surface(handle)
254 .expect("Failed to create wgpu surface"),
255 )
256 } else {
257 None
258 }
259 });
260
261 let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK_ADAPTER")
262 .map_or(options.force_fallback_adapter, |v| {
263 !(v.is_empty() || v == "0" || v == "false")
264 });
265
266 let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME")
267 .as_deref()
268 .map_or(options.adapter_name.clone(), |x| Some(x.to_lowercase()));
269
270 let request_adapter_options = RequestAdapterOptions {
271 power_preference: options.power_preference,
272 compatible_surface: surface.as_ref(),
273 force_fallback_adapter,
274 apply_limit_buckets: false,
275 };
276
277 #[cfg(not(target_family = "wasm"))]
278 let mut selected_adapter = if let Some(adapter_name) = desired_adapter_name {
279 find_adapter_by_name(
280 &instance,
281 options,
282 request_adapter_options.compatible_surface,
283 &adapter_name,
284 )
285 .await
286 } else {
287 None
288 };
289 #[cfg(target_family = "wasm")]
290 let mut selected_adapter = None;
291
292 #[cfg(target_family = "wasm")]
293 if desired_adapter_name.is_some() {
294 warn!("Choosing an adapter is not supported on wasm.");
295 }
296
297 if selected_adapter.is_none() {
298 debug!(
299 "Searching for adapter with options: {:?}",
300 request_adapter_options
301 );
302 selected_adapter = instance
303 .request_adapter(&request_adapter_options)
304 .await
305 .ok();
306 }
307
308 let adapter = selected_adapter.expect(GPU_NOT_FOUND_ERROR_MESSAGE);
309 let adapter_info = adapter.get_info();
310 info!("{:?}", adapter_info);
311
312 if adapter_info.device_type == DeviceType::Cpu {
313 warn!(
314 "The selected adapter is using a driver that only supports software rendering. \
315 This is likely to be very slow. See https://bevy.org/learn/errors/b0006/"
316 );
317 }
318
319 let mut features = wgpu::Features::empty();
321 let mut limits = options.limits.clone();
322 if matches!(options.priority, WgpuSettingsPriority::Functionality) {
323 features = adapter.features();
324 if adapter_info.device_type == DeviceType::DiscreteGpu {
325 features.remove(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS);
330 }
331
332 limits = adapter.limits();
333 }
334
335 if let Some(disabled_features) = options.disabled_features {
337 features.remove(disabled_features);
338 }
339 features |= options.features;
341
342 if let Some(constrained_limits) = options.constrained_limits.as_ref() {
344 limits = limits.or_worse_values_from(constrained_limits);
350 }
351
352 let device_descriptor = wgpu::DeviceDescriptor {
353 label: options.device_label.as_ref().map(AsRef::as_ref),
354 required_features: features,
355 required_limits: limits,
356 experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
358 memory_hints: options.memory_hints.clone(),
359 trace: Trace::Off,
361 };
362
363 #[cfg(not(feature = "raw_vulkan_init"))]
364 let (device, queue) = adapter.request_device(&device_descriptor).await.unwrap();
365
366 #[cfg(feature = "raw_vulkan_init")]
367 let (device, queue) = raw_vulkan_init::create_raw_device(
368 &adapter,
369 &device_descriptor,
370 &raw_vulkan_init_settings,
371 &mut additional_vulkan_features,
372 )
373 .await
374 .unwrap();
375
376 debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
377 debug!("Configured wgpu adapter Features: {:#?}", device.features());
378
379 RenderResources(
380 RenderDevice::from(device),
381 RenderQueue::new(queue),
382 RenderAdapterInfo::new(adapter_info),
383 RenderAdapter::new(adapter),
384 RenderInstance::new(instance),
385 #[cfg(feature = "raw_vulkan_init")]
386 additional_vulkan_features,
387 )
388}