bevy_render/renderer/
render_device.rs

1use crate::render_resource::{
2    BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
3    RenderPipeline, Sampler, Texture,
4};
5use bevy_ecs::system::Resource;
6use wgpu::{
7    util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
8    BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, MaintainResult,
9};
10
11use super::RenderQueue;
12
13use crate::render_resource::resource_macros::*;
14use crate::WgpuWrapper;
15
16render_resource_wrapper!(ErasedRenderDevice, wgpu::Device);
17
18/// This GPU device is responsible for the creation of most rendering and compute resources.
19#[derive(Resource, Clone)]
20pub struct RenderDevice {
21    device: WgpuWrapper<ErasedRenderDevice>,
22}
23
24impl From<wgpu::Device> for RenderDevice {
25    fn from(device: wgpu::Device) -> Self {
26        Self {
27            device: WgpuWrapper::new(ErasedRenderDevice::new(device)),
28        }
29    }
30}
31
32impl RenderDevice {
33    /// List all [`Features`](wgpu::Features) that may be used with this device.
34    ///
35    /// Functions may panic if you use unsupported features.
36    #[inline]
37    pub fn features(&self) -> wgpu::Features {
38        self.device.features()
39    }
40
41    /// List all [`Limits`](wgpu::Limits) that were requested of this device.
42    ///
43    /// If any of these limits are exceeded, functions may panic.
44    #[inline]
45    pub fn limits(&self) -> wgpu::Limits {
46        self.device.limits()
47    }
48
49    /// Creates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.
50    #[inline]
51    pub fn create_shader_module(&self, desc: wgpu::ShaderModuleDescriptor) -> wgpu::ShaderModule {
52        self.device.create_shader_module(desc)
53    }
54
55    /// Check for resource cleanups and mapping callbacks.
56    ///
57    /// Return `true` if the queue is empty, or `false` if there are more queue
58    /// submissions still in flight. (Note that, unless access to the [`wgpu::Queue`] is
59    /// coordinated somehow, this information could be out of date by the time
60    /// the caller receives it. `Queue`s can be shared between threads, so
61    /// other threads could submit new work at any time.)
62    ///
63    /// no-op on the web, device is automatically polled.
64    #[inline]
65    pub fn poll(&self, maintain: wgpu::Maintain) -> MaintainResult {
66        self.device.poll(maintain)
67    }
68
69    /// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder).
70    #[inline]
71    pub fn create_command_encoder(
72        &self,
73        desc: &wgpu::CommandEncoderDescriptor,
74    ) -> wgpu::CommandEncoder {
75        self.device.create_command_encoder(desc)
76    }
77
78    /// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder).
79    #[inline]
80    pub fn create_render_bundle_encoder(
81        &self,
82        desc: &wgpu::RenderBundleEncoderDescriptor,
83    ) -> wgpu::RenderBundleEncoder {
84        self.device.create_render_bundle_encoder(desc)
85    }
86
87    /// Creates a new [`BindGroup`](wgpu::BindGroup).
88    #[inline]
89    pub fn create_bind_group<'a>(
90        &self,
91        label: impl Into<wgpu::Label<'a>>,
92        layout: &'a BindGroupLayout,
93        entries: &'a [BindGroupEntry<'a>],
94    ) -> BindGroup {
95        let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
96            label: label.into(),
97            layout,
98            entries,
99        });
100        BindGroup::from(wgpu_bind_group)
101    }
102
103    /// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).
104    #[inline]
105    pub fn create_bind_group_layout<'a>(
106        &self,
107        label: impl Into<wgpu::Label<'a>>,
108        entries: &'a [BindGroupLayoutEntry],
109    ) -> BindGroupLayout {
110        BindGroupLayout::from(
111            self.device
112                .create_bind_group_layout(&BindGroupLayoutDescriptor {
113                    label: label.into(),
114                    entries,
115                }),
116        )
117    }
118
119    /// Creates a [`PipelineLayout`](wgpu::PipelineLayout).
120    #[inline]
121    pub fn create_pipeline_layout(
122        &self,
123        desc: &wgpu::PipelineLayoutDescriptor,
124    ) -> wgpu::PipelineLayout {
125        self.device.create_pipeline_layout(desc)
126    }
127
128    /// Creates a [`RenderPipeline`].
129    #[inline]
130    pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {
131        let wgpu_render_pipeline = self.device.create_render_pipeline(desc);
132        RenderPipeline::from(wgpu_render_pipeline)
133    }
134
135    /// Creates a [`ComputePipeline`].
136    #[inline]
137    pub fn create_compute_pipeline(
138        &self,
139        desc: &wgpu::ComputePipelineDescriptor,
140    ) -> ComputePipeline {
141        let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);
142        ComputePipeline::from(wgpu_compute_pipeline)
143    }
144
145    /// Creates a [`Buffer`].
146    pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {
147        let wgpu_buffer = self.device.create_buffer(desc);
148        Buffer::from(wgpu_buffer)
149    }
150
151    /// Creates a [`Buffer`] and initializes it with the specified data.
152    pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
153        let wgpu_buffer = self.device.create_buffer_init(desc);
154        Buffer::from(wgpu_buffer)
155    }
156
157    /// Creates a new [`Texture`] and initializes it with the specified data.
158    ///
159    /// `desc` specifies the general format of the texture.
160    /// `data` is the raw data.
161    pub fn create_texture_with_data(
162        &self,
163        render_queue: &RenderQueue,
164        desc: &wgpu::TextureDescriptor,
165        order: wgpu::util::TextureDataOrder,
166        data: &[u8],
167    ) -> Texture {
168        let wgpu_texture =
169            self.device
170                .create_texture_with_data(render_queue.as_ref(), desc, order, data);
171        Texture::from(wgpu_texture)
172    }
173
174    /// Creates a new [`Texture`].
175    ///
176    /// `desc` specifies the general format of the texture.
177    pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {
178        let wgpu_texture = self.device.create_texture(desc);
179        Texture::from(wgpu_texture)
180    }
181
182    /// Creates a new [`Sampler`].
183    ///
184    /// `desc` specifies the behavior of the sampler.
185    pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {
186        let wgpu_sampler = self.device.create_sampler(desc);
187        Sampler::from(wgpu_sampler)
188    }
189
190    /// Initializes [`Surface`](wgpu::Surface) for presentation.
191    ///
192    /// # Panics
193    ///
194    /// - A old [`SurfaceTexture`](wgpu::SurfaceTexture) is still alive referencing an old surface.
195    /// - Texture format requested is unsupported on the surface.
196    pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
197        surface.configure(&self.device, config);
198    }
199
200    /// Returns the wgpu [`Device`](wgpu::Device).
201    pub fn wgpu_device(&self) -> &wgpu::Device {
202        &self.device
203    }
204
205    pub fn map_buffer(
206        &self,
207        buffer: &wgpu::BufferSlice,
208        map_mode: wgpu::MapMode,
209        callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,
210    ) {
211        buffer.map_async(map_mode, callback);
212    }
213
214    pub fn align_copy_bytes_per_row(row_bytes: usize) -> usize {
215        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
216        let padded_bytes_per_row_padding = (align - row_bytes % align) % align;
217        row_bytes + padded_bytes_per_row_padding
218    }
219
220    pub fn get_supported_read_only_binding_type(
221        &self,
222        buffers_per_shader_stage: u32,
223    ) -> BufferBindingType {
224        if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {
225            BufferBindingType::Storage { read_only: true }
226        } else {
227            BufferBindingType::Uniform
228        }
229    }
230}