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 render_adapter.clone(),
212 synchronous_pipeline_compilation,
213 ));
214 render_world.insert_resource(DeviceErrorHandler::new(&device));
215 render_world.insert_resource(device);
216 render_world.insert_resource(queue);
217 render_world.insert_resource(render_adapter);
218 render_world.insert_resource(adapter_info);
219 }
220}
221
222pub enum RenderCreation {
224 Manual(RenderResources),
226 Automatic(Box<WgpuSettings>),
228}
229
230impl RenderCreation {
231 pub fn manual(
233 device: RenderDevice,
234 queue: RenderQueue,
235 adapter_info: RenderAdapterInfo,
236 adapter: RenderAdapter,
237 instance: RenderInstance,
238 #[cfg(feature = "raw_vulkan_init")]
239 additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures,
240 ) -> Self {
241 RenderResources(
242 device,
243 queue,
244 adapter_info,
245 adapter,
246 instance,
247 #[cfg(feature = "raw_vulkan_init")]
248 additional_vulkan_features,
249 )
250 .into()
251 }
252
253 pub(crate) fn create_render(
260 &self,
261 future_resources: FutureRenderResources,
262 primary_window: Option<RawHandleWrapperHolder>,
263 #[cfg(feature = "raw_vulkan_init")]
264 raw_vulkan_init_settings: renderer::raw_vulkan_init::RawVulkanInitSettings,
265 ) -> bool {
266 match self {
267 RenderCreation::Manual(resources) => {
268 *future_resources.lock().unwrap() = Some(resources.clone());
269 }
270 RenderCreation::Automatic(render_creation) => {
271 let Some(backends) = render_creation.backends else {
272 return false;
273 };
274 let settings = render_creation.clone();
275
276 let async_renderer = async move {
277 let render_resources = renderer::initialize_renderer(
278 backends,
279 primary_window,
280 &settings,
281 #[cfg(feature = "raw_vulkan_init")]
282 raw_vulkan_init_settings,
283 )
284 .await;
285
286 *future_resources.lock().unwrap() = Some(render_resources);
287 };
288
289 #[cfg(target_arch = "wasm32")]
291 bevy_tasks::IoTaskPool::get()
292 .spawn_local(async_renderer)
293 .detach();
294 #[cfg(not(target_arch = "wasm32"))]
296 bevy_tasks::block_on(async_renderer);
297 }
298 }
299 true
300 }
301}
302
303impl From<RenderResources> for RenderCreation {
304 fn from(value: RenderResources) -> Self {
305 Self::Manual(value)
306 }
307}
308
309impl Default for RenderCreation {
310 fn default() -> Self {
311 Self::Automatic(Default::default())
312 }
313}
314
315impl From<WgpuSettings> for RenderCreation {
316 fn from(value: WgpuSettings) -> Self {
317 Self::Automatic(Box::new(value))
318 }
319}
320
321pub fn settings_priority_from_env() -> Option<WgpuSettingsPriority> {
323 Some(
324 match std::env::var("WGPU_SETTINGS_PRIO")
325 .as_deref()
326 .map(str::to_lowercase)
327 .as_deref()
328 {
329 Ok("webgpu") => WgpuSettingsPriority::WebGPU,
330 Ok("functionality") => WgpuSettingsPriority::Functionality,
331 Ok("webgl2") => WgpuSettingsPriority::WebGL2,
332 _ => return None,
333 },
334 )
335}