refactor(cleanup): add primitive debug tool

This commit is contained in:
PancakeTAS 2025-11-30 13:10:26 +01:00
parent 96ae2c760c
commit 5ca9bc5de9
8 changed files with 272 additions and 188 deletions

View file

@ -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()

View file

@ -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<VkCommandBuffer> commandBuffer;
};

View file

@ -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, &region
);
}

36
lsfg-vk-debug/.clang-tidy Normal file
View file

@ -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"

View file

@ -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)

169
lsfg-vk-debug/src/debug.cpp Normal file
View file

@ -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 <cstddef>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <exception>
#include <fstream>
#include <iostream>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include <bits/time.h>
#include <time.h> // NOLINT (thanks clang-tidy)
#include <vulkan/vulkan_core.h>
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<uint64_t>(ts.tv_sec) * 1000000UL
+ static_cast<uint64_t>(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<char> code(static_cast<size_t>(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<VkPhysicalDevice>& devices) {
return devices.front();
}
};
std::pair<int, int> 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<vk::Image> destimgs{};
std::vector<int> 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
}

View file

@ -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()

View file

@ -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 <vulkan/vulkan_core.h>
#include <cstdlib>
#include <utility>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include <array>
using namespace LSFG;
// test configuration
const VkExtent2D SRC_EXTENT = { 2560 , 1440 };
const VkFormat SRC_FORMAT = VK_FORMAT_R8G8B8A8_UNORM;
const std::array<std::string, 3> 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<Core::Image, Core::Image> create_images(const Core::Device& device,
std::array<int, 2>& fds,
std::vector<int>& outFds, std::vector<Core::Image>& 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<int, 2>& fds, const std::vector<int>& 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<int, 2> fds{};
std::vector<int> outFds(MULTIPLIER - 1);
std::pair<Core::Image, Core::Image> frames{};
std::vector<Core::Image> 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;
}