Add support for submitting command buffers from worker threads

This commit is contained in:
Magnus Norddahl 2023-05-17 22:02:27 +02:00
commit 83254204ff
5 changed files with 85 additions and 30 deletions

View file

@ -731,6 +731,9 @@ public:
// API-dependent render interface
// Worker threads
virtual void FlushCommands() { }
// Vertices
virtual std::pair<FFlatVertex*, unsigned int> AllocVertices(unsigned int count) = 0;
virtual void SetShadowData(const TArray<FFlatVertex>& vertices, const TArray<uint32_t>& indexes) = 0;

View file

@ -71,24 +71,43 @@ VulkanCommandBuffer* VkCommandBufferManager::GetTransferCommands()
{
if (!mTransferCommands)
{
std::unique_lock<std::mutex> lock(mMutex);
mTransferCommands = mCommandPool->createBuffer();
mTransferCommands->SetDebugName("VulkanRenderDevice.mTransferCommands");
mTransferCommands->SetDebugName("TransferCommands");
mTransferCommands->begin();
}
return mTransferCommands.get();
}
VulkanCommandBuffer* VkCommandBufferManager::GetDrawCommands(int threadIndex)
VulkanCommandBuffer* VkCommandBufferManager::GetDrawCommands()
{
if (!mDrawCommands)
{
std::unique_lock<std::mutex> lock(mMutex);
mDrawCommands = mCommandPool->createBuffer();
mDrawCommands->SetDebugName("VulkanRenderDevice.mDrawCommands");
mDrawCommands->SetDebugName("DrawCommands");
mDrawCommands->begin();
}
return mDrawCommands.get();
}
std::unique_ptr<VulkanCommandBuffer> VkCommandBufferManager::BeginThreadCommands()
{
std::unique_lock<std::mutex> lock(mMutex);
auto commands = mCommandPool->createBuffer();
commands->SetDebugName("ThreadCommands");
lock.unlock();
commands->begin();
return commands;
}
void VkCommandBufferManager::EndThreadCommands(std::unique_ptr<VulkanCommandBuffer> commands)
{
commands->end();
std::unique_lock<std::mutex> lock(mMutex);
mThreadCommands.push_back(std::move(commands));
}
void VkCommandBufferManager::BeginFrame()
{
if (mNextTimestampQuery > 0)
@ -134,28 +153,37 @@ void VkCommandBufferManager::FlushCommands(bool finish, bool lastsubmit, bool up
if (!uploadOnly)
fb->GetRenderState()->EndRenderPass();
if ((!uploadOnly && mDrawCommands) || mTransferCommands)
std::unique_lock<std::mutex> lock(mMutex);
if (mTransferCommands)
{
VulkanCommandBuffer* commands[2];
size_t count = 0;
mTransferCommands->end();
mFlushCommands.push_back(mTransferCommands.get());
TransferDeleteList->Add(std::move(mTransferCommands));
}
if (mTransferCommands)
{
mTransferCommands->end();
commands[count++] = mTransferCommands.get();
TransferDeleteList->Add(std::move(mTransferCommands));
}
if (!uploadOnly && mDrawCommands)
if (!uploadOnly)
{
if (mDrawCommands)
{
mDrawCommands->end();
commands[count++] = mDrawCommands.get();
mFlushCommands.push_back(mDrawCommands.get());
DrawDeleteList->Add(std::move(mDrawCommands));
}
FlushCommands(commands, count, finish, lastsubmit);
for (auto& buffer : mThreadCommands)
{
mFlushCommands.push_back(buffer.get());
DrawDeleteList->Add(std::move(buffer));
}
mThreadCommands.clear();
}
current_rendered_commandbuffers += (int)count;
if (!mFlushCommands.empty())
{
FlushCommands(mFlushCommands.data(), mFlushCommands.size(), finish, lastsubmit);
current_rendered_commandbuffers += (int)mFlushCommands.size();
mFlushCommands.clear();
}
}

View file

@ -15,7 +15,10 @@ public:
void BeginFrame();
VulkanCommandBuffer* GetTransferCommands();
VulkanCommandBuffer* GetDrawCommands(int threadIndex = 0);
VulkanCommandBuffer* GetDrawCommands();
std::unique_ptr<VulkanCommandBuffer> BeginThreadCommands();
void EndThreadCommands(std::unique_ptr<VulkanCommandBuffer> commands);
void FlushCommands(bool finish, bool lastsubmit = false, bool uploadOnly = false);
@ -67,6 +70,7 @@ private:
std::unique_ptr<VulkanCommandBuffer> mTransferCommands;
std::unique_ptr<VulkanCommandBuffer> mDrawCommands;
std::vector<std::unique_ptr<VulkanCommandBuffer>> mThreadCommands;
enum { maxConcurrentSubmitCount = 8 };
std::unique_ptr<VulkanSemaphore> mSubmitSemaphore[maxConcurrentSubmitCount];
@ -86,4 +90,6 @@ private:
int mNextTimestampQuery = 0;
std::vector<size_t> mGroupStack;
std::vector<TimestampQuery> timeElapsedQueries;
std::mutex mMutex;
std::vector<VulkanCommandBuffer*> mFlushCommands;
};

