blob: 46956d6398e82069ec9e468daff52816f7edc6b1 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
#include <cutils/native_handle.h>
#include <sync/sync.h>
#include "cb_handle_30.h"
#include "host_connection_session.h"
#include "FormatConversions.h"
#include "debug.h"
#include "aemu/base/Tracing.h"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
const int kOMX_COLOR_FormatYUV420Planar = 19;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::graphics::common::V1_2::PixelFormat;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
namespace MapperV3 = ::android::hardware::graphics::mapper::V3_0;
using IMapper3 = MapperV3::IMapper;
using Error3 = MapperV3::Error;
using YCbCrLayout3 = MapperV3::YCbCrLayout;
namespace {
size_t align(const size_t v, const size_t a) { return (v + a - 1) & ~(a - 1); }
static int waitFenceFd(const int fd, const char* logname) {
const int warningTimeout = 5000;
if (sync_wait(fd, warningTimeout) < 0) {
if (errno == ETIME) {
ALOGW("%s: fence %d didn't signal in %d ms", logname, fd, warningTimeout);
if (sync_wait(fd, -1) < 0) {
RETURN_ERROR(errno);
} else {
RETURN(0);
}
} else {
RETURN_ERROR(errno);
}
} else {
RETURN(0);
}
}
int waitHidlFence(const hidl_handle& hidlHandle, const char* logname) {
const native_handle_t* nativeHandle = hidlHandle.getNativeHandle();
if (!nativeHandle) {
RETURN(0);
}
if (nativeHandle->numFds > 1) {
RETURN_ERROR(-EINVAL);
}
if (nativeHandle->numInts != 0) {
RETURN_ERROR(-EINVAL);
}
return waitFenceFd(nativeHandle->data[0], logname);
}
bool needGpuBuffer(const uint32_t usage) {
return usage & (BufferUsage::GPU_TEXTURE
| BufferUsage::GPU_RENDER_TARGET
| BufferUsage::COMPOSER_OVERLAY
| BufferUsage::COMPOSER_CLIENT_TARGET
| BufferUsage::GPU_DATA_BUFFER);
}
constexpr uint64_t one64 = 1;
constexpr uint64_t ones(int from, int to) {
return ((one64 << (to - from + 1)) - 1) << from;
}
class GoldfishMapper : public IMapper3 {
public:
GoldfishMapper() : m_hostConn(HostConnection::createUnique()) {
GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator(false);
CRASH_IF(!host_memory_allocator.is_opened(),
"GoldfishAddressSpaceHostMemoryAllocator failed to open");
GoldfishAddressSpaceBlock bufferBits;
CRASH_IF(host_memory_allocator.hostMalloc(&bufferBits, 256),
"hostMalloc failed");
m_physAddrToOffset = bufferBits.physAddr() - bufferBits.offset();
host_memory_allocator.hostFree(&bufferBits);
}
Return<void> importBuffer(const hidl_handle& hh,
importBuffer_cb hidl_cb) {
native_handle_t* imported = nullptr;
const Error3 e = importBufferImpl(hh.getNativeHandle(), &imported);
if (e == Error3::NONE) {
hidl_cb(Error3::NONE, imported);
} else {
hidl_cb(e, nullptr);
}
return {};
}
Return<Error3> freeBuffer(void* raw) {
if (!raw) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
cb_handle_30_t* cb = cb_handle_30_t::from(raw);
if (!cb) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (cb->mmapedSize > 0) {
GoldfishAddressSpaceBlock::memoryUnmap(cb->getBufferPtr(), cb->mmapedSize);
}
native_handle_close(cb);
native_handle_delete(cb);
RETURN(Error3::NONE);
}
Return<void> lock(void* raw,
uint64_t cpuUsage,
const Rect& accessRegion,
const hidl_handle& acquireFence,
lock_cb hidl_cb) {
void* ptr = nullptr;
int32_t bytesPerPixel = 0;
int32_t bytesPerStride = 0;
const Error3 e = lockImpl(raw, cpuUsage, accessRegion, acquireFence,
&ptr, &bytesPerPixel, &bytesPerStride);
if (e == Error3::NONE) {
hidl_cb(Error3::NONE, ptr, bytesPerPixel, bytesPerStride);
} else {
hidl_cb(e, nullptr, 0, 0);
}
return {};
}
Return<void> lockYCbCr(void* raw,
uint64_t cpuUsage,
const Rect& accessRegion,
const hidl_handle& acquireFence,
lockYCbCr_cb hidl_cb) {
YCbCrLayout3 ycbcr = {};
const Error3 e = lockYCbCrImpl(raw, cpuUsage, accessRegion, acquireFence,
&ycbcr);
if (e == Error3::NONE) {
hidl_cb(Error3::NONE, ycbcr);
} else {
hidl_cb(e, {});
}
return {};
}
Return<void> unlock(void* raw, unlock_cb hidl_cb) {
hidl_cb(unlockImpl(raw), {});
return {};
}
Return<void> createDescriptor(const BufferDescriptorInfo& description,
createDescriptor_cb hidl_cb) {
hidl_vec<uint32_t> raw;
encodeBufferDescriptorInfo(description, &raw);
hidl_cb(Error3::NONE, raw);
return {};
}
Return<void> isSupported(const IMapper::BufferDescriptorInfo& description,
isSupported_cb hidl_cb) {
hidl_cb(Error3::NONE, isSupportedImpl(description));
return {};
}
Return<Error3> validateBufferSize(void* buffer,
const BufferDescriptorInfo& descriptor,
uint32_t stride) {
const cb_handle_30_t* cb = cb_handle_30_t::from(buffer);
if (cb) {
return validateBufferSizeImpl(*cb, descriptor, stride);
} else {
RETURN_ERROR(Error3::BAD_BUFFER);
}
}
Return<void> getTransportSize(void* buffer,
getTransportSize_cb hidl_cb) {
const cb_handle_30_t* cb = cb_handle_30_t::from(buffer);
if (cb) {
hidl_cb(Error3::NONE, cb->numFds, cb->numInts);
} else {
hidl_cb(Error3::BAD_BUFFER, 0, 0);
}
return {};
}
private: // **** impl ****
Error3 importBufferImpl(const native_handle_t* nh, native_handle_t** phandle) {
if (!nh) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
native_handle_t* imported = native_handle_clone(nh);
if (!imported) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
cb_handle_30_t* cb = cb_handle_30_t::from(imported);
if (!cb) {
native_handle_close(imported);
native_handle_delete(imported);
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (cb->mmapedSize > 0) {
LOG_ALWAYS_FATAL_IF(cb->bufferFdIndex < 0);
void* ptr;
const int res = GoldfishAddressSpaceBlock::memoryMap(
cb->getBufferPtr(),
cb->mmapedSize,
cb->fds[cb->bufferFdIndex],
cb->getMmapedOffset(),
&ptr);
if (res) {
native_handle_close(imported);
native_handle_delete(imported);
RETURN_ERROR(Error3::NO_RESOURCES);
}
cb->setBufferPtr(ptr);
}
*phandle = imported;
RETURN(Error3::NONE);
}
void setLocked(cb_handle_30_t* cb, const uint8_t checkedUsage,
const Rect& accessRegion) {
if (checkedUsage & BufferUsage::CPU_WRITE_MASK) {
cb->lockedLeft = accessRegion.left;
cb->lockedTop = accessRegion.top;
cb->lockedWidth = accessRegion.width;
cb->lockedHeight = accessRegion.height;
} else {
cb->lockedLeft = 0;
cb->lockedTop = 0;
cb->lockedWidth = cb->width;
cb->lockedHeight = cb->height;
}
cb->lockedUsage = checkedUsage;
}
Error3 lockImpl(void* raw,
const uint64_t uncheckedUsage,
const Rect& accessRegion,
const hidl_handle& acquireFence,
void** pptr,
int32_t* pBytesPerPixel,
int32_t* pBytesPerStride) {
if (!raw) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
cb_handle_30_t* cb = cb_handle_30_t::from(raw);
if (!cb) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (cb->lockedUsage) {
RETURN_ERROR(Error3::BAD_VALUE);
}
const uint8_t checkedUsage = uncheckedUsage & cb->usage &
(BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK);
if (checkedUsage == 0) {
RETURN_ERROR(Error3::BAD_VALUE);
}
if (!cb->bufferSize) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
char* const bufferBits = static_cast<char*>(cb->getBufferPtr());
if (!bufferBits) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (waitHidlFence(acquireFence, __func__)) {
RETURN_ERROR(Error3::BAD_VALUE);
}
if (cb->hostHandle) {
const Error3 e = lockHostImpl(*cb, checkedUsage, accessRegion, bufferBits);
if (e != Error3::NONE) {
return e;
}
}
setLocked(cb, checkedUsage, accessRegion);
*pptr = bufferBits;
*pBytesPerPixel = cb->bytesPerPixel;
*pBytesPerStride = cb->bytesPerPixel * cb->stride;
RETURN(Error3::NONE);
}
Error3 lockYCbCrImpl(void* raw,
const uint64_t uncheckedUsage,
const Rect& accessRegion,
const hidl_handle& acquireFence,
YCbCrLayout3* pYcbcr) {
if (!raw) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
cb_handle_30_t* cb = cb_handle_30_t::from(raw);
if (!cb) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (cb->lockedUsage) {
RETURN_ERROR(Error3::BAD_VALUE);
}
const uint8_t checkedUsage = uncheckedUsage & cb->usage &
(BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK);
if (checkedUsage == 0) {
RETURN_ERROR(Error3::BAD_VALUE);
}
if (!cb->bufferSize) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
char* const bufferBits = static_cast<char*>(cb->getBufferPtr());
if (!bufferBits) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (waitHidlFence(acquireFence, __func__)) {
RETURN_ERROR(Error3::BAD_VALUE);
}
size_t uOffset;
size_t vOffset;
size_t yStride;
size_t cStride;
size_t cStep;
switch (static_cast<PixelFormat>(cb->format)) {
case PixelFormat::YCRCB_420_SP:
yStride = cb->width;
cStride = yStride;
vOffset = yStride * cb->height;
uOffset = vOffset + 1;
cStep = 2;
break;
case PixelFormat::YV12:
// https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12
yStride = align(cb->width, 16);
cStride = align(yStride / 2, 16);
vOffset = yStride * cb->height;
uOffset = vOffset + (cStride * cb->height / 2);
cStep = 1;
break;
case PixelFormat::YCBCR_420_888:
yStride = cb->width;
cStride = yStride / 2;
uOffset = cb->height * yStride;
vOffset = uOffset + cStride * cb->height / 2;
cStep = 1;
break;
case PixelFormat::YCBCR_P010:
yStride = cb->width * 2;
cStride = yStride;
uOffset = cb->height * yStride;
vOffset = uOffset + 2;
cStep = 4;
break;
default:
ALOGE("%s:%d unexpected format (%d)", __func__, __LINE__, cb->format);
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (cb->hostHandle) {
const Error3 e = lockHostImpl(*cb, checkedUsage, accessRegion, bufferBits);
if (e != Error3::NONE) {
return e;
}
}
setLocked(cb, checkedUsage, accessRegion);
pYcbcr->y = bufferBits;
pYcbcr->cb = bufferBits + uOffset;
pYcbcr->cr = bufferBits + vOffset;
pYcbcr->yStride = yStride;
pYcbcr->cStride = cStride;
pYcbcr->chromaStep = cStep;
RETURN(Error3::NONE);
}
Error3 lockHostImpl(cb_handle_30_t& cb,
const uint8_t checkedUsage,
const Rect& accessRegion,
char* const bufferBits) {
const HostConnectionSession conn = getHostConnectionSession();
ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder();
const bool usageSwRead = (checkedUsage & BufferUsage::CPU_READ_MASK) != 0;
const int res = rcEnc->rcColorBufferCacheFlush(
rcEnc, cb.hostHandle, 0, usageSwRead);
if (res < 0) {
RETURN_ERROR(Error3::NO_RESOURCES);
}
if (usageSwRead) {
if (gralloc_is_yuv_format(cb.format)) {
if (rcEnc->hasYUVCache()) {
uint32_t bufferSize;
switch (static_cast<PixelFormat>(cb.format)) {
case PixelFormat::YV12:
get_yv12_offsets(cb.width, cb.height,
nullptr, nullptr, &bufferSize);
break;
case PixelFormat::YCBCR_420_888:
get_yuv420p_offsets(cb.width, cb.height,
nullptr, nullptr, &bufferSize);
break;
default:
CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format");
break;
}
rcEnc->rcReadColorBufferYUV(rcEnc, cb.hostHandle,
0, 0, cb.width, cb.height,
bufferBits, bufferSize);
} else {
// We are using RGB888
std::vector<char> tmpBuf(cb.width * cb.height * 3);
rcEnc->rcReadColorBuffer(rcEnc, cb.hostHandle,
0, 0, cb.width, cb.height,
cb.glFormat, cb.glType,
tmpBuf.data());
switch (static_cast<PixelFormat>(cb.format)) {
case PixelFormat::YV12:
rgb888_to_yv12(bufferBits, tmpBuf.data(),
cb.width, cb.height,
accessRegion.left,
accessRegion.top,
accessRegion.left + accessRegion.width - 1,
accessRegion.top + accessRegion.height - 1);
break;
case PixelFormat::YCBCR_420_888:
rgb888_to_yuv420p(bufferBits, tmpBuf.data(),
cb.width, cb.height,
accessRegion.left,
accessRegion.top,
accessRegion.left + accessRegion.width - 1,
accessRegion.top + accessRegion.height - 1);
break;
default:
CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format");
break;
}
}
} else {
if (rcEnc->featureInfo()->hasReadColorBufferDma) {
{
AEMU_SCOPED_TRACE("bindDmaDirectly");
rcEnc->bindDmaDirectly(bufferBits,
getMmapedPhysAddr(cb.getMmapedOffset()));
}
rcEnc->rcReadColorBufferDMA(rcEnc,
cb.hostHandle,
0, 0, cb.width, cb.height,
cb.glFormat, cb.glType,
bufferBits, cb.width * cb.height * cb.bytesPerPixel);
} else {
rcEnc->rcReadColorBuffer(rcEnc,
cb.hostHandle,
0, 0, cb.width, cb.height,
cb.glFormat, cb.glType,
bufferBits);
}
}
}
RETURN(Error3::NONE);
}
Error3 unlockImpl(void* raw) {
AEMU_SCOPED_TRACE("unlockImpl body");
if (!raw) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
cb_handle_30_t* cb = cb_handle_30_t::from(raw);
if (!cb) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (cb->lockedUsage == 0) {
RETURN_ERROR(Error3::BAD_VALUE);
}
if (!cb->bufferSize) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
char* const bufferBits = static_cast<char*>(cb->getBufferPtr());
if (!bufferBits) {
RETURN_ERROR(Error3::BAD_BUFFER);
}
if (cb->hostHandle) {
unlockHostImpl(*cb, bufferBits);
}
cb->lockedLeft = 0;
cb->lockedTop = 0;
cb->lockedWidth = 0;
cb->lockedHeight = 0;
cb->lockedUsage = 0;
RETURN(Error3::NONE);
}
void unlockHostImpl(cb_handle_30_t& cb, char* const bufferBits) {
AEMU_SCOPED_TRACE("unlockHostImpl body");
if (cb.lockedUsage & BufferUsage::CPU_WRITE_MASK) {
const int bpp = glUtilsPixelBitSize(cb.glFormat, cb.glType) >> 3;
const uint32_t rgbSize = cb.width * cb.height * bpp;
const char* bitsToSend;
uint32_t sizeToSend;
if (gralloc_is_yuv_format(cb.format)) {
bitsToSend = bufferBits;
switch (static_cast<PixelFormat>(cb.format)) {
case PixelFormat::YV12:
get_yv12_offsets(cb.width, cb.height, nullptr, nullptr, &sizeToSend);
break;
case PixelFormat::YCBCR_420_888:
get_yuv420p_offsets(cb.width, cb.height, nullptr, nullptr, &sizeToSend);
break;
default:
CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format");
break;
}
} else {
bitsToSend = bufferBits;
sizeToSend = rgbSize;
}
{
const HostConnectionSession conn = getHostConnectionSession();
ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder();
{
AEMU_SCOPED_TRACE("bindDmaDirectly");
rcEnc->bindDmaDirectly(bufferBits,
getMmapedPhysAddr(cb.getMmapedOffset()));
}
{
AEMU_SCOPED_TRACE("updateColorBuffer");
rcEnc->rcUpdateColorBufferDMA(rcEnc, cb.hostHandle,
0, 0, cb.width, cb.height,
cb.glFormat, cb.glType,
const_cast<char*>(bitsToSend),
sizeToSend);
}
}
}
}
/* BufferUsage bits that must be zero */
static constexpr uint64_t kReservedUsage =
(one64 << 10)
| (one64 << 13)
| (one64 << 19)
| (one64 << 21)
| ones(25, 27) /* bits 25-27 must be zero and are reserved for future versions */
| ones(32, 47); /* bits 32-47 must be zero and are reserved for future versions */
bool isSupportedImpl(const IMapper::BufferDescriptorInfo& descriptor) const {
if (!descriptor.width) { RETURN(false); }
if (!descriptor.height) { RETURN(false); }
if (descriptor.layerCount != 1) { RETURN(false); }
const uint64_t usage64 = descriptor.usage;
if (usage64 & kReservedUsage) {
RETURN(false);
}
const uint32_t usage = usage64;
switch (descriptor.format) {
case PixelFormat::RGBA_8888:
case PixelFormat::RGBX_8888:
case PixelFormat::BGRA_8888:
case PixelFormat::RGB_565:
case PixelFormat::RGBA_FP16:
case PixelFormat::RGBA_1010102:
case PixelFormat::YV12:
case PixelFormat::YCBCR_420_888:
RETURN(true);
case PixelFormat::IMPLEMENTATION_DEFINED:
RETURN(false);
case PixelFormat::RGB_888:
case PixelFormat::YCRCB_420_SP:
case PixelFormat::RAW16:
case PixelFormat::Y16:
case PixelFormat::BLOB:
RETURN(!needGpuBuffer(usage));
default:
if (static_cast<int>(descriptor.format) == kOMX_COLOR_FormatYUV420Planar) {
return (usage & BufferUsage::VIDEO_DECODER) != 0;
}
RETURN(false);
}
}
Error3 validateBufferSizeImpl(const cb_handle_t& /*cb*/,
const BufferDescriptorInfo& /*descriptor*/,
uint32_t /*stride*/) {
RETURN(Error3::NONE);
}
HostConnectionSession getHostConnectionSession() const {
return HostConnectionSession(m_hostConn.get());
}
static void encodeBufferDescriptorInfo(const BufferDescriptorInfo& d,
hidl_vec<uint32_t>* raw) {
raw->resize(5);
(*raw)[0] = d.width;
(*raw)[1] = d.height;
(*raw)[2] = d.layerCount;
(*raw)[3] = static_cast<uint32_t>(d.format);
(*raw)[4] = d.usage & UINT32_MAX;
}
uint64_t getMmapedPhysAddr(uint64_t offset) const {
return m_physAddrToOffset + offset;
}
std::unique_ptr<HostConnection> m_hostConn;
uint64_t m_physAddrToOffset;
};
} // namespace
extern "C" IMapper3* HIDL_FETCH_IMapper(const char* /*name*/) {
return new GoldfishMapper;
}