Commit 7a06ac1b by Luc Ferron Committed by Commit Bot

Vulkan: Dynamic update of uniforms

- This change enables us to update uniforms indefintely using VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC descriptor types. - Enables 219 new dEQP tests in the uniform_api namespace. - Creates a new white box test to validate new buffer allocation. Bug: angleproject:2392 Change-Id: I8146e6104a6b7727f63265a4671577d251a8fca8 Reviewed-on: https://chromium-review.googlesource.com/965929 Commit-Queue: Luc Ferron <lucferron@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent ba939718
......@@ -69,11 +69,13 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
mDynamicDescriptorPool(),
mVertexArrayDirty(false),
mTexturesDirty(false),
mStreamingVertexData(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, kStreamingVertexDataSize, 1),
mStreamingIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kStreamingIndexDataSize, 1)
mStreamingVertexData(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, kStreamingVertexDataSize),
mStreamingIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kStreamingIndexDataSize)
{
memset(&mClearColorValue, 0, sizeof(mClearColorValue));
memset(&mClearDepthStencilValue, 0, sizeof(mClearDepthStencilValue));
mStreamingVertexData.init(1);
mStreamingIndexData.init(1);
}
ContextVk::~ContextVk()
......@@ -244,9 +246,12 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
{
ASSERT(!descriptorSets.empty());
const vk::PipelineLayout &pipelineLayout = mRenderer->getGraphicsPipelineLayout();
(*commandBuffer)
->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, usedRange.low(),
usedRange.length(), &descriptorSets[usedRange.low()], 0, nullptr);
usedRange.length(), &descriptorSets[usedRange.low()],
programVk->getDynamicOffsetsCount(),
programVk->getDynamicOffsets());
}
return gl::NoError();
......@@ -318,7 +323,7 @@ gl::Error ContextVk::drawElements(const gl::Context *context,
const bool computeIndexRange = vk::GetImpl(vao)->attribsToStream(contextVk).any();
gl::IndexRange range;
VkBuffer buffer = VK_NULL_HANDLE;
VkDeviceSize offset = 0;
uint32_t offset = 0;
if (elementArrayBuffer)
{
......
......@@ -85,7 +85,7 @@ vk::Error DynamicDescriptorPool::allocateDescriptorSets(
vk::Error DynamicDescriptorPool::allocateNewPool(const VkDevice &device)
{
VkDescriptorPoolSize poolSizes[DescriptorPoolIndexCount];
poolSizes[UniformBufferIndex].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[UniformBufferIndex].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
poolSizes[UniformBufferIndex].descriptorCount =
mUniformBufferDescriptorsPerSet * kMaxSets / DescriptorPoolIndexCount;
poolSizes[TextureIndex].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
......
......@@ -16,6 +16,7 @@
#include "libANGLE/renderer/vulkan/DynamicDescriptorPool.h"
#include "libANGLE/renderer/vulkan/GlslangWrapper.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/StreamingBuffer.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
namespace rx
......@@ -24,18 +25,18 @@ namespace rx
namespace
{
constexpr size_t kUniformBlockStreamingBufferMinSize = 256 * 128;
gl::Error InitDefaultUniformBlock(const gl::Context *context,
VkDevice device,
gl::Shader *shader,
vk::BufferAndMemory *storageOut,
sh::BlockLayoutMap *blockLayoutMapOut,
size_t *requiredSizeOut)
size_t *blockSizeOut)
{
const auto &uniforms = shader->getUniforms(context);
if (uniforms.empty())
{
*requiredSizeOut = 0;
*blockSizeOut = 0;
return gl::NoError();
}
......@@ -47,31 +48,11 @@ gl::Error InitDefaultUniformBlock(const gl::Context *context,
// TODO(jmadill): I think we still need a valid block for the pipeline even if zero sized.
if (blockSize == 0)
{
*requiredSizeOut = 0;
*blockSizeOut = 0;
return gl::NoError();
}
VkBufferCreateInfo uniformBufferInfo;
uniformBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
uniformBufferInfo.pNext = nullptr;
uniformBufferInfo.flags = 0;
uniformBufferInfo.size = blockSize;
uniformBufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
uniformBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
uniformBufferInfo.queueFamilyIndexCount = 0;
uniformBufferInfo.pQueueFamilyIndices = nullptr;
ANGLE_TRY(storageOut->buffer.init(device, uniformBufferInfo));
// Assume host vislble/coherent memory available.
VkMemoryPropertyFlags flags =
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(AllocateBufferMemory(contextVk->getRenderer(), flags, &storageOut->buffer,
&storageOut->memory, requiredSizeOut));
*blockSizeOut = blockSize;
return gl::NoError();
}
......@@ -114,15 +95,21 @@ void ReadFromDefaultUniformBlock(int componentCount,
}
}
vk::Error SyncDefaultUniformBlock(VkDevice device,
vk::DeviceMemory *bufferMemory,
const angle::MemoryBuffer &bufferData)
{
ASSERT(bufferMemory->valid() && !bufferData.empty());
uint8_t *mapPointer = nullptr;
ANGLE_TRY(bufferMemory->map(device, 0, bufferData.size(), 0, &mapPointer));
memcpy(mapPointer, bufferData.data(), bufferData.size());
bufferMemory->unmap(device);
vk::Error SyncDefaultUniformBlock(ContextVk *contextVk,
StreamingBuffer &streamingBuffer,
const angle::MemoryBuffer &bufferData,
uint32_t *outOffset,
bool *outBufferModified)
{
ASSERT(!bufferData.empty());
uint8_t *data = nullptr;
VkBuffer *outBuffer = nullptr;
uint32_t offset;
ANGLE_TRY(streamingBuffer.allocate(contextVk, bufferData.size(), &data, outBuffer, &offset,
outBufferModified));
*outOffset = offset;
memcpy(data, bufferData.data(), bufferData.size());
ANGLE_TRY(streamingBuffer.flush(contextVk));
return vk::NoError();
}
......@@ -132,7 +119,7 @@ enum ShaderIndex : uint32_t
MinShaderIndex = 0,
VertexShader = MinShaderIndex,
FragmentShader = 1,
MaxShaderIndex = 2,
MaxShaderIndex = kShaderTypeCount,
};
gl::Shader *GetShader(const gl::ProgramState &programState, uint32_t shaderIndex)
......@@ -152,7 +139,11 @@ gl::Shader *GetShader(const gl::ProgramState &programState, uint32_t shaderIndex
} // anonymous namespace
ProgramVk::DefaultUniformBlock::DefaultUniformBlock()
: storage(), uniformData(), uniformsDirty(false), uniformLayout()
: storage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
kUniformBlockStreamingBufferMinSize),
uniformData(),
uniformsDirty(false),
uniformLayout()
{
}
......@@ -161,8 +152,13 @@ ProgramVk::DefaultUniformBlock::~DefaultUniformBlock()
}
ProgramVk::ProgramVk(const gl::ProgramState &state)
: ProgramImpl(state), mDefaultUniformBlocks(), mUsedDescriptorSetRange(), mDirtyTextures(true)
: ProgramImpl(state),
mDefaultUniformBlocks(),
mUniformBlocksOffsets(),
mUsedDescriptorSetRange(),
mDirtyTextures(true)
{
mUniformBlocksOffsets.fill(0);
mUsedDescriptorSetRange.invalidate();
}
......@@ -184,8 +180,7 @@ vk::Error ProgramVk::reset(ContextVk *contextVk)
for (auto &uniformBlock : mDefaultUniformBlocks)
{
uniformBlock.storage.memory.destroy(device);
uniformBlock.storage.buffer.destroy(device);
uniformBlock.storage.destroy(device);
}
mEmptyUniformBlockStorage.memory.destroy(device);
......@@ -272,7 +267,7 @@ gl::LinkResult ProgramVk::link(const gl::Context *glContext,
mFragmentModuleSerial = renderer->issueProgramSerial();
}
ANGLE_TRY(initDescriptorSets(contextVk));
ANGLE_TRY(allocateDescriptorSets(contextVk));
ANGLE_TRY(initDefaultUniformBlocks(glContext));
if (!mState.getSamplerUniformRange().empty())
......@@ -292,13 +287,12 @@ gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
VkDevice device = contextVk->getDevice();
// Process vertex and fragment uniforms into std140 packing.
std::array<sh::BlockLayoutMap, 2> layoutMap;
std::array<size_t, 2> requiredBufferSize = {{0, 0}};
std::array<sh::BlockLayoutMap, MaxShaderIndex> layoutMap;
std::array<size_t, MaxShaderIndex> requiredBufferSize = {{0, 0}};
for (uint32_t shaderIndex = MinShaderIndex; shaderIndex < MaxShaderIndex; ++shaderIndex)
{
ANGLE_TRY(InitDefaultUniformBlock(glContext, device, GetShader(mState, shaderIndex),
&mDefaultUniformBlocks[shaderIndex].storage,
ANGLE_TRY(InitDefaultUniformBlock(glContext, GetShader(mState, shaderIndex),
&layoutMap[shaderIndex],
&requiredBufferSize[shaderIndex]));
}
......@@ -308,7 +302,7 @@ gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
const auto &uniforms = mState.getUniforms();
for (size_t locationIndex = 0; locationIndex < locations.size(); ++locationIndex)
{
std::array<sh::BlockMemberInfo, 2> layoutInfo;
std::array<sh::BlockMemberInfo, MaxShaderIndex> layoutInfo;
const auto &location = locations[locationIndex];
if (location.used() && !location.ignored)
......@@ -357,6 +351,12 @@ gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
{
return gl::OutOfMemory() << "Memory allocation failure.";
}
size_t minAlignment = static_cast<size_t>(
renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
mDefaultUniformBlocks[shaderIndex].storage.init(minAlignment);
// Initialize uniform buffer memory to zero by default.
mDefaultUniformBlocks[shaderIndex].uniformData.fill(0);
mDefaultUniformBlocks[shaderIndex].uniformsDirty = true;
......@@ -385,7 +385,7 @@ gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
ANGLE_TRY(mEmptyUniformBlockStorage.buffer.init(device, uniformBufferInfo));
// Assume host vislble/coherent memory available.
// Assume host visible/coherent memory available.
VkMemoryPropertyFlags flags =
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
size_t requiredSize = 0;
......@@ -393,8 +393,6 @@ gl::Error ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext)
&mEmptyUniformBlockStorage.memory, &requiredSize));
}
ANGLE_TRY(updateDefaultUniformsDescriptorSet(contextVk));
// Ensure the descriptor set range includes the uniform buffers at position 0.
mUsedDescriptorSetRange.extend(0);
}
......@@ -652,20 +650,21 @@ Serial ProgramVk::getFragmentModuleSerial() const
return mFragmentModuleSerial;
}
vk::Error ProgramVk::initDescriptorSets(ContextVk *contextVk)
vk::Error ProgramVk::allocateDescriptorSets(ContextVk *contextVk)
{
ASSERT(mDescriptorSets.empty());
RendererVk *renderer = contextVk->getRenderer();
// Write out to a new a descriptor set.
DynamicDescriptorPool *dynamicDescriptorPool = contextVk->getDynamicDescriptorPool();
const auto &descriptorSetLayouts = renderer->getGraphicsDescriptorSetLayouts();
uint32_t descriptorSetCount = static_cast<uint32_t>(descriptorSetLayouts.size());
mDescriptorSets.resize(descriptorSetCount, VK_NULL_HANDLE);
// TODO(lucferron): Its wasteful to reallocate the texture descriptor sets when we only
// care about the uniforms.
// http://anglebug.com/2421
ANGLE_TRY(dynamicDescriptorPool->allocateDescriptorSets(
contextVk, descriptorSetLayouts[0].ptr(), descriptorSetCount, &mDescriptorSets[0]));
return vk::NoError();
......@@ -696,36 +695,53 @@ vk::Error ProgramVk::updateUniforms(ContextVk *contextVk)
ASSERT(mUsedDescriptorSetRange.contains(0));
VkDevice device = contextVk->getDevice();
// Update buffer memory by immediate mapping. This immediate update only works once.
// TODO(jmadill): Handle inserting updates into the command stream, or use dynamic buffers.
for (auto &uniformBlock : mDefaultUniformBlocks)
bool anyNewBufferAllocated = false;
for (size_t index = 0; index < mDefaultUniformBlocks.size(); index++)
{
DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[index];
if (uniformBlock.uniformsDirty)
{
ANGLE_TRY(SyncDefaultUniformBlock(device, &uniformBlock.storage.memory,
uniformBlock.uniformData));
bool bufferModified = false;
ANGLE_TRY(SyncDefaultUniformBlock(contextVk, uniformBlock.storage,
uniformBlock.uniformData,
&mUniformBlocksOffsets[index], &bufferModified));
uniformBlock.uniformsDirty = false;
if (bufferModified)
{
anyNewBufferAllocated = true;
}
}
}
if (anyNewBufferAllocated)
{
// We need to reinitialize the descriptor sets if we newly allocated buffers since we can't
// modify the descriptor sets once initialized.
ANGLE_TRY(allocateDescriptorSets(contextVk));
ANGLE_TRY(updateDefaultUniformsDescriptorSet(contextVk));
}
return vk::NoError();
}
vk::Error ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
{
std::array<VkDescriptorBufferInfo, 2> descriptorBufferInfo;
std::array<VkWriteDescriptorSet, 2> writeDescriptorInfo;
std::array<VkDescriptorBufferInfo, MaxShaderIndex> descriptorBufferInfo;
std::array<VkWriteDescriptorSet, MaxShaderIndex> writeDescriptorInfo;
uint32_t bufferCount = 0;
for (auto &uniformBlock : mDefaultUniformBlocks)
{
auto &bufferInfo = descriptorBufferInfo[bufferCount];
auto &writeInfo = writeDescriptorInfo[bufferCount];
if (!uniformBlock.uniformData.empty())
{
bufferInfo.buffer = uniformBlock.storage.buffer.getHandle();
bufferInfo.buffer = uniformBlock.storage.getCurrentBufferHandle();
}
else
{
......@@ -735,15 +751,13 @@ vk::Error ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk)
bufferInfo.offset = 0;
bufferInfo.range = VK_WHOLE_SIZE;
auto &writeInfo = writeDescriptorInfo[bufferCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = mDescriptorSets[0];
writeInfo.dstBinding = bufferCount;
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
writeInfo.pImageInfo = nullptr;
writeInfo.pBufferInfo = &bufferInfo;
writeInfo.pTexelBufferView = nullptr;
......@@ -763,6 +777,24 @@ const std::vector<VkDescriptorSet> &ProgramVk::getDescriptorSets() const
return mDescriptorSets;
}
const uint32_t *ProgramVk::getDynamicOffsets()
{
// If we have no descriptor set being used, we do not need to specify any offsets when binding
// the descriptor sets.
if (!mUsedDescriptorSetRange.contains(0))
return nullptr;
return mUniformBlocksOffsets.data();
}
uint32_t ProgramVk::getDynamicOffsetsCount()
{
if (!mUsedDescriptorSetRange.contains(0))
return 0;
return static_cast<uint32_t>(mUniformBlocksOffsets.size());
}
const gl::RangeUI &ProgramVk::getUsedDescriptorSetRange() const
{
return mUsedDescriptorSetRange;
......@@ -837,4 +869,11 @@ void ProgramVk::invalidateTextures()
mDirtyTextures = true;
}
void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
{
for (DefaultUniformBlock &block : mDefaultUniformBlocks)
{
block.storage.setMinimumSize(minSize);
}
}
} // namespace rx
......@@ -10,14 +10,19 @@
#ifndef LIBANGLE_RENDERER_VULKAN_PROGRAMVK_H_
#define LIBANGLE_RENDERER_VULKAN_PROGRAMVK_H_
#include <array>
#include "libANGLE/Constants.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/vulkan/StreamingBuffer.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
#include <array>
namespace rx
{
namespace
{
constexpr uint32_t kShaderTypeCount = 2;
} // anonymous namespace.
class ProgramVk : public ProgramImpl
{
......@@ -107,6 +112,8 @@ class ProgramVk : public ProgramImpl
vk::Error updateUniforms(ContextVk *contextVk);
const std::vector<VkDescriptorSet> &getDescriptorSets() const;
const uint32_t *getDynamicOffsets();
uint32_t getDynamicOffsetsCount();
// In Vulkan, it is invalid to pass in a NULL descriptor set to vkCmdBindDescriptorSets.
// However, it's valid to leave them in an undefined, unbound state, if they are never used.
......@@ -119,9 +126,12 @@ class ProgramVk : public ProgramImpl
void updateTexturesDescriptorSet(ContextVk *contextVk);
void invalidateTextures();
// For testing only.
void setDefaultUniformBlocksMinSizeForTesting(size_t minSize);
private:
vk::Error reset(ContextVk *contextVk);
vk::Error initDescriptorSets(ContextVk *contextVk);
vk::Error allocateDescriptorSets(ContextVk *contextVk);
gl::Error initDefaultUniformBlocks(const gl::Context *glContext);
vk::Error updateDefaultUniformsDescriptorSet(ContextVk *contextVk);
......@@ -142,7 +152,7 @@ class ProgramVk : public ProgramImpl
DefaultUniformBlock();
~DefaultUniformBlock();
vk::BufferAndMemory storage;
StreamingBuffer storage;
// Shadow copies of the shader uniform data.
angle::MemoryBuffer uniformData;
......@@ -153,7 +163,8 @@ class ProgramVk : public ProgramImpl
std::vector<sh::BlockMemberInfo> uniformLayout;
};
std::array<DefaultUniformBlock, 2> mDefaultUniformBlocks;
std::array<DefaultUniformBlock, kShaderTypeCount> mDefaultUniformBlocks;
std::array<uint32_t, kShaderTypeCount> mUniformBlocksOffsets;
// This is a special "empty" placeholder buffer for when a shader has no uniforms.
// It is necessary because we want to keep a compatible pipeline layout in all cases,
......
......@@ -829,7 +829,7 @@ vk::Error RendererVk::initGraphicsPipelineLayout()
auto &layoutBinding = uniformBindings[blockCount];
layoutBinding.binding = blockCount;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layoutBinding.pImmutableSamplers = nullptr;
......@@ -841,7 +841,7 @@ vk::Error RendererVk::initGraphicsPipelineLayout()
auto &layoutBinding = uniformBindings[blockCount];
layoutBinding.binding = blockCount;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layoutBinding.pImmutableSamplers = nullptr;
......
......@@ -17,35 +17,48 @@
namespace rx
{
StreamingBuffer::StreamingBuffer(VkBufferUsageFlags usage, size_t minSize, size_t minAlignment)
StreamingBuffer::StreamingBuffer(VkBufferUsageFlags usage, size_t minSize)
: mUsage(usage),
mMinSize(minSize),
mNextWriteOffset(0),
mLastFlushOffset(0),
mSize(0),
mMinAlignment(minAlignment),
mAlignment(0),
mMappedMemory(nullptr)
{
}
void StreamingBuffer::init(size_t alignment)
{
ASSERT(alignment > 0);
mAlignment = alignment;
}
StreamingBuffer::~StreamingBuffer()
{
ASSERT(mAlignment == 0);
}
bool StreamingBuffer::valid()
{
return mAlignment > 0;
}
gl::Error StreamingBuffer::allocate(ContextVk *context,
vk::Error StreamingBuffer::allocate(ContextVk *context,
size_t sizeInBytes,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut,
uint32_t *offsetOut,
bool *outNewBufferAllocated)
{
ASSERT(valid());
RendererVk *renderer = context->getRenderer();
// TODO(fjhenigman): Update this when we have buffers that need to
// persist longer than one frame.
updateQueueSerial(renderer->getCurrentQueueSerial());
size_t sizeToAllocate = roundUp(sizeInBytes, mMinAlignment);
size_t sizeToAllocate = roundUp(sizeInBytes, mAlignment);
angle::base::CheckedNumeric<size_t> checkedNextWriteOffset = mNextWriteOffset;
checkedNextWriteOffset += sizeToAllocate;
......@@ -93,17 +106,21 @@ gl::Error StreamingBuffer::allocate(ContextVk *context,
}
ASSERT(mBuffer.valid());
*handleOut = mBuffer.getHandle();
if (handleOut != nullptr)
{
*handleOut = mBuffer.getHandle();
}
ASSERT(mMappedMemory);
*ptrOut = mMappedMemory + mNextWriteOffset;
*offsetOut = mNextWriteOffset;
mNextWriteOffset += sizeToAllocate;
mNextWriteOffset += static_cast<uint32_t>(sizeToAllocate);
return gl::NoError();
return vk::NoError();
}
gl::Error StreamingBuffer::flush(ContextVk *context)
vk::Error StreamingBuffer::flush(ContextVk *context)
{
if (mNextWriteOffset > mLastFlushOffset)
{
......@@ -117,13 +134,27 @@ gl::Error StreamingBuffer::flush(ContextVk *context)
mLastFlushOffset = mNextWriteOffset;
}
return gl::NoError();
return vk::NoError();
}
void StreamingBuffer::destroy(VkDevice device)
{
mAlignment = 0;
mBuffer.destroy(device);
mMemory.destroy(device);
}
VkBuffer StreamingBuffer::getCurrentBufferHandle() const
{
return mBuffer.getHandle();
}
void StreamingBuffer::setMinimumSize(size_t minSize)
{
// This will really only have an effect next time we call allocate.
mMinSize = minSize;
// Forces a new allocation on the next allocate.
mSize = 0;
}
} // namespace rx
......@@ -19,26 +19,32 @@ namespace rx
class StreamingBuffer : public ResourceVk
{
public:
StreamingBuffer(VkBufferUsageFlags usage, size_t minSize, size_t minAlignment);
StreamingBuffer(VkBufferUsageFlags usage, size_t minSize);
void init(size_t alignment);
bool valid();
~StreamingBuffer();
gl::Error allocate(ContextVk *context,
vk::Error allocate(ContextVk *context,
size_t sizeInBytes,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut,
uint32_t *offsetOut,
bool *outNewBufferAllocated);
gl::Error flush(ContextVk *context);
vk::Error flush(ContextVk *context);
void destroy(VkDevice device);
VkBuffer getCurrentBufferHandle() const;
// For testing only!
void setMinimumSize(size_t minSize);
private:
VkBufferUsageFlags mUsage;
size_t mMinSize;
vk::Buffer mBuffer;
vk::DeviceMemory mMemory;
size_t mNextWriteOffset;
size_t mLastFlushOffset;
uint32_t mNextWriteOffset;
uint32_t mLastFlushOffset;
size_t mSize;
size_t mMinAlignment;
size_t mAlignment;
uint8_t *mMappedMemory;
};
......
......@@ -81,9 +81,10 @@ gl::Error VertexArrayVk::streamVertexData(ContextVk *context,
const size_t lastByte =
lastVertex * binding.getStride() + gl::ComputeVertexAttributeTypeSize(attrib);
uint8_t *dst = nullptr;
uint32_t offset = 0;
ANGLE_TRY(stream->allocate(context, lastByte, &dst,
&mCurrentArrayBufferHandles[attribIndex],
&mCurrentArrayBufferOffsets[attribIndex], nullptr));
&mCurrentArrayBufferHandles[attribIndex], &offset, nullptr));
mCurrentArrayBufferOffsets[attribIndex] = static_cast<VkDeviceSize>(offset);
memcpy(dst + firstByte, static_cast<const uint8_t *>(attrib.pointer) + firstByte,
lastByte - firstByte);
}
......
......@@ -1367,11 +1367,11 @@ LineLoopHandler::LineLoopHandler()
: mObserverBinding(this, 0u),
mStreamingLineLoopIndicesData(
new StreamingBuffer(VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
kLineLoopStreamingBufferMinSize,
1)),
kLineLoopStreamingBufferMinSize)),
mLineLoopIndexBuffer(VK_NULL_HANDLE),
mLineLoopIndexBufferOffset(VK_NULL_HANDLE)
{
mStreamingLineLoopIndicesData->init(1);
}
LineLoopHandler::~LineLoopHandler() = default;
......
......@@ -724,7 +724,7 @@ class LineLoopHandler final : angle::NonCopyable, angle::ObserverInterface
angle::ObserverBinding mObserverBinding;
std::unique_ptr<StreamingBuffer> mStreamingLineLoopIndicesData;
VkBuffer mLineLoopIndexBuffer;
VkDeviceSize mLineLoopIndexBufferOffset;
uint32_t mLineLoopIndexBufferOffset;
Optional<int> mLineLoopBufferFirstIndex;
Optional<int> mLineLoopBufferLastIndex;
};
......
......@@ -222,6 +222,7 @@ if (is_win || is_linux || is_mac || is_android) {
if (angle_enable_vulkan) {
sources += [ "gl_tests/VulkanFormatTablesTest.cpp" ]
sources += [ "gl_tests/VulkanUniformUpdatesTest.cpp" ]
}
configs += [
......
......@@ -232,7 +232,97 @@
2161 VULKAN : dEQP-GLES2.functional.rasterization.limits.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.attribute_location.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.multisample.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.basic_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.struct_in_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.array_in_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.multiple_basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.multiple_nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.info_query.unused_uniforms.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic.bool_api_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic.bvec2_api_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic.bvec3_api_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic.bvec4_api_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic.sampler* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.struct_in_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.array_in_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.multiple_basic.vertex = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.multiple_basic.fragment = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.multiple_basic.both = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.multiple_basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.multiple_nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.basic.samplerCube_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.basic_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.struct_in_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.array_in_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.multiple_basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.multiple_nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.initial.render.nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.ivec2_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.ivec3_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.ivec4_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.bool_api_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.bvec2_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.bvec3_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.bvec4_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic.sampler* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.struct_in_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.array_in_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic_array_first_elem_without_brackets.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.multiple_basic.vertex = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.multiple_basic.fragment = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.multiple_basic.both = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.multiple_basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.multiple_nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic.mat2_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic.mat3_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic.ivec2_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic.ivec3_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic.ivec4_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic.samplerCube* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.struct_in_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.array_in_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.multiple_basic.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.ivec2_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.ivec3_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.ivec4_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.bool_api_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.bvec2_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.bvec3_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.bvec4_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic.sampler* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic_array_first_elem_without_brackets.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.struct_in_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.array_in_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.multiple_basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.multiple_nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.basic.ivec2_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.basic.ivec3_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.basic.ivec4_* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.basic.samplerCube* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.basic_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.basic_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.struct_in_array.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.array_in_struct.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.nested_structs_arrays.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.basic_array_assign_full.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.basic_array_assign_partial.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.value.assigned.unused_uniforms.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.uniform_api.random.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.read_pixels.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.depth_range.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.dither.* = SKIP
......
......@@ -1392,6 +1392,56 @@ TEST_P(SimpleStateChangeTest, ScissorTest)
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
}
// This test validates we are able to change the valid of a uniform dynamically.
TEST_P(SimpleStateChangeTest, UniformUpdateTest)
{
constexpr char kPositionUniformVertexShader[] = R"(
precision mediump float;
attribute vec2 position;
uniform vec2 uniPosModifier;
void main()
{
gl_Position = vec4(position + uniPosModifier, 0, 1);
})";
constexpr char kColorUniformFragmentShader[] = R"(
precision mediump float;
uniform vec4 uniColor;
void main()
{
gl_FragColor = uniColor;
})";
ANGLE_GL_PROGRAM(program, kPositionUniformVertexShader, kColorUniformFragmentShader);
glUseProgram(program);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint posUniformLocation = glGetUniformLocation(program, "uniPosModifier");
ASSERT_NE(posUniformLocation, -1);
GLint colorUniformLocation = glGetUniformLocation(program, "uniColor");
ASSERT_NE(colorUniformLocation, -1);
// draw a red quad to the left side.
glUniform2f(posUniformLocation, -0.5, 0.0);
glUniform4f(colorUniformLocation, 1.0, 0.0, 0.0, 1.0);
drawQuad(program.get(), "position", 0.0f, 0.5f, true);
// draw a green quad to the right side.
glUniform2f(posUniformLocation, 0.5, 0.0);
glUniform4f(colorUniformLocation, 0.0, 1.0, 0.0, 1.0);
drawQuad(program.get(), "position", 0.0f, 0.5f, true);
ASSERT_GL_NO_ERROR();
// Test the center of the left quad. Should be red.
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 2, GLColor::red);
// Test the center of the right quad. Should be green.
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4 * 3, getWindowHeight() / 2, GLColor::green);
}
// Tests that changing the storage of a Renderbuffer currently in use by GL works as expected.
TEST_P(SimpleStateChangeTest, RedefineRenderbufferInUse)
{
......
//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// VulkanUniformUpdatesTest:
// Tests to validate our Vulkan dynamic uniform updates are working as expected.
//
#include "test_utils/ANGLETest.h"
#include "test_utils/angle_test_instantiate.h"
// 'None' is defined as 'struct None {};' in
// third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h.
// But 'None' is also defined as a numeric constant 0L in <X11/X.h>.
// So we need to include ANGLETest.h first to avoid this conflict.
#include "libANGLE/Context.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
class VulkanUniformUpdatesTest : public ANGLETest
{
};
// This test updates a uniform until a new buffer is allocated and then make sure the uniform
// updates still work.
TEST_P(VulkanUniformUpdatesTest, UpdateUniformUntilNewBufferIsAllocated)
{
ASSERT_TRUE(IsVulkan());
constexpr char kPositionUniformVertexShader[] = R"(
precision mediump float;
attribute vec2 position;
uniform vec2 uniPosModifier;
void main()
{
gl_Position = vec4(position + uniPosModifier, 0, 1);
})";
constexpr char kColorUniformFragmentShader[] = R"(
precision mediump float;
uniform vec4 uniColor;
void main()
{
gl_FragColor = uniColor;
})";
// Hack the angle!
const gl::Context *context = reinterpret_cast<gl::Context *>(getEGLWindow()->getContext());
auto *contextVk = rx::GetImplAs<rx::ContextVk>(context);
ANGLE_GL_PROGRAM(program, kPositionUniformVertexShader, kColorUniformFragmentShader);
glUseProgram(program);
const gl::State &state = contextVk->getGLState();
rx::ProgramVk *programVk = rx::vk::GetImpl(state.getProgram());
// Set a really small min size so that uniform updates often allocates a new buffer.
programVk->setDefaultUniformBlocksMinSizeForTesting(128);
GLint posUniformLocation = glGetUniformLocation(program, "uniPosModifier");
ASSERT_NE(posUniformLocation, -1);
GLint colorUniformLocation = glGetUniformLocation(program, "uniColor");
ASSERT_NE(colorUniformLocation, -1);
// Sets both uniforms 10 times, it should certainly trigger new buffers creations by the
// underlying StreamingBuffer.
for (int i = 0; i < 100; i++)
{
glUniform2f(posUniformLocation, -0.5, 0.0);
glUniform4f(colorUniformLocation, 1.0, 0.0, 0.0, 1.0);
drawQuad(program, "position", 0.5f, 1.0f);
swapBuffers();
ASSERT_GL_NO_ERROR();
}
}
ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN());
} // anonymous namespace
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