Commit a2d8bbb5 by Mohan Maiya Committed by Commit Bot

Vulkan: Add GL_EXT_buffer_storage extension support

Addition of support for immutable storage to buffer objects. Also adds new end2end tests for these usecases * Basic BufferStorage * SubData update * map/unmap buffer Bug: angleproject:5056 Tests: angle_end2end_tests --gtest_filter=BufferStorageTestES3*Vulkan Change-Id: Iba74b372ad033711927b63c6a04cec0eeb4db699 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2419952Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
parent 68dcea7b
......@@ -33,7 +33,9 @@ BufferState::BufferState()
mMapLength(0),
mBindingCount(0),
mTransformFeedbackIndexedBindingCount(0),
mTransformFeedbackGenericBindingCount(0)
mTransformFeedbackGenericBindingCount(0),
mImmutable(false),
mStorageExtUsageFlags(0)
{}
BufferState::~BufferState() {}
......@@ -68,12 +70,32 @@ const std::string &Buffer::getLabel() const
return mState.mLabel;
}
angle::Result Buffer::bufferStorage(Context *context,
BufferBinding target,
GLsizeiptr size,
const void *data,
GLbitfield flags)
{
return bufferDataImpl(context, target, data, size, BufferUsage::InvalidEnum, flags);
}
angle::Result Buffer::bufferData(Context *context,
BufferBinding target,
const void *data,
GLsizeiptr size,
BufferUsage usage)
{
GLbitfield flags = (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_DYNAMIC_STORAGE_BIT_EXT);
return bufferDataImpl(context, target, data, size, usage, flags);
}
angle::Result Buffer::bufferDataImpl(Context *context,
BufferBinding target,
const void *data,
GLsizeiptr size,
BufferUsage usage,
GLbitfield flags)
{
const void *dataForImpl = data;
if (mState.isMapped())
......@@ -100,7 +122,8 @@ angle::Result Buffer::bufferData(Context *context,
dataForImpl = scratchBuffer->data();
}
if (mImpl->setData(context, target, dataForImpl, size, usage) == angle::Result::Stop)
if (mImpl->setDataWithUsageFlags(context, target, dataForImpl, size, usage, flags) ==
angle::Result::Stop)
{
// If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
mIndexRangeCache.clear();
......@@ -115,6 +138,8 @@ angle::Result Buffer::bufferData(Context *context,
mIndexRangeCache.clear();
mState.mUsage = usage;
mState.mSize = size;
mState.mImmutable = (usage == BufferUsage::InvalidEnum);
mState.mStorageExtUsageFlags = flags;
// Notify when storage changes.
onStateChange(angle::SubjectMessage::SubjectChanged);
......
......@@ -63,6 +63,8 @@ class BufferState final : angle::NonCopyable
int mBindingCount;
int mTransformFeedbackIndexedBindingCount;
int mTransformFeedbackGenericBindingCount;
GLboolean mImmutable;
GLbitfield mStorageExtUsageFlags;
};
class Buffer final : public RefCountObject<BufferID>,
......@@ -78,11 +80,22 @@ class Buffer final : public RefCountObject<BufferID>,
void setLabel(const Context *context, const std::string &label) override;
const std::string &getLabel() const override;
angle::Result bufferStorage(Context *context,
BufferBinding target,
GLsizeiptr size,
const void *data,
GLbitfield flags);
angle::Result bufferData(Context *context,
BufferBinding target,
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,
......@@ -119,6 +132,8 @@ class Buffer final : public RefCountObject<BufferID>,
GLint64 getMapLength() const { return mState.mMapLength; }
GLint64 getSize() const { return mState.mSize; }
GLint64 getMemorySize() const;
GLboolean isImmutable() const { return mState.mImmutable; }
GLbitfield getStorageExtUsageFlags() const { return mState.mStorageExtUsageFlags; }
rx::BufferImpl *getImplementation() const { return mImpl; }
......
......@@ -1019,6 +1019,7 @@ const ExtensionInfoMap &GetExtensionInfoMap()
map["GL_EXT_gpu_shader5"] = enableableExtension(&Extensions::gpuShader5EXT);
map["GL_APPLE_clip_distance"] = enableableExtension(&Extensions::clipDistanceAPPLE);
map["GL_EXT_EGL_image_array"] = enableableExtension(&Extensions::eglImageArray);
map["GL_EXT_buffer_storage"] = enableableExtension(&Extensions::bufferStorageEXT);
// GLES1 extensions
map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArrayOES);
map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMapOES);
......
......@@ -5437,7 +5437,9 @@ void Context::bufferStorage(BufferBinding target,
const void *data,
GLbitfield flags)
{
UNIMPLEMENTED();
Buffer *buffer = mState.getTargetBuffer(target);
ASSERT(buffer);
ANGLE_CONTEXT_TRY(buffer->bufferStorage(this, target, size, data, flags));
}
void Context::bufferData(BufferBinding target, GLsizeiptr size, const void *data, BufferUsage usage)
......
......@@ -54,10 +54,12 @@ MSG kBlitTypeMismatchSignedInteger = "If the read buffer contains signed integer
MSG kBlitTypeMismatchUnsignedInteger = "If the read buffer contains unsigned integer values the draw buffer must as well.";
MSG kBufferAlreadyMapped = "Buffer is already mapped.";
MSG kBufferBoundForTransformFeedback = "Buffer is bound for transform feedback.";
MSG kBufferImmutable = "Buffer is immutable.";
MSG kBufferMapped = "An active buffer is mapped";
MSG kBufferNotBound = "A buffer must be bound.";
MSG kBufferNotMappable = "Attempted to map buffer object zero.";
MSG kBufferNotMapped = "Buffer is not mapped.";
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.";
......@@ -181,6 +183,7 @@ MSG kInvalidBorder = "Border must be 0.";
MSG kInvalidBufferName = "name is not a valid buffer.";
MSG kInvalidBufferTypes = "Invalid buffer target.";
MSG kInvalidBufferUsage = "Invalid buffer usage enum.";
MSG kInvalidBufferUsageFlags = "Invalid buffer usage flags.";
MSG kInvalidClearMask = "Invalid mask bits.";
MSG kInvalidClientState = "Invalid client vertex array type.";
MSG kInvalidClipPlane = "Invalid clip plane.";
......
......@@ -640,6 +640,12 @@ void QueryBufferParameterBase(const Buffer *buffer, GLenum pname, ParamType *par
case GL_MEMORY_SIZE_ANGLE:
*params = CastFromStateValue<ParamType>(pname, buffer->getMemorySize());
break;
case GL_BUFFER_IMMUTABLE_STORAGE_EXT:
*params = CastFromStateValue<ParamType>(pname, buffer->isImmutable());
break;
case GL_BUFFER_STORAGE_FLAGS_EXT:
*params = CastFromGLintStateValue<ParamType>(pname, buffer->getStorageExtUsageFlags());
break;
default:
UNREACHABLE();
break;
......
......@@ -20,4 +20,14 @@ angle::Result BufferImpl::getSubData(const gl::Context *context,
return angle::Result::Stop;
}
angle::Result BufferImpl::setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
gl::BufferUsage usage,
GLbitfield flags)
{
return setData(context, target, data, size, usage);
}
} // namespace rx
......@@ -35,6 +35,12 @@ class BufferImpl : public angle::Subject
~BufferImpl() override {}
virtual void destroy(const gl::Context *context) {}
virtual angle::Result setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
gl::BufferUsage usage,
GLbitfield flags);
virtual angle::Result setData(const gl::Context *context,
gl::BufferBinding target,
const void *data,
......
......@@ -30,6 +30,24 @@ BufferNULL::~BufferNULL()
ASSERT(memoryReleaseResult);
}
angle::Result BufferNULL::setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
gl::BufferUsage usage,
GLbitfield flags)
{
ANGLE_CHECK_GL_ALLOC(GetImplAs<ContextNULL>(context),
mAllocationTracker->updateMemoryAllocation(mData.size(), size));
mData.resize(size, 0);
if (size > 0 && data != nullptr)
{
memcpy(mData.data(), data, size);
}
return angle::Result::Continue;
}
angle::Result BufferNULL::setData(const gl::Context *context,
gl::BufferBinding target,
const void *data,
......
......@@ -23,6 +23,12 @@ class BufferNULL : public BufferImpl
BufferNULL(const gl::BufferState &state, AllocationTrackerNULL *allocationTracker);
~BufferNULL() override;
angle::Result setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
gl::BufferUsage usage,
GLbitfield flags) override;
angle::Result setData(const gl::Context *context,
gl::BufferBinding target,
const void *data,
......
......@@ -78,6 +78,23 @@ ANGLE_INLINE VkMemoryPropertyFlags GetPreferredMemoryType(gl::BufferBinding targ
}
}
ANGLE_INLINE VkMemoryPropertyFlags GetStorageMemoryType(GLbitfield storageFlags)
{
constexpr VkMemoryPropertyFlags kDeviceLocalHostVisibleFlags =
(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
constexpr VkMemoryPropertyFlags kDeviceLocalHostCoherentFlags =
(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (((storageFlags & GL_MAP_COHERENT_BIT_EXT) != 0) ||
((storageFlags & GL_MAP_PERSISTENT_BIT_EXT) != 0))
{
return kDeviceLocalHostCoherentFlags;
}
return kDeviceLocalHostVisibleFlags;
}
ANGLE_INLINE bool SubDataSizeMeetsThreshold(size_t subDataSize, size_t bufferSize)
{
// A sub data update with size > 50% of buffer size meets the threshold
......@@ -179,12 +196,55 @@ void BufferVk::updateShadowBuffer(const uint8_t *data, size_t size, size_t offse
}
}
angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
gl::BufferUsage usage,
GLbitfield flags)
{
VkMemoryPropertyFlags memoryPropertyFlags = 0;
bool persistentMapRequired = false;
switch (usage)
{
case gl::BufferUsage::InvalidEnum:
{
// glBufferStorage API call
memoryPropertyFlags = GetStorageMemoryType(flags);
persistentMapRequired = (flags & GL_MAP_PERSISTENT_BIT_EXT) != 0;
break;
}
default:
{
// glBufferData API call
memoryPropertyFlags = GetPreferredMemoryType(target, usage);
break;
}
}
return setDataWithMemoryType(context, target, data, size, memoryPropertyFlags,
persistentMapRequired);
}
angle::Result BufferVk::setData(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
gl::BufferUsage usage)
{
// Assume host visible/coherent memory available.
VkMemoryPropertyFlags memoryPropertyFlags = GetPreferredMemoryType(target, usage);
return setDataWithMemoryType(context, target, data, size, memoryPropertyFlags, false);
}
angle::Result BufferVk::setDataWithMemoryType(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
VkMemoryPropertyFlags memoryPropertyFlags,
bool persistentMapRequired)
{
ContextVk *contextVk = vk::GetImpl(context);
// BufferData call is re-specifying the entire buffer
......@@ -207,9 +267,6 @@ angle::Result BufferVk::setData(const gl::Context *context,
usageFlags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
}
// Assume host visible/coherent memory available.
VkMemoryPropertyFlags memoryPropertyFlags = GetPreferredMemoryType(target, usage);
// mBuffer will be allocated through a DynamicBuffer
constexpr size_t kBufferHelperAlignment = 1;
constexpr size_t kBufferHelperPoolInitialSize = 0;
......@@ -219,9 +276,15 @@ angle::Result BufferVk::setData(const gl::Context *context,
ANGLE_TRY(acquireBufferHelper(contextVk, size, &mBuffer));
// persistentMapRequired may request that the server read from or write to the buffer while
// it is mapped. The client's pointer to the data store remains valid so long as the data
// store is mapped. So it cannot have shadow buffer
if (!persistentMapRequired)
{
// Initialize the shadow buffer
ANGLE_TRY(initializeShadowBuffer(contextVk, target, size));
}
}
if (data && size > 0)
{
......
......@@ -48,6 +48,12 @@ class BufferVk : public BufferImpl
~BufferVk() override;
void destroy(const gl::Context *context) override;
angle::Result setDataWithUsageFlags(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
gl::BufferUsage usage,
GLbitfield flags) override;
angle::Result setData(const gl::Context *context,
gl::BufferBinding target,
const void *data,
......@@ -148,6 +154,12 @@ class BufferVk : public BufferImpl
const uint8_t *data,
size_t size,
size_t offset);
angle::Result setDataWithMemoryType(const gl::Context *context,
gl::BufferBinding target,
const void *data,
size_t size,
VkMemoryPropertyFlags memoryPropertyFlags,
bool persistentMapRequired);
angle::Result setDataImpl(ContextVk *contextVk,
const uint8_t *data,
size_t size,
......
......@@ -3649,6 +3649,12 @@ angle::Result ContextVk::memoryBarrierImpl(GLbitfield barriers, VkPipelineStageF
mOutsideRenderPassCommands->getCommandBuffer().memoryBarrier(stageMask, stageMask,
&memoryBarrier);
if ((barriers & GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT) != 0)
{
// We need to make sure that all device-writes are host-visible, force a finish
ANGLE_TRY(finishImpl());
}
return angle::Result::Continue;
}
......@@ -4175,7 +4181,7 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
VkMemoryBarrier memoryBarrier = {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT;
mOutsideRenderPassCommands->getCommandBuffer().memoryBarrier(
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_HOST_BIT, &memoryBarrier);
......
......@@ -48,6 +48,9 @@ void RendererVk::ensureCapsInitialized() const
mNativeExtensions.setTextureExtensionSupport(mNativeTextureCaps);
// Enable GL_EXT_buffer_storage"
mNativeExtensions.bufferStorageEXT = true;
// TODO: http://anglebug.com/3609
// Due to a dEQP bug, this extension cannot be exposed until EXT_texture_sRGB_decode is
// implemented
......
......@@ -3954,11 +3954,35 @@ bool ValidateMapBufferRangeBase(const Context *context,
}
// Check for invalid bits in the mask
GLbitfield allAccessBits = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT |
GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT |
GL_MAP_UNSYNCHRONIZED_BIT;
constexpr GLbitfield kAllAccessBits =
GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT |
GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
if (access & ~(allAccessBits))
if (buffer->isImmutable())
{
// GL_EXT_buffer_storage's additions to glMapBufferRange
constexpr GLbitfield kBufferStorageAccessBits =
kAllAccessBits | GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT;
if ((access & ~kBufferStorageAccessBits) != 0)
{
context->validationError(GL_INVALID_VALUE, kInvalidAccessBits);
return false;
}
// It is invalid if any of bufferStorageMatchedAccessBits bits are included in access,
// but the same bits are not included in the buffer's storage flags
constexpr GLbitfield kBufferStorageMatchedAccessBits = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
GL_MAP_PERSISTENT_BIT_EXT |
GL_MAP_COHERENT_BIT_EXT;
GLbitfield accessFlags = access & kBufferStorageMatchedAccessBits;
if ((accessFlags & buffer->getStorageExtUsageFlags()) != accessFlags)
{
context->validationError(GL_INVALID_OPERATION, kInvalidAccessBits);
return false;
}
}
else if ((access & ~kAllAccessBits) != 0)
{
context->validationError(GL_INVALID_VALUE, kInvalidAccessBits);
return false;
......
......@@ -3168,6 +3168,15 @@ bool ValidateMapBufferOES(const Context *context, BufferBinding target, GLenum a
return false;
}
// Though there is no explicit mention of an interaction between GL_EXT_buffer_storage
// and GL_OES_mapbuffer extension, allow it as long as the access type of glMapBufferOES
// is compatible with the buffer's usage flags specified during glBufferStorageEXT
if (buffer->isImmutable() && (buffer->getStorageExtUsageFlags() & GL_MAP_WRITE_BIT) == 0)
{
context->validationError(GL_INVALID_OPERATION, kBufferNotMappable);
return false;
}
if (buffer->isMapped())
{
context->validationError(GL_INVALID_OPERATION, kBufferAlreadyMapped);
......@@ -3700,6 +3709,12 @@ bool ValidateBufferData(const Context *context,
return false;
}
if (buffer->isImmutable())
{
context->validationError(GL_INVALID_OPERATION, kBufferImmutable);
return false;
}
return true;
}
......@@ -3748,6 +3763,13 @@ bool ValidateBufferSubData(const Context *context,
return false;
}
if (buffer->isImmutable() &&
(buffer->getStorageExtUsageFlags() & GL_DYNAMIC_STORAGE_BIT_EXT) == 0)
{
context->validationError(GL_INVALID_OPERATION, kBufferNotUpdatable);
return false;
}
// Check for possible overflow of size + offset
angle::CheckedNumeric<size_t> checkedSize(size);
checkedSize += offset;
......
......@@ -1954,6 +1954,12 @@ bool ValidateMemoryBarrier(const Context *context, GLbitfield barriers)
GL_PIXEL_BUFFER_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT |
GL_FRAMEBUFFER_BARRIER_BIT | GL_TRANSFORM_FEEDBACK_BARRIER_BIT |
GL_ATOMIC_COUNTER_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT;
if (context->getExtensions().bufferStorageEXT)
{
supported_barrier_bits |= GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT;
}
if (barriers == 0 || (barriers & ~supported_barrier_bits) != 0)
{
context->validationError(GL_INVALID_VALUE, kInvalidMemoryBarrierBit);
......
......@@ -1057,7 +1057,54 @@ bool ValidateBufferStorageEXT(const Context *context,
const void *data,
GLbitfield flags)
{
UNIMPLEMENTED();
if (!context->isValidBufferBinding(targetPacked))
{
context->validationError(GL_INVALID_ENUM, kInvalidBufferTypes);
return false;
}
if (size < 0)
{
context->validationError(GL_INVALID_VALUE, kNegativeSize);
return false;
}
constexpr GLbitfield kAllUsageFlags =
(GL_DYNAMIC_STORAGE_BIT_EXT | GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_PERSISTENT_BIT_EXT | GL_CLIENT_STORAGE_BIT_EXT);
if ((flags & ~kAllUsageFlags) != 0)
{
context->validationError(GL_INVALID_VALUE, kInvalidBufferUsageFlags);
return false;
}
if (((flags & GL_MAP_PERSISTENT_BIT_EXT) != 0) &&
((flags & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == 0))
{
context->validationError(GL_INVALID_VALUE, kInvalidBufferUsageFlags);
return false;
}
if (((flags & GL_MAP_COHERENT_BIT_EXT) != 0) && ((flags & GL_MAP_PERSISTENT_BIT_EXT) == 0))
{
context->validationError(GL_INVALID_VALUE, kInvalidBufferUsageFlags);
return false;
}
Buffer *buffer = context->getState().getTargetBuffer(targetPacked);
if (buffer == nullptr)
{
context->validationError(GL_INVALID_OPERATION, kBufferNotBound);
return false;
}
if (buffer->isImmutable())
{
context->validationError(GL_INVALID_OPERATION, kBufferImmutable);
return false;
}
return true;
}
} // namespace gl
......@@ -820,10 +820,120 @@ TEST_P(BufferDataTestES3, BufferDataUnmap)
ASSERT_GL_NO_ERROR();
}
class BufferStorageTestES3 : public BufferDataTest
{};
// Verify that glBufferStorage makes a buffer immutable
TEST_P(BufferStorageTestES3, StorageBufferBufferData)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
std::vector<GLfloat> data(6, 1.0f);
GLBuffer buffer;
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferStorageEXT(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), data.data(), 0);
ASSERT_GL_NO_ERROR();
// Verify that calling glBufferStorageEXT again produces an error.
glBufferStorageEXT(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), data.data(), 0);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Verify that calling glBufferData after calling glBufferStorageEXT produces an error.
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), data.data(), GL_STATIC_DRAW);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Verify that glBufferStorageEXT can be called after glBufferData
TEST_P(BufferStorageTestES3, BufferDataStorageBuffer)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
std::vector<GLfloat> data(6, 1.0f);
GLBuffer buffer;
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), data.data(), GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
// Verify that calling glBufferStorageEXT again produces an error.
glBufferStorageEXT(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), data.data(), 0);
ASSERT_GL_NO_ERROR();
}
// Verify that we can perform subdata updates to a buffer marked with GL_DYNAMIC_STORAGE_BIT_EXT
// usage flag
TEST_P(BufferStorageTestES3, StorageBufferSubData)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
std::vector<GLfloat> data(6, 0.0f);
glUseProgram(mProgram);
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glBufferStorageEXT(GL_ARRAY_BUFFER, sizeof(GLfloat) * data.size(), nullptr,
GL_DYNAMIC_STORAGE_BIT_EXT);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * data.size(), data.data());
glVertexAttribPointer(mAttribLocation, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(mAttribLocation);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(8, 8, GLColor::black);
EXPECT_GL_NO_ERROR();
std::vector<GLfloat> data2(6, 1.0f);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * data2.size(), data2.data());
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(8, 8, GLColor::red);
EXPECT_GL_NO_ERROR();
}
// Test interaction between GL_OES_mapbuffer and GL_EXT_buffer_storage extensions.
TEST_P(BufferStorageTestES3, StorageBufferMapBufferOES)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage") ||
!IsGLExtensionEnabled("GL_EXT_map_buffer_range"));
std::vector<uint8_t> data(1024);
FillVectorWithRandomUBytes(&data);
GLBuffer buffer;
glBindBuffer(GL_ARRAY_BUFFER, buffer.get());
glBufferStorageEXT(GL_ARRAY_BUFFER, data.size(), nullptr, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
// Validate that other map flags don't work.
void *badMapPtr = glMapBufferOES(GL_ARRAY_BUFFER, GL_MAP_READ_BIT);
EXPECT_EQ(nullptr, badMapPtr);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Map and write.
void *mapPtr = glMapBufferOES(GL_ARRAY_BUFFER, GL_WRITE_ONLY_OES);
ASSERT_NE(nullptr, mapPtr);
ASSERT_GL_NO_ERROR();
memcpy(mapPtr, data.data(), data.size());
glUnmapBufferOES(GL_ARRAY_BUFFER);
// Validate data with EXT_map_buffer_range
void *readMapPtr = glMapBufferRangeEXT(GL_ARRAY_BUFFER, 0, data.size(), GL_MAP_READ_BIT_EXT);
ASSERT_NE(nullptr, readMapPtr);
ASSERT_GL_NO_ERROR();
std::vector<uint8_t> actualData(data.size());
memcpy(actualData.data(), readMapPtr, data.size());
glUnmapBufferOES(GL_ARRAY_BUFFER);
EXPECT_EQ(data, actualData);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(BufferDataTest);
ANGLE_INSTANTIATE_TEST_ES3(BufferDataTestES3);
ANGLE_INSTANTIATE_TEST_ES3(BufferStorageTestES3);
ANGLE_INSTANTIATE_TEST_ES3(IndexedBufferCopyTest);
#ifdef _WIN64
......
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