
# Objective structs containing wgpu types take a long time to compile. this is particularly bad for generics containing the wgpu structs (like the depth pipeline builder with `#[derive(SystemParam)]` i've been working on). we can avoid that by boxing and type-erasing in the bevy `render_resource` wrappers. type system magic is not a strength of mine so i guess there will be a cleaner way to achieve this, happy to take feedback or for it to be taken as a proof of concept if someone else wants to do a better job. ## Solution - add macros to box and type-erase in debug mode - leave current impl for release mode timings: <html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 15"> <link id=Main-File rel=Main-File href="file:///C:/Users/robfm/AppData/Local/Temp/msohtmlclip1/01/clip.htm"> <link rel=File-List href="file:///C:/Users/robfm/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml"> <!--table {mso-displayed-decimal-separator:"\."; mso-displayed-thousand-separator:"\,";} @page {margin:.75in .7in .75in .7in; mso-header-margin:.3in; mso-footer-margin:.3in;} tr {mso-height-source:auto;} col {mso-width-source:auto;} br {mso-data-placement:same-cell;} td {padding-top:1px; padding-right:1px; padding-left:1px; mso-ignore:padding; color:black; font-size:11.0pt; font-weight:400; font-style:normal; text-decoration:none; font-family:Calibri, sans-serif; mso-font-charset:0; mso-number-format:General; text-align:general; vertical-align:bottom; border:none; mso-background-source:auto; mso-pattern:auto; mso-protection:locked visible; white-space:nowrap; mso-rotate:0;} .xl65 {mso-number-format:0%;} .xl66 {vertical-align:middle; white-space:normal;} .xl67 {vertical-align:middle;} --> </head> <body link="#0563C1" vlink="#954F72"> current | | | -- | -- | -- | -- | Total time: | 64.9s | | bevy_pbr v0.9.0-dev | 19.2s | | bevy_render v0.9.0-dev | 17.0s | | bevy_sprite v0.9.0-dev | 15.1s | | DepthPipelineBuilder | 18.7s | | | | with type-erasing | | | diff | Total time: | 49.0s | -24% | bevy_render v0.9.0-dev | 12.0s | -38% | bevy_pbr v0.9.0-dev | 8.7s | -49% | bevy_sprite v0.9.0-dev | 6.1s | -60% | DepthPipelineBuilder | 1.2s | -94% </body> </html> the depth pipeline builder is a binary with body: ```rust use std::{marker::PhantomData, hash::Hash}; use bevy::{prelude::*, ecs::system::SystemParam, pbr::{RenderMaterials, MaterialPipeline, ShadowPipeline}, render::{renderer::RenderDevice, render_resource::{SpecializedMeshPipelines, PipelineCache}, render_asset::RenderAssets}}; fn main() { println!("Hello, world p!\n"); } #[derive(SystemParam)] pub struct DepthPipelineBuilder<'w, 's, M: Material> where M::Data: Eq + Hash + Clone, { render_device: Res<'w, RenderDevice>, material_pipeline: Res<'w, MaterialPipeline<M>>, material_pipelines: ResMut<'w, SpecializedMeshPipelines<MaterialPipeline<M>>>, shadow_pipeline: Res<'w, ShadowPipeline>, pipeline_cache: ResMut<'w, PipelineCache>, render_meshes: Res<'w, RenderAssets<Mesh>>, render_materials: Res<'w, RenderMaterials<M>>, msaa: Res<'w, Msaa>, #[system_param(ignore)] _p: PhantomData<&'s M>, } ```
204 lines
6.5 KiB
Rust
204 lines
6.5 KiB
Rust
use crate::render_resource::{
|
|
BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
|
|
RenderPipeline, Sampler, Texture,
|
|
};
|
|
use bevy_ecs::system::Resource;
|
|
use wgpu::{util::DeviceExt, BufferAsyncError, BufferBindingType};
|
|
|
|
use super::RenderQueue;
|
|
|
|
use crate::render_resource::resource_macros::*;
|
|
|
|
render_resource_wrapper!(ErasedRenderDevice, wgpu::Device);
|
|
|
|
/// This GPU device is responsible for the creation of most rendering and compute resources.
|
|
#[derive(Resource, Clone)]
|
|
pub struct RenderDevice {
|
|
device: ErasedRenderDevice,
|
|
}
|
|
|
|
impl From<wgpu::Device> for RenderDevice {
|
|
fn from(device: wgpu::Device) -> Self {
|
|
Self {
|
|
device: ErasedRenderDevice::new(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
|
|
}
|
|
}
|
|
}
|