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
43pub struct VulkanObjects {
45 pub device: Arc<ash::Device>,
47 pub alloc: Arc<Mutex<Allocator>>,
49 pub queue: vk::Queue,
51}
52
53#[derive(Clone)]
55pub struct VulkanInstance {
56 pub device: vk::Device,
58 pub instance: vk::Instance,
60 pub physical_device: vk::PhysicalDevice,
62 pub get_instance_proc_addr: Option<vk::PFN_vkGetInstanceProcAddr>,
65 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
161pub 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#[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 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 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 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 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 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 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 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 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 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 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 let (output_framebuffers, output_textures) = framebuffer_init.init_output_framebuffers()?;
456
457 let (feedback_framebuffers, feedback_textures) =
459 framebuffer_init.init_output_framebuffers()?;
460
461 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 uniform_storage,
549 uniform_bindings,
550 source: config.data,
551 meta: config.meta,
552 graphics_pipeline,
553 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 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 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 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 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 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 std::mem::swap(
719 &mut self.output_framebuffers,
720 &mut self.feedback_framebuffers,
721 );
722
723 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 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 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 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}