Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/renderer/render_device.rs
9331 views
1
use super::RenderQueue;
2
use crate::render_resource::{
3
BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
4
RenderPipeline, Sampler, Texture,
5
};
6
use crate::renderer::WgpuWrapper;
7
use bevy_ecs::resource::Resource;
8
use wgpu::{
9
util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
10
BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, PollError, PollStatus,
11
};
12
13
/// This GPU device is responsible for the creation of most rendering and compute resources.
14
#[derive(Resource, Clone)]
15
pub struct RenderDevice {
16
device: WgpuWrapper<wgpu::Device>,
17
}
18
19
impl From<wgpu::Device> for RenderDevice {
20
fn from(device: wgpu::Device) -> Self {
21
Self::new(WgpuWrapper::new(device))
22
}
23
}
24
25
impl RenderDevice {
26
pub fn new(device: WgpuWrapper<wgpu::Device>) -> Self {
27
Self { device }
28
}
29
30
/// List all [`Features`](wgpu::Features) that may be used with this device.
31
///
32
/// Functions may panic if you use unsupported features.
33
#[inline]
34
pub fn features(&self) -> wgpu::Features {
35
self.device.features()
36
}
37
38
/// List all [`Limits`](wgpu::Limits) that were requested of this device.
39
///
40
/// If any of these limits are exceeded, functions may panic.
41
#[inline]
42
pub fn limits(&self) -> wgpu::Limits {
43
self.device.limits()
44
}
45
46
/// Creates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.
47
///
48
/// # Safety
49
///
50
/// Creates a shader module with user-customizable runtime checks which allows shaders to
51
/// perform operations which can lead to undefined behavior like indexing out of bounds,
52
/// To avoid UB, ensure any unchecked shaders are sound!
53
/// This method should never be called for user-supplied shaders.
54
#[inline]
55
pub unsafe fn create_shader_module(
56
&self,
57
desc: wgpu::ShaderModuleDescriptor,
58
) -> wgpu::ShaderModule {
59
#[cfg(feature = "spirv_shader_passthrough")]
60
match &desc.source {
61
wgpu::ShaderSource::SpirV(source)
62
if self
63
.features()
64
.contains(wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS) =>
65
{
66
// SAFETY:
67
// This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior.
68
// No attempt is made to ensure that data is valid SPIR-V.
69
unsafe {
70
self.device.create_shader_module_passthrough(
71
wgpu::ShaderModuleDescriptorPassthrough {
72
label: desc.label,
73
spirv: Some(source.clone()),
74
..Default::default()
75
},
76
)
77
}
78
}
79
// SAFETY:
80
//
81
// This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior.
82
// No attempt is made to ensure that data is valid SPIR-V.
83
_ => unsafe {
84
self.device
85
.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
86
},
87
}
88
#[cfg(not(feature = "spirv_shader_passthrough"))]
89
// SAFETY: the caller is responsible for upholding the safety requirements
90
unsafe {
91
self.device
92
.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
93
}
94
}
95
96
/// Creates and validates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.
97
///
98
/// See [`ValidateShader`](bevy_shader::ValidateShader) for more information on the tradeoffs involved with shader validation.
99
#[inline]
100
pub fn create_and_validate_shader_module(
101
&self,
102
desc: wgpu::ShaderModuleDescriptor,
103
) -> wgpu::ShaderModule {
104
#[cfg(feature = "spirv_shader_passthrough")]
105
match &desc.source {
106
wgpu::ShaderSource::SpirV(_source) => panic!("no safety checks are performed for spirv shaders. use `create_shader_module` instead"),
107
_ => self.device.create_shader_module(desc),
108
}
109
#[cfg(not(feature = "spirv_shader_passthrough"))]
110
self.device.create_shader_module(desc)
111
}
112
113
/// Check for resource cleanups and mapping callbacks.
114
///
115
/// Return `true` if the queue is empty, or `false` if there are more queue
116
/// submissions still in flight. (Note that, unless access to the [`wgpu::Queue`] is
117
/// coordinated somehow, this information could be out of date by the time
118
/// the caller receives it. `Queue`s can be shared between threads, so
119
/// other threads could submit new work at any time.)
120
///
121
/// no-op on the web, device is automatically polled.
122
#[inline]
123
pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {
124
self.device.poll(maintain)
125
}
126
127
/// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder).
128
#[inline]
129
pub fn create_command_encoder(
130
&self,
131
desc: &wgpu::CommandEncoderDescriptor,
132
) -> wgpu::CommandEncoder {
133
self.device.create_command_encoder(desc)
134
}
135
136
/// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder).
137
#[inline]
138
pub fn create_render_bundle_encoder(
139
&self,
140
desc: &wgpu::RenderBundleEncoderDescriptor,
141
) -> wgpu::RenderBundleEncoder<'_> {
142
self.device.create_render_bundle_encoder(desc)
143
}
144
145
/// Creates a new [`BindGroup`](wgpu::BindGroup).
146
#[inline]
147
pub fn create_bind_group<'a>(
148
&self,
149
label: impl Into<wgpu::Label<'a>>,
150
layout: &'a BindGroupLayout,
151
entries: &'a [BindGroupEntry<'a>],
152
) -> BindGroup {
153
let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
154
label: label.into(),
155
layout,
156
entries,
157
});
158
BindGroup::from(wgpu_bind_group)
159
}
160
161
/// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).
162
#[inline]
163
pub fn create_bind_group_layout<'a>(
164
&self,
165
label: impl Into<wgpu::Label<'a>>,
166
entries: &'a [BindGroupLayoutEntry],
167
) -> BindGroupLayout {
168
BindGroupLayout::from(
169
self.device
170
.create_bind_group_layout(&BindGroupLayoutDescriptor {
171
label: label.into(),
172
entries,
173
}),
174
)
175
}
176
177
/// Creates a [`PipelineLayout`](wgpu::PipelineLayout).
178
#[inline]
179
pub fn create_pipeline_layout(
180
&self,
181
desc: &wgpu::PipelineLayoutDescriptor,
182
) -> wgpu::PipelineLayout {
183
self.device.create_pipeline_layout(desc)
184
}
185
186
/// Creates a [`RenderPipeline`].
187
#[inline]
188
pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {
189
let wgpu_render_pipeline = self.device.create_render_pipeline(desc);
190
RenderPipeline::from(wgpu_render_pipeline)
191
}
192
193
/// Creates a [`ComputePipeline`].
194
#[inline]
195
pub fn create_compute_pipeline(
196
&self,
197
desc: &wgpu::ComputePipelineDescriptor,
198
) -> ComputePipeline {
199
let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);
200
ComputePipeline::from(wgpu_compute_pipeline)
201
}
202
203
/// Creates a [`Buffer`].
204
pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {
205
let wgpu_buffer = self.device.create_buffer(desc);
206
Buffer::from(wgpu_buffer)
207
}
208
209
/// Creates a [`Buffer`] and initializes it with the specified data.
210
pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
211
let wgpu_buffer = self.device.create_buffer_init(desc);
212
Buffer::from(wgpu_buffer)
213
}
214
215
/// Creates a new [`Texture`] and initializes it with the specified data.
216
///
217
/// `desc` specifies the general format of the texture.
218
/// `data` is the raw data.
219
pub fn create_texture_with_data(
220
&self,
221
render_queue: &RenderQueue,
222
desc: &wgpu::TextureDescriptor,
223
order: wgpu::util::TextureDataOrder,
224
data: &[u8],
225
) -> Texture {
226
let wgpu_texture =
227
self.device
228
.create_texture_with_data(render_queue.as_ref(), desc, order, data);
229
Texture::from(wgpu_texture)
230
}
231
232
/// Creates a new [`Texture`].
233
///
234
/// `desc` specifies the general format of the texture.
235
pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {
236
let wgpu_texture = self.device.create_texture(desc);
237
Texture::from(wgpu_texture)
238
}
239
240
/// Creates a new [`Sampler`].
241
///
242
/// `desc` specifies the behavior of the sampler.
243
pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {
244
let wgpu_sampler = self.device.create_sampler(desc);
245
Sampler::from(wgpu_sampler)
246
}
247
248
/// Initializes [`Surface`](wgpu::Surface) for presentation.
249
///
250
/// # Panics
251
///
252
/// - A old [`SurfaceTexture`](wgpu::SurfaceTexture) is still alive referencing an old surface.
253
/// - Texture format requested is unsupported on the surface.
254
pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
255
surface.configure(&self.device, config);
256
}
257
258
/// Returns the wgpu [`Device`](wgpu::Device).
259
pub fn wgpu_device(&self) -> &wgpu::Device {
260
&self.device
261
}
262
263
pub fn map_buffer(
264
&self,
265
buffer: &wgpu::BufferSlice,
266
map_mode: wgpu::MapMode,
267
callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,
268
) {
269
buffer.map_async(map_mode, callback);
270
}
271
272
// Rounds up `row_bytes` to be a multiple of [`wgpu::COPY_BYTES_PER_ROW_ALIGNMENT`].
273
pub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize {
274
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
275
276
// If row_bytes is aligned calculate a value just under the next aligned value.
277
// Otherwise calculate a value greater than the next aligned value.
278
let over_aligned = row_bytes + align - 1;
279
280
// Round the number *down* to the nearest aligned value.
281
(over_aligned / align) * align
282
}
283
284
pub fn get_supported_read_only_binding_type(
285
&self,
286
buffers_per_shader_stage: u32,
287
) -> BufferBindingType {
288
if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {
289
BufferBindingType::Storage { read_only: true }
290
} else {
291
BufferBindingType::Uniform
292
}
293
}
294
}
295
296
#[cfg(test)]
297
mod tests {
298
use super::*;
299
300
#[test]
301
fn align_copy_bytes_per_row() {
302
// Test for https://github.com/bevyengine/bevy/issues/16992
303
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
304
305
assert_eq!(RenderDevice::align_copy_bytes_per_row(0), 0);
306
assert_eq!(RenderDevice::align_copy_bytes_per_row(1), align);
307
assert_eq!(RenderDevice::align_copy_bytes_per_row(align + 1), align * 2);
308
assert_eq!(RenderDevice::align_copy_bytes_per_row(align), align);
309
}
310
}
311
312