From 5ca9bc5de99e9d96e9d12b1538c3f6993657aa3c Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Sun, 30 Nov 2025 13:10:26 +0100 Subject: [PATCH] refactor(cleanup): add primitive debug tool --- CMakeLists.txt | 13 +- .../lsfg-vk-common/vulkan/command_buffer.hpp | 6 + lsfg-vk-common/src/vulkan/command_buffer.cpp | 41 +++++ lsfg-vk-debug/.clang-tidy | 36 ++++ lsfg-vk-debug/CMakeLists.txt | 8 + lsfg-vk-debug/src/debug.cpp | 169 ++++++++++++++++++ test/CMakeLists.txt | 57 ------ test/src/main.cpp | 130 -------------- 8 files changed, 272 insertions(+), 188 deletions(-) create mode 100644 lsfg-vk-debug/.clang-tidy create mode 100644 lsfg-vk-debug/CMakeLists.txt create mode 100644 lsfg-vk-debug/src/debug.cpp delete mode 100644 test/CMakeLists.txt delete mode 100644 test/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b6ea6e4..a1c6ec5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.10) project(lsfg-vk LANGUAGES CXX) +# === user facing options +option(LSFGVK_BUILD_DEBUG_TOOL + "Build the debug tool for testing and debugging" OFF) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -18,7 +22,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") -Weverything # disable incompatible warnings -Wno-pre-c++20-compat-pedantic - -Wno-c++98-compat + -Wno-c++98-compat-pedantic -Wno-switch-default -Wno-switch-enum # disable noisy warnings @@ -30,5 +34,12 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") endif() endif() +if(LSFGVK_BUILD_DEBUG_TOOL) + add_compile_definitions(LSFGVK__RENDERDOC_INTEGRATION) +endif() + add_subdirectory(lsfg-vk-common) add_subdirectory(lsfg-vk-backend) +if(LSFGVK_BUILD_DEBUG_TOOL) + add_subdirectory(lsfg-vk-debug) +endif() diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp index c665d67..f49a894 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp @@ -1,6 +1,7 @@ #pragma once #include "../helpers/pointers.hpp" +#include "buffer.hpp" #include "descriptor_set.hpp" #include "image.hpp" #include "shader.hpp" @@ -57,6 +58,11 @@ namespace vk { /// @param vk the vulkan instance /// @throws ls::vulkan_error on failure void submit(const vk::Vulkan& vk) const; + + /// copy buffer to image + /// @param buffer the source buffer + /// @param image the destination image + void copyBufferToImage(const vk::Buffer& buffer, const vk::Image& image) const; private: ls::owned_ptr commandBuffer; }; diff --git a/lsfg-vk-common/src/vulkan/command_buffer.cpp b/lsfg-vk-common/src/vulkan/command_buffer.cpp index b2618a5..b679ac2 100644 --- a/lsfg-vk-common/src/vulkan/command_buffer.cpp +++ b/lsfg-vk-common/src/vulkan/command_buffer.cpp @@ -1,6 +1,7 @@ #include "lsfg-vk-common/vulkan/command_buffer.hpp" #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" +#include "lsfg-vk-common/vulkan/buffer.hpp" #include "lsfg-vk-common/vulkan/descriptor_set.hpp" #include "lsfg-vk-common/vulkan/fence.hpp" #include "lsfg-vk-common/vulkan/image.hpp" @@ -160,3 +161,43 @@ void CommandBuffer::submit(const vk::Vulkan& vk) const { if (!fence.wait(vk)) throw ls::vulkan_error(VK_TIMEOUT, "Fence::wait() timed out"); } + +void CommandBuffer::copyBufferToImage(const vk::Buffer& buffer, const vk::Image& image) const { + const VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_NONE, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .image = image.handle(), + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .levelCount = 1, + .layerCount = 1 + } + }; + vkCmdPipelineBarrier(*this->commandBuffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier + ); + + const VkBufferImageCopy region{ + .bufferImageHeight = 0, + .imageSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1 + }, + .imageExtent = { + .width = image.getExtent().width, + .height = image.getExtent().height, + .depth = 1 + } + }; + vkCmdCopyBufferToImage(*this->commandBuffer, + buffer.handle(), image.handle(), + VK_IMAGE_LAYOUT_GENERAL, 1, ®ion + ); +} diff --git a/lsfg-vk-debug/.clang-tidy b/lsfg-vk-debug/.clang-tidy new file mode 100644 index 0000000..9567cc4 --- /dev/null +++ b/lsfg-vk-debug/.clang-tidy @@ -0,0 +1,36 @@ +Checks: +# enable basic checks +- "clang-analyzer-*" +# configure performance checks +- "performance-*" +- "-performance-enum-size" +# configure readability and bugprone checks +- "readability-*" +- "bugprone-*" +- "misc-*" +- "-readability-braces-around-statements" +- "-readability-function-cognitive-complexity" +- "-readability-identifier-length" +- "-readability-implicit-bool-conversion" +- "-readability-magic-numbers" +- "-readability-math-missing-parentheses" +- "-readability-named-parameter" +- "-bugprone-easily-swappable-parameters" +# configure modernization +- "modernize-*" +- "-modernize-use-trailing-return-type" +# configure cppcoreguidelines +- "cppcoreguidelines-*" +- "-cppcoreguidelines-avoid-magic-numbers" +- "-cppcoreguidelines-pro-type-reinterpret-cast" +- "-cppcoreguidelines-macro-usage" +# disable slow and pointless checks +- "-modernize-use-std-numbers" +- "-modernize-type-traits" +- "-cppcoreguidelines-owning-memory" +- "-cppcoreguidelines-macro-to-enum" +- "-readability-container-contains" +- "-bugprone-reserved-identifier" +- "-bugprone-stringview-nullptr" +- "-bugprone-standalone-empty" +- "-misc-unused-using-decls" diff --git a/lsfg-vk-debug/CMakeLists.txt b/lsfg-vk-debug/CMakeLists.txt new file mode 100644 index 0000000..1dd5646 --- /dev/null +++ b/lsfg-vk-debug/CMakeLists.txt @@ -0,0 +1,8 @@ +set(DEBUG_SOURCES + "src/debug.cpp") + +add_executable(lsfg-vk-debug ${DEBUG_SOURCES}) + +target_link_libraries(lsfg-vk-debug + PUBLIC lsfg-vk-common + PUBLIC lsfg-vk-backend) diff --git a/lsfg-vk-debug/src/debug.cpp b/lsfg-vk-debug/src/debug.cpp new file mode 100644 index 0000000..c6e9986 --- /dev/null +++ b/lsfg-vk-debug/src/debug.cpp @@ -0,0 +1,169 @@ +#include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk-common/vulkan/buffer.hpp" +#include "lsfg-vk-common/vulkan/command_buffer.hpp" +#include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp" +#include "lsfg-vk-common/vulkan/vulkan.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // NOLINT (thanks clang-tidy) + +#include + +const VkExtent2D EXTENT = { 1920, 1080 }; + +namespace { + /// returns the current time in microseconds + uint64_t get_current_time_us() { + struct timespec ts{}; + clock_gettime(CLOCK_MONOTONIC, &ts); + + return static_cast(ts.tv_sec) * 1000000UL + + static_cast(ts.tv_nsec) / 1000UL; + } + /// uploads an image from a dds file + void upload_image( + const vk::Vulkan& vk, + const vk::Image& image, + const std::string& path + ) { + // read image bytecode + std::ifstream file(path.data(), std::ios::binary | std::ios::ate); + if (!file.is_open()) + throw std::runtime_error("ifstream::ifstream() failed"); + + std::streamsize size = file.tellg(); + size -= 124 + 4; // dds header and magic bytes + + std::vector code(static_cast(size)); + file.seekg(124 + 4, std::ios::beg); + if (!file.read(code.data(), size)) + throw std::runtime_error("ifstream::read() failed"); + + file.close(); + + // upload to image + const vk::Buffer stagingbuf{vk, code.data(), code.size(), + VK_BUFFER_USAGE_TRANSFER_SRC_BIT}; + + const vk::CommandBuffer cmdbuf{vk}; + cmdbuf.copyBufferToImage(stagingbuf, image); + + const vk::TimelineSemaphore sema{vk, 0}; + cmdbuf.submit(vk, sema, 1, sema, 2); + + sema.signal(vk, 1); + if (!sema.wait(vk, 2)) + throw std::runtime_error("image upload failed"); + } +} + +int main() { + const uint64_t time_us = get_current_time_us(); + + const vk::Vulkan vk{ + "lsfg-vk-debug", vk::version{1, 1, 0}, + "lsfg-vk-debug-engine", vk::version{1, 0, 0}, + [](const std::vector& devices) { + return devices.front(); + } + }; + + std::pair srcfds{}; + const vk::Image frame_0{vk, + EXTENT, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + std::nullopt, + &srcfds.first + }; + const vk::Image frame_1{vk, + EXTENT, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + std::nullopt, + &srcfds.second + }; + + std::vector destimgs{}; + std::vector destfds{}; + for (size_t i = 0; i < 1; i++) { + int fd{}; + destimgs.emplace_back(vk, + EXTENT, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + std::nullopt, + &fd + ); + destfds.push_back(fd); + } + + int syncfd{}; + const vk::TimelineSemaphore sync{vk, 0, std::nullopt, &syncfd}; + + const uint64_t init_done_us = get_current_time_us(); + std::cerr << "vulkan initialized in " + << (init_done_us - time_us) << "us\n"; + + // initialize lsfg-vk + lsfgvk::Instance lsfgvk{ + [](const std::string&) { + return true; + }, + "/home/pancake/.steam/steam/steamapps/common/Lossless Scaling/Lossless.dll", + true + }; + lsfgvk::Context& lsfgvk_ctx = lsfgvk.openContext( + srcfds, destfds, + syncfd, EXTENT.width, EXTENT.height, + false, 1.0F / 0.5F, true + ); + + const uint64_t lsfg_init_done_us = get_current_time_us(); + std::cerr << "lsfg-vk initialized in " + << (lsfg_init_done_us - init_done_us) << "us\n"; + + // render destination images + size_t idx{1}; + for (size_t j = 0; j < 3; j++) { + try { + upload_image(vk, + j % 2 == 0 ? frame_0 : frame_1, + "s" + std::to_string(j + 1) + ".dds" + ); + } catch (const std::exception& e) { + std::cerr << "failed to upload image: " << e.what() << "\n"; + return EXIT_FAILURE; + } + + sync.signal(vk, idx++); + lsfgvk.scheduleFrames(lsfgvk_ctx); + + for (size_t i = 0; i < destimgs.size(); i++) { + auto success = sync.wait(vk, idx++); + if (!success) { + std::cerr << "failed to wait for frame " << j << ":" << i << "\n"; + return EXIT_FAILURE; + } + + const uint64_t frame_done_us = get_current_time_us(); + std::cerr << "frame " << j << ":" << i << " done after " + << (frame_done_us - lsfg_init_done_us) << "us\n"; + } + } + + // deinitialize lsfg-vk + lsfgvk.closeContext(lsfgvk_ctx); + return EXIT_SUCCESS; // let the vulkan objects go out of scope +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index a7305da..0000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -if(NOT LSFGVK_EXCESS_DEBUG) - set(CMAKE_C_VISIBILITY_PRESET "hidden") - set(CMAKE_CXX_VISIBILITY_PRESET "hidden") -endif() - -if(LSFGVK_EXCESS_DEBUG) - add_compile_definitions(LSFGVK_EXCESS_DEBUG) -endif() - -project(lsfg-vk-test - DESCRIPTION "Test: lsfg-vk" - LANGUAGES CXX) - -file(GLOB SOURCES - "src/*.cpp" -) - -add_executable(lsfg-vk-test ${SOURCES}) - -# target -set_target_properties(lsfg-vk-test PROPERTIES - CXX_STANDARD 20 - CXX_STANDARD_REQUIRED ON) -target_link_libraries(lsfg-vk-test PUBLIC - lsfg-vk lsfg-vk-framegen - vulkan) - -# diagnostics -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set_target_properties(lsfg-vk-test PROPERTIES - EXPORT_COMPILE_COMMANDS ON) -endif() - -if(LSFGVK_EXCESS_DEBUG) - target_compile_options(lsfg-vk-test PRIVATE - -Weverything - # disable compat c++ flags - -Wno-pre-c++20-compat-pedantic - -Wno-pre-c++17-compat - -Wno-c++98-compat-pedantic - -Wno-c++98-compat - # disable other flags - -Wno-missing-designated-field-initializers - -Wno-shadow # allow shadowing - -Wno-switch-enum # ignore missing cases - -Wno-switch-default # ignore missing default - -Wno-padded # ignore automatic padding - -Wno-exit-time-destructors # allow globals - -Wno-global-constructors # allow globals - -Wno-cast-function-type-strict # for vulkan - ) - - set_target_properties(lsfg-vk-test PROPERTIES - CXX_CLANG_TIDY clang-tidy) -endif() diff --git a/test/src/main.cpp b/test/src/main.cpp deleted file mode 100644 index 28f4d6a..0000000 --- a/test/src/main.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "common/utils.hpp" -#include "core/commandpool.hpp" -#include "core/device.hpp" -#include "core/image.hpp" -#include "core/instance.hpp" -#include "extract/extract.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace LSFG; - -// test configuration - -const VkExtent2D SRC_EXTENT = { 2560 , 1440 }; -const VkFormat SRC_FORMAT = VK_FORMAT_R8G8B8A8_UNORM; -const std::array SRC_FILES = { - "test/f0.dds", - "test/f1.dds", - "test/f2.dds" -}; - -const size_t MULTIPLIER = 3; -const bool IS_HDR = false; -const float FLOW_SCALE = 0.7F; -#define PERFORMANCE_MODE false - -// test configuration end - -#if PERFORMANCE_MODE -#include "lsfg_3_1p.hpp" -using namespace LSFG_3_1P; -#else -#include "lsfg_3_1.hpp" -using namespace LSFG_3_1; -#endif - -namespace { - /// Create images for frame generation - std::pair create_images(const Core::Device& device, - std::array& fds, - std::vector& outFds, std::vector& out_n) { - const Core::Image frame_0{device, - SRC_EXTENT, SRC_FORMAT, - VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, - &fds.at(0) - }; - const Core::Image frame_1{device, - SRC_EXTENT, SRC_FORMAT, - VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, - &fds.at(1) - }; - - for (size_t i = 0; i < (MULTIPLIER - 1); i++) - out_n.at(i) = Core::Image{device, - SRC_EXTENT, SRC_FORMAT, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_ASPECT_COLOR_BIT, - &outFds.at(i) - }; - - return { frame_0, frame_1 }; - } - - /// Create the LSFG context. - int32_t create_lsfg(const std::array& fds, const std::vector& outFds) { - Extract::extractShaders(); - initialize( - 0x1463ABAC, - IS_HDR, 1.0F / FLOW_SCALE, MULTIPLIER - 1, - false, - Extract::getShader - ); - initializeRenderDoc(); - return createContext( - fds.at(0), fds.at(1), outFds, - SRC_EXTENT, SRC_FORMAT - ); - } - - /// Destroy the LSFG context. - void delete_lsfg(int32_t id) { - deleteContext(id); - finalize(); - } -} - -namespace { - std::array fds{}; - std::vector outFds(MULTIPLIER - 1); - std::pair frames{}; - std::vector out_n(MULTIPLIER - 1); - int32_t lsfg_id{}; -} - -int main() { - // initialize host Vulkan - const Core::Instance instance{}; - const Core::Device device{instance, 0x1463ABAC, false}; - const Core::CommandPool commandPool{device}; - - // setup test - frames = create_images(device, fds, outFds, out_n); - lsfg_id = create_lsfg(fds, outFds); - - Utils::clearImage(device, frames.first); - Utils::clearImage(device, frames.second); - - // run - for (size_t fc = 0; fc < SRC_FILES.size(); fc++) { - if (fc % 2 == 0) - Utils::uploadImage(device, commandPool, frames.first, SRC_FILES.at(fc)); - else - Utils::uploadImage(device, commandPool, frames.second, SRC_FILES.at(fc)); - - // run the present - presentContext(lsfg_id, -1, {}); - } - - // destroy test - delete_lsfg(lsfg_id); - - return 0; -}