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
6596 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::SPIRV_SHADER_PASSTHROUGH) =>
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::SpirV(
72
wgpu::ShaderModuleDescriptorSpirV {
73
label: desc.label,
74
source: source.clone(),
75
},
76
),
77
)
78
}
79
}
80
// SAFETY:
81
//
82
// This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior.
83
// No attempt is made to ensure that data is valid SPIR-V.
84
_ => unsafe {
85
self.device
86
.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
87
},
88
}
89
#[cfg(not(feature = "spirv_shader_passthrough"))]
90
// SAFETY: the caller is responsible for upholding the safety requirements
91
unsafe {
92
self.device
93
.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
94
}
95
}
96
97
/// Creates and validates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.
98
///
99
/// See [`ValidateShader`](bevy_shader::ValidateShader) for more information on the tradeoffs involved with shader validation.
100
#[inline]
101
pub fn create_and_validate_shader_module(
102
&self,
103
desc: wgpu::ShaderModuleDescriptor,
104
) -> wgpu::ShaderModule {
105
#[cfg(feature = "spirv_shader_passthrough")]
106
match &desc.source {
107
wgpu::ShaderSource::SpirV(_source) => panic!("no safety checks are performed for spirv shaders. use `create_shader_module` instead"),
108
_ => self.device.create_shader_module(desc),
109
}
110
#[cfg(not(feature = "spirv_shader_passthrough"))]
111
self.device.create_shader_module(desc)
112
}
113
114
/// Check for resource cleanups and mapping callbacks.
115
///
116
/// Return `true` if the queue is empty, or `false` if there are more queue
117
/// submissions still in flight. (Note that, unless access to the [`wgpu::Queue`] is
118
/// coordinated somehow, this information could be out of date by the time
119
/// the caller receives it. `Queue`s can be shared between threads, so
120
/// other threads could submit new work at any time.)
121
///
122
/// no-op on the web, device is automatically polled.
123
#[inline]
124
pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {
125
self.device.poll(maintain)
126
}
127
128
/// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder).
129
#[inline]
130
pub fn create_command_encoder(
131
&self,
132
desc: &wgpu::CommandEncoderDescriptor,
133
) -> wgpu::CommandEncoder {
134
self.device.create_command_encoder(desc)
135
}
136
137
/// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder).
138
#[inline]
139
pub fn create_render_bundle_encoder(
140
&self,
141
desc: &wgpu::RenderBundleEncoderDescriptor,
142
) -> wgpu::RenderBundleEncoder<'_> {
143
self.device.create_render_bundle_encoder(desc)
144
}
145
146
/// Creates a new [`BindGroup`](wgpu::BindGroup).
147
#[inline]
148
pub fn create_bind_group<'a>(
149
&self,
150
label: impl Into<wgpu::Label<'a>>,
151
layout: &'a BindGroupLayout,
152
entries: &'a [BindGroupEntry<'a>],
153
) -> BindGroup {
154
let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
155
label: label.into(),
156
layout,
157
entries,
158
});
159
BindGroup::from(wgpu_bind_group)
160
}
161
162
/// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).
163
#[inline]
164
pub fn create_bind_group_layout<'a>(
165
&self,
166
label: impl Into<wgpu::Label<'a>>,
167
entries: &'a [BindGroupLayoutEntry],
168
) -> BindGroupLayout {
169
BindGroupLayout::from(
170
self.device
171
.create_bind_group_layout(&BindGroupLayoutDescriptor {
172
label: label.into(),
173
entries,
174
}),
175
)
176
}
177
178
/// Creates a [`PipelineLayout`](wgpu::PipelineLayout).
179
#[inline]
180
pub fn create_pipeline_layout(
181
&self,
182
desc: &wgpu::PipelineLayoutDescriptor,
183
) -> wgpu::PipelineLayout {
184
self.device.create_pipeline_layout(desc)
185
}
186
187
/// Creates a [`RenderPipeline`].
188
#[inline]
189
pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {
190
let wgpu_render_pipeline = self.device.create_render_pipeline(desc);
191
RenderPipeline::from(wgpu_render_pipeline)
192
}
193
194
/// Creates a [`ComputePipeline`].
195
#[inline]
196
pub fn create_compute_pipeline(
197
&self,
198
desc: &wgpu::ComputePipelineDescriptor,
199
) -> ComputePipeline {
200
let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);
201
ComputePipeline::from(wgpu_compute_pipeline)
202
}
203
204
/// Creates a [`Buffer`].
205
pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {
206
let wgpu_buffer = self.device.create_buffer(desc);
207
Buffer::from(wgpu_buffer)
208
}
209
210
/// Creates a [`Buffer`] and initializes it with the specified data.
211
pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
212
let wgpu_buffer = self.device.create_buffer_init(desc);
213
Buffer::from(wgpu_buffer)
214
}
215
216
/// Creates a new [`Texture`] and initializes it with the specified data.
217
///
218
/// `desc` specifies the general format of the texture.
219
/// `data` is the raw data.
220
pub fn create_texture_with_data(
221
&self,
222
render_queue: &RenderQueue,
223
desc: &wgpu::TextureDescriptor,
224
order: wgpu::util::TextureDataOrder,
225
data: &[u8],
226
) -> Texture {
227
let wgpu_texture =
228
self.device
229
.create_texture_with_data(render_queue.as_ref(), desc, order, data);
230
Texture::from(wgpu_texture)
231
}
232
233
/// Creates a new [`Texture`].
234
///
235
/// `desc` specifies the general format of the texture.
236
pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {
237
let wgpu_texture = self.device.create_texture(desc);
238
Texture::from(wgpu_texture)
239
}
240
241
/// Creates a new [`Sampler`].
242
///
243
/// `desc` specifies the behavior of the sampler.
244
pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {
245
let wgpu_sampler = self.device.create_sampler(desc);
246
Sampler::from(wgpu_sampler)
247
}
248
249
/// Initializes [`Surface`](wgpu::Surface) for presentation.
250
///
251
/// # Panics
252
///
253
/// - A old [`SurfaceTexture`](wgpu::SurfaceTexture) is still alive referencing an old surface.
254
/// - Texture format requested is unsupported on the surface.
255
pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
256
surface.configure(&self.device, config);
257
}
258
259
/// Returns the wgpu [`Device`](wgpu::Device).
260
pub fn wgpu_device(&self) -> &wgpu::Device {
261
&self.device
262
}
263
264
pub fn map_buffer(
265
&self,
266
buffer: &wgpu::BufferSlice,
267
map_mode: wgpu::MapMode,
268
callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,
269
) {
270
buffer.map_async(map_mode, callback);
271
}
272
273
// Rounds up `row_bytes` to be a multiple of [`wgpu::COPY_BYTES_PER_ROW_ALIGNMENT`].
274
pub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize {
275
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
276
277
// If row_bytes is aligned calculate a value just under the next aligned value.
278
// Otherwise calculate a value greater than the next aligned value.
279
let over_aligned = row_bytes + align - 1;
280
281
// Round the number *down* to the nearest aligned value.
282
(over_aligned / align) * align
283
}
284
285
pub fn get_supported_read_only_binding_type(
286
&self,
287
buffers_per_shader_stage: u32,
288
) -> BufferBindingType {
289
if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {
290
BufferBindingType::Storage { read_only: true }
291
} else {
292
BufferBindingType::Uniform
293
}
294
}
295
}
296
297
#[cfg(test)]
298
mod tests {
299
use super::*;
300
301
#[test]
302
fn align_copy_bytes_per_row() {
303
// Test for https://github.com/bevyengine/bevy/issues/16992
304
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
305
306
assert_eq!(RenderDevice::align_copy_bytes_per_row(0), 0);
307
assert_eq!(RenderDevice::align_copy_bytes_per_row(1), align);
308
assert_eq!(RenderDevice::align_copy_bytes_per_row(align + 1), align * 2);
309
assert_eq!(RenderDevice::align_copy_bytes_per_row(align), align);
310
}
311
}
312
313