Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9885f1f authored by John Reck's avatar John Reck Committed by Gerrit Code Review
Browse files

Merge "Fix PixelCopy & BQ crop"

parents f334f4ed 4464e463
Loading
Loading
Loading
Loading
+177 −1
Original line number Diff line number Diff line
@@ -35,11 +35,187 @@ using namespace android::uirenderer::renderthread;
namespace android {
namespace uirenderer {

CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap) {
// Deleter for an AHardwareBuffer, to be passed to an std::unique_ptr.
struct AHardwareBuffer_deleter {
    void operator()(AHardwareBuffer* ahb) const { AHardwareBuffer_release(ahb); }
};

using UniqueAHardwareBuffer = std::unique_ptr<AHardwareBuffer, AHardwareBuffer_deleter>;

#define ARECT_ARGS(r) float((r).left), float((r).top), float((r).right), float((r).bottom)

CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRect,
                                     SkBitmap* bitmap) {
    ATRACE_CALL();
    // Setup the source
    AHardwareBuffer* rawSourceBuffer;
    int rawSourceFence;
    ARect cropRect;
    uint32_t windowTransform;
    status_t err = ANativeWindow_getLastQueuedBuffer2(window, &rawSourceBuffer, &rawSourceFence,
                                                      &cropRect, &windowTransform);
    base::unique_fd sourceFence(rawSourceFence);
    // Really this shouldn't ever happen, but better safe than sorry.
    if (err == UNKNOWN_TRANSACTION) {
        ALOGW("Readback failed to ANativeWindow_getLastQueuedBuffer2 - who are we talking to?");
        return copySurfaceIntoLegacy(window, inSrcRect, bitmap);
    }
    ALOGV("Using new path, cropRect=" RECT_STRING ", transform=%x", ARECT_ARGS(cropRect),
          windowTransform);

    if (err != NO_ERROR) {
        ALOGW("Failed to get last queued buffer, error = %d", err);
        return CopyResult::UnknownError;
    }
    if (rawSourceBuffer == nullptr) {
        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
        return CopyResult::SourceEmpty;
    }
    UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
    AHardwareBuffer_Desc description;
    AHardwareBuffer_describe(sourceBuffer.get(), &description);
    if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
        ALOGW("Surface is protected, unable to copy from it");
        return CopyResult::SourceInvalid;
    }

