// See IGraphicBufferProducer::getLastQueuedBuffer
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) override;
+ sp<Fence>* outFence, float outTransformMatrix[16]) override;
private:
// This is required by the IBinder::DeathRecipient interface
// since the previous buffer might have already been acquired.
sp<Fence> mLastQueueBufferFence;
+ Rect mLastQueuedCrop;
+ uint32_t mLastQueuedTransform;
+
// Take-a-ticket system for ensuring that onFrame* callbacks are called in
// the order that frames are queued. While the BufferQueue lock
// (mCore->mMutex) is held, a ticket is retained by the producer. After
// functions.
void getTransformMatrix(float mtx[16]);
+ // Computes the transform matrix documented by getTransformMatrix
+ // from the BufferItem sub parts.
+ static void computeTransformMatrix(float outTransform[16],
+ const sp<GraphicBuffer>& buf, const Rect& cropRect,
+ uint32_t transform, bool filtering);
+
// getTimestamp retrieves the timestamp associated with the texture image
// set by the most recent call to updateTexImage.
//
// the queue, outBuffer will be populated with nullptr and outFence will be
// populated with Fence::NO_FENCE
//
+ // outTransformMatrix is not modified if outBuffer is null.
+ //
// Returns NO_ERROR or the status of the Binder transaction
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) = 0;
+ sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
};
// ----------------------------------------------------------------------------
bool waitForNextFrame(uint64_t lastFrame, nsecs_t timeout);
// See IGraphicBufferProducer::getLastQueuedBuffer
+ // See GLConsumer::getTransformMatrix for outTransformMatrix format
status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
+ sp<Fence>* outFence, float outTransformMatrix[16]);
protected:
virtual ~Surface();
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
+#include <gui/GLConsumer.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
mLastQueueBufferFence->waitForever("Throttling EGL Production");
}
mLastQueueBufferFence = fence;
+ mLastQueuedCrop = item.mCrop;
+ mLastQueuedTransform = item.mTransform;
return NO_ERROR;
}
}
status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) {
+ sp<Fence>* outFence, float outTransformMatrix[16]) {
ATRACE_CALL();
BQ_LOGV("getLastQueuedBuffer");
*outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer;
*outFence = mLastQueueBufferFence;
+ // Currently only SurfaceFlinger internally ever changes
+ // GLConsumer's filtering mode, so we just use 'true' here as
+ // this is slightly specialized for the current client of this API,
+ // which does want filtering.
+ GLConsumer::computeTransformMatrix(outTransformMatrix,
+ mSlots[mCore->mLastQueuedSlot].mGraphicBuffer, mLastQueuedCrop,
+ mLastQueuedTransform, true /* filter */);
+
return NO_ERROR;
}
void GLConsumer::computeCurrentTransformMatrixLocked() {
GLC_LOGV("computeCurrentTransformMatrixLocked");
+ sp<GraphicBuffer> buf = (mCurrentTextureImage == nullptr) ?
+ nullptr : mCurrentTextureImage->graphicBuffer();
+ if (buf == nullptr) {
+ GLC_LOGD("computeCurrentTransformMatrixLocked: "
+ "mCurrentTextureImage is NULL");
+ }
+ computeTransformMatrix(mCurrentTransformMatrix, buf,
+ isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop,
+ mCurrentTransform, mFilteringEnabled);
+}
+
+void GLConsumer::computeTransformMatrix(float outTransform[16],
+ const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform,
+ bool filtering) {
float xform[16];
for (int i = 0; i < 16; i++) {
xform[i] = mtxIdentity[i];
}
- if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
float result[16];
mtxMul(result, xform, mtxFlipH);
for (int i = 0; i < 16; i++) {
xform[i] = result[i];
}
}
- if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
float result[16];
mtxMul(result, xform, mtxFlipV);
for (int i = 0; i < 16; i++) {
xform[i] = result[i];
}
}
- if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
float result[16];
mtxMul(result, xform, mtxRot90);
for (int i = 0; i < 16; i++) {
}
}
- sp<GraphicBuffer> buf = (mCurrentTextureImage == NULL) ?
- NULL : mCurrentTextureImage->graphicBuffer();
-
- if (buf == NULL) {
- GLC_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureImage is NULL");
- }
-
float mtxBeforeFlipV[16];
- if (!isEglImageCroppable(mCurrentCrop)) {
- Rect cropRect = mCurrentCrop;
+ if (!cropRect.isEmpty()) {
float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
float bufferWidth = buf->getWidth();
float bufferHeight = buf->getHeight();
- if (!cropRect.isEmpty()) {
- float shrinkAmount = 0.0f;
- if (mFilteringEnabled) {
- // In order to prevent bilinear sampling beyond the edge of the
- // crop rectangle we may need to shrink it by 2 texels in each
- // dimension. Normally this would just need to take 1/2 a texel
- // off each end, but because the chroma channels of YUV420 images
- // are subsampled we may need to shrink the crop region by a whole
- // texel on each side.
- switch (buf->getPixelFormat()) {
- case PIXEL_FORMAT_RGBA_8888:
- case PIXEL_FORMAT_RGBX_8888:
- case PIXEL_FORMAT_RGB_888:
- case PIXEL_FORMAT_RGB_565:
- case PIXEL_FORMAT_BGRA_8888:
- // We know there's no subsampling of any channels, so we
- // only need to shrink by a half a pixel.
- shrinkAmount = 0.5;
- break;
-
- default:
- // If we don't recognize the format, we must assume the
- // worst case (that we care about), which is YUV420.
- shrinkAmount = 1.0;
- break;
- }
+ float shrinkAmount = 0.0f;
+ if (filtering) {
+ // In order to prevent bilinear sampling beyond the edge of the
+ // crop rectangle we may need to shrink it by 2 texels in each
+ // dimension. Normally this would just need to take 1/2 a texel
+ // off each end, but because the chroma channels of YUV420 images
+ // are subsampled we may need to shrink the crop region by a whole
+ // texel on each side.
+ switch (buf->getPixelFormat()) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGB_888:
+ case PIXEL_FORMAT_RGB_565:
+ case PIXEL_FORMAT_BGRA_8888:
+ // We know there's no subsampling of any channels, so we
+ // only need to shrink by a half a pixel.
+ shrinkAmount = 0.5;
+ break;
+
+ default:
+ // If we don't recognize the format, we must assume the
+ // worst case (that we care about), which is YUV420.
+ shrinkAmount = 1.0;
+ break;
}
+ }
- // Only shrink the dimensions that are not the size of the buffer.
- if (cropRect.width() < bufferWidth) {
- tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
- sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
- bufferWidth;
- }
- if (cropRect.height() < bufferHeight) {
- ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
- bufferHeight;
- sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
- bufferHeight;
- }
+ // Only shrink the dimensions that are not the size of the buffer.
+ if (cropRect.width() < bufferWidth) {
+ tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+ sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
+ bufferWidth;
+ }
+ if (cropRect.height() < bufferHeight) {
+ ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
+ bufferHeight;
+ sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
+ bufferHeight;
}
float crop[16] = {
sx, 0, 0, 0,
// coordinate of 0, so GLConsumer must behave the same way. We don't
// want to expose this to applications, however, so we must add an
// additional vertical flip to the transform after all the other transforms.
- mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
+ mtxMul(outTransform, mtxFlipV, mtxBeforeFlipV);
}
nsecs_t GLConsumer::getTimestamp() {
}
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) override {
+ sp<Fence>* outFence, float outTransformMatrix[16]) override {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER, data,
if (hasBuffer) {
buffer = new GraphicBuffer();
result = reply.read(*buffer);
+ if (result == NO_ERROR) {
+ result = reply.read(outTransformMatrix, sizeof(float) * 16);
+ }
}
if (result != NO_ERROR) {
ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
sp<GraphicBuffer> buffer(nullptr);
sp<Fence> fence(Fence::NO_FENCE);
- status_t result = getLastQueuedBuffer(&buffer, &fence);
+ float transform[16] = {};
+ status_t result = getLastQueuedBuffer(&buffer, &fence, transform);
reply->writeInt32(result);
if (result != NO_ERROR) {
return result;
} else {
reply->writeBool(true);
result = reply->write(*buffer);
+ if (result == NO_ERROR) {
+ reply->write(transform, sizeof(float) * 16);
+ }
}
if (result != NO_ERROR) {
ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
}
status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) {
- return mGraphicBufferProducer->getLastQueuedBuffer(outBuffer, outFence);
+ sp<Fence>* outFence, float outTransformMatrix[16]) {
+ return mGraphicBufferProducer->getLastQueuedBuffer(outBuffer, outFence,
+ outTransformMatrix);
}
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
// Make sure we got the second buffer back
sp<GraphicBuffer> returnedBuffer;
sp<Fence> returnedFence;
+ float transform[16];
ASSERT_EQ(OK,
- mProducer->getLastQueuedBuffer(&returnedBuffer, &returnedFence));
+ mProducer->getLastQueuedBuffer(&returnedBuffer, &returnedFence,
+ transform));
ASSERT_EQ(secondBuffer->getNativeBuffer()->handle,
returnedBuffer->getNativeBuffer()->handle);
}
}
status_t VirtualDisplaySurface::getLastQueuedBuffer(
- sp<GraphicBuffer>* /*outBuffer*/, sp<Fence>* /*outFence*/) {
+ sp<GraphicBuffer>* /*outBuffer*/, sp<Fence>* /*outFence*/,
+ float[16] /* outTransformMatrix*/) {
ALOGE("getLastQueuedBuffer not supported on VirtualDisplaySurface");
return INVALID_OPERATION;
}
virtual status_t setAutoRefresh(bool autoRefresh) override;
virtual status_t setDequeueTimeout(nsecs_t timeout) override;
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) override;
+ sp<Fence>* outFence, float outTransformMatrix[16]) override;
//
// Utility methods
// ---------------------------------------------------------------------------
#endif // ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
-
}
status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) {
- return mProducer->getLastQueuedBuffer(outBuffer, outFence);
+ sp<Fence>* outFence, float outTransformMatrix[16]) {
+ return mProducer->getLastQueuedBuffer(outBuffer, outFence,
+ outTransformMatrix);
}
IBinder* MonitoredProducer::onAsBinder() {
virtual uint64_t getNextFrameNumber() const override;
virtual status_t setDequeueTimeout(nsecs_t timeout) override;
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence) override;
+ sp<Fence>* outFence, float outTransformMatrix[16]) override;
virtual IBinder* onAsBinder();
virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
virtual status_t setAutoRefresh(bool autoRefresh) override;