mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-02-04 04:45:56 +00:00
refactor(cleanup): custom cli (including benchmark) & fixup
This commit is contained in:
parent
adffd92357
commit
6236dcf8fc
26 changed files with 851 additions and 368 deletions
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
|
|
@ -40,9 +40,10 @@ jobs:
|
|||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=./target \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DLSFGVK_BUILD_VULKAN_LAYER=On \
|
||||
-DLSFGVK_BUILD_USER_INTERFACE=On \
|
||||
-DLSFGVK_INSTALL_XDG_FILES=On \
|
||||
-DLSFGVK_BUILD_VK_LAYER=ON \
|
||||
-DLSFGVK_BUILD_UI=ON \
|
||||
-DLSFGVK_BUILD_CLI=ON \
|
||||
-DLSFGVK_INSTALL_XDG_FILES=ON \
|
||||
-DLSFGVK_LAYER_LIBRARY_PATH="../../../lib/liblsfg-vk-layer.so"
|
||||
- name: Build with Ninja
|
||||
run: |
|
||||
|
|
|
|||
7
.github/workflows/manual.yml
vendored
7
.github/workflows/manual.yml
vendored
|
|
@ -59,9 +59,10 @@ jobs:
|
|||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_INSTALL_PREFIX=./target \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DLSFGVK_BUILD_VULKAN_LAYER=On \
|
||||
-DLSFGVK_BUILD_USER_INTERFACE=On \
|
||||
-DLSFGVK_INSTALL_XDG_FILES=On \
|
||||
-DLSFGVK_BUILD_VK_LAYER=ON \
|
||||
-DLSFGVK_BUILD_UI=ON \
|
||||
-DLSFGVK_BUILD_CLI=ON \
|
||||
-DLSFGVK_INSTALL_XDG_FILES=ON \
|
||||
-DLSFGVK_LAYER_LIBRARY_PATH="../../../lib/liblsfg-vk-layer.so"
|
||||
- name: Build with Ninja
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ project(lsfg-vk LANGUAGES CXX)
|
|||
include(GNUInstallDirs)
|
||||
|
||||
# === READ HERE FOR BUILD OPTIONS ===
|
||||
option(LSFGVK_BUILD_VULKAN_LAYER "Build the Vulkan layer" ON)
|
||||
option(LSFGVK_BUILD_USER_INTERFACE "Build the user interface" OFF)
|
||||
option(LSFGVK_BUILD_DEBUG_TOOL "Build the debug tool" OFF)
|
||||
option(LSFGVK_INSTALL_DEVELOP "Install development libraries and headers" OFF)
|
||||
option(LSFGVK_INSTALL_XDG_FILES "Install the application icon and desktop files" OFF)
|
||||
option(LSFGVK_LAYER_LIBRARY_PATH "Change where Vulkan searches for the layer library" liblsfg-vk-layer.so)
|
||||
option(LSFGVK_BUILD_VK_LAYER "Build the Vulkan layer" ON)
|
||||
option(LSFGVK_BUILD_UI "Build the user interface" OFF)
|
||||
option(LSFGVK_BUILD_CLI "Build the command line interface" ON)
|
||||
option(LSFGVK_INSTALL_DEVELOP "Install development libraries and headers" OFF)
|
||||
option(LSFGVK_INSTALL_XDG_FILES "Install the application icon and desktop files" OFF)
|
||||
option(LSFGVK_LAYER_LIBRARY_PATH "Change where Vulkan searches for the layer library" liblsfg-vk-layer.so)
|
||||
option(LSFGVK_TESTING_RENDERDOC "Enable RenderDoc integration for testing purposes" OFF)
|
||||
# === READ HERE FOR BUILD OPTIONS ===
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
|
@ -43,18 +44,18 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(LSFGVK_BUILD_DEBUG_TOOL)
|
||||
add_compile_definitions(LSFGVK__RENDERDOC_INTEGRATION)
|
||||
if(LSFGVK_TESTING_RENDERDOC)
|
||||
add_compile_definitions(LSFGVK_TESTING_RENDERDOC)
|
||||
endif()
|
||||
|
||||
add_subdirectory(lsfg-vk-common)
|
||||
add_subdirectory(lsfg-vk-backend)
|
||||
if(LSFGVK_BUILD_VULKAN_LAYER)
|
||||
if(LSFGVK_BUILD_VK_LAYER)
|
||||
add_subdirectory(lsfg-vk-layer)
|
||||
endif()
|
||||
if(LSFGVK_BUILD_USER_INTERFACE)
|
||||
if(LSFGVK_BUILD_UI)
|
||||
add_subdirectory(lsfg-vk-ui)
|
||||
endif()
|
||||
if(LSFGVK_BUILD_DEBUG_TOOL)
|
||||
add_subdirectory(lsfg-vk-debug)
|
||||
if(LSFGVK_BUILD_CLI)
|
||||
add_subdirectory(lsfg-vk-cli)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ modules:
|
|||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On
|
||||
- -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
|
||||
- -DCMAKE_CXX_COMPILER=clang++
|
||||
- -DLSFGVK_BUILD_VK_LAYER=ON
|
||||
- -DLSFGVK_BUILD_UI=OFF
|
||||
- -DLSFGVK_BUILD_CLI=OFF
|
||||
- -DLSFGVK_LAYER_LIBRARY_PATH=/usr/lib/extensions/vulkan/lsfgvk/lib/liblsfg-vk-layer.so
|
||||
sources:
|
||||
- type: dir
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ modules:
|
|||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On
|
||||
- -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
|
||||
- -DCMAKE_CXX_COMPILER=clang++
|
||||
- -DLSFGVK_BUILD_VK_LAYER=ON
|
||||
- -DLSFGVK_BUILD_UI=OFF
|
||||
- -DLSFGVK_BUILD_CLI=OFF
|
||||
- -DLSFGVK_LAYER_LIBRARY_PATH=/usr/lib/extensions/vulkan/lsfgvk/lib/liblsfg-vk-layer.so
|
||||
sources:
|
||||
- type: dir
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ modules:
|
|||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=On
|
||||
- -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
|
||||
- -DCMAKE_CXX_COMPILER=clang++
|
||||
- -DLSFGVK_BUILD_VK_LAYER=ON
|
||||
- -DLSFGVK_BUILD_UI=OFF
|
||||
- -DLSFGVK_BUILD_CLI=OFF
|
||||
- -DLSFGVK_LAYER_LIBRARY_PATH=/usr/lib/extensions/vulkan/lsfgvk/lib/liblsfg-vk-layer.so
|
||||
sources:
|
||||
- type: dir
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ modules:
|
|||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/app
|
||||
- -DLSFGVK_BUILD_VULKAN_LAYER=Off
|
||||
- -DLSFGVK_BUILD_USER_INTERFACE=On
|
||||
- -DLSFGVK_INSTALL_XDG_FILES=On
|
||||
- -DLSFGVK_BUILD_VK_LAYER=OFF
|
||||
- -DLSFGVK_BUILD_UI=ON
|
||||
- -DLSFGVK_BUILD_CLI=OFF
|
||||
- -DLSFGVK_INSTALL_XDG_FILES=ON
|
||||
sources:
|
||||
- type: dir
|
||||
path: ../../..
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Benchmark {
|
||||
|
||||
///
|
||||
/// Run the benchmark.
|
||||
///
|
||||
/// @param width The width of the benchmark.
|
||||
/// @param height The height of the benchmark.
|
||||
///
|
||||
[[noreturn]] void run(uint32_t width, uint32_t height);
|
||||
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
||||
#include <renderdoc_app.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
|
@ -72,7 +72,7 @@ namespace lsfgvk::backend {
|
|||
/// get the shader registry
|
||||
/// @return the shader registry
|
||||
[[nodiscard]] const auto& getShaderRegistry() const { return this->shaders; }
|
||||
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
||||
/// get the RenderDoc API
|
||||
/// @return the RenderDoc API
|
||||
[[nodiscard]] const auto& getRenderDocAPI() const { return this->renderdoc; }
|
||||
|
|
@ -81,7 +81,7 @@ namespace lsfgvk::backend {
|
|||
vk::Vulkan vk;
|
||||
ShaderRegistry shaders;
|
||||
|
||||
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
||||
std::optional<RENDERDOC_API_1_6_0> renderdoc;
|
||||
#endif
|
||||
};
|
||||
|
|
@ -232,7 +232,7 @@ namespace {
|
|||
throw backend::error("Unable to build shader registry", e);
|
||||
}
|
||||
}
|
||||
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
||||
/// load RenderDoc integration
|
||||
std::optional<RENDERDOC_API_1_6_0> loadRenderDocIntegration() {
|
||||
void* module = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
|
|
@ -260,7 +260,7 @@ InstanceImpl::InstanceImpl(vk::PhysicalDeviceSelector selectPhysicalDevice,
|
|||
: vk(createVulkanInstance(selectPhysicalDevice)),
|
||||
shaders(createShaderRegistry(this->vk, shaderDllPath,
|
||||
allowLowPrecision && vk.supportsFP16())) {
|
||||
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
||||
this->renderdoc = loadRenderDocIntegration();
|
||||
#endif
|
||||
vk.persistPipelineCache(); // will silently fail
|
||||
|
|
@ -540,8 +540,8 @@ ContextImpl::ContextImpl(const InstanceImpl& instance,
|
|||
cmdbuf.submit(ctx.vk); // wait for completion
|
||||
}
|
||||
|
||||
void Instance::scheduleFrames(Context& context) {
|
||||
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||
void Instance::scheduleFrames(Context& context) { // NOLINT (static)
|
||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
||||
const auto& impl = this->m_impl;
|
||||
if (impl->getRenderDocAPI()) {
|
||||
impl->getRenderDocAPI()->StartFrameCapture(
|
||||
|
|
@ -554,7 +554,7 @@ void Instance::scheduleFrames(Context& context) {
|
|||
} catch (const std::exception& e) {
|
||||
throw backend::error("Unable to schedule frames", e);
|
||||
}
|
||||
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
||||
if (impl->getRenderDocAPI()) {
|
||||
impl->getVulkan().df().DeviceWaitIdle(impl->getVulkan().dev());
|
||||
impl->getRenderDocAPI()->EndFrameCapture(
|
||||
|
|
|
|||
|
|
@ -16,14 +16,17 @@ Checks:
|
|||
- "-readability-math-missing-parentheses"
|
||||
- "-readability-named-parameter"
|
||||
- "-bugprone-easily-swappable-parameters"
|
||||
- "-misc-non-private-member-variables-in-classes"
|
||||
# configure modernization
|
||||
- "modernize-*"
|
||||
- "-modernize-use-trailing-return-type"
|
||||
- "-modernize-use-designated-initializers"
|
||||
# configure cppcoreguidelines
|
||||
- "cppcoreguidelines-*"
|
||||
- "-cppcoreguidelines-avoid-magic-numbers"
|
||||
- "-cppcoreguidelines-pro-type-reinterpret-cast"
|
||||
- "-cppcoreguidelines-macro-usage"
|
||||
- "-cppcoreguidelines-pro-bounds-pointer-arithmetic"
|
||||
# disable slow and pointless checks
|
||||
- "-modernize-use-std-numbers"
|
||||
- "-modernize-type-traits"
|
||||
18
lsfg-vk-cli/CMakeLists.txt
Normal file
18
lsfg-vk-cli/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
set(CLI_SOURCES
|
||||
"src/tools/benchmark.cpp"
|
||||
"src/tools/debug.cpp"
|
||||
"src/tools/validate.cpp"
|
||||
"src/main.cpp")
|
||||
|
||||
add_executable(lsfg-vk-cli ${CLI_SOURCES})
|
||||
|
||||
target_link_libraries(lsfg-vk-cli
|
||||
PUBLIC lsfg-vk-common
|
||||
PUBLIC lsfg-vk-backend)
|
||||
|
||||
target_compile_options(lsfg-vk-cli PRIVATE
|
||||
-Wno-unknown-warning-option
|
||||
-Wno-unsafe-buffer-usage)
|
||||
|
||||
install(TARGETS lsfg-vk-cli
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
225
lsfg-vk-cli/src/main.cpp
Normal file
225
lsfg-vk-cli/src/main.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "tools/benchmark.hpp"
|
||||
#include "tools/debug.hpp"
|
||||
#include "tools/validate.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <getopt.h> // NOLINT
|
||||
#include <bits/getopt_core.h>
|
||||
#include <bits/getopt_ext.h>
|
||||
|
||||
using namespace lsfgvk::cli;
|
||||
|
||||
namespace {
|
||||
/// print usage information
|
||||
void usage(const std::string& prog) {
|
||||
std::cerr <<
|
||||
R"(Validate, benchmark, and debug lsfg-vk.
|
||||
|
||||
USAGE:
|
||||
)" << prog << R"( <COMMAND> [OPTIONS] [ARGS]
|
||||
|
||||
COMMANDS:
|
||||
validate Validate a configuration file
|
||||
benchmark Run a benchmark
|
||||
debug Run lsfg-vk on a set of images
|
||||
|
||||
SUBCOMMAND OPTIONS:
|
||||
|
||||
validate
|
||||
-c, --config <PATH> Optional path to the configuration file
|
||||
|
||||
benchmark & debug
|
||||
-d, --dll <PATH> Path to Lossless.dll
|
||||
-a, --allow-fp16 Allow FP16 acceleration
|
||||
-w, --width <INT> Width of the input frames
|
||||
-h, --height <INT> Height of the input frames
|
||||
-f, --flow <FLOAT> Flow scale
|
||||
-m, --multiplier <INT> Multiplier
|
||||
-p, --performance-mode Use performance mode
|
||||
-g, --gpu <STRING> GPU to use
|
||||
|
||||
benchmark
|
||||
-t, --duration <SECONDS> Benchmark duration in seconds
|
||||
|
||||
debug
|
||||
<folder> Path to the debug frames)" << '\n';
|
||||
}
|
||||
|
||||
/// parse the validate command options
|
||||
[[noreturn]] void on_validate(int argc, char** argv) {
|
||||
validate::Options opts{};
|
||||
|
||||
const std::array<option, 3> GETOPT {{
|
||||
{ "config", required_argument, nullptr, 'c' },
|
||||
{ nullptr, no_argument, nullptr, 0 }
|
||||
}};
|
||||
|
||||
int c{0};
|
||||
while ((c = getopt_long(argc, argv, "c:", GETOPT.data(), nullptr)) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
opts.config.emplace(optarg);
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage(*argv);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
usage(*argv);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::exit(validate::run(opts));
|
||||
}
|
||||
|
||||
/// parse the benchmark command options
|
||||
[[noreturn]] void on_benchmark(int argc, char** argv) {
|
||||
benchmark::Options opts{};
|
||||
|
||||
const std::array<option, 10> GETOPT {{
|
||||
{ "dll", required_argument, nullptr, 'd' },
|
||||
{ "allow-fp16", no_argument, nullptr, 'a' },
|
||||
{ "width", required_argument, nullptr, 'w' },
|
||||
{ "height", required_argument, nullptr, 'h' },
|
||||
{ "flow", required_argument, nullptr, 'f' },
|
||||
{ "multiplier", required_argument, nullptr, 'm' },
|
||||
{ "performance-mode", no_argument, nullptr, 'p' },
|
||||
{ "gpu", required_argument, nullptr, 'g' },
|
||||
{ "duration", required_argument, nullptr, 't' },
|
||||
{ nullptr, no_argument, nullptr, 0 }
|
||||
}};
|
||||
|
||||
int c{0};
|
||||
while ((c = getopt_long(argc, argv, "d:aw:h:f:m:pg:t:", GETOPT.data(), nullptr)) != -1) {
|
||||
switch (c) {
|
||||
case 'd':
|
||||
opts.dll.emplace(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
opts.allow_fp16 = true;
|
||||
break;
|
||||
case 'w':
|
||||
opts.width = std::stoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
opts.height = std::stoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
opts.flow = std::stof(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
opts.multiplier = std::stoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
opts.performance_mode = true;
|
||||
break;
|
||||
case 'g':
|
||||
opts.gpu.emplace(optarg);
|
||||
break;
|
||||
case 't':
|
||||
opts.duration = std::stoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage(*argv);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
usage(*argv);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::exit(benchmark::run(opts));
|
||||
}
|
||||
|
||||
/// parse the debug command options
|
||||
[[noreturn]] void on_debug(int argc, char** argv) {
|
||||
debug::Options opts{};
|
||||
|
||||
const std::array<option, 9> GETOPT {{
|
||||
{ "dll", required_argument, nullptr, 'd' },
|
||||
{ "allow-fp16", no_argument, nullptr, 'a' },
|
||||
{ "width", required_argument, nullptr, 'w' },
|
||||
{ "height", required_argument, nullptr, 'h' },
|
||||
{ "flow", required_argument, nullptr, 'f' },
|
||||
{ "multiplier", required_argument, nullptr, 'm' },
|
||||
{ "performance-mode", no_argument, nullptr, 'p' },
|
||||
{ "gpu", required_argument, nullptr, 'g' },
|
||||
{ nullptr, no_argument, nullptr, 0 }
|
||||
}};
|
||||
|
||||
int c{0};
|
||||
while ((c = getopt_long(argc, argv, "d:aw:h:f:m:pg:", GETOPT.data(), nullptr)) != -1) {
|
||||
switch (c) {
|
||||
case 'd':
|
||||
opts.dll.emplace(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
opts.allow_fp16 = true;
|
||||
break;
|
||||
case 'w':
|
||||
opts.width = std::stoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
opts.height = std::stoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
opts.flow = std::stof(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
opts.multiplier = std::stoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
opts.performance_mode = true;
|
||||
break;
|
||||
case 'g':
|
||||
opts.gpu.emplace(optarg);
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage(*argv);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optind + 1) != argc) {
|
||||
usage(*argv);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
opts.path = argv[optind];
|
||||
|
||||
std::exit(debug::run(opts));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
usage(*argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const std::string command{argv[1]};
|
||||
if (command == "validate")
|
||||
on_validate(argc - 1, argv + 1);
|
||||
else if (command == "benchmark")
|
||||
on_benchmark(argc - 1, argv + 1);
|
||||
else if (command == "debug")
|
||||
on_debug(argc - 1, argv + 1);
|
||||
|
||||
usage(*argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
181
lsfg-vk-cli/src/tools/benchmark.cpp
Normal file
181
lsfg-vk-cli/src/tools/benchmark.cpp
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "benchmark.hpp"
|
||||
#include "lsfg-vk-backend/lsfgvk.hpp"
|
||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
||||
#include "lsfg-vk-common/helpers/paths.hpp"
|
||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
||||
#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp"
|
||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
using namespace lsfgvk::cli;
|
||||
using namespace lsfgvk::cli::benchmark;
|
||||
|
||||
namespace {
|
||||
// get current time in milliseconds
|
||||
uint64_t ms() {
|
||||
struct timespec ts{};
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts); // NOLINT (IWYU)
|
||||
|
||||
return static_cast<uint64_t>(ts.tv_sec) * 1000ULL +
|
||||
static_cast<uint64_t>(ts.tv_nsec) / 1000000ULL;
|
||||
}
|
||||
}
|
||||
|
||||
int benchmark::run(const Options& opts) {
|
||||
try {
|
||||
// parse options
|
||||
if (opts.flow < 0.25F || opts.flow > 1.0F)
|
||||
throw ls::error("flow scale must be between 0.25 and 1.0");
|
||||
if (opts.multiplier < 2)
|
||||
throw ls::error("multiplier must be 2 or greater");
|
||||
if (opts.width <= 0 || opts.height <= 0)
|
||||
throw ls::error("width and height must be positive integers");
|
||||
if (opts.duration <= 0)
|
||||
throw ls::error("duration must be a positive integer");
|
||||
const VkExtent2D extent{
|
||||
static_cast<uint32_t>(opts.width),
|
||||
static_cast<uint32_t>(opts.height)
|
||||
};
|
||||
|
||||
// create instance
|
||||
const vk::Vulkan vk{
|
||||
"lsfg-vk-debug", vk::version{2, 0, 0},
|
||||
"lsfg-vk-debug-engine", vk::version{2, 0, 0},
|
||||
[opts](const vk::VulkanInstanceFuncs fi,
|
||||
const std::vector<VkPhysicalDevice>& devices) {
|
||||
if (!opts.gpu.has_value())
|
||||
return devices.front();
|
||||
|
||||
for (const VkPhysicalDevice& device : devices) {
|
||||
VkPhysicalDeviceProperties2 props{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2
|
||||
};
|
||||
fi.GetPhysicalDeviceProperties2(device, &props);
|
||||
|
||||
auto& properties = props.properties;
|
||||
std::array<char, 256> devname = std::to_array(properties.deviceName);
|
||||
devname[255] = '\0'; // ensure null-termination
|
||||
|
||||
if (std::string(devname.data()) == *opts.gpu)
|
||||
return device;
|
||||
}
|
||||
|
||||
throw ls::error("failed to find specified GPU: " + *opts.gpu);
|
||||
}
|
||||
};
|
||||
|
||||
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 (int i = 0; i < (opts.multiplier - 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};
|
||||
|
||||
// initialize backend
|
||||
std::string dll{};
|
||||
if (opts.dll.has_value())
|
||||
dll = *opts.dll;
|
||||
else
|
||||
dll = ls::findShaderDll();
|
||||
|
||||
lsfgvk::backend::Instance lsfgvk{
|
||||
[opts](
|
||||
const std::string& gpu_name,
|
||||
std::pair<const std::string&, const std::string&>,
|
||||
const std::optional<std::string>&
|
||||
) {
|
||||
return opts.gpu.value_or(gpu_name) == gpu_name;
|
||||
},
|
||||
dll, opts.allow_fp16
|
||||
};
|
||||
lsfgvk::backend::Context& lsfgvk_ctx = lsfgvk.openContext(
|
||||
srcfds, destfds,
|
||||
syncfd, extent.width, extent.height,
|
||||
false, 1.0F / opts.flow, opts.performance_mode
|
||||
);
|
||||
|
||||
// run the benchmark
|
||||
size_t iterations{0};
|
||||
size_t generated_frames{0};
|
||||
size_t total_frames{1};
|
||||
|
||||
uint64_t print_time = ms() + 1000ULL;
|
||||
const uint64_t end_time = ms() + static_cast<uint64_t>(opts.duration) * 1000ULL;
|
||||
while (ms() < end_time) {
|
||||
sync.signal(vk, total_frames++);
|
||||
lsfgvk.scheduleFrames(lsfgvk_ctx);
|
||||
|
||||
for (size_t i = 0; i < destimgs.size(); i++) {
|
||||
auto success = sync.wait(vk, total_frames++);
|
||||
if (!success)
|
||||
throw ls::error("failed to wait for frame");
|
||||
|
||||
generated_frames++;
|
||||
}
|
||||
|
||||
iterations++;
|
||||
|
||||
if (ms() >= print_time) {
|
||||
print_time += 1000ULL;
|
||||
std::cerr << "." << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
// output results
|
||||
|
||||
std::cerr << (opts.duration < 40 ? "\r" : "\n");
|
||||
std::cerr << "benchmark results (ran for " << opts.duration << " seconds):\n";
|
||||
std::cerr << " iterations: " << iterations << "\n";
|
||||
std::cerr << " generated frames: " << generated_frames << "\n";
|
||||
std::cerr << " total frames: " << total_frames << "\n";
|
||||
const auto time = static_cast<double>(opts.duration);
|
||||
const double fps_generated = static_cast<double>(generated_frames) / time;
|
||||
const double fps_total = static_cast<double>(total_frames) / time;
|
||||
std::cerr << std::setprecision(2) << std::fixed;
|
||||
std::cerr << " fps (generated): " << fps_generated << "fps\n";
|
||||
std::cerr << " fps (total): " << fps_total << "fps\n";
|
||||
|
||||
// deinitialize lsfg-vk
|
||||
lsfgvk.closeContext(lsfgvk_ctx);
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "error: " << e.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
29
lsfg-vk-cli/src/tools/benchmark.hpp
Normal file
29
lsfg-vk-cli/src/tools/benchmark.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace lsfgvk::cli::benchmark {
|
||||
|
||||
/// options for the "benchmark" command
|
||||
struct Options {
|
||||
std::optional<std::string> dll;
|
||||
bool allow_fp16{true};
|
||||
int width{1920};
|
||||
int height{1080};
|
||||
|
||||
float flow{0.85F};
|
||||
int multiplier{2};
|
||||
bool performance_mode{true};
|
||||
std::optional<std::string> gpu;
|
||||
|
||||
int duration{10};
|
||||
};
|
||||
|
||||
/// run the "benchmark" command
|
||||
/// @param opts the command options
|
||||
int run(const Options& opts);
|
||||
|
||||
}
|
||||
198
lsfg-vk-cli/src/tools/debug.cpp
Normal file
198
lsfg-vk-cli/src/tools/debug.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "debug.hpp"
|
||||
#include "lsfg-vk-backend/lsfgvk.hpp"
|
||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
||||
#include "lsfg-vk-common/helpers/paths.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 <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
using namespace lsfgvk::cli;
|
||||
using namespace lsfgvk::cli::debug;
|
||||
|
||||
namespace {
|
||||
/// 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 ls::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 ls::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.begin(vk);
|
||||
cmdbuf.copyBufferToImage(vk, stagingbuf, image);
|
||||
cmdbuf.end(vk);
|
||||
|
||||
const vk::TimelineSemaphore sema{vk, 0};
|
||||
cmdbuf.submit(vk);
|
||||
}
|
||||
}
|
||||
|
||||
int debug::run(const Options& opts) {
|
||||
try {
|
||||
// parse options
|
||||
if (opts.flow < 0.25F || opts.flow > 1.0F)
|
||||
throw ls::error("flow scale must be between 0.25 and 1.0");
|
||||
if (opts.multiplier < 2)
|
||||
throw ls::error("multiplier must be 2 or greater");
|
||||
if (opts.width <= 0 || opts.height <= 0)
|
||||
throw ls::error("width and height must be positive integers");
|
||||
const VkExtent2D extent{
|
||||
static_cast<uint32_t>(opts.width),
|
||||
static_cast<uint32_t>(opts.height)
|
||||
};
|
||||
if (!std::filesystem::exists(opts.path))
|
||||
throw ls::error("debug path does not exist: " + opts.path.string());
|
||||
std::vector<std::filesystem::path> paths{};
|
||||
for (const auto& entry : std::filesystem::directory_iterator(opts.path))
|
||||
paths.push_back(entry.path());
|
||||
std::ranges::sort(paths, [](const std::filesystem::path& a, const std::filesystem::path& b) {
|
||||
auto fa = a.filename().string();
|
||||
auto fb = b.filename().string();
|
||||
|
||||
auto norm_a = fa.find_first_of('.');
|
||||
if (norm_a == std::string::npos)
|
||||
throw ls::error("invalid debug file name: " + fa);
|
||||
auto norm_b = fb.find_first_of('.');
|
||||
if (norm_b == std::string::npos)
|
||||
throw ls::error("invalid debug file name: " + fb);
|
||||
|
||||
return std::stoi(fa.substr(0, norm_a)) < std::stoi(fb.substr(0, norm_b));
|
||||
});
|
||||
|
||||
// create instance
|
||||
const vk::Vulkan vk{
|
||||
"lsfg-vk-debug", vk::version{2, 0, 0},
|
||||
"lsfg-vk-debug-engine", vk::version{2, 0, 0},
|
||||
[opts](const vk::VulkanInstanceFuncs fi,
|
||||
const std::vector<VkPhysicalDevice>& devices) {
|
||||
if (!opts.gpu.has_value())
|
||||
return devices.front();
|
||||
|
||||
for (const VkPhysicalDevice& device : devices) {
|
||||
VkPhysicalDeviceProperties2 props{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2
|
||||
};
|
||||
fi.GetPhysicalDeviceProperties2(device, &props);
|
||||
|
||||
auto& properties = props.properties;
|
||||
std::array<char, 256> devname = std::to_array(properties.deviceName);
|
||||
devname[255] = '\0'; // ensure null-termination
|
||||
|
||||
if (std::string(devname.data()) == *opts.gpu)
|
||||
return device;
|
||||
}
|
||||
|
||||
throw ls::error("failed to find specified GPU: " + *opts.gpu);
|
||||
}
|
||||
};
|
||||
|
||||
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 (int i = 0; i < (opts.multiplier - 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};
|
||||
|
||||
// initialize backend
|
||||
std::string dll{};
|
||||
if (opts.dll.has_value())
|
||||
dll = *opts.dll;
|
||||
else
|
||||
dll = ls::findShaderDll();
|
||||
lsfgvk::backend::Instance lsfgvk{
|
||||
[opts](
|
||||
const std::string& gpu_name,
|
||||
std::pair<const std::string&, const std::string&>,
|
||||
const std::optional<std::string>&
|
||||
) {
|
||||
return opts.gpu.value_or(gpu_name) == gpu_name;
|
||||
},
|
||||
dll, opts.allow_fp16
|
||||
};
|
||||
lsfgvk::backend::Context& lsfgvk_ctx = lsfgvk.openContext(
|
||||
srcfds, destfds,
|
||||
syncfd, extent.width, extent.height,
|
||||
false, 1.0F / opts.flow, opts.performance_mode
|
||||
);
|
||||
|
||||
// render destination images
|
||||
size_t idx{1};
|
||||
for (size_t j = 0; j < paths.size(); j++) {
|
||||
upload_image(vk,
|
||||
j % 2 == 0 ? frame_0 : frame_1,
|
||||
paths.at(j).string()
|
||||
);
|
||||
|
||||
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)
|
||||
throw ls::error("failed to wait for frame");
|
||||
}
|
||||
}
|
||||
|
||||
// deinitialize lsfg-vk
|
||||
lsfgvk.closeContext(lsfgvk_ctx);
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "error: " << e.what() << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
30
lsfg-vk-cli/src/tools/debug.hpp
Normal file
30
lsfg-vk-cli/src/tools/debug.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace lsfgvk::cli::debug {
|
||||
|
||||
/// options for the "debug" command
|
||||
struct Options {
|
||||
std::optional<std::string> dll;
|
||||
bool allow_fp16{true};
|
||||
int width{1920};
|
||||
int height{1080};
|
||||
|
||||
float flow{0.85F};
|
||||
int multiplier{2};
|
||||
bool performance_mode{true};
|
||||
std::optional<std::string> gpu;
|
||||
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
/// run the "debug" command
|
||||
/// @param opts the command options
|
||||
int run(const Options& opts);
|
||||
|
||||
}
|
||||
31
lsfg-vk-cli/src/tools/validate.cpp
Normal file
31
lsfg-vk-cli/src/tools/validate.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "validate.hpp"
|
||||
#include "lsfg-vk-common/configuration/config.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
using namespace lsfgvk::cli;
|
||||
using namespace lsfgvk::cli::validate;
|
||||
|
||||
int validate::run(const Options& opts) {
|
||||
std::filesystem::path path{ls::findConfigurationFile()};
|
||||
if (opts.config.has_value())
|
||||
path = *opts.config;
|
||||
|
||||
if (!std::filesystem::exists(path)) {
|
||||
std::cerr << "Validation failed: configuration file does not exist\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
const ls::ConfigFile config{path};
|
||||
std::cerr << "Validation success\n";
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Validation failed: " << e.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
19
lsfg-vk-cli/src/tools/validate.hpp
Normal file
19
lsfg-vk-cli/src/tools/validate.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace lsfgvk::cli::validate {
|
||||
|
||||
/// options for the "validate" command
|
||||
struct Options {
|
||||
std::optional<std::string> config;
|
||||
};
|
||||
|
||||
/// run the "validate" command
|
||||
/// @param opts the command options
|
||||
int run(const Options& opts);
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ set(COMMON_SOURCES
|
|||
"src/configuration/config.cpp"
|
||||
"src/configuration/detection.cpp"
|
||||
"src/helpers/errors.cpp"
|
||||
"src/helpers/paths.cpp"
|
||||
"src/vulkan/buffer.cpp"
|
||||
"src/vulkan/command_buffer.cpp"
|
||||
"src/vulkan/descriptor_pool.cpp"
|
||||
|
|
|
|||
14
lsfg-vk-common/include/lsfg-vk-common/helpers/paths.hpp
Normal file
14
lsfg-vk-common/include/lsfg-vk-common/helpers/paths.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace ls {
|
||||
|
||||
/// find the location of the Lossless.dll
|
||||
/// @returns the path to the DLL
|
||||
/// @throws ls::error if the DLL could not be found
|
||||
std::filesystem::path findShaderDll();
|
||||
|
||||
}
|
||||
49
lsfg-vk-common/src/helpers/paths.cpp
Normal file
49
lsfg-vk-common/src/helpers/paths.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "lsfg-vk-common/helpers/paths.hpp"
|
||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
std::filesystem::path ls::findShaderDll() {
|
||||
const std::vector<std::filesystem::path> FRAGMENTS{{
|
||||
".local/share/Steam/steamapps/common",
|
||||
".steam/steam/steamapps/common",
|
||||
".steam/debian-installation/steamapps/common",
|
||||
".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common",
|
||||
"snap/steam/common/.local/share/Steam/steamapps/common"
|
||||
}};
|
||||
|
||||
// check XDG overridden location
|
||||
const char* xdgPath = std::getenv("XDG_DATA_HOME");
|
||||
if (xdgPath && *xdgPath != '\0') {
|
||||
auto base = std::filesystem::path(xdgPath);
|
||||
|
||||
for (const auto& frag : FRAGMENTS) {
|
||||
auto full = base / frag / "Lossless Scaling" / "Lossless.dll";
|
||||
if (std::filesystem::exists(full))
|
||||
return full;
|
||||
}
|
||||
}
|
||||
|
||||
// check home directory
|
||||
const char* homePath = std::getenv("HOME");
|
||||
if (homePath && *homePath != '\0') {
|
||||
auto base = std::filesystem::path(homePath);
|
||||
|
||||
for (const auto& frag : FRAGMENTS) {
|
||||
auto full = base / frag / "Lossless Scaling" / "Lossless.dll";
|
||||
if (std::filesystem::exists(full))
|
||||
return full;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to same directory
|
||||
auto local = std::filesystem::current_path() / "Lossless.dll";
|
||||
if (std::filesystem::exists(local))
|
||||
return local;
|
||||
|
||||
throw ls::error("unable to locate Lossless.dll, please set the path in the configuration");
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
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)
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#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.begin(vk);
|
||||
cmdbuf.copyBufferToImage(vk, stagingbuf, image);
|
||||
cmdbuf.end(vk);
|
||||
|
||||
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{2, 0, 0},
|
||||
"lsfg-vk-debug-engine", vk::version{2, 0, 0},
|
||||
[](const vk::VulkanInstanceFuncs,
|
||||
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 < 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::backend::Instance lsfgvk{
|
||||
[](
|
||||
const std::string&,
|
||||
std::pair<const std::string&, const std::string&>,
|
||||
const std::optional<std::string>&
|
||||
) {
|
||||
return true;
|
||||
},
|
||||
"/home/pancake/.steam/steam/steamapps/common/Lossless Scaling/Lossless.dll",
|
||||
true
|
||||
};
|
||||
lsfgvk::backend::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
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "instance.hpp"
|
||||
#include "lsfg-vk-common/helpers/paths.hpp"
|
||||
#include "swapchain.hpp"
|
||||
#include "lsfg-vk-common/configuration/detection.hpp"
|
||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
||||
|
|
@ -10,7 +11,6 @@
|
|||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
|
@ -41,47 +41,6 @@ namespace {
|
|||
|
||||
return extensions;
|
||||
}
|
||||
// find the shader dll
|
||||
std::filesystem::path findShaderDll() {
|
||||
const std::vector<std::filesystem::path> FRAGMENTS{{
|
||||
".local/share/Steam/steamapps/common",
|
||||
".steam/steam/steamapps/common",
|
||||
".steam/debian-installation/steamapps/common",
|
||||
".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common",
|
||||
"snap/steam/common/.local/share/Steam/steamapps/common"
|
||||
}};
|
||||
|
||||
// check XDG overridden location
|
||||
const char* xdgPath = std::getenv("XDG_DATA_HOME");
|
||||
if (xdgPath && *xdgPath != '\0') {
|
||||
auto base = std::filesystem::path(xdgPath);
|
||||
|
||||
for (const auto& frag : FRAGMENTS) {
|
||||
auto full = base / frag / "Lossless Scaling" / "Lossless.dll";
|
||||
if (std::filesystem::exists(full))
|
||||
return full;
|
||||
}
|
||||
}
|
||||
|
||||
// check home directory
|
||||
const char* homePath = std::getenv("HOME");
|
||||
if (homePath && *homePath != '\0') {
|
||||
auto base = std::filesystem::path(homePath);
|
||||
|
||||
for (const auto& frag : FRAGMENTS) {
|
||||
auto full = base / frag / "Lossless Scaling" / "Lossless.dll";
|
||||
if (std::filesystem::exists(full))
|
||||
return full;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to same directory
|
||||
auto local = std::filesystem::current_path() / "Lossless.dll";
|
||||
if (std::filesystem::exists(local))
|
||||
return local;
|
||||
|
||||
throw ls::error("unable to locate Lossless.dll, please set the path in the configuration");
|
||||
}
|
||||
}
|
||||
|
||||
Root::Root() {
|
||||
|
|
@ -216,6 +175,12 @@ void Root::createSwapchainContext(const vk::Vulkan& vk,
|
|||
setenv("DISABLE_LSFGVK", "1", 1); // NOLINT (c++-include)
|
||||
|
||||
try {
|
||||
std::string dll{};
|
||||
if (global.dll.has_value())
|
||||
dll = *global.dll;
|
||||
else
|
||||
dll = ls::findShaderDll();
|
||||
|
||||
this->backend.emplace(
|
||||
[gpu = profile.gpu](
|
||||
const std::string& deviceName,
|
||||
|
|
@ -229,8 +194,7 @@ void Root::createSwapchainContext(const vk::Vulkan& vk,
|
|||
|| (ids.first + ":" + ids.second == *gpu)
|
||||
|| (pci && *pci == *gpu);
|
||||
},
|
||||
global.dll.value_or(findShaderDll()),
|
||||
global.allow_fp16
|
||||
dll, global.allow_fp16
|
||||
);
|
||||
} catch (const std::exception& e) {
|
||||
unsetenv("DISABLE_LSFGVK"); // NOLINT (c++-include)
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
#include "utils/benchmark.hpp"
|
||||
#include "config/config.hpp"
|
||||
#include "extract/extract.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <lsfg_3_1.hpp>
|
||||
#include <lsfg_3_1p.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace Benchmark;
|
||||
|
||||
void Benchmark::run(uint32_t width, uint32_t height) {
|
||||
if (!Config::currentConf.has_value())
|
||||
_exit(1); // not possible
|
||||
const auto& globalConf = Config::globalConf;
|
||||
const auto& conf = *Config::currentConf;
|
||||
|
||||
auto* lsfgInitialize = LSFG_3_1::initialize;
|
||||
auto* lsfgCreateContext = LSFG_3_1::createContext;
|
||||
auto* lsfgPresentContext = LSFG_3_1::presentContext;
|
||||
if (conf.performance) {
|
||||
lsfgInitialize = LSFG_3_1P::initialize;
|
||||
lsfgCreateContext = LSFG_3_1P::createContext;
|
||||
lsfgPresentContext = LSFG_3_1P::presentContext;
|
||||
}
|
||||
|
||||
// create the benchmark context
|
||||
const char* lsfgDeviceUUID = std::getenv("LSFG_DEVICE_UUID");
|
||||
const uint64_t deviceUUID = lsfgDeviceUUID
|
||||
? std::stoull(std::string(lsfgDeviceUUID), nullptr, 16) : 0x1463ABAC;
|
||||
|
||||
setenv("DISABLE_LSFG", "1", 1); // NOLINT
|
||||
|
||||
Extract::extractShaders();
|
||||
lsfgInitialize(
|
||||
deviceUUID, // some magic number if not given
|
||||
conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1,
|
||||
globalConf.no_fp16,
|
||||
Extract::getShader
|
||||
);
|
||||
const int32_t ctx = lsfgCreateContext(-1, -1, {},
|
||||
{ .width = width, .height = height },
|
||||
conf.hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM
|
||||
);
|
||||
|
||||
unsetenv("DISABLE_LSFG"); // NOLINT
|
||||
|
||||
// run the benchmark (run 8*n + 1 so the fences are waited on)
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const uint64_t iterations = 8 * 500UL;
|
||||
|
||||
std::cerr << "lsfg-vk: Benchmark started, running " << iterations << " iterations...\n";
|
||||
for (uint64_t count = 0; count < iterations + 1; count++) {
|
||||
lsfgPresentContext(ctx, -1, {});
|
||||
|
||||
if (count % 50 == 0 && count > 0)
|
||||
std::cerr << "lsfg-vk: "
|
||||
<< std::setprecision(2) << std::fixed
|
||||
<< static_cast<float>(count) / static_cast<float>(iterations) * 100.0F
|
||||
<< "% done (" << count + 1 << "/" << iterations << ")\r";
|
||||
}
|
||||
const auto then = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// print results
|
||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(then - now).count();
|
||||
|
||||
const auto perIteration = static_cast<float>(ms) / static_cast<float>(iterations);
|
||||
|
||||
const uint64_t totalGen = (conf.multiplier - 1) * iterations;
|
||||
const auto genFps = static_cast<float>(totalGen) / (static_cast<float>(ms) / 1000.0F);
|
||||
|
||||
const uint64_t totalFrames = iterations * conf.multiplier;
|
||||
const auto totalFps = static_cast<float>(totalFrames) / (static_cast<float>(ms) / 1000.0F);
|
||||
|
||||
std::cerr << "lsfg-vk: Benchmark completed in " << ms << " ms\n";
|
||||
std::cerr << " Time taken per real frame: "
|
||||
<< std::setprecision(2) << std::fixed << perIteration << " ms\n";
|
||||
std::cerr << " Generated " << totalGen << " frames in total at "
|
||||
<< std::setprecision(2) << std::fixed << genFps << " FPS\n";
|
||||
std::cerr << " Total of " << totalFrames << " frames presented at "
|
||||
<< std::setprecision(2) << std::fixed << totalFps << " FPS\n";
|
||||
|
||||
// sleep for a second, then exit
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
_exit(0);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue