Spaces:
Runtime error
Runtime error
// SPDX-License-Identifier: Apache-2.0 | |
namespace kp { | |
Sequence::Sequence(std::shared_ptr<vk::PhysicalDevice> physicalDevice, | |
std::shared_ptr<vk::Device> device, | |
std::shared_ptr<vk::Queue> computeQueue, | |
uint32_t queueIndex, | |
uint32_t totalTimestamps) | |
{ | |
KP_LOG_DEBUG("Kompute Sequence Constructor with existing device & queue"); | |
this->mPhysicalDevice = physicalDevice; | |
this->mDevice = device; | |
this->mComputeQueue = computeQueue; | |
this->mQueueIndex = queueIndex; | |
this->createCommandPool(); | |
this->createCommandBuffer(); | |
if (totalTimestamps > 0) | |
this->createTimestampQueryPool(totalTimestamps + | |
1); //+1 for the first one | |
} | |
Sequence::~Sequence() | |
{ | |
KP_LOG_DEBUG("Kompute Sequence Destructor started"); | |
if (this->mDevice) { | |
this->destroy(); | |
} | |
} | |
void | |
Sequence::begin() | |
{ | |
KP_LOG_DEBUG("Kompute sequence called BEGIN"); | |
if (this->isRecording()) { | |
KP_LOG_DEBUG("Kompute Sequence begin called when already recording"); | |
return; | |
} | |
if (this->isRunning()) { | |
throw std::runtime_error( | |
"Kompute Sequence begin called when sequence still running"); | |
} | |
KP_LOG_INFO("Kompute Sequence command now started recording"); | |
this->mCommandBuffer->begin(vk::CommandBufferBeginInfo()); | |
this->mRecording = true; | |
// latch the first timestamp before any commands are submitted | |
if (this->timestampQueryPool) | |
this->mCommandBuffer->writeTimestamp( | |
vk::PipelineStageFlagBits::eAllCommands, | |
*this->timestampQueryPool, | |
0); | |
} | |
void | |
Sequence::end() | |
{ | |
KP_LOG_DEBUG("Kompute Sequence calling END"); | |
if (this->isRunning()) { | |
throw std::runtime_error( | |
"Kompute Sequence begin called when sequence still running"); | |
} | |
if (!this->isRecording()) { | |
KP_LOG_WARN("Kompute Sequence end called when not recording"); | |
return; | |
} else { | |
KP_LOG_INFO("Kompute Sequence command recording END"); | |
this->mCommandBuffer->end(); | |
this->mRecording = false; | |
} | |
} | |
void | |
Sequence::clear() | |
{ | |
KP_LOG_DEBUG("Kompute Sequence calling clear"); | |
if (this->isRecording()) { | |
this->end(); | |
} | |
} | |
std::shared_ptr<Sequence> | |
Sequence::eval() | |
{ | |
KP_LOG_DEBUG("Kompute sequence EVAL BEGIN"); | |
return this->evalAsync()->evalAwait(); | |
} | |
std::shared_ptr<Sequence> | |
Sequence::eval(std::shared_ptr<OpBase> op) | |
{ | |
this->clear(); | |
return this->record(op)->eval(); | |
} | |
std::shared_ptr<Sequence> | |
Sequence::evalAsync() | |
{ | |
if (this->isRecording()) { | |
this->end(); | |
} | |
if (this->mIsRunning) { | |
throw std::runtime_error( | |
"Kompute Sequence evalAsync called when an eval async was " | |
"called without successful wait"); | |
} | |
this->mIsRunning = true; | |
for (size_t i = 0; i < this->mOperations.size(); i++) { | |
this->mOperations[i]->preEval(*this->mCommandBuffer); | |
} | |
vk::SubmitInfo submitInfo( | |
0, nullptr, nullptr, 1, this->mCommandBuffer.get()); | |
this->mFence = this->mDevice->createFence(vk::FenceCreateInfo()); | |
KP_LOG_DEBUG( | |
"Kompute sequence submitting command buffer into compute queue"); | |
this->mComputeQueue->submit(1, &submitInfo, this->mFence); | |
return shared_from_this(); | |
} | |
std::shared_ptr<Sequence> | |
Sequence::evalAsync(std::shared_ptr<OpBase> op) | |
{ | |
this->clear(); | |
this->record(op); | |
this->evalAsync(); | |
return shared_from_this(); | |
} | |
std::shared_ptr<Sequence> | |
Sequence::evalAwait(uint64_t waitFor) | |
{ | |
if (!this->mIsRunning) { | |
KP_LOG_WARN("Kompute Sequence evalAwait called without existing eval"); | |
return shared_from_this(); | |
} | |
vk::Result result = | |
this->mDevice->waitForFences(1, &this->mFence, VK_TRUE, waitFor); | |
this->mDevice->destroy( | |
this->mFence, (vk::Optional<const vk::AllocationCallbacks>)nullptr); | |
this->mIsRunning = false; | |
if (result == vk::Result::eTimeout) { | |
KP_LOG_WARN("Kompute Sequence evalAwait reached timeout of {}", | |
waitFor); | |
return shared_from_this(); | |
} | |
for (size_t i = 0; i < this->mOperations.size(); i++) { | |
this->mOperations[i]->postEval(*this->mCommandBuffer); | |
} | |
return shared_from_this(); | |
} | |
bool | |
Sequence::isRunning() const | |
{ | |
return this->mIsRunning; | |
} | |
bool | |
Sequence::isRecording() const | |
{ | |
return this->mRecording; | |
} | |
bool | |
Sequence::isInit() const | |
{ | |
return this->mDevice && this->mCommandPool && this->mCommandBuffer && | |
this->mComputeQueue; | |
} | |
void | |
Sequence::rerecord() | |
{ | |
this->end(); | |
std::vector<std::shared_ptr<OpBase>> ops = this->mOperations; | |
this->mOperations.clear(); | |
for (const std::shared_ptr<kp::OpBase>& op : ops) { | |
this->record(op); | |
} | |
} | |
void | |
Sequence::destroy() | |
{ | |
KP_LOG_DEBUG("Kompute Sequence destroy called"); | |
if (!this->mDevice) { | |
KP_LOG_WARN("Kompute Sequence destroy called " | |
"with null Device pointer"); | |
return; | |
} | |
if (this->mFreeCommandBuffer) { | |
KP_LOG_INFO("Freeing CommandBuffer"); | |
if (!this->mCommandBuffer) { | |
KP_LOG_WARN("Kompute Sequence destroy called with null " | |
"CommandPool pointer"); | |
return; | |
} | |
this->mDevice->freeCommandBuffers( | |
*this->mCommandPool, 1, this->mCommandBuffer.get()); | |
this->mCommandBuffer = nullptr; | |
this->mFreeCommandBuffer = false; | |
KP_LOG_DEBUG("Kompute Sequence Freed CommandBuffer"); | |
} | |
if (this->mFreeCommandPool) { | |
KP_LOG_INFO("Destroying CommandPool"); | |
if (this->mCommandPool == nullptr) { | |
KP_LOG_WARN("Kompute Sequence destroy called with null " | |
"CommandPool pointer"); | |
return; | |
} | |
this->mDevice->destroy( | |
*this->mCommandPool, | |
(vk::Optional<const vk::AllocationCallbacks>)nullptr); | |
this->mCommandPool = nullptr; | |
this->mFreeCommandPool = false; | |
KP_LOG_DEBUG("Kompute Sequence Destroyed CommandPool"); | |
} | |
if (this->mOperations.size()) { | |
KP_LOG_INFO("Kompute Sequence clearing operations buffer"); | |
this->mOperations.clear(); | |
} | |
if (this->timestampQueryPool) { | |
KP_LOG_INFO("Destroying QueryPool"); | |
this->mDevice->destroy( | |
*this->timestampQueryPool, | |
(vk::Optional<const vk::AllocationCallbacks>)nullptr); | |
this->timestampQueryPool = nullptr; | |
KP_LOG_DEBUG("Kompute Sequence Destroyed QueryPool"); | |
} | |
if (this->mDevice) { | |
this->mDevice = nullptr; | |
} | |
if (this->mPhysicalDevice) { | |
this->mPhysicalDevice = nullptr; | |
} | |
if (this->mComputeQueue) { | |
this->mComputeQueue = nullptr; | |
} | |
} | |
std::shared_ptr<Sequence> | |
Sequence::record(std::shared_ptr<OpBase> op) | |
{ | |
KP_LOG_DEBUG("Kompute Sequence record function started"); | |
this->begin(); | |
KP_LOG_DEBUG( | |
"Kompute Sequence running record on OpBase derived class instance"); | |
op->record(*this->mCommandBuffer); | |
this->mOperations.push_back(op); | |
if (this->timestampQueryPool) | |
this->mCommandBuffer->writeTimestamp( | |
vk::PipelineStageFlagBits::eAllCommands, | |
*this->timestampQueryPool, | |
this->mOperations.size()); | |
return shared_from_this(); | |
} | |
void | |
Sequence::createCommandPool() | |
{ | |
KP_LOG_DEBUG("Kompute Sequence creating command pool"); | |
if (!this->mDevice) { | |
throw std::runtime_error("Kompute Sequence device is null"); | |
} | |
this->mFreeCommandPool = true; | |
vk::CommandPoolCreateInfo commandPoolInfo(vk::CommandPoolCreateFlags(), | |
this->mQueueIndex); | |
this->mCommandPool = std::make_shared<vk::CommandPool>(); | |
this->mDevice->createCommandPool( | |
&commandPoolInfo, nullptr, this->mCommandPool.get()); | |
KP_LOG_DEBUG("Kompute Sequence Command Pool Created"); | |
} | |
void | |
Sequence::createCommandBuffer() | |
{ | |
KP_LOG_DEBUG("Kompute Sequence creating command buffer"); | |
if (!this->mDevice) { | |
throw std::runtime_error("Kompute Sequence device is null"); | |
} | |
if (!this->mCommandPool) { | |
throw std::runtime_error("Kompute Sequence command pool is null"); | |
} | |
this->mFreeCommandBuffer = true; | |
vk::CommandBufferAllocateInfo commandBufferAllocateInfo( | |
*this->mCommandPool, vk::CommandBufferLevel::ePrimary, 1); | |
this->mCommandBuffer = std::make_shared<vk::CommandBuffer>(); | |
this->mDevice->allocateCommandBuffers(&commandBufferAllocateInfo, | |
this->mCommandBuffer.get()); | |
KP_LOG_DEBUG("Kompute Sequence Command Buffer Created"); | |
} | |
void | |
Sequence::createTimestampQueryPool(uint32_t totalTimestamps) | |
{ | |
KP_LOG_DEBUG("Kompute Sequence creating query pool"); | |
if (!this->isInit()) { | |
throw std::runtime_error( | |
"createTimestampQueryPool() called on uninitialized Sequence"); | |
} | |
if (!this->mPhysicalDevice) { | |
throw std::runtime_error("Kompute Sequence physical device is null"); | |
} | |
vk::PhysicalDeviceProperties physicalDeviceProperties = | |
this->mPhysicalDevice->getProperties(); | |
if (physicalDeviceProperties.limits.timestampComputeAndGraphics) { | |
vk::QueryPoolCreateInfo queryPoolInfo; | |
queryPoolInfo.setQueryCount(totalTimestamps); | |
queryPoolInfo.setQueryType(vk::QueryType::eTimestamp); | |
this->timestampQueryPool = std::make_shared<vk::QueryPool>( | |
this->mDevice->createQueryPool(queryPoolInfo)); | |
KP_LOG_DEBUG("Query pool for timestamps created"); | |
} else { | |
throw std::runtime_error("Device does not support timestamps"); | |
} | |
} | |
std::vector<std::uint64_t> | |
Sequence::getTimestamps() | |
{ | |
if (!this->timestampQueryPool) | |
throw std::runtime_error("Timestamp latching not enabled"); | |
const auto n = this->mOperations.size() + 1; | |
std::vector<std::uint64_t> timestamps(n, 0); | |
this->mDevice->getQueryPoolResults( | |
*this->timestampQueryPool, | |
0, | |
n, | |
timestamps.size() * sizeof(std::uint64_t), | |
timestamps.data(), | |
sizeof(uint64_t), | |
vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait); | |
return timestamps; | |
} | |
} | |