View file

@ -188,7 +188,7 @@ void VkRenderState::Apply(int dt)
drawcalls.Clock();
mApplyCount++;
if (mApplyCount >= vk_submit_size)
if (threadIndex == 0 && mApplyCount >= vk_submit_size)
{
fb->GetCommands()->FlushCommands(false);
mApplyCount = 0;
@ -310,7 +310,16 @@ void VkRenderState::ApplyRenderPass(int dt)
if (!inRenderPass)
{
mCommandBuffer = fb->GetCommands()->GetDrawCommands(threadIndex);
if (threadIndex == 0)
{
mCommandBuffer = fb->GetCommands()->GetDrawCommands();
}
else
{
mThreadCommandBuffer = fb->GetCommands()->BeginThreadCommands();
mCommandBuffer = mThreadCommandBuffer.get();
}
mScissorChanged = true;
mViewportChanged = true;
mStencilRefChanged = true;
@ -711,13 +720,18 @@ void VkRenderState::EndRenderPass()
{
mCommandBuffer->endRenderPass();
mCommandBuffer = nullptr;
mPipelineKey = {};
// Force rebind of all buffers
mLastViewpointOffset = 0xffffffff;
mLastVertexOffsets[0] = 0xffffffff;
mIndexBufferNeedsBind = true;
}
if (mThreadCommandBuffer)
{
fb->GetCommands()->EndThreadCommands(std::move(mThreadCommandBuffer));
}
// Force rebind of everything on next draw
mPipelineKey = {};
mLastViewpointOffset = 0xffffffff;
mLastVertexOffsets[0] = 0xffffffff;
mIndexBufferNeedsBind = true;
}
void VkRenderState::EndFrame()

View file

@ -42,11 +42,6 @@ public:
void EnableLineSmooth(bool on) override;
void EnableDrawBuffers(int count, bool apply) override;
void BeginFrame();
void SetRenderTarget(VkTextureImage *image, VulkanImageView *depthStencilView, int width, int height, VkFormat Format, VkSampleCountFlagBits samples);
void EndRenderPass();
void EndFrame();
// Buffers
int SetViewpoint(const HWViewpointUniforms& vp) override;
void SetViewpoint(int index) override;
@ -61,6 +56,14 @@ public:
void UpdateShadowData(unsigned int index, const FFlatVertex* vertices, unsigned int count) override;
void ResetVertices() override;
// Worker threads
void FlushCommands() override { EndRenderPass(); }
void BeginFrame();
void SetRenderTarget(VkTextureImage* image, VulkanImageView* depthStencilView, int width, int height, VkFormat Format, VkSampleCountFlagBits samples);
void EndRenderPass();
void EndFrame();
protected:
void Apply(int dt);
void ApplyRenderPass(int dt);
@ -144,6 +147,7 @@ protected:
std::map<VkRenderPassKey, std::unique_ptr<VkThreadRenderPassSetup>> mRenderPassSetups;
std::vector<VulkanPipelineLayout*> mPipelineLayouts;
std::unique_ptr<VulkanCommandBuffer> mThreadCommandBuffer;
};
class VkThreadRenderPassSetup