bevy/crates/bevy_render/src/renderer/render_device.rs
ira 992681b59b Make Resource trait opt-in, requiring #[derive(Resource)] V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.

While ergonomic, this results in several drawbacks:

* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
 * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
   *ira: My commits are not as well organized :')*
 * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
 * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.

## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.

## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.

If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.

`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.


Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00

199 lines
6.4 KiB
Rust

use crate::render_resource::{
BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
RenderPipeline, Sampler, Texture,
};
use bevy_ecs::system::Resource;
use std::sync::Arc;
use wgpu::{util::DeviceExt, BufferAsyncError, BufferBindingType};
use super::RenderQueue;
/// This GPU device is responsible for the creation of most rendering and compute resources.
#[derive(Resource, Clone)]
pub struct RenderDevice {
device: Arc<wgpu::Device>,
}
impl From<Arc<wgpu::Device>> for RenderDevice {
fn from(device: Arc<wgpu::Device>) -> Self {
Self { device }
}
}
impl RenderDevice {
/// List all [`Features`](wgpu::Features) that may be used with this device.
///
/// Functions may panic if you use unsupported features.
#[inline]
pub fn features(&self) -> wgpu::Features {
self.device.features()
}
/// List all [`Limits`](wgpu::Limits) that were requested of this device.
///
/// If any of these limits are exceeded, functions may panic.
#[inline]
pub fn limits(&self) -> wgpu::Limits {
self.device.limits()
}
/// Creates a [`ShaderModule`](wgpu::ShaderModule) from either SPIR-V or WGSL source code.
#[inline]
pub fn create_shader_module(&self, desc: wgpu::ShaderModuleDescriptor) -> wgpu::ShaderModule {
self.device.create_shader_module(desc)
}
/// Check for resource cleanups and mapping callbacks.
///
/// no-op on the web, device is automatically polled.
#[inline]
pub fn poll(&self, maintain: wgpu::Maintain) {
self.device.poll(maintain);
}
/// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder).
#[inline]
pub fn create_command_encoder(
&self,
desc: &wgpu::CommandEncoderDescriptor,
) -> wgpu::CommandEncoder {
self.device.create_command_encoder(desc)
}
/// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder).
#[inline]
pub fn create_render_bundle_encoder(
&self,
desc: &wgpu::RenderBundleEncoderDescriptor,
) -> wgpu::RenderBundleEncoder {
self.device.create_render_bundle_encoder(desc)
}
/// Creates a new [`BindGroup`](wgpu::BindGroup).
#[inline]
pub fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor) -> BindGroup {
let wgpu_bind_group = self.device.create_bind_group(desc);
BindGroup::from(wgpu_bind_group)
}
/// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).
#[inline]
pub fn create_bind_group_layout(
&self,
desc: &wgpu::BindGroupLayoutDescriptor,
) -> BindGroupLayout {
BindGroupLayout::from(self.device.create_bind_group_layout(desc))
}
/// Creates a [`PipelineLayout`](wgpu::PipelineLayout).
#[inline]
pub fn create_pipeline_layout(
&self,
desc: &wgpu::PipelineLayoutDescriptor,
) -> wgpu::PipelineLayout {
self.device.create_pipeline_layout(desc)
}
/// Creates a [`RenderPipeline`].
#[inline]
pub fn create_render_pipeline(&self, desc: &RawRenderPipelineDescriptor) -> RenderPipeline {
let wgpu_render_pipeline = self.device.create_render_pipeline(desc);
RenderPipeline::from(wgpu_render_pipeline)
}
/// Creates a [`ComputePipeline`].
#[inline]
pub fn create_compute_pipeline(
&self,
desc: &wgpu::ComputePipelineDescriptor,
) -> ComputePipeline {
let wgpu_compute_pipeline = self.device.create_compute_pipeline(desc);
ComputePipeline::from(wgpu_compute_pipeline)
}
/// Creates a [`Buffer`].
pub fn create_buffer(&self, desc: &wgpu::BufferDescriptor) -> Buffer {
let wgpu_buffer = self.device.create_buffer(desc);
Buffer::from(wgpu_buffer)
}
/// Creates a [`Buffer`] and initializes it with the specified data.
pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
let wgpu_buffer = self.device.create_buffer_init(desc);
Buffer::from(wgpu_buffer)
}
/// Creates a new [`Texture`] and initializes it with the specified data.
///
/// `desc` specifies the general format of the texture.
/// `data` is the raw data.
pub fn create_texture_with_data(
&self,
render_queue: &RenderQueue,
desc: &wgpu::TextureDescriptor,
data: &[u8],
) -> Texture {
let wgpu_texture = self
.device
.create_texture_with_data(render_queue.as_ref(), desc, data);
Texture::from(wgpu_texture)
}
/// Creates a new [`Texture`].
///
/// `desc` specifies the general format of the texture.
pub fn create_texture(&self, desc: &wgpu::TextureDescriptor) -> Texture {
let wgpu_texture = self.device.create_texture(desc);
Texture::from(wgpu_texture)
}
/// Creates a new [`Sampler`].
///
/// `desc` specifies the behavior of the sampler.
pub fn create_sampler(&self, desc: &wgpu::SamplerDescriptor) -> Sampler {
let wgpu_sampler = self.device.create_sampler(desc);
Sampler::from(wgpu_sampler)
}
/// Initializes [`Surface`](wgpu::Surface) for presentation.
///
/// # Panics
///
/// - A old [`SurfaceTexture`](wgpu::SurfaceTexture) is still alive referencing an old surface.
/// - Texture format requested is unsupported on the surface.
pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
surface.configure(&self.device, config);
}
/// Returns the wgpu [`Device`](wgpu::Device).
pub fn wgpu_device(&self) -> &wgpu::Device {
&self.device
}
pub fn map_buffer(
&self,
buffer: &wgpu::BufferSlice,
map_mode: wgpu::MapMode,
callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static,
) {
buffer.map_async(map_mode, callback);
}
pub fn align_copy_bytes_per_row(row_bytes: usize) -> usize {
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
let padded_bytes_per_row_padding = (align - row_bytes % align) % align;
row_bytes + padded_bytes_per_row_padding
}
pub fn get_supported_read_only_binding_type(
&self,
buffers_per_shader_stage: u32,
) -> BufferBindingType {
if self.limits().max_storage_buffers_per_shader_stage >= buffers_per_shader_stage {
BufferBindingType::Storage { read_only: true }
} else {
BufferBindingType::Uniform
}
}
}