bevy_render/renderer/
render_device.rs1use super::RenderQueue;
2use crate::render_resource::{
3 BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
4 RenderPipeline, Sampler, Texture,
5};
6use crate::renderer::WgpuWrapper;
7use bevy_ecs::resource::Resource;
8use wgpu::{
9 util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
10 BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, PollError, PollStatus,
11};
12
13#[derive(Resource, Clone)]
15pub struct RenderDevice {
16 device: WgpuWrapper<wgpu::Device>,
17}
18
19impl From<wgpu::Device> for RenderDevice {
20 fn from(device: wgpu::Device) -> Self {
21 Self::new(WgpuWrapper::new(device))
22 }
23}
24
25impl RenderDevice {
26 pub fn new(device: WgpuWrapper<wgpu::Device>) -> Self {
27 Self { device }
28 }
29
30 #[inline]
34 pub fn features(&self) -> wgpu::Features {
35 self.device.features()
36 }
37
38 #[inline]
42 pub fn limits(&self) -> wgpu::Limits {
43 self.device.limits()
44 }
45
46 #[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 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 _ => unsafe {
84 self.device
85 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
86 },
87 }
88 #[cfg(not(feature = "spirv_shader_passthrough"))]
89 unsafe {
91 self.device
92 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
93 }
94 }
95
96 #[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 #[inline]
123 pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {
124 self.device.poll(maintain)
125 }
126
127 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 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 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 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 pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
255 surface.configure(&self.device, config);
256 }
257
258 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 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 let over_aligned = row_bytes + align - 1;
279
280 (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)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn align_copy_bytes_per_row() {
302 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}