Tag Archives: surface

Understanding Android Graphics Internals – SurfaceFlinger (III)

In this post, we discuss BufferQueue, BufferQueue::BufferSlot and BufferQueue::BufferState defined in frameworks/native/include/gui/BufferQueue.h.

  • BufferQueue

We have learned the concepts of ANativeWindow and ANativeWindowBuffer defined in system/core/include/system/window.h with class Surface (in frameworks/native/include/gui/Surface.h) being concrete implementation of the former class and GraphicBuffer (in frameworks/native/include/ui/GraphicBuffer.h) being an implementation of the latter. IGraphicBufferProducer (in frameworks/native/include/gui/IGraphicBufferProducer.h) defines the Binder API interface for buffer interactions between a surface and SurfaceFlinger. Therefore a UI may draw on a GraphicBuffer and by invoking eglSwitchBuffesr() to render the current buffer; Within eglSwitchBuffers() calls queueBuffer(), dequeueBuffer(), and other APIs in ANativeWindow as necessary.

What is yet to learn is how a graphic buffer queued through eglSwitchBuffers() is composed into frame buffer or hardware-over-laid for rendering and then how the buffer is released to the surface. Here we attempt to uncover part of the myth.

IGraphicBufferProducer was named after the relation of GraphiBuffer content producer and consumer. When a surface finishes drawing on a graphic buffer and subsequent calling of the Surface::queueBuffer() in eglSwitchBuffers() will invoke BpGraphicBufferProducer::queueBuffer() to pass the graphic buffer  (through BufferQueue::queueBuffer())to a BufferQueue object in the Layer object corresponding to the Surface object.

BufferQueue implements BnGraphicBufferProducer and provides the graphic Buffer consumer APIs( e.g. acquireBuffer(), releaseBuffer(), etc.), Going forward, we use a more formal term frame to denote the content in the graphic buffer.

The Layer class implements SurfaceFlingerConsumer::FrameAvailableListener::onFrameAvailable() is set to be invoked in BufferQueue::queueBuffer(); Layer::onFrameAvailable () marks arrival of a new frame and signals to SurfaceFlinger by a signalLayerUpdate() call. The signalLayerUpdate() will either request a vsync on behalf of the surface or post a MessageQueue::INVALIDATE message.

  • BufferQueue::BufferSlot and BufferQueue::BufferSlot::BufferState.

Within BufferQueue, a GraphicBuffer object is assigned a descriptor BufferSlot object to contain attribute metadata such as  associated EGLDisplay, timestamp, transform, cropping, framenumber, fence, buffer state, and so on.

BufferQueue::BufferSlot::BufferState  represents the different states in which a buffer slot can be. SurfaceFlinger recognizes four buffer states, namely, FREE, DEQUED, QUEUED, ACQUIRED, respectively. You may refer to the BufferQueue.h for further information.

BufferQueue tracks all BufferSlots in an array and index each slot by a cardinal slot number, which is reference as buffer in IGraphicBufferProducer.

Before ending this post, I want to note that in BufferQueue::acquireBuffer(), an argument type BufferItem is used, which is sort of equivalent to BufferQueue::BufferSlot.

Understanding Android Graphics Internals – Graphics Basics(II)

After the preceding post on ANativeWindow and ANativeWindowBuffer, we have the fodder to discuss GraphicBuffer and Surface.

After the preceding posts on ANativeWindow and ANativeWindowBuffer, we have the fodder to discuss GraphicBuffer and Surface.

  • GraphicBuffer class

GraphicBuffer is an implementation of ANativeWindowBuffer, part of the definition from frameworks/native/include/ui/GraphicBuffer.h is quoted below.

class GraphicBuffer : public ANativeObjectBase

< ANativeWindowBuffer, GraphicBuffer, RefBase >,

public Flattenable

