mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-05-10 11:11:40 +00:00
feat(bindless): Implement backend coordinating class
This commit is contained in:
parent
f8097eddb9
commit
9da7c8fdf9
4 changed files with 368 additions and 1 deletions
|
|
@ -1,7 +1,231 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#include "lsfg-vk/lsfgvk.hpp"
|
||||
#include "lsfgvk.hpp"
|
||||
#include "modules/library.hpp"
|
||||
#include "modules/pipeline.hpp"
|
||||
#include "utility/pipelines.hpp"
|
||||
#include "utility/vkhelper.hpp"
|
||||
#include "vulkan/vulkan.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
using namespace lsfgvk;
|
||||
|
||||
/* TODO */
|
||||
Instance::Instance(
|
||||
const std::string& deviceId,
|
||||
const std::filesystem::path& lsfgvkDllPath,
|
||||
bool allowFP16
|
||||
) {
|
||||
// Create Vulkan context
|
||||
vk::detail::DispatchLoaderDynamic dld{};
|
||||
|
||||
auto instance{vkhelper::createInstance(dld)};
|
||||
auto physdev{vkhelper::findPhysicalDevice(dld, *instance, deviceId)};
|
||||
|
||||
const uint32_t qfi{vkhelper::findComputeQueueFamilyIndex(dld, physdev)};
|
||||
const bool fp16{allowFP16 && vkhelper::checkHalfPrecisionSupport(dld, physdev)};
|
||||
|
||||
auto [device, queue] = vkhelper::createDevice(dld, physdev, qfi, fp16);
|
||||
|
||||
// Construct instance
|
||||
library::ShaderLibrary library{
|
||||
dld,
|
||||
*device,
|
||||
fp16,
|
||||
lsfgvkDllPath
|
||||
};
|
||||
|
||||
this->m_priv = std::make_unique<priv::Instance>(priv::Instance {
|
||||
.vk = {
|
||||
.dld = dld,
|
||||
.instance = std::move(instance),
|
||||
.physdev = physdev,
|
||||
.device = std::move(device),
|
||||
.queue = queue,
|
||||
.qfi = qfi,
|
||||
.fp16 = fp16
|
||||
},
|
||||
.shaderLibrary = std::move(library)
|
||||
});
|
||||
}
|
||||
|
||||
Context::Context(
|
||||
const Instance& instance,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
float flowScale,
|
||||
bool performanceMode
|
||||
) {
|
||||
const auto& vk{instance.m_priv->vk};
|
||||
|
||||
pipeline::Pipeline pipeline{
|
||||
vk.dld,
|
||||
*vk.device,
|
||||
vk.physdev,
|
||||
vk.queue,
|
||||
vk.qfi,
|
||||
instance.m_priv->shaderLibrary,
|
||||
lsfgvk::getPipelineSignature(performanceMode),
|
||||
{ width, height },
|
||||
flowScale,
|
||||
performanceMode,
|
||||
false /* TODO: HDR */
|
||||
};
|
||||
|
||||
this->m_priv = std::make_unique<priv::Context>(priv::Context {
|
||||
.instance = std::ref(*instance.m_priv),
|
||||
.pipeline = std::move(pipeline),
|
||||
.syncSemaphore = { vkhelper::createTimelineSemaphore(vk.dld, *vk.device, true), 0 },
|
||||
.internalSemaphores = { vkhelper::createTimelineSemaphore(vk.dld, *vk.device), 0 },
|
||||
.fence = vkhelper::createFence(vk.dld, *vk.device),
|
||||
});
|
||||
}
|
||||
|
||||
FileDescriptors Context::exportFds() const {
|
||||
const auto& vk{this->m_priv->instance.get().vk};
|
||||
const auto& pipeline{this->m_priv->pipeline};
|
||||
|
||||
return{
|
||||
.sourceFd = vkhelper::exportMemoryFd(
|
||||
vk.dld, *vk.device,
|
||||
pipeline.getExternalInputs().front().memory
|
||||
),
|
||||
.destinationFd = vkhelper::exportMemoryFd(
|
||||
vk.dld, *vk.device,
|
||||
pipeline.getExternalOutputs().front().memory
|
||||
),
|
||||
.syncFd = vkhelper::exportSemaphoreFd(
|
||||
vk.dld, *vk.device,
|
||||
*this->m_priv->syncSemaphore.first
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
void Context::dispatch(uint32_t total) {
|
||||
auto& ctx{*this->m_priv};
|
||||
const auto& vk{ctx.instance.get().vk};
|
||||
|
||||
// Increment iteration counter after previous frame is completed
|
||||
auto* mapped{ctx.pipeline.getMappedBuffer()};
|
||||
if (ctx.firstIteration) {
|
||||
ctx.firstIteration = false;
|
||||
mapped->iteration = 0;
|
||||
} else {
|
||||
if (vk.device->waitForFences(*ctx.fence, true, UINT64_MAX, vk.dld) != vk::Result::eSuccess)
|
||||
throw std::runtime_error("Unable to wait for completion of previous iteration");
|
||||
vk.device->resetFences(*ctx.fence, vk.dld);
|
||||
mapped->iteration++;
|
||||
}
|
||||
|
||||
const auto& cmdbufs{ctx.pipeline.getCmdbufs()};
|
||||
|
||||
// Dispatch pre-pass
|
||||
auto& sync{ctx.syncSemaphore};
|
||||
sync.second++;
|
||||
|
||||
auto& internal{ctx.internalSemaphores};
|
||||
internal.second++;
|
||||
|
||||
vk::TimelineSemaphoreSubmitInfo timelineInfo{
|
||||
.waitSemaphoreValueCount = 1,
|
||||
.pWaitSemaphoreValues = &sync.second,
|
||||
.signalSemaphoreValueCount = 1,
|
||||
.pSignalSemaphoreValues = &internal.second
|
||||
};
|
||||
|
||||
const vk::PipelineStageFlags waitStage{vk::PipelineStageFlagBits::eTopOfPipe};
|
||||
vk.queue.submit(
|
||||
{{
|
||||
.pNext = &timelineInfo,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &*sync.first,
|
||||
.pWaitDstStageMask = &waitStage,
|
||||
.commandBufferCount = 1U,
|
||||
.pCommandBuffers = &*cmdbufs.at(0),
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = &*internal.first
|
||||
}},
|
||||
nullptr,
|
||||
vk.dld
|
||||
);
|
||||
|
||||
// Dispatch main passes
|
||||
uint64_t prevInternal{};
|
||||
for (uint32_t i = 0; i < total; i++) {
|
||||
const auto& transCmdbuf{ctx.pipeline.getCmdbufs().at(0)}; // FIXME: replace with actual buf
|
||||
|
||||
// Transition command buffer to next timestamp
|
||||
if (i == 0) {
|
||||
prevInternal = internal.second;
|
||||
timelineInfo.pWaitSemaphoreValues = &prevInternal;
|
||||
} else {
|
||||
sync.second++;
|
||||
timelineInfo.pWaitSemaphoreValues = &sync.second;
|
||||
}
|
||||
|
||||
internal.second++;
|
||||
timelineInfo.pSignalSemaphoreValues = &internal.second;
|
||||
|
||||
vk.queue.submit(
|
||||
{{
|
||||
.pNext = &timelineInfo,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = i == 0 ? &*internal.first : &*sync.first,
|
||||
.pWaitDstStageMask = &waitStage,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &*transCmdbuf,
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = &*internal.first
|
||||
}},
|
||||
nullptr,
|
||||
vk.dld
|
||||
);
|
||||
|
||||
// Dispatch main pass
|
||||
timelineInfo.pWaitSemaphoreValues = &internal.second;
|
||||
|
||||
sync.second++;
|
||||
timelineInfo.pSignalSemaphoreValues = &sync.second;
|
||||
|
||||
vk.queue.submit(
|
||||
{{
|
||||
.pNext = &timelineInfo,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &*internal.first,
|
||||
.pWaitDstStageMask = &waitStage,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &*cmdbufs.at(1),
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = &*sync.first
|
||||
}},
|
||||
i == (total - 1) ? *ctx.fence : nullptr,
|
||||
vk.dld
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::idle() const {
|
||||
const auto& ctx{*this->m_priv};
|
||||
const auto& vk{ctx.instance.get().vk};
|
||||
|
||||
vk.device->waitIdle(vk.dld);
|
||||
}
|
||||
|
||||
Context::~Context() {
|
||||
try {
|
||||
// NOTE: This will freeze if the user didn't signal the sync semaphore high enough to
|
||||
// allow the pipeline to complete.
|
||||
this->idle();
|
||||
} catch (...) { // NOLINT (empty catch)
|
||||
// Not much we can do here..
|
||||
}
|
||||
}
|
||||
|
||||
Instance::~Instance() = default;
|
||||
|
|
|
|||
56
lsfg-vk-backend/src/lsfgvk.hpp
Normal file
56
lsfg-vk-backend/src/lsfgvk.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "modules/pipeline.hpp"
|
||||
#include "modules/library.hpp"
|
||||
#include "utility/vkhelper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace lsfgvk::priv {
|
||||
|
||||
|
||||
/// Internal state of lsfg-vk
|
||||
struct Instance {
|
||||
/// Vulkan context
|
||||
struct Vulkan {
|
||||
/// Vulkan dispatch loader
|
||||
vk::detail::DispatchLoaderDynamic dld;
|
||||
/// Vulkan instance (1.2)
|
||||
vk::UniqueInstance instance;
|
||||
/// Vulkan physical device
|
||||
vk::PhysicalDevice physdev;
|
||||
/// Vulkan device with synchronization2 (extension), external memory & semaphore
|
||||
/// fd (extension) and timeline semaphores (core) enabled
|
||||
vk::UniqueDevice device;
|
||||
/// Compute queue
|
||||
vk::Queue queue;
|
||||
/// Compute queue family index
|
||||
uint32_t qfi;
|
||||
/// Whether fp16 is enabled and supported (shaderFloat16 is enabled)
|
||||
bool fp16;
|
||||
} vk;
|
||||
/// Shader library
|
||||
library::ShaderLibrary shaderLibrary;
|
||||
};
|
||||
|
||||
/// Internal context for frame generation
|
||||
struct Context {
|
||||
/// Parent instance
|
||||
std::reference_wrapper<Instance> instance;
|
||||
/// Pipeline instance
|
||||
pipeline::Pipeline pipeline;
|
||||
/// Shared synchronization semaphores
|
||||
std::pair<vk::UniqueSemaphore, uint64_t> syncSemaphore;
|
||||
/// Internal synchronization semaphores
|
||||
std::pair<vk::UniqueSemaphore, uint64_t> internalSemaphores;
|
||||
/// Frames-in-flight fence
|
||||
vk::UniqueFence fence;
|
||||
/// Is first iteration
|
||||
bool firstIteration{true};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -470,6 +470,24 @@ vk::UniqueCommandBuffer vkhelper::createCommandBuffer(
|
|||
return { std::move(device.allocateCommandBuffersUnique(cmdbufInfo, dld).front()) };
|
||||
}
|
||||
|
||||
vk::UniqueSemaphore vkhelper::createTimelineSemaphore(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
bool exportable
|
||||
) {
|
||||
const vk::ExportSemaphoreCreateInfo exportInfo{
|
||||
.handleTypes = vk::ExternalSemaphoreHandleTypeFlagBits::eOpaqueFd
|
||||
};
|
||||
const vk::SemaphoreTypeCreateInfo typeInfo{
|
||||
.pNext = exportable ? &exportInfo : nullptr,
|
||||
.semaphoreType = vk::SemaphoreType::eTimeline,
|
||||
};
|
||||
const vk::SemaphoreCreateInfo createInfo{
|
||||
.pNext = &typeInfo,
|
||||
};
|
||||
return device.createSemaphoreUnique(createInfo, nullptr, dld);
|
||||
}
|
||||
|
||||
vk::UniqueFence vkhelper::createFence(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device
|
||||
|
|
@ -549,3 +567,27 @@ std::pair<vk::UniqueImage, vk::UniqueDeviceMemory> vkhelper::createExternalImage
|
|||
std::move(memory)
|
||||
};
|
||||
}
|
||||
|
||||
int vkhelper::exportMemoryFd(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::DeviceMemory& memory
|
||||
) {
|
||||
const vk::MemoryGetFdInfoKHR fdInfo{
|
||||
.memory = memory,
|
||||
.handleType = vk::ExternalMemoryHandleTypeFlagBits::eOpaqueFd
|
||||
};
|
||||
return device.getMemoryFdKHR(fdInfo, dld);
|
||||
}
|
||||
|
||||
int vkhelper::exportSemaphoreFd(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::Semaphore& semaphore
|
||||
) {
|
||||
const vk::SemaphoreGetFdInfoKHR fdInfo{
|
||||
.semaphore = semaphore,
|
||||
.handleType = vk::ExternalSemaphoreHandleTypeFlagBits::eOpaqueFd
|
||||
};
|
||||
return device.getSemaphoreFdKHR(fdInfo, dld);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -338,6 +338,21 @@ namespace vkhelper {
|
|||
const vk::CommandPool& cmdpool
|
||||
);
|
||||
|
||||
///
|
||||
/// Create a timeline semaphore
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param exportable Whether the semaphore should be exportable as a fd
|
||||
/// @return RAII-wrapped Vulkan semaphore
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
vk::UniqueSemaphore createTimelineSemaphore(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
bool exportable = false
|
||||
);
|
||||
|
||||
///
|
||||
/// Create a fence
|
||||
///
|
||||
|
|
@ -376,4 +391,34 @@ namespace vkhelper {
|
|||
vk::ImageUsageFlags usage
|
||||
);
|
||||
|
||||
///
|
||||
/// Export a Vulkan memory allocation as a fd
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param memory Vulkan device memory
|
||||
/// @return File descriptor to the allocation
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
int exportMemoryFd(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::DeviceMemory& memory
|
||||
);
|
||||
|
||||
///
|
||||
/// Export a Vulkan semaphore as a fd
|
||||
///
|
||||
/// @param dld Dynamic dispatch loader
|
||||
/// @param device Vulkan device
|
||||
/// @param semaphore Vulkan semaphore
|
||||
/// @return File descriptor to the semaphore
|
||||
/// @throws std::runtime_error on failure
|
||||
///
|
||||
int exportSemaphoreFd(
|
||||
const vk::detail::DispatchLoaderDynamic& dld,
|
||||
const vk::Device& device,
|
||||
const vk::Semaphore& semaphore
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue