Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions crates/bevy_render/src/render_resource/pipeline_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
152 changes: 81 additions & 71 deletions crates/bevy_render/src/render_resource/sparse_buffer_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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
Expand All @@ -55,11 +55,17 @@ impl Plugin for SparseBufferPlugin {
};

render_app
.init_resource::<SparseBufferUpdateJobs>()
.init_resource::<SparseBufferUpdatePipelines>()
.init_resource::<SpecializedComputePipelines<SparseBufferUpdatePipelines>>()
.init_resource::<SparseBufferUpdateBindGroups>()
.init_gpu_resource::<SparseBufferUpdateJobs>()
.init_gpu_resource::<SpecializedComputePipelines<SparseBufferUpdatePipelines>>()
.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::<SpecializedComputePipelines<SparseBufferUpdatePipelines>>),
)
.chain(),
)
.add_systems(
RenderGraph,
// We perform sparse buffer updates very early so that sparse
Expand Down Expand Up @@ -131,7 +137,7 @@ pub struct SparseBufferUpdateBindGroups {
/// the bind group for that buffer goes away as well.
bind_groups: WeakKeyHashMap<Weak<SparseBufferId>, SparseBufferUpdateBindGroup>,
/// The ID of the update shader pipeline shared among all sparse buffers.
pipeline_id: CachedComputePipelineId,
pipeline_id: Option<CachedComputePipelineId>,
}

/// A single bind group for the sparse buffer update shader.
Expand Down Expand Up @@ -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;
};

Expand Down Expand Up @@ -244,56 +251,59 @@ fn clear_sparse_buffer_jobs(mut sparse_buffer_update_jobs: ResMut<SparseBufferUp
sparse_buffer_update_jobs.clear();
}

impl FromWorld for SparseBufferUpdatePipelines {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
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<AssetServer>,
render_device: Res<RenderDevice>,
) {
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<storage, read_write> dest_buffer: array<u32>;
storage_buffer::<u32>(false),
// @group(0) @binding(1) var<storage> src_buffer: array<u32>;
storage_buffer_read_only::<u32>(false),
// @group(0) @binding(2) var<storage> indices: array<u32>;
storage_buffer_read_only::<u32>(false),
// @group(0) @binding(3) var<uniform> metadata:
// SparseBufferUpdateMetadata;
uniform_buffer::<GpuSparseBufferUpdateMetadata>(false),
),
let bind_group_layout = BindGroupLayoutDescriptor::new(
"sparse buffer update bind group layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
// @group(0) @binding(0) var<storage, read_write> dest_buffer: array<u32>;
storage_buffer::<u32>(false),
// @group(0) @binding(1) var<storage> src_buffer: array<u32>;
storage_buffer_read_only::<u32>(false),
// @group(0) @binding(2) var<storage> indices: array<u32>;
storage_buffer_read_only::<u32>(false),
// @group(0) @binding(3) var<uniform> metadata:
// SparseBufferUpdateMetadata;
uniform_buffer::<GpuSparseBufferUpdateMetadata>(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<Shader>;

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()
}
Expand Down Expand Up @@ -768,26 +778,26 @@ where
}
}

impl FromWorld for SparseBufferUpdateBindGroups {
fn from_world(world: &mut World) -> Self {
world.resource_scope::<SpecializedComputePipelines<SparseBufferUpdatePipelines>, _>(
|world, mut specialized_sparse_buffer_update_pipelines| {
let pipeline_cache = world.resource::<PipelineCache>();
let sparse_buffer_update_pipelines =
world.resource::<SparseBufferUpdatePipelines>();
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<SparseBufferUpdatePipelines>,
>,
pipeline_cache: Res<PipelineCache>,
sparse_buffer_update_pipelines: Res<SparseBufferUpdatePipelines>,
) {
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
Expand Down