{

public:

...

status status_t initCheck() const;

uint32_t getWidth() const { return width; }

uint32_t getHeight() const { return height; }

uint32_t getStride() const { return stride; }

uint32_t getUsage() const { return usage; }

PixelFormat getPixelFormat() const { return format; }

Rect getBounds() const { return Rect(width, height); }

status_t reallocate(uint32_t w, uint32_t h, PixelFormat f, uint32_t usage);

status_t lock(uint32_t usage, void** vaddr);

status_t lock(uint32_t usage, const Rect& rect, void** vaddr);

// For HAL_PIXEL_FORMAT_YCbCr_420_888

status_t lockYCbCr(uint32_t usage, android_ycbcr *ycbcr);

status_t lockYCbCr(uint32_t usage, const Rect& rect, android_ycbcr *ycbcr);

status_t unlock();

ANativeWindowBuffer* getNativeBuffer() const;

void setIndex(int index); int getIndex() const;

private:

inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }

inline GraphicBufferMapper& getBufferMapper() {

return mBufferMapper; }

uint8_t mOwner;

private:

friend class Surface;

friend class BpSurface;

friend class BnSurface;

friend class LightRefBase<GraphicBuffer>;

status_t initSize(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage);

void free_handle();

GraphicBufferMapper& mBufferMapper;

ssize_t mInitCheck;

int mIndex;

// If we're wrapping another buffer then this reference will make sure it

// doesn't get freed.

sp<ANativeWindowBuffer> mWrappedBuffer;

...

};

At this point ,we shall not have doubt with respect to GraphicBuffer’s multiple inheritance from Flattenable, ANativeObjectBase and RefBase, and the geometric/color space parameters.

The lock/unlock function are used in mapping/unmapping virtual memory address. The GraphicBufferMapper is the work horse for the functionality.

  • Surface class

Surface class is well-documented in comments in frameworks/native/include/gui/Surface.h.

/*An implementation of ANativeWindow that feeds graphics buffers into a BufferQueue. This is typically used by programs that want to render frames through some means (maybe OpenGL, a software renderer, or a hardware decoder) and have the frames they create forwarded to SurfaceFlinger for compositing. For example, a video decoder could render a frame and call eglSwapBuffers(), which invokes ANativeWindow callbacks defined by Surface. Surface then forwards the buffers through Binder IPC to the BufferQueue’s producer interface, providing the new frame to a consumer such as GLConsumer. */

