f9cd64bc6c
The current gralloc allocates buffer memory for render targets that will typically have NPOT dimensions. Assuming that the vendor driver supports converting the resulting NPOT android_native_buffer_t to a NPOT EGLImage, SurfaceFlinger calls glEGLImageTargetTexture2DOES(), and uses glGetError() to test whether the GL can support creating an EGL target texture with the specified NPOT EGLImage. If it is supported, the DIRECT_TEXTURE flag remains set, otherwise it is cleared. Tangentially, if the driver advertises the GL_ARB_texture_non_power_of_two extension, the NPOT_EXTENSION flag is set, otherwise it is cleared. If the driver supported creating an EGL target texture from a NPOT source EGLImage, it implicitly creates a NPOT texture. This does not need any glScalef() texture coordinate correction in LayerBase::drawWithOpenGL(). However, the same driver may not advertise the GL_ARB_texture_non_power_of_two extension nor generally support NPOT textures that were not derived from EGLImages. So SurfaceFlinger may flag only DIRECT_TEXTURE, not NPOT_EXTENSION. Therefore, the test in LayerBase::drawWithOpenGL() should only perform the glScalef() if neither NPOT_EXTENSION or DIRECT_TEXTURE are flagged. Otherwise scaling is applied to NPOT EGL target textures when none is required.
790 lines
25 KiB
C++
790 lines
25 KiB
C++
/*
|
|
* Copyright (C) 2007 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 <binder/IPCThreadState.h>
|
|
#include <binder/IServiceManager.h>
|
|
|
|
#include <GLES/gl.h>
|
|
#include <GLES/glext.h>
|
|
|
|
#include <hardware/hardware.h>
|
|
|
|
#include "clz.h"
|
|
#include "LayerBase.h"
|
|
#include "LayerBlur.h"
|
|
#include "SurfaceFlinger.h"
|
|
#include "DisplayHardware/DisplayHardware.h"
|
|
|
|
|
|
// We don't honor the premultiplied alpha flags, which means that
|
|
// premultiplied surface may be composed using a non-premultiplied
|
|
// equation. We do this because it may be a lot faster on some hardware
|
|
// The correct value is HONOR_PREMULTIPLIED_ALPHA = 1
|
|
#define HONOR_PREMULTIPLIED_ALPHA 0
|
|
|
|
namespace android {
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const uint32_t LayerBase::typeInfo = 1;
|
|
const char* const LayerBase::typeID = "LayerBase";
|
|
|
|
const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2;
|
|
const char* const LayerBaseClient::typeID = "LayerBaseClient";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
|
|
: dpy(display), contentDirty(false),
|
|
mFlinger(flinger),
|
|
mTransformed(false),
|
|
mOrientation(0),
|
|
mTransactionFlags(0),
|
|
mPremultipliedAlpha(true),
|
|
mInvalidate(0)
|
|
{
|
|
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
|
|
mFlags = hw.getFlags();
|
|
}
|
|
|
|
LayerBase::~LayerBase()
|
|
{
|
|
}
|
|
|
|
const GraphicPlane& LayerBase::graphicPlane(int dpy) const
|
|
{
|
|
return mFlinger->graphicPlane(dpy);
|
|
}
|
|
|
|
GraphicPlane& LayerBase::graphicPlane(int dpy)
|
|
{
|
|
return mFlinger->graphicPlane(dpy);
|
|
}
|
|
|
|
void LayerBase::initStates(uint32_t w, uint32_t h, uint32_t flags)
|
|
{
|
|
uint32_t layerFlags = 0;
|
|
if (flags & ISurfaceComposer::eHidden)
|
|
layerFlags = ISurfaceComposer::eLayerHidden;
|
|
|
|
if (flags & ISurfaceComposer::eNonPremultiplied)
|
|
mPremultipliedAlpha = false;
|
|
|
|
mCurrentState.z = 0;
|
|
mCurrentState.w = w;
|
|
mCurrentState.h = h;
|
|
mCurrentState.alpha = 0xFF;
|
|
mCurrentState.flags = layerFlags;
|
|
mCurrentState.sequence = 0;
|
|
mCurrentState.transform.set(0, 0);
|
|
|
|
// drawing state & current state are identical
|
|
mDrawingState = mCurrentState;
|
|
}
|
|
|
|
void LayerBase::commitTransaction(bool skipSize) {
|
|
const uint32_t w = mDrawingState.w;
|
|
const uint32_t h = mDrawingState.h;
|
|
mDrawingState = mCurrentState;
|
|
if (skipSize) {
|
|
mDrawingState.w = w;
|
|
mDrawingState.h = h;
|
|
}
|
|
}
|
|
void LayerBase::forceVisibilityTransaction() {
|
|
// this can be called without SurfaceFlinger.mStateLock, but if we
|
|
// can atomically increment the sequence number, it doesn't matter.
|
|
android_atomic_inc(&mCurrentState.sequence);
|
|
requestTransaction();
|
|
}
|
|
bool LayerBase::requestTransaction() {
|
|
int32_t old = setTransactionFlags(eTransactionNeeded);
|
|
return ((old & eTransactionNeeded) == 0);
|
|
}
|
|
uint32_t LayerBase::getTransactionFlags(uint32_t flags) {
|
|
return android_atomic_and(~flags, &mTransactionFlags) & flags;
|
|
}
|
|
uint32_t LayerBase::setTransactionFlags(uint32_t flags) {
|
|
return android_atomic_or(flags, &mTransactionFlags);
|
|
}
|
|
|
|
void LayerBase::setSizeChanged(uint32_t w, uint32_t h) {
|
|
}
|
|
|
|
bool LayerBase::setPosition(int32_t x, int32_t y) {
|
|
if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y)
|
|
return false;
|
|
mCurrentState.sequence++;
|
|
mCurrentState.transform.set(x, y);
|
|
requestTransaction();
|
|
return true;
|
|
}
|
|
bool LayerBase::setLayer(uint32_t z) {
|
|
if (mCurrentState.z == z)
|
|
return false;
|
|
mCurrentState.sequence++;
|
|
mCurrentState.z = z;
|
|
requestTransaction();
|
|
return true;
|
|
}
|
|
bool LayerBase::setSize(uint32_t w, uint32_t h) {
|
|
if (mCurrentState.w == w && mCurrentState.h == h)
|
|
return false;
|
|
setSizeChanged(w, h);
|
|
mCurrentState.w = w;
|
|
mCurrentState.h = h;
|
|
requestTransaction();
|
|
return true;
|
|
}
|
|
bool LayerBase::setAlpha(uint8_t alpha) {
|
|
if (mCurrentState.alpha == alpha)
|
|
return false;
|
|
mCurrentState.sequence++;
|
|
mCurrentState.alpha = alpha;
|
|
requestTransaction();
|
|
return true;
|
|
}
|
|
bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) {
|
|
// TODO: check the matrix has changed
|
|
mCurrentState.sequence++;
|
|
mCurrentState.transform.set(
|
|
matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
|
|
requestTransaction();
|
|
return true;
|
|
}
|
|
bool LayerBase::setTransparentRegionHint(const Region& transparent) {
|
|
// TODO: check the region has changed
|
|
mCurrentState.sequence++;
|
|
mCurrentState.transparentRegion = transparent;
|
|
requestTransaction();
|
|
return true;
|
|
}
|
|
bool LayerBase::setFlags(uint8_t flags, uint8_t mask) {
|
|
const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
|
|
if (mCurrentState.flags == newFlags)
|
|
return false;
|
|
mCurrentState.sequence++;
|
|
mCurrentState.flags = newFlags;
|
|
requestTransaction();
|
|
return true;
|
|
}
|
|
|
|
Rect LayerBase::visibleBounds() const
|
|
{
|
|
return mTransformedBounds;
|
|
}
|
|
|
|
void LayerBase::setVisibleRegion(const Region& visibleRegion) {
|
|
// always called from main thread
|
|
visibleRegionScreen = visibleRegion;
|
|
}
|
|
|
|
void LayerBase::setCoveredRegion(const Region& coveredRegion) {
|
|
// always called from main thread
|
|
coveredRegionScreen = coveredRegion;
|
|
}
|
|
|
|
uint32_t LayerBase::doTransaction(uint32_t flags)
|
|
{
|
|
const Layer::State& front(drawingState());
|
|
const Layer::State& temp(currentState());
|
|
|
|
if (temp.sequence != front.sequence) {
|
|
// invalidate and recompute the visible regions if needed
|
|
flags |= eVisibleRegion;
|
|
this->contentDirty = true;
|
|
}
|
|
|
|
// Commit the transaction
|
|
commitTransaction(flags & eRestartTransaction);
|
|
return flags;
|
|
}
|
|
|
|
Point LayerBase::getPhysicalSize() const
|
|
{
|
|
const Layer::State& front(drawingState());
|
|
return Point(front.w, front.h);
|
|
}
|
|
|
|
void LayerBase::validateVisibility(const Transform& planeTransform)
|
|
{
|
|
const Layer::State& s(drawingState());
|
|
const Transform tr(planeTransform * s.transform);
|
|
const bool transformed = tr.transformed();
|
|
|
|
const Point size(getPhysicalSize());
|
|
uint32_t w = size.x;
|
|
uint32_t h = size.y;
|
|
tr.transform(mVertices[0], 0, 0);
|
|
tr.transform(mVertices[1], 0, h);
|
|
tr.transform(mVertices[2], w, h);
|
|
tr.transform(mVertices[3], w, 0);
|
|
if (UNLIKELY(transformed)) {
|
|
// NOTE: here we could also punt if we have too many rectangles
|
|
// in the transparent region
|
|
if (tr.preserveRects()) {
|
|
// transform the transparent region
|
|
transparentRegionScreen = tr.transform(s.transparentRegion);
|
|
} else {
|
|
// transformation too complex, can't do the transparent region
|
|
// optimization.
|
|
transparentRegionScreen.clear();
|
|
}
|
|
} else {
|
|
transparentRegionScreen = s.transparentRegion;
|
|
}
|
|
|
|
// cache a few things...
|
|
mOrientation = tr.getOrientation();
|
|
mTransformedBounds = tr.makeBounds(w, h);
|
|
mTransformed = transformed;
|
|
mLeft = tr.tx();
|
|
mTop = tr.ty();
|
|
}
|
|
|
|
void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
|
|
{
|
|
}
|
|
|
|
void LayerBase::unlockPageFlip(
|
|
const Transform& planeTransform, Region& outDirtyRegion)
|
|
{
|
|
if ((android_atomic_and(~1, &mInvalidate)&1) == 1) {
|
|
outDirtyRegion.orSelf(visibleRegionScreen);
|
|
}
|
|
}
|
|
|
|
void LayerBase::finishPageFlip()
|
|
{
|
|
}
|
|
|
|
void LayerBase::invalidate()
|
|
{
|
|
if ((android_atomic_or(1, &mInvalidate)&1) == 0) {
|
|
mFlinger->signalEvent();
|
|
}
|
|
}
|
|
|
|
void LayerBase::drawRegion(const Region& reg) const
|
|
{
|
|
Region::const_iterator it = reg.begin();
|
|
Region::const_iterator const end = reg.end();
|
|
if (it != end) {
|
|
Rect r;
|
|
const DisplayHardware& hw(graphicPlane(0).displayHardware());
|
|
const int32_t fbWidth = hw.getWidth();
|
|
const int32_t fbHeight = hw.getHeight();
|
|
const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 },
|
|
{ fbWidth, fbHeight }, { 0, fbHeight } };
|
|
glVertexPointer(2, GL_SHORT, 0, vertices);
|
|
while (it != end) {
|
|
const Rect& r = *it++;
|
|
const GLint sy = fbHeight - (r.top + r.height());
|
|
glScissor(r.left, sy, r.width(), r.height());
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LayerBase::draw(const Region& inClip) const
|
|
{
|
|
// invalidate the region we'll update
|
|
Region clip(inClip); // copy-on-write, so no-op most of the time
|
|
|
|
// Remove the transparent area from the clipping region
|
|
const State& s = drawingState();
|
|
if (LIKELY(!s.transparentRegion.isEmpty())) {
|
|
clip.subtract(transparentRegionScreen);
|
|
if (clip.isEmpty()) {
|
|
// usually this won't happen because this should be taken care of
|
|
// by SurfaceFlinger::computeVisibleRegions()
|
|
return;
|
|
}
|
|
}
|
|
|
|
// reset GL state
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
onDraw(clip);
|
|
|
|
/*
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_DITHER);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
glColor4x(0, 0x8000, 0, 0x10000);
|
|
drawRegion(transparentRegionScreen);
|
|
glDisable(GL_BLEND);
|
|
*/
|
|
}
|
|
|
|
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);
|
|
if (mFlags & DisplayHardware::SLOW_CONFIG) {
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
} else {
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
return textureName;
|
|
}
|
|
|
|
void LayerBase::clearWithOpenGL(const Region& clip) const
|
|
{
|
|
const DisplayHardware& hw(graphicPlane(0).displayHardware());
|
|
const uint32_t fbHeight = hw.getHeight();
|
|
glColor4x(0,0,0,0);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_DITHER);
|
|
|
|
Region::const_iterator it = clip.begin();
|
|
Region::const_iterator const end = clip.end();
|
|
if (it != end) {
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glVertexPointer(2, GL_FIXED, 0, mVertices);
|
|
while (it != end) {
|
|
const Rect& r = *it++;
|
|
const GLint sy = fbHeight - (r.top + r.height());
|
|
glScissor(r.left, sy, r.width(), r.height());
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
|
|
{
|
|
const DisplayHardware& hw(graphicPlane(0).displayHardware());
|
|
const uint32_t fbHeight = hw.getHeight();
|
|
const State& s(drawingState());
|
|
|
|
// bind our texture
|
|
validateTexture(texture.name);
|
|
uint32_t width = texture.width;
|
|
uint32_t height = texture.height;
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
// Dithering...
|
|
if (s.flags & ISurfaceComposer::eLayerDither) {
|
|
glEnable(GL_DITHER);
|
|
} else {
|
|
glDisable(GL_DITHER);
|
|
}
|
|
|
|
if (UNLIKELY(s.alpha < 0xFF)) {
|
|
// We have an alpha-modulation. We need to modulate all
|
|
// texture components by alpha because we're always using
|
|
// premultiplied alpha.
|
|
|
|
// If the texture doesn't have an alpha channel we can
|
|
// use REPLACE and switch to non premultiplied alpha
|
|
// blending (SRCA/ONE_MINUS_SRCA).
|
|
|
|
GLenum env, src;
|
|
if (needsBlending()) {
|
|
env = GL_MODULATE;
|
|
src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
|
|
} else {
|
|
env = GL_REPLACE;
|
|
src = GL_SRC_ALPHA;
|
|
}
|
|
const GGLfixed alpha = (s.alpha << 16)/255;
|
|
glColor4x(alpha, alpha, alpha, alpha);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
|
|
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env);
|
|
} else {
|
|
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
|
|
if (needsBlending()) {
|
|
GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
|
|
} else {
|
|
glDisable(GL_BLEND);
|
|
}
|
|
}
|
|
|
|
if (UNLIKELY(transformed()
|
|
|| !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) ))
|
|
{
|
|
//StopWatch watch("GL transformed");
|
|
Region::const_iterator it = clip.begin();
|
|
Region::const_iterator const end = clip.end();
|
|
if (it != end) {
|
|
// always use high-quality filtering with fast configurations
|
|
bool fast = !(mFlags & DisplayHardware::SLOW_CONFIG);
|
|
if (!fast && s.flags & ISurfaceComposer::eLayerFilter) {
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
const GLfixed texCoords[4][2] = {
|
|
{ 0, 0 },
|
|
{ 0, 0x10000 },
|
|
{ 0x10000, 0x10000 },
|
|
{ 0x10000, 0 }
|
|
};
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
glLoadIdentity();
|
|
|
|
// the texture's source is rotated
|
|
if (texture.transform == HAL_TRANSFORM_ROT_90) {
|
|
// TODO: handle the other orientations
|
|
glTranslatef(0, 1, 0);
|
|
glRotatef(-90, 0, 0, 1);
|
|
}
|
|
|
|
if (!(mFlags & (DisplayHardware::NPOT_EXTENSION |
|
|
DisplayHardware::DIRECT_TEXTURE))) {
|
|
// find the smallest power-of-two that will accommodate our surface
|
|
GLuint tw = 1 << (31 - clz(width));
|
|
GLuint th = 1 << (31 - clz(height));
|
|
if (tw < width) tw <<= 1;
|
|
if (th < height) th <<= 1;
|
|
// this divide should be relatively fast because it's
|
|
// a power-of-two (optimized path in libgcc)
|
|
GLfloat ws = GLfloat(width) /tw;
|
|
GLfloat hs = GLfloat(height)/th;
|
|
glScalef(ws, hs, 1.0f);
|
|
}
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glVertexPointer(2, GL_FIXED, 0, mVertices);
|
|
glTexCoordPointer(2, GL_FIXED, 0, texCoords);
|
|
|
|
while (it != end) {
|
|
const Rect& r = *it++;
|
|
const GLint sy = fbHeight - (r.top + r.height());
|
|
glScissor(r.left, sy, r.width(), r.height());
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
}
|
|
|
|
if (!fast && s.flags & ISurfaceComposer::eLayerFilter) {
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
}
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
} else {
|
|
Region::const_iterator it = clip.begin();
|
|
Region::const_iterator const end = clip.end();
|
|
if (it != end) {
|
|
GLint crop[4] = { 0, height, width, -height };
|
|
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
|
|
int x = tx();
|
|
int y = ty();
|
|
y = fbHeight - (y + height);
|
|
while (it != end) {
|
|
const Rect& r = *it++;
|
|
const GLint sy = fbHeight - (r.top + r.height());
|
|
glScissor(r.left, sy, r.width(), r.height());
|
|
glDrawTexiOES(x, y, 0, width, height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LayerBase::validateTexture(GLint textureName) const
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
// TODO: reload the texture if needed
|
|
// this is currently done in loadTexture() below
|
|
}
|
|
|
|
void LayerBase::loadTexture(Texture* texture, GLint textureName,
|
|
const Region& dirty, const GGLSurface& t) const
|
|
{
|
|
// TODO: defer the actual texture reload until LayerBase::validateTexture
|
|
// is called.
|
|
|
|
texture->name = textureName;
|
|
GLuint& textureWidth(texture->width);
|
|
GLuint& textureHeight(texture->height);
|
|
|
|
uint32_t flags = mFlags;
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
|
|
GLuint tw = t.width;
|
|
GLuint th = t.height;
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
GLuint texture_w = tw;
|
|
GLuint texture_h = th;
|
|
if (!(flags & DisplayHardware::NPOT_EXTENSION)) {
|
|
// find the smallest power-of-two that will accommodate our surface
|
|
texture_w = 1 << (31 - clz(t.width));
|
|
texture_h = 1 << (31 - clz(t.height));
|
|
if (texture_w < t.width) texture_w <<= 1;
|
|
if (texture_h < t.height) texture_h <<= 1;
|
|
}
|
|
|
|
regular:
|
|
Rect bounds(dirty.bounds());
|
|
GLvoid* data = 0;
|
|
if (texture_w!=textureWidth || texture_h!=textureHeight) {
|
|
// texture size changed, we need to create a new one
|
|
|
|
if (!textureWidth || !textureHeight) {
|
|
// this is the first time, load the whole texture
|
|
if (texture_w==tw && texture_h==th) {
|
|
// we can do it one pass
|
|
data = t.data;
|
|
} else {
|
|
// we have to create the texture first because it
|
|
// doesn't match the size of the buffer
|
|
bounds.set(Rect(tw, th));
|
|
}
|
|
}
|
|
|
|
if (t.format == GGL_PIXEL_FORMAT_RGB_565) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
GL_RGB, texture_w, texture_h, 0,
|
|
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
|
|
} else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
GL_RGBA, texture_w, texture_h, 0,
|
|
GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
|
|
} else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
GL_RGBA, texture_w, texture_h, 0,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
} else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP ||
|
|
t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) {
|
|
// just show the Y plane of YUV buffers
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
GL_LUMINANCE, texture_w, texture_h, 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, textureName, t.format);
|
|
textureName = -1;
|
|
}
|
|
textureWidth = texture_w;
|
|
textureHeight = texture_h;
|
|
}
|
|
if (!data && textureName>=0) {
|
|
if (t.format == GGL_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 == GGL_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 == GGL_PIXEL_FORMAT_RGBA_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 ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP ||
|
|
t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) {
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
int32_t LayerBaseClient::sIdentity = 0;
|
|
|
|
LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
|
|
const sp<Client>& client, int32_t i)
|
|
: LayerBase(flinger, display), client(client),
|
|
lcblk( client!=0 ? &(client->ctrlblk->layers[i]) : 0 ),
|
|
mIndex(i),
|
|
mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
|
|
{
|
|
}
|
|
|
|
void LayerBaseClient::onFirstRef()
|
|
{
|
|
sp<Client> client(this->client.promote());
|
|
if (client != 0) {
|
|
client->bindLayer(this, mIndex);
|
|
// Initialize this layer's control block
|
|
memset(this->lcblk, 0, sizeof(layer_cblk_t));
|
|
this->lcblk->identity = mIdentity;
|
|
Region::writeEmpty(&(this->lcblk->region[0]), sizeof(flat_region_t));
|
|
Region::writeEmpty(&(this->lcblk->region[1]), sizeof(flat_region_t));
|
|
}
|
|
}
|
|
|
|
LayerBaseClient::~LayerBaseClient()
|
|
{
|
|
sp<Client> client(this->client.promote());
|
|
if (client != 0) {
|
|
client->free(mIndex);
|
|
}
|
|
}
|
|
|
|
int32_t LayerBaseClient::serverIndex() const
|
|
{
|
|
sp<Client> client(this->client.promote());
|
|
if (client != 0) {
|
|
return (client->cid<<16)|mIndex;
|
|
}
|
|
return 0xFFFF0000 | mIndex;
|
|
}
|
|
|
|
sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
|
|
{
|
|
sp<Surface> s;
|
|
Mutex::Autolock _l(mLock);
|
|
s = mClientSurface.promote();
|
|
if (s == 0) {
|
|
s = createSurface();
|
|
mClientSurface = s;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const
|
|
{
|
|
return new Surface(mFlinger, clientIndex(), mIdentity,
|
|
const_cast<LayerBaseClient *>(this));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
LayerBaseClient::Surface::Surface(
|
|
const sp<SurfaceFlinger>& flinger,
|
|
SurfaceID id, int identity,
|
|
const sp<LayerBaseClient>& owner)
|
|
: mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner)
|
|
{
|
|
}
|
|
|
|
|
|
LayerBaseClient::Surface::~Surface()
|
|
{
|
|
/*
|
|
* This is a good place to clean-up all client resources
|
|
*/
|
|
|
|
// destroy client resources
|
|
sp<LayerBaseClient> layer = getOwner();
|
|
if (layer != 0) {
|
|
mFlinger->destroySurface(layer);
|
|
}
|
|
}
|
|
|
|
sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const {
|
|
sp<LayerBaseClient> owner(mOwner.promote());
|
|
return owner;
|
|
}
|
|
|
|
|
|
void LayerBaseClient::Surface::getSurfaceData(
|
|
ISurfaceFlingerClient::surface_data_t* params) const
|
|
{
|
|
params->token = mToken;
|
|
params->identity = mIdentity;
|
|
}
|
|
|
|
status_t LayerBaseClient::Surface::onTransact(
|
|
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
|
|
{
|
|
switch (code) {
|
|
case REGISTER_BUFFERS:
|
|
case UNREGISTER_BUFFERS:
|
|
case CREATE_OVERLAY:
|
|
{
|
|
if (!mFlinger->mAccessSurfaceFlinger.checkCalling()) {
|
|
IPCThreadState* ipc = IPCThreadState::self();
|
|
const int pid = ipc->getCallingPid();
|
|
const int uid = ipc->getCallingUid();
|
|
LOGE("Permission Denial: "
|
|
"can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
|
|
return PERMISSION_DENIED;
|
|
}
|
|
}
|
|
}
|
|
return BnSurface::onTransact(code, data, reply, flags);
|
|
}
|
|
|
|
sp<SurfaceBuffer> LayerBaseClient::Surface::getBuffer()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
status_t LayerBaseClient::Surface::registerBuffers(
|
|
const ISurface::BufferHeap& buffers)
|
|
{
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
void LayerBaseClient::Surface::postBuffer(ssize_t offset)
|
|
{
|
|
}
|
|
|
|
void LayerBaseClient::Surface::unregisterBuffers()
|
|
{
|
|
}
|
|
|
|
sp<OverlayRef> LayerBaseClient::Surface::createOverlay(
|
|
uint32_t w, uint32_t h, int32_t format)
|
|
{
|
|
return NULL;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
}; // namespace android
|