Skip to content

Commit

Permalink
Fix remote GraphicBuffer allocation in SurfaceFlinger.
Browse files Browse the repository at this point in the history
This change fixes a horrible hack that I did to allow application
processes to create GraphicBuffer objects by making a binder call to
SurfaceFlinger.  This change introduces a new binder interface
specifically for doing this, and does it in such a way that
SurfaceFlinger will maintain a reference to the buffers until the app is
done with them.

Change-Id: Icb240397c6c206d7f69124c1497a829f051cb49b
  • Loading branch information
Jamie Gennis committed Jan 13, 2011
1 parent e8d0e8a commit f7acf16
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 74 deletions.
19 changes: 19 additions & 0 deletions include/gui/SurfaceTexture.h
Expand Up @@ -26,12 +26,15 @@
#include <ui/GraphicBuffer.h>

#include <utils/threads.h>
#include <utils/Vector.h>

#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"

namespace android {
// ----------------------------------------------------------------------------

class IGraphicBufferAlloc;

class SurfaceTexture : public BnSurfaceTexture {
public:
enum { MIN_BUFFER_SLOTS = 3 };
Expand Down Expand Up @@ -140,6 +143,12 @@ class SurfaceTexture : public BnSurfaceTexture {
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture;

// mCurrentTextureBuf is the graphic buffer of the current texture. It's
// possible that this buffer is not associated with any buffer slot, so we
// must track it separately in order to properly use
// IGraphicBufferAlloc::freeAllGraphicBuffersExcept.
sp<GraphicBuffer> mCurrentTextureBuf;

// mCurrentCrop is the crop rectangle that applies to the current texture.
// It gets set to mLastQueuedCrop each time updateTexImage is called.
Rect mCurrentCrop;
Expand Down Expand Up @@ -176,6 +185,16 @@ class SurfaceTexture : public BnSurfaceTexture {
// changed with a call to setTexName.
const GLuint mTexName;

// mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to
// allocate new GraphicBuffer objects.
sp<IGraphicBufferAlloc> mGraphicBufferAlloc;

// mAllocdBuffers is mirror of the list of buffers that SurfaceFlinger is
// referencing. This is kept so that gralloc implementations do not need to
// properly handle the case where SurfaceFlinger no longer holds a reference
// to a buffer, but other processes do.
Vector<sp<GraphicBuffer> > mAllocdBuffers;

// mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
Expand Down
64 changes: 64 additions & 0 deletions include/surfaceflinger/IGraphicBufferAlloc.h
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2011 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_SF_IGRAPHIC_BUFFER_ALLOC_H
#define ANDROID_SF_IGRAPHIC_BUFFER_ALLOC_H

#include <stdint.h>
#include <sys/types.h>

#include <utils/RefBase.h>

#include <binder/IInterface.h>

namespace android {
// ----------------------------------------------------------------------------

class IGraphicBufferAlloc : public IInterface
{
public:
DECLARE_META_INTERFACE(GraphicBufferAlloc);

/* Create a new GraphicBuffer for the client to use. The server will
* maintain a reference to the newly created GraphicBuffer until
* freeAllGraphicBuffers is called.
*/
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
PixelFormat format, uint32_t usage) = 0;

/* Free all but one of the GraphicBuffer objects that the server is
* currently referencing. If bufIndex is not a valid index of the buffers
* the server is referencing, then all buffers are freed.
*/
virtual void freeAllGraphicBuffersExcept(int bufIndex) = 0;
};

// ----------------------------------------------------------------------------

class BnGraphicBufferAlloc : public BnInterface<IGraphicBufferAlloc>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};

// ----------------------------------------------------------------------------

}; // namespace android

#endif // ANDROID_SF_IGRAPHIC_BUFFER_ALLOC_H
14 changes: 6 additions & 8 deletions include/surfaceflinger/ISurfaceComposer.h
Expand Up @@ -28,6 +28,7 @@
#include <ui/PixelFormat.h>

#include <surfaceflinger/ISurfaceComposerClient.h>
#include <surfaceflinger/IGraphicBufferAlloc.h>

namespace android {
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -96,6 +97,10 @@ class ISurfaceComposer : public IInterface
*/
virtual sp<ISurfaceComposerClient> createClientConnection() = 0;

/* create a graphic buffer allocator
*/
virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() = 0;

/* retrieve the control block */
virtual sp<IMemoryHeap> getCblk() const = 0;

Expand Down Expand Up @@ -131,13 +136,6 @@ class ISurfaceComposer : public IInterface
* This is an ASYNCHRONOUS call.
*/
virtual void signal() const = 0;

/* Create a new GraphicBuffer for the client to use. SurfaceFlinger will
* not maintain a reference to the GraphicBuffer, so the underlying native
* handle will be freed once the client references are released.
*/
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
PixelFormat format, uint32_t usage) const = 0;
};

// ----------------------------------------------------------------------------
Expand All @@ -151,7 +149,7 @@ class BnSurfaceComposer : public BnInterface<ISurfaceComposer>
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
CREATE_CLIENT_CONNECTION,
CREATE_GRAPHIC_BUFFER,
CREATE_GRAPHIC_BUFFER_ALLOC,
GET_CBLK,
OPEN_GLOBAL_TRANSACTION,
CLOSE_GLOBAL_TRANSACTION,
Expand Down
43 changes: 30 additions & 13 deletions libs/gui/SurfaceTexture.cpp
Expand Up @@ -29,6 +29,7 @@

#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <surfaceflinger/IGraphicBufferAlloc.h>

#include <utils/Log.h>

Expand Down Expand Up @@ -83,6 +84,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex) :
mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
mSlots[i].mOwnedByClient = false;
}
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
}

