goldfish-opengl: threadID --> resourceId am: f4ee8074ec
Original change: https://android-review.googlesource.com/c/device/generic/goldfish-opengl/+/2180735
Change-Id: Ic688c68094cb5c663296df5c6f564fab22999ca2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.mk b/Android.mk
index 9a98fb0..8e3be61 100644
--- a/Android.mk
+++ b/Android.mk
@@ -167,6 +167,7 @@
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 28 -o $(IS_AT_LEAST_QPR1) = true && echo isApi29OrHigher),isApi29OrHigher)
# HWC2 enabled after P
include $(GOLDFISH_OPENGL_PATH)/system/hwc2/Android.mk
+ include $(GOLDFISH_OPENGL_PATH)/system/hwc3/Android.mk
# hardware codecs enabled after P
include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/common/Android.mk
include $(GOLDFISH_OPENGL_PATH)/system/codecs/omx/plugin/Android.mk
diff --git a/system/codecs/c2/decoders/avcdec/Android.bp b/system/codecs/c2/decoders/avcdec/Android.bp
index 78ffd82..5d840b0 100644
--- a/system/codecs/c2/decoders/avcdec/Android.bp
+++ b/system/codecs/c2/decoders/avcdec/Android.bp
@@ -15,6 +15,7 @@
],
srcs: ["C2GoldfishAvcDec.cpp",
+ "GoldfishH264Helper.cpp",
"MediaH264Decoder.cpp",
],
@@ -27,4 +28,7 @@
header_libs: [
"libgralloc_cb.ranchu",
],
+
+ static_libs: ["libavcdec",
+ ],
}
diff --git a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
index 6c57922..90b5653 100644
--- a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
+++ b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.cpp
@@ -55,7 +55,7 @@
namespace android {
namespace {
-constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+constexpr size_t kMinInputBufferSize = 6 * 1024 * 1024;
constexpr char COMPONENT_NAME[] = "c2.goldfish.h264.decoder";
constexpr uint32_t kDefaultOutputDelay = 8;
/* avc specification allows for a maximum delay of 16 frames.
@@ -643,7 +643,7 @@
uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
C2MemoryUsage::CPU_WRITE};
- usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
+ usage.expected = (uint64_t)(BufferUsage::VIDEO_DECODER);
// C2MemoryUsage usage = {(unsigned
// int)(BufferUsage::GPU_DATA_BUFFER)};// { C2MemoryUsage::CPU_READ,
// C2MemoryUsage::CPU_WRITE };
@@ -670,31 +670,14 @@
void C2GoldfishAvcDec::checkMode(const std::shared_ptr<C2BlockPool> &pool) {
mWidth = mIntf->width();
mHeight = mIntf->height();
- {
- // now get the block
- constexpr uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
- std::shared_ptr<C2GraphicBlock> block;
- C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
- C2MemoryUsage::CPU_WRITE};
- usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
-
- c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight,
- format, usage, &block);
- if (err != C2_OK) {
- ALOGE("fetchGraphicBlock for Output failed with status %d", err);
- return;
- }
- auto c2Handle = block->handle();
- native_handle_t *grallocHandle =
- UnwrapNativeCodec2GrallocHandle(c2Handle);
- int hostColorBufferId = getColorBufferHandle(grallocHandle);
- if (hostColorBufferId > 0) {
- DDD("decoding to host color buffer");
- mEnableAndroidNativeBuffers = true;
- } else {
- DDD("decoding to guest byte buffer");
- mEnableAndroidNativeBuffers = false;
- }
+ const bool isGraphic = (pool->getAllocatorId() & C2Allocator::GRAPHIC);
+ DDD("buffer id %d", (int)(pool->getAllocatorId()));
+ if (isGraphic) {
+ DDD("decoding to host color buffer");
+ mEnableAndroidNativeBuffers = true;
+ } else {
+ DDD("decoding to guest byte buffer");
+ mEnableAndroidNativeBuffers = false;
}
}
@@ -894,11 +877,6 @@
return;
}
- if (false == mHeaderDecoded) {
- /* Decode header and get dimensions */
- setParams(mStride);
- }
-
DDD("flag is %x", work->input.flags);
if (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) {
hasPicture = false;
@@ -913,9 +891,50 @@
removePts(mPts);
}
+ bool whChanged = false;
+ if (GoldfishH264Helper::isSpsFrame(mInPBuffer, mInPBufferSize)) {
+ mH264Helper.reset(new GoldfishH264Helper(mWidth, mHeight));
+ whChanged = mH264Helper->decodeHeader(mInPBuffer, mInPBufferSize);
+ if (whChanged) {
+ DDD("w changed from old %d to new %d\n", mWidth, mH264Helper->getWidth());
+ DDD("h changed from old %d to new %d\n", mHeight, mH264Helper->getHeight());
+ if (1) {
+ drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+ resetDecoder();
+ resetPlugin();
+ work->workletsProcessed = 0u;
+ }
+ {
+ mWidth = mH264Helper->getWidth();
+ mHeight = mH264Helper->getHeight();
+ C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(size));
+ ensureDecoderState(pool);
+ } else {
+ ALOGE("Cannot set width and height");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ }
+ if (!mContext) {
+ DDD("creating decoder context to host in process work");
+ checkMode(pool);
+ createDecoder();
+ }
+ continue;
+ } // end of whChanged
+ } // end of isSpsFrame
+
uint32_t delay;
GETTIME(&mTimeStart, nullptr);
TIME_DIFF(mTimeEnd, mTimeStart, delay);
+ (void)delay;
//(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
DDD("decoding");
h264_result_t h264Res =
@@ -932,44 +951,10 @@
uint32_t decodeTime;
GETTIME(&mTimeEnd, nullptr);
TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+ (void)decodeTime;
}
- // TODO: handle res change
- if (0) {
- DDD("resolution changed");
- drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
- resetDecoder();
- resetPlugin();
- work->workletsProcessed = 0u;
- /* Decode header and get new dimensions */
- setParams(mStride);
- // (void) ivdec_api_function(mDecHandle, &s_decode_ip,
- // &s_decode_op);
- }
if (mImg.data != nullptr) {
- // check for new width and height
- auto decodedW = mImg.width;
- auto decodedH = mImg.height;
- if (decodedW != mWidth || decodedH != mHeight) {
- mWidth = decodedW;
- mHeight = decodedH;
-
- C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
- std::vector<std::unique_ptr<C2SettingResult>> failures;
- c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
- if (err == OK) {
- work->worklets.front()->output.configUpdate.push_back(
- C2Param::Copy(size));
- ensureDecoderState(pool);
- } else {
- ALOGE("Cannot set width and height");
- mSignalledError = true;
- work->workletsProcessed = 1u;
- work->result = C2_CORRUPTED;
- return;
- }
- }
-
DDD("got data %" PRIu64 " with pts %" PRIu64, getWorkIndex(mImg.pts), mImg.pts);
mHeaderDecoded = true;
copyImageData(mImg);
diff --git a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
index 914a10e..afa27f5 100644
--- a/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
+++ b/system/codecs/c2/decoders/avcdec/C2GoldfishAvcDec.h
@@ -22,6 +22,7 @@
#include <media/stagefright/foundation/ColorUtils.h>
#include "MediaH264Decoder.h"
+#include "GoldfishH264Helper.h"
#include <SimpleC2Component.h>
#include <atomic>
#include <map>
@@ -152,6 +153,8 @@
std::vector<uint8_t> mCsd1;
void decodeHeaderAfterFlush();
+ std::unique_ptr<GoldfishH264Helper> mH264Helper;
+
C2_DO_NOT_COPY(C2GoldfishAvcDec);
};
diff --git a/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.cpp b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.cpp
new file mode 100644
index 0000000..077ef15
--- /dev/null
+++ b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2022 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 "GoldfishH264Helper.h"
+
+#define LOG_TAG "GoldfishH264Helper"
+#include <log/log.h>
+
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+
+#include <Codec2Mapper.h>
+
+#define ivdec_api_function ih264d_api_function
+#define ivdext_create_ip_t ih264d_create_ip_t
+#define ivdext_create_op_t ih264d_create_op_t
+#define ivdext_delete_ip_t ih264d_delete_ip_t
+#define ivdext_delete_op_t ih264d_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t ih264d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t ih264d_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_vui_params_ip_t ih264d_ctl_get_vui_params_ip_t
+#define ivdext_ctl_get_vui_params_op_t ih264d_ctl_get_vui_params_op_t
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define MAX_NUM_CORES 4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES \
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+namespace android {
+
+static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
+ (void) ctxt;
+ return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+ (void) ctxt;
+ free(mem);
+}
+
+
+GoldfishH264Helper::GoldfishH264Helper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); }
+
+GoldfishH264Helper::~GoldfishH264Helper() {
+ destroyDecoder();
+}
+
+void GoldfishH264Helper::createDecoder() {
+ ivdext_create_ip_t s_create_ip = {};
+ ivdext_create_op_t s_create_op = {};
+
+ s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+ s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
+ s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
+ s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
+ s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
+ s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
+ s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
+ s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
+ IV_API_CALL_STATUS_T status =
+ ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__,
+ s_create_op.s_ivd_create_op_t.u4_error_code);
+ return;
+ }
+ mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle;
+ mDecHandle->pv_fxns = (void *)ivdec_api_function;
+ mDecHandle->u4_size = sizeof(iv_obj_t);
+
+ mStride = ALIGN128(mWidth);
+
+ setNumCores();
+}
+
+void GoldfishH264Helper::destroyDecoder() {
+ if (mDecHandle) {
+ ivdext_delete_ip_t s_delete_ip = {};
+ ivdext_delete_op_t s_delete_op = {};
+
+ s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
+ s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
+ s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
+ IV_API_CALL_STATUS_T status =
+ ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__,
+ s_delete_op.s_ivd_delete_op_t.u4_error_code);
+ }
+ mDecHandle = nullptr;
+ }
+}
+
+void GoldfishH264Helper::setNumCores() {
+ ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
+ ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
+
+ s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+ s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+ s_set_num_cores_ip.u4_num_cores = mNumCores;
+ s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(
+ mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op);
+ if (IV_SUCCESS != status) {
+ DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+ }
+}
+
+void GoldfishH264Helper::resetDecoder() {
+ ivd_ctl_reset_ip_t s_reset_ip = {};
+ ivd_ctl_reset_op_t s_reset_op = {};
+
+ s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+ s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+ s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+ IV_API_CALL_STATUS_T status =
+ ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+ }
+ setNumCores();
+}
+
+void GoldfishH264Helper::setParams(size_t stride,
+ IVD_VIDEO_DECODE_MODE_T dec_mode) {
+ ih264d_ctl_set_config_ip_t s_h264d_set_dyn_params_ip = {};
+ ih264d_ctl_set_config_op_t s_h264d_set_dyn_params_op = {};
+ ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
+ &s_h264d_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
+ ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
+ &s_h264d_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
+
+ ps_set_dyn_params_ip->u4_size = sizeof(ih264d_ctl_set_config_ip_t);
+ ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
+ ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ ps_set_dyn_params_ip->u4_disp_wd = (UWORD32) stride;
+ ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
+ ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
+ ps_set_dyn_params_op->u4_size = sizeof(ih264d_ctl_set_config_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+ &s_h264d_set_dyn_params_ip,
+ &s_h264d_set_dyn_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__,
+ ps_set_dyn_params_op->u4_error_code);
+ }
+}
+
+bool GoldfishH264Helper::isSpsFrame(const uint8_t* frame, int inSize) {
+ if (inSize < 5) return false;
+ if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) {
+ const bool forbiddenBitIsInvalid = 0x80 & frame[4];
+ if (forbiddenBitIsInvalid) {
+ return false;
+ }
+ // nalu type is the lower 5 bits
+ uint8_t naluType = 0x1f & frame[4];
+ if (naluType == 7
+ || naluType == 8
+ ) return true;
+ else return false;
+ } else {
+ return false;
+ }
+}
+
+bool GoldfishH264Helper::decodeHeader(const uint8_t *frame, int inSize) {
+ // should we check the header for vps/sps/pps frame ? otherwise
+ // there is no point calling decoder
+ if (!isSpsFrame(frame, inSize)) {
+ DDD("could not find valid vps frame");
+ return false;
+ } else {
+ DDD("found valid vps frame");
+ }
+
+ ih264d_video_decode_ip_t s_h264d_decode_ip = {};
+ ih264d_video_decode_op_t s_h264d_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip = &s_h264d_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op = &s_h264d_decode_op.s_ivd_video_decode_op_t;
+
+ // setup input/output arguments to decoder
+ setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride,
+ 0, // offset
+ inSize, // size
+ 0 // time-stamp, does not matter
+ );
+
+ setParams(mStride, IVD_DECODE_HEADER);
+
+ // now kick off the decoding
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("failed to call decoder function for header\n");
+ ALOGE("error in %s: 0x%x", __func__,
+ ps_decode_op->u4_error_code);
+ }
+
+ if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
+ DDD("resolution changed, reset decoder");
+ resetDecoder();
+ setParams(mStride, IVD_DECODE_HEADER);
+ ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ }
+
+ // get the w/h and update
+ if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
+ DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+ DDD("existing w/h %d %d", mWidth, mHeight);
+ if (ps_decode_op->u4_pic_wd != mWidth || ps_decode_op->u4_pic_ht != mHeight) {
+ mWidth = ps_decode_op->u4_pic_wd;
+ mHeight = ps_decode_op->u4_pic_ht;
+ return true;
+ } else {
+ DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+ }
+ } else {
+ ALOGE("could not decode w/h");
+ }
+
+ // get output delay
+ if (ps_decode_op->i4_reorder_depth >= 0) {
+ if (mOutputDelay != ps_decode_op->i4_reorder_depth) {
+ mOutputDelay = ps_decode_op->i4_reorder_depth;
+ DDD("New Output delay %d ", mOutputDelay);
+ } else {
+ DDD("same Output delay %d ", mOutputDelay);
+ }
+ }
+
+ return false;
+}
+
+bool GoldfishH264Helper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+ ivd_video_decode_op_t *ps_decode_op,
+ const uint8_t *inBuffer,
+ uint32_t displayStride, size_t inOffset,
+ size_t inSize, uint32_t tsMarker) {
+ uint32_t displayHeight = mHeight;
+ size_t lumaSize = displayStride * displayHeight;
+ size_t chromaSize = lumaSize >> 2;
+
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ }
+
+ // force decoder to always decode header and get dimensions,
+ // hope this will be quick and cheap
+ setParams(mStride, IVD_DECODE_HEADER);
+
+ ps_decode_ip->u4_size = sizeof(ih264d_video_decode_ip_t);
+ ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+ if (inBuffer) {
+ ps_decode_ip->u4_ts = tsMarker;
+ ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer) + inOffset;
+ ps_decode_ip->u4_num_Bytes = inSize;
+ } else {
+ ps_decode_ip->u4_ts = 0;
+ ps_decode_ip->pv_stream_buffer = nullptr;
+ ps_decode_ip->u4_num_Bytes = 0;
+ }
+ DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7]
+ );
+ DDD("input bytes %d", ps_decode_ip->u4_num_Bytes);
+
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+ {
+ ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr;
+ ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr;
+ ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr;
+ }
+ ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+ ps_decode_op->u4_size = sizeof(ih264d_video_decode_op_t);
+ ps_decode_op->u4_output_present = 0;
+
+ return true;
+}
+
+} // namespace android
diff --git a/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.h b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.h
new file mode 100644
index 0000000..ec3b384
--- /dev/null
+++ b/system/codecs/c2/decoders/avcdec/GoldfishH264Helper.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef GOLDFISH_H264_HELPER_H_
+#define GOLDFISH_H264_HELPER_H_
+
+#include <inttypes.h>
+#include "ih264_typedefs.h"
+#include "ih264d.h"
+
+
+namespace android {
+
+// this class is just to provide some functions to decode header
+// so that we know w/h of each sps
+class GoldfishH264Helper {
+ public:
+ GoldfishH264Helper(int w, int h);
+ ~GoldfishH264Helper();
+
+ // check whether the frame is sps; typical h264 will have
+ // a frame that is sps/pps together
+ static bool isSpsFrame(const uint8_t* frame, int inSize);
+ public:
+ // return true if decoding finds out w/h changed;
+ // otherwise false
+ bool decodeHeader(const uint8_t *frame, int inSize);
+ int getWidth() const { return mWidth; }
+ int getHeight() const { return mHeight; }
+
+ private:
+ void createDecoder();
+ void destroyDecoder();
+ void resetDecoder();
+ void setNumCores();
+ void setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
+ bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+ ivd_video_decode_op_t *ps_decode_op,
+ const uint8_t *inBuffer, uint32_t displayStride,
+ size_t inOffset, size_t inSize, uint32_t tsMarker);
+
+ private:
+ iv_obj_t *mDecHandle = nullptr;
+ int mWidth = 320;
+ int mHeight = 240;
+ int mNumCores = 1;
+ int mStride = 16;
+ int mOutputDelay = 8; // default
+ IV_COLOR_FORMAT_T mIvColorformat = IV_YUV_420P;
+};
+
+} // namespace android
+#endif
diff --git a/system/codecs/c2/decoders/base/SimpleC2Interface.cpp b/system/codecs/c2/decoders/base/SimpleC2Interface.cpp
index 9f76520..5e18da9 100644
--- a/system/codecs/c2/decoders/base/SimpleC2Interface.cpp
+++ b/system/codecs/c2/decoders/base/SimpleC2Interface.cpp
@@ -29,6 +29,14 @@
/* SimpleInterface */
+static C2R SubscribedParamIndicesSetter(
+ bool mayBlock, C2InterfaceHelper::C2P<C2SubscribedParamIndicesTuning> &me) {
+ (void)mayBlock;
+ (void)me;
+
+ return C2R::Ok();
+}
+
SimpleInterface<void>::BaseParams::BaseParams(
const std::shared_ptr<C2ReflectorHelper> &reflector, C2String name,
C2Component::kind_t kind, C2Component::domain_t domain, C2String mediaType,
@@ -196,9 +204,7 @@
.withDefault(C2SubscribedParamIndicesTuning::AllocShared(0u))
.withFields({C2F(mSubscribedParamIndices, m.values[0]).any(),
C2F(mSubscribedParamIndices, m.values).any()})
- .withSetter(
- Setter<
- C2SubscribedParamIndicesTuning>::NonStrictValuesWithNoDeps)
+ .withSetter(SubscribedParamIndicesSetter)
.build());
/* TODO
diff --git a/system/codecs/c2/decoders/base/include/goldfish_media_utils.h b/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
index b3e26ad..efa8859 100644
--- a/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
+++ b/system/codecs/c2/decoders/base/include/goldfish_media_utils.h
@@ -22,7 +22,8 @@
VP8Codec = 0,
VP9Codec = 1,
H264Codec = 2,
- Max = 3,
+ HevcCodec = 3,
+ Max = 4,
};
enum class MediaOperation : __u8 {
diff --git a/system/codecs/c2/decoders/hevcdec/Android.bp b/system/codecs/c2/decoders/hevcdec/Android.bp
new file mode 100644
index 0000000..f1b9c82
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/Android.bp
@@ -0,0 +1,36 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "device_generic_goldfish-opengl_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["device_generic_goldfish-opengl_license"],
+}
+
+cc_library_shared {
+ name: "libcodec2_goldfish_hevcdec",
+ vendor: true,
+ defaults: [
+ "libcodec2_goldfish-defaults",
+ ],
+
+ srcs: ["C2GoldfishHevcDec.cpp",
+ "GoldfishHevcHelper.cpp",
+ "MediaHevcDecoder.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.mapper@3.0",
+ "libgoldfish_codec2_store",
+ ],
+
+ header_libs: [
+ "libgralloc_cb.ranchu",
+ ],
+
+ static_libs: ["libhevcdec",
+ ],
+
+}
+
diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp
new file mode 100644
index 0000000..7008bd5
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.cpp
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2022 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2GoldfishHevcDec"
+#include <inttypes.h>
+#include <log/log.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2PlatformSupport.h>
+//#include <android/hardware/graphics/common/1.0/types.h>
+
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <hidl/LegacySupport.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+#include <goldfish_codec2/store/GoldfishComponentStore.h>
+#include <gralloc_cb_bp.h>
+
+#include <color_buffer_utils.h>
+
+#include "C2GoldfishHevcDec.h"
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+using ::android::hardware::graphics::common::V1_0::BufferUsage;
+using ::android::hardware::graphics::common::V1_2::PixelFormat;
+
+namespace android {
+
+namespace {
+constexpr size_t kMinInputBufferSize = 6 * 1024 * 1024;
+constexpr char COMPONENT_NAME[] = "c2.goldfish.hevc.decoder";
+constexpr uint32_t kDefaultOutputDelay = 8;
+constexpr uint32_t kMaxOutputDelay = 16;
+} // namespace
+
+class C2GoldfishHevcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+ public:
+ explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+ : SimpleInterface<void>::BaseParams(
+ helper, COMPONENT_NAME, C2Component::KIND_DECODER,
+ C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_HEVC) {
+ noPrivateBuffers(); // TODO: account for our buffers here
+ noInputReferences();
+ noOutputReferences();
+ noInputLatency();
+ noTimeStretch();
+
+ // TODO: Proper support for reorder depth.
+ addParameter(
+ DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+ .withDefault(
+ new C2PortActualDelayTuning::output(kDefaultOutputDelay))
+ .withFields({C2F(mActualOutputDelay, value)
+ .inRange(0, kMaxOutputDelay)})
+ .withSetter(
+ Setter<
+ decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
+ .build());
+
+ // TODO: output latency and reordering
+
+ addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+ .withConstValue(new C2ComponentAttributesSetting(
+ C2Component::ATTRIB_IS_TEMPORAL))
+ .build());
+
+ // coded and output picture size is the same for this codec
+ addParameter(
+ DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+ .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+ .withFields({
+ C2F(mSize, width).inRange(2, 4096, 2),
+ C2F(mSize, height).inRange(2, 4096, 2),
+ })
+ .withSetter(SizeSetter)
+ .build());
+
+ addParameter(DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+ .withDefault(new C2StreamMaxPictureSizeTuning::output(
+ 0u, 320, 240))
+ .withFields({
+ C2F(mSize, width).inRange(2, 4096, 2),
+ C2F(mSize, height).inRange(2, 4096, 2),
+ })
+ .withSetter(MaxPictureSizeSetter, mSize)
+ .build());
+
+ addParameter(
+ DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+ .withDefault(new C2StreamProfileLevelInfo::input(
+ 0u, C2Config::PROFILE_HEVC_MAIN, C2Config::LEVEL_HEVC_MAIN_5_1))
+ .withFields({
+ C2F(mProfileLevel, profile).oneOf({
+ C2Config::PROFILE_HEVC_MAIN,
+ C2Config::PROFILE_HEVC_MAIN_STILL}),
+ C2F(mProfileLevel, level).oneOf({
+ C2Config::LEVEL_HEVC_MAIN_1,
+ C2Config::LEVEL_HEVC_MAIN_2, C2Config::LEVEL_HEVC_MAIN_2_1,
+ C2Config::LEVEL_HEVC_MAIN_3, C2Config::LEVEL_HEVC_MAIN_3_1,
+ C2Config::LEVEL_HEVC_MAIN_4, C2Config::LEVEL_HEVC_MAIN_4_1,
+ C2Config::LEVEL_HEVC_MAIN_5, C2Config::LEVEL_HEVC_MAIN_5_1,
+ C2Config::LEVEL_HEVC_MAIN_5_2, C2Config::LEVEL_HEVC_HIGH_4,
+ C2Config::LEVEL_HEVC_HIGH_4_1, C2Config::LEVEL_HEVC_HIGH_5,
+ C2Config::LEVEL_HEVC_HIGH_5_1, C2Config::LEVEL_HEVC_HIGH_5_2
+ })
+ })
+ .withSetter(ProfileLevelSetter, mSize)
+ .build());
+
+ addParameter(
+ DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+ .withDefault(new C2StreamMaxBufferSizeInfo::input(
+ 0u, kMinInputBufferSize))
+ .withFields({
+ C2F(mMaxInputSize, value).any(),
+ })
+ .calculatedAs(MaxInputSizeSetter, mMaxSize)
+ .build());
+
+ C2ChromaOffsetStruct locations[1] = {
+ C2ChromaOffsetStruct::ITU_YUV_420_0()};
+ std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+ C2StreamColorInfo::output::AllocShared(1u, 0u, 8u /* bitDepth */,
+ C2Color::YUV_420);
+ memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+ defaultColorInfo = C2StreamColorInfo::output::AllocShared(
+ {C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */,
+ C2Color::YUV_420);
+ helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+ addParameter(DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+ .withConstValue(defaultColorInfo)
+ .build());
+
+ addParameter(
+ DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsTuning::output(
+ 0u, C2Color::RANGE_UNSPECIFIED,
+ C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields({C2F(mDefaultColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED,
+ C2Color::RANGE_OTHER),
+ C2F(mDefaultColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mDefaultColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mDefaultColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED,
+ C2Color::MATRIX_OTHER)})
+ .withSetter(DefaultColorAspectsSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::input(
+ 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields({C2F(mCodedColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED,
+ C2Color::RANGE_OTHER),
+ C2F(mCodedColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mCodedColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mCodedColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED,
+ C2Color::MATRIX_OTHER)})
+ .withSetter(CodedColorAspectsSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::output(
+ 0u, C2Color::RANGE_UNSPECIFIED,
+ C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields({C2F(mColorAspects, range)
+ .inRange(C2Color::RANGE_UNSPECIFIED,
+ C2Color::RANGE_OTHER),
+ C2F(mColorAspects, primaries)
+ .inRange(C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::PRIMARIES_OTHER),
+ C2F(mColorAspects, transfer)
+ .inRange(C2Color::TRANSFER_UNSPECIFIED,
+ C2Color::TRANSFER_OTHER),
+ C2F(mColorAspects, matrix)
+ .inRange(C2Color::MATRIX_UNSPECIFIED,
+ C2Color::MATRIX_OTHER)})
+ .withSetter(ColorAspectsSetter, mDefaultColorAspects,
+ mCodedColorAspects)
+ .build());
+
+ // TODO: support more formats?
+ addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+ .withConstValue(new C2StreamPixelFormatInfo::output(
+ 0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+ .build());
+ }
+ static C2R SizeSetter(bool mayBlock,
+ const C2P<C2StreamPictureSizeInfo::output> &oldMe,
+ C2P<C2StreamPictureSizeInfo::output> &me) {
+ (void)mayBlock;
+ DDD("calling sizesetter now %d", oldMe.v.height);
+ DDD("new calling sizesetter now %d", me.v.height);
+
+ C2R res = C2R::Ok();
+ if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+ me.set().width = oldMe.v.width;
+ }
+ if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+ me.set().height = oldMe.v.height;
+ }
+ return res;
+ }
+
+ static C2R
+ MaxPictureSizeSetter(bool mayBlock,
+ C2P<C2StreamMaxPictureSizeTuning::output> &me,
+ const C2P<C2StreamPictureSizeInfo::output> &size) {
+ (void)mayBlock;
+ // TODO: get max width/height from the size's field helpers vs.
+ // hardcoding
+ me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u);
+ me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u);
+ return C2R::Ok();
+ }
+
+ static C2R MaxInputSizeSetter(
+ bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
+ const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
+ (void)mayBlock;
+ // assume compression ratio of 2
+ me.set().value = c2_max((((maxSize.v.width + 63) / 64) *
+ ((maxSize.v.height + 64) / 64) * 3072),
+ kMinInputBufferSize);
+ return C2R::Ok();
+ }
+
+ static C2R
+ ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+ const C2P<C2StreamPictureSizeInfo::output> &size) {
+ (void)mayBlock;
+ (void)size;
+ (void)me; // TODO: validate
+ return C2R::Ok();
+ }
+
+ static C2R
+ DefaultColorAspectsSetter(bool mayBlock,
+ C2P<C2StreamColorAspectsTuning::output> &me) {
+ (void)mayBlock;
+ if (me.v.range > C2Color::RANGE_OTHER) {
+ me.set().range = C2Color::RANGE_OTHER;
+ }
+ if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+ me.set().primaries = C2Color::PRIMARIES_OTHER;
+ }
+ if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+ me.set().transfer = C2Color::TRANSFER_OTHER;
+ }
+ if (me.v.matrix > C2Color::MATRIX_OTHER) {
+ me.set().matrix = C2Color::MATRIX_OTHER;
+ }
+ return C2R::Ok();
+ }
+
+ static C2R
+ CodedColorAspectsSetter(bool mayBlock,
+ C2P<C2StreamColorAspectsInfo::input> &me) {
+ (void)mayBlock;
+ if (me.v.range > C2Color::RANGE_OTHER) {
+ me.set().range = C2Color::RANGE_OTHER;
+ }
+ if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+ me.set().primaries = C2Color::PRIMARIES_OTHER;
+ }
+ if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+ me.set().transfer = C2Color::TRANSFER_OTHER;
+ }
+ if (me.v.matrix > C2Color::MATRIX_OTHER) {
+ me.set().matrix = C2Color::MATRIX_OTHER;
+ }
+ return C2R::Ok();
+ }
+
+ static C2R
+ ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
+ const C2P<C2StreamColorAspectsTuning::output> &def,
+ const C2P<C2StreamColorAspectsInfo::input> &coded) {
+ (void)mayBlock;
+ // take default values for all unspecified fields, and coded values for
+ // specified ones
+ me.set().range =
+ coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
+ me.set().primaries = coded.v.primaries == PRIMARIES_UNSPECIFIED
+ ? def.v.primaries
+ : coded.v.primaries;
+ me.set().transfer = coded.v.transfer == TRANSFER_UNSPECIFIED
+ ? def.v.transfer
+ : coded.v.transfer;
+ me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix
+ : coded.v.matrix;
+ return C2R::Ok();
+ }
+
+ std::shared_ptr<C2StreamColorAspectsInfo::output> getColorAspects_l() {
+ return mColorAspects;
+ }
+
+ int width() const { return mSize->width; }
+
+ int height() const { return mSize->height; }
+
+ private:
+ std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+ std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+ std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+ std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+ std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+ std::shared_ptr<C2StreamColorAspectsInfo::input> mCodedColorAspects;
+ std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+ std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
+ std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+};
+
+static void *ivd_aligned_malloc(void *ctxt, uint32_t alignment, uint32_t size) {
+ (void)ctxt;
+ return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+ (void)ctxt;
+ free(mem);
+}
+
+C2GoldfishHevcDec::C2GoldfishHevcDec(const char *name, c2_node_id_t id,
+ const std::shared_ptr<IntfImpl> &intfImpl)
+ : SimpleC2Component(
+ std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+ mIntf(intfImpl), mOutBufferFlush(nullptr), mWidth(1920), mHeight(1080),
+ mHeaderDecoded(false), mOutIndex(0u) {
+ mWidth = mIntf->width();
+ mHeight = mIntf->height();
+ DDD("creating hevc decoder now w %d h %d", mWidth, mHeight);
+}
+
+C2GoldfishHevcDec::~C2GoldfishHevcDec() { onRelease(); }
+
+c2_status_t C2GoldfishHevcDec::onInit() {
+ status_t err = initDecoder();
+ return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2GoldfishHevcDec::onStop() {
+ if (OK != resetDecoder())
+ return C2_CORRUPTED;
+ resetPlugin();
+ return C2_OK;
+}
+
+void C2GoldfishHevcDec::onReset() { (void)onStop(); }
+
+void C2GoldfishHevcDec::onRelease() {
+ deleteContext();
+ if (mOutBlock) {
+ mOutBlock.reset();
+ }
+}
+
+void C2GoldfishHevcDec::decodeHeaderAfterFlush() {
+ if (mContext && !mCsd0.empty() && !mCsd1.empty()) {
+ mContext->decodeFrame(&(mCsd0[0]), mCsd0.size(), 0);
+ mContext->decodeFrame(&(mCsd1[0]), mCsd1.size(), 0);
+ DDD("resending csd0 and csd1");
+ }
+}
+
+c2_status_t C2GoldfishHevcDec::onFlush_sm() {
+ if (OK != setFlushMode())
+ return C2_CORRUPTED;
+
+ if (!mContext) {
+ // just ignore if context is not even created
+ return C2_OK;
+ }
+
+ uint32_t bufferSize = mStride * mHeight * 3 / 2;
+ mOutBufferFlush = (uint8_t *)ivd_aligned_malloc(nullptr, 128, bufferSize);
+ if (!mOutBufferFlush) {
+ ALOGE("could not allocate tmp output buffer (for flush) of size %u ",
+ bufferSize);
+ return C2_NO_MEMORY;
+ }
+
+ while (true) {
+ mPts = 0;
+ setDecodeArgs(nullptr, nullptr, 0, 0, 0);
+ mImg = mContext->getImage();
+ if (mImg.data == nullptr) {
+ resetPlugin();
+ break;
+ }
+ }
+
+ if (mOutBufferFlush) {
+ ivd_aligned_free(nullptr, mOutBufferFlush);
+ mOutBufferFlush = nullptr;
+ }
+
+ deleteContext();
+ return C2_OK;
+}
+
+status_t C2GoldfishHevcDec::createDecoder() {
+
+ DDD("creating hevc context now w %d h %d", mWidth, mHeight);
+ if (mEnableAndroidNativeBuffers) {
+ mContext.reset(new MediaHevcDecoder(RenderMode::RENDER_BY_HOST_GPU));
+ } else {
+ mContext.reset(new MediaHevcDecoder(RenderMode::RENDER_BY_GUEST_CPU));
+ }
+ mContext->initHevcContext(mWidth, mHeight, mWidth, mHeight,
+ MediaHevcDecoder::PixelFormat::YUV420P);
+
+ return OK;
+}
+
+status_t C2GoldfishHevcDec::setParams(size_t stride) {
+ (void)stride;
+ return OK;
+}
+
+status_t C2GoldfishHevcDec::initDecoder() {
+ // if (OK != createDecoder()) return UNKNOWN_ERROR;
+ mStride = ALIGN2(mWidth);
+ mSignalledError = false;
+ resetPlugin();
+
+ return OK;
+}
+
+bool C2GoldfishHevcDec::setDecodeArgs(C2ReadView *inBuffer,
+ C2GraphicView *outBuffer, size_t inOffset,
+ size_t inSize, uint32_t tsMarker) {
+ uint32_t displayStride = mStride;
+ (void)inBuffer;
+ (void)inOffset;
+ (void)inSize;
+ (void)tsMarker;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
+
+ if (inBuffer) {
+ //= tsMarker;
+ mInPBuffer = const_cast<uint8_t *>(inBuffer->data() + inOffset);
+ mInPBufferSize = inSize;
+ mInTsMarker = tsMarker;
+ insertPts(tsMarker, mPts);
+ }
+
+ // uint32_t displayHeight = mHeight;
+ // size_t lumaSize = displayStride * displayHeight;
+ // size_t chromaSize = lumaSize >> 2;
+
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride))
+ return false;
+ }
+
+ return true;
+}
+
+status_t C2GoldfishHevcDec::setFlushMode() {
+ if (mContext) {
+ mContext->flush();
+ }
+ mHeaderDecoded = false;
+ return OK;
+}
+
+status_t C2GoldfishHevcDec::resetDecoder() {
+ mStride = 0;
+ mSignalledError = false;
+ mHeaderDecoded = false;
+ deleteContext();
+
+ return OK;
+}
+
+void C2GoldfishHevcDec::resetPlugin() {
+ mSignalledOutputEos = false;
+ gettimeofday(&mTimeStart, nullptr);
+ gettimeofday(&mTimeEnd, nullptr);
+}
+
+void C2GoldfishHevcDec::deleteContext() {
+ if (mContext) {
+ mContext->destroyHevcContext();
+ mContext.reset(nullptr);
+ mPts2Index.clear();
+ mOldPts2Index.clear();
+ mIndex2Pts.clear();
+ }
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+ uint32_t flags = 0;
+ if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+ flags |= C2FrameData::FLAG_END_OF_STREAM;
+ DDD("signalling eos");
+ }
+ DDD("fill empty work");
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+}
+
+void C2GoldfishHevcDec::finishWork(uint64_t index,
+ const std::unique_ptr<C2Work> &work) {
+ std::shared_ptr<C2Buffer> buffer =
+ createGraphicBuffer(std::move(mOutBlock), C2Rect(mWidth, mHeight));
+ mOutBlock = nullptr;
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ buffer->setInfo(mIntf->getColorAspects_l());
+ }
+
+ class FillWork {
+ public:
+ FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+ const std::shared_ptr<C2Buffer> &buffer)
+ : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+ ~FillWork() = default;
+
+ void operator()(const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.ordinal = mOrdinal;
+ work->workletsProcessed = 1u;
+ work->result = C2_OK;
+ if (mBuffer) {
+ work->worklets.front()->output.buffers.push_back(mBuffer);
+ }
+ DDD("timestamp = %lld, index = %lld, w/%s buffer",
+ mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+ mBuffer ? "" : "o");
+ }
+
+ private:
+ const uint32_t mFlags;
+ const C2WorkOrdinalStruct mOrdinal;
+ const std::shared_ptr<C2Buffer> mBuffer;
+ };
+
+ auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+ work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+ work->worklets.front()->output.buffers.clear();
+ work->worklets.front()->output.buffers.push_back(buffer);
+ work->worklets.front()->output.ordinal = work->input.ordinal;
+ work->workletsProcessed = 1u;
+ };
+ if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ // TODO: Check if cloneAndSend can be avoided by tracking number of
+ // frames remaining
+ if (eos) {
+ if (buffer) {
+ mOutIndex = index;
+ C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+ DDD("%s %d: cloneAndSend ", __func__, __LINE__);
+ cloneAndSend(
+ mOutIndex, work,
+ FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+ buffer.reset();
+ }
+ } else {
+ DDD("%s %d: fill", __func__, __LINE__);
+ fillWork(work);
+ }
+ } else {
+ DDD("%s %d: finish", __func__, __LINE__);
+ finish(index, fillWork);
+ }
+}
+
+c2_status_t
+C2GoldfishHevcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+ if (mOutBlock && (mOutBlock->width() != ALIGN2(mWidth) ||
+ mOutBlock->height() != mHeight)) {
+ mOutBlock.reset();
+ }
+ if (!mOutBlock) {
+ uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
+ C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
+ C2MemoryUsage::CPU_WRITE};
+ usage.expected = (uint64_t)(BufferUsage::VIDEO_DECODER);
+ // C2MemoryUsage usage = {(unsigned
+ // int)(BufferUsage::GPU_DATA_BUFFER)};// { C2MemoryUsage::CPU_READ,
+ // C2MemoryUsage::CPU_WRITE };
+ c2_status_t err = pool->fetchGraphicBlock(ALIGN2(mWidth), mHeight,
+ format, usage, &mOutBlock);
+ if (err != C2_OK) {
+ ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+ return err;
+ }
+ if (mEnableAndroidNativeBuffers) {
+ auto c2Handle = mOutBlock->handle();
+ native_handle_t *grallocHandle =
+ UnwrapNativeCodec2GrallocHandle(c2Handle);
+ mHostColorBufferId = getColorBufferHandle(grallocHandle);
+ DDD("found handle %d", mHostColorBufferId);
+ }
+ DDD("provided (%dx%d) required (%dx%d)", mOutBlock->width(),
+ mOutBlock->height(), ALIGN2(mWidth), mHeight);
+ }
+
+ return C2_OK;
+}
+
+void C2GoldfishHevcDec::checkMode(const std::shared_ptr<C2BlockPool> &pool) {
+ mWidth = mIntf->width();
+ mHeight = mIntf->height();
+ const bool isGraphic = (pool->getAllocatorId() & C2Allocator::GRAPHIC);
+ DDD("buffer id %d", (int)(pool->getAllocatorId()));
+ if (isGraphic) {
+ DDD("decoding to host color buffer");
+ mEnableAndroidNativeBuffers = true;
+ } else {
+ DDD("decoding to guest byte buffer");
+ mEnableAndroidNativeBuffers = false;
+ }
+}
+
+void C2GoldfishHevcDec::getVuiParams(hevc_image_t &img) {
+
+ VuiColorAspects vuiColorAspects;
+ vuiColorAspects.primaries = img.color_primaries;
+ vuiColorAspects.transfer = img.color_trc;
+ vuiColorAspects.coeffs = img.colorspace;
+ vuiColorAspects.fullRange = img.color_range == 2 ? true : false;
+
+ // convert vui aspects to C2 values if changed
+ if (!(vuiColorAspects == mBitstreamColorAspects)) {
+ mBitstreamColorAspects = vuiColorAspects;
+ ColorAspects sfAspects;
+ C2StreamColorAspectsInfo::input codedAspects = {0u};
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ vuiColorAspects.primaries, vuiColorAspects.transfer,
+ vuiColorAspects.coeffs, vuiColorAspects.fullRange, sfAspects);
+ if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+ codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+ codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+ codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+ }
+ if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+ codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ (void)mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+ }
+}
+
+void C2GoldfishHevcDec::copyImageData(hevc_image_t &img) {
+ getVuiParams(img);
+ if (mEnableAndroidNativeBuffers)
+ return;
+
+ auto writeView = mOutBlock->map().get();
+ if (writeView.error()) {
+ ALOGE("graphic view map failed %d", writeView.error());
+ return;
+ }
+ size_t dstYStride = writeView.layout().planes[C2PlanarLayout::PLANE_Y].rowInc;
+ size_t dstUVStride = writeView.layout().planes[C2PlanarLayout::PLANE_U].rowInc;
+
+ uint8_t *pYBuffer = const_cast<uint8_t *>(writeView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t *pUBuffer = const_cast<uint8_t *>(writeView.data()[C2PlanarLayout::PLANE_U]);
+ uint8_t *pVBuffer = const_cast<uint8_t *>(writeView.data()[C2PlanarLayout::PLANE_V]);
+
+ for (int i = 0; i < mHeight; ++i) {
+ memcpy(pYBuffer + i * dstYStride, img.data + i * mWidth, mWidth);
+ }
+ for (int i = 0; i < mHeight / 2; ++i) {
+ memcpy(pUBuffer + i * dstUVStride,
+ img.data + mWidth * mHeight + i * mWidth / 2, mWidth / 2);
+ }
+ for (int i = 0; i < mHeight / 2; ++i) {
+ memcpy(pVBuffer + i * dstUVStride,
+ img.data + mWidth * mHeight * 5 / 4 + i * mWidth / 2,
+ mWidth / 2);
+ }
+}
+
+uint64_t C2GoldfishHevcDec::getWorkIndex(uint64_t pts) {
+ if (!mOldPts2Index.empty()) {
+ auto iter = mOldPts2Index.find(pts);
+ if (iter != mOldPts2Index.end()) {
+ auto index = iter->second;
+ DDD("found index %d for pts %" PRIu64, (int)index, pts);
+ return index;
+ }
+ }
+ auto iter = mPts2Index.find(pts);
+ if (iter != mPts2Index.end()) {
+ auto index = iter->second;
+ DDD("found index %d for pts %" PRIu64, (int)index, pts);
+ return index;
+ }
+ DDD("not found index for pts %" PRIu64, pts);
+ return 0;
+}
+
+void C2GoldfishHevcDec::insertPts(uint32_t work_index, uint64_t pts) {
+ auto iter = mPts2Index.find(pts);
+ if (iter != mPts2Index.end()) {
+ // we have a collision here:
+ // apparently, older session is not done yet,
+ // lets save them
+ DDD("inserted to old pts %" PRIu64 " with index %d", pts, (int)iter->second);
+ mOldPts2Index[iter->first] = iter->second;
+ }
+ DDD("inserted pts %" PRIu64 " with index %d", pts, (int)work_index);
+ mIndex2Pts[work_index] = pts;
+ mPts2Index[pts] = work_index;
+}
+
+void C2GoldfishHevcDec::removePts(uint64_t pts) {
+ bool found = false;
+ uint64_t index = 0;
+ // note: check old pts first to see
+ // if we have some left over, check them
+ if (!mOldPts2Index.empty()) {
+ auto iter = mOldPts2Index.find(pts);
+ if (iter != mOldPts2Index.end()) {
+ mOldPts2Index.erase(iter);
+ index = iter->second;
+ found = true;
+ }
+ } else {
+ auto iter = mPts2Index.find(pts);
+ if (iter != mPts2Index.end()) {
+ mPts2Index.erase(iter);
+ index = iter->second;
+ found = true;
+ }
+ }
+
+ if (!found) return;
+
+ auto iter2 = mIndex2Pts.find(index);
+ if (iter2 == mIndex2Pts.end()) return;
+ mIndex2Pts.erase(iter2);
+}
+
+// TODO: can overall error checking be improved?
+// TODO: allow configuration of color format and usage for graphic buffers
+// instead
+// of hard coding them to HAL_PIXEL_FORMAT_YV12
+// TODO: pass coloraspects information to surface
+// TODO: test support for dynamic change in resolution
+// TODO: verify if the decoder sent back all frames
+void C2GoldfishHevcDec::process(const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ // Initialize output work
+ work->result = C2_OK;
+ work->workletsProcessed = 0u;
+ work->worklets.front()->output.flags = work->input.flags;
+ if (mSignalledError || mSignalledOutputEos) {
+ work->result = C2_BAD_VALUE;
+ return;
+ }
+
+ DDD("process work");
+ if (!mContext) {
+ DDD("creating decoder context to host in process work");
+ checkMode(pool);
+ createDecoder();
+ decodeHeaderAfterFlush();
+ }
+
+ size_t inOffset = 0u;
+ size_t inSize = 0u;
+ uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+ mPts = work->input.ordinal.timestamp.peeku();
+ C2ReadView rView = mDummyReadView;
+ if (!work->input.buffers.empty()) {
+ rView =
+ work->input.buffers[0]->data().linearBlocks().front().map().get();
+ inSize = rView.capacity();
+ if (inSize && rView.error()) {
+ ALOGE("read view map failed %d", rView.error());
+ work->result = rView.error();
+ return;
+ }
+ }
+ bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+ bool hasPicture = (inSize > 0);
+
+ DDD("in buffer attr. size %zu timestamp %d frameindex %d, flags %x", inSize,
+ (int)work->input.ordinal.timestamp.peeku(),
+ (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+ size_t inPos = 0;
+ while (inPos < inSize) {
+ if (C2_OK != ensureDecoderState(pool)) {
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ {
+ // C2GraphicView wView;// = mOutBlock->map().get();
+ // if (wView.error()) {
+ // ALOGE("graphic view map failed %d", wView.error());
+ // work->result = wView.error();
+ // return;
+ //}
+ if (!setDecodeArgs(&rView, nullptr, inOffset + inPos,
+ inSize - inPos, workIndex)) {
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+
+ DDD("flag is %x", work->input.flags);
+ if (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) {
+ hasPicture = false;
+ if (mCsd0.empty()) {
+ mCsd0.assign(mInPBuffer, mInPBuffer + mInPBufferSize);
+ DDD("assign to csd0 with %d bytpes", mInPBufferSize);
+ } else if (mCsd1.empty()) {
+ mCsd1.assign(mInPBuffer, mInPBuffer + mInPBufferSize);
+ DDD("assign to csd1 with %d bytpes", mInPBufferSize);
+ }
+ // this is not really a valid pts from config
+ removePts(mPts);
+ }
+
+ bool whChanged = false;
+ if (GoldfishHevcHelper::isVpsFrame(mInPBuffer, mInPBufferSize)) {
+ mHevcHelper.reset(new GoldfishHevcHelper(mWidth, mHeight));
+ whChanged = mHevcHelper->decodeHeader(mInPBuffer, mInPBufferSize);
+ if (whChanged) {
+ DDD("w changed from old %d to new %d\n", mWidth, mHevcHelper->getWidth());
+ DDD("h changed from old %d to new %d\n", mHeight, mHevcHelper->getHeight());
+ if (1) {
+ drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+ resetDecoder();
+ resetPlugin();
+ work->workletsProcessed = 0u;
+ }
+ {
+ mWidth = mHevcHelper->getWidth();
+ mHeight = mHevcHelper->getHeight();
+ C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(size));
+ ensureDecoderState(pool);
+ } else {
+ ALOGE("Cannot set width and height");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ }
+ if (!mContext) {
+ DDD("creating decoder context to host in process work");
+ checkMode(pool);
+ createDecoder();
+ }
+ continue;//return;
+ } // end of whChanged
+ } // end of isVpsFrame
+
+ uint32_t delay;
+ GETTIME(&mTimeStart, nullptr);
+ TIME_DIFF(mTimeEnd, mTimeStart, delay);
+ (void)delay;
+ //(void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+ DDD("decoding");
+ hevc_result_t hevcRes =
+ mContext->decodeFrame(mInPBuffer, mInPBufferSize, mIndex2Pts[mInTsMarker]);
+ mConsumedBytes = hevcRes.bytesProcessed;
+ DDD("decoding consumed %d", (int)mConsumedBytes);
+
+ if (mHostColorBufferId > 0) {
+ mImg = mContext->renderOnHostAndReturnImageMetadata(
+ mHostColorBufferId);
+ } else {
+ mImg = mContext->getImage();
+ }
+ uint32_t decodeTime;
+ GETTIME(&mTimeEnd, nullptr);
+ TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+ (void)decodeTime;
+ }
+ if (mImg.data != nullptr) {
+ DDD("got data %" PRIu64 " with pts %" PRIu64, getWorkIndex(mImg.pts), mImg.pts);
+ mHeaderDecoded = true;
+ copyImageData(mImg);
+ finishWork(getWorkIndex(mImg.pts), work);
+ removePts(mImg.pts);
+ } else {
+ work->workletsProcessed = 0u;
+ }
+
+ inPos += mConsumedBytes;
+ }
+ if (eos) {
+ DDD("drain because of eos");
+ drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+ mSignalledOutputEos = true;
+ } else if (!hasPicture) {
+ DDD("no picture, fill empty work");
+ fillEmptyWork(work);
+ }
+
+ work->input.buffers.clear();
+}
+
+c2_status_t
+C2GoldfishHevcDec::drainInternal(uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work) {
+ if (drainMode == NO_DRAIN) {
+ ALOGW("drain with NO_DRAIN: no-op");
+ return C2_OK;
+ }
+ if (drainMode == DRAIN_CHAIN) {
+ ALOGW("DRAIN_CHAIN not supported");
+ return C2_OMITTED;
+ }
+
+ if (OK != setFlushMode())
+ return C2_CORRUPTED;
+ while (true) {
+ if (C2_OK != ensureDecoderState(pool)) {
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return C2_CORRUPTED;
+ }
+ /*
+ C2GraphicView wView = mOutBlock->map().get();
+ if (wView.error()) {
+ ALOGE("graphic view map failed %d", wView.error());
+ return C2_CORRUPTED;
+ }
+ if (!setDecodeArgs(nullptr, &wView, 0, 0, 0)) {
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ return C2_CORRUPTED;
+ }
+ */
+
+ if (mHostColorBufferId > 0) {
+ mImg = mContext->renderOnHostAndReturnImageMetadata(
+ mHostColorBufferId);
+ } else {
+ mImg = mContext->getImage();
+ }
+
+ // TODO: maybe keep rendering to screen
+ // mImg = mContext->getImage();
+ if (mImg.data != nullptr) {
+ DDD("got data in drain mode %" PRIu64 " with pts %" PRIu64, getWorkIndex(mImg.pts), mImg.pts);
+ copyImageData(mImg);
+ finishWork(getWorkIndex(mImg.pts), work);
+ removePts(mImg.pts);
+ } else {
+ fillEmptyWork(work);
+ break;
+ }
+ }
+
+ return C2_OK;
+}
+
+c2_status_t C2GoldfishHevcDec::drain(uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) {
+ DDD("drainInternal because of drain");
+ return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2GoldfishHevcDecFactory : public C2ComponentFactory {
+ public:
+ C2GoldfishHevcDecFactory()
+ : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+ GoldfishComponentStore::Create()->getParamReflector())) {}
+
+ virtual c2_status_t
+ createComponent(c2_node_id_t id,
+ std::shared_ptr<C2Component> *const component,
+ std::function<void(C2Component *)> deleter) override {
+ *component = std::shared_ptr<C2Component>(
+ new C2GoldfishHevcDec(
+ COMPONENT_NAME, id,
+ std::make_shared<C2GoldfishHevcDec::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual c2_status_t createInterface(
+ c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *const interface,
+ std::function<void(C2ComponentInterface *)> deleter) override {
+ *interface = std::shared_ptr<C2ComponentInterface>(
+ new SimpleInterface<C2GoldfishHevcDec::IntfImpl>(
+ COMPONENT_NAME, id,
+ std::make_shared<C2GoldfishHevcDec::IntfImpl>(mHelper)),
+ deleter);
+ return C2_OK;
+ }
+
+ virtual ~C2GoldfishHevcDecFactory() override = default;
+
+ private:
+ std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+} // namespace android
+
+extern "C" ::C2ComponentFactory *CreateCodec2Factory() {
+ DDD("in %s", __func__);
+ return new ::android::C2GoldfishHevcDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory *factory) {
+ DDD("in %s", __func__);
+ delete factory;
+}
diff --git a/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h
new file mode 100644
index 0000000..fe080cf
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/C2GoldfishHevcDec.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_C2_SOFT_HEVC_DEC_H_
+#define ANDROID_C2_SOFT_HEVC_DEC_H_
+
+#include <sys/time.h>
+
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include "MediaHevcDecoder.h"
+#include "GoldfishHevcHelper.h"
+#include <SimpleC2Component.h>
+#include <atomic>
+#include <map>
+
+namespace android {
+
+#define ALIGN2(x) ((((x) + 1) >> 1) << 1)
+#define ALIGN8(x) ((((x) + 7) >> 3) << 3)
+#define ALIGN16(x) ((((x) + 15) >> 4) << 4)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
+#define MAX_NUM_CORES 4
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define GETTIME(a, b) gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff) \
+ diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+ ((end).tv_usec - (start).tv_usec);
+
+class C2GoldfishHevcDec : public SimpleC2Component {
+ public:
+ class IntfImpl;
+ C2GoldfishHevcDec(const char *name, c2_node_id_t id,
+ const std::shared_ptr<IntfImpl> &intfImpl);
+ virtual ~C2GoldfishHevcDec();
+
+ // From SimpleC2Component
+ c2_status_t onInit() override;
+ c2_status_t onStop() override;
+ void onReset() override;
+ void onRelease() override;
+ c2_status_t onFlush_sm() override;
+ void process(const std::unique_ptr<C2Work> &work,
+ const std::shared_ptr<C2BlockPool> &pool) override;
+ c2_status_t drain(uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool) override;
+
+ private:
+ std::unique_ptr<MediaHevcDecoder> mContext;
+ bool mEnableAndroidNativeBuffers{true};
+
+ void checkMode(const std::shared_ptr<C2BlockPool> &pool);
+ // status_t createDecoder();
+ status_t createDecoder();
+ status_t setParams(size_t stride);
+ status_t initDecoder();
+ bool setDecodeArgs(C2ReadView *inBuffer, C2GraphicView *outBuffer,
+ size_t inOffset, size_t inSize, uint32_t tsMarker);
+ c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+ void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+ status_t setFlushMode();
+ c2_status_t drainInternal(uint32_t drainMode,
+ const std::shared_ptr<C2BlockPool> &pool,
+ const std::unique_ptr<C2Work> &work);
+ status_t resetDecoder();
+ void resetPlugin();
+ void deleteContext();
+
+ std::shared_ptr<IntfImpl> mIntf;
+
+ void removePts(uint64_t pts);
+ void insertPts(uint32_t work_index, uint64_t pts);
+ uint64_t getWorkIndex(uint64_t pts);
+
+ // there are same pts matching to different work indices
+ // this happen during csd0/csd1 switching
+ std::map<uint64_t, uint64_t> mOldPts2Index;
+ std::map<uint64_t, uint64_t> mPts2Index;
+ std::map<uint64_t, uint64_t> mIndex2Pts;
+ uint64_t mPts {0};
+
+ // TODO:This is not the right place for this enum. These should
+ // be part of c2-vndk so that they can be accessed by all video plugins
+ // until then, make them feel at home
+ enum {
+ kNotSupported,
+ kPreferBitstream,
+ kPreferContainer,
+ };
+
+ std::shared_ptr<C2GraphicBlock> mOutBlock;
+ uint8_t *mOutBufferFlush;
+
+ int mHostColorBufferId{-1};
+
+ void getVuiParams(hevc_image_t &img);
+ void copyImageData(hevc_image_t &img);
+
+ hevc_image_t mImg{};
+ uint32_t mConsumedBytes{0};
+ uint8_t *mInPBuffer{nullptr};
+ uint32_t mInPBufferSize;
+ uint32_t mInTsMarker;
+
+ // size_t mNumCores;
+ // uint32_t mOutputDelay;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mStride;
+ bool mSignalledOutputEos;
+ bool mSignalledError;
+ bool mHeaderDecoded;
+ std::atomic_uint64_t mOutIndex;
+ // Color aspects. These are ISO values and are meant to detect changes in
+ // aspects to avoid converting them to C2 values for each frame
+ struct VuiColorAspects {
+ uint8_t primaries;
+ uint8_t transfer;
+ uint8_t coeffs;
+ uint8_t fullRange;
+
+ // default color aspects
+ VuiColorAspects()
+ : primaries(2), transfer(2), coeffs(2), fullRange(0) {}
+
+ bool operator==(const VuiColorAspects &o) {
+ return primaries == o.primaries && transfer == o.transfer &&
+ coeffs == o.coeffs && fullRange == o.fullRange;
+ }
+ } mBitstreamColorAspects;
+
+ // profile
+ struct timeval mTimeStart;
+ struct timeval mTimeEnd;
+#ifdef FILE_DUMP_ENABLE
+ char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+ std::vector<uint8_t> mCsd0;
+ std::vector<uint8_t> mCsd1;
+ void decodeHeaderAfterFlush();
+
+ std::unique_ptr<GoldfishHevcHelper> mHevcHelper;
+
+ C2_DO_NOT_COPY(C2GoldfishHevcDec);
+};
+
+} // namespace android
+
+#endif // ANDROID_C2_SOFT_HEVC_DEC_H_
diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp
new file mode 100644
index 0000000..1b93a0d
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2022 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 "GoldfishHevcHelper.h"
+
+#define LOG_TAG "GoldfishHevcHelper"
+#include <log/log.h>
+
+#include "ihevc_typedefs.h"
+#include "ihevcd_cxa.h"
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+
+#include <Codec2Mapper.h>
+
+#define ivdec_api_function ihevcd_cxa_api_function
+#define ivdext_create_ip_t ihevcd_cxa_create_ip_t
+#define ivdext_create_op_t ihevcd_cxa_create_op_t
+#define ivdext_delete_ip_t ihevcd_cxa_delete_ip_t
+#define ivdext_delete_op_t ihevcd_cxa_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t
+#define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define MAX_NUM_CORES 4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES \
+ (IVD_CONTROL_API_COMMAND_TYPE_T) IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+namespace android {
+
+static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
+ (void) ctxt;
+ return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+ (void) ctxt;
+ free(mem);
+}
+
+
+GoldfishHevcHelper::GoldfishHevcHelper(int w, int h):mWidth(w),mHeight(h) { createDecoder(); }
+
+GoldfishHevcHelper::~GoldfishHevcHelper() {
+ destroyDecoder();
+}
+
+void GoldfishHevcHelper::createDecoder() {
+ ivdext_create_ip_t s_create_ip = {};
+ ivdext_create_op_t s_create_op = {};
+
+ s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+ s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
+ s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
+ s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
+ s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
+ s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
+ s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
+ s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
+ IV_API_CALL_STATUS_T status =
+ ivdec_api_function(mDecHandle, &s_create_ip, &s_create_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__,
+ s_create_op.s_ivd_create_op_t.u4_error_code);
+ return;
+ }
+ mDecHandle = (iv_obj_t *)s_create_op.s_ivd_create_op_t.pv_handle;
+ mDecHandle->pv_fxns = (void *)ivdec_api_function;
+ mDecHandle->u4_size = sizeof(iv_obj_t);
+
+ mStride = ALIGN128(mWidth);
+
+ setNumCores();
+}
+
+void GoldfishHevcHelper::destroyDecoder() {
+ if (mDecHandle) {
+ ivdext_delete_ip_t s_delete_ip = {};
+ ivdext_delete_op_t s_delete_op = {};
+
+ s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
+ s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
+ s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
+ IV_API_CALL_STATUS_T status =
+ ivdec_api_function(mDecHandle, &s_delete_ip, &s_delete_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__,
+ s_delete_op.s_ivd_delete_op_t.u4_error_code);
+ }
+ mDecHandle = nullptr;
+ }
+}
+
+void GoldfishHevcHelper::setNumCores() {
+ ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip = {};
+ ivdext_ctl_set_num_cores_op_t s_set_num_cores_op = {};
+
+ s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+ s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+ s_set_num_cores_ip.u4_num_cores = mNumCores;
+ s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(
+ mDecHandle, &s_set_num_cores_ip, &s_set_num_cores_op);
+ if (IV_SUCCESS != status) {
+ DDD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+ }
+}
+
+void GoldfishHevcHelper::resetDecoder() {
+ ivd_ctl_reset_ip_t s_reset_ip = {};
+ ivd_ctl_reset_op_t s_reset_op = {};
+
+ s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+ s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+ s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+ IV_API_CALL_STATUS_T status =
+ ivdec_api_function(mDecHandle, &s_reset_ip, &s_reset_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+ }
+ setNumCores();
+}
+
+void GoldfishHevcHelper::setParams(size_t stride,
+ IVD_VIDEO_DECODE_MODE_T dec_mode) {
+ ihevcd_cxa_ctl_set_config_ip_t s_hevcd_set_dyn_params_ip = {};
+ ihevcd_cxa_ctl_set_config_op_t s_hevcd_set_dyn_params_op = {};
+ ivd_ctl_set_config_ip_t *ps_set_dyn_params_ip =
+ &s_hevcd_set_dyn_params_ip.s_ivd_ctl_set_config_ip_t;
+ ivd_ctl_set_config_op_t *ps_set_dyn_params_op =
+ &s_hevcd_set_dyn_params_op.s_ivd_ctl_set_config_op_t;
+
+ ps_set_dyn_params_ip->u4_size = sizeof(ihevcd_cxa_ctl_set_config_ip_t);
+ ps_set_dyn_params_ip->e_cmd = IVD_CMD_VIDEO_CTL;
+ ps_set_dyn_params_ip->e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ ps_set_dyn_params_ip->u4_disp_wd = (UWORD32)stride;
+ ps_set_dyn_params_ip->e_frm_skip_mode = IVD_SKIP_NONE;
+ ps_set_dyn_params_ip->e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ ps_set_dyn_params_ip->e_vid_dec_mode = dec_mode;
+ ps_set_dyn_params_op->u4_size = sizeof(ihevcd_cxa_ctl_set_config_op_t);
+ IV_API_CALL_STATUS_T status = ivdec_api_function(
+ mDecHandle, ps_set_dyn_params_ip, ps_set_dyn_params_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("error in %s: 0x%x", __func__,
+ ps_set_dyn_params_op->u4_error_code);
+ }
+}
+
+bool GoldfishHevcHelper::isVpsFrame(const uint8_t* frame, int inSize) {
+ if (inSize < 5) return false;
+ if (frame[0] == 0 && frame[1] == 0 && frame[2] == 0 && frame[3] == 1) {
+ const bool forbiddenBitIsInvalid = 0x80 & frame[4];
+ if (forbiddenBitIsInvalid) {
+ return false;
+ }
+ // nalu type is the lower 6 bits after shiftting to right 1 bit
+ uint8_t naluType = 0x3f & (frame[4] >> 1);
+ if (naluType == 32
+ || naluType == 33
+ || naluType == 34
+ ) return true;
+ else return false;
+ } else {
+ return false;
+ }
+}
+
+bool GoldfishHevcHelper::decodeHeader(const uint8_t *frame, int inSize) {
+ // should we check the header for vps/sps/pps frame ? otherwise
+ // there is no point calling decoder
+ if (!isVpsFrame(frame, inSize)) {
+ DDD("could not find valid vps frame");
+ return false;
+ } else {
+ DDD("found valid vps frame");
+ }
+
+ ihevcd_cxa_video_decode_ip_t s_hevcd_decode_ip = {};
+ ihevcd_cxa_video_decode_op_t s_hevcd_decode_op = {};
+ ivd_video_decode_ip_t *ps_decode_ip =
+ &s_hevcd_decode_ip.s_ivd_video_decode_ip_t;
+ ivd_video_decode_op_t *ps_decode_op =
+ &s_hevcd_decode_op.s_ivd_video_decode_op_t;
+
+ // setup input/output arguments to decoder
+ setDecodeArgs(ps_decode_ip, ps_decode_op, frame, mStride,
+ 0, // offset
+ inSize, // size
+ 0 // time-stamp, does not matter
+ );
+
+ setParams(mStride, IVD_DECODE_HEADER);
+
+ // now kick off the decoding
+ IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("failed to call decoder function for header\n");
+ ALOGE("error in %s: 0x%x", __func__,
+ ps_decode_op->u4_error_code);
+ }
+
+ if (IVD_RES_CHANGED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) {
+ DDD("resolution changed, reset decoder");
+ resetDecoder();
+ setParams(mStride, IVD_DECODE_HEADER);
+ ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op);
+ }
+
+ // get the w/h and update
+ if (0 < ps_decode_op->u4_pic_wd && 0 < ps_decode_op->u4_pic_ht) {
+ DDD("success decode w/h %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+ DDD("existing w/h %d %d", mWidth, mHeight);
+ if (ps_decode_op->u4_pic_wd != mWidth || ps_decode_op->u4_pic_ht != mHeight) {
+ mWidth = ps_decode_op->u4_pic_wd;
+ mHeight = ps_decode_op->u4_pic_ht;
+ return true;
+ } else {
+ DDD("success decode w/h, but they are the same %d %d", ps_decode_op->u4_pic_wd , ps_decode_op->u4_pic_ht);
+ }
+ } else {
+ ALOGE("could not decode w/h");
+ }
+
+ // get output delay
+ if (ps_decode_op->i4_reorder_depth >= 0) {
+ if (mOutputDelay != ps_decode_op->i4_reorder_depth) {
+ mOutputDelay = ps_decode_op->i4_reorder_depth;
+ DDD("New Output delay %d ", mOutputDelay);
+ } else {
+ DDD("same Output delay %d ", mOutputDelay);
+ }
+ }
+
+ return false;
+}
+
+bool GoldfishHevcHelper::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+ ivd_video_decode_op_t *ps_decode_op,
+ const uint8_t *inBuffer,
+ uint32_t displayStride, size_t inOffset,
+ size_t inSize, uint32_t tsMarker) {
+ uint32_t displayHeight = mHeight;
+ size_t lumaSize = displayStride * displayHeight;
+ size_t chromaSize = lumaSize >> 2;
+
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ }
+
+ // force decoder to always decode header and get dimensions,
+ // hope this will be quick and cheap
+ setParams(mStride, IVD_DECODE_HEADER);
+
+ ps_decode_ip->u4_size = sizeof(ihevcd_cxa_video_decode_ip_t);
+ ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+ if (inBuffer) {
+ ps_decode_ip->u4_ts = tsMarker;
+ ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer) + inOffset;
+ ps_decode_ip->u4_num_Bytes = inSize;
+ } else {
+ ps_decode_ip->u4_ts = 0;
+ ps_decode_ip->pv_stream_buffer = nullptr;
+ ps_decode_ip->u4_num_Bytes = 0;
+ }
+ DDD("setting pv_stream_buffer 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[0],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[1],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[2],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[3],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[4],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[5],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[6],
+ ((uint8_t*)(ps_decode_ip->pv_stream_buffer))[7]
+ );
+ DDD("input bytes %d", ps_decode_ip->u4_num_Bytes);
+
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+ ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+ {
+ ps_decode_ip->s_out_buffer.pu1_bufs[0] = nullptr;
+ ps_decode_ip->s_out_buffer.pu1_bufs[1] = nullptr;
+ ps_decode_ip->s_out_buffer.pu1_bufs[2] = nullptr;
+ }
+ ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+ ps_decode_op->u4_size = sizeof(ihevcd_cxa_video_decode_op_t);
+ ps_decode_op->u4_output_present = 0;
+
+ return true;
+}
+
+} // namespace android
diff --git a/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h
new file mode 100644
index 0000000..09de830
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/GoldfishHevcHelper.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef GOLDFISH_HEVC_HELPER_H_
+#define GOLDFISH_HEVC_HELPER_H_
+
+#include <inttypes.h>
+#include "ihevc_typedefs.h"
+#include "ihevcd_cxa.h"
+
+
+namespace android {
+
+// this class is just to provide some functions to decode header
+// so that we know w/h of each sps
+class GoldfishHevcHelper {
+ public:
+ GoldfishHevcHelper(int w, int h);
+ ~GoldfishHevcHelper();
+
+ // check whether the frame is vps; typical hevc will have
+ // a frame that is vps/sps/pps together
+ static bool isVpsFrame(const uint8_t* frame, int inSize);
+ public:
+ // return true if decoding finds out w/h changed;
+ // otherwise false
+ bool decodeHeader(const uint8_t *frame, int inSize);
+ int getWidth() const { return mWidth; }
+ int getHeight() const { return mHeight; }
+
+ private:
+ void createDecoder();
+ void destroyDecoder();
+ void resetDecoder();
+ void setNumCores();
+ void setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
+ bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+ ivd_video_decode_op_t *ps_decode_op,
+ const uint8_t *inBuffer, uint32_t displayStride,
+ size_t inOffset, size_t inSize, uint32_t tsMarker);
+
+ private:
+ iv_obj_t *mDecHandle = nullptr;
+ int mWidth = 320;
+ int mHeight = 240;
+ int mNumCores = 1;
+ int mStride = 16;
+ int mOutputDelay = 8; // default
+ IV_COLOR_FORMAT_T mIvColorformat = IV_YUV_420P;
+};
+
+} // namespace android
+#endif
diff --git a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp
new file mode 100644
index 0000000..bb2fbfa
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2022 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 <utils/Log.h>
+
+#define DEBUG 0
+#if DEBUG
+#define DDD(...) ALOGD(__VA_ARGS__)
+#else
+#define DDD(...) ((void)0)
+#endif
+
+#include "MediaHevcDecoder.h"
+#include "goldfish_media_utils.h"
+#include <string.h>
+
+MediaHevcDecoder::MediaHevcDecoder(RenderMode renderMode)
+ : mRenderMode(renderMode) {
+ if (renderMode == RenderMode::RENDER_BY_HOST_GPU) {
+ mVersion = 200;
+ } else if (renderMode == RenderMode::RENDER_BY_GUEST_CPU) {
+ mVersion = 100;
+ }
+}
+
+void MediaHevcDecoder::initHevcContext(unsigned int width, unsigned int height,
+ unsigned int outWidth,
+ unsigned int outHeight,
+ PixelFormat pixFmt) {
+ auto transport = GoldfishMediaTransport::getInstance();
+ if (!mHasAddressSpaceMemory) {
+ int slot = transport->getMemorySlot();
+ if (slot < 0) {
+ ALOGE("ERROR: Failed to initHevcContext: cannot get memory slot");
+ return;
+ }
+ mSlot = slot;
+ mAddressOffSet = static_cast<unsigned int>(mSlot) * (1 << 20);
+ DDD("got memory lot %d addrr %x", mSlot, mAddressOffSet);
+ mHasAddressSpaceMemory = true;
+ }
+ transport->writeParam(mVersion, 0, mAddressOffSet);
+ transport->writeParam(width, 1, mAddressOffSet);
+ transport->writeParam(height, 2, mAddressOffSet);
+ transport->writeParam(outWidth, 3, mAddressOffSet);
+ transport->writeParam(outHeight, 4, mAddressOffSet);
+ transport->writeParam(static_cast<uint64_t>(pixFmt), 5, mAddressOffSet);
+ transport->sendOperation(MediaCodecType::HevcCodec,
+ MediaOperation::InitContext, mAddressOffSet);
+ auto *retptr = transport->getReturnAddr(mAddressOffSet);
+ mHostHandle = *(uint64_t *)(retptr);
+ DDD("initHevcContext: got handle to host %lld", mHostHandle);
+}
+
+void MediaHevcDecoder::resetHevcContext(unsigned int width, unsigned int height,
+ unsigned int outWidth,
+ unsigned int outHeight,
+ PixelFormat pixFmt) {
+ auto transport = GoldfishMediaTransport::getInstance();
+ if (!mHasAddressSpaceMemory) {
+ ALOGE("%s no address space memory", __func__);
+ return;
+ }
+ transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+ transport->writeParam(width, 1, mAddressOffSet);
+ transport->writeParam(height, 2, mAddressOffSet);
+ transport->writeParam(outWidth, 3, mAddressOffSet);
+ transport->writeParam(outHeight, 4, mAddressOffSet);
+ transport->writeParam(static_cast<uint64_t>(pixFmt), 5, mAddressOffSet);
+ transport->sendOperation(MediaCodecType::HevcCodec, MediaOperation::Reset,
+ mAddressOffSet);
+ DDD("resetHevcContext: done");
+}
+
+void MediaHevcDecoder::destroyHevcContext() {
+
+ DDD("return memory lot %d addrr %x", (int)(mAddressOffSet >> 23),
+ mAddressOffSet);
+ auto transport = GoldfishMediaTransport::getInstance();
+ transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+ transport->sendOperation(MediaCodecType::HevcCodec,
+ MediaOperation::DestroyContext, mAddressOffSet);
+ transport->returnMemorySlot(mSlot);
+ mHasAddressSpaceMemory = false;
+}
+
+hevc_result_t MediaHevcDecoder::decodeFrame(uint8_t *img, size_t szBytes,
+ uint64_t pts) {
+ DDD("decode frame: use handle to host %lld", mHostHandle);
+ hevc_result_t res = {0, 0};
+ if (!mHasAddressSpaceMemory) {
+ ALOGE("%s no address space memory", __func__);
+ return res;
+ }
+ auto transport = GoldfishMediaTransport::getInstance();
+ uint8_t *hostSrc = transport->getInputAddr(mAddressOffSet);
+ if (img != nullptr && szBytes > 0) {
+ memcpy(hostSrc, img, szBytes);
+ }
+ transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+ transport->writeParam(transport->offsetOf((uint64_t)(hostSrc)) -
+ mAddressOffSet,
+ 1, mAddressOffSet);
+ transport->writeParam((uint64_t)szBytes, 2, mAddressOffSet);
+ transport->writeParam((uint64_t)pts, 3, mAddressOffSet);
+ transport->sendOperation(MediaCodecType::HevcCodec,
+ MediaOperation::DecodeImage, mAddressOffSet);
+
+ auto *retptr = transport->getReturnAddr(mAddressOffSet);
+ res.bytesProcessed = *(uint64_t *)(retptr);
+ res.ret = *(int *)(retptr + 8);
+
+ return res;
+}
+
+void MediaHevcDecoder::flush() {
+ if (!mHasAddressSpaceMemory) {
+ ALOGE("%s no address space memory", __func__);
+ return;
+ }
+ DDD("flush: use handle to host %lld", mHostHandle);
+ auto transport = GoldfishMediaTransport::getInstance();
+ transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+ transport->sendOperation(MediaCodecType::HevcCodec, MediaOperation::Flush,
+ mAddressOffSet);
+}
+
+hevc_image_t MediaHevcDecoder::getImage() {
+ DDD("getImage: use handle to host %lld", mHostHandle);
+ hevc_image_t res{};
+ if (!mHasAddressSpaceMemory) {
+ ALOGE("%s no address space memory", __func__);
+ return res;
+ }
+ auto transport = GoldfishMediaTransport::getInstance();
+ uint8_t *dst = transport->getInputAddr(
+ mAddressOffSet); // Note: reuse the same addr for input and output
+ transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+ transport->writeParam(transport->offsetOf((uint64_t)(dst)) - mAddressOffSet,
+ 1, mAddressOffSet);
+ transport->writeParam(-1, 2, mAddressOffSet);
+ transport->sendOperation(MediaCodecType::HevcCodec,
+ MediaOperation::GetImage, mAddressOffSet);
+ auto *retptr = transport->getReturnAddr(mAddressOffSet);
+ res.ret = *(int *)(retptr);
+ if (res.ret >= 0) {
+ res.data = dst;
+ res.width = *(uint32_t *)(retptr + 8);
+ res.height = *(uint32_t *)(retptr + 16);
+ res.pts = *(uint64_t *)(retptr + 24);
+ res.color_primaries = *(uint32_t *)(retptr + 32);
+ res.color_range = *(uint32_t *)(retptr + 40);
+ res.color_trc = *(uint32_t *)(retptr + 48);
+ res.colorspace = *(uint32_t *)(retptr + 56);
+ } else if (res.ret == (int)(Err::DecoderRestarted)) {
+ res.width = *(uint32_t *)(retptr + 8);
+ res.height = *(uint32_t *)(retptr + 16);
+ }
+ return res;
+}
+
+hevc_image_t
+MediaHevcDecoder::renderOnHostAndReturnImageMetadata(int hostColorBufferId) {
+ DDD("%s: use handle to host %lld", __func__, mHostHandle);
+ hevc_image_t res{};
+ if (hostColorBufferId < 0) {
+ ALOGE("%s negative color buffer id %d", __func__, hostColorBufferId);
+ return res;
+ }
+ DDD("%s send color buffer id %d", __func__, hostColorBufferId);
+ if (!mHasAddressSpaceMemory) {
+ ALOGE("%s no address space memory", __func__);
+ return res;
+ }
+ auto transport = GoldfishMediaTransport::getInstance();
+ uint8_t *dst = transport->getInputAddr(
+ mAddressOffSet); // Note: reuse the same addr for input and output
+ transport->writeParam((uint64_t)mHostHandle, 0, mAddressOffSet);
+ transport->writeParam(transport->offsetOf((uint64_t)(dst)) - mAddressOffSet,
+ 1, mAddressOffSet);
+ transport->writeParam((uint64_t)hostColorBufferId, 2, mAddressOffSet);
+ transport->sendOperation(MediaCodecType::HevcCodec,
+ MediaOperation::GetImage, mAddressOffSet);
+ auto *retptr = transport->getReturnAddr(mAddressOffSet);
+ res.ret = *(int *)(retptr);
+ if (res.ret >= 0) {
+ res.data = dst; // note: the data could be junk
+ res.width = *(uint32_t *)(retptr + 8);
+ res.height = *(uint32_t *)(retptr + 16);
+ res.pts = *(uint64_t *)(retptr + 24);
+ res.color_primaries = *(uint32_t *)(retptr + 32);
+ res.color_range = *(uint32_t *)(retptr + 40);
+ res.color_trc = *(uint32_t *)(retptr + 48);
+ res.colorspace = *(uint32_t *)(retptr + 56);
+ } else if (res.ret == (int)(Err::DecoderRestarted)) {
+ res.width = *(uint32_t *)(retptr + 8);
+ res.height = *(uint32_t *)(retptr + 16);
+ }
+ return res;
+}
diff --git a/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h
new file mode 100644
index 0000000..8dfb0cf
--- /dev/null
+++ b/system/codecs/c2/decoders/hevcdec/MediaHevcDecoder.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef GOLDFISH_MEDIA_Hevc_DEC_H_
+#define GOLDFISH_MEDIA_Hevc_DEC_H_
+
+struct hevc_init_result_t {
+ uint64_t host_handle;
+ int ret;
+};
+
+struct hevc_result_t {
+ int ret;
+ uint64_t bytesProcessed;
+};
+
+struct hevc_image_t {
+ const uint8_t *data;
+ uint32_t width;
+ uint32_t height;
+ uint64_t pts; // presentation time stamp
+ uint64_t color_primaries;
+ uint64_t color_range;
+ uint64_t color_trc;
+ uint64_t colorspace;
+ // on success, |ret| will indicate the size of |data|.
+ // If failed, |ret| will contain some negative error code.
+ int ret;
+};
+
+enum class RenderMode {
+ RENDER_BY_HOST_GPU = 1,
+ RENDER_BY_GUEST_CPU = 2,
+};
+
+class MediaHevcDecoder {
+ uint64_t mHostHandle = 0;
+ uint32_t mVersion = 100;
+ RenderMode mRenderMode = RenderMode::RENDER_BY_GUEST_CPU;
+
+ bool mHasAddressSpaceMemory = false;
+ uint64_t mAddressOffSet = 0;
+ int mSlot = -1;
+
+ public:
+ MediaHevcDecoder(RenderMode renderMode);
+ virtual ~MediaHevcDecoder() = default;
+
+ enum class PixelFormat : uint8_t {
+ YUV420P = 0,
+ UYVY422 = 1,
+ BGRA8888 = 2,
+ };
+
+ enum class Err : int {
+ NoErr = 0,
+ NoDecodedFrame = -1,
+ InitContextFailed = -2,
+ DecoderRestarted = -3,
+ NALUIgnored = -4,
+ };
+
+ bool getAddressSpaceMemory();
+ void initHevcContext(unsigned int width, unsigned int height,
+ unsigned int outWidth, unsigned int outHeight,
+ PixelFormat pixFmt);
+ void resetHevcContext(unsigned int width, unsigned int height,
+ unsigned int outWidth, unsigned int outHeight,
+ PixelFormat pixFmt);
+ void destroyHevcContext();
+ hevc_result_t decodeFrame(uint8_t *img, size_t szBytes, uint64_t pts);
+ void flush();
+ // ask host to copy image data back to guest, with image metadata
+ // to guest as well
+ hevc_image_t getImage();
+ // ask host to render to hostColorBufferId, return only image metadata back
+ // to guest
+ hevc_image_t renderOnHostAndReturnImageMetadata(int hostColorBufferId);
+};
+#endif
diff --git a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
index 7df99ff..99f0469 100644
--- a/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
+++ b/system/codecs/c2/decoders/vpxdec/C2GoldfishVpxDec.cpp
@@ -52,7 +52,7 @@
using ::android::hardware::graphics::common::V1_2::PixelFormat;
namespace android {
-constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
+constexpr size_t kMinInputBufferSize = 6 * 1024 * 1024;
#ifdef VP9
constexpr char COMPONENT_NAME[] = "c2.goldfish.vp9.decoder";
#else
@@ -465,32 +465,14 @@
mCtx = new vpx_codec_ctx_t;
mCtx->vpversion = mMode == MODE_VP8 ? 8 : 9;
- // check for decoding mode:
- {
- // now get the block
- constexpr uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
- std::shared_ptr<C2GraphicBlock> block;
- C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
- C2MemoryUsage::CPU_WRITE};
- usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
-
- c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 2), mHeight,
- format, usage, &block);
- if (err != C2_OK) {
- ALOGE("fetchGraphicBlock for Output failed with status %d", err);
- return;
- }
- auto c2Handle = block->handle();
- native_handle_t *grallocHandle =
- UnwrapNativeCodec2GrallocHandle(c2Handle);
- int hostColorBufferId = getColorBufferHandle(grallocHandle);
- if (hostColorBufferId > 0) {
- DDD("decoding to host color buffer");
- mEnableAndroidNativeBuffers = true;
- } else {
- DDD("decoding to guest byte buffer");
- mEnableAndroidNativeBuffers = false;
- }
+ const bool isGraphic = (pool->getAllocatorId() & C2Allocator::GRAPHIC);
+ DDD("buffer id %d", (int)(pool->getAllocatorId()));
+ if (isGraphic) {
+ DDD("decoding to host color buffer");
+ mEnableAndroidNativeBuffers = true;
+ } else {
+ DDD("decoding to guest byte buffer");
+ mEnableAndroidNativeBuffers = false;
}
mCtx->version = mEnableAndroidNativeBuffers ? 200 : 100;
@@ -711,7 +693,7 @@
std::shared_ptr<C2GraphicBlock> block;
C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
uint32_t format = HAL_PIXEL_FORMAT_YCBCR_420_888;
- usage.expected = (uint64_t)(BufferUsage::GPU_DATA_BUFFER);
+ usage.expected = (uint64_t)(BufferUsage::VIDEO_DECODER);
c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 2), mHeight, format,
usage, &block);
@@ -721,24 +703,24 @@
return UNKNOWN_ERROR;
}
- bool decodingToByteBuffer = false;
- {
+ int hostColorBufferId = -1;
+ const bool decodingToHostColorBuffer = mEnableAndroidNativeBuffers;
+ if(decodingToHostColorBuffer){
auto c2Handle = block->handle();
native_handle_t *grallocHandle =
UnwrapNativeCodec2GrallocHandle(c2Handle);
- int hostColorBufferId = getColorBufferHandle(grallocHandle);
+ hostColorBufferId = getColorBufferHandle(grallocHandle);
if (hostColorBufferId > 0) {
DDD("found handle %d", hostColorBufferId);
} else {
- decodingToByteBuffer = true;
DDD("decode to buffer, because handle %d is invalid",
hostColorBufferId);
// change to -1 so host knows it is definitely invalid
// 0 is a bit confusing
hostColorBufferId = -1;
}
- setup_ctx_parameters(mCtx, hostColorBufferId);
}
+ setup_ctx_parameters(mCtx, hostColorBufferId);
vpx_image_t *img = vpx_codec_get_frame(mCtx);
@@ -798,7 +780,7 @@
}
}
- if (decodingToByteBuffer) {
+ if (!decodingToHostColorBuffer) {
C2GraphicView wView = block->map().get();
if (wView.error()) {
diff --git a/system/codecs/c2/store/GoldfishComponentStore.cpp b/system/codecs/c2/store/GoldfishComponentStore.cpp
index 49c5a32..2d2ab47 100644
--- a/system/codecs/c2/store/GoldfishComponentStore.cpp
+++ b/system/codecs/c2/store/GoldfishComponentStore.cpp
@@ -272,6 +272,9 @@
if (useAndroidGoldfishComponentInstance("avcdec")) {
emplace("libcodec2_goldfish_avcdec.so");
}
+ if (useAndroidGoldfishComponentInstance("hevcdec")) {
+ emplace("libcodec2_goldfish_hevcdec.so");
+ }
}
c2_status_t
diff --git a/system/hals/allocator3.cpp b/system/hals/allocator3.cpp
index d3cf5c1..3619d91 100644
--- a/system/hals/allocator3.cpp
+++ b/system/hals/allocator3.cpp
@@ -309,7 +309,7 @@
}
RETURN_ERROR(Error3::UNSUPPORTED);
} else if (static_cast<int>(frameworkFormat) == kOMX_COLOR_FormatYUV420Planar &&
- (usage & BufferUsage::GPU_DATA_BUFFER)) {
+ (usage & BufferUsage::VIDEO_DECODER)) {
ALOGW("gralloc_alloc: Requested OMX_COLOR_FormatYUV420Planar, given "
"YCbCr_420_888, taking experimental path. "
"usage=%x", usage);
@@ -341,6 +341,7 @@
| BufferUsage::GPU_RENDER_TARGET
| BufferUsage::COMPOSER_OVERLAY
| BufferUsage::VIDEO_ENCODER
+ | BufferUsage::VIDEO_DECODER
| BufferUsage::COMPOSER_CLIENT_TARGET
| BufferUsage::CPU_READ_MASK));
}
diff --git a/system/hals/mapper3.cpp b/system/hals/mapper3.cpp
index 99a8d6a..6e3a664 100644
--- a/system/hals/mapper3.cpp
+++ b/system/hals/mapper3.cpp
@@ -634,7 +634,7 @@
default:
if (static_cast<int>(descriptor.format) == kOMX_COLOR_FormatYUV420Planar) {
- return (usage & BufferUsage::GPU_DATA_BUFFER) != 0;
+ return (usage & BufferUsage::VIDEO_DECODER) != 0;
}
RETURN(false);
diff --git a/system/hwc3/Android.mk b/system/hwc3/Android.mk
new file mode 100644
index 0000000..3d30e89
--- /dev/null
+++ b/system/hwc3/Android.mk
@@ -0,0 +1,101 @@
+#
+# Copyright 2022 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.hardware.graphics.composer3-service.ranchu
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../LICENSE
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_VENDOR_MODULE := true
+
+LOCAL_SHARED_LIBRARIES := \
+ android.hardware.graphics.composer@2.1-resources \
+ android.hardware.graphics.composer@2.2-resources \
+ android.hardware.graphics.composer3-V1-ndk \
+ android.hardware.graphics.mapper@2.0 \
+ android.hardware.graphics.mapper@4.0 \
+ libbase \
+ libbinder \
+ libbinder_ndk \
+ libEGL \
+ libcutils \
+ libcuttlefish_device_config \
+ libcuttlefish_device_config_proto \
+ libcuttlefish_utils \
+ libcuttlefish_fs \
+ libdrm \
+ libgralloctypes \
+ libhardware \
+ libhidlbase \
+ libjsoncpp \
+ libjpeg \
+ liblog \
+ libsync \
+ libui \
+ libutils \
+ libutils \
+ libOpenglSystemCommon \
+ lib_renderControl_enc \
+ libui
+
+LOCAL_STATIC_LIBRARIES := \
+ libaidlcommonsupport \
+ libyuv_static
+
+LOCAL_C_INCLUDES := \
+ device/generic/goldfish-opengl/host/include/libOpenglRender \
+ device/generic/goldfish-opengl/android-emu \
+ device/generic/goldfish-opengl/shared/OpenglCodecCommon \
+ device/generic/goldfish-opengl/system/OpenglSystemCommon \
+ device/generic/goldfish-opengl/system/include \
+ device/generic/goldfish-opengl/system/renderControl_enc \
+ external/libdrm \
+ external/minigbm/cros_gralloc \
+ system/core/libsync \
+ system/core/libsync/include \
+
+LOCAL_SRC_FILES := \
+ ClientFrameComposer.cpp \
+ Common.cpp \
+ Composer.cpp \
+ ComposerClient.cpp \
+ ComposerResources.cpp \
+ Device.cpp \
+ Display.cpp \
+ DisplayConfig.cpp \
+ DisplayFinder.cpp \
+ Drm.cpp \
+ DrmPresenter.cpp \
+ Gralloc.cpp \
+ GuestFrameComposer.cpp \
+ HostFrameComposer.cpp \
+ HostUtils.cpp \
+ Layer.cpp \
+ Main.cpp \
+ NoOpFrameComposer.cpp \
+ VsyncThread.cpp \
+
+LOCAL_VINTF_FRAGMENTS := hwc3.xml
+LOCAL_INIT_RC := hwc3.rc
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/system/hwc3/ClientFrameComposer.cpp b/system/hwc3/ClientFrameComposer.cpp
new file mode 100644
index 0000000..d7f8b55
--- /dev/null
+++ b/system/hwc3/ClientFrameComposer.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 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 "ClientFrameComposer.h"
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+#include <device_config_shared.h>
+#include <drm_fourcc.h>
+#include <libyuv.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include "Display.h"
+#include "Drm.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HWC3::Error ClientFrameComposer::init() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ HWC3::Error error = mDrmPresenter.init();
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to initialize DrmPresenter", __FUNCTION__);
+ return error;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::registerOnHotplugCallback(
+ const HotplugCallback& cb) {
+ return mDrmPresenter.registerOnHotplugCallback(cb);
+ return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::unregisterOnHotplugCallback() {
+ return mDrmPresenter.unregisterOnHotplugCallback();
+}
+
+HWC3::Error ClientFrameComposer::onDisplayCreate(Display* display) {
+ const auto displayId = display->getId();
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ // Ensure created.
+ mDisplayInfos.emplace(displayId, DisplayInfo{});
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::onDisplayDestroy(Display* display) {
+ const auto displayId = display->getId();
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ auto it = mDisplayInfos.find(displayId);
+ if (it == mDisplayInfos.end()) {
+ ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+ displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ mDisplayInfos.erase(it);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::onDisplayClientTargetSet(Display* display) {
+ const auto displayId = display->getId();
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ auto it = mDisplayInfos.find(displayId);
+ if (it == mDisplayInfos.end()) {
+ ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+ displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ DisplayInfo& displayInfo = it->second;
+
+ auto [drmBufferCreateError, drmBuffer] =
+ mDrmPresenter.create(display->getClientTarget().getBuffer());
+ if (drmBufferCreateError != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to create client target drm buffer",
+ __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+ displayInfo.clientTargetDrmBuffer = std::move(drmBuffer);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::onActiveConfigChange(Display* /*display*/) {
+ return HWC3::Error::None;
+};
+
+HWC3::Error ClientFrameComposer::validateDisplay(Display* display,
+ DisplayChanges* outChanges) {
+ const auto displayId = display->getId();
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ const std::vector<Layer*>& layers = display->getOrderedLayers();
+
+ for (Layer* layer : layers) {
+ const auto layerId = layer->getId();
+ const auto layerCompositionType = layer->getCompositionType();
+
+ if (layerCompositionType != Composition::CLIENT) {
+ outChanges->addLayerCompositionChange(displayId, layerId, Composition::CLIENT);
+ continue;
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ClientFrameComposer::presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t,
+ ::android::base::unique_fd>* /*outLayerFences*/) {
+ const auto displayId = display->getId();
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ auto displayInfoIt = mDisplayInfos.find(displayId);
+ if (displayInfoIt == mDisplayInfos.end()) {
+ ALOGE("%s: failed to find display buffers for display:%" PRIu64,
+ __FUNCTION__, displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ DisplayInfo& displayInfo = displayInfoIt->second;
+
+ ::android::base::unique_fd fence = display->getClientTarget().getFence();
+
+ auto [flushError, flushCompleteFence] = mDrmPresenter.flushToDisplay(
+ displayId, *displayInfo.clientTargetDrmBuffer, fence);
+ if (flushError != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64,
+ __FUNCTION__, displayId);
+ }
+
+ *outDisplayFence = std::move(flushCompleteFence);
+ return flushError;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/ClientFrameComposer.h b/system/hwc3/ClientFrameComposer.h
new file mode 100644
index 0000000..3fb0ba8
--- /dev/null
+++ b/system/hwc3/ClientFrameComposer.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_CLIENTFRAMECOMPOSER_H
+#define ANDROID_HWC_CLIENTFRAMECOMPOSER_H
+
+#include "Common.h"
+#include "Display.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "Gralloc.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+// A frame composer which always fallsback to client composition
+// (a.k.a make SurfaceFlinger do the composition).
+class ClientFrameComposer : public FrameComposer {
+ public:
+ ClientFrameComposer() = default;
+
+ ClientFrameComposer(const ClientFrameComposer&) = delete;
+ ClientFrameComposer& operator=(const ClientFrameComposer&) = delete;
+
+ ClientFrameComposer(ClientFrameComposer&&) = delete;
+ ClientFrameComposer& operator=(ClientFrameComposer&&) = delete;
+
+ HWC3::Error init() override;
+
+ HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+ HWC3::Error unregisterOnHotplugCallback() override;
+
+ HWC3::Error onDisplayCreate(Display* display) override;
+
+ HWC3::Error onDisplayDestroy(Display* display) override;
+
+ HWC3::Error onDisplayClientTargetSet(Display* display) override;
+
+ HWC3::Error onActiveConfigChange(Display* display) override;
+
+ // Determines if this composer can compose the given layers on the given
+ // display and requests changes for layers that can't not be composed.
+ HWC3::Error validateDisplay(Display* display,
+ DisplayChanges* outChanges) override;
+
+ // Performs the actual composition of layers and presents the composed result
+ // to the display.
+ HWC3::Error presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+ override;
+
+ private:
+ struct DisplayInfo {
+ std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;;
+ };
+
+ std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
+
+ DrmPresenter mDrmPresenter;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Common.cpp b/system/hwc3/Common.cpp
new file mode 100644
index 0000000..497f764
--- /dev/null
+++ b/system/hwc3/Common.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 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 "Common.h"
+
+#include <android-base/properties.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+bool IsCuttlefish() {
+ return ::android::base::GetProperty("ro.product.board", "") == "cutf";
+}
+
+bool IsCuttlefishFoldable() {
+ return IsCuttlefish() &&
+ ::android::base::GetProperty("ro.product.name", "").find("foldable") !=
+ std::string::npos;
+}
+
+bool IsNoOpMode() {
+ const std::string mode = ::android::base::GetProperty("ro.vendor.hwcomposer.mode", "");
+ DEBUG_LOG("%s: sysprop ro.vendor.hwcomposer.mode is %s", __FUNCTION__, mode.c_str());
+ return mode == "noop";
+}
+
+bool IsClientCompositionMode() {
+ const std::string mode = ::android::base::GetProperty("ro.vendor.hwcomposer.mode", "");
+ DEBUG_LOG("%s: sysprop ro.vendor.hwcomposer.mode is %s", __FUNCTION__, mode.c_str());
+ return mode == "client";
+}
+
+std::string toString(HWC3::Error error) {
+ switch (error) {
+ case HWC3::Error::None:
+ return "None";
+ case HWC3::Error::BadConfig:
+ return "BadConfig";
+ case HWC3::Error::BadDisplay:
+ return "BadDisplay";
+ case HWC3::Error::BadLayer:
+ return "BadLayer";
+ case HWC3::Error::BadParameter:
+ return "BadParameter";
+ case HWC3::Error::NoResources:
+ return "NoResources";
+ case HWC3::Error::NotValidated:
+ return "NotValidated";
+ case HWC3::Error::Unsupported:
+ return "Unsupported";
+ case HWC3::Error::SeamlessNotAllowed:
+ return "SeamlessNotAllowed";
+ }
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Common.h b/system/hwc3/Common.h
new file mode 100644
index 0000000..5b6587e
--- /dev/null
+++ b/system/hwc3/Common.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_COMMON_H
+#define ANDROID_HWC_COMMON_H
+
+#include <inttypes.h>
+
+#include <string>
+
+#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
+
+#undef LOG_TAG
+#define LOG_TAG "RanchuHwc"
+
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
+#include <android-base/logging.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+// Uncomment to enable additional debug logging.
+//#define DEBUG_RANCHU_HWC
+
+#if defined(DEBUG_RANCHU_HWC)
+#define DEBUG_LOG ALOGE
+#else
+#define DEBUG_LOG(...) ((void)0)
+#endif
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+bool IsCuttlefish();
+bool IsCuttlefishFoldable();
+bool IsNoOpMode();
+bool IsClientCompositionMode();
+
+namespace HWC3 {
+enum class Error : int32_t {
+ None = 0,
+ BadConfig = aidl::android::hardware::graphics::composer3::IComposerClient::
+ EX_BAD_CONFIG,
+ BadDisplay = aidl::android::hardware::graphics::composer3::IComposerClient::
+ EX_BAD_DISPLAY,
+ BadLayer = aidl::android::hardware::graphics::composer3::IComposerClient::
+ EX_BAD_LAYER,
+ BadParameter = aidl::android::hardware::graphics::composer3::IComposerClient::
+ EX_BAD_PARAMETER,
+ NoResources = aidl::android::hardware::graphics::composer3::IComposerClient::
+ EX_NO_RESOURCES,
+ NotValidated = aidl::android::hardware::graphics::composer3::IComposerClient::
+ EX_NOT_VALIDATED,
+ Unsupported = aidl::android::hardware::graphics::composer3::IComposerClient::
+ EX_UNSUPPORTED,
+ SeamlessNotAllowed = aidl::android::hardware::graphics::composer3::
+ IComposerClient::EX_SEAMLESS_NOT_ALLOWED,
+};
+} // namespace HWC3
+
+std::string toString(HWC3::Error error);
+
+inline ndk::ScopedAStatus ToBinderStatus(HWC3::Error error) {
+ if (error != HWC3::Error::None) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(error));
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Composer.cpp b/system/hwc3/Composer.cpp
new file mode 100644
index 0000000..b2a1b7e
--- /dev/null
+++ b/system/hwc3/Composer.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 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 "Composer.h"
+
+#include <android-base/logging.h>
+#include <android/binder_ibinder_platform.h>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+ndk::ScopedAStatus Composer::createClient(
+ std::shared_ptr<IComposerClient>* outClient) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mClientMutex);
+
+ const bool previousClientDestroyed = waitForClientDestroyedLocked(lock);
+ if (!previousClientDestroyed) {
+ ALOGE("%s: failed as composer client already exists", __FUNCTION__);
+ *outClient = nullptr;
+ return ToBinderStatus(HWC3::Error::NoResources);
+ }
+
+ auto client = ndk::SharedRefBase::make<ComposerClient>();
+ if (!client) {
+ ALOGE("%s: failed to init composer client", __FUNCTION__);
+ *outClient = nullptr;
+ return ToBinderStatus(HWC3::Error::NoResources);
+ }
+
+ auto error = client->init();
+ if (error != HWC3::Error::None) {
+ *outClient = nullptr;
+ return ToBinderStatus(error);
+ }
+
+ auto clientDestroyed = [this]() { onClientDestroyed(); };
+ client->setOnClientDestroyed(clientDestroyed);
+
+ mClient = client;
+ *outClient = client;
+
+ return ndk::ScopedAStatus::ok();
+}
+
+bool Composer::waitForClientDestroyedLocked(
+ std::unique_lock<std::mutex>& lock) {
+ if (!mClient.expired()) {
+ // In surface flinger we delete a composer client on one thread and
+ // then create a new client on another thread. Although surface
+ // flinger ensures the calls are made in that sequence (destroy and
+ // then create), sometimes the calls land in the composer service
+ // inverted (create and then destroy). Wait for a brief period to
+ // see if the existing client is destroyed.
+ constexpr const auto kTimeout = std::chrono::seconds(5);
+ mClientDestroyedCondition.wait_for(
+ lock, kTimeout, [this]() -> bool { return mClient.expired(); });
+ if (!mClient.expired()) {
+ ALOGW("%s: previous client was not destroyed", __FUNCTION__);
+ }
+ }
+
+ return mClient.expired();
+}
+
+void Composer::onClientDestroyed() {
+ std::lock_guard<std::mutex> lock(mClientMutex);
+
+ mClientDestroyedCondition.notify_all();
+}
+
+binder_status_t Composer::dump(int fd, const char** /*args*/,
+ uint32_t /*numArgs*/) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::string output("TODO");
+
+ write(fd, output.c_str(), output.size());
+ return STATUS_OK;
+}
+
+ndk::ScopedAStatus Composer::getCapabilities(std::vector<Capability>* caps) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ caps->clear();
+ caps->emplace_back(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
+ caps->emplace_back(Capability::BOOT_DISPLAY_CONFIG);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+::ndk::SpAIBinder Composer::createBinder() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto binder = BnComposer::createBinder();
+ AIBinder_setInheritRt(binder.get(), true);
+ return binder;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/Composer.h b/system/hwc3/Composer.h
new file mode 100644
index 0000000..f564169
--- /dev/null
+++ b/system/hwc3/Composer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_COMPOSER_H
+#define ANDROID_HWC_COMPOSER_H
+
+#include <aidl/android/hardware/graphics/composer3/BnComposer.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+#include "ComposerClient.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+// This class is basically just the interface to create a client.
+class Composer : public BnComposer {
+ public:
+ Composer() = default;
+
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+ // compser3 api
+ ndk::ScopedAStatus createClient(
+ std::shared_ptr<IComposerClient>* client) override;
+ ndk::ScopedAStatus getCapabilities(std::vector<Capability>* caps) override;
+
+ protected:
+ ndk::SpAIBinder createBinder() override;
+
+ private:
+ bool waitForClientDestroyedLocked(std::unique_lock<std::mutex>& lock);
+ void onClientDestroyed();
+
+ std::mutex mClientMutex;
+ std::weak_ptr<ComposerClient> mClient GUARDED_BY(mClientMutex);
+ std::condition_variable mClientDestroyedCondition;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/ComposerClient.cpp b/system/hwc3/ComposerClient.cpp
new file mode 100644
index 0000000..db05383
--- /dev/null
+++ b/system/hwc3/ComposerClient.cpp
@@ -0,0 +1,1379 @@
+/*
+ * Copyright (C) 2022 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 "ComposerClient.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android/binder_ibinder_platform.h>
+
+#include "Common.h"
+#include "Device.h"
+#include "GuestFrameComposer.h"
+#include "HostFrameComposer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+#define GET_DISPLAY_OR_RETURN_ERROR() \
+ Display* display = getDisplay(displayId); \
+ if (display == nullptr) { \
+ ALOGE("%s failed to get display:%" PRIu64, __FUNCTION__, displayId); \
+ return ToBinderStatus(HWC3::Error::BadDisplay); \
+ }
+
+} // namespace
+
+using ::aidl::android::hardware::graphics::common::PixelFormat;
+
+class ComposerClient::CommandResultWriter {
+ public:
+ CommandResultWriter(std::vector<CommandResultPayload>* results)
+ : mIndex(0), mResults(results) {}
+
+ void nextCommand() { ++mIndex; }
+
+ void addError(HWC3::Error error) {
+ CommandError commandErrorResult;
+ commandErrorResult.commandIndex = mIndex;
+ commandErrorResult.errorCode = static_cast<int32_t>(error);
+ mResults->emplace_back(std::move(commandErrorResult));
+ }
+
+ void addPresentFence(int64_t displayId, ::android::base::unique_fd fence) {
+ if (fence >= 0) {
+ PresentFence presentFenceResult;
+ presentFenceResult.display = displayId;
+ presentFenceResult.fence = ndk::ScopedFileDescriptor(fence.release());
+ mResults->emplace_back(std::move(presentFenceResult));
+ }
+ }
+
+ void addReleaseFences(
+ int64_t displayId,
+ std::unordered_map<int64_t, ::android::base::unique_fd> layerFences) {
+ ReleaseFences releaseFencesResult;
+ releaseFencesResult.display = displayId;
+ for (auto& [layer, layerFence] : layerFences) {
+ if (layerFence >= 0) {
+ ReleaseFences::Layer releaseFencesLayerResult;
+ releaseFencesLayerResult.layer = layer;
+ releaseFencesLayerResult.fence =
+ ndk::ScopedFileDescriptor(layerFence.release());
+ releaseFencesResult.layers.emplace_back(
+ std::move(releaseFencesLayerResult));
+ }
+ }
+ mResults->emplace_back(std::move(releaseFencesResult));
+ }
+
+ void addChanges(const DisplayChanges& changes) {
+ if (changes.compositionChanges) {
+ mResults->emplace_back(*changes.compositionChanges);
+ }
+ if (changes.displayRequestChanges) {
+ mResults->emplace_back(*changes.displayRequestChanges);
+ }
+ }
+
+ void addPresentOrValidateResult(int64_t displayId,
+ PresentOrValidate::Result pov) {
+ PresentOrValidate result;
+ result.display = displayId;
+ result.result = pov;
+ mResults->emplace_back(std::move(result));
+ }
+
+ private:
+ int32_t mIndex = 0;
+ std::vector<CommandResultPayload>* mResults = nullptr;
+};
+
+ComposerClient::ComposerClient() { DEBUG_LOG("%s", __FUNCTION__); }
+
+ComposerClient::~ComposerClient() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ destroyDisplaysLocked();
+
+ if (mOnClientDestroyed) {
+ mOnClientDestroyed();
+ }
+}
+
+HWC3::Error ComposerClient::init() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ HWC3::Error error = HWC3::Error::None;
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ mResources = std::make_unique<ComposerResources>();
+ if (!mResources) {
+ ALOGE("%s failed to allocate ComposerResources", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ error = mResources->init();
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to initialize ComposerResources", __FUNCTION__);
+ return error;
+ }
+
+ error = Device::getInstance().getComposer(&mComposer);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to get FrameComposer", __FUNCTION__);
+ return error;
+ }
+
+ const auto HotplugCallback = [this](bool connected, //
+ int32_t id, //
+ uint32_t width, //
+ uint32_t height, //
+ uint32_t dpiX, //
+ uint32_t dpiY, //
+ uint32_t refreshRate) {
+ handleHotplug(connected, id, width, height, dpiX, dpiY, refreshRate);
+ };
+ error = mComposer->registerOnHotplugCallback(HotplugCallback);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to register hotplug callback", __FUNCTION__);
+ return error;
+ }
+
+ error = createDisplaysLocked();
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to create displays.", __FUNCTION__);
+ return error;
+ }
+
+ DEBUG_LOG("%s initialized!", __FUNCTION__);
+ return HWC3::Error::None;
+}
+
+ndk::ScopedAStatus ComposerClient::createLayer(int64_t displayId,
+ int32_t bufferSlotCount,
+ int64_t* layerId) {
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ HWC3::Error error = display->createLayer(layerId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to create layer", __FUNCTION__,
+ displayId);
+ return ToBinderStatus(error);
+ }
+
+ error = mResources->addLayer(displayId, *layerId, bufferSlotCount);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " resources failed to create layer",
+ __FUNCTION__, displayId);
+ return ToBinderStatus(error);
+ }
+
+ return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::createVirtualDisplay(
+ int32_t /*width*/, int32_t /*height*/, PixelFormat /*formatHint*/,
+ int32_t /*outputBufferSlotCount*/, VirtualDisplay* /*display*/) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return ToBinderStatus(HWC3::Error::Unsupported);
+}
+
+ndk::ScopedAStatus ComposerClient::destroyLayer(int64_t displayId,
+ int64_t layerId) {
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ HWC3::Error error = display->destroyLayer(layerId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to destroy layer:%" PRIu64,
+ __FUNCTION__, displayId, layerId);
+ return ToBinderStatus(error);
+ }
+
+ error = mResources->removeLayer(displayId, layerId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " resources failed to destroy layer:%" PRIu64,
+ __FUNCTION__, displayId, layerId);
+ return ToBinderStatus(error);
+ }
+
+ return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::destroyVirtualDisplay(
+ int64_t /*displayId*/) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return ToBinderStatus(HWC3::Error::Unsupported);
+}
+
+ndk::ScopedAStatus ComposerClient::executeCommands(
+ const std::vector<DisplayCommand>& commands,
+ std::vector<CommandResultPayload>* commandResultPayloads) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ mCommandResults =
+ std::make_unique<CommandResultWriter>(commandResultPayloads);
+
+ for (const DisplayCommand& command : commands) {
+ executeDisplayCommand(command);
+ mCommandResults->nextCommand();
+ }
+
+ mCommandResults.reset();
+
+ return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::getActiveConfig(int64_t displayId,
+ int32_t* config) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getActiveConfig(config));
+}
+
+ndk::ScopedAStatus ComposerClient::getColorModes(
+ int64_t displayId, std::vector<ColorMode>* colorModes) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getColorModes(colorModes));
+}
+
+ndk::ScopedAStatus ComposerClient::getDataspaceSaturationMatrix(
+ common::Dataspace dataspace, std::vector<float>* matrix) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ if (dataspace != common::Dataspace::SRGB_LINEAR) {
+ return ToBinderStatus(HWC3::Error::BadParameter);
+ }
+
+ // clang-format off
+ constexpr std::array<float, 16> kUnit {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ };
+ // clang-format on
+ matrix->clear();
+ matrix->insert(matrix->begin(), kUnit.begin(), kUnit.end());
+
+ return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayAttribute(
+ int64_t displayId, int32_t config, DisplayAttribute attribute,
+ int32_t* value) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDisplayAttribute(config, attribute, value));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayCapabilities(
+ int64_t displayId, std::vector<DisplayCapability>* outCaps) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDisplayCapabilities(outCaps));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayConfigs(
+ int64_t displayId, std::vector<int32_t>* outConfigs) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDisplayConfigs(outConfigs));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayConnectionType(
+ int64_t displayId, DisplayConnectionType* outType) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDisplayConnectionType(outType));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayIdentificationData(
+ int64_t displayId, DisplayIdentification* outIdentification) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(
+ display->getDisplayIdentificationData(outIdentification));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayName(int64_t displayId,
+ std::string* outName) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDisplayName(outName));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayVsyncPeriod(
+ int64_t displayId, int32_t* outVsyncPeriod) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDisplayVsyncPeriod(outVsyncPeriod));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayedContentSample(
+ int64_t displayId, int64_t maxFrames, int64_t timestamp,
+ DisplayContentSample* outSamples) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(
+ display->getDisplayedContentSample(maxFrames, timestamp, outSamples));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayedContentSamplingAttributes(
+ int64_t displayId, DisplayContentSamplingAttributes* outAttributes) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(
+ display->getDisplayedContentSamplingAttributes(outAttributes));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayPhysicalOrientation(
+ int64_t displayId, common::Transform* outOrientation) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDisplayPhysicalOrientation(outOrientation));
+}
+
+ndk::ScopedAStatus ComposerClient::getHdrCapabilities(
+ int64_t displayId, HdrCapabilities* outCapabilities) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getHdrCapabilities(outCapabilities));
+}
+
+ndk::ScopedAStatus ComposerClient::getMaxVirtualDisplayCount(
+ int32_t* outCount) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ // Not supported.
+ *outCount = 0;
+
+ return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::getPerFrameMetadataKeys(
+ int64_t displayId, std::vector<PerFrameMetadataKey>* outKeys) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getPerFrameMetadataKeys(outKeys));
+}
+
+ndk::ScopedAStatus ComposerClient::getReadbackBufferAttributes(
+ int64_t displayId, ReadbackBufferAttributes* outAttributes) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getReadbackBufferAttributes(outAttributes));
+}
+
+ndk::ScopedAStatus ComposerClient::getReadbackBufferFence(
+ int64_t displayId, ndk::ScopedFileDescriptor* outAcquireFence) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getReadbackBufferFence(outAcquireFence));
+}
+
+ndk::ScopedAStatus ComposerClient::getRenderIntents(
+ int64_t displayId, ColorMode mode, std::vector<RenderIntent>* outIntents) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getRenderIntents(mode, outIntents));
+}
+
+ndk::ScopedAStatus ComposerClient::getSupportedContentTypes(
+ int64_t displayId, std::vector<ContentType>* outTypes) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getSupportedContentTypes(outTypes));
+}
+
+ndk::ScopedAStatus ComposerClient::getDisplayDecorationSupport(
+ int64_t displayId,
+ std::optional<common::DisplayDecorationSupport>* outSupport) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getDecorationSupport(outSupport));
+}
+
+ndk::ScopedAStatus ComposerClient::registerCallback(
+ const std::shared_ptr<IComposerCallback>& callback) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ const bool isFirstRegisterCallback = mCallbacks == nullptr;
+
+ mCallbacks = callback;
+
+ for (auto& [_, display] : mDisplays) {
+ display->registerCallback(callback);
+ }
+
+ if (isFirstRegisterCallback) {
+ lock.unlock();
+ for (auto& [displayId, _] : mDisplays) {
+ mCallbacks->onHotplug(displayId, /*connected=*/true);
+ }
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus ComposerClient::setActiveConfig(int64_t displayId,
+ int32_t configId) {
+ DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId,
+ configId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setActiveConfig(configId));
+}
+
+ndk::ScopedAStatus ComposerClient::setActiveConfigWithConstraints(
+ int64_t displayId, int32_t configId,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
+ DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId,
+ configId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setActiveConfigWithConstraints(
+ configId, constraints, outTimeline));
+}
+
+ndk::ScopedAStatus ComposerClient::setBootDisplayConfig(int64_t displayId,
+ int32_t configId) {
+ DEBUG_LOG("%s display:%" PRIu64 " config:%" PRIu32, __FUNCTION__, displayId,
+ configId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setBootConfig(configId));
+}
+
+ndk::ScopedAStatus ComposerClient::clearBootDisplayConfig(int64_t displayId) {
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->clearBootConfig());
+}
+
+ndk::ScopedAStatus ComposerClient::getPreferredBootDisplayConfig(
+ int64_t displayId, int32_t* outConfigId) {
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->getPreferredBootConfig(outConfigId));
+}
+
+ndk::ScopedAStatus ComposerClient::setAutoLowLatencyMode(int64_t displayId,
+ bool on) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setAutoLowLatencyMode(on));
+}
+
+ndk::ScopedAStatus ComposerClient::setClientTargetSlotCount(int64_t displayId,
+ int32_t count) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(
+ mResources->setDisplayClientTargetCacheSize(displayId, count));
+}
+
+ndk::ScopedAStatus ComposerClient::setColorMode(int64_t displayId,
+ ColorMode mode,
+ RenderIntent intent) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setColorMode(mode, intent));
+}
+
+ndk::ScopedAStatus ComposerClient::setContentType(int64_t displayId,
+ ContentType type) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setContentType(type));
+}
+
+ndk::ScopedAStatus ComposerClient::setDisplayedContentSamplingEnabled(
+ int64_t displayId, bool enable, FormatColorComponent componentMask,
+ int64_t maxFrames) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setDisplayedContentSamplingEnabled(
+ enable, componentMask, maxFrames));
+}
+
+ndk::ScopedAStatus ComposerClient::setPowerMode(int64_t displayId,
+ PowerMode mode) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setPowerMode(mode));
+}
+
+ndk::ScopedAStatus ComposerClient::setReadbackBuffer(
+ int64_t displayId,
+ const aidl::android::hardware::common::NativeHandle& buffer,
+ const ndk::ScopedFileDescriptor& releaseFence) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ // Owned by mResources.
+ buffer_handle_t importedBuffer = nullptr;
+
+ auto releaser = mResources->createReleaser(true /* isBuffer */);
+ auto error = mResources->getDisplayReadbackBuffer(
+ displayId, buffer, &importedBuffer, releaser.get());
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to get readback buffer from resources.", __FUNCTION__);
+ return ToBinderStatus(error);
+ }
+
+ error = display->setReadbackBuffer(importedBuffer, releaseFence);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to set readback buffer to display.", __FUNCTION__);
+ return ToBinderStatus(error);
+ }
+
+ return ToBinderStatus(HWC3::Error::None);
+}
+
+ndk::ScopedAStatus ComposerClient::setVsyncEnabled(int64_t displayId,
+ bool enabled) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setVsyncEnabled(enabled));
+}
+
+ndk::ScopedAStatus ComposerClient::setIdleTimerEnabled(int64_t displayId,
+ int32_t timeoutMs) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ GET_DISPLAY_OR_RETURN_ERROR();
+
+ return ToBinderStatus(display->setIdleTimerEnabled(timeoutMs));
+}
+
+ndk::SpAIBinder ComposerClient::createBinder() {
+ auto binder = BnComposerClient::createBinder();
+ AIBinder_setInheritRt(binder.get(), true);
+ return binder;
+}
+
+namespace {
+
+#define DISPATCH_LAYER_COMMAND(layerCmd, display, layer, field, funcName) \
+ do { \
+ if (layerCmd.field) { \
+ ComposerClient::executeLayerCommandSetLayer##funcName(display, layer, \
+ *layerCmd.field); \
+ } \
+ } while (0)
+
+#define DISPATCH_DISPLAY_COMMAND(displayCmd, display, field, funcName) \
+ do { \
+ if (displayCmd.field) { \
+ executeDisplayCommand##funcName(display, *displayCmd.field); \
+ } \
+ } while (0)
+
+#define DISPATCH_DISPLAY_BOOL_COMMAND(displayCmd, display, field, funcName) \
+ do { \
+ if (displayCmd.field) { \
+ executeDisplayCommand##funcName(display); \
+ } \
+ } while (0)
+
+#define DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCmd, display, field, \
+ data, funcName) \
+ do { \
+ if (displayCmd.field) { \
+ executeDisplayCommand##funcName(display, displayCmd.data); \
+ } \
+ } while (0)
+
+#define LOG_DISPLAY_COMMAND_ERROR(display, error) \
+ do { \
+ const std::string errorString = toString(error); \
+ ALOGE("%s: display:%" PRId64 " failed with:%s", __FUNCTION__, \
+ display->getId(), errorString.c_str()); \
+ } while (0)
+
+#define LOG_LAYER_COMMAND_ERROR(display, layer, error) \
+ do { \
+ const std::string errorString = toString(error); \
+ ALOGE("%s: display:%" PRId64 " layer:%" PRId64 " failed with:%s", \
+ __FUNCTION__, display->getId(), layer->getId(), \
+ errorString.c_str()); \
+ } while (0)
+
+} // namespace
+
+void ComposerClient::executeDisplayCommand(
+ const DisplayCommand& displayCommand) {
+ Display* display = getDisplay(displayCommand.display);
+ if (display == nullptr) {
+ mCommandResults->addError(HWC3::Error::BadDisplay);
+ return;
+ }
+
+ for (const LayerCommand& layerCmd : displayCommand.layers) {
+ executeLayerCommand(display, layerCmd);
+ }
+
+ DISPATCH_DISPLAY_COMMAND(displayCommand, display, colorTransformMatrix,
+ SetColorTransform);
+ DISPATCH_DISPLAY_COMMAND(displayCommand, display, brightness, SetBrightness);
+ DISPATCH_DISPLAY_COMMAND(displayCommand, display, clientTarget,
+ SetClientTarget);
+ DISPATCH_DISPLAY_COMMAND(displayCommand, display, virtualDisplayOutputBuffer,
+ SetOutputBuffer);
+ DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(displayCommand, display,
+ validateDisplay, expectedPresentTime,
+ ValidateDisplay);
+ DISPATCH_DISPLAY_BOOL_COMMAND(displayCommand, display, acceptDisplayChanges,
+ AcceptDisplayChanges);
+ DISPATCH_DISPLAY_BOOL_COMMAND(displayCommand, display, presentDisplay,
+ PresentDisplay);
+ DISPATCH_DISPLAY_BOOL_COMMAND_AND_DATA(
+ displayCommand, display, presentOrValidateDisplay, expectedPresentTime,
+ PresentOrValidateDisplay);
+}
+
+void ComposerClient::executeLayerCommand(Display* display,
+ const LayerCommand& layerCommand) {
+ Layer* layer = display->getLayer(layerCommand.layer);
+ if (layer == nullptr) {
+ mCommandResults->addError(HWC3::Error::BadLayer);
+ return;
+ }
+
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, cursorPosition,
+ CursorPosition);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, buffer, Buffer);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, damage, SurfaceDamage);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, blendMode, BlendMode);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, color, Color);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, composition,
+ Composition);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, dataspace, Dataspace);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, displayFrame,
+ DisplayFrame);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, planeAlpha, PlaneAlpha);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, sidebandStream,
+ SidebandStream);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, sourceCrop, SourceCrop);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, transform, Transform);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, visibleRegion,
+ VisibleRegion);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, z, ZOrder);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, colorTransform,
+ ColorTransform);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, brightness, Brightness);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, perFrameMetadata,
+ PerFrameMetadata);
+ DISPATCH_LAYER_COMMAND(layerCommand, display, layer, perFrameMetadataBlob,
+ PerFrameMetadataBlobs);
+}
+
+void ComposerClient::executeDisplayCommandSetColorTransform(
+ Display* display, const std::vector<float>& matrix) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = display->setColorTransform(matrix);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeDisplayCommandSetBrightness(
+ Display* display, const DisplayBrightness& brightness) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = display->setBrightness(brightness.brightness);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeDisplayCommandSetClientTarget(
+ Display* display, const ClientTarget& clientTarget) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ // Owned by mResources.
+ buffer_handle_t importedBuffer = nullptr;
+
+ auto releaser = mResources->createReleaser(/*isBuffer=*/true);
+ auto error = mResources->getDisplayClientTarget(
+ display->getId(), clientTarget.buffer, &importedBuffer, releaser.get());
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ return;
+ }
+
+ error = display->setClientTarget(importedBuffer, clientTarget.buffer.fence,
+ clientTarget.dataspace, clientTarget.damage);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ return;
+ }
+}
+
+void ComposerClient::executeDisplayCommandSetOutputBuffer(
+ Display* display, const Buffer& buffer) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ // Owned by mResources.
+ buffer_handle_t importedBuffer = nullptr;
+
+ auto releaser = mResources->createReleaser(/*isBuffer=*/true);
+ auto error = mResources->getDisplayOutputBuffer(
+ display->getId(), buffer, &importedBuffer, releaser.get());
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ return;
+ }
+
+ error = display->setOutputBuffer(importedBuffer, buffer.fence);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ return;
+ }
+}
+
+void ComposerClient::executeDisplayCommandValidateDisplay(
+ Display* display,
+ const std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = display->setExpectedPresentTime(expectedPresentTime);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ }
+
+ DisplayChanges changes;
+
+ error = display->validate(&changes);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ } else {
+ mCommandResults->addChanges(changes);
+ }
+
+ mResources->setDisplayMustValidateState(display->getId(), false);
+}
+
+void ComposerClient::executeDisplayCommandAcceptDisplayChanges(
+ Display* display) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = display->acceptChanges();
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeDisplayCommandPresentOrValidateDisplay(
+ Display* display,
+ const std::optional<ClockMonotonicTimestamp> expectedPresentTime) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ // TODO: Support SKIP_VALIDATE.
+
+ auto error = display->setExpectedPresentTime(expectedPresentTime);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ }
+
+ DisplayChanges changes;
+
+ error = display->validate(&changes);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ } else {
+ const int64_t displayId = display->getId();
+ mCommandResults->addChanges(changes);
+ mCommandResults->addPresentOrValidateResult(
+ displayId, PresentOrValidate::Result::Validated);
+ }
+
+ mResources->setDisplayMustValidateState(display->getId(), false);
+}
+
+void ComposerClient::executeDisplayCommandPresentDisplay(Display* display) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ if (mResources->mustValidateDisplay(display->getId())) {
+ ALOGE("%s: display:%" PRIu64 " not validated", __FUNCTION__,
+ display->getId());
+ mCommandResults->addError(HWC3::Error::NotValidated);
+ return;
+ }
+
+ ::android::base::unique_fd displayFence;
+ std::unordered_map<int64_t, ::android::base::unique_fd> layerFences;
+
+ auto error = display->present(&displayFence, &layerFences);
+ if (error != HWC3::Error::None) {
+ LOG_DISPLAY_COMMAND_ERROR(display, error);
+ mCommandResults->addError(error);
+ } else {
+ const int64_t displayId = display->getId();
+ mCommandResults->addPresentFence(displayId, std::move(displayFence));
+ mCommandResults->addReleaseFences(displayId, std::move(layerFences));
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerCursorPosition(
+ Display* display, Layer* layer, const common::Point& cursorPosition) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setCursorPosition(cursorPosition);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerBuffer(Display* display,
+ Layer* layer,
+ const Buffer& buffer) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ // Owned by mResources.
+ buffer_handle_t importedBuffer = nullptr;
+
+ auto releaser = mResources->createReleaser(/*isBuffer=*/true);
+ auto error =
+ mResources->getLayerBuffer(display->getId(), layer->getId(), buffer,
+ &importedBuffer, releaser.get());
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ return;
+ }
+
+ error = layer->setBuffer(importedBuffer, buffer.fence);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerSurfaceDamage(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<common::Rect>>& damage) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setSurfaceDamage(damage);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerBlendMode(
+ Display* display, Layer* layer, const ParcelableBlendMode& blendMode) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setBlendMode(blendMode.blendMode);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerColor(Display* display,
+ Layer* layer,
+ const Color& color) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setColor(color);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerComposition(
+ Display* display, Layer* layer, const ParcelableComposition& composition) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setCompositionType(composition.composition);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerDataspace(
+ Display* display, Layer* layer, const ParcelableDataspace& dataspace) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setDataspace(dataspace.dataspace);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerDisplayFrame(
+ Display* display, Layer* layer, const common::Rect& rect) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setDisplayFrame(rect);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerPlaneAlpha(
+ Display* display, Layer* layer, const PlaneAlpha& planeAlpha) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setPlaneAlpha(planeAlpha.alpha);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerSidebandStream(
+ Display* display, Layer* layer,
+ const aidl::android::hardware::common::NativeHandle& handle) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ // Owned by mResources.
+ buffer_handle_t importedStream = nullptr;
+
+ auto releaser = mResources->createReleaser(/*isBuffer=*/false);
+ auto error = mResources->getLayerSidebandStream(
+ display->getId(), layer->getId(), handle, &importedStream,
+ releaser.get());
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ return;
+ }
+
+ error = layer->setSidebandStream(importedStream);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerSourceCrop(
+ Display* display, Layer* layer, const common::FRect& sourceCrop) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setSourceCrop(sourceCrop);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerTransform(
+ Display* display, Layer* layer, const ParcelableTransform& transform) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setTransform(transform.transform);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerVisibleRegion(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<common::Rect>>& visibleRegion) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setVisibleRegion(visibleRegion);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerZOrder(Display* display,
+ Layer* layer,
+ const ZOrder& zOrder) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setZOrder(zOrder.z);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerPerFrameMetadata(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<PerFrameMetadata>>& perFrameMetadata) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setPerFrameMetadata(perFrameMetadata);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerColorTransform(
+ Display* display, Layer* layer, const std::vector<float>& colorTransform) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setColorTransform(colorTransform);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerBrightness(
+ Display* display, Layer* layer, const LayerBrightness& brightness) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setBrightness(brightness.brightness);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+void ComposerClient::executeLayerCommandSetLayerPerFrameMetadataBlobs(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<PerFrameMetadataBlob>>&
+ perFrameMetadataBlob) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ auto error = layer->setPerFrameMetadataBlobs(perFrameMetadataBlob);
+ if (error != HWC3::Error::None) {
+ LOG_LAYER_COMMAND_ERROR(display, layer, error);
+ mCommandResults->addError(error);
+ }
+}
+
+Display* ComposerClient::getDisplay(int64_t displayId) {
+ auto it = mDisplays.find(displayId);
+ if (it == mDisplays.end()) {
+ ALOGE("%s: no display:%" PRIu64, __FUNCTION__, displayId);
+ return nullptr;
+ }
+ return it->second.get();
+}
+
+HWC3::Error ComposerClient::createDisplaysLocked() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ if (!mComposer) {
+ ALOGE("%s composer not initialized!", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ std::vector<DisplayMultiConfigs> displays;
+
+ HWC3::Error error = findDisplays(displays);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to find display configs", __FUNCTION__);
+ return error;
+ }
+
+ for (const auto& iter : displays) {
+ error =
+ createDisplayLocked(iter.displayId, iter.activeConfigId, iter.configs);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to create display from config", __FUNCTION__);
+ return error;
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::createDisplayLocked(
+ int64_t displayId, int32_t activeConfigId,
+ const std::vector<DisplayConfig>& configs) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ if (!mComposer) {
+ ALOGE("%s composer not initialized!", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ auto display = std::make_unique<Display>(mComposer, displayId);
+ if (display == nullptr) {
+ ALOGE("%s failed to allocate display", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ HWC3::Error error = display->init(configs, activeConfigId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to initialize display:%" PRIu64, __FUNCTION__, displayId);
+ return error;
+ }
+
+ error = mComposer->onDisplayCreate(display.get());
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to register display:%" PRIu64 " with composer",
+ __FUNCTION__, displayId);
+ return error;
+ }
+
+ DEBUG_LOG("%s: adding display:%" PRIu64, __FUNCTION__, displayId);
+ mDisplays.emplace(displayId, std::move(display));
+
+ error = mResources->addPhysicalDisplay(displayId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to initialize display:%" PRIu64 " resources", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::destroyDisplaysLocked() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ std::vector<int64_t> displayIds;
+ for (const auto& [displayId, _] : mDisplays) {
+ displayIds.push_back(displayId);
+ }
+ for (const int64_t displayId : displayIds) {
+ destroyDisplayLocked(displayId);
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::destroyDisplayLocked(int64_t displayId) {
+ DEBUG_LOG("%s display:%" PRId64, __FUNCTION__, displayId);
+
+ auto it = mDisplays.find(displayId);
+ if (it == mDisplays.end()) {
+ ALOGE("%s: display:%" PRId64 " no such display?", __FUNCTION__, displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ HWC3::Error error = mComposer->onDisplayDestroy(it->second.get());
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " failed to destroy with frame composer",
+ __FUNCTION__, displayId);
+ }
+
+ error = mResources->removeDisplay(displayId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " failed to destroy with resources",
+ __FUNCTION__, displayId);
+ }
+
+ mDisplays.erase(it);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error ComposerClient::handleHotplug(bool connected, uint32_t id,
+ uint32_t width, uint32_t height,
+ uint32_t dpiX, uint32_t dpiY,
+ uint32_t refreshRate) {
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ if (!mCallbacks) {
+ return HWC3::Error::None;
+ }
+
+ const int64_t displayId = static_cast<int64_t>(id);
+
+ Display* display = getDisplay(displayId);
+ if (display != nullptr) {
+ ALOGI("Disconnecting display:%" PRIu64, displayId);
+ mCallbacks->onHotplug(displayId, /*connected=*/false);
+
+ destroyDisplayLocked(displayId);
+ }
+
+ if (connected) {
+ const int32_t configId = static_cast<int32_t>(id);
+ const std::vector<DisplayConfig> configs = {
+ DisplayConfig(configId, static_cast<int>(width),
+ static_cast<int>(height), static_cast<int>(dpiX),
+ static_cast<int>(dpiY), static_cast<int>(refreshRate))};
+
+ createDisplayLocked(displayId, configId, configs);
+
+ ALOGI("Connecting display:%" PRIu32 " w:%" PRIu32 " h:%" PRIu32
+ " dpiX:%" PRIu32 " dpiY %" PRIu32 "fps %" PRIu32,
+ id, width, height, dpiX, dpiY, refreshRate);
+
+ mCallbacks->onHotplug(displayId, /*connected=*/true);
+ }
+
+ return HWC3::Error::None;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/ComposerClient.h b/system/hwc3/ComposerClient.h
new file mode 100644
index 0000000..7e19d2c
--- /dev/null
+++ b/system/hwc3/ComposerClient.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_COMPOSERCLIENT_H
+#define ANDROID_HWC_COMPOSERCLIENT_H
+
+#include <aidl/android/hardware/graphics/composer3/BnComposerClient.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+#include "ComposerResources.h"
+#include "Display.h"
+#include "FrameComposer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class ComposerClient : public BnComposerClient {
+ public:
+ ComposerClient();
+ virtual ~ComposerClient();
+
+ HWC3::Error init();
+
+ void setOnClientDestroyed(std::function<void()> onClientDestroyed) {
+ mOnClientDestroyed = onClientDestroyed;
+ }
+
+ // HWC3 interface:
+ ndk::ScopedAStatus createLayer(int64_t displayId, int32_t bufferSlotCount,
+ int64_t* layer) override;
+ ndk::ScopedAStatus createVirtualDisplay(int32_t width, int32_t height,
+ common::PixelFormat formatHint,
+ int32_t outputBufferSlotCount,
+ VirtualDisplay* display) override;
+ ndk::ScopedAStatus destroyLayer(int64_t displayId, int64_t layer) override;
+ ndk::ScopedAStatus destroyVirtualDisplay(int64_t displayId) override;
+ ndk::ScopedAStatus executeCommands(
+ const std::vector<DisplayCommand>& commands,
+ std::vector<CommandResultPayload>* results) override;
+ ndk::ScopedAStatus getActiveConfig(int64_t displayId,
+ int32_t* config) override;
+ ndk::ScopedAStatus getColorModes(int64_t displayId,
+ std::vector<ColorMode>* colorModes) override;
+ ndk::ScopedAStatus getDataspaceSaturationMatrix(
+ common::Dataspace dataspace, std::vector<float>* matrix) override;
+ ndk::ScopedAStatus getDisplayAttribute(int64_t displayId, int32_t config,
+ DisplayAttribute attribute,
+ int32_t* value) override;
+ ndk::ScopedAStatus getDisplayCapabilities(
+ int64_t displayId, std::vector<DisplayCapability>* caps) override;
+ ndk::ScopedAStatus getDisplayConfigs(int64_t displayId,
+ std::vector<int32_t>* configs) override;
+ ndk::ScopedAStatus getDisplayConnectionType(
+ int64_t displayId, DisplayConnectionType* type) override;
+ ndk::ScopedAStatus getDisplayIdentificationData(
+ int64_t displayId, DisplayIdentification* id) override;
+ ndk::ScopedAStatus getDisplayName(int64_t displayId,
+ std::string* name) override;
+ ndk::ScopedAStatus getDisplayVsyncPeriod(int64_t displayId,
+ int32_t* vsyncPeriod) override;
+ ndk::ScopedAStatus getDisplayedContentSample(
+ int64_t displayId, int64_t maxFrames, int64_t timestamp,
+ DisplayContentSample* samples) override;
+ ndk::ScopedAStatus getDisplayedContentSamplingAttributes(
+ int64_t displayId, DisplayContentSamplingAttributes* attrs) override;
+ ndk::ScopedAStatus getDisplayPhysicalOrientation(
+ int64_t displayId, common::Transform* orientation) override;
+ ndk::ScopedAStatus getHdrCapabilities(int64_t displayId,
+ HdrCapabilities* caps) override;
+ ndk::ScopedAStatus getMaxVirtualDisplayCount(int32_t* count) override;
+ ndk::ScopedAStatus getPerFrameMetadataKeys(
+ int64_t displayId, std::vector<PerFrameMetadataKey>* keys) override;
+ ndk::ScopedAStatus getReadbackBufferAttributes(
+ int64_t displayId, ReadbackBufferAttributes* attrs) override;
+ ndk::ScopedAStatus getReadbackBufferFence(
+ int64_t displayId, ndk::ScopedFileDescriptor* acquireFence) override;
+ ndk::ScopedAStatus getRenderIntents(
+ int64_t displayId, ColorMode mode,
+ std::vector<RenderIntent>* intents) override;
+ ndk::ScopedAStatus getSupportedContentTypes(
+ int64_t displayId, std::vector<ContentType>* types) override;
+ ndk::ScopedAStatus getDisplayDecorationSupport(
+ int64_t displayId,
+ std::optional<common::DisplayDecorationSupport>* support) override;
+ ndk::ScopedAStatus registerCallback(
+ const std::shared_ptr<IComposerCallback>& callback) override;
+ ndk::ScopedAStatus setActiveConfig(int64_t displayId,
+ int32_t config) override;
+ ndk::ScopedAStatus setActiveConfigWithConstraints(
+ int64_t displayId, int32_t config,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* timeline) override;
+ ndk::ScopedAStatus setBootDisplayConfig(int64_t displayId,
+ int32_t config) override;
+ ndk::ScopedAStatus clearBootDisplayConfig(int64_t displayId) override;
+ ndk::ScopedAStatus getPreferredBootDisplayConfig(int64_t displayId,
+ int32_t* config) override;
+ ndk::ScopedAStatus setAutoLowLatencyMode(int64_t displayId, bool on) override;
+ ndk::ScopedAStatus setClientTargetSlotCount(int64_t displayId,
+ int32_t count) override;
+ ndk::ScopedAStatus setColorMode(int64_t displayId, ColorMode mode,
+ RenderIntent intent) override;
+ ndk::ScopedAStatus setContentType(int64_t displayId,
+ ContentType type) override;
+ ndk::ScopedAStatus setDisplayedContentSamplingEnabled(
+ int64_t displayId, bool enable, FormatColorComponent componentMask,
+ int64_t maxFrames) override;
+ ndk::ScopedAStatus setPowerMode(int64_t displayId, PowerMode mode) override;
+ ndk::ScopedAStatus setReadbackBuffer(
+ int64_t displayId,
+ const aidl::android::hardware::common::NativeHandle& buffer,
+ const ndk::ScopedFileDescriptor& releaseFence) override;
+ ndk::ScopedAStatus setVsyncEnabled(int64_t displayId, bool enabled) override;
+ ndk::ScopedAStatus setIdleTimerEnabled(int64_t displayId,
+ int32_t timeoutMs) override;
+
+ protected:
+ ndk::SpAIBinder createBinder() override;
+
+ private:
+ class CommandResultWriter;
+
+ void executeDisplayCommand(const DisplayCommand& displayCommand);
+ void executeLayerCommand(Display* display, const LayerCommand& layerCommand);
+
+ void executeDisplayCommandSetColorTransform(Display* display,
+ const std::vector<float>& matrix);
+ void executeDisplayCommandSetBrightness(Display* display,
+ const DisplayBrightness& brightness);
+ void executeDisplayCommandSetClientTarget(Display* display,
+ const ClientTarget& command);
+ void executeDisplayCommandSetOutputBuffer(Display* display,
+ const Buffer& buffer);
+ void executeDisplayCommandValidateDisplay(
+ Display* display,
+ const std::optional<ClockMonotonicTimestamp> expectedPresentTime);
+ void executeDisplayCommandAcceptDisplayChanges(Display* display);
+ void executeDisplayCommandPresentOrValidateDisplay(
+ Display* display,
+ const std::optional<ClockMonotonicTimestamp> expectedPresentTime);
+ void executeDisplayCommandPresentDisplay(Display* display);
+
+ void executeLayerCommandSetLayerCursorPosition(
+ Display* display, Layer* layer, const common::Point& cursorPosition);
+ void executeLayerCommandSetLayerBuffer(Display* display, Layer* layer,
+ const Buffer& buffer);
+ void executeLayerCommandSetLayerSurfaceDamage(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<common::Rect>>& damage);
+ void executeLayerCommandSetLayerBlendMode(
+ Display* display, Layer* layer, const ParcelableBlendMode& blendMode);
+ void executeLayerCommandSetLayerColor(Display* display, Layer* layer,
+ const Color& color);
+ void executeLayerCommandSetLayerComposition(
+ Display* display, Layer* layer, const ParcelableComposition& composition);
+ void executeLayerCommandSetLayerDataspace(
+ Display* display, Layer* layer, const ParcelableDataspace& dataspace);
+ void executeLayerCommandSetLayerDisplayFrame(Display* display, Layer* layer,
+ const common::Rect& rect);
+ void executeLayerCommandSetLayerPlaneAlpha(Display* display, Layer* layer,
+ const PlaneAlpha& planeAlpha);
+ void executeLayerCommandSetLayerSidebandStream(
+ Display* display, Layer* layer,
+ const aidl::android::hardware::common::NativeHandle& sidebandStream);
+ void executeLayerCommandSetLayerSourceCrop(Display* display, Layer* layer,
+ const common::FRect& sourceCrop);
+ void executeLayerCommandSetLayerTransform(
+ Display* display, Layer* layer, const ParcelableTransform& transform);
+ void executeLayerCommandSetLayerVisibleRegion(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<common::Rect>>& visibleRegion);
+ void executeLayerCommandSetLayerZOrder(Display* display, Layer* layer,
+ const ZOrder& zOrder);
+ void executeLayerCommandSetLayerPerFrameMetadata(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<PerFrameMetadata>>& perFrameMetadata);
+ void executeLayerCommandSetLayerColorTransform(
+ Display* display, Layer* layer, const std::vector<float>& colorTransform);
+ void executeLayerCommandSetLayerBrightness(Display* display, Layer* layer,
+ const LayerBrightness& brightness);
+ void executeLayerCommandSetLayerPerFrameMetadataBlobs(
+ Display* display, Layer* layer,
+ const std::vector<std::optional<PerFrameMetadataBlob>>&
+ perFrameMetadataBlob);
+
+ // Returns the display with the given id or nullptr if not found.
+ Display* getDisplay(int64_t displayId);
+
+ // Finds the Cuttlefish/Goldfish specific configuration and initializes the
+ // displays.
+ HWC3::Error createDisplaysLocked();
+
+ // Creates a display with the given properties.
+ HWC3::Error createDisplayLocked(int64_t displayId, int32_t activeConfigId,
+ const std::vector<DisplayConfig>& configs);
+
+ HWC3::Error destroyDisplaysLocked();
+
+ HWC3::Error destroyDisplayLocked(int64_t displayId);
+
+ HWC3::Error handleHotplug(bool connected, //
+ uint32_t id, //
+ uint32_t width, //
+ uint32_t height, //
+ uint32_t dpiX, //
+ uint32_t dpiY, //
+ uint32_t refreshRate);
+
+ std::mutex mStateMutex;
+
+ std::map<int64_t, std::unique_ptr<Display>> mDisplays;
+
+ // The onHotplug(), onVsync(), etc callbacks registered by SurfaceFlinger.
+ std::shared_ptr<IComposerCallback> mCallbacks;
+
+ std::function<void()> mOnClientDestroyed;
+
+ // Underlying interface for composing layers in the guest using libyuv or in
+ // the host using opengl. Owned by Device.
+ FrameComposer* mComposer = nullptr;
+
+ // For the duration of a executeCommands(), the helper used to collect
+ // individual command results.
+ std::unique_ptr<CommandResultWriter> mCommandResults;
+
+ // Manages importing and caching gralloc buffers for displays and layers.
+ std::unique_ptr<ComposerResources> mResources;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/ComposerResources.cpp b/system/hwc3/ComposerResources.cpp
new file mode 100644
index 0000000..2e17c2c
--- /dev/null
+++ b/system/hwc3/ComposerResources.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 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 "ComposerResources.h"
+
+#include <aidlcommonsupport/NativeHandle.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+HWC3::Error toHwc3Error(
+ ::android::hardware::graphics::composer::V2_1::Error error) {
+ switch (error) {
+ case ::android::hardware::graphics::composer::V2_1::Error::NONE:
+ return HWC3::Error::None;
+ case ::android::hardware::graphics::composer::V2_1::Error::BAD_CONFIG:
+ return HWC3::Error::BadConfig;
+ case ::android::hardware::graphics::composer::V2_1::Error::BAD_DISPLAY:
+ return HWC3::Error::BadDisplay;
+ case ::android::hardware::graphics::composer::V2_1::Error::BAD_LAYER:
+ return HWC3::Error::BadLayer;
+ case ::android::hardware::graphics::composer::V2_1::Error::BAD_PARAMETER:
+ return HWC3::Error::BadParameter;
+ case ::android::hardware::graphics::composer::V2_1::Error::NO_RESOURCES:
+ return HWC3::Error::NoResources;
+ case ::android::hardware::graphics::composer::V2_1::Error::NOT_VALIDATED:
+ return HWC3::Error::NotValidated;
+ case ::android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED:
+ return HWC3::Error::Unsupported;
+ }
+}
+
+::android::hardware::graphics::composer::V2_1::Display toHwc2Display(
+ int64_t displayId) {
+ return static_cast<::android::hardware::graphics::composer::V2_1::Display>(
+ displayId);
+}
+
+::android::hardware::graphics::composer::V2_1::Layer toHwc2Layer(
+ int64_t layerId) {
+ return static_cast<::android::hardware::graphics::composer::V2_1::Layer>(
+ layerId);
+}
+
+} // namespace
+
+std::unique_ptr<ComposerResourceReleaser> ComposerResources::createReleaser(
+ bool isBuffer) {
+ return std::make_unique<ComposerResourceReleaser>(isBuffer);
+}
+
+HWC3::Error ComposerResources::init() {
+ mImpl = ::android::hardware::graphics::composer::V2_2::hal::
+ ComposerResources::create();
+ if (!mImpl) {
+ ALOGE("%s: failed to create underlying ComposerResources.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ return HWC3::Error::None;
+}
+
+void ComposerResources::clear(
+ ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
+ RemoveDisplay removeDisplay) {
+ mImpl->clear(removeDisplay);
+}
+
+bool ComposerResources::hasDisplay(int64_t displayId) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return mImpl->hasDisplay(display);
+}
+
+HWC3::Error ComposerResources::addPhysicalDisplay(int64_t displayId) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, displayId);
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return toHwc3Error(mImpl->addPhysicalDisplay(display));
+}
+
+HWC3::Error ComposerResources::addVirtualDisplay(
+ int64_t displayId, uint32_t outputBufferCacheSize) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return toHwc3Error(mImpl->addVirtualDisplay(display, outputBufferCacheSize));
+}
+
+HWC3::Error ComposerResources::removeDisplay(int64_t displayId) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return toHwc3Error(mImpl->removeDisplay(display));
+}
+
+HWC3::Error ComposerResources::setDisplayClientTargetCacheSize(
+ int64_t displayId, uint32_t clientTargetCacheSize) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return toHwc3Error(
+ mImpl->setDisplayClientTargetCacheSize(display, clientTargetCacheSize));
+}
+
+HWC3::Error ComposerResources::getDisplayClientTargetCacheSize(
+ int64_t displayId, size_t* outCacheSize) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return toHwc3Error(
+ mImpl->getDisplayClientTargetCacheSize(display, outCacheSize));
+}
+
+HWC3::Error ComposerResources::getDisplayOutputBufferCacheSize(
+ int64_t displayId, size_t* outCacheSize) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return toHwc3Error(
+ mImpl->getDisplayOutputBufferCacheSize(display, outCacheSize));
+}
+
+HWC3::Error ComposerResources::addLayer(int64_t displayId, int64_t layerId,
+ uint32_t bufferCacheSize) {
+ DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+ layerId);
+
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ ::android::hardware::graphics::composer::V2_1::Layer layer =
+ toHwc2Layer(layerId);
+ return toHwc3Error(mImpl->addLayer(display, layer, bufferCacheSize));
+}
+
+HWC3::Error ComposerResources::removeLayer(int64_t displayId, int64_t layerId) {
+ DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+ layerId);
+
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ ::android::hardware::graphics::composer::V2_1::Layer layer =
+ toHwc2Layer(layerId);
+
+ return toHwc3Error(mImpl->removeLayer(display, layer));
+}
+
+void ComposerResources::setDisplayMustValidateState(int64_t displayId,
+ bool mustValidate) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ mImpl->setDisplayMustValidateState(display, mustValidate);
+}
+
+bool ComposerResources::mustValidateDisplay(int64_t displayId) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return mImpl->mustValidateDisplay(display);
+}
+
+HWC3::Error ComposerResources::getDisplayReadbackBuffer(
+ int64_t displayId,
+ const aidl::android::hardware::common::NativeHandle& handle,
+ buffer_handle_t* outHandle, ComposerResourceReleaser* releaser) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ return toHwc3Error(mImpl->getDisplayReadbackBuffer(
+ display, ::android::makeFromAidl(handle), outHandle,
+ releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getDisplayClientTarget(
+ int64_t displayId, const Buffer& buffer, buffer_handle_t* outHandle,
+ ComposerResourceReleaser* releaser) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+
+ const bool useCache = !buffer.handle.has_value();
+
+ buffer_handle_t bufferHandle = nullptr;
+ if (buffer.handle.has_value()) {
+ bufferHandle = ::android::makeFromAidl(*buffer.handle);
+ }
+
+ return toHwc3Error(mImpl->getDisplayClientTarget(
+ display, buffer.slot, useCache, bufferHandle, outHandle,
+ releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getDisplayOutputBuffer(
+ int64_t displayId, const Buffer& buffer, buffer_handle_t* outHandle,
+ ComposerResourceReleaser* releaser) {
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+
+ const bool useCache = !buffer.handle.has_value();
+
+ buffer_handle_t bufferHandle = nullptr;
+ if (buffer.handle.has_value()) {
+ bufferHandle = ::android::makeFromAidl(*buffer.handle);
+ }
+
+ return toHwc3Error(mImpl->getDisplayOutputBuffer(
+ display, buffer.slot, useCache, bufferHandle, outHandle,
+ releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getLayerBuffer(
+ int64_t displayId, int64_t layerId, const Buffer& buffer,
+ buffer_handle_t* outHandle, ComposerResourceReleaser* releaser) {
+ DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+ layerId);
+
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ ::android::hardware::graphics::composer::V2_1::Layer layer =
+ toHwc2Layer(layerId);
+
+ const bool useCache = !buffer.handle.has_value();
+
+ buffer_handle_t bufferHandle = nullptr;
+ if (buffer.handle.has_value()) {
+ bufferHandle = ::android::makeFromAidl(*buffer.handle);
+ }
+
+ DEBUG_LOG("%s fromCache:%s", __FUNCTION__, (useCache ? "yes" : "no"));
+ return toHwc3Error(mImpl->getLayerBuffer(display, layer, buffer.slot,
+ useCache, bufferHandle, outHandle,
+ releaser->getReplacedHandle()));
+}
+
+HWC3::Error ComposerResources::getLayerSidebandStream(
+ int64_t displayId, int64_t layerId,
+ const aidl::android::hardware::common::NativeHandle& handle,
+ buffer_handle_t* outHandle, ComposerResourceReleaser* releaser) {
+ DEBUG_LOG("%s: display:%" PRId64 " layer:%" PRId64, __FUNCTION__, displayId,
+ layerId);
+
+ ::android::hardware::graphics::composer::V2_1::Display display =
+ toHwc2Display(displayId);
+ ::android::hardware::graphics::composer::V2_1::Layer layer =
+ toHwc2Layer(layerId);
+ return toHwc3Error(mImpl->getLayerSidebandStream(
+ display, layer, ::android::makeFromAidl(handle), outHandle,
+ releaser->getReplacedHandle()));
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/ComposerResources.h b/system/hwc3/ComposerResources.h
new file mode 100644
index 0000000..faf6a4d
--- /dev/null
+++ b/system/hwc3/ComposerResources.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// Thin wrappers around V2_2::hal::ComposerResources related classes that
+// return HWC3 error codes and accept HWC3 argument types.
+
+#ifndef ANDROID_HWC_COMPOSERRESOURCES_H
+#define ANDROID_HWC_COMPOSERRESOURCES_H
+
+// Must include our LOG_TAG first:
+// clang-format off
+#include "Common.h"
+#include <composer-resources/2.2/ComposerResources.h>
+// clang-format on
+
+#include <memory>
+#include <optional>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class ComposerResourceReleaser {
+ public:
+ ComposerResourceReleaser(bool isBuffer) : mReplacedHandle(isBuffer) {}
+ virtual ~ComposerResourceReleaser() = default;
+
+ ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
+ ReplacedHandle*
+ getReplacedHandle() {
+ return &mReplacedHandle;
+ }
+
+ private:
+ ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
+ ReplacedHandle mReplacedHandle;
+};
+
+class ComposerResources {
+ public:
+ ComposerResources() = default;
+
+ HWC3::Error init();
+
+ std::unique_ptr<ComposerResourceReleaser> createReleaser(bool isBuffer);
+
+ void clear(::android::hardware::graphics::composer::V2_2::hal::
+ ComposerResources::RemoveDisplay removeDisplay);
+
+ bool hasDisplay(int64_t display);
+
+ HWC3::Error addPhysicalDisplay(int64_t display);
+
+ HWC3::Error addVirtualDisplay(int64_t displayId,
+ uint32_t outputBufferCacheSize);
+
+ HWC3::Error removeDisplay(int64_t display);
+
+ HWC3::Error setDisplayClientTargetCacheSize(int64_t displayId,
+ uint32_t clientTargetCacheSize);
+
+ HWC3::Error getDisplayClientTargetCacheSize(int64_t displayId,
+ size_t* outCacheSize);
+
+ HWC3::Error getDisplayOutputBufferCacheSize(int64_t displayId,
+ size_t* outCacheSize);
+
+ HWC3::Error addLayer(int64_t displayId, int64_t layerId,
+ uint32_t bufferCacheSize);
+
+ HWC3::Error removeLayer(int64_t displayId, int64_t layer);
+
+ void setDisplayMustValidateState(int64_t displayId, bool mustValidate);
+
+ bool mustValidateDisplay(int64_t displayId);
+
+ HWC3::Error getDisplayReadbackBuffer(
+ int64_t displayId,
+ const aidl::android::hardware::common::NativeHandle& handle,
+ buffer_handle_t* outHandle, ComposerResourceReleaser* bufReleaser);
+
+ HWC3::Error getDisplayClientTarget(int64_t displayId, const Buffer& buffer,
+ buffer_handle_t* outHandle,
+ ComposerResourceReleaser* bufReleaser);
+
+ HWC3::Error getDisplayOutputBuffer(int64_t displayId, const Buffer& buffer,
+ buffer_handle_t* outHandle,
+ ComposerResourceReleaser* bufReleaser);
+
+ HWC3::Error getLayerBuffer(int64_t displayId, int64_t layerId,
+ const Buffer& buffer,
+ buffer_handle_t* outBufferHandle,
+ ComposerResourceReleaser* bufReleaser);
+
+ HWC3::Error getLayerSidebandStream(
+ int64_t displayId, int64_t layerId,
+ const aidl::android::hardware::common::NativeHandle& rawHandle,
+ buffer_handle_t* outStreamHandle, ComposerResourceReleaser* bufReleaser);
+
+ private:
+ std::unique_ptr<
+ ::android::hardware::graphics::composer::V2_2::hal::ComposerResources>
+ mImpl;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/Device.cpp b/system/hwc3/Device.cpp
new file mode 100644
index 0000000..cf4be42
--- /dev/null
+++ b/system/hwc3/Device.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 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 "Device.h"
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <json/json.h>
+
+#include "ClientFrameComposer.h"
+#include "FrameComposer.h"
+#include "GuestFrameComposer.h"
+#include "HostFrameComposer.h"
+#include "NoOpFrameComposer.h"
+
+ANDROID_SINGLETON_STATIC_INSTANCE(
+ aidl::android::hardware::graphics::composer3::impl::Device);
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+bool shouldUseGuestComposer() {
+ return ::android::base::GetProperty("ro.hardware.vulkan", "") == "pastel";
+}
+
+std::string getPmemPath() {
+ return ::android::base::GetProperty("ro.vendor.hwcomposer.pmem", "");
+}
+
+HWC3::Error loadPersistentKeyValues(Json::Value* dictionary) {
+ *dictionary = Json::Value(Json::ValueType::objectValue);
+
+ const std::string path = getPmemPath();
+ if (path.empty()) {
+ ALOGE("%s: persistent key-value store path not available.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ std::string content;
+ if (!::android::base::ReadFileToString(path, &content)) {
+ ALOGE("%s: failed to read key-value store from %s", __FUNCTION__,
+ path.c_str());
+ return HWC3::Error::NoResources;
+ }
+
+ if (content.empty() || content[0] == '\0') {
+ return HWC3::Error::None;
+ }
+
+ Json::Reader reader;
+ if (!reader.parse(content, *dictionary)) {
+ const std::string error = reader.getFormattedErrorMessages();
+ ALOGE("%s: failed to parse key-value store from %s:%s", __FUNCTION__,
+ path.c_str(), error.c_str());
+ return HWC3::Error::NoResources;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error savePersistentKeyValues(const Json::Value& dictionary) {
+ const std::string path = getPmemPath();
+ if (path.empty()) {
+ ALOGE("%s: persistent key-value store path not available.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ const std::string contents = dictionary.toStyledString();
+ if (!::android::base::WriteStringToFile(contents, path)) {
+ ALOGE("%s: failed to write key-value store to %s", __FUNCTION__,
+ path.c_str());
+ return HWC3::Error::NoResources;
+ }
+
+ return HWC3::Error::None;
+}
+
+} // namespace
+
+HWC3::Error Device::getComposer(FrameComposer** outComposer) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ if (mComposer == nullptr) {
+ if (IsNoOpMode()) {
+ DEBUG_LOG("%s: using NoOpFrameComposer", __FUNCTION__);
+ mComposer = std::make_unique<NoOpFrameComposer>();
+ } else if (IsClientCompositionMode()) {
+ DEBUG_LOG("%s: using ClientFrameComposer", __FUNCTION__);
+ mComposer = std::make_unique<ClientFrameComposer>();
+ } else if (shouldUseGuestComposer()) {
+ DEBUG_LOG("%s: using GuestFrameComposer", __FUNCTION__);
+ mComposer = std::make_unique<GuestFrameComposer>();
+ } else {
+ DEBUG_LOG("%s: using HostFrameComposer", __FUNCTION__);
+ mComposer = std::make_unique<HostFrameComposer>();
+ }
+ if (!mComposer) {
+ ALOGE("%s failed to allocate FrameComposer", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ HWC3::Error error = mComposer->init();
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to init FrameComposer", __FUNCTION__);
+ return error;
+ }
+ }
+
+ *outComposer = mComposer.get();
+ return HWC3::Error::None;
+}
+
+HWC3::Error Device::getPersistentKeyValue(const std::string& key,
+ const std::string& defaultValue,
+ std::string* outValue) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ Json::Value dictionary;
+
+ HWC3::Error error = loadPersistentKeyValues(&dictionary);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to load pmem json", __FUNCTION__);
+ return error;
+ }
+
+ if (!dictionary.isMember(key)) {
+ *outValue = defaultValue;
+ return HWC3::Error::None;
+ }
+
+ *outValue = defaultValue;
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Device::setPersistentKeyValue(const std::string& key,
+ const std::string& value) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ Json::Value dictionary;
+
+ HWC3::Error error = loadPersistentKeyValues(&dictionary);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to load pmem json", __FUNCTION__);
+ return error;
+ }
+
+ dictionary[key] = value;
+
+ error = savePersistentKeyValues(dictionary);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to save pmem json", __FUNCTION__);
+ return error;
+ }
+
+ return HWC3::Error::None;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Device.h b/system/hwc3/Device.h
new file mode 100644
index 0000000..6a3d064
--- /dev/null
+++ b/system/hwc3/Device.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_DEVICE_H
+#define ANDROID_HWC_DEVICE_H
+
+#include <utils/Singleton.h>
+
+#include <memory>
+#include <thread>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class FrameComposer;
+
+// Provides resources that are stable for the duration of the virtual
+// device.
+class Device : public ::android::Singleton<Device> {
+ public:
+ virtual ~Device() = default;
+
+ HWC3::Error getComposer(FrameComposer** outComposer);
+
+ HWC3::Error getPersistentKeyValue(const std::string& key,
+ const std::string& defaultVal,
+ std::string* outValue);
+
+ HWC3::Error setPersistentKeyValue(const std::string& key,
+ const std::string& outValue);
+
+ private:
+ friend class Singleton<Device>;
+ Device() = default;
+
+ std::mutex mMutex;
+ std::unique_ptr<FrameComposer> mComposer;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/Display.cpp b/system/hwc3/Display.cpp
new file mode 100644
index 0000000..2281e49
--- /dev/null
+++ b/system/hwc3/Display.cpp
@@ -0,0 +1,1050 @@
+/*
+ * Copyright 2022 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 "Display.h"
+
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sync/sync.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <atomic>
+#include <numeric>
+#include <sstream>
+#include <thread>
+
+#include "Common.h"
+#include "Device.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+bool isValidColorMode(ColorMode mode) {
+ switch (mode) {
+ case ColorMode::NATIVE:
+ case ColorMode::STANDARD_BT601_625:
+ case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+ case ColorMode::STANDARD_BT601_525:
+ case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+ case ColorMode::STANDARD_BT709:
+ case ColorMode::DCI_P3:
+ case ColorMode::SRGB:
+ case ColorMode::ADOBE_RGB:
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::BT2020:
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ case ColorMode::DISPLAY_BT2020:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isValidRenderIntent(RenderIntent intent) {
+ switch (intent) {
+ case RenderIntent::COLORIMETRIC:
+ case RenderIntent::ENHANCE:
+ case RenderIntent::TONE_MAP_COLORIMETRIC:
+ case RenderIntent::TONE_MAP_ENHANCE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isValidPowerMode(PowerMode mode) {
+ switch (mode) {
+ case PowerMode::OFF:
+ case PowerMode::DOZE:
+ case PowerMode::DOZE_SUSPEND:
+ case PowerMode::ON:
+ case PowerMode::ON_SUSPEND:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace
+
+Display::Display(FrameComposer* composer, int64_t id)
+ : mComposer(composer), mId(id), mVsyncThread(id) {
+ setLegacyEdid();
+}
+
+Display::~Display() {}
+
+HWC3::Error Display::init(const std::vector<DisplayConfig>& configs,
+ int32_t activeConfigId,
+ const std::optional<std::vector<uint8_t>>& edid) {
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ for (const DisplayConfig& config : configs) {
+ mConfigs.emplace(config.getId(), config);
+ }
+
+ mActiveConfigId = activeConfigId;
+
+ auto bootConfigIdOpt = getBootConfigId();
+ if (bootConfigIdOpt) {
+ mActiveConfigId = *bootConfigIdOpt;
+ }
+
+ if (edid.has_value()) {
+ mEdid = *edid;
+ }
+
+ auto it = mConfigs.find(activeConfigId);
+ if (it == mConfigs.end()) {
+ ALOGE("%s: display:%" PRId64 "missing config:%" PRId32, __FUNCTION__, mId,
+ activeConfigId);
+ return HWC3::Error::NoResources;
+ }
+
+ const auto& activeConfig = it->second;
+ const auto activeConfigString = activeConfig.toString();
+ ALOGD("%s display:%" PRId64 " with config:%s", __FUNCTION__, mId,
+ activeConfigString.c_str());
+
+ mVsyncThread.start(activeConfig.getVsyncPeriod());
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::updateParameters(
+ uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY,
+ uint32_t refreshRateHz, const std::optional<std::vector<uint8_t>>& edid) {
+ DEBUG_LOG("%s: updating display:%" PRId64
+ " width:%d height:%d dpiX:%d dpiY:%d refreshRateHz:%d",
+ __FUNCTION__, mId, width, height, dpiX, dpiY, refreshRateHz);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto it = mConfigs.find(*mActiveConfigId);
+ if (it == mConfigs.end()) {
+ ALOGE("%s: failed to find config %" PRId32, __func__, *mActiveConfigId);
+ return HWC3::Error::NoResources;
+ }
+ it->second.setAttribute(DisplayAttribute::VSYNC_PERIOD,
+ 1000 * 1000 * 1000 / refreshRateHz);
+ it->second.setAttribute(DisplayAttribute::WIDTH, width);
+ it->second.setAttribute(DisplayAttribute::HEIGHT, height);
+ it->second.setAttribute(DisplayAttribute::DPI_X, dpiX);
+ it->second.setAttribute(DisplayAttribute::DPI_Y, dpiY);
+
+ if (edid.has_value()) {
+ mEdid = *edid;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::createLayer(int64_t* outLayerId) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto layer = std::make_unique<Layer>();
+
+ const int64_t layerId = layer->getId();
+ DEBUG_LOG("%s: created layer:%" PRId64, __FUNCTION__, layerId);
+
+ mLayers.emplace(layerId, std::move(layer));
+
+ *outLayerId = layerId;
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::destroyLayer(int64_t layerId) {
+ DEBUG_LOG("%s: destroy layer:%" PRId64, __FUNCTION__, layerId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto it = mLayers.find(layerId);
+ if (it == mLayers.end()) {
+ ALOGE("%s display:%" PRId64 " has no such layer:%." PRId64, __FUNCTION__,
+ mId, layerId);
+ return HWC3::Error::BadLayer;
+ }
+
+ mOrderedLayers.erase(std::remove_if(mOrderedLayers.begin(), //
+ mOrderedLayers.end(), //
+ [layerId](Layer* layer) {
+ return layer->getId() == layerId;
+ }),
+ mOrderedLayers.end());
+
+ mLayers.erase(it);
+
+ DEBUG_LOG("%s: destroyed layer:%" PRId64, __FUNCTION__, layerId);
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getActiveConfig(int32_t* outConfig) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mActiveConfigId) {
+ ALOGW("%s: display:%" PRId64 " has no active config.", __FUNCTION__, mId);
+ return HWC3::Error::BadConfig;
+ }
+
+ *outConfig = *mActiveConfigId;
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayAttribute(int32_t configId,
+ DisplayAttribute attribute,
+ int32_t* outValue) {
+ auto attributeString = toString(attribute);
+ DEBUG_LOG("%s: display:%" PRId64 " attribute:%s", __FUNCTION__, mId,
+ attributeString.c_str());
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto it = mConfigs.find(configId);
+ if (it == mConfigs.end()) {
+ ALOGW("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId,
+ configId);
+ return HWC3::Error::BadConfig;
+ }
+
+ const DisplayConfig& config = it->second;
+ *outValue = config.getAttribute(attribute);
+ DEBUG_LOG("%s: display:%" PRId64 " attribute:%s value is %" PRIi32,
+ __FUNCTION__, mId, attributeString.c_str(), *outValue);
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getColorModes(std::vector<ColorMode>* outModes) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ outModes->clear();
+ outModes->insert(outModes->end(), mColorModes.begin(), mColorModes.end());
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayCapabilities(
+ std::vector<DisplayCapability>* outCapabilities) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ outCapabilities->clear();
+ outCapabilities->push_back(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayConfigs(std::vector<int32_t>* outConfigIds) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ outConfigIds->clear();
+ outConfigIds->reserve(mConfigs.size());
+ for (const auto& [configId, _] : mConfigs) {
+ outConfigIds->push_back(configId);
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayConnectionType(DisplayConnectionType* outType) {
+ if (IsCuttlefishFoldable()) {
+ // Workaround to force all displays to INTERNAL for cf_x86_64_foldable.
+ // TODO(b/193568008): Allow configuring internal/external per display.
+ *outType = DisplayConnectionType::INTERNAL;
+ } else {
+ // Other devices default to the first display INTERNAL, others EXTERNAL.
+ *outType = mId == 0 ? DisplayConnectionType::INTERNAL
+ : DisplayConnectionType::EXTERNAL;
+ }
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayIdentificationData(
+ DisplayIdentification* outIdentification) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ if (outIdentification == nullptr) {
+ return HWC3::Error::BadParameter;
+ }
+
+ outIdentification->port = mId;
+ outIdentification->data = mEdid;
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayName(std::string* outName) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ *outName = mName;
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayVsyncPeriod(int32_t* outVsyncPeriod) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (!mActiveConfigId) {
+ ALOGE("%s : display:%" PRId64 " no active config", __FUNCTION__, mId);
+ return HWC3::Error::BadConfig;
+ }
+
+ const auto it = mConfigs.find(*mActiveConfigId);
+ if (it == mConfigs.end()) {
+ ALOGE("%s : display:%" PRId64 " failed to find active config:%" PRId32,
+ __FUNCTION__, mId, *mActiveConfigId);
+ return HWC3::Error::BadConfig;
+ }
+ const DisplayConfig& activeConfig = it->second;
+
+ *outVsyncPeriod = activeConfig.getAttribute(DisplayAttribute::VSYNC_PERIOD);
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDisplayedContentSample(
+ int64_t /*maxFrames*/, int64_t /*timestamp*/,
+ DisplayContentSample* /*samples*/) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getDisplayedContentSamplingAttributes(
+ DisplayContentSamplingAttributes* /*outAttributes*/) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getDisplayPhysicalOrientation(
+ common::Transform* outOrientation) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ *outOrientation = common::Transform::NONE;
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getHdrCapabilities(HdrCapabilities* outCapabilities) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ // No supported types.
+ outCapabilities->types.clear();
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getPerFrameMetadataKeys(
+ std::vector<PerFrameMetadataKey>* outKeys) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ outKeys->clear();
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getReadbackBufferAttributes(
+ ReadbackBufferAttributes* outAttributes) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ outAttributes->format = common::PixelFormat::RGBA_8888;
+ outAttributes->dataspace = common::Dataspace::UNKNOWN;
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getReadbackBufferFence(
+ ndk::ScopedFileDescriptor* /*outAcquireFence*/) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::getRenderIntents(ColorMode mode,
+ std::vector<RenderIntent>* outIntents) {
+ const auto modeString = toString(mode);
+ DEBUG_LOG("%s: display:%" PRId64 "for mode:%s", __FUNCTION__, mId,
+ modeString.c_str());
+
+ outIntents->clear();
+
+ if (!isValidColorMode(mode)) {
+ DEBUG_LOG("%s: display:%" PRId64 "invalid mode:%s", __FUNCTION__, mId,
+ modeString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+
+ outIntents->push_back(RenderIntent::COLORIMETRIC);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getSupportedContentTypes(
+ std::vector<ContentType>* outTypes) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ outTypes->clear();
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getDecorationSupport(
+ std::optional<common::DisplayDecorationSupport>* outSupport) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ outSupport->reset();
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::registerCallback(
+ const std::shared_ptr<IComposerCallback>& callback) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ mVsyncThread.setCallbacks(callback);
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setActiveConfig(int32_t configId) {
+ DEBUG_LOG("%s: display:%" PRId64 " setting active config to %" PRId32,
+ __FUNCTION__, mId, configId);
+
+ VsyncPeriodChangeConstraints constraints;
+ constraints.desiredTimeNanos = 0;
+ constraints.seamlessRequired = false;
+
+ VsyncPeriodChangeTimeline timeline;
+
+ return setActiveConfigWithConstraints(configId, constraints, &timeline);
+}
+
+HWC3::Error Display::setActiveConfigWithConstraints(
+ int32_t configId, const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
+ DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32, __FUNCTION__, mId,
+ configId);
+
+ if (outTimeline == nullptr) {
+ return HWC3::Error::BadParameter;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (mActiveConfigId == configId) {
+ return HWC3::Error::None;
+ }
+
+ DisplayConfig* newConfig = getConfig(configId);
+ if (newConfig == nullptr) {
+ ALOGE("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId,
+ configId);
+ return HWC3::Error::BadConfig;
+ }
+
+ if (constraints.seamlessRequired) {
+ if (mActiveConfigId) {
+ DisplayConfig* oldConfig = getConfig(*mActiveConfigId);
+ if (oldConfig == nullptr) {
+ ALOGE("%s: display:%" PRId64 " missing config:%" PRId32, __FUNCTION__,
+ mId, *mActiveConfigId);
+ return HWC3::Error::NoResources;
+ }
+
+ const int32_t newConfigGroup = newConfig->getConfigGroup();
+ const int32_t oldConfigGroup = oldConfig->getConfigGroup();
+ if (newConfigGroup != oldConfigGroup) {
+ DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32
+ " seamless not supported between different config groups "
+ "old:%d vs new:%d",
+ __FUNCTION__, mId, configId, oldConfigGroup, newConfigGroup);
+ return HWC3::Error::SeamlessNotAllowed;
+ }
+ }
+ }
+
+ mActiveConfigId = configId;
+
+ if (mComposer == nullptr) {
+ ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId);
+ return HWC3::Error::NoResources;
+ }
+
+ HWC3::Error error = mComposer->onActiveConfigChange(this);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " composer failed to handle config change",
+ __FUNCTION__, mId);
+ return error;
+ }
+
+ int32_t vsyncPeriod;
+ error = getDisplayVsyncPeriod(&vsyncPeriod);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " composer failed to handle config change",
+ __FUNCTION__, mId);
+ return error;
+ }
+
+ return mVsyncThread.scheduleVsyncUpdate(vsyncPeriod, constraints,
+ outTimeline);
+}
+
+std::optional<int32_t> Display::getBootConfigId() {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ std::string val;
+ HWC3::Error error = Device::getInstance().getPersistentKeyValue(
+ std::to_string(mId), "", &val);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " failed to get persistent boot config",
+ __FUNCTION__, mId);
+ return std::nullopt;
+ }
+
+ if (val.empty()) {
+ return std::nullopt;
+ }
+
+ int32_t configId = 0;
+ if (!::android::base::ParseInt(val, &configId)) {
+ ALOGE("%s: display:%" PRId64
+ " failed to parse persistent boot config from: %s",
+ __FUNCTION__, mId, val.c_str());
+ return std::nullopt;
+ }
+
+ if (!hasConfig(configId)) {
+ ALOGE("%s: display:%" PRId64 " invalid persistent boot config:%" PRId32,
+ __FUNCTION__, mId, configId);
+ return std::nullopt;
+ }
+
+ return configId;
+}
+
+HWC3::Error Display::setBootConfig(int32_t configId) {
+ DEBUG_LOG("%s: display:%" PRId64 " config:%" PRId32, __FUNCTION__, mId,
+ configId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ DisplayConfig* newConfig = getConfig(configId);
+ if (newConfig == nullptr) {
+ ALOGE("%s: display:%" PRId64 " bad config:%" PRId32, __FUNCTION__, mId,
+ configId);
+ return HWC3::Error::BadConfig;
+ }
+
+ const std::string key = std::to_string(mId);
+ const std::string val = std::to_string(configId);
+ HWC3::Error error = Device::getInstance().setPersistentKeyValue(key, val);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " failed to save persistent boot config",
+ __FUNCTION__, mId);
+ return error;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::clearBootConfig() {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ const std::string key = std::to_string(mId);
+ const std::string val = "";
+ HWC3::Error error = Device::getInstance().setPersistentKeyValue(key, val);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " failed to save persistent boot config",
+ __FUNCTION__, mId);
+ return error;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::getPreferredBootConfig(int32_t* outConfigId) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ std::vector<int32_t> configIds;
+ for (const auto [configId, _] : mConfigs) {
+ configIds.push_back(configId);
+ }
+ *outConfigId = *std::min_element(configIds.begin(), configIds.end());
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::setAutoLowLatencyMode(bool /*on*/) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setColorMode(ColorMode mode, RenderIntent intent) {
+ const std::string modeString = toString(mode);
+ const std::string intentString = toString(intent);
+ DEBUG_LOG("%s: display:%" PRId64 " setting color mode:%s intent:%s",
+ __FUNCTION__, mId, modeString.c_str(), intentString.c_str());
+
+ if (!isValidColorMode(mode)) {
+ ALOGE("%s: display:%" PRId64 " invalid color mode:%s", __FUNCTION__, mId,
+ modeString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+
+ if (!isValidRenderIntent(intent)) {
+ ALOGE("%s: display:%" PRId64 " invalid intent:%s", __FUNCTION__, mId,
+ intentString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (mColorModes.count(mode) == 0) {
+ ALOGE("%s: display %" PRId64 " mode %s not supported", __FUNCTION__, mId,
+ modeString.c_str());
+ return HWC3::Error::Unsupported;
+ }
+
+ mActiveColorMode = mode;
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::setContentType(ContentType contentType) {
+ auto contentTypeString = toString(contentType);
+ DEBUG_LOG("%s: display:%" PRId64 " content type:%s", __FUNCTION__, mId,
+ contentTypeString.c_str());
+
+ if (contentType != ContentType::NONE) {
+ return HWC3::Error::Unsupported;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::setDisplayedContentSamplingEnabled(
+ bool /*enable*/, FormatColorComponent /*componentMask*/,
+ int64_t /*maxFrames*/) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setPowerMode(PowerMode mode) {
+ auto modeString = toString(mode);
+ DEBUG_LOG("%s: display:%" PRId64 " to mode:%s", __FUNCTION__, mId,
+ modeString.c_str());
+
+ if (!isValidPowerMode(mode)) {
+ ALOGE("%s: display:%" PRId64 " invalid mode:%s", __FUNCTION__, mId,
+ modeString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+
+ if (mode == PowerMode::DOZE || mode == PowerMode::DOZE_SUSPEND ||
+ mode == PowerMode::ON_SUSPEND) {
+ ALOGE("%s display %" PRId64 " mode:%s not supported", __FUNCTION__, mId,
+ modeString.c_str());
+ return HWC3::Error::Unsupported;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ if (IsCuttlefish()) {
+ if (int fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); fd != -1) {
+ std::ostringstream stream;
+ stream << "VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED display=" << mId
+ << " mode=" << modeString;
+ std::string message = stream.str();
+ write(fd, message.c_str(), message.length());
+ close(fd);
+ }
+ }
+
+ mPowerMode = mode;
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::setReadbackBuffer(const buffer_handle_t buffer,
+ const ndk::ScopedFileDescriptor& fence) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ mReadbackBuffer.set(buffer, fence);
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setVsyncEnabled(bool enabled) {
+ DEBUG_LOG("%s: display:%" PRId64 " setting vsync %s", __FUNCTION__, mId,
+ (enabled ? "on" : "off"));
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ return mVsyncThread.setVsyncEnabled(enabled);
+}
+
+HWC3::Error Display::setIdleTimerEnabled(int32_t timeoutMs) {
+ DEBUG_LOG("%s: display:%" PRId64 " timeout:%" PRId32, __FUNCTION__, mId,
+ timeoutMs);
+
+ (void)timeoutMs;
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setColorTransform(
+ const std::vector<float>& transformMatrix) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ if (transformMatrix.size() < 16) {
+ ALOGE("%s: display:%" PRId64 " has non 4x4 matrix, size:%zu", __FUNCTION__,
+ mId, transformMatrix.size());
+ return HWC3::Error::BadParameter;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ auto& colorTransform = mColorTransform.emplace();
+ std::copy_n(transformMatrix.data(), colorTransform.size(),
+ colorTransform.begin());
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::setBrightness(float brightness) {
+ DEBUG_LOG("%s: display:%" PRId64 " brightness:%f", __FUNCTION__, mId,
+ brightness);
+
+ if (brightness < 0.0f) {
+ ALOGE("%s: display:%" PRId64 " invalid brightness:%f", __FUNCTION__, mId,
+ brightness);
+ return HWC3::Error::BadParameter;
+ }
+
+ return HWC3::Error::Unsupported;
+}
+
+HWC3::Error Display::setClientTarget(
+ buffer_handle_t buffer, const ndk::ScopedFileDescriptor& fence,
+ common::Dataspace /*dataspace*/,
+ const std::vector<common::Rect>& /*damage*/) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ mClientTarget.set(buffer, fence);
+
+ mComposer->onDisplayClientTargetSet(this);
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::setOutputBuffer(
+ buffer_handle_t /*buffer*/, const ndk::ScopedFileDescriptor& /*fence*/) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ // TODO: for virtual display
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::setExpectedPresentTime(
+ const std::optional<ClockMonotonicTimestamp>& expectedPresentTime) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ if (!expectedPresentTime.has_value()) {
+ return HWC3::Error::None;
+ }
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ mExpectedPresentTime.emplace(
+ asTimePoint(expectedPresentTime->timestampNanos));
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::validate(DisplayChanges* outChanges) {
+ ATRACE_CALL();
+
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ mPendingChanges.reset();
+
+ mOrderedLayers.clear();
+ mOrderedLayers.reserve(mLayers.size());
+ for (auto& [_, layerPtr] : mLayers) {
+ mOrderedLayers.push_back(layerPtr.get());
+ }
+ std::sort(mOrderedLayers.begin(), mOrderedLayers.end(),
+ [](const Layer* layerA, const Layer* layerB) {
+ const auto zA = layerA->getZOrder();
+ const auto zB = layerB->getZOrder();
+ if (zA != zB) {
+ return zA < zB;
+ }
+ return layerA->getId() < layerB->getId();
+ });
+
+ if (mComposer == nullptr) {
+ ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId);
+ return HWC3::Error::NoResources;
+ }
+
+ HWC3::Error error = mComposer->validateDisplay(this, &mPendingChanges);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRId64 " failed to validate", __FUNCTION__, mId);
+ return error;
+ }
+
+ if (mPendingChanges.hasAnyChanges()) {
+ mPresentFlowState = PresentFlowState::WAITING_FOR_ACCEPT;
+ DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_ACCEPT", __FUNCTION__,
+ mId);
+ } else {
+ mPresentFlowState = PresentFlowState::WAITING_FOR_PRESENT;
+ DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_PRESENT", __FUNCTION__,
+ mId);
+ }
+
+ *outChanges = mPendingChanges;
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::acceptChanges() {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ switch (mPresentFlowState) {
+ case PresentFlowState::WAITING_FOR_VALIDATE: {
+ ALOGE("%s: display %" PRId64 " failed, not validated", __FUNCTION__, mId);
+ return HWC3::Error::NotValidated;
+ }
+ case PresentFlowState::WAITING_FOR_ACCEPT:
+ case PresentFlowState::WAITING_FOR_PRESENT: {
+ break;
+ }
+ }
+
+ if (mPendingChanges.compositionChanges) {
+ const ChangedCompositionTypes& compositionChanges =
+ *mPendingChanges.compositionChanges;
+ for (const ChangedCompositionLayer& compositionChange :
+ compositionChanges.layers) {
+ const auto layerId = compositionChange.layer;
+ const auto layerComposition = compositionChange.composition;
+ auto* layer = getLayer(layerId);
+ if (layer == nullptr) {
+ ALOGE("%s: display:%" PRId64 " layer:%" PRId64
+ " dropped before acceptChanges()?",
+ __FUNCTION__, mId, layerId);
+ continue;
+ }
+
+ layer->setCompositionType(layerComposition);
+ }
+ }
+ mPendingChanges.reset();
+
+ mPresentFlowState = PresentFlowState::WAITING_FOR_PRESENT;
+ DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_PRESENT", __FUNCTION__,
+ mId);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Display::present(
+ ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences) {
+ ATRACE_CALL();
+
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ outDisplayFence->reset();
+ outLayerFences->clear();
+
+ std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+ switch (mPresentFlowState) {
+ case PresentFlowState::WAITING_FOR_VALIDATE: {
+ ALOGE("%s: display %" PRId64 " failed, not validated", __FUNCTION__, mId);
+ return HWC3::Error::NotValidated;
+ }
+ case PresentFlowState::WAITING_FOR_ACCEPT: {
+ ALOGE("%s: display %" PRId64 " failed, changes not accepted",
+ __FUNCTION__, mId);
+ return HWC3::Error::NotValidated;
+ }
+ case PresentFlowState::WAITING_FOR_PRESENT: {
+ break;
+ }
+ }
+ mPresentFlowState = PresentFlowState::WAITING_FOR_VALIDATE;
+ DEBUG_LOG("%s: display:%" PRId64 " now WAITING_FOR_VALIDATE", __FUNCTION__,
+ mId);
+
+ if (mComposer == nullptr) {
+ ALOGE("%s: display:%" PRId64 " missing composer", __FUNCTION__, mId);
+ return HWC3::Error::NoResources;
+ }
+
+ return mComposer->presentDisplay(this, outDisplayFence, outLayerFences);
+}
+
+bool Display::hasConfig(int32_t configId) const {
+ return mConfigs.find(configId) != mConfigs.end();
+}
+
+DisplayConfig* Display::getConfig(int32_t configId) {
+ auto it = mConfigs.find(configId);
+ if (it != mConfigs.end()) {
+ return &it->second;
+ }
+ return nullptr;
+}
+
+HWC3::Error Display::setEdid(std::vector<uint8_t> edid) {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ mEdid = edid;
+ return HWC3::Error::None;
+}
+
+void Display::setLegacyEdid() {
+ // thess EDIDs are carefully generated according to the EDID spec version 1.3,
+ // more info can be found from the following file:
+ // frameworks/native/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+ // approved pnp ids can be found here: https://uefi.org/pnp_id_list
+ // pnp id: GGL, name: EMU_display_0, last byte is checksum
+ // display id is local:8141603649153536
+ static constexpr const std::array<uint8_t, 128> kEdid0 = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
+ 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73,
+ 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x30, 0x00, 0x4b};
+
+ // pnp id: GGL, name: EMU_display_1
+ // display id is local:8140900251843329
+ static constexpr const std::array<uint8_t, 128> kEdid1 = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
+ 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73,
+ 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x31, 0x00, 0x3b};
+
+ // pnp id: GGL, name: EMU_display_2
+ // display id is local:8140940453066754
+ static constexpr const std::array<uint8_t, 128> kEdid2 = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
+ 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38,
+ 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73,
+ 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x32, 0x00, 0x49};
+
+ mEdid.clear();
+ switch (mId) {
+ case 0: {
+ mEdid.insert(mEdid.end(), kEdid0.begin(), kEdid0.end());
+ break;
+ }
+ case 1: {
+ mEdid.insert(mEdid.end(), kEdid1.begin(), kEdid1.end());
+ break;
+ }
+ case 2: {
+ mEdid.insert(mEdid.end(), kEdid2.begin(), kEdid2.end());
+ break;
+ }
+ default: {
+ mEdid.insert(mEdid.end(), kEdid2.begin(), kEdid2.end());
+ const uint32_t size = mEdid.size();
+ // Update the name to EMU_display_<mID>
+ mEdid[size - 3] = '0' + (uint8_t)mId;
+ // Update the checksum byte
+ uint8_t checksum = -(uint8_t)std::accumulate(
+ mEdid.data(), mEdid.data() + size - 1, static_cast<uint8_t>(0));
+ mEdid[size - 1] = checksum;
+ break;
+ }
+ }
+}
+
+Layer* Display::getLayer(int64_t layerId) {
+ auto it = mLayers.find(layerId);
+ if (it == mLayers.end()) {
+ ALOGE("%s Unknown layer:%" PRId64, __FUNCTION__, layerId);
+ return nullptr;
+ }
+
+ return it->second.get();
+}
+
+buffer_handle_t Display::waitAndGetClientTargetBuffer() {
+ DEBUG_LOG("%s: display:%" PRId64, __FUNCTION__, mId);
+
+ ::android::base::unique_fd fence = mClientTarget.getFence();
+ if (fence.ok()) {
+ int err = sync_wait(fence.get(), 3000);
+ if (err < 0 && errno == ETIME) {
+ ALOGE("%s waited on fence %" PRId32 " for 3000 ms", __FUNCTION__,
+ fence.get());
+ }
+ }
+
+ return mClientTarget.getBuffer();
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Display.h b/system/hwc3/Display.h
new file mode 100644
index 0000000..707bc19
--- /dev/null
+++ b/system/hwc3/Display.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_DISPLAY_H
+#define ANDROID_HWC_DISPLAY_H
+
+#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/composer3/ColorMode.h>
+#include <aidl/android/hardware/graphics/composer3/ContentType.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayAttribute.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayConnectionType.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayContentSample.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayIdentification.h>
+#include <aidl/android/hardware/graphics/composer3/HdrCapabilities.h>
+#include <aidl/android/hardware/graphics/composer3/PerFrameMetadataKey.h>
+#include <aidl/android/hardware/graphics/composer3/PowerMode.h>
+#include <aidl/android/hardware/graphics/composer3/ReadbackBufferAttributes.h>
+#include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeConstraints.h>
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeTimeline.h>
+#include <android-base/unique_fd.h>
+
+#include <array>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "Common.h"
+#include "DisplayChanges.h"
+#include "DisplayConfig.h"
+#include "DisplayFinder.h"
+#include "FencedBuffer.h"
+#include "FrameComposer.h"
+#include "Layer.h"
+#include "Time.h"
+#include "VsyncThread.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class FrameComposer;
+
+class Display {
+ public:
+ Display(FrameComposer* composer, int64_t id);
+ ~Display();
+
+ Display(const Display& display) = delete;
+ Display& operator=(const Display& display) = delete;
+
+ Display(Display&& display) = delete;
+ Display& operator=(Display&& display) = delete;
+
+ HWC3::Error init(
+ const std::vector<DisplayConfig>& configs, int32_t activeConfigId,
+ const std::optional<std::vector<uint8_t>>& edid = std::nullopt);
+
+ HWC3::Error updateParameters(
+ uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY,
+ uint32_t refreshRateHz,
+ const std::optional<std::vector<uint8_t>>& edid = std::nullopt);
+
+ // HWComposer3 interface.
+ HWC3::Error createLayer(int64_t* outLayerId);
+ HWC3::Error destroyLayer(int64_t layerId);
+ HWC3::Error getActiveConfig(int32_t* outConfigId);
+ HWC3::Error getDisplayAttribute(int32_t configId, DisplayAttribute attribute,
+ int32_t* outValue);
+ HWC3::Error getColorModes(std::vector<ColorMode>* outColorModes);
+ HWC3::Error getDisplayCapabilities(std::vector<DisplayCapability>* caps);
+ HWC3::Error getDisplayConfigs(std::vector<int32_t>* configs);
+ HWC3::Error getDisplayConnectionType(DisplayConnectionType* outType);
+ HWC3::Error getDisplayIdentificationData(
+ DisplayIdentification* outIdentification);
+ HWC3::Error getDisplayName(std::string* outName);
+ HWC3::Error getDisplayVsyncPeriod(int32_t* outVsyncPeriod);
+ HWC3::Error getDisplayedContentSample(int64_t maxFrames, int64_t timestamp,
+ DisplayContentSample* samples);
+ HWC3::Error getDisplayedContentSamplingAttributes(
+ DisplayContentSamplingAttributes* outAttributes);
+ HWC3::Error getDisplayPhysicalOrientation(common::Transform* outOrientation);
+ HWC3::Error getHdrCapabilities(HdrCapabilities* outCapabilities);
+ HWC3::Error getPerFrameMetadataKeys(
+ std::vector<PerFrameMetadataKey>* outKeys);
+ HWC3::Error getReadbackBufferAttributes(ReadbackBufferAttributes* attrs);
+ HWC3::Error getReadbackBufferFence(ndk::ScopedFileDescriptor* acquireFence);
+ HWC3::Error getRenderIntents(ColorMode mode,
+ std::vector<RenderIntent>* intents);
+ HWC3::Error getSupportedContentTypes(std::vector<ContentType>* types);
+ HWC3::Error getDecorationSupport(
+ std::optional<common::DisplayDecorationSupport>* support);
+ HWC3::Error registerCallback(
+ const std::shared_ptr<IComposerCallback>& callback);
+ HWC3::Error setActiveConfig(int32_t configId);
+ HWC3::Error setActiveConfigWithConstraints(
+ int32_t config, const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline);
+ HWC3::Error setBootConfig(int32_t configId);
+ HWC3::Error clearBootConfig();
+ HWC3::Error getPreferredBootConfig(int32_t* outConfigId);
+ HWC3::Error setAutoLowLatencyMode(bool on);
+ HWC3::Error setColorMode(ColorMode mode, RenderIntent intent);
+ HWC3::Error setContentType(ContentType contentType);
+ HWC3::Error setDisplayedContentSamplingEnabled(
+ bool enable, FormatColorComponent componentMask, int64_t maxFrames);
+ HWC3::Error setPowerMode(PowerMode mode);
+ HWC3::Error setReadbackBuffer(const buffer_handle_t buffer,
+ const ndk::ScopedFileDescriptor& releaseFence);
+ HWC3::Error setVsyncEnabled(bool enabled);
+ HWC3::Error setIdleTimerEnabled(int32_t timeoutMs);
+ HWC3::Error setColorTransform(const std::vector<float>& transform);
+ HWC3::Error setBrightness(float brightness);
+ HWC3::Error setClientTarget(buffer_handle_t buffer,
+ const ndk::ScopedFileDescriptor& fence,
+ common::Dataspace dataspace,
+ const std::vector<common::Rect>& damage);
+ HWC3::Error setOutputBuffer(buffer_handle_t buffer,
+ const ndk::ScopedFileDescriptor& fence);
+ HWC3::Error setExpectedPresentTime(
+ const std::optional<ClockMonotonicTimestamp>& expectedPresentTime);
+ HWC3::Error validate(DisplayChanges* outChanges);
+ HWC3::Error acceptChanges();
+ HWC3::Error present(
+ ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences);
+
+ // Non HWCComposer3 interface.
+ int64_t getId() const { return mId; }
+
+ Layer* getLayer(int64_t layerHandle);
+
+ HWC3::Error setEdid(std::vector<uint8_t> edid);
+
+ bool hasColorTransform() const { return mColorTransform.has_value(); }
+ std::array<float, 16> getColorTransform() const { return *mColorTransform; }
+
+ FencedBuffer& getClientTarget() { return mClientTarget; }
+ buffer_handle_t waitAndGetClientTargetBuffer();
+
+ const std::vector<Layer*>& getOrderedLayers() { return mOrderedLayers; }
+
+ private:
+ bool hasConfig(int32_t configId) const;
+ DisplayConfig* getConfig(int32_t configId);
+
+ std::optional<int32_t> getBootConfigId();
+
+ void setLegacyEdid();
+
+ // The state of this display should only be modified from
+ // SurfaceFlinger's main loop, with the exception of when dump is
+ // called. To prevent a bad state from crashing us during a dump
+ // call, all public calls into Display must acquire this mutex.
+ mutable std::recursive_mutex mStateMutex;
+
+ FrameComposer* mComposer = nullptr;
+ const int64_t mId;
+ std::string mName;
+ PowerMode mPowerMode = PowerMode::OFF;
+ VsyncThread mVsyncThread;
+ FencedBuffer mClientTarget;
+ FencedBuffer mReadbackBuffer;
+ // Will only be non-null after the Display has been validated and
+ // before it has been accepted.
+ enum class PresentFlowState {
+ WAITING_FOR_VALIDATE,
+ WAITING_FOR_ACCEPT,
+ WAITING_FOR_PRESENT,
+ };
+ PresentFlowState mPresentFlowState = PresentFlowState::WAITING_FOR_VALIDATE;
+ DisplayChanges mPendingChanges;
+ std::optional<TimePoint> mExpectedPresentTime;
+ std::unordered_map<int64_t, std::unique_ptr<Layer>> mLayers;
+ // Ordered layers available after validate().
+ std::vector<Layer*> mOrderedLayers;
+ std::optional<int32_t> mActiveConfigId;
+ std::unordered_map<int32_t, DisplayConfig> mConfigs;
+ std::unordered_set<ColorMode> mColorModes = {ColorMode::NATIVE};
+ ColorMode mActiveColorMode = ColorMode::NATIVE;
+ std::optional<std::array<float, 16>> mColorTransform;
+ std::vector<uint8_t> mEdid;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/DisplayChanges.h b/system/hwc3/DisplayChanges.h
new file mode 100644
index 0000000..2745a4d
--- /dev/null
+++ b/system/hwc3/DisplayChanges.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_DISPLAYCHANGES_H
+#define ANDROID_HWC_DISPLAYCHANGES_H
+
+#include <aidl/android/hardware/graphics/composer3/ChangedCompositionLayer.h>
+#include <aidl/android/hardware/graphics/composer3/ChangedCompositionTypes.h>
+#include <aidl/android/hardware/graphics/composer3/DisplayRequest.h>
+
+#include <optional>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+struct DisplayChanges {
+ std::optional<ChangedCompositionTypes> compositionChanges;
+ std::optional<DisplayRequest> displayRequestChanges;
+
+ void addLayerCompositionChange(int64_t displayId, int64_t layerId,
+ Composition layerComposition) {
+ if (!compositionChanges) {
+ compositionChanges.emplace();
+ compositionChanges->display = displayId;
+ }
+
+ ChangedCompositionLayer compositionChange;
+ compositionChange.layer = layerId;
+ compositionChange.composition = layerComposition;
+ compositionChanges->layers.emplace_back(std::move(compositionChange));
+ }
+
+ void clearLayerCompositionChanges() { compositionChanges.reset(); }
+
+ bool hasAnyChanges() const {
+ return compositionChanges.has_value() || displayRequestChanges.has_value();
+ }
+
+ void reset() {
+ compositionChanges.reset();
+ displayRequestChanges.reset();
+ }
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/DisplayConfig.cpp b/system/hwc3/DisplayConfig.cpp
new file mode 100644
index 0000000..3605010
--- /dev/null
+++ b/system/hwc3/DisplayConfig.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2022 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 "DisplayConfig.h"
+
+#include <unordered_map>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+template <class T>
+inline void hashCombine(size_t& hash, const T& value) {
+ std::hash<T> hasher;
+ hash ^= hasher(value) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
+}
+
+} // namespace
+
+void DisplayConfig::setAttribute(DisplayAttribute attribute, int32_t value) {
+ if (attribute == DisplayAttribute::WIDTH) {
+ mWidth = value;
+ }
+ if (attribute == DisplayAttribute::HEIGHT) {
+ mHeight = value;
+ }
+ if (attribute == DisplayAttribute::DPI_X) {
+ mDpiX = value;
+ }
+ if (attribute == DisplayAttribute::DPI_Y) {
+ mDpiY = value;
+ }
+ if (attribute == DisplayAttribute::VSYNC_PERIOD) {
+ mVsyncPeriodNanos = value;
+ }
+ if (attribute == DisplayAttribute::CONFIG_GROUP) {
+ mConfigGroup = value;
+ }
+}
+
+int32_t DisplayConfig::getAttribute(DisplayAttribute attribute) const {
+ if (attribute == DisplayAttribute::WIDTH) {
+ return mWidth;
+ }
+ if (attribute == DisplayAttribute::HEIGHT) {
+ return mHeight;
+ }
+ if (attribute == DisplayAttribute::DPI_X) {
+ // From hwcomposer2.h, HWC2_ATTRIBUTE_DPI_X returns "Dots per thousand
+ // inches (DPI * 1000)".
+ return getDotsPerThousandInchesX();
+ }
+ if (attribute == DisplayAttribute::DPI_Y) {
+ // From hwcomposer2.h, HWC2_ATTRIBUTE_DPI_Y returns "Dots per thousand
+ // inches (DPI * 1000)"
+ return getDotsPerThousandInchesY();
+ }
+ if (attribute == DisplayAttribute::VSYNC_PERIOD) {
+ return mVsyncPeriodNanos;
+ }
+ if (attribute == DisplayAttribute::CONFIG_GROUP) {
+ return mConfigGroup;
+ }
+ return -1;
+}
+
+std::string DisplayConfig::toString() const {
+ std::string output;
+ output += " id: " + std::to_string(mId);
+ output += " w:" + std::to_string(mWidth);
+ output += " h:" + std::to_string(mHeight);
+ output += " dpi-x:" + std::to_string(mDpiX);
+ output += " dpi-y:" + std::to_string(mDpiY);
+ output += " vsync:" + std::to_string(1e9 / mVsyncPeriodNanos);
+ output += " config-group:" + std::to_string(mConfigGroup);
+ return output;
+}
+
+/*static*/
+void DisplayConfig::addConfigGroups(std::vector<DisplayConfig>* configs) {
+ // From /hardware/interfaces/graphics/composer/2.4/IComposerClient.hal:
+ // "Configurations which share the same config group are similar in all
+ // attributes except for the vsync period."
+ struct ConfigForGroupHash {
+ size_t operator()(const DisplayConfig& config) const {
+ size_t hash = 0;
+ hashCombine(hash, config.mWidth);
+ hashCombine(hash, config.mHeight);
+ hashCombine(hash, config.mDpiX);
+ hashCombine(hash, config.mDpiY);
+ return hash;
+ }
+ };
+ struct ConfigForGroupEq {
+ size_t operator()(const DisplayConfig& a, const DisplayConfig& b) const {
+ if (a.mWidth != b.mWidth) {
+ return a.mWidth < b.mWidth;
+ }
+ if (a.mHeight != b.mHeight) {
+ return a.mHeight < b.mHeight;
+ }
+ if (a.mDpiX != b.mDpiX) {
+ return a.mDpiX < b.mDpiX;
+ }
+ return a.mDpiY < b.mDpiY;
+ }
+ };
+
+ std::unordered_map<DisplayConfig, int32_t, ConfigForGroupHash,
+ ConfigForGroupEq>
+ configToConfigGroup;
+
+ for (auto& config : *configs) {
+ auto [it, inserted] =
+ configToConfigGroup.try_emplace(config, configToConfigGroup.size());
+ config.setConfigGroup(it->second);
+ }
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/DisplayConfig.h b/system/hwc3/DisplayConfig.h
new file mode 100644
index 0000000..7130fff
--- /dev/null
+++ b/system/hwc3/DisplayConfig.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_DISPLAYCONFIG_H
+#define ANDROID_HWC_DISPLAYCONFIG_H
+
+#include <aidl/android/hardware/graphics/composer3/DisplayAttribute.h>
+
+#include <vector>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class DisplayConfig {
+ public:
+ DisplayConfig(int32_t configId) : mId(configId) {}
+
+ DisplayConfig(int32_t configId, int32_t width, int32_t height, int32_t dpiX,
+ int32_t dpiY, int32_t vsyncPeriodNanos)
+ : mId(configId),
+ mWidth(width),
+ mHeight(height),
+ mDpiX(dpiX),
+ mDpiY(dpiY),
+ mVsyncPeriodNanos(vsyncPeriodNanos) {}
+
+ DisplayConfig(const DisplayConfig& other) = default;
+ DisplayConfig& operator=(DisplayConfig& other) = default;
+
+ DisplayConfig(DisplayConfig&& other) = default;
+ DisplayConfig& operator=(DisplayConfig&& other) = default;
+
+ int32_t getId() const { return mId; }
+ void setId(int32_t id) { mId = id; }
+
+ int32_t getAttribute(DisplayAttribute attribute) const;
+ void setAttribute(DisplayAttribute attribute, int32_t value);
+
+ int32_t getWidth() const { return mWidth; }
+ void setWidth(int32_t width) { mWidth = width; }
+
+ int32_t getHeight() const { return mHeight; }
+ void getHeight(int32_t height) { mHeight = height; }
+
+ int32_t getDpiX() const { return mDpiX; }
+ void setDpiX(int32_t dpi) { mDpiX = dpi; }
+
+ int32_t getDpiY() const { return mDpiY; }
+ void setDpiY(int32_t dpi) { mDpiY = dpi; }
+
+ int32_t getDotsPerThousandInchesX() const { return mDpiX * 1000; }
+ int32_t getDotsPerThousandInchesY() const { return mDpiY * 1000; }
+
+ int32_t getVsyncPeriod() const { return mVsyncPeriodNanos; }
+ void setVsyncPeriod(int32_t vsync) { mVsyncPeriodNanos = vsync; }
+
+ int32_t getConfigGroup() const { return mConfigGroup; }
+ void setConfigGroup(int32_t group) { mConfigGroup = group; }
+
+ std::string toString() const;
+
+ static void addConfigGroups(std::vector<DisplayConfig>* configs);
+
+ private:
+ int32_t mId;
+ int32_t mWidth;
+ int32_t mHeight;
+ int32_t mDpiX;
+ int32_t mDpiY;
+ int32_t mVsyncPeriodNanos;
+ int32_t mConfigGroup;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/DisplayFinder.cpp b/system/hwc3/DisplayFinder.cpp
new file mode 100644
index 0000000..8e252cb
--- /dev/null
+++ b/system/hwc3/DisplayFinder.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2022 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 "DisplayFinder.h"
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <device_config_shared.h>
+
+#include "Common.h"
+#include "HostUtils.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+constexpr int32_t HertzToPeriodNanos(uint32_t hertz) {
+ return 1000 * 1000 * 1000 / hertz;
+}
+
+HWC3::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>& displays) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ // TODO: replace with initializing directly from DRM info.
+ const auto deviceConfig = cuttlefish::GetDeviceConfig();
+
+ int64_t displayId = 0;
+ for (const auto& deviceDisplayConfig : deviceConfig.display_config()) {
+ const auto vsyncPeriodNanos =
+ HertzToPeriodNanos(deviceDisplayConfig.refresh_rate_hz());
+
+ DisplayMultiConfigs display = {
+ .displayId = displayId,
+ .activeConfigId = 0,
+ .configs =
+ {
+ DisplayConfig(0, //
+ deviceDisplayConfig.width(), //
+ deviceDisplayConfig.height(), //
+ deviceDisplayConfig.dpi(), //
+ deviceDisplayConfig.dpi(), //
+ vsyncPeriodNanos),
+ },
+ };
+ displays.push_back(display);
+ ++displayId;
+ }
+
+ return HWC3::Error::None;
+}
+
+static int getVsyncHzFromProperty() {
+ static constexpr const auto kVsyncProp = "ro.boot.qemu.vsync";
+
+ const auto vsyncProp = ::android::base::GetProperty(kVsyncProp, "");
+ DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, vsyncProp.c_str());
+
+ uint64_t vsyncPeriod;
+ if (!::android::base::ParseUint(vsyncProp, &vsyncPeriod)) {
+ ALOGE("%s: failed to parse vsync period '%s', returning default 60",
+ __FUNCTION__, vsyncProp.c_str());
+ return 60;
+ }
+
+ return static_cast<int>(vsyncPeriod);
+}
+
+HWC3::Error findGoldfishPrimaryDisplay(
+ std::vector<DisplayMultiConfigs>& displays) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ DEFINE_AND_VALIDATE_HOST_CONNECTION
+ hostCon->lock();
+ const int32_t vsyncPeriodNanos = HertzToPeriodNanos(getVsyncHzFromProperty());
+ DisplayMultiConfigs display;
+ display.displayId = 0;
+ if (rcEnc->hasHWCMultiConfigs()) {
+ int count = rcEnc->rcGetFBDisplayConfigsCount(rcEnc);
+ if (count <= 0) {
+ ALOGE("%s failed to allocate primary display, config count %d", __func__,
+ count);
+ return HWC3::Error::NoResources;
+ }
+ display.activeConfigId = rcEnc->rcGetFBDisplayActiveConfig(rcEnc);
+ for (int configId = 0; configId < count; configId++) {
+ display.configs.push_back(DisplayConfig(
+ configId, //
+ rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_WIDTH), //
+ rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_HEIGHT), //
+ rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_XDPI), //
+ rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_YDPI), //
+ vsyncPeriodNanos //
+ ));
+ }
+ } else {
+ display.activeConfigId = 0;
+ display.configs.push_back(DisplayConfig(
+ 0, //
+ rcEnc->rcGetFBParam(rcEnc, FB_WIDTH), //
+ rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT), //
+ rcEnc->rcGetFBParam(rcEnc, FB_XDPI), //
+ rcEnc->rcGetFBParam(rcEnc, FB_YDPI), //
+ vsyncPeriodNanos //
+ ));
+ }
+ hostCon->unlock();
+
+ displays.push_back(display);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error findGoldfishSecondaryDisplays(
+ std::vector<DisplayMultiConfigs>& displays) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ static constexpr const char kExternalDisplayProp[] =
+ "hwservicemanager.external.displays";
+
+ const auto propString =
+ ::android::base::GetProperty(kExternalDisplayProp, "");
+ DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str());
+
+ if (propString.empty()) {
+ return HWC3::Error::None;
+ }
+
+ const std::vector<std::string> propStringParts =
+ ::android::base::Split(propString, ",");
+ if (propStringParts.size() % 5 != 0) {
+ ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+ kExternalDisplayProp, propString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+
+ std::vector<int> propIntParts;
+ for (const std::string& propStringPart : propStringParts) {
+ int propIntPart;
+ if (!::android::base::ParseInt(propStringPart, &propIntPart)) {
+ ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+ kExternalDisplayProp, propString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+ propIntParts.push_back(propIntPart);
+ }
+
+ int64_t secondaryDisplayId = 1;
+ while (!propIntParts.empty()) {
+ DisplayMultiConfigs display;
+ display.displayId = secondaryDisplayId;
+ display.activeConfigId = 0;
+ display.configs.push_back(DisplayConfig(
+ 0, //
+ /*width=*/propIntParts[1], //
+ /*heighth=*/propIntParts[2], //
+ /*dpiXh=*/propIntParts[3], //
+ /*dpiYh=*/propIntParts[3], //
+ /*vsyncPeriod=*/HertzToPeriodNanos(160) //
+ ));
+ displays.push_back(display);
+
+ ++secondaryDisplayId;
+
+ propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>& displays) {
+ HWC3::Error error = findGoldfishPrimaryDisplay(displays);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to find Goldfish primary display", __FUNCTION__);
+ return error;
+ }
+
+ error = findGoldfishSecondaryDisplays(displays);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__);
+ }
+
+ return error;
+}
+
+// This is currently only used for Gem5 bring-up where virtio-gpu and drm
+// are not currently available. For now, just return a placeholder display.
+HWC3::Error findNoOpDisplays(std::vector<DisplayMultiConfigs>& displays) {
+ displays.push_back(DisplayMultiConfigs{
+ .displayId = 0,
+ .activeConfigId = 0,
+ .configs = {DisplayConfig(0,
+ /*width=*/720, //
+ /*heighth=*/1280, //
+ /*dpiXh=*/320, //
+ /*dpiYh=*/320, //
+ /*vsyncPeriod=*/HertzToPeriodNanos(30) //
+ )},
+ });
+
+ return HWC3::Error::None;
+}
+
+} // namespace
+
+HWC3::Error findDisplays(std::vector<DisplayMultiConfigs>& displays) {
+ HWC3::Error error = HWC3::Error::None;
+ if (IsNoOpMode()) {
+ error = findNoOpDisplays(displays);
+ } else if (IsCuttlefish()) {
+ error = findCuttlefishDisplays(displays);
+ } else {
+ error = findGoldfishDisplays(displays);
+ }
+
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to find displays", __FUNCTION__);
+ return error;
+ }
+
+ for (auto& display : displays) {
+ DisplayConfig::addConfigGroups(&display.configs);
+ }
+
+ return HWC3::Error::None;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/DisplayFinder.h b/system/hwc3/DisplayFinder.h
new file mode 100644
index 0000000..8197a82
--- /dev/null
+++ b/system/hwc3/DisplayFinder.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_DISPLAYFINDER_H
+#define ANDROID_HWC_DISPLAYFINDER_H
+
+#include <vector>
+
+#include "Common.h"
+#include "DisplayConfig.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+struct DisplayMultiConfigs {
+ int64_t displayId;
+ int32_t activeConfigId;
+ // Modes that this display can be configured to use.
+ std::vector<DisplayConfig> configs;
+};
+
+HWC3::Error findDisplays(std::vector<DisplayMultiConfigs>& displays);
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Drm.cpp b/system/hwc3/Drm.cpp
new file mode 100644
index 0000000..6872eb4
--- /dev/null
+++ b/system/hwc3/Drm.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 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 "Drm.h"
+
+#include <drm_fourcc.h>
+#include <log/log.h>
+#include <system/graphics.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+const char* GetDrmFormatString(uint32_t drm_format) {
+ switch (drm_format) {
+ case DRM_FORMAT_ABGR1555:
+ return "DRM_FORMAT_ABGR1555";
+ case DRM_FORMAT_ABGR2101010:
+ return "DRM_FORMAT_ABGR2101010";
+ case DRM_FORMAT_ABGR4444:
+ return "DRM_FORMAT_ABGR4444";
+ case DRM_FORMAT_ABGR8888:
+ return "DRM_FORMAT_ABGR8888";
+ case DRM_FORMAT_ARGB1555:
+ return "DRM_FORMAT_ARGB1555";
+ case DRM_FORMAT_ARGB2101010:
+ return "DRM_FORMAT_ARGB2101010";
+ case DRM_FORMAT_ARGB4444:
+ return "DRM_FORMAT_ARGB4444";
+ case DRM_FORMAT_ARGB8888:
+ return "DRM_FORMAT_ARGB8888";
+ case DRM_FORMAT_AYUV:
+ return "DRM_FORMAT_AYUV";
+ case DRM_FORMAT_BGR233:
+ return "DRM_FORMAT_BGR233";
+ case DRM_FORMAT_BGR565:
+ return "DRM_FORMAT_BGR565";
+ case DRM_FORMAT_BGR888:
+ return "DRM_FORMAT_BGR888";
+ case DRM_FORMAT_BGRA1010102:
+ return "DRM_FORMAT_BGRA1010102";
+ case DRM_FORMAT_BGRA4444:
+ return "DRM_FORMAT_BGRA4444";
+ case DRM_FORMAT_BGRA5551:
+ return "DRM_FORMAT_BGRA5551";
+ case DRM_FORMAT_BGRA8888:
+ return "DRM_FORMAT_BGRA8888";
+ case DRM_FORMAT_BGRX1010102:
+ return "DRM_FORMAT_BGRX1010102";
+ case DRM_FORMAT_BGRX4444:
+ return "DRM_FORMAT_BGRX4444";
+ case DRM_FORMAT_BGRX5551:
+ return "DRM_FORMAT_BGRX5551";
+ case DRM_FORMAT_BGRX8888:
+ return "DRM_FORMAT_BGRX8888";
+ case DRM_FORMAT_C8:
+ return "DRM_FORMAT_C8";
+ case DRM_FORMAT_GR88:
+ return "DRM_FORMAT_GR88";
+ case DRM_FORMAT_NV12:
+ return "DRM_FORMAT_NV12";
+ case DRM_FORMAT_NV21:
+ return "DRM_FORMAT_NV21";
+ case DRM_FORMAT_R8:
+ return "DRM_FORMAT_R8";
+ case DRM_FORMAT_RG88:
+ return "DRM_FORMAT_RG88";
+ case DRM_FORMAT_RGB332:
+ return "DRM_FORMAT_RGB332";
+ case DRM_FORMAT_RGB565:
+ return "DRM_FORMAT_RGB565";
+ case DRM_FORMAT_RGB888:
+ return "DRM_FORMAT_RGB888";
+ case DRM_FORMAT_RGBA1010102:
+ return "DRM_FORMAT_RGBA1010102";
+ case DRM_FORMAT_RGBA4444:
+ return "DRM_FORMAT_RGBA4444";
+ case DRM_FORMAT_RGBA5551:
+ return "DRM_FORMAT_RGBA5551";
+ case DRM_FORMAT_RGBA8888:
+ return "DRM_FORMAT_RGBA8888";
+ case DRM_FORMAT_RGBX1010102:
+ return "DRM_FORMAT_RGBX1010102";
+ case DRM_FORMAT_RGBX4444:
+ return "DRM_FORMAT_RGBX4444";
+ case DRM_FORMAT_RGBX5551:
+ return "DRM_FORMAT_RGBX5551";
+ case DRM_FORMAT_RGBX8888:
+ return "DRM_FORMAT_RGBX8888";
+ case DRM_FORMAT_UYVY:
+ return "DRM_FORMAT_UYVY";
+ case DRM_FORMAT_VYUY:
+ return "DRM_FORMAT_VYUY";
+ case DRM_FORMAT_XBGR1555:
+ return "DRM_FORMAT_XBGR1555";
+ case DRM_FORMAT_XBGR2101010:
+ return "DRM_FORMAT_XBGR2101010";
+ case DRM_FORMAT_XBGR4444:
+ return "DRM_FORMAT_XBGR4444";
+ case DRM_FORMAT_XBGR8888:
+ return "DRM_FORMAT_XBGR8888";
+ case DRM_FORMAT_XRGB1555:
+ return "DRM_FORMAT_XRGB1555";
+ case DRM_FORMAT_XRGB2101010:
+ return "DRM_FORMAT_XRGB2101010";
+ case DRM_FORMAT_XRGB4444:
+ return "DRM_FORMAT_XRGB4444";
+ case DRM_FORMAT_XRGB8888:
+ return "DRM_FORMAT_XRGB8888";
+ case DRM_FORMAT_YUYV:
+ return "DRM_FORMAT_YUYV";
+ case DRM_FORMAT_YVU420:
+ return "DRM_FORMAT_YVU420";
+ case DRM_FORMAT_YVYU:
+ return "DRM_FORMAT_YVYU";
+ }
+ return "Unknown";
+}
+
+int GetDrmFormatBytesPerPixel(uint32_t drm_format) {
+ switch (drm_format) {
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XBGR8888:
+ return 4;
+ case DRM_FORMAT_BGR888:
+ return 3;
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_YVU420:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+ case DRM_FORMAT_FLEX_YCbCr_420_888:
+#endif
+ return 2;
+ case DRM_FORMAT_R8:
+ return 1;
+ }
+ ALOGE("%s: format size unknown %d(%s)", __FUNCTION__, drm_format,
+ GetDrmFormatString(drm_format));
+ return 8;
+}
+
+int GetDrmFormatFromHalFormat(int hal_format) {
+ switch (hal_format) {
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ return DRM_FORMAT_ABGR16161616F;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return DRM_FORMAT_ABGR8888;
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return DRM_FORMAT_XBGR8888;
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return DRM_FORMAT_ARGB8888;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return DRM_FORMAT_BGR888;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ return DRM_FORMAT_BGR565;
+ case HAL_PIXEL_FORMAT_YV12:
+ return DRM_FORMAT_YVU420;
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ return DRM_FORMAT_YVU420;
+ case HAL_PIXEL_FORMAT_BLOB:
+ return DRM_FORMAT_R8;
+ default:
+ break;
+ }
+ ALOGE("%s unhandled hal format: %d", __FUNCTION__, hal_format);
+ return 0;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/Drm.h b/system/hwc3/Drm.h
new file mode 100644
index 0000000..1d25033
--- /dev/null
+++ b/system/hwc3/Drm.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+#ifndef ANDROID_HWC_DRM_H
+#define ANDROID_HWC_DRM_H
+
+#include <cstdlib>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+const char* GetDrmFormatString(uint32_t drm_format);
+
+int GetDrmFormatBytesPerPixel(uint32_t drm_format);
+
+int GetDrmFormatFromHalFormat(int hal_format);
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/DrmPresenter.cpp b/system/hwc3/DrmPresenter.cpp
new file mode 100644
index 0000000..6e13209
--- /dev/null
+++ b/system/hwc3/DrmPresenter.cpp
@@ -0,0 +1,706 @@
+/*
+ * Copyright 2022 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 "DrmPresenter.h"
+
+#include <cros_gralloc_handle.h>
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+#include <chrono>
+#include <thread>
+
+using ::android::base::guest::AutoReadLock;
+using ::android::base::guest::AutoWriteLock;
+using ::android::base::guest::ReadWriteLock;
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+uint64_t addressAsUint(int* pointer) {
+ return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer));
+}
+
+} // namespace
+
+DrmPresenter::~DrmPresenter() {
+ if (mFd > 0) {
+ drmDropMaster(mFd.get());
+ }
+}
+
+HWC3::Error DrmPresenter::init() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ mFd = ::android::base::unique_fd(open("/dev/dri/card0", O_RDWR | O_CLOEXEC));
+ if (mFd < 0) {
+ ALOGE("%s: failed to open drm device: %s", __FUNCTION__, strerror(errno));
+ return HWC3::Error::NoResources;
+ }
+
+ int ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ if (ret) {
+ ALOGE("%s: failed to set cap universal plane %s\n", __FUNCTION__,
+ strerror(errno));
+ return HWC3::Error::NoResources;
+ }
+
+ ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_ATOMIC, 1);
+ if (ret) {
+ ALOGE("%s: failed to set cap atomic %s\n", __FUNCTION__, strerror(errno));
+ return HWC3::Error::NoResources;
+ }
+
+ drmSetMaster(mFd.get());
+
+ if (!drmIsMaster(mFd.get())) {
+ ALOGE("%s: failed to get master drm device", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ {
+ AutoWriteLock lock(mStateMutex);
+ bool initDrmRet = initDrmElementsLocked();
+ if (initDrmRet) {
+ DEBUG_LOG("%s: Successfully initialized DRM backend", __FUNCTION__);
+ } else {
+ ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ }
+
+ mDrmEventListener = ::android::sp<DrmEventListener>::make(*this);
+ if (mDrmEventListener->init()) {
+ DEBUG_LOG("%s: Successfully initialized DRM event listener", __FUNCTION__);
+ } else {
+ ALOGE("%s: Failed to initialize DRM event listener", __FUNCTION__);
+ }
+ mDrmEventListener->run("", ANDROID_PRIORITY_URGENT_DISPLAY);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error DrmPresenter::registerOnHotplugCallback(const HotplugCallback& cb) {
+ mHotplugCallback = cb;
+ return HWC3::Error::None;
+}
+
+HWC3::Error DrmPresenter::unregisterOnHotplugCallback() {
+ mHotplugCallback.reset();
+ return HWC3::Error::None;
+}
+
+bool DrmPresenter::initDrmElementsLocked() {
+ drmModeRes* res;
+ static const int32_t kUmPerInch = 25400;
+
+ res = drmModeGetResources(mFd.get());
+ if (res == nullptr) {
+ ALOGE("%s HWC3::Error reading drm resources: %d", __FUNCTION__, errno);
+ mFd.reset();
+ return false;
+ }
+
+ DEBUG_LOG(
+ "drmModeRes count fbs %d crtc %d connector %d encoder %d min w %d max w "
+ "%d min h %d max h %d",
+ res->count_fbs, res->count_crtcs, res->count_connectors,
+ res->count_encoders, res->min_width, res->max_width, res->min_height,
+ res->max_height);
+
+ for (uint32_t i = 0; i < res->count_crtcs; i++) {
+ DrmCrtc crtc = {};
+
+ drmModeCrtcPtr c = drmModeGetCrtc(mFd.get(), res->crtcs[i]);
+ crtc.mId = c->crtc_id;
+
+ drmModeObjectPropertiesPtr crtcProps =
+ drmModeObjectGetProperties(mFd.get(), c->crtc_id, DRM_MODE_OBJECT_CRTC);
+
+ for (uint32_t crtcPropsIndex = 0; crtcPropsIndex < crtcProps->count_props;
+ crtcPropsIndex++) {
+ drmModePropertyPtr crtcProp =
+ drmModeGetProperty(mFd.get(), crtcProps->props[crtcPropsIndex]);
+
+ if (!strcmp(crtcProp->name, "OUT_FENCE_PTR")) {
+ crtc.mOutFencePtrPropertyId = crtcProp->prop_id;
+ } else if (!strcmp(crtcProp->name, "ACTIVE")) {
+ crtc.mActivePropertyId = crtcProp->prop_id;
+ } else if (!strcmp(crtcProp->name, "MODE_ID")) {
+ crtc.mModePropertyId = crtcProp->prop_id;
+ }
+
+ drmModeFreeProperty(crtcProp);
+ }
+
+ drmModeFreeObjectProperties(crtcProps);
+
+ mCrtcs.push_back(crtc);
+ }
+
+ drmModePlaneResPtr planeRes = drmModeGetPlaneResources(mFd.get());
+ for (uint32_t i = 0; i < planeRes->count_planes; ++i) {
+ DrmPlane plane = {};
+
+ drmModePlanePtr p = drmModeGetPlane(mFd.get(), planeRes->planes[i]);
+ plane.mId = p->plane_id;
+
+ DEBUG_LOG(
+ "%s: plane id: %u crtcid %u fbid %u crtc xy %d %d xy %d %d "
+ "possible ctrcs 0x%x",
+ __FUNCTION__, p->plane_id, p->crtc_id, p->fb_id, p->crtc_x, p->crtc_y,
+ p->x, p->y, p->possible_crtcs);
+
+ drmModeObjectPropertiesPtr planeProps =
+ drmModeObjectGetProperties(mFd.get(), plane.mId, DRM_MODE_OBJECT_PLANE);
+
+ for (uint32_t planePropIndex = 0; planePropIndex < planeProps->count_props;
+ ++planePropIndex) {
+ drmModePropertyPtr planeProp =
+ drmModeGetProperty(mFd.get(), planeProps->props[planePropIndex]);
+
+ if (!strcmp(planeProp->name, "CRTC_ID")) {
+ plane.mCrtcPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "IN_FENCE_FD")) {
+ plane.mInFenceFdPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "FB_ID")) {
+ plane.mFbPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "CRTC_X")) {
+ plane.mCrtcXPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "CRTC_Y")) {
+ plane.mCrtcYPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "CRTC_W")) {
+ plane.mCrtcWPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "CRTC_H")) {
+ plane.mCrtcHPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "SRC_X")) {
+ plane.mSrcXPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "SRC_Y")) {
+ plane.mSrcYPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "SRC_W")) {
+ plane.mSrcWPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "SRC_H")) {
+ plane.mSrcHPropertyId = planeProp->prop_id;
+ } else if (!strcmp(planeProp->name, "type")) {
+ plane.mTypePropertyId = planeProp->prop_id;
+ uint64_t type = planeProp->values[0];
+ switch (type) {
+ case DRM_PLANE_TYPE_OVERLAY:
+ plane.mType = type;
+ DEBUG_LOG("%s: plane %" PRIu32 " is DRM_PLANE_TYPE_OVERLAY",
+ __FUNCTION__, plane.mId);
+ break;
+ case DRM_PLANE_TYPE_PRIMARY:
+ plane.mType = type;
+ DEBUG_LOG("%s: plane %" PRIu32 " is DRM_PLANE_TYPE_PRIMARY",
+ __FUNCTION__, plane.mId);
+ break;
+ default:
+ break;
+ }
+ }
+
+ drmModeFreeProperty(planeProp);
+ }
+
+ drmModeFreeObjectProperties(planeProps);
+
+ bool isPrimaryOrOverlay = plane.mType == DRM_PLANE_TYPE_OVERLAY ||
+ plane.mType == DRM_PLANE_TYPE_PRIMARY;
+ if (isPrimaryOrOverlay) {
+ for (uint32_t j = 0; j < mCrtcs.size(); j++) {
+ if ((0x1 << j) & p->possible_crtcs) {
+ DEBUG_LOG("%s: plane %" PRIu32 " compatible with crtc mask %" PRIu32,
+ __FUNCTION__, plane.mId, p->possible_crtcs);
+ if (mCrtcs[j].mPlaneId == -1) {
+ mCrtcs[j].mPlaneId = plane.mId;
+ DEBUG_LOG("%s: plane %" PRIu32 " associated with crtc %" PRIu32,
+ __FUNCTION__, plane.mId, j);
+ break;
+ }
+ }
+ }
+ }
+
+ drmModeFreePlane(p);
+ mPlanes[plane.mId] = plane;
+ }
+ drmModeFreePlaneResources(planeRes);
+
+ for (uint32_t i = 0; i < res->count_connectors; ++i) {
+ DrmConnector connector = {};
+ connector.mId = res->connectors[i];
+
+ {
+ drmModeObjectPropertiesPtr connectorProps = drmModeObjectGetProperties(
+ mFd.get(), connector.mId, DRM_MODE_OBJECT_CONNECTOR);
+
+ for (uint32_t connectorPropIndex = 0;
+ connectorPropIndex < connectorProps->count_props;
+ ++connectorPropIndex) {
+ drmModePropertyPtr connectorProp = drmModeGetProperty(
+ mFd.get(), connectorProps->props[connectorPropIndex]);
+ if (!strcmp(connectorProp->name, "CRTC_ID")) {
+ connector.mCrtcPropertyId = connectorProp->prop_id;
+ } else if (!strcmp(connectorProp->name, "EDID")) {
+ connector.mEdidBlobId =
+ connectorProps->prop_values[connectorPropIndex];
+ }
+ drmModeFreeProperty(connectorProp);
+ }
+
+ drmModeFreeObjectProperties(connectorProps);
+ }
+ {
+ drmModeConnector* c = drmModeGetConnector(mFd.get(), connector.mId);
+ if (c == nullptr) {
+ ALOGE("%s: Failed to get connector %" PRIu32 ": %d", __FUNCTION__,
+ connector.mId, errno);
+ return false;
+ }
+ connector.connection = c->connection;
+ if (c->count_modes > 0) {
+ memcpy(&connector.mMode, &c->modes[0], sizeof(drmModeModeInfo));
+ drmModeCreatePropertyBlob(mFd.get(), &connector.mMode,
+ sizeof(connector.mMode),
+ &connector.mModeBlobId);
+
+ // Dots per 1000 inches
+ connector.dpiX =
+ c->mmWidth ? (c->modes[0].hdisplay * kUmPerInch) / (c->mmWidth)
+ : -1;
+ // Dots per 1000 inches
+ connector.dpiY =
+ c->mmHeight ? (c->modes[0].vdisplay * kUmPerInch) / (c->mmHeight)
+ : -1;
+ }
+ DEBUG_LOG("%s connector %" PRIu32 " dpiX %" PRIi32 " dpiY %" PRIi32
+ " connection %d",
+ __FUNCTION__, connector.mId, connector.dpiX, connector.dpiY,
+ connector.connection);
+
+ drmModeFreeConnector(c);
+
+ connector.mRefreshRateAsFloat =
+ 1000.0f * connector.mMode.clock /
+ ((float)connector.mMode.vtotal * (float)connector.mMode.htotal);
+ connector.mRefreshRateAsInteger =
+ (uint32_t)(connector.mRefreshRateAsFloat + 0.5f);
+ }
+
+ mConnectors.push_back(connector);
+ }
+
+ drmModeFreeResources(res);
+ return true;
+}
+
+void DrmPresenter::resetDrmElementsLocked() {
+ for (auto& c : mConnectors) {
+ if (c.mModeBlobId) {
+ if (drmModeDestroyPropertyBlob(mFd.get(), c.mModeBlobId)) {
+ ALOGE("%s: Error destroy PropertyBlob %" PRIu32, __func__,
+ c.mModeBlobId);
+ }
+ }
+ }
+ mConnectors.clear();
+ mCrtcs.clear();
+ mPlanes.clear();
+}
+
+std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> DrmPresenter::create(
+ const native_handle_t* handle) {
+ auto buffer = std::unique_ptr<DrmBuffer>(new DrmBuffer(*this));
+
+ cros_gralloc_handle* crosHandle = (cros_gralloc_handle*)handle;
+ if (crosHandle == nullptr) {
+ ALOGE("%s: invalid cros_gralloc_handle", __FUNCTION__);
+ return std::make_tuple(HWC3::Error::NoResources,
+ std::unique_ptr<DrmBuffer>());
+ }
+
+ buffer->mWidth = crosHandle->width;
+ buffer->mHeight = crosHandle->height;
+ buffer->mDrmFormat = crosHandle->format;
+ buffer->mPlaneFds[0] = crosHandle->fds[0];
+ buffer->mPlanePitches[0] = crosHandle->strides[0];
+ buffer->mPlaneOffsets[0] = crosHandle->offsets[0];
+
+ HWC3::Error error = createDrmFramebuffer(buffer.get());
+ return std::make_tuple(error, std::move(buffer));
+}
+
+HWC3::Error DrmPresenter::createDrmFramebuffer(DrmBuffer* buffer) {
+ int ret;
+
+ ret = drmPrimeFDToHandle(mFd.get(), buffer->mPlaneFds[0],
+ &buffer->mPlaneHandles[0]);
+ if (ret) {
+ ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__,
+ strerror(errno), errno);
+ return HWC3::Error::NoResources;
+ }
+
+ uint32_t framebuffer = 0;
+ ret = drmModeAddFB2(mFd.get(), buffer->mWidth, buffer->mHeight,
+ buffer->mDrmFormat, buffer->mPlaneHandles,
+ buffer->mPlanePitches, buffer->mPlaneOffsets,
+ &framebuffer, 0);
+ if (ret) {
+ ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__,
+ strerror(errno), errno);
+ return HWC3::Error::NoResources;
+ }
+
+ DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
+
+ buffer->mDrmFramebuffer.emplace(framebuffer);
+ return HWC3::Error::None;
+}
+
+HWC3::Error DrmPresenter::destroyDrmFramebuffer(DrmBuffer* buffer) {
+ if (buffer->mDrmFramebuffer) {
+ uint32_t framebuffer = *buffer->mDrmFramebuffer;
+ if (drmModeRmFB(mFd.get(), framebuffer)) {
+ ALOGE("%s: drmModeRmFB failed: %s (errno %d)", __FUNCTION__,
+ strerror(errno), errno);
+ return HWC3::Error::NoResources;
+ }
+ DEBUG_LOG("%s: destroyed framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
+ buffer->mDrmFramebuffer.reset();
+ }
+ if (buffer->mPlaneHandles[0]) {
+ struct drm_gem_close gem_close = {};
+ gem_close.handle = buffer->mPlaneHandles[0];
+ if (drmIoctl(mFd.get(), DRM_IOCTL_GEM_CLOSE, &gem_close)) {
+ ALOGE("%s: DRM_IOCTL_GEM_CLOSE failed: %s (errno %d)", __FUNCTION__,
+ strerror(errno), errno);
+ return HWC3::Error::NoResources;
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+bool DrmPresenter::handleHotplug() {
+ std::vector<DrmConnector> oldConnectors(mConnectors);
+ {
+ AutoReadLock lock(mStateMutex);
+ oldConnectors.assign(mConnectors.begin(), mConnectors.end());
+ }
+ {
+ AutoWriteLock lock(mStateMutex);
+ resetDrmElementsLocked();
+ if (!initDrmElementsLocked()) {
+ ALOGE(
+ "%s: failed to initialize drm elements during hotplug. Displays may "
+ "not function correctly!",
+ __FUNCTION__);
+ return false;
+ }
+ }
+
+ AutoReadLock lock(mStateMutex);
+ for (int i = 0; i < mConnectors.size(); i++) {
+ bool changed =
+ oldConnectors[i].dpiX != mConnectors[i].dpiX ||
+ oldConnectors[i].dpiY != mConnectors[i].dpiY ||
+ oldConnectors[i].connection != mConnectors[i].connection ||
+ oldConnectors[i].mMode.hdisplay != mConnectors[i].mMode.hdisplay ||
+ oldConnectors[i].mMode.vdisplay != mConnectors[i].mMode.vdisplay;
+ if (changed) {
+ if (i == 0) {
+ ALOGE(
+ "%s: Ignoring changes to display:0 which is not configurable by "
+ "multi-display interface.",
+ __FUNCTION__);
+ continue;
+ }
+
+ const bool connected =
+ mConnectors[i].connection == DRM_MODE_CONNECTED ? true : false;
+ if (mHotplugCallback) {
+ (*mHotplugCallback)(connected, //
+ i, //
+ mConnectors[i].mMode.hdisplay, //
+ mConnectors[i].mMode.vdisplay, //
+ mConnectors[i].dpiX, //
+ mConnectors[i].dpiY, //
+ mConnectors[i].mRefreshRateAsInteger);
+ }
+ }
+ }
+ return true;
+}
+
+std::tuple<HWC3::Error, ::android::base::unique_fd>
+DrmPresenter::flushToDisplay(int display, const DrmBuffer& buffer,
+ ::android::base::borrowed_fd inSyncFd) {
+ ATRACE_CALL();
+
+ if (!buffer.mDrmFramebuffer) {
+ ALOGE("%s: failed, no framebuffer created.", __FUNCTION__);
+ return std::make_tuple(HWC3::Error::NoResources,
+ ::android::base::unique_fd());
+ }
+
+ AutoReadLock lock(mStateMutex);
+
+ DrmConnector& connector = mConnectors[display];
+ DrmCrtc& crtc = mCrtcs[display];
+
+ HWC3::Error error = HWC3::Error::None;
+
+ drmModeAtomicReqPtr pset = drmModeAtomicAlloc();
+
+ int ret;
+
+ if (!crtc.mDidSetCrtc) {
+ DEBUG_LOG("%s: Setting crtc.\n", __FUNCTION__);
+ ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mActivePropertyId, 1);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mModePropertyId,
+ connector.mModeBlobId);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, connector.mId,
+ connector.mCrtcPropertyId, crtc.mId);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+
+ crtc.mDidSetCrtc = true;
+ } else {
+ DEBUG_LOG("%s: Already set crtc\n", __FUNCTION__);
+ }
+
+ int flushFenceFd = -1;
+ ret = drmModeAtomicAddProperty(pset, crtc.mId, crtc.mOutFencePtrPropertyId,
+ addressAsUint(&flushFenceFd));
+ if (ret < 0) {
+ ALOGE("%s:%d: set OUT_FENCE_PTR failed %d errno %d\n", __FUNCTION__,
+ __LINE__, ret, errno);
+ }
+
+ if (crtc.mPlaneId == -1) {
+ ALOGE("%s:%d: no plane available for crtc id %" PRIu32, __FUNCTION__,
+ __LINE__, crtc.mId);
+ return std::make_tuple(HWC3::Error::NoResources,
+ ::android::base::unique_fd());
+ }
+
+ DrmPlane& plane = mPlanes[crtc.mPlaneId];
+
+ DEBUG_LOG("%s: set plane: plane id %d crtc id %d fbid %d bo w h %d %d\n",
+ __FUNCTION__, plane.mId, crtc.mId, *buffer.mDrmFramebuffer,
+ buffer.mWidth, buffer.mHeight);
+
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcPropertyId,
+ crtc.mId);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mInFenceFdPropertyId,
+ inSyncFd.get());
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mFbPropertyId,
+ *buffer.mDrmFramebuffer);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcXPropertyId, 0);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcYPropertyId, 0);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcWPropertyId,
+ buffer.mWidth);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mCrtcHPropertyId,
+ buffer.mHeight);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcXPropertyId, 0);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcYPropertyId, 0);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcWPropertyId,
+ buffer.mWidth << 16);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+ ret = drmModeAtomicAddProperty(pset, plane.mId, plane.mSrcHPropertyId,
+ buffer.mHeight << 16);
+ if (ret < 0) {
+ ALOGE("%s:%d: failed: %s\n", __FUNCTION__, __LINE__, strerror(errno));
+ }
+
+ constexpr const uint32_t kCommitFlags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+ ret = drmModeAtomicCommit(mFd.get(), pset, kCommitFlags, 0);
+
+ if (ret) {
+ ALOGE("%s:%d: atomic commit failed: %s\n", __FUNCTION__, __LINE__,
+ strerror(errno));
+ error = HWC3::Error::NoResources;
+ flushFenceFd = -1;
+ }
+
+ if (pset) {
+ drmModeAtomicFree(pset);
+ }
+
+ DEBUG_LOG("%s: flush fence:%d\n", __FUNCTION__, flushFenceFd);
+ return std::make_tuple(error, ::android::base::unique_fd(flushFenceFd));
+}
+
+std::optional<std::vector<uint8_t>> DrmPresenter::getEdid(uint32_t id) {
+ AutoReadLock lock(mStateMutex);
+
+ if (mConnectors[id].mEdidBlobId == -1) {
+ DEBUG_LOG("%s: EDID not supported", __func__);
+ return std::nullopt;
+ }
+ drmModePropertyBlobPtr blob =
+ drmModeGetPropertyBlob(mFd.get(), mConnectors[id].mEdidBlobId);
+ if (!blob) {
+ ALOGE("%s: fail to read EDID from DRM", __func__);
+ return std::nullopt;
+ }
+
+ std::vector<uint8_t> edid;
+ uint8_t* start = static_cast<uint8_t*>(blob->data);
+ edid.insert(edid.begin(), start, start + blob->length);
+
+ drmModeFreePropertyBlob(blob);
+
+ return edid;
+}
+
+DrmBuffer::DrmBuffer(DrmPresenter& DrmPresenter)
+ : mDrmPresenter(DrmPresenter) {}
+
+DrmBuffer::~DrmBuffer() { mDrmPresenter.destroyDrmFramebuffer(this); }
+
+DrmPresenter::DrmEventListener::DrmEventListener(DrmPresenter& presenter)
+ : mPresenter(presenter) {}
+
+DrmPresenter::DrmEventListener::~DrmEventListener() {}
+
+bool DrmPresenter::DrmEventListener::init() {
+ mEventFd = ::android::base::unique_fd(
+ socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT));
+ if (!mEventFd.ok()) {
+ ALOGE("Failed to open uevent socket: %s", strerror(errno));
+ return false;
+ }
+ struct sockaddr_nl addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0xFFFFFFFF;
+
+ int ret = bind(mEventFd, (struct sockaddr*)&addr, sizeof(addr));
+ if (ret) {
+ ALOGE("Failed to bind uevent socket: %s", strerror(errno));
+ return false;
+ }
+
+ FD_ZERO(&mMonitoredFds);
+ FD_SET(mPresenter.mFd.get(), &mMonitoredFds);
+ FD_SET(mEventFd.get(), &mMonitoredFds);
+ mMaxFd = std::max(mPresenter.mFd.get(), mEventFd.get());
+
+ return true;
+}
+
+bool DrmPresenter::DrmEventListener::threadLoop() {
+ int ret;
+ do {
+ ret = select(mMaxFd + 1, &mMonitoredFds, NULL, NULL, NULL);
+ } while (ret == -1 && errno == EINTR);
+
+ // if (FD_ISSET(mPresenter.mFd, &mFds)) {
+ // TODO: handle drm related events
+ // }
+
+ if (FD_ISSET(mEventFd.get(), &mMonitoredFds)) {
+ eventThreadLoop();
+ }
+ return true;
+}
+
+void DrmPresenter::DrmEventListener::eventThreadLoop() {
+ char buffer[1024];
+ int ret;
+
+ struct timespec ts;
+ uint64_t timestamp = 0;
+ ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (!ret) {
+ timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+ } else {
+ ALOGE("Failed to get monotonic clock on hotplug %d", ret);
+ }
+
+ while (true) {
+ ret = read(mEventFd.get(), &buffer, sizeof(buffer));
+ if (ret == 0) {
+ return;
+ } else if (ret < 0) {
+ ALOGE("Got error reading uevent %d", ret);
+ return;
+ }
+
+ bool drmEvent = false, hotplugEvent = false;
+ for (int i = 0; i < ret;) {
+ char* event = buffer + i;
+ if (strcmp(event, "DEVTYPE=drm_minor")) {
+ drmEvent = true;
+ } else if (strcmp(event, "HOTPLUG=1")) {
+ hotplugEvent = true;
+ }
+
+ i += strlen(event) + 1;
+ }
+
+ if (drmEvent && hotplugEvent) {
+ processHotplug(timestamp);
+ }
+ }
+}
+
+void DrmPresenter::DrmEventListener::processHotplug(uint64_t timestamp) {
+ ALOGD("DrmEventListener detected hotplug event %" PRIu64, timestamp);
+ mPresenter.handleHotplug();
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/DrmPresenter.h b/system/hwc3/DrmPresenter.h
new file mode 100644
index 0000000..61e546a
--- /dev/null
+++ b/system/hwc3/DrmPresenter.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_DRMPRESENTER_H
+#define ANDROID_HWC_DRMPRESENTER_H
+
+#include <android-base/unique_fd.h>
+#include <cutils/native_handle.h>
+#include <utils/Thread.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include <map>
+#include <memory>
+#include <tuple>
+#include <vector>
+
+#include "Common.h"
+#include "android/base/synchronization/AndroidLock.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class DrmBuffer;
+class DrmPresenter;
+
+// A RAII object that will clear a drm framebuffer upon destruction.
+class DrmBuffer {
+ public:
+ ~DrmBuffer();
+
+ DrmBuffer(const DrmBuffer&) = delete;
+ DrmBuffer& operator=(const DrmBuffer&) = delete;
+
+ DrmBuffer(DrmBuffer&&) = delete;
+ DrmBuffer& operator=(DrmBuffer&&) = delete;
+
+ private:
+ friend class DrmPresenter;
+ DrmBuffer(DrmPresenter& drmPresenter);
+
+ DrmPresenter& mDrmPresenter;
+
+ uint32_t mWidth = 0;
+ uint32_t mHeight = 0;
+ uint32_t mDrmFormat = 0;
+ uint32_t mPlaneFds[4] = {0, 0, 0, 0};
+ uint32_t mPlaneHandles[4] = {0, 0, 0, 0};
+ uint32_t mPlanePitches[4] = {0, 0, 0, 0};
+ uint32_t mPlaneOffsets[4] = {0, 0, 0, 0};
+
+ std::optional<uint32_t> mDrmFramebuffer;
+};
+
+class DrmPresenter {
+ public:
+ DrmPresenter() = default;
+ ~DrmPresenter();
+
+ DrmPresenter(const DrmPresenter&) = delete;
+ DrmPresenter& operator=(const DrmPresenter&) = delete;
+
+ DrmPresenter(DrmPresenter&&) = delete;
+ DrmPresenter& operator=(DrmPresenter&&) = delete;
+
+ HWC3::Error init();
+
+ using HotplugCallback = std::function<void(bool /*connected*/, //
+ uint32_t /*id*/, //
+ uint32_t /*width*/, //
+ uint32_t /*height*/, //
+ uint32_t /*dpiX*/, //
+ uint32_t /*dpiY*/, //
+ uint32_t /*refreshRate*/)>;
+
+ HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb);
+ HWC3::Error unregisterOnHotplugCallback();
+
+ uint32_t refreshRate() const { return mConnectors[0].mRefreshRateAsInteger; }
+
+ std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> create(
+ const native_handle_t* handle);
+
+ std::tuple<HWC3::Error, ::android::base::unique_fd> flushToDisplay(
+ int display, const DrmBuffer& buffer,
+ ::android::base::borrowed_fd inWaitSyncFd);
+
+ std::optional<std::vector<uint8_t>> getEdid(uint32_t id);
+
+ private:
+ // Grant visibility for createDrmFramebuffer and clearDrmFB to DrmBuffer.
+ friend class DrmBuffer;
+ HWC3::Error createDrmFramebuffer(DrmBuffer* buffer);
+ HWC3::Error destroyDrmFramebuffer(DrmBuffer* buffer);
+
+ // Grant visibility for handleHotplug to DrmEventListener.
+ bool handleHotplug();
+
+ bool initDrmElementsLocked();
+ void resetDrmElementsLocked();
+
+ // Drm device.
+ ::android::base::unique_fd mFd;
+
+ std::optional<HotplugCallback> mHotplugCallback;
+
+ // Protects access to the below drm structs.
+ ::android::base::guest::ReadWriteLock mStateMutex;
+
+ struct DrmPlane {
+ uint32_t mId = -1;
+ uint32_t mCrtcPropertyId = -1;
+ uint32_t mInFenceFdPropertyId = -1;
+ uint32_t mFbPropertyId = -1;
+ uint32_t mCrtcXPropertyId = -1;
+ uint32_t mCrtcYPropertyId = -1;
+ uint32_t mCrtcWPropertyId = -1;
+ uint32_t mCrtcHPropertyId = -1;
+ uint32_t mSrcXPropertyId = -1;
+ uint32_t mSrcYPropertyId = -1;
+ uint32_t mSrcWPropertyId = -1;
+ uint32_t mSrcHPropertyId = -1;
+ uint32_t mTypePropertyId = -1;
+ uint64_t mType = -1;
+ };
+ std::map<uint32_t, DrmPlane> mPlanes;
+
+ struct DrmCrtc {
+ uint32_t mId = -1;
+ uint32_t mActivePropertyId = -1;
+ uint32_t mModePropertyId = -1;
+ uint32_t mOutFencePtrPropertyId = -1;
+ uint32_t mPlaneId = -1;
+
+ bool mDidSetCrtc = false;
+ };
+ std::vector<DrmCrtc> mCrtcs;
+
+ struct DrmConnector {
+ uint32_t mId = -1;
+ uint32_t mCrtcPropertyId = -1;
+ drmModeModeInfo mMode;
+ int32_t dpiX;
+ int32_t dpiY;
+ drmModeConnection connection;
+ uint32_t mModeBlobId = 0;
+ float mRefreshRateAsFloat;
+ uint32_t mRefreshRateAsInteger;
+ uint64_t mEdidBlobId = -1;
+ };
+ std::vector<DrmConnector> mConnectors;
+
+ class DrmEventListener : public ::android::Thread {
+ public:
+ DrmEventListener(DrmPresenter& presenter);
+ virtual ~DrmEventListener();
+
+ bool init();
+
+ private:
+ bool threadLoop() final;
+ void eventThreadLoop();
+ void processHotplug(uint64_t timestamp);
+
+ DrmPresenter& mPresenter;
+ ::android::base::unique_fd mEventFd;
+ int mMaxFd;
+ fd_set mMonitoredFds;
+ };
+ ::android::sp<DrmEventListener> mDrmEventListener;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/FencedBuffer.h b/system/hwc3/FencedBuffer.h
new file mode 100644
index 0000000..50e8534
--- /dev/null
+++ b/system/hwc3/FencedBuffer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_FENCEDBUFFER_H
+#define ANDROID_HWC_FENCEDBUFFER_H
+
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/unique_fd.h>
+#include <cutils/native_handle.h>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class FencedBuffer {
+ public:
+ FencedBuffer() : mBuffer(nullptr) {}
+
+ void set(buffer_handle_t buffer, const ndk::ScopedFileDescriptor& fence) {
+ mBuffer = buffer;
+ mFence = GetUniqueFd(fence);
+ }
+
+ buffer_handle_t getBuffer() const { return mBuffer; }
+
+ ::android::base::unique_fd getFence() const {
+ if (mFence.ok()) {
+ return ::android::base::unique_fd(dup(mFence.get()));
+ } else {
+ return ::android::base::unique_fd();
+ }
+ }
+
+ private:
+ static ::android::base::unique_fd GetUniqueFd(
+ const ndk::ScopedFileDescriptor& in) {
+ auto& sfd = const_cast<ndk::ScopedFileDescriptor&>(in);
+ ::android::base::unique_fd ret(sfd.get());
+ *sfd.getR() = -1;
+ return ret;
+ }
+
+ buffer_handle_t mBuffer;
+ ::android::base::unique_fd mFence;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/FrameComposer.h b/system/hwc3/FrameComposer.h
new file mode 100644
index 0000000..fdb6db3
--- /dev/null
+++ b/system/hwc3/FrameComposer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_COMPOSER_H
+#define ANDROID_HWC_COMPOSER_H
+
+#include <android-base/unique_fd.h>
+
+#include <functional>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include "Common.h"
+#include "DisplayChanges.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class Display;
+
+class FrameComposer {
+ public:
+ virtual ~FrameComposer() {}
+
+ virtual HWC3::Error init() = 0;
+
+ using HotplugCallback = std::function<void(bool /*connected*/, //
+ uint32_t /*id*/, //
+ uint32_t /*width*/, //
+ uint32_t /*height*/, //
+ uint32_t /*dpiX*/, //
+ uint32_t /*dpiY*/, //
+ uint32_t /*refreshRate*/)>;
+
+ virtual HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) = 0;
+
+ virtual HWC3::Error unregisterOnHotplugCallback() = 0;
+
+ virtual HWC3::Error onDisplayCreate(Display* display) = 0;
+
+ virtual HWC3::Error onDisplayDestroy(Display* display) = 0;
+
+ virtual HWC3::Error onDisplayClientTargetSet(Display* display) = 0;
+
+ // Determines if this composer can compose the given layers and requests
+ // changes for layers that can't not be composed.
+ virtual HWC3::Error validateDisplay(Display* display,
+ DisplayChanges* outChanges) = 0;
+
+ // Performs the actual composition of layers and presents the composed result
+ // to the display.
+ virtual HWC3::Error presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>*
+ outLayerFences) = 0;
+
+ virtual HWC3::Error onActiveConfigChange(Display* display) = 0;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Gralloc.cpp b/system/hwc3/Gralloc.cpp
new file mode 100644
index 0000000..9234df4
--- /dev/null
+++ b/system/hwc3/Gralloc.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2022 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 "Gralloc.h"
+
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponent.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+#include <drm_fourcc.h>
+#include <gralloctypes/Gralloc4.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <algorithm>
+
+#include "Drm.h"
+
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_vec;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+using android::hardware::graphics::mapper::V4_0::Error;
+using android::hardware::graphics::mapper::V4_0::IMapper;
+using MetadataType =
+ android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+Gralloc::Gralloc() {
+ ::android::hardware::preloadPassthroughService<IMapper>();
+
+ gralloc4_ = IMapper::getService();
+ if (gralloc4_ != nullptr) {
+ ALOGE("%s using Gralloc4.", __FUNCTION__);
+ return;
+ }
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+
+ ALOGE("%s No Grallocs available!", __FUNCTION__);
+}
+
+Error Gralloc::GetMetadata(buffer_handle_t buffer, MetadataType type,
+ hidl_vec<uint8_t>* metadata) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return Error::NO_RESOURCES;
+ }
+
+ if (metadata == nullptr) {
+ return Error::BAD_VALUE;
+ }
+
+ Error error = Error::NONE;
+
+ auto native_handle = const_cast<native_handle_t*>(buffer);
+
+ auto ret =
+ gralloc4_->get(native_handle, type,
+ [&](const auto& get_error, const auto& get_metadata) {
+ error = get_error;
+ *metadata = get_metadata;
+ });
+
+ if (!ret.isOk()) {
+ error = Error::NO_RESOURCES;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("%s failed to get metadata %s", __FUNCTION__, type.name.c_str());
+ }
+ return error;
+}
+
+std::optional<uint32_t> Gralloc::GetWidth(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ hidl_vec<uint8_t> encoded_width;
+
+ Error error = GetMetadata(buffer, ::android::gralloc4::MetadataType_Width,
+ &encoded_width);
+ if (error != Error::NONE) {
+ return std::nullopt;
+ }
+
+ uint64_t width = 0;
+ ::android::gralloc4::decodeWidth(encoded_width, &width);
+ return static_cast<uint32_t>(width);
+}
+
+std::optional<uint32_t> Gralloc::GetHeight(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ hidl_vec<uint8_t> encoded_height;
+
+ Error error = GetMetadata(buffer, ::android::gralloc4::MetadataType_Height,
+ &encoded_height);
+ if (error != Error::NONE) {
+ return std::nullopt;
+ }
+
+ uint64_t height = 0;
+ ::android::gralloc4::decodeHeight(encoded_height, &height);
+ return static_cast<uint32_t>(height);
+}
+
+std::optional<uint32_t> Gralloc::GetDrmFormat(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ hidl_vec<uint8_t> encoded_format;
+
+ Error error =
+ GetMetadata(buffer, ::android::gralloc4::MetadataType_PixelFormatFourCC,
+ &encoded_format);
+ if (error != Error::NONE) {
+ return std::nullopt;
+ }
+
+ uint32_t format = 0;
+ ::android::gralloc4::decodePixelFormatFourCC(encoded_format, &format);
+ return static_cast<uint32_t>(format);
+}
+
+std::optional<std::vector<PlaneLayout>> Gralloc::GetPlaneLayouts(
+ buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ hidl_vec<uint8_t> encoded_layouts;
+
+ Error error = GetMetadata(
+ buffer, ::android::gralloc4::MetadataType_PlaneLayouts, &encoded_layouts);
+ if (error != Error::NONE) {
+ return std::nullopt;
+ }
+
+ std::vector<PlaneLayout> plane_layouts;
+ ::android::gralloc4::decodePlaneLayouts(encoded_layouts, &plane_layouts);
+ return plane_layouts;
+}
+
+std::optional<uint32_t> Gralloc::GetMonoPlanarStrideBytes(
+ buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ auto plane_layouts_opt = GetPlaneLayouts(buffer);
+ if (!plane_layouts_opt) {
+ return std::nullopt;
+ }
+
+ std::vector<PlaneLayout>& plane_layouts = *plane_layouts_opt;
+ if (plane_layouts.size() != 1) {
+ return std::nullopt;
+ }
+
+ return static_cast<uint32_t>(plane_layouts[0].strideInBytes);
+}
+
+std::optional<GrallocBuffer> Gralloc::Import(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ buffer_handle_t imported_buffer;
+
+ Error error;
+ auto ret =
+ gralloc4_->importBuffer(buffer, [&](const auto& err, const auto& buf) {
+ error = err;
+ if (err == Error::NONE) {
+ imported_buffer = static_cast<buffer_handle_t>(buf);
+ }
+ });
+
+ if (!ret.isOk() || error != Error::NONE) {
+ ALOGE("%s failed to import buffer", __FUNCTION__);
+ return std::nullopt;
+ }
+ return GrallocBuffer(this, imported_buffer);
+}
+
+void Gralloc::Release(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return;
+ }
+
+ auto native_buffer = const_cast<native_handle_t*>(buffer);
+ auto ret = gralloc4_->freeBuffer(native_buffer);
+
+ if (!ret.isOk()) {
+ ALOGE("%s failed to release buffer", __FUNCTION__);
+ }
+}
+
+std::optional<void*> Gralloc::Lock(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ auto native_buffer = const_cast<native_handle_t*>(buffer);
+
+ const auto buffer_usage = static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN |
+ BufferUsage::CPU_WRITE_OFTEN);
+
+ auto width_opt = GetWidth(buffer);
+ if (!width_opt) {
+ return std::nullopt;
+ }
+
+ auto height_opt = GetHeight(buffer);
+ if (!height_opt) {
+ return std::nullopt;
+ }
+
+ IMapper::Rect buffer_region;
+ buffer_region.left = 0;
+ buffer_region.top = 0;
+ buffer_region.width = *width_opt;
+ buffer_region.height = *height_opt;
+
+ // Empty fence, lock immedietly.
+ hidl_handle fence;
+
+ Error error = Error::NONE;
+ void* data = nullptr;
+
+ auto ret =
+ gralloc4_->lock(native_buffer, buffer_usage, buffer_region, fence,
+ [&](const auto& lock_error, const auto& lock_data) {
+ error = lock_error;
+ if (lock_error == Error::NONE) {
+ data = lock_data;
+ }
+ });
+
+ if (!ret.isOk()) {
+ error = Error::NO_RESOURCES;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("%s failed to lock buffer", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ return data;
+}
+
+std::optional<android_ycbcr> Gralloc::LockYCbCr(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ auto format_opt = GetDrmFormat(buffer);
+ if (!format_opt) {
+ ALOGE("%s failed to check format of buffer", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ if (*format_opt != DRM_FORMAT_NV12 && *format_opt != DRM_FORMAT_NV21 &&
+ *format_opt != DRM_FORMAT_YVU420) {
+ ALOGE("%s called on non-ycbcr buffer", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ auto lock_opt = Lock(buffer);
+ if (!lock_opt) {
+ ALOGE("%s failed to lock buffer", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ auto plane_layouts_opt = GetPlaneLayouts(buffer);
+ if (!plane_layouts_opt) {
+ ALOGE("%s failed to get plane layouts", __FUNCTION__);
+ return std::nullopt;
+ }
+
+ android_ycbcr buffer_ycbcr;
+ buffer_ycbcr.y = nullptr;
+ buffer_ycbcr.cb = nullptr;
+ buffer_ycbcr.cr = nullptr;
+ buffer_ycbcr.ystride = 0;
+ buffer_ycbcr.cstride = 0;
+ buffer_ycbcr.chroma_step = 0;
+
+ for (const auto& plane_layout : *plane_layouts_opt) {
+ for (const auto& plane_layout_component : plane_layout.components) {
+ const auto& type = plane_layout_component.type;
+
+ if (!::android::gralloc4::isStandardPlaneLayoutComponentType(type)) {
+ continue;
+ }
+
+ auto* component_data = reinterpret_cast<uint8_t*>(*lock_opt) +
+ plane_layout.offsetInBytes +
+ plane_layout_component.offsetInBits / 8;
+
+ switch (static_cast<PlaneLayoutComponentType>(type.value)) {
+ case PlaneLayoutComponentType::Y:
+ buffer_ycbcr.y = component_data;
+ buffer_ycbcr.ystride = plane_layout.strideInBytes;
+ break;
+ case PlaneLayoutComponentType::CB:
+ buffer_ycbcr.cb = component_data;
+ buffer_ycbcr.cstride = plane_layout.strideInBytes;
+ buffer_ycbcr.chroma_step = plane_layout.sampleIncrementInBits / 8;
+ break;
+ case PlaneLayoutComponentType::CR:
+ buffer_ycbcr.cr = component_data;
+ buffer_ycbcr.cstride = plane_layout.strideInBytes;
+ buffer_ycbcr.chroma_step = plane_layout.sampleIncrementInBits / 8;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return buffer_ycbcr;
+}
+
+void Gralloc::Unlock(buffer_handle_t buffer) {
+ if (gralloc4_ == nullptr) {
+ ALOGE("%s Gralloc4 not available.", __FUNCTION__);
+ return;
+ }
+
+ auto native_handle = const_cast<native_handle_t*>(buffer);
+
+ Error error = Error::NONE;
+ auto ret = gralloc4_->unlock(
+ native_handle,
+ [&](const auto& unlock_error, const auto&) { error = unlock_error; });
+
+ if (!ret.isOk()) {
+ error = Error::NO_RESOURCES;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("%s failed to unlock buffer", __FUNCTION__);
+ }
+}
+
+GrallocBuffer::GrallocBuffer(Gralloc* gralloc, buffer_handle_t buffer)
+ : gralloc_(gralloc), buffer_(buffer) {}
+
+GrallocBuffer::~GrallocBuffer() { Release(); }
+
+GrallocBuffer::GrallocBuffer(GrallocBuffer&& rhs) { *this = std::move(rhs); }
+
+GrallocBuffer& GrallocBuffer::operator=(GrallocBuffer&& rhs) {
+ gralloc_ = rhs.gralloc_;
+ buffer_ = rhs.buffer_;
+ rhs.gralloc_ = nullptr;
+ rhs.buffer_ = nullptr;
+ return *this;
+}
+
+void GrallocBuffer::Release() {
+ if (gralloc_ && buffer_) {
+ gralloc_->Release(buffer_);
+ gralloc_ = nullptr;
+ buffer_ = nullptr;
+ }
+}
+
+std::optional<GrallocBufferView> GrallocBuffer::Lock() {
+ if (gralloc_ && buffer_) {
+ auto format_opt = GetDrmFormat();
+ if (!format_opt) {
+ ALOGE("%s failed to check format of buffer", __FUNCTION__);
+ return std::nullopt;
+ }
+ if (*format_opt != DRM_FORMAT_NV12 && *format_opt != DRM_FORMAT_NV21 &&
+ *format_opt != DRM_FORMAT_YVU420) {
+ auto locked_opt = gralloc_->Lock(buffer_);
+ if (!locked_opt) {
+ return std::nullopt;
+ }
+ return GrallocBufferView(this, *locked_opt);
+ } else {
+ auto locked_ycbcr_opt = gralloc_->LockYCbCr(buffer_);
+ if (!locked_ycbcr_opt) {
+ ALOGE("%s failed to lock ycbcr buffer", __FUNCTION__);
+ return std::nullopt;
+ }
+ return GrallocBufferView(this, *locked_ycbcr_opt);
+ }
+ }
+ return std::nullopt;
+}
+
+void GrallocBuffer::Unlock() {
+ if (gralloc_ && buffer_) {
+ gralloc_->Unlock(buffer_);
+ }
+}
+
+std::optional<uint32_t> GrallocBuffer::GetWidth() {
+ if (gralloc_ && buffer_) {
+ return gralloc_->GetWidth(buffer_);
+ }
+ return std::nullopt;
+}
+
+std::optional<uint32_t> GrallocBuffer::GetHeight() {
+ if (gralloc_ && buffer_) {
+ return gralloc_->GetHeight(buffer_);
+ }
+ return std::nullopt;
+}
+
+std::optional<uint32_t> GrallocBuffer::GetDrmFormat() {
+ if (gralloc_ && buffer_) {
+ return gralloc_->GetDrmFormat(buffer_);
+ }
+ return std::nullopt;
+}
+
+std::optional<std::vector<PlaneLayout>> GrallocBuffer::GetPlaneLayouts() {
+ if (gralloc_ && buffer_) {
+ return gralloc_->GetPlaneLayouts(buffer_);
+ }
+ return std::nullopt;
+}
+
+std::optional<uint32_t> GrallocBuffer::GetMonoPlanarStrideBytes() {
+ if (gralloc_ && buffer_) {
+ return gralloc_->GetMonoPlanarStrideBytes(buffer_);
+ }
+ return std::nullopt;
+}
+
+GrallocBufferView::GrallocBufferView(GrallocBuffer* buffer, void* raw)
+ : gralloc_buffer_(buffer), locked_(raw) {}
+
+GrallocBufferView::GrallocBufferView(GrallocBuffer* buffer, android_ycbcr raw)
+ : gralloc_buffer_(buffer), locked_ycbcr_(raw) {}
+
+GrallocBufferView::~GrallocBufferView() {
+ if (gralloc_buffer_) {
+ gralloc_buffer_->Unlock();
+ }
+}
+
+GrallocBufferView::GrallocBufferView(GrallocBufferView&& rhs) {
+ *this = std::move(rhs);
+}
+
+GrallocBufferView& GrallocBufferView::operator=(GrallocBufferView&& rhs) {
+ std::swap(gralloc_buffer_, rhs.gralloc_buffer_);
+ std::swap(locked_, rhs.locked_);
+ std::swap(locked_ycbcr_, rhs.locked_ycbcr_);
+ return *this;
+}
+
+const std::optional<void*> GrallocBufferView::Get() const { return locked_; }
+
+const std::optional<android_ycbcr>& GrallocBufferView::GetYCbCr() const {
+ return locked_ycbcr_;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Gralloc.h b/system/hwc3/Gralloc.h
new file mode 100644
index 0000000..f259f29
--- /dev/null
+++ b/system/hwc3/Gralloc.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_GRALLOC_H
+#define ANDROID_HWC_GRALLOC_H
+
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class Gralloc;
+class GrallocBuffer;
+
+// An RAII object that will Unlock() a GrallocBuffer upon destruction.
+class GrallocBufferView {
+ public:
+ virtual ~GrallocBufferView();
+
+ GrallocBufferView(const GrallocBufferView& rhs) = delete;
+ GrallocBufferView& operator=(const GrallocBufferView& rhs) = delete;
+
+ GrallocBufferView(GrallocBufferView&& rhs);
+ GrallocBufferView& operator=(GrallocBufferView&& rhs);
+
+ const std::optional<void*> Get() const;
+
+ const std::optional<android_ycbcr>& GetYCbCr() const;
+
+ private:
+ friend class GrallocBuffer;
+ GrallocBufferView(GrallocBuffer* buffer, void* raw);
+ GrallocBufferView(GrallocBuffer* buffer, android_ycbcr raw);
+
+ // The GrallocBuffer that should be unlocked upon destruction of this object.
+ GrallocBuffer* gralloc_buffer_ = nullptr;
+
+ std::optional<void*> locked_;
+ std::optional<android_ycbcr> locked_ycbcr_;
+};
+
+// A gralloc 4.0 buffer that has been imported in the current process and
+// that will be released upon destruction. Users must ensure that the Gralloc
+// instance that this buffer is created with out lives this buffer.
+class GrallocBuffer {
+ public:
+ GrallocBuffer(Gralloc* gralloc, buffer_handle_t buffer);
+ virtual ~GrallocBuffer();
+
+ GrallocBuffer(const GrallocBuffer& rhs) = delete;
+ GrallocBuffer& operator=(const GrallocBuffer& rhs) = delete;
+
+ GrallocBuffer(GrallocBuffer&& rhs);
+ GrallocBuffer& operator=(GrallocBuffer&& rhs);
+
+ // Locks the buffer for reading and returns a view if successful.
+ std::optional<GrallocBufferView> Lock();
+
+ std::optional<uint32_t> GetWidth();
+ std::optional<uint32_t> GetHeight();
+ std::optional<uint32_t> GetDrmFormat();
+
+ // Returns the stride of the buffer if it is a single plane buffer or fails
+ // and returns nullopt if the buffer is for a multi plane buffer.
+ std::optional<uint32_t> GetMonoPlanarStrideBytes();
+
+ std::optional<
+ std::vector<aidl::android::hardware::graphics::common::PlaneLayout>>
+ GetPlaneLayouts();
+
+ private:
+ // Internal visibility for Unlock().
+ friend class GrallocBufferView;
+
+ // Unlocks the buffer from reading.
+ void Unlock();
+
+ void Release();
+
+ Gralloc* gralloc_ = nullptr;
+ buffer_handle_t buffer_ = nullptr;
+};
+
+class Gralloc {
+ public:
+ Gralloc();
+ virtual ~Gralloc() = default;
+
+ // Imports the given buffer handle into the current process and returns an
+ // imported buffer which can be used for reading. Users must ensure that the
+ // Gralloc instance outlives any GrallocBuffers.
+ std::optional<GrallocBuffer> Import(buffer_handle_t buffer);
+
+ private:
+ // The below functions are made available only to GrallocBuffer so that
+ // users only call gralloc functions on *imported* buffers.
+ friend class GrallocBuffer;
+
+ // See GrallocBuffer::Release.
+ void Release(buffer_handle_t buffer);
+
+ // See GrallocBuffer::Lock.
+ std::optional<void*> Lock(buffer_handle_t buffer);
+
+ // See GrallocBuffer::LockYCbCr.
+ std::optional<android_ycbcr> LockYCbCr(buffer_handle_t buffer);
+
+ // See GrallocBuffer::Unlock.
+ void Unlock(buffer_handle_t buffer);
+
+ // See GrallocBuffer::GetWidth.
+ std::optional<uint32_t> GetWidth(buffer_handle_t buffer);
+
+ // See GrallocBuffer::GetHeight.
+ std::optional<uint32_t> GetHeight(buffer_handle_t buffer);
+
+ // See GrallocBuffer::GetDrmFormat.
+ std::optional<uint32_t> GetDrmFormat(buffer_handle_t buffer);
+
+ // See GrallocBuffer::GetPlaneLayouts.
+ std::optional<
+ std::vector<aidl::android::hardware::graphics::common::PlaneLayout>>
+ GetPlaneLayouts(buffer_handle_t buffer);
+
+ // Returns the stride of the buffer if it is a single plane buffer or fails
+ // and returns nullopt if the buffer is for a multi plane buffer.
+ std::optional<uint32_t> GetMonoPlanarStrideBytes(buffer_handle_t);
+
+ // See GrallocBuffer::GetMetadata.
+ ::android::hardware::graphics::mapper::V4_0::Error GetMetadata(
+ buffer_handle_t buffer,
+ ::android::hardware::graphics::mapper::V4_0::IMapper::MetadataType type,
+ ::android::hardware::hidl_vec<uint8_t>* metadata);
+
+ ::android::sp<::android::hardware::graphics::mapper::V4_0::IMapper> gralloc4_;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/GuestFrameComposer.cpp b/system/hwc3/GuestFrameComposer.cpp
new file mode 100644
index 0000000..b7d7322
--- /dev/null
+++ b/system/hwc3/GuestFrameComposer.cpp
@@ -0,0 +1,1185 @@
+/*
+ * Copyright 2022 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 "GuestFrameComposer.h"
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+#include <device_config_shared.h>
+#include <drm_fourcc.h>
+#include <libyuv.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include "Display.h"
+#include "Drm.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+using ::android::hardware::graphics::common::V1_0::ColorTransform;
+
+uint64_t AlignToPower2(uint64_t val, uint8_t align_log) {
+ uint64_t align = 1ULL << align_log;
+ return ((val + (align - 1)) / align) * align;
+}
+
+bool LayerNeedsScaling(const Layer& layer) {
+ common::Rect crop = layer.getSourceCropInt();
+ common::Rect frame = layer.getDisplayFrame();
+
+ int fromW = crop.right - crop.left;
+ int fromH = crop.bottom - crop.top;
+ int toW = frame.right - frame.left;
+ int toH = frame.bottom - frame.top;
+
+ bool not_rot_scale = fromW != toW || fromH != toH;
+ bool rot_scale = fromW != toH || fromH != toW;
+
+ bool needs_rot = static_cast<int32_t>(layer.getTransform()) &
+ static_cast<int32_t>(common::Transform::ROT_90);
+
+ return needs_rot ? rot_scale : not_rot_scale;
+}
+
+bool LayerNeedsBlending(const Layer& layer) {
+ return layer.getBlendMode() != common::BlendMode::NONE;
+}
+
+bool LayerNeedsAttenuation(const Layer& layer) {
+ return layer.getBlendMode() == common::BlendMode::COVERAGE;
+}
+
+struct BufferSpec;
+typedef int (*ConverterFunction)(const BufferSpec& src, const BufferSpec& dst,
+ bool v_flip);
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool vFlip);
+int ConvertFromRGB565(const BufferSpec& src, const BufferSpec& dst, bool vFlip);
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool vFlip);
+
+ConverterFunction GetConverterForDrmFormat(uint32_t drmFormat) {
+ switch (drmFormat) {
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ return &DoCopy;
+ case DRM_FORMAT_RGB565:
+ return &ConvertFromRGB565;
+ case DRM_FORMAT_YVU420:
+ return &ConvertFromYV12;
+ }
+ DEBUG_LOG("Unsupported drm format: %d(%s), returning null converter",
+ drmFormat, GetDrmFormatString(drmFormat));
+ return nullptr;
+}
+
+bool IsDrmFormatSupported(uint32_t drmFormat) {
+ return GetConverterForDrmFormat(drmFormat) != nullptr;
+}
+
+// Libyuv's convert functions only allow the combination of any rotation
+// (multiple of 90 degrees) and a vertical flip, but not horizontal flips.
+// Surfaceflinger's transformations are expressed in terms of a vertical flip,
+// a horizontal flip and/or a single 90 degrees clockwise rotation (see
+// NATIVE_WINDOW_TRANSFORM_HINT documentation on system/window.h for more
+// insight). The following code allows to turn a horizontal flip into a 180
+// degrees rotation and a vertical flip.
+libyuv::RotationMode GetRotationFromTransform(common::Transform transform) {
+ uint32_t rotation = 0;
+ rotation += (static_cast<int32_t>(transform) &
+ static_cast<int32_t>(common::Transform::ROT_90))
+ ? 1
+ : 0; // 1 * ROT90 bit
+ rotation += (static_cast<int32_t>(transform) &
+ static_cast<int32_t>(common::Transform::FLIP_H))
+ ? 2
+ : 0; // 2 * VFLIP bit
+ return static_cast<libyuv::RotationMode>(90 * rotation);
+}
+
+bool GetVFlipFromTransform(common::Transform transform) {
+ // vertical flip xor horizontal flip
+ bool hasVFlip = static_cast<int32_t>(transform) &
+ static_cast<int32_t>(common::Transform::FLIP_V);
+ bool hasHFlip = static_cast<int32_t>(transform) &
+ static_cast<int32_t>(common::Transform::FLIP_H);
+ return hasVFlip ^ hasHFlip;
+}
+
+struct BufferSpec {
+ uint8_t* buffer;
+ std::optional<android_ycbcr> buffer_ycbcr;
+ int width;
+ int height;
+ int cropX;
+ int cropY;
+ int cropWidth;
+ int cropHeight;
+ uint32_t drmFormat;
+ int strideBytes;
+ int sampleBytes;
+
+ BufferSpec(uint8_t* buffer, std::optional<android_ycbcr> buffer_ycbcr,
+ int width, int height, int cropX, int cropY, int cropWidth,
+ int cropHeight, uint32_t drmFormat, int strideBytes,
+ int sampleBytes)
+ : buffer(buffer),
+ buffer_ycbcr(buffer_ycbcr),
+ width(width),
+ height(height),
+ cropX(cropX),
+ cropY(cropY),
+ cropWidth(cropWidth),
+ cropHeight(cropHeight),
+ drmFormat(drmFormat),
+ strideBytes(strideBytes),
+ sampleBytes(sampleBytes) {}
+
+ BufferSpec(uint8_t* buffer, int width, int height, int strideBytes)
+ : BufferSpec(buffer,
+ /*buffer_ycbcr=*/std::nullopt, width, height,
+ /*cropX=*/0,
+ /*cropY=*/0,
+ /*cropWidth=*/width,
+ /*cropHeight=*/height,
+ /*drmFormat=*/DRM_FORMAT_ABGR8888, strideBytes,
+ /*sampleBytes=*/4) {}
+};
+
+int ConvertFromRGB565(const BufferSpec& src, const BufferSpec& dst,
+ bool vFlip) {
+ ATRACE_CALL();
+
+ // Point to the upper left corner of the crop rectangle
+ uint8_t* srcBuffer =
+ src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+ uint8_t* dstBuffer =
+ dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+
+ int width = src.cropWidth;
+ int height = src.cropHeight;
+ if (vFlip) {
+ height = -height;
+ }
+
+ return libyuv::RGB565ToARGB(srcBuffer, src.strideBytes, //
+ dstBuffer, dst.strideBytes, //
+ width, height);
+}
+
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool vFlip) {
+ ATRACE_CALL();
+
+ // The following calculation of plane offsets and alignments are based on
+ // swiftshader's Sampler::setTextureLevel() implementation
+ // (Renderer/Sampler.cpp:225)
+
+ auto& srcBufferYCbCrOpt = src.buffer_ycbcr;
+ if (!srcBufferYCbCrOpt) {
+ ALOGE("%s called on non ycbcr buffer", __FUNCTION__);
+ return -1;
+ }
+ auto& srcBufferYCbCr = *srcBufferYCbCrOpt;
+
+ // The libyuv::I420ToARGB() function is for tri-planar.
+ if (srcBufferYCbCr.chroma_step != 1) {
+ ALOGE("%s called with bad chroma step", __FUNCTION__);
+ return -1;
+ }
+
+ uint8_t* srcY = reinterpret_cast<uint8_t*>(srcBufferYCbCr.y);
+ int strideY = srcBufferYCbCr.ystride;
+ uint8_t* srcU = reinterpret_cast<uint8_t*>(srcBufferYCbCr.cb);
+ int strideU = srcBufferYCbCr.cstride;
+ uint8_t* srcV = reinterpret_cast<uint8_t*>(srcBufferYCbCr.cr);
+ int strideV = srcBufferYCbCr.cstride;
+
+ // Adjust for crop
+ srcY += src.cropY * strideY + src.cropX;
+ srcV += (src.cropY / 2) * strideV + (src.cropX / 2);
+ srcU += (src.cropY / 2) * strideU + (src.cropX / 2);
+ uint8_t* dstBuffer =
+ dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+
+ int width = dst.cropWidth;
+ int height = dst.cropHeight;
+
+ if (vFlip) {
+ height = -height;
+ }
+
+ // YV12 is the same as I420, with the U and V planes swapped
+ return libyuv::I420ToARGB(srcY, strideY, srcV, strideV, srcU, strideU,
+ dstBuffer, dst.strideBytes, width, height);
+}
+
+int DoConversion(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ ConverterFunction func = GetConverterForDrmFormat(src.drmFormat);
+ if (!func) {
+ // GetConverterForDrmFormat should've logged the issue for us.
+ return -1;
+ }
+ return func(src, dst, v_flip);
+}
+
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ ATRACE_CALL();
+
+ // Point to the upper left corner of the crop rectangle
+ uint8_t* srcBuffer =
+ src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+ uint8_t* dstBuffer =
+ dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+ int width = src.cropWidth;
+ int height = src.cropHeight;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ // HAL formats are named based on the order of the pixel components on the
+ // byte stream, while libyuv formats are named based on the order of those
+ // pixel components in an integer written from left to right. So
+ // libyuv::FOURCC_ARGB is equivalent to HAL_PIXEL_FORMAT_BGRA_8888.
+ auto ret = libyuv::ARGBCopy(srcBuffer, src.strideBytes, dstBuffer,
+ dst.strideBytes, width, height);
+ return ret;
+}
+
+int DoRotation(const BufferSpec& src, const BufferSpec& dst,
+ libyuv::RotationMode rotation, bool v_flip) {
+ ATRACE_CALL();
+
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* srcBuffer =
+ src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+ uint8_t* dstBuffer =
+ dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+ int width = src.cropWidth;
+ int height = src.cropHeight;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ return libyuv::ARGBRotate(srcBuffer, src.strideBytes, dstBuffer,
+ dst.strideBytes, width, height, rotation);
+}
+
+int DoScaling(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ ATRACE_CALL();
+
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* srcBuffer =
+ src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+ uint8_t* dstBuffer =
+ dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+ int srcWidth = src.cropWidth;
+ int srcHeight = src.cropHeight;
+ int dstWidth = dst.cropWidth;
+ int dstHeight = dst.cropHeight;
+
+ if (v_flip) {
+ srcHeight = -srcHeight;
+ }
+
+ return libyuv::ARGBScale(srcBuffer, src.strideBytes, srcWidth, srcHeight,
+ dstBuffer, dst.strideBytes, dstWidth, dstHeight,
+ libyuv::kFilterBilinear);
+}
+
+int DoAttenuation(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ ATRACE_CALL();
+
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* srcBuffer =
+ src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+ uint8_t* dstBuffer =
+ dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+ int width = dst.cropWidth;
+ int height = dst.cropHeight;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ return libyuv::ARGBAttenuate(srcBuffer, src.strideBytes, dstBuffer,
+ dst.strideBytes, width, height);
+}
+
+int DoBlending(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ ATRACE_CALL();
+
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* srcBuffer =
+ src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes;
+ uint8_t* dstBuffer =
+ dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes;
+ int width = dst.cropWidth;
+ int height = dst.cropHeight;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ // libyuv's ARGB format is hwcomposer's BGRA format, since blending only cares
+ // for the position of alpha in the pixel and not the position of the colors
+ // this function is perfectly usable.
+ return libyuv::ARGBBlend(srcBuffer, src.strideBytes, dstBuffer,
+ dst.strideBytes, dstBuffer, dst.strideBytes, width,
+ height);
+}
+
+std::optional<BufferSpec> GetBufferSpec(GrallocBuffer& buffer,
+ GrallocBufferView& bufferView,
+ const common::Rect& bufferCrop) {
+ auto bufferFormatOpt = buffer.GetDrmFormat();
+ if (!bufferFormatOpt) {
+ ALOGE("Failed to get gralloc buffer format.");
+ return std::nullopt;
+ }
+ uint32_t bufferFormat = *bufferFormatOpt;
+
+ auto bufferWidthOpt = buffer.GetWidth();
+ if (!bufferWidthOpt) {
+ ALOGE("Failed to get gralloc buffer width.");
+ return std::nullopt;
+ }
+ uint32_t bufferWidth = *bufferWidthOpt;
+
+ auto bufferHeightOpt = buffer.GetHeight();
+ if (!bufferHeightOpt) {
+ ALOGE("Failed to get gralloc buffer height.");
+ return std::nullopt;
+ }
+ uint32_t bufferHeight = *bufferHeightOpt;
+
+ uint8_t* bufferData = nullptr;
+ uint32_t bufferStrideBytes = 0;
+ std::optional<android_ycbcr> bufferYCbCrData;
+
+ if (bufferFormat == DRM_FORMAT_NV12 || bufferFormat == DRM_FORMAT_NV21 ||
+ bufferFormat == DRM_FORMAT_YVU420) {
+ bufferYCbCrData = bufferView.GetYCbCr();
+ if (!bufferYCbCrData) {
+ ALOGE("%s failed to get raw ycbcr from view.", __FUNCTION__);
+ return std::nullopt;
+ }
+ } else {
+ auto bufferDataOpt = bufferView.Get();
+ if (!bufferDataOpt) {
+ ALOGE("%s failed to lock gralloc buffer.", __FUNCTION__);
+ return std::nullopt;
+ }
+ bufferData = reinterpret_cast<uint8_t*>(*bufferDataOpt);
+
+ auto bufferStrideBytesOpt = buffer.GetMonoPlanarStrideBytes();
+ if (!bufferStrideBytesOpt) {
+ ALOGE("%s failed to get plane stride.", __FUNCTION__);
+ return std::nullopt;
+ }
+ bufferStrideBytes = *bufferStrideBytesOpt;
+ }
+
+ return BufferSpec(bufferData, bufferYCbCrData, bufferWidth, bufferHeight,
+ bufferCrop.left, bufferCrop.top,
+ bufferCrop.right - bufferCrop.left,
+ bufferCrop.bottom - bufferCrop.top, bufferFormat,
+ bufferStrideBytes, GetDrmFormatBytesPerPixel(bufferFormat));
+}
+
+} // namespace
+
+HWC3::Error GuestFrameComposer::init() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ HWC3::Error error = mDrmPresenter.init();
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to initialize DrmPresenter", __FUNCTION__);
+ return error;
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::registerOnHotplugCallback(
+ const HotplugCallback& cb) {
+ return mDrmPresenter.registerOnHotplugCallback(cb);
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::unregisterOnHotplugCallback() {
+ return mDrmPresenter.unregisterOnHotplugCallback();
+}
+
+HWC3::Error GuestFrameComposer::onDisplayCreate(Display* display) {
+ int64_t displayId = display->getId();
+ int32_t displayConfigId;
+ int32_t displayWidth;
+ int32_t displayHeight;
+
+ HWC3::Error error = display->getActiveConfig(&displayConfigId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " has no active config", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::WIDTH,
+ &displayWidth);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to get width", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ error = display->getDisplayAttribute(
+ displayConfigId, DisplayAttribute::HEIGHT, &displayHeight);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ auto it = mDisplayInfos.find(displayId);
+ if (it != mDisplayInfos.end()) {
+ ALOGE("%s: display:%" PRIu64 " already created?", __FUNCTION__, displayId);
+ }
+
+ DisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+ uint32_t bufferStride;
+ buffer_handle_t bufferHandle;
+
+ auto status = ::android::GraphicBufferAllocator::get().allocate(
+ displayWidth, //
+ displayHeight, //
+ ::android::PIXEL_FORMAT_RGBA_8888, //
+ /*layerCount=*/1, //
+ ::android::GraphicBuffer::USAGE_HW_COMPOSER |
+ ::android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+ ::android::GraphicBuffer::USAGE_SW_WRITE_OFTEN, //
+ &bufferHandle, //
+ &bufferStride, //
+ "RanchuHwc");
+ if (status != ::android::OK) {
+ ALOGE("%s: failed to allocate composition buffer for display:%" PRIu64,
+ __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ displayInfo.compositionResultBuffer = bufferHandle;
+
+ auto [drmBufferCreateError, drmBuffer] = mDrmPresenter.create(bufferHandle);
+ if (drmBufferCreateError != HWC3::Error::None) {
+ ALOGE("%s: failed to create drm buffer for display:%" PRIu64, __FUNCTION__,
+ displayId);
+ return drmBufferCreateError;
+ }
+ displayInfo.compositionResultDrmBuffer = std::move(drmBuffer);
+
+ if (displayId == 0) {
+ auto [flushError, flushSyncFd] = mDrmPresenter.flushToDisplay(
+ displayId, *displayInfo.compositionResultDrmBuffer, -1);
+ if (flushError != HWC3::Error::None) {
+ ALOGW(
+ "%s: Initial display flush failed. HWComposer assuming that we are "
+ "running in QEMU without a display and disabling presenting.",
+ __FUNCTION__);
+ mPresentDisabled = true;
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::onDisplayDestroy(Display* display) {
+ auto displayId = display->getId();
+
+ auto it = mDisplayInfos.find(displayId);
+ if (it == mDisplayInfos.end()) {
+ ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+ displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ DisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+ ::android::GraphicBufferAllocator::get().free(
+ displayInfo.compositionResultBuffer);
+
+ mDisplayInfos.erase(it);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::onDisplayClientTargetSet(Display*) {
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::onActiveConfigChange(Display* /*display*/) {
+ return HWC3::Error::None;
+};
+
+HWC3::Error GuestFrameComposer::getDisplayConfigsFromDeviceConfig(
+ std::vector<GuestFrameComposer::DisplayConfig>* configs) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ const auto deviceConfig = cuttlefish::GetDeviceConfig();
+ for (const auto& deviceDisplayConfig : deviceConfig.display_config()) {
+ DisplayConfig displayConfig = {
+ .width = deviceDisplayConfig.width(),
+ .height = deviceDisplayConfig.height(),
+ .dpiX = deviceDisplayConfig.dpi(),
+ .dpiY = deviceDisplayConfig.dpi(),
+ .refreshRateHz = deviceDisplayConfig.refresh_rate_hz(),
+ };
+
+ configs->push_back(displayConfig);
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::getDisplayConfigsFromSystemProp(
+ std::vector<GuestFrameComposer::DisplayConfig>* configs) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ static constexpr const char kExternalDisplayProp[] =
+ "hwservicemanager.external.displays";
+
+ const auto propString =
+ ::android::base::GetProperty(kExternalDisplayProp, "");
+ DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str());
+
+ if (propString.empty()) {
+ return HWC3::Error::None;
+ }
+
+ const std::vector<std::string> propStringParts =
+ ::android::base::Split(propString, ",");
+ if (propStringParts.size() % 5 != 0) {
+ ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+ kExternalDisplayProp, propString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+
+ std::vector<int> propIntParts;
+ for (const std::string& propStringPart : propStringParts) {
+ int propIntPart;
+ if (!::android::base::ParseInt(propStringPart, &propIntPart)) {
+ ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
+ kExternalDisplayProp, propString.c_str());
+ return HWC3::Error::BadParameter;
+ }
+ propIntParts.push_back(propIntPart);
+ }
+
+ while (!propIntParts.empty()) {
+ DisplayConfig display_config = {
+ .width = propIntParts[1],
+ .height = propIntParts[2],
+ .dpiX = propIntParts[3],
+ .dpiY = propIntParts[3],
+ .refreshRateHz = 160,
+ };
+
+ configs->push_back(display_config);
+
+ propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::validateDisplay(Display* display,
+ DisplayChanges* outChanges) {
+ const auto displayId = display->getId();
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ const std::vector<Layer*>& layers = display->getOrderedLayers();
+
+ bool fallbackToClientComposition = false;
+ for (Layer* layer : layers) {
+ const auto layerId = layer->getId();
+ const auto layerCompositionType = layer->getCompositionType();
+ const auto layerCompositionTypeString = toString(layerCompositionType);
+
+ if (layerCompositionType == Composition::INVALID) {
+ ALOGE("%s display:%" PRIu64 " layer:%" PRIu64 " has Invalid composition",
+ __FUNCTION__, displayId, layerId);
+ continue;
+ }
+
+ if (layerCompositionType == Composition::CLIENT ||
+ layerCompositionType == Composition::CURSOR ||
+ layerCompositionType == Composition::SIDEBAND ||
+ layerCompositionType == Composition::SOLID_COLOR) {
+ DEBUG_LOG("%s: display:%" PRIu64 " layer:%" PRIu64
+ " has composition type %s, falling back to client composition",
+ __FUNCTION__, displayId, layerId,
+ layerCompositionTypeString.c_str());
+ fallbackToClientComposition = true;
+ break;
+ }
+
+ if (layerCompositionType == Composition::DISPLAY_DECORATION) {
+ return HWC3::Error::Unsupported;
+ }
+
+ if (!canComposeLayer(layer)) {
+ DEBUG_LOG(
+ "%s: display:%" PRIu64 " layer:%" PRIu64
+ " composition not supported, falling back to client composition",
+ __FUNCTION__, displayId, layerId);
+ fallbackToClientComposition = true;
+ break;
+ }
+ }
+
+ if (fallbackToClientComposition) {
+ for (Layer* layer : layers) {
+ const auto layerId = layer->getId();
+ const auto layerCompositionType = layer->getCompositionType();
+
+ if (layerCompositionType == Composition::INVALID) {
+ continue;
+ }
+
+ if (layerCompositionType != Composition::CLIENT) {
+ DEBUG_LOG("%s display:%" PRIu64 " layer:%" PRIu64
+ "composition updated to Client",
+ __FUNCTION__, displayId, layerId);
+
+ outChanges->addLayerCompositionChange(displayId, layerId,
+ Composition::CLIENT);
+ }
+ }
+ }
+
+ // We can not draw below a Client (SurfaceFlinger) composed layer. Change all
+ // layers below a Client composed layer to also be Client composed.
+ if (layers.size() > 1) {
+ for (std::size_t layerIndex = layers.size() - 1; layerIndex > 0;
+ layerIndex--) {
+ auto layer = layers[layerIndex];
+ auto layerCompositionType = layer->getCompositionType();
+
+ if (layerCompositionType == Composition::CLIENT) {
+ for (std::size_t lowerLayerIndex = 0; lowerLayerIndex < layerIndex;
+ lowerLayerIndex++) {
+ auto lowerLayer = layers[lowerLayerIndex];
+ auto lowerLayerId = lowerLayer->getId();
+ auto lowerLayerCompositionType = lowerLayer->getCompositionType();
+
+ if (lowerLayerCompositionType != Composition::CLIENT) {
+ DEBUG_LOG("%s: display:%" PRIu64 " changing layer:%" PRIu64
+ " to Client because"
+ "hwcomposer can not draw below the Client composed "
+ "layer:%" PRIu64,
+ __FUNCTION__, displayId, lowerLayerId, layer->getId());
+
+ outChanges->addLayerCompositionChange(displayId, lowerLayerId,
+ Composition::CLIENT);
+ }
+ }
+ }
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error GuestFrameComposer::presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t,
+ ::android::base::unique_fd>* /*outLayerFences*/) {
+ const auto displayId = display->getId();
+ DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId);
+
+ if (mPresentDisabled) {
+ return HWC3::Error::None;
+ }
+
+ auto it = mDisplayInfos.find(displayId);
+ if (it == mDisplayInfos.end()) {
+ ALOGE("%s: display:%" PRIu64 " not found", __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ DisplayInfo& displayInfo = it->second;
+
+ if (displayInfo.compositionResultBuffer == nullptr) {
+ ALOGE("%s: display:%" PRIu64 " missing composition result buffer",
+ __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ if (displayInfo.compositionResultDrmBuffer == nullptr) {
+ ALOGE("%s: display:%" PRIu64 " missing composition result drm buffer",
+ __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ std::optional<GrallocBuffer> compositionResultBufferOpt =
+ mGralloc.Import(displayInfo.compositionResultBuffer);
+ if (!compositionResultBufferOpt) {
+ ALOGE("%s: display:%" PRIu64 " failed to import buffer", __FUNCTION__,
+ displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ std::optional<uint32_t> compositionResultBufferWidthOpt =
+ compositionResultBufferOpt->GetWidth();
+ if (!compositionResultBufferWidthOpt) {
+ ALOGE("%s: display:%" PRIu64 " failed to query buffer width", __FUNCTION__,
+ displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ std::optional<uint32_t> compositionResultBufferHeightOpt =
+ compositionResultBufferOpt->GetHeight();
+ if (!compositionResultBufferHeightOpt) {
+ ALOGE("%s: display:%" PRIu64 " failed to query buffer height", __FUNCTION__,
+ displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ std::optional<uint32_t> compositionResultBufferStrideOpt =
+ compositionResultBufferOpt->GetMonoPlanarStrideBytes();
+ if (!compositionResultBufferStrideOpt) {
+ ALOGE("%s: display:%" PRIu64 " failed to query buffer stride", __FUNCTION__,
+ displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ std::optional<GrallocBufferView> compositionResultBufferViewOpt =
+ compositionResultBufferOpt->Lock();
+ if (!compositionResultBufferViewOpt) {
+ ALOGE("%s: display:%" PRIu64 " failed to get buffer view", __FUNCTION__,
+ displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ const std::optional<void*> compositionResultBufferDataOpt =
+ compositionResultBufferViewOpt->Get();
+ if (!compositionResultBufferDataOpt) {
+ ALOGE("%s: display:%" PRIu64 " failed to get buffer data", __FUNCTION__,
+ displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ uint32_t compositionResultBufferWidth = *compositionResultBufferWidthOpt;
+ uint32_t compositionResultBufferHeight = *compositionResultBufferHeightOpt;
+ uint32_t compositionResultBufferStride = *compositionResultBufferStrideOpt;
+ uint8_t* compositionResultBufferData =
+ reinterpret_cast<uint8_t*>(*compositionResultBufferDataOpt);
+
+ const std::vector<Layer*>& layers = display->getOrderedLayers();
+
+ const bool noOpComposition = layers.empty();
+ const bool allLayersClientComposed =
+ std::all_of(layers.begin(), //
+ layers.end(), //
+ [](const Layer* layer) {
+ return layer->getCompositionType() == Composition::CLIENT;
+ });
+
+ if (noOpComposition) {
+ ALOGW("%s: display:%" PRIu64 " empty composition", __FUNCTION__, displayId);
+ } else if (allLayersClientComposed) {
+ auto clientTargetBufferOpt =
+ mGralloc.Import(display->waitAndGetClientTargetBuffer());
+ if (!clientTargetBufferOpt) {
+ ALOGE("%s: failed to import client target buffer.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ GrallocBuffer& clientTargetBuffer = *clientTargetBufferOpt;
+
+ auto clientTargetBufferViewOpt = clientTargetBuffer.Lock();
+ if (!clientTargetBufferViewOpt) {
+ ALOGE("%s: failed to lock client target buffer.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ GrallocBufferView& clientTargetBufferView = *clientTargetBufferViewOpt;
+
+ auto clientTargetPlaneLayoutsOpt = clientTargetBuffer.GetPlaneLayouts();
+ if (!clientTargetPlaneLayoutsOpt) {
+ ALOGE("Failed to get client target buffer plane layouts.");
+ return HWC3::Error::NoResources;
+ }
+ auto& clientTargetPlaneLayouts = *clientTargetPlaneLayoutsOpt;
+
+ if (clientTargetPlaneLayouts.size() != 1) {
+ ALOGE("Unexpected number of plane layouts for client target buffer.");
+ return HWC3::Error::NoResources;
+ }
+
+ std::size_t clientTargetPlaneSize =
+ clientTargetPlaneLayouts[0].totalSizeInBytes;
+
+ auto clientTargetDataOpt = clientTargetBufferView.Get();
+ if (!clientTargetDataOpt) {
+ ALOGE("%s failed to lock gralloc buffer.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ auto* clientTargetData = reinterpret_cast<uint8_t*>(*clientTargetDataOpt);
+
+ std::memcpy(compositionResultBufferData, clientTargetData,
+ clientTargetPlaneSize);
+ } else {
+ for (Layer* layer : layers) {
+ const auto layerId = layer->getId();
+ const auto layerCompositionType = layer->getCompositionType();
+ if (layerCompositionType != Composition::DEVICE) {
+ continue;
+ }
+
+ HWC3::Error error = composeLayerInto(layer, //
+ compositionResultBufferData, //
+ compositionResultBufferWidth, //
+ compositionResultBufferHeight, //
+ compositionResultBufferStride, //
+ 4);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to compose layer:%" PRIu64,
+ __FUNCTION__, displayId, layerId);
+ return error;
+ }
+ }
+ }
+
+ if (display->hasColorTransform()) {
+ HWC3::Error error =
+ applyColorTransformToRGBA(display->getColorTransform(), //
+ compositionResultBufferData, //
+ compositionResultBufferWidth, //
+ compositionResultBufferHeight, //
+ compositionResultBufferStride);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to apply color transform",
+ __FUNCTION__, displayId);
+ return error;
+ }
+ }
+
+ DEBUG_LOG("%s display:%" PRIu64 " flushing drm buffer", __FUNCTION__,
+ displayId);
+
+ auto [error, fence] = mDrmPresenter.flushToDisplay(
+ displayId, *displayInfo.compositionResultDrmBuffer, -1);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64,
+ __FUNCTION__, displayId);
+ }
+
+ *outDisplayFence = std::move(fence);
+ return error;
+}
+
+bool GuestFrameComposer::canComposeLayer(Layer* layer) {
+ buffer_handle_t bufferHandle = layer->getBuffer().getBuffer();
+ if (bufferHandle == nullptr) {
+ ALOGW("%s received a layer with a null handle", __FUNCTION__);
+ return false;
+ }
+
+ auto bufferOpt = mGralloc.Import(bufferHandle);
+ if (!bufferOpt) {
+ ALOGE("Failed to import layer buffer.");
+ return false;
+ }
+ GrallocBuffer& buffer = *bufferOpt;
+
+ auto bufferFormatOpt = buffer.GetDrmFormat();
+ if (!bufferFormatOpt) {
+ ALOGE("Failed to get layer buffer format.");
+ return false;
+ }
+ uint32_t bufferFormat = *bufferFormatOpt;
+
+ if (!IsDrmFormatSupported(bufferFormat)) {
+ return false;
+ }
+
+ return true;
+}
+
+HWC3::Error GuestFrameComposer::composeLayerInto(
+ Layer* srcLayer, //
+ std::uint8_t* dstBuffer, //
+ std::uint32_t dstBufferWidth, //
+ std::uint32_t dstBufferHeight, //
+ std::uint32_t dstBufferStrideBytes, //
+ std::uint32_t dstBufferBytesPerPixel) {
+ ATRACE_CALL();
+
+ libyuv::RotationMode rotation =
+ GetRotationFromTransform(srcLayer->getTransform());
+
+ auto srcBufferOpt = mGralloc.Import(srcLayer->waitAndGetBuffer());
+ if (!srcBufferOpt) {
+ ALOGE("%s: failed to import layer buffer.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ GrallocBuffer& srcBuffer = *srcBufferOpt;
+
+ auto srcBufferViewOpt = srcBuffer.Lock();
+ if (!srcBufferViewOpt) {
+ ALOGE("%s: failed to lock import layer buffer.", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ GrallocBufferView& srcBufferView = *srcBufferViewOpt;
+
+ common::Rect srcLayerCrop = srcLayer->getSourceCropInt();
+ common::Rect srcLayerDisplayFrame = srcLayer->getDisplayFrame();
+
+ auto srcLayerSpecOpt = GetBufferSpec(srcBuffer, srcBufferView, srcLayerCrop);
+ if (!srcLayerSpecOpt) {
+ return HWC3::Error::NoResources;
+ }
+ BufferSpec srcLayerSpec = *srcLayerSpecOpt;
+
+ // TODO(jemoreira): Remove the hardcoded fomat.
+ bool needsConversion = srcLayerSpec.drmFormat != DRM_FORMAT_XBGR8888 &&
+ srcLayerSpec.drmFormat != DRM_FORMAT_ABGR8888;
+ bool needsScaling = LayerNeedsScaling(*srcLayer);
+ bool needsRotation = rotation != libyuv::kRotate0;
+ bool needsTranspose = needsRotation && rotation != libyuv::kRotate180;
+ bool needsVFlip = GetVFlipFromTransform(srcLayer->getTransform());
+ bool needsAttenuation = LayerNeedsAttenuation(*srcLayer);
+ bool needsBlending = LayerNeedsBlending(*srcLayer);
+ bool needsCopy = !(needsConversion || needsScaling || needsRotation ||
+ needsVFlip || needsAttenuation || needsBlending);
+
+ BufferSpec dstLayerSpec(
+ dstBuffer,
+ /*buffer_ycbcr=*/std::nullopt, dstBufferWidth, dstBufferHeight,
+ srcLayerDisplayFrame.left, srcLayerDisplayFrame.top,
+ srcLayerDisplayFrame.right - srcLayerDisplayFrame.left,
+ srcLayerDisplayFrame.bottom - srcLayerDisplayFrame.top,
+ DRM_FORMAT_XBGR8888, dstBufferStrideBytes, dstBufferBytesPerPixel);
+
+ // Add the destination layer to the bottom of the buffer stack
+ std::vector<BufferSpec> dstBufferStack(1, dstLayerSpec);
+
+ // If more than operation is to be performed, a temporary buffer is needed for
+ // each additional operation
+
+ // N operations need N destination buffers, the destination layer (the
+ // framebuffer) is one of them, so only N-1 temporary buffers are needed.
+ // Vertical flip is not taken into account because it can be done together
+ // with any other operation.
+ int neededScratchBuffers = (needsConversion ? 1 : 0) +
+ (needsScaling ? 1 : 0) + (needsRotation ? 1 : 0) +
+ (needsAttenuation ? 1 : 0) +
+ (needsBlending ? 1 : 0) + (needsCopy ? 1 : 0) - 1;
+
+ int mScratchBufferWidth =
+ srcLayerDisplayFrame.right - srcLayerDisplayFrame.left;
+ int mScratchBufferHeight =
+ srcLayerDisplayFrame.bottom - srcLayerDisplayFrame.top;
+ int mScratchBufferStrideBytes =
+ AlignToPower2(mScratchBufferWidth * dstBufferBytesPerPixel, 4);
+ int mScratchBufferSizeBytes =
+ mScratchBufferHeight * mScratchBufferStrideBytes;
+
+ for (int i = 0; i < neededScratchBuffers; i++) {
+ BufferSpec mScratchBufferspec(
+ getRotatingScratchBuffer(mScratchBufferSizeBytes, i),
+ mScratchBufferWidth, mScratchBufferHeight, mScratchBufferStrideBytes);
+ dstBufferStack.push_back(mScratchBufferspec);
+ }
+
+ // Conversion and scaling should always be the first operations, so that every
+ // other operation works on equally sized frames (guaranteed to fit in the
+ // scratch buffers).
+
+ // TODO(jemoreira): We are converting to ARGB as the first step under the
+ // assumption that scaling ARGB is faster than scaling I420 (the most common).
+ // This should be confirmed with testing.
+ if (needsConversion) {
+ BufferSpec& dstBufferSpec = dstBufferStack.back();
+ if (needsScaling || needsTranspose) {
+ // If a rotation or a scaling operation are needed the dimensions at the
+ // top of the buffer stack are wrong (wrong sizes for scaling, swapped
+ // width and height for 90 and 270 rotations).
+ // Make width and height match the crop sizes on the source
+ int srcWidth = srcLayerSpec.cropWidth;
+ int srcHeight = srcLayerSpec.cropHeight;
+ int dst_stride_bytes =
+ AlignToPower2(srcWidth * dstBufferBytesPerPixel, 4);
+ size_t needed_size = dst_stride_bytes * srcHeight;
+ dstBufferSpec.width = srcWidth;
+ dstBufferSpec.height = srcHeight;
+ // Adjust the stride accordingly
+ dstBufferSpec.strideBytes = dst_stride_bytes;
+ // Crop sizes also need to be adjusted
+ dstBufferSpec.cropWidth = srcWidth;
+ dstBufferSpec.cropHeight = srcHeight;
+ // cropX and y are fine at 0, format is already set to match destination
+
+ // In case of a scale, the source frame may be bigger than the default tmp
+ // buffer size
+ dstBufferSpec.buffer = getSpecialScratchBuffer(needed_size);
+ }
+
+ int retval = DoConversion(srcLayerSpec, dstBufferSpec, needsVFlip);
+ if (retval) {
+ ALOGE("Got error code %d from DoConversion function", retval);
+ }
+ needsVFlip = false;
+ srcLayerSpec = dstBufferSpec;
+ dstBufferStack.pop_back();
+ }
+
+ if (needsScaling) {
+ BufferSpec& dstBufferSpec = dstBufferStack.back();
+ if (needsTranspose) {
+ // If a rotation is needed, the temporary buffer has the correct size but
+ // needs to be transposed and have its stride updated accordingly. The
+ // crop sizes also needs to be transposed, but not the x and y since they
+ // are both zero in a temporary buffer (and it is a temporary buffer
+ // because a rotation will be performed next).
+ std::swap(dstBufferSpec.width, dstBufferSpec.height);
+ std::swap(dstBufferSpec.cropWidth, dstBufferSpec.cropHeight);
+ // TODO (jemoreira): Aligment (To align here may cause the needed size to
+ // be bigger than the buffer, so care should be taken)
+ dstBufferSpec.strideBytes = dstBufferSpec.width * dstBufferBytesPerPixel;
+ }
+ int retval = DoScaling(srcLayerSpec, dstBufferSpec, needsVFlip);
+ needsVFlip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoScaling function", retval);
+ }
+ srcLayerSpec = dstBufferSpec;
+ dstBufferStack.pop_back();
+ }
+
+ if (needsRotation) {
+ int retval =
+ DoRotation(srcLayerSpec, dstBufferStack.back(), rotation, needsVFlip);
+ needsVFlip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoTransform function", retval);
+ }
+ srcLayerSpec = dstBufferStack.back();
+ dstBufferStack.pop_back();
+ }
+
+ if (needsAttenuation) {
+ int retval = DoAttenuation(srcLayerSpec, dstBufferStack.back(), needsVFlip);
+ needsVFlip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ srcLayerSpec = dstBufferStack.back();
+ dstBufferStack.pop_back();
+ }
+
+ if (needsCopy) {
+ int retval = DoCopy(srcLayerSpec, dstBufferStack.back(), needsVFlip);
+ needsVFlip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ srcLayerSpec = dstBufferStack.back();
+ dstBufferStack.pop_back();
+ }
+
+ // Blending (if needed) should always be the last operation, so that it reads
+ // and writes in the destination layer and not some temporary buffer.
+ if (needsBlending) {
+ int retval = DoBlending(srcLayerSpec, dstBufferStack.back(), needsVFlip);
+ needsVFlip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ // Don't need to assign destination to source in the last one
+ dstBufferStack.pop_back();
+ }
+
+ return HWC3::Error::None;
+}
+
+namespace {
+
+// Returns a color matrix that can be used with libyuv by converting values
+// in -1 to 1 into -64 to 64 and transposing.
+std::array<std::int8_t, 16> ToLibyuvColorMatrix(
+ const std::array<float, 16>& in) {
+ std::array<std::int8_t, 16> out;
+
+ for (int r = 0; r < 4; r++) {
+ for (int c = 0; c < 4; c++) {
+ int indexIn = (4 * r) + c;
+ int indexOut = (4 * c) + r;
+
+ out[indexOut] = std::max(
+ -128, std::min(127, static_cast<int>(in[indexIn] * 64.0f + 0.5f)));
+ }
+ }
+
+ return out;
+}
+
+} // namespace
+
+HWC3::Error GuestFrameComposer::applyColorTransformToRGBA(
+ const std::array<float, 16>& transfromMatrix, //
+ std::uint8_t* buffer, //
+ std::uint32_t bufferWidth, //
+ std::uint32_t bufferHeight, //
+ std::uint32_t bufferStrideBytes) {
+ ATRACE_CALL();
+
+ const auto transformMatrixLibyuv = ToLibyuvColorMatrix(transfromMatrix);
+ libyuv::ARGBColorMatrix(buffer, bufferStrideBytes, // in buffer params
+ buffer, bufferStrideBytes, // out buffer params
+ transformMatrixLibyuv.data(), //
+ bufferWidth, //
+ bufferHeight);
+
+ return HWC3::Error::None;
+}
+
+uint8_t* GuestFrameComposer::getRotatingScratchBuffer(std::size_t neededSize,
+ std::uint32_t order) {
+ static constexpr const int kNumScratchBufferPieces = 2;
+
+ std::size_t totalNeededSize = neededSize * kNumScratchBufferPieces;
+ if (mScratchBuffer.size() < totalNeededSize) {
+ mScratchBuffer.resize(totalNeededSize);
+ }
+
+ std::size_t bufferIndex = order % kNumScratchBufferPieces;
+ std::size_t bufferOffset = bufferIndex * neededSize;
+ return &mScratchBuffer[bufferOffset];
+}
+
+uint8_t* GuestFrameComposer::getSpecialScratchBuffer(size_t neededSize) {
+ if (mSpecialScratchBuffer.size() < neededSize) {
+ mSpecialScratchBuffer.resize(neededSize);
+ }
+
+ return &mSpecialScratchBuffer[0];
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/GuestFrameComposer.h b/system/hwc3/GuestFrameComposer.h
new file mode 100644
index 0000000..af46b3d
--- /dev/null
+++ b/system/hwc3/GuestFrameComposer.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_GUESTFRAMECOMPOSER_H
+#define ANDROID_HWC_GUESTFRAMECOMPOSER_H
+
+#include "Common.h"
+#include "Display.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "Gralloc.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class GuestFrameComposer : public FrameComposer {
+ public:
+ GuestFrameComposer() = default;
+
+ GuestFrameComposer(const GuestFrameComposer&) = delete;
+ GuestFrameComposer& operator=(const GuestFrameComposer&) = delete;
+
+ GuestFrameComposer(GuestFrameComposer&&) = delete;
+ GuestFrameComposer& operator=(GuestFrameComposer&&) = delete;
+
+ HWC3::Error init() override;
+
+ HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+ HWC3::Error unregisterOnHotplugCallback() override;
+
+ HWC3::Error onDisplayCreate(Display*) override;
+
+ HWC3::Error onDisplayDestroy(Display*) override;
+
+ HWC3::Error onDisplayClientTargetSet(Display*) override;
+
+ // Determines if this composer can compose the given layers on the given
+ // display and requests changes for layers that can't not be composed.
+ HWC3::Error validateDisplay(Display* display,
+ DisplayChanges* outChanges) override;
+
+ // Performs the actual composition of layers and presents the composed result
+ // to the display.
+ HWC3::Error presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+ override;
+
+ HWC3::Error onActiveConfigChange(Display* /*display*/) override;
+
+ private:
+ struct DisplayConfig {
+ int width;
+ int height;
+ int dpiX;
+ int dpiY;
+ int refreshRateHz;
+ };
+
+ HWC3::Error getDisplayConfigsFromDeviceConfig(
+ std::vector<DisplayConfig>* configs);
+
+ HWC3::Error getDisplayConfigsFromSystemProp(
+ std::vector<DisplayConfig>* configs);
+
+ // Returns true if the given layer's buffer has supported format.
+ bool canComposeLayer(Layer* layer);
+
+ // Composes the given layer into the given destination buffer.
+ HWC3::Error composeLayerInto(Layer* layer, std::uint8_t* dstBuffer,
+ std::uint32_t dstBufferWidth,
+ std::uint32_t dstBufferHeight,
+ std::uint32_t dstBufferStrideBytes,
+ std::uint32_t dstBufferBytesPerPixel);
+
+ struct DisplayInfo {
+ // Additional per display buffer for the composition result.
+ buffer_handle_t compositionResultBuffer = nullptr;
+
+ std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+ };
+
+ std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
+
+ Gralloc mGralloc;
+
+ DrmPresenter mDrmPresenter;
+
+ // Cuttlefish on QEMU does not have a display. Disable presenting to avoid
+ // spamming logcat with DRM commit failures.
+ bool mPresentDisabled = false;
+
+ uint8_t* getRotatingScratchBuffer(std::size_t neededSize,
+ std::uint32_t order);
+ uint8_t* getSpecialScratchBuffer(std::size_t neededSize);
+
+ HWC3::Error applyColorTransformToRGBA(
+ const std::array<float, 16>& colorTransform, //
+ std::uint8_t* buffer, //
+ std::uint32_t bufferWidth, //
+ std::uint32_t bufferHeight, //
+ std::uint32_t bufferStrideBytes);
+
+ std::vector<uint8_t> mScratchBuffer;
+ std::vector<uint8_t> mSpecialScratchBuffer;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/HostFrameComposer.cpp b/system/hwc3/HostFrameComposer.cpp
new file mode 100644
index 0000000..7b090c2
--- /dev/null
+++ b/system/hwc3/HostFrameComposer.cpp
@@ -0,0 +1,800 @@
+/*
+ * Copyright 2022 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 "HostFrameComposer.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <hardware/hwcomposer2.h>
+#include <poll.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include <optional>
+#include <tuple>
+
+#include "../egl/goldfish_sync.h"
+#include "Display.h"
+#include "HostUtils.h"
+#include "virtgpu_drm.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+hwc_rect AsHwcRect(const common::Rect& rect) {
+ hwc_rect out;
+ out.left = rect.left;
+ out.top = rect.top;
+ out.right = rect.right;
+ out.bottom = rect.bottom;
+ return out;
+}
+
+hwc_frect AsHwcFrect(const common::FRect& rect) {
+ hwc_frect out;
+ out.left = rect.left;
+ out.top = rect.top;
+ out.right = rect.right;
+ out.bottom = rect.bottom;
+ return out;
+}
+
+hwc_color AsHwcColor(const Color& color) {
+ hwc_color out;
+ out.r = color.r;
+ out.g = color.g;
+ out.b = color.b;
+ out.a = color.a;
+ return out;
+}
+
+hwc_transform_t AsHwcTransform(const common::Transform& transform) {
+ switch (transform) {
+ case common::Transform::NONE:
+ return static_cast<hwc_transform_t>(0);
+ case common::Transform::FLIP_H:
+ return HWC_TRANSFORM_FLIP_H;
+ case common::Transform::FLIP_V:
+ return HWC_TRANSFORM_FLIP_V;
+ case common::Transform::ROT_90:
+ return HWC_TRANSFORM_ROT_90;
+ case common::Transform::ROT_180:
+ return HWC_TRANSFORM_ROT_180;
+ case common::Transform::ROT_270:
+ return HWC_TRANSFORM_ROT_270;
+ }
+}
+
+static bool isMinigbmFromProperty() {
+ static constexpr const auto kGrallocProp = "ro.hardware.gralloc";
+
+ const auto grallocProp = ::android::base::GetProperty(kGrallocProp, "");
+ DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, grallocProp.c_str());
+
+ if (grallocProp == "minigbm") {
+ DEBUG_LOG("%s: Using minigbm, in minigbm mode.\n", __FUNCTION__);
+ return true;
+ } else {
+ DEBUG_LOG("%s: Is not using minigbm, in goldfish mode.\n", __FUNCTION__);
+ return false;
+ }
+}
+
+static bool useAngleFromProperty() {
+ static constexpr const auto kEglProp = "ro.hardware.egl";
+
+ const auto eglProp = ::android::base::GetProperty(kEglProp, "");
+ DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, eglProp.c_str());
+
+ if (eglProp == "angle") {
+ DEBUG_LOG("%s: Using ANGLE.\n", __FUNCTION__);
+ return true;
+ } else {
+ DEBUG_LOG("%s: Not using ANGLE.\n", __FUNCTION__);
+ return false;
+ }
+}
+
+typedef struct compose_layer {
+ uint32_t cbHandle;
+ hwc2_composition_t composeMode;
+ hwc_rect_t displayFrame;
+ hwc_frect_t crop;
+ int32_t blendMode;
+ float alpha;
+ hwc_color_t color;
+ hwc_transform_t transform;
+} ComposeLayer;
+
+typedef struct compose_device {
+ uint32_t version;
+ uint32_t targetHandle;
+ uint32_t numLayers;
+ struct compose_layer layer[0];
+} ComposeDevice;
+
+typedef struct compose_device_v2 {
+ uint32_t version;
+ uint32_t displayId;
+ uint32_t targetHandle;
+ uint32_t numLayers;
+ struct compose_layer layer[0];
+} ComposeDevice_v2;
+
+class ComposeMsg {
+ public:
+ ComposeMsg(uint32_t layerCnt = 0)
+ : mData(sizeof(ComposeDevice) + layerCnt * sizeof(ComposeLayer)) {
+ mComposeDevice = reinterpret_cast<ComposeDevice*>(mData.data());
+ mLayerCnt = layerCnt;
+ }
+
+ ComposeDevice* get() { return mComposeDevice; }
+
+ uint32_t getLayerCnt() { return mLayerCnt; }
+
+ private:
+ std::vector<uint8_t> mData;
+ uint32_t mLayerCnt;
+ ComposeDevice* mComposeDevice;
+};
+
+class ComposeMsg_v2 {
+ public:
+ ComposeMsg_v2(uint32_t layerCnt = 0)
+ : mData(sizeof(ComposeDevice_v2) + layerCnt * sizeof(ComposeLayer)) {
+ mComposeDevice = reinterpret_cast<ComposeDevice_v2*>(mData.data());
+ mLayerCnt = layerCnt;
+ }
+
+ ComposeDevice_v2* get() { return mComposeDevice; }
+
+ uint32_t getLayerCnt() { return mLayerCnt; }
+
+ private:
+ std::vector<uint8_t> mData;
+ uint32_t mLayerCnt;
+ ComposeDevice_v2* mComposeDevice;
+};
+
+const native_handle_t* AllocateDisplayColorBuffer(int width, int height) {
+ const uint32_t layerCount = 1;
+ const uint64_t graphicBufferId = 0; // not used
+ buffer_handle_t handle;
+ uint32_t stride;
+
+ if (::android::GraphicBufferAllocator::get().allocate(
+ width, height, ::android::PIXEL_FORMAT_RGBA_8888, layerCount,
+ ::android::GraphicBuffer::USAGE_HW_COMPOSER |
+ ::android::GraphicBuffer::USAGE_HW_RENDER,
+ &handle, &stride, graphicBufferId, "EmuHWC2") == ::android::OK) {
+ return static_cast<const native_handle_t*>(handle);
+ } else {
+ return nullptr;
+ }
+}
+
+void FreeDisplayColorBuffer(const native_handle_t* h) {
+ ::android::GraphicBufferAllocator::get().free(h);
+}
+
+} // namespace
+
+HWC3::Error HostFrameComposer::init() {
+ mIsMinigbm = isMinigbmFromProperty();
+ mUseAngle = useAngleFromProperty();
+
+ if (mIsMinigbm) {
+ mDrmPresenter.emplace();
+
+ HWC3::Error error = mDrmPresenter->init();
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: failed to initialize DrmPresenter", __FUNCTION__);
+ return error;
+ }
+ } else {
+ mSyncDeviceFd = goldfish_sync_open();
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::registerOnHotplugCallback(
+ const HotplugCallback& cb) {
+ if (mDrmPresenter) {
+ mDrmPresenter->registerOnHotplugCallback(cb);
+ }
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::unregisterOnHotplugCallback() {
+ if (mDrmPresenter) {
+ mDrmPresenter->unregisterOnHotplugCallback();
+ }
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::createHostComposerDisplayInfo(
+ Display* display, uint32_t hostDisplayId) {
+ HWC3::Error error = HWC3::Error::None;
+
+ int64_t displayId = display->getId();
+ int32_t displayConfigId;
+ int32_t displayWidth;
+ int32_t displayHeight;
+
+ error = display->getActiveConfig(&displayConfigId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " has no active config", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::WIDTH,
+ &displayWidth);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to get width", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ error = display->getDisplayAttribute(
+ displayConfigId, DisplayAttribute::HEIGHT, &displayHeight);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+ displayInfo.hostDisplayId = hostDisplayId;
+
+ if (displayInfo.compositionResultBuffer) {
+ FreeDisplayColorBuffer(displayInfo.compositionResultBuffer);
+ }
+ displayInfo.compositionResultBuffer =
+ AllocateDisplayColorBuffer(displayWidth, displayHeight);
+ if (displayInfo.compositionResultBuffer == nullptr) {
+ ALOGE("%s: display:%" PRIu64 " failed to create target buffer",
+ __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+
+ if (mDrmPresenter) {
+ auto [drmBufferCreateError, drmBuffer] =
+ mDrmPresenter->create(displayInfo.compositionResultBuffer);
+ if (drmBufferCreateError != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to create target drm buffer",
+ __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+ displayInfo.compositionResultDrmBuffer = std::move(drmBuffer);
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::onDisplayCreate(Display* display) {
+ HWC3::Error error = HWC3::Error::None;
+
+ int64_t displayId = display->getId();
+ int32_t displayConfigId;
+ int32_t displayWidth;
+ int32_t displayHeight;
+ int32_t displayDpiX;
+
+ error = display->getActiveConfig(&displayConfigId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " has no active config", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::WIDTH,
+ &displayWidth);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to get width", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ error = display->getDisplayAttribute(
+ displayConfigId, DisplayAttribute::HEIGHT, &displayHeight);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::DPI_X,
+ &displayDpiX);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__,
+ displayId);
+ return error;
+ }
+
+ uint32_t hostDisplayId = 0;
+
+ DEFINE_AND_VALIDATE_HOST_CONNECTION
+ if (displayId == 0) {
+ // Primary display:
+ hostCon->lock();
+ if (rcEnc->rcCreateDisplayById(rcEnc, displayId)) {
+ ALOGE("%s host failed to create display %" PRIu64, __func__, displayId);
+ hostCon->unlock();
+ return HWC3::Error::NoResources;
+ }
+ if (rcEnc->rcSetDisplayPoseDpi(rcEnc, displayId, -1, -1, displayWidth,
+ displayHeight, displayDpiX / 1000)) {
+ ALOGE("%s host failed to set display %" PRIu64, __func__, displayId);
+ hostCon->unlock();
+ return HWC3::Error::NoResources;
+ }
+ hostCon->unlock();
+ } else {
+ // Secondary display:
+ static constexpr const uint32_t kHostDisplayIdStart = 6;
+
+ uint32_t expectedHostDisplayId = kHostDisplayIdStart + displayId - 1;
+ uint32_t actualHostDisplayId = 0;
+
+ hostCon->lock();
+ rcEnc->rcDestroyDisplay(rcEnc, expectedHostDisplayId);
+ rcEnc->rcCreateDisplay(rcEnc, &actualHostDisplayId);
+ rcEnc->rcSetDisplayPose(rcEnc, actualHostDisplayId, -1, -1, displayWidth,
+ displayHeight);
+ hostCon->unlock();
+
+ if (actualHostDisplayId != expectedHostDisplayId) {
+ ALOGE(
+ "Something wrong with host displayId allocation, expected %d "
+ "but received %d",
+ expectedHostDisplayId, actualHostDisplayId);
+ }
+
+ hostDisplayId = actualHostDisplayId;
+ }
+
+ error = createHostComposerDisplayInfo(display, hostDisplayId);
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to initialize host info for display:%" PRIu64,
+ __FUNCTION__, displayId);
+ return error;
+ }
+
+ std::optional<std::vector<uint8_t>> edid;
+ if (mDrmPresenter) {
+ edid = mDrmPresenter->getEdid(displayId);
+ if (edid) {
+ display->setEdid(*edid);
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::onDisplayDestroy(Display* display) {
+ int64_t displayId = display->getId();
+
+ auto it = mDisplayInfos.find(displayId);
+ if (it == mDisplayInfos.end()) {
+ ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+ displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+ if (displayId != 0) {
+ DEFINE_AND_VALIDATE_HOST_CONNECTION
+ hostCon->lock();
+ rcEnc->rcDestroyDisplay(rcEnc, displayInfo.hostDisplayId);
+ hostCon->unlock();
+ }
+
+ FreeDisplayColorBuffer(displayInfo.compositionResultBuffer);
+
+ mDisplayInfos.erase(it);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::onDisplayClientTargetSet(Display* display) {
+ int64_t displayId = display->getId();
+
+ auto it = mDisplayInfos.find(displayId);
+ if (it == mDisplayInfos.end()) {
+ ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__,
+ displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId];
+
+ if (mIsMinigbm) {
+ FencedBuffer& clientTargetFencedBuffer = display->getClientTarget();
+
+ auto [drmBufferCreateError, drmBuffer] =
+ mDrmPresenter->create(clientTargetFencedBuffer.getBuffer());
+ if (drmBufferCreateError != HWC3::Error::None) {
+ ALOGE("%s: display:%" PRIu64 " failed to create client target drm buffer",
+ __FUNCTION__, displayId);
+ return HWC3::Error::NoResources;
+ }
+ displayInfo.clientTargetDrmBuffer = std::move(drmBuffer);
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::validateDisplay(Display* display,
+ DisplayChanges* outChanges) {
+ const auto& displayId = display->getId();
+
+ DEFINE_AND_VALIDATE_HOST_CONNECTION
+ hostCon->lock();
+ bool hostCompositionV1 = rcEnc->hasHostCompositionV1();
+ bool hostCompositionV2 = rcEnc->hasHostCompositionV2();
+ hostCon->unlock();
+
+ const std::vector<Layer*> layers = display->getOrderedLayers();
+ for (const auto& layer : layers) {
+ switch (layer->getCompositionType()) {
+ case Composition::INVALID:
+ // Log error for unused layers, layer leak?
+ ALOGE("%s layer:%" PRIu64 " CompositionType not set", __FUNCTION__,
+ layer->getId());
+ break;
+ case Composition::DISPLAY_DECORATION:
+ return HWC3::Error::Unsupported;
+ default:
+ break;
+ }
+ }
+
+ // If one layer requires a fall back to the client composition type, all
+ // layers will fall back to the client composition type.
+ bool fallBackToClient = (!hostCompositionV1 && !hostCompositionV2) ||
+ display->hasColorTransform();
+
+ if (!fallBackToClient) {
+ for (const auto& layer : layers) {
+ const auto& layerId = layer->getId();
+ const auto& layerCompositionType = layer->getCompositionType();
+
+ std::optional<Composition> layerFallBackTo = std::nullopt;
+ switch (layerCompositionType) {
+ case Composition::CLIENT:
+ case Composition::SIDEBAND:
+ ALOGI("%s: layer %" PRIu32 " CompositionType %d, fallback to client",
+ __FUNCTION__, static_cast<uint32_t>(layer->getId()),
+ layerCompositionType);
+ layerFallBackTo = Composition::CLIENT;
+ break;
+ case Composition::CURSOR:
+ ALOGI("%s: layer %" PRIu32 " CompositionType %d, fallback to device",
+ __FUNCTION__, static_cast<uint32_t>(layer->getId()),
+ layerCompositionType);
+ layerFallBackTo = Composition::DEVICE;
+ break;
+ case Composition::INVALID:
+ case Composition::DEVICE:
+ case Composition::SOLID_COLOR:
+ layerFallBackTo = std::nullopt;
+ break;
+ default:
+ ALOGE("%s: layer %" PRIu32 " has an unknown composition type: %d",
+ __FUNCTION__, static_cast<uint32_t>(layer->getId()),
+ layerCompositionType);
+ }
+ if (layerFallBackTo == Composition::CLIENT) {
+ fallBackToClient = true;
+ }
+ if (layerFallBackTo.has_value()) {
+ outChanges->addLayerCompositionChange(displayId, layerId,
+ *layerFallBackTo);
+ }
+ }
+ }
+
+ if (fallBackToClient) {
+ outChanges->clearLayerCompositionChanges();
+ for (auto& layer : layers) {
+ const auto& layerId = layer->getId();
+ if (layer->getCompositionType() == Composition::INVALID) {
+ continue;
+ }
+ if (layer->getCompositionType() != Composition::CLIENT) {
+ outChanges->addLayerCompositionChange(displayId, layerId,
+ Composition::CLIENT);
+ }
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error HostFrameComposer::presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences) {
+ auto displayId = display->getId();
+ auto displayInfoIt = mDisplayInfos.find(displayId);
+ if (displayInfoIt == mDisplayInfos.end()) {
+ ALOGE("%s: failed to find display buffers for display:%" PRIu64,
+ __FUNCTION__, displayId);
+ return HWC3::Error::BadDisplay;
+ }
+
+ HostComposerDisplayInfo& displayInfo = displayInfoIt->second;
+
+ HostConnection* hostCon;
+ ExtendedRCEncoderContext* rcEnc;
+ HWC3::Error error = getAndValidateHostConnection(&hostCon, &rcEnc);
+ if (error != HWC3::Error::None) {
+ return error;
+ }
+ hostCon->lock();
+ bool hostCompositionV1 = rcEnc->hasHostCompositionV1();
+ bool hostCompositionV2 = rcEnc->hasHostCompositionV2();
+ hostCon->unlock();
+
+ // Ff we supports v2, then discard v1
+ if (hostCompositionV2) {
+ hostCompositionV1 = false;
+ }
+
+ const std::vector<Layer*> layers = display->getOrderedLayers();
+ if (hostCompositionV2 || hostCompositionV1) {
+ uint32_t numLayer = 0;
+ for (auto layer : layers) {
+ if (layer->getCompositionType() == Composition::DEVICE ||
+ layer->getCompositionType() == Composition::SOLID_COLOR) {
+ numLayer++;
+ }
+ }
+
+ DEBUG_LOG("%s: presenting display:%" PRIu64 " with %d layers", __FUNCTION__,
+ displayId, static_cast<int>(layers.size()));
+
+ if (numLayer == 0) {
+ ALOGW(
+ "%s display has no layers to compose, flushing client target buffer.",
+ __FUNCTION__);
+
+ FencedBuffer& displayClientTarget = display->getClientTarget();
+ if (displayClientTarget.getBuffer() != nullptr) {
+ ::android::base::unique_fd fence = displayClientTarget.getFence();
+ if (mIsMinigbm) {
+ auto [_, flushCompleteFence] = mDrmPresenter->flushToDisplay(
+ displayId, *displayInfo.clientTargetDrmBuffer, fence);
+
+ *outDisplayFence = std::move(flushCompleteFence);
+ } else {
+ post(hostCon, rcEnc, displayClientTarget.getBuffer());
+ *outDisplayFence = std::move(fence);
+ }
+ }
+ return HWC3::Error::None;
+ }
+
+ std::unique_ptr<ComposeMsg> composeMsg;
+ std::unique_ptr<ComposeMsg_v2> composeMsgV2;
+
+ if (hostCompositionV1) {
+ composeMsg.reset(new ComposeMsg(numLayer));
+ } else {
+ composeMsgV2.reset(new ComposeMsg_v2(numLayer));
+ }
+
+ // Handle the composition
+ ComposeDevice* p;
+ ComposeDevice_v2* p2;
+ ComposeLayer* l;
+
+ if (hostCompositionV1) {
+ p = composeMsg->get();
+ l = p->layer;
+ } else {
+ p2 = composeMsgV2->get();
+ l = p2->layer;
+ }
+
+ std::vector<int64_t> releaseLayerIds;
+ for (auto layer : layers) {
+ // TODO: use local var composisitonType to store getCompositionType()
+ if (layer->getCompositionType() != Composition::DEVICE &&
+ layer->getCompositionType() != Composition::SOLID_COLOR) {
+ ALOGE("%s: Unsupported composition types %d layer %u", __FUNCTION__,
+ layer->getCompositionType(), (uint32_t)layer->getId());
+ continue;
+ }
+ // send layer composition command to host
+ if (layer->getCompositionType() == Composition::DEVICE) {
+ releaseLayerIds.emplace_back(layer->getId());
+
+ ::android::base::unique_fd fence = layer->getBuffer().getFence();
+ if (fence.ok()) {
+ int err = sync_wait(fence.get(), 3000);
+ if (err < 0 && errno == ETIME) {
+ ALOGE("%s waited on fence %d for 3000 ms", __FUNCTION__,
+ fence.get());
+ }
+ } else {
+ ALOGV("%s: acquire fence not set for layer %u", __FUNCTION__,
+ (uint32_t)layer->getId());
+ }
+ const native_handle_t* cb = layer->getBuffer().getBuffer();
+ if (cb != nullptr) {
+ l->cbHandle = hostCon->grallocHelper()->getHostHandle(cb);
+ } else {
+ ALOGE("%s null buffer for layer %d", __FUNCTION__,
+ (uint32_t)layer->getId());
+ }
+ } else {
+ // solidcolor has no buffer
+ l->cbHandle = 0;
+ }
+ l->composeMode = (hwc2_composition_t)layer->getCompositionType();
+ l->displayFrame = AsHwcRect(layer->getDisplayFrame());
+ l->crop = AsHwcFrect(layer->getSourceCrop());
+ l->blendMode = static_cast<int32_t>(layer->getBlendMode());
+ l->alpha = layer->getPlaneAlpha();
+ l->color = AsHwcColor(layer->getColor());
+ l->transform = AsHwcTransform(layer->getTransform());
+ ALOGV(
+ " cb %d blendmode %d alpha %f %d %d %d %d z %d"
+ " composeMode %d, transform %d",
+ l->cbHandle, l->blendMode, l->alpha, l->displayFrame.left,
+ l->displayFrame.top, l->displayFrame.right, l->displayFrame.bottom,
+ layer->getZOrder(), l->composeMode, l->transform);
+ l++;
+ }
+ if (hostCompositionV1) {
+ p->version = 1;
+ p->targetHandle = hostCon->grallocHelper()->getHostHandle(
+ displayInfo.compositionResultBuffer);
+ p->numLayers = numLayer;
+ } else {
+ p2->version = 2;
+ p2->displayId = displayInfo.hostDisplayId;
+ p2->targetHandle = hostCon->grallocHelper()->getHostHandle(
+ displayInfo.compositionResultBuffer);
+ p2->numLayers = numLayer;
+ }
+
+ void* buffer;
+ uint32_t bufferSize;
+ if (hostCompositionV1) {
+ buffer = (void*)p;
+ bufferSize = sizeof(ComposeDevice) + numLayer * sizeof(ComposeLayer);
+ } else {
+ bufferSize = sizeof(ComposeDevice_v2) + numLayer * sizeof(ComposeLayer);
+ buffer = (void*)p2;
+ }
+
+ ::android::base::unique_fd retire_fd;
+ hostCon->lock();
+ if (rcEnc->hasAsyncFrameCommands()) {
+ if (mIsMinigbm) {
+ rcEnc->rcComposeAsyncWithoutPost(rcEnc, bufferSize, buffer);
+ } else {
+ rcEnc->rcComposeAsync(rcEnc, bufferSize, buffer);
+ }
+ } else {
+ if (mIsMinigbm) {
+ rcEnc->rcComposeWithoutPost(rcEnc, bufferSize, buffer);
+ } else {
+ rcEnc->rcCompose(rcEnc, bufferSize, buffer);
+ }
+ }
+ hostCon->unlock();
+
+ // Send a retire fence and use it as the release fence for all layers,
+ // since media expects it
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_ANDROID,
+ EGL_NO_NATIVE_FENCE_FD_ANDROID};
+
+ uint64_t sync_handle, thread_handle;
+
+ // We don't use rc command to sync if we are using ANGLE on the guest with
+ // virtio-gpu.
+ bool useRcCommandToSync = !(mUseAngle && mIsMinigbm);
+
+ if (useRcCommandToSync) {
+ hostCon->lock();
+ rcEnc->rcCreateSyncKHR(
+ rcEnc, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs, 2 * sizeof(EGLint),
+ true /* destroy when signaled */, &sync_handle, &thread_handle);
+ hostCon->unlock();
+ }
+
+ if (mIsMinigbm) {
+ auto [_, fence] = mDrmPresenter->flushToDisplay(
+ displayId, *displayInfo.compositionResultDrmBuffer, -1);
+ retire_fd = std::move(fence);
+ } else {
+ int fd;
+ goldfish_sync_queue_work(mSyncDeviceFd, sync_handle, thread_handle, &fd);
+ retire_fd = ::android::base::unique_fd(fd);
+ }
+
+ for (int64_t layerId : releaseLayerIds) {
+ (*outLayerFences)[layerId] =
+ ::android::base::unique_fd(dup(retire_fd.get()));
+ }
+ *outDisplayFence = ::android::base::unique_fd(dup(retire_fd.get()));
+
+ if (useRcCommandToSync) {
+ hostCon->lock();
+ if (rcEnc->hasAsyncFrameCommands()) {
+ rcEnc->rcDestroySyncKHRAsync(rcEnc, sync_handle);
+ } else {
+ rcEnc->rcDestroySyncKHR(rcEnc, sync_handle);
+ }
+ hostCon->unlock();
+ }
+
+ } else {
+ // we set all layers Composition::CLIENT, so do nothing.
+ FencedBuffer& displayClientTarget = display->getClientTarget();
+ ::android::base::unique_fd displayClientTargetFence =
+ displayClientTarget.getFence();
+ if (mIsMinigbm) {
+ auto [_, flushFence] = mDrmPresenter->flushToDisplay(
+ displayId, *displayInfo.compositionResultDrmBuffer,
+ displayClientTargetFence);
+ *outDisplayFence = std::move(flushFence);
+ } else {
+ post(hostCon, rcEnc, displayClientTarget.getBuffer());
+ *outDisplayFence = std::move(displayClientTargetFence);
+ }
+ ALOGV("%s fallback to post, returns outRetireFence %d", __FUNCTION__,
+ outDisplayFence->get());
+ }
+ return HWC3::Error::None;
+}
+
+void HostFrameComposer::post(HostConnection* hostCon,
+ ExtendedRCEncoderContext* rcEnc,
+ buffer_handle_t h) {
+ assert(cb && "native_handle_t::from(h) failed");
+
+ hostCon->lock();
+ rcEnc->rcFBPost(rcEnc, hostCon->grallocHelper()->getHostHandle(h));
+ hostCon->flush();
+ hostCon->unlock();
+}
+
+HWC3::Error HostFrameComposer::onActiveConfigChange(Display* display) {
+ DEBUG_LOG("%s: display:%" PRIu64, __FUNCTION__, display->getId());
+ HWC3::Error error = createHostComposerDisplayInfo(display, display->getId());
+ if (error != HWC3::Error::None) {
+ ALOGE("%s failed to update host info for display:%" PRIu64, __FUNCTION__,
+ display->getId());
+ return error;
+ }
+ return HWC3::Error::None;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/HostFrameComposer.h b/system/hwc3/HostFrameComposer.h
new file mode 100644
index 0000000..e99e6f2
--- /dev/null
+++ b/system/hwc3/HostFrameComposer.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_HOSTFRAMECOMPOSER_H
+#define ANDROID_HWC_HOSTFRAMECOMPOSER_H
+
+#include <android-base/unique_fd.h>
+
+#include <optional>
+#include <tuple>
+
+#include "Common.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "HostConnection.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class HostFrameComposer : public FrameComposer {
+ public:
+ HostFrameComposer() = default;
+
+ HostFrameComposer(const HostFrameComposer&) = delete;
+ HostFrameComposer& operator=(const HostFrameComposer&) = delete;
+
+ HostFrameComposer(HostFrameComposer&&) = delete;
+ HostFrameComposer& operator=(HostFrameComposer&&) = delete;
+
+ HWC3::Error init() override;
+
+ HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+ HWC3::Error unregisterOnHotplugCallback() override;
+
+ HWC3::Error onDisplayCreate(Display* display) override;
+
+ HWC3::Error onDisplayDestroy(Display* display) override;
+
+ HWC3::Error onDisplayClientTargetSet(Display* display) override;
+
+ // Determines if this composer can compose the given layers on the given
+ // display and requests changes for layers that can't not be composed.
+ HWC3::Error validateDisplay(Display* display,
+ DisplayChanges* outChanges) override;
+
+ // Performs the actual composition of layers and presents the composed result
+ // to the display.
+ HWC3::Error presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+ override;
+
+ HWC3::Error onActiveConfigChange(Display* display) override;
+
+ private:
+ HWC3::Error createHostComposerDisplayInfo(Display* display,
+ uint32_t hostDisplayId);
+
+ void post(HostConnection* hostCon, ExtendedRCEncoderContext* rcEnc,
+ buffer_handle_t h);
+
+ bool mIsMinigbm = false;
+
+ bool mUseAngle = false;
+
+ int mSyncDeviceFd = -1;
+
+ struct HostComposerDisplayInfo {
+ uint32_t hostDisplayId = 0;
+
+ // Additional per display buffer for the composition result.
+ const native_handle_t* compositionResultBuffer = nullptr;
+
+ // Drm info for the additional composition result buffer.
+ std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+
+ // Drm info for the displays client target buffer.
+ std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;
+ };
+
+ std::unordered_map<int64_t, HostComposerDisplayInfo> mDisplayInfos;
+
+ std::optional<DrmPresenter> mDrmPresenter;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/HostUtils.cpp b/system/hwc3/HostUtils.cpp
new file mode 100644
index 0000000..ea0e807
--- /dev/null
+++ b/system/hwc3/HostUtils.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 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 "HostUtils.h"
+
+#include <memory>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HostConnection* createOrGetHostConnection() {
+ static std::unique_ptr<HostConnection> sHostCon;
+
+ if (!sHostCon) {
+ sHostCon = HostConnection::createUnique();
+ }
+ return sHostCon.get();
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/system/hwc3/HostUtils.h b/system/hwc3/HostUtils.h
new file mode 100644
index 0000000..ef02389
--- /dev/null
+++ b/system/hwc3/HostUtils.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ANDROID_HWC_HOSTUTILS_H
+#define ANDROID_HWC_HOSTUTILS_H
+
+#include "Common.h"
+#include "HostConnection.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HostConnection* createOrGetHostConnection();
+
+inline HWC3::Error getAndValidateHostConnection(
+ HostConnection** ppHostCon, ExtendedRCEncoderContext** ppRcEnc) {
+ *ppHostCon = nullptr;
+ *ppRcEnc = nullptr;
+
+ HostConnection* hostCon = createOrGetHostConnection();
+ if (!hostCon) {
+ ALOGE("%s: Failed to get host connection\n", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+ ExtendedRCEncoderContext* rcEnc = hostCon->rcEncoder();
+ if (!rcEnc) {
+ ALOGE("%s: Failed to get renderControl encoder context\n", __FUNCTION__);
+ return HWC3::Error::NoResources;
+ }
+
+ *ppHostCon = hostCon;
+ *ppRcEnc = rcEnc;
+ return HWC3::Error::None;
+}
+
+#define DEFINE_AND_VALIDATE_HOST_CONNECTION \
+ HostConnection* hostCon; \
+ ExtendedRCEncoderContext* rcEnc; \
+ { \
+ HWC3::Error res = getAndValidateHostConnection(&hostCon, &rcEnc); \
+ if (res != HWC3::Error::None) { \
+ return res; \
+ } \
+ }
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Layer.cpp b/system/hwc3/Layer.cpp
new file mode 100644
index 0000000..f58d5a5
--- /dev/null
+++ b/system/hwc3/Layer.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2022 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 "Layer.h"
+
+#include <android-base/unique_fd.h>
+#include <sync/sync.h>
+
+#include <atomic>
+#include <cmath>
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+std::atomic<int64_t> sNextId{1};
+
+} // namespace
+
+Layer::Layer() : mId(sNextId++) {}
+
+HWC3::Error Layer::setCursorPosition(const common::Point& position) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ if (mCompositionType != Composition::CURSOR) {
+ ALOGE("%s: CompositionType not Cursor type", __FUNCTION__);
+ return HWC3::Error::BadLayer;
+ }
+
+ mCursorPosition = position;
+ return HWC3::Error::None;
+}
+
+common::Point Layer::getCursorPosition() const {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return mCursorPosition;
+}
+
+HWC3::Error Layer::setBuffer(buffer_handle_t buffer,
+ const ndk::ScopedFileDescriptor& fence) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ if (buffer == nullptr) {
+ ALOGE("%s: missing handle", __FUNCTION__);
+ return HWC3::Error::BadParameter;
+ }
+
+ mBuffer.set(buffer, fence);
+ return HWC3::Error::None;
+}
+
+FencedBuffer& Layer::getBuffer() {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return mBuffer;
+}
+
+buffer_handle_t Layer::waitAndGetBuffer() {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ ::android::base::unique_fd fence = mBuffer.getFence();
+ if (fence.ok()) {
+ int err = sync_wait(fence.get(), 3000);
+ if (err < 0 && errno == ETIME) {
+ ALOGE("%s waited on fence %" PRId32 " for 3000 ms", __FUNCTION__,
+ fence.get());
+ }
+ }
+
+ return mBuffer.getBuffer();
+}
+
+HWC3::Error Layer::setSurfaceDamage(
+ const std::vector<std::optional<common::Rect>>& /*damage*/) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Layer::setBlendMode(common::BlendMode blendMode) {
+ const auto blendModeString = toString(blendMode);
+ DEBUG_LOG("%s: layer:%" PRId64 " blend mode:%s", __FUNCTION__, mId,
+ blendModeString.c_str());
+
+ mBlendMode = blendMode;
+ return HWC3::Error::None;
+}
+
+common::BlendMode Layer::getBlendMode() const {
+ const auto blendMode = mBlendMode;
+ const auto blendModeString = toString(blendMode);
+ DEBUG_LOG("%s: layer:%" PRId64 " blend mode:%s", __FUNCTION__, mId,
+ blendModeString.c_str());
+
+ return blendMode;
+}
+
+HWC3::Error Layer::setColor(Color color) {
+ DEBUG_LOG("%s: layer:%" PRId64
+ " color-r:%d color-g:%d color-b:%d color-a:%d)",
+ __FUNCTION__, mId, color.r, color.g, color.b, color.a);
+
+ mColor = color;
+ return HWC3::Error::None;
+}
+
+Color Layer::getColor() const {
+ auto color = mColor;
+ DEBUG_LOG("%s: layer:%" PRId64
+ " color-r:%d color-g:%d color-b:%d color-a:%d)",
+ __FUNCTION__, mId, color.r, color.g, color.b, color.a);
+
+ return color;
+}
+
+HWC3::Error Layer::setCompositionType(Composition compositionType) {
+ const auto compositionTypeString = toString(compositionType);
+ DEBUG_LOG("%s: layer:%" PRId64 " composition type:%s", __FUNCTION__, mId,
+ compositionTypeString.c_str());
+
+ mCompositionType = compositionType;
+ return HWC3::Error::None;
+}
+
+Composition Layer::getCompositionType() const {
+ const auto compositionTypeString = toString(mCompositionType);
+ DEBUG_LOG("%s: layer:%" PRId64 " composition type:%s", __FUNCTION__, mId,
+ compositionTypeString.c_str());
+
+ return mCompositionType;
+}
+
+HWC3::Error Layer::setDataspace(common::Dataspace dataspace) {
+ const auto dataspaceString = toString(dataspace);
+ DEBUG_LOG("%s: layer:%" PRId64 " dataspace:%s", __FUNCTION__, mId,
+ dataspaceString.c_str());
+
+ mDataspace = dataspace;
+ return HWC3::Error::None;
+}
+
+common::Dataspace Layer::getDataspace() const {
+ const auto dataspaceString = toString(mDataspace);
+ DEBUG_LOG("%s: layer:%" PRId64 " dataspace:%s", __FUNCTION__, mId,
+ dataspaceString.c_str());
+
+ return mDataspace;
+}
+
+HWC3::Error Layer::setDisplayFrame(common::Rect frame) {
+ DEBUG_LOG("%s: layer:%" PRId64
+ " display frame rect-left:%d rect-top:%d rect-right:%d rect-bot:%d",
+ __FUNCTION__, mId, frame.left, frame.top, frame.right,
+ frame.bottom);
+
+ mDisplayFrame = frame;
+ return HWC3::Error::None;
+}
+
+common::Rect Layer::getDisplayFrame() const {
+ auto frame = mDisplayFrame;
+ DEBUG_LOG("%s: layer:%" PRId64
+ " display frame rect-left:%d rect-top:%d rect-right:%d rect-bot:%d",
+ __FUNCTION__, mId, frame.left, frame.top, frame.right,
+ frame.bottom);
+
+ return frame;
+}
+
+HWC3::Error Layer::setPlaneAlpha(float alpha) {
+ DEBUG_LOG("%s: layer:%" PRId64 "alpha:%f", __FUNCTION__, mId, alpha);
+
+ mPlaneAlpha = alpha;
+ return HWC3::Error::None;
+}
+
+float Layer::getPlaneAlpha() const {
+ auto alpha = mPlaneAlpha;
+ DEBUG_LOG("%s: layer:%" PRId64 "alpha:%f", __FUNCTION__, mId, alpha);
+
+ return alpha;
+}
+
+HWC3::Error Layer::setSidebandStream(buffer_handle_t /*stream*/) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Layer::setSourceCrop(common::FRect crop) {
+ DEBUG_LOG("%s: layer:%" PRId64
+ "crop rect-left:%f rect-top:%f rect-right:%f rect-bot:%f",
+ __FUNCTION__, mId, crop.left, crop.top, crop.right, crop.bottom);
+
+ mSourceCrop = crop;
+ return HWC3::Error::None;
+}
+
+common::FRect Layer::getSourceCrop() const {
+ common::FRect crop = mSourceCrop;
+ DEBUG_LOG("%s: layer:%" PRId64
+ "crop rect-left:%f rect-top:%f rect-right:%f rect-bot:%f",
+ __FUNCTION__, mId, crop.left, crop.top, crop.right, crop.bottom);
+
+ return crop;
+}
+
+common::Rect Layer::getSourceCropInt() const {
+ common::Rect crop = {};
+ crop.left = static_cast<int>(mSourceCrop.left);
+ crop.top = static_cast<int>(mSourceCrop.top);
+ crop.right = static_cast<int>(mSourceCrop.right);
+ crop.bottom = static_cast<int>(mSourceCrop.bottom);
+ DEBUG_LOG("%s: layer:%" PRId64
+ "crop rect-left:%d rect-top:%d rect-right:%d rect-bot:%d",
+ __FUNCTION__, mId, crop.left, crop.top, crop.right, crop.bottom);
+
+ return crop;
+}
+
+HWC3::Error Layer::setTransform(common::Transform transform) {
+ const auto transformString = toString(transform);
+ DEBUG_LOG("%s: layer:%" PRId64 " transform:%s", __FUNCTION__, mId,
+ transformString.c_str());
+
+ mTransform = transform;
+ return HWC3::Error::None;
+}
+
+common::Transform Layer::getTransform() const {
+ const auto transformString = toString(mTransform);
+ DEBUG_LOG("%s: layer:%" PRId64 " transform:%s", __FUNCTION__, mId,
+ transformString.c_str());
+
+ return mTransform;
+}
+
+HWC3::Error Layer::setVisibleRegion(
+ const std::vector<std::optional<common::Rect>>& visible) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ mVisibleRegion.clear();
+ mVisibleRegion.reserve(visible.size());
+ for (const auto& rectOption : visible) {
+ if (rectOption) {
+ mVisibleRegion.push_back(*rectOption);
+ }
+ }
+
+ return HWC3::Error::None;
+}
+
+std::size_t Layer::getNumVisibleRegions() const {
+ const std::size_t num = mVisibleRegion.size();
+ DEBUG_LOG("%s: layer:%" PRId64 " number of visible regions: %zu",
+ __FUNCTION__, mId, num);
+
+ return num;
+}
+
+HWC3::Error Layer::setZOrder(int32_t z) {
+ DEBUG_LOG("%s: layer:%" PRId64 " z:%d", __FUNCTION__, mId, z);
+
+ mZOrder = z;
+ return HWC3::Error::None;
+}
+
+int32_t Layer::getZOrder() const {
+ DEBUG_LOG("%s: layer:%" PRId64 " z:%d", __FUNCTION__, mId, mZOrder);
+
+ return mZOrder;
+}
+
+HWC3::Error Layer::setPerFrameMetadata(
+ const std::vector<std::optional<PerFrameMetadata>>& /*perFrameMetadata*/) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error Layer::setColorTransform(const std::vector<float>& colorTransform) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ if (colorTransform.size() < 16) {
+ return HWC3::Error::BadParameter;
+ }
+
+ mColorTransform.emplace();
+ std::copy_n(colorTransform.data(), 16, mColorTransform->data());
+ return HWC3::Error::None;
+}
+
+const std::optional<std::array<float, 16>>& Layer::getColorTransform() const {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return mColorTransform;
+}
+
+HWC3::Error Layer::setBrightness(float brightness) {
+ DEBUG_LOG("%s: layer:%" PRId64 " brightness:%f", __FUNCTION__, mId,
+ brightness);
+
+ if (std::isnan(brightness) || brightness < 0.0f || brightness > 1.0f) {
+ ALOGE("%s: layer:%" PRId64 " brightness:%f", __FUNCTION__, mId, brightness);
+ return HWC3::Error::BadParameter;
+ }
+
+ mBrightness = brightness;
+ return HWC3::Error::None;
+}
+
+float Layer::getBrightness() const {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return mBrightness;
+}
+
+HWC3::Error Layer::setPerFrameMetadataBlobs(
+ const std::vector<
+ std::optional<PerFrameMetadataBlob>>& /*perFrameMetadata*/) {
+ DEBUG_LOG("%s: layer:%" PRId64, __FUNCTION__, mId);
+
+ return HWC3::Error::None;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/Layer.h b/system/hwc3/Layer.h
new file mode 100644
index 0000000..7d6a9ed
--- /dev/null
+++ b/system/hwc3/Layer.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_LAYER_H
+#define ANDROID_HWC_LAYER_H
+
+#include <vector>
+
+#include "Common.h"
+#include "FencedBuffer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class Layer {
+ public:
+ explicit Layer();
+
+ Layer(const Layer&) = delete;
+ Layer& operator=(const Layer&) = delete;
+
+ Layer(Layer&&) = default;
+ Layer& operator=(Layer&&) = default;
+
+ int64_t getId() const { return mId; }
+
+ HWC3::Error setCursorPosition(const common::Point& cursorPosition);
+ common::Point getCursorPosition() const;
+
+ HWC3::Error setBuffer(buffer_handle_t buffer,
+ const ndk::ScopedFileDescriptor& fence);
+ FencedBuffer& getBuffer();
+ buffer_handle_t waitAndGetBuffer();
+
+ HWC3::Error setSurfaceDamage(
+ const std::vector<std::optional<common::Rect>>& damage);
+
+ HWC3::Error setBlendMode(common::BlendMode mode);
+ common::BlendMode getBlendMode() const;
+
+ HWC3::Error setColor(Color color);
+ Color getColor() const;
+
+ HWC3::Error setCompositionType(Composition composition);
+ Composition getCompositionType() const;
+
+ HWC3::Error setDataspace(common::Dataspace dataspace);
+ common::Dataspace getDataspace() const;
+
+ HWC3::Error setDisplayFrame(common::Rect frame);
+ common::Rect getDisplayFrame() const;
+
+ HWC3::Error setPlaneAlpha(float alpha);
+ float getPlaneAlpha() const;
+
+ HWC3::Error setSidebandStream(buffer_handle_t stream);
+
+ HWC3::Error setSourceCrop(common::FRect crop);
+ common::FRect getSourceCrop() const;
+ common::Rect getSourceCropInt() const;
+
+ HWC3::Error setTransform(common::Transform transform);
+ common::Transform getTransform() const;
+
+ HWC3::Error setVisibleRegion(
+ const std::vector<std::optional<common::Rect>>& visible);
+ std::size_t getNumVisibleRegions() const;
+
+ HWC3::Error setZOrder(int32_t z);
+ int32_t getZOrder() const;
+
+ HWC3::Error setPerFrameMetadata(
+ const std::vector<std::optional<PerFrameMetadata>>& perFrameMetadata);
+
+ HWC3::Error setColorTransform(const std::vector<float>& colorTransform);
+ const std::optional<std::array<float, 16>>& getColorTransform() const;
+
+ HWC3::Error setBrightness(float brightness);
+ float getBrightness() const;
+
+ HWC3::Error setPerFrameMetadataBlobs(
+ const std::vector<std::optional<PerFrameMetadataBlob>>& perFrameMetadata);
+
+ private:
+ const int64_t mId;
+ common::Point mCursorPosition;
+ FencedBuffer mBuffer;
+ common::BlendMode mBlendMode = common::BlendMode::NONE;
+ Color mColor = {0, 0, 0, 0};
+ Composition mCompositionType = Composition::INVALID;
+ common::Dataspace mDataspace = common::Dataspace::UNKNOWN;
+ common::Rect mDisplayFrame = {0, 0, -1, -1};
+ float mPlaneAlpha = 0.0f;
+ common::FRect mSourceCrop = {0.0f, 0.0f, -1.0f, -1.0f};
+ common::Transform mTransform = common::Transform{0};
+ std::vector<common::Rect> mVisibleRegion;
+ int32_t mZOrder = 0;
+ std::optional<std::array<float, 16>> mColorTransform;
+ float mBrightness = 1.0f;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/Main.cpp b/system/hwc3/Main.cpp
new file mode 100644
index 0000000..440a1c2
--- /dev/null
+++ b/system/hwc3/Main.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022, 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-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/ProcessState.h>
+#include <sched.h>
+
+#include "Composer.h"
+
+using aidl::android::hardware::graphics::composer3::impl::Composer;
+
+int main(int /*argc*/, char** /*argv*/) {
+ ALOGI("RanchuHWC (HWComposer3/HWC3) starting up...");
+
+ // same as SF main thread
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) {
+ ALOGE("%s: failed to set priority: %s", __FUNCTION__, strerror(errno));
+ }
+
+ auto composer = ndk::SharedRefBase::make<Composer>();
+ CHECK(composer != nullptr);
+
+ const std::string instance =
+ std::string() + Composer::descriptor + "/default";
+ binder_status_t status =
+ AServiceManager_addService(composer->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ // Thread pool for vendor libbinder for internal vendor services
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(2);
+ android::ProcessState::self()->startThreadPool();
+
+ // Thread pool for system libbinder (via libbinder_ndk) for aidl services
+ // IComposer and IDisplay
+ ABinderProcess_setThreadPoolMaxThreadCount(5);
+ ABinderProcess_startThreadPool();
+ ABinderProcess_joinThreadPool();
+
+ return EXIT_FAILURE;
+}
\ No newline at end of file
diff --git a/system/hwc3/NoOpFrameComposer.cpp b/system/hwc3/NoOpFrameComposer.cpp
new file mode 100644
index 0000000..e988d46
--- /dev/null
+++ b/system/hwc3/NoOpFrameComposer.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2022 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 "NoOpFrameComposer.h"
+
+#include "Display.h"
+#include "Drm.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+HWC3::Error NoOpFrameComposer::init() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::registerOnHotplugCallback(
+ const HotplugCallback&) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::unregisterOnHotplugCallback() {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onDisplayCreate(Display*) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onDisplayDestroy(Display*) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onDisplayClientTargetSet(Display*) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::onActiveConfigChange(Display*) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+};
+
+HWC3::Error NoOpFrameComposer::validateDisplay(Display*, DisplayChanges*) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error NoOpFrameComposer::presentDisplay(
+ Display*, ::android::base::unique_fd*,
+ std::unordered_map<int64_t,
+ ::android::base::unique_fd>* /*outLayerFences*/) {
+ DEBUG_LOG("%s", __FUNCTION__);
+
+ return HWC3::Error::None;
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/NoOpFrameComposer.h b/system/hwc3/NoOpFrameComposer.h
new file mode 100644
index 0000000..4722477
--- /dev/null
+++ b/system/hwc3/NoOpFrameComposer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_NOOPFRAMECOMPOSER_H
+#define ANDROID_HWC_NOOPFRAMECOMPOSER_H
+
+#include "Common.h"
+#include "Display.h"
+#include "DrmPresenter.h"
+#include "FrameComposer.h"
+#include "Gralloc.h"
+#include "Layer.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+class NoOpFrameComposer : public FrameComposer {
+ public:
+ NoOpFrameComposer() = default;
+
+ NoOpFrameComposer(const NoOpFrameComposer&) = delete;
+ NoOpFrameComposer& operator=(const NoOpFrameComposer&) = delete;
+
+ NoOpFrameComposer(NoOpFrameComposer&&) = delete;
+ NoOpFrameComposer& operator=(NoOpFrameComposer&&) = delete;
+
+ HWC3::Error init() override;
+
+ HWC3::Error registerOnHotplugCallback(const HotplugCallback& cb) override;
+
+ HWC3::Error unregisterOnHotplugCallback() override;
+
+ HWC3::Error onDisplayCreate(Display*) override;
+
+ HWC3::Error onDisplayDestroy(Display*) override;
+
+ HWC3::Error onDisplayClientTargetSet(Display*) override;
+
+ // Determines if this composer can compose the given layers on the given
+ // display and requests changes for layers that can't not be composed.
+ HWC3::Error validateDisplay(Display* display,
+ DisplayChanges* outChanges) override;
+
+ // Performs the actual composition of layers and presents the composed result
+ // to the display.
+ HWC3::Error presentDisplay(
+ Display* display, ::android::base::unique_fd* outDisplayFence,
+ std::unordered_map<int64_t, ::android::base::unique_fd>* outLayerFences)
+ override;
+
+ HWC3::Error onActiveConfigChange(Display* /*display*/) override;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/Time.h b/system/hwc3/Time.h
new file mode 100644
index 0000000..79473a5
--- /dev/null
+++ b/system/hwc3/Time.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_TIME_H
+#define ANDROID_HWC_TIME_H
+
+#include <utils/Timers.h>
+
+#include <chrono>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+using Nanoseconds = std::chrono::nanoseconds;
+
+using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
+
+inline TimePoint asTimePoint(int64_t nanos) {
+ return TimePoint(Nanoseconds(nanos));
+}
+
+inline TimePoint now() {
+ return asTimePoint(systemTime(SYSTEM_TIME_MONOTONIC));
+}
+
+inline int32_t asNanosDuration(Nanoseconds duration) {
+ return duration.count();
+}
+
+inline int64_t asNanosTimePoint(TimePoint time) {
+ TimePoint zero(Nanoseconds(0));
+ return std::chrono::duration_cast<Nanoseconds>(time - zero).count();
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
diff --git a/system/hwc3/VsyncThread.cpp b/system/hwc3/VsyncThread.cpp
new file mode 100644
index 0000000..350a906
--- /dev/null
+++ b/system/hwc3/VsyncThread.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2022 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 "VsyncThread.h"
+
+#include <utils/ThreadDefs.h>
+
+#include <thread>
+
+#include "Time.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+namespace {
+
+// Returns the timepoint of the next vsync after the 'now' timepoint that is
+// a multiple of 'vsyncPeriod' in-phase/offset-from 'previousSync'.
+//
+// Some examples:
+// * vsyncPeriod=50ns previousVsync=500ns now=510ns => 550ns
+// * vsyncPeriod=50ns previousVsync=300ns now=510ns => 550ns
+// * vsyncPeriod=50ns previousVsync=500ns now=550ns => 550ns
+TimePoint GetNextVsyncInPhase(Nanoseconds vsyncPeriod, TimePoint previousVsync,
+ TimePoint now) {
+ const auto elapsed = Nanoseconds(now - previousVsync);
+ const auto nextMultiple = (elapsed / vsyncPeriod) + 1;
+ return previousVsync + (nextMultiple * vsyncPeriod);
+}
+
+} // namespace
+
+VsyncThread::VsyncThread(int64_t displayId) : mDisplayId(displayId) {
+ mPreviousVsync = std::chrono::steady_clock::now() - mVsyncPeriod;
+}
+
+VsyncThread::~VsyncThread() { stop(); }
+
+HWC3::Error VsyncThread::start(int32_t vsyncPeriodNanos) {
+ DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
+
+ mVsyncPeriod = Nanoseconds(vsyncPeriodNanos);
+
+ mThread = std::thread([this]() { threadLoop(); });
+
+ const std::string name =
+ "display_" + std::to_string(mDisplayId) + "_vsync_thread";
+
+ int ret = pthread_setname_np(mThread.native_handle(), name.c_str());
+ if (ret != 0) {
+ ALOGE("%s: failed to set Vsync thread name: %s", __FUNCTION__,
+ strerror(ret));
+ }
+
+ struct sched_param param = {
+ .sched_priority = ANDROID_PRIORITY_DISPLAY,
+ };
+ ret = pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m);
+ if (ret != 0) {
+ ALOGE("%s: failed to set Vsync thread priority: %s", __FUNCTION__,
+ strerror(ret));
+ }
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::stop() {
+ mShuttingDown.store(true);
+ mThread.join();
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::setCallbacks(
+ const std::shared_ptr<IComposerCallback>& callback) {
+ DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ mCallbacks = callback;
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::setVsyncEnabled(bool enabled) {
+ DEBUG_LOG("%s for display:%" PRIu64 " enabled:%d", __FUNCTION__, mDisplayId,
+ enabled);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ mVsyncEnabled = enabled;
+
+ return HWC3::Error::None;
+}
+
+HWC3::Error VsyncThread::scheduleVsyncUpdate(
+ int32_t newVsyncPeriod, const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
+ DEBUG_LOG("%s for display:%" PRIu64, __FUNCTION__, mDisplayId);
+
+ PendingUpdate update;
+ update.period = Nanoseconds(newVsyncPeriod);
+ update.updateAfter = asTimePoint(constraints.desiredTimeNanos);
+
+ std::unique_lock<std::mutex> lock(mStateMutex);
+ mPendingUpdate.emplace(std::move(update));
+
+ TimePoint nextVsync =
+ GetNextVsyncInPhase(mVsyncPeriod, mPreviousVsync, update.updateAfter);
+
+ outTimeline->newVsyncAppliedTimeNanos = asNanosTimePoint(nextVsync);
+ outTimeline->refreshRequired = false;
+ outTimeline->refreshTimeNanos = 0;
+
+ return HWC3::Error::None;
+}
+
+Nanoseconds VsyncThread::updateVsyncPeriodLocked(TimePoint now) {
+ if (mPendingUpdate && now > mPendingUpdate->updateAfter) {
+ mVsyncPeriod = mPendingUpdate->period;
+ mPendingUpdate.reset();
+ }
+
+ return mVsyncPeriod;
+}
+
+void VsyncThread::threadLoop() {
+ ALOGI("Vsync thread for display:%" PRId64 " starting", mDisplayId);
+
+ Nanoseconds vsyncPeriod = mVsyncPeriod;
+
+ int vsyncs = 0;
+ TimePoint previousLog = std::chrono::steady_clock::now();
+
+ while (!mShuttingDown.load()) {
+ TimePoint now = std::chrono::steady_clock::now();
+ TimePoint nextVsync = GetNextVsyncInPhase(vsyncPeriod, mPreviousVsync, now);
+
+ std::this_thread::sleep_until(nextVsync);
+ {
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ mPreviousVsync = nextVsync;
+
+ // Display has finished refreshing at previous vsync period. Update the
+ // vsync period if there was a pending update.
+ vsyncPeriod = updateVsyncPeriodLocked(mPreviousVsync);
+ }
+
+ if (mVsyncEnabled) {
+ if (mCallbacks) {
+ DEBUG_LOG("%s: for display:%" PRIu64 " calling vsync", __FUNCTION__,
+ mDisplayId);
+ mCallbacks->onVsync(mDisplayId, asNanosTimePoint(nextVsync),
+ asNanosDuration(vsyncPeriod));
+ }
+ }
+
+ static constexpr const int kLogIntervalSeconds = 60;
+ if (now > (previousLog + std::chrono::seconds(kLogIntervalSeconds))) {
+ DEBUG_LOG("%s: for display:%" PRIu64 " send %" PRIu32
+ " in last %d seconds",
+ __FUNCTION__, mDisplayId, vsyncs, kLogIntervalSeconds);
+ previousLog = now;
+ vsyncs = 0;
+ }
+ ++vsyncs;
+ }
+
+ ALOGI("Vsync thread for display:%" PRId64 " finished", mDisplayId);
+}
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/system/hwc3/VsyncThread.h b/system/hwc3/VsyncThread.h
new file mode 100644
index 0000000..5738b5d
--- /dev/null
+++ b/system/hwc3/VsyncThread.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_HWC_VSYNCTHREAD_H
+#define ANDROID_HWC_VSYNCTHREAD_H
+
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeConstraints.h>
+#include <aidl/android/hardware/graphics/composer3/VsyncPeriodChangeTimeline.h>
+#include <android/hardware/graphics/common/1.0/types.h>
+
+#include <chrono>
+#include <mutex>
+#include <optional>
+#include <thread>
+
+#include "Common.h"
+
+namespace aidl::android::hardware::graphics::composer3::impl {
+
+// Generates Vsync signals in software.
+class VsyncThread {
+ public:
+ VsyncThread(int64_t id);
+ virtual ~VsyncThread();
+
+ VsyncThread(const VsyncThread&) = default;
+ VsyncThread& operator=(const VsyncThread&) = default;
+
+ VsyncThread(VsyncThread&&) = default;
+ VsyncThread& operator=(VsyncThread&&) = default;
+
+ HWC3::Error start(int32_t periodNanos);
+
+ HWC3::Error setCallbacks(const std::shared_ptr<IComposerCallback>& callback);
+
+ HWC3::Error setVsyncEnabled(bool enabled);
+
+ HWC3::Error scheduleVsyncUpdate(
+ int32_t newVsyncPeriod,
+ const VsyncPeriodChangeConstraints& newVsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* timeline);
+
+ private:
+ HWC3::Error stop();
+
+ void threadLoop();
+
+ std::chrono::nanoseconds updateVsyncPeriodLocked(
+ std::chrono::time_point<std::chrono::steady_clock> now);
+
+ const int64_t mDisplayId;
+
+ std::thread mThread;
+
+ std::mutex mStateMutex;
+
+ std::atomic<bool> mShuttingDown{false};
+
+ std::shared_ptr<IComposerCallback> mCallbacks;
+
+ bool mVsyncEnabled = false;
+ std::chrono::nanoseconds mVsyncPeriod;
+ std::chrono::time_point<std::chrono::steady_clock> mPreviousVsync;
+
+ struct PendingUpdate {
+ std::chrono::nanoseconds period;
+ std::chrono::time_point<std::chrono::steady_clock> updateAfter;
+ };
+ std::optional<PendingUpdate> mPendingUpdate;
+};
+
+} // namespace aidl::android::hardware::graphics::composer3::impl
+
+#endif
\ No newline at end of file
diff --git a/system/hwc3/hwc3.rc b/system/hwc3/hwc3.rc
new file mode 100644
index 0000000..ebb2948
--- /dev/null
+++ b/system/hwc3/hwc3.rc
@@ -0,0 +1,7 @@
+service vendor.hwcomposer-3 /vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu
+ class hal animation
+ user system
+ group graphics drmrpc
+ capabilities SYS_NICE
+ onrestart restart surfaceflinger
+ task_profiles ServiceCapacityLow
\ No newline at end of file
diff --git a/system/hwc3/hwc3.xml b/system/hwc3/hwc3.xml
new file mode 100644
index 0000000..79d6a8d
--- /dev/null
+++ b/system/hwc3/hwc3.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.graphics.composer3</name>
+ <version>1</version>
+ <interface>
+ <name>IComposer</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
\ No newline at end of file
diff --git a/system/hwc3/virtgpu_drm.h b/system/hwc3/virtgpu_drm.h
new file mode 100644
index 0000000..a13e20c
--- /dev/null
+++ b/system/hwc3/virtgpu_drm.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2013 Red Hat
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef VIRTGPU_DRM_H
+#define VIRTGPU_DRM_H
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints.
+ *
+ * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel
+ * compatibility Keep fields aligned to their size
+ */
+
+#define DRM_VIRTGPU_MAP 0x01
+#define DRM_VIRTGPU_EXECBUFFER 0x02
+#define DRM_VIRTGPU_GETPARAM 0x03
+#define DRM_VIRTGPU_RESOURCE_CREATE 0x04
+#define DRM_VIRTGPU_RESOURCE_INFO 0x05
+#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x06
+#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07
+#define DRM_VIRTGPU_WAIT 0x08
+#define DRM_VIRTGPU_GET_CAPS 0x09
+#define DRM_VIRTGPU_RESOURCE_CREATE_BLOB 0x0a
+#define DRM_VIRTGPU_CONTEXT_INIT 0x0b
+
+#define VIRTGPU_EXECBUF_FENCE_FD_IN 0x01
+#define VIRTGPU_EXECBUF_FENCE_FD_OUT 0x02
+#define VIRTGPU_EXECBUF_RING_IDX 0x04
+#define VIRTGPU_EXECBUF_FLAGS (\
+ VIRTGPU_EXECBUF_FENCE_FD_IN |\
+ VIRTGPU_EXECBUF_FENCE_FD_OUT |\
+ VIRTGPU_EXECBUF_RING_IDX |\
+ 0)
+
+struct drm_virtgpu_map {
+ __u64 offset; /* use for mmap system call */
+ __u32 handle;
+ __u32 pad;
+};
+
+struct drm_virtgpu_execbuffer {
+ __u32 flags;
+ __u32 size;
+ __u64 command; /* void* */
+ __u64 bo_handles;
+ __u32 num_bo_handles;
+ __s32 fence_fd; /* in/out fence fd (see VIRTGPU_EXECBUF_FENCE_FD_IN/OUT) */
+ __u32 ring_idx; /* command ring index (see VIRTGPU_EXECBUF_RING_IDX) */
+ __u32 pad;
+};
+
+#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
+#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */
+#define VIRTGPU_PARAM_RESOURCE_BLOB 3 /* DRM_VIRTGPU_RESOURCE_CREATE_BLOB */
+#define VIRTGPU_PARAM_HOST_VISIBLE 4 /* Host blob resources are mappable */
+#define VIRTGPU_PARAM_CROSS_DEVICE 5 /* Cross virtio-device resource sharing */
+#define VIRTGPU_PARAM_CONTEXT_INIT 6 /* DRM_VIRTGPU_CONTEXT_INIT */
+#define VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs 7 /* Bitmask of supported capability set ids */
+
+struct drm_virtgpu_getparam {
+ __u64 param;
+ __u64 value;
+};
+
+/* NO_BO flags? NO resource flag? */
+/* resource flag for y_0_top */
+struct drm_virtgpu_resource_create {
+ __u32 target;
+ __u32 format;
+ __u32 bind;
+ __u32 width;
+ __u32 height;
+ __u32 depth;
+ __u32 array_size;
+ __u32 last_level;
+ __u32 nr_samples;
+ __u32 flags;
+ __u32 bo_handle; /* if this is set - recreate a new resource attached to this bo ? */
+ __u32 res_handle; /* returned by kernel */
+ __u32 size; /* validate transfer in the host */
+ __u32 stride; /* validate transfer in the host */
+};
+
+struct drm_virtgpu_resource_info {
+ __u32 bo_handle;
+ __u32 res_handle;
+ __u32 size;
+ __u32 blob_mem;
+};
+
+struct drm_virtgpu_3d_box {
+ __u32 x;
+ __u32 y;
+ __u32 z;
+ __u32 w;
+ __u32 h;
+ __u32 d;
+};
+
+struct drm_virtgpu_3d_transfer_to_host {
+ __u32 bo_handle;
+ struct drm_virtgpu_3d_box box;
+ __u32 level;
+ __u32 offset;
+ __u32 stride;
+ __u32 layer_stride;
+};
+
+struct drm_virtgpu_3d_transfer_from_host {
+ __u32 bo_handle;
+ struct drm_virtgpu_3d_box box;
+ __u32 level;
+ __u32 offset;
+ __u32 stride;
+ __u32 layer_stride;
+};
+
+#define VIRTGPU_WAIT_NOWAIT 1 /* like it */
+struct drm_virtgpu_3d_wait {
+ __u32 handle; /* 0 is an invalid handle */
+ __u32 flags;
+};
+
+struct drm_virtgpu_get_caps {
+ __u32 cap_set_id;
+ __u32 cap_set_ver;
+ __u64 addr;
+ __u32 size;
+ __u32 pad;
+};
+
+struct drm_virtgpu_resource_create_blob {
+#define VIRTGPU_BLOB_MEM_GUEST 0x0001
+#define VIRTGPU_BLOB_MEM_HOST3D 0x0002
+#define VIRTGPU_BLOB_MEM_HOST3D_GUEST 0x0003
+
+#define VIRTGPU_BLOB_FLAG_USE_MAPPABLE 0x0001
+#define VIRTGPU_BLOB_FLAG_USE_SHAREABLE 0x0002
+#define VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
+ /* zero is invalid blob_mem */
+ __u32 blob_mem;
+ __u32 blob_flags;
+ __u32 bo_handle;
+ __u32 res_handle;
+ __u64 size;
+
+ /*
+ * for 3D contexts with VIRTGPU_BLOB_MEM_HOST3D_GUEST and
+ * VIRTGPU_BLOB_MEM_HOST3D otherwise, must be zero.
+ */
+ __u32 pad;
+ __u32 cmd_size;
+ __u64 cmd;
+ __u64 blob_id;
+};
+
+#define VIRTGPU_CONTEXT_PARAM_CAPSET_ID 0x0001
+#define VIRTGPU_CONTEXT_PARAM_NUM_RINGS 0x0002
+#define VIRTGPU_CONTEXT_PARAM_POLL_RINGS_MASK 0x0003
+struct drm_virtgpu_context_set_param {
+ __u64 param;
+ __u64 value;
+};
+
+struct drm_virtgpu_context_init {
+ __u32 num_params;
+ __u32 pad;
+
+ /* pointer to drm_virtgpu_context_set_param array */
+ __u64 ctx_set_params;
+};
+
+#define DRM_IOCTL_VIRTGPU_MAP \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map)
+
+#define DRM_IOCTL_VIRTGPU_EXECBUFFER \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_EXECBUFFER,\
+ struct drm_virtgpu_execbuffer)
+
+#define DRM_IOCTL_VIRTGPU_GETPARAM \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GETPARAM,\
+ struct drm_virtgpu_getparam)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE, \
+ struct drm_virtgpu_resource_create)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_INFO \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_INFO, \
+ struct drm_virtgpu_resource_info)
+
+#define DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_FROM_HOST, \
+ struct drm_virtgpu_3d_transfer_from_host)
+
+#define DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_TO_HOST, \
+ struct drm_virtgpu_3d_transfer_to_host)
+
+#define DRM_IOCTL_VIRTGPU_WAIT \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT, \
+ struct drm_virtgpu_3d_wait)
+
+#define DRM_IOCTL_VIRTGPU_GET_CAPS \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \
+ struct drm_virtgpu_get_caps)
+
+#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE_BLOB, \
+ struct drm_virtgpu_resource_create_blob)
+
+#define DRM_IOCTL_VIRTGPU_CONTEXT_INIT \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_CONTEXT_INIT, \
+ struct drm_virtgpu_context_init)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif