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()
     32     {
     33         reset();
     34     }
     35 
     36     void reset()
     37     {
     38         clock_gettime(CLOCK_MONOTONIC, &start_);
     39     }
     40 
     41     double get() const
     42     {
     43         struct timespec now;
     44         clock_gettime(CLOCK_MONOTONIC, &now);
     45 
     46         constexpr long one_s_in_ns = 1000 * 1000 * 1000;
     47         constexpr double one_s_in_ns_d = static_cast<double>(one_s_in_ns);
     48 
     49         time_t s = now.tv_sec - start_.tv_sec;
     50         long ns;
     51         if (now.tv_nsec > start_.tv_nsec) {
     52             ns = now.tv_nsec - start_.tv_nsec;
     53         } else {
     54             assert(s > 0);
     55             s--;
     56             ns = one_s_in_ns - (start_.tv_nsec - now.tv_nsec);
     57         }
     58 
     59         return static_cast<double>(s) + static_cast<double>(ns) / one_s_in_ns_d;
     60     }
     61 
     62 private:
     63     struct timespec start_;
     64 };
     65 
     66 } // namespace
     67 
     68 ShellAndroid::ShellAndroid(android_app &app, Game &game) : Shell(game), app_(app)
     69 {
     70     instance_extensions_.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
     71 
     72     app_dummy();
     73     app_.userData = this;
     74     app_.onAppCmd = on_app_cmd;
     75     app_.onInputEvent = on_input_event;
     76 
     77     init_vk();
     78 }
     79 
     80 ShellAndroid::~ShellAndroid()
     81 {
     82     cleanup_vk();
     83     dlclose(lib_handle_);
     84 }
     85 
     86 void ShellAndroid::log(LogPriority priority, const char *msg)
     87 {
     88     int prio;
     89 
     90     switch (priority) {
     91     case LOG_DEBUG:
     92         prio = ANDROID_LOG_DEBUG;
     93         break;
     94     case LOG_INFO:
     95         prio = ANDROID_LOG_INFO;
     96         break;
     97     case LOG_WARN:
     98         prio = ANDROID_LOG_WARN;
     99         break;
    100     case LOG_ERR:
    101         prio = ANDROID_LOG_ERROR;
    102         break;
    103     default:
    104         prio = ANDROID_LOG_UNKNOWN;
    105         break;
    106     }
    107 
    108     __android_log_write(prio, settings_.name.c_str(), msg);
    109 }
    110 
    111 PFN_vkGetInstanceProcAddr ShellAndroid::load_vk()
    112 {
    113     const char filename[] = "libvulkan.so";
    114     void *handle = nullptr, *symbol = nullptr;
    115 
    116     handle = dlopen(filename, RTLD_LAZY);
    117     if (handle)
    118         symbol = dlsym(handle, "vkGetInstanceProcAddr");
    119     if (!symbol) {
    120         if (handle)
    121             dlclose(handle);
    122 
    123         throw std::runtime_error(dlerror());
    124     }
    125 
    126     lib_handle_ = handle;
    127 
    128     return reinterpret_cast<PFN_vkGetInstanceProcAddr>(symbol);
    129 }
    130 
    131 VkSurfaceKHR ShellAndroid::create_surface(VkInstance instance)
    132 {
    133     VkAndroidSurfaceCreateInfoKHR surface_info = {};
    134     surface_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
    135     surface_info.window = app_.window;
    136 
    137     VkSurfaceKHR surface;
    138     vk::assert_success(vk::CreateAndroidSurfaceKHR(instance, &surface_info, nullptr, &surface));
    139 
    140     return surface;
    141 }
    142 
    143 void ShellAndroid::on_app_cmd(int32_t cmd)
    144 {
    145     switch (cmd) {
    146     case APP_CMD_INIT_WINDOW:
    147         create_context();
    148         resize_swapchain(0, 0);
    149         break;
    150     case APP_CMD_TERM_WINDOW:
    151         destroy_context();
    152         break;
    153     case APP_CMD_WINDOW_RESIZED:
    154         resize_swapchain(0, 0);
    155         break;
    156     case APP_CMD_STOP:
    157         ANativeActivity_finish(app_.activity);
    158         break;
    159     default:
    160         break;
    161     }
    162 }
    163 
    164 int32_t ShellAndroid::on_input_event(const AInputEvent *event)
    165 {
    166     if (AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION)
    167         return false;
    168 
    169     bool handled = false;
    170 
    171     switch (AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK) {
    172     case AMOTION_EVENT_ACTION_UP:
    173         game_.on_key(Game::KEY_SPACE);
    174         handled = true;
    175         break;
    176     default:
    177         break;
    178     }
    179 
    180     return handled;
    181 }
    182 
    183 void ShellAndroid::quit()
    184 {
    185     ANativeActivity_finish(app_.activity);
    186 }
    187 
    188 void ShellAndroid::run()
    189 {
    190     PosixTimer timer;
    191 
    192     double current_time = timer.get();
    193 
    194     while (true) {
    195         struct android_poll_source *source;
    196         while (true) {
    197             int timeout = (settings_.animate && app_.window) ? 0 : -1;
    198             if (ALooper_pollAll(timeout, nullptr, nullptr,
    199                     reinterpret_cast<void **>(&source)) < 0)
    200                 break;
    201 
    202             if (source)
    203                 source->process(&app_, source);
    204         }
    205 
    206         if (app_.destroyRequested)
    207             break;
    208 
    209         if (!app_.window)
    210             continue;
    211 
    212         acquire_back_buffer();
    213 
    214         double t = timer.get();
    215         add_game_time(static_cast<float>(t - current_time));
    216 
    217         present_back_buffer();
    218 
    219         current_time = t;
    220     }
    221 }
    222