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::SPIRV_SHADER_PASSTHROUGH) =>
65 {
66 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 _ => unsafe {
85 self.device
86 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
87 },
88 }
89 #[cfg(not(feature = "spirv_shader_passthrough"))]
90 unsafe {
92 self.device
93 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
94 }
95 }
96
97 #[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 #[inline]
124 pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {
125 self.device.poll(maintain)
126 }
127
128 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 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 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 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 pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
256 surface.configure(&self.device, config);
257 }
258
259 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 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 let over_aligned = row_bytes + align - 1;
280
281 (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)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn align_copy_bytes_per_row() {
303 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}