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
mDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] = &ContextVk::handleDirtyDescriptorSets;
mDirtyBits = mNewCommandBufferDirtyBits;
mActiveTextures.fill(nullptr);
}
#undef INIT
......@@ -1999,7 +2000,9 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
const gl::State &glState = mState;
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::ActiveTextureMask &activeTextures = program->getActiveSamplersMask();
......@@ -2016,7 +2019,29 @@ angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
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;
......
......@@ -301,6 +301,13 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
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:
// Dirty bits.
enum DirtyBitType : size_t
......@@ -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
// index (also in the shader). This info is used in the descriptor update step.
gl::ActiveTextureArray<TextureVk *> mActiveTextures;
vk::TextureDescriptorDesc mActiveTexturesDesc;
// "Current Value" aka default vertex attribute state.
gl::AttributesMask mDirtyDefaultAttribsMask;
......@@ -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
// double.
uint64_t mGpuEventTimestampOrigin;
// Generator for texure serials.
SerialFactory mTextureSerialFactory;
};
} // namespace rx
......
......@@ -261,6 +261,8 @@ void ProgramVk::reset(ContextVk *contextVk)
{
descriptorPool.release(contextVk);
}
mTextureDescriptorsCache.clear();
}
std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
......@@ -779,6 +781,14 @@ void ProgramVk::setPathFragmentInputGen(const std::string &inputName,
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];
uint32_t potentialNewCount = descriptorSetIndex + 1;
......@@ -789,9 +799,9 @@ angle::Result ProgramVk::allocateDescriptorSet(ContextVk *contextVk, uint32_t de
const vk::DescriptorSetLayout &descriptorSetLayout =
mDescriptorSetLayouts[descriptorSetIndex].get();
ANGLE_TRY(dynamicDescriptorPool.allocateSets(contextVk, descriptorSetLayout.ptr(), 1,
&mDescriptorPoolBindings[descriptorSetIndex],
&mDescriptorSets[descriptorSetIndex]));
ANGLE_TRY(dynamicDescriptorPool.allocateSetsAndGetInfo(
contextVk, descriptorSetLayout.ptr(), 1, &mDescriptorPoolBindings[descriptorSetIndex],
&mDescriptorSets[descriptorSetIndex], newPoolAllocatedOut));
mEmptyDescriptorSets[descriptorSetIndex] = VK_NULL_HANDLE;
return angle::Result::Continue;
......@@ -982,8 +992,25 @@ angle::Result ProgramVk::updateUniformBuffersDescriptorSet(ContextVk *contextVk,
angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
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());
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];
......@@ -1006,24 +1033,8 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
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();
// 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];
imageInfo.sampler = textureVk->getSampler().getHandle();
......@@ -1052,6 +1063,8 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
ASSERT(writeCount > 0);
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
mTextureDescriptorsCache.emplace(texturesDesc, descriptorSet);
return angle::Result::Continue;
}
......
......@@ -154,6 +154,9 @@ class ProgramVk : public ProgramImpl
void reset(ContextVk *contextVk);
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 updateDefaultUniformsDescriptorSet(ContextVk *contextVk);
......@@ -227,6 +230,8 @@ class ProgramVk : public ProgramImpl
std::vector<VkDescriptorSet> mDescriptorSets;
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
// deleted while this program is in use.
vk::BindingPointer<vk::PipelineLayout> mPipelineLayout;
......
......@@ -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)
{
setImageHelper(context, new vk::ImageHelper(), mState.getType(), format, 0, 0, true);
setImageHelper(contextVk, new vk::ImageHelper(), mState.getType(), format, 0, 0, true);
}
else
{
updateImageHelper(context, format);
updateImageHelper(contextVk, format);
}
return angle::Result::Continue;
}
void TextureVk::setImageHelper(ContextVk *context,
void TextureVk::setImageHelper(ContextVk *contextVk,
vk::ImageHelper *imageHelper,
gl::TextureType imageType,
const vk::Format &format,
......@@ -865,19 +865,21 @@ void TextureVk::setImageHelper(ContextVk *context,
mImageLevelOffset = imageLevelOffset;
mImageLayerOffset = imageLayerOffset;
mImage = imageHelper;
mImage->initStagingBuffer(context->getRenderer(), format);
mImage->initStagingBuffer(contextVk->getRenderer(), format);
mRenderTarget.init(mImage, &mDrawBaseLevelImageView, &mFetchBaseLevelImageView,
getNativeImageLevel(0), getNativeImageLayer(0));
// Force re-creation of cube map render targets next time they are needed
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);
mImage->initStagingBuffer(context->getRenderer(), format);
mImage->initStagingBuffer(contextVk->getRenderer(), format);
}
angle::Result TextureVk::redefineImage(const gl::Context *context,
......@@ -1191,26 +1193,30 @@ angle::Result TextureVk::syncState(const gl::Context *context,
bool anisotropyEnable = extensions.textureFilterAnisotropic && maxAnisotropy > 1.0f;
// 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 = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
samplerInfo.compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc());
samplerInfo.minLod = samplerState.getMinLod();
samplerInfo.maxLod = samplerState.getMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
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 = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
samplerInfo.compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc());
samplerInfo.minLod = samplerState.getMinLod();
samplerInfo.maxLod = samplerState.getMaxLod();
samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
ANGLE_VK_TRY(contextVk, mSampler.init(contextVk->getDevice(), samplerInfo));
// Regenerate the serial on a sampler change.
mSerial = contextVk->generateTextureSerial();
return angle::Result::Continue;
}
......@@ -1363,6 +1369,8 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
}
}
mSerial = contextVk->generateTextureSerial();
return angle::Result::Continue;
}
......@@ -1414,13 +1422,13 @@ angle::Result TextureVk::initImageViews(ContextVk *contextVk,
return angle::Result::Continue;
}
void TextureVk::releaseImage(ContextVk *context)
void TextureVk::releaseImage(ContextVk *contextVk)
{
if (mImage)
{
if (mOwnsImage)
{
mImage->releaseImage(context);
mImage->releaseImage(contextVk);
}
else
{
......@@ -1428,25 +1436,25 @@ void TextureVk::releaseImage(ContextVk *context)
}
}
Serial currentSerial = context->getCurrentQueueSerial();
Serial currentSerial = contextVk->getCurrentQueueSerial();
context->releaseObject(currentSerial, &mDrawBaseLevelImageView);
context->releaseObject(currentSerial, &mReadBaseLevelImageView);
context->releaseObject(currentSerial, &mReadMipmapImageView);
context->releaseObject(currentSerial, &mFetchBaseLevelImageView);
context->releaseObject(currentSerial, &mFetchMipmapImageView);
contextVk->releaseObject(currentSerial, &mDrawBaseLevelImageView);
contextVk->releaseObject(currentSerial, &mReadBaseLevelImageView);
contextVk->releaseObject(currentSerial, &mReadMipmapImageView);
contextVk->releaseObject(currentSerial, &mFetchBaseLevelImageView);
contextVk->releaseObject(currentSerial, &mFetchMipmapImageView);
for (auto &layerViews : mLayerLevelDrawImageViews)
{
for (vk::ImageView &imageView : layerViews)
{
context->releaseObject(currentSerial, &imageView);
contextVk->releaseObject(currentSerial, &imageView);
}
}
mLayerLevelDrawImageViews.clear();
for (vk::ImageView &imageView : mLayerFetchImageView)
{
context->releaseObject(currentSerial, &imageView);
contextVk->releaseObject(currentSerial, &imageView);
}
mLayerFetchImageView.clear();
mCubeMapRenderTargets.clear();
......
......@@ -163,6 +163,8 @@ class TextureVk : public TextureImpl
angle::Result ensureImageInitialized(ContextVk *contextVk);
Serial getSerial() const { return mSerial; }
private:
// 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
......@@ -170,9 +172,9 @@ class TextureVk : public TextureImpl
uint32_t getNativeImageLevel(uint32_t frontendLevel) const;
uint32_t getNativeImageLayer(uint32_t frontendLayer) const;
void releaseAndDeleteImage(ContextVk *context);
angle::Result ensureImageAllocated(ContextVk *context, const vk::Format &format);
void setImageHelper(ContextVk *context,
void releaseAndDeleteImage(ContextVk *contextVk);
angle::Result ensureImageAllocated(ContextVk *contextVk, const vk::Format &format);
void setImageHelper(ContextVk *contextVk,
vk::ImageHelper *imageHelper,
gl::TextureType imageType,
const vk::Format &format,
......@@ -302,6 +304,9 @@ class TextureVk : public TextureImpl
RenderTargetVk mRenderTarget;
std::vector<vk::ImageView> mLayerFetchImageView;
std::vector<RenderTargetVk> mCubeMapRenderTargets;
// The serial is used for cache indexing.
Serial mSerial;
};
} // namespace rx
......
......@@ -1436,6 +1436,52 @@ void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits,
{
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
// RenderPassCache implementation.
......
......@@ -684,6 +684,28 @@ class PipelineHelper final : angle::NonCopyable
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 rx
......@@ -719,6 +741,12 @@ struct hash<rx::vk::PipelineLayoutDesc>
{
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 rx
......@@ -884,7 +912,6 @@ constexpr uint32_t kReservedDriverUniformBindingCount = 1;
constexpr uint32_t kVertexUniformsBindingIndex = 0;
// Binding index for default uniforms in the fragment shader:
constexpr uint32_t kFragmentUniformsBindingIndex = 1;
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VK_CACHE_UTILS_H_
......@@ -3646,7 +3646,8 @@ void SimpleStateChangeTest::drawToFboWithCulling(const GLenum frontFace, bool ea
GLTexture texture1;
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);
......
......@@ -20,6 +20,7 @@
#include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include "util/shader_utils.h"
using namespace angle;
......@@ -165,23 +166,15 @@ TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdates)
ASSERT_TRUE(IsVulkan());
// Initialize texture program.
constexpr char kVS[] = R"(attribute vec2 position;
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;
constexpr char kFS[] = R"(varying mediump vec2 v_texCoord;
uniform sampler2D tex;
uniform mediump vec4 colorMask;
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);
limitMaxSets(program);
......@@ -215,26 +208,106 @@ void main()
// Draw with white.
glUniform1i(texLoc, 0);
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.
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.
glUniform1i(texLoc, 1);
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.
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();
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.
TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdatesTwoShaders)
{
......
......@@ -30,11 +30,17 @@ class GLWrapper : angle::NonCopyable
// The move-constructor and move-assignment operators are necessary so that the data within a
// 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)
{
if (this != &rht)
{
mGenFunc = rht.mGenFunc;
mDeleteFunc = rht.mDeleteFunc;
std::swap(mHandle, rht.mHandle);
}
return *this;
......
......@@ -277,6 +277,11 @@ const char *ColorUniform()
return "u_color";
}
const char *Texture2DUniform()
{
return "u_tex2D";
}
namespace vs
{
......@@ -318,16 +323,16 @@ void main()
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
// texcoord.
const char *Texture()
const char *Texture2D()
{
return R"(precision highp float;
attribute vec4 a_position;
varying vec2 texcoord;
varying vec2 v_texCoord;
void main()
{
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()
}
// A shader that samples the texture.
const char *Texture()
const char *Texture2D()
{
return R"(precision highp float;
uniform sampler2D tex;
varying vec2 texcoord;
return R"(precision mediump float;
uniform sampler2D u_tex;
varying vec2 v_texCoord;
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
ANGLE_UTIL_EXPORT const char *PositionAttrib();
ANGLE_UTIL_EXPORT const char *ColorUniform();
ANGLE_UTIL_EXPORT const char *Texture2DUniform();
namespace vs
{
......@@ -66,7 +67,9 @@ ANGLE_UTIL_EXPORT const char *Simple();
// v_position.
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
......@@ -90,7 +93,7 @@ ANGLE_UTIL_EXPORT const char *Green();
ANGLE_UTIL_EXPORT const char *Blue();
// A shader that samples the texture
ANGLE_UTIL_EXPORT const char *Texture();
ANGLE_UTIL_EXPORT const char *Texture2D();
} // namespace fs
} // 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