Commit f197ebac by Mohan Maiya Committed by Commit Bot

Vulkan: Add EXT_external_buffer support

Addition of buffer support for external memory Also adds new end2end tests for these usecases * SubData update * map/unmap buffer * dispatch compute with external buffer Bug: angleproject:5073 Test: ExternalBufferTestES31.*Vulkan Change-Id: Ib3cccaca77b76830effe49d3731782552e7424ec Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2525105 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 12804827
......@@ -408,5 +408,10 @@ EGLClientBuffer AHardwareBufferToClientBuffer(const AHardwareBuffer *hardwareBuf
kAHardwareBufferToANativeWindowBufferOffset);
}
AHardwareBuffer *ClientBufferToAHardwareBuffer(EGLClientBuffer clientBuffer)
{
return offsetPointer<AHardwareBuffer>(clientBuffer,
-kAHardwareBufferToANativeWindowBufferOffset);
}
} // namespace android
} // namespace angle
......@@ -30,6 +30,7 @@ constexpr std::array<GLenum, 3> kSupportedSizedInternalFormats = {GL_RGBA8, GL_R
ANativeWindowBuffer *ClientBufferToANativeWindowBuffer(EGLClientBuffer clientBuffer);
EGLClientBuffer AHardwareBufferToClientBuffer(const AHardwareBuffer *hardwareBuffer);
AHardwareBuffer *ClientBufferToAHardwareBuffer(EGLClientBuffer clientBuffer);
EGLClientBuffer CreateEGLClientBufferFromAHardwareBuffer(int width,
int height,
......
......@@ -34,8 +34,9 @@ BufferState::BufferState()
mBindingCount(0),
mTransformFeedbackIndexedBindingCount(0),
mTransformFeedbackGenericBindingCount(0),
mImmutable(false),
mStorageExtUsageFlags(0)
mImmutable(GL_FALSE),
mStorageExtUsageFlags(0),
mExternal(GL_FALSE)
{}
BufferState::~BufferState() {}
......@@ -70,6 +71,15 @@ const std::string &Buffer::getLabel() const
return mState.mLabel;
}
angle::Result Buffer::bufferStorageExternal(Context *context,
BufferBinding target,
GLsizeiptr size,
GLeglClientBufferEXT clientBuffer,
GLbitfield flags)
{
return bufferExternalDataImpl(context, target, clientBuffer, size, flags);
}
angle::Result Buffer::bufferStorage(Context *context,
BufferBinding target,
GLsizeiptr size,
......@@ -122,7 +132,7 @@ angle::Result Buffer::bufferDataImpl(Context *context,
dataForImpl = scratchBuffer->data();
}
if (mImpl->setDataWithUsageFlags(context, target, dataForImpl, size, usage, flags) ==
if (mImpl->setDataWithUsageFlags(context, target, nullptr, dataForImpl, size, usage, flags) ==
angle::Result::Stop)
{
// If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
......@@ -147,6 +157,51 @@ angle::Result Buffer::bufferDataImpl(Context *context,
return angle::Result::Continue;
}
angle::Result Buffer::bufferExternalDataImpl(Context *context,
BufferBinding target,
GLeglClientBufferEXT clientBuffer,
GLsizeiptr size,
GLbitfield flags)
{
if (mState.isMapped())
{
// Per the OpenGL ES 3.0 spec, buffers are implicity unmapped when a call to
// BufferData happens on a mapped buffer:
//
// If any portion of the buffer object is mapped in the current context or any context
// current to another thread, it is as though UnmapBuffer (see section 2.10.3) is
// executed in each such context prior to deleting the existing data store.
//
GLboolean dontCare = GL_FALSE;
ANGLE_TRY(unmap(context, &dontCare));
}
if (mImpl->setDataWithUsageFlags(context, target, clientBuffer, nullptr, size,
BufferUsage::InvalidEnum, flags) == angle::Result::Stop)
{
// If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
mIndexRangeCache.clear();
mState.mSize = 0;
// Notify when storage changes.
onStateChange(angle::SubjectMessage::SubjectChanged);
return angle::Result::Stop;
}
mIndexRangeCache.clear();
mState.mUsage = BufferUsage::InvalidEnum;
mState.mSize = size;
mState.mImmutable = GL_TRUE;
mState.mStorageExtUsageFlags = flags;
mState.mExternal = GL_TRUE;
// Notify when storage changes.
onStateChange(angle::SubjectMessage::SubjectChanged);
return angle::Result::Continue;
}
angle::Result Buffer::bufferSubData(const Context *context,
BufferBinding target,
const void *data,
......
......@@ -66,6 +66,7 @@ class BufferState final : angle::NonCopyable
int mTransformFeedbackGenericBindingCount;
GLboolean mImmutable;
GLbitfield mStorageExtUsageFlags;
GLboolean mExternal;
};
class Buffer final : public RefCountObject<BufferID>,
......@@ -81,6 +82,11 @@ class Buffer final : public RefCountObject<BufferID>,
void setLabel(const Context *context, const std::string &label) override;
const std::string &getLabel() const override;
angle::Result bufferStorageExternal(Context *context,
BufferBinding target,
GLsizeiptr size,
GLeglClientBufferEXT clientBuffer,
GLbitfield flags);
angle::Result bufferStorage(Context *context,
BufferBinding target,
GLsizeiptr size,
......@@ -91,12 +97,6 @@ class Buffer final : public RefCountObject<BufferID>,
const void *data,
GLsizeiptr size,
BufferUsage usage);
angle::Result bufferDataImpl(Context *context,
BufferBinding target,
const void *data,
GLsizeiptr size,
BufferUsage usage,
GLbitfield flags);
angle::Result bufferSubData(const Context *context,
BufferBinding target,
const void *data,
......@@ -166,6 +166,18 @@ class Buffer final : public RefCountObject<BufferID>,
void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override;
private:
angle::Result bufferDataImpl(Context *context,
BufferBinding target,
const void *data,
GLsizeiptr size,
BufferUsage usage,
GLbitfield flags);
angle::Result bufferExternalDataImpl(Context *context,
BufferBinding target,
GLeglClientBufferEXT clientBuffer,
GLsizeiptr size,
GLbitfield flags);
BufferState mState;
rx::BufferImpl *mImpl;
angle::ObserverBinding mImplObserver;
......
......@@ -5611,13 +5611,16 @@ void Context::bufferStorage(BufferBinding target,
ANGLE_CONTEXT_TRY(buffer->bufferStorage(this, target, size, data, flags));
}
void Context::bufferStorageExternal(BufferBinding targetPacked,
void Context::bufferStorageExternal(BufferBinding target,
GLintptr offset,
GLsizeiptr size,
GLeglClientBufferEXT clientBuffer,
GLbitfield flags)
{
UNIMPLEMENTED();
Buffer *buffer = mState.getTargetBuffer(target);
ASSERT(buffer);
ANGLE_CONTEXT_TRY(buffer->bufferStorageExternal(this, target, size, clientBuffer, flags));
}
void Context::namedBufferStorageExternal(GLuint buffer,
......
......@@ -64,6 +64,7 @@ MSG kBufferNotUpdatable = "Buffer is not updatable.";
MSG kBufferOffsetOverflow = "Buffer offset overflow.";
MSG kBufferPointerNotAvailable = "Can not get pointer for reserved buffer name zero.";
MSG kCannotPopDefaultDebugGroup = "Cannot pop the default debug group.";
MSG kClientBufferInvalid = "Size must not exceed the size of clientbuffer";
MSG kClientDataInVertexArray = "Client data cannot be used with a non-default vertex array object.";
MSG kColorNumberGreaterThanMaxDrawBuffers = "Color number for primary color greater than or equal to MAX_DRAW_BUFFERS";
MSG kColorNumberGreaterThanMaxDualSourceDrawBuffers = "Color number for secondary color greater than or equal to MAX_DUAL_SOURCE_DRAW_BUFFERS";
......@@ -132,6 +133,7 @@ MSG kExpectedShaderName = "Expected a shader name, but found a program name.";
MSG kExtensionNotEnabled = "Extension is not enabled.";
MSG kExtensionNotDisablable = "Extension is not disablable.";
MSG kExtensionNotRequestable = "Extension is not requestable.";
MSG kExternalBufferInvalidOffset = "Offset must be zero for external buffers";
MSG kExternalTextureAttachmentNotYUV = "External texture attached to framebuffer is not YUV.";
MSG kExternalTextureNotSupported = "External texture extension not enabled";
MSG kFeedbackLoop = "Feedback loop formed between Framebuffer and active Texture.";
......@@ -400,6 +402,7 @@ MSG kNoActiveGraphicsShaderStage = "It is a undefined behaviour to render withou
MSG kNoActiveProgramWithComputeShader = "No active program for the compute shader stage.";
MSG kNoDefinedClearConversion = "No defined conversion between clear value and attachment format.";
MSG kNonPositiveDrawTextureDimension = "Both width and height argument of drawn texture must be positive.";
MSG kNonPositiveSize = "Size must be greater than 0";
MSG kNoProgramBinaryFormats = "No program binary formats supported.";
MSG kNoReadFramebuffer = "No active read framebuffer.";
MSG kNoSampleAlphaToCoveragesLimitation = "Current renderer doesn't support alpha-to-coverage.";
......
......@@ -22,6 +22,7 @@ angle::Result BufferImpl::getSubData(const gl::Context *context,
angle::Result BufferImpl::setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
const void *data,
size_t size,
gl::BufferUsage usage,
......
......@@ -37,6 +37,7 @@ class BufferImpl : public angle::Subject
virtual angle::Result setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
const void *data,
size_t size,
gl::BufferUsage usage,
......
......@@ -32,6 +32,7 @@ BufferNULL::~BufferNULL()
angle::Result BufferNULL::setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
const void *data,
size_t size,
gl::BufferUsage usage,
......
......@@ -25,6 +25,7 @@ class BufferNULL : public BufferImpl
angle::Result setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
const void *data,
size_t size,
gl::BufferUsage usage,
......
......@@ -84,6 +84,8 @@ _vulkan_backend_sources = [
"UtilsVk.h",
"VertexArrayVk.cpp",
"VertexArrayVk.h",
"android/vk_android_utils.cpp",
"android/vk_android_utils.h",
"vk_cache_utils.cpp",
"vk_cache_utils.h",
"vk_caps_utils.cpp",
......
......@@ -78,7 +78,8 @@ ANGLE_INLINE VkMemoryPropertyFlags GetPreferredMemoryType(gl::BufferBinding targ
}
}
ANGLE_INLINE VkMemoryPropertyFlags GetStorageMemoryType(GLbitfield storageFlags)
ANGLE_INLINE VkMemoryPropertyFlags GetStorageMemoryType(GLbitfield storageFlags,
bool externalBuffer)
{
constexpr VkMemoryPropertyFlags kDeviceLocalHostVisibleFlags =
(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
......@@ -87,7 +88,7 @@ ANGLE_INLINE VkMemoryPropertyFlags GetStorageMemoryType(GLbitfield storageFlags)
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (((storageFlags & GL_MAP_COHERENT_BIT_EXT) != 0) ||
((storageFlags & GL_MAP_PERSISTENT_BIT_EXT) != 0))
((storageFlags & GL_MAP_PERSISTENT_BIT_EXT) != 0) || externalBuffer)
{
return kDeviceLocalHostCoherentFlags;
}
......@@ -153,6 +154,12 @@ void BufferVk::destroy(const gl::Context *context)
void BufferVk::release(ContextVk *contextVk)
{
RendererVk *renderer = contextVk->getRenderer();
// For external buffers, mBuffer is not a reference to a chunk in mBufferPool.
// It was allocated explicitly and needs to be deallocated during release(...)
if (mBuffer && mBuffer->isExternalBuffer())
{
mBuffer->release(renderer);
}
mShadowBuffer.release();
mBufferPool.release(renderer);
mBuffer = nullptr;
......@@ -196,8 +203,52 @@ void BufferVk::updateShadowBuffer(const uint8_t *data, size_t size, size_t offse
}
}
angle::Result BufferVk::setExternalBufferData(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
size_t size,
VkMemoryPropertyFlags memoryPropertyFlags)
{
ContextVk *contextVk = vk::GetImpl(context);
// Release and re-create the memory and buffer.
release(contextVk);
// We could potentially use multiple backing buffers for different usages.
// For now keep a single buffer with all relevant usage flags.
VkImageUsageFlags usageFlags =
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
{
usageFlags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
}
std::unique_ptr<vk::BufferHelper> buffer = std::make_unique<vk::BufferHelper>();
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.flags = 0;
createInfo.size = size;
createInfo.usage = usageFlags;
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
ANGLE_TRY(buffer->initExternal(contextVk, memoryPropertyFlags, createInfo, clientBuffer));
ASSERT(!mBuffer);
mBuffer = buffer.release();
return angle::Result::Continue;
}
angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
const void *data,
size_t size,
gl::BufferUsage usage,
......@@ -205,13 +256,14 @@ angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context,
{
VkMemoryPropertyFlags memoryPropertyFlags = 0;
bool persistentMapRequired = false;
const bool isExternalBuffer = clientBuffer != nullptr;
switch (usage)
{
case gl::BufferUsage::InvalidEnum:
{
// glBufferStorage API call
memoryPropertyFlags = GetStorageMemoryType(flags);
memoryPropertyFlags = GetStorageMemoryType(flags, isExternalBuffer);
persistentMapRequired = (flags & GL_MAP_PERSISTENT_BIT_EXT) != 0;
break;
}
......@@ -223,6 +275,10 @@ angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context,
}
}
if (isExternalBuffer)
{
return setExternalBufferData(context, target, clientBuffer, size, memoryPropertyFlags);
}
return setDataWithMemoryType(context, target, data, size, memoryPropertyFlags,
persistentMapRequired);
}
......@@ -715,6 +771,9 @@ angle::Result BufferVk::acquireBufferHelper(ContextVk *contextVk,
size_t sizeInBytes,
vk::BufferHelper **bufferHelperOut)
{
// This method should not be called if it is an ExternalBuffer
ASSERT(mBuffer == nullptr || mBuffer->isExternalBuffer() == false);
bool needToReleasePreviousBuffers = false;
size_t size = roundUpPow2(sizeInBytes, kBufferSizeGranularity);
......
......@@ -48,8 +48,14 @@ class BufferVk : public BufferImpl
~BufferVk() override;
void destroy(const gl::Context *context) override;
angle::Result setExternalBufferData(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
size_t size,
VkMemoryPropertyFlags memoryPropertyFlags);
angle::Result setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
GLeglClientBufferEXT clientBuffer,
const void *data,
size_t size,
gl::BufferUsage usage,
......
//
// Copyright 2020 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.
//
// vk_android_utils.cpp: Vulkan utilities for using the Android platform
#include "libANGLE/renderer/vulkan/android/vk_android_utils.h"
#include "common/android_util.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
#if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 26
# define ANGLE_AHARDWARE_BUFFER_SUPPORT
// NDK header file for access to Android Hardware Buffers
# include <android/hardware_buffer.h>
#endif
namespace rx
{
namespace vk
{
angle::Result GetClientBufferMemoryRequirements(ContextVk *contextVk,
const AHardwareBuffer *hardwareBuffer,
VkMemoryRequirements &memRequirements)
{
#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
// Get Android Buffer Properties
VkAndroidHardwareBufferFormatPropertiesANDROID bufferFormatProperties = {};
bufferFormatProperties.sType =
VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
VkAndroidHardwareBufferPropertiesANDROID bufferProperties = {};
bufferProperties.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
bufferProperties.pNext = &bufferFormatProperties;
VkDevice device = contextVk->getRenderer()->getDevice();
ANGLE_VK_TRY(contextVk, vkGetAndroidHardwareBufferPropertiesANDROID(device, hardwareBuffer,
&bufferProperties));
memRequirements.size = bufferProperties.allocationSize;
memRequirements.alignment = 0;
memRequirements.memoryTypeBits = bufferProperties.memoryTypeBits;
return angle::Result::Continue;
#else
ANGLE_VK_UNREACHABLE(contextVk);
return angle::Result::Stop;
#endif // ANGLE_AHARDWARE_BUFFER_SUPPORT
}
angle::Result InitAndroidExternalMemory(ContextVk *contextVk,
EGLClientBuffer clientBuffer,
VkMemoryPropertyFlags memoryProperties,
Buffer *buffer,
VkMemoryPropertyFlags *memoryPropertyFlagsOut,
DeviceMemory *deviceMemoryOut)
{
#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
struct AHardwareBuffer *hardwareBuffer =
angle::android::ClientBufferToAHardwareBuffer(clientBuffer);
VkMemoryRequirements externalMemoryRequirements = {};
ANGLE_TRY(
GetClientBufferMemoryRequirements(contextVk, hardwareBuffer, externalMemoryRequirements));
// Import Vulkan DeviceMemory from Android Hardware Buffer.
VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {};
importHardwareBufferInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
importHardwareBufferInfo.buffer = hardwareBuffer;
ANGLE_TRY(AllocateBufferMemoryWithRequirements(
contextVk, memoryProperties, externalMemoryRequirements, &importHardwareBufferInfo, buffer,
memoryPropertyFlagsOut, deviceMemoryOut));
AHardwareBuffer_acquire(hardwareBuffer);
return angle::Result::Continue;
#else
ANGLE_VK_UNREACHABLE(contextVk);
return angle::Result::Stop;
#endif // ANGLE_AHARDWARE_BUFFER_SUPPORT
}
void ReleaseAndroidExternalMemory(EGLClientBuffer clientBuffer)
{
#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
struct AHardwareBuffer *hardwareBuffer =
angle::android::ClientBufferToAHardwareBuffer(clientBuffer);
AHardwareBuffer_release(hardwareBuffer);
#endif // ANGLE_AHARDWARE_BUFFER_SUPPORT
}
} // namespace vk
} // namespace rx
//
// Copyright 2020 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.
//
// vk_android_utils.h: Vulkan utilities for using the Android platform
#ifndef LIBANGLE_RENDERER_VULKAN_ANDROID_VK_ANDROID_UTILS_H_
#define LIBANGLE_RENDERER_VULKAN_ANDROID_VK_ANDROID_UTILS_H_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "common/vulkan/vk_headers.h"
#include "libANGLE/Error.h"
class ContextVk;
class Buffer;
class DeviceMemory;
namespace rx
{
namespace vk
{
angle::Result InitAndroidExternalMemory(ContextVk *contextVk,
EGLClientBuffer clientBuffer,
VkMemoryPropertyFlags memoryProperties,
Buffer *buffer,
VkMemoryPropertyFlags *memoryPropertyFlagsOut,
DeviceMemory *deviceMemoryOut);
void ReleaseAndroidExternalMemory(EGLClientBuffer clientBuffer);
} // namespace vk
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_ANDROID_VK_ANDROID_UTILS_H_
......@@ -369,6 +369,10 @@ void RendererVk::ensureCapsInitialized() const
mNativeExtensions.shadowSamplersEXT = true;
// Enable EXT_external_buffer on Andoid. External buffers are implemented using Android hadware
// buffer (struct AHardwareBuffer).
mNativeExtensions.externalBufferEXT = IsAndroid() && (GetAndroidSDKVersion() >= 26);
// From the Vulkan specs:
// sampleRateShading specifies whether Sample Shading and multisample interpolation are
// supported. If this feature is not enabled, the sampleShadingEnable member of the
......
......@@ -7,6 +7,7 @@
// Helper utilitiy classes that manage Vulkan resources.
#include "libANGLE/renderer/vulkan/vk_helpers.h"
#include "libANGLE/renderer/driver_utils.h"
#include "common/utilities.h"
#include "image_util/loadimage.h"
......@@ -18,6 +19,7 @@
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/android/vk_android_utils.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
#include "libANGLE/trace.h"
......@@ -2912,7 +2914,6 @@ void PipelineBarrier::addDiagnosticsString(std::ostringstream &out) const
BufferHelper::BufferHelper()
: mMemoryPropertyFlags{},
mSize(0),
mMappedMemory(nullptr),
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mCurrentWriteAccess(0),
mCurrentReadAccess(0),
......@@ -2921,6 +2922,117 @@ BufferHelper::BufferHelper()
mSerial()
{}
BufferMemory::BufferMemory() : mClientBuffer(nullptr), mMappedMemory(nullptr) {}
BufferMemory::~BufferMemory() = default;
angle::Result BufferMemory::initExternal(GLeglClientBufferEXT clientBuffer)
{
ASSERT(clientBuffer != nullptr);
mClientBuffer = clientBuffer;
return angle::Result::Continue;
}
angle::Result BufferMemory::init()
{
ASSERT(mClientBuffer == nullptr);
return angle::Result::Continue;
}
void BufferMemory::unmap(RendererVk *renderer)
{
if (mMappedMemory != nullptr)
{
if (isExternalBuffer())
{
mExternalMemory.unmap(renderer->getDevice());
}
else
{
mAllocation.unmap(renderer->getAllocator());
}
mMappedMemory = nullptr;
}
}
void BufferMemory::destroy(RendererVk *renderer)
{
if (isExternalBuffer())
{
mExternalMemory.destroy(renderer->getDevice());
ReleaseAndroidExternalMemory(mClientBuffer);
}
else
{
mAllocation.destroy(renderer->getAllocator());
}
}
void BufferMemory::flush(RendererVk *renderer,
VkMemoryMapFlags memoryPropertyFlags,
VkDeviceSize offset,
VkDeviceSize size)
{
if (isExternalBuffer())
{
// if the memory type is not host coherent, we perform an explicit flush
if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
{
VkMappedMemoryRange mappedRange = {};
mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
mappedRange.memory = mExternalMemory.getHandle();
mappedRange.offset = offset;
mappedRange.size = size;
mExternalMemory.flush(renderer->getDevice(), mappedRange);
}
}
else
{
mAllocation.flush(renderer->getAllocator(), offset, size);
}
}
void BufferMemory::invalidate(RendererVk *renderer,
VkMemoryMapFlags memoryPropertyFlags,
VkDeviceSize offset,
VkDeviceSize size)
{
if (isExternalBuffer())
{
// if the memory type is not device coherent, we perform an explicit invalidate
if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD) == 0)
{
VkMappedMemoryRange memoryRanges = {};
memoryRanges.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
memoryRanges.memory = mExternalMemory.getHandle();
memoryRanges.offset = offset;
memoryRanges.size = size;
mExternalMemory.invalidate(renderer->getDevice(), memoryRanges);
}
}
else
{
mAllocation.invalidate(renderer->getAllocator(), offset, size);
}
}
angle::Result BufferMemory::mapImpl(ContextVk *contextVk, VkDeviceSize size)
{
if (isExternalBuffer())
{
ANGLE_VK_TRY(contextVk, mExternalMemory.map(contextVk->getRenderer()->getDevice(), 0, size,
0, &mMappedMemory));
}
else
{
ANGLE_VK_TRY(contextVk,
mAllocation.map(contextVk->getRenderer()->getAllocator(), &mMappedMemory));
}
return angle::Result::Continue;
}
BufferHelper::~BufferHelper() = default;
angle::Result BufferHelper::init(ContextVk *contextVk,
......@@ -2965,8 +3077,7 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
ANGLE_VK_TRY(contextVk, allocator.createBuffer(*createInfo, requiredFlags, preferredFlags,
persistentlyMapped, &memoryTypeIndex, &mBuffer,
&mAllocation));
mMemory.getMemoryObject()));
allocator.getMemoryTypeProperties(memoryTypeIndex, &mMemoryPropertyFlags);
mCurrentQueueFamilyIndex = renderer->getQueueFamilyIndex();
......@@ -2985,11 +3096,47 @@ angle::Result BufferHelper::init(ContextVk *contextVk,
// Can map the memory.
// Pick an arbitrary value to initialize non-zero memory for sanitization.
constexpr int kNonZeroInitValue = 55;
ANGLE_TRY(InitMappableAllocation(contextVk, allocator, &mAllocation, mSize,
ANGLE_TRY(InitMappableAllocation(contextVk, allocator, mMemory.getMemoryObject(), mSize,
kNonZeroInitValue, mMemoryPropertyFlags));
}
}
ANGLE_TRY(mMemory.init());
return angle::Result::Continue;
}
angle::Result BufferHelper::initExternal(ContextVk *contextVk,
VkMemoryPropertyFlags memoryProperties,
const VkBufferCreateInfo &requestedCreateInfo,
GLeglClientBufferEXT clientBuffer)
{
ASSERT(IsAndroid());
RendererVk *renderer = contextVk->getRenderer();
mSerial = renderer->getResourceSerialFactory().generateBufferSerial();
mSize = requestedCreateInfo.size;
VkBufferCreateInfo modifiedCreateInfo = requestedCreateInfo;
VkExternalMemoryBufferCreateInfo externCreateInfo = {};
externCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO;
externCreateInfo.handleTypes =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
externCreateInfo.pNext = nullptr;
modifiedCreateInfo.pNext = &externCreateInfo;
ANGLE_VK_TRY(contextVk, mBuffer.init(renderer->getDevice(), modifiedCreateInfo));
ANGLE_TRY(InitAndroidExternalMemory(contextVk, clientBuffer, memoryProperties, &mBuffer,
&mMemoryPropertyFlags, mMemory.getExternalMemoryObject()));
ANGLE_TRY(mMemory.initExternal(clientBuffer));
// Set local variables
mMemoryPropertyFlags = memoryProperties;
mCurrentQueueFamilyIndex = renderer->getQueueFamilyIndex();
return angle::Result::Continue;
}
......@@ -3031,7 +3178,7 @@ void BufferHelper::destroy(RendererVk *renderer)
mSize = 0;
mBuffer.destroy(device);
mAllocation.destroy(renderer->getAllocator());
mMemory.destroy(renderer);
}
void BufferHelper::release(RendererVk *renderer)
......@@ -3039,7 +3186,8 @@ void BufferHelper::release(RendererVk *renderer)
unmap(renderer);
mSize = 0;
renderer->collectGarbageAndReinit(&mUse, &mBuffer, &mAllocation);
renderer->collectGarbageAndReinit(&mUse, &mBuffer, mMemory.getExternalMemoryObject(),
mMemory.getMemoryObject());
}
angle::Result BufferHelper::copyFromBuffer(ContextVk *contextVk,
......@@ -3059,21 +3207,9 @@ angle::Result BufferHelper::copyFromBuffer(ContextVk *contextVk,
return angle::Result::Continue;
}
angle::Result BufferHelper::mapImpl(ContextVk *contextVk)
{
ANGLE_VK_TRY(contextVk,
mAllocation.map(contextVk->getRenderer()->getAllocator(), &mMappedMemory));
return angle::Result::Continue;
}
void BufferHelper::unmap(RendererVk *renderer)
{
if (mMappedMemory)
{
mAllocation.unmap(renderer->getAllocator());
mMappedMemory = nullptr;
}
mMemory.unmap(renderer);
}
angle::Result BufferHelper::flush(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size)
......@@ -3082,7 +3218,7 @@ angle::Result BufferHelper::flush(RendererVk *renderer, VkDeviceSize offset, VkD
bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (hostVisible && !hostCoherent)
{
mAllocation.flush(renderer->getAllocator(), offset, size);
mMemory.flush(renderer, mMemoryPropertyFlags, offset, size);
}
return angle::Result::Continue;
}
......@@ -3093,7 +3229,7 @@ angle::Result BufferHelper::invalidate(RendererVk *renderer, VkDeviceSize offset
bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (hostVisible && !hostCoherent)
{
mAllocation.invalidate(renderer->getAllocator(), offset, size);
mMemory.invalidate(renderer, mMemoryPropertyFlags, offset, size);
}
return angle::Result::Continue;
}
......
......@@ -734,6 +734,51 @@ using PipelineBarrierArray = angle::PackedEnumMap<PipelineStage, PipelineBarrier
class FramebufferHelper;
class BufferMemory : angle::NonCopyable
{
public:
BufferMemory();
~BufferMemory();
angle::Result initExternal(GLeglClientBufferEXT clientBuffer);
angle::Result init();
void destroy(RendererVk *renderer);
angle::Result map(ContextVk *contextVk, VkDeviceSize size, uint8_t **ptrOut)
{
if (mMappedMemory == nullptr)
{
ANGLE_TRY(mapImpl(contextVk, size));
}
*ptrOut = mMappedMemory;
return angle::Result::Continue;
}
void unmap(RendererVk *renderer);
void flush(RendererVk *renderer,
VkMemoryMapFlags memoryPropertyFlags,
VkDeviceSize offset,
VkDeviceSize size);
void invalidate(RendererVk *renderer,
VkMemoryMapFlags memoryPropertyFlags,
VkDeviceSize offset,
VkDeviceSize size);
bool isExternalBuffer() const { return mClientBuffer != nullptr; }
uint8_t *getMappedMemory() const { return mMappedMemory; }
DeviceMemory *getExternalMemoryObject() { return &mExternalMemory; }
Allocation *getMemoryObject() { return &mAllocation; }
private:
angle::Result mapImpl(ContextVk *contextVk, VkDeviceSize size);
Allocation mAllocation; // use mAllocation if isExternalBuffer() is false
DeviceMemory mExternalMemory; // use mExternalMemory if isExternalBuffer() is true
GLeglClientBufferEXT mClientBuffer;
uint8_t *mMappedMemory;
};
class BufferHelper final : public Resource
{
public:
......@@ -743,6 +788,10 @@ class BufferHelper final : public Resource
angle::Result init(ContextVk *contextVk,
const VkBufferCreateInfo &createInfo,
VkMemoryPropertyFlags memoryPropertyFlags);
angle::Result initExternal(ContextVk *contextVk,
VkMemoryPropertyFlags memoryProperties,
const VkBufferCreateInfo &requestedCreateInfo,
GLeglClientBufferEXT clientBuffer);
void destroy(RendererVk *renderer);
void release(RendererVk *renderer);
......@@ -754,7 +803,7 @@ class BufferHelper final : public Resource
uint8_t *getMappedMemory() const
{
ASSERT(isMapped());
return mMappedMemory;
return mMemory.getMappedMemory();
}
bool isHostVisible() const
{
......@@ -765,7 +814,8 @@ class BufferHelper final : public Resource
return (mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
}
bool isMapped() const { return mMappedMemory != nullptr; }
bool isMapped() const { return mMemory.getMappedMemory() != nullptr; }
bool isExternalBuffer() const { return mMemory.isExternalBuffer(); }
// Also implicitly sets up the correct barriers.
angle::Result copyFromBuffer(ContextVk *contextVk,
......@@ -775,18 +825,13 @@ class BufferHelper final : public Resource
angle::Result map(ContextVk *contextVk, uint8_t **ptrOut)
{
if (!mMappedMemory)
{
ANGLE_TRY(mapImpl(contextVk));
}
*ptrOut = mMappedMemory;
return angle::Result::Continue;
return mMemory.map(contextVk, mSize, ptrOut);
}
angle::Result mapWithOffset(ContextVk *contextVk, uint8_t **ptrOut, size_t offset)
{
uint8_t *mapBufPointer;
ANGLE_TRY(map(contextVk, &mapBufPointer));
ANGLE_TRY(mMemory.map(contextVk, mSize, &mapBufPointer));
*ptrOut = mapBufPointer + offset;
return angle::Result::Continue;
}
......@@ -825,17 +870,15 @@ class BufferHelper final : public Resource
PipelineBarrier *barrier);
private:
angle::Result mapImpl(ContextVk *contextVk);
angle::Result initializeNonZeroMemory(Context *context, VkDeviceSize size);
// Vulkan objects.
Buffer mBuffer;
Allocation mAllocation;
BufferMemory mMemory;
// Cached properties.
VkMemoryPropertyFlags mMemoryPropertyFlags;
VkDeviceSize mSize;
uint8_t *mMappedMemory;
uint32_t mCurrentQueueFamilyIndex;
// For memory barriers.
......
......@@ -562,6 +562,19 @@ angle::Result AllocateImageMemoryWithRequirements(vk::Context *context,
deviceMemoryOut);
}
angle::Result AllocateBufferMemoryWithRequirements(vk::Context *context,
VkMemoryPropertyFlags memoryPropertyFlags,
const VkMemoryRequirements &memoryRequirements,
const void *extraAllocationInfo,
Buffer *buffer,
VkMemoryPropertyFlags *memoryPropertyFlagsOut,
DeviceMemory *deviceMemoryOut)
{
return AllocateAndBindBufferOrImageMemory(context, memoryPropertyFlags, memoryPropertyFlagsOut,
memoryRequirements, extraAllocationInfo, buffer,
deviceMemoryOut);
}
angle::Result InitShaderAndSerial(Context *context,
ShaderAndSerial *shaderAndSerial,
const uint32_t *shaderCode,
......
......@@ -407,6 +407,14 @@ angle::Result AllocateImageMemoryWithRequirements(Context *context,
Image *image,
DeviceMemory *deviceMemoryOut);
angle::Result AllocateBufferMemoryWithRequirements(vk::Context *context,
VkMemoryPropertyFlags memoryPropertyFlags,
const VkMemoryRequirements &memoryRequirements,
const void *extraAllocationInfo,
Buffer *buffer,
VkMemoryPropertyFlags *memoryPropertyFlagsOut,
DeviceMemory *deviceMemoryOut);
using ShaderAndSerial = ObjectAndSerial<ShaderModule>;
angle::Result InitShaderAndSerial(Context *context,
......
......@@ -461,6 +461,8 @@ class DeviceMemory final : public WrappedObject<DeviceMemory, VkDeviceMemory>
VkMemoryMapFlags flags,
uint8_t **mapPointer) const;
void unmap(VkDevice device) const;
void flush(VkDevice device, VkMappedMemoryRange &memRange);
void invalidate(VkDevice device, VkMappedMemoryRange &memRange);
};
class Allocator : public WrappedObject<Allocator, VmaAllocator>
......@@ -1403,6 +1405,16 @@ ANGLE_INLINE void DeviceMemory::unmap(VkDevice device) const
vkUnmapMemory(device, mHandle);
}
ANGLE_INLINE void DeviceMemory::flush(VkDevice device, VkMappedMemoryRange &memRange)
{
vkFlushMappedMemoryRanges(device, 1, &memRange);
}
ANGLE_INLINE void DeviceMemory::invalidate(VkDevice device, VkMappedMemoryRange &memRange)
{
vkInvalidateMappedMemoryRanges(device, 1, &memRange);
}
// Allocator implementation.
ANGLE_INLINE void Allocator::destroy()
{
......
......@@ -1121,9 +1121,9 @@ bool ValidateBufferStorageEXT(const Context *context,
return false;
}
if (size < 0)
if (size <= 0)
{
context->validationError(GL_INVALID_VALUE, kNegativeSize);
context->validationError(GL_INVALID_VALUE, kNonPositiveSize);
return false;
}
......@@ -1174,8 +1174,24 @@ bool ValidateBufferStorageExternalEXT(const Context *context,
GLeglClientBufferEXT clientBuffer,
GLbitfield flags)
{
UNIMPLEMENTED();
return false;
if (!ValidateBufferStorageEXT(context, targetPacked, size, nullptr, flags))
{
return false;
}
if (offset != 0)
{
context->validationError(GL_INVALID_VALUE, kExternalBufferInvalidOffset);
return false;
}
if (clientBuffer == nullptr && size > 0)
{
context->validationError(GL_INVALID_VALUE, kClientBufferInvalid);
return false;
}
return true;
}
bool ValidateNamedBufferStorageExternalEXT(const Context *context,
......
......@@ -62,6 +62,7 @@ angle_end2end_tests_sources = [
"gl_tests/DrawElementsTest.cpp",
"gl_tests/ETCTextureTest.cpp",
"gl_tests/ExplicitContextTest.cpp",
"gl_tests/ExternalBufferTest.cpp",
"gl_tests/ExternalWrapTest.cpp",
"gl_tests/FenceSyncTests.cpp",
"gl_tests/FloatingPointSurfaceTest.cpp",
......
......@@ -823,6 +823,19 @@ TEST_P(BufferDataTestES3, BufferDataUnmap)
class BufferStorageTestES3 : public BufferDataTest
{};
// Tests that proper error value is returned when bad size is passed in
TEST_P(BufferStorageTestES3, BufferStorageInvalidSize)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
std::vector<GLfloat> data(6, 1.0f);
GLBuffer buffer;
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferStorageEXT(GL_ARRAY_BUFFER, 0, data.data(), 0);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Verify that glBufferStorage makes a buffer immutable
TEST_P(BufferStorageTestES3, StorageBufferBufferData)
{
......
//
// Copyright 2020 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.
//
// ExternalBufferTest:
// Tests the correctness of external buffer ext extension.
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include "common/android_util.h"
#if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 26
# define ANGLE_AHARDWARE_BUFFER_SUPPORT
// NDK header file for access to Android Hardware Buffers
# include <android/hardware_buffer.h>
#endif
namespace angle
{
class ExternalBufferTestES31 : public ANGLETest
{
protected:
ExternalBufferTestES31()
{
setWindowWidth(16);
setWindowHeight(16);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
}
AHardwareBuffer *createAndroidHardwareBuffer(size_t size, const GLubyte *data)
{
#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
// The height and width are number of pixels of size format
AHardwareBuffer_Desc aHardwareBufferDescription = {};
aHardwareBufferDescription.width = size;
aHardwareBufferDescription.height = 1;
aHardwareBufferDescription.layers = 1;
aHardwareBufferDescription.format = AHARDWAREBUFFER_FORMAT_BLOB;
aHardwareBufferDescription.usage =
AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
aHardwareBufferDescription.stride = 0;
// Allocate memory from Android Hardware Buffer
AHardwareBuffer *aHardwareBuffer = nullptr;
EXPECT_EQ(0, AHardwareBuffer_allocate(&aHardwareBufferDescription, &aHardwareBuffer));
void *mappedMemory = nullptr;
EXPECT_EQ(0, AHardwareBuffer_lock(aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
-1, nullptr, &mappedMemory));
// Need to grab the stride the implementation might have enforced
AHardwareBuffer_describe(aHardwareBuffer, &aHardwareBufferDescription);
memcpy(mappedMemory, data, size);
EXPECT_EQ(0, AHardwareBuffer_unlock(aHardwareBuffer, nullptr));
return aHardwareBuffer;
#else
return nullptr;
#endif // ANGLE_PLATFORM_ANDROID
}
void destroyAndroidHardwareBuffer(AHardwareBuffer *aHardwareBuffer)
{
#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
AHardwareBuffer_release(aHardwareBuffer);
#endif
}
void *lockAndroidHardwareBuffer(AHardwareBuffer *aHardwareBuffer)
{
void *data = nullptr;
#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
EXPECT_EQ(0, AHardwareBuffer_lock(aHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
-1, nullptr, &data));
#endif
return data;
}
void unlockAndroidHardwareBuffer(AHardwareBuffer *aHardwareBuffer)
{
#if defined(ANGLE_AHARDWARE_BUFFER_SUPPORT)
AHardwareBuffer_unlock(aHardwareBuffer, nullptr);
#endif
}
};
// Testing subdata update with external buffer from AHB
TEST_P(ExternalBufferTestES31, BufferSubData)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_external_buffer") ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
constexpr uint8_t kBufferSize = 16;
std::vector<GLubyte> initData(kBufferSize, 0xA);
// Create the Image
AHardwareBuffer *aHardwareBuffer;
constexpr GLbitfield kFlags = GL_DYNAMIC_STORAGE_BIT_EXT;
aHardwareBuffer = createAndroidHardwareBuffer(kBufferSize, initData.data());
GLBuffer buffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
glBufferStorageExternalEXT(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize,
eglGetNativeClientBufferANDROID(aHardwareBuffer), kFlags);
ASSERT_GL_NO_ERROR();
std::vector<GLubyte> expectedData(kBufferSize, 0xFF);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, expectedData.data());
glFinish();
ASSERT_GL_NO_ERROR();
// Inspect the data written into the buffer using CPU access.
uint8_t *data = static_cast<uint8_t *>(lockAndroidHardwareBuffer(aHardwareBuffer));
for (uint32_t i = 0; i < kBufferSize; ++i)
{
EXPECT_EQ(data[i], 0xFF);
}
unlockAndroidHardwareBuffer(aHardwareBuffer);
// Delete the source AHB when in use
destroyAndroidHardwareBuffer(aHardwareBuffer);
}
// Testing dispatch compute shader external from source AHB
TEST_P(ExternalBufferTestES31, DispatchCompute)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_external_buffer") ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
constexpr char kCS[] = R"(#version 310 es
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(std430, binding=0) buffer Output {
uint data[];
} bOutput;
void main() {
bOutput.data[gl_GlobalInvocationID.x] =
gl_GlobalInvocationID.x * 3u;
}
)";
constexpr uint8_t kBufferSize = 16 * 4;
std::vector<GLubyte> initData(kBufferSize, 0xA);
// Create the Image
AHardwareBuffer *aHardwareBuffer;
constexpr GLbitfield kFlags = GL_MAP_READ_BIT;
aHardwareBuffer = createAndroidHardwareBuffer(kBufferSize, initData.data());
GLBuffer buffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
glBufferStorageExternalEXT(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize,
eglGetNativeClientBufferANDROID(aHardwareBuffer), kFlags);
ASSERT_GL_NO_ERROR();
GLProgram program;
program.makeCompute(kCS);
ASSERT_NE(program.get(), 0U);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
glDispatchCompute(kBufferSize, 1, 1);
glFinish();
ASSERT_GL_NO_ERROR();
// Inspect the data written into the buffer using CPU access.
uint32_t *data = static_cast<uint32_t *>(lockAndroidHardwareBuffer(aHardwareBuffer));
for (uint32_t i = 0; i < (kBufferSize / sizeof(uint32_t)); ++i)
{
EXPECT_EQ(data[i], static_cast<uint32_t>(i * 3));
}
unlockAndroidHardwareBuffer(aHardwareBuffer);
// Delete the source AHB when in use
destroyAndroidHardwareBuffer(aHardwareBuffer);
}
// Test interaction between GL_OES_mapbuffer and GL_EXT_external_buffer extensions.
TEST_P(ExternalBufferTestES31, MapBuffer)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_external_buffer") ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage") ||
!IsGLExtensionEnabled("GL_EXT_map_buffer_range"));
constexpr uint8_t kBufferSize = 16;
std::vector<GLubyte> initData(kBufferSize, 0xFF);
// Create the Image
AHardwareBuffer *aHardwareBuffer;
constexpr GLbitfield kFlags = (GL_MAP_READ_BIT_EXT | GL_MAP_WRITE_BIT_EXT);
aHardwareBuffer = createAndroidHardwareBuffer(kBufferSize, initData.data());
GLBuffer buffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
glBufferStorageExternalEXT(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize,
eglGetNativeClientBufferANDROID(aHardwareBuffer), kFlags);
ASSERT_GL_NO_ERROR();
// Inspect the data written into the buffer using CPU access.
uint8_t *data = static_cast<uint8_t *>(
glMapBufferRangeEXT(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT_EXT));
ASSERT_GL_NO_ERROR();
for (uint32_t i = 0; i < kBufferSize; ++i)
{
EXPECT_EQ(data[i], 0xFF);
}
glUnmapBufferOES(GL_SHADER_STORAGE_BUFFER);
// Delete the source AHB when in use
destroyAndroidHardwareBuffer(aHardwareBuffer);
}
ANGLE_INSTANTIATE_TEST_ES31(ExternalBufferTestES31);
} // namespace angle
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