diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 13e95bb704..f20f58e638 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -131,6 +131,8 @@ add_library(video_core STATIC renderer_vulkan/present/lsfg_alpha.h renderer_vulkan/present/lsfg_beta.cpp renderer_vulkan/present/lsfg_beta.h + renderer_vulkan/present/lsfg_chain.cpp + renderer_vulkan/present/lsfg_chain.h renderer_vulkan/present/lsfg_common.cpp renderer_vulkan/present/lsfg_common.h renderer_vulkan/present/lsfg_delta.cpp diff --git a/src/video_core/renderer_vulkan/present/frame_gen.cpp b/src/video_core/renderer_vulkan/present/frame_gen.cpp index 0386c5687a..ff66ee5def 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.cpp +++ b/src/video_core/renderer_vulkan/present/frame_gen.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include #include "common/fs/file.h" #include "common/fs/fs.h" @@ -18,26 +19,124 @@ namespace Vulkan { namespace { constexpr f32 LSFG_FLOW_SCALE = 1.0f; +constexpr size_t COLOR_CHANNELS = 4; -void WriteGrayscalePgm(const std::filesystem::path& path, VkExtent2D extent, - std::span pixels) { +bool IsBlueFirst(VkFormat format) { + return format == VK_FORMAT_B8G8R8A8_UNORM || format == VK_FORMAT_B8G8R8A8_SRGB; +} + +void WritePortablePixmap(const std::filesystem::path& path, const std::string& magic, + VkExtent2D extent, std::span pixels) { Common::FS::IOFile file{path, Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile}; if (!file.IsOpen()) { return; } - const std::string header = - "P5\n" + std::to_string(extent.width) + " " + std::to_string(extent.height) + "\n255\n"; + const std::string header = magic + "\n" + std::to_string(extent.width) + " " + + std::to_string(extent.height) + "\n255\n"; if (file.Write(header) != header.size()) { return; } - const size_t expected = static_cast(extent.width) * extent.height; - void(file.Write(pixels.subspan(0, std::min(expected, pixels.size())))); + void(file.Write(pixels)); void(file.Flush()); } +void WriteGrayscalePgm(const std::filesystem::path& path, VkExtent2D extent, + std::span pixels) { + const size_t expected = static_cast(extent.width) * extent.height; + WritePortablePixmap(path, "P5", extent, pixels.subspan(0, std::min(expected, pixels.size()))); +} + +void WriteColorPpm(const std::filesystem::path& path, VkExtent2D extent, + std::span pixels, bool blue_first) { + const size_t pixel_count = static_cast(extent.width) * extent.height; + if (pixels.size() < pixel_count * COLOR_CHANNELS) { + return; + } + + std::vector rgb(pixel_count * 3); + for (size_t i = 0; i < pixel_count; ++i) { + const u8 first = pixels[i * COLOR_CHANNELS]; + const u8 green = pixels[i * COLOR_CHANNELS + 1]; + const u8 third = pixels[i * COLOR_CHANNELS + 2]; + rgb[i * 3] = blue_first ? third : first; + rgb[i * 3 + 1] = green; + rgb[i * 3 + 2] = blue_first ? first : third; + } + + WritePortablePixmap(path, "P6", extent, rgb); +} + +void CopyPresentedFrame(vk::CommandBuffer cmdbuf, VkImage source, LsfgImage& destination, + VkExtent2D extent) { + const auto make_barrier = [](VkImage image, VkAccessFlags src_access, VkAccessFlags dst_access, + VkImageLayout old_layout, VkImageLayout new_layout) { + return VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_access, + .dstAccessMask = dst_access, + .oldLayout = old_layout, + .newLayout = new_layout, + .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, + }, + }; + }; + + const std::array before{ + make_barrier(source, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL), + make_barrier(destination.Handle(), VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + destination.Layout(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL), + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, before); + + const VkImageCopy region{ + .srcSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffset = {}, + .dstSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffset = {}, + .extent = {.width = extent.width, .height = extent.height, .depth = 1}, + }; + cmdbuf.CopyImage(source, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, destination.Handle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); + + const std::array after{ + make_barrier(source, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL), + make_barrier(destination.Handle(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL), + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, {}, {}, after); + + destination.SetLayout(VK_IMAGE_LAYOUT_GENERAL); +} + } // Anonymous namespace FrameGen::FrameGen(MemoryAllocator& memory_allocator_, Scheduler& scheduler_) @@ -45,7 +144,7 @@ FrameGen::FrameGen(MemoryAllocator& memory_allocator_, Scheduler& scheduler_) FrameGen::~FrameGen() = default; -void FrameGen::Process(const Device& device, Frame* frame) { +void FrameGen::Process(const Device& device, Frame* frame, VkFormat format) { if (unavailable || !Settings::values.frame_gen.GetValue()) { return; } @@ -59,54 +158,77 @@ void FrameGen::Process(const Device& device, Frame* frame) { } const VkExtent2D extent{.width = frame->width, .height = frame->height}; - if (!mipmaps || built_extent.width != extent.width || built_extent.height != extent.height) { - Rebuild(device, extent); + if (!chain || built_extent.width != extent.width || built_extent.height != extent.height || + built_format != format) { + Rebuild(device, extent, format); } - mipmaps->Dispatch(device, scheduler, *frame->image, *frame->image_view, frame_count); - ++frame_count; + const u64 count = frame_count++; + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([this, source = *frame->image, extent, count](vk::CommandBuffer cmdbuf) { + CopyPresentedFrame(cmdbuf, source, chain->Input(count), extent); + chain->Dispatch(cmdbuf, count); + }); const bool dump_requested = Settings::values.frame_gen_dump_flow.GetValue(); if (!dump_requested) { dumped = false; } else if (!dumped) { - DumpFlowPyramid(device); + DumpDebugImages(); dumped = true; } } -void FrameGen::Rebuild(const Device& device, VkExtent2D extent) { +void FrameGen::Rebuild(const Device& device, VkExtent2D extent, VkFormat format) { scheduler.Finish(); - mipmaps.emplace(device, memory_allocator, *shaders, extent, LSFG_FLOW_SCALE); + chain.reset(); + chain.emplace(device, memory_allocator, *shaders, extent, format, LSFG_FLOW_SCALE); built_extent = extent; + built_format = format; frame_count = 0; } -void FrameGen::DumpFlowPyramid(const Device& device) { +void FrameGen::DumpDebugImages() { const std::filesystem::path directory = Common::FS::GetEdenPath(Common::FS::EdenPath::LosslessDir) / "debug"; if (!Common::FS::CreateDirs(directory)) { return; } - for (size_t level = 0; level < LSFG_MIP_LEVELS; ++level) { - const VkExtent2D extent = mipmaps->GetLevelExtent(level); - const VkDeviceSize size = static_cast(extent.width) * extent.height; - vk::Buffer readback = CreateWrappedBuffer(memory_allocator, size, MemoryUsage::Download); + const auto readback = [&](LsfgImage& image, VkDeviceSize size) { + vk::Buffer buffer = CreateWrappedBuffer(memory_allocator, size, MemoryUsage::Download); + const VkExtent2D extent = image.Extent(); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([image = mipmaps->GetLevelImage(level), dst = *readback, + scheduler.Record([handle = image.Handle(), dst = *buffer, extent](vk::CommandBuffer cmdbuf) { - DownloadColorImage(cmdbuf, image, dst, - VkExtent3D{.width = extent.width, .height = extent.height, + DownloadColorImage(cmdbuf, handle, dst, + VkExtent3D{.width = extent.width, + .height = extent.height, .depth = 1}); }); scheduler.Finish(); - readback.Invalidate(); + buffer.Invalidate(); + return buffer; + }; + + for (size_t level = 0; level < LSFG_MIP_LEVELS; ++level) { + LsfgImage& image = chain->FlowLevel(level); + const VkExtent2D extent = image.Extent(); + const VkDeviceSize size = static_cast(extent.width) * extent.height; + vk::Buffer buffer = readback(image, size); WriteGrayscalePgm(directory / ("flow_mip" + std::to_string(level) + ".pgm"), extent, - readback.Mapped()); + buffer.Mapped()); } + + LsfgImage& output = chain->Output(); + const VkExtent2D extent = output.Extent(); + const VkDeviceSize size = + static_cast(extent.width) * extent.height * COLOR_CHANNELS; + vk::Buffer buffer = readback(output, size); + WriteColorPpm(directory / "generated.ppm", extent, buffer.Mapped(), IsBlueFirst(built_format)); } } // 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 e81a4fcba9..b2c3c95531 100644 --- a/src/video_core/renderer_vulkan/present/frame_gen.h +++ b/src/video_core/renderer_vulkan/present/frame_gen.h @@ -6,7 +6,7 @@ #include #include "common/common_types.h" -#include "video_core/renderer_vulkan/present/lsfg_mipmaps.h" +#include "video_core/renderer_vulkan/present/lsfg_chain.h" #include "video_core/renderer_vulkan/present/lsfg_shaders.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" @@ -21,18 +21,19 @@ public: explicit FrameGen(MemoryAllocator& memory_allocator, Scheduler& scheduler); ~FrameGen(); - void Process(const Device& device, Frame* frame); + void Process(const Device& device, Frame* frame, VkFormat format); private: - void Rebuild(const Device& device, VkExtent2D extent); - void DumpFlowPyramid(const Device& device); + void Rebuild(const Device& device, VkExtent2D extent, VkFormat format); + void DumpDebugImages(); MemoryAllocator& memory_allocator; Scheduler& scheduler; std::optional shaders; - std::optional mipmaps; + std::optional chain; VkExtent2D built_extent{}; + VkFormat built_format{VK_FORMAT_UNDEFINED}; u64 frame_count{}; bool unavailable{}; bool dumped{}; diff --git a/src/video_core/renderer_vulkan/present/lsfg_chain.cpp b/src/video_core/renderer_vulkan/present/lsfg_chain.cpp new file mode 100644 index 0000000000..a242fda7f7 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/lsfg_chain.cpp @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#include "video_core/renderer_vulkan/present/lsfg_chain.h" +#include "video_core/renderer_vulkan/present/lsfg_shaders.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +namespace { + +constexpr u32 DESCRIPTOR_SET_COUNT = 256; +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)} { + for (auto& image : frames) { + image = LsfgImage(device, memory_allocator, extent, format); + } + + mipmaps = LsfgMipmaps(device, memory_allocator, shaders, resources, descriptor_pool, frames, + flow_scale); + + for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { + alpha[i] = LsfgAlpha(device, memory_allocator, shaders, resources, descriptor_pool, + mipmaps.Output(i)); + } + + beta = LsfgBeta(device, memory_allocator, shaders, resources, descriptor_pool, + alpha[0].Outputs()); + + for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { + const size_t level = LSFG_MIP_LEVELS - 1 - i; + 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()); + + if (i < FIRST_DELTA_LEVEL) { + continue; + } + + const size_t index = i - FIRST_DELTA_LEVEL; + delta[index] = LsfgDelta( + 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()); + } + + 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); +} + +void LsfgChain::Dispatch(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); + } + + 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); + } + } + + 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 new file mode 100644 index 0000000000..be8026ea71 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/lsfg_chain.h @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "video_core/renderer_vulkan/present/lsfg_alpha.h" +#include "video_core/renderer_vulkan/present/lsfg_beta.h" +#include "video_core/renderer_vulkan/present/lsfg_common.h" +#include "video_core/renderer_vulkan/present/lsfg_delta.h" +#include "video_core/renderer_vulkan/present/lsfg_gamma.h" +#include "video_core/renderer_vulkan/present/lsfg_generate.h" +#include "video_core/renderer_vulkan/present/lsfg_mipmaps.h" + +namespace Vulkan { + +class Device; +class LsfgShaders; + +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); + + LsfgChain(const LsfgChain&) = delete; + LsfgChain& operator=(const LsfgChain&) = delete; + + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count); + + [[nodiscard]] LsfgImage& Input(u64 frame_count) { + return frames[frame_count % frames.size()]; + } + + [[nodiscard]] LsfgImage& Output() { + return generate.Output(); + } + + [[nodiscard]] LsfgImage& FlowLevel(size_t level) { + return mipmaps.Output(level); + } + +private: + LsfgResources resources; + vk::DescriptorPool descriptor_pool; + + LsfgImagePair frames; + LsfgMipmaps mipmaps; + std::array alpha; + LsfgBeta beta; + std::array gamma; + std::array delta; + LsfgGenerate generate; +}; + +} // 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 2d22fba675..17fab47931 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_common.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_common.cpp @@ -28,6 +28,28 @@ struct LsfgConstants { }; static_assert(sizeof(LsfgConstants) == 48); +vk::Image CreateChainImage(MemoryAllocator& memory_allocator, VkExtent2D extent, VkFormat format) { + const VkImageCreateInfo image_ci{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = {.width = extent.width, .height = extent.height, .depth = 1}, + .mipLevels = 1, + .arrayLayers = 1, + .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_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + return memory_allocator.CreateImage(image_ci); +} + vk::Buffer CreateUniformBuffer(MemoryAllocator& memory_allocator, VkDeviceSize size) { const VkBufferCreateInfo buffer_ci{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, @@ -69,7 +91,7 @@ VkImageMemoryBarrier MakeBarrier(const LsfgImage& image, VkAccessFlags src_acces LsfgImage::LsfgImage(const Device& device, MemoryAllocator& memory_allocator, VkExtent2D extent_, VkFormat format) : extent{std::max(1u, extent_.width), std::max(1u, extent_.height)} { - image = CreateWrappedImage(memory_allocator, extent, format); + image = CreateChainImage(memory_allocator, extent, format); view = CreateWrappedImageView(device, image, format); } diff --git a/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp b/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp index 230f4a3d5c..89ab54e79d 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp +++ b/src/video_core/renderer_vulkan/present/lsfg_mipmaps.cpp @@ -2,233 +2,77 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include -#include +#include #include "video_core/frame_gen/lossless_dll.h" #include "video_core/renderer_vulkan/present/lsfg_mipmaps.h" #include "video_core/renderer_vulkan/present/lsfg_shaders.h" #include "video_core/renderer_vulkan/present/util.h" -#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/vulkan_common/vulkan_device.h" namespace Vulkan { namespace { -constexpr VkFormat FLOW_FORMAT = VK_FORMAT_R8_UNORM; constexpr u32 DISPATCH_TILE_SHIFT = 6; -constexpr size_t DESCRIPTOR_SET_COUNT = 2; -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); - -constexpr std::array MIPMAPS_BINDINGS{ - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLER, - VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, -}; - -vk::Sampler CreateFlowSampler(const Device& device) { - return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .mipLodBias = 0.0f, - .anisotropyEnable = VK_FALSE, - .maxAnisotropy = 0.0f, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_NEVER, - .minLod = 0.0f, - .maxLod = VK_LOD_CLAMP_NONE, - .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, - .unnormalizedCoordinates = VK_FALSE, - }); +[[nodiscard]] u32 GroupCount(u32 size) { + return (size + (1u << DISPATCH_TILE_SHIFT) - 1) >> DISPATCH_TILE_SHIFT; } } // Anonymous namespace -LsfgMipmaps::LsfgMipmaps(const Device& device, MemoryAllocator& memory_allocator_, - const LsfgShaders& shaders, VkExtent2D input_extent, f32 flow_scale) - : memory_allocator{memory_allocator_} { +LsfgMipmaps::LsfgMipmaps(const Device& device, MemoryAllocator& memory_allocator, + const LsfgShaders& shaders, LsfgResources& resources, + vk::DescriptorPool& descriptor_pool, LsfgImagePair& frames_, + f32 flow_scale) + : frames{&frames_} { + using namespace VideoCore::FrameGen::PerformanceShader; + + pass = LsfgPass(device, shaders, MIPMAPS, + {{1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}, + {1, VK_DESCRIPTOR_TYPE_SAMPLER}, + {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE}, + {LSFG_MIP_LEVELS, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); + + const VkExtent2D input_extent = (*frames)[0].Extent(); flow_extent = VkExtent2D{ .width = std::max(1u, static_cast(static_cast(input_extent.width) * flow_scale)), .height = std::max(1u, static_cast(static_cast(input_extent.height) * flow_scale)), }; - CreateImages(device); - CreateUniformBuffer(flow_scale); - - sampler = CreateFlowSampler(device); - descriptor_pool = CreateWrappedDescriptorPool( - device, DESCRIPTOR_SET_COUNT * MIPMAPS_BINDINGS.size(), DESCRIPTOR_SET_COUNT, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLER, - VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}); - descriptor_set_layout = CreateWrappedDescriptorSetLayout( - device, std::span{MIPMAPS_BINDINGS}, VK_SHADER_STAGE_COMPUTE_BIT); - - const std::vector layouts(DESCRIPTOR_SET_COUNT, - *descriptor_set_layout); - descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); - - pipeline_layout = CreateWrappedPipelineLayout(device, descriptor_set_layout); - pipeline = CreateWrappedComputePipeline( - device, pipeline_layout, shaders.Get(VideoCore::FrameGen::PerformanceShader::MIPMAPS)); -} - -VkExtent2D LsfgMipmaps::GetLevelExtent(size_t level) const { - return VkExtent2D{ - .width = std::max(1u, flow_extent.width >> level), - .height = std::max(1u, flow_extent.height >> level), - }; -} - -void LsfgMipmaps::CreateImages(const Device& device) { for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { - images[i] = CreateWrappedImage(memory_allocator, GetLevelExtent(i), FLOW_FORMAT); - image_views[i] = CreateWrappedImageView(device, images[i], FLOW_FORMAT); - } -} - -void LsfgMipmaps::CreateUniformBuffer(f32 flow_scale) { - uniform_buffer = CreateWrappedBuffer(memory_allocator, sizeof(LsfgConstants), - MemoryUsage::Upload); - - const LsfgConstants constants{ - .input_offset = {0, 0}, - .first_iter = 0, - .first_iter_s = 0, - .advanced_color_kind = 0, - .hdr_support = 0, - .resolution_inv_scale = 1.0f / flow_scale, - .timestamp = 0.0f, - .ui_threshold = 0.5f, - .padding = {0, 0, 0}, - }; - - const std::span mapped = uniform_buffer.Mapped(); - std::memcpy(mapped.data(), &constants, sizeof(constants)); - uniform_buffer.Flush(); -} - -void LsfgMipmaps::Dispatch(const Device& device, Scheduler& scheduler, VkImage current_image, - VkImageView current_view, u64 frame_count) { - const size_t set_index = frame_count % DESCRIPTOR_SET_COUNT; - const VkDescriptorSet set = descriptor_sets[set_index]; - - const VkDescriptorBufferInfo buffer_info{ - .buffer = *uniform_buffer, - .offset = 0, - .range = sizeof(LsfgConstants), - }; - const VkDescriptorImageInfo sampler_info{ - .sampler = *sampler, - .imageView = VK_NULL_HANDLE, - .imageLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }; - const VkDescriptorImageInfo sampled_info{ - .sampler = VK_NULL_HANDLE, - .imageView = current_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - std::array storage_infos{}; - for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { - storage_infos[i] = VkDescriptorImageInfo{ - .sampler = VK_NULL_HANDLE, - .imageView = *image_views[i], - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + const VkExtent2D level_extent{ + .width = flow_extent.width >> i, + .height = flow_extent.height >> i, }; + out_images[i] = LsfgImage(device, memory_allocator, level_extent, LSFG_FLOW_FORMAT); } - std::vector writes; - writes.reserve(MIPMAPS_BINDINGS.size()); - const auto push = [&](u32 binding, VkDescriptorType type, - const VkDescriptorImageInfo* image_info, - const VkDescriptorBufferInfo* buf_info) { - writes.push_back(VkWriteDescriptorSet{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = set, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = type, - .pImageInfo = image_info, - .pBufferInfo = buf_info, - .pTexelBufferView = nullptr, - }); - }; + const std::vector layouts(descriptor_sets.size(), pass.SetLayout()); + owned_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); - push(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, nullptr, &buffer_info); - push(1, VK_DESCRIPTOR_TYPE_SAMPLER, &sampler_info, nullptr); - push(2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &sampled_info, nullptr); - for (u32 i = 0; i < LSFG_MIP_LEVELS; ++i) { - push(3 + i, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &storage_infos[i], nullptr); + const VkSampler sampler = resources.GetSampler(); + const VkBuffer buffer = resources.GetBuffer(); + + 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) + .AddSampledImage((*frames)[i]) + .AddStorageImages(out_images) + .Build(device); } +} - device.GetLogical().UpdateDescriptorSets(writes, {}); +void LsfgMipmaps::Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count) { + const size_t slot = frame_count % descriptor_sets.size(); - const u32 groups_x = (flow_extent.width + (1u << DISPATCH_TILE_SHIFT) - 1) >> - DISPATCH_TILE_SHIFT; - const u32 groups_y = (flow_extent.height + (1u << DISPATCH_TILE_SHIFT) - 1) >> - DISPATCH_TILE_SHIFT; + LsfgBarriers(cmdbuf).WriteToRead((*frames)[slot]).ReadToWriteAll(out_images).Build(); - std::array raw_images{}; - for (size_t i = 0; i < LSFG_MIP_LEVELS; ++i) { - raw_images[i] = *images[i]; - } - - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([raw_images, current_image, set, groups_x, groups_y, - layout = *pipeline_layout, - compute_pipeline = *pipeline](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier input_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = current_image, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, input_barrier); - - for (const VkImage image : raw_images) { - TransitionImageLayout(cmdbuf, image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_UNDEFINED); - } - - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, compute_pipeline); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {}); - cmdbuf.Dispatch(groups_x, groups_y, 1); - }); + pass.Bind(cmdbuf, descriptor_sets[slot]); + cmdbuf.Dispatch(GroupCount(flow_extent.width), GroupCount(flow_extent.height), 1); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/lsfg_mipmaps.h b/src/video_core/renderer_vulkan/present/lsfg_mipmaps.h index 955d61e552..5af1cfb53d 100644 --- a/src/video_core/renderer_vulkan/present/lsfg_mipmaps.h +++ b/src/video_core/renderer_vulkan/present/lsfg_mipmaps.h @@ -6,52 +6,37 @@ #include #include "common/common_types.h" -#include "video_core/vulkan_common/vulkan_memory_allocator.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" +#include "video_core/renderer_vulkan/present/lsfg_common.h" namespace Vulkan { class Device; class LsfgShaders; -class Scheduler; constexpr size_t LSFG_MIP_LEVELS = 7; class LsfgMipmaps { public: - explicit LsfgMipmaps(const Device& device, MemoryAllocator& memory_allocator, - const LsfgShaders& shaders, VkExtent2D input_extent, f32 flow_scale); + LsfgMipmaps() = default; + LsfgMipmaps(const Device& device, MemoryAllocator& memory_allocator, const LsfgShaders& shaders, + LsfgResources& resources, vk::DescriptorPool& descriptor_pool, + LsfgImagePair& frames, f32 flow_scale); - void Dispatch(const Device& device, Scheduler& scheduler, VkImage current_image, - VkImageView current_view, u64 frame_count); + void Dispatch(vk::CommandBuffer cmdbuf, u64 frame_count); - [[nodiscard]] VkImageView GetLevelView(size_t level) const { - return *image_views[level]; + [[nodiscard]] LsfgImage& Output(size_t level) { + return out_images[level]; } - [[nodiscard]] VkImage GetLevelImage(size_t level) const { - return *images[level]; - } - - [[nodiscard]] VkExtent2D GetLevelExtent(size_t level) const; - private: - void CreateImages(const Device& device); - void CreateUniformBuffer(f32 flow_scale); + LsfgImagePair* frames{}; + + LsfgPass pass; + std::array descriptor_sets{}; + vk::DescriptorSets owned_sets; - MemoryAllocator& memory_allocator; VkExtent2D flow_extent{}; - - vk::Buffer uniform_buffer; - vk::Sampler sampler; - vk::DescriptorPool descriptor_pool; - vk::DescriptorSetLayout descriptor_set_layout; - vk::DescriptorSets descriptor_sets; - vk::PipelineLayout pipeline_layout; - vk::Pipeline pipeline; - - std::array images; - std::array image_views; + std::array out_images; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 3924163c6d..a0c1f017dc 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -193,7 +193,7 @@ void RendererVulkan::Composite(std::span framebu render_window.GetFramebufferLayout(), swapchain.GetImageCount(), swapchain.GetImageViewFormat()); - frame_gen.Process(device, frame); + frame_gen.Process(device, frame, swapchain.GetImageFormat()); scheduler.Flush(*frame->render_ready);