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