diff options
| -rw-r--r-- | libs/ultrahdr/include/ultrahdr/ultrahdr.h | 4 | ||||
| -rw-r--r-- | libs/ultrahdr/jpegencoderhelper.cpp | 67 | ||||
| -rw-r--r-- | libs/ultrahdr/jpegr.cpp | 46 | ||||
| -rw-r--r-- | libs/ultrahdr/tests/jpegencoderhelper_test.cpp | 11 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 7 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 2 | ||||
| -rw-r--r-- | services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp | 5 | ||||
| -rw-r--r-- | services/surfaceflinger/DisplayHardware/PowerAdvisor.h | 3 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 5 |
9 files changed, 117 insertions, 33 deletions
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h index d6153e9890..21751b4634 100644 --- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h +++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h @@ -20,7 +20,7 @@ namespace android::ultrahdr { // Color gamuts for image data typedef enum { - ULTRAHDR_COLORGAMUT_UNSPECIFIED, + ULTRAHDR_COLORGAMUT_UNSPECIFIED = -1, ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100, @@ -52,7 +52,7 @@ typedef enum { */ struct ultrahdr_metadata_struct { // Ultra HDR library version - const char* version; + std::string version; // Max Content Boost for the map float maxContentBoost; // Min Content Boost for the map diff --git a/libs/ultrahdr/jpegencoderhelper.cpp b/libs/ultrahdr/jpegencoderhelper.cpp index 10a763035f..ab2f8c7b5a 100644 --- a/libs/ultrahdr/jpegencoderhelper.cpp +++ b/libs/ultrahdr/jpegencoderhelper.cpp @@ -22,6 +22,8 @@ namespace android::ultrahdr { +#define ALIGNM(x, m) ((((x) + ((m) - 1)) / (m)) * (m)) + // The destination manager that can access |mResultBuffer| in JpegEncoderHelper. struct destination_mgr { public: @@ -175,6 +177,37 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]); memset(empty.get(), 0, cinfo->image_width); + const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize); + const bool is_width_aligned = (aligned_width == cinfo->image_width); + std::unique_ptr<uint8_t[]> buffer_intrm = nullptr; + uint8_t* y_plane_intrm = nullptr; + uint8_t* u_plane_intrm = nullptr; + uint8_t* v_plane_intrm = nullptr; + JSAMPROW y_intrm[kCompressBatchSize]; + JSAMPROW cb_intrm[kCompressBatchSize / 2]; + JSAMPROW cr_intrm[kCompressBatchSize / 2]; + JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm}; + if (!is_width_aligned) { + size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2; + buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size); + y_plane_intrm = buffer_intrm.get(); + u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize); + v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4; + for (int i = 0; i < kCompressBatchSize; ++i) { + y_intrm[i] = y_plane_intrm + i * aligned_width; + memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width); + } + for (int i = 0; i < kCompressBatchSize / 2; ++i) { + int offset_intrm = i * (aligned_width / 2); + cb_intrm[i] = u_plane_intrm + offset_intrm; + cr_intrm[i] = v_plane_intrm + offset_intrm; + memset(cb_intrm[i] + cinfo->image_width / 2, 0, + (aligned_width - cinfo->image_width) / 2); + memset(cr_intrm[i] + cinfo->image_width / 2, 0, + (aligned_width - cinfo->image_width) / 2); + } + } + while (cinfo->next_scanline < cinfo->image_height) { for (int i = 0; i < kCompressBatchSize; ++i) { size_t scanline = cinfo->next_scanline + i; @@ -183,6 +216,9 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* } else { y[i] = empty.get(); } + if (!is_width_aligned) { + memcpy(y_intrm[i], y[i], cinfo->image_width); + } } // cb, cr only have half scanlines for (int i = 0; i < kCompressBatchSize / 2; ++i) { @@ -194,9 +230,13 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* } else { cb[i] = cr[i] = empty.get(); } + if (!is_width_aligned) { + memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2); + memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2); + } } - - int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize); + int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm, + kCompressBatchSize); if (processed != kCompressBatchSize) { ALOGE("Number of processed lines does not equal input lines."); return false; @@ -213,6 +253,23 @@ bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]); memset(empty.get(), 0, cinfo->image_width); + const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize); + bool is_width_aligned = (aligned_width == cinfo->image_width); + std::unique_ptr<uint8_t[]> buffer_intrm = nullptr; + uint8_t* y_plane_intrm = nullptr; + uint8_t* u_plane_intrm = nullptr; + JSAMPROW y_intrm[kCompressBatchSize]; + JSAMPARRAY planes_intrm[]{y_intrm}; + if (!is_width_aligned) { + size_t mcu_row_size = aligned_width * kCompressBatchSize; + buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size); + y_plane_intrm = buffer_intrm.get(); + for (int i = 0; i < kCompressBatchSize; ++i) { + y_intrm[i] = y_plane_intrm + i * aligned_width; + memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width); + } + } + while (cinfo->next_scanline < cinfo->image_height) { for (int i = 0; i < kCompressBatchSize; ++i) { size_t scanline = cinfo->next_scanline + i; @@ -221,8 +278,12 @@ bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const } else { y[i] = empty.get(); } + if (!is_width_aligned) { + memcpy(y_intrm[i], y[i], cinfo->image_width); + } } - int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize); + int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm, + kCompressBatchSize); if (processed != kCompressBatchSize / 2) { ALOGE("Number of processed lines does not equal input lines."); return false; diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp index c250aa02fd..b2bde6c485 100644 --- a/libs/ultrahdr/jpegr.cpp +++ b/libs/ultrahdr/jpegr.cpp @@ -76,9 +76,9 @@ static const int kMinHeight = 2 * kMapDimensionScaleFactor; // JPEG encoding / decoding will require block based DCT transform 16 x 16 for luma, // and 8 x 8 for chroma. // Width must be 16 dividable for luma, and 8 dividable for chroma. -// If this criteria is not ficilitated, we will pad zeros based on the required block size. +// If this criteria is not facilitated, we will pad zeros based to each line on the +// required block size. static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize; -static const size_t kJpegBlockSquare = kJpegBlock * kJpegBlock; // JPEG compress quality (0 ~ 100) for gain map static const int kMapCompressQuality = 85; @@ -145,7 +145,8 @@ status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_ima return ERROR_JPEGR_INVALID_NULL_PTR; } - if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX) { + if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX + || hdr_tf == ULTRAHDR_TF_SRGB) { ALOGE("Invalid hdr transfer function %d", hdr_tf); return ERROR_JPEGR_INVALID_INPUT_TYPE; } @@ -228,13 +229,8 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, metadata.version = kJpegrVersion; jpegr_uncompressed_struct uncompressed_yuv_420_image; - size_t gain_map_length = uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2; - // Pad a pseudo chroma block (kJpegBlock / 2) x (kJpegBlock / 2) - // if width is not kJpegBlock aligned. - if (uncompressed_p010_image->width % kJpegBlock != 0) { - gain_map_length += kJpegBlockSquare / 4; - } - unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(gain_map_length); + unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>( + uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2); uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get(); JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image)); @@ -509,11 +505,6 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, return ERROR_JPEGR_INVALID_INPUT_TYPE; } - if (gain_map != nullptr && gain_map->data == nullptr) { - ALOGE("received nullptr address for gain map data"); - return ERROR_JPEGR_INVALID_INPUT_TYPE; - } - if (output_format == ULTRAHDR_OUTPUT_SDR) { JpegDecoderHelper jpeg_decoder; if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length, @@ -555,6 +546,11 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, if (!gain_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) { return ERROR_JPEGR_DECODE_ERROR; } + if ((gain_map_decoder.getDecompressedImageWidth() * + gain_map_decoder.getDecompressedImageHeight()) > + gain_map_decoder.getDecompressedImageSize()) { + return ERROR_JPEGR_CALCULATION_ERROR; + } if (gain_map != nullptr) { gain_map->width = gain_map_decoder.getDecompressedImageWidth(); @@ -584,6 +580,11 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) { return ERROR_JPEGR_DECODE_ERROR; } + if ((jpeg_decoder.getDecompressedImageWidth() * + jpeg_decoder.getDecompressedImageHeight() * 3 / 2) > + jpeg_decoder.getDecompressedImageSize()) { + return ERROR_JPEGR_CALCULATION_ERROR; + } if (exif != nullptr) { if (exif->data == nullptr) { @@ -605,7 +606,6 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr(); uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth(); uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight(); - JPEGR_CHECK(applyGainMap(&uncompressed_yuv_420_image, &map, &uhdr_metadata, output_format, max_display_boost, dest)); return NO_ERROR; @@ -848,6 +848,20 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image, return ERROR_JPEGR_INVALID_NULL_PTR; } + // TODO: remove once map scaling factor is computed based on actual map dims + size_t image_width = uncompressed_yuv_420_image->width; + size_t image_height = uncompressed_yuv_420_image->height; + size_t map_width = image_width / kMapDimensionScaleFactor; + size_t map_height = image_height / kMapDimensionScaleFactor; + map_width = static_cast<size_t>( + floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock; + map_height = ((map_height + 1) >> 1) << 1; + if (map_width != uncompressed_gain_map->width + || map_height != uncompressed_gain_map->height) { + ALOGE("gain map dimensions and primary image dimensions are not to scale"); + return ERROR_JPEGR_INVALID_INPUT_TYPE; + } + dest->width = uncompressed_yuv_420_image->width; dest->height = uncompressed_yuv_420_image->height; ShepardsIDW idwTable(kMapDimensionScaleFactor); diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp index 8f18ac0004..f0e1fa4968 100644 --- a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp +++ b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp @@ -108,18 +108,9 @@ TEST_F(JpegEncoderHelperTest, encodeAlignedImage) { ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0)); } -// The width of the "unaligned" image is not 16-aligned, and will fail if encoded directly. -// Should pass with the padding zero method. TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) { JpegEncoderHelper encoder; - const size_t paddingZeroLength = JpegEncoderHelper::kCompressBatchSize - * JpegEncoderHelper::kCompressBatchSize / 4; - std::unique_ptr<uint8_t[]> imageWithPaddingZeros( - new uint8_t[UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2 - + paddingZeroLength]); - memcpy(imageWithPaddingZeros.get(), mUnalignedImage.buffer.get(), - UNALIGNED_IMAGE_WIDTH * UNALIGNED_IMAGE_HEIGHT * 3 / 2); - EXPECT_TRUE(encoder.compressImage(imageWithPaddingZeros.get(), mUnalignedImage.width, + EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(), mUnalignedImage.width, mUnalignedImage.height, JPEG_QUALITY, NULL, 0)); ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0)); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 0cc7cfbcc8..fbbb38835a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -6700,6 +6700,13 @@ void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) for (const auto& [displayId, handles] : handlesPerDisplay) { setInputWindowsLocked(handles, displayId); } + + if (update.vsyncId < mWindowInfosVsyncId) { + ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64 + ", current update vsync id: %" PRId64, + mWindowInfosVsyncId, update.vsyncId); + } + mWindowInfosVsyncId = update.vsyncId; } // Wake up poll loop since it may need to make new input dispatching choices. mLooper->wake(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 8ca01b7a09..6b22f2f24f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -204,6 +204,8 @@ private: const IdGenerator mIdGenerator; + int64_t mWindowInfosVsyncId GUARDED_BY(mLock); + // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index 37b68c865e..f8b466c93c 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -223,7 +223,7 @@ void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) { } void PowerAdvisor::reportActualWorkDuration() { - if (!mBootFinished || !usePowerHintSession()) { + if (!mBootFinished || !sUseReportActualDuration || !usePowerHintSession()) { ALOGV("Actual work duration power hint cannot be sent, skipping"); return; } @@ -564,6 +564,9 @@ const Duration PowerAdvisor::sTargetSafetyMargin = std::chrono::microseconds( base::GetIntProperty<int64_t>("debug.sf.hint_margin_us", ticks<std::micro>(PowerAdvisor::kDefaultTargetSafetyMargin))); +const bool PowerAdvisor::sUseReportActualDuration = + base::GetBoolProperty(std::string("debug.adpf.use_report_actual_duration"), true); + power::PowerHalController& PowerAdvisor::getPowerHal() { static std::once_flag halFlag; std::call_once(halFlag, [this] { mPowerHal->init(); }); diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 7a0d4267fe..f0d3fd8518 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -269,6 +269,9 @@ private: static const Duration sTargetSafetyMargin; static constexpr const Duration kDefaultTargetSafetyMargin{1ms}; + // Whether we should send reportActualWorkDuration calls + static const bool sUseReportActualDuration; + // How long we expect hwc to run after the present call until it waits for the fence static constexpr const Duration kFenceWaitStartDelayValidated{150us}; static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us}; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2ac1db961f..79378befcc 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2485,7 +2485,10 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe mPowerAdvisor->setFrameDelay(frameDelay); mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration); - mPowerAdvisor->updateTargetWorkDuration(vsyncPeriod); + + const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get(); + const Period idealVsyncPeriod = display->getActiveMode().fps.getPeriod(); + mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod); } if (mRefreshRateOverlaySpinner) { |