diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 399846d01626e..49e9848c76964 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -712,8 +712,14 @@ impl PipelineCache { CachedPipelineState::Err(err) => match err { // Retry - ShaderCacheError::ShaderNotLoaded(_) - | ShaderCacheError::ShaderImportNotYetAvailable => { + ShaderCacheError::ShaderImportNotYetAvailable => { + bevy_log::debug!("retry processing pipeline {id}, shader import is not loaded"); + cached_pipeline.state = CachedPipelineState::Queued; + } + ShaderCacheError::ShaderNotLoaded(shader_id) => { + bevy_log::debug!( + "retry processing pipeline {id}, shader {shader_id} is not loaded" + ); cached_pipeline.state = CachedPipelineState::Queued; } diff --git a/crates/bevy_render/src/render_resource/sparse_buffer_vec.rs b/crates/bevy_render/src/render_resource/sparse_buffer_vec.rs index 94a9ed4f115af..98fe05da4d6c0 100644 --- a/crates/bevy_render/src/render_resource/sparse_buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/sparse_buffer_vec.rs @@ -8,13 +8,12 @@ use core::{ }; use bevy_app::{App, Plugin}; -use bevy_asset::{embedded_asset, load_embedded_asset, Handle}; +use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ resource::Resource, schedule::IntoScheduleConfigs as _, - system::{Res, ResMut}, - world::{FromWorld, World}, + system::{Commands, Res, ResMut}, }; use bevy_log::{debug, error, info}; use bevy_material::{ @@ -32,12 +31,13 @@ use wgpu::{BufferDescriptor, BufferUsages, ComputePassDescriptor, ShaderStages}; use crate::{ diagnostic::RecordDiagnostics as _, + init_gpu_resource, render_resource::{ AtomicPod, BindGroup, BindGroupEntries, Buffer, PipelineCache, RawBufferVec, SpecializedComputePipeline, SpecializedComputePipelines, UniformBuffer, }, renderer::{RenderContext, RenderDevice, RenderGraph, RenderGraphSystems, RenderQueue}, - ExtractSchedule, RenderApp, + ExtractSchedule, GpuResourceAppExt, RenderApp, RenderStartup, }; /// A plugin that allows sparse updates of GPU buffers if only a small number of @@ -55,11 +55,17 @@ impl Plugin for SparseBufferPlugin { }; render_app - .init_resource::() - .init_resource::() - .init_resource::>() - .init_resource::() + .init_gpu_resource::() + .init_gpu_resource::>() .add_systems(ExtractSchedule, clear_sparse_buffer_jobs) + .add_systems( + RenderStartup, + ( + init_sparse_buffer_update_pipelines, + init_sparse_buffer_update_bind_groups.after(init_gpu_resource::>), + ) + .chain(), + ) .add_systems( RenderGraph, // We perform sparse buffer updates very early so that sparse @@ -131,7 +137,7 @@ pub struct SparseBufferUpdateBindGroups { /// the bind group for that buffer goes away as well. bind_groups: WeakKeyHashMap, SparseBufferUpdateBindGroup>, /// The ID of the update shader pipeline shared among all sparse buffers. - pipeline_id: CachedComputePipelineId, + pipeline_id: Option, } /// A single bind group for the sparse buffer update shader. @@ -196,9 +202,10 @@ pub fn update_sparse_buffers( return; } - let Some(compute_pipeline) = - pipeline_cache.get_compute_pipeline(sparse_buffer_update_bind_groups.pipeline_id) - else { + let Some(pipeline_id) = sparse_buffer_update_bind_groups.pipeline_id else { + return; + }; + let Some(compute_pipeline) = pipeline_cache.get_compute_pipeline(pipeline_id) else { return; }; @@ -244,56 +251,59 @@ fn clear_sparse_buffer_jobs(mut sparse_buffer_update_jobs: ResMut Self { - let render_device = world.resource::(); - let limit = render_device.limits().max_storage_buffers_per_shader_stage; - - if limit < 3 { - info!( - "Sparse buffer updates disabled. RenderDevice lacks support: max_storage_buffers_per_shader_stage ({}) < 3.", - limit - ); - - return SparseBufferUpdatePipelines { - bind_group_layout: None, - shader: None, - }; - } +pub fn init_sparse_buffer_update_pipelines( + mut commands: Commands, + asset_server: Res, + render_device: Res, +) { + let limit = render_device.limits().max_storage_buffers_per_shader_stage; + if limit < 3 { + info!( + "Sparse buffer updates disabled. RenderDevice lacks support: max_storage_buffers_per_shader_stage ({}) < 3.", + limit + ); + commands.insert_resource(SparseBufferUpdatePipelines { + bind_group_layout: None, + shader: None, + }); + return; + } - let bind_group_layout = BindGroupLayoutDescriptor::new( - "sparse buffer update bind group layout", - &BindGroupLayoutEntries::sequential( - ShaderStages::COMPUTE, - ( - // @group(0) @binding(0) var dest_buffer: array; - storage_buffer::(false), - // @group(0) @binding(1) var src_buffer: array; - storage_buffer_read_only::(false), - // @group(0) @binding(2) var indices: array; - storage_buffer_read_only::(false), - // @group(0) @binding(3) var metadata: - // SparseBufferUpdateMetadata; - uniform_buffer::(false), - ), + let bind_group_layout = BindGroupLayoutDescriptor::new( + "sparse buffer update bind group layout", + &BindGroupLayoutEntries::sequential( + ShaderStages::COMPUTE, + ( + // @group(0) @binding(0) var dest_buffer: array; + storage_buffer::(false), + // @group(0) @binding(1) var src_buffer: array; + storage_buffer_read_only::(false), + // @group(0) @binding(2) var indices: array; + storage_buffer_read_only::(false), + // @group(0) @binding(3) var metadata: + // SparseBufferUpdateMetadata; + uniform_buffer::(false), ), - ); + ), + ); - SparseBufferUpdatePipelines { - bind_group_layout: Some(bind_group_layout), - shader: Some(load_embedded_asset!(world, "sparse_buffer_update.wgsl")), - } - } + commands.insert_resource(SparseBufferUpdatePipelines { + bind_group_layout: Some(bind_group_layout), + shader: Some(load_embedded_asset!( + asset_server.as_ref(), + "sparse_buffer_update.wgsl" + )), + }); } impl SpecializedComputePipeline for SparseBufferUpdatePipelines { - type Key = (); + type Key = Handle; - fn specialize(&self, _: Self::Key) -> ComputePipelineDescriptor { + fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor { ComputePipelineDescriptor { label: Some("sparse buffer update pipeline".into()), layout: self.bind_group_layout.clone().into_iter().collect(), - shader: self.shader.clone().unwrap_or_default(), + shader: key, shader_defs: vec![], ..ComputePipelineDescriptor::default() } @@ -768,26 +778,26 @@ where } } -impl FromWorld for SparseBufferUpdateBindGroups { - fn from_world(world: &mut World) -> Self { - world.resource_scope::, _>( - |world, mut specialized_sparse_buffer_update_pipelines| { - let pipeline_cache = world.resource::(); - let sparse_buffer_update_pipelines = - world.resource::(); - let pipeline_id = specialized_sparse_buffer_update_pipelines.specialize( - pipeline_cache, - sparse_buffer_update_pipelines, - (), - ); - - SparseBufferUpdateBindGroups { - bind_groups: WeakKeyHashMap::default(), - pipeline_id, - } - }, +pub fn init_sparse_buffer_update_bind_groups( + mut commands: Commands, + mut specialized_sparse_buffer_update_pipelines: ResMut< + SpecializedComputePipelines, + >, + pipeline_cache: Res, + sparse_buffer_update_pipelines: Res, +) { + let pipeline_id = sparse_buffer_update_pipelines.shader.clone().map(|shader| { + specialized_sparse_buffer_update_pipelines.specialize( + &pipeline_cache, + &sparse_buffer_update_pipelines, + shader, ) - } + }); + + commands.insert_resource(SparseBufferUpdateBindGroups { + bind_groups: WeakKeyHashMap::default(), + pipeline_id, + }); } /// Marks elements within the range `old_len..new_len` as dirty, under the