- use one render pass for the entire scene or until postprocess or command buffer flushing forces it to end

This commit is contained in:
Magnus Norddahl 2019-05-18 08:12:46 +02:00
parent 7e37d640dc
commit 680a6f348b
4 changed files with 59 additions and 84 deletions

View file

@ -200,12 +200,11 @@ std::unique_ptr<VulkanDescriptorSet> VkRenderPassManager::AllocateTextureDescrip
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
VkRenderPassSetup::VkRenderPassSetup(const VkRenderPassKey &key) VkRenderPassSetup::VkRenderPassSetup(const VkRenderPassKey &key) : PassKey(key)
{ {
CreateRenderPass(key);
} }
void VkRenderPassSetup::CreateRenderPass(const VkRenderPassKey &key) std::unique_ptr<VulkanRenderPass> VkRenderPassSetup::CreateRenderPass(int clearTargets)
{ {
auto buffers = GetVulkanFrameBuffer()->GetBuffers(); auto buffers = GetVulkanFrameBuffer()->GetBuffers();
@ -214,31 +213,31 @@ void VkRenderPassSetup::CreateRenderPass(const VkRenderPassKey &key)
RenderPassBuilder builder; RenderPassBuilder builder;
builder.addAttachment( builder.addAttachment(
key.DrawBufferFormat, (VkSampleCountFlagBits)key.Samples, PassKey.DrawBufferFormat, (VkSampleCountFlagBits)PassKey.Samples,
(key.ClearTargets & CT_Color) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, (clearTargets & CT_Color) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
for (int i = 1; i < key.DrawBuffers; i++) for (int i = 1; i < PassKey.DrawBuffers; i++)
{ {
builder.addAttachment( builder.addAttachment(
drawBufferFormats[i], buffers->GetSceneSamples(), drawBufferFormats[i], buffers->GetSceneSamples(),
(key.ClearTargets & CT_Color) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, (clearTargets & CT_Color) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
} }
if (key.UsesDepthStencil()) if (PassKey.DepthStencil)
{ {
builder.addDepthStencilAttachment( builder.addDepthStencilAttachment(
buffers->SceneDepthStencilFormat, key.DrawBufferFormat == VK_FORMAT_R8G8B8A8_UNORM ? VK_SAMPLE_COUNT_1_BIT : buffers->GetSceneSamples(), buffers->SceneDepthStencilFormat, PassKey.DrawBufferFormat == VK_FORMAT_R8G8B8A8_UNORM ? VK_SAMPLE_COUNT_1_BIT : buffers->GetSceneSamples(),
(key.ClearTargets & CT_Depth) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, (clearTargets & CT_Depth) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
(key.ClearTargets & CT_Stencil) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, (clearTargets & CT_Stencil) ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
} }
builder.addSubpass(); builder.addSubpass();
for (int i = 0; i < key.DrawBuffers; i++) for (int i = 0; i < PassKey.DrawBuffers; i++)
builder.addSubpassColorAttachmentRef(i, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); builder.addSubpassColorAttachmentRef(i, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (key.UsesDepthStencil()) if (PassKey.DepthStencil)
{ {
builder.addSubpassDepthStencilAttachmentRef(key.DrawBuffers, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); builder.addSubpassDepthStencilAttachmentRef(PassKey.DrawBuffers, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
builder.addExternalSubpassDependency( builder.addExternalSubpassDependency(
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
@ -253,8 +252,16 @@ void VkRenderPassSetup::CreateRenderPass(const VkRenderPassKey &key)
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); VK_ACCESS_COLOR_ATTACHMENT_READ_BIT);
} }
RenderPass = builder.create(GetVulkanFrameBuffer()->device); auto renderpass = builder.create(GetVulkanFrameBuffer()->device);
RenderPass->SetDebugName("VkRenderPassSetup.RenderPass"); renderpass->SetDebugName("VkRenderPassSetup.RenderPass");
return renderpass;
}
VulkanRenderPass *VkRenderPassSetup::GetRenderPass(int clearTargets)
{
if (!RenderPasses[clearTargets])
RenderPasses[clearTargets] = CreateRenderPass(clearTargets);
return RenderPasses[clearTargets].get();
} }
VulkanPipeline *VkRenderPassSetup::GetPipeline(const VkPipelineKey &key) VulkanPipeline *VkRenderPassSetup::GetPipeline(const VkPipelineKey &key)
@ -273,11 +280,11 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipeli
VkShaderProgram *program; VkShaderProgram *program;
if (key.SpecialEffect != EFF_NONE) if (key.SpecialEffect != EFF_NONE)
{ {
program = fb->GetShaderManager()->GetEffect(key.SpecialEffect, key.DrawBuffers > 1 ? GBUFFER_PASS : NORMAL_PASS); program = fb->GetShaderManager()->GetEffect(key.SpecialEffect, PassKey.DrawBuffers > 1 ? GBUFFER_PASS : NORMAL_PASS);
} }
else else
{ {
program = fb->GetShaderManager()->Get(key.EffectState, key.AlphaTest, key.DrawBuffers > 1 ? GBUFFER_PASS : NORMAL_PASS); program = fb->GetShaderManager()->Get(key.EffectState, key.AlphaTest, PassKey.DrawBuffers > 1 ? GBUFFER_PASS : NORMAL_PASS);
} }
builder.addVertexShader(program->vert.get()); builder.addVertexShader(program->vert.get());
builder.addFragmentShader(program->frag.get()); builder.addFragmentShader(program->frag.get());
@ -305,7 +312,7 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipeli
inputLocations[attr.location] = true; inputLocations[attr.location] = true;
} }
// To do: does vulkan absolutely needs a binding for each location or not? What happens if it isn't specified? Better be safe than sorry.. // Vulkan requires an attribute binding for each location specified in the shader
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
if (!inputLocations[i]) if (!inputLocations[i])
@ -350,11 +357,11 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipeli
builder.setColorWriteMask((VkColorComponentFlags)key.ColorMask); builder.setColorWriteMask((VkColorComponentFlags)key.ColorMask);
builder.setStencil(VK_STENCIL_OP_KEEP, op2vk[key.StencilPassOp], VK_STENCIL_OP_KEEP, VK_COMPARE_OP_EQUAL, 0xffffffff, 0xffffffff, 0); builder.setStencil(VK_STENCIL_OP_KEEP, op2vk[key.StencilPassOp], VK_STENCIL_OP_KEEP, VK_COMPARE_OP_EQUAL, 0xffffffff, 0xffffffff, 0);
builder.setBlendMode(key.RenderStyle); builder.setBlendMode(key.RenderStyle);
builder.setSubpassColorAttachmentCount(key.DrawBuffers); builder.setSubpassColorAttachmentCount(PassKey.DrawBuffers);
builder.setRasterizationSamples((VkSampleCountFlagBits)key.Samples); builder.setRasterizationSamples((VkSampleCountFlagBits)PassKey.Samples);
builder.setLayout(fb->GetRenderPassManager()->GetPipelineLayout(key.NumTextureLayers)); builder.setLayout(fb->GetRenderPassManager()->GetPipelineLayout(key.NumTextureLayers));
builder.setRenderPass(RenderPass.get()); builder.setRenderPass(GetRenderPass(0));
auto pipeline = builder.create(fb->device); auto pipeline = builder.create(fb->device);
pipeline->SetDebugName("VkRenderPassSetup.Pipeline"); pipeline->SetDebugName("VkRenderPassSetup.Pipeline");
return pipeline; return pipeline;

View file

@ -28,13 +28,7 @@ public:
int CullMode; int CullMode;
int VertexFormat; int VertexFormat;
int DrawType; int DrawType;
int Samples;
int ClearTargets;
int DrawBuffers;
int NumTextureLayers; int NumTextureLayers;
VkFormat DrawBufferFormat;
bool UsesDepthStencil() const { return DepthTest || DepthWrite || StencilTest || (ClearTargets & (CT_Depth | CT_Stencil)); }
bool operator<(const VkPipelineKey &other) const { return memcmp(this, &other, sizeof(VkPipelineKey)) < 0; } bool operator<(const VkPipelineKey &other) const { return memcmp(this, &other, sizeof(VkPipelineKey)) < 0; }
bool operator==(const VkPipelineKey &other) const { return memcmp(this, &other, sizeof(VkPipelineKey)) == 0; } bool operator==(const VkPipelineKey &other) const { return memcmp(this, &other, sizeof(VkPipelineKey)) == 0; }
@ -44,28 +38,11 @@ public:
class VkRenderPassKey class VkRenderPassKey
{ {
public: public:
VkRenderPassKey() = default; int DepthStencil;
VkRenderPassKey(const VkPipelineKey &key)
{
DepthWrite = key.DepthWrite;
DepthTest = key.DepthTest;
StencilTest = key.StencilTest;
Samples = key.Samples;
ClearTargets = key.ClearTargets;
DrawBuffers = key.DrawBuffers;
DrawBufferFormat = key.DrawBufferFormat;
}
int DepthWrite;
int DepthTest;
int StencilTest;
int Samples; int Samples;
int ClearTargets;
int DrawBuffers; int DrawBuffers;
VkFormat DrawBufferFormat; VkFormat DrawBufferFormat;
bool UsesDepthStencil() const { return DepthTest || DepthWrite || StencilTest || (ClearTargets & (CT_Depth | CT_Stencil)); }
bool operator<(const VkRenderPassKey &other) const { return memcmp(this, &other, sizeof(VkRenderPassKey)) < 0; } bool operator<(const VkRenderPassKey &other) const { return memcmp(this, &other, sizeof(VkRenderPassKey)) < 0; }
bool operator==(const VkRenderPassKey &other) const { return memcmp(this, &other, sizeof(VkRenderPassKey)) == 0; } bool operator==(const VkRenderPassKey &other) const { return memcmp(this, &other, sizeof(VkRenderPassKey)) == 0; }
bool operator!=(const VkRenderPassKey &other) const { return memcmp(this, &other, sizeof(VkRenderPassKey)) != 0; } bool operator!=(const VkRenderPassKey &other) const { return memcmp(this, &other, sizeof(VkRenderPassKey)) != 0; }
@ -76,14 +53,16 @@ class VkRenderPassSetup
public: public:
VkRenderPassSetup(const VkRenderPassKey &key); VkRenderPassSetup(const VkRenderPassKey &key);
VulkanRenderPass *GetRenderPass(int clearTargets);
VulkanPipeline *GetPipeline(const VkPipelineKey &key); VulkanPipeline *GetPipeline(const VkPipelineKey &key);
std::unique_ptr<VulkanRenderPass> RenderPass; VkRenderPassKey PassKey;
std::unique_ptr<VulkanRenderPass> RenderPasses[8];
std::map<VkPipelineKey, std::unique_ptr<VulkanPipeline>> Pipelines; std::map<VkPipelineKey, std::unique_ptr<VulkanPipeline>> Pipelines;
std::map<VkImageView, std::unique_ptr<VulkanFramebuffer>> Framebuffer; std::map<VkImageView, std::unique_ptr<VulkanFramebuffer>> Framebuffer;
private: private:
void CreateRenderPass(const VkRenderPassKey &key); std::unique_ptr<VulkanRenderPass> CreateRenderPass(int clearTargets);
std::unique_ptr<VulkanPipeline> CreatePipeline(const VkPipelineKey &key); std::unique_ptr<VulkanPipeline> CreatePipeline(const VkPipelineKey &key);
}; };

View file

@ -195,9 +195,8 @@ void VkRenderState::ApplyDepthBias()
void VkRenderState::ApplyRenderPass(int dt) void VkRenderState::ApplyRenderPass(int dt)
{ {
// Find a render pass that matches our state // Find a pipeline that matches our state
VkPipelineKey pipelineKey; VkPipelineKey pipelineKey;
pipelineKey.ClearTargets = mPipelineKey.ClearTargets | mClearTargets;
pipelineKey.DrawType = dt; pipelineKey.DrawType = dt;
pipelineKey.VertexFormat = static_cast<VKVertexBuffer*>(mVertexBuffer)->VertexFormat; pipelineKey.VertexFormat = static_cast<VKVertexBuffer*>(mVertexBuffer)->VertexFormat;
pipelineKey.RenderStyle = mRenderStyle; pipelineKey.RenderStyle = mRenderStyle;
@ -210,9 +209,6 @@ void VkRenderState::ApplyRenderPass(int dt)
pipelineKey.StencilPassOp = mStencilOp; pipelineKey.StencilPassOp = mStencilOp;
pipelineKey.ColorMask = mColorMask; pipelineKey.ColorMask = mColorMask;
pipelineKey.CullMode = mCullMode; pipelineKey.CullMode = mCullMode;
pipelineKey.DrawBufferFormat = mRenderTarget.Format;
pipelineKey.Samples = mRenderTarget.Samples;
pipelineKey.DrawBuffers = mRenderTarget.DrawBuffers;
pipelineKey.NumTextureLayers = mMaterial.mMaterial ? mMaterial.mMaterial->GetLayers() : 0; pipelineKey.NumTextureLayers = mMaterial.mMaterial ? mMaterial.mMaterial->GetLayers() : 0;
if (mSpecialEffect > EFF_NONE) if (mSpecialEffect > EFF_NONE)
{ {
@ -228,44 +224,25 @@ void VkRenderState::ApplyRenderPass(int dt)
pipelineKey.AlphaTest = mAlphaThreshold >= 0.f; pipelineKey.AlphaTest = mAlphaThreshold >= 0.f;
} }
// Is this the one we already have or do we need to change pipeline? // Is this the one we already have?
bool changingPipeline = (pipelineKey != mPipelineKey);
bool inRenderPass = mCommandBuffer; bool inRenderPass = mCommandBuffer;
bool changingPipeline = (!inRenderPass) || (pipelineKey != mPipelineKey);
if (!inRenderPass) if (!inRenderPass)
{ {
mCommandBuffer = GetVulkanFrameBuffer()->GetDrawCommands(); mCommandBuffer = GetVulkanFrameBuffer()->GetDrawCommands();
changingPipeline = true;
mScissorChanged = true; mScissorChanged = true;
mViewportChanged = true; mViewportChanged = true;
mStencilRefChanged = true; mStencilRefChanged = true;
mBias.mChanged = true; mBias.mChanged = true;
BeginRenderPass(mCommandBuffer);
} }
if (changingPipeline) if (changingPipeline)
{ {
pipelineKey.ClearTargets = mClearTargets; mCommandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mPassSetup->GetPipeline(pipelineKey));
// Only clear depth+stencil if the render target actually has that
if (!mRenderTarget.DepthStencil)
pipelineKey.ClearTargets &= ~(CT_Depth | CT_Stencil);
// Begin new render pass if needed
VkRenderPassKey passKey = pipelineKey;
if (!inRenderPass || passKey != VkRenderPassKey(mPipelineKey))
{
if (inRenderPass)
mCommandBuffer->endRenderPass();
BeginRenderPass(passKey, mCommandBuffer);
}
// Bind the pipeline
VkRenderPassSetup *passSetup = GetVulkanFrameBuffer()->GetRenderPassManager()->GetRenderPass(passKey);
mCommandBuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, passSetup->GetPipeline(pipelineKey));
mPipelineKey = pipelineKey; mPipelineKey = pipelineKey;
mClearTargets = 0;
} }
} }
@ -563,32 +540,42 @@ void VkRenderState::SetRenderTarget(VulkanImageView *view, VulkanImageView *dept
mRenderTarget.Samples = samples; mRenderTarget.Samples = samples;
} }
void VkRenderState::BeginRenderPass(const VkRenderPassKey &key, VulkanCommandBuffer *cmdbuffer) void VkRenderState::BeginRenderPass(VulkanCommandBuffer *cmdbuffer)
{ {
auto fb = GetVulkanFrameBuffer(); auto fb = GetVulkanFrameBuffer();
VkRenderPassSetup *passSetup = fb->GetRenderPassManager()->GetRenderPass(key); VkRenderPassKey key = {};
key.DrawBufferFormat = mRenderTarget.Format;
key.Samples = mRenderTarget.Samples;
key.DrawBuffers = mRenderTarget.DrawBuffers;
key.DepthStencil = !!mRenderTarget.DepthStencil;
auto &framebuffer = passSetup->Framebuffer[mRenderTarget.View->view]; mPassSetup = fb->GetRenderPassManager()->GetRenderPass(key);
auto &framebuffer = mPassSetup->Framebuffer[mRenderTarget.View->view];
if (!framebuffer) if (!framebuffer)
{ {
auto buffers = fb->GetBuffers(); auto buffers = fb->GetBuffers();
FramebufferBuilder builder; FramebufferBuilder builder;
builder.setRenderPass(passSetup->RenderPass.get()); builder.setRenderPass(mPassSetup->GetRenderPass(0));
builder.setSize(mRenderTarget.Width, mRenderTarget.Height); builder.setSize(mRenderTarget.Width, mRenderTarget.Height);
builder.addAttachment(mRenderTarget.View); builder.addAttachment(mRenderTarget.View);
if (key.DrawBuffers > 1) if (key.DrawBuffers > 1)
builder.addAttachment(buffers->SceneFog.View.get()); builder.addAttachment(buffers->SceneFog.View.get());
if (key.DrawBuffers > 2) if (key.DrawBuffers > 2)
builder.addAttachment(buffers->SceneNormal.View.get()); builder.addAttachment(buffers->SceneNormal.View.get());
if (key.UsesDepthStencil()) if (key.DepthStencil)
builder.addAttachment(mRenderTarget.DepthStencil); builder.addAttachment(mRenderTarget.DepthStencil);
framebuffer = builder.create(GetVulkanFrameBuffer()->device); framebuffer = builder.create(GetVulkanFrameBuffer()->device);
framebuffer->SetDebugName("VkRenderPassSetup.Framebuffer"); framebuffer->SetDebugName("VkRenderPassSetup.Framebuffer");
} }
// Only clear depth+stencil if the render target actually has that
if (!mRenderTarget.DepthStencil)
mClearTargets &= ~(CT_Depth | CT_Stencil);
RenderPassBegin beginInfo; RenderPassBegin beginInfo;
beginInfo.setRenderPass(passSetup->RenderPass.get()); beginInfo.setRenderPass(mPassSetup->GetRenderPass(mClearTargets));
beginInfo.setRenderArea(0, 0, mRenderTarget.Width, mRenderTarget.Height); beginInfo.setRenderArea(0, 0, mRenderTarget.Width, mRenderTarget.Height);
beginInfo.setFramebuffer(framebuffer.get()); beginInfo.setFramebuffer(framebuffer.get());
beginInfo.addClearColor(screen->mSceneClearColor[0], screen->mSceneClearColor[1], screen->mSceneClearColor[2], screen->mSceneClearColor[3]); beginInfo.addClearColor(screen->mSceneClearColor[0], screen->mSceneClearColor[1], screen->mSceneClearColor[2], screen->mSceneClearColor[3]);
@ -600,6 +587,7 @@ void VkRenderState::BeginRenderPass(const VkRenderPassKey &key, VulkanCommandBuf
cmdbuffer->beginRenderPass(beginInfo); cmdbuffer->beginRenderPass(beginInfo);
mMaterial.mChanged = true; mMaterial.mChanged = true;
mClearTargets = 0;
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

View file

@ -62,11 +62,12 @@ protected:
void ApplyVertexBuffers(); void ApplyVertexBuffers();
void ApplyMaterial(); void ApplyMaterial();
void BeginRenderPass(const VkRenderPassKey &key, VulkanCommandBuffer *cmdbuffer); void BeginRenderPass(VulkanCommandBuffer *cmdbuffer);
bool mDepthClamp = true; bool mDepthClamp = true;
VulkanCommandBuffer *mCommandBuffer = nullptr; VulkanCommandBuffer *mCommandBuffer = nullptr;
VkPipelineKey mPipelineKey = {}; VkPipelineKey mPipelineKey = {};
VkRenderPassSetup *mPassSetup = nullptr;
int mClearTargets = 0; int mClearTargets = 0;
bool mNeedApply = true; bool mNeedApply = true;