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