Home | History | Annotate | Download | only in smoke
      1 /*
      2  * Copyright (C) 2016 Google, Inc.
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice shall be included
     12  * in all copies or substantial portions of the Software.
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     20  * DEALINGS IN THE SOFTWARE.
     21  */
     22 
     23 #include <cassert>
     24 #include <dlfcn.h>
     25 #include <time.h>
     26 #include <android/log.h>
     27 
     28 #include "Helpers.h"
     29 #include "Game.h"
     30 #include "ShellAndroid.h"
     31 
     32 namespace {
     33 
     34 // copied from ShellXCB.cpp
     35 class PosixTimer {
     36 public:
     37     PosixTimer()
     38     {
     39         reset();
     40     }
     41 
     42     void reset()
     43     {
     44         clock_gettime(CLOCK_MONOTONIC, &start_);
     45     }
     46 
     47     double get() const
     48     {
     49         struct timespec now;
     50         clock_gettime(CLOCK_MONOTONIC, &now);
     51 
     52         constexpr long one_s_in_ns = 1000 * 1000 * 1000;
     53         constexpr double one_s_in_ns_d = static_cast<double>(one_s_in_ns);
     54 
     55         time_t s = now.tv_sec - start_.tv_sec;
     56         long ns;
     57         if (now.tv_nsec > start_.tv_nsec) {
     58             ns = now.tv_nsec - start_.tv_nsec;
     59         } else {
     60             assert(s > 0);
     61             s--;
     62             ns = one_s_in_ns - (start_.tv_nsec - now.tv_nsec);
     63         }
     64 
     65         return static_cast<double>(s) + static_cast<double>(ns) / one_s_in_ns_d;
     66     }
     67 
     68 private:
     69     struct timespec start_;
     70 };
     71 
     72 } // namespace
     73 
     74 ShellAndroid::ShellAndroid(android_app &app, Game &game) : Shell(game), app_(app)
     75 {
     76     instance_extensions_.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
     77 
     78     app_dummy();
     79     app_.userData = this;
     80     app_.onAppCmd = on_app_cmd;
     81     app_.onInputEvent = on_input_event;
     82 
     83     init_vk();
     84 }
     85 
     86 ShellAndroid::~ShellAndroid()
     87 {
     88     cleanup_vk();
     89     dlclose(lib_handle_);
     90 }
     91 
     92 void ShellAndroid::log(LogPriority priority, const char *msg)
     93 {
     94     int prio;
     95 
     96     switch (priority) {
     97     case LOG_DEBUG:
     98         prio = ANDROID_LOG_DEBUG;
     99         break;
    100     case LOG_INFO:
    101         prio = ANDROID_LOG_INFO;
    102         break;
    103     case LOG_WARN:
    104         prio = ANDROID_LOG_WARN;
    105         break;
    106     case LOG_ERR:
    107         prio = ANDROID_LOG_ERROR;
    108         break;
    109     default:
    110         prio = ANDROID_LOG_UNKNOWN;
    111         break;
    112     }
    113 
    114     __android_log_write(prio, settings_.name.c_str(), msg);
    115 }
    116 
    117 PFN_vkGetInstanceProcAddr ShellAndroid::load_vk()
    118 {
    119     const char filename[] = "libvulkan.so";
    120     void *handle = nullptr, *symbol = nullptr;
    121 
    122     handle = dlopen(filename, RTLD_LAZY);
    123     if (handle)
    124         symbol = dlsym(handle, "vkGetInstanceProcAddr");
    125     if (!symbol) {
    126         if (handle)
    127             dlclose(handle);
    128 
    129         throw std::runtime_error(dlerror());
    130     }
    131 
    132     lib_handle_ = handle;
    133 
    134     return reinterpret_cast<PFN_vkGetInstanceProcAddr>(symbol);
    135 }
    136 
    137 VkSurfaceKHR ShellAndroid::create_surface(VkInstance instance)
    138 {
    139     VkAndroidSurfaceCreateInfoKHR surface_info = {};
    140     surface_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
    141     surface_info.window = app_.window;
    142 
    143     VkSurfaceKHR surface;
    144     vk::assert_success(vk::CreateAndroidSurfaceKHR(instance, &surface_info, nullptr, &surface));
    145 
    146     return surface;
    147 }
    148 
    149 void ShellAndroid::on_app_cmd(int32_t cmd)
    150 {
    151     switch (cmd) {
    152     case APP_CMD_INIT_WINDOW:
    153         create_context();
    154         resize_swapchain(0, 0);
    155         break;
    156     case APP_CMD_TERM_WINDOW:
    157         destroy_context();
    158         break;
    159     case APP_CMD_WINDOW_RESIZED:
    160         resize_swapchain(0, 0);
    161         break;
    162     case APP_CMD_STOP:
    163         ANativeActivity_finish(app_.activity);
    164         break;
    165     default:
    166         break;
    167     }
    168 }
    169 
    170 int32_t ShellAndroid::on_input_event(const AInputEvent *event)
    171 {
    172     if (AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION)
    173         return false;
    174 
    175     bool handled = false;
    176 
    177     switch (AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK) {
    178     case AMOTION_EVENT_ACTION_UP:
    179         game_.on_key(Game::KEY_SPACE);
    180         handled = true;
    181         break;
    182     default:
    183         break;
    184     }
    185 
    186     return handled;
    187 }
    188 
    189 void ShellAndroid::quit()
    190 {
    191     ANativeActivity_finish(app_.activity);
    192 }
    193 
    194 void ShellAndroid::run()
    195 {
    196     PosixTimer timer;
    197 
    198     double current_time = timer.get();
    199 
    200     while (true) {
    201         struct android_poll_source *source;
    202         while (true) {
    203             int timeout = (settings_.animate && app_.window) ? 0 : -1;
    204             if (ALooper_pollAll(timeout, nullptr, nullptr,
    205                     reinterpret_cast<void **>(&source)) < 0)
    206                 break;
    207 
    208             if (source)
    209                 source->process(&app_, source);
    210         }
    211 
    212         if (app_.destroyRequested)
    213             break;
    214 
    215         if (!app_.window)
    216             continue;
    217 
    218         acquire_back_buffer();
    219 
    220         double t = timer.get();
    221         add_game_time(static_cast<float>(t - current_time));
    222 
    223         present_back_buffer();
    224 
    225         current_time = t;
    226     }
    227 }
    228