diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index e29b7bc8c4..7d094b56fd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -38,6 +38,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"), RENDERER_SAMPLE_SHADING("sample_shading"), RENDERER_FRAME_GEN("frame_gen"), + RENDERER_FRAME_GEN_HDR("frame_gen_hdr"), RENDERER_FRAME_GEN_DUMP_FLOW("frame_gen_dump_flow"), GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"), PICTURE_IN_PICTURE("picture_in_picture"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt index 117336ab64..ea41fb6cbd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt @@ -19,6 +19,8 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { RENDERER_ASTC_DECODE_METHOD("accelerate_astc"), RENDERER_ACCURACY("gpu_accuracy"), RENDERER_RESOLUTION("resolution_setup"), + RENDERER_FRAME_GEN_MULTIPLIER("frame_gen_multiplier"), + RENDERER_FRAME_GEN_FLOW_SCALE("frame_gen_flow_scale"), RENDERER_VSYNC("use_vsync"), RENDERER_SCALING_FILTER("scaling_filter"), RENDERER_ANTI_ALIASING("anti_aliasing"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 22d09769c1..94d9f3441c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -614,6 +614,32 @@ abstract class SettingsItem( descriptionId = R.string.frame_gen_description ) ) + put( + SingleChoiceSetting( + IntSetting.RENDERER_FRAME_GEN_MULTIPLIER, + titleId = R.string.frame_gen_multiplier, + descriptionId = R.string.frame_gen_multiplier_description, + choicesId = R.array.frameGenMultiplierNames, + valuesId = R.array.frameGenMultiplierValues + ) + ) + put( + SliderSetting( + IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE, + titleId = R.string.frame_gen_flow_scale, + descriptionId = R.string.frame_gen_flow_scale_description, + min = 25, + max = 100, + units = "%" + ) + ) + put( + SwitchSetting( + BooleanSetting.RENDERER_FRAME_GEN_HDR, + titleId = R.string.frame_gen_hdr, + descriptionId = R.string.frame_gen_hdr_description + ) + ) put( SwitchSetting( BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 83746f8051..56e60529ca 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -116,6 +116,9 @@ class SettingsFragmentPresenter( } add(BooleanSetting.RENDERER_FRAME_GEN.key) + add(IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key) + add(IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key) + add(BooleanSetting.RENDERER_FRAME_GEN_HDR.key) add(BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key) } } diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 341d89d6b4..2778e76ee7 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -162,6 +162,18 @@ @string/resolution_four + + @string/frame_gen_multiplier_2x + @string/frame_gen_multiplier_3x + @string/frame_gen_multiplier_4x + + + + 2 + 3 + 4 + + @string/renderer_vsync_immediate @string/renderer_vsync_mailbox diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 99252cedb7..b6f0d90af7 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -300,6 +300,15 @@ Install alternative drivers for potentially better performance or accuracy Frame generation Run the Lossless Scaling interpolation shaders on every frame. Incomplete: the interpolated frame is computed but not yet displayed, so this only costs performance for now. + Frame multiplier + How many frames to display for each rendered frame. Higher values cost proportionally more GPU time. + 2x (one generated frame) + 3x (two generated frames) + 4x (three generated frames) + Motion estimation resolution + Resolution of the optical flow pass, as a fraction of the output. Lowering it is the cheapest way to reclaim performance. + HDR interpolation + Treat frames as HDR when interpolating. Only useful on an HDR output. Dump generated frame Write the optical flow mip levels and the interpolated frame to the lossless/debug folder once, for troubleshooting Frame generation unavailable diff --git a/src/common/settings.h b/src/common/settings.h index b1b6cec526..aa3514f137 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -390,6 +390,33 @@ struct Values { SwitchableSetting frame_gen{linkage, false, "frame_gen", Category::Renderer, Specialization::Default, true, true}; + + SwitchableSetting frame_gen_multiplier{linkage, + 2, + 2, + 4, + "frame_gen_multiplier", + Category::Renderer, + Specialization::Countable, + true, + true, + &frame_gen}; + + SwitchableSetting frame_gen_flow_scale{linkage, + 100, + 25, + 100, + "frame_gen_flow_scale", + Category::Renderer, + Specialization::Countable | + Specialization::Percentage, + true, + true, + &frame_gen}; + + SwitchableSetting frame_gen_hdr{linkage, false, "frame_gen_hdr", Category::Renderer, + Specialization::Default, true, true, &frame_gen}; + SwitchableSetting frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow", Category::Renderer}; diff --git a/src/video_core/renderer_vulkan/present/frame_gen.cpp b/src/video_core/renderer_vulkan/present/frame_gen.cpp index c6ab83e018..7ec2364083 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.cpp +++ b/src/video_core/renderer_vulkan/present/frame_gen.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include #include #include @@ -18,10 +19,19 @@ namespace Vulkan { namespace { -constexpr f32 LSFG_FLOW_SCALE = 1.0f; constexpr size_t COLOR_CHANNELS = 4; constexpr u64 LSFG_REQUIRED_FRAMES = 2; +[[nodiscard]] f32 ConfiguredFlowScale() { + return static_cast(Settings::values.frame_gen_flow_scale.GetValue()) / 100.0f; +} + +[[nodiscard]] size_t ConfiguredGenerations() { + const u32 multiplier = std::clamp(Settings::values.frame_gen_multiplier.GetValue(), + LSFG_MIN_MULTIPLIER, LSFG_MAX_MULTIPLIER); + return multiplier - 1; +} + bool IsBlueFirst(VkFormat format) { return format == VK_FORMAT_B8G8R8A8_UNORM || format == VK_FORMAT_B8G8R8A8_SRGB; } @@ -187,7 +197,9 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format) { const VkExtent2D extent{.width = frame->width, .height = frame->height}; if (!chain || built_extent.width != extent.width || built_extent.height != extent.height || - built_format != format) { + built_format != format || built_flow_scale != ConfiguredFlowScale() || + built_hdr != Settings::values.frame_gen_hdr.GetValue() || + built_generations != ConfiguredGenerations()) { Rebuild(device, extent, format); } @@ -208,15 +220,19 @@ void FrameGen::Process(const Device& device, Frame* frame, VkFormat format) { } } -bool FrameGen::HasGeneratedFrame() const { - return chain.has_value() && frame_count >= LSFG_REQUIRED_FRAMES && - Settings::values.frame_gen.GetValue(); +size_t FrameGen::GeneratedFrameCount() const { + if (!chain || frame_count < LSFG_REQUIRED_FRAMES || + !Settings::values.frame_gen.GetValue()) { + return 0; + } + return chain->GenerationCount(); } -void FrameGen::CopyToFrame(Frame* destination) { +void FrameGen::CopyToFrame(Frame* destination, size_t generation) { scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, target = *destination->image](vk::CommandBuffer cmdbuf) { - LsfgImage& source = chain->Output(); + scheduler.Record([this, target = *destination->image, + generation](vk::CommandBuffer cmdbuf) { + LsfgImage& source = chain->Output(generation); const VkExtent2D extent = source.Extent(); const std::array before{ @@ -255,7 +271,13 @@ void FrameGen::CopyToFrame(Frame* destination) { void FrameGen::Rebuild(const Device& device, VkExtent2D extent, VkFormat format) { scheduler.Finish(); chain.reset(); - chain.emplace(device, memory_allocator, *shaders, extent, format, LSFG_FLOW_SCALE); + + built_flow_scale = ConfiguredFlowScale(); + built_hdr = Settings::values.frame_gen_hdr.GetValue(); + built_generations = ConfiguredGenerations(); + + chain.emplace(device, memory_allocator, *shaders, extent, format, built_flow_scale, built_hdr, + built_generations); built_extent = extent; built_format = format; frame_count = 0; @@ -319,7 +341,9 @@ 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)); - dump("generated", chain->Output()); + for (size_t generation = 0; generation < chain->GenerationCount(); ++generation) { + dump("generated" + std::to_string(generation), chain->Output(generation)); + } } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/frame_gen.h b/src/video_core/renderer_vulkan/present/frame_gen.h index 35bb5bb5b0..42991fbf23 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.h +++ b/src/video_core/renderer_vulkan/present/frame_gen.h @@ -23,9 +23,9 @@ public: void Process(const Device& device, Frame* frame, VkFormat format); - [[nodiscard]] bool HasGeneratedFrame() const; + [[nodiscard]] size_t GeneratedFrameCount() const; - void CopyToFrame(Frame* destination); + void CopyToFrame(Frame* destination, size_t generation); private: void Rebuild(const Device& device, VkExtent2D extent, VkFormat format); @@ -38,6 +38,9 @@ private: std::optional chain; VkExtent2D built_extent{}; VkFormat built_format{VK_FORMAT_UNDEFINED}; + f32 built_flow_scale{}; + bool built_hdr{}; + size_t built_generations{}; u64 frame_count{}; bool unavailable{}; bool dumped{}; diff --git a/src/video_core/renderer_vulkan/present/lsfg_beta.cpp b/src/video_core/renderer_vulkan/present/lsfg_beta.cpp index 049948e761..7b0ff37666 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_beta.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_beta.cpp @@ -102,7 +102,7 @@ LsfgBeta::LsfgBeta(const Device& device, MemoryAllocator& memory_allocator, .AddStorageImages(temp2) .Build(device); LsfgDescriptorWriter(descriptor_sets[3]) - .AddUniformBuffer(resources.GetBuffer(LSFG_TIMESTAMP), LsfgResources::BufferSize()) + .AddUniformBuffer(resources.GetBuffer(0.5f), LsfgResources::BufferSize()) .AddSampler(sampler) .AddSampledImages(temp2) .AddStorageImages(out_images) diff --git a/src/video_core/renderer_vulkan/present/lsfg_chain.cpp b/src/video_core/renderer_vulkan/present/lsfg_chain.cpp index a242fda7f7..886f9eb16c 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_chain.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_chain.cpp @@ -11,16 +11,20 @@ namespace Vulkan { namespace { -constexpr u32 DESCRIPTOR_SET_COUNT = 256; +constexpr u32 FIXED_DESCRIPTOR_SETS = 64; +constexpr u32 DESCRIPTOR_SETS_PER_GENERATION = 96; constexpr size_t FIRST_DELTA_LEVEL = 4; } // Anonymous namespace LsfgChain::LsfgChain(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, VkExtent2D extent, VkFormat format, - f32 flow_scale) - : resources{device, memory_allocator, flow_scale}, - descriptor_pool{CreateLsfgDescriptorPool(device, DESCRIPTOR_SET_COUNT)} { + f32 flow_scale, bool is_hdr, size_t generation_count_) + : generation_count{generation_count_}, + resources{device, memory_allocator, flow_scale, is_hdr}, + descriptor_pool{CreateLsfgDescriptorPool( + device, FIXED_DESCRIPTOR_SETS + + DESCRIPTOR_SETS_PER_GENERATION * static_cast(generation_count))} { for (auto& image : frames) { image = LsfgImage(device, memory_allocator, extent, format); } @@ -41,7 +45,7 @@ LsfgChain::LsfgChain(const Device& device, MemoryAllocator& memory_allocator, gamma[i] = LsfgGamma(device, memory_allocator, shaders, resources, descriptor_pool, alpha[level].Outputs(), beta.Output(std::min(level, LSFG_BETA_OUTPUTS - 1)), - i == 0 ? nullptr : &gamma[i - 1].Output()); + i == 0 ? nullptr : &gamma[i - 1].Output(), generation_count); if (i < FIRST_DELTA_LEVEL) { continue; @@ -52,13 +56,13 @@ LsfgChain::LsfgChain(const Device& device, MemoryAllocator& memory_allocator, device, memory_allocator, shaders, resources, descriptor_pool, alpha[level].Outputs(), beta.Output(level), i == FIRST_DELTA_LEVEL ? nullptr : &gamma[i - 1].Output(), i == FIRST_DELTA_LEVEL ? nullptr : &delta[index - 1].Output1(), - i == FIRST_DELTA_LEVEL ? nullptr : &delta[index - 1].Output2()); + i == FIRST_DELTA_LEVEL ? nullptr : &delta[index - 1].Output2(), generation_count); } generate = LsfgGenerate(device, memory_allocator, shaders, resources, descriptor_pool, frames, gamma[LSFG_MIP_LEVELS - 1].Output(), delta[LSFG_DELTA_INSTANCES - 1].Output1(), - delta[LSFG_DELTA_INSTANCES - 1].Output2(), format); + delta[LSFG_DELTA_INSTANCES - 1].Output2(), format, generation_count); } void LsfgChain::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { @@ -70,14 +74,15 @@ void LsfgChain::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { beta.Dispatch(cmdbuf, frame_count); - for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { - gamma[i].Dispatch(cmdbuf, frame_count); - if (i >= FIRST_DELTA_LEVEL) { - delta[i - FIRST_DELTA_LEVEL].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); + } } + generate.Dispatch(cmdbuf, frame_count, generation); } - - generate.Dispatch(cmdbuf, frame_count); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_chain.h b/src/video_core/renderer_vulkan/present/lsfg_chain.h index 91f58ed13a..36dc9fff32 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_chain.h +++ b/src/video_core/renderer_vulkan/present/lsfg_chain.h @@ -24,7 +24,8 @@ constexpr size_t LSFG_DELTA_INSTANCES = 3; class LsfgChain { public: LsfgChain(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, - VkExtent2D extent, VkFormat format, f32 flow_scale); + VkExtent2D extent, VkFormat format, f32 flow_scale, bool is_hdr, + size_t generation_count_); LsfgChain(const LsfgChain&) = delete; LsfgChain& operator=(const LsfgChain&) = delete; @@ -35,8 +36,12 @@ public: return frames[frame_count % frames.size()]; } - [[nodiscard]] LsfgImage& Output() { - return generate.Output(); + [[nodiscard]] LsfgImage& Output(size_t generation) { + return generate.Output(generation); + } + + [[nodiscard]] size_t GenerationCount() const { + return generation_count; } [[nodiscard]] LsfgImage& FlowLevel(size_t level) { @@ -64,6 +69,7 @@ public: } private: + size_t generation_count{}; LsfgResources resources; vk::DescriptorPool descriptor_pool; diff --git a/src/video_core/renderer_vulkan/present/lsfg_common.cpp b/src/video_core/renderer_vulkan/present/lsfg_common.cpp index c84bf544c5..b882a10263 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_common.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_common.cpp @@ -154,8 +154,8 @@ VkBuffer LsfgResources::GetBuffer(f32 timestamp, bool first_iter, bool first_ite .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, + .advanced_color_kind = is_hdr ? 2u : 0u, + .hdr_support = is_hdr ? 1u : 0u, .resolution_inv_scale = 1.0f / flow_scale, .timestamp = timestamp, .ui_threshold = 0.5f, diff --git a/src/video_core/renderer_vulkan/present/lsfg_common.h b/src/video_core/renderer_vulkan/present/lsfg_common.h index d53150d35b..9a2cd3b608 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_common.h +++ b/src/video_core/renderer_vulkan/present/lsfg_common.h @@ -24,8 +24,13 @@ 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); +constexpr size_t LSFG_MIN_MULTIPLIER = 2; +constexpr size_t LSFG_MAX_MULTIPLIER = 4; +constexpr size_t LSFG_MAX_GENERATIONS = LSFG_MAX_MULTIPLIER - 1; + +[[nodiscard]] constexpr f32 LsfgTimestamp(size_t generation, size_t generation_count) { + return static_cast(generation + 1) / static_cast(generation_count + 1); +} class LsfgImage { public: @@ -71,8 +76,10 @@ 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_} {} + LsfgResources(const Device& device_, MemoryAllocator& memory_allocator_, f32 flow_scale_, + bool is_hdr_) + : device{&device_}, memory_allocator{&memory_allocator_}, flow_scale{flow_scale_}, + is_hdr{is_hdr_} {} [[nodiscard]] VkSampler GetSampler( VkSamplerAddressMode address_mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, @@ -87,6 +94,7 @@ private: const Device* device{}; MemoryAllocator* memory_allocator{}; f32 flow_scale{1.0f}; + bool is_hdr{}; std::map samplers; std::map buffers; diff --git a/src/video_core/renderer_vulkan/present/lsfg_delta.cpp b/src/video_core/renderer_vulkan/present/lsfg_delta.cpp index e903e47757..f0a48c49b7 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_delta.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_delta.cpp @@ -25,7 +25,7 @@ LsfgDelta::LsfgDelta(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImageHistory& inputs_, LsfgImage& flow_input_, LsfgImage* previous_gamma_, LsfgImage* previous1_, - LsfgImage* previous2_) + LsfgImage* previous2_, size_t generation_count) : inputs{&inputs_}, flow_input{&flow_input_}, previous_gamma{previous_gamma_}, previous1{previous1_}, previous2{previous2_} { using namespace VideoCore::FrameGen::PerformanceShader; @@ -80,115 +80,123 @@ LsfgDelta::LsfgDelta(const Device& device, MemoryAllocator& memory_allocator, out_image2 = LsfgImage(device, memory_allocator, extent, LSFG_MOTION_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 <= 4; ++i) { - layouts.push_back(passes[i].SetLayout()); - } - for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { - layouts.push_back(passes[5].SetLayout()); - } - for (size_t i = 6; i < LSFG_DELTA_STAGES; ++i) { - layouts.push_back(passes[i].SetLayout()); + for (size_t generation = 0; generation < generation_count; ++generation) { + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + layouts.push_back(passes[0].SetLayout()); + } + for (size_t i = 1; i <= 4; ++i) { + layouts.push_back(passes[i].SetLayout()); + } + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + layouts.push_back(passes[5].SetLayout()); + } + for (size_t i = 6; i < LSFG_DELTA_STAGES; ++i) { + layouts.push_back(passes[i].SetLayout()); + } } owned_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); - size_t next = 0; - for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { - first_descriptor_sets[i] = owned_sets[next++]; - } - for (size_t i = 0; i < 4; ++i) { - descriptor_sets[i] = owned_sets[next++]; - } - for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { - sixth_descriptor_sets[i] = owned_sets[next++]; - } - for (size_t i = 4; i < LSFG_DELTA_STAGES - 2; ++i) { - descriptor_sets[i] = owned_sets[next++]; - } - const VkSampler sampler = resources.GetSampler(); const VkSampler border_sampler = resources.GetSampler( VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true); const VkSampler edge_sampler = resources.GetSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false); - const VkBuffer buffer = - resources.GetBuffer(LSFG_TIMESTAMP, false, previous_gamma == nullptr); + generations.resize(generation_count); + size_t next = 0; + for (size_t generation = 0; generation < generation_count; ++generation) { + Generation& pass = generations[generation]; + const VkBuffer buffer = resources.GetBuffer( + LsfgTimestamp(generation, generation_count), false, previous_gamma == nullptr); - for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { - LsfgDescriptorWriter(first_descriptor_sets[i]) - .AddUniformBuffer(buffer, LsfgResources::BufferSize()) - .AddSampler(border_sampler) - .AddSampler(edge_sampler) - .AddSampledImages((*inputs)[(i + 2) % LSFG_HISTORY_SLOTS]) - .AddSampledImages((*inputs)[i % LSFG_HISTORY_SLOTS]) - .AddSampledImage(previous_gamma) - .AddStorageImages(temp1) + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + pass.first_descriptor_sets[i] = owned_sets[next++]; + } + for (size_t i = 0; i < 4; ++i) { + pass.descriptor_sets[i] = owned_sets[next++]; + } + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + pass.sixth_descriptor_sets[i] = owned_sets[next++]; + } + for (size_t i = 4; i < LSFG_DELTA_STAGES - 2; ++i) { + pass.descriptor_sets[i] = owned_sets[next++]; + } + + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + LsfgDescriptorWriter(pass.first_descriptor_sets[i]) + .AddUniformBuffer(buffer, LsfgResources::BufferSize()) + .AddSampler(border_sampler) + .AddSampler(edge_sampler) + .AddSampledImages((*inputs)[(i + 2) % LSFG_HISTORY_SLOTS]) + .AddSampledImages((*inputs)[i % LSFG_HISTORY_SLOTS]) + .AddSampledImage(previous_gamma) + .AddStorageImages(temp1) + .Build(device); + LsfgDescriptorWriter(pass.sixth_descriptor_sets[i]) + .AddUniformBuffer(buffer, LsfgResources::BufferSize()) + .AddSampler(border_sampler) + .AddSampler(edge_sampler) + .AddSampledImages((*inputs)[(i + 2) % LSFG_HISTORY_SLOTS]) + .AddSampledImages((*inputs)[i % LSFG_HISTORY_SLOTS]) + .AddSampledImage(previous_gamma) + .AddSampledImage(previous1) + .AddStorageImage(temp2[0]) + .Build(device); + } + LsfgDescriptorWriter(pass.descriptor_sets[0]) + .AddSampler(sampler) + .AddSampledImages(temp1) + .AddStorageImages(temp2) .Build(device); - LsfgDescriptorWriter(sixth_descriptor_sets[i]) + LsfgDescriptorWriter(pass.descriptor_sets[1]) + .AddSampler(sampler) + .AddSampledImages(temp2) + .AddStorageImage(temp1[0]) + .AddStorageImage(temp1[1]) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[2]) + .AddSampler(sampler) + .AddSampledImage(temp1[0]) + .AddSampledImage(temp1[1]) + .AddStorageImages(temp2) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[3]) .AddUniformBuffer(buffer, LsfgResources::BufferSize()) - .AddSampler(border_sampler) + .AddSampler(sampler) .AddSampler(edge_sampler) - .AddSampledImages((*inputs)[(i + 2) % LSFG_HISTORY_SLOTS]) - .AddSampledImages((*inputs)[i % LSFG_HISTORY_SLOTS]) + .AddSampledImages(temp2) .AddSampledImage(previous_gamma) - .AddSampledImage(previous1) + .AddSampledImage(*flow_input) + .AddStorageImage(out_image1) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[4]) + .AddSampler(sampler) + .AddSampledImage(temp2[0]) + .AddStorageImage(temp1[0]) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[5]) + .AddSampler(sampler) + .AddSampledImage(temp1[0]) .AddStorageImage(temp2[0]) .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[6]) + .AddSampler(sampler) + .AddSampledImage(temp2[0]) + .AddStorageImage(temp1[0]) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[7]) + .AddUniformBuffer(buffer, LsfgResources::BufferSize()) + .AddSampler(sampler) + .AddSampler(edge_sampler) + .AddSampledImage(temp1[0]) + .AddSampledImage(previous2) + .AddStorageImage(out_image2) + .Build(device); } - LsfgDescriptorWriter(descriptor_sets[0]) - .AddSampler(sampler) - .AddSampledImages(temp1) - .AddStorageImages(temp2) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[1]) - .AddSampler(sampler) - .AddSampledImages(temp2) - .AddStorageImage(temp1[0]) - .AddStorageImage(temp1[1]) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[2]) - .AddSampler(sampler) - .AddSampledImage(temp1[0]) - .AddSampledImage(temp1[1]) - .AddStorageImages(temp2) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[3]) - .AddUniformBuffer(buffer, LsfgResources::BufferSize()) - .AddSampler(sampler) - .AddSampler(edge_sampler) - .AddSampledImages(temp2) - .AddSampledImage(previous_gamma) - .AddSampledImage(*flow_input) - .AddStorageImage(out_image1) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[4]) - .AddSampler(sampler) - .AddSampledImage(temp2[0]) - .AddStorageImage(temp1[0]) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[5]) - .AddSampler(sampler) - .AddSampledImage(temp1[0]) - .AddStorageImage(temp2[0]) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[6]) - .AddSampler(sampler) - .AddSampledImage(temp2[0]) - .AddStorageImage(temp1[0]) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[7]) - .AddUniformBuffer(buffer, LsfgResources::BufferSize()) - .AddSampler(sampler) - .AddSampler(edge_sampler) - .AddSampledImage(temp1[0]) - .AddSampledImage(previous2) - .AddStorageImage(out_image2) - .Build(device); } -void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { +void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation) { + const Generation& pass = generations[generation]; + const VkExtent2D extent = temp1[0].Extent(); const u32 groups_x = GroupCount(extent.width); const u32 groups_y = GroupCount(extent.height); @@ -202,19 +210,19 @@ void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(previous_gamma) .ReadToWriteAll(temp1) .Build(); - passes[0].Bind(cmdbuf, first_descriptor_sets[slot]); + passes[0].Bind(cmdbuf, pass.first_descriptor_sets[slot]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); - passes[1].Bind(cmdbuf, descriptor_sets[0]); + passes[1].Bind(cmdbuf, pass.descriptor_sets[0]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp2).ReadToWriteAll(temp1).Build(); - passes[2].Bind(cmdbuf, descriptor_sets[1]); + passes[2].Bind(cmdbuf, pass.descriptor_sets[1]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); - passes[3].Bind(cmdbuf, descriptor_sets[2]); + passes[3].Bind(cmdbuf, pass.descriptor_sets[2]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -223,7 +231,7 @@ void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(*flow_input) .ReadToWrite(out_image1) .Build(); - passes[4].Bind(cmdbuf, descriptor_sets[3]); + passes[4].Bind(cmdbuf, pass.descriptor_sets[3]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -233,7 +241,7 @@ void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(previous1) .ReadToWriteAll(temp2) .Build(); - passes[5].Bind(cmdbuf, sixth_descriptor_sets[slot]); + passes[5].Bind(cmdbuf, pass.sixth_descriptor_sets[slot]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -241,7 +249,7 @@ void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .ReadToWrite(temp1[0]) .ReadToWrite(temp1[1]) .Build(); - passes[6].Bind(cmdbuf, descriptor_sets[4]); + passes[6].Bind(cmdbuf, pass.descriptor_sets[4]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -249,7 +257,7 @@ void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(temp1[1]) .ReadToWriteAll(temp2) .Build(); - passes[7].Bind(cmdbuf, descriptor_sets[5]); + passes[7].Bind(cmdbuf, pass.descriptor_sets[5]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -257,7 +265,7 @@ void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .ReadToWrite(temp1[0]) .ReadToWrite(temp1[1]) .Build(); - passes[8].Bind(cmdbuf, descriptor_sets[6]); + passes[8].Bind(cmdbuf, pass.descriptor_sets[6]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -266,7 +274,7 @@ void LsfgDelta::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(previous2) .ReadToWrite(out_image2) .Build(); - passes[9].Bind(cmdbuf, descriptor_sets[7]); + passes[9].Bind(cmdbuf, pass.descriptor_sets[7]); cmdbuf.Dispatch(groups_x, groups_y, 1); } diff --git a/src/video_core/renderer_vulkan/present/lsfg_delta.h b/src/video_core/renderer_vulkan/present/lsfg_delta.h index 2681ca2933..f4a014b016 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_delta.h +++ b/src/video_core/renderer_vulkan/present/lsfg_delta.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "common/common_types.h" #include "video_core/renderer_vulkan/present/lsfg_common.h" @@ -22,9 +23,9 @@ public: LsfgDelta(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImageHistory& inputs, LsfgImage& flow_input, LsfgImage* previous_gamma, - LsfgImage* previous1, LsfgImage* previous2); + LsfgImage* previous1, LsfgImage* previous2, size_t generation_count); - void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count); + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation); [[nodiscard]] LsfgImage& Output1() { return out_image1; @@ -35,6 +36,12 @@ public: } private: + struct Generation { + std::array first_descriptor_sets{}; + std::array sixth_descriptor_sets{}; + std::array descriptor_sets{}; + }; + LsfgImageHistory* inputs{}; LsfgImage* flow_input{}; LsfgImage* previous_gamma{}; @@ -42,9 +49,7 @@ private: LsfgImage* previous2{}; std::array passes; - std::array first_descriptor_sets{}; - std::array sixth_descriptor_sets{}; - std::array descriptor_sets{}; + std::vector generations; vk::DescriptorSets owned_sets; std::array temp1; diff --git a/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp b/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp index 0fb6d547b5..bd55b2d1da 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_gamma.cpp @@ -24,7 +24,7 @@ constexpr u32 DISPATCH_TILE_SHIFT = 3; LsfgGamma::LsfgGamma(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImageHistory& inputs_, - LsfgImage& flow_input_, LsfgImage* previous_) + LsfgImage& flow_input_, LsfgImage* previous_, size_t generation_count) : inputs{&inputs_}, flow_input{&flow_input_}, previous{previous_} { using namespace VideoCore::FrameGen::PerformanceShader; @@ -61,68 +61,79 @@ LsfgGamma::LsfgGamma(const Device& device, MemoryAllocator& memory_allocator, out_image = LsfgImage(device, memory_allocator, extent, LSFG_MOTION_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_GAMMA_STAGES; ++i) { - layouts.push_back(passes[i].SetLayout()); + for (size_t generation = 0; generation < generation_count; ++generation) { + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + layouts.push_back(passes[0].SetLayout()); + } + for (size_t i = 1; i < LSFG_GAMMA_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_GAMMA_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); const VkSampler edge_sampler = resources.GetSampler(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false); - const VkBuffer buffer = resources.GetBuffer(LSFG_TIMESTAMP, previous == nullptr); - for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { - LsfgDescriptorWriter(first_descriptor_sets[i]) + generations.resize(generation_count); + size_t next = 0; + for (size_t generation = 0; generation < generation_count; ++generation) { + Generation& pass = generations[generation]; + const VkBuffer buffer = resources.GetBuffer( + LsfgTimestamp(generation, generation_count), previous == nullptr); + + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + pass.first_descriptor_sets[i] = owned_sets[next++]; + } + for (size_t i = 0; i < LSFG_GAMMA_STAGES - 1; ++i) { + pass.descriptor_sets[i] = owned_sets[next++]; + } + + for (size_t i = 0; i < LSFG_HISTORY_SLOTS; ++i) { + LsfgDescriptorWriter(pass.first_descriptor_sets[i]) + .AddUniformBuffer(buffer, LsfgResources::BufferSize()) + .AddSampler(border_sampler) + .AddSampler(edge_sampler) + .AddSampledImages((*inputs)[(i + 2) % LSFG_HISTORY_SLOTS]) + .AddSampledImages((*inputs)[i % LSFG_HISTORY_SLOTS]) + .AddSampledImage(previous) + .AddStorageImages(temp1) + .Build(device); + } + LsfgDescriptorWriter(pass.descriptor_sets[0]) + .AddSampler(sampler) + .AddSampledImages(temp1) + .AddStorageImages(temp2) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[1]) + .AddSampler(sampler) + .AddSampledImages(temp2) + .AddStorageImage(temp1[0]) + .AddStorageImage(temp1[1]) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[2]) + .AddSampler(sampler) + .AddSampledImage(temp1[0]) + .AddSampledImage(temp1[1]) + .AddStorageImages(temp2) + .Build(device); + LsfgDescriptorWriter(pass.descriptor_sets[3]) .AddUniformBuffer(buffer, LsfgResources::BufferSize()) - .AddSampler(border_sampler) + .AddSampler(sampler) .AddSampler(edge_sampler) - .AddSampledImages((*inputs)[(i + 2) % LSFG_HISTORY_SLOTS]) - .AddSampledImages((*inputs)[i % LSFG_HISTORY_SLOTS]) + .AddSampledImages(temp2) .AddSampledImage(previous) - .AddStorageImages(temp1) + .AddSampledImage(*flow_input) + .AddStorageImage(out_image) .Build(device); } - LsfgDescriptorWriter(descriptor_sets[0]) - .AddSampler(sampler) - .AddSampledImages(temp1) - .AddStorageImages(temp2) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[1]) - .AddSampler(sampler) - .AddSampledImages(temp2) - .AddStorageImage(temp1[0]) - .AddStorageImage(temp1[1]) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[2]) - .AddSampler(sampler) - .AddSampledImage(temp1[0]) - .AddSampledImage(temp1[1]) - .AddStorageImages(temp2) - .Build(device); - LsfgDescriptorWriter(descriptor_sets[3]) - .AddUniformBuffer(buffer, LsfgResources::BufferSize()) - .AddSampler(sampler) - .AddSampler(edge_sampler) - .AddSampledImages(temp2) - .AddSampledImage(previous) - .AddSampledImage(*flow_input) - .AddStorageImage(out_image) - .Build(device); } -void LsfgGamma::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { +void LsfgGamma::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation) { + const Generation& pass = generations[generation]; + const VkExtent2D extent = temp1[0].Extent(); const u32 groups_x = GroupCount(extent.width); const u32 groups_y = GroupCount(extent.height); @@ -136,11 +147,11 @@ void LsfgGamma::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(previous) .ReadToWriteAll(temp1) .Build(); - passes[0].Bind(cmdbuf, first_descriptor_sets[slot]); + passes[0].Bind(cmdbuf, pass.first_descriptor_sets[slot]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf).WriteToReadAll(temp1).ReadToWriteAll(temp2).Build(); - passes[1].Bind(cmdbuf, descriptor_sets[0]); + passes[1].Bind(cmdbuf, pass.descriptor_sets[0]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -148,7 +159,7 @@ void LsfgGamma::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .ReadToWrite(temp1[0]) .ReadToWrite(temp1[1]) .Build(); - passes[2].Bind(cmdbuf, descriptor_sets[1]); + passes[2].Bind(cmdbuf, pass.descriptor_sets[1]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -156,7 +167,7 @@ void LsfgGamma::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(temp1[1]) .ReadToWriteAll(temp2) .Build(); - passes[3].Bind(cmdbuf, descriptor_sets[2]); + passes[3].Bind(cmdbuf, pass.descriptor_sets[2]); cmdbuf.Dispatch(groups_x, groups_y, 1); LsfgBarriers(cmdbuf) @@ -165,7 +176,7 @@ void LsfgGamma::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { .WriteToRead(*flow_input) .ReadToWrite(out_image) .Build(); - passes[4].Bind(cmdbuf, descriptor_sets[3]); + passes[4].Bind(cmdbuf, pass.descriptor_sets[3]); cmdbuf.Dispatch(groups_x, groups_y, 1); } diff --git a/src/video_core/renderer_vulkan/present/lsfg_gamma.h b/src/video_core/renderer_vulkan/present/lsfg_gamma.h index 2a2a36d4c2..7acf021669 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_gamma.h +++ b/src/video_core/renderer_vulkan/present/lsfg_gamma.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "common/common_types.h" #include "video_core/renderer_vulkan/present/lsfg_common.h" @@ -21,22 +22,27 @@ public: LsfgGamma() = default; LsfgGamma(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, LsfgResources& resources, vk::DescriptorPool& descriptor_pool, - LsfgImageHistory& inputs, LsfgImage& flow_input, LsfgImage* previous); + LsfgImageHistory& inputs, LsfgImage& flow_input, LsfgImage* previous, + size_t generation_count); - void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count); + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation); [[nodiscard]] LsfgImage& Output() { return out_image; } private: + struct Generation { + std::array first_descriptor_sets{}; + std::array descriptor_sets{}; + }; + LsfgImageHistory* inputs{}; LsfgImage* flow_input{}; LsfgImage* previous{}; std::array passes; - std::array first_descriptor_sets{}; - std::array descriptor_sets{}; + std::vector generations; vk::DescriptorSets owned_sets; std::array temp1; diff --git a/src/video_core/renderer_vulkan/present/lsfg_generate.cpp b/src/video_core/renderer_vulkan/present/lsfg_generate.cpp index 8498f2487d..d6c7adca10 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_generate.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_generate.cpp @@ -25,7 +25,7 @@ LsfgGenerate::LsfgGenerate(const Device& device, MemoryAllocator& memory_allocat const LsfgShaders& shaders, LsfgResources& resources, vk::DescriptorPool& descriptor_pool, LsfgImagePair& frames_, LsfgImage& motion_, LsfgImage& detail1_, LsfgImage& detail2_, - VkFormat format) + VkFormat format, size_t generation_count) : frames{&frames_}, motion{&motion_}, detail1{&detail1_}, detail2{&detail2_} { using namespace VideoCore::FrameGen::PerformanceShader; @@ -35,44 +35,54 @@ LsfgGenerate::LsfgGenerate(const Device& device, MemoryAllocator& memory_allocat {5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE}, {1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); - out_image = LsfgImage(device, memory_allocator, (*frames)[0].Extent(), format); + generations.resize(generation_count); - const std::vector layouts(descriptor_sets.size(), pass.SetLayout()); + const std::vector layouts( + generation_count * generations[0].descriptor_sets.size(), 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); - const VkBuffer buffer = resources.GetBuffer(LSFG_TIMESTAMP); - for (size_t i = 0; i < descriptor_sets.size(); ++i) { - descriptor_sets[i] = owned_sets[i]; - LsfgDescriptorWriter(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(out_image) - .Build(device); + 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); + + 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); + } } } -void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { - const VkExtent2D extent = out_image.Extent(); +void LsfgGenerate::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation) { + Generation& target = generations[generation]; + const VkExtent2D extent = target.out_image.Extent(); LsfgBarriers(cmdbuf) .WriteToReadAll(*frames) .WriteToRead(*motion) .WriteToRead(*detail1) .WriteToRead(*detail2) - .ReadToWrite(out_image) + .ReadToWrite(target.out_image) .Build(); - pass.Bind(cmdbuf, descriptor_sets[frame_count % descriptor_sets.size()]); + pass.Bind(cmdbuf, target.descriptor_sets[frame_count % target.descriptor_sets.size()]); cmdbuf.Dispatch(GroupCount(extent.width), GroupCount(extent.height), 1); } diff --git a/src/video_core/renderer_vulkan/present/lsfg_generate.h b/src/video_core/renderer_vulkan/present/lsfg_generate.h index 0ee90b4b71..67a315801b 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_generate.h +++ b/src/video_core/renderer_vulkan/present/lsfg_generate.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "common/common_types.h" #include "video_core/renderer_vulkan/present/lsfg_common.h" @@ -19,25 +20,29 @@ public: 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); + LsfgImage& detail1, LsfgImage& detail2, VkFormat format, + size_t generation_count); - void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count); + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count, size_t generation); - [[nodiscard]] LsfgImage& Output() { - return out_image; + [[nodiscard]] LsfgImage& Output(size_t generation) { + return generations[generation].out_image; } private: + struct Generation { + std::array descriptor_sets{}; + LsfgImage out_image; + }; + LsfgImagePair* frames{}; LsfgImage* motion{}; LsfgImage* detail1{}; LsfgImage* detail2{}; LsfgPass pass; - std::array descriptor_sets{}; + std::vector generations; vk::DescriptorSets owned_sets; - - LsfgImage out_image; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 01b4269a6d..7dc0043640 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -195,10 +195,15 @@ void RendererVulkan::Composite(std::span framebu frame_gen.Process(device, frame, swapchain.GetImageFormat()); - if (frame_gen.HasGeneratedFrame() && present_manager.CanQueueExtraFrame()) { + const size_t generated_frames = frame_gen.GeneratedFrameCount(); + for (size_t generation = 0; generation < generated_frames; ++generation) { + if (!present_manager.CanQueueExtraFrame()) { + break; + } + Frame* generated = present_manager.GetRenderFrame(); blit_swapchain.PrepareFrame(device, generated, render_window.GetFramebufferLayout()); - frame_gen.CopyToFrame(generated); + frame_gen.CopyToFrame(generated, generation); scheduler.Flush(*generated->render_ready); present_manager.Present(generated); } diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 98ba048783..bbef7b6e9e 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -19,6 +19,8 @@ namespace Vulkan { namespace { +constexpr size_t FRAME_GEN_EXTRA_FRAMES = 2; + bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat format) { const VkFormatProperties props{physical_device.GetFormatProperties(format)}; return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT); @@ -180,12 +182,8 @@ void PresentManager::Present(Frame* frame) { } bool PresentManager::CanQueueExtraFrame() { - if (!use_present_thread) { - return false; - } - - std::scoped_lock lock{queue_mutex, free_mutex}; - return present_queue.empty() && !free_queue.empty(); + std::scoped_lock lock{free_mutex}; + return !free_queue.empty(); } void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat image_view_format, @@ -311,7 +309,11 @@ 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. - image_count = std::min(swapchain.GetImageCount(), 7); + const size_t generated = + Settings::values.frame_gen.GetValue() + ? Settings::values.frame_gen_multiplier.GetValue() * FRAME_GEN_EXTRA_FRAMES + : 0; + image_count = std::min(swapchain.GetImageCount() + generated, 7); } void PresentManager::CopyToSwapchain(Frame* frame) {