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

Commit 5cc9ac0a authored by Vishnu Nair's avatar Vishnu Nair
Browse files

Fix crop scaling with BSL

With BQL, each layer had a concept of buffer space and layer space.
The caller could set a buffer crop and a layer crop. The buffer crop
would be applied in buffer space and the content would be scaled
to the buffer size. Then the scaled content would then be scaled
again to the layer size (also referred to as the window size).

With BSL we do not have a concept of buffer space and layer space.
So we should not set a buffer/content crop otherwise this might
result in incorrect scaling or cropping. To fix this, in BBQ, when
calculating the scale, use the buffer crop provided by the client
and in SurfaceFlinger, instead of setting a buffer crop only set
the layer crop. The buffer crop/content crop can be cleaned up
once BQL is removed.

Test: BLASTBufferQueueTest (cropped buffer scales to buffer and window size)
Test: 720p content in android tv is not cropped incorrectly
Bug: 178622186
Change-Id: I173df901120a43f397f6d623a7e7b93537a508e2
parent a3a235f5
Loading
Loading
Loading
Loading
+11 −7
Original line number Diff line number Diff line
@@ -186,6 +186,7 @@ BLASTBufferQueue::~BLASTBufferQueue() {
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
                              int32_t format) {
    std::unique_lock _lock{mMutex};
    BQA_LOGV("update width=%d height=%d format=%d", width, height, format);
    if (mFormat != format) {
        mFormat = format;
        mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
@@ -377,10 +378,11 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
    // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
    incStrong((void*)transactionCallbackThunk);

    Rect crop = computeCrop(bufferItem);
    mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
    mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
                           bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
                           bufferItem.mScalingMode);
                           bufferItem.mScalingMode, crop);

    auto releaseBufferCallback =
            std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
@@ -394,7 +396,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
    t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));

    setMatrix(t, mLastBufferInfo);
    t->setCrop(mSurfaceControl, computeCrop(bufferItem));
    t->setCrop(mSurfaceControl, crop);
    t->setTransform(mSurfaceControl, bufferItem.mTransform);
    t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
    if (!bufferItem.mIsAutoTimestamp) {
@@ -522,13 +524,15 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {

void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
                                 const BufferInfo& bufferInfo) {
    uint32_t bufWidth = bufferInfo.width;
    uint32_t bufHeight = bufferInfo.height;
    uint32_t bufWidth = bufferInfo.crop.getWidth();
    uint32_t bufHeight = bufferInfo.crop.getHeight();

    float dsdx = mSize.width / static_cast<float>(bufWidth);
    float dsdy = mSize.height / static_cast<float>(bufHeight);
    float sx = mSize.width / static_cast<float>(bufWidth);
    float sy = mSize.height / static_cast<float>(bufHeight);

    t->setMatrix(mSurfaceControl, dsdx, 0, 0, dsdy);
    t->setMatrix(mSurfaceControl, sx, 0, 0, sy);
    // Update position based on crop.
    t->setPosition(mSurfaceControl, bufferInfo.crop.left * sx * -1, bufferInfo.crop.top * sy * -1);
}

void BLASTBufferQueue::setTransactionCompleteCallback(
+7 −1
Original line number Diff line number Diff line
@@ -151,14 +151,20 @@ private:
        // we get the next buffer. This will support scenarios where the layer can change sizes
        // and the buffer will scale to fit the new size.
        uint32_t scalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
        Rect crop;

        void update(bool hasBuffer, uint32_t width, uint32_t height, uint32_t transform,
                    uint32_t scalingMode) {
                    uint32_t scalingMode, const Rect& crop) {
            this->hasBuffer = hasBuffer;
            this->width = width;
            this->height = height;
            this->transform = transform;
            this->scalingMode = scalingMode;
            if (!crop.isEmpty()) {
                this->crop = crop;
            } else {
                this->crop = Rect(width, height);
            }
        }
    };

+138 −8
Original line number Diff line number Diff line
@@ -522,16 +522,146 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
    adapter.waitForCallbacks();
    // capture screen and verify that it is red
    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
                                               {10, 10, (int32_t)bufferSideLength - 10,
                                                (int32_t)bufferSideLength - 10}));
    ASSERT_NO_FATAL_FAILURE(
            checkScreenCapture(0, 0, 0,
                               {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
                               /*border*/ 0, /*outsideRegion*/ true));
}

    Rect bounds;
    bounds.left = finalCropSideLength / 2;
    bounds.top = 0;
    bounds.right = bounds.left + finalCropSideLength;
    bounds.bottom = finalCropSideLength;
TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
    // add black background
    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
                                     ISurfaceComposerClient::eFXSurfaceEffect);
    ASSERT_NE(nullptr, bg.get());
    Transaction t;
    t.setLayerStack(bg, 0)
            .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
            .setColor(bg, half3{0, 0, 0})
            .setLayer(bg, 0)
            .apply();

    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b, bounds));
    ASSERT_NO_FATAL_FAILURE(
            checkScreenCapture(0, 0, 0, bounds, /*border*/ 0, /*outsideRegion*/ true));
    Rect windowSize(1000, 1000);
    Rect bufferSize(windowSize);
    Rect bufferCrop(200, 200, 700, 700);

    BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
    sp<IGraphicBufferProducer> igbProducer;
    setUpProducer(adapter, igbProducer);
    int slot;
    sp<Fence> fence;
    sp<GraphicBuffer> buf;
    auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
                                          bufferSize.getHeight(), PIXEL_FORMAT_RGBA_8888,
                                          GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));

    uint32_t* bufData;
    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
              reinterpret_cast<void**>(&bufData));
    // fill buffer with grey
    fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);

    // fill crop area with different colors so we can verify the cropped region has been scaled
    // correctly.
    fillBuffer(bufData, Rect(200, 200, 450, 450), buf->getStride(), /* rgb */ 255, 0, 0);
    fillBuffer(bufData, Rect(200, 451, 450, 700), buf->getStride(), /* rgb */ 0, 255, 0);
    fillBuffer(bufData, Rect(451, 200, 700, 450), buf->getStride(), /* rgb */ 0, 0, 255);
    fillBuffer(bufData, Rect(451, 451, 700, 700), buf->getStride(), /* rgb */ 255, 0, 0);
    buf->unlock();

    IGraphicBufferProducer::QueueBufferOutput qbOutput;
    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
                                                   HAL_DATASPACE_UNKNOWN,
                                                   bufferCrop /* Rect::INVALID_RECT */,
                                                   NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
                                                   Fence::NO_FENCE);
    igbProducer->queueBuffer(slot, input, &qbOutput);
    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);

    adapter.waitForCallbacks();

    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));

    // Verify cropped region is scaled correctly.
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
    // Verify outside region is black.
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
                                               {0, 0, (int32_t)windowSize.getWidth(),
                                                (int32_t)windowSize.getHeight()},
                                               /*border*/ 0, /*outsideRegion*/ true));
}

TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
    // add black background
    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
                                     ISurfaceComposerClient::eFXSurfaceEffect);
    ASSERT_NE(nullptr, bg.get());
    Transaction t;
    t.setLayerStack(bg, 0)
            .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
            .setColor(bg, half3{0, 0, 0})
            .setLayer(bg, 0)
            .apply();

    Rect windowSize(1000, 1000);
    Rect bufferSize(500, 500);
    Rect bufferCrop(100, 100, 350, 350);

    BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
    sp<IGraphicBufferProducer> igbProducer;
    setUpProducer(adapter, igbProducer);
    int slot;
    sp<Fence> fence;
    sp<GraphicBuffer> buf;
    auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
                                          bufferSize.getHeight(), PIXEL_FORMAT_RGBA_8888,
                                          GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr);
    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));

    uint32_t* bufData;
    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
              reinterpret_cast<void**>(&bufData));
    // fill buffer with grey
    fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);

    // fill crop area with different colors so we can verify the cropped region has been scaled
    // correctly.
    fillBuffer(bufData, Rect(100, 100, 225, 225), buf->getStride(), /* rgb */ 255, 0, 0);
    fillBuffer(bufData, Rect(100, 226, 225, 350), buf->getStride(), /* rgb */ 0, 255, 0);
    fillBuffer(bufData, Rect(226, 100, 350, 225), buf->getStride(), /* rgb */ 0, 0, 255);
    fillBuffer(bufData, Rect(226, 226, 350, 350), buf->getStride(), /* rgb */ 255, 0, 0);
    buf->unlock();

    IGraphicBufferProducer::QueueBufferOutput qbOutput;
    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
                                                   HAL_DATASPACE_UNKNOWN,
                                                   bufferCrop /* Rect::INVALID_RECT */,
                                                   NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
                                                   Fence::NO_FENCE);
    igbProducer->queueBuffer(slot, input, &qbOutput);
    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);

    adapter.waitForCallbacks();

    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
    // Verify cropped region is scaled correctly.
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
    // Verify outside region is black.
    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
                                               {0, 0, (int32_t)windowSize.getWidth(),
                                                (int32_t)windowSize.getHeight()},
                                               /*border*/ 0, /*outsideRegion*/ true));
}

class TestProducerListener : public BnProducerListener {
+4 −20
Original line number Diff line number Diff line
@@ -790,7 +790,7 @@ void BufferStateLayer::gatherBufferInfo() {
    mBufferInfo.mFence = s.acquireFence;
    mBufferInfo.mTransform = s.bufferTransform;
    mBufferInfo.mDataspace = translateDataspace(s.dataspace);
    mBufferInfo.mCrop = computeCrop(s);
    mBufferInfo.mCrop = computeBufferCrop(s);
    mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
    mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
    mBufferInfo.mHdrMetadata = s.hdrMetadata;
@@ -803,27 +803,11 @@ uint32_t BufferStateLayer::getEffectiveScalingMode() const {
   return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
}

Rect BufferStateLayer::computeCrop(const State& s) {
    if (s.crop.isEmpty() && s.buffer) {
Rect BufferStateLayer::computeBufferCrop(const State& s) {
    if (s.buffer) {
        return s.buffer->getBuffer()->getBounds();
    } else if (s.buffer) {
        Rect crop = s.crop;
        crop.left = std::max(crop.left, 0);
        crop.top = std::max(crop.top, 0);
        uint32_t bufferWidth = s.buffer->getBuffer()->getWidth();
        uint32_t bufferHeight = s.buffer->getBuffer()->getHeight();
        if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
            bufferWidth <= std::numeric_limits<int32_t>::max()) {
            crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
            crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
        }
        if (!crop.isValid()) {
            // Crop rect is out of bounds, return whole buffer
            return s.buffer->getBuffer()->getBounds();
        }
        return crop;
    }
    return s.crop;
    return Rect::INVALID_RECT;
}

sp<Layer> BufferStateLayer::createClone() {
+1 −1
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ private:
    sp<Layer> createClone() override;

    // Crop that applies to the buffer
    Rect computeCrop(const State& s);
    Rect computeBufferCrop(const State& s);

    bool willPresentCurrentTransaction() const;