Merge "sensorservice: switch to use sp<> in sensor list" into nyc-dev
diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp
index 6892b57..917c813 100644
--- a/cmds/bugreport/bugreport.cpp
+++ b/cmds/bugreport/bugreport.cpp
@@ -28,6 +28,11 @@
// output. All of the dumpstate output is written to stdout, including
// any errors encountered while reading/writing the output.
int main() {
+
+ fprintf(stderr, "=============================================================================\n");
+ fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n");
+ fprintf(stderr, "=============================================================================\n\n\n");
+
// Start the dumpstate service.
property_set("ctl.start", "dumpstate");
diff --git a/cmds/bugreportz/Android.mk b/cmds/bugreportz/Android.mk
new file mode 100644
index 0000000..14ba225
--- /dev/null
+++ b/cmds/bugreportz/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= bugreportz.cpp
+
+LOCAL_MODULE:= bugreportz
+
+LOCAL_CFLAGS := -Wall
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
new file mode 100644
index 0000000..b6856bb
--- /dev/null
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 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 <errno.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+// TODO: code below was copy-and-pasted from bugreport.cpp (except by the timeout value);
+// should be reused instead.
+int main() {
+
+ // Start the dumpstatez service.
+ property_set("ctl.start", "dumpstatez");
+
+ // Socket will not be available until service starts.
+ int s;
+ for (int i = 0; i < 20; i++) {
+ s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (s >= 0)
+ break;
+ // Try again in 1 second.
+ sleep(1);
+ }
+
+ if (s == -1) {
+ printf("Failed to connect to dumpstatez service: %s\n", strerror(errno));
+ return 1;
+ }
+
+ // Set a timeout so that if nothing is read in 10 minutes, we'll stop
+ // reading and quit. No timeout in dumpstate is longer than 60 seconds,
+ // so this gives lots of leeway in case of unforeseen time outs.
+ struct timeval tv;
+ tv.tv_sec = 10 * 60;
+ tv.tv_usec = 0;
+ if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+ printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
+ }
+
+ while (1) {
+ char buffer[65536];
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(
+ read(s, buffer, sizeof(buffer)));
+ if (bytes_read == 0) {
+ break;
+ } else if (bytes_read == -1) {
+ // EAGAIN really means time out, so change the errno.
+ if (errno == EAGAIN) {
+ errno = ETIMEDOUT;
+ }
+ printf("\nBugreport read terminated abnormally (%s).\n",
+ strerror(errno));
+ break;
+ }
+
+ ssize_t bytes_to_send = bytes_read;
+ ssize_t bytes_written;
+ do {
+ bytes_written = TEMP_FAILURE_RETRY(
+ write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send,
+ bytes_to_send));
+ if (bytes_written == -1) {
+ printf(
+ "Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
+ bytes_read, bytes_to_send, strerror(errno));
+ return 1;
+ }
+ bytes_to_send -= bytes_written;
+ } while (bytes_written != 0 && bytes_to_send > 0);
+ }
+
+ close(s);
+ return 0;
+
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index f486e08..ea14c66 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -63,6 +63,7 @@
void add_mountinfo();
static bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
static bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
+static int control_socket_fd;
#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
@@ -92,8 +93,7 @@
* See bugreport-format.txt for more info.
*/
// TODO: change to "v1" before final N build
-static std::string VERSION_DEFAULT = "v1-dev3";
-static std::string VERSION_BUILD_ON_NAME = "v1-dev4";
+static std::string VERSION_DEFAULT = "v1-dev4";
/* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
* otherwise gets just those modified in the last half an hour. */
@@ -929,20 +929,26 @@
}
static void usage() {
- fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s] [-q] [-B] [-P] [-R] [-V version]\n"
- " -b: play sound file instead of vibrate, at beginning of job\n"
- " -e: play sound file instead of vibrate, at end of job\n"
- " -o: write to file (instead of stdout)\n"
- " -d: append date to filename (requires -o)\n"
- " -p: capture screenshot to filename.png (requires -o)\n"
- " -z: generates zipped file (requires -o)\n"
- " -s: write output to control socket (for init)\n"
- " -q: disable vibrate\n"
- " -B: send broadcast when finished (requires -o)\n"
- " -P: send broadcast when started and update system properties on progress (requires -o and -B)\n"
- " -R: take bugreport in remote mode (requires -o, -z, -d and -B, shouldn't be used with -P)\n"
- " -V: sets the bugreport format version (valid values: %s, %s)\n",
- VERSION_DEFAULT.c_str(), VERSION_BUILD_ON_NAME.c_str());
+ fprintf(stderr,
+ "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] "
+ "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+ " -h: display this help message\n"
+ " -b: play sound file instead of vibrate, at beginning of job\n"
+ " -e: play sound file instead of vibrate, at end of job\n"
+ " -o: write to file (instead of stdout)\n"
+ " -d: append date to filename (requires -o)\n"
+ " -p: capture screenshot to filename.png (requires -o)\n"
+ " -z: generate zipped file (requires -o)\n"
+ " -s: write output to control socket (for init)\n"
+ " -S: write file location to control socket (for init; requires -o and -z)"
+ " -q: disable vibrate\n"
+ " -B: send broadcast when finished (requires -o)\n"
+ " -P: send broadcast when started and update system properties on "
+ "progress (requires -o and -B)\n"
+ " -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
+ "shouldn't be used with -P)\n"
+ " -V: sets the bugreport format version (valid values: %s)\n",
+ VERSION_DEFAULT.c_str());
}
static void sigpipe_handler(int n) {
@@ -1014,6 +1020,7 @@
int do_vibrate = 1;
char* use_outfile = 0;
int use_socket = 0;
+ int use_control_socket = 0;
int do_fb = 0;
int do_broadcast = 0;
int do_early_screenshot = 0;
@@ -1022,15 +1029,6 @@
now = time(NULL);
- if (getuid() != 0) {
- // Old versions of the adb client would call the
- // dumpstate command directly. Newer clients
- // call /system/bin/bugreport instead. If we detect
- // we're being called incorrectly, then exec the
- // correct program.
- return execl("/system/bin/bugreport", "/system/bin/bugreport", NULL);
- }
-
MYLOGI("begin\n");
/* gets the sequential id */
@@ -1059,12 +1057,13 @@
format_args(argc, const_cast<const char **>(argv), &args);
MYLOGD("Dumpstate command line: %s\n", args.c_str());
int c;
- while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) {
+ while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
switch (c) {
case 'd': do_add_date = 1; break;
case 'z': do_zip_file = 1; break;
case 'o': use_outfile = optarg; break;
case 's': use_socket = 1; break;
+ case 'S': use_control_socket = 1; break;
case 'v': break; // compatibility no-op
case 'q': do_vibrate = 0; break;
case 'p': do_fb = 1; break;
@@ -1084,6 +1083,11 @@
exit(1);
}
+ if (use_control_socket && !do_zip_file) {
+ usage();
+ exit(1);
+ }
+
if (do_update_progress && !do_broadcast) {
usage();
exit(1);
@@ -1094,9 +1098,9 @@
exit(1);
}
- if (version != VERSION_DEFAULT && version != VERSION_BUILD_ON_NAME) {
- usage();
- exit(1);
+ if (version != VERSION_DEFAULT) {
+ usage();
+ exit(1);
}
MYLOGI("bugreport format version: %s\n", version.c_str());
@@ -1109,6 +1113,11 @@
redirect_to_socket(stdout, "dumpstate");
}
+ if (use_control_socket) {
+ MYLOGD("Opening control socket\n");
+ control_socket_fd = open_socket("dumpstate");
+ }
+
/* full path of the directory where the bugreport files will be written */
std::string bugreport_dir;
@@ -1150,11 +1159,9 @@
} else {
suffix = "undated";
}
- if (version == VERSION_BUILD_ON_NAME) {
- char build_id[PROPERTY_VALUE_MAX];
- property_get("ro.build.id", build_id, "UNKNOWN_BUILD");
- base_name = base_name + "-" + build_id;
- }
+ char build_id[PROPERTY_VALUE_MAX];
+ property_get("ro.build.id", build_id, "UNKNOWN_BUILD");
+ base_name = base_name + "-" + build_id;
if (do_fb) {
// TODO: if dumpstate was an object, the paths could be internal variables and then
// we could have a function to calculate the derived values, such as:
@@ -1350,6 +1357,14 @@
path.clear();
}
}
+ if (use_control_socket) {
+ if (do_text_file) {
+ dprintf(control_socket_fd, "FAIL:could not create zip file, check %s "
+ "for more details\n", log_path.c_str());
+ } else {
+ dprintf(control_socket_fd, "OK:%s\n", path.c_str());
+ }
+ }
}
/* vibrate a few but shortly times to let user know it's finished */
@@ -1397,5 +1412,10 @@
fclose(stderr);
}
+ if (use_control_socket && control_socket_fd >= 0) {
+ MYLOGD("Closing control socket\n");
+ close(control_socket_fd);
+ }
+
return 0;
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index baba0f9..c51c79a 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -130,6 +130,9 @@
/* prints all the system properties */
void print_properties();
+/** opens a socket and returns its file descriptor */
+int open_socket(const char *service);
+
/* redirect output to a service control socket */
void redirect_to_socket(FILE *redirect, const char *service);
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 96232c4..1f56d21 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -9,6 +9,15 @@
disabled
oneshot
+# dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
+# it is finished.
+service dumpstatez /system/bin/dumpstate -S -d -z \
+ -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
+ socket dumpstate stream 0660 shell log
+ class main
+ disabled
+ oneshot
+
# bugreportplus is an enhanced version of bugreport that provides a better
# user interface (like displaying progress and allowing user to enter details).
# It's typically triggered by the power button or developer settings.
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index d9738bb..f1a1ed6 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -51,10 +51,14 @@
static const int64_t NANOS_PER_SEC = 1000000000;
/* list of native processes to include in the native dumps */
+// This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
static const char* native_processes_to_dump[] = {
"/system/bin/audioserver",
"/system/bin/cameraserver",
"/system/bin/drmserver",
+ "/system/bin/mediacodec", // media.codec
+ "/system/bin/mediadrmserver",
+ "/system/bin/mediaextractor", // media.extractor
"/system/bin/mediaserver",
"/system/bin/sdcard",
"/system/bin/surfaceflinger",
@@ -909,8 +913,7 @@
printf("\n");
}
-/* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service) {
+int open_socket(const char *service) {
int s = android_get_control_socket(service);
if (s < 0) {
MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
@@ -930,11 +933,18 @@
exit(1);
}
+ return fd;
+}
+
+/* redirect output to a service control socket */
+void redirect_to_socket(FILE *redirect, const char *service) {
+ int fd = open_socket(service);
fflush(redirect);
dup2(fd, fileno(redirect));
close(fd);
}
+// TODO: should call is_valid_output_file and/or be merged into it.
void create_parent_dirs(const char *path) {
char *chp = const_cast<char *> (path);
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 878fb2d..5c2ad2d 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -104,11 +104,16 @@
struct dirent* ent;
while ((ent = readdir(dir))) {
if (ent->d_ino == ce_data_inode) {
+ auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
+ if (resolved != fallback) {
+ LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
+ << " instead of " << fallback;
+ }
closedir(dir);
- return StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
+ return resolved;
}
}
- LOG(WARNING) << "Failed to find inode " << ce_data_inode << " for package " << package_name;
+ LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback;
closedir(dir);
return fallback;
} else {
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 8ed8460..ab89ef5 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -24,15 +24,11 @@
-->
<permissions>
<feature name="android.hardware.audio.output" />
- <feature name="android.hardware.camera" />
<feature name="android.hardware.location" />
<feature name="android.hardware.location.network" />
- <feature name="android.hardware.sensor.compass" />
- <feature name="android.hardware.sensor.accelerometer" />
<feature name="android.hardware.bluetooth" />
<feature name="android.hardware.touchscreen" />
<feature name="android.hardware.microphone" />
- <feature name="android.hardware.screen.landscape" />
<!-- Feature to specify if the device is a car -->
<feature name="android.hardware.type.automotive" />
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
index f9edc2a..64cf72e 100644
--- a/include/binder/ProcessState.h
+++ b/include/binder/ProcessState.h
@@ -91,6 +91,8 @@
size_t mExecutingThreadsCount;
// Maximum number for binder threads allowed for this process.
size_t mMaxThreads;
+ // Time when thread pool was emptied
+ int64_t mStarvationStartTimeMs;
mutable Mutex mLock; // protects everything below.
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 5d1d7bf..f76a9be 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -122,6 +122,13 @@
// See IGraphicBufferProducer::setDequeueTimeout
status_t setDequeueTimeout(nsecs_t timeout);
+ /*
+ * Wait for frame number to increase past lastFrame for at most
+ * timeoutNs. Useful for one thread to wait for another unknown
+ * thread to queue a buffer.
+ */
+ bool waitForNextFrame(uint64_t lastFrame, nsecs_t timeout);
+
protected:
virtual ~Surface();
@@ -348,6 +355,8 @@
// This is true if the shared buffer has already been queued/canceled. It's
// used to prevent a mismatch between the number of queue/dequeue calls.
bool mSharedBufferHasBeenQueued;
+
+ Condition mQueueBufferCondition;
};
namespace view {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 1cbcfe4..d90798f 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -24,12 +24,14 @@
#include <cutils/sched_policy.h>
#include <utils/Log.h>
+#include <utils/SystemClock.h>
#include <utils/threads.h>
#include <private/binder/binder_module.h>
#include <private/binder/Static.h>
#include <errno.h>
+#include <inttypes.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
@@ -434,12 +436,25 @@
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount++;
+ if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
+ mProcess->mStarvationStartTimeMs == 0) {
+ mProcess->mStarvationStartTimeMs = uptimeMillis();
+ }
pthread_mutex_unlock(&mProcess->mThreadCountLock);
result = executeCommand(cmd);
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount--;
+ if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
+ mProcess->mStarvationStartTimeMs != 0) {
+ int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
+ if (starvationTimeMs > 100) {
+ ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
+ mProcess->mMaxThreads, starvationTimeMs);
+ }
+ mProcess->mStarvationStartTimeMs = 0;
+ }
pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
pthread_mutex_unlock(&mProcess->mThreadCountLock);
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 33fe26c..f13f49f 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -40,12 +40,12 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
+#include <sys/types.h>
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
#define DEFAULT_MAX_BINDER_THREADS 15
-
-// ---------------------------------------------------------------------------
+// -------------------------------------------------------------------------
namespace android {
@@ -278,8 +278,9 @@
String8 ProcessState::makeBinderThreadName() {
int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ pid_t pid = getpid();
String8 name;
- name.appendFormat("Binder_%X", s);
+ name.appendFormat("Binder:%d_%X", pid, s);
return name;
}
@@ -342,6 +343,7 @@
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
+ , mStarvationStartTimeMs(0)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 76b62f1..5efc333 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -480,6 +480,8 @@
mSharedBufferHasBeenQueued = true;
}
+ mQueueBufferCondition.broadcast();
+
return err;
}
@@ -1259,6 +1261,15 @@
return err;
}
+bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) {
+ Mutex::Autolock lock(mMutex);
+ uint64_t currentFrame = mGraphicBufferProducer->getNextFrameNumber();
+ if (currentFrame > lastFrame) {
+ return true;
+ }
+ return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK;
+}
+
namespace view {
status_t Surface::writeToParcel(Parcel* parcel) const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f031296..418892a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -291,6 +291,9 @@
s->w = w;
s->h = h;
+ // Resizing a surface makes the transaction synchronous.
+ mForceSynchronous = true;
+
return NO_ERROR;
}
@@ -451,6 +454,7 @@
s.viewport = layerStackRect;
s.frame = displayRect;
s.what |= DisplayState::eDisplayProjectionChanged;
+ mForceSynchronous = true; // TODO: do we actually still need this?
}
void Composer::setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height) {
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 5d838e6..bef5f02 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -602,8 +602,8 @@
#define EGL_ANDROID_create_native_client_buffer 1
#define EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
#define EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
-#define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_ANDROID 0x00000002
-#define EGL_NATIVE_BUFFER_USAGE_TEXTURE_ANDROID 0x00000004
+#define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
+#define EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID 0x00000004
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI EGLClientBuffer eglCreateNativeClientBufferANDROID (const EGLint *attrib_list);
#else
@@ -616,6 +616,10 @@
#define EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID 0x314C
#endif
+#ifndef EGL_KHR_mutable_render_buffer
+#define EGL_KHR_mutable_render_buffer 1
+#define EGL_MUTABLE_RENDER_BUFFER_BIT_KHR 0x1000
+#endif
#ifdef __cplusplus
}
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index e7703d8..df639cd 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -115,6 +115,8 @@
"EGL_KHR_partial_update " // strongly recommended
"EGL_EXT_buffer_age " // strongly recommended with partial_update
"EGL_KHR_create_context_no_error "
+ "EGL_KHR_mutable_render_buffer "
+ "EGL_EXT_yuv_surface "
;
// extensions not exposed to applications but used by the ANDROID system
@@ -603,6 +605,7 @@
EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
if (result == EGL_TRUE) {
_s.terminate();
+ dp->removeSurface(surface);
}
return result;
}
@@ -1814,21 +1817,17 @@
case EGL_NATIVE_BUFFER_USAGE_ANDROID:
if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
usage |= GRALLOC_USAGE_PROTECTED;
- // If we are using QCOM then add in extra bits. This
- // should be removed before launch. These correspond to:
- // USAGE_PRIVATE_MM_HEAP | USAGE_PRIVATE_UNCACHED
- usage |= 0x82000000;
}
- if (value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_ANDROID) {
+ if (value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) {
usage |= GRALLOC_USAGE_HW_RENDER;
}
- if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_ANDROID) {
+ if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID) {
usage |= GRALLOC_USAGE_HW_TEXTURE;
}
// The buffer must be used for either a texture or a
// renderbuffer.
- if ((value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_ANDROID) &&
- (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_ANDROID)) {
+ if ((value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) &&
+ (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID)) {
return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
}
break;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 6a9d7b6..d849693 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -89,6 +89,31 @@
return false;
}
+void egl_display_t::addContext(egl_context_t* context) {
+ Mutex::Autolock _l(lock);
+ contexts.add(context);
+}
+
+void egl_display_t::removeContext(egl_context_t* context) {
+ Mutex::Autolock _l(lock);
+ contexts.remove(context);
+}
+
+void egl_display_t::removeSurface(EGLSurface surface) const {
+ Mutex::Autolock _l(lock);
+ for (size_t i = 0; i < contexts.size(); i++) {
+ egl_context_t* context = contexts[i];
+ if (context->read == surface) {
+ SurfaceRef _r(get_surface(context->read));
+ _r.release();
+ }
+ if (context->draw == surface) {
+ SurfaceRef _d(get_surface(context->draw));
+ _d.release();
+ }
+ }
+}
+
EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) {
if (uintptr_t(disp) >= NUM_DISPLAYS)
return NULL;
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 2d86295..0ede705 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -68,6 +68,13 @@
// add reference to this object. returns true if this is a valid object.
bool getObject(egl_object_t* object) const;
+ // add context to this display's list
+ void addContext(egl_context_t* context);
+ // remove context from this display's list
+ void removeContext(egl_context_t* context);
+ // search for surface on all contexts and remove the references
+ void removeSurface(EGLSurface surface) const;
+
// These notifications allow the display to keep track of how many window
// surfaces exist, which it uses to decide whether to hibernate the
// underlying EGL implementation. They can be called by any thread without
@@ -135,6 +142,7 @@
mutable Mutex lock, refLock;
mutable Condition refCond;
SortedVector<egl_object_t*> objects;
+ SortedVector<egl_context_t*> contexts;
String8 mVendorString;
String8 mVersionString;
String8 mClientApiString;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 918faa8..8859387 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -91,6 +91,12 @@
egl_connection_t const* cnx, int version) :
egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
config(config), read(0), draw(0), cnx(cnx), version(version) {
+ get_display_nowake(dpy)->addContext(this);
+}
+
+void egl_context_t::terminate() {
+ display->removeContext(this);
+ egl_object_t::terminate();
}
void egl_context_t::onLooseCurrent() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 17a8304..8268900 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -40,11 +40,12 @@
class egl_display_t;
class egl_object_t {
- egl_display_t *display;
mutable volatile int32_t count;
protected:
virtual ~egl_object_t();
+ virtual void terminate();
+ egl_display_t *display;
public:
egl_object_t(egl_display_t* display);
@@ -55,7 +56,6 @@
inline egl_display_t* getDisplay() const { return display; }
private:
- void terminate();
static bool get(egl_display_t const* display, egl_object_t* object);
public:
@@ -143,6 +143,7 @@
class egl_context_t: public egl_object_t {
protected:
~egl_context_t() {}
+ void terminate() override;
public:
typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
new file mode 100644
index 0000000..a6fae80
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
@@ -0,0 +1,197 @@
+Name
+
+ ANDROID_create_native_client_buffer
+
+Name Strings
+
+ EGL_ANDROID_create_native_client_buffer
+
+Contributors
+
+ Craig Donner
+
+Contact
+
+ Craig Donner, Google Inc. (cdonner 'at' google.com)
+
+Status
+
+ Draft
+
+Version
+
+ Version 1, January 19, 2016
+
+Number
+
+ EGL Extension #XXX
+
+Dependencies
+
+ Requires EGL 1.2.
+
+ EGL_ANDROID_image_native_buffer and EGL_KHR_image_base are required.
+
+ This extension is written against the wording of the EGL 1.2
+ Specification as modified by EGL_KHR_image_base and
+ EGL_ANDROID_image_native_buffer.
+
+Overview
+
+ This extension allows creating an EGLClientBuffer backed by an Android
+ window buffer (struct ANativeWindowBuffer) which can be later used to
+ create an EGLImage.
+
+New Types
+
+ None.
+
+New Procedures and Functions
+
+EGLClientBuffer eglCreateNativeClientBufferANDROID(
+ const EGLint *attrib_list)
+
+New Tokens
+
+ EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
+ EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
+ EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
+ EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID 0x00000004
+
+Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors)
+
+ Add the following to section 2.5.1 "EGLImage Specification" (as modified by
+ the EGL_KHR_image_base and EGL_ANDROID_image_native_buffer specifications),
+ below the description of eglCreateImageKHR:
+
+ "The command
+
+ EGLClientBuffer eglCreateNativeClientBufferANDROID(
+ const EGLint *attrib_list)
+
+ may be used to create an EGLClientBuffer backed by an ANativeWindowBuffer
+ struct. EGL implementations must guarantee that the lifetime of the
+ returned EGLClientBuffer is at least as long as the EGLImage(s) it is bound
+ to, following the lifetime semantics described below in section 2.5.2; the
+ EGLClientBuffer must be destroyed no earlier than when all of its associated
+ EGLImages are destroyed by eglDestroyImageKHR. <attrib_list> is a list of
+ attribute-value pairs which is used to specify the dimensions, format, and
+ usage of the underlying buffer structure. If <attrib_list> is non-NULL, the
+ last attribute specified in the list must be EGL_NONE.
+
+ Attribute names accepted in <attrib_list> are shown in Table aaa,
+ together with the <target> for which each attribute name is valid, and
+ the default value used for each attribute if it is not included in
+ <attrib_list>.
+
+ +---------------------------------+----------------------+---------------+
+ | Attribute | Description | Default Value |
+ | | | |
+ +---------------------------------+----------------------+---------------+
+ | EGL_NONE | Marks the end of the | N/A |
+ | | attribute-value list | |
+ | EGL_WIDTH | The width of the | 0 |
+ | | buffer data | |
+ | EGL_HEIGHT | The height of the | 0 |
+ | | buffer data | |
+ | EGL_RED_SIZE | The bits of Red in | 0 |
+ | | the color buffer | |
+ | EGL_GREEN_SIZE | The bits of Green in | 0 |
+ | | the color buffer | |
+ | EGL_BLUE_SIZE | The bits of Blue in | 0 |
+ | | the color buffer | |
+ | EGL_ALPHA_SIZE | The bits of Alpha in | 0 |
+ | | the color buffer | |
+ | | buffer data | |
+ | EGL_NATIVE_BUFFER_USAGE_ANDROID | The usage bits of | 0 |
+ | | the buffer data | |
+ +---------------------------------+----------------------+---------------+
+ Table aaa. Legal attributes for eglCreateNativeClientBufferANDROID
+ <attrib_list> parameter.
+
+ The maximum width and height may depend on the amount of available memory,
+ which may also depend on the format and usage flags. The values of
+ EGL_RED_SIZE, EGL_GREEN_SIZE, and EGL_BLUE_SIZE must be non-zero and
+ correspond to a valid pixel format for the implementation. If EGL_ALPHA_SIZE
+ is non-zero then the combination of all four sizes must correspond to a
+ valid pixel format for the implementation. The
+ EGL_NATIVE_BUFFER_USAGE_ANDROID flag may include any of the following bits:
+
+ EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID: Indicates that the
+ created buffer must have a hardware-protected path to external display
+ sink. If a hardware-protected path is not available, then either don't
+ composite only this buffer (preferred) to the external sink, or (less
+ desirable) do not route the entire composition to the external sink.
+
+ EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID: The buffer will be
+ used to create a renderbuffer. This flag must not be set if
+ EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID is set.
+
+ EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID: The buffer will be used to
+ create a texture. This flag must not be set if
+ EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID is set.
+
+ Errors
+
+ If eglCreateNativeClientBufferANDROID fails, NULL will be returned, no
+ memory will be allocated, and one of the following errors will be
+ generated:
+
+ * If the value of EGL_WIDTH or EGL_HEIGHT is not positive, the error
+ EGL_BAD_PARAMETER is generated.
+
+ * If the combination of the values of EGL_RED_SIZE, EGL_GREEN_SIZE,
+ EGL_BLUE_SIZE, and EGL_ALPHA_SIZE is not a valid pixel format for the
+ EGL implementation, the error EGL_BAD_PARAMETER is generated.
+
+ * If the value of EGL_NATIVE_BUFFER_ANDROID is not a valid combination
+ of gralloc usage flags for the EGL implementation, or is incompatible
+ with the value of EGL_FORMAT, the error EGL_BAD_PARAMETER is
+ Generated.
+
+ * If both the EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID and
+ EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID are set in the value of
+ EGL_NATIVE_BUFFER_USAGE_ANDROID, the error EGL_BAD_PARAMETER is
+ Generated."
+
+Issues
+
+ 1. Should this extension define what combinations of formats and usage flags
+ EGL implementations are required to support?
+
+ RESOLVED: Partially.
+
+ The set of valid color combinations is implementation-specific and may
+ depend on additional EGL extensions, but generally RGB565 and RGBA888 should
+ be supported. The particular valid combinations for a given Android version
+ and implementation should be documented by that version.
+
+ 2. Should there be an eglDestroyNativeClientBufferANDROID to destroy the
+ client buffers created by this extension?
+
+ RESOLVED: No.
+
+ A destroy function would add several complications:
+
+ a) ANativeWindowBuffer is a reference counted object, may be used
+ outside of EGL.
+ b) The same buffer may back multiple EGLImages, though this usage may
+ result in undefined behavior.
+ c) The interactions between the lifetimes of EGLImages and their
+ EGLClientBuffers would become needlessly complex.
+
+ Because ANativeWindowBuffer is a reference counted object, implementations
+ of this extension should ensure the buffer has a lifetime at least as long
+ as a generated EGLImage (via EGL_ANDROID_image_native_buffer). The simplest
+ method is to increment the reference count of the buffer in
+ eglCreateImagKHR, and then decrement it in eglDestroyImageKHR. This should
+ ensure proper lifetime semantics.
+
+Revision History
+
+#2 (Craig Donner, April 15, 2016)
+ - Set color formats and usage bits explicitly using additional attributes,
+ and add value for new token EGL_NATIVE_BUFFER_USAGE_ANDROID.
+
+#1 (Craig Donner, January 19, 2016)
+ - Initial draft.
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 4cf9370..5ba387d 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -15,7 +15,6 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
// This is needed for stdint.h to define INT64_MAX in C++
#define __STDC_LIMIT_MACROS
@@ -34,21 +33,12 @@
#include "DispSync.h"
#include "EventLog/EventLog.h"
-#include <algorithm>
-
-using std::max;
-using std::min;
-
namespace android {
// Setting this to true enables verbose tracing that can be used to debug
// vsync event model or phase issues.
static const bool kTraceDetailedInfo = false;
-// Setting this to true adds a zero-phase tracer for correlating with hardware
-// vsync events
-static const bool kEnableZeroPhaseTracer = false;
-
// This is the threshold used to determine when hardware vsync events are
// needed to re-synchronize the software vsync model with the hardware. The
// error metric used is the mean of the squared difference between each
@@ -59,36 +49,28 @@
// vsync event.
static const int64_t kPresentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
-#undef LOG_TAG
-#define LOG_TAG "DispSyncThread"
class DispSyncThread: public Thread {
public:
- DispSyncThread(const char* name):
- mName(name),
+ DispSyncThread():
mStop(false),
mPeriod(0),
mPhase(0),
mReferenceTime(0),
- mWakeupLatency(0),
- mFrameNumber(0) {}
+ mWakeupLatency(0) {
+ }
virtual ~DispSyncThread() {}
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
- if (kTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = phase;
mReferenceTime = referenceTime;
- ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
- " mReferenceTime = %" PRId64, mName, ns2us(mPeriod),
- ns2us(mPhase), ns2us(mReferenceTime));
mCond.signal();
}
void stop() {
- if (kTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
mStop = true;
mCond.signal();
@@ -107,12 +89,6 @@
{ // Scope for lock
Mutex::Autolock lock(mMutex);
- if (kTraceDetailedInfo) {
- ATRACE_INT64("DispSync:Frame", mFrameNumber);
- }
- ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
- ++mFrameNumber;
-
if (mStop) {
return false;
}
@@ -133,9 +109,6 @@
bool isWakeup = false;
if (now < targetTime) {
- ALOGV("[%s] Waiting until %" PRId64, mName,
- ns2us(targetTime));
- if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
err = mCond.waitRelative(mMutex, targetTime - now);
if (err == TIMED_OUT) {
@@ -149,15 +122,15 @@
now = systemTime(SYSTEM_TIME_MONOTONIC);
- // Don't correct by more than 1.5 ms
- static const nsecs_t kMaxWakeupLatency = us2ns(1500);
-
if (isWakeup) {
mWakeupLatency = ((mWakeupLatency * 63) +
(now - targetTime)) / 64;
- mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
+ if (mWakeupLatency > 500000) {
+ // Don't correct by more than 500 us
+ mWakeupLatency = 500000;
+ }
if (kTraceDetailedInfo) {
- ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
+ ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
}
}
@@ -173,9 +146,7 @@
return false;
}
- status_t addEventListener(const char* name, nsecs_t phase,
- const sp<DispSync::Callback>& callback) {
- if (kTraceDetailedInfo) ATRACE_CALL();
+ status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) {
Mutex::Autolock lock(mMutex);
for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -185,14 +156,15 @@
}
EventListener listener;
- listener.mName = name;
listener.mPhase = phase;
listener.mCallback = callback;
// We want to allow the firstmost future event to fire without
- // allowing any past events to fire
- listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase -
- mWakeupLatency;
+ // allowing any past events to fire. Because
+ // computeListenerNextEventTimeLocked filters out events within a half
+ // a period of the last event time, we need to initialize the last
+ // event time to a half a period in the past.
+ listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC) - mPeriod / 2;
mEventListeners.push(listener);
@@ -202,7 +174,6 @@
}
status_t removeEventListener(const sp<DispSync::Callback>& callback) {
- if (kTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -218,7 +189,6 @@
// This method is only here to handle the kIgnorePresentFences case.
bool hasAnyEventListeners() {
- if (kTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
return !mEventListeners.empty();
}
@@ -226,7 +196,6 @@
private:
struct EventListener {
- const char* mName;
nsecs_t mPhase;
nsecs_t mLastEventTime;
sp<DispSync::Callback> mCallback;
@@ -238,8 +207,6 @@
};
nsecs_t computeNextEventTimeLocked(nsecs_t now) {
- if (kTraceDetailedInfo) ATRACE_CALL();
- ALOGV("[%s] computeNextEventTimeLocked", mName);
nsecs_t nextEventTime = INT64_MAX;
for (size_t i = 0; i < mEventListeners.size(); i++) {
nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
@@ -250,28 +217,21 @@
}
}
- ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime));
return nextEventTime;
}
Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
- if (kTraceDetailedInfo) ATRACE_CALL();
- ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName,
- ns2us(now));
-
Vector<CallbackInvocation> callbackInvocations;
- nsecs_t onePeriodAgo = now - mPeriod;
+ nsecs_t ref = now - mPeriod;
for (size_t i = 0; i < mEventListeners.size(); i++) {
nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
- onePeriodAgo);
+ ref);
if (t < now) {
CallbackInvocation ci;
ci.mCallback = mEventListeners[i].mCallback;
ci.mEventTime = t;
- ALOGV("[%s] [%s] Preparing to fire", mName,
- mEventListeners[i].mName);
callbackInvocations.push(ci);
mEventListeners.editItemAt(i).mLastEventTime = t;
}
@@ -281,67 +241,29 @@
}
nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
- nsecs_t baseTime) {
- if (kTraceDetailedInfo) ATRACE_CALL();
- ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")",
- mName, listener.mName, ns2us(baseTime));
+ nsecs_t ref) {
- nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
- ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime));
- if (baseTime < lastEventTime) {
- baseTime = lastEventTime;
- ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName,
- ns2us(baseTime));
+ nsecs_t lastEventTime = listener.mLastEventTime;
+ if (ref < lastEventTime) {
+ ref = lastEventTime;
}
- baseTime -= mReferenceTime;
- ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime));
- nsecs_t phase = mPhase + listener.mPhase;
- ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase));
- baseTime -= phase;
- ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime));
+ nsecs_t phase = mReferenceTime + mPhase + listener.mPhase;
+ nsecs_t t = (((ref - phase) / mPeriod) + 1) * mPeriod + phase;
- // If our previous time is before the reference (because the reference
- // has since been updated), the division by mPeriod will truncate
- // towards zero instead of computing the floor. Since in all cases
- // before the reference we want the next time to be effectively now, we
- // set baseTime to -mPeriod so that numPeriods will be -1.
- // When we add 1 and the phase, we will be at the correct event time for
- // this period.
- if (baseTime < 0) {
- ALOGV("[%s] Correcting negative baseTime", mName);
- baseTime = -mPeriod;
- }
-
- nsecs_t numPeriods = baseTime / mPeriod;
- ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods);
- nsecs_t t = (numPeriods + 1) * mPeriod + phase;
- ALOGV("[%s] t = %" PRId64, mName, ns2us(t));
- t += mReferenceTime;
- ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t));
-
- // Check that it's been slightly more than half a period since the last
- // event so that we don't accidentally fall into double-rate vsyncs
- if (t - listener.mLastEventTime < (3 * mPeriod / 5)) {
+ if (t - listener.mLastEventTime < mPeriod / 2) {
t += mPeriod;
- ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
}
- t -= mWakeupLatency;
- ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t));
-
return t;
}
void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
- if (kTraceDetailedInfo) ATRACE_CALL();
for (size_t i = 0; i < callbacks.size(); i++) {
callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
}
}
- const char* const mName;
-
bool mStop;
nsecs_t mPeriod;
@@ -349,17 +271,12 @@
nsecs_t mReferenceTime;
nsecs_t mWakeupLatency;
- int64_t mFrameNumber;
-
Vector<EventListener> mEventListeners;
Mutex mMutex;
Condition mCond;
};
-#undef LOG_TAG
-#define LOG_TAG "DispSync"
-
class ZeroPhaseTracer : public DispSync::Callback {
public:
ZeroPhaseTracer() : mParity(false) {}
@@ -373,10 +290,9 @@
bool mParity;
};
-DispSync::DispSync(const char* name) :
- mName(name),
+DispSync::DispSync() :
mRefreshSkipCount(0),
- mThread(new DispSyncThread(name)) {
+ mThread(new DispSyncThread()) {
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
@@ -389,8 +305,8 @@
// Even if we're just ignoring the fences, the zero-phase tracing is
// not needed because any time there is an event registered we will
// turn on the HW vsync events.
- if (!kIgnorePresentFences && kEnableZeroPhaseTracer) {
- addEventListener("ZeroPhaseTracer", 0, new ZeroPhaseTracer());
+ if (!kIgnorePresentFences) {
+ addEventListener(0, new ZeroPhaseTracer());
}
}
}
@@ -435,7 +351,7 @@
void DispSync::beginResync() {
Mutex::Autolock lock(mMutex);
- ALOGV("[%s] beginResync", mName);
+
mModelUpdated = false;
mNumResyncSamples = 0;
}
@@ -443,17 +359,11 @@
bool DispSync::addResyncSample(nsecs_t timestamp) {
Mutex::Autolock lock(mMutex);
- ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
-
size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
mResyncSamples[idx] = timestamp;
if (mNumResyncSamples == 0) {
mPhase = 0;
mReferenceTime = timestamp;
- ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, "
- "mReferenceTime = %" PRId64, mName, ns2us(mPeriod),
- ns2us(mReferenceTime));
- mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
@@ -477,21 +387,17 @@
return mThread->hasAnyEventListeners();
}
- // Check against kErrorThreshold / 2 to add some hysteresis before having to
- // resync again
- bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
- ALOGV("[%s] addResyncSample returning %s", mName,
- modelLocked ? "locked" : "unlocked");
- return !modelLocked;
+ return !mModelUpdated || mError > kErrorThreshold;
}
void DispSync::endResync() {
}
-status_t DispSync::addEventListener(const char* name, nsecs_t phase,
+status_t DispSync::addEventListener(nsecs_t phase,
const sp<Callback>& callback) {
+
Mutex::Autolock lock(mMutex);
- return mThread->addEventListener(name, phase, callback);
+ return mThread->addEventListener(phase, callback);
}
void DispSync::setRefreshSkipCount(int count) {
@@ -521,32 +427,20 @@
}
void DispSync::updateModelLocked() {
- ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples);
if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
- ALOGV("[%s] Computing...", mName);
nsecs_t durationSum = 0;
- nsecs_t minDuration = INT64_MAX;
- nsecs_t maxDuration = 0;
for (size_t i = 1; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
- nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
- durationSum += duration;
- minDuration = min(minDuration, duration);
- maxDuration = max(maxDuration, duration);
+ durationSum += mResyncSamples[idx] - mResyncSamples[prev];
}
- // Exclude the min and max from the average
- durationSum -= minDuration + maxDuration;
- mPeriod = durationSum / (mNumResyncSamples - 3);
-
- ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
+ mPeriod = durationSum / (mNumResyncSamples - 1);
double sampleAvgX = 0;
double sampleAvgY = 0;
double scale = 2.0 * M_PI / double(mPeriod);
- // Intentionally skip the first sample
- for (size_t i = 1; i < mNumResyncSamples; i++) {
+ for (size_t i = 0; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
double samplePhase = double(sample % mPeriod) * scale;
@@ -554,21 +448,18 @@
sampleAvgY += sin(samplePhase);
}
- sampleAvgX /= double(mNumResyncSamples - 1);
- sampleAvgY /= double(mNumResyncSamples - 1);
+ sampleAvgX /= double(mNumResyncSamples);
+ sampleAvgY /= double(mNumResyncSamples);
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
- ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase));
-
- if (mPhase < -(mPeriod / 2)) {
+ if (mPhase < 0) {
mPhase += mPeriod;
- ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
}
if (kTraceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
- ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
+ ATRACE_INT64("DispSync:Phase", mPhase);
}
// Artificially inflate the period if requested.
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
index 537c81b..a8524b9 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/DispSync.h
@@ -26,8 +26,11 @@
namespace android {
// Ignore present (retire) fences if the device doesn't have support for the
-// sync framework
-#if defined(RUNNING_WITHOUT_SYNC_FRAMEWORK)
+// sync framework, or if all phase offsets are zero. The latter is useful
+// because it allows us to avoid resync bursts on devices that don't need
+// phase-offset VSYNC events.
+#if defined(RUNNING_WITHOUT_SYNC_FRAMEWORK) || \
+ (VSYNC_EVENT_PHASE_OFFSET_NS == 0 && SF_VSYNC_EVENT_PHASE_OFFSET_NS == 0)
static const bool kIgnorePresentFences = true;
#else
static const bool kIgnorePresentFences = false;
@@ -61,7 +64,7 @@
virtual void onDispSyncEvent(nsecs_t when) = 0;
};
- DispSync(const char* name);
+ DispSync();
~DispSync();
// reset clears the resync samples and error value.
@@ -111,8 +114,7 @@
// given phase offset from the hardware vsync events. The callback is
// called from a separate thread and it should return reasonably quickly
// (i.e. within a few hundred microseconds).
- status_t addEventListener(const char* name, nsecs_t phase,
- const sp<Callback>& callback);
+ status_t addEventListener(nsecs_t phase, const sp<Callback>& callback);
// removeEventListener removes an already-registered event callback. Once
// this method returns that callback will no longer be called by the
@@ -135,12 +137,10 @@
void resetErrorLocked();
enum { MAX_RESYNC_SAMPLES = 32 };
- enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 };
+ enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 3 };
enum { NUM_PRESENT_SAMPLES = 8 };
enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
- const char* const mName;
-
// mPeriod is the computed period of the modeled vsync events in
// nanoseconds.
nsecs_t mPeriod;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 5c78c68..f7678e4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -30,6 +30,7 @@
#include <android/configuration.h>
+#include <algorithm>
#include <inttypes.h>
extern "C" {
@@ -303,6 +304,12 @@
mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt);
}
+bool Device::hasCapability(HWC2::Capability capability) const
+{
+ return std::find(mCapabilities.cbegin(), mCapabilities.cend(),
+ capability) != mCapabilities.cend();
+}
+
void Device::loadFunctionPointers()
{
// For all of these early returns, we log an error message inside
@@ -378,8 +385,10 @@
mSetLayerDisplayFrame)) return;
if (!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha,
mSetLayerPlaneAlpha)) return;
- if (!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
- mSetLayerSidebandStream)) return;
+ if (hasCapability(Capability::SidebandStream)) {
+ if (!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
+ mSetLayerSidebandStream)) return;
+ }
if (!loadFunctionPointer(FunctionDescriptor::SetLayerSourceCrop,
mSetLayerSourceCrop)) return;
if (!loadFunctionPointer(FunctionDescriptor::SetLayerTransform,
@@ -973,6 +982,11 @@
Error Layer::setSidebandStream(const native_handle_t* stream)
{
+ if (!mDevice.hasCapability(Capability::SidebandStream)) {
+ ALOGE("Attempted to call setSidebandStream without checking that the "
+ "device supports sideband streams");
+ return Error::Unsupported;
+ }
int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice,
mDisplayId, mId, stream);
return static_cast<Error>(intError);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 7d33a0a..967add0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -89,6 +89,8 @@
// as connected
std::shared_ptr<Display> getDisplayById(hwc2_display_t id);
+ bool hasCapability(HWC2::Capability capability) const;
+
private:
// Initialization methods
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index cd2e05f..0bec0b8 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -116,22 +116,20 @@
abort();
}
- if (module->module_api_version >= 0x0200) {
- hwc2_device_t* hwc2device = nullptr;
- int error = hwc2_open(module, &hwc2device);
- if (error != 0) {
- ALOGE("Failed to open HWC2 device (%s), aborting", strerror(-error));
- abort();
- }
- mHwcDevice = std::make_unique<HWC2::Device>(hwc2device);
+ hw_device_t* device = nullptr;
+ int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);
+ if (error != 0) {
+ ALOGE("Failed to open HWC device (%s), aborting", strerror(-error));
+ abort();
+ }
+
+ uint32_t majorVersion = (device->version >> 24) & 0xF;
+ if (majorVersion == 2) {
+ mHwcDevice = std::make_unique<HWC2::Device>(
+ reinterpret_cast<hwc2_device_t*>(device));
} else {
- hwc_composer_device_1_t* hwc1device = nullptr;
- int error = hwc_open_1(module, &hwc1device);
- if (error) {
- ALOGE("Failed to open HWC1 device (%s), aborting", strerror(-error));
- abort();
- }
- mAdapter = std::make_unique<HWC2On1Adapter>(hwc1device);
+ mAdapter = std::make_unique<HWC2On1Adapter>(
+ reinterpret_cast<hwc_composer_device_1_t*>(device));
uint8_t minorVersion = mAdapter->getHwc1MinorVersion();
if (minorVersion < 1) {
ALOGE("Cannot adapt to HWC version %d.%d",
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index d37fcb2..4afd8a2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -926,16 +926,19 @@
protected:
HWCTYPE* const mLayerList;
HWCTYPE* mCurrentLayer;
- Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer) { }
+ Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
+ mIndex(0) { }
inline HWCTYPE const * getLayer() const { return mCurrentLayer; }
inline HWCTYPE* getLayer() { return mCurrentLayer; }
virtual ~Iterable() { }
+ size_t mIndex;
private:
// returns a copy of ourselves
virtual HWComposer::HWCLayer* dup() {
return new CONCRETE( static_cast<const CONCRETE&>(*this) );
}
virtual status_t setLayer(size_t index) {
+ mIndex = index;
mCurrentLayer = &mLayerList[index];
return NO_ERROR;
}
@@ -948,8 +951,12 @@
class HWCLayerVersion1 : public Iterable<HWCLayerVersion1, hwc_layer_1_t> {
struct hwc_composer_device_1* mHwc;
public:
- HWCLayerVersion1(struct hwc_composer_device_1* hwc, hwc_layer_1_t* layer)
- : Iterable<HWCLayerVersion1, hwc_layer_1_t>(layer), mHwc(hwc) { }
+ HWCLayerVersion1(struct hwc_composer_device_1* hwc, hwc_layer_1_t* layer,
+ Vector<Region>* visibleRegions,
+ Vector<Region>* surfaceDamageRegions)
+ : Iterable<HWCLayerVersion1, hwc_layer_1_t>(layer), mHwc(hwc),
+ mVisibleRegions(visibleRegions),
+ mSurfaceDamageRegions(surfaceDamageRegions) {}
virtual int32_t getCompositionType() const {
return getLayer()->compositionType;
@@ -1037,9 +1044,10 @@
}
virtual void setVisibleRegionScreen(const Region& reg) {
hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen;
- mVisibleRegion = reg;
+ mVisibleRegions->editItemAt(mIndex) = reg;
visibleRegion.rects = reinterpret_cast<hwc_rect_t const *>(
- mVisibleRegion.getArray(&visibleRegion.numRects));
+ mVisibleRegions->itemAt(mIndex).getArray(
+ &visibleRegion.numRects));
}
virtual void setSurfaceDamage(const Region& reg) {
if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_5)) {
@@ -1053,9 +1061,10 @@
surfaceDamage.rects = NULL;
return;
}
- mSurfaceDamage = reg;
+ mSurfaceDamageRegions->editItemAt(mIndex) = reg;
surfaceDamage.rects = reinterpret_cast<hwc_rect_t const *>(
- mSurfaceDamage.getArray(&surfaceDamage.numRects));
+ mSurfaceDamageRegions->itemAt(mIndex).getArray(
+ &surfaceDamage.numRects));
}
virtual void setSidebandStream(const sp<NativeHandle>& stream) {
ALOG_ASSERT(stream->handle() != NULL);
@@ -1081,11 +1090,10 @@
}
protected:
- // We need to hold "copies" of these for memory management purposes. The
- // actual hwc_layer_1_t holds pointers to the memory within. Vector<>
- // internally doesn't copy the memory unless one of the copies is modified.
- Region mVisibleRegion;
- Region mSurfaceDamage;
+ // Pointers to the vectors of Region backing-memory held in DisplayData.
+ // Only the Region at mIndex corresponds to this Layer.
+ Vector<Region>* mVisibleRegions;
+ Vector<Region>* mSurfaceDamageRegions;
};
/*
@@ -1095,11 +1103,18 @@
if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
return LayerListIterator();
}
- const DisplayData& disp(mDisplayData[id]);
+ DisplayData& disp(mDisplayData[id]);
if (!mHwc || !disp.list || index > disp.list->numHwLayers) {
return LayerListIterator();
}
- return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers), index);
+ if (disp.visibleRegions.size() < disp.list->numHwLayers) {
+ disp.visibleRegions.resize(disp.list->numHwLayers);
+ }
+ if (disp.surfaceDamageRegions.size() < disp.list->numHwLayers) {
+ disp.surfaceDamageRegions.resize(disp.list->numHwLayers);
+ }
+ return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers,
+ &disp.visibleRegions, &disp.surfaceDamageRegions), index);
}
/*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index f5f7d77..c861817 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -346,6 +346,13 @@
// protected by mEventControlLock
int32_t events;
+
+ // We need to hold "copies" of these for memory management purposes. The
+ // actual hwc_layer_1_t holds pointers to the memory within. Vector<>
+ // internally doesn't copy the memory unless one of the copies is
+ // modified.
+ Vector<Region> visibleRegions;
+ Vector<Region> surfaceDamageRegions;
};
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index dd88adb..f760200 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -44,9 +44,8 @@
return;
}
-EventThread::EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger)
+EventThread::EventThread(const sp<VSyncSource>& src)
: mVSyncSource(src),
- mFlinger(flinger),
mUseSoftwareVSync(false),
mVsyncEnabled(false),
mDebugVsyncEnabled(false),
@@ -127,9 +126,6 @@
void EventThread::requestNextVsync(
const sp<EventThread::Connection>& connection) {
Mutex::Autolock _l(mLock);
-
- mFlinger.resyncWithRateLimit();
-
if (connection->count < 0) {
connection->count = 0;
mCondition.broadcast();
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 34654fa..9ba179a 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -77,7 +77,7 @@
public:
- EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger);
+ EventThread(const sp<VSyncSource>& src);
sp<Connection> createEventConnection() const;
status_t registerDisplayEventConnection(const sp<Connection>& connection);
@@ -116,7 +116,6 @@
// constants
sp<VSyncSource> mVSyncSource;
PowerHAL mPowerHAL;
- SurfaceFlinger& mFlinger;
mutable Mutex mLock;
mutable Condition mCondition;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a2c0462..90ab70d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1309,16 +1309,16 @@
mPendingStates.push_back(mCurrentState);
}
-void Layer::popPendingState() {
- auto oldFlags = mCurrentState.flags;
- mCurrentState = mPendingStates[0];
- mCurrentState.flags = (oldFlags & ~mCurrentState.mask) |
- (mCurrentState.flags & mCurrentState.mask);
+void Layer::popPendingState(State* stateToCommit) {
+ auto oldFlags = stateToCommit->flags;
+ *stateToCommit = mPendingStates[0];
+ stateToCommit->flags = (oldFlags & ~stateToCommit->mask) |
+ (stateToCommit->flags & stateToCommit->mask);
mPendingStates.removeAt(0);
}
-bool Layer::applyPendingStates() {
+bool Layer::applyPendingStates(State* stateToCommit) {
bool stateUpdateAvailable = false;
while (!mPendingStates.empty()) {
if (mPendingStates[0].handle != nullptr) {
@@ -1327,7 +1327,7 @@
// will be visually wrong, but it should keep us from getting
// into too much trouble.
ALOGE("[%s] No local sync point found", mName.string());
- popPendingState();
+ popPendingState(stateToCommit);
stateUpdateAvailable = true;
continue;
}
@@ -1345,7 +1345,7 @@
if (mRemoteSyncPoints.front()->frameIsAvailable()) {
// Apply the state update
- popPendingState();
+ popPendingState(stateToCommit);
stateUpdateAvailable = true;
// Signal our end of the sync point and then dispose of it
@@ -1355,7 +1355,7 @@
break;
}
} else {
- popPendingState();
+ popPendingState(stateToCommit);
stateUpdateAvailable = true;
}
}
@@ -1385,12 +1385,12 @@
ATRACE_CALL();
pushPendingState();
- if (!applyPendingStates()) {
+ Layer::State c = getCurrentState();
+ if (!applyPendingStates(&c)) {
return 0;
}
const Layer::State& s(getDrawingState());
- const Layer::State& c(getCurrentState());
const bool sizeChanged = (c.requested.w != s.requested.w) ||
(c.requested.h != s.requested.h);
@@ -1454,8 +1454,7 @@
// this is used by Layer, which special cases resizes.
if (flags & eDontUpdateGeometryState) {
} else {
- Layer::State& editCurrentState(getCurrentState());
- editCurrentState.active = c.requested;
+ c.active = c.requested;
}
if (s.active != c.active) {
@@ -1475,12 +1474,12 @@
}
// Commit the transaction
- commitTransaction();
+ commitTransaction(c);
return flags;
}
-void Layer::commitTransaction() {
- mDrawingState = mCurrentState;
+void Layer::commitTransaction(const State& stateToCommit) {
+ mDrawingState = stateToCommit;
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1d73b43..c623672 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -431,7 +431,7 @@
virtual void onFrameReplaced(const BufferItem& item) override;
virtual void onSidebandStreamChanged() override;
- void commitTransaction();
+ void commitTransaction(const State& stateToCommit);
// needsLinearFiltering - true if this surface's state requires filtering
bool needsFiltering(const sp<const DisplayDevice>& hw) const;
@@ -500,8 +500,8 @@
bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
void pushPendingState();
- void popPendingState();
- bool applyPendingStates();
+ void popPendingState(State* stateToCommit);
+ bool applyPendingStates(State* stateToCommit);
public:
void notifyAvailableFrames();
private:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a448639..1f85bee 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -149,7 +149,6 @@
mLastTransactionTime(0),
mBootFinished(false),
mForceFullDamage(false),
- mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
mDaltonize(false),
@@ -332,12 +331,11 @@
class DispSyncSource : public VSyncSource, private DispSync::Callback {
public:
DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
- const char* name) :
- mName(name),
+ const char* label) :
mValue(0),
mTraceVsync(traceVsync),
- mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
- mVsyncEventLabel(String8::format("VSYNC-%s", name)),
+ mVsyncOnLabel(String8::format("VsyncOn-%s", label)),
+ mVsyncEventLabel(String8::format("VSYNC-%s", label)),
mDispSync(dispSync),
mCallbackMutex(),
mCallback(),
@@ -350,7 +348,7 @@
virtual void setVSyncEnabled(bool enable) {
Mutex::Autolock lock(mVsyncMutex);
if (enable) {
- status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
+ status_t err = mDispSync->addEventListener(mPhaseOffset,
static_cast<DispSync::Callback*>(this));
if (err != NO_ERROR) {
ALOGE("error registering vsync callback: %s (%d)",
@@ -401,7 +399,7 @@
}
// Add a listener with the new offset
- err = mDispSync->addEventListener(mName, mPhaseOffset,
+ err = mDispSync->addEventListener(mPhaseOffset,
static_cast<DispSync::Callback*>(this));
if (err != NO_ERROR) {
ALOGE("error registering vsync callback: %s (%d)",
@@ -427,8 +425,6 @@
}
}
- const char* const mName;
-
int mValue;
const bool mTraceVsync;
@@ -459,10 +455,10 @@
// start the EventThread
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
- mEventThread = new EventThread(vsyncSrc, *this);
+ mEventThread = new EventThread(vsyncSrc);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = new EventThread(sfVsyncSrc, *this);
+ mSFEventThread = new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);
// Get a RenderEngine for the given display / config (can't fail)
@@ -831,13 +827,6 @@
}
}
-void SurfaceFlinger::resyncWithRateLimit() {
- static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
- if (systemTime() - mLastSwapTime > kIgnoreDelay) {
- resyncToHardwareVsync(true);
- }
-}
-
void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) {
bool needsHwVsync = false;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 633e956..8c974d0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -409,11 +409,8 @@
* VSync
*/
void enableHardwareVsync();
- void resyncToHardwareVsync(bool makeAvailable);
void disableHardwareVsync(bool makeUnavailable);
-public:
- void resyncWithRateLimit();
-private:
+ void resyncToHardwareVsync(bool makeAvailable);
/* ------------------------------------------------------------------------
* Debugging & dumpsys
@@ -525,7 +522,7 @@
static const size_t NUM_BUCKETS = 8; // < 1-7, 7+
nsecs_t mFrameBuckets[NUM_BUCKETS];
nsecs_t mTotalTime;
- std::atomic<nsecs_t> mLastSwapTime;
+ nsecs_t mLastSwapTime;
};
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index ea685e7..e9b3d99 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -148,7 +148,6 @@
mLastTransactionTime(0),
mBootFinished(false),
mForceFullDamage(false),
- mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
mDaltonize(false),
@@ -330,12 +329,11 @@
class DispSyncSource : public VSyncSource, private DispSync::Callback {
public:
DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
- const char* name) :
- mName(name),
+ const char* label) :
mValue(0),
mTraceVsync(traceVsync),
- mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
- mVsyncEventLabel(String8::format("VSYNC-%s", name)),
+ mVsyncOnLabel(String8::format("VsyncOn-%s", label)),
+ mVsyncEventLabel(String8::format("VSYNC-%s", label)),
mDispSync(dispSync),
mCallbackMutex(),
mCallback(),
@@ -348,7 +346,7 @@
virtual void setVSyncEnabled(bool enable) {
Mutex::Autolock lock(mVsyncMutex);
if (enable) {
- status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
+ status_t err = mDispSync->addEventListener(mPhaseOffset,
static_cast<DispSync::Callback*>(this));
if (err != NO_ERROR) {
ALOGE("error registering vsync callback: %s (%d)",
@@ -399,7 +397,7 @@
}
// Add a listener with the new offset
- err = mDispSync->addEventListener(mName, mPhaseOffset,
+ err = mDispSync->addEventListener(mPhaseOffset,
static_cast<DispSync::Callback*>(this));
if (err != NO_ERROR) {
ALOGE("error registering vsync callback: %s (%d)",
@@ -425,8 +423,6 @@
}
}
- const char* const mName;
-
int mValue;
const bool mTraceVsync;
@@ -456,10 +452,10 @@
// start the EventThread
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
- mEventThread = new EventThread(vsyncSrc, *this);
+ mEventThread = new EventThread(vsyncSrc);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = new EventThread(sfVsyncSrc, *this);
+ mSFEventThread = new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);
// Initialize the H/W composer object. There may or may not be an
@@ -851,13 +847,6 @@
}
}
-void SurfaceFlinger::resyncWithRateLimit() {
- static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
- if (systemTime() - mLastSwapTime > kIgnoreDelay) {
- resyncToHardwareVsync(true);
- }
-}
-
void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
bool needsHwVsync = false;
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index d7cb899..320fddb 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -459,4 +459,61 @@
}
}
+TEST_F(LayerUpdateTest, DeferredTransactionTest) {
+ sp<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before anything");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel( 32, 32, 63, 63, 195);
+ sc->checkPixel( 96, 96, 195, 63, 63);
+ sc->checkPixel(160, 160, 63, 63, 195);
+ }
+
+ // set up two deferred transactions on different frames
+ SurfaceComposerClient::openGlobalTransaction();
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75));
+ mFGSurfaceControl->deferTransactionUntil(mSyncSurfaceControl->getHandle(),
+ mSyncSurfaceControl->getSurface()->getNextFrameNumber());
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128,128));
+ mFGSurfaceControl->deferTransactionUntil(mSyncSurfaceControl->getHandle(),
+ mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ {
+ SCOPED_TRACE("before any trigger");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel( 32, 32, 63, 63, 195);
+ sc->checkPixel( 96, 96, 195, 63, 63);
+ sc->checkPixel(160, 160, 63, 63, 195);
+ }
+
+ // should trigger the first deferred transaction, but not the second one
+ fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+ {
+ SCOPED_TRACE("after first trigger");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel( 32, 32, 63, 63, 195);
+ sc->checkPixel( 96, 96, 162, 63, 96);
+ sc->checkPixel(160, 160, 63, 63, 195);
+ }
+
+ // should show up immediately since it's not deferred
+ SurfaceComposerClient::openGlobalTransaction();
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0));
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ // trigger the second deferred transaction
+ fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+ {
+ SCOPED_TRACE("after second trigger");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel( 32, 32, 63, 63, 195);
+ sc->checkPixel( 96, 96, 63, 63, 195);
+ sc->checkPixel(160, 160, 195, 63, 63);
+ }
+}
+
}
diff --git a/vulkan/include/vulkan/vk_layer_interface.h b/vulkan/include/vulkan/vk_layer_interface.h
index 5aae51e..f2a5232 100644
--- a/vulkan/include/vulkan/vk_layer_interface.h
+++ b/vulkan/include/vulkan/vk_layer_interface.h
@@ -14,10 +14,6 @@
* The above copyright notice(s) and this permission notice shall be included in
* all copies or substantial portions of the Materials.
*
- * The Materials are Confidential Information as defined by the Khronos
- * Membership Agreement until designated non-confidential by Khronos, at which
- * point this condition clause shall be removed.
- *
* THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -37,38 +33,18 @@
typedef enum VkLayerFunction_ {
VK_LAYER_FUNCTION_LINK = 0,
- VK_LAYER_FUNCTION_DEVICE = 1,
- VK_LAYER_FUNCTION_INSTANCE = 2
+ VK_LAYER_FUNCTION_DATA_CALLBACK = 1
} VkLayerFunction;
-/*
- * When creating the device chain the loader needs to pass
- * down information about it's device structure needed at
- * the end of the chain. Passing the data via the
- * VkLayerInstanceInfo avoids issues with finding the
- * exact instance being used.
- */
-typedef struct VkLayerInstanceInfo_ {
- void* instance_info;
- PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
-} VkLayerInstanceInfo;
-
typedef struct VkLayerInstanceLink_ {
struct VkLayerInstanceLink_* pNext;
PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
} VkLayerInstanceLink;
-/*
- * When creating the device chain the loader needs to pass
- * down information about it's device structure needed at
- * the end of the chain. Passing the data via the
- * VkLayerDeviceInfo avoids issues with finding the
- * exact instance being used.
- */
-typedef struct VkLayerDeviceInfo_ {
- void* device_info;
- PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
-} VkLayerDeviceInfo;
+typedef VkResult(VKAPI_PTR* PFN_vkSetInstanceLoaderData)(VkInstance instance,
+ void* object);
+typedef VkResult(VKAPI_PTR* PFN_vkSetDeviceLoaderData)(VkDevice device,
+ void* object);
typedef struct {
VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
@@ -76,7 +52,7 @@
VkLayerFunction function;
union {
VkLayerInstanceLink* pLayerInfo;
- VkLayerInstanceInfo instanceInfo;
+ PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData;
} u;
} VkLayerInstanceCreateInfo;
@@ -92,6 +68,6 @@
VkLayerFunction function;
union {
VkLayerDeviceLink* pLayerInfo;
- VkLayerDeviceInfo deviceInfo;
+ PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData;
} u;
} VkLayerDeviceCreateInfo;
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk
index 0979471..7830c26 100644
--- a/vulkan/libvulkan/Android.mk
+++ b/vulkan/libvulkan/Android.mk
@@ -45,6 +45,7 @@
driver.cpp \
driver_gen.cpp \
layers_extensions.cpp \
+ stubhal.cpp \
swapchain.cpp \
vulkan_loader_data.cpp
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 4e19af5..e7f10b3 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -431,6 +431,11 @@
uint32_t count,
const VkAllocationCallbacks& allocator);
+ static VKAPI_ATTR VkResult SetInstanceLoaderData(VkInstance instance,
+ void* object);
+ static VKAPI_ATTR VkResult SetDeviceLoaderData(VkDevice device,
+ void* object);
+
static VKAPI_ATTR VkBool32
DebugReportCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT obj_type,
@@ -454,12 +459,13 @@
PFN_vkGetDeviceProcAddr get_device_proc_addr_;
union {
- VkLayerInstanceCreateInfo instance_chain_info_;
- VkLayerDeviceCreateInfo device_chain_info_;
+ VkLayerInstanceCreateInfo instance_chain_info_[2];
+ VkLayerDeviceCreateInfo device_chain_info_[2];
};
VkExtensionProperties* driver_extensions_;
uint32_t driver_extension_count_;
+ std::bitset<driver::ProcHook::EXTENSION_COUNT> enabled_extensions_;
};
LayerChain::LayerChain(bool is_instance, const VkAllocationCallbacks& allocator)
@@ -472,7 +478,9 @@
get_instance_proc_addr_(nullptr),
get_device_proc_addr_(nullptr),
driver_extensions_(nullptr),
- driver_extension_count_(0) {}
+ driver_extension_count_(0) {
+ enabled_extensions_.set(driver::ProcHook::EXTENSION_CORE);
+}
LayerChain::~LayerChain() {
allocator_.pfnFree(allocator_.pUserData, driver_extensions_);
@@ -616,18 +624,19 @@
void LayerChain::ModifyCreateInfo(VkInstanceCreateInfo& info) {
if (layer_count_) {
- const ActiveLayer& layer = layers_[0];
+ auto& link_info = instance_chain_info_[1];
+ link_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
+ link_info.pNext = info.pNext;
+ link_info.function = VK_LAYER_FUNCTION_LINK;
+ link_info.u.pLayerInfo = &layers_[0].instance_link;
- instance_chain_info_.sType =
- VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
- instance_chain_info_.function = VK_LAYER_FUNCTION_LINK;
- // TODO fix vk_layer_interface.h and get rid of const_cast?
- instance_chain_info_.u.pLayerInfo =
- const_cast<VkLayerInstanceLink*>(&layer.instance_link);
+ auto& cb_info = instance_chain_info_[0];
+ cb_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
+ cb_info.pNext = &link_info;
+ cb_info.function = VK_LAYER_FUNCTION_DATA_CALLBACK;
+ cb_info.u.pfnSetInstanceLoaderData = SetInstanceLoaderData;
- // insert layer info
- instance_chain_info_.pNext = info.pNext;
- info.pNext = &instance_chain_info_;
+ info.pNext = &cb_info;
}
if (override_layers_.Count()) {
@@ -643,17 +652,19 @@
void LayerChain::ModifyCreateInfo(VkDeviceCreateInfo& info) {
if (layer_count_) {
- const ActiveLayer& layer = layers_[0];
+ auto& link_info = device_chain_info_[1];
+ link_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
+ link_info.pNext = info.pNext;
+ link_info.function = VK_LAYER_FUNCTION_LINK;
+ link_info.u.pLayerInfo = &layers_[0].device_link;
- device_chain_info_.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
- device_chain_info_.function = VK_LAYER_FUNCTION_LINK;
- // TODO fix vk_layer_interface.h and get rid of const_cast?
- device_chain_info_.u.pLayerInfo =
- const_cast<VkLayerDeviceLink*>(&layer.device_link);
+ auto& cb_info = device_chain_info_[0];
+ cb_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
+ cb_info.pNext = &link_info;
+ cb_info.function = VK_LAYER_FUNCTION_DATA_CALLBACK;
+ cb_info.u.pfnSetDeviceLoaderData = SetDeviceLoaderData;
- // insert layer info
- device_chain_info_.pNext = info.pNext;
- info.pNext = &device_chain_info_;
+ info.pNext = &cb_info;
}
if (override_layers_.Count()) {
@@ -686,11 +697,11 @@
// initialize InstanceData
InstanceData& data = GetData(instance);
- memset(&data, 0, sizeof(data));
data.instance = instance;
- if (!InitDispatchTable(instance, get_instance_proc_addr_)) {
+ if (!InitDispatchTable(instance, get_instance_proc_addr_,
+ enabled_extensions_)) {
if (data.dispatch.DestroyInstance)
data.dispatch.DestroyInstance(instance, allocator);
@@ -766,9 +777,8 @@
// initialize DeviceData
DeviceData& data = GetData(dev);
- memset(&data, 0, sizeof(data));
- if (!InitDispatchTable(dev, get_device_proc_addr_)) {
+ if (!InitDispatchTable(dev, get_device_proc_addr_, enabled_extensions_)) {
if (data.dispatch.DestroyDevice)
data.dispatch.DestroyDevice(dev, allocator);
@@ -808,6 +818,10 @@
ALOGE("Failed to enable missing instance extension %s", name);
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
+
+ auto ext_bit = driver::GetProcHookExtension(name);
+ if (ext_bit != driver::ProcHook::EXTENSION_UNKNOWN)
+ enabled_extensions_.set(ext_bit);
}
return VK_SUCCESS;
@@ -841,6 +855,10 @@
ALOGE("Failed to enable missing device extension %s", name);
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
+
+ auto ext_bit = driver::GetProcHookExtension(name);
+ if (ext_bit != driver::ProcHook::EXTENSION_UNKNOWN)
+ enabled_extensions_.set(ext_bit);
}
return VK_SUCCESS;
@@ -890,6 +908,24 @@
allocator.pfnFree(allocator.pUserData, layers);
}
+VkResult LayerChain::SetInstanceLoaderData(VkInstance instance, void* object) {
+ driver::InstanceDispatchable dispatchable =
+ reinterpret_cast<driver::InstanceDispatchable>(object);
+
+ return (driver::SetDataInternal(dispatchable, &driver::GetData(instance)))
+ ? VK_SUCCESS
+ : VK_ERROR_INITIALIZATION_FAILED;
+}
+
+VkResult LayerChain::SetDeviceLoaderData(VkDevice device, void* object) {
+ driver::DeviceDispatchable dispatchable =
+ reinterpret_cast<driver::DeviceDispatchable>(object);
+
+ return (driver::SetDataInternal(dispatchable, &driver::GetData(device)))
+ ? VK_SUCCESS
+ : VK_ERROR_INITIALIZATION_FAILED;
+}
+
VkBool32 LayerChain::DebugReportCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT obj_type,
uint64_t obj,
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 0c4cd9a..fe4136f 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -37,14 +37,81 @@
} \
} while (0)
-// TODO do we want to point to a stub or nullptr when ext is not enabled?
-#define INIT_PROC_EXT(ext, obj, proc) \
- do { \
- INIT_PROC(obj, proc); \
+// Exported extension functions may be invoked even when their extensions
+// are disabled. Dispatch to stubs when that happens.
+#define INIT_PROC_EXT(ext, obj, proc) \
+ do { \
+ if (extensions[driver::ProcHook::ext]) \
+ INIT_PROC(obj, proc); \
+ else \
+ data.dispatch.proc = disabled##proc; \
} while (0)
-bool InitDispatchTable(VkInstance instance,
- PFN_vkGetInstanceProcAddr get_proc) {
+namespace {
+
+// clang-format off
+
+VKAPI_ATTR void disabledDestroySurfaceKHR(VkInstance, VkSurfaceKHR, const VkAllocationCallbacks*) {
+ ALOGE("VK_KHR_surface not enabled. vkDestroySurfaceKHR not executed.");
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice, uint32_t, VkSurfaceKHR, VkBool32*) {
+ ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceSupportKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice, VkSurfaceKHR, VkSurfaceCapabilitiesKHR*) {
+ ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceCapabilitiesKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkSurfaceFormatKHR*) {
+ ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceFormatsKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkPresentModeKHR*) {
+ ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfacePresentModesKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledCreateSwapchainKHR(VkDevice, const VkSwapchainCreateInfoKHR*, const VkAllocationCallbacks*, VkSwapchainKHR*) {
+ ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR void disabledDestroySwapchainKHR(VkDevice, VkSwapchainKHR, const VkAllocationCallbacks*) {
+ ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+}
+
+VKAPI_ATTR VkResult disabledGetSwapchainImagesKHR(VkDevice, VkSwapchainKHR, uint32_t*, VkImage*) {
+ ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledAcquireNextImageKHR(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t*) {
+ ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledQueuePresentKHR(VkQueue, const VkPresentInfoKHR*) {
+ ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
+ ALOGE("VK_KHR_android_surface not enabled. vkCreateAndroidSurfaceKHR not executed.");
+ return VK_SUCCESS;
+}
+
+// clang-format on
+
+} // anonymous
+
+bool InitDispatchTable(
+ VkInstance instance,
+ PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
auto& data = GetData(instance);
bool success = true;
@@ -73,7 +140,10 @@
return success;
}
-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc) {
+bool InitDispatchTable(
+ VkDevice dev,
+ PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
auto& data = GetData(dev);
bool success = true;
@@ -272,11 +342,14 @@
std::binary_search(
known_non_device_names, known_non_device_names + count, pName,
[](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
- ALOGE("vkGetDeviceProcAddr called with %s", pName);
+ ALOGE("vkGetDeviceProcAddr called with %s", (pName) ? pName : "(null)");
return nullptr;
}
// clang-format off
+ if (strcmp(pName, "vkGetDeviceProcAddr") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceProcAddr);
+ if (strcmp(pName, "vkDestroyDevice") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::DestroyDevice);
+
return vulkan::api::GetData(device).dispatch.GetDeviceProcAddr(device, pName);
}
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 54be83b..779b654 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -19,7 +19,9 @@
#ifndef LIBVULKAN_API_GEN_H
#define LIBVULKAN_API_GEN_H
+#include <bitset>
#include <vulkan/vulkan.h>
+#include "driver_gen.h"
namespace vulkan {
namespace api {
@@ -179,8 +181,14 @@
// clang-format on
};
-bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+bool InitDispatchTable(
+ VkInstance instance,
+ PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
+bool InitDispatchTable(
+ VkDevice dev,
+ PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
} // namespace api
} // namespace vulkan
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index afe0d84..3968371 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -37,7 +37,9 @@
#ifndef LIBVULKAN_API_GEN_H
#define LIBVULKAN_API_GEN_H
¶
+#include <bitset>
#include <vulkan/vulkan.h>
+#include "driver_gen.h"
¶
namespace vulkan {«
namespace api {«
@@ -62,8 +64,14 @@
// clang-format on
};
¶
-bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+bool InitDispatchTable(
+ VkInstance instance,
+ PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions);
+bool InitDispatchTable(
+ VkDevice dev,
+ PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions);
¶
»} // namespace api
»} // namespace vulkan
@@ -95,7 +103,21 @@
¶
{{Macro "api.C++.DefineInitProcExtMacro"}}
¶
-bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc) {
+namespace {«
+¶
+// clang-format off
+¶
+{{range $f := AllCommands $}}
+ {{Macro "api.C++.DefineExtensionStub" $f}}
+{{end}}
+// clang-format on
+¶
+»} // anonymous
+¶
+bool InitDispatchTable(
+ VkInstance instance,
+ PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) {
auto& data = GetData(instance);
bool success = true;
¶
@@ -110,7 +132,10 @@
return success;
}
¶
-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc) {
+bool InitDispatchTable(
+ VkDevice dev,
+ PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) {
auto& data = GetData(dev);
bool success = true;
¶
@@ -163,6 +188,7 @@
#ifndef LIBVULKAN_DRIVER_GEN_H
#define LIBVULKAN_DRIVER_GEN_H
¶
+#include <bitset>
#include <vulkan/vulkan.h>
#include <vulkan/vk_android_native_buffer.h>
¶
@@ -194,8 +220,10 @@
const ProcHook* GetProcHook(const char* name);
ProcHook::Extension GetProcHookExtension(const char* name);
¶
-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT> &extensions);
+bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT> &extensions);
¶
»} // namespace driver
»} // namespace vulkan
@@ -228,7 +256,7 @@
// clang-format off
¶
{{range $f := AllCommands $}}
- {{Macro "driver.C++.DefineProcHookStubs" $f}}
+ {{Macro "driver.C++.DefineProcHookStub" $f}}
{{end}}
// clang-format on
¶
@@ -273,7 +301,8 @@
¶
{{Macro "driver.C++.DefineInitProcExtMacro"}}
¶
-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc)
+bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
{
auto& data = GetData(instance);
bool success = true;
@@ -289,7 +318,8 @@
return success;
}
¶
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc)
+bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
{
auto& data = GetData(dev);
bool success = true;
@@ -428,14 +458,42 @@
-------------------------------------------------------------------------------
*/}}
{{define "api.C++.DefineInitProcExtMacro"}}
- // TODO do we want to point to a stub or nullptr when ext is not enabled?
+ // Exported extension functions may be invoked even when their extensions
+ // are disabled. Dispatch to stubs when that happens.
#define INIT_PROC_EXT(ext, obj, proc) do { \
- INIT_PROC(obj, proc); \
+ if (extensions[driver::ProcHook::ext]) \
+ INIT_PROC(obj, proc); \
+ else \
+ data.dispatch.proc = disabled ## proc; \
} while(0)
{{end}}
{{/*
+-------------------------------------------------------------------------------
+ Emits a stub for an exported extension function.
+-------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.DefineExtensionStub"}}
+ {{AssertType $ "Function"}}
+
+ {{$ext := GetAnnotation $ "extension"}}
+ {{if and $ext (Macro "IsFunctionExported" $)}}
+ {{$ext_name := index $ext.Arguments 0}}
+
+ {{$base := (Macro "BaseName" $)}}
+ {{$unnamed_params := (ForEach $.CallParameters "ParameterType" | JoinWith ", ")}}
+
+ VKAPI_ATTR {{Node "Type" $.Return}} disabled{{$base}}({{$unnamed_params}}) {
+ ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
+ {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
+ }
+ ¶
+ {{end}}
+{{end}}
+
+
+{{/*
------------------------------------------------------------------------------
Emits code for vkGetInstanceProcAddr for function interception.
------------------------------------------------------------------------------
@@ -529,11 +587,25 @@
std::binary_search(
known_non_device_names, known_non_device_names + count, pName,
[](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
- ALOGE("vkGetDeviceProcAddr called with %s", pName);
+ ALOGE("vkGetDeviceProcAddr called with %s", (pName) ? pName : "(null)");
return nullptr;
}
// clang-format off
¶
+ {{range $f := AllCommands $}}
+ {{if (Macro "IsDeviceDispatched" $f)}}
+ {{ if (Macro "api.IsIntercepted" $f)}}
+ if (strcmp(pName, "{{$f.Name}}") == 0) return §
+ reinterpret_cast<PFN_vkVoidFunction>(§
+ vulkan::api::{{Macro "BaseName" $f}});
+ {{else if eq $f.Name "vkGetDeviceProcAddr"}}
+ if (strcmp(pName, "{{$f.Name}}") == 0) return §
+ reinterpret_cast<PFN_vkVoidFunction>(§
+ {{$f.Name}});
+ {{end}}
+ {{end}}
+ {{end}}
+ ¶
{{end}}
@@ -628,13 +700,13 @@
{{/*
------------------------------------------------------------------------------
- Emits true if a function needs ProcHook stubs.
+ Emits true if a function needs a ProcHook stub.
------------------------------------------------------------------------------
*/}}
-{{define "driver.NeedProcHookStubs"}}
+{{define "driver.NeedProcHookStub"}}
{{AssertType $ "Function"}}
- {{if (Macro "driver.IsIntercepted" $)}}
+ {{if and (Macro "driver.IsIntercepted" $) (Macro "IsDeviceDispatched" $)}}
{{$ext := GetAnnotation $ "extension"}}
{{if $ext}}
{{if not (Macro "IsExtensionInternal" $ext)}}true{{end}}
@@ -672,8 +744,7 @@
Extension extension;
¶
PFN_vkVoidFunction proc;
- PFN_vkVoidFunction disabled_proc; // nullptr for global hooks
- PFN_vkVoidFunction checked_proc; // nullptr for global/instance hooks
+ PFN_vkVoidFunction checked_proc; // always nullptr for non-device hooks
};
{{end}}
@@ -685,7 +756,7 @@
*/}}
{{define "driver.C++.DefineInitProcExtMacro"}}
#define INIT_PROC_EXT(ext, obj, proc) do { \
- if (data.hal_extensions[ProcHook::ext]) \
+ if (extensions[ProcHook::ext]) \
INIT_PROC(obj, proc); \
} while(0)
{{end}}
@@ -693,35 +764,31 @@
{{/*
-------------------------------------------------------------------------------
- Emits definitions of stub functions for ProcHook.
+ Emits a stub for ProcHook::checked_proc.
-------------------------------------------------------------------------------
*/}}
-{{define "driver.C++.DefineProcHookStubs"}}
+{{define "driver.C++.DefineProcHookStub"}}
{{AssertType $ "Function"}}
- {{if (Macro "driver.NeedProcHookStubs" $)}}
+ {{if (Macro "driver.NeedProcHookStub" $)}}
{{$ext := GetAnnotation $ "extension"}}
{{$ext_name := index $ext.Arguments 0}}
{{$base := (Macro "BaseName" $)}}
{{$unnamed_params := (ForEach $.CallParameters "ParameterType" | JoinWith ", ")}}
- VKAPI_ATTR {{Node "Type" $.Return}} disabled{{$base}}({{$unnamed_params}}) {
- ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
- {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
- }
- {{if (Macro "IsDeviceDispatched" $)}}
- ¶
- VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) {
- {{if not (IsVoid $.Return.Type)}}return §{{end}}
+ VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) {
+ {{$p0 := index $.CallParameters 0}}
+ {{$ext_hook := Strings ("ProcHook::") (Macro "BaseName" $ext)}}
- {{$p0 := index $.CallParameters 0}}
- {{$ext_hook := Strings ("ProcHook::") (Macro "BaseName" $ext)}}
- (GetData({{$p0.Name}}).hook_extensions[{{$ext_hook}}]) ? §
- {{$base}}({{Macro "Arguments" $}}) : §
- disabled{{$base}}({{Macro "Arguments" $}});
+ if (GetData({{$p0.Name}}).hook_extensions[{{$ext_hook}}]) {
+ {{if not (IsVoid $.Return.Type)}}return §{{end}}
+ {{$base}}({{Macro "Arguments" $}});
+ } else {
+ ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
+ {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
}
- {{end}}
+ }
¶
{{end}}
{{end}}
@@ -748,7 +815,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
nullptr,
- nullptr,
},
{{end}}
@@ -774,17 +840,14 @@
{{if (Macro "IsExtensionInternal" $ext)}}
nullptr,
nullptr,
- nullptr,
{{else}}
reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
- reinterpret_cast<PFN_vkVoidFunction>(disabled{{$base}}),
nullptr,
{{end}}
{{else}}
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
nullptr,
- nullptr,
{{end}}
},
{{end}}
@@ -811,17 +874,14 @@
{{if (Macro "IsExtensionInternal" $ext)}}
nullptr,
nullptr,
- nullptr,
{{else}}
reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
- reinterpret_cast<PFN_vkVoidFunction>(disabled{{$base}}),
reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}),
{{end}}
{{else}}
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
nullptr,
- nullptr,
{{end}}
},
{{end}}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 9dbdcc2..17ccc72 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -23,6 +23,7 @@
#include <sys/prctl.h>
#include "driver.h"
+#include "stubhal.h"
// #define ENABLE_ALLOC_CALLSTACKS 1
#if ENABLE_ALLOC_CALLSTACKS
@@ -47,7 +48,7 @@
class CreateInfoWrapper {
public:
- CreateInfoWrapper(hwvulkan_device_t* hw_dev,
+ CreateInfoWrapper(const hwvulkan_device_t* hw_dev,
const VkInstanceCreateInfo& create_info,
const VkAllocationCallbacks& allocator);
CreateInfoWrapper(VkPhysicalDevice physical_dev,
@@ -87,7 +88,7 @@
const VkAllocationCallbacks& allocator_;
union {
- hwvulkan_device_t* hw_dev_;
+ const hwvulkan_device_t* hw_dev_;
VkPhysicalDevice physical_dev_;
};
@@ -102,7 +103,7 @@
std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions_;
};
-CreateInfoWrapper::CreateInfoWrapper(hwvulkan_device_t* hw_dev,
+CreateInfoWrapper::CreateInfoWrapper(const hwvulkan_device_t* hw_dev,
const VkInstanceCreateInfo& create_info,
const VkAllocationCallbacks& allocator)
: is_instance_(true),
@@ -330,17 +331,19 @@
if (strcmp(name, props.extensionName) != 0)
continue;
- if (ext_bit == ProcHook::ANDROID_native_buffer)
- hook_extensions_.set(ProcHook::KHR_swapchain);
-
filter.names[filter.name_count++] = name;
- hal_extensions_.set(ext_bit);
+ if (ext_bit != ProcHook::EXTENSION_UNKNOWN) {
+ if (ext_bit == ProcHook::ANDROID_native_buffer)
+ hook_extensions_.set(ProcHook::KHR_swapchain);
+
+ hal_extensions_.set(ext_bit);
+ }
break;
}
}
-hwvulkan_device_t* g_hwdevice = nullptr;
+const hwvulkan_device_t* g_hwdevice = nullptr;
VKAPI_ATTR void* DefaultAllocate(void*,
size_t size,
@@ -428,15 +431,17 @@
}
bool OpenHAL() {
- if (g_hwdevice)
- return true;
+ ALOG_ASSERT(!g_hwdevice, "OpenHAL called more than once");
+
+ // Use a stub device unless we successfully open a real HAL device.
+ g_hwdevice = &stubhal::kDevice;
const hwvulkan_module_t* module;
int result =
hw_get_module("vulkan", reinterpret_cast<const hw_module_t**>(&module));
if (result != 0) {
- ALOGE("failed to load vulkan hal: %s (%d)", strerror(-result), result);
- return false;
+ ALOGV("no Vulkan HAL present, using stub HAL");
+ return true;
}
hwvulkan_device_t* device;
@@ -444,7 +449,8 @@
module->common.methods->open(&module->common, HWVULKAN_DEVICE_0,
reinterpret_cast<hw_device_t**>(&device));
if (result != 0) {
- ALOGE("failed to open vulkan driver: %s (%d)", strerror(-result),
+ // Any device with a Vulkan HAL should be able to open the device.
+ ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result),
result);
return false;
}
@@ -493,7 +499,7 @@
case ProcHook::INSTANCE:
proc = (GetData(instance).hook_extensions[hook->extension])
? hook->proc
- : hook->disabled_proc;
+ : nullptr;
break;
case ProcHook::DEVICE:
proc = (hook->extension == ProcHook::EXTENSION_CORE)
@@ -522,9 +528,8 @@
return nullptr;
}
- return (GetData(device).hook_extensions[hook->extension])
- ? hook->proc
- : hook->disabled_proc;
+ return (GetData(device).hook_extensions[hook->extension]) ? hook->proc
+ : nullptr;
}
VkResult EnumerateInstanceExtensionProperties(
@@ -610,7 +615,6 @@
return VK_ERROR_OUT_OF_HOST_MEMORY;
data->hook_extensions |= wrapper.GetHookExtensions();
- data->hal_extensions |= wrapper.GetHalExtensions();
// call into the driver
VkInstance instance;
@@ -624,7 +628,8 @@
// initialize InstanceDriverTable
if (!SetData(instance, *data) ||
- !InitDriverTable(instance, g_hwdevice->GetInstanceProcAddr)) {
+ !InitDriverTable(instance, g_hwdevice->GetInstanceProcAddr,
+ wrapper.GetHalExtensions())) {
data->driver.DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
g_hwdevice->GetInstanceProcAddr(instance, "vkDestroyInstance"));
if (data->driver.DestroyInstance)
@@ -681,7 +686,6 @@
return VK_ERROR_OUT_OF_HOST_MEMORY;
data->hook_extensions |= wrapper.GetHookExtensions();
- data->hal_extensions |= wrapper.GetHalExtensions();
// call into the driver
VkDevice dev;
@@ -695,7 +699,8 @@
// initialize DeviceDriverTable
if (!SetData(dev, *data) ||
- !InitDriverTable(dev, instance_data.get_device_proc_addr)) {
+ !InitDriverTable(dev, instance_data.get_device_proc_addr,
+ wrapper.GetHalExtensions())) {
data->driver.DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(
instance_data.get_device_proc_addr(dev, "vkDestroyDevice"));
if (data->driver.DestroyDevice)
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 22db93f..85b36b6 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -64,6 +64,9 @@
namespace driver {
+VK_DEFINE_HANDLE(InstanceDispatchable)
+VK_DEFINE_HANDLE(DeviceDispatchable)
+
struct InstanceData {
InstanceData(const VkAllocationCallbacks& alloc)
: opaque_api_data(),
@@ -71,7 +74,6 @@
driver(),
get_device_proc_addr(nullptr) {
hook_extensions.set(ProcHook::EXTENSION_CORE);
- hal_extensions.set(ProcHook::EXTENSION_CORE);
}
api::InstanceData opaque_api_data;
@@ -79,7 +81,6 @@
const VkAllocationCallbacks allocator;
std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
- std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions;
InstanceDriverTable driver;
PFN_vkGetDeviceProcAddr get_device_proc_addr;
@@ -91,7 +92,6 @@
DeviceData(const VkAllocationCallbacks& alloc)
: opaque_api_data(), allocator(alloc), driver() {
hook_extensions.set(ProcHook::EXTENSION_CORE);
- hal_extensions.set(ProcHook::EXTENSION_CORE);
}
api::DeviceData opaque_api_data;
@@ -99,7 +99,6 @@
const VkAllocationCallbacks allocator;
std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
- std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions;
DeviceDriverTable driver;
};
@@ -127,12 +126,15 @@
template <typename DispatchableType>
void StaticAssertDispatchable(DispatchableType) {
- static_assert(std::is_same<DispatchableType, VkInstance>::value ||
- std::is_same<DispatchableType, VkPhysicalDevice>::value ||
- std::is_same<DispatchableType, VkDevice>::value ||
- std::is_same<DispatchableType, VkQueue>::value ||
- std::is_same<DispatchableType, VkCommandBuffer>::value,
- "unrecognized dispatchable type");
+ static_assert(
+ std::is_same<DispatchableType, VkInstance>::value ||
+ std::is_same<DispatchableType, VkPhysicalDevice>::value ||
+ std::is_same<DispatchableType, VkDevice>::value ||
+ std::is_same<DispatchableType, InstanceDispatchable>::value ||
+ std::is_same<DispatchableType, VkQueue>::value ||
+ std::is_same<DispatchableType, VkCommandBuffer>::value ||
+ std::is_same<DispatchableType, DeviceDispatchable>::value,
+ "unrecognized dispatchable type");
}
template <typename DispatchableType>
@@ -170,6 +172,11 @@
return SetDataInternal(physical_dev, &data);
}
+inline bool SetData(InstanceDispatchable dispatchable,
+ const InstanceData& data) {
+ return SetDataInternal(dispatchable, &data);
+}
+
inline bool SetData(VkDevice dev, const DeviceData& data) {
return SetDataInternal(dev, &data);
}
@@ -182,6 +189,10 @@
return SetDataInternal(cmd, &data);
}
+inline bool SetData(DeviceDispatchable dispatchable, const DeviceData& data) {
+ return SetDataInternal(dispatchable, &data);
+}
+
inline InstanceData& GetData(VkInstance instance) {
return *reinterpret_cast<InstanceData*>(GetDataInternal(instance));
}
@@ -190,6 +201,10 @@
return *reinterpret_cast<InstanceData*>(GetDataInternal(physical_dev));
}
+inline InstanceData& GetData(InstanceDispatchable dispatchable) {
+ return *reinterpret_cast<InstanceData*>(GetDataInternal(dispatchable));
+}
+
inline DeviceData& GetData(VkDevice dev) {
return *reinterpret_cast<DeviceData*>(GetDataInternal(dev));
}
@@ -202,6 +217,10 @@
return *reinterpret_cast<DeviceData*>(GetDataInternal(cmd));
}
+inline DeviceData& GetData(DeviceDispatchable dispatchable) {
+ return *reinterpret_cast<DeviceData*>(GetDataInternal(dispatchable));
+}
+
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 8b816ba..5bd2159 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -29,90 +29,48 @@
// clang-format off
-VKAPI_ATTR void disabledDestroySurfaceKHR(VkInstance, VkSurfaceKHR, const VkAllocationCallbacks*) {
- ALOGE("VK_KHR_surface not enabled. vkDestroySurfaceKHR not executed.");
-}
-
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice, uint32_t, VkSurfaceKHR, VkBool32*) {
- ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceSupportKHR not executed.");
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice, VkSurfaceKHR, VkSurfaceCapabilitiesKHR*) {
- ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceCapabilitiesKHR not executed.");
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkSurfaceFormatKHR*) {
- ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceFormatsKHR not executed.");
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkPresentModeKHR*) {
- ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfacePresentModesKHR not executed.");
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult disabledCreateSwapchainKHR(VkDevice, const VkSwapchainCreateInfoKHR*, const VkAllocationCallbacks*, VkSwapchainKHR*) {
- ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
- return VK_SUCCESS;
-}
-
VKAPI_ATTR VkResult checkedCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
- return (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain) : disabledCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
-}
-
-VKAPI_ATTR void disabledDestroySwapchainKHR(VkDevice, VkSwapchainKHR, const VkAllocationCallbacks*) {
- ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+ if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+ return CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
+ } else {
+ ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
+ return VK_SUCCESS;
+ }
}
VKAPI_ATTR void checkedDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) {
- (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? DestroySwapchainKHR(device, swapchain, pAllocator) : disabledDestroySwapchainKHR(device, swapchain, pAllocator);
-}
-
-VKAPI_ATTR VkResult disabledGetSwapchainImagesKHR(VkDevice, VkSwapchainKHR, uint32_t*, VkImage*) {
- ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
- return VK_SUCCESS;
+ if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+ DestroySwapchainKHR(device, swapchain, pAllocator);
+ } else {
+ ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+ }
}
VKAPI_ATTR VkResult checkedGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
- return (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages) : disabledGetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
-}
-
-VKAPI_ATTR VkResult disabledAcquireNextImageKHR(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t*) {
- ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
- return VK_SUCCESS;
+ if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+ return GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
+ } else {
+ ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
+ return VK_SUCCESS;
+ }
}
VKAPI_ATTR VkResult checkedAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) {
- return (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex) : disabledAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
-}
-
-VKAPI_ATTR VkResult disabledQueuePresentKHR(VkQueue, const VkPresentInfoKHR*) {
- ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
- return VK_SUCCESS;
+ if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+ return AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
+ } else {
+ ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
+ return VK_SUCCESS;
+ }
}
VKAPI_ATTR VkResult checkedQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) {
- return (GetData(queue).hook_extensions[ProcHook::KHR_swapchain]) ? QueuePresentKHR(queue, pPresentInfo) : disabledQueuePresentKHR(queue, pPresentInfo);
-}
-
-VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
- ALOGE("VK_KHR_android_surface not enabled. vkCreateAndroidSurfaceKHR not executed.");
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult disabledCreateDebugReportCallbackEXT(VkInstance, const VkDebugReportCallbackCreateInfoEXT*, const VkAllocationCallbacks*, VkDebugReportCallbackEXT*) {
- ALOGE("VK_EXT_debug_report not enabled. vkCreateDebugReportCallbackEXT not executed.");
- return VK_SUCCESS;
-}
-
-VKAPI_ATTR void disabledDestroyDebugReportCallbackEXT(VkInstance, VkDebugReportCallbackEXT, const VkAllocationCallbacks*) {
- ALOGE("VK_EXT_debug_report not enabled. vkDestroyDebugReportCallbackEXT not executed.");
-}
-
-VKAPI_ATTR void disabledDebugReportMessageEXT(VkInstance, VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char*, const char*) {
- ALOGE("VK_EXT_debug_report not enabled. vkDebugReportMessageEXT not executed.");
+ if (GetData(queue).hook_extensions[ProcHook::KHR_swapchain]) {
+ return QueuePresentKHR(queue, pPresentInfo);
+ } else {
+ ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
+ return VK_SUCCESS;
+ }
}
// clang-format on
@@ -125,14 +83,12 @@
ProcHook::ANDROID_native_buffer,
nullptr,
nullptr,
- nullptr,
},
{
"vkAcquireNextImageKHR",
ProcHook::DEVICE,
ProcHook::KHR_swapchain,
reinterpret_cast<PFN_vkVoidFunction>(AcquireNextImageKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledAcquireNextImageKHR),
reinterpret_cast<PFN_vkVoidFunction>(checkedAcquireNextImageKHR),
},
{
@@ -141,14 +97,12 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(AllocateCommandBuffers),
nullptr,
- nullptr,
},
{
"vkCreateAndroidSurfaceKHR",
ProcHook::INSTANCE,
ProcHook::KHR_android_surface,
reinterpret_cast<PFN_vkVoidFunction>(CreateAndroidSurfaceKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledCreateAndroidSurfaceKHR),
nullptr,
},
{
@@ -156,7 +110,6 @@
ProcHook::INSTANCE,
ProcHook::EXT_debug_report,
reinterpret_cast<PFN_vkVoidFunction>(CreateDebugReportCallbackEXT),
- reinterpret_cast<PFN_vkVoidFunction>(disabledCreateDebugReportCallbackEXT),
nullptr,
},
{
@@ -165,7 +118,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(CreateDevice),
nullptr,
- nullptr,
},
{
"vkCreateInstance",
@@ -173,14 +125,12 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(CreateInstance),
nullptr,
- nullptr,
},
{
"vkCreateSwapchainKHR",
ProcHook::DEVICE,
ProcHook::KHR_swapchain,
reinterpret_cast<PFN_vkVoidFunction>(CreateSwapchainKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledCreateSwapchainKHR),
reinterpret_cast<PFN_vkVoidFunction>(checkedCreateSwapchainKHR),
},
{
@@ -188,7 +138,6 @@
ProcHook::INSTANCE,
ProcHook::EXT_debug_report,
reinterpret_cast<PFN_vkVoidFunction>(DebugReportMessageEXT),
- reinterpret_cast<PFN_vkVoidFunction>(disabledDebugReportMessageEXT),
nullptr,
},
{
@@ -196,7 +145,6 @@
ProcHook::INSTANCE,
ProcHook::EXT_debug_report,
reinterpret_cast<PFN_vkVoidFunction>(DestroyDebugReportCallbackEXT),
- reinterpret_cast<PFN_vkVoidFunction>(disabledDestroyDebugReportCallbackEXT),
nullptr,
},
{
@@ -205,7 +153,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(DestroyDevice),
nullptr,
- nullptr,
},
{
"vkDestroyInstance",
@@ -213,14 +160,12 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance),
nullptr,
- nullptr,
},
{
"vkDestroySurfaceKHR",
ProcHook::INSTANCE,
ProcHook::KHR_surface,
reinterpret_cast<PFN_vkVoidFunction>(DestroySurfaceKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledDestroySurfaceKHR),
nullptr,
},
{
@@ -228,7 +173,6 @@
ProcHook::DEVICE,
ProcHook::KHR_swapchain,
reinterpret_cast<PFN_vkVoidFunction>(DestroySwapchainKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledDestroySwapchainKHR),
reinterpret_cast<PFN_vkVoidFunction>(checkedDestroySwapchainKHR),
},
{
@@ -237,7 +181,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(EnumerateDeviceExtensionProperties),
nullptr,
- nullptr,
},
{
"vkEnumerateInstanceExtensionProperties",
@@ -245,7 +188,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties),
nullptr,
- nullptr,
},
{
"vkEnumeratePhysicalDevices",
@@ -253,7 +195,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices),
nullptr,
- nullptr,
},
{
"vkGetDeviceProcAddr",
@@ -261,7 +202,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr),
nullptr,
- nullptr,
},
{
"vkGetDeviceQueue",
@@ -269,7 +209,6 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue),
nullptr,
- nullptr,
},
{
"vkGetInstanceProcAddr",
@@ -277,14 +216,12 @@
ProcHook::EXTENSION_CORE,
reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr),
nullptr,
- nullptr,
},
{
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
ProcHook::INSTANCE,
ProcHook::KHR_surface,
reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceCapabilitiesKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfaceCapabilitiesKHR),
nullptr,
},
{
@@ -292,7 +229,6 @@
ProcHook::INSTANCE,
ProcHook::KHR_surface,
reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceFormatsKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfaceFormatsKHR),
nullptr,
},
{
@@ -300,7 +236,6 @@
ProcHook::INSTANCE,
ProcHook::KHR_surface,
reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfacePresentModesKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfacePresentModesKHR),
nullptr,
},
{
@@ -308,7 +243,6 @@
ProcHook::INSTANCE,
ProcHook::KHR_surface,
reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceSupportKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfaceSupportKHR),
nullptr,
},
{
@@ -317,14 +251,12 @@
ProcHook::ANDROID_native_buffer,
nullptr,
nullptr,
- nullptr,
},
{
"vkGetSwapchainImagesKHR",
ProcHook::DEVICE,
ProcHook::KHR_swapchain,
reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledGetSwapchainImagesKHR),
reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainImagesKHR),
},
{
@@ -332,7 +264,6 @@
ProcHook::DEVICE,
ProcHook::KHR_swapchain,
reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR),
- reinterpret_cast<PFN_vkVoidFunction>(disabledQueuePresentKHR),
reinterpret_cast<PFN_vkVoidFunction>(checkedQueuePresentKHR),
},
{
@@ -341,7 +272,6 @@
ProcHook::ANDROID_native_buffer,
nullptr,
nullptr,
- nullptr,
},
// clang-format on
};
@@ -381,13 +311,15 @@
} \
} while (0)
-#define INIT_PROC_EXT(ext, obj, proc) \
- do { \
- if (data.hal_extensions[ProcHook::ext]) \
- INIT_PROC(obj, proc); \
+#define INIT_PROC_EXT(ext, obj, proc) \
+ do { \
+ if (extensions[ProcHook::ext]) \
+ INIT_PROC(obj, proc); \
} while (0)
-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc) {
+bool InitDriverTable(VkInstance instance,
+ PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
auto& data = GetData(instance);
bool success = true;
@@ -406,7 +338,9 @@
return success;
}
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc) {
+bool InitDriverTable(VkDevice dev,
+ PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
auto& data = GetData(dev);
bool success = true;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 1eb7d79..ca17d57 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -19,6 +19,7 @@
#ifndef LIBVULKAN_DRIVER_GEN_H
#define LIBVULKAN_DRIVER_GEN_H
+#include <bitset>
#include <vulkan/vulkan.h>
#include <vulkan/vk_android_native_buffer.h>
@@ -48,8 +49,7 @@
Extension extension;
PFN_vkVoidFunction proc;
- PFN_vkVoidFunction disabled_proc; // nullptr for global hooks
- PFN_vkVoidFunction checked_proc; // nullptr for global/instance hooks
+ PFN_vkVoidFunction checked_proc; // always nullptr for non-device hooks
};
struct InstanceDriverTable {
@@ -83,8 +83,12 @@
const ProcHook* GetProcHook(const char* name);
ProcHook::Extension GetProcHookExtension(const char* name);
-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+bool InitDriverTable(VkInstance instance,
+ PFN_vkGetInstanceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+bool InitDriverTable(VkDevice dev,
+ PFN_vkGetDeviceProcAddr get_proc,
+ const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
} // namespace driver
} // namespace vulkan
diff --git a/vulkan/libvulkan/stubhal.cpp b/vulkan/libvulkan/stubhal.cpp
new file mode 100644
index 0000000..89fcebb
--- /dev/null
+++ b/vulkan/libvulkan/stubhal.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 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.
+ */
+
+/* NOTE:
+ * This stub HAL is only used internally by the loader when a real HAL
+ * implementation is not present, in order to avoid needing "null HAL" checks
+ * throughout the loader. It does not enumerate any physical devices, and is
+ * only as conformant to the Vulkan and Android HAL interfaces as the loader
+ * needs it to be. Do not use it as an example of a correct implementation; the
+ * code in ../null_driver is better for that.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "vkstub"
+
+#include <array>
+#include <bitset>
+#include <mutex>
+#include <hardware/hwvulkan.h>
+#include <log/log.h>
+#include "stubhal.h"
+
+namespace vulkan {
+namespace stubhal {
+
+namespace {
+
+const size_t kMaxInstances = 32;
+static std::mutex g_instance_mutex;
+static std::bitset<kMaxInstances> g_instance_used(false);
+static std::array<hwvulkan_dispatch_t, kMaxInstances> g_instances;
+
+[[noreturn]] void NoOp() {
+ LOG_ALWAYS_FATAL("invalid stub function called");
+}
+
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* /*layer_name*/,
+ uint32_t* count,
+ VkExtensionProperties* /*properties*/) {
+ *count = 0;
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult
+EnumerateInstanceLayerProperties(uint32_t* count,
+ VkLayerProperties* /*properties*/) {
+ *count = 0;
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* /*create_info*/,
+ const VkAllocationCallbacks* /*allocator*/,
+ VkInstance* instance) {
+ std::lock_guard<std::mutex> lock(g_instance_mutex);
+ for (size_t i = 0; i < kMaxInstances; i++) {
+ if (!g_instance_used[i]) {
+ g_instance_used[i] = true;
+ g_instances[i].magic = HWVULKAN_DISPATCH_MAGIC;
+ *instance = reinterpret_cast<VkInstance>(&g_instances[i]);
+ return VK_SUCCESS;
+ }
+ }
+ ALOGE("no more instances available (max=%zu)", kMaxInstances);
+ return VK_ERROR_INITIALIZATION_FAILED;
+}
+
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+ const VkAllocationCallbacks* /*allocator*/) {
+ std::lock_guard<std::mutex> lock(g_instance_mutex);
+ ssize_t idx =
+ reinterpret_cast<hwvulkan_dispatch_t*>(instance) - &g_instances[0];
+ ALOG_ASSERT(idx >= 0 && idx < g_instance_used.size(),
+ "DestroyInstance: invalid instance handle");
+ g_instance_used[static_cast<size_t>(idx)] = false;
+}
+
+VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance /*instance*/,
+ uint32_t* count,
+ VkPhysicalDevice* /*gpus*/) {
+ *count = 0;
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance /*instance*/,
+ const char* name) {
+ if (strcmp(name, "vkCreateInstance") == 0)
+ return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance);
+ if (strcmp(name, "vkDestroyInstance") == 0)
+ return reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance);
+ if (strcmp(name, "vkEnumerateInstanceExtensionProperties") == 0)
+ return reinterpret_cast<PFN_vkVoidFunction>(
+ EnumerateInstanceExtensionProperties);
+ if (strcmp(name, "vkEnumeratePhysicalDevices") == 0)
+ return reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices);
+ if (strcmp(name, "vkGetInstanceProcAddr") == 0)
+ return reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr);
+
+ // None of the other Vulkan functions should ever be called, as they all
+ // take a VkPhysicalDevice or other object obtained from a physical device.
+ return reinterpret_cast<PFN_vkVoidFunction>(NoOp);
+}
+
+} // anonymous namespace
+
+const hwvulkan_device_t kDevice = {
+ .common =
+ {
+ .tag = HARDWARE_DEVICE_TAG,
+ .version = HWVULKAN_DEVICE_API_VERSION_0_1,
+ .module = nullptr,
+ .close = nullptr,
+ },
+ .EnumerateInstanceExtensionProperties =
+ EnumerateInstanceExtensionProperties,
+ .CreateInstance = CreateInstance,
+ .GetInstanceProcAddr = GetInstanceProcAddr,
+};
+
+} // namespace stubhal
+} // namespace vulkan
diff --git a/vulkan/libvulkan/stubhal.h b/vulkan/libvulkan/stubhal.h
new file mode 100644
index 0000000..9ba7d04
--- /dev/null
+++ b/vulkan/libvulkan/stubhal.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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 LIBVULKAN_STUBHAL_H
+#define LIBVULKAN_STUBHAL_H 1
+
+struct hwvulkan_device_t;
+
+namespace vulkan {
+namespace stubhal {
+
+extern const hwvulkan_device_t kDevice;
+
+} // namespace stubhal
+} // namespace vulkan
+
+#endif // LIBVULKAN_STUBHAL_H