Add a new class to receive vsync events.
Change-Id: I4e384336d2813752a6d65fda6a77e86113a4510c
This commit is contained in:
115
core/java/android/view/DisplayEventReceiver.java
Normal file
115
core/java/android/view/DisplayEventReceiver.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import dalvik.system.CloseGuard;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Provides a low-level mechanism for an application to receive display events
|
||||
* such as vertical sync.
|
||||
* @hide
|
||||
*/
|
||||
public abstract class DisplayEventReceiver {
|
||||
private static final String TAG = "DisplayEventReceiver";
|
||||
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
|
||||
private int mReceiverPtr;
|
||||
|
||||
// We keep a reference message queue object here so that it is not
|
||||
// GC'd while the native peer of the receiver is using them.
|
||||
private MessageQueue mMessageQueue;
|
||||
|
||||
private static native int nativeInit(DisplayEventReceiver receiver,
|
||||
MessageQueue messageQueue);
|
||||
private static native void nativeDispose(int receiverPtr);
|
||||
private static native void nativeScheduleVsync(int receiverPtr);
|
||||
|
||||
/**
|
||||
* Creates a display event receiver.
|
||||
*
|
||||
* @param looper The looper to use when invoking callbacks.
|
||||
*/
|
||||
public DisplayEventReceiver(Looper looper) {
|
||||
if (looper == null) {
|
||||
throw new IllegalArgumentException("looper must not be null");
|
||||
}
|
||||
|
||||
mMessageQueue = looper.getQueue();
|
||||
mReceiverPtr = nativeInit(this, mMessageQueue);
|
||||
|
||||
mCloseGuard.open("dispose");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
dispose();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the receiver.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (mCloseGuard != null) {
|
||||
mCloseGuard.close();
|
||||
}
|
||||
if (mReceiverPtr != 0) {
|
||||
nativeDispose(mReceiverPtr);
|
||||
mReceiverPtr = 0;
|
||||
}
|
||||
mMessageQueue = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a vertical sync pulse is received.
|
||||
* The recipient should render a frame and then call {@link #scheduleVsync}
|
||||
* to schedule the next vertical sync pulse.
|
||||
*
|
||||
* @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
|
||||
* timebase.
|
||||
* @param frame The frame number. Increases by one for each vertical sync interval.
|
||||
*/
|
||||
public void onVsync(long timestampNanos, int frame) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a single vertical sync pulse to be delivered when the next
|
||||
* display frame begins.
|
||||
*/
|
||||
public void scheduleVsync() {
|
||||
if (mReceiverPtr == 0) {
|
||||
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
|
||||
+ "receiver has already been disposed.");
|
||||
} else {
|
||||
nativeScheduleVsync(mReceiverPtr);
|
||||
}
|
||||
}
|
||||
|
||||
// Called from native code.
|
||||
@SuppressWarnings("unused")
|
||||
private void dispatchVsync(long timestampNanos, int frame) {
|
||||
onVsync(timestampNanos, frame);
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ LOCAL_SRC_FILES:= \
|
||||
android_database_SQLiteStatement.cpp \
|
||||
android_emoji_EmojiFactory.cpp \
|
||||
android_view_Display.cpp \
|
||||
android_view_DisplayEventReceiver.cpp \
|
||||
android_view_Surface.cpp \
|
||||
android_view_TextureView.cpp \
|
||||
android_view_InputChannel.cpp \
|
||||
|
||||
@@ -115,6 +115,7 @@ extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
|
||||
extern int register_android_graphics_Xfermode(JNIEnv* env);
|
||||
extern int register_android_graphics_PixelFormat(JNIEnv* env);
|
||||
extern int register_android_view_Display(JNIEnv* env);
|
||||
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
|
||||
extern int register_android_view_GLES20Canvas(JNIEnv* env);
|
||||
extern int register_android_view_HardwareRenderer(JNIEnv* env);
|
||||
extern int register_android_view_Surface(JNIEnv* env);
|
||||
@@ -1098,6 +1099,7 @@ static const RegJNIRec gRegJNI[] = {
|
||||
REG_JNI(register_android_os_SystemProperties),
|
||||
REG_JNI(register_android_os_Binder),
|
||||
REG_JNI(register_android_view_Display),
|
||||
REG_JNI(register_android_view_DisplayEventReceiver),
|
||||
REG_JNI(register_android_nio_utils),
|
||||
REG_JNI(register_android_graphics_PixelFormat),
|
||||
REG_JNI(register_android_graphics_Graphics),
|
||||
|
||||
263
core/jni/android_view_DisplayEventReceiver.cpp
Normal file
263
core/jni/android_view_DisplayEventReceiver.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "DisplayEventReceiver"
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
|
||||
#include "JNIHelp.h"
|
||||
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/threads.h>
|
||||
#include <gui/DisplayEventReceiver.h>
|
||||
#include "android_os_MessageQueue.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
// Number of events to read at a time from the DisplayEventReceiver pipe.
|
||||
// The value should be large enough that we can quickly drain the pipe
|
||||
// using just a few large reads.
|
||||
static const size_t EVENT_BUFFER_SIZE = 100;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
|
||||
jmethodID dispatchVsync;
|
||||
} gDisplayEventReceiverClassInfo;
|
||||
|
||||
|
||||
class NativeDisplayEventReceiver : public RefBase {
|
||||
public:
|
||||
NativeDisplayEventReceiver(JNIEnv* env,
|
||||
jobject receiverObj, const sp<Looper>& looper);
|
||||
|
||||
status_t initialize();
|
||||
status_t scheduleVsync();
|
||||
static int handleReceiveCallback(int receiveFd, int events, void* data);
|
||||
|
||||
protected:
|
||||
virtual ~NativeDisplayEventReceiver();
|
||||
|
||||
private:
|
||||
jobject mReceiverObjGlobal;
|
||||
sp<Looper> mLooper;
|
||||
DisplayEventReceiver mReceiver;
|
||||
bool mWaitingForVsync;
|
||||
bool mFdCallbackRegistered;
|
||||
};
|
||||
|
||||
|
||||
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
|
||||
jobject receiverObj, const sp<Looper>& looper) :
|
||||
mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
|
||||
mLooper(looper), mWaitingForVsync(false), mFdCallbackRegistered(false) {
|
||||
ALOGV("receiver %p ~ Initializing input event receiver.", this);
|
||||
}
|
||||
|
||||
NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
|
||||
ALOGV("receiver %p ~ Disposing display event receiver.", this);
|
||||
|
||||
if (mFdCallbackRegistered) {
|
||||
mLooper->removeFd(mReceiver.getFd());
|
||||
}
|
||||
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
env->DeleteGlobalRef(mReceiverObjGlobal);
|
||||
}
|
||||
|
||||
status_t NativeDisplayEventReceiver::initialize() {
|
||||
status_t result = mReceiver.initCheck();
|
||||
if (result) {
|
||||
LOGW("Failed to initialize display event receiver, status=%d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t NativeDisplayEventReceiver::scheduleVsync() {
|
||||
if (!mWaitingForVsync) {
|
||||
ALOGV("receiver %p ~ Scheduling vsync.", this);
|
||||
|
||||
// Drain all pending events.
|
||||
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
|
||||
ssize_t n;
|
||||
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
|
||||
ALOGV("receiver %p ~ Drained %d events.", this, int(n));
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
LOGW("Failed to drain events from display event receiver, status=%d", status_t(n));
|
||||
return status_t(n);
|
||||
}
|
||||
|
||||
if (!mFdCallbackRegistered) {
|
||||
int rc = mLooper->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
|
||||
handleReceiveCallback, this);
|
||||
if (rc < 0) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
mFdCallbackRegistered = true;
|
||||
}
|
||||
|
||||
mWaitingForVsync = true;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int NativeDisplayEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) {
|
||||
sp<NativeDisplayEventReceiver> r = static_cast<NativeDisplayEventReceiver*>(data);
|
||||
|
||||
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
|
||||
LOGE("Display event receiver pipe was closed or an error occurred. "
|
||||
"events=0x%x", events);
|
||||
r->mFdCallbackRegistered = false;
|
||||
return 0; // remove the callback
|
||||
}
|
||||
|
||||
if (!(events & ALOOPER_EVENT_INPUT)) {
|
||||
LOGW("Received spurious callback for unhandled poll event. "
|
||||
"events=0x%x", events);
|
||||
return 1; // keep the callback
|
||||
}
|
||||
|
||||
// Drain all pending events, keep the last vsync.
|
||||
nsecs_t vsyncTimestamp = -1;
|
||||
uint32_t vsyncCount = 0;
|
||||
|
||||
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
|
||||
ssize_t n;
|
||||
while ((n = r->mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
|
||||
ALOGV("receiver %p ~ Read %d events.", this, int(n));
|
||||
while (n-- > 0) {
|
||||
if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
|
||||
vsyncTimestamp = buf[n].header.timestamp;
|
||||
vsyncCount = buf[n].vsync.count;
|
||||
break; // stop at last vsync in the buffer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vsyncTimestamp < 0) {
|
||||
ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", this);
|
||||
return 1; // keep the callback, did not obtain a vsync pulse
|
||||
}
|
||||
|
||||
ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, count=%d",
|
||||
this, vsyncTimestamp, vsyncCount);
|
||||
r->mWaitingForVsync = false;
|
||||
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
|
||||
ALOGV("receiver %p ~ Invoking vsync handler.", this);
|
||||
env->CallVoidMethod(r->mReceiverObjGlobal,
|
||||
gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount);
|
||||
ALOGV("receiver %p ~ Returned from vsync handler.", this);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
LOGE("An exception occurred while dispatching a vsync event.");
|
||||
LOGE_EX(env);
|
||||
env->ExceptionClear();
|
||||
}
|
||||
|
||||
// Check whether dispatchVsync called scheduleVsync reentrantly and set mWaitingForVsync.
|
||||
// If so, keep the callback, otherwise remove it.
|
||||
if (r->mWaitingForVsync) {
|
||||
return 1; // keep the callback
|
||||
}
|
||||
r->mFdCallbackRegistered = false;
|
||||
return 0; // remove the callback
|
||||
}
|
||||
|
||||
|
||||
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
|
||||
jobject messageQueueObj) {
|
||||
sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
|
||||
if (looper == NULL) {
|
||||
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
|
||||
receiverObj, looper);
|
||||
status_t status = receiver->initialize();
|
||||
if (status) {
|
||||
String8 message;
|
||||
message.appendFormat("Failed to initialize display event receiver. status=%d", status);
|
||||
jniThrowRuntimeException(env, message.string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
|
||||
return reinterpret_cast<jint>(receiver.get());
|
||||
}
|
||||
|
||||
static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
|
||||
sp<NativeDisplayEventReceiver> receiver =
|
||||
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
|
||||
receiver->decStrong(gDisplayEventReceiverClassInfo.clazz); // drop reference held by the object
|
||||
}
|
||||
|
||||
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jint receiverPtr) {
|
||||
sp<NativeDisplayEventReceiver> receiver =
|
||||
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
|
||||
status_t status = receiver->scheduleVsync();
|
||||
if (status) {
|
||||
String8 message;
|
||||
message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status);
|
||||
jniThrowRuntimeException(env, message.string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
/* name, signature, funcPtr */
|
||||
{ "nativeInit",
|
||||
"(Landroid/view/DisplayEventReceiver;Landroid/os/MessageQueue;)I",
|
||||
(void*)nativeInit },
|
||||
{ "nativeDispose",
|
||||
"(I)V",
|
||||
(void*)nativeDispose },
|
||||
{ "nativeScheduleVsync", "(I)V",
|
||||
(void*)nativeScheduleVsync }
|
||||
};
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
var = env->FindClass(className); \
|
||||
LOG_FATAL_IF(! var, "Unable to find class " className); \
|
||||
var = jclass(env->NewGlobalRef(var));
|
||||
|
||||
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
|
||||
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
|
||||
LOG_FATAL_IF(! var, "Unable to find method " methodName);
|
||||
|
||||
int register_android_view_DisplayEventReceiver(JNIEnv* env) {
|
||||
int res = jniRegisterNativeMethods(env, "android/view/DisplayEventReceiver",
|
||||
gMethods, NELEM(gMethods));
|
||||
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
|
||||
|
||||
FIND_CLASS(gDisplayEventReceiverClassInfo.clazz, "android/view/DisplayEventReceiver");
|
||||
|
||||
GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync,
|
||||
gDisplayEventReceiverClassInfo.clazz,
|
||||
"dispatchVsync", "(JI)V");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/KeyedVector.h>
|
||||
#include <utils/threads.h>
|
||||
#include <ui/InputTransport.h>
|
||||
#include "android_os_MessageQueue.h"
|
||||
|
||||
Reference in New Issue
Block a user