gralloc: add flush and reread for locked buffers
When a buffer is locked (mapped to the CPU) by two or more clients, there is no good way to manage a reader/writer relationship. There are no requirements for how the writes should propagate to the readers. Clients must unlock and relock to be sure writes are flushed. They must unlock and relock to get the lastest copy of the buffer. This patch adds explicit flush and reread commands to help readers and writers synchronize without having to unmap and remap the buffer. Bug: 136316517 Test: TODO Change-Id: I10d3de1b0e46c4f3b50dc34aea653701933638a9
This commit is contained in:
parent
b524bbb922
commit
2c45bb16f5
5 changed files with 166 additions and 13 deletions
|
@ -258,6 +258,50 @@ interface IMapper {
|
|||
*/
|
||||
unlock(pointer buffer) generates (Error error, handle releaseFence);
|
||||
|
||||
/**
|
||||
* Flushes the contents of a locked buffer.
|
||||
*
|
||||
* This function flushes the CPUs caches for the range of all the buffer's
|
||||
* planes and metadata. This should behave similarly to unlock() except the
|
||||
* buffer should remain mapped to the CPU.
|
||||
*
|
||||
* The client is still responsible for calling unlock() when it is done
|
||||
* with all CPU accesses to the buffer.
|
||||
*
|
||||
* If non-CPU blocks are simultaneously writing the buffer, the locked
|
||||
* copy should still be flushed but what happens is undefined except that
|
||||
* it should not cause any crashes.
|
||||
*
|
||||
* @param buffer Buffer to flush.
|
||||
* @return error Error status of the call, which may be
|
||||
* - `NONE` upon success.
|
||||
* - `BAD_BUFFER` if the buffer is invalid or not locked.
|
||||
* @return releaseFence Handle containing a file descriptor referring to a
|
||||
* sync fence object. The sync fence object will be signaled when the
|
||||
* mapper has completed any pending work. @p releaseFence may be an
|
||||
* empty fence.
|
||||
*/
|
||||
flushLockedBuffer(pointer buffer) generates (Error error, handle releaseFence);
|
||||
|
||||
/**
|
||||
* Rereads the contents of a locked buffer.
|
||||
*
|
||||
* This should fetch the most recent copy of the locked buffer.
|
||||
*
|
||||
* It may reread locked copies of the buffer in other processes.
|
||||
*
|
||||
* The client is still responsible for calling unlock() when it is done
|
||||
* with all CPU accesses to the buffer.
|
||||
*
|
||||
* @param buffer Buffer to reread.
|
||||
* @return error Error status of the call, which may be
|
||||
* - `NONE` upon success.
|
||||
* - `BAD_BUFFER` if the buffer is invalid or not locked.
|
||||
* - `NO_RESOURCES` if the buffer cannot be reread at this time. Note
|
||||
* that rereading may succeed at a later time.
|
||||
*/
|
||||
rereadLockedBuffer(pointer buffer) generates(Error error);
|
||||
|
||||
/**
|
||||
* Test whether the given BufferDescriptorInfo is allocatable.
|
||||
*
|
||||
|
|
|
@ -246,6 +246,34 @@ int Gralloc::unlock(const native_handle_t* bufferHandle) {
|
|||
return releaseFence;
|
||||
}
|
||||
|
||||
int Gralloc::flushLockedBuffer(const native_handle_t* bufferHandle) {
|
||||
auto buffer = const_cast<native_handle_t*>(bufferHandle);
|
||||
|
||||
int releaseFence = -1;
|
||||
mMapper->flushLockedBuffer(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
|
||||
ASSERT_EQ(Error::NONE, tmpError) << "failed to flush locked buffer " << buffer;
|
||||
|
||||
auto fenceHandle = tmpReleaseFence.getNativeHandle();
|
||||
if (fenceHandle) {
|
||||
ASSERT_EQ(0, fenceHandle->numInts) << "invalid fence handle " << fenceHandle;
|
||||
if (fenceHandle->numFds == 1) {
|
||||
releaseFence = dup(fenceHandle->data[0]);
|
||||
ASSERT_LT(0, releaseFence) << "failed to dup fence fd";
|
||||
} else {
|
||||
ASSERT_EQ(0, fenceHandle->numFds) << " invalid fence handle " << fenceHandle;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return releaseFence;
|
||||
}
|
||||
|
||||
void Gralloc::rereadLockedBuffer(const native_handle_t* bufferHandle) {
|
||||
auto buffer = const_cast<native_handle_t*>(bufferHandle);
|
||||
|
||||
ASSERT_EQ(Error::NONE, mMapper->rereadLockedBuffer(buffer));
|
||||
}
|
||||
|
||||
bool Gralloc::validateBufferSize(const native_handle_t* bufferHandle,
|
||||
const IMapper::BufferDescriptorInfo& descriptorInfo,
|
||||
uint32_t stride) {
|
||||
|
|
|
@ -73,6 +73,9 @@ class Gralloc {
|
|||
const IMapper::Rect& accessRegion, int acquireFence);
|
||||
int unlock(const native_handle_t* bufferHandle);
|
||||
|
||||
int flushLockedBuffer(const native_handle_t* bufferHandle);
|
||||
void rereadLockedBuffer(const native_handle_t* bufferHandle);
|
||||
|
||||
bool validateBufferSize(const native_handle_t* bufferHandle,
|
||||
const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t stride);
|
||||
void getTransportSize(const native_handle_t* bufferHandle, uint32_t* outNumFds,
|
||||
|
|
|
@ -20,6 +20,7 @@ cc_test {
|
|||
srcs: ["VtsHalGraphicsMapperV4_0TargetTest.cpp"],
|
||||
static_libs: [
|
||||
"android.hardware.graphics.mapper@4.0-vts",
|
||||
"libsync",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.graphics.allocator@4.0",
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <VtsHalHidlTargetTestBase.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/sync.h>
|
||||
#include <gralloctypes/Gralloc4.h>
|
||||
#include <mapper-vts/4.0/MapperVts.h>
|
||||
#include <system/graphics.h>
|
||||
|
@ -273,6 +274,24 @@ class GraphicsMapperHidlTest : public ::testing::VtsHalHidlTargetTestBase {
|
|||
ASSERT_NE(nullptr, outYCbCr->cr);
|
||||
}
|
||||
|
||||
void fillRGBA8888(uint8_t* data, uint32_t height, size_t strideInBytes, size_t widthInBytes,
|
||||
uint32_t seed = 0) {
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
memset(data, y + seed, widthInBytes);
|
||||
data += strideInBytes;
|
||||
}
|
||||
}
|
||||
|
||||
void verifyRGBA8888(uint8_t* data, uint32_t height, size_t strideInBytes, size_t widthInBytes,
|
||||
uint32_t seed = 0) {
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
for (size_t i = 0; i < widthInBytes; i++) {
|
||||
EXPECT_EQ(static_cast<uint8_t>(y + seed), data[i]);
|
||||
}
|
||||
data += strideInBytes;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Gralloc> mGralloc;
|
||||
IMapper::BufferDescriptorInfo mDummyDescriptorInfo{};
|
||||
static const std::set<StandardMetadataType> sRequiredMetadataTypes;
|
||||
|
@ -529,25 +548,14 @@ TEST_F(GraphicsMapperHidlTest, LockUnlockBasic) {
|
|||
data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
|
||||
|
||||
// RGBA_8888
|
||||
size_t strideInBytes = stride * 4;
|
||||
size_t writeInBytes = info.width * 4;
|
||||
|
||||
for (uint32_t y = 0; y < info.height; y++) {
|
||||
memset(data, y, writeInBytes);
|
||||
data += strideInBytes;
|
||||
}
|
||||
fillRGBA8888(data, info.height, stride * 4, info.width * 4);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
|
||||
|
||||
// lock again for reading
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
|
||||
for (uint32_t y = 0; y < info.height; y++) {
|
||||
for (size_t i = 0; i < writeInBytes; i++) {
|
||||
EXPECT_EQ(static_cast<uint8_t>(y), data[i]);
|
||||
}
|
||||
data += strideInBytes;
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(verifyRGBA8888(data, info.height, stride * 4, info.width * 4));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
|
||||
if (fence >= 0) {
|
||||
|
@ -705,6 +713,75 @@ TEST_F(GraphicsMapperHidlTest, UnlockNegative) {
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IMapper::flush and IMapper::reread.
|
||||
*/
|
||||
TEST_F(GraphicsMapperHidlTest, FlushRereadBasic) {
|
||||
const auto& info = mDummyDescriptorInfo;
|
||||
|
||||
const native_handle_t* rawHandle;
|
||||
uint32_t stride;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
rawHandle = mGralloc->allocate(mDummyDescriptorInfo, false, false, &stride));
|
||||
|
||||
const native_handle_t* writeBufferHandle;
|
||||
const native_handle_t* readBufferHandle;
|
||||
ASSERT_NO_FATAL_FAILURE(writeBufferHandle = mGralloc->importBuffer(rawHandle));
|
||||
ASSERT_NO_FATAL_FAILURE(readBufferHandle = mGralloc->importBuffer(rawHandle));
|
||||
|
||||
// lock buffer for writing
|
||||
const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
|
||||
static_cast<int32_t>(info.height)};
|
||||
uint8_t* writeData;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
writeData = static_cast<uint8_t*>(mGralloc->lock(
|
||||
writeBufferHandle, static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN), region,
|
||||
-1)));
|
||||
|
||||
uint8_t* readData;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
readData = static_cast<uint8_t*>(mGralloc->lock(
|
||||
readBufferHandle, static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN), region,
|
||||
-1)));
|
||||
|
||||
fillRGBA8888(writeData, info.height, stride * 4, info.width * 4);
|
||||
|
||||
int fence;
|
||||
ASSERT_NO_FATAL_FAILURE(fence = mGralloc->flushLockedBuffer(writeBufferHandle));
|
||||
ASSERT_EQ(0, sync_wait(fence, 3500));
|
||||
close(fence);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(mGralloc->rereadLockedBuffer(readBufferHandle));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(verifyRGBA8888(readData, info.height, stride * 4, info.width * 4));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(readBufferHandle));
|
||||
if (fence >= 0) {
|
||||
close(fence);
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(writeBufferHandle));
|
||||
if (fence >= 0) {
|
||||
close(fence);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IMapper::flushLockedBuffer with bad buffer
|
||||
*/
|
||||
TEST_F(GraphicsMapperHidlTest, FlushLockedBufferBadBuffer) {
|
||||
ASSERT_NO_FATAL_FAILURE(mGralloc->getMapper()->flushLockedBuffer(
|
||||
nullptr, [&](const auto& tmpError, const auto& /*tmpReleaseFence*/) {
|
||||
ASSERT_EQ(Error::BAD_BUFFER, tmpError);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IMapper::rereadLockedBuffer with bad buffer
|
||||
*/
|
||||
TEST_F(GraphicsMapperHidlTest, RereadLockedBufferBadBuffer) {
|
||||
ASSERT_EQ(Error::BAD_BUFFER, mGralloc->getMapper()->rereadLockedBuffer(nullptr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IMapper::isSupported with required format RGBA_8888
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue