// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "video_core/framebuffer_config.h" #include "video_core/renderer_vulkan/present/filters.h" #include "video_core/renderer_vulkan/present/layer.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" #include "video_core/renderer_vulkan/vk_present_manager.h" #include "video_core/renderer_vulkan/vk_scheduler.h" namespace Vulkan { BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, MemoryAllocator& memory_allocator_, PresentManager& present_manager_, Scheduler& scheduler_) : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, present_manager{present_manager_}, scheduler{scheduler_}, image_count{1}, swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} BlitScreen::~BlitScreen() = default; void BlitScreen::WaitIdle() { present_manager.WaitPresent(); scheduler.Finish(); device.GetLogical().WaitIdle(); } void BlitScreen::SetWindowAdaptPass() { layers.clear(); scaling_filter = Settings::values.scaling_filter.GetValue(); switch (scaling_filter) { case Settings::ScalingFilter::NearestNeighbor: window_adapt = MakeNearestNeighbor(device, swapchain_view_format); break; case Settings::ScalingFilter::Bicubic: window_adapt = MakeBicubic(device, swapchain_view_format); break; case Settings::ScalingFilter::Gaussian: window_adapt = MakeGaussian(device, swapchain_view_format); break; case Settings::ScalingFilter::ScaleForce: window_adapt = MakeScaleForce(device, swapchain_view_format); break; case Settings::ScalingFilter::Fsr: case Settings::ScalingFilter::Bilinear: default: window_adapt = MakeBilinear(device, swapchain_view_format); break; } } void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, std::span framebuffers, const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, VkFormat current_swapchain_view_format) { bool resource_update_required = false; bool presentation_recreate_required = false; // Recreate dynamic resources if the adapting filter changed if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) { resource_update_required = true; } // Recreate dynamic resources if the image count changed const size_t old_swapchain_image_count = std::exchange(image_count, current_swapchain_image_count); if (old_swapchain_image_count != current_swapchain_image_count) { resource_update_required = true; } // Recreate the presentation frame if the format or dimensions of the window changed const VkFormat old_swapchain_view_format = std::exchange(swapchain_view_format, current_swapchain_view_format); if (old_swapchain_view_format != current_swapchain_view_format || layout.width != frame->width || layout.height != frame->height) { resource_update_required = true; presentation_recreate_required = true; } // If we have a pending resource update, perform it if (resource_update_required) { // Wait for idle to ensure no resources are in use WaitIdle(); // Update window adapt pass SetWindowAdaptPass(); // Update frame format if needed if (presentation_recreate_required) { present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, window_adapt->GetRenderPass()); } } // Add additional layers if needed const VkExtent2D window_size{ .width = layout.screen.GetWidth(), .height = layout.screen.GetHeight(), }; while (layers.size() < framebuffers.size()) { layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, window_size, window_adapt->GetDescriptorSetLayout()); } // Perform the draw window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame); // Advance to next image if (++image_index >= image_count) { image_index = 0; } } vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, VkImageView image_view, VkFormat current_view_format) { const bool format_updated = std::exchange(swapchain_view_format, current_view_format) != current_view_format; if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || format_updated) { WaitIdle(); SetWindowAdaptPass(); } const VkExtent2D extent{ .width = layout.width, .height = layout.height, }; return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass()); } vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, VkRenderPass render_pass) { return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .renderPass = render_pass, .attachmentCount = 1, .pAttachments = &image_view, .width = extent.width, .height = extent.height, .layers = 1, }); } } // namespace Vulkan