• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
PosixTimer()37     PosixTimer()
38     {
39         reset();
40     }
41 
reset()42     void reset()
43     {
44         clock_gettime(CLOCK_MONOTONIC, &start_);
45     }
46 
get() const47     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 
ShellAndroid(android_app & app,Game & game)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 
~ShellAndroid()86 ShellAndroid::~ShellAndroid()
87 {
88     cleanup_vk();
89     dlclose(lib_handle_);
90 }
91 
log(LogPriority priority,const char * msg)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 
load_vk()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 
create_surface(VkInstance instance)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 
on_app_cmd(int32_t cmd)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 
on_input_event(const AInputEvent * event)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 
quit()189 void ShellAndroid::quit()
190 {
191     ANativeActivity_finish(app_.activity);
192 }
193 
run()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