1use crate::{
2 error_handler::DeviceErrorHandler,
3 render_resource::PipelineCache,
4 renderer::{self, RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue},
5 FutureRenderResources,
6};
7use alloc::borrow::Cow;
8use bevy_ecs::world::World;
9use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats};
10use bevy_window::RawHandleWrapperHolder;
11
12use wgpu::MemoryBudgetThresholds;
13pub use wgpu::{
14 Backends, Dx12Compiler, Features as WgpuFeatures, Gles3MinorVersion, InstanceFlags,
15 Limits as WgpuLimits, MemoryHints, PowerPreference,
16};
17
18#[derive(Clone)]
20pub enum WgpuSettingsPriority {
21 WebGPU,
23 Functionality,
25 WebGL2,
27}
28
29#[derive(Clone)]
39pub struct WgpuSettings {
40 pub device_label: Option<Cow<'static, str>>,
41 pub backends: Option<Backends>,
42 pub power_preference: PowerPreference,
43 pub priority: WgpuSettingsPriority,
44 pub features: WgpuFeatures,
47 pub disabled_features: Option<WgpuFeatures>,
49 pub limits: WgpuLimits,
51 pub constrained_limits: Option<WgpuLimits>,
53 pub dx12_shader_compiler: Dx12Compiler,
55 pub gles3_minor_version: Gles3MinorVersion,
58 pub instance_flags: InstanceFlags,
60 pub memory_hints: MemoryHints,
62 pub instance_memory_budget_thresholds: MemoryBudgetThresholds,
64 pub force_fallback_adapter: bool,
66 pub adapter_name: Option<String>,
68}
69
70impl Default for WgpuSettings {
71 fn default() -> Self {
72 let default_backends = if cfg!(all(
73 feature = "webgl",
74 target_arch = "wasm32",
75 not(feature = "webgpu")
76 )) {
77 Backends::GL
78 } else if cfg!(all(feature = "webgpu", target_arch = "wasm32")) {
79 Backends::BROWSER_WEBGPU
80 } else {
81 Backends::all()
82 };
83
84 let backends = Some(Backends::from_env().unwrap_or(default_backends));
85
86 let power_preference =
87 PowerPreference::from_env().unwrap_or(PowerPreference::HighPerformance);
88
89 let priority = settings_priority_from_env().unwrap_or(WgpuSettingsPriority::Functionality);
90
91 let limits = if cfg!(all(
92 feature = "webgl",
93 target_arch = "wasm32",
94 not(feature = "webgpu")
95 )) || matches!(priority, WgpuSettingsPriority::WebGL2)
96 {
97 wgpu::Limits::downlevel_webgl2_defaults()
98 } else {
99 #[expect(clippy::allow_attributes, reason = "`unused_mut` is not always linted")]
100 #[allow(
101 unused_mut,
102 reason = "This variable needs to be mutable if the `ci_limits` feature is enabled"
103 )]
104 let mut limits = wgpu::Limits::default();
105 #[cfg(feature = "ci_limits")]
106 {
107 limits.max_storage_textures_per_shader_stage = 4;
108 limits.max_texture_dimension_3d = 1024;
109 }
110 limits
111 };
112
113 let dx12_shader_compiler =
114 Dx12Compiler::from_env().unwrap_or(if cfg!(feature = "statically-linked-dxc") {
115 Dx12Compiler::StaticDxc
116 } else {
117 let dxc = "dxcompiler.dll";
118
119 if cfg!(target_os = "windows") && std::fs::metadata(dxc).is_ok() {
120 Dx12Compiler::DynamicDxc {
121 dxc_path: String::from(dxc),
122 }
123 } else {
124 Dx12Compiler::Fxc
125 }
126 });
127
128 let gles3_minor_version = Gles3MinorVersion::from_env().unwrap_or_default();
129
130 let mut instance_flags = InstanceFlags::default();
131 #[cfg(not(debug_assertions))]
132 {
133 if !backends.is_some_and(|backends| backends.contains(Backends::DX12)) {
136 instance_flags.remove(InstanceFlags::VALIDATION_INDIRECT_CALL);
138 }
139 }
140 #[cfg(all(not(debug_assertions), feature = "raw_vulkan_init"))]
141 instance_flags.remove(InstanceFlags::VALIDATION_INDIRECT_CALL);
143
144 instance_flags = instance_flags.with_env();
145
146 Self {
147 device_label: Default::default(),
148 backends,
149 power_preference,
150 priority,
151 features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
152 disabled_features: None,
153 limits,
154 constrained_limits: None,
155 dx12_shader_compiler,
156 gles3_minor_version,
157 instance_flags,
158 memory_hints: MemoryHints::default(),
159 instance_memory_budget_thresholds: MemoryBudgetThresholds::default(),
160 force_fallback_adapter: false,
161 adapter_name: None,
162 }
163 }
164}
165
166#[derive(Clone)]
167pub struct RenderResources(
168 pub RenderDevice,
169 pub RenderQueue,
170 pub RenderAdapterInfo,
171 pub RenderAdapter,
172 pub RenderInstance,
173 #[cfg(feature = "raw_vulkan_init")] pub renderer::raw_vulkan_init::AdditionalVulkanFeatures,
174);
175
176impl RenderResources {
177 pub(crate) fn unpack_into(
185 self,
186 main_world: &mut World,
187 render_world: &mut World,
188 synchronous_pipeline_compilation: bool,
189 ) {
190 let RenderResources(device, queue, adapter_info, render_adapter, instance, ..) = self;
191
192 let compressed_image_format_support =
193 CompressedImageFormatSupport(CompressedImageFormats::from_features(device.features()));
194
195 main_world.insert_resource(device.clone());
196 main_world.insert_resource(queue.clone());
197 main_world.insert_resource(adapter_info.clone());
198 main_world.insert_resource(render_adapter.clone());
199 main_world.insert_resource(compressed_image_format_support);
200
201 #[cfg(feature = "raw_vulkan_init")]
202 {
203 let additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures =
204 self.5;
205 render_world.insert_resource(additional_vulkan_features);
206 }
207
208 render_world.insert_resource(instance);
209 render_world.insert_resource(PipelineCache::new(
210 device.clone(),
211 synchronous_pipeline_compilation,
212 ));
213 render_world.insert_resource(DeviceErrorHandler::new(&device));
214 render_world.insert_resource(device);
215 render_world.insert_resource(queue);
216 render_world.insert_resource(render_adapter);
217 render_world.insert_resource(adapter_info);
218 }
219}
220
221pub enum RenderCreation {
223 Manual(Box<RenderResources>),
225 Automatic(Box<WgpuSettings>),
227}
228
229impl RenderCreation {
230 pub fn manual(
232 device: RenderDevice,
233 queue: RenderQueue,
234 adapter_info: RenderAdapterInfo,
235 adapter: RenderAdapter,
236 instance: RenderInstance,
237 #[cfg(feature = "raw_vulkan_init")]
238 additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures,
239 ) -> Self {
240 RenderResources(
241 device,
242 queue,
243 adapter_info,
244 adapter,
245 instance,
246 #[cfg(feature = "raw_vulkan_init")]
247 additional_vulkan_features,
248 )
249 .into()
250 }
251
252 pub(crate) fn create_render(
259 &self,
260 future_resources: FutureRenderResources,
261 primary_window: Option<RawHandleWrapperHolder>,
262 #[cfg(feature = "raw_vulkan_init")]
263 raw_vulkan_init_settings: renderer::raw_vulkan_init::RawVulkanInitSettings,
264 ) -> bool {
265 match self {
266 RenderCreation::Manual(resources) => {
267 *future_resources.lock().unwrap() = Some(*resources.clone());
268 }
269 RenderCreation::Automatic(render_creation) => {
270 let Some(backends) = render_creation.backends else {
271 return false;
272 };
273 let settings = render_creation.clone();
274
275 let async_renderer = async move {
276 let render_resources = renderer::initialize_renderer(
277 backends,
278 primary_window,
279 &settings,
280 #[cfg(feature = "raw_vulkan_init")]
281 raw_vulkan_init_settings,
282 )
283 .await;
284
285 *future_resources.lock().unwrap() = Some(render_resources);
286 };
287
288 #[cfg(target_arch = "wasm32")]
290 bevy_tasks::IoTaskPool::get()
291 .spawn_local(async_renderer)
292 .detach();
293 #[cfg(not(target_arch = "wasm32"))]
295 bevy_tasks::block_on(async_renderer);
296 }
297 }
298 true
299 }
300}
301
302impl From<RenderResources> for RenderCreation {
303 fn from(value: RenderResources) -> Self {
304 Self::Manual(Box::new(value))
305 }
306}
307
308impl Default for RenderCreation {
309 fn default() -> Self {
310 Self::Automatic(Default::default())
311 }
312}
313
314impl From<WgpuSettings> for RenderCreation {
315 fn from(value: WgpuSettings) -> Self {
316 Self::Automatic(Box::new(value))
317 }
318}
319
320pub fn settings_priority_from_env() -> Option<WgpuSettingsPriority> {
322 Some(
323 match std::env::var("WGPU_SETTINGS_PRIO")
324 .as_deref()
325 .map(str::to_lowercase)
326 .as_deref()
327 {
328 Ok("webgpu") => WgpuSettingsPriority::WebGPU,
329 Ok("functionality") => WgpuSettingsPriority::Functionality,
330 Ok("webgl2") => WgpuSettingsPriority::WebGL2,
331 _ => return None,
332 },
333 )
334}