// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2025 lsfg-vk // SPDX-License-Identifier: GPL-3.0-or-later #include #include "video_core/frame_gen/lossless_dll.h" #include "video_core/renderer_vulkan/present/lsfg_beta.h" #include "video_core/renderer_vulkan/present/lsfg_shaders.h" #include "video_core/renderer_vulkan/present/util.h" #include "video_core/vulkan_common/vulkan_device.h" namespace Vulkan { namespace { constexpr u32 DISPATCH_TILE_SHIFT = 3; constexpr u32 OUTPUT_TILE_SHIFT = 5; [[nodiscard]] u32 GroupCount(u32 size, u32 shift) { return (size + (1u << shift) - 1) >> shift; } } // Anonymous namespace LsfgBeta::LsfgBeta(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImageHistory& inputs_) : inputs{&inputs_} { using namespace VideoCore::FrameGen::PerformanceShader; passes[0] = LsfgPass(device, shaders, BETA[0], {{1, VK_DESCRIPTOR_TYPE_SAMPLER}, {6, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE}, {2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); for (size_t i = 1; i < LSFG_BETA_STAGES - 1; ++i) { passes[i] = LsfgPass(device, shaders, BETA[i], {{1, VK_DESCRIPTOR_TYPE_SAMPLER}, {2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE}, {2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); } passes[4] = LsfgPass(device, shaders, BETA[4], {{1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}, {1, VK_DESCRIPTOR_TYPE_SAMPLER}, {2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE}, {6, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); const VkExtent2D extent = (*inputs)[0][0].Extent(); for (size_t i = 0; i < temp1.size(); ++i) { temp1[i] = LsfgImage(device, memory_allocator, extent); temp2[i] = LsfgImage(device, memory_allocator, extent); } for (size_t i = 0; i < LSFG_BETA_OUTPUTS; ++i) { const VkExtent2D level_extent{ .width = extent.width >> i, .height = extent.height >> i, }; out_images[i] = LsfgImage(device, memory_allocator, level_extent, LSFG_FLOW_FORMAT); } std::vector layouts; for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { layouts.push_back(passes[0].SetLayout()); } for (size_t i = 1; i < LSFG_BETA_STAGES; ++i) { layouts.push_back(passes[i].SetLayout()); } owned_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { first_descriptor_sets[i] = owned_sets[i]; } for (size_t i = 0; i < LSFG_BETA_STAGES - 1; ++i) { descriptor_sets[i] = owned_sets[LSFG_HISTORY_SLOTS + i]; } const VkSampler sampler = resources.GetSampler(); const VkSampler border_sampler = resources.GetSampler( VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true); for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { LsfgDescriptorWriter(first_descriptor_sets[i]) .AddSampler(border_sampler) .AddSampledImages((*inputs)[(i + 1) % LSFG_HISTORY_SLOTS]) .AddSampledImages((*inputs)[(i + 2) % LSFG_HISTORY_SLOTS]) .AddSampledImages((*inputs)[i % LSFG_HISTORY_SLOTS]) .AddStorageImages(temp1) .Build(device); } LsfgDescriptorWriter(descriptor_sets[0]) .AddSampler(sampler) .AddSampledImages(temp1) .AddStorageImages(temp2) .Build(device); LsfgDescriptorWriter(descriptor_sets[1]) .AddSampler(sampler) .AddSampledImages(temp2) .AddStorageImages(temp1) .Build(device); LsfgDescriptorWriter(descriptor_sets[2]) .AddSampler(sampler) .AddSampledImages(temp1) .AddStorageImages(temp2) .Build(device); LsfgDescriptorWriter(descriptor_sets[3]) .AddUniformBuffer(resources.GetBuffer(0.5f), LsfgResources::BufferSize()) .AddSampler(sampler) .AddSampledImages(temp2) .AddStorageImages(out_images) .Build(device); } void LsfgBeta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { const VkExtent2D extent = temp1[0].Extent(); const u32 groups_x = GroupCount(extent.width, DISPATCH_TILE_SHIFT); const u32 groups_y = GroupCount(extent.height, DISPATCH_TILE_SHIFT); LsfgBarriers barriers(cmdbuf); for (auto& slot : *inputs) { barriers.WriteToReadAll(slot); } barriers.ReadToWriteAll(temp1).Build(); passes[0].Bind(cmdbuf, first_descriptor_sets[frame_count % LSFG_HISTORY_SLOTS]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); passes[1].Bind(cmdbuf, descriptor_sets[0]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp2).ReadToWriteAll(temp1).Build(); passes[2].Bind(cmdbuf, descriptor_sets[1]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); passes[3].Bind(cmdbuf, descriptor_sets[2]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp2).ReadToWriteAll(out_images).Build(); passes[4].Bind(cmdbuf, descriptor_sets[3]); cmdbuf.Dispatch(GroupCount(extent.width, OUTPUT_TILE_SHIFT), GroupCount(extent.height, OUTPUT_TILE_SHIFT), 1); } } // namespace Vulkan