blob: 254b8a592fa2df5a259c1e3cd8f488f495963a5b [file] [log] [blame]
//
// 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);
if (data)
{
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);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Delete the source AHB
destroyAndroidHardwareBuffer(aHardwareBuffer);
}
// Verify that subdata updates to an external buffer backed by an AHB doesn't orphan the AHB
TEST_P(ExternalBufferTestES31, SubDataDoesNotCauseOrphaning)
{
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 AHB
AHardwareBuffer *aHardwareBuffer;
constexpr GLbitfield kFlags = GL_DYNAMIC_STORAGE_BIT_EXT;
aHardwareBuffer = createAndroidHardwareBuffer(kBufferSize, initData.data());
// Create externalBuffer
GLBuffer externalBuffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, externalBuffer);
glBufferStorageExternalEXT(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize,
eglGetNativeClientBufferANDROID(aHardwareBuffer), kFlags);
ASSERT_GL_NO_ERROR();
// Create a copy read buffer
std::vector<GLubyte> copyReadBufferData(kBufferSize, 0xB);
GLBuffer copyReadBuffer;
glBindBuffer(GL_COPY_READ_BUFFER, copyReadBuffer);
glBufferData(GL_COPY_READ_BUFFER, kBufferSize, copyReadBufferData.data(), GL_STATIC_READ);
ASSERT_GL_NO_ERROR();
// Copy from copyReadBuffer to externalBuffer
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_SHADER_STORAGE_BUFFER, 0, 0, kBufferSize);
ASSERT_GL_NO_ERROR();
// Update externalBuffer
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 AHB, through externalBuffer, 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);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Delete the source AHB
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, 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);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Delete the source AHB
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 AHB
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);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Delete the source AHB
destroyAndroidHardwareBuffer(aHardwareBuffer);
}
// Verify that mapping an external buffer backed by an AHB doesn't orphan the AHB
TEST_P(ExternalBufferTestES31, MapBufferDoesNotCauseOrphaning)
{
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, 0xA);
// Create the AHB
AHardwareBuffer *aHardwareBuffer;
constexpr GLbitfield kFlags =
(GL_MAP_READ_BIT_EXT | GL_MAP_WRITE_BIT_EXT | 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();
// Create a copy read buffer
std::vector<GLubyte> copyReadBufferData(kBufferSize, 0xB);
GLBuffer copyReadBuffer;
glBindBuffer(GL_COPY_READ_BUFFER, copyReadBuffer);
glBufferData(GL_COPY_READ_BUFFER, kBufferSize, copyReadBufferData.data(), GL_STATIC_READ);
ASSERT_GL_NO_ERROR();
// Copy from copyReadBuffer to externalBuffer
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_SHADER_STORAGE_BUFFER, 0, 0, kBufferSize);
ASSERT_GL_NO_ERROR();
// Inspect the data written into the buffer using map buffer API.
constexpr GLbitfield kMapFlags = (GL_MAP_WRITE_BIT_EXT | GL_MAP_INVALIDATE_BUFFER_BIT);
uint8_t *mapData = static_cast<uint8_t *>(
glMapBufferRangeEXT(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, kMapFlags));
ASSERT_GL_NO_ERROR();
EXPECT_NE(mapData, nullptr);
glUnmapBufferOES(GL_SHADER_STORAGE_BUFFER);
// Update externalBuffer
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 AHB, through externalBuffer, 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);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
// Delete the source AHB
destroyAndroidHardwareBuffer(aHardwareBuffer);
}
// Verify that create and destroy external buffer backed by an AHB doesn't leak AHB
TEST_P(ExternalBufferTestES31, BufferDoesNotLeakAHB)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_external_buffer") ||
!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
// Create and destroy 128M AHB backed buffer in a loop. If we leak AHB, it will fail due to AHB
// allocation failure before loop ends.
constexpr size_t kBufferSize = 128 * 1024 * 1024;
for (int loop = 0; loop < 1000; loop++)
{
// Create the AHB
AHardwareBuffer *aHardwareBuffer;
constexpr GLbitfield kFlags = GL_DYNAMIC_STORAGE_BIT_EXT;
aHardwareBuffer = createAndroidHardwareBuffer(kBufferSize, nullptr);
GLBuffer buffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
glBufferStorageExternalEXT(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize,
eglGetNativeClientBufferANDROID(aHardwareBuffer), kFlags);
ASSERT_GL_NO_ERROR();
// Delete the source AHB
destroyAndroidHardwareBuffer(aHardwareBuffer);
}
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ExternalBufferTestES31);
ANGLE_INSTANTIATE_TEST_ES31(ExternalBufferTestES31);
} // namespace angle