diff --git a/android/build.gradle b/android/build.gradle index c774fed51..edad97447 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -40,8 +40,8 @@ allprojects { ext { abiFilters = "arm64-v8a" minSdkVersion = 21 - targetSdkVersion = 26 - compileSdkVersion = 26 + targetSdkVersion = 35 + compileSdkVersion = 35 shaderPath = '../../../shaders/' assetPath = '../../../assets/' } \ No newline at end of file diff --git a/android/examples/base/CMakeLists.txt b/android/examples/base/CMakeLists.txt index e564d5229..839a850f3 100644 --- a/android/examples/base/CMakeLists.txt +++ b/android/examples/base/CMakeLists.txt @@ -9,6 +9,8 @@ include_directories(../../../external/gli) include_directories(../../../external/imgui) include_directories(${EXTERNAL_DIR}/tinygltf) include_directories(${ANDROID_NDK}/sources/android/native_app_glue) +include_directories(${game-activity-include}) +include_directories(${games-frame-pacing-include}) set(KTX_DIR ../../../external/ktx) set(KTX_SOURCES @@ -33,6 +35,7 @@ set_property(TARGET libktx PROPERTY FOLDER "external") target_link_libraries( libbase + games-frame-pacing::swappy_static android log z diff --git a/android/examples/deferredmultisampling/CMakeLists.txt b/android/examples/deferredmultisampling/CMakeLists.txt index 65b468933..f474aaf98 100644 --- a/android/examples/deferredmultisampling/CMakeLists.txt +++ b/android/examples/deferredmultisampling/CMakeLists.txt @@ -1,6 +1,19 @@ cmake_minimum_required(VERSION 3.4.1 FATAL_ERROR) set(NAME deferredmultisampling) +project(${NAME}) +find_package(games-frame-pacing REQUIRED CONFIG) +find_package(game-activity REQUIRED CONFIG) + +get_target_property(game-activity-include game-activity::game-activity INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(games-frame-pacing-include games-frame-pacing::swappy INTERFACE_INCLUDE_DIRECTORIES) + +# Export GameActivity_onCreate(), +# Refer to: https://github.com/android-ndk/ndk/issues/381. +set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u GameActivity_onCreate") +set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u Java_com_google_androidgamesdk_GameActivity_initializeNativeCode") set(SRC_DIR ../../../examples/${NAME}) set(BASE_DIR ../../../base) @@ -12,11 +25,11 @@ file(GLOB EXAMPLE_SRC "${SRC_DIR}/*.cpp") add_library(native-lib SHARED ${EXAMPLE_SRC}) -add_library(native-app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) +# add_library(native-app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) add_subdirectory(../base ${CMAKE_SOURCE_DIR}/../base) -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") +# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") include_directories(${BASE_DIR}) include_directories(${EXTERNAL_DIR}) @@ -27,7 +40,8 @@ include_directories(${ANDROID_NDK}/sources/android/native_app_glue) target_link_libraries( native-lib - native-app-glue + games-frame-pacing::swappy_static + game-activity::game-activity_static libbase android log diff --git a/android/examples/deferredmultisampling/build.gradle b/android/examples/deferredmultisampling/build.gradle index 4d08713ef..c4e4af2d6 100644 --- a/android/examples/deferredmultisampling/build.gradle +++ b/android/examples/deferredmultisampling/build.gradle @@ -33,6 +33,22 @@ android { path "CMakeLists.txt" } } + buildFeatures { + prefab true + } +} + +dependencies { + implementation "androidx.games:games-frame-pacing:2.1.2" + + // To use the Games Activity library + implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0" + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-runtime:2.2.0" + implementation "androidx.core:core:1.5.0" + implementation "androidx.constraintlayout:constraintlayout:2.0.4" + implementation 'androidx.fragment:fragment:1.2.5' + implementation "androidx.games:games-activity:2.0.2" } task copyTask { diff --git a/android/examples/deferredmultisampling/src/main/AndroidManifest.xml b/android/examples/deferredmultisampling/src/main/AndroidManifest.xml index 494264689..6b287e70d 100644 --- a/android/examples/deferredmultisampling/src/main/AndroidManifest.xml +++ b/android/examples/deferredmultisampling/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + + + + + + diff --git a/android/examples/gltfscenerendering/CMakeLists.txt b/android/examples/gltfscenerendering/CMakeLists.txt index 8457151a8..59aeba810 100644 --- a/android/examples/gltfscenerendering/CMakeLists.txt +++ b/android/examples/gltfscenerendering/CMakeLists.txt @@ -1,6 +1,19 @@ cmake_minimum_required(VERSION 3.4.1 FATAL_ERROR) set(NAME gltfscenerendering) +project(${NAME}) +find_package(games-frame-pacing REQUIRED CONFIG) +find_package(game-activity REQUIRED CONFIG) + +get_target_property(game-activity-include game-activity::game-activity INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(games-frame-pacing-include games-frame-pacing::swappy INTERFACE_INCLUDE_DIRECTORIES) + +# Export GameActivity_onCreate(), +# Refer to: https://github.com/android-ndk/ndk/issues/381. +set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u GameActivity_onCreate") +set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u Java_com_google_androidgamesdk_GameActivity_initializeNativeCode") set(SRC_DIR ../../../examples/${NAME}) set(BASE_DIR ../../../base) @@ -12,11 +25,11 @@ file(GLOB EXAMPLE_SRC "${SRC_DIR}/*.cpp") add_library(native-lib SHARED ${EXAMPLE_SRC}) -add_library(native-app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) +# add_library(native-app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) add_subdirectory(../base ${CMAKE_SOURCE_DIR}/../base) -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") +# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") include_directories(${BASE_DIR}) include_directories(${EXTERNAL_DIR}) @@ -27,7 +40,8 @@ include_directories(${ANDROID_NDK}/sources/android/native_app_glue) target_link_libraries( native-lib - native-app-glue + games-frame-pacing::swappy_static + game-activity::game-activity_static libbase android log diff --git a/android/examples/gltfscenerendering/build.gradle b/android/examples/gltfscenerendering/build.gradle index 34660ebc5..c81c83cb4 100644 --- a/android/examples/gltfscenerendering/build.gradle +++ b/android/examples/gltfscenerendering/build.gradle @@ -33,6 +33,22 @@ android { path "CMakeLists.txt" } } + buildFeatures { + prefab true + } +} + +dependencies { + implementation "androidx.games:games-frame-pacing:2.1.2" + + // To use the Games Activity library + implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0" + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-runtime:2.2.0" + implementation "androidx.core:core:1.5.0" + implementation "androidx.constraintlayout:constraintlayout:2.0.4" + implementation 'androidx.fragment:fragment:1.2.5' + implementation "androidx.games:games-activity:2.0.2" } task copyTask { diff --git a/android/examples/gltfscenerendering/src/main/AndroidManifest.xml b/android/examples/gltfscenerendering/src/main/AndroidManifest.xml index 6ce148be1..f5c58a513 100644 --- a/android/examples/gltfscenerendering/src/main/AndroidManifest.xml +++ b/android/examples/gltfscenerendering/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> + + + + + + diff --git a/android/gradle.properties b/android/gradle.properties index 2b474b577..b19ff2a04 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -15,3 +15,5 @@ android.defaults.buildfeatures.buildconfig=true android.nonFinalResIds=false android.nonTransitiveRClass=false org.gradle.jvmargs=-Xmx4096M +android.useAndroidX=true +android.prefabVersion=2.0.0 diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index 1f0099e43..9282c445c 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -1,6 +1,9 @@ file(GLOB BASE_SRC "*.cpp" "*.hpp" "*.h" "../external/imgui/*.cpp") file(GLOB BASE_HEADERS "*.hpp" "*.h") +include_directories(${game-activity-include}) +include_directories(${games-frame-pacing-include}) + set(KTX_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external/ktx) set(KTX_SOURCES ${KTX_DIR}/lib/texture.c diff --git a/base/Entrypoints.h b/base/Entrypoints.h index bd283810f..e063a8a7a 100644 --- a/base/Entrypoints.h +++ b/base/Entrypoints.h @@ -45,12 +45,12 @@ void android_main(android_app* state) \ vulkanExample = new VulkanExample(); \ state->userData = vulkanExample; \ state->onAppCmd = VulkanExample::handleAppCommand; \ - state->onInputEvent = VulkanExample::handleAppInput; \ androidApp = state; \ vks::android::getDeviceConfig(); \ + vulkanExample->initAndroidObjects(state); \ vulkanExample->renderLoop(); \ delete(vulkanExample); \ -} +} \ #elif defined(_DIRECT2DISPLAY) /* diff --git a/base/Log.h b/base/Log.h new file mode 100644 index 000000000..3e1d3c6f0 --- /dev/null +++ b/base/Log.h @@ -0,0 +1,28 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#define LOG_TAG "ADPF" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); +#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); +#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); +#ifdef NDEBUG +#define ALOGV(...) +#else +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__); +#endif diff --git a/base/VulkanAndroid.cpp b/base/VulkanAndroid.cpp index 0a5470e71..969651ba4 100644 --- a/base/VulkanAndroid.cpp +++ b/base/VulkanAndroid.cpp @@ -12,6 +12,7 @@ #include #include #include + #include "Log.h" android_app* androidApp; @@ -352,10 +353,10 @@ namespace vks jstring jmessage = jni->NewStringUTF(message); - jclass clazz = jni->GetObjectClass(androidApp->activity->clazz); + jclass clazz = jni->GetObjectClass(androidApp->activity->javaGameActivity); // Signature has to match java implementation (arguments) jmethodID methodID = jni->GetMethodID(clazz, "showAlert", "(Ljava/lang/String;)V"); - jni->CallVoidMethod(androidApp->activity->clazz, methodID, jmessage); + jni->CallVoidMethod(androidApp->activity->javaGameActivity, methodID, jmessage); jni->DeleteLocalRef(jmessage); androidApp->activity->vm->DetachCurrentThread(); diff --git a/base/VulkanAndroid.h b/base/VulkanAndroid.h index 9db7e82b8..f997aa72e 100644 --- a/base/VulkanAndroid.h +++ b/base/VulkanAndroid.h @@ -24,7 +24,8 @@ #if defined(__ANDROID__) #include -#include +// #include +#include "game-activity/native_app_glue/android_native_app_glue.h" #include #include #include diff --git a/base/VulkanDevice.cpp b/base/VulkanDevice.cpp index c32ce912f..710c53e70 100644 --- a/base/VulkanDevice.cpp +++ b/base/VulkanDevice.cpp @@ -15,8 +15,15 @@ #include #include +#include "swappy/swappyVk.h" + +#include "Log.h" + namespace vks { + // This is a bit of a hack because of how swappy reports extensions + static char *swappy_extension_strings = nullptr; + /** * Default constructor * @@ -42,18 +49,73 @@ namespace vks vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); // Get list of supported extensions - uint32_t extCount = 0; - vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, nullptr); - if (extCount > 0) - { - std::vector extensions(extCount); - if (vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, &extensions.front()) == VK_SUCCESS) - { - for (auto& ext : extensions) - { - supportedExtensions.push_back(ext.extensionName); - } + uint32_t extension_count = 0; + vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, nullptr); + // if (extension_count > 0) + // { + // std::vector extensions(extension_count); + // if (vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, &extensions.front()) == VK_SUCCESS) + // { + // for (auto& ext : extensions) + // { + // supportedExtensions.push_back(ext.extensionName); + // } + // } + // } + + // SWAPPY: Add any available extensions Swappy wants + std::vector available_extensions(extension_count); + vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extension_count, + available_extensions.data()); + + // Add any available extensions Swappy wants + uint32_t swappy_extension_count = 0; + SwappyVk_determineDeviceExtensions(physicalDevice, extension_count, available_extensions.data(), + &swappy_extension_count, nullptr); + + ALOGI("VulkanDevice::VulkanDevice SwappyVk_determineDeviceExtensions: %d", swappy_extension_count); // 1 // VK_GOOGLE_display_timing + + std::vector device_extensions; + + // Swappy expects valid string buffers for its extension names, rather than just copying + // pointers to its constants. Make a buffer for it to strcpy into + const size_t swappy_string_size = VK_MAX_EXTENSION_NAME_SIZE * swappy_extension_count; + if (swappy_extension_count > 0) { + if (swappy_extension_strings != nullptr) { + free(swappy_extension_strings); } + swappy_extension_strings = (char *) malloc(swappy_string_size); + memset(swappy_extension_strings, 0, swappy_string_size); + } + char *base = swappy_extension_strings; + for (uint32_t i = 0; i < swappy_extension_count; ++i) { + device_extensions.push_back(base); + base += VK_MAX_EXTENSION_NAME_SIZE; + } + + SwappyVk_determineDeviceExtensions(physicalDevice, extension_count, available_extensions.data(), + &swappy_extension_count, + const_cast(device_extensions.data())); + + device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // VK_KHR_swapchain + + // If we are on a Vulkan 1.0 device, require the VK_KHR_maintenance1 extension, which is + // core from Vulkan 1.1+ + VkPhysicalDeviceProperties device_properties{}; + vkGetPhysicalDeviceProperties(physicalDevice, &device_properties); + const uint32_t device_major_version = VK_VERSION_MAJOR(device_properties.apiVersion); + const uint32_t device_minor_version = VK_VERSION_MINOR(device_properties.apiVersion); + if (device_major_version == 1 && device_minor_version == 0) { + device_extensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME); + } + + for ( int i = 0; i < device_extensions.size(); i++ ) { + std::string ext(device_extensions[i]); + supportedExtensions.push_back(ext); + swappyExtensions.push_back(ext); + ALOGI("VulkanDevice::VulkanDevice supportedExtensions: %s", ext.c_str()); + //2024-09-05 11:51:07.844 13472-13520 ADPF de....awillems.vulkanScenerendering I VulkanDevice::VulkanDevice supportedExtensions: VK_GOOGLE_display_timing + //2024-09-05 11:51:07.844 13472-13520 ADPF de....awillems.vulkanScenerendering I VulkanDevice::VulkanDevice supportedExtensions: VK_KHR_swapchain } } @@ -72,6 +134,12 @@ namespace vks { vkDestroyDevice(logicalDevice, nullptr); } + + // Take this opportunity to clean up the swappy string alloc, if it exists + if (swappy_extension_strings != nullptr) { + free(swappy_extension_strings); + swappy_extension_strings = nullptr; + } } /** @@ -277,6 +345,10 @@ namespace vks } #endif + for (const std::string &swappyExtension : swappyExtensions) + { + deviceExtensions.push_back(swappyExtension.c_str()); + } if (deviceExtensions.size() > 0) { for (const char* enabledExtension : deviceExtensions) diff --git a/base/VulkanDevice.h b/base/VulkanDevice.h index 875976873..67cd12909 100644 --- a/base/VulkanDevice.h +++ b/base/VulkanDevice.h @@ -37,6 +37,8 @@ struct VulkanDevice std::vector queueFamilyProperties; /** @brief List of extensions supported by the device */ std::vector supportedExtensions; + /** @brief List of extensions needed to enable for swappy */ + std::vector swappyExtensions; /** @brief Default command pool for the graphics queue family index */ VkCommandPool commandPool = VK_NULL_HANDLE; /** @brief Contains queue family indices */ diff --git a/base/VulkanSwapChain.cpp b/base/VulkanSwapChain.cpp index 0920c941c..dcb886859 100644 --- a/base/VulkanSwapChain.cpp +++ b/base/VulkanSwapChain.cpp @@ -10,6 +10,8 @@ #include "VulkanSwapChain.h" +#include "swappy/swappyVk.h" + /** @brief Creates the platform specific surface abstraction of the native platform window used for presentation */ #if defined(VK_USE_PLATFORM_WIN32_KHR) void VulkanSwapChain::initSurface(void* platformHandle, void* platformWindow) @@ -352,6 +354,7 @@ void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync, bool // This also cleans up all the presentable images if (oldSwapchain != VK_NULL_HANDLE) { + SwappyVk_destroySwapchain(device, swapChain); for (uint32_t i = 0; i < imageCount; i++) { vkDestroyImageView(device, buffers[i].view, nullptr); @@ -415,7 +418,8 @@ VkResult VulkanSwapChain::queuePresent(VkQueue queue, uint32_t imageIndex, VkSem presentInfo.pWaitSemaphores = &waitSemaphore; presentInfo.waitSemaphoreCount = 1; } - return vkQueuePresentKHR(queue, &presentInfo); + // return vkQueuePresentKHR(queue, &presentInfo); + return SwappyVk_queuePresent(queue, &presentInfo); } diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index a85a8bbc3..35fcaa675 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -8,6 +8,12 @@ #include "vulkanexamplebase.h" +#include "swappy/swappyVk.h" + +#include "Log.h" + +#define USE_SWAPPY 1 + #if defined(VK_EXAMPLE_XCODE_GENERATED) #if (defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT)) #include @@ -189,6 +195,21 @@ void VulkanExampleBase::destroyCommandBuffers() vkFreeCommandBuffers(device, cmdPool, static_cast(drawCmdBuffers.size()), drawCmdBuffers.data()); } +void VulkanExampleBase::initAndroidObjects(void* app) +{ + and_app = app; + android_app* androidApp = (android_app*)app; + JNIEnv* env; + activity_obj = androidApp->activity->javaGameActivity; + native_window = androidApp->window; + if ( 0 != androidApp->activity->vm->AttachCurrentThread(&env, NULL) ) { + ALOGI("VulkanExampleBase::initAndroidObjects failed to get JNIEnv"); + } else { + ALOGI("VulkanExampleBase::initAndroidObjects get JNIEnv"); + jni_env = env; + } +} + std::string VulkanExampleBase::getShadersPath() const { return getShaderBasePath() + shaderDir + "/"; @@ -212,6 +233,7 @@ void VulkanExampleBase::prepare() setupRenderPass(); createPipelineCache(); setupFrameBuffer(); + setupFramePacing(); settings.overlay = settings.overlay && (!benchmark.active); if (settings.overlay) { ui.device = vulkanDevice; @@ -371,7 +393,7 @@ void VulkanExampleBase::renderLoop() // Exit loop, example will be destroyed in application main if (destroy) { - ANativeActivity_finish(androidApp->activity); + //ANativeActivity_finish(androidApp->activity); break; } @@ -406,6 +428,9 @@ void VulkanExampleBase::renderLoop() bool updateView = false; + // HandleGameActivityInput + handleGameActivityInput(); + // Check touch state (for movement) if (touchDown) { touchTimer += frameTimer; @@ -769,15 +794,15 @@ void VulkanExampleBase::submitFrame() { VkResult result = swapChain.queuePresent(queue, currentBuffer, semaphores.renderComplete); // Recreate the swapchain if it's no longer compatible with the surface (OUT_OF_DATE) or no longer optimal for presentation (SUBOPTIMAL) - if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) { - windowResize(); - if (result == VK_ERROR_OUT_OF_DATE_KHR) { - return; - } - } - else { - VK_CHECK_RESULT(result); - } + // if ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)) { + // windowResize(); + // if (result == VK_ERROR_OUT_OF_DATE_KHR) { + // return; + // } + // } + // else { + // VK_CHECK_RESULT(result); + // } VK_CHECK_RESULT(vkQueueWaitIdle(queue)); } @@ -1409,6 +1434,76 @@ void VulkanExampleBase::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR OnHandleMessage(hWnd, uMsg, wParam, lParam); } #elif defined(VK_USE_PLATFORM_ANDROID_KHR) +void VulkanExampleBase::processMotionEvent(const void* event) +{ + const GameActivityMotionEvent* motionEvent = (GameActivityMotionEvent*) event; + if ( motionEvent->source == AINPUT_SOURCE_TOUCHSCREEN && motionEvent->pointerCount > 0 ) { + const uint32_t pointerIndex = 0; + const GameActivityPointerAxes pointerAxes = motionEvent->pointers[0]; + const int action = motionEvent->action; + const int actionMasked = action & AMOTION_EVENT_ACTION_MASK; + const int64_t eventTime = motionEvent->eventTime; + const int64_t downTime = motionEvent->downTime; + switch (actionMasked ) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_POINTER_UP: + lastTapTime = eventTime; + touchPos.x = pointerAxes.rawX; + touchPos.y = pointerAxes.rawY; + touchTimer = 0.0; + touchDown = false; + camera.keys.up = false; + + // Detect single tap + if ( eventTime - downTime <= vks::android::TAP_TIMEOUT) { + mouseState.buttons.left = true; + } + break; + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + if ( eventTime - lastTapTime <= vks::android::DOUBLE_TAP_TIMEOUT ) { + keyPressed(TOUCH_DOUBLE_TAP); + touchDown = false; + } else { + touchDown = true; + } + touchPos.x = pointerAxes.rawX; + touchPos.y = pointerAxes.rawY; + mouseState.position.x = pointerAxes.rawX; + mouseState.position.y = pointerAxes.rawY; + break; + case AMOTION_EVENT_ACTION_MOVE: + bool handled = false; + if ( settings.overlay ) { + ImGuiIO& io = ImGui::GetIO(); + handled = io.WantCaptureMouse && ui.visible; + } + if ( !handled ) { + int32_t eventX = pointerAxes.rawX; + int32_t eventY = pointerAxes.rawY; + + float deltaX = (float)(touchPos.y - eventY) * camera.rotationSpeed * 0.5f; + float deltaY = (float)(touchPos.x - eventX) * camera.rotationSpeed * 0.5f; + + camera.rotate(glm::vec3(deltaX, 0.0f, 0.0f)); + camera.rotate(glm::vec3(0.0f, -deltaY, 0.0f)); + + touchPos.x = eventX; + touchPos.y = eventY; + } + break; + } + } +} + +/** + * Stub to be implemented by child class + * Actually we can abstract some implementation here, but not sure why there is linker error + * when accessing all the android objects (eg: android_app_swap_input_buffers) here + */ +void VulkanExampleBase::handleGameActivityInput() { +} + int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* event) { VulkanExampleBase* vulkanExample = reinterpret_cast(app->userData); @@ -1570,6 +1665,7 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd) LOGD("APP_CMD_INIT_WINDOW"); if (androidApp->window != NULL) { + vulkanExample->native_window = androidApp->window; if (vulkanExample->initVulkan()) { vulkanExample->prepare(); assert(vulkanExample->prepared); @@ -3173,6 +3269,36 @@ void VulkanExampleBase::getEnabledFeatures() {} void VulkanExampleBase::getEnabledExtensions() {} +void VulkanExampleBase::setupFramePacing() { +#if defined(USE_SWAPPY) + uint64_t refresh_rate = 0; + + assert(jni_env); + assert(activity_obj); + assert(physicalDevice); + assert(device); + assert(swapChain.swapChain); + + uint32_t present_queue_index_ = vulkanDevice->queueFamilyIndices.graphics; + ALOGI("VulkanExampleBase::setupFramePacing present_queue_index_ %d", present_queue_index_); + SwappyVk_setQueueFamilyIndex(device, queue, present_queue_index_); + + bool success = SwappyVk_initAndGetRefreshCycleDuration( + (JNIEnv*) (jni_env), + (jobject) activity_obj, + physicalDevice, device, swapChain.swapChain, &refresh_rate + ); + ALOGI("VulkanExampleBase::setupFramePacing result: %d", success); + + SwappyVk_setSwapIntervalNS(device, swapChain.swapChain, SWAPPY_SWAP_60FPS); + SwappyVk_setWindow(device, swapChain.swapChain, (ANativeWindow*)native_window); + +// AdpfPerfHintMgr::getInstance().setupQueryTimer(); +// AdpfPerfHintMgr::getInstance().updateTargetWorkDuration(SWAPPY_SWAP_60FPS); + +#endif +} + void VulkanExampleBase::windowResize() { if (!prepared) @@ -3189,6 +3315,8 @@ void VulkanExampleBase::windowResize() width = destWidth; height = destHeight; createSwapChain(); + // Reset swappy to use the new swap chain + setupFramePacing(); // Recreate the frame buffers vkDestroyImageView(device, depthStencil.view, nullptr); diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index 33bc30da7..b4a575c7e 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -8,6 +8,7 @@ #pragma once +#include <__config> #ifdef _WIN32 #pragma comment(linker, "/subsystem:windows") #include @@ -17,7 +18,8 @@ #elif defined(VK_USE_PLATFORM_ANDROID_KHR) #include #include -#include +// #include +#include "game-activity/native_app_glue/android_native_app_glue.h" #include #include "VulkanAndroid.h" #elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) @@ -164,6 +166,12 @@ class VulkanExampleBase vks::UIOverlay ui; CommandLineParser commandLineParser; + void *native_window = nullptr; + void *and_app = nullptr; + void *activity_obj = nullptr; + void *jni_env = nullptr; + void initAndroidObjects(void *app); + /** @brief Last frame time measured using a high performance timer (if available) */ float frameTimer = 1.0f; @@ -298,6 +306,8 @@ class VulkanExampleBase #elif defined(VK_USE_PLATFORM_ANDROID_KHR) static int32_t handleAppInput(struct android_app* app, AInputEvent* event); static void handleAppCommand(android_app* app, int32_t cmd); + virtual void handleGameActivityInput(); + virtual void processMotionEvent(const void* event); #elif (defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT)) void* setupWindow(void* view); void displayLinkOutputCb(); @@ -385,6 +395,8 @@ class VulkanExampleBase /** @brief (Virtual) Called after the physical device extensions have been read, can be used to enable extensions based on the supported extension listing*/ virtual void getEnabledExtensions(); + virtual void setupFramePacing(); + /** @brief Prepares all Vulkan resources and functions required to run the sample */ virtual void prepare(); diff --git a/examples/deferredmultisampling/deferredmultisampling.cpp b/examples/deferredmultisampling/deferredmultisampling.cpp index c908cf9d9..62a6d7f58 100644 --- a/examples/deferredmultisampling/deferredmultisampling.cpp +++ b/examples/deferredmultisampling/deferredmultisampling.cpp @@ -12,6 +12,8 @@ #include "VulkanFrameBuffer.hpp" #include "VulkanglTFModel.h" +#include "Log.h" + class VulkanExample : public VulkanExampleBase { public: @@ -623,6 +625,45 @@ class VulkanExample : public VulkanExampleBase } } + void handleGameActivityInput() + { + //ALOGI("VulkanAndroid::handleGameActivityInput"); + + // Swap input buffers so we don't miss any events while processing + // inputBuffer. + android_input_buffer *inputBuffer = android_app_swap_input_buffers(androidApp); + // Early exit if no events. + if (inputBuffer == nullptr) + return; + + ALOGI("handleGameActivityInput keyEventsCount %lu motionEventsCount: %lu", inputBuffer->keyEventsCount, inputBuffer->motionEventsCount); + + if (inputBuffer->keyEventsCount != 0) { + // TODO: add support for controllers + android_app_clear_key_events(inputBuffer); + } + + if (inputBuffer->motionEventsCount != 0) { + for (uint64_t i = 0; i < inputBuffer->motionEventsCount; ++i) { + GameActivityMotionEvent *motionEvent = &inputBuffer->motionEvents[i]; + // ALOGI("motion action: %d, deviceId: %d eventTime: %ld pointerCount: %d, source: %d, precisionXY: [%f, %f]", + // motionEvent->action, + // motionEvent->deviceId, + // motionEvent->eventTime, + // motionEvent->pointerCount, + // motionEvent->source, + // motionEvent->precisionX, + // motionEvent->precisionY + // ); + // for ( int i = 0; i < motionEvent->pointerCount; i++ ) { + // ALOGI("handleGameActivityInput pointer %d [%f, %f]", i, motionEvent->pointers[i].rawX, motionEvent->pointers[i].rawY); + // } + processMotionEvent(motionEvent); + } + android_app_clear_motion_events(inputBuffer); + } + } + // Returns the maximum sample count usable by the platform VkSampleCountFlagBits getMaxUsableSampleCount() { diff --git a/examples/gltfscenerendering/gltfscenerendering.cpp b/examples/gltfscenerendering/gltfscenerendering.cpp index 7faef44c4..8e4c1abfb 100644 --- a/examples/gltfscenerendering/gltfscenerendering.cpp +++ b/examples/gltfscenerendering/gltfscenerendering.cpp @@ -12,6 +12,8 @@ * This sample comes with a tutorial, see the README.md in this folder */ +#include "Log.h" + #include "gltfscenerendering.h" /* @@ -660,4 +662,44 @@ void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay* overlay) } } + void VulkanExample::handleGameActivityInput() + { + //ALOGI("VulkanAndroid::handleGameActivityInput"); + + // Swap input buffers so we don't miss any events while processing + // inputBuffer. + android_input_buffer *inputBuffer = android_app_swap_input_buffers(androidApp); + // Early exit if no events. + if (inputBuffer == nullptr) + return; + + ALOGI("handleGameActivityInput keyEventsCount %lu motionEventsCount: %lu", inputBuffer->keyEventsCount, inputBuffer->motionEventsCount); + + if (inputBuffer->keyEventsCount != 0) { + // TODO: add support for controllers + android_app_clear_key_events(inputBuffer); + } + + if (inputBuffer->motionEventsCount != 0) { + for (uint64_t i = 0; i < inputBuffer->motionEventsCount; ++i) { + GameActivityMotionEvent *motionEvent = &inputBuffer->motionEvents[i]; + // ALOGI("motion action: %d, deviceId: %d eventTime: %ld pointerCount: %d, source: %d, precisionXY: [%f, %f]", + // motionEvent->action, + // motionEvent->deviceId, + // motionEvent->eventTime, + // motionEvent->pointerCount, + // motionEvent->source, + // motionEvent->precisionX, + // motionEvent->precisionY + // ); + // for ( int i = 0; i < motionEvent->pointerCount; i++ ) { + // ALOGI("handleGameActivityInput pointer %d [%f, %f]", i, motionEvent->pointers[i].rawX, motionEvent->pointers[i].rawY); + // } + processMotionEvent(motionEvent); + } + android_app_clear_motion_events(inputBuffer); + } + } + + VULKAN_EXAMPLE_MAIN() diff --git a/examples/gltfscenerendering/gltfscenerendering.h b/examples/gltfscenerendering/gltfscenerendering.h index 7e71bbfea..0d1764826 100644 --- a/examples/gltfscenerendering/gltfscenerendering.h +++ b/examples/gltfscenerendering/gltfscenerendering.h @@ -167,4 +167,5 @@ class VulkanExample : public VulkanExampleBase void prepare(); virtual void render(); virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay); + virtual void handleGameActivityInput(); };