Path: blob/main/crates/bevy_render/src/renderer/render_device.rs
9331 views
use super::RenderQueue;1use crate::render_resource::{2BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,3RenderPipeline, Sampler, Texture,4};5use crate::renderer::WgpuWrapper;6use bevy_ecs::resource::Resource;7use wgpu::{8util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,9BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, PollError, PollStatus,10};1112/// This GPU device is responsible for the creation of most rendering and compute resources.13#[derive(Resource, Clone)]14pub struct RenderDevice {15device: WgpuWrapper<wgpu::Device>,16}1718impl From<wgpu::Device> for RenderDevice {19fn from(device: wgpu::Device) -> Self {20Self::new(WgpuWrapper::new(device))21}22}2324impl RenderDevice {25pub fn new(device: WgpuWrapper<wgpu::Device>) -> Self {26Self { device }27}2829/// List all [`Features`](wgpu::Features) that may be used with this device.30///31/// Functions may panic if you use unsupported features.32#[inline]33pub fn features(&self) -> wgpu::Features {34self.device.features()35}3637/// List all [`Limits`](wgpu::Limits) that were requested of this device.38///39/// If any of these limits are exceeded, functions may panic.40#[inline]41pub fn limits(&self) -> wgpu::Limits {42self.device.limits()43}4445/// Creates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.46///47/// # Safety48///49/// Creates a shader module with user-customizable runtime checks which allows shaders to50/// perform operations which can lead to undefined behavior like indexing out of bounds,51/// To avoid UB, ensure any unchecked shaders are sound!52/// This method should never be called for user-supplied shaders.53#[inline]54pub unsafe fn create_shader_module(55&self,56desc: wgpu::ShaderModuleDescriptor,57) -> wgpu::ShaderModule {58#[cfg(feature = "spirv_shader_passthrough")]59match &desc.source {60wgpu::ShaderSource::SpirV(source)61if self62.features()63.contains(wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS) =>64{65// SAFETY:66// This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior.67// No attempt is made to ensure that data is valid SPIR-V.68unsafe {69self.device.create_shader_module_passthrough(70wgpu::ShaderModuleDescriptorPassthrough {71label: desc.label,72spirv: Some(source.clone()),73..Default::default()74},75)76}77}78// SAFETY:79//80// This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior.81// No attempt is made to ensure that data is valid SPIR-V.82_ => unsafe {83self.device84.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())85},86}87#[cfg(not(feature = "spirv_shader_passthrough"))]88// SAFETY: the caller is responsible for upholding the safety requirements89unsafe {90self.device91.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())92}93}9495/// Creates and validates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.96///97/// See [`ValidateShader`](bevy_shader::ValidateShader) for more information on the tradeoffs involved with shader validation.98#[inline]99pub fn create_and_validate_shader_module(100&self,101desc: wgpu::ShaderModuleDescriptor,102) -> wgpu::ShaderModule {103#[cfg(feature = "spirv_shader_passthrough")]104match &desc.source {105wgpu::ShaderSource::SpirV(_source) => panic!("no safety checks are performed for spirv shaders. use `create_shader_module` instead"),106_ => self.device.create_shader_module(desc),107}108#[cfg(not(feature = "spirv_shader_passthrough"))]109self.device.create_shader_module(desc)110}111112/// Check for resource cleanups and mapping callbacks.113///114/// Return `true` if the queue is empty, or `false` if there are more queue115/// submissions still in flight. (Note that, unless access to the [`wgpu::Queue`] is116/// coordinated somehow, this information could be out of date by the time117/// the caller receives it. `Queue`s can be shared between threads, so118/// other threads could submit new work at any time.)119///120/// no-op on the web, device is automatically polled.121#[inline]122pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {123self.device.poll(maintain)124}125126/// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder).127#[inline]128pub fn create_command_encoder(129&self,130desc: &wgpu::CommandEncoderDescriptor,131) -> wgpu::CommandEncoder {132self.device.create_command_encoder(desc)133}134135/// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder).136#[inline]137pub fn create_render_bundle_encoder(138&self,139desc: &wgpu::RenderBundleEncoderDescriptor,140) -> wgpu::RenderBundleEncoder<'_> {141self.device.create_render_bundle_encoder(desc)142}143144/// Creates a new [`BindGroup`](wgpu::BindGroup).145#[inline]146pub fn create_bind_group<'a>(147&self,148label: impl Into<wgpu::Label<'a>>,149layout: &'a BindGroupLayout,150entries: &'a [BindGroupEntry<'a>],151) -> BindGroup {152let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {153label: label.into(),154layout,155entries,156});157BindGroup::from(wgpu_bind_group)158}159160/// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).161#[inline]162pub fn create_bind_group_layout<'a>(163&self,164label: impl Into<wgpu::Label<'a>>,165entries: &'a [BindGroupLayoutEntry],166) -> BindGroupLayout {167BindGroupLayout::from(168self.device169.create_bind_group_layout(&BindGroupLayoutDescriptor {170label: label.into(),171entries,172}),173)174}175176/// Creates a [`PipelineLayout`](wgpu::PipelineLayout).177#[inline]178pub fn create_pipeline_layout(179&self,180desc: &wgpu::PipelineLayoutDescriptor,181) -> wgpu::PipelineLayout {182self.device.create_pipeline_layout(desc)183}184185/// Creates a [`RenderPipeline`].186#[inline]187pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {188let wgpu_render_pipeline = self.device.create_render_pipeline(desc);189RenderPipeline::from(wgpu_render_pipeline)190}191192/// Creates a [`ComputePipeline`].193#[inline]194pub fn create_compute_pipeline(195&self,196desc: &wgpu::ComputePipelineDescriptor,197) -> ComputePipeline {198let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);199ComputePipeline::from(wgpu_compute_pipeline)200}201202/// Creates a [`Buffer`].203pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {204let wgpu_buffer = self.device.create_buffer(desc);205Buffer::from(wgpu_buffer)206}207208/// Creates a [`Buffer`] and initializes it with the specified data.209pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {210let wgpu_buffer = self.device.create_buffer_init(desc);211Buffer::from(wgpu_buffer)212}213214/// Creates a new [`Texture`] and initializes it with the specified data.215///216/// `desc` specifies the general format of the texture.217/// `data` is the raw data.218pub fn create_texture_with_data(219&self,220render_queue: &RenderQueue,221desc: &wgpu::TextureDescriptor,222order: wgpu::util::TextureDataOrder,223data: &[u8],224) -> Texture {225let wgpu_texture =226self.device227.create_texture_with_data(render_queue.as_ref(), desc, order, data);228Texture::from(wgpu_texture)229}230231/// Creates a new [`Texture`].232///233/// `desc` specifies the general format of the texture.234pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {235let wgpu_texture = self.device.create_texture(desc);236Texture::from(wgpu_texture)237}238239/// Creates a new [`Sampler`].240///241/// `desc` specifies the behavior of the sampler.242pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {243let wgpu_sampler = self.device.create_sampler(desc);244Sampler::from(wgpu_sampler)245}246247/// Initializes [`Surface`](wgpu::Surface) for presentation.248///249/// # Panics250///251/// - A old [`SurfaceTexture`](wgpu::SurfaceTexture) is still alive referencing an old surface.252/// - Texture format requested is unsupported on the surface.253pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {254surface.configure(&self.device, config);255}256257/// Returns the wgpu [`Device`](wgpu::Device).258pub fn wgpu_device(&self) -> &wgpu::Device {259&self.device260}261262pub fn map_buffer(263&self,264buffer: &wgpu::BufferSlice,265map_mode: wgpu::MapMode,266callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,267) {268buffer.map_async(map_mode, callback);269}270271// Rounds up `row_bytes` to be a multiple of [`wgpu::COPY_BYTES_PER_ROW_ALIGNMENT`].272pub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize {273let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;274275// If row_bytes is aligned calculate a value just under the next aligned value.276// Otherwise calculate a value greater than the next aligned value.277let over_aligned = row_bytes + align - 1;278279// Round the number *down* to the nearest aligned value.280(over_aligned / align) * align281}282283pub fn get_supported_read_only_binding_type(284&self,285buffers_per_shader_stage: u32,286) -> BufferBindingType {287if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {288BufferBindingType::Storage { read_only: true }289} else {290BufferBindingType::Uniform291}292}293}294295#[cfg(test)]296mod tests {297use super::*;298299#[test]300fn align_copy_bytes_per_row() {301// Test for https://github.com/bevyengine/bevy/issues/16992302let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;303304assert_eq!(RenderDevice::align_copy_bytes_per_row(0), 0);305assert_eq!(RenderDevice::align_copy_bytes_per_row(1), align);306assert_eq!(RenderDevice::align_copy_bytes_per_row(align + 1), align * 2);307assert_eq!(RenderDevice::align_copy_bytes_per_row(align), align);308}309}310311312