Commit ffa4cbb6 by Jamie Madill Committed by Commit Bot

Vulkan: Implement the Pipeline cache.

This currently keeps a cache of every PSO compiled and does not trim the cache or evict old members on memory pressure. This will be done as a follow-up. Improves the speed of the Draw Call microbenchmark 50x when using a single state change. Bug: angleproject:2163 Change-Id: I2cceb38ca57ae639f36a944f4571b627481b92da Reviewed-on: https://chromium-review.googlesource.com/876954Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 1436d434
......@@ -73,7 +73,6 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
ContextVk::~ContextVk()
{
invalidateCurrentPipeline();
}
void ContextVk::onDestroy(const gl::Context *context)
......@@ -127,7 +126,7 @@ gl::Error ContextVk::finish(const gl::Context *context)
gl::Error ContextVk::initPipeline(const gl::Context *context)
{
ASSERT(!mCurrentPipeline.valid());
ASSERT(!mCurrentPipeline);
const gl::State &state = mState.getState();
VertexArrayVk *vertexArrayVk = vk::GetImpl(state.getVertexArray());
......@@ -144,7 +143,7 @@ gl::Error ContextVk::initPipeline(const gl::Context *context)
mPipelineDesc->updateRenderPassDesc(framebufferVk->getRenderPassDesc(context));
// TODO(jmadill): Validate with ASSERT against physical device limits/caps?
ANGLE_TRY(mPipelineDesc->initializePipeline(mRenderer, programVk, &mCurrentPipeline));
ANGLE_TRY(mRenderer->getPipeline(programVk, *mPipelineDesc, &mCurrentPipeline));
return gl::NoError();
}
......@@ -160,10 +159,9 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
mCurrentDrawMode = mode;
}
if (!mCurrentPipeline.valid())
if (!mCurrentPipeline)
{
ANGLE_TRY(initPipeline(context));
ASSERT(mCurrentPipeline.valid());
}
const auto &state = mState.getState();
......@@ -229,14 +227,14 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
}
}
(*commandBuffer)->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline);
(*commandBuffer)->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline->get());
(*commandBuffer)
->bindVertexBuffers(0, maxAttrib, vertexHandles.data(),
reinterpret_cast<const VkDeviceSize *>(zeroBuf->data()));
// Update the queue serial for the pipeline object.
// TODO(jmadill): the queue serial should be bound to the pipeline.
updateQueueSerial(queueSerial);
ASSERT(mCurrentPipeline && mCurrentPipeline->valid());
mCurrentPipeline->updateSerial(queueSerial);
// TODO(jmadill): Can probably use more dirty bits here.
ANGLE_TRY(programVk->updateUniforms(this));
......@@ -731,10 +729,9 @@ std::vector<PathImpl *> ContextVk::createPaths(GLsizei)
return std::vector<PathImpl *>();
}
// TODO(jmadill): Use pipeline cache.
void ContextVk::invalidateCurrentPipeline()
{
mRenderer->releaseResource(*this, &mCurrentPipeline);
mCurrentPipeline = nullptr;
}
void ContextVk::onVertexArrayChange()
......
......@@ -19,7 +19,7 @@ namespace rx
{
class RendererVk;
class ContextVk : public ContextImpl, public ResourceVk
class ContextVk : public ContextImpl
{
public:
ContextVk(const gl::ContextState &state, RendererVk *renderer);
......@@ -152,9 +152,7 @@ class ContextVk : public ContextImpl, public ResourceVk
VkDevice getDevice() const;
RendererVk *getRenderer() { return mRenderer; }
// TODO(jmadill): Use pipeline cache.
void invalidateCurrentPipeline();
void onVertexArrayChange();
vk::DescriptorPool *getDescriptorPool();
......@@ -167,7 +165,7 @@ class ContextVk : public ContextImpl, public ResourceVk
vk::CommandBuffer **commandBuffer);
RendererVk *mRenderer;
vk::Pipeline mCurrentPipeline;
vk::PipelineAndSerial *mCurrentPipeline;
GLenum mCurrentDrawMode;
// Keep a cached pipeline description structure that can be used to query the pipeline cache.
......
......@@ -353,7 +353,6 @@ void FramebufferVk::syncState(const gl::Context *context,
// Trigger a new set of secondary commands next time we render to this FBO,.
mLastRenderNodeSerial = Serial();
// TODO(jmadill): Use pipeline cache.
contextVk->invalidateCurrentPipeline();
}
......
......@@ -21,6 +21,7 @@
#include "libANGLE/renderer/vulkan/CompilerVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapper.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
#include "libANGLE/renderer/vulkan/VertexArrayVk.h"
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
......@@ -144,6 +145,7 @@ RendererVk::~RendererVk()
mGraphicsPipelineLayout.destroy(mDevice);
mRenderPassCache.destroy(mDevice);
mPipelineCache.destroy(mDevice);
if (mGlslangWrapper)
{
......@@ -982,4 +984,20 @@ Serial RendererVk::issueProgramSerial()
return mProgramSerialFactory.generate();
}
vk::Error RendererVk::getPipeline(const ProgramVk *programVk,
const vk::PipelineDesc &desc,
vk::PipelineAndSerial **pipelineOut)
{
ASSERT(programVk->getVertexModuleSerial() == desc.getShaderStageInfo()[0].moduleSerial);
ASSERT(programVk->getFragmentModuleSerial() == desc.getShaderStageInfo()[1].moduleSerial);
// Pull in a compatible RenderPass.
vk::RenderPass *compatibleRenderPass = nullptr;
ANGLE_TRY(getCompatibleRenderPass(desc.getRenderPassDesc(), &compatibleRenderPass));
return mPipelineCache.getPipeline(mDevice, *compatibleRenderPass, mGraphicsPipelineLayout,
programVk->getLinkedVertexModule(),
programVk->getLinkedFragmentModule(), desc, pipelineOut);
}
} // namespace rx
......@@ -112,6 +112,10 @@ class RendererVk : angle::NonCopyable
const vk::AttachmentOpsArray &ops,
vk::RenderPass **renderPassOut);
vk::Error getPipeline(const ProgramVk *programVk,
const vk::PipelineDesc &desc,
vk::PipelineAndSerial **pipelineOut);
// This should only be called from ResourceVk.
// TODO(jmadill): Keep in ContextVk to enable threaded rendering.
vk::CommandBufferNode *allocateCommandNode();
......@@ -176,6 +180,7 @@ class RendererVk : angle::NonCopyable
vk::FormatTable mFormatTable;
RenderPassCache mRenderPassCache;
PipelineCache mPipelineCache;
std::vector<vk::CommandBufferNode *> mOpenCommandGraph;
// ANGLE uses a single pipeline layout for all GL programs. It is owned here in the Renderer.
......
......@@ -47,7 +47,6 @@ void VertexArrayVk::syncState(const gl::Context *context,
ASSERT(dirtyBits.any());
// Invalidate current pipeline.
// TODO(jmadill): Use pipeline cache.
ContextVk *contextVk = vk::GetImpl(context);
contextVk->onVertexArrayChange();
......
......@@ -73,7 +73,7 @@ void UnpackStencilState(const vk::PackedStencilOpState &packedState, VkStencilOp
stateOut->reference = packedState.reference;
}
void UnpackBlendAttachmentState(vk::PackedColorBlendAttachmentState &packedState,
void UnpackBlendAttachmentState(const vk::PackedColorBlendAttachmentState &packedState,
VkPipelineColorBlendAttachmentState *stateOut)
{
stateOut->blendEnable = static_cast<VkBool32>(packedState.blendEnable);
......@@ -350,9 +350,12 @@ void PipelineDesc::initDefaults()
blendAttachmentState);
}
Error PipelineDesc::initializePipeline(RendererVk *renderer,
ProgramVk *programVk,
Pipeline *pipelineOut)
Error PipelineDesc::initializePipeline(VkDevice device,
const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout,
const ShaderModule &vertexModule,
const ShaderModule &fragmentModule,
Pipeline *pipelineOut) const
{
VkPipelineShaderStageCreateInfo shaderStages[2];
VkPipelineVertexInputStateCreateInfo vertexInputState;
......@@ -366,21 +369,19 @@ Error PipelineDesc::initializePipeline(RendererVk *renderer,
VkPipelineColorBlendStateCreateInfo blendState;
VkGraphicsPipelineCreateInfo createInfo;
ASSERT(programVk->getVertexModuleSerial() == mShaderStageInfo[0].moduleSerial);
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[0].pNext = nullptr;
shaderStages[0].flags = 0;
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderStages[0].module = programVk->getLinkedVertexModule().getHandle();
shaderStages[0].module = vertexModule.getHandle();
shaderStages[0].pName = "main";
shaderStages[0].pSpecializationInfo = nullptr;
ASSERT(programVk->getFragmentModuleSerial() == mShaderStageInfo[1].moduleSerial);
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStages[1].pNext = nullptr;
shaderStages[1].flags = 0;
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = programVk->getLinkedFragmentModule().getHandle();
shaderStages[1].module = fragmentModule.getHandle();
shaderStages[1].pName = "main";
shaderStages[1].pSpecializationInfo = nullptr;
......@@ -512,11 +513,6 @@ Error PipelineDesc::initializePipeline(RendererVk *renderer,
// TODO(jmadill): Dynamic state.
// Pull in a compatible RenderPass.
RenderPass *compatibleRenderPass = nullptr;
ANGLE_TRY(renderer->getCompatibleRenderPass(mRenderPassDesc, &compatibleRenderPass));
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
......@@ -531,17 +527,22 @@ Error PipelineDesc::initializePipeline(RendererVk *renderer,
createInfo.pDepthStencilState = &depthStencilState;
createInfo.pColorBlendState = &blendState;
createInfo.pDynamicState = nullptr;
createInfo.layout = renderer->getGraphicsPipelineLayout().getHandle();
createInfo.renderPass = compatibleRenderPass->getHandle();
createInfo.layout = pipelineLayout.getHandle();
createInfo.renderPass = compatibleRenderPass.getHandle();
createInfo.subpass = 0;
createInfo.basePipelineHandle = VK_NULL_HANDLE;
createInfo.basePipelineIndex = 0;
ANGLE_TRY(pipelineOut->initGraphics(renderer->getDevice(), createInfo));
ANGLE_TRY(pipelineOut->initGraphics(device, createInfo));
return NoError();
}
const ShaderStageInfo &PipelineDesc::getShaderStageInfo() const
{
return mShaderStageInfo;
}
void PipelineDesc::updateShaders(ProgramVk *programVk)
{
ASSERT(programVk->getVertexModuleSerial() < std::numeric_limits<uint32_t>::max());
......@@ -596,6 +597,11 @@ void PipelineDesc::updateLineWidth(float lineWidth)
mRasterizationStateInfo.lineWidth = lineWidth;
}
const RenderPassDesc &PipelineDesc::getRenderPassDesc() const
{
return mRenderPassDesc;
}
void PipelineDesc::updateRenderPassDesc(const RenderPassDesc &renderPassDesc)
{
mRenderPassDesc = renderPassDesc;
......@@ -689,6 +695,7 @@ vk::Error RenderPassCache::getCompatibleRenderPass(VkDevice device,
ASSERT(!innerCache.empty());
// Find the first element and return it.
innerCache.begin()->second.updateSerial(serial);
*renderPassOut = &innerCache.begin()->second.get();
return vk::NoError();
}
......@@ -750,4 +757,51 @@ vk::Error RenderPassCache::getRenderPassWithOps(VkDevice device,
return vk::NoError();
}
// PipelineCache implementation.
PipelineCache::PipelineCache()
{
}
PipelineCache::~PipelineCache()
{
ASSERT(mPayload.empty());
}
void PipelineCache::destroy(VkDevice device)
{
for (auto &item : mPayload)
{
item.second.get().destroy(device);
}
mPayload.clear();
}
vk::Error PipelineCache::getPipeline(VkDevice device,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const vk::ShaderModule &vertexModule,
const vk::ShaderModule &fragmentModule,
const vk::PipelineDesc &desc,
vk::PipelineAndSerial **pipelineOut)
{
auto item = mPayload.find(desc);
if (item != mPayload.end())
{
*pipelineOut = &item->second;
return vk::NoError();
}
vk::Pipeline newPipeline;
ANGLE_TRY(desc.initializePipeline(device, compatibleRenderPass, pipelineLayout, vertexModule,
fragmentModule, &newPipeline));
// The Serial will be updated outside of this query.
auto insertedItem =
mPayload.emplace(desc, vk::PipelineAndSerial(std::move(newPipeline), Serial()));
*pipelineOut = &insertedItem.first->second;
return vk::NoError();
}
} // namespace rx
......@@ -256,11 +256,18 @@ class PipelineDesc final
bool operator==(const PipelineDesc &other) const;
void initDefaults();
Error initializePipeline(RendererVk *renderer, ProgramVk *programVk, Pipeline *pipelineOut);
Error initializePipeline(VkDevice device,
const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout,
const ShaderModule &vertexModule,
const ShaderModule &fragmentModule,
Pipeline *pipelineOut) const;
void updateViewport(const gl::Rectangle &viewport, float nearPlane, float farPlane);
// Shader stage info
const ShaderStageInfo &getShaderStageInfo() const;
void updateShaders(ProgramVk *programVk);
// Vertex input state
......@@ -276,6 +283,7 @@ class PipelineDesc final
void updateLineWidth(float lineWidth);
// RenderPass description.
const RenderPassDesc &getRenderPassDesc() const;
void updateRenderPassDesc(const RenderPassDesc &renderPassDesc);
private:
......@@ -308,6 +316,9 @@ constexpr size_t PipelineDescSumOfSizes =
sizeof(RenderPassDesc);
static_assert(sizeof(PipelineDesc) == PipelineDescSumOfSizes, "Size mismatch");
using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
using PipelineAndSerial = ObjectAndSerial<Pipeline>;
} // namespace vk
} // namespace rx
......@@ -325,12 +336,19 @@ struct hash<rx::vk::AttachmentOpsArray>
{
size_t operator()(const rx::vk::AttachmentOpsArray &key) const { return key.hash(); }
};
template <>
struct hash<rx::vk::PipelineDesc>
{
size_t operator()(const rx::vk::PipelineDesc &key) const { return key.hash(); }
};
} // namespace std
namespace rx
{
// TODO(jmadill): Add cache trimming.
class RenderPassCache
// TODO(jmadill): Add cache trimming/eviction.
class RenderPassCache final : angle::NonCopyable
{
public:
RenderPassCache();
......@@ -357,6 +375,27 @@ class RenderPassCache
OuterCache mPayload;
};
// TODO(jmadill): Add cache trimming/eviction.
class PipelineCache final : angle::NonCopyable
{
public:
PipelineCache();
~PipelineCache();
void destroy(VkDevice device);
vk::Error getPipeline(VkDevice device,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const vk::ShaderModule &vertexModule,
const vk::ShaderModule &fragmentModule,
const vk::PipelineDesc &desc,
vk::PipelineAndSerial **pipelineOut);
private:
std::unordered_map<vk::PipelineDesc, vk::PipelineAndSerial> mPayload;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VK_CACHE_UTILS_H_
......@@ -631,6 +631,8 @@ class ObjectAndSerial final : angle::NonCopyable
const ObjT &get() const { return mObject; }
ObjT &get() { return mObject; }
bool valid() const { return mObject.valid(); }
private:
ObjT mObject;
Serial mQueueSerial;
......@@ -652,8 +654,6 @@ struct BufferAndMemory final : private angle::NonCopyable
vk::DeviceMemory memory;
};
using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
} // namespace vk
namespace gl_vk
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment