Path: blob/main/crates/bevy_render/src/renderer/render_device.rs
6596 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::SPIRV_SHADER_PASSTHROUGH) =>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::SpirV(71wgpu::ShaderModuleDescriptorSpirV {72label: desc.label,73source: source.clone(),74},75),76)77}78}79// SAFETY:80//81// This call passes binary data to the backend as-is and can potentially result in a driver crash or bogus behavior.82// No attempt is made to ensure that data is valid SPIR-V.83_ => unsafe {84self.device85.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())86},87}88#[cfg(not(feature = "spirv_shader_passthrough"))]89// SAFETY: the caller is responsible for upholding the safety requirements90unsafe {91self.device92.create_shader_module_trusted(desc, wgpu::ShaderRuntimeChecks::unchecked())93}94}9596/// Creates and validates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.97///98/// See [`ValidateShader`](bevy_shader::ValidateShader) for more information on the tradeoffs involved with shader validation.99#[inline]100pub fn create_and_validate_shader_module(101&self,102desc: wgpu::ShaderModuleDescriptor,103) -> wgpu::ShaderModule {104#[cfg(feature = "spirv_shader_passthrough")]105match &desc.source {106wgpu::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"))]110self.device.create_shader_module(desc)111}112113/// Check for resource cleanups and mapping callbacks.114///115/// Return `true` if the queue is empty, or `false` if there are more queue116/// submissions still in flight. (Note that, unless access to the [`wgpu::Queue`] is117/// coordinated somehow, this information could be out of date by the time118/// the caller receives it. `Queue`s can be shared between threads, so119/// other threads could submit new work at any time.)120///121/// no-op on the web, device is automatically polled.122#[inline]123pub fn poll(&self, maintain: wgpu::PollType) -> Result<PollStatus, PollError> {124self.device.poll(maintain)125}126127/// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder).128#[inline]129pub fn create_command_encoder(130&self,131desc: &wgpu::CommandEncoderDescriptor,132) -> wgpu::CommandEncoder {133self.device.create_command_encoder(desc)134}135136/// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder).137#[inline]138pub fn create_render_bundle_encoder(139&self,140desc: &wgpu::RenderBundleEncoderDescriptor,141) -> wgpu::RenderBundleEncoder<'_> {142self.device.create_render_bundle_encoder(desc)143}144145/// Creates a new [`BindGroup`](wgpu::BindGroup).146#[inline]147pub fn create_bind_group<'a>(148&self,149label: impl Into<wgpu::Label<'a>>,150layout: &'a BindGroupLayout,151entries: &'a [BindGroupEntry<'a>],152) -> BindGroup {153let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {154label: label.into(),155layout,156entries,157});158BindGroup::from(wgpu_bind_group)159}160161/// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).162#[inline]163pub fn create_bind_group_layout<'a>(164&self,165label: impl Into<wgpu::Label<'a>>,166entries: &'a [BindGroupLayoutEntry],167) -> BindGroupLayout {168BindGroupLayout::from(169self.device170.create_bind_group_layout(&BindGroupLayoutDescriptor {171label: label.into(),172entries,173}),174)175}176177/// Creates a [`PipelineLayout`](wgpu::PipelineLayout).178#[inline]179pub fn create_pipeline_layout(180&self,181desc: &wgpu::PipelineLayoutDescriptor,182) -> wgpu::PipelineLayout {183self.device.create_pipeline_layout(desc)184}185186/// Creates a [`RenderPipeline`].187#[inline]188pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {189let wgpu_render_pipeline = self.device.create_render_pipeline(desc);190RenderPipeline::from(wgpu_render_pipeline)191}192193/// Creates a [`ComputePipeline`].194#[inline]195pub fn create_compute_pipeline(196&self,197desc: &wgpu::ComputePipelineDescriptor,198) -> ComputePipeline {199let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);200ComputePipeline::from(wgpu_compute_pipeline)201}202203/// Creates a [`Buffer`].204pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {205let wgpu_buffer = self.device.create_buffer(desc);206Buffer::from(wgpu_buffer)207}208209/// Creates a [`Buffer`] and initializes it with the specified data.210pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {211let wgpu_buffer = self.device.create_buffer_init(desc);212Buffer::from(wgpu_buffer)213}214215/// Creates a new [`Texture`] and initializes it with the specified data.216///217/// `desc` specifies the general format of the texture.218/// `data` is the raw data.219pub fn create_texture_with_data(220&self,221render_queue: &RenderQueue,222desc: &wgpu::TextureDescriptor,223order: wgpu::util::TextureDataOrder,224data: &[u8],225) -> Texture {226let wgpu_texture =227self.device228.create_texture_with_data(render_queue.as_ref(), desc, order, data);229Texture::from(wgpu_texture)230}231232/// Creates a new [`Texture`].233///234/// `desc` specifies the general format of the texture.235pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {236let wgpu_texture = self.device.create_texture(desc);237Texture::from(wgpu_texture)238}239240/// Creates a new [`Sampler`].241///242/// `desc` specifies the behavior of the sampler.243pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {244let wgpu_sampler = self.device.create_sampler(desc);245Sampler::from(wgpu_sampler)246}247248/// Initializes [`Surface`](wgpu::Surface) for presentation.249///250/// # Panics251///252/// - A old [`SurfaceTexture`](wgpu::SurfaceTexture) is still alive referencing an old surface.253/// - Texture format requested is unsupported on the surface.254pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {255surface.configure(&self.device, config);256}257258/// Returns the wgpu [`Device`](wgpu::Device).259pub fn wgpu_device(&self) -> &wgpu::Device {260&self.device261}262263pub fn map_buffer(264&self,265buffer: &wgpu::BufferSlice,266map_mode: wgpu::MapMode,267callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,268) {269buffer.map_async(map_mode, callback);270}271272// Rounds up `row_bytes` to be a multiple of [`wgpu::COPY_BYTES_PER_ROW_ALIGNMENT`].273pub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize {274let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;275276// If row_bytes is aligned calculate a value just under the next aligned value.277// Otherwise calculate a value greater than the next aligned value.278let over_aligned = row_bytes + align - 1;279280// Round the number *down* to the nearest aligned value.281(over_aligned / align) * align282}283284pub fn get_supported_read_only_binding_type(285&self,286buffers_per_shader_stage: u32,287) -> BufferBindingType {288if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {289BufferBindingType::Storage { read_only: true }290} else {291BufferBindingType::Uniform292}293}294}295296#[cfg(test)]297mod tests {298use super::*;299300#[test]301fn align_copy_bytes_per_row() {302// Test for https://github.com/bevyengine/bevy/issues/16992303let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;304305assert_eq!(RenderDevice::align_copy_bytes_per_row(0), 0);306assert_eq!(RenderDevice::align_copy_bytes_per_row(1), align);307assert_eq!(RenderDevice::align_copy_bytes_per_row(align + 1), align * 2);308assert_eq!(RenderDevice::align_copy_bytes_per_row(align), align);309}310}311312313