diff --git a/crates/benilla-app/src/post/grading.rs b/crates/benilla-app/src/post/grading.rs index 5a4dc42a..b3fd694d 100644 --- a/crates/benilla-app/src/post/grading.rs +++ b/crates/benilla-app/src/post/grading.rs @@ -25,7 +25,7 @@ use bevy::{ renderer::{RenderContext, RenderDevice, RenderQueue}, texture::GpuImage, view::ViewTarget, - RenderApp, RenderStartup, + Render, RenderApp, RenderStartup, RenderSystems, }, }; use std::collections::{HashMap, HashSet}; @@ -73,6 +73,10 @@ impl Plugin for GradingPlugin { render_app .insert_resource(GradeShader(shader)) .add_systems(RenderStartup, init_pipeline) + .add_systems( + Render, + prepare_uniforms.in_set(RenderSystems::PrepareResources), + ) .add_render_graph_node::>(Core3d, GradeLabel) .add_render_graph_edges( Core3d, @@ -344,17 +348,47 @@ fn init_pipeline( }); } +/// MONKEY (polish): the view's uniform, created once and rewritten in prepare, like bloom's. +#[derive(Component)] +struct GradeUniformBuffer(Buffer); + +fn prepare_uniforms( + mut commands: Commands, + device: Res, + queue: Res, + views: Query<(Entity, &GradeView, Option<&GradeUniformBuffer>)>, +) { + for (entity, grade, buffer) in &views { + let rows = [grade.control.to_array()]; + match buffer { + Some(buffer) => queue.write_buffer(&buffer.0, 0, bytemuck::cast_slice(&rows)), + None => { + let buffer = device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("post_grading_uniform"), + contents: bytemuck::cast_slice(&rows), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); + commands.entity(entity).insert(GradeUniformBuffer(buffer)); + } + } + } +} + #[derive(Default)] struct GradeNode; impl ViewNode for GradeNode { - type ViewQuery = (&'static ViewTarget, &'static GradeView); + type ViewQuery = ( + &'static ViewTarget, + &'static GradeView, + &'static GradeUniformBuffer, + ); fn run<'w>( &self, _graph: &mut RenderGraphContext, context: &mut RenderContext<'w>, - (target, grade): QueryItem<'w, '_, Self::ViewQuery>, + (target, grade, uniform): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let settings = world.resource::(); @@ -367,10 +401,6 @@ impl ViewNode for GradeNode { return Ok(()); }; let device = context.render_device(); - let mut uniform = UniformBuffer::from(GradeUniform { - control: grade.control, - }); - uniform.write_buffer(device, world.resource::()); let out = target.post_process_write(); let bind = device.create_bind_group( "post_grading", @@ -381,7 +411,7 @@ impl ViewNode for GradeNode { &day.texture_view, &night.texture_view, &settings.lut_sampler, - uniform.binding().unwrap(), + uniform.0.as_entire_binding(), )), ); let diagnostics = context.diagnostic_recorder(); diff --git a/crates/benilla-app/src/post/sun_shafts.rs b/crates/benilla-app/src/post/sun_shafts.rs index 04228740..b82420d1 100644 --- a/crates/benilla-app/src/post/sun_shafts.rs +++ b/crates/benilla-app/src/post/sun_shafts.rs @@ -165,6 +165,10 @@ const MASK_FORMAT: TextureFormat = TextureFormat::R8Unorm; #[derive(Component)] struct ShaftMask(CachedTexture); +/// MONKEY (polish): the view's uniform, created once and rewritten in prepare, like bloom's. +#[derive(Component)] +struct ShaftUniform(Buffer); + #[derive(Component)] struct ViewShaftPipeline(CachedRenderPipelineId); @@ -277,17 +281,31 @@ fn prepare_pipelines( } } -/// The half-size mask target, rounded up so an odd edge column still has a texel. +/// The half-size mask target, rounded up so an odd edge column still has a texel, and the +/// view's uniform, written in place. fn prepare_masks( mut commands: Commands, mut textures: ResMut, device: Res, - views: Query<(Entity, &ExtractedCamera), With>, + queue: Res, + views: Query<(Entity, &ExtractedCamera, &ShaftView, Option<&ShaftUniform>)>, ) { - for (entity, camera) in &views { + for (entity, camera, shaft, uniform) in &views { let Some(size) = camera.physical_viewport_size else { continue; }; + let rows = [shaft.sun.to_array(), shaft.color.to_array()]; + match uniform { + Some(uniform) => queue.write_buffer(&uniform.0, 0, bytemuck::cast_slice(&rows)), + None => { + let buffer = device.create_buffer_with_data(&BufferInitDescriptor { + label: Some("post_sun_shafts_uniform"), + contents: bytemuck::cast_slice(&rows), + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); + commands.entity(entity).insert(ShaftUniform(buffer)); + } + } let mask = textures.get( &device, TextureDescriptor { @@ -316,16 +334,18 @@ impl ViewNode for ShaftNode { type ViewQuery = ( &'static ViewTarget, &'static ViewDepthTexture, + // Gates the pass: the mask and uniform outlive a disabled frame. &'static ShaftView, &'static ViewShaftPipeline, &'static ShaftMask, + &'static ShaftUniform, ); fn run<'w>( &self, _graph: &mut RenderGraphContext, context: &mut RenderContext<'w>, - (target, depth, shaft, id, mask): QueryItem<'w, '_, Self::ViewQuery>, + (target, depth, _, id, mask, uniform): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let cache = world.resource::(); @@ -350,8 +370,6 @@ impl ViewNode for ShaftNode { &cache.get_bind_group_layout(&settings.mask_layouts[multisampled]), &BindGroupEntries::single(depth.view()), ); - let mut uniform = UniformBuffer::from(*shaft); - uniform.write_buffer(device, world.resource::()); let out = target.post_process_write(); let bind = device.create_bind_group( "post_sun_shafts", @@ -360,7 +378,7 @@ impl ViewNode for ShaftNode { out.source, &settings.sampler, &mask.0.default_view, - uniform.binding().unwrap(), + uniform.0.as_entire_binding(), )), ); let diagnostics = context.diagnostic_recorder();