#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(vk, stagingbuf, image); const vk::TimelineSemaphore sema{vk, 0}; cmdbuf.submit(vk); } } 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 vk::VulkanInstanceFuncs, 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 < 4; 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&, std::pair, const std::optional& ) { 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 }