Track and accumulate pipeline flags for render passes.

(Information that will later let us make some interesting optimizations)
This commit is contained in:
Henrik Rydgård 2020-10-11 11:47:24 +02:00
parent 4ff059d3d8
commit 5ece3de8ba
10 changed files with 75 additions and 34 deletions

View File

@ -32,11 +32,19 @@ enum class VKRRenderCommand : uint8_t {
NUM_RENDER_COMMANDS,
};
enum PipelineFlags {
PIPELINE_FLAG_NONE = 0,
PIPELINE_FLAG_USES_LINES = (1 << 2),
PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3),
PIPELINE_FLAG_USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth buffer.
};
struct VkRenderData {
VKRRenderCommand cmd;
union {
struct {
VkPipeline pipeline;
PipelineFlags flags;
} pipeline;
struct {
VkPipelineLayout pipelineLayout;
@ -143,6 +151,7 @@ struct VKRStep {
int numReads;
VkImageLayout finalColorLayout;
VkImageLayout finalDepthStencilLayout;
u32 pipelineFlags;
} render;
struct {
VKRFramebuffer *src;

View File

@ -524,6 +524,18 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
return frameData_[curFrame].initCmd;
}
void VulkanRenderManager::EndCurRenderStep() {
// Save the accumulated pipeline flags so we can use that to configure the render pass.
// We'll often be able to avoid loading/saving the depth/stencil buffer.
if (curRenderStep_) {
curRenderStep_->render.pipelineFlags = curPipelineFlags_;
// We no longer have a current render step.
curRenderStep_ = nullptr;
curPipelineFlags_ = 0;
}
}
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, VKRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) {
_dbg_assert_(insideFrame_);
// Eliminate dupes, instantly convert to a clear if possible.
@ -568,15 +580,19 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
}
// More redundant bind elimination.
if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color != VKRRenderPassAction::CLEAR && curRenderStep_->render.depth != VKRRenderPassAction::CLEAR && curRenderStep_->render.stencil != VKRRenderPassAction::CLEAR) {
// Can trivially kill the last empty render step.
_dbg_assert_(steps_.back() == curRenderStep_);
delete steps_.back();
steps_.pop_back();
curRenderStep_ = nullptr;
}
if (curRenderStep_ && curRenderStep_->commands.size() == 0) {
VLOG("Empty render step. Usually happens after uploading pixels..");
if (curRenderStep_) {
if (curRenderStep_->commands.empty()) {
if (curRenderStep_->render.color != VKRRenderPassAction::CLEAR && curRenderStep_->render.depth != VKRRenderPassAction::CLEAR && curRenderStep_->render.stencil != VKRRenderPassAction::CLEAR) {
// Can trivially kill the last empty render step.
_dbg_assert_(steps_.back() == curRenderStep_);
delete steps_.back();
steps_.pop_back();
curRenderStep_ = nullptr;
}
VLOG("Empty render step. Usually happens after uploading pixels..");
}
EndCurRenderStep();
}
// Older Mali drivers have issues with depth and stencil don't match load/clear/etc.
@ -649,6 +665,8 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
}
}
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::READBACK };
step->readback.aspectMask = aspectBits;
step->readback.src = src;
@ -658,8 +676,6 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
step->tag = tag;
steps_.push_back(step);
curRenderStep_ = nullptr;
FlushSync();
Draw::DataFormat srcFormat = Draw::DataFormat::UNDEFINED;
@ -704,6 +720,9 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) {
_dbg_assert_(insideFrame_);
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::READBACK_IMAGE };
step->readback_image.image = image;
step->readback_image.srcRect.offset = { x, y };
@ -712,8 +731,6 @@ void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int
step->tag = tag;
steps_.push_back(step);
curRenderStep_ = nullptr;
FlushSync();
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
@ -975,6 +992,8 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
}
}
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::COPY };
step->copy.aspectMask = aspectMask;
@ -990,7 +1009,6 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
std::unique_lock<std::mutex> lock(mutex_);
steps_.push_back(step);
curRenderStep_ = nullptr;
}
void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag) {
@ -1019,6 +1037,8 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
}
}
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::BLIT };
step->blit.aspectMask = aspectMask;
@ -1035,7 +1055,6 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
std::unique_lock<std::mutex> lock(mutex_);
steps_.push_back(step);
curRenderStep_ = nullptr;
}
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int attachment) {
@ -1078,7 +1097,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in
}
void VulkanRenderManager::Finish() {
curRenderStep_ = nullptr;
EndCurRenderStep();
// Let's do just a bit of cleanup on render commands now.
for (auto &step : steps_) {

View File

@ -104,11 +104,12 @@ public:
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);
void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag);
void BindPipeline(VkPipeline pipeline) {
void BindPipeline(VkPipeline pipeline, PipelineFlags flags) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != VK_NULL_HANDLE);
VkRenderData data{ VKRRenderCommand::BIND_PIPELINE };
data.pipeline.pipeline = pipeline;
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
@ -265,6 +266,8 @@ public:
private:
bool InitBackbufferFramebuffers(int width, int height);
bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
void EndCurRenderStep();
void BeginSubmitFrame(int frame);
void EndSubmitFrame(int frame);
void Submit(int frame, bool triggerFence);
@ -328,6 +331,8 @@ private:
VKRStep *curRenderStep_ = nullptr;
bool curStepHasViewport_ = false;
bool curStepHasScissor_ = false;
u32 curPipelineFlags_ = 0;
std::vector<VKRStep *> steps_;
bool splitSubmit_ = false;

View File

