diff options
119 files changed, 14217 insertions, 2926 deletions
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl new file mode 100644 index 0000000000..674c16387e --- /dev/null +++ b/aidl/gui/android/view/Surface.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/view/Surface.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.view; + +parcelable Surface cpp_header "gui/Surface.h"; diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index 8515a0160d..a588ef4bfe 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -18,7 +18,7 @@ LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux # ZipArchive support, the order matters here to get all symbols. LOCAL_STATIC_LIBRARIES := libziparchive libz libbase libmincrypt LOCAL_HAL_STATIC_LIBRARIES := libdumpstate -LOCAL_CFLAGS += -Wall -Wno-unused-parameter -std=gnu99 +LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter LOCAL_INIT_RC := dumpstate.rc include $(BUILD_EXECUTABLE) diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 98617f4e78..f302529f2a 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -56,6 +56,7 @@ static char cmdline_buf[16384] = "(unknown)"; static const char *dump_traces_path = NULL; // TODO: should be part of dumpstate object +static unsigned long id; static char build_type[PROPERTY_VALUE_MAX]; static time_t now; static std::unique_ptr<ZipWriter> zip_writer; @@ -89,9 +90,7 @@ const std::string& ZIP_ROOT_DIR = "FS"; * See bugreport-format.txt for more info. */ // TODO: change to "v1" before final N build -static std::string VERSION_DEFAULT = "v1-dev1"; -// TODO: remove before final N build -static std::string VERSION_DUMPSYS_SPLIT = "v1-dev1-dumpsys-split"; +static std::string VERSION_DEFAULT = "v1-dev2"; /* 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. */ @@ -144,7 +143,7 @@ void add_mountinfo() { mount_points.clear(); DurationReporter duration_reporter(title, NULL); for_each_pid(do_mountinfo, NULL); - MYLOGD("%s: %lu entries added to zip file\n", title, mount_points.size()); + MYLOGD("%s: %d entries added to zip file\n", title, (int) mount_points.size()); } static void dump_dev_files(const char *title, const char *driverpath, const char *filename) @@ -477,16 +476,19 @@ static void print_header(std::string version) { dump_file(NULL, "/proc/version"); printf("Command line: %s\n", strtok(cmdline_buf, "\n")); printf("Bugreport format version: %s\n", version.c_str()); + printf("Dumpstate info: id=%lu pid=%d\n", id, getpid()); printf("\n"); } /* adds a new entry to the existing zip file. */ static bool add_zip_entry_from_fd(const std::string& entry_name, int fd) { if (!zip_writer) { - MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n", entry_name.c_str()); + MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n", + entry_name.c_str()); return false; } - MYLOGD("Adding zip entry %s\n", entry_name.c_str()); + // Logging statement below is useful to time how long each entry takes, but it's too verbose. + // MYLOGD("Adding zip entry %s\n", entry_name.c_str()); int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, get_mtime(fd, now)); if (err) { @@ -542,6 +544,7 @@ void add_dir(const char *dir, bool recursive) { MYLOGD("Not adding dir %s because zip_writer is not set\n", dir); return; } + MYLOGD("Adding dir %s (recursive: %d)\n", dir, recursive); DurationReporter duration_reporter(dir, NULL); dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd); } @@ -576,7 +579,7 @@ static bool add_text_zip_entry(const std::string& entry_name, const std::string& return true; } -static void dumpstate(const std::string& screenshot_path) { +static void dumpstate(const std::string& screenshot_path, const std::string& version) { DurationReporter duration_reporter("DUMPSTATE"); unsigned long timeout; @@ -595,7 +598,6 @@ static void dumpstate(const std::string& screenshot_path) { dump_file("BUDDYINFO", "/proc/buddyinfo"); dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index"); - dump_file("KERNEL WAKELOCKS", "/proc/wakelocks"); dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources"); dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); dump_file("KERNEL SYNC", "/d/sync"); @@ -613,6 +615,7 @@ static void dumpstate(const std::string& screenshot_path) { run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL); for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); + for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); if (!screenshot_path.empty()) { MYLOGI("taking late screenshot\n"); @@ -846,7 +849,7 @@ static void dumpstate(const std::string& screenshot_path) { /* the full dumpsys is starting to take a long time, so we need to increase its timeout. we really need to do the timeouts in dumpsys itself... */ - run_command("DUMPSYS", 60, "dumpsys", NULL); + run_command("DUMPSYS", 60, "dumpsys", "--skip", "meminfo,cpuinfo", NULL); printf("========================================================\n"); printf("== Checkins\n"); @@ -899,8 +902,8 @@ static void usage() { " -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 (%s or %s)\n", - VERSION_DEFAULT.c_str(), VERSION_DUMPSYS_SPLIT.c_str()); + " -V: sets the bugreport format version (valid values: %s)\n", + VERSION_DEFAULT.c_str()); } static void sigpipe_handler(int n) { @@ -1034,6 +1037,14 @@ int main(int argc, char *argv[]) { MYLOGI("begin\n"); + /* gets the sequential id */ + char last_id[PROPERTY_VALUE_MAX]; + property_get("dumpstate.last_id", last_id, "0"); + id = strtoul(last_id, NULL, 10) + 1; + sprintf(last_id, "%lu", id); + property_set("dumpstate.last_id", last_id); + MYLOGI("dumpstate id: %lu\n", id); + /* clear SIGPIPE handler */ memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = sigpipe_handler; @@ -1048,6 +1059,7 @@ int main(int argc, char *argv[]) { } /* parse arguments */ + log_args("Dumpstate command line", argc, const_cast<const char **>(argv)); int c; while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) { switch (c) { @@ -1084,7 +1096,7 @@ int main(int argc, char *argv[]) { exit(1); } - if (version != VERSION_DEFAULT && version != VERSION_DUMPSYS_SPLIT) { + if (version != VERSION_DEFAULT) { usage(); exit(1); } @@ -1172,8 +1184,9 @@ int main(int argc, char *argv[]) { if (do_update_progress) { std::vector<std::string> am_args = { - "--receiver-permission", "android.permission.DUMP", + "--receiver-permission", "android.permission.DUMP", "--receiver-foreground", "--es", "android.intent.extra.NAME", suffix, + "--ei", "android.intent.extra.ID", std::to_string(id), "--ei", "android.intent.extra.PID", std::to_string(getpid()), "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL), }; @@ -1218,40 +1231,45 @@ int main(int argc, char *argv[]) { } } - /* collect stack traces from Dalvik and native processes (needs root) */ - dump_traces_path = dump_traces(); - - /* Get the tombstone fds, recovery files, and mount info here while we are running as root. */ - get_tombstone_fds(tombstone_data); - add_dir(RECOVERY_DIR, true); - add_mountinfo(); - - if (!drop_root()) { - return -1; - } - if (is_redirecting) { redirect_to_file(stderr, const_cast<char*>(log_path.c_str())); + if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", + log_path.c_str(), strerror(errno)); + } /* TODO: rather than generating a text file now and zipping it later, it would be more efficient to redirect stdout to the zip entry directly, but the libziparchive doesn't support that option yet. */ redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str())); + if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", + tmp_path.c_str(), strerror(errno)); + } } // NOTE: there should be no stdout output until now, otherwise it would break the header. // In particular, DurationReport objects should be created passing 'title, NULL', so their // duration is logged into MYLOG instead. print_header(version); - dumpstate(do_early_screenshot ? "": screenshot_path); + // Invoking the following dumpsys calls before dump_traces() to try and + // keep the system stats as close to its initial state as possible. + run_command("DUMPSYS MEMINFO", 30, SU_PATH, "shell", "dumpsys", "meminfo", "-a", NULL); + run_command("DUMPSYS CPUINFO", 30, SU_PATH, "shell", "dumpsys", "cpuinfo", "-a", NULL); - /* done */ - if (vibrator) { - for (int i = 0; i < 3; i++) { - vibrate(vibrator.get(), 75); - usleep((75 + 50) * 1000); - } + /* collect stack traces from Dalvik and native processes (needs root) */ + dump_traces_path = dump_traces(); + + /* Get the tombstone fds, recovery files, and mount info here while we are running as root. */ + get_tombstone_fds(tombstone_data); + add_dir(RECOVERY_DIR, true); + add_mountinfo(); + + if (!drop_root()) { + return -1; } + dumpstate(do_early_screenshot ? "": screenshot_path, version); + /* close output if needed */ if (is_redirecting) { fclose(stdout); @@ -1293,17 +1311,29 @@ int main(int argc, char *argv[]) { bool do_text_file = true; if (do_zip_file) { - MYLOGD("Adding text entry to .zip bugreport\n"); - if (!finish_zip_file(base_name + "-" + suffix + ".txt", tmp_path, now)) { + std::string entry_name = base_name + "-" + suffix + ".txt"; + MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str()); + if (!finish_zip_file(entry_name, tmp_path, now)) { MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); do_text_file = true; } else { do_text_file = false; + // Since zip file is already created, it needs to be renamed. + std::string new_path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip"; + if (path != new_path) { + MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str()); + if (rename(path.c_str(), new_path.c_str())) { + MYLOGE("rename(%s, %s): %s\n", path.c_str(), + new_path.c_str(), strerror(errno)); + } else { + path = new_path; + } + } } } if (do_text_file) { - MYLOGD("Generating .txt bugreport\n"); path = bugreport_dir + "/" + base_name + "-" + suffix + ".txt"; + MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str()); if (rename(tmp_path.c_str(), path.c_str())) { MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno)); path.clear(); @@ -1311,13 +1341,23 @@ int main(int argc, char *argv[]) { } } + /* vibrate a few but shortly times to let user know it's finished */ + if (vibrator) { + for (int i = 0; i < 3; i++) { + vibrate(vibrator.get(), 75); + usleep((75 + 50) * 1000); + } + } + /* tell activity manager we're done */ if (do_broadcast) { if (!path.empty()) { MYLOGI("Final bugreport path: %s\n", path.c_str()); std::vector<std::string> am_args = { "--receiver-permission", "android.permission.DUMP", "--receiver-foreground", + "--ei", "android.intent.extra.ID", std::to_string(id), "--ei", "android.intent.extra.PID", std::to_string(getpid()), + "--ei", "android.intent.extra.MAX", std::to_string(weight_total), "--es", "android.intent.extra.BUGREPORT", path, "--es", "android.intent.extra.DUMPSTATE_LOG", log_path }; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 5a93f8cc09..9c975d27b1 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -30,15 +30,15 @@ #endif #ifndef MYLOGD -#define MYLOGD(fmt...) fprintf(stderr, fmt); ALOGD(fmt); +#define MYLOGD(...) fprintf(stderr, __VA_ARGS__); ALOGD(__VA_ARGS__); #endif #ifndef MYLOGI -#define MYLOGI(fmt...) fprintf(stderr, fmt); ALOGI(fmt); +#define MYLOGI(...) fprintf(stderr, __VA_ARGS__); ALOGI(__VA_ARGS__); #endif #ifndef MYLOGE -#define MYLOGE(fmt...) fprintf(stderr, fmt); ALOGE(fmt); +#define MYLOGE(...) fprintf(stderr, __VA_ARGS__); ALOGE(__VA_ARGS__); #endif #include <time.h> @@ -138,6 +138,9 @@ void for_each_tid(for_each_tid_func func, const char *header); /* Displays a blocked processes in-kernel wait channel */ void show_wchan(int pid, int tid, const char *name); +/* Displays a processes times */ +void show_showtime(int pid, const char *name); + /* Runs "showmap" for a process */ void do_showmap(int pid, const char *name); @@ -168,6 +171,9 @@ time_t get_mtime(int fd, time_t default_mtime); /* dump eMMC Extended CSD data */ void dump_emmc_ecsd(const char *ext_csd_path); +/** logs command-line arguments */ +void log_args(const std::string& message, int argc, const char *argv[]); + /* * Helper class used to report how long it takes for a section to finish. * diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 683091fc1c..f0ae325457 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -27,6 +27,7 @@ #include <string.h> #include <sys/inotify.h> #include <sys/stat.h> +#include <sys/sysconf.h> #include <sys/time.h> #include <sys/wait.h> #include <sys/klog.h> @@ -133,13 +134,32 @@ static void __for_each_pid(void (*helper)(int, const char *, void *), const char continue; } - sprintf(cmdpath,"/proc/%d/cmdline", pid); memset(cmdline, 0, sizeof(cmdline)); - if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) < 0) { - strcpy(cmdline, "N/A"); - } else { - read(fd, cmdline, sizeof(cmdline) - 1); + + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2)); close(fd); + if (cmdline[0]) { + helper(pid, cmdline, arg); + continue; + } + } + + // if no cmdline, a kernel thread has comm + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4)); + close(fd); + if (cmdline[1]) { + cmdline[0] = '['; + size_t len = strcspn(cmdline, "\f\b\r\n"); + cmdline[len] = ']'; + cmdline[len+1] = '\0'; + } + } + if (!cmdline[0]) { + strcpy(cmdline, "N/A"); } helper(pid, cmdline, arg); } @@ -191,7 +211,7 @@ static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { strcpy(comm, "N/A"); } else { char *c; - read(fd, comm, sizeof(comm) - 1); + TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2)); close(fd); c = strrchr(comm, '\n'); @@ -214,7 +234,7 @@ void show_wchan(int pid, int tid, const char *name) { ON_DRY_RUN_RETURN(); char path[255]; char buffer[255]; - int fd; + int fd, ret, save_errno; char name_buffer[255]; memset(buffer, 0, sizeof(buffer)); @@ -225,9 +245,13 @@ void show_wchan(int pid, int tid, const char *name) { return; } - if (read(fd, buffer, sizeof(buffer)) < 0) { - printf("Failed to read '%s' (%s)\n", path, strerror(errno)); - goto out_close; + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; + close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; } snprintf(name_buffer, sizeof(name_buffer), "%*s%s", @@ -235,8 +259,103 @@ void show_wchan(int pid, int tid, const char *name) { printf("%-7d %-32s %s\n", tid, name_buffer, buffer); -out_close: + return; +} + +// print time in centiseconds +static void snprcent(char *buffer, size_t len, size_t spc, + unsigned long long time) { + static long hz; // cache discovered hz + + if (hz <= 0) { + hz = sysconf(_SC_CLK_TCK); + if (hz <= 0) { + hz = 1000; + } + } + + // convert to centiseconds + time = (time * 100 + (hz / 2)) / hz; + + char str[16]; + + snprintf(str, sizeof(str), " %llu.%02u", + time / 100, (unsigned)(time % 100)); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +// print permille as a percent +static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) { + char str[16]; + + snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +void show_showtime(int pid, const char *name) { + ON_DRY_RUN_RETURN(); + char path[255]; + char buffer[1023]; + int fd, ret, save_errno; + + memset(buffer, 0, sizeof(buffer)); + + sprintf(path, "/proc/%d/stat", pid); + if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { + printf("Failed to open '%s' (%s)\n", path, strerror(errno)); + return; + } + + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; + } + + // field 14 is utime + // field 15 is stime + // field 42 is iotime + unsigned long long utime = 0, stime = 0, iotime = 0; + if (sscanf(buffer, + "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ", + &utime, &stime, &iotime) != 3) { + return; + } + + unsigned long long total = utime + stime; + if (!total) { + return; + } + + unsigned permille = (iotime * 1000 + (total / 2)) / total; + if (permille > 1000) { + permille = 1000; + } + + // try to beautify and stabilize columns at <80 characters + snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name); + if ((name[0] != '[') || utime) { + snprcent(buffer, sizeof(buffer), 57, utime); + } + snprcent(buffer, sizeof(buffer), 65, stime); + if ((name[0] != '[') || iotime) { + snprcent(buffer, sizeof(buffer), 73, iotime); + } + if (iotime) { + snprdec(buffer, sizeof(buffer), 79, permille); + } + puts(buffer); // adds a trailing newline + return; } @@ -586,14 +705,15 @@ void send_broadcast(const std::string& action, const std::vector<std::string>& a fprintf(stderr, "send_broadcast: too many arguments (%d)\n", (int) args.size()); return; } - const char *am_args[1024] = { "/system/bin/am", "broadcast", + const char *am_args[1024] = { SU_PATH, "shell", "/system/bin/am", "broadcast", "--user", "0", "-a", action.c_str() }; - size_t am_index = 5; // Starts at the index of last initial value above. + size_t am_index = 7; // Starts at the index of last initial value above. for (const std::string& arg : args) { am_args[++am_index] = arg.c_str(); } // Always terminate with NULL. am_args[am_index + 1] = NULL; + log_args("send_broadcast arguments", am_index, am_args); run_command_always(NULL, 5, am_args); } @@ -658,7 +778,7 @@ void redirect_to_socket(FILE *redirect, const char *service) { } void create_parent_dirs(const char *path) { - char *chp = (char*) path; + char *chp = const_cast<char *> (path); /* skip initial slash */ if (chp[0] == '/') @@ -1068,3 +1188,12 @@ void dump_emmc_ecsd(const char *ext_csd_path) { printf("\n"); } + +void log_args(const std::string& message, int argc, const char *argv[]) { + std::string args; + for (int i = 0; i < argc; i++) { + args.append(argv[i]); + args.append(" "); + } + MYLOGI("%s: %s\n", message.c_str(), args.c_str()); +} diff --git a/cmds/dumpsys/Android.mk b/cmds/dumpsys/Android.mk index 9be0901946..8335c14df6 100644 --- a/cmds/dumpsys/Android.mk +++ b/cmds/dumpsys/Android.mk @@ -5,10 +5,11 @@ LOCAL_SRC_FILES:= \ dumpsys.cpp LOCAL_SHARED_LIBRARIES := \ + libbase \ libutils \ liblog \ libbinder - + ifeq ($(TARGET_OS),linux) LOCAL_CFLAGS += -DXP_UNIX diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index ef009da0ed..003fcc394d 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -5,21 +5,33 @@ #define LOG_TAG "dumpsys" -#include <utils/Log.h> +#include <algorithm> +#include <chrono> +#include <thread> + +#include <android-base/file.h> +#include <android-base/unique_fd.h> +#include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> -#include <binder/IServiceManager.h> #include <binder/TextOutput.h> +#include <utils/Log.h> #include <utils/Vector.h> +#include <fcntl.h> #include <getopt.h> -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <unistd.h> +#include <sys/poll.h> +#include <sys/socket.h> #include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> using namespace android; +using android::base::unique_fd; +using android::base::WriteFully; static int sort_func(const String16* lhs, const String16* rhs) { @@ -121,23 +133,107 @@ int main(int argc, char* const argv[]) return 0; } - for (size_t i=0; i<N; i++) { - if (IsSkipped(skippedServices, services[i])) continue; + for (size_t i = 0; i < N; i++) { + String16 service_name = std::move(services[i]); + if (IsSkipped(skippedServices, service_name)) continue; - sp<IBinder> service = sm->checkService(services[i]); + sp<IBinder> service = sm->checkService(service_name); if (service != NULL) { + int sfd[2]; + + if (pipe(sfd) != 0) { + aerr << "Failed to create pipe to dump service info for " << service_name + << ": " << strerror(errno) << endl; + continue; + } + + unique_fd local_end(sfd[0]); + unique_fd remote_end(sfd[1]); + sfd[0] = sfd[1] = -1; + if (N > 1) { aout << "------------------------------------------------------------" "-------------------" << endl; - aout << "DUMP OF SERVICE " << services[i] << ":" << endl; + aout << "DUMP OF SERVICE " << service_name << ":" << endl; } - int err = service->dump(STDOUT_FILENO, args); - if (err != 0) { - aerr << "Error dumping service info: (" << strerror(err) - << ") " << services[i] << endl; + + // dump blocks until completion, so spawn a thread.. + std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable { + int err = service->dump(remote_end.get(), args); + + // It'd be nice to be able to close the remote end of the socketpair before the dump + // call returns, to terminate our reads if the other end closes their copy of the + // file descriptor, but then hangs for some reason. There doesn't seem to be a good + // way to do this, though. + remote_end.clear(); + + if (err != 0) { + aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name + << endl; + } + }); + + // TODO: Make this configurable at runtime. + constexpr auto timeout = std::chrono::seconds(10); + auto end = std::chrono::steady_clock::now() + timeout; + + struct pollfd pfd = { + .fd = local_end.get(), + .events = POLLIN + }; + + bool timed_out = false; + bool error = false; + while (true) { + // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout. + auto time_left_ms = [end]() { + auto now = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now); + return std::max(diff.count(), 0ll); + }; + + int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); + if (rc < 0) { + aerr << "Error in poll while dumping service " << service_name << " : " + << strerror(errno) << endl; + error = true; + break; + } else if (rc == 0) { + timed_out = true; + break; + } + + char buf[4096]; + rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf))); + if (rc < 0) { + aerr << "Failed to read while dumping service " << service_name << ": " + << strerror(errno) << endl; + error = true; + break; + } else if (rc == 0) { + // EOF. + break; + } + + if (!WriteFully(STDOUT_FILENO, buf, rc)) { + aerr << "Failed to write while dumping service " << service_name << ": " + << strerror(errno) << endl; + error = true; + break; + } + } + + if (timed_out) { + aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl; + } + + if (timed_out || error) { + dump_thread.detach(); + } else { + dump_thread.join(); } } else { - aerr << "Can't find service: " << services[i] << endl; + aerr << "Can't find service: " << service_name << endl; } } diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp index 866203f3f2..c47b0c8f4b 100644 --- a/cmds/flatland/Main.cpp +++ b/cmds/flatland/Main.cpp @@ -206,8 +206,8 @@ static const BenchmarkDesc benchmarks[] = { static const ShaderDesc shaders[] = { { - name: "Blit", - vertexShader: { + .name="Blit", + .vertexShader={ "precision mediump float;", "", "attribute vec4 position;", @@ -223,7 +223,7 @@ static const ShaderDesc shaders[] = { " texCoords = uvToTex * uv;", "}", }, - fragmentShader: { + .fragmentShader={ "#extension GL_OES_EGL_image_external : require", "precision mediump float;", "", @@ -240,8 +240,8 @@ static const ShaderDesc shaders[] = { }, { - name: "Gradient", - vertexShader: { + .name="Gradient", + .vertexShader={ "precision mediump float;", "", "attribute vec4 position;", @@ -257,7 +257,7 @@ static const ShaderDesc shaders[] = { " interp = (uvToInterp * uv).x;", "}", }, - fragmentShader: { + .fragmentShader={ "precision mediump float;", "", "varying float interp;", diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp index e54407cf91..44e2dd23ae 100644 --- a/cmds/installd/commands.cpp +++ b/cmds/installd/commands.cpp @@ -24,6 +24,7 @@ #include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/wait.h> #include <sys/xattr.h> #include <unistd.h> @@ -56,6 +57,17 @@ static constexpr const char* kXattrDefault = "user.default"; #define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M +typedef int fd_t; + +static bool property_get_bool(const char* property_name, bool default_value = false) { + char tmp_property_value[kPropertyValueMax]; + bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0; + if (!have_property) { + return default_value; + } + return strcmp(tmp_property_value, "true") == 0; +} + int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags, appid_t appid, const char* seinfo, int target_sdk_version) { uid_t uid = multiuser_get_uid(userid, appid); @@ -83,6 +95,24 @@ int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int // TODO: include result once 25796509 is fixed return 0; } + + if (property_get_bool("dalvik.vm.usejitprofiles")) { + const std::string profile_path = create_data_user_profile_package_path(userid, pkgname); + // read-write-execute only for the app user. + if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) { + PLOG(ERROR) << "Failed to prepare " << profile_path; + return -1; + } + const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname); + // dex2oat/profman runs under the shared app gid and it needs to read/write reference + // profiles. + appid_t shared_app_gid = multiuser_get_shared_app_gid(uid); + if (fs_prepare_dir_strict( + ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) { + PLOG(ERROR) << "Failed to prepare " << ref_profile_path; + return -1; + } + } } return 0; } @@ -125,6 +155,40 @@ int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int return 0; } +// Keep profile paths in sync with ActivityThread. +constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof"; +static std::string create_primary_profile(const std::string& profile_dir) { + return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME); +} + +static void unlink_reference_profile(const char* pkgname) { + std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname); + std::string reference_profile = create_primary_profile(reference_profile_dir); + if (unlink(reference_profile.c_str()) != 0) { + if (errno != ENOENT) { + PLOG(WARNING) << "Could not unlink " << reference_profile; + } + } +} + +static void unlink_current_profiles(const char* pkgname) { + std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); + for (auto user : users) { + std::string profile_dir = create_data_user_profile_package_path(user, pkgname); + std::string profile = create_primary_profile(profile_dir); + if (unlink(profile.c_str()) != 0) { + if (errno != ENOENT) { + PLOG(WARNING) << "Could not unlink " << profile; + } + } + } +} + +static void unlink_all_profiles(const char* pkgname) { + unlink_reference_profile(pkgname); + unlink_current_profiles(pkgname); +} + int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) { std::string suffix = ""; if (flags & FLAG_CLEAR_CACHE_ONLY) { @@ -146,6 +210,7 @@ int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int f // TODO: include result once 25796509 is fixed delete_dir_contents(path); } + unlink_all_profiles(pkgname); } return res; } @@ -160,6 +225,7 @@ int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int // TODO: include result once 25796509 is fixed delete_dir_contents_and_dir( create_data_user_de_package_path(uuid, userid, pkgname)); + unlink_all_profiles(pkgname); } return res; } @@ -289,11 +355,13 @@ int delete_user(const char *uuid, userid_t userid) { std::string data_path(create_data_user_path(uuid, userid)); std::string data_de_path(create_data_user_de_path(uuid, userid)); std::string media_path(create_data_media_path(uuid, userid)); + std::string profiles_path(create_data_user_profiles_path(userid)); res |= delete_dir_contents_and_dir(data_path); // TODO: include result once 25796509 is fixed delete_dir_contents_and_dir(data_de_path); res |= delete_dir_contents_and_dir(media_path); + res |= delete_dir_contents_and_dir(profiles_path); // Config paths only exist on internal storage if (uuid == nullptr) { @@ -630,19 +698,10 @@ static void run_patchoat(int input_fd, int oat_fd, const char* input_file_name, ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno)); } -static bool check_boolean_property(const char* property_name, bool default_value = false) { - char tmp_property_value[kPropertyValueMax]; - bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0; - if (!have_property) { - return default_value; - } - return strcmp(tmp_property_value, "true") == 0; -} - static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_file_name, - const char* output_file_name, int swap_fd, const char *instruction_set, - bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool extract_only, - const std::vector<int>& profile_files_fd, const std::vector<int>& reference_profile_files_fd) { + const char* output_file_name, int swap_fd, const char *instruction_set, + bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool extract_only, + int profile_fd) { static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { @@ -651,12 +710,6 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ return; } - if (profile_files_fd.size() != reference_profile_files_fd.size()) { - ALOGE("Invalid configuration of profile files: pf_size (%zu) != rpf_size (%zu)", - profile_files_fd.size(), reference_profile_files_fd.size()); - return; - } - char dex2oat_Xms_flag[kPropertyValueMax]; bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0; @@ -705,7 +758,7 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 || (strcmp(vold_decrypt, "1") == 0))); - bool generate_debug_info = check_boolean_property("debug.generate-debug-info"); + bool generate_debug_info = property_get_bool("debug.generate-debug-info"); char app_image_format[kPropertyValueMax]; char image_format_arg[strlen("--image-format=") + kPropertyValueMax]; @@ -779,18 +832,12 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) && (prop_buf[0] == '1'); } - std::vector<std::string> profile_file_args(profile_files_fd.size()); - std::vector<std::string> reference_profile_file_args(profile_files_fd.size()); - // "reference-profile-file-fd" is longer than "profile-file-fd" so we can - // use it to set the max length. - char profile_buf[strlen("--reference-profile-file-fd=") + MAX_INT_LEN]; - for (size_t k = 0; k < profile_files_fd.size(); k++) { - sprintf(profile_buf, "--profile-file-fd=%d", profile_files_fd[k]); - profile_file_args[k].assign(profile_buf); - sprintf(profile_buf, "--reference-profile-file-fd=%d", reference_profile_files_fd[k]); - reference_profile_file_args[k].assign(profile_buf); + char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN]; + if (profile_fd != -1) { + sprintf(profile_arg, "--profile-file-fd=%d", profile_fd); } + ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); const char* argv[7 // program name, mandatory arguments and the final NULL @@ -807,8 +854,7 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ + (debuggable ? 1 : 0) + (have_app_image_format ? 1 : 0) + dex2oat_flags_count - + profile_files_fd.size() - + reference_profile_files_fd.size()]; + + (profile_fd == -1 ? 0 : 1)]; int i = 0; argv[i++] = DEX2OAT_BIN; argv[i++] = zip_fd_arg; @@ -858,9 +904,8 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ argv[i++] = RUNTIME_ARG; argv[i++] = dex2oat_norelocation; } - for (size_t k = 0; k < profile_file_args.size(); k++) { - argv[i++] = profile_file_args[k].c_str(); - argv[i++] = reference_profile_file_args[k].c_str(); + if (profile_fd != -1) { + argv[i++] = profile_arg; } // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; @@ -906,7 +951,7 @@ static bool ShouldUseSwapFileForDexopt() { return true; } - bool is_low_mem = check_boolean_property("ro.config.low_ram"); + bool is_low_mem = property_get_bool("ro.config.low_ram"); if (is_low_mem) { return true; } @@ -928,10 +973,7 @@ static void SetDex2OatAndPatchOatScheduling(bool set_to_bg) { } } -constexpr const char* PROFILE_FILE_EXTENSION = ".prof"; -constexpr const char* REFERENCE_PROFILE_FILE_EXTENSION = ".prof.ref"; - -static void close_all_fds(const std::vector<int>& fds, const char* description) { +static void close_all_fds(const std::vector<fd_t>& fds, const char* description) { for (size_t i = 0; i < fds.size(); i++) { if (close(fds[i]) != 0) { PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i; @@ -939,92 +981,224 @@ static void close_all_fds(const std::vector<int>& fds, const char* description) } } -static int open_code_cache_for_user(userid_t user, const char* volume_uuid, const char* pkgname) { - std::string code_cache_path = - create_data_user_package_path(volume_uuid, user, pkgname) + CODE_CACHE_DIR_POSTFIX; - +static fd_t open_profile_dir(const std::string& profile_dir) { struct stat buffer; - // Check that the code cache exists. If not, return and don't log an error. - if (TEMP_FAILURE_RETRY(lstat(code_cache_path.c_str(), &buffer)) == -1) { - if (errno != ENOENT) { - PLOG(ERROR) << "Failed to lstat code_cache: " << code_cache_path; - return -1; - } + if (TEMP_FAILURE_RETRY(lstat(profile_dir.c_str(), &buffer)) == -1) { + PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir; + return -1; } - int code_cache_fd = open(code_cache_path.c_str(), - O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW); - if (code_cache_fd < 0) { - PLOG(ERROR) << "Failed to open code_cache: " << code_cache_path; + fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(), + O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW)); + if (profile_dir_fd < 0) { + PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir; } - return code_cache_fd; + return profile_dir_fd; } -// Keep profile paths in sync with ActivityThread. -static void open_profile_files_for_user(uid_t uid, const char* pkgname, int code_cache_fd, - /*out*/ int* profile_fd, /*out*/ int* reference_profile_fd) { - *profile_fd = -1; - *reference_profile_fd = -1; - std::string profile_file(pkgname); - profile_file += PROFILE_FILE_EXTENSION; - - // Check if the profile exists. If not, early return and don't log an error. - struct stat buffer; - if (TEMP_FAILURE_RETRY(fstatat( - code_cache_fd, profile_file.c_str(), &buffer, AT_SYMLINK_NOFOLLOW)) == -1) { +static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) { + fd_t profile_dir_fd = open_profile_dir(profile_dir); + if (profile_dir_fd < 0) { + return -1; + } + + fd_t profile_fd = -1; + std::string profile_file = create_primary_profile(profile_dir); + + profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW)); + if (profile_fd == -1) { + // It's not an error if the profile file does not exist. if (errno != ENOENT) { - PLOG(ERROR) << "Failed to fstatat profile file: " << profile_file; - return; + PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir; } } + // TODO(calin): use AutoCloseFD instead of closing the fd manually. + if (close(profile_dir_fd) != 0) { + PLOG(WARNING) << "Could not close profile dir " << profile_dir; + } + return profile_fd; +} - // Open in read-write to allow transfer of information from the current profile - // to the reference profile. - *profile_fd = openat(code_cache_fd, profile_file.c_str(), O_RDWR | O_NOFOLLOW); - if (*profile_fd < 0) { - PLOG(ERROR) << "Failed to open profile file: " << profile_file; - return; +static fd_t open_primary_profile_file(userid_t user, const char* pkgname) { + std::string profile_dir = create_data_user_profile_package_path(user, pkgname); + return open_primary_profile_file_from_dir(profile_dir, O_RDONLY); +} + +static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) { + std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname); + int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY; + fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags); + if (fd < 0) { + return -1; + } + if (read_write) { + // Fix the owner. + if (fchown(fd, uid, uid) < 0) { + close(fd); + return -1; + } } + return fd; +} - std::string reference_profile(pkgname); - reference_profile += REFERENCE_PROFILE_FILE_EXTENSION; - // Give read-write permissions just for the user (changed with fchown after opening). - // We need write permission because dex2oat will update the reference profile files - // with the content of the corresponding current profile files. - *reference_profile_fd = openat(code_cache_fd, reference_profile.c_str(), - O_CREAT | O_RDWR | O_NOFOLLOW, S_IWUSR | S_IRUSR); +static void open_profile_files(uid_t uid, const char* pkgname, + /*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) { + // Open the reference profile in read-write mode as profman might need to save the merge. + *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true); if (*reference_profile_fd < 0) { - close(*profile_fd); + // We can't access the reference profile file. return; } - if (fchown(*reference_profile_fd, uid, uid) < 0) { - PLOG(ERROR) << "Cannot change reference profile file owner: " << reference_profile; - close(*profile_fd); - *profile_fd = -1; - *reference_profile_fd = -1; + + std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); + for (auto user : users) { + fd_t profile_fd = open_primary_profile_file(user, pkgname); + // Add to the lists only if both fds are valid. + if (profile_fd >= 0) { + profiles_fd->push_back(profile_fd); + } } } -static void open_profile_files(const char* volume_uuid, uid_t uid, const char* pkgname, - std::vector<int>* profile_fds, std::vector<int>* reference_profile_fds) { - std::vector<userid_t> users = get_known_users(volume_uuid); - for (auto user : users) { - int code_cache_fd = open_code_cache_for_user(user, volume_uuid, pkgname); - if (code_cache_fd < 0) { - continue; +static void drop_capabilities(uid_t uid) { + if (setgid(uid) != 0) { + ALOGE("setgid(%d) failed in installd during dexopt\n", uid); + exit(64); + } + if (setuid(uid) != 0) { + ALOGE("setuid(%d) failed in installd during dexopt\n", uid); + exit(65); + } + // drop capabilities + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata[2]; + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + if (capset(&capheader, &capdata[0]) < 0) { + ALOGE("capset failed: %s\n", strerror(errno)); + exit(66); + } +} + +static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0; +static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1; +static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2; +static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3; +static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4; + +static void run_profman(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) { + static const size_t MAX_INT_LEN = 32; + static const char* PROFMAN_BIN = "/system/bin/profman"; + + std::vector<std::string> profile_args(profiles_fd.size()); + char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN]; + for (size_t k = 0; k < profiles_fd.size(); k++) { + sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]); + profile_args[k].assign(profile_buf); + } + char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN]; + sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd); + + // program name, reference profile fd, the final NULL and the profile fds + const char* argv[3 + profiles_fd.size()]; + int i = 0; + argv[i++] = PROFMAN_BIN; + argv[i++] = reference_profile_arg; + for (size_t k = 0; k < profile_args.size(); k++) { + argv[i++] = profile_args[k].c_str(); + } + // Do not add after dex2oat_flags, they should override others for debugging. + argv[i] = NULL; + + execv(PROFMAN_BIN, (char * const *)argv); + ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno)); + exit(68); /* only get here on exec failure */ +} + +// Decides if profile guided compilation is needed or not based on existing profiles. +// Returns true if there is enough information in the current profiles that worth +// a re-compilation of the package. +// If the return value is true all the current profiles would have been merged into +// the reference profiles accessible with open_reference_profile(). +static bool analyse_profiles(uid_t uid, const char* pkgname) { + std::vector<fd_t> profiles_fd; + fd_t reference_profile_fd = -1; + open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd); + if (profiles_fd.empty() || (reference_profile_fd == -1)) { + // Skip profile guided compilation because no profiles were found. + // Or if the reference profile info couldn't be opened. + close_all_fds(profiles_fd, "profiles_fd"); + if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) { + PLOG(WARNING) << "Failed to close fd for reference profile"; } - int profile_fd = -1; - int reference_profile_fd = -1; - open_profile_files_for_user( - uid, pkgname, code_cache_fd, &profile_fd, &reference_profile_fd); - close(code_cache_fd); + return false; + } - // Add to the lists only if both fds are valid. - if ((profile_fd >= 0) && (reference_profile_fd >= 0)) { - profile_fds->push_back(profile_fd); - reference_profile_fds->push_back(reference_profile_fd); + ALOGV("PROFMAN: --- BEGIN '%s' ---\n", pkgname); + + pid_t pid = fork(); + if (pid == 0) { + /* child -- drop privileges before continuing */ + drop_capabilities(uid); + run_profman(profiles_fd, reference_profile_fd); + exit(68); /* only get here on exec failure */ + } + /* parent */ + int return_code = wait_child(pid); + bool need_to_compile = false; + bool delete_current_profiles = false; + bool delete_reference_profile = false; + if (!WIFEXITED(return_code)) { + LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code; + } else { + return_code = WEXITSTATUS(return_code); + switch (return_code) { + case PROFMAN_BIN_RETURN_CODE_COMPILE: + need_to_compile = true; + delete_current_profiles = true; + delete_reference_profile = false; + break; + case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION: + need_to_compile = false; + delete_current_profiles = false; + delete_reference_profile = false; + break; + case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES: + LOG(WARNING) << "Bad profiles for package " << pkgname; + need_to_compile = false; + delete_current_profiles = true; + delete_reference_profile = true; + break; + case PROFMAN_BIN_RETURN_CODE_ERROR_IO: // fall-through + case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING: + // Temporary IO problem (e.g. locking). Ignore but log a warning. + LOG(WARNING) << "IO error while reading profiles for package " << pkgname; + need_to_compile = false; + delete_current_profiles = false; + delete_reference_profile = false; + break; + default: + // Unknown return code or error. Unlink profiles. + LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname + << ": " << return_code; + need_to_compile = false; + delete_current_profiles = true; + delete_reference_profile = true; + break; } } + close_all_fds(profiles_fd, "profiles_fd"); + if (close(reference_profile_fd) != 0) { + PLOG(WARNING) << "Failed to close fd for reference profile"; + } + if (delete_current_profiles) { + unlink_current_profiles(pkgname); + } + if (delete_reference_profile) { + unlink_reference_profile(pkgname); + } + return need_to_compile; } static void trim_extension(char* path) { @@ -1066,9 +1240,36 @@ static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const return true; } +static bool create_oat_out_path(const char* apk_path, const char* instruction_set, + const char* oat_dir, /*out*/ char* out_path) { + // Early best-effort check whether we can fit the the path into our buffers. + // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run + // without a swap file, if necessary. Reference profiles file also add an extra ".prof" + // extension to the cache path (5 bytes). + if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) { + ALOGE("apk_path too long '%s'\n", apk_path); + return false; + } + + if (oat_dir != NULL && oat_dir[0] != '!') { + if (validate_apk_path(oat_dir)) { + ALOGE("invalid oat_dir '%s'\n", oat_dir); + return false; + } + if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) { + return false; + } + } else { + if (!create_cache_path(out_path, apk_path, instruction_set)) { + return false; + } + } + return true; +} + int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set, - int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* volume_uuid, - bool use_profiles) + int dexopt_needed, const char* oat_dir, int dexopt_flags, + const char* volume_uuid ATTRIBUTE_UNUSED, bool use_profiles) { struct utimbuf ut; struct stat input_stat; @@ -1077,19 +1278,33 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins char image_path[PKG_PATH_MAX]; const char *input_file; char in_odex_path[PKG_PATH_MAX]; - int res, input_fd=-1, out_fd=-1, image_fd=-1, swap_fd=-1; - bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0; + int res; + fd_t input_fd=-1, out_fd=-1, image_fd=-1, swap_fd=-1; + bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0); bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0; bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0; bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0; bool extract_only = (dexopt_flags & DEXOPT_EXTRACTONLY) != 0; - std::vector<int> profile_files_fd; - std::vector<int> reference_profile_files_fd; + fd_t reference_profile_fd = -1; + + if (is_public && use_profiles) { + // We should not give public access to apks compiled with profile information. + // Log an error and return early if are asked to do so. + ALOGE("use_profiles should not be used with is_public."); + return -1; + } + if (use_profiles) { - open_profile_files(volume_uuid, uid, pkgname, - &profile_files_fd, &reference_profile_files_fd); - if (profile_files_fd.empty()) { - // Skip profile guided compilation because no profiles were found. + if (analyse_profiles(uid, pkgname)) { + // Open again reference profile in read only mode as dex2oat does not get write + // permissions. + reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false); + if (reference_profile_fd == -1) { + PLOG(WARNING) << "Couldn't open reference profile in read only mode " << pkgname; + exit(72); + } + } else { + // No need to (re)compile. Return early. return 0; } } @@ -1098,26 +1313,8 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins LOG_FATAL("dexopt flags contains unknown fields\n"); } - // Early best-effort check whether we can fit the the path into our buffers. - // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run - // without a swap file, if necessary. - if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) { - ALOGE("apk_path too long '%s'\n", apk_path); - return -1; - } - - if (oat_dir != NULL && oat_dir[0] != '!') { - if (validate_apk_path(oat_dir)) { - ALOGE("invalid oat_dir '%s'\n", oat_dir); - return -1; - } - if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) { - return -1; - } - } else { - if (!create_cache_path(out_path, apk_path, instruction_set)) { - return -1; - } + if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) { + return false; } switch (dexopt_needed) { @@ -1207,24 +1404,8 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ - if (setgid(uid) != 0) { - ALOGE("setgid(%d) failed in installd during dexopt\n", uid); - exit(64); - } - if (setuid(uid) != 0) { - ALOGE("setuid(%d) failed in installd during dexopt\n", uid); - exit(65); - } - // drop capabilities - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata[2]; - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - capheader.version = _LINUX_CAPABILITY_VERSION_3; - if (capset(&capheader, &capdata[0]) < 0) { - ALOGE("capset failed: %s\n", strerror(errno)); - exit(66); - } + drop_capabilities(uid); + SetDex2OatAndPatchOatScheduling(boot_complete); if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno)); @@ -1244,7 +1425,7 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins } run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd, instruction_set, vm_safe_mode, debuggable, boot_complete, extract_only, - profile_files_fd, reference_profile_files_fd); + reference_profile_fd); } else { ALOGE("Invalid dexopt needed: %d\n", dexopt_needed); exit(73); @@ -1269,9 +1450,8 @@ int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* ins if (swap_fd >= 0) { close(swap_fd); } - if (use_profiles != 0) { - close_all_fds(profile_files_fd, "profile_files_fd"); - close_all_fds(reference_profile_files_fd, "reference_profile_files_fd"); + if (reference_profile_fd >= 0) { + close(reference_profile_fd); } if (image_fd >= 0) { close(image_fd); @@ -1286,9 +1466,11 @@ fail: if (input_fd >= 0) { close(input_fd); } - if (use_profiles != 0) { - close_all_fds(profile_files_fd, "profile_files_fd"); - close_all_fds(reference_profile_files_fd, "reference_profile_files_fd"); + if (reference_profile_fd >= 0) { + close(reference_profile_fd); + // We failed to compile. Unlink the reference profile. Current profiles are already unlinked + // when profmoan advises compilation. + unlink_reference_profile(pkgname); } if (swap_fd >= 0) { close(swap_fd); @@ -1604,5 +1786,83 @@ int link_file(const char* relative_path, const char* from_base, const char* to_b return 0; } +// Helper for move_ab, so that we can have common failure-case cleanup. +static bool unlink_and_rename(const char* from, const char* to) { + // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise, + // return a failure. + struct stat s; + if (stat(to, &s) == 0) { + if (!S_ISREG(s.st_mode)) { + LOG(ERROR) << from << " is not a regular file to replace for A/B."; + return false; + } + if (unlink(to) != 0) { + LOG(ERROR) << "Could not unlink " << to << " to move A/B."; + return false; + } + } else { + // This may be a permission problem. We could investigate the error code, but we'll just + // let the rename failure do the work for us. + } + + // Try to rename "to" to "from." + if (rename(from, to) != 0) { + PLOG(ERROR) << "Could not rename " << from << " to " << to; + return false; + } + + return true; +} + +int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) { + if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) { + LOG(ERROR) << "Cannot move_ab with null input"; + return -1; + } + if (validate_apk_path(apk_path) != 0) { + LOG(ERROR) << "invalid apk_path " << apk_path; + return -1; + } + if (validate_apk_path(oat_dir) != 0) { + LOG(ERROR) << "invalid oat_dir " << oat_dir; + return -1; + } + + char a_path[PKG_PATH_MAX]; + if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) { + return -1; + } + + // B path = A path + ".b" + std::string b_path = StringPrintf("%s.b", a_path); + + // Check whether B exists. + { + struct stat s; + if (stat(b_path.c_str(), &s) != 0) { + LOG(ERROR) << "Can't find A/B artifact at " << b_path; + return -1; + } + if (!S_ISREG(s.st_mode)) { + LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file."; + // Try to unlink, but swallow errors. + unlink(b_path.c_str()); + return -1; + } + } + + // Rename B to A. + if (!unlink_and_rename(b_path.c_str(), a_path)) { + // Delete the b_path so we don't try again (or fail earlier). + if (unlink(b_path.c_str()) != 0) { + PLOG(ERROR) << "Could not unlink " << b_path; + } + + return -1; + } + + return 0; +} + } // namespace installd } // namespace android diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h index fe03397c5f..8507effb04 100644 --- a/cmds/installd/commands.h +++ b/cmds/installd/commands.h @@ -58,6 +58,9 @@ int create_oat_dir(const char* oat_dir, const char *instruction_set); int rm_package_dir(const char* apk_path); int link_file(const char *relative_path, const char *from_base, const char *to_base); +// Move a B version over to the A location. Only works for oat_dir != nullptr. +int move_ab(const char *apk_path, const char *instruction_set, const char* oat_dir); + } // namespace installd } // namespace android diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp index bee2790c2b..6a67e29396 100644 --- a/cmds/installd/globals.cpp +++ b/cmds/installd/globals.cpp @@ -39,6 +39,7 @@ dir_rec_t android_asec_dir; dir_rec_t android_data_dir; dir_rec_t android_media_dir; dir_rec_t android_mnt_expand_dir; +dir_rec_t android_profiles_dir; dir_rec_array_t android_system_dirs; @@ -99,6 +100,11 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { return false; } + // Get the android profiles directory. + if (copy_and_append(&android_profiles_dir, &android_data_dir, PROFILES_SUBDIR) < 0) { + return false; + } + // Take note of the system and vendor directories. android_system_dirs.count = 4; diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h index 2e61f85d6a..3e523460ad 100644 --- a/cmds/installd/globals.h +++ b/cmds/installd/globals.h @@ -43,6 +43,7 @@ extern dir_rec_t android_asec_dir; extern dir_rec_t android_data_dir; extern dir_rec_t android_media_dir; extern dir_rec_t android_mnt_expand_dir; +extern dir_rec_t android_profiles_dir; extern dir_rec_array_t android_system_dirs; diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 63290a9ba8..54d397075e 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -336,6 +336,11 @@ static int do_link_file(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) return link_file(arg[0], arg[1], arg[2]); } +static int do_move_ab(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) { + // apk_path, instruction_set, oat_dir + return move_ab(arg[0], arg[1], arg[2]); +} + struct cmdinfo { const char *name; unsigned numargs; @@ -364,6 +369,7 @@ struct cmdinfo cmds[] = { { "createoatdir", 2, do_create_oat_dir }, { "rmpackagedir", 1, do_rm_package_dir }, { "linkfile", 3, do_link_file }, + { "move_ab", 3, do_move_ab }, }; static int readx(int s, void *_buf, int count) diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h index 4e1d38fe56..8f6e928a9d 100644 --- a/cmds/installd/installd_constants.h +++ b/cmds/installd/installd_constants.h @@ -4,7 +4,7 @@ ** ** 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 +** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** @@ -41,6 +41,8 @@ constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROI constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA +constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA + /* other handy constants */ constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under ANDROID_DATA diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 27f7939b57..3aac48baf9 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -235,10 +235,48 @@ private: } } - // Prepare and run dex2oat. + // Prepare to create. // TODO: Delete files, just for a blank slate. const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName); + std::string preopted_boot_art_path = StringPrintf("%s/system/framework/%s/boot.art", + b_mount_path_.c_str(), + isa); + if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { + return PatchoatBootImage(art_path, isa); + } else { + // No preopted boot image. Try to compile. + return Dex2oatBootImage(boot_cp, art_path, oat_path, isa); + } + } + + bool PatchoatBootImage(const std::string& art_path, const char* isa) { + // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. + + std::vector<std::string> cmd; + cmd.push_back(b_mount_path_ + "/system/bin/patchoat"); + + cmd.push_back("--input-image-location=/system/framework/boot.art"); + cmd.push_back(StringPrintf("--output-image-file=%s", art_path.c_str())); + + cmd.push_back(StringPrintf("--instruction-set=%s", isa)); + + int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA, + ART_BASE_ADDRESS_MAX_DELTA); + cmd.push_back(StringPrintf("--base-offset-delta=%d", base_offset)); + + std::string error_msg; + bool result = Exec(cmd, &error_msg); + if (!result) { + LOG(ERROR) << "Could not generate boot image: " << error_msg; + } + return result; + } + + bool Dex2oatBootImage(const std::string& boot_cp, + const std::string& art_path, + const std::string& oat_path, + const char* isa) { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector<std::string> cmd; cmd.push_back(b_mount_path_ + "/system/bin/dex2oat"); diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index d25bf71e95..74f4264f80 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -59,6 +59,11 @@ static bool is_valid_filename(const std::string& name) { } } +static void check_package_name(const char* package_name) { + CHECK(is_valid_filename(package_name)); + CHECK(is_valid_package_name(package_name) == 0); +} + /** * Create the path name where package app contents should be stored for * the given volume UUID and package name. An empty UUID is assumed to @@ -66,9 +71,7 @@ static bool is_valid_filename(const std::string& name) { */ std::string create_data_app_package_path(const char* volume_uuid, const char* package_name) { - CHECK(is_valid_filename(package_name)); - CHECK(is_valid_package_name(package_name) == 0); - + check_package_name(package_name); return StringPrintf("%s/%s", create_data_app_path(volume_uuid).c_str(), package_name); } @@ -80,18 +83,14 @@ std::string create_data_app_package_path(const char* volume_uuid, */ std::string create_data_user_package_path(const char* volume_uuid, userid_t user, const char* package_name) { - CHECK(is_valid_filename(package_name)); - CHECK(is_valid_package_name(package_name) == 0); - + check_package_name(package_name); return StringPrintf("%s/%s", create_data_user_path(volume_uuid, user).c_str(), package_name); } std::string create_data_user_de_package_path(const char* volume_uuid, userid_t user, const char* package_name) { - CHECK(is_valid_filename(package_name)); - CHECK(is_valid_package_name(package_name) == 0); - + check_package_name(package_name); return StringPrintf("%s/%s", create_data_user_de_path(volume_uuid, user).c_str(), package_name); } @@ -161,6 +160,20 @@ std::string create_data_media_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid); } +std::string create_data_user_profiles_path(userid_t userid) { + return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid); +} + +std::string create_data_user_profile_package_path(userid_t user, const char* package_name) { + check_package_name(package_name); + return StringPrintf("%s/%s",create_data_user_profiles_path(user).c_str(), package_name); +} + +std::string create_data_ref_profile_package_path(const char* package_name) { + check_package_name(package_name); + return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name); +} + std::vector<userid_t> get_known_users(const char* volume_uuid) { std::vector<userid_t> users; diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 2d9573e21b..9bbddcaec9 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -84,6 +84,10 @@ std::string create_data_user_de_package_path(const char* volume_uuid, std::string create_data_media_path(const char* volume_uuid, userid_t userid); +std::string create_data_user_profiles_path(userid_t userid); +std::string create_data_user_profile_package_path(userid_t user, const char* package_name); +std::string create_data_ref_profile_package_path(const char* package_name); + std::vector<userid_t> get_known_users(const char* volume_uuid); int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid); @@ -135,6 +139,7 @@ char *build_string2(const char *s1, const char *s2); char *build_string3(const char *s1, const char *s2, const char *s3); int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid); +int ensure_media_user_dirs(const char* uuid, userid_t userid); int ensure_config_user_dirs(userid_t userid); int wait_child(pid_t pid); diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk index 7ee0dd184e..73c036722b 100644 --- a/cmds/servicemanager/Android.mk +++ b/cmds/servicemanager/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH:= $(call my-dir) svc_c_flags = \ - -Wall -Wextra \ + -Wall -Wextra -Werror \ ifneq ($(TARGET_USES_64_BIT_BINDER),true) ifneq ($(TARGET_IS_64_BIT),true) diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c index fd91633ec7..646665444b 100644 --- a/cmds/servicemanager/bctest.c +++ b/cmds/servicemanager/bctest.c @@ -58,7 +58,6 @@ unsigned token; int main(int argc, char **argv) { - int fd; struct binder_state *bs; uint32_t svcmgr = BINDER_SERVICE_MANAGER; uint32_t handle; diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c index 3d14451b2f..9e99085c57 100644 --- a/cmds/servicemanager/binder.c +++ b/cmds/servicemanager/binder.c @@ -449,7 +449,7 @@ static void *bio_alloc(struct binder_io *bio, size_t size) } void binder_done(struct binder_state *bs, - struct binder_io *msg, + __unused struct binder_io *msg, struct binder_io *reply) { struct { diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index 8596f962bd..21fdff0f0f 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -178,7 +178,7 @@ uint16_t svcmgr_id[] = { }; -uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid, pid_t spid) +uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid) { struct svcinfo *si = find_svc(s, len); @@ -304,7 +304,7 @@ int svcmgr_handler(struct binder_state *bs, if (s == NULL) { return -1; } - handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid); + handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid); if (!handle) break; bio_put_ref(reply, handle); @@ -349,7 +349,7 @@ int svcmgr_handler(struct binder_state *bs, } -static int audit_callback(void *data, security_class_t cls, char *buf, size_t len) +static int audit_callback(void *data, __unused security_class_t cls, char *buf, size_t len) { struct audit_data *ad = (struct audit_data *)data; @@ -362,7 +362,7 @@ static int audit_callback(void *data, security_class_t cls, char *buf, size_t le return 0; } -int main(int argc, char **argv) +int main() { struct binder_state *bs; diff --git a/data/etc/android.hardware.vulkan.level-0.xml b/data/etc/android.hardware.vulkan.level-0.xml new file mode 100644 index 0000000000..39c65ac5af --- /dev/null +++ b/data/etc/android.hardware.vulkan.level-0.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This is the standard feature indicating that the device supports Vulkan + hardware level 0. --> +<permissions> + <feature name="android.hardware.vulkan.level" version="0" /> +</permissions> diff --git a/data/etc/android.hardware.vulkan.level-1.xml b/data/etc/android.hardware.vulkan.level-1.xml new file mode 100644 index 0000000000..c3f5513e71 --- /dev/null +++ b/data/etc/android.hardware.vulkan.level-1.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This is the standard feature indicating that the device supports Vulkan + hardware level 1. --> +<permissions> + <feature name="android.hardware.vulkan.level" version="1" /> +</permissions> diff --git a/data/etc/android.hardware.vulkan.version-1_0_3.xml b/data/etc/android.hardware.vulkan.version-1_0_3.xml new file mode 100644 index 0000000000..adc5a5da8f --- /dev/null +++ b/data/etc/android.hardware.vulkan.version-1_0_3.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This is the standard feature indicating that the device has a Vulkan + driver that supports API version 1.0.3 (0x00400003) --> +<permissions> + <feature name="android.hardware.vulkan.version" version="4194307" /> +</permissions> diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h index 34ce0f00c2..69dc9a79f1 100644 --- a/include/binder/IProcessInfoService.h +++ b/include/binder/IProcessInfoService.h @@ -44,16 +44,6 @@ public: // ---------------------------------------------------------------------- -class BnProcessInfoService : public BnInterface<IProcessInfoService> { -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------- - }; // namespace android #endif // ANDROID_I_PROCESS_INFO_SERVICE_H diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index 5956e133b0..44fd59e1d2 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -126,6 +126,8 @@ public: status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val); status_t writeByteVector(const std::vector<int8_t>& val); + status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val); + status_t writeByteVector(const std::vector<uint8_t>& val); status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val); status_t writeInt32Vector(const std::vector<int32_t>& val); status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val); @@ -271,6 +273,8 @@ public: status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const; status_t readByteVector(std::vector<int8_t>* val) const; + status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const; + status_t readByteVector(std::vector<uint8_t>* val) const; status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const; status_t readInt32Vector(std::vector<int32_t>* val) const; status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const; diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h index d10ba2f2c6..acc8c4b070 100644 --- a/include/gui/BufferQueueCore.h +++ b/include/gui/BufferQueueCore.h @@ -67,8 +67,13 @@ public: // consumer can run asynchronously. enum { MAX_MAX_ACQUIRED_BUFFERS = BufferQueueDefs::NUM_BUFFER_SLOTS - 2 }; - // The default API number used to indicate that no producer is connected - enum { NO_CONNECTED_API = 0 }; + enum { + // The API number used to indicate the currently connected producer + CURRENTLY_CONNECTED_API = -1, + + // The API number used to indicate that no producer is connected + NO_CONNECTED_API = 0, + }; typedef Vector<BufferItem> Fifo; diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h index 9307a26fb3..1b63552fc1 100644 --- a/include/gui/ConsumerBase.h +++ b/include/gui/ConsumerBase.h @@ -26,6 +26,8 @@ #include <utils/threads.h> #include <gui/IConsumerListener.h> +#include <queue> + namespace android { // ---------------------------------------------------------------------------- @@ -108,18 +110,18 @@ protected: // from the derived class. virtual void onLastStrongRef(const void* id); - // Implementation of the IConsumerListener interface. These - // calls are used to notify the ConsumerBase of asynchronous events in the - // BufferQueue. The onFrameAvailable, onFrameReplaced, and - // onBuffersReleased methods should not need to be overridden by derived - // classes, but if they are overridden the ConsumerBase implementation must - // be called from the derived class. The ConsumerBase version of - // onSidebandStreamChanged does nothing and can be overriden by derived - // classes if they want the notification. - virtual void onFrameAvailable(const BufferItem& item) override; - virtual void onFrameReplaced(const BufferItem& item) override; - virtual void onBuffersReleased() override; - virtual void onSidebandStreamChanged() override; + // Handlers for the IConsumerListener interface, these will be called from + // the message queue thread. These calls are used to notify the ConsumerBase + // of asynchronous events in the BufferQueue. The onFrameAvailableHandler, + // onFrameReplacedHandler, and onBuffersReleasedHandler methods should not + // need to be overridden by derived classes, but if they are overridden the + // ConsumerBase implementation must be called from the derived class. The + // ConsumerBase version of onSidebandStreamChangedHandler does nothing and + // can be overriden by derived classes if they want the notification. + virtual void onFrameAvailableHandler(const BufferItem& item); + virtual void onFrameReplacedHandler(const BufferItem& item); + virtual void onBuffersReleasedHandler(); + virtual void onSidebandStreamChangedHandler(); // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in that @@ -244,6 +246,35 @@ protected: // // This mutex is intended to be locked by derived classes. mutable Mutex mMutex; + + // Implements the ConsumerListener interface + virtual void onFrameAvailable(const BufferItem& item) override; + virtual void onFrameReplaced(const BufferItem& item) override; + virtual void onBuffersReleased() override; + virtual void onSidebandStreamChanged() override; + + enum MessageType { + ON_FRAME_AVAILABLE, + ON_FRAME_REPLACED, + ON_BUFFERS_RELEASED, + ON_SIDEBAND_STREAM_CHANGED, + EXIT, + }; + + mutable Mutex mMessageQueueLock; + Condition mMessageAvailable; + std::queue<std::pair<MessageType, BufferItem>> mMessageQueue; + + class MessageThread : public Thread { + public: + MessageThread(ConsumerBase* consumerBase) : + mConsumerBase(consumerBase) {}; + protected: + virtual bool threadLoop() override; + ConsumerBase* mConsumerBase; + }; + + sp<MessageThread> mMessageThread; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h index 0e4acee838..8de3302401 100644 --- a/include/gui/GLConsumer.h +++ b/include/gui/GLConsumer.h @@ -110,7 +110,7 @@ public: // when the current buffer is released by updateTexImage(). Multiple // fences can be set for a given buffer; they will be merged into a single // union fence. - void setReleaseFence(const sp<Fence>& fence); + virtual void setReleaseFence(const sp<Fence>& fence); // getTransformMatrix retrieves the 4x4 texture coordinate transform matrix // associated with the texture image set by the most recent call to @@ -253,10 +253,25 @@ protected: static bool isExternalFormat(PixelFormat format); + struct PendingRelease { + PendingRelease() : isPending(false), currentTexture(-1), + graphicBuffer(), display(nullptr), fence(nullptr) {} + + bool isPending; + int currentTexture; + sp<GraphicBuffer> graphicBuffer; + EGLDisplay display; + EGLSyncKHR fence; + }; + // This releases the buffer in the slot referenced by mCurrentTexture, // then updates state to refer to the BufferItem, which must be a - // newly-acquired buffer. - status_t updateAndReleaseLocked(const BufferItem& item); + // newly-acquired buffer. If pendingRelease is not null, the parameters + // which would have been passed to releaseBufferLocked upon the successful + // completion of the method will instead be returned to the caller, so that + // it may call releaseBufferLocked itself later. + status_t updateAndReleaseLocked(const BufferItem& item, + PendingRelease* pendingRelease = nullptr); // Binds mTexName and the current buffer to mTexTarget. Uses // mCurrentTexture if it's set, mCurrentTextureImage if not. If the diff --git a/include/gui/Surface.h b/include/gui/Surface.h index 3afdaae561..9f51cdd8af 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -23,6 +23,8 @@ #include <ui/ANativeObjectBase.h> #include <ui/Region.h> +#include <binder/Parcelable.h> + #include <utils/RefBase.h> #include <utils/threads.h> #include <utils/KeyedVector.h> @@ -348,6 +350,43 @@ private: bool mSharedBufferHasBeenQueued; }; +namespace view { + +/** + * A simple holder for an IGraphicBufferProducer, to match the managed-side + * android.view.Surface parcelable behavior. + * + * This implements android/view/Surface.aidl + * + * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly + * used in managed Binder calls. + */ +class Surface : public Parcelable { + public: + + String16 name; + sp<IGraphicBufferProducer> graphicBufferProducer; + + virtual status_t writeToParcel(Parcel* parcel) const override; + virtual status_t readFromParcel(const Parcel* parcel) override; + + // nameAlreadyWritten set to true by Surface.java, because it splits + // Parceling itself between managed and native code, so it only wants a part + // of the full parceling to happen on its native side. + status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const; + + // nameAlreadyRead set to true by Surface.java, because it splits + // Parceling itself between managed and native code, so it only wants a part + // of the full parceling to happen on its native side. + status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead); + + private: + + static String16 readMaybeEmptyString16(const Parcel* parcel); +}; + +} // namespace view + }; // namespace android #endif // ANDROID_GUI_SURFACE_H diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h index 993a92f930..76ce68db91 100644 --- a/include/gui/SurfaceControl.h +++ b/include/gui/SurfaceControl.h @@ -57,6 +57,9 @@ public: // release surface data from java void clear(); + // disconnect any api that's connected + void disconnect(); + status_t setLayerStack(uint32_t layerStack); status_t setLayer(uint32_t layer); status_t setPosition(float x, float y); diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h index 9ba5f7f379..c373c281d7 100644 --- a/include/media/hardware/HardwareAPI.h +++ b/include/media/hardware/HardwareAPI.h @@ -27,6 +27,11 @@ namespace android { +// This structure is used to enable Android native buffer use for either +// graphic buffers or secure buffers. +// +// TO CONTROL ANDROID GRAPHIC BUFFER USAGE: +// // A pointer to this struct is passed to the OMX_SetParameter when the extension // index for the 'OMX.google.android.index.enableAndroidNativeBuffers' extension // is given. @@ -45,6 +50,25 @@ namespace android { // 'OMX.google.android.index.useAndroidNativeBuffer2' extension, it should // expect to receive UseAndroidNativeBuffer calls (via OMX_SetParameter) rather // than UseBuffer calls for that port. +// +// TO CONTROL ANDROID SECURE BUFFER USAGE: +// +// A pointer to this struct is passed to the OMX_SetParameter when the extension +// index for the 'OMX.google.android.index.allocateNativeHandle' extension +// is given. +// +// When native handle use is disabled for a port (the default state), +// the OMX node should operate as normal, and expect AllocateBuffer calls to +// return buffer pointers. This is the mode that will be used for non-secure +// buffers if component requires allocate buffers instead of use buffers. +// +// When native handle use has been enabled for a given port, the component +// shall allocate native_buffer_t objects containing that can be passed between +// processes using binder. This is the mode that will be used for secure buffers. +// When an OMX component allocates native handle for buffers, it must close and +// delete that handle when it frees those buffers. Even though pBuffer will point +// to a native handle, nFilledLength, nAllocLength and nOffset will correspond +// to the data inside the opaque buffer. struct EnableAndroidNativeBuffersParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -52,6 +76,8 @@ struct EnableAndroidNativeBuffersParams { OMX_BOOL enable; }; +typedef struct EnableAndroidNativeBuffersParams AllocateNativeHandleParams; + // A pointer to this struct is passed to OMX_SetParameter() when the extension index // "OMX.google.android.index.storeMetaDataInBuffers" or // "OMX.google.android.index.storeANWBufferInMetadata" is given. @@ -232,7 +258,7 @@ struct MediaImage { }; struct MediaImage2 { - enum Type { + enum Type : uint32_t { MEDIA_IMAGE_TYPE_UNKNOWN = 0, MEDIA_IMAGE_TYPE_YUV, MEDIA_IMAGE_TYPE_YUVA, @@ -241,7 +267,7 @@ struct MediaImage2 { MEDIA_IMAGE_TYPE_Y, }; - enum PlaneIndex { + enum PlaneIndex : uint32_t { Y = 0, U = 1, V = 2, @@ -268,6 +294,8 @@ struct MediaImage2 { uint32_t mVertSubsampling; // subsampling compared to the largest plane }; PlaneInfo mPlane[MAX_NUM_PLANES]; + + void initFromV1(const MediaImage&); // for internal use only }; // A pointer to this struct is passed to OMX_GetParameter when the extension @@ -298,6 +326,7 @@ struct MediaImage2 { // buffers, the component shall set mNumPlanes to 0, and mType to MEDIA_IMAGE_TYPE_UNKNOWN. // @deprecated: use DescribeColorFormat2Params +struct DescribeColorFormat2Params; struct DescribeColorFormatParams { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -311,6 +340,8 @@ struct DescribeColorFormatParams { // output: fill out the MediaImage fields MediaImage sMediaImage; + + DescribeColorFormatParams(const DescribeColorFormat2Params&); // for internal use only }; // A pointer to this struct is passed to OMX_GetParameter when the extension @@ -330,6 +361,8 @@ struct DescribeColorFormat2Params { // output: fill out the MediaImage2 fields MediaImage2 sMediaImage; + + void initFromV1(const DescribeColorFormatParams&); // for internal use only }; // A pointer to this struct is passed to OMX_SetParameter or OMX_GetParameter @@ -358,22 +391,42 @@ struct ConfigureVideoTunnelModeParams { // 'OMX.google.android.index.describeColorAspects' extension is given. // // Video encoders: the framework uses OMX_SetConfig to specify color aspects -// of the coded video before the component transitions to idle state. +// of the coded video before the component transitions to idle state, as well +// as before an input frame with a different color aspect is sent: +// 1. The component should maintain an internal color aspect state, initialized +// to Unspecified values. +// 2. Upon OMX_SetConfig, it SHOULD update its internal state for the aspects that are not +// Unspecified in the config param. +// 3. If an aspect value cannot be encoded into the bitstream (including the Other value), that +// aspect should be reset to the Unspecified value (in the internal state). +// 4. OMX_GetConfig SHOULD return the current internal state. +// 5. If changing the color aspects after the first input frame is not supported, and the config +// params would actually cause a change, OMX_SetConfig should fail with the internal state +// unchanged. +// 6. If changing a portion of the aspects after the first input frame is supported, OMX_SetConfig +// should succeed with the portion of the internal state updated. // // Video decoders: the framework uses OMX_SetConfig to specify color aspects // of the coded video parsed from the container before the component transitions -// to idle state. If the bitstream contains color information, the component should -// update the appropriate color aspects - unless the bitstream contains the -// "unspecified" value. For "reserved" values, the component should set the aspect -// to "Other". -// -// The framework subsequently uses OMX_GetConfig to get any updates of the -// color aspects from the decoder. If the color aspects change at any time -// during the processing of the stream, the component shall signal a -// OMX_EventPortSettingsChanged event with data2 set to the extension index -// (or OMX_IndexConfigCommonOutputCrop, as it is handled identically). Component -// shall not signal a separate event purely for color aspect change, if it occurs -// together with a port definition (e.g. size) or crop change. +// to idle state. +// 1. The component should maintiain an internal color aspect state, initialized to Unspecified +// values. +// 2. Upon OMX_SetConfig, it SHOULD update its internal state for the aspects that are not +// Unspecified in the config param, regardless of whether such aspects could be supplied by the +// component bitstream. (E.g. it should blindly support all enumeration values, even unknown +// ones, and the Other value). +// 3. OMX_GetConfig SHOULD return the current internal state. +// 4. When the component processes color aspect information in the bitstream with a non-Unspecified +// value, it should update its internal state with that information just before the frame +// with the new information is outputted, and the component SHALL signal an +// OMX_EventPortSettingsChanged event with data2 set to the extension index (or +// OMX_IndexConfigCommonOutputCrop, as it is handled identically). +// 4a. Component shall not signal a separate event purely for color aspect change, if it occurs +// together with a port definition (e.g. size) or crop change. +// 5. If the aspects a component encounters in the bitstream cannot be represented with the below +// enumeration values, it should set those aspects to Other. Restricted values in the bitstream +// should be treated as defined by the relevant bitstream specifications/standards, or as +// Unspecified, if not defined. // // NOTE: this structure is expected to grow in the future if new color aspects are // added to codec bitstreams. OMX component should not require a specific nSize @@ -381,16 +434,6 @@ struct ConfigureVideoTunnelModeParams { // time of implementation. All new fields will be added at the end of the structure // ensuring backward compatibility. -struct DescribeColorAspectsParams { - OMX_U32 nSize; // IN - OMX_VERSIONTYPE nVersion; // IN - OMX_U32 nPortIndex; // IN - OMX_U32 nRange; // IN/OUT (one of the ColorAspects.Range enums) - OMX_U32 nPrimaries; // IN/OUT (one of the ColorAspects.Primaries enums) - OMX_U32 nTransfer; // IN/OUT (one of the ColorAspects.Transfer enums) - OMX_U32 nMatrixCoeffs; // IN/OUT (one of the ColorAspects.MatrixCoeffs enums) -}; - struct ColorAspects { // this is in sync with the range values in graphics.h enum Range : uint32_t { @@ -456,6 +499,18 @@ struct ColorAspects { StandardFilm, // PrimariesGenericFilm and KR=0.253, KB=0.068 StandardOther = 0xff, }; + + Range mRange; // IN/OUT + Primaries mPrimaries; // IN/OUT + Transfer mTransfer; // IN/OUT + MatrixCoeffs mMatrixCoeffs; // IN/OUT +}; + +struct DescribeColorAspectsParams { + OMX_U32 nSize; // IN + OMX_VERSIONTYPE nVersion; // IN + OMX_U32 nPortIndex; // IN + ColorAspects sAspects; // IN/OUT }; } // namespace android diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h index a741f6da45..0aa567df8b 100644 --- a/include/media/openmax/OMX_AsString.h +++ b/include/media/openmax/OMX_AsString.h @@ -904,10 +904,11 @@ inline static const char *asString( inline static const char *asString(OMX_VIDEO_HEVCPROFILETYPE i, const char *def = "!!") { switch (i) { - case OMX_VIDEO_HEVCProfileUnknown: return "Unknown"; // unused - case OMX_VIDEO_HEVCProfileMain: return "Main"; - case OMX_VIDEO_HEVCProfileMain10: return "Main10"; - default: return def; + case OMX_VIDEO_HEVCProfileUnknown: return "Unknown"; // unused + case OMX_VIDEO_HEVCProfileMain: return "Main"; + case OMX_VIDEO_HEVCProfileMain10: return "Main10"; + case OMX_VIDEO_HEVCProfileMain10HDR10: return "Main10HDR10"; + default: return def; } } diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h index 836a1533ac..1a2f0b59e7 100644 --- a/include/media/openmax/OMX_VideoExt.h +++ b/include/media/openmax/OMX_VideoExt.h @@ -177,10 +177,12 @@ typedef struct OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE { /** HEVC Profile enum type */ typedef enum OMX_VIDEO_HEVCPROFILETYPE { - OMX_VIDEO_HEVCProfileUnknown = 0x0, - OMX_VIDEO_HEVCProfileMain = 0x1, - OMX_VIDEO_HEVCProfileMain10 = 0x2, - OMX_VIDEO_HEVCProfileMax = 0x7FFFFFFF + OMX_VIDEO_HEVCProfileUnknown = 0x0, + OMX_VIDEO_HEVCProfileMain = 0x1, + OMX_VIDEO_HEVCProfileMain10 = 0x2, + // Main10 profile with HDR SEI support. + OMX_VIDEO_HEVCProfileMain10HDR10 = 0x1000, + OMX_VIDEO_HEVCProfileMax = 0x7FFFFFFF } OMX_VIDEO_HEVCPROFILETYPE; /** HEVC Level enum type */ diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index e4d82019f1..9a061a0ceb 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -28,7 +28,7 @@ static sp<IBinder> gToken; static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) { pthread_mutex_lock(&gTokenMutex); - if (gToken == NULL) { + if (gToken == NULL || gToken->pingBinder() != NO_ERROR) { gToken = service->getToken(new BBinder()); } pthread_mutex_unlock(&gTokenMutex); diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index e9891a830d..fb8d620b4c 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -26,6 +26,7 @@ #include <sys/mman.h> #include <binder/IMemory.h> +#include <cutils/log.h> #include <utils/KeyedVector.h> #include <utils/threads.h> #include <utils/Atomic.h> @@ -187,15 +188,26 @@ sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const if (heap != 0) { mHeap = interface_cast<IMemoryHeap>(heap); if (mHeap != 0) { - mOffset = o; - mSize = s; + size_t heapSize = mHeap->getSize(); + if (s <= heapSize + && o >= 0 + && (static_cast<size_t>(o) <= heapSize - s)) { + mOffset = o; + mSize = s; + } else { + // Hm. + android_errorWriteWithInfoLog(0x534e4554, + "26877992", -1, NULL, 0); + mOffset = 0; + mSize = 0; + } } } } } if (offset) *offset = mOffset; if (size) *size = mSize; - return mHeap; + return (mSize > 0) ? mHeap : 0; } // --------------------------------------------------------------------------- diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp index c37920d8c2..76508b88bf 100644 --- a/libs/binder/IProcessInfoService.cpp +++ b/libs/binder/IProcessInfoService.cpp @@ -88,72 +88,4 @@ IMPLEMENT_META_INTERFACE(ProcessInfoService, "android.os.IProcessInfoService"); // ---------------------------------------------------------------------- -status_t BnProcessInfoService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - switch(code) { - case GET_PROCESS_STATES_FROM_PIDS: { - CHECK_INTERFACE(IProcessInfoService, data, reply); - int32_t arrayLen = data.readInt32(); - if (arrayLen <= 0) { - reply->writeNoException(); - reply->writeInt32(0); - reply->writeInt32(NOT_ENOUGH_DATA); - return NO_ERROR; - } - - size_t len = static_cast<size_t>(arrayLen); - int32_t pids[len]; - status_t res = data.read(pids, len * sizeof(*pids)); - - // Ignore output array length returned in the parcel here, as the states array must - // always be the same length as the input PIDs array. - int32_t states[len]; - for (size_t i = 0; i < len; i++) states[i] = -1; - if (res == NO_ERROR) { - res = getProcessStatesFromPids(len, /*in*/ pids, /*out*/ states); - } - reply->writeNoException(); - reply->writeInt32Array(len, states); - reply->writeInt32(res); - return NO_ERROR; - } break; - case GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS: { - CHECK_INTERFACE(IProcessInfoService, data, reply); - int32_t arrayLen = data.readInt32(); - if (arrayLen <= 0) { - reply->writeNoException(); - reply->writeInt32(0); - reply->writeInt32(NOT_ENOUGH_DATA); - return NO_ERROR; - } - - size_t len = static_cast<size_t>(arrayLen); - int32_t pids[len]; - status_t res = data.read(pids, len * sizeof(*pids)); - - // Ignore output array length returned in the parcel here, as the - // states array must always be the same length as the input PIDs array. - int32_t states[len]; - int32_t scores[len]; - for (size_t i = 0; i < len; i++) { - states[i] = -1; - scores[i] = -10000; - } - if (res == NO_ERROR) { - res = getProcessStatesAndOomScoresFromPids( - len, /*in*/ pids, /*out*/ states, /*out*/ scores); - } - reply->writeNoException(); - reply->writeInt32Array(len, states); - reply->writeInt32Array(len, scores); - reply->writeInt32(res); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -// ---------------------------------------------------------------------- - }; // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 1008f02d8c..678d98bc8a 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -808,16 +808,10 @@ status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) { return writeUtf8AsUtf16(*str); } -status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) -{ - if (!val) { - return writeInt32(-1); - } +namespace { - return writeByteVector(*val); -} - -status_t Parcel::writeByteVector(const std::vector<int8_t>& val) +template<typename T> +status_t writeByteVectorInternal(Parcel* parcel, const std::vector<T>& val) { status_t status; if (val.size() > std::numeric_limits<int32_t>::max()) { @@ -825,12 +819,12 @@ status_t Parcel::writeByteVector(const std::vector<int8_t>& val) return status; } - status = writeInt32(val.size()); + status = parcel->writeInt32(val.size()); if (status != OK) { return status; } - void* data = writeInplace(val.size()); + void* data = parcel->writeInplace(val.size()); if (!data) { status = BAD_VALUE; return status; @@ -840,6 +834,37 @@ status_t Parcel::writeByteVector(const std::vector<int8_t>& val) return status; } +template<typename T> +status_t writeByteVectorInternalPtr(Parcel* parcel, + const std::unique_ptr<std::vector<T>>& val) +{ + if (!val) { + return parcel->writeInt32(-1); + } + + return writeByteVectorInternal(parcel, *val); +} + +} // namespace + +status_t Parcel::writeByteVector(const std::vector<int8_t>& val) { + return writeByteVectorInternal(this, val); +} + +status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) +{ + return writeByteVectorInternalPtr(this, val); +} + +status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) { + return writeByteVectorInternal(this, val); +} + +status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val) +{ + return writeByteVectorInternalPtr(this, val); +} + status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val) { return writeTypedVector(val, &Parcel::writeInt32); @@ -1406,11 +1431,15 @@ restart_write: return err; } -status_t Parcel::readByteVector(std::vector<int8_t>* val) const { +namespace { + +template<typename T> +status_t readByteVectorInternal(const Parcel* parcel, + std::vector<T>* val) { val->clear(); int32_t size; - status_t status = readInt32(&size); + status_t status = parcel->readInt32(&size); if (status != OK) { return status; @@ -1420,12 +1449,12 @@ status_t Parcel::readByteVector(std::vector<int8_t>* val) const { status = UNEXPECTED_NULL; return status; } - if (size_t(size) > dataAvail()) { + if (size_t(size) > parcel->dataAvail()) { status = BAD_VALUE; return status; } - const void* data = readInplace(size); + const void* data = parcel->readInplace(size); if (!data) { status = BAD_VALUE; return status; @@ -1436,20 +1465,23 @@ status_t Parcel::readByteVector(std::vector<int8_t>* val) const { return status; } -status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const { - const int32_t start = dataPosition(); +template<typename T> +status_t readByteVectorInternalPtr( + const Parcel* parcel, + std::unique_ptr<std::vector<T>>* val) { + const int32_t start = parcel->dataPosition(); int32_t size; - status_t status = readInt32(&size); + status_t status = parcel->readInt32(&size); val->reset(); if (status != OK || size < 0) { return status; } - setDataPosition(start); - val->reset(new std::vector<int8_t>()); + parcel->setDataPosition(start); + val->reset(new std::vector<T>()); - status = readByteVector(val->get()); + status = readByteVectorInternal(parcel, val->get()); if (status != OK) { val->reset(); @@ -1458,6 +1490,24 @@ status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const return status; } +} // namespace + +status_t Parcel::readByteVector(std::vector<int8_t>* val) const { + return readByteVectorInternal(this, val); +} + +status_t Parcel::readByteVector(std::vector<uint8_t>* val) const { + return readByteVectorInternal(this, val); +} + +status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const { + return readByteVectorInternalPtr(this, val); +} + +status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const { + return readByteVectorInternalPtr(this, val); +} + status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const { return readNullableTypedVector(val, &Parcel::readInt32); } diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index f8b50cc3ca..4029496936 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -32,6 +32,10 @@ #include <gui/IConsumerListener.h> #include <gui/IProducerListener.h> +#include <binder/IPCThreadState.h> +#include <binder/PermissionCache.h> +#include <private/android_filesystem_config.h> + namespace android { BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) : @@ -712,7 +716,18 @@ sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const { } void BufferQueueConsumer::dump(String8& result, const char* prefix) const { - mCore->dump(result, prefix); + const IPCThreadState* ipc = IPCThreadState::self(); + const pid_t pid = ipc->getCallingPid(); + const uid_t uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) + && !PermissionCache::checkPermission(String16( + "android.permission.DUMP"), pid, uid)) { + result.appendFormat("Permission Denial: can't dump BufferQueueConsumer " + "from pid=%d, uid=%d\n", pid, uid); + android_errorWriteWithInfoLog(0x534e4554, "27046057", uid, NULL, 0); + } else { + mCore->dump(result, prefix); + } } } // namespace android diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index e065e6151d..818fac6613 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -222,9 +222,9 @@ int BufferQueueProducer::getFreeSlotLocked() const { if (mCore->mFreeSlots.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } - auto slot = mCore->mFreeSlots.begin(); + int slot = *(mCore->mFreeSlots.begin()); mCore->mFreeSlots.erase(slot); - return *slot; + return slot; } status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, @@ -1115,6 +1115,10 @@ status_t BufferQueueProducer::disconnect(int api) { return NO_ERROR; } + if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) { + api = mCore->mConnectedApi; + } + switch (api) { case NATIVE_WINDOW_API_EGL: case NATIVE_WINDOW_API_CPU: diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index d01187fbbf..a22b81bd95 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -74,12 +74,26 @@ ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool c } else { mConsumer->setConsumerName(mName); } + + mMessageThread = new MessageThread(this); + mMessageThread->run(); } ConsumerBase::~ConsumerBase() { CB_LOGV("~ConsumerBase"); - Mutex::Autolock lock(mMutex); + mMessageThread->requestExit(); + { + Mutex::Autolock lock(mMessageQueueLock); + mMessageQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(EXIT), + std::forward_as_tuple()); + mMessageAvailable.signal(); + } + + mMessageThread->join(); + + Mutex::Autolock lock(mMutex); // Verify that abandon() has been called before we get here. This should // be done by ConsumerBase::onLastStrongRef(), but it's possible for a // derived class to override that method and not call @@ -100,6 +114,13 @@ void ConsumerBase::freeBufferLocked(int slotIndex) { } void ConsumerBase::onFrameAvailable(const BufferItem& item) { + Mutex::Autolock lock(mMessageQueueLock); + mMessageQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(ON_FRAME_AVAILABLE), + std::forward_as_tuple(item)); + mMessageAvailable.signal(); +} +void ConsumerBase::onFrameAvailableHandler(const BufferItem& item) { CB_LOGV("onFrameAvailable"); sp<FrameAvailableListener> listener; @@ -115,6 +136,14 @@ void ConsumerBase::onFrameAvailable(const BufferItem& item) { } void ConsumerBase::onFrameReplaced(const BufferItem &item) { + Mutex::Autolock lock(mMessageQueueLock); + mMessageQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(ON_FRAME_REPLACED), + std::forward_as_tuple(item)); + mMessageAvailable.signal(); +} + +void ConsumerBase::onFrameReplacedHandler(const BufferItem &item) { CB_LOGV("onFrameReplaced"); sp<FrameAvailableListener> listener; @@ -130,6 +159,14 @@ void ConsumerBase::onFrameReplaced(const BufferItem &item) { } void ConsumerBase::onBuffersReleased() { + Mutex::Autolock lock(mMessageQueueLock); + mMessageQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(ON_BUFFERS_RELEASED), + std::forward_as_tuple()); + mMessageAvailable.signal(); +} + +void ConsumerBase::onBuffersReleasedHandler() { Mutex::Autolock lock(mMutex); CB_LOGV("onBuffersReleased"); @@ -149,6 +186,45 @@ void ConsumerBase::onBuffersReleased() { } void ConsumerBase::onSidebandStreamChanged() { + Mutex::Autolock lock(mMessageQueueLock); + mMessageQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(ON_SIDEBAND_STREAM_CHANGED), + std::forward_as_tuple()); + mMessageAvailable.signal(); +} + +void ConsumerBase::onSidebandStreamChangedHandler() { +} + +bool ConsumerBase::MessageThread::threadLoop() { + Mutex::Autolock lock(mConsumerBase->mMessageQueueLock); + + if (mConsumerBase->mMessageQueue.empty()) { + mConsumerBase->mMessageAvailable.wait(mConsumerBase->mMessageQueueLock); + } + + while (!mConsumerBase->mMessageQueue.empty()) { + auto nextMessage = mConsumerBase->mMessageQueue.front(); + + switch (nextMessage.first) { + case ON_FRAME_AVAILABLE: + mConsumerBase->onFrameAvailableHandler(nextMessage.second); + break; + case ON_FRAME_REPLACED: + mConsumerBase->onFrameReplacedHandler(nextMessage.second); + break; + case ON_BUFFERS_RELEASED: + mConsumerBase->onBuffersReleasedHandler(); + break; + case ON_SIDEBAND_STREAM_CHANGED: + mConsumerBase->onSidebandStreamChangedHandler(); + break; + case EXIT: + break; + } + mConsumerBase->mMessageQueue.pop(); + } + return true; } void ConsumerBase::abandon() { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 55059dd87b..149f5bd65c 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -370,7 +370,8 @@ status_t GLConsumer::releaseBufferLocked(int buf, return err; } -status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item) +status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, + PendingRelease* pendingRelease) { status_t err = NO_ERROR; @@ -432,14 +433,23 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item) // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t status = releaseBufferLocked( - mCurrentTexture, mCurrentTextureImage->graphicBuffer(), - mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); - if (status < NO_ERROR) { - GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", - strerror(-status), status); - err = status; - // keep going, with error raised [?] + if (pendingRelease == nullptr) { + status_t status = releaseBufferLocked( + mCurrentTexture, mCurrentTextureImage->graphicBuffer(), + mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", + strerror(-status), status); + err = status; + // keep going, with error raised [?] + } + } else { + pendingRelease->currentTexture = mCurrentTexture; + pendingRelease->graphicBuffer = + mCurrentTextureImage->graphicBuffer(); + pendingRelease->display = mEglDisplay; + pendingRelease->fence = mEglSlots[mCurrentTexture].mEglFence; + pendingRelease->isPending = true; } } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 42adf90840..8025ca58b5 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -896,10 +896,6 @@ int Surface::setBufferCount(int bufferCount) } } - if (err == NO_ERROR) { - freeAllBuffers(); - } - ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s", bufferCount, strerror(-err)); @@ -916,10 +912,6 @@ int Surface::setMaxDequeuedBufferCount(int maxDequeuedBuffers) { ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) " "returned %s", maxDequeuedBuffers, strerror(-err)); - if (err == NO_ERROR) { - freeAllBuffers(); - } - return err; } @@ -932,10 +924,6 @@ int Surface::setAsyncMode(bool async) { ALOGE_IF(err, "IGraphicBufferProducer::setAsyncMode(%d) returned %s", async, strerror(-err)); - if (err == NO_ERROR) { - freeAllBuffers(); - } - return err; } @@ -1271,4 +1259,57 @@ status_t Surface::unlockAndPost() return err; } +namespace view { + +status_t Surface::writeToParcel(Parcel* parcel) const { + return writeToParcel(parcel, false); +} + +status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const { + if (parcel == nullptr) return BAD_VALUE; + + status_t res = OK; + + if (!nameAlreadyWritten) res = parcel->writeString16(name); + + if (res == OK) { + res = parcel->writeStrongBinder( + IGraphicBufferProducer::asBinder(graphicBufferProducer)); + } + return res; +} + +status_t Surface::readFromParcel(const Parcel* parcel) { + return readFromParcel(parcel, false); +} + +status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) { + if (parcel == nullptr) return BAD_VALUE; + + if (!nameAlreadyRead) { + name = readMaybeEmptyString16(parcel); + } + + sp<IBinder> binder; + + status_t res = parcel->readStrongBinder(&binder); + if (res != OK) return res; + + graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder); + + return OK; +} + +String16 Surface::readMaybeEmptyString16(const Parcel* parcel) { + size_t len; + const char16_t* str = parcel->readString16Inplace(&len); + if (str != nullptr) { + return String16(str, len); + } else { + return String16(); + } +} + +} // namespace view + }; // namespace android diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index a945358cc0..e1a951c93f 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -33,6 +33,7 @@ #include <ui/GraphicBuffer.h> #include <ui/Rect.h> +#include <gui/BufferQueueCore.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> @@ -81,6 +82,13 @@ void SurfaceControl::clear() destroy(); } +void SurfaceControl::disconnect() { + if (mGraphicBufferProducer != NULL) { + mGraphicBufferProducer->disconnect( + BufferQueueCore::CURRENTLY_CONNECTED_API); + } +} + bool SurfaceControl::isSameSurface( const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { diff --git a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp index c28b4d141c..b8a7a90be2 100644 --- a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp @@ -192,6 +192,10 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { ASSERT_EQ(EGL_SUCCESS, eglGetError()); mProducerEglSurface = EGL_NO_SURFACE; + // sleep for 10ms to allow any asynchronous operations to complete before + // checking the reference counts + usleep(10000); + // This test should have the only reference to buffer 0. EXPECT_EQ(1, buffers[0]->getStrongCount()); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 6f0104a25a..0de60c983f 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -232,4 +232,30 @@ TEST_F(SurfaceTest, GetConsumerName) { EXPECT_STREQ("TestConsumer", surface->getConsumerName().string()); } +TEST_F(SurfaceTest, DynamicSetBufferCount) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<DummyConsumer> dummyConsumer(new DummyConsumer); + consumer->consumerConnect(dummyConsumer, false); + consumer->setConsumerName(String8("TestConsumer")); + + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + + ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), + NATIVE_WINDOW_API_CPU)); + native_window_set_buffer_count(window.get(), 4); + + int fence; + ANativeWindowBuffer* buffer; + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + native_window_set_buffer_count(window.get(), 3); + ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); + native_window_set_buffer_count(window.get(), 2); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); +} + } diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index fe649fb548..0627ca6d6f 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -608,7 +608,7 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { return NULL; } if (numKeys > MAX_KEYS) { - ALOGE("Too many keys in KeyCharacterMap (%d > %d)", numKeys, MAX_KEYS); + ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS); return NULL; } diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index e593a721ef..49f501d3a6 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -92,7 +92,7 @@ checkGlesEmulationStatus(void) return -1; /* We are in the emulator, get GPU status value */ - property_get("ro.kernel.qemu.gles",prop,"0"); + property_get("qemu.gles",prop,"0"); return atoi(prop); } @@ -174,11 +174,45 @@ static void* load_wrapper(const char* path) { #endif #endif +static void setEmulatorGlesValue(void) { + char prop[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", prop, "0"); + if (atoi(prop) != 1) return; + + property_get("ro.kernel.qemu.gles",prop,"0"); + if (atoi(prop) == 1) { + ALOGD("Emulator has host GPU support, qemu.gles is set to 1."); + property_set("qemu.gles", "1"); + return; + } + + // for now, checking the following + // directory is good enough for emulator system images + const char* vendor_lib_path = +#if defined(__LP64__) + "/vendor/lib64/egl"; +#else + "/vendor/lib/egl"; +#endif + + const bool has_vendor_lib = (access(vendor_lib_path, R_OK) == 0); + if (has_vendor_lib) { + ALOGD("Emulator has vendor provided software renderer, qemu.gles is set to 2."); + property_set("qemu.gles", "2"); + } else { + ALOGD("Emulator without GPU support detected. " + "Fallback to legacy software renderer, qemu.gles is set to 0."); + property_set("qemu.gles", "0"); + } +} + void* Loader::open(egl_connection_t* cnx) { void* dso; driver_t* hnd = 0; + setEmulatorGlesValue(); + dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2); if (dso) { hnd = new driver_t(dso); @@ -280,8 +314,6 @@ void *Loader::load_driver(const char* kind, int emulationStatus = checkGlesEmulationStatus(); switch (emulationStatus) { case 0: - ALOGD("Emulator without GPU support detected. " - "Fallback to legacy software renderer."); #if defined(__LP64__) result.setTo("/system/lib64/egl/libGLES_android.so"); #else diff --git a/opengl/tests/hwc/Android.mk b/opengl/tests/hwc/Android.mk index f83846b691..693fba416e 100644 --- a/opengl/tests/hwc/Android.mk +++ b/opengl/tests/hwc/Android.mk @@ -19,7 +19,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE_TAGS := tests LOCAL_MODULE:= libhwcTest -LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror LOCAL_CXX_STL := libc++ LOCAL_SRC_FILES:= hwcTestLib.cpp LOCAL_C_INCLUDES += system/extras/tests/include \ @@ -32,7 +32,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE:= hwcStress LOCAL_MODULE_TAGS := tests -LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror LOCAL_CXX_STL := libc++ LOCAL_SRC_FILES:= hwcStress.cpp @@ -62,7 +62,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE:= hwcRects LOCAL_MODULE_TAGS := tests -LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror LOCAL_CXX_STL := libc++ LOCAL_SRC_FILES:= hwcRects.cpp @@ -92,7 +92,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE:= hwcColorEquiv LOCAL_MODULE_TAGS := tests -LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror LOCAL_CXX_STL := libc++ LOCAL_SRC_FILES:= hwcColorEquiv.cpp @@ -122,7 +122,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE:= hwcCommit LOCAL_MODULE_TAGS := tests -LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror LOCAL_CXX_STL := libc++ LOCAL_SRC_FILES:= hwcCommit.cpp diff --git a/opengl/tests/hwc/hwcColorEquiv.cpp b/opengl/tests/hwc/hwcColorEquiv.cpp index 06a0191f60..f1361b8c43 100644 --- a/opengl/tests/hwc/hwcColorEquiv.cpp +++ b/opengl/tests/hwc/hwcColorEquiv.cpp @@ -62,6 +62,8 @@ * frame for cases where an equivalent color does not exist. */ +#define LOG_TAG "hwcColorEquivTest" + #include <algorithm> #include <assert.h> #include <cerrno> @@ -87,7 +89,6 @@ #include <ui/GraphicBuffer.h> -#define LOG_TAG "hwcColorEquivTest" #include <utils/Log.h> #include <testUtil.h> diff --git a/opengl/tests/hwc/hwcCommit.cpp b/opengl/tests/hwc/hwcCommit.cpp index 1bcb8604fe..6b287e9126 100644 --- a/opengl/tests/hwc/hwcCommit.cpp +++ b/opengl/tests/hwc/hwcCommit.cpp @@ -70,6 +70,8 @@ * made and reported for each of the known graphic format. */ +#define LOG_TAG "hwcCommitTest" + #include <algorithm> #include <assert.h> #include <cerrno> @@ -98,7 +100,6 @@ #include <ui/GraphicBuffer.h> -#define LOG_TAG "hwcCommitTest" #include <utils/Log.h> #include <testUtil.h> @@ -120,8 +121,6 @@ const uint32_t defaultBlend = HWC_BLENDING_NONE; const ColorFract defaultColor(0.5, 0.5, 0.5); const float defaultAlpha = 1.0; // Opaque const HwcTestDim defaultSourceDim(1, 1); -const struct hwc_rect defaultSourceCrop = {0, 0, 1, 1}; -const struct hwc_rect defaultDisplayFrame = {0, 0, 100, 100}; // Global Constants const uint32_t printFieldWidth = 2; diff --git a/opengl/tests/hwc/hwcRects.cpp b/opengl/tests/hwc/hwcRects.cpp index 56c1a2ad39..2e2b204b4d 100644 --- a/opengl/tests/hwc/hwcRects.cpp +++ b/opengl/tests/hwc/hwcRects.cpp @@ -80,6 +80,8 @@ * a list of attributes and the format of their expected value. */ +#define LOG_TAG "hwcRectsTest" + #include <algorithm> #include <assert.h> #include <cerrno> @@ -105,8 +107,6 @@ #include <GLES2/gl2ext.h> #include <ui/GraphicBuffer.h> - -#define LOG_TAG "hwcRectsTest" #include <utils/Log.h> #include <testUtil.h> diff --git a/opengl/tests/hwc/hwcStress.cpp b/opengl/tests/hwc/hwcStress.cpp index b1d6c764f8..60c29efa4c 100644 --- a/opengl/tests/hwc/hwcStress.cpp +++ b/opengl/tests/hwc/hwcStress.cpp @@ -78,6 +78,8 @@ * a different color from the rest of the rectangle. */ +#define LOG_TAG "hwcStressTest" + #include <algorithm> #include <assert.h> #include <cerrno> @@ -103,7 +105,6 @@ #include <ui/GraphicBuffer.h> -#define LOG_TAG "hwcStressTest" #include <utils/Log.h> #include <testUtil.h> diff --git a/opengl/tests/lib/Android.mk b/opengl/tests/lib/Android.mk index e5124adb30..4407e7b5c5 100644 --- a/opengl/tests/lib/Android.mk +++ b/opengl/tests/lib/Android.mk @@ -22,6 +22,6 @@ LOCAL_SRC_FILES:= glTestLib.cpp WindowSurface.cpp LOCAL_C_INCLUDES += system/extras/tests/include \ $(call include-path-for, opengl-tests-includes) -LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror include $(BUILD_STATIC_LIBRARY) diff --git a/opengl/tests/lib/glTestLib.cpp b/opengl/tests/lib/glTestLib.cpp index b434fc7efa..213dffd50f 100644 --- a/opengl/tests/lib/glTestLib.cpp +++ b/opengl/tests/lib/glTestLib.cpp @@ -31,7 +31,6 @@ #include <utils/Log.h> #include <testUtil.h> -using namespace std; using namespace android; void glTestPrintGLString(const char *name, GLenum s) diff --git a/opengl/tools/glgen/specs/gles11/GLES30.spec b/opengl/tools/glgen/specs/gles11/GLES30.spec index a426eb012b..f190fc0b6a 100644 --- a/opengl/tools/glgen/specs/gles11/GLES30.spec +++ b/opengl/tools/glgen/specs/gles11/GLES30.spec @@ -108,3 +108,4 @@ void glInvalidateSubFramebuffer ( GLenum target, GLsizei numAttachments, const G void glTexStorage2D ( GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height ) void glTexStorage3D ( GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth ) void glGetInternalformativ ( GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params ) +void glReadPixels ( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint offset ) diff --git a/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp b/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp index e7014811f4..52295a120a 100644 --- a/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp +++ b/opengl/tools/glgen/stubs/gles11/glCreateShaderProgramv.cpp @@ -2,7 +2,67 @@ static jint android_glCreateShaderProgramv (JNIEnv *_env, jobject _this, jint type, jobjectArray strings) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + GLsizei _count; + const GLchar** _strings = NULL; + jstring* _jstrings = NULL; + GLuint _returnValue = 0; - jniThrowException(_env, "java/lang/UnsupportedOperationException", "not yet implemented"); - return 0; + if (!strings) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "strings == null"; + goto exit; + } + + _count = _env->GetArrayLength(strings); + + _strings = (const GLchar**) calloc(_count, sizeof(const GLchar*)); + if (!_strings) { + _exception = 1; + _exceptionType = "java/lang/OutOfMemoryError"; + _exceptionMessage = "out of memory"; + goto exit; + } + + _jstrings = (jstring*) calloc(_count, sizeof(jstring)); + if (!_jstrings) { + _exception = 1; + _exceptionType = "java/lang/OutOfMemoryError"; + _exceptionMessage = "out of memory"; + goto exit; + } + + for(int i = 0; i < _count; i++) { + _jstrings[i] = (jstring) _env->GetObjectArrayElement(strings, i); + if (!_jstrings[i]) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "strings == null"; + goto exit; + } + _strings[i] = _env->GetStringUTFChars(_jstrings[i], 0); + } + + _returnValue = glCreateShaderProgramv((GLenum)type, _count, _strings); +exit: + if (_strings && _jstrings) { + for(int i = 0; i < _count; i++) { + if (_strings[i] && _jstrings[i]) { + _env->ReleaseStringUTFChars(_jstrings[i], _strings[i]); + } + } + } + if (_strings) { + free(_strings); + } + if (_jstrings) { + free(_jstrings); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return (jint)_returnValue; } diff --git a/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.cpp b/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.cpp index 47f232d5b3..aa0d2a4616 100644 --- a/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.cpp +++ b/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.cpp @@ -118,22 +118,35 @@ exit: static void android_glGetTransformFeedbackVarying__IIILjava_nio_IntBuffer_2Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2B (JNIEnv *_env, jobject _this, jint program, jint index, jint bufsize, jobject length_buf, jobject size_buf, jobject type_buf, jbyte name) { + jniThrowException(_env, "java/lang/UnsupportedOperationException", "deprecated"); +} + +/* void glGetTransformFeedbackVarying ( GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name ) */ +static void +android_glGetTransformFeedbackVarying__IIILjava_nio_IntBuffer_2Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2Ljava_nio_ByteBuffer_2 + (JNIEnv *_env, jobject _this, jint program, jint index, jint bufsize, jobject length_buf, jobject size_buf, jobject type_buf, jobject name_buf) { jintArray _lengthArray = (jintArray) 0; jint _lengthBufferOffset = (jint) 0; jintArray _sizeArray = (jintArray) 0; jint _sizeBufferOffset = (jint) 0; jintArray _typeArray = (jintArray) 0; jint _typeBufferOffset = (jint) 0; + jbyteArray _nameArray = (jbyteArray)0; + jint _nameBufferOffset = (jint)0; jint _lengthRemaining; GLsizei *length = (GLsizei *) 0; jint _sizeRemaining; GLint *size = (GLint *) 0; jint _typeRemaining; GLenum *type = (GLenum *) 0; + jint _nameRemaining; + GLchar* name = (GLchar*)0; + length = (GLsizei *)getPointer(_env, length_buf, (jarray*)&_lengthArray, &_lengthRemaining, &_lengthBufferOffset); size = (GLint *)getPointer(_env, size_buf, (jarray*)&_sizeArray, &_sizeRemaining, &_sizeBufferOffset); type = (GLenum *)getPointer(_env, type_buf, (jarray*)&_typeArray, &_typeRemaining, &_typeBufferOffset); + name = (GLchar*)getPointer(_env, name_buf, (jarray*)&_nameArray, &_nameRemaining, &_nameBufferOffset); if (length == NULL) { char * _lengthBase = (char *)_env->GetIntArrayElements(_lengthArray, (jboolean *) 0); length = (GLsizei *) (_lengthBase + _lengthBufferOffset); @@ -146,6 +159,10 @@ android_glGetTransformFeedbackVarying__IIILjava_nio_IntBuffer_2Ljava_nio_IntBuff char * _typeBase = (char *)_env->GetIntArrayElements(_typeArray, (jboolean *) 0); type = (GLenum *) (_typeBase + _typeBufferOffset); } + if (name == NULL) { + char* _nameBase = (char *)_env->GetByteArrayElements(_nameArray, (jboolean*)0); + name = (GLchar *) (_nameBase + _nameBufferOffset); + } glGetTransformFeedbackVarying( (GLuint)program, (GLuint)index, @@ -153,11 +170,7 @@ android_glGetTransformFeedbackVarying__IIILjava_nio_IntBuffer_2Ljava_nio_IntBuff (GLsizei *)length, (GLint *)size, (GLenum *)type, - // The cast below is incorrect. The driver will end up writing to the - // address specified by name, which will always crash the process since - // it is guaranteed to be in low memory. The additional static_cast - // suppresses the warning for now. http://b/19478262 - (char *)static_cast<uintptr_t>(name) + (GLchar*)name ); if (_typeArray) { releaseArrayPointer<jintArray, jint*, IntArrayReleaser>(_env, _typeArray, (jint*)type, JNI_TRUE); @@ -168,6 +181,9 @@ android_glGetTransformFeedbackVarying__IIILjava_nio_IntBuffer_2Ljava_nio_IntBuff if (_lengthArray) { releaseArrayPointer<jintArray, jint*, IntArrayReleaser>(_env, _lengthArray, (jint*)length, JNI_TRUE); } + if (_nameArray) { + releaseArrayPointer<jbyteArray, jbyte*, ByteArrayReleaser>(_env, _nameArray, (jbyte*)name, JNI_TRUE); + } } /* void glGetTransformFeedbackVarying ( GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name ) */ diff --git a/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.java b/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.java index f73bbfe30b..e54359d7d4 100644 --- a/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.java +++ b/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.java @@ -15,7 +15,10 @@ ); // C function void glGetTransformFeedbackVarying ( GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name ) - + /** + * @deprecated + * Use the version that takes a ByteBuffer as the last argument, or the versions that return a String. + * */ public static native void glGetTransformFeedbackVarying( int program, int index, @@ -28,6 +31,18 @@ // C function void glGetTransformFeedbackVarying ( GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name ) + public static native void glGetTransformFeedbackVarying( + int program, + int index, + int bufsize, + java.nio.IntBuffer length, + java.nio.IntBuffer size, + java.nio.IntBuffer type, + java.nio.ByteBuffer name + ); + + // C function void glGetTransformFeedbackVarying ( GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name ) + public static native String glGetTransformFeedbackVarying( int program, int index, diff --git a/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.nativeReg b/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.nativeReg index 412d04ccc5..7f5829bbcb 100644 --- a/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.nativeReg +++ b/opengl/tools/glgen/stubs/gles11/glGetTransformFeedbackVarying.nativeReg @@ -1,4 +1,5 @@ {"glGetTransformFeedbackVarying", "(III[II[II[II[BI)V", (void *) android_glGetTransformFeedbackVarying__III_3II_3II_3II_3BI }, {"glGetTransformFeedbackVarying", "(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;B)V", (void *) android_glGetTransformFeedbackVarying__IIILjava_nio_IntBuffer_2Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2B }, +{"glGetTransformFeedbackVarying", "(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/ByteBuffer;)V", (void *) android_glGetTransformFeedbackVarying__IIILjava_nio_IntBuffer_2Ljava_nio_IntBuffer_2Ljava_nio_IntBuffer_2Ljava_nio_ByteBuffer_2 }, {"glGetTransformFeedbackVarying", "(II[II[II)Ljava/lang/String;", (void *) android_glGetTransformFeedbackVarying1 }, {"glGetTransformFeedbackVarying", "(IILjava/nio/IntBuffer;Ljava/nio/IntBuffer;)Ljava/lang/String;", (void *) android_glGetTransformFeedbackVarying2 }, diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk index d84ce42cdf..ef1763055e 100644 --- a/services/sensorservice/Android.mk +++ b/services/sensorservice/Android.mk @@ -20,6 +20,8 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\" +LOCAL_CFLAGS += -Wall -Werror + LOCAL_CFLAGS += -fvisibility=hidden LOCAL_SHARED_LIBRARIES := \ diff --git a/services/sensorservice/Fusion.cpp b/services/sensorservice/Fusion.cpp index 359d289773..e92b23dab4 100644 --- a/services/sensorservice/Fusion.cpp +++ b/services/sensorservice/Fusion.cpp @@ -72,8 +72,6 @@ static const float SYMMETRY_TOLERANCE = 1e-10f; */ static const float NOMINAL_GRAVITY = 9.81f; static const float FREE_FALL_THRESHOLD = 0.1f * (NOMINAL_GRAVITY); -static const float FREE_FALL_THRESHOLD_SQ = - FREE_FALL_THRESHOLD*FREE_FALL_THRESHOLD; /* * The geomagnetic-field should be between 30uT and 60uT. @@ -104,7 +102,6 @@ static const float MIN_VALID_CROSS_PRODUCT_MAG = 1.0e-3; static const float MIN_VALID_CROSS_PRODUCT_MAG_SQ = MIN_VALID_CROSS_PRODUCT_MAG*MIN_VALID_CROSS_PRODUCT_MAG; -static const float W_EPS = 1e-4f; static const float SQRT_3 = 1.732f; static const float WVEC_EPS = 1e-4f/SQRT_3; // ----------------------------------------------------------------------- diff --git a/services/sensorservice/MostRecentEventLogger.cpp b/services/sensorservice/MostRecentEventLogger.cpp index 0bd0e17ff4..a5d8456b38 100644 --- a/services/sensorservice/MostRecentEventLogger.cpp +++ b/services/sensorservice/MostRecentEventLogger.cpp @@ -16,10 +16,12 @@ #include "MostRecentEventLogger.h" +#include <inttypes.h> + namespace android { SensorService::MostRecentEventLogger::MostRecentEventLogger(int sensorType) : - mSensorType(sensorType), mNextInd(0) { + mNextInd(0), mSensorType(sensorType) { mBufSize = (sensorType == SENSOR_TYPE_STEP_COUNTER || sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION || @@ -61,13 +63,13 @@ void SensorService::MostRecentEventLogger::printBuffer(String8& result) const { } result.appendFormat("%d) ", eventNum++); if (mSensorType == SENSOR_TYPE_STEP_COUNTER) { - result.appendFormat("%llu,", mTrimmedSensorEventArr[i]->mStepCounter); + result.appendFormat("%" PRIu64 ",", mTrimmedSensorEventArr[i]->mStepCounter); } else { for (int j = 0; j < numData; ++j) { result.appendFormat("%5.1f,", mTrimmedSensorEventArr[i]->mData[j]); } } - result.appendFormat("%lld %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp, + result.appendFormat("%" PRId64 " %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp, mTrimmedSensorEventArr[i]->mHour, mTrimmedSensorEventArr[i]->mMin, mTrimmedSensorEventArr[i]->mSec); i = (i + 1) % mBufSize; diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index d70b069c3b..8cc9fc175b 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -15,11 +15,11 @@ LOCAL_SRC_FILES := \ LayerDim.cpp \ MessageQueue.cpp \ MonitoredProducer.cpp \ - SurfaceFlinger.cpp \ SurfaceFlingerConsumer.cpp \ Transform.cpp \ DisplayHardware/FramebufferSurface.cpp \ - DisplayHardware/HWComposer.cpp \ + DisplayHardware/HWC2.cpp \ + DisplayHardware/HWC2On1Adapter.cpp \ DisplayHardware/PowerHAL.cpp \ DisplayHardware/VirtualDisplaySurface.cpp \ Effects/Daltonizer.cpp \ @@ -40,6 +40,19 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +USE_HWC2 := false +ifeq ($(USE_HWC2),true) + LOCAL_CFLAGS += -DUSE_HWC2 + LOCAL_SRC_FILES += \ + SurfaceFlinger.cpp \ + DisplayHardware/HWComposer.cpp +else + LOCAL_SRC_FILES += \ + SurfaceFlinger_hwc1.cpp \ + DisplayHardware/HWComposer_hwc1.cpp +endif + ifeq ($(TARGET_BOARD_PLATFORM),omap4) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY endif diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index bdf8f7404a..8d8e40d0da 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "DisplayDevice" + #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -33,6 +37,9 @@ #include "DisplayHardware/DisplaySurface.h" #include "DisplayHardware/HWComposer.h" +#ifdef USE_HWC2 +#include "DisplayHardware/HWC2.h" +#endif #include "RenderEngine/RenderEngine.h" #include "clz.h" @@ -65,7 +72,9 @@ DisplayDevice::DisplayDevice( const sp<SurfaceFlinger>& flinger, DisplayType type, int32_t hwcId, +#ifndef USE_HWC2 int format, +#endif bool isSecure, const wp<IBinder>& displayToken, const sp<DisplaySurface>& displaySurface, @@ -73,16 +82,20 @@ DisplayDevice::DisplayDevice( EGLConfig config) : lastCompositionHadVisibleLayers(false), mFlinger(flinger), - mType(type), mHwcDisplayId(hwcId), + mType(type), + mHwcDisplayId(hwcId), mDisplayToken(displayToken), mDisplaySurface(displaySurface), mDisplay(EGL_NO_DISPLAY), mSurface(EGL_NO_SURFACE), - mDisplayWidth(), mDisplayHeight(), mFormat(), + mDisplayWidth(), + mDisplayHeight(), +#ifndef USE_HWC2 + mFormat(), +#endif mFlags(), mPageFlipCount(), mIsSecure(isSecure), - mSecureLayerVisible(false), mLayerStack(NO_LAYER_STACK), mOrientation(), mPowerMode(HWC_POWER_MODE_OFF), @@ -99,7 +112,11 @@ DisplayDevice::DisplayDevice( EGLSurface eglSurface; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (config == EGL_NO_CONFIG) { +#ifdef USE_HWC2 + config = RenderEngine::chooseEglConfig(display, PIXEL_FORMAT_RGBA_8888); +#else config = RenderEngine::chooseEglConfig(display, format); +#endif } eglSurface = eglCreateWindowSurface(display, config, window, NULL); eglQuerySurface(display, eglSurface, EGL_WIDTH, &mDisplayWidth); @@ -118,7 +135,9 @@ DisplayDevice::DisplayDevice( mConfig = config; mDisplay = display; mSurface = eglSurface; - mFormat = format; +#ifndef USE_HWC2 + mFormat = format; +#endif mPageFlipCount = 0; mViewport.makeInvalid(); mFrame.makeInvalid(); @@ -159,8 +178,10 @@ DisplayDevice::~DisplayDevice() { void DisplayDevice::disconnect(HWComposer& hwc) { if (mHwcDisplayId >= 0) { hwc.disconnectDisplay(mHwcDisplayId); +#ifndef USE_HWC2 if (mHwcDisplayId >= DISPLAY_VIRTUAL) hwc.freeDisplayId(mHwcDisplayId); +#endif mHwcDisplayId = -1; } } @@ -177,9 +198,11 @@ int DisplayDevice::getHeight() const { return mDisplayHeight; } +#ifndef USE_HWC2 PixelFormat DisplayDevice::getFormat() const { return mFormat; } +#endif EGLSurface DisplayDevice::getEGLSurface() const { return mSurface; @@ -196,9 +219,11 @@ uint32_t DisplayDevice::getPageFlipCount() const { return mPageFlipCount; } +#ifndef USE_HWC2 status_t DisplayDevice::compositionComplete() const { return mDisplaySurface->compositionComplete(); } +#endif void DisplayDevice::flip(const Region& dirty) const { @@ -220,6 +245,31 @@ status_t DisplayDevice::beginFrame(bool mustRecompose) const { return mDisplaySurface->beginFrame(mustRecompose); } +#ifdef USE_HWC2 +status_t DisplayDevice::prepareFrame(HWComposer& hwc) { + status_t error = hwc.prepare(*this); + if (error != NO_ERROR) { + return error; + } + + DisplaySurface::CompositionType compositionType; + bool hasClient = hwc.hasClientComposition(mHwcDisplayId); + bool hasDevice = hwc.hasDeviceComposition(mHwcDisplayId); + if (hasClient && hasDevice) { + compositionType = DisplaySurface::COMPOSITION_MIXED; + } else if (hasClient) { + compositionType = DisplaySurface::COMPOSITION_GLES; + } else if (hasDevice) { + compositionType = DisplaySurface::COMPOSITION_HWC; + } else { + // Nothing to do -- when turning the screen off we get a frame like + // this. Call it a HWC frame since we won't be doing any GLES work but + // will do a prepare/set cycle. + compositionType = DisplaySurface::COMPOSITION_HWC; + } + return mDisplaySurface->prepareFrame(compositionType); +} +#else status_t DisplayDevice::prepareFrame(const HWComposer& hwc) const { DisplaySurface::CompositionType compositionType; bool haveGles = hwc.hasGlesComposition(mHwcDisplayId); @@ -238,8 +288,12 @@ status_t DisplayDevice::prepareFrame(const HWComposer& hwc) const { } return mDisplaySurface->prepareFrame(compositionType); } +#endif void DisplayDevice::swapBuffers(HWComposer& hwc) const { +#ifdef USE_HWC2 + if (hwc.hasClientComposition(mHwcDisplayId)) { +#else // We need to call eglSwapBuffers() if: // (1) we don't have a hardware composer, or // (2) we did GLES composition this frame, and either @@ -249,6 +303,7 @@ void DisplayDevice::swapBuffers(HWComposer& hwc) const { if (hwc.initCheck() != NO_ERROR || (hwc.hasGlesComposition(mHwcDisplayId) && (hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL))) { +#endif EGLBoolean success = eglSwapBuffers(mDisplay, mSurface); if (!success) { EGLint error = eglGetError(); @@ -270,11 +325,17 @@ void DisplayDevice::swapBuffers(HWComposer& hwc) const { } } +#ifdef USE_HWC2 +void DisplayDevice::onSwapBuffersCompleted() const { + mDisplaySurface->onFrameCommitted(); +} +#else void DisplayDevice::onSwapBuffersCompleted(HWComposer& hwc) const { if (hwc.initCheck() == NO_ERROR) { mDisplaySurface->onFrameCommitted(); } } +#endif uint32_t DisplayDevice::getFlags() const { @@ -303,28 +364,22 @@ void DisplayDevice::setViewportAndProjection() const { false, Transform::ROT_0); } +#ifdef USE_HWC2 +const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const { + return mDisplaySurface->getClientTargetAcquireFence(); +} +#endif + // ---------------------------------------------------------------------------- void DisplayDevice::setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers) { mVisibleLayersSortedByZ = layers; - mSecureLayerVisible = false; - size_t count = layers.size(); - for (size_t i=0 ; i<count ; i++) { - const sp<Layer>& layer(layers[i]); - if (layer->isSecure()) { - mSecureLayerVisible = true; - } - } } const Vector< sp<Layer> >& DisplayDevice::getVisibleLayersSortedByZ() const { return mVisibleLayersSortedByZ; } -bool DisplayDevice::getSecureLayerVisible() const { - return mSecureLayerVisible; -} - Region DisplayDevice::getDirtyRegion(bool repaintEverything) const { Region dirty; if (repaintEverything) { @@ -506,13 +561,13 @@ void DisplayDevice::dump(String8& result) const { result.appendFormat( "+ DisplayDevice: %s\n" " type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p, orient=%2d (type=%08x), " - "flips=%u, isSecure=%d, secureVis=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n" + "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n" " v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d]," "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n", mDisplayName.string(), mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight, mNativeWindow.get(), mOrientation, tr.getType(), getPageFlipCount(), - mIsSecure, mSecureLayerVisible, mPowerMode, mActiveConfig, + mIsSecure, mPowerMode, mActiveConfig, mVisibleLayersSortedByZ.size(), mViewport.left, mViewport.top, mViewport.right, mViewport.bottom, mFrame.left, mFrame.top, mFrame.right, mFrame.bottom, diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 8695a44eca..38241d994b 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -17,21 +17,31 @@ #ifndef ANDROID_DISPLAY_DEVICE_H #define ANDROID_DISPLAY_DEVICE_H +#include "Transform.h" + #include <stdlib.h> +#ifndef USE_HWC2 #include <ui/PixelFormat.h> +#endif #include <ui/Region.h> #include <EGL/egl.h> #include <EGL/eglext.h> +#ifdef USE_HWC2 +#include <binder/IBinder.h> +#include <utils/RefBase.h> +#endif #include <utils/Mutex.h> #include <utils/String8.h> #include <utils/Timers.h> #include <hardware/hwcomposer_defs.h> -#include "Transform.h" +#ifdef USE_HWC2 +#include <memory> +#endif struct ANativeWindow; @@ -39,6 +49,9 @@ namespace android { struct DisplayInfo; class DisplaySurface; +#ifdef USE_HWC2 +class Fence; +#endif class IGraphicBufferProducer; class Layer; class SurfaceFlinger; @@ -75,8 +88,10 @@ public: DisplayDevice( const sp<SurfaceFlinger>& flinger, DisplayType type, - int32_t hwcId, // negative for non-HWC-composited displays + int32_t hwcId, +#ifndef USE_HWC2 int format, +#endif bool isSecure, const wp<IBinder>& displayToken, const sp<DisplaySurface>& displaySurface, @@ -99,14 +114,15 @@ public: int getWidth() const; int getHeight() const; +#ifndef USE_HWC2 PixelFormat getFormat() const; +#endif uint32_t getFlags() const; EGLSurface getEGLSurface() const; void setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers); const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const; - bool getSecureLayerVisible() const; Region getDirtyRegion(bool repaintEverything) const; void setLayerStack(uint32_t stack); @@ -129,13 +145,23 @@ public: // We pass in mustRecompose so we can keep VirtualDisplaySurface's state // machine happy without actually queueing a buffer if nothing has changed status_t beginFrame(bool mustRecompose) const; +#ifdef USE_HWC2 + status_t prepareFrame(HWComposer& hwc); +#else status_t prepareFrame(const HWComposer& hwc) const; +#endif void swapBuffers(HWComposer& hwc) const; +#ifndef USE_HWC2 status_t compositionComplete() const; +#endif // called after h/w composer has completed its set() call +#ifdef USE_HWC2 + void onSwapBuffersCompleted() const; +#else void onSwapBuffersCompleted(HWComposer& hwc) const; +#endif Rect getBounds() const { return Rect(mDisplayWidth, mDisplayHeight); @@ -148,6 +174,10 @@ public: EGLBoolean makeCurrent(EGLDisplay dpy, EGLContext ctx) const; void setViewportAndProjection() const; +#ifdef USE_HWC2 + const sp<Fence>& getClientTargetAcquireFence() const; +#endif + /* ------------------------------------------------------------------------ * Display power mode management. */ @@ -188,7 +218,9 @@ private: EGLSurface mSurface; int mDisplayWidth; int mDisplayHeight; +#ifndef USE_HWC2 PixelFormat mFormat; +#endif uint32_t mFlags; mutable uint32_t mPageFlipCount; String8 mDisplayName; @@ -202,10 +234,6 @@ private: // list of visible layers on that display Vector< sp<Layer> > mVisibleLayersSortedByZ; - // Whether we have a visible secure layer on this display - bool mSecureLayerVisible; - - /* * Transaction state */ diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h index 2f743c1901..d819f8311b 100644 --- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h @@ -49,11 +49,13 @@ public: }; virtual status_t prepareFrame(CompositionType compositionType) = 0; +#ifndef USE_HWC2 // Should be called when composition rendering is complete for a frame (but // eglSwapBuffers hasn't necessarily been called). Required by certain // older drivers for synchronization. // TODO: Remove this when we drop support for HWC 1.0. virtual status_t compositionComplete() = 0; +#endif // Inform the surface that GLES composition is complete for this frame, and // the surface should make sure that HWComposer has the correct buffer for @@ -74,6 +76,10 @@ public: virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0; +#ifdef USE_HWC2 + virtual const sp<Fence>& getClientTargetAcquireFence() const = 0; +#endif + protected: DisplaySurface() {} virtual ~DisplaySurface() {} diff --git a/services/surfaceflinger/DisplayHardware/FloatRect.h b/services/surfaceflinger/DisplayHardware/FloatRect.h index 3b75cc05e6..9ad1040cd2 100644 --- a/services/surfaceflinger/DisplayHardware/FloatRect.h +++ b/services/surfaceflinger/DisplayHardware/FloatRect.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SF_FLOAT_RECT #define ANDROID_SF_FLOAT_RECT +#include <ui/Rect.h> #include <utils/TypeHelpers.h> namespace android { diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index a6bc158e9c..29e0766383 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -15,6 +15,10 @@ ** limitations under the License. */ +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FramebufferSurface" + #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -56,16 +60,35 @@ FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp, ConsumerBase(consumer), mDisplayType(disp), mCurrentBufferSlot(-1), +#ifdef USE_HWC2 + mCurrentBuffer(), + mCurrentFence(Fence::NO_FENCE), + mHwc(hwc), + mHasPendingRelease(false), + mPreviousBufferSlot(BufferQueue::INVALID_BUFFER_SLOT), + mPreviousBuffer() +#else mCurrentBuffer(0), mHwc(hwc) +#endif { +#ifdef USE_HWC2 + ALOGV("Creating for display %d", disp); +#endif + mName = "FramebufferSurface"; mConsumer->setConsumerName(mName); mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER); +#ifdef USE_HWC2 + const auto& activeConfig = mHwc.getActiveConfig(disp); + mConsumer->setDefaultBufferSize(activeConfig->getWidth(), + activeConfig->getHeight()); +#else mConsumer->setDefaultBufferFormat(mHwc.getFormat(disp)); - mConsumer->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp)); + mConsumer->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp)); +#endif mConsumer->setMaxAcquiredBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS - 1); } @@ -78,13 +101,35 @@ status_t FramebufferSurface::prepareFrame(CompositionType /*compositionType*/) { } status_t FramebufferSurface::advanceFrame() { +#ifdef USE_HWC2 + sp<GraphicBuffer> buf; + sp<Fence> acquireFence(Fence::NO_FENCE); + android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN; + status_t result = nextBuffer(buf, acquireFence, dataspace); + if (result != NO_ERROR) { + ALOGE("error latching next FramebufferSurface buffer: %s (%d)", + strerror(-result), result); + return result; + } + result = mHwc.setClientTarget(mDisplayType, acquireFence, buf, dataspace); + if (result != NO_ERROR) { + ALOGE("error posting framebuffer: %d", result); + } + return result; +#else // Once we remove FB HAL support, we can call nextBuffer() from here // instead of using onFrameAvailable(). No real benefit, except it'll be // more like VirtualDisplaySurface. return NO_ERROR; +#endif } +#ifdef USE_HWC2 +status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, + sp<Fence>& outFence, android_dataspace_t& outDataspace) { +#else status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) { +#endif Mutex::Autolock lock(mMutex); BufferItem item; @@ -107,6 +152,11 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& // had released the old buffer first. if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT && item.mSlot != mCurrentBufferSlot) { +#ifdef USE_HWC2 + mHasPendingRelease = true; + mPreviousBufferSlot = mCurrentBufferSlot; + mPreviousBuffer = mCurrentBuffer; +#else // Release the previous buffer. err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); @@ -114,14 +164,23 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& ALOGE("error releasing buffer: %s (%d)", strerror(-err), err); return err; } +#endif } mCurrentBufferSlot = item.mSlot; mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer; +#ifdef USE_HWC2 + mCurrentFence = item.mFence; +#endif + outFence = item.mFence; outBuffer = mCurrentBuffer; +#ifdef USE_HWC2 + outDataspace = item.mDataSpace; +#endif return NO_ERROR; } +#ifndef USE_HWC2 // Overrides ConsumerBase::onFrameAvailable(), does not call base class impl. void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) { sp<GraphicBuffer> buf; @@ -137,6 +196,7 @@ void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) { ALOGE("error posting framebuffer: %d", err); } } +#endif void FramebufferSurface::freeBufferLocked(int slotIndex) { ConsumerBase::freeBufferLocked(slotIndex); @@ -146,6 +206,24 @@ void FramebufferSurface::freeBufferLocked(int slotIndex) { } void FramebufferSurface::onFrameCommitted() { +#ifdef USE_HWC2 + if (mHasPendingRelease) { + sp<Fence> fence = mHwc.getRetireFence(mDisplayType); + if (fence->isValid()) { + status_t result = addReleaseFence(mPreviousBufferSlot, + mPreviousBuffer, fence); + ALOGE_IF(result != NO_ERROR, "onFrameCommitted: failed to add the" + " fence: %s (%d)", strerror(-result), result); + } + status_t result = releaseBufferLocked(mPreviousBufferSlot, + mPreviousBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + ALOGE_IF(result != NO_ERROR, "onFrameCommitted: error releasing buffer:" + " %s (%d)", strerror(-result), result); + + mPreviousBuffer.clear(); + mHasPendingRelease = false; + } +#else sp<Fence> fence = mHwc.getAndResetReleaseFence(mDisplayType); if (fence->isValid() && mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) { @@ -154,12 +232,15 @@ void FramebufferSurface::onFrameCommitted() { ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)", strerror(-err), err); } +#endif } +#ifndef USE_HWC2 status_t FramebufferSurface::compositionComplete() { return mHwc.fbCompositionComplete(); } +#endif void FramebufferSurface::dumpAsString(String8& result) const { ConsumerBase::dump(result); @@ -167,10 +248,18 @@ void FramebufferSurface::dumpAsString(String8& result) const { void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const { +#ifndef USE_HWC2 mHwc.fbDump(result); +#endif ConsumerBase::dumpLocked(result, prefix); } +#ifdef USE_HWC2 +const sp<Fence>& FramebufferSurface::getClientTargetAcquireFence() const { + return mCurrentFence; +} +#endif + // ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index 3d1784086e..0605602a10 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -41,7 +41,9 @@ public: virtual status_t beginFrame(bool mustRecompose); virtual status_t prepareFrame(CompositionType compositionType); +#ifndef USE_HWC2 virtual status_t compositionComplete(); +#endif virtual status_t advanceFrame(); virtual void onFrameCommitted(); virtual void dumpAsString(String8& result) const; @@ -50,10 +52,16 @@ public: // displays. virtual void resizeBuffers(const uint32_t /*w*/, const uint32_t /*h*/) { }; +#ifdef USE_HWC2 + virtual const sp<Fence>& getClientTargetAcquireFence() const override; +#endif + private: virtual ~FramebufferSurface() { }; // this class cannot be overloaded +#ifndef USE_HWC2 virtual void onFrameAvailable(const BufferItem& item); +#endif virtual void freeBufferLocked(int slotIndex); virtual void dumpLocked(String8& result, const char* prefix) const; @@ -61,7 +69,12 @@ private: // nextBuffer waits for and then latches the next buffer from the // BufferQueue and releases the previously latched buffer to the // BufferQueue. The new buffer is returned in the 'buffer' argument. +#ifdef USE_HWC2 + status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence, + android_dataspace_t& outDataspace); +#else status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence); +#endif // mDisplayType must match one of the HWC display types int mDisplayType; @@ -75,8 +88,20 @@ private: // no current buffer. sp<GraphicBuffer> mCurrentBuffer; +#ifdef USE_HWC2 + // mCurrentFence is the current buffer's acquire fence + sp<Fence> mCurrentFence; +#endif + // Hardware composer, owned by SurfaceFlinger. HWComposer& mHwc; + +#ifdef USE_HWC2 + // Previous buffer to release after getting an updated retire fence + bool mHasPendingRelease; + int mPreviousBufferSlot; + sp<GraphicBuffer> mPreviousBuffer; +#endif }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp new file mode 100644 index 0000000000..0e97a5382a --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -0,0 +1,992 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 + +#undef LOG_TAG +#define LOG_TAG "HWC2" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "HWC2.h" + +#include "FloatRect.h" + +#include <ui/Fence.h> +#include <ui/GraphicBuffer.h> +#include <ui/Region.h> + +#include <android/configuration.h> + +#include <inttypes.h> + +extern "C" { + static void hotplug_hook(hwc2_callback_data_t callbackData, + hwc2_display_t displayId, int32_t intConnected) { + auto device = static_cast<HWC2::Device*>(callbackData); + auto display = device->getDisplayById(displayId); + if (display) { + auto connected = static_cast<HWC2::Connection>(intConnected); + device->callHotplug(std::move(display), connected); + } else { + ALOGE("Hotplug callback called with unknown display %" PRIu64, + displayId); + } + } + + static void refresh_hook(hwc2_callback_data_t callbackData, + hwc2_display_t displayId) { + auto device = static_cast<HWC2::Device*>(callbackData); + auto display = device->getDisplayById(displayId); + if (display) { + device->callRefresh(std::move(display)); + } else { + ALOGE("Refresh callback called with unknown display %" PRIu64, + displayId); + } + } + + static void vsync_hook(hwc2_callback_data_t callbackData, + hwc2_display_t displayId, int64_t timestamp) { + auto device = static_cast<HWC2::Device*>(callbackData); + auto display = device->getDisplayById(displayId); + if (display) { + device->callVsync(std::move(display), timestamp); + } else { + ALOGE("Vsync callback called with unknown display %" PRIu64, + displayId); + } + } +} + +using android::Fence; +using android::FloatRect; +using android::GraphicBuffer; +using android::Rect; +using android::Region; +using android::sp; + +namespace HWC2 { + +// Device methods + +Device::Device(hwc2_device_t* device) + : mHwcDevice(device), + mCreateVirtualDisplay(nullptr), + mDestroyVirtualDisplay(nullptr), + mDump(nullptr), + mGetMaxVirtualDisplayCount(nullptr), + mRegisterCallback(nullptr), + mAcceptDisplayChanges(nullptr), + mCreateLayer(nullptr), + mDestroyLayer(nullptr), + mGetActiveConfig(nullptr), + mGetChangedCompositionTypes(nullptr), + mGetDisplayAttribute(nullptr), + mGetDisplayConfigs(nullptr), + mGetDisplayName(nullptr), + mGetDisplayRequests(nullptr), + mGetDisplayType(nullptr), + mGetDozeSupport(nullptr), + mGetReleaseFences(nullptr), + mPresentDisplay(nullptr), + mSetActiveConfig(nullptr), + mSetClientTarget(nullptr), + mSetOutputBuffer(nullptr), + mSetPowerMode(nullptr), + mSetVsyncEnabled(nullptr), + mValidateDisplay(nullptr), + mSetCursorPosition(nullptr), + mSetLayerBuffer(nullptr), + mSetLayerSurfaceDamage(nullptr), + mSetLayerBlendMode(nullptr), + mSetLayerColor(nullptr), + mSetLayerCompositionType(nullptr), + mSetLayerDisplayFrame(nullptr), + mSetLayerPlaneAlpha(nullptr), + mSetLayerSidebandStream(nullptr), + mSetLayerSourceCrop(nullptr), + mSetLayerTransform(nullptr), + mSetLayerVisibleRegion(nullptr), + mSetLayerZOrder(nullptr), + mCapabilities(), + mDisplays(), + mHotplug(), + mPendingHotplugs(), + mRefresh(), + mPendingRefreshes(), + mVsync(), + mPendingVsyncs() +{ + loadCapabilities(); + loadFunctionPointers(); + registerCallbacks(); +} + +Device::~Device() +{ + if (mHwcDevice == nullptr) { + return; + } + + for (auto element : mDisplays) { + auto display = element.second; + + DisplayType displayType = HWC2::DisplayType::Invalid; + auto error = display->getType(&displayType); + if (error != Error::None) { + ALOGE("~Device: Failed to determine type of display %" PRIu64 + ": %s (%d)", display->getId(), to_string(error).c_str(), + static_cast<int32_t>(error)); + continue; + } + + if (displayType == HWC2::DisplayType::Physical) { + error = display->setVsyncEnabled(HWC2::Vsync::Disable); + if (error != Error::None) { + ALOGE("~Device: Failed to disable vsync for display %" PRIu64 + ": %s (%d)", display->getId(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + } + } + + hwc2_close(mHwcDevice); +} + +// Required by HWC2 device + +std::string Device::dump() const +{ + uint32_t numBytes = 0; + mDump(mHwcDevice, &numBytes, nullptr); + + std::vector<char> buffer(numBytes); + mDump(mHwcDevice, &numBytes, buffer.data()); + + return std::string(buffer.data(), buffer.size()); +} + +uint32_t Device::getMaxVirtualDisplayCount() const +{ + return mGetMaxVirtualDisplayCount(mHwcDevice); +} + +Error Device::createVirtualDisplay(uint32_t width, uint32_t height, + std::shared_ptr<Display>* outDisplay) +{ + ALOGI("Creating virtual display"); + + hwc2_display_t displayId = 0; + int32_t intError = mCreateVirtualDisplay(mHwcDevice, width, height, + &displayId); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + ALOGI("Created virtual display"); + *outDisplay = getDisplayById(displayId); + (*outDisplay)->setVirtual(); + return Error::None; +} + +void Device::registerHotplugCallback(HotplugCallback hotplug) +{ + ALOGV("registerHotplugCallback"); + mHotplug = hotplug; + for (auto& pending : mPendingHotplugs) { + auto& display = pending.first; + auto connected = pending.second; + ALOGV("Sending pending hotplug(%" PRIu64 ", %s)", display->getId(), + to_string(connected).c_str()); + mHotplug(std::move(display), connected); + } +} + +void Device::registerRefreshCallback(RefreshCallback refresh) +{ + mRefresh = refresh; + for (auto& pending : mPendingRefreshes) { + mRefresh(std::move(pending)); + } +} + +void Device::registerVsyncCallback(VsyncCallback vsync) +{ + mVsync = vsync; + for (auto& pending : mPendingVsyncs) { + auto& display = pending.first; + auto timestamp = pending.second; + mVsync(std::move(display), timestamp); + } +} + +// For use by Device callbacks + +void Device::callHotplug(std::shared_ptr<Display> display, Connection connected) +{ + if (connected == Connection::Connected) { + if (!display->isConnected()) { + display->loadConfigs(); + display->setConnected(true); + } + } else { + display->setConnected(false); + mDisplays.erase(display->getId()); + } + + if (mHotplug) { + mHotplug(std::move(display), connected); + } else { + ALOGV("callHotplug called, but no valid callback registered, storing"); + mPendingHotplugs.emplace_back(std::move(display), connected); + } +} + +void Device::callRefresh(std::shared_ptr<Display> display) +{ + if (mRefresh) { + mRefresh(std::move(display)); + } else { + ALOGV("callRefresh called, but no valid callback registered, storing"); + mPendingRefreshes.emplace_back(std::move(display)); + } +} + +void Device::callVsync(std::shared_ptr<Display> display, nsecs_t timestamp) +{ + if (mVsync) { + mVsync(std::move(display), timestamp); + } else { + ALOGV("callVsync called, but no valid callback registered, storing"); + mPendingVsyncs.emplace_back(std::move(display), timestamp); + } +} + +// Other Device methods + +std::shared_ptr<Display> Device::getDisplayById(hwc2_display_t id) { + if (mDisplays.count(id) != 0) { + return mDisplays.at(id); + } + + auto display = std::make_shared<Display>(*this, id); + mDisplays.emplace(id, display); + return display; +} + +// Device initialization methods + +void Device::loadCapabilities() +{ + static_assert(sizeof(Capability) == sizeof(int32_t), + "Capability size has changed"); + uint32_t numCapabilities = 0; + mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr); + mCapabilities.resize(numCapabilities); + auto asInt = reinterpret_cast<int32_t*>(mCapabilities.data()); + mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt); +} + +void Device::loadFunctionPointers() +{ + // For all of these early returns, we log an error message inside + // loadFunctionPointer specifying which function failed to load + + // Display function pointers + if(!loadFunctionPointer(FunctionDescriptor::CreateVirtualDisplay, + mCreateVirtualDisplay)) return; + if(!loadFunctionPointer(FunctionDescriptor::DestroyVirtualDisplay, + mDestroyVirtualDisplay)) return; + if(!loadFunctionPointer(FunctionDescriptor::Dump, mDump)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetMaxVirtualDisplayCount, + mGetMaxVirtualDisplayCount)) return; + if(!loadFunctionPointer(FunctionDescriptor::RegisterCallback, + mRegisterCallback)) return; + + // Device function pointers + if(!loadFunctionPointer(FunctionDescriptor::AcceptDisplayChanges, + mAcceptDisplayChanges)) return; + if(!loadFunctionPointer(FunctionDescriptor::CreateLayer, + mCreateLayer)) return; + if(!loadFunctionPointer(FunctionDescriptor::DestroyLayer, + mDestroyLayer)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetActiveConfig, + mGetActiveConfig)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetChangedCompositionTypes, + mGetChangedCompositionTypes)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetDisplayAttribute, + mGetDisplayAttribute)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetDisplayConfigs, + mGetDisplayConfigs)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetDisplayName, + mGetDisplayName)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetDisplayRequests, + mGetDisplayRequests)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetDisplayType, + mGetDisplayType)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetDozeSupport, + mGetDozeSupport)) return; + if(!loadFunctionPointer(FunctionDescriptor::GetReleaseFences, + mGetReleaseFences)) return; + if(!loadFunctionPointer(FunctionDescriptor::PresentDisplay, + mPresentDisplay)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetActiveConfig, + mSetActiveConfig)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetClientTarget, + mSetClientTarget)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetOutputBuffer, + mSetOutputBuffer)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetPowerMode, + mSetPowerMode)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetVsyncEnabled, + mSetVsyncEnabled)) return; + if(!loadFunctionPointer(FunctionDescriptor::ValidateDisplay, + mValidateDisplay)) return; + + // Layer function pointers + if(!loadFunctionPointer(FunctionDescriptor::SetCursorPosition, + mSetCursorPosition)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerBuffer, + mSetLayerBuffer)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerSurfaceDamage, + mSetLayerSurfaceDamage)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerBlendMode, + mSetLayerBlendMode)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerColor, + mSetLayerColor)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerCompositionType, + mSetLayerCompositionType)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerDisplayFrame, + mSetLayerDisplayFrame)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha, + mSetLayerPlaneAlpha)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream, + mSetLayerSidebandStream)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerSourceCrop, + mSetLayerSourceCrop)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerTransform, + mSetLayerTransform)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerVisibleRegion, + mSetLayerVisibleRegion)) return; + if(!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder, + mSetLayerZOrder)) return; +} + +void Device::registerCallbacks() +{ + registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook); + registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook); + registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook); +} + + +// For use by Display + +void Device::destroyVirtualDisplay(hwc2_display_t display) +{ + ALOGI("Destroying virtual display"); + int32_t intError = mDestroyVirtualDisplay(mHwcDevice, display); + auto error = static_cast<Error>(intError); + ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:" + " %s (%d)", display, to_string(error).c_str(), intError); +} + +// Display methods + +Display::Display(Device& device, hwc2_display_t id) + : mDevice(device), + mId(id), + mIsConnected(false), + mIsVirtual(false) +{ + ALOGV("Created display %" PRIu64, id); +} + +Display::~Display() +{ + ALOGV("Destroyed display %" PRIu64, mId); + if (mIsVirtual) { + mDevice.destroyVirtualDisplay(mId); + } +} + +Display::Config::Config(Display& display, hwc2_config_t id) + : mDisplay(display), + mId(id), + mWidth(-1), + mHeight(-1), + mVsyncPeriod(-1), + mDpiX(-1), + mDpiY(-1) {} + +Display::Config::Builder::Builder(Display& display, hwc2_config_t id) + : mConfig(new Config(display, id)) {} + +float Display::Config::Builder::getDefaultDensity() { + // Default density is based on TVs: 1080p displays get XHIGH density, lower- + // resolution displays get TV density. Maybe eventually we'll need to update + // it for 4k displays, though hopefully those will just report accurate DPI + // information to begin with. This is also used for virtual displays and + // older HWC implementations, so be careful about orientation. + + auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight); + if (longDimension >= 1080) { + return ACONFIGURATION_DENSITY_XHIGH; + } else { + return ACONFIGURATION_DENSITY_TV; + } +} + +// Required by HWC2 display + +Error Display::acceptChanges() +{ + int32_t intError = mDevice.mAcceptDisplayChanges(mDevice.mHwcDevice, mId); + return static_cast<Error>(intError); +} + +Error Display::createLayer(std::shared_ptr<Layer>* outLayer) +{ + hwc2_layer_t layerId = 0; + int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + auto layer = std::make_shared<Layer>(shared_from_this(), layerId); + mLayers.emplace(layerId, layer); + *outLayer = std::move(layer); + return Error::None; +} + +Error Display::getActiveConfig( + std::shared_ptr<const Display::Config>* outConfig) const +{ + ALOGV("[%" PRIu64 "] getActiveConfig", mId); + hwc2_config_t configId = 0; + int32_t intError = mDevice.mGetActiveConfig(mDevice.mHwcDevice, mId, + &configId); + auto error = static_cast<Error>(intError); + + if (error != Error::None) { + return error; + } + + if (mConfigs.count(configId) != 0) { + *outConfig = mConfigs.at(configId); + } else { + ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, + configId); + // Return no error, but the caller needs to check for a null pointer to + // detect this case + *outConfig = nullptr; + } + + return Error::None; +} + +Error Display::getChangedCompositionTypes( + std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes) +{ + uint32_t numElements = 0; + int32_t intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice, + mId, &numElements, nullptr, nullptr); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + std::vector<hwc2_layer_t> layerIds(numElements); + std::vector<int32_t> types(numElements); + intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice, mId, + &numElements, layerIds.data(), types.data()); + error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + outTypes->clear(); + outTypes->reserve(numElements); + for (uint32_t element = 0; element < numElements; ++element) { + auto layer = getLayerById(layerIds[element]); + if (layer) { + auto type = static_cast<Composition>(types[element]); + ALOGV("getChangedCompositionTypes: adding %" PRIu64 " %s", + layer->getId(), to_string(type).c_str()); + outTypes->emplace(layer, type); + } else { + ALOGE("getChangedCompositionTypes: invalid layer %" PRIu64 " found" + " on display %" PRIu64, layerIds[element], mId); + } + } + + return Error::None; +} + +std::vector<std::shared_ptr<const Display::Config>> Display::getConfigs() const +{ + std::vector<std::shared_ptr<const Config>> configs; + for (const auto& element : mConfigs) { + configs.emplace_back(element.second); + } + return configs; +} + +Error Display::getName(std::string* outName) const +{ + uint32_t size; + int32_t intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size, + nullptr); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + std::vector<char> rawName(size); + intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size, + rawName.data()); + error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + *outName = std::string(rawName.cbegin(), rawName.cend()); + return Error::None; +} + +Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests, + std::unordered_map<std::shared_ptr<Layer>, LayerRequest>* + outLayerRequests) +{ + int32_t intDisplayRequests = 0; + uint32_t numElements = 0; + int32_t intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId, + &intDisplayRequests, &numElements, nullptr, nullptr); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + std::vector<hwc2_layer_t> layerIds(numElements); + std::vector<int32_t> layerRequests(numElements); + intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId, + &intDisplayRequests, &numElements, layerIds.data(), + layerRequests.data()); + error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + *outDisplayRequests = static_cast<DisplayRequest>(intDisplayRequests); + outLayerRequests->clear(); + outLayerRequests->reserve(numElements); + for (uint32_t element = 0; element < numElements; ++element) { + auto layer = getLayerById(layerIds[element]); + if (layer) { + auto layerRequest = + static_cast<LayerRequest>(layerRequests[element]); + outLayerRequests->emplace(layer, layerRequest); + } else { + ALOGE("getRequests: invalid layer %" PRIu64 " found on display %" + PRIu64, layerIds[element], mId); + } + } + + return Error::None; +} + +Error Display::getType(DisplayType* outType) const +{ + int32_t intType = 0; + int32_t intError = mDevice.mGetDisplayType(mDevice.mHwcDevice, mId, + &intType); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + *outType = static_cast<DisplayType>(intType); + return Error::None; +} + +Error Display::supportsDoze(bool* outSupport) const +{ + int32_t intSupport = 0; + int32_t intError = mDevice.mGetDozeSupport(mDevice.mHwcDevice, mId, + &intSupport); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + *outSupport = static_cast<bool>(intSupport); + return Error::None; +} + +Error Display::getReleaseFences( + std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const +{ + uint32_t numElements = 0; + int32_t intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId, + &numElements, nullptr, nullptr); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + std::vector<hwc2_layer_t> layerIds(numElements); + std::vector<int32_t> fenceFds(numElements); + intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId, &numElements, + layerIds.data(), fenceFds.data()); + error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + std::unordered_map<std::shared_ptr<Layer>, sp<Fence>> releaseFences; + releaseFences.reserve(numElements); + for (uint32_t element = 0; element < numElements; ++element) { + auto layer = getLayerById(layerIds[element]); + if (layer) { + sp<Fence> fence(new Fence(fenceFds[element])); + releaseFences.emplace(std::move(layer), fence); + } else { + ALOGE("getReleaseFences: invalid layer %" PRIu64 + " found on display %" PRIu64, layerIds[element], mId); + return Error::BadLayer; + } + } + + *outFences = std::move(releaseFences); + return Error::None; +} + +Error Display::present(sp<Fence>* outRetireFence) +{ + int32_t retireFenceFd = 0; + int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId, + &retireFenceFd); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + return error; + } + + *outRetireFence = new Fence(retireFenceFd); + return Error::None; +} + +Error Display::setActiveConfig(const std::shared_ptr<const Config>& config) +{ + if (config->getDisplayId() != mId) { + ALOGE("setActiveConfig received config %u for the wrong display %" + PRIu64 " (expected %" PRIu64 ")", config->getId(), + config->getDisplayId(), mId); + return Error::BadConfig; + } + int32_t intError = mDevice.mSetActiveConfig(mDevice.mHwcDevice, mId, + config->getId()); + return static_cast<Error>(intError); +} + +Error Display::setClientTarget(buffer_handle_t target, + const sp<Fence>& acquireFence, android_dataspace_t dataspace) +{ + int32_t fenceFd = acquireFence->dup(); + int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target, + fenceFd, static_cast<int32_t>(dataspace)); + return static_cast<Error>(intError); +} + +Error Display::setOutputBuffer(const sp<GraphicBuffer>& buffer, + const sp<Fence>& releaseFence) +{ + int32_t fenceFd = releaseFence->dup(); + auto handle = buffer->getNativeBuffer()->handle; + int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle, + fenceFd); + return static_cast<Error>(intError); +} + +Error Display::setPowerMode(PowerMode mode) +{ + auto intMode = static_cast<int32_t>(mode); + int32_t intError = mDevice.mSetPowerMode(mDevice.mHwcDevice, mId, intMode); + return static_cast<Error>(intError); +} + +Error Display::setVsyncEnabled(Vsync enabled) +{ + auto intEnabled = static_cast<int32_t>(enabled); + int32_t intError = mDevice.mSetVsyncEnabled(mDevice.mHwcDevice, mId, + intEnabled); + return static_cast<Error>(intError); +} + +Error Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests) +{ + uint32_t numTypes = 0; + uint32_t numRequests = 0; + int32_t intError = mDevice.mValidateDisplay(mDevice.mHwcDevice, mId, + &numTypes, &numRequests); + auto error = static_cast<Error>(intError); + if (error != Error::None && error != Error::HasChanges) { + return error; + } + + *outNumTypes = numTypes; + *outNumRequests = numRequests; + return error; +} + +// For use by Device + +int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute) +{ + int32_t value = 0; + int32_t intError = mDevice.mGetDisplayAttribute(mDevice.mHwcDevice, mId, + configId, static_cast<int32_t>(attribute), &value); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId, + configId, to_string(attribute).c_str(), + to_string(error).c_str(), intError); + return -1; + } + return value; +} + +void Display::loadConfig(hwc2_config_t configId) +{ + ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId); + + auto config = Config::Builder(*this, configId) + .setWidth(getAttribute(configId, Attribute::Width)) + .setHeight(getAttribute(configId, Attribute::Height)) + .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod)) + .setDpiX(getAttribute(configId, Attribute::DpiX)) + .setDpiY(getAttribute(configId, Attribute::DpiY)) + .build(); + mConfigs.emplace(configId, std::move(config)); +} + +void Display::loadConfigs() +{ + ALOGV("[%" PRIu64 "] loadConfigs", mId); + + uint32_t numConfigs = 0; + int32_t intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId, + &numConfigs, nullptr); + auto error = static_cast<Error>(intError); + if (error != Error::None) { + ALOGE("[%" PRIu64 "] getDisplayConfigs [1] failed: %s (%d)", mId, + to_string(error).c_str(), intError); + return; + } + + std::vector<hwc2_config_t> configIds(numConfigs); + intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId, &numConfigs, + configIds.data()); + error = static_cast<Error>(intError); + if (error != Error::None) { + ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId, + to_string(error).c_str(), intError); + return; + } + + for (auto configId : configIds) { + loadConfig(configId); + } +} + +// For use by Layer + +void Display::destroyLayer(hwc2_layer_t layerId) +{ + int32_t intError = mDevice.mDestroyLayer(mDevice.mHwcDevice, mId, layerId); + auto error = static_cast<Error>(intError); + ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")" + " failed: %s (%d)", mId, layerId, to_string(error).c_str(), + intError); + mLayers.erase(layerId); +} + +// Other Display methods + +std::shared_ptr<Layer> Display::getLayerById(hwc2_layer_t id) const +{ + if (mLayers.count(id) == 0) { + return nullptr; + } + + auto layer = mLayers.at(id).lock(); + return layer; +} + +// Layer methods + +Layer::Layer(const std::shared_ptr<Display>& display, hwc2_layer_t id) + : mDisplay(display), + mDisplayId(display->getId()), + mDevice(display->getDevice()), + mId(id) +{ + ALOGV("Created layer %" PRIu64 " on display %" PRIu64, id, + display->getId()); +} + +Layer::~Layer() +{ + auto display = mDisplay.lock(); + if (display) { + display->destroyLayer(mId); + } +} + +Error Layer::setCursorPosition(int32_t x, int32_t y) +{ + int32_t intError = mDevice.mSetCursorPosition(mDevice.mHwcDevice, + mDisplayId, mId, x, y); + return static_cast<Error>(intError); +} + +Error Layer::setBuffer(buffer_handle_t buffer, + const sp<Fence>& acquireFence) +{ + int32_t fenceFd = acquireFence->dup(); + int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId, + mId, buffer, fenceFd); + return static_cast<Error>(intError); +} + +Error Layer::setSurfaceDamage(const Region& damage) +{ + // We encode default full-screen damage as INVALID_RECT upstream, but as 0 + // rects for HWC + int32_t intError = 0; + if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) { + intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice, + mDisplayId, mId, {0, nullptr}); + } else { + size_t rectCount = 0; + auto rectArray = damage.getArray(&rectCount); + + std::vector<hwc_rect_t> hwcRects; + for (size_t rect = 0; rect < rectCount; ++rect) { + hwcRects.push_back({rectArray[rect].left, rectArray[rect].top, + rectArray[rect].right, rectArray[rect].bottom}); + } + + hwc_region_t hwcRegion = {}; + hwcRegion.numRects = rectCount; + hwcRegion.rects = hwcRects.data(); + + intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice, + mDisplayId, mId, hwcRegion); + } + + return static_cast<Error>(intError); +} + +Error Layer::setBlendMode(BlendMode mode) +{ + auto intMode = static_cast<int32_t>(mode); + int32_t intError = mDevice.mSetLayerBlendMode(mDevice.mHwcDevice, + mDisplayId, mId, intMode); + return static_cast<Error>(intError); +} + +Error Layer::setColor(hwc_color_t color) +{ + int32_t intError = mDevice.mSetLayerColor(mDevice.mHwcDevice, mDisplayId, + mId, color); + return static_cast<Error>(intError); +} + +Error Layer::setCompositionType(Composition type) +{ + auto intType = static_cast<int32_t>(type); + int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice, + mDisplayId, mId, intType); + return static_cast<Error>(intError); +} + +Error Layer::setDisplayFrame(const Rect& frame) +{ + hwc_rect_t hwcRect{frame.left, frame.top, frame.right, frame.bottom}; + int32_t intError = mDevice.mSetLayerDisplayFrame(mDevice.mHwcDevice, + mDisplayId, mId, hwcRect); + return static_cast<Error>(intError); +} + +Error Layer::setPlaneAlpha(float alpha) +{ + int32_t intError = mDevice.mSetLayerPlaneAlpha(mDevice.mHwcDevice, + mDisplayId, mId, alpha); + return static_cast<Error>(intError); +} + +Error Layer::setSidebandStream(const native_handle_t* stream) +{ + int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice, + mDisplayId, mId, stream); + return static_cast<Error>(intError); +} + +Error Layer::setSourceCrop(const FloatRect& crop) +{ + hwc_frect_t hwcRect{crop.left, crop.top, crop.right, crop.bottom}; + int32_t intError = mDevice.mSetLayerSourceCrop(mDevice.mHwcDevice, + mDisplayId, mId, hwcRect); + return static_cast<Error>(intError); +} + +Error Layer::setTransform(Transform transform) +{ + auto intTransform = static_cast<int32_t>(transform); + int32_t intError = mDevice.mSetLayerTransform(mDevice.mHwcDevice, + mDisplayId, mId, intTransform); + return static_cast<Error>(intError); +} + +Error Layer::setVisibleRegion(const Region& region) +{ + size_t rectCount = 0; + auto rectArray = region.getArray(&rectCount); + + std::vector<hwc_rect_t> hwcRects; + for (size_t rect = 0; rect < rectCount; ++rect) { + hwcRects.push_back({rectArray[rect].left, rectArray[rect].top, + rectArray[rect].right, rectArray[rect].bottom}); + } + + hwc_region_t hwcRegion = {}; + hwcRegion.numRects = rectCount; + hwcRegion.rects = hwcRects.data(); + + int32_t intError = mDevice.mSetLayerVisibleRegion(mDevice.mHwcDevice, + mDisplayId, mId, hwcRegion); + return static_cast<Error>(intError); +} + +Error Layer::setZOrder(uint32_t z) +{ + int32_t intError = mDevice.mSetLayerZOrder(mDevice.mHwcDevice, mDisplayId, + mId, z); + return static_cast<Error>(intError); +} + +} // namespace HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h new file mode 100644 index 0000000000..a7bd28c2e7 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -0,0 +1,377 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SF_HWC2_H +#define ANDROID_SF_HWC2_H + +#define HWC2_INCLUDE_STRINGIFICATION +#define HWC2_USE_CPP11 +#include <hardware/hwcomposer2.h> +#undef HWC2_INCLUDE_STRINGIFICATION +#undef HWC2_USE_CPP11 + +#include <utils/Log.h> +#include <utils/StrongPointer.h> +#include <utils/Timers.h> + +#include <functional> +#include <string> +#include <unordered_map> +#include <vector> + +namespace android { + class Fence; + class FloatRect; + class GraphicBuffer; + class Rect; + class Region; +} + +namespace HWC2 { + +class Display; +class Layer; + +typedef std::function<void(std::shared_ptr<Display>, Connection)> + HotplugCallback; +typedef std::function<void(std::shared_ptr<Display>)> RefreshCallback; +typedef std::function<void(std::shared_ptr<Display>, nsecs_t)> VsyncCallback; + +class Device +{ +public: + Device(hwc2_device_t* device); + ~Device(); + + friend class HWC2::Display; + friend class HWC2::Layer; + + // Required by HWC2 + + std::string dump() const; + + const std::vector<Capability>& getCapabilities() const { + return mCapabilities; + }; + + uint32_t getMaxVirtualDisplayCount() const; + Error createVirtualDisplay(uint32_t width, uint32_t height, + std::shared_ptr<Display>* outDisplay); + + void registerHotplugCallback(HotplugCallback hotplug); + void registerRefreshCallback(RefreshCallback refresh); + void registerVsyncCallback(VsyncCallback vsync); + + // For use by callbacks + + void callHotplug(std::shared_ptr<Display> display, Connection connected); + void callRefresh(std::shared_ptr<Display> display); + void callVsync(std::shared_ptr<Display> display, nsecs_t timestamp); + + // Other Device methods + + // This will create a Display if one is not found, but it will not be marked + // as connected + std::shared_ptr<Display> getDisplayById(hwc2_display_t id); + +private: + // Initialization methods + + template <typename PFN> + [[clang::warn_unused_result]] bool loadFunctionPointer( + FunctionDescriptor desc, PFN& outPFN) { + auto intDesc = static_cast<int32_t>(desc); + auto pfn = mHwcDevice->getFunction(mHwcDevice, intDesc); + if (pfn != nullptr) { + outPFN = reinterpret_cast<PFN>(pfn); + return true; + } else { + ALOGE("Failed to load function %s", to_string(desc).c_str()); + return false; + } + } + + template <typename PFN, typename HOOK> + void registerCallback(Callback callback, HOOK hook) { + static_assert(std::is_same<PFN, HOOK>::value, + "Incompatible function pointer"); + auto intCallback = static_cast<int32_t>(callback); + auto callbackData = static_cast<hwc2_callback_data_t>(this); + auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook); + mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn); + } + + void loadCapabilities(); + void loadFunctionPointers(); + void registerCallbacks(); + + // For use by Display + + void destroyVirtualDisplay(hwc2_display_t display); + + // Member variables + + hwc2_device_t* mHwcDevice; + + // Device function pointers + HWC2_PFN_CREATE_VIRTUAL_DISPLAY mCreateVirtualDisplay; + HWC2_PFN_DESTROY_VIRTUAL_DISPLAY mDestroyVirtualDisplay; + HWC2_PFN_DUMP mDump; + HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT mGetMaxVirtualDisplayCount; + HWC2_PFN_REGISTER_CALLBACK mRegisterCallback; + + // Display function pointers + HWC2_PFN_ACCEPT_DISPLAY_CHANGES mAcceptDisplayChanges; + HWC2_PFN_CREATE_LAYER mCreateLayer; + HWC2_PFN_DESTROY_LAYER mDestroyLayer; + HWC2_PFN_GET_ACTIVE_CONFIG mGetActiveConfig; + HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES mGetChangedCompositionTypes; + HWC2_PFN_GET_DISPLAY_ATTRIBUTE mGetDisplayAttribute; + HWC2_PFN_GET_DISPLAY_CONFIGS mGetDisplayConfigs; + HWC2_PFN_GET_DISPLAY_NAME mGetDisplayName; + HWC2_PFN_GET_DISPLAY_REQUESTS mGetDisplayRequests; + HWC2_PFN_GET_DISPLAY_TYPE mGetDisplayType; + HWC2_PFN_GET_DOZE_SUPPORT mGetDozeSupport; + HWC2_PFN_GET_RELEASE_FENCES mGetReleaseFences; + HWC2_PFN_PRESENT_DISPLAY mPresentDisplay; + HWC2_PFN_SET_ACTIVE_CONFIG mSetActiveConfig; + HWC2_PFN_SET_CLIENT_TARGET mSetClientTarget; + HWC2_PFN_SET_OUTPUT_BUFFER mSetOutputBuffer; + HWC2_PFN_SET_POWER_MODE mSetPowerMode; + HWC2_PFN_SET_VSYNC_ENABLED mSetVsyncEnabled; + HWC2_PFN_VALIDATE_DISPLAY mValidateDisplay; + + // Layer function pointers + HWC2_PFN_SET_CURSOR_POSITION mSetCursorPosition; + HWC2_PFN_SET_LAYER_BUFFER mSetLayerBuffer; + HWC2_PFN_SET_LAYER_SURFACE_DAMAGE mSetLayerSurfaceDamage; + HWC2_PFN_SET_LAYER_BLEND_MODE mSetLayerBlendMode; + HWC2_PFN_SET_LAYER_COLOR mSetLayerColor; + HWC2_PFN_SET_LAYER_COMPOSITION_TYPE mSetLayerCompositionType; + HWC2_PFN_SET_LAYER_DISPLAY_FRAME mSetLayerDisplayFrame; + HWC2_PFN_SET_LAYER_PLANE_ALPHA mSetLayerPlaneAlpha; + HWC2_PFN_SET_LAYER_SIDEBAND_STREAM mSetLayerSidebandStream; + HWC2_PFN_SET_LAYER_SOURCE_CROP mSetLayerSourceCrop; + HWC2_PFN_SET_LAYER_TRANSFORM mSetLayerTransform; + HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion; + HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder; + + std::vector<Capability> mCapabilities; + std::unordered_map<hwc2_display_t, std::shared_ptr<Display>> mDisplays; + + HotplugCallback mHotplug; + std::vector<std::pair<std::shared_ptr<Display>, Connection>> + mPendingHotplugs; + RefreshCallback mRefresh; + std::vector<std::shared_ptr<Display>> mPendingRefreshes; + VsyncCallback mVsync; + std::vector<std::pair<std::shared_ptr<Display>, nsecs_t>> mPendingVsyncs; +}; + +class Display : public std::enable_shared_from_this<Display> +{ +public: + Display(Device& device, hwc2_display_t id); + ~Display(); + + friend class HWC2::Device; + friend class HWC2::Layer; + + class Config + { + public: + class Builder + { + public: + Builder(Display& display, hwc2_config_t id); + + std::shared_ptr<const Config> build() { + return std::const_pointer_cast<const Config>( + std::move(mConfig)); + } + + Builder& setWidth(int32_t width) { + mConfig->mWidth = width; + return *this; + } + Builder& setHeight(int32_t height) { + mConfig->mHeight = height; + return *this; + } + Builder& setVsyncPeriod(int32_t vsyncPeriod) { + mConfig->mVsyncPeriod = vsyncPeriod; + return *this; + } + Builder& setDpiX(int32_t dpiX) { + if (dpiX == -1) { + mConfig->mDpiX = getDefaultDensity(); + } else { + mConfig->mDpiX = dpiX / 1000.0f; + } + return *this; + } + Builder& setDpiY(int32_t dpiY) { + if (dpiY == -1) { + mConfig->mDpiY = getDefaultDensity(); + } else { + mConfig->mDpiY = dpiY / 1000.0f; + } + return *this; + } + + private: + float getDefaultDensity(); + std::shared_ptr<Config> mConfig; + }; + + hwc2_display_t getDisplayId() const { return mDisplay.getId(); } + hwc2_config_t getId() const { return mId; } + + int32_t getWidth() const { return mWidth; } + int32_t getHeight() const { return mHeight; } + nsecs_t getVsyncPeriod() const { return mVsyncPeriod; } + float getDpiX() const { return mDpiX; } + float getDpiY() const { return mDpiY; } + + private: + Config(Display& display, hwc2_config_t id); + + Display& mDisplay; + hwc2_config_t mId; + + int32_t mWidth; + int32_t mHeight; + nsecs_t mVsyncPeriod; + float mDpiX; + float mDpiY; + }; + + // Required by HWC2 + + [[clang::warn_unused_result]] Error acceptChanges(); + [[clang::warn_unused_result]] Error createLayer( + std::shared_ptr<Layer>* outLayer); + [[clang::warn_unused_result]] Error getActiveConfig( + std::shared_ptr<const Config>* outConfig) const; + [[clang::warn_unused_result]] Error getChangedCompositionTypes( + std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes); + + // Doesn't call into the HWC2 device, so no errors are possible + std::vector<std::shared_ptr<const Config>> getConfigs() const; + + [[clang::warn_unused_result]] Error getName(std::string* outName) const; + [[clang::warn_unused_result]] Error getRequests( + DisplayRequest* outDisplayRequests, + std::unordered_map<std::shared_ptr<Layer>, LayerRequest>* + outLayerRequests); + [[clang::warn_unused_result]] Error getType(DisplayType* outType) const; + [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const; + [[clang::warn_unused_result]] Error getReleaseFences( + std::unordered_map<std::shared_ptr<Layer>, + android::sp<android::Fence>>* outFences) const; + [[clang::warn_unused_result]] Error present( + android::sp<android::Fence>* outRetireFence); + [[clang::warn_unused_result]] Error setActiveConfig( + const std::shared_ptr<const Config>& config); + [[clang::warn_unused_result]] Error setClientTarget( + buffer_handle_t target, + const android::sp<android::Fence>& acquireFence, + android_dataspace_t dataspace); + [[clang::warn_unused_result]] Error setOutputBuffer( + const android::sp<android::GraphicBuffer>& buffer, + const android::sp<android::Fence>& releaseFence); + [[clang::warn_unused_result]] Error setPowerMode(PowerMode mode); + [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled); + [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes, + uint32_t* outNumRequests); + + // Other Display methods + + Device& getDevice() const { return mDevice; } + hwc2_display_t getId() const { return mId; } + bool isConnected() const { return mIsConnected; } + +private: + // For use by Device + + // Virtual displays are always connected + void setVirtual() { + mIsVirtual = true; + mIsConnected = true; + } + + void setConnected(bool connected) { mIsConnected = connected; } + int32_t getAttribute(hwc2_config_t configId, Attribute attribute); + void loadConfig(hwc2_config_t configId); + void loadConfigs(); + + // For use by Layer + void destroyLayer(hwc2_layer_t layerId); + + // This may fail (and return a null pointer) if no layer with this ID exists + // on this display + std::shared_ptr<Layer> getLayerById(hwc2_layer_t id) const; + + // Member variables + + Device& mDevice; + hwc2_display_t mId; + bool mIsConnected; + bool mIsVirtual; + std::unordered_map<hwc2_layer_t, std::weak_ptr<Layer>> mLayers; + std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs; +}; + +class Layer +{ +public: + Layer(const std::shared_ptr<Display>& display, hwc2_layer_t id); + ~Layer(); + + bool isAbandoned() const { return mDisplay.expired(); } + hwc2_layer_t getId() const { return mId; } + + [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y); + [[clang::warn_unused_result]] Error setBuffer(buffer_handle_t buffer, + const android::sp<android::Fence>& acquireFence); + [[clang::warn_unused_result]] Error setSurfaceDamage( + const android::Region& damage); + + [[clang::warn_unused_result]] Error setBlendMode(BlendMode mode); + [[clang::warn_unused_result]] Error setColor(hwc_color_t color); + [[clang::warn_unused_result]] Error setCompositionType(Composition type); + [[clang::warn_unused_result]] Error setDisplayFrame( + const android::Rect& frame); + [[clang::warn_unused_result]] Error setPlaneAlpha(float alpha); + [[clang::warn_unused_result]] Error setSidebandStream( + const native_handle_t* stream); + [[clang::warn_unused_result]] Error setSourceCrop( + const android::FloatRect& crop); + [[clang::warn_unused_result]] Error setTransform(Transform transform); + [[clang::warn_unused_result]] Error setVisibleRegion( + const android::Region& region); + [[clang::warn_unused_result]] Error setZOrder(uint32_t z); + +private: + std::weak_ptr<Display> mDisplay; + hwc2_display_t mDisplayId; + Device& mDevice; + hwc2_layer_t mId; +}; + +} // namespace HWC2 + +#endif // ANDROID_SF_HWC2_H diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp new file mode 100644 index 0000000000..dabc77fb09 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp @@ -0,0 +1,2333 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 + +#undef LOG_TAG +#define LOG_TAG "HWC2On1Adapter" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "HWC2On1Adapter.h" + +#include <hardware/hwcomposer.h> +#include <log/log.h> +#include <utils/Trace.h> + +#include <cstdlib> +#include <chrono> +#include <inttypes.h> +#include <sstream> + +using namespace std::chrono_literals; + +static bool operator==(const hwc_color_t& lhs, const hwc_color_t& rhs) { + return lhs.r == rhs.r && + lhs.g == rhs.g && + lhs.b == rhs.b && + lhs.a == rhs.a; +} + +static bool operator==(const hwc_rect_t& lhs, const hwc_rect_t& rhs) { + return lhs.left == rhs.left && + lhs.top == rhs.top && + lhs.right == rhs.right && + lhs.bottom == rhs.bottom; +} + +static bool operator==(const hwc_frect_t& lhs, const hwc_frect_t& rhs) { + return lhs.left == rhs.left && + lhs.top == rhs.top && + lhs.right == rhs.right && + lhs.bottom == rhs.bottom; +} + +template <typename T> +static inline bool operator!=(const T& lhs, const T& rhs) +{ + return !(lhs == rhs); +} + +static uint8_t getMinorVersion(struct hwc_composer_device_1* device) +{ + auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK; + return (version >> 16) & 0xF; +} + +template <typename PFN, typename T> +static hwc2_function_pointer_t asFP(T function) +{ + static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer"); + return reinterpret_cast<hwc2_function_pointer_t>(function); +} + +using namespace HWC2; + +namespace android { + +void HWC2On1Adapter::DisplayContentsDeleter::operator()( + hwc_display_contents_1_t* contents) +{ + if (contents != nullptr) { + for (size_t l = 0; l < contents->numHwLayers; ++l) { + auto& layer = contents->hwLayers[l]; + std::free(const_cast<hwc_rect_t*>(layer.visibleRegionScreen.rects)); + } + } + std::free(contents); +} + +class HWC2On1Adapter::Callbacks : public hwc_procs_t { + public: + Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) { + invalidate = &invalidateHook; + vsync = &vsyncHook; + hotplug = &hotplugHook; + } + + static void invalidateHook(const hwc_procs_t* procs) { + auto callbacks = static_cast<const Callbacks*>(procs); + callbacks->mAdapter.hwc1Invalidate(); + } + + static void vsyncHook(const hwc_procs_t* procs, int display, + int64_t timestamp) { + auto callbacks = static_cast<const Callbacks*>(procs); + callbacks->mAdapter.hwc1Vsync(display, timestamp); + } + + static void hotplugHook(const hwc_procs_t* procs, int display, + int connected) { + auto callbacks = static_cast<const Callbacks*>(procs); + callbacks->mAdapter.hwc1Hotplug(display, connected); + } + + private: + HWC2On1Adapter& mAdapter; +}; + +static int closeHook(hw_device_t* /*device*/) +{ + // Do nothing, since the real work is done in the class destructor, but we + // need to provide a valid function pointer for hwc2_close to call + return 0; +} + +HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device) + : mDumpString(), + mHwc1Device(hwc1Device), + mHwc1MinorVersion(getMinorVersion(hwc1Device)), + mHwc1SupportsVirtualDisplays(false), + mHwc1Callbacks(std::make_unique<Callbacks>(*this)), + mCapabilities(), + mLayers(), + mHwc1VirtualDisplay(), + mStateMutex(), + mCallbacks(), + mHasPendingInvalidate(false), + mPendingVsyncs(), + mPendingHotplugs(), + mDisplays(), + mHwc1DisplayMap() +{ + common.close = closeHook; + getCapabilities = getCapabilitiesHook; + getFunction = getFunctionHook; + populateCapabilities(); + populatePrimary(); + mHwc1Device->registerProcs(mHwc1Device, + static_cast<const hwc_procs_t*>(mHwc1Callbacks.get())); +} + +HWC2On1Adapter::~HWC2On1Adapter() { + hwc_close_1(mHwc1Device); +} + +void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount, + int32_t* outCapabilities) +{ + if (outCapabilities == nullptr) { + *outCount = mCapabilities.size(); + return; + } + + auto capabilityIter = mCapabilities.cbegin(); + for (size_t written = 0; written < *outCount; ++written) { + if (capabilityIter == mCapabilities.cend()) { + return; + } + outCapabilities[written] = static_cast<int32_t>(*capabilityIter); + ++capabilityIter; + } +} + +hwc2_function_pointer_t HWC2On1Adapter::doGetFunction( + FunctionDescriptor descriptor) +{ + switch (descriptor) { + // Device functions + case FunctionDescriptor::CreateVirtualDisplay: + return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>( + createVirtualDisplayHook); + case FunctionDescriptor::DestroyVirtualDisplay: + return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>( + destroyVirtualDisplayHook); + case FunctionDescriptor::Dump: + return asFP<HWC2_PFN_DUMP>(dumpHook); + case FunctionDescriptor::GetMaxVirtualDisplayCount: + return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>( + getMaxVirtualDisplayCountHook); + case FunctionDescriptor::RegisterCallback: + return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook); + + // Display functions + case FunctionDescriptor::AcceptDisplayChanges: + return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>( + displayHook<decltype(&Display::acceptChanges), + &Display::acceptChanges>); + case FunctionDescriptor::CreateLayer: + return asFP<HWC2_PFN_CREATE_LAYER>( + displayHook<decltype(&Display::createLayer), + &Display::createLayer, hwc2_layer_t*>); + case FunctionDescriptor::DestroyLayer: + return asFP<HWC2_PFN_DESTROY_LAYER>( + displayHook<decltype(&Display::destroyLayer), + &Display::destroyLayer, hwc2_layer_t>); + case FunctionDescriptor::GetActiveConfig: + return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>( + displayHook<decltype(&Display::getActiveConfig), + &Display::getActiveConfig, hwc2_config_t*>); + case FunctionDescriptor::GetChangedCompositionTypes: + return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>( + displayHook<decltype(&Display::getChangedCompositionTypes), + &Display::getChangedCompositionTypes, uint32_t*, + hwc2_layer_t*, int32_t*>); + case FunctionDescriptor::GetDisplayAttribute: + return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>( + getDisplayAttributeHook); + case FunctionDescriptor::GetDisplayConfigs: + return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>( + displayHook<decltype(&Display::getConfigs), + &Display::getConfigs, uint32_t*, hwc2_config_t*>); + case FunctionDescriptor::GetDisplayName: + return asFP<HWC2_PFN_GET_DISPLAY_NAME>( + displayHook<decltype(&Display::getName), + &Display::getName, uint32_t*, char*>); + case FunctionDescriptor::GetDisplayRequests: + return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>( + displayHook<decltype(&Display::getRequests), + &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*, + int32_t*>); + case FunctionDescriptor::GetDisplayType: + return asFP<HWC2_PFN_GET_DISPLAY_TYPE>( + displayHook<decltype(&Display::getType), + &Display::getType, int32_t*>); + case FunctionDescriptor::GetDozeSupport: + return asFP<HWC2_PFN_GET_DOZE_SUPPORT>( + displayHook<decltype(&Display::getDozeSupport), + &Display::getDozeSupport, int32_t*>); + case FunctionDescriptor::GetReleaseFences: + return asFP<HWC2_PFN_GET_RELEASE_FENCES>( + displayHook<decltype(&Display::getReleaseFences), + &Display::getReleaseFences, uint32_t*, hwc2_layer_t*, + int32_t*>); + case FunctionDescriptor::PresentDisplay: + return asFP<HWC2_PFN_PRESENT_DISPLAY>( + displayHook<decltype(&Display::present), + &Display::present, int32_t*>); + case FunctionDescriptor::SetActiveConfig: + return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>( + displayHook<decltype(&Display::setActiveConfig), + &Display::setActiveConfig, hwc2_config_t>); + case FunctionDescriptor::SetClientTarget: + return asFP<HWC2_PFN_SET_CLIENT_TARGET>( + displayHook<decltype(&Display::setClientTarget), + &Display::setClientTarget, buffer_handle_t, int32_t, + int32_t>); + case FunctionDescriptor::SetOutputBuffer: + return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>( + displayHook<decltype(&Display::setOutputBuffer), + &Display::setOutputBuffer, buffer_handle_t, int32_t>); + case FunctionDescriptor::SetPowerMode: + return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook); + case FunctionDescriptor::SetVsyncEnabled: + return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook); + case FunctionDescriptor::ValidateDisplay: + return asFP<HWC2_PFN_VALIDATE_DISPLAY>( + displayHook<decltype(&Display::validate), + &Display::validate, uint32_t*, uint32_t*>); + + // Layer functions + case FunctionDescriptor::SetCursorPosition: + return asFP<HWC2_PFN_SET_CURSOR_POSITION>( + layerHook<decltype(&Layer::setCursorPosition), + &Layer::setCursorPosition, int32_t, int32_t>); + case FunctionDescriptor::SetLayerBuffer: + return asFP<HWC2_PFN_SET_LAYER_BUFFER>( + layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer, + buffer_handle_t, int32_t>); + case FunctionDescriptor::SetLayerSurfaceDamage: + return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>( + layerHook<decltype(&Layer::setSurfaceDamage), + &Layer::setSurfaceDamage, hwc_region_t>); + + // Layer state functions + case FunctionDescriptor::SetLayerBlendMode: + return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>( + setLayerBlendModeHook); + case FunctionDescriptor::SetLayerColor: + return asFP<HWC2_PFN_SET_LAYER_COLOR>( + layerHook<decltype(&Layer::setColor), &Layer::setColor, + hwc_color_t>); + case FunctionDescriptor::SetLayerCompositionType: + return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>( + setLayerCompositionTypeHook); + case FunctionDescriptor::SetLayerDisplayFrame: + return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>( + layerHook<decltype(&Layer::setDisplayFrame), + &Layer::setDisplayFrame, hwc_rect_t>); + case FunctionDescriptor::SetLayerPlaneAlpha: + return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>( + layerHook<decltype(&Layer::setPlaneAlpha), + &Layer::setPlaneAlpha, float>); + case FunctionDescriptor::SetLayerSidebandStream: + return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>( + layerHook<decltype(&Layer::setSidebandStream), + &Layer::setSidebandStream, const native_handle_t*>); + case FunctionDescriptor::SetLayerSourceCrop: + return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>( + layerHook<decltype(&Layer::setSourceCrop), + &Layer::setSourceCrop, hwc_frect_t>); + case FunctionDescriptor::SetLayerTransform: + return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerTransformHook); + case FunctionDescriptor::SetLayerVisibleRegion: + return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>( + layerHook<decltype(&Layer::setVisibleRegion), + &Layer::setVisibleRegion, hwc_region_t>); + case FunctionDescriptor::SetLayerZOrder: + return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerZOrderHook); + + default: + ALOGE("doGetFunction: Unknown function descriptor: %d (%s)", + static_cast<int32_t>(descriptor), + to_string(descriptor).c_str()); + return nullptr; + } +} + +// Device functions + +Error HWC2On1Adapter::createVirtualDisplay(uint32_t width, + uint32_t height, hwc2_display_t* outDisplay) +{ + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + if (mHwc1VirtualDisplay) { + // We have already allocated our only HWC1 virtual display + ALOGE("createVirtualDisplay: HWC1 virtual display already allocated"); + return Error::NoResources; + } + + if (MAX_VIRTUAL_DISPLAY_DIMENSION != 0 && + (width > MAX_VIRTUAL_DISPLAY_DIMENSION || + height > MAX_VIRTUAL_DISPLAY_DIMENSION)) { + ALOGE("createVirtualDisplay: Can't create a virtual display with" + " a dimension > %u (tried %u x %u)", + MAX_VIRTUAL_DISPLAY_DIMENSION, width, height); + return Error::NoResources; + } + + mHwc1VirtualDisplay = std::make_shared<HWC2On1Adapter::Display>(*this, + HWC2::DisplayType::Virtual); + mHwc1VirtualDisplay->populateConfigs(width, height); + const auto displayId = mHwc1VirtualDisplay->getId(); + mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId; + mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL); + mDisplays.emplace(displayId, mHwc1VirtualDisplay); + *outDisplay = displayId; + + return Error::None; +} + +Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId) +{ + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) { + return Error::BadDisplay; + } + + mHwc1VirtualDisplay.reset(); + mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL); + mDisplays.erase(displayId); + + return Error::None; +} + +void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer) +{ + if (outBuffer != nullptr) { + auto copiedBytes = mDumpString.copy(outBuffer, *outSize); + *outSize = static_cast<uint32_t>(copiedBytes); + return; + } + + std::stringstream output; + + output << "-- HWC2On1Adapter --\n"; + + output << "Adapting to a HWC 1." << static_cast<int>(mHwc1MinorVersion) << + " device\n"; + + // Attempt to acquire the lock for 1 second, but proceed without the lock + // after that, so we can still get some information if we're deadlocked + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex, + std::defer_lock); + lock.try_lock_for(1s); + + if (mCapabilities.empty()) { + output << "Capabilities: None\n"; + } else { + output << "Capabilities:\n"; + for (auto capability : mCapabilities) { + output << " " << to_string(capability) << '\n'; + } + } + + output << "Displays:\n"; + for (const auto& element : mDisplays) { + const auto& display = element.second; + output << display->dump(); + } + output << '\n'; + + // Release the lock before calling into HWC1, and since we no longer require + // mutual exclusion to access mCapabilities or mDisplays + lock.unlock(); + + if (mHwc1Device->dump) { + output << "HWC1 dump:\n"; + std::vector<char> hwc1Dump(4096); + // Call with size - 1 to preserve a null character at the end + mHwc1Device->dump(mHwc1Device, hwc1Dump.data(), + static_cast<int>(hwc1Dump.size() - 1)); + output << hwc1Dump.data(); + } + + mDumpString = output.str(); + *outSize = static_cast<uint32_t>(mDumpString.size()); +} + +uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount() +{ + return mHwc1SupportsVirtualDisplays ? 1 : 0; +} + +static bool isValid(Callback descriptor) { + switch (descriptor) { + case Callback::Hotplug: // Fall-through + case Callback::Refresh: // Fall-through + case Callback::Vsync: return true; + default: return false; + } +} + +Error HWC2On1Adapter::registerCallback(Callback descriptor, + hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) +{ + if (!isValid(descriptor)) { + return Error::BadParameter; + } + + ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(), + callbackData, pointer); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + mCallbacks[descriptor] = {callbackData, pointer}; + + bool hasPendingInvalidate = false; + std::vector<hwc2_display_t> displayIds; + std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs; + std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs; + + if (descriptor == Callback::Refresh) { + hasPendingInvalidate = mHasPendingInvalidate; + if (hasPendingInvalidate) { + for (auto& displayPair : mDisplays) { + displayIds.emplace_back(displayPair.first); + } + } + mHasPendingInvalidate = false; + } else if (descriptor == Callback::Vsync) { + for (auto pending : mPendingVsyncs) { + auto hwc1DisplayId = pending.first; + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", + hwc1DisplayId); + continue; + } + auto displayId = mHwc1DisplayMap[hwc1DisplayId]; + auto timestamp = pending.second; + pendingVsyncs.emplace_back(displayId, timestamp); + } + mPendingVsyncs.clear(); + } else if (descriptor == Callback::Hotplug) { + // Hotplug the primary display + pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY], + static_cast<int32_t>(Connection::Connected)); + + for (auto pending : mPendingHotplugs) { + auto hwc1DisplayId = pending.first; + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d", + hwc1DisplayId); + continue; + } + auto displayId = mHwc1DisplayMap[hwc1DisplayId]; + auto connected = pending.second; + pendingHotplugs.emplace_back(displayId, connected); + } + } + + // Call pending callbacks without the state lock held + lock.unlock(); + + if (hasPendingInvalidate) { + auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer); + for (auto displayId : displayIds) { + refresh(callbackData, displayId); + } + } + if (!pendingVsyncs.empty()) { + auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer); + for (auto& pendingVsync : pendingVsyncs) { + vsync(callbackData, pendingVsync.first, pendingVsync.second); + } + } + if (!pendingHotplugs.empty()) { + auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer); + for (auto& pendingHotplug : pendingHotplugs) { + hotplug(callbackData, pendingHotplug.first, pendingHotplug.second); + } + } + return Error::None; +} + +// Display functions + +std::atomic<hwc2_display_t> HWC2On1Adapter::Display::sNextId(1); + +HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type) + : mId(sNextId++), + mDevice(device), + mDirtyCount(0), + mStateMutex(), + mZIsDirty(false), + mHwc1RequestedContents(nullptr), + mHwc1ReceivedContents(nullptr), + mRetireFence(), + mChanges(), + mHwc1Id(-1), + mConfigs(), + mActiveConfig(nullptr), + mName(), + mType(type), + mPowerMode(PowerMode::Off), + mVsyncEnabled(Vsync::Invalid), + mClientTarget(), + mOutputBuffer(), + mLayers(), + mHwc1LayerMap() {} + +Error HWC2On1Adapter::Display::acceptChanges() +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges) { + ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId); + return Error::NotValidated; + } + + ALOGV("[%" PRIu64 "] acceptChanges", mId); + + for (auto& change : mChanges->getTypeChanges()) { + auto layerId = change.first; + auto type = change.second; + auto layer = mDevice.mLayers[layerId]; + layer->setCompositionType(type); + } + + mChanges->clearTypeChanges(); + + mHwc1RequestedContents = std::move(mHwc1ReceivedContents); + + return Error::None; +} + +Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + auto layer = *mLayers.emplace(std::make_shared<Layer>(*this)); + mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer)); + *outLayerId = layer->getId(); + ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId); + return Error::None; +} + +Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + const auto mapLayer = mDevice.mLayers.find(layerId); + if (mapLayer == mDevice.mLayers.end()) { + ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer", + mId, layerId); + return Error::BadLayer; + } + const auto layer = mapLayer->second; + mDevice.mLayers.erase(mapLayer); + const auto zRange = mLayers.equal_range(layer); + for (auto current = zRange.first; current != zRange.second; ++current) { + if (**current == *layer) { + current = mLayers.erase(current); + break; + } + } + ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId); + return Error::None; +} + +Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mActiveConfig) { + ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId, + to_string(Error::BadConfig).c_str()); + return Error::BadConfig; + } + auto configId = mActiveConfig->getId(); + ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId); + *outConfig = configId; + return Error::None; +} + +Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId, + Attribute attribute, int32_t* outValue) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { + ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId, + configId); + return Error::BadConfig; + } + *outValue = mConfigs[configId]->getAttribute(attribute); + ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId, + to_string(attribute).c_str(), *outValue); + return Error::None; +} + +Error HWC2On1Adapter::Display::getChangedCompositionTypes( + uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges) { + ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated", + mId); + return Error::NotValidated; + } + + if ((outLayers == nullptr) || (outTypes == nullptr)) { + *outNumElements = mChanges->getTypeChanges().size(); + return Error::None; + } + + uint32_t numWritten = 0; + for (const auto& element : mChanges->getTypeChanges()) { + if (numWritten == *outNumElements) { + break; + } + auto layerId = element.first; + auto intType = static_cast<int32_t>(element.second); + ALOGV("Adding %" PRIu64 " %s", layerId, + to_string(element.second).c_str()); + outLayers[numWritten] = layerId; + outTypes[numWritten] = intType; + ++numWritten; + } + *outNumElements = numWritten; + + return Error::None; +} + +Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs, + hwc2_config_t* outConfigs) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!outConfigs) { + *outNumConfigs = mConfigs.size(); + return Error::None; + } + uint32_t numWritten = 0; + for (const auto& config : mConfigs) { + if (numWritten == *outNumConfigs) { + break; + } + outConfigs[numWritten] = config->getId(); + ++numWritten; + } + *outNumConfigs = numWritten; + return Error::None; +} + +Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) { + *outSupport = 0; + } else { + *outSupport = 1; + } + return Error::None; +} + +Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!outName) { + *outSize = mName.size(); + return Error::None; + } + auto numCopied = mName.copy(outName, *outSize); + *outSize = numCopied; + return Error::None; +} + +Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements, + hwc2_layer_t* outLayers, int32_t* outFences) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + uint32_t numWritten = 0; + bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr); + for (const auto& layer : mLayers) { + if (outputsNonNull && (numWritten == *outNumElements)) { + break; + } + + auto releaseFence = layer->getReleaseFence(); + if (releaseFence != Fence::NO_FENCE) { + if (outputsNonNull) { + outLayers[numWritten] = layer->getId(); + outFences[numWritten] = releaseFence->dup(); + } + ++numWritten; + } + } + *outNumElements = numWritten; + + return Error::None; +} + +Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests, + uint32_t* outNumElements, hwc2_layer_t* outLayers, + int32_t* outLayerRequests) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges) { + return Error::NotValidated; + } + + if (outLayers == nullptr || outLayerRequests == nullptr) { + *outNumElements = mChanges->getNumLayerRequests(); + return Error::None; + } + + *outDisplayRequests = mChanges->getDisplayRequests(); + uint32_t numWritten = 0; + for (const auto& request : mChanges->getLayerRequests()) { + if (numWritten == *outNumElements) { + break; + } + outLayers[numWritten] = request.first; + outLayerRequests[numWritten] = static_cast<int32_t>(request.second); + ++numWritten; + } + + return Error::None; +} + +Error HWC2On1Adapter::Display::getType(int32_t* outType) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + *outType = static_cast<int32_t>(mType); + return Error::None; +} + +Error HWC2On1Adapter::Display::present(int32_t* outRetireFence) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (mChanges) { + Error error = mDevice.setAllDisplays(); + if (error != Error::None) { + ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId, + to_string(error).c_str()); + return error; + } + } + + *outRetireFence = mRetireFence.get()->dup(); + ALOGV("[%" PRIu64 "] present returning retire fence %d", mId, + *outRetireFence); + + return Error::None; +} + +Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + auto config = getConfig(configId); + if (!config) { + return Error::BadConfig; + } + mActiveConfig = config; + if (mDevice.mHwc1MinorVersion >= 4) { + int error = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, + mHwc1Id, static_cast<int>(configId)); + ALOGE_IF(error != 0, + "setActiveConfig: Failed to set active config on HWC1 (%d)", + error); + } + return Error::None; +} + +Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target, + int32_t acquireFence, int32_t /*dataspace*/) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence); + mClientTarget.setBuffer(target); + mClientTarget.setFence(acquireFence); + // dataspace can't be used by HWC1, so ignore it + return Error::None; +} + +Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer, + int32_t releaseFence) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence); + mOutputBuffer.setBuffer(buffer); + mOutputBuffer.setFence(releaseFence); + return Error::None; +} + +static bool isValid(PowerMode mode) +{ + switch (mode) { + case PowerMode::Off: // Fall-through + case PowerMode::DozeSuspend: // Fall-through + case PowerMode::Doze: // Fall-through + case PowerMode::On: return true; + default: return false; + } +} + +static int getHwc1PowerMode(PowerMode mode) +{ + switch (mode) { + case PowerMode::Off: return HWC_POWER_MODE_OFF; + case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND; + case PowerMode::Doze: return HWC_POWER_MODE_DOZE; + case PowerMode::On: return HWC_POWER_MODE_NORMAL; + default: return HWC_POWER_MODE_OFF; + } +} + +Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode) +{ + if (!isValid(mode)) { + return Error::BadParameter; + } + if (mode == mPowerMode) { + return Error::None; + } + + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + int error = 0; + if (mDevice.mHwc1MinorVersion < 4) { + error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id, + mode == PowerMode::Off); + } else { + error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device, + mHwc1Id, getHwc1PowerMode(mode)); + } + ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)", + error); + + ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str()); + mPowerMode = mode; + return Error::None; +} + +static bool isValid(Vsync enable) { + switch (enable) { + case Vsync::Enable: // Fall-through + case Vsync::Disable: return true; + default: return false; + } +} + +Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable) +{ + if (!isValid(enable)) { + return Error::BadParameter; + } + if (enable == mVsyncEnabled) { + return Error::None; + } + + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device, + mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable); + ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)", + error); + + mVsyncEnabled = enable; + return Error::None; +} + +Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes, + uint32_t* outNumRequests) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("[%" PRIu64 "] Entering validate", mId); + + if (!mChanges) { + if (!mDevice.prepareAllDisplays()) { + return Error::BadDisplay; + } + } + + *outNumTypes = mChanges->getNumTypes(); + *outNumRequests = mChanges->getNumLayerRequests(); + ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes, + *outNumRequests); + for (auto request : mChanges->getTypeChanges()) { + ALOGV("Layer %" PRIu64 " --> %s", request.first, + to_string(request.second).c_str()); + } + return *outNumTypes > 0 ? Error::HasChanges : Error::None; +} + +// Display helpers + +Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + const auto mapLayer = mDevice.mLayers.find(layerId); + if (mapLayer == mDevice.mLayers.end()) { + ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId); + return Error::BadLayer; + } + + const auto layer = mapLayer->second; + const auto zRange = mLayers.equal_range(layer); + bool layerOnDisplay = false; + for (auto current = zRange.first; current != zRange.second; ++current) { + if (**current == *layer) { + if ((*current)->getZ() == z) { + // Don't change anything if the Z hasn't changed + return Error::None; + } + current = mLayers.erase(current); + layerOnDisplay = true; + break; + } + } + + if (!layerOnDisplay) { + ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display", + mId); + return Error::BadLayer; + } + + layer->setZ(z); + mLayers.emplace(std::move(layer)); + mZIsDirty = true; + + return Error::None; +} + +static constexpr uint32_t ATTRIBUTES[] = { + HWC_DISPLAY_VSYNC_PERIOD, + HWC_DISPLAY_WIDTH, + HWC_DISPLAY_HEIGHT, + HWC_DISPLAY_DPI_X, + HWC_DISPLAY_DPI_Y, + HWC_DISPLAY_NO_ATTRIBUTE, +}; +static constexpr size_t NUM_ATTRIBUTES = sizeof(ATTRIBUTES) / sizeof(uint32_t); + +static constexpr uint32_t ATTRIBUTE_MAP[] = { + 5, // HWC_DISPLAY_NO_ATTRIBUTE = 0 + 0, // HWC_DISPLAY_VSYNC_PERIOD = 1, + 1, // HWC_DISPLAY_WIDTH = 2, + 2, // HWC_DISPLAY_HEIGHT = 3, + 3, // HWC_DISPLAY_DPI_X = 4, + 4, // HWC_DISPLAY_DPI_Y = 5, +}; + +template <uint32_t attribute> +static constexpr bool attributesMatch() +{ + return ATTRIBUTES[ATTRIBUTE_MAP[attribute]] == attribute; +} +static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(), + "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_WIDTH>(), "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync"); +static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync"); + +void HWC2On1Adapter::Display::populateConfigs() +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + ALOGV("[%" PRIu64 "] populateConfigs", mId); + + if (mHwc1Id == -1) { + ALOGE("populateConfigs: HWC1 ID not set"); + return; + } + + const size_t MAX_NUM_CONFIGS = 128; + uint32_t configs[MAX_NUM_CONFIGS] = {}; + size_t numConfigs = MAX_NUM_CONFIGS; + mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id, + configs, &numConfigs); + + for (size_t c = 0; c < numConfigs; ++c) { + uint32_t hwc1ConfigId = configs[c]; + hwc2_config_t id = static_cast<hwc2_config_t>(mConfigs.size()); + mConfigs.emplace_back( + std::make_shared<Config>(*this, id, hwc1ConfigId)); + auto& config = mConfigs[id]; + + int32_t values[NUM_ATTRIBUTES] = {}; + mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device, mHwc1Id, + hwc1ConfigId, ATTRIBUTES, values); + + config->setAttribute(Attribute::VsyncPeriod, + values[ATTRIBUTE_MAP[HWC_DISPLAY_VSYNC_PERIOD]]); + config->setAttribute(Attribute::Width, + values[ATTRIBUTE_MAP[HWC_DISPLAY_WIDTH]]); + config->setAttribute(Attribute::Height, + values[ATTRIBUTE_MAP[HWC_DISPLAY_HEIGHT]]); + config->setAttribute(Attribute::DpiX, + values[ATTRIBUTE_MAP[HWC_DISPLAY_DPI_X]]); + config->setAttribute(Attribute::DpiY, + values[ATTRIBUTE_MAP[HWC_DISPLAY_DPI_Y]]); + + ALOGV("Found config: %s", config->toString().c_str()); + } + + ALOGV("Getting active config"); + if (mDevice.mHwc1Device->getActiveConfig != nullptr) { + auto activeConfig = mDevice.mHwc1Device->getActiveConfig( + mDevice.mHwc1Device, mHwc1Id); + if (activeConfig >= 0) { + ALOGV("Setting active config to %d", activeConfig); + mActiveConfig = mConfigs[activeConfig]; + } + } else { + ALOGV("getActiveConfig is null, choosing config 0"); + mActiveConfig = mConfigs[0]; + } +} + +void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + mConfigs.emplace_back(std::make_shared<Config>(*this, 0, 0)); + auto& config = mConfigs[0]; + + config->setAttribute(Attribute::Width, static_cast<int32_t>(width)); + config->setAttribute(Attribute::Height, static_cast<int32_t>(height)); + mActiveConfig = config; +} + +bool HWC2On1Adapter::Display::prepare() +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + // Only prepare display contents for displays HWC1 knows about + if (mHwc1Id == -1) { + return true; + } + + // It doesn't make sense to prepare a display for which there is no active + // config, so return early + if (!mActiveConfig) { + ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId); + return false; + } + + ALOGV("[%" PRIu64 "] Entering prepare", mId); + + auto currentCount = mHwc1RequestedContents ? + mHwc1RequestedContents->numHwLayers : 0; + auto requiredCount = mLayers.size() + 1; + ALOGV("[%" PRIu64 "] Requires %zd layers, %zd allocated in %p", mId, + requiredCount, currentCount, mHwc1RequestedContents.get()); + + bool layerCountChanged = (currentCount != requiredCount); + if (layerCountChanged) { + reallocateHwc1Contents(); + } + + bool applyAllState = false; + if (layerCountChanged || mZIsDirty) { + assignHwc1LayerIds(); + mZIsDirty = false; + applyAllState = true; + } + + mHwc1RequestedContents->retireFenceFd = -1; + mHwc1RequestedContents->flags = 0; + if (isDirty() || applyAllState) { + mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED; + } + + for (auto& layer : mLayers) { + auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()]; + hwc1Layer.releaseFenceFd = -1; + layer->applyState(hwc1Layer, applyAllState); + } + + mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer(); + mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence(); + + prepareFramebufferTarget(); + + return true; +} + +static void cloneHWCRegion(hwc_region_t& region) +{ + auto size = sizeof(hwc_rect_t) * region.numRects; + auto newRects = static_cast<hwc_rect_t*>(std::malloc(size)); + std::copy_n(region.rects, region.numRects, newRects); + region.rects = newRects; +} + +HWC2On1Adapter::Display::HWC1Contents + HWC2On1Adapter::Display::cloneRequestedContents() const +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + size_t size = sizeof(hwc_display_contents_1_t) + + sizeof(hwc_layer_1_t) * (mHwc1RequestedContents->numHwLayers); + auto contents = static_cast<hwc_display_contents_1_t*>(std::malloc(size)); + std::memcpy(contents, mHwc1RequestedContents.get(), size); + for (size_t layerId = 0; layerId < contents->numHwLayers; ++layerId) { + auto& layer = contents->hwLayers[layerId]; + // Deep copy the regions to avoid double-frees + cloneHWCRegion(layer.visibleRegionScreen); + cloneHWCRegion(layer.surfaceDamage); + } + return HWC1Contents(contents); +} + +void HWC2On1Adapter::Display::setReceivedContents(HWC1Contents contents) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + mHwc1ReceivedContents = std::move(contents); + + mChanges.reset(new Changes); + + size_t numLayers = mHwc1ReceivedContents->numHwLayers; + for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) { + const auto& receivedLayer = mHwc1ReceivedContents->hwLayers[hwc1Id]; + if (mHwc1LayerMap.count(hwc1Id) == 0) { + ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET, + "setReceivedContents: HWC1 layer %zd doesn't have a" + " matching HWC2 layer, and isn't the framebuffer target", + hwc1Id); + continue; + } + + Layer& layer = *mHwc1LayerMap[hwc1Id]; + updateTypeChanges(receivedLayer, layer); + updateLayerRequests(receivedLayer, layer); + } +} + +bool HWC2On1Adapter::Display::hasChanges() const +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + return mChanges != nullptr; +} + +Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + if (!mChanges || (mChanges->getNumTypes() > 0)) { + ALOGE("[%" PRIu64 "] set failed: not validated", mId); + return Error::NotValidated; + } + + // Set up the client/framebuffer target + auto numLayers = hwcContents.numHwLayers; + + // Close acquire fences on FRAMEBUFFER layers, since they will not be used + // by HWC + for (size_t l = 0; l < numLayers - 1; ++l) { + auto& layer = hwcContents.hwLayers[l]; + if (layer.compositionType == HWC_FRAMEBUFFER) { + ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l); + close(layer.acquireFenceFd); + layer.acquireFenceFd = -1; + } + } + + auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1]; + if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) { + clientTargetLayer.handle = mClientTarget.getBuffer(); + clientTargetLayer.acquireFenceFd = mClientTarget.getFence(); + } else { + ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET", + mId); + } + + mChanges.reset(); + + return Error::None; +} + +void HWC2On1Adapter::Display::addRetireFence(int fenceFd) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + mRetireFence.add(fenceFd); +} + +void HWC2On1Adapter::Display::addReleaseFences( + const hwc_display_contents_1_t& hwcContents) +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + size_t numLayers = hwcContents.numHwLayers; + for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) { + const auto& receivedLayer = hwcContents.hwLayers[hwc1Id]; + if (mHwc1LayerMap.count(hwc1Id) == 0) { + if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) { + ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a" + " matching HWC2 layer, and isn't the framebuffer" + " target", hwc1Id); + } + // Close the framebuffer target release fence since we will use the + // display retire fence instead + if (receivedLayer.releaseFenceFd != -1) { + close(receivedLayer.releaseFenceFd); + } + continue; + } + + Layer& layer = *mHwc1LayerMap[hwc1Id]; + ALOGV("Adding release fence %d to layer %" PRIu64, + receivedLayer.releaseFenceFd, layer.getId()); + layer.addReleaseFence(receivedLayer.releaseFenceFd); + } +} + +static std::string hwc1CompositionString(int32_t type) +{ + switch (type) { + case HWC_FRAMEBUFFER: return "Framebuffer"; + case HWC_OVERLAY: return "Overlay"; + case HWC_BACKGROUND: return "Background"; + case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget"; + case HWC_SIDEBAND: return "Sideband"; + case HWC_CURSOR_OVERLAY: return "CursorOverlay"; + default: + return std::string("Unknown (") + std::to_string(type) + ")"; + } +} + +static std::string hwc1TransformString(int32_t transform) +{ + switch (transform) { + case 0: return "None"; + case HWC_TRANSFORM_FLIP_H: return "FlipH"; + case HWC_TRANSFORM_FLIP_V: return "FlipV"; + case HWC_TRANSFORM_ROT_90: return "Rotate90"; + case HWC_TRANSFORM_ROT_180: return "Rotate180"; + case HWC_TRANSFORM_ROT_270: return "Rotate270"; + case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90"; + case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90"; + default: + return std::string("Unknown (") + std::to_string(transform) + ")"; + } +} + +static std::string hwc1BlendModeString(int32_t mode) +{ + switch (mode) { + case HWC_BLENDING_NONE: return "None"; + case HWC_BLENDING_PREMULT: return "Premultiplied"; + case HWC_BLENDING_COVERAGE: return "Coverage"; + default: + return std::string("Unknown (") + std::to_string(mode) + ")"; + } +} + +static std::string rectString(hwc_rect_t rect) +{ + std::stringstream output; + output << "[" << rect.left << ", " << rect.top << ", "; + output << rect.right << ", " << rect.bottom << "]"; + return output.str(); +} + +static std::string approximateFloatString(float f) +{ + if (static_cast<int32_t>(f) == f) { + return std::to_string(static_cast<int32_t>(f)); + } + int32_t truncated = static_cast<int32_t>(f * 10); + bool approximate = (static_cast<float>(truncated) != f * 10); + const size_t BUFFER_SIZE = 32; + char buffer[BUFFER_SIZE] = {}; + auto bytesWritten = snprintf(buffer, BUFFER_SIZE, + "%s%.1f", approximate ? "~" : "", f); + return std::string(buffer, bytesWritten); +} + +static std::string frectString(hwc_frect_t frect) +{ + std::stringstream output; + output << "[" << approximateFloatString(frect.left) << ", "; + output << approximateFloatString(frect.top) << ", "; + output << approximateFloatString(frect.right) << ", "; + output << approximateFloatString(frect.bottom) << "]"; + return output.str(); +} + +static std::string colorString(hwc_color_t color) +{ + std::stringstream output; + output << "RGBA ["; + output << static_cast<int32_t>(color.r) << ", "; + output << static_cast<int32_t>(color.g) << ", "; + output << static_cast<int32_t>(color.b) << ", "; + output << static_cast<int32_t>(color.a) << "]"; + return output.str(); +} + +static std::string alphaString(float f) +{ + const size_t BUFFER_SIZE = 8; + char buffer[BUFFER_SIZE] = {}; + auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f); + return std::string(buffer, bytesWritten); +} + +static std::string to_string(const hwc_layer_1_t& hwcLayer, + int32_t hwc1MinorVersion) +{ + const char* fill = " "; + + std::stringstream output; + + output << " Composition: " << + hwc1CompositionString(hwcLayer.compositionType); + + if (hwcLayer.compositionType == HWC_BACKGROUND) { + output << " Color: " << colorString(hwcLayer.backgroundColor) << '\n'; + } else if (hwcLayer.compositionType == HWC_SIDEBAND) { + output << " Stream: " << hwcLayer.sidebandStream << '\n'; + } else { + output << " Buffer: " << hwcLayer.handle << "/" << + hwcLayer.acquireFenceFd << '\n'; + } + + output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) << + '\n'; + + output << fill << "Source crop: "; + if (hwc1MinorVersion >= 3) { + output << frectString(hwcLayer.sourceCropf) << '\n'; + } else { + output << rectString(hwcLayer.sourceCropi) << '\n'; + } + + output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform); + output << " Blend mode: " << hwc1BlendModeString(hwcLayer.blending); + if (hwcLayer.planeAlpha != 0xFF) { + output << " Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f); + } + output << '\n'; + + if (hwcLayer.hints != 0) { + output << fill << "Hints:"; + if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) { + output << " TripleBuffer"; + } + if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) { + output << " ClearFB"; + } + output << '\n'; + } + + if (hwcLayer.flags != 0) { + output << fill << "Flags:"; + if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) { + output << " SkipLayer"; + } + if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) { + output << " IsCursorLayer"; + } + output << '\n'; + } + + return output.str(); +} + +static std::string to_string(const hwc_display_contents_1_t& hwcContents, + int32_t hwc1MinorVersion) +{ + const char* fill = " "; + + std::stringstream output; + output << fill << "Geometry changed: " << + ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n"); + + output << fill << hwcContents.numHwLayers << " Layer" << + ((hwcContents.numHwLayers == 1) ? "\n" : "s\n"); + for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) { + output << fill << " Layer " << layer; + output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion); + } + + if (hwcContents.outbuf != nullptr) { + output << fill << "Output buffer: " << hwcContents.outbuf << "/" << + hwcContents.outbufAcquireFenceFd << '\n'; + } + + return output.str(); +} + +std::string HWC2On1Adapter::Display::dump() const +{ + std::unique_lock<std::recursive_mutex> lock(mStateMutex); + + std::stringstream output; + + output << " Display " << mId << ": "; + output << to_string(mType) << " "; + output << "HWC1 ID: " << mHwc1Id << " "; + output << "Power mode: " << to_string(mPowerMode) << " "; + output << "Vsync: " << to_string(mVsyncEnabled) << '\n'; + + output << " " << mConfigs.size() << " Config" << + (mConfigs.size() == 1 ? "" : "s") << " (* Active)\n"; + for (const auto& config : mConfigs) { + if (config == mActiveConfig) { + output << " * " << config->toString(); + } else { + output << " " << config->toString(); + } + } + output << '\n'; + + output << " " << mLayers.size() << " Layer" << + (mLayers.size() == 1 ? "" : "s") << '\n'; + for (const auto& layer : mLayers) { + output << layer->dump(); + } + + output << " Client target: " << mClientTarget.getBuffer() << '\n'; + + if (mOutputBuffer.getBuffer() != nullptr) { + output << " Output buffer: " << mOutputBuffer.getBuffer() << '\n'; + } + + if (mHwc1ReceivedContents) { + output << " Last received HWC1 state\n"; + output << to_string(*mHwc1ReceivedContents, mDevice.mHwc1MinorVersion); + } else if (mHwc1RequestedContents) { + output << " Last requested HWC1 state\n"; + output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion); + } + + return output.str(); +} + +void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute, + int32_t value) +{ + mAttributes[attribute] = value; +} + +int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const +{ + if (mAttributes.count(attribute) == 0) { + return -1; + } + return mAttributes.at(attribute); +} + +std::string HWC2On1Adapter::Display::Config::toString() const +{ + std::string output; + + const size_t BUFFER_SIZE = 100; + char buffer[BUFFER_SIZE] = {}; + auto writtenBytes = snprintf(buffer, BUFFER_SIZE, + "[%u] %u x %u", mHwcId, + mAttributes.at(HWC2::Attribute::Width), + mAttributes.at(HWC2::Attribute::Height)); + output.append(buffer, writtenBytes); + + if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) { + std::memset(buffer, 0, BUFFER_SIZE); + writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz", + 1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod)); + output.append(buffer, writtenBytes); + } + + if (mAttributes.count(HWC2::Attribute::DpiX) != 0 && + mAttributes.at(HWC2::Attribute::DpiX) != -1) { + std::memset(buffer, 0, BUFFER_SIZE); + writtenBytes = snprintf(buffer, BUFFER_SIZE, + ", DPI: %.1f x %.1f", + mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f, + mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f); + output.append(buffer, writtenBytes); + } + + return output; +} + +std::shared_ptr<const HWC2On1Adapter::Display::Config> + HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const +{ + if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { + return nullptr; + } + return mConfigs[configId]; +} + +void HWC2On1Adapter::Display::reallocateHwc1Contents() +{ + // Allocate an additional layer for the framebuffer target + auto numLayers = mLayers.size() + 1; + size_t size = sizeof(hwc_display_contents_1_t) + + sizeof(hwc_layer_1_t) * numLayers; + ALOGV("[%" PRIu64 "] reallocateHwc1Contents creating %zd layer%s", mId, + numLayers, numLayers != 1 ? "s" : ""); + auto contents = + static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1)); + contents->numHwLayers = numLayers; + mHwc1RequestedContents.reset(contents); +} + +void HWC2On1Adapter::Display::assignHwc1LayerIds() +{ + mHwc1LayerMap.clear(); + size_t nextHwc1Id = 0; + for (auto& layer : mLayers) { + mHwc1LayerMap[nextHwc1Id] = layer; + layer->setHwc1Id(nextHwc1Id++); + } +} + +void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer, + const Layer& layer) +{ + auto layerId = layer.getId(); + switch (hwc1Layer.compositionType) { + case HWC_FRAMEBUFFER: + if (layer.getCompositionType() != Composition::Client) { + mChanges->addTypeChange(layerId, Composition::Client); + } + break; + case HWC_OVERLAY: + if (layer.getCompositionType() != Composition::Device) { + mChanges->addTypeChange(layerId, Composition::Device); + } + break; + case HWC_BACKGROUND: + ALOGE_IF(layer.getCompositionType() != Composition::SolidColor, + "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2" + " wasn't expecting SolidColor"); + break; + case HWC_FRAMEBUFFER_TARGET: + // Do nothing, since it shouldn't be modified by HWC1 + break; + case HWC_SIDEBAND: + ALOGE_IF(layer.getCompositionType() != Composition::Sideband, + "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2" + " wasn't expecting Sideband"); + break; + case HWC_CURSOR_OVERLAY: + ALOGE_IF(layer.getCompositionType() != Composition::Cursor, + "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but" + " HWC2 wasn't expecting Cursor"); + break; + } +} + +void HWC2On1Adapter::Display::updateLayerRequests( + const hwc_layer_1_t& hwc1Layer, const Layer& layer) +{ + if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) { + mChanges->addLayerRequest(layer.getId(), + LayerRequest::ClearClientTarget); + } +} + +void HWC2On1Adapter::Display::prepareFramebufferTarget() +{ + // We check that mActiveConfig is valid in Display::prepare + int32_t width = mActiveConfig->getAttribute(Attribute::Width); + int32_t height = mActiveConfig->getAttribute(Attribute::Height); + + auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()]; + hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET; + hwc1Target.releaseFenceFd = -1; + hwc1Target.hints = 0; + hwc1Target.flags = 0; + hwc1Target.transform = 0; + hwc1Target.blending = HWC_BLENDING_PREMULT; + if (mDevice.getHwc1MinorVersion() < 3) { + hwc1Target.sourceCropi = {0, 0, width, height}; + } else { + hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast<float>(width), + static_cast<float>(height)}; + } + hwc1Target.displayFrame = {0, 0, width, height}; + hwc1Target.planeAlpha = 255; + hwc1Target.visibleRegionScreen.numRects = 1; + auto rects = static_cast<hwc_rect_t*>(std::malloc(sizeof(hwc_rect_t))); + rects[0].left = 0; + rects[0].top = 0; + rects[0].right = width; + rects[0].bottom = height; + hwc1Target.visibleRegionScreen.rects = rects; + + // We will set this to the correct value in set + hwc1Target.acquireFenceFd = -1; +} + +// Layer functions + +std::atomic<hwc2_layer_t> HWC2On1Adapter::Layer::sNextId(1); + +HWC2On1Adapter::Layer::Layer(Display& display) + : mId(sNextId++), + mDisplay(display), + mDirtyCount(0), + mBuffer(), + mSurfaceDamage(), + mBlendMode(*this, BlendMode::None), + mColor(*this, {0, 0, 0, 0}), + mCompositionType(*this, Composition::Invalid), + mDisplayFrame(*this, {0, 0, -1, -1}), + mPlaneAlpha(*this, 0.0f), + mSidebandStream(*this, nullptr), + mSourceCrop(*this, {0.0f, 0.0f, -1.0f, -1.0f}), + mTransform(*this, Transform::None), + mVisibleRegion(*this, std::vector<hwc_rect_t>()), + mZ(0), + mReleaseFence(), + mHwc1Id(0), + mHasUnsupportedPlaneAlpha(false) {} + +bool HWC2On1Adapter::SortLayersByZ::operator()( + const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs) +{ + return lhs->getZ() < rhs->getZ(); +} + +Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer, + int32_t acquireFence) +{ + ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId); + mBuffer.setBuffer(buffer); + mBuffer.setFence(acquireFence); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y) +{ + if (mCompositionType.getValue() != Composition::Cursor) { + return Error::BadLayer; + } + + if (mDisplay.hasChanges()) { + return Error::NotValidated; + } + + auto displayId = mDisplay.getHwc1Id(); + auto hwc1Device = mDisplay.getDevice().getHwc1Device(); + hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage) +{ + mSurfaceDamage.resize(damage.numRects); + std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin()); + return Error::None; +} + +// Layer state functions + +Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode) +{ + mBlendMode.setPending(mode); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setColor(hwc_color_t color) +{ + mColor.setPending(color); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setCompositionType(Composition type) +{ + mCompositionType.setPending(type); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame) +{ + mDisplayFrame.setPending(frame); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha) +{ + mPlaneAlpha.setPending(alpha); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream) +{ + mSidebandStream.setPending(stream); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop) +{ + mSourceCrop.setPending(crop); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setTransform(Transform transform) +{ + mTransform.setPending(transform); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t rawVisible) +{ + std::vector<hwc_rect_t> visible(rawVisible.rects, + rawVisible.rects + rawVisible.numRects); + mVisibleRegion.setPending(std::move(visible)); + return Error::None; +} + +Error HWC2On1Adapter::Layer::setZ(uint32_t z) +{ + mZ = z; + return Error::None; +} + +void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd) +{ + ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId); + mReleaseFence.add(fenceFd); +} + +const sp<Fence>& HWC2On1Adapter::Layer::getReleaseFence() const +{ + return mReleaseFence.get(); +} + +void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer, + bool applyAllState) +{ + applyCommonState(hwc1Layer, applyAllState); + auto compositionType = mCompositionType.getPendingValue(); + if (compositionType == Composition::SolidColor) { + applySolidColorState(hwc1Layer, applyAllState); + } else if (compositionType == Composition::Sideband) { + applySidebandState(hwc1Layer, applyAllState); + } else { + applyBufferState(hwc1Layer); + } + applyCompositionType(hwc1Layer, applyAllState); +} + +// Layer dump helpers + +static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion, + const std::vector<hwc_rect_t>& surfaceDamage) +{ + std::string regions; + regions += " Visible Region"; + regions.resize(40, ' '); + regions += "Surface Damage\n"; + + size_t numPrinted = 0; + size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size()); + while (numPrinted < maxSize) { + std::string line(" "); + if (visibleRegion.empty() && numPrinted == 0) { + line += "None"; + } else if (numPrinted < visibleRegion.size()) { + line += rectString(visibleRegion[numPrinted]); + } + line.resize(40, ' '); + if (surfaceDamage.empty() && numPrinted == 0) { + line += "None"; + } else if (numPrinted < surfaceDamage.size()) { + line += rectString(surfaceDamage[numPrinted]); + } + line += '\n'; + regions += line; + ++numPrinted; + } + return regions; +} + +std::string HWC2On1Adapter::Layer::dump() const +{ + std::stringstream output; + const char* fill = " "; + + output << fill << to_string(mCompositionType.getPendingValue()); + output << " Layer HWC2/1: " << mId << "/" << mHwc1Id << " "; + output << "Z: " << mZ; + if (mCompositionType.getValue() == HWC2::Composition::SolidColor) { + output << " " << colorString(mColor.getValue()); + } else if (mCompositionType.getValue() == HWC2::Composition::Sideband) { + output << " Handle: " << mSidebandStream.getValue() << '\n'; + } else { + output << " Buffer: " << mBuffer.getBuffer() << "/" << + mBuffer.getFence() << '\n'; + output << fill << " Display frame [LTRB]: " << + rectString(mDisplayFrame.getValue()) << '\n'; + output << fill << " Source crop: " << + frectString(mSourceCrop.getValue()) << '\n'; + output << fill << " Transform: " << to_string(mTransform.getValue()); + output << " Blend mode: " << to_string(mBlendMode.getValue()); + if (mPlaneAlpha.getValue() != 1.0f) { + output << " Alpha: " << + alphaString(mPlaneAlpha.getValue()) << '\n'; + } else { + output << '\n'; + } + output << regionStrings(mVisibleRegion.getValue(), mSurfaceDamage); + } + return output.str(); +} + +static int getHwc1Blending(HWC2::BlendMode blendMode) +{ + switch (blendMode) { + case BlendMode::Coverage: return HWC_BLENDING_COVERAGE; + case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT; + default: return HWC_BLENDING_NONE; + } +} + +void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer, + bool applyAllState) +{ + auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion(); + if (applyAllState || mBlendMode.isDirty()) { + hwc1Layer.blending = getHwc1Blending(mBlendMode.getPendingValue()); + mBlendMode.latch(); + } + if (applyAllState || mDisplayFrame.isDirty()) { + hwc1Layer.displayFrame = mDisplayFrame.getPendingValue(); + mDisplayFrame.latch(); + } + if (applyAllState || mPlaneAlpha.isDirty()) { + auto pendingAlpha = mPlaneAlpha.getPendingValue(); + if (minorVersion < 2) { + mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f; + } else { + hwc1Layer.planeAlpha = + static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f); + } + mPlaneAlpha.latch(); + } + if (applyAllState || mSourceCrop.isDirty()) { + if (minorVersion < 3) { + auto pending = mSourceCrop.getPendingValue(); + hwc1Layer.sourceCropi.left = + static_cast<int32_t>(std::ceil(pending.left)); + hwc1Layer.sourceCropi.top = + static_cast<int32_t>(std::ceil(pending.top)); + hwc1Layer.sourceCropi.right = + static_cast<int32_t>(std::floor(pending.right)); + hwc1Layer.sourceCropi.bottom = + static_cast<int32_t>(std::floor(pending.bottom)); + } else { + hwc1Layer.sourceCropf = mSourceCrop.getPendingValue(); + } + mSourceCrop.latch(); + } + if (applyAllState || mTransform.isDirty()) { + hwc1Layer.transform = + static_cast<uint32_t>(mTransform.getPendingValue()); + mTransform.latch(); + } + if (applyAllState || mVisibleRegion.isDirty()) { + auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen; + + std::free(const_cast<hwc_rect_t*>(hwc1VisibleRegion.rects)); + + auto pending = mVisibleRegion.getPendingValue(); + hwc_rect_t* newRects = static_cast<hwc_rect_t*>( + std::malloc(sizeof(hwc_rect_t) * pending.size())); + std::copy(pending.begin(), pending.end(), newRects); + hwc1VisibleRegion.rects = const_cast<const hwc_rect_t*>(newRects); + hwc1VisibleRegion.numRects = pending.size(); + mVisibleRegion.latch(); + } +} + +void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer, + bool applyAllState) +{ + if (applyAllState || mColor.isDirty()) { + hwc1Layer.backgroundColor = mColor.getPendingValue(); + mColor.latch(); + } +} + +void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer, + bool applyAllState) +{ + if (applyAllState || mSidebandStream.isDirty()) { + hwc1Layer.sidebandStream = mSidebandStream.getPendingValue(); + mSidebandStream.latch(); + } +} + +void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer) +{ + hwc1Layer.handle = mBuffer.getBuffer(); + hwc1Layer.acquireFenceFd = mBuffer.getFence(); +} + +void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer, + bool applyAllState) +{ + if (mHasUnsupportedPlaneAlpha) { + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags = HWC_SKIP_LAYER; + return; + } + + if (applyAllState || mCompositionType.isDirty()) { + hwc1Layer.flags = 0; + switch (mCompositionType.getPendingValue()) { + case Composition::Client: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; + break; + case Composition::Device: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + break; + case Composition::SolidColor: + hwc1Layer.compositionType = HWC_BACKGROUND; + break; + case Composition::Cursor: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) { + hwc1Layer.hints |= HWC_IS_CURSOR_LAYER; + } + break; + case Composition::Sideband: + if (mDisplay.getDevice().getHwc1MinorVersion() < 4) { + hwc1Layer.compositionType = HWC_SIDEBAND; + } else { + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; + } + break; + default: + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; + break; + } + ALOGV("Layer %" PRIu64 " %s set to %d", mId, + to_string(mCompositionType.getPendingValue()).c_str(), + hwc1Layer.compositionType); + ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, " and skipping"); + mCompositionType.latch(); + } +} + +// Adapter helpers + +void HWC2On1Adapter::populateCapabilities() +{ + ALOGV("populateCapabilities"); + if (mHwc1MinorVersion >= 3U) { + int supportedTypes = 0; + auto result = mHwc1Device->query(mHwc1Device, + HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes); + if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL) != 0)) { + ALOGI("Found support for HWC virtual displays"); + mHwc1SupportsVirtualDisplays = true; + } + } + if (mHwc1MinorVersion >= 4U) { + mCapabilities.insert(Capability::SidebandStream); + } +} + +HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id) +{ + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + auto display = mDisplays.find(id); + if (display == mDisplays.end()) { + return nullptr; + } + + return display->second.get(); +} + +std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer( + hwc2_display_t displayId, hwc2_layer_t layerId) +{ + auto display = getDisplay(displayId); + if (!display) { + return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay); + } + + auto layerEntry = mLayers.find(layerId); + if (layerEntry == mLayers.end()) { + return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer); + } + + auto layer = layerEntry->second; + if (layer->getDisplay().getId() != displayId) { + return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer); + } + return std::make_tuple(layer.get(), Error::None); +} + +void HWC2On1Adapter::populatePrimary() +{ + ALOGV("populatePrimary"); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + auto display = + std::make_shared<Display>(*this, HWC2::DisplayType::Physical); + mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId(); + display->setHwc1Id(HWC_DISPLAY_PRIMARY); + display->populateConfigs(); + mDisplays.emplace(display->getId(), std::move(display)); +} + +bool HWC2On1Adapter::prepareAllDisplays() +{ + ATRACE_CALL(); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + for (const auto& displayPair : mDisplays) { + auto& display = displayPair.second; + if (!display->prepare()) { + return false; + } + } + + if (mHwc1DisplayMap.count(0) == 0) { + ALOGE("prepareAllDisplays: Unable to find primary HWC1 display"); + return false; + } + + // Always push the primary display + std::vector<HWC2On1Adapter::Display::HWC1Contents> requestedContents; + auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY]; + auto& primaryDisplay = mDisplays[primaryDisplayId]; + auto primaryDisplayContents = primaryDisplay->cloneRequestedContents(); + requestedContents.push_back(std::move(primaryDisplayContents)); + + // Push the external display, if present + if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) { + auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL]; + auto& externalDisplay = mDisplays[externalDisplayId]; + auto externalDisplayContents = + externalDisplay->cloneRequestedContents(); + requestedContents.push_back(std::move(externalDisplayContents)); + } else { + // Even if an external display isn't present, we still need to send + // at least two displays down to HWC1 + requestedContents.push_back(nullptr); + } + + // Push the hardware virtual display, if supported and present + if (mHwc1MinorVersion >= 3) { + if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) { + auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL]; + auto& virtualDisplay = mDisplays[virtualDisplayId]; + auto virtualDisplayContents = + virtualDisplay->cloneRequestedContents(); + requestedContents.push_back(std::move(virtualDisplayContents)); + } else { + requestedContents.push_back(nullptr); + } + } + + mHwc1Contents.clear(); + for (auto& displayContents : requestedContents) { + mHwc1Contents.push_back(displayContents.get()); + if (!displayContents) { + continue; + } + + ALOGV("Display %zd layers:", mHwc1Contents.size() - 1); + for (size_t l = 0; l < displayContents->numHwLayers; ++l) { + auto& layer = displayContents->hwLayers[l]; + ALOGV(" %zd: %d", l, layer.compositionType); + } + } + + ALOGV("Calling HWC1 prepare"); + { + ATRACE_NAME("HWC1 prepare"); + mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(), + mHwc1Contents.data()); + } + + for (size_t c = 0; c < mHwc1Contents.size(); ++c) { + auto& contents = mHwc1Contents[c]; + if (!contents) { + continue; + } + ALOGV("Display %zd layers:", c); + for (size_t l = 0; l < contents->numHwLayers; ++l) { + ALOGV(" %zd: %d", l, contents->hwLayers[l].compositionType); + } + } + + // Return the received contents to their respective displays + for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { + if (mHwc1Contents[hwc1Id] == nullptr) { + continue; + } + + auto displayId = mHwc1DisplayMap[hwc1Id]; + auto& display = mDisplays[displayId]; + display->setReceivedContents(std::move(requestedContents[hwc1Id])); + } + + return true; +} + +Error HWC2On1Adapter::setAllDisplays() +{ + ATRACE_CALL(); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // Make sure we're ready to validate + for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { + if (mHwc1Contents[hwc1Id] == nullptr) { + continue; + } + + auto displayId = mHwc1DisplayMap[hwc1Id]; + auto& display = mDisplays[displayId]; + Error error = display->set(*mHwc1Contents[hwc1Id]); + if (error != Error::None) { + ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id, + to_string(error).c_str()); + return error; + } + } + + ALOGV("Calling HWC1 set"); + { + ATRACE_NAME("HWC1 set"); + mHwc1Device->set(mHwc1Device, mHwc1Contents.size(), + mHwc1Contents.data()); + } + + // Add retire and release fences + for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) { + if (mHwc1Contents[hwc1Id] == nullptr) { + continue; + } + + auto displayId = mHwc1DisplayMap[hwc1Id]; + auto& display = mDisplays[displayId]; + auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd; + ALOGV("setAllDisplays: Adding retire fence %d to display %zd", + retireFenceFd, hwc1Id); + display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd); + display->addReleaseFences(*mHwc1Contents[hwc1Id]); + } + + return Error::None; +} + +void HWC2On1Adapter::hwc1Invalidate() +{ + ALOGV("Received hwc1Invalidate"); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // If the HWC2-side callback hasn't been registered yet, buffer this until + // it is registered + if (mCallbacks.count(Callback::Refresh) == 0) { + mHasPendingInvalidate = true; + return; + } + + const auto& callbackInfo = mCallbacks[Callback::Refresh]; + std::vector<hwc2_display_t> displays; + for (const auto& displayPair : mDisplays) { + displays.emplace_back(displayPair.first); + } + + // Call back without the state lock held + lock.unlock(); + + auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer); + for (auto display : displays) { + refresh(callbackInfo.data, display); + } +} + +void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp) +{ + ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp); + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // If the HWC2-side callback hasn't been registered yet, buffer this until + // it is registered + if (mCallbacks.count(Callback::Vsync) == 0) { + mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp); + return; + } + + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId); + return; + } + + const auto& callbackInfo = mCallbacks[Callback::Vsync]; + auto displayId = mHwc1DisplayMap[hwc1DisplayId]; + + // Call back without the state lock held + lock.unlock(); + + auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer); + vsync(callbackInfo.data, displayId, timestamp); +} + +void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) +{ + ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected); + + if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) { + ALOGE("hwc1Hotplug: Received hotplug for non-external display"); + return; + } + + std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex); + + // If the HWC2-side callback hasn't been registered yet, buffer this until + // it is registered + if (mCallbacks.count(Callback::Hotplug) == 0) { + mPendingHotplugs.emplace_back(hwc1DisplayId, connected); + return; + } + + hwc2_display_t displayId = UINT64_MAX; + if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) { + if (connected == 0) { + ALOGW("hwc1Hotplug: Received disconnect for unconnected display"); + return; + } + + // Create a new display on connect + auto display = std::make_shared<HWC2On1Adapter::Display>(*this, + HWC2::DisplayType::Physical); + display->setHwc1Id(HWC_DISPLAY_EXTERNAL); + display->populateConfigs(); + displayId = display->getId(); + mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId; + mDisplays.emplace(displayId, std::move(display)); + } else { + if (connected != 0) { + ALOGW("hwc1Hotplug: Received connect for previously connected " + "display"); + return; + } + + // Disconnect an existing display + displayId = mHwc1DisplayMap[hwc1DisplayId]; + mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL); + mDisplays.erase(displayId); + } + + const auto& callbackInfo = mCallbacks[Callback::Hotplug]; + + // Call back without the state lock held + lock.unlock(); + + auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer); + auto hwc2Connected = (connected == 0) ? + HWC2::Connection::Disconnected : HWC2::Connection::Connected; + hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected)); +} + +} // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h new file mode 100644 index 0000000000..bffeefe75d --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h @@ -0,0 +1,630 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SF_HWC2_ON_1_ADAPTER_H +#define ANDROID_SF_HWC2_ON_1_ADAPTER_H + +#define HWC2_INCLUDE_STRINGIFICATION +#define HWC2_USE_CPP11 +#include <hardware/hwcomposer2.h> +#undef HWC2_INCLUDE_STRINGIFICATION +#undef HWC2_USE_CPP11 + +#include <ui/Fence.h> + +#include <atomic> +#include <map> +#include <mutex> +#include <queue> +#include <set> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +struct hwc_composer_device_1; +struct hwc_display_contents_1; +struct hwc_layer_1; + +namespace android { + +class HWC2On1Adapter : public hwc2_device_t +{ +public: + HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device); + ~HWC2On1Adapter(); + + struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; } + uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; } + +private: + static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) { + return static_cast<HWC2On1Adapter*>(device); + } + + // getCapabilities + + void doGetCapabilities(uint32_t* outCount, + int32_t* /*hwc2_capability_t*/ outCapabilities); + static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount, + int32_t* /*hwc2_capability_t*/ outCapabilities) { + getAdapter(device)->doGetCapabilities(outCount, outCapabilities); + } + + // getFunction + + hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor); + static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device, + int32_t intDesc) { + auto descriptor = static_cast<HWC2::FunctionDescriptor>(intDesc); + return getAdapter(device)->doGetFunction(descriptor); + } + + // Device functions + + HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height, + hwc2_display_t* outDisplay); + static int32_t createVirtualDisplayHook(hwc2_device_t* device, + uint32_t width, uint32_t height, hwc2_display_t* outDisplay) { + auto error = getAdapter(device)->createVirtualDisplay(width, height, + outDisplay); + return static_cast<int32_t>(error); + } + + HWC2::Error destroyVirtualDisplay(hwc2_display_t display); + static int32_t destroyVirtualDisplayHook(hwc2_device_t* device, + hwc2_display_t display) { + auto error = getAdapter(device)->destroyVirtualDisplay(display); + return static_cast<int32_t>(error); + } + + std::string mDumpString; + void dump(uint32_t* outSize, char* outBuffer); + static void dumpHook(hwc2_device_t* device, uint32_t* outSize, + char* outBuffer) { + getAdapter(device)->dump(outSize, outBuffer); + } + + uint32_t getMaxVirtualDisplayCount(); + static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) { + return getAdapter(device)->getMaxVirtualDisplayCount(); + } + + HWC2::Error registerCallback(HWC2::Callback descriptor, + hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer); + static int32_t registerCallbackHook(hwc2_device_t* device, + int32_t intDesc, hwc2_callback_data_t callbackData, + hwc2_function_pointer_t pointer) { + auto descriptor = static_cast<HWC2::Callback>(intDesc); + auto error = getAdapter(device)->registerCallback(descriptor, + callbackData, pointer); + return static_cast<int32_t>(error); + } + + // Display functions + + class Layer; + + class SortLayersByZ { + public: + bool operator()(const std::shared_ptr<Layer>& lhs, + const std::shared_ptr<Layer>& rhs); + }; + + class DisplayContentsDeleter { + public: + void operator()(struct hwc_display_contents_1* contents); + }; + + class DeferredFence { + public: + DeferredFence() + : mMutex(), + mFences({Fence::NO_FENCE, Fence::NO_FENCE}) {} + + void add(int32_t fenceFd) { + mFences.emplace(new Fence(fenceFd)); + mFences.pop(); + } + + const sp<Fence>& get() const { + return mFences.front(); + } + + private: + mutable std::mutex mMutex; + std::queue<sp<Fence>> mFences; + }; + + class FencedBuffer { + public: + FencedBuffer() : mBuffer(nullptr), mFence(Fence::NO_FENCE) {} + + void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; } + void setFence(int fenceFd) { mFence = new Fence(fenceFd); } + + buffer_handle_t getBuffer() const { return mBuffer; } + int getFence() const { return mFence->dup(); } + + private: + buffer_handle_t mBuffer; + sp<Fence> mFence; + }; + + class Display { + public: + typedef std::unique_ptr<hwc_display_contents_1, + DisplayContentsDeleter> HWC1Contents; + + Display(HWC2On1Adapter& device, HWC2::DisplayType type); + + hwc2_display_t getId() const { return mId; } + HWC2On1Adapter& getDevice() const { return mDevice; } + + // Does not require locking because it is set before adding the + // Displays to the Adapter's list of displays + void setHwc1Id(int32_t id) { mHwc1Id = id; } + int32_t getHwc1Id() const { return mHwc1Id; } + + void incDirty() { ++mDirtyCount; } + void decDirty() { --mDirtyCount; } + bool isDirty() const { return mDirtyCount > 0 || mZIsDirty; } + + // HWC2 Display functions + HWC2::Error acceptChanges(); + HWC2::Error createLayer(hwc2_layer_t* outLayerId); + HWC2::Error destroyLayer(hwc2_layer_t layerId); + HWC2::Error getActiveConfig(hwc2_config_t* outConfigId); + HWC2::Error getAttribute(hwc2_config_t configId, + HWC2::Attribute attribute, int32_t* outValue); + HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements, + hwc2_layer_t* outLayers, int32_t* outTypes); + HWC2::Error getConfigs(uint32_t* outNumConfigs, + hwc2_config_t* outConfigIds); + HWC2::Error getDozeSupport(int32_t* outSupport); + HWC2::Error getName(uint32_t* outSize, char* outName); + HWC2::Error getReleaseFences(uint32_t* outNumElements, + hwc2_layer_t* outLayers, int32_t* outFences); + HWC2::Error getRequests(int32_t* outDisplayRequests, + uint32_t* outNumElements, hwc2_layer_t* outLayers, + int32_t* outLayerRequests); + HWC2::Error getType(int32_t* outType); + HWC2::Error present(int32_t* outRetireFence); + HWC2::Error setActiveConfig(hwc2_config_t configId); + HWC2::Error setClientTarget(buffer_handle_t target, + int32_t acquireFence, int32_t dataspace); + HWC2::Error setOutputBuffer(buffer_handle_t buffer, + int32_t releaseFence); + HWC2::Error setPowerMode(HWC2::PowerMode mode); + HWC2::Error setVsyncEnabled(HWC2::Vsync enabled); + HWC2::Error validate(uint32_t* outNumTypes, + uint32_t* outNumRequests); + + HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z); + + // Read configs from HWC1 device + void populateConfigs(); + + // Set configs for a virtual display + void populateConfigs(uint32_t width, uint32_t height); + + bool prepare(); + HWC1Contents cloneRequestedContents() const; + void setReceivedContents(HWC1Contents contents); + bool hasChanges() const; + HWC2::Error set(hwc_display_contents_1& hwcContents); + void addRetireFence(int fenceFd); + void addReleaseFences(const hwc_display_contents_1& hwcContents); + + std::string dump() const; + + private: + class Config { + public: + Config(Display& display, hwc2_config_t id, uint32_t hwcId) + : mDisplay(display), + mId(id), + mHwcId(hwcId), + mAttributes() {} + + bool isOnDisplay(const Display& display) const { + return display.getId() == mDisplay.getId(); + } + + hwc2_config_t getId() const { return mId; } + uint32_t getHwcId() const { return mHwcId; } + + void setAttribute(HWC2::Attribute attribute, int32_t value); + int32_t getAttribute(HWC2::Attribute attribute) const; + + std::string toString() const; + + private: + Display& mDisplay; + const hwc2_config_t mId; + const uint32_t mHwcId; + std::unordered_map<HWC2::Attribute, int32_t> mAttributes; + }; + + class Changes { + public: + uint32_t getNumTypes() const { + return static_cast<uint32_t>(mTypeChanges.size()); + } + + uint32_t getNumLayerRequests() const { + return static_cast<uint32_t>(mLayerRequests.size()); + } + + const std::unordered_map<hwc2_layer_t, HWC2::Composition>& + getTypeChanges() const { + return mTypeChanges; + } + + const std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>& + getLayerRequests() const { + return mLayerRequests; + } + + int32_t getDisplayRequests() const { + int32_t requests = 0; + for (auto request : mDisplayRequests) { + requests |= static_cast<int32_t>(request); + } + return requests; + } + + void addTypeChange(hwc2_layer_t layerId, + HWC2::Composition type) { + mTypeChanges.insert({layerId, type}); + } + + void clearTypeChanges() { mTypeChanges.clear(); } + + void addLayerRequest(hwc2_layer_t layerId, + HWC2::LayerRequest request) { + mLayerRequests.insert({layerId, request}); + } + + private: + std::unordered_map<hwc2_layer_t, HWC2::Composition> + mTypeChanges; + std::unordered_map<hwc2_layer_t, HWC2::LayerRequest> + mLayerRequests; + std::unordered_set<HWC2::DisplayRequest> mDisplayRequests; + }; + + std::shared_ptr<const Config> + getConfig(hwc2_config_t configId) const; + + void reallocateHwc1Contents(); + void assignHwc1LayerIds(); + + void updateTypeChanges(const struct hwc_layer_1& hwc1Layer, + const Layer& layer); + void updateLayerRequests(const struct hwc_layer_1& hwc1Layer, + const Layer& layer); + + void prepareFramebufferTarget(); + + static std::atomic<hwc2_display_t> sNextId; + const hwc2_display_t mId; + HWC2On1Adapter& mDevice; + + std::atomic<size_t> mDirtyCount; + + // The state of this display should only be modified from + // SurfaceFlinger's main loop, with the exception of when dump is + // called. To prevent a bad state from crashing us during a dump + // call, all public calls into Display must acquire this mutex. + // + // It is recursive because we don't want to deadlock in validate + // (or present) when we call HWC2On1Adapter::prepareAllDisplays + // (or setAllDisplays), which calls back into Display functions + // which require locking. + mutable std::recursive_mutex mStateMutex; + + bool mZIsDirty; + HWC1Contents mHwc1RequestedContents; + HWC1Contents mHwc1ReceivedContents; + DeferredFence mRetireFence; + + // Will only be non-null after the layer has been validated but + // before it has been presented + std::unique_ptr<Changes> mChanges; + + int32_t mHwc1Id; + std::vector<std::shared_ptr<Config>> mConfigs; + std::shared_ptr<const Config> mActiveConfig; + std::string mName; + HWC2::DisplayType mType; + HWC2::PowerMode mPowerMode; + HWC2::Vsync mVsyncEnabled; + + FencedBuffer mClientTarget; + FencedBuffer mOutputBuffer; + + std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers; + std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap; + }; + + template <typename ...Args> + static int32_t callDisplayFunction(hwc2_device_t* device, + hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...), + Args... args) { + auto display = getAdapter(device)->getDisplay(displayId); + if (!display) { + return static_cast<int32_t>(HWC2::Error::BadDisplay); + } + auto error = ((*display).*member)(std::forward<Args>(args)...); + return static_cast<int32_t>(error); + } + + template <typename MF, MF memFunc, typename ...Args> + static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId, + Args... args) { + return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc, + std::forward<Args>(args)...); + } + + static int32_t getDisplayAttributeHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_config_t config, + int32_t intAttribute, int32_t* outValue) { + auto attribute = static_cast<HWC2::Attribute>(intAttribute); + return callDisplayFunction(device, display, &Display::getAttribute, + config, attribute, outValue); + } + + static int32_t setPowerModeHook(hwc2_device_t* device, + hwc2_display_t display, int32_t intMode) { + auto mode = static_cast<HWC2::PowerMode>(intMode); + return callDisplayFunction(device, display, &Display::setPowerMode, + mode); + } + + static int32_t setVsyncEnabledHook(hwc2_device_t* device, + hwc2_display_t display, int32_t intEnabled) { + auto enabled = static_cast<HWC2::Vsync>(intEnabled); + return callDisplayFunction(device, display, &Display::setVsyncEnabled, + enabled); + } + + // Layer functions + + template <typename T> + class LatchedState { + public: + LatchedState(Layer& parent, T initialValue) + : mParent(parent), + mPendingValue(initialValue), + mValue(initialValue) {} + + void setPending(T value) { + if (value == mPendingValue) { + return; + } + if (mPendingValue == mValue) { + mParent.incDirty(); + } else if (value == mValue) { + mParent.decDirty(); + } + mPendingValue = value; + } + + T getValue() const { return mValue; } + T getPendingValue() const { return mPendingValue; } + + bool isDirty() const { return mPendingValue != mValue; } + + void latch() { + if (isDirty()) { + mValue = mPendingValue; + mParent.decDirty(); + } + } + + private: + Layer& mParent; + T mPendingValue; + T mValue; + }; + + class Layer { + public: + Layer(Display& display); + + bool operator==(const Layer& other) { return mId == other.mId; } + bool operator!=(const Layer& other) { return !(*this == other); } + + hwc2_layer_t getId() const { return mId; } + Display& getDisplay() const { return mDisplay; } + + void incDirty() { if (mDirtyCount++ == 0) mDisplay.incDirty(); } + void decDirty() { if (--mDirtyCount == 0) mDisplay.decDirty(); } + bool isDirty() const { return mDirtyCount > 0; } + + // HWC2 Layer functions + HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence); + HWC2::Error setCursorPosition(int32_t x, int32_t y); + HWC2::Error setSurfaceDamage(hwc_region_t damage); + + // HWC2 Layer state functions + HWC2::Error setBlendMode(HWC2::BlendMode mode); + HWC2::Error setColor(hwc_color_t color); + HWC2::Error setCompositionType(HWC2::Composition type); + HWC2::Error setDisplayFrame(hwc_rect_t frame); + HWC2::Error setPlaneAlpha(float alpha); + HWC2::Error setSidebandStream(const native_handle_t* stream); + HWC2::Error setSourceCrop(hwc_frect_t crop); + HWC2::Error setTransform(HWC2::Transform transform); + HWC2::Error setVisibleRegion(hwc_region_t visible); + HWC2::Error setZ(uint32_t z); + + HWC2::Composition getCompositionType() const { + return mCompositionType.getValue(); + } + uint32_t getZ() const { return mZ; } + + void addReleaseFence(int fenceFd); + const sp<Fence>& getReleaseFence() const; + + void setHwc1Id(size_t id) { mHwc1Id = id; } + size_t getHwc1Id() const { return mHwc1Id; } + + void applyState(struct hwc_layer_1& hwc1Layer, bool applyAllState); + + std::string dump() const; + + private: + void applyCommonState(struct hwc_layer_1& hwc1Layer, + bool applyAllState); + void applySolidColorState(struct hwc_layer_1& hwc1Layer, + bool applyAllState); + void applySidebandState(struct hwc_layer_1& hwc1Layer, + bool applyAllState); + void applyBufferState(struct hwc_layer_1& hwc1Layer); + void applyCompositionType(struct hwc_layer_1& hwc1Layer, + bool applyAllState); + + static std::atomic<hwc2_layer_t> sNextId; + const hwc2_layer_t mId; + Display& mDisplay; + size_t mDirtyCount; + + FencedBuffer mBuffer; + std::vector<hwc_rect_t> mSurfaceDamage; + + LatchedState<HWC2::BlendMode> mBlendMode; + LatchedState<hwc_color_t> mColor; + LatchedState<HWC2::Composition> mCompositionType; + LatchedState<hwc_rect_t> mDisplayFrame; + LatchedState<float> mPlaneAlpha; + LatchedState<const native_handle_t*> mSidebandStream; + LatchedState<hwc_frect_t> mSourceCrop; + LatchedState<HWC2::Transform> mTransform; + LatchedState<std::vector<hwc_rect_t>> mVisibleRegion; + uint32_t mZ; + + DeferredFence mReleaseFence; + + size_t mHwc1Id; + bool mHasUnsupportedPlaneAlpha; + }; + + template <typename ...Args> + static int32_t callLayerFunction(hwc2_device_t* device, + hwc2_display_t displayId, hwc2_layer_t layerId, + HWC2::Error (Layer::*member)(Args...), Args... args) { + auto result = getAdapter(device)->getLayer(displayId, layerId); + auto error = std::get<HWC2::Error>(result); + if (error == HWC2::Error::None) { + auto layer = std::get<Layer*>(result); + error = ((*layer).*member)(std::forward<Args>(args)...); + } + return static_cast<int32_t>(error); + } + + template <typename MF, MF memFunc, typename ...Args> + static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId, + hwc2_layer_t layerId, Args... args) { + return HWC2On1Adapter::callLayerFunction(device, displayId, layerId, + memFunc, std::forward<Args>(args)...); + } + + // Layer state functions + + static int32_t setLayerBlendModeHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) { + auto mode = static_cast<HWC2::BlendMode>(intMode); + return callLayerFunction(device, display, layer, + &Layer::setBlendMode, mode); + } + + static int32_t setLayerCompositionTypeHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, int32_t intType) { + auto type = static_cast<HWC2::Composition>(intType); + return callLayerFunction(device, display, layer, + &Layer::setCompositionType, type); + } + + static int32_t setLayerTransformHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) { + auto transform = static_cast<HWC2::Transform>(intTransform); + return callLayerFunction(device, display, layer, &Layer::setTransform, + transform); + } + + static int32_t setLayerZOrderHook(hwc2_device_t* device, + hwc2_display_t display, hwc2_layer_t layer, uint32_t z) { + return callDisplayFunction(device, display, &Display::updateLayerZ, + layer, z); + } + + // Adapter internals + + void populateCapabilities(); + Display* getDisplay(hwc2_display_t id); + std::tuple<Layer*, HWC2::Error> getLayer(hwc2_display_t displayId, + hwc2_layer_t layerId); + void populatePrimary(); + + bool prepareAllDisplays(); + std::vector<struct hwc_display_contents_1*> mHwc1Contents; + HWC2::Error setAllDisplays(); + + void hwc1Invalidate(); + void hwc1Vsync(int hwc1DisplayId, int64_t timestamp); + void hwc1Hotplug(int hwc1DisplayId, int connected); + + // These are set in the constructor and before any asynchronous events are + // possible + + struct hwc_composer_device_1* const mHwc1Device; + const uint8_t mHwc1MinorVersion; + bool mHwc1SupportsVirtualDisplays; + + class Callbacks; + const std::unique_ptr<Callbacks> mHwc1Callbacks; + + std::unordered_set<HWC2::Capability> mCapabilities; + + // These are only accessed from the main SurfaceFlinger thread (not from + // callbacks or dump + + std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers; + std::shared_ptr<Display> mHwc1VirtualDisplay; + + // These are potentially accessed from multiple threads, and are protected + // by this mutex. This needs to be recursive, since the HWC1 implementation + // can call back into the invalidate callback on the same thread that is + // calling prepare. + std::recursive_timed_mutex mStateMutex; + + struct CallbackInfo { + hwc2_callback_data_t data; + hwc2_function_pointer_t pointer; + }; + std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks; + bool mHasPendingInvalidate; + std::vector<std::pair<int, int64_t>> mPendingVsyncs; + std::vector<std::pair<int, int>> mPendingHotplugs; + + std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays; + std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap; +}; + +} // namespace android + +#endif diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index d37fcb2b8a..26f951979c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 + +#undef LOG_TAG +#define LOG_TAG "HWComposer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <inttypes.h> @@ -43,6 +47,8 @@ #include <cutils/properties.h> #include "HWComposer.h" +#include "HWC2On1Adapter.h" +#include "HWC2.h" #include "../Layer.h" // needed only for debugging #include "../SurfaceFlinger.h" @@ -51,1088 +57,688 @@ namespace android { #define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION -static uint32_t hwcApiVersion(const hwc_composer_device_1_t* hwc) { - uint32_t hwcVersion = hwc->common.version; - return hwcVersion & HARDWARE_API_VERSION_2_MAJ_MIN_MASK; -} - -static uint32_t hwcHeaderVersion(const hwc_composer_device_1_t* hwc) { - uint32_t hwcVersion = hwc->common.version; - return hwcVersion & HARDWARE_API_VERSION_2_HEADER_MASK; -} - -static bool hwcHasApiVersion(const hwc_composer_device_1_t* hwc, - uint32_t version) { - return hwcApiVersion(hwc) >= (version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK); -} - -// --------------------------------------------------------------------------- - -struct HWComposer::cb_context { - struct callbacks : public hwc_procs_t { - // these are here to facilitate the transition when adding - // new callbacks (an implementation can check for NULL before - // calling a new callback). - void (*zero[4])(void); - }; - callbacks procs; - HWComposer* hwc; -}; - // --------------------------------------------------------------------------- -HWComposer::HWComposer( - const sp<SurfaceFlinger>& flinger, - EventHandler& handler) +HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger) : mFlinger(flinger), - mFbDev(0), mHwc(0), mNumDisplays(1), - mCBContext(new cb_context), - mEventHandler(handler), - mDebugForceFakeVSync(false) + mAdapter(), + mHwcDevice(), + mDisplayData(2), + mFreeDisplaySlots(), + mHwcDisplaySlots(), + mCBContext(), + mEventHandler(nullptr), + mVSyncCounts(), + mRemainingHwcVirtualDisplays(0) { - for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) { - mLists[i] = 0; - } - for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) { mLastHwVSync[i] = 0; mVSyncCounts[i] = 0; } - char value[PROPERTY_VALUE_MAX]; - property_get("debug.sf.no_hw_vsync", value, "0"); - mDebugForceFakeVSync = atoi(value); - - bool needVSyncThread = true; - - // Note: some devices may insist that the FB HAL be opened before HWC. - int fberr = loadFbHalModule(); loadHwcModule(); +} - if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - // close FB HAL if we don't needed it. - // FIXME: this is temporary until we're not forced to open FB HAL - // before HWC. - framebuffer_close(mFbDev); - mFbDev = NULL; - } - - // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory. - if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) - && !mFbDev) { - ALOGE("ERROR: failed to open framebuffer (%s), aborting", - strerror(-fberr)); - abort(); - } - - // these display IDs are always reserved - for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { - mAllocatedDisplayIDs.markBit(i); - } - - if (mHwc) { - ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER, - (hwcApiVersion(mHwc) >> 24) & 0xff, - (hwcApiVersion(mHwc) >> 16) & 0xff); - if (mHwc->registerProcs) { - mCBContext->hwc = this; - mCBContext->procs.invalidate = &hook_invalidate; - mCBContext->procs.vsync = &hook_vsync; - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) - mCBContext->procs.hotplug = &hook_hotplug; - else - mCBContext->procs.hotplug = NULL; - memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero)); - mHwc->registerProcs(mHwc, &mCBContext->procs); - } +HWComposer::~HWComposer() {} - // don't need a vsync thread if we have a hardware composer - needVSyncThread = false; - // always turn vsync off when we start - eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0); - - // the number of displays we actually have depends on the - // hw composer version - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { - // 1.3 adds support for virtual displays - mNumDisplays = MAX_HWC_DISPLAYS; - } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - // 1.1 adds support for multiple displays - mNumDisplays = NUM_BUILTIN_DISPLAYS; - } else { - mNumDisplays = 1; - } +void HWComposer::setEventHandler(EventHandler* handler) +{ + if (handler == nullptr) { + ALOGE("setEventHandler: Rejected attempt to clear handler"); + return; } - if (mFbDev) { - ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)), - "should only have fbdev if no hwc or hwc is 1.0"); - - DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]); - disp.connected = true; - disp.format = mFbDev->format; - DisplayConfig config = DisplayConfig(); - config.width = mFbDev->width; - config.height = mFbDev->height; - config.xdpi = mFbDev->xdpi; - config.ydpi = mFbDev->ydpi; - config.refresh = nsecs_t(1e9 / mFbDev->fps); - disp.configs.push_back(config); - disp.currentConfig = 0; - } else if (mHwc) { - // here we're guaranteed to have at least HWC 1.1 - for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { - queryDisplayProperties(i); - } - } + bool wasNull = (mEventHandler == nullptr); + mEventHandler = handler; - if (needVSyncThread) { - // we don't have VSYNC support, we need to fake it - mVSyncThread = new VSyncThread(*this); + if (wasNull) { + auto hotplugHook = std::bind(&HWComposer::hotplug, this, + std::placeholders::_1, std::placeholders::_2); + mHwcDevice->registerHotplugCallback(hotplugHook); + auto invalidateHook = std::bind(&HWComposer::invalidate, this, + std::placeholders::_1); + mHwcDevice->registerRefreshCallback(invalidateHook); + auto vsyncHook = std::bind(&HWComposer::vsync, this, + std::placeholders::_1, std::placeholders::_2); + mHwcDevice->registerVsyncCallback(vsyncHook); } } -HWComposer::~HWComposer() { - if (mHwc) { - eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0); - } - if (mVSyncThread != NULL) { - mVSyncThread->requestExitAndWait(); - } - if (mHwc) { - hwc_close_1(mHwc); - } - if (mFbDev) { - framebuffer_close(mFbDev); - } - delete mCBContext; -} - // Load and prepare the hardware composer module. Sets mHwc. void HWComposer::loadHwcModule() { + ALOGV("loadHwcModule"); + hw_module_t const* module; if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) { - ALOGE("%s module not found", HWC_HARDWARE_MODULE_ID); - return; - } - - int err = hwc_open_1(module, &mHwc); - if (err) { - ALOGE("%s device failed to initialize (%s)", - HWC_HARDWARE_COMPOSER, strerror(-err)); - return; - } - - if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_0) || - hwcHeaderVersion(mHwc) < MIN_HWC_HEADER_VERSION || - hwcHeaderVersion(mHwc) > HWC_HEADER_VERSION) { - ALOGE("%s device version %#x unsupported, will not be used", - HWC_HARDWARE_COMPOSER, mHwc->common.version); - hwc_close_1(mHwc); - mHwc = NULL; - return; + ALOGE("%s module not found, aborting", HWC_HARDWARE_MODULE_ID); + abort(); } -} -// Load and prepare the FB HAL, which uses the gralloc module. Sets mFbDev. -int HWComposer::loadFbHalModule() -{ - hw_module_t const* module; - - int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); - if (err != 0) { - ALOGE("%s module not found", GRALLOC_HARDWARE_MODULE_ID); - return err; + 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); + } 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); + uint8_t minorVersion = mAdapter->getHwc1MinorVersion(); + if (minorVersion < 1) { + ALOGE("Cannot adapt to HWC version %d.%d", + static_cast<int32_t>((minorVersion >> 8) & 0xF), + static_cast<int32_t>(minorVersion & 0xF)); + abort(); + } + mHwcDevice = std::make_unique<HWC2::Device>( + static_cast<hwc2_device_t*>(mAdapter.get())); + } + + mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount(); +} + +bool HWComposer::isValidDisplay(int32_t displayId) const { + return static_cast<size_t>(displayId) < mDisplayData.size() && + mDisplayData[displayId].hwcDisplay; +} + +void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) { + bool valid = true; + switch (from) { + case HWC2::Composition::Client: + valid = false; + break; + case HWC2::Composition::Device: + case HWC2::Composition::SolidColor: + valid = (to == HWC2::Composition::Client); + break; + case HWC2::Composition::Cursor: + case HWC2::Composition::Sideband: + valid = (to == HWC2::Composition::Client || + to == HWC2::Composition::Device); + break; + default: + break; + } + + if (!valid) { + ALOGE("Invalid layer type change: %s --> %s", to_string(from).c_str(), + to_string(to).c_str()); + } +} + +void HWComposer::hotplug(const std::shared_ptr<HWC2::Display>& display, + HWC2::Connection connected) { + ALOGV("hotplug: %" PRIu64 ", %s", display->getId(), + to_string(connected).c_str()); + int32_t disp = 0; + if (!mDisplayData[0].hwcDisplay) { + ALOGE_IF(connected != HWC2::Connection::Connected, "Assumed primary" + " display would be connected"); + mDisplayData[0].hwcDisplay = display; + mHwcDisplaySlots[display->getId()] = 0; + disp = DisplayDevice::DISPLAY_PRIMARY; + } else { + // Disconnect is handled through HWComposer::disconnectDisplay via + // SurfaceFlinger's onHotplugReceived callback handling + if (connected == HWC2::Connection::Connected) { + mDisplayData[1].hwcDisplay = display; + mHwcDisplaySlots[display->getId()] = 1; + } + disp = DisplayDevice::DISPLAY_EXTERNAL; } - - return framebuffer_open(module, &mFbDev); -} - -status_t HWComposer::initCheck() const { - return mHwc ? NO_ERROR : NO_INIT; -} - -void HWComposer::hook_invalidate(const struct hwc_procs* procs) { - cb_context* ctx = reinterpret_cast<cb_context*>( - const_cast<hwc_procs_t*>(procs)); - ctx->hwc->invalidate(); -} - -void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp, - int64_t timestamp) { - cb_context* ctx = reinterpret_cast<cb_context*>( - const_cast<hwc_procs_t*>(procs)); - ctx->hwc->vsync(disp, timestamp); + mEventHandler->onHotplugReceived(disp, + connected == HWC2::Connection::Connected); } -void HWComposer::hook_hotplug(const struct hwc_procs* procs, int disp, - int connected) { - cb_context* ctx = reinterpret_cast<cb_context*>( - const_cast<hwc_procs_t*>(procs)); - ctx->hwc->hotplug(disp, connected); -} - -void HWComposer::invalidate() { +void HWComposer::invalidate(const std::shared_ptr<HWC2::Display>& /*display*/) { mFlinger->repaintEverything(); } -void HWComposer::vsync(int disp, int64_t timestamp) { - if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) { - { - Mutex::Autolock _l(mLock); - - // There have been reports of HWCs that signal several vsync events - // with the same timestamp when turning the display off and on. This - // is a bug in the HWC implementation, but filter the extra events - // out here so they don't cause havoc downstream. - if (timestamp == mLastHwVSync[disp]) { - ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")", - timestamp); - return; - } - - mLastHwVSync[disp] = timestamp; - } - - char tag[16]; - snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp); - ATRACE_INT(tag, ++mVSyncCounts[disp] & 1); - - mEventHandler.onVSyncReceived(disp, timestamp); +void HWComposer::vsync(const std::shared_ptr<HWC2::Display>& display, + int64_t timestamp) { + auto displayType = HWC2::DisplayType::Invalid; + auto error = display->getType(&displayType); + if (error != HWC2::Error::None) { + ALOGE("vsync: Failed to determine type of display %" PRIu64, + display->getId()); + return; } -} -void HWComposer::hotplug(int disp, int connected) { - if (disp >= VIRTUAL_DISPLAY_ID_BASE) { - ALOGE("hotplug event received for invalid display: disp=%d connected=%d", - disp, connected); + if (displayType == HWC2::DisplayType::Virtual) { + ALOGE("Virtual display %" PRIu64 " passed to vsync callback", + display->getId()); return; } - queryDisplayProperties(disp); - // Do not teardown or recreate the primary display - if (disp != HWC_DISPLAY_PRIMARY) { - mEventHandler.onHotplugReceived(disp, bool(connected)); - } -} -static float getDefaultDensity(uint32_t width, uint32_t height) { - // Default density is based on TVs: 1080p displays get XHIGH density, - // lower-resolution displays get TV density. Maybe eventually we'll need - // to update it for 4K displays, though hopefully those just report - // accurate DPI information to begin with. This is also used for virtual - // displays and even primary displays with older hwcomposers, so be - // careful about orientation. - - uint32_t h = width < height ? width : height; - if (h >= 1080) return ACONFIGURATION_DENSITY_XHIGH; - else return ACONFIGURATION_DENSITY_TV; -} - -static const uint32_t DISPLAY_ATTRIBUTES[] = { - HWC_DISPLAY_VSYNC_PERIOD, - HWC_DISPLAY_WIDTH, - HWC_DISPLAY_HEIGHT, - HWC_DISPLAY_DPI_X, - HWC_DISPLAY_DPI_Y, - HWC_DISPLAY_COLOR_TRANSFORM, - HWC_DISPLAY_NO_ATTRIBUTE, -}; -#define NUM_DISPLAY_ATTRIBUTES (sizeof(DISPLAY_ATTRIBUTES) / sizeof(DISPLAY_ATTRIBUTES)[0]) - -static const uint32_t PRE_HWC15_DISPLAY_ATTRIBUTES[] = { - HWC_DISPLAY_VSYNC_PERIOD, - HWC_DISPLAY_WIDTH, - HWC_DISPLAY_HEIGHT, - HWC_DISPLAY_DPI_X, - HWC_DISPLAY_DPI_Y, - HWC_DISPLAY_NO_ATTRIBUTE, -}; - -status_t HWComposer::queryDisplayProperties(int disp) { - - LOG_ALWAYS_FATAL_IF(!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)); - - // use zero as default value for unspecified attributes - int32_t values[NUM_DISPLAY_ATTRIBUTES - 1]; - memset(values, 0, sizeof(values)); - - const size_t MAX_NUM_CONFIGS = 128; - uint32_t configs[MAX_NUM_CONFIGS] = {0}; - size_t numConfigs = MAX_NUM_CONFIGS; - status_t err = mHwc->getDisplayConfigs(mHwc, disp, configs, &numConfigs); - if (err != NO_ERROR) { - // this can happen if an unpluggable display is not connected - mDisplayData[disp].connected = false; - return err; + if (mHwcDisplaySlots.count(display->getId()) == 0) { + ALOGE("Unknown physical display %" PRIu64 " passed to vsync callback", + display->getId()); + return; } - mDisplayData[disp].currentConfig = 0; - for (size_t c = 0; c < numConfigs; ++c) { - err = mHwc->getDisplayAttributes(mHwc, disp, configs[c], - DISPLAY_ATTRIBUTES, values); - // If this is a pre-1.5 HWC, it may not know about color transform, so - // try again with a smaller set of attributes - if (err != NO_ERROR) { - err = mHwc->getDisplayAttributes(mHwc, disp, configs[c], - PRE_HWC15_DISPLAY_ATTRIBUTES, values); - } - if (err != NO_ERROR) { - // we can't get this display's info. turn it off. - mDisplayData[disp].connected = false; - return err; - } - - DisplayConfig config = DisplayConfig(); - for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) { - switch (DISPLAY_ATTRIBUTES[i]) { - case HWC_DISPLAY_VSYNC_PERIOD: - config.refresh = nsecs_t(values[i]); - break; - case HWC_DISPLAY_WIDTH: - config.width = values[i]; - break; - case HWC_DISPLAY_HEIGHT: - config.height = values[i]; - break; - case HWC_DISPLAY_DPI_X: - config.xdpi = values[i] / 1000.0f; - break; - case HWC_DISPLAY_DPI_Y: - config.ydpi = values[i] / 1000.0f; - break; - case HWC_DISPLAY_COLOR_TRANSFORM: - config.colorTransform = values[i]; - break; - default: - ALOG_ASSERT(false, "unknown display attribute[%zu] %#x", - i, DISPLAY_ATTRIBUTES[i]); - break; - } - } + int32_t disp = mHwcDisplaySlots[display->getId()]; + { + Mutex::Autolock _l(mLock); - if (config.xdpi == 0.0f || config.ydpi == 0.0f) { - float dpi = getDefaultDensity(config.width, config.height); - config.xdpi = dpi; - config.ydpi = dpi; + // There have been reports of HWCs that signal several vsync events + // with the same timestamp when turning the display off and on. This + // is a bug in the HWC implementation, but filter the extra events + // out here so they don't cause havoc downstream. + if (timestamp == mLastHwVSync[disp]) { + ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")", + timestamp); + return; } - mDisplayData[disp].configs.push_back(config); + mLastHwVSync[disp] = timestamp; } - // FIXME: what should we set the format to? - mDisplayData[disp].format = HAL_PIXEL_FORMAT_RGBA_8888; - mDisplayData[disp].connected = true; - return NO_ERROR; + char tag[16]; + snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp); + ATRACE_INT(tag, ++mVSyncCounts[disp] & 1); + + mEventHandler->onVSyncReceived(disp, timestamp); } -status_t HWComposer::setVirtualDisplayProperties(int32_t id, - uint32_t w, uint32_t h, uint32_t format) { - if (id < VIRTUAL_DISPLAY_ID_BASE || id >= int32_t(mNumDisplays) || - !mAllocatedDisplayIDs.hasBit(id)) { - return BAD_INDEX; +status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, + int32_t *outId) { + if (mRemainingHwcVirtualDisplays == 0) { + ALOGE("allocateVirtualDisplay: No remaining virtual displays"); + return NO_MEMORY; } - size_t configId = mDisplayData[id].currentConfig; - mDisplayData[id].format = format; - DisplayConfig& config = mDisplayData[id].configs.editItemAt(configId); - config.width = w; - config.height = h; - config.xdpi = config.ydpi = getDefaultDensity(w, h); - return NO_ERROR; -} -int32_t HWComposer::allocateDisplayId() { - if (mAllocatedDisplayIDs.count() >= mNumDisplays) { + std::shared_ptr<HWC2::Display> display; + auto error = mHwcDevice->createVirtualDisplay(width, height, &display); + if (error != HWC2::Error::None) { + ALOGE("allocateVirtualDisplay: Failed to create HWC virtual display"); + return NO_MEMORY; + } + + size_t displaySlot = 0; + if (!mFreeDisplaySlots.empty()) { + displaySlot = *mFreeDisplaySlots.begin(); + mFreeDisplaySlots.erase(displaySlot); + } else if (mDisplayData.size() < INT32_MAX) { + // Don't bother allocating a slot larger than we can return + displaySlot = mDisplayData.size(); + mDisplayData.resize(displaySlot + 1); + } else { + ALOGE("allocateVirtualDisplay: Unable to allocate a display slot"); return NO_MEMORY; } - int32_t id = mAllocatedDisplayIDs.firstUnmarkedBit(); - mAllocatedDisplayIDs.markBit(id); - mDisplayData[id].connected = true; - mDisplayData[id].configs.resize(1); - mDisplayData[id].currentConfig = 0; - return id; + + mDisplayData[displaySlot].hwcDisplay = display; + + --mRemainingHwcVirtualDisplays; + *outId = static_cast<int32_t>(displaySlot); + + return NO_ERROR; } -status_t HWComposer::freeDisplayId(int32_t id) { - if (id < NUM_BUILTIN_DISPLAYS) { - // cannot free the reserved IDs - return BAD_VALUE; +std::shared_ptr<HWC2::Layer> HWComposer::createLayer(int32_t displayId) { + if (!isValidDisplay(displayId)) { + ALOGE("Failed to create layer on invalid display %d", displayId); + return nullptr; } - if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { - return BAD_INDEX; + auto display = mDisplayData[displayId].hwcDisplay; + std::shared_ptr<HWC2::Layer> layer; + auto error = display->createLayer(&layer); + if (error != HWC2::Error::None) { + ALOGE("Failed to create layer on display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return nullptr; } - mAllocatedDisplayIDs.clearBit(id); - mDisplayData[id].connected = false; - return NO_ERROR; + return layer; } -nsecs_t HWComposer::getRefreshTimestamp(int disp) const { +nsecs_t HWComposer::getRefreshTimestamp(int32_t disp) const { // this returns the last refresh timestamp. // if the last one is not available, we estimate it based on // the refresh period and whatever closest timestamp we have. Mutex::Autolock _l(mLock); nsecs_t now = systemTime(CLOCK_MONOTONIC); - size_t configId = mDisplayData[disp].currentConfig; - return now - ((now - mLastHwVSync[disp]) % - mDisplayData[disp].configs[configId].refresh); -} - -sp<Fence> HWComposer::getDisplayFence(int disp) const { - return mDisplayData[disp].lastDisplayFence; + auto vsyncPeriod = getActiveConfig(disp)->getVsyncPeriod(); + return now - ((now - mLastHwVSync[disp]) % vsyncPeriod); } -uint32_t HWComposer::getFormat(int disp) const { - if (static_cast<uint32_t>(disp) >= MAX_HWC_DISPLAYS || !mAllocatedDisplayIDs.hasBit(disp)) { - return HAL_PIXEL_FORMAT_RGBA_8888; - } else { - return mDisplayData[disp].format; +bool HWComposer::isConnected(int32_t disp) const { + if (!isValidDisplay(disp)) { + ALOGE("isConnected: Attempted to access invalid display %d", disp); + return false; } + return mDisplayData[disp].hwcDisplay->isConnected(); } -bool HWComposer::isConnected(int disp) const { - return mDisplayData[disp].connected; -} - -uint32_t HWComposer::getWidth(int disp) const { - size_t currentConfig = mDisplayData[disp].currentConfig; - return mDisplayData[disp].configs[currentConfig].width; -} - -uint32_t HWComposer::getHeight(int disp) const { - size_t currentConfig = mDisplayData[disp].currentConfig; - return mDisplayData[disp].configs[currentConfig].height; -} - -float HWComposer::getDpiX(int disp) const { - size_t currentConfig = mDisplayData[disp].currentConfig; - return mDisplayData[disp].configs[currentConfig].xdpi; -} - -float HWComposer::getDpiY(int disp) const { - size_t currentConfig = mDisplayData[disp].currentConfig; - return mDisplayData[disp].configs[currentConfig].ydpi; -} - -nsecs_t HWComposer::getRefreshPeriod(int disp) const { - size_t currentConfig = mDisplayData[disp].currentConfig; - return mDisplayData[disp].configs[currentConfig].refresh; +std::vector<std::shared_ptr<const HWC2::Display::Config>> + HWComposer::getConfigs(int32_t displayId) const { + if (!isValidDisplay(displayId)) { + ALOGE("getConfigs: Attempted to access invalid display %d", displayId); + return {}; + } + auto& displayData = mDisplayData[displayId]; + auto configs = mDisplayData[displayId].hwcDisplay->getConfigs(); + if (displayData.configMap.empty()) { + for (size_t i = 0; i < configs.size(); ++i) { + displayData.configMap[i] = configs[i]; + } + } + return configs; } -const Vector<HWComposer::DisplayConfig>& HWComposer::getConfigs(int disp) const { - return mDisplayData[disp].configs; -} +std::shared_ptr<const HWC2::Display::Config> + HWComposer::getActiveConfig(int32_t displayId) const { + if (!isValidDisplay(displayId)) { + ALOGE("getActiveConfigs: Attempted to access invalid display %d", + displayId); + return nullptr; + } + std::shared_ptr<const HWC2::Display::Config> config; + auto error = mDisplayData[displayId].hwcDisplay->getActiveConfig(&config); + if (error == HWC2::Error::BadConfig) { + ALOGV("getActiveConfig: No config active, returning null"); + return nullptr; + } else if (error != HWC2::Error::None) { + ALOGE("getActiveConfig failed for display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return nullptr; + } else if (!config) { + ALOGE("getActiveConfig returned an unknown config for display %d", + displayId); + return nullptr; + } -size_t HWComposer::getCurrentConfig(int disp) const { - return mDisplayData[disp].currentConfig; + return config; } -void HWComposer::eventControl(int disp, int event, int enabled) { - if (uint32_t(disp)>31 || !mAllocatedDisplayIDs.hasBit(disp)) { - ALOGD("eventControl ignoring event %d on unallocated disp %d (en=%d)", - event, disp, enabled); +void HWComposer::setVsyncEnabled(int32_t disp, HWC2::Vsync enabled) { + if (disp < 0 || disp >= HWC_DISPLAY_VIRTUAL) { + ALOGD("setVsyncEnabled: Ignoring for virtual display %d", disp); return; } - if (event != EVENT_VSYNC) { - ALOGW("eventControl got unexpected event %d (disp=%d en=%d)", - event, disp, enabled); + + if (!isValidDisplay(disp)) { + ALOGE("setVsyncEnabled: Attempted to access invalid display %d", disp); return; } - status_t err = NO_ERROR; - if (mHwc && !mDebugForceFakeVSync) { - // NOTE: we use our own internal lock here because we have to call - // into the HWC with the lock held, and we want to make sure - // that even if HWC blocks (which it shouldn't), it won't - // affect other threads. - Mutex::Autolock _l(mEventControlLock); - const int32_t eventBit = 1UL << event; - const int32_t newValue = enabled ? eventBit : 0; - const int32_t oldValue = mDisplayData[disp].events & eventBit; - if (newValue != oldValue) { - ATRACE_CALL(); - err = mHwc->eventControl(mHwc, disp, event, enabled); - if (!err) { - int32_t& events(mDisplayData[disp].events); - events = (events & ~eventBit) | newValue; - - char tag[16]; - snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp); - ATRACE_INT(tag, enabled); - } - } - // error here should not happen -- not sure what we should - // do if it does. - ALOGE_IF(err, "eventControl(%d, %d) failed %s", - event, enabled, strerror(-err)); - } - if (err == NO_ERROR && mVSyncThread != NULL) { - mVSyncThread->setEnabled(enabled); + // NOTE: we use our own internal lock here because we have to call + // into the HWC with the lock held, and we want to make sure + // that even if HWC blocks (which it shouldn't), it won't + // affect other threads. + Mutex::Autolock _l(mVsyncLock); + auto& displayData = mDisplayData[disp]; + if (enabled != displayData.vsyncEnabled) { + ATRACE_CALL(); + auto error = displayData.hwcDisplay->setVsyncEnabled(enabled); + if (error == HWC2::Error::None) { + displayData.vsyncEnabled = enabled; + + char tag[16]; + snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp); + ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0); + } else { + ALOGE("setVsyncEnabled: Failed to set vsync to %s on %d/%" PRIu64 + ": %s (%d)", to_string(enabled).c_str(), disp, + mDisplayData[disp].hwcDisplay->getId(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } } } -status_t HWComposer::createWorkList(int32_t id, size_t numLayers) { - if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { +status_t HWComposer::setClientTarget(int32_t displayId, + const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, + android_dataspace_t dataspace) { + if (!isValidDisplay(displayId)) { return BAD_INDEX; } - if (mHwc) { - DisplayData& disp(mDisplayData[id]); - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - // we need space for the HWC_FRAMEBUFFER_TARGET - numLayers++; - } - if (disp.capacity < numLayers || disp.list == NULL) { - size_t size = sizeof(hwc_display_contents_1_t) - + numLayers * sizeof(hwc_layer_1_t); - free(disp.list); - disp.list = (hwc_display_contents_1_t*)malloc(size); - disp.capacity = numLayers; - } - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1]; - memset(disp.framebufferTarget, 0, sizeof(hwc_layer_1_t)); - const DisplayConfig& currentConfig = - disp.configs[disp.currentConfig]; - const hwc_rect_t r = { 0, 0, - (int) currentConfig.width, (int) currentConfig.height }; - disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET; - disp.framebufferTarget->hints = 0; - disp.framebufferTarget->flags = 0; - disp.framebufferTarget->handle = disp.fbTargetHandle; - disp.framebufferTarget->transform = 0; - disp.framebufferTarget->blending = HWC_BLENDING_PREMULT; - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { - disp.framebufferTarget->sourceCropf.left = 0; - disp.framebufferTarget->sourceCropf.top = 0; - disp.framebufferTarget->sourceCropf.right = - currentConfig.width; - disp.framebufferTarget->sourceCropf.bottom = - currentConfig.height; - } else { - disp.framebufferTarget->sourceCrop = r; - } - disp.framebufferTarget->displayFrame = r; - disp.framebufferTarget->visibleRegionScreen.numRects = 1; - disp.framebufferTarget->visibleRegionScreen.rects = - &disp.framebufferTarget->displayFrame; - disp.framebufferTarget->acquireFenceFd = -1; - disp.framebufferTarget->releaseFenceFd = -1; - disp.framebufferTarget->planeAlpha = 0xFF; - } - disp.list->retireFenceFd = -1; - disp.list->flags = HWC_GEOMETRY_CHANGED; - disp.list->numHwLayers = numLayers; + ALOGV("setClientTarget for display %d", displayId); + auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; + buffer_handle_t handle = nullptr; + if ((target != nullptr) && target->getNativeBuffer()) { + handle = target->getNativeBuffer()->handle; } + auto error = hwcDisplay->setClientTarget(handle, acquireFence, dataspace); + if (error != HWC2::Error::None) { + ALOGE("Failed to set client target for display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return BAD_VALUE; + } + return NO_ERROR; } -status_t HWComposer::setFramebufferTarget(int32_t id, - const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf) { - if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { +status_t HWComposer::prepare(DisplayDevice& displayDevice) { + ATRACE_CALL(); + + Mutex::Autolock _l(mDisplayLock); + auto displayId = displayDevice.getHwcDisplayId(); + if (!isValidDisplay(displayId)) { return BAD_INDEX; } - DisplayData& disp(mDisplayData[id]); - if (!disp.framebufferTarget) { - // this should never happen, but apparently eglCreateWindowSurface() - // triggers a Surface::queueBuffer() on some - // devices (!?) -- log and ignore. - ALOGE("HWComposer: framebufferTarget is null"); + + auto& displayData = mDisplayData[displayId]; + auto& hwcDisplay = displayData.hwcDisplay; + if (!hwcDisplay->isConnected()) { return NO_ERROR; } - int acquireFenceFd = -1; - if (acquireFence->isValid()) { - acquireFenceFd = acquireFence->dup(); + uint32_t numTypes = 0; + uint32_t numRequests = 0; + auto error = hwcDisplay->validate(&numTypes, &numRequests); + if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) { + ALOGE("prepare: validate failed for display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return BAD_INDEX; } - // ALOGD("fbPost: handle=%p, fence=%d", buf->handle, acquireFenceFd); - disp.fbTargetHandle = buf->handle; - disp.framebufferTarget->handle = disp.fbTargetHandle; - disp.framebufferTarget->acquireFenceFd = acquireFenceFd; - return NO_ERROR; -} + std::unordered_map<std::shared_ptr<HWC2::Layer>, HWC2::Composition> + changedTypes; + changedTypes.reserve(numTypes); + error = hwcDisplay->getChangedCompositionTypes(&changedTypes); + if (error != HWC2::Error::None) { + ALOGE("prepare: getChangedCompositionTypes failed on display %d: " + "%s (%d)", displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + return BAD_INDEX; + } -status_t HWComposer::prepare() { - Mutex::Autolock _l(mDisplayLock); - for (size_t i=0 ; i<mNumDisplays ; i++) { - DisplayData& disp(mDisplayData[i]); - if (disp.framebufferTarget) { - // make sure to reset the type to HWC_FRAMEBUFFER_TARGET - // DO NOT reset the handle field to NULL, because it's possible - // that we have nothing to redraw (eg: eglSwapBuffers() not called) - // in which case, we should continue to use the same buffer. - LOG_FATAL_IF(disp.list == NULL); - disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET; - } - if (!disp.connected && disp.list != NULL) { - ALOGW("WARNING: disp %zu: connected, non-null list, layers=%zu", - i, disp.list->numHwLayers); - } - mLists[i] = disp.list; - if (mLists[i]) { - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { - mLists[i]->outbuf = disp.outbufHandle; - mLists[i]->outbufAcquireFenceFd = -1; - } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - // garbage data to catch improper use - mLists[i]->dpy = (hwc_display_t)0xDEADBEEF; - mLists[i]->sur = (hwc_surface_t)0xDEADBEEF; - } else { - mLists[i]->dpy = EGL_NO_DISPLAY; - mLists[i]->sur = EGL_NO_SURFACE; - } - } + + displayData.displayRequests = static_cast<HWC2::DisplayRequest>(0); + std::unordered_map<std::shared_ptr<HWC2::Layer>, HWC2::LayerRequest> + layerRequests; + layerRequests.reserve(numRequests); + error = hwcDisplay->getRequests(&displayData.displayRequests, + &layerRequests); + if (error != HWC2::Error::None) { + ALOGE("prepare: getRequests failed on display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return BAD_INDEX; } - int err = mHwc->prepare(mHwc, mNumDisplays, mLists); - ALOGE_IF(err, "HWComposer: prepare failed (%s)", strerror(-err)); - - if (err == NO_ERROR) { - // here we're just making sure that "skip" layers are set - // to HWC_FRAMEBUFFER and we're also counting how many layers - // we have of each type. - // - // If there are no window layers, we treat the display has having FB - // composition, because SurfaceFlinger will use GLES to draw the - // wormhole region. - for (size_t i=0 ; i<mNumDisplays ; i++) { - DisplayData& disp(mDisplayData[i]); - disp.hasFbComp = false; - disp.hasOvComp = false; - if (disp.list) { - for (size_t i=0 ; i<disp.list->numHwLayers ; i++) { - hwc_layer_1_t& l = disp.list->hwLayers[i]; - - //ALOGD("prepare: %d, type=%d, handle=%p", - // i, l.compositionType, l.handle); - - if (l.flags & HWC_SKIP_LAYER) { - l.compositionType = HWC_FRAMEBUFFER; - } - if (l.compositionType == HWC_FRAMEBUFFER) { - disp.hasFbComp = true; - } - if (l.compositionType == HWC_OVERLAY) { - disp.hasOvComp = true; - } - if (l.compositionType == HWC_CURSOR_OVERLAY) { - disp.hasOvComp = true; - } - } - if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) { - disp.hasFbComp = true; - } - } else { - disp.hasFbComp = true; + displayData.hasClientComposition = false; + displayData.hasDeviceComposition = false; + for (auto& layer : displayDevice.getVisibleLayersSortedByZ()) { + auto hwcLayer = layer->getHwcLayer(displayId); + + if (changedTypes.count(hwcLayer) != 0) { + // We pass false so we only update our state and don't call back + // into the HWC device + validateChange(layer->getCompositionType(displayId), + changedTypes[hwcLayer]); + layer->setCompositionType(displayId, changedTypes[hwcLayer], false); + } + + switch (layer->getCompositionType(displayId)) { + case HWC2::Composition::Client: + displayData.hasClientComposition = true; + break; + case HWC2::Composition::Device: + case HWC2::Composition::SolidColor: + case HWC2::Composition::Cursor: + case HWC2::Composition::Sideband: + displayData.hasDeviceComposition = true; + break; + default: + break; + } + + if (layerRequests.count(hwcLayer) != 0 && + layerRequests[hwcLayer] == + HWC2::LayerRequest::ClearClientTarget) { + layer->setClearClientTarget(displayId, true); + } else { + if (layerRequests.count(hwcLayer) != 0) { + ALOGE("prepare: Unknown layer request: %s", + to_string(layerRequests[hwcLayer]).c_str()); } + layer->setClearClientTarget(displayId, false); } } - return (status_t)err; + + error = hwcDisplay->acceptChanges(); + if (error != HWC2::Error::None) { + ALOGE("prepare: acceptChanges failed: %s", to_string(error).c_str()); + return BAD_INDEX; + } + + return NO_ERROR; } -bool HWComposer::hasHwcComposition(int32_t id) const { - if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) +bool HWComposer::hasDeviceComposition(int32_t displayId) const { + if (!isValidDisplay(displayId)) { + ALOGE("hasDeviceComposition: Invalid display %d", displayId); return false; - return mDisplayData[id].hasOvComp; + } + return mDisplayData[displayId].hasDeviceComposition; } -bool HWComposer::hasGlesComposition(int32_t id) const { - if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) +bool HWComposer::hasClientComposition(int32_t displayId) const { + if (!isValidDisplay(displayId)) { + ALOGE("hasClientComposition: Invalid display %d", displayId); return true; - return mDisplayData[id].hasFbComp; + } + return mDisplayData[displayId].hasClientComposition; } -sp<Fence> HWComposer::getAndResetReleaseFence(int32_t id) { - if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) +sp<Fence> HWComposer::getRetireFence(int32_t displayId) const { + if (!isValidDisplay(displayId)) { + ALOGE("getRetireFence failed for invalid display %d", displayId); return Fence::NO_FENCE; - - int fd = INVALID_OPERATION; - if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - const DisplayData& disp(mDisplayData[id]); - if (disp.framebufferTarget) { - fd = disp.framebufferTarget->releaseFenceFd; - disp.framebufferTarget->acquireFenceFd = -1; - disp.framebufferTarget->releaseFenceFd = -1; - } } - return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE; + return mDisplayData[displayId].lastRetireFence; } -status_t HWComposer::commit() { - int err = NO_ERROR; - if (mHwc) { - if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - // On version 1.0, the OpenGL ES target surface is communicated - // by the (dpy, sur) fields and we are guaranteed to have only - // a single display. - mLists[0]->dpy = eglGetCurrentDisplay(); - mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW); - } - - for (size_t i=VIRTUAL_DISPLAY_ID_BASE; i<mNumDisplays; i++) { - DisplayData& disp(mDisplayData[i]); - if (disp.outbufHandle) { - mLists[i]->outbuf = disp.outbufHandle; - mLists[i]->outbufAcquireFenceFd = - disp.outbufAcquireFence->dup(); - } - } +sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId, + const std::shared_ptr<HWC2::Layer>& layer) const { + if (!isValidDisplay(displayId)) { + ALOGE("getLayerReleaseFence: Invalid display"); + return Fence::NO_FENCE; + } + auto displayFences = mDisplayData[displayId].releaseFences; + if (displayFences.count(layer) == 0) { + ALOGV("getLayerReleaseFence: Release fence not found"); + return Fence::NO_FENCE; + } + return displayFences[layer]; +} - err = mHwc->set(mHwc, mNumDisplays, mLists); +status_t HWComposer::commit(int32_t displayId) { + ATRACE_CALL(); - for (size_t i=0 ; i<mNumDisplays ; i++) { - DisplayData& disp(mDisplayData[i]); - disp.lastDisplayFence = disp.lastRetireFence; - disp.lastRetireFence = Fence::NO_FENCE; - if (disp.list) { - if (disp.list->retireFenceFd != -1) { - disp.lastRetireFence = new Fence(disp.list->retireFenceFd); - disp.list->retireFenceFd = -1; - } - disp.list->flags &= ~HWC_GEOMETRY_CHANGED; - } - } + if (!isValidDisplay(displayId)) { + return BAD_INDEX; } - return (status_t)err; -} -status_t HWComposer::setPowerMode(int disp, int mode) { - LOG_FATAL_IF(disp >= VIRTUAL_DISPLAY_ID_BASE); - if (mHwc) { - if (mode == HWC_POWER_MODE_OFF) { - eventControl(disp, HWC_EVENT_VSYNC, 0); - } - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) { - return (status_t)mHwc->setPowerMode(mHwc, disp, mode); - } else { - return (status_t)mHwc->blank(mHwc, disp, - mode == HWC_POWER_MODE_OFF ? 1 : 0); - } + auto& displayData = mDisplayData[displayId]; + auto& hwcDisplay = displayData.hwcDisplay; + auto error = hwcDisplay->present(&displayData.lastRetireFence); + if (error != HWC2::Error::None) { + ALOGE("commit: present failed for display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return UNKNOWN_ERROR; } - return NO_ERROR; -} -status_t HWComposer::setActiveConfig(int disp, int mode) { - LOG_FATAL_IF(disp >= VIRTUAL_DISPLAY_ID_BASE); - DisplayData& dd(mDisplayData[disp]); - dd.currentConfig = mode; - if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) { - return (status_t)mHwc->setActiveConfig(mHwc, disp, mode); - } else { - LOG_FATAL_IF(mode != 0); + std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences; + error = hwcDisplay->getReleaseFences(&releaseFences); + if (error != HWC2::Error::None) { + ALOGE("commit: Failed to get release fences for display %d: %s (%d)", + displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + return UNKNOWN_ERROR; } - return NO_ERROR; -} -void HWComposer::disconnectDisplay(int disp) { - LOG_ALWAYS_FATAL_IF(disp < 0 || disp == HWC_DISPLAY_PRIMARY); - DisplayData& dd(mDisplayData[disp]); - free(dd.list); - dd.list = NULL; - dd.framebufferTarget = NULL; // points into dd.list - dd.fbTargetHandle = NULL; - dd.outbufHandle = NULL; - dd.lastRetireFence = Fence::NO_FENCE; - dd.lastDisplayFence = Fence::NO_FENCE; - dd.outbufAcquireFence = Fence::NO_FENCE; - // clear all the previous configs and repopulate when a new - // device is added - dd.configs.clear(); -} + displayData.releaseFences = std::move(releaseFences); -int HWComposer::getVisualID() const { - if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - // FIXME: temporary hack until HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED - // is supported by the implementation. we can only be in this case - // if we have HWC 1.1 - return HAL_PIXEL_FORMAT_RGBA_8888; - //return HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; - } else { - return mFbDev->format; - } + return NO_ERROR; } -bool HWComposer::supportsFramebufferTarget() const { - return (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)); -} +status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) { + ALOGV("setPowerMode(%d, %d)", displayId, intMode); + if (!isValidDisplay(displayId)) { + ALOGE("setPowerMode: Bad display"); + return BAD_INDEX; + } + if (displayId >= VIRTUAL_DISPLAY_ID_BASE) { + ALOGE("setPowerMode: Virtual display %d passed in, returning", + displayId); + return BAD_INDEX; + } -int HWComposer::fbPost(int32_t id, - const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) { - if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - return setFramebufferTarget(id, acquireFence, buffer); - } else { - acquireFence->waitForever("HWComposer::fbPost"); - return mFbDev->post(mFbDev, buffer->handle); + auto mode = static_cast<HWC2::PowerMode>(intMode); + if (mode == HWC2::PowerMode::Off) { + setVsyncEnabled(displayId, HWC2::Vsync::Disable); } -} -int HWComposer::fbCompositionComplete() { - if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) - return NO_ERROR; + auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; + switch (mode) { + case HWC2::PowerMode::Off: + case HWC2::PowerMode::On: + ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str()); + { + auto error = hwcDisplay->setPowerMode(mode); + if (error != HWC2::Error::None) { + ALOGE("setPowerMode: Unable to set power mode %s for " + "display %d: %s (%d)", to_string(mode).c_str(), + displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + } + break; + case HWC2::PowerMode::Doze: + case HWC2::PowerMode::DozeSuspend: + ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str()); + { + bool supportsDoze = false; + auto error = hwcDisplay->supportsDoze(&supportsDoze); + if (error != HWC2::Error::None) { + ALOGE("setPowerMode: Unable to query doze support for " + "display %d: %s (%d)", displayId, + to_string(error).c_str(), + static_cast<int32_t>(error)); + } + if (!supportsDoze) { + mode = HWC2::PowerMode::On; + } - if (mFbDev->compositionComplete) { - return mFbDev->compositionComplete(mFbDev); - } else { - return INVALID_OPERATION; + error = hwcDisplay->setPowerMode(mode); + if (error != HWC2::Error::None) { + ALOGE("setPowerMode: Unable to set power mode %s for " + "display %d: %s (%d)", to_string(mode).c_str(), + displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + } + break; + default: + ALOGV("setPowerMode: Not calling HWC"); + break; } + + return NO_ERROR; } -void HWComposer::fbDump(String8& result) { - if (mFbDev && mFbDev->common.version >= 1 && mFbDev->dump) { - const size_t SIZE = 4096; - char buffer[SIZE]; - mFbDev->dump(mFbDev, buffer, SIZE); - result.append(buffer); +status_t HWComposer::setActiveConfig(int32_t displayId, size_t configId) { + if (!isValidDisplay(displayId)) { + ALOGE("setActiveConfig: Display %d is not valid", displayId); + return BAD_INDEX; } -} -status_t HWComposer::setOutputBuffer(int32_t id, const sp<Fence>& acquireFence, - const sp<GraphicBuffer>& buf) { - if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) + auto& displayData = mDisplayData[displayId]; + if (displayData.configMap.count(configId) == 0) { + ALOGE("setActiveConfig: Invalid config %zd", configId); return BAD_INDEX; - if (id < VIRTUAL_DISPLAY_ID_BASE) - return INVALID_OPERATION; + } + + auto error = displayData.hwcDisplay->setActiveConfig( + displayData.configMap[configId]); + if (error != HWC2::Error::None) { + ALOGE("setActiveConfig: Failed to set config %zu on display %d: " + "%s (%d)", configId, displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + return UNKNOWN_ERROR; + } - DisplayData& disp(mDisplayData[id]); - disp.outbufHandle = buf->handle; - disp.outbufAcquireFence = acquireFence; return NO_ERROR; } -sp<Fence> HWComposer::getLastRetireFence(int32_t id) const { - if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) - return Fence::NO_FENCE; - return mDisplayData[id].lastRetireFence; -} +void HWComposer::disconnectDisplay(int displayId) { + LOG_ALWAYS_FATAL_IF(displayId < 0); + auto& displayData = mDisplayData[displayId]; -status_t HWComposer::setCursorPositionAsync(int32_t id, const Rect& pos) -{ - if (mHwc->setCursorPositionAsync) { - return (status_t)mHwc->setCursorPositionAsync(mHwc, id, pos.left, pos.top); + auto displayType = HWC2::DisplayType::Invalid; + auto error = displayData.hwcDisplay->getType(&displayType); + if (error != HWC2::Error::None) { + ALOGE("disconnectDisplay: Failed to determine type of display %d", + displayId); + return; } - else { - return NO_ERROR; + + // If this was a virtual display, add its slot back for reuse by future + // virtual displays + if (displayType == HWC2::DisplayType::Virtual) { + mFreeDisplaySlots.insert(displayId); + ++mRemainingHwcVirtualDisplays; } + + auto hwcId = displayData.hwcDisplay->getId(); + mHwcDisplaySlots.erase(hwcId); + displayData.reset(); } -/* - * Helper template to implement a concrete HWCLayer - * This holds the pointer to the concrete hwc layer type - * and implements the "iterable" side of HWCLayer. - */ -template<typename CONCRETE, typename HWCTYPE> -class Iterable : public HWComposer::HWCLayer { -protected: - HWCTYPE* const mLayerList; - HWCTYPE* mCurrentLayer; - Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer) { } - inline HWCTYPE const * getLayer() const { return mCurrentLayer; } - inline HWCTYPE* getLayer() { return mCurrentLayer; } - virtual ~Iterable() { } -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) { - mCurrentLayer = &mLayerList[index]; - return NO_ERROR; +status_t HWComposer::setOutputBuffer(int32_t displayId, + const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) { + if (!isValidDisplay(displayId)) { + ALOGE("setOutputBuffer: Display %d is not valid", displayId); + return BAD_INDEX; } -}; -/* - * Concrete implementation of HWCLayer for HWC_DEVICE_API_VERSION_1_0. - * This implements the HWCLayer side of HWCIterableLayer. - */ -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) { } - - virtual int32_t getCompositionType() const { - return getLayer()->compositionType; - } - virtual uint32_t getHints() const { - return getLayer()->hints; - } - virtual sp<Fence> getAndResetReleaseFence() { - int fd = getLayer()->releaseFenceFd; - getLayer()->releaseFenceFd = -1; - return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE; - } - virtual void setAcquireFenceFd(int fenceFd) { - getLayer()->acquireFenceFd = fenceFd; - } - virtual void setPerFrameDefaultState() { - //getLayer()->compositionType = HWC_FRAMEBUFFER; - } - virtual void setPlaneAlpha(uint8_t alpha) { - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_2)) { - getLayer()->planeAlpha = alpha; - } else { - if (alpha < 0xFF) { - getLayer()->flags |= HWC_SKIP_LAYER; - } - } - } - virtual void setDefaultState() { - hwc_layer_1_t* const l = getLayer(); - l->compositionType = HWC_FRAMEBUFFER; - l->hints = 0; - l->flags = HWC_SKIP_LAYER; - l->handle = 0; - l->transform = 0; - l->blending = HWC_BLENDING_NONE; - l->visibleRegionScreen.numRects = 0; - l->visibleRegionScreen.rects = NULL; - l->acquireFenceFd = -1; - l->releaseFenceFd = -1; - l->planeAlpha = 0xFF; - } - virtual void setSkip(bool skip) { - if (skip) { - getLayer()->flags |= HWC_SKIP_LAYER; - } else { - getLayer()->flags &= ~HWC_SKIP_LAYER; - } - } - virtual void setIsCursorLayerHint(bool isCursor) { - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) { - if (isCursor) { - getLayer()->flags |= HWC_IS_CURSOR_LAYER; - } - else { - getLayer()->flags &= ~HWC_IS_CURSOR_LAYER; - } - } - } - virtual void setBlending(uint32_t blending) { - getLayer()->blending = blending; - } - virtual void setTransform(uint32_t transform) { - getLayer()->transform = transform; - } - virtual void setFrame(const Rect& frame) { - getLayer()->displayFrame = reinterpret_cast<hwc_rect_t const&>(frame); - } - virtual void setCrop(const FloatRect& crop) { - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { - getLayer()->sourceCropf = reinterpret_cast<hwc_frect_t const&>(crop); - } else { - /* - * Since h/w composer didn't support a flot crop rect before version 1.3, - * using integer coordinates instead produces a different output from the GL code in - * Layer::drawWithOpenGL(). The difference can be large if the buffer crop to - * window size ratio is large and a window crop is defined - * (i.e.: if we scale the buffer a lot and we also crop it with a window crop). - */ - hwc_rect_t& r = getLayer()->sourceCrop; - r.left = int(ceilf(crop.left)); - r.top = int(ceilf(crop.top)); - r.right = int(floorf(crop.right)); - r.bottom= int(floorf(crop.bottom)); - } - } - virtual void setVisibleRegionScreen(const Region& reg) { - hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen; - mVisibleRegion = reg; - visibleRegion.rects = reinterpret_cast<hwc_rect_t const *>( - mVisibleRegion.getArray(&visibleRegion.numRects)); - } - virtual void setSurfaceDamage(const Region& reg) { - if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_5)) { - return; - } - hwc_region_t& surfaceDamage = getLayer()->surfaceDamage; - // We encode default full-screen damage as INVALID_RECT upstream, but as - // 0 rects for HWComposer - if (reg.isRect() && reg.getBounds() == Rect::INVALID_RECT) { - surfaceDamage.numRects = 0; - surfaceDamage.rects = NULL; - return; - } - mSurfaceDamage = reg; - surfaceDamage.rects = reinterpret_cast<hwc_rect_t const *>( - mSurfaceDamage.getArray(&surfaceDamage.numRects)); - } - virtual void setSidebandStream(const sp<NativeHandle>& stream) { - ALOG_ASSERT(stream->handle() != NULL); - getLayer()->compositionType = HWC_SIDEBAND; - getLayer()->sidebandStream = stream->handle(); - } - virtual void setBuffer(const sp<GraphicBuffer>& buffer) { - if (buffer == 0 || buffer->handle == 0) { - getLayer()->compositionType = HWC_FRAMEBUFFER; - getLayer()->flags |= HWC_SKIP_LAYER; - getLayer()->handle = 0; - } else { - if (getLayer()->compositionType == HWC_SIDEBAND) { - // If this was a sideband layer but the stream was removed, reset - // it to FRAMEBUFFER. The HWC can change it to OVERLAY in prepare. - getLayer()->compositionType = HWC_FRAMEBUFFER; - } - getLayer()->handle = buffer->handle; - } - } - virtual void onDisplayed() { - getLayer()->acquireFenceFd = -1; + auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; + auto displayType = HWC2::DisplayType::Invalid; + auto error = hwcDisplay->getType(&displayType); + if (error != HWC2::Error::None) { + ALOGE("setOutputBuffer: Failed to determine type of display %d", + displayId); + return NAME_NOT_FOUND; } -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; -}; - -/* - * returns an iterator initialized at a given index in the layer list - */ -HWComposer::LayerListIterator HWComposer::getLayerIterator(int32_t id, size_t index) { - if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { - return LayerListIterator(); + if (displayType != HWC2::DisplayType::Virtual) { + ALOGE("setOutputBuffer: Display %d is not virtual", displayId); + return INVALID_OPERATION; } - const DisplayData& disp(mDisplayData[id]); - if (!mHwc || !disp.list || index > disp.list->numHwLayers) { - return LayerListIterator(); + + error = hwcDisplay->setOutputBuffer(buffer, acquireFence); + if (error != HWC2::Error::None) { + ALOGE("setOutputBuffer: Failed to set buffer on display %d: %s (%d)", + displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + return UNKNOWN_ERROR; } - return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers), index); -} -/* - * returns an iterator on the beginning of the layer list - */ -HWComposer::LayerListIterator HWComposer::begin(int32_t id) { - return getLayerIterator(id, 0); + return NO_ERROR; } -/* - * returns an iterator on the end of the layer list - */ -HWComposer::LayerListIterator HWComposer::end(int32_t id) { - size_t numLayers = 0; - if (uint32_t(id) <= 31 && mAllocatedDisplayIDs.hasBit(id)) { - const DisplayData& disp(mDisplayData[id]); - if (mHwc && disp.list) { - numLayers = disp.list->numHwLayers; - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { - // with HWC 1.1, the last layer is always the HWC_FRAMEBUFFER_TARGET, - // which we ignore when iterating through the layer list. - ALOGE_IF(!numLayers, "mDisplayData[%d].list->numHwLayers is 0", id); - if (numLayers) { - numLayers--; - } - } - } +void HWComposer::clearReleaseFences(int32_t displayId) { + if (!isValidDisplay(displayId)) { + ALOGE("clearReleaseFences: Display %d is not valid", displayId); + return; } - return getLayerIterator(id, numLayers); + mDisplayData[displayId].releaseFences.clear(); } // Converts a PixelFormat to a human-readable string. Max 11 chars. // (Could use a table of prefab String8 objects.) +/* static String8 getFormatStr(PixelFormat format) { switch (format) { case PIXEL_FORMAT_RGBA_8888: return String8("RGBA_8888"); @@ -1148,174 +754,34 @@ static String8 getFormatStr(PixelFormat format) { return result; } } +*/ void HWComposer::dump(String8& result) const { - Mutex::Autolock _l(mDisplayLock); - if (mHwc) { - result.appendFormat("Hardware Composer state (version %08x):\n", hwcApiVersion(mHwc)); - result.appendFormat(" mDebugForceFakeVSync=%d\n", mDebugForceFakeVSync); - for (size_t i=0 ; i<mNumDisplays ; i++) { - const DisplayData& disp(mDisplayData[i]); - if (!disp.connected) - continue; - - const Vector< sp<Layer> >& visibleLayersSortedByZ = - mFlinger->getLayerSortedByZForHwcDisplay(i); - - - result.appendFormat(" Display[%zd] configurations (* current):\n", i); - for (size_t c = 0; c < disp.configs.size(); ++c) { - const DisplayConfig& config(disp.configs[c]); - result.appendFormat(" %s%zd: %ux%u, xdpi=%f, ydpi=%f" - ", refresh=%" PRId64 ", colorTransform=%d\n", - c == disp.currentConfig ? "* " : "", c, - config.width, config.height, config.xdpi, config.ydpi, - config.refresh, config.colorTransform); - } - - if (disp.list) { - result.appendFormat( - " numHwLayers=%zu, flags=%08x\n", - disp.list->numHwLayers, disp.list->flags); - - result.append( - " type | handle | hint | flag | tr | blnd | format | source crop (l,t,r,b) | frame | name \n" - "-----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------\n"); - // " _________ | ________ | ____ | ____ | __ | ____ | ___________ |_____._,_____._,_____._,_____._ |_____,_____,_____,_____ | ___... - for (size_t i=0 ; i<disp.list->numHwLayers ; i++) { - const hwc_layer_1_t&l = disp.list->hwLayers[i]; - int32_t format = -1; - String8 name("unknown"); - - if (i < visibleLayersSortedByZ.size()) { - const sp<Layer>& layer(visibleLayersSortedByZ[i]); - const sp<GraphicBuffer>& buffer( - layer->getActiveBuffer()); - if (buffer != NULL) { - format = buffer->getPixelFormat(); - } - name = layer->getName(); - } - - int type = l.compositionType; - if (type == HWC_FRAMEBUFFER_TARGET) { - name = "HWC_FRAMEBUFFER_TARGET"; - format = disp.format; - } - - static char const* compositionTypeName[] = { - "GLES", - "HWC", - "BKGND", - "FB TARGET", - "SIDEBAND", - "HWC_CURSOR", - "UNKNOWN"}; - if (type >= NELEM(compositionTypeName)) - type = NELEM(compositionTypeName) - 1; - - String8 formatStr = getFormatStr(format); - if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { - result.appendFormat( - " %9s | %08" PRIxPTR " | %04x | %04x | %02x | %04x | %-11s |%7.1f,%7.1f,%7.1f,%7.1f |%5d,%5d,%5d,%5d | %s\n", - compositionTypeName[type], - intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, formatStr.string(), - l.sourceCropf.left, l.sourceCropf.top, l.sourceCropf.right, l.sourceCropf.bottom, - l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, - name.string()); - } else { - result.appendFormat( - " %9s | %08" PRIxPTR " | %04x | %04x | %02x | %04x | %-11s |%7d,%7d,%7d,%7d |%5d,%5d,%5d,%5d | %s\n", - compositionTypeName[type], - intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, formatStr.string(), - l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom, - l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, - name.string()); - } - } - } - } - } - - if (mHwc && mHwc->dump) { - const size_t SIZE = 4096; - char buffer[SIZE]; - mHwc->dump(mHwc, buffer, SIZE); - result.append(buffer); - } + // TODO: In order to provide a dump equivalent to HWC1, we need to shadow + // all the state going into the layers. This is probably better done in + // Layer itself, but it's going to take a bit of work to get there. + result.append(mHwcDevice->dump().c_str()); } // --------------------------------------------------------------------------- -HWComposer::VSyncThread::VSyncThread(HWComposer& hwc) - : mHwc(hwc), mEnabled(false), - mNextFakeVSync(0), - mRefreshPeriod(hwc.getRefreshPeriod(HWC_DISPLAY_PRIMARY)) -{ -} - -void HWComposer::VSyncThread::setEnabled(bool enabled) { - Mutex::Autolock _l(mLock); - if (mEnabled != enabled) { - mEnabled = enabled; - mCondition.signal(); - } -} - -void HWComposer::VSyncThread::onFirstRef() { - run("VSyncThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); +HWComposer::DisplayData::DisplayData() + : hasClientComposition(false), + hasDeviceComposition(false), + hwcDisplay(), + lastRetireFence(Fence::NO_FENCE), + outbufHandle(nullptr), + outbufAcquireFence(Fence::NO_FENCE), + vsyncEnabled(HWC2::Vsync::Disable) { + ALOGV("Created new DisplayData"); } -bool HWComposer::VSyncThread::threadLoop() { - { // scope for lock - Mutex::Autolock _l(mLock); - while (!mEnabled) { - mCondition.wait(mLock); - } - } - - const nsecs_t period = mRefreshPeriod; - const nsecs_t now = systemTime(CLOCK_MONOTONIC); - nsecs_t next_vsync = mNextFakeVSync; - nsecs_t sleep = next_vsync - now; - if (sleep < 0) { - // we missed, find where the next vsync should be - sleep = (period - ((now - next_vsync) % period)); - next_vsync = now + sleep; - } - mNextFakeVSync = next_vsync + period; - - struct timespec spec; - spec.tv_sec = next_vsync / 1000000000; - spec.tv_nsec = next_vsync % 1000000000; - - int err; - do { - err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); - } while (err<0 && errno == EINTR); - - if (err == 0) { - mHwc.mEventHandler.onVSyncReceived(0, next_vsync); - } - - return true; +HWComposer::DisplayData::~DisplayData() { } -HWComposer::DisplayData::DisplayData() -: configs(), - currentConfig(0), - format(HAL_PIXEL_FORMAT_RGBA_8888), - connected(false), - hasFbComp(false), hasOvComp(false), - capacity(0), list(NULL), - framebufferTarget(NULL), fbTargetHandle(0), - lastRetireFence(Fence::NO_FENCE), lastDisplayFence(Fence::NO_FENCE), - outbufHandle(NULL), outbufAcquireFence(Fence::NO_FENCE), - events(0) -{} - -HWComposer::DisplayData::~DisplayData() { - free(list); +void HWComposer::DisplayData::reset() { + ALOGV("DisplayData reset"); + *this = DisplayData(); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 5e0b3d85ea..30c8f6719a 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -14,14 +14,18 @@ * limitations under the License. */ +#ifndef USE_HWC2 +#include "HWComposer_hwc1.h" +#else + #ifndef ANDROID_SF_HWCOMPOSER_H #define ANDROID_SF_HWCOMPOSER_H +#include "HWC2.h" + #include <stdint.h> #include <sys/types.h> -#include <hardware/hwcomposer_defs.h> - #include <ui/Fence.h> #include <utils/BitSet.h> @@ -32,22 +36,29 @@ #include <utils/Timers.h> #include <utils/Vector.h> +#include <memory> +#include <set> +#include <vector> + extern "C" int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain); -struct hwc_composer_device_1; -struct hwc_display_contents_1; -struct hwc_layer_1; -struct hwc_procs; struct framebuffer_device_t; +namespace HWC2 { + class Device; + class Display; +} + namespace android { // --------------------------------------------------------------------------- +class DisplayDevice; class Fence; class FloatRect; class GraphicBuffer; +class HWC2On1Adapter; class NativeHandle; class Region; class String8; @@ -58,198 +69,70 @@ class HWComposer public: class EventHandler { friend class HWComposer; - virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0; - virtual void onHotplugReceived(int disp, bool connected) = 0; + virtual void onVSyncReceived(int32_t disp, nsecs_t timestamp) = 0; + virtual void onHotplugReceived(int32_t disp, bool connected) = 0; protected: virtual ~EventHandler() {} }; - enum { - NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES, - MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES, - VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL, - }; - - HWComposer( - const sp<SurfaceFlinger>& flinger, - EventHandler& handler); + HWComposer(const sp<SurfaceFlinger>& flinger); ~HWComposer(); - status_t initCheck() const; + void setEventHandler(EventHandler* handler); - // Returns a display ID starting at VIRTUAL_DISPLAY_ID_BASE, this ID is to - // be used with createWorkList (and all other methods requiring an ID - // below). - // IDs below NUM_BUILTIN_DISPLAYS are pre-defined and therefore are - // always valid. - // Returns -1 if an ID cannot be allocated - int32_t allocateDisplayId(); - - // Recycles the given virtual display ID and frees the associated worklist. - // IDs below NUM_BUILTIN_DISPLAYS are not recycled. - status_t freeDisplayId(int32_t id); + // Attempts to allocate a virtual display. If the virtual display is created + // on the HWC device, outId will contain its HWC ID. + status_t allocateVirtualDisplay(uint32_t width, uint32_t height, + int32_t* outId); + // Attempts to create a new layer on this display + std::shared_ptr<HWC2::Layer> createLayer(int32_t displayId); // Asks the HAL what it can do - status_t prepare(); + status_t prepare(DisplayDevice& displayDevice); + + status_t setClientTarget(int32_t displayId, const sp<Fence>& acquireFence, + const sp<GraphicBuffer>& target, android_dataspace_t dataspace); - // commits the list - status_t commit(); + // Finalize the layers and present them + status_t commit(int32_t displayId); // set power mode - status_t setPowerMode(int disp, int mode); + status_t setPowerMode(int32_t displayId, int mode); // set active config - status_t setActiveConfig(int disp, int mode); + status_t setActiveConfig(int32_t displayId, size_t configId); // reset state when an external, non-virtual display is disconnected - void disconnectDisplay(int disp); - - // create a work list for numLayers layer. sets HWC_GEOMETRY_CHANGED. - status_t createWorkList(int32_t id, size_t numLayers); - - bool supportsFramebufferTarget() const; + void disconnectDisplay(int32_t displayId); // does this display have layers handled by HWC - bool hasHwcComposition(int32_t id) const; + bool hasDeviceComposition(int32_t displayId) const; // does this display have layers handled by GLES - bool hasGlesComposition(int32_t id) const; - - // get the releaseFence file descriptor for a display's framebuffer layer. - // the release fence is only valid after commit() - sp<Fence> getAndResetReleaseFence(int32_t id); - - // needed forward declarations - class LayerListIterator; + bool hasClientComposition(int32_t displayId) const; - // return the visual id to be used to find a suitable EGLConfig for - // *ALL* displays. - int getVisualID() const; + // get the retire fence for the previous frame (i.e., corresponding to the + // last call to presentDisplay + sp<Fence> getRetireFence(int32_t displayId) const; - // Forwarding to FB HAL for pre-HWC-1.1 code (see FramebufferSurface). - int fbPost(int32_t id, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf); - int fbCompositionComplete(); - void fbDump(String8& result); + // Get last release fence for the given layer + sp<Fence> getLayerReleaseFence(int32_t displayId, + const std::shared_ptr<HWC2::Layer>& layer) const; // Set the output buffer and acquire fence for a virtual display. - // Returns INVALID_OPERATION if id is not a virtual display. - status_t setOutputBuffer(int32_t id, const sp<Fence>& acquireFence, + // Returns INVALID_OPERATION if displayId is not a virtual display. + status_t setOutputBuffer(int32_t displayId, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf); - // Get the retire fence for the last committed frame. This fence will - // signal when the h/w composer is completely finished with the frame. - // For physical displays, it is no longer being displayed. For virtual - // displays, writes to the output buffer are complete. - sp<Fence> getLastRetireFence(int32_t id) const; - - status_t setCursorPositionAsync(int32_t id, const Rect &pos); - - /* - * Interface to hardware composer's layers functionality. - * This abstracts the HAL interface to layers which can evolve in - * incompatible ways from one release to another. - * The idea is that we could extend this interface as we add - * features to h/w composer. - */ - class HWCLayerInterface { - protected: - virtual ~HWCLayerInterface() { } - public: - virtual int32_t getCompositionType() const = 0; - virtual uint32_t getHints() const = 0; - virtual sp<Fence> getAndResetReleaseFence() = 0; - virtual void setDefaultState() = 0; - virtual void setSkip(bool skip) = 0; - virtual void setIsCursorLayerHint(bool isCursor = true) = 0; - virtual void setBlending(uint32_t blending) = 0; - virtual void setTransform(uint32_t transform) = 0; - virtual void setFrame(const Rect& frame) = 0; - virtual void setCrop(const FloatRect& crop) = 0; - virtual void setVisibleRegionScreen(const Region& reg) = 0; - virtual void setSurfaceDamage(const Region& reg) = 0; - virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0; - virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0; - virtual void setAcquireFenceFd(int fenceFd) = 0; - virtual void setPlaneAlpha(uint8_t alpha) = 0; - virtual void onDisplayed() = 0; - }; - - /* - * Interface used to implement an iterator to a list - * of HWCLayer. - */ - class HWCLayer : public HWCLayerInterface { - friend class LayerListIterator; - // select the layer at the given index - virtual status_t setLayer(size_t index) = 0; - virtual HWCLayer* dup() = 0; - static HWCLayer* copy(HWCLayer *rhs) { - return rhs ? rhs->dup() : NULL; - } - protected: - virtual ~HWCLayer() { } - }; - - /* - * Iterator through a HWCLayer list. - * This behaves more or less like a forward iterator. - */ - class LayerListIterator { - friend class HWComposer; - HWCLayer* const mLayerList; - size_t mIndex; - - LayerListIterator() : mLayerList(NULL), mIndex(0) { } - - LayerListIterator(HWCLayer* layer, size_t index) - : mLayerList(layer), mIndex(index) { } - - // we don't allow assignment, because we don't need it for now - LayerListIterator& operator = (const LayerListIterator& rhs); - - public: - // copy operators - LayerListIterator(const LayerListIterator& rhs) - : mLayerList(HWCLayer::copy(rhs.mLayerList)), mIndex(rhs.mIndex) { - } - - ~LayerListIterator() { delete mLayerList; } - - // pre-increment - LayerListIterator& operator++() { - mLayerList->setLayer(++mIndex); - return *this; - } - - // dereference - HWCLayerInterface& operator * () { return *mLayerList; } - HWCLayerInterface* operator -> () { return mLayerList; } - - // comparison - bool operator == (const LayerListIterator& rhs) const { - return mIndex == rhs.mIndex; - } - bool operator != (const LayerListIterator& rhs) const { - return !operator==(rhs); - } - }; - - // Returns an iterator to the beginning of the layer list - LayerListIterator begin(int32_t id); - - // Returns an iterator to the end of the layer list - LayerListIterator end(int32_t id); - + // After SurfaceFlinger has retrieved the release fences for all the frames, + // it can call this to clear the shared pointers in the release fence map + void clearReleaseFences(int32_t displayId); // Events handling --------------------------------------------------------- - enum { - EVENT_VSYNC = HWC_EVENT_VSYNC - }; - - void eventControl(int disp, int event, int enabled); + void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled); struct DisplayConfig { uint32_t width; @@ -262,119 +145,81 @@ public: // Query display parameters. Pass in a display index (e.g. // HWC_DISPLAY_PRIMARY). - nsecs_t getRefreshTimestamp(int disp) const; - sp<Fence> getDisplayFence(int disp) const; - uint32_t getFormat(int disp) const; - bool isConnected(int disp) const; - - // These return the values for the current config of a given display index. - // To get the values for all configs, use getConfigs below. - uint32_t getWidth(int disp) const; - uint32_t getHeight(int disp) const; - float getDpiX(int disp) const; - float getDpiY(int disp) const; - nsecs_t getRefreshPeriod(int disp) const; - - const Vector<DisplayConfig>& getConfigs(int disp) const; - size_t getCurrentConfig(int disp) const; - - status_t setVirtualDisplayProperties(int32_t id, uint32_t w, uint32_t h, - uint32_t format); - - // this class is only used to fake the VSync event on systems that don't - // have it. - class VSyncThread : public Thread { - HWComposer& mHwc; - mutable Mutex mLock; - Condition mCondition; - bool mEnabled; - mutable nsecs_t mNextFakeVSync; - nsecs_t mRefreshPeriod; - virtual void onFirstRef(); - virtual bool threadLoop(); - public: - VSyncThread(HWComposer& hwc); - void setEnabled(bool enabled); - }; + nsecs_t getRefreshTimestamp(int32_t disp) const; + bool isConnected(int32_t disp) const; + + // Non-const because it can update configMap inside of mDisplayData + std::vector<std::shared_ptr<const HWC2::Display::Config>> + getConfigs(int32_t displayId) const; - friend class VSyncThread; + std::shared_ptr<const HWC2::Display::Config> + getActiveConfig(int32_t displayId) const; // for debugging ---------------------------------------------------------- void dump(String8& out) const; private: + static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2; + void loadHwcModule(); - int loadFbHalModule(); - LayerListIterator getLayerIterator(int32_t id, size_t index); + bool isValidDisplay(int32_t displayId) const; + static void validateChange(HWC2::Composition from, HWC2::Composition to); struct cb_context; - static void hook_invalidate(const struct hwc_procs* procs); - static void hook_vsync(const struct hwc_procs* procs, int disp, + void invalidate(const std::shared_ptr<HWC2::Display>& display); + void vsync(const std::shared_ptr<HWC2::Display>& display, int64_t timestamp); - static void hook_hotplug(const struct hwc_procs* procs, int disp, - int connected); - - inline void invalidate(); - inline void vsync(int disp, int64_t timestamp); - inline void hotplug(int disp, int connected); - - status_t queryDisplayProperties(int disp); - - status_t setFramebufferTarget(int32_t id, - const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf); + void hotplug(const std::shared_ptr<HWC2::Display>& display, + HWC2::Connection connected); struct DisplayData { DisplayData(); ~DisplayData(); - Vector<DisplayConfig> configs; - size_t currentConfig; - uint32_t format; // pixel format from FB hal, for pre-hwc-1.1 - bool connected; - bool hasFbComp; - bool hasOvComp; - size_t capacity; - hwc_display_contents_1* list; - hwc_layer_1* framebufferTarget; - buffer_handle_t fbTargetHandle; + void reset(); + + bool hasClientComposition; + bool hasDeviceComposition; + std::shared_ptr<HWC2::Display> hwcDisplay; + HWC2::DisplayRequest displayRequests; sp<Fence> lastRetireFence; // signals when the last set op retires - sp<Fence> lastDisplayFence; // signals when the last set op takes - // effect on screen + std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> + releaseFences; buffer_handle_t outbufHandle; sp<Fence> outbufAcquireFence; + mutable std::unordered_map<int32_t, + std::shared_ptr<const HWC2::Display::Config>> configMap; - // protected by mEventControlLock - int32_t events; + // protected by mVsyncLock + HWC2::Vsync vsyncEnabled; }; sp<SurfaceFlinger> mFlinger; - framebuffer_device_t* mFbDev; - struct hwc_composer_device_1* mHwc; - // invariant: mLists[0] != NULL iff mHwc != NULL - // mLists[i>0] can be NULL. that display is to be ignored - struct hwc_display_contents_1* mLists[MAX_HWC_DISPLAYS]; - DisplayData mDisplayData[MAX_HWC_DISPLAYS]; + std::unique_ptr<HWC2On1Adapter> mAdapter; + std::unique_ptr<HWC2::Device> mHwcDevice; + std::vector<DisplayData> mDisplayData; + std::set<size_t> mFreeDisplaySlots; + std::unordered_map<hwc2_display_t, int32_t> mHwcDisplaySlots; // protect mDisplayData from races between prepare and dump mutable Mutex mDisplayLock; - size_t mNumDisplays; cb_context* mCBContext; - EventHandler& mEventHandler; + EventHandler* mEventHandler; size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; - sp<VSyncThread> mVSyncThread; - bool mDebugForceFakeVSync; - BitSet32 mAllocatedDisplayIDs; + uint32_t mRemainingHwcVirtualDisplays; // protected by mLock mutable Mutex mLock; - mutable nsecs_t mLastHwVSync[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; + mutable std::unordered_map<int32_t, nsecs_t> mLastHwVSync; // thread-safe - mutable Mutex mEventControlLock; + mutable Mutex mVsyncLock; }; // --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_SF_HWCOMPOSER_H + +#endif // #ifdef USE_HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp new file mode 100644 index 0000000000..d37fcb2b8a --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp @@ -0,0 +1,1322 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <inttypes.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/misc.h> +#include <utils/NativeHandle.h> +#include <utils/String8.h> +#include <utils/Thread.h> +#include <utils/Trace.h> +#include <utils/Vector.h> + +#include <ui/GraphicBuffer.h> + +#include <hardware/hardware.h> +#include <hardware/hwcomposer.h> + +#include <android/configuration.h> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include "HWComposer.h" + +#include "../Layer.h" // needed only for debugging +#include "../SurfaceFlinger.h" + +namespace android { + +#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION + +static uint32_t hwcApiVersion(const hwc_composer_device_1_t* hwc) { + uint32_t hwcVersion = hwc->common.version; + return hwcVersion & HARDWARE_API_VERSION_2_MAJ_MIN_MASK; +} + +static uint32_t hwcHeaderVersion(const hwc_composer_device_1_t* hwc) { + uint32_t hwcVersion = hwc->common.version; + return hwcVersion & HARDWARE_API_VERSION_2_HEADER_MASK; +} + +static bool hwcHasApiVersion(const hwc_composer_device_1_t* hwc, + uint32_t version) { + return hwcApiVersion(hwc) >= (version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK); +} + +// --------------------------------------------------------------------------- + +struct HWComposer::cb_context { + struct callbacks : public hwc_procs_t { + // these are here to facilitate the transition when adding + // new callbacks (an implementation can check for NULL before + // calling a new callback). + void (*zero[4])(void); + }; + callbacks procs; + HWComposer* hwc; +}; + +// --------------------------------------------------------------------------- + +HWComposer::HWComposer( + const sp<SurfaceFlinger>& flinger, + EventHandler& handler) + : mFlinger(flinger), + mFbDev(0), mHwc(0), mNumDisplays(1), + mCBContext(new cb_context), + mEventHandler(handler), + mDebugForceFakeVSync(false) +{ + for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) { + mLists[i] = 0; + } + + for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) { + mLastHwVSync[i] = 0; + mVSyncCounts[i] = 0; + } + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.no_hw_vsync", value, "0"); + mDebugForceFakeVSync = atoi(value); + + bool needVSyncThread = true; + + // Note: some devices may insist that the FB HAL be opened before HWC. + int fberr = loadFbHalModule(); + loadHwcModule(); + + if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + // close FB HAL if we don't needed it. + // FIXME: this is temporary until we're not forced to open FB HAL + // before HWC. + framebuffer_close(mFbDev); + mFbDev = NULL; + } + + // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory. + if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) + && !mFbDev) { + ALOGE("ERROR: failed to open framebuffer (%s), aborting", + strerror(-fberr)); + abort(); + } + + // these display IDs are always reserved + for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { + mAllocatedDisplayIDs.markBit(i); + } + + if (mHwc) { + ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER, + (hwcApiVersion(mHwc) >> 24) & 0xff, + (hwcApiVersion(mHwc) >> 16) & 0xff); + if (mHwc->registerProcs) { + mCBContext->hwc = this; + mCBContext->procs.invalidate = &hook_invalidate; + mCBContext->procs.vsync = &hook_vsync; + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) + mCBContext->procs.hotplug = &hook_hotplug; + else + mCBContext->procs.hotplug = NULL; + memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero)); + mHwc->registerProcs(mHwc, &mCBContext->procs); + } + + // don't need a vsync thread if we have a hardware composer + needVSyncThread = false; + // always turn vsync off when we start + eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0); + + // the number of displays we actually have depends on the + // hw composer version + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + // 1.3 adds support for virtual displays + mNumDisplays = MAX_HWC_DISPLAYS; + } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + // 1.1 adds support for multiple displays + mNumDisplays = NUM_BUILTIN_DISPLAYS; + } else { + mNumDisplays = 1; + } + } + + if (mFbDev) { + ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)), + "should only have fbdev if no hwc or hwc is 1.0"); + + DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]); + disp.connected = true; + disp.format = mFbDev->format; + DisplayConfig config = DisplayConfig(); + config.width = mFbDev->width; + config.height = mFbDev->height; + config.xdpi = mFbDev->xdpi; + config.ydpi = mFbDev->ydpi; + config.refresh = nsecs_t(1e9 / mFbDev->fps); + disp.configs.push_back(config); + disp.currentConfig = 0; + } else if (mHwc) { + // here we're guaranteed to have at least HWC 1.1 + for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) { + queryDisplayProperties(i); + } + } + + if (needVSyncThread) { + // we don't have VSYNC support, we need to fake it + mVSyncThread = new VSyncThread(*this); + } +} + +HWComposer::~HWComposer() { + if (mHwc) { + eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0); + } + if (mVSyncThread != NULL) { + mVSyncThread->requestExitAndWait(); + } + if (mHwc) { + hwc_close_1(mHwc); + } + if (mFbDev) { + framebuffer_close(mFbDev); + } + delete mCBContext; +} + +// Load and prepare the hardware composer module. Sets mHwc. +void HWComposer::loadHwcModule() +{ + hw_module_t const* module; + + if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) { + ALOGE("%s module not found", HWC_HARDWARE_MODULE_ID); + return; + } + + int err = hwc_open_1(module, &mHwc); + if (err) { + ALOGE("%s device failed to initialize (%s)", + HWC_HARDWARE_COMPOSER, strerror(-err)); + return; + } + + if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_0) || + hwcHeaderVersion(mHwc) < MIN_HWC_HEADER_VERSION || + hwcHeaderVersion(mHwc) > HWC_HEADER_VERSION) { + ALOGE("%s device version %#x unsupported, will not be used", + HWC_HARDWARE_COMPOSER, mHwc->common.version); + hwc_close_1(mHwc); + mHwc = NULL; + return; + } +} + +// Load and prepare the FB HAL, which uses the gralloc module. Sets mFbDev. +int HWComposer::loadFbHalModule() +{ + hw_module_t const* module; + + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + if (err != 0) { + ALOGE("%s module not found", GRALLOC_HARDWARE_MODULE_ID); + return err; + } + + return framebuffer_open(module, &mFbDev); +} + +status_t HWComposer::initCheck() const { + return mHwc ? NO_ERROR : NO_INIT; +} + +void HWComposer::hook_invalidate(const struct hwc_procs* procs) { + cb_context* ctx = reinterpret_cast<cb_context*>( + const_cast<hwc_procs_t*>(procs)); + ctx->hwc->invalidate(); +} + +void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp, + int64_t timestamp) { + cb_context* ctx = reinterpret_cast<cb_context*>( + const_cast<hwc_procs_t*>(procs)); + ctx->hwc->vsync(disp, timestamp); +} + +void HWComposer::hook_hotplug(const struct hwc_procs* procs, int disp, + int connected) { + cb_context* ctx = reinterpret_cast<cb_context*>( + const_cast<hwc_procs_t*>(procs)); + ctx->hwc->hotplug(disp, connected); +} + +void HWComposer::invalidate() { + mFlinger->repaintEverything(); +} + +void HWComposer::vsync(int disp, int64_t timestamp) { + if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) { + { + Mutex::Autolock _l(mLock); + + // There have been reports of HWCs that signal several vsync events + // with the same timestamp when turning the display off and on. This + // is a bug in the HWC implementation, but filter the extra events + // out here so they don't cause havoc downstream. + if (timestamp == mLastHwVSync[disp]) { + ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")", + timestamp); + return; + } + + mLastHwVSync[disp] = timestamp; + } + + char tag[16]; + snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp); + ATRACE_INT(tag, ++mVSyncCounts[disp] & 1); + + mEventHandler.onVSyncReceived(disp, timestamp); + } +} + +void HWComposer::hotplug(int disp, int connected) { + if (disp >= VIRTUAL_DISPLAY_ID_BASE) { + ALOGE("hotplug event received for invalid display: disp=%d connected=%d", + disp, connected); + return; + } + queryDisplayProperties(disp); + // Do not teardown or recreate the primary display + if (disp != HWC_DISPLAY_PRIMARY) { + mEventHandler.onHotplugReceived(disp, bool(connected)); + } +} + +static float getDefaultDensity(uint32_t width, uint32_t height) { + // Default density is based on TVs: 1080p displays get XHIGH density, + // lower-resolution displays get TV density. Maybe eventually we'll need + // to update it for 4K displays, though hopefully those just report + // accurate DPI information to begin with. This is also used for virtual + // displays and even primary displays with older hwcomposers, so be + // careful about orientation. + + uint32_t h = width < height ? width : height; + if (h >= 1080) return ACONFIGURATION_DENSITY_XHIGH; + else return ACONFIGURATION_DENSITY_TV; +} + +static const uint32_t DISPLAY_ATTRIBUTES[] = { + HWC_DISPLAY_VSYNC_PERIOD, + HWC_DISPLAY_WIDTH, + HWC_DISPLAY_HEIGHT, + HWC_DISPLAY_DPI_X, + HWC_DISPLAY_DPI_Y, + HWC_DISPLAY_COLOR_TRANSFORM, + HWC_DISPLAY_NO_ATTRIBUTE, +}; +#define NUM_DISPLAY_ATTRIBUTES (sizeof(DISPLAY_ATTRIBUTES) / sizeof(DISPLAY_ATTRIBUTES)[0]) + +static const uint32_t PRE_HWC15_DISPLAY_ATTRIBUTES[] = { + HWC_DISPLAY_VSYNC_PERIOD, + HWC_DISPLAY_WIDTH, + HWC_DISPLAY_HEIGHT, + HWC_DISPLAY_DPI_X, + HWC_DISPLAY_DPI_Y, + HWC_DISPLAY_NO_ATTRIBUTE, +}; + +status_t HWComposer::queryDisplayProperties(int disp) { + + LOG_ALWAYS_FATAL_IF(!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)); + + // use zero as default value for unspecified attributes + int32_t values[NUM_DISPLAY_ATTRIBUTES - 1]; + memset(values, 0, sizeof(values)); + + const size_t MAX_NUM_CONFIGS = 128; + uint32_t configs[MAX_NUM_CONFIGS] = {0}; + size_t numConfigs = MAX_NUM_CONFIGS; + status_t err = mHwc->getDisplayConfigs(mHwc, disp, configs, &numConfigs); + if (err != NO_ERROR) { + // this can happen if an unpluggable display is not connected + mDisplayData[disp].connected = false; + return err; + } + + mDisplayData[disp].currentConfig = 0; + for (size_t c = 0; c < numConfigs; ++c) { + err = mHwc->getDisplayAttributes(mHwc, disp, configs[c], + DISPLAY_ATTRIBUTES, values); + // If this is a pre-1.5 HWC, it may not know about color transform, so + // try again with a smaller set of attributes + if (err != NO_ERROR) { + err = mHwc->getDisplayAttributes(mHwc, disp, configs[c], + PRE_HWC15_DISPLAY_ATTRIBUTES, values); + } + if (err != NO_ERROR) { + // we can't get this display's info. turn it off. + mDisplayData[disp].connected = false; + return err; + } + + DisplayConfig config = DisplayConfig(); + for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) { + switch (DISPLAY_ATTRIBUTES[i]) { + case HWC_DISPLAY_VSYNC_PERIOD: + config.refresh = nsecs_t(values[i]); + break; + case HWC_DISPLAY_WIDTH: + config.width = values[i]; + break; + case HWC_DISPLAY_HEIGHT: + config.height = values[i]; + break; + case HWC_DISPLAY_DPI_X: + config.xdpi = values[i] / 1000.0f; + break; + case HWC_DISPLAY_DPI_Y: + config.ydpi = values[i] / 1000.0f; + break; + case HWC_DISPLAY_COLOR_TRANSFORM: + config.colorTransform = values[i]; + break; + default: + ALOG_ASSERT(false, "unknown display attribute[%zu] %#x", + i, DISPLAY_ATTRIBUTES[i]); + break; + } + } + + if (config.xdpi == 0.0f || config.ydpi == 0.0f) { + float dpi = getDefaultDensity(config.width, config.height); + config.xdpi = dpi; + config.ydpi = dpi; + } + + mDisplayData[disp].configs.push_back(config); + } + + // FIXME: what should we set the format to? + mDisplayData[disp].format = HAL_PIXEL_FORMAT_RGBA_8888; + mDisplayData[disp].connected = true; + return NO_ERROR; +} + +status_t HWComposer::setVirtualDisplayProperties(int32_t id, + uint32_t w, uint32_t h, uint32_t format) { + if (id < VIRTUAL_DISPLAY_ID_BASE || id >= int32_t(mNumDisplays) || + !mAllocatedDisplayIDs.hasBit(id)) { + return BAD_INDEX; + } + size_t configId = mDisplayData[id].currentConfig; + mDisplayData[id].format = format; + DisplayConfig& config = mDisplayData[id].configs.editItemAt(configId); + config.width = w; + config.height = h; + config.xdpi = config.ydpi = getDefaultDensity(w, h); + return NO_ERROR; +} + +int32_t HWComposer::allocateDisplayId() { + if (mAllocatedDisplayIDs.count() >= mNumDisplays) { + return NO_MEMORY; + } + int32_t id = mAllocatedDisplayIDs.firstUnmarkedBit(); + mAllocatedDisplayIDs.markBit(id); + mDisplayData[id].connected = true; + mDisplayData[id].configs.resize(1); + mDisplayData[id].currentConfig = 0; + return id; +} + +status_t HWComposer::freeDisplayId(int32_t id) { + if (id < NUM_BUILTIN_DISPLAYS) { + // cannot free the reserved IDs + return BAD_VALUE; + } + if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { + return BAD_INDEX; + } + mAllocatedDisplayIDs.clearBit(id); + mDisplayData[id].connected = false; + return NO_ERROR; +} + +nsecs_t HWComposer::getRefreshTimestamp(int disp) const { + // this returns the last refresh timestamp. + // if the last one is not available, we estimate it based on + // the refresh period and whatever closest timestamp we have. + Mutex::Autolock _l(mLock); + nsecs_t now = systemTime(CLOCK_MONOTONIC); + size_t configId = mDisplayData[disp].currentConfig; + return now - ((now - mLastHwVSync[disp]) % + mDisplayData[disp].configs[configId].refresh); +} + +sp<Fence> HWComposer::getDisplayFence(int disp) const { + return mDisplayData[disp].lastDisplayFence; +} + +uint32_t HWComposer::getFormat(int disp) const { + if (static_cast<uint32_t>(disp) >= MAX_HWC_DISPLAYS || !mAllocatedDisplayIDs.hasBit(disp)) { + return HAL_PIXEL_FORMAT_RGBA_8888; + } else { + return mDisplayData[disp].format; + } +} + +bool HWComposer::isConnected(int disp) const { + return mDisplayData[disp].connected; +} + +uint32_t HWComposer::getWidth(int disp) const { + size_t currentConfig = mDisplayData[disp].currentConfig; + return mDisplayData[disp].configs[currentConfig].width; +} + +uint32_t HWComposer::getHeight(int disp) const { + size_t currentConfig = mDisplayData[disp].currentConfig; + return mDisplayData[disp].configs[currentConfig].height; +} + +float HWComposer::getDpiX(int disp) const { + size_t currentConfig = mDisplayData[disp].currentConfig; + return mDisplayData[disp].configs[currentConfig].xdpi; +} + +float HWComposer::getDpiY(int disp) const { + size_t currentConfig = mDisplayData[disp].currentConfig; + return mDisplayData[disp].configs[currentConfig].ydpi; +} + +nsecs_t HWComposer::getRefreshPeriod(int disp) const { + size_t currentConfig = mDisplayData[disp].currentConfig; + return mDisplayData[disp].configs[currentConfig].refresh; +} + +const Vector<HWComposer::DisplayConfig>& HWComposer::getConfigs(int disp) const { + return mDisplayData[disp].configs; +} + +size_t HWComposer::getCurrentConfig(int disp) const { + return mDisplayData[disp].currentConfig; +} + +void HWComposer::eventControl(int disp, int event, int enabled) { + if (uint32_t(disp)>31 || !mAllocatedDisplayIDs.hasBit(disp)) { + ALOGD("eventControl ignoring event %d on unallocated disp %d (en=%d)", + event, disp, enabled); + return; + } + if (event != EVENT_VSYNC) { + ALOGW("eventControl got unexpected event %d (disp=%d en=%d)", + event, disp, enabled); + return; + } + status_t err = NO_ERROR; + if (mHwc && !mDebugForceFakeVSync) { + // NOTE: we use our own internal lock here because we have to call + // into the HWC with the lock held, and we want to make sure + // that even if HWC blocks (which it shouldn't), it won't + // affect other threads. + Mutex::Autolock _l(mEventControlLock); + const int32_t eventBit = 1UL << event; + const int32_t newValue = enabled ? eventBit : 0; + const int32_t oldValue = mDisplayData[disp].events & eventBit; + if (newValue != oldValue) { + ATRACE_CALL(); + err = mHwc->eventControl(mHwc, disp, event, enabled); + if (!err) { + int32_t& events(mDisplayData[disp].events); + events = (events & ~eventBit) | newValue; + + char tag[16]; + snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp); + ATRACE_INT(tag, enabled); + } + } + // error here should not happen -- not sure what we should + // do if it does. + ALOGE_IF(err, "eventControl(%d, %d) failed %s", + event, enabled, strerror(-err)); + } + + if (err == NO_ERROR && mVSyncThread != NULL) { + mVSyncThread->setEnabled(enabled); + } +} + +status_t HWComposer::createWorkList(int32_t id, size_t numLayers) { + if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { + return BAD_INDEX; + } + + if (mHwc) { + DisplayData& disp(mDisplayData[id]); + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + // we need space for the HWC_FRAMEBUFFER_TARGET + numLayers++; + } + if (disp.capacity < numLayers || disp.list == NULL) { + size_t size = sizeof(hwc_display_contents_1_t) + + numLayers * sizeof(hwc_layer_1_t); + free(disp.list); + disp.list = (hwc_display_contents_1_t*)malloc(size); + disp.capacity = numLayers; + } + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1]; + memset(disp.framebufferTarget, 0, sizeof(hwc_layer_1_t)); + const DisplayConfig& currentConfig = + disp.configs[disp.currentConfig]; + const hwc_rect_t r = { 0, 0, + (int) currentConfig.width, (int) currentConfig.height }; + disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET; + disp.framebufferTarget->hints = 0; + disp.framebufferTarget->flags = 0; + disp.framebufferTarget->handle = disp.fbTargetHandle; + disp.framebufferTarget->transform = 0; + disp.framebufferTarget->blending = HWC_BLENDING_PREMULT; + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + disp.framebufferTarget->sourceCropf.left = 0; + disp.framebufferTarget->sourceCropf.top = 0; + disp.framebufferTarget->sourceCropf.right = + currentConfig.width; + disp.framebufferTarget->sourceCropf.bottom = + currentConfig.height; + } else { + disp.framebufferTarget->sourceCrop = r; + } + disp.framebufferTarget->displayFrame = r; + disp.framebufferTarget->visibleRegionScreen.numRects = 1; + disp.framebufferTarget->visibleRegionScreen.rects = + &disp.framebufferTarget->displayFrame; + disp.framebufferTarget->acquireFenceFd = -1; + disp.framebufferTarget->releaseFenceFd = -1; + disp.framebufferTarget->planeAlpha = 0xFF; + } + disp.list->retireFenceFd = -1; + disp.list->flags = HWC_GEOMETRY_CHANGED; + disp.list->numHwLayers = numLayers; + } + return NO_ERROR; +} + +status_t HWComposer::setFramebufferTarget(int32_t id, + const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf) { + if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { + return BAD_INDEX; + } + DisplayData& disp(mDisplayData[id]); + if (!disp.framebufferTarget) { + // this should never happen, but apparently eglCreateWindowSurface() + // triggers a Surface::queueBuffer() on some + // devices (!?) -- log and ignore. + ALOGE("HWComposer: framebufferTarget is null"); + return NO_ERROR; + } + + int acquireFenceFd = -1; + if (acquireFence->isValid()) { + acquireFenceFd = acquireFence->dup(); + } + + // ALOGD("fbPost: handle=%p, fence=%d", buf->handle, acquireFenceFd); + disp.fbTargetHandle = buf->handle; + disp.framebufferTarget->handle = disp.fbTargetHandle; + disp.framebufferTarget->acquireFenceFd = acquireFenceFd; + return NO_ERROR; +} + +status_t HWComposer::prepare() { + Mutex::Autolock _l(mDisplayLock); + for (size_t i=0 ; i<mNumDisplays ; i++) { + DisplayData& disp(mDisplayData[i]); + if (disp.framebufferTarget) { + // make sure to reset the type to HWC_FRAMEBUFFER_TARGET + // DO NOT reset the handle field to NULL, because it's possible + // that we have nothing to redraw (eg: eglSwapBuffers() not called) + // in which case, we should continue to use the same buffer. + LOG_FATAL_IF(disp.list == NULL); + disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET; + } + if (!disp.connected && disp.list != NULL) { + ALOGW("WARNING: disp %zu: connected, non-null list, layers=%zu", + i, disp.list->numHwLayers); + } + mLists[i] = disp.list; + if (mLists[i]) { + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + mLists[i]->outbuf = disp.outbufHandle; + mLists[i]->outbufAcquireFenceFd = -1; + } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + // garbage data to catch improper use + mLists[i]->dpy = (hwc_display_t)0xDEADBEEF; + mLists[i]->sur = (hwc_surface_t)0xDEADBEEF; + } else { + mLists[i]->dpy = EGL_NO_DISPLAY; + mLists[i]->sur = EGL_NO_SURFACE; + } + } + } + + int err = mHwc->prepare(mHwc, mNumDisplays, mLists); + ALOGE_IF(err, "HWComposer: prepare failed (%s)", strerror(-err)); + + if (err == NO_ERROR) { + // here we're just making sure that "skip" layers are set + // to HWC_FRAMEBUFFER and we're also counting how many layers + // we have of each type. + // + // If there are no window layers, we treat the display has having FB + // composition, because SurfaceFlinger will use GLES to draw the + // wormhole region. + for (size_t i=0 ; i<mNumDisplays ; i++) { + DisplayData& disp(mDisplayData[i]); + disp.hasFbComp = false; + disp.hasOvComp = false; + if (disp.list) { + for (size_t i=0 ; i<disp.list->numHwLayers ; i++) { + hwc_layer_1_t& l = disp.list->hwLayers[i]; + + //ALOGD("prepare: %d, type=%d, handle=%p", + // i, l.compositionType, l.handle); + + if (l.flags & HWC_SKIP_LAYER) { + l.compositionType = HWC_FRAMEBUFFER; + } + if (l.compositionType == HWC_FRAMEBUFFER) { + disp.hasFbComp = true; + } + if (l.compositionType == HWC_OVERLAY) { + disp.hasOvComp = true; + } + if (l.compositionType == HWC_CURSOR_OVERLAY) { + disp.hasOvComp = true; + } + } + if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) { + disp.hasFbComp = true; + } + } else { + disp.hasFbComp = true; + } + } + } + return (status_t)err; +} + +bool HWComposer::hasHwcComposition(int32_t id) const { + if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) + return false; + return mDisplayData[id].hasOvComp; +} + +bool HWComposer::hasGlesComposition(int32_t id) const { + if (!mHwc || uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) + return true; + return mDisplayData[id].hasFbComp; +} + +sp<Fence> HWComposer::getAndResetReleaseFence(int32_t id) { + if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) + return Fence::NO_FENCE; + + int fd = INVALID_OPERATION; + if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + const DisplayData& disp(mDisplayData[id]); + if (disp.framebufferTarget) { + fd = disp.framebufferTarget->releaseFenceFd; + disp.framebufferTarget->acquireFenceFd = -1; + disp.framebufferTarget->releaseFenceFd = -1; + } + } + return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE; +} + +status_t HWComposer::commit() { + int err = NO_ERROR; + if (mHwc) { + if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + // On version 1.0, the OpenGL ES target surface is communicated + // by the (dpy, sur) fields and we are guaranteed to have only + // a single display. + mLists[0]->dpy = eglGetCurrentDisplay(); + mLists[0]->sur = eglGetCurrentSurface(EGL_DRAW); + } + + for (size_t i=VIRTUAL_DISPLAY_ID_BASE; i<mNumDisplays; i++) { + DisplayData& disp(mDisplayData[i]); + if (disp.outbufHandle) { + mLists[i]->outbuf = disp.outbufHandle; + mLists[i]->outbufAcquireFenceFd = + disp.outbufAcquireFence->dup(); + } + } + + err = mHwc->set(mHwc, mNumDisplays, mLists); + + for (size_t i=0 ; i<mNumDisplays ; i++) { + DisplayData& disp(mDisplayData[i]); + disp.lastDisplayFence = disp.lastRetireFence; + disp.lastRetireFence = Fence::NO_FENCE; + if (disp.list) { + if (disp.list->retireFenceFd != -1) { + disp.lastRetireFence = new Fence(disp.list->retireFenceFd); + disp.list->retireFenceFd = -1; + } + disp.list->flags &= ~HWC_GEOMETRY_CHANGED; + } + } + } + return (status_t)err; +} + +status_t HWComposer::setPowerMode(int disp, int mode) { + LOG_FATAL_IF(disp >= VIRTUAL_DISPLAY_ID_BASE); + if (mHwc) { + if (mode == HWC_POWER_MODE_OFF) { + eventControl(disp, HWC_EVENT_VSYNC, 0); + } + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) { + return (status_t)mHwc->setPowerMode(mHwc, disp, mode); + } else { + return (status_t)mHwc->blank(mHwc, disp, + mode == HWC_POWER_MODE_OFF ? 1 : 0); + } + } + return NO_ERROR; +} + +status_t HWComposer::setActiveConfig(int disp, int mode) { + LOG_FATAL_IF(disp >= VIRTUAL_DISPLAY_ID_BASE); + DisplayData& dd(mDisplayData[disp]); + dd.currentConfig = mode; + if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) { + return (status_t)mHwc->setActiveConfig(mHwc, disp, mode); + } else { + LOG_FATAL_IF(mode != 0); + } + return NO_ERROR; +} + +void HWComposer::disconnectDisplay(int disp) { + LOG_ALWAYS_FATAL_IF(disp < 0 || disp == HWC_DISPLAY_PRIMARY); + DisplayData& dd(mDisplayData[disp]); + free(dd.list); + dd.list = NULL; + dd.framebufferTarget = NULL; // points into dd.list + dd.fbTargetHandle = NULL; + dd.outbufHandle = NULL; + dd.lastRetireFence = Fence::NO_FENCE; + dd.lastDisplayFence = Fence::NO_FENCE; + dd.outbufAcquireFence = Fence::NO_FENCE; + // clear all the previous configs and repopulate when a new + // device is added + dd.configs.clear(); +} + +int HWComposer::getVisualID() const { + if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + // FIXME: temporary hack until HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED + // is supported by the implementation. we can only be in this case + // if we have HWC 1.1 + return HAL_PIXEL_FORMAT_RGBA_8888; + //return HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + } else { + return mFbDev->format; + } +} + +bool HWComposer::supportsFramebufferTarget() const { + return (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)); +} + +int HWComposer::fbPost(int32_t id, + const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) { + if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + return setFramebufferTarget(id, acquireFence, buffer); + } else { + acquireFence->waitForever("HWComposer::fbPost"); + return mFbDev->post(mFbDev, buffer->handle); + } +} + +int HWComposer::fbCompositionComplete() { + if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) + return NO_ERROR; + + if (mFbDev->compositionComplete) { + return mFbDev->compositionComplete(mFbDev); + } else { + return INVALID_OPERATION; + } +} + +void HWComposer::fbDump(String8& result) { + if (mFbDev && mFbDev->common.version >= 1 && mFbDev->dump) { + const size_t SIZE = 4096; + char buffer[SIZE]; + mFbDev->dump(mFbDev, buffer, SIZE); + result.append(buffer); + } +} + +status_t HWComposer::setOutputBuffer(int32_t id, const sp<Fence>& acquireFence, + const sp<GraphicBuffer>& buf) { + if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) + return BAD_INDEX; + if (id < VIRTUAL_DISPLAY_ID_BASE) + return INVALID_OPERATION; + + DisplayData& disp(mDisplayData[id]); + disp.outbufHandle = buf->handle; + disp.outbufAcquireFence = acquireFence; + return NO_ERROR; +} + +sp<Fence> HWComposer::getLastRetireFence(int32_t id) const { + if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) + return Fence::NO_FENCE; + return mDisplayData[id].lastRetireFence; +} + +status_t HWComposer::setCursorPositionAsync(int32_t id, const Rect& pos) +{ + if (mHwc->setCursorPositionAsync) { + return (status_t)mHwc->setCursorPositionAsync(mHwc, id, pos.left, pos.top); + } + else { + return NO_ERROR; + } +} + +/* + * Helper template to implement a concrete HWCLayer + * This holds the pointer to the concrete hwc layer type + * and implements the "iterable" side of HWCLayer. + */ +template<typename CONCRETE, typename HWCTYPE> +class Iterable : public HWComposer::HWCLayer { +protected: + HWCTYPE* const mLayerList; + HWCTYPE* mCurrentLayer; + Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer) { } + inline HWCTYPE const * getLayer() const { return mCurrentLayer; } + inline HWCTYPE* getLayer() { return mCurrentLayer; } + virtual ~Iterable() { } +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) { + mCurrentLayer = &mLayerList[index]; + return NO_ERROR; + } +}; + +/* + * Concrete implementation of HWCLayer for HWC_DEVICE_API_VERSION_1_0. + * This implements the HWCLayer side of HWCIterableLayer. + */ +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) { } + + virtual int32_t getCompositionType() const { + return getLayer()->compositionType; + } + virtual uint32_t getHints() const { + return getLayer()->hints; + } + virtual sp<Fence> getAndResetReleaseFence() { + int fd = getLayer()->releaseFenceFd; + getLayer()->releaseFenceFd = -1; + return fd >= 0 ? new Fence(fd) : Fence::NO_FENCE; + } + virtual void setAcquireFenceFd(int fenceFd) { + getLayer()->acquireFenceFd = fenceFd; + } + virtual void setPerFrameDefaultState() { + //getLayer()->compositionType = HWC_FRAMEBUFFER; + } + virtual void setPlaneAlpha(uint8_t alpha) { + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_2)) { + getLayer()->planeAlpha = alpha; + } else { + if (alpha < 0xFF) { + getLayer()->flags |= HWC_SKIP_LAYER; + } + } + } + virtual void setDefaultState() { + hwc_layer_1_t* const l = getLayer(); + l->compositionType = HWC_FRAMEBUFFER; + l->hints = 0; + l->flags = HWC_SKIP_LAYER; + l->handle = 0; + l->transform = 0; + l->blending = HWC_BLENDING_NONE; + l->visibleRegionScreen.numRects = 0; + l->visibleRegionScreen.rects = NULL; + l->acquireFenceFd = -1; + l->releaseFenceFd = -1; + l->planeAlpha = 0xFF; + } + virtual void setSkip(bool skip) { + if (skip) { + getLayer()->flags |= HWC_SKIP_LAYER; + } else { + getLayer()->flags &= ~HWC_SKIP_LAYER; + } + } + virtual void setIsCursorLayerHint(bool isCursor) { + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_4)) { + if (isCursor) { + getLayer()->flags |= HWC_IS_CURSOR_LAYER; + } + else { + getLayer()->flags &= ~HWC_IS_CURSOR_LAYER; + } + } + } + virtual void setBlending(uint32_t blending) { + getLayer()->blending = blending; + } + virtual void setTransform(uint32_t transform) { + getLayer()->transform = transform; + } + virtual void setFrame(const Rect& frame) { + getLayer()->displayFrame = reinterpret_cast<hwc_rect_t const&>(frame); + } + virtual void setCrop(const FloatRect& crop) { + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + getLayer()->sourceCropf = reinterpret_cast<hwc_frect_t const&>(crop); + } else { + /* + * Since h/w composer didn't support a flot crop rect before version 1.3, + * using integer coordinates instead produces a different output from the GL code in + * Layer::drawWithOpenGL(). The difference can be large if the buffer crop to + * window size ratio is large and a window crop is defined + * (i.e.: if we scale the buffer a lot and we also crop it with a window crop). + */ + hwc_rect_t& r = getLayer()->sourceCrop; + r.left = int(ceilf(crop.left)); + r.top = int(ceilf(crop.top)); + r.right = int(floorf(crop.right)); + r.bottom= int(floorf(crop.bottom)); + } + } + virtual void setVisibleRegionScreen(const Region& reg) { + hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen; + mVisibleRegion = reg; + visibleRegion.rects = reinterpret_cast<hwc_rect_t const *>( + mVisibleRegion.getArray(&visibleRegion.numRects)); + } + virtual void setSurfaceDamage(const Region& reg) { + if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_5)) { + return; + } + hwc_region_t& surfaceDamage = getLayer()->surfaceDamage; + // We encode default full-screen damage as INVALID_RECT upstream, but as + // 0 rects for HWComposer + if (reg.isRect() && reg.getBounds() == Rect::INVALID_RECT) { + surfaceDamage.numRects = 0; + surfaceDamage.rects = NULL; + return; + } + mSurfaceDamage = reg; + surfaceDamage.rects = reinterpret_cast<hwc_rect_t const *>( + mSurfaceDamage.getArray(&surfaceDamage.numRects)); + } + virtual void setSidebandStream(const sp<NativeHandle>& stream) { + ALOG_ASSERT(stream->handle() != NULL); + getLayer()->compositionType = HWC_SIDEBAND; + getLayer()->sidebandStream = stream->handle(); + } + virtual void setBuffer(const sp<GraphicBuffer>& buffer) { + if (buffer == 0 || buffer->handle == 0) { + getLayer()->compositionType = HWC_FRAMEBUFFER; + getLayer()->flags |= HWC_SKIP_LAYER; + getLayer()->handle = 0; + } else { + if (getLayer()->compositionType == HWC_SIDEBAND) { + // If this was a sideband layer but the stream was removed, reset + // it to FRAMEBUFFER. The HWC can change it to OVERLAY in prepare. + getLayer()->compositionType = HWC_FRAMEBUFFER; + } + getLayer()->handle = buffer->handle; + } + } + virtual void onDisplayed() { + getLayer()->acquireFenceFd = -1; + } + +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; +}; + +/* + * returns an iterator initialized at a given index in the layer list + */ +HWComposer::LayerListIterator HWComposer::getLayerIterator(int32_t id, size_t index) { + if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) { + return LayerListIterator(); + } + const DisplayData& disp(mDisplayData[id]); + if (!mHwc || !disp.list || index > disp.list->numHwLayers) { + return LayerListIterator(); + } + return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers), index); +} + +/* + * returns an iterator on the beginning of the layer list + */ +HWComposer::LayerListIterator HWComposer::begin(int32_t id) { + return getLayerIterator(id, 0); +} + +/* + * returns an iterator on the end of the layer list + */ +HWComposer::LayerListIterator HWComposer::end(int32_t id) { + size_t numLayers = 0; + if (uint32_t(id) <= 31 && mAllocatedDisplayIDs.hasBit(id)) { + const DisplayData& disp(mDisplayData[id]); + if (mHwc && disp.list) { + numLayers = disp.list->numHwLayers; + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) { + // with HWC 1.1, the last layer is always the HWC_FRAMEBUFFER_TARGET, + // which we ignore when iterating through the layer list. + ALOGE_IF(!numLayers, "mDisplayData[%d].list->numHwLayers is 0", id); + if (numLayers) { + numLayers--; + } + } + } + } + return getLayerIterator(id, numLayers); +} + +// Converts a PixelFormat to a human-readable string. Max 11 chars. +// (Could use a table of prefab String8 objects.) +static String8 getFormatStr(PixelFormat format) { + switch (format) { + case PIXEL_FORMAT_RGBA_8888: return String8("RGBA_8888"); + case PIXEL_FORMAT_RGBX_8888: return String8("RGBx_8888"); + case PIXEL_FORMAT_RGB_888: return String8("RGB_888"); + case PIXEL_FORMAT_RGB_565: return String8("RGB_565"); + case PIXEL_FORMAT_BGRA_8888: return String8("BGRA_8888"); + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + return String8("ImplDef"); + default: + String8 result; + result.appendFormat("? %08x", format); + return result; + } +} + +void HWComposer::dump(String8& result) const { + Mutex::Autolock _l(mDisplayLock); + if (mHwc) { + result.appendFormat("Hardware Composer state (version %08x):\n", hwcApiVersion(mHwc)); + result.appendFormat(" mDebugForceFakeVSync=%d\n", mDebugForceFakeVSync); + for (size_t i=0 ; i<mNumDisplays ; i++) { + const DisplayData& disp(mDisplayData[i]); + if (!disp.connected) + continue; + + const Vector< sp<Layer> >& visibleLayersSortedByZ = + mFlinger->getLayerSortedByZForHwcDisplay(i); + + + result.appendFormat(" Display[%zd] configurations (* current):\n", i); + for (size_t c = 0; c < disp.configs.size(); ++c) { + const DisplayConfig& config(disp.configs[c]); + result.appendFormat(" %s%zd: %ux%u, xdpi=%f, ydpi=%f" + ", refresh=%" PRId64 ", colorTransform=%d\n", + c == disp.currentConfig ? "* " : "", c, + config.width, config.height, config.xdpi, config.ydpi, + config.refresh, config.colorTransform); + } + + if (disp.list) { + result.appendFormat( + " numHwLayers=%zu, flags=%08x\n", + disp.list->numHwLayers, disp.list->flags); + + result.append( + " type | handle | hint | flag | tr | blnd | format | source crop (l,t,r,b) | frame | name \n" + "-----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------\n"); + // " _________ | ________ | ____ | ____ | __ | ____ | ___________ |_____._,_____._,_____._,_____._ |_____,_____,_____,_____ | ___... + for (size_t i=0 ; i<disp.list->numHwLayers ; i++) { + const hwc_layer_1_t&l = disp.list->hwLayers[i]; + int32_t format = -1; + String8 name("unknown"); + + if (i < visibleLayersSortedByZ.size()) { + const sp<Layer>& layer(visibleLayersSortedByZ[i]); + const sp<GraphicBuffer>& buffer( + layer->getActiveBuffer()); + if (buffer != NULL) { + format = buffer->getPixelFormat(); + } + name = layer->getName(); + } + + int type = l.compositionType; + if (type == HWC_FRAMEBUFFER_TARGET) { + name = "HWC_FRAMEBUFFER_TARGET"; + format = disp.format; + } + + static char const* compositionTypeName[] = { + "GLES", + "HWC", + "BKGND", + "FB TARGET", + "SIDEBAND", + "HWC_CURSOR", + "UNKNOWN"}; + if (type >= NELEM(compositionTypeName)) + type = NELEM(compositionTypeName) - 1; + + String8 formatStr = getFormatStr(format); + if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) { + result.appendFormat( + " %9s | %08" PRIxPTR " | %04x | %04x | %02x | %04x | %-11s |%7.1f,%7.1f,%7.1f,%7.1f |%5d,%5d,%5d,%5d | %s\n", + compositionTypeName[type], + intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, formatStr.string(), + l.sourceCropf.left, l.sourceCropf.top, l.sourceCropf.right, l.sourceCropf.bottom, + l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, + name.string()); + } else { + result.appendFormat( + " %9s | %08" PRIxPTR " | %04x | %04x | %02x | %04x | %-11s |%7d,%7d,%7d,%7d |%5d,%5d,%5d,%5d | %s\n", + compositionTypeName[type], + intptr_t(l.handle), l.hints, l.flags, l.transform, l.blending, formatStr.string(), + l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom, + l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom, + name.string()); + } + } + } + } + } + + if (mHwc && mHwc->dump) { + const size_t SIZE = 4096; + char buffer[SIZE]; + mHwc->dump(mHwc, buffer, SIZE); + result.append(buffer); + } +} + +// --------------------------------------------------------------------------- + +HWComposer::VSyncThread::VSyncThread(HWComposer& hwc) + : mHwc(hwc), mEnabled(false), + mNextFakeVSync(0), + mRefreshPeriod(hwc.getRefreshPeriod(HWC_DISPLAY_PRIMARY)) +{ +} + +void HWComposer::VSyncThread::setEnabled(bool enabled) { + Mutex::Autolock _l(mLock); + if (mEnabled != enabled) { + mEnabled = enabled; + mCondition.signal(); + } +} + +void HWComposer::VSyncThread::onFirstRef() { + run("VSyncThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); +} + +bool HWComposer::VSyncThread::threadLoop() { + { // scope for lock + Mutex::Autolock _l(mLock); + while (!mEnabled) { + mCondition.wait(mLock); + } + } + + const nsecs_t period = mRefreshPeriod; + const nsecs_t now = systemTime(CLOCK_MONOTONIC); + nsecs_t next_vsync = mNextFakeVSync; + nsecs_t sleep = next_vsync - now; + if (sleep < 0) { + // we missed, find where the next vsync should be + sleep = (period - ((now - next_vsync) % period)); + next_vsync = now + sleep; + } + mNextFakeVSync = next_vsync + period; + + struct timespec spec; + spec.tv_sec = next_vsync / 1000000000; + spec.tv_nsec = next_vsync % 1000000000; + + int err; + do { + err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); + } while (err<0 && errno == EINTR); + + if (err == 0) { + mHwc.mEventHandler.onVSyncReceived(0, next_vsync); + } + + return true; +} + +HWComposer::DisplayData::DisplayData() +: configs(), + currentConfig(0), + format(HAL_PIXEL_FORMAT_RGBA_8888), + connected(false), + hasFbComp(false), hasOvComp(false), + capacity(0), list(NULL), + framebufferTarget(NULL), fbTargetHandle(0), + lastRetireFence(Fence::NO_FENCE), lastDisplayFence(Fence::NO_FENCE), + outbufHandle(NULL), outbufAcquireFence(Fence::NO_FENCE), + events(0) +{} + +HWComposer::DisplayData::~DisplayData() { + free(list); +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h new file mode 100644 index 0000000000..f5f7d7702b --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SF_HWCOMPOSER_HWC1_H +#define ANDROID_SF_HWCOMPOSER_HWC1_H + +#include <stdint.h> +#include <sys/types.h> + +#include <hardware/hwcomposer_defs.h> + +#include <ui/Fence.h> + +#include <utils/BitSet.h> +#include <utils/Condition.h> +#include <utils/Mutex.h> +#include <utils/StrongPointer.h> +#include <utils/Thread.h> +#include <utils/Timers.h> +#include <utils/Vector.h> + +extern "C" int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain); + +struct hwc_composer_device_1; +struct hwc_display_contents_1; +struct hwc_layer_1; +struct hwc_procs; +struct framebuffer_device_t; + +namespace android { +// --------------------------------------------------------------------------- + +class Fence; +class FloatRect; +class GraphicBuffer; +class NativeHandle; +class Region; +class String8; +class SurfaceFlinger; + +class HWComposer +{ +public: + class EventHandler { + friend class HWComposer; + virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0; + virtual void onHotplugReceived(int disp, bool connected) = 0; + protected: + virtual ~EventHandler() {} + }; + + enum { + NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES, + MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES, + VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL, + }; + + HWComposer( + const sp<SurfaceFlinger>& flinger, + EventHandler& handler); + + ~HWComposer(); + + status_t initCheck() const; + + // Returns a display ID starting at VIRTUAL_DISPLAY_ID_BASE, this ID is to + // be used with createWorkList (and all other methods requiring an ID + // below). + // IDs below NUM_BUILTIN_DISPLAYS are pre-defined and therefore are + // always valid. + // Returns -1 if an ID cannot be allocated + int32_t allocateDisplayId(); + + // Recycles the given virtual display ID and frees the associated worklist. + // IDs below NUM_BUILTIN_DISPLAYS are not recycled. + status_t freeDisplayId(int32_t id); + + + // Asks the HAL what it can do + status_t prepare(); + + // commits the list + status_t commit(); + + // set power mode + status_t setPowerMode(int disp, int mode); + + // set active config + status_t setActiveConfig(int disp, int mode); + + // reset state when an external, non-virtual display is disconnected + void disconnectDisplay(int disp); + + // create a work list for numLayers layer. sets HWC_GEOMETRY_CHANGED. + status_t createWorkList(int32_t id, size_t numLayers); + + bool supportsFramebufferTarget() const; + + // does this display have layers handled by HWC + bool hasHwcComposition(int32_t id) const; + + // does this display have layers handled by GLES + bool hasGlesComposition(int32_t id) const; + + // get the releaseFence file descriptor for a display's framebuffer layer. + // the release fence is only valid after commit() + sp<Fence> getAndResetReleaseFence(int32_t id); + + // needed forward declarations + class LayerListIterator; + + // return the visual id to be used to find a suitable EGLConfig for + // *ALL* displays. + int getVisualID() const; + + // Forwarding to FB HAL for pre-HWC-1.1 code (see FramebufferSurface). + int fbPost(int32_t id, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf); + int fbCompositionComplete(); + void fbDump(String8& result); + + // Set the output buffer and acquire fence for a virtual display. + // Returns INVALID_OPERATION if id is not a virtual display. + status_t setOutputBuffer(int32_t id, const sp<Fence>& acquireFence, + const sp<GraphicBuffer>& buf); + + // Get the retire fence for the last committed frame. This fence will + // signal when the h/w composer is completely finished with the frame. + // For physical displays, it is no longer being displayed. For virtual + // displays, writes to the output buffer are complete. + sp<Fence> getLastRetireFence(int32_t id) const; + + status_t setCursorPositionAsync(int32_t id, const Rect &pos); + + /* + * Interface to hardware composer's layers functionality. + * This abstracts the HAL interface to layers which can evolve in + * incompatible ways from one release to another. + * The idea is that we could extend this interface as we add + * features to h/w composer. + */ + class HWCLayerInterface { + protected: + virtual ~HWCLayerInterface() { } + public: + virtual int32_t getCompositionType() const = 0; + virtual uint32_t getHints() const = 0; + virtual sp<Fence> getAndResetReleaseFence() = 0; + virtual void setDefaultState() = 0; + virtual void setSkip(bool skip) = 0; + virtual void setIsCursorLayerHint(bool isCursor = true) = 0; + virtual void setBlending(uint32_t blending) = 0; + virtual void setTransform(uint32_t transform) = 0; + virtual void setFrame(const Rect& frame) = 0; + virtual void setCrop(const FloatRect& crop) = 0; + virtual void setVisibleRegionScreen(const Region& reg) = 0; + virtual void setSurfaceDamage(const Region& reg) = 0; + virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0; + virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0; + virtual void setAcquireFenceFd(int fenceFd) = 0; + virtual void setPlaneAlpha(uint8_t alpha) = 0; + virtual void onDisplayed() = 0; + }; + + /* + * Interface used to implement an iterator to a list + * of HWCLayer. + */ + class HWCLayer : public HWCLayerInterface { + friend class LayerListIterator; + // select the layer at the given index + virtual status_t setLayer(size_t index) = 0; + virtual HWCLayer* dup() = 0; + static HWCLayer* copy(HWCLayer *rhs) { + return rhs ? rhs->dup() : NULL; + } + protected: + virtual ~HWCLayer() { } + }; + + /* + * Iterator through a HWCLayer list. + * This behaves more or less like a forward iterator. + */ + class LayerListIterator { + friend class HWComposer; + HWCLayer* const mLayerList; + size_t mIndex; + + LayerListIterator() : mLayerList(NULL), mIndex(0) { } + + LayerListIterator(HWCLayer* layer, size_t index) + : mLayerList(layer), mIndex(index) { } + + // we don't allow assignment, because we don't need it for now + LayerListIterator& operator = (const LayerListIterator& rhs); + + public: + // copy operators + LayerListIterator(const LayerListIterator& rhs) + : mLayerList(HWCLayer::copy(rhs.mLayerList)), mIndex(rhs.mIndex) { + } + + ~LayerListIterator() { delete mLayerList; } + + // pre-increment + LayerListIterator& operator++() { + mLayerList->setLayer(++mIndex); + return *this; + } + + // dereference + HWCLayerInterface& operator * () { return *mLayerList; } + HWCLayerInterface* operator -> () { return mLayerList; } + + // comparison + bool operator == (const LayerListIterator& rhs) const { + return mIndex == rhs.mIndex; + } + bool operator != (const LayerListIterator& rhs) const { + return !operator==(rhs); + } + }; + + // Returns an iterator to the beginning of the layer list + LayerListIterator begin(int32_t id); + + // Returns an iterator to the end of the layer list + LayerListIterator end(int32_t id); + + + // Events handling --------------------------------------------------------- + + enum { + EVENT_VSYNC = HWC_EVENT_VSYNC + }; + + void eventControl(int disp, int event, int enabled); + + struct DisplayConfig { + uint32_t width; + uint32_t height; + float xdpi; + float ydpi; + nsecs_t refresh; + int colorTransform; + }; + + // Query display parameters. Pass in a display index (e.g. + // HWC_DISPLAY_PRIMARY). + nsecs_t getRefreshTimestamp(int disp) const; + sp<Fence> getDisplayFence(int disp) const; + uint32_t getFormat(int disp) const; + bool isConnected(int disp) const; + + // These return the values for the current config of a given display index. + // To get the values for all configs, use getConfigs below. + uint32_t getWidth(int disp) const; + uint32_t getHeight(int disp) const; + float getDpiX(int disp) const; + float getDpiY(int disp) const; + nsecs_t getRefreshPeriod(int disp) const; + + const Vector<DisplayConfig>& getConfigs(int disp) const; + size_t getCurrentConfig(int disp) const; + + status_t setVirtualDisplayProperties(int32_t id, uint32_t w, uint32_t h, + uint32_t format); + + // this class is only used to fake the VSync event on systems that don't + // have it. + class VSyncThread : public Thread { + HWComposer& mHwc; + mutable Mutex mLock; + Condition mCondition; + bool mEnabled; + mutable nsecs_t mNextFakeVSync; + nsecs_t mRefreshPeriod; + virtual void onFirstRef(); + virtual bool threadLoop(); + public: + VSyncThread(HWComposer& hwc); + void setEnabled(bool enabled); + }; + + friend class VSyncThread; + + // for debugging ---------------------------------------------------------- + void dump(String8& out) const; + +private: + void loadHwcModule(); + int loadFbHalModule(); + + LayerListIterator getLayerIterator(int32_t id, size_t index); + + struct cb_context; + + static void hook_invalidate(const struct hwc_procs* procs); + static void hook_vsync(const struct hwc_procs* procs, int disp, + int64_t timestamp); + static void hook_hotplug(const struct hwc_procs* procs, int disp, + int connected); + + inline void invalidate(); + inline void vsync(int disp, int64_t timestamp); + inline void hotplug(int disp, int connected); + + status_t queryDisplayProperties(int disp); + + status_t setFramebufferTarget(int32_t id, + const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf); + + struct DisplayData { + DisplayData(); + ~DisplayData(); + Vector<DisplayConfig> configs; + size_t currentConfig; + uint32_t format; // pixel format from FB hal, for pre-hwc-1.1 + bool connected; + bool hasFbComp; + bool hasOvComp; + size_t capacity; + hwc_display_contents_1* list; + hwc_layer_1* framebufferTarget; + buffer_handle_t fbTargetHandle; + sp<Fence> lastRetireFence; // signals when the last set op retires + sp<Fence> lastDisplayFence; // signals when the last set op takes + // effect on screen + buffer_handle_t outbufHandle; + sp<Fence> outbufAcquireFence; + + // protected by mEventControlLock + int32_t events; + }; + + sp<SurfaceFlinger> mFlinger; + framebuffer_device_t* mFbDev; + struct hwc_composer_device_1* mHwc; + // invariant: mLists[0] != NULL iff mHwc != NULL + // mLists[i>0] can be NULL. that display is to be ignored + struct hwc_display_contents_1* mLists[MAX_HWC_DISPLAYS]; + DisplayData mDisplayData[MAX_HWC_DISPLAYS]; + // protect mDisplayData from races between prepare and dump + mutable Mutex mDisplayLock; + size_t mNumDisplays; + + cb_context* mCBContext; + EventHandler& mEventHandler; + size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; + sp<VSyncThread> mVSyncThread; + bool mDebugForceFakeVSync; + BitSet32 mAllocatedDisplayIDs; + + // protected by mLock + mutable Mutex mLock; + mutable nsecs_t mLastHwVSync[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; + + // thread-safe + mutable Mutex mEventControlLock; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SF_HWCOMPOSER_H diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 1a0d6898fd..1afed360f6 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -57,8 +57,20 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, mHwc(hwc), mDisplayId(dispId), mDisplayName(name), + mSource{}, + mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), + mOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), mOutputUsage(GRALLOC_USAGE_HW_COMPOSER), mProducerSlotSource(0), + mProducerBuffers(), + mQueueBufferOutput(), + mSinkBufferWidth(0), + mSinkBufferHeight(0), + mCompositionType(COMPOSITION_UNKNOWN), + mFbFence(Fence::NO_FENCE), + mOutputFence(Fence::NO_FENCE), + mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT), + mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT), mDbgState(DBG_STATE_IDLE), mDbgLastCompositionType(COMPOSITION_UNKNOWN), mMustRecompose(false) @@ -163,9 +175,11 @@ status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { return NO_ERROR; } +#ifndef USE_HWC2 status_t VirtualDisplaySurface::compositionComplete() { return NO_ERROR; } +#endif status_t VirtualDisplaySurface::advanceFrame() { if (mDisplayId < 0) @@ -206,7 +220,13 @@ status_t VirtualDisplaySurface::advanceFrame() { status_t result = NO_ERROR; if (fbBuffer != NULL) { +#ifdef USE_HWC2 + // TODO: Correctly propagate the dataspace from GL composition + result = mHwc.setClientTarget(mDisplayId, mFbFence, fbBuffer, + HAL_DATASPACE_UNKNOWN); +#else result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer); +#endif } return result; @@ -220,13 +240,22 @@ void VirtualDisplaySurface::onFrameCommitted() { "Unexpected onFrameCommitted() in %s state", dbgStateStr()); mDbgState = DBG_STATE_IDLE; +#ifdef USE_HWC2 + sp<Fence> retireFence = mHwc.getRetireFence(mDisplayId); +#else sp<Fence> fbFence = mHwc.getAndResetReleaseFence(mDisplayId); +#endif if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) { // release the scratch buffer back to the pool Mutex::Autolock lock(mMutex); int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot); VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot); +#ifdef USE_HWC2 + addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], + retireFence); +#else addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], fbFence); +#endif releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot], EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); } @@ -234,7 +263,9 @@ void VirtualDisplaySurface::onFrameCommitted() { if (mOutputProducerSlot >= 0) { int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot); QueueBufferOutput qbo; +#ifndef USE_HWC2 sp<Fence> outFence = mHwc.getLastRetireFence(mDisplayId); +#endif VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot); if (mMustRecompose) { status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot, @@ -243,7 +274,11 @@ void VirtualDisplaySurface::onFrameCommitted() { HAL_DATASPACE_UNKNOWN, Rect(mSinkBufferWidth, mSinkBufferHeight), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0 /* transform */, +#ifdef USE_HWC2 + retireFence), +#else outFence), +#endif &qbo); if (result == NO_ERROR) { updateQueueBufferOutput(qbo); @@ -253,7 +288,11 @@ void VirtualDisplaySurface::onFrameCommitted() { // through the motions of updating the display to keep our state // machine happy. We cancel the buffer to avoid triggering another // re-composition and causing an infinite loop. +#ifdef USE_HWC2 + mSource[SOURCE_SINK]->cancelBuffer(sslot, retireFence); +#else mSource[SOURCE_SINK]->cancelBuffer(sslot, outFence); +#endif } } @@ -272,6 +311,12 @@ void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) { mSinkBufferHeight = h; } +#ifdef USE_HWC2 +const sp<Fence>& VirtualDisplaySurface::getClientTargetAcquireFence() const { + return mFbFence; +} +#endif + status_t VirtualDisplaySurface::requestBuffer(int pslot, sp<GraphicBuffer>* outBuf) { if (mDisplayId < 0) diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index ede204c224..fe187c20a3 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -83,11 +83,16 @@ public: // virtual status_t beginFrame(bool mustRecompose); virtual status_t prepareFrame(CompositionType compositionType); +#ifndef USE_HWC2 virtual status_t compositionComplete(); +#endif virtual status_t advanceFrame(); virtual void onFrameCommitted(); virtual void dumpAsString(String8& result) const; virtual void resizeBuffers(const uint32_t w, const uint32_t h); +#ifdef USE_HWC2 + virtual const sp<Fence>& getClientTargetAcquireFence() const override; +#endif private: enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1}; diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/EventControlThread.cpp index 65040913fa..ee6e886d12 100644 --- a/services/surfaceflinger/EventControlThread.cpp +++ b/services/surfaceflinger/EventControlThread.cpp @@ -35,8 +35,12 @@ bool EventControlThread::threadLoop() { bool vsyncEnabled = mVsyncEnabled; +#ifdef USE_HWC2 + mFlinger->setVsyncEnabled(HWC_DISPLAY_PRIMARY, mVsyncEnabled); +#else mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled); +#endif while (true) { status_t err = mCond.wait(mMutex); @@ -47,8 +51,12 @@ bool EventControlThread::threadLoop() { } if (vsyncEnabled != mVsyncEnabled) { +#ifdef USE_HWC2 + mFlinger->setVsyncEnabled(HWC_DISPLAY_PRIMARY, mVsyncEnabled); +#else mFlinger->eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, mVsyncEnabled); +#endif vsyncEnabled = mVsyncEnabled; } } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 6574898154..99f0f0b725 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "Layer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <stdlib.h> @@ -90,6 +93,10 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, mUpdateTexImageFailed(false), mAutoRefresh(false) { +#ifdef USE_HWC2 + ALOGV("Creating Layer %s", name.string()); +#endif + mCurrentCrop.makeInvalid(); mFlinger->getRenderEngine().genTextures(1, &mTextureName); mTexture.init(Texture::TEXTURE_EXTERNAL, mTextureName); @@ -109,20 +116,30 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, mCurrentState.active.w = w; mCurrentState.active.h = h; + mCurrentState.active.transform.set(0, 0); mCurrentState.active.crop.makeInvalid(); mCurrentState.z = 0; +#ifdef USE_HWC2 + mCurrentState.alpha = 1.0f; +#else mCurrentState.alpha = 0xFF; +#endif mCurrentState.layerStack = 0; mCurrentState.flags = layerFlags; mCurrentState.sequence = 0; - mCurrentState.transform.set(0, 0); mCurrentState.requested = mCurrentState.active; // drawing state & current state are identical mDrawingState = mCurrentState; +#ifdef USE_HWC2 + const auto& hwc = flinger->getHwComposer(); + const auto& activeConfig = hwc.getActiveConfig(HWC_DISPLAY_PRIMARY); + nsecs_t displayPeriod = activeConfig->getVsyncPeriod(); +#else nsecs_t displayPeriod = flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); +#endif mFrameTracker.setDisplayRefreshPeriod(displayPeriod); } @@ -148,6 +165,11 @@ void Layer::onFirstRef() { } Layer::~Layer() { + sp<Client> c(mClientRef.promote()); + if (c != 0) { + c->detachLayer(this); + } + for (auto& point : mRemoteSyncPoints) { point->setTransactionApplied(); } @@ -159,6 +181,14 @@ Layer::~Layer() { // callbacks // --------------------------------------------------------------------------- +#ifdef USE_HWC2 +void Layer::onLayerDisplayed(const sp<Fence>& releaseFence) { + if (mHwcLayers.empty()) { + return; + } + mSurfaceFlingerConsumer->setReleaseFence(releaseFence); +} +#else void Layer::onLayerDisplayed(const sp<const DisplayDevice>& /* hw */, HWComposer::HWCLayerInterface* layer) { if (layer) { @@ -166,6 +196,7 @@ void Layer::onLayerDisplayed(const sp<const DisplayDevice>& /* hw */, mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFence()); } } +#endif void Layer::onFrameAvailable(const BufferItem& item) { // Add this buffer from our internal queue tracker @@ -234,10 +265,6 @@ void Layer::onSidebandStreamChanged() { // the layer has been remove from the current state list (and just before // it's removed from the drawing state list) void Layer::onRemoved() { - sp<Client> c(mClientRef.promote()); - if (c != 0) { - c->detachLayer(this); - } mSurfaceFlingerConsumer->abandon(); } @@ -373,9 +400,9 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { activeCrop = s.active.crop; } - activeCrop = s.transform.transform(activeCrop); + activeCrop = s.active.transform.transform(activeCrop); activeCrop.intersect(hw->getViewport(), &activeCrop); - activeCrop = s.transform.inverse().transform(activeCrop); + activeCrop = s.active.transform.inverse().transform(activeCrop); // This needs to be here as transform.transform(Rect) computes the // transformed rect and then takes the bounding box of the result before @@ -452,35 +479,69 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { return crop; } +#ifdef USE_HWC2 +void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice) +#else void Layer::setGeometry( const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer) +#endif { +#ifdef USE_HWC2 + const auto hwcId = displayDevice->getHwcDisplayId(); + auto& hwcInfo = mHwcLayers[hwcId]; +#else layer.setDefaultState(); +#endif // enable this layer +#ifdef USE_HWC2 + hwcInfo.forceClientComposition = false; + + if (isSecure() && !displayDevice->isSecure()) { + hwcInfo.forceClientComposition = true; + } + + auto& hwcLayer = hwcInfo.layer; +#else layer.setSkip(false); if (isSecure() && !hw->isSecure()) { layer.setSkip(true); } +#endif // this gives us only the "orientation" component of the transform const State& s(getDrawingState()); +#ifdef USE_HWC2 + if (!isOpaque(s) || s.alpha != 1.0f) { + auto blendMode = mPremultipliedAlpha ? + HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage; + auto error = hwcLayer->setBlendMode(blendMode); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set blend mode %s:" + " %s (%d)", mName.string(), to_string(blendMode).c_str(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } +#else if (!isOpaque(s) || s.alpha != 0xFF) { layer.setBlending(mPremultipliedAlpha ? HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE); } +#endif // apply the layer's transform, followed by the display's global transform // here we're guaranteed that the layer's transform preserves rects Region activeTransparentRegion(s.activeTransparentRegion); if (!s.active.crop.isEmpty()) { Rect activeCrop(s.active.crop); - activeCrop = s.transform.transform(activeCrop); + activeCrop = s.active.transform.transform(activeCrop); +#ifdef USE_HWC2 + activeCrop.intersect(displayDevice->getViewport(), &activeCrop); +#else activeCrop.intersect(hw->getViewport(), &activeCrop); - activeCrop = s.transform.inverse().transform(activeCrop); +#endif + activeCrop = s.active.transform.inverse().transform(activeCrop); // This needs to be here as transform.transform(Rect) computes the // transformed rect and then takes the bounding box of the result before // returning. This means @@ -497,12 +558,42 @@ void Layer::setGeometry( activeTransparentRegion.orSelf(Rect(activeCrop.right, activeCrop.top, s.active.w, activeCrop.bottom)); } - Rect frame(s.transform.transform(computeBounds(activeTransparentRegion))); + Rect frame(s.active.transform.transform(computeBounds(activeTransparentRegion))); +#ifdef USE_HWC2 + frame.intersect(displayDevice->getViewport(), &frame); + const Transform& tr(displayDevice->getTransform()); + Rect transformedFrame = tr.transform(frame); + auto error = hwcLayer->setDisplayFrame(transformedFrame); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set display frame " + "[%d, %d, %d, %d]: %s (%d)", mName.string(), transformedFrame.left, + transformedFrame.top, transformedFrame.right, + transformedFrame.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + + FloatRect sourceCrop = computeCrop(displayDevice); + error = hwcLayer->setSourceCrop(sourceCrop); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set source crop " + "[%.3f, %.3f, %.3f, %.3f]: %s (%d)", mName.string(), + sourceCrop.left, sourceCrop.top, sourceCrop.right, + sourceCrop.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + + error = hwcLayer->setPlaneAlpha(s.alpha); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set plane alpha %.3f: " + "%s (%d)", mName.string(), s.alpha, to_string(error).c_str(), + static_cast<int32_t>(error)); + + error = hwcLayer->setZOrder(s.z); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", + mName.string(), s.z, to_string(error).c_str(), + static_cast<int32_t>(error)); +#else frame.intersect(hw->getViewport(), &frame); const Transform& tr(hw->getTransform()); layer.setFrame(tr.transform(frame)); layer.setCrop(computeCrop(hw)); layer.setPlaneAlpha(s.alpha); +#endif /* * Transformations are applied in this order: @@ -513,13 +604,17 @@ void Layer::setGeometry( */ const Transform bufferOrientation(mCurrentTransform); - Transform transform(tr * s.transform * bufferOrientation); + Transform transform(tr * s.active.transform * bufferOrientation); if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) { /* * the code below applies the display's inverse transform to the buffer */ +#ifdef USE_HWC2 + uint32_t invTransform = displayDevice->getOrientationTransform(); +#else uint32_t invTransform = hw->getOrientationTransform(); +#endif uint32_t t_orientation = transform.getOrientation(); // calculate the inverse transform if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { @@ -540,14 +635,110 @@ void Layer::setGeometry( // this gives us only the "orientation" component of the transform const uint32_t orientation = transform.getOrientation(); +#ifdef USE_HWC2 + if (orientation & Transform::ROT_INVALID) { + // we can only handle simple transformation + hwcInfo.forceClientComposition = true; + } else { + auto transform = static_cast<HWC2::Transform>(orientation); + auto error = hwcLayer->setTransform(transform); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set transform %s: " + "%s (%d)", mName.string(), to_string(transform).c_str(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } +#else if (orientation & Transform::ROT_INVALID) { // we can only handle simple transformation layer.setSkip(true); } else { layer.setTransform(orientation); } +#endif } +#ifdef USE_HWC2 +void Layer::forceClientComposition(int32_t hwcId) { + if (mHwcLayers.count(hwcId) == 0) { + ALOGE("forceClientComposition: no HWC layer found (%d)", hwcId); + return; + } + + mHwcLayers[hwcId].forceClientComposition = true; +} +#endif + +#ifdef USE_HWC2 +void Layer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { + // Apply this display's projection's viewport to the visible region + // before giving it to the HWC HAL. + const Transform& tr = displayDevice->getTransform(); + const auto& viewport = displayDevice->getViewport(); + Region visible = tr.transform(visibleRegion.intersect(viewport)); + auto hwcId = displayDevice->getHwcDisplayId(); + auto& hwcLayer = mHwcLayers[hwcId].layer; + auto error = hwcLayer->setVisibleRegion(visible); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + visible.dump(LOG_TAG); + } + + error = hwcLayer->setSurfaceDamage(surfaceDamageRegion); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + surfaceDamageRegion.dump(LOG_TAG); + } + + auto compositionType = HWC2::Composition::Invalid; + if (mSidebandStream.get()) { + compositionType = HWC2::Composition::Sideband; + auto error = hwcLayer->setSidebandStream(mSidebandStream->handle()); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", + mName.string(), mSidebandStream->handle(), + to_string(error).c_str(), static_cast<int32_t>(error)); + return; + } + } else { + if (mActiveBuffer == nullptr || mActiveBuffer->handle == nullptr) { + compositionType = HWC2::Composition::Client; + auto error = hwcLayer->setBuffer(nullptr, Fence::NO_FENCE); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set null buffer: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + return; + } + } else { + if (mPotentialCursor) { + compositionType = HWC2::Composition::Cursor; + } + auto acquireFence = mSurfaceFlingerConsumer->getCurrentFence(); + auto error = hwcLayer->setBuffer(mActiveBuffer->handle, + acquireFence); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), + mActiveBuffer->handle, to_string(error).c_str(), + static_cast<int32_t>(error)); + return; + } + // If it's not a cursor, default to device composition + } + } + + if (mHwcLayers[hwcId].forceClientComposition) { + ALOGV("[%s] Forcing Client composition", mName.string()); + setCompositionType(hwcId, HWC2::Composition::Client); + } else if (compositionType != HWC2::Composition::Invalid) { + ALOGV("[%s] Requesting %s composition", mName.string(), + to_string(compositionType).c_str()); + setCompositionType(hwcId, compositionType); + } else { + ALOGV("[%s] Requesting Device composition", mName.string()); + setCompositionType(hwcId, HWC2::Composition::Device); + } +} +#else void Layer::setPerFrameData(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer) { // we have to set the visible region on every frame because @@ -568,7 +759,40 @@ void Layer::setPerFrameData(const sp<const DisplayDevice>& hw, layer.setBuffer(mActiveBuffer); } } +#endif + +#ifdef USE_HWC2 +void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { + auto hwcId = displayDevice->getHwcDisplayId(); + if (mHwcLayers.count(hwcId) == 0 || + getCompositionType(hwcId) != HWC2::Composition::Cursor) { + return; + } + + // This gives us only the "orientation" component of the transform + const State& s(getCurrentState()); + + // Apply the layer's transform, followed by the display's global transform + // Here we're guaranteed that the layer's transform preserves rects + Rect win(s.active.w, s.active.h); + if (!s.active.crop.isEmpty()) { + win.intersect(s.active.crop, &win); + } + // Subtract the transparent region and snap to the bounds + Rect bounds = reduce(win, s.activeTransparentRegion); + Rect frame(s.transform.transform(bounds)); + frame.intersect(displayDevice->getViewport(), &frame); + auto& displayTransform(displayDevice->getTransform()); + auto position = displayTransform.transform(frame); + auto error = mHwcLayers[hwcId].layer->setCursorPosition(position.left, + position.top); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set cursor position " + "to (%d, %d): %s (%d)", mName.string(), position.left, + position.top, to_string(error).c_str(), + static_cast<int32_t>(error)); +} +#else void Layer::setAcquireFence(const sp<const DisplayDevice>& /* hw */, HWComposer::HWCLayerInterface& layer) { int fenceFd = -1; @@ -602,11 +826,12 @@ Rect Layer::getPosition( } // subtract the transparent region and snap to the bounds Rect bounds = reduce(win, s.activeTransparentRegion); - Rect frame(s.transform.transform(bounds)); + Rect frame(s.active.transform.transform(bounds)); frame.intersect(hw->getViewport(), &frame); const Transform& tr(hw->getTransform()); return Rect(tr.transform(frame)); } +#endif // --------------------------------------------------------------------------- // drawing... @@ -778,6 +1003,55 @@ void Layer::drawWithOpenGL(const sp<const DisplayDevice>& hw, engine.disableBlending(); } +#ifdef USE_HWC2 +void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, + bool callIntoHwc) { + if (mHwcLayers.count(hwcId) == 0) { + ALOGE("setCompositionType called without a valid HWC layer"); + return; + } + auto& hwcInfo = mHwcLayers[hwcId]; + auto& hwcLayer = hwcInfo.layer; + ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), + to_string(type).c_str(), static_cast<int>(callIntoHwc)); + if (hwcInfo.compositionType != type) { + ALOGV(" actually setting"); + hwcInfo.compositionType = type; + if (callIntoHwc) { + auto error = hwcLayer->setCompositionType(type); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set " + "composition type %s: %s (%d)", mName.string(), + to_string(type).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + } +} + +HWC2::Composition Layer::getCompositionType(int32_t hwcId) const { + if (mHwcLayers.count(hwcId) == 0) { + ALOGE("getCompositionType called without a valid HWC layer"); + return HWC2::Composition::Invalid; + } + return mHwcLayers.at(hwcId).compositionType; +} + +void Layer::setClearClientTarget(int32_t hwcId, bool clear) { + if (mHwcLayers.count(hwcId) == 0) { + ALOGE("setClearClientTarget called without a valid HWC layer"); + return; + } + mHwcLayers[hwcId].clearClientTarget = clear; +} + +bool Layer::getClearClientTarget(int32_t hwcId) const { + if (mHwcLayers.count(hwcId) == 0) { + ALOGE("getClearClientTarget called without a valid HWC layer"); + return false; + } + return mHwcLayers.at(hwcId).clearClientTarget; +} +#endif + uint32_t Layer::getProducerStickyTransform() const { int producerStickyTransform = 0; int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform); @@ -847,7 +1121,7 @@ void Layer::computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh, { const Layer::State& s(getDrawingState()); const Transform tr(useIdentityTransform ? - hw->getTransform() : hw->getTransform() * s.transform); + hw->getTransform() : hw->getTransform() * s.active.transform); const uint32_t hw_h = hw->getHeight(); Rect win(s.active.w, s.active.h); if (!s.active.crop.isEmpty()) { @@ -1000,8 +1274,9 @@ bool Layer::applyPendingStates() { // Signal our end of the sync point and then dispose of it mRemoteSyncPoints.front()->setTransactionApplied(); mRemoteSyncPoints.pop_front(); + } else { + break; } - break; } else { popPendingState(); stateUpdateAvailable = true; @@ -1129,8 +1404,8 @@ uint32_t Layer::doTransaction(uint32_t flags) { this->contentDirty = true; // we may use linear filtering, if the matrix scales us - const uint8_t type = c.transform.getType(); - mNeedsFiltering = (!c.transform.preserveRects() || + const uint8_t type = c.active.transform.getType(); + mNeedsFiltering = (!c.active.transform.preserveRects() || (type >= Transform::SCALE)); } @@ -1152,10 +1427,10 @@ uint32_t Layer::setTransactionFlags(uint32_t flags) { } bool Layer::setPosition(float x, float y) { - if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) + if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y) return false; mCurrentState.sequence++; - mCurrentState.transform.set(x, y); + mCurrentState.requested.transform.set(x, y); mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); return true; @@ -1178,7 +1453,11 @@ bool Layer::setSize(uint32_t w, uint32_t h) { setTransactionFlags(eTransactionNeeded); return true; } +#ifdef USE_HWC2 +bool Layer::setAlpha(float alpha) { +#else bool Layer::setAlpha(uint8_t alpha) { +#endif if (mCurrentState.alpha == alpha) return false; mCurrentState.sequence++; @@ -1189,7 +1468,7 @@ bool Layer::setAlpha(uint8_t alpha) { } bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) { mCurrentState.sequence++; - mCurrentState.transform.set( + mCurrentState.requested.transform.set( matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -1240,6 +1519,8 @@ void Layer::deferTransactionUntil(const sp<IBinder>& handle, // request without any other state updates shouldn't actually induce a delay mCurrentState.modified = true; pushPendingState(); + mCurrentState.handle = nullptr; + mCurrentState.frameNumber = 0; mCurrentState.modified = false; } @@ -1302,7 +1583,11 @@ void Layer::onPostComposition() { } const HWComposer& hwc = mFlinger->getHwComposer(); +#ifdef USE_HWC2 + sp<Fence> presentFence = hwc.getRetireFence(HWC_DISPLAY_PRIMARY); +#else sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY); +#endif if (presentFence->isValid()) { mFrameTracker.setActualPresentFence(presentFence); } else { @@ -1317,10 +1602,21 @@ void Layer::onPostComposition() { } } +#ifdef USE_HWC2 +void Layer::releasePendingBuffer() { + mSurfaceFlingerConsumer->releasePendingBuffer(); +} +#endif + bool Layer::isVisible() const { const Layer::State& s(mDrawingState); +#ifdef USE_HWC2 + return !(s.flags & layer_state_t::eLayerHidden) && s.alpha > 0.0f + && (mActiveBuffer != NULL || mSidebandStream != NULL); +#else return !(s.flags & layer_state_t::eLayerHidden) && s.alpha && (mActiveBuffer != NULL || mSidebandStream != NULL); +#endif } Region Layer::latchBuffer(bool& recomputeVisibleRegions) @@ -1337,7 +1633,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) recomputeVisibleRegions = true; const State& s(getDrawingState()); - return s.transform.transform(Region(Rect(s.active.w, s.active.h))); + return s.active.transform.transform(Region(Rect(s.active.w, s.active.h))); } Region outDirtyRegion; @@ -1643,7 +1939,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) Region dirtyRegion(Rect(s.active.w, s.active.h)); // transform the dirty region to window-manager space - outDirtyRegion = (s.transform.transform(dirtyRegion)); + outDirtyRegion = (s.active.transform.transform(dirtyRegion)); } return outDirtyRegion; } @@ -1699,15 +1995,19 @@ void Layer::dump(String8& result, Colorizer& colorizer) const result.appendFormat( " " "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), crop=(%4d,%4d,%4d,%4d), " "isOpaque=%1d, invalidate=%1d, " +#ifdef USE_HWC2 + "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" +#else "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" +#endif " client=%p\n", - s.layerStack, s.z, s.transform.tx(), s.transform.ty(), s.active.w, s.active.h, + s.layerStack, s.z, s.active.transform.tx(), s.active.transform.ty(), s.active.w, s.active.h, s.active.crop.left, s.active.crop.top, s.active.crop.right, s.active.crop.bottom, isOpaque(s), contentDirty, s.alpha, s.flags, - s.transform[0][0], s.transform[0][1], - s.transform[1][0], s.transform[1][1], + s.active.transform[0][0], s.active.transform[0][1], + s.active.transform[1][0], s.active.transform[1][1], client.get()); sp<const GraphicBuffer> buf0(mActiveBuffer); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 1773dafe46..b0088e616a 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -94,6 +94,8 @@ public: uint32_t w; uint32_t h; Rect crop; + Transform transform; + inline bool operator ==(const Geometry& rhs) const { return (w == rhs.w && h == rhs.h && crop == rhs.crop); } @@ -107,12 +109,15 @@ public: Geometry requested; uint32_t z; uint32_t layerStack; +#ifdef USE_HWC2 + float alpha; +#else uint8_t alpha; +#endif uint8_t flags; uint8_t mask; uint8_t reserved[2]; int32_t sequence; // changes when visible regions can change - Transform transform; bool modified; // If set, defers this state update until the Layer identified by handle @@ -141,7 +146,11 @@ public: bool setPosition(float x, float y); bool setLayer(uint32_t z); bool setSize(uint32_t w, uint32_t h); +#ifdef USE_HWC2 + bool setAlpha(float alpha); +#else bool setAlpha(uint8_t alpha); +#endif bool setMatrix(const layer_state_t::matrix22_t& matrix); bool setTransparentRegionHint(const Region& transparent); bool setFlags(uint8_t flags, uint8_t mask); @@ -214,6 +223,22 @@ protected: public: // ----------------------------------------------------------------------- +#ifdef USE_HWC2 + void setGeometry(const sp<const DisplayDevice>& displayDevice); + void forceClientComposition(int32_t hwcId); + void setPerFrameData(const sp<const DisplayDevice>& displayDevice); + + // callIntoHwc exists so we can update our local state and call + // acceptDisplayChanges without unnecessarily updating the device's state + void setCompositionType(int32_t hwcId, HWC2::Composition type, + bool callIntoHwc = true); + HWC2::Composition getCompositionType(int32_t hwcId) const; + + void setClearClientTarget(int32_t hwcId, bool clear); + bool getClearClientTarget(int32_t hwcId) const; + + void updateCursorPosition(const sp<const DisplayDevice>& hw); +#else void setGeometry(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer); void setPerFrameData(const sp<const DisplayDevice>& hw, @@ -222,12 +247,17 @@ public: HWComposer::HWCLayerInterface& layer); Rect getPosition(const sp<const DisplayDevice>& hw); +#endif /* * called after page-flip */ +#ifdef USE_HWC2 + void onLayerDisplayed(const sp<Fence>& releaseFence); +#else void onLayerDisplayed(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface* layer); +#endif bool shouldPresentNow(const DispSync& dispSync) const; @@ -242,6 +272,11 @@ public: */ void onPostComposition(); +#ifdef USE_HWC2 + // If a buffer was replaced this frame, release the former buffer + void releasePendingBuffer(); +#endif + /* * draw - performs some global clipping optimizations * and calls onDraw(). @@ -309,6 +344,37 @@ public: bool hasQueuedFrame() const { return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; } +#ifdef USE_HWC2 + // ----------------------------------------------------------------------- + + bool hasHwcLayer(int32_t hwcId) { + if (mHwcLayers.count(hwcId) == 0) { + return false; + } + if (mHwcLayers[hwcId].layer->isAbandoned()) { + ALOGI("Erasing abandoned layer %s on %d", mName.string(), hwcId); + mHwcLayers.erase(hwcId); + return false; + } + return true; + } + + std::shared_ptr<HWC2::Layer> getHwcLayer(int32_t hwcId) { + if (mHwcLayers.count(hwcId) == 0) { + return nullptr; + } + return mHwcLayers[hwcId].layer; + } + + void setHwcLayer(int32_t hwcId, std::shared_ptr<HWC2::Layer>&& layer) { + if (layer) { + mHwcLayers[hwcId].layer = layer; + } else { + mHwcLayers.erase(hwcId); + } + } + +#endif // ----------------------------------------------------------------------- void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip) const; @@ -474,6 +540,23 @@ private: // The texture used to draw the layer in GLES composition mode mutable Texture mTexture; +#ifdef USE_HWC2 + // HWC items, accessed from the main thread + struct HWCInfo { + HWCInfo() + : layer(), + forceClientComposition(false), + compositionType(HWC2::Composition::Invalid), + clearClientTarget(false) {} + + std::shared_ptr<HWC2::Layer> layer; + bool forceClientComposition; + HWC2::Composition compositionType; + bool clearClientTarget; + }; + std::unordered_map<int32_t, HWCInfo> mHwcLayers; +#endif + // page-flip thread (currently main thread) bool mProtectedByApp; // application requires protected path to external sink diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp index 14aa328fef..4d5b3dbdf3 100644 --- a/services/surfaceflinger/LayerDim.cpp +++ b/services/surfaceflinger/LayerDim.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "LayerDim" + #include <stdlib.h> #include <stdint.h> #include <sys/types.h> diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp index 9a47568f55..579affb25d 100644 --- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp +++ b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.cpp @@ -28,13 +28,25 @@ GLES10RenderEngine::~GLES10RenderEngine() { } void GLES10RenderEngine::setupLayerBlending( +#ifdef USE_HWC2 + bool premultipliedAlpha, bool opaque, float alpha) { +#else bool premultipliedAlpha, bool opaque, int alpha) { +#endif // OpenGL ES 1.0 doesn't support texture combiners. // This path doesn't properly handle opaque layers that have non-opaque // alpha values. The alpha channel will be copied into the framebuffer or // screenshot, so if the framebuffer or screenshot is blended on top of // something else, whatever is below the window will incorrectly show // through. +#ifdef USE_HWC2 + if (CC_UNLIKELY(alpha < 1.0f)) { + if (premultipliedAlpha) { + glColor4f(alpha, alpha, alpha, alpha); + } else { + glColor4f(1.0f, 1.0f, 1.0f, alpha); + } +#else if (CC_UNLIKELY(alpha < 0xFF)) { GLfloat floatAlpha = alpha * (1.0f / 255.0f); if (premultipliedAlpha) { @@ -42,12 +54,17 @@ void GLES10RenderEngine::setupLayerBlending( } else { glColor4f(1.0f, 1.0f, 1.0f, floatAlpha); } +#endif glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } else { glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } +#ifdef USE_HWC2 + if (alpha < 1.0f || !opaque) { +#else if (alpha < 0xFF || !opaque) { +#endif glEnable(GL_BLEND); glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h index f9c7c04557..61abd6a679 100644 --- a/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/GLES10RenderEngine.h @@ -30,7 +30,12 @@ namespace android { class GLES10RenderEngine : public GLES11RenderEngine { virtual ~GLES10RenderEngine(); protected: +#ifdef USE_HWC2 + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, + float alpha) override; +#else virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha); +#endif }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp index 1a9f59b582..847cdb388a 100644 --- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp +++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp @@ -107,20 +107,33 @@ void GLES11RenderEngine::setViewportAndProjection( glMatrixMode(GL_MODELVIEW); } +#ifdef USE_HWC2 +void GLES11RenderEngine::setupLayerBlending(bool premultipliedAlpha, + bool opaque, float alpha) { +#else void GLES11RenderEngine::setupLayerBlending( bool premultipliedAlpha, bool opaque, int alpha) { +#endif GLenum combineRGB; GLenum combineAlpha; GLenum src0Alpha; GLfloat envColor[4]; +#ifdef USE_HWC2 + if (CC_UNLIKELY(alpha < 1.0f)) { +#else if (CC_UNLIKELY(alpha < 0xFF)) { +#endif // Cv = premultiplied ? Cs*alpha : Cs // Av = !opaque ? As*alpha : As combineRGB = premultipliedAlpha ? GL_MODULATE : GL_REPLACE; combineAlpha = !opaque ? GL_MODULATE : GL_REPLACE; src0Alpha = GL_CONSTANT; +#ifdef USE_HWC2 + envColor[0] = alpha; +#else envColor[0] = alpha * (1.0f / 255.0f); +#endif } else { // Cv = Cs // Av = opaque ? 1.0 : As @@ -152,7 +165,11 @@ void GLES11RenderEngine::setupLayerBlending( glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, envColor); } +#ifdef USE_HWC2 + if (alpha < 1.0f || !opaque) { +#else if (alpha < 0xFF || !opaque) { +#endif glEnable(GL_BLEND); glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -161,16 +178,28 @@ void GLES11RenderEngine::setupLayerBlending( } } +#ifdef USE_HWC2 +void GLES11RenderEngine::setupDimLayerBlending(float alpha) { +#else void GLES11RenderEngine::setupDimLayerBlending(int alpha) { +#endif glDisable(GL_TEXTURE_EXTERNAL_OES); glDisable(GL_TEXTURE_2D); +#ifdef USE_HWC2 + if (alpha == 1.0f) { +#else if (alpha == 0xFF) { +#endif glDisable(GL_BLEND); } else { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } +#ifdef USE_HWC2 + glColor4f(0, 0, 0, alpha); +#else glColor4f(0, 0, 0, alpha/255.0f); +#endif } void GLES11RenderEngine::setupLayerTexturing(const Texture& texture) { diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h index 08de6465f4..4cd968d125 100644 --- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h @@ -51,9 +51,17 @@ protected: virtual void dump(String8& result); virtual void setViewportAndProjection(size_t vpw, size_t vph, - Rect sourceCrop, size_t hwh, bool yswap, Transform::orientation_flags rotation); - virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha); + Rect sourceCrop, size_t hwh, bool yswap, + Transform::orientation_flags rotation); +#ifdef USE_HWC2 + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, + float alpha) override; + virtual void setupDimLayerBlending(float alpha) override; +#else + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, + int alpha); virtual void setupDimLayerBlending(int alpha); +#endif virtual void setupLayerTexturing(const Texture& texture); virtual void setupLayerBlackedOut(); virtual void setupFillWithColor(float r, float g, float b, float a) ; diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp index 1fabaf5bee..406e611eef 100644 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp @@ -117,14 +117,25 @@ void GLES20RenderEngine::setViewportAndProjection( mVpHeight = vph; } +#ifdef USE_HWC2 +void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, + bool opaque, float alpha) { +#else void GLES20RenderEngine::setupLayerBlending( bool premultipliedAlpha, bool opaque, int alpha) { +#endif mState.setPremultipliedAlpha(premultipliedAlpha); mState.setOpaque(opaque); +#ifdef USE_HWC2 + mState.setPlaneAlpha(alpha); + + if (alpha < 1.0f || !opaque) { +#else mState.setPlaneAlpha(alpha / 255.0f); if (alpha < 0xFF || !opaque) { +#endif glEnable(GL_BLEND); glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { @@ -132,14 +143,26 @@ void GLES20RenderEngine::setupLayerBlending( } } +#ifdef USE_HWC2 +void GLES20RenderEngine::setupDimLayerBlending(float alpha) { +#else void GLES20RenderEngine::setupDimLayerBlending(int alpha) { +#endif mState.setPlaneAlpha(1.0f); mState.setPremultipliedAlpha(true); mState.setOpaque(false); +#ifdef USE_HWC2 + mState.setColor(0, 0, 0, alpha); +#else mState.setColor(0, 0, 0, alpha/255.0f); +#endif mState.disableTexture(); +#ifdef USE_HWC2 + if (alpha == 1.0f) { +#else if (alpha == 0xFF) { +#endif glDisable(GL_BLEND); } else { glEnable(GL_BLEND); diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h index 819356a62b..7c3f9b5d4a 100644 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h @@ -66,9 +66,17 @@ protected: virtual void dump(String8& result); virtual void setViewportAndProjection(size_t vpw, size_t vph, - Rect sourceCrop, size_t hwh, bool yswap, Transform::orientation_flags rotation); - virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha); + Rect sourceCrop, size_t hwh, bool yswap, + Transform::orientation_flags rotation); +#ifdef USE_HWC2 + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, + float alpha) override; + virtual void setupDimLayerBlending(float alpha) override; +#else + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, + int alpha); virtual void setupDimLayerBlending(int alpha); +#endif virtual void setupLayerTexturing(const Texture& texture); virtual void setupLayerBlackedOut(); virtual void setupFillWithColor(float r, float g, float b, float a); diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h index 31a961efe3..9cc1ed7a09 100644 --- a/services/surfaceflinger/RenderEngine/RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/RenderEngine.h @@ -93,8 +93,13 @@ public: virtual void checkErrors() const; virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap, Transform::orientation_flags rotation) = 0; +#ifdef USE_HWC2 + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, float alpha) = 0; + virtual void setupDimLayerBlending(float alpha) = 0; +#else virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha) = 0; virtual void setupDimLayerBlending(int alpha) = 0; +#endif virtual void setupLayerTexturing(const Texture& texture) = 0; virtual void setupLayerBlackedOut() = 0; virtual void setupFillWithColor(float r, float g, float b, float a) = 0; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1e33847406..db03c23b36 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <stdint.h> @@ -134,8 +135,9 @@ SurfaceFlinger::SurfaceFlinger() mRepaintEverything(0), mRenderEngine(NULL), mBootTime(systemTime()), + mBuiltinDisplays(), mVisibleRegionsDirty(false), - mHwWorkListDirty(false), + mGeometryInvalid(false), mAnimCompositionPending(false), mDebugRegion(0), mDebugDDMS(0), @@ -263,6 +265,7 @@ void SurfaceFlinger::destroyDisplay(const sp<IBinder>& display) { } void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) { + ALOGV("createBuiltinDisplayLocked(%d)", type); ALOGW_IF(mBuiltinDisplays[type], "Overwriting display token for display type %d", type); mBuiltinDisplays[type] = new BBinder(); @@ -442,28 +445,34 @@ void SurfaceFlinger::init() { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); - Mutex::Autolock _l(mStateLock); + { // Autolock scope + Mutex::Autolock _l(mStateLock); - // initialize EGL for the default display - mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(mEGLDisplay, NULL, NULL); + // initialize EGL for the default display + mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(mEGLDisplay, NULL, NULL); - // start the EventThread - sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, - vsyncPhaseOffsetNs, true, "app"); - mEventThread = new EventThread(vsyncSrc); - sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, - sfVsyncPhaseOffsetNs, true, "sf"); - mSFEventThread = new EventThread(sfVsyncSrc); - mEventQueue.setEventThread(mSFEventThread); + // start the EventThread + sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, + vsyncPhaseOffsetNs, true, "app"); + mEventThread = new EventThread(vsyncSrc); + sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, + sfVsyncPhaseOffsetNs, true, "sf"); + mSFEventThread = new EventThread(sfVsyncSrc); + mEventQueue.setEventThread(mSFEventThread); - // Initialize the H/W composer object. There may or may not be an - // actual hardware composer underneath. - mHwc = new HWComposer(this, - *static_cast<HWComposer::EventHandler *>(this)); + // Get a RenderEngine for the given display / config (can't fail) + mRenderEngine = RenderEngine::create(mEGLDisplay, + HAL_PIXEL_FORMAT_RGBA_8888); + } - // get a RenderEngine for the given display / config (can't fail) - mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc->getVisualID()); + // Drop the state lock while we initialize the hardware composer. We drop + // the lock because on creation, it will call back into SurfaceFlinger to + // initialize the primary display. + mHwc = new HWComposer(this); + mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this)); + + Mutex::Autolock _l(mStateLock); // retrieve the EGL context that was selected/created mEGLContext = mRenderEngine->getEGLContext(); @@ -471,51 +480,13 @@ void SurfaceFlinger::init() { LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT, "couldn't create EGLContext"); - // initialize our non-virtual displays - for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { - DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i); - // set-up the displays that are already connected - if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) { - // All non-virtual displays are currently considered secure. - bool isSecure = true; - createBuiltinDisplayLocked(type); - wp<IBinder> token = mBuiltinDisplays[i]; - - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer, - new GraphicBufferAlloc()); - - sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i, - consumer); - int32_t hwcId = allocateHwcDisplayId(type); - sp<DisplayDevice> hw = new DisplayDevice(this, - type, hwcId, mHwc->getFormat(hwcId), isSecure, token, - fbs, producer, - mRenderEngine->getEGLConfig()); - if (i > DisplayDevice::DISPLAY_PRIMARY) { - // FIXME: currently we don't get blank/unblank requests - // for displays other than the main display, so we always - // assume a connected display is unblanked. - ALOGD("marking display %zu as acquired/unblanked", i); - hw->setPowerMode(HWC_POWER_MODE_NORMAL); - } - mDisplays.add(token, hw); - } - } - - // make the GLContext current so that we can create textures when creating Layers - // (which may happens before we render something) + // make the GLContext current so that we can create textures when creating + // Layers (which may happens before we render something) getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); mEventControlThread = new EventControlThread(this); mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY); - // set a fake vsync period if there is no HWComposer - if (mHwc->initCheck() != NO_ERROR) { - mPrimaryDispSync.setPeriod(16666667); - } - // initialize our drawing state mDrawingState = mCurrentState; @@ -524,11 +495,8 @@ void SurfaceFlinger::init() { // start boot animation startBootAnim(); -} -int32_t SurfaceFlinger::allocateHwcDisplayId(DisplayDevice::DisplayType type) { - return (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) ? - type : mHwc->allocateDisplayId(); + ALOGV("Done initializing"); } void SurfaceFlinger::startBootAnim() { @@ -594,14 +562,11 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, configs->clear(); - const Vector<HWComposer::DisplayConfig>& hwConfigs = - getHwComposer().getConfigs(type); - for (size_t c = 0; c < hwConfigs.size(); ++c) { - const HWComposer::DisplayConfig& hwConfig = hwConfigs[c]; + for (const auto& hwConfig : getHwComposer().getConfigs(type)) { DisplayInfo info = DisplayInfo(); - float xdpi = hwConfig.xdpi; - float ydpi = hwConfig.ydpi; + float xdpi = hwConfig->getDpiX(); + float ydpi = hwConfig->getDpiY(); if (type == DisplayDevice::DISPLAY_PRIMARY) { // The density of the device is provided by a build property @@ -629,13 +594,15 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.orientation = 0; } - info.w = hwConfig.width; - info.h = hwConfig.height; + info.w = hwConfig->getWidth(); + info.h = hwConfig->getHeight(); info.xdpi = xdpi; info.ydpi = ydpi; - info.fps = float(1e9 / hwConfig.refresh); + info.fps = 1e9 / hwConfig->getVsyncPeriod(); info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS; - info.colorTransform = hwConfig.colorTransform; + + // TODO: Hook this back up + info.colorTransform = 0; // This is how far in advance a buffer must be queued for // presentation at a given time. If you want a buffer to appear @@ -649,8 +616,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, // // We add an additional 1ms to allow for processing time and // differences between the ideal and actual refresh rate. - info.presentationDeadline = - hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000; + info.presentationDeadline = hwConfig->getVsyncPeriod() - + SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000; // All non-virtual displays are currently considered secure. info.secure = true; @@ -812,8 +779,8 @@ void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) { return; } - const nsecs_t period = - getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const nsecs_t period = activeConfig->getVsyncPeriod(); mPrimaryDispSync.reset(); mPrimaryDispSync.setPeriod(period); @@ -839,7 +806,7 @@ void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) { } } -void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) { +void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) { bool needsHwVsync = false; { // Scope for the lock @@ -856,19 +823,34 @@ void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) { } } -void SurfaceFlinger::onHotplugReceived(int type, bool connected) { - if (mEventThread == NULL) { - // This is a temporary workaround for b/7145521. A non-null pointer - // does not mean EventThread has finished initializing, so this - // is not a correct fix. - ALOGW("WARNING: EventThread not started, ignoring hotplug"); - return; - } +void SurfaceFlinger::onHotplugReceived(int32_t disp, bool connected) { + ALOGV("onHotplugReceived(%d, %s)", disp, connected ? "true" : "false"); + if (disp == DisplayDevice::DISPLAY_PRIMARY) { + Mutex::Autolock lock(mStateLock); + + // All non-virtual displays are currently considered secure. + bool isSecure = true; - if (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + int32_t type = DisplayDevice::DISPLAY_PRIMARY; + createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY); + wp<IBinder> token = mBuiltinDisplays[type]; + + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer, + new GraphicBufferAlloc()); + + sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, + DisplayDevice::DISPLAY_PRIMARY, consumer); + sp<DisplayDevice> hw = new DisplayDevice(this, + DisplayDevice::DISPLAY_PRIMARY, disp, isSecure, token, fbs, + producer, mRenderEngine->getEGLConfig()); + mDisplays.add(token, hw); + } else { + auto type = DisplayDevice::DISPLAY_EXTERNAL; Mutex::Autolock _l(mStateLock); if (connected) { - createBuiltinDisplayLocked((DisplayDevice::DisplayType)type); + createBuiltinDisplayLocked(type); } else { mCurrentState.displays.removeItem(mBuiltinDisplays[type]); mBuiltinDisplays[type].clear(); @@ -879,9 +861,10 @@ void SurfaceFlinger::onHotplugReceived(int type, bool connected) { } } -void SurfaceFlinger::eventControl(int disp, int event, int enabled) { +void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) { ATRACE_CALL(); - getHwComposer().eventControl(disp, event, enabled); + getHwComposer().setVsyncEnabled(disp, + enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable); } void SurfaceFlinger::onMessageReceived(int32_t what) { @@ -950,6 +933,12 @@ void SurfaceFlinger::handleMessageRefresh() { postComposition(); } + // Release any buffers which were replaced this frame + for (auto& layer : mLayersWithQueuedFrames) { + layer->releasePendingBuffer(); + } + mLayersWithQueuedFrames.clear(); + previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0); } @@ -974,7 +963,6 @@ void SurfaceFlinger::doDebugFlashRegions() RenderEngine& engine(getRenderEngine()); engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1); - hw->compositionComplete(); hw->swapBuffers(getHwComposer()); } } @@ -986,15 +974,18 @@ void SurfaceFlinger::doDebugFlashRegions() usleep(mDebugRegion * 1000); } - HWComposer& hwc(getHwComposer()); - if (hwc.initCheck() == NO_ERROR) { - status_t err = hwc.prepare(); - ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err)); + for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { + status_t result = mDisplays[displayId]->prepareFrame(*mHwc); + ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:" + " %d (%s)", displayId, result, strerror(-result)); } } void SurfaceFlinger::preComposition() { + ATRACE_CALL(); + ALOGV("preComposition"); + bool needExtraInvalidate = false; const LayerVector& layers(mDrawingState.layersSortedByZ); const size_t count = layers.size(); @@ -1010,14 +1001,16 @@ void SurfaceFlinger::preComposition() void SurfaceFlinger::postComposition() { + ATRACE_CALL(); + ALOGV("postComposition"); + const LayerVector& layers(mDrawingState.layersSortedByZ); const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { layers[i]->onPostComposition(); } - const HWComposer& hwc = getHwComposer(); - sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY); + sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY); if (presentFence->isValid()) { if (mPrimaryDispSync.addPresentFence(presentFence)) { @@ -1042,7 +1035,8 @@ void SurfaceFlinger::postComposition() } else { // The HWC doesn't support present fences, so use the refresh // timestamp instead. - nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY); + nsecs_t presentTime = + mHwc->getRefreshTimestamp(HWC_DISPLAY_PRIMARY); mAnimFrameTracker.setActualPresentTime(presentTime); } mAnimFrameTracker.advanceFrame(); @@ -1070,6 +1064,9 @@ void SurfaceFlinger::postComposition() } void SurfaceFlinger::rebuildLayerStacks() { + ATRACE_CALL(); + ALOGV("rebuildLayerStacks"); + // rebuild the visible layer list per screen if (CC_UNLIKELY(mVisibleRegionsDirty)) { ATRACE_CALL(); @@ -1080,37 +1077,47 @@ void SurfaceFlinger::rebuildLayerStacks() { for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { Region opaqueRegion; Region dirtyRegion; - Vector< sp<Layer> > layersSortedByZ; - const sp<DisplayDevice>& hw(mDisplays[dpy]); - const Transform& tr(hw->getTransform()); - const Rect bounds(hw->getBounds()); - if (hw->isDisplayOn()) { + Vector<sp<Layer>> layersSortedByZ; + const sp<DisplayDevice>& displayDevice(mDisplays[dpy]); + const Transform& tr(displayDevice->getTransform()); + const Rect bounds(displayDevice->getBounds()); + if (displayDevice->isDisplayOn()) { SurfaceFlinger::computeVisibleRegions(layers, - hw->getLayerStack(), dirtyRegion, opaqueRegion); + displayDevice->getLayerStack(), dirtyRegion, + opaqueRegion); const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { const sp<Layer>& layer(layers[i]); const Layer::State& s(layer->getDrawingState()); - if (s.layerStack == hw->getLayerStack()) { + if (s.layerStack == displayDevice->getLayerStack()) { Region drawRegion(tr.transform( layer->visibleNonTransparentRegion)); drawRegion.andSelf(bounds); if (!drawRegion.isEmpty()) { layersSortedByZ.add(layer); + } else { + // Clear out the HWC layer if this layer was + // previously visible, but no longer is + layer->setHwcLayer(displayDevice->getHwcDisplayId(), + nullptr); } } } } - hw->setVisibleLayersSortedByZ(layersSortedByZ); - hw->undefinedRegion.set(bounds); - hw->undefinedRegion.subtractSelf(tr.transform(opaqueRegion)); - hw->dirtyRegion.orSelf(dirtyRegion); + displayDevice->setVisibleLayersSortedByZ(layersSortedByZ); + displayDevice->undefinedRegion.set(bounds); + displayDevice->undefinedRegion.subtractSelf( + tr.transform(opaqueRegion)); + displayDevice->dirtyRegion.orSelf(dirtyRegion); } } } void SurfaceFlinger::setUpHWComposer() { + ATRACE_CALL(); + ALOGV("setUpHWComposer"); + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { bool dirty = !mDisplays[dpy]->getDirtyRegion(false).isEmpty(); bool empty = mDisplays[dpy]->getVisibleLayersSortedByZ().size() == 0; @@ -1140,86 +1147,61 @@ void SurfaceFlinger::setUpHWComposer() { } } - HWComposer& hwc(getHwComposer()); - if (hwc.initCheck() == NO_ERROR) { - // build the h/w work list - if (CC_UNLIKELY(mHwWorkListDirty)) { - mHwWorkListDirty = false; - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> hw(mDisplays[dpy]); - const int32_t id = hw->getHwcDisplayId(); - if (id >= 0) { - const Vector< sp<Layer> >& currentLayers( - hw->getVisibleLayersSortedByZ()); - const size_t count = currentLayers.size(); - if (hwc.createWorkList(id, count) == NO_ERROR) { - HWComposer::LayerListIterator cur = hwc.begin(id); - const HWComposer::LayerListIterator end = hwc.end(id); - for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { - const sp<Layer>& layer(currentLayers[i]); - layer->setGeometry(hw, *cur); - if (mDebugDisableHWC || mDebugRegion || mDaltonize || mHasColorMatrix) { - cur->setSkip(true); - } + // build the h/w work list + if (CC_UNLIKELY(mGeometryInvalid)) { + mGeometryInvalid = false; + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> displayDevice(mDisplays[dpy]); + const auto hwcId = displayDevice->getHwcDisplayId(); + if (hwcId >= 0) { + const Vector<sp<Layer>>& currentLayers( + displayDevice->getVisibleLayersSortedByZ()); + bool foundLayerWithoutHwc = false; + for (auto& layer : currentLayers) { + if (!layer->hasHwcLayer(hwcId)) { + auto hwcLayer = mHwc->createLayer(hwcId); + if (hwcLayer) { + layer->setHwcLayer(hwcId, std::move(hwcLayer)); + } else { + layer->forceClientComposition(hwcId); + foundLayerWithoutHwc = true; + continue; } } - } - } - } - // set the per-frame data - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> hw(mDisplays[dpy]); - const int32_t id = hw->getHwcDisplayId(); - if (id >= 0) { - const Vector< sp<Layer> >& currentLayers( - hw->getVisibleLayersSortedByZ()); - const size_t count = currentLayers.size(); - HWComposer::LayerListIterator cur = hwc.begin(id); - const HWComposer::LayerListIterator end = hwc.end(id); - for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { - /* - * update the per-frame h/w composer data for each layer - * and build the transparent region of the FB - */ - const sp<Layer>& layer(currentLayers[i]); - layer->setPerFrameData(hw, *cur); - } - } - } - - // If possible, attempt to use the cursor overlay on each display. - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> hw(mDisplays[dpy]); - const int32_t id = hw->getHwcDisplayId(); - if (id >= 0) { - const Vector< sp<Layer> >& currentLayers( - hw->getVisibleLayersSortedByZ()); - const size_t count = currentLayers.size(); - HWComposer::LayerListIterator cur = hwc.begin(id); - const HWComposer::LayerListIterator end = hwc.end(id); - for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { - const sp<Layer>& layer(currentLayers[i]); - if (layer->isPotentialCursor()) { - cur->setIsCursorLayerHint(); - break; + layer->setGeometry(displayDevice); + if (mDebugDisableHWC || mDebugRegion || mDaltonize || + mHasColorMatrix) { + layer->forceClientComposition(hwcId); } } } } + } - status_t err = hwc.prepare(); - ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err)); - - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> hw(mDisplays[dpy]); - hw->prepareFrame(hwc); + // Set the per-frame data + for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { + auto& displayDevice = mDisplays[displayId]; + const auto hwcId = displayDevice->getHwcDisplayId(); + if (hwcId < 0) { + continue; + } + for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + layer->setPerFrameData(displayDevice); } } + + for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { + status_t result = mDisplays[displayId]->prepareFrame(*mHwc); + ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:" + " %d (%s)", displayId, result, strerror(-result)); + } } void SurfaceFlinger::doComposition() { ATRACE_CALL(); + ALOGV("doComposition"); + const bool repaintEverything = android_atomic_and(0, &mRepaintEverything); for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { const sp<DisplayDevice>& hw(mDisplays[dpy]); @@ -1234,8 +1216,6 @@ void SurfaceFlinger::doComposition() { hw->flip(hw->swapRegion); hw->swapRegion.clear(); } - // inform the h/w that we're done compositing - hw->compositionComplete(); } postFramebuffer(); } @@ -1243,43 +1223,37 @@ void SurfaceFlinger::doComposition() { void SurfaceFlinger::postFramebuffer() { ATRACE_CALL(); + ALOGV("postFramebuffer"); const nsecs_t now = systemTime(); mDebugInSwapBuffers = now; - HWComposer& hwc(getHwComposer()); - if (hwc.initCheck() == NO_ERROR) { - if (!hwc.supportsFramebufferTarget()) { - // EGL spec says: - // "surface must be bound to the calling thread's current context, - // for the current rendering API." - getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); - } - hwc.commit(); - } - - // make the default display current because the VirtualDisplayDevice code cannot - // deal with dequeueBuffer() being called outside of the composition loop; however - // the code below can call glFlush() which is allowed (and does in some case) call - // dequeueBuffer(). - getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); - - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> hw(mDisplays[dpy]); - const Vector< sp<Layer> >& currentLayers(hw->getVisibleLayersSortedByZ()); - hw->onSwapBuffersCompleted(hwc); - const size_t count = currentLayers.size(); - int32_t id = hw->getHwcDisplayId(); - if (id >=0 && hwc.initCheck() == NO_ERROR) { - HWComposer::LayerListIterator cur = hwc.begin(id); - const HWComposer::LayerListIterator end = hwc.end(id); - for (size_t i = 0; cur != end && i < count; ++i, ++cur) { - currentLayers[i]->onLayerDisplayed(hw, &*cur); - } - } else { - for (size_t i = 0; i < count; i++) { - currentLayers[i]->onLayerDisplayed(hw, NULL); + for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { + auto& displayDevice = mDisplays[displayId]; + const auto hwcId = displayDevice->getHwcDisplayId(); + if (hwcId >= 0) { + mHwc->commit(hwcId); + } + if (displayId == 0) { + // Make the default display current because the VirtualDisplayDevice + // code cannot deal with dequeueBuffer() being called outside of the + // composition loop; however the code below can call glFlush() which + // is allowed to (and does in some case) call dequeueBuffer(). + displayDevice->makeCurrent(mEGLDisplay, mEGLContext); + } + displayDevice->onSwapBuffersCompleted(); + for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + sp<Fence> releaseFence = Fence::NO_FENCE; + if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) { + releaseFence = displayDevice->getClientTargetAcquireFence(); + } else { + auto hwcLayer = layer->getHwcLayer(hwcId); + releaseFence = mHwc->getLayerReleaseFence(hwcId, hwcLayer); } + layer->onLayerDisplayed(releaseFence); + } + if (hwcId >= 0) { + mHwc->clearReleaseFences(hwcId); } } @@ -1439,7 +1413,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) BufferQueue::createBufferQueue(&bqProducer, &bqConsumer, new GraphicBufferAlloc()); - int32_t hwcDisplayId = -1; + int32_t hwcId = -1; if (state.isVirtualDisplay()) { // Virtual displays without a surface are dormant: // they have external state (layer stack, projection, @@ -1456,15 +1430,14 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) NATIVE_WINDOW_HEIGHT, &height); ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status); - if (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 || - (width <= MAX_VIRTUAL_DISPLAY_DIMENSION && - height <= MAX_VIRTUAL_DISPLAY_DIMENSION)) { - hwcDisplayId = allocateHwcDisplayId(state.type); - } - sp<VirtualDisplaySurface> vds = new VirtualDisplaySurface( - *mHwc, hwcDisplayId, state.surface, - bqProducer, bqConsumer, state.displayName); + mHwc->allocateVirtualDisplay(width, height, + &hwcId); + + sp<VirtualDisplaySurface> vds = + new VirtualDisplaySurface(*mHwc, + hwcId, state.surface, bqProducer, + bqConsumer, state.displayName); dispSurface = vds; producer = vds; @@ -1474,33 +1447,30 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) "adding a supported display, but rendering " "surface is provided (%p), ignoring it", state.surface.get()); - hwcDisplayId = allocateHwcDisplayId(state.type); - // for supported (by hwc) displays we provide our - // own rendering surface - dispSurface = new FramebufferSurface(*mHwc, state.type, - bqConsumer); - producer = bqProducer; + if (state.type == DisplayDevice::DISPLAY_EXTERNAL) { + hwcId = DisplayDevice::DISPLAY_EXTERNAL; + dispSurface = new FramebufferSurface(*mHwc, + DisplayDevice::DISPLAY_EXTERNAL, + bqConsumer); + producer = bqProducer; + } else { + ALOGE("Attempted to add non-external non-virtual" + " display"); + } } const wp<IBinder>& display(curr.keyAt(i)); if (dispSurface != NULL) { sp<DisplayDevice> hw = new DisplayDevice(this, - state.type, hwcDisplayId, - mHwc->getFormat(hwcDisplayId), state.isSecure, - display, dispSurface, producer, + state.type, hwcId, state.isSecure, display, + dispSurface, producer, mRenderEngine->getEGLConfig()); hw->setLayerStack(state.layerStack); hw->setProjection(state.orientation, state.viewport, state.frame); hw->setDisplayName(state.displayName); mDisplays.add(display, hw); - if (state.isVirtualDisplay()) { - if (hwcDisplayId >= 0) { - mHwc->setVirtualDisplayProperties(hwcDisplayId, - hw->getWidth(), hw->getHeight(), - hw->getFormat()); - } - } else { + if (!state.isVirtualDisplay()) { mEventThread->onHotplugReceived(state.type, true); } } @@ -1593,7 +1563,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // compute the actual visible region // TODO: we could cache the transformed region const Layer::State& s(layer->getDrawingState()); - Region visibleReg = s.transform.transform( + Region visibleReg = s.active.transform.transform( Region(Rect(s.active.w, s.active.h))); invalidateLayerStack(s.layerStack, visibleReg); } @@ -1607,26 +1577,14 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) void SurfaceFlinger::updateCursorAsync() { - HWComposer& hwc(getHwComposer()); - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> hw(mDisplays[dpy]); - const int32_t id = hw->getHwcDisplayId(); - if (id < 0) { + for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { + auto& displayDevice = mDisplays[displayId]; + if (displayDevice->getHwcDisplayId() < 0) { continue; } - const Vector< sp<Layer> >& currentLayers( - hw->getVisibleLayersSortedByZ()); - const size_t count = currentLayers.size(); - HWComposer::LayerListIterator cur = hwc.begin(id); - const HWComposer::LayerListIterator end = hwc.end(id); - for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { - if (cur->getCompositionType() != HWC_CURSOR_OVERLAY) { - continue; - } - const sp<Layer>& layer(currentLayers[i]); - Rect cursorPos = layer->getPosition(hw); - hwc.setCursorPositionAsync(id, cursorPos); - break; + + for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + layer->updateCursorPosition(displayDevice); } } } @@ -1656,6 +1614,7 @@ void SurfaceFlinger::computeVisibleRegions( Region& outDirtyRegion, Region& outOpaqueRegion) { ATRACE_CALL(); + ALOGV("computeVisibleRegions"); Region aboveOpaqueLayers; Region aboveCoveredLayers; @@ -1707,12 +1666,12 @@ void SurfaceFlinger::computeVisibleRegions( // handle hidden surfaces by setting the visible region to empty if (CC_LIKELY(layer->isVisible())) { const bool translucent = !layer->isOpaque(s); - Rect bounds(s.transform.transform(layer->computeBounds())); + Rect bounds(s.active.transform.transform(layer->computeBounds())); visibleRegion.set(bounds); if (!visibleRegion.isEmpty()) { // Remove the transparent area from the visible region if (translucent) { - const Transform tr(s.transform); + const Transform tr(s.active.transform); if (tr.transformed()) { if (tr.preserveRects()) { // transform the transparent region @@ -1728,8 +1687,8 @@ void SurfaceFlinger::computeVisibleRegions( } // compute the opaque region - const int32_t layerOrientation = s.transform.getOrientation(); - if (s.alpha==255 && !translucent && + const int32_t layerOrientation = s.active.transform.getOrientation(); + if (s.alpha == 1.0f && !translucent && ((layerOrientation & Transform::ROT_INVALID) == false)) { // the opaque region is the layer's footprint opaqueRegion = visibleRegion; @@ -1802,6 +1761,8 @@ void SurfaceFlinger::invalidateLayerStack(uint32_t layerStack, bool SurfaceFlinger::handlePageFlip() { + ALOGV("handlePageFlip"); + Region dirtyRegion; bool visibleRegions = false; @@ -1817,13 +1778,12 @@ bool SurfaceFlinger::handlePageFlip() // 3.) Layer 1 is latched. // Display is now waiting on Layer 1's frame, which is behind layer 0's // second frame. But layer 0's second frame could be waiting on display. - Vector<Layer*> layersWithQueuedFrames; for (size_t i = 0, count = layers.size(); i<count ; i++) { const sp<Layer>& layer(layers[i]); if (layer->hasQueuedFrame()) { frameQueued = true; if (layer->shouldPresentNow(mPrimaryDispSync)) { - layersWithQueuedFrames.push_back(layer.get()); + mLayersWithQueuedFrames.push_back(layer.get()); } else { layer->useEmptyDamage(); } @@ -1831,8 +1791,7 @@ bool SurfaceFlinger::handlePageFlip() layer->useEmptyDamage(); } } - for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) { - Layer* layer = layersWithQueuedFrames[i]; + for (auto& layer : mLayersWithQueuedFrames) { const Region dirty(layer->latchBuffer(visibleRegions)); layer->useSurfaceDamage(); const Layer::State& s(layer->getDrawingState()); @@ -1844,17 +1803,17 @@ bool SurfaceFlinger::handlePageFlip() // If we will need to wake up at some time in the future to deal with a // queued frame that shouldn't be displayed during this vsync period, wake // up during the next vsync period to check again. - if (frameQueued && layersWithQueuedFrames.empty()) { + if (frameQueued && mLayersWithQueuedFrames.empty()) { signalLayerUpdate(); } // Only continue with the refresh if there is actually new work to do - return !layersWithQueuedFrames.empty(); + return !mLayersWithQueuedFrames.empty(); } void SurfaceFlinger::invalidateHwcGeometry() { - mHwWorkListDirty = true; + mGeometryInvalid = true; } @@ -1867,9 +1826,12 @@ void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, // 2) There is work to be done (the dirty region isn't empty) bool isHwcDisplay = hw->getHwcDisplayId() >= 0; if (!isHwcDisplay && inDirtyRegion.isEmpty()) { + ALOGV("Skipping display composition"); return; } + ALOGV("doDisplayComposition"); + Region dirtyRegion(inDirtyRegion); // compute the invalid region @@ -1915,19 +1877,19 @@ void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, hw->swapBuffers(getHwComposer()); } -bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty) +bool SurfaceFlinger::doComposeSurfaces( + const sp<const DisplayDevice>& displayDevice, const Region& dirty) { - RenderEngine& engine(getRenderEngine()); - const int32_t id = hw->getHwcDisplayId(); - HWComposer& hwc(getHwComposer()); - HWComposer::LayerListIterator cur = hwc.begin(id); - const HWComposer::LayerListIterator end = hwc.end(id); + ALOGV("doComposeSurfaces"); - bool hasGlesComposition = hwc.hasGlesComposition(id); - if (hasGlesComposition) { - if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) { + const auto hwcId = displayDevice->getHwcDisplayId(); + bool hasClientComposition = mHwc->hasClientComposition(hwcId); + if (hasClientComposition) { + ALOGV("hasClientComposition"); + + if (!displayDevice->makeCurrent(mEGLDisplay, mEGLContext)) { ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s", - hw->getDisplayName().string()); + displayDevice->getDisplayName().string()); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if(!getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext)) { ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting."); @@ -1936,25 +1898,25 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const } // Never touch the framebuffer if we don't have any framebuffer layers - const bool hasHwcComposition = hwc.hasHwcComposition(id); - if (hasHwcComposition) { + const bool hasDeviceComposition = mHwc->hasDeviceComposition(hwcId); + if (hasDeviceComposition) { // when using overlays, we assume a fully transparent framebuffer // NOTE: we could reduce how much we need to clear, for instance // remove where there are opaque FB layers. however, on some // GPUs doing a "clean slate" clear might be more efficient. // We'll revisit later if needed. - engine.clearWithColor(0, 0, 0, 0); + mRenderEngine->clearWithColor(0, 0, 0, 0); } else { // we start with the whole screen area - const Region bounds(hw->getBounds()); + const Region bounds(displayDevice->getBounds()); // we remove the scissor part // we're left with the letterbox region // (common case is that letterbox ends-up being empty) - const Region letterbox(bounds.subtract(hw->getScissor())); + const Region letterbox(bounds.subtract(displayDevice->getScissor())); // compute the area to clear - Region region(hw->undefinedRegion.merge(letterbox)); + Region region(displayDevice->undefinedRegion.merge(letterbox)); // but limit it to the dirty region region.andSelf(dirty); @@ -1962,24 +1924,24 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const // screen is already cleared here if (!region.isEmpty()) { // can happen with SurfaceView - drawWormhole(hw, region); + drawWormhole(displayDevice, region); } } - if (hw->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) { + if (displayDevice->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) { // just to be on the safe side, we don't set the // scissor on the main display. It should never be needed // anyways (though in theory it could since the API allows it). - const Rect& bounds(hw->getBounds()); - const Rect& scissor(hw->getScissor()); + const Rect& bounds(displayDevice->getBounds()); + const Rect& scissor(displayDevice->getScissor()); if (scissor != bounds) { // scissor doesn't match the screen's dimensions, so we // need to clear everything outside of it and enable // the GL scissor so we don't draw anything where we shouldn't // enable scissor for this frame - const uint32_t height = hw->getHeight(); - engine.setScissor(scissor.left, height - scissor.bottom, + const uint32_t height = displayDevice->getHeight(); + mRenderEngine->setScissor(scissor.left, height - scissor.bottom, scissor.getWidth(), scissor.getHeight()); } } @@ -1989,57 +1951,57 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const * and then, render the layers targeted at the framebuffer */ - const Vector< sp<Layer> >& layers(hw->getVisibleLayersSortedByZ()); - const size_t count = layers.size(); - const Transform& tr = hw->getTransform(); - if (cur != end) { + ALOGV("Rendering client layers"); + const Transform& displayTransform = displayDevice->getTransform(); + if (hwcId >= 0) { // we're using h/w composer - for (size_t i=0 ; i<count && cur!=end ; ++i, ++cur) { - const sp<Layer>& layer(layers[i]); - const Region clip(dirty.intersect(tr.transform(layer->visibleRegion))); + bool firstLayer = true; + for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + const Region clip(dirty.intersect( + displayTransform.transform(layer->visibleRegion))); + ALOGV("Layer: %s", layer->getName().string()); + ALOGV(" Composition type: %s", + to_string(layer->getCompositionType(hwcId)).c_str()); if (!clip.isEmpty()) { - switch (cur->getCompositionType()) { - case HWC_CURSOR_OVERLAY: - case HWC_OVERLAY: { + switch (layer->getCompositionType(hwcId)) { + case HWC2::Composition::Cursor: + case HWC2::Composition::Device: + case HWC2::Composition::SolidColor: { const Layer::State& state(layer->getDrawingState()); - if ((cur->getHints() & HWC_HINT_CLEAR_FB) - && i - && layer->isOpaque(state) && (state.alpha == 0xFF) - && hasGlesComposition) { + if (layer->getClearClientTarget(hwcId) && !firstLayer && + layer->isOpaque(state) && (state.alpha == 1.0f) + && hasClientComposition) { // never clear the very first layer since we're // guaranteed the FB is already cleared - layer->clearWithOpenGL(hw, clip); + layer->clearWithOpenGL(displayDevice, clip); } break; } - case HWC_FRAMEBUFFER: { - layer->draw(hw, clip); + case HWC2::Composition::Client: { + layer->draw(displayDevice, clip); break; } - case HWC_FRAMEBUFFER_TARGET: { - // this should not happen as the iterator shouldn't - // let us get there. - ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%zu)", i); + default: break; - } } + } else { + ALOGV(" Skipping for empty clip"); } - layer->setAcquireFence(hw, *cur); + firstLayer = false; } } else { // we're not using h/w composer - for (size_t i=0 ; i<count ; ++i) { - const sp<Layer>& layer(layers[i]); + for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { const Region clip(dirty.intersect( - tr.transform(layer->visibleRegion))); + displayTransform.transform(layer->visibleRegion))); if (!clip.isEmpty()) { - layer->draw(hw, clip); + layer->draw(displayDevice, clip); } } } // disable scissor at the end of the frame - engine.disableScissor(); + mRenderEngine->disableScissor(); return true; } @@ -2255,7 +2217,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( } } if (what & layer_state_t::eAlphaChanged) { - if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) + if (layer->setAlpha(s.alpha)) flags |= eTraversalNeeded; } if (what & layer_state_t::eMatrixChanged) { @@ -2423,8 +2385,8 @@ void SurfaceFlinger::onInitializeDisplays() { setTransactionState(state, displays, 0); setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL); - const nsecs_t period = - getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const nsecs_t period = activeConfig->getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(period); } @@ -2611,8 +2573,8 @@ void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index index++; } - const nsecs_t period = - getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const nsecs_t period = activeConfig->getVsyncPeriod(); result.appendFormat("%" PRId64 "\n", period); if (name.isEmpty()) { @@ -2734,13 +2696,15 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.append(SyncFeatures::getInstance().toString()); result.append("\n"); + const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + colorizer.bold(result); result.append("DispSync configuration: "); colorizer.reset(result); result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, " "present offset %d ns (refresh %" PRId64 " ns)", - vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, PRESENT_TIME_OFFSET_FROM_VSYNC_NS, - mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY)); + vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, + PRESENT_TIME_OFFSET_FROM_VSYNC_NS, activeConfig->getVsyncPeriod()); result.append("\n"); // Dump static screen stats @@ -2808,9 +2772,9 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0, mTransactionFlags, - 1e9 / hwc.getRefreshPeriod(HWC_DISPLAY_PRIMARY), - hwc.getDpiX(HWC_DISPLAY_PRIMARY), - hwc.getDpiY(HWC_DISPLAY_PRIMARY), + 1e9 / activeConfig->getVsyncPeriod(), + activeConfig->getDpiX(), + activeConfig->getDpiY(), !mGpuToCpuSupported); result.appendFormat(" eglSwapBuffers time: %f us\n", @@ -2830,10 +2794,10 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, colorizer.bold(result); result.append("h/w composer state:\n"); colorizer.reset(result); - result.appendFormat(" h/w composer %s and %s\n", - hwc.initCheck()==NO_ERROR ? "present" : "not present", - (mDebugDisableHWC || mDebugRegion || mDaltonize - || mHasColorMatrix) ? "disabled" : "enabled"); + bool hwcDisabled = mDebugDisableHWC || mDebugRegion || mDaltonize || + mHasColorMatrix; + result.appendFormat(" h/w composer %s\n", + hwcDisabled ? "disabled" : "enabled"); hwc.dump(result); /* @@ -3183,14 +3147,7 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, // if we have secure windows on this display, never allow the screen capture // unless the producer interface is local (i.e.: we can take a screenshot for // ourselves). - if (!IInterface::asBinder(producer)->localBinder()) { - Mutex::Autolock _l(mStateLock); - sp<const DisplayDevice> hw(getDisplayDevice(display)); - if (hw->getSecureLayerVisible()) { - ALOGW("FB is protected: PERMISSION_DENIED"); - return PERMISSION_DENIED; - } - } + bool isLocalScreenshot = IInterface::asBinder(producer)->localBinder(); // Convert to surfaceflinger's internal rotation type. Transform::orientation_flags rotationFlags; @@ -3223,19 +3180,22 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, bool useIdentityTransform; Transform::orientation_flags rotation; status_t result; + bool isLocalScreenshot; public: MessageCaptureScreen(SurfaceFlinger* flinger, const sp<IBinder>& display, const sp<IGraphicBufferProducer>& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ, - bool useIdentityTransform, Transform::orientation_flags rotation) + bool useIdentityTransform, + Transform::orientation_flags rotation, + bool isLocalScreenshot) : flinger(flinger), display(display), producer(producer), sourceCrop(sourceCrop), reqWidth(reqWidth), reqHeight(reqHeight), minLayerZ(minLayerZ), maxLayerZ(maxLayerZ), useIdentityTransform(useIdentityTransform), - rotation(rotation), - result(PERMISSION_DENIED) + rotation(rotation), result(PERMISSION_DENIED), + isLocalScreenshot(isLocalScreenshot) { } status_t getResult() const { @@ -3246,7 +3206,7 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<const DisplayDevice> hw(flinger->getDisplayDevice(display)); result = flinger->captureScreenImplLocked(hw, producer, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, - useIdentityTransform, rotation); + useIdentityTransform, rotation, isLocalScreenshot); static_cast<GraphicProducerWrapper*>(IInterface::asBinder(producer).get())->exit(result); return true; } @@ -3269,7 +3229,7 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<MessageBase> msg = new MessageCaptureScreen(this, display, IGraphicBufferProducer::asInterface( wrapper ), sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, - useIdentityTransform, rotationFlags); + useIdentityTransform, rotationFlags, isLocalScreenshot); status_t res = postMessageAsync(msg); if (res == NO_ERROR) { @@ -3342,8 +3302,6 @@ void SurfaceFlinger::renderScreenImplLocked( } } - // compositionComplete is needed for older driver - hw->compositionComplete(); hw->setViewportAndProjection(); } @@ -3353,7 +3311,8 @@ status_t SurfaceFlinger::captureScreenImplLocked( const sp<IGraphicBufferProducer>& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ, - bool useIdentityTransform, Transform::orientation_flags rotation) + bool useIdentityTransform, Transform::orientation_flags rotation, + bool isLocalScreenshot) { ATRACE_CALL(); @@ -3374,6 +3333,24 @@ status_t SurfaceFlinger::captureScreenImplLocked( reqWidth = (!reqWidth) ? hw_w : reqWidth; reqHeight = (!reqHeight) ? hw_h : reqHeight; + bool secureLayerIsVisible = false; + const LayerVector& layers(mDrawingState.layersSortedByZ); + const size_t count = layers.size(); + for (size_t i = 0 ; i < count ; ++i) { + const sp<Layer>& layer(layers[i]); + const Layer::State& state(layer->getDrawingState()); + if (state.layerStack == hw->getLayerStack() && state.z >= minLayerZ && + state.z <= maxLayerZ && layer->isVisible() && + layer->isSecure()) { + secureLayerIsVisible = true; + } + } + + if (!isLocalScreenshot && secureLayerIsVisible) { + ALOGW("FB is protected: PERMISSION_DENIED"); + return PERMISSION_DENIED; + } + // create a surface (because we're a producer, and we need to // dequeue/queue a buffer) sp<Surface> sur = new Surface(producer, false); @@ -3501,7 +3478,7 @@ void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* v const bool visible = (state.layerStack == hw->getLayerStack()) && (state.z >= minLayerZ && state.z <= maxLayerZ) && (layer->isVisible()); - ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x", + ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f", visible ? '+' : '-', i, layer->getName().string(), state.layerStack, state.z, layer->isVisible(), state.flags, state.alpha); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 4e0160a1d4..7d6f139157 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -119,7 +119,11 @@ public: // enable/disable h/w composer event // TODO: this should be made accessible only to EventThread +#ifdef USE_HWC2 + void setVsyncEnabled(int disp, int enabled); +#else void eventControl(int disp, int event, int enabled); +#endif // called on the main thread by MessageQueue when an internal message // is received @@ -329,7 +333,8 @@ private: const sp<IGraphicBufferProducer>& producer, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ, - bool useIdentityTransform, Transform::orientation_flags rotation); + bool useIdentityTransform, Transform::orientation_flags rotation, + bool isLocalScreenshot); /* ------------------------------------------------------------------------ * EGL @@ -360,8 +365,9 @@ private: // region of all screens presenting this layer stack. void invalidateLayerStack(uint32_t layerStack, const Region& dirty); - // allocate a h/w composer display id +#ifndef USE_HWC2 int32_t allocateHwcDisplayId(DisplayDevice::DisplayType type); +#endif /* ------------------------------------------------------------------------ * H/W composer @@ -457,8 +463,15 @@ private: // don't need synchronization State mDrawingState; bool mVisibleRegionsDirty; +#ifndef USE_HWC2 bool mHwWorkListDirty; +#else + bool mGeometryInvalid; +#endif bool mAnimCompositionPending; +#ifdef USE_HWC2 + std::vector<sp<Layer>> mLayersWithQueuedFrames; +#endif // this may only be written from the main thread with mStateLock held // it may be read from other threads with mStateLock held diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp index d3b66e6a2e..4c80fa063a 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp +++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp @@ -87,7 +87,11 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter, } // Release the previous buffer. +#ifdef USE_HWC2 + err = updateAndReleaseLocked(item, &mPendingRelease); +#else err = updateAndReleaseLocked(item); +#endif if (err != NO_ERROR) { return err; } @@ -183,6 +187,40 @@ nsecs_t SurfaceFlingerConsumer::computeExpectedPresent(const DispSync& dispSync) return nextRefresh + extraPadding; } +#ifdef USE_HWC2 +void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence) +{ + if (!mPendingRelease.isPending) { + GLConsumer::setReleaseFence(fence); + return; + } + auto currentTexture = mPendingRelease.currentTexture; + if (fence->isValid() && + currentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + status_t result = addReleaseFence(currentTexture, + mPendingRelease.graphicBuffer, fence); + ALOGE_IF(result != NO_ERROR, "setReleaseFence: failed to add the" + " fence: %s (%d)", strerror(-result), result); + } +} + +void SurfaceFlingerConsumer::releasePendingBuffer() +{ + if (!mPendingRelease.isPending) { + ALOGV("Pending buffer already released"); + return; + } + ALOGV("Releasing pending buffer"); + Mutex::Autolock lock(mMutex); + status_t result = releaseBufferLocked(mPendingRelease.currentTexture, + mPendingRelease.graphicBuffer, mPendingRelease.display, + mPendingRelease.fence); + ALOGE_IF(result != NO_ERROR, "releasePendingBuffer failed: %s (%d)", + strerror(-result), result); + mPendingRelease = PendingRelease(); +} +#endif + void SurfaceFlingerConsumer::setContentsChangedListener( const wp<ContentsChangedListener>& listener) { setFrameAvailableListener(listener); diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h index f3942abee9..f40d53e964 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.h +++ b/services/surfaceflinger/SurfaceFlingerConsumer.h @@ -75,6 +75,11 @@ public: nsecs_t computeExpectedPresent(const DispSync& dispSync); +#ifdef USE_HWC2 + virtual void setReleaseFence(const sp<Fence>& fence) override; + void releasePendingBuffer(); +#endif + private: virtual void onSidebandStreamChanged(); @@ -87,6 +92,12 @@ private: // The portion of this surface that has changed since the previous frame Region mSurfaceDamage; + +#ifdef USE_HWC2 + // A release that is pending on the receipt of a new release fence from + // presentDisplay + PendingRelease mPendingRelease; +#endif }; // ---------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp new file mode 100644 index 0000000000..16332f34cd --- /dev/null +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -0,0 +1,3590 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <stdint.h> +#include <sys/types.h> +#include <errno.h> +#include <math.h> +#include <dlfcn.h> +#include <inttypes.h> +#include <stdatomic.h> + +#include <EGL/egl.h> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryHeapBase.h> +#include <binder/PermissionCache.h> + +#include <ui/DisplayInfo.h> +#include <ui/DisplayStatInfo.h> + +#include <gui/BitTube.h> +#include <gui/BufferQueue.h> +#include <gui/GuiConfig.h> +#include <gui/IDisplayEventConnection.h> +#include <gui/Surface.h> +#include <gui/GraphicBufferAlloc.h> + +#include <ui/GraphicBufferAllocator.h> +#include <ui/PixelFormat.h> +#include <ui/UiConfig.h> + +#include <utils/misc.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/StopWatch.h> +#include <utils/Timers.h> +#include <utils/Trace.h> + +#include <private/android_filesystem_config.h> +#include <private/gui/SyncFeatures.h> + +#include "Client.h" +#include "clz.h" +#include "Colorizer.h" +#include "DdmConnection.h" +#include "DisplayDevice.h" +#include "DispSync.h" +#include "EventControlThread.h" +#include "EventThread.h" +#include "Layer.h" +#include "LayerDim.h" +#include "SurfaceFlinger.h" + +#include "DisplayHardware/FramebufferSurface.h" +#include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/VirtualDisplaySurface.h" + +#include "Effects/Daltonizer.h" + +#include "RenderEngine/RenderEngine.h" +#include <cutils/compiler.h> + +#define DISPLAY_COUNT 1 + +/* + * DEBUG_SCREENSHOTS: set to true to check that screenshots are not all + * black pixels. + */ +#define DEBUG_SCREENSHOTS false + +EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); + +namespace android { + +// This is the phase offset in nanoseconds of the software vsync event +// relative to the vsync event reported by HWComposer. The software vsync +// event is when SurfaceFlinger and Choreographer-based applications run each +// frame. +// +// This phase offset allows adjustment of the minimum latency from application +// wake-up (by Choregographer) time to the time at which the resulting window +// image is displayed. This value may be either positive (after the HW vsync) +// or negative (before the HW vsync). Setting it to 0 will result in a +// minimum latency of two vsync periods because the app and SurfaceFlinger +// will run just after the HW vsync. Setting it to a positive number will +// result in the minimum latency being: +// +// (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD)) +// +// Note that reducing this latency makes it more likely for the applications +// to not have their window content image ready in time. When this happens +// the latency will end up being an additional vsync period, and animations +// will hiccup. Therefore, this latency should be tuned somewhat +// conservatively (or at least with awareness of the trade-off being made). +static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS; + +// This is the phase offset at which SurfaceFlinger's composition runs. +static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS; + +// --------------------------------------------------------------------------- + +const String16 sHardwareTest("android.permission.HARDWARE_TEST"); +const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"); +const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER"); +const String16 sDump("android.permission.DUMP"); + +// --------------------------------------------------------------------------- + +SurfaceFlinger::SurfaceFlinger() + : BnSurfaceComposer(), + mTransactionFlags(0), + mTransactionPending(false), + mAnimTransactionPending(false), + mLayersRemoved(false), + mRepaintEverything(0), + mRenderEngine(NULL), + mBootTime(systemTime()), + mVisibleRegionsDirty(false), + mHwWorkListDirty(false), + mAnimCompositionPending(false), + mDebugRegion(0), + mDebugDDMS(0), + mDebugDisableHWC(0), + mDebugDisableTransformHint(0), + mDebugInSwapBuffers(0), + mLastSwapBufferTime(0), + mDebugInTransaction(0), + mLastTransactionTime(0), + mBootFinished(false), + mForceFullDamage(false), + mPrimaryHWVsyncEnabled(false), + mHWVsyncAvailable(false), + mDaltonize(false), + mHasColorMatrix(false), + mHasPoweredOff(false), + mFrameBuckets(), + mTotalTime(0), + mLastSwapTime(0) +{ + ALOGI("SurfaceFlinger is starting"); + + // debugging stuff... + char value[PROPERTY_VALUE_MAX]; + + property_get("ro.bq.gpu_to_cpu_unsupported", value, "0"); + mGpuToCpuSupported = !atoi(value); + + property_get("debug.sf.drop_missed_frames", value, "0"); + mDropMissedFrames = atoi(value); + + property_get("debug.sf.showupdates", value, "0"); + mDebugRegion = atoi(value); + + property_get("debug.sf.ddms", value, "0"); + mDebugDDMS = atoi(value); + if (mDebugDDMS) { + if (!startDdmConnection()) { + // start failed, and DDMS debugging not enabled + mDebugDDMS = 0; + } + } + ALOGI_IF(mDebugRegion, "showupdates enabled"); + ALOGI_IF(mDebugDDMS, "DDMS debugging enabled"); +} + +void SurfaceFlinger::onFirstRef() +{ + mEventQueue.init(this); +} + +SurfaceFlinger::~SurfaceFlinger() +{ + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(display); +} + +void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */) +{ + // the window manager died on us. prepare its eulogy. + + // restore initial conditions (default device unblank, etc) + initializeDisplays(); + + // restart the boot-animation + startBootAnim(); +} + +sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() +{ + sp<ISurfaceComposerClient> bclient; + sp<Client> client(new Client(this)); + status_t err = client->initCheck(); + if (err == NO_ERROR) { + bclient = client; + } + return bclient; +} + +sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, + bool secure) +{ + class DisplayToken : public BBinder { + sp<SurfaceFlinger> flinger; + virtual ~DisplayToken() { + // no more references, this display must be terminated + Mutex::Autolock _l(flinger->mStateLock); + flinger->mCurrentState.displays.removeItem(this); + flinger->setTransactionFlags(eDisplayTransactionNeeded); + } + public: + DisplayToken(const sp<SurfaceFlinger>& flinger) + : flinger(flinger) { + } + }; + + sp<BBinder> token = new DisplayToken(this); + + Mutex::Autolock _l(mStateLock); + DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure); + info.displayName = displayName; + mCurrentState.displays.add(token, info); + + return token; +} + +void SurfaceFlinger::destroyDisplay(const sp<IBinder>& display) { + Mutex::Autolock _l(mStateLock); + + ssize_t idx = mCurrentState.displays.indexOfKey(display); + if (idx < 0) { + ALOGW("destroyDisplay: invalid display token"); + return; + } + + const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx)); + if (!info.isVirtualDisplay()) { + ALOGE("destroyDisplay called for non-virtual display"); + return; + } + + mCurrentState.displays.removeItemsAt(idx); + setTransactionFlags(eDisplayTransactionNeeded); +} + +void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) { + ALOGW_IF(mBuiltinDisplays[type], + "Overwriting display token for display type %d", type); + mBuiltinDisplays[type] = new BBinder(); + // All non-virtual displays are currently considered secure. + DisplayDeviceState info(type, true); + mCurrentState.displays.add(mBuiltinDisplays[type], info); +} + +sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) { + if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id); + return NULL; + } + return mBuiltinDisplays[id]; +} + +sp<IGraphicBufferAlloc> SurfaceFlinger::createGraphicBufferAlloc() +{ + sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc()); + return gba; +} + +void SurfaceFlinger::bootFinished() +{ + const nsecs_t now = systemTime(); + const nsecs_t duration = now - mBootTime; + ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + mBootFinished = true; + + // wait patiently for the window manager death + const String16 name("window"); + sp<IBinder> window(defaultServiceManager()->getService(name)); + if (window != 0) { + window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); + } + + // stop boot animation + // formerly we would just kill the process, but we now ask it to exit so it + // can choose where to stop the animation. + property_set("service.bootanim.exit", "1"); + + const int LOGTAG_SF_STOP_BOOTANIM = 60110; + LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, + ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); +} + +void SurfaceFlinger::deleteTextureAsync(uint32_t texture) { + class MessageDestroyGLTexture : public MessageBase { + RenderEngine& engine; + uint32_t texture; + public: + MessageDestroyGLTexture(RenderEngine& engine, uint32_t texture) + : engine(engine), texture(texture) { + } + virtual bool handler() { + engine.deleteTextures(1, &texture); + return true; + } + }; + postMessageAsync(new MessageDestroyGLTexture(getRenderEngine(), texture)); +} + +class DispSyncSource : public VSyncSource, private DispSync::Callback { +public: + DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, + const char* label) : + mValue(0), + mTraceVsync(traceVsync), + mVsyncOnLabel(String8::format("VsyncOn-%s", label)), + mVsyncEventLabel(String8::format("VSYNC-%s", label)), + mDispSync(dispSync), + mCallbackMutex(), + mCallback(), + mVsyncMutex(), + mPhaseOffset(phaseOffset), + mEnabled(false) {} + + virtual ~DispSyncSource() {} + + virtual void setVSyncEnabled(bool enable) { + Mutex::Autolock lock(mVsyncMutex); + if (enable) { + status_t err = mDispSync->addEventListener(mPhaseOffset, + static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error registering vsync callback: %s (%d)", + strerror(-err), err); + } + //ATRACE_INT(mVsyncOnLabel.string(), 1); + } else { + status_t err = mDispSync->removeEventListener( + static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error unregistering vsync callback: %s (%d)", + strerror(-err), err); + } + //ATRACE_INT(mVsyncOnLabel.string(), 0); + } + mEnabled = enable; + } + + virtual void setCallback(const sp<VSyncSource::Callback>& callback) { + Mutex::Autolock lock(mCallbackMutex); + mCallback = callback; + } + + virtual void setPhaseOffset(nsecs_t phaseOffset) { + Mutex::Autolock lock(mVsyncMutex); + + // Normalize phaseOffset to [0, period) + auto period = mDispSync->getPeriod(); + phaseOffset %= period; + if (phaseOffset < 0) { + // If we're here, then phaseOffset is in (-period, 0). After this + // operation, it will be in (0, period) + phaseOffset += period; + } + mPhaseOffset = phaseOffset; + + // If we're not enabled, we don't need to mess with the listeners + if (!mEnabled) { + return; + } + + // Remove the listener with the old offset + status_t err = mDispSync->removeEventListener( + static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error unregistering vsync callback: %s (%d)", + strerror(-err), err); + } + + // Add a listener with the new offset + err = mDispSync->addEventListener(mPhaseOffset, + static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error registering vsync callback: %s (%d)", + strerror(-err), err); + } + } + +private: + virtual void onDispSyncEvent(nsecs_t when) { + sp<VSyncSource::Callback> callback; + { + Mutex::Autolock lock(mCallbackMutex); + callback = mCallback; + + if (mTraceVsync) { + mValue = (mValue + 1) % 2; + ATRACE_INT(mVsyncEventLabel.string(), mValue); + } + } + + if (callback != NULL) { + callback->onVSyncEvent(when); + } + } + + int mValue; + + const bool mTraceVsync; + const String8 mVsyncOnLabel; + const String8 mVsyncEventLabel; + + DispSync* mDispSync; + + Mutex mCallbackMutex; // Protects the following + sp<VSyncSource::Callback> mCallback; + + Mutex mVsyncMutex; // Protects the following + nsecs_t mPhaseOffset; + bool mEnabled; +}; + +void SurfaceFlinger::init() { + ALOGI( "SurfaceFlinger's main thread ready to run. " + "Initializing graphics H/W..."); + + Mutex::Autolock _l(mStateLock); + + // initialize EGL for the default display + mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(mEGLDisplay, NULL, NULL); + + // start the EventThread + sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, + vsyncPhaseOffsetNs, true, "app"); + mEventThread = new EventThread(vsyncSrc); + sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, + sfVsyncPhaseOffsetNs, true, "sf"); + mSFEventThread = new EventThread(sfVsyncSrc); + mEventQueue.setEventThread(mSFEventThread); + + // Initialize the H/W composer object. There may or may not be an + // actual hardware composer underneath. + mHwc = new HWComposer(this, + *static_cast<HWComposer::EventHandler *>(this)); + + // get a RenderEngine for the given display / config (can't fail) + mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc->getVisualID()); + + // retrieve the EGL context that was selected/created + mEGLContext = mRenderEngine->getEGLContext(); + + LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT, + "couldn't create EGLContext"); + + // initialize our non-virtual displays + for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { + DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i); + // set-up the displays that are already connected + if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) { + // All non-virtual displays are currently considered secure. + bool isSecure = true; + createBuiltinDisplayLocked(type); + wp<IBinder> token = mBuiltinDisplays[i]; + + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer, + new GraphicBufferAlloc()); + + sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i, + consumer); + int32_t hwcId = allocateHwcDisplayId(type); + sp<DisplayDevice> hw = new DisplayDevice(this, + type, hwcId, mHwc->getFormat(hwcId), isSecure, token, + fbs, producer, + mRenderEngine->getEGLConfig()); + if (i > DisplayDevice::DISPLAY_PRIMARY) { + // FIXME: currently we don't get blank/unblank requests + // for displays other than the main display, so we always + // assume a connected display is unblanked. + ALOGD("marking display %zu as acquired/unblanked", i); + hw->setPowerMode(HWC_POWER_MODE_NORMAL); + } + mDisplays.add(token, hw); + } + } + + // make the GLContext current so that we can create textures when creating Layers + // (which may happens before we render something) + getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); + + mEventControlThread = new EventControlThread(this); + mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY); + + // set a fake vsync period if there is no HWComposer + if (mHwc->initCheck() != NO_ERROR) { + mPrimaryDispSync.setPeriod(16666667); + } + + // initialize our drawing state + mDrawingState = mCurrentState; + + // set initial conditions (e.g. unblank default device) + initializeDisplays(); + + // start boot animation + startBootAnim(); +} + +int32_t SurfaceFlinger::allocateHwcDisplayId(DisplayDevice::DisplayType type) { + return (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) ? + type : mHwc->allocateDisplayId(); +} + +void SurfaceFlinger::startBootAnim() { + // start boot animation + property_set("service.bootanim.exit", "0"); + property_set("ctl.start", "bootanim"); +} + +size_t SurfaceFlinger::getMaxTextureSize() const { + return mRenderEngine->getMaxTextureSize(); +} + +size_t SurfaceFlinger::getMaxViewportDims() const { + return mRenderEngine->getMaxViewportDims(); +} + +// ---------------------------------------------------------------------------- + +bool SurfaceFlinger::authenticateSurfaceTexture( + const sp<IGraphicBufferProducer>& bufferProducer) const { + Mutex::Autolock _l(mStateLock); + sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer)); + return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0; +} + +status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, + Vector<DisplayInfo>* configs) { + if ((configs == NULL) || (display.get() == NULL)) { + return BAD_VALUE; + } + + if (!display.get()) + return NAME_NOT_FOUND; + + int32_t type = NAME_NOT_FOUND; + for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { + if (display == mBuiltinDisplays[i]) { + type = i; + break; + } + } + + if (type < 0) { + return type; + } + + // TODO: Not sure if display density should handled by SF any longer + class Density { + static int getDensityFromProperty(char const* propName) { + char property[PROPERTY_VALUE_MAX]; + int density = 0; + if (property_get(propName, property, NULL) > 0) { + density = atoi(property); + } + return density; + } + public: + static int getEmuDensity() { + return getDensityFromProperty("qemu.sf.lcd_density"); } + static int getBuildDensity() { + return getDensityFromProperty("ro.sf.lcd_density"); } + }; + + configs->clear(); + + const Vector<HWComposer::DisplayConfig>& hwConfigs = + getHwComposer().getConfigs(type); + for (size_t c = 0; c < hwConfigs.size(); ++c) { + const HWComposer::DisplayConfig& hwConfig = hwConfigs[c]; + DisplayInfo info = DisplayInfo(); + + float xdpi = hwConfig.xdpi; + float ydpi = hwConfig.ydpi; + + if (type == DisplayDevice::DISPLAY_PRIMARY) { + // The density of the device is provided by a build property + float density = Density::getBuildDensity() / 160.0f; + if (density == 0) { + // the build doesn't provide a density -- this is wrong! + // use xdpi instead + ALOGE("ro.sf.lcd_density must be defined as a build property"); + density = xdpi / 160.0f; + } + if (Density::getEmuDensity()) { + // if "qemu.sf.lcd_density" is specified, it overrides everything + xdpi = ydpi = density = Density::getEmuDensity(); + density /= 160.0f; + } + info.density = density; + + // TODO: this needs to go away (currently needed only by webkit) + sp<const DisplayDevice> hw(getDefaultDisplayDevice()); + info.orientation = hw->getOrientation(); + } else { + // TODO: where should this value come from? + static const int TV_DENSITY = 213; + info.density = TV_DENSITY / 160.0f; + info.orientation = 0; + } + + info.w = hwConfig.width; + info.h = hwConfig.height; + info.xdpi = xdpi; + info.ydpi = ydpi; + info.fps = float(1e9 / hwConfig.refresh); + info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS; + info.colorTransform = hwConfig.colorTransform; + + // This is how far in advance a buffer must be queued for + // presentation at a given time. If you want a buffer to appear + // on the screen at time N, you must submit the buffer before + // (N - presentationDeadline). + // + // Normally it's one full refresh period (to give SF a chance to + // latch the buffer), but this can be reduced by configuring a + // DispSync offset. Any additional delays introduced by the hardware + // composer or panel must be accounted for here. + // + // We add an additional 1ms to allow for processing time and + // differences between the ideal and actual refresh rate. + info.presentationDeadline = + hwConfig.refresh - SF_VSYNC_EVENT_PHASE_OFFSET_NS + 1000000; + + // All non-virtual displays are currently considered secure. + info.secure = true; + + configs->push_back(info); + } + + return NO_ERROR; +} + +status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */, + DisplayStatInfo* stats) { + if (stats == NULL) { + return BAD_VALUE; + } + + // FIXME for now we always return stats for the primary display + memset(stats, 0, sizeof(*stats)); + stats->vsyncTime = mPrimaryDispSync.computeNextRefresh(0); + stats->vsyncPeriod = mPrimaryDispSync.getPeriod(); + return NO_ERROR; +} + +int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) { + sp<DisplayDevice> device(getDisplayDevice(display)); + if (device != NULL) { + return device->getActiveConfig(); + } + return BAD_VALUE; +} + +void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode) { + ALOGD("Set active config mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(), + this); + int32_t type = hw->getDisplayType(); + int currentMode = hw->getActiveConfig(); + + if (mode == currentMode) { + ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode); + return; + } + + if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + ALOGW("Trying to set config for virtual display"); + return; + } + + hw->setActiveConfig(mode); + getHwComposer().setActiveConfig(type, mode); +} + +status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) { + class MessageSetActiveConfig: public MessageBase { + SurfaceFlinger& mFlinger; + sp<IBinder> mDisplay; + int mMode; + public: + MessageSetActiveConfig(SurfaceFlinger& flinger, const sp<IBinder>& disp, + int mode) : + mFlinger(flinger), mDisplay(disp) { mMode = mode; } + virtual bool handler() { + Vector<DisplayInfo> configs; + mFlinger.getDisplayConfigs(mDisplay, &configs); + if (mMode < 0 || mMode >= static_cast<int>(configs.size())) { + ALOGE("Attempt to set active config = %d for display with %zu configs", + mMode, configs.size()); + } + sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); + if (hw == NULL) { + ALOGE("Attempt to set active config = %d for null display %p", + mMode, mDisplay.get()); + } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { + ALOGW("Attempt to set active config = %d for virtual display", + mMode); + } else { + mFlinger.setActiveConfigInternal(hw, mMode); + } + return true; + } + }; + sp<MessageBase> msg = new MessageSetActiveConfig(*this, display, mode); + postMessageSync(msg); + return NO_ERROR; +} + +status_t SurfaceFlinger::clearAnimationFrameStats() { + Mutex::Autolock _l(mStateLock); + mAnimFrameTracker.clearStats(); + return NO_ERROR; +} + +status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const { + Mutex::Autolock _l(mStateLock); + mAnimFrameTracker.getStats(outStats); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() { + return mEventThread->createEventConnection(); +} + +// ---------------------------------------------------------------------------- + +void SurfaceFlinger::waitForEvent() { + mEventQueue.waitMessage(); +} + +void SurfaceFlinger::signalTransaction() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signalLayerUpdate() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signalRefresh() { + mEventQueue.refresh(); +} + +status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t /* flags */) { + return mEventQueue.postMessage(msg, reltime); +} + +status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t /* flags */) { + status_t res = mEventQueue.postMessage(msg, reltime); + if (res == NO_ERROR) { + msg->wait(); + } + return res; +} + +void SurfaceFlinger::run() { + do { + waitForEvent(); + } while (true); +} + +void SurfaceFlinger::enableHardwareVsync() { + Mutex::Autolock _l(mHWVsyncLock); + if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { + mPrimaryDispSync.beginResync(); + //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); + mEventControlThread->setVsyncEnabled(true); + mPrimaryHWVsyncEnabled = true; + } +} + +void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) { + Mutex::Autolock _l(mHWVsyncLock); + + if (makeAvailable) { + mHWVsyncAvailable = true; + } else if (!mHWVsyncAvailable) { + ALOGE("resyncToHardwareVsync called when HW vsync unavailable"); + return; + } + + const nsecs_t period = + getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + + mPrimaryDispSync.reset(); + mPrimaryDispSync.setPeriod(period); + + if (!mPrimaryHWVsyncEnabled) { + mPrimaryDispSync.beginResync(); + //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); + mEventControlThread->setVsyncEnabled(true); + mPrimaryHWVsyncEnabled = true; + } +} + +void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) { + Mutex::Autolock _l(mHWVsyncLock); + if (mPrimaryHWVsyncEnabled) { + //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false); + mEventControlThread->setVsyncEnabled(false); + mPrimaryDispSync.endResync(); + mPrimaryHWVsyncEnabled = false; + } + if (makeUnavailable) { + mHWVsyncAvailable = false; + } +} + +void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) { + bool needsHwVsync = false; + + { // Scope for the lock + Mutex::Autolock _l(mHWVsyncLock); + if (type == 0 && mPrimaryHWVsyncEnabled) { + needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp); + } + } + + if (needsHwVsync) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } +} + +void SurfaceFlinger::onHotplugReceived(int type, bool connected) { + if (mEventThread == NULL) { + // This is a temporary workaround for b/7145521. A non-null pointer + // does not mean EventThread has finished initializing, so this + // is not a correct fix. + ALOGW("WARNING: EventThread not started, ignoring hotplug"); + return; + } + + if (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + Mutex::Autolock _l(mStateLock); + if (connected) { + createBuiltinDisplayLocked((DisplayDevice::DisplayType)type); + } else { + mCurrentState.displays.removeItem(mBuiltinDisplays[type]); + mBuiltinDisplays[type].clear(); + } + setTransactionFlags(eDisplayTransactionNeeded); + + // Defer EventThread notification until SF has updated mDisplays. + } +} + +void SurfaceFlinger::eventControl(int disp, int event, int enabled) { + ATRACE_CALL(); + getHwComposer().eventControl(disp, event, enabled); +} + +void SurfaceFlinger::onMessageReceived(int32_t what) { + ATRACE_CALL(); + switch (what) { + case MessageQueue::TRANSACTION: { + handleMessageTransaction(); + break; + } + case MessageQueue::INVALIDATE: { + bool refreshNeeded = handleMessageTransaction(); + refreshNeeded |= handleMessageInvalidate(); + refreshNeeded |= mRepaintEverything; + if (refreshNeeded) { + // Signal a refresh if a transaction modified the window state, + // a new buffer was latched, or if HWC has requested a full + // repaint + signalRefresh(); + } + break; + } + case MessageQueue::REFRESH: { + handleMessageRefresh(); + break; + } + } +} + +bool SurfaceFlinger::handleMessageTransaction() { + uint32_t transactionFlags = peekTransactionFlags(eTransactionMask); + if (transactionFlags) { + handleTransaction(transactionFlags); + return true; + } + return false; +} + +bool SurfaceFlinger::handleMessageInvalidate() { + ATRACE_CALL(); + return handlePageFlip(); +} + +void SurfaceFlinger::handleMessageRefresh() { + ATRACE_CALL(); + + static nsecs_t previousExpectedPresent = 0; + nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0); + static bool previousFrameMissed = false; + bool frameMissed = (expectedPresent == previousExpectedPresent); + if (frameMissed != previousFrameMissed) { + ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); + } + previousFrameMissed = frameMissed; + + if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) { + // Latch buffers, but don't send anything to HWC, then signal another + // wakeup for the next vsync + preComposition(); + repaintEverything(); + } else { + preComposition(); + rebuildLayerStacks(); + setUpHWComposer(); + doDebugFlashRegions(); + doComposition(); + postComposition(); + } + + previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0); +} + +void SurfaceFlinger::doDebugFlashRegions() +{ + // is debugging enabled + if (CC_LIKELY(!mDebugRegion)) + return; + + const bool repaintEverything = mRepaintEverything; + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + const sp<DisplayDevice>& hw(mDisplays[dpy]); + if (hw->isDisplayOn()) { + // transform the dirty region into this screen's coordinate space + const Region dirtyRegion(hw->getDirtyRegion(repaintEverything)); + if (!dirtyRegion.isEmpty()) { + // redraw the whole screen + doComposeSurfaces(hw, Region(hw->bounds())); + + // and draw the dirty region + const int32_t height = hw->getHeight(); + RenderEngine& engine(getRenderEngine()); + engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1); + + hw->compositionComplete(); + hw->swapBuffers(getHwComposer()); + } + } + } + + postFramebuffer(); + + if (mDebugRegion > 1) { + usleep(mDebugRegion * 1000); + } + + HWComposer& hwc(getHwComposer()); + if (hwc.initCheck() == NO_ERROR) { + status_t err = hwc.prepare(); + ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err)); + } +} + +void SurfaceFlinger::preComposition() +{ + bool needExtraInvalidate = false; + const LayerVector& layers(mDrawingState.layersSortedByZ); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; i++) { + if (layers[i]->onPreComposition()) { + needExtraInvalidate = true; + } + } + if (needExtraInvalidate) { + signalLayerUpdate(); + } +} + +void SurfaceFlinger::postComposition() +{ + const LayerVector& layers(mDrawingState.layersSortedByZ); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; i++) { + layers[i]->onPostComposition(); + } + + const HWComposer& hwc = getHwComposer(); + sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY); + + if (presentFence->isValid()) { + if (mPrimaryDispSync.addPresentFence(presentFence)) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } + } + + const sp<const DisplayDevice> hw(getDefaultDisplayDevice()); + if (kIgnorePresentFences) { + if (hw->isDisplayOn()) { + enableHardwareVsync(); + } + } + + if (mAnimCompositionPending) { + mAnimCompositionPending = false; + + if (presentFence->isValid()) { + mAnimFrameTracker.setActualPresentFence(presentFence); + } else { + // The HWC doesn't support present fences, so use the refresh + // timestamp instead. + nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY); + mAnimFrameTracker.setActualPresentTime(presentTime); + } + mAnimFrameTracker.advanceFrame(); + } + + if (hw->getPowerMode() == HWC_POWER_MODE_OFF) { + return; + } + + nsecs_t currentTime = systemTime(); + if (mHasPoweredOff) { + mHasPoweredOff = false; + } else { + nsecs_t period = mPrimaryDispSync.getPeriod(); + nsecs_t elapsedTime = currentTime - mLastSwapTime; + size_t numPeriods = static_cast<size_t>(elapsedTime / period); + if (numPeriods < NUM_BUCKETS - 1) { + mFrameBuckets[numPeriods] += elapsedTime; + } else { + mFrameBuckets[NUM_BUCKETS - 1] += elapsedTime; + } + mTotalTime += elapsedTime; + } + mLastSwapTime = currentTime; +} + +void SurfaceFlinger::rebuildLayerStacks() { + // rebuild the visible layer list per screen + if (CC_UNLIKELY(mVisibleRegionsDirty)) { + ATRACE_CALL(); + mVisibleRegionsDirty = false; + invalidateHwcGeometry(); + + const LayerVector& layers(mDrawingState.layersSortedByZ); + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + Region opaqueRegion; + Region dirtyRegion; + Vector< sp<Layer> > layersSortedByZ; + const sp<DisplayDevice>& hw(mDisplays[dpy]); + const Transform& tr(hw->getTransform()); + const Rect bounds(hw->getBounds()); + if (hw->isDisplayOn()) { + SurfaceFlinger::computeVisibleRegions(layers, + hw->getLayerStack(), dirtyRegion, opaqueRegion); + + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(layers[i]); + const Layer::State& s(layer->getDrawingState()); + if (s.layerStack == hw->getLayerStack()) { + Region drawRegion(tr.transform( + layer->visibleNonTransparentRegion)); + drawRegion.andSelf(bounds); + if (!drawRegion.isEmpty()) { + layersSortedByZ.add(layer); + } + } + } + } + hw->setVisibleLayersSortedByZ(layersSortedByZ); + hw->undefinedRegion.set(bounds); + hw->undefinedRegion.subtractSelf(tr.transform(opaqueRegion)); + hw->dirtyRegion.orSelf(dirtyRegion); + } + } +} + +void SurfaceFlinger::setUpHWComposer() { + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + bool dirty = !mDisplays[dpy]->getDirtyRegion(false).isEmpty(); + bool empty = mDisplays[dpy]->getVisibleLayersSortedByZ().size() == 0; + bool wasEmpty = !mDisplays[dpy]->lastCompositionHadVisibleLayers; + + // If nothing has changed (!dirty), don't recompose. + // If something changed, but we don't currently have any visible layers, + // and didn't when we last did a composition, then skip it this time. + // The second rule does two things: + // - When all layers are removed from a display, we'll emit one black + // frame, then nothing more until we get new layers. + // - When a display is created with a private layer stack, we won't + // emit any black frames until a layer is added to the layer stack. + bool mustRecompose = dirty && !(empty && wasEmpty); + + ALOGV_IF(mDisplays[dpy]->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL, + "dpy[%zu]: %s composition (%sdirty %sempty %swasEmpty)", dpy, + mustRecompose ? "doing" : "skipping", + dirty ? "+" : "-", + empty ? "+" : "-", + wasEmpty ? "+" : "-"); + + mDisplays[dpy]->beginFrame(mustRecompose); + + if (mustRecompose) { + mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty; + } + } + + HWComposer& hwc(getHwComposer()); + if (hwc.initCheck() == NO_ERROR) { + // build the h/w work list + if (CC_UNLIKELY(mHwWorkListDirty)) { + mHwWorkListDirty = false; + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + const int32_t id = hw->getHwcDisplayId(); + if (id >= 0) { + const Vector< sp<Layer> >& currentLayers( + hw->getVisibleLayersSortedByZ()); + const size_t count = currentLayers.size(); + if (hwc.createWorkList(id, count) == NO_ERROR) { + HWComposer::LayerListIterator cur = hwc.begin(id); + const HWComposer::LayerListIterator end = hwc.end(id); + for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { + const sp<Layer>& layer(currentLayers[i]); + layer->setGeometry(hw, *cur); + if (mDebugDisableHWC || mDebugRegion || mDaltonize || mHasColorMatrix) { + cur->setSkip(true); + } + } + } + } + } + } + + // set the per-frame data + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + const int32_t id = hw->getHwcDisplayId(); + if (id >= 0) { + const Vector< sp<Layer> >& currentLayers( + hw->getVisibleLayersSortedByZ()); + const size_t count = currentLayers.size(); + HWComposer::LayerListIterator cur = hwc.begin(id); + const HWComposer::LayerListIterator end = hwc.end(id); + for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { + /* + * update the per-frame h/w composer data for each layer + * and build the transparent region of the FB + */ + const sp<Layer>& layer(currentLayers[i]); + layer->setPerFrameData(hw, *cur); + } + } + } + + // If possible, attempt to use the cursor overlay on each display. + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + const int32_t id = hw->getHwcDisplayId(); + if (id >= 0) { + const Vector< sp<Layer> >& currentLayers( + hw->getVisibleLayersSortedByZ()); + const size_t count = currentLayers.size(); + HWComposer::LayerListIterator cur = hwc.begin(id); + const HWComposer::LayerListIterator end = hwc.end(id); + for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { + const sp<Layer>& layer(currentLayers[i]); + if (layer->isPotentialCursor()) { + cur->setIsCursorLayerHint(); + break; + } + } + } + } + + status_t err = hwc.prepare(); + ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err)); + + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + hw->prepareFrame(hwc); + } + } +} + +void SurfaceFlinger::doComposition() { + ATRACE_CALL(); + const bool repaintEverything = android_atomic_and(0, &mRepaintEverything); + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + const sp<DisplayDevice>& hw(mDisplays[dpy]); + if (hw->isDisplayOn()) { + // transform the dirty region into this screen's coordinate space + const Region dirtyRegion(hw->getDirtyRegion(repaintEverything)); + + // repaint the framebuffer (if needed) + doDisplayComposition(hw, dirtyRegion); + + hw->dirtyRegion.clear(); + hw->flip(hw->swapRegion); + hw->swapRegion.clear(); + } + // inform the h/w that we're done compositing + hw->compositionComplete(); + } + postFramebuffer(); +} + +void SurfaceFlinger::postFramebuffer() +{ + ATRACE_CALL(); + + const nsecs_t now = systemTime(); + mDebugInSwapBuffers = now; + + HWComposer& hwc(getHwComposer()); + if (hwc.initCheck() == NO_ERROR) { + if (!hwc.supportsFramebufferTarget()) { + // EGL spec says: + // "surface must be bound to the calling thread's current context, + // for the current rendering API." + getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); + } + hwc.commit(); + } + + // make the default display current because the VirtualDisplayDevice code cannot + // deal with dequeueBuffer() being called outside of the composition loop; however + // the code below can call glFlush() which is allowed (and does in some case) call + // dequeueBuffer(). + getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext); + + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + const Vector< sp<Layer> >& currentLayers(hw->getVisibleLayersSortedByZ()); + hw->onSwapBuffersCompleted(hwc); + const size_t count = currentLayers.size(); + int32_t id = hw->getHwcDisplayId(); + if (id >=0 && hwc.initCheck() == NO_ERROR) { + HWComposer::LayerListIterator cur = hwc.begin(id); + const HWComposer::LayerListIterator end = hwc.end(id); + for (size_t i = 0; cur != end && i < count; ++i, ++cur) { + currentLayers[i]->onLayerDisplayed(hw, &*cur); + } + } else { + for (size_t i = 0; i < count; i++) { + currentLayers[i]->onLayerDisplayed(hw, NULL); + } + } + } + + mLastSwapBufferTime = systemTime() - now; + mDebugInSwapBuffers = 0; + + uint32_t flipCount = getDefaultDisplayDevice()->getPageFlipCount(); + if (flipCount % LOG_FRAME_STATS_PERIOD == 0) { + logFrameStats(); + } +} + +void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) +{ + ATRACE_CALL(); + + // here we keep a copy of the drawing state (that is the state that's + // going to be overwritten by handleTransactionLocked()) outside of + // mStateLock so that the side-effects of the State assignment + // don't happen with mStateLock held (which can cause deadlocks). + State drawingState(mDrawingState); + + Mutex::Autolock _l(mStateLock); + const nsecs_t now = systemTime(); + mDebugInTransaction = now; + + // Here we're guaranteed that some transaction flags are set + // so we can call handleTransactionLocked() unconditionally. + // We call getTransactionFlags(), which will also clear the flags, + // with mStateLock held to guarantee that mCurrentState won't change + // until the transaction is committed. + + transactionFlags = getTransactionFlags(eTransactionMask); + handleTransactionLocked(transactionFlags); + + mLastTransactionTime = systemTime() - now; + mDebugInTransaction = 0; + invalidateHwcGeometry(); + // here the transaction has been committed +} + +void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) +{ + const LayerVector& currentLayers(mCurrentState.layersSortedByZ); + const size_t count = currentLayers.size(); + + // Notify all layers of available frames + for (size_t i = 0; i < count; ++i) { + currentLayers[i]->notifyAvailableFrames(); + } + + /* + * Traversal of the children + * (perform the transaction for each of them if needed) + */ + + if (transactionFlags & eTraversalNeeded) { + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(currentLayers[i]); + uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); + if (!trFlags) continue; + + const uint32_t flags = layer->doTransaction(0); + if (flags & Layer::eVisibleRegion) + mVisibleRegionsDirty = true; + } + } + + /* + * Perform display own transactions if needed + */ + + if (transactionFlags & eDisplayTransactionNeeded) { + // here we take advantage of Vector's copy-on-write semantics to + // improve performance by skipping the transaction entirely when + // know that the lists are identical + const KeyedVector< wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays); + const KeyedVector< wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays); + if (!curr.isIdenticalTo(draw)) { + mVisibleRegionsDirty = true; + const size_t cc = curr.size(); + size_t dc = draw.size(); + + // find the displays that were removed + // (ie: in drawing state but not in current state) + // also handle displays that changed + // (ie: displays that are in both lists) + for (size_t i=0 ; i<dc ; i++) { + const ssize_t j = curr.indexOfKey(draw.keyAt(i)); + if (j < 0) { + // in drawing state but not in current state + if (!draw[i].isMainDisplay()) { + // Call makeCurrent() on the primary display so we can + // be sure that nothing associated with this display + // is current. + const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDevice()); + defaultDisplay->makeCurrent(mEGLDisplay, mEGLContext); + sp<DisplayDevice> hw(getDisplayDevice(draw.keyAt(i))); + if (hw != NULL) + hw->disconnect(getHwComposer()); + if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) + mEventThread->onHotplugReceived(draw[i].type, false); + mDisplays.removeItem(draw.keyAt(i)); + } else { + ALOGW("trying to remove the main display"); + } + } else { + // this display is in both lists. see if something changed. + const DisplayDeviceState& state(curr[j]); + const wp<IBinder>& display(curr.keyAt(j)); + const sp<IBinder> state_binder = IInterface::asBinder(state.surface); + const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface); + if (state_binder != draw_binder) { + // changing the surface is like destroying and + // recreating the DisplayDevice, so we just remove it + // from the drawing state, so that it get re-added + // below. + sp<DisplayDevice> hw(getDisplayDevice(display)); + if (hw != NULL) + hw->disconnect(getHwComposer()); + mDisplays.removeItem(display); + mDrawingState.displays.removeItemsAt(i); + dc--; i--; + // at this point we must loop to the next item + continue; + } + + const sp<DisplayDevice> disp(getDisplayDevice(display)); + if (disp != NULL) { + if (state.layerStack != draw[i].layerStack) { + disp->setLayerStack(state.layerStack); + } + if ((state.orientation != draw[i].orientation) + || (state.viewport != draw[i].viewport) + || (state.frame != draw[i].frame)) + { + disp->setProjection(state.orientation, + state.viewport, state.frame); + } + if (state.width != draw[i].width || state.height != draw[i].height) { + disp->setDisplaySize(state.width, state.height); + } + } + } + } + + // find displays that were added + // (ie: in current state but not in drawing state) + for (size_t i=0 ; i<cc ; i++) { + if (draw.indexOfKey(curr.keyAt(i)) < 0) { + const DisplayDeviceState& state(curr[i]); + + sp<DisplaySurface> dispSurface; + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferProducer> bqProducer; + sp<IGraphicBufferConsumer> bqConsumer; + BufferQueue::createBufferQueue(&bqProducer, &bqConsumer, + new GraphicBufferAlloc()); + + int32_t hwcDisplayId = -1; + if (state.isVirtualDisplay()) { + // Virtual displays without a surface are dormant: + // they have external state (layer stack, projection, + // etc.) but no internal state (i.e. a DisplayDevice). + if (state.surface != NULL) { + + int width = 0; + int status = state.surface->query( + NATIVE_WINDOW_WIDTH, &width); + ALOGE_IF(status != NO_ERROR, + "Unable to query width (%d)", status); + int height = 0; + status = state.surface->query( + NATIVE_WINDOW_HEIGHT, &height); + ALOGE_IF(status != NO_ERROR, + "Unable to query height (%d)", status); + if (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 || + (width <= MAX_VIRTUAL_DISPLAY_DIMENSION && + height <= MAX_VIRTUAL_DISPLAY_DIMENSION)) { + hwcDisplayId = allocateHwcDisplayId(state.type); + } + + sp<VirtualDisplaySurface> vds = new VirtualDisplaySurface( + *mHwc, hwcDisplayId, state.surface, + bqProducer, bqConsumer, state.displayName); + + dispSurface = vds; + producer = vds; + } + } else { + ALOGE_IF(state.surface!=NULL, + "adding a supported display, but rendering " + "surface is provided (%p), ignoring it", + state.surface.get()); + hwcDisplayId = allocateHwcDisplayId(state.type); + // for supported (by hwc) displays we provide our + // own rendering surface + dispSurface = new FramebufferSurface(*mHwc, state.type, + bqConsumer); + producer = bqProducer; + } + + const wp<IBinder>& display(curr.keyAt(i)); + if (dispSurface != NULL) { + sp<DisplayDevice> hw = new DisplayDevice(this, + state.type, hwcDisplayId, + mHwc->getFormat(hwcDisplayId), state.isSecure, + display, dispSurface, producer, + mRenderEngine->getEGLConfig()); + hw->setLayerStack(state.layerStack); + hw->setProjection(state.orientation, + state.viewport, state.frame); + hw->setDisplayName(state.displayName); + mDisplays.add(display, hw); + if (state.isVirtualDisplay()) { + if (hwcDisplayId >= 0) { + mHwc->setVirtualDisplayProperties(hwcDisplayId, + hw->getWidth(), hw->getHeight(), + hw->getFormat()); + } + } else { + mEventThread->onHotplugReceived(state.type, true); + } + } + } + } + } + } + + if (transactionFlags & (eTraversalNeeded|eDisplayTransactionNeeded)) { + // The transform hint might have changed for some layers + // (either because a display has changed, or because a layer + // as changed). + // + // Walk through all the layers in currentLayers, + // and update their transform hint. + // + // If a layer is visible only on a single display, then that + // display is used to calculate the hint, otherwise we use the + // default display. + // + // NOTE: we do this here, rather than in rebuildLayerStacks() so that + // the hint is set before we acquire a buffer from the surface texture. + // + // NOTE: layer transactions have taken place already, so we use their + // drawing state. However, SurfaceFlinger's own transaction has not + // happened yet, so we must use the current state layer list + // (soon to become the drawing state list). + // + sp<const DisplayDevice> disp; + uint32_t currentlayerStack = 0; + for (size_t i=0; i<count; i++) { + // NOTE: we rely on the fact that layers are sorted by + // layerStack first (so we don't have to traverse the list + // of displays for every layer). + const sp<Layer>& layer(currentLayers[i]); + uint32_t layerStack = layer->getDrawingState().layerStack; + if (i==0 || currentlayerStack != layerStack) { + currentlayerStack = layerStack; + // figure out if this layerstack is mirrored + // (more than one display) if so, pick the default display, + // if not, pick the only display it's on. + disp.clear(); + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + if (hw->getLayerStack() == currentlayerStack) { + if (disp == NULL) { + disp = hw; + } else { + disp = NULL; + break; + } + } + } + } + if (disp == NULL) { + // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to + // redraw after transform hint changes. See bug 8508397. + + // could be null when this layer is using a layerStack + // that is not visible on any display. Also can occur at + // screen off/on times. + disp = getDefaultDisplayDevice(); + } + layer->updateTransformHint(disp); + } + } + + + /* + * Perform our own transaction if needed + */ + + const LayerVector& layers(mDrawingState.layersSortedByZ); + if (currentLayers.size() > layers.size()) { + // layers have been added + mVisibleRegionsDirty = true; + } + + // some layers might have been removed, so + // we need to update the regions they're exposing. + if (mLayersRemoved) { + mLayersRemoved = false; + mVisibleRegionsDirty = true; + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(layers[i]); + if (currentLayers.indexOf(layer) < 0) { + // this layer is not visible anymore + // TODO: we could traverse the tree from front to back and + // compute the actual visible region + // TODO: we could cache the transformed region + const Layer::State& s(layer->getDrawingState()); + Region visibleReg = s.active.transform.transform( + Region(Rect(s.active.w, s.active.h))); + invalidateLayerStack(s.layerStack, visibleReg); + } + } + } + + commitTransaction(); + + updateCursorAsync(); +} + +void SurfaceFlinger::updateCursorAsync() +{ + HWComposer& hwc(getHwComposer()); + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + sp<const DisplayDevice> hw(mDisplays[dpy]); + const int32_t id = hw->getHwcDisplayId(); + if (id < 0) { + continue; + } + const Vector< sp<Layer> >& currentLayers( + hw->getVisibleLayersSortedByZ()); + const size_t count = currentLayers.size(); + HWComposer::LayerListIterator cur = hwc.begin(id); + const HWComposer::LayerListIterator end = hwc.end(id); + for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { + if (cur->getCompositionType() != HWC_CURSOR_OVERLAY) { + continue; + } + const sp<Layer>& layer(currentLayers[i]); + Rect cursorPos = layer->getPosition(hw); + hwc.setCursorPositionAsync(id, cursorPos); + break; + } + } +} + +void SurfaceFlinger::commitTransaction() +{ + if (!mLayersPendingRemoval.isEmpty()) { + // Notify removed layers now that they can't be drawn from + for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) { + mLayersPendingRemoval[i]->onRemoved(); + } + mLayersPendingRemoval.clear(); + } + + // If this transaction is part of a window animation then the next frame + // we composite should be considered an animation as well. + mAnimCompositionPending = mAnimTransactionPending; + + mDrawingState = mCurrentState; + mTransactionPending = false; + mAnimTransactionPending = false; + mTransactionCV.broadcast(); +} + +void SurfaceFlinger::computeVisibleRegions( + const LayerVector& currentLayers, uint32_t layerStack, + Region& outDirtyRegion, Region& outOpaqueRegion) +{ + ATRACE_CALL(); + + Region aboveOpaqueLayers; + Region aboveCoveredLayers; + Region dirty; + + outDirtyRegion.clear(); + + size_t i = currentLayers.size(); + while (i--) { + const sp<Layer>& layer = currentLayers[i]; + + // start with the whole surface at its current location + const Layer::State& s(layer->getDrawingState()); + + // only consider the layers on the given layer stack + if (s.layerStack != layerStack) + continue; + + /* + * opaqueRegion: area of a surface that is fully opaque. + */ + Region opaqueRegion; + + /* + * visibleRegion: area of a surface that is visible on screen + * and not fully transparent. This is essentially the layer's + * footprint minus the opaque regions above it. + * Areas covered by a translucent surface are considered visible. + */ + Region visibleRegion; + + /* + * coveredRegion: area of a surface that is covered by all + * visible regions above it (which includes the translucent areas). + */ + Region coveredRegion; + + /* + * transparentRegion: area of a surface that is hinted to be completely + * transparent. This is only used to tell when the layer has no visible + * non-transparent regions and can be removed from the layer list. It + * does not affect the visibleRegion of this layer or any layers + * beneath it. The hint may not be correct if apps don't respect the + * SurfaceView restrictions (which, sadly, some don't). + */ + Region transparentRegion; + + + // handle hidden surfaces by setting the visible region to empty + if (CC_LIKELY(layer->isVisible())) { + const bool translucent = !layer->isOpaque(s); + Rect bounds(s.active.transform.transform(layer->computeBounds())); + visibleRegion.set(bounds); + if (!visibleRegion.isEmpty()) { + // Remove the transparent area from the visible region + if (translucent) { + const Transform tr(s.active.transform); + if (tr.transformed()) { + if (tr.preserveRects()) { + // transform the transparent region + transparentRegion = tr.transform(s.activeTransparentRegion); + } else { + // transformation too complex, can't do the + // transparent region optimization. + transparentRegion.clear(); + } + } else { + transparentRegion = s.activeTransparentRegion; + } + } + + // compute the opaque region + const int32_t layerOrientation = s.active.transform.getOrientation(); + if (s.alpha==255 && !translucent && + ((layerOrientation & Transform::ROT_INVALID) == false)) { + // the opaque region is the layer's footprint + opaqueRegion = visibleRegion; + } + } + } + + // Clip the covered region to the visible region + coveredRegion = aboveCoveredLayers.intersect(visibleRegion); + + // Update aboveCoveredLayers for next (lower) layer + aboveCoveredLayers.orSelf(visibleRegion); + + // subtract the opaque region covered by the layers above us + visibleRegion.subtractSelf(aboveOpaqueLayers); + + // compute this layer's dirty region + if (layer->contentDirty) { + // we need to invalidate the whole region + dirty = visibleRegion; + // as well, as the old visible region + dirty.orSelf(layer->visibleRegion); + layer->contentDirty = false; + } else { + /* compute the exposed region: + * the exposed region consists of two components: + * 1) what's VISIBLE now and was COVERED before + * 2) what's EXPOSED now less what was EXPOSED before + * + * note that (1) is conservative, we start with the whole + * visible region but only keep what used to be covered by + * something -- which mean it may have been exposed. + * + * (2) handles areas that were not covered by anything but got + * exposed because of a resize. + */ + const Region newExposed = visibleRegion - coveredRegion; + const Region oldVisibleRegion = layer->visibleRegion; + const Region oldCoveredRegion = layer->coveredRegion; + const Region oldExposed = oldVisibleRegion - oldCoveredRegion; + dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed); + } + dirty.subtractSelf(aboveOpaqueLayers); + + // accumulate to the screen dirty region + outDirtyRegion.orSelf(dirty); + + // Update aboveOpaqueLayers for next (lower) layer + aboveOpaqueLayers.orSelf(opaqueRegion); + + // Store the visible region in screen space + layer->setVisibleRegion(visibleRegion); + layer->setCoveredRegion(coveredRegion); + layer->setVisibleNonTransparentRegion( + visibleRegion.subtract(transparentRegion)); + } + + outOpaqueRegion = aboveOpaqueLayers; +} + +void SurfaceFlinger::invalidateLayerStack(uint32_t layerStack, + const Region& dirty) { + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + const sp<DisplayDevice>& hw(mDisplays[dpy]); + if (hw->getLayerStack() == layerStack) { + hw->dirtyRegion.orSelf(dirty); + } + } +} + +bool SurfaceFlinger::handlePageFlip() +{ + Region dirtyRegion; + + bool visibleRegions = false; + const LayerVector& layers(mDrawingState.layersSortedByZ); + bool frameQueued = false; + + // Store the set of layers that need updates. This set must not change as + // buffers are being latched, as this could result in a deadlock. + // Example: Two producers share the same command stream and: + // 1.) Layer 0 is latched + // 2.) Layer 0 gets a new frame + // 2.) Layer 1 gets a new frame + // 3.) Layer 1 is latched. + // Display is now waiting on Layer 1's frame, which is behind layer 0's + // second frame. But layer 0's second frame could be waiting on display. + Vector<Layer*> layersWithQueuedFrames; + for (size_t i = 0, count = layers.size(); i<count ; i++) { + const sp<Layer>& layer(layers[i]); + if (layer->hasQueuedFrame()) { + frameQueued = true; + if (layer->shouldPresentNow(mPrimaryDispSync)) { + layersWithQueuedFrames.push_back(layer.get()); + } else { + layer->useEmptyDamage(); + } + } else { + layer->useEmptyDamage(); + } + } + for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) { + Layer* layer = layersWithQueuedFrames[i]; + const Region dirty(layer->latchBuffer(visibleRegions)); + layer->useSurfaceDamage(); + const Layer::State& s(layer->getDrawingState()); + invalidateLayerStack(s.layerStack, dirty); + } + + mVisibleRegionsDirty |= visibleRegions; + + // If we will need to wake up at some time in the future to deal with a + // queued frame that shouldn't be displayed during this vsync period, wake + // up during the next vsync period to check again. + if (frameQueued && layersWithQueuedFrames.empty()) { + signalLayerUpdate(); + } + + // Only continue with the refresh if there is actually new work to do + return !layersWithQueuedFrames.empty(); +} + +void SurfaceFlinger::invalidateHwcGeometry() +{ + mHwWorkListDirty = true; +} + + +void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, + const Region& inDirtyRegion) +{ + // We only need to actually compose the display if: + // 1) It is being handled by hardware composer, which may need this to + // keep its virtual display state machine in sync, or + // 2) There is work to be done (the dirty region isn't empty) + bool isHwcDisplay = hw->getHwcDisplayId() >= 0; + if (!isHwcDisplay && inDirtyRegion.isEmpty()) { + return; + } + + Region dirtyRegion(inDirtyRegion); + + // compute the invalid region + hw->swapRegion.orSelf(dirtyRegion); + + uint32_t flags = hw->getFlags(); + if (flags & DisplayDevice::SWAP_RECTANGLE) { + // we can redraw only what's dirty, but since SWAP_RECTANGLE only + // takes a rectangle, we must make sure to update that whole + // rectangle in that case + dirtyRegion.set(hw->swapRegion.bounds()); + } else { + if (flags & DisplayDevice::PARTIAL_UPDATES) { + // We need to redraw the rectangle that will be updated + // (pushed to the framebuffer). + // This is needed because PARTIAL_UPDATES only takes one + // rectangle instead of a region (see DisplayDevice::flip()) + dirtyRegion.set(hw->swapRegion.bounds()); + } else { + // we need to redraw everything (the whole screen) + dirtyRegion.set(hw->bounds()); + hw->swapRegion = dirtyRegion; + } + } + + if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) { + if (!doComposeSurfaces(hw, dirtyRegion)) return; + } else { + RenderEngine& engine(getRenderEngine()); + mat4 colorMatrix = mColorMatrix; + if (mDaltonize) { + colorMatrix = colorMatrix * mDaltonizer(); + } + mat4 oldMatrix = engine.setupColorTransform(colorMatrix); + doComposeSurfaces(hw, dirtyRegion); + engine.setupColorTransform(oldMatrix); + } + + // update the swap region and clear the dirty region + hw->swapRegion.orSelf(dirtyRegion); + + // swap buffers (presentation) + hw->swapBuffers(getHwComposer()); +} + +bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty) +{ + RenderEngine& engine(getRenderEngine()); + const int32_t id = hw->getHwcDisplayId(); + HWComposer& hwc(getHwComposer()); + HWComposer::LayerListIterator cur = hwc.begin(id); + const HWComposer::LayerListIterator end = hwc.end(id); + + bool hasGlesComposition = hwc.hasGlesComposition(id); + if (hasGlesComposition) { + if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) { + ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s", + hw->getDisplayName().string()); + eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if(!getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext)) { + ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting."); + } + return false; + } + + // Never touch the framebuffer if we don't have any framebuffer layers + const bool hasHwcComposition = hwc.hasHwcComposition(id); + if (hasHwcComposition) { + // when using overlays, we assume a fully transparent framebuffer + // NOTE: we could reduce how much we need to clear, for instance + // remove where there are opaque FB layers. however, on some + // GPUs doing a "clean slate" clear might be more efficient. + // We'll revisit later if needed. + engine.clearWithColor(0, 0, 0, 0); + } else { + // we start with the whole screen area + const Region bounds(hw->getBounds()); + + // we remove the scissor part + // we're left with the letterbox region + // (common case is that letterbox ends-up being empty) + const Region letterbox(bounds.subtract(hw->getScissor())); + + // compute the area to clear + Region region(hw->undefinedRegion.merge(letterbox)); + + // but limit it to the dirty region + region.andSelf(dirty); + + // screen is already cleared here + if (!region.isEmpty()) { + // can happen with SurfaceView + drawWormhole(hw, region); + } + } + + if (hw->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) { + // just to be on the safe side, we don't set the + // scissor on the main display. It should never be needed + // anyways (though in theory it could since the API allows it). + const Rect& bounds(hw->getBounds()); + const Rect& scissor(hw->getScissor()); + if (scissor != bounds) { + // scissor doesn't match the screen's dimensions, so we + // need to clear everything outside of it and enable + // the GL scissor so we don't draw anything where we shouldn't + + // enable scissor for this frame + const uint32_t height = hw->getHeight(); + engine.setScissor(scissor.left, height - scissor.bottom, + scissor.getWidth(), scissor.getHeight()); + } + } + } + + /* + * and then, render the layers targeted at the framebuffer + */ + + const Vector< sp<Layer> >& layers(hw->getVisibleLayersSortedByZ()); + const size_t count = layers.size(); + const Transform& tr = hw->getTransform(); + if (cur != end) { + // we're using h/w composer + for (size_t i=0 ; i<count && cur!=end ; ++i, ++cur) { + const sp<Layer>& layer(layers[i]); + const Region clip(dirty.intersect(tr.transform(layer->visibleRegion))); + if (!clip.isEmpty()) { + switch (cur->getCompositionType()) { + case HWC_CURSOR_OVERLAY: + case HWC_OVERLAY: { + const Layer::State& state(layer->getDrawingState()); + if ((cur->getHints() & HWC_HINT_CLEAR_FB) + && i + && layer->isOpaque(state) && (state.alpha == 0xFF) + && hasGlesComposition) { + // never clear the very first layer since we're + // guaranteed the FB is already cleared + layer->clearWithOpenGL(hw, clip); + } + break; + } + case HWC_FRAMEBUFFER: { + layer->draw(hw, clip); + break; + } + case HWC_FRAMEBUFFER_TARGET: { + // this should not happen as the iterator shouldn't + // let us get there. + ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%zu)", i); + break; + } + } + } + layer->setAcquireFence(hw, *cur); + } + } else { + // we're not using h/w composer + for (size_t i=0 ; i<count ; ++i) { + const sp<Layer>& layer(layers[i]); + const Region clip(dirty.intersect( + tr.transform(layer->visibleRegion))); + if (!clip.isEmpty()) { + layer->draw(hw, clip); + } + } + } + + // disable scissor at the end of the frame + engine.disableScissor(); + return true; +} + +void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const { + const int32_t height = hw->getHeight(); + RenderEngine& engine(getRenderEngine()); + engine.fillRegionWithColor(region, height, 0, 0, 0, 0); +} + +status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, + const sp<IBinder>& handle, + const sp<IGraphicBufferProducer>& gbc, + const sp<Layer>& lbc) +{ + // add this layer to the current state list + { + Mutex::Autolock _l(mStateLock); + if (mCurrentState.layersSortedByZ.size() >= MAX_LAYERS) { + return NO_MEMORY; + } + mCurrentState.layersSortedByZ.add(lbc); + mGraphicBufferProducerList.add(IInterface::asBinder(gbc)); + } + + // attach this layer to the client + client->attachLayer(handle, lbc); + + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) { + Mutex::Autolock _l(mStateLock); + ssize_t index = mCurrentState.layersSortedByZ.remove(layer); + if (index >= 0) { + mLayersPendingRemoval.push(layer); + mLayersRemoved = true; + setTransactionFlags(eTransactionNeeded); + return NO_ERROR; + } + return status_t(index); +} + +uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) { + return android_atomic_release_load(&mTransactionFlags); +} + +uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} + +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { + uint32_t old = android_atomic_or(flags, &mTransactionFlags); + if ((old & flags)==0) { // wake the server up + signalTransaction(); + } + return old; +} + +void SurfaceFlinger::setTransactionState( + const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, + uint32_t flags) +{ + ATRACE_CALL(); + Mutex::Autolock _l(mStateLock); + uint32_t transactionFlags = 0; + + if (flags & eAnimation) { + // For window updates that are part of an animation we must wait for + // previous animation "frames" to be handled. + while (mAnimTransactionPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // caller after a few seconds. + ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out " + "waiting for previous animation frame"); + mAnimTransactionPending = false; + break; + } + } + } + + size_t count = displays.size(); + for (size_t i=0 ; i<count ; i++) { + const DisplayState& s(displays[i]); + transactionFlags |= setDisplayStateLocked(s); + } + + count = state.size(); + for (size_t i=0 ; i<count ; i++) { + const ComposerState& s(state[i]); + // Here we need to check that the interface we're given is indeed + // one of our own. A malicious client could give us a NULL + // IInterface, or one of its own or even one of our own but a + // different type. All these situations would cause us to crash. + // + // NOTE: it would be better to use RTTI as we could directly check + // that we have a Client*. however, RTTI is disabled in Android. + if (s.client != NULL) { + sp<IBinder> binder = IInterface::asBinder(s.client); + if (binder != NULL) { + String16 desc(binder->getInterfaceDescriptor()); + if (desc == ISurfaceComposerClient::descriptor) { + sp<Client> client( static_cast<Client *>(s.client.get()) ); + transactionFlags |= setClientStateLocked(client, s.state); + } + } + } + } + + if (transactionFlags) { + // this triggers the transaction + setTransactionFlags(transactionFlags); + + // if this is a synchronous transaction, wait for it to take effect + // before returning. + if (flags & eSynchronous) { + mTransactionPending = true; + } + if (flags & eAnimation) { + mAnimTransactionPending = true; + } + while (mTransactionPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // called after a few seconds. + ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!"); + mTransactionPending = false; + break; + } + } + } +} + +uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) +{ + ssize_t dpyIdx = mCurrentState.displays.indexOfKey(s.token); + if (dpyIdx < 0) + return 0; + + uint32_t flags = 0; + DisplayDeviceState& disp(mCurrentState.displays.editValueAt(dpyIdx)); + if (disp.isValid()) { + const uint32_t what = s.what; + if (what & DisplayState::eSurfaceChanged) { + if (IInterface::asBinder(disp.surface) != IInterface::asBinder(s.surface)) { + disp.surface = s.surface; + flags |= eDisplayTransactionNeeded; + } + } + if (what & DisplayState::eLayerStackChanged) { + if (disp.layerStack != s.layerStack) { + disp.layerStack = s.layerStack; + flags |= eDisplayTransactionNeeded; + } + } + if (what & DisplayState::eDisplayProjectionChanged) { + if (disp.orientation != s.orientation) { + disp.orientation = s.orientation; + flags |= eDisplayTransactionNeeded; + } + if (disp.frame != s.frame) { + disp.frame = s.frame; + flags |= eDisplayTransactionNeeded; + } + if (disp.viewport != s.viewport) { + disp.viewport = s.viewport; + flags |= eDisplayTransactionNeeded; + } + } + if (what & DisplayState::eDisplaySizeChanged) { + if (disp.width != s.width) { + disp.width = s.width; + flags |= eDisplayTransactionNeeded; + } + if (disp.height != s.height) { + disp.height = s.height; + flags |= eDisplayTransactionNeeded; + } + } + } + return flags; +} + +uint32_t SurfaceFlinger::setClientStateLocked( + const sp<Client>& client, + const layer_state_t& s) +{ + uint32_t flags = 0; + sp<Layer> layer(client->getLayerUser(s.surface)); + if (layer != 0) { + const uint32_t what = s.what; + if (what & layer_state_t::ePositionChanged) { + if (layer->setPosition(s.x, s.y)) + flags |= eTraversalNeeded; + } + if (what & layer_state_t::eLayerChanged) { + // NOTE: index needs to be calculated before we update the state + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); + if (layer->setLayer(s.z) && idx >= 0) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); + // we need traversal (state changed) + // AND transaction (list changed) + flags |= eTransactionNeeded|eTraversalNeeded; + } + } + if (what & layer_state_t::eSizeChanged) { + if (layer->setSize(s.w, s.h)) { + flags |= eTraversalNeeded; + } + } + if (what & layer_state_t::eAlphaChanged) { + if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) + flags |= eTraversalNeeded; + } + if (what & layer_state_t::eMatrixChanged) { + if (layer->setMatrix(s.matrix)) + flags |= eTraversalNeeded; + } + if (what & layer_state_t::eTransparentRegionChanged) { + if (layer->setTransparentRegionHint(s.transparentRegion)) + flags |= eTraversalNeeded; + } + if (what & layer_state_t::eFlagsChanged) { + if (layer->setFlags(s.flags, s.mask)) + flags |= eTraversalNeeded; + } + if (what & layer_state_t::eCropChanged) { + if (layer->setCrop(s.crop)) + flags |= eTraversalNeeded; + } + if (what & layer_state_t::eLayerStackChanged) { + // NOTE: index needs to be calculated before we update the state + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); + if (layer->setLayerStack(s.layerStack) && idx >= 0) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); + // we need traversal (state changed) + // AND transaction (list changed) + flags |= eTransactionNeeded|eTraversalNeeded; + } + } + if (what & layer_state_t::eDeferTransaction) { + layer->deferTransactionUntil(s.handle, s.frameNumber); + // We don't trigger a traversal here because if no other state is + // changed, we don't want this to cause any more work + } + } + return flags; +} + +status_t SurfaceFlinger::createLayer( + const String8& name, + const sp<Client>& client, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, + sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) +{ + //ALOGD("createLayer for (%d x %d), name=%s", w, h, name.string()); + if (int32_t(w|h) < 0) { + ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)", + int(w), int(h)); + return BAD_VALUE; + } + + status_t result = NO_ERROR; + + sp<Layer> layer; + + switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { + case ISurfaceComposerClient::eFXSurfaceNormal: + result = createNormalLayer(client, + name, w, h, flags, format, + handle, gbp, &layer); + break; + case ISurfaceComposerClient::eFXSurfaceDim: + result = createDimLayer(client, + name, w, h, flags, + handle, gbp, &layer); + break; + default: + result = BAD_VALUE; + break; + } + + if (result != NO_ERROR) { + return result; + } + + result = addClientLayer(client, *handle, *gbp, layer); + if (result != NO_ERROR) { + return result; + } + + setTransactionFlags(eTransactionNeeded); + return result; +} + +status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client, + const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format, + sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) +{ + // initialize the surfaces + switch (format) { + case PIXEL_FORMAT_TRANSPARENT: + case PIXEL_FORMAT_TRANSLUCENT: + format = PIXEL_FORMAT_RGBA_8888; + break; + case PIXEL_FORMAT_OPAQUE: + format = PIXEL_FORMAT_RGBX_8888; + break; + } + + *outLayer = new Layer(this, client, name, w, h, flags); + status_t err = (*outLayer)->setBuffers(w, h, format, flags); + if (err == NO_ERROR) { + *handle = (*outLayer)->getHandle(); + *gbp = (*outLayer)->getProducer(); + } + + ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err)); + return err; +} + +status_t SurfaceFlinger::createDimLayer(const sp<Client>& client, + const String8& name, uint32_t w, uint32_t h, uint32_t flags, + sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) +{ + *outLayer = new LayerDim(this, client, name, w, h, flags); + *handle = (*outLayer)->getHandle(); + *gbp = (*outLayer)->getProducer(); + return NO_ERROR; +} + +status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle) +{ + // called by the window manager when it wants to remove a Layer + status_t err = NO_ERROR; + sp<Layer> l(client->getLayerUser(handle)); + if (l != NULL) { + err = removeLayer(l); + ALOGE_IF(err<0 && err != NAME_NOT_FOUND, + "error removing layer=%p (%s)", l.get(), strerror(-err)); + } + return err; +} + +status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer) +{ + // called by ~LayerCleaner() when all references to the IBinder (handle) + // are gone + status_t err = NO_ERROR; + sp<Layer> l(layer.promote()); + if (l != NULL) { + err = removeLayer(l); + ALOGE_IF(err<0 && err != NAME_NOT_FOUND, + "error removing layer=%p (%s)", l.get(), strerror(-err)); + } + return err; +} + +// --------------------------------------------------------------------------- + +void SurfaceFlinger::onInitializeDisplays() { + // reset screen orientation and use primary layer stack + Vector<ComposerState> state; + Vector<DisplayState> displays; + DisplayState d; + d.what = DisplayState::eDisplayProjectionChanged | + DisplayState::eLayerStackChanged; + d.token = mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]; + d.layerStack = 0; + d.orientation = DisplayState::eOrientationDefault; + d.frame.makeInvalid(); + d.viewport.makeInvalid(); + d.width = 0; + d.height = 0; + displays.add(d); + setTransactionState(state, displays, 0); + setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL); + + const nsecs_t period = + getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + mAnimFrameTracker.setDisplayRefreshPeriod(period); +} + +void SurfaceFlinger::initializeDisplays() { + class MessageScreenInitialized : public MessageBase { + SurfaceFlinger* flinger; + public: + MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { } + virtual bool handler() { + flinger->onInitializeDisplays(); + return true; + } + }; + sp<MessageBase> msg = new MessageScreenInitialized(this); + postMessageAsync(msg); // we may be called from main thread, use async message +} + +void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, + int mode) { + ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(), + this); + int32_t type = hw->getDisplayType(); + int currentMode = hw->getPowerMode(); + + if (mode == currentMode) { + ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode); + return; + } + + hw->setPowerMode(mode); + if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + ALOGW("Trying to set power mode for virtual display"); + return; + } + + if (currentMode == HWC_POWER_MODE_OFF) { + getHwComposer().setPowerMode(type, mode); + if (type == DisplayDevice::DISPLAY_PRIMARY) { + // FIXME: eventthread only knows about the main display right now + mEventThread->onScreenAcquired(); + resyncToHardwareVsync(true); + } + + mVisibleRegionsDirty = true; + mHasPoweredOff = true; + repaintEverything(); + } else if (mode == HWC_POWER_MODE_OFF) { + if (type == DisplayDevice::DISPLAY_PRIMARY) { + disableHardwareVsync(true); // also cancels any in-progress resync + + // FIXME: eventthread only knows about the main display right now + mEventThread->onScreenReleased(); + } + + getHwComposer().setPowerMode(type, mode); + mVisibleRegionsDirty = true; + // from this point on, SF will stop drawing on this display + } else { + getHwComposer().setPowerMode(type, mode); + } +} + +void SurfaceFlinger::setPowerMode(const sp<IBinder>& display, int mode) { + class MessageSetPowerMode: public MessageBase { + SurfaceFlinger& mFlinger; + sp<IBinder> mDisplay; + int mMode; + public: + MessageSetPowerMode(SurfaceFlinger& flinger, + const sp<IBinder>& disp, int mode) : mFlinger(flinger), + mDisplay(disp) { mMode = mode; } + virtual bool handler() { + sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); + if (hw == NULL) { + ALOGE("Attempt to set power mode = %d for null display %p", + mMode, mDisplay.get()); + } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { + ALOGW("Attempt to set power mode = %d for virtual display", + mMode); + } else { + mFlinger.setPowerModeInternal(hw, mMode); + } + return true; + } + }; + sp<MessageBase> msg = new MessageSetPowerMode(*this, display, mode); + postMessageSync(msg); +} + +// --------------------------------------------------------------------------- + +status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) +{ + String8 result; + + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission(sDump, pid, uid)) { + result.appendFormat("Permission Denial: " + "can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid); + } else { + // Try to get the main lock, but give up after one second + // (this would indicate SF is stuck, but we want to be able to + // print something in dumpsys). + status_t err = mStateLock.timedLock(s2ns(1)); + bool locked = (err == NO_ERROR); + if (!locked) { + result.appendFormat( + "SurfaceFlinger appears to be unresponsive (%s [%d]), " + "dumping anyways (no locks held)\n", strerror(-err), err); + } + + bool dumpAll = true; + size_t index = 0; + size_t numArgs = args.size(); + if (numArgs) { + if ((index < numArgs) && + (args[index] == String16("--list"))) { + index++; + listLayersLocked(args, index, result); + dumpAll = false; + } + + if ((index < numArgs) && + (args[index] == String16("--latency"))) { + index++; + dumpStatsLocked(args, index, result); + dumpAll = false; + } + + if ((index < numArgs) && + (args[index] == String16("--latency-clear"))) { + index++; + clearStatsLocked(args, index, result); + dumpAll = false; + } + + if ((index < numArgs) && + (args[index] == String16("--dispsync"))) { + index++; + mPrimaryDispSync.dump(result); + dumpAll = false; + } + + if ((index < numArgs) && + (args[index] == String16("--static-screen"))) { + index++; + dumpStaticScreenStats(result); + dumpAll = false; + } + } + + if (dumpAll) { + dumpAllLocked(args, index, result); + } + + if (locked) { + mStateLock.unlock(); + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */, + size_t& /* index */, String8& result) const +{ + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(currentLayers[i]); + result.appendFormat("%s\n", layer->getName().string()); + } +} + +void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index, + String8& result) const +{ + String8 name; + if (index < args.size()) { + name = String8(args[index]); + index++; + } + + const nsecs_t period = + getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); + result.appendFormat("%" PRId64 "\n", period); + + if (name.isEmpty()) { + mAnimFrameTracker.dumpStats(result); + } else { + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(currentLayers[i]); + if (name == layer->getName()) { + layer->dumpFrameStats(result); + } + } + } +} + +void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index, + String8& /* result */) +{ + String8 name; + if (index < args.size()) { + name = String8(args[index]); + index++; + } + + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(currentLayers[i]); + if (name.isEmpty() || (name == layer->getName())) { + layer->clearFrameStats(); + } + } + + mAnimFrameTracker.clearStats(); +} + +// This should only be called from the main thread. Otherwise it would need +// the lock and should use mCurrentState rather than mDrawingState. +void SurfaceFlinger::logFrameStats() { + const LayerVector& drawingLayers = mDrawingState.layersSortedByZ; + const size_t count = drawingLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(drawingLayers[i]); + layer->logFrameStats(); + } + + mAnimFrameTracker.logAndResetStats(String8("<win-anim>")); +} + +/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result) +{ + static const char* config = + " [sf" +#ifdef HAS_CONTEXT_PRIORITY + " HAS_CONTEXT_PRIORITY" +#endif +#ifdef NEVER_DEFAULT_TO_ASYNC_MODE + " NEVER_DEFAULT_TO_ASYNC_MODE" +#endif +#ifdef TARGET_DISABLE_TRIPLE_BUFFERING + " TARGET_DISABLE_TRIPLE_BUFFERING" +#endif + "]"; + result.append(config); +} + +void SurfaceFlinger::dumpStaticScreenStats(String8& result) const +{ + result.appendFormat("Static screen stats:\n"); + for (size_t b = 0; b < NUM_BUCKETS - 1; ++b) { + float bucketTimeSec = mFrameBuckets[b] / 1e9; + float percent = 100.0f * + static_cast<float>(mFrameBuckets[b]) / mTotalTime; + result.appendFormat(" < %zd frames: %.3f s (%.1f%%)\n", + b + 1, bucketTimeSec, percent); + } + float bucketTimeSec = mFrameBuckets[NUM_BUCKETS - 1] / 1e9; + float percent = 100.0f * + static_cast<float>(mFrameBuckets[NUM_BUCKETS - 1]) / mTotalTime; + result.appendFormat(" %zd+ frames: %.3f s (%.1f%%)\n", + NUM_BUCKETS - 1, bucketTimeSec, percent); +} + +void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, + String8& result) const +{ + bool colorize = false; + if (index < args.size() + && (args[index] == String16("--color"))) { + colorize = true; + index++; + } + + Colorizer colorizer(colorize); + + // figure out if we're stuck somewhere + const nsecs_t now = systemTime(); + const nsecs_t inSwapBuffers(mDebugInSwapBuffers); + const nsecs_t inTransaction(mDebugInTransaction); + nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0; + nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; + + /* + * Dump library configuration. + */ + + colorizer.bold(result); + result.append("Build configuration:"); + colorizer.reset(result); + appendSfConfigString(result); + appendUiConfigString(result); + appendGuiConfigString(result); + result.append("\n"); + + colorizer.bold(result); + result.append("Sync configuration: "); + colorizer.reset(result); + result.append(SyncFeatures::getInstance().toString()); + result.append("\n"); + + colorizer.bold(result); + result.append("DispSync configuration: "); + colorizer.reset(result); + result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, " + "present offset %d ns (refresh %" PRId64 " ns)", + vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, PRESENT_TIME_OFFSET_FROM_VSYNC_NS, + mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY)); + result.append("\n"); + + // Dump static screen stats + result.append("\n"); + dumpStaticScreenStats(result); + result.append("\n"); + + /* + * Dump the visible layer list + */ + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + colorizer.bold(result); + result.appendFormat("Visible layers (count = %zu)\n", count); + colorizer.reset(result); + for (size_t i=0 ; i<count ; i++) { + const sp<Layer>& layer(currentLayers[i]); + layer->dump(result, colorizer); + } + + /* + * Dump Display state + */ + + colorizer.bold(result); + result.appendFormat("Displays (%zu entries)\n", mDisplays.size()); + colorizer.reset(result); + for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + const sp<const DisplayDevice>& hw(mDisplays[dpy]); + hw->dump(result); + } + + /* + * Dump SurfaceFlinger global state + */ + + colorizer.bold(result); + result.append("SurfaceFlinger global state:\n"); + colorizer.reset(result); + + HWComposer& hwc(getHwComposer()); + sp<const DisplayDevice> hw(getDefaultDisplayDevice()); + + colorizer.bold(result); + result.appendFormat("EGL implementation : %s\n", + eglQueryStringImplementationANDROID(mEGLDisplay, EGL_VERSION)); + colorizer.reset(result); + result.appendFormat("%s\n", + eglQueryStringImplementationANDROID(mEGLDisplay, EGL_EXTENSIONS)); + + mRenderEngine->dump(result); + + hw->undefinedRegion.dump(result, "undefinedRegion"); + result.appendFormat(" orientation=%d, isDisplayOn=%d\n", + hw->getOrientation(), hw->isDisplayOn()); + result.appendFormat( + " last eglSwapBuffers() time: %f us\n" + " last transaction time : %f us\n" + " transaction-flags : %08x\n" + " refresh-rate : %f fps\n" + " x-dpi : %f\n" + " y-dpi : %f\n" + " gpu_to_cpu_unsupported : %d\n" + , + mLastSwapBufferTime/1000.0, + mLastTransactionTime/1000.0, + mTransactionFlags, + 1e9 / hwc.getRefreshPeriod(HWC_DISPLAY_PRIMARY), + hwc.getDpiX(HWC_DISPLAY_PRIMARY), + hwc.getDpiY(HWC_DISPLAY_PRIMARY), + !mGpuToCpuSupported); + + result.appendFormat(" eglSwapBuffers time: %f us\n", + inSwapBuffersDuration/1000.0); + + result.appendFormat(" transaction time: %f us\n", + inTransactionDuration/1000.0); + + /* + * VSYNC state + */ + mEventThread->dump(result); + + /* + * Dump HWComposer state + */ + colorizer.bold(result); + result.append("h/w composer state:\n"); + colorizer.reset(result); + result.appendFormat(" h/w composer %s and %s\n", + hwc.initCheck()==NO_ERROR ? "present" : "not present", + (mDebugDisableHWC || mDebugRegion || mDaltonize + || mHasColorMatrix) ? "disabled" : "enabled"); + hwc.dump(result); + + /* + * Dump gralloc state + */ + const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); + alloc.dump(result); +} + +const Vector< sp<Layer> >& +SurfaceFlinger::getLayerSortedByZForHwcDisplay(int id) { + // Note: mStateLock is held here + wp<IBinder> dpy; + for (size_t i=0 ; i<mDisplays.size() ; i++) { + if (mDisplays.valueAt(i)->getHwcDisplayId() == id) { + dpy = mDisplays.keyAt(i); + break; + } + } + if (dpy == NULL) { + ALOGE("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id); + // Just use the primary display so we have something to return + dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY); + } + return getDisplayDevice(dpy)->getVisibleLayersSortedByZ(); +} + +bool SurfaceFlinger::startDdmConnection() +{ + void* libddmconnection_dso = + dlopen("libsurfaceflinger_ddmconnection.so", RTLD_NOW); + if (!libddmconnection_dso) { + return false; + } + void (*DdmConnection_start)(const char* name); + DdmConnection_start = + (decltype(DdmConnection_start))dlsym(libddmconnection_dso, "DdmConnection_start"); + if (!DdmConnection_start) { + dlclose(libddmconnection_dso); + return false; + } + (*DdmConnection_start)(getServiceName()); + return true; +} + +status_t SurfaceFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case CREATE_CONNECTION: + case CREATE_DISPLAY: + case SET_TRANSACTION_STATE: + case BOOT_FINISHED: + case CLEAR_ANIMATION_FRAME_STATS: + case GET_ANIMATION_FRAME_STATS: + case SET_POWER_MODE: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) && + !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) { + ALOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + break; + } + case CAPTURE_SCREEN: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_GRAPHICS) && + !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { + ALOGE("Permission Denial: " + "can't read framebuffer pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + break; + } + } + + status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); + if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + if (CC_UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + ALOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + int n; + switch (code) { + case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE + case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE + return NO_ERROR; + case 1002: // SHOW_UPDATES + n = data.readInt32(); + mDebugRegion = n ? n : (mDebugRegion ? 0 : 1); + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; + case 1004:{ // repaint everything + repaintEverything(); + return NO_ERROR; + } + case 1005:{ // force transaction + setTransactionFlags( + eTransactionNeeded| + eDisplayTransactionNeeded| + eTraversalNeeded); + return NO_ERROR; + } + case 1006:{ // send empty update + signalRefresh(); + return NO_ERROR; + } + case 1008: // toggle use of hw composer + n = data.readInt32(); + mDebugDisableHWC = n ? 1 : 0; + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; + case 1009: // toggle use of transform hint + n = data.readInt32(); + mDebugDisableTransformHint = n ? 1 : 0; + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; + case 1010: // interrogate. + reply->writeInt32(0); + reply->writeInt32(0); + reply->writeInt32(mDebugRegion); + reply->writeInt32(0); + reply->writeInt32(mDebugDisableHWC); + return NO_ERROR; + case 1013: { + Mutex::Autolock _l(mStateLock); + sp<const DisplayDevice> hw(getDefaultDisplayDevice()); + reply->writeInt32(hw->getPageFlipCount()); + return NO_ERROR; + } + case 1014: { + // daltonize + n = data.readInt32(); + switch (n % 10) { + case 1: mDaltonizer.setType(Daltonizer::protanomaly); break; + case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break; + case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break; + } + if (n >= 10) { + mDaltonizer.setMode(Daltonizer::correction); + } else { + mDaltonizer.setMode(Daltonizer::simulation); + } + mDaltonize = n > 0; + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; + } + case 1015: { + // apply a color matrix + n = data.readInt32(); + mHasColorMatrix = n ? 1 : 0; + if (n) { + // color matrix is sent as mat3 matrix followed by vec3 + // offset, then packed into a mat4 where the last row is + // the offset and extra values are 0 + for (size_t i = 0 ; i < 4; i++) { + for (size_t j = 0; j < 4; j++) { + mColorMatrix[i][j] = data.readFloat(); + } + } + } else { + mColorMatrix = mat4(); + } + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; + } + // This is an experimental interface + // Needs to be shifted to proper binder interface when we productize + case 1016: { + n = data.readInt32(); + mPrimaryDispSync.setRefreshSkipCount(n); + return NO_ERROR; + } + case 1017: { + n = data.readInt32(); + mForceFullDamage = static_cast<bool>(n); + return NO_ERROR; + } + case 1018: { // Modify Choreographer's phase offset + n = data.readInt32(); + mEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + return NO_ERROR; + } + case 1019: { // Modify SurfaceFlinger's phase offset + n = data.readInt32(); + mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + return NO_ERROR; + } + } + } + return err; +} + +void SurfaceFlinger::repaintEverything() { + android_atomic_or(1, &mRepaintEverything); + signalTransaction(); +} + +// --------------------------------------------------------------------------- +// Capture screen into an IGraphiBufferProducer +// --------------------------------------------------------------------------- + +/* The code below is here to handle b/8734824 + * + * We create a IGraphicBufferProducer wrapper that forwards all calls + * from the surfaceflinger thread to the calling binder thread, where they + * are executed. This allows the calling thread in the calling process to be + * reused and not depend on having "enough" binder threads to handle the + * requests. + */ +class GraphicProducerWrapper : public BBinder, public MessageHandler { + /* Parts of GraphicProducerWrapper are run on two different threads, + * communicating by sending messages via Looper but also by shared member + * data. Coherence maintenance is subtle and in places implicit (ugh). + * + * Don't rely on Looper's sendMessage/handleMessage providing + * release/acquire semantics for any data not actually in the Message. + * Data going from surfaceflinger to binder threads needs to be + * synchronized explicitly. + * + * Barrier open/wait do provide release/acquire semantics. This provides + * implicit synchronization for data coming back from binder to + * surfaceflinger threads. + */ + + sp<IGraphicBufferProducer> impl; + sp<Looper> looper; + status_t result; + bool exitPending; + bool exitRequested; + Barrier barrier; + uint32_t code; + Parcel const* data; + Parcel* reply; + + enum { + MSG_API_CALL, + MSG_EXIT + }; + + /* + * Called on surfaceflinger thread. This is called by our "fake" + * BpGraphicBufferProducer. We package the data and reply Parcel and + * forward them to the binder thread. + */ + virtual status_t transact(uint32_t code, + const Parcel& data, Parcel* reply, uint32_t /* flags */) { + this->code = code; + this->data = &data; + this->reply = reply; + if (exitPending) { + // if we've exited, we run the message synchronously right here. + // note (JH): as far as I can tell from looking at the code, this + // never actually happens. if it does, i'm not sure if it happens + // on the surfaceflinger or binder thread. + handleMessage(Message(MSG_API_CALL)); + } else { + barrier.close(); + // Prevent stores to this->{code, data, reply} from being + // reordered later than the construction of Message. + atomic_thread_fence(memory_order_release); + looper->sendMessage(this, Message(MSG_API_CALL)); + barrier.wait(); + } + return result; + } + + /* + * here we run on the binder thread. All we've got to do is + * call the real BpGraphicBufferProducer. + */ + virtual void handleMessage(const Message& message) { + int what = message.what; + // Prevent reads below from happening before the read from Message + atomic_thread_fence(memory_order_acquire); + if (what == MSG_API_CALL) { + result = IInterface::asBinder(impl)->transact(code, data[0], reply); + barrier.open(); + } else if (what == MSG_EXIT) { + exitRequested = true; + } + } + +public: + GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl) + : impl(impl), + looper(new Looper(true)), + result(NO_ERROR), + exitPending(false), + exitRequested(false), + code(0), + data(NULL), + reply(NULL) + {} + + // Binder thread + status_t waitForResponse() { + do { + looper->pollOnce(-1); + } while (!exitRequested); + return result; + } + + // Client thread + void exit(status_t result) { + this->result = result; + exitPending = true; + // Ensure this->result is visible to the binder thread before it + // handles the message. + atomic_thread_fence(memory_order_release); + looper->sendMessage(this, Message(MSG_EXIT)); + } +}; + + +status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, + const sp<IGraphicBufferProducer>& producer, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ, + bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { + + if (CC_UNLIKELY(display == 0)) + return BAD_VALUE; + + if (CC_UNLIKELY(producer == 0)) + return BAD_VALUE; + + // if we have secure windows on this display, never allow the screen capture + // unless the producer interface is local (i.e.: we can take a screenshot for + // ourselves). + bool isLocalScreenshot = IInterface::asBinder(producer)->localBinder(); + + // Convert to surfaceflinger's internal rotation type. + Transform::orientation_flags rotationFlags; + switch (rotation) { + case ISurfaceComposer::eRotateNone: + rotationFlags = Transform::ROT_0; + break; + case ISurfaceComposer::eRotate90: + rotationFlags = Transform::ROT_90; + break; + case ISurfaceComposer::eRotate180: + rotationFlags = Transform::ROT_180; + break; + case ISurfaceComposer::eRotate270: + rotationFlags = Transform::ROT_270; + break; + default: + rotationFlags = Transform::ROT_0; + ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation); + break; + } + + class MessageCaptureScreen : public MessageBase { + SurfaceFlinger* flinger; + sp<IBinder> display; + sp<IGraphicBufferProducer> producer; + Rect sourceCrop; + uint32_t reqWidth, reqHeight; + uint32_t minLayerZ,maxLayerZ; + bool useIdentityTransform; + Transform::orientation_flags rotation; + status_t result; + bool isLocalScreenshot; + public: + MessageCaptureScreen(SurfaceFlinger* flinger, + const sp<IBinder>& display, + const sp<IGraphicBufferProducer>& producer, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ, + bool useIdentityTransform, + Transform::orientation_flags rotation, + bool isLocalScreenshot) + : flinger(flinger), display(display), producer(producer), + sourceCrop(sourceCrop), reqWidth(reqWidth), reqHeight(reqHeight), + minLayerZ(minLayerZ), maxLayerZ(maxLayerZ), + useIdentityTransform(useIdentityTransform), + rotation(rotation), result(PERMISSION_DENIED), + isLocalScreenshot(isLocalScreenshot) + { + } + status_t getResult() const { + return result; + } + virtual bool handler() { + Mutex::Autolock _l(flinger->mStateLock); + sp<const DisplayDevice> hw(flinger->getDisplayDevice(display)); + result = flinger->captureScreenImplLocked(hw, producer, + sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, + useIdentityTransform, rotation, isLocalScreenshot); + static_cast<GraphicProducerWrapper*>(IInterface::asBinder(producer).get())->exit(result); + return true; + } + }; + + // make sure to process transactions before screenshots -- a transaction + // might already be pending but scheduled for VSYNC; this guarantees we + // will handle it before the screenshot. When VSYNC finally arrives + // the scheduled transaction will be a no-op. If no transactions are + // scheduled at this time, this will end-up being a no-op as well. + mEventQueue.invalidateTransactionNow(); + + // this creates a "fake" BBinder which will serve as a "fake" remote + // binder to receive the marshaled calls and forward them to the + // real remote (a BpGraphicBufferProducer) + sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer); + + // the asInterface() call below creates our "fake" BpGraphicBufferProducer + // which does the marshaling work forwards to our "fake remote" above. + sp<MessageBase> msg = new MessageCaptureScreen(this, + display, IGraphicBufferProducer::asInterface( wrapper ), + sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, + useIdentityTransform, rotationFlags, isLocalScreenshot); + + status_t res = postMessageAsync(msg); + if (res == NO_ERROR) { + res = wrapper->waitForResponse(); + } + return res; +} + + +void SurfaceFlinger::renderScreenImplLocked( + const sp<const DisplayDevice>& hw, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ, + bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation) +{ + ATRACE_CALL(); + RenderEngine& engine(getRenderEngine()); + + // get screen geometry + const int32_t hw_w = hw->getWidth(); + const int32_t hw_h = hw->getHeight(); + const bool filtering = static_cast<int32_t>(reqWidth) != hw_w || + static_cast<int32_t>(reqHeight) != hw_h; + + // if a default or invalid sourceCrop is passed in, set reasonable values + if (sourceCrop.width() == 0 || sourceCrop.height() == 0 || + !sourceCrop.isValid()) { + sourceCrop.setLeftTop(Point(0, 0)); + sourceCrop.setRightBottom(Point(hw_w, hw_h)); + } + + // ensure that sourceCrop is inside screen + if (sourceCrop.left < 0) { + ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left); + } + if (sourceCrop.right > hw_w) { + ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, hw_w); + } + if (sourceCrop.top < 0) { + ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top); + } + if (sourceCrop.bottom > hw_h) { + ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, hw_h); + } + + // make sure to clear all GL error flags + engine.checkErrors(); + + // set-up our viewport + engine.setViewportAndProjection( + reqWidth, reqHeight, sourceCrop, hw_h, yswap, rotation); + engine.disableTexturing(); + + // redraw the screen entirely... + engine.clearWithColor(0, 0, 0, 1); + + const LayerVector& layers( mDrawingState.layersSortedByZ ); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; ++i) { + const sp<Layer>& layer(layers[i]); + const Layer::State& state(layer->getDrawingState()); + if (state.layerStack == hw->getLayerStack()) { + if (state.z >= minLayerZ && state.z <= maxLayerZ) { + if (layer->isVisible()) { + if (filtering) layer->setFiltering(true); + layer->draw(hw, useIdentityTransform); + if (filtering) layer->setFiltering(false); + } + } + } + } + + // compositionComplete is needed for older driver + hw->compositionComplete(); + hw->setViewportAndProjection(); +} + + +status_t SurfaceFlinger::captureScreenImplLocked( + const sp<const DisplayDevice>& hw, + const sp<IGraphicBufferProducer>& producer, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ, + bool useIdentityTransform, Transform::orientation_flags rotation, + bool isLocalScreenshot) +{ + ATRACE_CALL(); + + // get screen geometry + uint32_t hw_w = hw->getWidth(); + uint32_t hw_h = hw->getHeight(); + + if (rotation & Transform::ROT_90) { + std::swap(hw_w, hw_h); + } + + if ((reqWidth > hw_w) || (reqHeight > hw_h)) { + ALOGE("size mismatch (%d, %d) > (%d, %d)", + reqWidth, reqHeight, hw_w, hw_h); + return BAD_VALUE; + } + + reqWidth = (!reqWidth) ? hw_w : reqWidth; + reqHeight = (!reqHeight) ? hw_h : reqHeight; + + bool secureLayerIsVisible = false; + const LayerVector& layers(mDrawingState.layersSortedByZ); + const size_t count = layers.size(); + for (size_t i = 0 ; i < count ; ++i) { + const sp<Layer>& layer(layers[i]); + const Layer::State& state(layer->getDrawingState()); + if (state.layerStack == hw->getLayerStack() && state.z >= minLayerZ && + state.z <= maxLayerZ && layer->isVisible() && + layer->isSecure()) { + secureLayerIsVisible = true; + } + } + + if (!isLocalScreenshot && secureLayerIsVisible) { + ALOGW("FB is protected: PERMISSION_DENIED"); + return PERMISSION_DENIED; + } + + // create a surface (because we're a producer, and we need to + // dequeue/queue a buffer) + sp<Surface> sur = new Surface(producer, false); + ANativeWindow* window = sur.get(); + + status_t result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); + if (result == NO_ERROR) { + uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + + int err = 0; + err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight); + err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888); + err |= native_window_set_usage(window, usage); + + if (err == NO_ERROR) { + ANativeWindowBuffer* buffer; + /* TODO: Once we have the sync framework everywhere this can use + * server-side waits on the fence that dequeueBuffer returns. + */ + result = native_window_dequeue_buffer_and_wait(window, &buffer); + if (result == NO_ERROR) { + int syncFd = -1; + // create an EGLImage from the buffer so we can later + // turn it into a texture + EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, buffer, NULL); + if (image != EGL_NO_IMAGE_KHR) { + // this binds the given EGLImage as a framebuffer for the + // duration of this scope. + RenderEngine::BindImageAsFramebuffer imageBond(getRenderEngine(), image); + if (imageBond.getStatus() == NO_ERROR) { + // this will in fact render into our dequeued buffer + // via an FBO, which means we didn't have to create + // an EGLSurface and therefore we're not + // dependent on the context's EGLConfig. + renderScreenImplLocked( + hw, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, true, + useIdentityTransform, rotation); + + // Attempt to create a sync khr object that can produce a sync point. If that + // isn't available, create a non-dupable sync object in the fallback path and + // wait on it directly. + EGLSyncKHR sync; + if (!DEBUG_SCREENSHOTS) { + sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); + // native fence fd will not be populated until flush() is done. + getRenderEngine().flush(); + } else { + sync = EGL_NO_SYNC_KHR; + } + if (sync != EGL_NO_SYNC_KHR) { + // get the sync fd + syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync); + if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGW("captureScreen: failed to dup sync khr object"); + syncFd = -1; + } + eglDestroySyncKHR(mEGLDisplay, sync); + } else { + // fallback path + sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL); + if (sync != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 2000000000 /*2 sec*/); + EGLint eglErr = eglGetError(); + if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGW("captureScreen: fence wait timed out"); + } else { + ALOGW_IF(eglErr != EGL_SUCCESS, + "captureScreen: error waiting on EGL fence: %#x", eglErr); + } + eglDestroySyncKHR(mEGLDisplay, sync); + } else { + ALOGW("captureScreen: error creating EGL fence: %#x", eglGetError()); + } + } + if (DEBUG_SCREENSHOTS) { + uint32_t* pixels = new uint32_t[reqWidth*reqHeight]; + getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels); + checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, + hw, minLayerZ, maxLayerZ); + delete [] pixels; + } + + } else { + ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot"); + result = INVALID_OPERATION; + } + // destroy our image + eglDestroyImageKHR(mEGLDisplay, image); + } else { + result = BAD_VALUE; + } + // queueBuffer takes ownership of syncFd + result = window->queueBuffer(window, buffer, syncFd); + } + } else { + result = BAD_VALUE; + } + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + } + + return result; +} + +void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr, + const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) { + if (DEBUG_SCREENSHOTS) { + for (size_t y=0 ; y<h ; y++) { + uint32_t const * p = (uint32_t const *)vaddr + y*s; + for (size_t x=0 ; x<w ; x++) { + if (p[x] != 0xFF000000) return; + } + } + ALOGE("*** we just took a black screenshot ***\n" + "requested minz=%d, maxz=%d, layerStack=%d", + minLayerZ, maxLayerZ, hw->getLayerStack()); + const LayerVector& layers( mDrawingState.layersSortedByZ ); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; ++i) { + const sp<Layer>& layer(layers[i]); + const Layer::State& state(layer->getDrawingState()); + const bool visible = (state.layerStack == hw->getLayerStack()) + && (state.z >= minLayerZ && state.z <= maxLayerZ) + && (layer->isVisible()); + ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x", + visible ? '+' : '-', + i, layer->getName().string(), state.layerStack, state.z, + layer->isVisible(), state.flags, state.alpha); + } + } +} + +// --------------------------------------------------------------------------- + +SurfaceFlinger::LayerVector::LayerVector() { +} + +SurfaceFlinger::LayerVector::LayerVector(const LayerVector& rhs) + : SortedVector<sp<Layer> >(rhs) { +} + +int SurfaceFlinger::LayerVector::do_compare(const void* lhs, + const void* rhs) const +{ + // sort layers per layer-stack, then by z-order and finally by sequence + const sp<Layer>& l(*reinterpret_cast<const sp<Layer>*>(lhs)); + const sp<Layer>& r(*reinterpret_cast<const sp<Layer>*>(rhs)); + + uint32_t ls = l->getCurrentState().layerStack; + uint32_t rs = r->getCurrentState().layerStack; + if (ls != rs) + return ls - rs; + + uint32_t lz = l->getCurrentState().z; + uint32_t rz = r->getCurrentState().z; + if (lz != rz) + return lz - rz; + + return l->sequence - r->sequence; +} + +// --------------------------------------------------------------------------- + +SurfaceFlinger::DisplayDeviceState::DisplayDeviceState() + : type(DisplayDevice::DISPLAY_ID_INVALID), + layerStack(DisplayDevice::NO_LAYER_STACK), + orientation(0), + width(0), + height(0), + isSecure(false) { +} + +SurfaceFlinger::DisplayDeviceState::DisplayDeviceState( + DisplayDevice::DisplayType type, bool isSecure) + : type(type), + layerStack(DisplayDevice::NO_LAYER_STACK), + orientation(0), + width(0), + height(0), + isSecure(isSecure) { + viewport.makeInvalid(); + frame.makeInvalid(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android + + +#if defined(__gl_h_) +#error "don't include gl/gl.h in this file" +#endif + +#if defined(__gl2_h_) +#error "don't include gl2/gl2.h in this file" +#endif diff --git a/vulkan/api/templates/vulkan_h.tmpl b/vulkan/api/templates/vulkan_h.tmpl index b2a77ec4a7..83a5e40804 100644 --- a/vulkan/api/templates/vulkan_h.tmpl +++ b/vulkan/api/templates/vulkan_h.tmpl @@ -17,7 +17,7 @@ extern "C" { #endif ¶ /* -** Copyright (c) 2015 The Khronos Group Inc. +** Copyright (c) 2015-2016 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the @@ -47,12 +47,16 @@ extern "C" { #define VK_VERSION_1_0 1 #include "vk_platform.h" ¶ -#define VK_MAKE_VERSION(major, minor, patch) ((major << 22) | (minor << 12) | patch) +#define VK_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch)) ¶ // Vulkan API version supported by this file #define VK_API_VERSION \ VK_MAKE_VERSION({{Global "VERSION_MAJOR"}}, {{Global "VERSION_MINOR"}}, {{Global "VERSION_PATCH"}}) ¶ +#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) +#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) +¶ #if defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1800 || __cplusplus >= 201103L) #define VK_NULL_HANDLE nullptr #else diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api index 9b1e6842d0..10565abb8b 100644 --- a/vulkan/api/vulkan.api +++ b/vulkan/api/vulkan.api @@ -28,7 +28,7 @@ import platform "platform.api" // API version (major.minor.patch) define VERSION_MAJOR 1 define VERSION_MINOR 0 -define VERSION_PATCH 2 +define VERSION_PATCH 3 // API limits define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 @@ -75,7 +75,7 @@ define NULL_HANDLE 0 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION 5 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME "VK_KHR_win32_surface" -@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 2 +@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 1 @extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME "VK_EXT_debug_report" @@ -1299,9 +1299,9 @@ type VkFlags VkWin32SurfaceCreateFlagsKHR type VkFlags VkDebugReportFlagsEXT @extension("VK_EXT_debug_report") bitfield VkDebugReportFlagBitsEXT { - VK_DEBUG_REPORT_INFO_BIT_EXT = 0x00000001, - VK_DEBUG_REPORT_WARN_BIT_EXT = 0x00000002, - VK_DEBUG_REPORT_PERF_WARN_BIT_EXT = 0x00000004, + VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001, + VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002, + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004, VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, } @@ -5130,7 +5130,7 @@ cmd VkResult vkCreateWin32SurfaceKHR( cmd VkResult vkGetPhysicalDeviceWin32PresentationSupportKHR( VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) + physicalDeviceObject := GetPhysicalDevice(physicalDevice) return ? } diff --git a/vulkan/include/vulkan/vk_ext_debug_report.h b/vulkan/include/vulkan/vk_ext_debug_report.h deleted file mode 100644 index c391033129..0000000000 --- a/vulkan/include/vulkan/vk_ext_debug_report.h +++ /dev/null @@ -1,149 +0,0 @@ -// -// File: vk_ext_debug_report.h -// -/* - * - * Copyright (C) 2015 Valve Corporation - * Copyright (C) 2015 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS 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. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Cody Northrop <cody@lunarg.com> - * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> - * Author: Tony Barbour <tony@LunarG.com> - * - */ - -#pragma once - -#include "vulkan/vulkan.h" - -#ifdef __cplusplus -extern "C" -{ -#endif // __cplusplus - -/* -*************************************************************************************************** -* DebugReport Vulkan Extension API -*************************************************************************************************** -*/ -#define VK_EXT_debug_report 1 -VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) - -#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 2 -#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" - - -typedef enum VkDebugReportObjectTypeEXT { - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, - VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, - VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, - VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, - VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, - VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, - VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, - VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, - VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, - VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, - VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, - VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, - VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, - VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, - VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, - VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, - VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, - VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, - VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, - VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, - VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, - VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, - VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, - VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, - VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28, -} VkDebugReportObjectTypeEXT; - -typedef enum VkDebugReportErrorEXT { - VK_DEBUG_REPORT_ERROR_NONE_EXT = 0, - VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1, -} VkDebugReportErrorEXT; - -typedef enum VkDebugReportFlagBitsEXT { - VK_DEBUG_REPORT_INFO_BIT_EXT = 0x00000001, - VK_DEBUG_REPORT_WARN_BIT_EXT = 0x00000002, - VK_DEBUG_REPORT_PERF_WARN_BIT_EXT = 0x00000004, - VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, - VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, -} VkDebugReportFlagBitsEXT; -typedef VkFlags VkDebugReportFlagsEXT; - -typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, - size_t location, - int32_t messageCode, - const char* pLayerPrefix, - const char* pMessage, - void* pUserData); - - -typedef struct VkDebugReportCallbackCreateInfoEXT { - VkStructureType sType; - const void* pNext; - VkDebugReportFlagsEXT flags; - PFN_vkDebugReportCallbackEXT pfnCallback; - void* pUserData; -} VkDebugReportCallbackCreateInfoEXT; - -typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); -typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); -typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); - -#ifndef VK_NO_PROTOTYPES -VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT( - VkInstance instance, - const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDebugReportCallbackEXT* pCallback); - -VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT( - VkInstance instance, - VkDebugReportCallbackEXT callback, - const VkAllocationCallbacks* pAllocator); - -VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( - VkInstance instance, - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, - size_t location, - int32_t messageCode, - const char* pLayerPrefix, - const char* pMessage); -#endif - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus - diff --git a/vulkan/include/vulkan/vk_layer_interface.h b/vulkan/include/vulkan/vk_layer_interface.h new file mode 100644 index 0000000000..5aae51efec --- /dev/null +++ b/vulkan/include/vulkan/vk_layer_interface.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * Copyright (c) 2016 Google Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * 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. + * + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + */ +#pragma once + +#include <vulkan/vulkan.h> + +// ------------------------------------------------------------------------------------------------ +// CreateInstance and CreateDevice support structures + +typedef enum VkLayerFunction_ { + VK_LAYER_FUNCTION_LINK = 0, + VK_LAYER_FUNCTION_DEVICE = 1, + VK_LAYER_FUNCTION_INSTANCE = 2 +} 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 struct { + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO + const void* pNext; + VkLayerFunction function; + union { + VkLayerInstanceLink* pLayerInfo; + VkLayerInstanceInfo instanceInfo; + } u; +} VkLayerInstanceCreateInfo; + +typedef struct VkLayerDeviceLink_ { + struct VkLayerDeviceLink_* pNext; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr; +} VkLayerDeviceLink; + +typedef struct { + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO + const void* pNext; + VkLayerFunction function; + union { + VkLayerDeviceLink* pLayerInfo; + VkLayerDeviceInfo deviceInfo; + } u; +} VkLayerDeviceCreateInfo; diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h index 9940f856e8..cd6a71ac1a 100644 --- a/vulkan/include/vulkan/vulkan.h +++ b/vulkan/include/vulkan/vulkan.h @@ -6,7 +6,7 @@ extern "C" { #endif /* -** Copyright (c) 2015 The Khronos Group Inc. +** Copyright (c) 2015-2016 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the @@ -38,11 +38,14 @@ extern "C" { #include "vk_platform.h" #define VK_MAKE_VERSION(major, minor, patch) \ - ((major << 22) | (minor << 12) | patch) + (((major) << 22) | ((minor) << 12) | (patch)) // Vulkan API version supported by this file -#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 2) +#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 3) +#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) +#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) #define VK_NULL_HANDLE 0 @@ -3664,6 +3667,107 @@ VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR( #endif #endif /* VK_USE_PLATFORM_WIN32_KHR */ +#define VK_EXT_debug_report 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) + +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 1 +#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" + + +typedef enum VkDebugReportObjectTypeEXT { + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, + VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, + VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, + VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, + VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, + VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, + VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, + VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, + VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, + VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, + VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, + VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, + VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28, +} VkDebugReportObjectTypeEXT; + +typedef enum VkDebugReportErrorEXT { + VK_DEBUG_REPORT_ERROR_NONE_EXT = 0, + VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1, +} VkDebugReportErrorEXT; + + +typedef enum VkDebugReportFlagBitsEXT { + VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001, + VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002, + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004, + VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, + VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, +} VkDebugReportFlagBitsEXT; +typedef VkFlags VkDebugReportFlagsEXT; + +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserData); + + +typedef struct VkDebugReportCallbackCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportFlagsEXT flags; + PFN_vkDebugReportCallbackEXT pfnCallback; + void* pUserData; +} VkDebugReportCallbackCreateInfoEXT; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); +typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT( + VkInstance instance, + const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugReportCallbackEXT* pCallback); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT( + VkInstance instance, + VkDebugReportCallbackEXT callback, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( + VkInstance instance, + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage); +#endif + #ifdef __cplusplus } #endif diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk index a196a366fd..85b8ab2ea3 100644 --- a/vulkan/libvulkan/Android.mk +++ b/vulkan/libvulkan/Android.mk @@ -16,19 +16,22 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_CLANG := true +LOCAL_SANITIZE := integer + LOCAL_CFLAGS := -DLOG_TAG=\"vulkan\" \ -std=c99 -fvisibility=hidden -fstrict-aliasing \ -Weverything -Werror \ -Wno-padded \ + -Wno-switch-enum \ -Wno-undef #LOCAL_CFLAGS += -DLOG_NDEBUG=0 LOCAL_CPPFLAGS := -std=c++14 \ -fexceptions \ + -Wno-c99-extensions \ -Wno-c++98-compat-pedantic \ -Wno-exit-time-destructors \ - -Wno-c99-extensions \ - -Wno-zero-length-array \ - -Wno-global-constructors + -Wno-global-constructors \ + -Wno-zero-length-array LOCAL_C_INCLUDES := \ frameworks/native/vulkan/include \ diff --git a/vulkan/libvulkan/debug_report.cpp b/vulkan/libvulkan/debug_report.cpp index fea9f18528..41b604063b 100644 --- a/vulkan/libvulkan/debug_report.cpp +++ b/vulkan/libvulkan/debug_report.cpp @@ -23,11 +23,16 @@ VkResult DebugReportCallbackList::CreateCallback( const VkDebugReportCallbackCreateInfoEXT* create_info, const VkAllocationCallbacks* allocator, VkDebugReportCallbackEXT* callback) { - VkDebugReportCallbackEXT driver_callback; - VkResult result = GetDriverDispatch(instance).CreateDebugReportCallbackEXT( - GetDriverInstance(instance), create_info, allocator, &driver_callback); - if (result != VK_SUCCESS) - return result; + VkDebugReportCallbackEXT driver_callback = VK_NULL_HANDLE; + + if (GetDriverDispatch(instance).CreateDebugReportCallbackEXT) { + VkResult result = + GetDriverDispatch(instance).CreateDebugReportCallbackEXT( + GetDriverInstance(instance), create_info, allocator, + &driver_callback); + if (result != VK_SUCCESS) + return result; + } const VkAllocationCallbacks* alloc = allocator ? allocator : GetAllocator(instance); @@ -35,8 +40,10 @@ VkResult DebugReportCallbackList::CreateCallback( alloc->pfnAllocation(alloc->pUserData, sizeof(Node), alignof(Node), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!mem) { - GetDriverDispatch(instance).DestroyDebugReportCallbackEXT( - GetDriverInstance(instance), driver_callback, allocator); + if (GetDriverDispatch(instance).DestroyDebugReportCallbackEXT) { + GetDriverDispatch(instance).DestroyDebugReportCallbackEXT( + GetDriverInstance(instance), driver_callback, allocator); + } return VK_ERROR_OUT_OF_HOST_MEMORY; } @@ -61,8 +68,10 @@ void DebugReportCallbackList::DestroyCallback( prev->next = node->next; lock.unlock(); - GetDriverDispatch(instance).DestroyDebugReportCallbackEXT( - GetDriverInstance(instance), node->driver_callback, allocator); + if (GetDriverDispatch(instance).DestroyDebugReportCallbackEXT) { + GetDriverDispatch(instance).DestroyDebugReportCallbackEXT( + GetDriverInstance(instance), node->driver_callback, allocator); + } const VkAllocationCallbacks* alloc = allocator ? allocator : GetAllocator(instance); @@ -112,9 +121,11 @@ void DebugReportMessageEXT_Bottom(VkInstance instance, int32_t message_code, const char* layer_prefix, const char* message) { - GetDriverDispatch(instance).DebugReportMessageEXT( - GetDriverInstance(instance), flags, object_type, object, location, - message_code, layer_prefix, message); + if (GetDriverDispatch(instance).DebugReportMessageEXT) { + GetDriverDispatch(instance).DebugReportMessageEXT( + GetDriverInstance(instance), flags, object_type, object, location, + message_code, layer_prefix, message); + } GetDebugReportCallbacks(instance).Message(flags, object_type, object, location, message_code, layer_prefix, message); diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h index 5bce24085f..c6f7570152 100644 --- a/vulkan/libvulkan/debug_report.h +++ b/vulkan/libvulkan/debug_report.h @@ -18,7 +18,6 @@ #define LIBVULKAN_DEBUG_REPORT_H 1 #include <shared_mutex> -#include <vulkan/vk_ext_debug_report.h> namespace vulkan { diff --git a/vulkan/libvulkan/dispatch.tmpl b/vulkan/libvulkan/dispatch.tmpl index 054a2357a3..0a0338ed71 100644 --- a/vulkan/libvulkan/dispatch.tmpl +++ b/vulkan/libvulkan/dispatch.tmpl @@ -46,7 +46,6 @@ ¶ #define VK_USE_PLATFORM_ANDROID_KHR #include <vulkan/vk_android_native_buffer.h> -#include <vulkan/vk_ext_debug_report.h> #include <vulkan/vulkan.h> ¶ namespace vulkan { @@ -465,7 +464,7 @@ bool LoadDriverDispatchTable(VkInstance instance, {{define "IsInstanceDispatched"}} {{AssertType $ "Function"}} {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}} - {{if (ne $.Name "vkGetInstanceProcAddr")}}true{{end}} + {{if and (ne $.Name "vkEnumerateDeviceLayerProperties") (ne $.Name "vkGetInstanceProcAddr")}}true{{end}} {{end}} {{end}} @@ -560,6 +559,9 @@ bool LoadDriverDispatchTable(VkInstance instance, objects */}} {{else if eq $.Name "vkGetDeviceQueue"}}true {{else if eq $.Name "vkAllocateCommandBuffers"}}true + {{else if eq $.Name "vkCreateDevice"}}true + {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true + {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true {{/* vkDestroy for dispatchable objects needs to handle VK_NULL_HANDLE; trying to dispatch through that would crash. */}} diff --git a/vulkan/libvulkan/dispatch_gen.cpp b/vulkan/libvulkan/dispatch_gen.cpp index 0d2d605c71..b41efb8ee2 100644 --- a/vulkan/libvulkan/dispatch_gen.cpp +++ b/vulkan/libvulkan/dispatch_gen.cpp @@ -211,9 +211,12 @@ const NameProc kLoaderGlobalProcs[] = { const NameProc kLoaderTopProcs[] = { // clang-format off {"vkAllocateCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateCommandBuffers>(AllocateCommandBuffers_Top))}, + {"vkCreateDevice", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateDevice>(CreateDevice_Top))}, {"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateInstance>(CreateInstance_Top))}, {"vkDestroyDevice", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyDevice>(DestroyDevice_Top))}, {"vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyInstance>(DestroyInstance_Top))}, + {"vkEnumerateDeviceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateDeviceExtensionProperties>(EnumerateDeviceExtensionProperties_Top))}, + {"vkEnumerateDeviceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateDeviceLayerProperties>(EnumerateDeviceLayerProperties_Top))}, {"vkEnumerateInstanceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateInstanceExtensionProperties>(EnumerateInstanceExtensionProperties_Top))}, {"vkEnumerateInstanceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateInstanceLayerProperties>(EnumerateInstanceLayerProperties_Top))}, {"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr_Top))}, @@ -295,7 +298,6 @@ const NameOffset kInstanceDispatchOffsets[] = { {"vkDestroyInstance", offsetof(InstanceDispatchTable, DestroyInstance)}, {"vkDestroySurfaceKHR", offsetof(InstanceDispatchTable, DestroySurfaceKHR)}, {"vkEnumerateDeviceExtensionProperties", offsetof(InstanceDispatchTable, EnumerateDeviceExtensionProperties)}, - {"vkEnumerateDeviceLayerProperties", offsetof(InstanceDispatchTable, EnumerateDeviceLayerProperties)}, {"vkEnumeratePhysicalDevices", offsetof(InstanceDispatchTable, EnumeratePhysicalDevices)}, {"vkGetPhysicalDeviceFeatures", offsetof(InstanceDispatchTable, GetPhysicalDeviceFeatures)}, {"vkGetPhysicalDeviceFormatProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceFormatProperties)}, @@ -521,11 +523,6 @@ bool LoadInstanceDispatchTable(VkInstance instance, ALOGE("missing instance proc: %s", "vkCreateDevice"); success = false; } - dispatch.EnumerateDeviceLayerProperties = reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>(get_proc_addr(instance, "vkEnumerateDeviceLayerProperties")); - if (UNLIKELY(!dispatch.EnumerateDeviceLayerProperties)) { - ALOGE("missing instance proc: %s", "vkEnumerateDeviceLayerProperties"); - success = false; - } dispatch.EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(get_proc_addr(instance, "vkEnumerateDeviceExtensionProperties")); if (UNLIKELY(!dispatch.EnumerateDeviceExtensionProperties)) { ALOGE("missing instance proc: %s", "vkEnumerateDeviceExtensionProperties"); @@ -1270,11 +1267,6 @@ bool LoadDriverDispatchTable(VkInstance instance, ALOGE("missing driver proc: %s", "vkCreateDevice"); success = false; } - dispatch.EnumerateDeviceLayerProperties = reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>(get_proc_addr(instance, "vkEnumerateDeviceLayerProperties")); - if (UNLIKELY(!dispatch.EnumerateDeviceLayerProperties)) { - ALOGE("missing driver proc: %s", "vkEnumerateDeviceLayerProperties"); - success = false; - } dispatch.EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(get_proc_addr(instance, "vkEnumerateDeviceExtensionProperties")); if (UNLIKELY(!dispatch.EnumerateDeviceExtensionProperties)) { ALOGE("missing driver proc: %s", "vkEnumerateDeviceExtensionProperties"); @@ -1401,7 +1393,7 @@ VKAPI_ATTR VkResult vkGetPhysicalDeviceImageFormatProperties(VkPhysicalDevice ph __attribute__((visibility("default"))) VKAPI_ATTR VkResult vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { - return GetDispatchTable(physicalDevice).CreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice); + return CreateDevice_Top(physicalDevice, pCreateInfo, pAllocator, pDevice); } __attribute__((visibility("default"))) @@ -1421,12 +1413,12 @@ VKAPI_ATTR VkResult vkEnumerateInstanceExtensionProperties(const char* pLayerNam __attribute__((visibility("default"))) VKAPI_ATTR VkResult vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties) { - return GetDispatchTable(physicalDevice).EnumerateDeviceLayerProperties(physicalDevice, pPropertyCount, pProperties); + return EnumerateDeviceLayerProperties_Top(physicalDevice, pPropertyCount, pProperties); } __attribute__((visibility("default"))) VKAPI_ATTR VkResult vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) { - return GetDispatchTable(physicalDevice).EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pPropertyCount, pProperties); + return EnumerateDeviceExtensionProperties_Top(physicalDevice, pLayerName, pPropertyCount, pProperties); } __attribute__((visibility("default"))) diff --git a/vulkan/libvulkan/dispatch_gen.h b/vulkan/libvulkan/dispatch_gen.h index 14a4b95d1d..cef4ccfbbc 100644 --- a/vulkan/libvulkan/dispatch_gen.h +++ b/vulkan/libvulkan/dispatch_gen.h @@ -18,7 +18,6 @@ #define VK_USE_PLATFORM_ANDROID_KHR #include <vulkan/vk_android_native_buffer.h> -#include <vulkan/vk_ext_debug_report.h> #include <vulkan/vulkan.h> namespace vulkan { @@ -34,7 +33,6 @@ struct InstanceDispatchTable { PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties; PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties; PFN_vkCreateDevice CreateDevice; - PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties; PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties; PFN_vkDestroySurfaceKHR DestroySurfaceKHR; @@ -190,7 +188,6 @@ struct DriverDispatchTable { PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties; PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties; PFN_vkCreateDevice CreateDevice; - PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties; PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties; PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp index 287e69b24a..e77952a09d 100644 --- a/vulkan/libvulkan/layers_extensions.cpp +++ b/vulkan/libvulkan/layers_extensions.cpp @@ -394,6 +394,14 @@ LayerRef::~LayerRef() { } } +const char* LayerRef::GetName() { + return layer_->properties.layerName; +} + +uint32_t LayerRef::GetSpecVersion() { + return layer_->properties.specVersion; +} + LayerRef::LayerRef(LayerRef&& other) : layer_(std::move(other.layer_)) { other.layer_ = nullptr; } diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp index 939f3b919f..df088b3c00 100644 --- a/vulkan/libvulkan/loader.cpp +++ b/vulkan/libvulkan/loader.cpp @@ -36,6 +36,7 @@ #include <hardware/hwvulkan.h> #include <log/log.h> #include <vulkan/vulkan_loader_data.h> +#include <vulkan/vk_layer_interface.h> // #define ENABLE_ALLOC_CALLSTACKS 1 #if ENABLE_ALLOC_CALLSTACKS @@ -59,16 +60,6 @@ static const uint32_t kMaxPhysicalDevices = 4; namespace { -// These definitions are taken from the LunarG Vulkan Loader. They are used to -// enforce compatability between the Loader and Layers. -typedef void* (*PFN_vkGetProcAddr)(void* obj, const char* pName); - -typedef struct VkLayerLinkedListElem_ { - PFN_vkGetProcAddr get_proc_addr; - void* next_element; - void* base_object; -} VkLayerLinkedListElem; - // ---------------------------------------------------------------------------- // Standard-library allocator that delegates to VkAllocationCallbacks. @@ -463,12 +454,6 @@ template <class TCreateInfo> bool AddExtensionToCreateInfo(TCreateInfo& local_create_info, const char* extension_name, const VkAllocationCallbacks* alloc) { - for (uint32_t i = 0; i < local_create_info.enabledExtensionCount; ++i) { - if (!strcmp(extension_name, - local_create_info.ppEnabledExtensionNames[i])) { - return false; - } - } uint32_t extension_count = local_create_info.enabledExtensionCount; local_create_info.enabledExtensionCount++; void* mem = alloc->pfnAllocation( @@ -511,7 +496,7 @@ VkBool32 LogDebugMessageCallback(VkDebugReportFlagsEXT flags, void* /*user_data*/) { if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { ALOGE("[%s] Code %d : %s", layer_prefix, message_code, message); - } else if (flags & VK_DEBUG_REPORT_WARN_BIT_EXT) { + } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { ALOGW("[%s] Code %d : %s", layer_prefix, message_code, message); } return false; @@ -521,6 +506,48 @@ VkResult Noop() { return VK_SUCCESS; } +/* + * This function will return the pNext pointer of any + * CreateInfo extensions that are not loader extensions. + * This is used to skip past the loader extensions prepended + * to the list during CreateInstance and CreateDevice. + */ +void* StripCreateExtensions(const void* pNext) { + VkLayerInstanceCreateInfo* create_info = + const_cast<VkLayerInstanceCreateInfo*>( + static_cast<const VkLayerInstanceCreateInfo*>(pNext)); + + while ( + create_info && + (create_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO || + create_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO)) { + create_info = const_cast<VkLayerInstanceCreateInfo*>( + static_cast<const VkLayerInstanceCreateInfo*>(create_info->pNext)); + } + + return create_info; +} + +// Clean up and deallocate an Instance; called from both the failure paths in +// CreateInstance_Top as well as from DestroyInstance_Top. This function does +// not call down the dispatch chain; that should be done before calling this +// function, iff the lower vkCreateInstance call has been made and returned +// successfully. +void DestroyInstance(Instance* instance, + const VkAllocationCallbacks* allocator) { + if (instance->message) { + PFN_vkDestroyDebugReportCallbackEXT destroy_debug_report_callback; + destroy_debug_report_callback = + reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>( + GetInstanceProcAddr_Top(instance->handle, + "vkDestroyDebugReportCallbackEXT")); + destroy_debug_report_callback(instance->handle, instance->message, + allocator); + } + instance->~Instance(); + allocator->pfnFree(allocator->pUserData, instance); +} + } // anonymous namespace namespace vulkan { @@ -532,9 +559,23 @@ namespace vulkan { VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkInstance* vkinstance) { - Instance& instance = GetDispatchParent(*vkinstance); VkResult result; + VkLayerInstanceCreateInfo* chain_info = + const_cast<VkLayerInstanceCreateInfo*>( + static_cast<const VkLayerInstanceCreateInfo*>(create_info->pNext)); + while ( + chain_info && + !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO && + chain_info->function == VK_LAYER_FUNCTION_INSTANCE)) { + chain_info = const_cast<VkLayerInstanceCreateInfo*>( + static_cast<const VkLayerInstanceCreateInfo*>(chain_info->pNext)); + } + ALOG_ASSERT(chain_info != nullptr, "Missing initialization chain info!"); + + Instance& instance = GetDispatchParent( + static_cast<VkInstance>(chain_info->u.instanceInfo.instance_info)); + // Check that all enabled extensions are supported InstanceExtensionSet enabled_extensions; uint32_t num_driver_extensions = 0; @@ -547,11 +588,14 @@ VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info, enabled_extensions.set(id); continue; } - if (id == kKHR_surface || id == kKHR_android_surface || - id == kEXT_debug_report) { + if (id == kKHR_surface || id == kKHR_android_surface) { enabled_extensions.set(id); continue; } + // The loader natively supports debug report. + if (id == kEXT_debug_report) { + continue; + } } bool supported = false; for (const auto& layer : instance.active_layers) { @@ -569,6 +613,7 @@ VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info, } VkInstanceCreateInfo driver_create_info = *create_info; + driver_create_info.pNext = StripCreateExtensions(create_info->pNext); driver_create_info.enabledLayerCount = 0; driver_create_info.ppEnabledLayerNames = nullptr; driver_create_info.enabledExtensionCount = 0; @@ -602,15 +647,14 @@ VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info, hwvulkan_dispatch_t* drv_dispatch = reinterpret_cast<hwvulkan_dispatch_t*>(instance.drv.instance); - if (drv_dispatch->magic == HWVULKAN_DISPATCH_MAGIC) { - // Skip setting drv_dispatch->vtbl, since we never call through it; - // we go through instance.drv.dispatch instead. - } else { + if (drv_dispatch->magic != HWVULKAN_DISPATCH_MAGIC) { ALOGE("invalid VkInstance dispatch magic: 0x%" PRIxPTR, drv_dispatch->magic); DestroyInstance_Bottom(instance.handle, allocator); return VK_ERROR_INITIALIZATION_FAILED; } + // Skip setting drv_dispatch->vtbl, since we never call through it; + // we go through instance.drv.dispatch instead. if (!LoadDriverDispatchTable(instance.drv.instance, g_hwdevice->GetInstanceProcAddr, @@ -689,6 +733,8 @@ VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info, instance.drv.num_physical_devices = num_physical_devices; instance.num_physical_devices = instance.drv.num_physical_devices; + *vkinstance = instance.handle; + return VK_SUCCESS; } @@ -776,55 +822,25 @@ void GetPhysicalDeviceSparseImageFormatProperties_Bottom( properties); } +// This is a no-op, the Top function returns the aggregate layer property +// data. This is to keep the dispatch generator happy. VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties_Bottom( - VkPhysicalDevice gpu, - const char* layer_name, - uint32_t* properties_count, - VkExtensionProperties* properties) { - const VkExtensionProperties* extensions = nullptr; - uint32_t num_extensions = 0; - if (layer_name) { - GetDeviceLayerExtensions(layer_name, &extensions, &num_extensions); - } else { - Instance& instance = GetDispatchParent(gpu); - size_t gpu_idx = 0; - while (instance.physical_devices[gpu_idx] != gpu) - gpu_idx++; - const DeviceExtensionSet driver_extensions = - instance.physical_device_driver_extensions[gpu_idx]; - - // We only support VK_KHR_swapchain if the GPU supports - // VK_ANDROID_native_buffer - VkExtensionProperties* available = static_cast<VkExtensionProperties*>( - alloca(kDeviceExtensionCount * sizeof(VkExtensionProperties))); - if (driver_extensions[kANDROID_native_buffer]) { - available[num_extensions++] = VkExtensionProperties{ - VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_SPEC_VERSION}; - } - - // TODO(jessehall): We need to also enumerate extensions supported by - // implicitly-enabled layers. Currently we don't have that list of - // layers until instance creation. - extensions = available; - } - - if (!properties || *properties_count > num_extensions) - *properties_count = num_extensions; - if (properties) - std::copy(extensions, extensions + *properties_count, properties); - return *properties_count < num_extensions ? VK_INCOMPLETE : VK_SUCCESS; + VkPhysicalDevice /*pdev*/, + const char* /*layer_name*/, + uint32_t* /*properties_count*/, + VkExtensionProperties* /*properties*/) { + return VK_SUCCESS; } +// This is a no-op, the Top function returns the aggregate layer property +// data. This is to keep the dispatch generator happy. VKAPI_ATTR -VkResult EnumerateDeviceLayerProperties_Bottom(VkPhysicalDevice /*pdev*/, - uint32_t* properties_count, - VkLayerProperties* properties) { - uint32_t layer_count = - EnumerateDeviceLayers(properties ? *properties_count : 0, properties); - if (!properties || *properties_count > layer_count) - *properties_count = layer_count; - return *properties_count < layer_count ? VK_INCOMPLETE : VK_SUCCESS; +VkResult EnumerateDeviceLayerProperties_Bottom( + VkPhysicalDevice /*pdev*/, + uint32_t* /*properties_count*/, + VkLayerProperties* /*properties*/) { + return VK_SUCCESS; } VKAPI_ATTR @@ -832,31 +848,28 @@ VkResult CreateDevice_Bottom(VkPhysicalDevice gpu, const VkDeviceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkDevice* device_out) { - Instance& instance = GetDispatchParent(gpu); - VkResult result; - - // FIXME(jessehall): We don't have good conventions or infrastructure yet to - // do better than just using the instance allocator and scope for - // everything. See b/26732122. - if (true /*!allocator*/) - allocator = instance.alloc; - - void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Device), - alignof(Device), - VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); - if (!mem) - return VK_ERROR_OUT_OF_HOST_MEMORY; - Device* device = new (mem) Device(&instance); - - result = ActivateAllLayers(create_info, &instance, device); - if (result != VK_SUCCESS) { - DestroyDevice(device); - return result; + VkLayerDeviceCreateInfo* chain_info = const_cast<VkLayerDeviceCreateInfo*>( + static_cast<const VkLayerDeviceCreateInfo*>(create_info->pNext)); + while (chain_info && + !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO && + chain_info->function == VK_LAYER_FUNCTION_DEVICE)) { + chain_info = const_cast<VkLayerDeviceCreateInfo*>( + static_cast<const VkLayerDeviceCreateInfo*>(chain_info->pNext)); } + ALOG_ASSERT(chain_info != nullptr, "Missing initialization chain info!"); + Instance& instance = GetDispatchParent(gpu); size_t gpu_idx = 0; while (instance.physical_devices[gpu_idx] != gpu) gpu_idx++; + Device* device = static_cast<Device*>(chain_info->u.deviceInfo.device_info); + PFN_vkGetInstanceProcAddr get_instance_proc_addr = + chain_info->u.deviceInfo.pfnNextGetInstanceProcAddr; + + VkDeviceCreateInfo driver_create_info = *create_info; + driver_create_info.pNext = StripCreateExtensions(create_info->pNext); + driver_create_info.enabledLayerCount = 0; + driver_create_info.ppEnabledLayerNames = nullptr; uint32_t num_driver_extensions = 0; const char** driver_extensions = static_cast<const char**>( @@ -869,6 +882,8 @@ VkResult CreateDevice_Bottom(VkPhysicalDevice gpu, driver_extensions[num_driver_extensions++] = name; continue; } + // Add the VK_ANDROID_native_buffer extension to the list iff + // the VK_KHR_swapchain extension was requested if (id == kKHR_swapchain && instance.physical_device_driver_extensions [gpu_idx][kANDROID_native_buffer]) { @@ -887,28 +902,17 @@ VkResult CreateDevice_Bottom(VkPhysicalDevice gpu, "requested device extension '%s' not supported by loader, " "driver, or any active layers", name); - DestroyDevice(device); return VK_ERROR_EXTENSION_NOT_PRESENT; } } - VkDeviceCreateInfo driver_create_info = *create_info; - driver_create_info.enabledLayerCount = 0; - driver_create_info.ppEnabledLayerNames = nullptr; - // TODO(jessehall): As soon as we enumerate device extensions supported by - // the driver, we need to filter the requested extension list to those - // supported by the driver here. Also, add the VK_ANDROID_native_buffer - // extension to the list iff the VK_KHR_swapchain extension was requested, - // instead of adding it unconditionally like we do now. driver_create_info.enabledExtensionCount = num_driver_extensions; driver_create_info.ppEnabledExtensionNames = driver_extensions; - VkDevice drv_device; - result = instance.drv.dispatch.CreateDevice(gpu, &driver_create_info, - allocator, &drv_device); + VkResult result = instance.drv.dispatch.CreateDevice( + gpu, &driver_create_info, allocator, &drv_device); if (result != VK_SUCCESS) { - DestroyDevice(device); - return result; + return VK_ERROR_INITIALIZATION_FAILED; } hwvulkan_dispatch_t* drv_dispatch = @@ -921,68 +925,15 @@ VkResult CreateDevice_Bottom(VkPhysicalDevice gpu, instance.drv.dispatch.GetDeviceProcAddr(drv_device, "vkDestroyDevice")); destroy_device(drv_device, allocator); - DestroyDevice(device); return VK_ERROR_INITIALIZATION_FAILED; } + + // Set dispatch table for newly created Device + // CreateDevice_Top will fill in the details drv_dispatch->vtbl = &device->dispatch; device->get_device_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>( instance.drv.dispatch.GetDeviceProcAddr(drv_device, "vkGetDeviceProcAddr")); - - void* base_object = static_cast<void*>(drv_device); - void* next_object = base_object; - VkLayerLinkedListElem* next_element; - PFN_vkGetDeviceProcAddr next_get_proc_addr = GetDeviceProcAddr_Bottom; - Vector<VkLayerLinkedListElem> elem_list( - CallbackAllocator<VkLayerLinkedListElem>(instance.alloc)); - try { - elem_list.resize(device->active_layers.size()); - } catch (std::bad_alloc&) { - ALOGE("device creation failed: out of memory"); - PFN_vkDestroyDevice destroy_device = - reinterpret_cast<PFN_vkDestroyDevice>( - instance.drv.dispatch.GetDeviceProcAddr(drv_device, - "vkDestroyDevice")); - destroy_device(drv_device, allocator); - DestroyDevice(device); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } - - for (size_t i = elem_list.size(); i > 0; i--) { - size_t idx = i - 1; - next_element = &elem_list[idx]; - next_element->get_proc_addr = - reinterpret_cast<PFN_vkGetProcAddr>(next_get_proc_addr); - next_element->base_object = base_object; - next_element->next_element = next_object; - next_object = static_cast<void*>(next_element); - - next_get_proc_addr = device->active_layers[idx].GetGetDeviceProcAddr(); - if (!next_get_proc_addr) { - next_object = next_element->next_element; - next_get_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>( - next_element->get_proc_addr); - } - } - - // This is the magic call that initializes all the layer devices and - // allows them to create their device_handle -> device_data mapping. - next_get_proc_addr(static_cast<VkDevice>(next_object), - "vkGetDeviceProcAddr"); - - // We must create all the layer devices *before* retrieving the device - // procaddrs, so that the layers know which extensions are enabled and - // therefore which functions to return procaddrs for. - PFN_vkCreateDevice create_device = reinterpret_cast<PFN_vkCreateDevice>( - next_get_proc_addr(drv_device, "vkCreateDevice")); - create_device(gpu, create_info, allocator, &drv_device); - - if (!LoadDeviceDispatchTable(static_cast<VkDevice>(base_object), - next_get_proc_addr, device->dispatch)) { - DestroyDevice(device); - return VK_ERROR_INITIALIZATION_FAILED; - } - *device_out = drv_device; return VK_SUCCESS; } @@ -997,34 +948,14 @@ void DestroyInstance_Bottom(VkInstance vkinstance, if (instance.drv.instance != VK_NULL_HANDLE && instance.drv.dispatch.DestroyInstance) { instance.drv.dispatch.DestroyInstance(instance.drv.instance, allocator); + instance.drv.instance = VK_NULL_HANDLE; } - if (instance.message) { - PFN_vkDestroyDebugReportCallbackEXT destroy_debug_report_callback; - destroy_debug_report_callback = - reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>( - vkGetInstanceProcAddr(vkinstance, - "vkDestroyDebugReportCallbackEXT")); - destroy_debug_report_callback(vkinstance, instance.message, allocator); - } - instance.active_layers.clear(); - const VkAllocationCallbacks* alloc = instance.alloc; - instance.~Instance(); - alloc->pfnFree(alloc->pUserData, &instance); } PFN_vkVoidFunction GetDeviceProcAddr_Bottom(VkDevice vkdevice, const char* name) { if (strcmp(name, "vkCreateDevice") == 0) { - // TODO(jessehall): Blegh, having this here is disgusting. The current - // layer init process can't call through the instance dispatch table's - // vkCreateDevice, because that goes through the instance layers rather - // than through the device layers. So we need to be able to get the - // vkCreateDevice pointer through the *device* layer chain. - // - // Because we've already created the driver device before calling - // through the layer vkCreateDevice functions, the loader bottom proc - // is a no-op. - return reinterpret_cast<PFN_vkVoidFunction>(Noop); + return reinterpret_cast<PFN_vkVoidFunction>(CreateDevice_Bottom); } // VK_ANDROID_native_buffer should be hidden from applications and layers. @@ -1094,6 +1025,51 @@ VkResult EnumerateInstanceLayerProperties_Top(uint32_t* properties_count, return *properties_count < layer_count ? VK_INCOMPLETE : VK_SUCCESS; } +VKAPI_ATTR +VkResult EnumerateDeviceExtensionProperties_Top( + VkPhysicalDevice gpu, + const char* layer_name, + uint32_t* properties_count, + VkExtensionProperties* properties) { + const VkExtensionProperties* extensions = nullptr; + uint32_t num_extensions = 0; + + ALOGV("EnumerateDeviceExtensionProperties_Top:"); + if (layer_name) { + ALOGV(" layer %s", layer_name); + GetDeviceLayerExtensions(layer_name, &extensions, &num_extensions); + } else { + ALOGV(" no layer"); + Instance& instance = GetDispatchParent(gpu); + size_t gpu_idx = 0; + while (instance.physical_devices[gpu_idx] != gpu) + gpu_idx++; + const DeviceExtensionSet driver_extensions = + instance.physical_device_driver_extensions[gpu_idx]; + + // We only support VK_KHR_swapchain if the GPU supports + // VK_ANDROID_native_buffer + VkExtensionProperties* available = static_cast<VkExtensionProperties*>( + alloca(kDeviceExtensionCount * sizeof(VkExtensionProperties))); + if (driver_extensions[kANDROID_native_buffer]) { + available[num_extensions++] = VkExtensionProperties{ + VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_SPEC_VERSION}; + } + + // TODO(jessehall): We need to also enumerate extensions supported by + // implicitly-enabled layers. Currently we don't have that list of + // layers until instance creation. + extensions = available; + } + + ALOGV(" num: %d, extensions: %p", num_extensions, extensions); + if (!properties || *properties_count > num_extensions) + *properties_count = num_extensions; + if (properties) + std::copy(extensions, extensions + *properties_count, properties); + return *properties_count < num_extensions ? VK_INCOMPLETE : VK_SUCCESS; +} + VkResult CreateInstance_Top(const VkInstanceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkInstance* instance_out) { @@ -1117,95 +1093,111 @@ VkResult CreateInstance_Top(const VkInstanceCreateInfo* create_info, result = ActivateAllLayers(create_info, instance, instance); if (result != VK_SUCCESS) { - DestroyInstance_Bottom(instance->handle, allocator); + DestroyInstance(instance, allocator); return result; } - void* base_object = static_cast<void*>(instance->handle); - void* next_object = base_object; - VkLayerLinkedListElem* next_element; - PFN_vkGetInstanceProcAddr next_get_proc_addr = GetInstanceProcAddr_Bottom; - Vector<VkLayerLinkedListElem> elem_list( - CallbackAllocator<VkLayerLinkedListElem>(instance->alloc)); - try { - elem_list.resize(instance->active_layers.size()); - } catch (std::bad_alloc&) { - ALOGE("instance creation failed: out of memory"); - DestroyInstance_Bottom(instance->handle, allocator); - return VK_ERROR_OUT_OF_HOST_MEMORY; - } + uint32_t activated_layers = 0; + VkLayerInstanceCreateInfo chain_info; + VkLayerInstanceLink* layer_instance_link_info = nullptr; + PFN_vkGetInstanceProcAddr next_gipa = GetInstanceProcAddr_Bottom; + VkInstance local_instance = nullptr; + + if (instance->active_layers.size() > 0) { + chain_info.u.pLayerInfo = nullptr; + chain_info.pNext = create_info->pNext; + chain_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO; + chain_info.function = VK_LAYER_FUNCTION_LINK; + local_create_info.pNext = &chain_info; + + layer_instance_link_info = static_cast<VkLayerInstanceLink*>(alloca( + sizeof(VkLayerInstanceLink) * instance->active_layers.size())); + if (!layer_instance_link_info) { + ALOGE("Failed to alloc Instance objects for layers"); + DestroyInstance(instance, allocator); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } - for (size_t i = elem_list.size(); i > 0; i--) { - size_t idx = i - 1; - next_element = &elem_list[idx]; - next_element->get_proc_addr = - reinterpret_cast<PFN_vkGetProcAddr>(next_get_proc_addr); - next_element->base_object = base_object; - next_element->next_element = next_object; - next_object = static_cast<void*>(next_element); - - next_get_proc_addr = - instance->active_layers[idx].GetGetInstanceProcAddr(); - if (!next_get_proc_addr) { - next_object = next_element->next_element; - next_get_proc_addr = reinterpret_cast<PFN_vkGetInstanceProcAddr>( - next_element->get_proc_addr); + /* Create instance chain of enabled layers */ + for (auto rit = instance->active_layers.rbegin(); + rit != instance->active_layers.rend(); ++rit) { + LayerRef& layer = *rit; + layer_instance_link_info[activated_layers].pNext = + chain_info.u.pLayerInfo; + layer_instance_link_info[activated_layers] + .pfnNextGetInstanceProcAddr = next_gipa; + chain_info.u.pLayerInfo = + &layer_instance_link_info[activated_layers]; + next_gipa = layer.GetGetInstanceProcAddr(); + + ALOGV("Insert instance layer %s (v%u)", layer.GetName(), + layer.GetSpecVersion()); + + activated_layers++; } } - // This is the magic call that initializes all the layer instances and - // allows them to create their instance_handle -> instance_data mapping. - next_get_proc_addr(static_cast<VkInstance>(next_object), - "vkGetInstanceProcAddr"); - - if (!LoadInstanceDispatchTable(static_cast<VkInstance>(base_object), - next_get_proc_addr, instance->dispatch)) { - DestroyInstance_Bottom(instance->handle, allocator); + PFN_vkCreateInstance create_instance = + reinterpret_cast<PFN_vkCreateInstance>( + next_gipa(VK_NULL_HANDLE, "vkCreateInstance")); + if (!create_instance) { + DestroyInstance(instance, allocator); return VK_ERROR_INITIALIZATION_FAILED; } + VkLayerInstanceCreateInfo instance_create_info; + + instance_create_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO; + instance_create_info.function = VK_LAYER_FUNCTION_INSTANCE; + + instance_create_info.u.instanceInfo.instance_info = instance; + instance_create_info.u.instanceInfo.pfnNextGetInstanceProcAddr = next_gipa; + + instance_create_info.pNext = local_create_info.pNext; + local_create_info.pNext = &instance_create_info; // Force enable callback extension if required bool enable_callback = false; - bool enable_logging = false; if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { enable_callback = property_get_bool("debug.vulkan.enable_callback", false); - enable_logging = enable_callback; if (enable_callback) { enable_callback = AddExtensionToCreateInfo( local_create_info, "VK_EXT_debug_report", instance->alloc); } } - VkInstance handle = instance->handle; - PFN_vkCreateInstance create_instance = - reinterpret_cast<PFN_vkCreateInstance>( - next_get_proc_addr(instance->handle, "vkCreateInstance")); - result = create_instance(create_info, allocator, &handle); + result = create_instance(&local_create_info, allocator, &local_instance); if (enable_callback) - FreeAllocatedCreateInfo(local_create_info, instance->alloc); - if (result >= 0) { - *instance_out = instance->handle; - } else { - // For every layer, including the loader top and bottom layers: - // - If a call to the next CreateInstance fails, the layer must clean - // up anything it has successfully done so far, and propagate the - // error upwards. - // - If a layer successfully calls the next layer's CreateInstance, and - // afterwards must fail for some reason, it must call the next layer's - // DestroyInstance before returning. - // - The layer must not call the next layer's DestroyInstance if that - // layer's CreateInstance wasn't called, or returned failure. - - // On failure, CreateInstance_Bottom frees the instance struct, so it's - // already gone at this point. Nothing to do. + FreeAllocatedCreateInfo(local_create_info, allocator); + if (result != VK_SUCCESS) { + DestroyInstance(instance, allocator); + return result; + } + + const InstanceDispatchTable& instance_dispatch = + GetDispatchTable(local_instance); + if (!LoadInstanceDispatchTable( + local_instance, next_gipa, + const_cast<InstanceDispatchTable&>(instance_dispatch))) { + ALOGV("Failed to initialize instance dispatch table"); + PFN_vkDestroyInstance destroy_instance = + reinterpret_cast<PFN_vkDestroyInstance>( + next_gipa(VK_NULL_HANDLE, "vkDestroyInstance")); + if (!destroy_instance) { + ALOGD("Loader unable to find DestroyInstance"); + return VK_ERROR_INITIALIZATION_FAILED; + } + destroy_instance(local_instance, allocator); + DestroyInstance(instance, allocator); + return VK_ERROR_INITIALIZATION_FAILED; } + *instance_out = local_instance; - if (enable_logging) { + if (enable_callback) { const VkDebugReportCallbackCreateInfoEXT callback_create_info = { .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT, .flags = - VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARN_BIT_EXT, + VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, .pfnCallback = LogDebugMessageCallback, }; PFN_vkCreateDebugReportCallbackEXT create_debug_report_callback = @@ -1240,11 +1232,150 @@ PFN_vkVoidFunction GetInstanceProcAddr_Top(VkInstance vkinstance, return GetLoaderExportProcAddr(name); } -void DestroyInstance_Top(VkInstance instance, +void DestroyInstance_Top(VkInstance vkinstance, const VkAllocationCallbacks* allocator) { - if (!instance) + if (!vkinstance) return; - GetDispatchTable(instance).DestroyInstance(instance, allocator); + if (!allocator) + allocator = &kDefaultAllocCallbacks; + GetDispatchTable(vkinstance).DestroyInstance(vkinstance, allocator); + DestroyInstance(&(GetDispatchParent(vkinstance)), allocator); +} + +VKAPI_ATTR +VkResult EnumerateDeviceLayerProperties_Top(VkPhysicalDevice /*pdev*/, + uint32_t* properties_count, + VkLayerProperties* properties) { + uint32_t layer_count = + EnumerateDeviceLayers(properties ? *properties_count : 0, properties); + if (!properties || *properties_count > layer_count) + *properties_count = layer_count; + return *properties_count < layer_count ? VK_INCOMPLETE : VK_SUCCESS; +} + +VKAPI_ATTR +VkResult CreateDevice_Top(VkPhysicalDevice gpu, + const VkDeviceCreateInfo* create_info, + const VkAllocationCallbacks* allocator, + VkDevice* device_out) { + Instance& instance = GetDispatchParent(gpu); + VkResult result; + + // FIXME(jessehall): We don't have good conventions or infrastructure yet to + // do better than just using the instance allocator and scope for + // everything. See b/26732122. + if (true /*!allocator*/) + allocator = instance.alloc; + + void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Device), + alignof(Device), + VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); + if (!mem) + return VK_ERROR_OUT_OF_HOST_MEMORY; + Device* device = new (mem) Device(&instance); + + result = ActivateAllLayers(create_info, &instance, device); + if (result != VK_SUCCESS) { + DestroyDevice(device); + return result; + } + + size_t gpu_idx = 0; + while (instance.physical_devices[gpu_idx] != gpu) + gpu_idx++; + + uint32_t activated_layers = 0; + VkLayerDeviceCreateInfo chain_info; + VkLayerDeviceLink* layer_device_link_info = nullptr; + PFN_vkGetInstanceProcAddr next_gipa = GetInstanceProcAddr_Bottom; + PFN_vkGetDeviceProcAddr next_gdpa = GetDeviceProcAddr_Bottom; + VkDeviceCreateInfo local_create_info = *create_info; + VkDevice local_device = nullptr; + + if (device->active_layers.size() > 0) { + chain_info.u.pLayerInfo = nullptr; + chain_info.pNext = local_create_info.pNext; + chain_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO; + chain_info.function = VK_LAYER_FUNCTION_LINK; + local_create_info.pNext = &chain_info; + + layer_device_link_info = static_cast<VkLayerDeviceLink*>( + alloca(sizeof(VkLayerDeviceLink) * device->active_layers.size())); + if (!layer_device_link_info) { + ALOGE("Failed to alloc Device objects for layers"); + DestroyDevice(device); + return VK_ERROR_OUT_OF_HOST_MEMORY; + } + + /* Create device chain of enabled layers */ + for (auto rit = device->active_layers.rbegin(); + rit != device->active_layers.rend(); ++rit) { + LayerRef& layer = *rit; + layer_device_link_info[activated_layers].pNext = + chain_info.u.pLayerInfo; + layer_device_link_info[activated_layers].pfnNextGetDeviceProcAddr = + next_gdpa; + layer_device_link_info[activated_layers] + .pfnNextGetInstanceProcAddr = next_gipa; + chain_info.u.pLayerInfo = &layer_device_link_info[activated_layers]; + + next_gipa = layer.GetGetInstanceProcAddr(); + next_gdpa = layer.GetGetDeviceProcAddr(); + + ALOGV("Insert device layer %s (v%u)", layer.GetName(), + layer.GetSpecVersion()); + + activated_layers++; + } + } + + PFN_vkCreateDevice create_device = reinterpret_cast<PFN_vkCreateDevice>( + next_gipa(VK_NULL_HANDLE, "vkCreateDevice")); + if (!create_device) { + ALOGE("Unable to find vkCreateDevice for driver"); + DestroyDevice(device); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VkLayerDeviceCreateInfo device_create_info; + + device_create_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO; + device_create_info.function = VK_LAYER_FUNCTION_DEVICE; + + device_create_info.u.deviceInfo.device_info = device; + device_create_info.u.deviceInfo.pfnNextGetInstanceProcAddr = next_gipa; + + device_create_info.pNext = local_create_info.pNext; + local_create_info.pNext = &device_create_info; + + result = create_device(gpu, &local_create_info, allocator, &local_device); + + if (result != VK_SUCCESS) { + DestroyDevice(device); + return result; + } + + // Set dispatch table for newly created Device + hwvulkan_dispatch_t* vulkan_dispatch = + reinterpret_cast<hwvulkan_dispatch_t*>(local_device); + vulkan_dispatch->vtbl = &device->dispatch; + + const DeviceDispatchTable& device_dispatch = GetDispatchTable(local_device); + if (!LoadDeviceDispatchTable( + local_device, next_gdpa, + const_cast<DeviceDispatchTable&>(device_dispatch))) { + ALOGV("Failed to initialize device dispatch table"); + PFN_vkDestroyDevice destroy_device = + reinterpret_cast<PFN_vkDestroyDevice>( + next_gipa(VK_NULL_HANDLE, "vkDestroyDevice")); + ALOG_ASSERT(destroy_device != nullptr, + "Loader unable to find DestroyDevice"); + destroy_device(local_device, allocator); + return VK_ERROR_INITIALIZATION_FAILED; + } + *device_out = local_device; + + return VK_SUCCESS; } PFN_vkVoidFunction GetDeviceProcAddr_Top(VkDevice device, const char* name) { diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h index 3e2d1c43b5..77c8ebea5a 100644 --- a/vulkan/libvulkan/loader.h +++ b/vulkan/libvulkan/loader.h @@ -94,6 +94,9 @@ VKAPI_ATTR void DestroyInstance_Top(VkInstance instance, const VkAllocationCallb VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr_Top(VkDevice drv_device, const char* name); VKAPI_ATTR void GetDeviceQueue_Top(VkDevice drv_device, uint32_t family, uint32_t index, VkQueue* out_queue); VKAPI_ATTR VkResult AllocateCommandBuffers_Top(VkDevice device, const VkCommandBufferAllocateInfo* alloc_info, VkCommandBuffer* cmdbufs); +VKAPI_ATTR VkResult EnumerateDeviceLayerProperties_Top(VkPhysicalDevice pdev, uint32_t* properties_count, VkLayerProperties* properties); +VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties_Top(VkPhysicalDevice pdev, const char * layer_name, uint32_t* properties_count, VkExtensionProperties* properties); +VKAPI_ATTR VkResult CreateDevice_Top(VkPhysicalDevice pdev, const VkDeviceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkDevice* device_out); VKAPI_ATTR void DestroyDevice_Top(VkDevice drv_device, const VkAllocationCallbacks* allocator); VKAPI_ATTR VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkInstance* vkinstance); @@ -150,6 +153,9 @@ class LayerRef { LayerRef(const LayerRef&) = delete; LayerRef& operator=(const LayerRef&) = delete; + const char* GetName(); + uint32_t GetSpecVersion(); + // provides bool-like behavior operator const Layer*() const { return layer_; } diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index bab5a598d6..7f944cf171 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -107,6 +107,79 @@ std::shared_ptr<T> InitSharedPtr(Host host, T* obj) { } } +const VkSurfaceTransformFlagsKHR kSupportedTransforms = + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | + VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | + VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | + VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR | + // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform. + // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR | + // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR | + // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR | + // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR | + VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; + +VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) { + // Native and Vulkan transforms are isomorphic, but are represented + // differently. Vulkan transforms are built up of an optional horizontal + // mirror, followed by a clockwise 0/90/180/270-degree rotation. Native + // transforms are built up from a horizontal flip, vertical flip, and + // 90-degree rotation, all optional but always in that order. + + // TODO(jessehall): For now, only support pure rotations, not + // flip or flip-and-rotate, until I have more time to test them and build + // sample code. As far as I know we never actually use anything besides + // pure rotations anyway. + + switch (native) { + case 0: // 0x0 + return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + // case NATIVE_WINDOW_TRANSFORM_FLIP_H: // 0x1 + // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR; + // case NATIVE_WINDOW_TRANSFORM_FLIP_V: // 0x2 + // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_ROT_180: // FLIP_H | FLIP_V + return VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_ROT_90: // 0x4 + return VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR; + // case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90: + // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR; + // case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90: + // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_ROT_270: // FLIP_H | FLIP_V | ROT_90 + return VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY: + default: + return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } +} + +int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) { + switch (transform) { + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_270; + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_180; + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_90; + // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform. + // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR: + // return NATIVE_WINDOW_TRANSFORM_FLIP_H; + // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: + // return NATIVE_WINDOW_TRANSFORM_FLIP_H | + // NATIVE_WINDOW_TRANSFORM_ROT_90; + // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR: + // return NATIVE_WINDOW_TRANSFORM_FLIP_V; + // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: + // return NATIVE_WINDOW_TRANSFORM_FLIP_V | + // NATIVE_WINDOW_TRANSFORM_ROT_90; + case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: + case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: + default: + return 0; + } +} + // ---------------------------------------------------------------------------- struct Surface { @@ -238,26 +311,35 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR_Bottom( return VK_ERROR_INITIALIZATION_FAILED; } - capabilities->currentExtent = - VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; + int transform_hint; + err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint); + if (err != 0) { + ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_INITIALIZATION_FAILED; + } // TODO(jessehall): Figure out what the min/max values should be. capabilities->minImageCount = 2; capabilities->maxImageCount = 3; + capabilities->currentExtent = + VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; + // TODO(jessehall): Figure out what the max extent should be. Maximum // texture dimension maybe? capabilities->minImageExtent = VkExtent2D{1, 1}; capabilities->maxImageExtent = VkExtent2D{4096, 4096}; - // TODO(jessehall): We can support all transforms, fix this once - // implemented. - capabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + capabilities->maxImageArrayLayers = 1; - // TODO(jessehall): Implement based on NATIVE_WINDOW_TRANSFORM_HINT. - capabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + capabilities->supportedTransforms = kSupportedTransforms; + capabilities->currentTransform = + TranslateNativeToVulkanTransform(transform_hint); - capabilities->maxImageArrayLayers = 1; + // On Android, window composition is a WindowManager property, not something + // associated with the bufferqueue. It can't be changed from here. + capabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; // TODO(jessehall): I think these are right, but haven't thought hard about // it. Do we need to query the driver for support of any of these? @@ -289,6 +371,7 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR_Bottom( const VkSurfaceFormatKHR kFormats[] = { {VK_FORMAT_R8G8B8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR}, {VK_FORMAT_R8G8B8A8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLORSPACE_SRGB_NONLINEAR_KHR}, }; const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); @@ -338,22 +421,54 @@ VkResult CreateSwapchainKHR_Bottom(VkDevice device, "Swapchain imageArrayLayers (%u) != 1 not supported", create_info->imageArrayLayers); - ALOGE_IF(create_info->imageFormat != VK_FORMAT_R8G8B8A8_UNORM, - "swapchain formats other than R8G8B8A8_UNORM not yet implemented"); ALOGE_IF(create_info->imageColorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR, "color spaces other than SRGB_NONLINEAR not yet implemented"); ALOGE_IF(create_info->oldSwapchain, "swapchain re-creation not yet implemented"); - ALOGE_IF(create_info->preTransform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, - "swapchain preTransform not yet implemented"); - ALOGE_IF(create_info->presentMode != VK_PRESENT_MODE_FIFO_KHR, - "present modes other than FIFO are not yet implemented"); + ALOGE_IF((create_info->preTransform & ~kSupportedTransforms) != 0, + "swapchain preTransform %d not supported", + create_info->preTransform); + ALOGW_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR || + create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR), + "swapchain present mode %d not supported", + create_info->presentMode); // -- Configure the native window -- Surface& surface = *SurfaceFromHandle(create_info->surface); const DriverDispatchTable& dispatch = GetDriverDispatch(device); + int native_format = HAL_PIXEL_FORMAT_RGBA_8888; + switch (create_info->imageFormat) { + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SRGB: + native_format = HAL_PIXEL_FORMAT_RGBA_8888; + break; + case VK_FORMAT_R5G6B5_UNORM_PACK16: + native_format = HAL_PIXEL_FORMAT_RGB_565; + break; + default: + ALOGE("unsupported swapchain format %d", create_info->imageFormat); + break; + } + err = native_window_set_buffers_format(surface.window.get(), native_format); + if (err != 0) { + // TODO(jessehall): Improve error reporting. Can we enumerate possible + // errors and translate them to valid Vulkan result codes? + ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)", + native_format, strerror(-err), err); + return VK_ERROR_INITIALIZATION_FAILED; + } + err = native_window_set_buffers_data_space(surface.window.get(), + HAL_DATASPACE_SRGB_LINEAR); + if (err != 0) { + // TODO(jessehall): Improve error reporting. Can we enumerate possible + // errors and translate them to valid Vulkan result codes? + ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)", + HAL_DATASPACE_SRGB_LINEAR, strerror(-err), err); + return VK_ERROR_INITIALIZATION_FAILED; + } + err = native_window_set_buffers_dimensions( surface.window.get(), static_cast<int>(create_info->imageExtent.width), static_cast<int>(create_info->imageExtent.height)); @@ -366,6 +481,26 @@ VkResult CreateSwapchainKHR_Bottom(VkDevice device, return VK_ERROR_INITIALIZATION_FAILED; } + // VkSwapchainCreateInfo::preTransform indicates the transformation the app + // applied during rendering. native_window_set_transform() expects the + // inverse: the transform the app is requesting that the compositor perform + // during composition. With native windows, pre-transform works by rendering + // with the same transform the compositor is applying (as in Vulkan), but + // then requesting the inverse transform, so that when the compositor does + // it's job the two transforms cancel each other out and the compositor ends + // up applying an identity transform to the app's buffer. + err = native_window_set_buffers_transform( + surface.window.get(), + InvertTransformToNative(create_info->preTransform)); + if (err != 0) { + // TODO(jessehall): Improve error reporting. Can we enumerate possible + // errors and translate them to valid Vulkan result codes? + ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", + InvertTransformToNative(create_info->preTransform), + strerror(-err), err); + return VK_ERROR_INITIALIZATION_FAILED; + } + err = native_window_set_scaling_mode( surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); if (err != 0) { @@ -376,16 +511,25 @@ VkResult CreateSwapchainKHR_Bottom(VkDevice device, return VK_ERROR_INITIALIZATION_FAILED; } - uint32_t min_undequeued_buffers; - err = surface.window->query( - surface.window.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - reinterpret_cast<int*>(&min_undequeued_buffers)); - if (err != 0) { + int query_value; + err = surface.window->query(surface.window.get(), + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &query_value); + if (err != 0 || query_value < 0) { // TODO(jessehall): Improve error reporting. Can we enumerate possible // errors and translate them to valid Vulkan result codes? - ALOGE("window->query failed: %s (%d)", strerror(-err), err); + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, + query_value); return VK_ERROR_INITIALIZATION_FAILED; } + uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); + // The MIN_UNDEQUEUED_BUFFERS query doesn't know whether we'll be using + // async mode or not, and assumes not. But in async mode, the BufferQueue + // requires an extra undequeued buffer. + // See BufferQueueCore::getMinUndequeuedBufferCountLocked(). + if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) + min_undequeued_buffers += 1; + uint32_t num_images = (create_info->minImageCount - 1) + min_undequeued_buffers; err = native_window_set_buffer_count(surface.window.get(), num_images); @@ -418,6 +562,17 @@ VkResult CreateSwapchainKHR_Bottom(VkDevice device, return VK_ERROR_INITIALIZATION_FAILED; } + err = surface.window->setSwapInterval( + surface.window.get(), + create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1); + if (err != 0) { + // TODO(jessehall): Improve error reporting. Can we enumerate possible + // errors and translate them to valid Vulkan result codes? + ALOGE("native_window->setSwapInterval failed: %s (%d)", strerror(-err), + err); + return VK_ERROR_INITIALIZATION_FAILED; + } + // -- Allocate our Swapchain object -- // After this point, we must deallocate the swapchain on error. @@ -442,7 +597,7 @@ VkResult CreateSwapchainKHR_Bottom(VkDevice device, .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = &image_native_buffer, .imageType = VK_IMAGE_TYPE_2D, - .format = VK_FORMAT_R8G8B8A8_UNORM, // TODO(jessehall) + .format = create_info->imageFormat, .extent = {0, 0, 1}, .mipLevels = 1, .arrayLayers = 1, diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index 8f47bd11a5..6f57238b5d 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -15,7 +15,6 @@ */ #include <hardware/hwvulkan.h> -#include <vulkan/vk_ext_debug_report.h> #include <algorithm> #include <array> @@ -89,7 +88,7 @@ enum Enum { }; } // namespace HandleType -const VkDeviceSize kMaxDeviceMemory = VkDeviceSize(INTPTR_MAX) + 1; +const VkDeviceSize kMaxDeviceMemory = 0x10000000; // 256 MiB, arbitrary } // anonymous namespace @@ -346,6 +345,121 @@ void GetPhysicalDeviceProperties(VkPhysicalDevice, strcpy(properties->deviceName, "Android Vulkan Null Driver"); memset(properties->pipelineCacheUUID, 0, sizeof(properties->pipelineCacheUUID)); + properties->limits = VkPhysicalDeviceLimits{ + 4096, // maxImageDimension1D + 4096, // maxImageDimension2D + 256, // maxImageDimension3D + 4096, // maxImageDimensionCube + 256, // maxImageArrayLayers + 65536, // maxTexelBufferElements + 16384, // maxUniformBufferRange + 1 << 27, // maxStorageBufferRange + 128, // maxPushConstantsSize + 4096, // maxMemoryAllocationCount + 4000, // maxSamplerAllocationCount + 1, // bufferImageGranularity + 0, // sparseAddressSpaceSize + 4, // maxBoundDescriptorSets + 16, // maxPerStageDescriptorSamplers + 12, // maxPerStageDescriptorUniformBuffers + 4, // maxPerStageDescriptorStorageBuffers + 16, // maxPerStageDescriptorSampledImages + 4, // maxPerStageDescriptorStorageImages + 4, // maxPerStageDescriptorInputAttachments + 128, // maxPerStageResources + 96, // maxDescriptorSetSamplers + 72, // maxDescriptorSetUniformBuffers + 8, // maxDescriptorSetUniformBuffersDynamic + 24, // maxDescriptorSetStorageBuffers + 4, // maxDescriptorSetStorageBuffersDynamic + 96, // maxDescriptorSetSampledImages + 24, // maxDescriptorSetStorageImages + 4, // maxDescriptorSetInputAttachments + 16, // maxVertexInputAttributes + 16, // maxVertexInputBindings + 2047, // maxVertexInputAttributeOffset + 2048, // maxVertexInputBindingStride + 64, // maxVertexOutputComponents + 0, // maxTessellationGenerationLevel + 0, // maxTessellationPatchSize + 0, // maxTessellationControlPerVertexInputComponents + 0, // maxTessellationControlPerVertexOutputComponents + 0, // maxTessellationControlPerPatchOutputComponents + 0, // maxTessellationControlTotalOutputComponents + 0, // maxTessellationEvaluationInputComponents + 0, // maxTessellationEvaluationOutputComponents + 0, // maxGeometryShaderInvocations + 0, // maxGeometryInputComponents + 0, // maxGeometryOutputComponents + 0, // maxGeometryOutputVertices + 0, // maxGeometryTotalOutputComponents + 64, // maxFragmentInputComponents + 4, // maxFragmentOutputAttachments + 0, // maxFragmentDualSrcAttachments + 4, // maxFragmentCombinedOutputResources + 16384, // maxComputeSharedMemorySize + {65536, 65536, 65536}, // maxComputeWorkGroupCount[3] + 128, // maxComputeWorkGroupInvocations + {128, 128, 64}, // maxComputeWorkGroupSize[3] + 4, // subPixelPrecisionBits + 4, // subTexelPrecisionBits + 4, // mipmapPrecisionBits + UINT32_MAX, // maxDrawIndexedIndexValue + 1, // maxDrawIndirectCount + 2, // maxSamplerLodBias + 1, // maxSamplerAnisotropy + 1, // maxViewports + {4096, 4096}, // maxViewportDimensions[2] + {-8192.0f, 8191.0f}, // viewportBoundsRange[2] + 0, // viewportSubPixelBits + 64, // minMemoryMapAlignment + 256, // minTexelBufferOffsetAlignment + 256, // minUniformBufferOffsetAlignment + 256, // minStorageBufferOffsetAlignment + -8, // minTexelOffset + 7, // maxTexelOffset + 0, // minTexelGatherOffset + 0, // maxTexelGatherOffset + 0.0f, // minInterpolationOffset + 0.0f, // maxInterpolationOffset + 0, // subPixelInterpolationOffsetBits + 4096, // maxFramebufferWidth + 4096, // maxFramebufferHeight + 256, // maxFramebufferLayers + VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT, // framebufferColorSampleCounts + VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT, // framebufferDepthSampleCounts + VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT, // framebufferStencilSampleCounts + VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT, // framebufferNoAttachmentsSampleCounts + 4, // maxColorAttachments + VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT, // sampledImageColorSampleCounts + VK_SAMPLE_COUNT_1_BIT, // sampledImageIntegerSampleCounts + VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT, // sampledImageDepthSampleCounts + VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT, // sampledImageStencilSampleCounts + VK_SAMPLE_COUNT_1_BIT, // storageImageSampleCounts + 1, // maxSampleMaskWords + VK_TRUE, // timestampComputeAndGraphics + 1, // timestampPeriod + 0, // maxClipDistances + 0, // maxCullDistances + 0, // maxCombinedClipAndCullDistances + 2, // discreteQueuePriorities + {1.0f, 1.0f}, // pointSizeRange[2] + {1.0f, 1.0f}, // lineWidthRange[2] + 0.0f, // pointSizeGranularity + 0.0f, // lineWidthGranularity + VK_TRUE, // strictLines + VK_TRUE, // standardSampleLocations + 1, // optimalBufferCopyOffsetAlignment + 1, // optimalBufferCopyRowPitchAlignment + 64, // nonCoherentAtomSize + }; } void GetPhysicalDeviceQueueFamilyProperties( diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl index 7488692af0..3a84971983 100644 --- a/vulkan/nulldrv/null_driver.tmpl +++ b/vulkan/nulldrv/null_driver.tmpl @@ -49,7 +49,6 @@ #define NULLDRV_NULL_DRIVER_H 1 ¶ #include <vulkan/vk_android_native_buffer.h> -#include <vulkan/vk_ext_debug_report.h> #include <vulkan/vulkan.h> ¶ namespace null_driver {« diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index 87ff681913..98952b8df8 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -20,7 +20,6 @@ #define NULLDRV_NULL_DRIVER_H 1 #include <vulkan/vk_android_native_buffer.h> -#include <vulkan/vk_ext_debug_report.h> #include <vulkan/vulkan.h> namespace null_driver { diff --git a/vulkan/patches/README b/vulkan/patches/README deleted file mode 100644 index d424dd8b84..0000000000 --- a/vulkan/patches/README +++ /dev/null @@ -1,26 +0,0 @@ -frameworks/native/vulkan/patches -================================ -Each subdirectory corresponds to a sequence of patches. These are -"virtual branches": we only have one shared branch, so these let us -share experimental or auxiliary changes without disturbing the main -branch. - -To apply: -$ cd <somewhere in target git repo> -$ git am $VULKAN_PATCHES/$PATCH_DIR/* - - -frameworks_base-apk_library_dir -------------------------------- -This branch is for $TOP/frameworks/base. It modifies the framework to -inform the Vulkan loader, during activity startup, where the -activity's native library directory. The loader will search this -directory for layer libraries. Without this change, layers will only -be loaded from a global location under /data. - - -build-install_libvulkan ------------------------ -This branch is for $TOP/build. It adds libvulkan.so to the base -PRODUCT_PACKAGES variable, so it will be built and installed on the system -partition by default. diff --git a/vulkan/patches/build-install_libvulkan/0001-Add-libvulkan-to-base-PRODUCT_PACKAGES.patch b/vulkan/patches/build-install_libvulkan/0001-Add-libvulkan-to-base-PRODUCT_PACKAGES.patch deleted file mode 100644 index 9d214bd8b3..0000000000 --- a/vulkan/patches/build-install_libvulkan/0001-Add-libvulkan-to-base-PRODUCT_PACKAGES.patch +++ /dev/null @@ -1,25 +0,0 @@ -From a0aa01fb36a2769b7113316c86e902def62001d9 Mon Sep 17 00:00:00 2001 -From: Jesse Hall <jessehall@google.com> -Date: Wed, 14 Oct 2015 15:20:34 -0700 -Subject: [PATCH] Add libvulkan to base PRODUCT_PACKAGES - -Change-Id: I6c3ad4732148888a88fe980bf8e2bedf26ee74c8 ---- - target/product/base.mk | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/target/product/base.mk b/target/product/base.mk -index 1699156..4b9ce92 100644 ---- a/target/product/base.mk -+++ b/target/product/base.mk -@@ -94,6 +94,7 @@ PRODUCT_PACKAGES += \ - libvisualizer \ - libvorbisidec \ - libmediandk \ -+ libvulkan \ - libwifi-service \ - media \ - media_cmd \ --- -2.6.0.rc2.230.g3dd15c0 - diff --git a/vulkan/patches/frameworks_base-apk_library_dir/0001-Adding-plumbing-for-passing-the-lib-directory.patch b/vulkan/patches/frameworks_base-apk_library_dir/0001-Adding-plumbing-for-passing-the-lib-directory.patch deleted file mode 100644 index 81022d6965..0000000000 --- a/vulkan/patches/frameworks_base-apk_library_dir/0001-Adding-plumbing-for-passing-the-lib-directory.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 5c7e465f1d11bccecdc5cacce87d1fd7deeb5adb Mon Sep 17 00:00:00 2001 -From: Michael Lentine <mlentine@google.com> -Date: Mon, 14 Sep 2015 13:28:25 -0500 -Subject: [PATCH] Adding plumbing for passing the lib directory. - -Added call in handleBindApplication which will pass the library path into -HardwareRender which then passes it to libvulkan through ThreadedRenderer's -jni interface. - -Change-Id: Ie5709ac46f47c4af5c020d604a479e78745d7777 ---- - core/java/android/app/ActivityThread.java | 7 +++++-- - core/java/android/view/HardwareRenderer.java | 11 +++++++++++ - core/java/android/view/ThreadedRenderer.java | 1 + - core/jni/Android.mk | 2 ++ - core/jni/android_view_ThreadedRenderer.cpp | 15 +++++++++++++++ - 5 files changed, 34 insertions(+), 2 deletions(-) - -diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java -index da21eaf..76608c6 100644 ---- a/core/java/android/app/ActivityThread.java -+++ b/core/java/android/app/ActivityThread.java -@@ -4520,8 +4520,11 @@ public final class ActivityThread { - } else { - Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory"); - } -- } -- -+ } -+ -+ // Add the lib dir path to hardware renderer so that vulkan layers -+ // can be searched for within that directory. -+ HardwareRenderer.setLibDir(data.info.getLibDir()); - - final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24)); - DateFormat.set24HourTimePref(is24Hr); -diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java -index 5e58250..ed99115 100644 ---- a/core/java/android/view/HardwareRenderer.java -+++ b/core/java/android/view/HardwareRenderer.java -@@ -301,6 +301,17 @@ public abstract class HardwareRenderer { - } - - /** -+ * Sets the library directory to use as a search path for vulkan layers. -+ * -+ * @param libDir A directory that contains vulkan layers -+ * -+ * @hide -+ */ -+ public static void setLibDir(String libDir) { -+ ThreadedRenderer.setupVulkanLayerPath(libDir); -+ } -+ -+ /** - * Indicates that the specified hardware layer needs to be updated - * as soon as possible. - * -diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java -index f6119e2..d3e5175 100644 ---- a/core/java/android/view/ThreadedRenderer.java -+++ b/core/java/android/view/ThreadedRenderer.java -@@ -492,6 +492,7 @@ public class ThreadedRenderer extends HardwareRenderer { - } - - static native void setupShadersDiskCache(String cacheFile); -+ static native void setupVulkanLayerPath(String layerPath); - - private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map); - private static native void nSetProcessStatsBuffer(long nativeProxy, int fd); -diff --git a/core/jni/Android.mk b/core/jni/Android.mk -index 6b07a47..438e95b 100644 ---- a/core/jni/Android.mk -+++ b/core/jni/Android.mk -@@ -177,6 +177,7 @@ LOCAL_C_INCLUDES += \ - $(LOCAL_PATH)/android/graphics \ - $(LOCAL_PATH)/../../libs/hwui \ - $(LOCAL_PATH)/../../../native/opengl/libs \ -+ $(LOCAL_PATH)/../../../native/vulkan/include \ - $(call include-path-for, bluedroid) \ - $(call include-path-for, libhardware)/hardware \ - $(call include-path-for, libhardware_legacy)/hardware_legacy \ -@@ -225,6 +226,7 @@ LOCAL_SHARED_LIBRARIES := \ - libEGL \ - libGLESv1_CM \ - libGLESv2 \ -+ libvulkan \ - libETC1 \ - libhardware \ - libhardware_legacy \ -diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp -index 47132f4..69e8ca6 100644 ---- a/core/jni/android_view_ThreadedRenderer.cpp -+++ b/core/jni/android_view_ThreadedRenderer.cpp -@@ -27,6 +27,7 @@ - #include <EGL/egl.h> - #include <EGL/eglext.h> - #include <EGL/egl_cache.h> -+#include <vulkan/vulkan_loader_data.h> - - #include <utils/StrongPointer.h> - #include <android_runtime/android_view_Surface.h> -@@ -448,6 +449,18 @@ static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, job - } - - // ---------------------------------------------------------------------------- -+// Layers -+// ---------------------------------------------------------------------------- -+ -+static void android_view_ThreadedRenderer_setupVulkanLayerPath(JNIEnv* env, jobject clazz, -+ jstring layerPath) { -+ -+ const char* layerArray = env->GetStringUTFChars(layerPath, NULL); -+ vulkan::LoaderData::GetInstance().layer_path = layerArray; -+ env->ReleaseStringUTFChars(layerPath, layerArray); -+} -+ -+// ---------------------------------------------------------------------------- - // JNI Glue - // ---------------------------------------------------------------------------- - -@@ -487,6 +500,8 @@ static JNINativeMethod gMethods[] = { - { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData }, - { "setupShadersDiskCache", "(Ljava/lang/String;)V", - (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, -+ { "setupVulkanLayerPath", "(Ljava/lang/String;)V", -+ (void*) android_view_ThreadedRenderer_setupVulkanLayerPath }, - }; - - int register_android_view_ThreadedRenderer(JNIEnv* env) { --- -2.6.0.rc2.230.g3dd15c0 - diff --git a/vulkan/tools/Android.mk b/vulkan/tools/Android.mk index 31d6089e3b..337e683ac7 100644 --- a/vulkan/tools/Android.mk +++ b/vulkan/tools/Android.mk @@ -21,7 +21,8 @@ LOCAL_CFLAGS += -DLOG_TAG=\"vkinfo\" LOCAL_CFLAGS += -Weverything -Werror -Wno-padded -Wno-undef -Wno-switch-enum LOCAL_CPPFLAGS := -std=c++1y \ -Wno-c++98-compat-pedantic \ - -Wno-c99-extensions + -Wno-c99-extensions \ + -Wno-old-style-cast LOCAL_C_INCLUDES := \ frameworks/native/vulkan/include diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp index 462a8e5ca0..e97e5f56c4 100644 --- a/vulkan/tools/vkinfo.cpp +++ b/vulkan/tools/vkinfo.cpp @@ -22,13 +22,19 @@ #include <vector> #include <vulkan/vulkan.h> -#include <vulkan/vk_ext_debug_report.h> #define LOG_TAG "vkinfo" #include <log/log.h> namespace { +struct Options { + bool layer_description; + bool layer_extensions; + bool unsupported_features; + bool validate; +}; + struct GpuInfo { VkPhysicalDeviceProperties properties; VkPhysicalDeviceMemoryProperties memory; @@ -116,7 +122,9 @@ void EnumerateDeviceExtensions(VkPhysicalDevice gpu, die("vkEnumerateDeviceExtensionProperties (data)", result); } -void GatherGpuInfo(VkPhysicalDevice gpu, GpuInfo& info) { +void GatherGpuInfo(VkPhysicalDevice gpu, + const Options &options, + GpuInfo& info) { VkResult result; uint32_t count; @@ -161,17 +169,35 @@ void GatherGpuInfo(VkPhysicalDevice gpu, GpuInfo& info) { } VkDevice device; + float queue_priorities[] = {0.0}; const VkDeviceQueueCreateInfo queue_create_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = 0, .queueCount = 1, + queue_priorities }; + // clang-format off + const char *kValidationLayers[] = { + "VK_LAYER_GOOGLE_threading", + "VK_LAYER_LUNARG_device_limits", + "VK_LAYER_LUNARG_draw_state", + "VK_LAYER_LUNARG_image", + "VK_LAYER_LUNARG_mem_tracker", + "VK_LAYER_LUNARG_object_tracker", + "VK_LAYER_LUNARG_param_checker", + "VK_LAYER_LUNARG_swapchain", + "VK_LAYER_GOOGLE_unique_objects" + }; + // clang-format on + uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*); const VkDeviceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queue_create_info, .enabledExtensionCount = num_extensions, .ppEnabledExtensionNames = extensions, + .enabledLayerCount = (options.validate) ? num_layers : 0, + .ppEnabledLayerNames = kValidationLayers, .pEnabledFeatures = &info.features, }; result = vkCreateDevice(gpu, &create_info, nullptr, &device); @@ -180,7 +206,7 @@ void GatherGpuInfo(VkPhysicalDevice gpu, GpuInfo& info) { vkDestroyDevice(device, nullptr); } -void GatherInfo(VulkanInfo* info) { +void GatherInfo(VulkanInfo* info, const Options& options) { VkResult result; uint32_t count; @@ -216,10 +242,36 @@ void GatherInfo(VulkanInfo* info) { extensions[num_extensions++] = desired_ext; } + // clang-format off + const char *kValidationLayers[] = { + "VK_LAYER_GOOGLE_threading", + "VK_LAYER_LUNARG_device_limits", + "VK_LAYER_LUNARG_draw_state", + "VK_LAYER_LUNARG_image", + "VK_LAYER_LUNARG_mem_tracker", + "VK_LAYER_LUNARG_object_tracker", + "VK_LAYER_LUNARG_param_checker", + "VK_LAYER_LUNARG_swapchain", + "VK_LAYER_GOOGLE_unique_objects" + }; + // clang-format on + uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*); + + const VkApplicationInfo application_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = "vkinfo", + .applicationVersion = 0, + .pEngineName = "vkinfo", + .engineVersion = 0, + .apiVersion = VK_API_VERSION, + }; const VkInstanceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &application_info, .enabledExtensionCount = num_extensions, .ppEnabledExtensionNames = extensions, + .enabledLayerCount = (options.validate) ? num_layers : 0, + .ppEnabledLayerNames = kValidationLayers, }; VkInstance instance; result = vkCreateInstance(&create_info, nullptr, &instance); @@ -240,18 +292,13 @@ void GatherInfo(VulkanInfo* info) { info->gpus.resize(num_gpus); for (size_t i = 0; i < gpus.size(); i++) - GatherGpuInfo(gpus[i], info->gpus.at(i)); + GatherGpuInfo(gpus[i], options, info->gpus.at(i)); vkDestroyInstance(instance, nullptr); } // ---------------------------------------------------------------------------- -struct Options { - bool layer_description; - bool layer_extensions; -}; - const size_t kMaxIndent = 8; const size_t kIndentSize = 3; std::array<char, kMaxIndent * kIndentSize + 1> kIndent; @@ -266,16 +313,6 @@ const char* Indent(size_t n) { (kIndent.size() - (kIndentSize * std::min(n, kMaxIndent) + 1)); } -uint32_t ExtractMajorVersion(uint32_t version) { - return (version >> 22) & 0x3FF; -} -uint32_t ExtractMinorVersion(uint32_t version) { - return (version >> 12) & 0x3FF; -} -uint32_t ExtractPatchVersion(uint32_t version) { - return (version >> 0) & 0xFFF; -} - const char* VkPhysicalDeviceTypeStr(VkPhysicalDeviceType type) { switch (type) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: @@ -307,9 +344,9 @@ void PrintLayers( size_t indent) { for (size_t i = 0; i < layers.size(); i++) { printf("%s%s %u.%u.%u/%u\n", Indent(indent), layers[i].layerName, - ExtractMajorVersion(layers[i].specVersion), - ExtractMinorVersion(layers[i].specVersion), - ExtractPatchVersion(layers[i].specVersion), + VK_VERSION_MAJOR(layers[i].specVersion), + VK_VERSION_MINOR(layers[i].specVersion), + VK_VERSION_PATCH(layers[i].specVersion), layers[i].implementationVersion); if (options.layer_description) printf("%s%s\n", Indent(indent + 1), layers[i].description); @@ -323,6 +360,128 @@ void PrintLayers( } } +void PrintAllFeatures(const char* indent, + const VkPhysicalDeviceFeatures& features) { + // clang-format off + printf("%srobustBufferAccess: %s\n", indent, features.robustBufferAccess ? "YES" : "NO"); + printf("%sfullDrawIndexUint32: %s\n", indent, features.fullDrawIndexUint32 ? "YES" : "NO"); + printf("%simageCubeArray: %s\n", indent, features.imageCubeArray ? "YES" : "NO"); + printf("%sindependentBlend: %s\n", indent, features.independentBlend ? "YES" : "NO"); + printf("%sgeometryShader: %s\n", indent, features.geometryShader ? "YES" : "NO"); + printf("%stessellationShader: %s\n", indent, features.tessellationShader ? "YES" : "NO"); + printf("%ssampleRateShading: %s\n", indent, features.sampleRateShading ? "YES" : "NO"); + printf("%sdualSrcBlend: %s\n", indent, features.dualSrcBlend ? "YES" : "NO"); + printf("%slogicOp: %s\n", indent, features.logicOp ? "YES" : "NO"); + printf("%smultiDrawIndirect: %s\n", indent, features.multiDrawIndirect ? "YES" : "NO"); + printf("%sdrawIndirectFirstInstance: %s\n", indent, features.drawIndirectFirstInstance ? "YES" : "NO"); + printf("%sdepthClamp: %s\n", indent, features.depthClamp ? "YES" : "NO"); + printf("%sdepthBiasClamp: %s\n", indent, features.depthBiasClamp ? "YES" : "NO"); + printf("%sfillModeNonSolid: %s\n", indent, features.fillModeNonSolid ? "YES" : "NO"); + printf("%sdepthBounds: %s\n", indent, features.depthBounds ? "YES" : "NO"); + printf("%swideLines: %s\n", indent, features.wideLines ? "YES" : "NO"); + printf("%slargePoints: %s\n", indent, features.largePoints ? "YES" : "NO"); + printf("%salphaToOne: %s\n", indent, features.alphaToOne ? "YES" : "NO"); + printf("%smultiViewport: %s\n", indent, features.multiViewport ? "YES" : "NO"); + printf("%ssamplerAnisotropy: %s\n", indent, features.samplerAnisotropy ? "YES" : "NO"); + printf("%stextureCompressionETC2: %s\n", indent, features.textureCompressionETC2 ? "YES" : "NO"); + printf("%stextureCompressionASTC_LDR: %s\n", indent, features.textureCompressionASTC_LDR ? "YES" : "NO"); + printf("%stextureCompressionBC: %s\n", indent, features.textureCompressionBC ? "YES" : "NO"); + printf("%socclusionQueryPrecise: %s\n", indent, features.occlusionQueryPrecise ? "YES" : "NO"); + printf("%spipelineStatisticsQuery: %s\n", indent, features.pipelineStatisticsQuery ? "YES" : "NO"); + printf("%svertexPipelineStoresAndAtomics: %s\n", indent, features.vertexPipelineStoresAndAtomics ? "YES" : "NO"); + printf("%sfragmentStoresAndAtomics: %s\n", indent, features.fragmentStoresAndAtomics ? "YES" : "NO"); + printf("%sshaderTessellationAndGeometryPointSize: %s\n", indent, features.shaderTessellationAndGeometryPointSize ? "YES" : "NO"); + printf("%sshaderImageGatherExtended: %s\n", indent, features.shaderImageGatherExtended ? "YES" : "NO"); + printf("%sshaderStorageImageExtendedFormats: %s\n", indent, features.shaderStorageImageExtendedFormats ? "YES" : "NO"); + printf("%sshaderStorageImageMultisample: %s\n", indent, features.shaderStorageImageMultisample ? "YES" : "NO"); + printf("%sshaderStorageImageReadWithoutFormat: %s\n", indent, features.shaderStorageImageReadWithoutFormat ? "YES" : "NO"); + printf("%sshaderStorageImageWriteWithoutFormat: %s\n", indent, features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO"); + printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", indent, features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO"); + printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", indent, features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO"); + printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", indent, features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO"); + printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", indent, features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO"); + printf("%sshaderClipDistance: %s\n", indent, features.shaderClipDistance ? "YES" : "NO"); + printf("%sshaderCullDistance: %s\n", indent, features.shaderCullDistance ? "YES" : "NO"); + printf("%sshaderFloat64: %s\n", indent, features.shaderFloat64 ? "YES" : "NO"); + printf("%sshaderInt64: %s\n", indent, features.shaderInt64 ? "YES" : "NO"); + printf("%sshaderInt16: %s\n", indent, features.shaderInt16 ? "YES" : "NO"); + printf("%sshaderResourceResidency: %s\n", indent, features.shaderResourceResidency ? "YES" : "NO"); + printf("%sshaderResourceMinLod: %s\n", indent, features.shaderResourceMinLod ? "YES" : "NO"); + printf("%ssparseBinding: %s\n", indent, features.sparseBinding ? "YES" : "NO"); + printf("%ssparseResidencyBuffer: %s\n", indent, features.sparseResidencyBuffer ? "YES" : "NO"); + printf("%ssparseResidencyImage2D: %s\n", indent, features.sparseResidencyImage2D ? "YES" : "NO"); + printf("%ssparseResidencyImage3D: %s\n", indent, features.sparseResidencyImage3D ? "YES" : "NO"); + printf("%ssparseResidency2Samples: %s\n", indent, features.sparseResidency2Samples ? "YES" : "NO"); + printf("%ssparseResidency4Samples: %s\n", indent, features.sparseResidency4Samples ? "YES" : "NO"); + printf("%ssparseResidency8Samples: %s\n", indent, features.sparseResidency8Samples ? "YES" : "NO"); + printf("%ssparseResidency16Samples: %s\n", indent, features.sparseResidency16Samples ? "YES" : "NO"); + printf("%ssparseResidencyAliased: %s\n", indent, features.sparseResidencyAliased ? "YES" : "NO"); + printf("%svariableMultisampleRate: %s\n", indent, features.variableMultisampleRate ? "YES" : "NO"); + printf("%sinheritedQueries: %s\n", indent, features.inheritedQueries ? "YES" : "NO"); + // clang-format on +} + +void PrintSupportedFeatures(const char* indent, + const VkPhysicalDeviceFeatures& features) { + // clang-format off + if (features.robustBufferAccess) printf("%srobustBufferAccess\n", indent); + if (features.fullDrawIndexUint32) printf("%sfullDrawIndexUint32\n", indent); + if (features.imageCubeArray) printf("%simageCubeArray\n", indent); + if (features.independentBlend) printf("%sindependentBlend\n", indent); + if (features.geometryShader) printf("%sgeometryShader\n", indent); + if (features.tessellationShader) printf("%stessellationShader\n", indent); + if (features.sampleRateShading) printf("%ssampleRateShading\n", indent); + if (features.dualSrcBlend) printf("%sdualSrcBlend\n", indent); + if (features.logicOp) printf("%slogicOp\n", indent); + if (features.multiDrawIndirect) printf("%smultiDrawIndirect\n", indent); + if (features.drawIndirectFirstInstance) printf("%sdrawIndirectFirstInstance\n", indent); + if (features.depthClamp) printf("%sdepthClamp\n", indent); + if (features.depthBiasClamp) printf("%sdepthBiasClamp\n", indent); + if (features.fillModeNonSolid) printf("%sfillModeNonSolid\n", indent); + if (features.depthBounds) printf("%sdepthBounds\n", indent); + if (features.wideLines) printf("%swideLines\n", indent); + if (features.largePoints) printf("%slargePoints\n", indent); + if (features.alphaToOne) printf("%salphaToOne\n", indent); + if (features.multiViewport) printf("%smultiViewport\n", indent); + if (features.samplerAnisotropy) printf("%ssamplerAnisotropy\n", indent); + if (features.textureCompressionETC2) printf("%stextureCompressionETC2\n", indent); + if (features.textureCompressionASTC_LDR) printf("%stextureCompressionASTC_LDR\n", indent); + if (features.textureCompressionBC) printf("%stextureCompressionBC\n", indent); + if (features.occlusionQueryPrecise) printf("%socclusionQueryPrecise\n", indent); + if (features.pipelineStatisticsQuery) printf("%spipelineStatisticsQuery\n", indent); + if (features.vertexPipelineStoresAndAtomics) printf("%svertexPipelineStoresAndAtomics\n", indent); + if (features.fragmentStoresAndAtomics) printf("%sfragmentStoresAndAtomics\n", indent); + if (features.shaderTessellationAndGeometryPointSize) printf("%sshaderTessellationAndGeometryPointSize\n", indent); + if (features.shaderImageGatherExtended) printf("%sshaderImageGatherExtended\n", indent); + if (features.shaderStorageImageExtendedFormats) printf("%sshaderStorageImageExtendedFormats\n", indent); + if (features.shaderStorageImageMultisample) printf("%sshaderStorageImageMultisample\n", indent); + if (features.shaderStorageImageReadWithoutFormat) printf("%sshaderStorageImageReadWithoutFormat\n", indent); + if (features.shaderStorageImageWriteWithoutFormat) printf("%sshaderStorageImageWriteWithoutFormat\n", indent); + if (features.shaderUniformBufferArrayDynamicIndexing) printf("%sshaderUniformBufferArrayDynamicIndexing\n", indent); + if (features.shaderSampledImageArrayDynamicIndexing) printf("%sshaderSampledImageArrayDynamicIndexing\n", indent); + if (features.shaderStorageBufferArrayDynamicIndexing) printf("%sshaderStorageBufferArrayDynamicIndexing\n", indent); + if (features.shaderStorageImageArrayDynamicIndexing) printf("%sshaderStorageImageArrayDynamicIndexing\n", indent); + if (features.shaderClipDistance) printf("%sshaderClipDistance\n", indent); + if (features.shaderCullDistance) printf("%sshaderCullDistance\n", indent); + if (features.shaderFloat64) printf("%sshaderFloat64\n", indent); + if (features.shaderInt64) printf("%sshaderInt64\n", indent); + if (features.shaderInt16) printf("%sshaderInt16\n", indent); + if (features.shaderResourceResidency) printf("%sshaderResourceResidency\n", indent); + if (features.shaderResourceMinLod) printf("%sshaderResourceMinLod\n", indent); + if (features.sparseBinding) printf("%ssparseBinding\n", indent); + if (features.sparseResidencyBuffer) printf("%ssparseResidencyBuffer\n", indent); + if (features.sparseResidencyImage2D) printf("%ssparseResidencyImage2D\n", indent); + if (features.sparseResidencyImage3D) printf("%ssparseResidencyImage3D\n", indent); + if (features.sparseResidency2Samples) printf("%ssparseResidency2Samples\n", indent); + if (features.sparseResidency4Samples) printf("%ssparseResidency4Samples\n", indent); + if (features.sparseResidency8Samples) printf("%ssparseResidency8Samples\n", indent); + if (features.sparseResidency16Samples) printf("%ssparseResidency16Samples\n", indent); + if (features.sparseResidencyAliased) printf("%ssparseResidencyAliased\n", indent); + if (features.variableMultisampleRate) printf("%svariableMultisampleRate\n", indent); + if (features.inheritedQueries) printf("%sinheritedQueries\n", indent); + // clang-format on +} + void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) { VkResult result; std::ostringstream strbuf; @@ -330,9 +489,9 @@ void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) { printf("%s\"%s\" (%s) %u.%u.%u/%#x [%04x:%04x]\n", Indent(indent), info.properties.deviceName, VkPhysicalDeviceTypeStr(info.properties.deviceType), - ExtractMajorVersion(info.properties.apiVersion), - ExtractMinorVersion(info.properties.apiVersion), - ExtractPatchVersion(info.properties.apiVersion), + VK_VERSION_MAJOR(info.properties.apiVersion), + VK_VERSION_MINOR(info.properties.apiVersion), + VK_VERSION_PATCH(info.properties.apiVersion), info.properties.driverVersion, info.properties.vendorID, info.properties.deviceID); @@ -342,7 +501,7 @@ void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) { strbuf << "DEVICE_LOCAL"; printf("%sHeap %u: %" PRIu64 " MiB (0x%" PRIx64 " B) %s\n", Indent(indent + 1), heap, - info.memory.memoryHeaps[heap].size / 0x1000000, + info.memory.memoryHeaps[heap].size / 0x100000, info.memory.memoryHeaps[heap].size, strbuf.str().c_str()); strbuf.str(std::string()); @@ -387,64 +546,12 @@ void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) { qprops.minImageTransferGranularity.depth); } - // clang-format off printf("%sFeatures:\n", Indent(indent + 1)); - printf("%srobustBufferAccess: %s\n", Indent(indent + 2), info.features.robustBufferAccess ? "YES" : "NO"); - printf("%sfullDrawIndexUint32: %s\n", Indent(indent + 2), info.features.fullDrawIndexUint32 ? "YES" : "NO"); - printf("%simageCubeArray: %s\n", Indent(indent + 2), info.features.imageCubeArray ? "YES" : "NO"); - printf("%sindependentBlend: %s\n", Indent(indent + 2), info.features.independentBlend ? "YES" : "NO"); - printf("%sgeometryShader: %s\n", Indent(indent + 2), info.features.geometryShader ? "YES" : "NO"); - printf("%stessellationShader: %s\n", Indent(indent + 2), info.features.tessellationShader ? "YES" : "NO"); - printf("%ssampleRateShading: %s\n", Indent(indent + 2), info.features.sampleRateShading ? "YES" : "NO"); - printf("%sdualSrcBlend: %s\n", Indent(indent + 2), info.features.dualSrcBlend ? "YES" : "NO"); - printf("%slogicOp: %s\n", Indent(indent + 2), info.features.logicOp ? "YES" : "NO"); - printf("%smultiDrawIndirect: %s\n", Indent(indent + 2), info.features.multiDrawIndirect ? "YES" : "NO"); - printf("%sdrawIndirectFirstInstance: %s\n", Indent(indent + 2), info.features.drawIndirectFirstInstance ? "YES" : "NO"); - printf("%sdepthClamp: %s\n", Indent(indent + 2), info.features.depthClamp ? "YES" : "NO"); - printf("%sdepthBiasClamp: %s\n", Indent(indent + 2), info.features.depthBiasClamp ? "YES" : "NO"); - printf("%sfillModeNonSolid: %s\n", Indent(indent + 2), info.features.fillModeNonSolid ? "YES" : "NO"); - printf("%sdepthBounds: %s\n", Indent(indent + 2), info.features.depthBounds ? "YES" : "NO"); - printf("%swideLines: %s\n", Indent(indent + 2), info.features.wideLines ? "YES" : "NO"); - printf("%slargePoints: %s\n", Indent(indent + 2), info.features.largePoints ? "YES" : "NO"); - printf("%salphaToOne: %s\n", Indent(indent + 2), info.features.alphaToOne ? "YES" : "NO"); - printf("%smultiViewport: %s\n", Indent(indent + 2), info.features.multiViewport ? "YES" : "NO"); - printf("%ssamplerAnisotropy: %s\n", Indent(indent + 2), info.features.samplerAnisotropy ? "YES" : "NO"); - printf("%stextureCompressionETC2: %s\n", Indent(indent + 2), info.features.textureCompressionETC2 ? "YES" : "NO"); - printf("%stextureCompressionASTC_LDR: %s\n", Indent(indent + 2), info.features.textureCompressionASTC_LDR ? "YES" : "NO"); - printf("%stextureCompressionBC: %s\n", Indent(indent + 2), info.features.textureCompressionBC ? "YES" : "NO"); - printf("%socclusionQueryPrecise: %s\n", Indent(indent + 2), info.features.occlusionQueryPrecise ? "YES" : "NO"); - printf("%spipelineStatisticsQuery: %s\n", Indent(indent + 2), info.features.pipelineStatisticsQuery ? "YES" : "NO"); - printf("%svertexPipelineStoresAndAtomics: %s\n", Indent(indent + 2), info.features.vertexPipelineStoresAndAtomics ? "YES" : "NO"); - printf("%sfragmentStoresAndAtomics: %s\n", Indent(indent + 2), info.features.fragmentStoresAndAtomics ? "YES" : "NO"); - printf("%sshaderTessellationAndGeometryPointSize: %s\n", Indent(indent + 2), info.features.shaderTessellationAndGeometryPointSize ? "YES" : "NO"); - printf("%sshaderImageGatherExtended: %s\n", Indent(indent + 2), info.features.shaderImageGatherExtended ? "YES" : "NO"); - printf("%sshaderStorageImageExtendedFormats: %s\n", Indent(indent + 2), info.features.shaderStorageImageExtendedFormats ? "YES" : "NO"); - printf("%sshaderStorageImageMultisample: %s\n", Indent(indent + 2), info.features.shaderStorageImageMultisample ? "YES" : "NO"); - printf("%sshaderStorageImageReadWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageReadWithoutFormat ? "YES" : "NO"); - printf("%sshaderStorageImageWriteWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO"); - printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderClipDistance: %s\n", Indent(indent + 2), info.features.shaderClipDistance ? "YES" : "NO"); - printf("%sshaderCullDistance: %s\n", Indent(indent + 2), info.features.shaderCullDistance ? "YES" : "NO"); - printf("%sshaderFloat64: %s\n", Indent(indent + 2), info.features.shaderFloat64 ? "YES" : "NO"); - printf("%sshaderInt64: %s\n", Indent(indent + 2), info.features.shaderInt64 ? "YES" : "NO"); - printf("%sshaderInt16: %s\n", Indent(indent + 2), info.features.shaderInt16 ? "YES" : "NO"); - printf("%sshaderResourceResidency: %s\n", Indent(indent + 2), info.features.shaderResourceResidency ? "YES" : "NO"); - printf("%sshaderResourceMinLod: %s\n", Indent(indent + 2), info.features.shaderResourceMinLod ? "YES" : "NO"); - printf("%ssparseBinding: %s\n", Indent(indent + 2), info.features.sparseBinding ? "YES" : "NO"); - printf("%ssparseResidencyBuffer: %s\n", Indent(indent + 2), info.features.sparseResidencyBuffer ? "YES" : "NO"); - printf("%ssparseResidencyImage2D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage2D ? "YES" : "NO"); - printf("%ssparseResidencyImage3D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage3D ? "YES" : "NO"); - printf("%ssparseResidency2Samples: %s\n", Indent(indent + 2), info.features.sparseResidency2Samples ? "YES" : "NO"); - printf("%ssparseResidency4Samples: %s\n", Indent(indent + 2), info.features.sparseResidency4Samples ? "YES" : "NO"); - printf("%ssparseResidency8Samples: %s\n", Indent(indent + 2), info.features.sparseResidency8Samples ? "YES" : "NO"); - printf("%ssparseResidency16Samples: %s\n", Indent(indent + 2), info.features.sparseResidency16Samples ? "YES" : "NO"); - printf("%ssparseResidencyAliased: %s\n", Indent(indent + 2), info.features.sparseResidencyAliased ? "YES" : "NO"); - printf("%svariableMultisampleRate: %s\n", Indent(indent + 2), info.features.variableMultisampleRate ? "YES" : "NO"); - printf("%sinheritedQueries: %s\n", Indent(indent + 2), info.features.inheritedQueries ? "YES" : "NO"); - // clang-format on + if (options.unsupported_features) { + PrintAllFeatures(Indent(indent + 2), info.features); + } else { + PrintSupportedFeatures(Indent(indent + 2), info.features); + } printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size()); if (!info.extensions.empty()) @@ -470,27 +577,54 @@ void PrintInfo(const VulkanInfo& info, const Options& options) { PrintGpuInfo(gpu, options, indent + 1); } +const char kUsageString[] = + "usage: vkinfo [options]\n" + " -v enable all the following verbose options\n" + " -layer_description print layer description strings\n" + " -layer_extensions print extensions supported by each layer\n" + " -unsupported_features print all physical device features\n" + " -validate enable validation layers if present\n" + " -debug_pause pause at start until resumed via debugger\n"; + } // namespace // ---------------------------------------------------------------------------- int main(int argc, char const* argv[]) { + static volatile bool startup_pause = false; Options options = { .layer_description = false, .layer_extensions = false, + .unsupported_features = false, + .validate = false, }; for (int argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "-h") == 0) { + fputs(kUsageString, stdout); + return 0; + } if (strcmp(argv[argi], "-v") == 0) { options.layer_description = true; options.layer_extensions = true; + options.unsupported_features = true; } else if (strcmp(argv[argi], "-layer_description") == 0) { options.layer_description = true; } else if (strcmp(argv[argi], "-layer_extensions") == 0) { options.layer_extensions = true; + } else if (strcmp(argv[argi], "-unsupported_features") == 0) { + options.unsupported_features = true; + } else if (strcmp(argv[argi], "-validate") == 0) { + options.validate = true; + } else if (strcmp(argv[argi], "-debug_pause") == 0) { + startup_pause = true; } } + while (startup_pause) { + sleep(0); + } + VulkanInfo info; - GatherInfo(&info); + GatherInfo(&info, options); PrintInfo(info, options); return 0; } |