Shared sampler and constant buffer pools on lsfg

This commit is contained in:
CamilleLaVey
2026-08-12 23:37:21 -04:00
parent 61f3a1d135
commit 7467506cfa
4 changed files with 152 additions and 28 deletions
@@ -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);
@@ -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<LsfgImage, LSFG_ALPHA_OUTPUTS>;
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<LsfgAlphaOutputs, LSFG_ALPHA_HISTORY>& GetAllOutputs() const {
[[nodiscard]] LsfgImageHistory& Outputs() {
return out_images;
}
private:
const LsfgImage* input{};
LsfgImage* input{};
std::array<LsfgPass, LSFG_ALPHA_STAGES> passes;
std::array<VkDescriptorSet, LSFG_ALPHA_STAGES - 1> descriptor_sets{};
std::array<VkDescriptorSet, LSFG_ALPHA_HISTORY> last_descriptor_sets{};
std::array<VkDescriptorSet, LSFG_HISTORY_SLOTS> last_descriptor_sets{};
vk::DescriptorSets owned_sets;
LsfgImage temp1;
LsfgImage temp2;
std::array<LsfgImage, LSFG_ALPHA_OUTPUTS> temp3;
std::array<LsfgAlphaOutputs, LSFG_ALPHA_HISTORY> out_images;
LsfgImagePair temp3;
LsfgImageHistory out_images;
};
} // namespace Vulkan
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <cstring>
#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<u32, 2> 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<u32, 3> 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<u64>(address_mode) | (static_cast<u64>(compare_op) << 8) |
(static_cast<u64>(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(&timestamp_bits, &timestamp, sizeof(timestamp_bits));
const u64 key = static_cast<u64>(timestamp_bits) | (static_cast<u64>(first_iter) << 32) |
(static_cast<u64>(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<u8> 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());
}
@@ -3,8 +3,10 @@
#pragma once
#include <array>
#include <deque>
#include <initializer_list>
#include <map>
#include <utility>
#include <vector>
@@ -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<f32>(LSFG_GENERATION_COUNT + 1);
class LsfgImage {
public:
@@ -52,12 +60,41 @@ private:
VkImageLayout layout{VK_IMAGE_LAYOUT_UNDEFINED};
};
using LsfgImagePair = std::array<LsfgImage, 2>;
using LsfgImageHistory = std::array<LsfgImagePair, LSFG_HISTORY_SLOTS>;
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<u64, vk::Sampler> samplers;
std::map<u64, vk::Buffer> 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 <typename Range>
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);