You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
17 KiB
460 lines
17 KiB
/*
|
|
* 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/allocator/3.0/IAllocator.h>
|
|
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
|
|
#include <hidl/LegacySupport.h>
|
|
#include <qemu_pipe_bp.h>
|
|
|
|
#include "glUtils.h"
|
|
#include "cb_handle_30.h"
|
|
#include "host_connection_session.h"
|
|
#include "types.h"
|
|
#include "debug.h"
|
|
|
|
const int kOMX_COLOR_FormatYUV420Planar = 19;
|
|
|
|
using ::android::hardware::hidl_handle;
|
|
using ::android::hardware::hidl_vec;
|
|
using ::android::hardware::hidl_bitfield;
|
|
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 AllocatorV3 = ::android::hardware::graphics::allocator::V3_0;
|
|
namespace MapperV3 = ::android::hardware::graphics::mapper::V3_0;
|
|
|
|
using IAllocator3 = AllocatorV3::IAllocator;
|
|
using IMapper3 = MapperV3::IMapper;
|
|
using Error3 = MapperV3::Error;
|
|
using BufferDescriptorInfo = IMapper3::BufferDescriptorInfo;
|
|
|
|
class GoldfishAllocator : public IAllocator3 {
|
|
public:
|
|
GoldfishAllocator() : m_hostConn(HostConnection::createUnique()) {}
|
|
|
|
Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
|
|
hidl_cb("GoldfishAllocator::dumpDebugInfo is not implemented");
|
|
return {};
|
|
}
|
|
|
|
Return<void> allocate(const hidl_vec<uint32_t>& rawDescriptor,
|
|
uint32_t count,
|
|
allocate_cb hidl_cb) {
|
|
uint32_t stride = 0;
|
|
std::vector<cb_handle_30_t*> cbs;
|
|
cbs.reserve(count);
|
|
|
|
const Error3 e = allocateImpl(rawDescriptor, count, &stride, &cbs);
|
|
if (e == Error3::NONE) {
|
|
hidl_vec<hidl_handle> handles(cbs.cbegin(), cbs.cend());
|
|
hidl_cb(Error3::NONE, stride, handles);
|
|
} else {
|
|
hidl_cb(e, 0, {});
|
|
}
|
|
|
|
for (cb_handle_30_t* cb : cbs) {
|
|
freeCb(std::unique_ptr<cb_handle_30_t>(cb));
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
private:
|
|
// this function should be in sync with GoldfishMapper::isSupportedImpl
|
|
Error3 allocateImpl(const hidl_vec<uint32_t>& rawDescriptor,
|
|
uint32_t count,
|
|
uint32_t* pStride,
|
|
std::vector<cb_handle_30_t*>* cbs) {
|
|
BufferDescriptorInfo descriptor;
|
|
if (!decodeBufferDescriptorInfo(rawDescriptor, &descriptor)) {
|
|
RETURN_ERROR(Error3::BAD_DESCRIPTOR);
|
|
}
|
|
|
|
if (!descriptor.width) { RETURN_ERROR(Error3::UNSUPPORTED); }
|
|
if (!descriptor.height) { RETURN_ERROR(Error3::UNSUPPORTED); }
|
|
if (descriptor.layerCount != 1) { RETURN_ERROR(Error3::UNSUPPORTED); }
|
|
|
|
const uint32_t usage = descriptor.usage;
|
|
const bool usageSwWrite = usage & BufferUsage::CPU_WRITE_MASK;
|
|
const bool usageSwRead = usage & BufferUsage::CPU_READ_MASK;
|
|
const bool usageHwCamWrite = usage & BufferUsage::CAMERA_OUTPUT;
|
|
const bool usageHwCamRead = usage & BufferUsage::CAMERA_INPUT;
|
|
|
|
int bpp = 1;
|
|
int glFormat = 0;
|
|
int glType = 0;
|
|
int align = 1;
|
|
bool yuv_format = false;
|
|
EmulatorFrameworkFormat emulatorFrameworkFormat =
|
|
EmulatorFrameworkFormat::GL_COMPATIBLE;
|
|
|
|
PixelFormat format;
|
|
Error3 e = getBufferFormat(descriptor.format, usage, &format);
|
|
if (e != Error3::NONE) {
|
|
ALOGE("%s:%d Unsupported format: frameworkFormat=%d, usage=%x",
|
|
__func__, __LINE__, descriptor.format, usage);
|
|
return e;
|
|
}
|
|
|
|
switch (format) {
|
|
case PixelFormat::RGBA_8888:
|
|
case PixelFormat::RGBX_8888:
|
|
case PixelFormat::BGRA_8888:
|
|
bpp = 4;
|
|
glFormat = GL_RGBA;
|
|
glType = GL_UNSIGNED_BYTE;
|
|
break;
|
|
|
|
case PixelFormat::RGB_888:
|
|
if (usage & (BufferUsage::GPU_TEXTURE |
|
|
BufferUsage::GPU_RENDER_TARGET |
|
|
BufferUsage::COMPOSER_OVERLAY |
|
|
BufferUsage::COMPOSER_CLIENT_TARGET)) {
|
|
RETURN_ERROR(Error3::UNSUPPORTED);
|
|
} else {
|
|
bpp = 3;
|
|
glFormat = GL_RGB;
|
|
glType = GL_UNSIGNED_BYTE;
|
|
}
|
|
break;
|
|
|
|
case PixelFormat::RGB_565:
|
|
bpp = 2;
|
|
glFormat = GL_RGB565;
|
|
glType = GL_UNSIGNED_SHORT_5_6_5;
|
|
break;
|
|
|
|
case PixelFormat::RGBA_FP16:
|
|
bpp = 8;
|
|
glFormat = GL_RGBA16F;
|
|
glType = GL_HALF_FLOAT;
|
|
break;
|
|
|
|
case PixelFormat::RGBA_1010102:
|
|
bpp = 4;
|
|
glFormat = GL_RGB10_A2;
|
|
glType = GL_UNSIGNED_INT_2_10_10_10_REV;
|
|
break;
|
|
|
|
case PixelFormat::RAW16:
|
|
case PixelFormat::Y16:
|
|
bpp = 2;
|
|
align = 16 * bpp;
|
|
if (!((usageSwRead || usageHwCamRead) && (usageSwWrite || usageHwCamWrite))) {
|
|
// Raw sensor data or Y16 only goes between camera and CPU
|
|
RETURN_ERROR(Error3::UNSUPPORTED);
|
|
}
|
|
// Not expecting to actually create any GL surfaces for this
|
|
glFormat = GL_LUMINANCE;
|
|
glType = GL_UNSIGNED_SHORT;
|
|
break;
|
|
|
|
case PixelFormat::BLOB:
|
|
if (!usageSwRead) {
|
|
// Blob data cannot be used by HW other than camera emulator
|
|
// But there is a CTS test trying to have access to it
|
|
// BUG: https://buganizer.corp.google.com/issues/37719518
|
|
RETURN_ERROR(Error3::UNSUPPORTED);
|
|
}
|
|
// Not expecting to actually create any GL surfaces for this
|
|
glFormat = GL_LUMINANCE;
|
|
glType = GL_UNSIGNED_BYTE;
|
|
break;
|
|
|
|
case PixelFormat::YCRCB_420_SP:
|
|
yuv_format = true;
|
|
// Not expecting to actually create any GL surfaces for this
|
|
break;
|
|
|
|
case PixelFormat::YV12:
|
|
align = 16;
|
|
yuv_format = true;
|
|
// We are going to use RGB8888 on the host for Vulkan
|
|
glFormat = GL_RGBA;
|
|
glType = GL_UNSIGNED_BYTE;
|
|
emulatorFrameworkFormat = EmulatorFrameworkFormat::YV12;
|
|
break;
|
|
|
|
case PixelFormat::YCBCR_420_888:
|
|
yuv_format = true;
|
|
// We are going to use RGBA 8888 on the host
|
|
glFormat = GL_RGBA;
|
|
glType = GL_UNSIGNED_BYTE;
|
|
emulatorFrameworkFormat = EmulatorFrameworkFormat::YUV_420_888;
|
|
break;
|
|
|
|
default:
|
|
if (static_cast<android::hardware::graphics::common::V1_1::PixelFormat>(format) ==
|
|
android::hardware::graphics::common::V1_1::PixelFormat::YCBCR_P010) {
|
|
yuv_format = true;
|
|
glFormat = GL_RGBA;
|
|
glType = GL_UNSIGNED_BYTE;
|
|
bpp = 2;
|
|
break;
|
|
}
|
|
|
|
ALOGE("%s:%d Unsupported format: format=%d, frameworkFormat=%d, usage=%x",
|
|
__func__, __LINE__, format, descriptor.format, usage);
|
|
RETURN_ERROR(Error3::UNSUPPORTED);
|
|
}
|
|
|
|
const size_t align1 = align - 1;
|
|
const uint32_t width = descriptor.width;
|
|
const uint32_t height = descriptor.height;
|
|
uint32_t stride;
|
|
size_t bufferSize;
|
|
|
|
if (yuv_format) {
|
|
const size_t yStride = (width * bpp + align1) & ~align1;
|
|
const size_t uvStride = (yStride / 2 + align1) & ~align1;
|
|
const size_t uvHeight = height / 2;
|
|
bufferSize = yStride * height + 2 * (uvHeight * uvStride);
|
|
stride = yStride / bpp;
|
|
} else {
|
|
const size_t bpr = (width * bpp + align1) & ~align1;
|
|
bufferSize = bpr * height;
|
|
stride = bpr / bpp;
|
|
}
|
|
|
|
*pStride = stride;
|
|
|
|
return allocateImpl2(usage,
|
|
width, height,
|
|
format, emulatorFrameworkFormat,
|
|
glFormat, glType,
|
|
bufferSize,
|
|
bpp, stride,
|
|
count, cbs);
|
|
}
|
|
|
|
Error3 allocateImpl2(const uint32_t usage,
|
|
const uint32_t width, const uint32_t height,
|
|
const PixelFormat format,
|
|
const EmulatorFrameworkFormat emulatorFrameworkFormat,
|
|
const int glFormat, const int glType,
|
|
const size_t bufferSize,
|
|
const uint32_t bytesPerPixel,
|
|
const uint32_t stride,
|
|
const uint32_t count,
|
|
std::vector<cb_handle_30_t*>* cbs) {
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
cb_handle_30_t* cb;
|
|
Error3 e = allocateCb(usage,
|
|
width, height,
|
|
format, emulatorFrameworkFormat,
|
|
glFormat, glType,
|
|
bufferSize,
|
|
bytesPerPixel, stride,
|
|
&cb);
|
|
if (e == Error3::NONE) {
|
|
cbs->push_back(cb);
|
|
} else {
|
|
return e;
|
|
}
|
|
}
|
|
|
|
RETURN(Error3::NONE);
|
|
}
|
|
|
|
// see GoldfishMapper::encodeBufferDescriptorInfo
|
|
static bool decodeBufferDescriptorInfo(const hidl_vec<uint32_t>& raw,
|
|
BufferDescriptorInfo* d) {
|
|
if (raw.size() == 5) {
|
|
d->width = raw[0];
|
|
d->height = raw[1];
|
|
d->layerCount = raw[2];
|
|
d->format = static_cast<PixelFormat>(raw[3]);
|
|
d->usage = raw[4];
|
|
|
|
RETURN(true);
|
|
} else {
|
|
RETURN_ERROR(false);
|
|
}
|
|
}
|
|
|
|
static Error3 getBufferFormat(const PixelFormat frameworkFormat,
|
|
const uint32_t usage,
|
|
PixelFormat* format) {
|
|
if (frameworkFormat == PixelFormat::IMPLEMENTATION_DEFINED) {
|
|
if (usage & BufferUsage::CAMERA_OUTPUT) {
|
|
if (usage & BufferUsage::GPU_TEXTURE) {
|
|
// Camera-to-display is RGBA
|
|
*format = PixelFormat::RGBA_8888;
|
|
RETURN(Error3::NONE);
|
|
} else if (usage & BufferUsage::VIDEO_ENCODER) {
|
|
// Camera-to-encoder is NV21
|
|
*format = PixelFormat::YCRCB_420_SP;
|
|
RETURN(Error3::NONE);
|
|
} else {
|
|
// b/189957071
|
|
*format = PixelFormat::YCBCR_420_888;
|
|
RETURN(Error3::NONE);
|
|
}
|
|
}
|
|
RETURN_ERROR(Error3::UNSUPPORTED);
|
|
} else if (static_cast<int>(frameworkFormat) == kOMX_COLOR_FormatYUV420Planar &&
|
|
(usage & BufferUsage::GPU_DATA_BUFFER)) {
|
|
ALOGW("gralloc_alloc: Requested OMX_COLOR_FormatYUV420Planar, given "
|
|
"YCbCr_420_888, taking experimental path. "
|
|
"usage=%x", usage);
|
|
*format = PixelFormat::YCBCR_420_888;
|
|
RETURN(Error3::NONE);
|
|
} else {
|
|
*format = frameworkFormat;
|
|
RETURN(Error3::NONE);
|
|
}
|
|
}
|
|
|
|
static bool needHostCb(const uint32_t usage, const PixelFormat format) {
|
|
if (static_cast<android::hardware::graphics::common::V1_1::PixelFormat>(format) ==
|
|
android::hardware::graphics::common::V1_1::PixelFormat::YCBCR_P010) {
|
|
return false;
|
|
}
|
|
|
|
// b/186585177
|
|
if ((usage & (BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK)) &&
|
|
(0 == (usage & ~(BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK)))) {
|
|
return false;
|
|
}
|
|
|
|
return ((usage & BufferUsage::GPU_DATA_BUFFER)
|
|
|| (format != PixelFormat::BLOB &&
|
|
format != PixelFormat::RAW16 &&
|
|
format != PixelFormat::Y16))
|
|
&& (usage & (BufferUsage::GPU_TEXTURE
|
|
| BufferUsage::GPU_RENDER_TARGET
|
|
| BufferUsage::COMPOSER_OVERLAY
|
|
| BufferUsage::VIDEO_ENCODER
|
|
| BufferUsage::COMPOSER_CLIENT_TARGET
|
|
| BufferUsage::CPU_READ_MASK));
|
|
}
|
|
|
|
Error3 allocateCb(const uint32_t usage,
|
|
const uint32_t width, const uint32_t height,
|
|
const PixelFormat format,
|
|
const EmulatorFrameworkFormat emulatorFrameworkFormat,
|
|
const int glFormat, const int glType,
|
|
const size_t bufferSize,
|
|
const int32_t bytesPerPixel,
|
|
const int32_t stride,
|
|
cb_handle_30_t** cb) {
|
|
const HostConnectionSession conn = getHostConnectionSession();
|
|
ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder();
|
|
CRASH_IF(!rcEnc, "conn.getRcEncoder() failed");
|
|
|
|
GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator(
|
|
rcEnc->featureInfo_const()->hasSharedSlotsHostMemoryAllocator);
|
|
if (!host_memory_allocator.is_opened()) {
|
|
RETURN_ERROR(Error3::NO_RESOURCES);
|
|
}
|
|
|
|
GoldfishAddressSpaceBlock bufferBits;
|
|
if (host_memory_allocator.hostMalloc(&bufferBits, bufferSize)) {
|
|
RETURN_ERROR(Error3::NO_RESOURCES);
|
|
}
|
|
|
|
uint32_t hostHandle = 0;
|
|
QEMU_PIPE_HANDLE hostHandleRefCountFd = QEMU_PIPE_INVALID_HANDLE;
|
|
if (needHostCb(usage, format)) {
|
|
hostHandleRefCountFd = qemu_pipe_open("refcount");
|
|
if (!qemu_pipe_valid(hostHandleRefCountFd)) {
|
|
RETURN_ERROR(Error3::NO_RESOURCES);
|
|
}
|
|
|
|
const GLenum allocFormat =
|
|
(PixelFormat::RGBX_8888 == format) ? GL_RGB : glFormat;
|
|
|
|
hostHandle = rcEnc->rcCreateColorBufferDMA(
|
|
rcEnc,
|
|
width, height,
|
|
allocFormat, static_cast<int>(emulatorFrameworkFormat));
|
|
|
|
if (!hostHandle) {
|
|
qemu_pipe_close(hostHandleRefCountFd);
|
|
RETURN_ERROR(Error3::NO_RESOURCES);
|
|
}
|
|
|
|
if (qemu_pipe_write(hostHandleRefCountFd,
|
|
&hostHandle,
|
|
sizeof(hostHandle)) != sizeof(hostHandle)) {
|
|
rcEnc->rcCloseColorBuffer(rcEnc, hostHandle);
|
|
qemu_pipe_close(hostHandleRefCountFd);
|
|
RETURN_ERROR(Error3::NO_RESOURCES);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<cb_handle_30_t> handle =
|
|
std::make_unique<cb_handle_30_t>(
|
|
host_memory_allocator.release(),
|
|
hostHandleRefCountFd,
|
|
hostHandle,
|
|
usage,
|
|
width,
|
|
height,
|
|
static_cast<int>(format),
|
|
glFormat,
|
|
glType,
|
|
bufferSize,
|
|
bufferBits.guestPtr(),
|
|
bufferBits.size(),
|
|
bufferBits.offset(),
|
|
bytesPerPixel,
|
|
stride);
|
|
|
|
bufferBits.release();
|
|
*cb = handle.release();
|
|
RETURN(Error3::NONE);
|
|
}
|
|
|
|
void freeCb(std::unique_ptr<cb_handle_30_t> cb) {
|
|
// no need to undo .hostMalloc: the kernel will take care of it once the
|
|
// last bufferFd (duped) is closed.
|
|
|
|
if (qemu_pipe_valid(cb->hostHandleRefCountFd)) {
|
|
qemu_pipe_close(cb->hostHandleRefCountFd);
|
|
}
|
|
GoldfishAddressSpaceBlock::memoryUnmap(cb->getBufferPtr(), cb->mmapedSize);
|
|
GoldfishAddressSpaceHostMemoryAllocator::closeHandle(cb->bufferFd);
|
|
}
|
|
|
|
HostConnectionSession getHostConnectionSession() const {
|
|
return HostConnectionSession(m_hostConn.get());
|
|
}
|
|
|
|
std::unique_ptr<HostConnection> m_hostConn;
|
|
};
|
|
|
|
int main(int, char**) {
|
|
using ::android::sp;
|
|
|
|
::android::hardware::configureRpcThreadpool(4, true /* callerWillJoin */);
|
|
|
|
sp<IAllocator3> allocator(new GoldfishAllocator());
|
|
if (allocator->registerAsService() != ::android::NO_ERROR) {
|
|
ALOGE("failed to register graphics IAllocator@3.0 service");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ALOGI("graphics IAllocator@3.0 service is initialized");
|
|
::android::hardware::joinRpcThreadpool();
|
|
|
|
ALOGI("graphics IAllocator@3.0 service is terminating");
|
|
return 0;
|
|
}
|