Commit 6fc0066a by Jamie Madill Committed by Commit Bot

Vulkan: Add a sampler cache.

This uses a packed sampler description to re-use samplers for multliple VkImages. The samplers will persist for the lifetime of RendererVk. In the future we could look at doing cache eviction for large object counts. Reduces the active VkSampler cache in Manhattan from over 1200 to 9. Also should reduce the number of VkSamplers used with Chrome. Bug: angleproject:4491 Change-Id: Idca00e4ed8cb660a0865281544aaa57cf884bbdb Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2160771Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarTobin Ehlis <tobine@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent e58df20c
......@@ -1312,15 +1312,6 @@ ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(
&image);
textureVk->retainImageViews(&mResourceUseList);
if (unit.sampler)
{
unit.sampler->retain(&mResourceUseList);
}
else
{
textureVk->retainSampler(&mResourceUseList);
}
}
if (executable->hasTextures())
......
......@@ -632,6 +632,7 @@ void RendererVk::onDestroy()
mDescriptorSetLayoutCache.destroy(mDevice);
mPipelineCache.destroy(mDevice);
mSamplerCache.destroy(mDevice);
vma::DestroyAllocator(mAllocator);
......
......@@ -245,6 +245,8 @@ class RendererVk : angle::NonCopyable
bool enableDebugUtils() const { return mEnableDebugUtils; }
SamplerCache &getSamplerCache() { return mSamplerCache; }
private:
angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
void ensureCapsInitialized() const;
......@@ -360,6 +362,7 @@ class RendererVk : angle::NonCopyable
bool mGlslangInitialized;
VmaAllocator mAllocator;
SamplerCache mSamplerCache;
};
} // namespace rx
......
......@@ -22,8 +22,7 @@ SamplerVk::~SamplerVk() = default;
void SamplerVk::onDestroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
mSampler.release(contextVk->getRenderer());
mSampler.reset();
}
angle::Result SamplerVk::syncState(const gl::Context *context, const bool dirty)
......@@ -37,42 +36,12 @@ angle::Result SamplerVk::syncState(const gl::Context *context, const bool dirty)
{
return angle::Result::Continue;
}
mSampler.release(renderer);
mSampler.reset();
}
const gl::Extensions &extensions = renderer->getNativeExtensions();
float maxAnisotropy = mState.getMaxAnisotropy();
bool anisotropyEnable = extensions.textureFilterAnisotropic && maxAnisotropy > 1.0f;
VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.flags = 0;
samplerInfo.magFilter = gl_vk::GetFilter(mState.getMagFilter());
samplerInfo.minFilter = gl_vk::GetFilter(mState.getMinFilter());
samplerInfo.mipmapMode = gl_vk::GetSamplerMipmapMode(mState.getMinFilter());
samplerInfo.addressModeU = gl_vk::GetSamplerAddressMode(mState.getWrapS());
samplerInfo.addressModeV = gl_vk::GetSamplerAddressMode(mState.getWrapT());
samplerInfo.addressModeW = gl_vk::GetSamplerAddressMode(mState.getWrapR());
samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = anisotropyEnable;
samplerInfo.maxAnisotropy = maxAnisotropy;
samplerInfo.compareEnable = mState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
samplerInfo.compareOp = gl_vk::GetCompareOp(mState.getCompareFunc());
samplerInfo.minLod = mState.getMinLod();
samplerInfo.maxLod = mState.getMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
if (!gl::IsMipmapFiltered(mState))
{
// Per the Vulkan spec, GL_NEAREST and GL_LINEAR do not map directly to Vulkan, so
// they must be emulated (See "Mapping of OpenGL to Vulkan filter modes")
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.25f;
}
vk::SamplerDesc desc(mState, false);
ANGLE_TRY(renderer->getSamplerCache().getSampler(contextVk, desc, &mSampler));
ANGLE_VK_TRY(contextVk, mSampler.get().init(contextVk->getDevice(), samplerInfo));
// Regenerate the serial on a sampler change.
mSerial = contextVk->generateTextureSerial();
return angle::Result::Continue;
......
......@@ -34,10 +34,8 @@ class SamplerVk : public SamplerImpl
Serial getSerial() const { return mSerial; }
void retain(vk::ResourceUseList *resourceUseList) { mSampler.retain(resourceUseList); }
private:
vk::SamplerHelper mSampler;
vk::BindingPointer<vk::Sampler> mSampler;
// The serial is used for cache indexing.
Serial mSerial;
};
......
......@@ -125,7 +125,7 @@ void TextureVk::onDestroy(const gl::Context *context)
ContextVk *contextVk = vk::GetImpl(context);
releaseAndDeleteImage(contextVk);
mSampler.release(contextVk->getRenderer());
mSampler.reset();
}
angle::Result TextureVk::setImage(const gl::Context *context,
......@@ -1469,7 +1469,7 @@ angle::Result TextureVk::syncState(const gl::Context *context,
RendererVk *renderer = contextVk->getRenderer();
if (mSampler.valid())
{
mSampler.release(renderer);
mSampler.reset();
}
if (dirtyBits.test(gl::Texture::DIRTY_BIT_SWIZZLE_RED) ||
......@@ -1493,52 +1493,8 @@ angle::Result TextureVk::syncState(const gl::Context *context,
}
}
const gl::Extensions &extensions = renderer->getNativeExtensions();
const gl::SamplerState &samplerState = mState.getSamplerState();
float maxAnisotropy = samplerState.getMaxAnisotropy();
bool anisotropyEnable = extensions.textureFilterAnisotropic && maxAnisotropy > 1.0f;
bool compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
VkCompareOp compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc());
// When sampling from stencil, deqp tests expect texture compare to have no effect
// dEQP - GLES31.functional.stencil_texturing.misc.compare_mode_effect
// states: NOTE: Texture compare mode has no effect when reading stencil values.
if (mState.isStencilMode())
{
compareEnable = VK_FALSE;
compareOp = VK_COMPARE_OP_ALWAYS;
}
// Create a simple sampler. Force basic parameter settings.
VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.flags = 0;
samplerInfo.magFilter = gl_vk::GetFilter(samplerState.getMagFilter());
samplerInfo.minFilter = gl_vk::GetFilter(samplerState.getMinFilter());
samplerInfo.mipmapMode = gl_vk::GetSamplerMipmapMode(samplerState.getMinFilter());
samplerInfo.addressModeU = gl_vk::GetSamplerAddressMode(samplerState.getWrapS());
samplerInfo.addressModeV = gl_vk::GetSamplerAddressMode(samplerState.getWrapT());
samplerInfo.addressModeW = gl_vk::GetSamplerAddressMode(samplerState.getWrapR());
samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = anisotropyEnable;
samplerInfo.maxAnisotropy = maxAnisotropy;
samplerInfo.compareEnable = compareEnable;
samplerInfo.compareOp = compareOp;
samplerInfo.minLod = samplerState.getMinLod();
samplerInfo.maxLod = samplerState.getMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
if (!gl::IsMipmapFiltered(samplerState))
{
// Per the Vulkan spec, GL_NEAREST and GL_LINEAR do not map directly to Vulkan, so
// they must be emulated (See "Mapping of OpenGL to Vulkan filter modes")
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.25f;
}
ANGLE_VK_TRY(contextVk, mSampler.get().init(contextVk->getDevice(), samplerInfo));
vk::SamplerDesc samplerDesc(mState.getSamplerState(), mState.isStencilMode());
ANGLE_TRY(renderer->getSamplerCache().getSampler(contextVk, samplerDesc, &mSampler));
// Regenerate the serial on a sampler change.
mSerial = contextVk->generateTextureSerial();
......
......@@ -185,8 +185,6 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
mImageViews.retain(resourceUseList);
}
void retainSampler(vk::ResourceUseList *resourceUseList) { mSampler.retain(resourceUseList); }
void releaseOwnershipOfImage(const gl::Context *context);
const vk::ImageView &getReadImageViewAndRecordUse(ContextVk *contextVk) const;
......@@ -415,7 +413,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
// |mSampler| contains the relevant Vulkan sampler states reprensenting the OpenGL Texture
// sampling states for the Texture.
vk::SamplerHelper mSampler;
vk::BindingPointer<vk::Sampler> mSampler;
// Render targets stored as vector of vectors
// Level is first dimension, layer is second
......
......@@ -1693,6 +1693,115 @@ uint32_t FramebufferDesc::attachmentCount() const
}
return count;
}
// SamplerDesc implementation.
SamplerDesc::SamplerDesc()
{
reset();
}
SamplerDesc::~SamplerDesc() = default;
SamplerDesc::SamplerDesc(const SamplerDesc &other) = default;
SamplerDesc &SamplerDesc::operator=(const SamplerDesc &rhs) = default;
SamplerDesc::SamplerDesc(const gl::SamplerState &samplerState, bool stencilMode)
{
update(samplerState, stencilMode);
}
void SamplerDesc::reset()
{
mMipLodBias = 0.0f;
mMaxAnisotropy = 0.0f;
mMinLod = 0.0f;
mMaxLod = 0.0f;
mMagFilter = 0;
mMinFilter = 0;
mMipmapMode = 0;
mAddressModeU = 0;
mAddressModeV = 0;
mAddressModeW = 0;
mCompareEnabled = 0;
mCompareOp = 0;
mReserved = 0;
}
void SamplerDesc::update(const gl::SamplerState &samplerState, bool stencilMode)
{
mMipLodBias = 0.0f;
mMaxAnisotropy = samplerState.getMaxAnisotropy();
mMinLod = samplerState.getMinLod();
mMaxLod = samplerState.getMaxLod();
bool compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
VkCompareOp compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc());
// When sampling from stencil, deqp tests expect texture compare to have no effect
// dEQP - GLES31.functional.stencil_texturing.misc.compare_mode_effect
// states: NOTE: Texture compare mode has no effect when reading stencil values.
if (stencilMode)
{
compareEnable = VK_FALSE;
compareOp = VK_COMPARE_OP_ALWAYS;
}
SetBitField(mMagFilter, gl_vk::GetFilter(samplerState.getMagFilter()));
SetBitField(mMinFilter, gl_vk::GetFilter(samplerState.getMinFilter()));
SetBitField(mMipmapMode, gl_vk::GetSamplerMipmapMode(samplerState.getMinFilter()));
SetBitField(mAddressModeU, gl_vk::GetSamplerAddressMode(samplerState.getWrapS()));
SetBitField(mAddressModeV, gl_vk::GetSamplerAddressMode(samplerState.getWrapT()));
SetBitField(mAddressModeW, gl_vk::GetSamplerAddressMode(samplerState.getWrapR()));
SetBitField(mCompareEnabled, compareEnable);
SetBitField(mCompareOp, compareOp);
if (!gl::IsMipmapFiltered(samplerState))
{
// Per the Vulkan spec, GL_NEAREST and GL_LINEAR do not map directly to Vulkan, so
// they must be emulated (See "Mapping of OpenGL to Vulkan filter modes")
SetBitField(mMipmapMode, VK_SAMPLER_MIPMAP_MODE_NEAREST);
mMinLod = 0.0f;
mMaxLod = 0.25f;
}
}
VkSamplerCreateInfo SamplerDesc::unpack(ContextVk *contextVk) const
{
const gl::Extensions &extensions = contextVk->getExtensions();
bool anisotropyEnable = extensions.textureFilterAnisotropic && mMaxAnisotropy > 1.0f;
VkSamplerCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
createInfo.flags = 0;
createInfo.magFilter = static_cast<VkFilter>(mMagFilter);
createInfo.minFilter = static_cast<VkFilter>(mMinFilter);
createInfo.mipmapMode = static_cast<VkSamplerMipmapMode>(mMipmapMode);
createInfo.addressModeU = static_cast<VkSamplerAddressMode>(mAddressModeU);
createInfo.addressModeV = static_cast<VkSamplerAddressMode>(mAddressModeV);
createInfo.addressModeW = static_cast<VkSamplerAddressMode>(mAddressModeW);
createInfo.mipLodBias = 0.0f;
createInfo.anisotropyEnable = anisotropyEnable;
createInfo.maxAnisotropy = mMaxAnisotropy;
createInfo.compareEnable = mCompareEnabled ? VK_TRUE : VK_FALSE;
createInfo.compareOp = static_cast<VkCompareOp>(mCompareOp);
createInfo.minLod = mMinLod;
createInfo.maxLod = mMaxLod;
createInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
createInfo.unnormalizedCoordinates = VK_FALSE;
return createInfo;
}
size_t SamplerDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool SamplerDesc::operator==(const SamplerDesc &other) const
{
return (memcmp(this, &other, sizeof(SamplerDesc)) == 0);
}
} // namespace vk
// RenderPassCache implementation.
......@@ -2003,4 +2112,48 @@ angle::Result PipelineLayoutCache::getPipelineLayout(
return angle::Result::Continue;
}
// SamplerCache implementation.
SamplerCache::SamplerCache() = default;
SamplerCache::~SamplerCache()
{
ASSERT(mPayload.empty());
}
void SamplerCache::destroy(VkDevice device)
{
for (auto &iter : mPayload)
{
vk::RefCountedSampler &sampler = iter.second;
ASSERT(!sampler.isReferenced());
sampler.get().destroy(device);
}
mPayload.clear();
}
angle::Result SamplerCache::getSampler(ContextVk *contextVk,
const vk::SamplerDesc &desc,
vk::BindingPointer<vk::Sampler> *samplerOut)
{
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::RefCountedSampler &sampler = iter->second;
samplerOut->set(&sampler);
return angle::Result::Continue;
}
VkSamplerCreateInfo createInfo = desc.unpack(contextVk);
vk::Sampler sampler;
ANGLE_VK_TRY(contextVk, sampler.init(contextVk->getDevice(), createInfo));
auto insertedItem = mPayload.emplace(desc, vk::RefCountedSampler(std::move(sampler)));
vk::RefCountedSampler &insertedSampler = insertedItem.first->second;
samplerOut->set(&insertedSampler);
return angle::Result::Continue;
}
} // namespace rx
......@@ -28,6 +28,7 @@ using PipelineAndSerial = ObjectAndSerial<Pipeline>;
using RefCountedDescriptorSetLayout = RefCounted<DescriptorSetLayout>;
using RefCountedPipelineLayout = RefCounted<PipelineLayout>;
using RefCountedSampler = RefCounted<Sampler>;
// Helper macro that casts to a bitfield type then verifies no bits were dropped.
#define SetBitField(lhs, rhs) \
......@@ -596,6 +597,58 @@ static_assert(sizeof(PipelineLayoutDesc) ==
sizeof(gl::ShaderMap<PackedPushConstantRange>)),
"Unexpected Size");
// Packed sampler description for the sampler cache.
class SamplerDesc final
{
public:
SamplerDesc();
explicit SamplerDesc(const gl::SamplerState &samplerState, bool stencilMode);
~SamplerDesc();
SamplerDesc(const SamplerDesc &other);
SamplerDesc &operator=(const SamplerDesc &rhs);
void update(const gl::SamplerState &samplerState, bool stencilMode);
void reset();
VkSamplerCreateInfo unpack(ContextVk *contextVk) const;
size_t hash() const;
bool operator==(const SamplerDesc &other) const;
private:
// 32*4 bits for floating point data.
// Note: anisotropy enabled is implicitly determined by maxAnisotropy and caps.
float mMipLodBias;
float mMaxAnisotropy;
float mMinLod;
float mMaxLod;
// 16 bits for modes + states.
// 1 bit per filter (only 2 possible values in GL: linear/nearest)
uint16_t mMagFilter : 1;
uint16_t mMinFilter : 1;
uint16_t mMipmapMode : 1;
// 3 bits per address mode (5 possible values)
uint16_t mAddressModeU : 3;
uint16_t mAddressModeV : 3;
uint16_t mAddressModeW : 3;
// 1 bit for compare enabled (2 possible values)
uint16_t mCompareEnabled : 1;
// 3 bits for compare op. (8 possible values)
uint16_t mCompareOp : 3;
// Border color and unnormalized coordinates implicitly set to contants.
// 16 extra bits reserved for future use.
uint16_t mReserved;
};
// Total size: 160 bits == 20 bytes.
static_assert(sizeof(SamplerDesc) == 20, "Unexpected SamplerDesc size");
// Disable warnings about struct padding.
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
......@@ -806,6 +859,12 @@ struct hash<rx::vk::FramebufferDesc>
{
size_t operator()(const rx::vk::FramebufferDesc &key) const { return key.hash(); }
};
template <>
struct hash<rx::vk::SamplerDesc>
{
size_t operator()(const rx::vk::SamplerDesc &key) const { return key.hash(); }
};
} // namespace std
namespace rx
......@@ -951,6 +1010,22 @@ class PipelineLayoutCache final : angle::NonCopyable
std::unordered_map<vk::PipelineLayoutDesc, vk::RefCountedPipelineLayout> mPayload;
};
class SamplerCache final : angle::NonCopyable
{
public:
SamplerCache();
~SamplerCache();
void destroy(VkDevice device);
angle::Result getSampler(ContextVk *contextVk,
const vk::SamplerDesc &desc,
vk::BindingPointer<vk::Sampler> *samplerOut);
private:
std::unordered_map<vk::SamplerDesc, vk::RefCountedSampler> mPayload;
};
// Some descriptor set and pipeline layout constants.
//
// The set/binding assignment is done as following:
......
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