SurfaceTexture::~SurfaceTexture() {
Expand Down Expand Up @@ -110,9 +113,8 @@ sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
return 0;
}
usage |= GraphicBuffer::USAGE_HW_TEXTURE;
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
sp<GraphicBuffer> graphicBuffer(composer->createGraphicBuffer(w, h,
format, usage));
sp<GraphicBuffer> graphicBuffer(
mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
if (graphicBuffer == 0) {
LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
} else {
Expand All @@ -122,6 +124,7 @@ sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
}
mAllocdBuffers.add(graphicBuffer);
}
return graphicBuffer;
}
Expand Down Expand Up @@ -204,27 +207,28 @@ status_t SurfaceTexture::updateTexImage() {
// Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
// so this check will fail until a buffer gets queued.
if (mCurrentTexture != mLastQueued) {
// Update the SurfaceTexture state.
mCurrentTexture = mLastQueued;
mCurrentCrop = mLastQueuedCrop;
mCurrentTransform = mLastQueuedTransform;

// Update the GL texture object.
EGLImageKHR image = mSlots[mCurrentTexture].mEglImage;
EGLImageKHR image = mSlots[mLastQueued].mEglImage;
if (image == EGL_NO_IMAGE_KHR) {
EGLDisplay dpy = eglGetCurrentDisplay();
sp<GraphicBuffer> graphicBuffer = mSlots[mCurrentTexture].mGraphicBuffer;
sp<GraphicBuffer> graphicBuffer = mSlots[mLastQueued].mGraphicBuffer;
image = createImage(dpy, graphicBuffer);
mSlots[mCurrentTexture].mEglImage = image;
mSlots[mCurrentTexture].mEglDisplay = dpy;
mSlots[mLastQueued].mEglImage = image;
mSlots[mLastQueued].mEglDisplay = dpy;
}
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
GLint error = glGetError();
if (error != GL_NO_ERROR) {
LOGE("error binding external texture image %p (slot %d): %#04x",
image, mCurrentTexture, error);
image, mLastQueued, error);
return -EINVAL;
}

// Update the SurfaceTexture state.
mCurrentTexture = mLastQueued;
mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
mCurrentCrop = mLastQueuedCrop;
mCurrentTransform = mLastQueuedTransform;
}
return OK;
}
Expand Down Expand Up @@ -282,6 +286,19 @@ void SurfaceTexture::freeAllBuffers() {
mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
}
}

int exceptBuf = -1;
for (size_t i = 0; i < mAllocdBuffers.size(); i++) {
if (mAllocdBuffers[i] == mCurrentTextureBuf) {
exceptBuf = i;
break;
}
}
mAllocdBuffers.clear();
if (exceptBuf >= 0) {
mAllocdBuffers.add(mCurrentTextureBuf);
}
mGraphicBufferAlloc->freeAllGraphicBuffersExcept(exceptBuf);
}

EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
Expand Down
1 change: 1 addition & 0 deletions libs/surfaceflinger_client/Android.mk
Expand Up @@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
ISurfaceComposer.cpp \
ISurface.cpp \
ISurfaceComposerClient.cpp \
IGraphicBufferAlloc.cpp \
LayerState.cpp \
SharedBufferStack.cpp \
Surface.cpp \
Expand Down
108 changes: 108 additions & 0 deletions libs/surfaceflinger_client/IGraphicBufferAlloc.cpp
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2011 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.
*/

// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"

#include <stdint.h>
#include <sys/types.h>

#include <binder/Parcel.h>

#include <ui/GraphicBuffer.h>

#include <surfaceflinger/IGraphicBufferAlloc.h>

// ---------------------------------------------------------------------------

namespace android {

enum {
CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
FREE_ALL_GRAPHIC_BUFFERS_EXCEPT,
};

class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc>
{
public:
BpGraphicBufferAlloc(const sp<IBinder>& impl)
: BpInterface<IGraphicBufferAlloc>(impl)
{
}

virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
PixelFormat format, uint32_t usage) {
Parcel data, reply;
data.writeInterfaceToken(
IGraphicBufferAlloc::getInterfaceDescriptor());
data.writeInt32(w);
data.writeInt32(h);
data.writeInt32(format);
data.writeInt32(usage);
remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply);
sp<GraphicBuffer> graphicBuffer;
bool nonNull = (bool)reply.readInt32();
if (nonNull) {
graphicBuffer = new GraphicBuffer();
reply.read(*graphicBuffer);
}
return graphicBuffer;
}

virtual void freeAllGraphicBuffersExcept(int bufIdx) {
Parcel data, reply;
data.writeInterfaceToken(
IGraphicBufferAlloc::getInterfaceDescriptor());
data.writeInt32(bufIdx);
remote()->transact(FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, data, &reply);
}
};

IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc");

// ----------------------------------------------------------------------

status_t BnGraphicBufferAlloc::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// codes that don't require permission check

switch(code) {
case CREATE_GRAPHIC_BUFFER: {
CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
uint32_t w = data.readInt32();
uint32_t h = data.readInt32();
PixelFormat format = data.readInt32();
uint32_t usage = data.readInt32();
sp<GraphicBuffer> result(createGraphicBuffer(w, h, format, usage));
reply->writeInt32(result != 0);
if (result != 0) {
reply->write(*result);
}
return NO_ERROR;
} break;
case FREE_ALL_GRAPHIC_BUFFERS_EXCEPT: {
CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
int bufIdx = data.readInt32();
freeAllGraphicBuffersExcept(bufIdx);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}

}; // namespace android

0 comments on commit f7acf16

Please sign in to comment.