Commit 8765b46a by Charlie Lao Committed by Commit Bot

Vulkan: Make mDefaultUniformStorage per ContextVk

Right now the dynamic buffer for default uniform is per program. Most of time the buffer is unused. That is a huge waste of memory (and these memory are wired memory). This CL moves the mDefaultUniformStorage from per ProgramVk to ContextVk so that we all share with each other. Bug: b/161391337 Change-Id: I1fe8523b2b2dbc39bec3509a3432e38e34bd5713 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2274870Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com> Commit-Queue: Charlie Lao <cclao@google.com>
parent d92c4ab6
...@@ -748,6 +748,7 @@ void ContextVk::onDestroy(const gl::Context *context) ...@@ -748,6 +748,7 @@ void ContextVk::onDestroy(const gl::Context *context)
mDriverUniformsDescriptorPool.destroy(device); mDriverUniformsDescriptorPool.destroy(device);
mDefaultUniformStorage.release(mRenderer);
mEmptyBuffer.release(mRenderer); mEmptyBuffer.release(mRenderer);
for (vk::DynamicBuffer &defaultBuffer : mDefaultAttribBuffers) for (vk::DynamicBuffer &defaultBuffer : mDefaultAttribBuffers)
...@@ -882,6 +883,14 @@ angle::Result ContextVk::initialize() ...@@ -882,6 +883,14 @@ angle::Result ContextVk::initialize()
TRACE_EVENT_PHASE_BEGIN, eventName)); TRACE_EVENT_PHASE_BEGIN, eventName));
} }
size_t minAlignment = static_cast<size_t>(
mRenderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
// This size is picked based on experience, may needs more tuning.
size_t uniformBufferSize =
std::min(16 * 1024u, mRenderer->getPhysicalDeviceProperties().limits.maxUniformBufferRange);
mDefaultUniformStorage.init(mRenderer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, minAlignment,
uniformBufferSize, true);
// Initialize an "empty" buffer for use with default uniform blocks where there are no uniforms, // Initialize an "empty" buffer for use with default uniform blocks where there are no uniforms,
// or atomic counter buffer array indices that are unused. // or atomic counter buffer array indices that are unused.
constexpr VkBufferUsageFlags kEmptyBufferUsage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | constexpr VkBufferUsageFlags kEmptyBufferUsage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
...@@ -1486,7 +1495,7 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation( ...@@ -1486,7 +1495,7 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation(
} }
// TODO(http://anglebug.com/3570): Need to update to handle Program Pipelines // TODO(http://anglebug.com/3570): Need to update to handle Program Pipelines
vk::BufferHelper *uniformBuffer = mProgram->getDefaultUniformBuffer(); vk::BufferHelper *uniformBuffer = mDefaultUniformStorage.getCurrentBuffer();
vk::UniformsAndXfbDesc xfbBufferDesc = transformFeedbackVk->getTransformFeedbackDesc(); vk::UniformsAndXfbDesc xfbBufferDesc = transformFeedbackVk->getTransformFeedbackDesc();
xfbBufferDesc.updateDefaultUniformBuffer(uniformBuffer ? uniformBuffer->getBufferSerial() xfbBufferDesc.updateDefaultUniformBuffer(uniformBuffer ? uniformBuffer->getBufferSerial()
: kInvalidBufferSerial); : kInvalidBufferSerial);
...@@ -2684,6 +2693,15 @@ void ContextVk::invalidateProgramBindingHelper(const gl::State &glState) ...@@ -2684,6 +2693,15 @@ void ContextVk::invalidateProgramBindingHelper(const gl::State &glState)
mExecutable = &mProgramPipeline->getExecutable(); mExecutable = &mProgramPipeline->getExecutable();
} }
} }
if (mProgram)
{
mProgram->onProgramBind();
}
else if (mProgramPipeline)
{
mProgramPipeline->onProgramBind(this);
}
} }
angle::Result ContextVk::invalidateProgramExecutableHelper(const gl::Context *context) angle::Result ContextVk::invalidateProgramExecutableHelper(const gl::Context *context)
...@@ -3947,6 +3965,7 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore) ...@@ -3947,6 +3965,7 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
{ {
driverUniform.dynamicBuffer.releaseInFlightBuffersToResourceUseList(this); driverUniform.dynamicBuffer.releaseInFlightBuffersToResourceUseList(this);
} }
mDefaultUniformStorage.releaseInFlightBuffersToResourceUseList(this);
if (mRenderer->getFeatures().enableCommandProcessingThread.enabled) if (mRenderer->getFeatures().enableCommandProcessingThread.enabled)
{ {
...@@ -4701,4 +4720,9 @@ ImageViewSerial ContextVk::generateAttachmentImageViewSerial() ...@@ -4701,4 +4720,9 @@ ImageViewSerial ContextVk::generateAttachmentImageViewSerial()
return mShareGroupVk->generateImageViewSerial(); return mShareGroupVk->generateImageViewSerial();
} }
void ContextVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
{
mDefaultUniformStorage.setMinimumSizeForTesting(minSize);
}
} // namespace rx } // namespace rx
...@@ -586,6 +586,10 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -586,6 +586,10 @@ class ContextVk : public ContextImpl, public vk::Context
return mWriteInfos[oldSize]; return mWriteInfos[oldSize];
} }
vk::DynamicBuffer *getDefaultUniformStorage() { return &mDefaultUniformStorage; }
// For testing only.
void setDefaultUniformBlocksMinSizeForTesting(size_t minSize);
vk::BufferHelper &getEmptyBuffer() { return mEmptyBuffer; } vk::BufferHelper &getEmptyBuffer() { return mEmptyBuffer; }
private: private:
...@@ -1062,6 +1066,9 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -1062,6 +1066,9 @@ class ContextVk : public ContextImpl, public vk::Context
ShareGroupVk *mShareGroupVk; ShareGroupVk *mShareGroupVk;
// Storage for default uniforms of ProgramVks and ProgramPipelineVks.
vk::DynamicBuffer mDefaultUniformStorage;
// This is a special "empty" placeholder buffer for use when we just need a dummy buffer but not // This is a special "empty" placeholder buffer for use when we just need a dummy buffer but not
// the data. Examples are shader that has no uniform or doesn't use all slots in the atomic // the data. Examples are shader that has no uniform or doesn't use all slots in the atomic
// counter buffer array, or places where there is no vertex buffer since Vulkan does not allow // counter buffer array, or places where there is no vertex buffer since Vulkan does not allow
......
...@@ -188,8 +188,9 @@ void ProgramExecutableVk::reset(ContextVk *contextVk) ...@@ -188,8 +188,9 @@ void ProgramExecutableVk::reset(ContextVk *contextVk)
} }
mTextureDescriptorsCache.clear(); mTextureDescriptorsCache.clear();
mDescriptorBuffersCache.clear();
mUniformsAndXfbDescriptorSetCache.clear(); mUniformsAndXfbDescriptorSetCache.clear();
// Initialize with a unique BufferSerial
mCurrentDefaultUniformBufferSerial = contextVk->generateBufferSerial();
for (ProgramInfo &programInfo : mGraphicsProgramInfos) for (ProgramInfo &programInfo : mGraphicsProgramInfos)
{ {
...@@ -338,6 +339,8 @@ angle::Result ProgramExecutableVk::allocUniformAndXfbDescriptorSet( ...@@ -338,6 +339,8 @@ angle::Result ProgramExecutableVk::allocUniformAndXfbDescriptorSet(
const vk::UniformsAndXfbDesc &xfbBufferDesc, const vk::UniformsAndXfbDesc &xfbBufferDesc,
bool *newDescriptorSetAllocated) bool *newDescriptorSetAllocated)
{ {
mCurrentDefaultUniformBufferSerial = xfbBufferDesc.getDefaultUniformBufferSerial();
// Look up in the cache first // Look up in the cache first
auto iter = mUniformsAndXfbDescriptorSetCache.find(xfbBufferDesc); auto iter = mUniformsAndXfbDescriptorSetCache.find(xfbBufferDesc);
if (iter != mUniformsAndXfbDescriptorSetCache.end()) if (iter != mUniformsAndXfbDescriptorSetCache.end())
...@@ -881,14 +884,12 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet( ...@@ -881,14 +884,12 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
if (!defaultUniformBlock.uniformData.empty()) if (!defaultUniformBlock.uniformData.empty())
{ {
bufferInfo.buffer = defaultUniformBuffer->getBuffer().getHandle(); bufferInfo.buffer = defaultUniformBuffer->getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(defaultUniformBuffer);
} }
else else
{ {
vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer(); vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
emptyBuffer.retain(&contextVk->getResourceUseList()); emptyBuffer.retain(&contextVk->getResourceUseList());
bufferInfo.buffer = emptyBuffer.getBuffer().getHandle(); bufferInfo.buffer = emptyBuffer.getBuffer().getHandle();
mDescriptorBuffersCache.emplace_back(&emptyBuffer);
} }
bufferInfo.offset = 0; bufferInfo.offset = 0;
...@@ -1212,7 +1213,6 @@ angle::Result ProgramExecutableVk::updateTransformFeedbackDescriptorSet( ...@@ -1212,7 +1213,6 @@ angle::Result ProgramExecutableVk::updateTransformFeedbackDescriptorSet(
if (newDescriptorSetAllocated) if (newDescriptorSetAllocated)
{ {
mDescriptorBuffersCache.clear();
for (const gl::ShaderType shaderType : executable.getLinkedShaderStages()) for (const gl::ShaderType shaderType : executable.getLinkedShaderStages())
{ {
updateDefaultUniformsDescriptorSet(shaderType, defaultUniformBlocks[shaderType], updateDefaultUniformsDescriptorSet(shaderType, defaultUniformBlocks[shaderType],
...@@ -1456,11 +1456,6 @@ angle::Result ProgramExecutableVk::updateDescriptorSets(ContextVk *contextVk, ...@@ -1456,11 +1456,6 @@ angle::Result ProgramExecutableVk::updateDescriptorSets(ContextVk *contextVk,
mDynamicBufferOffsets.data()); mDynamicBufferOffsets.data());
} }
for (vk::BufferHelper *buffer : mDescriptorBuffersCache)
{
buffer->retain(&contextVk->getResourceUseList());
}
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -123,6 +123,10 @@ class ProgramExecutableVk ...@@ -123,6 +123,10 @@ class ProgramExecutableVk
return mGraphicsProgramInfos[optionBits.to_ulong()]; return mGraphicsProgramInfos[optionBits.to_ulong()];
} }
ProgramInfo &getComputeProgramInfo() { return mComputeProgramInfo; } ProgramInfo &getComputeProgramInfo() { return mComputeProgramInfo; }
BufferSerial getCurrentDefaultUniformBufferSerial() const
{
return mCurrentDefaultUniformBufferSerial;
}
angle::Result getGraphicsPipeline(ContextVk *contextVk, angle::Result getGraphicsPipeline(ContextVk *contextVk,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
...@@ -215,8 +219,8 @@ class ProgramExecutableVk ...@@ -215,8 +219,8 @@ class ProgramExecutableVk
// Descriptor sets for uniform blocks and textures for this program. // Descriptor sets for uniform blocks and textures for this program.
std::vector<VkDescriptorSet> mDescriptorSets; std::vector<VkDescriptorSet> mDescriptorSets;
vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets; vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets;
std::vector<vk::BufferHelper *> mDescriptorBuffersCache;
size_t mNumDefaultUniformDescriptors; size_t mNumDefaultUniformDescriptors;
BufferSerial mCurrentDefaultUniformBufferSerial;
std::unordered_map<vk::UniformsAndXfbDesc, VkDescriptorSet> mUniformsAndXfbDescriptorSetCache; std::unordered_map<vk::UniformsAndXfbDesc, VkDescriptorSet> mUniformsAndXfbDescriptorSetCache;
std::unordered_map<vk::TextureDescriptorDesc, VkDescriptorSet> mTextureDescriptorsCache; std::unordered_map<vk::TextureDescriptorDesc, VkDescriptorSet> mTextureDescriptorsCache;
......
...@@ -89,42 +89,116 @@ angle::Result ProgramPipelineVk::link(const gl::Context *glContext) ...@@ -89,42 +89,116 @@ angle::Result ProgramPipelineVk::link(const gl::Context *glContext)
return mExecutable.createPipelineLayout(glContext); return mExecutable.createPipelineLayout(glContext);
} }
size_t ProgramPipelineVk::calcUniformUpdateRequiredSpace(
ContextVk *contextVk,
const gl::ProgramExecutable &glExecutable,
const gl::State &glState,
gl::ShaderMap<VkDeviceSize> *uniformOffsets) const
{
size_t requiredSpace = 0;
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
ProgramVk *programVk = getShaderProgram(glState, shaderType);
ASSERT(programVk);
if (programVk->isShaderUniformDirty(shaderType))
{
(*uniformOffsets)[shaderType] = requiredSpace;
requiredSpace += programVk->getDefaultUniformAlignedSize(contextVk, shaderType);
}
}
return requiredSpace;
}
angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk) angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
{ {
const gl::State &glState = contextVk->getState();
const gl::ProgramExecutable &glExecutable = *glState.getProgramExecutable();
vk::DynamicBuffer *defaultUniformStorage = contextVk->getDefaultUniformStorage();
uint8_t *bufferData = nullptr;
VkDeviceSize bufferOffset = 0;
uint32_t offsetIndex = 0; uint32_t offsetIndex = 0;
bool anyNewBufferAllocated = false; bool anyNewBufferAllocated = false;
const gl::ProgramExecutable *glExecutable = contextVk->getState().getProgramExecutable(); gl::ShaderMap<VkDeviceSize> offsets; // offset to the beginning of bufferData
size_t requiredSpace;
// We usually only update uniform data for shader stages that are actually dirty. But when the
// buffer for uniform data have switched, because all shader stages are using the same buffer,
// we then must update uniform data for all shader stages to keep all shader stages' unform data
// in the same buffer.
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, glState, &offsets);
ASSERT(requiredSpace > 0);
// Allocate space from dynamicBuffer. Always try to allocate from the current buffer first.
// If that failed, we deal with fall out and try again.
if (!defaultUniformStorage->allocateFromCurrentBuffer(requiredSpace, &bufferData,
&bufferOffset))
{
setAllDefaultUniformsDirty(contextVk->getState());
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, glState, &offsets);
ANGLE_TRY(defaultUniformStorage->allocate(contextVk, requiredSpace, &bufferData, nullptr,
&bufferOffset, &anyNewBufferAllocated));
}
for (const gl::ShaderType shaderType : glExecutable->getLinkedShaderStages()) for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{ {
ProgramVk *programVk = getShaderProgram(contextVk->getState(), shaderType); ProgramVk *programVk = getShaderProgram(glState, shaderType);
if (programVk && programVk->dirtyUniforms()) ASSERT(programVk);
if (programVk->isShaderUniformDirty(shaderType))
{ {
ANGLE_TRY(programVk->updateShaderUniforms( const angle::MemoryBuffer &uniformData =
contextVk, shaderType, &mExecutable.mDynamicBufferOffsets[offsetIndex], programVk->getDefaultUniformBlocks()[shaderType].uniformData;
&anyNewBufferAllocated)); memcpy(&bufferData[offsets[shaderType]], uniformData.data(), uniformData.size());
mExecutable.mDynamicBufferOffsets[offsetIndex] =
static_cast<uint32_t>(bufferOffset + offsets[shaderType]);
programVk->clearShaderUniformDirtyBit(shaderType);
} }
++offsetIndex; ++offsetIndex;
} }
ANGLE_TRY(defaultUniformStorage->flush(contextVk));
// The PPO's list of descriptor sets being empty without a new buffer being allocated indicates
// a Program that was already used in a draw command (and thus already allocated uniform // Because the uniform buffers are per context, we can't rely on dynamicBuffer's allocate
// buffers) has been bound to this PPO. // function to tell us if you have got a new buffer or not. Other program's use of the buffer
if (anyNewBufferAllocated || mExecutable.mDescriptorSets.empty()) // might already pushed dynamicBuffer to a new buffer. We record which buffer (represented by
// the unique BufferSerial number) we were using with the current descriptor set and then we
// use that recorded BufferSerial compare to the current uniform buffer to quickly detect if
// there is a buffer switch or not. We need to retrieve from the descriptor set cache or
// allocate a new descriptor set whenever there is uniform buffer switch.
vk::BufferHelper *defaultUniformBuffer = defaultUniformStorage->getCurrentBuffer();
if (mExecutable.getCurrentDefaultUniformBufferSerial() !=
defaultUniformBuffer->getBufferSerial())
{ {
// We need to reinitialize the descriptor sets if we newly allocated buffers since we can't // We need to reinitialize the descriptor sets if we newly allocated buffers since we can't
// modify the descriptor sets once initialized. // modify the descriptor sets once initialized.
ANGLE_TRY(mExecutable.allocateDescriptorSet(contextVk, kUniformsAndXfbDescriptorSetIndex)); vk::UniformsAndXfbDesc defaultUniformsDesc;
vk::UniformsAndXfbDesc *uniformsAndXfbBufferDesc;
mExecutable.mDescriptorBuffersCache.clear(); if (glExecutable.hasTransformFeedbackOutput())
for (const gl::ShaderType shaderType : glExecutable->getLinkedShaderStages()) {
TransformFeedbackVk *transformFeedbackVk =
vk::GetImpl(glState.getCurrentTransformFeedback());
uniformsAndXfbBufferDesc = &transformFeedbackVk->getTransformFeedbackDesc();
uniformsAndXfbBufferDesc->updateDefaultUniformBuffer(
defaultUniformBuffer->getBufferSerial());
}
else
{ {
ProgramVk *programVk = getShaderProgram(contextVk->getState(), shaderType); defaultUniformsDesc.updateDefaultUniformBuffer(defaultUniformBuffer->getBufferSerial());
if (programVk) uniformsAndXfbBufferDesc = &defaultUniformsDesc;
}
bool newDescriptorSetAllocated;
ANGLE_TRY(mExecutable.allocUniformAndXfbDescriptorSet(contextVk, *uniformsAndXfbBufferDesc,
&newDescriptorSetAllocated));
if (newDescriptorSetAllocated)
{
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{ {
ProgramVk *programVk = getShaderProgram(glState, shaderType);
mExecutable.updateDefaultUniformsDescriptorSet( mExecutable.updateDefaultUniformsDescriptorSet(
shaderType, programVk->getDefaultUniformBlock(shaderType), shaderType, programVk->getDefaultUniformBlocks()[shaderType],
programVk->getDefaultUniformBuffer(), contextVk); defaultUniformBuffer, contextVk);
mExecutable.updateTransformFeedbackDescriptorSetImpl(programVk->getState(), mExecutable.updateTransformFeedbackDescriptorSetImpl(programVk->getState(),
contextVk); contextVk);
} }
...@@ -148,4 +222,21 @@ bool ProgramPipelineVk::dirtyUniforms(const gl::State &glState) ...@@ -148,4 +222,21 @@ bool ProgramPipelineVk::dirtyUniforms(const gl::State &glState)
return false; return false;
} }
void ProgramPipelineVk::setAllDefaultUniformsDirty(const gl::State &glState)
{
const gl::ProgramExecutable &glExecutable = *glState.getProgramExecutable();
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
ProgramVk *programVk = getShaderProgram(glState, shaderType);
ASSERT(programVk);
programVk->setShaderUniformDirtyBit(shaderType);
}
}
void ProgramPipelineVk::onProgramBind(ContextVk *contextVk)
{
setAllDefaultUniformsDirty(contextVk->getState());
}
} // namespace rx } // namespace rx
...@@ -50,8 +50,15 @@ class ProgramPipelineVk : public ProgramPipelineImpl ...@@ -50,8 +50,15 @@ class ProgramPipelineVk : public ProgramPipelineImpl
angle::Result updateUniforms(ContextVk *contextVk); angle::Result updateUniforms(ContextVk *contextVk);
bool dirtyUniforms(const gl::State &glState); bool dirtyUniforms(const gl::State &glState);
void onProgramBind(ContextVk *contextVk);
private: private:
size_t calcUniformUpdateRequiredSpace(ContextVk *contextVk,
const gl::ProgramExecutable &glExecutable,
const gl::State &glState,
gl::ShaderMap<VkDeviceSize> *uniformOffsets) const;
void setAllDefaultUniformsDirty(const gl::State &glState);
ProgramExecutableVk mExecutable; ProgramExecutableVk mExecutable;
}; };
......
...@@ -24,9 +24,6 @@ namespace rx ...@@ -24,9 +24,6 @@ namespace rx
namespace namespace
{ {
// This size is picked according to the required maxUniformBufferRange in the Vulkan spec.
constexpr size_t kUniformBlockDynamicBufferMinSize = 16384u;
// Identical to Std140 encoder in all aspects, except it ignores opaque uniform types. // Identical to Std140 encoder in all aspects, except it ignores opaque uniform types.
class VulkanDefaultBlockEncoder : public sh::Std140BlockEncoder class VulkanDefaultBlockEncoder : public sh::Std140BlockEncoder
{ {
...@@ -133,26 +130,6 @@ void ReadFromDefaultUniformBlock(int componentCount, ...@@ -133,26 +130,6 @@ void ReadFromDefaultUniformBlock(int componentCount,
} }
} }
angle::Result SyncDefaultUniformBlock(ContextVk *contextVk,
vk::DynamicBuffer *dynamicBuffer,
const angle::MemoryBuffer &bufferData,
uint32_t *outOffset,
bool *outBufferModified)
{
dynamicBuffer->releaseInFlightBuffers(contextVk);
ASSERT(!bufferData.empty());
uint8_t *data = nullptr;
VkBuffer *outBuffer = nullptr;
VkDeviceSize offset = 0;
ANGLE_TRY(dynamicBuffer->allocate(contextVk, bufferData.size(), &data, outBuffer, &offset,
outBufferModified));
*outOffset = static_cast<uint32_t>(offset);
memcpy(data, bufferData.data(), bufferData.size());
ANGLE_TRY(dynamicBuffer->flush(contextVk));
return angle::Result::Continue;
}
class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
{ {
public: public:
...@@ -177,12 +154,8 @@ void ProgramVk::destroy(const gl::Context *context) ...@@ -177,12 +154,8 @@ void ProgramVk::destroy(const gl::Context *context)
void ProgramVk::reset(ContextVk *contextVk) void ProgramVk::reset(ContextVk *contextVk)
{ {
RendererVk *renderer = contextVk->getRenderer();
mOriginalShaderInfo.release(contextVk); mOriginalShaderInfo.release(contextVk);
mDefaultUniformStorage.release(renderer);
GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(&mGlslangProgramInterfaceInfo); GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(&mGlslangProgramInterfaceInfo);
mExecutable.reset(contextVk); mExecutable.reset(contextVk);
...@@ -411,7 +384,6 @@ void ProgramVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap ...@@ -411,7 +384,6 @@ void ProgramVk::initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap
angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk, angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk,
gl::ShaderMap<size_t> &requiredBufferSize) gl::ShaderMap<size_t> &requiredBufferSize)
{ {
RendererVk *renderer = contextVk->getRenderer();
const gl::ProgramExecutable &glExecutable = mState.getExecutable(); const gl::ProgramExecutable &glExecutable = mState.getExecutable();
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages()) for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
...@@ -430,12 +402,6 @@ angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk, ...@@ -430,12 +402,6 @@ angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk,
} }
} }
size_t minAlignment = static_cast<size_t>(
renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
mDefaultUniformStorage.init(
renderer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
minAlignment, kUniformBlockDynamicBufferMinSize, true);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -725,30 +691,6 @@ void ProgramVk::getUniformuiv(const gl::Context *context, GLint location, GLuint ...@@ -725,30 +691,6 @@ void ProgramVk::getUniformuiv(const gl::Context *context, GLint location, GLuint
getUniformImpl(location, params, GL_UNSIGNED_INT); getUniformImpl(location, params, GL_UNSIGNED_INT);
} }
angle::Result ProgramVk::updateShaderUniforms(ContextVk *contextVk,
gl::ShaderType shaderType,
uint32_t *outOffset,
bool *anyNewBufferAllocated)
{
// Update buffer memory by immediate mapping. This immediate update only works once.
DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
if (mDefaultUniformBlocksDirty[shaderType])
{
bool bufferModified = false;
ANGLE_TRY(SyncDefaultUniformBlock(contextVk, &mDefaultUniformStorage,
uniformBlock.uniformData, outOffset, &bufferModified));
mDefaultUniformBlocksDirty.reset(shaderType);
if (bufferModified)
{
*anyNewBufferAllocated = true;
}
}
return angle::Result::Continue;
}
size_t ProgramVk::calcUniformUpdateRequiredSpace(ContextVk *contextVk, size_t ProgramVk::calcUniformUpdateRequiredSpace(ContextVk *contextVk,
const gl::ProgramExecutable &glExecutable, const gl::ProgramExecutable &glExecutable,
gl::ShaderMap<VkDeviceSize> &uniformOffsets) const gl::ShaderMap<VkDeviceSize> &uniformOffsets) const
...@@ -774,7 +716,7 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) ...@@ -774,7 +716,7 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
VkDeviceSize bufferOffset = 0; VkDeviceSize bufferOffset = 0;
uint32_t offsetIndex = 0; uint32_t offsetIndex = 0;
const gl::ProgramExecutable &glExecutable = mState.getExecutable(); const gl::ProgramExecutable &glExecutable = mState.getExecutable();
gl::ShaderMap<VkDeviceSize> offsets; gl::ShaderMap<VkDeviceSize> offsets; // offset to the beginning of bufferData
size_t requiredSpace; size_t requiredSpace;
// We usually only update uniform data for shader stages that are actually dirty. But when the // We usually only update uniform data for shader stages that are actually dirty. But when the
...@@ -786,7 +728,8 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) ...@@ -786,7 +728,8 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
// Allocate space from dynamicBuffer. Always try to allocate from the current buffer first. // Allocate space from dynamicBuffer. Always try to allocate from the current buffer first.
// If that failed, we deal with fall out and try again. // If that failed, we deal with fall out and try again.
if (!mDefaultUniformStorage.allocateFromCurrentBuffer(requiredSpace, &bufferData, vk::DynamicBuffer *defaultUniformStorage = contextVk->getDefaultUniformStorage();
if (!defaultUniformStorage->allocateFromCurrentBuffer(requiredSpace, &bufferData,
&bufferOffset)) &bufferOffset))
{ {
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages()) for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
...@@ -797,10 +740,8 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) ...@@ -797,10 +740,8 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
} }
} }
mDefaultUniformStorage.releaseInFlightBuffersToResourceUseList(contextVk);
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, offsets); requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, offsets);
ANGLE_TRY(mDefaultUniformStorage.allocate(contextVk, requiredSpace, &bufferData, nullptr, ANGLE_TRY(defaultUniformStorage->allocate(contextVk, requiredSpace, &bufferData, nullptr,
&bufferOffset, &anyNewBufferAllocated)); &bufferOffset, &anyNewBufferAllocated));
} }
...@@ -817,14 +758,14 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) ...@@ -817,14 +758,14 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
} }
++offsetIndex; ++offsetIndex;
} }
ANGLE_TRY(mDefaultUniformStorage.flush(contextVk)); ANGLE_TRY(defaultUniformStorage->flush(contextVk));
if (anyNewBufferAllocated) vk::BufferHelper *defaultUniformBuffer = defaultUniformStorage->getCurrentBuffer();
if (mExecutable.getCurrentDefaultUniformBufferSerial() !=
defaultUniformBuffer->getBufferSerial())
{ {
// We need to reinitialize the descriptor sets if we newly allocated buffers since we can't // We need to reinitialize the descriptor sets if we newly allocated buffers since we can't
// modify the descriptor sets once initialized. // modify the descriptor sets once initialized.
mExecutable.mDescriptorBuffersCache.clear();
vk::BufferHelper *defaultUniformBuffer = mDefaultUniformStorage.getCurrentBuffer();
vk::UniformsAndXfbDesc defaultUniformsDesc; vk::UniformsAndXfbDesc defaultUniformsDesc;
vk::UniformsAndXfbDesc *uniformsAndXfbBufferDesc; vk::UniformsAndXfbDesc *uniformsAndXfbBufferDesc;
...@@ -856,18 +797,27 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) ...@@ -856,18 +797,27 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
} }
mExecutable.updateTransformFeedbackDescriptorSetImpl(mState, contextVk); mExecutable.updateTransformFeedbackDescriptorSetImpl(mState, contextVk);
} }
else
{
mExecutable.mDescriptorBuffersCache.emplace_back(defaultUniformBuffer);
}
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize) void ProgramVk::setAllDefaultUniformsDirty()
{ {
mDefaultUniformStorage.setMinimumSizeForTesting(minSize); const gl::ProgramExecutable &glExecutable = mState.getExecutable();
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
setShaderUniformDirtyBit(shaderType);
}
} }
void ProgramVk::onProgramBind()
{
// Because all programs share default uniform buffers, when we switch programs, we have to
// re-update all uniform data. We could do more tracking to avoid update if the context's
// current uniform buffer is still the same buffer we last time used and buffer has not been
// recycled. But statistics gathered on gfxbench shows that app always update uniform data on
// program bind anyway, so not really worth it to add more tracking logic here.
setAllDefaultUniformsDirty();
}
} // namespace rx } // namespace rx
...@@ -102,16 +102,25 @@ class ProgramVk : public ProgramImpl ...@@ -102,16 +102,25 @@ class ProgramVk : public ProgramImpl
void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override; void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override;
void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override; void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override;
angle::Result updateShaderUniforms(ContextVk *contextVk,
gl::ShaderType shaderType,
uint32_t *outOffset,
bool *anyNewBufferAllocated);
angle::Result updateUniforms(ContextVk *contextVk); angle::Result updateUniforms(ContextVk *contextVk);
// For testing only.
void setDefaultUniformBlocksMinSizeForTesting(size_t minSize);
bool dirtyUniforms() const { return mDefaultUniformBlocksDirty.any(); } bool dirtyUniforms() const { return mDefaultUniformBlocksDirty.any(); }
bool isShaderUniformDirty(gl::ShaderType shaderType) const
{
return mDefaultUniformBlocksDirty[shaderType];
}
void setShaderUniformDirtyBit(gl::ShaderType shaderType)
{
if (!mDefaultUniformBlocks[shaderType].uniformData.empty())
{
mDefaultUniformBlocksDirty.set(shaderType);
}
}
void clearShaderUniformDirtyBit(gl::ShaderType shaderType)
{
mDefaultUniformBlocksDirty.reset(shaderType);
}
void onProgramBind();
// Used in testing only. // Used in testing only.
vk::DynamicDescriptorPool *getDynamicDescriptorPool(uint32_t poolIndex) vk::DynamicDescriptorPool *getDynamicDescriptorPool(uint32_t poolIndex)
...@@ -123,14 +132,6 @@ class ProgramVk : public ProgramImpl ...@@ -123,14 +132,6 @@ class ProgramVk : public ProgramImpl
ProgramExecutableVk &getExecutable() { return mExecutable; } ProgramExecutableVk &getExecutable() { return mExecutable; }
gl::ShaderMap<DefaultUniformBlock> &getDefaultUniformBlocks() { return mDefaultUniformBlocks; } gl::ShaderMap<DefaultUniformBlock> &getDefaultUniformBlocks() { return mDefaultUniformBlocks; }
const DefaultUniformBlock &getDefaultUniformBlock(const gl::ShaderType shaderType) const
{
return mDefaultUniformBlocks[shaderType];
}
vk::BufferHelper *getDefaultUniformBuffer() const
{
return mDefaultUniformStorage.getCurrentBuffer();
}
size_t getDefaultUniformAlignedSize(ContextVk *contextVk, const gl::ShaderType shaderType) const size_t getDefaultUniformAlignedSize(ContextVk *contextVk, const gl::ShaderType shaderType) const
{ {
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
...@@ -161,8 +162,6 @@ class ProgramVk : public ProgramImpl ...@@ -161,8 +162,6 @@ class ProgramVk : public ProgramImpl
executableVk); executableVk);
} }
ShaderInfo &getOriginalShaderInfo() { return mOriginalShaderInfo; }
GlslangProgramInterfaceInfo &getGlslangProgramInterfaceInfo() GlslangProgramInterfaceInfo &getGlslangProgramInterfaceInfo()
{ {
return mGlslangProgramInterfaceInfo; return mGlslangProgramInterfaceInfo;
...@@ -210,9 +209,10 @@ class ProgramVk : public ProgramImpl ...@@ -210,9 +209,10 @@ class ProgramVk : public ProgramImpl
return angle::Result::Continue; return angle::Result::Continue;
} }
void setAllDefaultUniformsDirty();
gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks; gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks;
gl::ShaderBitSet mDefaultUniformBlocksDirty; gl::ShaderBitSet mDefaultUniformBlocksDirty;
vk::DynamicBuffer mDefaultUniformStorage;
// We keep the SPIR-V code to use for draw call pipeline creation. // We keep the SPIR-V code to use for draw call pipeline creation.
ShaderInfo mOriginalShaderInfo; ShaderInfo mOriginalShaderInfo;
......
...@@ -809,6 +809,10 @@ class UniformsAndXfbDesc ...@@ -809,6 +809,10 @@ class UniformsAndXfbDesc
UniformsAndXfbDesc(const UniformsAndXfbDesc &other); UniformsAndXfbDesc(const UniformsAndXfbDesc &other);
UniformsAndXfbDesc &operator=(const UniformsAndXfbDesc &other); UniformsAndXfbDesc &operator=(const UniformsAndXfbDesc &other);
BufferSerial getDefaultUniformBufferSerial() const
{
return mBufferSerials[kDefaultUniformBufferIndex];
}
void updateDefaultUniformBuffer(BufferSerial bufferSerial) void updateDefaultUniformBuffer(BufferSerial bufferSerial)
{ {
mBufferSerials[kDefaultUniformBufferIndex] = bufferSerial; mBufferSerials[kDefaultUniformBufferIndex] = bufferSerial;
......
...@@ -31,6 +31,16 @@ namespace ...@@ -31,6 +31,16 @@ namespace
class VulkanUniformUpdatesTest : public ANGLETest class VulkanUniformUpdatesTest : public ANGLETest
{ {
protected: protected:
VulkanUniformUpdatesTest() : mLastContext(nullptr) {}
virtual void testSetUp() override
{
// Some of the tests bellow forces uniform buffer size to 128 bytes which may affect other
// tests. This is to ensure that the assumption that each TEST_P will recreate context.
ASSERT(mLastContext != getEGLWindow()->getContext());
mLastContext = getEGLWindow()->getContext();
}
rx::ContextVk *hackANGLE() const rx::ContextVk *hackANGLE() const
{ {
// Hack the angle! // Hack the angle!
...@@ -90,6 +100,9 @@ class VulkanUniformUpdatesTest : public ANGLETest ...@@ -90,6 +100,9 @@ class VulkanUniformUpdatesTest : public ANGLETest
rx::TextureVk *textureVk = hackTexture(texture); rx::TextureVk *textureVk = hackTexture(texture);
textureVk->overrideStagingBufferSizeForTesting(kTextureStagingBufferSizeForTesting); textureVk->overrideStagingBufferSizeForTesting(kTextureStagingBufferSizeForTesting);
} }
private:
EGLContext mLastContext;
}; };
// This test updates a uniform until a new buffer is allocated and then make sure the uniform // This test updates a uniform until a new buffer is allocated and then make sure the uniform
...@@ -117,10 +130,9 @@ void main() ...@@ -117,10 +130,9 @@ void main()
limitMaxSets(program); limitMaxSets(program);
rx::ProgramVk *programVk = hackProgram(program);
// Set a really small min size so that uniform updates often allocates a new buffer. // Set a really small min size so that uniform updates often allocates a new buffer.
programVk->setDefaultUniformBlocksMinSizeForTesting(128); rx::ContextVk *contextVk = hackANGLE();
contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
GLint posUniformLocation = glGetUniformLocation(program, "uniPosModifier"); GLint posUniformLocation = glGetUniformLocation(program, "uniPosModifier");
ASSERT_NE(posUniformLocation, -1); ASSERT_NE(posUniformLocation, -1);
...@@ -360,12 +372,9 @@ void main() ...@@ -360,12 +372,9 @@ void main()
limitMaxSets(program1); limitMaxSets(program1);
limitMaxSets(program2); limitMaxSets(program2);
rx::ProgramVk *program1Vk = hackProgram(program1);
rx::ProgramVk *program2Vk = hackProgram(program2);
// Set a really small min size so that uniform updates often allocates a new buffer. // Set a really small min size so that uniform updates often allocates a new buffer.
program1Vk->setDefaultUniformBlocksMinSizeForTesting(128); rx::ContextVk *contextVk = hackANGLE();
program2Vk->setDefaultUniformBlocksMinSizeForTesting(128); contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
// Get uniform locations. // Get uniform locations.
GLint colorMaskLoc1 = glGetUniformLocation(program1, "colorMask"); GLint colorMaskLoc1 = glGetUniformLocation(program1, "colorMask");
...@@ -506,10 +515,9 @@ void main() ...@@ -506,10 +515,9 @@ void main()
limitMaxSets(program); limitMaxSets(program);
rx::ProgramVk *programVk = hackProgram(program);
// Set a really small min size so that every uniform update actually allocates a new buffer. // Set a really small min size so that every uniform update actually allocates a new buffer.
programVk->setDefaultUniformBlocksMinSizeForTesting(128); rx::ContextVk *contextVk = hackANGLE();
contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
GLint uniformVSLocation = glGetUniformLocation(program, "uniformVS"); GLint uniformVSLocation = glGetUniformLocation(program, "uniformVS");
ASSERT_NE(uniformVSLocation, -1); ASSERT_NE(uniformVSLocation, -1);
...@@ -569,7 +577,6 @@ class PipelineProgramUniformUpdatesTest : public VulkanUniformUpdatesTest ...@@ -569,7 +577,6 @@ class PipelineProgramUniformUpdatesTest : public VulkanUniformUpdatesTest
TEST_P(PipelineProgramUniformUpdatesTest, ToggleBetweenPPOAndProgramVKWithUniformUpdate) TEST_P(PipelineProgramUniformUpdatesTest, ToggleBetweenPPOAndProgramVKWithUniformUpdate)
{ {
ASSERT_TRUE(IsVulkan()); ASSERT_TRUE(IsVulkan());
ANGLE_SKIP_TEST_IF(true && "Known bug. Expected to be fixed by http://b/161391337");
const GLchar *kPositionUniformVertexShader = R"(attribute vec2 position; const GLchar *kPositionUniformVertexShader = R"(attribute vec2 position;
uniform float uniformVS; uniform float uniformVS;
...@@ -608,8 +615,8 @@ void main() ...@@ -608,8 +615,8 @@ void main()
glUseProgram(program); glUseProgram(program);
limitMaxSets(program); limitMaxSets(program);
// Set a really small min size so that every uniform update actually allocates a new buffer. // Set a really small min size so that every uniform update actually allocates a new buffer.
rx::ProgramVk *programVk = hackProgram(program); rx::ContextVk *contextVk = hackANGLE();
programVk->setDefaultUniformBlocksMinSizeForTesting(128); contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
// Setup vertices // Setup vertices
std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices(); std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices();
......
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