mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-12-17 05:22:15 +00:00
refactor(cleanup): main backend context & instance logic
This commit is contained in:
parent
28ee6dbce0
commit
0d95d550aa
27 changed files with 719 additions and 7575 deletions
|
|
@ -1,37 +0,0 @@
|
||||||
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" # allows reinterpret_cast
|
|
||||||
- "-cppcoreguidelines-avoid-non-const-global-variables"
|
|
||||||
- "-cppcoreguidelines-pro-type-union-access"
|
|
||||||
# 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"
|
|
||||||
3
framegen/.gitattributes
vendored
3
framegen/.gitattributes
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
*.cpp diff=cpp eol=lf
|
|
||||||
*.hpp diff=cpp eol=lf
|
|
||||||
*.md diff=markdown eol=lf
|
|
||||||
9
framegen/.gitignore
vendored
9
framegen/.gitignore
vendored
|
|
@ -1,9 +0,0 @@
|
||||||
# cmake files
|
|
||||||
/build
|
|
||||||
|
|
||||||
# ide/lsp files
|
|
||||||
/.zed
|
|
||||||
/.vscode
|
|
||||||
/.clangd
|
|
||||||
/.cache
|
|
||||||
/.ccls
|
|
||||||
|
|
@ -1,76 +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-framegen
|
|
||||||
DESCRIPTION "Lossless Scaling Frame Generation Backend"
|
|
||||||
LANGUAGES C CXX)
|
|
||||||
|
|
||||||
file(GLOB SOURCES
|
|
||||||
"src/common/*.cpp"
|
|
||||||
"src/config/*.cpp"
|
|
||||||
"src/core/*.cpp"
|
|
||||||
"src/pool/*.cpp"
|
|
||||||
"src/*.cpp"
|
|
||||||
"v3.1_src/core/*.cpp"
|
|
||||||
"v3.1_src/pool/*.cpp"
|
|
||||||
"v3.1_src/shaders/*.cpp"
|
|
||||||
"v3.1_src/utils/*.cpp"
|
|
||||||
"v3.1_src/*.cpp"
|
|
||||||
"v3.1p_src/core/*.cpp"
|
|
||||||
"v3.1p_src/pool/*.cpp"
|
|
||||||
"v3.1p_src/shaders/*.cpp"
|
|
||||||
"v3.1p_src/utils/*.cpp"
|
|
||||||
"v3.1p_src/*.cpp"
|
|
||||||
"src/thirdparty/*.c"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(lsfg-vk-framegen STATIC ${SOURCES})
|
|
||||||
|
|
||||||
# target
|
|
||||||
set_target_properties(lsfg-vk-framegen PROPERTIES
|
|
||||||
CXX_STANDARD 20
|
|
||||||
CXX_STANDARD_REQUIRED ON)
|
|
||||||
target_include_directories(lsfg-vk-framegen SYSTEM
|
|
||||||
PUBLIC include/thirdparty)
|
|
||||||
target_include_directories(lsfg-vk-framegen
|
|
||||||
PUBLIC include
|
|
||||||
PUBLIC public
|
|
||||||
PRIVATE v3.1_include
|
|
||||||
PRIVATE v3.1p_include)
|
|
||||||
|
|
||||||
# diagnostics
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
set_target_properties(lsfg-vk-framegen PROPERTIES
|
|
||||||
EXPORT_COMPILE_COMMANDS ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(LSFGVK_EXCESS_DEBUG)
|
|
||||||
target_compile_options(lsfg-vk-framegen 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-framegen PROPERTIES
|
|
||||||
CXX_CLANG_TIDY clang-tidy)
|
|
||||||
endif()
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
## MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 lsfg-vk
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
## lsfg-vk-framegen
|
|
||||||
Lossless Scaling Frame Generation
|
|
||||||
|
|
||||||
This is a subproject of lsfg-vk and contains the dedicated Vulkan logic for generating frames.
|
|
||||||
|
|
||||||
The project is intentionally structured as a fully external project, such that it can be integrated into other applications.
|
|
||||||
|
|
||||||
### Interface
|
|
||||||
|
|
||||||
Interfacing with lsfg-vk-framegen is done via `lsfg_x_x.hpp` header. The internal Vulkan instance is created using `LSFG_X_X::initialize()` and requires a specific deviceUUID, as well as parts of the lsfg-vk configuration, including a function loading SPIR-V shaders by name. Cleanup is done via `LSFG_X_X::finalize()` after which `LSFG_X_X::initialize()` may be called again. Please note that the initialization process is expensive and may take a while. It is recommended to call this function once during the applications lifetime.
|
|
||||||
|
|
||||||
Once the format and extent of the requested images is determined, `LSFG_X_X::createContext()` should be called to initialize a frame generation context. The Vulkan images are created from backing memory, which is passed through the file descriptor arguments. A context can be destroyed using `LSFG_X_X::deleteContext()`.
|
|
||||||
|
|
||||||
Presenting the context can be done via `LSFG_X_X::presentContext()`. Before calling the function a second time, make sure the outgoing semaphores have been signaled.
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/commandbuffer.hpp"
|
|
||||||
#include "core/commandpool.hpp"
|
|
||||||
#include "core/descriptorpool.hpp"
|
|
||||||
#include "core/image.hpp"
|
|
||||||
#include "core/device.hpp"
|
|
||||||
#include "pool/resourcepool.hpp"
|
|
||||||
#include "pool/shaderpool.hpp"
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace LSFG::Utils {
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Insert memory barriers for images in a command buffer.
|
|
||||||
///
|
|
||||||
/// @throws std::logic_error if the command buffer is not in Recording state
|
|
||||||
///
|
|
||||||
class BarrierBuilder {
|
|
||||||
public:
|
|
||||||
/// Create a barrier builder.
|
|
||||||
BarrierBuilder(const Core::CommandBuffer& buffer)
|
|
||||||
: commandBuffer(&buffer) {
|
|
||||||
this->barriers.reserve(16); // this is performance critical
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a resource to the barrier builder.
|
|
||||||
BarrierBuilder& addR2W(Core::Image& image);
|
|
||||||
BarrierBuilder& addW2R(Core::Image& image);
|
|
||||||
|
|
||||||
// Add an optional resource to the barrier builder.
|
|
||||||
BarrierBuilder& addR2W(std::optional<Core::Image>& image) {
|
|
||||||
if (image.has_value()) this->addR2W(*image); return *this; }
|
|
||||||
BarrierBuilder& addW2R(std::optional<Core::Image>& image) {
|
|
||||||
if (image.has_value()) this->addW2R(*image); return *this; }
|
|
||||||
|
|
||||||
/// Add a list of resources to the barrier builder.
|
|
||||||
BarrierBuilder& addR2W(std::vector<Core::Image>& images) {
|
|
||||||
for (auto& image : images) this->addR2W(image); return *this; }
|
|
||||||
BarrierBuilder& addW2R(std::vector<Core::Image>& images) {
|
|
||||||
for (auto& image : images) this->addW2R(image); return *this; }
|
|
||||||
|
|
||||||
/// Add an array of resources to the barrier builder.
|
|
||||||
template<std::size_t N>
|
|
||||||
BarrierBuilder& addR2W(std::array<Core::Image, N>& images) {
|
|
||||||
for (auto& image : images) this->addR2W(image); return *this; }
|
|
||||||
template<std::size_t N>
|
|
||||||
BarrierBuilder& addW2R(std::array<Core::Image, N>& images) {
|
|
||||||
for (auto& image : images) this->addW2R(image); return *this; }
|
|
||||||
|
|
||||||
/// Finish building the barrier
|
|
||||||
void build() const;
|
|
||||||
private:
|
|
||||||
const Core::CommandBuffer* commandBuffer;
|
|
||||||
|
|
||||||
std::vector<VkImageMemoryBarrier2> barriers;
|
|
||||||
};
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Upload a DDS file to a Vulkan image.
|
|
||||||
///
|
|
||||||
/// @param device The Vulkan device
|
|
||||||
/// @param commandPool The command pool
|
|
||||||
/// @param image The Vulkan image to upload to
|
|
||||||
/// @param path The path to the DDS file.
|
|
||||||
///
|
|
||||||
/// @throws std::system_error If the file cannot be opened or read.
|
|
||||||
/// @throws ls:vulkan_error If the Vulkan image cannot be created or updated.
|
|
||||||
///
|
|
||||||
void uploadImage(const Core::Device& device,
|
|
||||||
const Core::CommandPool& commandPool,
|
|
||||||
Core::Image& image, const std::string& path);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Clear a texture to white during setup.
|
|
||||||
///
|
|
||||||
/// @param device The Vulkan device.
|
|
||||||
/// @param image The image to clear.
|
|
||||||
/// @param white If true, the image will be cleared to white, otherwise to black.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error If the Vulkan image cannot be cleared.
|
|
||||||
///
|
|
||||||
void clearImage(const Core::Device& device, Core::Image& image, bool white = false);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace LSFG {
|
|
||||||
struct Vulkan {
|
|
||||||
Core::Device device;
|
|
||||||
Core::CommandPool commandPool;
|
|
||||||
Core::DescriptorPool descriptorPool;
|
|
||||||
|
|
||||||
uint64_t generationCount;
|
|
||||||
float flowScale;
|
|
||||||
bool isHdr;
|
|
||||||
|
|
||||||
Pool::ShaderPool shaders;
|
|
||||||
Pool::ResourcePool resources;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/device.hpp"
|
|
||||||
#include "core/buffer.hpp"
|
|
||||||
#include "core/sampler.hpp"
|
|
||||||
|
|
||||||
#include "vulkan/vulkan_core.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace LSFG::Pool {
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Resource pool for each Vulkan device.
|
|
||||||
///
|
|
||||||
class ResourcePool {
|
|
||||||
public:
|
|
||||||
ResourcePool() noexcept = default;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Create the resource pool.
|
|
||||||
///
|
|
||||||
/// @param isHdr HDR support stored in buffers.
|
|
||||||
/// @param flowScale Scale factor stored in buffers.
|
|
||||||
///
|
|
||||||
/// @throws std::runtime_error if the resource pool cannot be created.
|
|
||||||
///
|
|
||||||
ResourcePool(bool isHdr, float flowScale)
|
|
||||||
: isHdr(isHdr), flowScale(flowScale) {}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Retrieve a buffer with given parameters or create it.
|
|
||||||
///
|
|
||||||
/// @param timestamp Timestamp stored in buffer
|
|
||||||
/// @param firstIter First iteration stored in buffer
|
|
||||||
/// @param firstIterS First special iteration stored in buffer
|
|
||||||
/// @return Created or cached buffer
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the buffer cannot be created.
|
|
||||||
///
|
|
||||||
Core::Buffer getBuffer(
|
|
||||||
const Core::Device& device,
|
|
||||||
float timestamp = 0.0F, bool firstIter = false, bool firstIterS = false);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Retrieve a sampler by type or create it.
|
|
||||||
///
|
|
||||||
/// @param type Type of the sampler
|
|
||||||
/// @param compare Compare operation for the sampler
|
|
||||||
/// @param isWhite Whether the sampler is white
|
|
||||||
/// @return Created or cached sampler
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the sampler cannot be created.
|
|
||||||
///
|
|
||||||
Core::Sampler getSampler(
|
|
||||||
const Core::Device& device,
|
|
||||||
VkSamplerAddressMode type = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
|
||||||
VkCompareOp compare = VK_COMPARE_OP_NEVER,
|
|
||||||
bool isWhite = false);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<uint64_t, Core::Buffer> buffers;
|
|
||||||
std::unordered_map<uint64_t, Core::Sampler> samplers;
|
|
||||||
bool isHdr{};
|
|
||||||
float flowScale{};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
2647
framegen/include/thirdparty/volk.h
vendored
2647
framegen/include/thirdparty/volk.h
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,83 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace LSFG_3_1 {
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Initialize the LSFG library.
|
|
||||||
///
|
|
||||||
/// @param deviceUUID The UUID of the Vulkan device to use.
|
|
||||||
/// @param isHdr Whether the images are in HDR format.
|
|
||||||
/// @param flowScale Internal flow scale factor.
|
|
||||||
/// @param generationCount Number of frames to generate.
|
|
||||||
/// @param forceDisableFp16 Whether to force-disable FP16 optimizations.
|
|
||||||
/// @param loader Function to load shader source code by name.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if Vulkan objects fail to initialize.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void initialize(uint64_t deviceUUID,
|
|
||||||
bool isHdr, float flowScale, uint64_t generationCount,
|
|
||||||
bool forceDisableFp16,
|
|
||||||
const std::function<std::vector<uint8_t>(const std::string&, bool)>& loader);
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
///
|
|
||||||
/// Initialize the renderdoc API.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the renderdoc API cannot be initialized.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void initializeRenderDoc();
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a new LSFG context on a swapchain.
|
|
||||||
///
|
|
||||||
/// @param in0 File descriptor for the first input image.
|
|
||||||
/// @param in1 File descriptor for the second input image.
|
|
||||||
/// @param outN File descriptor for each output image. This defines the LSFG level.
|
|
||||||
/// @param extent The size of the images
|
|
||||||
/// @param format The format of the images.
|
|
||||||
/// @return A unique identifier for the created context.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context cannot be created.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
int32_t createContext(
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Present a context.
|
|
||||||
///
|
|
||||||
/// @param id Unique identifier of the context to present.
|
|
||||||
/// @param inSem Semaphore to wait on before starting the generation.
|
|
||||||
/// @param outSem Semaphores to signal once each output image is ready.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context cannot be presented.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void presentContext(int32_t id, int inSem, const std::vector<int>& outSem);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Delete an LSFG context.
|
|
||||||
///
|
|
||||||
/// @param id Unique identifier of the context to delete.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void deleteContext(int32_t id);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Deinitialize the LSFG library.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void finalize();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace LSFG_3_1P {
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Initialize the LSFG library.
|
|
||||||
///
|
|
||||||
/// @param deviceUUID The UUID of the Vulkan device to use.
|
|
||||||
/// @param isHdr Whether the images are in HDR format.
|
|
||||||
/// @param flowScale Internal flow scale factor.
|
|
||||||
/// @param generationCount Number of frames to generate.
|
|
||||||
/// @param forceDisableFp16 Whether to force-disable FP16 optimizations.
|
|
||||||
/// @param loader Function to load shader source code by name.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if Vulkan objects fail to initialize.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void initialize(uint64_t deviceUUID,
|
|
||||||
bool isHdr, float flowScale, uint64_t generationCount,
|
|
||||||
bool forceDisableFp16,
|
|
||||||
const std::function<std::vector<uint8_t>(const std::string&, bool)>& loader);
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
///
|
|
||||||
/// Initialize the renderdoc API.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the renderdoc API cannot be initialized.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void initializeRenderDoc();
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a new LSFG context on a swapchain.
|
|
||||||
///
|
|
||||||
/// @param in0 File descriptor for the first input image.
|
|
||||||
/// @param in1 File descriptor for the second input image.
|
|
||||||
/// @param outN File descriptor for each output image. This defines the LSFG level.
|
|
||||||
/// @param extent The size of the images
|
|
||||||
/// @param format The format of the images.
|
|
||||||
/// @return A unique identifier for the created context.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context cannot be created.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
int32_t createContext(
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Present a context.
|
|
||||||
///
|
|
||||||
/// @param id Unique identifier of the context to present.
|
|
||||||
/// @param inSem Semaphore to wait on before starting the generation.
|
|
||||||
/// @param outSem Semaphores to signal once each output image is ready.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context cannot be presented.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void presentContext(int32_t id, int inSem, const std::vector<int>& outSem);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Delete an LSFG context.
|
|
||||||
///
|
|
||||||
/// @param id Unique identifier of the context to delete.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void deleteContext(int32_t id);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Deinitialize the LSFG library.
|
|
||||||
///
|
|
||||||
[[gnu::visibility("default")]]
|
|
||||||
void finalize();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
#include <volk.h>
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include "common/utils.hpp"
|
|
||||||
#include "core/buffer.hpp"
|
|
||||||
#include "core/image.hpp"
|
|
||||||
#include "core/device.hpp"
|
|
||||||
#include "core/commandpool.hpp"
|
|
||||||
#include "core/fence.hpp"
|
|
||||||
#include "common/exception.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <ios>
|
|
||||||
#include <system_error>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace LSFG;
|
|
||||||
using namespace LSFG::Utils;
|
|
||||||
|
|
||||||
BarrierBuilder& BarrierBuilder::addR2W(Core::Image& image) {
|
|
||||||
this->barriers.emplace_back(VkImageMemoryBarrier2 {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
|
||||||
.srcAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
|
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
|
|
||||||
.oldLayout = image.getLayout(),
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.image = image.handle(),
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = image.getAspectFlags(),
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
image.setLayout(VK_IMAGE_LAYOUT_GENERAL);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BarrierBuilder& BarrierBuilder::addW2R(Core::Image& image) {
|
|
||||||
this->barriers.emplace_back(VkImageMemoryBarrier2 {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
|
||||||
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
|
||||||
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
|
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
|
|
||||||
.oldLayout = image.getLayout(),
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.image = image.handle(),
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = image.getAspectFlags(),
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
image.setLayout(VK_IMAGE_LAYOUT_GENERAL);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BarrierBuilder::build() const {
|
|
||||||
const VkDependencyInfo dependencyInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
|
||||||
.imageMemoryBarrierCount = static_cast<uint32_t>(this->barriers.size()),
|
|
||||||
.pImageMemoryBarriers = this->barriers.data()
|
|
||||||
};
|
|
||||||
vkCmdPipelineBarrier2(this->commandBuffer->handle(), &dependencyInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::uploadImage(const Core::Device& device, const Core::CommandPool& commandPool,
|
|
||||||
Core::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::system_error(errno, std::generic_category(), "Failed to open image: " + path);
|
|
||||||
|
|
||||||
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::system_error(errno, std::generic_category(), "Failed to read image: " + path);
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
// copy data to buffer
|
|
||||||
const Core::Buffer stagingBuffer(
|
|
||||||
device, code.data(), static_cast<uint32_t>(code.size()),
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT
|
|
||||||
);
|
|
||||||
|
|
||||||
// perform the upload
|
|
||||||
Core::CommandBuffer commandBuffer(device, commandPool);
|
|
||||||
commandBuffer.begin();
|
|
||||||
|
|
||||||
const VkImageMemoryBarrier barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.srcAccessMask = VK_ACCESS_NONE,
|
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.oldLayout = image.getLayout(),
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.image = image.handle(),
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = image.getAspectFlags(),
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
image.setLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
||||||
vkCmdPipelineBarrier(
|
|
||||||
commandBuffer.handle(),
|
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
0, 0, nullptr, 0, nullptr, 1, &barrier
|
|
||||||
);
|
|
||||||
|
|
||||||
auto extent = image.getExtent();
|
|
||||||
const VkBufferImageCopy region{
|
|
||||||
.bufferImageHeight = 0,
|
|
||||||
.imageSubresource = {
|
|
||||||
.aspectMask = image.getAspectFlags(),
|
|
||||||
.layerCount = 1
|
|
||||||
},
|
|
||||||
.imageExtent = { extent.width, extent.height, 1 }
|
|
||||||
};
|
|
||||||
vkCmdCopyBufferToImage(
|
|
||||||
commandBuffer.handle(),
|
|
||||||
stagingBuffer.handle(), image.handle(),
|
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion
|
|
||||||
);
|
|
||||||
|
|
||||||
commandBuffer.end();
|
|
||||||
|
|
||||||
Core::Fence fence(device);
|
|
||||||
commandBuffer.submit(device.getComputeQueue(), fence);
|
|
||||||
|
|
||||||
// wait for the upload to complete
|
|
||||||
if (!fence.wait(device))
|
|
||||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Upload operation timed out");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Utils::clearImage(const Core::Device& device, Core::Image& image, bool white) {
|
|
||||||
Core::Fence fence(device);
|
|
||||||
const Core::CommandPool cmdPool(device);
|
|
||||||
Core::CommandBuffer cmdBuf(device, cmdPool);
|
|
||||||
cmdBuf.begin();
|
|
||||||
|
|
||||||
const VkImageMemoryBarrier2 barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
|
||||||
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
|
|
||||||
.oldLayout = image.getLayout(),
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.image = image.handle(),
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = image.getAspectFlags(),
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const VkDependencyInfo dependencyInfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
|
||||||
.imageMemoryBarrierCount = 1,
|
|
||||||
.pImageMemoryBarriers = &barrier
|
|
||||||
};
|
|
||||||
image.setLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
||||||
vkCmdPipelineBarrier2(cmdBuf.handle(), &dependencyInfo);
|
|
||||||
|
|
||||||
const float clearValue = white ? 1.0F : 0.0F;
|
|
||||||
const VkClearColorValue clearColor = {{ clearValue, clearValue, clearValue, clearValue }};
|
|
||||||
const VkImageSubresourceRange subresourceRange = {
|
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
};
|
|
||||||
vkCmdClearColorImage(cmdBuf.handle(),
|
|
||||||
image.handle(), image.getLayout(),
|
|
||||||
&clearColor,
|
|
||||||
1, &subresourceRange);
|
|
||||||
|
|
||||||
cmdBuf.end();
|
|
||||||
|
|
||||||
cmdBuf.submit(device.getComputeQueue(), fence);
|
|
||||||
if (!fence.wait(device))
|
|
||||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Failed to wait for clearing fence.");
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
#include "pool/resourcepool.hpp"
|
|
||||||
#include "core/buffer.hpp"
|
|
||||||
#include "core/device.hpp"
|
|
||||||
#include "core/sampler.hpp"
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using namespace LSFG;
|
|
||||||
using namespace LSFG::Pool;
|
|
||||||
|
|
||||||
struct ConstantBuffer {
|
|
||||||
std::array<uint32_t, 2> inputOffset;
|
|
||||||
uint32_t firstIter;
|
|
||||||
uint32_t firstIterS;
|
|
||||||
uint32_t advancedColorKind;
|
|
||||||
uint32_t hdrSupport;
|
|
||||||
float resolutionInvScale;
|
|
||||||
float timestamp;
|
|
||||||
float uiThreshold;
|
|
||||||
std::array<uint32_t, 3> pad;
|
|
||||||
};
|
|
||||||
|
|
||||||
Core::Buffer ResourcePool::getBuffer(
|
|
||||||
const Core::Device& device,
|
|
||||||
float timestamp, bool firstIter, bool firstIterS) {
|
|
||||||
uint64_t hash = 0;
|
|
||||||
const union { float f; uint32_t i; } u{
|
|
||||||
.f = timestamp };
|
|
||||||
hash |= u.i;
|
|
||||||
hash |= static_cast<uint64_t>(firstIter) << 32;
|
|
||||||
hash |= static_cast<uint64_t>(firstIterS) << 33;
|
|
||||||
|
|
||||||
auto it = buffers.find(hash);
|
|
||||||
if (it != buffers.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
// create the buffer
|
|
||||||
const ConstantBuffer data{
|
|
||||||
.inputOffset = { 0, 0 },
|
|
||||||
.advancedColorKind = this->isHdr ? 2U : 0U,
|
|
||||||
.hdrSupport = this->isHdr,
|
|
||||||
.resolutionInvScale = this->flowScale,
|
|
||||||
.timestamp = timestamp,
|
|
||||||
.uiThreshold = 0.5F,
|
|
||||||
};
|
|
||||||
Core::Buffer buffer(device, data, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
|
|
||||||
buffers[hash] = buffer;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Sampler ResourcePool::getSampler(
|
|
||||||
const Core::Device& device,
|
|
||||||
VkSamplerAddressMode type,
|
|
||||||
VkCompareOp compare,
|
|
||||||
bool isWhite) {
|
|
||||||
uint64_t hash = 0;
|
|
||||||
hash |= static_cast<uint64_t>(type) << 0;
|
|
||||||
hash |= static_cast<uint64_t>(compare) << 8;
|
|
||||||
hash |= static_cast<uint64_t>(isWhite) << 16;
|
|
||||||
|
|
||||||
auto it = samplers.find(hash);
|
|
||||||
if (it != samplers.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
// create the sampler
|
|
||||||
Core::Sampler sampler(device, type, compare, isWhite);
|
|
||||||
samplers[hash] = sampler;
|
|
||||||
return sampler;
|
|
||||||
}
|
|
||||||
3468
framegen/src/thirdparty/volk.c
vendored
3468
framegen/src/thirdparty/volk.c
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,85 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/image.hpp"
|
|
||||||
#include "core/semaphore.hpp"
|
|
||||||
#include "core/fence.hpp"
|
|
||||||
#include "core/commandbuffer.hpp"
|
|
||||||
#include "shaders/alpha.hpp"
|
|
||||||
#include "shaders/beta.hpp"
|
|
||||||
#include "shaders/delta.hpp"
|
|
||||||
#include "shaders/gamma.hpp"
|
|
||||||
#include "shaders/generate.hpp"
|
|
||||||
#include "shaders/mipmaps.hpp"
|
|
||||||
#include "common/utils.hpp"
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace LSFG_3_1 {
|
|
||||||
|
|
||||||
using namespace LSFG;
|
|
||||||
|
|
||||||
class Context {
|
|
||||||
public:
|
|
||||||
///
|
|
||||||
/// Create a context
|
|
||||||
///
|
|
||||||
/// @param vk The Vulkan instance to use.
|
|
||||||
/// @param in0 File descriptor for the first input image.
|
|
||||||
/// @param in1 File descriptor for the second input image.
|
|
||||||
/// @param outN File descriptors for the output images.
|
|
||||||
/// @param extent The size of the images.
|
|
||||||
/// @param format The format of the images.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context fails to initialize.
|
|
||||||
///
|
|
||||||
Context(Vulkan& vk,
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Present on the context.
|
|
||||||
///
|
|
||||||
/// @param inSem Semaphore to wait on before starting the generation.
|
|
||||||
/// @param outSem Semaphores to signal after each generation is done.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context fails to present.
|
|
||||||
///
|
|
||||||
void present(Vulkan& vk,
|
|
||||||
int inSem, const std::vector<int>& outSem);
|
|
||||||
|
|
||||||
// Trivially copyable, moveable and destructible
|
|
||||||
Context(const Context&) = default;
|
|
||||||
Context& operator=(const Context&) = default;
|
|
||||||
Context(Context&&) = default;
|
|
||||||
Context& operator=(Context&&) = default;
|
|
||||||
~Context() = default;
|
|
||||||
private:
|
|
||||||
Core::Image inImg_0, inImg_1; // inImg_0 is next when fc % 2 == 0
|
|
||||||
uint64_t frameIdx{0};
|
|
||||||
|
|
||||||
struct RenderData {
|
|
||||||
Core::Semaphore inSemaphore; // signaled when input is ready
|
|
||||||
std::vector<Core::Semaphore> internalSemaphores; // signaled when first step is done
|
|
||||||
std::vector<Core::Semaphore> outSemaphores; // signaled when each pass is done
|
|
||||||
std::vector<Core::Fence> completionFences; // fence for completion of each pass
|
|
||||||
|
|
||||||
Core::CommandBuffer cmdBuffer1;
|
|
||||||
std::vector<Core::CommandBuffer> cmdBuffers2; // command buffers for second step
|
|
||||||
|
|
||||||
bool shouldWait{false};
|
|
||||||
};
|
|
||||||
std::array<RenderData, 8> data;
|
|
||||||
|
|
||||||
Shaders::Mipmaps mipmaps;
|
|
||||||
std::array<Shaders::Alpha, 7> alpha;
|
|
||||||
Shaders::Beta beta;
|
|
||||||
std::array<Shaders::Gamma, 7> gamma;
|
|
||||||
std::array<Shaders::Delta, 3> delta;
|
|
||||||
Shaders::Generate generate;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
#include <volk.h>
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include "v3_1/context.hpp"
|
|
||||||
#include "common/utils.hpp"
|
|
||||||
#include "common/exception.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <optional>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using namespace LSFG_3_1;
|
|
||||||
|
|
||||||
Context::Context(Vulkan& vk,
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format) {
|
|
||||||
// import input images
|
|
||||||
this->inImg_0 = Core::Image(vk.device, extent, format,
|
|
||||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT, in0);
|
|
||||||
this->inImg_1 = Core::Image(vk.device, extent, format,
|
|
||||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT, in1);
|
|
||||||
|
|
||||||
// prepare render data
|
|
||||||
for (size_t i = 0; i < 8; i++) {
|
|
||||||
auto& data = this->data.at(i);
|
|
||||||
data.internalSemaphores.resize(vk.generationCount);
|
|
||||||
data.outSemaphores.resize(vk.generationCount);
|
|
||||||
data.completionFences.resize(vk.generationCount);
|
|
||||||
data.cmdBuffers2.resize(vk.generationCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create shader chains
|
|
||||||
this->mipmaps = Shaders::Mipmaps(vk, this->inImg_0, this->inImg_1);
|
|
||||||
for (size_t i = 0; i < 7; i++)
|
|
||||||
this->alpha.at(i) = Shaders::Alpha(vk, this->mipmaps.getOutImages().at(i));
|
|
||||||
this->beta = Shaders::Beta(vk, this->alpha.at(0).getOutImages());
|
|
||||||
for (size_t i = 0; i < 7; i++) {
|
|
||||||
this->gamma.at(i) = Shaders::Gamma(vk,
|
|
||||||
this->alpha.at(6 - i).getOutImages(),
|
|
||||||
this->beta.getOutImages().at(std::min<size_t>(6 - i, 5)),
|
|
||||||
(i == 0) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()));
|
|
||||||
if (i < 4) continue;
|
|
||||||
|
|
||||||
this->delta.at(i - 4) = Shaders::Delta(vk,
|
|
||||||
this->alpha.at(6 - i).getOutImages(),
|
|
||||||
this->beta.getOutImages().at(6 - i),
|
|
||||||
(i == 4) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()),
|
|
||||||
(i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage1()),
|
|
||||||
(i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage2()));
|
|
||||||
}
|
|
||||||
this->generate = Shaders::Generate(vk,
|
|
||||||
this->inImg_0, this->inImg_1,
|
|
||||||
this->gamma.at(6).getOutImage(),
|
|
||||||
this->delta.at(2).getOutImage1(),
|
|
||||||
this->delta.at(2).getOutImage2(),
|
|
||||||
outN, format);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Context::present(Vulkan& vk,
|
|
||||||
int inSem, const std::vector<int>& outSem) {
|
|
||||||
auto& data = this->data.at(this->frameIdx % 8);
|
|
||||||
|
|
||||||
// 3. wait for completion of previous frame in this slot
|
|
||||||
if (data.shouldWait)
|
|
||||||
for (auto& fence : data.completionFences)
|
|
||||||
if (!fence.wait(vk.device, UINT64_MAX))
|
|
||||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Fence wait timed out");
|
|
||||||
data.shouldWait = true;
|
|
||||||
|
|
||||||
// 1. create mipmaps and process input image
|
|
||||||
if (inSem >= 0) data.inSemaphore = Core::Semaphore(vk.device, inSem);
|
|
||||||
for (size_t i = 0; i < vk.generationCount; i++)
|
|
||||||
data.internalSemaphores.at(i) = Core::Semaphore(vk.device);
|
|
||||||
|
|
||||||
data.cmdBuffer1 = Core::CommandBuffer(vk.device, vk.commandPool);
|
|
||||||
data.cmdBuffer1.begin();
|
|
||||||
|
|
||||||
this->mipmaps.Dispatch(data.cmdBuffer1, this->frameIdx);
|
|
||||||
for (size_t i = 0; i < 7; i++)
|
|
||||||
this->alpha.at(6 - i).Dispatch(data.cmdBuffer1, this->frameIdx);
|
|
||||||
this->beta.Dispatch(data.cmdBuffer1, this->frameIdx);
|
|
||||||
|
|
||||||
data.cmdBuffer1.end();
|
|
||||||
std::vector<Core::Semaphore> waits = { data.inSemaphore };
|
|
||||||
if (inSem < 0) waits.clear();
|
|
||||||
data.cmdBuffer1.submit(vk.device.getComputeQueue(), std::nullopt,
|
|
||||||
waits, std::nullopt,
|
|
||||||
data.internalSemaphores, std::nullopt);
|
|
||||||
|
|
||||||
// 2. generate intermediary frames
|
|
||||||
for (size_t pass = 0; pass < vk.generationCount; pass++) {
|
|
||||||
auto& internalSemaphore = data.internalSemaphores.at(pass);
|
|
||||||
auto& outSemaphore = data.outSemaphores.at(pass);
|
|
||||||
if (inSem >= 0) outSemaphore = Core::Semaphore(vk.device, outSem.empty() ? -1 : outSem.at(pass));
|
|
||||||
auto& completionFence = data.completionFences.at(pass);
|
|
||||||
completionFence = Core::Fence(vk.device);
|
|
||||||
|
|
||||||
auto& buf2 = data.cmdBuffers2.at(pass);
|
|
||||||
buf2 = Core::CommandBuffer(vk.device, vk.commandPool);
|
|
||||||
buf2.begin();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 7; i++) {
|
|
||||||
this->gamma.at(i).Dispatch(buf2, this->frameIdx, pass);
|
|
||||||
if (i >= 4)
|
|
||||||
this->delta.at(i - 4).Dispatch(buf2, this->frameIdx, pass);
|
|
||||||
}
|
|
||||||
this->generate.Dispatch(buf2, this->frameIdx, pass);
|
|
||||||
|
|
||||||
buf2.end();
|
|
||||||
std::vector<Core::Semaphore> signals = { outSemaphore };
|
|
||||||
if (inSem < 0) signals.clear();
|
|
||||||
buf2.submit(vk.device.getComputeQueue(), completionFence,
|
|
||||||
{ internalSemaphore }, std::nullopt,
|
|
||||||
signals, std::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->frameIdx++;
|
|
||||||
}
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
#include <volk.h>
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include "lsfg_3_1.hpp"
|
|
||||||
#include "v3_1/context.hpp"
|
|
||||||
#include "core/commandpool.hpp"
|
|
||||||
#include "core/descriptorpool.hpp"
|
|
||||||
#include "core/instance.hpp"
|
|
||||||
#include "pool/shaderpool.hpp"
|
|
||||||
#include "common/exception.hpp"
|
|
||||||
#include "common/utils.hpp"
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
#include <renderdoc_app.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <optional>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <ctime>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace LSFG;
|
|
||||||
using namespace LSFG_3_1;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::optional<Core::Instance> instance;
|
|
||||||
std::optional<Vulkan> device;
|
|
||||||
std::unordered_map<int32_t, Context> contexts;
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
std::optional<RENDERDOC_API_1_6_0*> renderdoc;
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1::initialize(uint64_t deviceUUID,
|
|
||||||
bool isHdr, float flowScale, uint64_t generationCount,
|
|
||||||
bool forceDisableFp16,
|
|
||||||
const std::function<std::vector<uint8_t>(const std::string&, bool)>& loader) {
|
|
||||||
if (instance.has_value() || device.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
instance.emplace();
|
|
||||||
device.emplace(Vulkan {
|
|
||||||
.device{*instance, deviceUUID, forceDisableFp16},
|
|
||||||
.generationCount = generationCount,
|
|
||||||
.flowScale = flowScale,
|
|
||||||
.isHdr = isHdr
|
|
||||||
});
|
|
||||||
contexts = std::unordered_map<int32_t, Context>();
|
|
||||||
|
|
||||||
device->commandPool = Core::CommandPool(device->device);
|
|
||||||
device->descriptorPool = Core::DescriptorPool(device->device);
|
|
||||||
|
|
||||||
device->resources = Pool::ResourcePool(device->isHdr, device->flowScale);
|
|
||||||
device->shaders = Pool::ShaderPool(loader, device->device.getFP16Support());
|
|
||||||
|
|
||||||
std::srand(static_cast<uint32_t>(std::time(nullptr)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
void LSFG_3_1::initializeRenderDoc() {
|
|
||||||
if (renderdoc.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (void* mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD)) {
|
|
||||||
auto rdocGetAPI = reinterpret_cast<pRENDERDOC_GetAPI>(dlsym(mod, "RENDERDOC_GetAPI"));
|
|
||||||
RENDERDOC_API_1_6_0* rdoc{};
|
|
||||||
rdocGetAPI(eRENDERDOC_API_Version_1_6_0, reinterpret_cast<void**>(&rdoc));
|
|
||||||
|
|
||||||
renderdoc.emplace(rdoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!renderdoc.has_value()) {
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "RenderDoc API not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
int32_t LSFG_3_1::createContext(
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format) {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
|
||||||
|
|
||||||
const int32_t id = std::rand();
|
|
||||||
contexts.emplace(id, Context(*device, in0, in1, outN, extent, format));
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1::presentContext(int32_t id, int inSem, const std::vector<int>& outSem) {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
|
||||||
|
|
||||||
auto it = contexts.find(id);
|
|
||||||
if (it == contexts.end())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Context not found");
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
if (renderdoc.has_value())
|
|
||||||
(*renderdoc)->StartFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance->handle()), nullptr);
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
it->second.present(*device, inSem, outSem);
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
if (renderdoc.has_value()) {
|
|
||||||
vkDeviceWaitIdle(device->device.handle());
|
|
||||||
(*renderdoc)->EndFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance->handle()), nullptr);
|
|
||||||
}
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1::deleteContext(int32_t id) {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
|
||||||
|
|
||||||
auto it = contexts.find(id);
|
|
||||||
if (it == contexts.end())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_DEVICE_LOST, "No such context");
|
|
||||||
|
|
||||||
vkDeviceWaitIdle(device->device.handle());
|
|
||||||
contexts.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1::finalize() {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
vkDeviceWaitIdle(device->device.handle());
|
|
||||||
contexts.clear();
|
|
||||||
device.reset();
|
|
||||||
instance.reset();
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/image.hpp"
|
|
||||||
#include "core/semaphore.hpp"
|
|
||||||
#include "core/fence.hpp"
|
|
||||||
#include "core/commandbuffer.hpp"
|
|
||||||
#include "shaders/alpha.hpp"
|
|
||||||
#include "shaders/beta.hpp"
|
|
||||||
#include "shaders/delta.hpp"
|
|
||||||
#include "shaders/gamma.hpp"
|
|
||||||
#include "shaders/generate.hpp"
|
|
||||||
#include "shaders/mipmaps.hpp"
|
|
||||||
#include "common/utils.hpp"
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace LSFG_3_1P {
|
|
||||||
|
|
||||||
using namespace LSFG;
|
|
||||||
|
|
||||||
class Context {
|
|
||||||
public:
|
|
||||||
///
|
|
||||||
/// Create a context
|
|
||||||
///
|
|
||||||
/// @param vk The Vulkan instance to use.
|
|
||||||
/// @param in0 File descriptor for the first input image.
|
|
||||||
/// @param in1 File descriptor for the second input image.
|
|
||||||
/// @param outN File descriptors for the output images.
|
|
||||||
/// @param extent The size of the images.
|
|
||||||
/// @param format The format of the images.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context fails to initialize.
|
|
||||||
///
|
|
||||||
Context(Vulkan& vk,
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Present on the context.
|
|
||||||
///
|
|
||||||
/// @param inSem Semaphore to wait on before starting the generation.
|
|
||||||
/// @param outSem Semaphores to signal after each generation is done.
|
|
||||||
///
|
|
||||||
/// @throws LSFG::vulkan_error if the context fails to present.
|
|
||||||
///
|
|
||||||
void present(Vulkan& vk,
|
|
||||||
int inSem, const std::vector<int>& outSem);
|
|
||||||
|
|
||||||
// Trivially copyable, moveable and destructible
|
|
||||||
Context(const Context&) = default;
|
|
||||||
Context& operator=(const Context&) = default;
|
|
||||||
Context(Context&&) = default;
|
|
||||||
Context& operator=(Context&&) = default;
|
|
||||||
~Context() = default;
|
|
||||||
private:
|
|
||||||
Core::Image inImg_0, inImg_1; // inImg_0 is next when fc % 2 == 0
|
|
||||||
uint64_t frameIdx{0};
|
|
||||||
|
|
||||||
struct RenderData {
|
|
||||||
Core::Semaphore inSemaphore; // signaled when input is ready
|
|
||||||
std::vector<Core::Semaphore> internalSemaphores; // signaled when first step is done
|
|
||||||
std::vector<Core::Semaphore> outSemaphores; // signaled when each pass is done
|
|
||||||
std::vector<Core::Fence> completionFences; // fence for completion of each pass
|
|
||||||
|
|
||||||
Core::CommandBuffer cmdBuffer1;
|
|
||||||
std::vector<Core::CommandBuffer> cmdBuffers2; // command buffers for second step
|
|
||||||
|
|
||||||
bool shouldWait{false};
|
|
||||||
};
|
|
||||||
std::array<RenderData, 8> data;
|
|
||||||
|
|
||||||
Shaders::Mipmaps mipmaps;
|
|
||||||
std::array<Shaders::Alpha, 7> alpha;
|
|
||||||
Shaders::Beta beta;
|
|
||||||
std::array<Shaders::Gamma, 7> gamma;
|
|
||||||
std::array<Shaders::Delta, 3> delta;
|
|
||||||
Shaders::Generate generate;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
#include <volk.h>
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include "v3_1p/context.hpp"
|
|
||||||
#include "common/utils.hpp"
|
|
||||||
#include "common/exception.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <optional>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using namespace LSFG;
|
|
||||||
using namespace LSFG_3_1P;
|
|
||||||
|
|
||||||
Context::Context(Vulkan& vk,
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format) {
|
|
||||||
// import input images
|
|
||||||
this->inImg_0 = Core::Image(vk.device, extent, format,
|
|
||||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT, in0);
|
|
||||||
this->inImg_1 = Core::Image(vk.device, extent, format,
|
|
||||||
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT, in1);
|
|
||||||
|
|
||||||
// prepare render data
|
|
||||||
for (size_t i = 0; i < 8; i++) {
|
|
||||||
auto& data = this->data.at(i);
|
|
||||||
data.internalSemaphores.resize(vk.generationCount);
|
|
||||||
data.outSemaphores.resize(vk.generationCount);
|
|
||||||
data.completionFences.resize(vk.generationCount);
|
|
||||||
data.cmdBuffers2.resize(vk.generationCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create shader chains
|
|
||||||
this->mipmaps = Shaders::Mipmaps(vk, this->inImg_0, this->inImg_1);
|
|
||||||
for (size_t i = 0; i < 7; i++)
|
|
||||||
this->alpha.at(i) = Shaders::Alpha(vk, this->mipmaps.getOutImages().at(i));
|
|
||||||
this->beta = Shaders::Beta(vk, this->alpha.at(0).getOutImages());
|
|
||||||
for (size_t i = 0; i < 7; i++) {
|
|
||||||
this->gamma.at(i) = Shaders::Gamma(vk,
|
|
||||||
this->alpha.at(6 - i).getOutImages(),
|
|
||||||
this->beta.getOutImages().at(std::min<size_t>(6 - i, 5)),
|
|
||||||
(i == 0) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()));
|
|
||||||
if (i < 4) continue;
|
|
||||||
|
|
||||||
this->delta.at(i - 4) = Shaders::Delta(vk,
|
|
||||||
this->alpha.at(6 - i).getOutImages(),
|
|
||||||
this->beta.getOutImages().at(6 - i),
|
|
||||||
(i == 4) ? std::nullopt : std::make_optional(this->gamma.at(i - 1).getOutImage()),
|
|
||||||
(i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage1()),
|
|
||||||
(i == 4) ? std::nullopt : std::make_optional(this->delta.at(i - 5).getOutImage2()));
|
|
||||||
}
|
|
||||||
this->generate = Shaders::Generate(vk,
|
|
||||||
this->inImg_0, this->inImg_1,
|
|
||||||
this->gamma.at(6).getOutImage(),
|
|
||||||
this->delta.at(2).getOutImage1(),
|
|
||||||
this->delta.at(2).getOutImage2(),
|
|
||||||
outN, format);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Context::present(Vulkan& vk,
|
|
||||||
int inSem, const std::vector<int>& outSem) {
|
|
||||||
auto& data = this->data.at(this->frameIdx % 8);
|
|
||||||
|
|
||||||
// 3. wait for completion of previous frame in this slot
|
|
||||||
if (data.shouldWait)
|
|
||||||
for (auto& fence : data.completionFences)
|
|
||||||
if (!fence.wait(vk.device, UINT64_MAX))
|
|
||||||
throw LSFG::vulkan_error(VK_TIMEOUT, "Fence wait timed out");
|
|
||||||
data.shouldWait = true;
|
|
||||||
|
|
||||||
// 1. create mipmaps and process input image
|
|
||||||
if (inSem >= 0) data.inSemaphore = Core::Semaphore(vk.device, inSem);
|
|
||||||
for (size_t i = 0; i < vk.generationCount; i++)
|
|
||||||
data.internalSemaphores.at(i) = Core::Semaphore(vk.device);
|
|
||||||
|
|
||||||
data.cmdBuffer1 = Core::CommandBuffer(vk.device, vk.commandPool);
|
|
||||||
data.cmdBuffer1.begin();
|
|
||||||
|
|
||||||
this->mipmaps.Dispatch(data.cmdBuffer1, this->frameIdx);
|
|
||||||
for (size_t i = 0; i < 7; i++)
|
|
||||||
this->alpha.at(6 - i).Dispatch(data.cmdBuffer1, this->frameIdx);
|
|
||||||
this->beta.Dispatch(data.cmdBuffer1, this->frameIdx);
|
|
||||||
|
|
||||||
data.cmdBuffer1.end();
|
|
||||||
std::vector<Core::Semaphore> waits = { data.inSemaphore };
|
|
||||||
if (inSem < 0) waits.clear();
|
|
||||||
data.cmdBuffer1.submit(vk.device.getComputeQueue(), std::nullopt,
|
|
||||||
waits, std::nullopt,
|
|
||||||
data.internalSemaphores, std::nullopt);
|
|
||||||
|
|
||||||
// 2. generate intermediary frames
|
|
||||||
for (size_t pass = 0; pass < vk.generationCount; pass++) {
|
|
||||||
auto& internalSemaphore = data.internalSemaphores.at(pass);
|
|
||||||
auto& outSemaphore = data.outSemaphores.at(pass);
|
|
||||||
if (inSem >= 0) outSemaphore = Core::Semaphore(vk.device, outSem.empty() ? -1 : outSem.at(pass));
|
|
||||||
auto& completionFence = data.completionFences.at(pass);
|
|
||||||
completionFence = Core::Fence(vk.device);
|
|
||||||
|
|
||||||
auto& buf2 = data.cmdBuffers2.at(pass);
|
|
||||||
buf2 = Core::CommandBuffer(vk.device, vk.commandPool);
|
|
||||||
buf2.begin();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 7; i++) {
|
|
||||||
this->gamma.at(i).Dispatch(buf2, this->frameIdx, pass);
|
|
||||||
if (i >= 4)
|
|
||||||
this->delta.at(i - 4).Dispatch(buf2, this->frameIdx, pass);
|
|
||||||
}
|
|
||||||
this->generate.Dispatch(buf2, this->frameIdx, pass);
|
|
||||||
|
|
||||||
buf2.end();
|
|
||||||
std::vector<Core::Semaphore> signals = { outSemaphore };
|
|
||||||
if (inSem < 0) signals.clear();
|
|
||||||
buf2.submit(vk.device.getComputeQueue(), completionFence,
|
|
||||||
{ internalSemaphore }, std::nullopt,
|
|
||||||
signals, std::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->frameIdx++;
|
|
||||||
}
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
#include <volk.h>
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include "lsfg_3_1p.hpp"
|
|
||||||
#include "v3_1p/context.hpp"
|
|
||||||
#include "core/commandpool.hpp"
|
|
||||||
#include "core/descriptorpool.hpp"
|
|
||||||
#include "core/instance.hpp"
|
|
||||||
#include "pool/shaderpool.hpp"
|
|
||||||
#include "common/exception.hpp"
|
|
||||||
#include "common/utils.hpp"
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
#include <renderdoc_app.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <optional>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <ctime>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace LSFG;
|
|
||||||
using namespace LSFG_3_1P;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::optional<Core::Instance> instance;
|
|
||||||
std::optional<Vulkan> device;
|
|
||||||
std::unordered_map<int32_t, Context> contexts;
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
std::optional<RENDERDOC_API_1_6_0*> renderdoc;
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1P::initialize(uint64_t deviceUUID,
|
|
||||||
bool isHdr, float flowScale, uint64_t generationCount,
|
|
||||||
bool forceDisableFp16,
|
|
||||||
const std::function<std::vector<uint8_t>(const std::string&, bool)>& loader) {
|
|
||||||
if (instance.has_value() || device.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
instance.emplace();
|
|
||||||
device.emplace(Vulkan {
|
|
||||||
.device{*instance, deviceUUID, forceDisableFp16},
|
|
||||||
.generationCount = generationCount,
|
|
||||||
.flowScale = flowScale,
|
|
||||||
.isHdr = isHdr
|
|
||||||
});
|
|
||||||
contexts = std::unordered_map<int32_t, Context>();
|
|
||||||
|
|
||||||
device->commandPool = Core::CommandPool(device->device);
|
|
||||||
device->descriptorPool = Core::DescriptorPool(device->device);
|
|
||||||
|
|
||||||
device->resources = Pool::ResourcePool(device->isHdr, device->flowScale);
|
|
||||||
device->shaders = Pool::ShaderPool(loader, device->device.getFP16Support());
|
|
||||||
|
|
||||||
std::srand(static_cast<uint32_t>(std::time(nullptr)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
void LSFG_3_1P::initializeRenderDoc() {
|
|
||||||
if (renderdoc.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (void* mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD)) {
|
|
||||||
auto rdocGetAPI = reinterpret_cast<pRENDERDOC_GetAPI>(dlsym(mod, "RENDERDOC_GetAPI"));
|
|
||||||
RENDERDOC_API_1_6_0* rdoc{};
|
|
||||||
rdocGetAPI(eRENDERDOC_API_Version_1_6_0, reinterpret_cast<void**>(&rdoc));
|
|
||||||
|
|
||||||
renderdoc.emplace(rdoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!renderdoc.has_value()) {
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "RenderDoc API not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
int32_t LSFG_3_1P::createContext(
|
|
||||||
int in0, int in1, const std::vector<int>& outN,
|
|
||||||
VkExtent2D extent, VkFormat format) {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
|
||||||
|
|
||||||
const int32_t id = std::rand();
|
|
||||||
contexts.emplace(id, Context(*device, in0, in1, outN, extent, format));
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1P::presentContext(int32_t id, int inSem, const std::vector<int>& outSem) {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
|
||||||
|
|
||||||
auto it = contexts.find(id);
|
|
||||||
if (it == contexts.end())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Context not found");
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
if (renderdoc.has_value())
|
|
||||||
(*renderdoc)->StartFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance->handle()), nullptr);
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
|
|
||||||
it->second.present(*device, inSem, outSem);
|
|
||||||
|
|
||||||
#ifdef LSFGVK_EXCESS_DEBUG
|
|
||||||
if (renderdoc.has_value()) {
|
|
||||||
vkDeviceWaitIdle(device->device.handle());
|
|
||||||
(*renderdoc)->EndFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance->handle()), nullptr);
|
|
||||||
}
|
|
||||||
#endif // LSFGVK_EXCESS_DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1P::deleteContext(int32_t id) {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized");
|
|
||||||
|
|
||||||
auto it = contexts.find(id);
|
|
||||||
if (it == contexts.end())
|
|
||||||
throw LSFG::vulkan_error(VK_ERROR_DEVICE_LOST, "No such context");
|
|
||||||
|
|
||||||
vkDeviceWaitIdle(device->device.handle());
|
|
||||||
contexts.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LSFG_3_1P::finalize() {
|
|
||||||
if (!instance.has_value() || !device.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
vkDeviceWaitIdle(device->device.handle());
|
|
||||||
contexts.clear();
|
|
||||||
device.reset();
|
|
||||||
instance.reset();
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
set(BACKEND_SOURCES
|
set(BACKEND_SOURCES
|
||||||
"src/extraction/dll_reader.cpp"
|
"src/extraction/dll_reader.cpp"
|
||||||
"src/extraction/shader_registry.cpp"
|
"src/extraction/shader_registry.cpp"
|
||||||
"src/helpers/managed_shader.cpp")
|
"src/helpers/managed_shader.cpp"
|
||||||
|
"src/helpers/utils.cpp"
|
||||||
|
"src/lsfgvk.cpp")
|
||||||
|
|
||||||
add_library(lsfg-vk-backend STATIC ${BACKEND_SOURCES})
|
add_library(lsfg-vk-backend STATIC ${BACKEND_SOURCES})
|
||||||
|
|
||||||
|
|
|
||||||
122
lsfg-vk-backend/include/lsfg-vk-backend/lsfgvk.hpp
Normal file
122
lsfg-vk-backend/include/lsfg-vk-backend/lsfgvk.hpp
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace lsfgvk {
|
||||||
|
|
||||||
|
class [[gnu::visibility("default")]] ContextImpl;
|
||||||
|
class [[gnu::visibility("default")]] InstanceImpl;
|
||||||
|
|
||||||
|
using Context = ContextImpl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Primitive exception class that deliveres a detailed error message
|
||||||
|
///
|
||||||
|
class [[gnu::visibility("default")]] error : public std::runtime_error { // NOLINT
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Construct an error
|
||||||
|
///
|
||||||
|
/// @param msg Error message.
|
||||||
|
/// @param inner Inner exception.
|
||||||
|
///
|
||||||
|
explicit error(const std::string& msg, const std::exception& inner);
|
||||||
|
|
||||||
|
/// Get the exception message
|
||||||
|
[[nodiscard]] const char* what() const noexcept override {
|
||||||
|
return msg.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
~error() override;
|
||||||
|
private:
|
||||||
|
std::string msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Main entry point of the library
|
||||||
|
///
|
||||||
|
class [[gnu::visibility("default")]] Instance {
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Create a lsfg-vk instance
|
||||||
|
///
|
||||||
|
/// @param devicePicker Function that picks a physical device based on its name.
|
||||||
|
/// @param shaderDllPath Path to the Lossless.dll file to load shaders from.
|
||||||
|
/// @param allowLowPrecision Whether to load low-precision (FP16) shaders if supported by the device.
|
||||||
|
///
|
||||||
|
/// @throws lsfgvk::error on failure
|
||||||
|
///
|
||||||
|
Instance(
|
||||||
|
const std::function<bool(const std::string&)>& devicePicker,
|
||||||
|
const std::filesystem::path& shaderDllPath,
|
||||||
|
bool allowLowPrecision
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Open a frame generation context.
|
||||||
|
///
|
||||||
|
/// The VkFormat of the exchanged images is inferred from whether hdr is true or false:
|
||||||
|
/// - false: VK_FORMAT_R8G8B8A8_UNORM
|
||||||
|
/// - true: VK_FORMAT_R16G16B16A16_SFLOAT
|
||||||
|
///
|
||||||
|
/// The application and library must keep track of the frame index. When the next frame
|
||||||
|
/// is ready, signal the syncFd with one increment (with the first trigger being 1).
|
||||||
|
/// Each generated frame will increment the semaphore by one:
|
||||||
|
/// - Application signals 1 -> Start generating with (curr, next) source images
|
||||||
|
/// - Library signals 1 -> First frame between (curr, next) is ready
|
||||||
|
/// - Library signals N -> N-th frame between (curr, next) is ready
|
||||||
|
/// - Application signals N+1 -> Start generating with (next, curr) source images
|
||||||
|
///
|
||||||
|
/// @param sourceFds Pair of file descriptors for the source images alternated between.
|
||||||
|
/// @param destFds Vector with file descriptors to import output images from.
|
||||||
|
/// @param syncFd File descriptor for the timeline semaphore used for synchronization.
|
||||||
|
/// @param width Width of the images.
|
||||||
|
/// @param height Height of the images.
|
||||||
|
/// @param hdr Whether the images are HDR.
|
||||||
|
/// @param flow Motion flow factor.
|
||||||
|
/// @param perf Whether to enable performance mode.
|
||||||
|
///
|
||||||
|
/// @throws lsfgvk::error on failure
|
||||||
|
///
|
||||||
|
Context& openContext(
|
||||||
|
std::pair<int, int> sourceFds,
|
||||||
|
const std::vector<int>& destFds,
|
||||||
|
int syncFd,
|
||||||
|
uint32_t width, uint32_t height,
|
||||||
|
bool hdr, float flow, bool perf
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Schedule a new set of generated frames.
|
||||||
|
///
|
||||||
|
/// @param context Context to use.
|
||||||
|
/// @throws lsfgvk::error on failure
|
||||||
|
///
|
||||||
|
void scheduleFrames(Context& context);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Close a frame generation context
|
||||||
|
///
|
||||||
|
/// @param context Context to close.
|
||||||
|
///
|
||||||
|
void closeContext(const Context& context);
|
||||||
|
|
||||||
|
// Non-copyable and non-movable
|
||||||
|
Instance(const Instance&) = delete;
|
||||||
|
Instance& operator=(const Instance&) = delete;
|
||||||
|
Instance(Instance&&) = delete;
|
||||||
|
Instance& operator=(Instance&&) = delete;
|
||||||
|
virtual ~Instance();
|
||||||
|
private:
|
||||||
|
std::unique_ptr<InstanceImpl> m_impl;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Context>> m_contexts;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
37
lsfg-vk-backend/src/helpers/utils.cpp
Normal file
37
lsfg-vk-backend/src/helpers/utils.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
using namespace ls;
|
||||||
|
|
||||||
|
ConstantBuffer ls::getDefaultConstantBuffer(
|
||||||
|
size_t index, size_t total,
|
||||||
|
bool hdr, float invFlow,
|
||||||
|
bool isFirst, bool isFirst2) {
|
||||||
|
return ConstantBuffer {
|
||||||
|
.firstIter = isFirst ? 1U : 0U,
|
||||||
|
.firstIterS = isFirst2 ? 1U : 0U,
|
||||||
|
.advancedColorKind = hdr ? 2U : 0U,
|
||||||
|
.hdrSupport = hdr ? 1U : 0U,
|
||||||
|
.resolutionInvScale = invFlow,
|
||||||
|
.timestamp = static_cast<float>(index + 1) / static_cast<float>(total + 1),
|
||||||
|
.uiThreshold = 0.5F
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D ls::shift_extent(VkExtent2D extent, uint32_t i) {
|
||||||
|
return VkExtent2D{
|
||||||
|
.width = extent.width >> i,
|
||||||
|
.height = extent.height >> i
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D ls::add_shift_extent(VkExtent2D extent, uint32_t a, uint32_t i) {
|
||||||
|
return VkExtent2D{
|
||||||
|
.width = (extent.width + a) >> i,
|
||||||
|
.height = (extent.height + a) >> i
|
||||||
|
};
|
||||||
|
}
|
||||||
74
lsfg-vk-backend/src/helpers/utils.hpp
Normal file
74
lsfg-vk-backend/src/helpers/utils.hpp
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../extraction/shader_registry.hpp"
|
||||||
|
#include "lsfg-vk-common/helpers/pointers.hpp"
|
||||||
|
#include "lsfg-vk-common/vulkan/buffer.hpp"
|
||||||
|
#include "lsfg-vk-common/vulkan/sampler.hpp"
|
||||||
|
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
namespace ls {
|
||||||
|
/// exposed context data
|
||||||
|
struct Ctx {
|
||||||
|
ls::R<const vk::Vulkan> vk; // safe back reference
|
||||||
|
ls::R<const extr::ShaderRegistry> shaders; // safe back reference
|
||||||
|
|
||||||
|
vk::Buffer constantBuffer;
|
||||||
|
vk::Sampler bnbSampler; //!< border, no compare, black
|
||||||
|
vk::Sampler bnwSampler; //!< border, no compare, white
|
||||||
|
vk::Sampler eabSampler; //!< edge, always compare, black
|
||||||
|
|
||||||
|
VkExtent2D sourceExtent;
|
||||||
|
VkExtent2D flowExtent;
|
||||||
|
|
||||||
|
bool hdr;
|
||||||
|
float flow;
|
||||||
|
bool perf;
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// constant buffer used in shaders
|
||||||
|
struct ConstantBuffer {
|
||||||
|
std::array<uint32_t, 2> inputOffset;
|
||||||
|
uint32_t firstIter;
|
||||||
|
uint32_t firstIterS;
|
||||||
|
uint32_t advancedColorKind;
|
||||||
|
uint32_t hdrSupport;
|
||||||
|
float resolutionInvScale;
|
||||||
|
float timestamp;
|
||||||
|
float uiThreshold;
|
||||||
|
std::array<uint32_t, 3> pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// get a prefilled constant buffer
|
||||||
|
/// @param index timestamp index
|
||||||
|
/// @param total total amount of images
|
||||||
|
/// @param hdr whether HDR is enabled
|
||||||
|
/// @param invFlow inverted flow scale value
|
||||||
|
/// @param isFirst whether this is the first iteration
|
||||||
|
/// @param isFirst2 whether this is the first iteration (second flag)
|
||||||
|
/// @return prefilled constant buffer
|
||||||
|
ConstantBuffer getDefaultConstantBuffer(
|
||||||
|
size_t index, size_t total,
|
||||||
|
bool hdr, float invFlow,
|
||||||
|
bool isFirst, bool isFirst2 // TODO: figure out if necessary
|
||||||
|
);
|
||||||
|
|
||||||
|
/// round down a VkExtent2D
|
||||||
|
/// @param extent the extent to shift
|
||||||
|
/// @param i the amount to shift by
|
||||||
|
/// @return the shifted extent
|
||||||
|
VkExtent2D shift_extent(VkExtent2D extent, uint32_t i);
|
||||||
|
|
||||||
|
/// round up a VkExtent2D
|
||||||
|
/// @param extent the extent to shift
|
||||||
|
/// @param a the amount to add before shifting
|
||||||
|
/// @param i the amount to shift by
|
||||||
|
/// @return the shifted extent
|
||||||
|
VkExtent2D add_shift_extent(VkExtent2D extent, uint32_t a, uint32_t i);
|
||||||
|
}
|
||||||
378
lsfg-vk-backend/src/lsfgvk.cpp
Normal file
378
lsfg-vk-backend/src/lsfgvk.cpp
Normal file
|
|
@ -0,0 +1,378 @@
|
||||||
|
#include "lsfg-vk-backend/lsfgvk.hpp"
|
||||||
|
#include "extraction/dll_reader.hpp"
|
||||||
|
#include "extraction/shader_registry.hpp"
|
||||||
|
#include "helpers/utils.hpp"
|
||||||
|
#include "lsfg-vk-common/helpers/errors.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 <exception>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
|
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||||
|
#include <renderdoc_app.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace lsfgvk;
|
||||||
|
|
||||||
|
namespace lsfgvk {
|
||||||
|
/// instance class
|
||||||
|
class InstanceImpl {
|
||||||
|
public:
|
||||||
|
/// create an instance
|
||||||
|
/// (see lsfg-vk documentation)
|
||||||
|
InstanceImpl(vk::PhysicalDeviceSelector selectPhysicalDevice,
|
||||||
|
const std::filesystem::path& shaderDllPath,
|
||||||
|
bool allowLowPrecision);
|
||||||
|
|
||||||
|
/// get the Vulkan instance
|
||||||
|
/// @return the Vulkan instance
|
||||||
|
[[nodiscard]] const auto& getVulkan() const { return this->vk; }
|
||||||
|
/// get the shader registry
|
||||||
|
/// @return the shader registry
|
||||||
|
[[nodiscard]] const auto& getShaderRegistry() const { return this->shaders; }
|
||||||
|
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||||
|
/// get the RenderDoc API
|
||||||
|
/// @return the RenderDoc API
|
||||||
|
[[nodiscard]] const auto& getRenderDocAPI() const { return this->renderdoc; }
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
vk::Vulkan vk;
|
||||||
|
extr::ShaderRegistry shaders;
|
||||||
|
|
||||||
|
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||||
|
std::optional<RENDERDOC_API_1_6_0> renderdoc;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// context class
|
||||||
|
class ContextImpl {
|
||||||
|
public:
|
||||||
|
/// create a context
|
||||||
|
/// (see lsfg-vk documentation)
|
||||||
|
ContextImpl(const InstanceImpl& instance,
|
||||||
|
std::pair<int, int> sourceFds, const std::vector<int>& destFds, int syncFd,
|
||||||
|
VkExtent2D extent, bool hdr, float flow, bool perf);
|
||||||
|
|
||||||
|
/// schedule frames
|
||||||
|
/// (see lsfg-vk documentation)
|
||||||
|
void scheduleFrames();
|
||||||
|
private:
|
||||||
|
std::pair<vk::Image, vk::Image> sourceImages;
|
||||||
|
std::vector<vk::Image> destImages;
|
||||||
|
|
||||||
|
vk::TimelineSemaphore syncSemaphore; // imported
|
||||||
|
vk::TimelineSemaphore prepassSemaphore;
|
||||||
|
size_t idx{1};
|
||||||
|
|
||||||
|
std::vector<vk::CommandBuffer> cmdbufs; // TODO: ponder reuse
|
||||||
|
size_t cmdbuf_idx{0};
|
||||||
|
|
||||||
|
ls::Ctx ctx;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance::Instance(
|
||||||
|
const std::function<bool(const std::string&)>& devicePicker,
|
||||||
|
const std::filesystem::path& shaderDllPath,
|
||||||
|
bool allowLowPrecision) {
|
||||||
|
const auto selectFunc = [&devicePicker](const std::vector<VkPhysicalDevice>& devices) {
|
||||||
|
for (const auto& device : devices) {
|
||||||
|
VkPhysicalDeviceProperties props;
|
||||||
|
vkGetPhysicalDeviceProperties(device, &props);
|
||||||
|
|
||||||
|
std::array<char, 256> devname = std::to_array(props.deviceName);
|
||||||
|
devname[255] = '\0'; // ensure null-termination
|
||||||
|
|
||||||
|
const std::string& deviceName{devname.data()};
|
||||||
|
if (devicePicker(deviceName))
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ls::vulkan_error("no suitable physical device found");
|
||||||
|
};
|
||||||
|
|
||||||
|
this->m_impl = std::make_unique<InstanceImpl>(
|
||||||
|
selectFunc, shaderDllPath, allowLowPrecision
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// create a Vulkan instance
|
||||||
|
vk::Vulkan createVulkanInstance(vk::PhysicalDeviceSelector selectPhysicalDevice) {
|
||||||
|
try {
|
||||||
|
return{
|
||||||
|
"lsfg-vk", vk::version{1, 1, 0},
|
||||||
|
"lsfg-vk-engine", vk::version{1, 1, 0},
|
||||||
|
selectPhysicalDevice
|
||||||
|
};
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to initialize Vulkan", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// build a shader registry
|
||||||
|
extr::ShaderRegistry createShaderRegistry(vk::Vulkan& vk,
|
||||||
|
const std::filesystem::path& shaderDllPath,
|
||||||
|
bool allowLowPrecision) {
|
||||||
|
std::unordered_map<uint32_t, std::vector<uint8_t>> resources{};
|
||||||
|
|
||||||
|
try {
|
||||||
|
resources = extr::extractResourcesFromDLL(shaderDllPath);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to parse Lossless Scaling DLL", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return extr::buildShaderRegistry(
|
||||||
|
vk, allowLowPrecision && vk.supportsFP16(),
|
||||||
|
resources
|
||||||
|
);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to build shader registry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||||
|
/// load RenderDoc integration
|
||||||
|
std::optional<RENDERDOC_API_1_6_0> loadRenderDocIntegration() {
|
||||||
|
void* module = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
|
||||||
|
if (!module)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto renderdocGetAPI = reinterpret_cast<pRENDERDOC_GetAPI>(
|
||||||
|
dlsym(module, "RENDERDOC_GetAPI"));
|
||||||
|
if (!renderdocGetAPI)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
RENDERDOC_API_1_6_0* api{};
|
||||||
|
renderdocGetAPI(eRENDERDOC_API_Version_1_6_0, reinterpret_cast<void**>(&api));
|
||||||
|
if (!api)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return *api;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceImpl::InstanceImpl(vk::PhysicalDeviceSelector selectPhysicalDevice,
|
||||||
|
const std::filesystem::path& shaderDllPath,
|
||||||
|
bool allowLowPrecision)
|
||||||
|
: vk(createVulkanInstance(selectPhysicalDevice)),
|
||||||
|
shaders(createShaderRegistry(this->vk, shaderDllPath,
|
||||||
|
allowLowPrecision && vk.supportsFP16())) {
|
||||||
|
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||||
|
this->renderdoc = loadRenderDocIntegration();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Context& Instance::openContext(std::pair<int, int> sourceFds, const std::vector<int>& destFds,
|
||||||
|
int syncFd, uint32_t width, uint32_t height,
|
||||||
|
bool hdr, float flow, bool perf) {
|
||||||
|
const VkExtent2D extent{ width, height };
|
||||||
|
return *this->m_contexts.emplace_back(std::make_unique<ContextImpl>(*this->m_impl,
|
||||||
|
sourceFds, destFds, syncFd,
|
||||||
|
extent, hdr, flow, perf
|
||||||
|
)).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// import source images
|
||||||
|
std::pair<vk::Image, vk::Image> importImages(const vk::Vulkan& vk,
|
||||||
|
const std::pair<int, int>& sourceFds,
|
||||||
|
VkExtent2D extent, VkFormat format) {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
vk::Image(vk, extent, format,
|
||||||
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, sourceFds.first),
|
||||||
|
vk::Image(vk, extent, format,
|
||||||
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, sourceFds.second)
|
||||||
|
};
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to import destination images", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// import destination images
|
||||||
|
std::vector<vk::Image> importImages(const vk::Vulkan& vk,
|
||||||
|
const std::vector<int>& destFds,
|
||||||
|
VkExtent2D extent, VkFormat format) {
|
||||||
|
try {
|
||||||
|
std::vector<vk::Image> destImages;
|
||||||
|
destImages.reserve(destFds.size());
|
||||||
|
|
||||||
|
for (const auto& fd : destFds)
|
||||||
|
destImages.emplace_back(vk, extent, format,
|
||||||
|
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, fd);
|
||||||
|
|
||||||
|
return destImages;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to import destination images", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// import timeline semaphore
|
||||||
|
vk::TimelineSemaphore importTimelineSemaphore(const vk::Vulkan& vk, int syncFd) {
|
||||||
|
try {
|
||||||
|
return{vk, 0, syncFd};
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to import timeline semaphore", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// create prepass semaphores
|
||||||
|
vk::TimelineSemaphore createPrepassSemaphore(const vk::Vulkan& vk) {
|
||||||
|
try {
|
||||||
|
return{vk, 0};
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to create prepass semaphore", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// create command buffers
|
||||||
|
std::vector<vk::CommandBuffer> createCommandBuffers(const vk::Vulkan& vk, size_t count) {
|
||||||
|
try {
|
||||||
|
std::vector<vk::CommandBuffer> cmdbufs;
|
||||||
|
cmdbufs.reserve(count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
cmdbufs.emplace_back(vk);
|
||||||
|
|
||||||
|
return cmdbufs;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to create command buffers", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// create context data
|
||||||
|
ls::Ctx createCtx(const InstanceImpl& instance, VkExtent2D extent,
|
||||||
|
bool hdr, float flow, bool perf, size_t count) {
|
||||||
|
const auto& vk = instance.getVulkan();
|
||||||
|
const auto& shaders = instance.getShaderRegistry();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
.vk = std::ref(vk),
|
||||||
|
.shaders = std::ref(shaders),
|
||||||
|
.constantBuffer{vk,
|
||||||
|
ls::getDefaultConstantBuffer(0, 1,
|
||||||
|
hdr, flow, false, false)},
|
||||||
|
.bnbSampler{vk, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, false},
|
||||||
|
.bnwSampler{vk, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, VK_COMPARE_OP_NEVER, true},
|
||||||
|
.eabSampler{vk, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_COMPARE_OP_ALWAYS, false},
|
||||||
|
.sourceExtent = extent,
|
||||||
|
.flowExtent = VkExtent2D {
|
||||||
|
.width = static_cast<uint32_t>(static_cast<float>(extent.width) / flow),
|
||||||
|
.height = static_cast<uint32_t>(static_cast<float>(extent.height) / flow)
|
||||||
|
},
|
||||||
|
.hdr = hdr,
|
||||||
|
.flow = flow,
|
||||||
|
.perf = perf,
|
||||||
|
.count = count
|
||||||
|
};
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to create context", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextImpl::ContextImpl(const InstanceImpl& instance,
|
||||||
|
std::pair<int, int> sourceFds, const std::vector<int>& destFds, int syncFd,
|
||||||
|
VkExtent2D extent, bool hdr, float flow, bool perf) :
|
||||||
|
sourceImages(importImages(instance.getVulkan(), sourceFds,
|
||||||
|
extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM)),
|
||||||
|
destImages(importImages(instance.getVulkan(), destFds,
|
||||||
|
extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM)),
|
||||||
|
syncSemaphore(importTimelineSemaphore(instance.getVulkan(), syncFd)),
|
||||||
|
prepassSemaphore(createPrepassSemaphore(instance.getVulkan())),
|
||||||
|
cmdbufs(createCommandBuffers(instance.getVulkan(), 16)),
|
||||||
|
ctx(createCtx(instance, extent, hdr, flow, perf, destFds.size())) {
|
||||||
|
// initialize all images
|
||||||
|
const vk::CommandBuffer cmdbuf{ctx.vk};
|
||||||
|
// (...)
|
||||||
|
cmdbuf.submit(ctx.vk); // wait for completion
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instance::scheduleFrames(Context& context) {
|
||||||
|
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||||
|
const auto& impl = this->m_impl;
|
||||||
|
if (impl->getRenderDocAPI()) {
|
||||||
|
impl->getRenderDocAPI()->StartFrameCapture(
|
||||||
|
RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(impl->getVulkan().inst()),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
try {
|
||||||
|
context.scheduleFrames();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw lsfgvk::error("Unable to schedule frames", e);
|
||||||
|
}
|
||||||
|
#ifdef LSFGVK__RENDERDOC_INTEGRATION
|
||||||
|
if (impl->getRenderDocAPI()) {
|
||||||
|
vkDeviceWaitIdle(impl->getVulkan().dev());
|
||||||
|
impl->getRenderDocAPI()->EndFrameCapture(
|
||||||
|
RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(impl->getVulkan().inst()),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::scheduleFrames() {
|
||||||
|
// schedule pre-pass
|
||||||
|
vk::CommandBuffer& cmdbuf = this->cmdbufs.at(this->cmdbuf_idx++ % this->cmdbufs.size());
|
||||||
|
cmdbuf = vk::CommandBuffer(this->ctx.vk);
|
||||||
|
|
||||||
|
// (...)
|
||||||
|
|
||||||
|
cmdbuf.submit(this->ctx.vk,
|
||||||
|
this->syncSemaphore, this->idx,
|
||||||
|
this->prepassSemaphore, this->idx
|
||||||
|
);
|
||||||
|
|
||||||
|
this->idx++;
|
||||||
|
|
||||||
|
// schedule main passes
|
||||||
|
for (size_t i = 0; i < this->destImages.size(); ++i) {
|
||||||
|
vk::CommandBuffer& cmdbuf = this->cmdbufs.at(this->cmdbuf_idx++ % this->cmdbufs.size());
|
||||||
|
cmdbuf = vk::CommandBuffer(this->ctx.vk);
|
||||||
|
|
||||||
|
// (...)
|
||||||
|
|
||||||
|
cmdbuf.submit(this->ctx.vk,
|
||||||
|
this->prepassSemaphore, this->idx - 1,
|
||||||
|
this->syncSemaphore, this->idx + i
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->idx += this->destImages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instance::closeContext(const Context& context) {
|
||||||
|
auto it = std::ranges::find_if(this->m_contexts,
|
||||||
|
[context = &context](const std::unique_ptr<ContextImpl>& ctx) {
|
||||||
|
return ctx.get() == context;
|
||||||
|
});
|
||||||
|
if (it == this->m_contexts.end())
|
||||||
|
throw lsfgvk::error("attempted to close unknown context",
|
||||||
|
std::runtime_error("no such context"));
|
||||||
|
|
||||||
|
vkDeviceWaitIdle(this->m_impl->getVulkan().dev());
|
||||||
|
this->m_contexts.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance::~Instance() = default;
|
||||||
|
|
||||||
|
error::error(const std::string& msg, const std::exception& inner)
|
||||||
|
: std::runtime_error(msg + "\n- " + inner.what()) {}
|
||||||
|
|
||||||
|
error::~error() = default;
|
||||||
|
|
@ -2,10 +2,13 @@
|
||||||
|
|
||||||
#include "../helpers/pointers.hpp"
|
#include "../helpers/pointers.hpp"
|
||||||
#include "descriptor_set.hpp"
|
#include "descriptor_set.hpp"
|
||||||
|
#include "image.hpp"
|
||||||
#include "shader.hpp"
|
#include "shader.hpp"
|
||||||
|
#include "timeline_semaphore.hpp"
|
||||||
#include "vulkan.hpp"
|
#include "vulkan.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
@ -22,6 +25,12 @@ namespace vk {
|
||||||
/// @throws ls::vulkan_error on failure
|
/// @throws ls::vulkan_error on failure
|
||||||
CommandBuffer(const vk::Vulkan& vk);
|
CommandBuffer(const vk::Vulkan& vk);
|
||||||
|
|
||||||
|
/// initialize an image
|
||||||
|
/// @param image the image to initialize
|
||||||
|
/// @param clearColor optional clear color
|
||||||
|
void prepareImage(const vk::Image& image,
|
||||||
|
const std::optional<VkClearColorValue>& clearColor = std::nullopt) const;
|
||||||
|
|
||||||
/// dispatch a compute shader
|
/// dispatch a compute shader
|
||||||
/// @param shader the compute shader
|
/// @param shader the compute shader
|
||||||
/// @param set the descriptor set
|
/// @param set the descriptor set
|
||||||
|
|
@ -34,8 +43,20 @@ namespace vk {
|
||||||
uint32_t x, uint32_t y, uint32_t z) const;
|
uint32_t x, uint32_t y, uint32_t z) const;
|
||||||
|
|
||||||
/// submit the command buffer
|
/// submit the command buffer
|
||||||
|
/// @param vk the vulkan instance
|
||||||
|
/// @param waitSemaphore the semaphore to wait on
|
||||||
|
/// @param waitValue the value to wait for
|
||||||
|
/// @param signalSemaphore the semaphore to signal
|
||||||
|
/// @param signalValue the value to signal
|
||||||
/// @throws ls::vulkan_error on failure
|
/// @throws ls::vulkan_error on failure
|
||||||
void submit(); // FIXME: method needs to actually submit, depending on needs
|
void submit(const vk::Vulkan& vk,
|
||||||
|
const vk::TimelineSemaphore& waitSemaphore, uint64_t waitValue,
|
||||||
|
const vk::TimelineSemaphore& signalSemaphore, uint64_t signalValue) const;
|
||||||
|
|
||||||
|
/// submit the command buffer instantly
|
||||||
|
/// @param vk the vulkan instance
|
||||||
|
/// @throws ls::vulkan_error on failure
|
||||||
|
void submit(const vk::Vulkan& vk) const;
|
||||||
private:
|
private:
|
||||||
ls::owned_ptr<VkCommandBuffer> commandBuffer;
|
ls::owned_ptr<VkCommandBuffer> commandBuffer;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@
|
||||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
#include "lsfg-vk-common/helpers/errors.hpp"
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
#include "lsfg-vk-common/helpers/pointers.hpp"
|
||||||
#include "lsfg-vk-common/vulkan/descriptor_set.hpp"
|
#include "lsfg-vk-common/vulkan/descriptor_set.hpp"
|
||||||
|
#include "lsfg-vk-common/vulkan/fence.hpp"
|
||||||
|
#include "lsfg-vk-common/vulkan/image.hpp"
|
||||||
#include "lsfg-vk-common/vulkan/shader.hpp"
|
#include "lsfg-vk-common/vulkan/shader.hpp"
|
||||||
|
#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp"
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
@ -48,6 +52,39 @@ CommandBuffer::CommandBuffer(const vk::Vulkan& vk)
|
||||||
throw ls::vulkan_error(res, "vkBeginCommandBuffer() failed");
|
throw ls::vulkan_error(res, "vkBeginCommandBuffer() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::prepareImage(const vk::Image& image,
|
||||||
|
const std::optional<VkClearColorValue>& clearColor) 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_UNDEFINED,
|
||||||
|
.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
|
||||||
|
);
|
||||||
|
|
||||||
|
if (clearColor.has_value()) {
|
||||||
|
vkCmdClearColorImage(*this->commandBuffer,
|
||||||
|
image.handle(),
|
||||||
|
VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
&clearColor.value(),
|
||||||
|
1, &barrier.subresourceRange
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CommandBuffer::dispatch(const vk::Shader& shader,
|
void CommandBuffer::dispatch(const vk::Shader& shader,
|
||||||
const vk::DescriptorSet& set,
|
const vk::DescriptorSet& set,
|
||||||
const std::vector<vk::Barrier>& barriers,
|
const std::vector<vk::Barrier>& barriers,
|
||||||
|
|
@ -73,8 +110,53 @@ void CommandBuffer::dispatch(const vk::Shader& shader,
|
||||||
vkCmdDispatch(*this->commandBuffer, x, y, z);
|
vkCmdDispatch(*this->commandBuffer, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::submit() {
|
void CommandBuffer::submit(const vk::Vulkan& vk,
|
||||||
|
const vk::TimelineSemaphore& waitSemaphore, uint64_t waitValue,
|
||||||
|
const vk::TimelineSemaphore& signalSemaphore, uint64_t signalValue) const {
|
||||||
auto res = vkEndCommandBuffer(*this->commandBuffer);
|
auto res = vkEndCommandBuffer(*this->commandBuffer);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
throw ls::vulkan_error(res, "vkEndCommandBuffer() failed");
|
throw ls::vulkan_error(res, "vkEndCommandBuffer() failed");
|
||||||
|
|
||||||
|
|
||||||
|
const VkTimelineSemaphoreSubmitInfo timelineInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
||||||
|
.waitSemaphoreValueCount = 1,
|
||||||
|
.pWaitSemaphoreValues = &waitValue,
|
||||||
|
.signalSemaphoreValueCount = 1,
|
||||||
|
.pSignalSemaphoreValues = &signalValue
|
||||||
|
};
|
||||||
|
const VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
const VkSubmitInfo submitInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
|
.pNext = &timelineInfo,
|
||||||
|
.waitSemaphoreCount = 1,
|
||||||
|
.pWaitSemaphores = &waitSemaphore.handle(),
|
||||||
|
.pWaitDstStageMask = &stage,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = &*this->commandBuffer,
|
||||||
|
.signalSemaphoreCount = 1,
|
||||||
|
.pSignalSemaphores = &signalSemaphore.handle()
|
||||||
|
};
|
||||||
|
res = vkQueueSubmit(vk.queue(), 1, &submitInfo, VK_NULL_HANDLE);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
throw ls::vulkan_error(res, "vkQueueSubmit() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::submit(const vk::Vulkan& vk) const {
|
||||||
|
auto res = vkEndCommandBuffer(*this->commandBuffer);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
throw ls::vulkan_error(res, "vkEndCommandBuffer() failed");
|
||||||
|
|
||||||
|
const VkSubmitInfo submitInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = &*this->commandBuffer
|
||||||
|
};
|
||||||
|
const vk::Fence fence{vk};
|
||||||
|
res = vkQueueSubmit(vk.queue(), 1, &submitInfo, fence.handle());
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
throw ls::vulkan_error(res, "vkQueueSubmit() failed");
|
||||||
|
|
||||||
|
if (!fence.wait(vk))
|
||||||
|
throw ls::vulkan_error(VK_TIMEOUT, "Fence::wait() timed out");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue