227 lines
5.4 KiB
C++
227 lines
5.4 KiB
C++
/*
|
|
* Copyright (C) 2016 Google, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <cassert>
|
|
#include <dlfcn.h>
|
|
#include <time.h>
|
|
#include <android/log.h>
|
|
|
|
#include "Helpers.h"
|
|
#include "Game.h"
|
|
#include "ShellAndroid.h"
|
|
|
|
namespace {
|
|
|
|
// copied from ShellXCB.cpp
|
|
class PosixTimer {
|
|
public:
|
|
PosixTimer()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
clock_gettime(CLOCK_MONOTONIC, &start_);
|
|
}
|
|
|
|
double get() const
|
|
{
|
|
struct timespec now;
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
constexpr long one_s_in_ns = 1000 * 1000 * 1000;
|
|
constexpr double one_s_in_ns_d = static_cast<double>(one_s_in_ns);
|
|
|
|
time_t s = now.tv_sec - start_.tv_sec;
|
|
long ns;
|
|
if (now.tv_nsec > start_.tv_nsec) {
|
|
ns = now.tv_nsec - start_.tv_nsec;
|
|
} else {
|
|
assert(s > 0);
|
|
s--;
|
|
ns = one_s_in_ns - (start_.tv_nsec - now.tv_nsec);
|
|
}
|
|
|
|
return static_cast<double>(s) + static_cast<double>(ns) / one_s_in_ns_d;
|
|
}
|
|
|
|
private:
|
|
struct timespec start_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
ShellAndroid::ShellAndroid(android_app &app, Game &game) : Shell(game), app_(app)
|
|
{
|
|
instance_extensions_.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
|
|
|
app_dummy();
|
|
app_.userData = this;
|
|
app_.onAppCmd = on_app_cmd;
|
|
app_.onInputEvent = on_input_event;
|
|
|
|
init_vk();
|
|
}
|
|
|
|
ShellAndroid::~ShellAndroid()
|
|
{
|
|
cleanup_vk();
|
|
dlclose(lib_handle_);
|
|
}
|
|
|
|
void ShellAndroid::log(LogPriority priority, const char *msg)
|
|
{
|
|
int prio;
|
|
|
|
switch (priority) {
|
|
case LOG_DEBUG:
|
|
prio = ANDROID_LOG_DEBUG;
|
|
break;
|
|
case LOG_INFO:
|
|
prio = ANDROID_LOG_INFO;
|
|
break;
|
|
case LOG_WARN:
|
|
prio = ANDROID_LOG_WARN;
|
|
break;
|
|
case LOG_ERR:
|
|
prio = ANDROID_LOG_ERROR;
|
|
break;
|
|
default:
|
|
prio = ANDROID_LOG_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
__android_log_write(prio, settings_.name.c_str(), msg);
|
|
}
|
|
|
|
PFN_vkGetInstanceProcAddr ShellAndroid::load_vk()
|
|
{
|
|
const char filename[] = "libvulkan.so";
|
|
void *handle = nullptr, *symbol = nullptr;
|
|
|
|
handle = dlopen(filename, RTLD_LAZY);
|
|
if (handle)
|
|
symbol = dlsym(handle, "vkGetInstanceProcAddr");
|
|
if (!symbol) {
|
|
if (handle)
|
|
dlclose(handle);
|
|
|
|
throw std::runtime_error(dlerror());
|
|
}
|
|
|
|
lib_handle_ = handle;
|
|
|
|
return reinterpret_cast<PFN_vkGetInstanceProcAddr>(symbol);
|
|
}
|
|
|
|
VkSurfaceKHR ShellAndroid::create_surface(VkInstance instance)
|
|
{
|
|
VkAndroidSurfaceCreateInfoKHR surface_info = {};
|
|
surface_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
|
|
surface_info.window = app_.window;
|
|
|
|
VkSurfaceKHR surface;
|
|
vk::assert_success(vk::CreateAndroidSurfaceKHR(instance, &surface_info, nullptr, &surface));
|
|
|
|
return surface;
|
|
}
|
|
|
|
void ShellAndroid::on_app_cmd(int32_t cmd)
|
|
{
|
|
switch (cmd) {
|
|
case APP_CMD_INIT_WINDOW:
|
|
create_context();
|
|
resize_swapchain(0, 0);
|
|
break;
|
|
case APP_CMD_TERM_WINDOW:
|
|
destroy_context();
|
|
break;
|
|
case APP_CMD_WINDOW_RESIZED:
|
|
resize_swapchain(0, 0);
|
|
break;
|
|
case APP_CMD_STOP:
|
|
ANativeActivity_finish(app_.activity);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int32_t ShellAndroid::on_input_event(const AInputEvent *event)
|
|
{
|
|
if (AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION)
|
|
return false;
|
|
|
|
bool handled = false;
|
|
|
|
switch (AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK) {
|
|
case AMOTION_EVENT_ACTION_UP:
|
|
game_.on_key(Game::KEY_SPACE);
|
|
handled = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
void ShellAndroid::quit()
|
|
{
|
|
ANativeActivity_finish(app_.activity);
|
|
}
|
|
|
|
void ShellAndroid::run()
|
|
{
|
|
PosixTimer timer;
|
|
|
|
double current_time = timer.get();
|
|
|
|
while (true) {
|
|
struct android_poll_source *source;
|
|
while (true) {
|
|
int timeout = (settings_.animate && app_.window) ? 0 : -1;
|
|
if (ALooper_pollAll(timeout, nullptr, nullptr,
|
|
reinterpret_cast<void **>(&source)) < 0)
|
|
break;
|
|
|
|
if (source)
|
|
source->process(&app_, source);
|
|
}
|
|
|
|
if (app_.destroyRequested)
|
|
break;
|
|
|
|
if (!app_.window)
|
|
continue;
|
|
|
|
acquire_back_buffer();
|
|
|
|
double t = timer.get();
|
|
add_game_time(static_cast<float>(t - current_time));
|
|
|
|
present_back_buffer();
|
|
|
|
current_time = t;
|
|
}
|
|
}
|