/*
* Copyright 2025
*
* This file is part of CasparCG (www.casparcg.com).
*
* CasparCG is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CasparCG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CasparCG. If not, see .
*
* Author: Niklas Andersson, niklas@niklaspandersson.se
*/
#include "command_context.h"
#include "vulkan_queue.h"
namespace caspar { namespace accelerator { namespace vulkan {
command_context::command_context(vk::Device device, std::shared_ptr queue)
: device_(device)
, queue_(std::move(queue))
{
vk::CommandPoolCreateInfo pool_info;
pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
pool_info.queueFamilyIndex = queue_->family_index();
pool_ = device_.createCommandPool(pool_info);
vk::SemaphoreTypeCreateInfo timeline_info{};
timeline_info.semaphoreType = vk::SemaphoreType::eTimeline;
timeline_info.initialValue = 0;
vk::SemaphoreCreateInfo semaphore_info{};
semaphore_info.pNext = &timeline_info;
timeline_ = device_.createSemaphore(semaphore_info);
}
command_context::~command_context()
{
inflight_.clear();
device_.destroySemaphore(timeline_);
device_.destroyCommandPool(pool_);
}
vk::CommandBuffer command_context::acquire_command_buffer()
{
if (inflight_.size() > 1) {
auto completed = device_.getSemaphoreCounterValue(timeline_);
// try to reuse the oldest existing command buffer
if (inflight_.front().value <= completed) {
auto cmd = inflight_.front().cmd;
cmd.reset();
inflight_.pop_front();
return cmd;
}
}
vk::CommandBufferAllocateInfo alloc_info{};
alloc_info.commandPool = pool_;
alloc_info.level = vk::CommandBufferLevel::ePrimary;
alloc_info.commandBufferCount = 1;
return device_.allocateCommandBuffers(alloc_info)[0];
}
completion_token command_context::record_and_submit(const std::function& record)
{
std::lock_guard lock(mutex_);
auto cmd = acquire_command_buffer();
cmd.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
record(cmd);
cmd.end();
auto signal_value = ++value_;
vk::TimelineSemaphoreSubmitInfo timeline_submit{};
timeline_submit.setSignalSemaphoreValues(signal_value);
vk::SubmitInfo submit_info{};
submit_info.setCommandBuffers(cmd);
submit_info.setSignalSemaphores(timeline_);
submit_info.pNext = &timeline_submit;
queue_->submit(submit_info);
inflight_.push_back({cmd, signal_value});
return completion_token{timeline_, signal_value};
}
bool command_context::wait(const completion_token& token, uint64_t timeout_ns) const
{
if (!token)
return true;
vk::SemaphoreWaitInfo wait_info{};
wait_info.setSemaphores(token.timeline);
wait_info.setValues(token.value);
return device_.waitSemaphores(wait_info, timeout_ns) == vk::Result::eSuccess;
}
}}} // namespace caspar::accelerator::vulkan