mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-05-10 19:21:42 +00:00
feat(bindless): Write backend base
This commit is contained in:
parent
3211d8f9e8
commit
93816ef4ab
35 changed files with 249 additions and 2864 deletions
|
|
@ -1,143 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
|
|
||||||
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 {
|
|
||||||
public:
|
|
||||||
///
|
|
||||||
/// Construct an error
|
|
||||||
///
|
|
||||||
/// @param msg Error message.
|
|
||||||
/// @param inner Inner exception.
|
|
||||||
///
|
|
||||||
explicit error(const std::string &msg, const std::exception &inner);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Construct an error
|
|
||||||
///
|
|
||||||
/// @param msg Error message.
|
|
||||||
///
|
|
||||||
explicit error(const std::string &msg);
|
|
||||||
|
|
||||||
error(const error &) = default;
|
|
||||||
error &operator=(const error &) = default;
|
|
||||||
error(error &&) = default;
|
|
||||||
error &operator=(error &&) = default;
|
|
||||||
~error() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Function type for picking a device based on its name and IDs
|
|
||||||
using DevicePicker = std::function<bool(
|
|
||||||
const std::string& deviceName,
|
|
||||||
std::pair<const std::string&, const std::string&> ids, // (vendor ID, device ID) 0xXXXX format
|
|
||||||
const std::optional<std::string>& pci // (bus:slot.func) if available, no padded zeros
|
|
||||||
)>;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// 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 some identifiers.
|
|
||||||
/// @param shaderDllPath Path to the Lossless.dll file to load shaders from.
|
|
||||||
/// @param allowLowPrecision Whether to load low-precision (FP16) shaders if supported.
|
|
||||||
///
|
|
||||||
/// @throws backend::error on failure
|
|
||||||
///
|
|
||||||
Instance(
|
|
||||||
const DevicePicker& 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 backend::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 backend::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;
|
|
||||||
};
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Make all lsfg-vk instances leaking.
|
|
||||||
/// This is to workaround a bug in the Vulkan loader, which
|
|
||||||
/// makes it impossible to destroy Vulkan instances and devices.
|
|
||||||
///
|
|
||||||
void makeLeaking();
|
|
||||||
|
|
||||||
}
|
|
||||||
138
lsfg-vk-backend/include/lsfg-vk/lsfgvk.hpp
Normal file
138
lsfg-vk-backend/include/lsfg-vk/lsfgvk.hpp
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace lsfgvk {
|
||||||
|
|
||||||
|
/// Forward declaration of implementation classes
|
||||||
|
namespace priv {
|
||||||
|
struct [[gnu::visibility("default")]] Instance;
|
||||||
|
struct [[gnu::visibility("default")]] Context;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Main entrypoint of the library
|
||||||
|
///
|
||||||
|
class [[gnu::visibility("default")]] Instance {
|
||||||
|
friend class Context;
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Create a lsfg-vk instance
|
||||||
|
///
|
||||||
|
/// The device identifier may be one of:
|
||||||
|
/// - Device name (e.g. "NVIDIA GeForce RTX 5080")
|
||||||
|
/// - Vendor ID + Device ID in lowercase hexadecimal (e.g. "10de:2c02")
|
||||||
|
/// - PCI bus ID with padded zeroes (e.g. "0000:01:00.0")
|
||||||
|
///
|
||||||
|
/// @param deviceId Device identifier (see above)
|
||||||
|
/// @param lsfgvkDllPath Path to the lsfg-vk DLL file
|
||||||
|
/// @param allowFP16 Whether to allow usage of fp16 shader variants
|
||||||
|
/// @throws std::runtime_error on failure
|
||||||
|
///
|
||||||
|
Instance(
|
||||||
|
const std::string& deviceId,
|
||||||
|
const std::filesystem::path& lsfgvkDllPath,
|
||||||
|
bool allowFP16
|
||||||
|
);
|
||||||
|
|
||||||
|
// Non-copyable, non-movable
|
||||||
|
Instance(const Instance&) = delete;
|
||||||
|
Instance& operator=(const Instance&) = delete;
|
||||||
|
Instance(Instance&&) = delete;
|
||||||
|
Instance& operator=(Instance&&) = delete;
|
||||||
|
~Instance();
|
||||||
|
private:
|
||||||
|
std::unique_ptr<priv::Instance> m_priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// File descriptors exported from a context, the user must close them after use.
|
||||||
|
///
|
||||||
|
struct FileDescriptors {
|
||||||
|
///
|
||||||
|
/// File descriptor for a Vulkan memory allocation containing
|
||||||
|
/// a 2D array of RGBA8 pixels with length 2 and optimal allocation.
|
||||||
|
///
|
||||||
|
/// Starting at iteration 0, the next frame for which frames should be interpolated
|
||||||
|
/// inbetween should be placed in image `iteration % 2`.
|
||||||
|
///
|
||||||
|
int sourceFd;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// File descriptor for a Vulkan memory allocation containing a single RGBA8
|
||||||
|
/// image into which each generated frame will be written to.
|
||||||
|
///
|
||||||
|
int destinationFd;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// File descriptor for a timeline semaphore. When scheduling frames for generation,
|
||||||
|
/// a specific value is waited for and signaled on return. It is up to the user to ensure
|
||||||
|
/// the destination image is not overwritten before it is read.
|
||||||
|
///
|
||||||
|
int syncFd;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A context for generating frames
|
||||||
|
///
|
||||||
|
class [[gnu::visibility("default")]] Context {
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Create a frame generation context
|
||||||
|
///
|
||||||
|
/// @param instance Parent instance
|
||||||
|
/// @param width Image width
|
||||||
|
/// @param height Image height
|
||||||
|
/// @param flowScale Flow estimation scale factor
|
||||||
|
/// @param performanceMode Whether to enable performance mode
|
||||||
|
/// @throws std::runtime_error on failure
|
||||||
|
///
|
||||||
|
Context(
|
||||||
|
const Instance& instance,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
float flowScale,
|
||||||
|
bool performanceMode
|
||||||
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Export the internal resources
|
||||||
|
///
|
||||||
|
/// @return File descriptors for internal resources
|
||||||
|
/// @throws std::runtime_error on failure
|
||||||
|
///
|
||||||
|
[[nodiscard]] FileDescriptors exportFds() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Dispatch frame generation
|
||||||
|
///
|
||||||
|
/// Let `so - 1` be the current value of the timeline semaphore, starting at 0.
|
||||||
|
/// The user must signal `so` to start the generation of the next frame, after
|
||||||
|
/// which lsfg-vk will signal `so + 1`. The user must ensure the previously
|
||||||
|
/// generated frame is read before signaling the next one (at `so + 2` and so on).
|
||||||
|
///
|
||||||
|
/// @param total Total number of frames to generate
|
||||||
|
/// @throws std::runtime_error on failure
|
||||||
|
///
|
||||||
|
void dispatch(uint32_t total);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Wait for the device to be idle
|
||||||
|
///
|
||||||
|
void idle() const;
|
||||||
|
|
||||||
|
// Non-copyable, non-movable
|
||||||
|
Context(const Context&) = delete;
|
||||||
|
Context& operator=(const Context&) = delete;
|
||||||
|
Context(Context&&) = delete;
|
||||||
|
Context& operator=(Context&&) = delete;
|
||||||
|
~Context();
|
||||||
|
private:
|
||||||
|
std::unique_ptr<priv::Context> m_priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "dll_reader.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
|
||||||
|
|
||||||
#include <ios>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <optional>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <fstream>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <array>
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
using namespace lsfgvk;
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
/// DOS file header
|
|
||||||
struct DOSHeader {
|
|
||||||
uint16_t magic; // 0x5A4D
|
|
||||||
std::array<uint16_t, 29> pad;
|
|
||||||
int32_t pe_offset; // file offset
|
|
||||||
};
|
|
||||||
|
|
||||||
/// PE header
|
|
||||||
struct PEHeader {
|
|
||||||
uint32_t signature; // "PE\0\0"
|
|
||||||
std::array<uint16_t, 1> pad1;
|
|
||||||
uint16_t sect_count;
|
|
||||||
std::array<uint16_t, 6> pad2;
|
|
||||||
uint16_t opt_hdr_size;
|
|
||||||
std::array<uint16_t, 1> pad3;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// (partial!) PE optional header
|
|
||||||
struct PEOptionalHeader {
|
|
||||||
uint16_t magic; // 0x20B
|
|
||||||
std::array<uint16_t, 63> pad4;
|
|
||||||
std::pair<uint32_t, uint32_t> resource_table; // file offset/size
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Section header
|
|
||||||
struct SectionHeader {
|
|
||||||
std::array<uint16_t, 4> pad1;
|
|
||||||
uint32_t vsize; // virtual
|
|
||||||
uint32_t vaddress;
|
|
||||||
uint32_t fsize; // raw
|
|
||||||
uint32_t foffset;
|
|
||||||
std::array<uint16_t, 8> pad2;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Resource directory
|
|
||||||
struct ResourceDirectory {
|
|
||||||
std::array<uint16_t, 6> pad;
|
|
||||||
uint16_t name_count;
|
|
||||||
uint16_t id_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Resource directory entry
|
|
||||||
struct ResourceDirectoryEntry {
|
|
||||||
uint32_t id;
|
|
||||||
uint32_t offset; // high bit = directory
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Resource data entry
|
|
||||||
struct ResourceDataEntry {
|
|
||||||
uint32_t offset;
|
|
||||||
uint32_t size;
|
|
||||||
std::array<uint32_t, 2> pad;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
|
||||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-container"
|
|
||||||
namespace {
|
|
||||||
/// Safely cast a vector to a pointer of type T
|
|
||||||
template<typename T>
|
|
||||||
const T* safe_cast(const std::vector<uint8_t>& data, size_t offset) {
|
|
||||||
const size_t end = offset + sizeof(T);
|
|
||||||
if (end > data.size() || end < offset)
|
|
||||||
throw ls::error("buffer overflow/underflow during safe cast");
|
|
||||||
return reinterpret_cast<const T*>(&data.at(offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Safely cast a vector to a span of T
|
|
||||||
template<typename T>
|
|
||||||
std::span<const T> span_cast(const std::vector<uint8_t>& data, size_t offset, size_t count) {
|
|
||||||
const size_t end = offset + (count * sizeof(T));
|
|
||||||
if (end > data.size() || end < offset)
|
|
||||||
throw ls::error("buffer overflow/underflow during safe cast");
|
|
||||||
return std::span<const T>(reinterpret_cast<const T*>(&data.at(offset)), count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
std::unordered_map<uint32_t, std::vector<uint8_t>> backend::extractResourcesFromDLL(
|
|
||||||
const std::filesystem::path& dll) {
|
|
||||||
std::ifstream file(dll, std::ios::binary | std::ios::ate);
|
|
||||||
if (!file.is_open())
|
|
||||||
throw ls::error("failed to open dll file");
|
|
||||||
|
|
||||||
const std::streamsize size = static_cast<std::streamsize>(file.tellg());
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
std::vector<uint8_t> data(static_cast<size_t>(size));
|
|
||||||
if (!file.read(reinterpret_cast<char*>(data.data()), size))
|
|
||||||
throw ls::error("failed to read dll file");
|
|
||||||
|
|
||||||
// parse dos header
|
|
||||||
size_t fileOffset = 0;
|
|
||||||
const auto* dosHdr = safe_cast<const DOSHeader>(data, 0);
|
|
||||||
if (dosHdr->magic != 0x5A4D)
|
|
||||||
throw ls::error("dos header magic number is incorrect");
|
|
||||||
|
|
||||||
// parse pe header
|
|
||||||
fileOffset += static_cast<size_t>(dosHdr->pe_offset);
|
|
||||||
const auto* peHdr = safe_cast<const PEHeader>(data, fileOffset);
|
|
||||||
if (peHdr->signature != 0x00004550)
|
|
||||||
throw ls::error("pe header signature is incorrect");
|
|
||||||
|
|
||||||
// parse optional pe header
|
|
||||||
fileOffset += sizeof(PEHeader);
|
|
||||||
const auto* peOptHdr = safe_cast<const PEOptionalHeader>(data, fileOffset);
|
|
||||||
if (peOptHdr->magic != 0x20B)
|
|
||||||
throw ls::error("pe format is not PE32+");
|
|
||||||
const auto& [rsrc_rva, rsrc_size] = peOptHdr->resource_table;
|
|
||||||
|
|
||||||
// locate section containing resources
|
|
||||||
std::optional<size_t> rsrc_offset;
|
|
||||||
fileOffset += peHdr->opt_hdr_size;
|
|
||||||
const auto sectHdrs = span_cast<const SectionHeader>(data, fileOffset, peHdr->sect_count);
|
|
||||||
for (const auto& sectHdr : sectHdrs) {
|
|
||||||
if (rsrc_rva < sectHdr.vaddress || rsrc_rva > (sectHdr.vaddress + sectHdr.vsize))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rsrc_offset.emplace((rsrc_rva - sectHdr.vaddress) + sectHdr.foffset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!rsrc_offset)
|
|
||||||
throw ls::error("unable to locate resource section");
|
|
||||||
|
|
||||||
// parse resource directory
|
|
||||||
fileOffset = rsrc_offset.value();
|
|
||||||
const auto* rsrcDir = safe_cast<const ResourceDirectory>(data, fileOffset);
|
|
||||||
if (rsrcDir->id_count < 3)
|
|
||||||
throw ls::error("resource directory does not have enough entries");
|
|
||||||
|
|
||||||
// find resource table with data type
|
|
||||||
std::optional<size_t> rsrc_tbl_offset;
|
|
||||||
fileOffset = rsrc_offset.value() + sizeof(ResourceDirectory);
|
|
||||||
const auto rsrcDirEntries = span_cast<const ResourceDirectoryEntry>(
|
|
||||||
data, fileOffset, rsrcDir->name_count + rsrcDir->id_count);
|
|
||||||
for (const auto& rsrcDirEntry : rsrcDirEntries) {
|
|
||||||
if (rsrcDirEntry.id != 10) // RT_RCDATA
|
|
||||||
continue;
|
|
||||||
if ((rsrcDirEntry.offset & 0x80000000) == 0)
|
|
||||||
throw ls::error("expected resource directory, found data entry");
|
|
||||||
|
|
||||||
rsrc_tbl_offset.emplace(rsrcDirEntry.offset & 0x7FFFFFFF);
|
|
||||||
}
|
|
||||||
if (!rsrc_tbl_offset)
|
|
||||||
throw ls::error("unabele to locate RT_RCDATA directory");
|
|
||||||
|
|
||||||
// parse data type resource directory
|
|
||||||
fileOffset = rsrc_offset.value() + rsrc_tbl_offset.value();
|
|
||||||
const auto* rsrcTbl = safe_cast<const ResourceDirectory>(data, fileOffset);
|
|
||||||
if (rsrcTbl->id_count < 1)
|
|
||||||
throw ls::error("RT_RCDATA directory does not have enough entries");
|
|
||||||
|
|
||||||
// collect all resources
|
|
||||||
fileOffset += sizeof(ResourceDirectory);
|
|
||||||
const auto rsrcTblEntries = span_cast<const ResourceDirectoryEntry>(
|
|
||||||
data, fileOffset, rsrcTbl->name_count + rsrcTbl->id_count);
|
|
||||||
std::unordered_map<uint32_t, std::vector<uint8_t>> resources;
|
|
||||||
for (const auto& rsrcTblEntry : rsrcTblEntries) {
|
|
||||||
if ((rsrcTblEntry.offset & 0x80000000) == 0)
|
|
||||||
throw ls::error("expected resource directory, found data entry");
|
|
||||||
|
|
||||||
// skip over language directory
|
|
||||||
fileOffset = rsrc_offset.value() + (rsrcTblEntry.offset & 0x7FFFFFFF);
|
|
||||||
const auto* langDir = safe_cast<const ResourceDirectory>(data, fileOffset);
|
|
||||||
if (langDir->id_count < 1)
|
|
||||||
throw ls::error("Incorrect language directory");
|
|
||||||
|
|
||||||
fileOffset += sizeof(ResourceDirectory);
|
|
||||||
const auto* langDirEntry = safe_cast<const ResourceDirectoryEntry>(data, fileOffset);
|
|
||||||
if ((langDirEntry->offset & 0x80000000) != 0)
|
|
||||||
throw ls::error("expected resource data entry, but found directory");
|
|
||||||
|
|
||||||
// parse resource data entry
|
|
||||||
fileOffset = rsrc_offset.value() + (langDirEntry->offset & 0x7FFFFFFF);
|
|
||||||
const auto* entry = safe_cast<const ResourceDataEntry>(data, fileOffset);
|
|
||||||
if (entry->offset < rsrc_rva || entry->offset > (rsrc_rva + rsrc_size))
|
|
||||||
throw ls::error("resource data entry points outside resource section");
|
|
||||||
|
|
||||||
// extract resource
|
|
||||||
std::vector<uint8_t> resource(entry->size);
|
|
||||||
fileOffset = (entry->offset - rsrc_rva) + rsrc_offset.value();
|
|
||||||
if (fileOffset + entry->size > data.size())
|
|
||||||
throw ls::error("resource data entry points outside file");
|
|
||||||
std::copy_n(&data.at(fileOffset), entry->size, resource.data());
|
|
||||||
resources.emplace(rsrcTblEntry.id, std::move(resource));
|
|
||||||
}
|
|
||||||
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
|
|
||||||
/// extract all resources from a DLL file
|
|
||||||
/// @param dll path to the DLL file
|
|
||||||
/// @return map of resource IDs to their binary data
|
|
||||||
/// @throws ls::error on various failure points
|
|
||||||
std::unordered_map<uint32_t, std::vector<uint8_t>> extractResourcesFromDLL(
|
|
||||||
const std::filesystem::path& dll);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "shader_registry.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/shader.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace lsfgvk;
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
/// get the source code for a shader
|
|
||||||
const std::vector<uint8_t>& getShaderSource(uint32_t id, bool fp16, bool perf,
|
|
||||||
const std::unordered_map<uint32_t, std::vector<uint8_t>>& resources) {
|
|
||||||
const size_t BASE_OFFSET = 49;
|
|
||||||
const size_t OFFSET_PERF = 23;
|
|
||||||
const size_t OFFSET_FP32 = 49;
|
|
||||||
|
|
||||||
auto it = resources.find(BASE_OFFSET + id +
|
|
||||||
(perf ? OFFSET_PERF : 0) +
|
|
||||||
(fp16 ? 0 : OFFSET_FP32));
|
|
||||||
if (it == resources.end())
|
|
||||||
throw ls::error("unable to find shader with id: " + std::to_string(id));
|
|
||||||
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
/// patch the generate shader
|
|
||||||
void patchGenerateShader(std::vector<uint8_t>& data, bool hdr) {
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wunknown-warning-option"
|
|
||||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-container"
|
|
||||||
auto* _ptr = data.data();
|
|
||||||
const std::span<uint32_t> words(
|
|
||||||
reinterpret_cast<uint32_t*>(_ptr),
|
|
||||||
data.size() / sizeof(uint32_t)
|
|
||||||
);
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
const uint16_t SpvOpCapability = 17;
|
|
||||||
const uint16_t SpvOpTypeImage = 25;
|
|
||||||
const uint32_t SpvCapabilityStorageImageWriteWithoutFormat = 56;
|
|
||||||
const uint32_t SpvCapabilityShader = 1;
|
|
||||||
const uint32_t SpvImageFormatRgba16f = 2;
|
|
||||||
const uint32_t SpvImageFormatRgba8 = 4;
|
|
||||||
|
|
||||||
for (size_t i = 5; i < words.size();) {
|
|
||||||
const uint32_t& word = words[i]; // NOLINT ([]-usage)
|
|
||||||
const uint16_t wc = (word >> 16);
|
|
||||||
const uint16_t op = word & 0xFFFF;
|
|
||||||
|
|
||||||
// remove write without format capability
|
|
||||||
if (op == SpvOpCapability && wc >= 2) {
|
|
||||||
uint32_t& cap = words[i + 1]; // NOLINT ([]-usage)
|
|
||||||
if (cap == SpvCapabilityStorageImageWriteWithoutFormat)
|
|
||||||
cap = SpvCapabilityShader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// patch format in image instructions
|
|
||||||
if (op == SpvOpTypeImage && wc >= 9) {
|
|
||||||
const uint32_t sampled = words[i + 7]; // NOLINT ([]-usage)
|
|
||||||
if (sampled == 2)
|
|
||||||
words[i + 8] = // NOLINT ([]-usage)
|
|
||||||
hdr ? SpvImageFormatRgba16f : SpvImageFormatRgba8;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += wc ? wc : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderRegistry backend::buildShaderRegistry(const vk::Vulkan& vk, bool fp16,
|
|
||||||
const std::unordered_map<uint32_t, std::vector<uint8_t>>& resources) {
|
|
||||||
// patch the generate shader
|
|
||||||
std::vector<uint8_t> generate_data = getShaderSource(256, fp16, false, resources);
|
|
||||||
std::vector<uint8_t> generate_data_hdr = generate_data;
|
|
||||||
patchGenerateShader(generate_data, false);
|
|
||||||
patchGenerateShader(generate_data_hdr, true);
|
|
||||||
|
|
||||||
// load all other shaders
|
|
||||||
#define SHADER(id, p1, p2, p3, p4) \
|
|
||||||
vk::Shader(vk, getShaderSource(id, fp16, PERF, resources), \
|
|
||||||
p1, p2, p3, p4)
|
|
||||||
|
|
||||||
return {
|
|
||||||
#define PERF false
|
|
||||||
.mipmaps = SHADER(255, 1, 7, 1, 1),
|
|
||||||
.generate = vk::Shader(vk, generate_data, 5, 1, 1, 2),
|
|
||||||
.generate_hdr = vk::Shader(vk, generate_data_hdr, 5, 1, 1, 2),
|
|
||||||
.quality = {
|
|
||||||
.alpha = {
|
|
||||||
SHADER(267, 1, 2, 0, 1),
|
|
||||||
SHADER(268, 2, 2, 0, 1),
|
|
||||||
SHADER(269, 2, 4, 0, 1),
|
|
||||||
SHADER(270, 4, 4, 0, 1)
|
|
||||||
},
|
|
||||||
.beta = {
|
|
||||||
SHADER(275, 12, 2, 0, 1),
|
|
||||||
SHADER(276, 2, 2, 0, 1),
|
|
||||||
SHADER(277, 2, 2, 0, 1),
|
|
||||||
SHADER(278, 2, 2, 0, 1),
|
|
||||||
SHADER(279, 2, 6, 1, 1)
|
|
||||||
},
|
|
||||||
.gamma = {
|
|
||||||
SHADER(257, 9, 3, 1, 2),
|
|
||||||
SHADER(259, 3, 4, 0, 1),
|
|
||||||
SHADER(260, 4, 4, 0, 1),
|
|
||||||
SHADER(261, 4, 4, 0, 1),
|
|
||||||
SHADER(262, 6, 1, 1, 2)
|
|
||||||
},
|
|
||||||
.delta = {
|
|
||||||
SHADER(257, 9, 3, 1, 2),
|
|
||||||
SHADER(263, 3, 4, 0, 1),
|
|
||||||
SHADER(264, 4, 4, 0, 1),
|
|
||||||
SHADER(265, 4, 4, 0, 1),
|
|
||||||
SHADER(266, 6, 1, 1, 2),
|
|
||||||
SHADER(258, 10, 2, 1, 2),
|
|
||||||
SHADER(271, 2, 2, 0, 1),
|
|
||||||
SHADER(272, 2, 2, 0, 1),
|
|
||||||
SHADER(273, 2, 2, 0, 1),
|
|
||||||
SHADER(274, 3, 1, 1, 2)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#undef PERF
|
|
||||||
#define PERF true
|
|
||||||
.performance = {
|
|
||||||
.alpha = {
|
|
||||||
SHADER(267, 1, 1, 0, 1),
|
|
||||||
SHADER(268, 1, 1, 0, 1),
|
|
||||||
SHADER(269, 1, 2, 0, 1),
|
|
||||||
SHADER(270, 2, 2, 0, 1)
|
|
||||||
},
|
|
||||||
.beta = {
|
|
||||||
SHADER(275, 6, 2, 0, 1),
|
|
||||||
SHADER(276, 2, 2, 0, 1),
|
|
||||||
SHADER(277, 2, 2, 0, 1),
|
|
||||||
SHADER(278, 2, 2, 0, 1),
|
|
||||||
SHADER(279, 2, 6, 1, 1)
|
|
||||||
},
|
|
||||||
.gamma = {
|
|
||||||
SHADER(257, 5, 3, 1, 2),
|
|
||||||
SHADER(259, 3, 2, 0, 1),
|
|
||||||
SHADER(260, 2, 2, 0, 1),
|
|
||||||
SHADER(261, 2, 2, 0, 1),
|
|
||||||
SHADER(262, 4, 1, 1, 2)
|
|
||||||
},
|
|
||||||
.delta = {
|
|
||||||
SHADER(257, 5, 3, 1, 2),
|
|
||||||
SHADER(263, 3, 2, 0, 1),
|
|
||||||
SHADER(264, 2, 2, 0, 1),
|
|
||||||
SHADER(265, 2, 2, 0, 1),
|
|
||||||
SHADER(266, 4, 1, 1, 2),
|
|
||||||
SHADER(258, 6, 1, 1, 2),
|
|
||||||
SHADER(271, 1, 1, 0, 1),
|
|
||||||
SHADER(272, 1, 1, 0, 1),
|
|
||||||
SHADER(273, 1, 1, 0, 1),
|
|
||||||
SHADER(274, 2, 1, 1, 2)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#undef PERF
|
|
||||||
.is_fp16 = fp16
|
|
||||||
};
|
|
||||||
|
|
||||||
#undef SHADER
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lsfg-vk-common/vulkan/shader.hpp"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
|
|
||||||
/// shader collection struct
|
|
||||||
struct Shaders {
|
|
||||||
std::array<vk::Shader, 4> alpha;
|
|
||||||
std::array<vk::Shader, 5> beta;
|
|
||||||
std::array<vk::Shader, 5> gamma;
|
|
||||||
std::array<vk::Shader, 10> delta;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// shader registry struct
|
|
||||||
struct ShaderRegistry {
|
|
||||||
vk::Shader mipmaps;
|
|
||||||
vk::Shader generate, generate_hdr;
|
|
||||||
Shaders quality;
|
|
||||||
Shaders performance;
|
|
||||||
|
|
||||||
bool is_fp16; //!< whether the fp16 shader variants were loaded
|
|
||||||
};
|
|
||||||
|
|
||||||
/// build a shader registry from resources
|
|
||||||
/// @param vk Vulkan instance
|
|
||||||
/// @param fp16 whether to load fp16 variants
|
|
||||||
/// @param resources map of resource IDs to their binary data
|
|
||||||
/// @return constructed shader registry
|
|
||||||
/// @throws ls::error if shaders are missing
|
|
||||||
/// @throws vk::vulkan_error on Vulkan errors
|
|
||||||
ShaderRegistry buildShaderRegistry(const vk::Vulkan& vk, bool fp16,
|
|
||||||
const std::unordered_map<uint32_t, std::vector<uint8_t>>& resources);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "limits.hpp"
|
|
||||||
|
|
||||||
#include "lsfg-vk-common/vulkan/descriptor_pool.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using namespace lsfgvk;
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
const vk::Limits BASE_LIMITS{
|
|
||||||
.sets = 51,
|
|
||||||
.uniform_buffers = 3,
|
|
||||||
.samplers = 51,
|
|
||||||
.sampled_images = 165,
|
|
||||||
.storage_images = 172
|
|
||||||
};
|
|
||||||
const vk::Limits BASE_LIMITS_PERF{
|
|
||||||
.sampled_images = 91,
|
|
||||||
.storage_images = 102
|
|
||||||
};
|
|
||||||
const vk::Limits GEN_LIMITS{
|
|
||||||
.sets = 93,
|
|
||||||
.uniform_buffers = 54,
|
|
||||||
.samplers = 147,
|
|
||||||
.sampled_images = 567,
|
|
||||||
.storage_images = 261
|
|
||||||
};
|
|
||||||
const vk::Limits GEN_LIMITS_PERF{
|
|
||||||
.sampled_images = 339,
|
|
||||||
.storage_images = 183
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Limits backend::calculateDescriptorPoolLimits(size_t count, bool perf) {
|
|
||||||
const auto m = static_cast<uint16_t>(count);
|
|
||||||
|
|
||||||
vk::Limits a{BASE_LIMITS};
|
|
||||||
vk::Limits b{GEN_LIMITS};
|
|
||||||
if (perf) {
|
|
||||||
a.sampled_images = BASE_LIMITS_PERF.sampled_images;
|
|
||||||
b.sampled_images = GEN_LIMITS_PERF.sampled_images;
|
|
||||||
a.storage_images = BASE_LIMITS_PERF.storage_images;
|
|
||||||
b.storage_images = GEN_LIMITS_PERF.storage_images;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.sets += b.sets * m;
|
|
||||||
a.uniform_buffers += b.uniform_buffers * m;
|
|
||||||
a.samplers += b.samplers * m;
|
|
||||||
a.sampled_images += b.sampled_images * m;
|
|
||||||
a.storage_images += b.storage_images * m;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lsfg-vk-common/vulkan/descriptor_pool.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// calculate limits for descriptor pools
|
|
||||||
/// @param count number of images
|
|
||||||
/// @param perf whether performance mode is enabled
|
|
||||||
/// @return calculated limits
|
|
||||||
vk::Limits calculateDescriptorPoolLimits(size_t count, bool perf);
|
|
||||||
}
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "managed_shader.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/descriptor_pool.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/sampler.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/shader.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <functional>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk;
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
ManagedShaderBuilder& ManagedShaderBuilder::sampled(const vk::Image& image) {
|
|
||||||
this->sampledImages.push_back(std::ref(image));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedShaderBuilder& ManagedShaderBuilder::sampleds(
|
|
||||||
const std::vector<vk::Image>& images,
|
|
||||||
size_t offset, size_t count) {
|
|
||||||
if (count == 0 || offset + count > images.size())
|
|
||||||
count = images.size() - offset;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i)
|
|
||||||
this->sampledImages.push_back(std::ref(images.at(offset + i)));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ManagedShaderBuilder& ManagedShaderBuilder::storage(const vk::Image& image) {
|
|
||||||
this->storageImages.push_back(std::ref(image));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedShaderBuilder& ManagedShaderBuilder::storages(
|
|
||||||
const std::vector<vk::Image>& images,
|
|
||||||
size_t offset, size_t count) {
|
|
||||||
if (count == 0 || offset + count > images.size())
|
|
||||||
count = images.size() - offset;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i)
|
|
||||||
this->storageImages.push_back(std::ref(images.at(offset + i)));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedShaderBuilder& ManagedShaderBuilder::sampler(const vk::Sampler& sampler) {
|
|
||||||
this->imageSamplers.push_back(std::ref(sampler));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedShaderBuilder& ManagedShaderBuilder::samplers(
|
|
||||||
const std::vector<vk::Sampler>& samplers) {
|
|
||||||
for (const auto& sampler : samplers)
|
|
||||||
this->imageSamplers.push_back(std::ref(sampler));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedShaderBuilder& ManagedShaderBuilder::buffer(const vk::Buffer& buffer) {
|
|
||||||
this->constantBuffers.push_back(std::ref(buffer));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedShader ManagedShaderBuilder::build(const vk::Vulkan& vk,
|
|
||||||
const vk::DescriptorPool& pool, const vk::Shader& shader) const {
|
|
||||||
std::vector<vk::Barrier> barriers;
|
|
||||||
barriers.reserve(this->storageImages.size() + this->sampledImages.size());
|
|
||||||
|
|
||||||
for (const auto& img : this->sampledImages)
|
|
||||||
barriers.push_back({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = img.get().handle(),
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (const auto& img : this->storageImages)
|
|
||||||
barriers.push_back({
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = img.get().handle(),
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
std::ref(shader),
|
|
||||||
std::move(barriers),
|
|
||||||
vk::DescriptorSet(vk, pool, shader,
|
|
||||||
this->sampledImages,
|
|
||||||
this->storageImages,
|
|
||||||
this->imageSamplers,
|
|
||||||
this->constantBuffers)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManagedShader::dispatch(const vk::Vulkan& vk, const vk::CommandBuffer& cmd,
|
|
||||||
VkExtent2D extent) const {
|
|
||||||
cmd.dispatch(vk, this->shader,
|
|
||||||
this->descriptorSet,
|
|
||||||
this->barriers,
|
|
||||||
extent.width, extent.height, 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/descriptor_pool.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/descriptor_set.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/shader.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
|
|
||||||
/// managed shader handling dispatch and barriers
|
|
||||||
/// this class is NOT memory-safe
|
|
||||||
class ManagedShader {
|
|
||||||
friend class ManagedShaderBuilder;
|
|
||||||
public:
|
|
||||||
/// dispatch the managed shader
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer to use
|
|
||||||
/// @param extent dispatch size
|
|
||||||
/// @throws ls::vulkan_error on failure
|
|
||||||
void dispatch(const vk::Vulkan& vk,
|
|
||||||
const vk::CommandBuffer& cmd, VkExtent2D extent) const;
|
|
||||||
private:
|
|
||||||
ls::R<const vk::Shader> shader;
|
|
||||||
|
|
||||||
std::vector<vk::Barrier> barriers;
|
|
||||||
vk::DescriptorSet descriptorSet;
|
|
||||||
|
|
||||||
// simple move constructor
|
|
||||||
ManagedShader(ls::R<const vk::Shader> shader,
|
|
||||||
std::vector<vk::Barrier> barriers,
|
|
||||||
vk::DescriptorSet descriptorSet) :
|
|
||||||
shader(shader),
|
|
||||||
barriers(std::move(barriers)),
|
|
||||||
descriptorSet(std::move(descriptorSet)) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// class for building managed shaders
|
|
||||||
/// this class is NOT memory-safe
|
|
||||||
class ManagedShaderBuilder {
|
|
||||||
public:
|
|
||||||
/// default constructor
|
|
||||||
ManagedShaderBuilder() = default;
|
|
||||||
|
|
||||||
/// add a sampled image
|
|
||||||
/// @param image image to add
|
|
||||||
[[nodiscard]] ManagedShaderBuilder& sampled(const vk::Image& image);
|
|
||||||
/// add multiple sampled images
|
|
||||||
/// @param images images to add
|
|
||||||
/// @param offset offset into images
|
|
||||||
/// @param count number of images to add (0 = all)
|
|
||||||
[[nodiscard]] ManagedShaderBuilder& sampleds(const std::vector<vk::Image>& images,
|
|
||||||
size_t offset = 0, size_t count = 0);
|
|
||||||
|
|
||||||
/// add a storage image
|
|
||||||
/// @param image image to add
|
|
||||||
[[nodiscard]] ManagedShaderBuilder& storage(const vk::Image& image);
|
|
||||||
/// add multiple storage images
|
|
||||||
/// @param images images to add
|
|
||||||
/// @param offset offset into images
|
|
||||||
/// @param count number of images to add (0 = all)
|
|
||||||
[[nodiscard]] ManagedShaderBuilder& storages(const std::vector<vk::Image>& images,
|
|
||||||
size_t offset = 0, size_t count = 0);
|
|
||||||
|
|
||||||
/// add a sampler
|
|
||||||
/// @param sampler sampler to add
|
|
||||||
[[nodiscard]] ManagedShaderBuilder& sampler(const vk::Sampler& sampler);
|
|
||||||
/// add multiple samplers
|
|
||||||
/// @param samplers samplers to add
|
|
||||||
[[nodiscard]] ManagedShaderBuilder& samplers(const std::vector<vk::Sampler>& samplers);
|
|
||||||
|
|
||||||
/// add a buffer
|
|
||||||
/// @param buffer buffer to add
|
|
||||||
[[nodiscard]] ManagedShaderBuilder& buffer(const vk::Buffer& buffer);
|
|
||||||
|
|
||||||
/// build the managed shader
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param pool the descriptor pool to use
|
|
||||||
/// @param shader the shader to use
|
|
||||||
/// @returns the built managed shader
|
|
||||||
[[nodiscard]] ManagedShader build(const vk::Vulkan& vk,
|
|
||||||
const vk::DescriptorPool& pool, const vk::Shader& shader) const;
|
|
||||||
private:
|
|
||||||
std::vector<ls::R<const vk::Image>> sampledImages;
|
|
||||||
std::vector<ls::R<const vk::Image>> storageImages;
|
|
||||||
std::vector<ls::R<const vk::Sampler>> imageSamplers;
|
|
||||||
std::vector<ls::R<const vk::Buffer>> constantBuffers;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk;
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
ConstantBuffer backend::getDefaultConstantBuffer(
|
|
||||||
size_t index, size_t total,
|
|
||||||
bool hdr, float invFlow) {
|
|
||||||
return ConstantBuffer {
|
|
||||||
.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 backend::shift_extent(VkExtent2D extent, uint32_t i) {
|
|
||||||
return VkExtent2D{
|
|
||||||
.width = extent.width >> i,
|
|
||||||
.height = extent.height >> i
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
VkExtent2D backend::add_shift_extent(VkExtent2D extent, uint32_t a, uint32_t i) {
|
|
||||||
return VkExtent2D{
|
|
||||||
.width = (extent.width + a) >> i,
|
|
||||||
.height = (extent.height + a) >> i
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string backend::to_hex_id(uint32_t id) {
|
|
||||||
const std::array<char, 17> chars = std::to_array("0123456789ABCDEF");
|
|
||||||
|
|
||||||
std::string result = "0x";
|
|
||||||
result += chars.at((id >> 12) & 0xF);
|
|
||||||
result += chars.at((id >> 8) & 0xF);
|
|
||||||
result += chars.at((id >> 4) & 0xF);
|
|
||||||
result += chars.at(id & 0xF);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#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/descriptor_pool.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/sampler.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// exposed context data
|
|
||||||
struct Ctx {
|
|
||||||
ls::R<const vk::Vulkan> vk; // safe back reference
|
|
||||||
ls::R<const ShaderRegistry> shaders; // safe back reference
|
|
||||||
|
|
||||||
vk::DescriptorPool pool;
|
|
||||||
|
|
||||||
vk::Buffer constantBuffer;
|
|
||||||
std::vector<vk::Buffer> constantBuffers;
|
|
||||||
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
|
|
||||||
/// @return prefilled constant buffer
|
|
||||||
ConstantBuffer getDefaultConstantBuffer(
|
|
||||||
size_t index, size_t total,
|
|
||||||
bool hdr, float invFlow
|
|
||||||
);
|
|
||||||
|
|
||||||
/// 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);
|
|
||||||
|
|
||||||
/// convert a device/vendor id into a hex string
|
|
||||||
std::string to_hex_id(uint32_t id);
|
|
||||||
}
|
|
||||||
|
|
@ -1,666 +1,7 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
#include "lsfg-vk-backend/lsfgvk.hpp"
|
#include "lsfg-vk/lsfgvk.hpp"
|
||||||
#include "extraction/dll_reader.hpp"
|
|
||||||
#include "extraction/shader_registry.hpp"
|
|
||||||
#include "helpers/limits.hpp"
|
|
||||||
#include "helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/errors.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/fence.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
#include "shaderchains/alpha0.hpp"
|
|
||||||
#include "shaderchains/alpha1.hpp"
|
|
||||||
#include "shaderchains/beta0.hpp"
|
|
||||||
#include "shaderchains/beta1.hpp"
|
|
||||||
#include "shaderchains/delta0.hpp"
|
|
||||||
#include "shaderchains/delta1.hpp"
|
|
||||||
#include "shaderchains/gamma0.hpp"
|
|
||||||
#include "shaderchains/gamma1.hpp"
|
|
||||||
#include "shaderchains/generate.hpp"
|
|
||||||
#include "shaderchains/mipmaps.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <exception>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
|
||||||
#include <renderdoc_app.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace lsfgvk;
|
using namespace lsfgvk;
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
/* TODO */
|
||||||
error::error(const std::string& msg, const std::exception& inner)
|
|
||||||
: std::runtime_error(msg + "\n- " + inner.what()) {}
|
|
||||||
error::error(const std::string& msg)
|
|
||||||
: std::runtime_error(msg) {}
|
|
||||||
error::~error() = default;
|
|
||||||
|
|
||||||
/// 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_TESTING_RENDERDOC
|
|
||||||
/// get the RenderDoc API
|
|
||||||
/// @return the RenderDoc API
|
|
||||||
[[nodiscard]] const auto& getRenderDocAPI() const { return this->renderdoc; }
|
|
||||||
#endif
|
|
||||||
// Movable, non-copyable, custom destructor
|
|
||||||
InstanceImpl(const InstanceImpl&) = delete;
|
|
||||||
InstanceImpl& operator=(const InstanceImpl&) = delete;
|
|
||||||
InstanceImpl(InstanceImpl&&) = default;
|
|
||||||
InstanceImpl& operator=(InstanceImpl&&) = default;
|
|
||||||
~InstanceImpl();
|
|
||||||
private:
|
|
||||||
vk::Vulkan vk;
|
|
||||||
ShaderRegistry shaders;
|
|
||||||
|
|
||||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
|
||||||
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::Image blackImage;
|
|
||||||
|
|
||||||
vk::TimelineSemaphore syncSemaphore; // imported
|
|
||||||
vk::TimelineSemaphore prepassSemaphore;
|
|
||||||
size_t idx{1};
|
|
||||||
size_t fidx{0}; // real frame index
|
|
||||||
|
|
||||||
std::vector<vk::CommandBuffer> cmdbufs;
|
|
||||||
vk::Fence cmdbufFence;
|
|
||||||
|
|
||||||
Ctx ctx;
|
|
||||||
|
|
||||||
Mipmaps mipmaps;
|
|
||||||
std::array<Alpha0, 7> alpha0;
|
|
||||||
std::array<Alpha1, 7> alpha1;
|
|
||||||
Beta0 beta0;
|
|
||||||
Beta1 beta1;
|
|
||||||
struct Pass {
|
|
||||||
std::vector<Gamma0> gamma0;
|
|
||||||
std::vector<Gamma1> gamma1;
|
|
||||||
|
|
||||||
std::vector<Delta0> delta0;
|
|
||||||
std::vector<Delta1> delta1;
|
|
||||||
ls::lazy<Generate> generate;
|
|
||||||
};
|
|
||||||
std::vector<Pass> passes;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Instance::Instance(
|
|
||||||
const DevicePicker& devicePicker,
|
|
||||||
const std::filesystem::path& shaderDllPath,
|
|
||||||
bool allowLowPrecision) {
|
|
||||||
const auto selectFunc = [&devicePicker](const vk::VulkanInstanceFuncs funcs,
|
|
||||||
const std::vector<VkPhysicalDevice>& devices) {
|
|
||||||
for (const auto& device : devices) {
|
|
||||||
// check if the physical device supports VK_EXT_pci_bus_info
|
|
||||||
uint32_t ext_count{};
|
|
||||||
funcs.EnumerateDeviceExtensionProperties(device, nullptr, &ext_count, VK_NULL_HANDLE);
|
|
||||||
|
|
||||||
std::vector<VkExtensionProperties> extensions(ext_count);
|
|
||||||
funcs.EnumerateDeviceExtensionProperties(device, nullptr, &ext_count, extensions.data());
|
|
||||||
|
|
||||||
const bool has_pci_ext = std::ranges::find_if(extensions,
|
|
||||||
[](const VkExtensionProperties& ext) {
|
|
||||||
return std::string(std::to_array(ext.extensionName).data())
|
|
||||||
== VK_EXT_PCI_BUS_INFO_EXTENSION_NAME;
|
|
||||||
}) != extensions.end();
|
|
||||||
|
|
||||||
// then fetch all available properties
|
|
||||||
VkPhysicalDevicePCIBusInfoPropertiesEXT pciInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT
|
|
||||||
};
|
|
||||||
VkPhysicalDeviceProperties2 props{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
|
||||||
.pNext = has_pci_ext ? &pciInfo : nullptr
|
|
||||||
};
|
|
||||||
funcs.GetPhysicalDeviceProperties2(device, &props);
|
|
||||||
|
|
||||||
std::array<char, 256> devname = std::to_array(props.properties.deviceName);
|
|
||||||
devname.at(255) = '\0'; // ensure null-termination
|
|
||||||
|
|
||||||
if (devicePicker(
|
|
||||||
std::string(devname.data()),
|
|
||||||
{ backend::to_hex_id(props.properties.vendorID),
|
|
||||||
backend::to_hex_id(props.properties.deviceID) },
|
|
||||||
has_pci_ext ? std::optional<std::string>{
|
|
||||||
std::to_string(pciInfo.pciBus) + ":" +
|
|
||||||
std::to_string(pciInfo.pciDevice) + "." +
|
|
||||||
std::to_string(pciInfo.pciFunction)
|
|
||||||
} : std::nullopt
|
|
||||||
))
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ls::vulkan_error("no suitable physical device found");
|
|
||||||
};
|
|
||||||
|
|
||||||
this->m_impl = std::make_unique<InstanceImpl>(
|
|
||||||
selectFunc, shaderDllPath, allowLowPrecision
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
/// find the cache file path
|
|
||||||
std::filesystem::path findCacheFilePath() {
|
|
||||||
const char* xdgCacheHome = std::getenv("XDG_CACHE_HOME");
|
|
||||||
if (xdgCacheHome && *xdgCacheHome != '\0')
|
|
||||||
return std::filesystem::path(xdgCacheHome) / "lsfg-vk_pipeline_cache.bin";
|
|
||||||
|
|
||||||
const char* home = std::getenv("HOME");
|
|
||||||
if (home && *home != '\0')
|
|
||||||
return std::filesystem::path(home) / ".cache" / "lsfg-vk_pipeline_cache.bin";
|
|
||||||
|
|
||||||
return{"/tmp/lsfg-vk_pipeline_cache.bin"};
|
|
||||||
}
|
|
||||||
/// create a Vulkan instance
|
|
||||||
vk::Vulkan createVulkanInstance(vk::PhysicalDeviceSelector selectPhysicalDevice) {
|
|
||||||
try {
|
|
||||||
return{
|
|
||||||
"lsfg-vk", vk::version{2, 0, 0},
|
|
||||||
"lsfg-vk-engine", vk::version{2, 0, 0},
|
|
||||||
selectPhysicalDevice,
|
|
||||||
false, std::nullopt,
|
|
||||||
findCacheFilePath()
|
|
||||||
};
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
throw backend::error("Unable to initialize Vulkan", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// build a shader registry
|
|
||||||
ShaderRegistry createShaderRegistry(vk::Vulkan& vk,
|
|
||||||
const std::filesystem::path& shaderDllPath,
|
|
||||||
bool allowLowPrecision) {
|
|
||||||
std::unordered_map<uint32_t, std::vector<uint8_t>> resources{};
|
|
||||||
|
|
||||||
try {
|
|
||||||
resources = backend::extractResourcesFromDLL(shaderDllPath);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
throw backend::error("Unable to parse Lossless Scaling DLL", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return backend::buildShaderRegistry(
|
|
||||||
vk, allowLowPrecision && vk.supportsFP16(),
|
|
||||||
resources
|
|
||||||
);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
throw backend::error("Unable to build shader registry", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
|
||||||
/// 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_TESTING_RENDERDOC
|
|
||||||
this->renderdoc = loadRenderDocIntegration();
|
|
||||||
#endif
|
|
||||||
vk.persistPipelineCache(); // will silently fail
|
|
||||||
}
|
|
||||||
|
|
||||||
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 backend::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 backend::error("Unable to import destination images", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// create a black image
|
|
||||||
vk::Image createBlackImage(const vk::Vulkan& vk) {
|
|
||||||
try {
|
|
||||||
return{vk,
|
|
||||||
{ .width = 4, .height = 4 }
|
|
||||||
};
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
throw backend::error("Unable to create black image", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// import timeline semaphore
|
|
||||||
vk::TimelineSemaphore importTimelineSemaphore(const vk::Vulkan& vk, int syncFd) {
|
|
||||||
try {
|
|
||||||
return{vk, 0, syncFd};
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
throw backend::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 backend::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 backend::error("Unable to create command buffers", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// create context data
|
|
||||||
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 {
|
|
||||||
std::vector<vk::Buffer> constantBuffers{};
|
|
||||||
constantBuffers.reserve(count);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < count; ++i)
|
|
||||||
constantBuffers.emplace_back(vk,
|
|
||||||
backend::getDefaultConstantBuffer(
|
|
||||||
i, count,
|
|
||||||
hdr, flow
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
.vk = std::ref(vk),
|
|
||||||
.shaders = std::ref(shaders),
|
|
||||||
.pool{vk, backend::calculateDescriptorPoolLimits(count, perf)},
|
|
||||||
.constantBuffer{vk, backend::getDefaultConstantBuffer(0, 1, hdr, flow)},
|
|
||||||
.constantBuffers{std::move(constantBuffers)},
|
|
||||||
.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 backend::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)),
|
|
||||||
blackImage(createBlackImage(instance.getVulkan())),
|
|
||||||
syncSemaphore(importTimelineSemaphore(instance.getVulkan(), syncFd)),
|
|
||||||
prepassSemaphore(createPrepassSemaphore(instance.getVulkan())),
|
|
||||||
cmdbufs(createCommandBuffers(instance.getVulkan(), destFds.size() + 1)),
|
|
||||||
cmdbufFence(instance.getVulkan()),
|
|
||||||
ctx(createCtx(instance, extent, hdr, flow, perf, destFds.size())),
|
|
||||||
mipmaps(ctx, sourceImages),
|
|
||||||
alpha0{
|
|
||||||
Alpha0(ctx, mipmaps.getImages().at(0)),
|
|
||||||
Alpha0(ctx, mipmaps.getImages().at(1)),
|
|
||||||
Alpha0(ctx, mipmaps.getImages().at(2)),
|
|
||||||
Alpha0(ctx, mipmaps.getImages().at(3)),
|
|
||||||
Alpha0(ctx, mipmaps.getImages().at(4)),
|
|
||||||
Alpha0(ctx, mipmaps.getImages().at(5)),
|
|
||||||
Alpha0(ctx, mipmaps.getImages().at(6))
|
|
||||||
},
|
|
||||||
alpha1{
|
|
||||||
Alpha1(ctx, 3, alpha0.at(0).getImages()),
|
|
||||||
Alpha1(ctx, 2, alpha0.at(1).getImages()),
|
|
||||||
Alpha1(ctx, 2, alpha0.at(2).getImages()),
|
|
||||||
Alpha1(ctx, 2, alpha0.at(3).getImages()),
|
|
||||||
Alpha1(ctx, 2, alpha0.at(4).getImages()),
|
|
||||||
Alpha1(ctx, 2, alpha0.at(5).getImages()),
|
|
||||||
Alpha1(ctx, 2, alpha0.at(6).getImages())
|
|
||||||
},
|
|
||||||
beta0(ctx, alpha1.at(0).getImages()),
|
|
||||||
beta1(ctx, beta0.getImages()) {
|
|
||||||
// build main passes
|
|
||||||
for (size_t i = 0; i < destImages.size(); ++i) {
|
|
||||||
auto& pass = this->passes.emplace_back();
|
|
||||||
|
|
||||||
pass.gamma0.reserve(7);
|
|
||||||
pass.gamma1.reserve(7);
|
|
||||||
pass.delta0.reserve(3);
|
|
||||||
pass.delta1.reserve(3);
|
|
||||||
for (size_t j = 0; j < 7; j++) {
|
|
||||||
if (j == 0) { // first pass has no prior data
|
|
||||||
pass.gamma0.emplace_back(ctx, i,
|
|
||||||
this->alpha1.at(6 - j).getImages(),
|
|
||||||
this->blackImage
|
|
||||||
);
|
|
||||||
pass.gamma1.emplace_back(ctx, i,
|
|
||||||
pass.gamma0.at(j).getImages(),
|
|
||||||
this->blackImage,
|
|
||||||
this->beta1.getImages().at(5)
|
|
||||||
);
|
|
||||||
} else { // other passes use prior data
|
|
||||||
pass.gamma0.emplace_back(ctx, i,
|
|
||||||
this->alpha1.at(6 - j).getImages(),
|
|
||||||
pass.gamma1.at(j - 1).getImage()
|
|
||||||
);
|
|
||||||
pass.gamma1.emplace_back(ctx, i,
|
|
||||||
pass.gamma0.at(j).getImages(),
|
|
||||||
pass.gamma1.at(j - 1).getImage(),
|
|
||||||
this->beta1.getImages().at(6 - j)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j == 4) { // first special pass has no prior data
|
|
||||||
pass.delta0.emplace_back(ctx, i,
|
|
||||||
this->alpha1.at(6 - j).getImages(),
|
|
||||||
this->blackImage,
|
|
||||||
pass.gamma1.at(j - 1).getImage()
|
|
||||||
);
|
|
||||||
pass.delta1.emplace_back(ctx, i,
|
|
||||||
pass.delta0.at(j - 4).getImages0(),
|
|
||||||
pass.delta0.at(j - 4).getImages1(),
|
|
||||||
this->blackImage,
|
|
||||||
this->beta1.getImages().at(6 - j),
|
|
||||||
this->blackImage
|
|
||||||
);
|
|
||||||
} else if (j > 4) { // further passes do
|
|
||||||
pass.delta0.emplace_back(ctx, i,
|
|
||||||
this->alpha1.at(6 - j).getImages(),
|
|
||||||
pass.delta1.at(j - 5).getImage0(),
|
|
||||||
pass.gamma1.at(j - 1).getImage()
|
|
||||||
);
|
|
||||||
pass.delta1.emplace_back(ctx, i,
|
|
||||||
pass.delta0.at(j - 4).getImages0(),
|
|
||||||
pass.delta0.at(j - 4).getImages1(),
|
|
||||||
pass.delta1.at(j - 5).getImage0(),
|
|
||||||
this->beta1.getImages().at(6 - j),
|
|
||||||
pass.delta1.at(j - 5).getImage1()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pass.generate.emplace(ctx, i,
|
|
||||||
this->sourceImages,
|
|
||||||
pass.gamma1.at(6).getImage(),
|
|
||||||
pass.delta1.at(2).getImage0(),
|
|
||||||
pass.delta1.at(2).getImage1(),
|
|
||||||
this->destImages.at(i)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize all images
|
|
||||||
std::vector<VkImage> images{};
|
|
||||||
images.push_back(this->blackImage.handle());
|
|
||||||
mipmaps.prepare(images);
|
|
||||||
for (size_t i = 0; i < 7; ++i) {
|
|
||||||
alpha0.at(i).prepare(images);
|
|
||||||
alpha1.at(i).prepare(images);
|
|
||||||
}
|
|
||||||
beta0.prepare(images);
|
|
||||||
beta1.prepare(images);
|
|
||||||
for (const auto& pass : this->passes) {
|
|
||||||
for (size_t i = 0; i < 7; ++i) {
|
|
||||||
pass.gamma0.at(i).prepare(images);
|
|
||||||
pass.gamma1.at(i).prepare(images);
|
|
||||||
|
|
||||||
if (i < 4) continue;
|
|
||||||
pass.delta0.at(i - 4).prepare(images);
|
|
||||||
pass.delta1.at(i - 4).prepare(images);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<vk::Barrier> barriers{};
|
|
||||||
barriers.reserve(images.size());
|
|
||||||
|
|
||||||
for (const auto& image : images) {
|
|
||||||
barriers.emplace_back(vk::Barrier {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = image,
|
|
||||||
.subresourceRange = {
|
|
||||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
.levelCount = 1,
|
|
||||||
.layerCount = 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const vk::CommandBuffer cmdbuf{ctx.vk};
|
|
||||||
cmdbuf.begin(ctx.vk);
|
|
||||||
cmdbuf.insertBarriers(ctx.vk, barriers);
|
|
||||||
cmdbuf.end(ctx.vk);
|
|
||||||
cmdbuf.submit(ctx.vk); // wait for completion
|
|
||||||
}
|
|
||||||
|
|
||||||
void Instance::scheduleFrames(Context& context) { // NOLINT (static)
|
|
||||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
|
||||||
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 backend::error("Unable to schedule frames", e);
|
|
||||||
}
|
|
||||||
#ifdef LSFGVK_TESTING_RENDERDOC
|
|
||||||
if (impl->getRenderDocAPI()) {
|
|
||||||
impl->getVulkan().df().DeviceWaitIdle(impl->getVulkan().dev());
|
|
||||||
impl->getRenderDocAPI()->EndFrameCapture(
|
|
||||||
RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(impl->getVulkan().inst()),
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Context::scheduleFrames() {
|
|
||||||
// wait for previous pre-pass to complete
|
|
||||||
if (this->fidx && !this->cmdbufFence.wait(this->ctx.vk))
|
|
||||||
throw backend::error("Timeout waiting for previous frame to complete");
|
|
||||||
this->cmdbufFence.reset(this->ctx.vk);
|
|
||||||
|
|
||||||
// schedule pre-pass
|
|
||||||
const auto& cmdbuf = this->cmdbufs.at(0);
|
|
||||||
cmdbuf.begin(ctx.vk);
|
|
||||||
|
|
||||||
this->mipmaps.render(ctx.vk, cmdbuf, this->fidx);
|
|
||||||
for (size_t i = 0; i < 7; ++i) {
|
|
||||||
this->alpha0.at(6 - i).render(ctx.vk, cmdbuf);
|
|
||||||
this->alpha1.at(6 - i).render(ctx.vk, cmdbuf, this->fidx);
|
|
||||||
}
|
|
||||||
this->beta0.render(ctx.vk, cmdbuf, this->fidx);
|
|
||||||
this->beta1.render(ctx.vk, cmdbuf);
|
|
||||||
|
|
||||||
cmdbuf.end(ctx.vk);
|
|
||||||
cmdbuf.submit(this->ctx.vk,
|
|
||||||
{}, this->syncSemaphore.handle(), this->idx,
|
|
||||||
{}, this->prepassSemaphore.handle(), this->idx
|
|
||||||
);
|
|
||||||
|
|
||||||
this->idx++;
|
|
||||||
|
|
||||||
// schedule main passes
|
|
||||||
for (size_t i = 0; i < this->destImages.size(); i++) {
|
|
||||||
const auto& cmdbuf = this->cmdbufs.at(i + 1);
|
|
||||||
cmdbuf.begin(ctx.vk);
|
|
||||||
|
|
||||||
const auto& pass = this->passes.at(i);
|
|
||||||
for (size_t j = 0; j < 7; j++) {
|
|
||||||
pass.gamma0.at(j).render(ctx.vk, cmdbuf, this->fidx);
|
|
||||||
pass.gamma1.at(j).render(ctx.vk, cmdbuf);
|
|
||||||
|
|
||||||
if (j < 4) continue;
|
|
||||||
pass.delta0.at(j - 4).render(ctx.vk, cmdbuf, this->fidx);
|
|
||||||
pass.delta1.at(j - 4).render(ctx.vk, cmdbuf);
|
|
||||||
}
|
|
||||||
pass.generate->render(ctx.vk, cmdbuf, this->fidx);
|
|
||||||
|
|
||||||
cmdbuf.end(ctx.vk);
|
|
||||||
cmdbuf.submit(this->ctx.vk,
|
|
||||||
{}, this->prepassSemaphore.handle(), this->idx - 1,
|
|
||||||
{}, this->syncSemaphore.handle(), this->idx + i,
|
|
||||||
i == this->destImages.size() - 1 ? this->cmdbufFence.handle() : VK_NULL_HANDLE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->idx += this->destImages.size();
|
|
||||||
this->fidx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 backend::error("attempted to close unknown context",
|
|
||||||
std::runtime_error("no such context"));
|
|
||||||
|
|
||||||
const auto& vk = this->m_impl->getVulkan();
|
|
||||||
vk.df().DeviceWaitIdle(vk.dev());
|
|
||||||
|
|
||||||
this->m_contexts.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
Instance::~Instance() = default;
|
|
||||||
|
|
||||||
// leaking shenanigans
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
bool leaking{false}; // NOLINT (global variable)
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceImpl::~InstanceImpl() {
|
|
||||||
if (!leaking) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
new vk::Vulkan(std::move(this->vk));
|
|
||||||
} catch (...) {
|
|
||||||
std::cerr << "lsfg-vk: failed to leak Vulkan instance\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void backend::makeLeaking() {
|
|
||||||
leaking = true;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "alpha0.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Alpha0::Alpha0(const Ctx& ctx,
|
|
||||||
const vk::Image& sourceImage) {
|
|
||||||
const size_t m = ctx.perf ? 1 : 2; // multiplier
|
|
||||||
const VkExtent2D halfExtent = backend::add_shift_extent(sourceImage.getExtent(), 1, 1);
|
|
||||||
const VkExtent2D quarterExtent = backend::add_shift_extent(halfExtent, 1, 1);
|
|
||||||
|
|
||||||
// create temporary & output images
|
|
||||||
this->tempImages0.reserve(m);
|
|
||||||
this->tempImages1.reserve(m);
|
|
||||||
for (size_t i = 0; i < m; i++) {
|
|
||||||
this->tempImages0.emplace_back(ctx.vk, halfExtent);
|
|
||||||
this->tempImages1.emplace_back(ctx.vk, halfExtent);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->images.reserve(2 * m);
|
|
||||||
for (size_t i = 0; i < (2 * m); i++)
|
|
||||||
this->images.emplace_back(ctx.vk, quarterExtent);
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shaders = ctx.perf ? ctx.shaders.get().performance : ctx.shaders.get().quality;
|
|
||||||
this->sets.reserve(3);
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampled(sourceImage)
|
|
||||||
.storages(this->tempImages0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.alpha.at(0)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0)
|
|
||||||
.storages(this->tempImages1)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.alpha.at(1)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages1)
|
|
||||||
.storages(this->images)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.alpha.at(2)));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent0 = backend::add_shift_extent(halfExtent, 7, 3);
|
|
||||||
this->dispatchExtent1 = backend::add_shift_extent(quarterExtent, 7, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Alpha0::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (size_t i = 0; i < this->tempImages0.size(); i++) {
|
|
||||||
images.push_back(this->tempImages0.at(i).handle());
|
|
||||||
images.push_back(this->tempImages1.at(i).handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& image : this->images)
|
|
||||||
images.push_back(image.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Alpha0::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const {
|
|
||||||
this->sets.at(0).dispatch(vk, cmd, this->dispatchExtent0);
|
|
||||||
this->sets.at(1).dispatch(vk, cmd, this->dispatchExtent0);
|
|
||||||
this->sets.at(2).dispatch(vk, cmd, this->dispatchExtent1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// pre-alpha shaderchain
|
|
||||||
class Alpha0 {
|
|
||||||
public:
|
|
||||||
/// create a pre-alpha shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param sourceImage source image
|
|
||||||
Alpha0(const Ctx& ctx,
|
|
||||||
const vk::Image& sourceImage);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the pre-alpha shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const;
|
|
||||||
|
|
||||||
/// get the generated images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages() const { return this->images; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> tempImages0;
|
|
||||||
std::vector<vk::Image> tempImages1;
|
|
||||||
std::vector<vk::Image> images;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent0{};
|
|
||||||
VkExtent2D dispatchExtent1{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "alpha1.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Alpha1::Alpha1(const Ctx& ctx, size_t temporal,
|
|
||||||
const std::vector<vk::Image>& sourceImages) {
|
|
||||||
const size_t m = ctx.perf ? 1 : 2; // multiplier
|
|
||||||
const VkExtent2D quarterExtent = sourceImages.at(0).getExtent();
|
|
||||||
|
|
||||||
// create output images for mod3
|
|
||||||
this->images.reserve(temporal);
|
|
||||||
for(size_t i = 0; i < temporal; i++) {
|
|
||||||
auto& vec = this->images.emplace_back();
|
|
||||||
|
|
||||||
vec.reserve(2 * m);
|
|
||||||
for (size_t j = 0; j < (2 * m); j++)
|
|
||||||
vec.emplace_back(ctx.vk, quarterExtent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shaders = ctx.perf ? ctx.shaders.get().performance : ctx.shaders.get().quality;
|
|
||||||
this->sets.reserve(temporal);
|
|
||||||
for (size_t i = 0; i < temporal; i++)
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages)
|
|
||||||
.storages(this->images.at(i))
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.alpha.at(3)));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(quarterExtent, 7, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Alpha1::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (const auto& vec : this->images)
|
|
||||||
for (const auto& img : vec)
|
|
||||||
images.push_back(img.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Alpha1::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const {
|
|
||||||
this->sets.at(idx % this->sets.size()).dispatch(vk, cmd, dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// alpha shaderchain
|
|
||||||
class Alpha1 {
|
|
||||||
public:
|
|
||||||
/// create a alpha shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param temporal temporal count
|
|
||||||
/// @param sourceImages source images
|
|
||||||
Alpha1(const Ctx& ctx, size_t temporal,
|
|
||||||
const std::vector<vk::Image>& sourceImages);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the alpha shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
/// @param idx frame index
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const;
|
|
||||||
|
|
||||||
/// get the generated images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages() const { return this->images; }
|
|
||||||
private:
|
|
||||||
std::vector<std::vector<vk::Image>> images;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "beta0.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Beta0::Beta0(const Ctx& ctx,
|
|
||||||
const std::vector<std::vector<vk::Image>>& sourceImages) {
|
|
||||||
const VkExtent2D extent = sourceImages.at(0).at(0).getExtent();
|
|
||||||
|
|
||||||
// create output images
|
|
||||||
this->images.reserve(2);
|
|
||||||
for(size_t i = 0; i < 2; i++)
|
|
||||||
this->images.emplace_back(ctx.vk, extent);
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shader = (ctx.perf ?
|
|
||||||
ctx.shaders.get().performance : ctx.shaders.get().quality).beta.at(0);
|
|
||||||
this->sets.reserve(sourceImages.size());
|
|
||||||
for (size_t i = 0; i < sourceImages.size(); i++)
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages.at((i + (sourceImages.size() - 2)) % sourceImages.size()))
|
|
||||||
.sampleds(sourceImages.at((i + (sourceImages.size() - 1)) % sourceImages.size()))
|
|
||||||
.sampleds(sourceImages.at(i % sourceImages.size()))
|
|
||||||
.storages(this->images)
|
|
||||||
.sampler(ctx.bnwSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shader));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(extent, 7, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Beta0::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (const auto& img : this->images)
|
|
||||||
images.push_back(img.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Beta0::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const {
|
|
||||||
this->sets.at(idx % this->sets.size()).dispatch(vk, cmd, dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// beta shaderchain
|
|
||||||
class Beta0 {
|
|
||||||
public:
|
|
||||||
/// create a beta shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param sourceImages source images
|
|
||||||
Beta0(const Ctx& ctx,
|
|
||||||
const std::vector<std::vector<vk::Image>>& sourceImages);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the beta shaderchain
|
|
||||||
/// @param vk vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
/// @param idx frame index
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const;
|
|
||||||
|
|
||||||
/// get the generated images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages() const { return this->images; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> images;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "beta1.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Beta1::Beta1(const Ctx& ctx,
|
|
||||||
const std::vector<vk::Image>& sourceImages) {
|
|
||||||
const VkExtent2D extent = sourceImages.at(0).getExtent();
|
|
||||||
|
|
||||||
// create temporary & output images
|
|
||||||
this->tempImages0.reserve(2);
|
|
||||||
this->tempImages1.reserve(2);
|
|
||||||
for(uint32_t i = 0; i < 2; i++) {
|
|
||||||
this->tempImages0.emplace_back(ctx.vk, extent);
|
|
||||||
this->tempImages1.emplace_back(ctx.vk, extent);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->images.reserve(6);
|
|
||||||
for (uint32_t i = 0; i < 6; i++)
|
|
||||||
this->images.emplace_back(ctx.vk,
|
|
||||||
backend::shift_extent(extent, i),
|
|
||||||
VK_FORMAT_R8_UNORM);
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shaders = (ctx.perf ?
|
|
||||||
ctx.shaders.get().performance : ctx.shaders.get().quality).beta;
|
|
||||||
this->sets.reserve(4);
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages)
|
|
||||||
.storages(this->tempImages0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(1)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0)
|
|
||||||
.storages(this->tempImages1)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(2)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages1)
|
|
||||||
.storages(this->tempImages0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(3)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0)
|
|
||||||
.storages(this->images)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.buffer(ctx.constantBuffer)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(4)));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent0 = backend::add_shift_extent(extent, 7, 3);
|
|
||||||
this->dispatchExtent1 = backend::add_shift_extent(extent, 31, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Beta1::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (size_t i = 0; i < 2; i++) {
|
|
||||||
images.push_back(this->tempImages0.at(i).handle());
|
|
||||||
images.push_back(this->tempImages1.at(i).handle());
|
|
||||||
}
|
|
||||||
for (const auto& img : this->images)
|
|
||||||
images.push_back(img.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Beta1::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const {
|
|
||||||
this->sets.at(0).dispatch(vk, cmd, this->dispatchExtent0);
|
|
||||||
this->sets.at(1).dispatch(vk, cmd, this->dispatchExtent0);
|
|
||||||
this->sets.at(2).dispatch(vk, cmd, this->dispatchExtent0);
|
|
||||||
this->sets.at(3).dispatch(vk, cmd, this->dispatchExtent1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// beta shaderchain
|
|
||||||
class Beta1 {
|
|
||||||
public:
|
|
||||||
/// create a beta shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param sourceImages source images
|
|
||||||
Beta1(const Ctx& ctx,
|
|
||||||
const std::vector<vk::Image>& sourceImages);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the beta shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const;
|
|
||||||
|
|
||||||
/// get the generated images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages() const { return this->images; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> tempImages0;
|
|
||||||
std::vector<vk::Image> tempImages1;
|
|
||||||
std::vector<vk::Image> images;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent0{};
|
|
||||||
VkExtent2D dispatchExtent1{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "delta0.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Delta0::Delta0(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<std::vector<vk::Image>>& sourceImages,
|
|
||||||
const vk::Image& additionalInput0,
|
|
||||||
const vk::Image& additionalInput1) {
|
|
||||||
const size_t m = ctx.perf ? 1 : 2; // multiplier
|
|
||||||
const VkExtent2D extent = sourceImages.at(0).at(0).getExtent();
|
|
||||||
|
|
||||||
// create output images
|
|
||||||
this->images0.reserve(3);
|
|
||||||
for(size_t i = 0; i < 3; i++)
|
|
||||||
this->images0.emplace_back(ctx.vk, extent);
|
|
||||||
this->images1.reserve(m);
|
|
||||||
for (size_t i = 0; i < m; i++)
|
|
||||||
this->images1.emplace_back(ctx.vk, extent);
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shaders = (ctx.perf ?
|
|
||||||
ctx.shaders.get().performance : ctx.shaders.get().quality).delta;
|
|
||||||
|
|
||||||
this->sets0.reserve(sourceImages.size());
|
|
||||||
for (size_t i = 0; i < sourceImages.size(); i++)
|
|
||||||
this->sets0.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages.at((i + (sourceImages.size() - 1)) % sourceImages.size()))
|
|
||||||
.sampleds(sourceImages.at(i % sourceImages.size()))
|
|
||||||
.sampled(additionalInput0)
|
|
||||||
.storages(this->images0)
|
|
||||||
.sampler(ctx.bnwSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(0)));
|
|
||||||
|
|
||||||
this->sets1.reserve(sourceImages.size());
|
|
||||||
for (size_t i = 0; i < sourceImages.size(); i++)
|
|
||||||
this->sets1.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages.at((i + (sourceImages.size() - 1)) % sourceImages.size()))
|
|
||||||
.sampleds(sourceImages.at(i % sourceImages.size()))
|
|
||||||
.sampled(additionalInput1)
|
|
||||||
.sampled(additionalInput0)
|
|
||||||
.storages(this->images1)
|
|
||||||
.sampler(ctx.bnwSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(5)));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(extent, 7, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Delta0::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (const auto& img : this->images0)
|
|
||||||
images.push_back(img.handle());
|
|
||||||
for (const auto& img : this->images1)
|
|
||||||
images.push_back(img.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Delta0::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const {
|
|
||||||
this->sets0.at(idx % this->sets0.size()).dispatch(vk, cmd, dispatchExtent);
|
|
||||||
this->sets1.at(idx % this->sets1.size()).dispatch(vk, cmd, dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// delta shaderchain
|
|
||||||
class Delta0 {
|
|
||||||
public:
|
|
||||||
/// create a delta shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param idx generated frame index
|
|
||||||
/// @param sourceImages source images
|
|
||||||
/// @param additionalInput0 additional input image
|
|
||||||
/// @param additionalInput1 additional input image
|
|
||||||
Delta0(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<std::vector<vk::Image>>& sourceImages,
|
|
||||||
const vk::Image& additionalInput0,
|
|
||||||
const vk::Image& additionalInput1);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the delta shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
/// @param idx frame index
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const;
|
|
||||||
|
|
||||||
/// get the generated images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages0() const { return this->images0; }
|
|
||||||
|
|
||||||
/// get the other generated images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages1() const { return this->images1; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> images0;
|
|
||||||
std::vector<vk::Image> images1;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets0;
|
|
||||||
std::vector<ManagedShader> sets1;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "delta1.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Delta1::Delta1(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<vk::Image>& sourceImages0,
|
|
||||||
const std::vector<vk::Image>& sourceImages1,
|
|
||||||
const vk::Image& additionalInput0,
|
|
||||||
const vk::Image& additionalInput1,
|
|
||||||
const vk::Image& additionalInput2) {
|
|
||||||
const size_t m = ctx.perf ? 1 : 2; // multiplier
|
|
||||||
const VkExtent2D extent = sourceImages0.at(0).getExtent();
|
|
||||||
|
|
||||||
// create temporary & output images
|
|
||||||
for (size_t i = 0; i < (2 * m); i++) {
|
|
||||||
this->tempImages0.emplace_back(ctx.vk, extent);
|
|
||||||
this->tempImages1.emplace_back(ctx.vk, extent);
|
|
||||||
}
|
|
||||||
this->image0.emplace(ctx.vk,
|
|
||||||
VkExtent2D { extent.width, extent.height },
|
|
||||||
VK_FORMAT_R16G16B16A16_SFLOAT
|
|
||||||
);
|
|
||||||
this->image1.emplace(ctx.vk,
|
|
||||||
VkExtent2D { extent.width, extent.height },
|
|
||||||
VK_FORMAT_R16G16B16A16_SFLOAT
|
|
||||||
);
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shaders = (ctx.perf ?
|
|
||||||
ctx.shaders.get().performance : ctx.shaders.get().quality).delta;
|
|
||||||
this->sets.reserve(4 + 4);
|
|
||||||
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages0)
|
|
||||||
.storages(this->tempImages0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(1)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0)
|
|
||||||
.storages(this->tempImages1)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(2)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages1)
|
|
||||||
.storages(this->tempImages0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(3)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0)
|
|
||||||
.sampled(additionalInput0)
|
|
||||||
.sampled(additionalInput1)
|
|
||||||
.storage(*this->image0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(4)));
|
|
||||||
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages1)
|
|
||||||
.storages(this->tempImages0, 0, m)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(6)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0, 0, m)
|
|
||||||
.storages(this->tempImages1, 0, m)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(7)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages1, 0, m)
|
|
||||||
.storages(this->tempImages0, 0, m)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(8)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0, 0, m)
|
|
||||||
.sampled(additionalInput2)
|
|
||||||
.storage(*this->image1)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(9)));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(extent, 7, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Delta1::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (size_t i = 0; i < this->tempImages0.size(); i++) {
|
|
||||||
images.push_back(this->tempImages0.at(i).handle());
|
|
||||||
images.push_back(this->tempImages1.at(i).handle());
|
|
||||||
}
|
|
||||||
images.push_back(this->image0->handle());
|
|
||||||
images.push_back(this->image1->handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Delta1::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const {
|
|
||||||
for (const auto& set : this->sets)
|
|
||||||
set.dispatch(vk, cmd, dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// gamma shaderchain
|
|
||||||
class Delta1 {
|
|
||||||
public:
|
|
||||||
/// create a gamma shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param idx generated frame index
|
|
||||||
/// @param sourceImages0 source images
|
|
||||||
/// @param sourceImages1 source images
|
|
||||||
/// @param additionalInput0 additional input image
|
|
||||||
/// @param additionalInput1 additional input image
|
|
||||||
/// @param additionalInput2 additional input image
|
|
||||||
Delta1(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<vk::Image>& sourceImages0,
|
|
||||||
const std::vector<vk::Image>& sourceImages1,
|
|
||||||
const vk::Image& additionalInput0,
|
|
||||||
const vk::Image& additionalInput1,
|
|
||||||
const vk::Image& additionalInput2);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the gamma shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const;
|
|
||||||
|
|
||||||
/// get the first generated image
|
|
||||||
/// @return image
|
|
||||||
[[nodiscard]] const auto& getImage0() const { return *this->image0; }
|
|
||||||
|
|
||||||
/// get the second generated image
|
|
||||||
/// @return image
|
|
||||||
[[nodiscard]] const auto& getImage1() const { return *this->image1; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> tempImages0;
|
|
||||||
std::vector<vk::Image> tempImages1;
|
|
||||||
ls::lazy<vk::Image> image0;
|
|
||||||
ls::lazy<vk::Image> image1;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "gamma0.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Gamma0::Gamma0(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<std::vector<vk::Image>>& sourceImages,
|
|
||||||
const vk::Image& additionalInput) {
|
|
||||||
const VkExtent2D extent = sourceImages.at(0).at(0).getExtent();
|
|
||||||
|
|
||||||
// create output images
|
|
||||||
this->images.reserve(3);
|
|
||||||
for(size_t i = 0; i < 3; i++)
|
|
||||||
this->images.emplace_back(ctx.vk, extent);
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shader = (ctx.perf ?
|
|
||||||
ctx.shaders.get().performance : ctx.shaders.get().quality).gamma.at(0);
|
|
||||||
this->sets.reserve(sourceImages.size());
|
|
||||||
for (size_t i = 0; i < sourceImages.size(); i++)
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages.at((i + (sourceImages.size() - 1)) % sourceImages.size()))
|
|
||||||
.sampleds(sourceImages.at(i % sourceImages.size()))
|
|
||||||
.sampled(additionalInput)
|
|
||||||
.storages(this->images)
|
|
||||||
.sampler(ctx.bnwSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shader));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(extent, 7, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gamma0::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (const auto& img : this->images)
|
|
||||||
images.push_back(img.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gamma0::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const {
|
|
||||||
this->sets.at(idx % this->sets.size()).dispatch(vk, cmd, dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// gamma shaderchain
|
|
||||||
class Gamma0 {
|
|
||||||
public:
|
|
||||||
/// create a gamma shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param idx generated frame index
|
|
||||||
/// @param sourceImages source images
|
|
||||||
/// @param additionalInput additional input image
|
|
||||||
Gamma0(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<std::vector<vk::Image>>& sourceImages,
|
|
||||||
const vk::Image& additionalInput);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the gamma shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
/// @param idx frame index
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const;
|
|
||||||
|
|
||||||
/// get the generated images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages() const { return this->images; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> images;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "gamma1.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Gamma1::Gamma1(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<vk::Image>& sourceImages,
|
|
||||||
const vk::Image& additionalInput0,
|
|
||||||
const vk::Image& additionalInput1) {
|
|
||||||
const size_t m = ctx.perf ? 1 : 2; // multiplier
|
|
||||||
const VkExtent2D extent = sourceImages.at(0).getExtent();
|
|
||||||
|
|
||||||
// create temporary & output images
|
|
||||||
for (size_t i = 0; i < (2 * m); i++) {
|
|
||||||
this->tempImages0.emplace_back(ctx.vk, extent);
|
|
||||||
this->tempImages1.emplace_back(ctx.vk, extent);
|
|
||||||
}
|
|
||||||
this->image.emplace(ctx.vk,
|
|
||||||
VkExtent2D { extent.width, extent.height },
|
|
||||||
VK_FORMAT_R16G16B16A16_SFLOAT
|
|
||||||
);
|
|
||||||
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shaders = (ctx.perf ?
|
|
||||||
ctx.shaders.get().performance : ctx.shaders.get().quality).gamma;
|
|
||||||
this->sets.reserve(4);
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(sourceImages)
|
|
||||||
.storages(this->tempImages0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(1)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0)
|
|
||||||
.storages(this->tempImages1)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(2)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages1)
|
|
||||||
.storages(this->tempImages0)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(3)));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampleds(this->tempImages0)
|
|
||||||
.sampled(additionalInput0)
|
|
||||||
.sampled(additionalInput1)
|
|
||||||
.storage(*this->image)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shaders.at(4)));
|
|
||||||
|
|
||||||
// store dispatch extents
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(extent, 7, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gamma1::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (size_t i = 0; i < this->tempImages0.size(); i++) {
|
|
||||||
images.push_back(this->tempImages0.at(i).handle());
|
|
||||||
images.push_back(this->tempImages1.at(i).handle());
|
|
||||||
}
|
|
||||||
images.push_back(this->image->handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gamma1::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const {
|
|
||||||
for (const auto& set : this->sets)
|
|
||||||
set.dispatch(vk, cmd, dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// gamma shaderchain
|
|
||||||
class Gamma1 {
|
|
||||||
public:
|
|
||||||
/// create a gamma shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param idx generated frame index
|
|
||||||
/// @param sourceImages source images
|
|
||||||
/// @param additionalInput0 additional input image
|
|
||||||
/// @param additionalInput1 additional input image
|
|
||||||
Gamma1(const Ctx& ctx, size_t idx,
|
|
||||||
const std::vector<vk::Image>& sourceImages,
|
|
||||||
const vk::Image& additionalInput0,
|
|
||||||
const vk::Image& additionalInput1);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the gamma shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd) const;
|
|
||||||
|
|
||||||
/// get the generated image
|
|
||||||
/// @return image
|
|
||||||
[[nodiscard]] const auto& getImage() const { return *this->image; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> tempImages0;
|
|
||||||
std::vector<vk::Image> tempImages1;
|
|
||||||
ls::lazy<vk::Image> image;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "generate.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Generate::Generate(const Ctx& ctx, size_t idx,
|
|
||||||
const std::pair<vk::Image, vk::Image>& sourceImages,
|
|
||||||
const vk::Image& inputImage1,
|
|
||||||
const vk::Image& inputImage2,
|
|
||||||
const vk::Image& inputImage3,
|
|
||||||
const vk::Image& outputImage) {
|
|
||||||
// create descriptor sets
|
|
||||||
const auto& shader = ctx.hdr ?
|
|
||||||
ctx.shaders.get().generate_hdr : ctx.shaders.get().generate;
|
|
||||||
this->sets.reserve(2);
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampled(sourceImages.second)
|
|
||||||
.sampled(sourceImages.first)
|
|
||||||
.sampled(inputImage1)
|
|
||||||
.sampled(inputImage2)
|
|
||||||
.sampled(inputImage3)
|
|
||||||
.storage(outputImage)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shader));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampled(sourceImages.first)
|
|
||||||
.sampled(sourceImages.second)
|
|
||||||
.sampled(inputImage1)
|
|
||||||
.sampled(inputImage2)
|
|
||||||
.sampled(inputImage3)
|
|
||||||
.storage(outputImage)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.sampler(ctx.eabSampler)
|
|
||||||
.buffer(ctx.constantBuffers.at(idx))
|
|
||||||
.build(ctx.vk, ctx.pool, shader));
|
|
||||||
|
|
||||||
// store dispatch extent
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(ctx.sourceExtent, 15, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Generate::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const {
|
|
||||||
this->sets.at(idx % 2).dispatch(vk, cmd, this->dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// generate shaderchain
|
|
||||||
class Generate {
|
|
||||||
public:
|
|
||||||
/// create a generate shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param idx generated frame index
|
|
||||||
/// @param sourceImages pair of source images
|
|
||||||
/// @param inputImage1 input image 1
|
|
||||||
/// @param inputImage2 input image 2
|
|
||||||
/// @param inputImage3 input image 3
|
|
||||||
Generate(const Ctx& ctx, size_t idx,
|
|
||||||
const std::pair<vk::Image, vk::Image>& sourceImages,
|
|
||||||
const vk::Image& inputImage1,
|
|
||||||
const vk::Image& inputImage2,
|
|
||||||
const vk::Image& inputImage3,
|
|
||||||
const vk::Image& outputImage);
|
|
||||||
|
|
||||||
/// render the generate shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
/// @param idx frame index
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const;
|
|
||||||
private:
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#include "mipmaps.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/helpers/pointers.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
using namespace lsfgvk::backend;
|
|
||||||
|
|
||||||
Mipmaps::Mipmaps(const Ctx& ctx,
|
|
||||||
const std::pair<vk::Image, vk::Image>& sourceImages) {
|
|
||||||
// create output images for base and 6 mips
|
|
||||||
this->images.reserve(7);
|
|
||||||
for (uint32_t i = 0; i < 7; i++)
|
|
||||||
this->images.emplace_back(ctx.vk,
|
|
||||||
backend::shift_extent(ctx.flowExtent, i), VK_FORMAT_R8_UNORM);
|
|
||||||
|
|
||||||
// create descriptor sets for both input images
|
|
||||||
this->sets.reserve(2);
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampled(sourceImages.first)
|
|
||||||
.storages(this->images)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.buffer(ctx.constantBuffer)
|
|
||||||
.build(ctx.vk, ctx.pool, ctx.shaders.get().mipmaps));
|
|
||||||
this->sets.emplace_back(ManagedShaderBuilder()
|
|
||||||
.sampled(sourceImages.second)
|
|
||||||
.storages(this->images)
|
|
||||||
.sampler(ctx.bnbSampler)
|
|
||||||
.buffer(ctx.constantBuffer)
|
|
||||||
.build(ctx.vk, ctx.pool, ctx.shaders.get().mipmaps));
|
|
||||||
|
|
||||||
// store dispatch extent
|
|
||||||
this->dispatchExtent = backend::add_shift_extent(ctx.flowExtent, 63, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mipmaps::prepare(std::vector<VkImage>& images) const {
|
|
||||||
for (const auto& img : this->images)
|
|
||||||
images.push_back(img.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mipmaps::render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const {
|
|
||||||
this->sets.at(idx % 2).dispatch(vk, cmd, this->dispatchExtent);
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../helpers/managed_shader.hpp"
|
|
||||||
#include "../helpers/utils.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/command_buffer.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/image.hpp"
|
|
||||||
#include "lsfg-vk-common/vulkan/vulkan.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
namespace ctx { struct Ctx; }
|
|
||||||
|
|
||||||
namespace lsfgvk::backend {
|
|
||||||
/// mipmaps shaderchain
|
|
||||||
class Mipmaps {
|
|
||||||
public:
|
|
||||||
/// create a mipmaps shaderchain
|
|
||||||
/// @param ctx context
|
|
||||||
/// @param sourceImages pair of source images
|
|
||||||
Mipmaps(const Ctx& ctx,
|
|
||||||
const std::pair<vk::Image, vk::Image>& sourceImages);
|
|
||||||
|
|
||||||
/// prepare the shaderchain initially
|
|
||||||
/// @param images vector to fill with image handles
|
|
||||||
void prepare(std::vector<VkImage>& images) const;
|
|
||||||
|
|
||||||
/// render the mipmaps shaderchain
|
|
||||||
/// @param vk the vulkan instance
|
|
||||||
/// @param cmd command buffer
|
|
||||||
/// @param idx frame index
|
|
||||||
void render(const vk::Vulkan& vk, const vk::CommandBuffer& cmd, size_t idx) const;
|
|
||||||
|
|
||||||
/// get the generated mipmap images
|
|
||||||
/// @return vector of images
|
|
||||||
[[nodiscard]] const auto& getImages() const { return this->images; }
|
|
||||||
private:
|
|
||||||
std::vector<vk::Image> images;
|
|
||||||
|
|
||||||
std::vector<ManagedShader> sets;
|
|
||||||
VkExtent2D dispatchExtent{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
53
lsfg-vk-backend/src/utility/logger.cpp
Normal file
53
lsfg-vk-backend/src/utility/logger.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
using namespace lsfgvk;
|
||||||
|
using namespace lsfgvk::logger;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// Get the current minimum log level
|
||||||
|
Level& currentLevel() {
|
||||||
|
static Level level{Level::Debug};
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format a log level as a string
|
||||||
|
constexpr std::string_view formatLevel(Level level) {
|
||||||
|
switch (level) {
|
||||||
|
case Level::Debug:
|
||||||
|
return "DEBUG";
|
||||||
|
case Level::Info:
|
||||||
|
return "INFO ";
|
||||||
|
case Level::Warning:
|
||||||
|
return "WARN ";
|
||||||
|
case Level::Error:
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void logger::setLevel(Level level) {
|
||||||
|
#ifdef NDEBUG
|
||||||
|
if (level == Level::Debug) {
|
||||||
|
LOG_WARNING("Release builds do not support debug log level, defaulting to info");
|
||||||
|
level = Level::Info;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
currentLevel() = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logger::log(Level level, const std::string& message) {
|
||||||
|
if (level < currentLevel())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::ostringstream log;
|
||||||
|
log << "(lsfg-vk) [" << formatLevel(level) << "] " << message << '\n';
|
||||||
|
|
||||||
|
std::cerr << log.str();
|
||||||
|
}
|
||||||
56
lsfg-vk-backend/src/utility/logger.hpp
Normal file
56
lsfg-vk-backend/src/utility/logger.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <sstream> // IWYU pragma: keep
|
||||||
|
#include <string>
|
||||||
|
#include <string_view> // IWYU pragma: keep
|
||||||
|
|
||||||
|
namespace lsfgvk::logger {
|
||||||
|
|
||||||
|
/// Various levels for log messages
|
||||||
|
enum class Level : uint8_t {
|
||||||
|
/// Detailed debugging information
|
||||||
|
Debug,
|
||||||
|
/// General informational messages
|
||||||
|
Info,
|
||||||
|
/// Potentially problematic situations
|
||||||
|
Warning,
|
||||||
|
/// Irrecoverable errors
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set the minimum log level
|
||||||
|
///
|
||||||
|
/// @param level Inclusive minimum log level
|
||||||
|
///
|
||||||
|
void setLevel(Level level);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Log a message
|
||||||
|
///
|
||||||
|
/// @param level Log level
|
||||||
|
/// @param message Log message
|
||||||
|
///
|
||||||
|
void log(Level level, const std::string& message);
|
||||||
|
|
||||||
|
// NOLINTBEGIN (macro parentheses)
|
||||||
|
#define LOG(level, msg) { \
|
||||||
|
std::ostringstream _oss; \
|
||||||
|
_oss << msg; \
|
||||||
|
lsfgvk::logger::log(level, _oss.str()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOG_INFO(msg) LOG(lsfgvk::logger::Level::Info, msg)
|
||||||
|
#define LOG_WARNING(msg) LOG(lsfgvk::logger::Level::Warning, msg)
|
||||||
|
#define LOG_ERROR(msg) LOG(lsfgvk::logger::Level::Error, msg)
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define LOG_DEBUG(msg)
|
||||||
|
#else
|
||||||
|
#define LOG_DEBUG(msg) LOG(lsfgvk::logger::Level::Debug, msg)
|
||||||
|
#endif
|
||||||
|
// NOLINTEND
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue