Commit 9f2a8613 by Jamie Madill Committed by Commit Bot

Vulkan: Implement a RenderPass cache.

This cache replaces the RenderPass-per-Framebuffer approach. Although the concepts of a RenderPass are closely associated with rendering to a Framebuffer, there can be multiple RenderPasses used with a single FBO, especially considering the nature of Load and Store operations. This code will then lend itself to the implementation of the deferred RenderPasses, which are created on flush. These RenderPasses won't be owned by a Framebuffer. Bug: angleproject:2264 Change-Id: I4dce07c302118f7e05f5225e2a3b0569ad1e52bf Reviewed-on: https://chromium-review.googlesource.com/789534Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent e218f15f
......@@ -278,8 +278,10 @@ gl::Error ContextVk::initPipeline(const gl::Context *context)
mCurrentInputAssemblyState.topology = gl_vk::GetPrimitiveTopology(mCurrentDrawMode);
const vk::RenderPassDesc &desc = vkFBO->getRenderPassDesc(context);
vk::RenderPass *renderPass = nullptr;
ANGLE_TRY_RESULT(vkFBO->getRenderPass(context, device), renderPass);
ANGLE_TRY(mRenderer->getCompatibleRenderPass(desc, &renderPass));
ASSERT(renderPass && renderPass->valid());
const vk::PipelineLayout &pipelineLayout = programVk->getPipelineLayout();
......
......@@ -81,12 +81,12 @@ FramebufferVk *FramebufferVk::CreateDefaultFBO(const gl::FramebufferState &state
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state)
: FramebufferImpl(state), mBackbuffer(nullptr), mRenderPass(), mFramebuffer()
: FramebufferImpl(state), mBackbuffer(nullptr), mRenderPassDesc(), mFramebuffer()
{
}
FramebufferVk::FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer)
: FramebufferImpl(state), mBackbuffer(backbuffer), mRenderPass(), mFramebuffer()
: FramebufferImpl(state), mBackbuffer(backbuffer), mRenderPassDesc(), mFramebuffer()
{
}
......@@ -98,7 +98,6 @@ void FramebufferVk::destroy(const gl::Context *context)
{
RendererVk *renderer = vk::GetImpl(context)->getRenderer();
renderer->releaseResource(*this, &mRenderPass);
renderer->releaseResource(*this, &mFramebuffer);
}
......@@ -106,7 +105,6 @@ void FramebufferVk::destroyDefault(const egl::Display *display)
{
VkDevice device = vk::GetImpl(display)->getRenderer()->getDevice();
mRenderPass.destroy(device);
mFramebuffer.destroy(device);
}
......@@ -363,7 +361,7 @@ void FramebufferVk::syncState(const gl::Context *context,
ASSERT(dirtyBits.any());
// TODO(jmadill): Smarter update.
renderer->releaseResource(*this, &mRenderPass);
mRenderPassDesc.reset();
renderer->releaseResource(*this, &mFramebuffer);
renderer->onReleaseRenderPass(this);
......@@ -371,12 +369,11 @@ void FramebufferVk::syncState(const gl::Context *context,
contextVk->invalidateCurrentPipeline();
}
gl::ErrorOrResult<vk::RenderPass *> FramebufferVk::getRenderPass(const gl::Context *context,
VkDevice device)
const vk::RenderPassDesc &FramebufferVk::getRenderPassDesc(const gl::Context *context)
{
if (mRenderPass.valid())
if (mRenderPassDesc.valid())
{
return &mRenderPass;
return mRenderPassDesc.value();
}
vk::RenderPassDesc desc;
......@@ -388,7 +385,7 @@ gl::ErrorOrResult<vk::RenderPass *> FramebufferVk::getRenderPass(const gl::Conte
if (colorAttachment.isAttached())
{
RenderTargetVk *renderTarget = nullptr;
ANGLE_TRY(colorAttachment.getRenderTarget(context, &renderTarget));
ANGLE_SWALLOW_ERR(colorAttachment.getRenderTarget(context, &renderTarget));
VkAttachmentDescription *colorDesc = desc.nextColorAttachment();
......@@ -415,7 +412,7 @@ gl::ErrorOrResult<vk::RenderPass *> FramebufferVk::getRenderPass(const gl::Conte
if (depthStencilAttachment && depthStencilAttachment->isAttached())
{
RenderTargetVk *renderTarget = nullptr;
ANGLE_TRY(depthStencilAttachment->getRenderTarget(context, &renderTarget));
ANGLE_SWALLOW_ERR(depthStencilAttachment->getRenderTarget(context, &renderTarget));
VkAttachmentDescription *depthStencilDesc = desc.nextDepthStencilAttachment();
......@@ -430,13 +427,12 @@ gl::ErrorOrResult<vk::RenderPass *> FramebufferVk::getRenderPass(const gl::Conte
depthStencilDesc->finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
ANGLE_TRY(vk::InitializeRenderPassFromDesc(device, desc, &mRenderPass));
return &mRenderPass;
mRenderPassDesc = desc;
return mRenderPassDesc.value();
}
gl::ErrorOrResult<vk::Framebuffer *> FramebufferVk::getFramebuffer(const gl::Context *context,
VkDevice device)
RendererVk *rendererVk)
{
// If we've already created our cached Framebuffer, return it.
if (mFramebuffer.valid())
......@@ -444,10 +440,13 @@ gl::ErrorOrResult<vk::Framebuffer *> FramebufferVk::getFramebuffer(const gl::Con
return &mFramebuffer;
}
const vk::RenderPassDesc &desc = getRenderPassDesc(context);
vk::RenderPass *renderPass = nullptr;
ANGLE_TRY_RESULT(getRenderPass(context, device), renderPass);
ANGLE_TRY(rendererVk->getCompatibleRenderPass(desc, &renderPass));
// If we've a Framebuffer provided by a Surface (default FBO/backbuffer), query it.
VkDevice device = rendererVk->getDevice();
if (mBackbuffer)
{
return mBackbuffer->getCurrentFramebuffer(device, *renderPass);
......@@ -490,7 +489,7 @@ gl::ErrorOrResult<vk::Framebuffer *> FramebufferVk::getFramebuffer(const gl::Con
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.pNext = nullptr;
framebufferInfo.flags = 0;
framebufferInfo.renderPass = mRenderPass.getHandle();
framebufferInfo.renderPass = renderPass->getHandle();
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = static_cast<uint32_t>(attachmentsSize.width);
......@@ -509,7 +508,7 @@ gl::Error FramebufferVk::getSamplePosition(size_t index, GLfloat *xy) const
}
gl::Error FramebufferVk::beginRenderPass(const gl::Context *context,
VkDevice device,
RendererVk *rendererVk,
vk::CommandBuffer *commandBuffer,
Serial queueSerial)
{
......@@ -533,11 +532,13 @@ gl::Error FramebufferVk::beginRenderPass(const gl::Context *context,
}
vk::Framebuffer *framebuffer = nullptr;
ANGLE_TRY_RESULT(getFramebuffer(context, device), framebuffer);
ANGLE_TRY_RESULT(getFramebuffer(context, rendererVk), framebuffer);
ASSERT(framebuffer && framebuffer->valid());
const vk::RenderPassDesc &desc = getRenderPassDesc(context);
vk::RenderPass *renderPass = nullptr;
ANGLE_TRY_RESULT(getRenderPass(context, device), renderPass);
ANGLE_TRY(rendererVk->getMatchingRenderPass(desc, &renderPass));
ASSERT(renderPass && renderPass->valid());
// TODO(jmadill): Proper clear value implementation.
......
......@@ -15,6 +15,7 @@
namespace rx
{
class RendererVk;
class RenderTargetVk;
class WindowSurfaceVk;
......@@ -84,22 +85,22 @@ class FramebufferVk : public FramebufferImpl, public ResourceVk
gl::Error getSamplePosition(size_t index, GLfloat *xy) const override;
gl::Error beginRenderPass(const gl::Context *context,
VkDevice device,
RendererVk *rendererVk,
vk::CommandBuffer *commandBuffer,
Serial queueSerial);
gl::ErrorOrResult<vk::RenderPass *> getRenderPass(const gl::Context *context, VkDevice device);
const vk::RenderPassDesc &getRenderPassDesc(const gl::Context *context);
private:
FramebufferVk(const gl::FramebufferState &state);
FramebufferVk(const gl::FramebufferState &state, WindowSurfaceVk *backbuffer);
gl::ErrorOrResult<vk::Framebuffer *> getFramebuffer(const gl::Context *context,
VkDevice device);
RendererVk *rendererVk);
WindowSurfaceVk *mBackbuffer;
vk::RenderPass mRenderPass;
Optional<vk::RenderPassDesc> mRenderPassDesc;
vk::Framebuffer mFramebuffer;
};
......
......@@ -84,6 +84,63 @@ VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT flags,
} // anonymous namespace
// RenderPassCache implementation.
RenderPassCache::RenderPassCache()
{
}
RenderPassCache::~RenderPassCache()
{
ASSERT(mPayload.empty());
}
void RenderPassCache::destroy(VkDevice device)
{
for (auto &renderPassIt : mPayload)
{
renderPassIt.second.get().destroy(device);
}
mPayload.clear();
}
vk::Error RenderPassCache::getCompatibleRenderPass(VkDevice device,
Serial serial,
const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut)
{
// TODO(jmadill): Return compatible RenderPass when possible.
return getMatchingRenderPass(device, serial, desc, renderPassOut);
}
vk::Error RenderPassCache::getMatchingRenderPass(VkDevice device,
Serial serial,
const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut)
{
auto it = mPayload.find(desc);
if (it != mPayload.end())
{
// Update the serial before we return.
// TODO(jmadill): Could possibly use an MRU cache here.
it->second.updateSerial(serial);
*renderPassOut = &it->second.get();
return vk::NoError();
}
vk::RenderPass newRenderPass;
ANGLE_TRY(vk::InitializeRenderPassFromDesc(device, desc, &newRenderPass));
vk::RenderPassAndSerial withSerial(std::move(newRenderPass), serial);
auto insertPos = mPayload.emplace(desc, std::move(withSerial));
*renderPassOut = &insertPos.first->second.get();
// TODO(jmadill): Trim cache, and pre-populate with the most common RPs on startup.
return vk::NoError();
}
// RendererVk implementation.
RendererVk::RendererVk()
: mCapsInitialized(false),
mInstance(VK_NULL_HANDLE),
......@@ -112,6 +169,8 @@ RendererVk::~RendererVk()
}
}
mRenderPassCache.destroy(mDevice);
if (mGlslangWrapper)
{
GlslangWrapper::ReleaseReference();
......@@ -798,8 +857,7 @@ gl::Error RendererVk::ensureInRenderPass(const gl::Context *context, Framebuffer
{
endRenderPass();
}
ANGLE_TRY(
framebufferVk->beginRenderPass(context, mDevice, &mCommandBuffer, mCurrentQueueSerial));
ANGLE_TRY(framebufferVk->beginRenderPass(context, this, &mCommandBuffer, mCurrentQueueSerial));
mCurrentRenderPassFramebuffer = framebufferVk;
return gl::NoError();
}
......@@ -832,4 +890,18 @@ bool RendererVk::isSerialInUse(Serial serial)
return serial > mLastCompletedQueueSerial;
}
vk::Error RendererVk::getCompatibleRenderPass(const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut)
{
return mRenderPassCache.getCompatibleRenderPass(mDevice, mCurrentQueueSerial, desc,
renderPassOut);
}
vk::Error RendererVk::getMatchingRenderPass(const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut)
{
return mRenderPassCache.getMatchingRenderPass(mDevice, mCurrentQueueSerial, desc,
renderPassOut);
}
} // namespace rx
......@@ -33,6 +33,28 @@ namespace vk
struct Format;
}
// TODO(jmadill): Add cache trimming.
class RenderPassCache
{
public:
RenderPassCache();
~RenderPassCache();
void destroy(VkDevice device);
vk::Error getCompatibleRenderPass(VkDevice device,
Serial serial,
const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut);
vk::Error getMatchingRenderPass(VkDevice device,
Serial serial,
const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut);
private:
std::unordered_map<vk::RenderPassDesc, vk::RenderPassAndSerial> mPayload;
};
class RendererVk : angle::NonCopyable
{
public:
......@@ -115,6 +137,10 @@ class RendererVk : angle::NonCopyable
return mFormatTable[internalFormat];
}
vk::Error getCompatibleRenderPass(const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut);
vk::Error getMatchingRenderPass(const vk::RenderPassDesc &desc, vk::RenderPass **renderPassOut);
private:
void ensureCapsInitialized() const;
void generateCaps(gl::Caps *outCaps,
......@@ -157,6 +183,8 @@ class RendererVk : angle::NonCopyable
// TODO(jmadill): Don't keep a single renderpass in the Renderer.
FramebufferVk *mCurrentRenderPassFramebuffer;
RenderPassCache mRenderPassCache;
};
} // namespace rx
......
......@@ -9,6 +9,7 @@
#include "renderervk_utils.h"
#include "libANGLE/SizedMRUCache.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
......@@ -1287,6 +1288,19 @@ VkAttachmentDescription *RenderPassDesc::nextDepthStencilAttachment()
return &attachmentDescs[depthStencilAttachmentCount++];
}
size_t RenderPassDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool RenderPassDesc::operator==(const RenderPassDesc &other) const
{
return colorAttachmentCount == other.colorAttachmentCount &&
depthStencilAttachmentCount == other.depthStencilAttachmentCount &&
(memcmp(attachmentDescs.data(), other.attachmentDescs.data(),
sizeof(VkAttachmentDescription) * attachmentDescs.size()) == 0);
}
uint32_t RenderPassDesc::attachmentCount() const
{
return (colorAttachmentCount + depthStencilAttachmentCount);
......
......@@ -624,6 +624,11 @@ class ObjectAndSerial final : angle::NonCopyable
}
Serial queueSerial() const { return mQueueSerial; }
void updateSerial(Serial newSerial)
{
ASSERT(newSerial >= mQueueSerial);
mQueueSerial = newSerial;
}
const ObjT &get() const { return mObject; }
ObjT &get() { return mObject; }
......@@ -667,6 +672,7 @@ class CommandBufferAndState : public vk::CommandBuffer
using CommandBufferAndSerial = ObjectAndSerial<CommandBufferAndState>;
using FenceAndSerial = ObjectAndSerial<Fence>;
using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
struct RenderPassDesc final
{
......@@ -680,6 +686,9 @@ struct RenderPassDesc final
VkAttachmentDescription *nextDepthStencilAttachment();
uint32_t attachmentCount() const;
size_t hash() const;
bool operator==(const RenderPassDesc &other) const;
// Fully padded out, with no bools, to avoid any undefined behaviour.
uint32_t colorAttachmentCount;
uint32_t depthStencilAttachmentCount;
......@@ -717,4 +726,14 @@ VkFrontFace GetFrontFace(GLenum frontFace);
std::ostream &operator<<(std::ostream &stream, const rx::vk::Error &error);
// Introduce a std::hash for a RenderPassDesc
namespace std
{
template <>
struct hash<rx::vk::RenderPassDesc>
{
size_t operator()(const rx::vk::RenderPassDesc &key) const { return key.hash(); }
};
} // namespace std
#endif // LIBANGLE_RENDERER_VULKAN_RENDERERVK_UTILS_H_
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