Commit 88596bea by Jamie Madill Committed by Commit Bot

Vulkan: Implement a texture descriptor cache.

We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 95428331
...@@ -211,6 +211,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk ...@@ -211,6 +211,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] = &ContextVk::handleDirtyDescriptorSets; mDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] = &ContextVk::handleDirtyDescriptorSets;
mDirtyBits = mNewCommandBufferDirtyBits; mDirtyBits = mNewCommandBufferDirtyBits;
mActiveTextures.fill(nullptr);
} }
#undef INIT #undef INIT
...@@ -1999,7 +2000,9 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context) ...@@ -1999,7 +2000,9 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
const gl::State &glState = mState; const gl::State &glState = mState;
const gl::Program *program = glState.getProgram(); const gl::Program *program = glState.getProgram();
mActiveTextures.fill(nullptr); uint32_t prevMaxIndex = mActiveTexturesDesc.getMaxIndex();
memset(mActiveTextures.data(), 0, sizeof(mActiveTextures[0]) * prevMaxIndex);
mActiveTexturesDesc.reset();
const gl::ActiveTexturePointerArray &textures = glState.getActiveTexturesCache(); const gl::ActiveTexturePointerArray &textures = glState.getActiveTexturesCache();
const gl::ActiveTextureMask &activeTextures = program->getActiveSamplersMask(); const gl::ActiveTextureMask &activeTextures = program->getActiveSamplersMask();
...@@ -2016,7 +2019,29 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context) ...@@ -2016,7 +2019,29 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
ANGLE_TRY(getIncompleteTexture(context, textureType, &texture)); ANGLE_TRY(getIncompleteTexture(context, textureType, &texture));
} }
mActiveTextures[textureUnit] = vk::GetImpl(texture); TextureVk *textureVk = vk::GetImpl(texture);
// Ensure any writes to the textures are flushed before we read from them.
ANGLE_TRY(textureVk->ensureImageInitialized(this));
vk::ImageHelper &image = textureVk->getImage();
// Ensure the image is in read-only layout
if (image.isLayoutChangeNecessary(vk::ImageLayout::FragmentShaderReadOnly))
{
vk::CommandBuffer *srcLayoutChange;
ANGLE_TRY(image.recordCommands(this, &srcLayoutChange));
VkImageAspectFlags aspectFlags = image.getAspectFlags();
ASSERT(aspectFlags != 0);
image.changeLayout(aspectFlags, vk::ImageLayout::FragmentShaderReadOnly,
srcLayoutChange);
}
image.addReadDependency(mDrawFramebuffer->getFramebuffer());
mActiveTextures[textureUnit] = textureVk;
mActiveTexturesDesc.update(textureUnit, textureVk->getSerial());
} }
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -301,6 +301,13 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff ...@@ -301,6 +301,13 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
vk::DescriptorSetLayoutDesc getDriverUniformsDescriptorSetDesc() const; vk::DescriptorSetLayoutDesc getDriverUniformsDescriptorSetDesc() const;
// We use texture serials to optimize texture binding updates. Each permutation of a
// {VkImage/VkSampler} generates a unique serial. These serials are combined to form a unique
// signature for each descriptor set. This allows us to keep a cache of descriptor sets and
// avoid calling vkAllocateDesctiporSets each texture update.
Serial generateTextureSerial() { return mTextureSerialFactory.generate(); }
const vk::TextureDescriptorDesc &getActiveTexturesDesc() const { return mActiveTexturesDesc; }
private: private:
// Dirty bits. // Dirty bits.
enum DirtyBitType : size_t enum DirtyBitType : size_t
...@@ -464,6 +471,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff ...@@ -464,6 +471,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
// This cache should also probably include the texture index (shader location) and array // This cache should also probably include the texture index (shader location) and array
// index (also in the shader). This info is used in the descriptor update step. // index (also in the shader). This info is used in the descriptor update step.
gl::ActiveTextureArray<TextureVk *> mActiveTextures; gl::ActiveTextureArray<TextureVk *> mActiveTextures;
vk::TextureDescriptorDesc mActiveTexturesDesc;
// "Current Value" aka default vertex attribute state. // "Current Value" aka default vertex attribute state.
gl::AttributesMask mDirtyDefaultAttribsMask; gl::AttributesMask mDirtyDefaultAttribsMask;
...@@ -568,6 +576,9 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff ...@@ -568,6 +576,9 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
// have a value close to zero, to avoid losing 12 bits when converting these 64 bit values to // have a value close to zero, to avoid losing 12 bits when converting these 64 bit values to
// double. // double.
uint64_t mGpuEventTimestampOrigin; uint64_t mGpuEventTimestampOrigin;
// Generator for texure serials.
SerialFactory mTextureSerialFactory;
}; };
} // namespace rx } // namespace rx
......
...@@ -261,6 +261,8 @@ void ProgramVk::reset(ContextVk *contextVk) ...@@ -261,6 +261,8 @@ void ProgramVk::reset(ContextVk *contextVk)
{ {
descriptorPool.release(contextVk); descriptorPool.release(contextVk);
} }
mTextureDescriptorsCache.clear();
} }
std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context, std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
...@@ -779,6 +781,14 @@ void ProgramVk::setPathFragmentInputGen(const std::string &inputName, ...@@ -779,6 +781,14 @@ void ProgramVk::setPathFragmentInputGen(const std::string &inputName,
angle::Result ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex) angle::Result ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex)
{ {
bool ignoreNewPoolAllocated;
return allocateDescriptorSetAndGetInfo(contextVk, descriptorSetIndex, &ignoreNewPoolAllocated);
}
angle::Result ProgramVk::allocateDescriptorSetAndGetInfo(ContextVk *contextVk,
uint32_t descriptorSetIndex,
bool *newPoolAllocatedOut)
{
vk::DynamicDescriptorPool &dynamicDescriptorPool = mDynamicDescriptorPools[descriptorSetIndex]; vk::DynamicDescriptorPool &dynamicDescriptorPool = mDynamicDescriptorPools[descriptorSetIndex];
uint32_t potentialNewCount = descriptorSetIndex + 1; uint32_t potentialNewCount = descriptorSetIndex + 1;
...@@ -789,9 +799,9 @@ angle::Result ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t de ...@@ -789,9 +799,9 @@ angle::Result ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t de
const vk::DescriptorSetLayout &descriptorSetLayout = const vk::DescriptorSetLayout &descriptorSetLayout =
mDescriptorSetLayouts[descriptorSetIndex].get(); mDescriptorSetLayouts[descriptorSetIndex].get();
ANGLE_TRY(dynamicDescriptorPool.allocateSets(contextVk, descriptorSetLayout.ptr(), 1, ANGLE_TRY(dynamicDescriptorPool.allocateSetsAndGetInfo(
&mDescriptorPoolBindings[descriptorSetIndex], contextVk, descriptorSetLayout.ptr(), 1, &mDescriptorPoolBindings[descriptorSetIndex],
&mDescriptorSets[descriptorSetIndex])); &mDescriptorSets[descriptorSetIndex], newPoolAllocatedOut));
mEmptyDescriptorSets[descriptorSetIndex] = VK_NULL_HANDLE; mEmptyDescriptorSets[descriptorSetIndex] = VK_NULL_HANDLE;
return angle::Result::Continue; return angle::Result::Continue;
...@@ -982,8 +992,25 @@ angle::Result ProgramVk::updateUniformBuffersDescriptorSet(ContextVk *contextVk, ...@@ -982,8 +992,25 @@ angle::Result ProgramVk::updateUniformBuffersDescriptorSet(ContextVk *contextVk,
angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk, angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
vk::FramebufferHelper *framebuffer) vk::FramebufferHelper *framebuffer)
{ {
const vk::TextureDescriptorDesc &texturesDesc = contextVk->getActiveTexturesDesc();
auto iter = mTextureDescriptorsCache.find(texturesDesc);
if (iter != mTextureDescriptorsCache.end())
{
mDescriptorSets[kTextureDescriptorSetIndex] = iter->second;
return angle::Result::Continue;
}
ASSERT(hasTextures()); ASSERT(hasTextures());
ANGLE_TRY(allocateDescriptorSet(contextVk, kTextureDescriptorSetIndex)); bool newPoolAllocated;
ANGLE_TRY(
allocateDescriptorSetAndGetInfo(contextVk, kTextureDescriptorSetIndex, &newPoolAllocated));
// Clear descriptor set cache. It may no longer be valid.
if (newPoolAllocated)
{
mTextureDescriptorsCache.clear();
}
VkDescriptorSet descriptorSet = mDescriptorSets[kTextureDescriptorSetIndex]; VkDescriptorSet descriptorSet = mDescriptorSets[kTextureDescriptorSetIndex];
...@@ -1006,24 +1033,8 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk, ...@@ -1006,24 +1033,8 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement]; GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit]; TextureVk *textureVk = activeTextures[textureUnit];
// Ensure any writes to the textures are flushed before we read from them.
ANGLE_TRY(textureVk->ensureImageInitialized(contextVk));
vk::ImageHelper &image = textureVk->getImage(); vk::ImageHelper &image = textureVk->getImage();
// Ensure the image is in read-only layout
if (image.isLayoutChangeNecessary(vk::ImageLayout::FragmentShaderReadOnly))
{
vk::CommandBuffer *srcLayoutChange;
ANGLE_TRY(image.recordCommands(contextVk, &srcLayoutChange));
VkImageAspectFlags aspectFlags = image.getAspectFlags();
ASSERT(aspectFlags != 0);
image.changeLayout(aspectFlags, vk::ImageLayout::FragmentShaderReadOnly,
srcLayoutChange);
}
image.addReadDependency(framebuffer);
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount]; VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
imageInfo.sampler = textureVk->getSampler().getHandle(); imageInfo.sampler = textureVk->getSampler().getHandle();
...@@ -1052,6 +1063,8 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk, ...@@ -1052,6 +1063,8 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
ASSERT(writeCount > 0); ASSERT(writeCount > 0);
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr); vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
mTextureDescriptorsCache.emplace(texturesDesc, descriptorSet);
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -154,6 +154,9 @@ class ProgramVk : public ProgramImpl ...@@ -154,6 +154,9 @@ class ProgramVk : public ProgramImpl
void reset(ContextVk *contextVk); void reset(ContextVk *contextVk);
angle::Result allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex); angle::Result allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex);
angle::Result allocateDescriptorSetAndGetInfo(ContextVk *contextVk,
uint32_t descriptorSetIndex,
bool *newPoolAllocatedOut);
angle::Result initDefaultUniformBlocks(const gl::Context *glContext); angle::Result initDefaultUniformBlocks(const gl::Context *glContext);
angle::Result updateDefaultUniformsDescriptorSet(ContextVk *contextVk); angle::Result updateDefaultUniformsDescriptorSet(ContextVk *contextVk);
...@@ -227,6 +230,8 @@ class ProgramVk : public ProgramImpl ...@@ -227,6 +230,8 @@ class ProgramVk : public ProgramImpl
std::vector<VkDescriptorSet> mDescriptorSets; std::vector<VkDescriptorSet> mDescriptorSets;
vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets; vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets;
std::unordered_map<vk::TextureDescriptorDesc, VkDescriptorSet> mTextureDescriptorsCache;
// We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get // We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get
// deleted while this program is in use. // deleted while this program is in use.
vk::BindingPointer<vk::PipelineLayout> mPipelineLayout; vk::BindingPointer<vk::PipelineLayout> mPipelineLayout;
......
...@@ -836,21 +836,21 @@ void TextureVk::releaseAndDeleteImage(ContextVk *context) ...@@ -836,21 +836,21 @@ void TextureVk::releaseAndDeleteImage(ContextVk *context)
} }
} }
angle::Result TextureVk::ensureImageAllocated(ContextVk *context, const vk::Format &format) angle::Result TextureVk::ensureImageAllocated(ContextVk *contextVk, const vk::Format &format)
{ {
if (mImage == nullptr) if (mImage == nullptr)
{ {
setImageHelper(context, new vk::ImageHelper(), mState.getType(), format, 0, 0, true); setImageHelper(contextVk, new vk::ImageHelper(), mState.getType(), format, 0, 0, true);
} }
else else
{ {
updateImageHelper(context, format); updateImageHelper(contextVk, format);
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
void TextureVk::setImageHelper(ContextVk *context, void TextureVk::setImageHelper(ContextVk *contextVk,
vk::ImageHelper *imageHelper, vk::ImageHelper *imageHelper,
gl::TextureType imageType, gl::TextureType imageType,
const vk::Format &format, const vk::Format &format,
...@@ -865,19 +865,21 @@ void TextureVk::setImageHelper(ContextVk *context, ...@@ -865,19 +865,21 @@ void TextureVk::setImageHelper(ContextVk *context,
mImageLevelOffset = imageLevelOffset; mImageLevelOffset = imageLevelOffset;
mImageLayerOffset = imageLayerOffset; mImageLayerOffset = imageLayerOffset;
mImage = imageHelper; mImage = imageHelper;
mImage->initStagingBuffer(context->getRenderer(), format); mImage->initStagingBuffer(contextVk->getRenderer(), format);
mRenderTarget.init(mImage, &mDrawBaseLevelImageView, &mFetchBaseLevelImageView, mRenderTarget.init(mImage, &mDrawBaseLevelImageView, &mFetchBaseLevelImageView,
getNativeImageLevel(0), getNativeImageLayer(0)); getNativeImageLevel(0), getNativeImageLayer(0));
// Force re-creation of cube map render targets next time they are needed // Force re-creation of cube map render targets next time they are needed
mCubeMapRenderTargets.clear(); mCubeMapRenderTargets.clear();
mSerial = contextVk->generateTextureSerial();
} }
void TextureVk::updateImageHelper(ContextVk *context, const vk::Format &format) void TextureVk::updateImageHelper(ContextVk *contextVk, const vk::Format &format)
{ {
ASSERT(mImage != nullptr); ASSERT(mImage != nullptr);
mImage->initStagingBuffer(context->getRenderer(), format); mImage->initStagingBuffer(contextVk->getRenderer(), format);
} }
angle::Result TextureVk::redefineImage(const gl::Context *context, angle::Result TextureVk::redefineImage(const gl::Context *context,
...@@ -1191,26 +1193,30 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -1191,26 +1193,30 @@ angle::Result TextureVk::syncState(const gl::Context *context,
bool anisotropyEnable = extensions.textureFilterAnisotropic && maxAnisotropy > 1.0f; bool anisotropyEnable = extensions.textureFilterAnisotropic && maxAnisotropy > 1.0f;
// Create a simple sampler. Force basic parameter settings. // Create a simple sampler. Force basic parameter settings.
VkSamplerCreateInfo samplerInfo = {}; VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.flags = 0; samplerInfo.flags = 0;
samplerInfo.magFilter = gl_vk::GetFilter(samplerState.getMagFilter()); samplerInfo.magFilter = gl_vk::GetFilter(samplerState.getMagFilter());
samplerInfo.minFilter = gl_vk::GetFilter(samplerState.getMinFilter()); samplerInfo.minFilter = gl_vk::GetFilter(samplerState.getMinFilter());
samplerInfo.mipmapMode = gl_vk::GetSamplerMipmapMode(samplerState.getMinFilter()); samplerInfo.mipmapMode = gl_vk::GetSamplerMipmapMode(samplerState.getMinFilter());
samplerInfo.addressModeU = gl_vk::GetSamplerAddressMode(samplerState.getWrapS()); samplerInfo.addressModeU = gl_vk::GetSamplerAddressMode(samplerState.getWrapS());
samplerInfo.addressModeV = gl_vk::GetSamplerAddressMode(samplerState.getWrapT()); samplerInfo.addressModeV = gl_vk::GetSamplerAddressMode(samplerState.getWrapT());
samplerInfo.addressModeW = gl_vk::GetSamplerAddressMode(samplerState.getWrapR()); samplerInfo.addressModeW = gl_vk::GetSamplerAddressMode(samplerState.getWrapR());
samplerInfo.mipLodBias = 0.0f; samplerInfo.mipLodBias = 0.0f;
samplerInfo.anisotropyEnable = anisotropyEnable; samplerInfo.anisotropyEnable = anisotropyEnable;
samplerInfo.maxAnisotropy = maxAnisotropy; samplerInfo.maxAnisotropy = maxAnisotropy;
samplerInfo.compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE; samplerInfo.compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
samplerInfo.compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc()); samplerInfo.compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc());
samplerInfo.minLod = samplerState.getMinLod(); samplerInfo.minLod = samplerState.getMinLod();
samplerInfo.maxLod = samplerState.getMaxLod(); samplerInfo.maxLod = samplerState.getMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK; samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE; samplerInfo.unnormalizedCoordinates = VK_FALSE;
ANGLE_VK_TRY(contextVk, mSampler.init(contextVk->getDevice(), samplerInfo)); ANGLE_VK_TRY(contextVk, mSampler.init(contextVk->getDevice(), samplerInfo));
// Regenerate the serial on a sampler change.
mSerial = contextVk->generateTextureSerial();
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1363,6 +1369,8 @@ angle::Result TextureVk::initImage(ContextVk *contextVk, ...@@ -1363,6 +1369,8 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
} }
} }
mSerial = contextVk->generateTextureSerial();
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1414,13 +1422,13 @@ angle::Result TextureVk::initImageViews(ContextVk *contextVk, ...@@ -1414,13 +1422,13 @@ angle::Result TextureVk::initImageViews(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
void TextureVk::releaseImage(ContextVk *context) void TextureVk::releaseImage(ContextVk *contextVk)
{ {
if (mImage) if (mImage)
{ {
if (mOwnsImage) if (mOwnsImage)
{ {
mImage->releaseImage(context); mImage->releaseImage(contextVk);
} }
else else
{ {
...@@ -1428,25 +1436,25 @@ void TextureVk::releaseImage(ContextVk *context) ...@@ -1428,25 +1436,25 @@ void TextureVk::releaseImage(ContextVk *context)
} }
} }
Serial currentSerial = context->getCurrentQueueSerial(); Serial currentSerial = contextVk->getCurrentQueueSerial();
context->releaseObject(currentSerial, &mDrawBaseLevelImageView); contextVk->releaseObject(currentSerial, &mDrawBaseLevelImageView);
context->releaseObject(currentSerial, &mReadBaseLevelImageView); contextVk->releaseObject(currentSerial, &mReadBaseLevelImageView);
context->releaseObject(currentSerial, &mReadMipmapImageView); contextVk->releaseObject(currentSerial, &mReadMipmapImageView);
context->releaseObject(currentSerial, &mFetchBaseLevelImageView); contextVk->releaseObject(currentSerial, &mFetchBaseLevelImageView);
context->releaseObject(currentSerial, &mFetchMipmapImageView); contextVk->releaseObject(currentSerial, &mFetchMipmapImageView);
for (auto &layerViews : mLayerLevelDrawImageViews) for (auto &layerViews : mLayerLevelDrawImageViews)
{ {
for (vk::ImageView &imageView : layerViews) for (vk::ImageView &imageView : layerViews)
{ {
context->releaseObject(currentSerial, &imageView); contextVk->releaseObject(currentSerial, &imageView);
} }
} }
mLayerLevelDrawImageViews.clear(); mLayerLevelDrawImageViews.clear();
for (vk::ImageView &imageView : mLayerFetchImageView) for (vk::ImageView &imageView : mLayerFetchImageView)
{ {
context->releaseObject(currentSerial, &imageView); contextVk->releaseObject(currentSerial, &imageView);
} }
mLayerFetchImageView.clear(); mLayerFetchImageView.clear();
mCubeMapRenderTargets.clear(); mCubeMapRenderTargets.clear();
......
...@@ -163,6 +163,8 @@ class TextureVk : public TextureImpl ...@@ -163,6 +163,8 @@ class TextureVk : public TextureImpl
angle::Result ensureImageInitialized(ContextVk *contextVk); angle::Result ensureImageInitialized(ContextVk *contextVk);
Serial getSerial() const { return mSerial; }
private: private:
// Transform an image index from the frontend into one that can be used on the backing // Transform an image index from the frontend into one that can be used on the backing
// ImageHelper, taking into account mipmap or cube face offsets // ImageHelper, taking into account mipmap or cube face offsets
...@@ -170,9 +172,9 @@ class TextureVk : public TextureImpl ...@@ -170,9 +172,9 @@ class TextureVk : public TextureImpl
uint32_t getNativeImageLevel(uint32_t frontendLevel) const; uint32_t getNativeImageLevel(uint32_t frontendLevel) const;
uint32_t getNativeImageLayer(uint32_t frontendLayer) const; uint32_t getNativeImageLayer(uint32_t frontendLayer) const;
void releaseAndDeleteImage(ContextVk *context); void releaseAndDeleteImage(ContextVk *contextVk);
angle::Result ensureImageAllocated(ContextVk *context, const vk::Format &format); angle::Result ensureImageAllocated(ContextVk *contextVk, const vk::Format &format);
void setImageHelper(ContextVk *context, void setImageHelper(ContextVk *contextVk,
vk::ImageHelper *imageHelper, vk::ImageHelper *imageHelper,
gl::TextureType imageType, gl::TextureType imageType,
const vk::Format &format, const vk::Format &format,
...@@ -302,6 +304,9 @@ class TextureVk : public TextureImpl ...@@ -302,6 +304,9 @@ class TextureVk : public TextureImpl
RenderTargetVk mRenderTarget; RenderTargetVk mRenderTarget;
std::vector<vk::ImageView> mLayerFetchImageView; std::vector<vk::ImageView> mLayerFetchImageView;
std::vector<RenderTargetVk> mCubeMapRenderTargets; std::vector<RenderTargetVk> mCubeMapRenderTargets;
// The serial is used for cache indexing.
Serial mSerial;
}; };
} // namespace rx } // namespace rx
......
...@@ -1436,6 +1436,52 @@ void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits, ...@@ -1436,6 +1436,52 @@ void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits,
{ {
mTransitions.emplace_back(bits, desc, pipeline); mTransitions.emplace_back(bits, desc, pipeline);
} }
TextureDescriptorDesc::TextureDescriptorDesc() : mMaxIndex(0)
{
mSerials.fill(0);
}
TextureDescriptorDesc::~TextureDescriptorDesc() = default;
TextureDescriptorDesc::TextureDescriptorDesc(const TextureDescriptorDesc &other) = default;
TextureDescriptorDesc &TextureDescriptorDesc::operator=(const TextureDescriptorDesc &other) =
default;
void TextureDescriptorDesc::update(size_t index, Serial serial)
{
if (index >= mMaxIndex)
{
mMaxIndex = index + 1;
}
// If the serial number overflows we should defragment and regenerate all serials.
// There should never be more than UINT_MAX textures alive at a time.
ASSERT(serial.getValue() < std::numeric_limits<uint32_t>::max());
mSerials[index] = static_cast<uint32_t>(serial.getValue());
}
size_t TextureDescriptorDesc::hash() const
{
return angle::ComputeGenericHash(&mSerials, sizeof(uint32_t) * mMaxIndex);
}
void TextureDescriptorDesc::reset()
{
memset(mSerials.data(), 0, sizeof(mSerials[0]) * mMaxIndex);
mMaxIndex = 0;
}
bool TextureDescriptorDesc::operator==(const TextureDescriptorDesc &other) const
{
if (mMaxIndex != other.mMaxIndex)
return false;
if (mMaxIndex == 0)
return true;
return memcmp(mSerials.data(), other.mSerials.data(), sizeof(uint32_t) * mMaxIndex) == 0;
}
} // namespace vk } // namespace vk
// RenderPassCache implementation. // RenderPassCache implementation.
......
...@@ -684,6 +684,28 @@ class PipelineHelper final : angle::NonCopyable ...@@ -684,6 +684,28 @@ class PipelineHelper final : angle::NonCopyable
ANGLE_INLINE PipelineHelper::PipelineHelper(Pipeline &&pipeline) : mPipeline(std::move(pipeline)) {} ANGLE_INLINE PipelineHelper::PipelineHelper(Pipeline &&pipeline) : mPipeline(std::move(pipeline)) {}
class TextureDescriptorDesc
{
public:
TextureDescriptorDesc();
~TextureDescriptorDesc();
TextureDescriptorDesc(const TextureDescriptorDesc &other);
TextureDescriptorDesc &operator=(const TextureDescriptorDesc &other);
void update(size_t index, Serial serial);
size_t hash() const;
void reset();
bool operator==(const TextureDescriptorDesc &other) const;
// Note: this is an exclusive index. If there is one index it will return "1".
uint32_t getMaxIndex() const { return mMaxIndex; }
private:
uint32_t mMaxIndex;
gl::ActiveTextureArray<uint32_t> mSerials;
};
} // namespace vk } // namespace vk
} // namespace rx } // namespace rx
...@@ -719,6 +741,12 @@ struct hash<rx::vk::PipelineLayoutDesc> ...@@ -719,6 +741,12 @@ struct hash<rx::vk::PipelineLayoutDesc>
{ {
size_t operator()(const rx::vk::PipelineLayoutDesc &key) const { return key.hash(); } size_t operator()(const rx::vk::PipelineLayoutDesc &key) const { return key.hash(); }
}; };
template <>
struct hash<rx::vk::TextureDescriptorDesc>
{
size_t operator()(const rx::vk::TextureDescriptorDesc &key) const { return key.hash(); }
};
} // namespace std } // namespace std
namespace rx namespace rx
...@@ -884,7 +912,6 @@ constexpr uint32_t kReservedDriverUniformBindingCount = 1; ...@@ -884,7 +912,6 @@ constexpr uint32_t kReservedDriverUniformBindingCount = 1;
constexpr uint32_t kVertexUniformsBindingIndex = 0; constexpr uint32_t kVertexUniformsBindingIndex = 0;
// Binding index for default uniforms in the fragment shader: // Binding index for default uniforms in the fragment shader:
constexpr uint32_t kFragmentUniformsBindingIndex = 1; constexpr uint32_t kFragmentUniformsBindingIndex = 1;
} // namespace rx } // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VK_CACHE_UTILS_H_ #endif // LIBANGLE_RENDERER_VULKAN_VK_CACHE_UTILS_H_
...@@ -3646,7 +3646,8 @@ void SimpleStateChangeTest::drawToFboWithCulling(const GLenum frontFace, bool ea ...@@ -3646,7 +3646,8 @@ void SimpleStateChangeTest::drawToFboWithCulling(const GLenum frontFace, bool ea
GLTexture texture1; GLTexture texture1;
ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture(), essl1_shaders::fs::Texture()); ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
essl1_shaders::fs::Texture2D());
bindTextureToFbo(fbo1, texture1); bindTextureToFbo(fbo1, texture1);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "libANGLE/renderer/vulkan/ProgramVk.h" #include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "test_utils/gl_raii.h" #include "test_utils/gl_raii.h"
#include "util/EGLWindow.h" #include "util/EGLWindow.h"
#include "util/shader_utils.h"
using namespace angle; using namespace angle;
...@@ -165,23 +166,15 @@ TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdates) ...@@ -165,23 +166,15 @@ TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdates)
ASSERT_TRUE(IsVulkan()); ASSERT_TRUE(IsVulkan());
// Initialize texture program. // Initialize texture program.
constexpr char kVS[] = R"(attribute vec2 position; constexpr char kFS[] = R"(varying mediump vec2 v_texCoord;
varying mediump vec2 texCoord;
void main()
{
gl_Position = vec4(position, 0, 1);
texCoord = position * 0.5 + vec2(0.5);
})";
constexpr char kFS[] = R"(varying mediump vec2 texCoord;
uniform sampler2D tex; uniform sampler2D tex;
uniform mediump vec4 colorMask; uniform mediump vec4 colorMask;
void main() void main()
{ {
gl_FragColor = texture2D(tex, texCoord) * colorMask; gl_FragColor = texture2D(tex, v_texCoord) * colorMask;
})"; })";
ANGLE_GL_PROGRAM(program, kVS, kFS); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
glUseProgram(program); glUseProgram(program);
limitMaxSets(program); limitMaxSets(program);
...@@ -215,26 +208,106 @@ void main() ...@@ -215,26 +208,106 @@ void main()
// Draw with white. // Draw with white.
glUniform1i(texLoc, 0); glUniform1i(texLoc, 0);
glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f); glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, "position", 0.5f, 1.0f, true); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
// Draw with white masking out red. // Draw with white masking out red.
glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f); glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, "position", 0.5f, 1.0f, true); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
// Draw with magenta. // Draw with magenta.
glUniform1i(texLoc, 1); glUniform1i(texLoc, 1);
glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f); glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, "position", 0.5f, 1.0f, true); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
// Draw with magenta masking out red. // Draw with magenta masking out red.
glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f); glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, "position", 0.5f, 1.0f, true); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
swapBuffers(); swapBuffers();
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
} }
// Uniform updates along with Texture regeneration.
TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureRegeneration)
{
ASSERT_TRUE(IsVulkan());
// Initialize texture program.
constexpr char kFS[] = R"(varying mediump vec2 v_texCoord;
uniform sampler2D tex;
uniform mediump vec4 colorMask;
void main()
{
gl_FragColor = texture2D(tex, v_texCoord) * colorMask;
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
glUseProgram(program);
limitMaxSets(program);
// Initialize large arrays of textures.
std::vector<GLTexture> whiteTextures;
std::vector<GLTexture> magentaTextures;
for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
{
// Initialize white texture.
GLTexture whiteTexture;
InitTexture(GLColor::white, &whiteTexture);
ASSERT_GL_NO_ERROR();
whiteTextures.emplace_back(std::move(whiteTexture));
// Initialize magenta texture.
GLTexture magentaTexture;
InitTexture(GLColor::magenta, &magentaTexture);
ASSERT_GL_NO_ERROR();
magentaTextures.emplace_back(std::move(magentaTexture));
}
// Get uniform locations.
GLint texLoc = glGetUniformLocation(program, "tex");
ASSERT_NE(-1, texLoc);
GLint colorMaskLoc = glGetUniformLocation(program, "colorMask");
ASSERT_NE(-1, colorMaskLoc);
// Draw multiple times, each iteration will create a new descriptor set.
for (int outerIteration = 0; outerIteration < 2; ++outerIteration)
{
for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, whiteTextures[iteration]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, magentaTextures[iteration]);
// Draw with white.
glUniform1i(texLoc, 0);
glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
// Draw with white masking out red.
glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
// Draw with magenta.
glUniform1i(texLoc, 1);
glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
// Draw with magenta masking out red.
glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
swapBuffers();
ASSERT_GL_NO_ERROR();
}
}
}
// Uniform updates along with Texture updates. // Uniform updates along with Texture updates.
TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdatesTwoShaders) TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdatesTwoShaders)
{ {
......
...@@ -30,11 +30,17 @@ class GLWrapper : angle::NonCopyable ...@@ -30,11 +30,17 @@ class GLWrapper : angle::NonCopyable
// The move-constructor and move-assignment operators are necessary so that the data within a // The move-constructor and move-assignment operators are necessary so that the data within a
// GLWrapper object can be relocated. // GLWrapper object can be relocated.
GLWrapper(GLWrapper &&rht) : mHandle(rht.mHandle) { rht.mHandle = 0u; } GLWrapper(GLWrapper &&rht)
: mGenFunc(rht.mGenFunc), mDeleteFunc(rht.mDeleteFunc), mHandle(rht.mHandle)
{
rht.mHandle = 0u;
}
GLWrapper &operator=(GLWrapper &&rht) GLWrapper &operator=(GLWrapper &&rht)
{ {
if (this != &rht) if (this != &rht)
{ {
mGenFunc = rht.mGenFunc;
mDeleteFunc = rht.mDeleteFunc;
std::swap(mHandle, rht.mHandle); std::swap(mHandle, rht.mHandle);
} }
return *this; return *this;
......
...@@ -277,6 +277,11 @@ const char *ColorUniform() ...@@ -277,6 +277,11 @@ const char *ColorUniform()
return "u_color"; return "u_color";
} }
const char *Texture2DUniform()
{
return "u_tex2D";
}
namespace vs namespace vs
{ {
...@@ -318,16 +323,16 @@ void main() ...@@ -318,16 +323,16 @@ void main()
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
// texcoord. // texcoord.
const char *Texture() const char *Texture2D()
{ {
return R"(precision highp float; return R"(precision highp float;
attribute vec4 a_position; attribute vec4 a_position;
varying vec2 texcoord; varying vec2 v_texCoord;
void main() void main()
{ {
gl_Position = vec4(a_position.xy, 0.0, 1.0); gl_Position = vec4(a_position.xy, 0.0, 1.0);
texcoord = a_position.xy; v_texCoord = a_position.xy * 0.5 + vec2(0.5);
})"; })";
} }
...@@ -400,15 +405,15 @@ void main() ...@@ -400,15 +405,15 @@ void main()
} }
// A shader that samples the texture. // A shader that samples the texture.
const char *Texture() const char *Texture2D()
{ {
return R"(precision highp float; return R"(precision mediump float;
uniform sampler2D tex; uniform sampler2D u_tex;
varying vec2 texcoord; varying vec2 v_texCoord;
void main() void main()
{ {
gl_FragColor = vec4(texture2D(tex, texcoord).rgb, 1.0); gl_FragColor = texture2D(u_tex, v_texCoord);
})"; })";
} }
......
...@@ -52,6 +52,7 @@ namespace essl1_shaders ...@@ -52,6 +52,7 @@ namespace essl1_shaders
ANGLE_UTIL_EXPORT const char *PositionAttrib(); ANGLE_UTIL_EXPORT const char *PositionAttrib();
ANGLE_UTIL_EXPORT const char *ColorUniform(); ANGLE_UTIL_EXPORT const char *ColorUniform();
ANGLE_UTIL_EXPORT const char *Texture2DUniform();
namespace vs namespace vs
{ {
...@@ -66,7 +67,9 @@ ANGLE_UTIL_EXPORT const char *Simple(); ...@@ -66,7 +67,9 @@ ANGLE_UTIL_EXPORT const char *Simple();
// v_position. // v_position.
ANGLE_UTIL_EXPORT const char *Passthrough(); ANGLE_UTIL_EXPORT const char *Passthrough();
ANGLE_UTIL_EXPORT const char *Texture(); // A shader that simply passes through attribute a_position, setting it to gl_Position and varying
// texcoord.
ANGLE_UTIL_EXPORT const char *Texture2D();
} // namespace vs } // namespace vs
...@@ -90,7 +93,7 @@ ANGLE_UTIL_EXPORT const char *Green(); ...@@ -90,7 +93,7 @@ ANGLE_UTIL_EXPORT const char *Green();
ANGLE_UTIL_EXPORT const char *Blue(); ANGLE_UTIL_EXPORT const char *Blue();
// A shader that samples the texture // A shader that samples the texture
ANGLE_UTIL_EXPORT const char *Texture(); ANGLE_UTIL_EXPORT const char *Texture2D();
} // namespace fs } // namespace fs
} // namespace essl1_shaders } // namespace essl1_shaders
......
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