@ -245,7 +245,7 @@ public:
class VKPipeline : public Pipeline {
public:
VKPipeline(VulkanContext *vulkan, size_t size) : vulkan_(vulkan) {
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : vulkan_(vulkan), flags(_flags) {
uboSize_ = (int)size;
ubo_ = new uint8_t[uboSize_];
}
@ -274,6 +274,8 @@ public:
VkPipeline backbufferPipeline = VK_NULL_HANDLE;
VkPipeline framebufferPipeline = VK_NULL_HANDLE;
PipelineFlags flags;
int stride[4]{};
int dynamicUniformSize = 0;
@ -1014,11 +1016,18 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
}
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float));
VKInputLayout *input = (VKInputLayout *)desc.inputLayout;
VKBlendState *blend = (VKBlendState *)desc.blend;
VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil;
VKRasterState *raster = (VKRasterState *)desc.raster;
u32 pipelineFlags = 0;
if (depth->info.depthTestEnable || depth->info.stencilTestEnable) {
pipelineFlags |= PIPELINE_FLAG_USES_DEPTH_STENCIL;
}
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags);
for (int i = 0; i < (int)input->bindings.size(); i++) {
pipeline->stride[i] = input->bindings[i].stride;
}
@ -1336,9 +1345,9 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
void VKContext::BindCompatiblePipeline() {
VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass();
if (renderPass == renderManager_.GetBackbufferRenderPass()) {
renderManager_.BindPipeline(curPipeline_->backbufferPipeline);
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags);
} else {
renderManager_.BindPipeline(curPipeline_->framebufferPipeline);
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags);
}
}

View File

@ -839,7 +839,7 @@ void DrawEngineVulkan::DoFlush() {
lastRenderStepId_ = curRenderStepId;
}
renderManager->BindPipeline(pipeline->pipeline);
renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags);
if (pipeline != lastPipeline_) {
if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) {
gstate_c.Dirty(DIRTY_BLEND_STATE);
@ -964,7 +964,7 @@ void DrawEngineVulkan::DoFlush() {
lastRenderStepId_ = curRenderStepId;
}
renderManager->BindPipeline(pipeline->pipeline);
renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags);
if (pipeline != lastPipeline_) {
if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) {
gstate_c.Dirty(DIRTY_BLEND_STATE);

View File

@ -225,7 +225,7 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa
VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE);
VkBuffer vbuffer;
VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer);
renderManager->BindPipeline(cur2DPipeline_);
renderManager->BindPipeline(cur2DPipeline_, (PipelineFlags)0);
renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4);
}

View File

@ -325,6 +325,9 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
vulkanPipeline->flags |= PIPELINE_FLAG_USES_BLEND_CONSTANT;
if (key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP)
vulkanPipeline->flags |= PIPELINE_FLAG_USES_LINES;
if (dss.depthTestEnable || dss.stencilTestEnable) {
vulkanPipeline->flags |= PIPELINE_FLAG_USES_DEPTH_STENCIL;
}
return vulkanPipeline;
}
@ -550,7 +553,7 @@ void PipelineManagerVulkan::SetLineWidth(float lineWidth) {
// Wipe all line-drawing pipelines.
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
if (value->UsesLines()) {
if (value->flags & PIPELINE_FLAG_USES_LINES) {
if (value->pipeline)
vulkan_->Delete().QueueDeletePipeline(value->pipeline);
delete value;
@ -619,7 +622,6 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
// Make sure the set of pipelines we write is "unique".
std::set<StoredVulkanPipelineKey> keys;
// TODO: Use derivative pipelines when possible, helps Mali driver pipeline creation speed at least.
pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) {
if (failed)
return;

View File

@ -25,6 +25,8 @@
#include "GPU/Vulkan/VulkanUtil.h"
#include "GPU/Vulkan/StateMappingVulkan.h"
#include "GPU/Vulkan/VulkanQueueRunner.h"
// PSP vertex format.
enum class PspAttributeLocation {
POSITION = 0,
@ -56,19 +58,14 @@ struct VulkanPipelineKey {
std::string GetDescription(DebugShaderStringType stringType) const;
};
enum PipelineFlags {
PIPELINE_FLAG_USES_LINES = (1 << 2),
PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3),
};
// Simply wraps a Vulkan pipeline, providing some metadata.
struct VulkanPipeline {
VkPipeline pipeline;
int flags; // PipelineFlags enum above.
// Convenience.
bool UsesBlendConstant() const { return (flags & PIPELINE_FLAG_USES_BLEND_CONSTANT) != 0; }
bool UsesLines() const { return (flags & PIPELINE_FLAG_USES_LINES) != 0; }
bool UsesDepthStencil() const { return (flags & PIPELINE_FLAG_USES_DEPTH_STENCIL) != 0; }
};
class VulkanContext;

View File

@ -180,7 +180,7 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, StencilUp
}
VkPipeline pipeline = vulkan2D_->GetPipeline(rp, stencilVs_, stencilFs_, false, Vulkan2D::VK2DDepthStencilMode::STENCIL_REPLACE_ALWAYS);
renderManager->BindPipeline(pipeline);
renderManager->BindPipeline(pipeline, PIPELINE_FLAG_USES_DEPTH_STENCIL);
renderManager->SetViewport({ 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f });
renderManager->SetScissor({ { 0, 0, },{ (uint32_t)w, (uint32_t)h } });

View File

@ -635,7 +635,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(fbo, samplerNearest_, clutTexture->GetImageView(), samplerNearest_);
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
renderManager->BindPipeline(depalShader->pipeline);
renderManager->BindPipeline(depalShader->pipeline, (PipelineFlags)0);
if (depth) {
DepthScaleFactors scaleFactors = GetDepthScaleFactors();