Vulkan: Add code (disabled) to be able to run with more in-flight frames. Only improves performance marginally and needs more testing.

This commit is contained in:
Henrik Rydgård 2017-08-17 17:55:21 +02:00
parent d65547edb5
commit 560eaa5390
10 changed files with 67 additions and 42 deletions

View File

@ -71,8 +71,7 @@ VulkanContext::VulkanContext(const char *app_name, int app_ver, uint32_t flags)
swapchainImageCount(0),
swap_chain_(VK_NULL_HANDLE),
cmd_pool_(VK_NULL_HANDLE),
queue_count(0),
curFrame_(0) {
queue_count(0) {
if (!VulkanLoad()) {
init_error_ = "Failed to load Vulkan driver library";
// No DLL?
@ -311,7 +310,10 @@ void VulkanContext::EndFrame() {
assert(!res);
frame->deleteList.Take(globalDeleteList_);
curFrame_ ^= 1;
curFrame_++;
if (curFrame_ >= inflightFrames_) {
curFrame_ = 0;
}
}
void VulkanContext::WaitUntilQueueIdle() {
@ -353,18 +355,17 @@ bool VulkanContext::InitObjects(bool depthPresent) {
VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
cmd_alloc.commandPool = cmd_pool_;
cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd_alloc.commandBufferCount = 4;
cmd_alloc.commandBufferCount = MAX_INFLIGHT_FRAMES * 2;
VkCommandBuffer cmdBuf[4];
VkCommandBuffer cmdBuf[MAX_INFLIGHT_FRAMES * 2];
VkResult res = vkAllocateCommandBuffers(device_, &cmd_alloc, cmdBuf);
assert(res == VK_SUCCESS);
frame_[0].cmdBuf = cmdBuf[0];
frame_[0].cmdInit = cmdBuf[1];
frame_[0].fence = CreateFence(true); // So it can be instantly waited on
frame_[1].cmdBuf = cmdBuf[2];
frame_[1].cmdInit = cmdBuf[3];
frame_[1].fence = CreateFence(true);
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
frame_[i].cmdBuf = cmdBuf[i * 2];
frame_[i].cmdInit = cmdBuf[i * 2 + 1];
frame_[i].fence = CreateFence(true); // So it can be instantly waited on
}
VkCommandBuffer cmd = GetInitCommandBuffer();
if (!InitSwapchain(cmd)) {
@ -381,11 +382,16 @@ bool VulkanContext::InitObjects(bool depthPresent) {
}
void VulkanContext::DestroyObjects() {
VkCommandBuffer cmdBuf[4] = { frame_[0].cmdBuf, frame_[0].cmdInit, frame_[1].cmdBuf, frame_[1].cmdInit };
vkFreeCommandBuffers(device_, cmd_pool_, sizeof(cmdBuf) / sizeof(cmdBuf[0]), cmdBuf);
vkDestroyFence(device_, frame_[0].fence, nullptr);
vkDestroyFence(device_, frame_[1].fence, nullptr);
VkCommandBuffer *cmdBuf = new VkCommandBuffer[MAX_INFLIGHT_FRAMES * 2];
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
cmdBuf[i * 2] = frame_[i].cmdBuf;
cmdBuf[i * 2 + 1] = frame_[i].cmdInit;
}
vkFreeCommandBuffers(device_, cmd_pool_, MAX_INFLIGHT_FRAMES * 2, cmdBuf);
delete[] cmdBuf;
for (int i = 0; i < MAX_INFLIGHT_FRAMES; i++) {
vkDestroyFence(device_, frame_[i].fence, nullptr);
}
DestroyFramebuffers();
DestroySurfaceRenderPass();

View File

@ -263,7 +263,7 @@ public:
}
// This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass.
VkCommandBuffer GetSurfaceCommandBuffer() {
return frame_[curFrame_ & 1].cmdBuf;
return frame_[curFrame_].cmdBuf;
}
VkCommandBuffer BeginFrame();
@ -314,7 +314,15 @@ public:
const VkPhysicalDeviceFeatures &GetFeaturesEnabled() const { return featuresEnabled_; }
const VulkanPhysicalDeviceInfo &GetDeviceInfo() const { return deviceInfo_; }
int GetInflightFrames() const {
return inflightFrames_;
}
enum {
MAX_INFLIGHT_FRAMES = 2,
};
private:
VkSemaphore acquireSemaphore;
VkSemaphore renderingCompleteSemaphore;
@ -368,6 +376,8 @@ private:
VkSwapchainKHR swap_chain_;
std::vector<swap_chain_buffer> swapChainBuffers;
int inflightFrames_ = MAX_INFLIGHT_FRAMES;
// Manages flipping command buffers for the backbuffer render pass.
// It is recommended to do the same for other rendering passes.
struct FrameData {
@ -380,8 +390,8 @@ private:
VulkanDeleteList deleteList;
};
FrameData frame_[2];
int curFrame_;
FrameData frame_[MAX_INFLIGHT_FRAMES];
int curFrame_ = 0;
// At the end of the frame, this is copied into the frame's delete list, so it can be processed
// the next time the frame comes around again.

View File

@ -162,7 +162,7 @@ void DrawEngineVulkan::InitDeviceObjects() {
// We are going to use one-shot descriptors in the initial implementation. Might look into caching them
// if creating and updating them turns out to be expensive.
for (int i = 0; i < 2; i++) {
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
// If we run out of memory, try with less descriptors.
for (int tries = 0; tries < 3; ++tries) {
VkResult res = vkCreateDescriptorPool(vulkan_->GetDevice(), &dp, nullptr, &frame_[i].descPool);
@ -274,7 +274,7 @@ void DrawEngineVulkan::BeginFrame() {
lastCmd_ = VK_NULL_HANDLE;
lastPipeline_ = nullptr;
FrameData *frame = &frame_[curFrame_ & 1];
FrameData *frame = &frame_[curFrame_];
vkResetDescriptorPool(vulkan_->GetDevice(), frame->descPool, 0);
frame->descSets.clear();
@ -347,7 +347,7 @@ void DrawEngineVulkan::BeginFrame() {
}
void DrawEngineVulkan::EndFrame() {
FrameData *frame = &frame_[curFrame_ & 1];
FrameData *frame = &frame_[curFrame_];
stats_.pushUBOSpaceUsed = (int)frame->pushUBO->GetOffset();
stats_.pushVertexSpaceUsed = (int)frame->pushVertex->GetOffset();
stats_.pushIndexSpaceUsed = (int)frame->pushIndex->GetOffset();
@ -355,6 +355,8 @@ void DrawEngineVulkan::EndFrame() {
frame->pushVertex->End();
frame->pushIndex->End();
curFrame_++;
if (curFrame_ >= vulkan_->GetInflightFrames())
curFrame_ = 0;
vertexCache_->End();
}
@ -508,7 +510,7 @@ VkDescriptorSet DrawEngineVulkan::GetDescriptorSet(VkImageView imageView, VkSamp
assert(light != VK_NULL_HANDLE);
assert(bone != VK_NULL_HANDLE);
FrameData *frame = &frame_[curFrame_ & 1];
FrameData *frame = &frame_[curFrame_];
if (!(gstate_c.bezier || gstate_c.spline)) { // Has no cache when HW tessellation.
auto iter = frame->descSets.find(key);
if (iter != frame->descSets.end()) {
@ -645,7 +647,7 @@ void DrawEngineVulkan::DoFlush() {
if (!rp)
Crash();
FrameData *frame = &frame_[curFrame_ & 1];
FrameData *frame = &frame_[curFrame_];
bool textureNeedsApply = false;
if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) {

View File

@ -162,7 +162,7 @@ public:
void DirtyAllUBOs();
VulkanPushBuffer *GetPushBufferForTextureData() {
return frame_[curFrame_ & 1].pushUBO;
return frame_[curFrame_].pushUBO;
}
const DrawEngineVulkanStats &GetStats() const {
@ -230,7 +230,7 @@ private:
GEPrimitiveType lastPrim_ = GE_PRIM_INVALID;
int curFrame_;
FrameData frame_[2];
FrameData frame_[VulkanContext::MAX_INFLIGHT_FRAMES];
// Other
ShaderManagerVulkan *shaderManager_ = nullptr;

View File

@ -115,7 +115,7 @@ void FramebufferManagerVulkan::SetShaderManager(ShaderManagerVulkan *sm) {
void FramebufferManagerVulkan::InitDeviceObjects() {
// Initialize framedata
for (int i = 0; i < 2; i++) {
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024);
}
@ -146,7 +146,7 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
}
void FramebufferManagerVulkan::DestroyDeviceObjects() {
for (int i = 0; i < 2; i++) {
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
if (frameData_[i].push_) {
frameData_[i].push_->Destroy(vulkan_);
delete frameData_[i].push_;
@ -948,7 +948,9 @@ void FramebufferManagerVulkan::EndFrame() {
vulkan2D_.EndFrame();
curFrame_++;
curFrame_ &= 1;
if (curFrame_ >= vulkan_->GetInflightFrames()) {
curFrame_ = 0;
}
}
void FramebufferManagerVulkan::DeviceLost() {

View File

@ -163,7 +163,7 @@ private:
VulkanPushBuffer *push_;
};
FrameData frameData_[2];
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];
int curFrame_;
// This gets copied to the current frame's push buffer as needed.

View File

@ -60,7 +60,7 @@ void Vulkan2D::Shutdown() {
}
void Vulkan2D::DestroyDeviceObjects() {
for (int i = 0; i < 2; i++) {
for (int i = 0; i < vulkan_->GetInflightFrames(); i++) {
if (frameData_[i].descPool != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteDescriptorPool(frameData_[i].descPool);
}
@ -146,7 +146,9 @@ void Vulkan2D::BeginFrame() {
}
void Vulkan2D::EndFrame() {
curFrame_ = (curFrame_ + 1) & 1;
curFrame_++;
if (curFrame_ >= vulkan_->GetInflightFrames())
curFrame_ = 0;
}
VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2) {
@ -156,7 +158,7 @@ VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1,
key.sampler[0] = sampler1;
key.sampler[1] = sampler2;
FrameData *frame = &frameData_[curFrame_ & 1];
FrameData *frame = &frameData_[curFrame_];
auto iter = frame->descSets.find(key);
if (iter != frame->descSets.end()) {
return iter->second;

View File

@ -131,8 +131,8 @@ private:
std::map<DescriptorSetKey, VkDescriptorSet> descSets;
};
FrameData frameData_[2];
int curFrame_;
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];
int curFrame_ = 0;
std::map<PipelineKey, VkPipeline> pipelines_;
};

View File

@ -1090,6 +1090,7 @@ retry:
NativeDeviceLost();
renderer_inited = false;
ILOG("Shutting down graphics context.");
graphicsContext->Shutdown();
delete graphicsContext;
graphicsContext = nullptr;

View File

@ -541,7 +541,7 @@ private:
VkDescriptorPool descriptorPool;
};
FrameData frame_[2]{};
FrameData frame_[VulkanContext::MAX_INFLIGHT_FRAMES]{};
int frameNum_ = 0;
VulkanPushBuffer *push_ = nullptr;
@ -735,7 +735,7 @@ VKContext::VKContext(VulkanContext *vulkan)
p.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
p.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();
for (int i = 0; i < 2; i++) {
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool);
assert(VK_SUCCESS == res);
res = vkCreateCommandPool(device_, &p, nullptr, &frame_[i].cmdPool_);
@ -846,7 +846,7 @@ VKContext::~VKContext() {
vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]);
}
// This also destroys all descriptor sets.
for (int i = 0; i < 2; i++) {
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
frame_[i].descSets_.clear();
vulkan_->Delete().QueueDeleteDescriptorPool(frame_[i].descriptorPool);
frame_[i].pushBuffer->Destroy(vulkan_);
@ -860,7 +860,7 @@ VKContext::~VKContext() {
// Effectively wiped every frame, just allocate new ones!
VkCommandBuffer VKContext::AllocCmdBuf() {
FrameData *frame = &frame_[frameNum_ & 1];
FrameData *frame = &frame_[frameNum_];
if (frame->numCmdBufs >= MAX_FRAME_COMMAND_BUFFERS)
Crash();
@ -887,7 +887,7 @@ VkCommandBuffer VKContext::AllocCmdBuf() {
void VKContext::BeginFrame() {
vulkan_->BeginFrame();
FrameData &frame = frame_[frameNum_ & 1];
FrameData &frame = frame_[frameNum_];
frame.startCmdBufs_ = 0;
frame.numCmdBufs = 0;
vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0);
@ -924,7 +924,7 @@ void VKContext::EndFrame() {
Crash();
// Cap off and submit all the command buffers we recorded during the frame.
FrameData &frame = frame_[frameNum_ & 1];
FrameData &frame = frame_[frameNum_];
for (int i = frame.startCmdBufs_; i < frame.numCmdBufs; i++) {
vkEndCommandBuffer(frame.cmdBufs[i]);
vulkan_->QueueBeforeSurfaceRender(frame.cmdBufs[i]);
@ -936,6 +936,8 @@ void VKContext::EndFrame() {
vulkan_->EndFrame();
frameNum_++;
if (frameNum_ >= vulkan_->GetInflightFrames())
frameNum_ = 0;
push_ = nullptr;
DirtyDynamicState();
@ -944,7 +946,7 @@ void VKContext::EndFrame() {
VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
DescriptorSetKey key;
FrameData *frame = &frame_[frameNum_ & 1];
FrameData *frame = &frame_[frameNum_];
key.texture_ = boundTextures_[0];
key.sampler_ = boundSamplers_[0];