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::WgpuWrapper;
7use bevy_ecs::resource::Resource;
8use wgpu::{
9 util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
10 BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, MaintainResult,
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
71 .create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
72 label: desc.label,
73 source: source.clone(),
74 })
75 }
76 }
77 _ => unsafe {
82 self.device
83 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
84 },
85 }
86 #[cfg(not(feature = "spirv_shader_passthrough"))]
87 unsafe {
89 self.device
90 .create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())
91 }
92 }
93
94 #[inline]
98 pub fn create_and_validate_shader_module(
99 &self,
100 desc: wgpu::ShaderModuleDescriptor,
101 ) -> wgpu::ShaderModule {
102 #[cfg(feature = "spirv_shader_passthrough")]
103 match &desc.source {
104 wgpu::ShaderSource::SpirV(_source) => panic!("no safety checks are performed for spirv shaders. use `create_shader_module` instead"),
105 _ => self.device.create_shader_module(desc),
106 }
107 #[cfg(not(feature = "spirv_shader_passthrough"))]
108 self.device.create_shader_module(desc)
109 }
110
111 #[inline]
121 pub fn poll(&self, maintain: wgpu::Maintain) -> MaintainResult {
122 self.device.poll(maintain)
123 }
124
125 #[inline]
127 pub fn create_command_encoder(
128 &self,
129 desc: &wgpu::CommandEncoderDescriptor,
130 ) -> wgpu::CommandEncoder {
131 self.device.create_command_encoder(desc)
132 }
133
134 #[inline]
136 pub fn create_render_bundle_encoder(
137 &self,
138 desc: &wgpu::RenderBundleEncoderDescriptor,
139 ) -> wgpu::RenderBundleEncoder {
140 self.device.create_render_bundle_encoder(desc)
141 }
142
143 #[inline]
145 pub fn create_bind_group<'a>(
146 &self,
147 label: impl Into<wgpu::Label<'a>>,
148 layout: &'a BindGroupLayout,
149 entries: &'a [BindGroupEntry<'a>],
150 ) -> BindGroup {
151 let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
152 label: label.into(),
153 layout,
154 entries,
155 });
156 BindGroup::from(wgpu_bind_group)
157 }
158
159 #[inline]
161 pub fn create_bind_group_layout<'a>(
162 &self,
163 label: impl Into<wgpu::Label<'a>>,
164 entries: &'a [BindGroupLayoutEntry],
165 ) -> BindGroupLayout {
166 BindGroupLayout::from(
167 self.device
168 .create_bind_group_layout(&BindGroupLayoutDescriptor {
169 label: label.into(),
170 entries,
171 }),
172 )
173 }
174
175 #[inline]
177 pub fn create_pipeline_layout(
178 &self,
179 desc: &wgpu::PipelineLayoutDescriptor,
180 ) -> wgpu::PipelineLayout {
181 self.device.create_pipeline_layout(desc)
182 }
183
184 #[inline]
186 pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {
187 let wgpu_render_pipeline = self.device.create_render_pipeline(desc);
188 RenderPipeline::from(wgpu_render_pipeline)
189 }
190
191 #[inline]
193 pub fn create_compute_pipeline(
194 &self,
195 desc: &wgpu::ComputePipelineDescriptor,
196 ) -> ComputePipeline {
197 let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);
198 ComputePipeline::from(wgpu_compute_pipeline)
199 }
200
201 pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {
203 let wgpu_buffer = self.device.create_buffer(desc);
204 Buffer::from(wgpu_buffer)
205 }
206
207 pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
209 let wgpu_buffer = self.device.create_buffer_init(desc);
210 Buffer::from(wgpu_buffer)
211 }
212
213 pub fn create_texture_with_data(
218 &self,
219 render_queue: &RenderQueue,
220 desc: &wgpu::TextureDescriptor,
221 order: wgpu::util::TextureDataOrder,
222 data: &[u8],
223 ) -> Texture {
224 let wgpu_texture =
225 self.device
226 .create_texture_with_data(render_queue.as_ref(), desc, order, data);
227 Texture::from(wgpu_texture)
228 }
229
230 pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {
234 let wgpu_texture = self.device.create_texture(desc);
235 Texture::from(wgpu_texture)
236 }
237
238 pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {
242 let wgpu_sampler = self.device.create_sampler(desc);
243 Sampler::from(wgpu_sampler)
244 }
245
246 pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
253 surface.configure(&self.device, config);
254 }
255
256 pub fn wgpu_device(&self) -> &wgpu::Device {
258 &self.device
259 }
260
261 pub fn map_buffer(
262 &self,
263 buffer: &wgpu::BufferSlice,
264 map_mode: wgpu::MapMode,
265 callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,
266 ) {
267 buffer.map_async(map_mode, callback);
268 }
269
270 pub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize {
272 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
273
274 let over_aligned = row_bytes + align - 1;
277
278 (over_aligned / align) * align
280 }
281
282 pub fn get_supported_read_only_binding_type(
283 &self,
284 buffers_per_shader_stage: u32,
285 ) -> BufferBindingType {
286 if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {
287 BufferBindingType::Storage { read_only: true }
288 } else {
289 BufferBindingType::Uniform
290 }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn align_copy_bytes_per_row() {
300 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
302
303 assert_eq!(RenderDevice::align_copy_bytes_per_row(0), 0);
304 assert_eq!(RenderDevice::align_copy_bytes_per_row(1), align);
305 assert_eq!(RenderDevice::align_copy_bytes_per_row(align + 1), align * 2);
306 assert_eq!(RenderDevice::align_copy_bytes_per_row(align), align);
307 }
308}