librashader_runtime_vk/
filter_chain.rs

1use crate::draw_quad::DrawQuad;
2use crate::error::FilterChainError;
3use crate::filter_pass::FilterPass;
4use crate::framebuffer::OutputImage;
5use crate::graphics_pipeline::VulkanGraphicsPipeline;
6use crate::luts::LutTexture;
7use crate::memory::RawVulkanBuffer;
8use crate::options::{FilterChainOptionsVulkan, FrameOptionsVulkan};
9use crate::queue_selection::get_graphics_queue;
10use crate::samplers::SamplerSet;
11use crate::texture::{InputImage, OwnedImage, OwnedImageLayout, VulkanImage};
12use crate::{error, memory, util};
13use ash::vk;
14use librashader_common::{ImageFormat, Size, Viewport};
15
16use ash::vk::Handle;
17use gpu_allocator::vulkan::Allocator;
18use librashader_cache::CachedCompilation;
19use librashader_common::map::FastHashMap;
20use librashader_presets::context::VideoDriver;
21use librashader_presets::{ShaderFeatures, ShaderPreset};
22use librashader_reflect::back::targets::SPIRV;
23use librashader_reflect::back::{CompileReflectShader, CompileShader};
24use librashader_reflect::front::SpirvCompilation;
25use librashader_reflect::reflect::cross::SpirvCross;
26use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
27use librashader_reflect::reflect::semantics::ShaderSemantics;
28use librashader_reflect::reflect::ReflectShader;
29use librashader_runtime::binding::BindingUtil;
30use librashader_runtime::framebuffer::FramebufferInit;
31use librashader_runtime::image::{ImageError, LoadedTexture, UVDirection, BGRA8};
32use librashader_runtime::quad::QuadType;
33use librashader_runtime::render_target::RenderTarget;
34use librashader_runtime::scaling::ScaleFramebuffer;
35use librashader_runtime::uniforms::UniformStorage;
36use parking_lot::Mutex;
37use rayon::prelude::*;
38use std::collections::VecDeque;
39use std::convert::Infallible;
40use std::path::Path;
41use std::sync::Arc;
42
43/// A Vulkan device and metadata that is required by the shader runtime.
44pub struct VulkanObjects {
45    /// The handle to the initialized `ash::Device`
46    pub device: Arc<ash::Device>,
47    /// The instance of the `gpu-allocator` to use.
48    pub alloc: Arc<Mutex<Allocator>>,
49    /// The graphics queue to do work on.
50    pub queue: vk::Queue,
51}
52
53/// A collection of handles needed to access the Vulkan instance.
54#[derive(Clone)]
55pub struct VulkanInstance {
56    /// A `VkDevice` handle.
57    pub device: vk::Device,
58    /// A `VkInstance` handle.
59    pub instance: vk::Instance,
60    /// A `VkPhysicalDevice` handle.
61    pub physical_device: vk::PhysicalDevice,
62    /// A function pointer to the Vulkan library entry point.
63    /// If this is `None`, [`FilterChainError::HandleIsNull`] will be returned.
64    pub get_instance_proc_addr: Option<vk::PFN_vkGetInstanceProcAddr>,
65    /// The graphics queue to use to submit commands. If this is `None`,
66    /// a queue will be chosen.
67    pub queue: Option<vk::Queue>,
68}
69
70impl TryFrom<VulkanInstance> for VulkanObjects {
71    type Error = FilterChainError;
72
73    fn try_from(vulkan: VulkanInstance) -> Result<Self, FilterChainError> {
74        if vulkan.queue.is_some_and(|q| q.is_null())
75            || vulkan.device.is_null()
76            || vulkan.instance.is_null()
77        {
78            return Err(FilterChainError::HandleIsNull);
79        };
80
81        let Some(get_instance_proc_addr) = vulkan.get_instance_proc_addr else {
82            return Err(FilterChainError::HandleIsNull);
83        };
84
85        unsafe {
86            let instance = ash::Instance::load(
87                &ash::StaticFn {
88                    get_instance_proc_addr,
89                },
90                vulkan.instance,
91            );
92
93            let device = ash::Device::load(instance.fp_v1_0(), vulkan.device);
94
95            let queue = vulkan.queue.unwrap_or(get_graphics_queue(
96                &instance,
97                &device,
98                vulkan.physical_device,
99            ));
100
101            let alloc = memory::create_allocator(device.clone(), instance, vulkan.physical_device)?;
102
103            Ok(VulkanObjects {
104                device: Arc::new(device),
105                alloc,
106                queue,
107            })
108        }
109    }
110}
111
112impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device)> for VulkanObjects {
113    type Error = FilterChainError;
114
115    fn try_from(value: (vk::PhysicalDevice, ash::Instance, ash::Device)) -> error::Result<Self> {
116        if value.0.is_null() {
117            return Err(FilterChainError::HandleIsNull);
118        }
119
120        let device = value.2;
121
122        let queue = get_graphics_queue(&value.1, &device, value.0);
123
124        let alloc = memory::create_allocator(device.clone(), value.1, value.0)?;
125
126        Ok(VulkanObjects {
127            alloc,
128            device: Arc::new(device),
129            queue,
130        })
131    }
132}
133
134impl TryFrom<(vk::PhysicalDevice, ash::Instance, ash::Device, vk::Queue)> for VulkanObjects {
135    type Error = FilterChainError;
136
137    fn try_from(
138        value: (vk::PhysicalDevice, ash::Instance, ash::Device, vk::Queue),
139    ) -> error::Result<Self> {
140        if value.0.is_null() {
141            return Err(FilterChainError::HandleIsNull);
142        }
143
144        let device = value.2;
145        let queue = if value.3.is_null() {
146            get_graphics_queue(&value.1, &device, value.0)
147        } else {
148            value.3
149        };
150
151        let alloc = memory::create_allocator(device.clone(), value.1, value.0)?;
152
153        Ok(VulkanObjects {
154            alloc,
155            device: Arc::new(device),
156            queue,
157        })
158    }
159}
160
161/// A Vulkan filter chain.
162pub struct FilterChainVulkan {
163    pub(crate) common: FilterCommon,
164    passes: Box<[FilterPass]>,
165    vulkan: VulkanObjects,
166    output_framebuffers: Box<[OwnedImage]>,
167    feedback_framebuffers: Box<[OwnedImage]>,
168    history_framebuffers: VecDeque<OwnedImage>,
169    disable_mipmaps: bool,
170    residuals: Box<[FrameResiduals]>,
171    default_options: FrameOptionsVulkan,
172    draw_last_pass_feedback: bool,
173}
174
175pub(crate) struct FilterCommon {
176    pub(crate) luts: FastHashMap<usize, LutTexture>,
177    pub samplers: SamplerSet,
178    pub(crate) draw_quad: DrawQuad,
179    pub output_textures: Box<[Option<InputImage>]>,
180    pub feedback_textures: Box<[Option<InputImage>]>,
181    pub history_textures: Box<[Option<InputImage>]>,
182    pub config: RuntimeParameters,
183    pub device: Arc<ash::Device>,
184    pub(crate) internal_frame_count: usize,
185}
186
187/// Contains residual intermediate `VkImageView` and `VkImage` objects created
188/// for intermediate shader passes.
189///
190/// These Vulkan objects must stay alive until the command buffer is submitted
191/// to the rendering queue, and the GPU is done with the objects.
192#[must_use]
193struct FrameResiduals {
194    device: ash::Device,
195    image_views: Vec<vk::ImageView>,
196    owned: Vec<OwnedImage>,
197    framebuffers: Vec<Option<vk::Framebuffer>>,
198}
199
200impl FrameResiduals {
201    pub(crate) fn new(device: &ash::Device) -> Self {
202        FrameResiduals {
203            device: device.clone(),
204            image_views: Vec::new(),
205            owned: Vec::new(),
206            framebuffers: Vec::new(),
207        }
208    }
209
210    pub(crate) fn dispose_outputs(&mut self, output_framebuffer: OutputImage) {
211        self.image_views.push(output_framebuffer.image_view);
212    }
213
214    pub(crate) fn dispose_owned(&mut self, owned: OwnedImage) {
215        self.owned.push(owned)
216    }
217
218    pub(crate) fn dispose_framebuffers(&mut self, fb: Option<vk::Framebuffer>) {
219        self.framebuffers.push(fb)
220    }
221
222    /// Dispose of the intermediate objects created during a frame.
223    pub fn dispose(&mut self) {
224        for image_view in self.image_views.drain(0..) {
225            if image_view != vk::ImageView::null() {
226                unsafe {
227                    self.device.destroy_image_view(image_view, None);
228                }
229            }
230        }
231        for framebuffer in self.framebuffers.drain(0..) {
232            if let Some(framebuffer) =
233                framebuffer.filter(|framebuffer| *framebuffer != vk::Framebuffer::null())
234            {
235                unsafe {
236                    self.device.destroy_framebuffer(framebuffer, None);
237                }
238            }
239        }
240        self.owned.clear()
241    }
242}
243
244impl Drop for FrameResiduals {
245    fn drop(&mut self) {
246        // Will not double free because dispose removes active items from storage.
247        self.dispose()
248    }
249}
250
251mod compile {
252    use super::*;
253    use librashader_pack::PassResource;
254
255    #[cfg(not(feature = "stable"))]
256    pub type ShaderPassMeta =
257        ShaderPassArtifact<impl CompileReflectShader<SPIRV, SpirvCompilation, SpirvCross> + Send>;
258
259    #[cfg(feature = "stable")]
260    pub type ShaderPassMeta = ShaderPassArtifact<
261        Box<dyn CompileReflectShader<SPIRV, SpirvCompilation, SpirvCross> + Send>,
262    >;
263
264    #[cfg_attr(not(feature = "stable"), define_opaque(ShaderPassMeta))]
265    pub fn compile_passes(
266        shaders: Vec<PassResource>,
267        textures: &[TextureResource],
268        disable_cache: bool,
269    ) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
270        let (passes, semantics) = if !disable_cache {
271            SPIRV::compile_preset_passes::<
272                CachedCompilation<SpirvCompilation>,
273                SpirvCross,
274                FilterChainError,
275            >(shaders, textures.iter().map(|t| &t.meta))?
276        } else {
277            SPIRV::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
278                shaders,
279                textures.iter().map(|t| &t.meta),
280            )?
281        };
282
283        Ok((passes, semantics))
284    }
285}
286
287use compile::{compile_passes, ShaderPassMeta};
288use librashader_pack::{ShaderPresetPack, TextureResource};
289use librashader_runtime::parameters::RuntimeParameters;
290
291impl FilterChainVulkan {
292    /// Load the shader preset at the given path into a filter chain.
293    pub unsafe fn load_from_path<V, E>(
294        path: impl AsRef<Path>,
295        features: ShaderFeatures,
296        vulkan: V,
297        options: Option<&FilterChainOptionsVulkan>,
298    ) -> error::Result<FilterChainVulkan>
299    where
300        V: TryInto<VulkanObjects, Error = E>,
301        FilterChainError: From<E>,
302    {
303        // load passes from preset
304        let preset =
305            ShaderPreset::try_parse_with_driver_context(path, features, VideoDriver::Vulkan)?;
306        unsafe { Self::load_from_preset::<V, E>(preset, vulkan, options) }
307    }
308
309    /// Load a filter chain from a pre-parsed `ShaderPreset`.
310    pub unsafe fn load_from_preset<V, E>(
311        preset: ShaderPreset,
312        vulkan: V,
313        options: Option<&FilterChainOptionsVulkan>,
314    ) -> error::Result<FilterChainVulkan>
315    where
316        V: TryInto<VulkanObjects, Error = E>,
317        FilterChainError: From<E>,
318    {
319        let pack = ShaderPresetPack::load_from_preset::<FilterChainError>(preset)?;
320        unsafe { Self::load_from_pack(pack, vulkan, options) }
321    }
322
323    /// Load a filter chain from a pre-parsed and loaded `ShaderPresetPack`.
324    pub unsafe fn load_from_pack<V, E>(
325        preset: ShaderPresetPack,
326        vulkan: V,
327        options: Option<&FilterChainOptionsVulkan>,
328    ) -> error::Result<FilterChainVulkan>
329    where
330        V: TryInto<VulkanObjects, Error = E>,
331        FilterChainError: From<E>,
332    {
333        let vulkan = vulkan.try_into().map_err(|e| e.into())?;
334        let device = Arc::clone(&vulkan.device);
335        let queue = vulkan.queue.clone();
336
337        let command_pool = unsafe {
338            device.create_command_pool(
339                &vk::CommandPoolCreateInfo::default()
340                    .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER),
341                None,
342            )?
343        };
344
345        let command_buffer = unsafe {
346            // panic safety: command buffer count = 1
347            device.allocate_command_buffers(
348                &vk::CommandBufferAllocateInfo::default()
349                    .command_pool(command_pool)
350                    .level(vk::CommandBufferLevel::PRIMARY)
351                    .command_buffer_count(1),
352            )?[0]
353        };
354
355        unsafe {
356            device.begin_command_buffer(
357                command_buffer,
358                &vk::CommandBufferBeginInfo::default()
359                    .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
360            )?
361        }
362
363        let filter_chain = unsafe {
364            Self::load_from_pack_deferred::<_, Infallible>(preset, vulkan, command_buffer, options)?
365        };
366
367        unsafe {
368            device.end_command_buffer(command_buffer)?;
369
370            let buffers = [command_buffer];
371            let submit_info = vk::SubmitInfo::default().command_buffers(&buffers);
372
373            device.queue_submit(queue, &[submit_info], vk::Fence::null())?;
374            device.queue_wait_idle(queue)?;
375            device.free_command_buffers(command_pool, &buffers);
376            device.destroy_command_pool(command_pool, None);
377        }
378
379        Ok(filter_chain)
380    }
381
382    /// Load a filter chain from a pre-parsed `ShaderPreset`, deferring and GPU-side initialization
383    /// to the caller. This function therefore requires no external synchronization of the device queue.
384    ///
385    /// ## Safety
386    /// The provided command buffer must be ready for recording and contain no prior commands.
387    /// The caller is responsible for ending the command buffer and immediately submitting it to a
388    /// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame).
389    pub unsafe fn load_from_preset_deferred<V, E>(
390        preset: ShaderPreset,
391        vulkan: V,
392        cmd: vk::CommandBuffer,
393        options: Option<&FilterChainOptionsVulkan>,
394    ) -> error::Result<FilterChainVulkan>
395    where
396        V: TryInto<VulkanObjects, Error = E>,
397        FilterChainError: From<E>,
398    {
399        let pack = ShaderPresetPack::load_from_preset::<FilterChainError>(preset)?;
400        unsafe { Self::load_from_pack_deferred(pack, vulkan, cmd, options) }
401    }
402
403    /// Load a filter chain from a pre-parsed, loaded `ShaderPresetPack`, deferring and GPU-side initialization
404    /// to the caller. This function therefore requires no external synchronization of the device queue.
405    ///
406    /// ## Safety
407    /// The provided command buffer must be ready for recording and contain no prior commands.
408    /// The caller is responsible for ending the command buffer and immediately submitting it to a
409    /// graphics queue. The command buffer must be completely executed before calling [`frame`](Self::frame).
410    pub unsafe fn load_from_pack_deferred<V, E>(
411        preset: ShaderPresetPack,
412        vulkan: V,
413        cmd: vk::CommandBuffer,
414        options: Option<&FilterChainOptionsVulkan>,
415    ) -> error::Result<FilterChainVulkan>
416    where
417        V: TryInto<VulkanObjects, Error = E>,
418        FilterChainError: From<E>,
419    {
420        let config = RuntimeParameters::new(&preset);
421
422        let disable_cache = options.map_or(false, |o| o.disable_cache);
423        let (passes, semantics) = compile_passes(preset.passes, &preset.textures, disable_cache)?;
424
425        let device = vulkan.try_into().map_err(From::from)?;
426
427        let mut frames_in_flight = options.map_or(0, |o| o.frames_in_flight);
428        if frames_in_flight == 0 {
429            frames_in_flight = 3;
430        }
431
432        // initialize passes
433        let filters = Self::init_passes(
434            &device,
435            passes,
436            &semantics,
437            frames_in_flight,
438            options.map_or(false, |o| o.use_dynamic_rendering),
439            disable_cache,
440        )?;
441
442        let luts = FilterChainVulkan::load_luts(&device, cmd, preset.textures)?;
443        let samplers = SamplerSet::new(&device.device)?;
444
445        let framebuffer_gen =
446            || OwnedImage::new(&device, Size::new(1, 1), ImageFormat::R8G8B8A8Unorm, 1);
447        let input_gen = || None;
448        let framebuffer_init = FramebufferInit::new(
449            filters.iter().map(|f| &f.reflection.meta),
450            &framebuffer_gen,
451            &input_gen,
452        );
453
454        // initialize output framebuffers
455        let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
456
457        // initialize feedback framebuffers
458        let (feedback_framebuffers, feedback_textures) =
459            framebuffer_init.init_output_framebuffers()?;
460
461        // initialize history
462        let (history_framebuffers, history_textures) = framebuffer_init.init_history()?;
463
464        let mut intermediates = Vec::new();
465        intermediates.resize_with(frames_in_flight as usize, || {
466            FrameResiduals::new(&device.device)
467        });
468
469        Ok(FilterChainVulkan {
470            draw_last_pass_feedback: framebuffer_init.uses_final_pass_as_feedback(),
471            common: FilterCommon {
472                luts,
473                samplers,
474                config,
475                draw_quad: DrawQuad::new(&device.device, &device.alloc)?,
476                device: device.device.clone(),
477                output_textures,
478                feedback_textures,
479                history_textures,
480                internal_frame_count: 0,
481            },
482            passes: filters,
483            vulkan: device,
484            output_framebuffers,
485            feedback_framebuffers,
486            history_framebuffers,
487            residuals: intermediates.into_boxed_slice(),
488            disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
489            default_options: Default::default(),
490        })
491    }
492
493    fn init_passes(
494        vulkan: &VulkanObjects,
495        passes: Vec<ShaderPassMeta>,
496        semantics: &ShaderSemantics,
497        frames_in_flight: u32,
498        use_dynamic_rendering: bool,
499        disable_cache: bool,
500    ) -> error::Result<Box<[FilterPass]>> {
501        let frames_in_flight = std::cmp::max(1, frames_in_flight);
502
503        let filters: Vec<error::Result<FilterPass>> = passes
504            .into_par_iter()
505            .enumerate()
506            .map(|(index, (config, mut reflect))| {
507                let reflection = reflect.reflect(index, semantics)?;
508                let spirv_words = reflect.compile(None)?;
509
510                let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize);
511                let uniform_storage = UniformStorage::new_with_ubo_storage(
512                    RawVulkanBuffer::new(
513                        &vulkan.device,
514                        &vulkan.alloc,
515                        vk::BufferUsageFlags::UNIFORM_BUFFER,
516                        ubo_size,
517                    )?,
518                    reflection
519                        .push_constant
520                        .as_ref()
521                        .map_or(0, |push| push.size as usize),
522                );
523
524                let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset());
525
526                let render_pass_format = if use_dynamic_rendering {
527                    vk::Format::UNDEFINED
528                } else if let Some(format) = config.meta.get_format_override() {
529                    format.into()
530                } else if config.data.format != ImageFormat::Unknown {
531                    config.data.format.into()
532                } else {
533                    ImageFormat::R8G8B8A8Unorm.into()
534                };
535
536                let graphics_pipeline = VulkanGraphicsPipeline::new(
537                    &vulkan.device,
538                    &spirv_words,
539                    &reflection,
540                    frames_in_flight,
541                    render_pass_format,
542                    disable_cache,
543                )?;
544
545                Ok(FilterPass {
546                    reflection,
547                    // compiled: spirv_words,
548                    uniform_storage,
549                    uniform_bindings,
550                    source: config.data,
551                    meta: config.meta,
552                    graphics_pipeline,
553                    // ubo_ring,
554                    frames_in_flight,
555                })
556            })
557            .collect();
558
559        let filters: error::Result<Vec<FilterPass>> = filters.into_iter().collect();
560        let filters = filters?;
561        Ok(filters.into_boxed_slice())
562    }
563
564    fn load_luts(
565        vulkan: &VulkanObjects,
566        command_buffer: vk::CommandBuffer,
567        textures: Vec<TextureResource>,
568    ) -> error::Result<FastHashMap<usize, LutTexture>> {
569        let mut luts = FastHashMap::default();
570        let textures = textures
571            .into_par_iter()
572            .map(|texture| LoadedTexture::from_texture(texture, UVDirection::TopLeft))
573            .collect::<Result<Vec<LoadedTexture<BGRA8>>, ImageError>>()?;
574        for (index, LoadedTexture { meta, image }) in textures.into_iter().enumerate() {
575            let texture = LutTexture::new(vulkan, command_buffer, image, &meta)?;
576            luts.insert(index, texture);
577        }
578        Ok(luts)
579    }
580
581    // image must be in SHADER_READ_OPTIMAL
582    fn push_history(&mut self, input: &VulkanImage, cmd: vk::CommandBuffer) -> error::Result<()> {
583        if let Some(mut back) = self.history_framebuffers.pop_back() {
584            if back.image.size != input.size
585                || (input.format != vk::Format::UNDEFINED && input.format != back.image.format)
586            {
587                // eprintln!("[history] resizing");
588                // old back will get dropped.. do we need to defer?
589                let old_back = std::mem::replace(
590                    &mut back,
591                    OwnedImage::new(&self.vulkan, input.size, input.format.into(), 1)?,
592                );
593                self.residuals[self.common.internal_frame_count % self.residuals.len()]
594                    .dispose_owned(old_back);
595            }
596
597            unsafe {
598                util::vulkan_image_layout_transition_levels(
599                    &self.vulkan.device,
600                    cmd,
601                    input.image,
602                    1,
603                    vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
604                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
605                    vk::AccessFlags::SHADER_READ,
606                    vk::AccessFlags::TRANSFER_READ,
607                    vk::PipelineStageFlags::FRAGMENT_SHADER,
608                    vk::PipelineStageFlags::TRANSFER,
609                    vk::QUEUE_FAMILY_IGNORED,
610                    vk::QUEUE_FAMILY_IGNORED,
611                );
612
613                back.copy_from(cmd, input, vk::ImageLayout::TRANSFER_SRC_OPTIMAL);
614
615                util::vulkan_image_layout_transition_levels(
616                    &self.vulkan.device,
617                    cmd,
618                    input.image,
619                    1,
620                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
621                    vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
622                    vk::AccessFlags::TRANSFER_READ,
623                    vk::AccessFlags::SHADER_READ,
624                    vk::PipelineStageFlags::TRANSFER,
625                    vk::PipelineStageFlags::FRAGMENT_SHADER,
626                    vk::QUEUE_FAMILY_IGNORED,
627                    vk::QUEUE_FAMILY_IGNORED,
628                );
629            }
630
631            self.history_framebuffers.push_front(back)
632        }
633
634        Ok(())
635    }
636    /// Records shader rendering commands to the provided command buffer.
637    ///
638    /// * The input image must be in the `VK_SHADER_READ_ONLY_OPTIMAL` layout.
639    /// * The output image must be in `VK_COLOR_ATTACHMENT_OPTIMAL` layout.
640    ///
641    /// librashader **will not** create a pipeline barrier for the final pass. The output image will
642    /// remain in `VK_COLOR_ATTACHMENT_OPTIMAL` after all shader passes. The caller must transition
643    /// the output image to the final layout.
644    pub unsafe fn frame(
645        &mut self,
646        input: &VulkanImage,
647        viewport: &Viewport<VulkanImage>,
648        cmd: vk::CommandBuffer,
649        frame_count: usize,
650        options: Option<&FrameOptionsVulkan>,
651    ) -> error::Result<()> {
652        let intermediates =
653            &mut self.residuals[self.common.internal_frame_count % self.residuals.len()];
654        intermediates.dispose();
655
656        // limit number of passes to those enabled.
657        let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
658        let passes = &mut self.passes[0..max];
659
660        if let Some(options) = &options {
661            if options.clear_history {
662                for history in &mut self.history_framebuffers {
663                    history.clear(cmd);
664                }
665            }
666        }
667
668        if passes.is_empty() {
669            return Ok(());
670        }
671
672        let original_image_view = unsafe {
673            let create_info = vk::ImageViewCreateInfo::default()
674                .image(input.image)
675                .format(input.format)
676                .view_type(vk::ImageViewType::TYPE_2D)
677                .subresource_range(
678                    vk::ImageSubresourceRange::default()
679                        .aspect_mask(vk::ImageAspectFlags::COLOR)
680                        .level_count(1)
681                        .layer_count(1),
682                )
683                .components(
684                    vk::ComponentMapping::default()
685                        .r(vk::ComponentSwizzle::R)
686                        .g(vk::ComponentSwizzle::G)
687                        .b(vk::ComponentSwizzle::B)
688                        .a(vk::ComponentSwizzle::A),
689                );
690
691            self.vulkan.device.create_image_view(&create_info, None)?
692        };
693
694        let filter = passes[0].meta.filter;
695        let wrap_mode = passes[0].meta.wrap_mode;
696
697        // update history
698        for (texture, image) in self
699            .common
700            .history_textures
701            .iter_mut()
702            .zip(self.history_framebuffers.iter())
703        {
704            *texture = Some(image.as_input(filter, wrap_mode));
705        }
706
707        let original = InputImage {
708            image: input.clone(),
709            image_view: original_image_view,
710            wrap_mode,
711            filter_mode: filter,
712            mip_filter: filter,
713        };
714
715        let mut source = original.clone();
716
717        // swap output and feedback **before** recording command buffers
718        std::mem::swap(
719            &mut self.output_framebuffers,
720            &mut self.feedback_framebuffers,
721        );
722
723        // rescale render buffers to ensure all bindings are valid.
724        OwnedImage::scale_framebuffers_with_context(
725            source.image.size,
726            viewport.output.size,
727            original.image.size,
728            &mut self.output_framebuffers,
729            &mut self.feedback_framebuffers,
730            passes,
731            &Some(OwnedImageLayout {
732                dst_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
733                dst_access: vk::AccessFlags::SHADER_READ,
734                src_stage: vk::PipelineStageFlags::TOP_OF_PIPE,
735                dst_stage: vk::PipelineStageFlags::FRAGMENT_SHADER,
736                cmd,
737            }),
738            Some(&mut |index: usize,
739                       pass: &FilterPass,
740                       output: &OwnedImage,
741                       feedback: &OwnedImage| {
742                // refresh inputs
743                self.common.feedback_textures[index] =
744                    Some(feedback.as_input(pass.meta.filter, pass.meta.wrap_mode));
745                self.common.output_textures[index] =
746                    Some(output.as_input(pass.meta.filter, pass.meta.wrap_mode));
747                Ok(())
748            }),
749        )?;
750
751        let passes_len = passes.len();
752        let (pass, last) = passes.split_at_mut(passes_len - 1);
753
754        let options = options.unwrap_or(&self.default_options);
755
756        self.common
757            .draw_quad
758            .bind_vbo_for_frame(&self.vulkan.device, cmd);
759        for (index, pass) in pass.iter_mut().enumerate() {
760            let target = &self.output_framebuffers[index];
761            source.filter_mode = pass.meta.filter;
762            source.wrap_mode = pass.meta.wrap_mode;
763            source.mip_filter = pass.meta.filter;
764
765            let output_image = OutputImage::new(&self.vulkan.device, target.image.clone())?;
766            let out = RenderTarget::identity(&output_image)?;
767
768            let residual_fb = pass.draw(
769                cmd,
770                target.image.format,
771                index,
772                &self.common,
773                pass.meta.get_frame_count(frame_count),
774                options,
775                viewport,
776                &original,
777                &source,
778                &out,
779                QuadType::Offscreen,
780                false,
781            )?;
782
783            if target.max_miplevels > 1 && !self.disable_mipmaps {
784                target.generate_mipmaps_and_end_pass(cmd);
785            } else {
786                out.output.end_pass(&self.vulkan.device, cmd);
787            }
788
789            source = self.common.output_textures[index].clone().unwrap();
790            intermediates.dispose_outputs(output_image);
791            intermediates.dispose_framebuffers(residual_fb);
792        }
793
794        // try to hint the optimizer
795        assert_eq!(last.len(), 1);
796        if let Some(pass) = last.iter_mut().next() {
797            let index = passes_len - 1;
798            if pass
799                .graphics_pipeline
800                .render_passes
801                .get(&viewport.output.format)
802                .is_none()
803            {
804                // need to recompile
805                pass.graphics_pipeline.recompile(viewport.output.format)?;
806            }
807
808            source.filter_mode = pass.meta.filter;
809            source.wrap_mode = pass.meta.wrap_mode;
810            source.mip_filter = pass.meta.filter;
811
812            if self.draw_last_pass_feedback {
813                let target = &self.output_framebuffers[index];
814
815                let output_image = OutputImage::new(&self.vulkan.device, target.image.clone())?;
816                let out = RenderTarget::viewport_with_output(&output_image, viewport);
817
818                let residual_fb = pass.draw(
819                    cmd,
820                    target.image.format,
821                    index,
822                    &self.common,
823                    pass.meta.get_frame_count(frame_count),
824                    options,
825                    viewport,
826                    &original,
827                    &source,
828                    &out,
829                    QuadType::Final,
830                    true,
831                )?;
832                out.output.end_pass(&self.vulkan.device, cmd);
833                intermediates.dispose_outputs(output_image);
834                intermediates.dispose_framebuffers(residual_fb);
835            }
836
837            let output_image = OutputImage::new(&self.vulkan.device, viewport.output.clone())?;
838            let out = RenderTarget::viewport_with_output(&output_image, viewport);
839
840            let residual_fb = pass.draw(
841                cmd,
842                viewport.output.format,
843                index,
844                &self.common,
845                pass.meta.get_frame_count(frame_count),
846                options,
847                viewport,
848                &original,
849                &source,
850                &out,
851                QuadType::Final,
852                false,
853            )?;
854
855            intermediates.dispose_outputs(output_image);
856            intermediates.dispose_framebuffers(residual_fb);
857        }
858
859        self.push_history(input, cmd)?;
860        self.common.internal_frame_count = self.common.internal_frame_count.wrapping_add(1);
861        Ok(())
862    }
863}