Vulkan SDK for Android 1.1.1 Mali Developer Center
android.cpp
1 /* Copyright (c) 2016-2017, ARM Limited and Contributors
2  *
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge,
6  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 #include "android.hpp"
22 using namespace std;
23 
24 namespace MaliSDK
25 {
26 Platform &Platform::get()
27 {
28  // Not initialized until first call to Platform::get().
29  // Initialization is thread-safe.
30  static AndroidPlatform singleton;
31  return singleton;
32 }
33 
34 Platform::Status AndroidPlatform::getWindowStatus()
35 {
36  return STATUS_RUNNING;
37 }
38 
39 VkSurfaceKHR AndroidPlatform::createSurface()
40 {
41  VkSurfaceKHR surface;
42  PFN_vkCreateAndroidSurfaceKHR fpCreateAndroidSurfaceKHR;
43  if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_SYMBOL(instance, "vkCreateAndroidSurfaceKHR", fpCreateAndroidSurfaceKHR))
44  {
45  LOGE("Couldn't find android surface creation symbol.");
46  return VK_NULL_HANDLE;
47  }
48 
49  VkAndroidSurfaceCreateInfoKHR info = { VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR };
50  info.window = pNativeWindow;
51 
52  VK_CHECK(fpCreateAndroidSurfaceKHR(instance, &info, nullptr, &surface));
53  return surface;
54 }
55 
56 AssetManager &MaliSDK::OS::getAssetManager()
57 {
58  static AndroidAssetManager manager;
59  return manager;
60 }
61 
62 double MaliSDK::OS::getCurrentTime()
63 {
64  timespec ts;
65  if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
66  {
67  LOGE("clock_gettime() failed.\n");
68  return 0.0;
69  }
70 
71  return ts.tv_sec + ts.tv_nsec * 1e-9;
72 }
73 
74 unsigned MaliSDK::OS::getNumberOfCpuThreads()
75 {
76  unsigned count = android_getCpuCount();
77  LOGI("Detected %u CPUs.\n", count);
78  return count;
79 }
80 
81 Platform::SwapchainDimensions AndroidPlatform::getPreferredSwapchain()
82 {
83  SwapchainDimensions chain = { 1280, 720, VK_FORMAT_B8G8R8A8_UNORM };
84  return chain;
85 }
86 
87 Result AndroidPlatform::createWindow(const SwapchainDimensions &swapchain)
88 {
89  return initVulkan(swapchain, { "VK_KHR_surface", "VK_KHR_android_surface" }, { "VK_KHR_swapchain" });
90 }
91 
92 void AndroidPlatform::onResume(const SwapchainDimensions &swapchain)
93 {
94  vkDeviceWaitIdle(device);
95  initSwapchain(swapchain);
96 }
97 
98 void AndroidPlatform::onPause()
99 {
100  destroySwapchain();
101 }
102 
103 void AndroidPlatform::terminate()
104 {
105  WSIPlatform::terminate();
106 }
107 }
108 
109 using namespace MaliSDK;
110 
111 static int32_t engineHandleInput(android_app *, AInputEvent *)
112 {
113  return 0;
114 }
115 
116 static void engineHandleCmd(android_app *pApp, int32_t cmd)
117 {
118  auto *state = static_cast<AndroidState *>(pApp->userData);
119  auto &platform = static_cast<AndroidPlatform &>(Platform::get());
120 
121  switch (cmd)
122  {
123  case APP_CMD_RESUME:
124  {
125  state->active = true;
126  Platform::SwapchainDimensions dim = platform.getPreferredSwapchain();
127 
128  if (state->pVulkanApp)
129  {
130  LOGI("Resuming swapchain!\n");
131  platform.onResume(dim);
132 
133  vector<VkImage> images;
134  platform.getCurrentSwapchain(&images, &dim);
135  state->pVulkanApp->updateSwapchain(images, dim);
136  }
137  break;
138  }
139 
140  case APP_CMD_PAUSE:
141  {
142  LOGI("Pausing swapchain!\n");
143  state->active = false;
144  platform.onPause();
145  break;
146  }
147 
148  case APP_CMD_INIT_WINDOW:
149  if (pApp->window != nullptr)
150  {
151  LOGI("Initializing platform!\n");
152  if (FAILED(platform.initialize()))
153  {
154  LOGE("Failed to initialize platform.\n");
155  abort();
156  }
157 
158  auto &platform = static_cast<AndroidPlatform &>(Platform::get());
159  platform.setNativeWindow(state->pApp->window);
160 
161  Platform::SwapchainDimensions dim = platform.getPreferredSwapchain();
162 
163  LOGI("Creating window!\n");
164  if (FAILED(platform.createWindow(dim)))
165  {
166  LOGE("Failed to create Vulkan window.\n");
167  abort();
168  }
169 
170  LOGI("Creating application!\n");
171  state->pVulkanApp = createApplication();
172 
173  LOGI("Initializing application!\n");
174  if (!state->pVulkanApp->initialize(&platform.getContext()))
175  {
176  LOGE("Failed to initialize Vulkan application.\n");
177  abort();
178  }
179 
180  LOGI("Updating swapchain!\n");
181  vector<VkImage> images;
182  platform.getCurrentSwapchain(&images, &dim);
183  state->pVulkanApp->updateSwapchain(images, dim);
184  }
185  break;
186 
187  case APP_CMD_TERM_WINDOW:
188  if (state->pVulkanApp)
189  {
190  LOGI("Terminating application!\n");
191  state->pVulkanApp->terminate();
192  delete state->pVulkanApp;
193  state->pVulkanApp = nullptr;
194  platform.terminate();
195  }
196  break;
197  }
198 }
199 
200 void android_main(android_app *state)
201 {
202  LOGI("Entering android_main()!\n");
203 
204  // Make sure glue is not stripped.
205  app_dummy();
206 
207  AndroidState engine;
208  memset(&engine, 0, sizeof(engine));
209  engine.pApp = state;
210 
211  state->userData = &engine;
212  state->onAppCmd = engineHandleCmd;
213  state->onInputEvent = engineHandleInput;
214  engine.pApp = state;
215 
216  auto &platform = static_cast<AndroidPlatform &>(Platform::get());
217  static_cast<AndroidAssetManager &>(OS::getAssetManager()).setAssetManager(state->activity->assetManager);
218 
219  unsigned frameCount = 0;
220  double startTime = OS::getCurrentTime();
221 
222  for (;;)
223  {
224  struct android_poll_source *source;
225  int ident;
226  int events;
227 
228  while ((ident = ALooper_pollAll(engine.pVulkanApp && engine.active ? 0 : -1, nullptr, &events,
229  (void **)&source)) >= 0)
230  {
231  if (source)
232  source->process(state, source);
233 
234  if (state->destroyRequested)
235  return;
236  }
237 
238  if (engine.pVulkanApp && engine.active)
239  {
240  unsigned index;
241  vector<VkImage> images;
243 
244  Result res = platform.acquireNextImage(&index);
245  while (res == RESULT_ERROR_OUTDATED_SWAPCHAIN)
246  {
247  platform.acquireNextImage(&index);
248  platform.getCurrentSwapchain(&images, &dim);
249  engine.pVulkanApp->updateSwapchain(images, dim);
250  }
251 
252  if (FAILED(res))
253  {
254  LOGE("Unrecoverable swapchain error.\n");
255  break;
256  }
257 
258  engine.pVulkanApp->render(index, 0.0166f);
259  res = platform.presentImage(index);
260 
261  // Handle Outdated error in acquire.
262  if (FAILED(res) && res != RESULT_ERROR_OUTDATED_SWAPCHAIN)
263  break;
264 
265  frameCount++;
266  if (frameCount == 100)
267  {
268  double endTime = OS::getCurrentTime();
269  LOGI("FPS: %.3f\n", frameCount / (endTime - startTime));
270  frameCount = 0;
271  startTime = endTime;
272  }
273  }
274  }
275 }
The asset manager reads data from a platform specific location. This class is used internally to load...
void setNativeWindow(ANativeWindow *pWindow)
Sets the native window used to create Vulkan swapchain. Called by the mainloop.
Definition: android.hpp:56
VulkanApplication * pVulkanApp
The Vulkan application.
Definition: android.hpp:43
virtual void render(unsigned swapchainIndex, float deltaTime)=0
Render a frame.
virtual void updateSwapchain(const std::vector< VkImage > &backbuffers, const Platform::SwapchainDimensions &dimensions)=0
Called when the swapchain has been initialized.
State used for the android mainloop.
Definition: android.hpp:37
Describes the size and format of the swapchain.
Definition: platform.hpp:54
bool active
The application is in focus and running.
Definition: android.hpp:46
Status
Describes the status of the application lifecycle.
Definition: platform.hpp:65
The platform class is to abstract the Vulkan implementation of a particular platform. It is not used directly by applications, but by the mainloop implementation which is OS specific.
Definition: platform.hpp:38
The Android specific platform.
Definition: android.hpp:50
struct android_app * pApp
The ANativeActivity handle.
Definition: android.hpp:40
An asset manager implementation for Android. Uses AAssetManager to load assets.