/* Copyright 2023 flyinghead This file is part of Flycast. Flycast 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 2 of the License, or (at your option) any later version. Flycast 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 Flycast. If not, see . */ #include "commandpool.h" #include "vulkan_context.h" void CommandPool::Init(size_t chainSize) { this->chainSize = chainSize; device = VulkanContext::Instance()->GetDevice(); if (commandPools.size() > chainSize) { commandPools.resize(chainSize); fences.resize(chainSize); } else { while (commandPools.size() < chainSize) { commandPools.push_back(device.createCommandPoolUnique( vk::CommandPoolCreateInfo(vk::CommandPoolCreateFlagBits::eTransient, VulkanContext::Instance()->GetGraphicsQueueFamilyIndex()))); fences.push_back(device.createFenceUnique(vk::FenceCreateInfo(vk::FenceCreateFlagBits::eSignaled))); } } freeBuffers.resize(chainSize); inFlightBuffers.resize(chainSize); inFlightObjects.resize(chainSize); } void CommandPool::Term() { if (!fences.empty()) { std::vector allFences = vk::uniqueToRaw(fences); vk::Result res = device.waitForFences(allFences, true, UINT64_MAX); if (res != vk::Result::eSuccess) WARN_LOG(RENDERER, "CommandPool::Term: waitForFences failed %d", (int)res); } inFlightObjects.clear(); freeBuffers.clear(); inFlightBuffers.clear(); fences.clear(); commandPools.clear(); } void CommandPool::BeginFrame() { if (frameStarted) return; frameStarted = true; index = (index + 1) % chainSize; vk::Result res = device.waitForFences(fences[index].get(), true, UINT64_MAX); if (res != vk::Result::eSuccess) WARN_LOG(RENDERER, "CommandPool::BeginFrame: waitForFences failed %d", (int)res); std::vector& inFlightBuf = inFlightBuffers[index]; std::vector& freeBuf = freeBuffers[index]; std::move(inFlightBuf.begin(), inFlightBuf.end(), std::back_inserter(freeBuf)); inFlightBuf.clear(); device.resetCommandPool(*commandPools[index], vk::CommandPoolResetFlagBits::eReleaseResources); inFlightObjects[index].clear(); lastBuffers.clear(); } void CommandPool::EndFrame() { if (!frameStarted) return; frameStarted = false; std::vector commandBuffers = vk::uniqueToRaw(inFlightBuffers[index]); if (!commandBuffers.empty()) { // sort buffers: !last, last size_t len = commandBuffers.size() - 1; while (len != 0) { for (size_t i = 0; i < len; i++) if (lastBuffers[i] && !lastBuffers[i + 1]) { std::vector::swap(lastBuffers[i], lastBuffers[i + 1]); std::swap(commandBuffers[i], commandBuffers[i + 1]); } len--; } } device.resetFences(fences[index].get()); VulkanContext::Instance()->SubmitCommandBuffers(commandBuffers, *fences[index]); } vk::CommandBuffer CommandPool::Allocate(bool submitLast) { if (freeBuffers[index].empty()) { inFlightBuffers[index].emplace_back(std::move( device.allocateCommandBuffersUnique(vk::CommandBufferAllocateInfo(*commandPools[index], vk::CommandBufferLevel::ePrimary, 1)) .front())); } else { inFlightBuffers[index].emplace_back(std::move(freeBuffers[index].back())); freeBuffers[index].pop_back(); } lastBuffers.push_back(submitLast); return *inFlightBuffers[index].back(); } void CommandPool::EndFrameAndWait() { EndFrame(); vk::Result res = device.waitForFences(fences[index].get(), true, UINT64_MAX); if (res != vk::Result::eSuccess) WARN_LOG(RENDERER, "CommandPool::waitForCommandCompletion: waitForFences failed %d", (int)res); inFlightObjects[index].clear(); }