Create pipelines in parallel

This commit is contained in:
Henrik Rydgård 2022-06-12 13:23:24 +02:00
parent 5ca0bfe716
commit 8821d619e4
2 changed files with 70 additions and 23 deletions

View file

@ -1,6 +1,7 @@
#include <algorithm>
#include <cstdint>
#include <map>
#include <sstream>
#include "Common/Log.h"
@ -218,26 +219,27 @@ void VKRGraphicsPipeline::LogCreationFailure() const {
ERROR_LOG(G3D, "======== END OF PIPELINE ==========");
}
bool VKRComputePipeline::Create(VulkanContext *vulkan) {
bool VKRComputePipeline::CreateAsync(VulkanContext *vulkan) {
if (!desc) {
// Already failed to create this one.
return false;
}
VkPipeline vkpipeline;
VkResult result = vkCreateComputePipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline);
pipeline->SpawnEmpty(&g_threadManager, [=] {
VkPipeline vkpipeline;
VkResult result = vkCreateComputePipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline);
bool success = true;
if (result != VK_SUCCESS) {
pipeline->Post(VK_NULL_HANDLE);
ERROR_LOG(G3D, "Failed creating compute pipeline! result='%s'", VulkanResultToString(result));
success = false;
} else {
pipeline->Post(vkpipeline);
}
delete desc;
bool success = true;
if (result == VK_SUCCESS) {
return vkpipeline;
} else {
ERROR_LOG(G3D, "Failed creating compute pipeline! result='%s'", VulkanResultToString(result));
success = false;
return (VkPipeline)VK_NULL_HANDLE;
}
delete desc;
}, TaskType::CPU_COMPUTE);
desc = nullptr;
return success;
return true;
}
VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan)
@ -370,7 +372,6 @@ VulkanRenderManager::~VulkanRenderManager() {
vulkan_->WaitUntilQueueIdle();
DrainCompileQueue();
VkDevice device = vulkan_->GetDevice();
frameDataShared_.Destroy(vulkan_);
for (int i = 0; i < inflightFramesAtStart_; i++) {
@ -379,12 +380,41 @@ VulkanRenderManager::~VulkanRenderManager() {
queueRunner_.DestroyDeviceObjects();
}
struct SinglePipelineTask {
VKRGraphicsPipeline *pipeline;
VkRenderPass compatibleRenderPass;
RenderPassType rpType;
VkSampleCountFlagBits sampleCount;
};
class CreateMultiPipelinesTask : public Task {
public:
CreateMultiPipelinesTask(VulkanContext *vulkan, std::vector<SinglePipelineTask> tasks) : vulkan_(vulkan), tasks_(tasks) {}
~CreateMultiPipelinesTask() {}
TaskType Type() const override {
return TaskType::CPU_COMPUTE;
}
void Run() override {
for (auto &task : tasks_) {
task.pipeline->Create(vulkan_, task.compatibleRenderPass, task.rpType, task.sampleCount);
}
}
VulkanContext *vulkan_;
std::vector<SinglePipelineTask> tasks_;
};
void VulkanRenderManager::CompileThreadFunc() {
SetCurrentThreadName("ShaderCompile");
while (true) {
std::vector<CompileQueueEntry> toCompile;
{
std::unique_lock<std::mutex> lock(compileMutex_);
// TODO: Should this be while?
// It may be beneficial also to unlock and wait a little bit to see if we get some more shaders
// so we can do a better job of thread-sorting them.
if (compileQueue_.empty() && run_) {
compileCond_.wait(lock);
}
@ -395,24 +425,41 @@ void VulkanRenderManager::CompileThreadFunc() {
break;
}
double time = time_now_d();
// TODO: Here we can sort the pending pipelines by vertex and fragment shaders,
NOTICE_LOG(G3D, "Compilation thread has %d pipelines to create", (int)toCompile.size());
// Here we sort the pending pipelines by vertex and fragment shaders,
std::map<std::pair<Promise<VkShaderModule> *, Promise<VkShaderModule> *>, std::vector<SinglePipelineTask>> map;
// TODO: Here we can sort pending graphics pipelines by vertex and fragment shaders,
// and split up further.
// Those with the same pairs of shaders should be on the same thread.
for (auto &entry : toCompile) {
switch (entry.type) {
case CompileQueueEntry::Type::GRAPHICS:
entry.graphics->Create(vulkan_, entry.compatibleRenderPass, entry.renderPassType, entry.sampleCount);
map[std::pair< Promise<VkShaderModule> *, Promise<VkShaderModule> *>(entry.graphics->desc->vertexShader, entry.graphics->desc->fragmentShader)].push_back(
SinglePipelineTask{
entry.graphics,
entry.compatibleRenderPass,
entry.renderPassType,
entry.sampleCount,
}
);
break;
case CompileQueueEntry::Type::COMPUTE:
entry.compute->Create(vulkan_);
// Queue up pending compute pipelines on separate tasks.
entry.compute->CreateAsync(vulkan_);
break;
}
}
double delta = time_now_d() - time;
if (delta > 0.005f) {
INFO_LOG(G3D, "CompileThreadFunc: Creating %d pipelines took %0.3f ms", (int)toCompile.size(), delta * 1000.0f);
for (auto iter : map) {
auto &shaders = iter.first;
auto &entries = iter.second;
NOTICE_LOG(G3D, "For this shader pair, we have %d pipelines to create", (int)entries.size());
Task *task = new CreateMultiPipelinesTask(vulkan_, entries);
g_threadManager.EnqueueTask(task);
}
queueRunner_.NotifyCompileDone();

View file

@ -153,7 +153,7 @@ struct VKRComputePipeline {
VKRComputePipelineDesc *desc = nullptr;
Promise<VkPipeline> *pipeline = nullptr;
bool Create(VulkanContext *vulkan);
bool CreateAsync(VulkanContext *vulkan);
bool Pending() const {
return pipeline == VK_NULL_HANDLE && desc != nullptr;
}