    if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
        return CopyResult::Timeout;
    }

    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
            static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
    sk_sp<SkImage> image =
            SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);

    if (!image.get()) {
        return CopyResult::UnknownError;
    }

    sk_sp<GrContext> grContext = mRenderThread.requireGrContext();

    SkRect srcRect = inSrcRect.toSkRect();

    SkRect imageSrcRect =
            SkRect::MakeLTRB(cropRect.left, cropRect.top, cropRect.right, cropRect.bottom);
    if (imageSrcRect.isEmpty()) {
        imageSrcRect = SkRect::MakeIWH(description.width, description.height);
    }
    ALOGV("imageSrcRect = " RECT_STRING, SK_RECT_ARGS(imageSrcRect));

    // Represents the "logical" width/height of the texture. That is, the dimensions of the buffer
    // after respecting crop & rotate. flipV/flipH still result in the same width & height
    // so we can ignore those for this.
    const SkRect textureRect =
            (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
                    ? SkRect::MakeIWH(imageSrcRect.height(), imageSrcRect.width())
                    : SkRect::MakeIWH(imageSrcRect.width(), imageSrcRect.height());

    if (srcRect.isEmpty()) {
        srcRect = textureRect;
    } else {
        ALOGV("intersecting " RECT_STRING " with " RECT_STRING, SK_RECT_ARGS(srcRect),
              SK_RECT_ARGS(textureRect));
        if (!srcRect.intersect(textureRect)) {
            return CopyResult::UnknownError;
        }
    }

    sk_sp<SkSurface> tmpSurface =
            SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
                                        bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);

    // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
    // attempt to do the intermediate rendering step in 8888
    if (!tmpSurface.get()) {
        SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
        tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
                                                 tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
        if (!tmpSurface.get()) {
            ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
            return CopyResult::UnknownError;
        }
    }

    /*
     * The grand ordering of events.
     * First we apply the buffer's crop, done by using a srcRect of the crop with a dstRect of the
     * same width/height as the srcRect but with a 0x0 origin
     *
     * Second we apply the window transform via a Canvas matrix. Ordering for that is as follows:
     *  1) FLIP_H
     *  2) FLIP_V
     *  3) ROT_90
     * as per GLConsumer::computeTransformMatrix
     *
     * Third we apply the user's supplied cropping & scale to the output by doing a RectToRect
     * matrix transform from srcRect to {0,0, bitmapWidth, bitmapHeight}
     *
     * Finally we're done messing with this bloody thing for hopefully the last time.
     *
     * That's a lie since...
     * TODO: Do all this same stuff for TextureView as it's strictly more correct & easier
     * to rationalize. And we can fix the 1-px crop bug.
     */

    SkMatrix m;
    const SkRect imageDstRect = SkRect::MakeIWH(imageSrcRect.width(), imageSrcRect.height());
    const float px = imageDstRect.centerX();
    const float py = imageDstRect.centerY();
    if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
        m.postScale(-1.f, 1.f, px, py);
    }
    if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
        m.postScale(1.f, -1.f, px, py);
    }
    if (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
        m.postRotate(90, 0, 0);
        m.postTranslate(imageDstRect.height(), 0);
    }

    ALOGV("Mapping from " RECT_STRING " to " RECT_STRING, SK_RECT_ARGS(srcRect),
          SK_RECT_ARGS(SkRect::MakeWH(bitmap->width(), bitmap->height())));
    m.postConcat(SkMatrix::MakeRectToRect(srcRect,
                                          SkRect::MakeWH(bitmap->width(), bitmap->height()),
                                          SkMatrix::kFill_ScaleToFit));

    SkCanvas* canvas = tmpSurface->getCanvas();
    canvas->save();
    canvas->concat(m);
    SkPaint paint;
    paint.setAlpha(255);
    paint.setBlendMode(SkBlendMode::kSrc);
    if (srcRect.width() != bitmap->width() || srcRect.height() != bitmap->height()) {
        paint.setFilterQuality(kLow_SkFilterQuality);
    }
    canvas->drawImageRect(image, imageSrcRect, imageDstRect, &paint,
                          SkCanvas::kFast_SrcRectConstraint);
    canvas->restore();

    if (!tmpSurface->readPixels(*bitmap, 0, 0)) {
        // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
        // 8888 and then convert that into the destination format before giving up.
        SkBitmap tmpBitmap;
        SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
        if (bitmap->info().colorType() == SkColorType::kN32_SkColorType ||
            !tmpBitmap.tryAllocPixels(tmpInfo) || !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
            !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
            ALOGW("Unable to convert content into the provided bitmap");
            return CopyResult::UnknownError;
        }
    }

    bitmap->notifyPixelsChanged();

    return CopyResult::Success;
}

CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect,
                                           SkBitmap* bitmap) {
    // Setup the source
    AHardwareBuffer* rawSourceBuffer;
    int rawSourceFence;
    Matrix4 texTransform;
    status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
                                                     texTransform.data);
+1 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ public:
    CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);

private:
    CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
    CopyResult copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
                             const Rect& srcRect, SkBitmap* bitmap);

+9 −0
Original line number Diff line number Diff line
@@ -275,6 +275,15 @@ void RenderThread::setGrContext(sk_sp<GrContext> context) {
    }
}

sk_sp<GrContext> RenderThread::requireGrContext() {
    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
        requireGlContext();
    } else {
        requireVkContext();
    }
    return mGrContext;
}

int RenderThread::choreographerCallback(int fd, int events, void* data) {
    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
        ALOGE("Display event receiver pipe was closed or an error occurred.  "
+1 −0
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@ public:

    GrContext* getGrContext() const { return mGrContext.get(); }
    void setGrContext(sk_sp<GrContext> cxt);
    sk_sp<GrContext> requireGrContext();

    CacheManager& cacheManager() { return *mCacheManager; }
    VulkanManager& vulkanManager() { return *mVkManager; }