Commit c7918ceb by Jamie Madill Committed by Commit Bot

Vulkan: Add PipelineLayout and DescriptorSetLayout caches.

This will be necessary to support more than one PipelineLayout per instance of ANGLE. Sampler array handling requires different layouts for different sampler uses. Bug: angleproject:2462 Change-Id: I1d8b4919eed1a589002ad1898b05186f420061c7 Reviewed-on: https://chromium-review.googlesource.com/1089806 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarLuc Ferron <lucferron@chromium.org>
parent b971f499
......@@ -125,9 +125,11 @@ gl::Error ContextVk::initPipeline()
// Ensure that the RenderPass description is updated.
mPipelineDesc->updateRenderPassDesc(framebufferVk->getRenderPassDesc());
const vk::PipelineLayout &pipelineLayout = mRenderer->getGraphicsPipelineLayout();
// TODO(jmadill): Validate with ASSERT against physical device limits/caps?
ANGLE_TRY(mRenderer->getAppPipeline(programVk, *mPipelineDesc, activeAttribLocationsMask,
&mCurrentPipeline));
pipelineLayout, &mCurrentPipeline));
return gl::NoError();
}
......
......@@ -705,7 +705,6 @@ vk::Error ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t descri
// Write out to a new a descriptor set.
vk::DynamicDescriptorPool *dynamicDescriptorPool = contextVk->getDynamicDescriptorPool();
const auto &descriptorSetLayouts = renderer->getGraphicsDescriptorSetLayouts();
uint32_t potentialNewCount = descriptorSetIndex + 1;
if (potentialNewCount > mDescriptorSets.size())
......@@ -713,10 +712,10 @@ vk::Error ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t descri
mDescriptorSets.resize(potentialNewCount, VK_NULL_HANDLE);
}
const VkDescriptorSetLayout *descriptorSetLayout =
descriptorSetLayouts[descriptorSetIndex].ptr();
const vk::DescriptorSetLayout &descriptorSetLayout =
renderer->getGraphicsDescriptorSetLayout(descriptorSetIndex);
ANGLE_TRY(dynamicDescriptorPool->allocateDescriptorSets(contextVk, descriptorSetLayout, 1,
ANGLE_TRY(dynamicDescriptorPool->allocateDescriptorSets(contextVk, descriptorSetLayout.ptr(), 1,
&mDescriptorSets[descriptorSetIndex]));
return vk::NoError();
}
......@@ -772,7 +771,7 @@ vk::Error ProgramVk::updateUniforms(ContextVk *contextVk)
{
// We need to reinitialize the descriptor sets if we newly allocated buffers since we can't
// modify the descriptor sets once initialized.
ANGLE_TRY(allocateDescriptorSet(contextVk, vk::UniformBufferIndex));
ANGLE_TRY(allocateDescriptorSet(contextVk, kUniformsDescriptorSetIndex));
ANGLE_TRY(updateDefaultUniformsDescriptorSet(contextVk));
}
......@@ -857,10 +856,10 @@ gl::Error ProgramVk::updateTexturesDescriptorSet(const gl::Context *context)
}
ContextVk *contextVk = GetImplAs<ContextVk>(context);
ANGLE_TRY(allocateDescriptorSet(contextVk, vk::TextureIndex));
ANGLE_TRY(allocateDescriptorSet(contextVk, kTextureDescriptorSetIndex));
ASSERT(mUsedDescriptorSetRange.contains(1));
VkDescriptorSet descriptorSet = mDescriptorSets[1];
VkDescriptorSet descriptorSet = mDescriptorSets[kTextureDescriptorSetIndex];
// TODO(jmadill): Don't hard-code the texture limit.
ShaderTextureArray<VkDescriptorImageInfo> descriptorImageInfo;
......
......@@ -210,10 +210,13 @@ RendererVk::~RendererVk()
for (auto &descriptorSetLayout : mGraphicsDescriptorSetLayouts)
{
descriptorSetLayout.destroy(mDevice);
descriptorSetLayout.reset();
}
mGraphicsPipelineLayout.destroy(mDevice);
mGraphicsPipelineLayout.reset();
mPipelineLayoutCache.destroy(mDevice);
mDescriptorSetLayoutCache.destroy(mDevice);
mInternalPushConstantPipelineLayout.destroy(mDevice);
mRenderPassCache.destroy(mDevice);
......@@ -842,12 +845,12 @@ vk::Error RendererVk::flush(const gl::Context *context,
const vk::PipelineLayout &RendererVk::getGraphicsPipelineLayout() const
{
return mGraphicsPipelineLayout;
return mGraphicsPipelineLayout.get();
}
const std::vector<vk::DescriptorSetLayout> &RendererVk::getGraphicsDescriptorSetLayouts() const
const vk::DescriptorSetLayout &RendererVk::getGraphicsDescriptorSetLayout(uint32_t setIndex) const
{
return mGraphicsDescriptorSetLayouts;
return mGraphicsDescriptorSetLayouts[setIndex].get();
}
vk::Error RendererVk::initGraphicsPipelineLayout()
......@@ -855,84 +858,32 @@ vk::Error RendererVk::initGraphicsPipelineLayout()
ASSERT(!mGraphicsPipelineLayout.valid());
// Create two descriptor set layouts: one for default uniform info, and one for textures.
// Skip one or both if there are no uniforms.
VkDescriptorSetLayoutBinding uniformBindings[2];
uint32_t blockCount = 0;
{
VkDescriptorSetLayoutBinding &layoutBinding = uniformBindings[blockCount];
layoutBinding.binding = blockCount;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layoutBinding.pImmutableSamplers = nullptr;
blockCount++;
}
{
VkDescriptorSetLayoutBinding &layoutBinding = uniformBindings[blockCount];
layoutBinding.binding = blockCount;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layoutBinding.pImmutableSamplers = nullptr;
blockCount++;
}
{
VkDescriptorSetLayoutCreateInfo uniformInfo;
uniformInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
uniformInfo.pNext = nullptr;
uniformInfo.flags = 0;
uniformInfo.bindingCount = blockCount;
uniformInfo.pBindings = uniformBindings;
vk::DescriptorSetLayout uniformLayout;
ANGLE_TRY(uniformLayout.init(mDevice, uniformInfo));
mGraphicsDescriptorSetLayouts.push_back(std::move(uniformLayout));
}
vk::DescriptorSetLayoutDesc uniformsSetDesc;
uniformsSetDesc.update(kVertexUniformsBindingIndex, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
1);
uniformsSetDesc.update(kFragmentUniformsBindingIndex, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
1);
ANGLE_TRY(mDescriptorSetLayoutCache.getDescriptorSetLayout(mDevice, uniformsSetDesc,
&mGraphicsDescriptorSetLayouts[0]));
std::vector<VkDescriptorSetLayoutBinding> textureBindings(getMaxActiveTextures());
const uint32_t maxTextures = getMaxActiveTextures();
// TODO(jmadill): This approach might not work well for texture arrays.
for (uint32_t textureIndex = 0; textureIndex < textureBindings.size(); ++textureIndex)
vk::DescriptorSetLayoutDesc texturesSetDesc;
for (uint32_t textureIndex = 0; textureIndex < maxTextures; ++textureIndex)
{
VkDescriptorSetLayoutBinding &layoutBinding = textureBindings[textureIndex];
layoutBinding.binding = textureIndex;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
layoutBinding.pImmutableSamplers = nullptr;
// TODO(jmadll): Sampler arrays. http://anglebug.com/2462
texturesSetDesc.update(textureIndex, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1);
}
{
VkDescriptorSetLayoutCreateInfo textureInfo;
textureInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
textureInfo.pNext = nullptr;
textureInfo.flags = 0;
textureInfo.bindingCount = static_cast<uint32_t>(textureBindings.size());
textureInfo.pBindings = textureBindings.data();
vk::DescriptorSetLayout textureLayout;
ANGLE_TRY(textureLayout.init(mDevice, textureInfo));
mGraphicsDescriptorSetLayouts.push_back(std::move(textureLayout));
}
ANGLE_TRY(mDescriptorSetLayoutCache.getDescriptorSetLayout(mDevice, texturesSetDesc,
&mGraphicsDescriptorSetLayouts[1]));
VkPipelineLayoutCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.setLayoutCount = static_cast<uint32_t>(mGraphicsDescriptorSetLayouts.size());
createInfo.pSetLayouts = mGraphicsDescriptorSetLayouts[0].ptr();
createInfo.pushConstantRangeCount = 0;
createInfo.pPushConstantRanges = nullptr;
vk::PipelineLayoutDesc pipelineLayoutDesc;
pipelineLayoutDesc.updateDescriptorSetLayout(kUniformsDescriptorSetIndex, uniformsSetDesc);
pipelineLayoutDesc.updateDescriptorSetLayout(kTextureDescriptorSetIndex, texturesSetDesc);
ANGLE_TRY(mGraphicsPipelineLayout.init(mDevice, createInfo));
ANGLE_TRY(mPipelineLayoutCache.getPipelineLayout(
mDevice, pipelineLayoutDesc, mGraphicsDescriptorSetLayouts, &mGraphicsPipelineLayout));
return vk::NoError();
}
......@@ -973,6 +924,7 @@ Serial RendererVk::issueShaderSerial()
vk::Error RendererVk::getAppPipeline(const ProgramVk *programVk,
const vk::PipelineDesc &desc,
const gl::AttributesMask &activeAttribLocationsMask,
const vk::PipelineLayout &pipelineLayout,
vk::PipelineAndSerial **pipelineOut)
{
ASSERT(programVk->getVertexModuleSerial() ==
......@@ -984,7 +936,7 @@ vk::Error RendererVk::getAppPipeline(const ProgramVk *programVk,
vk::RenderPass *compatibleRenderPass = nullptr;
ANGLE_TRY(getCompatibleRenderPass(desc.getRenderPassDesc(), &compatibleRenderPass));
return mPipelineCache.getPipeline(mDevice, *compatibleRenderPass, mGraphicsPipelineLayout,
return mPipelineCache.getPipeline(mDevice, *compatibleRenderPass, pipelineLayout,
activeAttribLocationsMask, programVk->getLinkedVertexModule(),
programVk->getLinkedFragmentModule(), desc, pipelineOut);
}
......
......@@ -108,6 +108,7 @@ class RendererVk : angle::NonCopyable
vk::Error getAppPipeline(const ProgramVk *programVk,
const vk::PipelineDesc &desc,
const gl::AttributesMask &activeAttribLocationsMask,
const vk::PipelineLayout &pipelineLayout,
vk::PipelineAndSerial **pipelineOut);
// For getting a vk::Pipeline for an internal draw call. Use an explicit RenderPass.
......@@ -122,10 +123,12 @@ class RendererVk : angle::NonCopyable
// TODO(jmadill): Keep in ContextVk to enable threaded rendering.
vk::CommandGraph *getCommandGraph();
// TODO(jmadill): Use PipelineLayout cache. http://anglebug.com/2462
const vk::PipelineLayout &getGraphicsPipelineLayout() const;
const std::vector<vk::DescriptorSetLayout> &getGraphicsDescriptorSetLayouts() const;
const vk::DescriptorSetLayout &getGraphicsDescriptorSetLayout(uint32_t setIndex) const;
// Used in internal shaders.
// TODO(jmadill): Use PipelineLayout cache. http://anglebug.com/2462
vk::Error getInternalPushConstantPipelineLayout(const vk::PipelineLayout **pipelineLayoutOut);
// Issues a new serial for linked shader modules. Used in the pipeline cache.
......@@ -187,12 +190,18 @@ class RendererVk : angle::NonCopyable
// See CommandGraph.h for a desription of the Command Graph.
vk::CommandGraph mCommandGraph;
// ANGLE uses a single pipeline layout for all GL programs. It is owned here in the Renderer.
// See the design doc for an overview of the pipeline layout structure.
vk::PipelineLayout mGraphicsPipelineLayout;
std::vector<vk::DescriptorSetLayout> mGraphicsDescriptorSetLayouts;
// ANGLE uses a PipelineLayout cache to store compatible pipeline layouts.
PipelineLayoutCache mPipelineLayoutCache;
// DescriptorSetLayouts are also managed in a cache.
DescriptorSetLayoutCache mDescriptorSetLayoutCache;
// TODO(jmadill): Only use the pipeline layout cache. http://anglebug.com/2462
vk::BindingPointer<vk::PipelineLayout> mGraphicsPipelineLayout;
vk::DescriptorSetLayoutPointerArray mGraphicsDescriptorSetLayouts;
// Used for internal shaders.
// TODO(jmadill): Use PipelineLayout cache. http://anglebug.com/2462
vk::PipelineLayout mInternalPushConstantPipelineLayout;
// Internal shader library.
......
......@@ -860,6 +860,108 @@ bool operator==(const AttachmentOpsArray &lhs, const AttachmentOpsArray &rhs)
{
return (memcmp(&lhs, &rhs, sizeof(AttachmentOpsArray)) == 0);
}
// DescriptorSetLayoutDesc implementation.
DescriptorSetLayoutDesc::DescriptorSetLayoutDesc() : mPackedDescriptorSetLayout{}
{
}
DescriptorSetLayoutDesc::~DescriptorSetLayoutDesc() = default;
DescriptorSetLayoutDesc::DescriptorSetLayoutDesc(const DescriptorSetLayoutDesc &other) = default;
DescriptorSetLayoutDesc &DescriptorSetLayoutDesc::operator=(const DescriptorSetLayoutDesc &other) =
default;
size_t DescriptorSetLayoutDesc::hash() const
{
return angle::ComputeGenericHash(mPackedDescriptorSetLayout);
}
bool DescriptorSetLayoutDesc::operator==(const DescriptorSetLayoutDesc &other) const
{
return (memcmp(&mPackedDescriptorSetLayout, &other.mPackedDescriptorSetLayout,
sizeof(mPackedDescriptorSetLayout)) == 0);
}
void DescriptorSetLayoutDesc::update(uint32_t bindingIndex, VkDescriptorType type, uint32_t count)
{
ASSERT(static_cast<size_t>(type) < std::numeric_limits<uint16_t>::max());
ASSERT(count < std::numeric_limits<uint16_t>::max());
PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
packedBinding.type = static_cast<uint16_t>(type);
packedBinding.count = static_cast<uint16_t>(count);
}
void DescriptorSetLayoutDesc::unpackBindings(DescriptorSetLayoutBindingVector *bindings) const
{
for (uint32_t bindingIndex = 0; bindingIndex < kMaxDescriptorSetLayoutBindings; ++bindingIndex)
{
const PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
if (packedBinding.count == 0)
continue;
VkDescriptorSetLayoutBinding binding;
binding.binding = bindingIndex;
binding.descriptorCount = packedBinding.count;
binding.descriptorType = static_cast<VkDescriptorType>(packedBinding.type);
binding.stageFlags = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
binding.pImmutableSamplers = nullptr;
bindings->push_back(binding);
}
}
// PipelineLayoutDesc implementation.
PipelineLayoutDesc::PipelineLayoutDesc() : mDescriptorSetLayouts{}, mPushConstantRanges{}
{
}
PipelineLayoutDesc::~PipelineLayoutDesc() = default;
PipelineLayoutDesc::PipelineLayoutDesc(const PipelineLayoutDesc &other) = default;
PipelineLayoutDesc &PipelineLayoutDesc::operator=(const PipelineLayoutDesc &rhs)
{
mDescriptorSetLayouts = rhs.mDescriptorSetLayouts;
mPushConstantRanges = rhs.mPushConstantRanges;
return *this;
}
size_t PipelineLayoutDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool PipelineLayoutDesc::operator==(const PipelineLayoutDesc &other) const
{
return memcmp(this, &other, sizeof(PipelineLayoutDesc)) == 0;
}
void PipelineLayoutDesc::updateDescriptorSetLayout(uint32_t setIndex,
const DescriptorSetLayoutDesc &desc)
{
ASSERT(setIndex < mDescriptorSetLayouts.size());
mDescriptorSetLayouts[setIndex] = desc;
}
void PipelineLayoutDesc::updatePushConstantRange(gl::ShaderType shaderType,
uint32_t offset,
uint32_t size)
{
ASSERT(shaderType == gl::ShaderType::Vertex || shaderType == gl::ShaderType::Fragment);
PackedPushConstantRange &packed = mPushConstantRanges[static_cast<size_t>(shaderType)];
packed.offset = offset;
packed.size = size;
}
const PushConstantRangeArray<PackedPushConstantRange> &PipelineLayoutDesc::getPushConstantRanges()
const
{
return mPushConstantRanges;
}
} // namespace vk
// RenderPassCache implementation.
......@@ -1021,4 +1123,123 @@ void PipelineCache::populate(const vk::PipelineDesc &desc, vk::Pipeline &&pipeli
mPayload.emplace(desc, vk::PipelineAndSerial(std::move(pipeline), Serial()));
}
// DescriptorSetLayoutCache implementation.
DescriptorSetLayoutCache::DescriptorSetLayoutCache() = default;
DescriptorSetLayoutCache::~DescriptorSetLayoutCache()
{
ASSERT(mPayload.empty());
}
void DescriptorSetLayoutCache::destroy(VkDevice device)
{
for (auto &item : mPayload)
{
vk::SharedDescriptorSetLayout &layout = item.second;
ASSERT(!layout.isReferenced());
layout.get().destroy(device);
}
mPayload.clear();
}
vk::Error DescriptorSetLayoutCache::getDescriptorSetLayout(
VkDevice device,
const vk::DescriptorSetLayoutDesc &desc,
vk::BindingPointer<vk::DescriptorSetLayout> *descriptorSetLayoutOut)
{
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::SharedDescriptorSetLayout &layout = iter->second;
descriptorSetLayoutOut->set(&layout);
return vk::NoError();
}
// We must unpack the descriptor set layout description.
vk::DescriptorSetLayoutBindingVector bindings;
desc.unpackBindings(&bindings);
VkDescriptorSetLayoutCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.bindingCount = static_cast<uint32_t>(bindings.size());
createInfo.pBindings = bindings.data();
vk::DescriptorSetLayout newLayout;
ANGLE_TRY(newLayout.init(device, createInfo));
auto insertedItem = mPayload.emplace(desc, vk::SharedDescriptorSetLayout(std::move(newLayout)));
vk::SharedDescriptorSetLayout &insertedLayout = insertedItem.first->second;
descriptorSetLayoutOut->set(&insertedLayout);
return vk::NoError();
}
// PipelineLayoutCache implementation.
PipelineLayoutCache::PipelineLayoutCache() = default;
PipelineLayoutCache::~PipelineLayoutCache()
{
ASSERT(mPayload.empty());
}
void PipelineLayoutCache::destroy(VkDevice device)
{
for (auto &item : mPayload)
{
vk::SharedPipelineLayout &layout = item.second;
layout.get().destroy(device);
}
mPayload.clear();
}
vk::Error PipelineLayoutCache::getPipelineLayout(
VkDevice device,
const vk::PipelineLayoutDesc &desc,
const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts,
vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut)
{
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::SharedPipelineLayout &layout = iter->second;
pipelineLayoutOut->set(&layout);
return vk::NoError();
}
// Note this does not handle gaps in descriptor set layouts gracefully.
angle::FixedVector<VkDescriptorSetLayout, vk::kMaxDescriptorSetLayouts> setLayoutHandles;
for (const vk::BindingPointer<vk::DescriptorSetLayout> &layoutPtr : descriptorSetLayouts)
{
VkDescriptorSetLayout setLayout = layoutPtr.get().getHandle();
if (setLayout != VK_NULL_HANDLE)
{
setLayoutHandles.push_back(setLayout);
}
}
// No pipeline layout found. We must create a new one.
VkPipelineLayoutCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.setLayoutCount = static_cast<uint32_t>(setLayoutHandles.size());
createInfo.pSetLayouts = setLayoutHandles.data();
// TODO(jmadill): Init push constant ranges. http://anglebug.com/2462
createInfo.pushConstantRangeCount = 0;
createInfo.pPushConstantRanges = nullptr;
vk::PipelineLayout newLayout;
ANGLE_TRY(newLayout.init(device, createInfo));
auto insertedItem = mPayload.emplace(desc, vk::SharedPipelineLayout(std::move(newLayout)));
vk::SharedPipelineLayout &insertedLayout = insertedItem.first->second;
pipelineLayoutOut->set(&insertedLayout);
return vk::NoError();
}
} // namespace rx
......@@ -21,6 +21,88 @@ namespace vk
{
class ImageHelper;
// This is a very simple RefCount class that has no autoreleasing. Used in the descriptor set and
// pipeline layout caches.
template <typename T>
class RefCounted : angle::NonCopyable
{
public:
RefCounted() : mRefCount(0) {}
explicit RefCounted(T &&newObject) : mRefCount(0), mObject(std::move(newObject)) {}
~RefCounted() { ASSERT(mRefCount == 0 && !mObject.valid()); }
RefCounted(RefCounted &&copy) : mRefCount(copy.mRefCount), mObject(std::move(copy.mObject))
{
copy.mRefCount = 0;
}
RefCounted &operator=(RefCounted &&rhs)
{
std::swap(mRefCount, rhs.mRefCount);
mObject = std::move(rhs.mObject);
return *this;
}
void addRef()
{
ASSERT(mRefCount != std::numeric_limits<uint32_t>::max());
mRefCount++;
}
void releaseRef()
{
ASSERT(isReferenced());
mRefCount--;
}
bool isReferenced() const { return mRefCount != 0; }
T &get() { return mObject; }
const T &get() const { return mObject; }
private:
uint32_t mRefCount;
T mObject;
};
template <typename T>
class BindingPointer final : angle::NonCopyable
{
public:
BindingPointer() : mRefCounted(nullptr) {}
~BindingPointer() { reset(); }
void set(RefCounted<T> *refCounted)
{
if (mRefCounted)
{
mRefCounted->releaseRef();
}
mRefCounted = refCounted;
if (mRefCounted)
{
mRefCounted->addRef();
}
}
void reset() { set(nullptr); }
T &get() { return mRefCounted->get(); }
const T &get() const { return mRefCounted->get(); }
bool valid() const { return mRefCounted != nullptr; }
private:
RefCounted<T> *mRefCounted;
};
using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
using PipelineAndSerial = ObjectAndSerial<Pipeline>;
using SharedDescriptorSetLayout = RefCounted<DescriptorSetLayout>;
using SharedPipelineLayout = RefCounted<PipelineLayout>;
// Packed Vk resource descriptions.
// Most Vk types use many more bits than required to represent the underlying data.
// Since ANGLE wants cache things like RenderPasses and Pipeline State Objects using
......@@ -336,17 +418,105 @@ class PipelineDesc final
// This is not guaranteed by the spec, but is validated by a compile-time check.
// No gaps or padding at the end ensures that hashing and memcmp checks will not run
// into uninitialized memory regions.
constexpr size_t PipelineDescSumOfSizes =
constexpr size_t kPipelineDescSumOfSizes =
sizeof(ShaderStageInfo) + sizeof(VertexInputBindings) + sizeof(VertexInputAttributes) +
sizeof(PackedInputAssemblyInfo) + sizeof(VkViewport) + sizeof(VkRect2D) +
sizeof(PackedRasterizationStateInfo) + sizeof(PackedMultisampleStateInfo) +
sizeof(PackedDepthStencilStateInfo) + sizeof(PackedColorBlendStateInfo) +
sizeof(RenderPassDesc);
static_assert(sizeof(PipelineDesc) == PipelineDescSumOfSizes, "Size mismatch");
static_assert(sizeof(PipelineDesc) == kPipelineDescSumOfSizes, "Size mismatch");
using RenderPassAndSerial = ObjectAndSerial<RenderPass>;
using PipelineAndSerial = ObjectAndSerial<Pipeline>;
constexpr uint32_t kMaxDescriptorSetLayoutBindings = gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES;
using DescriptorSetLayoutBindingVector =
angle::FixedVector<VkDescriptorSetLayoutBinding, kMaxDescriptorSetLayoutBindings>;
// A packed description of a descriptor set layout. Use similarly to RenderPassDesc and
// PipelineDesc. Currently we only need to differentiate layouts based on sampler usage. In the
// future we could generalize this.
class DescriptorSetLayoutDesc final
{
public:
DescriptorSetLayoutDesc();
~DescriptorSetLayoutDesc();
DescriptorSetLayoutDesc(const DescriptorSetLayoutDesc &other);
DescriptorSetLayoutDesc &operator=(const DescriptorSetLayoutDesc &other);
size_t hash() const;
bool operator==(const DescriptorSetLayoutDesc &other) const;
void update(uint32_t bindingIndex, VkDescriptorType type, uint32_t count);
void unpackBindings(DescriptorSetLayoutBindingVector *bindings) const;
private:
struct PackedDescriptorSetBinding
{
uint16_t type; // Stores a packed VkDescriptorType descriptorType.
uint16_t count; // Stores a packed uint32_t descriptorCount.
// We currently make all descriptors available in the VS and FS shaders.
};
static_assert(sizeof(PackedDescriptorSetBinding) == sizeof(uint32_t), "Unexpected size");
// This is a compact representation of a descriptor set layout.
std::array<PackedDescriptorSetBinding, kMaxDescriptorSetLayoutBindings>
mPackedDescriptorSetLayout;
};
// The following are for caching descriptor set layouts. Limited to max two descriptor set layouts
// and two push constants. One push constant per shader stage. This can be extended in the future.
constexpr size_t kMaxDescriptorSetLayouts = 2;
constexpr size_t kMaxPushConstantRanges = 2;
struct PackedPushConstantRange
{
uint32_t offset;
uint32_t size;
};
template <typename T>
using DescriptorSetLayoutArray = std::array<T, kMaxDescriptorSetLayouts>;
using DescriptorSetLayoutPointerArray =
DescriptorSetLayoutArray<BindingPointer<DescriptorSetLayout>>;
template <typename T>
using PushConstantRangeArray = std::array<T, kMaxPushConstantRanges>;
class PipelineLayoutDesc final
{
public:
PipelineLayoutDesc();
~PipelineLayoutDesc();
PipelineLayoutDesc(const PipelineLayoutDesc &other);
PipelineLayoutDesc &operator=(const PipelineLayoutDesc &rhs);
size_t hash() const;
bool operator==(const PipelineLayoutDesc &other) const;
void updateDescriptorSetLayout(uint32_t setIndex, const DescriptorSetLayoutDesc &desc);
void updatePushConstantRange(gl::ShaderType shaderType, uint32_t offset, uint32_t size);
const PushConstantRangeArray<PackedPushConstantRange> &getPushConstantRanges() const;
private:
DescriptorSetLayoutArray<DescriptorSetLayoutDesc> mDescriptorSetLayouts;
PushConstantRangeArray<PackedPushConstantRange> mPushConstantRanges;
// Verify the arrays are properly packed.
static_assert(sizeof(decltype(mDescriptorSetLayouts)) ==
(sizeof(DescriptorSetLayoutDesc) * kMaxDescriptorSetLayouts),
"Unexpected size");
static_assert(sizeof(decltype(mPushConstantRanges)) ==
(sizeof(PackedPushConstantRange) * kMaxPushConstantRanges),
"Unexpected size");
};
// Verify the structure is properly packed.
static_assert(sizeof(PipelineLayoutDesc) ==
(sizeof(DescriptorSetLayoutArray<DescriptorSetLayoutDesc>) +
sizeof(std::array<PackedPushConstantRange, kMaxPushConstantRanges>)),
"Unexpected Size");
} // namespace vk
} // namespace rx
......@@ -371,6 +541,17 @@ struct hash<rx::vk::PipelineDesc>
size_t operator()(const rx::vk::PipelineDesc &key) const { return key.hash(); }
};
template <>
struct hash<rx::vk::DescriptorSetLayoutDesc>
{
size_t operator()(const rx::vk::DescriptorSetLayoutDesc &key) const { return key.hash(); }
};
template <>
struct hash<rx::vk::PipelineLayoutDesc>
{
size_t operator()(const rx::vk::PipelineLayoutDesc &key) const { return key.hash(); }
};
} // namespace std
namespace rx
......@@ -426,6 +607,47 @@ class PipelineCache final : angle::NonCopyable
std::unordered_map<vk::PipelineDesc, vk::PipelineAndSerial> mPayload;
};
class DescriptorSetLayoutCache final : angle::NonCopyable
{
public:
DescriptorSetLayoutCache();
~DescriptorSetLayoutCache();
void destroy(VkDevice device);
vk::Error getDescriptorSetLayout(
VkDevice device,
const vk::DescriptorSetLayoutDesc &desc,
vk::BindingPointer<vk::DescriptorSetLayout> *descriptorSetLayoutOut);
private:
std::unordered_map<vk::DescriptorSetLayoutDesc, vk::SharedDescriptorSetLayout> mPayload;
};
class PipelineLayoutCache final : angle::NonCopyable
{
public:
PipelineLayoutCache();
~PipelineLayoutCache();
void destroy(VkDevice device);
vk::Error getPipelineLayout(VkDevice device,
const vk::PipelineLayoutDesc &desc,
const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts,
vk::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut);
private:
std::unordered_map<vk::PipelineLayoutDesc, vk::SharedPipelineLayout> mPayload;
};
// Some descriptor set and pipeline layout constants.
constexpr uint32_t kVertexUniformsBindingIndex = 0;
constexpr uint32_t kFragmentUniformsBindingIndex = 1;
constexpr uint32_t kUniformsDescriptorSetIndex = 0;
constexpr uint32_t kTextureDescriptorSetIndex = 1;
constexpr uint32_t kDescriptorSetCount = 2;
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VK_CACHE_UTILS_H_
......@@ -331,12 +331,12 @@ Error DynamicDescriptorPool::allocateDescriptorSets(
Error DynamicDescriptorPool::allocateNewPool(const VkDevice &device)
{
VkDescriptorPoolSize poolSizes[DescriptorPoolIndexCount];
poolSizes[UniformBufferIndex].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[UniformBufferIndex].descriptorCount =
VkDescriptorPoolSize poolSizes[kDescriptorSetCount];
poolSizes[kUniformsDescriptorSetIndex].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[kUniformsDescriptorSetIndex].descriptorCount =
mUniformBufferDescriptorsPerSet * mMaxSetsPerPool;
poolSizes[TextureIndex].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[TextureIndex].descriptorCount =
poolSizes[kTextureDescriptorSetIndex].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[kTextureDescriptorSetIndex].descriptorCount =
mCombinedImageSamplerDescriptorsPerSet * mMaxSetsPerPool;
VkDescriptorPoolCreateInfo descriptorPoolInfo;
......@@ -346,7 +346,7 @@ Error DynamicDescriptorPool::allocateNewPool(const VkDevice &device)
descriptorPoolInfo.maxSets = mMaxSetsPerPool;
// Reserve pools for uniform blocks and textures.
descriptorPoolInfo.poolSizeCount = DescriptorPoolIndexCount;
descriptorPoolInfo.poolSizeCount = kDescriptorSetCount;
descriptorPoolInfo.pPoolSizes = poolSizes;
mCurrentAllocatedDescriptorSetCount = 0;
......
......@@ -86,13 +86,6 @@ class DynamicBuffer : angle::NonCopyable
// Uses DescriptorPool to allocate descriptor sets as needed. If the descriptor pool
// is full, we simply allocate a new pool to keep allocating descriptor sets as needed and
// leave the renderer take care of the life time of the pools that become unused.
enum DescriptorPoolIndex : uint8_t
{
UniformBufferIndex = 0,
TextureIndex = 1,
DescriptorPoolIndexCount = 2
};
class DynamicDescriptorPool final : angle::NonCopyable
{
public:
......
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