return writeStr(k_traceBufferSizePath, str);
}
+// Read the trace_clock sysfs file and return true if it matches the requested
+// value. The trace_clock file format is:
+// local [global] counter uptime perf
+static bool isTraceClock(const char *mode)
+{
+ int fd = open(k_traceClockPath, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "error opening %s: %s (%d)\n", k_traceClockPath,
+ strerror(errno), errno);
+ return false;
+ }
+
+ char buf[4097];
+ ssize_t n = read(fd, buf, 4096);
+ close(fd);
+ if (n == -1) {
+ fprintf(stderr, "error reading %s: %s (%d)\n", k_traceClockPath,
+ strerror(errno), errno);
+ return false;
+ }
+ buf[n] = '\0';
+
+ char *start = strchr(buf, '[');
+ if (start == NULL) {
+ return false;
+ }
+ start++;
+
+ char *end = strchr(start, ']');
+ if (end == NULL) {
+ return false;
+ }
+ *end = '\0';
+
+ return strcmp(mode, start) == 0;
+}
+
// Enable or disable the kernel's use of the global clock. Disabling the global
// clock will result in the kernel using a per-CPU local clock.
+// Any write to the trace_clock sysfs file will reset the buffer, so only
+// update it if the requested value is not the current value.
static bool setGlobalClockEnable(bool enable)
{
- return writeStr(k_traceClockPath, enable ? "global" : "local");
+ const char *clock = enable ? "global" : "local";
+
+ if (isTraceClock(clock)) {
+ return true;
+ }
+
+ return writeStr(k_traceClockPath, clock);
}
static bool setPrintTgidEnableIfPresent(bool enable)
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- Feature for devices with a hardware electrocardiography(ECG) sensor. -->
+<permissions>
+ <feature name="android.hardware.sensor.heartrate.ecg" />
+</permissions>
BufferedOutputStream *stream) :
mId(id),
mVersion(version),
+ mVersionMajor(0),
+ mVersionMinor(0),
+ mVersionParsed(false),
mState(state),
mBufferedOutputStream(stream),
mElementArrayBuffers(DefaultKeyedVector<GLuint, ElementArrayBuffer*>(NULL))
return mVersion;
}
+int GLTraceContext::getVersionMajor() {
+ if (!mVersionParsed) {
+ parseGlesVersion();
+ mVersionParsed = true;
+ }
+ return mVersionMajor;
+}
+
+int GLTraceContext::getVersionMinor() {
+ if (!mVersionParsed) {
+ parseGlesVersion();
+ mVersionParsed = true;
+ }
+ return mVersionMinor;
+}
+
GLTraceState *GLTraceContext::getGlobalTraceState() {
return mState;
}
+void GLTraceContext::parseGlesVersion() {
+ const char* str = (const char*)hooks->gl.glGetString(GL_VERSION);
+ int major, minor;
+ if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
+ if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
+ ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
+ major = 1;
+ minor = 0;
+ }
+ }
+ mVersionMajor = major;
+ mVersionMinor = minor;
+}
+
void GLTraceContext::resizeFBMemory(unsigned minSize) {
if (fbcontentsSize >= minSize) {
return;
class GLTraceContext {
int mId; /* unique context id */
int mVersion; /* GL version, e.g: egl_connection_t::GLESv2_INDEX */
+ int mVersionMajor; /* GL major version. Lazily parsed in getVersionX(). */
+ int mVersionMinor; /* GL minor version. Lazily parsed in getVersionX(). */
+ bool mVersionParsed; /* True if major and minor versions have been parsed. */
GLTraceState *mState; /* parent GL Trace state (for per process GL Trace State Info) */
void *fbcontents; /* memory area to read framebuffer contents */
/* list of element array buffers in use. */
DefaultKeyedVector<GLuint, ElementArrayBuffer*> mElementArrayBuffers;
+ /* Parses the GL version string returned from glGetString(GL_VERSION) to get find the major and
+ minor versions of the GLES API. The context must be current before calling. */
+ void parseGlesVersion();
void resizeFBMemory(unsigned minSize);
public:
gl_hooks_t *hooks;
GLTraceContext(int id, int version, GLTraceState *state, BufferedOutputStream *stream);
int getId();
int getVersion();
+ int getVersionMajor();
+ int getVersionMinor();
GLTraceState *getGlobalTraceState();
void getCompressedFB(void **fb, unsigned *fbsize,
unsigned *fbwidth, unsigned *fbheight,
namespace android {
namespace gltrace {
+GLint glGetInteger(GLTraceContext *context, GLenum param) {
+ GLint x;
+ context->hooks->gl.glGetIntegerv(param, &x);
+ return x;
+}
+
+GLint glGetVertexAttrib(GLTraceContext *context, GLuint index, GLenum pname) {
+ GLint x;
+ context->hooks->gl.glGetVertexAttribiv(index, pname, &x);
+ return x;
+}
+
+bool isUsingPixelBuffers(GLTraceContext *context) {
+ if (context->getVersionMajor() < 3) {
+ return false; // PBOs not supported prior to GLES 3.0
+ }
+ return glGetInteger(context, GL_PIXEL_UNPACK_BUFFER_BINDING) != 0;
+}
+
+bool isUsingArrayBuffers(GLTraceContext *context) {
+ return glGetInteger(context, GL_ARRAY_BUFFER_BINDING) != 0;
+}
+
+bool isUsingElementArrayBuffers(GLTraceContext *context) {
+ return glGetInteger(context, GL_ELEMENT_ARRAY_BUFFER_BINDING) != 0;
+}
+
unsigned getBytesPerTexel(const GLenum format, const GLenum type) {
/*
Description from glTexImage2D spec:
}
/** Common fixup routing for glTexImage2D & glTexSubImage2D. */
-void fixup_glTexImage(int widthIndex, int heightIndex, GLMessage *glmsg, void *dataSrc) {
+void fixup_glTexImage(GLTraceContext *context, int widthIndex, int heightIndex, GLMessage *glmsg,
+ void *dataSrc) {
GLMessage_DataType arg_width = glmsg->args(widthIndex);
GLMessage_DataType arg_height = glmsg->args(heightIndex);
arg_data->set_type(GLMessage::DataType::BYTE);
arg_data->clear_rawbytes();
- if (data != NULL) {
+ if (data != NULL && !isUsingPixelBuffers(context)) {
arg_data->set_isarray(true);
arg_data->add_rawbytes(data, bytesPerTexel * width * height);
} else {
}
-void fixup_glTexImage2D(GLMessage *glmsg, void *pointersToFixup[]) {
+void fixup_glTexImage2D(GLTraceContext *context, GLMessage *glmsg, void *pointersToFixup[]) {
/* void glTexImage2D(GLenum target,
GLint level,
GLint internalformat,
*/
int widthIndex = 3;
int heightIndex = 4;
- fixup_glTexImage(widthIndex, heightIndex, glmsg, pointersToFixup[0]);
+ fixup_glTexImage(context, widthIndex, heightIndex, glmsg, pointersToFixup[0]);
}
-void fixup_glTexSubImage2D(GLMessage *glmsg, void *pointersToFixup[]) {
+void fixup_glTexSubImage2D(GLTraceContext *context, GLMessage *glmsg, void *pointersToFixup[]) {
/*
void glTexSubImage2D(GLenum target,
GLint level,
*/
int widthIndex = 4;
int heightIndex = 5;
- fixup_glTexImage(widthIndex, heightIndex, glmsg, pointersToFixup[0]);
+ fixup_glTexImage(context, widthIndex, heightIndex, glmsg, pointersToFixup[0]);
}
-void fixup_glCompressedTexImage2D(GLMessage *glmsg, void *pointersToFixup[]) {
+void fixup_glCompressedTexImage2D(GLTraceContext *context, GLMessage *glmsg,
+ void *pointersToFixup[]) {
/* void glCompressedTexImage2D(GLenum target,
GLint level,
GLenum internalformat,
arg_data->set_type(GLMessage::DataType::BYTE);
arg_data->clear_rawbytes();
- if (data != NULL) {
+ if (data != NULL && !isUsingPixelBuffers(context)) {
arg_data->set_isarray(true);
arg_data->add_rawbytes(data, size);
} else {
}
}
-void fixup_glCompressedTexSubImage2D(GLMessage *glmsg, void *pointersToFixup[]) {
+void fixup_glCompressedTexSubImage2D(GLTraceContext *context, GLMessage *glmsg,
+ void *pointersToFixup[]) {
/* void glCompressedTexSubImage2D(GLenum target,
GLint level,
GLint xoffset,
arg_data->set_type(GLMessage::DataType::BYTE);
arg_data->clear_rawbytes();
- if (data != NULL) {
+ if (data != NULL && !isUsingPixelBuffers(context)) {
arg_data->set_isarray(true);
arg_data->add_rawbytes(data, size);
} else {
arg_location->add_intvalue(location);
}
-GLint glGetInteger(GLTraceContext *context, GLenum param) {
- GLint x;
- context->hooks->gl.glGetIntegerv(param, &x);
- return x;
-}
-
-GLint glGetVertexAttrib(GLTraceContext *context, GLuint index, GLenum pname) {
- GLint x;
- context->hooks->gl.glGetVertexAttribiv(index, pname, &x);
- return x;
-}
-
-bool isUsingArrayBuffers(GLTraceContext *context) {
- return glGetInteger(context, GL_ARRAY_BUFFER_BINDING) != 0;
-}
-
-bool isUsingElementArrayBuffers(GLTraceContext *context) {
- return glGetInteger(context, GL_ELEMENT_ARRAY_BUFFER_BINDING) != 0;
-}
-
/** Copy @len bytes of data from @src into the @dataIndex'th argument of the message. */
void addGlBufferData(GLMessage *glmsg, int dataIndex, GLvoid *src, GLsizeiptr len) {
GLMessage_DataType *arg_datap = glmsg->mutable_args(dataIndex);
break;
case GLMessage::glTexImage2D:
if (context->getGlobalTraceState()->shouldCollectTextureDataOnGlTexImage()) {
- fixup_glTexImage2D(glmsg, pointersToFixup);
+ fixup_glTexImage2D(context, glmsg, pointersToFixup);
}
break;
case GLMessage::glTexSubImage2D:
if (context->getGlobalTraceState()->shouldCollectTextureDataOnGlTexImage()) {
- fixup_glTexSubImage2D(glmsg, pointersToFixup);
+ fixup_glTexSubImage2D(context, glmsg, pointersToFixup);
}
break;
case GLMessage::glCompressedTexImage2D:
if (context->getGlobalTraceState()->shouldCollectTextureDataOnGlTexImage()) {
- fixup_glCompressedTexImage2D(glmsg, pointersToFixup);
+ fixup_glCompressedTexImage2D(context, glmsg, pointersToFixup);
}
break;
case GLMessage::glCompressedTexSubImage2D:
if (context->getGlobalTraceState()->shouldCollectTextureDataOnGlTexImage()) {
- fixup_glCompressedTexSubImage2D(glmsg, pointersToFixup);
+ fixup_glCompressedTexSubImage2D(context, glmsg, pointersToFixup);
}
break;
case GLMessage::glShaderSource:
}
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
+ mTotalAcksNeeded = mTotalAcksReceived = 0;
#endif
}
mCacheSize,
mMaxCacheSize);
#if DEBUG_CONNECTIONS
- result.appendFormat("\t events recvd: %d | sent %d | cache %d | dropped %d\n",
+ result.appendFormat("\t events recvd: %d | sent %d | cache %d | dropped %d |"
+ " total_acks_needed %d | total_acks_recvd %d\n",
mEventsReceived,
mEventsSent,
mEventsSentFromCache,
- mEventsReceived - (mEventsSentFromCache
- mEventsSent + mCacheSize));
+ mEventsReceived - (mEventsSentFromCache +
+ mEventsSent + mCacheSize),
+ mTotalAcksNeeded,
+ mTotalAcksReceived);
#endif
}
if (index_wake_up_event >= 0) {
scratch[index_wake_up_event].flags |= WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
++mWakeLockRefCount;
+#if DEBUG_CONNECTIONS
+ ++mTotalAcksNeeded;
+#endif
}
// NOTE: ASensorEvent and sensors_event_t are the same type.
// If there was a wake_up sensor_event, reset the flag.
scratch[index_wake_up_event].flags &= ~WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
--mWakeLockRefCount;
+#if DEBUG_CONNECTIONS
+ --mTotalAcksNeeded;
+#endif
}
if (mEventCache == NULL) {
mMaxCacheSize = computeMaxCacheSizeLocked();
mEventCache[index_wake_up_event + numEventsSent].flags |=
WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
++mWakeLockRefCount;
+#if DEBUG_CONNECTIONS
+ ++mTotalAcksNeeded;
+#endif
}
ssize_t size = SensorEventQueue::write(mChannel,
mEventCache[index_wake_up_event + numEventsSent].flags &=
~WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
--mWakeLockRefCount;
+#if DEBUG_CONNECTIONS
+ --mTotalAcksNeeded;
+#endif
}
memmove(mEventCache, &mEventCache[numEventsSent],
(mCacheSize - numEventsSent) * sizeof(sensors_event_t));
{
Mutex::Autolock _l(mConnectionLock);
--mWakeLockRefCount;
+#if DEBUG_CONNECTIONS
+ ++mTotalAcksReceived;
+#endif
}
// Check if wakelock can be released by sensorservice. mConnectionLock needs to be released
// here as checkWakeLockState() will need it.
if (fifoWakeUpSensors + fifoNonWakeUpSensors == 0) {
// It is extremely unlikely that there is a write failure in non batch mode. Return a cache
// size that is equal to that of the batch mode.
- ALOGI("Write failure in non-batch mode");
+ // ALOGW("Write failure in non-batch mode");
return MAX_SOCKET_BUFFER_SIZE_BATCHED/sizeof(sensors_event_t);
}
return fifoWakeUpSensors + fifoNonWakeUpSensors;
#if DEBUG_CONNECTIONS
int mEventsReceived, mEventsSent, mEventsSentFromCache;
+ int mTotalAcksNeeded, mTotalAcksReceived;
#endif
public:
const sp<DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer,
EGLConfig config)
- : mFlinger(flinger),
+ : lastCompositionHadVisibleLayers(false),
+ mFlinger(flinger),
mType(type), mHwcDisplayId(hwcId),
mDisplayToken(displayToken),
mDisplaySurface(displaySurface),
void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
dirtyRegion.set(getBounds());
+ if (mSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mDisplay, mSurface);
+ mSurface = EGL_NO_SURFACE;
+ }
+
mDisplaySurface->resizeBuffers(newWidth, newHeight);
ANativeWindow* const window = mNativeWindow.get();
mutable Region swapRegion;
// region in screen space
Region undefinedRegion;
+ bool lastCompositionHadVisibleLayers;
enum DisplayType {
DISPLAY_ID_INVALID = -1,
dd.lastRetireFence = Fence::NO_FENCE;
dd.lastDisplayFence = Fence::NO_FENCE;
dd.outbufAcquireFence = Fence::NO_FENCE;
+ // clear all the previous configs and repopulate when a new
+ // device is added
+ dd.configs.clear();
}
int HWComposer::getVisualID() const {
int winWidth = s.active.w;
int winHeight = s.active.h;
if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
- NATIVE_WINDOW_TRANSFORM_FLIP_H;
+ // If the activeCrop has been rotate the ends are rotated but not
+ // the space itself so when transforming ends back we can't rely on
+ // a modification of the axes of rotation. To account for this we
+ // need to reorient the inverse rotation in terms of the current
+ // axes of rotation.
+ bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0;
+ bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0;
+ if (is_h_flipped == is_v_flipped) {
+ invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
+ NATIVE_WINDOW_TRANSFORM_FLIP_H;
+ }
winWidth = s.active.h;
winHeight = s.active.w;
}
void SurfaceFlinger::setUpHWComposer() {
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
- bool mustRecompose =
- !(mDisplays[dpy]->getDirtyRegion(false).isEmpty());
+ bool dirty = !mDisplays[dpy]->getDirtyRegion(false).isEmpty();
+ bool empty = mDisplays[dpy]->getVisibleLayersSortedByZ().size() == 0;
+ bool wasEmpty = !mDisplays[dpy]->lastCompositionHadVisibleLayers;
+
+ // If nothing has changed (!dirty), don't recompose.
+ // If something changed, but we don't currently have any visible layers,
+ // and didn't when we last did a composition, then skip it this time.
+ // The second rule does two things:
+ // - When all layers are removed from a display, we'll emit one black
+ // frame, then nothing more until we get new layers.
+ // - When a display is created with a private layer stack, we won't
+ // emit any black frames until a layer is added to the layer stack.
+ bool mustRecompose = dirty && !(empty && wasEmpty);
+
+ ALOGV_IF(mDisplays[dpy]->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL,
+ "dpy[%zu]: %s composition (%sdirty %sempty %swasEmpty)", dpy,
+ mustRecompose ? "doing" : "skipping",
+ dirty ? "+" : "-",
+ empty ? "+" : "-",
+ wasEmpty ? "+" : "-");
+
mDisplays[dpy]->beginFrame(mustRecompose);
+
+ if (mustRecompose) {
+ mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty;
+ }
}
HWComposer& hwc(getHwComposer());