Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/settings.rs
9324 views
1
use crate::{
2
error_handler::DeviceErrorHandler,
3
render_resource::PipelineCache,
4
renderer::{self, RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue},
5
FutureRenderResources,
6
};
7
use alloc::borrow::Cow;
8
use bevy_ecs::world::World;
9
use bevy_image::{CompressedImageFormatSupport, CompressedImageFormats};
10
use bevy_window::RawHandleWrapperHolder;
11
12
pub use wgpu::{
13
Backends, Dx12Compiler, Features as WgpuFeatures, Gles3MinorVersion, InstanceFlags,
14
Limits as WgpuLimits, MemoryHints, PowerPreference,
15
};
16
use wgpu::{DxcShaderModel, MemoryBudgetThresholds};
17
18
/// Configures the priority used when automatically configuring the features/limits of `wgpu`.
19
#[derive(Clone)]
20
pub enum WgpuSettingsPriority {
21
/// WebGPU default features and limits
22
Compatibility,
23
/// The maximum supported features and limits of the adapter and backend
24
Functionality,
25
/// WebGPU default limits plus additional constraints in order to be compatible with WebGL2
26
WebGL2,
27
}
28
29
/// Provides configuration for renderer initialization. Use [`RenderDevice::features`](RenderDevice::features),
30
/// [`RenderDevice::limits`](RenderDevice::limits), and the [`RenderAdapterInfo`]
31
/// resource to get runtime information about the actual adapter, backend, features, and limits.
32
/// NOTE: [`Backends::DX12`](Backends::DX12), [`Backends::METAL`](Backends::METAL), and
33
/// [`Backends::VULKAN`](Backends::VULKAN) are enabled by default for non-web and the best choice
34
/// is automatically selected. Web using the `webgl` feature uses [`Backends::GL`](Backends::GL).
35
/// NOTE: If you want to use [`Backends::GL`](Backends::GL) in a native app on `Windows` and/or `macOS`, you must
36
/// use [`ANGLE`](https://github.com/gfx-rs/wgpu#angle) and enable the `gles` feature. This is
37
/// because wgpu requires EGL to create a GL context without a window and only ANGLE supports that.
38
#[derive(Clone)]
39
pub 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
/// The features to ensure are enabled regardless of what the adapter/backend supports.
45
/// Setting these explicitly may cause renderer initialization to fail.
46
pub features: WgpuFeatures,
47
/// The features to ensure are disabled regardless of what the adapter/backend supports
48
pub disabled_features: Option<WgpuFeatures>,
49
/// The imposed limits.
50
pub limits: WgpuLimits,
51
/// The constraints on limits allowed regardless of what the adapter/backend supports
52
pub constrained_limits: Option<WgpuLimits>,
53
/// The shader compiler to use for the DX12 backend.
54
pub dx12_shader_compiler: Dx12Compiler,
55
/// Allows you to choose which minor version of GLES3 to use (3.0, 3.1, 3.2, or automatic)
56
/// This only applies when using ANGLE and the GL backend.
57
pub gles3_minor_version: Gles3MinorVersion,
58
/// These are for controlling WGPU's debug information to eg. enable validation and shader debug info in release builds.
59
pub instance_flags: InstanceFlags,
60
/// This hints to the WGPU device about the preferred memory allocation strategy.
61
pub memory_hints: MemoryHints,
62
/// The thresholds for device memory budget.
63
pub instance_memory_budget_thresholds: MemoryBudgetThresholds,
64
/// If true, will force wgpu to use a software renderer, if available.
65
pub force_fallback_adapter: bool,
66
/// The name of the adapter to use.
67
pub adapter_name: Option<String>,
68
}
69
70
impl 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
max_shader_model: DxcShaderModel::V6_7,
123
}
124
} else {
125
Dx12Compiler::Fxc
126
}
127
});
128
129
let gles3_minor_version = Gles3MinorVersion::from_env().unwrap_or_default();
130
131
let instance_flags = InstanceFlags::default().with_env();
132
133
Self {
134
device_label: Default::default(),
135
backends,
136
power_preference,
137
priority,
138
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
139
disabled_features: None,
140
limits,
141
constrained_limits: None,
142
dx12_shader_compiler,
143
gles3_minor_version,
144
instance_flags,
145
memory_hints: MemoryHints::default(),
146
instance_memory_budget_thresholds: MemoryBudgetThresholds::default(),
147
force_fallback_adapter: false,
148
adapter_name: None,
149
}
150
}
151
}
152
153
#[derive(Clone)]
154
pub struct RenderResources(
155
pub RenderDevice,
156
pub RenderQueue,
157
pub RenderAdapterInfo,
158
pub RenderAdapter,
159
pub RenderInstance,
160
#[cfg(feature = "raw_vulkan_init")] pub renderer::raw_vulkan_init::AdditionalVulkanFeatures,
161
);
162
163
impl RenderResources {
164
/// Effectively, this replaces the current render backend entirely with the given resources.
165
///
166
/// We deconstruct the [`RenderResources`] and make them usable by the main and render worlds,
167
/// and insert [`PipelineCache`] and [`CompressedImageFormats`] which directly depend on having
168
/// references to these resources within them to be accurate. This causes all shaders to
169
/// be recompiled, and the set of supported images to possibly change. This is necessary
170
/// because the new backend may have different compression support or shader language.
171
pub(crate) fn unpack_into(
172
self,
173
main_world: &mut World,
174
render_world: &mut World,
175
synchronous_pipeline_compilation: bool,
176
) {
177
let RenderResources(device, queue, adapter_info, render_adapter, instance, ..) = self;
178
179
let compressed_image_format_support =
180
CompressedImageFormatSupport(CompressedImageFormats::from_features(device.features()));
181
182
main_world.insert_resource(device.clone());
183
main_world.insert_resource(queue.clone());
184
main_world.insert_resource(adapter_info.clone());
185
main_world.insert_resource(render_adapter.clone());
186
main_world.insert_resource(compressed_image_format_support);
187
188
#[cfg(feature = "raw_vulkan_init")]
189
{
190
let additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures =
191
self.5;
192
render_world.insert_resource(additional_vulkan_features);
193
}
194
195
render_world.insert_resource(instance);
196
render_world.insert_resource(PipelineCache::new(
197
device.clone(),
198
render_adapter.clone(),
199
synchronous_pipeline_compilation,
200
));
201
render_world.insert_resource(DeviceErrorHandler::new(&device));
202
render_world.insert_resource(device);
203
render_world.insert_resource(queue);
204
render_world.insert_resource(render_adapter);
205
render_world.insert_resource(adapter_info);
206
}
207
}
208
209
/// An enum describing how the renderer will initialize resources. This is used when creating the [`RenderPlugin`](crate::RenderPlugin).
210
pub enum RenderCreation {
211
/// Allows renderer resource initialization to happen outside of the rendering plugin.
212
Manual(RenderResources),
213
/// Lets the rendering plugin create resources itself.
214
Automatic(Box<WgpuSettings>),
215
}
216
217
impl RenderCreation {
218
/// Function to create a [`RenderCreation::Manual`] variant.
219
pub fn manual(
220
device: RenderDevice,
221
queue: RenderQueue,
222
adapter_info: RenderAdapterInfo,
223
adapter: RenderAdapter,
224
instance: RenderInstance,
225
#[cfg(feature = "raw_vulkan_init")]
226
additional_vulkan_features: renderer::raw_vulkan_init::AdditionalVulkanFeatures,
227
) -> Self {
228
RenderResources(
229
device,
230
queue,
231
adapter_info,
232
adapter,
233
instance,
234
#[cfg(feature = "raw_vulkan_init")]
235
additional_vulkan_features,
236
)
237
.into()
238
}
239
240
/// Creates [`RenderResources`] from this [`RenderCreation`] and an optional primary window
241
/// and writes them into `future_resources`, possibly asynchronously.
242
///
243
/// Returns true if creation was successful, false otherwise.
244
///
245
/// Note: [`RenderCreation::Manual`] will ignore the provided primary window.
246
pub(crate) fn create_render(
247
&self,
248
future_resources: FutureRenderResources,
249
primary_window: Option<RawHandleWrapperHolder>,
250
#[cfg(feature = "raw_vulkan_init")]
251
raw_vulkan_init_settings: renderer::raw_vulkan_init::RawVulkanInitSettings,
252
) -> bool {
253
match self {
254
RenderCreation::Manual(resources) => {
255
*future_resources.lock().unwrap() = Some(resources.clone());
256
}
257
RenderCreation::Automatic(render_creation) => {
258
let Some(backends) = render_creation.backends else {
259
return false;
260
};
261
let settings = render_creation.clone();
262
263
let async_renderer = async move {
264
let render_resources = renderer::initialize_renderer(
265
backends,
266
primary_window,
267
&settings,
268
#[cfg(feature = "raw_vulkan_init")]
269
raw_vulkan_init_settings,
270
)
271
.await;
272
273
*future_resources.lock().unwrap() = Some(render_resources);
274
};
275
276
// In wasm, spawn a task and detach it for execution
277
#[cfg(target_arch = "wasm32")]
278
bevy_tasks::IoTaskPool::get()
279
.spawn_local(async_renderer)
280
.detach();
281
// Otherwise, just block for it to complete
282
#[cfg(not(target_arch = "wasm32"))]
283
bevy_tasks::block_on(async_renderer);
284
}
285
}
286
true
287
}
288
}
289
290
impl From<RenderResources> for RenderCreation {
291
fn from(value: RenderResources) -> Self {
292
Self::Manual(value)
293
}
294
}
295
296
impl Default for RenderCreation {
297
fn default() -> Self {
298
Self::Automatic(Default::default())
299
}
300
}
301
302
impl From<WgpuSettings> for RenderCreation {
303
fn from(value: WgpuSettings) -> Self {
304
Self::Automatic(Box::new(value))
305
}
306
}
307
308
/// Get a features/limits priority from the environment variable `WGPU_SETTINGS_PRIO`
309
pub fn settings_priority_from_env() -> Option<WgpuSettingsPriority> {
310
Some(
311
match std::env::var("WGPU_SETTINGS_PRIO")
312
.as_deref()
313
.map(str::to_lowercase)
314
.as_deref()
315
{
316
Ok("compatibility") => WgpuSettingsPriority::Compatibility,
317
Ok("functionality") => WgpuSettingsPriority::Functionality,
318
Ok("webgl2") => WgpuSettingsPriority::WebGL2,
319
_ => return None,
320
},
321
)
322
}
323
324