Home | History | Annotate | Download | only in smoke
      1 /*
      2  * Copyright (C) 2016 Google, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <cassert>
     18 #include <dlfcn.h>
     19 #include <time.h>
     20 #include <android/log.h>
     21 
     22 #include "Helpers.h"
     23 #include "Game.h"
     24 #include "ShellAndroid.h"
     25 
     26 namespace {
     27 
     28 // copied from ShellXCB.cpp
     29 class PosixTimer {
     30    public:
     31     PosixTimer() { reset(); }
     32 
     33     void reset() { clock_gettime(CLOCK_MONOTONIC, &start_); }
     34 
     35     double get() const {
     36         struct timespec now;
     37         clock_gettime(CLOCK_MONOTONIC, &now);
     38 
     39         constexpr long one_s_in_ns = 1000 * 1000 * 1000;
     40         constexpr double one_s_in_ns_d = static_cast<double>(one_s_in_ns);
     41 
     42         time_t s = now.tv_sec - start_.tv_sec;
     43         long ns;
     44         if (now.tv_nsec > start_.tv_nsec) {
     45             ns = now.tv_nsec - start_.tv_nsec;
     46         } else {
     47             assert(s > 0);
     48             s--;
     49             ns = one_s_in_ns - (start_.tv_nsec - now.tv_nsec);
     50         }
     51 
     52         return static_cast<double>(s) + static_cast<double>(ns) / one_s_in_ns_d;
     53     }
     54 
     55    private:
     56     struct timespec start_;
     57 };
     58 
     59 }  // namespace
     60 
     61 std::vector<std::string> ShellAndroid::get_args(android_app &app) {
     62     const char intent_extra_data_key[] = "args";
     63     std::vector<std::string> args;
     64 
     65     JavaVM &vm = *app.activity->vm;
     66     JNIEnv *p_env;
     67     if (vm.AttachCurrentThread(&p_env, nullptr) != JNI_OK) return args;
     68 
     69     JNIEnv &env = *p_env;
     70     jobject activity = app.activity->clazz;
     71     jmethodID get_intent_method = env.GetMethodID(env.GetObjectClass(activity), "getIntent", "()Landroid/content/Intent;");
     72     jobject intent = env.CallObjectMethod(activity, get_intent_method);
     73 
     74     jmethodID get_string_extra_method =
     75         env.GetMethodID(env.GetObjectClass(intent), "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;");
     76     jvalue get_string_extra_args;
     77     get_string_extra_args.l = env.NewStringUTF(intent_extra_data_key);
     78     jstring extra_str = static_cast<jstring>(env.CallObjectMethodA(intent, get_string_extra_method, &get_string_extra_args));
     79 
     80     std::string args_str;
     81     if (extra_str) {
     82         const char *extra_utf = env.GetStringUTFChars(extra_str, nullptr);
     83         args_str = extra_utf;
     84         env.ReleaseStringUTFChars(extra_str, extra_utf);
     85 
     86         env.DeleteLocalRef(extra_str);
     87     }
     88 
     89     env.DeleteLocalRef(get_string_extra_args.l);
     90     env.DeleteLocalRef(intent);
     91 
     92     vm.DetachCurrentThread();
     93 
     94     // split args_str
     95     std::stringstream ss(args_str);
     96     std::string arg;
     97     while (std::getline(ss, arg, ' ')) {
     98         if (!arg.empty()) args.push_back(arg);
     99     }
    100 
    101     return args;
    102 }
    103 
    104 ShellAndroid::ShellAndroid(android_app &app, Game &game) : Shell(game), app_(app) {
    105     if (game.settings().validate) {
    106         instance_layers_.push_back("VK_LAYER_GOOGLE_threading");
    107         instance_layers_.push_back("VK_LAYER_LUNARG_parameter_validation");
    108         instance_layers_.push_back("VK_LAYER_LUNARG_object_tracker");
    109         instance_layers_.push_back("VK_LAYER_LUNARG_core_validation");
    110         instance_layers_.push_back("VK_LAYER_GOOGLE_unique_objects");
    111     }
    112 
    113     instance_extensions_.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
    114 
    115     app_.userData = this;
    116     app_.onAppCmd = on_app_cmd;
    117     app_.onInputEvent = on_input_event;
    118 
    119     init_vk();
    120 }
    121 
    122 ShellAndroid::~ShellAndroid() {
    123     cleanup_vk();
    124     dlclose(lib_handle_);
    125 }
    126 
    127 void ShellAndroid::log(LogPriority priority, const char *msg) {
    128     int prio;
    129 
    130     switch (priority) {
    131         case LOG_DEBUG:
    132             prio = ANDROID_LOG_DEBUG;
    133             break;
    134         case LOG_INFO:
    135             prio = ANDROID_LOG_INFO;
    136             break;
    137         case LOG_WARN:
    138             prio = ANDROID_LOG_WARN;
    139             break;
    140         case LOG_ERR:
    141             prio = ANDROID_LOG_ERROR;
    142             break;
    143         default:
    144             prio = ANDROID_LOG_UNKNOWN;
    145             break;
    146     }
    147 
    148     __android_log_write(prio, settings_.name.c_str(), msg);
    149 }
    150 
    151 PFN_vkGetInstanceProcAddr ShellAndroid::load_vk() {
    152     const char filename[] = "libvulkan.so";
    153     void *handle = nullptr, *symbol = nullptr;
    154 
    155     handle = dlopen(filename, RTLD_LAZY);
    156     if (handle) symbol = dlsym(handle, "vkGetInstanceProcAddr");
    157     if (!symbol) {
    158         if (handle) dlclose(handle);
    159 
    160         throw std::runtime_error(dlerror());
    161     }
    162 
    163     lib_handle_ = handle;
    164 
    165     return reinterpret_cast<PFN_vkGetInstanceProcAddr>(symbol);
    166 }
    167 
    168 VkSurfaceKHR ShellAndroid::create_surface(VkInstance instance) {
    169     VkAndroidSurfaceCreateInfoKHR surface_info = {};
    170     surface_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
    171     surface_info.window = app_.window;
    172 
    173     VkSurfaceKHR surface;
    174     vk::assert_success(vk::CreateAndroidSurfaceKHR(instance, &surface_info, nullptr, &surface));
    175 
    176     return surface;
    177 }
    178 
    179 void ShellAndroid::on_app_cmd(int32_t cmd) {
    180     switch (cmd) {
    181         case APP_CMD_INIT_WINDOW:
    182             create_context();
    183             resize_swapchain(0, 0);
    184             break;
    185         case APP_CMD_TERM_WINDOW:
    186             destroy_context();
    187             break;
    188         case APP_CMD_WINDOW_RESIZED:
    189             resize_swapchain(0, 0);
    190             break;
    191         case APP_CMD_STOP:
    192             ANativeActivity_finish(app_.activity);
    193             break;
    194         default:
    195             break;
    196     }
    197 }
    198 
    199 int32_t ShellAndroid::on_input_event(const AInputEvent *event) {
    200     if (AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION) return false;
    201 
    202     bool handled = false;
    203 
    204     switch (AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK) {
    205         case AMOTION_EVENT_ACTION_UP:
    206             game_.on_key(Game::KEY_SPACE);
    207             handled = true;
    208             break;
    209         default:
    210             break;
    211     }
    212 
    213     return handled;
    214 }
    215 
    216 void ShellAndroid::quit() { ANativeActivity_finish(app_.activity); }
    217 
    218 void ShellAndroid::run() {
    219     PosixTimer timer;
    220 
    221     double current_time = timer.get();
    222 
    223     while (true) {
    224         struct android_poll_source *source;
    225         while (true) {
    226             int timeout = (settings_.animate && app_.window) ? 0 : -1;
    227             if (ALooper_pollAll(timeout, nullptr, nullptr, reinterpret_cast<void **>(&source)) < 0) break;
    228 
    229             if (source) source->process(&app_, source);
    230         }
    231 
    232         if (app_.destroyRequested) break;
    233 
    234         if (!app_.window) continue;
    235 
    236         acquire_back_buffer();
    237 
    238         double t = timer.get();
    239         add_game_time(static_cast<float>(t - current_time));
    240 
    241         present_back_buffer();
    242 
    243         current_time = t;
    244     }
    245 }
    246