Commit 2d5df9d9 by Mohan Maiya Committed by Commit Bot

Vulkan: Don't assume host visibility for external buffers

When importing external buffers, Vulkan ICDs could choose to import the memory into a memoryType that doesn't support the VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT property. Account for this possibility. Bug: angleproject:5073 Bug: angleproject:5909 Change-Id: Ied063b38fa48d0c8508c4aaca9214cc526f393ad Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2783669 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent e61909f6
...@@ -178,6 +178,7 @@ void BufferVk::release(ContextVk *contextVk) ...@@ -178,6 +178,7 @@ void BufferVk::release(ContextVk *contextVk)
} }
mShadowBuffer.release(); mShadowBuffer.release();
mBufferPool.release(renderer); mBufferPool.release(renderer);
mHostVisibleBufferPool.release(renderer);
mBuffer = nullptr; mBuffer = nullptr;
for (ConversionBuffer &buffer : mVertexConversionBuffers) for (ConversionBuffer &buffer : mVertexConversionBuffers)
...@@ -211,6 +212,24 @@ angle::Result BufferVk::initializeShadowBuffer(ContextVk *contextVk, ...@@ -211,6 +212,24 @@ angle::Result BufferVk::initializeShadowBuffer(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
void BufferVk::initializeHostVisibleBufferPool(ContextVk *contextVk)
{
// These buffers will only be used as transfer sources or transfer targets.
constexpr VkImageUsageFlags kUsageFlags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
// These buffers need to be host visible.
constexpr VkMemoryPropertyFlags kDeviceLocalHostCoherentFlags =
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
constexpr size_t kBufferHelperAlignment = 1;
constexpr size_t kBufferHelperPoolInitialSize = 0;
mHostVisibleBufferPool.initWithFlags(
contextVk->getRenderer(), kUsageFlags, kBufferHelperAlignment, kBufferHelperPoolInitialSize,
kDeviceLocalHostCoherentFlags, vk::DynamicBufferPolicy::SporadicTextureUpload);
}
void BufferVk::updateShadowBuffer(const uint8_t *data, size_t size, size_t offset) void BufferVk::updateShadowBuffer(const uint8_t *data, size_t size, size_t offset)
{ {
if (mShadowBuffer.valid()) if (mShadowBuffer.valid())
...@@ -300,6 +319,10 @@ angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context, ...@@ -300,6 +319,10 @@ angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context,
// support a persistent map request. // support a persistent map request.
ANGLE_VK_CHECK(vk::GetImpl(context), !persistentMapRequired, ANGLE_VK_CHECK(vk::GetImpl(context), !persistentMapRequired,
VK_ERROR_MEMORY_MAP_FAILED); VK_ERROR_MEMORY_MAP_FAILED);
// Since external buffer is not host visible, allocate a host visible buffer pool
// to handle map/unmap operations.
initializeHostVisibleBufferPool(vk::GetImpl(context));
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -448,6 +471,48 @@ angle::Result BufferVk::copySubData(const gl::Context *context, ...@@ -448,6 +471,48 @@ angle::Result BufferVk::copySubData(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result BufferVk::handleDeviceLocalBufferMap(ContextVk *contextVk,
VkDeviceSize offset,
VkDeviceSize size,
void **mapPtr)
{
// The buffer is device local, create a copy of the buffer and return its CPU pointer.
bool needToReleasePreviousBuffers = false;
ANGLE_TRY(mHostVisibleBufferPool.allocate(
contextVk, static_cast<size_t>(size), reinterpret_cast<uint8_t **>(mapPtr), nullptr,
&mHostVisibleBufferOffset, &needToReleasePreviousBuffers));
if (needToReleasePreviousBuffers)
{
// Release previous buffers
mHostVisibleBufferPool.releaseInFlightBuffers(contextVk);
}
// Copy data from device local buffer to host visible staging buffer.
vk::BufferHelper *hostVisibleBuffer = mHostVisibleBufferPool.getCurrentBuffer();
ASSERT(hostVisibleBuffer && hostVisibleBuffer->valid());
VkBufferCopy copyRegion = {offset, mHostVisibleBufferOffset, size};
ANGLE_TRY(hostVisibleBuffer->copyFromBuffer(contextVk, mBuffer, 1, &copyRegion));
ANGLE_TRY(
hostVisibleBuffer->waitForIdle(contextVk, "GPU stall due to mapping device local buffer"));
return angle::Result::Continue;
}
angle::Result BufferVk::handleDeviceLocalBufferUnmap(ContextVk *contextVk,
VkDeviceSize offset,
VkDeviceSize size)
{
// Copy data from the host visible buffer into the device local buffer.
vk::BufferHelper *hostVisibleBuffer = mHostVisibleBufferPool.getCurrentBuffer();
ASSERT(hostVisibleBuffer && hostVisibleBuffer->valid());
VkBufferCopy copyRegion = {mHostVisibleBufferOffset, offset, size};
ANGLE_TRY(mBuffer->copyFromBuffer(contextVk, hostVisibleBuffer, 1, &copyRegion));
return angle::Result::Continue;
}
angle::Result BufferVk::map(const gl::Context *context, GLenum access, void **mapPtr) angle::Result BufferVk::map(const gl::Context *context, GLenum access, void **mapPtr)
{ {
ASSERT(mBuffer && mBuffer->valid()); ASSERT(mBuffer && mBuffer->valid());
...@@ -497,8 +562,17 @@ angle::Result BufferVk::mapRangeImpl(ContextVk *contextVk, ...@@ -497,8 +562,17 @@ angle::Result BufferVk::mapRangeImpl(ContextVk *contextVk,
"GPU stall due to mapping buffer in use by the GPU")); "GPU stall due to mapping buffer in use by the GPU"));
} }
ANGLE_TRY(mBuffer->mapWithOffset(contextVk, reinterpret_cast<uint8_t **>(mapPtr), if (mBuffer->isHostVisible())
static_cast<size_t>(offset))); {
// If the buffer is host visible, map and return.
ANGLE_TRY(mBuffer->mapWithOffset(contextVk, reinterpret_cast<uint8_t **>(mapPtr),
static_cast<size_t>(offset)));
}
else
{
// Handle device local buffers.
ANGLE_TRY(handleDeviceLocalBufferMap(contextVk, offset, length, mapPtr));
}
} }
else else
{ {
...@@ -529,29 +603,41 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk) ...@@ -529,29 +603,41 @@ angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
{ {
ASSERT(mBuffer && mBuffer->valid()); ASSERT(mBuffer && mBuffer->valid());
if (!mShadowBuffer.valid()) bool writeOperation = ((mState.getAccessFlags() & GL_MAP_WRITE_BIT) != 0);
if (!mShadowBuffer.valid() && mBuffer->isHostVisible())
{ {
mBuffer->unmap(contextVk->getRenderer()); mBuffer->unmap(contextVk->getRenderer());
} }
else else
{ {
bool writeOperation = ((mState.getAccessFlags() & GL_MAP_WRITE_BIT) != 0); size_t offset = static_cast<size_t>(mState.getMapOffset());
size_t offset = static_cast<size_t>(mState.getMapOffset()); size_t size = static_cast<size_t>(mState.getMapLength());
size_t size = static_cast<size_t>(mState.getMapLength());
// If it was a write operation we need to update the GPU buffer. // If it was a write operation we need to update the buffer with new data.
if (writeOperation) if (writeOperation)
{ {
// We do not yet know if this data will ever be used. Perform a staged if (mShadowBuffer.valid())
// update which will get flushed if and when necessary. {
const uint8_t *data = getShadowBuffer(offset); // We do not yet know if this data will ever be used. Perform a staged
ANGLE_TRY(stagedUpdate(contextVk, data, size, offset)); // update which will get flushed if and when necessary.
const uint8_t *data = getShadowBuffer(offset);
ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
mShadowBuffer.unmap();
}
else
{
// The buffer is device local.
ASSERT(!mBuffer->isHostVisible());
ANGLE_TRY(handleDeviceLocalBufferUnmap(contextVk, offset, size));
}
} }
mShadowBuffer.unmap();
} }
markConversionBuffersDirty(); if (writeOperation)
{
markConversionBuffersDirty();
}
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -607,6 +693,21 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context, ...@@ -607,6 +693,21 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result BufferVk::updateBuffer(ContextVk *contextVk,
const uint8_t *data,
size_t size,
size_t offset)
{
if (mBuffer->isHostVisible())
{
ANGLE_TRY(directUpdate(contextVk, data, size, offset));
}
else
{
ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
}
return angle::Result::Continue;
}
angle::Result BufferVk::directUpdate(ContextVk *contextVk, angle::Result BufferVk::directUpdate(ContextVk *contextVk,
const uint8_t *data, const uint8_t *data,
size_t size, size_t size,
...@@ -677,7 +778,7 @@ angle::Result BufferVk::acquireAndUpdate(ContextVk *contextVk, ...@@ -677,7 +778,7 @@ angle::Result BufferVk::acquireAndUpdate(ContextVk *contextVk,
} }
ANGLE_TRY(acquireBufferHelper(contextVk, bufferSize, &mBuffer)); ANGLE_TRY(acquireBufferHelper(contextVk, bufferSize, &mBuffer));
ANGLE_TRY(directUpdate(contextVk, data, updateSize, offset)); ANGLE_TRY(updateBuffer(contextVk, data, updateSize, offset));
constexpr int kMaxCopyRegions = 2; constexpr int kMaxCopyRegions = 2;
angle::FixedVector<VkBufferCopy, kMaxCopyRegions> copyRegions; angle::FixedVector<VkBufferCopy, kMaxCopyRegions> copyRegions;
...@@ -728,7 +829,7 @@ angle::Result BufferVk::setDataImpl(ContextVk *contextVk, ...@@ -728,7 +829,7 @@ angle::Result BufferVk::setDataImpl(ContextVk *contextVk,
} }
else else
{ {
ANGLE_TRY(directUpdate(contextVk, data, size, offset)); ANGLE_TRY(updateBuffer(contextVk, data, size, offset));
} }
// Update conversions // Update conversions
......
...@@ -136,6 +136,7 @@ class BufferVk : public BufferImpl ...@@ -136,6 +136,7 @@ class BufferVk : public BufferImpl
angle::Result initializeShadowBuffer(ContextVk *contextVk, angle::Result initializeShadowBuffer(ContextVk *contextVk,
gl::BufferBinding target, gl::BufferBinding target,
size_t size); size_t size);
void initializeHostVisibleBufferPool(ContextVk *contextVk);
ANGLE_INLINE uint8_t *getShadowBuffer(size_t offset) ANGLE_INLINE uint8_t *getShadowBuffer(size_t offset)
{ {
...@@ -148,6 +149,10 @@ class BufferVk : public BufferImpl ...@@ -148,6 +149,10 @@ class BufferVk : public BufferImpl
} }
void updateShadowBuffer(const uint8_t *data, size_t size, size_t offset); void updateShadowBuffer(const uint8_t *data, size_t size, size_t offset);
angle::Result updateBuffer(ContextVk *contextVk,
const uint8_t *data,
size_t size,
size_t offset);
angle::Result directUpdate(ContextVk *contextVk, angle::Result directUpdate(ContextVk *contextVk,
const uint8_t *data, const uint8_t *data,
size_t size, size_t size,
...@@ -166,6 +171,13 @@ class BufferVk : public BufferImpl ...@@ -166,6 +171,13 @@ class BufferVk : public BufferImpl
size_t size, size_t size,
VkMemoryPropertyFlags memoryPropertyFlags, VkMemoryPropertyFlags memoryPropertyFlags,
bool persistentMapRequired); bool persistentMapRequired);
angle::Result handleDeviceLocalBufferMap(ContextVk *contextVk,
VkDeviceSize offset,
VkDeviceSize size,
void **mapPtr);
angle::Result handleDeviceLocalBufferUnmap(ContextVk *contextVk,
VkDeviceSize offset,
VkDeviceSize size);
angle::Result setDataImpl(ContextVk *contextVk, angle::Result setDataImpl(ContextVk *contextVk,
const uint8_t *data, const uint8_t *data,
size_t size, size_t size,
...@@ -199,6 +211,10 @@ class BufferVk : public BufferImpl ...@@ -199,6 +211,10 @@ class BufferVk : public BufferImpl
// Pool of BufferHelpers for mBuffer to acquire from // Pool of BufferHelpers for mBuffer to acquire from
vk::DynamicBuffer mBufferPool; vk::DynamicBuffer mBufferPool;
// DynamicBuffer to aid map operations of buffers when they are not host visible.
vk::DynamicBuffer mHostVisibleBufferPool;
VkDeviceSize mHostVisibleBufferOffset;
// For GPU-read only buffers glMap* latency is reduced by maintaining a copy // For GPU-read only buffers glMap* latency is reduced by maintaining a copy
// of the buffer which is writeable only by the CPU. The contents are updated on all // of the buffer which is writeable only by the CPU. The contents are updated on all
// glData/glSubData/glCopy calls. With this, a glMap* call becomes a non-blocking // glData/glSubData/glCopy calls. With this, a glMap* call becomes a non-blocking
......
...@@ -616,7 +616,11 @@ TEST_P(BufferDataTestES3, MapBufferRangeUnsynchronizedBit) ...@@ -616,7 +616,11 @@ TEST_P(BufferDataTestES3, MapBufferRangeUnsynchronizedBit)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
for (size_t i = 0; i < numElements; ++i) for (size_t i = 0; i < numElements; ++i)
{ {
EXPECT_EQ(dstData[i], data[i]); // Allow for the possibility that data matches either "dstData" or "srcData"
if (dstData[i] != data[i])
{
EXPECT_EQ(srcData[i], data[i]);
}
} }
glUnmapBuffer(GL_COPY_WRITE_BUFFER); glUnmapBuffer(GL_COPY_WRITE_BUFFER);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
......
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