raze/source/common/rendering/vulkan/system/vk_commandbuffer.cpp
2022-07-02 10:20:11 +02:00

268 lines
7.8 KiB
C++

/*
** Vulkan backend
** Copyright (c) 2016-2020 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include "vk_commandbuffer.h"
#include "vk_framebuffer.h"
#include "vk_swapchain.h"
#include "vk_builders.h"
#include "vulkan/renderer/vk_renderstate.h"
#include "vulkan/renderer/vk_postprocess.h"
#include "hw_clock.h"
#include "v_video.h"
extern int rendered_commandbuffers;
int current_rendered_commandbuffers;
extern bool gpuStatActive;
extern bool keepGpuStatActive;
extern FString gpuStatOutput;
VkCommandBufferManager::VkCommandBufferManager(VulkanFrameBuffer* fb) : fb(fb)
{
mCommandPool.reset(new VulkanCommandPool(fb->device, fb->device->graphicsFamily));
swapChain = std::make_unique<VulkanSwapChain>(fb->device);
mSwapChainImageAvailableSemaphore.reset(new VulkanSemaphore(fb->device));
mRenderFinishedSemaphore.reset(new VulkanSemaphore(fb->device));
for (auto& semaphore : mSubmitSemaphore)
semaphore.reset(new VulkanSemaphore(fb->device));
for (auto& fence : mSubmitFence)
fence.reset(new VulkanFence(fb->device));
for (int i = 0; i < maxConcurrentSubmitCount; i++)
mSubmitWaitFences[i] = mSubmitFence[i]->fence;
if (fb->device->graphicsTimeQueries)
{
mTimestampQueryPool = QueryPoolBuilder()
.QueryType(VK_QUERY_TYPE_TIMESTAMP, MaxTimestampQueries)
.Create(fb->device);
GetDrawCommands()->resetQueryPool(mTimestampQueryPool.get(), 0, MaxTimestampQueries);
}
}
VkCommandBufferManager::~VkCommandBufferManager()
{
}
VulkanCommandBuffer* VkCommandBufferManager::GetTransferCommands()
{
if (!mTransferCommands)
{
mTransferCommands = mCommandPool->createBuffer();
mTransferCommands->SetDebugName("VulkanFrameBuffer.mTransferCommands");
mTransferCommands->begin();
}
return mTransferCommands.get();
}
VulkanCommandBuffer* VkCommandBufferManager::GetDrawCommands()
{
if (!mDrawCommands)
{
mDrawCommands = mCommandPool->createBuffer();
mDrawCommands->SetDebugName("VulkanFrameBuffer.mDrawCommands");
mDrawCommands->begin();
}
return mDrawCommands.get();
}
void VkCommandBufferManager::BeginFrame()
{
if (mNextTimestampQuery > 0)
{
GetDrawCommands()->resetQueryPool(mTimestampQueryPool.get(), 0, mNextTimestampQuery);
mNextTimestampQuery = 0;
}
}
void VkCommandBufferManager::FlushCommands(VulkanCommandBuffer** commands, size_t count, bool finish, bool lastsubmit)
{
int currentIndex = mNextSubmit % maxConcurrentSubmitCount;
if (mNextSubmit >= maxConcurrentSubmitCount)
{
vkWaitForFences(fb->device->device, 1, &mSubmitFence[currentIndex]->fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
vkResetFences(fb->device->device, 1, &mSubmitFence[currentIndex]->fence);
}
QueueSubmit submit;
for (size_t i = 0; i < count; i++)
submit.AddCommandBuffer(commands[i]);
if (mNextSubmit > 0)
submit.AddWait(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mSubmitSemaphore[(mNextSubmit - 1) % maxConcurrentSubmitCount].get());
if (finish && presentImageIndex != 0xffffffff)
{
submit.AddWait(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mSwapChainImageAvailableSemaphore.get());
submit.AddSignal(mRenderFinishedSemaphore.get());
}
if (!lastsubmit)
submit.AddSignal(mSubmitSemaphore[currentIndex].get());
submit.Execute(fb->device, fb->device->graphicsQueue, mSubmitFence[currentIndex].get());
mNextSubmit++;
}
void VkCommandBufferManager::FlushCommands(bool finish, bool lastsubmit, bool uploadOnly)
{
if (!uploadOnly)
fb->GetRenderState()->EndRenderPass();
if ((!uploadOnly && mDrawCommands) || mTransferCommands)
{
VulkanCommandBuffer* commands[2];
size_t count = 0;
if (mTransferCommands)
{
mTransferCommands->end();
commands[count++] = mTransferCommands.get();
TransferDeleteList->Add(std::move(mTransferCommands));
}
if (!uploadOnly && mDrawCommands)
{
mDrawCommands->end();
commands[count++] = mDrawCommands.get();
DrawDeleteList->Add(std::move(mDrawCommands));
}
FlushCommands(commands, count, finish, lastsubmit);
current_rendered_commandbuffers += (int)count;
}
}
void VkCommandBufferManager::WaitForCommands(bool finish, bool uploadOnly)
{
if (finish)
{
Finish.Reset();
Finish.Clock();
presentImageIndex = swapChain->AcquireImage(fb->GetClientWidth(), fb->GetClientHeight(), fb->GetVSync(), mSwapChainImageAvailableSemaphore.get());
if (presentImageIndex != 0xffffffff)
fb->GetPostprocess()->DrawPresentTexture(fb->mOutputLetterbox, true, false);
}
FlushCommands(finish, true, uploadOnly);
if (finish)
{
fb->FPSLimit();
if (presentImageIndex != 0xffffffff)
swapChain->QueuePresent(presentImageIndex, mRenderFinishedSemaphore.get());
}
int numWaitFences = min(mNextSubmit, (int)maxConcurrentSubmitCount);
if (numWaitFences > 0)
{
vkWaitForFences(fb->device->device, numWaitFences, mSubmitWaitFences, VK_TRUE, std::numeric_limits<uint64_t>::max());
vkResetFences(fb->device->device, numWaitFences, mSubmitWaitFences);
}
DeleteFrameObjects(uploadOnly);
mNextSubmit = 0;
if (finish)
{
Finish.Unclock();
rendered_commandbuffers = current_rendered_commandbuffers;
current_rendered_commandbuffers = 0;
}
}
void VkCommandBufferManager::DeleteFrameObjects(bool uploadOnly)
{
TransferDeleteList = std::make_unique<DeleteList>();
if (!uploadOnly)
DrawDeleteList = std::make_unique<DeleteList>();
}
void VkCommandBufferManager::PushGroup(const FString& name)
{
if (!gpuStatActive)
return;
if (mNextTimestampQuery < MaxTimestampQueries && fb->device->graphicsTimeQueries)
{
TimestampQuery q;
q.name = name;
q.startIndex = mNextTimestampQuery++;
q.endIndex = 0;
GetDrawCommands()->writeTimestamp(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mTimestampQueryPool.get(), q.startIndex);
mGroupStack.push_back(timeElapsedQueries.size());
timeElapsedQueries.push_back(q);
}
}
void VkCommandBufferManager::PopGroup()
{
if (!gpuStatActive || mGroupStack.empty())
return;
TimestampQuery& q = timeElapsedQueries[mGroupStack.back()];
mGroupStack.pop_back();
if (mNextTimestampQuery < MaxTimestampQueries && fb->device->graphicsTimeQueries)
{
q.endIndex = mNextTimestampQuery++;
GetDrawCommands()->writeTimestamp(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mTimestampQueryPool.get(), q.endIndex);
}
}
void VkCommandBufferManager::UpdateGpuStats()
{
uint64_t timestamps[MaxTimestampQueries];
if (mNextTimestampQuery > 0)
mTimestampQueryPool->getResults(0, mNextTimestampQuery, sizeof(uint64_t) * mNextTimestampQuery, timestamps, sizeof(uint64_t), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
double timestampPeriod = fb->device->PhysicalDevice.Properties.limits.timestampPeriod;
gpuStatOutput = "";
for (auto& q : timeElapsedQueries)
{
if (q.endIndex <= q.startIndex)
continue;
int64_t timeElapsed = max(static_cast<int64_t>(timestamps[q.endIndex] - timestamps[q.startIndex]), (int64_t)0);
double timeNS = timeElapsed * timestampPeriod;
FString out;
out.Format("%s=%04.2f ms\n", q.name.GetChars(), timeNS / 1000000.0f);
gpuStatOutput += out;
}
timeElapsedQueries.clear();
mGroupStack.clear();
gpuStatActive = keepGpuStatActive;
keepGpuStatActive = false;
}