class Surface : public ANativeObjectBase<ANativeWindow, Surface, RefBase> {

public:

/* creates a Surface from the given IGraphicBufferProducer (which concrete * implementation is a BufferQueue). Surface is mainly state-less while it’s disconnected, it can be viewed as a glorified IGraphicBufferProducer holder. It’s therefore safe to create other Surfaces from the same IGraphicBufferProducer. However, once a Surface is connected, it’ll prevent other Surfaces referring to the same IGraphicBufferProducer to become connected and therefore prevent them to be used as actual producers of buffers. */

Surface(const sp<IGraphicBufferProducer>& bufferProducer);

/* getIGraphicBufferProducer() returns the GraphicBufferProducer this Surface was created with. Usually it’s an error to use the IGraphicBufferProducer while the Surface is connected. */

sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;

/* convenience function to check that the given surface is non NULL as well as its IGraphicBufferProducer */

static bool isValid(const sp<Surface>& surface) { return surface != NULL && surface->getIGraphicBufferProducer() != NULL; }

private:

/* mSurfaceTexture is the interface to the surface texture server. All operations on the surface texture client ultimately translate into interactions with the server using this interface. TODO: rename to mBufferProducer */

sp<IGraphicBufferProducer> mGraphicBufferProducer;

/* mSlots stores the buffers that have been allocated for each buffer slot. It is initialized to null pointers, and gets filled in with the result of IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a slot that has not yet been used. The buffer allocated to a slot will also be replaced if the requested buffer usage or geometry differs from that of the buffer allocated to a slot. */

BufferSlot mSlots[NUM_BUFFER_SLOTS];

/*mReqWidth is the buffer width that will be requested at the next dequeue operation. It is initialized to 1.

uint32_t mReqWidth;

/*mReqHeight is the buffer height that will be requested at the next dequeue operation. It is initialized to 1.

uint32_t mReqHeight;

/* mReqFormat is the buffer pixel format that will be requested at the next deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.

uint32_t mReqFormat;

/* mReqUsage is the set of buffer usage flags that will be requested at the next deuque operation. It is initialized to 0. uint32_t mReqUsage;

/* mTimestamp is the timestamp that will be used for the next buffer queue operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that a timestamp is auto-generated when queueBuffer is called. */

int64_t mTimestamp;

/* mCrop is the crop rectangle that will be used for the next buffer that gets queued. It is set by calling setCrop. Rect mCrop; mScalingMode is the scaling mode that will be used for the next buffers that get queued. It is set by calling setScalingMode. */

int mScalingMode;

//mTransform is the transform identifier that will be used for the next buffer that gets queued. It is set by calling setTransform. uint32_t mTransform; mDefaultWidth is default width of the buffers, regardless of the native_window_set_buffers_dimensions call. uint32_t mDefaultWidth; mDefaultHeight is default height of the buffers, regardless of the native_window_set_buffers_dimensions call. */

uint32_t mDefaultHeight;

/* mUserWidth, if non-zero, is an application-specified override of mDefaultWidth. This is lower priority than the width set by native_window_set_buffers_dimensions. */ uint32_t mUserWidth;

/* mUserHeight, if non-zero, is an application-specified override of mDefaultHeight. This is lower priority than the height set by native_window_set_buffers_dimensions. */

uint32_t mUserHeight;

/*mTransformHint is the transform probably applied to buffers of this window. this is only a hint, actual transform may differ. */

uint32_t mTransformHint;

/* mConsumerRunningBehind whether the consumer is running more than one buffer behind the producer. */

mutable bool mConsumerRunningBehind;

/* mMutex is the mutex used to prevent concurrent access to the member variables of Surface objects. It must be locked whenever the member variables are accessed.

mutable Mutex mMutex;

// must be used from the lock/unlock thread sp<GraphicBuffer> mLockedBuffer;

sp<GraphicBuffer> mPostedBuffer; bool mConnectedToCpu;

// must be accessed from lock/unlock thread only

Region mDirtyRegion;

};

 

It is worth to stress that the buffer operations in Surface is carried out by IGraphiciBufferProducer instance mGraphicBufferProducer, which is assigned in Surface Constructor.

Last note before concluding this post, Surface uses a set of static hook methods to specify the callback function pointes in ANativceWindow.

Understanding Android Graphics Internals – Graphics Basics (I)

In this post, it is presumed that you know about frame buffer driver, display control, display overlay, frame buffer, frame rate, common color space schemes, graphics pipeline ,EGL, Direct Rendering Infrastructure; If you do not, Wikipedia is an excellent source to learn these concepts.

The graphics subsystem underwent significant changes from ICS to Jelly Bean release which introduces vsync and triple buffering to improve on frame rate and animation smoothness along with major code refactoring.

  • Surfaces and centralized surface composition

Android graphics runs on top of the Linux graphics DRM driver and EGL, understandable that it did not port from X11 which services remote network login. SurfaceFlinger provides the compositor service and a UI application does not draw to the frame buffer directly, thus must obtain a surface of depth to draw graphics from SurfaceFlinger.

From UI application point of view, a surface is a rectangular canvas window on the screen with 2D geometrical metrics and a particular color scheme. The depth of the surface is specified upon acquisition.

The SurfaceFlinger determines when and whether a surface shows on the screen, partially or entirely; regions of a surface which is lower than other surfaces will not be displayed or will be blended with colors of higher surfaces; regions of a surface out of the scope of the display screen will not be drawn at all. SurfaceFlinger drivers the composition of surface areas and the rendering to the display screen.

  • What is a window on android? (ANativeWindow)

Each OS platform defines its distinct window structure and window types in EGL are derived from OS native window types. On android, the abstract window prototype is defined in system/core/include/system/window.h, namely ANativeWindow or its synonym EGLNativeWindowType in EGL as defined in frameworks/native/opengl/include/EGL/eglplatform.h, essentially two interchangeable terms in two domains.

Innately, a window is associated a certain count of graphic buffers which are used to contain drawing artifacts.

The struct ANativeWindow encompasses static attributes such as 2D resolutions and dynamic ones such as scaling modes, transforms, buffer swap interval range, a collection of callback function pointers.

The (*query)() callback is used to retrieve attributes from the native window.

The (*setSwapInterval) callback is to set swap interval.

(*queueBuffer), (*dequeueBuffer), (*cancellBuffer) are buffer operation routines; in JellyBean, the file descriptor fenceFd is the newly added argument linked to the fence in EGL extension API and is used for buffer idle signaling. When the window needs a fresh buffer to draw on, this callback is invoked to acquire one; when the current buffer is ready for rending, (*queueBuffer) is invoked to post the buffer for rendering. (*cancellBuffer) is invoked to cancel a buffer already dequeued but not used.

Another special (*perform) callback is invoked to perform customized operations. Valid operations include NATIVE_WINDOW_SET_USAGE, NATIVE_WINDOW_SET_CROP, NATIVE_WINDOW_SET_BUFFER_COUNT, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, NATIVE_WINDOW_SET_BUFFERS_FORMAT, NATIVE_WINDOW_SET_SCALING_MODE, NATIVE_WINDOW_LOCK,NATIVE_WINDOW_UNLOCK_AND_POST, NATIVE_WINDOW_API_CONNECT, NATIVE_WINDOW_API_DISCONNECT, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP.

More operation codes may be added in the future.

Both ANativeWindow and ANativeWindowBuffer has a field common of type  struct android_native_base_t for reference counting checking.

  • The native graphic buffer handle

In Android, a surface is allocated in the SurfaceFlinger and is passed over binder interface to the client ui app. The buffer affiliated with the surface is also allocated in SurfaceFlinger and passes to client over binder interface. As a note here, if not using ion driver, a surface graphic buffer is consistently allocated in ashmem driver and linked with a process-unique file descriptor. The memory address for the buffer can be restored by re-mapping the file descriptor. The details of preserving the file descriptor and restoring the memory address varies from one platform hardware vendor to another. Android only defines a generic native graphic handle as follows in system/core/include/cutils/native_handle.h; vendor-specific private handle extends this structure in the gralloc.c.

typedef struct native_handle
{
int version;        /* sizeof(native_handle_t) */
int numFds;         /* number of file-descriptors at &data[0] */
int numInts;        /* number of ints at &data[numFds] */
int data[0];        /* numFds + numInts ints */
} native_handle_t;

