Adjustments to the frames multiplier

This commit is contained in:
CamilleLaVey
2026-08-13 14:30:03 -04:00
parent 4ed084153f
commit ae395352a7
13 changed files with 280 additions and 141 deletions
@@ -189,6 +189,11 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format, bool
return;
}
if (!frame->storage_view) {
unavailable = true;
return;
}
if (!shaders) {
shaders.emplace(device);
if (!shaders->IsValid()) {
@@ -205,6 +210,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format, bool
}
const u64 count = frame_count++;
last_count = count;
generated = generate && count + 1 >= LSFG_REQUIRED_FRAMES;
scheduler.RequestOutsideRenderPassOperationContext();
@@ -212,7 +218,7 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format, bool
dispatch = generated](vk::CommandBuffer cmdbuf) {
CopyPresentedFrame(cmdbuf, source, chain->Input(count), extent);
if (dispatch) {
chain->Dispatch(cmdbuf, count);
chain->DispatchShared(cmdbuf, count);
}
});
@@ -236,43 +242,15 @@ size_t FrameGen::GeneratedFrameCount() const {
return generated && chain ? chain->GenerationCount() : 0;
}
void FrameGen::CopyToFrame(Frame* destination, size_t generation) {
void FrameGen::GenerateInto(const Device& device, Frame* destination, size_t generation) {
chain->SetTarget(device, generation, destination->index, *destination->storage_view);
const VkExtent2D extent{.width = destination->width, .height = destination->height};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, target = *destination->image,
generation](vk::CommandBuffer cmdbuf) {
LsfgImage& source = chain->Output(generation);
const VkExtent2D extent = source.Extent();
const std::array before{
MakeTransitionBarrier(source.Handle(), VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT, source.Layout(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL),
MakeTransitionBarrier(target, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL),
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, before);
cmdbuf.CopyImage(source.Handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, target,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, MakeCopyRegion(extent));
const std::array after{
MakeTransitionBarrier(source.Handle(), VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_SHADER_WRITE_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL),
MakeTransitionBarrier(target, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL),
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, {}, {}, after);
source.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
scheduler.Record([this, count = last_count, generation, target = destination->index,
image = *destination->image, extent](vk::CommandBuffer cmdbuf) {
chain->DispatchGeneration(cmdbuf, count, generation, target, image, extent);
});
}
@@ -349,9 +327,6 @@ void FrameGen::DumpDebugImages(u64 count) {
dump("gamma6", chain->GammaOutput(LSFG_MIP_LEVELS - 1));
dump("delta2_out1", chain->DeltaOutput1(LSFG_DELTA_INSTANCES - 1));
dump("delta2_out2", chain->DeltaOutput2(LSFG_DELTA_INSTANCES - 1));
for (size_t generation = 0; generation < chain->GenerationCount(); ++generation) {
dump("generated" + std::to_string(generation), chain->Output(generation));
}
}
} // namespace Vulkan
@@ -27,7 +27,7 @@ public:
[[nodiscard]] size_t GeneratedFrameCount() const;
void CopyToFrame(Frame* destination, size_t generation);
void GenerateInto(const Device& device, Frame* destination, size_t generation);
private:
void Rebuild(const Device& device, VkExtent2D extent, VkFormat format);
@@ -43,6 +43,7 @@ private:
f32 built_flow_scale{};
size_t built_generations{};
u64 frame_count{};
u64 last_count{};
bool generated{};
bool unavailable{};
bool dumped{};
@@ -31,10 +31,7 @@ constexpr u32 DISPATCH_TILE_SHIFT = 3;
} // Anonymous namespace
LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator,
const LsfgShaders& shaders, LsfgResources& resources,
vk::DescriptorPool& descriptor_pool, LsfgImage& input_)
: input{&input_} {
LsfgAlphaPasses::LsfgAlphaPasses(const Device& device, const LsfgShaders& shaders) {
using namespace VideoCore::FrameGen::PerformanceShader;
passes[0] = LsfgPass(device, shaders, ALPHA[0],
@@ -53,7 +50,12 @@ LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator,
{{1, VK_DESCRIPTOR_TYPE_SAMPLER},
{2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE},
{2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}});
}
LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator,
const LsfgAlphaPasses& passes_, LsfgResources& resources,
vk::DescriptorPool& descriptor_pool, LsfgImage& input_)
: passes{&passes_}, input{&input_} {
const VkExtent2D half_extent = HalveExtent(input->Extent());
const VkExtent2D quarter_extent = HalveExtent(half_extent);
@@ -68,10 +70,10 @@ LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator,
std::vector<VkDescriptorSetLayout> layouts;
for (size_t i = 0; i < LSFG_ALPHA_STAGES - 1; ++i) {
layouts.push_back(passes[i].SetLayout());
layouts.push_back(passes->Get(i).SetLayout());
}
for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) {
layouts.push_back(passes[3].SetLayout());
layouts.push_back(passes->Get(3).SetLayout());
}
owned_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts);
@@ -108,31 +110,31 @@ LsfgAlpha::LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator,
}
}
void LsfgAlpha::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) {
const VkExtent2D half_extent = temp1.Extent();
u32 groups_x = GroupCount(half_extent.width);
u32 groups_y = GroupCount(half_extent.height);
void LsfgAlpha::PushBarriers(LsfgBarriers& barriers, u64 frame_count, size_t stage) {
switch (stage) {
case 0:
barriers.WriteToRead(*input).ReadToWrite(temp1);
break;
case 1:
barriers.WriteToRead(temp1).ReadToWrite(temp2);
break;
case 2:
barriers.WriteToRead(temp2).ReadToWriteAll(temp3);
break;
default:
barriers.WriteToReadAll(temp3).ReadToWriteAll(out_images[frame_count % LSFG_HISTORY_SLOTS]);
break;
}
}
LsfgBarriers(cmdbuf).WriteToRead(*input).ReadToWrite(temp1).Build();
passes[0].Bind(cmdbuf, descriptor_sets[0]);
cmdbuf.Dispatch(groups_x, groups_y, 1);
void LsfgAlpha::DispatchStage(vk::CommandBuffer cmdbuf, u64 frame_count, size_t stage) {
const VkExtent2D extent = stage < 2 ? temp1.Extent() : temp3[0].Extent();
const VkDescriptorSet set = stage < LSFG_ALPHA_STAGES - 1
? descriptor_sets[stage]
: last_descriptor_sets[frame_count % LSFG_HISTORY_SLOTS];
LsfgBarriers(cmdbuf).WriteToRead(temp1).ReadToWrite(temp2).Build();
passes[1].Bind(cmdbuf, descriptor_sets[1]);
cmdbuf.Dispatch(groups_x, groups_y, 1);
const VkExtent2D quarter_extent = temp3[0].Extent();
groups_x = GroupCount(quarter_extent.width);
groups_y = GroupCount(quarter_extent.height);
LsfgBarriers(cmdbuf).WriteToRead(temp2).ReadToWriteAll(temp3).Build();
passes[2].Bind(cmdbuf, descriptor_sets[2]);
cmdbuf.Dispatch(groups_x, groups_y, 1);
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);
passes->Get(stage).BindSet(cmdbuf, set);
cmdbuf.Dispatch(GroupCount(extent.width), GroupCount(extent.height), 1);
}
} // namespace Vulkan
@@ -18,22 +18,37 @@ class LsfgShaders;
constexpr size_t LSFG_ALPHA_STAGES = 4;
class LsfgAlphaPasses {
public:
LsfgAlphaPasses() = default;
LsfgAlphaPasses(const Device& device, const LsfgShaders& shaders);
[[nodiscard]] const LsfgPass& Get(size_t stage) const {
return passes[stage];
}
private:
std::array<LsfgPass, LSFG_ALPHA_STAGES> passes;
};
class LsfgAlpha {
public:
LsfgAlpha() = default;
LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders,
LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImage& input);
LsfgAlpha(const Device& device, MemoryAllocator& memory_allocator,
const LsfgAlphaPasses& passes_, LsfgResources& resources,
vk::DescriptorPool& descriptor_pool, LsfgImage& input);
void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count);
void PushBarriers(LsfgBarriers& barriers, u64 frame_count, size_t stage);
void DispatchStage(vk::CommandBuffer cmdbuf, u64 frame_count, size_t stage);
[[nodiscard]] LsfgImageHistory& Outputs() {
return out_images;
}
private:
const LsfgAlphaPasses* passes{};
LsfgImage* input{};
std::array<LsfgPass, LSFG_ALPHA_STAGES> passes;
std::array<VkDescriptorSet, LSFG_ALPHA_STAGES - 1> descriptor_sets{};
std::array<VkDescriptorSet, LSFG_HISTORY_SLOTS> last_descriptor_sets{};
vk::DescriptorSets owned_sets;
@@ -15,7 +15,7 @@ namespace Vulkan {
namespace {
constexpr u32 FIXED_DESCRIPTOR_SETS = 64;
constexpr u32 DESCRIPTOR_SETS_PER_GENERATION = 96;
constexpr u32 DESCRIPTOR_SETS_PER_GENERATION = 112;
constexpr size_t FIRST_DELTA_LEVEL = 4;
} // Anonymous namespace
@@ -35,8 +35,9 @@ LsfgChain::LsfgChain(const Device& device, MemoryAllocator& memory_allocator,
mipmaps = LsfgMipmaps(device, memory_allocator, shaders, resources, descriptor_pool, frames,
flow_scale);
alpha_passes = LsfgAlphaPasses(device, shaders);
for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) {
alpha[i] = LsfgAlpha(device, memory_allocator, shaders, resources, descriptor_pool,
alpha[i] = LsfgAlpha(device, memory_allocator, alpha_passes, resources, descriptor_pool,
mipmaps.Output(i));
}
@@ -62,30 +63,40 @@ LsfgChain::LsfgChain(const Device& device, MemoryAllocator& memory_allocator,
i == FIRST_DELTA_LEVEL ? nullptr : &delta[index - 1].Output2(), generation_count);
}
generate = LsfgGenerate(device, memory_allocator, shaders, resources, descriptor_pool, frames,
generate = LsfgGenerate(device, shaders, resources, descriptor_pool, frames,
gamma[LSFG_MIP_LEVELS - 1].Output(),
delta[LSFG_DELTA_INSTANCES - 1].Output1(),
delta[LSFG_DELTA_INSTANCES - 1].Output2(), format, generation_count);
delta[LSFG_DELTA_INSTANCES - 1].Output2(), generation_count);
}
void LsfgChain::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) {
void LsfgChain::DispatchShared(vk::CommandBuffer cmdbuf, u64 frame_count) {
mipmaps.Dispatch(cmdbuf, frame_count);
for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) {
alpha[LSFG_MIP_LEVELS - 1 - i].Dispatch(cmdbuf, frame_count);
for (size_t stage = 0; stage < LSFG_ALPHA_STAGES; ++stage) {
LsfgBarriers barriers(cmdbuf);
for (auto& level : alpha) {
level.PushBarriers(barriers, frame_count, stage);
}
barriers.Build();
alpha_passes.Get(stage).BindPipeline(cmdbuf);
for (auto& level : alpha) {
level.DispatchStage(cmdbuf, frame_count, stage);
}
}
beta.Dispatch(cmdbuf, frame_count);
}
for (size_t generation = 0; generation < generation_count; ++generation) {
for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) {
gamma[i].Dispatch(cmdbuf, frame_count, generation);
if (i >= FIRST_DELTA_LEVEL) {
delta[i - FIRST_DELTA_LEVEL].Dispatch(cmdbuf, frame_count, generation);
}
void LsfgChain::DispatchGeneration(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation,
u32 target, VkImage image, VkExtent2D extent) {
for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) {
gamma[i].Dispatch(cmdbuf, frame_count, generation);
if (i >= FIRST_DELTA_LEVEL) {
delta[i - FIRST_DELTA_LEVEL].Dispatch(cmdbuf, frame_count, generation);
}
generate.Dispatch(cmdbuf, frame_count, generation);
}
generate.Dispatch(cmdbuf, frame_count, generation, target, image, extent);
}
} // namespace Vulkan
@@ -32,16 +32,19 @@ public:
LsfgChain(const LsfgChain&) = delete;
LsfgChain& operator=(const LsfgChain&) = delete;
void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count);
void DispatchShared(vk::CommandBuffer cmdbuf, u64 frame_count);
void DispatchGeneration(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation,
u32 target, VkImage image, VkExtent2D extent);
void SetTarget(const Device& device, size_t generation, u32 target, VkImageView view) {
generate.SetTarget(device, generation, target, view);
}
[[nodiscard]] LsfgImage& Input(u64 frame_count) {
return frames[frame_count % frames.size()];
}
[[nodiscard]] LsfgImage& Output(size_t generation) {
return generate.Output(generation);
}
[[nodiscard]] size_t GenerationCount() const {
return generation_count;
}
@@ -77,6 +80,7 @@ private:
LsfgImagePair frames;
LsfgMipmaps mipmaps;
LsfgAlphaPasses alpha_passes;
std::array<LsfgAlpha, LSFG_MIP_LEVELS> alpha;
LsfgBeta beta;
std::array<LsfgGamma, LSFG_MIP_LEVELS> gamma;
@@ -121,6 +121,28 @@ LsfgBarriers& LsfgBarriers::ReadToWrite(LsfgImage* image) {
return image == nullptr ? *this : ReadToWrite(*image);
}
LsfgBarriers& LsfgBarriers::DiscardToWrite(VkImage image) {
barriers.push_back(VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
});
return *this;
}
VkDeviceSize LsfgResources::BufferSize() {
return sizeof(LsfgConstants);
}
@@ -222,6 +244,10 @@ LsfgDescriptorWriter& LsfgDescriptorWriter::AddStorageImage(const LsfgImage& ima
return PushImage(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_NULL_HANDLE, image.View());
}
LsfgDescriptorWriter& LsfgDescriptorWriter::AddStorageView(VkImageView view) {
return PushImage(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_NULL_HANDLE, view);
}
LsfgDescriptorWriter& LsfgDescriptorWriter::AddUniformBuffer(VkBuffer buffer, VkDeviceSize size) {
buffer_infos.push_back(VkDescriptorBufferInfo{
.buffer = buffer,
@@ -266,7 +292,15 @@ LsfgPass::LsfgPass(const Device& device, const LsfgShaders& shaders, u32 shader_
}
void LsfgPass::Bind(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const {
BindPipeline(cmdbuf);
BindSet(cmdbuf, set);
}
void LsfgPass::BindPipeline(vk::CommandBuffer cmdbuf) const {
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
}
void LsfgPass::BindSet(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const {
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, set, {});
}
@@ -30,6 +30,7 @@ constexpr size_t LSFG_HISTORY_SLOTS = 3;
constexpr size_t LSFG_MIN_MULTIPLIER = 2;
constexpr size_t LSFG_MAX_MULTIPLIER = 4;
constexpr size_t LSFG_MAX_GENERATIONS = LSFG_MAX_MULTIPLIER - 1;
constexpr size_t LSFG_MAX_TARGETS = 7;
[[nodiscard]] constexpr f32 LsfgTimestamp(size_t generation, size_t generation_count) {
return static_cast<f32>(generation + 1) / static_cast<f32>(generation_count + 1);
@@ -108,6 +109,7 @@ public:
LsfgBarriers& ReadToWrite(LsfgImage& image);
LsfgBarriers& WriteToRead(LsfgImage* image);
LsfgBarriers& ReadToWrite(LsfgImage* image);
LsfgBarriers& DiscardToWrite(VkImage image);
template <typename Range>
LsfgBarriers& WriteToReadAll(Range& images) {
@@ -142,6 +144,7 @@ public:
LsfgDescriptorWriter& AddSampledImage(const LsfgImage& image);
LsfgDescriptorWriter& AddSampledImage(const LsfgImage* image);
LsfgDescriptorWriter& AddStorageImage(const LsfgImage& image);
LsfgDescriptorWriter& AddStorageView(VkImageView view);
LsfgDescriptorWriter& AddUniformBuffer(VkBuffer buffer, VkDeviceSize size);
template <typename Range>
@@ -189,6 +192,8 @@ public:
}
void Bind(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const;
void BindPipeline(vk::CommandBuffer cmdbuf) const;
void BindSet(vk::CommandBuffer cmdbuf, VkDescriptorSet set) const;
private:
vk::DescriptorSetLayout descriptor_set_layout;
@@ -22,13 +22,34 @@ constexpr u32 DISPATCH_TILE_SHIFT = 4;
return (size + (1u << DISPATCH_TILE_SHIFT) - 1) >> DISPATCH_TILE_SHIFT;
}
VkImageMemoryBarrier MakeTargetBarrier(VkImage image, VkAccessFlags src_access,
VkAccessFlags dst_access, VkImageLayout old_layout) {
return VkImageMemoryBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = src_access,
.dstAccessMask = dst_access,
.oldLayout = old_layout,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
}
} // Anonymous namespace
LsfgGenerate::LsfgGenerate(const Device& device, MemoryAllocator& memory_allocator,
const LsfgShaders& shaders, LsfgResources& resources,
vk::DescriptorPool& descriptor_pool, LsfgImagePair& frames_,
LsfgImage& motion_, LsfgImage& detail1_, LsfgImage& detail2_,
VkFormat format, size_t generation_count)
LsfgGenerate::LsfgGenerate(const Device& device, const LsfgShaders& shaders,
LsfgResources& resources, vk::DescriptorPool& descriptor_pool,
LsfgImagePair& frames_, LsfgImage& motion_, LsfgImage& detail1_,
LsfgImage& detail2_, size_t generation_count)
: frames{&frames_}, motion{&motion_}, detail1{&detail1_}, detail2{&detail2_} {
using namespace VideoCore::FrameGen::PerformanceShader;
@@ -38,55 +59,74 @@ LsfgGenerate::LsfgGenerate(const Device& device, MemoryAllocator& memory_allocat
{5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE},
{1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}});
sampler = resources.GetSampler();
edge_sampler =
resources.GetSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false);
generations.resize(generation_count);
const std::vector<VkDescriptorSetLayout> layouts(
generation_count * generations[0].descriptor_sets.size(), pass.SetLayout());
generation_count * LSFG_MAX_TARGETS * 2, pass.SetLayout());
owned_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts);
const VkSampler sampler = resources.GetSampler();
const VkSampler edge_sampler =
resources.GetSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false);
size_t next = 0;
for (size_t generation = 0; generation < generation_count; ++generation) {
Generation& target = generations[generation];
target.out_image = LsfgImage(device, memory_allocator, (*frames)[0].Extent(), format);
target.buffer = resources.GetBuffer(LsfgTimestamp(generation, generation_count));
const VkBuffer buffer =
resources.GetBuffer(LsfgTimestamp(generation, generation_count));
for (size_t i = 0; i < target.descriptor_sets.size(); ++i) {
target.descriptor_sets[i] = owned_sets[next++];
LsfgDescriptorWriter(target.descriptor_sets[i])
.AddUniformBuffer(buffer, LsfgResources::BufferSize())
.AddSampler(sampler)
.AddSampler(edge_sampler)
.AddSampledImage((*frames)[1 - i])
.AddSampledImage((*frames)[i])
.AddSampledImage(*motion)
.AddSampledImage(*detail1)
.AddSampledImage(*detail2)
.AddStorageImage(target.out_image)
.Build(device);
for (auto& slot : target.targets) {
for (auto& set : slot.descriptor_sets) {
set = owned_sets[next++];
}
}
}
}
void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation) {
Generation& target = generations[generation];
const VkExtent2D extent = target.out_image.Extent();
void LsfgGenerate::SetTarget(const Device& device, size_t generation, u32 target, VkImageView view) {
Target& slot = generations[generation].targets[target];
if (slot.view == view) {
return;
}
slot.view = view;
for (size_t i = 0; i < slot.descriptor_sets.size(); ++i) {
LsfgDescriptorWriter(slot.descriptor_sets[i])
.AddUniformBuffer(generations[generation].buffer, LsfgResources::BufferSize())
.AddSampler(sampler)
.AddSampler(edge_sampler)
.AddSampledImage((*frames)[1 - i])
.AddSampledImage((*frames)[i])
.AddSampledImage(*motion)
.AddSampledImage(*detail1)
.AddSampledImage(*detail2)
.AddStorageView(view)
.Build(device);
}
}
void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation,
u32 target, VkImage image, VkExtent2D extent) {
const Target& slot = generations[generation].targets[target];
LsfgBarriers(cmdbuf)
.WriteToReadAll(*frames)
.WriteToRead(*motion)
.WriteToRead(*detail1)
.WriteToRead(*detail2)
.ReadToWrite(target.out_image)
.DiscardToWrite(image)
.Build();
pass.Bind(cmdbuf, target.descriptor_sets[frame_count % target.descriptor_sets.size()]);
pass.Bind(cmdbuf, slot.descriptor_sets[frame_count % slot.descriptor_sets.size()]);
cmdbuf.Dispatch(GroupCount(extent.width), GroupCount(extent.height), 1);
const std::array after{MakeTargetBarrier(
image, VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT,
VK_IMAGE_LAYOUT_GENERAL)};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, {}, {}, after);
}
} // namespace Vulkan
@@ -20,28 +20,32 @@ class LsfgShaders;
class LsfgGenerate {
public:
LsfgGenerate() = default;
LsfgGenerate(const Device& device, MemoryAllocator& memory_allocator,
const LsfgShaders& shaders, LsfgResources& resources,
LsfgGenerate(const Device& device, const LsfgShaders& shaders, LsfgResources& resources,
vk::DescriptorPool& descriptor_pool, LsfgImagePair& frames, LsfgImage& motion,
LsfgImage& detail1, LsfgImage& detail2, VkFormat format,
size_t generation_count);
LsfgImage& detail1, LsfgImage& detail2, size_t generation_count);
void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation);
void SetTarget(const Device& device, size_t generation, u32 target, VkImageView view);
[[nodiscard]] LsfgImage& Output(size_t generation) {
return generations[generation].out_image;
}
void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation, u32 target,
VkImage image, VkExtent2D extent);
private:
struct Generation {
struct Target {
std::array<VkDescriptorSet, 2> descriptor_sets{};
LsfgImage out_image;
VkImageView view{};
};
struct Generation {
std::array<Target, LSFG_MAX_TARGETS> targets{};
VkBuffer buffer{};
};
LsfgImagePair* frames{};
LsfgImage* motion{};
LsfgImage* detail1{};
LsfgImage* detail2{};
VkSampler sampler{};
VkSampler edge_sampler{};
LsfgPass pass;
std::vector<Generation> generations;
@@ -202,7 +202,7 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
for (size_t generation = 0; generation < generated_frames; ++generation) {
Frame* generated = present_manager.GetRenderFrame();
blit_swapchain.PrepareFrame(device, generated, render_window.GetFramebufferLayout());
frame_gen.CopyToFrame(generated, generation);
frame_gen.GenerateInto(device, generated, generation);
scheduler.Flush(*generated->render_ready);
present_manager.Present(generated);
}
@@ -7,6 +7,7 @@
#include "common/settings.h"
#include "common/thread.h"
#include "core/frontend/emu_window.h"
#include "video_core/renderer_vulkan/present/lsfg_common.h"
#include "video_core/renderer_vulkan/vk_present_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -19,7 +20,16 @@ namespace Vulkan {
namespace {
constexpr size_t FRAME_GEN_EXTRA_FRAMES = 2;
constexpr size_t MAX_FRAMES_IN_FLIGHT = 7;
static_assert(MAX_FRAMES_IN_FLIGHT <= LSFG_MAX_TARGETS);
bool CanStoreToFrame(const vk::PhysicalDevice& physical_device, VkFormat format) {
if (!Settings::values.frame_gen.GetValue()) {
return false;
}
const VkFormatProperties props{physical_device.GetFormatProperties(format)};
return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
}
bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat format) {
const VkFormatProperties props{physical_device.GetFormatProperties(format)};
@@ -112,6 +122,7 @@ PresentManager::PresentManager(const vk::Instance& instance_,
, swapchain{swapchain_}
, surface{surface_}
, blit_supported{CanBlitToSwapchain(device.GetPhysical(), swapchain.GetImageViewFormat())}
, storage_supported{CanStoreToFrame(device.GetPhysical(), swapchain.GetImageFormat())}
, use_present_thread{Settings::values.async_presentation.GetValue()}
{
SetImageCount();
@@ -129,6 +140,7 @@ PresentManager::PresentManager(const vk::Instance& instance_,
frames.resize(image_count);
for (u32 i = 0; i < frames.size(); i++) {
Frame& frame = frames[i];
frame.index = i;
frame.cmdbuf = vk::CommandBuffer{cmdbuffers[i], device.GetDispatchLoader()};
frame.render_ready = dld.CreateSemaphore({
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
@@ -193,6 +205,9 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat
frame->width = width;
frame->height = height;
const VkImageUsageFlags storage_usage =
storage_supported ? static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_STORAGE_BIT) : 0;
frame->image = memory_allocator.CreateImage({
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
@@ -210,7 +225,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | storage_usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -241,6 +256,33 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat
},
});
frame->storage_view = vk::ImageView{};
if (storage_supported) {
frame->storage_view = dld.CreateImageView({
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = *frame->image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = swapchain.GetImageFormat(),
.components =
{
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
});
}
const VkImageView image_view{*frame->image_view};
frame->framebuffer = dld.CreateFramebuffer({
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
@@ -309,11 +351,14 @@ void PresentManager::SetImageCount() {
// We cannot have more than 7 images in flight at any given time.
// FRAMES_IN_FLIGHT is 8, and the cache TICKS_TO_DESTROY is 8.
// Mali drivers will give us 6.
const size_t generated =
const size_t generations =
Settings::values.frame_gen.GetValue()
? Settings::values.frame_gen_multiplier.GetValue() * FRAME_GEN_EXTRA_FRAMES
? static_cast<size_t>(Settings::values.frame_gen_multiplier.GetValue()) - 1
: 0;
image_count = std::min<size_t>(swapchain.GetImageCount() + generated, 7);
const size_t frames_per_composite = generations + 1;
image_count = std::min<size_t>(
std::max<size_t>(swapchain.GetImageCount() + generations, frames_per_composite * 2),
MAX_FRAMES_IN_FLIGHT);
}
void PresentManager::CopyToSwapchain(Frame* frame) {
@@ -28,8 +28,10 @@ class Swapchain;
struct Frame {
u32 width;
u32 height;
u32 index;
vk::Image image;
vk::ImageView image_view;
vk::ImageView storage_view;
vk::Framebuffer framebuffer;
vk::CommandBuffer cmdbuf;
vk::Semaphore render_ready;
@@ -93,6 +95,7 @@ private:
std::mutex free_mutex;
std::jthread present_thread;
bool blit_supported;
bool storage_supported;
bool use_present_thread;
std::size_t image_count{};
};