diff --git a/src/video_core/renderer_vulkan/present/lsfg_alpha.cpp b/src/video_core/renderer_vulkan/present/lsfg_alpha.cpp index b309441514..48e6624d22 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_alpha.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_alpha.cpp @@ -29,8 +29,8 @@ constexpr u32 DISPATCH_TILE_SHIFT = 3; } // Anonymous namespace LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator, - const LsfgShaders& shaders, vk::DescriptorPool& descriptor_pool, - vk::Sampler& sampler, const LsfgImage& input_) + const LsfgShaders& shaders, LsfgResources& resources, + vk::DescriptorPool& descriptor_pool, LsfgImage& input_) : input{&input_} { using namespace VideoCore::FrameGen::PerformanceShader; @@ -56,9 +56,9 @@ LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator, temp1 = LsfgImage(device, memory_allocator, half_extent); temp2 = LsfgImage(device, memory_allocator, half_extent); - for (size_t i = 0; i < LSFG_ALPHA_OUTPUTS; ++i) { + for (size_t i = 0; i < temp3.size(); ++i) { temp3[i] = LsfgImage(device, memory_allocator, quarter_extent); - for (size_t j = 0; j < LSFG_ALPHA_HISTORY; ++j) { + for (size_t j = 0; j < LSFG_HISTORY_SLOTS; ++j) { out_images[j][i] = LsfgImage(device, memory_allocator, quarter_extent); } } @@ -67,7 +67,7 @@ LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator, for (size_t i = 0; i < LSFG_ALPHA_STAGES - 1; ++i) { layouts.push_back(passes[i].SetLayout()); } - for (size_t i = 0; i < LSFG_ALPHA_HISTORY; ++i) { + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { layouts.push_back(passes[3].SetLayout()); } owned_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); @@ -75,28 +75,30 @@ LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator, for (size_t i = 0; i < LSFG_ALPHA_STAGES - 1; ++i) { descriptor_sets[i] = owned_sets[i]; } - for (size_t i = 0; i < LSFG_ALPHA_HISTORY; ++i) { + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { last_descriptor_sets[i] = owned_sets[LSFG_ALPHA_STAGES - 1 + i]; } + const VkSampler sampler = resources.GetSampler(); + LsfgDescriptorWriter(descriptor_sets[0]) - .AddSampler(*sampler) + .AddSampler(sampler) .AddSampledImage(*input) .AddStorageImage(temp1) .Build(device); LsfgDescriptorWriter(descriptor_sets[1]) - .AddSampler(*sampler) + .AddSampler(sampler) .AddSampledImage(temp1) .AddStorageImage(temp2) .Build(device); LsfgDescriptorWriter(descriptor_sets[2]) - .AddSampler(*sampler) + .AddSampler(sampler) .AddSampledImage(temp2) .AddStorageImages(temp3) .Build(device); - for (size_t i = 0; i < LSFG_ALPHA_HISTORY; ++i) { + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { LsfgDescriptorWriter(last_descriptor_sets[i]) - .AddSampler(*sampler) + .AddSampler(sampler) .AddSampledImages(temp3) .AddStorageImages(out_images[i]) .Build(device); @@ -108,7 +110,7 @@ void LsfgAlpha::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { u32 groups_x = GroupCount(half_extent.width); u32 groups_y = GroupCount(half_extent.height); - LsfgBarriers(cmdbuf).ReadToWrite(temp1).Build(); + LsfgBarriers(cmdbuf).WriteToRead(*input).ReadToWrite(temp1).Build(); passes[0].Bind(cmdbuf, descriptor_sets[0]); cmdbuf.Dispatch(groups_x, groups_y, 1); @@ -124,7 +126,7 @@ void LsfgAlpha::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { passes[2].Bind(cmdbuf, descriptor_sets[2]); cmdbuf.Dispatch(groups_x, groups_y, 1); - const size_t slot = frame_count % LSFG_ALPHA_HISTORY; + const size_t slot = frame_count % LSFG_HISTORY_SLOTS; LsfgBarriers(cmdbuf).WriteToReadAll(temp3).ReadToWriteAll(out_images[slot]).Build(); passes[3].Bind(cmdbuf, last_descriptor_sets[slot]); cmdbuf.Dispatch(groups_x, groups_y, 1); diff --git a/src/video_core/renderer_vulkan/present/lsfg_alpha.h b/src/video_core/renderer_vulkan/present/lsfg_alpha.h index 4505e5d332..670a920f22 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_alpha.h +++ b/src/video_core/renderer_vulkan/present/lsfg_alpha.h @@ -12,42 +12,33 @@ namespace Vulkan { class Device; class LsfgShaders; -class Scheduler; constexpr size_t LSFG_ALPHA_STAGES = 4; -constexpr size_t LSFG_ALPHA_HISTORY = 3; -constexpr size_t LSFG_ALPHA_OUTPUTS = 2; - -using LsfgAlphaOutputs = std::array; class LsfgAlpha { public: LsfgAlpha() = default; LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, - vk::DescriptorPool& descriptor_pool, vk::Sampler& sampler, const LsfgImage& input); + LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImage& input); void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count); - [[nodiscard]] const LsfgAlphaOutputs& GetOutputs(u64 frame_count) const { - return out_images[frame_count % LSFG_ALPHA_HISTORY]; - } - - [[nodiscard]] const std::array& GetAllOutputs() const { + [[nodiscard]] LsfgImageHistory& Outputs() { return out_images; } private: - const LsfgImage* input{}; + LsfgImage* input{}; std::array passes; std::array descriptor_sets{}; - std::array last_descriptor_sets{}; + std::array last_descriptor_sets{}; vk::DescriptorSets owned_sets; LsfgImage temp1; LsfgImage temp2; - std::array temp3; - std::array out_images; + LsfgImagePair temp3; + LsfgImageHistory out_images; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_common.cpp b/src/video_core/renderer_vulkan/present/lsfg_common.cpp index 775a48c691..2d22fba675 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_common.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_common.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include #include "video_core/renderer_vulkan/present/lsfg_common.h" #include "video_core/renderer_vulkan/present/lsfg_shaders.h" @@ -14,6 +15,33 @@ namespace { constexpr u32 DESCRIPTORS_PER_TYPE = 4096; +struct LsfgConstants { + std::array input_offset; + u32 first_iter; + u32 first_iter_s; + u32 advanced_color_kind; + u32 hdr_support; + f32 resolution_inv_scale; + f32 timestamp; + f32 ui_threshold; + std::array padding; +}; +static_assert(sizeof(LsfgConstants) == 48); + +vk::Buffer CreateUniformBuffer(MemoryAllocator& memory_allocator, VkDeviceSize size) { + const VkBufferCreateInfo buffer_ci{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = size, + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + return memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::Upload); +} + VkImageMemoryBarrier MakeBarrier(const LsfgImage& image, VkAccessFlags src_access, VkAccessFlags dst_access) { return VkImageMemoryBarrier{ @@ -60,6 +88,66 @@ LsfgBarriers& LsfgBarriers::ReadToWrite(LsfgImage& image) { return Push(image, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT); } +LsfgBarriers& LsfgBarriers::WriteToRead(LsfgImage* image) { + return image == nullptr ? *this : WriteToRead(*image); +} + +LsfgBarriers& LsfgBarriers::ReadToWrite(LsfgImage* image) { + return image == nullptr ? *this : ReadToWrite(*image); +} + +VkDeviceSize LsfgResources::BufferSize() { + return sizeof(LsfgConstants); +} + +VkSampler LsfgResources::GetSampler(VkSamplerAddressMode address_mode, VkCompareOp compare_op, + bool white_border) { + const u64 key = static_cast(address_mode) | (static_cast(compare_op) << 8) | + (static_cast(white_border) << 16); + + const auto it = samplers.find(key); + if (it != samplers.end()) { + return *it->second; + } + + const auto [entry, inserted] = + samplers.emplace(key, CreateLsfgSampler(*device, address_mode, compare_op, white_border)); + return *entry->second; +} + +VkBuffer LsfgResources::GetBuffer(f32 timestamp, bool first_iter, bool first_iter_s) { + u32 timestamp_bits{}; + std::memcpy(×tamp_bits, ×tamp, sizeof(timestamp_bits)); + const u64 key = static_cast(timestamp_bits) | (static_cast(first_iter) << 32) | + (static_cast(first_iter_s) << 33); + + const auto it = buffers.find(key); + if (it != buffers.end()) { + return *it->second; + } + + vk::Buffer buffer = CreateUniformBuffer(*memory_allocator, sizeof(LsfgConstants)); + + const LsfgConstants constants{ + .input_offset = {0, 0}, + .first_iter = first_iter ? 1u : 0u, + .first_iter_s = first_iter_s ? 1u : 0u, + .advanced_color_kind = 0, + .hdr_support = 0, + .resolution_inv_scale = 1.0f / flow_scale, + .timestamp = timestamp, + .ui_threshold = 0.5f, + .padding = {0, 0, 0}, + }; + + const std::span mapped = buffer.Mapped(); + std::memcpy(mapped.data(), &constants, sizeof(constants)); + buffer.Flush(); + + const auto [entry, inserted] = buffers.emplace(key, std::move(buffer)); + return *entry->second; +} + void LsfgBarriers::Build() { if (barriers.empty()) { return; @@ -100,6 +188,11 @@ LsfgDescriptorWriter& LsfgDescriptorWriter::AddSampledImage(const LsfgImage& ima return PushImage(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_NULL_HANDLE, image.View()); } +LsfgDescriptorWriter& LsfgDescriptorWriter::AddSampledImage(const LsfgImage* image) { + return PushImage(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_NULL_HANDLE, + image == nullptr ? VK_NULL_HANDLE : image->View()); +} + LsfgDescriptorWriter& LsfgDescriptorWriter::AddStorageImage(const LsfgImage& image) { return PushImage(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_NULL_HANDLE, image.View()); } diff --git a/src/video_core/renderer_vulkan/present/lsfg_common.h b/src/video_core/renderer_vulkan/present/lsfg_common.h index 30c489eb82..3042e3d597 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_common.h +++ b/src/video_core/renderer_vulkan/present/lsfg_common.h @@ -3,8 +3,10 @@ #pragma once +#include #include #include +#include #include #include @@ -18,6 +20,12 @@ class Device; class LsfgShaders; constexpr VkFormat LSFG_DEFAULT_FORMAT = VK_FORMAT_R8G8B8A8_UNORM; +constexpr VkFormat LSFG_FLOW_FORMAT = VK_FORMAT_R8_UNORM; +constexpr VkFormat LSFG_MOTION_FORMAT = VK_FORMAT_R16G16B16A16_SFLOAT; + +constexpr size_t LSFG_HISTORY_SLOTS = 3; +constexpr size_t LSFG_GENERATION_COUNT = 1; +constexpr f32 LSFG_TIMESTAMP = 1.0f / static_cast(LSFG_GENERATION_COUNT + 1); class LsfgImage { public: @@ -52,12 +60,41 @@ private: VkImageLayout layout{VK_IMAGE_LAYOUT_UNDEFINED}; }; +using LsfgImagePair = std::array; +using LsfgImageHistory = std::array; + +class LsfgResources { +public: + LsfgResources() = default; + LsfgResources(const Device& device_, MemoryAllocator& memory_allocator_, f32 flow_scale_) + : device{&device_}, memory_allocator{&memory_allocator_}, flow_scale{flow_scale_} {} + + [[nodiscard]] VkSampler GetSampler( + VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + VkCompareOp compare_op = VK_COMPARE_OP_NEVER, bool white_border = false); + + [[nodiscard]] VkBuffer GetBuffer(f32 timestamp = 0.0f, bool first_iter = false, + bool first_iter_s = false); + + [[nodiscard]] static VkDeviceSize BufferSize(); + +private: + const Device* device{}; + MemoryAllocator* memory_allocator{}; + f32 flow_scale{1.0f}; + + std::map samplers; + std::map buffers; +}; + class LsfgBarriers { public: explicit LsfgBarriers(vk::CommandBuffer cmdbuf_) : cmdbuf{cmdbuf_} {} LsfgBarriers& WriteToRead(LsfgImage& image); LsfgBarriers& ReadToWrite(LsfgImage& image); + LsfgBarriers& WriteToRead(LsfgImage* image); + LsfgBarriers& ReadToWrite(LsfgImage* image); template LsfgBarriers& WriteToReadAll(Range& images) { @@ -90,6 +127,7 @@ public: LsfgDescriptorWriter& AddSampler(VkSampler sampler); LsfgDescriptorWriter& AddSampledImage(const LsfgImage& image); + LsfgDescriptorWriter& AddSampledImage(const LsfgImage* image); LsfgDescriptorWriter& AddStorageImage(const LsfgImage& image); LsfgDescriptorWriter& AddUniformBuffer(VkBuffer buffer, VkDeviceSize size);