In window.h the actually used buffer is typedefed as

typedef const native_handle_t* buffer_handle_t;

  • The native graphic buffer ANativeWindowBuffer

The ANativeWindowBuffer is structured in the same file as ANativeWindow.

typedef struct ANativeWindowBuffer {

#ifdef __cplusplus     ANativeWindowBuffer() {

        common.magic = ANDROID_NATIVE_BUFFER_MAGIC;         common.version = sizeof(ANativeWindowBuffer);         memset(common.reserved, 0, sizeof(common.reserved));}

// Implement the methods that sp<ANativeWindowBuffer> expects so that it     // can be used to automatically refcount ANativeWindowBuffer's.    

void incStrong(const void* id) const {         common.incRef(const_cast<android_native_base_t*>(&common));     }    

void decStrong(const void* id) const {         common.decRef(const_cast<android_native_base_t*>(&common));     }

#endif

struct android_native_base_t common;

int width;    

int height;    

int stride;    

int format;    

int usage;

void* reserved[2];

buffer_handle_t handle;

void* reserved_proc[8]; }

ANativeWindowBuffer_t;

  • Flattenable

For easy traversing over binder interface, an  graphic object may opts to implement the Flattenable interface whose declaration is quoted below.

/*  * The Flattenable interface allows an object to serialize itself out  * to a byte-buffer and an array of file descriptors.  */

class Flattenable {

public:    

// size in bytes of the flattened object    

virtual size_t getFlattenedSize() const = 0;

// number of file descriptors to flatten    

virtual size_t getFdCount() const = 0;

// flattens the object into buffer.    

// size should be at least of getFlattenedSize()  

// file descriptors are written in the fds[] array but ownership is    

// not transfered (ie: they must be dupped by the caller of    

// flatten() if needed).    

virtual status_t flatten(void* buffer, size_t size,             int fds[], size_t count) const = 0;

// unflattens the object from buffer.    

// size should be equal to the value of getFlattenedSize() when the    

// object was flattened.    

// unflattened file descriptors are found in the fds[] array and    

// don't need to be dupped(). ie: the caller of unflatten doesn't    

// keep ownership. If a fd is not retained by unflatten() it must be    

// explicitly closed.    

virtual status_t unflatten(void const* buffer,   size_t size, int fds[], size_t count) = 0;

protected:     virtual ~Flattenable() = 0;

};