bevy_render/renderer/
render_device.rs1use super::RenderQueue;
2use crate::render_resource::{
3 BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawMeshPipelineDescriptor,
4 RawRenderPipelineDescriptor, RenderPipeline, Sampler, Texture,
5};
6use crate::renderer::wgpu_wrapper;
7use bevy_ecs::resource::Resource;
8use wgpu::{
9 util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
10 BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, PollError, PollStatus,
11};
12
13wgpu_wrapper! {
14 #[derive(Clone)]
15 struct WgpuDevice(wgpu::Device);
16}
17
18#[derive(Resource, Clone)]
20pub struct RenderDevice {
21 device: WgpuDevice,
22}
23
24impl From<wgpu::Device> for RenderDevice {
25 fn from(device: wgpu::Device) -> Self {
26 Self::new(device)
27 }
28}
29
30impl RenderDevice {
31 pub fn new(device: wgpu::Device) -> Self {
32 Self {
33 device: WgpuDevice::new(device),
34 }
35 }
36
37 #[inline]
41 pub fn features(&self) -> wgpu::Features {
42 self.device.features()
43 }
44
45 #[inline]
49 pub fn limits(&self) -> wgpu::Limits {
50 self.device.limits()
51 }
52
53 #[inline]
62 pub unsafe fn create_shader_module(
63 &self,
64 desc: wgpu::ShaderModuleDescriptor,
65 ) -> wgpu::ShaderModule {
66 #[cfg(feature = "spirv_shader_passthrough")]
67 match &desc.source {
68 wgpu::ShaderSource::SpirV(source)
69 if self
70 .features()
71 .contains(wgpu::Features::PASSTHROUGH_SHADERS) =>
72 {
73 unsafe {
77 self.device.create_shader_module_passthrough(
78 wgpu::ShaderModuleDescriptorPassthrough {
79 label: desc.label,
80 spirv: Some(source.clone()),
81 ..Default::default()
82 },
83 )
84 }
85 }
86 _ => unsafe {
91 self.device
92 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
93 },
94 }
95 #[cfg(not(feature = "spirv_shader_passthrough"))]
96 unsafe {
98 self.device
99 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
100 }
101 }
102
103 #[inline]
107 pub fn create_and_validate_shader_module(
108 &self,
109 desc: wgpu::ShaderModuleDescriptor,
110 ) -> wgpu::ShaderModule {
111 #[cfg(feature = "spirv_shader_passthrough")]
112 match &desc.source {
113 wgpu::ShaderSource::SpirV(_source) => panic!("no safety checks are performed for spirv shaders. use `create_shader_module` instead"),
114 _ => self.device.create_shader_module(desc),
115 }
116 #[cfg(not(feature = "spirv_shader_passthrough"))]
117 self.device.create_shader_module(desc)
118 }
119
120 #[inline]
130 pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {
131 self.device.poll(maintain)
132 }
133
134 #[inline]
136 pub fn create_command_encoder(
137 &self,
138 desc: &wgpu::CommandEncoderDescriptor,
139 ) -> wgpu::CommandEncoder {
140 self.device.create_command_encoder(desc)
141 }
142
143 #[inline]
145 pub fn create_render_bundle_encoder(
146 &self,
147 desc: &wgpu::RenderBundleEncoderDescriptor,
148 ) -> wgpu::RenderBundleEncoder<'_> {
149 self.device.create_render_bundle_encoder(desc)
150 }
151
152 #[inline]
154 pub fn create_bind_group<'a>(
155 &self,
156 label: impl Into<wgpu::Label<'a>>,
157 layout: &'a BindGroupLayout,
158 entries: &'a [BindGroupEntry<'a>],
159 ) -> BindGroup {
160 let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
161 label: label.into(),
162 layout,
163 entries,
164 });
165 BindGroup::from(wgpu_bind_group)
166 }
167
168 #[inline]
170 pub fn create_bind_group_layout<'a>(
171 &self,
172 label: impl Into<wgpu::Label<'a>>,
173 entries: &'a [BindGroupLayoutEntry],
174 ) -> BindGroupLayout {
175 BindGroupLayout::from(
176 self.device
177 .create_bind_group_layout(&BindGroupLayoutDescriptor {
178 label: label.into(),
179 entries,
180 }),
181 )
182 }
183
184 #[inline]
186 pub fn create_pipeline_layout(
187 &self,
188 desc: &wgpu::PipelineLayoutDescriptor,
189 ) -> wgpu::PipelineLayout {
190 self.device.create_pipeline_layout(desc)
191 }
192
193 #[inline]
195 pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {
196 let wgpu_render_pipeline = self.device.create_render_pipeline(desc);
197 RenderPipeline::from(wgpu_render_pipeline)
198 }
199
200 #[inline]
202 pub fn create_compute_pipeline(
203 &self,
204 desc: &wgpu::ComputePipelineDescriptor,
205 ) -> ComputePipeline {
206 let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);
207 ComputePipeline::from(wgpu_compute_pipeline)
208 }
209
210 #[inline]
212 pub fn create_mesh_pipeline(&self, desc: &RawMeshPipelineDescriptor) -> RenderPipeline {
213 let wgpu_mesh_pipeline = self.device.create_mesh_pipeline(desc);
214 RenderPipeline::from(wgpu_mesh_pipeline)
215 }
216
217 pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {
219 let wgpu_buffer = self.device.create_buffer(desc);
220 Buffer::from(wgpu_buffer)
221 }
222
223 pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
225 let wgpu_buffer = self.device.create_buffer_init(desc);
226 Buffer::from(wgpu_buffer)
227 }
228
229 pub fn create_texture_with_data(
234 &self,
235 render_queue: &RenderQueue,
236 desc: &wgpu::TextureDescriptor,
237 order: wgpu::util::TextureDataOrder,
238 data: &[u8],
239 ) -> Texture {
240 let wgpu_texture = self
241 .device
242 .create_texture_with_data(render_queue, desc, order, data);
243 Texture::from(wgpu_texture)
244 }
245
246 pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {
250 let wgpu_texture = self.device.create_texture(desc);
251 Texture::from(wgpu_texture)
252 }
253
254 pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {
258 let wgpu_sampler = self.device.create_sampler(desc);
259 Sampler::from(wgpu_sampler)
260 }
261
262 pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
269 surface.configure(&self.device, config);
270 }
271
272 pub fn wgpu_device(&self) -> &wgpu::Device {
274 &self.device
275 }
276
277 pub fn map_buffer(
278 &self,
279 buffer: &wgpu::BufferSlice,
280 map_mode: wgpu::MapMode,
281 callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,
282 ) {
283 buffer.map_async(map_mode, callback);
284 }
285
286 pub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize {
288 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
289
290 let over_aligned = row_bytes + align - 1;
293
294 (over_aligned / align) * align
296 }
297
298 pub fn get_supported_read_only_binding_type(
299 &self,
300 buffers_per_shader_stage: u32,
301 ) -> BufferBindingType {
302 if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {
303 BufferBindingType::Storage { read_only: true }
304 } else {
305 BufferBindingType::Uniform
306 }
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn align_copy_bytes_per_row() {
316 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
318
319 assert_eq!(RenderDevice::align_copy_bytes_per_row(0), 0);
320 assert_eq!(RenderDevice::align_copy_bytes_per_row(1), align);
321 assert_eq!(RenderDevice::align_copy_bytes_per_row(align + 1), align * 2);
322 assert_eq!(RenderDevice::align_copy_bytes_per_row(align), align);
323 }
324}