am 6a70cb8a
: Merge "Refactor some code in surfaceflinger in preparation of upcoming changes" into kraken
This commit is contained in:
@ -13,6 +13,7 @@ LOCAL_SRC_FILES:= \
|
|||||||
LayerDim.cpp \
|
LayerDim.cpp \
|
||||||
MessageQueue.cpp \
|
MessageQueue.cpp \
|
||||||
SurfaceFlinger.cpp \
|
SurfaceFlinger.cpp \
|
||||||
|
TextureManager.cpp \
|
||||||
Tokenizer.cpp \
|
Tokenizer.cpp \
|
||||||
Transform.cpp
|
Transform.cpp
|
||||||
|
|
||||||
|
@ -48,40 +48,48 @@ template <typename T> inline T min(T a, T b) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
Layer::Layer(SurfaceFlinger* flinger, DisplayID display,
|
Layer::Layer(SurfaceFlinger* flinger, DisplayID display,
|
||||||
const sp<Client>& c, int32_t i)
|
const sp<Client>& client, int32_t i)
|
||||||
: LayerBaseClient(flinger, display, c, i),
|
: LayerBaseClient(flinger, display, client, i),
|
||||||
|
lcblk(NULL),
|
||||||
mSecure(false),
|
mSecure(false),
|
||||||
mNeedsBlending(true),
|
mNeedsBlending(true),
|
||||||
mNeedsDithering(false)
|
mNeedsDithering(false),
|
||||||
|
mTextureManager(mFlags),
|
||||||
|
mBufferManager(mTextureManager)
|
||||||
{
|
{
|
||||||
// no OpenGL operation is possible here, since we might not be
|
// no OpenGL operation is possible here, since we might not be
|
||||||
// in the OpenGL thread.
|
// in the OpenGL thread.
|
||||||
mFrontBufferIndex = lcblk->getFrontBuffer();
|
lcblk = new SharedBufferServer(
|
||||||
|
client->ctrlblk, i, mBufferManager.getBufferCount(),
|
||||||
|
getIdentity());
|
||||||
|
|
||||||
|
mBufferManager.setActiveBufferIndex( lcblk->getFrontBuffer() );
|
||||||
}
|
}
|
||||||
|
|
||||||
Layer::~Layer()
|
Layer::~Layer()
|
||||||
{
|
{
|
||||||
destroy();
|
destroy();
|
||||||
// the actual buffers will be destroyed here
|
// the actual buffers will be destroyed here
|
||||||
|
delete lcblk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// called with SurfaceFlinger::mStateLock as soon as the layer is entered
|
||||||
|
// in the purgatory list
|
||||||
|
void Layer::onRemoved()
|
||||||
|
{
|
||||||
|
// wake up the condition
|
||||||
|
lcblk->setStatus(NO_INIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Layer::destroy()
|
void Layer::destroy()
|
||||||
{
|
{
|
||||||
for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
|
|
||||||
if (mTextures[i].name != -1U) {
|
|
||||||
glDeleteTextures(1, &mTextures[i].name);
|
|
||||||
mTextures[i].name = -1U;
|
|
||||||
}
|
|
||||||
if (mTextures[i].image != EGL_NO_IMAGE_KHR) {
|
|
||||||
EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
|
EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
|
||||||
eglDestroyImageKHR(dpy, mTextures[i].image);
|
mBufferManager.destroy(dpy);
|
||||||
mTextures[i].image = EGL_NO_IMAGE_KHR;
|
|
||||||
}
|
|
||||||
Mutex::Autolock _l(mLock);
|
|
||||||
mBuffers[i].clear();
|
|
||||||
mWidth = mHeight = 0;
|
|
||||||
}
|
|
||||||
mSurface.clear();
|
mSurface.clear();
|
||||||
|
|
||||||
|
Mutex::Autolock _l(mLock);
|
||||||
|
mWidth = mHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<LayerBaseClient::Surface> Layer::createSurface() const
|
sp<LayerBaseClient::Surface> Layer::createSurface() const
|
||||||
@ -131,17 +139,13 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h,
|
|||||||
int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED);
|
int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED);
|
||||||
mNeedsDithering = layerRedsize > displayRedSize;
|
mNeedsDithering = layerRedsize > displayRedSize;
|
||||||
|
|
||||||
for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
|
|
||||||
mBuffers[i] = new GraphicBuffer();
|
|
||||||
}
|
|
||||||
mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);
|
mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Layer::reloadTexture(const Region& dirty)
|
void Layer::reloadTexture(const Region& dirty)
|
||||||
{
|
{
|
||||||
Mutex::Autolock _l(mLock);
|
sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
|
||||||
sp<GraphicBuffer> buffer(getFrontBufferLocked());
|
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
// this situation can happen if we ran out of memory for instance.
|
// this situation can happen if we ran out of memory for instance.
|
||||||
// not much we can do. continue to use whatever texture was bound
|
// not much we can do. continue to use whatever texture was bound
|
||||||
@ -149,37 +153,24 @@ void Layer::reloadTexture(const Region& dirty)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int index = mFrontBufferIndex;
|
|
||||||
|
|
||||||
// create the new texture name if needed
|
|
||||||
if (UNLIKELY(mTextures[index].name == -1U)) {
|
|
||||||
mTextures[index].name = createTexture();
|
|
||||||
mTextures[index].width = 0;
|
|
||||||
mTextures[index].height = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef EGL_ANDROID_image_native_buffer
|
#ifdef EGL_ANDROID_image_native_buffer
|
||||||
if (mFlags & DisplayHardware::DIRECT_TEXTURE) {
|
if (mFlags & DisplayHardware::DIRECT_TEXTURE) {
|
||||||
if (mTextures[index].dirty) {
|
EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
|
||||||
if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) {
|
if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) {
|
||||||
// not sure what we can do here...
|
// not sure what we can do here...
|
||||||
mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
|
mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
|
||||||
goto slowpath;
|
goto slowpath;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
slowpath:
|
slowpath:
|
||||||
for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
|
|
||||||
mTextures[i].image = EGL_NO_IMAGE_KHR;
|
|
||||||
}
|
|
||||||
GGLSurface t;
|
GGLSurface t;
|
||||||
status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
|
status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
|
||||||
LOGE_IF(res, "error %d (%s) locking buffer %p",
|
LOGE_IF(res, "error %d (%s) locking buffer %p",
|
||||||
res, strerror(res), buffer.get());
|
res, strerror(res), buffer.get());
|
||||||
if (res == NO_ERROR) {
|
if (res == NO_ERROR) {
|
||||||
loadTexture(&mTextures[0], dirty, t);
|
mBufferManager.loadTexture(dirty, t);
|
||||||
buffer->unlock();
|
buffer->unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,11 +178,8 @@ slowpath:
|
|||||||
|
|
||||||
void Layer::onDraw(const Region& clip) const
|
void Layer::onDraw(const Region& clip) const
|
||||||
{
|
{
|
||||||
int index = mFrontBufferIndex;
|
Texture tex(mBufferManager.getActiveTexture());
|
||||||
if (mTextures[index].image == EGL_NO_IMAGE_KHR)
|
if (tex.name == -1LU) {
|
||||||
index = 0;
|
|
||||||
GLuint textureName = mTextures[index].name;
|
|
||||||
if (UNLIKELY(textureName == -1LU)) {
|
|
||||||
// the texture has not been created yet, this Layer has
|
// the texture has not been created yet, this Layer has
|
||||||
// in fact never been drawn into. This happens frequently with
|
// in fact never been drawn into. This happens frequently with
|
||||||
// SurfaceView because the WindowManager can't know when the client
|
// SurfaceView because the WindowManager can't know when the client
|
||||||
@ -217,7 +205,7 @@ void Layer::onDraw(const Region& clip) const
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
drawWithOpenGL(clip, mTextures[index]);
|
drawWithOpenGL(clip, tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
|
sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
|
||||||
@ -254,15 +242,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
|
|||||||
Mutex::Autolock _l(mLock);
|
Mutex::Autolock _l(mLock);
|
||||||
w = mWidth;
|
w = mWidth;
|
||||||
h = mHeight;
|
h = mHeight;
|
||||||
buffer = mBuffers[index];
|
buffer = mBufferManager.detachBuffer(index);
|
||||||
|
|
||||||
// destroy() could have been called before we get here, we log it
|
|
||||||
// because it's uncommon, and the code below should handle it
|
|
||||||
LOGW_IF(buffer==0,
|
|
||||||
"mBuffers[%d] is null (mWidth=%d, mHeight=%d)",
|
|
||||||
index, w, h);
|
|
||||||
|
|
||||||
mBuffers[index].clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t effectiveUsage = getEffectiveUsage(usage);
|
const uint32_t effectiveUsage = getEffectiveUsage(usage);
|
||||||
@ -290,10 +270,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
|
|||||||
if (err == NO_ERROR && buffer->handle != 0) {
|
if (err == NO_ERROR && buffer->handle != 0) {
|
||||||
Mutex::Autolock _l(mLock);
|
Mutex::Autolock _l(mLock);
|
||||||
if (mWidth && mHeight) {
|
if (mWidth && mHeight) {
|
||||||
// and we have new buffer
|
mBufferManager.attachBuffer(index, buffer);
|
||||||
mBuffers[index] = buffer;
|
|
||||||
// texture is now dirty...
|
|
||||||
mTextures[index].dirty = true;
|
|
||||||
} else {
|
} else {
|
||||||
// oops we got killed while we were allocating the buffer
|
// oops we got killed while we were allocating the buffer
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
@ -338,13 +315,10 @@ uint32_t Layer::doTransaction(uint32_t flags)
|
|||||||
(front.requested_h != temp.requested_h)) {
|
(front.requested_h != temp.requested_h)) {
|
||||||
// the size changed, we need to ask our client to request a new buffer
|
// the size changed, we need to ask our client to request a new buffer
|
||||||
LOGD_IF(DEBUG_RESIZE,
|
LOGD_IF(DEBUG_RESIZE,
|
||||||
"resize (layer=%p), requested (%dx%d), "
|
"resize (layer=%p), requested (%dx%d), drawing (%d,%d)",
|
||||||
"drawing (%d,%d), (%dx%d), (%dx%d)",
|
|
||||||
this,
|
this,
|
||||||
int(temp.requested_w), int(temp.requested_h),
|
int(temp.requested_w), int(temp.requested_h),
|
||||||
int(front.requested_w), int(front.requested_h),
|
int(front.requested_w), int(front.requested_h));
|
||||||
int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()),
|
|
||||||
int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight()));
|
|
||||||
|
|
||||||
// we're being resized and there is a freeze display request,
|
// we're being resized and there is a freeze display request,
|
||||||
// acquire a freeze lock, so that the screen stays put
|
// acquire a freeze lock, so that the screen stays put
|
||||||
@ -396,22 +370,25 @@ void Layer::setDrawingSize(uint32_t w, uint32_t h) {
|
|||||||
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
|
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
|
||||||
{
|
{
|
||||||
ssize_t buf = lcblk->retireAndLock();
|
ssize_t buf = lcblk->retireAndLock();
|
||||||
if (buf < NO_ERROR) {
|
if (buf == NOT_ENOUGH_DATA) {
|
||||||
//LOGW("nothing to retire (%s)", strerror(-buf));
|
// NOTE: This is not an error, it simply means there is nothing to
|
||||||
// NOTE: here the buffer is locked because we will used
|
// retire. The buffer is locked because we will use it
|
||||||
// for composition later in the loop
|
// for composition later in the loop
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ouch, this really should never happen
|
if (buf < NO_ERROR) {
|
||||||
if (uint32_t(buf)>=NUM_BUFFERS) {
|
|
||||||
LOGE("retireAndLock() buffer index (%d) out of range", buf);
|
LOGE("retireAndLock() buffer index (%d) out of range", buf);
|
||||||
mPostedDirtyRegion.clear();
|
mPostedDirtyRegion.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we retired a buffer, which becomes the new front buffer
|
// we retired a buffer, which becomes the new front buffer
|
||||||
mFrontBufferIndex = buf;
|
if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) {
|
||||||
|
LOGE("retireAndLock() buffer index (%d) out of range", buf);
|
||||||
|
mPostedDirtyRegion.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// get the dirty region
|
// get the dirty region
|
||||||
sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
|
sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
|
||||||
@ -503,10 +480,9 @@ void Layer::unlockPageFlip(
|
|||||||
|
|
||||||
void Layer::finishPageFlip()
|
void Layer::finishPageFlip()
|
||||||
{
|
{
|
||||||
status_t err = lcblk->unlock( mFrontBufferIndex );
|
int buf = mBufferManager.getActiveBufferIndex();
|
||||||
LOGE_IF(err!=NO_ERROR,
|
status_t err = lcblk->unlock( buf );
|
||||||
"layer %p, buffer=%d wasn't locked!",
|
LOGE_IF(err!=NO_ERROR, "layer %p, buffer=%d wasn't locked!", this, buf);
|
||||||
this, mFrontBufferIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -543,6 +519,109 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Layer::BufferManager::BufferManager(TextureManager& tm)
|
||||||
|
: mTextureManager(tm), mActiveBuffer(0), mFailover(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Layer::BufferManager::getBufferCount() const {
|
||||||
|
return NUM_BUFFERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only for debugging
|
||||||
|
sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const {
|
||||||
|
return mBufferData[index].buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Layer::BufferManager::setActiveBufferIndex(size_t index) {
|
||||||
|
// TODO: need to validate 'index'
|
||||||
|
mActiveBuffer = index;
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Layer::BufferManager::getActiveBufferIndex() const {
|
||||||
|
return mActiveBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture Layer::BufferManager::getActiveTexture() const {
|
||||||
|
return mFailover ? mFailoverTexture : mBufferData[mActiveBuffer].texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const {
|
||||||
|
Mutex::Autolock _l(mLock);
|
||||||
|
return mBufferData[mActiveBuffer].buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index)
|
||||||
|
{
|
||||||
|
sp<GraphicBuffer> buffer;
|
||||||
|
Mutex::Autolock _l(mLock);
|
||||||
|
buffer = mBufferData[index].buffer;
|
||||||
|
mBufferData[index].buffer = 0;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Layer::BufferManager::attachBuffer(size_t index,
|
||||||
|
const sp<GraphicBuffer>& buffer)
|
||||||
|
{
|
||||||
|
Mutex::Autolock _l(mLock);
|
||||||
|
mBufferData[index].buffer = buffer;
|
||||||
|
mBufferData[index].texture.dirty = true;
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Layer::BufferManager::destroyTexture(Texture* tex, EGLDisplay dpy)
|
||||||
|
{
|
||||||
|
if (tex->name != -1U) {
|
||||||
|
glDeleteTextures(1, &tex->name);
|
||||||
|
tex->name = -1U;
|
||||||
|
}
|
||||||
|
if (tex->image != EGL_NO_IMAGE_KHR) {
|
||||||
|
eglDestroyImageKHR(dpy, tex->image);
|
||||||
|
tex->image = EGL_NO_IMAGE_KHR;
|
||||||
|
}
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Layer::BufferManager::destroy(EGLDisplay dpy)
|
||||||
|
{
|
||||||
|
Mutex::Autolock _l(mLock);
|
||||||
|
for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
|
||||||
|
destroyTexture(&mBufferData[i].texture, dpy);
|
||||||
|
mBufferData[i].buffer = 0;
|
||||||
|
}
|
||||||
|
destroyTexture(&mFailoverTexture, dpy);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Layer::BufferManager::initEglImage(EGLDisplay dpy,
|
||||||
|
const sp<GraphicBuffer>& buffer)
|
||||||
|
{
|
||||||
|
size_t index = mActiveBuffer;
|
||||||
|
Texture& texture(mBufferData[index].texture);
|
||||||
|
status_t err = mTextureManager.initEglImage(&texture, dpy, buffer);
|
||||||
|
// if EGLImage fails, we switch to regular texture mode, and we
|
||||||
|
// free all resources associated with using EGLImages.
|
||||||
|
if (err == NO_ERROR) {
|
||||||
|
mFailover = false;
|
||||||
|
destroyTexture(&mFailoverTexture, dpy);
|
||||||
|
} else {
|
||||||
|
mFailover = true;
|
||||||
|
for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
|
||||||
|
destroyTexture(&mBufferData[i].texture, dpy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Layer::BufferManager::loadTexture(
|
||||||
|
const Region& dirty, const GGLSurface& t)
|
||||||
|
{
|
||||||
|
return mTextureManager.loadTexture(&mFailoverTexture, dirty, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,
|
Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,
|
||||||
SurfaceID id, const sp<Layer>& owner)
|
SurfaceID id, const sp<Layer>& owner)
|
||||||
: Surface(flinger, id, owner->getIdentity(), owner)
|
: Surface(flinger, id, owner->getIdentity(), owner)
|
||||||
@ -558,12 +637,8 @@ sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage)
|
|||||||
sp<GraphicBuffer> buffer;
|
sp<GraphicBuffer> buffer;
|
||||||
sp<Layer> owner(getOwner());
|
sp<Layer> owner(getOwner());
|
||||||
if (owner != 0) {
|
if (owner != 0) {
|
||||||
LOGE_IF(uint32_t(index)>=NUM_BUFFERS,
|
|
||||||
"getBuffer() index (%d) out of range", index);
|
|
||||||
if (uint32_t(index) < NUM_BUFFERS) {
|
|
||||||
buffer = owner->requestBuffer(index, usage);
|
buffer = owner->requestBuffer(index, usage);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "LayerBase.h"
|
#include "LayerBase.h"
|
||||||
#include "Transform.h"
|
#include "Transform.h"
|
||||||
|
#include "TextureManager.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@ -41,11 +42,13 @@ class FreezeLock;
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const size_t NUM_BUFFERS = 2;
|
|
||||||
|
|
||||||
class Layer : public LayerBaseClient
|
class Layer : public LayerBaseClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
// lcblk is (almost) only accessed from the main SF thread, in the places
|
||||||
|
// where it's not, a reference to Client must be held
|
||||||
|
SharedBufferServer* lcblk;
|
||||||
|
|
||||||
Layer(SurfaceFlinger* flinger, DisplayID display,
|
Layer(SurfaceFlinger* flinger, DisplayID display,
|
||||||
const sp<Client>& client, int32_t i);
|
const sp<Client>& client, int32_t i);
|
||||||
|
|
||||||
@ -66,15 +69,14 @@ public:
|
|||||||
virtual bool isSecure() const { return mSecure; }
|
virtual bool isSecure() const { return mSecure; }
|
||||||
virtual sp<Surface> createSurface() const;
|
virtual sp<Surface> createSurface() const;
|
||||||
virtual status_t ditch();
|
virtual status_t ditch();
|
||||||
|
virtual void onRemoved();
|
||||||
|
|
||||||
// only for debugging
|
// only for debugging
|
||||||
inline sp<GraphicBuffer> getBuffer(int i) const { return mBuffers[i]; }
|
inline sp<GraphicBuffer> getBuffer(int i) const { return mBufferManager.getBuffer(i); }
|
||||||
// only for debugging
|
// only for debugging
|
||||||
inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
|
inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
|
||||||
// only for debugging
|
// only for debugging
|
||||||
inline PixelFormat pixelFormat() const { return mFormat; }
|
inline PixelFormat pixelFormat() const { return mFormat; }
|
||||||
// only for debugging
|
|
||||||
inline int getFrontBufferIndex() const { return mFrontBufferIndex; }
|
|
||||||
|
|
||||||
virtual const char* getTypeId() const { return "Layer"; }
|
virtual const char* getTypeId() const { return "Layer"; }
|
||||||
|
|
||||||
@ -82,10 +84,6 @@ protected:
|
|||||||
virtual void dump(String8& result, char* scratch, size_t size) const;
|
virtual void dump(String8& result, char* scratch, size_t size) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline sp<GraphicBuffer> getFrontBufferLocked() {
|
|
||||||
return mBuffers[mFrontBufferIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
void reloadTexture(const Region& dirty);
|
void reloadTexture(const Region& dirty);
|
||||||
|
|
||||||
uint32_t getEffectiveUsage(uint32_t usage) const;
|
uint32_t getEffectiveUsage(uint32_t usage) const;
|
||||||
@ -116,13 +114,63 @@ private:
|
|||||||
sp<FreezeLock> mFreezeLock;
|
sp<FreezeLock> mFreezeLock;
|
||||||
PixelFormat mFormat;
|
PixelFormat mFormat;
|
||||||
|
|
||||||
// protected by mLock
|
class BufferManager {
|
||||||
sp<GraphicBuffer> mBuffers[NUM_BUFFERS];
|
static const size_t NUM_BUFFERS = 2;
|
||||||
Texture mTextures[NUM_BUFFERS];
|
struct BufferData {
|
||||||
uint32_t mWidth;
|
sp<GraphicBuffer> buffer;
|
||||||
uint32_t mHeight;
|
Texture texture;
|
||||||
|
};
|
||||||
|
mutable Mutex mLock;
|
||||||
|
BufferData mBufferData[NUM_BUFFERS];
|
||||||
|
Texture mFailoverTexture;
|
||||||
|
TextureManager& mTextureManager;
|
||||||
|
ssize_t mActiveBuffer;
|
||||||
|
bool mFailover;
|
||||||
|
static status_t destroyTexture(Texture* tex, EGLDisplay dpy);
|
||||||
|
|
||||||
|
public:
|
||||||
|
BufferManager(TextureManager& tm);
|
||||||
|
|
||||||
|
size_t getBufferCount() const;
|
||||||
|
|
||||||
|
// detach/attach buffer from/to given index
|
||||||
|
sp<GraphicBuffer> detachBuffer(size_t index);
|
||||||
|
status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer);
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// must be called from GL thread
|
||||||
|
|
||||||
|
// set/get active buffer index
|
||||||
|
status_t setActiveBufferIndex(size_t index);
|
||||||
|
size_t getActiveBufferIndex() const;
|
||||||
|
|
||||||
|
// return the active buffer
|
||||||
|
sp<GraphicBuffer> getActiveBuffer() const;
|
||||||
|
|
||||||
|
// return the active texture (or fail-over)
|
||||||
|
Texture getActiveTexture() const;
|
||||||
|
|
||||||
|
// frees resources associated with all buffers
|
||||||
|
status_t destroy(EGLDisplay dpy);
|
||||||
|
|
||||||
|
// load bitmap data into the active buffer
|
||||||
|
status_t loadTexture(const Region& dirty, const GGLSurface& t);
|
||||||
|
|
||||||
|
// make active buffer an EGLImage if needed
|
||||||
|
status_t initEglImage(EGLDisplay dpy,
|
||||||
|
const sp<GraphicBuffer>& buffer);
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// only for debugging
|
||||||
|
sp<GraphicBuffer> getBuffer(size_t index) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
TextureManager mTextureManager;
|
||||||
|
BufferManager mBufferManager;
|
||||||
|
|
||||||
mutable Mutex mLock;
|
mutable Mutex mLock;
|
||||||
|
uint32_t mWidth;
|
||||||
|
uint32_t mHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "LayerBase.h"
|
#include "LayerBase.h"
|
||||||
#include "SurfaceFlinger.h"
|
#include "SurfaceFlinger.h"
|
||||||
#include "DisplayHardware/DisplayHardware.h"
|
#include "DisplayHardware/DisplayHardware.h"
|
||||||
|
#include "TextureManager.h"
|
||||||
|
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
@ -340,18 +341,6 @@ void LayerBase::draw(const Region& inClip) const
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint LayerBase::createTexture() const
|
|
||||||
{
|
|
||||||
GLuint textureName = -1;
|
|
||||||
glGenTextures(1, &textureName);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
||||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
return textureName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red,
|
void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red,
|
||||||
GLclampx green, GLclampx blue,
|
GLclampx green, GLclampx blue,
|
||||||
GLclampx alpha) const
|
GLclampx alpha) const
|
||||||
@ -492,187 +481,6 @@ void LayerBase::validateTexture(GLint textureName) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LayerBase::isSupportedYuvFormat(int format) const
|
|
||||||
{
|
|
||||||
switch (format) {
|
|
||||||
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
|
||||||
case HAL_PIXEL_FORMAT_YCbCr_420_SP:
|
|
||||||
case HAL_PIXEL_FORMAT_YCbCr_422_P:
|
|
||||||
case HAL_PIXEL_FORMAT_YCbCr_420_P:
|
|
||||||
case HAL_PIXEL_FORMAT_YCbCr_422_I:
|
|
||||||
case HAL_PIXEL_FORMAT_YCbCr_420_I:
|
|
||||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LayerBase::loadTexture(Texture* texture,
|
|
||||||
const Region& dirty, const GGLSurface& t) const
|
|
||||||
{
|
|
||||||
if (texture->name == -1U) {
|
|
||||||
// uh?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->name);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* In OpenGL ES we can't specify a stride with glTexImage2D (however,
|
|
||||||
* GL_UNPACK_ALIGNMENT is a limited form of stride).
|
|
||||||
* So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
|
|
||||||
* need to do something reasonable (here creating a bigger texture).
|
|
||||||
*
|
|
||||||
* extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
|
|
||||||
*
|
|
||||||
* This situation doesn't happen often, but some h/w have a limitation
|
|
||||||
* for their framebuffer (eg: must be multiple of 8 pixels), and
|
|
||||||
* we need to take that into account when using these buffers as
|
|
||||||
* textures.
|
|
||||||
*
|
|
||||||
* This should never be a problem with POT textures
|
|
||||||
*/
|
|
||||||
|
|
||||||
int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
|
|
||||||
unpack = 1 << ((unpack > 3) ? 3 : unpack);
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* round to POT if needed
|
|
||||||
*/
|
|
||||||
if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
|
|
||||||
texture->NPOTAdjust = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture->NPOTAdjust) {
|
|
||||||
// find the smallest power-of-two that will accommodate our surface
|
|
||||||
texture->potWidth = 1 << (31 - clz(t.width));
|
|
||||||
texture->potHeight = 1 << (31 - clz(t.height));
|
|
||||||
if (texture->potWidth < t.width) texture->potWidth <<= 1;
|
|
||||||
if (texture->potHeight < t.height) texture->potHeight <<= 1;
|
|
||||||
texture->wScale = float(t.width) / texture->potWidth;
|
|
||||||
texture->hScale = float(t.height) / texture->potHeight;
|
|
||||||
} else {
|
|
||||||
texture->potWidth = t.width;
|
|
||||||
texture->potHeight = t.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rect bounds(dirty.bounds());
|
|
||||||
GLvoid* data = 0;
|
|
||||||
if (texture->width != t.width || texture->height != t.height) {
|
|
||||||
texture->width = t.width;
|
|
||||||
texture->height = t.height;
|
|
||||||
|
|
||||||
// texture size changed, we need to create a new one
|
|
||||||
bounds.set(Rect(t.width, t.height));
|
|
||||||
if (t.width == texture->potWidth &&
|
|
||||||
t.height == texture->potHeight) {
|
|
||||||
// we can do it one pass
|
|
||||||
data = t.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
GL_RGB, texture->potWidth, texture->potHeight, 0,
|
|
||||||
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
|
|
||||||
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
GL_RGBA, texture->potWidth, texture->potHeight, 0,
|
|
||||||
GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
|
|
||||||
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
|
|
||||||
t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
GL_RGBA, texture->potWidth, texture->potHeight, 0,
|
|
||||||
GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
||||||
} else if (isSupportedYuvFormat(t.format)) {
|
|
||||||
// just show the Y plane of YUV buffers
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
|
|
||||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
|
|
||||||
} else {
|
|
||||||
// oops, we don't handle this format!
|
|
||||||
LOGE("layer %p, texture=%d, using format %d, which is not "
|
|
||||||
"supported by the GL", this, texture->name, t.format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!data) {
|
|
||||||
if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
0, bounds.top, t.width, bounds.height(),
|
|
||||||
GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
|
|
||||||
t.data + bounds.top*t.stride*2);
|
|
||||||
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
0, bounds.top, t.width, bounds.height(),
|
|
||||||
GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
|
|
||||||
t.data + bounds.top*t.stride*2);
|
|
||||||
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
|
|
||||||
t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
0, bounds.top, t.width, bounds.height(),
|
|
||||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
|
||||||
t.data + bounds.top*t.stride*4);
|
|
||||||
} else if (isSupportedYuvFormat(t.format)) {
|
|
||||||
// just show the Y plane of YUV buffers
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
0, bounds.top, t.width, bounds.height(),
|
|
||||||
GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
|
||||||
t.data + bounds.top*t.stride);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t LayerBase::initializeEglImage(
|
|
||||||
const sp<GraphicBuffer>& buffer, Texture* texture)
|
|
||||||
{
|
|
||||||
status_t err = NO_ERROR;
|
|
||||||
|
|
||||||
// we need to recreate the texture
|
|
||||||
EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
|
|
||||||
|
|
||||||
// free the previous image
|
|
||||||
if (texture->image != EGL_NO_IMAGE_KHR) {
|
|
||||||
eglDestroyImageKHR(dpy, texture->image);
|
|
||||||
texture->image = EGL_NO_IMAGE_KHR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// construct an EGL_NATIVE_BUFFER_ANDROID
|
|
||||||
android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
|
|
||||||
|
|
||||||
// create the new EGLImageKHR
|
|
||||||
const EGLint attrs[] = {
|
|
||||||
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
|
||||||
EGL_NONE, EGL_NONE
|
|
||||||
};
|
|
||||||
texture->image = eglCreateImageKHR(
|
|
||||||
dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
|
|
||||||
(EGLClientBuffer)clientBuf, attrs);
|
|
||||||
|
|
||||||
if (texture->image != EGL_NO_IMAGE_KHR) {
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture->name);
|
|
||||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
|
|
||||||
(GLeglImageOES)texture->image);
|
|
||||||
GLint error = glGetError();
|
|
||||||
if (UNLIKELY(error != GL_NO_ERROR)) {
|
|
||||||
LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) "
|
|
||||||
"failed err=0x%04x",
|
|
||||||
this, texture->image, error);
|
|
||||||
err = INVALID_OPERATION;
|
|
||||||
} else {
|
|
||||||
// Everything went okay!
|
|
||||||
texture->NPOTAdjust = false;
|
|
||||||
texture->dirty = false;
|
|
||||||
texture->width = clientBuf->width;
|
|
||||||
texture->height = clientBuf->height;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x",
|
|
||||||
this, eglGetError());
|
|
||||||
err = INVALID_OPERATION;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
|
void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
|
||||||
{
|
{
|
||||||
const Layer::State& s(drawingState());
|
const Layer::State& s(drawingState());
|
||||||
@ -696,12 +504,9 @@ int32_t LayerBaseClient::sIdentity = 0;
|
|||||||
|
|
||||||
LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
|
LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
|
||||||
const sp<Client>& client, int32_t i)
|
const sp<Client>& client, int32_t i)
|
||||||
: LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i),
|
: LayerBase(flinger, display), client(client), mIndex(i),
|
||||||
mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
|
mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
|
||||||
{
|
{
|
||||||
lcblk = new SharedBufferServer(
|
|
||||||
client->ctrlblk, i, NUM_BUFFERS,
|
|
||||||
mIdentity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayerBaseClient::onFirstRef()
|
void LayerBaseClient::onFirstRef()
|
||||||
@ -718,7 +523,6 @@ LayerBaseClient::~LayerBaseClient()
|
|||||||
if (client != 0) {
|
if (client != 0) {
|
||||||
client->free(mIndex);
|
client->free(mIndex);
|
||||||
}
|
}
|
||||||
delete lcblk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t LayerBaseClient::serverIndex() const
|
ssize_t LayerBaseClient::serverIndex() const
|
||||||
@ -748,14 +552,6 @@ sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const
|
|||||||
const_cast<LayerBaseClient *>(this));
|
const_cast<LayerBaseClient *>(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// called with SurfaceFlinger::mStateLock as soon as the layer is entered
|
|
||||||
// in the purgatory list
|
|
||||||
void LayerBaseClient::onRemoved()
|
|
||||||
{
|
|
||||||
// wake up the condition
|
|
||||||
lcblk->setStatus(NO_INIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const
|
void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const
|
||||||
{
|
{
|
||||||
LayerBase::dump(result, buffer, SIZE);
|
LayerBase::dump(result, buffer, SIZE);
|
||||||
|
@ -46,6 +46,7 @@ class Client;
|
|||||||
class GraphicBuffer;
|
class GraphicBuffer;
|
||||||
class GraphicPlane;
|
class GraphicPlane;
|
||||||
class SurfaceFlinger;
|
class SurfaceFlinger;
|
||||||
|
class Texture;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -221,35 +222,10 @@ protected:
|
|||||||
const GraphicPlane& graphicPlane(int dpy) const;
|
const GraphicPlane& graphicPlane(int dpy) const;
|
||||||
GraphicPlane& graphicPlane(int dpy);
|
GraphicPlane& graphicPlane(int dpy);
|
||||||
|
|
||||||
GLuint createTexture() const;
|
|
||||||
|
|
||||||
struct Texture {
|
|
||||||
Texture() : name(-1U), width(0), height(0),
|
|
||||||
image(EGL_NO_IMAGE_KHR), transform(0),
|
|
||||||
NPOTAdjust(false), dirty(true) { }
|
|
||||||
GLuint name;
|
|
||||||
GLuint width;
|
|
||||||
GLuint height;
|
|
||||||
GLuint potWidth;
|
|
||||||
GLuint potHeight;
|
|
||||||
GLfloat wScale;
|
|
||||||
GLfloat hScale;
|
|
||||||
EGLImageKHR image;
|
|
||||||
uint32_t transform;
|
|
||||||
bool NPOTAdjust;
|
|
||||||
bool dirty;
|
|
||||||
};
|
|
||||||
|
|
||||||
void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g,
|
void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g,
|
||||||
GLclampx b, GLclampx alpha) const;
|
GLclampx b, GLclampx alpha) const;
|
||||||
void clearWithOpenGL(const Region& clip) const;
|
void clearWithOpenGL(const Region& clip) const;
|
||||||
void drawWithOpenGL(const Region& clip, const Texture& texture) const;
|
void drawWithOpenGL(const Region& clip, const Texture& texture) const;
|
||||||
void loadTexture(Texture* texture,
|
|
||||||
const Region& dirty, const GGLSurface& t) const;
|
|
||||||
status_t initializeEglImage(
|
|
||||||
const sp<GraphicBuffer>& buffer, Texture* texture);
|
|
||||||
|
|
||||||
bool isSupportedYuvFormat(int format) const;
|
|
||||||
|
|
||||||
sp<SurfaceFlinger> mFlinger;
|
sp<SurfaceFlinger> mFlinger;
|
||||||
uint32_t mFlags;
|
uint32_t mFlags;
|
||||||
@ -294,10 +270,6 @@ class LayerBaseClient : public LayerBase
|
|||||||
public:
|
public:
|
||||||
class Surface;
|
class Surface;
|
||||||
|
|
||||||
// lcblk is (almost) only accessed from the main SF thread, in the places
|
|
||||||
// where it's not, a reference to Client must be held
|
|
||||||
SharedBufferServer* lcblk;
|
|
||||||
|
|
||||||
LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
|
LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
|
||||||
const sp<Client>& client, int32_t i);
|
const sp<Client>& client, int32_t i);
|
||||||
virtual ~LayerBaseClient();
|
virtual ~LayerBaseClient();
|
||||||
@ -311,7 +283,6 @@ public:
|
|||||||
sp<Surface> getSurface();
|
sp<Surface> getSurface();
|
||||||
virtual sp<Surface> createSurface() const;
|
virtual sp<Surface> createSurface() const;
|
||||||
virtual ssize_t serverIndex() const;
|
virtual ssize_t serverIndex() const;
|
||||||
virtual void onRemoved();
|
|
||||||
virtual const char* getTypeId() const { return "LayerBaseClient"; }
|
virtual const char* getTypeId() const { return "LayerBaseClient"; }
|
||||||
|
|
||||||
class Surface : public BnSurface
|
class Surface : public BnSurface
|
||||||
|
@ -328,7 +328,7 @@ bool LayerBuffer::Source::transformed() const {
|
|||||||
LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
|
LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
|
||||||
const ISurface::BufferHeap& buffers)
|
const ISurface::BufferHeap& buffers)
|
||||||
: Source(layer), mStatus(NO_ERROR), mBufferSize(0),
|
: Source(layer), mStatus(NO_ERROR), mBufferSize(0),
|
||||||
mUseEGLImageDirectly(true)
|
mTextureManager(layer.mFlags)
|
||||||
{
|
{
|
||||||
if (buffers.heap == NULL) {
|
if (buffers.heap == NULL) {
|
||||||
// this is allowed, but in this case, it is illegal to receive
|
// this is allowed, but in this case, it is illegal to receive
|
||||||
@ -460,35 +460,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
|
|||||||
NativeBuffer src(ourBuffer->getBuffer());
|
NativeBuffer src(ourBuffer->getBuffer());
|
||||||
const Rect transformedBounds(mLayer.getTransformedBounds());
|
const Rect transformedBounds(mLayer.getTransformedBounds());
|
||||||
|
|
||||||
if (UNLIKELY(mTexture.name == -1LU)) {
|
|
||||||
mTexture.name = mLayer.createTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(EGL_ANDROID_image_native_buffer)
|
#if defined(EGL_ANDROID_image_native_buffer)
|
||||||
if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
|
if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
|
||||||
err = INVALID_OPERATION;
|
err = INVALID_OPERATION;
|
||||||
if (ourBuffer->supportsCopybit()) {
|
if (ourBuffer->supportsCopybit()) {
|
||||||
|
|
||||||
// there are constraints on buffers used by the GPU and these may not
|
|
||||||
// be honored here. We need to change the API so the buffers
|
|
||||||
// are allocated with gralloc. For now disable this code-path
|
|
||||||
#if 0
|
|
||||||
// First, try to use the buffer as an EGLImage directly
|
|
||||||
if (mUseEGLImageDirectly) {
|
|
||||||
// NOTE: Assume the buffer is allocated with the proper USAGE flags
|
|
||||||
|
|
||||||
sp<GraphicBuffer> buffer = new GraphicBuffer(
|
|
||||||
src.img.w, src.img.h, src.img.format,
|
|
||||||
GraphicBuffer::USAGE_HW_TEXTURE,
|
|
||||||
src.img.w, src.img.handle, false);
|
|
||||||
|
|
||||||
err = mLayer.initializeEglImage(buffer, &mTexture);
|
|
||||||
if (err != NO_ERROR) {
|
|
||||||
mUseEGLImageDirectly = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
copybit_device_t* copybit = mLayer.mBlitEngine;
|
copybit_device_t* copybit = mLayer.mBlitEngine;
|
||||||
if (copybit && err != NO_ERROR) {
|
if (copybit && err != NO_ERROR) {
|
||||||
// create our EGLImageKHR the first time
|
// create our EGLImageKHR the first time
|
||||||
@ -525,7 +500,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
|
|||||||
t.format = src.img.format;
|
t.format = src.img.format;
|
||||||
t.data = (GGLubyte*)src.img.base;
|
t.data = (GGLubyte*)src.img.base;
|
||||||
const Region dirty(Rect(t.width, t.height));
|
const Region dirty(Rect(t.width, t.height));
|
||||||
mLayer.loadTexture(&mTexture, dirty, t);
|
mTextureManager.loadTexture(&mTexture, dirty, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
mTexture.transform = mBufferHeap.transform;
|
mTexture.transform = mBufferHeap.transform;
|
||||||
@ -591,7 +566,8 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
|
|||||||
dst.crop.r = w;
|
dst.crop.r = w;
|
||||||
dst.crop.b = h;
|
dst.crop.b = h;
|
||||||
|
|
||||||
err = mLayer.initializeEglImage(buffer, &mTexture);
|
EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
|
||||||
|
err = mTextureManager.initEglImage(&mTexture, dpy, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@ -607,7 +583,6 @@ void LayerBuffer::BufferSource::clearTempBufferImage() const
|
|||||||
glDeleteTextures(1, &mTexture.name);
|
glDeleteTextures(1, &mTexture.name);
|
||||||
Texture defaultTexture;
|
Texture defaultTexture;
|
||||||
mTexture = defaultTexture;
|
mTexture = defaultTexture;
|
||||||
mTexture.name = mLayer.createTexture();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "LayerBase.h"
|
#include "LayerBase.h"
|
||||||
|
#include "TextureManager.h"
|
||||||
|
|
||||||
struct copybit_device_t;
|
struct copybit_device_t;
|
||||||
|
|
||||||
@ -139,9 +140,9 @@ private:
|
|||||||
status_t mStatus;
|
status_t mStatus;
|
||||||
ISurface::BufferHeap mBufferHeap;
|
ISurface::BufferHeap mBufferHeap;
|
||||||
size_t mBufferSize;
|
size_t mBufferSize;
|
||||||
mutable LayerBase::Texture mTexture;
|
mutable Texture mTexture;
|
||||||
mutable NativeBuffer mTempBuffer;
|
mutable NativeBuffer mTempBuffer;
|
||||||
mutable bool mUseEGLImageDirectly;
|
mutable TextureManager mTextureManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OverlaySource : public Source {
|
class OverlaySource : public Source {
|
||||||
|
242
libs/surfaceflinger/TextureManager.cpp
Normal file
242
libs/surfaceflinger/TextureManager.cpp
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <utils/Errors.h>
|
||||||
|
#include <utils/Log.h>
|
||||||
|
|
||||||
|
#include <ui/GraphicBuffer.h>
|
||||||
|
|
||||||
|
#include <GLES/gl.h>
|
||||||
|
#include <GLES/glext.h>
|
||||||
|
|
||||||
|
#include <hardware/hardware.h>
|
||||||
|
|
||||||
|
#include "clz.h"
|
||||||
|
#include "DisplayHardware/DisplayHardware.h"
|
||||||
|
#include "TextureManager.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TextureManager::TextureManager(uint32_t flags)
|
||||||
|
: mFlags(flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint TextureManager::createTexture()
|
||||||
|
{
|
||||||
|
GLuint textureName = -1;
|
||||||
|
glGenTextures(1, &textureName);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
||||||
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
return textureName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureManager::isSupportedYuvFormat(int format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
||||||
|
case HAL_PIXEL_FORMAT_YCbCr_420_SP:
|
||||||
|
case HAL_PIXEL_FORMAT_YCbCr_422_P:
|
||||||
|
case HAL_PIXEL_FORMAT_YCbCr_420_P:
|
||||||
|
case HAL_PIXEL_FORMAT_YCbCr_422_I:
|
||||||
|
case HAL_PIXEL_FORMAT_YCbCr_420_I:
|
||||||
|
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t TextureManager::initEglImage(Texture* texture,
|
||||||
|
EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
|
||||||
|
{
|
||||||
|
status_t err = NO_ERROR;
|
||||||
|
if (!texture->dirty) return err;
|
||||||
|
|
||||||
|
// free the previous image
|
||||||
|
if (texture->image != EGL_NO_IMAGE_KHR) {
|
||||||
|
eglDestroyImageKHR(dpy, texture->image);
|
||||||
|
texture->image = EGL_NO_IMAGE_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct an EGL_NATIVE_BUFFER_ANDROID
|
||||||
|
android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
|
||||||
|
|
||||||
|
// create the new EGLImageKHR
|
||||||
|
const EGLint attrs[] = {
|
||||||
|
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
||||||
|
EGL_NONE, EGL_NONE
|
||||||
|
};
|
||||||
|
texture->image = eglCreateImageKHR(
|
||||||
|
dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
|
||||||
|
(EGLClientBuffer)clientBuf, attrs);
|
||||||
|
|
||||||
|
if (texture->image != EGL_NO_IMAGE_KHR) {
|
||||||
|
if (texture->name == -1UL) {
|
||||||
|
texture->name = createTexture();
|
||||||
|
texture->width = 0;
|
||||||
|
texture->height = 0;
|
||||||
|
}
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture->name);
|
||||||
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
|
||||||
|
(GLeglImageOES)texture->image);
|
||||||
|
GLint error = glGetError();
|
||||||
|
if (error != GL_NO_ERROR) {
|
||||||
|
LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
|
||||||
|
texture->image, error);
|
||||||
|
err = INVALID_OPERATION;
|
||||||
|
} else {
|
||||||
|
// Everything went okay!
|
||||||
|
texture->NPOTAdjust = false;
|
||||||
|
texture->dirty = false;
|
||||||
|
texture->width = clientBuf->width;
|
||||||
|
texture->height = clientBuf->height;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
|
||||||
|
err = INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t TextureManager::loadTexture(Texture* texture,
|
||||||
|
const Region& dirty, const GGLSurface& t)
|
||||||
|
{
|
||||||
|
if (texture->name == -1UL) {
|
||||||
|
texture->name = createTexture();
|
||||||
|
texture->width = 0;
|
||||||
|
texture->height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture->name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In OpenGL ES we can't specify a stride with glTexImage2D (however,
|
||||||
|
* GL_UNPACK_ALIGNMENT is a limited form of stride).
|
||||||
|
* So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
|
||||||
|
* need to do something reasonable (here creating a bigger texture).
|
||||||
|
*
|
||||||
|
* extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
|
||||||
|
*
|
||||||
|
* This situation doesn't happen often, but some h/w have a limitation
|
||||||
|
* for their framebuffer (eg: must be multiple of 8 pixels), and
|
||||||
|
* we need to take that into account when using these buffers as
|
||||||
|
* textures.
|
||||||
|
*
|
||||||
|
* This should never be a problem with POT textures
|
||||||
|
*/
|
||||||
|
|
||||||
|
int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
|
||||||
|
unpack = 1 << ((unpack > 3) ? 3 : unpack);
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* round to POT if needed
|
||||||
|
*/
|
||||||
|
if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
|
||||||
|
texture->NPOTAdjust = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture->NPOTAdjust) {
|
||||||
|
// find the smallest power-of-two that will accommodate our surface
|
||||||
|
texture->potWidth = 1 << (31 - clz(t.width));
|
||||||
|
texture->potHeight = 1 << (31 - clz(t.height));
|
||||||
|
if (texture->potWidth < t.width) texture->potWidth <<= 1;
|
||||||
|
if (texture->potHeight < t.height) texture->potHeight <<= 1;
|
||||||
|
texture->wScale = float(t.width) / texture->potWidth;
|
||||||
|
texture->hScale = float(t.height) / texture->potHeight;
|
||||||
|
} else {
|
||||||
|
texture->potWidth = t.width;
|
||||||
|
texture->potHeight = t.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect bounds(dirty.bounds());
|
||||||
|
GLvoid* data = 0;
|
||||||
|
if (texture->width != t.width || texture->height != t.height) {
|
||||||
|
texture->width = t.width;
|
||||||
|
texture->height = t.height;
|
||||||
|
|
||||||
|
// texture size changed, we need to create a new one
|
||||||
|
bounds.set(Rect(t.width, t.height));
|
||||||
|
if (t.width == texture->potWidth &&
|
||||||
|
t.height == texture->potHeight) {
|
||||||
|
// we can do it one pass
|
||||||
|
data = t.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
GL_RGB, texture->potWidth, texture->potHeight, 0,
|
||||||
|
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
|
||||||
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
GL_RGBA, texture->potWidth, texture->potHeight, 0,
|
||||||
|
GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
|
||||||
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
|
||||||
|
t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
GL_RGBA, texture->potWidth, texture->potHeight, 0,
|
||||||
|
GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||||
|
} else if (isSupportedYuvFormat(t.format)) {
|
||||||
|
// just show the Y plane of YUV buffers
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
|
||||||
|
GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
|
||||||
|
} else {
|
||||||
|
// oops, we don't handle this format!
|
||||||
|
LOGE("texture=%d, using format %d, which is not "
|
||||||
|
"supported by the GL", texture->name, t.format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
0, bounds.top, t.width, bounds.height(),
|
||||||
|
GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
|
||||||
|
t.data + bounds.top*t.stride*2);
|
||||||
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
0, bounds.top, t.width, bounds.height(),
|
||||||
|
GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
|
||||||
|
t.data + bounds.top*t.stride*2);
|
||||||
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
|
||||||
|
t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
0, bounds.top, t.width, bounds.height(),
|
||||||
|
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||||
|
t.data + bounds.top*t.stride*4);
|
||||||
|
} else if (isSupportedYuvFormat(t.format)) {
|
||||||
|
// just show the Y plane of YUV buffers
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||||
|
0, bounds.top, t.width, bounds.height(),
|
||||||
|
GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
||||||
|
t.data + bounds.top*t.stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
}; // namespace android
|
79
libs/surfaceflinger/TextureManager.h
Normal file
79
libs/surfaceflinger/TextureManager.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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_TEXTURE_MANAGER_H
|
||||||
|
#define ANDROID_TEXTURE_MANAGER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
#include <GLES/gl.h>
|
||||||
|
|
||||||
|
#include <ui/Region.h>
|
||||||
|
|
||||||
|
#include <pixelflinger/pixelflinger.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class GraphicBuffer;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct Texture {
|
||||||
|
Texture() : name(-1U), width(0), height(0),
|
||||||
|
image(EGL_NO_IMAGE_KHR), transform(0),
|
||||||
|
NPOTAdjust(false), dirty(true) { }
|
||||||
|
GLuint name;
|
||||||
|
GLuint width;
|
||||||
|
GLuint height;
|
||||||
|
GLuint potWidth;
|
||||||
|
GLuint potHeight;
|
||||||
|
GLfloat wScale;
|
||||||
|
GLfloat hScale;
|
||||||
|
EGLImageKHR image;
|
||||||
|
uint32_t transform;
|
||||||
|
bool NPOTAdjust;
|
||||||
|
bool dirty;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TextureManager {
|
||||||
|
uint32_t mFlags;
|
||||||
|
GLuint createTexture();
|
||||||
|
static bool isSupportedYuvFormat(int format);
|
||||||
|
public:
|
||||||
|
|
||||||
|
TextureManager(uint32_t flags);
|
||||||
|
|
||||||
|
// load bitmap data into the active buffer
|
||||||
|
status_t loadTexture(Texture* texture,
|
||||||
|
const Region& dirty, const GGLSurface& t);
|
||||||
|
|
||||||
|
// make active buffer an EGLImage if needed
|
||||||
|
status_t initEglImage(Texture* texture,
|
||||||
|
EGLDisplay dpy, const sp<GraphicBuffer>& buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
}; // namespace android
|
||||||
|
|
||||||
|
#endif // ANDROID_TEXTURE_MANAGER_H
|
@ -513,6 +513,7 @@ int32_t SharedBufferServer::getQueuedCount() const
|
|||||||
|
|
||||||
status_t SharedBufferServer::assertReallocate(int buf)
|
status_t SharedBufferServer::assertReallocate(int buf)
|
||||||
{
|
{
|
||||||
|
// TODO: need to validate "buf"
|
||||||
ReallocateCondition condition(this, buf);
|
ReallocateCondition condition(this, buf);
|
||||||
status_t err = waitForCondition(condition);
|
status_t err = waitForCondition(condition);
|
||||||
return err;
|
return err;
|
||||||
|
Reference in New Issue
Block a user