mirror of
https://github.com/DrBeef/Raze.git
synced 2024-12-02 17:11:51 +00:00
269 lines
7.8 KiB
C++
269 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;
|
||
|
}
|